From 2029bb3324ea380cfb686594ffaa9b038fdb820f Mon Sep 17 00:00:00 2001 From: sparky4 Date: Tue, 23 Jun 2015 11:28:48 -0500 Subject: [PATCH] wwww added catacombs source~ new file: 16/cawat/16_in.c new file: 16/cawat/16_in.h new file: 16/cawat/16_mm.c new file: 16/cawat/16_mm.h new file: 16/cawat/ARMGAME.DSK new file: 16/cawat/ARMGAME.PRJ new file: 16/cawat/AUDIOARM.H new file: 16/cawat/C5_ACT1.C new file: 16/cawat/C5_ACT2.C new file: 16/cawat/C5_ACT3.C new file: 16/cawat/C5_ACT4.C new file: 16/cawat/C5_ASM.ASM new file: 16/cawat/C5_DEBUG.C new file: 16/cawat/C5_DRAW.C new file: 16/cawat/C5_GAME.C new file: 16/cawat/C5_MAIN.C new file: 16/cawat/C5_PLAY.C new file: 16/cawat/C5_SCALE.C new file: 16/cawat/C5_SCA_A.ASM new file: 16/cawat/C5_STATE.C new file: 16/cawat/C5_TRACE.C new file: 16/cawat/C5_WIZ.C new file: 16/cawat/COPYING new file: 16/cawat/DEF.H new file: 16/cawat/GELIB.C new file: 16/cawat/GELIB.H new file: 16/cawat/GFXE_ARM.EQU new file: 16/cawat/GFXE_ARM.H new file: 16/cawat/ID_ASM.EQU new file: 16/cawat/ID_CA.C new file: 16/cawat/ID_CA.H new file: 16/cawat/ID_HEADS.H new file: 16/cawat/ID_IN.C new file: 16/cawat/ID_IN.H new file: 16/cawat/ID_MM.C new file: 16/cawat/ID_MM.H new file: 16/cawat/ID_RF.C new file: 16/cawat/ID_RF.H new file: 16/cawat/ID_RF_A.ASM new file: 16/cawat/ID_SD.C new file: 16/cawat/ID_SD.H new file: 16/cawat/ID_STRS.H new file: 16/cawat/ID_US.C new file: 16/cawat/ID_US.H new file: 16/cawat/ID_US_1.C new file: 16/cawat/ID_US_2.C new file: 16/cawat/ID_US_A.ASM new file: 16/cawat/ID_VW.C new file: 16/cawat/ID_VW.H new file: 16/cawat/ID_VW_A.ASM new file: 16/cawat/ID_VW_AC.ASM new file: 16/cawat/ID_VW_AE.ASM new file: 16/cawat/JABHACK.ASM new file: 16/cawat/JAMPAK.C new file: 16/cawat/JAMPAK.H new file: 16/cawat/JAM_IO.C new file: 16/cawat/JAM_IO.H new file: 16/cawat/LZHUF.C new file: 16/cawat/LZHUFF.H new file: 16/cawat/LZW.C new file: 16/cawat/LZW.H new file: 16/cawat/MAPSARM.H new file: 16/cawat/README.md new file: 16/cawat/SL_FILE.H new file: 16/cawat/SOFT.C new file: 16/cawat/SOFT.H new file: 16/cawat/TC0000.SWP new file: 16/cawat/TD.TR new file: 16/cawat/TDCONFIG.TD new file: 16/cawat/cawat.bfproject new file: 16/cawat/lib_head.h new file: 16/cawat/port.h new file: 16/cawat/types.h --- 16/cawat/16_in.c | 1287 +++++++++++++ 16/cawat/16_in.h | 214 +++ 16/cawat/16_mm.c | 1116 ++++++++++++ 16/cawat/16_mm.h | 136 ++ 16/cawat/ARMGAME.DSK | Bin 0 -> 164 bytes 16/cawat/ARMGAME.PRJ | Bin 0 -> 12826 bytes 16/cawat/AUDIOARM.H | 91 + 16/cawat/C5_ACT1.C | 820 +++++++++ 16/cawat/C5_ACT2.C | 827 +++++++++ 16/cawat/C5_ACT3.C | 1218 +++++++++++++ 16/cawat/C5_ACT4.C | 414 +++++ 16/cawat/C5_ASM.ASM | 248 +++ 16/cawat/C5_DEBUG.C | 799 +++++++++ 16/cawat/C5_DRAW.C | 1932 ++++++++++++++++++++ 16/cawat/C5_GAME.C | 1657 +++++++++++++++++ 16/cawat/C5_MAIN.C | 1052 +++++++++++ 16/cawat/C5_PLAY.C | 1425 +++++++++++++++ 16/cawat/C5_SCALE.C | 697 +++++++ 16/cawat/C5_SCA_A.ASM | 153 ++ 16/cawat/C5_STATE.C | 682 +++++++ 16/cawat/C5_TRACE.C | 872 +++++++++ 16/cawat/C5_WIZ.C | 3296 ++++++++++++++++++++++++++++++++++ 16/cawat/COPYING | 339 ++++ 16/cawat/DEF.H | 865 +++++++++ 16/cawat/GELIB.C | 2876 +++++++++++++++++++++++++++++ 16/cawat/GELIB.H | 210 +++ 16/cawat/GFXE_ARM.EQU | 571 ++++++ 16/cawat/GFXE_ARM.H | 647 +++++++ 16/cawat/ID_ASM.EQU | 114 ++ 16/cawat/ID_CA.C | 2190 ++++++++++++++++++++++ 16/cawat/ID_CA.H | 123 ++ 16/cawat/ID_HEADS.H | 146 ++ 16/cawat/ID_IN.C | 1284 +++++++++++++ 16/cawat/ID_IN.H | 214 +++ 16/cawat/ID_MM.C | 1150 ++++++++++++ 16/cawat/ID_MM.H | 110 ++ 16/cawat/ID_RF.C | 2845 +++++++++++++++++++++++++++++ 16/cawat/ID_RF.H | 153 ++ 16/cawat/ID_RF_A.ASM | 690 +++++++ 16/cawat/ID_SD.C | 1319 ++++++++++++++ 16/cawat/ID_SD.H | 206 +++ 16/cawat/ID_STRS.H | 128 ++ 16/cawat/ID_US.C | 3691 ++++++++++++++++++++++++++++++++++++++ 16/cawat/ID_US.H | 146 ++ 16/cawat/ID_US_1.C | 1317 ++++++++++++++ 16/cawat/ID_US_2.C | 1822 +++++++++++++++++++ 16/cawat/ID_US_A.ASM | 117 ++ 16/cawat/ID_VW.C | 1591 ++++++++++++++++ 16/cawat/ID_VW.H | 374 ++++ 16/cawat/ID_VW_A.ASM | 752 ++++++++ 16/cawat/ID_VW_AC.ASM | 1485 +++++++++++++++ 16/cawat/ID_VW_AE.ASM | 1900 ++++++++++++++++++++ 16/cawat/JABHACK.ASM | 115 ++ 16/cawat/JAMPAK.C | 1113 ++++++++++++ 16/cawat/JAMPAK.H | 118 ++ 16/cawat/JAM_IO.C | 121 ++ 16/cawat/JAM_IO.H | 107 ++ 16/cawat/LZHUF.C | 1069 +++++++++++ 16/cawat/LZHUFF.H | 35 + 16/cawat/LZW.C | 608 +++++++ 16/cawat/LZW.H | 40 + 16/cawat/MAPSARM.H | 53 + 16/cawat/README.md | 16 + 16/cawat/SL_FILE.H | 109 ++ 16/cawat/SOFT.C | 479 +++++ 16/cawat/SOFT.H | 25 + 16/cawat/TC0000.SWP | Bin 0 -> 262144 bytes 16/cawat/TD.TR | Bin 0 -> 65 bytes 16/cawat/TDCONFIG.TD | Bin 0 -> 691 bytes 16/cawat/cawat.bfproject | 103 ++ 16/cawat/lib_head.h | 38 + 16/cawat/port.h | 135 ++ 16/cawat/types.h | 14 + 73 files changed, 52609 insertions(+) create mode 100644 16/cawat/16_in.c create mode 100644 16/cawat/16_in.h create mode 100644 16/cawat/16_mm.c create mode 100644 16/cawat/16_mm.h create mode 100644 16/cawat/ARMGAME.DSK create mode 100644 16/cawat/ARMGAME.PRJ create mode 100644 16/cawat/AUDIOARM.H create mode 100644 16/cawat/C5_ACT1.C create mode 100644 16/cawat/C5_ACT2.C create mode 100644 16/cawat/C5_ACT3.C create mode 100644 16/cawat/C5_ACT4.C create mode 100644 16/cawat/C5_ASM.ASM create mode 100644 16/cawat/C5_DEBUG.C create mode 100644 16/cawat/C5_DRAW.C create mode 100644 16/cawat/C5_GAME.C create mode 100644 16/cawat/C5_MAIN.C create mode 100644 16/cawat/C5_PLAY.C create mode 100644 16/cawat/C5_SCALE.C create mode 100644 16/cawat/C5_SCA_A.ASM create mode 100644 16/cawat/C5_STATE.C create mode 100644 16/cawat/C5_TRACE.C create mode 100644 16/cawat/C5_WIZ.C create mode 100644 16/cawat/COPYING create mode 100644 16/cawat/DEF.H create mode 100644 16/cawat/GELIB.C create mode 100644 16/cawat/GELIB.H create mode 100644 16/cawat/GFXE_ARM.EQU create mode 100644 16/cawat/GFXE_ARM.H create mode 100644 16/cawat/ID_ASM.EQU create mode 100644 16/cawat/ID_CA.C create mode 100644 16/cawat/ID_CA.H create mode 100644 16/cawat/ID_HEADS.H create mode 100644 16/cawat/ID_IN.C create mode 100644 16/cawat/ID_IN.H create mode 100644 16/cawat/ID_MM.C create mode 100644 16/cawat/ID_MM.H create mode 100644 16/cawat/ID_RF.C create mode 100644 16/cawat/ID_RF.H create mode 100644 16/cawat/ID_RF_A.ASM create mode 100644 16/cawat/ID_SD.C create mode 100644 16/cawat/ID_SD.H create mode 100644 16/cawat/ID_STRS.H create mode 100644 16/cawat/ID_US.C create mode 100644 16/cawat/ID_US.H create mode 100644 16/cawat/ID_US_1.C create mode 100644 16/cawat/ID_US_2.C create mode 100644 16/cawat/ID_US_A.ASM create mode 100644 16/cawat/ID_VW.C create mode 100644 16/cawat/ID_VW.H create mode 100644 16/cawat/ID_VW_A.ASM create mode 100644 16/cawat/ID_VW_AC.ASM create mode 100644 16/cawat/ID_VW_AE.ASM create mode 100644 16/cawat/JABHACK.ASM create mode 100644 16/cawat/JAMPAK.C create mode 100644 16/cawat/JAMPAK.H create mode 100644 16/cawat/JAM_IO.C create mode 100644 16/cawat/JAM_IO.H create mode 100644 16/cawat/LZHUF.C create mode 100644 16/cawat/LZHUFF.H create mode 100644 16/cawat/LZW.C create mode 100644 16/cawat/LZW.H create mode 100644 16/cawat/MAPSARM.H create mode 100644 16/cawat/README.md create mode 100644 16/cawat/SL_FILE.H create mode 100644 16/cawat/SOFT.C create mode 100644 16/cawat/SOFT.H create mode 100644 16/cawat/TC0000.SWP create mode 100644 16/cawat/TD.TR create mode 100644 16/cawat/TDCONFIG.TD create mode 100644 16/cawat/cawat.bfproject create mode 100644 16/cawat/lib_head.h create mode 100644 16/cawat/port.h create mode 100644 16/cawat/types.h diff --git a/16/cawat/16_in.c b/16/cawat/16_in.c new file mode 100644 index 00000000..67e02e29 --- /dev/null +++ b/16/cawat/16_in.c @@ -0,0 +1,1287 @@ +/* Catacomb Armageddon Source Code + * Copyright (C) 1993-2014 Flat Rock Software + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +// +// ID Engine +// ID_IN.c - Input Manager +// v1.0d1 +// By Jason Blochowiak +// + +// +// This module handles dealing with the various input devices +// +// Depends on: Memory Mgr (for demo recording), Sound Mgr (for timing stuff), +// User Mgr (for command line parms) +// +// Globals: +// LastScan - The keyboard scan code of the last key pressed +// LastASCII - The ASCII value of the last key pressed +// DEBUG - there are more globals +// + +//#include "ID_HEADS.H" +#include "16_in.h" +//#pragma hdrstop + +#define KeyInt 9 // The keyboard ISR number + +// Stuff for the joystick +#define JoyScaleMax 32768 +#define JoyScaleShift 8 +#define MaxJoyValue 5000 + +// Global variables + boolean JoystickCalibrated=false; // MDM (GAMERS EDGE) - added + ControlType ControlTypeUsed; // MDM (GAMERS EDGE) - added + + boolean Keyboard[NumCodes], + JoysPresent[MaxJoys], + MousePresent; + boolean Paused; + char LastASCII; + ScanCode LastScan; + KeyboardDef KbdDefs[MaxKbds] = {{0x1d,0x38,0x47,0x48,0x49,0x4b,0x4d,0x4f,0x50,0x51}}; + JoystickDef JoyDefs[MaxJoys]; + ControlType Controls[MaxPlayers]; + +// Demo DemoMode = demo_Off; +// byte /*_1seg*/ *DemoBuffer; +// word DemoOffset,DemoSize; + +// Internal variables +static boolean IN_Started; +static boolean CapsLock; +static ScanCode CurCode,LastCode; +static byte far ASCIINames[] = // Unshifted ASCII for scan codes + { +// 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0 ,27 ,'1','2','3','4','5','6','7','8','9','0','-','=',8 ,9 , // 0 + 'q','w','e','r','t','y','u','i','o','p','[',']',13 ,0 ,'a','s', // 1 + 'd','f','g','h','j','k','l',';',39 ,'`',0 ,92 ,'z','x','c','v', // 2 + 'b','n','m',',','.','/',0 ,'*',0 ,' ',0 ,0 ,0 ,0 ,0 ,0 , // 3 + 0 ,0 ,0 ,0 ,0 ,0 ,0 ,'7','8','9','-','4','5','6','+','1', // 4 + '2','3','0',127,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 , // 5 + 0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 , // 6 + 0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 // 7 + }, + far ShiftNames[] = // Shifted ASCII for scan codes + { +// 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0 ,27 ,'!','@','#','$','%','^','&','*','(',')','_','+',8 ,9 , // 0 + 'Q','W','E','R','T','Y','U','I','O','P','{','}',13 ,0 ,'A','S', // 1 + 'D','F','G','H','J','K','L',':',34 ,'~',0 ,'|','Z','X','C','V', // 2 + 'B','N','M','<','>','?',0 ,'*',0 ,' ',0 ,0 ,0 ,0 ,0 ,0 , // 3 + 0 ,0 ,0 ,0 ,0 ,0 ,0 ,'7','8','9','-','4','5','6','+','1', // 4 + '2','3','0',127,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 , // 5 + 0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 , // 6 + 0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 // 7 + }, + far SpecialNames[] = // ASCII for 0xe0 prefixed codes + { +// 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 , // 0 + 0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,13 ,0 ,0 ,0 , // 1 + 0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 , // 2 + 0 ,0 ,0 ,0 ,0 ,'/',0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 , // 3 + 0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 , // 4 + 0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 , // 5 + 0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 , // 6 + 0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 // 7 + }, + +#if 0 + *ScanNames[] = // Scan code names with single chars + { + "?","?","1","2","3","4","5","6","7","8","9","0","-","+","?","?", + "Q","W","E","R","T","Y","U","I","O","P","[","]","|","?","A","S", + "D","F","G","H","J","K","L",";","\"","?","?","?","Z","X","C","V", + "B","N","M",",",".","/","?","?","?","?","?","?","?","?","?","?", + "?","?","?","?","?","?","?","?","\xf","?","-","\x15","5","\x11","+","?", + "\x13","?","?","?","?","?","?","?","?","?","?","?","?","?","?","?", + "?","?","?","?","?","?","?","?","?","?","?","?","?","?","?","?", + "?","?","?","?","?","?","?","?","?","?","?","?","?","?","?","?" + }, // DEBUG - consolidate these +#endif + + far ExtScanCodes[] = // Scan codes with >1 char names + { + 1,0xe,0xf,0x1d,0x2a,0x39,0x3a,0x3b,0x3c,0x3d,0x3e, + 0x3f,0x40,0x41,0x42,0x43,0x44,0x57,0x59,0x46,0x1c,0x36, + 0x37,0x38,0x47,0x49,0x4f,0x51,0x52,0x53,0x45,0x48, + 0x50,0x4b,0x4d,0x00 + }; +#if 0 + *ExtScanNames[] = // Names corresponding to ExtScanCodes + { + "Esc","BkSp","Tab","Ctrl","LShft","Space","CapsLk","F1","F2","F3","F4", + "F5","F6","F7","F8","F9","F10","F11","F12","ScrlLk","Enter","RShft", + "PrtSc","Alt","Home","PgUp","End","PgDn","Ins","Del","NumLk","Up", + "Down","Left","Right","" + }; +#endif +static Direction DirTable[] = // Quick lookup for total direction + { + dir_NorthWest, dir_North, dir_NorthEast, + dir_West, dir_None, dir_East, + dir_SouthWest, dir_South, dir_SouthEast + }; + +static void (*INL_KeyHook)(void); +static void interrupt (*OldKeyVect)(void); + +static char *ParmStrings[] = {"nojoys","nomouse",nil}; + +// Internal routines + +/////////////////////////////////////////////////////////////////////////// +// +// INL_KeyService() - Handles a keyboard interrupt (key up/down) +// +/////////////////////////////////////////////////////////////////////////// +static void interrupt +INL_KeyService(void) +{ +static boolean special; + byte k,c, + temp; + + k = inp(0x60); // Get the scan code + + // Tell the XT keyboard controller to clear the key + outp(0x61,(temp = inp(0x61)) | 0x80); + outp(0x61,temp); + + if (k == 0xe0) // Special key prefix + special = true; + else if (k == 0xe1) // Handle Pause key + Paused = true; + else + { + if (k & 0x80) // Break code + { + k &= 0x7f; + +// DEBUG - handle special keys: ctl-alt-delete, print scrn + + Keyboard[k] = false; + } + else // Make code + { + LastCode = CurCode; + CurCode = LastScan = k; + Keyboard[k] = true; + + if (special) + c = SpecialNames[k]; + else + { + if (k == sc_CapsLock) + { + CapsLock ^= true; + // DEBUG - make caps lock light work + } + + if (Keyboard[sc_LShift] || Keyboard[sc_RShift]) // If shifted + { + c = ShiftNames[k]; + if ((c >= 'A') && (c <= 'Z') && CapsLock) + c += 'a' - 'A'; + } + else + { + c = ASCIINames[k]; + if ((c >= 'a') && (c <= 'z') && CapsLock) + c -= 'a' - 'A'; + } + } + if (c) + LastASCII = c; + } + + special = false; + } + + if (INL_KeyHook && !special) + INL_KeyHook(); + outp(0x20,0x20); +} + +/////////////////////////////////////////////////////////////////////////// +// +// INL_GetMouseDelta() - Gets the amount that the mouse has moved from the +// mouse driver +// +/////////////////////////////////////////////////////////////////////////// +static void +INL_GetMouseDelta(int *x,int *y) +{ + Mouse(MDelta); + *x = _CX; + *y = _DX; +} + +/////////////////////////////////////////////////////////////////////////// +// +// INL_GetMouseButtons() - Gets the status of the mouse buttons from the +// mouse driver +// +/////////////////////////////////////////////////////////////////////////// +static word +INL_GetMouseButtons(void) +{ + word buttons; + + Mouse(MButtons); + buttons = _BX; + return(buttons); +} + +/////////////////////////////////////////////////////////////////////////// +// +// IN_GetJoyAbs() - Reads the absolute position of the specified joystick +// +/////////////////////////////////////////////////////////////////////////// +void +IN_GetJoyAbs(word joy,word *xp,word *yp) +{ + byte xb,yb, + xs,ys; + word x,y; + + x = y = 0; + xs = joy? 2 : 0; // Find shift value for x axis + xb = 1 << xs; // Use shift value to get x bit mask + ys = joy? 3 : 1; // Do the same for y axis + yb = 1 << ys; + +// Read the absolute joystick values + __asm + { + pushf // Save some registers + push si + push di + cli // Make sure an interrupt doesn't screw the timings + + + mov dx,0x201 + in al,dx + out dx,al // Clear the resistors + + mov ah,[xb] // Get masks into registers + mov ch,[yb] + + xor si,si // Clear count registers + xor di,di + xor bh,bh // Clear high byte of bx for later + + push bp // Don't mess up stack frame + mov bp,MaxJoyValue + +loop: + in al,dx // Get bits indicating whether all are finished + + dec bp // Check bounding register + jz done // We have a silly value - abort + + mov bl,al // Duplicate the bits + and bl,ah // Mask off useless bits (in [xb]) + add si,bx // Possibly increment count register + mov cl,bl // Save for testing later + + mov bl,al + and bl,ch // [yb] + add di,bx + + add cl,bl + jnz loop // If both bits were 0, drop out + +done: + pop bp + + mov cl,[xs] // Get the number of bits to shift + shr si,cl // and shift the count that many times + + mov cl,[ys] + shr di,cl + + mov [x],si // Store the values into the variables + mov [y],di + + pop di + pop si + popf // Restore the registers + } + + *xp = x; + *yp = y; +} + +/////////////////////////////////////////////////////////////////////////// +// +// INL_GetJoyDelta() - Returns the relative movement of the specified +// joystick (from +/-127, scaled adaptively) +// +/////////////////////////////////////////////////////////////////////////// +static void +INL_GetJoyDelta(word joy,int *dx,int *dy,boolean adaptive) +{ + word x,y; + dword time; + JoystickDef *def; +static dword lasttime; + + IN_GetJoyAbs(joy,&x,&y); + def = JoyDefs + joy; + + if (x < def->threshMinX) + { + if (x < def->joyMinX) + x = def->joyMinX; + + x = -(x - def->threshMinX); + x *= def->joyMultXL; + x >>= JoyScaleShift; + *dx = (x > 127)? -127 : -x; + } + else if (x > def->threshMaxX) + { + if (x > def->joyMaxX) + x = def->joyMaxX; + + x = x - def->threshMaxX; + x *= def->joyMultXH; + x >>= JoyScaleShift; + *dx = (x > 127)? 127 : x; + } + else + *dx = 0; + + if (y < def->threshMinY) + { + if (y < def->joyMinY) + y = def->joyMinY; + + y = -(y - def->threshMinY); + y *= def->joyMultYL; + y >>= JoyScaleShift; + *dy = (y > 127)? -127 : -y; + } + else if (y > def->threshMaxY) + { + if (y > def->joyMaxY) + y = def->joyMaxY; + + y = y - def->threshMaxY; + y *= def->joyMultYH; + y >>= JoyScaleShift; + *dy = (y > 127)? 127 : y; + } + else + *dy = 0; + + if (adaptive) + { + time = (TimeCount - lasttime) / 2; + if (time) + { + if (time > 8) + time = 8; + *dx *= time; + *dy *= time; + } + } + lasttime = TimeCount; +} + +/////////////////////////////////////////////////////////////////////////// +// +// INL_GetJoyButtons() - Returns the button status of the specified +// joystick +// +/////////////////////////////////////////////////////////////////////////// +static word +INL_GetJoyButtons(word joy) +{ +register word result; + + result = inp(0x201); // Get all the joystick buttons + result >>= joy? 6 : 4; // Shift into bits 0-1 + result &= 3; // Mask off the useless bits + result ^= 3; + return(result); +} + +/////////////////////////////////////////////////////////////////////////// +// +// IN_GetJoyButtonsDB() - Returns the de-bounced button status of the +// specified joystick +// +/////////////////////////////////////////////////////////////////////////// +word +IN_GetJoyButtonsDB(word joy) +{ + dword lasttime; + word result1,result2; + + do + { + result1 = INL_GetJoyButtons(joy); + lasttime = TimeCount; + while (TimeCount == lasttime) + ; + result2 = INL_GetJoyButtons(joy); + } while (result1 != result2); + return(result1); +} + +/////////////////////////////////////////////////////////////////////////// +// +// INL_StartKbd() - Sets up my keyboard stuff for use +// +/////////////////////////////////////////////////////////////////////////// +static void +INL_StartKbd(void) +{ + INL_KeyHook = 0; // Clear key hook + + IN_ClearKeysDown(); + + OldKeyVect = getvect(KeyInt); + setvect(KeyInt,INL_KeyService); +} + +/////////////////////////////////////////////////////////////////////////// +// +// INL_ShutKbd() - Restores keyboard control to the BIOS +// +/////////////////////////////////////////////////////////////////////////// +static void +INL_ShutKbd(void) +{ + poke(0x40,0x17,peek(0x40,0x17) & 0xfaf0); // Clear ctrl/alt/shift flags + + setvect(KeyInt,OldKeyVect); +} + +/////////////////////////////////////////////////////////////////////////// +// +// INL_StartMouse() - Detects and sets up the mouse +// +/////////////////////////////////////////////////////////////////////////// +static boolean +INL_StartMouse(void) +{ + if (getvect(MouseInt)) + { + Mouse(MReset); + if (_AX == 0xffff) + return(true); + } + return(false); +} + +/////////////////////////////////////////////////////////////////////////// +// +// INL_ShutMouse() - Cleans up after the mouse +// +/////////////////////////////////////////////////////////////////////////// +static void +INL_ShutMouse(void) +{ +} + +// +// INL_SetJoyScale() - Sets up scaling values for the specified joystick +// +static void +INL_SetJoyScale(word joy) +{ + JoystickDef *def; + + def = &JoyDefs[joy]; + def->joyMultXL = JoyScaleMax / (def->threshMinX - def->joyMinX); + def->joyMultXH = JoyScaleMax / (def->joyMaxX - def->threshMaxX); + def->joyMultYL = JoyScaleMax / (def->threshMinY - def->joyMinY); + def->joyMultYH = JoyScaleMax / (def->joyMaxY - def->threshMaxY); +} + +/////////////////////////////////////////////////////////////////////////// +// +// IN_SetupJoy() - Sets up thresholding values and calls INL_SetJoyScale() +// to set up scaling values +// +/////////////////////////////////////////////////////////////////////////// +void +IN_SetupJoy(word joy,word minx,word maxx,word miny,word maxy) +{ + word d,r; + JoystickDef *def; + + def = &JoyDefs[joy]; + + def->joyMinX = minx; + def->joyMaxX = maxx; + r = maxx - minx; + d = r / 5; + def->threshMinX = ((r / 2) - d) + minx; + def->threshMaxX = ((r / 2) + d) + minx; + + def->joyMinY = miny; + def->joyMaxY = maxy; + r = maxy - miny; + d = r / 5; + def->threshMinY = ((r / 2) - d) + miny; + def->threshMaxY = ((r / 2) + d) + miny; + + INL_SetJoyScale(joy); +} + +/////////////////////////////////////////////////////////////////////////// +// +// INL_StartJoy() - Detects & auto-configures the specified joystick +// The auto-config assumes the joystick is centered +// +/////////////////////////////////////////////////////////////////////////// +static boolean +INL_StartJoy(word joy) +{ + word x,y; + + IN_GetJoyAbs(joy,&x,&y); + + if + ( + ((x == 0) || (x > MaxJoyValue - 10)) + || ((y == 0) || (y > MaxJoyValue - 10)) + ) + return(false); + else + { + IN_SetupJoy(joy,0,x * 2,0,y * 2); + return(true); + } +} + +/////////////////////////////////////////////////////////////////////////// +// +// INL_ShutJoy() - Cleans up the joystick stuff +// +/////////////////////////////////////////////////////////////////////////// +static void +INL_ShutJoy(word joy) +{ + JoysPresent[joy] = false; +} + +// Public routines + +/////////////////////////////////////////////////////////////////////////// +// +// IN_Startup() - Starts up the Input Mgr +// +/////////////////////////////////////////////////////////////////////////// +void +IN_Startup(void) +{ + boolean checkjoys,checkmouse; + word i; + + if (IN_Started) + return; + + checkjoys = true; + checkmouse = true; + for (i = 1;i < _argc;i++) + { + switch (US_CheckParm(_argv[i],ParmStrings)) + { + case 0: + checkjoys = false; + break; + case 1: + checkmouse = false; + break; + } + } + + INL_StartKbd(); + MousePresent = checkmouse? INL_StartMouse() : false; + + for (i = 0;i < MaxJoys;i++) + JoysPresent[i] = checkjoys? INL_StartJoy(i) : false; + + IN_Started = true; +} + +/////////////////////////////////////////////////////////////////////////// +// +// IN_Default() - Sets up default conditions for the Input Mgr +// +/////////////////////////////////////////////////////////////////////////// +void +IN_Default(boolean gotit,ControlType in) +{ + if + ( + (!gotit) + || ((in == ctrl_Joystick1) && !JoysPresent[0]) + || ((in == ctrl_Joystick2) && !JoysPresent[1]) + || ((in == ctrl_Mouse) && !MousePresent) + ) + in = ctrl_Keyboard1; + IN_SetControlType(0,in); +} + +/////////////////////////////////////////////////////////////////////////// +// +// IN_Shutdown() - Shuts down the Input Mgr +// +/////////////////////////////////////////////////////////////////////////// +void +IN_Shutdown(void) +{ + word i; + + if (!IN_Started) + return; + + INL_ShutMouse(); + for (i = 0;i < MaxJoys;i++) + INL_ShutJoy(i); + INL_ShutKbd(); + + IN_Started = false; +} + +/////////////////////////////////////////////////////////////////////////// +// +// IN_SetKeyHook() - Sets the routine that gets called by INL_KeyService() +// everytime a real make/break code gets hit +// +/////////////////////////////////////////////////////////////////////////// +void +IN_SetKeyHook(void (*hook)()) +{ + INL_KeyHook = hook; +} + +/////////////////////////////////////////////////////////////////////////// +// +// IN_ClearKeyDown() - Clears the keyboard array +// +/////////////////////////////////////////////////////////////////////////// +void +IN_ClearKeysDown(void) +{ + int i; + + LastScan = sc_None; + LastASCII = key_None; + for (i = 0;i < NumCodes;i++) + Keyboard[i] = false; +} + +/////////////////////////////////////////////////////////////////////////// +// +// INL_AdjustCursor() - Internal routine of common code from IN_ReadCursor() +// +/////////////////////////////////////////////////////////////////////////// +static void +INL_AdjustCursor(CursorInfo *info,word buttons,int dx,int dy) +{ + if (buttons & (1 << 0)) + info->button0 = true; + if (buttons & (1 << 1)) + info->button1 = true; + + info->x += dx; + info->y += dy; +} + +/////////////////////////////////////////////////////////////////////////// +// +// IN_ReadCursor() - Reads the input devices and fills in the cursor info +// struct +// +/////////////////////////////////////////////////////////////////////////// +void +IN_ReadCursor(CursorInfo *info) +{ + word i, + buttons; + int dx,dy; + + info->x = info->y = 0; + info->button0 = info->button1 = false; + + if (MousePresent) + { + buttons = INL_GetMouseButtons(); + INL_GetMouseDelta(&dx,&dy); + INL_AdjustCursor(info,buttons,dx,dy); + } + + for (i = 0;i < MaxJoys;i++) + { + if (!JoysPresent[i]) + continue; + + buttons = INL_GetJoyButtons(i); + INL_GetJoyDelta(i,&dx,&dy,true); + dx /= 64; + dy /= 64; + INL_AdjustCursor(info,buttons,dx,dy); + } +} + +/////////////////////////////////////////////////////////////////////////// +// +// IN_ReadControl() - Reads the device associated with the specified +// player and fills in the control info struct +// +/////////////////////////////////////////////////////////////////////////// +void +IN_ReadControl(int player,ControlInfo *info) +{ + boolean realdelta=false; // MDM (GAMERS EDGE) + byte dbyte; + word buttons; + int dx,dy; + Motion mx,my; + ControlType type; +register KeyboardDef *def; + + dx = dy = 0; + mx = my = motion_None; + buttons = 0; + +#if 0 + if (DemoMode == demo_Playback) + { + dbyte = DemoBuffer[DemoOffset + 1]; + my = (dbyte & 3) - 1; + mx = ((dbyte >> 2) & 3) - 1; + buttons = (dbyte >> 4) & 3; + + if (!(--DemoBuffer[DemoOffset])) + { + DemoOffset += 2; + if (DemoOffset >= DemoSize) + DemoMode = demo_PlayDone; + } + + realdelta = false; + } + else if (DemoMode == demo_PlayDone) + Quit("Demo playback exceeded"); + else +#endif + { + // MDM begin (GAMERS EDGE) - added this block + ControlTypeUsed = ctrl_None; + + // Handle mouse input... + // + if ((MousePresent) && (ControlTypeUsed == ctrl_None)) + { + INL_GetMouseDelta(&dx,&dy); + buttons = INL_GetMouseButtons(); + realdelta = true; + if (dx || dy || buttons) + ControlTypeUsed = ctrl_Mouse; + } + + // Handle joystick input... + // + if ((JoystickCalibrated) && (ControlTypeUsed == ctrl_None)) + { + type = ctrl_Joystick1; + INL_GetJoyDelta(type - ctrl_Joystick,&dx,&dy,false); + buttons = INL_GetJoyButtons(type - ctrl_Joystick); + realdelta = true; + if (dx || dy || buttons) + ControlTypeUsed = ctrl_Joystick; + } + + // Handle keyboard input... + // + if (ControlTypeUsed == ctrl_None) + { + type = ctrl_Keyboard1; + def = &KbdDefs[type - ctrl_Keyboard]; + + if (Keyboard[def->upleft]) + mx = motion_Left,my = motion_Up; + else if (Keyboard[def->upright]) + mx = motion_Right,my = motion_Up; + else if (Keyboard[def->downleft]) + mx = motion_Left,my = motion_Down; + else if (Keyboard[def->downright]) + mx = motion_Right,my = motion_Down; + + if (Keyboard[def->up]) + my = motion_Up; + else if (Keyboard[def->down]) + my = motion_Down; + + if (Keyboard[def->left]) + mx = motion_Left; + else if (Keyboard[def->right]) + mx = motion_Right; + + if (Keyboard[def->button0]) + buttons += 1 << 0; + if (Keyboard[def->button1]) + buttons += 1 << 1; + realdelta = false; + if (mx || my || buttons) + ControlTypeUsed = ctrl_Keyboard; + } // MDM end (GAMERS EDGE) + } + + if (realdelta) + { + mx = (dx < 0)? motion_Left : ((dx > 0)? motion_Right : motion_None); + my = (dy < 0)? motion_Up : ((dy > 0)? motion_Down : motion_None); + } + else + { + dx = mx * 127; + dy = my * 127; + } + + info->x = dx; + info->xaxis = mx; + info->y = dy; + info->yaxis = my; + info->button0 = buttons & (1 << 0); + info->button1 = buttons & (1 << 1); + info->dir = DirTable[((my + 1) * 3) + (mx + 1)]; + +#if 0 + if (DemoMode == demo_Record) + { + // Pack the control info into a byte + dbyte = (buttons << 4) | ((mx + 1) << 2) | (my + 1); + + if + ( + (DemoBuffer[DemoOffset + 1] == dbyte) + && (DemoBuffer[DemoOffset] < 255) + ) + (DemoBuffer[DemoOffset])++; + else + { + if (DemoOffset || DemoBuffer[DemoOffset]) + DemoOffset += 2; + + if (DemoOffset >= DemoSize) + Quit("Demo buffer overflow"); + + DemoBuffer[DemoOffset] = 1; + DemoBuffer[DemoOffset + 1] = dbyte; + } + } +#endif +} + +#if 0 +/////////////////////////////////////////////////////////////////////////// +// +// IN_ReadControl() - Reads the device associated with the specified +// player and fills in the control info struct +// +/////////////////////////////////////////////////////////////////////////// +void +IN_ReadControl(int player,ControlInfo *info) +{ + boolean realdelta; + byte dbyte; + word buttons; + int dx,dy; + Motion mx,my; + ControlType type; +register KeyboardDef *def; + + dx = dy = 0; + mx = my = motion_None; + buttons = 0; + +#if 0 + if (DemoMode == demo_Playback) + { + dbyte = DemoBuffer[DemoOffset + 1]; + my = (dbyte & 3) - 1; + mx = ((dbyte >> 2) & 3) - 1; + buttons = (dbyte >> 4) & 3; + + if (!(--DemoBuffer[DemoOffset])) + { + DemoOffset += 2; + if (DemoOffset >= DemoSize) + DemoMode = demo_PlayDone; + } + + realdelta = false; + } + else if (DemoMode == demo_PlayDone) + Quit("Demo playback exceeded"); + else +#endif + { + switch (type = Controls[player]) + { + case ctrl_Keyboard1: + case ctrl_Keyboard2: + def = &KbdDefs[type - ctrl_Keyboard]; + + if (Keyboard[def->upleft]) + mx = motion_Left,my = motion_Up; + else if (Keyboard[def->upright]) + mx = motion_Right,my = motion_Up; + else if (Keyboard[def->downleft]) + mx = motion_Left,my = motion_Down; + else if (Keyboard[def->downright]) + mx = motion_Right,my = motion_Down; + + if (Keyboard[def->up]) + my = motion_Up; + else if (Keyboard[def->down]) + my = motion_Down; + + if (Keyboard[def->left]) + mx = motion_Left; + else if (Keyboard[def->right]) + mx = motion_Right; + + if (Keyboard[def->button0]) + buttons += 1 << 0; + if (Keyboard[def->button1]) + buttons += 1 << 1; + realdelta = false; + break; + case ctrl_Joystick1: + case ctrl_Joystick2: + INL_GetJoyDelta(type - ctrl_Joystick,&dx,&dy,false); + buttons = INL_GetJoyButtons(type - ctrl_Joystick); + realdelta = true; + break; + case ctrl_Mouse: + INL_GetMouseDelta(&dx,&dy); + buttons = INL_GetMouseButtons(); + realdelta = true; + break; + } + } + + if (realdelta) + { + mx = (dx < 0)? motion_Left : ((dx > 0)? motion_Right : motion_None); + my = (dy < 0)? motion_Up : ((dy > 0)? motion_Down : motion_None); + } + else + { + dx = mx * 127; + dy = my * 127; + } + + info->x = dx; + info->xaxis = mx; + info->y = dy; + info->yaxis = my; + info->button0 = buttons & (1 << 0); + info->button1 = buttons & (1 << 1); + info->dir = DirTable[((my + 1) * 3) + (mx + 1)]; + +#if 0 + if (DemoMode == demo_Record) + { + // Pack the control info into a byte + dbyte = (buttons << 4) | ((mx + 1) << 2) | (my + 1); + + if + ( + (DemoBuffer[DemoOffset + 1] == dbyte) + && (DemoBuffer[DemoOffset] < 255) + ) + (DemoBuffer[DemoOffset])++; + else + { + if (DemoOffset || DemoBuffer[DemoOffset]) + DemoOffset += 2; + + if (DemoOffset >= DemoSize) + Quit("Demo buffer overflow"); + + DemoBuffer[DemoOffset] = 1; + DemoBuffer[DemoOffset + 1] = dbyte; + } + } +#endif +} +#endif + +/////////////////////////////////////////////////////////////////////////// +// +// IN_SetControlType() - Sets the control type to be used by the specified +// player +// +/////////////////////////////////////////////////////////////////////////// +void +IN_SetControlType(int player,ControlType type) +{ + // DEBUG - check that requested type is present? + Controls[player] = type; +} + +#if 0 +/////////////////////////////////////////////////////////////////////////// +// +// IN_StartDemoRecord() - Starts the demo recording, using a buffer the +// size passed. Returns if the buffer allocation was successful +// +/////////////////////////////////////////////////////////////////////////// +boolean +IN_StartDemoRecord(word bufsize) +{ + if (!bufsize) + return(false); + + MM_GetPtr((memptr *)&DemoBuffer,bufsize); + DemoMode = demo_Record; + DemoSize = bufsize & ~1; + DemoOffset = 0; + DemoBuffer[0] = DemoBuffer[1] = 0; + + return(true); +} + +/////////////////////////////////////////////////////////////////////////// +// +// IN_StartDemoPlayback() - Plays back the demo pointed to of the given size +// +/////////////////////////////////////////////////////////////////////////// +void +IN_StartDemoPlayback(byte /*_1seg*/ *buffer,word bufsize) +{ + DemoBuffer = buffer; + DemoMode = demo_Playback; + DemoSize = bufsize & ~1; + DemoOffset = 0; +} + +/////////////////////////////////////////////////////////////////////////// +// +// IN_StopDemo() - Turns off demo mode +// +/////////////////////////////////////////////////////////////////////////// +void +IN_StopDemo(void) +{ + if ((DemoMode == demo_Record) && DemoOffset) + DemoOffset += 2; + + DemoMode = demo_Off; +} + +/////////////////////////////////////////////////////////////////////////// +// +// IN_FreeDemoBuffer() - Frees the demo buffer, if it's been allocated +// +/////////////////////////////////////////////////////////////////////////// +void +IN_FreeDemoBuffer(void) +{ + if (DemoBuffer) + MM_FreePtr((memptr *)&DemoBuffer); +} +#endif + + +#if 0 +/////////////////////////////////////////////////////////////////////////// +// +// IN_GetScanName() - Returns a string containing the name of the +// specified scan code +// +/////////////////////////////////////////////////////////////////////////// +byte * +IN_GetScanName(ScanCode scan) +{ + byte **p; + ScanCode far *s; + + for (s = ExtScanCodes,p = ExtScanNames;*s;p++,s++) + if (*s == scan) + return(*p); + + return(ScanNames[scan]); +} +#endif + + +/////////////////////////////////////////////////////////////////////////// +// +// IN_WaitForKey() - Waits for a scan code, then clears LastScan and +// returns the scan code +// +/////////////////////////////////////////////////////////////////////////// +ScanCode +IN_WaitForKey(void) +{ + ScanCode result; + + while (!(result = LastScan)) + ; + LastScan = 0; + return(result); +} + +/////////////////////////////////////////////////////////////////////////// +// +// IN_WaitForASCII() - Waits for an ASCII char, then clears LastASCII and +// returns the ASCII value +// +/////////////////////////////////////////////////////////////////////////// +char +IN_WaitForASCII(void) +{ + char result; + + while (!(result = LastASCII)) + ; + LastASCII = '\0'; + return(result); +} + +/////////////////////////////////////////////////////////////////////////// +// +// IN_AckBack() - Waits for either an ASCII keypress or a button press +// +/////////////////////////////////////////////////////////////////////////// +void +IN_AckBack(void) +{ + word i; + + while (!LastScan) + { + if (MousePresent) + { + if (INL_GetMouseButtons()) + { + while (INL_GetMouseButtons()) + ; + return; + } + } + + for (i = 0;i < MaxJoys;i++) + { + if (JoysPresent[i]) + { + if (IN_GetJoyButtonsDB(i)) + { + while (IN_GetJoyButtonsDB(i)) + ; + return; + } + } + } + } + + IN_ClearKey(LastScan); + LastScan = sc_None; +} + +/////////////////////////////////////////////////////////////////////////// +// +// IN_Ack() - Clears user input & then calls IN_AckBack() +// +/////////////////////////////////////////////////////////////////////////// +void +IN_Ack(void) +{ + word i; + + IN_ClearKey(LastScan); + LastScan = sc_None; + + if (MousePresent) + while (INL_GetMouseButtons()) + ; + for (i = 0;i < MaxJoys;i++) + if (JoysPresent[i]) + while (IN_GetJoyButtonsDB(i)) + ; + + IN_AckBack(); +} + +/////////////////////////////////////////////////////////////////////////// +// +// IN_IsUserInput() - Returns true if a key has been pressed or a button +// is down +// +/////////////////////////////////////////////////////////////////////////// +boolean +IN_IsUserInput(void) +{ + boolean result; + word i; + + result = LastScan; + + if (MousePresent) + if (INL_GetMouseButtons()) + result = true; + + for (i = 0;i < MaxJoys;i++) + if (JoysPresent[i]) + if (INL_GetJoyButtons(i)) + result = true; + + return(result); +} + +/////////////////////////////////////////////////////////////////////////// +// +// IN_UserInput() - Waits for the specified delay time (in ticks) or the +// user pressing a key or a mouse button. If the clear flag is set, it +// then either clears the key or waits for the user to let the mouse +// button up. +// +/////////////////////////////////////////////////////////////////////////// +boolean +IN_UserInput(dword delay,boolean clear) +{ + dword lasttime; + + lasttime = TimeCount; + do + { + if (IN_IsUserInput()) + { + if (clear) + IN_AckBack(); + return(true); + } + } while (TimeCount - lasttime < delay); + return(false); +} diff --git a/16/cawat/16_in.h b/16/cawat/16_in.h new file mode 100644 index 00000000..ba1e1386 --- /dev/null +++ b/16/cawat/16_in.h @@ -0,0 +1,214 @@ +/* Catacomb Armageddon Source Code + * Copyright (C) 1993-2014 Flat Rock Software + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +// +// ID Engine +// ID_IN.h - Header file for Input Manager +// v1.0d1 +// By Jason Blochowiak +// + +#include "lib_head.h" + +#ifndef __TYPES__ +//#include "ID_Types.h" +#endif + +#ifndef __ID_IN__ +#define __ID_IN__ + +#ifdef __DEBUG__ +#define __DEBUG_InputMgr__ +#endif + +#define MaxPlayers 4 +#define MaxKbds 2 +#define MaxJoys 2 +#define NumCodes 128 + +typedef byte ScanCode; +#define sc_None 0 +#define sc_Bad 0xff +#define sc_Return 0x1c +#define sc_Enter sc_Return +#define sc_Escape 0x01 +#define sc_Space 0x39 +#define sc_BackSpace 0x0e +#define sc_Tab 0x0f +#define sc_Alt 0x38 +#define sc_Control 0x1d +#define sc_CapsLock 0x3a +#define sc_LShift 0x2a +#define sc_RShift 0x36 +#define sc_UpArrow 0x48 +#define sc_DownArrow 0x50 +#define sc_LeftArrow 0x4b +#define sc_RightArrow 0x4d +#define sc_Insert 0x52 +#define sc_Delete 0x53 +#define sc_Home 0x47 +#define sc_End 0x4f +#define sc_PgUp 0x49 +#define sc_PgDn 0x51 +#define sc_F1 0x3b +#define sc_F2 0x3c +#define sc_F3 0x3d +#define sc_F4 0x3e +#define sc_F5 0x3f +#define sc_F6 0x40 +#define sc_F7 0x41 +#define sc_F8 0x42 +#define sc_F9 0x43 +#define sc_F10 0x44 +#define sc_F11 0x57 +#define sc_F12 0x59 + +#define sc_A 0x1e +#define sc_B 0x30 +#define sc_C 0x2e +#define sc_D 0x20 +#define sc_E 0x12 +#define sc_F 0x21 +#define sc_G 0x22 +#define sc_H 0x23 +#define sc_I 0x17 +#define sc_J 0x24 +#define sc_K 0x25 +#define sc_L 0x26 +#define sc_M 0x32 +#define sc_N 0x31 +#define sc_O 0x18 +#define sc_P 0x19 +#define sc_Q 0x10 +#define sc_R 0x13 +#define sc_S 0x1f +#define sc_T 0x14 +#define sc_U 0x16 +#define sc_V 0x2f +#define sc_W 0x11 +#define sc_X 0x2d +#define sc_Y 0x15 +#define sc_Z 0x2c + +#define key_None 0 +#define key_Return 0x0d +#define key_Enter key_Return +#define key_Escape 0x1b +#define key_Space 0x20 +#define key_BackSpace 0x08 +#define key_Tab 0x09 +#define key_Delete 0x7f + +// Stuff for the mouse +#define MReset 0 +#define MButtons 3 +#define MDelta 11 + +#define MouseInt 0x33 +#define Mouse(x) _AX = x,_dos_geninterrupt(MouseInt) + +typedef enum { + demo_Off,demo_Record,demo_Playback,demo_PlayDone + } Demo; +typedef enum { + ctrl_None, // MDM (GAMERS EDGE) - added + ctrl_Keyboard, + ctrl_Keyboard1 = ctrl_Keyboard,ctrl_Keyboard2, + ctrl_Joystick, + ctrl_Joystick1 = ctrl_Joystick,ctrl_Joystick2, + ctrl_Mouse + } ControlType; +typedef enum { + motion_Left = -1,motion_Up = -1, + motion_None = 0, + motion_Right = 1,motion_Down = 1 + } Motion; +typedef enum { + dir_North,dir_NorthEast, + dir_East,dir_SouthEast, + dir_South,dir_SouthWest, + dir_West,dir_NorthWest, + dir_None + } Direction; +typedef struct { + boolean button0,button1; + int x,y; + Motion xaxis,yaxis; + Direction dir; + } CursorInfo; +typedef CursorInfo ControlInfo; +typedef struct { + ScanCode button0,button1, + upleft, up, upright, + left, right, + downleft, down, downright; + } KeyboardDef; +typedef struct { + word joyMinX,joyMinY, + threshMinX,threshMinY, + threshMaxX,threshMaxY, + joyMaxX,joyMaxY, + joyMultXL,joyMultYL, + joyMultXH,joyMultYH; + } JoystickDef; +// Global variables +extern boolean Keyboard[], + MousePresent, + JoysPresent[]; +extern boolean Paused; +extern char LastASCII; +extern ScanCode LastScan; +extern KeyboardDef KbdDefs[]; +extern JoystickDef JoyDefs[]; +extern ControlType Controls[MaxPlayers]; + +extern boolean JoystickCalibrated; // MDM (GAMERS EDGE) - added +extern ControlType ControlTypeUsed; // MDM (GAMERS EDGE) - added + +extern Demo DemoMode; +extern byte /*_seg*/ *DemoBuffer; +extern word DemoOffset,DemoSize; + +// Function prototypes +#define IN_KeyDown(code) (Keyboard[(code)]) +#define IN_ClearKey(code) {Keyboard[code] = false;\ + if (code == LastScan) LastScan = sc_None;} + +// DEBUG - put names in prototypes +extern void IN_Startup(void),IN_Shutdown(void), + IN_Default(boolean gotit,ControlType in), + IN_SetKeyHook(void (*)()), + IN_ClearKeysDown(void), + IN_ReadCursor(CursorInfo *), + IN_ReadControl(int,ControlInfo *), + IN_SetControlType(int,ControlType), + IN_GetJoyAbs(word joy,word *xp,word *yp), + IN_SetupJoy(word joy,word minx,word maxx, + word miny,word maxy), + IN_StartDemoPlayback(byte /*_seg*/ *buffer,word bufsize), + IN_StopDemo(void),IN_FreeDemoBuffer(void), + IN_Ack(void),IN_AckBack(void); +extern boolean IN_UserInput(dword delay,boolean clear), + IN_IsUserInput(void), + IN_StartDemoRecord(word bufsize); +extern byte *IN_GetScanName(ScanCode); +extern char IN_WaitForASCII(void); +extern ScanCode IN_WaitForKey(void); +extern word IN_GetJoyButtonsDB(word joy); + +#endif diff --git a/16/cawat/16_mm.c b/16/cawat/16_mm.c new file mode 100644 index 00000000..95e394b0 --- /dev/null +++ b/16/cawat/16_mm.c @@ -0,0 +1,1116 @@ +/* Catacomb Armageddon Source Code + * Copyright (C) 1993-2014 Flat Rock Software + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +// NEWMM.C + +/* +============================================================================= + + ID software memory manager + -------------------------- + +Primary coder: John Carmack + +RELIES ON +--------- +Quit (char *error) function + + +WORK TO DO +---------- +MM_SizePtr to change the size of a given pointer + +Multiple purge levels utilized + +EMS / XMS unmanaged routines + +============================================================================= +*/ + +//#include "LIB_HEAD.H" +#include "16_mm.h" + +/* +============================================================================= + + LOCAL INFO + +============================================================================= +*/ + +#define LOCKBIT 0x80 // if set in attributes, block cannot be moved +#define PURGEBITS 3 // 0-3 level, 0= unpurgable, 3= purge first +#define PURGEMASK 0xfffc +#define BASEATTRIBUTES 0 // unlocked, non purgable + +#define MAXUMBS 10 + +typedef struct mmblockstruct +{ + unsigned start,length; + unsigned attributes; + memptr *useptr; // pointer to the segment start + struct mmblockstruct far *next; +} mmblocktype; + + +//#define GETNEWBLOCK {if(!(mmnew=mmfree))Quit("MM_GETNEWBLOCK: No free blocks!");mmfree=mmfree->next;} +#define GETNEWBLOCK {if(!mmfree)MML_ClearBlock();mmnew=mmfree;mmfree=mmfree->next;} +#define FREEBLOCK(x) {*x->useptr=NULL;x->next=mmfree;mmfree=x;} + +/* +============================================================================= + + GLOBAL VARIABLES + +============================================================================= +*/ + +mminfotype mminfo; +memptr bufferseg; +boolean mmerror; + +void (* beforesort) (void); +void (* aftersort) (void); + +/* +============================================================================= + + LOCAL VARIABLES + +============================================================================= +*/ + +boolean mmstarted; + +void huge *hugeheap; +void far *farheap; +void *nearheap; + +mmblocktype far mmblocks[MAXBLOCKS] + ,far *mmhead,far *mmfree,far *mmrover,far *mmnew; + +boolean bombonerror; + +unsigned totalEMSpages,freeEMSpages,EMSpageframe,EMSpagesmapped,EMShandle; + +void (* XMSaddr) (void); // far pointer to XMS driver + +unsigned numUMBs,UMBbase[MAXUMBS]; + + +/* +====================== += += MML_CheckForEMS += += Routine from p36 of Extending DOS += +======================= +*/ + +boolean MML_CheckForEMS (void) +{ + boolean emmcfems; + char emmname[] = "EMMXXXX0"; + + __asm { + mov dx,OFF emmname + mov ax,0x3d00 + int 0x21 // try to open EMMXXXX0 device + jc error + + mov bx,ax + mov ax,0x4400 + + int 0x21 // get device info + jc error + + and dx,0x80 + jz error + + mov ax,0x4407 + + int 0x21 // get status + jc error + or al,al + jz error + + mov ah,0x3e + int 0x21 // close handle + jc error + // + // EMS is good + // + mov emmcfems,1 + jmp End + error: + // + // EMS is bad + // + mov emmcfems,0 + End: + } + return(emmcfems); +} + + +/* +====================== += += MML_SetupEMS += +======================= +*/ + +void MML_SetupEMS (void) +{ + char str[80],str2[10]; + unsigned error; + + totalEMSpages = freeEMSpages = EMSpageframe = EMSpagesmapped = 0; + + __asm { + mov ah,EMS_STATUS + int EMS_INT // make sure EMS hardware is present + or ah,ah + jnz error + + mov ah,EMS_VERSION + int EMS_INT + or ah,ah + jnz error + cmp al,0x32 // only work on ems 3.2 or greater + jb error + + mov ah,EMS_GETFRAME + int EMS_INT // find the page frame address + or ah,ah + jnz error + mov [EMSpageframe],bx + + mov ah,EMS_GETPAGES + int EMS_INT // find out how much EMS is there + or ah,ah + jnz error + mov [totalEMSpages],dx + mov [freeEMSpages],bx + or bx,bx + jz noEMS // no EMS at all to allocate + + cmp bx,4 + jle getpages // there is only 1,2,3,or 4 pages + mov bx,4 // we can't use more than 4 pages + } + +getpages: +asm { + mov [EMSpagesmapped],bx + mov ah,EMS_ALLOCPAGES // allocate up to 64k of EMS + int EMS_INT + or ah,ah + jnz error + mov [EMShandle],dx + } + return; + +error: + error = _AH; + strcpy (str,"MML_SetupEMS: EMS error 0x"); + itoa(error,str2,16); + strcpy (str,str2); + Quit (str); + +noEMS: +; +} + + +/* +====================== += += MML_ShutdownEMS += +======================= +*/ + +void MML_ShutdownEMS (void) +{ + if (!EMShandle) + return; + + { + mov ah,EMS_FREEPAGES + mov dx,[EMShandle] + int EMS_INT + or ah,ah + jz ok + } + + Quit ("MML_ShutdownEMS: Error freeing EMS!"); + +ok: +; +} + +/* +==================== += += MM_MapEMS += += Maps the 64k of EMS used by memory manager into the page frame += for general use. This only needs to be called if you are keeping += other things in EMS. += +==================== +*/ + +void MM_MapEMS (void) +{ + char str[80],str2[10]; + unsigned error; + int i; + + for (i=0;istart+scan->length < segstart) + { + last = scan; + scan = scan->next; + } + +// +// take the given range out of the block +// + oldend = scan->start + scan->length; + extra = oldend - (segstart+seglength); + if (extra < 0) + Quit ("MML_UseSpace: Segment spans two blocks!"); + + if (segstart == scan->start) + { + last->next = scan->next; // unlink block + FREEBLOCK(scan); + scan = last; + } + else + scan->length = segstart-scan->start; // shorten block + + if (extra > 0) + { + GETNEWBLOCK; + mmnew->next = scan->next; + scan->next = mmnew; + mmnew->start = segstart+seglength; + mmnew->length = extra; + mmnew->attributes = LOCKBIT; + } + +} + +//========================================================================== + +/* +==================== += += MML_ClearBlock += += We are out of blocks, so free a purgable block += +==================== +*/ + +void MML_ClearBlock (void) +{ + mmblocktype far *scan,far *last; + + scan = mmhead->next; + + while (scan) + { + if (!(scan->attributes&LOCKBIT) && (scan->attributes&PURGEBITS) ) + { + MM_FreePtr(scan->useptr); + return; + } + scan = scan->next; + } + + Quit ("MM_ClearBlock: No purgable blocks!"); +} + + +//========================================================================== + +/* +=================== += += MM_Startup += += Grabs all space from turbo with malloc/farmalloc += Allocates bufferseg misc buffer += +=================== +*/ + +static char *ParmStrings[] = {"noems","noxms",""}; + +void MM_Startup (void) +{ + int i; + unsigned long length; + void far *start; + unsigned segstart,seglength,endfree; + + if (mmstarted) + MM_Shutdown (); + + + mmstarted = true; + bombonerror = true; +// +// set up the linked list (everything in the free list; +// + mmhead = NULL; + mmfree = &mmblocks[0]; + for (i=0;istart = 0; + mmnew->length = 0xffff; + mmnew->attributes = LOCKBIT; + mmnew->next = NULL; + mmrover = mmhead; + + +// +// get all available near conventional memory segments +// + length=coreleft(); + start = (void far *)(nearheap = malloc(length)); + + length -= 16-(FP_OFF(start)&15); + length -= SAVENEARHEAP; + seglength = length / 16; // now in paragraphs + segstart = FP_SEG(start)+(FP_OFF(start)+15)/16; + MML_UseSpace (segstart,seglength); + mminfo.nearheap = length; + +// +// get all available far conventional memory segments +// + length=farcoreleft(); + start = farheap = farmalloc(length); + length -= 16-(FP_OFF(start)&15); + length -= SAVEFARHEAP; + seglength = length / 16; // now in paragraphs + segstart = FP_SEG(start)+(FP_OFF(start)+15)/16; + MML_UseSpace (segstart,seglength); + mminfo.farheap = length; + mminfo.mainmem = mminfo.nearheap + mminfo.farheap; + + +// +// detect EMS and allocate up to 64K at page frame +// + mminfo.EMSmem = 0; + for (i = 1;i < _argc;i++) + { + if ( US_CheckParm(_argv[i],ParmStrings) == 0) + goto emsskip; // param NOEMS + } + + if (MML_CheckForEMS()) + { + MML_SetupEMS(); // allocate space + MML_UseSpace (EMSpageframe,EMSpagesmapped*0x400); + MM_MapEMS(); // map in used pages + mminfo.EMSmem = EMSpagesmapped*0x4000l; + } + +// +// detect XMS and get upper memory blocks +// +emsskip: + mminfo.XMSmem = 0; + for (i = 1;i < _argc;i++) + { + if ( US_CheckParm(_argv[i],ParmStrings) == 0) + goto xmsskip; // param NOXMS + } + + if (MML_CheckForXMS()) + MML_SetupXMS(); // allocate as many UMBs as possible + +// +// allocate the misc buffer +// +xmsskip: + mmrover = mmhead; // start looking for space after low block + + MM_GetPtr (&bufferseg,BUFFERSIZE); +} + +//========================================================================== + +/* +==================== += += MM_Shutdown += += Frees all conventional, EMS, and XMS allocated += +==================== +*/ + +void MM_Shutdown (void) +{ + if (!mmstarted) + return; + + farfree (farheap); + free (nearheap); + MML_ShutdownEMS (); + MML_ShutdownXMS (); +} + +//========================================================================== + +/* +==================== += += MM_GetPtr += += Allocates an unlocked, unpurgable block += +==================== +*/ + +void MM_GetPtr (memptr *baseptr,unsigned long size) +{ + mmblocktype far *scan,far *lastscan,far *endscan + ,far *purge,far *next; + int search; + unsigned needed,startseg; + + needed = (size+15)/16; // convert size from bytes to paragraphs + + GETNEWBLOCK; // fill in start and next after a spot is found + mmnew->length = needed; + mmnew->useptr = baseptr; + mmnew->attributes = BASEATTRIBUTES; + + for (search = 0; search<3; search++) + { + // + // first search: try to allocate right after the rover, then on up + // second search: search from the head pointer up to the rover + // third search: compress memory, then scan from start + if (search == 1 && mmrover == mmhead) + search++; + + switch (search) + { + case 0: + lastscan = mmrover; + scan = mmrover->next; + endscan = NULL; + break; + case 1: + lastscan = mmhead; + scan = mmhead->next; + endscan = mmrover; + break; + case 2: + MM_SortMem (); + lastscan = mmhead; + scan = mmhead->next; + endscan = NULL; + break; + } + + startseg = lastscan->start + lastscan->length; + + while (scan != endscan) + { + if (scan->start - startseg >= needed) + { + // + // got enough space between the end of lastscan and + // the start of scan, so throw out anything in the middle + // and allocate the new block + // + purge = lastscan->next; + lastscan->next = mmnew; + mmnew->start = *(unsigned *)baseptr = startseg; + mmnew->next = scan; + while ( purge != scan) + { // free the purgable block + next = purge->next; + FREEBLOCK(purge); + purge = next; // purge another if not at scan + } + mmrover = mmnew; + return; // good allocation! + } + + // + // if this block is purge level zero or locked, skip past it + // + if ( (scan->attributes & LOCKBIT) + || !(scan->attributes & PURGEBITS) ) + { + lastscan = scan; + startseg = lastscan->start + lastscan->length; + } + + + scan=scan->next; // look at next line + } + } + + if (bombonerror) + Quit (OUT_OF_MEM_MSG,(size-mminfo.nearheap)); + else + mmerror = true; +} + +//========================================================================== + +/* +==================== += += MM_FreePtr += += Allocates an unlocked, unpurgable block += +==================== +*/ + +void MM_FreePtr (memptr *baseptr) +{ + mmblocktype far *scan,far *last; + + last = mmhead; + scan = last->next; + + if (baseptr == mmrover->useptr) // removed the last allocated block + mmrover = mmhead; + + while (scan->useptr != baseptr && scan) + { + last = scan; + scan = scan->next; + } + + if (!scan) + Quit ("MM_FreePtr: Block not found!"); + + last->next = scan->next; + + FREEBLOCK(scan); +} +//========================================================================== + +/* +===================== += += MM_SetPurge += += Sets the purge level for a block (locked blocks cannot be made purgable) += +===================== +*/ + +void MM_SetPurge (memptr *baseptr, int purge) +{ + mmblocktype far *start; + + start = mmrover; + + do + { + if (mmrover->useptr == baseptr) + break; + + mmrover = mmrover->next; + + if (!mmrover) + mmrover = mmhead; + else if (mmrover == start) + Quit ("MM_SetPurge: Block not found!"); + + } while (1); + + mmrover->attributes &= ~PURGEBITS; + mmrover->attributes |= purge; +} + +//========================================================================== + +/* +===================== += += MM_SetLock += += Locks / unlocks the block += +===================== +*/ + +void MM_SetLock (memptr *baseptr, boolean locked) +{ + mmblocktype far *start; + + start = mmrover; + + do + { + if (mmrover->useptr == baseptr) + break; + + mmrover = mmrover->next; + + if (!mmrover) + mmrover = mmhead; + else if (mmrover == start) + Quit ("MM_SetLock: Block not found!"); + + } while (1); + + mmrover->attributes &= ~LOCKBIT; + mmrover->attributes |= locked*LOCKBIT; +} + +//========================================================================== + +/* +===================== += += MM_SortMem += += Throws out all purgable stuff and compresses movable blocks += +===================== +*/ + +void MM_SortMem (void) +{ + mmblocktype far *scan,far *last,far *next; + unsigned start,length,source,dest,oldborder; + int playing; + + // + // lock down a currently playing sound + // + playing = SD_SoundPlaying (); + if (playing) + { + switch (SoundMode) + { + case sdm_PC: + playing += STARTPCSOUNDS; + break; + case sdm_AdLib: + playing += STARTADLIBSOUNDS; + break; + } + MM_SetLock(&(memptr)audiosegs[playing],true); + } + + + SD_StopSound(); +// oldborder = bordercolor; +// VW_ColorBorder (15); + + if (beforesort) + beforesort(); + + scan = mmhead; + + last = NULL; // shut up compiler warning + + while (scan) + { + if (scan->attributes & LOCKBIT) + { + // + // block is locked, so try to pile later blocks right after it + // + start = scan->start + scan->length; + } + else + { + if (scan->attributes & PURGEBITS) + { + // + // throw out the purgable block + // + next = scan->next; + FREEBLOCK(scan); + last->next = next; + scan = next; + continue; + } + else + { + // + // push the non purgable block on top of the last moved block + // + if (scan->start != start) + { + length = scan->length; + source = scan->start; + dest = start; + while (length > 0xf00) + { + movedata(source,0,dest,0,0xf00*16); + length -= 0xf00; + source += 0xf00; + dest += 0xf00; + } + movedata(source,0,dest,0,length*16); + + scan->start = start; + *(unsigned *)scan->useptr = start; + } + start = scan->start + scan->length; + } + } + + last = scan; + scan = scan->next; // go to next block + } + + mmrover = mmhead; + + if (aftersort) + aftersort(); + +// VW_ColorBorder (oldborder); + + if (playing) + MM_SetLock(&(memptr)audiosegs[playing],false); +} + + +//========================================================================== + +#if 0 +/* +===================== += += MM_ShowMemory += +===================== +*/ + +void MM_ShowMemory (void) +{ + mmblocktype far *scan; + unsigned color,temp; + long end,owner; + char scratch[80],str[10]; + + VW_SetDefaultColors(); + VW_SetLineWidth(40); + temp = bufferofs; + bufferofs = 0; + VW_SetScreen (0,0); + + scan = mmhead; + + end = -1; + +//CA_OpenDebug (); + + while (scan) + { + if (scan->attributes & PURGEBITS) + color = 5; // dark purple = purgable + else + color = 9; // medium blue = non purgable + if (scan->attributes & LOCKBIT) + color = 12; // red = locked + if (scan->start<=end) + Quit ("MM_ShowMemory: Memory block order currupted!"); + end = scan->start+scan->length-1; + VW_Hlin(scan->start,(unsigned)end,0,color); + VW_Plot(scan->start,0,15); + if (scan->next->start > end+1) + VW_Hlin(end+1,scan->next->start,0,0); // black = free + +#if 0 +strcpy (scratch,"Size:"); +ltoa ((long)scan->length*16,str,10); +strcat (scratch,str); +strcat (scratch,"\tOwner:0x"); +owner = (unsigned)scan->useptr; +ultoa (owner,str,16); +strcat (scratch,str); +strcat (scratch,"\n"); +write (debughandle,scratch,strlen(scratch)); +#endif + + scan = scan->next; + } + +//CA_CloseDebug (); + + IN_Ack(); + VW_SetLineWidth(64); + bufferofs = temp; +} +#endif + +//========================================================================== + + +/* +====================== += += MM_UnusedMemory += += Returns the total free space without purging += +====================== +*/ + +long MM_UnusedMemory (void) +{ + unsigned free; + mmblocktype far *scan; + + free = 0; + scan = mmhead; + + while (scan->next) + { + free += scan->next->start - (scan->start + scan->length); + scan = scan->next; + } + + return free*16l; +} + +//========================================================================== + + +/* +====================== += += MM_TotalFree += += Returns the total free space with purging += +====================== +*/ + +long MM_TotalFree (void) +{ + unsigned free; + mmblocktype far *scan; + + free = 0; + scan = mmhead; + + while (scan->next) + { + if ((scan->attributes&PURGEBITS) && !(scan->attributes&LOCKBIT)) + free += scan->length; + free += scan->next->start - (scan->start + scan->length); + scan = scan->next; + } + + return free*16l; +} + +//========================================================================== + +/* +===================== += += MM_BombOnError += +===================== +*/ + +void MM_BombOnError (boolean bomb) +{ + bombonerror = bomb; +} + + diff --git a/16/cawat/16_mm.h b/16/cawat/16_mm.h new file mode 100644 index 00000000..622a2b4d --- /dev/null +++ b/16/cawat/16_mm.h @@ -0,0 +1,136 @@ +/* Catacomb Armageddon Source Code + * Copyright (C) 1993-2014 Flat Rock Software + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +// ID_MM.H + +#ifndef __ID_EXMM__ +#define __ID_EXMM__ + +#include "lib_head.h" +//#pragma hdrstop + +//#pragma warn -pro +//#pragma warn -use + +#if 1 // 1 == Debug/Dev ; 0 == Production/final +#define OUT_OF_MEM_MSG "MM_GetPtr: Out of memory!\nYou were short :%ld bytes" +#else +#define OUT_OF_MEM_MSG "\npee\n" +#endif + + +#define SAVENEARHEAP 0x400 // space to leave in data segment +#define SAVEFARHEAP 0 // space to leave in far heap + +#define BUFFERSIZE 0x1000 // miscelanious, allways available buffer + +#define MAXBLOCKS 600 + + +//-------- + +#define EMS_INT 0x67 + +#define EMS_STATUS 0x40 +#define EMS_GETFRAME 0x41 +#define EMS_GETPAGES 0x42 +#define EMS_ALLOCPAGES 0x43 +#define EMS_MAPPAGE 0x44 +#define EMS_FREEPAGES 0x45 +#define EMS_VERSION 0x46 + +//-------- + +#define XMS_VERSION 0x00 + +#define XMS_ALLOCHMA 0x01 +#define XMS_FREEHMA 0x02 + +#define XMS_GENABLEA20 0x03 +#define XMS_GDISABLEA20 0x04 +#define XMS_LENABLEA20 0x05 +#define XMS_LDISABLEA20 0x06 +#define XMS_QUERYA20 0x07 + +#define XMS_QUERYREE 0x08 +#define XMS_ALLOC 0x09 +#define XMS_FREE 0x0A +#define XMS_MOVE 0x0B +#define XMS_LOCK 0x0C +#define XMS_UNLOCK 0x0D +#define XMS_GETINFO 0x0E +#define XMS_RESIZE 0x0F + +#define XMS_ALLOCUMB 0x10 +#define XMS_FREEUMB 0x11 + +//========================================================================== + +typedef void /*_seg*/ * memptr; + +typedef struct +{ + long nearheap,farheap,EMSmem,XMSmem,mainmem; +} mminfotype; + +//========================================================================== + +extern mminfotype mminfo; +extern memptr bufferseg; +extern boolean mmerror; + +extern void (* beforesort) (void); +extern void (* aftersort) (void); + +//========================================================================== + +void MM_Startup (void); +void MM_Shutdown (void); +void MM_MapEMS (void); + +void MM_GetPtr (memptr *baseptr,unsigned long size); +void MM_FreePtr (memptr *baseptr); + +void MM_SetPurge (memptr *baseptr, int purge); +void MM_SetLock (memptr *baseptr, boolean locked); +void MM_SortMem (void); + +void MM_ShowMemory (void); + +long MM_UnusedMemory (void); +long MM_TotalFree (void); + +void MM_BombOnError (boolean bomb); + +//========================================================================== + +// +// local prototypes +// + +boolean MML_CheckForEMS (void); +void MML_ShutdownEMS (void); +void MM_MapEMS (void); +boolean MML_CheckForXMS (void); +void MML_ShutdownXMS (void); +void MML_UseSpace (unsigned segstart, unsigned seglength); +void MML_ClearBlock (void); + +//========================================================================== + +#endif diff --git a/16/cawat/ARMGAME.DSK b/16/cawat/ARMGAME.DSK new file mode 100644 index 0000000000000000000000000000000000000000..1b741b027f5eabc8c3a9b5498e34b19d4d9bb378 GIT binary patch literal 164 zcmWG3ElSE)a8_{6&nro-C{b|B%t=*{VqoMI5@(cRVBkt+;9+27U}9ilU|`VFb7uI; zn8N@RmjrTw41V%es#YDw8I|c_i$N2gCIr_R97ywOU NBmfy0{{LrS0028i814W7 literal 0 HcmV?d00001 diff --git a/16/cawat/ARMGAME.PRJ b/16/cawat/ARMGAME.PRJ new file mode 100644 index 0000000000000000000000000000000000000000..bea242cfed9409f3a8ea8a05991ca1d35ca6fe16 GIT binary patch literal 12826 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 = true; + new->temp1 = number; + new->obclass = bonusobj; + + switch (number) + { + case B_POTION: + case B_CHEST: + case B_BOLT: + case B_NUKE: + new->flags |= of_shootable; + break; + + default: + new->flags &= ~of_shootable; + break; + } +} + + +/* +=============== += += SpawnTombstone += +=============== +*/ + +statetype s_tombs[3] = {{TOMB1PIC,8,NULL,&s_tombs[0]}, + {TOMB2PIC,8,NULL,&s_tombs[1]}, + {TOMB3PIC,8,NULL,&s_tombs[2]}}; + +void SpawnTombstone (int tilex, int tiley, int shape) +{ + statetype *state=&s_tombs[shape]; + + SpawnNewObj (tilex,tiley,state,TILEGLOBAL/2); +// new->tileobject = true; + new->obclass = realsolidobj; + new->flags |= of_shootable; +} + + +/* +============================================================================ + + FREEZE TIME OBJECT + +============================================================================ +*/ + +extern statetype s_ftimebonus; +extern statetype s_ftimebonus2; + +statetype s_ftimebonus = {TIMEOBJ1PIC,6,NULL,&s_ftimebonus2}; +statetype s_ftimebonus2 = {TIMEOBJ2PIC,6,NULL,&s_ftimebonus}; + +/* +=============== += += SpawnFTime += +=============== +*/ +void SpawnFTime(int tilex, int tiley) +{ + SpawnNewObj(tilex,tiley,&s_ftimebonus,TILEGLOBAL/2); +// new->tileobject = true; + new->obclass = freezeobj; + new->flags |= of_shootable; +} + +/* +============================================================================= + + EXPLODING WALL + +============================================================================= +*/ + + +void T_WallDie (objtype *ob); + +extern statetype s_walldie1; +extern statetype s_walldie2; +extern statetype s_walldie3; +extern statetype s_walldie4; +extern statetype s_walldie5; +extern statetype s_walldie6; + +statetype s_walldie1 = {0,20,NULL,&s_walldie2}; +statetype s_walldie2 = {0,-1,T_WallDie,&s_walldie3}; +statetype s_walldie3 = {0,20,NULL,&s_walldie4}; +statetype s_walldie4 = {0,-1,T_WallDie,&s_walldie5}; +statetype s_walldie5 = {0,20,NULL,&s_walldie6}; +statetype s_walldie6 = {0,-1,T_WallDie,NULL}; + + +/* +================ += += ExplodeWall += +================ +*/ + +void ExplodeWall (int tilex, int tiley) +{ + extern unsigned gcolor; + unsigned tilenum; + + DSpawnNewObj (tilex,tiley,&s_walldie1,0); + if (new == &dummyobj) + return; + new->obclass = inertobj; + new->active = always; + if (gcolor == 0x0101) + tilenum = WATEREXP; + else + tilenum = WALLEXP; + (unsigned)actorat[new->tilex][new->tiley] = tilemap[new->tilex][new->tiley] = + *(mapsegs[0]+farmapylookup[new->tiley]+new->tilex) = tilenum; + *(mapsegs[2]+farmapylookup[new->tiley]+new->tilex) &= 0xFF; +} + + +/* +================ += += T_WallDie += +================ +*/ + +void T_WallDie (objtype *ob) +{ + extern unsigned gcolor; + unsigned tile,other,spot,x,y; + + if (++ob->temp1 == 3) + tile = 0; + else + if (gcolor == 0x0101) + tile = WATEREXP-1 + ob->temp1; + else + tile = WALLEXP-1 + ob->temp1; + x = ob->tilex; + y = ob->tiley; + + (unsigned)actorat[x][y] = tilemap[x][y] = *(mapsegs[0]+farmapylookup[y]+x) = tile; + + if (ob->temp1 == 1) + { + // + // blow up nearby walls + // + spot = (*(mapsegs[2]+farmapylookup[y]+(x-1))) >> 8; + if (spot == EXP_WALL_CODE) + ExplodeWall (x-1,y); + spot = (*(mapsegs[2]+farmapylookup[y]+(x+1))) >> 8; + if (spot == EXP_WALL_CODE) + ExplodeWall (x+1,y); + spot = (*(mapsegs[2]+farmapylookup[y-1]+x)) >> 8; + if (spot == EXP_WALL_CODE) + ExplodeWall (x,y-1); + spot = (*(mapsegs[2]+farmapylookup[y+1]+x)) >> 8; + if (spot == EXP_WALL_CODE) + ExplodeWall (x,y+1); + } +} +/* +============================================================================= + + OBJ_WARP GATE + +============================================================================= +*/ + +void T_Gate (objtype *ob); + +extern statetype s_obj_gate1; +extern statetype s_obj_gate2; +extern statetype s_obj_gate3; +extern statetype s_obj_gate4; + +statetype s_obj_gate1 = {OBJ_WARP1PIC,10,T_Gate,&s_obj_gate2}; +statetype s_obj_gate2 = {OBJ_WARP2PIC,10,T_Gate,&s_obj_gate3}; +statetype s_obj_gate3 = {OBJ_WARP3PIC,10,T_Gate,&s_obj_gate4}; +statetype s_obj_gate4 = {OBJ_WARP4PIC,10,T_Gate,&s_obj_gate1}; + +extern statetype s_anthill; +statetype s_anthill = {ANT_HILLPIC, 20, T_Gate, &s_anthill}; + +//--------------------------------------------------------------------------- +// SpawnWarp() +// +// TYPE : Type param is the gate number (1-what ever) will link you to +// gate of that type. +//--------------------------------------------------------------------------- +void SpawnWarp (int tilex, int tiley, int type) +{ + + if (type) + SpawnNewObj (tilex,tiley,&s_obj_gate1,TILEGLOBAL/3); + else + SpawnNewObj (tilex,tiley,&s_anthill,TILEGLOBAL/3); + new->obclass = gateobj; + new->temp1 = type; +} + + + +/* +=============== += += T_Gate += +=============== +*/ + +#define STATUSCOLOR 4 + +void T_Gate (objtype *ob) +{ + objtype *check; + unsigned temp,spot; + + if (CheckHandAttack (ob) && !playstate) + { + // make + // +// spot = (*(mapsegs[2]+farmapylookup[ob->tiley+1]+ob->tilex)) >> 8; +// if (spot--) +// if (gamestate.keys[spot]) +// TakeKey(spot); +// else +// return; + + // + // warp + // + +// temp = bufferofs; +// bufferofs = 0; +// VW_Bar (26,4,232,9,STATUSCOLOR); // clear text description +// bufferofs = temp; + +// IN_ClearKeysDown (); + if (ob->temp1) + { + // + // teleport inside level + // + + for (check=player->next;check;check=check->next) + if (check->obclass==gateobj && check->temp1==ob->temp1 && + check != ob) + { + player->x = check->x; + player->y = check->y; + Thrust (player->angle,TILEGLOBAL/2); // move forwards + Thrust (player->angle,TILEGLOBAL/2); // move forwards + Thrust (player->angle,TILEGLOBAL/2); // move forwards + fizzlein=true; + SD_PlaySound(WARPSND); + } + } + else + { + // + // teleport out of level + // + + playstate = ex_warped; + spot = (*(mapsegs[2]+farmapylookup[ob->tiley+1]+ob->tilex)) >> 8; + gamestate.mapon=spot; + SD_PlaySound(WARPUPSND); + } + } +} + + + +/* +============================================================================= + + FAT DEMON + +============================================================================= +*/ + +#define FATCLOUDDAMAGE 2 + +void T_FatDemon (objtype *ob); +void T_CheckCnt(objtype *ob); +void ExplodeSound(objtype *ob); + +extern statetype s_fatdemon_pause; +extern statetype s_fatdemon_walk1; +extern statetype s_fatdemon_walk2; +extern statetype s_fatdemon_walk3; +extern statetype s_fatdemon_walk4; +extern statetype s_fatdemon_attack1; +extern statetype s_fatdemon_attack2; +extern statetype s_fatdemon_blowup2; +extern statetype s_fatdemon_blowup3; +extern statetype s_fatdemon_blowup4; +extern statetype s_fatdemon_blowup5; +extern statetype s_fatdemon_blowup6; +extern statetype s_fatdemon_blowup7; +extern statetype s_fatdemon_explode; +extern statetype s_fatdemon_feet; + +statetype s_fatdemon_pause = {FATDEMON_WALK1PIC,40,NULL,&s_fatdemon_walk2}; + +statetype s_fatdemon_walk1 = {FATDEMON_WALK1PIC,13,T_FatDemon,&s_fatdemon_walk2}; +statetype s_fatdemon_walk2 = {FATDEMON_WALK2PIC,13,T_FatDemon,&s_fatdemon_walk3}; +statetype s_fatdemon_walk3 = {FATDEMON_WALK3PIC,13,T_FatDemon,&s_fatdemon_walk4}; +statetype s_fatdemon_walk4 = {FATDEMON_WALK4PIC,13,T_FatDemon,&s_fatdemon_walk1}; + +statetype s_fatdemon_attack1 = {FATDEMON_ATTACK1PIC,20,NULL,&s_fatdemon_attack2}; +statetype s_fatdemon_attack2 = {FATDEMON_ATTACK2PIC,20,T_DoDamage,&s_fatdemon_pause}; + +statetype s_fatdemon_ouch = {FATDEMON_OUCHPIC,14,NULL,&s_fatdemon_walk1}; + +statetype s_fatdemon_blowup1 = {FATDEMON_BLOWUP1PIC,25,NULL,&s_fatdemon_blowup2}; +statetype s_fatdemon_blowup2 = {FATDEMON_BLOWUP2PIC,25,NULL,&s_fatdemon_blowup3}; +statetype s_fatdemon_blowup3 = {FATDEMON_BLOWUP1PIC,15,NULL,&s_fatdemon_blowup4}; +statetype s_fatdemon_blowup4 = {FATDEMON_BLOWUP2PIC,15,NULL,&s_fatdemon_blowup5}; +statetype s_fatdemon_blowup5 = {FATDEMON_BLOWUP1PIC,6,NULL,&s_fatdemon_blowup6}; +statetype s_fatdemon_blowup6 = {FATDEMON_BLOWUP2PIC,6,T_CheckCnt,&s_fatdemon_blowup5}; +statetype s_fatdemon_blowup7 = {FATDEMON_BLOWUP3PIC,30,NULL,&s_fatdemon_explode}; + + +statetype s_fatdemon_explode = {FATDEMON_EXPLODEPIC,40,ExplodeSound,&s_fatdemon_feet}; +statetype s_fatdemon_feet = {FATDEMON_FEETPIC,30,NULL,&s_fatdemon_feet}; + +#define cnt ob->temp1 +#define cloud_delay ob->temp2 + +/* +=============== += += SpawnFatDemon += +=============== +*/ + +void SpawnFatDemon (int tilex, int tiley) +{ + SpawnNewObj(tilex,tiley,&s_fatdemon_walk1,35*PIXRADIUS); + new->speed = 2500; + new->obclass = fatdemonobj; + new->flags |= of_shootable; + new->hitpoints = EasyHitPoints(10); + new->temp1 = 25; //used to "shake" the fat dude?????? +} + + +/* +=============== += += T_FatDemon += +=============== +*/ + +void T_FatDemon (objtype *ob) +{ + if (Chase(ob,true) || (random(1000)state = &s_fatdemon_attack1; + ob->ticcount = ob->state->tictime; + return; + } +} + + +/* +=============== += += T_DecCnt += +=============== +*/ + +void T_CheckCnt (objtype *ob) +{ + ob->temp1--; + if (!ob->temp1) + { + ob->state = &s_fatdemon_blowup7; + ob->ticcount = ob->state->tictime; + } +} + +/* +=============== += += ExplodeSound += +=============== +*/ +void ExplodeSound(objtype *ob) +{ + if (ob->temp1 != 666) // Has this think been called already? + { + SD_PlaySound(BODY_EXPLODESND); + ob->temp1 = 666; // Has now! + } +} + + +/* +============================================================================= + + WATER DRAGON + +============================================================================= +*/ + +extern statetype s_dragon_shot1; +extern statetype s_dragon_shot2; + + +void T_Dragon(objtype *ob); +void T_DragonShoot(objtype *ob); + + +statetype s_wet_bubbles1 = {DRAGON_BUBBLES1PIC,13,T_Dragon,&s_wet_bubbles2}; +statetype s_wet_bubbles2 = {DRAGON_BUBBLES2PIC,15,T_Dragon,&s_wet_bubbles1}; +statetype s_wet_bubbles3 = {0,35,T_Dragon,&s_wet_bubbles1}; + +statetype s_wet_peek = {DRAGON_EYESPIC,45,NULL,&s_wet_bubbles1}; + +statetype s_wet_rise1 = {DRAGON_BUBBLES2PIC,15,NULL,&s_wet_rise3}; +statetype s_wet_rise3 = {DRAGON_EYESPIC,20,NULL,&s_wet_rise4}; +statetype s_wet_rise4 = {DRAGON_RISE1PIC,20,NULL,&s_wet_rise5}; +statetype s_wet_rise5 = {DRAGON_RISE2PIC,20,NULL,&s_wet_walk1}; + +statetype s_wet_sink1 = {DRAGON_RISE2PIC,20,NULL,&s_wet_sink2}; +statetype s_wet_sink2 = {DRAGON_RISE1PIC,20,NULL,&s_wet_sink3}; +statetype s_wet_sink3 = {DRAGON_EYESPIC,20,NULL,&s_wet_bubbles1}; + +statetype s_wet_walk1 = {DRAGON_WALK1PIC,12,T_Dragon,&s_wet_walk2}; +statetype s_wet_walk2 = {DRAGON_WALK2PIC,12,T_Dragon,&s_wet_walk3}; +statetype s_wet_walk3 = {DRAGON_WALK3PIC,12,T_Dragon,&s_wet_walk4}; +statetype s_wet_walk4 = {DRAGON_WALK4PIC,12,T_Dragon,&s_wet_walk1}; + +statetype s_wet_attack1 = {DRAGON_ATTACK1PIC,10,NULL,&s_wet_attack2}; +statetype s_wet_attack2 = {DRAGON_ATTACK2PIC,10,NULL,&s_wet_attack3}; +statetype s_wet_attack3 = {DRAGON_ATTACK2PIC,10,NULL,&s_wet_attack4}; +statetype s_wet_attack4 = {DRAGON_ATTACK3PIC,10,T_DragonShoot,&s_wet_walk1}; + +statetype s_wet_ouch = {DRAGON_OUCHPIC,10,T_Dragon,&s_wet_walk1}; + +statetype s_wet_die1 = {DRAGON_DEATH1PIC,27,NULL,&s_wet_die2}; +statetype s_wet_die2 = {DRAGON_DEATH2PIC,29,NULL,&s_wet_die3}; +statetype s_wet_die3 = {DRAGON_DEATH3PIC,44,NULL,&s_wet_die4}; +statetype s_wet_die4 = {DRAGON_BUBBLES2PIC,26,NULL,&s_wet_die5}; +statetype s_wet_die5 = {DRAGON_BUBBLES1PIC,23,NULL,NULL}; + +statetype s_dragon_shot1 = {PSHOT1PIC,8,&T_ShootPlayer,&s_dragon_shot2}; +statetype s_dragon_shot2 = {PSHOT2PIC,8,&T_ShootPlayer,&s_dragon_shot1}; + + +typedef enum {wt_BUBBLES,wt_WALK,wt_CORNER1,wt_CORNER2,wt_CORNER3,wt_CORNER4} DragonTypes; + + +#define WD_TIMEREMAIN (ob->temp1) +#define WD_STAGE (ob->temp2) +#define WATER_DRAGON_LEAVE 0x04 + +/* +=============== += += SpawnDragon += +=============== +*/ +void SpawnDragon(int tilex, int tiley) +{ + objtype *ob; + SpawnNewObj(tilex,tiley,&s_wet_bubbles1,PIXRADIUS*35); + ob=new; + + WD_STAGE = wt_BUBBLES; + WD_TIMEREMAIN = 80; + + new->obclass = wetobj; + new->speed = 1000; + new->flags &= ~of_shootable; + new->hitpoints = EasyHitPoints(20); +} + + +/* +=============== += += T_Dragon += +=============== +*/ + +void T_Dragon(objtype *ob) +{ + switch (WD_STAGE) + { + case wt_BUBBLES: + ob->flags &= ~of_shootable; + if (Chase(ob,true)) + { + // RISE & GOTO WALK STAGE + // + + WD_STAGE = wt_WALK; + WD_TIMEREMAIN = 60*8+random(60*5); + ob->state = &s_wet_rise1; + ob->speed = 2200; + ob->ticcount = ob->state->tictime; + } + else + { + // DEC COUNTER - And check for WALK + // + if ((WD_TIMEREMAIN-=realtics) < 0) + { + // RISE & GOTO WALK STAGE + // + + WD_STAGE = wt_WALK; + WD_TIMEREMAIN = 60*8+random(60*5); + ob->state = &s_wet_rise1; + ob->speed = 2200; + ob->ticcount = ob->state->tictime; + } + else + if (random(1000)<5) + { + // RANDOM PEEK UP OUT OF WATER + // + + ob->state=&s_wet_peek; + ob->ticcount = ob->state->tictime; + } + } + break; + + + case wt_WALK: + ob->flags |= of_shootable; + + if (Chase(ob,true) || (CheckHandAttack(ob))) + + { + ob->flags |= WATER_DRAGON_LEAVE; + WD_STAGE = random(wt_CORNER3) + 2; + WD_TIMEREMAIN = 60*2+(random(6)*60); + ob->state = &s_wet_bubbles1; + ob->ticcount = ob->state->tictime; + } + else + if (AngleNearPlayer(ob) != -1) + { + ob->state = &s_wet_attack1; + ob->ticcount = ob->state->tictime; + } + + else + { + // DEC COUNTER - And check for SINK + // + if ((WD_TIMEREMAIN-=realtics) < 0) + { + // SINK & GOTO BUBBLE STAGE + // + + WD_STAGE = wt_BUBBLES; + WD_TIMEREMAIN = 60*2+random(60*2); + ob->state = &s_wet_sink1; + ob->speed = 1200; + ob->ticcount = ob->state->tictime; + ob->flags &= ~of_shootable; + } + + } + break; + case wt_CORNER1: + case wt_CORNER2: + case wt_CORNER3: + case wt_CORNER4: + ob->flags &= ~of_shootable; + if ((WD_TIMEREMAIN -= realtics) < 0) + { + WD_STAGE = wt_BUBBLES; + ob->flags &= ~WATER_DRAGON_LEAVE; + } + else + { + fixed tempx,tempy; + unsigned temp_tilex,temp_tiley; + + tempx = player->x; + tempy = player->y; + temp_tilex = player->tilex; + temp_tiley = player->tiley; + + player->x = ((long)other_x[WD_STAGE-2]<y = ((long)other_y[WD_STAGE-2]<tilex = other_x[WD_STAGE-2]; + player->tiley = other_y[WD_STAGE-2]; + + + Chase(ob,true); + + player->x = tempx; + player->y = tempy; + player->tilex = temp_tilex; + player->tiley = temp_tiley; + } + break; + } +} + +/* +=============== += += T_DragonShoot += +=============== +*/ +void T_DragonShoot (objtype *ob) +{ + ShootPlayer(ob,dshotobj,10000,&s_dragon_shot1); +} diff --git a/16/cawat/C5_ACT2.C b/16/cawat/C5_ACT2.C new file mode 100644 index 00000000..fd8633a2 --- /dev/null +++ b/16/cawat/C5_ACT2.C @@ -0,0 +1,827 @@ +/* Catacomb Armageddon Source Code + * Copyright (C) 1993-2014 Flat Rock Software + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +// C3_PLAY.C + +#include "DEF.H" +#pragma hdrstop + +/* +============================================================================= + + LOCAL CONSTANTS + +============================================================================= +*/ +void SpawnSkeleton(int tilex, int tiley); + +#if 0 +#define MSHOTDAMAGE 2 +#define MSHOTSPEED 10000 + +#define ESHOTDAMAGE 1 +#define ESHOTSPEED 5000 + +#define SSHOTDAMAGE 3 +#define SSHOTSPEED 6500 + +#define RANDOM_ATTACK 20 +#endif + +/* +============================================================================= + + GLOBAL VARIABLES + +============================================================================= +*/ + +boolean ShootPlayer (objtype *ob, short obclass, short speed, statetype *state); +void T_ShootPlayer(objtype *ob); + +/* +============================================================================= + + LOCAL VARIABLES + +============================================================================= +*/ + + + +/* +============================================================================= + + SKELETON IN WALL + +============================================================================= +*/ + +void T_WallSkeleton(objtype *ob); + +statetype s_wallskel = {0,40,T_WallSkeleton,&s_wallskel}; +statetype s_wallskel2 = {0,1,NULL,NULL}; + + +enum wskel_modes {ws_wall1,ws_wall2,ws_wall3,ws_exit}; +//enum wskel_modes {ws_wall1,ws_exit}; + +#define wskel_mode ob->temp1 +#define wskel_delay ob->temp2 +#define wskel_base ob->angle +#define wskel_wallx ob->hitpoints +#define wskel_wally ob->speed + +/* +=============== += += SpawnWallSkeleton += +=============== +*/ +void SpawnWallSkeleton(int tilex, int tiley) +{ + char xofs[] = {0,0,-1,+1}; + char yofs[] = {-1,+1,0,0}; + + objtype *ob; + int wallx=tilex,wally=tiley,wallbase,wallmode,loop; + unsigned tile,current_delay; + + for (loop=0; loop<4; loop++) + { + tile = *(mapsegs[0]+farmapylookup[tiley+yofs[loop]]+tilex+xofs[loop]); + switch (tile) + { +// case WALL_SKELETON_CODE: +// case WALL_SKELETON_CODE+1: +// case WALL_SKELETON_CODE+2: +// wallmode = ws_wall1+(tile-WALL_SKELETON_CODE); +// wallbase = WALL_SKELETON_CODE; +// goto foundtile; +// break; + + case 66: + case 68: +// case 21: + wallmode = ws_wall1+(tile-66); + wallbase = 66; + goto foundtile; +// break; + + case 67: + case 69: + wallmode = ws_wall1+(tile-67); + wallbase = 67; + goto foundtile; +// break; + } + } + + return; +foundtile:; + + wallx += xofs[loop]; + wally += yofs[loop]; + + SpawnNewObj(tilex,tiley,&s_wallskel,PIXRADIUS*35); + ob = new; + new->obclass = wallskelobj; + new->speed = 1900; + new->flags &= ~of_shootable; + new->hitpoints = 12; + +// new->tilex = wallx; +// new->tiley = wally; + wskel_wallx = wallx; + wskel_wally = wally; + wskel_base = wallbase; + new->active = no; + + wskel_mode = wallmode; + + tile = *(mapsegs[2]+farmapylookup[wally]+wallx); + if (tile) + wskel_delay = (tile>>8)*30; + else + { + current_delay = (2*60)+random(4*60); + wskel_delay = zombie_base_delay+current_delay; + zombie_base_delay += current_delay; + if (zombie_base_delay > 8*60) + zombie_base_delay = 0; + } +} + +/* +=============== += += T_WallSkeleton += +=============== +*/ +void T_WallSkeleton(objtype *ob) +{ + int x=wskel_wallx,y=wskel_wally; + + wskel_delay -= realtics; + if (wskel_delay > 0) + return; + + switch (wskel_mode) + { + case ws_wall2: + if ((wskel_base == 66) || (wskel_base == 67)) + wskel_mode++; + case ws_wall1: + case ws_wall3: + (unsigned)actorat[x][y] + = tilemap[x][y] + = *(mapsegs[0]+farmapylookup[y]+x) + = wskel_base+(wskel_mode-ws_wall1); + + wskel_mode++; + wskel_delay = (120); + ob->active = always; + break; + + case ws_exit: + (unsigned)actorat[x][y] + = tilemap[x][y] + = *(mapsegs[0]+farmapylookup[y]+x) + = 21; +// = wskel_base; + ob->tilex = ob->x >> TILESHIFT; + ob->tiley = ob->y >> TILESHIFT; + + ob->obclass = skeletonobj; + ob->speed = 2036; + ob->flags |= of_shootable; + ob->hitpoints = 12; + ob->state = &s_skel_1; + ob->ticcount = ob->state->tictime; + break; + } +} + + +/* +============================================================================= + + SKELETONS + +============================================================================= +*/ + +void T_Skeleton(objtype *ob); + + + + +statetype s_skel_pause = {SKELETON_1PIC,40,NULL,&s_skel_2}; + +statetype s_skel_1 = {SKELETON_1PIC,10,T_Skeleton,&s_skel_2}; +statetype s_skel_2 = {SKELETON_2PIC,10,T_Skeleton,&s_skel_3}; +statetype s_skel_3 = {SKELETON_3PIC,10,T_Skeleton,&s_skel_4}; +statetype s_skel_4 = {SKELETON_4PIC,10,T_Skeleton,&s_skel_1}; + +statetype s_skel_attack1 = {SKELETON_ATTACK_1PIC,12,NULL,&s_skel_attack2}; +statetype s_skel_attack2 = {SKELETON_ATTACK_2PIC,12,NULL,&s_skel_attack3}; +statetype s_skel_attack3 = {SKELETON_ATTACK_3PIC,12,T_DoDamage,&s_skel_pause}; + +statetype s_skel_ouch = {SKELETON_OUCHPIC,8,NULL,&s_skel_1}; + +statetype s_skel_die1 = {SKELETON_OUCHPIC,18,NULL,&s_skel_die2}; +statetype s_skel_die2 = {SKELETON_DEATH_1PIC,18,NULL,&s_skel_die3}; +statetype s_skel_die3 = {SKELETON_DEATH_2PIC,18,NULL,&s_skel_die3}; + +/* +=============== += += SpawnSkeleton += +=============== +*/ +void SpawnSkeleton(int tilex, int tiley) +{ + SpawnNewObj(tilex,tiley,&s_skel_1,PIXRADIUS*35); + new->obclass = skeletonobj; + new->speed = 2036; + new->flags |= of_shootable; + new->hitpoints = EasyHitPoints(12); +} + + +/* +=============== += += T_Skeleton += +=============== +*/ + +void T_Skeleton(objtype *ob) +{ + if (Chase (ob,true) || (random(1000)state = &s_skel_attack1; + ob->ticcount = ob->state->tictime; + return; + } +} + +/* +============================================================================= + + EYE + +============================================================================= +*/ + +void T_EyeMage (objtype *ob); +boolean T_EyeShoot (objtype *ob, boolean eyeshot); +void T_EyeShootPlayer (objtype *ob); + +extern statetype s_eye_shootplayer_1; +extern statetype s_eye_shootplayer_2; + +statetype s_eye_pause = {EYE_WALK1PIC,40,NULL,&s_eye_2}; + +statetype s_eye_1 = {EYE_WALK1PIC,20,T_EyeMage,&s_eye_2}; +statetype s_eye_2 = {EYE_WALK2PIC,20,T_EyeMage,&s_eye_3}; +statetype s_eye_3 = {EYE_WALK3PIC,20,T_EyeMage,&s_eye_4}; +statetype s_eye_4 = {EYE_WALK2PIC,20,T_EyeMage,&s_eye_1}; +statetype s_eye_shootplayer_1 = {EYE_SCOWLPIC,1,T_EyeShootPlayer,&s_eye_shootplayer_2}; +statetype s_eye_shootplayer_2 = {EYE_SCOWLPIC,20,NULL,&s_eye_1}; + +statetype s_eye_ouch = {EYE_OUCH1PIC,8,NULL,&s_eye_ouch2}; +statetype s_eye_ouch2 = {EYE_OUCH2PIC,8,NULL,&s_eye_1}; + +statetype s_eye_die1 = {EYE_DEATH1PIC,22,NULL,&s_eye_die2}; +statetype s_eye_die2 = {EYE_DEATH2PIC,22,NULL,&s_eye_die3}; +statetype s_eye_die3 = {EYE_DEATH2PIC,22,NULL,NULL}; + +extern statetype s_eshot2; + +statetype s_eshot1 = {EYE_SHOT1PIC,8,&T_ShootPlayer,&s_eshot2}; +statetype s_eshot2 = {EYE_SHOT2PIC,8,&T_ShootPlayer,&s_eshot1}; + +#define eye_mode ob->temp1 +#define eye_delay ob->temp2 + + +//------------------------------------------------------------------------- +// SpawnEye() +//------------------------------------------------------------------------- +void SpawnEye(int tilex, int tiley) +{ + objtype *ob; + + SpawnNewObj(tilex,tiley,&s_eye_1,PIXRADIUS*35); + ob = new; + new->obclass = eyeobj; + new->speed = 1200; + new->flags |= of_shootable; + new->hitpoints = EasyHitPoints(15); + eye_mode = em_other1; +} + + +//--------------------------------------------------------------------------- +// T_EyeShootPlayer +//--------------------------------------------------------------------------- +void T_EyeShootPlayer (objtype *ob) +{ + ShootPlayer(ob,eshotobj,ESHOTSPEED,&s_eshot1); +} + + +/* +============================================================================= + + SUCCUBUS + +============================================================================= +*/ + +void T_Succubus (objtype *ob); +void T_SuccubusShot (objtype *ob); + +extern statetype s_succubus_pause; +extern statetype s_succubus_walk1; +extern statetype s_succubus_walk2; +extern statetype s_succubus_walk3; +extern statetype s_succubus_walk4; +extern statetype s_succubus_shot1; +extern statetype s_succubus_attack1; +extern statetype s_succubus_attack2; +extern statetype s_succubus_attack3; +extern statetype s_succubus_death1; +extern statetype s_succubus_death2; + +statetype s_succubus_pause = {SUCCUBUS_WALK2PIC,10,NULL,&s_succubus_walk3}; + +statetype s_succubus_walk1 = {SUCCUBUS_WALK1PIC,10,T_EyeMage,&s_succubus_walk2}; +statetype s_succubus_walk2 = {SUCCUBUS_WALK2PIC,10,T_EyeMage,&s_succubus_walk3}; +statetype s_succubus_walk3 = {SUCCUBUS_WALK3PIC,10,T_EyeMage,&s_succubus_walk4}; +statetype s_succubus_walk4 = {SUCCUBUS_WALK4PIC,10,T_EyeMage,&s_succubus_walk1}; + +statetype s_succubus_attack1 = {SUCCUBUS_ATTACK1PIC,15,NULL,&s_succubus_attack2}; +statetype s_succubus_attack2 = {SUCCUBUS_ATTACK1PIC,-1,T_SuccubusShot,&s_succubus_attack3}; +statetype s_succubus_attack3 = {SUCCUBUS_ATTACK2PIC,15,NULL,&s_succubus_pause}; + +statetype s_succubus_ouch = {SUCCUBUS_OUCHPIC,15,NULL,&s_succubus_walk1}; + +statetype s_succubus_death1 = {SUCCUBUS_DEATH1PIC,55,NULL,&s_succubus_death2}; +statetype s_succubus_death2 = {SUCCUBUS_DEATH2PIC,20,NULL,&s_succubus_death2}; + +statetype s_succubus_shot1 = {SUCCUBUS_SHOT1PIC,12,&T_ShootPlayer,&s_succubus_shot1}; + +/* +=============== += += SpawnSuccubus += +=============== +*/ + +void SpawnSuccubus (int tilex, int tiley) +{ + SpawnNewObj(tilex,tiley,&s_succubus_walk1,PIXRADIUS*30); + new->obclass = succubusobj; + new->speed = 2500; + new->flags |= of_shootable; + new->hitpoints = EasyHitPoints(12); +} + +/* +=============== += += T_SuccubusShot += +=============== +*/ + +void T_SuccubusShot (objtype *ob) +{ + ShootPlayer(ob,sshotobj,ob->temp1 ? MSHOTSPEED : SSHOTSPEED,&s_succubus_shot1); +// ob->state = &s_succubus_attack3; +// ob->ticcount = ob->temp1 ? 7 : ob->state->tictime; +} + + +/* +============================================================================= + + MAGE + +============================================================================= +*/ + + +void T_MageShoot (objtype *ob); + +extern statetype s_magepause; + +extern statetype s_mage1; +extern statetype s_mage2; + +extern statetype s_mageattack1; +extern statetype s_mageattack2; +extern statetype s_mageattack3; + +extern statetype s_mageouch; + +extern statetype s_magedie1; +extern statetype s_magedie2; + + +statetype s_magepause = {MAGE1PIC,10,NULL,&s_mage2}; + +statetype s_mage1 = {MAGE1PIC,20,T_EyeMage,&s_mage2}; +statetype s_mage2 = {MAGE2PIC,20,T_EyeMage,&s_mage1}; + +//statetype s_mageattack1 = {MAGEATTACKPIC,20,NULL,&s_mageattack2}; +//statetype s_mageattack2 = {MAGEATTACKPIC,-1,T_MageShoot,&s_mageattack3}; +statetype s_mageattack3 = {MAGEATTACKPIC,30,NULL,&s_magepause}; + +statetype s_mageouch = {MAGEOUCHPIC,10,NULL,&s_mage1}; + +statetype s_magedie1 = {MAGEDIE1PIC,20,NULL,&s_magedie2}; +statetype s_magedie2 = {MAGEDIE2PIC,0,NULL,&s_magedie2}; + + +statetype s_mshot1 = {PSHOT1PIC,8,&T_ShootPlayer,&s_mshot2}; +statetype s_mshot2 = {PSHOT2PIC,8,&T_ShootPlayer,&s_mshot1}; + +/* +=============== += += SpawnMage += +=============== +*/ + +void SpawnMage (int tilex, int tiley) +{ + SpawnNewObj(tilex,tiley,&s_mage1,PIXRADIUS*35); + new->obclass = mageobj; + new->speed = 3072; + new->flags |= of_shootable; + new->hitpoints = EasyHitPoints(12); +} + + +/* +=============== += += T_EyeMage += += ********** += ***NOTE*** This routine controls the thinks for the Eye, Mage, and Succubus. += ********** += +=============== +*/ + +void T_EyeMage (objtype *ob) +{ + fixed tempx,tempy; + unsigned temp_tilex,temp_tiley; + int angle; + + eye_delay -= realtics; + if (eye_delay < 0) + { + eye_mode = random(em_dummy); + eye_delay = (10*60); + } + + tempx = player->x; + tempy = player->y; + temp_tilex = player->tilex; + temp_tiley = player->tiley; + + + switch (eye_mode) + { + case em_other1: + case em_other2: + case em_other3: + case em_other4: + player->x = ((long)other_x[eye_mode]<y = ((long)other_y[eye_mode]<tilex = other_x[eye_mode]; + player->tiley = other_y[eye_mode]; + break; + } + + if (Chase(ob,true)) + eye_delay = 0; + + player->x = tempx; + player->y = tempy; + player->tilex = temp_tilex; + player->tiley = temp_tiley; + + + if (ob->obclass == mageobj) // do the mage shot + { + if (!random(10)) + if (ShootPlayer(ob,mshotobj,MSHOTSPEED,&s_mshot1)) + { + ob->state = &s_mageattack3; + ob->ticcount = ob->state->tictime; + } + } + else + if (ob->obclass == succubusobj) // do the succubus shot + { + angle = AngleNearPlayer(ob); // make sure angle is correct + // - see AngleNearPlayer + if (!random(5) && (angle != -1)) // if correct angle and random # attack + { + ob->state = &s_succubus_attack1; // change state to attack + ob->ticcount = ob->state->tictime; // init ticcount - otherwise + } // object may get hung in a + } // endless state + + else + { + angle = AngleNearPlayer(ob); // do the eye shot + + if (!random(2) && (angle != -1)) + { + ob->state = &s_eye_shootplayer_1; + ob->ticcount = ob->state->tictime; + } + } + +} + + +/* +============================================================================= + + BUNNY + +============================================================================= +*/ + +void T_HarmlessBunnyWalk(objtype *ob); +void T_Bunny(objtype *ob); + +extern statetype s_bunny_left1; +extern statetype s_bunny_left2; +extern statetype s_bunny_left3; +extern statetype s_bunny_right1; +extern statetype s_bunny_right2; +extern statetype s_bunny_right3; +extern statetype s_bunny_meta1; +extern statetype s_bunny_meta2; +extern statetype s_bunny_walk1; +extern statetype s_bunny_walk2; +extern statetype s_bunny_attack1; +extern statetype s_bunny_attack2; +extern statetype s_bunny_pause; +extern statetype s_bunny_death1; +extern statetype s_bunny_death2; +extern statetype s_bunny_death3; + +statetype s_bunny_left1 = {BUNNY_LEFT1PIC, 55, NULL, &s_bunny_left2}; +statetype s_bunny_left2 = {BUNNY_LEFT1PIC, 10, T_HarmlessBunnyWalk, &s_bunny_left1}; +statetype s_bunny_left3 = {BUNNY_LEFT2PIC, 30, NULL, &s_bunny_left1}; + +statetype s_bunny_right1 = {BUNNY_RIGHT1PIC, 55, NULL, &s_bunny_right2}; +statetype s_bunny_right2 = {BUNNY_RIGHT1PIC, 10, T_HarmlessBunnyWalk, &s_bunny_right1}; +statetype s_bunny_right3 = {BUNNY_RIGHT2PIC, 30, NULL, &s_bunny_right1}; + +statetype s_bunny_meta1 = {BUNNY_META1PIC, 30, NULL, &s_bunny_meta2}; +statetype s_bunny_meta2 = {BUNNY_META2PIC, 30, NULL, &s_bunny_walk1}; + +statetype s_bunny_walk1 = {BUNNY_WALK1PIC, 25, T_Bunny, &s_bunny_walk2}; +statetype s_bunny_walk2 = {BUNNY_WALK2PIC, 25, T_Bunny, &s_bunny_walk1}; + +statetype s_bunny_attack1 = {BUNNY_WALK1PIC, 25, NULL, &s_bunny_attack2}; +statetype s_bunny_attack2 = {BUNNY_WALK2PIC, 25, T_DoDamage, &s_bunny_walk1}; + +statetype s_bunny_ouch = {BUNNY_OUCHPIC, 30, NULL, &s_bunny_pause}; +statetype s_bunny_pause = {BUNNY_WALK1PIC, 50, T_Bunny, &s_bunny_walk2}; + +statetype s_bunny_death1 = {BUNNY_OUCHPIC, 40, NULL, &s_bunny_death2}; +statetype s_bunny_death2 = {BUNNY_DEATH1PIC, 50, NULL, &s_bunny_death3}; +statetype s_bunny_death3 = {BUNNY_DEATH2PIC, 20, NULL, &s_bunny_death3}; + + +#define bunny_dir_hop ob->temp1 +#define bunny_delay ob->temp2 +#define LEFTSIDE 0x8 // 1=left 0=right --side showing + +/* +=============== += += SpawnBunny += +=============== +*/ + +void SpawnBunny (int tilex, int tiley) +{ + SpawnNewObj(tilex,tiley,&s_bunny_left1,PIXRADIUS*35); + new->obclass = hbunnyobj; + new->speed = 1947; + new->temp1 = (random(3))+2; + new->temp2 = random(30); + new->flags &= ~of_shootable; + new->flags |= LEFTSIDE; //left side showing} +} + +/* +=============== += += T_HarmlessBunnyWalk += +=============== +*/ + + +void T_HarmlessBunnyWalk(objtype *ob) +{ + int valid_dir[8][2] = {{6,5}, {7,6}, {4,7}, {5,4}, {3,2}, {0,3}, {1,0}, {2,1}}; + long move; + dirtype player_dir; + fixed old_x, old_y; + unsigned old_tilex, old_tiley; + long old_distance; + + + ob->temp2 -= realtics; + if (ob->temp2 <= 0) + { + if (CheckHandAttack(ob)) + { + ob->temp2 = -1; + return; + } + + actorat[ob->tilex][ob->tiley] = 0; + ob->x = ((long)ob->tilex<y = ((long)ob->tiley<distance = TILEGLOBAL; + ob->state = &s_bunny_meta1; + ob->ticcount = ob->state->tictime; + ob->obclass = bunnyobj; + ob->flags |= of_shootable; + ob->hitpoints = EasyHitPoints(10); + ob->dir = nodir; + ChaseThink(ob,true); // JTR - testing.. + return; + } + + // The direction of the player isn't updated so it must be + // calculated. This is done so the correct side (left/right) + // of the bunny will be showed. + + if ((player->angle > 337) || (player->angle <= 22)) + player_dir = east; + else + if (player->angle <= 67) + player_dir = northeast; + else + if (player->angle <= 112) + player_dir = north; + else + if (player->angle <= 157) + player_dir = northwest; + else + if (player->angle <= 202) + player_dir = west; + else + if (player->angle <= 247) + player_dir = southwest; + else + if (player->angle <= 292) + player_dir = south; + else + if (player->angle <= 337) + player_dir = southeast; + if (ob->temp1) + ob->temp1--; + else + ob->temp1 = (random(3))+2; + if (ob->flags & LEFTSIDE) + { + if (ob->temp1) + { + if (valid_dir[player_dir][0] != ob->dir) + { + ob->dir = valid_dir[player_dir][0]; + } + } + else + { + ob->state = &s_bunny_right1; + ob->ticcount = ob->state->tictime; + ob->flags &= ~LEFTSIDE; + ob->dir = valid_dir[player_dir][1]; + return; + } + } + else + { + if (ob->temp1) + { + if (valid_dir[player_dir][1] != ob->dir) + { + ob->dir = valid_dir[player_dir][1]; + } + } + else + { + ob->state = &s_bunny_left1; + ob->ticcount = ob->state->tictime; + ob->flags |= LEFTSIDE; + ob->dir = valid_dir[player_dir][2]; + return; + } + } + + move = ob->speed*tics; + + do + { + old_distance = ob->distance; + old_x = ob->x; + old_y = ob->y; + old_tilex = ob->tilex; + old_tiley = ob->tiley; + + MoveObj (ob, move); + + ob->tilex = ob->x >> TILESHIFT; + ob->tiley = ob->y >> TILESHIFT; + + if (ob->tilex == old_tilex && ob->tiley == old_tiley) + { + break; + } + else + if (actorat[ob->tilex][ob->tiley] == 0) + { + actorat[old_tilex][old_tiley] = 0; + actorat[ob->tilex][ob->tiley] = ob; + ob->distance = TILEGLOBAL; + } + else + { + ob->distance = old_distance; + ob->x = old_x; + ob->y = old_y; + ob->tilex = old_tilex; + ob->tiley = old_tiley; + return; + } + + } while (0); + + CalcBounds (ob); + + if (ob->flags & LEFTSIDE) + ob->state = &s_bunny_left3; + else + ob->state = &s_bunny_right3; + ob->ticcount = ob->state->tictime; +} + +/* +=============== += += T_Bunny += +=============== +*/ + +void T_Bunny(objtype *ob) +{ + if (Chase (ob, true) || (random(1000)state = &s_bunny_attack1; + ob->ticcount = ob->state->tictime; + return; + } +} diff --git a/16/cawat/C5_ACT3.C b/16/cawat/C5_ACT3.C new file mode 100644 index 00000000..79378d42 --- /dev/null +++ b/16/cawat/C5_ACT3.C @@ -0,0 +1,1218 @@ +/* Catacomb Armageddon Source Code + * Copyright (C) 1993-2014 Flat Rock Software + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +// C3_PLAY.C + +#include "DEF.H" +#pragma hdrstop + +/* +============================================================================= + + LOCAL CONSTANTS + +============================================================================= +*/ + +#if 0 +#define MSHOTDAMAGE 2 +#define MSHOTSPEED 10000 + +#define ESHOTDAMAGE 1 +#define ESHOTSPEED 5000 + +#define SSHOTDAMAGE 3 +#define SSHOTSPEED 6500 + +#define RANDOM_ATTACK 20 +#endif + +/* +============================================================================= + + GLOBAL VARIABLES + +============================================================================= +*/ + +boolean ShootPlayer (objtype *ob, short obclass, short speed, statetype *state); +void T_ShootPlayer(objtype *ob); + +/* +============================================================================= + + LOCAL VARIABLES + +============================================================================= +*/ + +/* +============================================================================= + + RED DEMON + +============================================================================= +*/ + +void T_RedDemon (objtype *ob); +void T_RedDemonCheckCnt (objtype *ob); + +extern statetype s_red_demonpause; + +extern statetype s_red_demon1; +extern statetype s_red_demon2; +extern statetype s_red_demon3; +extern statetype s_red_demon4; + +extern statetype s_red_demonattack1; +extern statetype s_red_demonattack2; +extern statetype s_red_demonattack3; + +extern statetype s_red_demonouch; + +extern statetype s_red_demondie1; +extern statetype s_red_demondie2; +extern statetype s_red_demondie3; +extern statetype s_red_demondie4; + +statetype s_red_demonpause = {RED_DEMON1PIC,30,NULL,&s_red_demon2}; + +statetype s_red_demon1 = {RED_DEMON1PIC,20,T_RedDemon,&s_red_demon2}; +statetype s_red_demon2 = {RED_DEMON2PIC,20,T_RedDemon,&s_red_demon3}; +statetype s_red_demon3 = {RED_DEMON3PIC,20,T_RedDemon,&s_red_demon4}; +statetype s_red_demon4 = {RED_DEMON4PIC,20,T_RedDemon,&s_red_demon1}; + +statetype s_red_demonattack1 = {RED_DEMONATTACK1PIC,20,NULL,&s_red_demonattack2}; +statetype s_red_demonattack2 = {RED_DEMONATTACK2PIC,20,NULL,&s_red_demonattack3}; +statetype s_red_demonattack3 = {RED_DEMONATTACK3PIC,30,T_DoDamage,&s_red_demon2}; + +statetype s_red_demonouch = {RED_DEMONOUCHPIC,30,NULL,&s_red_demon1}; + +statetype s_red_demondie1 = {RED_DEMONOUCHPIC,9,NULL,&s_red_demondie2}; +statetype s_red_demondie2 = {RED_DEMONDIE1PIC,9,T_RedDemonCheckCnt,&s_red_demondie1}; +statetype s_red_demondie3 = {RED_DEMONDIE2PIC,20,NULL,&s_red_demondie4}; +statetype s_red_demondie4 = {RED_DEMONDIE3PIC,10,NULL,&s_red_demondie4}; + + +/* +=============== += += SpawnRedDemon += +=============== +*/ + +void SpawnRedDemon (int tilex, int tiley) +{ + SpawnNewObj(tilex,tiley,&s_red_demon1,PIXRADIUS*35); + new->obclass = reddemonobj; + new->speed = 2048; + new->flags |= of_shootable; + new->hitpoints = EasyHitPoints(50); + new->temp1 = 25; +} + + +/* +=============== += += T_RedDemon += +=============== +*/ + +void T_RedDemon (objtype *ob) +{ + if (Chase (ob,true) || (random(1000)state = &s_red_demonattack1; + ob->ticcount = ob->state->tictime; + return; + } +} + +/* +=============== += += T_RedDemonCheckCnt += +=============== +*/ + +void T_RedDemonCheckCnt (objtype *ob) +{ + ob->temp1--; + if (!ob->temp1) + { + ob->state = &s_red_demondie3; + ob->ticcount = ob->state->tictime; + } +} + + +/* +============================================================================= + + GRELMINAR + +============================================================================= +*/ + + +void T_Grelminar (objtype *ob); +void T_GrelminarShoot (objtype *ob); +void T_Grelm_DropKey(objtype *ob); + +extern statetype s_grelpause; + +extern statetype s_grel1; +extern statetype s_grel2; + +extern statetype s_grelattack1; +extern statetype s_grelattack2; +extern statetype s_grelattack3; + +extern statetype s_grelouch; + +extern statetype s_greldie1; +extern statetype s_greldie2; +extern statetype s_greldie3; +extern statetype s_greldie4; +extern statetype s_greldie5; +extern statetype s_greldie5a; +extern statetype s_greldie6; + + +statetype s_grelpause = {GREL1PIC,50,NULL,&s_grel2}; + +statetype s_grel1 = {GREL1PIC,20,T_Grelminar,&s_grel2}; +statetype s_grel2 = {GREL2PIC,20,T_Grelminar,&s_grel1}; + +//statetype s_grelattack1 = {GRELATTACKPIC,20,NULL,&s_grelattack2}; +//statetype s_grelattack2 = {GRELATTACKPIC,-1,T_GrelminarShoot,&s_grelattack3}; +statetype s_grelattack3 = {GRELATTACKPIC,30,NULL,&s_grelpause}; + +statetype s_grelouch = {GRELHITPIC,6,NULL,&s_grel1}; + +statetype s_greldie1 = {GRELDIE1PIC,22,NULL,&s_greldie2}; +statetype s_greldie2 = {GRELDIE2PIC,22,NULL,&s_greldie3}; +statetype s_greldie3 = {GRELDIE3PIC,22,NULL,&s_greldie4}; +statetype s_greldie4 = {GRELDIE4PIC,22,NULL,&s_greldie5}; +statetype s_greldie5 = {GRELDIE5PIC,22,NULL,&s_greldie5a}; +statetype s_greldie5a = {GRELDIE5PIC,1,T_Grelm_DropKey,&s_greldie6}; +statetype s_greldie6 = {GRELDIE6PIC,0,NULL,&s_greldie6}; + + +extern statetype s_gshot1; + +statetype s_gshot1 = {SKULL_SHOTPIC,8,T_ShootPlayer,&s_gshot1}; + +/* +=============== += += SpawnGrelminar += +=============== +*/ + +void SpawnGrelminar (int tilex, int tiley) +{ + unsigned Grel_Hard; + unsigned DropKey; + + SpawnNewObj(tilex,tiley,&s_grel1,PIXRADIUS*35); + new->obclass = grelmobj; + new->speed = 2048; + new->flags |= of_shootable; + + // + // if Grelminar is to drop a key the info-plane byte to the right + // should have a 1 in the highbyte, else he will not drop the key. + // + DropKey = *(mapsegs[2]+farmapylookup[tiley]+tilex+1); + if (DropKey) + new->temp1 = DropKey>>8; + else + new->temp1 = 0; + + // + // The info-plane byte below Grelminar will determine how powerful + // Grelminar is. If nothing is there, he is the most powerful. + // -- affected are the hit points and the shot damage. + // The hit points are controlled here, the shot damage is controlled + // within the spawning of the shot. See ShootPlayer for more info. + // + Grel_Hard = *(mapsegs[2]+farmapylookup[tiley+1]+tilex); + if (Grel_Hard) + { + new->temp2 = Grel_Hard>>8; + new->hitpoints = EasyHitPoints((new->temp2 * 10)); + } + else + new->hitpoints = EasyHitPoints(100); +} + + +/* +=============== += += T_Grelminar += +=============== +*/ + +void T_Grelminar (objtype *ob) +{ + Chase (ob,false); + + if (!random(10)) + if (ShootPlayer(ob,gshotobj,ob->temp2,&s_gshot1)) + { + ob->state = &s_grelattack3; + ob->ticcount = ob->state->tictime; + } + if (CheckHandAttack(ob)) + TakeDamage (ob->temp2*3); + +} + + +//================================= +// +// T_Grelm_DropKey +// +//================================= +void T_Grelm_DropKey(objtype *ob) +{ + if (!(ob->temp1)) + { + ob->state = NULL; + return; + } + + SpawnBonus(ob->tilex,ob->tiley,B_RKEY); + SD_PlaySound(GRELM_DEADSND); + ob->temp1 = false; +} + +/* +============================================================================= + + BAT + +============================================================================= +*/ + +void T_Bat (objtype *ob); +void T_BatPast (objtype *ob); + +extern statetype s_bat1; +extern statetype s_bat2; +extern statetype s_bat3; +extern statetype s_bat4; + +extern statetype s_batdie1; +extern statetype s_batdie2; + + +statetype s_bat1 = {BAT1PIC,6,T_Bat,&s_bat2}; +statetype s_bat2 = {BAT2PIC,6,T_Bat,&s_bat3}; +statetype s_bat3 = {BAT3PIC,6,T_Bat,&s_bat4}; +statetype s_bat4 = {BAT4PIC,6,T_Bat,&s_bat1}; + +statetype s_batpast = {BAT4PIC,80,T_BatPast,&s_bat1}; + +statetype s_batdie1 = {BATDIE1PIC,18,NULL,&s_batdie2}; +statetype s_batdie2 = {BATDIE2PIC,18,NULL,NULL}; + + +/* +=============== += += SpawnBat += +=============== +*/ + +void SpawnBat (int tilex, int tiley) +{ + SpawnNewObj(tilex,tiley,&s_bat1,PIXRADIUS*35); + new->obclass = batobj; + new->flags |= of_shootable; + + new->hitpoints = 1; + new->speed = 2000; +} + + +/* +================================== += += BatChaseThink += +================================== +*/ + +void BatChaseThink (objtype *obj) +{ + int deltax,deltay; + + deltax=player->tilex - obj->tilex; + deltay=player->tiley - obj->tiley; + + if (deltax>0) + deltax = 2; + else if (deltax<0) + deltax = 0; + else deltax = 1; + + if (deltay>0) + deltay = 2; + else if (deltay<0) + deltay = 0; + else deltay = 1; + + obj->dir = dirtable[deltay*3+deltax]; + if (Walk(obj)) + return; + + obj->dir = dirtable[3+deltax]; + if (Walk(obj)) + return; + + obj->dir = dirtable[deltay*3+1]; + if (Walk(obj)) + return; + + obj->dir = nodir; +} + + +void BatRunThink (objtype *obj) +{ + int deltax,deltay; + + deltax=player->tilex - obj->tilex; + deltay=player->tiley - obj->tiley; + + if (deltax>=0) + deltax = 0; + else + deltax = 2; + + if (deltay>=0) + deltay = 0; + else + deltay = 2; + + obj->dir = dirtable[deltay*3+deltax]; + if (Walk(obj)) + return; + + obj->dir = dirtable[3+deltax]; + if (Walk(obj)) + return; + + obj->dir = dirtable[deltay*3+1]; + Walk(obj); +} + + + +/* +=============== += += T_Bat += +=============== +*/ + +void T_Bat (objtype *ob) +{ + long move; + long deltax,deltay,size; + + move = ob->speed*tics; + size = (long)ob->size + player->size + move; + + + do + { + deltax = ob->x - player->x; + deltay = ob->y - player->y; + + if (deltax <= size && deltax >= -size + && deltay <= size && deltay >= -size && !ob->temp1) + { + TakeDamage (4); + ob->temp1 = 2; + } + + if (move < ob->distance) + { + MoveObj (ob,move); + break; + } + + actorat[ob->tilex][ob->tiley] = 0; // pick up marker from goal + if (ob->dir == nodir) + ob->dir = north; + + ob->x = ((long)ob->tilex<y = ((long)ob->tiley<distance; + + if (ob->temp1) + { + Walk (ob); // go straight + if (!--ob->temp1) + { + ob->state = &s_batpast; + ob->ticcount = ob->state->tictime; + } + } + else + BatChaseThink (ob); // head towards player + + actorat[ob->tilex][ob->tiley] = ob; // set down a new goal marker + } while (0); // just once + CalcBounds (ob); +} + + +/* +=============== += += T_BatPast += +=============== +*/ + +void T_BatPast (objtype *ob) +{ + long move; + long deltax,deltay,size; + + move = ob->speed*tics; + + do + { + if (move < ob->distance) + { + MoveObj (ob,move); + break; + } + actorat[ob->tilex][ob->tiley] = 0; // pick up marker from goal + + ob->x = ((long)ob->tilex<y = ((long)ob->tiley<distance; + + BatRunThink (ob); + + actorat[ob->tilex][ob->tiley] = ob; // set down a new goal marker + } while (0); //(move) + CalcBounds (ob); +} + + +void T_ChaseThink(objtype *obj); +void T_AwakeThink(objtype *obj); + + + +/* +============================================================================= + + GODESS + +============================================================================= +*/ + +void T_Godess (objtype *ob); + + +extern statetype s_godesspause; + +extern statetype s_godess_statue1; +extern statetype s_godess_statue2; + +extern statetype s_godess1; +extern statetype s_godess2; +extern statetype s_godess3; + +extern statetype s_godessattack1; +extern statetype s_godessattack2; +extern statetype s_godessattack3; + +extern statetype s_godessouch; + +extern statetype s_godessdie1; +extern statetype s_godessdie2; +extern statetype s_godessdie3; + + +statetype s_godesspause = {GODESS_WALK1PIC,25,NULL,&s_godess2}; + +statetype s_godess_statue1 = {GODESS_STATUEPIC,20,T_ChaseThink,&s_godess_statue1}; +statetype s_godess_statue2 = {GODESS_STATUEPIC,1,T_AwakeThink,&s_godess1}; + +statetype s_godess1 = {GODESS_WALK1PIC,20,T_ChaseThink,&s_godess2}; +statetype s_godess2 = {GODESS_WALK2PIC,20,T_ChaseThink,&s_godess3}; +statetype s_godess3 = {GODESS_WALK3PIC,20,T_ChaseThink,&s_godess1}; + +statetype s_godessattack1 = {GODESS_ATTACK1PIC,10,NULL,&s_godessattack2};//20 +statetype s_godessattack2 = {GODESS_ATTACK2PIC,8,NULL,&s_godessattack3};//20 +statetype s_godessattack3 = {GODESS_ATTACK3PIC,10,T_DoDamage,&s_godesspause};//30 + +statetype s_godessouch = {GODESS_OUCHPIC,10,NULL,&s_godess1}; + +statetype s_godessdie1 = {GODESS_DEATH1PIC,65,NULL,&s_godessdie2}; +statetype s_godessdie2 = {GODESS_DEATH2PIC,30,NULL,&s_godessdie2}; + + + +/* +=============== += += SpawnGodess += +=============== +*/ + +void SpawnGodess (int tilex, int tiley) +{ + objtype *ob; + short current_zombie_delay; + unsigned tile; + + SpawnNewObj(tilex,tiley,&s_godess_statue1,PIXRADIUS*35); + ob = new; + zombie_mode = zm_wait_for_dark; + + tile = *(mapsegs[2]+farmapylookup[tiley+1]+tilex); + if (tile) + zombie_delay = (tile>>8)*30; + else + { + current_zombie_delay = (2*60)+random(4*60); + zombie_delay = zombie_base_delay+current_zombie_delay; + zombie_base_delay += current_zombie_delay; + if (zombie_base_delay > 8*60) + zombie_base_delay = 0; + } + + new->obclass = realsolidobj;//godessobj; + new->speed = 3000; + new->flags |= of_shootable; + new->flags &= ~of_tree; +// new->hitpoints = EasyHitPoints(10); +} + + + + +/* +============================================================================= + + ANT + +============================================================================= +*/ + +void T_Ant(objtype *ob); + +statetype s_ant_wait = {ANT_EGG1PIC,10,T_ChaseThink,&s_ant_wait}; + +statetype s_ant_egg = {ANT_EGG2PIC,45,T_AwakeThink,&s_ant_walk1}; + +statetype s_ant_walk1 = {ANT_WALK1PIC,20,T_ChaseThink,&s_ant_walk2}; +statetype s_ant_walk2 = {ANT_WALK2PIC,20,T_ChaseThink,&s_ant_walk3}; +statetype s_ant_walk3 = {ANT_WALK3PIC,20,T_ChaseThink,&s_ant_walk1}; + +statetype s_ant_attack1 = {ANT_ATTACKPIC,20,NULL,&s_ant_pause}; + +statetype s_ant_pause = {ANT_WALK2PIC,15,T_DoDamage,&s_ant_walk1}; + +statetype s_ant_ouch = {ANT_WALK1PIC,15,NULL,&s_ant_walk1}; + +statetype s_ant_die1 = {ANT_DEATH1PIC,40,NULL,&s_ant_die2}; +statetype s_ant_die2 = {ANT_DEATH2PIC,10,NULL,&s_ant_die3}; +statetype s_ant_die3 = {ANT_DEATH3PIC,10,NULL,&s_ant_die2}; + +#define ant_mode ob->temp1 +#define ant_delay ob->temp2 + +/* +=============== += += SpawnAnt += +=============== +*/ +void SpawnAnt(int tilex, int tiley) +{ + objtype *ob; + unsigned tile; + SpawnNewObj(tilex,tiley,&s_ant_wait,PIXRADIUS*35); + ob = new; + + tile = *(mapsegs[2]+farmapylookup[tiley+1]+tilex); + if (tile) + ant_delay = (tile>>8)*30; + else + ant_delay = 2*60+random(5*60); + + ant_mode = zm_wait_for_dark; + + new->obclass = antobj; + new->speed = 1900; + new->flags &= ~of_shootable; + new->hitpoints = EasyHitPoints(15); +} + + + +/* +============================================================================= + + ZOMBIE + +============================================================================= +*/ + +extern statetype s_zombie_rise1; +extern statetype s_zombie_rise2; +extern statetype s_zombie_rise3; +extern statetype s_zombie_rise4; + +extern statetype s_zombie_alive1; +extern statetype s_zombie_alive2; +extern statetype s_zombie_alive3; + +//extern statetype s_zombie_attack1; + +extern statetype s_zombie_death1; +extern statetype s_zombie_death2; +extern statetype s_zombie_death3; + +void T_Zombie (objtype *ob); +void T_ZombieRisen(objtype *obj); + +statetype s_zombie_risen = {ZOMB_WALK3PIC,1,T_AwakeThink,&s_zombie_alive1}; + +statetype s_zombie_pause = {ZOMB_WALK1PIC,20,NULL,&s_zombie_alive1}; + +statetype s_zombie_inground = {0,13,T_ChaseThink,&s_zombie_inground}; + +statetype s_zombie_rise1 = {ZOMB_APPEAR1PIC,24,NULL,&s_zombie_rise2}; +statetype s_zombie_rise2 = {ZOMB_APPEAR2PIC,24,NULL,&s_zombie_rise3}; +statetype s_zombie_rise3 = {ZOMB_APPEAR3PIC,24,NULL,&s_zombie_rise4}; +statetype s_zombie_rise4 = {ZOMB_APPEAR4PIC,24,NULL,&s_zombie_risen}; + +statetype s_zombie_alive1 = {ZOMB_WALK1PIC,13,T_ChaseThink,&s_zombie_alive2}; +statetype s_zombie_alive2 = {ZOMB_WALK2PIC,13,T_ChaseThink,&s_zombie_alive3}; +statetype s_zombie_alive3 = {ZOMB_WALK3PIC,13,T_ChaseThink,&s_zombie_alive1}; + +statetype s_zombie_death1 = {ZOMB_DIE1PIC,16,NULL,&s_zombie_death2}; +statetype s_zombie_death2 = {ZOMB_DIE2PIC,16,NULL,&s_zombie_death3}; +statetype s_zombie_death3 = {ZOMB_DIE3PIC,16,NULL,&s_zombie_death3}; + +statetype s_zombie_attack = {ZOMB_ATTACKPIC,15,T_DoDamage,&s_zombie_pause}; +//statetype s_zombie_attack1 = {ZOMB_ATTACKPIC,15,NULL,&s_zombie_pause}; + +statetype s_zombie_ouch = {ZOMB_OUCHPIC,15,NULL,&s_zombie_alive1}; + + +//-------------------------------------------------------------------------- +// SpawnZombie() +//-------------------------------------------------------------------------- +void SpawnZombie (int tilex, int tiley) +{ + objtype *ob; + short current_zombie_delay; + unsigned tile; + + SpawnNewObj(tilex,tiley,&s_zombie_inground,35*PIXRADIUS); + ob = new; + zombie_mode = zm_wait_for_dark; + + tile = *(mapsegs[2]+farmapylookup[tiley+1]+tilex); + if (tile) + zombie_delay = (tile>>8)*30; + else + { + current_zombie_delay = (2*60)+random(4*60); + zombie_delay = zombie_base_delay+current_zombie_delay; + zombie_base_delay += current_zombie_delay; + if (zombie_base_delay > 8*60) + zombie_base_delay = 0; + } + + new->speed = 2500; + new->obclass = zombieobj; + new->hitpoints = EasyHitPoints(8); + new->active = yes; + new->flags &= ~of_shootable; +} + + +/* +============================================================================= + + TREE + +============================================================================= +*/ + +extern statetype s_tree_pause; +extern statetype s_tree_idle; +extern statetype s_tree_awakening1; +extern statetype s_tree_awakening2; +extern statetype s_tree_walk1; +extern statetype s_tree_walk2; +extern statetype s_tree_walk3; +extern statetype s_tree_death1; +extern statetype s_tree_death2; +extern statetype s_tree_death3; +extern statetype s_tree_death4; +extern statetype s_tree_death5; +extern statetype s_tree_attack1; +extern statetype s_tree_attack2; +extern statetype s_tree_attack3; +extern statetype s_tree_ouch; + +void T_Tree (objtype *ob); +void T_DeathThink(objtype *ob); + +statetype s_tree_pause = {TREE_WALK1PIC,25,NULL,&s_tree_walk2}; + +statetype s_tree_idle = {TREE_IDLEPIC,13,T_ChaseThink,&s_tree_idle}; + +statetype s_tree_awakening1 = {TREE_AWAKENINGPIC,1,T_AwakeThink,&s_tree_awakening2}; +statetype s_tree_awakening2 = {TREE_AWAKENINGPIC,50,NULL,&s_tree_walk1}; + +statetype s_tree_walk1 = {TREE_WALK1PIC,13,T_ChaseThink,&s_tree_walk2}; +statetype s_tree_walk2 = {TREE_WALK2PIC,13,T_ChaseThink,&s_tree_walk1}; + +statetype s_tree_death1 = {TREE_DEATH1PIC,45,NULL,&s_tree_death2}; +statetype s_tree_death2 = {TREE_DEATH2PIC,25,NULL,&s_tree_death3}; +statetype s_tree_death3 = {TREE_DEATH1PIC,15,T_DeathThink,&s_tree_death4}; +statetype s_tree_death4 = {TREE_DEATH2PIC,15,T_DeathThink,&s_tree_death5}; +statetype s_tree_death5 = {TREE_DEATH3PIC,15,T_DeathThink,&s_tree_death3}; + +statetype s_tree_attack1 = {TREE_ATTACK1PIC,15,T_DoDamage,&s_tree_attack2}; +statetype s_tree_attack2 = {TREE_ATTACK2PIC,15,T_DoDamage,&s_tree_attack3}; +statetype s_tree_attack3 = {TREE_ATTACK3PIC,15,T_DoDamage,&s_tree_pause}; + +statetype s_tree_ouch = {TREE_AWAKENINGPIC,15,NULL,&s_tree_walk1}; + + +#define zombie_mode ob->temp1 +#define zombie_delay ob->temp2 + + +//-------------------------------------------------------------------------- +// SpawnTree() +//-------------------------------------------------------------------------- +void SpawnTree(int tilex, int tiley) +{ + objtype *ob; + short current_zombie_delay; + unsigned tile; + + SpawnNewObj(tilex,tiley,&s_tree_idle,35*PIXRADIUS); + ob = new; + zombie_mode = zm_wait_for_dark; + + tile = *(mapsegs[2]+farmapylookup[tiley+1]+tilex); + if (tile) + zombie_delay = (tile>>8)*30; + else + { + current_zombie_delay = (2*60)+random(4*60); + zombie_delay = zombie_base_delay+current_zombie_delay; + zombie_base_delay += current_zombie_delay; + if (zombie_base_delay > 8*60) + zombie_base_delay = 0; + } + + new->speed = 2500; + new->obclass = realsolidobj; +// new->hitpoints = EasyHitPoints(12); + new->active = yes; + new->flags |= of_shootable; + new->flags |= of_tree; +} + +//-------------------------------------------------------------------------- +// T_DeathThink() +//-------------------------------------------------------------------------- +void T_DeathThink(objtype *ob) +{ + char num; + + if ((ob->ticcount - realtics) <= 0) + { + num = random(2); + switch (ob->temp1) + { + case 3: + if (num) + ob->state = &s_tree_death4; + else + ob->state = &s_tree_death5; + ob->temp1++; + break; + + case 4: + if (num) + ob->state = &s_tree_death3; + else + ob->state = &s_tree_death5; + ob->temp1++; + break; + + case 5: + if (num) + ob->state = &s_tree_death3; + else + ob->state = &s_tree_death4; + ob->temp1 = 3; + break; + } + ob->ticcount = ob->state->tictime; + } + + + + if (CheckHandAttack(ob)) + TakeDamage (1); +} + + +////////////////////////////////////////////////////////////////////////// +// +// GENERAL THINK ROUTINES USED BY THE ZOMBIE, TREE, ANT, AND GODESS +// ----trying to cut down on the code size---- +// +////////////////////////////////////////////////////////////////////////// + + +//-------------------------------------------------------------------------- +// T_ChaseThink() +//-------------------------------------------------------------------------- +void T_ChaseThink(objtype *ob) +{ + switch (zombie_mode) + { + case zm_wait_for_dark: +#if 0 + if (gamestate.mapon == 0) + { + if (BGFLAGS & BGF_NIGHT) + zombie_mode = zm_wait_to_rise; + } + else +#endif + zombie_mode = zm_wait_to_rise; + break; + + case zm_wait_to_rise: + if (zombie_delay < 0) + { + if ((ob->tilex == player->tilex) && (ob->tiley == player->tiley)) + break; + if (CheckHandAttack(ob)) + break; + + ob->active = always; + switch (ob->obclass) + { + case zombieobj: + ob->state = &s_zombie_rise1; + break; + + case antobj: + ob->state = &s_ant_egg; + break; + + case realsolidobj: //tree and godess + if (ob->flags & of_tree) + ob->state = &s_tree_awakening1; + else + ob->state = &s_godess_statue2; + break; + } + ob->ticcount = ob->state->tictime; + zombie_mode = zm_active; + } + else + zombie_delay -= tics; + + break; + + case zm_active: + if (Chase (ob,true) || (random(1000)obclass) + { + case zombieobj: + ob->state = &s_zombie_attack; + break; + + case antobj: + ob->state = &s_ant_attack1; + break; + + case treeobj: + ob->state = &s_tree_attack1; + break; + + case godessobj: + ob->state = &s_godessattack1; + break; + } + ob->ticcount = ob->state->tictime; + return; + } + break; + } +} + +//-------------------------------------------------------------------------- +// T_AwakeThink() +//-------------------------------------------------------------------------- +void T_AwakeThink(objtype *obj) +{ + if (obj->obclass == realsolidobj) + { + if (obj->flags & of_tree) + obj->obclass = treeobj; + else + obj->obclass = godessobj; + obj->hitpoints = EasyHitPoints(12); + } + else + obj->flags |= of_shootable; +} + + + + + +//-------------------------------------------------------------------------- +// ShootPlayer() +//-------------------------------------------------------------------------- +boolean ShootPlayer(objtype *ob, short obclass, short speed, statetype *state) +{ + int angle = AngleNearPlayer(ob); + + if (angle == -1) + return(false); + + DSpawnNewObjFrac (ob->x,ob->y,state,PIXRADIUS*35); + new->obclass = obclass; + new->active = always; + new->angle = angle; + + // + // If the shot is Grelminar's, then determine the power of the shot. + // The shot speed is hard-wired as 10000. But the shot power is + // determined by speed. Speed now contains "Grelminar's level of + // hardness" and this is multiplied by 3 to get the shot power. + // + if (obclass == gshotobj) + { + new->speed = 10000; + new->temp1 = speed*3; + } + else + new->speed = speed; + + + return(true); +} + +//-------------------------------------------------------------------------- +// T_ShootPlayer() +//-------------------------------------------------------------------------- +void T_ShootPlayer(objtype *ob) +{ + objtype *check; + long xmove,ymove,speed; + + speed = ob->speed*tics; + + xmove = FixedByFrac(speed,costable[ob->angle]); + ymove = -FixedByFrac(speed,sintable[ob->angle]); + + if (ShotClipMove(ob,xmove,ymove)) + { + ob->state = &s_pshot_exp1; + ob->ticcount = ob->state->tictime; + return; + } + + ob->tilex = ob->x >> TILESHIFT; + ob->tiley = ob->y >> TILESHIFT; + + +// check for collision with wall +// + if (tilemap[ob->tilex][ob->tiley]) + { + SD_PlaySound (SHOOTWALLSND); + ob->state = &s_pshot_exp1; + ob->ticcount = s_pshot_exp1.tictime; + return; + } + + + +// check for collision with player +// + if ( ob->xl <= player->xh + && ob->xh >= player->xl + && ob->yl <= player->yh + && ob->yh >= player->yl) + { + switch (ob->obclass) + { + case eshotobj: + TakeDamage (ESHOTDAMAGE); + break; + + case mshotobj: + TakeDamage (MSHOTDAMAGE); + break; + + case gshotobj: + TakeDamage (ob->temp1); // the damage of Grelminar's shot - + break; // see Grelminar's spawning + + case sshotobj: + TakeDamage(SSHOTDAMAGE); + break; + + case dshotobj: + TakeDamage(7); + break; + } + ob->state = NULL; + return; + } + +// check for collision with other solid and realsolid objects. +// Great terminology!! -- solid objects really aren't solid +// -- realsolid objects ARE solid +// if ((actorat[ob->tilex][ob->tiley]) && (actorat[ob->tilex][ob->tiley]->obclass != ob->obclass)) + if (((actorat[ob->tilex][ob->tiley]->obclass == realsolidobj) || + (actorat[ob->tilex][ob->tiley]->obclass == solidobj)) && + (actorat[ob->tilex][ob->tiley]->flags & of_shootable)) + { + ob->state = &s_pshot_exp1; + ob->ticcount = s_pshot_exp1.tictime; + return; + } + + +// check for collision with player +// + for (check = player->next; check; check=check->next) + if ((ob->flags & of_shootable) && ob->obclass != mageobj + && ob->xl <= check->xh + && ob->xh >= check->xl + && ob->yl <= check->yh + && ob->yh >= check->yl) + { + switch (ob->obclass) + { + case eshotobj: + ShootActor (check,ESHOTDAMAGE); + break; + + case mshotobj: + ShootActor (check,MSHOTDAMAGE); + break; + + case gshotobj: + ShootActor (check,25); //NOLAN--check on me!!!!!!! + break; + + case pshotobj: + ShootActor (check,25); + break; + + case sshotobj: + ShootActor(check, SSHOTDAMAGE); + break; + + case dshotobj: + ShootActor(check, 7); + break; + } + ob->state = &s_pshot_exp1; + ob->ticcount = s_pshot_exp1.tictime; + return; + } +} + +//------------------------------------------------------------------------- +// AngleNearPlayer() +//------------------------------------------------------------------------- +int AngleNearPlayer(objtype *ob) +{ + int angle=-1; + int xdiff = ob->tilex-player->tilex; + int ydiff = ob->tiley-player->tiley; + + if (ob->tiley == player->tiley) + { + if (ob->tilex < player->tilex) + angle = 0; + else + angle = 180; + } + else + if (ob->tilex == player->tilex) + { + if (ob->tiley < player->tiley) + angle = 270; + else + angle = 90; + } + else + if (xdiff == ydiff) + if (ob->tilex < player->tilex) + { + if (ob->tiley < player->tiley) + angle = 315; + else + angle = 45; + } + else + { + if (ob->tiley < player->tiley) + angle = 225; + else + angle = 135; + } + + return(angle); +} diff --git a/16/cawat/C5_ACT4.C b/16/cawat/C5_ACT4.C new file mode 100644 index 00000000..676e0b7e --- /dev/null +++ b/16/cawat/C5_ACT4.C @@ -0,0 +1,414 @@ +/* Catacomb Armageddon Source Code + * Copyright (C) 1993-2014 Flat Rock Software + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +// C4_PLAY.C + +#include "DEF.H" +#pragma hdrstop + +/* +============================================================================= + + LOCAL CONSTANTS + +============================================================================= +*/ + + + +//------------------------------------------------------------------------- +// +// ARCH OBJECTS +// +//------------------------------------------------------------------------- + + + +//------------------------------------------------------------------------- +// ARCH +//------------------------------------------------------------------------- + +void SpawnArch(int tilex, int tiley, int num); + +extern statetype s_arch_1; +extern statetype s_arch_2; +extern statetype s_arch_3; +extern statetype s_arch_4; +extern statetype s_arch_5; +extern statetype s_arch_6; +extern statetype s_arch_7; +extern statetype s_arch_8; +extern statetype s_arch_9; +extern statetype s_arch_10; +extern statetype s_arch_11; +extern statetype s_arch_12; +extern statetype s_arch_13; + +statetype s_arch_1 = {ARCH1PIC, 20, NULL, &s_arch_1}; +statetype s_arch_2 = {ARCH2PIC, 20, NULL, &s_arch_2}; +statetype s_arch_3 = {ARCH3PIC, 20, NULL, &s_arch_3}; +statetype s_arch_4 = {ARCH4PIC, 20, NULL, &s_arch_4}; +statetype s_arch_5 = {ARCH5PIC, 20, NULL, &s_arch_5}; +statetype s_arch_6 = {ARCH6PIC, 20, NULL, &s_arch_6}; +statetype s_arch_7 = {ARCH7PIC, 20, NULL, &s_arch_7}; +statetype s_arch_8 = {ARCH8PIC, 20, NULL, &s_arch_8}; +statetype s_arch_9 = {ARCH9PIC, 20, NULL, &s_arch_9}; +statetype s_arch_10 = {ARCH10PIC, 20, NULL, &s_arch_10}; +statetype s_arch_11 = {ARCH11PIC, 20, NULL, &s_arch_11}; +statetype s_arch_12 = {ARCH12PIC, 20, NULL, &s_arch_12}; +statetype s_arch_13 = {ARCH13PIC, 20, NULL, &s_arch_13}; + +void SpawnArch (int tilex, int tiley, int num) +{ + statetype *objstate; + + + switch (num) + { + case 1: + objstate = &s_arch_1; + break; + case 2: + objstate = &s_arch_2; + break; + case 3: + objstate = &s_arch_3; + break; + case 4: + objstate = &s_arch_4; + break; + case 5: + objstate = &s_arch_5; + break; + case 6: + objstate = &s_arch_6; + break; + case 7: + objstate = &s_arch_7; + break; + case 8: + objstate = &s_arch_8; + break; + case 9: + objstate = &s_arch_9; + break; + case 10: + objstate = &s_arch_10; + break; + case 11: + objstate = &s_arch_11; + break; + case 12: + objstate = &s_arch_12; + break; + case 13: + objstate = &s_arch_13; + break; + } + ASpawnNewObj(tilex,tiley,objstate,PIXRADIUS*35); + new->obclass = solidobj; + new->flags &= ~of_shootable; +} + + +//------------------------------------------------------------------------- +// +// MISC OBJECTS +// +//------------------------------------------------------------------------- + + +//------------------------------------------------------------------------- +// COLUMN, SULPHUR GAS HOLE, FIRE POT, FOUNTAIN +//------------------------------------------------------------------------- + + +void SpawnMiscObjects(int tilex, int tiley, int num); + +extern statetype s_column; +extern statetype s_sulphur_gas_1; +extern statetype s_sulphur_gas_2; +extern statetype s_sulphur_gas_3; +extern statetype s_fire_pot_1; +extern statetype s_fire_pot_2; +extern statetype s_fountain; + +statetype s_column = {COLUMNPIC, 20, NULL, &s_column}; +statetype s_sulphur_gas_1 = {SULPHUR_GAS_1PIC, 20, NULL, &s_sulphur_gas_2}; +statetype s_sulphur_gas_2 = {SULPHUR_GAS_2PIC, 20, NULL, &s_sulphur_gas_3}; +statetype s_sulphur_gas_3 = {SULPHUR_GAS_3PIC, 20, NULL, &s_sulphur_gas_1}; +statetype s_fire_pot_1 = {FIRE_POT_1PIC, 20, NULL, &s_fire_pot_2}; +statetype s_fire_pot_2 = {FIRE_POT_2PIC, 20, NULL, &s_fire_pot_1}; +statetype s_fountain = {WFOUNTAINPIC, 20, NULL, &s_fountain}; + + +void SpawnMiscObjects(int tilex, int tiley, int num) +{ + statetype *objstate; + + switch (num) + { + case 1: + objstate = &s_column; + break; + + case 2: + objstate = &s_sulphur_gas_1; + break; + + case 3: + objstate = &s_fire_pot_1; + break; + + case 4: + objstate = &s_fountain; + break; + } + + SpawnNewObj(tilex,tiley,objstate,PIXRADIUS*35); + new->obclass = realsolidobj; + if (num == 2) + new->flags &= ~of_shootable; + else + new->flags |= of_shootable; +} + + + + + + +#if 0 +void SpawnColumn(int tilex, int tiley); + +extern statetype s_column; +statetype s_column = {COLUMNPIC, 20, NULL, &s_column}; + +void SpawnColumn(int tilex, int tiley) +{ + SpawnNewObj(tilex,tiley,&s_column,PIXRADIUS*35); + new->obclass = realsolidobj; + new->flags |= of_shootable; +} + + +//------------------------------------------------------------------------- +// SULPHUR GAS +//------------------------------------------------------------------------- + +void SpawnSulphurGas(int tilex, int tiley); + +extern statetype s_sulphur_gas_1; +extern statetype s_sulphur_gas_2; +extern statetype s_sulphur_gas_3; + +statetype s_sulphur_gas_1 = {SULPHUR_GAS_1PIC, 20, NULL, &s_sulphur_gas_2}; +statetype s_sulphur_gas_2 = {SULPHUR_GAS_2PIC, 20, NULL, &s_sulphur_gas_3}; +statetype s_sulphur_gas_3 = {SULPHUR_GAS_3PIC, 20, NULL, &s_sulphur_gas_1}; + +void SpawnSulphurGas(int tilex, int tiley) +{ + SpawnNewObj(tilex,tiley,&s_sulphur_gas_1,PIXRADIUS*35); + new->obclass = realsolidobj; + new->flags &= ~of_shootable; +} + + +//------------------------------------------------------------------------- +// FIRE POT +//------------------------------------------------------------------------- + +void SpawnFirePot(int tilex, int tiley); + +extern statetype s_fire_pot_1; +extern statetype s_fire_pot_2; + +statetype s_fire_pot_1 = {FIRE_POT_1PIC, 20, NULL, &s_fire_pot_2}; +statetype s_fire_pot_2 = {FIRE_POT_2PIC, 20, NULL, &s_fire_pot_1}; + +void SpawnFirePot(int tilex, int tiley) +{ + SpawnNewObj(tilex,tiley,&s_fire_pot_1,PIXRADIUS*35); + new->obclass = realsolidobj; + new->flags |= of_shootable; + +} + +//------------------------------------------------------------------------ +// FOUNTAIN +//------------------------------------------------------------------------ + +void SpawnFountain(int tilex, int tiley); + +extern statetype s_fountain; +statetype s_fountain = {WFOUNTAINPIC, 20, NULL, &s_fountain}; + +void SpawnFountain(int tilex, int tiley) +{ + SpawnNewObj(tilex,tiley,&s_fountain,PIXRADIUS*35); + new->obclass = realsolidobj; + new->flags |= of_shootable; +} + +#endif + + +//------------------------------------------------------------------------ +// FORCE FIELD +//------------------------------------------------------------------------ + +void SpawnForceField(int tilex, int tiley); +void T_ForceField(objtype *ob); +void T_ForceFieldRemove(objtype *ob); + +extern statetype s_force_field_1; +extern statetype s_force_field_2; +extern statetype s_force_field_3; +extern statetype s_force_field_4; +extern statetype s_force_field_die1; + +statetype s_force_field_1 = {FORCE_FIELD_1PIC, 10, T_ForceField, &s_force_field_2}; +statetype s_force_field_2 = {FORCE_FIELD_2PIC, 10, T_ForceField, &s_force_field_3}; +statetype s_force_field_3 = {FORCE_FIELD_3PIC, 10, T_ForceField, &s_force_field_4}; +statetype s_force_field_4 = {FORCE_FIELD_4PIC, 10, T_ForceField, &s_force_field_1}; + +statetype s_force_field_die = {0,0,T_ForceFieldRemove,&s_force_field_die1}; +statetype s_force_field_die1 = {0,0,NULL,NULL}; + +void SpawnForceField(int tilex, int tiley) +{ + SpawnNewObj(tilex,tiley,&s_force_field_1,PIXRADIUS*35); + new->obclass = solidobj; + new->hitpoints = EasyHitPoints(20); + new->flags |= of_forcefield; //sets bit 7 :: makes it nonsolid, but also detectable + // without adding another object type! + new->flags |= of_shootable; +} + +void T_ForceField(objtype *ob) +{ + long move,deltax,deltay,size; + + size = (long)ob->size + player->size; + + deltax = ob->x - player->x; + deltay = ob->y - player->y; + + if (deltax <= size && deltax >= -size + && deltay <= size && deltay >= -size) + TakeDamage (20); + +} + +void T_ForceFieldRemove(objtype *ob) +{ + actorat[ob->tilex][ob->tiley] = 0; +} + + +//------------------------------------------------------------------------ +// SKELETON HANGING FROM CEILING +//------------------------------------------------------------------------ + +void SpawnSkeletonHanging(int tilex, int tiley); +void T_SkelHangThink(objtype *ob); + +extern statetype s_skeleton_hanging; +statetype s_skeleton_hanging = {SKEL_HANGPIC, 20, T_SkelHangThink, &s_skeleton_hanging}; + +void SpawnSkeletonHanging(int tilex, int tiley) +{ + unsigned tile; + + SpawnNewObj(tilex,tiley,&s_skeleton_hanging,PIXRADIUS*35); + new->obclass = solidobj; + + tile = *(mapsegs[2]+farmapylookup[tiley+1]+tilex); + if (tile) + new->temp1 = (tile>>8)*30; + else + new->temp1 = (3*60)+random(4*60); + + new->flags |= of_shootable; +} + +void T_SkelHangThink(objtype *ob) +{ + ob->temp1 -= realtics; + if (ob->temp1 <= 0) + { + ob->state = &s_skel_1; + ob->ticcount = ob->state->tictime; + ob->obclass = skeletonobj; + ob->speed = 2036; + ob->flags |= of_shootable; + ob->hitpoints = EasyHitPoints(12); + } +} + + +///////////////////////////////////////////////////////////////////////////// +// +// EasyHitPoints +// +// Checks to see if the player has selected the easy mode for playing. +// If so then the normal hit points are cut in half. +// This is when the object is spawned. +// +// Parms +// NrmHitPts - the normal hit points +// +// Returns +// Half of NrmHitPts +// +///////////////////////////////////////////////////////////////////////////// + +int EasyHitPoints(int NrmHitPts) +{ + if (EASYMODEON) // Wimpy, Wimpy, Wimpy!!!!! + { + return(NrmHitPts/4); + } + else + return(NrmHitPts); +} + +///////////////////////////////////////////////////////////////////////////// +// +// EasyDoDamage +// +// Checks to see if the player has selected the easy mode for playing. +// If so then the normal amount of damage is cut in half. +// This is called each time a monster does damage. +// +// Parms +// Damage - the normal damage taken +// +// Returns +// Half of Damage +// +///////////////////////////////////////////////////////////////////////////// + +int EasyDoDamage(int Damage) +{ + if (EASYMODEON) // Wimpy, Wimpy, Wimpy!!!!! + return(Damage/2); + else + return(Damage); +} + diff --git a/16/cawat/C5_ASM.ASM b/16/cawat/C5_ASM.ASM new file mode 100644 index 00000000..a4390b2d --- /dev/null +++ b/16/cawat/C5_ASM.ASM @@ -0,0 +1,248 @@ +; Catacomb Armageddon Source Code +; Copyright (C) 1993-2014 Flat Rock Software +; +; This program is free software; you can redistribute it and/or modify +; it under the terms of the GNU General Public License as published by +; the Free Software Foundation; either version 2 of the License, or +; (at your option) any later version. +; +; This program is distributed in the hope that it will be useful, +; but WITHOUT ANY WARRANTY; without even the implied warranty of +; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +; GNU General Public License for more details. +; +; You should have received a copy of the GNU General Public License along +; with this program; if not, write to the Free Software Foundation, Inc., +; 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +IDEAL + +MODEL MEDIUM,C + +INCLUDE "ID_ASM.EQU" + +VIEWWIDTH = (40*8) ;33 +GC_INDEX = 03CEh + +DATASEG +EVEN + +;=================== Tables filled in by DrawVWall ========================== + +; +; wallheight has the height (scale number) of that collumn of scaled wall +; it is pre bounded to 1-MAXSCALE (the actuial height on screen is 2*height) +; +wallheight dw VIEWWIDTH dup (?) + +; +; wallwidth has the pixel width (1-7) of that collumn +; +wallwidth dw VIEWWIDTH dup (?) + +; +; wallseg has the segment of the wall picture +; +wallseg dw VIEWWIDTH dup (?) + +; +; wallofs has the offset of the wall picture +; +wallofs dw VIEWWIDTH dup (?) + +;============================================================================ + +; +; screenbyte is just position/8 +; +LABEL screenbyte WORD +pos = 0 +REPT VIEWWIDTH + dw pos/8 +pos = pos+1 +ENDM + +; +; screenbit is (position&7)*16 +; +LABEL screenbit WORD +pos = 0 +REPT VIEWWIDTH + dw (pos AND 7)*16 +pos = pos+1 +ENDM + +; +; Use offset: screenbit[]+pixwidth*2 +; acess from bitmasks-2+offset for one biased pixwidth +; the low byte of bitmasks is for the first screen byte, the high byte +; is the bitmask for the second screen byte (if non 0) +; + +bitmasks dw 0080h,00c0h,00e0h,00f0h,00f8h,00fch,00feh,00ffh + dw 0040h,0060h,0070h,0078h,007ch,007eh,007fh,807fh + dw 0020h,0030h,0038h,003ch,003eh,003fh,803fh,0c03fh + dw 0010h,0018h,001ch,001eh,001fh,801fh,0c01fh,0e01fh + dw 0008h,000ch,000eh,000fh,800fh,0c00fh,0e00fh,0f00fh + dw 0004h,0006h,0007h,8007h,0c007h,0e007h,0f007h,0f807h + dw 0002h,0003h,8003h,0c003h,0e003h,0f003h,0f803h,0fc03h + dw 0001h,8001h,0c001h,0e001h,0f001h,0f801h,0fc01h,0fe01h + + +; +; wallscalecall is a far pointer to the start of a compiled scaler +; The low word will never change, while the high word is set to +; compscaledirectory[scale] +; +wallscalecall dd (65*6) ; offset of t_compscale->code[0] + + +PUBLIC wallheight,wallwidth,wallseg,wallofs,screenbyte,screenbit +PUBLIC bitmasks,wallscalecall + + +EXTRN scaledirectory:WORD ; array of MAXSCALE segment pointers to + ; compiled scalers +EXTRN screenseg:WORD ; basically just 0xa000 +EXTRN bufferofs:WORD ; offset of the current work screen +EXTRN ylookup:WORD +EXTRN screenpage:WORD + +CODESEG + +;============================================================================ +; +; ScaleWalls +; +; AX AL is scratched in bit mask setting and scaling +; BX table index +; CX pixwidth*2 +; DX GC_INDEX +; SI offset into wall data to scale from, allways 0,64,128,...4032 +; DI byte at top of screen that the collumn is contained in +; BP x pixel * 2, index into VIEWWIDTH wide tables +; DS segment of the wall data to texture map +; ES screenseg +; SS addressing DGROUP variables +; +;============================================================================ + +PROC ScaleWalls +PUBLIC ScaleWalls +USES SI,DI,BP + + xor bp,bp ; start at location 0 in the tables + mov dx,GC_INDEX+1 + mov es,[screenseg] + +; +; scale one collumn of data, possibly across two bytes +; +nextcollumn: + + mov bx,[wallheight+bp] ; height of walls (1-MAXSCALE) + shl bx,1 + mov ax,[ss:scaledirectory+bx] ; segment of the compiled scaler + mov [WORD PTR ss:wallscalecall+2],ax + + mov cx,[wallwidth+bp] + or cx,cx + jnz okwidth + mov cx,2 + jmp next + +okwidth: + shl cx,1 + mov ds,[wallseg+bp] + mov si,[wallofs+bp] + + mov di,[screenbyte+bp] ; byte at the top of the scaled collumn + add di,[ss:bufferofs] ; offset of current page flip + mov bx,[screenbit+bp] ; 0-7 << 4 + add bx,cx + mov ax,[ss:bitmasks-2+bx] + out dx,al ; set bit mask register + call [DWORD PTR ss:wallscalecall] ; scale the line of pixels + or ah,ah ; is there anything in the second byte? + jnz secondbyte +; +; next +; +next: + add bp,cx + cmp bp,VIEWWIDTH*2 + jb nextcollumn + jmp done + +; +; draw a second byte for vertical strips that cross two bytes +; +secondbyte: + mov al,ah + inc di ; next byte over + out dx,al ; set bit mask register + call [DWORD PTR ss:wallscalecall] ; scale the line of pixels +; +; next +; + add bp,cx + cmp bp,VIEWWIDTH*2 + jb nextcollumn + +done: + mov ax,ss + mov ds,ax + ret + +ENDP + +;--------------------------------------------------------------------------- +; +; RadarBlip() +; +; Displays a 'blip' (1 pixel wide X 2 pixel high) on the radar at +; an (X,Y) relative to (RADAR_X,RADAR_Y) (defined below...) +; +;--------------------------------------------------------------------------- + +PROC RadarBlip x:WORD, y:WORD, color:WORD +USES SI,DI +PUBLIC RadarBlip + + mov ax,[screenseg] + + mov es,ax + xor di,di + + lea si,[ylookup] + add si,[y] + add si,[y] + add di,[si] + + mov ax,[x] + shr ax,1 + shr ax,1 + shr ax,1 + add di,ax + + mov ax,[x] + and ax,7 + mov cl,al + mov ah,080h + shr ah,cl + cli + mov al,GC_BITMASK + mov dx,GC_INDEX + out dx,ax + sti + + mov ax,[color] + mov ah,[es:di] ; read into latches + mov [es:di],al ; write latches / color bit + + ret + +ENDP + +END + diff --git a/16/cawat/C5_DEBUG.C b/16/cawat/C5_DEBUG.C new file mode 100644 index 00000000..d8e2b1aa --- /dev/null +++ b/16/cawat/C5_DEBUG.C @@ -0,0 +1,799 @@ +/* Catacomb Armageddon Source Code + * Copyright (C) 1993-2014 Flat Rock Software + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +// C3_DEBUG.C + +#include "DEF.H" +#include "gelib.h" +#pragma hdrstop + +/* +============================================================================= + + LOCAL CONSTANTS + +============================================================================= +*/ + +#define DEBUG_OVERHEAD 0 + + +#define VIEWTILEX 20 +#define VIEWTILEY (VIEWHEIGHT/16) + +/* +============================================================================= + + GLOBAL VARIABLES + +============================================================================= +*/ + + +/* +============================================================================= + + LOCAL VARIABLES + +============================================================================= +*/ + +short colordelay=0; +boolean autofire=false; +int maporgx; +int maporgy; +enum {mapview,tilemapview,actoratview,visview,mapseg2,lastview} viewtype; + +void ViewMap (void); + +//=========================================================================== + +#if 0 +/* +================ += += PicturePause += +================ +*/ + +void PicturePause (void) +{ + int y; + unsigned source; + + source = displayofs+panadjust; + +// VW_ColorBorder (15); + VW_SetLineWidth (40); + VW_SetScreen (0,0); + + if (source<0x10000l-200*64) + { + // + // copy top line first + // + for (y=0;y<200;y++) + VW_ScreenToScreen (source+y*64,y*40,40,1); + } + else + { + // + // copy bottom line first + // + for (y=199;y>=0;y--) + VW_ScreenToScreen (source+y*64,y*40,40,1); + } + + IN_Shutdown (); + + VW_WaitVBL(70); + bioskey(0); + VW_WaitVBL(70); + Quit (NULL); +} +#endif + + +//=========================================================================== + +//=========================================================================== + +#define sc_1 0x02 +#define sc_2 0x03 +#define sc_3 0x04 +#define sc_4 0x05 +#define sc_5 0x06 +#define sc_6 0x07 +#define sc_7 0x08 +#define sc_8 0x09 +#define sc_9 0x0a +#define sc_0 0x0b + + + +/* +================ += += DebugKeys += +================ +*/ + +int DebugKeys (void) +{ + boolean esc; + int level,i; + +#if DEBUG_KEYS_AVAILABLE + if (Keyboard[sc_R]) + { + CenterWindow (12,2); + if (autofire) + US_PrintCentered ("Rapid-Fire OFF"); + else + US_PrintCentered ("Rapid-Fire ON"); + VW_UpdateScreen(); + IN_Ack(); + autofire ^= 1; + return 1; + } +#endif + +#if DEBUG_KEYS_AVAILABLE + if (Keyboard[sc_A]) + { + char levelstr[50]; + unsigned org_tile,org_mapon,msgnum; + boolean newmsg=true,newlevel=false; + + VW_FixRefreshBuffer (); + CenterWindow (16,3); + US_Print("\n"); + US_CPrint("Message Test"); + VW_UpdateScreen(); + + org_mapon = mapon; + msgnum = (org_tile = *(mapsegs[0]+farmapylookup[player->tiley]+player->tilex))-NAMESTART; + while (1) + { + // Get outta' here + // + if (Keyboard[sc_Escape]) + { + while (Keyboard[sc_Escape]); + break; + } + + // Move to previous message + // + if (Keyboard[sc_UpArrow]) + { + if (msgnum) + { + msgnum--; + newmsg = true; + } + } + + // Move to next message + // + if (Keyboard[sc_DownArrow]) + { + if (msgnum < 24) + { + msgnum++; + newmsg = true; + } + } + + // Move to previous level + // + if (Keyboard[sc_LeftArrow]) + { + if (mapon) + { + MM_SetPurge(&grsegs[LEVEL1TEXT+mapon],3); + mapon--; + newlevel = true; + } + } + + // Move to next level + // + if (Keyboard[sc_RightArrow]) + { + if (mapon < LASTMAP-1) + { + MM_SetPurge(&grsegs[LEVEL1TEXT+mapon],3); + mapon++; + newlevel = true; + } + } + + // Load new level text + // + if (newlevel) + { + CA_CacheGrChunk(LEVEL1TEXT+mapon); + ScanText(); + newmsg = true; + newlevel=false; + } + + // Display new message text + // + if (newmsg) + { + *(mapsegs[0]+farmapylookup[player->tiley]+player->tilex) = msgnum+NAMESTART; + DrawText(true); + strcpy(levelstr,"Level: "); + itoa(mapon,levelstr+strlen(levelstr),10); + strcat(levelstr," Msg: "); + itoa(msgnum,levelstr+strlen(levelstr),10); + DisplaySMsg(levelstr,NULL); + newmsg = false; + + if (Keyboard[sc_UpArrow] || Keyboard[sc_DownArrow] || Keyboard[sc_LeftArrow] || Keyboard[sc_RightArrow]) + VW_WaitVBL(6); + } + + } +// Restore game +// + MM_SetPurge(&grsegs[LEVEL1TEXT+mapon],3); + mapon = org_mapon; + CA_CacheGrChunk(LEVEL1TEXT+mapon); + ScanText(); + *(mapsegs[0]+farmapylookup[player->tiley]+player->tilex) = org_tile; + DrawText(true); + status_flag = 0; + } + + + if (Keyboard[sc_V]) + { + displayofs = bufferofs = screenloc[screenpage]; + CenterWindow (16,4); + US_CPrint("\n"GAMENAME); + US_CPrint(VERSION); + US_CPrint(REVISION); + VW_UpdateScreen(); + IN_Ack (); + } + + + if (Keyboard[sc_Q]) // Q = Insta-Quit! + Quit("Insta-Quit!"); + + if (Keyboard[sc_Z]) // Z = freeze Time + { + if (FreezeTime) + FreezeTime = 1; // Allow refresh to dec to zero.. + else + StopTime(); + + IN_Ack(); + return 1; + } +#endif + + +// if (Keyboard[sc_E]) +// FaceDoor((player->x>>16l)+1,(player->y>>16l)); +// FaceAngle(90); + +#if 0 + if (Keyboard[sc_B]) // B = border color + { + CenterWindow(24,3); + PrintY+=6; + US_Print(" Border color (0-15):"); + VW_UpdateScreen(); + esc = !US_LineInput (px,py,str,NULL,true,2,0); + if (!esc) + { + level = atoi (str); + if (level>=0 && level<=15) + VW_ColorBorder (level); + } + return 1; + } +#endif + + +#if 1//DEBUG_KEYS_AVAILABLE + if (Keyboard[sc_O]) + { + extern unsigned objectcount,latchmemavail; + unsigned unused,total; + + CenterWindow (30,13); + US_Print ("Objects: "); + US_PrintUnsigned (objectcount); + + US_Print("\n\nTics: "); + US_PrintUnsigned (tics); + US_Print(" Real Tics: "); + US_PrintUnsigned(realtics); + + US_Print ("\n\n Total Available: "); + US_PrintUnsigned (mminfo.mainmem/1024); + US_Print ("k\n Mem In Use: "); + unused=MM_UnusedMemory()/1024; + US_PrintUnsigned (unused); + US_Print ("k\n Mem After Purge: "); + total=MM_TotalFree()/1024; + US_PrintUnsigned (total); + US_Print ("k ("); + US_PrintUnsigned (total-unused); + + US_Print (")\n\nLatch Mem Free: "); + US_PrintUnsigned (latchmemavail); + US_Print ("\n"); + VW_UpdateScreen(); + IN_Ack(); + } + + if (colordelay<1) + { + if (Keyboard[26]) + { + extern unsigned *groundcolor,debug_gnd; + + groundcolor = &debug_gnd; + debug_gnd += 0x0101; + if (debug_gnd == 0x1010) + debug_gnd = 0; + colordelay = 10; + } + + if (Keyboard[27]) + { + extern unsigned *skycolor,debug_sky; + + skycolor = &debug_sky; + debug_sky += 0x0101; + if (debug_sky == 0x1010) + debug_sky = 0; + colordelay = 10; + } + } + else + colordelay -= realtics; +#endif + + +#if 0 + if (Keyboard[sc_C]) // C = count objects + { + CountObjects(); + return 1; + } + + + if (Keyboard[sc_D]) // D = start / end demo record + { + if (DemoMode == demo_Off) + StartDemoRecord (); + else if (DemoMode == demo_Record) + { + EndDemoRecord (); + playstate = ex_completed; + } + return 1; + } +#endif + +#if 0 + if (Keyboard[sc_E]) // E = quit level + { + if (tedlevel) + TEDDeath(); + playstate = ex_warped; + gamestate.mapon++; + } +#endif + +#if 0 + if (Keyboard[sc_F]) // F = facing spot + { + CenterWindow (12,4); + US_Print ("X:"); + US_PrintUnsigned (player->x); + US_Print ("Y:"); + US_PrintUnsigned (player->y); + US_Print ("A:"); + US_PrintUnsigned (player->angle); + VW_UpdateScreen(); + IN_Ack(); + return 1; + } +#endif + + if (Keyboard[sc_G]) // G = god mode + { + CenterWindow (12,2); + if (godmode) + US_PrintCentered ("God mode OFF"); + else + US_PrintCentered ("God mode ON"); + VW_UpdateScreen(); + IN_Ack(); + godmode ^= 1; + return 1; + } + +#if 0 + if (Keyboard[sc_H]) // H = hurt self + { + TakeDamage (5); + } +#endif + + if (Keyboard[sc_I]) // I = item cheat + { + extern boolean redraw_gems; + + CenterWindow (12,3); + US_PrintCentered ("Free items!"); + VW_UpdateScreen(); + for (i=0;i<4;i++) + { + GiveBolt (); + GiveNuke (); + GivePotion (); +// if (!gamestate.keys[i]) + GiveKey (i); + gamestate.gems[i] = GEM_DELAY_TIME; + } + gamestate.gems[4] = GEM_DELAY_TIME; + redraw_gems = true; +///////// for (i=0;i<8;i++) +///////// GiveScroll (i,false); + + IN_Ack (); + return 1; + } + +#if DEBUG_OVERHEAD + if (Keyboard[sc_Z]) // O is used elsewhere... + { + ViewMap(); + return 1; + } +#endif + +#if 0 + if (Keyboard[sc_P]) // P = pause with no screen disruptioon + { + PicturePause (); + return 1; + } +#endif + +#if 0 + if (Keyboard[sc_S]) // S = slow motion + { + singlestep^=1; + CenterWindow (18,3); + if (singlestep) + US_PrintCentered ("Slow motion ON"); + else + US_PrintCentered ("Slow motion OFF"); + VW_UpdateScreen(); + IN_Ack (); + return 1; + } +#endif + +#if 0 + if (Keyboard[sc_V]) // V = extra VBLs + { + CenterWindow(30,3); + PrintY+=6; + US_Print(" Add how many extra VBLs(0-8):"); + VW_UpdateScreen(); + esc = !US_LineInput (px,py,str,NULL,true,2,0); + if (!esc) + { + level = atoi (str); + if (level>=0 && level<=8) + extravbls = level; + } + return 1; + } +#endif + + if (Keyboard[sc_W]) // W = warp to level + { + CenterWindow(26,3); + PrintY+=6; + US_Print(" Warp to which level(0-16):"); + VW_UpdateScreen(); + esc = !US_LineInput (px,py,str,NULL,true,2,0); + if (!esc) + { + level = atoi (str); + if (level>=0 && level<=LASTMAP-1) + { + gamestate.mapon = level; + playstate = ex_warped; + lasttext = -1; + } + } + return 1; + } + +#if 0 + if (Keyboard[sc_X]) // X = item cheat + { + CenterWindow (12,3); + US_PrintCentered ("Extra stuff!"); + VW_UpdateScreen(); + for (i=0;i<4;i++) + { + GiveBolt (); + GiveNuke (); + GivePotion (); + } + IN_Ack (); + return 1; + } +#endif + +//////// if (LastScan >= sc_1 && LastScan <= sc_8) // free scrolls +//////// { +//////// GiveScroll (LastScan-sc_1,false); +//////// IN_ClearKeysDown (); +//////// } + + return 0; +} + + +#if DEBUG_OVERHEAD + +/* +===================== += += LatchDrawChar += +===================== +*/ + +void LatchDrawChar (unsigned x, unsigned y, unsigned picnum) +{ + unsigned source, dest; + + dest = bufferofs + ylookup[y]+x; + source = latchpics[0]+picnum*8; + + EGAWRITEMODE(1); + EGAMAPMASK(15); + +asm mov bx,[linewidth] +asm dec bx + +asm mov ax,[screenseg] +asm mov es,ax +asm mov ds,ax + +asm mov si,[source] +asm mov di,[dest] + +asm movsb +asm add di,bx +asm movsb +asm add di,bx +asm movsb +asm add di,bx +asm movsb +asm add di,bx +asm movsb +asm add di,bx +asm movsb +asm add di,bx +asm movsb +asm add di,bx +asm movsb + +asm mov ax,ss +asm mov ds,ax // restore turbo's data segment + + EGAWRITEMODE(0); +} + +#endif + + +#if DEBUG_OVERHEAD +/* +===================== += += LatchDrawTile += +===================== +*/ + +void LatchDrawTile (unsigned x, unsigned y, unsigned picnum) +{ + unsigned source, dest; + + dest = bufferofs + ylookup[y]+x; + source = tileoffsets[picnum]; + + EGAWRITEMODE(1); + EGAMAPMASK(15); + +asm mov bx,[linewidth] +asm sub bx,2 + +asm mov ax,[screenseg] +asm mov es,ax +asm mov ds,ax + +asm mov si,[source] +asm mov di,[dest] +asm mov dx,16 + +lineloop: +asm movsb +asm movsb +asm add di,bx + +asm dec dx +asm jnz lineloop + +asm mov ax,ss +asm mov ds,ax // restore turbo's data segment + + EGAWRITEMODE(0); +} +#endif + + +#if DEBUG_OVERHEAD +/* +=================== += += OverheadRefresh += +=================== +*/ + +void OverheadRefresh (void) +{ + unsigned x,y,endx,endy,sx,sy; + unsigned tile; + + + if (++screenpage == 3) + screenpage = 0; + + bufferofs = screenloc[screenpage]; + + endx = maporgx+VIEWTILEX; + endy = maporgy+VIEWTILEY; + + for (y=maporgy;y>12)); + LatchDrawChar(sx+1,sy,NUMBERCHARS+((tile&0x0f00)>>8)); + LatchDrawChar(sx,sy+8,NUMBERCHARS+((tile&0x00f0)>>4)); + LatchDrawChar(sx+1,sy+8,NUMBERCHARS+(tile&0x000f)); + } + } + + VW_SetScreen (bufferofs,0); + displayofs = bufferofs; +} + + +/* +=================== += += ViewMap += +=================== +*/ + +void ViewMap (void) +{ + boolean button0held; + + viewtype = actoratview; + button0held = false; + + + maporgx = player->tilex - VIEWTILEX/2; + if (maporgx<0) + maporgx = 0; + maporgy = player->tiley - VIEWTILEY/2; + if (maporgy<0) + maporgy = 0; + + do + { +// +// let user pan around +// + IN_ReadControl(0,&control); + if (control.xaxis == -1 && maporgx>0) + maporgx--; + if (control.xaxis == 1 && maporgx0) + maporgy--; + if (control.yaxis == 1 && maporgyVIEWYH) + Quit("DrawLine: y>VIEWYH"); + + xlp = xl&7; + maskleft = leftmask[xlp]; + maskright = rightmask[xh&7]; + + mid = xhb-xlb-1; + dest = bufferofs+ylookup[y]+xlb; + + // + // set the GC index register to point to the bit mask register + // + asm mov al,GC_BITMASK + asm mov dx,GC_INDEX + asm out dx,al + + if (xlb==xhb) + { + // + // entire line is in one byte + // + + maskleft&=maskright; + + asm mov es,[screenseg] + asm mov di,[dest] + asm mov dx,GC_INDEX+1 + + asm mov al,[BYTE PTR maskleft] + asm out dx,al // mask off pixels + + asm mov al,[BYTE PTR color] + asm xchg al,[es:di] // load latches and write pixels + + return; + } + +asm mov es,[screenseg] +asm mov di,[dest] +asm mov dx,GC_INDEX+1 +asm mov bh,[BYTE PTR color] + +// +// draw left side +// +asm mov al,[BYTE PTR maskleft] +asm out dx,al // mask off pixels + +asm mov al,bh +asm xchg al,[es:di] // load latches and write pixels +asm inc di + +// +// draw middle +// +asm mov al,255 +asm out dx,al // no masking + +asm mov al,bh +asm mov cx,[mid] +asm rep stosb + +// +// draw right side +// +asm mov al,[BYTE PTR maskright] +asm out dx,al // mask off pixels +asm xchg bh,[es:di] // load latches and write pixels + +} + +//========================================================================== + +void DrawLineDot (int xl, int xh, int y,int color) +{ + unsigned dest,xlp,xlb,xhb,maskleft,maskright,maskdot,mid; + + xlb=xl/8; + xhb=xh/8; + + if (xhVIEWYH) + Quit("DrawLine: y>VIEWYH"); + + xlp = xl&7; + maskdot = dotmask[xlp]; + maskleft = leftmask[xlp]; + maskright = rightmask[xh&7]; + + mid = xhb-xlb-1; + dest = bufferofs+ylookup[y]+xlb; + + // + // set the GC index register to point to the bit mask register + // + asm mov al,GC_BITMASK + asm mov dx,GC_INDEX + asm out dx,al + + if (xlb==xhb) + { + // + // entire line is in one byte + // + + maskleft&=maskright; + + asm mov es,[screenseg] + asm mov di,[dest] + asm mov dx,GC_INDEX+1 + + asm mov al,[BYTE PTR maskleft] + asm out dx,al // mask off pixels + + asm mov al,[BYTE PTR color] + asm xchg al,[es:di] // load latches and write pixels + + + // + // write the black dot at the start + // + asm mov al,[BYTE PTR maskdot] + asm out dx,al // mask off pixels + + asm xor al,al + asm xchg al,[es:di] // load latches and write pixels + + + return; + } + +asm mov es,[screenseg] +asm mov di,[dest] +asm mov dx,GC_INDEX+1 +asm mov bh,[BYTE PTR color] + +// +// draw left side +// +asm mov al,[BYTE PTR maskleft] +asm out dx,al // mask off pixels + +asm mov al,bh +asm xchg al,[es:di] // load latches and write pixels + +// +// write the black dot at the start +// +asm mov al,[BYTE PTR maskdot] +asm out dx,al // mask off pixels +asm xor al,al +asm xchg al,[es:di] // load latches and write pixels +asm inc di + +// +// draw middle +// +asm mov al,255 +asm out dx,al // no masking + +asm mov al,bh +asm mov cx,[mid] +asm rep stosb + +// +// draw right side +// +asm mov al,[BYTE PTR maskright] +asm out dx,al // mask off pixels +asm xchg bh,[es:di] // load latches and write pixels + +} + +#endif + +//========================================================================== + + +long wallscalesource; + +#ifdef DRAWEACH +/* +==================== += += ScaleOneWall += +==================== +*/ + +void near ScaleOneWall (int xl, int xh) +{ + int x,pixwidth,height; + + *(((unsigned *)&wallscalesource)+1) = wallseg[xl]; + + for (x=xl;x<=xh;x+=pixwidth) + { + height = wallheight[x]; + pixwidth = wallwidth[x]; + (unsigned)wallscalesource = wallofs[x]; + + *(((unsigned *)&scaletablecall)+1) = (unsigned)scaledirectory[height]; + (unsigned)scaletablecall = scaledirectory[height]->codeofs[0]; + + // + // scale a byte wide strip of wall + // + asm mov bx,[x] + asm mov di,bx + asm shr di,1 + asm shr di,1 + asm shr di,1 // X in bytes + asm add di,[bufferofs] + asm and bx,7 + asm shl bx,1 + asm shl bx,1 + asm shl bx,1 + asm add bx,[pixwidth] // bx = pixel*8+pixwidth-1 + asm dec bx + asm mov al,BYTE PTR [bitmasks1+bx] + asm mov dx,GC_INDEX+1 + asm out dx,al // set bit mask register + asm mov es,[screenseg] + asm lds si,[wallscalesource] + asm call [DWORD PTR ss:scaletablecall] // scale the line of pixels + + asm mov al,BYTE PTR [ss:bitmasks2+bx] + asm or al,al + asm jz nosecond + + // + // draw a second byte for vertical strips that cross two bytes + // + asm inc di + asm out dx,al // set bit mask register + asm call [DWORD PTR ss:scaletablecall] // scale the line of pixels + nosecond: + asm mov ax,ss + asm mov ds,ax + } +} + +#endif + +char wall_anim_pos[NUMFLOORS]; + +// EAST / WEST WALLS +// +int far walllight1[NUMFLOORS] = {0, + + CRYSTAL_LIGHT_1PIC, + CRYSTAL_LIGHT_2PIC, + CRYSTAL_LIGHT_3PIC, + CRYSTAL_LIGHT_4PIC, //4 + + FIRE_WALL_1PIC, + FIRE_WALL_2PIC, + FIRE_WALL_3PIC, + FIRE_WALL_4PIC, //8 + + BRN_STONE_GATEPIC, + BRN_STONE_WALL_1PIC, + KUDZU_WEAK_LIGHTPIC, + KUDZU_LIGHT_WALLPIC, + HEDGE_WALLPIC, + HEDGE_EYESPIC, //14 + + W_GEN_DOOR1PIC, //15 + BRN_WINDOW_LIGHTPIC, + + ALTAR_LEFTPIC, + ALTAR_RIGHTPIC, + GRAY_LIGHT_WALLPIC, + GRAY_LIGHT_SIGNPIC, //20 + + MANICLE_LIGHT_WALLPIC, + MANICLE_LIGHT_BLOODYPIC, + + LIGHT_CURTAIN_WINDOWPIC, + LIGHT_CURTAIN_WALLPIC, + BRN_LIGHT_SIGNPIC, //25 + + LIGHT_STONE_WALLPIC, + + W_GEN_DOOR2PIC, //27 + + TROLL_LIGHT_STONEPIC, + + BRN_FLAGSTONE_LIGHT_2PIC, + + W_CRYSTAL_DOORPIC, + + DMG_BRN_FSTN_LTPIC, + + RUST_METAL_LIGHTPIC, + GRAY_METAL_LIGHTPIC, //33 + + WEAK_STONE_LIGHTPIC, + + DMG_FIN_FSTN_LTPIC, + + WEAK_GRAY_RFGSTN_LIGHTPIC, + 0, + + WEAK_CRYSTAL_LIGHTPIC, + + RED_MUD_LIGHTPIC, + + STEEL_DOOR1PIC, //40 + + RED_MUD_WEAK_LIGHTPIC, + + STEEL_DOOR2PIC, //42 + + HORN_DOORPIC, + TROLL_BLOODY_LT_STONEPIC, + CLOSED_DOOR_1PIC, + + GRY_DOOR_LTPIC, //46 + + BRN_DOOR_LTPIC, //47 + + GRY_FGSTN_LTPIC, //48 + DOOR_2PIC, + + WATER_LIGHT_WEAK_1PIC, + WATER_LIGHT_WEAK_2PIC, + WATER_LIGHT_WEAK_3PIC, //52 + + WATER_LIGHT_1PIC, + WATER_LIGHT_2PIC, + WATER_LIGHT_3PIC, + + LIGHT_BREATH_1PIC, + LIGHT_BREATH_2PIC, + LIGHT_BREATH_3PIC, //58 + + EXP_WALL_1PIC, + EXP_WALL_2PIC, + EXP_WALL_3PIC, + + WATER_EXP_WALL_1PIC, + WATER_EXP_WALL_2PIC, + WATER_EXP_WALL_3PIC, //64 + + FINALWALLPIC, + + LT_SKEL1PIC, + DK_SKEL1PIC, + LT_SKEL2PIC, + DK_SKEL2PIC, + + 0, + + TAP_1PIC, + TAP_2PIC, + TAP_3PIC, + TAP_4PIC, + TAP_5PIC, + + WATER_DOOR1_PIC, + WATER_DOOR2_PIC, + }; + +// NORTH / SOUTH WALLS +// +int far walldark1[NUMFLOORS] = {0, + + CRYSTAL_DARK_1PIC, + CRYSTAL_DARK_2PIC, + CRYSTAL_DARK_3PIC, + CRYSTAL_DARK_4PIC, //4 + + FIRE_WALL_1PIC, + FIRE_WALL_2PIC, + FIRE_WALL_3PIC, + FIRE_WALL_4PIC, //8 + + BRN_STONE_GATEPIC, + BRN_STONE_WALL_2PIC, + KUDZU_WEAK_DARKPIC, + KUDZU_DARK_WALLPIC, + HEDGE_WALLPIC, + HEDGE_EYESPIC, //14 + + W_GEN_DOOR1PIC, //15 + BRN_WINDOW_DARKPIC, + + ALTAR_LEFTPIC, + ALTAR_RIGHTPIC, + GRAY_DARK_WALLPIC, + GRAY_DARK_SIGNPIC, //20 + + MANICLE_DARK_WALLPIC, + MANICLE_DARK_BLOODYPIC, + + DARK_CURTAIN_WINDOWPIC, + DARK_CURTAIN_WALLPIC, + BRN_DARK_SIGNPIC, + + DARK_STONE_WALLPIC, + + W_GEN_DOOR2PIC, //27 + + TROLL_DARK_STONEPIC, + + BRN_FLAGSTONE_DARK_2PIC, + + W_CRYSTAL_DOORPIC, //30 + + DMG_BRN_FSTN_DKPIC, + + RUST_METAL_DARKPIC, + GRAY_METAL_DARKPIC, + + WEAK_STONE_DARKPIC, + + DMG_FIN_FSTN_DKPIC, //35 + + WEAK_GRAY_RFGSTN_DARKPIC, + 0, + + WEAK_CRYSTAL_DARKPIC, + + BRN_MUD_DARKPIC, + + STEEL_DOOR1PIC, //40 + + BRN_MUD_WEAK_DARKPIC, + + STEEL_DOOR2PIC, + + HORN_DOORPIC, + TROLL_BLOODY_DK_STONEPIC, + + CLOSED_DOOR_1PIC, + + GRY_DOOR_DKPIC, //46 + BRN_DOOR_DKPIC, //47 + GRY_FGSTN_DKPIC, //48 + DOOR_2PIC, + + WATER_DARK_WEAK_1PIC, + WATER_DARK_WEAK_2PIC, + WATER_DARK_WEAK_3PIC, + + WATER_DARK_1PIC, + WATER_DARK_2PIC, + WATER_DARK_3PIC, + + DARK_BREATH_1PIC, + DARK_BREATH_2PIC, + DARK_BREATH_3PIC, + + EXP_WALL_1PIC, + EXP_WALL_2PIC, + EXP_WALL_3PIC, + + WATER_EXP_WALL_1PIC, + WATER_EXP_WALL_2PIC, + WATER_EXP_WALL_3PIC, + + FINALWALLPIC, + + LT_SKEL1PIC, + DK_SKEL1PIC, + LT_SKEL2PIC, + DK_SKEL2PIC, + + 0, + + TAP_1PIC, + TAP_2PIC, + TAP_3PIC, + TAP_4PIC, + TAP_5PIC, + + WATER_DOOR1_PIC, + WATER_DOOR2_PIC, + }; + + +/* +===================== += += DrawVWall += += Draws a wall by vertical segments, for texture mapping! += += wallptr->side is true for east/west walls (constant x) += += fracheight and fracstep are 16.16 bit fractions += +===================== +*/ + +void DrawVWall (walltype *wallptr) +{ + int x,i; + unsigned source; + unsigned width,sourceint; + unsigned wallpic,wallpicseg; + unsigned skip; + long fracheight,fracstep,longheightchange; + unsigned height; + int heightchange; + unsigned slope,distance; + int traceangle,angle; + int mapadd; + unsigned lastpix,lastsource,lastwidth; + + if (wallptr->rightclip < wallptr->leftclip) + Quit ("DrawVWall: Right < Left"); + +// +// setup for height calculation +// + wallptr->height1 >>= 1; + wallptr->height2 >>= 1; + wallptr->planecoord>>=10; // remove non significant bits + + width = wallptr->x2 - wallptr->x1; + if (width) + { + heightchange = wallptr->height2 - wallptr->height1; + asm mov ax,[heightchange] + asm mov WORD PTR [longheightchange+2],ax + asm mov WORD PTR [longheightchange],0 // avoid long shift by 16 + fracstep = longheightchange/width; + } + + fracheight = ((long)wallptr->height1<<16)+0x8000; + skip = wallptr->leftclip - wallptr->x1; + if (skip) + fracheight += fracstep*skip; + +// +// setup for texture mapping +// +// mapadd is 64*64 (to keep source positive) + the origin wall intercept +// distance has 6 unit bits, and 6 frac bits +// traceangle is the center view angle in FINEANGLES, moved to be in +// the +-90 degree range (to thew right of origin) +// + traceangle = fineviewangle; + // + // find wall picture to map from + // + if (wallptr->side) + { // east or west wall + + wallpic = walllight1[wallptr->color+wall_anim_pos[wallptr->color]]; + if (wallptr->planecoord < viewxpix) + { + distance = viewxpix-wallptr->planecoord; + traceangle -= FINEANGLES/2; + mapadd = (64-viewypix&63); // the pixel spot of the origin + } + else + { + distance = wallptr->planecoord-viewxpix; + // traceangle is correct + mapadd = viewypix&63; // the pixel spot of the origin + } + } + else + { // north or south wall + + wallpic = walldark1[wallptr->color+wall_anim_pos[wallptr->color]]; + if (wallptr->planecoord < viewypix) + { + distance = viewypix-wallptr->planecoord; + traceangle -= FINEANGLES/4; + mapadd = viewxpix&63; // the pixel spot of the origin + } + else + { + distance = wallptr->planecoord-viewypix; + traceangle -= FINEANGLES*3/4; + mapadd = (64-viewxpix&63); // the pixel spot of the origin + } + } + + mapadd = 64*64-mapadd; // make sure it stays positive + + wallpicseg = (unsigned)walldirectory[wallpic-FIRSTWALLPIC]; + if (traceangle > FINEANGLES/2) + traceangle -= FINEANGLES; + +// +// calculate everything +// +// IMPORTANT! This loop is executed around 5000 times / second! +// + lastpix = lastsource = (unsigned)-1; + + for (x = wallptr->leftclip ; x <= wallptr->rightclip ; x++) + { + // + // height + // + asm mov ax,WORD PTR [fracheight] + asm mov dx,WORD PTR [fracheight+2] + asm mov cx,dx + asm add ax,WORD PTR [fracstep] + asm adc dx,WORD PTR [fracstep+2] + asm mov WORD PTR [fracheight],ax + asm mov WORD PTR [fracheight+2],dx + asm mov bx,[x] + asm shl bx,1 + asm cmp cx,MAXSCALEHEIGHT + asm jbe storeheight + asm mov cx,MAXSCALEHEIGHT +storeheight: + asm mov WORD PTR [wallheight+bx],cx + asm mov WORD PTR [zbuffer+bx],cx + +// height = fracheight>>16; +// fracheight += fracstep; +// if (height > MAXSCALEHEIGHT) +// height = MAXSCALEHEIGHT; +// wallheight[x] = zbuffer[x] = height; + + // + // texture map + // + angle = pixelangle[x]+traceangle; + if (angle<0) + angle+=FINEANGLES; + + slope = finetangent[angle]; + +// +// distance is an unsigned 6.6 bit number (12 pixel bits) +// slope is a signed 5.10 bit number +// result is a signed 11.16 bit number +// + +#if 0 + source = distance*slope; + source >>=20; + + source += mapadd; + source &= 63; // mask off the unused units + source = 63-source; + source <<= 6; // multiply by 64 for offset into pic +#endif + asm mov ax,[distance] + asm imul [slope] // ax is the source pixel + asm mov al,ah + asm shr al,1 + asm shr al,1 // low 6 bits is now pixel number + asm add ax,[mapadd] + asm and ax,63 + asm mov dx,63 + asm sub dx,ax // otherwise it is backwards + asm shl dx,1 + asm shl dx,1 + asm shl dx,1 + asm shl dx,1 + asm shl dx,1 + asm shl dx,1 // *64 to index into shape + asm mov [source],dx + + if (source != lastsource) + { + if (lastpix != (unsigned)-1) + { + wallofs[lastpix] = lastsource; + wallseg[lastpix] = wallpicseg; + wallwidth[lastpix] = lastwidth; + } + lastpix = x; + lastsource = source; + lastwidth = 1; + } + else + lastwidth++; // optimized draw, same map as last one + } + wallofs[lastpix] = lastsource; + wallseg[lastpix] = wallpicseg; + wallwidth[lastpix] = lastwidth; +} + + +//========================================================================== + + +/* +================= += += TraceRay += += Used to find the left and rightmost tile in the view area to be traced from += Follows a ray of the given angle from viewx,viewy in the global map until += it hits a solid tile += sets: += tile.x,tile.y : tile coordinates of contacted tile += tilecolor : solid tile's color += +================== +*/ + +int tilecolor; + +void TraceRay (unsigned angle) +{ + long tracex,tracey,tracexstep,traceystep,searchx,searchy; + fixed fixtemp; + int otx,oty,searchsteps; + + tracexstep = costable[angle]; + traceystep = sintable[angle]; + +// +// advance point so it is even with the view plane before we start checking +// + fixtemp = FixedByFrac(prestep,tracexstep); + tracex = viewx+fixtemp; + fixtemp = FixedByFrac(prestep,traceystep); + tracey = viewy-fixtemp; + + tile.x = tracex>>TILESHIFT; // starting point in tiles + tile.y = tracey>>TILESHIFT; + + + if (tracexstep<0) // use 2's complement, not signed magnitude + tracexstep = -(tracexstep&0x7fffffff); + + if (traceystep<0) // use 2's complement, not signed magnitude + traceystep = -(traceystep&0x7fffffff); + +// +// we assume viewx,viewy is not inside a solid tile, so go ahead one step +// + + do // until a solid tile is hit + { + otx = tile.x; + oty = tile.y; + spotvis[otx][oty] = true; + tracex += tracexstep; + tracey -= traceystep; + tile.x = tracex>>TILESHIFT; + tile.y = tracey>>TILESHIFT; + + if (tile.x!=otx && tile.y!=oty && (tilemap[otx][tile.y] || tilemap[tile.x][oty]) ) + { + // + // trace crossed two solid tiles, so do a binary search along the line + // to find a spot where only one tile edge is crossed + // + searchsteps = 0; + searchx = tracexstep; + searchy = traceystep; + do + { + searchx/=2; + searchy/=2; + if (tile.x!=otx && tile.y!=oty) + { + // still too far + tracex -= searchx; + tracey += searchy; + } + else + { + // not far enough, no tiles crossed + tracex += searchx; + tracey -= searchy; + } + + // + // if it is REAL close, go for the most clockwise intersection + // + if (++searchsteps == 16) + { + tracex = (long)otx<0) + { + if (traceystep<0) + { + tracex += TILEGLOBAL-1; + tracey += TILEGLOBAL; + } + else + { + tracex += TILEGLOBAL; + } + } + else + { + if (traceystep<0) + { + tracex --; + tracey += TILEGLOBAL-1; + } + else + { + tracey --; + } + } + } + + tile.x = tracex>>TILESHIFT; + tile.y = tracey>>TILESHIFT; + + } while (( tile.x!=otx && tile.y!=oty) || (tile.x==otx && tile.y==oty) ); + } + } while (!(tilecolor = tilemap[tile.x][tile.y]) ); + +} + +//========================================================================== + + +/* +======================== += += FixedByFrac += += multiply a 16/16 bit, 2's complement fixed point number by a 16 bit += fraction, passed as a signed magnitude 32 bit number += +======================== +*/ + +#pragma warn -rvl // I stick the return value in with ASMs + +fixed FixedByFrac (fixed a, fixed b) +{ + fixed value; + +// +// setup +// +asm mov si,[WORD PTR b+2] // sign of result = sign of fraction + +asm mov ax,[WORD PTR a] +asm mov cx,[WORD PTR a+2] + +asm or cx,cx +asm jns aok: // negative? +asm not ax +asm not cx +asm add ax,1 +asm adc cx,0 +asm xor si,0x8000 // toggle sign of result +aok: + +// +// multiply cx:ax by bx +// +asm mov bx,[WORD PTR b] +asm mul bx // fraction*fraction +asm mov di,dx // di is low word of result +asm mov ax,cx // +asm mul bx // units*fraction +asm add ax,di +asm adc dx,0 + +// +// put result dx:ax in 2's complement +// +asm test si,0x8000 // is the result negative? +asm jz ansok: +asm not ax +asm not dx +asm add ax,1 +asm adc dx,0 + +ansok:; + +} + +#pragma warn +rvl + +#if 0 +/* +========================= += += FixedAdd += += add two 16 bit fixed point numbers += to subtract, invert the sign of B before invoking += +========================= +*/ + +fixed FixedAdd (fixed a, fixed b) +{ + fixed value; + +asm mov ax,[WORD PTR a] +asm mov dx,[WORD PTR a+2] + +asm mov bx,[WORD PTR b] +asm mov cx,[WORD PTR b+2] + +asm or dx,dx +asm jns aok: // negative? +asm and dx,0x7fff +asm not ax // convert a from signed magnitude to 2's compl +asm not dx +asm add ax,1 +asm adc dx,0 +aok: + +asm or cx,cx +asm jns bok: // negative? +asm and cx,0x7fff +asm not bx // convert b from signed magnitude to 2's compl +asm not cx +asm add bx,1 +asm adc cx,0 +bok: + +asm add ax,bx // perform the addition +asm adc dx,cx +asm jns done + +asm and dx,0x7fff // value was negative +asm not ax // back to signed magnitude +asm not dx +asm add ax,1 +asm adc dx,0 + +done: + +asm mov [WORD PTR value],ax +asm mov [WORD PTR value+2],dx + + return value; +} +#endif + +//========================================================================== + + +/* +======================== += += TransformPoint += += Takes paramaters: += gx,gy : globalx/globaly of point += += globals: += viewx,viewy : point of view += viewcos,viewsin : sin/cos of viewangle += += += defines: += CENTERX : pixel location of center of view window += TILEGLOBAL : size of one += FOCALLENGTH : distance behind viewx/y for center of projection += scale : conversion from global value to screen value += += returns: += screenx,screenheight: projected edge location and size += +======================== +*/ + +void TransformPoint (fixed gx, fixed gy, int *screenx, unsigned *screenheight) +{ + int ratio; + fixed gxt,gyt,nx,ny; + +// +// translate point to view centered coordinates +// + gx = gx-viewx; + gy = gy-viewy; + +// +// calculate newx +// + gxt = FixedByFrac(gx,viewcos); + gyt = FixedByFrac(gy,viewsin); + nx = gxt-gyt; + +// +// calculate newy +// + gxt = FixedByFrac(gx,viewsin); + gyt = FixedByFrac(gy,viewcos); + ny = gyt+gxt; + +// +// calculate perspective ratio +// + if (nx<0) + nx = 0; + + ratio = nx*scale/FOCALLENGTH; + + if (ratio<=MINRATIO) + ratio = MINRATIO; + + *screenx = CENTERX + ny/ratio; + + *screenheight = TILEGLOBAL/ratio; + +} + + +// +// transform actor +// +void TransformActor (objtype *ob) +{ + int ratio; + fixed gx,gy,gxt,gyt,nx,ny; + +// +// translate point to view centered coordinates +// + gx = ob->x-viewx; + gy = ob->y-viewy; + +// +// calculate newx +// + gxt = FixedByFrac(gx,viewcos); + gyt = FixedByFrac(gy,viewsin); + nx = gxt-gyt-ob->size; + +// +// calculate newy +// + gxt = FixedByFrac(gx,viewsin); + gyt = FixedByFrac(gy,viewcos); + ny = gyt+gxt; + +// +// calculate perspective ratio +// + if (nx<0) + nx = 0; + + ratio = nx*scale/FOCALLENGTH; + + if (ratio<=MINRATIO) + ratio = MINRATIO; + + ob->viewx = CENTERX + ny/ratio; + + ob->viewheight = TILEGLOBAL/ratio; +} + +//========================================================================== + +fixed TransformX (fixed gx, fixed gy) +{ + int ratio; + fixed gxt,gyt,nx,ny; + +// +// translate point to view centered coordinates +// + gx = gx-viewx; + gy = gy-viewy; + +// +// calculate newx +// + gxt = FixedByFrac(gx,viewcos); + gyt = FixedByFrac(gy,viewsin); + + return gxt-gyt; +} + +//========================================================================== + +/* +================== += += BuildTables += += Calculates: += += scale projection constant += sintable/costable overlapping fractional tables += firstangle/lastangle angles from focalpoint to left/right view edges += prestep distance from focal point before checking for tiles += +================== +*/ + +void BuildTables (void) +{ + int i; + long intang; + long x; + float angle,anglestep,radtoint; + double tang; + fixed value; + +// +// calculate the angle offset from view angle of each pixel's ray +// + radtoint = (float)FINEANGLES/2/PI; + for (i=0;iMAXINT) + intang = 0x8f00 | (intang & 0xff); + else if (intangangle),12); + while (pfoffset >= 640) + pfoffset -= 640; + LatchDrawPicStrip(0,0,SKY1PIC+topimage,pfoffset+8); + } + + if (bottomimage) + { +//// pfoffset = LONG_PERCENTAGE(3200,359,(359-player->angle),12)+320; +// pfoffset += 320; +// while (pfoffset >= 640) +// pfoffset -= 640; +// LatchDrawPicStrip(0,64,SKY1PIC+topimage,pfoffset+8); + bottomimage -= 16; + LatchDrawPic(0,64,GND1PIC+bottomimage); + } +#endif + + +asm mov dx,GC_INDEX +asm mov ax,GC_MODE + 256*10 // read mode 1, write mode 2 +asm out dx,ax +asm mov al,GC_BITMASK +asm out dx,al + +} + +//========================================================================== + +/* +===================== += += DrawWallList += += Clips and draws all the walls traced this refresh += +===================== +*/ + +void DrawWallList (void) +{ + int i,leftx,newleft,rightclip; + walltype *wall, *check; + +asm mov ax,ds +asm mov es,ax +asm mov di,OFFSET wallwidth +asm xor ax,ax +asm mov cx,VIEWWIDTH/2 +asm rep stosw + + ClearScreen (); + + rightwall->x1 = VIEWXH+1; + rightwall->height1 = 32000; + (rightwall+1)->x1 = 32000; + + leftx = -1; + + for (wall=&walls[1];wall= wall->x2) + continue; + + rightclip = wall->x2; + + check = wall+1; + while (check->x1 <= rightclip && check->height1 >= wall->height2) + { + rightclip = check->x1-1; + check++; + } + + if (rightclip>VIEWXH) + rightclip=VIEWXH; + + if (leftx < wall->x1 - 1) + newleft = wall->x1-1; // there was black space between walls + else + newleft = leftx; + + if (rightclip > newleft) + { + wall->leftclip = newleft+1; + wall->rightclip = rightclip; + DrawVWall (wall); + leftx = rightclip; + } + } + +#ifndef DRAWEACH + ScaleWalls (); // draw all the walls +#endif +} + +//========================================================================== + +/* +===================== += += DrawScaleds += += Draws all objects that are visable += +===================== +*/ + +objtype *depthsort[MAXACTORS]; + +void DrawScaleds (void) +{ +#if USE_INERT_LIST + extern inertobjtype inertobjlist[], *inert; + + boolean inertlist=false; +#endif + int i,j,least,numvisable,height; + objtype *obj,**vislist,*farthest; + memptr shape; + byte *tilespot,*visspot; + + numvisable = 0; + +// +// calculate base positions of all objects +// + vislist = &depthsort[0]; + + obj = player->next; + while (obj) + { + tilespot = &tilemap[0][0]+(obj->tilex<<6)+obj->tiley; + visspot = &spotvis[0][0]+(obj->tilex<<6)+obj->tiley; + // + // could be in any of the nine surrounding tiles + // + if (*visspot + || ( *(visspot-1) && !*(tilespot-1) ) + || ( *(visspot+1) && !*(tilespot+1) ) + || ( *(visspot-65) && !*(tilespot-65) ) + || ( *(visspot-64) && !*(tilespot-64) ) + || ( *(visspot-63) && !*(tilespot-63) ) + || ( *(visspot+65) && !*(tilespot+65) ) + || ( *(visspot+64) && !*(tilespot+64) ) + || ( *(visspot+63) && !*(tilespot+63) ) ) + { +#if USE_INERT_LIST + if (!inertlist) +#endif + if ((obj->active == noalways) || (obj->active == always)) + obj->active = always; + else + obj->active = yes; + TransformActor (obj); + if (!obj->viewheight || obj->viewheight > VIEWWIDTH) + goto cont; // too close or far away + + if (!obj->state->shapenum) + goto cont; + + *vislist++ = obj; + numvisable++; + } + else +#if USE_INERT_LIST + if (!inertlist) +#endif + if ((obj->active != always) && (obj->active != noalways)) + obj->active = no; + +cont:; + obj = obj->next; +#if USE_INERT_LIST + if ((!obj) && (!inertlist)) + { + if (inert != inertobjlist) + obj = (objtype *)inertobjlist; + inertlist = true; + } +#endif + } + + if (vislist == &depthsort[0]) + return; // no visable objects + +// +// draw from back to front +// + for (i = 0; iviewheight; + if (height < least) + { + least = height; + farthest = depthsort[j]; + } + } + // + // draw farthest + // + shape = shapedirectory[farthest->state->shapenum-FIRSTSCALEPIC]; + ScaleShape(farthest->viewx,shape,farthest->viewheight); + farthest->viewheight = 32000; + } +} + +//========================================================================== + + +/* +===================== += += CalcTics += +===================== +*/ + +void CalcTics (void) +{ + long newtime,oldtimecount; + + +#ifdef PROFILE + tics = 1; + return; +#endif + +// +// calculate tics since last refresh for adaptive timing +// + if (lasttimecount > TimeCount) + TimeCount = lasttimecount; // if the game was paused a LONG time + +#if 0 + if (DemoMode) // demo recording and playback needs + { // to be constant +// +// take DEMOTICS or more tics, and modify Timecount to reflect time taken +// + oldtimecount = lasttimecount; + while (TimeCountMAXTICS) + { + TimeCount -= (tics-MAXTICS); + tics = MAXTICS; + } + + if (realtics>MAXREALTICS) + realtics = MAXREALTICS; + } +} + + +//========================================================================== + + +/* +======================== += += DrawHand += +======================== +*/ + +void DrawHand (void) +{ + #define HAND_X_POS ((VIEWWIDTH/16)-(10/2)) // "10" = hand width in bytes + + #define picnum HAND1PICM + + memptr source; + unsigned dest,width,height; + +// if (gamestate.shotpower || boltsleft) +// picnum += (((unsigned)TimeCount>>3)&1); + + source = grsegs[picnum]; + dest = ylookup[VIEWHEIGHT-handheight]+HAND_X_POS+bufferofs; // 12 + width = picmtable[picnum-STARTPICM].width; + height = picmtable[picnum-STARTPICM].height; + + VW_MaskBlock(source,0,dest,width,handheight,width*height); + EGAMAPMASK(15); +} + +//========================================================================== + + +/* +======================== += += ThreeDRefresh += +======================== +*/ + +void ThreeDRefresh (void) +{ + int tracedir; + +restart: + aborttrace = false; + +// +// clear out the traced array +// +asm mov ax,ds +asm mov es,ax +asm mov di,OFFSET spotvis +asm xor ax,ax +asm mov cx,[mapwidth] // mapheight*32 words +asm shl cx,1 +asm shl cx,1 +asm shl cx,1 +asm shl cx,1 +asm shl cx,1 +asm rep stosw + + +// +// set up variables for this view +// + + viewangle = player->angle; + fineviewangle = viewangle*(FINEANGLES/ANGLES); + viewsin = sintable[viewangle]; + viewcos = costable[viewangle]; + viewx = player->x - FixedByFrac(FOCALLENGTH,viewcos); + viewy = player->y + FixedByFrac(FOCALLENGTH,viewsin); + viewx &= 0xfffffc00; // stop on a pixel boundary + viewy &= 0xfffffc00; + viewx += 0x180; + viewy += 0x180; + viewxpix = viewx>>10; + viewypix = viewy>>10; + + focal.x = viewx>>TILESHIFT; + focal.y = viewy>>TILESHIFT; + +// +// find the rightmost visable tile in view +// + tracedir = viewangle + lastangle; + if (tracedir<0) + tracedir+=ANGLES; + else if (tracedir>=ANGLES) + tracedir-=ANGLES; + TraceRay( tracedir ); + right.x = tile.x; + right.y = tile.y; + +// +// find the leftmost visable tile in view +// + tracedir = viewangle + firstangle; + if (tracedir<0) + tracedir+=ANGLES; + else if (tracedir>=ANGLES) + tracedir-=ANGLES; + TraceRay( tracedir ); + +// +// follow the walls from there to the right +// + rightwall = &walls[1]; + FollowWalls (); + + if (aborttrace) + goto restart; + +// +// actually draw stuff +// + if (++screenpage == 3) + screenpage = 0; + + bufferofs = screenloc[screenpage]; + + EGAWRITEMODE(2); + EGAMAPMASK(15); + +// +// draw the wall list saved be FollowWalls () +// +// animframe = (TimeCount&8)>>3; + +// +// draw all the scaled images +// + asm mov dx,GC_INDEX + + asm mov ax,GC_COLORDONTCARE + asm out dx,ax // don't look at any of the planes + + asm mov ax,GC_MODE + 256*(10) // read mode 1, write mode 2 + asm out dx,ax + + asm mov al,GC_BITMASK + asm out dx,al + + AnimateWallList(); + DrawWallList(); + DrawScaleds(); + + EGAWRITEMODE(0); + EGABITMASK(0xff); + +// +// draw hand +// + if (handheight) + DrawHand (); + +// +// show screen and time last cycle +// + if (fizzlein) + { + fizzlein = false; + FizzleFade(bufferofs,displayofs,VIEWWIDTH,VIEWHEIGHT,true); + lasttimecount = TimeCount; + if (MousePresent) Mouse(MDelta); // Clear accumulated mouse movement + } + +asm cli +asm mov cx,[bufferofs] +asm mov dx,3d4h // CRTC address register +asm mov al,0ch // start address high register +asm out dx,al +asm inc dx +asm mov al,ch +asm out dx,al // set the high byte +asm dec dx +asm mov al,0dh // start address low register +asm out dx,al +asm inc dx +asm mov al,cl +asm out dx,al // set the low byte +asm sti + + displayofs = bufferofs; + + CalcTics (); + +} + diff --git a/16/cawat/C5_GAME.C b/16/cawat/C5_GAME.C new file mode 100644 index 00000000..14693a72 --- /dev/null +++ b/16/cawat/C5_GAME.C @@ -0,0 +1,1657 @@ +/* Catacomb Armageddon Source Code + * Copyright (C) 1993-2014 Flat Rock Software + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +// C3_GAME.C + +#include + +#include "DEF.H" +#include "gelib.h" +#pragma hdrstop + +#ifdef PROFILE +#include "TIME.H" +#endif + + +/* +============================================================================= + + LOCAL CONSTANTS + +============================================================================= +*/ + +#define NUMLUMPS 61 + +#define SUCCUBUSLUMP 0 +#define FATDEMONLUMP 1 +#define BOLTLUMP 2 +#define NUKELUMP 3 +#define POTIONLUMP 4 +#define RKEYLUMP 5 +#define YKEYLUMP 6 +#define GKEYLUMP 7 +#define BKEYLUMP 8 +//#define SCROLLLUMP 9 +#define CHESTLUMP 10 +#define PLAYERLUMP 11 +#define WALL1LUMP 12 +#define WALL2LUMP 13 +#define BDOORLUMP 14 +#define GODESSLUMP 15 +#define MAGELUMP 16 +#define BATLUMP 17 +#define GRELLUMP 18 +#define TOMBSTONESLUMP 19 +#define ZOMBIELUMP 20 +#define ANTLUMP 21 +#define SKELETONLUMP 22 +#define RGEMLUMP 23 +#define GGEMLUMP 24 +#define BGEMLUMP 25 +#define YGEMLUMP 26 +#define PGEMLUMP 27 +//#define RKEY2LUMP 28 +#define DRAGONLUMP 29 +#define OBJ_WARPLUMP 30 +#define EYELUMP 31 +#define REDDEMONLUMP 32 +//#define PITLUMP 33 +#define FTIMELUMP 34 +#define WATERCHESTLUMP 35 +#define TREELUMP 36 +#define ARCH1LUMP 37 +#define BUNNYLUMP 38 +#define ANTHILLLUMP 39 +#define COLUMNLUMP 40 +#define SULPHURGASLUMP 41 +#define FIREPOTLUMP 42 +//#define WHIRLPOOLLUMP 43 +#define FOUNTAINLUMP 44 +#define FORCEFIELDLUMP 45 +#define ARCH2LUMP 46 +#define ARCH3LUMP 47 +#define ARCH4LUMP 48 +#define ARCH5LUMP 49 +#define ARCH6LUMP 50 +#define SKELHANGLUMP 51 +//#define SKELPILELUMP 52 +#define ARCH7LUMP 53 +#define ARCH8LUMP 54 +#define ARCH9LUMP 55 +#define ARCH10LUMP 56 +#define ARCH11LUMP 57 +#define ARCH12LUMP 58 +#define ARCH13LUMP 59 + +int lumpstart[NUMLUMPS] = { +SUCCUBUS_LUMP_START, +FATDEMON_LUMP_START, +BOLT_LUMP_START, +NUKE_LUMP_START, +POTION_LUMP_START, +RKEY_LUMP_START, +YKEY_LUMP_START, +GKEY_LUMP_START, +BKEY_LUMP_START, +0, +//SCROLL_LUMP_START, +CHEST_LUMP_START, +PLAYER_LUMP_START, +//WALL1_LUMP_START, +//WALL2_LUMP_START, +//BDOOR_LUMP_START, +0,0,0, +GODESS_LUMP_START, +MAGE_LUMP_START, +BAT_LUMP_START, +GREL_LUMP_START, +TOMBSTONES_LUMP_START, +ZOMBIE_LUMP_START, +ANT_LUMP_START, +SKELDUDE_LUMP_START, +RGEM_LUMP_START, +GGEM_LUMP_START, +BGEM_LUMP_START, +YGEM_LUMP_START, +PGEM_LUMP_START, +0, //RKEY2_LUMP_START, +DRAGON_LUMP_START, +OBJ_WARP_LUMP_START, +EYE_LUMP_START, +REDDEMON_LUMP_START, +0, //PIT_LUMP_START, +TIME_LUMP_START, +O_WATER_CHEST_LUMP_START, +TREE_LUMP_START, +ARCH1_LUMP_START, +BUNNY_LUMP_START, +ANTHILL_LUMP_START, +COLUMN_LUMP_START, +SULPHURGAS_LUMP_START, +FIREPOT_LUMP_START, +0, //WHIRLPOOL_LUMP_START, +FOUNTAIN_LUMP_START, +FORCEFIELD_LUMP_START, +ARCH2_LUMP_START, +ARCH3_LUMP_START, +ARCH4_LUMP_START, +ARCH5_LUMP_START, +ARCH6_LUMP_START, +SKELHANG_LUMP_START, +0, //SKELPILE_LUMP_START, +ARCH7_LUMP_START, +ARCH8_LUMP_START, +ARCH9_LUMP_START, +ARCH10_LUMP_START, +ARCH11_LUMP_START, +ARCH12_LUMP_START, +ARCH13_LUMP_START, +}; + + +int lumpend[NUMLUMPS] = { +SUCCUBUS_LUMP_END, +FATDEMON_LUMP_END, +BOLT_LUMP_END, +NUKE_LUMP_END, +POTION_LUMP_END, +RKEY_LUMP_END, +YKEY_LUMP_END, +GKEY_LUMP_END, +BKEY_LUMP_END, +0, +//SCROLL_LUMP_END, +CHEST_LUMP_END, +PLAYER_LUMP_END, +0,0,0, +GODESS_LUMP_END, +MAGE_LUMP_END, +BAT_LUMP_END, +GREL_LUMP_END, +TOMBSTONES_LUMP_END, +ZOMBIE_LUMP_END, +ANT_LUMP_END, +SKELDUDE_LUMP_END, +RGEM_LUMP_END, +GGEM_LUMP_END, +BGEM_LUMP_END, +YGEM_LUMP_END, +PGEM_LUMP_END, +0, //RKEY2_LUMP_END, +DRAGON_LUMP_END, +OBJ_WARP_LUMP_END, +EYE_LUMP_END, +REDDEMON_LUMP_END, +0, //PIT_LUMP_END, +TIME_LUMP_END, +O_WATER_CHEST_LUMP_END, +TREE_LUMP_END, +ARCH1_LUMP_END, +BUNNY_LUMP_END, +ANTHILL_LUMP_END, +COLUMN_LUMP_END, +SULPHURGAS_LUMP_END, +FIREPOT_LUMP_END, +0, //WHIRLPOOL_LUMP_END, +FOUNTAIN_LUMP_END, +FORCEFIELD_LUMP_END, +ARCH2_LUMP_END, +ARCH3_LUMP_END, +ARCH4_LUMP_END, +ARCH5_LUMP_END, +ARCH6_LUMP_END, +SKELHANG_LUMP_END, +0, //SKELPILE_LUMP_END, +ARCH7_LUMP_END, +ARCH8_LUMP_END, +ARCH9_LUMP_END, +ARCH10_LUMP_END, +ARCH11_LUMP_END, +ARCH12_LUMP_END, +ARCH13_LUMP_END, +}; + + +//extern unsigned scolor,gcolor; + + +/* +============================================================================= + + GLOBAL VARIABLES + +============================================================================= +*/ + +unsigned latchpics[NUMLATCHPICS]; +unsigned tileoffsets[NUMTILE16]; +unsigned textstarts[27]; + +boolean splitscreen=false; + +/* +============================================================================= + + LOCAL VARIABLES + +============================================================================= +*/ + +boolean lumpneeded[NUMLUMPS]; + + +//=========================================================================== + +//========================================================================== +// +// +// LOCAL PROTOTYPES +// +// +//========================================================================== + +void CashPoints(void); + + + +/* +========================== += += ScanInfoPlane += += Spawn all actors and mark down special places += +========================== +*/ + +void ScanInfoPlane (void) +{ + unsigned char hibyte; + unsigned x,y,i,j; + unsigned int tile; + unsigned far *start; + + InitObjList(); // start spawning things with a clean slate + + scolor = gcolor = 0; + skycolor = &scolor; + groundcolor = &gcolor; + + + memset (lumpneeded,0,sizeof(lumpneeded)); + + start = mapsegs[2]; + for (y=0;y> 8; + tile &= 0xff; + + switch (hibyte) + { + char hi; + + case 0xFB: + wall_anim_time = tile; + tile = 0; + break; + + case 0xfa: // sky/ground color + case 0xf9: // sky/ground 'strip' + x++; + tile = *start++; + hi = tile >> 8; + tile &= 0xff; + switch (hibyte) + { + case 0xfa: // sky / ground color + scolor = ((hi)|(hi<<8)); + gcolor = ((tile)|(tile<<8)); + skycolor = &scolor; + groundcolor = &gcolor; + break; + + case 0xf9: // sky / ground 'strip' + break; + } + break; + } + + if ((!tile) || (hibyte)) + continue; + + switch (tile) + { + case 1: + case 2: + case 3: + case 4: + lumpneeded[PLAYERLUMP] = true; + SpawnPlayer(x,y,NORTH+tile-1); + break; + + case 5: + case 6: + case 7: + case 8: + case 9: + case 10: + case 11: + lumpneeded[tile-5+BOLTLUMP] = true; + SpawnBonus(x,y,tile-5); + break; + +#if 0 + case 12: + case 13: + case 14: + case 15: + case 16: + case 17: + case 18: + case 19: + lumpneeded[SCROLLLUMP] = true; + SpawnBonus(x,y,B_SCROLL1+tile-12); + break; +#endif + + case 20: + lumpneeded[REDDEMONLUMP] = true; + SpawnRedDemon (x,y); + break; + +#if 0 + case 20: // goal + lumpneeded[GOALLUMP] = true; + SpawnBonus(x,y,B_GOAL); + break; +#endif + + case 21: + lumpneeded[GODESSLUMP] = true; + SpawnGodess (x,y); + break; + + case 22: + lumpneeded[FATDEMONLUMP] = true; + SpawnFatDemon (x,y); + break; + + case 23: + lumpneeded[SUCCUBUSLUMP] = true; + SpawnSuccubus (x,y); + break; + + case 24: + lumpneeded[DRAGONLUMP] = true; + SpawnDragon(x,y); + break; + + case 25: + lumpneeded[BATLUMP] = true; + SpawnBat (x,y); + break; + + case 26: + lumpneeded[EYELUMP] = true; + SpawnEye(x,y); + break; + + case 27: + lumpneeded[MAGELUMP] = true; + SpawnMage (x,y); + break; + + case 28: + lumpneeded[RKEYLUMP] = lumpneeded[GRELLUMP] = true; + SpawnGrelminar (x,y); + break; + + case 30: + lumpneeded[ANTLUMP] = true; + SpawnAnt(x,y); + break; + + case 31: + case 32: + case 33: + case 34: + case 35: + lumpneeded[OBJ_WARPLUMP] = true; + SpawnWarp (x,y,tile-30); + break; + + case 36: + lumpneeded[ZOMBIELUMP] = true; + SpawnZombie(x,y); + break; + + case 37: + lumpneeded[SKELETONLUMP] = true; + SpawnSkeleton(x,y); + break; + + case 38: + lumpneeded[SKELETONLUMP] = true; + SpawnWallSkeleton(x,y); + break; + + case 39: + lumpneeded[FTIMELUMP] = true; + SpawnFTime(x,y); + break; + + case 40: + case 41: + case 42: + case 43: + case 44: + lumpneeded[tile-40+RGEMLUMP] = true; + SpawnBonus(x,y,tile-40+B_RGEM); + break; + + case 45: + case 46: + case 47: + lumpneeded[TOMBSTONESLUMP] = true; + SpawnTombstone(x,y,tile-45); + break; + +#if 0 + case 48: + lumpneeded[PITLUMP] = true; + SpawnWarp(x,y,0); + break; +#endif + case 49: // chest + if (gcolor == 0x0101) + lumpneeded[WATERCHESTLUMP] = true; + else + lumpneeded[CHESTLUMP] = true; + SpawnBonus(x,y,B_CHEST); + break; + + case 50: + lumpneeded[TREELUMP] = true; + SpawnTree(x,y); + break; + + case 51: + lumpneeded[BUNNYLUMP] = true; + SpawnBunny(x,y); + break; + + case 52: + lumpneeded[ARCH1LUMP] = true; + SpawnArch(x,y,1); + break; + + case 53: + lumpneeded[ANTHILLLUMP] = true; + SpawnWarp(x,y,0); + break; + + case 54: + lumpneeded[COLUMNLUMP] = true; + SpawnMiscObjects(x,y,1); //1=column,2=sulphur hole,3=fire pot,4=fountain + break; + + case 55: + lumpneeded[SULPHURGASLUMP] = true; + SpawnMiscObjects(x,y,2); + break; + + case 56: + lumpneeded[FIREPOTLUMP] = true; + SpawnMiscObjects(x,y,3); + break; + + case 57: + lumpneeded[ARCH13LUMP] = true; + SpawnArch(x,y,13); + break; + + case 58: + lumpneeded[FOUNTAINLUMP] = true; + SpawnMiscObjects(x,y,4); + break; + + case 59: + lumpneeded[FORCEFIELDLUMP] = true; + SpawnForceField(x,y); + break; + + case 60: + lumpneeded[ARCH2LUMP] = true; + SpawnArch(x,y,2); + break; + + case 61: + lumpneeded[ARCH3LUMP] = true; + SpawnArch(x,y,3); + break; + + case 62: + lumpneeded[ARCH4LUMP] = true; + SpawnArch(x,y,4); + break; + + case 63: + lumpneeded[ARCH5LUMP] = true; + SpawnArch(x,y,5); + break; + + case 64: + lumpneeded[ARCH6LUMP] = true; + SpawnArch(x,y,6); + break; + + case 65: + lumpneeded[SKELHANGLUMP] = true; + lumpneeded[SKELETONLUMP] = true; + SpawnSkeletonHanging(x,y); + break; + + case 66: + lumpneeded[ARCH12LUMP] = true; + SpawnArch(x,y,12); + break; + + case 67: + lumpneeded[ARCH7LUMP] = true; + SpawnArch(x,y,7); + break; + + case 68: + lumpneeded[ARCH8LUMP] = true; + SpawnArch(x,y,8); + break; + + case 69: + lumpneeded[ARCH9LUMP] = true; + SpawnArch(x,y,9); + break; + + case 70: + lumpneeded[ARCH10LUMP] = true; + SpawnArch(x,y,10); + break; + + case 71: + lumpneeded[ARCH11LUMP] = true; + SpawnArch(x,y,11); + break; + } + } + +} + +//========================================================================== + +/* +================== += += ScanText += +================== +*/ + +void ScanText (void) +{ + int i; + char far *text; + + text = (char _seg *)grsegs[LEVEL1TEXT+mapon]; + + textstarts[0] = 0; + + for (i=1;i<=26;i++) + { + while (*text != '\n') + { + if (*text == '\r') + *text = 0; + text++; + } + text++; + textstarts[i] = FP_OFF(text); + } + +} + +//========================================================================== + +/* +================== += += DrawEnterScreen += +================== +*/ +#if 0 +static char *levelnames[] = + { + "Programmers Test Map", + "The Garden of Tears", + "The Den of Zombies", + "The Mausoleum Grounds", + "The Main Floor of the Mausoleum", + "Mike's Blastable Passage", + "The Crypt of Nemesis the Undead", + "The Subterranean Vault", + "The Ancient Aqueduct", + "The Orc Mines", + "The Lair of the Troll", + "The Demon's Inferno", + "The Battleground of the Titans", + "The Coven of Mages", + "The Inner Sanctum", + "The Haunt of Nemesis", + "The Passage to the Surface", + "Big Jim's Domain", + "Nolan", + "19", + "20", + "21", + "22", + "23", + "24", + "25", + "26", + "27", + "28", + "29", + "30", + "31", + "32", + "33", + "34", + "35", + "36", + "37", + "38", + "39", + }; +#endif + +void DrawEnterScreen (void) +{ + int width; + + bufferofs = displayofs = screenloc[screenpage]; + VW_Bar(0,0,VIEWWIDTH,VIEWHEIGHT,0); +// width = strlen(levelnames[gamestate.mapon]); + width = strlen("You enter a new area ..."); + if (width < 20) + width = 20; + CenterWindow(width,3); + US_CPrint("\nYou enter a new area ...\n"); +// US_CPrint(levelnames[gamestate.mapon]); +} + +//========================================================================== + +boolean tileneeded[NUMFLOORS]; + + +/* +================== += += CacheScaleds += +================== +*/ + +void CacheScaleds (void) +{ + int i,j; + unsigned source,dest; + + FreeUpMemory (); + CA_CacheGrChunk(LEVEL1TEXT+mapon); + ScanText (); + +// +// make sure we are displaying screenpage 0 +// + if (screenpage) + { + source = screenloc[screenpage]; + dest = screenloc[0]; + VW_ScreenToScreen (source,dest,40,VIEWHEIGHT); + screenpage = 0; + VW_SetScreen (dest,0); + displayofs = dest; + } + +// +// cache wall pictures +// + for (i=1;iwidth; + mapheight = mapheaderseg[mapon]->height; + +// +// make a lookup table for the maps left edge +// + spot = 0; + for (y=0;y>8) == EXP_WALL_CODE) + { + exploding_walls_present = true; + } + + if (tile0) + (unsigned)actorat[x][y] = tile; + } + spotptr++; + } + + + // + // Mark any gfx chunks needed + // + +// CA_MarkGrChunk(NORTHICONSPR); +// CA_CacheMarks(NULL); + + +// +// decide which graphics are needed and spawn actors +// + zombie_base_delay = 0; // (1*60) + random(1*60); + ScanInfoPlane (); + _fmemset(wall_anim_pos,0,sizeof(wall_anim_pos)); + + +// +// mark which exploding walls are needed ---- the check for floor color +// is preformed in ScanInfoPlane. +// + + if (exploding_walls_present) + { + extern unsigned gnd_colors[]; + + if (gcolor == 0x0101) + tileneeded[WATEREXP] = tileneeded[WATEREXP+1] = tileneeded[WATEREXP+2] = true; + else + tileneeded[WALLEXP] = tileneeded[WALLEXP+1] = tileneeded[WALLEXP+2] = true; + + } + + +// +// have the caching manager load and purge stuff to make sure all marks +// are in memory +// + CA_LoadAllSounds (); +} + +//========================================================================== + +/* +===================== += += LatchDrawPic += +===================== +*/ + +void LatchDrawPic (unsigned x, unsigned y, unsigned picnum) +{ + unsigned height, source, dest; + unsigned wide; + + wide = pictable[picnum-STARTPICS].width; + height = pictable[picnum-STARTPICS].height; + dest = bufferofs + ylookup[y]+x; + source = latchpics[picnum-FIRSTLATCHPIC]; + + EGAWRITEMODE(1); + EGAMAPMASK(15); + +asm mov bx,[linewidth] +asm sub bx,[wide] + +asm mov ax,[screenseg] +asm mov es,ax +asm mov ds,ax + +asm mov si,[source] +asm mov di,[dest] +asm mov dx,[height] // scan lines to draw +asm mov ax,[wide] + +lineloop: +asm mov cx,ax +asm rep movsb +asm add di,bx + +asm dec dx +asm jnz lineloop + +asm mov ax,ss +asm mov ds,ax // restore turbo's data segment + + EGAWRITEMODE(0); +} + +#if USE_STRIPS + +//-------------------------------------------------------------------------- +// LatchDrawPicStrip() - srcoff is distance into source file (in PIXELS!) +//-------------------------------------------------------------------------- +void LatchDrawPicStrip (unsigned x, unsigned y, unsigned picnum, unsigned srcoff) +{ + unsigned wide, height, source, dest, shift, srcmod; + + shift = (srcoff & 7) >> 1; + srcoff >>= 3; + wide = pictable[picnum-STARTPICS].width; + srcmod = wide - linewidth + (shift != 3); + if (wide > linewidth) + wide = linewidth; + height = pictable[picnum-STARTPICS].height; + dest = bufferofs + ylookup[y]+x; + + picnum = ((picnum - (FIRSTSTRIPPIC+1)) >> 2) + (shift); + source = latchpics[(FIRSTSTRIPPIC-FIRSTLATCHPIC+1)+picnum]; + + EGAWRITEMODE(1); + EGAMAPMASK(15); + +asm mov bx,[linewidth] +asm sub bx,[wide] + +asm mov ax,[screenseg] +asm mov es,ax +asm mov ds,ax + +asm mov si,[source] +asm add si,[srcoff] +asm mov di,[dest] +asm mov dx,[height] // scan lines to draw +asm mov ax,[wide] + +lineloop: +asm mov cx,ax +asm rep movsb +asm add di,bx +asm add si,[srcmod] + +asm dec dx +asm jnz lineloop + +asm mov ax,ss +asm mov ds,ax // restore turbo's data segment + + EGAWRITEMODE(0); +} + +#endif + + +//========================================================================== + +/* +===================== += += Victory += +===================== +*/ + +void Victory (boolean playsounds) +{ + struct Shape shape; + + if (playsounds) + { + SD_PlaySound (GETBOLTSND); + SD_WaitSoundDone (); + SD_PlaySound (GETNUKESND); + SD_WaitSoundDone (); + SD_PlaySound (GETPOTIONSND); + SD_WaitSoundDone (); + SD_PlaySound (GETKEYSND); + SD_WaitSoundDone (); + SD_PlaySound (GETSCROLLSND); + SD_WaitSoundDone (); + SD_PlaySound (GETPOINTSSND); + } + + FreeUpMemory(); + + if (!screenfaded) + VW_FadeOut(); + + NormalScreen (); + + screenpage = bufferofs = 0; + + CA_CacheGrChunk (FINALEPIC); + UNMARKGRCHUNK(FINALEPIC); + VW_DrawPic(0, 0, FINALEPIC); + + VW_FadeIn(); +} + +//========================================================================== + +#if 0 +/* +=================== += += Died += +=================== +*/ + +void Died (void) +{ + unsigned page1,page2; +// +// fizzle fade screen to grey +// + FreeUpMemory (); + SD_PlaySound (GAMEOVERSND); + bufferofs = screenloc[(screenpage+1)%3]; + DisplayMsg("Though fallen, your Spirit ...",NULL); +// LatchDrawPic(0,0,DEADPIC); +// FizzleFade(bufferofs,displayofs,VIEWWIDTH,VIEWHEIGHT,false); + IN_ClearKeysDown(); + while (!Keyboard[sc_Enter]); +// IN_Ack(); + VW_SetScreen (bufferofs,0); + VW_ColorBorder(0); +} +#endif + +//========================================================================== + +/* +=================== += += NormalScreen += +=================== +*/ + +void NormalScreen (void) +{ + VW_SetSplitScreen (200); + bufferofs = displayofs = SCREEN1START; + VW_Bar(0,0,320,200,0); + bufferofs = SCREEN2START; + VW_Bar(0,0,320,200,0); + VW_SetScreen (displayofs,0); + splitscreen = false; +} + +//========================================================================== + +/* +=================== += += DrawPlayScreen += +=================== +*/ + +void DrawPlayScreen (void) +{ + int i,j,p,m; + + screenpage = 0; + + bufferofs = 0; + VW_Bar (0,0,320,STATUSLINES,0); + for (i=0;i<3;i++) + { + bufferofs = screenloc[i]; + VW_Bar (0,0,320,VIEWHEIGHT,0); + } + + splitscreen = true; + VW_SetSplitScreen(120); + VW_SetScreen(screenloc[0],0); + + CA_CacheGrChunk (STATUSPIC); + + bufferofs = 0; + VW_DrawPic (0,0,STATUSPIC); + + grneeded[STATUSPIC] &= ~ca_levelbit; + MM_SetPurge(&grsegs[STATUSPIC],3); + + RedrawStatusWindow (); + bufferofs = displayofs = screenloc[0]; +} + + +//========================================================================== + +/* +=================== += += LoadLatchMem += +=================== +*/ + +unsigned latchmemavail; + +void LoadLatchMem (void) +{ + static unsigned base_destoff=0; + static int base_numpics=0; + int i,j,p,m,numpics; + byte far *src, far *dest; + unsigned destoff; + + EGAWRITEMODE(0); + +// +// draw some pics into latch memory +// + + if (!base_numpics) + { + +// +// tile 8s +// + latchpics[0] = freelatch; + src = (byte _seg *)grsegs[STARTTILE8]; + dest = MK_FP(0xa000,freelatch); + + for (i=0;i>4)),destoff,planesize,1); + destoff+=planesize; + } + + // Copy unshifted strip to latch + // + latchpics[numpics++] = destoff; + planesize = pictable[pic].width * pictable[pic].height; + VW_MemToScreen (work,destoff,planesize,1); + destoff+=planesize; + + // Free work buffer + // + MM_FreePtr(&work); + } +#endif + +// Keep track of how much latch memory we have... +// + latchmemavail = 65535-destoff; + + EGAMAPMASK(15); +} + +//========================================================================== + +/* +=================== += += FizzleOut += +=================== +*/ + +void FizzleOut (int showlevel) +{ + unsigned page1,page2; +// +// fizzle fade screen to grey +// + bufferofs = screenloc[(screenpage+1)%3]; + if (showlevel) + DrawEnterScreen (); + FizzleFade(bufferofs,displayofs,VIEWWIDTH,VIEWHEIGHT,false); +} + +//========================================================================== + +/* +==================== += += FreeUpMemory += +==================== +*/ + +void FreeUpMemory (void) +{ + int i; + + for (i=0;iname); + + // + // level + // + ultoa(s->completed,buffer,10); + for (str = buffer;*str;str++) + *str = *str + (129 - '0'); // Used fixed-width numbers (129...) + USL_MeasureString(buffer,&w,&h); + PrintX = (25 * 8) - 8 - w; + US_Print(buffer); + + // + // score + // + ultoa(s->score,buffer,10); + for (str = buffer;*str;str++) + *str = *str + (129 - '0'); // Used fixed-width numbers (129...) + USL_MeasureString(buffer,&w,&h); + PrintX = (34 * 8) - 8 - w; + US_Print(buffer); + } + + fontcolor = F_BLACK; +} + + + +/* +======================= += += CheckHighScore += +======================= +*/ + +void CheckHighScore (long score,word other) +{ + word i,j; + int n; + HighScore myscore; + + strcpy(myscore.name,""); + myscore.score = score; + myscore.completed = other; + + for (i = 0,n = -1;i < MaxScores;i++) + { + if + ( + (myscore.score > Scores[i].score) + || ( + (myscore.score == Scores[i].score) + && (myscore.completed > Scores[i].completed) + ) + ) + { + for (j = MaxScores;--j > i;) + Scores[j] = Scores[j - 1]; + Scores[i] = myscore; + n = i; + HighScoresDirty = true; + break; + } + } + + if (n != -1) + { + // + // got a high score + // + DrawHighScores (); + PrintY = 68 + (16 * n); + PrintX = 60; + US_LineInput(PrintX,PrintY,Scores[n].name,nil,true,MaxHighName,100); + } +} + +#endif + + +//========================================================================== + +/* +=================== += += GameLoop += +=================== +*/ + +void GameLoop (void) +{ + boolean wait = false; + int i,xl,yl,xh,yh; + char num[20]; +#ifdef PROFILE + clock_t start,end; +#endif + + DrawPlayScreen (); + IN_ClearKeysDown(); + +restart: + if (!loadedgame) + { + gamestate.difficulty = restartgame; + restartgame = gd_Continue; + DrawEnterScreen (); + if (gamestate.mapon != 8) + fizzlein = true; + wait = true; + } + + do + { + playstate = gd_Continue; + if (!loadedgame) + SetupGameLevel (); + else + loadedgame = false; + + FreeUpMemory(); + LoadLatchMem(); + CacheScaleds (); + + if (EASYMODEON) + DisplaySMsg("*** NOVICE ***", NULL); + else + DisplaySMsg("*** WARRIOR ***", NULL); + + status_delay = 250; + + RedrawStatusWindow(); + if (wait) + { + VW_WaitVBL(120); + wait = false; + } + +#ifdef PROFILE +start = clock(); +while (start == clock()); +start++; +#endif + + PlayLoop (); + +#ifdef PROFILE +end = clock(); +itoa(end-start,str,10); + Quit (str); +#endif + + + switch (playstate) + { + case ex_abort: + FreeUpMemory (); + return; + case ex_resetgame: + NewGame(); + case ex_loadedgame: + case ex_warped: + FreeUpMemory(); + if (playstate != ex_resetgame) + DisplayMsg(" ", NULL); + DisplaySMsg(" ", NULL); + goto restart; + case ex_victorious: + screenpage = 0; + bufferofs = 0; + status_flag = 0; + return; + } + + } while (1); + +} + + +#if 0 +// +// make wall pictures purgable +// + for (i=0;i +#include + +#include "DEF.H" +#include "GELIB.H" +#pragma hdrstop +#include + +/* +============================================================================= + + LOCAL CONSTANTS + +============================================================================= +*/ + + +/* +============================================================================= + + GLOBAL VARIABLES + +============================================================================= +*/ + +PresenterInfo MainHelpText; + +GameDiff restartgame; +boolean loadedgame,abortgame,ingame; + + +memptr scalesegs[NUMPICS]; +char str[80],str2[20]; +unsigned tedlevelnum; +boolean tedlevel; +gametype gamestate; +exittype playstate; +char SlowMode = 0; +int starting_level; + +//extern unsigned scolor,gcolor; //NPM + +short NumGames=0; +unsigned Flags=0; + +boolean LoadShapes = true; +boolean EASYMODEON = false; + +void DisplayIntroText(void); + +/* +============================================================================= + + LOCAL VARIABLES + +============================================================================= +*/ + + + +//=========================================================================== + +#if 0 +// JAB Hack begin +#define MyInterrupt 0x60 +void interrupt (*intaddr)(); +void interrupt (*oldintaddr)(); + char *JHParmStrings[] = {"no386",nil}; + +void +jabhack(void) +{ +extern void far jabhack2(void); +extern int far CheckIs386(void); + + int i; + + oldintaddr = getvect(MyInterrupt); + + for (i = 1;i < _argc;i++) + if (US_CheckParm(_argv[i],JHParmStrings) == 0) + return; + + if (CheckIs386()) + { + jabhack2(); + setvect(MyInterrupt,intaddr); + } +} + +void +jabunhack(void) +{ + setvect(MyInterrupt,oldintaddr); +} +// JAB Hack end +#endif + +//=========================================================================== + +/* +===================== += += NewGame += += Set up new game to start from the beginning += +===================== +*/ + +void NewGame (void) +{ + if (!loadedgame) + { + memset (&gamestate,0,sizeof(gamestate)); + gamestate.mapon = starting_level; + gamestate.body = MAXBODY; + } + + BGFLAGS = BGF_NOT_LIGHTNING; + Flags &= FL_CLEAR; + + boltsleft = bolttimer = 0; + +// memset (gamestate.levels,-1,sizeof(gamestate.levels)); +} + +//=========================================================================== + +#define RLETAG 0xABCD + +/* +================== += += SaveTheGame += +================== +*/ + +boolean SaveTheGame(int file) +{ + word i,compressed,expanded; + objtype *o; + memptr bigbuffer; + + // save the sky and ground colors + if (!CA_FarWrite(file,(void far *)&skycolor,sizeof(skycolor))) + return(false); + if (!CA_FarWrite(file,(void far *)&groundcolor,sizeof(groundcolor))) + return(false); + + if (!CA_FarWrite(file,(void far *)&FreezeTime,sizeof(FreezeTime))) + return(false); + + if (!CA_FarWrite(file,(void far *)&gamestate,sizeof(gamestate))) + return(false); + + if (!CA_FarWrite(file,(void far *)&EASYMODEON,sizeof(EASYMODEON))) + return(false); + + expanded = mapwidth * mapheight * 2; + MM_GetPtr (&bigbuffer,expanded); + + for (i = 0;i < 3;i+=2) // Write planes 0 and 2 + { +// +// leave a word at start of compressed data for compressed length +// + compressed = (unsigned)CA_RLEWCompress ((unsigned huge *)mapsegs[i] + ,expanded,((unsigned huge *)bigbuffer)+1,RLETAG); + + *(unsigned huge *)bigbuffer = compressed; + + if (!CA_FarWrite(file,(void far *)bigbuffer,compressed+2) ) + { + MM_FreePtr (&bigbuffer); + return(false); + } + } + + for (o = player;o;o = o->next) + if (!CA_FarWrite(file,(void far *)o,sizeof(objtype))) + { + MM_FreePtr (&bigbuffer); + return(false); + } + + MM_FreePtr (&bigbuffer); + + return(true); +} + +//=========================================================================== + + +/* +================== += += LoadTheGame += +================== +*/ + +boolean LoadTheGame(int file) +{ + unsigned i,x,y; + objtype *obj,*prev,*next,*followed; + unsigned compressed,expanded; + unsigned far *map,tile; + memptr bigbuffer; + + screenpage = 0; + FreeUpMemory(); + + playstate = ex_loadedgame; + // load the sky and ground colors + if (!CA_FarRead(file,(void far *)&skycolor,sizeof(skycolor))) + return(false); + if (!CA_FarRead(file,(void far *)&groundcolor,sizeof(groundcolor))) + return(false); + + if (!CA_FarRead(file,(void far *)&FreezeTime,sizeof(FreezeTime))) + return(false); + + if (!CA_FarRead(file,(void far *)&gamestate,sizeof(gamestate))) + return(false); + + if (!CA_FarRead(file,(void far *)&EASYMODEON,sizeof(EASYMODEON))) + return(false); + + SetupGameLevel (); // load in and cache the base old level + + if (!FindFile(Filename,"SAVE GAME",-1)) + Quit("Error: Can't find saved game file!"); + + expanded = mapwidth * mapheight * 2; + MM_GetPtr (&bigbuffer,expanded); + + for (i = 0;i < 3;i+=2) // Read planes 0 and 2 + { + if (!CA_FarRead(file,(void far *)&compressed,sizeof(compressed)) ) + { + MM_FreePtr (&bigbuffer); + return(false); + } + + if (!CA_FarRead(file,(void far *)bigbuffer,compressed) ) + { + MM_FreePtr (&bigbuffer); + return(false); + } + + CA_RLEWexpand ((unsigned huge *)bigbuffer, + (unsigned huge *)mapsegs[i],expanded,RLETAG); + } + + MM_FreePtr (&bigbuffer); +// +// copy the wall data to a data segment array again, to handle doors and +// bomb walls that are allready opened +// + memset (tilemap,0,sizeof(tilemap)); + memset (actorat,0,sizeof(actorat)); + map = mapsegs[0]; + for (y=0;y0) + (unsigned)actorat[x][y] = tile; + } + } + + + // Read the object list back in - assumes at least one object in list + + InitObjList (); + new = player; + while (true) + { + prev = new->prev; + next = new->next; + if (!CA_FarRead(file,(void far *)new,sizeof(objtype))) + return(false); + followed = new->next; + new->prev = prev; + new->next = next; + actorat[new->tilex][new->tiley] = new; // drop a new marker + + if (followed) + GetNewObj (false); + else + break; + } + + return(true); +} + +//=========================================================================== + +/* +================== += += ResetGame += +================== +*/ + +void ResetGame(void) +{ + NewGame (); + + ca_levelnum--; + ca_levelbit>>=1; + CA_ClearMarks(); + ca_levelbit<<=1; + ca_levelnum++; +} + +//=========================================================================== + + +/* +========================== += += ShutdownId += += Shuts down all ID_?? managers += +========================== +*/ + +void ShutdownId (void) +{ + US_Shutdown (); +#ifndef PROFILE + SD_Shutdown (); + IN_Shutdown (); +#endif + VW_Shutdown (); + CA_Shutdown (); + MM_Shutdown (); +} + + +//=========================================================================== + +/* +========================== += += InitGame += += Load a few things right away += +========================== +*/ + +void InitGame (void) +{ + unsigned segstart,seglength; + int i,x,y; + unsigned *blockstart; + +// US_TextScreen(); + + MM_Startup (); + VW_Startup (); +#ifndef PROFILE + IN_Startup (); + SD_Startup (); +#endif + US_Startup (); + +// US_UpdateTextScreen(); + + CA_Startup (); + US_Setup (); + + US_SetLoadSaveHooks(LoadTheGame,SaveTheGame,ResetGame); + + +// +// load in and lock down some basic chunks +// + + CA_ClearMarks (); + + CA_MarkGrChunk(STARTFONT); + CA_MarkGrChunk(STARTTILE8); + CA_MarkGrChunk(STARTTILE8M); + CA_MarkGrChunk(HAND1PICM); + + CA_MarkGrChunk(NORTHICONSPR); + CA_CacheMarks (NULL); + + MM_SetLock (&grsegs[STARTFONT],true); + MM_SetLock (&grsegs[STARTTILE8],true); + MM_SetLock (&grsegs[STARTTILE8M],true); + MM_SetLock (&grsegs[HAND1PICM],true); + + fontcolor = WHITE; + + +// +// build some tables +// + for (i=0;i= MINMEMORY) + return; + + CA_CacheGrChunk (OUTOFMEM); + finscreen = (unsigned)grsegs[OUTOFMEM]; + ShutdownId (); + movedata (finscreen,7,0xb800,0,4000); + gotoxy (1,24); + exit(1); +} +#endif + +//=========================================================================== + + +/* +========================== += += main += +========================== +*/ + +char *MainParmStrings[] = {"q","l","ver","nomemcheck","helptest",nil}; + +void main (void) +{ + short i; + + starting_level = 0; + + for (i = 1;i < _argc;i++) + { + switch (US_CheckParm(_argv[i],MainParmStrings)) + { + case 0: + Flags |= FL_QUICK; + break; + + case 1: + starting_level = atoi(_argv[i]+1); + if ((starting_level < 0) || (starting_level > LASTMAP-1)) + starting_level = 0; + break; + + case 2: + printf("%s\n", GAMENAME); + printf("Copyright 1992-93 Softdisk Publishing\n"); + printf("%s %s\n",VERSION,REVISION); + printf("\n"); + exit(0); + break; + + case 3: + Flags |= FL_NOMEMCHECK; + break; + + case 4: + Flags |= (FL_HELPTEST|FL_QUICK); + break; + } + } + + if (stricmp(_argv[1], "^(a@&r`")) + Quit("You must type CATARM to run CATACOMB ARMAGEDDON 3-D\n"); + + MainHelpText.xl = 0; + MainHelpText.yl = 0; + MainHelpText.xh = 639; + MainHelpText.yh = 199; + MainHelpText.bgcolor = 7; + MainHelpText.ltcolor = 15; + MainHelpText.dkcolor = 8; + +// jabhack(); + + randomize(); + + InitGame (); +// CheckMemory (); + LoadLatchMem (); + +// if (!LoadTextFile("MAINHELP."EXT,&MainHelpText)) +// Quit("Can't load MAINHELP."EXT); + +#ifdef PROFILE + NewGame (); + GameLoop (); +#endif + + DemoLoop(); + Quit(NULL); +} + +//------------------------------------------------------------------------- +// Display640() +//------------------------------------------------------------------------- +void Display640() +{ +// Can you believe it takes all this just to change to 640 mode!!???! +// + VW_ScreenToScreen(0,FREESTART-STATUSLEN,40,80); + VW_SetLineWidth(80); + MoveScreen(0,0); + VW_Bar (0,0,640,200,0); + VW_SetScreenMode(EGA640GR); + VW_SetLineWidth(80); + BlackPalette(); +} + +//------------------------------------------------------------------------- +// Display320() +//------------------------------------------------------------------------- +void Display320() +{ +// Can you believe it takes all this just to change to 320 mode!!???! +// + VW_ColorBorder(0); + VW_FadeOut(); + VW_SetLineWidth(40); + MoveScreen(0,0); + VW_Bar (0,0,320,200,0); + VW_SetScreenMode(EGA320GR); + BlackPalette(); + VW_ScreenToScreen(FREESTART-STATUSLEN,0,40,80); +} + +void PrintHelp(void) +{ + char oldfontcolor = fontcolor; + PrintY = 1; + WindowX = 135; + WindowW = 640; + + VW_FadeOut(); + bufferofs = displayofs = screenloc[0]; + VW_Bar(0,0,320,200,0); + + Display640(); + + VW_Bar(0, 0, 640, 200, 7); + + fontcolor = (7 ^ 1); + US_Print ("\n\n SUMMARY OF GAME CONTROLS\n\n"); + + fontcolor = (7 ^ 4); + US_Print (" ACTION\n\n"); + + US_Print ("Arrow keys, joystick, or mouse\n"); + US_Print ("TAB or V while turning\n"); + US_Print ("ALT or Button 2 while turning\n"); + US_Print ("CTRL or Button 1\n"); + US_Print ("Z\n"); + US_Print ("X or Enter\n"); + US_Print ("F1\n"); + US_Print ("F2\n"); + US_Print ("F3\n"); + US_Print ("F4\n"); + US_Print ("F5\n"); + US_Print ("ESC\n\n"); +#ifndef CATALOG + fontcolor = (7 ^ 0); + US_Print (" (See complete Instructions for more info)\n"); +#endif + + fontcolor = (7 ^ 8); + PrintX = 400; + PrintY = 37; + WindowX = 400; + US_Print (" REACTION\n\n"); + US_Print ("Move and turn\n"); + US_Print ("Turn quickly (Quick Turn)\n"); + US_Print ("Move sideways\n"); + US_Print ("Shoot a Missile\n"); + US_Print ("Shoot a Zapper\n"); + US_Print ("Shoot an Xterminator\n"); + US_Print ("Help (this screen)\n"); + US_Print ("Sound control\n"); + US_Print ("Save game position\n"); + US_Print ("Restore a saved game\n"); + US_Print ("Joystick control\n"); + US_Print ("System options\n\n\n"); + + VW_UpdateScreen(); + VW_FadeIn(); + VW_ColorBorder(8 | 56); + IN_Ack(); + Display320(); + fontcolor = oldfontcolor; +} \ No newline at end of file diff --git a/16/cawat/C5_PLAY.C b/16/cawat/C5_PLAY.C new file mode 100644 index 00000000..96f77757 --- /dev/null +++ b/16/cawat/C5_PLAY.C @@ -0,0 +1,1425 @@ +/* Catacomb Armageddon Source Code + * Copyright (C) 1993-2014 Flat Rock Software + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +// C3_PLAY.C + +#include "DEF.H" +#include "gelib.h" +#pragma hdrstop + +/* +============================================================================= + + LOCAL CONSTANTS + +============================================================================= +*/ + +#define POINTTICS 6 +#define PAUSE 300 + +/* +============================================================================= + + GLOBAL VARIABLES + +============================================================================= +*/ + +byte bcolor; +short skytimer=-1,skytimer_reset; +short groundtimer=-1,groundtimer_reset; + +unsigned scolor,gcolor; +unsigned *skycolor,*groundcolor,debug_sky,debug_gnd; + +unsigned nocolorchange=0xFFFF; +byte BGFLAGS, // global that holds all current flags + bgflag; // used by BG changer, this flag is set when done + + +unsigned sky_daytonight[]={0x0909,0x0101,0x0808,0x0000,0xFFFF}; +//unsigned gnd_daytonight[]={0x0202,0xFFFF}; + +unsigned sky_lightning[]={0x0101,0x0909,0x0f0f,0x0808,0x0000,0xFFFF}; + +unsigned sky_colors[NUMLEVELS]={0x0000,0x0000,0x0000,0x0000,0x0808, + 0x0404,0x0000,0x0000,0x0000,0x0000, + 0x0000,0x0000,0x0000,0x0000,0x0606, + 0x0000,0x0000,0x0000,0x0000,0x0000, + 0x0000}; +unsigned gnd_colors[NUMLEVELS]={0x0202,0x0202,0x0606,0x0202,0x0707, + 0x0505,0x0808,0x0606,0x0101,0x0808, + 0x0606,0x0404,0x0808,0x0c0c,0x0e0e, + 0x0808,0x0808,0x0c0c,0x0000,0x0707, + 0x0808}; + + +ControlInfo control; +boolean running=false; //,slowturn; + +int bordertime; +objtype objlist[MAXACTORS],*new,*obj,*player,*lastobj,*objfreelist; + +#if USE_INERT_LIST +inertobjtype inertobjlist[MAXINERTOBJ],*inert; +#endif + +unsigned farmapylookup[MAPSIZE]; +byte *nearmapylookup[MAPSIZE]; + +boolean singlestep,godmode; +int extravbls; +status_flags status_flag; +int status_delay; + +// +// replacing refresh manager +// +unsigned mapwidth,mapheight,tics,realtics; +boolean compatability; +byte *updateptr; +unsigned mapwidthtable[64]; +unsigned uwidthtable[UPDATEHIGH]; +unsigned blockstarts[UPDATEWIDE*UPDATEHIGH]; +#define UPDATESCREENSIZE (UPDATEWIDE*PORTTILESHIGH+2) +#define UPDATESPARESIZE (UPDATEWIDE*2+4) +#define UPDATESIZE (UPDATESCREENSIZE+2*UPDATESPARESIZE) +byte update[UPDATESIZE]; + +int mousexmove,mouseymove; +int pointcount,pointsleft; + +short BeepTime = 0; + +/* +============================================================================= + + LOCAL VARIABLES + +============================================================================= +*/ + +void CalcBounds (objtype *ob); +void DrawPlayScreen (void); +void PreFullDisplay(void); +void PostFullDisplay(boolean draw_view); + + +// +// near data map array (wall values only, get text number from far data) +// +byte tilemap[MAPSIZE][MAPSIZE]; +byte spotvis[MAPSIZE][MAPSIZE]; +objtype *actorat[MAPSIZE][MAPSIZE]; + +objtype dummyobj; + +int bordertime; +int objectcount; + +void StopMusic(void); +void StartMusic(void); + +void CalibrateJoystick(short joynum); + +//========================================================================== + +/////////////////////////////////////////////////////////////////////////// +// +// CenterWindow() - Generates a window of a given width & height in the +// middle of the screen +// +/////////////////////////////////////////////////////////////////////////// + +#define MAXX 320 +#define MAXY 120 + +void CenterWindow(word w,word h) +{ + US_DrawWindow(((MAXX / 8) - w) / 2,((MAXY / 8) - h) / 2,w,h); +} + +//=========================================================================== + + +/* +===================== += += CheckKeys += +===================== +*/ + +void CheckKeys (void) +{ + extern boolean autofire; + + if (screenfaded) // don't do anything with a faded screen + return; + +#if 0 +// +// pause key wierdness can't be checked as a scan code +// + if (Paused) + { + CenterWindow (8,3); + US_PrintCentered ("PAUSED"); + VW_UpdateScreen (); +// SD_MusicOff(); + IN_Ack(); +// SD_MusicOn(); + Paused = false; + if (MousePresent) Mouse(MDelta); // Clear accumulated mouse movement + } + else + if (Keyboard[sc_Enter]) // P = pause with no screen disruptioon + { +// SD_MusicOff(); + DisplaySMsg("PAUSED",NULL); + IN_Ack(); +// SD_MusicOn(); + if (MousePresent) Mouse(MDelta); // Clear accumulated mouse movement + } + else + if (Keyboard[sc_S]) + { + char *Text[] = {{"Slow Mode ON"},{"Slow Mode OFF"}}; + + SlowMode ^= 1; + extravbls = SlowMode << 3; + CenterWindow (8,3); + US_PrintCentered (Text[SlowMode]); + VW_UpdateScreen (); +// SD_MusicOff(); + IN_Ack(); +// SD_MusicOn(); + if (MousePresent) Mouse(MDelta); // Clear accumulated mouse movement + } +#endif + + +// F2 - SOUND OPTIONS +// + if (Keyboard[sc_F2]) + { + int height=7; + boolean ChoiceMade = false; + + if (AdLibPresent) + height++; + + VW_FixRefreshBuffer(); + CenterWindow(22,height); + US_Print( "\n 1 ) NO SOUND \n"); + US_Print( " 2 ) PC AUDIO \n"); + + if (AdLibPresent) + US_Print(" 3 ) ADLIB AUDIO\n"); + + US_Print( "\n ESC) EXIT "); + VW_UpdateScreen(); + + // Switch audio device ON/OFF & load sounds if there + // was a change in the device. + + do { + + if (Keyboard[1]) // ESC - Exit + ChoiceMade = true; + else + if (Keyboard[2]) // 1 - No Sound + { + SD_SetSoundMode(sdm_Off); + ChoiceMade = true; + } + else + if (Keyboard[3]) // 2 - PC Audio + { + SD_SetSoundMode(sdm_PC); +// if (oldsoundmode != sdm_PC) + CA_LoadAllSounds(); + ChoiceMade = true; + } + else + if ((Keyboard[4]) && AdLibPresent) // 3 - AdLib Audio + { + SD_SetSoundMode(sdm_AdLib); +// if (oldsoundmode != sdm_AdLib) + CA_LoadAllSounds(); + ChoiceMade = true; + } + + } while (!ChoiceMade); + tics = realtics = 1; + IN_ClearKeysDown(); + } + +// F5 - CALIBRATE JOYSTICK +// + if (Keyboard[sc_F5]) + { + CalibrateJoystick(0); + tics = realtics = 1; + IN_ClearKeysDown(); + } + +deadloop:; +// ESCAPE - quits game +// + if ((Keyboard[sc_Escape]) || (Flags & FL_DEAD)) + { + char ch; + + DisplaySMsg("Options", NULL); + status_flag = S_NONE; + + + if (Flags & FL_DEAD) + { + char choices[] = {sc_Escape,sc_R,sc_N,sc_Q,0}; + ch = DisplayMsg("Restore New Quit",choices); + } + else + { + char choices[] = {sc_Escape,sc_S,sc_R,sc_N,sc_Q,0}; + ch = DisplayMsg("Save Restore New Quit",choices); + } + DrawText(true); + + switch (ch) + { + case sc_S: + if (!(Flags & FL_DEAD)) + Keyboard[sc_F3] = true; + break; + + case sc_R: + Keyboard[sc_F4] = true; + break; + + case sc_N: + DisplaySMsg("Starting anew", NULL); + VW_WaitVBL(60); + playstate = ex_resetgame; + Flags &= ~FL_DEAD; + break; + + case sc_Q: + DisplaySMsg("FARE THEE WELL!", NULL); + VW_WaitVBL(120); + if (!Flags & FL_QUICK) + VW_FadeOut(); + NormalScreen(); + FreeUpMemory(); + Quit(NULL); + break; + } + tics = realtics = 1; + } + +// F1 - DISPLAY HELP +// + if (Keyboard[sc_F1]) + { + PrintHelp(); + +#ifdef TEXT_PRESENTER + + extern PresenterInfo MainHelpText; + + VW_FadeOut(); + + FreeUpMemory(); + if (!LoadPresenterScript("HELP.TXT",&MainHelpText)) + { + VW_FadeIn(); + CenterWindow(30,5); + US_CPrint("\nError loading HELP file.\n"); + US_CPrint("Press any key."); + IN_Ack(); + VW_FadeOut(); + } + else + { + VW_SetSplitScreen(200); + bufferofs = displayofs = screenloc[0]; + VW_Bar(0,0,320,200,0); + + Display640(); + Presenter(&MainHelpText); + Display320(); + } + FreePresenterScript(&MainHelpText); +#endif + VW_SetSplitScreen(120); + VW_SetScreen(screenloc[0],0); + screenpage = 0; + CacheScaleds(); + + bufferofs = 0; + RedrawStatusWindow(); + ThreeDRefresh(); + VW_FadeIn(); + Keyboard[sc_F1] = false; + tics = realtics = 1; + IN_ClearKeysDown(); + } + +// F3 - SAVE GAME +// + if ((Keyboard[sc_F3]) && (!(Flags & FL_DEAD))) + { + PreFullDisplay(); + GE_SaveGame(); + PostFullDisplay(true); + tics = realtics = 1; + IN_ClearKeysDown(); + } + +// F4 - LOAD GAME +// + if (Keyboard[sc_F4]) + { + PreFullDisplay(); + if (GE_LoadGame()) + { + loadedgame = true; + playstate = ex_loadedgame; + Flags &= ~FL_DEAD; + lasttext = -1; + PostFullDisplay(false); + } + else + if (playstate == ex_victorious) + { + PostFullDisplay(false); + Victory(false); + IN_Ack(); +// gamestate.mapon++; + } + else + PostFullDisplay(true); + Keyboard[sc_F5] = false; + tics = realtics = 1; + IN_ClearKeysDown(); + } + + if (Flags & FL_DEAD) + goto deadloop; + +// +// F10-? debug keys +// + if (Keyboard[sc_BackSpace]) + { + DebugKeys(); + if (MousePresent) Mouse(MDelta); // Clear accumulated mouse movement + lasttimecount = TimeCount; + } +} + +//------------------------------------------------------------------------- +// PreFullDisplay() +//------------------------------------------------------------------------- +void PreFullDisplay() +{ + VW_FadeOut(); + VW_SetSplitScreen(200); + bufferofs = displayofs = screenloc[0]; + VW_Bar(0,0,320,200,0); +} + +//------------------------------------------------------------------------- +// PostFullDisplay() +//------------------------------------------------------------------------- +void PostFullDisplay(boolean draw_view) +{ + VW_SetSplitScreen(120); + bufferofs = 0; + RedrawStatusWindow(); + if (draw_view) + { + ThreeDRefresh(); + VW_FadeIn(); + } +} + + +//=========================================================================== + +/* +############################################################################# + + The objlist data structure + +############################################################################# + +objlist containt structures for every actor currently playing. The structure +is accessed as a linked list starting at *player, ending when ob->next == +NULL. GetNewObj inserts a new object at the end of the list, meaning that +if an actor spawn another actor, the new one WILL get to think and react the +same frame. RemoveObj unlinks the given object and returns it to the free +list, but does not damage the objects ->next pointer, so if the current object +removes itself, a linked list following loop can still safely get to the +next element. + + + +############################################################################# +*/ + + +/* +========================= += += InitObjList += += Call to clear out the entire object list, returning them all to the free += list. Allocates a special spot for the player. += +========================= +*/ + +void InitObjList (void) +{ + int i; + + for (i=0;iprev; + memset (new,0,sizeof(*new)); + + if (lastobj) + lastobj->next = new; + new->prev = lastobj; // new->next is allready NULL from memset + + new->active = false; + lastobj = new; + + objectcount++; +} + +//=========================================================================== + +/* +========================= += += RemoveObj += += Add the given object back into the free list, and unlink it from it's += neighbors += +========================= +*/ + +void RemoveObj (objtype *gone) +{ + objtype **spotat; + + if (gone == player) + Quit ("RemoveObj: Tried to remove the player!"); + +// +// fix the next object's back link +// + if (gone == lastobj) + lastobj = (objtype *)gone->prev; + else + gone->next->prev = gone->prev; + +// +// fix the previous object's forward link +// + gone->prev->next = gone->next; + +// +// add it back in to the free list +// + gone->prev = objfreelist; + objfreelist = gone; + + objectcount--; +} + +#if USE_INERT_LIST + +//-------------------------------------------------------------------------- +// MoveObjToInert() +//-------------------------------------------------------------------------- +void MoveObjToInert(objtype *obj) +{ + + if (inert == &inertobjlist[MAXINERTOBJ]) + return; + +// Transfer info needed by inert objtype +// + inert->x = obj->x; + inert->y = obj->y; + inert->size = obj->size; + inert->viewx = obj->viewx; + inert->tilex = obj->tilex; + inert->tiley = obj->tiley; + inert->state = obj->state; + inert->ticcount = obj->ticcount; + +// Setup links between inert objects +// + if (inert != inertobjlist) + (inert-1)->next = inert; + inert->next = NULL; + inert++; + +// Free 'real' object from list. +// + RemoveObj(obj); +} + +#endif + +//========================================================================== + +/* +=================== += += PollControls += +=================== +*/ + +void PollControls (void) +{ + unsigned buttons; + + IN_ReadControl(0,&control); + + if (MousePresent) + { + Mouse(MButtons); + buttons = _BX; + Mouse(MDelta); + mousexmove = _CX; + mouseymove = _DX; + + if (buttons&1) + control.button0 = 1; + if (buttons&2) + control.button1 = 1; + + } + + if (Keyboard[sc_V] || Keyboard[sc_Tab]) + running = true; + else + running = false; +} + +//========================================================================== + +#if 0 +/* +================= += += StopMusic += +================= +*/ + +void StopMusic(void) +{ + int i; + + SD_MusicOff(); + for (i = 0;i < LASTMUSIC;i++) + if (audiosegs[STARTMUSIC + i]) + { + MM_SetPurge(&((memptr)audiosegs[STARTMUSIC + i]),3); + MM_SetLock(&((memptr)audiosegs[STARTMUSIC + i]),false); + } +} + +//========================================================================== + + +/* +================= += += StartMusic += +================= +*/ + +// JAB - Cache & start the appropriate music for this level +void StartMusic(void) +{ + musicnames chunk; + + SD_MusicOff(); + chunk = TOOHOT_MUS; +// if ((chunk == -1) || (MusicMode != smm_AdLib)) +//DEBUG control panel return; + + MM_BombOnError (false); + CA_CacheAudioChunk(STARTMUSIC + chunk); + MM_BombOnError (true); + if (mmerror) + mmerror = false; + else + { + MM_SetLock(&((memptr)audiosegs[STARTMUSIC + chunk]),true); + SD_StartMusic((MusicGroup far *)audiosegs[STARTMUSIC + chunk]); + } +} +#endif + +//========================================================================== + + +/* +=================== += += PlayLoop += +=================== +*/ + +void PlayLoop (void) +{ + char shot_color[3] = {4,9,14}; + + int allgems[5]={GEM_DELAY_TIME, // used for Q & D comparison + GEM_DELAY_TIME, // for having all gems... + GEM_DELAY_TIME, // the "allgems" declaration MUST + GEM_DELAY_TIME, // match the "gems" declaration in + GEM_DELAY_TIME // the gametype structure! + }; + +// int originx=0; +// int i=100; + signed long dx,dy,radius,psin,pcos,newx,newy; + int give; + short objnum; + signed long ox,oy,xl,xh,yl,yh,px,py,norm_dx,norm_dy; + short o_radius; + + void (*think)(); + + ingame = true; + playstate = TimeCount = 0; + gamestate.shotpower = handheight = 0; + pointcount = pointsleft = 0; + + status_flag = S_NONE; + +#if 0 + // setup sky/ground colors and effects (based on level) + // + switch (gamestate.mapon) + { + case 255: + if (!(BGFLAGS & BGF_NIGHT)) + { + InitBgChange(3*60,sky_daytonight,-1,NULL,BGF_NIGHT); + groundcolor = &gnd_colors[0]; + } + else + { + skycolor = &sky_colors[0]; + groundcolor = &gnd_colors[0]; + } + break; + + default: + skycolor = &sky_colors[gamestate.mapon]; + groundcolor = &gnd_colors[gamestate.mapon]; + skytimer = groundtimer = -1; + break; + } +#endif + + BGFLAGS |= BGF_NOT_LIGHTNING; + skytimer = groundtimer = -1; + + debug_gnd = *groundcolor; + debug_sky = *skycolor; + RedrawStatusWindow(); + ThreeDRefresh(); + if (screenfaded) + VW_FadeIn(); + +#ifndef PROFILE + fizzlein = true; // fizzle fade in the first refresh +#endif + TimeCount = lasttimecount = lastnuke = 0; + + PollControls (); // center mouse +// StartMusic (); + do + { +#ifndef PROFILE + PollControls(); +#else + control.xaxis = 1; + if (++TimeCount == 300) + return; +#endif + DisplayStatus(&status_flag); + + objnum=0; + for (obj = player;obj;obj = obj->next) + { + if ((obj->active >= yes) && (!(FreezeTime && (obj!=player)))) + { + if (obj->ticcount) + { + obj->ticcount-=realtics; + while ( obj->ticcount <= 0) + { + think = obj->state->think; + if (think) + { + statetype *oldstate=obj->state; + + think (obj); + if (!obj->state) + { + RemoveObj (obj); + goto nextactor; + } + if (obj->state != oldstate) + break; + } + + obj->state = obj->state->next; + if (!obj->state) + { + RemoveObj (obj); + goto nextactor; + } + if (!obj->state->tictime) + { + obj->ticcount = 0; + goto nextactor; + } + if (obj->state->tictime>0) + obj->ticcount += obj->state->tictime; + } + } + + think = obj->state->think; + if (think) + { + think (obj); + if (!obj->state) + RemoveObj (obj); + } +nextactor:; + } + + // keep a list of objects around the player for radar updates + // + if (obj == player) + { + px = player->x; + py = player->y; + psin = sintable[player->angle]; + pcos = costable[player->angle]; + xl = px-((long)RADAR_WIDTH< MAX_RADAR_BLIPS-2) + objnum = MAX_RADAR_BLIPS-2; + + ox = obj->x; + oy = obj->y; + + + if ((ox >= xl) && (ox <= xh) && (oy >= yl) && (oy <= yh)) + { + norm_dx = (dx = px-ox)>>TILESHIFT; + norm_dy = (dy = oy-py)>>TILESHIFT; + + o_radius = IntSqrt((norm_dx * norm_dx) + (norm_dy * norm_dy)); + + if (o_radius < RADAR_RADIUS) + { + newx = FixedByFrac(dy,pcos)-FixedByFrac(dx,psin); + newy = FixedByFrac(dy,psin)+FixedByFrac(dx,pcos); + + RadarXY[objnum][0]=newx>>TILESHIFT; + RadarXY[objnum][1]=newy>>TILESHIFT; + + // Define color to use for this object... + // + + switch (obj->obclass) + { + // NO GEM NEEDED + // + // THE WIZARD! (YOU) + // + case playerobj: + RadarXY[objnum++][2]=15; + break; + + // WIZARD'S SHOTS + // + case pshotobj: + case bigpshotobj: + RadarXY[objnum++][2]=shot_color[screenpage]; + break; + + // BATS (DK GRAY) + // + case batobj: + if (obj->active == always) + RadarXY[objnum++][2]=8; + break; + + // RABBITS (LT GRAY) + // + case bunnyobj: + if (obj->active == always) + RadarXY[objnum++][2]=7; + break; + + // RED GEM + // + // EYE, RED DEMON (DK RED) + // + case eyeobj: + case reddemonobj: + if (gamestate.gems[B_RGEM-B_RGEM]) + if (obj->active == always) + RadarXY[objnum++][2]=4; + break; + + // RED MAGE (LT RED) + // + case mageobj: + if (gamestate.gems[B_RGEM-B_RGEM]) + if (obj->active == always) + RadarXY[objnum++][2]=12; + break; + + // BLUE GEM + // + // SUCCUBUS (LT BLUE) + // + case succubusobj: + if (gamestate.gems[B_BGEM-B_RGEM]) + if (obj->active == always) + RadarXY[objnum++][2]=9; + break; + + // WATER DRAGON (DK BLUE) + // + case wetobj: + if (gamestate.gems[B_GGEM-B_RGEM]) + if (obj->active == always) + RadarXY[objnum++][2]=1; + break; + + + + // GREEN GEM + // + // GREEN TROLL (LT GREEN) + // + case fatdemonobj: + if (gamestate.gems[B_GGEM-B_RGEM]) + if (obj->active == always) + RadarXY[objnum++][2]=10; + break; + + // GODESS (DK GREEN) + // + case godessobj: + if (gamestate.gems[B_GGEM-B_RGEM]) + if (obj->active == always) + RadarXY[objnum++][2]=2; + break; + + // YELLOW GEM + // + // ANT (BROWN) + // + case antobj: + case treeobj: + if (gamestate.gems[B_YGEM-B_RGEM]) + if (obj->active == always) + RadarXY[objnum++][2]=6; + break; + + // SKELETON (YELLOW) + // + case skeletonobj: + if (gamestate.gems[B_YGEM-B_RGEM]) + if (obj->active == always) + RadarXY[objnum++][2]=14; + break; + + // PURPLE GEM + // + // ZOMBIE + // + case zombieobj: + if (gamestate.gems[B_PGEM-B_RGEM]) + if (obj->active == always) + RadarXY[objnum++][2]=13; + break; + + // ALL GEMS NEEDED + // + // NEMESIS + // + case grelmobj: + if (!memcmp(gamestate.gems,allgems,sizeof(gamestate.gems))) + if (obj->active == always) + RadarXY[objnum++][2]=15; + break; + } + } + } + } + RadarXY[objnum][2]=-1; // Signals end of RadarXY list... + +#if USE_INERT_LIST + if (inert != inertobjlist) + for (obj=(objtype *)inertobjlist;obj;obj=obj->next) + if (obj->ticcount) + { + obj->ticcount-=realtics; + while ( obj->ticcount <= 0) + { + obj->state = obj->state->next; + if (!obj->state) + Quit("Removable object in INERT list."); + + if (!obj->state->tictime) + { + obj->ticcount = 0; + goto nextactor; + } + + if (obj->state->tictime>0) + obj->ticcount += obj->state->tictime; + } + } +#endif + + if (bordertime) + { + bordertime -= realtics; + if (bordertime<=0) + { + bordertime = 0; + VW_ColorBorder(0); + } + } + +#if 1 +// random lightning? +// + if (BGFLAGS & (BGF_NOT_LIGHTNING)) + { + if ((scolor & 0xe0) && (!(random(20-realtics)))) + { + BGFLAGS &= ~BGF_NOT_LIGHTNING; + InitBgChange(1,sky_lightning,-1,NULL,BGF_NOT_LIGHTNING); + } + } + +// handle sky/ground color changes +// + if (skytimer != -1) + { + skytimer -= realtics; + if (skytimer < 0) + { + skycolor++; + if (*skycolor == 0xffff) + { + skytimer = -1; +// skycolor--; + skycolor = &scolor; + if (groundtimer == -1) + BGFLAGS |= bgflag; + } + else + skytimer = skytimer_reset; + } + } + + if (groundtimer != -1) + { + groundtimer -= realtics; + if (groundtimer < 0) + { + groundcolor++; + if (*groundcolor == 0xffff) + { + groundtimer = -1; +// groundcolor--; + groundcolor = &gcolor; + if (skytimer == -1) + BGFLAGS |= bgflag; + } + else + groundtimer = groundtimer_reset; + } + } +#endif + + +// +// Handle FreezeTime counter.. +// + if (FreezeTime) + { + if (FreezeTime<20*30) + if ((BeepTime+=realtics)>=60) + { + BeepTime -= 60; + SD_PlaySound(TICKSND); + } + + if ((FreezeTime-=realtics)<=0) + { + FreezeTime=0; + SD_PlaySound(TIMERETURNSND); + DisplaySMsg(NULL,NULL); + status_flag = S_NONE; + } + } + + +// refresh all +// + ThreeDRefresh (); + + if (Flags & FL_DEAD) + { + SD_PlaySound (GAMEOVERSND); + DisplaySMsg("DEAD",NULL); + DrawHealth(); + if (gamestate.potions) + { + bufferofs = displayofs = screenloc[screenpage]; + CenterWindow(35,3); + US_CPrint("\nYou should use your Cure Potions wisely\n"); + IN_Ack(); + } + } + +// check for win +// + if (playstate == ex_victorious) + { + Victory(true); +// Flags |= FL_DEAD; + IN_Ack(); +// gamestate.mapon++; + } + + CheckKeys(); + + }while (!playstate); +// StopMusic (); + + ingame = false; + if (bordertime) + { + bordertime = 0; + VW_ColorBorder(0); + } + + if (abortgame) + abortgame = false; +} + +//-------------------------------------------------------------------------- +// IntSqrt() - by Master Programmer, George Leritte! +//-------------------------------------------------------------------------- +int IntSqrt(long va) +{ +asm mov AX, word ptr va +asm mov DX, word ptr va+2 +asm mov bx,dx // {bx = integer square root of dx:ax} +asm or bx,ax // {if dx:ax=0 then return} +asm jz isq01 +asm mov bx,dx +asm shl bx,1 +asm or bl,ah +asm or bl,al +asm dec bx +asm add bx,dx // { initial guess} +asm jg isq10 +asm inc bx // { don't return zero} +asm jg isq10 +asm mov bx,7fffh +isq01:; + goto exitrout; + +isq10:; +asm push ax +asm push dx +asm div bx +asm sub ax,bx +asm cmp ax,1 +asm jbe isq90 +asm cmp ax,-1 +asm jae isq90 +asm sar ax,1 +asm add bx,ax +asm pop dx +asm pop ax +asm jmp isq10 +isq90:; +asm pop dx +asm pop ax +exitrout:; +asm mov ax,bx +} + +//------------------------------------------------------------------------- +// InitBgChange() +//------------------------------------------------------------------------- +void InitBgChange(short stimer, unsigned *scolors, short gtimer, unsigned *gcolors, byte flag) +{ + skytimer_reset = skytimer = stimer; + if (scolors) + skycolor = scolors; + + groundtimer_reset = groundtimer = gtimer; + if (gcolors) + groundcolor = gcolors; + + bgflag = flag; +} + +//////////////////////////////////////////////////////// +// +// DisplayStatus +// +// Stat_Flag - contains the type of status displayed +// -- also uses status_delay (global variable) will not +// change display until this variable is zero. +// -- heirarchy is determined by the series of if statements, +// to change it, rearrange th if statements. +// +//////////////////////////////////////////////////////// + +#define MESSAGEDELAY 25 +void DisplayStatus (status_flags *stat_flag) +{ + status_flags temp_status; + + + if (*stat_flag == S_TIMESTOP) + return; + + if (status_delay > 0) + { + status_delay -= realtics; + return; + } + else + status_delay = 0; + + // check for a change in status from previous call + + temp_status = S_VIEWING; //precaution + + if (Keyboard[sc_Control] || control.button0) + temp_status = S_MISSLE; + + if (Keyboard[sc_Z] && !Keyboard[sc_F10]) + temp_status = S_ZAPPER; + + if ((Keyboard[sc_X] && !Keyboard[sc_F10]) || Keyboard[sc_Enter]) + temp_status = S_XTER; + + if (control.x) + temp_status = S_TURN; + + if ((Keyboard[sc_V] || Keyboard[sc_Tab]) && control.x) + temp_status = S_QTURN; + + if (Keyboard[sc_Alt] && control.x) + temp_status = S_SIDESTEP; + + if (control.y < 0) + temp_status = S_ADVANCE; + + if (control.y > 0) + temp_status = S_RETREAT; + + if (Keyboard[sc_F5]) + temp_status = S_JOYSTICK; + + if (Keyboard[sc_F4]) + temp_status = S_RESTORING; + + if (Keyboard[sc_F3]) + temp_status = S_SAVING; + + if (Keyboard[sc_F2]) + temp_status = S_SND; + + if (Keyboard[sc_F1]) + temp_status = S_HELP; + + if (temp_status != *stat_flag) + { + *stat_flag = temp_status; + + + switch (*stat_flag) + { + case S_MISSLE: + DisplaySMsg("Magick Missile", NULL); + status_delay = MESSAGEDELAY; + break; + + case S_ZAPPER: + if (gamestate.bolts) + { + DisplaySMsg("Zapper", NULL); + status_delay = MESSAGEDELAY+10; + } + break; + + case S_XTER: + if (gamestate.nukes) + { + DisplaySMsg("Xterminator", NULL); + status_delay = MESSAGEDELAY+5; + } + break; + + case S_TURN: + DisplaySMsg("Turning", NULL); + status_delay = MESSAGEDELAY; + break; + + case S_QTURN: + DisplaySMsg("Quick Turning", NULL); + status_delay = MESSAGEDELAY; + break; + + case S_SIDESTEP: + DisplaySMsg("Sidestepping", NULL); + status_delay = MESSAGEDELAY; + break; + + case S_ADVANCE: + DisplaySMsg("Advancing", NULL); + status_delay = MESSAGEDELAY; + break; + + case S_RETREAT: + DisplaySMsg("Retreating", NULL); + status_delay = MESSAGEDELAY; + break; + + case S_JOYSTICK: + DisplaySMsg("Adjusting Joystick", NULL); + break; + + case S_RESTORING: + DisplaySMsg("Restoring", NULL); + break; + + case S_SAVING: + DisplaySMsg("Saving", NULL); + break; + + case S_SND: + DisplaySMsg("Select Sound", NULL); + break; + + case S_HELP: + DisplaySMsg("Getting Help", NULL); + break; + + case S_VIEWING: + DisplaySMsg("Viewing", NULL); + break; + } + bufferofs = displayofs = screenloc[screenpage]; + + } +} diff --git a/16/cawat/C5_SCALE.C b/16/cawat/C5_SCALE.C new file mode 100644 index 00000000..fee4dbed --- /dev/null +++ b/16/cawat/C5_SCALE.C @@ -0,0 +1,697 @@ +/* Catacomb Armageddon Source Code + * Copyright (C) 1993-2014 Flat Rock Software + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +// C3_SCALE.C + +#include "DEF.H" +#pragma hdrstop + +//const unsigned viewheight = 144; +const unsigned screenbwide = 40; +const byte BACKGROUNDPIX = 5; + +unsigned shapesize[NUMSCALEPICS]; +t_compscale _seg *scaledirectory[NUMSCALEPICS]; +t_compshape _seg *shapedirectory[NUMSCALEPICS]; +memptr walldirectory[NUMSCALEWALLS]; + +/* +=========================== += += DeplanePic += += Takes a raw bit map of width bytes by height and creates a scaleable shape += += Returns the length of the shape in bytes += += Fills in spotvis (a convenient 64*64 array) with the color values += +=========================== +*/ + +void DeplanePic (int picnum) +{ + byte far *plane0,far *plane1,far *plane2,far *plane3; + byte by0,by1,by2,by3; + unsigned x,y,b,color,shift,width,height; + byte *dest; + +// +// convert ega pixels to byte color values in a temp buffer +// + width = pictable[picnum-STARTPICS].width; + height = pictable[picnum-STARTPICS].height; + + if (width>8 || height!=64) + Quit ("DePlanePic: Bad size shape"); + + memset (spotvis,BACKGROUNDPIX,sizeof(spotvis)); + + plane0 = (byte _seg *)grsegs[picnum]; + plane1 = plane0 + width*height; + plane2 = plane1 + width*height; + plane3 = plane2 + width*height; + + for (y=0;ycode[0]; + toppix = (viewheight-height)/2; + fix = 0; + + for (src=0;src<=64;src++) + { + startpix = fix>>16; + fix += step; + endpix = fix>>16; + + work->start[src] = startpix; + if (endpix>startpix) + work->width[src] = endpix-startpix; + else + work->width[src] = 0; + +// +// mark the start of the code +// + work->codeofs[src] = FP_OFF(code); + +// +// compile some code if the source pixel generates any screen pixels +// + startpix+=toppix; + endpix+=toppix; + + if (startpix == endpix || endpix < 0 || startpix >= VIEWHEIGHT || src == 64) + continue; + + // + // mov al,[si+src] + // + *code++ = 0x8a; + *code++ = 0x44; + *code++ = src; + + for (;startpix= VIEWHEIGHT) + break; // off the bottom of the view area + if (startpix < 0) + continue; // not into the view area + + // + // and [es:di+heightofs],al + // + *code++ = 0x26; + *code++ = 0x20; + *code++ = 0x85; + *((unsigned far *)code)++ = startpix*screenbwide; + } + + } + +// +// retf +// + *code++ = 0xcb; + + totalsize = FP_OFF(code); + MM_GetPtr (finalspot,totalsize); + _fmemcpy ((byte _seg *)(*finalspot),(byte _seg *)work,totalsize); + MM_FreePtr (&(memptr)work); + + return totalsize; +} + + + + +/* +======================== += += BuildCompShape += += typedef struct += { += unsigned width; += unsigned codeofs[64]; += } t_compshape; += += Width is the number of compiled line draws in the shape. The shape += drawing code will assume that the midpoint of the shape is in the += middle of the width. += += The non background pixel data will start at codeofs[width], so codeofs += greater than width will be invalid. += += Each code offset will draw one vertical line of the shape, consisting += of 0 or more segments of scaled pixels. += += The scaled shapes use ss:0-4 as a scratch variable for the far call to += the compiled scaler, so zero it back out after the shape is scaled, or += a "null pointer assignment" will result upon termination. += += Setup for a call to a compiled shape += ----------------------------------- += ax toast += bx toast += cx toast += dx segment of compiled shape += si toast += di byte at top of view area to draw the line in += bp 0 += ss:2 and ds the segment of the compiled scaler to use += es screenseg += += Upon return, ds IS NOT SET to the data segment. Do: += mov ax,ss += mov ds,ax += += += GC_BITMASK set to the pixels to be drawn in the row of bytes under DI += GC_MODE read mode 1, write mode 2 += GC_COLORDONTCARE set to 0, so all reads from video memory return 0xff += += += Code generated for each segment += ------------------------------- += mov bx,[(segend+1)*2] += mov cx,[bx] += mov [BYTE PTR bx],0xc8 // far return += mov ax,[segstart*2] += mov [ss:0],ax // entry point into the compiled scaler += mov ds,dx // (mov ds,cs) the data is after the compiled code += mov si,ofs data += call [bp] // scale some pixels += mov ds,[bp+2] += mov [bx],cx // un patch return += += Code generated after all segments on a line += ------------------------------------------- += retf += +======================== +*/ + +unsigned BuildCompShape (t_compshape _seg **finalspot) +{ + t_compshape _seg *work; + byte far *code; + int firstline,lastline,x,y; + unsigned firstpix,lastpix,width; + unsigned totalsize,pixelofs; + unsigned buff; + + +// MM_GetPtr (&(memptr)work,20000); + EGAWRITEMODE(0); + EGAREADMAP(0); // use ega screen memory for temp buffer + EGAMAPMASK(1); + buff = screenloc[1]; + work = (t_compshape _seg *)(0xa000+(buff+15)/16); + +// +// find the width of the shape +// + firstline = -1; + x=0; + do + { + for (y=0;y<64;y++) + if (spotvis[y][x] != BACKGROUNDPIX) + { + firstline = x; + break; + } + if (++x == 64) + Quit ("BuildCompShape: No shape data!"); + } while (firstline == -1); + + lastline = -1; + x=63; + do + { + for (y=0;y<64;y++) + if (spotvis[y][x] != BACKGROUNDPIX) + { + lastline = x; + break; + } + x--; + } while (lastline == -1); + + width = lastline-firstline+1; + + work->width = width; + code = (byte far *)&work->codeofs[width]; + +// +// copy all non background pixels to the work space +// + pixelofs = FP_OFF(code); + + for (x=firstline;x<=lastline;x++) + for (y=0;y<64;y++) + if (spotvis[y][x] != BACKGROUNDPIX) + *code++ = spotvis[y][x]; + +// +// start compiling the vertical lines +// + for (x=firstline;x<=lastline;x++) + { + work->codeofs[x-firstline] = FP_OFF(code); + + y=0; + do + { + // + // scan past black background pixels + // + while (spotvis[y][x] == BACKGROUNDPIX && y<64) + y++; + + if (y>63) // no more segments + break; + + firstpix = y+1; // +1 because width is before codeofs + + // + // scan past scalable pixels + // + while (spotvis[y][x] != BACKGROUNDPIX && y<64) + y++; + + if (y>63) + lastpix = 65; + else + lastpix = y+1; // actually one pixel past the last displayed + + // + // compile the scale call + // + *code++ = 0x8b; // mov bx,[lastpix*2] + *code++ = 0x1e; + *((unsigned far *)code)++ = lastpix*2; + + *code++ = 0x8b; // mov cx,[bx] + *code++ = 0x0f; + + *code++ = 0xc6; // move [BYTE bx],0xcb + *code++ = 0x07; + *code++ = 0xcb; + + *code++ = 0xa1; // mov ax,[firstpix*2] /************* + *((unsigned far *)code)++ = firstpix*2; + + *code++ = 0x36; // mov [ss:0],ax + *code++ = 0xa3; + *code++ = 0x00; + *code++ = 0x00; + + *code++ = 0x8e; // mov ds,dx (mov ds,cs) + *code++ = 0xda; + + *code++ = 0xbe; // mov si,OFFSET pixelofs-firstpixel + *((unsigned far *)code)++ = pixelofs-firstpix; + + *code++ = 0xff; // call [DWORD bp] + *code++ = 0x5e; + *code++ = 0x00; + + *code++ = 0x8e; // mov ds,[bp+2] + *code++ = 0x5e; + *code++ = 0x02; + + *code++ = 0x89; // mov [bx],cx + *code++ = 0x0f; + + pixelofs += (lastpix-firstpix); + } while (y<63); + + // + // retf + // + *code++ = 0xcb; + } + + +// +// copy the final shape to a properly sized buffer +// + totalsize = FP_OFF(code); + + if (totalsize >= (PAGELEN*2)) + Quit("BuildCompShape(): Shape is too complex!"); + + MM_GetPtr ((memptr *)finalspot,totalsize); + _fmemcpy ((byte _seg *)(*finalspot),(byte _seg *)work,totalsize); +// MM_FreePtr (&(memptr)work); + + return totalsize; +} + + + +/* +======================= += += ScaleShape += += Draws a compiled shape at [scale] pixels high += += Setup for call += -------------- += GC_MODE read mode 1, write mode 2 += GC_COLORDONTCARE set to 0, so all reads from video memory return 0xff += GC_INDEX pointing at GC_BITMASK += +======================= +*/ + +static long longtemp; + +void ScaleShape (int xcenter, t_compshape _seg *compshape, unsigned scale) +{ + #define MAX_OBJ_SCALE (MAXSCALE) + + + t_compscale _seg *comptable; + unsigned width,scalewidth; + int x,pixel,lastpixel,pixwidth,min; + unsigned far *codehandle, far *widthptr; + unsigned badcodeptr; + int rightclip; + + if (!compshape) + Quit ("ScaleShape: NULL compshape ptr!"); + + scale = (scale+1)/2; + if (!scale) + return; // too far away + if (scale>MAX_OBJ_SCALE) + scale = MAX_OBJ_SCALE; + comptable = scaledirectory[scale]; + + width = compshape->width; + scalewidth = comptable->start[width]; + + pixel = xcenter - scalewidth/2; + lastpixel = pixel+scalewidth-1; + if (pixel >= VIEWWIDTH || lastpixel < 0) + return; // totally off screen + +// +// scan backwards from the right edge until pixels are visable +// rightclip is the first NON VISABLE pixel +// + if (lastpixel>=VIEWWIDTH-1) + rightclip = VIEWWIDTH-1; + else + rightclip = lastpixel; + + if (zbuffer[rightclip]>scale) + { + if (pixel>0) + min = pixel; + else + min = 0; + do + { + if (--rightclip < min) + return; // totally covered or before 0 + if (zbuffer[rightclip]<=scale) + break; + } while (1); + } + rightclip++; + +// +// scan from the left until it is on screen, leaving +// [pixel],[pixwidth],[codehandle],and [widthptr] set correctly +// + *(((unsigned *)&longtemp)+1) = (unsigned)compshape; // seg of shape + codehandle = &compshape->codeofs[0]; + badcodeptr = compshape->codeofs[width]; + widthptr = &comptable->width[0]; + asm mov ax,[comptable] + asm mov WORD PTR [2],ax // ds:0-4 is used as a far call pointer + // by the compiled shapes + pixwidth = *widthptr; // scaled width of this pixel + while (!pixwidth) + { + pixwidth = *++widthptr; // find the first visable pixel + codehandle++; + } + + if (pixel<0) + { + do + { + if (pixel+pixwidth>0) + { + pixwidth += pixel; + pixel = 0; + break; + } + do + { + pixwidth = *++widthptr; + codehandle++; + } while (!pixwidth); + pixel+=pixwidth; + } while (1); + } + +// +// scan until it is visable, leaving +// [pixel],[pixwidth],[codehandle],and [widthptr] set correctly +// + do + { + if (zbuffer[pixel] <= scale) + break; // start drawing here + pixel++; + if (!--pixwidth) + { + do + { + pixwidth = *++widthptr; + codehandle++; + } while (!pixwidth); + } + } while (1); + + if (pixel+pixwidth>rightclip) + pixwidth = rightclip-pixel; +// +// draw lines +// + do // while (1) + { + // + // scale a vertical segment [pixwidth] pixels wide at [pixel] + // + (unsigned)longtemp = *codehandle; // offset of compiled code + if ((unsigned)longtemp == badcodeptr) + Quit ("ScaleShape: codehandle past end!"); + + asm mov bx,[pixel] + asm mov di,bx + asm shr di,1 + asm shr di,1 + asm shr di,1 // X in bytes + asm add di,[bufferofs] + asm and bx,7 + asm shl bx,1 + asm shl bx,1 + asm shl bx,1 + asm add bx,[pixwidth] // bx = pixel*8+pixwidth-1 + asm dec bx + asm push bx + asm mov al,BYTE PTR [bitmasks1+bx] + asm mov dx,GC_INDEX+1 + asm out dx,al // set bit mask register + + asm mov es,[screenseg] + asm push si + asm push di + asm push bp + asm xor bp,bp + asm mov dx,[WORD PTR longtemp+2] + asm mov ds,[2] + asm call ss:[DWORD PTR longtemp] // scale the line of pixels + asm mov ax,ss + asm mov ds,ax + asm pop bp + asm pop di + asm pop si + + asm pop bx + asm mov al,BYTE PTR [bitmasks2+bx] + asm or al,al + asm jz nosecond + + // + // draw a second byte for vertical strips that cross two bytes + // + asm inc di + asm mov dx,GC_INDEX+1 + asm out dx,al // set bit mask register + asm push si + asm push di + asm push bp + asm xor bp,bp + asm mov dx,[WORD PTR longtemp+2] + asm mov ds,[2] + asm call ss:[DWORD PTR longtemp] // scale the line of pixels + asm mov ax,ss + asm mov ds,ax + asm pop bp + asm pop di + asm pop si + + + // + // advance to the next drawn line + // +nosecond:; + if ( (pixel+=pixwidth) == rightclip ) + { + asm mov WORD PTR [0],0 + asm mov WORD PTR [2],0 + return; // all done! + } + + do + { + pixwidth = *++widthptr; + codehandle++; + } while (!pixwidth); + + if (pixel+pixwidth > rightclip) + pixwidth = rightclip-pixel; + + } while (1); + +} + +// +// bit mask tables for drawing scaled strips up to eight pixels wide +// + +byte bitmasks1[8][8] = { +{0x80,0xc0,0xe0,0xf0,0xf8,0xfc,0xfe,0xff}, +{0x40,0x60,0x70,0x78,0x7c,0x7e,0x7f,0x7f}, +{0x20,0x30,0x38,0x3c,0x3e,0x3f,0x3f,0x3f}, +{0x10,0x18,0x1c,0x1e,0x1f,0x1f,0x1f,0x1f}, +{0x8,0xc,0xe,0xf,0xf,0xf,0xf,0xf}, +{0x4,0x6,0x7,0x7,0x7,0x7,0x7,0x7}, +{0x2,0x3,0x3,0x3,0x3,0x3,0x3,0x3}, +{0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1} }; + +byte bitmasks2[8][8] = { +{0,0,0,0,0,0,0,0}, +{0,0,0,0,0,0,0,0x80}, +{0,0,0,0,0,0,0x80,0xc0}, +{0,0,0,0,0,0x80,0xc0,0xe0}, +{0,0,0,0,0x80,0xc0,0xe0,0xf0}, +{0,0,0,0x80,0xc0,0xe0,0xf0,0xf8}, +{0,0,0x80,0xc0,0xe0,0xf0,0xf8,0xfc}, +{0,0x80,0xc0,0xe0,0xf0,0xf8,0xfc,0xfe} }; + + + + + + diff --git a/16/cawat/C5_SCA_A.ASM b/16/cawat/C5_SCA_A.ASM new file mode 100644 index 00000000..44c8eb18 --- /dev/null +++ b/16/cawat/C5_SCA_A.ASM @@ -0,0 +1,153 @@ +; Catacomb Armageddon Source Code +; Copyright (C) 1993-2014 Flat Rock Software +; +; This program is free software; you can redistribute it and/or modify +; it under the terms of the GNU General Public License as published by +; the Free Software Foundation; either version 2 of the License, or +; (at your option) any later version. +; +; This program is distributed in the hope that it will be useful, +; but WITHOUT ANY WARRANTY; without even the implied warranty of +; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +; GNU General Public License for more details. +; +; You should have received a copy of the GNU General Public License along +; with this program; if not, write to the Free Software Foundation, Inc., +; 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +IDEAL +MODEL MEDIUM,C + +include "ID_ASM.EQU" + +;=========================================================================== +; +; SCALING GRAPHICS +; +;=========================================================================== + + + +MACRO MAKELAB NUM + +lab&NUM: + +ENDM + +MACRO MAKEREF NUM + +dw OFFSET lab&NUM + +ENDM + + +;========================================================================= + +MAXSCALES equ 256 + + DATASEG + +EXTRN screenseg:WORD +EXTRN linewidth:WORD + +LABEL endtable WORD +labcount = 0 +REPT MAXSCALES +MAKEREF %labcount +labcount = labcount + 1 +ENDM + + + CODESEG + +;================================================== +; +; void scaleline (int scale, unsigned picseg, unsigned maskseg, +; unsigned screen, unsigned width) +; +;================================================== + +PROC ScaleLine pixels:word, scaleptr:dword, picptr:dword, screen:word +USES si,di +PUBLIC ScaleLine + +; +; modify doline procedure for proper width +; + mov bx,[pixels] + cmp bx,MAXSCALES + jbe @@scaleok + mov bx,MAXSCALES +@@scaleok: + shl bx,1 + mov bx,[endtable+bx] + push [cs:bx] ;save the code that will be modified over + mov [WORD cs:bx],0d18eh ;mov ss,cx + push [cs:bx+2] ;save the code that will be modified over + mov [WORD cs:bx+2],90c3h ;ret / nop + push bx + + mov dx,[linewidth] + + mov di,[WORD screen] + mov es,[screenseg] + + mov si,[WORD scaleptr] + mov ds,[WORD scaleptr+2] + + mov bx,[WORD picptr] + mov ax,[WORD picptr+2] ;will be moved into ss after call + + mov bp,bx + + cli + call doline + sti +; +; restore doline to regular state +; + pop bx ;address of modified code + pop [cs:bx+2] + pop [cs:bx] + + mov ax,ss + mov ds,ax + ret + +;================ +; +; doline +; +; Big unwound scaling routine +; +; ds:si = scale table +; ss:bx = pic data +; es:di = screen location +; +;================ + +doline: + + mov cx,ss + mov ss,ax ;can't call a routine with ss used... + +labcount = 0 + +REPT MAXSCALES + +MAKELAB %labcount +labcount = labcount + 1 + + lodsb ; get scaled pixel number + xlat [ss:bx] ; look it up in the picture + xchg [es:di],al ; load latches and write pixel to screen + add di,dx ; down to next line + +ENDM + + mov ss,cx + ret + +ENDP + +END \ No newline at end of file diff --git a/16/cawat/C5_STATE.C b/16/cawat/C5_STATE.C new file mode 100644 index 00000000..ac10ae89 --- /dev/null +++ b/16/cawat/C5_STATE.C @@ -0,0 +1,682 @@ +/* Catacomb Armageddon Source Code + * Copyright (C) 1993-2014 Flat Rock Software + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +// C3_STATE.C + +#include "DEF.H" +#pragma hdrstop + +/* +============================================================================= + + LOCAL CONSTANTS + +============================================================================= +*/ + + +/* +============================================================================= + + GLOBAL VARIABLES + +============================================================================= +*/ + + + +/* +============================================================================= + + LOCAL VARIABLES + +============================================================================= +*/ + + +dirtype opposite[9] = + {south,west,north,east,southwest,northwest,northeast,southeast,nodir}; + + + +//=========================================================================== + + +/* +=================== += += Internal_SpawnNewObj += +=================== +*/ +void Internal_SpawnNewObj (unsigned x, unsigned y, statetype *state, unsigned size, boolean UseDummy, boolean PutInActorat) +{ + extern objtype dummyobj; + + GetNewObj(UseDummy); + new->size = size; + new->state = state; + new->ticcount = random (state->tictime)+1; + + new->tilex = x; + new->tiley = y; + new->x = ((long)x<y = ((long)y<dir = nodir; + new->active = noalways; + + if (new != &dummyobj && PutInActorat) + actorat[new->tilex][new->tiley] = new; +} + +void Internal_SpawnNewObjFrac (long x, long y, statetype *state, unsigned size,boolean UseDummy) +{ + GetNewObj(UseDummy); + new->size = size; + new->state = state; + new->ticcount = random (state->tictime)+1; + new->active = noalways; + + new->x = x; + new->y = y; + new->tilex = x>>TILESHIFT; + new->tiley = y>>TILESHIFT; + CalcBounds(new); + new->distance = 100; + new->dir = nodir; +} + + + + +/* +=================== += += CheckHandAttack += += If the object can move next to the player, it will return true += +=================== +*/ + +boolean CheckHandAttack (objtype *ob) +{ + long deltax,deltay,size; + + size = (long)ob->size + player->size + ob->speed*tics + SIZE_TEST; + deltax = ob->x - player->x; + deltay = ob->y - player->y; + + if (deltax > size || deltax < -size || deltay > size || deltay < -size) + return false; + + return true; +} + + +/* +=================== += += T_DoDamage += += Attacks the player if still nearby, then immediately changes to next state += +=================== +*/ + +void T_DoDamage (objtype *ob) +{ + int points; + + + if (CheckHandAttack(ob) && (!(ob->flags & of_damagedone))) + { + points = 0; + + switch (ob->obclass) + { + case zombieobj: + case fatdemonobj: + points = 8; + break; + case reddemonobj: + case godessobj: + points = 15; + break; + case antobj: + points = 2; + break; + case skeletonobj: + points = 6; + break; + + case wetobj: + points = 7; + break; + case treeobj: + points = 7; + break; + case bunnyobj: + points = 4; + break; + } + TakeDamage (points); + ob->flags |= of_damagedone; + } +} + + +//========================================================================== + +/* +================================== += += Walk += +================================== +*/ + +boolean Walk (objtype *ob) +{ + switch (ob->dir) + { + case north: + if (actorat[ob->tilex][ob->tiley-1]) + return false; + ob->tiley--; + ob->distance = TILEGLOBAL; + return true; + + case northeast: + if (actorat[ob->tilex+1][ob->tiley-1]) + return false; + ob->tilex++; + ob->tiley--; + ob->distance = TILEGLOBAL; + return true; + + case east: + if (actorat[ob->tilex+1][ob->tiley]) + return false; + ob->tilex++; + ob->distance = TILEGLOBAL; + return true; + + case southeast: + if (actorat[ob->tilex+1][ob->tiley+1]) + return false; + ob->tilex++; + ob->tiley++; + ob->distance = TILEGLOBAL; + return true; + + case south: + if (actorat[ob->tilex][ob->tiley+1]) + return false; + ob->tiley++; + ob->distance = TILEGLOBAL; + return true; + + case southwest: + if (actorat[ob->tilex-1][ob->tiley+1]) + return false; + ob->tilex--; + ob->tiley++; + ob->distance = TILEGLOBAL; + return true; + + case west: + if (actorat[ob->tilex-1][ob->tiley]) + return false; + ob->tilex--; + ob->distance = TILEGLOBAL; + return true; + + case northwest: + if (actorat[ob->tilex-1][ob->tiley-1]) + return false; + ob->tilex--; + ob->tiley--; + ob->distance = TILEGLOBAL; + return true; + + case nodir: + return false; + } + + Quit ("Walk: Bad dir"); + return false; +} + + + +/* +================================== += += ChaseThink += have the current monster go after the player, += either diagonally or straight on += +================================== +*/ + +void ChaseThink (objtype *obj, boolean diagonal) +{ + int deltax,deltay,i; + dirtype d[3]; + dirtype tdir, olddir, turnaround; + + + olddir=obj->dir; + turnaround=opposite[olddir]; + + deltax=player->tilex - obj->tilex; + deltay=player->tiley - obj->tiley; + + d[1]=nodir; + d[2]=nodir; + + if (deltax>0) + d[1]= east; + if (deltax<0) + d[1]= west; + if (deltay>0) + d[2]=south; + if (deltay<0) + d[2]=north; + + if (abs(deltay)>abs(deltax)) + { + tdir=d[1]; + d[1]=d[2]; + d[2]=tdir; + } + + if (d[1]==turnaround) + d[1]=nodir; + if (d[2]==turnaround) + d[2]=nodir; + + + if (diagonal) + { /*ramdiagonals try the best dir first*/ + if (d[1]!=nodir) + { + obj->dir=d[1]; + if (Walk(obj)) + return; /*either moved forward or attacked*/ + } + + if (d[2]!=nodir) + { + obj->dir=d[2]; + if (Walk(obj)) + return; + } + } + else + { /*ramstraights try the second best dir first*/ + + if (d[2]!=nodir) + { + obj->dir=d[2]; + if (Walk(obj)) + return; + } + + if (d[1]!=nodir) + { + obj->dir=d[1]; + if (Walk(obj)) + return; + } + } + +/* there is no direct path to the player, so pick another direction */ + + obj->dir=olddir; + if (Walk(obj)) + return; + + if (US_RndT()>128) /*randomly determine direction of search*/ + { + for (tdir=north;tdir<=west;tdir++) + { + if (tdir!=turnaround) + { + obj->dir=tdir; + if (Walk(obj)) + return; + } + } + } + else + { + for (tdir=west;tdir>=north;tdir--) + { + if (tdir!=turnaround) + { + obj->dir=tdir; + if (Walk(obj)) + return; + } + } + } + + obj->dir=turnaround; + Walk(obj); /*last chance, don't worry about returned value*/ +} + + +/* +================= += += MoveObj += +================= +*/ + +void MoveObj (objtype *ob, long move) +{ + ob->distance -=move; + + switch (ob->dir) + { + case north: + ob->y -= move; + return; + case northeast: + ob->x += move; + ob->y -= move; + return; + case east: + ob->x += move; + return; + case southeast: + ob->x += move; + ob->y += move; + return; + case south: + ob->y += move; + return; + case southwest: + ob->x -= move; + ob->y += move; + return; + case west: + ob->x -= move; + return; + case northwest: + ob->x -= move; + ob->y -= move; + return; + + case nodir: + return; + } +} + + +/* +================= += += Chase += += returns true if hand attack range += +================= +*/ + +boolean Chase (objtype *ob, boolean diagonal) +{ + long move; + long deltax,deltay,size; + + ob->flags &= ~of_damagedone; + + move = ob->speed*tics; + size = (long)ob->size + player->size + move + SIZE_TEST; + + while (move) + { + deltax = ob->x - player->x; + deltay = ob->y - player->y; + + if (deltax <= size && deltax >= -size + && deltay <= size && deltay >= -size) + { + CalcBounds (ob); + return true; + } + + if (move < ob->distance) + { + MoveObj (ob,move); + break; + } + actorat[ob->tilex][ob->tiley] = 0; // pick up marker from goal + if (ob->dir == nodir) + ob->dir = north; + + ob->x = ((long)ob->tilex<y = ((long)ob->tiley<distance; + + ChaseThink (ob,diagonal); + if (!ob->distance) + break; // no possible move + actorat[ob->tilex][ob->tiley] = ob; // set down a new goal marker + } + CalcBounds (ob); + return false; +} + +//=========================================================================== + + +/* +=================== += += ShootActor += +=================== +*/ + +void ShootActor (objtype *ob, unsigned damage) +{ + ob->hitpoints -= damage; + + if (ob->hitpoints<=0) + { + switch (ob->obclass) + { + case reddemonobj: + ob->state = &s_red_demondie1; + break; + case succubusobj: + ob->state = &s_succubus_death1; + break; + case fatdemonobj: + ob->state = &s_fatdemon_blowup1; + break; + case godessobj: + ob->state = &s_godessdie1; + break; + case mageobj: + ob->state = &s_magedie1; + break; + case batobj: + ob->state = &s_batdie1; +#if USE_INERT_LIST + ob->obclass = solidobj; // don't add this obj to inert list +#endif + break; + case grelmobj: + ob->state = &s_greldie1; + break; + + case zombieobj: + ob->state = &s_zombie_death1; + break; + + case skeletonobj: + ob->state = &s_skel_die1; + break; + + case antobj: + ob->state = &s_ant_die1; + break; + + case wetobj: + ob->state = &s_wet_die1; +#if USE_INERT_LIST + ob->obclass = solidobj; // don't add this obj to inert list +#endif + break; + + case eyeobj: + ob->state = &s_eye_die1; + break; + + case sshotobj: + case eshotobj: + case mshotobj: + ob->state = &s_bonus_die; +#if USE_INERT_LIST + ob->obclass = solidobj; // don't add these objs to inert list +#endif + break; + + case treeobj: + ob->state = &s_tree_death1; + ob->obclass = solidobj; + ob->temp1 = 3; + ob->flags &= ~of_damagedone; + CalcBounds(ob); + break; + + case bunnyobj: + ob->state = &s_bunny_death1; + break; + + case bonusobj: + case freezeobj: + switch (ob->temp1) + { + case B_POTION: + case B_CHEST: + case B_NUKE: + case B_BOLT: + ob->state = &s_pshot_exp1; + ob->obclass = expobj; + ob->ticcount = ob->state->tictime; + SpawnBigExplosion(ob->x,ob->y,12,(16l<<16L)); + bordertime = FLASHTICS<<2; + bcolor = 14; + VW_ColorBorder(14 | 56); + DisplaySMsg("Item destroyed", NULL); + status_flag = S_NONE; + status_delay = 80; + break; + } +#if USE_INERT_LIST + ob->obclass = solidobj; // don't add this obj to inert list +#endif + break; + + } + + if (ob->obclass != solidobj && ob->obclass != realsolidobj) + { + ob->obclass = inertobj; + ob->flags &= ~of_shootable; + actorat[ob->tilex][ob->tiley] = NULL; +#if USE_INERT_LIST + MoveObjToInert(ob); +#endif + } + else + { + if (ob->flags & of_forcefield) + { + ob->state = &s_force_field_die; + ob->flags &= ~of_shootable; + } + } + } + else + { + switch (ob->obclass) + { + case reddemonobj: + if (!(random(8))) + ob->state = &s_red_demonouch; + else + return; + break; + case succubusobj: + ob->state = &s_succubus_ouch; + break; + case fatdemonobj: + ob->state = &s_fatdemon_ouch; + break; + case godessobj: + ob->state = &s_godessouch; + break; + case mageobj: + ob->state = &s_mageouch; + break; + + case grelmobj: + ob->state = &s_grelouch; + break; + + case zombieobj: + ob->state = &s_zombie_ouch; + break; + + case antobj: + ob->state = &s_ant_ouch; + break; + + case skeletonobj: + ob->state = &s_skel_ouch; + break; + + case wetobj: + ob->state = &s_wet_ouch; + break; + + case eyeobj: + ob->state = &s_eye_ouch; + break; + + case treeobj: + ob->state = &s_tree_ouch; + break; + + case bunnyobj: + ob->state = &s_bunny_ouch; + break; + } + } + ob->ticcount = ob->state->tictime; +} + + diff --git a/16/cawat/C5_TRACE.C b/16/cawat/C5_TRACE.C new file mode 100644 index 00000000..d9ed20e5 --- /dev/null +++ b/16/cawat/C5_TRACE.C @@ -0,0 +1,872 @@ +/* Catacomb Armageddon Source Code + * Copyright (C) 1993-2014 Flat Rock Software + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +// C3_TRACE.C + +#include "DEF.H" +#pragma hdrstop + +/* +============================================================================= + + LOCAL CONSTANTS + +============================================================================= +*/ + + +// +// TESTWALLVISABLE will set the global variable wallvisable to 1 or 0 +// depending on if tile.x,tile.y,wallon is visable from focal point +// +#define TESTWALLVISABLE { \ + if (tile.yfocal.x) \ + voffset += 2; \ + wallvisable = visable[voffset][wallon]; } + + +/* +============================================================================= + + GLOBAL VARIABLES + +============================================================================= +*/ + +boolean aborttrace; + +/* +============================================================================= + + LOCAL VARIABLES + +============================================================================= +*/ + +unsigned wallvisable,voffset; + + +fixed edgex,edgey; + +int wallon; +int basecolor; + +walltype *oldwall; + +// +// offsets from upper left corner of a tile to the left and right edges of +// a given wall (NORTH-WEST) +// +fixed point1x[4] = {GLOBAL1,GLOBAL1,0 ,0 }; +fixed point1y[4] = {0 ,GLOBAL1,GLOBAL1,0 }; + +fixed point2x[4] = {0 ,GLOBAL1,GLOBAL1,0 }; +fixed point2y[4] = {0 ,0 ,GLOBAL1 ,GLOBAL1}; + + +// +// offset from tile.x,tile.y of the tile that shares wallon side +// (side is not visable if it is shared) +// +int sharex[4] = { 0, 1, 0,-1}; +int sharey[4] = {-1, 0, 1, 0}; + +// +// amount to move tile.x,tile.y to follow wallon to another tile +// +int followx[4] = {-1, 0, 1, 0}; +int followy[4] = { 0,-1, 0, 1}; + +// +// cornerwall gives the wall on the same tile to start following when the +// wall ends at an empty tile (go around an edge on same tile) +// turnwall gives the wall on tile.x+sharex,tile.y+sharey to start following +// when the wall hits another tile (right angle corner) +// +int cornerwall[4] = {WEST,NORTH,EAST,SOUTH}; +int turnwall[4] = {EAST,SOUTH,WEST,NORTH}; + +// +// wall visabilities in reletive locations +// -,- 0,- +,- +// -,0 0,0 +,0 +// -,+ 0,+ +,+ +// +int visable[9][4] = +{ + {0,1,1,0}, {0,0,1,0}, {0,0,1,1}, + {0,1,0,0}, {0,0,0,0}, {0,0,0,1}, + {1,1,0,0}, {1,0,0,0}, {1,0,0,1} +}; + +int startwall[9] = {2,2,3, 1,0,3, 1,0,0}; +int backupwall[9] = {3,3,0, 2,0,0, 2,1,1}; + + +int walllength; + +/* +============================================================================= + + FUNCTIONS + +============================================================================= +*/ + +/* +======================== += += FollowTrace += +======================== +*/ + +int FollowTrace (fixed tracex, fixed tracey, long deltax, long deltay, int max) +{ + int tx,ty,otx,oty; + long absdx,absdy,xstep,ystep; + + tx = tracex>>TILESHIFT; + ty = tracey>>TILESHIFT; + + spotvis[tx][ty] = true; + + absdx=LABS(deltax); + absdy=LABS(deltay); + + if (absdx>absdy) + { + ystep = (deltay<<8)/(absdx>>8); + + if (!ystep) + ystep = deltay>0 ? 1 : -1; + + oty = (tracey+ystep)>>TILESHIFT; + if (deltax>0) + { +//############### +// +// step x by +1 +// +//############### + do + { + tx++; + spotvis[tx][ty] = true; + tracey+=ystep; + ty = tracey>>TILESHIFT; + + if (ty!=oty) + { + if (tilemap[tx-1][ty]) + { + tile.x = tx-1; + tile.y = ty; + return 1; + } + oty = ty; + } + if (tilemap[tx][ty]) + { + tile.x = tx; + tile.y = ty; + return 1; + } + } while (--max); + return 0; + } + else + { +//############### +// +// step x by -1 +// +//############### + do + { + tx--; + spotvis[tx][ty] = true; + tracey+=ystep; + ty = tracey>>TILESHIFT; + + if (ty!=oty) + { + if (tilemap[tx][oty]) + { + tile.x = tx; + tile.y = oty; + return 1; + } + oty = ty; + } + if (tilemap[tx][ty]) + { + tile.x = tx; + tile.y = ty; + return 1; + } + } while (--max); + return 0; + + } + } + else + { + xstep = (deltax<<8)/(absdy>>8); + if (!xstep) + xstep = deltax>0 ? 1 : -1; + + + otx = (tracex+xstep)>>TILESHIFT; + if (deltay>0) + { +//############### +// +// step y by +1 +// +//############### + do + { + ty++; + spotvis[tx][ty] = true; + tracex+=xstep; + tx = tracex>>TILESHIFT; + + if (tx!=otx) + { + if (tilemap[tx][ty-1]) + { + tile.x = tx; + tile.y = ty-1; + return 1; + } + otx = tx; + } + if (tilemap[tx][ty]) + { + tile.x = tx; + tile.y = ty; + return 1; + } + } while (--max); + return 0; + } + else + { +//############### +// +// step y by -1 +// +//############### + do + { + ty--; + spotvis[tx][ty] = true; + tracex+=xstep; + tx = tracex>>TILESHIFT; + + if (tx!=otx) + { + if (tilemap[otx][ty]) + { + tile.x = otx; + tile.y = ty; + return 1; + } + otx = tx; + } + if (tilemap[tx][ty]) + { + tile.x = tx; + tile.y = ty; + return 1; + } + } while (--max); + return 0; + } + + } + +} + + +//=========================================================================== + + +/* +================= += += BackTrace += += Traces backwards from edgex,edgey to viewx,viewy to see if a closer += tile obscures the given point. If it does, it finishes the wall and += starts a new one. += Returns true if a tile is hit. += Call with a 1 to have it automatically finish the current wall += +================= +*/ + +int BackTrace (int finish) +{ + fixed tracex,tracey; + long deltax,deltay,absdx,absdy; + int steps,otx,oty,testx,testheight,offset,wall; + + deltax = viewx-edgex; + deltay = viewy-edgey; + + absdx = LABS(deltax); + absdy = LABS(deltay); + + if (absdx>absdy) + steps = ABS(focal.x-(edgex>>TILESHIFT))-1; + else + steps = ABS(focal.y-(edgey>>TILESHIFT))-1; + + if (steps<=0) + return 0; + + otx = tile.x; + oty = tile.y; + if (!FollowTrace(edgex,edgey,deltax,deltay,steps)) + return 0; + +// +// if the start wall is behind the focal point, the trace went too far back +// + if (ABS(tile.x-focal.x)<2 && ABS(tile.y-focal.y)<2) // too close + { + if (tile.x == focal.x && tile.y == focal.y) + { + tile.x = otx; + tile.y = oty; + return 0; + } + + if (tile.xx1 = oldwall->x2; // common edge with last wall + rightwall->height1 = oldwall->height2; + return 0; + } + + +// +// back up along the intersecting face to find the rightmost wall +// + + if (tile.yfocal.x) + offset += 2; + + wallon = backupwall[offset]; + + while (tilemap[tile.x][tile.y]) + { + tile.x += followx[wallon]; + tile.y += followy[wallon]; + }; + + tile.x -= followx[wallon]; + tile.y -= followy[wallon]; + + wallon = cornerwall[wallon]; // turn to first visable face + + edgex = ((long)tile.x<<16); + edgey = ((long)tile.y<<16); + + TransformPoint (edgex+point1x[wallon],edgey+point1y[wallon], + &rightwall->x1,&rightwall->height1); + + basecolor = tilemap[tile.x][tile.y]; + + return 1; +} + +//=========================================================================== + + +/* +================= += += ForwardTrace += += Traces forwards from edgex,edgey along the line from viewx,viewy until += a solid tile is hit. Sets tile.x,tile.y += +================= +*/ + +void ForwardTrace (void) +{ + int offset; + fixed tracex,tracey; + long deltax,deltay; + + deltax = edgex-viewx; + deltay = edgey-viewy; + + FollowTrace(edgex,edgey,deltax,deltay,0); + + if (tile.yfocal.x) + offset += 2; + + wallon = startwall[offset]; + +// +// start the new wall +// + edgex = ((long)tile.x<<16); + edgey = ((long)tile.y<<16); + +// +// if entire first wall is invisable, corner +// + TransformPoint (edgex+point2x[wallon],edgey+point2y[wallon], + &rightwall->x2,&rightwall->height2); + + if (tilemap [tile.x+sharex[wallon]] [tile.y+sharey[wallon]] + || rightwall->x2 < (rightwall-1)->x2 ) + wallon = cornerwall [wallon]; + +// +// transform first point +// + + TransformPoint (edgex+point1x[wallon],edgey+point1y[wallon], + &rightwall->x1,&rightwall->height1); + + basecolor = tilemap[tile.x][tile.y]; +} + + +//=========================================================================== + + +/* +================= += += FinishWall += += Transforms edgex,edgey as the next point of the current wall += and sticks it in the wall list += +================= +*/ + +int FinishWall (void) +{ + char num[20]; + + oldwall = rightwall; + + rightwall->color = basecolor; + + TransformPoint (edgex,edgey,&rightwall->x2,&rightwall->height2); + + if (rightwall->x2 <= (rightwall-1)->x2+2 + && rightwall->height2 < (rightwall-1)->height2 ) + return 0; + + rightwall->walllength = walllength; + + switch (wallon) + { + case north: + case south: + rightwall->side = 0; + rightwall->planecoord = edgey; + break; + + case west: + case east: + rightwall->side = 1; + rightwall->planecoord = edgex; + break; + } + + walllength = 1; + + rightwall++; + + return 1; +} + +//=========================================================================== + + +/* +================= += += InsideCorner += +================= +*/ + +void InsideCorner (void) +{ + int offset; + + // + // the wall turned -90 degrees, so draw what we have, move to the new tile, + // change wallon, change color, and continue following. + // + FinishWall (); + + tile.x += sharex[wallon]; + tile.y += sharey[wallon]; + + wallon = turnwall[wallon]; + + // + // if the new wall is visable, continue following it. Otherwise + // follow it backwards until it turns + // + TESTWALLVISABLE; + + if (wallvisable) + { + // + // just turn to the next wall and continue + // + rightwall->x1 = oldwall->x2; // common edge with last wall + rightwall->height1 = oldwall->height2; + basecolor = tilemap[tile.x][tile.y]; + return; // continue from here + } + + // + // back follow the invisable wall until it turns, then follow that + // + do + { + tile.x += followx[wallon]; + tile.y += followy[wallon]; + } while (tilemap[tile.x][tile.y]); + + tile.x -= followx[wallon]; + tile.y -= followy[wallon]; + + wallon = cornerwall[wallon]; // turn to first visable face + + edgex = ((long)tile.x<<16)+point1x[wallon]; + edgey = ((long)tile.y<<16)+point1y[wallon]; + + if (!BackTrace(0)) // backtrace without finishing a wall + { + TransformPoint (edgex,edgey,&rightwall->x1,&rightwall->height1); + basecolor = tilemap[tile.x][tile.y]; + } +} + +//=========================================================================== + + +/* +================= += += OutsideCorner += +================= +*/ + +void OutsideCorner (void) +{ + int offset; + + // + // edge is the outside edge of a corner, so draw the current wall and + // turn the corner (+90 degrees) + // + FinishWall (); + + tile.x -= followx[wallon]; // backup to the real tile + tile.y -= followy[wallon]; + wallon = cornerwall[wallon]; + + // + // if the new wall is visable, continue following it. Otherwise + // trace a ray from the corner to find a wall in the distance to + // follow + // + TESTWALLVISABLE; + + if (wallvisable) + { + // + // the new wall is visable, so just continue on + // + rightwall->x1 = oldwall->x2; // common edge with last wall + rightwall->height1 = oldwall->height2; + return; // still on same tile, so color is ok + } + +// +// start from a new tile further away +// + ForwardTrace(); // find the next wall further back + +} + + +//=========================================================================== + + +/* +================= += += FollowWalls += += Starts a wall edge at the leftmost edge of tile.x,tile.y and follows it += until something else is seen or the entire view area is covered += +================= +*/ + +void FollowWalls (void) +{ + int height,newcolor,offset,wall; + +//#################### +// +// figure leftmost wall of new tile +// +//#################### + +restart: + + walllength = 1; + + if (tile.yfocal.x) + offset += 2; + + wallon = startwall[offset]; + +// +// if the start wall is inside a block, skip it by cornering to the second wall +// + if ( tilemap [tile.x+sharex[wallon]] [tile.y+sharey[wallon]]) + wallon = cornerwall [wallon]; + +// +// transform first edge to screen coordinates +// + edgex = ((long)tile.x<<16); + edgey = ((long)tile.y<<16); + + TransformPoint (edgex+point1x[wallon],edgey+point1y[wallon], + &rightwall->x1,&rightwall->height1); + + basecolor = tilemap[tile.x][tile.y]; + +//################## +// +// follow the wall as long as possible +// +//################## + +advance: + + do // while ( tile.x != right.x || tile.y != right.y) + { +// +// check for conditions that shouldn't happed... +// + if (rightwall->x1 > VIEWXH) // somehow missed right tile... + return; + + if (rightwall == &walls[DANGERHIGH]) + { + // + // somethiing got messed up! Correct by thrusting ahead... + // +// VW_ColorBorder(6); + bordertime = 60; + Thrust(player->angle,TILEGLOBAL/4); + player->angle+=5; + if (player->angle>ANGLES) + player->angle-=ANGLES; + aborttrace = true; + return; + +#if 0 + strcpy (str,"Wall list overflow at LE:"); + itoa(mapon+1,str2,10); + strcat (str,str2); + strcat (str," X:"); + ltoa(objlist[0].x,str2,10); + strcat (str,str2); + strcat (str," Y:"); + ltoa(objlist[0].y,str2,10); + strcat (str,str2); + strcat (str," AN:"); + itoa(objlist[0].angle,str2,10); + strcat (str,str2); + + Quit (str); +#endif + } + +// +// proceed along wall +// + + edgex = ((long)tile.x<<16)+point2x[wallon]; + edgey = ((long)tile.y<<16)+point2y[wallon]; + + if (BackTrace(1)) // went behind a closer wall + continue; + + // + // advance to next tile along wall + // + tile.x += followx[wallon]; + tile.y += followy[wallon]; + + if (tilemap [tile.x+sharex[wallon]] [tile.y+sharey[wallon]]) + { + InsideCorner (); // turn at a corner + continue; + } + + newcolor = tilemap[tile.x][tile.y]; + + if (!newcolor) // turn around an edge + { + OutsideCorner (); + continue; + } + + if (newcolor != basecolor) + { + // + // wall changed color, so draw what we have and continue following + // + FinishWall (); + rightwall->x1 = oldwall->x2; // new wall shares this edge + rightwall->height1 = oldwall->height2; + basecolor = newcolor; + + continue; + } + walllength++; + } while (tile.x != right.x || tile.y != right.y); + + + +//###################### +// +// draw the last tile +// +//###################### + + edgex = ((long)tile.x<<16)+point2x[wallon]; + edgey = ((long)tile.y<<16)+point2y[wallon]; + FinishWall(); + + wallon = cornerwall[wallon]; + + // + // if the corner wall is visable, draw it + // + TESTWALLVISABLE; + + if (wallvisable) + { + rightwall->x1 = oldwall->x2; // common edge with last wall + rightwall->height1 = oldwall->height2; + edgex = ((long)tile.x<<16)+point2x[wallon]; + edgey = ((long)tile.y<<16)+point2y[wallon]; + FinishWall(); + } + +} + +//=========================================================================== diff --git a/16/cawat/C5_WIZ.C b/16/cawat/C5_WIZ.C new file mode 100644 index 00000000..b899ef62 --- /dev/null +++ b/16/cawat/C5_WIZ.C @@ -0,0 +1,3296 @@ +/* Catacomb Armageddon Source Code + * Copyright (C) 1993-2014 Flat Rock Software + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +// C3_WIZ.C + +#include "DEF.H" +#include "gelib.h" +#pragma hdrstop + +/* +============================================================================= + + LOCAL CONSTANTS + +============================================================================= +*/ + +////////#define NUMSCROLLS 8 + +#define SHOWITEMS 9 + +#define NUKETIME 40 +#define NUMBOLTS 10 +#define BOLTTICS 6 + +#define STATUSCOLOR 8 +#define TEXTCOLOR 14 + +#define SIDEBARWIDTH 5 + +#define BODYLINE 8 +#define POWERLINE 80 + +#define SPECTILESTART 0 // 18 + + +#define SHOTDAMAGE 1 +#define BIGSHOTDAMAGE 3 + + +#define PLAYERSPEED 5120 +#define RUNSPEED (8192<<1) + +#define SHOTSPEED 10000 + +//#define LASTWALLTILE 47 +//#define LASTSPECIALTILE 37 + +#define LASTTILE (LASTWALLPIC-FIRSTWALLPIC) // 47 + +#define FIRETIME 2 + +#define HANDPAUSE 60 + +#define RIGHTEDGE 205; +#define LEFTEDGE 95; +#define PRNY 32; +#define WINX 10; +#define WINY 32; + +/* +============================================================================= + + GLOBAL VARIABLES + +============================================================================= +*/ + +long lastnuke,lasthand; +int lasttext; +int handheight; +int boltsleft,bolttimer; +short RadarXY[MAX_RADAR_BLIPS][3]={-1,-1,-1}; +short radarx=RADARX,radary=RADARY,radar_xcenter=RADAR_XCENTER,radar_ycenter=RADAR_YCENTER; +int key_x[4]={24,27,27,24},key_y[4]={30,57,30,57}; + +boolean redraw_gems,button0down; + +/* +============================================================================= + + LOCAL VARIABLES + +============================================================================= +*/ + +int lastradar; +unsigned lastfiretime; + +int strafeangle[9] = {0,90,180,270,45,135,225,315,0}; + +short RotateAngle = -1; // -1 == No Angle to turn to... +short FreezeTime = 0; // Stops all think (except player) +short RotateSpeed; // Speed (and dir) to rotate.. + + +//=========================================================================== + +void CalcBounds(objtype *ob); +boolean VerifyGateExit(void); +void DrawNSEWIcons(void); +void DrawGems(void); +void DrawRadar (void); +void DrawChar (unsigned x, unsigned y, unsigned tile); +void RedrawStatusWindow (void); +void GiveBolt (void); +void TakeBolt (void); +void GiveNuke (void); +void TakeNuke (void); +void GivePotion (void); +void TakePotion (void); +void GiveKey (int keytype); +void TakeKey (int keytype); +////////////void GiveScroll (int scrolltype,boolean show); +////////////void ReadScroll (int scroll); +////////////void DrawScrolls(void); + +void DrawNum(short x,short y,short value,short maxdigits); + +//---------- + +void Shoot (void); +void BigShoot (void); +void CastBolt (void); +void CastNuke (void); +void DrinkPotion (void); + +//---------- +void DrawHealth(void); + +void SpawnPlayer (int tilex, int tiley, int dir); +void Thrust (int angle, unsigned speed); +void T_Player (objtype *ob); + +//void AddPoints (int points); + +void ClipMove (objtype *ob, long xmove, long ymove); +boolean ShotClipMove (objtype *ob, long xmove, long ymove); + +//=========================================================================== + + +/* +=============== += += DrawChar += +=============== +*/ + +void DrawChar (unsigned x, unsigned y, unsigned tile) +{ + unsigned junk = latchpics[0]; + + EGAWRITEMODE(1); +asm mov bx,[y] +asm shl bx,1 +asm mov di,[WORD PTR ylookup+bx] +asm add di,[x] +asm mov si,[tile] +asm shl si,1 +asm shl si,1 +asm shl si,1 +asm add si,[junk] // the damn inline assembler won't reference latchpics +asm mov ax,[screenseg] +asm mov es,ax +asm mov ds,ax +asm mov dx,SCREENWIDTH-1 +asm movsb +asm add di,dx +asm movsb +asm add di,dx +asm movsb +asm add di,dx +asm movsb +asm add di,dx +asm movsb +asm add di,dx +asm movsb +asm add di,dx +asm movsb +asm add di,dx +asm movsb + +asm mov ax,ss +asm mov ds,ax + EGAWRITEMODE(0); +} + + +//=========================================================================== + +/* +=============== += += RedrawStatusWindow += +=============== +*/ + +void RedrawStatusWindow (void) +{ + short keytype; + + EGABITMASK(0xff); + for (keytype=0; keytype<4; keytype++) + DrawNum(key_x[keytype],key_y[keytype],gamestate.keys[keytype],2); + DrawNum(20,54,gamestate.potions,2); + DrawNum(20,36,gamestate.nukes,2); + DrawNum(20,18,gamestate.bolts,2); + + DrawHealth(); + DrawRadar(); + EGAWRITEMODE(0); + DrawGems(); +//////// DrawScrolls(); + redraw_gems = false; +} + + +//=========================================================================== + +/* +=============== += += GiveBolt += +=============== +*/ + +void GiveBolt (void) +{ + if (gamestate.bolts == 99) + return; + + SD_PlaySound (GETBOLTSND); + DrawNum(20,18,++gamestate.bolts,2); +} + + +/* +=============== += += TakeBolt += +=============== +*/ + +void TakeBolt (void) +{ + SD_PlaySound (USEBOLTSND); + DrawNum(20,18,--gamestate.bolts,2); +} + +//=========================================================================== + +/* +=============== += += GiveNuke += +=============== +*/ + +void GiveNuke (void) +{ + if (gamestate.nukes == 99) + return; + + SD_PlaySound (GETNUKESND); + DrawNum(20,36,++gamestate.nukes,2); +} + + +/* +=============== += += TakeNuke += +=============== +*/ + +void TakeNuke (void) +{ + SD_PlaySound (USENUKESND); + DrawNum(20,36,--gamestate.nukes,2); +} + +//=========================================================================== + +/* +=============== += += GivePotion += +=============== +*/ + +void GivePotion (void) +{ + if (gamestate.potions == 99) + return; + + SD_PlaySound (GETPOTIONSND); + DrawNum(20,54,++gamestate.potions,2); +} + + +/* +=============== += += TakePotion += +=============== +*/ + +void TakePotion (void) +{ + SD_PlaySound (USEPOTIONSND); + DrawNum(20,54,--gamestate.potions,2); +} + +//=========================================================================== + +/* +=============== += += GiveKey += +=============== +*/ + +void GiveKey (int keytype) +{ + int i,j,x; + + if (gamestate.keys[keytype] == 99) + return; + + SD_PlaySound (GETKEYSND); + DrawNum(key_x[keytype],key_y[keytype],++gamestate.keys[keytype],2); +} + + +/* +=============== += += TakeKey += +=============== +*/ + +void TakeKey (int keytype) +{ + int i,j,x; + char *key_colors[] = {"a RED key", + "a YELLOW key", + "a GREEN key", + "a BLUE key"}; + + + SD_PlaySound (USEKEYSND); + DrawNum(key_x[keytype],key_y[keytype],--gamestate.keys[keytype],2); + displayofs = bufferofs = screenloc[screenpage]; + CenterWindow(20,5); + US_CPrint("\nYou use\n"); + US_CPrint(key_colors[keytype]); + VW_UpdateScreen(); + VW_WaitVBL(120); +} + + +//=========================================================================== + +/* +=============== += += GiveGem += +=============== +*/ + +void GiveGem (int gemtype) +{ +#if 0 + int i,j,x; + + SD_PlaySound (GETKEYSND); + DrawNum(key_x[keytype],key_y[keytype],++gamestate.keys[keytype],2); +#endif +} + + +/* +=============== += += TakeGem += +=============== +*/ + +void TakeGem (int gemtype) +{ +#if 0 + int i,j,x; + + SD_PlaySound (USEKEYSND); + DrawNum(key_x[keytype],key_y[keytype],--gamestate.keys[keytype],2); +#endif +} + +/* +=============== += += DrawGem += +=============== +*/ + +void DrawGems() +{ + short loop; + + redraw_gems = false; + + bufferofs = 0; + LatchDrawPic (31,51,RADAR_BOTTOMPIC); + for (loop=0; loop<5; loop++) + if (gamestate.gems[loop]) + LatchDrawPic (32+loop,53,RADAR_RGEMPIC+loop); +} + +//=========================================================================== + +#if 0 + +/* +=============== += += GiveScroll += +=============== +*/ + +void GiveScroll (int scrolltype,boolean show) +{ + int i,j,x,y,scrollnum; + + SD_PlaySound (GETSCROLLSND); + gamestate.scrolls[scrolltype] = true; + + y = 30 + ((scrolltype > 3) * 10); + x = 26 + (scrolltype % 4); + DrawChar(x,y,SCROLLCHARS+scrolltype); + + if (show) + ReadScroll(scrolltype); +} + +/* +=============== += += DrawScrolls += += Force draw of all scrolls += +=============== +*/ +void DrawScrolls() +{ + int loop,x,y; + + VW_Bar(210,30,30,18,0xf); + + for (loop=0;loop<8;loop++) + if (gamestate.scrolls[loop]) + { + y = 30 + ((loop > 3) * 10); + x = 26 + (loop % 4); + DrawChar(x,y,SCROLLCHARS+loop); + } +} +#endif + + +//=========================================================================== + +#if 0 +/* +=============== += += GivePoints += +=============== +*/ + +void GivePoints (int points) +{ + pointcount = 1; + pointsleft += points; +} +#endif + + +//=========================================================================== + +#if 0 +/* +=============== += += AddPoints += +=============== +*/ + +void AddPoints (int points) +{ + char str[10]; + int len,x,i; + + gamestate.score += points; + + ltoa (gamestate.score,str,10); + len = strlen (str); + + x=24+(8-len); + for (i=0;i 75) + picnum = FACE1PIC; + else + if (percentage > 50) + picnum = FACE2PIC; + else + if (percentage > 25) + picnum = FACE3PIC; + else + if (percentage) + picnum = FACE4PIC; + else + { + picnum = FACE5PIC; + CA_CacheGrChunk (picnum); + } + + bufferofs = 0; + if (!percentage) + { + UNMARKGRCHUNK(picnum); +// VW_DrawPic(8,14,picnum); + VW_DrawPic(10,14,picnum); + } + else + LatchDrawPic(10,14,picnum); +} + +//=========================================================================== + +/* +=============== += += DrawFreezeTime += +=============== +*/ +void DrawFreezeTime() +{ + long percentage; + percentage = PERCENTAGE(100,MAXFREEZETIME,(long)FreezeTime,7); + DrawNum(23,70,percentage,3); +} + +//=========================================================================== + +/* +=============== += += DrawNum += +=============== +*/ +void DrawNum(short x,short y,short value,short maxdigits) +{ + char str[10],len,i; + + itoa(value,str,10); + len=strlen(str); + + for (i=len; itiley]+player->tilex)-NAMESTART; + + if ( number>26 ) + number = 0; + + if ((number == lasttext) && (!draw_text_whether_it_needs_it_or_not)) + return; + + lasttext = number; + + text = (char _seg *)grsegs[LEVEL1TEXT+mapon]+textstarts[number]; + + _fmemcpy (str,text,80); + DisplayMsg(str,NULL); +} + +//=========================================================================== + +/* +=============== += += DisplayMsg += +=============== +*/ + +char DisplayMsg(char *text,char *choices) +{ + char ch=true; + short temp; + + bufferofs = 0; + PrintY = 1; + WindowX = 20; + WindowW = 270; + + VW_Bar (WindowX,2,WindowW,8,STATUSCOLOR); + temp = fontcolor; + fontcolor = TEXTCOLOR^STATUSCOLOR; + US_CPrintLine (text); + fontcolor = temp; + + if (choices) + { + ch=GetKeyChoice(choices,true); + LastScan = 0; + } + + return(ch); +} + +/* +=============== += += DisplaySMsg += +=============== +*/ +char DisplaySMsg(char *text,char *choices) +{ + char ch=true; + short temp; + + bufferofs = 0; + PrintY = 69; + WindowX = 98; + WindowW = 115; + + VW_Bar(WindowX,PrintY+1,WindowW,8,STATUSCOLOR); + temp = fontcolor; + fontcolor = TEXTCOLOR^STATUSCOLOR; + US_CPrintLine (text); + fontcolor = temp; + + if (choices) + { + ch=GetKeyChoice(choices,true); + LastScan = 0; + } + + return(ch); +} + +//=========================================================================== + +/* +=============== += += DrawRadar += +=============== +*/ + +void DrawRadar (void) +{ + int angle,number; + short objnum; + + bufferofs = 0; + LatchDrawPic (radarx,radary,RADAR_TOPPIC); + + asm cli + asm mov dx,GC_INDEX + asm mov ax,2*256+GC_MODE + asm out dx,ax // write mode 2 + + asm mov ax,GC_DATAROTATE + asm out dx,ax // no rotation / logical operation + + asm mov dx,SC_INDEX + asm mov al,SC_MAPMASK + asm mov ah,15 + asm out dx,ax // write to all four planes + asm sti + + objnum = 0; + while (RadarXY[objnum][2] != -1) + { + RadarBlip(radar_xcenter+RadarXY[objnum][0],radar_ycenter+RadarXY[objnum][1],RadarXY[objnum][2]); + objnum++; + } + + asm cli + asm mov dx,GC_INDEX + asm mov ax,255*256+GC_BITMASK + asm out dx,ax // reset bitmask to %11111111 + asm sti +} + +//=========================================================================== + + +//-------------------------------------------------------------------------- +// DrawNSEWIcons(void) +//-------------------------------------------------------------------------- + +void DrawRadarObj(short dx, short dy, unsigned sprnum,signed long psin,signed long pcos); + +void DrawNSEWIcons() +{ + signed x,y; + + x = -FixedByFrac(RADAR_X_IRADIUS,costable[player->angle]); + y = -FixedByFrac(RADAR_Y_IRADIUS,sintable[player->angle]); + + VWB_DrawSprite(radar_xcenter+x-3,radar_ycenter+y-3,NORTHICONSPR); + +} + +#if 0 +/* +=============== += += DrawBars += +=============== +*/ + +void DrawBars (void) +{ + int i; + unsigned source,dest,topline; + + for (i=0;i<3;i++) + { + bufferofs = screenloc[i]; + VW_Bar (34*8,POWERLINE,40,MAXSHOTPOWER,1); + } + EGAWRITEMODE(1); + asm mov es,[screenseg] + +// +// shot power +// + if (gamestate.shotpower) + { + topline = MAXSHOTPOWER - gamestate.shotpower; + + source = latchpics[SHOTPOWERPIC-FIRSTLATCHPIC]+topline*SIDEBARWIDTH; + dest = (POWERLINE+topline)*SCREENWIDTH+34; + + asm mov si,[source] + asm mov di,[dest] + + asm mov cx,[WORD PTR gamestate.shotpower] +newline: + asm mov al,[es:si] + asm mov [es:di+PAGE1START],al + asm mov [es:di+PAGE2START],al + asm mov [es:di+PAGE3START],al + asm mov al,[es:si+1] + asm mov [es:di+1+PAGE1START],al + asm mov [es:di+1+PAGE2START],al + asm mov [es:di+1+PAGE3START],al + asm mov al,[es:si+2] + asm mov [es:di+2+PAGE1START],al + asm mov [es:di+2+PAGE2START],al + asm mov [es:di+2+PAGE3START],al + asm mov al,[es:si+3] + asm mov [es:di+3+PAGE1START],al + asm mov [es:di+3+PAGE2START],al + asm mov [es:di+3+PAGE3START],al + asm mov al,[es:si+4] + asm mov [es:di+4+PAGE1START],al + asm mov [es:di+4+PAGE2START],al + asm mov [es:di+4+PAGE3START],al + + asm add di,SCREENWIDTH + asm add si,5 + + asm loop newline + } + +// +// body +// + if (gamestate.body) + { + source = latchpics[BODYPIC-FIRSTLATCHPIC]; + dest = BODYLINE*SCREENWIDTH+34; + + asm mov si,[source] + asm mov di,[dest] + + asm mov cx,[WORD PTR gamestate.body] +newline2: + asm mov al,[es:si] + asm mov [es:di+PAGE1START],al + asm mov [es:di+PAGE2START],al + asm mov [es:di+PAGE3START],al + asm mov al,[es:si+1] + asm mov [es:di+1+PAGE1START],al + asm mov [es:di+1+PAGE2START],al + asm mov [es:di+1+PAGE3START],al + asm mov al,[es:si+2] + asm mov [es:di+2+PAGE1START],al + asm mov [es:di+2+PAGE2START],al + asm mov [es:di+2+PAGE3START],al + asm mov al,[es:si+3] + asm mov [es:di+3+PAGE1START],al + asm mov [es:di+3+PAGE2START],al + asm mov [es:di+3+PAGE3START],al + asm mov al,[es:si+4] + asm mov [es:di+4+PAGE1START],al + asm mov [es:di+4+PAGE2START],al + asm mov [es:di+4+PAGE3START],al + + asm add di,SCREENWIDTH + asm add si,5 + + asm loop newline2 + } + + if (gamestate.body != MAXBODY) + { + source = latchpics[NOBODYPIC-FIRSTLATCHPIC]+gamestate.body*SIDEBARWIDTH; + dest = (BODYLINE+gamestate.body)*SCREENWIDTH+34; + topline = MAXBODY-gamestate.body; + + asm mov si,[source] + asm mov di,[dest] + + asm mov cx,[WORD PTR topline] +newline3: + asm mov al,[es:si] + asm mov [es:di+PAGE1START],al + asm mov [es:di+PAGE2START],al + asm mov [es:di+PAGE3START],al + asm mov al,[es:si+1] + asm mov [es:di+1+PAGE1START],al + asm mov [es:di+1+PAGE2START],al + asm mov [es:di+1+PAGE3START],al + asm mov al,[es:si+2] + asm mov [es:di+2+PAGE1START],al + asm mov [es:di+2+PAGE2START],al + asm mov [es:di+2+PAGE3START],al + asm mov al,[es:si+3] + asm mov [es:di+3+PAGE1START],al + asm mov [es:di+3+PAGE2START],al + asm mov [es:di+3+PAGE3START],al + asm mov al,[es:si+4] + asm mov [es:di+4+PAGE1START],al + asm mov [es:di+4+PAGE2START],al + asm mov [es:di+4+PAGE3START],al + + asm add di,SCREENWIDTH + asm add si,5 + + asm loop newline3 + } + + EGAWRITEMODE(0); +} +#endif + +///////////////////////////////////////////////////////////////////////////// +// +// Check the object and make sure it is a monster. Used in making the sound +// of a monster being shot. +// +///////////////////////////////////////////////////////////////////////////// + +boolean PlayMonsterSound(classtype objclass) +{ + switch (objclass) + { + case solidobj: + case realsolidobj: + return false; + default: + return true; + } +} + + +/* +============================================================================= + + SHOTS + +============================================================================= +*/ + +void T_Pshot (objtype *ob); + + +extern statetype s_pshot1; +extern statetype s_pshot2; + +//extern statetype s_bigpshot1; +//extern statetype s_bigpshot2; + + +statetype s_pshot1 = {PSHOT1PIC,8,&T_Pshot,&s_pshot2}; +statetype s_pshot2 = {PSHOT2PIC,8,&T_Pshot,&s_pshot1}; + + +statetype s_pshot_exp1 = {PSHOT_EXP1PIC,7,NULL,&s_pshot_exp2}; +statetype s_pshot_exp2 = {PSHOT_EXP2PIC,7,NULL,&s_pshot_exp3}; +statetype s_pshot_exp3 = {PSHOT_EXP3PIC,7,NULL,NULL}; + + +//statetype s_shotexplode = {PSHOT2PIC,8,NULL,NULL}; + +//statetype s_bigpshot1 = {BIGPSHOT1PIC,8,&T_Pshot,&s_bigpshot2}; +//statetype s_bigpshot2 = {BIGPSHOT2PIC,8,&T_Pshot,&s_bigpshot1}; + + +/* +=================== += += SpawnPShot += +=================== +*/ + +void SpawnPShot (void) +{ + DSpawnNewObjFrac (player->x,player->y,&s_pshot1,PIXRADIUS*7); + new->obclass = pshotobj; + new->speed = SHOTSPEED; + new->angle = player->angle; + new->active = always; +} + +#if 0 +void SpawnBigPShot (void) +{ + SpawnNewObjFrac (player->x,player->y,&s_bigpshot1,24*PIXRADIUS); + new->obclass = bigpshotobj; + new->speed = SHOTSPEED; + new->angle = player->angle; +} +#endif + + +/* +=================== += += JimsShotClipMove += += Only checks corners, so the object better be less than one tile wide! += +=================== +*/ +boolean JimsShotClipMove (objtype *ob, long xmove, long ymove) +{ + int xl,yl,xh,yh,tx,ty,nt1,nt2,x,y; + long intersect,basex,basey,pointx,pointy; + unsigned inside,total,tile; + objtype *check; + boolean moveok; + +// +// move player and check to see if any corners are in solid tiles +// +// basex = ob->x; +// basey = ob->y; + +// ob->x += xmove; +// ob->y += ymove; + +// CalcBounds (ob); + + xl = ob->xl>>TILESHIFT; + yl = ob->yl>>TILESHIFT; + + xh = ob->xh>>TILESHIFT; + yh = ob->yh>>TILESHIFT; + + for (y=yl;y<=yh;y++) + for (x=xl;x<=xh;x++) + { + check = actorat[x][y]; + + if ((!check) || (check == player) || (!(check->flags & of_shootable))) + continue; + + ob->x -= xmove; + ob->y -= ymove; + + if (check->obclass != solidobj && check->obclass != hbunnyobj) + { + if (PlayMonsterSound(check->obclass)) + SD_PlaySound (SHOOTMONSTERSND); + if (ob->obclass == bigpshotobj) + ShootActor (check,BIGSHOTDAMAGE); + else + ShootActor (check,SHOTDAMAGE); + } + else + if (check->obclass == solidobj && (check->flags & of_forcefield)) + { + if (PlayMonsterSound(check->obclass)) + SD_PlaySound (SHOOTMONSTERSND); + if (ob->obclass == bigpshotobj) + ShootActor (check,BIGSHOTDAMAGE); + else + ShootActor (check,SHOTDAMAGE); + } + ob->state = &s_pshot_exp1; + ob->ticcount = ob->state->tictime; + return(true); + } + + return(false); // move is OK! + +} + + +/* +=============== += += T_Pshot += +=============== +*/ +#if 0 +void T_Pshot (objtype *ob) +{ + objtype *check; + long xmove,ymove,speed; + +// +// check current position for monsters having moved into it +// + for (check = player->next; check; check=check->next) + if ((check->flags & of_shootable) + && ob->xl <= check->xh + && ob->xh >= check->xl + && ob->yl <= check->yh + && ob->yh >= check->yl) + { + + if (check->obclass != solidobj) + { + if (PlayMonsterSound(check->obclass)) + SD_PlaySound (SHOOTMONSTERSND); + if (ob->obclass == bigpshotobj) + ShootActor (check,BIGSHOTDAMAGE); + else + ShootActor (check,SHOTDAMAGE); + } + + ob->state = &s_pshot_exp1; + ob->ticcount = ob->state->tictime; + return; + } + + +// +// move ahead, possibly hitting a wall +// + speed = ob->speed*tics; + + xmove = FixedByFrac(speed,costable[ob->angle]); + ymove = -FixedByFrac(speed,sintable[ob->angle]); + + if (ShotClipMove(ob,xmove,ymove)) + { + ob->state = &s_pshot_exp1; + ob->ticcount = ob->state->tictime; + return; + } + + ob->tilex = ob->x >> TILESHIFT; + ob->tiley = ob->y >> TILESHIFT; + +// +// check final position for monsters hit +// + for (check = player->next; check; check=check->next) + if ((ob->flags & of_shootable) + && ob->xl <= check->xh + && ob->xh >= check->xl + && ob->yl <= check->yh + && ob->yh >= check->yl) + { + ShootActor (check,SHOTDAMAGE); + ob->state = &s_pshot_exp1; + ob->ticcount = ob->state->tictime; + return; + } +} +#endif + + + +void T_Pshot (objtype *ob) +{ + objtype *check; + long xmove,ymove,speed; + int xl,yl,xh,yh,tx,ty,nt1,nt2,x,y; + long intersect,basex,basey,pointx,pointy; + unsigned inside,total,tile; + boolean moveok; + +// +// check current position for monsters having moved into it +// + for (check = player->next; check; check=check->next) + if ((check->flags & of_shootable) + && ob->xl <= check->xh + && ob->xh >= check->xl + && ob->yl <= check->yh + && ob->yh >= check->yl) + { + + if (check->obclass != solidobj && check->obclass != hbunnyobj) + { + if (PlayMonsterSound(check->obclass)) + SD_PlaySound (SHOOTMONSTERSND); + if (ob->obclass == bigpshotobj) + ShootActor (check,BIGSHOTDAMAGE); + else + ShootActor (check,SHOTDAMAGE); + } + + ob->state = &s_pshot_exp1; + ob->obclass = expobj; + ob->ticcount = ob->state->tictime; + return; + } + + +// +// move ahead, possibly hitting a wall +// + speed = ob->speed*tics; + + xmove = FixedByFrac(speed,costable[ob->angle]); + ymove = -FixedByFrac(speed,sintable[ob->angle]); + + if (ShotClipMove(ob,xmove,ymove)) + { + ob->state = &s_pshot_exp1; + ob->obclass = expobj; + ob->ticcount = ob->state->tictime; + return; + } + + ob->tilex = ob->x >> TILESHIFT; + ob->tiley = ob->y >> TILESHIFT; + +// +// check final position for monsters hit +// + + JimsShotClipMove(obj,xmove,ymove); + +} + + +/* +============================================================================= + + PLAYER ACTIONS + +============================================================================= +*/ + +/* +=============== += += BuildShotPower += +=============== +*/ + +void BuildShotPower (void) +{ + int newlines,topline; + long i; + unsigned source,dest; + + if (gamestate.shotpower == MAXSHOTPOWER) + return; + + newlines = 0; + for (i=lasttimecount-realtics;i MAXSHOTPOWER) + { + newlines -= (gamestate.shotpower - MAXSHOTPOWER); + gamestate.shotpower = MAXSHOTPOWER; + } +} + + +//=========================================================================== + +/* +=============== += += ClearShotPower += +=============== +*/ + +void ClearShotPower (void) +{ + unsigned source,dest,topline; + +#if 0 + topline = MAXSHOTPOWER - gamestate.shotpower; + + source = latchpics[L_NOSHOT]+topline*SIDEBARWIDTH; + dest = (POWERLINE+topline)*SCREENWIDTH+34; + + asm mov es,[screenseg] + asm mov si,[source] + asm mov di,[dest] + + if (!gamestate.shotpower) + return; + + EGAWRITEMODE(1); + + asm mov cx,[WORD PTR gamestate.shotpower] +newline: + asm mov al,[es:si] + asm mov [es:di+PAGE1START],al + asm mov [es:di+PAGE2START],al + asm mov [es:di+PAGE3START],al + asm mov al,[es:si+1] + asm mov [es:di+1+PAGE1START],al + asm mov [es:di+1+PAGE2START],al + asm mov [es:di+1+PAGE3START],al + asm mov al,[es:si+2] + asm mov [es:di+2+PAGE1START],al + asm mov [es:di+2+PAGE2START],al + asm mov [es:di+2+PAGE3START],al + asm mov al,[es:si+3] + asm mov [es:di+3+PAGE1START],al + asm mov [es:di+3+PAGE2START],al + asm mov [es:di+3+PAGE3START],al + asm mov al,[es:si+4] + asm mov [es:di+4+PAGE1START],al + asm mov [es:di+4+PAGE2START],al + asm mov [es:di+4+PAGE3START],al + + asm add di,SCREENWIDTH + asm add si,5 + + asm loop newline + + EGAWRITEMODE(0); +#endif + + gamestate.shotpower = 0; +} + +//=========================================================================== + +/* +=============== += += Shoot += +=============== +*/ + +void Shoot (void) +{ + ClearShotPower (); + SD_PlaySound (SHOOTSND); + SpawnPShot (); +} + +//=========================================================================== + +#if 0 +/* +=============== += += BigShoot += +=============== +*/ + +void BigShoot (void) +{ + ClearShotPower (); + SD_PlaySound (BIGSHOOTSND); + SpawnBigPShot (); +} +#endif + +//=========================================================================== + +/* +=============== += += CastBolt += +=============== +*/ + +void CastBolt (void) +{ + if (!gamestate.bolts) + { + SD_PlaySound (NOITEMSND); + return; + } + + TakeBolt (); + boltsleft = NUMBOLTS; + bolttimer = BOLTTICS; + Shoot (); +} + + +/* +=============== += += ContinueBolt += +=============== +*/ + +void ContinueBolt (void) +{ + bolttimer-=realtics; + if (bolttimer<0) + { + boltsleft--; + bolttimer = BOLTTICS; + Shoot (); + } +} + + +//=========================================================================== + +/* +=============== += += CastNuke += +=============== +*/ + +void CastNuke (void) +{ + extern boolean autofire; + + int angle; + + if (!gamestate.nukes) + { + SD_PlaySound (NOITEMSND); + return; + } + + if (!autofire) + TakeNuke (); + lastnuke = TimeCount; + + for (angle = 0; angle < ANGLES; angle+= ANGLES/16) + { + DSpawnNewObjFrac (player->x,player->y,&s_pshot1,24*PIXRADIUS); + new->obclass = bigpshotobj; + new->speed = SHOTSPEED; + new->angle = angle; + new->active = always; + } +} + +//=========================================================================== + +/* +=============== += += DrinkPotion += +=============== +*/ + +void DrinkPotion (void) +{ + unsigned source,dest,topline; + + if (!gamestate.potions) + { + SD_PlaySound (NOITEMSND); + return; + } + + DisplaySMsg("Curing", NULL); + TakePotion (); + gamestate.body = MAXBODY; + VW_WaitVBL(30); + status_flag = S_NONE; + +#if 0 +// +// draw a full up bar +// + source = latchpics[L_BODYBAR]; + dest = BODYLINE*SCREENWIDTH+34; + + asm mov es,[screenseg] + asm mov si,[source] + asm mov di,[dest] + + EGAWRITEMODE(1); + + asm mov cx,MAXBODY +newline: + asm mov al,[es:si] + asm mov [es:di+PAGE1START],al + asm mov [es:di+PAGE2START],al + asm mov [es:di+PAGE3START],al + asm mov al,[es:si+1] + asm mov [es:di+1+PAGE1START],al + asm mov [es:di+1+PAGE2START],al + asm mov [es:di+1+PAGE3START],al + asm mov al,[es:si+2] + asm mov [es:di+2+PAGE1START],al + asm mov [es:di+2+PAGE2START],al + asm mov [es:di+2+PAGE3START],al + asm mov al,[es:si+3] + asm mov [es:di+3+PAGE1START],al + asm mov [es:di+3+PAGE2START],al + asm mov [es:di+3+PAGE3START],al + asm mov al,[es:si+4] + asm mov [es:di+4+PAGE1START],al + asm mov [es:di+4+PAGE2START],al + asm mov [es:di+4+PAGE3START],al + asm add di,SCREENWIDTH + asm add si,5 + + asm loop newline + + EGAWRITEMODE(0); +#endif +} + + + +//=========================================================================== + +#if 0 + +//////////////////////////////////////////////////////////////////////////// +// +// GetScrollText +// +// parms - scroll -- the number of the scroll to display +// returns - a far pointer to the scroll text +// +//////////////////////////////////////////////////////////////////////////// + +char far *GetScrollText (int scroll) +{ + boolean found; + int i; + char far *txt; + unsigned ofset; + + CA_CacheGrChunk(SCROLLTEXT); + + found = false; + i = 0; + + txt = (char _seg *)grsegs[SCROLLTEXT]; + + while (!found) + { + while (*txt != '\n') + { + if (*txt == '\r') + *txt = 0; + txt++; + } + txt++; + if (i == scroll) + { + found = true; + ofset = FP_OFF(txt); + + while (*txt != '\n') + { + if (*txt == '\r') + *txt = 0; + txt++; + } + } + i++; + } + txt = (char _seg *)grsegs[SCROLLTEXT]+ofset; + + UNMARKGRCHUNK(SCROLLTEXT); + return(txt); +} //End of GetScrollText + +//=========================================================================== + +/* +=============== += += ReadScroll += +=============== +*/ + +extern boolean tileneeded[NUMFLOORS]; + +void ReadScroll (int scroll) +{ + PresenterInfo pi; + int i; + unsigned *skytemp,*gndtemp,blackcolor=0; + char far *scrolltext; + + DisplaySMsg("Reading Scroll", NULL); + bufferofs = displayofs = screenloc[screenpage]; + + if (status_flag != S_TIMESTOP) + status_flag = S_NONE; + + FreeUpMemory(); + + CA_CacheGrChunk (SCROLLTOPPIC); + CA_CacheGrChunk (SCROLL1PIC); + CA_CacheGrChunk (SCROLLBOTTOMPIC); + + skytemp = skycolor; + gndtemp = groundcolor; + skycolor = groundcolor = &blackcolor; + + VW_Bar(0,0,VIEWWIDTH,VIEWHEIGHT,0); + VW_DrawPic (10,0,SCROLLTOPPIC); + VW_DrawPic (10,32,SCROLL1PIC); + VW_DrawPic (10,88,SCROLLBOTTOMPIC); + + scrolltext = GetScrollText(scroll); + + pi.xl = LEFTEDGE; + pi.yl = PRNY; + pi.xh = RIGHTEDGE; + pi.yh = PRNY+1; + pi.bgcolor = 7; + pi.script[0] = (char far *)scrolltext; + Presenter(&pi); + + skycolor = skytemp; + groundcolor = gndtemp; + + UNMARKGRCHUNK(SCROLL1PIC); + UNMARKGRCHUNK(SCROLLTOPPIC); + UNMARKGRCHUNK(SCROLLBOTTOMPIC); + MM_FreePtr (&grsegs[SCROLL1PIC]); + MM_FreePtr (&grsegs[SCROLLTOPPIC]); + MM_FreePtr (&grsegs[SCROLLBOTTOMPIC]); + + CacheScaleds(); + + IN_ClearKeysDown (); + lasttext = -1; + DisplayMsg("Press ENTER or ESC to exit.",NULL); + while ((!Keyboard[sc_Escape]) && (!Keyboard[sc_Enter])); + IN_ClearKeysDown (); + + if (status_flag == S_TIMESTOP) + DisplaySMsg("Time Stopped: ",NULL); +} + +#endif + + +//=============== +// +// StopTime() +// +// +//=============== +void StopTime() +{ + FreezeTime = MAXFREEZETIME; + SD_PlaySound(FREEZETIMESND); + DisplaySMsg("Time Stopped: ",NULL); + status_flag = S_TIMESTOP; +} + + +/* +=============== += += TakeDamage += +=============== +*/ + +void TakeDamage (int points) +{ + unsigned source,dest,topline; + + if (!gamestate.body || (bordertime && bcolor==FLASHCOLOR) || godmode) + return; + + if (points != 1) + points = EasyDoDamage(points); + + if (points >= gamestate.body) + { + points = gamestate.body; + Flags |= FL_DEAD; + } + + bordertime = FLASHTICS<<2; + bcolor = FLASHCOLOR; + VW_ColorBorder (FLASHCOLOR); + + DisplaySMsg("Damaging blows!", NULL); + status_flag = S_NONE; + status_delay = 80; + + if (gamestate.body> 8; + if (spot == CANT_OPEN_CODE) // CAN'T EVER OPEN (it's just for looks) + { + CenterWindow(30,4); + US_CPrint("\nThis door is permanently blocked"); + VW_UpdateScreen(); + IN_ClearKeysDown(); + IN_Ack(); + return; + } + + // make sure player has key to get into door + // + + if (TILE_FLAGS(tile) & tf_EMBEDDED_KEY_COLOR) + keyspot = GATE_KEY_COLOR(tile); + else + keyspot = (*(mapsegs[2]+farmapylookup[y+1]+x)) >> 8; + + if (keyspot--) + if (!gamestate.keys[keyspot]) + { + SD_PlaySound(HIT_GATESND); + CenterWindow(20,5); + US_CPrint("\nYou need\n"); + US_CPrint(key_colors[keyspot]); + VW_UpdateScreen(); + IN_ClearKeysDown(); + IN_Ack(); + return; + } + + // + // deal with this gate (warp? simply open? whatever...) + // + switch (spot) + { + case NEXT_LEVEL_CODE: // WARP TO NEXT LEVEL + newlevel = gamestate.mapon+1; + playstate = ex_warped; + break; + + case REMOVE_DOOR_CODE: // REMOVE DOOR + (unsigned)actorat[x][y] = tilemap[x][y] = *(mapsegs[0]+farmapylookup[y]+x) = 0; + *(mapsegs[2]+farmapylookup[y+1]+x) = 0; // key no longer needed + if (keyspot>=0) + TakeKey(keyspot); + break; + + default: // WARP TO A LEVEL + newlevel = spot; + playstate = ex_warped; + break; + } + + if (playstate == ex_warped) + { + SD_PlaySound(HIT_GATESND); +// levelinfo *li=&gamestate.levels[curmap]; + +// OldAngle = FaceDoor(x,y); + + if (!VerifyGateExit()) + { + IN_ClearKeysDown (); + playstate = ex_stillplaying; + break; + } + +// FaceAngle(OldAngle); + + if (keyspot>=0) + TakeKey(keyspot); + *(mapsegs[2]+farmapylookup[y+1]+x) = 0; // key no longer needed + + gamestate.mapon = newlevel; + SD_PlaySound(WARPUPSND); + IN_ClearKeysDown (); + +// li->x = player->tilex; +// li->y = player->tiley; +// li->angle = player->angle+180; +// if (li->angle > 360) +// li->angle -= 360; + } + } + break; + } + + return true; +} + +//------------------------------------------------------------------------- +// VerifyGateExit() +//------------------------------------------------------------------------- +boolean VerifyGateExit() +{ + char choices[] = {sc_Escape,sc_Y,sc_N,0},ch; + + ch=DisplayMsg("Pass this way? Y/N",choices); + DrawText(true); + + return(ch == sc_Y); +} + + +/* +================== += += TouchActor += += Returns true if the move is blocked += +================== +*/ + +boolean TouchActor (objtype *ob, objtype *check) +{ + if (ob->xh < check->xl || ob->xl > check->xh || + ob->yh < check->yl || ob->yl > check->yh) + return false; // not quite touching + + switch (check->obclass) + { + case bonusobj: + switch (check->temp1) + { + case B_BOLT: GiveBolt (); break; + + case B_NUKE: GiveNuke (); break; + + case B_POTION: GivePotion (); break; + +// case B_RKEY2: GiveKey(B_RKEY-B_RKEY); break; + + case B_RKEY: + case B_YKEY: + case B_GKEY: + case B_BKEY: GiveKey (check->temp1-B_RKEY); break; + +#if 0 + case B_SCROLL1: + case B_SCROLL2: + case B_SCROLL3: + case B_SCROLL4: + case B_SCROLL5: + case B_SCROLL6: + case B_SCROLL7: + case B_SCROLL8: GiveScroll (check->temp1-B_SCROLL1,true); break; +#endif + + case B_CHEST: GiveChest (); break; + + case B_RGEM: + case B_YGEM: + case B_GGEM: + case B_BGEM: + case B_PGEM: + SD_PlaySound(GETGEMSND); + gamestate.gems[check->temp1-B_RGEM] = GEM_DELAY_TIME; + redraw_gems = true; + break; + + default: + Quit("TouchActor(): INVALID BONUS"); + break; + } + + (unsigned)actorat[check->tilex][check->tiley] = 0; + RemoveObj (check); + + return false; + + case freezeobj: + StopTime(); + (unsigned)actorat[check->tilex][check->tiley] = 0; + RemoveObj(check); + return(false); + + case cloudobj: + TakeDamage(2); + return false; + } + + return true; +} + + +/* +================== += += CalcBounds += +================== +*/ + +void CalcBounds (objtype *ob) +{ +// +// calculate hit rect +// + ob->xl = ob->x - ob->size; + ob->xh = ob->x + ob->size; + ob->yl = ob->y - ob->size; + ob->yh = ob->y + ob->size; +} + + +/* +=================== += += LocationInActor += +=================== +*/ + +boolean LocationInActor (objtype *ob) +{ + int x,y,xmin,ymin,xmax,ymax; + objtype *check; + + CalcBounds (ob); + + xmin = (ob->x >> TILESHIFT)-2; + ymin = (ob->y >> TILESHIFT)-2; + xmax = xmin+5; + ymax = ymin+5; + + for (x=xmin;x(objtype *)LASTTILE + && (check->flags & of_shootable) + && (check->obclass != bonusobj) + && (check->obclass != freezeobj) + && (check->obclass != solidobj) + && ob->xl-SIZE_TEST <= check->xh + && ob->xh+SIZE_TEST >= check->xl + && ob->yl-SIZE_TEST <= check->yh + && ob->yh+SIZE_TEST >= check->yl) + return true; + } + + return false; +} + +/* +=================== += += ClipXMove += += Only checks corners, so the object better be less than one tile wide! += +=================== +*/ +void ClipXMove (objtype *ob, long xmove) +{ + int xl,yl,xh,yh,tx,ty,nt1,nt2,x,y; + long intersect,basex,basey,pointx,pointy; + unsigned inside,total,tile; + objtype *check; + boolean moveok; + boolean invisible_present = false; + +// +// move player and check to see if any corners are in solid tiles +// + basex = ob->x; + basey = ob->y; + + ob->x += xmove; + + CalcBounds (ob); + + xl = ob->xl>>TILESHIFT; + yl = ob->yl>>TILESHIFT; + + xh = ob->xh>>TILESHIFT; + yh = ob->yh>>TILESHIFT; + + for (y=yl;y<=yh;y++) + for (x=xl;x<=xh;x++) + { + check = actorat[x][y]; + + if (!check) + continue; // blank floor, walk ok + + if ((unsigned)check <= LASTTILE) + { + if (TILE_FLAGS((unsigned)check) & tf_SPECIAL) + { + HitSpecialTile(x,y,(unsigned)check-SPECTILESTART); + goto blockmove; + } + + if (TILE_FLAGS((unsigned)check) & tf_INVISIBLE_WALL) + { + invisible_present = true; + goto blockmove; + } + + + if (TILE_FLAGS((unsigned)check) & tf_SOLID) + { + goto blockmove; // solid wall + } + } + + TouchActor(ob,check); // pick up items + } + +// +// check nearby actors +// + if (LocationInActor(ob)) + { + ob->x -= xmove; + if (LocationInActor(ob)) + { + ob->x += xmove; + if (LocationInActor(ob)) + ob->x -= xmove; + } + } + return; // move is OK! + + +blockmove: + +// if (!SD_SoundPlaying()) +// SD_PlaySound (HITWALLSND); + + moveok = false; + + do + { + xmove /= 2; + if (moveok) + { + ob->x += xmove; + } + else + { + ob->x -= xmove; + } + CalcBounds (ob); + xl = ob->xl>>TILESHIFT; + yl = ob->yl>>TILESHIFT; + xh = ob->xh>>TILESHIFT; + yh = ob->yh>>TILESHIFT; + if (tilemap[xl][yl] || tilemap[xh][yl] + || tilemap[xh][yh] || tilemap[xl][yh] ) + { + moveok = false; + if (xmove>=-2048 && xmove <=2048) + { + ob->x = basex; + ob->y = basey; + return; + } + } + else + if (invisible_present) + { + moveok = false; + if (xmove>=-2048 && xmove <=2048) + { + ob->x = basex; + ob->y = basey; + return; + } + } + else + if (xmove>=-2048 && xmove <=2048) + return; + moveok = true; + } while (1); +} + + +/* +=================== += += ClipYMove += += Only checks corners, so the object better be less than one tile wide! += +=================== +*/ +void ClipYMove (objtype *ob, long ymove) +{ + int xl,yl,xh,yh,tx,ty,nt1,nt2,x,y; + long intersect,basex,basey,pointx,pointy; + unsigned inside,total,tile; + objtype *check; + boolean moveok; + boolean invisible_present = false; + +// +// move player and check to see if any corners are in solid tiles +// + basex = ob->x; + basey = ob->y; + + ob->y += ymove; + + CalcBounds (ob); + + xl = ob->xl>>TILESHIFT; + yl = ob->yl>>TILESHIFT; + + xh = ob->xh>>TILESHIFT; + yh = ob->yh>>TILESHIFT; + + for (y=yl;y<=yh;y++) + for (x=xl;x<=xh;x++) + { + check = actorat[x][y]; + if (!check) + continue; // blank floor, walk ok + + if ((unsigned)check <= LASTTILE) + { + if (TILE_FLAGS((unsigned)check) & tf_SPECIAL) // <=LASTSPECIALTILE) + { + HitSpecialTile (x,y,(unsigned)check-SPECTILESTART); + goto blockmove; + } + + if (TILE_FLAGS((unsigned)check) & tf_INVISIBLE_WALL) + { + invisible_present = true; + goto blockmove; + } + + + if (TILE_FLAGS((unsigned)check) & tf_SOLID) // LASTWALLTILE) + { + goto blockmove; // solid wall + } + } + + TouchActor(ob,check); // pick up items + } + +// +// check nearby actors +// + if (LocationInActor(ob)) + { + if (LocationInActor(ob)) + { + ob->y -= ymove; + } + } + return; // move is OK! + + +blockmove: + +// if (!SD_SoundPlaying()) +// SD_PlaySound (HITWALLSND); + + moveok = false; + + do + { + ymove /= 2; + if (moveok) + { + ob->y += ymove; + } + else + { + ob->y -= ymove; + } + CalcBounds (ob); + xl = ob->xl>>TILESHIFT; + yl = ob->yl>>TILESHIFT; + xh = ob->xh>>TILESHIFT; + yh = ob->yh>>TILESHIFT; + if (tilemap[xl][yl] || tilemap[xh][yl] + || tilemap[xh][yh] || tilemap[xl][yh] ) + { + moveok = false; + if (ymove>=-2048 && ymove <=2048) + { + ob->x = basex; + ob->y = basey; + return; + } + } + else + if (invisible_present) + { + moveok = false; + if (ymove>=-2048 && ymove <=2048) + { + ob->x = basex; + ob->y = basey; + return; + } + } + else + if (ymove>=-2048 && ymove <=2048) + return; + moveok = true; + } while (1); +} + + +//========================================================================== + + +/* +=================== += += ShotClipMove += += Only checks corners, so the object better be less than one tile wide! += +=================== +*/ + +boolean ShotClipMove (objtype *ob, long xmove, long ymove) +{ + int xl,yl,xh,yh,tx,ty,nt1,nt2,x,y; + long intersect,basex,basey,pointx,pointy; + unsigned inside,total,spot,tile; + objtype *check; + boolean moveok; + +// +// move shot and check to see if any corners are in solid tiles +// + basex = ob->x; + basey = ob->y; + + ob->x += xmove; + ob->y += ymove; + + CalcBounds (ob); + + xl = ob->xl>>TILESHIFT; + yl = ob->yl>>TILESHIFT; + + xh = ob->xh>>TILESHIFT; + yh = ob->yh>>TILESHIFT; + + for (y=yl;y<=yh;y++) + for (x=xl;x<=xh;x++) + { + spot = (*(mapsegs[2]+farmapylookup[y]+x)) >> 8; + if (spot == EXP_WALL_CODE) + switch (ob->obclass) + { + case pshotobj: + case bigpshotobj: + ExplodeWall (x,y); + goto blockmove; + break; + } + + tile = *(mapsegs[0]+farmapylookup[y]+x); + if (TILE_FLAGS(tile) & tf_SOLID) + goto blockmove; + } + return false; // move is OK! + + +blockmove: + + SD_PlaySound (SHOOTWALLSND); + + moveok = false; + + do + { + xmove /= 2; + ymove /= 2; + if (moveok) + { + ob->x += xmove; + ob->y += ymove; + } + else + { + ob->x -= xmove; + ob->y -= ymove; + } + CalcBounds (ob); + xl = ob->xl>>TILESHIFT; + yl = ob->yl>>TILESHIFT; + xh = ob->xh>>TILESHIFT; + yh = ob->yh>>TILESHIFT; + if (tilemap[xl][yl] || tilemap[xh][yl] + || tilemap[xh][yh] || tilemap[xl][yh] ) + { + moveok = false; + if (xmove>=-2048 && xmove <=2048 && ymove>=-2048 && ymove <=2048) + { + ob->x = basex; + ob->y = basey; + return true; + } + } + else + { + if (xmove>=-2048 && xmove <=2048 && ymove>=-2048 && ymove <=2048) + return true; + moveok = true; + } + } while (1); +} + + + +/* +============================================================================= + + PLAYER CONTROL + +============================================================================= +*/ + + + +void T_Player (objtype *ob); + +statetype s_player = {0,0,&T_Player,&s_player}; + +/* +=============== += += SpawnPlayer += +=============== +*/ + +void SpawnPlayer (int tilex, int tiley, int dir) +{ +#if 0 + levelinfo *li=&gamestate.levels[gamestate.mapon]; + + if (li->x != -1) + { + tilex = li->x; + tiley = li->y; + player->angle = li->angle; + } + else + player->angle = (1-dir)*90; +#endif + + player->obclass = playerobj; + player->active = always; + player->tilex = tilex; + player->tiley = tiley; + player->x = ((long)tilex<y = ((long)tiley<state = &s_player; + player->size = MINDIST; + CalcBounds(player); + player->angle = (1-dir)*90; + if (player->angle<0) + player->angle += ANGLES; +} + + +/* +=================== += += Thrust += +=================== +*/ + +void Thrust (int angle, unsigned speed) +{ + long xmove,ymove; + + if (lasttimecount>>5 != ((lasttimecount-tics)>>5) ) + { + // + // walk sound + // + if (lasttimecount&32) + SD_PlaySound (WALK1SND); + else + SD_PlaySound (WALK2SND); + } + + xmove = FixedByFrac(speed,costable[angle]); + ymove = -FixedByFrac(speed,sintable[angle]); + + ClipXMove(player,xmove); + ClipYMove(player,ymove); + player->tilex = player->x >> TILESHIFT; + player->tiley = player->y >> TILESHIFT; +} + + + +/* +======================= += += ControlMovement += +======================= +*/ + +void ControlMovement (objtype *ob) +{ + int angle; + long speed; + + + if (control.button1) + { + // + // strafing + // + // + // side to side move + // + if (!mousexmove) + speed = 0; + else if (mousexmove<0) + speed = -(long)mousexmove*300; + else + speed = -(long)mousexmove*300; + + if (control.xaxis == -1) + { + speed += PLAYERSPEED*tics; + } + else if (control.xaxis == 1) + { + speed -= PLAYERSPEED*tics; + } + + if (speed > 0) + { + if (speed >= TILEGLOBAL) + speed = TILEGLOBAL-1; + angle = ob->angle + ANGLES/4; + if (angle >= ANGLES) + angle -= ANGLES; + Thrust (angle,speed); // move to left + } + else if (speed < 0) + { + if (speed <= -TILEGLOBAL) + speed = -TILEGLOBAL+1; + angle = ob->angle - ANGLES/4; + if (angle < 0) + angle += ANGLES; + Thrust (angle,-speed); // move to right + } + } + else + { + // + // not strafing + // + + // + // turning + // + if (control.xaxis == 1) + { + ob->angle -= tics; + if (running) // fast turn + ob->angle -= (tics<<1); + } + else if (control.xaxis == -1) + { + ob->angle+= tics; + if (running) // fast turn + ob->angle += (tics<<1); + } + + ob->angle -= (mousexmove/10); + + if (ob->angle >= ANGLES) + ob->angle -= ANGLES; + if (ob->angle < 0) + ob->angle += ANGLES; + + } + + // + // forward/backwards move + // + if (!mouseymove) + speed = 0; + else if (mouseymove<0) + speed = -(long)mouseymove*500; + else + speed = -(long)mouseymove*200; + + if (control.yaxis == -1) + { + speed += PLAYERSPEED*tics; + } + else if (control.yaxis == 1) + { + speed -= PLAYERSPEED*tics; + } + + if (speed > 0) + { + if (speed >= TILEGLOBAL) + speed = TILEGLOBAL-1; + Thrust (ob->angle,speed); // move forwards + } + else if (speed < 0) + { + if (speed <= -TILEGLOBAL) + speed = -TILEGLOBAL+1; + angle = ob->angle + ANGLES/2; + if (angle >= ANGLES) + angle -= ANGLES; + Thrust (angle,-speed); // move backwards + } +} + + +/* +=============== += += T_Player += +=============== +*/ + +void T_Player (objtype *ob) +{ + extern boolean autofire; + + int angle,speed,scroll,loop; + unsigned text,tilex,tiley; + long lspeed; + +// boolean radar_moved=false; + + + ControlMovement (ob); + + + // + // firing + // + if (boltsleft) + { + handheight+=(realtics<<2); + if (handheight>MAXHANDHEIGHT) + handheight = MAXHANDHEIGHT; + + ContinueBolt (); + lasthand = lasttimecount; + } + else + { + if (control.button0) + { + handheight+=(realtics<<2); + if (handheight>MAXHANDHEIGHT) + handheight = MAXHANDHEIGHT; + lasthand = lasttimecount; + + if (!button0down) + Shoot(); + + if (!autofire) + button0down=true; + } + else + { + if (lasttimecount > lasthand+HANDPAUSE) + { + handheight-=(realtics<<1); + if (handheight<0) + handheight = 0; + } + + button0down = false; + } +} + +#if 0 + if (control.button0) + { + handheight+=(realtics<<2); + if (handheight>MAXHANDHEIGHT) + handheight = MAXHANDHEIGHT; + + if ((unsigned)TimeCount/FIRETIME != lastfiretime) + BuildShotPower (); + lasthand = lasttimecount; + } + else + { + if (lasttimecount > lasthand+HANDPAUSE) + { + handheight-=(realtics<<1); + if (handheight<0) + handheight = 0; + } + + if (gamestate.shotpower) + { + lastfiretime = (unsigned)TimeCount/FIRETIME; + Shoot (); + } + } + } +#endif + + // + // special actions + // + + if ((Keyboard[sc_Space] || Keyboard[sc_C]) && gamestate.body != MAXBODY) + DrinkPotion (); + + if (Keyboard[sc_Z] && !boltsleft) + CastBolt (); + + if ( (Keyboard[sc_Enter] || Keyboard[sc_X]) && ((TimeCount-lastnuke > NUKETIME) || (autofire))) + CastNuke (); + +#if 0 + scroll = LastScan-2; + if ( scroll>=0 && scrollx>>16l),(player->y>>16l)-y); + FreezeTime = StopTime; + + diff = player->angle - RotateAngle; + + if (((diff>0) && (diff<180)) || ((diff<0) && (diff>-180))) + RotateSpeed = -ROTATE_SPEED; + else + RotateSpeed = ROTATE_SPEED; +} +#endif + +#if 0 +//------------------------------------------------------------------------ +// CalcAngle() - +// +// DESC: Calculates the angle from a given dy & dx +//------------------------------------------------------------------------ +short CalcAngle(short dx,short dy) +{ + #define degtorad (180/PI) + float angle; + short diff; + float rad_angle; + + if (dx) + { + angle = atan((float)dy/dx)* degtorad; + if (angle<=0) + angle += 180; + if (dy>0) + angle += 180; + } + else + { + // 90 Deg shift + + if (dy < 0) + angle = 0 + 90; // Above player (NORTH) + else + angle = 180 + 90; // Below player (SOUTH) + } + + if (!angle) // HACK + angle++; + + return((short)abs(angle)); +} + +#endif + +#if 0 + +//------------------------------------------------------------------------- +// RotateView() - +// +// DESC : Rotates view (current view of game) to a dest angle. +//------------------------------------------------------------------------- +void RotateView() +{ + short LastPos; + + // Store old angle position then change angle... + // + + LastPos = player->angle; + + player->angle += RotateSpeed; + + // Check to see if we cranked past out dest angle... + // + + + if ((player->angle>ANGLES) || (!player->angle)) + player->angle = 1; + else + if (player->angle<1) + player->angle = ANGLES; + + // Check to see if we over shot our dest angle... + // + + if (((LastPos < RotateAngle) && (player->angle > RotateAngle) && (RotateSpeed > 0)) || + ((LastPos > RotateAngle) && (player->angle < RotateAngle) && (RotateSpeed < 0))) + player->angle = RotateAngle; + + // Check for ending force turn.... + // + + if (player->angle == RotateAngle) + RotateAngle = -1; + +} + + +//-------------------------------------------------------------------------- +// InitRotate() +//-------------------------------------------------------------------------- +void InitRotate(short DestAngle) +{ + if (player->angle != DestAngle) + { + RotateAngle = DestAngle; + + if (player->angle > DestAngle) + RotateSpeed = -ROTATE_SPEED; + else + RotateSpeed = ROTATE_SPEED; + + if (abs(player->angle - RotateAngle) > 180) + RotateSpeed =- RotateSpeed; + } +} + + + +//------------------------------------------------------------------------ +// FaceAngle() - +// +// PARAMS : DestAngle - Destination angle to turn to +//------------------------------------------------------------------------ +void FaceAngle(short DestAngle) +{ + signed long dx,dy,radius,psin,pcos,newx,newy; + int give; + short objnum,LastPos; + signed long ox,oy,xl,xh,yl,yh,px,py,norm_dx,norm_dy; + short o_radius; + void (*think)(); + + + // Calculate the direction we want to turn to... + // + + InitRotate(DestAngle); + + RedrawStatusWindow(); + + while (RotateAngle != -1) + { + + RotateView(); + +// PollControls(); + + objnum=0; + + for (obj = player;obj;obj = obj->next) + { + if (obj->active >= yes) + { + + // keep a list of objects around the player for radar updates + // + if (obj == player) + { + px = player->x; + py = player->y; + psin = sintable[player->angle]; + pcos = costable[player->angle]; + xl = px-((long)RADAR_WIDTH< MAX_RADAR_BLIPS-2) + objnum = MAX_RADAR_BLIPS-2; + + ox = obj->x; + oy = obj->y; + + + if ((ox >= xl) && (ox <= xh) && (oy >= yl) && (oy <= yh)) + { + norm_dx = (dx = px-ox)>>TILESHIFT; + norm_dy = (dy = oy-py)>>TILESHIFT; + + o_radius = IntSqrt((norm_dx * norm_dx) + (norm_dy * norm_dy)); + + if (o_radius < RADAR_RADIUS) + { + newx = FixedByFrac(dy,pcos)-FixedByFrac(dx,psin); + newy = FixedByFrac(dy,psin)+FixedByFrac(dx,pcos); + + RadarXY[objnum][0]=newx>>TILESHIFT; + RadarXY[objnum][1]=newy>>TILESHIFT; + + // Define color to use for this object... + // + + switch (obj->obclass) + { + case playerobj: + RadarXY[objnum++][2]=15; + break; + + // RED GEM + // + case godessobj: + if (gamestate.gems[B_RGEM-B_RGEM]) + if (obj->active == always) + RadarXY[objnum++][2]=12; + break; + + // GREEN GEM + // + case fatdemonobj: + if (gamestate.gems[B_GGEM-B_RGEM]) + if (obj->active == always) + RadarXY[objnum++][2]=10; + break; + + // YELLOW GEM + // + case skeletonobj: + if (gamestate.gems[B_YGEM-B_RGEM]) + if (obj->active == always) + RadarXY[objnum++][2]=14; + break; + + // BLUE GEM + // + case mageobj: + case wetobj: + if (gamestate.gems[B_BGEM-B_RGEM]) + if (obj->active == always) + RadarXY[objnum++][2]=9; + break; + + // PURPLE GEM + // + case zombieobj: + if (gamestate.gems[B_PGEM-B_RGEM]) + if (obj->active == always) + RadarXY[objnum++][2]=13; + break; + } + } + } + } + } + + RadarXY[objnum][2]=-1; // Signals end of RadarXY list... + +// refresh all +// + + ThreeDRefresh(); + DrawRadar(); + EGAWRITEMODE(0); + DrawNSEWIcons(); + +// CheckKeys(); + } +} + + +//------------------------------------------------------------------------- +// FaceDoor() - Turns the player to face a door (a tile) at a given TILE x & y +// +// RETURNS : Returns the orginal angle of the player. +//------------------------------------------------------------------------ +short FaceDoor(short x, short y) +{ + short p_x,p_y,angle,old_angle; + + old_angle = player->angle; + + p_x = player->x>>16l; + p_y = player->y>>16l; + + if (p_x != x) + { + if (p_x > x) + angle = 180; // Face Left + else + angle = 1; // Face Right + } + + if (p_y != y) + { + if (p_y > y) + angle = 90; // Face Up + else + angle = 270; // Face Down + } + + FaceAngle(angle); + + return(old_angle); +} + + +#endif + + + +/*========================================================================== + + EXPLOSION SPAWNING ROUTINES + +===========================================================================*/ + +statetype s_explode = {0,1,T_ExpThink,&s_explode}; + +//------------------------------------------------------------------------- +// SpawnExplosion() +//------------------------------------------------------------------------ +void SpawnExplosion(fixed x, fixed y, short Delay) +{ + DSpawnNewObjFrac(x,y,&s_explode,PIXRADIUS*7); + new->obclass = expobj; + new->active = always; + new->temp1 = Delay; +} + + +//--------------------------------------------------------------------------- +// T_ExpThink() +//--------------------------------------------------------------------------- +void T_ExpThink(objtype *obj) +{ + if (obj->temp1) + { + if ((obj->temp1-=realtics) <= 0) + obj->temp1 = 0; + } + else + { + obj->state = &s_pshot_exp1; + obj->ticcount = obj->state->tictime; + SD_PlaySound(BOOMSND); + } +} + + + +//------------------------------------------------------------------------- +// SpawnBigExplosion() +//------------------------------------------------------------------------ +void SpawnBigExplosion(fixed x, fixed y, short Delay, fixed Range) +{ + SpawnExplosion(x-random(Range),y+random(Range),random(Delay)); + SpawnExplosion(x+random(Range),y-random(Range),random(Delay)); + SpawnExplosion(x-random(Range),y-random(Range),random(Delay)); + SpawnExplosion(x+random(Range),y+random(Range),random(Delay)); +} + diff --git a/16/cawat/COPYING b/16/cawat/COPYING new file mode 100644 index 00000000..d159169d --- /dev/null +++ b/16/cawat/COPYING @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/16/cawat/DEF.H b/16/cawat/DEF.H new file mode 100644 index 00000000..63c5a055 --- /dev/null +++ b/16/cawat/DEF.H @@ -0,0 +1,865 @@ +/* Catacomb Armageddon Source Code + * Copyright (C) 1993-2014 Flat Rock Software + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "ID_HEADS.H" +#include +#include + +//#define PROFILE + +#define DEBUG_KEYS_AVAILABLE 0 + +/* +============================================================================= + + GLOBAL CONSTANTS + +============================================================================= +*/ + +// +// SOFTLIB GFX FILENAME +// +#define SLIB_GFX "ARM_SLIB."EXT + + +#define USE_INERT_LIST false + + +// +// MAX_BASE - represents 100 percent in 1st base +// MAX_PERC - represents 100 percent in 2nd base +// PERC - fractional portion of 2nd base +// SCALE - arbitrary scaling value (bigger number means more accurate) +// +// ex: PERCENTAGE(320,16,8,7) returns 160 +// +// Make sure values used won't overflow a WORD! In general, if largest number +// to be used (320 in ex: above) * (1<>SCALE) +#define LONG_PERCENTAGE(MAX_BASE,MAX_PERC,PERC,SCALE) (((long)MAX_BASE*(((long)PERC<>SCALE) + +#define PI 3.141592657 + +//#define SIZE_TEST 65000 +#define SIZE_TEST 0 + + +#define FL_QUICK 0x01 +#define FL_NOMEMCHECK 0x02 +#define FL_HELPTEST 0x04 + +#define FL_CLEAR (FL_QUICK|FL_NOMEMCHECK|FL_HELPTEST) + +#if 0 +#define GEM_SHIFT 2 +#define FL_RGEM 0x04 +#define FL_GGEM 0x08 +#define FL_BGEM 0x10 +#define FL_YGEM 0x20 +#define FL_PGEM 0x40 +#endif + +#define FL_DEAD 0x80 + + + +#define MAXBOLTS 10 +#define MAXNUKES 10 +#define MAXPOTIONS 10 + +#define NUKE_COST (1000) +#define BOLT_COST (1200) +#define POTION_COST (1300) + +#define NUKE_COST_TXT ("1000") // Allows for Q&D positioning.. +#define BOLT_COST_TXT ("1200") +#define POTION_COST_TXT ("1300") + +#define RADARX 31 // bytes +#define RADARY 11 // pixels +#define RADAR_WIDTH 51 // " +#define RADAR_HEIGHT 51 // " +#define RADAR_XCENTER ((RADARX*8)+(RADAR_WIDTH/2)+3) // " +#define RADAR_YCENTER ((RADARY-8)+(RADAR_HEIGHT/2)+5) // " +#define MAX_RADAR_BLIPS 60 + + +#define RADAR_RADIUS 17 +#define RADAR_RADIUS_NSEW 15 +#define RADAR_X_IRADIUS (113/5) +#define RADAR_Y_IRADIUS (113/7) +#define RADAR_ICON_CENTER 4 // Center offset into icon. + +#define NAMESTART 180 +#define REMOVED_DOOR_TILE NAMESTART + +#define NEXT_LEVEL_CODE 0xff +#define REMOVE_DOOR_CODE 0xfe +#define CANT_OPEN_CODE 0xfd +#define EXP_WALL_CODE 0xfc + + +#define UNMARKGRCHUNK(chunk) (grneeded[chunk]&=~ca_levelbit) + +#define MOUSEINT 0x33 + +#define EXPWALLSTART 8 +#define NUMEXPWALLS 7 +#define WALLEXP 59 +#define WATEREXP 62 +#define NUMFLOORS 80 //71 + +#define NUMLATCHPICS (FIRSTWALLPIC-FIRSTLATCHPIC) //+5 +#define NUMSCALEPICS (FIRSTWALLPIC-FIRSTSCALEPIC) //+5 +#define NUMSCALEWALLS (LASTWALLPIC-FIRSTWALLPIC) //+5 + + +#define FLASHCOLOR 12 +#define FLASHTICS 4 + + +#define NUMLEVELS 32 //21 + +#define VIEWX 0 // corner of view window +#define VIEWY 0 +#define VIEWWIDTH (40*8) // size of view window // 33 +#define VIEWHEIGHT (15*8) // 18 +#define VIEWXH (VIEWX+VIEWWIDTH-1) +#define VIEWYH (VIEWY+VIEWHEIGHT-1) + +#define CENTERX (VIEWX+VIEWWIDTH/2-1) // middle of view window +#define CENTERY (VIEWY+VIEWHEIGHT/2-1) + +#define GLOBAL1 (1l<<16) +#define TILEGLOBAL GLOBAL1 +#define TILESHIFT 16l + +#define MINDIST (2*GLOBAL1/5) +#define FOCALLENGTH (TILEGLOBAL) // in global coordinates + +#define ANGLES 360 // must be divisable by 4 + +#define MAPSIZE 64 // maps are 64*64 max +#define MAXACTORS 100 // max number of tanks, etc / map + +#define NORTH 0 +#define EAST 1 +#define SOUTH 2 +#define WEST 3 + +#define SIGN(x) ((x)>0?1:-1) +#define ABS(x) ((int)(x)>0?(x):-(x)) +#define LABS(x) ((long)(x)>0?(x):-(x)) + +#define MAXSCALE (VIEWWIDTH/2) + + +#define MAXBODY 100 +#define MAXSHOTPOWER 56 + +#define SCREEN1START 0 +#define SCREEN2START 8320 + +#define STATUSLEN 0xc80 +#define PAGELEN 0x1700 + +#define PAGE1START STATUSLEN +#define PAGE2START (PAGE1START+PAGELEN) +#define PAGE3START (PAGE2START+PAGELEN) +#define FREESTART (PAGE3START+PAGELEN) + +#define PIXRADIUS 512 + +#define STATUSLINES (200-VIEWHEIGHT) + +enum bonusnumbers {B_BOLT,B_NUKE,B_POTION,B_RKEY,B_YKEY,B_GKEY,B_BKEY,B_SCROLL1, + B_SCROLL2,B_SCROLL3,B_SCROLL4,B_SCROLL5,B_SCROLL6,B_SCROLL7,B_SCROLL8, + B_GOAL,B_CHEST,B_RGEM,B_GGEM,B_BGEM,B_YGEM,B_PGEM}; + + +#define MAX_DOOR_STORAGE 5 + +#define GEM_DELAY_TIME (120*60) + +#define ROTATE_SPEED (6) + +#define WALL_SKELETON_CODE 6 + +#define MAXREALTICS (2*60) + +#define MAXFREEZETIME (100*30) // 50 secs (100 half) + +#define INVISIBLEWALL 0x46 + +#define USE_STRIPS FALSE + +/* +============================================================================= + + GLOBAL TYPES + +============================================================================= +*/ + +enum {BLANKCHAR=9,BOLTCHAR,NUKECHAR,POTIONCHAR,KEYCHARS,SCROLLCHARS=17, + NUMBERCHARS=25}; + +typedef long fixed; + +typedef struct {int x,y;} tilept; +typedef struct {fixed x,y;} globpt; + +typedef struct +{ + int x1,x2,leftclip,rightclip;// first pixel of wall (may not be visable) + unsigned height1,height2,color,walllength,side; + long planecoord; +} walltype; + +typedef enum + {nothing,playerobj,bonusobj,succubusobj,batobj,skeletonobj,fatdemonobj,godessobj, + mageobj,pshotobj,bigpshotobj,mshotobj,inertobj,bounceobj,grelmobj,sshotobj, + gateobj,zombieobj,antobj,wetobj,expobj,eyeobj,wallskelobj,eshotobj,treeobj, + gshotobj,reddemonobj,freezeobj,solidobj,cloudobj,dshotobj,hbunnyobj,bunnyobj, + realsolidobj} classtype; + +typedef enum {north,east,south,west,northeast,southeast,southwest, + northwest,nodir} dirtype; // a catacombs 2 carryover + +typedef struct statestruct +{ + int shapenum; + int tictime; + void (*think) (); + struct statestruct far *next; +} statetypestruct; + +#define statetype statetypestruct far + +#define of_shootable 0x01 +#define of_damagedone 0x02 +#define of_forcefield 0x40 // defines a solid object as a forcefield??????????? +#define of_tree 0x80 // used to identify between a tree and a statue -- + // last minute changes for Greg + +typedef struct objstruct +{ + + int ticcount; // + statetype *state; // THESE MEMBERS MUST BE IN THE SAME + fixed x,y; // ORDER AS THE MEMBERS DEFINED IN + int viewx; // IOBJSTRUCT OR ALL HELL WILL BREAK + unsigned tilex,tiley; // LOOSE!! + unsigned viewheight; // + unsigned size; // + struct objstruct *next; // + + + struct objstruct *prev; + enum {no,noalways,yes,always} active; + classtype obclass; + + unsigned char flags; + + long distance; + dirtype dir; + + int angle; + int hitpoints; + long speed; + + fixed xl,xh,yl,yh; // hit rectangle + + int temp1,temp2; +} objtype; + +#if USE_INERT_LIST + +#define MAXINERTOBJ 20 + +typedef struct iobjstruct { + int ticcount; + statetype *state; + fixed x,y; + int viewx; + unsigned tilex,tiley; + unsigned viewheight; + unsigned size; + struct iobjstruct *next; +} inertobjtype; + +#endif + + +typedef enum {ex_stillplaying,ex_died,ex_warped,ex_resetgame + ,ex_loadedgame,ex_victorious,ex_turning,ex_abort} exittype; + + +typedef enum {S_NONE, S_HELP, S_SND, S_SAVING, S_RESTORING, + S_JOYSTICK, S_RETREAT, S_ADVANCE, S_SIDESTEP, S_QTURN, + S_MISSLE, S_ZAPPER, S_XTER, S_CURING, S_READ, + S_VIEWING, S_ITEMDES, S_DAMAGE, S_TURN, S_TIMESTOP} status_flags; + +typedef struct { + char x,y; + unsigned ondoor,underdoor; +} doorinfo; + +typedef struct { + char x,y; + short angle; + doorinfo doors[MAX_DOOR_STORAGE]; +} levelinfo; + +typedef struct +{ + int difficulty; + int mapon; + int bolts,nukes,potions,keys[4],scrolls[8]; + + int gems[5]; // "int allgems[5]" is used for 1:1 comparison + // in play loop for radar... CHANGE IT, TOO! + + long score; + int body,shotpower; + short mapwidth,mapheight; +// levelinfo levels[NUMLEVELS]; +} gametype; + +/* +============================================================================= + + C3_MAIN DEFINITIONS + +============================================================================= +*/ + +extern char inlevel[][2]; +extern char str[80],str2[20]; +extern unsigned tedlevelnum; +extern boolean tedlevel; +extern gametype gamestate; +extern exittype playstate; +extern char SlowMode; +extern unsigned Flags; +extern boolean LoadShapes; +extern boolean EASYMODEON; + + +void NewGame (void); +boolean SaveTheGame(int file); +boolean LoadTheGame(int file); +void ResetGame(void); +void ShutdownId (void); +void InitGame (void); +void Quit (char *error, ...); +void TEDDeath(void); +void DemoLoop (void); +void SetupScalePic (unsigned picnum); +void SetupScaleWall (unsigned picnum); +void SetupScaling (void); +void main (void); +void Display320(void); +void Display640(void); +void PrintHelp(void); + +/* +============================================================================= + + C3_GAME DEFINITIONS + +============================================================================= +*/ + +extern unsigned latchpics[NUMLATCHPICS]; +extern unsigned tileoffsets[NUMTILE16]; +extern unsigned textstarts[27]; + + +#define L_CHARS 0 +#define L_NOSHOT 1 +#define L_SHOTBAR 2 +#define L_NOBODY 3 +#define L_BODYBAR 4 + + +void ScanInfoPlane (void); +void ScanText (void); +void SetupGameLevel(void); +void Victory (boolean playsounds); +void Died (void); +void NormalScreen (void); +void DrawPlayScreen (void); +void LoadLatchMem (void); +void FizzleFade (unsigned source, unsigned dest, + unsigned width,unsigned height, boolean abortable); +void FizzleOut (int showlevel); +void FreeUpMemory (void); +void GameLoop (void); + + +/* +============================================================================= + + C3_PLAY DEFINITIONS + +============================================================================= +*/ + +#define BGF_NIGHT 0x01 // it is officially night +#define BGF_NOT_LIGHTNING 0x02 // lightning flash has ended + +extern byte BGFLAGS,bcolor; + +extern unsigned *skycolor,*groundcolor; + +extern ControlInfo control; +extern boolean running,slowturn; + +extern int bordertime; + +extern byte tilemap[MAPSIZE][MAPSIZE]; +extern objtype *actorat[MAPSIZE][MAPSIZE]; +extern byte spotvis[MAPSIZE][MAPSIZE]; + +extern objtype objlist[MAXACTORS],*new,*obj,*player; + +extern unsigned farmapylookup[MAPSIZE]; +extern byte *nearmapylookup[MAPSIZE]; +extern byte update[]; + +extern boolean godmode,singlestep; +extern int extravbls; + +extern int mousexmove,mouseymove; +extern int pointcount,pointsleft; +extern status_flags status_flag; +extern int status_delay; + +extern objtype dummyobj; +extern short BeepTime; +extern unsigned scolor,gcolor; + +void CenterWindow(word w,word h); +void DebugMemory (void); +void PicturePause (void); +int DebugKeys (void); +void CheckKeys (void); +void InitObjList (void); +void GetNewObj (boolean usedummy); +void RemoveObj (objtype *gone); +void PollControlls (void); +void PlayLoop (void); +void InitBgChange(short stimer, unsigned *scolors, short gtimer, unsigned *gcolors, byte flag); + +void DisplayStatus (status_flags *stat_flag); + +/* +============================================================================= + + C3_STATE DEFINITIONS + +============================================================================= +*/ + +//void SpawnNewObj (unsigned x, unsigned y, statetype *state, unsigned size); +//void SpawnNewObjFrac (long x, long y, statetype *state, unsigned size); + +void Internal_SpawnNewObj (unsigned x, unsigned y, statetype *state, unsigned size, boolean UseDummy, boolean PutInActorat); +void Internal_SpawnNewObjFrac (long x, long y, statetype *state, unsigned size,boolean UseDummy); + +#define DSpawnNewObj(x, y, state, size) Internal_SpawnNewObj(x,y,state,size,true,true) +#define SpawnNewObj(x, y, state, size) Internal_SpawnNewObj(x,y,state,size,false,true) +#define ASpawnNewObj(x, y, state, size) Internal_SpawnNewObj(x,y,state,size,false,false) +#define SpawnNewObjFrac(x, y, state, size,Dummy) Internal_SpawnNewObjFrac(x, y, state, size,false) +#define DSpawnNewObjFrac(x, y, state, size) Internal_SpawnNewObjFrac(x, y, state, size,true) + +boolean CheckHandAttack (objtype *ob); +void T_DoDamage (objtype *ob); +boolean Walk (objtype *ob); +void ChaseThink (objtype *obj, boolean diagonal); +void MoveObj (objtype *ob, long move); +boolean Chase (objtype *ob, boolean diagonal); + +extern dirtype opposite[9]; + +/* +============================================================================= + + C3_TRACE DEFINITIONS + +============================================================================= +*/ + +int FollowTrace (fixed tracex, fixed tracey, long deltax, long deltay, int max); +int BackTrace (int finish); +void ForwardTrace (void); +int FinishWall (void); +void InsideCorner (void); +void OutsideCorner (void); +void FollowWalls (void); + +extern boolean aborttrace; + +/* +============================================================================= + + C3_DRAW DEFINITIONS + +============================================================================= +*/ + +#define MAXWALLS 50 +#define DANGERHIGH 45 + +#define MIDWALL (MAXWALLS/2) + +//========================================================================== + +extern tilept tile,lasttile,focal,left,mid,right; + +extern globpt edge,view; + +extern unsigned screenloc[3]; +extern unsigned freelatch; + +extern int screenpage; + +extern boolean fizzlein; + +extern long lasttimecount; + +extern int firstangle,lastangle; + +extern fixed prestep; + +extern int traceclip,tracetop; + +extern fixed sintable[ANGLES+ANGLES/4],*costable; + +extern fixed viewx,viewy,viewsin,viewcos; // the focal point +extern int viewangle; + +extern fixed scale,scaleglobal; +extern unsigned slideofs; + +extern int zbuffer[VIEWXH+1]; + +extern walltype walls[MAXWALLS],*leftwall,*rightwall; + + +extern fixed tileglobal; +extern fixed focallength; +extern fixed mindist; +extern int viewheight; +extern fixed scale; + +extern int far walllight1[NUMFLOORS]; +extern int far walldark1[NUMFLOORS]; + +extern unsigned topcolor,bottomcolor; + +extern char wall_anim_info[NUMFLOORS]; +extern char wall_anim_pos[NUMFLOORS]; + +//========================================================================== + +void DrawLine (int xl, int xh, int y,int color); +void DrawWall (walltype *wallptr); +void TraceRay (unsigned angle); +fixed FixedByFrac (fixed a, fixed b); +void TransformPoint (fixed gx, fixed gy, int *screenx, unsigned *screenheight); +fixed TransformX (fixed gx, fixed gy); +int FollowTrace (fixed tracex, fixed tracey, long deltax, long deltay, int max); +void ForwardTrace (void); +int FinishWall (void); +int TurnClockwise (void); +int TurnCounterClockwise (void); +void FollowWall (void); + +void NewScene (void); +void BuildTables (void); + + +/* +============================================================================= + + C3_SCALE DEFINITIONS + +============================================================================= +*/ + + +#define COMPSCALECODESTART (65*6) // offset to start of code in comp scaler + +typedef struct +{ + unsigned codeofs[65]; + unsigned start[65]; + unsigned width[65]; + byte code[]; +} t_compscale; + +typedef struct +{ + unsigned width; + unsigned codeofs[64]; +} t_compshape; + + +extern unsigned scaleblockwidth, + scaleblockheight, + scaleblockdest; + +extern byte plotpix[8]; +extern byte bitmasks1[8][8]; +extern byte bitmasks2[8][8]; + + +extern t_compscale _seg *scaledirectory[NUMSCALEPICS]; +extern t_compshape _seg *shapedirectory[NUMSCALEPICS]; +extern memptr walldirectory[NUMSCALEWALLS]; +extern unsigned shapesize[NUMSCALEPICS]; + +void DeplanePic (int picnum); +void ScaleShape (int xcenter, t_compshape _seg *compshape, unsigned scale); +unsigned BuildCompShape (t_compshape _seg **finalspot); + + +/* +============================================================================= + + C3_ASM DEFINITIONS + +============================================================================= +*/ + +extern unsigned wallheight [VIEWWIDTH]; +extern unsigned wallwidth [VIEWWIDTH]; +extern unsigned wallseg [VIEWWIDTH]; +extern unsigned wallofs [VIEWWIDTH]; +extern unsigned screenbyte [VIEWWIDTH]; +extern unsigned screenbit [VIEWWIDTH]; +extern unsigned bitmasks [64]; + +extern long wallscalecall; + +void ScaleWalls (void); + +/* +============================================================================= + + C3_WIZ DEFINITIONS + +============================================================================= +*/ + +#define MAXHANDHEIGHT 72 + +extern statetype s_pshot_exp1; +extern statetype s_pshot_exp2; +extern statetype s_pshot_exp3; + +extern long lastnuke; +extern int lasttext; +extern int handheight; +extern int boltsleft,bolttimer; +extern short RadarXY[][3]; + +extern short RotateAngle; +extern short FreezeTime; + +//void FaceDir(short x, short y, boolean StopTime); +//short CalcAngle(short dx, short dy); + +void FaceAngle(short DestAngle); +void RotateView(); +void InitRotate(short DestAngle); +short FaceDoor(short x, short y); + +char DisplayMsg(char *text,char *choices); +char DisplaySMsg(char *text,char *choices); + +extern statetype s_explode; + +void SpawnExplosion(fixed x, fixed y,short Delay); +void T_ExpThink(objtype *obj); +void SpawnBigExplosion(fixed x, fixed y, short Delay, fixed Range); + + +/* +============================================================================= + + C3_ACT1 DEFINITIONS + +============================================================================= +*/ + +int EasyHitPoints(int NrmHitPts); +int EasyDoDamage(int Damage); + +#define zombie_mode ob->temp1 +#define zombie_delay ob->temp2 + +enum zombie_modes {zm_wait_for_dark,zm_wait_to_rise,zm_active}; + +enum eye_modes {em_other1,em_player1,em_other2,em_player2,em_other3,em_player3,em_other4,em_player4,em_dummy}; + +#define MSHOTDAMAGE 2 +#define MSHOTSPEED 10000 + +#define ESHOTDAMAGE 1 +#define ESHOTSPEED 5000 + +#define SSHOTDAMAGE 3 +#define SSHOTSPEED 6500 + +#define RANDOM_ATTACK 20 + +extern dirtype dirtable[]; +extern short other_x[], other_y[]; +extern short zombie_base_delay; + +extern statetype s_fatdemon_ouch; +extern statetype s_fatdemon_blowup1; + +extern statetype s_succubus_ouch; +extern statetype s_succubus_death1; + +extern statetype s_godessouch; +extern statetype s_godessdie1; + +extern statetype s_mageouch; +extern statetype s_magedie1; + +extern statetype s_grelouch; +extern statetype s_greldie1; + +extern statetype s_batdie1; + +extern statetype s_zombie_death1; +extern statetype s_zombie_ouch; + +extern statetype s_zombie_rise1; +extern statetype s_zombie_rise2; +extern statetype s_zombie_rise3; +extern statetype s_zombie_rise4; + +extern statetype s_ant_wait; +extern statetype s_ant_egg; +extern statetype s_ant_walk1; +extern statetype s_ant_walk2; +extern statetype s_ant_walk3; +extern statetype s_ant_attack1; +extern statetype s_ant_pause; +extern statetype s_ant_ouch; +extern statetype s_ant_die1; +extern statetype s_ant_die2; +extern statetype s_ant_die3; + +extern statetype s_skel_pause; +extern statetype s_skel_1; +extern statetype s_skel_2; +extern statetype s_skel_3; +extern statetype s_skel_4; +extern statetype s_skel_attack1; +extern statetype s_skel_attack2; +extern statetype s_skel_attack3; +extern statetype s_skel_ouch; +extern statetype s_skel_die1; +extern statetype s_skel_die2; +extern statetype s_skel_die3; + +extern statetype s_wet_pause; + +extern statetype s_wet_bubbles1; +extern statetype s_wet_bubbles2; +extern statetype s_wet_bubbles3; +extern statetype s_wet_bubbles4; + +extern statetype s_wet_peek; + +extern statetype s_wet_rise1; +extern statetype s_wet_rise2; +extern statetype s_wet_rise3; +extern statetype s_wet_rise4; +extern statetype s_wet_rise5; + +extern statetype s_wet_sink1; +extern statetype s_wet_sink2; +extern statetype s_wet_sink3; + +extern statetype s_wet_walk1; +extern statetype s_wet_walk2; +extern statetype s_wet_walk3; +extern statetype s_wet_walk4; + +extern statetype s_wet_attack1; +extern statetype s_wet_attack2; +extern statetype s_wet_attack3; +extern statetype s_wet_attack4; + +extern statetype s_wet_ouch; + +extern statetype s_wet_die1; +extern statetype s_wet_die2; +extern statetype s_wet_die3; +extern statetype s_wet_die4; +extern statetype s_wet_die5; + +extern statetype s_obj_gate1; +extern statetype s_obj_gate2; +extern statetype s_obj_gate3; +extern statetype s_obj_gate4; + +extern statetype s_eye_pause; + +extern statetype s_eye_1; +extern statetype s_eye_2; +extern statetype s_eye_3; +extern statetype s_eye_4; + +extern statetype s_eye_ouch; +extern statetype s_eye_ouch2; + +extern statetype s_eye_die1; +extern statetype s_eye_die2; +extern statetype s_eye_die3; + +extern statetype s_mshot1; +extern statetype s_mshot2; + +extern statetype s_bonus_die; + +extern statetype s_red_demonouch; +extern statetype s_red_demondie1; + +extern statetype s_bunny_death1; +extern statetype s_bunny_ouch; + +extern statetype s_tree_death1; +extern statetype s_tree_ouch; + +extern statetype s_force_field_die; \ No newline at end of file diff --git a/16/cawat/GELIB.C b/16/cawat/GELIB.C new file mode 100644 index 00000000..5e0bd6f9 --- /dev/null +++ b/16/cawat/GELIB.C @@ -0,0 +1,2876 @@ +/* Catacomb Armageddon Source Code + * Copyright (C) 1993-2014 Flat Rock Software + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include +#include +#include +#include +#include "mem.h" +#include "string.h" +#include "time.h" +#include "stdarg.h" +#include "io.h" + +#include "DEF.H" +#include "gelib.h" +#include "sl_file.h" + + +#define MAX_GAMELIST_NAMES 20 +#define FNAME_LEN 9 + +//////////////////////////////////////////////////////////////////////////// +// +// Global variables +// +boolean InLoadSaveGame = false; +//AudioDeviceType ge_DigiMode; +boolean ConserveMemory = false; +char GameListNames[MAX_GAMELIST_NAMES+1][FNAME_LEN],current_disk=1; +short NumGames; +short PPT_LeftEdge=0,PPT_RightEdge=320; +boolean LeaveDriveOn=false,ge_textmode=true; +char Filename[FILENAME_LEN+1], ID[sizeof(GAMENAME)], VER[sizeof(SAVEVER_DATA)]; +short wall_anim_delay,wall_anim_time = 7; +BufferedIO lzwBIO; + + + + + +//////////////////////////////////////////////////////////////////////////// +// +// CalibrateJoystick() +// +void CalibrateJoystick(short joynum) +{ + word minx,maxx, + miny,maxy; + + IN_ClearKeysDown(); + + VW_HideCursor(); + + VW_FixRefreshBuffer(); + CenterWindow(30,8); + + US_Print("\n"); + US_CPrintLine("Move joystick to the upper-left"); + US_CPrintLine("and press one of the buttons."); + VW_UpdateScreen(); + + while ((LastScan != sc_Escape) && !IN_GetJoyButtonsDB(joynum)); + if (LastScan == sc_Escape) + return; + + IN_GetJoyAbs(joynum,&minx,&miny); + while (IN_GetJoyButtonsDB(joynum)); + + US_Print("\n"); + US_CPrintLine("Move joystick to the lower-right"); + US_CPrintLine("and press one of the buttons."); + VW_UpdateScreen(); + + while ((LastScan != sc_Escape) && !IN_GetJoyButtonsDB(joynum)); + if (LastScan == sc_Escape) + return; + + IN_GetJoyAbs(joynum,&maxx,&maxy); + if ((minx == maxx) && (miny == maxy)) + return; + + IN_SetupJoy(joynum,minx,maxx,miny,maxy); + + while (IN_GetJoyButtonsDB(joynum)); + if (LastScan) + IN_ClearKeysDown(); + + JoystickCalibrated = true; +} + +//////////////////////////////////////////////////////////////////////////// +// +// WaitKeyVBL() +// +void WaitKeyVBL(short key, short vbls) +{ + while (vbls--) + { + VW_WaitVBL(1); + IN_ReadControl(0,&control); + if ((control.button0|control.button1)||(Keyboard[key])) + break; + } +} + +//////////////////////////////////////////////////////////////////////////// +// +// MoveScreen() +// +// panadjust must be saved and restored if MoveScreen is being called from +// inside a level. +// +void MoveScreen(short x, short y) +{ + unsigned address; + + address = (y*linewidth)+(x/8); + VW_SetScreen(address,0); + bufferofs = displayofs = address; + panadjust=0; +} + +//////////////////////////////////////////////////////////////////////////// +// +// MoveGfxDst() +// +void MoveGfxDst(short x, short y) +{ + unsigned address; + + address = (y*linewidth)+(x/8); + bufferofs = displayofs = address; +} + +#if 0 + +#if GRAPHIC_PIRATE + +/////////////////////////////////////////////////////////////////////////// +// +// DoPiracy() - Graphics piracy code... +// +void DoPiracy() +{ + struct Shape Pirate1Shp; + struct Shape Pirate2Shp; + + VW_SetScreenMode (EGA320GR); + VW_ClearVideo(BLACK); + + // Load shapes... + // + if (LoadShape("PIRATE1E."EXT,&Pirate1Shp)) + TrashProg("Can't load PIRATE1E.BIO"); + + if (LoadShape("PIRATE2E."EXT,&Pirate2Shp)) + TrashProg("Can't load PIRATE2E.BIO"); + + // Deal with shapes... + // + VW_SetLineWidth(40); + + VW_FadeOut(); + + MoveScreen(0,0); + UnpackEGAShapeToScreen(&Pirate1Shp,(linewidth-Pirate1Shp.BPR)<<2,0); + + MoveScreen(0,200); + UnpackEGAShapeToScreen(&Pirate2Shp,(linewidth-Pirate2Shp.BPR)<<2,0); + + MoveScreen(0,0); + VW_FadeIn(); + WaitKeyVBL(57,200); + while (Keyboard[57]); + + SD_PlaySound(GOOD_PICKSND); + + MoveScreen(0,200); + WaitKeyVBL(57,300); + while (Keyboard[57]); + VW_FadeOut(); + + FreeShape(&Pirate1Shp); + FreeShape(&Pirate2Shp); +} + +#else + +/////////////////////////////////////////////////////////////////////////// +// +// DoPiracy() - Text-based piracy code... +// +void DoPiracy() +{ +} + +#endif +#endif + +//-------------------------------------------------------------------------- +// BlackPalette() +//-------------------------------------------------------------------------- +void BlackPalette() +{ + extern char colors[7][17]; + + _ES=FP_SEG(&colors[0]); + _DX=FP_OFF(&colors[0]); + _AX=0x1002; + geninterrupt(0x10); + screenfaded = true; +} + +//-------------------------------------------------------------------------- +// ColoredPalette() +//-------------------------------------------------------------------------- +void ColoredPalette() +{ + extern char colors[7][17]; + + _ES=FP_SEG(&colors[3]); + _DX=FP_OFF(&colors[3]); + _AX=0x1002; + geninterrupt(0x10); + screenfaded = false; +} + +//////////////////////////////////////////////////////////////////////////// +// +// Verify() +// +long Verify(char *filename) +{ + int handle; + long size; + + if ((handle=open(filename,O_BINARY))==-1) + return (0); + size=filelength(handle); + close(handle); + return(size); +} + +/////////////////////////////////////////////////////////////////////////// +// +// GE_SaveGame +// +// Handles user i/o for saving a game +// +/////////////////////////////////////////////////////////////////////////// + +void GE_SaveGame() +{ + boolean GettingFilename=true; +// char Filename[FILENAME_LEN+1]; //, ID[sizeof(GAMENAME)], VER[sizeof(SAVEVER_DATA)]; + int handle; + struct dfree dfree; + long davail; + + VW_FixRefreshBuffer(); + ReadGameList(); + while (GettingFilename) + { + DisplayGameList(2,7,3,10); + US_DrawWindow(5,1,30,3); + memset(Filename,0,sizeof(Filename)); + US_CPrint("Enter name to SAVE this game:"); + VW_UpdateScreen(); + if (screenfaded) + VW_FadeIn(); + if (!US_LineInput((linewidth<<2)-32,20,Filename,"",true,8,0)) + goto EXIT_FUNC; + if (!strlen(Filename)) + goto EXIT_FUNC; + getdfree(getdisk()+1,&dfree); + davail = (long)dfree.df_avail*(long)dfree.df_bsec*(long)dfree.df_sclus; + if (davail < 10000) + { + US_CenterWindow(22,4); + US_Print("\n"); + US_CPrintLine("Disk Full: Can't save game."); + US_CPrintLine("Try inserting another disk."); + VW_UpdateScreen(); + + IN_Ack(); + } + else + { + strcat(Filename,".SAV"); + GettingFilename = false; + if (Verify(Filename)) // FILE EXISTS + { + US_CenterWindow(22,4); + US_CPrintLine("That file already exists..."); + US_CPrintLine("Overwrite it ????"); + US_CPrintLine("(Y)es or (N)o?"); + VW_UpdateScreen(); + + while((!Keyboard[21]) && (!Keyboard[49]) && !Keyboard[27]); + + if (Keyboard[27]) + goto EXIT_FUNC; + if (Keyboard[49]) + { + GettingFilename = true; + VW_UpdateScreen(); + } + } + } + } + + handle = open(Filename,O_RDWR|O_CREAT|O_BINARY,S_IREAD|S_IWRITE); + if (handle==-1) + goto EXIT_FUNC; + + if ((!CA_FarWrite(handle,(void far *)GAMENAME,sizeof(GAMENAME))) || (!CA_FarWrite(handle,(void far *)SAVEVER_DATA,sizeof(SAVEVER_DATA)))) + { + if (!screenfaded) + VW_FadeOut(); + + return; + } + + if (!USL_SaveGame(handle)) + Quit("Save game error"); + + + +EXIT_FUNC:; + + if (handle!=-1) + close(handle); + + if (handle==-1) + { + remove(Filename); + US_CenterWindow(22,6); + US_CPrintLine("DISK ERROR"); + US_CPrintLine("Check: Write protect..."); + US_CPrintLine("File name..."); + US_CPrintLine("Bytes free on disk..."); + US_CPrintLine("Press SPACE to continue."); + VW_UpdateScreen(); + while (!Keyboard[57]); + while (Keyboard[57]); + } + + while (Keyboard[1]); + + if (!screenfaded) + VW_FadeOut(); +} + + +/////////////////////////////////////////////////////////////////////////// +// +// GE_LoadGame +// +// Handles user i/o for loading a game +// +/////////////////////////////////////////////////////////////////////////// + +boolean GE_LoadGame() +{ + boolean GettingFilename=true,rt_code=false; + int handle; + + IN_ClearKeysDown(); + memset(ID,0,sizeof(ID)); + memset(VER,0,sizeof(VER)); + VW_FixRefreshBuffer(); + ReadGameList(); + while (GettingFilename) + { + DisplayGameList(2,7,3,10); + US_DrawWindow(5,1,30,3); + memset(Filename,0,sizeof(Filename)); + US_CPrint("Enter name of game to RESTORE:"); + VW_UpdateScreen(); + if (screenfaded) + VW_FadeIn(); + if (!US_LineInput((linewidth<<2)-32,20,Filename,"",true,8,0)) + goto EXIT_FUNC; + strcat(Filename,".SAV"); + GettingFilename = false; + + if (!Verify(Filename)) // FILE DOESN'T EXIST + { + US_CenterWindow(22,3); + US_CPrintLine(" That file doesn't exist...."); + US_CPrintLine("Press SPACE to try again."); + VW_UpdateScreen(); + + while (!Keyboard[57]); + while (Keyboard[57]); + GettingFilename = true; + } + } + + handle = open(Filename,O_RDWR|O_BINARY); + if (handle==-1) + goto EXIT_FUNC; + + if ((!CA_FarRead(handle,(void far *)&ID,sizeof(ID))) || (!CA_FarRead(handle,(void far *)&VER,sizeof(VER)))) + return(false); + + if ((strcmp(ID,GAMENAME)) || (strcmp(VER,SAVEVER_DATA))) + { + US_CenterWindow(32,4); + US_CPrintLine("That isn't a "GAMENAME); + US_CPrintLine(".SAV file."); + US_CPrintLine("Press SPACE to continue."); + VW_UpdateScreen(); + while (!Keyboard[57]); + while (Keyboard[57]); + + if (!screenfaded) + VW_FadeOut(); + + return(false); + } + + if (!USL_LoadGame(handle)) + Quit("Load game error."); + + rt_code = true; + + +EXIT_FUNC:; + if (handle==-1) + { + US_CenterWindow(22,3); + US_CPrintLine("DISK ERROR ** LOAD **"); + US_CPrintLine("Press SPACE to continue."); + while (!Keyboard[57]); + while (Keyboard[57]); + } + else + close(handle); + + if (!screenfaded) + VW_FadeOut(); + + return(rt_code); +} + +/////////////////////////////////////////////////////////////////////////// +// +// GE_HardError() - Handles the Abort/Retry/Fail sort of errors passed +// from DOS. Hard coded to ignore if during Load/Save Game. +// +/////////////////////////////////////////////////////////////////////////// +#pragma warn -par +#pragma warn -rch +int GE_HardError(word errval,int ax,int bp,int si) +{ +#define IGNORE 0 +#define RETRY 1 +#define ABORT 2 +extern void ShutdownId(void); + +static char buf[32]; +static WindowRec wr; +static boolean oldleavedriveon; + int di; + char c,*s,*t; +boolean holdscreenfaded; + + if (InLoadSaveGame) + hardresume(IGNORE); + + + di = _DI; + + oldleavedriveon = LeaveDriveOn; + LeaveDriveOn = false; + + if (ax < 0) + s = "Device Error"; + else + { + if ((di & 0x00ff) == 0) + s = "Drive ~ is Write Protected"; + else + s = "Error on Drive ~"; + for (t = buf;*s;s++,t++) // Can't use sprintf() + if ((*t = *s) == '~') + *t = (ax & 0x00ff) + 'A'; + *t = '\0'; + s = buf; + } + + c = peekb(0x40,0x49); // Get the current screen mode + if ((c < 4) || (c == 7)) + goto oh_kill_me; + + // DEBUG - handle screen cleanup + holdscreenfaded=screenfaded; + + US_SaveWindow(&wr); + VW_ClearVideo(0); ////////////// added for exiting + US_CenterWindow(30,3); + US_CPrint(s); + US_CPrint("(R)etry or (A)bort?"); + VW_UpdateScreen(); + if (holdscreenfaded) + VW_FadeIn(); + IN_ClearKeysDown(); + +asm sti // Let the keyboard interrupts come through + + while (true) + { + switch (IN_WaitForASCII()) + { + case key_Escape: + case 'a': + case 'A': + goto oh_kill_me; + break; + case key_Return: + case key_Space: + case 'r': + case 'R': + if (holdscreenfaded) + VW_FadeOut(); + US_ClearWindow(); + VW_UpdateScreen(); + US_RestoreWindow(&wr); + LeaveDriveOn = oldleavedriveon; + return(RETRY); + break; + } + } + +oh_kill_me: + abortprogram = s; + TrashProg("Terminal Error: %s\n",s); +// if (tedlevel) +// fprintf(stderr,"You launched from TED. I suggest that you reboot...\n"); + + return(ABORT); +#undef IGNORE +#undef RETRY +#undef ABORT +} +#pragma warn +par +#pragma warn +rch + +//-------------------------------------------------------------------------- +// +// +// B O B ROUTINES +// +// +//-------------------------------------------------------------------------- + + + +#ifdef BOBLIST + +//////////////////////////////////////////////////////////////////////////// +// +// UpdateBOBList() - Adds a sprite to an objects BOBlist. The BOB List +// must already be allocated and have an available slot. +// +// RETURNS : true = Success adding Sprite / false = Failure. +// +// NOTE : This also sets the users 'needtoreact' flag to true. +// +boolean UpdateBOBList(objtype *obj,struct Simple_Shape *Shape,shapeclass Class, short priority, spriteflags sprflags) +{ + struct BOB_Shape *CurBOBShape = NULL; + +#pragma warn -pia + + if (CurBOBShape = obj->nextshape) + { + // Treverse down BOBList looking for a sprite with the same class + // OR an empty shape struct to store the new shape. + + while ((CurBOBShape->class != Class) && (CurBOBShape->class) && CurBOBShape) + { + CurBOBShape = CurBOBShape->nextshape; + } + + if (CurBOBShape) + { + RF_RemoveSprite(&CurBOBShape->sprite); + CurBOBShape->shapenum = Shape->shapenum; + CurBOBShape->x_offset = Shape->x_offset; + CurBOBShape->y_offset = Shape->y_offset; + CurBOBShape->priority = priority; + CurBOBShape->sprflags = sprflags; + CurBOBShape->class = Class; + return(true); + } + } + return(false); + +#pragma warn +pia + +} + +///////////////////////////////////////////////////////////////////////////// +// +// RemoveBOBShape() - Removes a sprite from a BOBList. +// +// RETURNS : true = Success / false = Failure (shape not found) +// +boolean RemoveBOBShape(objtype *obj, shapeclass Class) +{ + struct BOB_Shape *CurBOBShape = NULL; + +#pragma warn -pia + + if (CurBOBShape = obj->nextshape) + { + while ((CurBOBShape->class != Class) && (!CurBOBShape->class) && CurBOBShape) + { + CurBOBShape = CurBOBShape->nextshape; + } + + if (CurBOBShape) + { + CurBOBShape->class = noshape; + return(true); + } + } + return(false); + +#pragma warn +pia + +} + + +///////////////////////////////////////////////////////////////////////////// +// +// RemoveBOBList() - Removes an entire BOBList attached to an object. +// +// +void RemoveBOBList(objtype *obj) +{ + struct BOB_Shape *CurBOBShape; + +#pragma warn -pia + + if (CurBOBShape = obj->nextshape) + { + // Treverse down BOBList looking for a sprite with the same class + // OR an empty shape struct to store the new shape. + + while (CurBOBShape) + { + if (CurBOBShape->class) + { + CurBOBShape->class = noshape; + RF_RemoveSprite (&CurBOBShape->sprite); + } + CurBOBShape = CurBOBShape->nextshape; + } + } + +#pragma warn +pia + +} + + + +///////////////////////////////////////////////////////////////////////////// +// +// InitBOBList() -- This initializes a BOB list for all the possible shapes +// attached at one time. This is done with an array of +// BOB_Shape structs and links the 'nextshape' pointer to +// to the next element. +// +// +void InitBOBList(objtype *obj, struct BOB_Shape *BOB_Shape, short NumElements) +{ + struct BOB_Shape *CurShape; + short loop; + + obj->nextshape = BOB_Shape; + + for (loop=1;loopnextshape = BOB_Shape; + } + + BOB_Shape->nextshape = NULL; +} + + +//////////////////////////////////////////////////////////////////////////// +// +// RefreshBOBList() -- This routine updates all sprites attached to the +// BOBList and refreshes there position in the sprite +// list. +// +void RefreshBOBList(objtype *obj) +{ + struct BOB_Shape *Shape; + + Shape = obj->nextshape; + + while (Shape) + { + if (Shape->class) + RF_PlaceSprite(&Shape->sprite,obj->x+Shape->x_offset,obj->y+Shape->y_offset, Shape->shapenum, spritedraw,Shape->priority,Shape->sprflags); + Shape = Shape->nextshape; + } +} +#endif + + + +//-------------------------------------------------------------------------- +// InitBufferedIO() +//-------------------------------------------------------------------------- +memptr InitBufferedIO(int handle, BufferedIO *bio) +{ + bio->handle = handle; + bio->offset = BIO_BUFFER_LEN; + bio->status = 0; + MM_GetPtr(&bio->buffer,BIO_BUFFER_LEN); + + return(bio->buffer); +} + +//-------------------------------------------------------------------------- +// FreeBufferedIO() +//-------------------------------------------------------------------------- +void FreeBufferedIO(BufferedIO *bio) +{ + if (bio->buffer) + MM_FreePtr(&bio->buffer); +} + +//-------------------------------------------------------------------------- +// bio_readch() +//-------------------------------------------------------------------------- +byte bio_readch(BufferedIO *bio) +{ + byte far *buffer; + + if (bio->offset == BIO_BUFFER_LEN) + { + bio->offset = 0; + bio_fillbuffer(bio); + } + + buffer = MK_FP(bio->buffer,bio->offset++); + + return(*buffer); +} + +//-------------------------------------------------------------------------- +// bio_fillbuffer() +// +// BUGS (Not really bugs... More like RULES!) +// +// 1) This code assumes BIO_BUFFER_LEN is no smaller than +// NEAR_BUFFER_LEN!! +// +// 2) BufferedIO.status should be altered by this code to report +// read errors, end of file, etc... If you know how big the file +// is you're reading, determining EOF should be no problem. +// +//-------------------------------------------------------------------------- +void bio_fillbuffer(BufferedIO *bio) +{ + #define NEAR_BUFFER_LEN (64) + byte near_buffer[NEAR_BUFFER_LEN]; + short bio_length,bytes_read,bytes_requested; + + bytes_read = 0; + bio_length = BIO_BUFFER_LEN; + while (bio_length) + { + if (bio_length > NEAR_BUFFER_LEN-1) + bytes_requested = NEAR_BUFFER_LEN; + else + bytes_requested = bio_length; + + read(bio->handle,near_buffer,bytes_requested); + _fmemcpy(MK_FP(bio->buffer,bytes_read),near_buffer,bytes_requested); + + bio_length -= bytes_requested; + bytes_read += bytes_requested; + } +} + +/////////////////////////////////////////////////////////////////////////// +// +// SwapLong() +// +void SwapLong(long far *Var) +{ + asm les bx,Var + asm mov ax,[es:bx] + asm xchg ah,al + asm xchg ax,[es:bx+2] + asm xchg ah,al + asm mov [es:bx],ax +} + +/////////////////////////////////////////////////////////////////////////// +// +// SwapWord() +// +void SwapWord(unsigned int far *Var) +{ + asm les bx,Var + asm mov ax,[es:bx] + asm xchg ah,al + asm mov [es:bx],ax +} + + +#if 0 + +//////////////////////////////////////////////////////////////////////////// +// +// LoadShape() +// +int LoadShape(char *Filename,struct Shape *SHP) +{ + #define CHUNK(Name) (*ptr == *Name) && \ + (*(ptr+1) == *(Name+1)) && \ + (*(ptr+2) == *(Name+2)) && \ + (*(ptr+3) == *(Name+3)) + + + int RT_CODE; +// struct ffblk ffblk; + FILE *fp; + char CHUNK[5]; + char far *ptr; + memptr IFFfile = NULL; + unsigned long FileLen, size, ChunkLen; + int loop; + + + RT_CODE = 1; + + // Decompress to ram and return ptr to data and return len of data in + // passed variable... + + if (!(FileLen = BLoad(Filename,&IFFfile))) + TrashProg("Can't load Compressed Shape - Possibly corrupt file!"); + + // Evaluate the file + // + ptr = MK_FP(IFFfile,0); + if (!CHUNK("FORM")) + goto EXIT_FUNC; + ptr += 4; + + FileLen = *(long far *)ptr; + SwapLong((long far *)&FileLen); + ptr += 4; + + if (!CHUNK("ILBM")) + goto EXIT_FUNC; + ptr += 4; + + FileLen += 4; + while (FileLen) + { + ChunkLen = *(long far *)(ptr+4); + SwapLong((long far *)&ChunkLen); + ChunkLen = (ChunkLen+1) & 0xFFFFFFFE; + + if (CHUNK("BMHD")) + { + ptr += 8; + SHP->bmHdr.w = ((struct BitMapHeader far *)ptr)->w; + SHP->bmHdr.h = ((struct BitMapHeader far *)ptr)->h; + SHP->bmHdr.x = ((struct BitMapHeader far *)ptr)->x; + SHP->bmHdr.y = ((struct BitMapHeader far *)ptr)->y; + SHP->bmHdr.d = ((struct BitMapHeader far *)ptr)->d; + SHP->bmHdr.trans = ((struct BitMapHeader far *)ptr)->trans; + SHP->bmHdr.comp = ((struct BitMapHeader far *)ptr)->comp; + SHP->bmHdr.pad = ((struct BitMapHeader far *)ptr)->pad; + SwapWord(&SHP->bmHdr.w); + SwapWord(&SHP->bmHdr.h); + SwapWord(&SHP->bmHdr.x); + SwapWord(&SHP->bmHdr.y); + ptr += ChunkLen; + } + else + if (CHUNK("BODY")) + { + ptr += 4; + size = *((long far *)ptr); + ptr += 4; + SwapLong((long far *)&size); + SHP->BPR = (SHP->bmHdr.w+7) >> 3; + MM_GetPtr(&SHP->Data,size); + if (!SHP->Data) + goto EXIT_FUNC; + movedata(FP_SEG(ptr),FP_OFF(ptr),FP_SEG(SHP->Data),0,size); + ptr += ChunkLen; + + break; + } + else + ptr += ChunkLen+8; + + FileLen -= ChunkLen+8; + } + + RT_CODE = 0; + +EXIT_FUNC:; + if (IFFfile) + { +// segptr = (memptr)FP_SEG(IFFfile); + MM_FreePtr(&IFFfile); + } + + return (RT_CODE); +} + +#endif + +//////////////////////////////////////////////////////////////////////////// +// +// FreeShape() +// +void FreeShape(struct Shape *shape) +{ + if (shape->Data) + MM_FreePtr(&shape->Data); +} + +//////////////////////////////////////////////////////////////////////////// +// +// UnpackEGAShapeToScreen() +// +int UnpackEGAShapeToScreen(struct Shape *SHP,int startx,int starty) +{ + int currenty; + signed char n, Rep, far *Src, far *Dst[8], loop, Plane; + unsigned int BPR, Height; + boolean NotWordAligned; + + NotWordAligned = SHP->BPR & 1; + startx>>=3; + Src = MK_FP(SHP->Data,0); + currenty = starty; + Plane = 0; + Height = SHP->bmHdr.h; + while (Height--) + { + Dst[0] = (MK_FP(0xA000,displayofs)); + Dst[0] += ylookup[currenty]; + Dst[0] += startx; + for (loop=1; loopbmHdr.d; loop++) + Dst[loop] = Dst[0]; + + + for (Plane=0; PlanebmHdr.d; Plane++) + { + outport(0x3c4,((1<BPR+1) >> 1) << 1; // IGNORE WORD ALIGN + while (BPR) + { + if (SHP->bmHdr.comp) + n = *Src++; + else + n = BPR-1; + + if (n < 0) + { + if (n != -128) + { + n = (-n)+1; + BPR -= n; + Rep = *Src++; + if ((!BPR) && (NotWordAligned)) // IGNORE WORD ALIGN + n--; + + while (n--) + *Dst[Plane]++ = Rep; + } + else + BPR--; + } + else + { + n++; + BPR -= n; + if ((!BPR) && (NotWordAligned)) // IGNORE WORD ALIGN + n--; + + while (n--) + *Dst[Plane]++ = *Src++; + + if ((!BPR) && (NotWordAligned)) // IGNORE WORD ALIGN + Src++; + } + } + } + currenty++; + } + + return(0); +} + +//////////////////////////////////////////////////////////////////////////// +// +// GetKeyChoice() +// +char GetKeyChoice(char *choices,boolean clear) +{ + extern void DoEvents(void); + + boolean waiting; + char *s,*ss; + + IN_ClearKeysDown(); + + waiting = true; + while (waiting) + { + s = choices; + while (*s) + { + if (Keyboard[*s++]) + { + waiting=false; + break; + } + } + } + + IN_ClearKeysDown(); + + return(*(--s)); +} + +#if 0 + +//////////////////////////////////////////////////////////////////////////// +// +// AnimateObj() +// +boolean AnimateObj(objtype *obj) +{ + boolean Done; + + Done = false; + + if (obj->animtype == at_NONE) // Animation finished? + return(true); // YEP! + + if (obj->animdelay) // Check animation delay. + { + obj->animdelay -= tics; + if (obj->animdelay < 0) + obj->animdelay = 0; + return(false); + } + + switch (obj->animtype) // Animate this object! + { + case at_ONCE: + case at_CYCLE: + switch (obj->animdir) + { + case at_FWD: + if (obj->curframe < obj->maxframe) + AdvanceAnimFWD(obj); + else + if (obj->animtype == at_CYCLE) + { + obj->curframe = 0; // RESET CYCLE ANIMATION + obj->animdelay=1; + } + else + { + obj->animtype = at_NONE; // TERMINATE ONCE ANIM + Done = true; + } + break; + + case at_REV: + if (obj->curframe > 0) + AdvanceAnimREV(obj); + else + if (obj->animtype == at_CYCLE) + { + obj->curframe = obj->maxframe; // RESET CYCLE ANIMATION + obj->animdelay = 1; + } + else + { + obj->animtype = at_NONE; // TERMINATE ONCE ANIM + Done = true; + } + break; // REV + } + break; + + case at_REBOUND: + switch (obj->animdir) + { + case at_FWD: + if (obj->curframe < obj->maxframe) + AdvanceAnimFWD(obj); + else + { + obj->animdir = at_REV; + obj->animdelay = 1; + } + break; + + case at_REV: + if (obj->curframe > 0) + AdvanceAnimREV(obj); + else + { + obj->animdir = at_FWD; + obj->animdelay = 1; + Done = true; + } + break; + } + break; /* REBOUND */ + + case at_WAIT: + Done = true; + break; + } + + return(Done); +} + +void AdvanceAnimFWD(objtype *obj) // Advances a Frame of ANIM for ONCE,CYCLE, REBOUND +{ + obj->curframe++; // INC frames + obj->animdelay = obj->maxdelay; // Init Delay Counter. + obj->needtoreact = true; +} + + +void AdvanceAnimREV(objtype *obj) // Advances a Frame of ANIM for ONCE,CYCLE, REBOUND +{ + obj->curframe--; // DEC frames + obj->animdelay = obj->maxdelay; // Init Delay Counter. + obj->needtoreact = true; +} +#endif + +#if 0 +/////////////////////////////////////////////////////////////////////////// +// +// LoadASArray() - Load an array of audio samples in FAR memory. +// +void LoadASArray(struct Sample *ASArray) +{ + int loop = 0; + + while (ASArray[loop].filename) + { + if (!BLoad(ASArray[loop].filename,(memptr *)&ASArray[loop].data)) + TrashProg("Unable to load sample in LoadASArray()"); + loop++; + } +} + + + +//////////////////////////////////////////////////////////////////////////// +// +// FreeASArray() - Frees an ASArray from memory that has been loaded with +// LoadASArray() +// +void FreeASArray(struct Sample *ASArray) +{ + unsigned loop = 0; + + while (ASArray[loop].data) + { + MM_SetPurge((memptr *)&ASArray[loop++].data,3); + MM_FreePtr((memptr *)&ASArray[loop++].data); + } +} + +/////////////////////////////////////////////////////////////////////////// +// +// GE_LoadAllDigiSounds() - This is a hook that CA_LoadAllSounds() +// calls to load all of the Digitized sounds for +// Sound Blaster & Sound Source. +// +// NOTE : This stub would do any other necessary routines for DigiSounds +// specific to GAMERS EDGE code. (Keeping seperate from ID's) +// +void GE_LoadAllDigiSounds() +{ + LoadASArray(DigiSounds); +} + + + +///////////////////////////////////////////////////////////////////////// +// +// GE_FreeAllDigiSounds() -- This is a hook that CA_LoadAllSounds() +// calls to free all digitized sounds for +// which ever hardware and allow for any necessary +// clean up. +// +// +// NOTE : This stub would do any other necessary routines for DigiSounds +// specific to GAMERS EDGE code. (Keeping seperate from ID's) +// +void GE_FreeAllDigiSounds() +{ + FreeASArray(DigiSounds); +} + + + +/////////////////////////////////////////////////////////////////////////// +// +// GE_LoadAllSounds() - Loads ALL sounds needed for detected hardware. +// +void GE_LoadAllSounds() +{ + unsigned i,start; + + start = STARTPCSOUNDS; + for (i=0;i 0); + if (h < list_height) + list_height = h; + + // Open window and print header... + // + US_DrawWindow(winx,winy,list_width*(8+SPACES*2),list_height+3); + US_CPrintLine("LIST OF SAVED GAMES"); + US_Print("\n"); + + col = orgcol = PrintX; + row = PrintY; + + // Display as many 'save game' files as can fit in the window. + // + width = list_width; + while ((games_printedbaseshape+obj->curframe-STARTSPRITES]; + + width = (sprite->xh-sprite->xl+(1<> 1; + if (obj->sprflags&sf_vertflip) + { + height = (sprite->yl-(sprite->height<yh+(1<> 1; + } + else + height = (sprite->yh-sprite->yl+(1<> 1; + + obj->x = x-width; + obj->y = y-height; +} +#endif + +#if 0 +//------------------------------------------------------------------------- +// cacheout() +//------------------------------------------------------------------------- +void cacheout(short s,short e) +{ + short i; + + for(i=(s);i<=(e);i++) + { + grneeded[i]&=~ca_levelbit; + if (grsegs[i]) + MM_SetPurge(&grsegs[i],3); + } + +} + +//------------------------------------------------------------------------- +// cachein() +//------------------------------------------------------------------------- +void cachein(short s,short e) +{ + short i; + + for(i=(s);i<=(e);i++) + { + CA_MarkGrChunk(i); + if (grsegs[i]) + MM_SetPurge(&grsegs[i],0); + } +} +#endif + +#if 0 +//////////////////////////////////////////////////////////////////////////// +// +// SetUpdateBlock() +// +void SetUpdateBlock(unsigned x,unsigned y,unsigned width,unsigned height,char refresh) +{ + eraseblocktype *erase; + +#if 0 //SP unsure if this is needed + x = (x+((MAPBORDER+MAPXLEFTOFFSET)<<4))>>3; + y += ((MAPBORDER+MAPYTOPOFFSET)<<4); +#else + x = (x+(MAPBORDER<<4))>>3; + y += (MAPBORDER<<4); +#endif + width >>= 3; + + if (refresh & 1) + { + erase = eraselistptr[0]++; + erase->screenx=x; + erase->screeny=y; + erase->width=width; + erase->height=height; + } + + if (refresh & 2) + { + erase = eraselistptr[1]++; + erase->screenx=x; + erase->screeny=y; + erase->width=width; + erase->height=height; + } +} + + +//////////////////////////////////////////////////////////////////////////// +// +// ObjHeight +// +unsigned ObjHeight(objtype *obj) +{ + spritetabletype far *sprite; + + sprite=&spritetable[obj->baseshape+obj->curframe-STARTSPRITES]; + + if (obj->sprflags&sf_vertflip) + { + return((sprite->yl-(sprite->height<yh+(1<> 1); + } + else + return((sprite->yh-sprite->yl+(1<> 1); +} + + +//////////////////////////////////////////////////////////////////////////// +// +// ObjWidth +// +unsigned ObjWidth(objtype *obj) +{ + spritetabletype far *sprite; + + sprite=&spritetable[obj->baseshape+obj->curframe-STARTSPRITES]; + + return((sprite->xh-sprite->xl+(1<> 1); +} +#endif + +#if 0 +//-------------------------------------------------------------------------- +// visible_on() +//-------------------------------------------------------------------------- +boolean visible_on(objtype *obj) +{ + if (!(obj->flags & of_visible)) + { + obj->needtoreact=true; + obj->flags |= of_visible; + return(true); + } + + return(false); +} + +//-------------------------------------------------------------------------- +// visible_off() +//-------------------------------------------------------------------------- +boolean visible_off(objtype *obj) +{ + if (obj->flags & of_visible) + { + obj->needtoreact=true; + obj->flags &= ~of_visible; + return(true); + } + + return(false); +} +#endif + + +/* +=================== += += FizzleFade += +=================== +*/ + +#define PIXPERFRAME 10000 + +void FizzleFade (unsigned source, unsigned dest, + unsigned width,unsigned height, boolean abortable) +{ + unsigned drawofs,pagedelta; + unsigned char maskb[8] = {1,2,4,8,16,32,64,128}; + unsigned x,y,p,frame; + long rndval; + ScanCode lastLastScan=LastScan=0; + + width--; + height--; + + pagedelta = dest-source; +// VW_SetScreen (dest,0); + rndval = 1; + y = 0; + +asm mov es,[screenseg] +asm mov dx,SC_INDEX +asm mov al,SC_MAPMASK +asm out dx,al + + TimeCount=frame=0; + do // while (1) + { + if ((abortable) || (Flags & FL_QUICK)) + { + IN_ReadControl(0,&control); + if (control.button0 || control.button1 || (lastLastScan != LastScan) + || Keyboard[sc_Escape] || (Flags & FL_QUICK)) + { + VW_ScreenToScreen (source,dest,(width+1)/8,height+1); + goto exitfunc; + } + } + + for (p=0;pwidth || y>height) + continue; + drawofs = source+ylookup[y]; + + asm mov cx,[x] + asm mov si,cx + asm and si,7 + asm mov dx,GC_INDEX + asm mov al,GC_BITMASK + asm mov ah,BYTE PTR [maskb+si] + asm out dx,ax + + asm mov si,[drawofs] + asm shr cx,1 + asm shr cx,1 + asm shr cx,1 + asm add si,cx + asm mov di,si + asm add di,[pagedelta] + + asm mov dx,GC_INDEX + asm mov al,GC_READMAP // leave GC_INDEX set to READMAP + asm out dx,al + + asm mov dx,SC_INDEX+1 + asm mov al,1 + asm out dx,al + asm mov dx,GC_INDEX+1 + asm mov al,0 + asm out dx,al + + asm mov bl,[es:si] + asm xchg [es:di],bl + + asm mov dx,SC_INDEX+1 + asm mov al,2 + asm out dx,al + asm mov dx,GC_INDEX+1 + asm mov al,1 + asm out dx,al + + asm mov bl,[es:si] + asm xchg [es:di],bl + + asm mov dx,SC_INDEX+1 + asm mov al,4 + asm out dx,al + asm mov dx,GC_INDEX+1 + asm mov al,2 + asm out dx,al + + asm mov bl,[es:si] + asm xchg [es:di],bl + + asm mov dx,SC_INDEX+1 + asm mov al,8 + asm out dx,al + asm mov dx,GC_INDEX+1 + asm mov al,3 + asm out dx,al + + asm mov bl,[es:si] + asm xchg [es:di],bl + + if (rndval == 1) // entire sequence has been completed + goto exitfunc; + } +// frame++; +// while (TimeCount= 23) + { + video -= (x<<1); + _fmemcpy(MK_FP(0xb000,0x0000),MK_FP(0xb000,0x00a0),3840); + } + else + { + y++; + video += ((80-x)<<1); + } + x=0; + break; + + default: + *video = *ptr; + video[1] = 15; + video += 2; + x++; + break; + } + ptr++; + } + + va_end(ap); +} +#endif + +#if 0 + +//-------------------------------------------------------------------------- +// FULL SCREEN REFRESH/ANIM MANAGERS +//-------------------------------------------------------------------------- + +/////////////////////////////////////////////////////////////////////////// +// +// InitLatchRefresh() -- Loads an ILBM (JAMPAK'd) into the Master Latch +// to be used as the background refresh screen... +// +void InitLatchRefresh(char *filename) +{ + struct Shape RefreshShp; + short yofs; + + VW_ClearVideo(0); + + if (LoadShape(filename,&RefreshShp)) + TrashProg("Can't load %s",filename); + + VW_SetLineWidth(RefreshShp.BPR); + + yofs = masterswap/SCREENWIDTH; + MoveGfxDst(0,yofs); // Handle title screen + UnpackEGAShapeToScreen(&RefreshShp,0,0); + FreeShape(&RefreshShp); + + MoveScreen(0,0); + VW_InitDoubleBuffer(); + + RF_NewPosition(0,0); + + RF_Refresh(); + + SetUpdateBlock(0,0,RefreshShp.bmHdr.w,RefreshShp.bmHdr.h,3); +} + + +//////////////////////////////////////////////////////////////////////////// +// +// InitFullScreenAnim() -- Initialize ALL necessary functions for full screen +// animation types. +// +// filename - filename for background lbm. +// SpawnAll - spawn function to call to spawn all inital objects.. +// +void InitFullScreenAnim(char *filename, void (*SpawnAll)()) +{ + unsigned old_flags; + + old_flags = GE_SystemFlags.flags; + GE_SystemFlags.flags &= ~(GEsf_Tiles | GEsf_Panning | GEsf_RefreshVec); + + CA_ClearMarks(); + + RFL_InitSpriteList(); + InitObjArray(); + + if (SpawnAll) + SpawnAll(); + + CA_CacheMarks(NULL); + + CalcInactivate(); + CalcSprites(); + + InitLatchRefresh(filename); + + RF_ForceRefresh(); + RF_ForceRefresh(); + + GE_SystemFlags.flags = old_flags; +} + + + +/////////////////////////////////////////////////////////////////////////// +// +// DoFullScreenAnim() -- a General "Do-Every-Thing" function that +// displays a full screen animation... +// +// filename - Filename of background lbm +// SpawnAll - Function to call to spawn all inital objects (REQUIRED) +// CheckKey - Function to call every cycle - The function called must +// return a value of greater than zero (0) to terminate the +// animation cycle. (REQUIRED) +// CleanUp - Function to call upon exiting the animation. (OPTIONAL) +// +void DoFullScreenAnim(char *filename, void (*SpawnAll)(), short (*CheckKey)(),void (*CleanUp)()) +{ + unsigned old_flags; + boolean ExitAnim = false; + + // Save off the current system flags.... + + old_flags = GE_SystemFlags.flags; + GE_SystemFlags.flags &= ~(GEsf_Tiles | GEsf_Panning); + +// randomize(); + + // Init refresh latch screen, initalize all object, and setup video mode. + + InitFullScreenAnim(filename,SpawnAll); + + VW_FadeIn(); + + while (!ExitAnim) + { + CalcInactivate(); + CalcSprites(); + RF_Refresh(); + + ExitAnim = (boolean)CheckKey(); + } + +// RemoveBOBList(player); +// CalcInactivate(); + + if (CleanUp) + CleanUp(); + + // Restore old system flags... + + GE_SystemFlags.flags = old_flags; +} + +#endif + +//-------------------------------------------------------------------------- +// FindFile() +//-------------------------------------------------------------------------- +boolean FindFile(char *filename,char *disktext,char disknum) +{ + extern boolean splitscreen; + + char command[100]; + char choices[]={sc_Escape,sc_Space,0},drive[2]; + boolean fadeitout=false,rt_code=2; + + if (!disktext) + disktext = GAMENAME; + drive[0] = getdisk() + 'A'; + drive[1] = 0; + while (rt_code == 2) + { + if (Verify(filename)) + rt_code = true; + else + { + if (ge_textmode) + { + clrscr(); + gotoxy(1,1); + printf("\nInsert %s disk %d into drive %s.\n",disktext,disknum,drive); + printf("Press SPACE to continue, ESC to abort.\n"); + } + else + { + if (splitscreen) + { + bufferofs = displayofs = screenloc[screenpage]; + CenterWindow(38,5); + } + else + { + bufferofs = displayofs = 0; + US_CenterWindow(38,5); + } + + strcpy(command,"\nInsert "); + strcat(command,disktext); + strcat(command," disk"); + if (disknum != -1) + { + strcat(command," "); + itoa(disknum,command+strlen(command),10); + } + strcat(command," into drive "); + strcat(command,drive); + strcat(command,"."); + US_CPrint(command); + US_CPrint("Press SPACE to continue, ESC to abort."); + } + + sound(300); + VW_WaitVBL(20); + nosound(); + + if (!ge_textmode) + { + if (screenfaded) + { + VW_FadeIn(); + fadeitout=true; + } + } + if (GetKeyChoice(choices,true) == sc_Escape) + rt_code = false; + } + } + + if (!ge_textmode) + if (fadeitout) + VW_FadeOut(); + + return(rt_code); +} + +#if 0 +//-------------------------------------------------------------------------- +// CacheAll() +//-------------------------------------------------------------------------- +void CacheAV(char *title) +{ + if (Verify("EGAGRAPH."EXT)) + { + CA_CacheMarks(title); + if (!FindFile("EGAGRAPH."EXT,AUDIO_DISK)) + TrashProg("Can't find graphics files."); + +// cache in audio + + current_disk = AUDIO_DISK; + } + else + { + +// cache in audio + + if (!FindFile("EGAGRAPH."EXT,VIDEO_DISK)) + TrashProg("Can't find audio files."); + CA_CacheMarks(title); + + current_disk = VIDEO_DISK; + } +} +#endif + +#ifdef TEXT_PRESENTER +//-------------------------------------------------------------------------- +// +// TEXT PRESENTER CODE +// +//-------------------------------------------------------------------------- + +typedef enum pi_stype {pis_pic2x,pis_latch_pic} pi_stype; + + +typedef struct { // 4 bytes + unsigned shapenum; + pi_stype shape_type; +} pi_shape_info; + +#define pia_active 0x01 + +typedef struct { // 10 bytes + char baseshape; + char frame; + char maxframes; + short delay; + short maxdelay; + short x,y; +} pi_anim_info; + + #define PI_CASE_SENSITIVE + + #define PI_RETURN_CHAR '\n' + #define PI_CONTROL_CHAR '^' + + #define PI_CNVT_CODE(c1,c2) ((c1)|(c2<<8)) + +// shape table provides a way for the presenter to access and +// display any shape. +// +pi_shape_info far pi_shape_table[] = { + + {BOLTOBJPIC,pis_pic2x}, // 0 + {NUKEOBJPIC,pis_pic2x}, + {SKELETON_1PIC,pis_pic2x}, + {EYE_WALK1PIC,pis_pic2x}, + {ZOMB_WALK1PIC,pis_pic2x}, + + {TIMEOBJ1PIC,pis_pic2x}, // 5 + {POTIONOBJPIC,pis_pic2x}, + {RKEYOBJPIC,pis_pic2x}, + {YKEYOBJPIC,pis_pic2x}, + {GKEYOBJPIC,pis_pic2x}, + + {BKEYOBJPIC,pis_pic2x}, // 10 + {RGEM1PIC,pis_pic2x}, + {GGEM1PIC,pis_pic2x}, + {BGEM1PIC,pis_pic2x}, + {YGEM1PIC,pis_pic2x}, + + {PGEM1PIC,pis_pic2x}, // 15 + {CHESTOBJPIC,pis_pic2x}, + {PSHOT1PIC,pis_pic2x}, + {RED_DEMON1PIC,pis_pic2x}, + {MAGE1PIC,pis_pic2x}, + + {BAT1PIC,pis_pic2x}, // 20 + {GREL1PIC,pis_pic2x}, + {GODESS_WALK1PIC,pis_pic2x}, + {ANT_WALK1PIC,pis_pic2x}, + {FATDEMON_WALK1PIC,pis_pic2x}, + + {SUCCUBUS_WALK1PIC,pis_pic2x}, //25 + {TREE_WALK1PIC,pis_pic2x}, + {DRAGON_WALK1PIC,pis_pic2x}, + {BUNNY_LEFT1PIC,pis_pic2x}, + +}; + +// anim table holds info about each different animation. +// +pi_anim_info far pi_anim_table[] = {{0,0,3,0,10}, // 0 BOLT + {1,0,3,0,10}, // NUKE + {2,0,4,0,10}, // SKELETON + {3,0,3,0,10}, // EYE + {4,0,3,0,10}, // ZOMBIE + + {5,0,2,0,10}, // 5 FREEZE TIME + {11,0,2,0,10}, // RED GEM + {12,0,2,0,10}, // GREEN GEM + {13,0,2,0,10}, // BLUE GEM + {14,0,2,0,10}, // YELLOW GEM + + {15,0,2,0,10}, // 10 PURPLE GEM + {17,0,2,0,10}, // PLAYER'S SHOT + {18,0,3,0,10}, // RED DEMON + {19,0,2,0,10}, // MAGE + {20,0,4,0,10}, // BAT + + {21,0,2,0,10}, // 15 GRELMINAR + {22,0,3,0,10}, // GODESS + {23,0,3,0,10}, // ANT + {24,0,4,0,10}, // FAT DEMON + {25,0,4,0,10}, // SUCCUBUS + + {26,0,2,0,10}, // 20 TREE + {27,0,3,0,10}, // DRAGON + {28,0,2,0,10}, // BUNNY +}; + +// anim list is created on the fly from the anim table... +// this allows a single animation to be display in more than +// one place... +// +pi_anim_info far pi_anim_list[PI_MAX_ANIMS]; +boolean pi_recursing=false; + +//-------------------------------------------------------------------------- +// Presenter() - DANGEROUS DAVE "Presenter()" is more up-to-date than this. +// +// +// Active control codes: +// +// ^CE - center text between 'left margin' and 'right margin' +// ^FCn - set font color +// ^LMnnn - set left margin (if 'nnn' == "fff" uses current x) +// ^RMnnn - set right margin (if 'nnn' == "fff" uses current x) +// ^EP - end of page (waits for up/down arrow) +// ^PXnnn - move x to coordinate 'n' +// ^PYnnn - move y to coordinate 'n' +// ^XX - exit presenter +// ^LJ - left justify --\ ^RJ doesn't handle imbedded control +// ^RJ - right justify --/ codes properly. Use with caution. +// ^BGn - set background color +// ^ANnn - define animation +// ^SHnnn - display shape 'n' at current x,y +// +// +// Future control codes: +// +// ^OBnnn - activate object 'n' +// ^FL - flush to edges (whatever it's called) +// +// +// Other info: +// +// All 'n' values are hex numbers (0 - f), case insensitive. +// The number of N's listed is the number of digits REQUIRED by that control +// code. (IE: ^LMnnn MUST have 3 values! --> 003, 1a2, 01f, etc...) +// +// If a line consists only of control codes, the cursor is NOT advanced +// to the next line (the ending is skipped). If just ONE non-control +// code is added, the number "8" for example, then the "8" is displayed +// and the cursor is advanced to the next line. +// +// ^CE must be on the same line as the text it should center! +// +//-------------------------------------------------------------------------- +void Presenter(PresenterInfo *pi) +{ +#ifdef AMIGA + XBitMap **font = pi->font; + + #define ch_width(ch) font[ch]->pad + char font_height = font[0]->Rows; +#else + fontstruct _seg *font = (fontstruct _seg *)grsegs[STARTFONT]; + + #define MAX_PB 150 + #define ch_width(ch) font->width[ch] + char font_height = font->height-1; // "-1" squeezes font vertically + char pb[MAX_PB]; + short length; +#endif + + enum {jm_left,jm_right,jm_flush}; + char justify_mode = jm_left; + boolean centering=false; + + short bgcolor = pi->bgcolor; + short xl=pi->xl,yl=pi->yl,xh=pi->xh,yh=pi->yh; + short cur_x = xl, cur_y = yl; + char far *first_ch = pi->script[0]; + + char far *scan_ch,temp; + short scan_x,PageNum=0,numanims=0; + boolean up_released=true,dn_released=true; + boolean presenting=true,start_of_line=true; + +// if set background is first thing in file, make sure initial screen +// clear uses this color. +// + if (!_fstrncmp(first_ch,"^BG",3)) + bgcolor = PI_VALUE(first_ch+3,1); + + if (!pi_recursing) + { + PurgeAllGfx(); + CachePage(first_ch); + } + VW_Bar(xl,yl,xh-xl+1,yh-yl+1,bgcolor); + + while (presenting) + { +// +// HANDLE WORD-WRAPPING TEXT +// + if (*first_ch != PI_CONTROL_CHAR) + { + start_of_line = false; + + // Parse script until one of the following: + // + // 1) text extends beyond right margin + // 2) NULL termination is reached + // 3) PI_RETURN_CHAR is reached + // 4) PI_CONTROL_CHAR is reached + // + scan_x = cur_x; + scan_ch = first_ch; + while ((scan_x+ch_width(*scan_ch) <= xh) && (*scan_ch) && + (*scan_ch != PI_RETURN_CHAR) && (*scan_ch != PI_CONTROL_CHAR)) + scan_x += ch_width(*scan_ch++); + + // if 'text extends beyond right margin', scan backwards for + // a SPACE + // + if (scan_x+ch_width(*scan_ch) > xh) + { + short last_x = scan_x; + char far *last_ch = scan_ch; + + while ((scan_ch != first_ch) && (*scan_ch != ' ') && (*scan_ch != PI_RETURN_CHAR)) + scan_x -= ch_width(*scan_ch--); + + if (scan_ch == first_ch) + scan_ch = last_ch, scan_x = last_x; + } + + // print current line + // +#ifdef AMIGA + while (first_ch < scan_ch) + { + qBlit(font[*first_ch++],pi->dst,cur_x,cur_y); +// qBlit(font[*first_ch],pi->dst,cur_x,cur_y); +// cur_x += ch_width(*first_ch++); + } +#else + temp = *scan_ch; + *scan_ch = 0; + + if ((justify_mode == jm_right)&&(!centering)) + { + unsigned width,height; + + VWL_MeasureString(first_ch,&width,&height,font); + cur_x = xh-width; + if (cur_x < xl) + cur_x = xl; + } + + px = cur_x; + py = cur_y; + + length = scan_ch-first_ch+1; // USL_DrawString only works with + if (length > MAX_PB) + Quit("Presenter(): Print buffer string too long!"); + _fmemcpy(pb,first_ch,length); // near pointers... + + if (*first_ch != '\n') + USL_DrawString(pb); + + *scan_ch = temp; + first_ch = scan_ch; +#endif + cur_x = scan_x; + centering = false; + + // skip SPACES / RETURNS at end of line + // + if ((*first_ch==' ') || (*first_ch==PI_RETURN_CHAR)) + first_ch++; + + // PI_CONTROL_CHARs don't advance to next character line + // + if (*scan_ch != PI_CONTROL_CHAR) + { + cur_x = xl; + cur_y += font_height; + } + } + else +// +// HANDLE CONTROL CODES +// + { + PresenterInfo endmsg; + pi_anim_info far *anim; + pi_shape_info far *shape_info; + unsigned shapenum; + short length; + char far *s; + + if (first_ch[-1] == '\n') + start_of_line = true; + + first_ch++; +#ifndef PI_CASE_SENSITIVE + *first_ch=toupper(*first_ch); + *(first_ch+1)=toupper(*(first_ch+1)); +#endif + switch (*((unsigned far *)first_ch)++) + { + // CENTER TEXT ------------------------------------------------------ + // + case PI_CNVT_CODE('C','E'): + length = 0; + s = first_ch; + while (*s && (*s != PI_RETURN_CHAR)) + { + switch (*s) + { + case PI_CONTROL_CHAR: + s++; + switch (*((unsigned *)s)++) + { +#ifndef AMIGA + case PI_CNVT_CODE('F','C'): + case PI_CNVT_CODE('B','G'): + s++; + break; +#endif + + case PI_CNVT_CODE('L','M'): + case PI_CNVT_CODE('R','M'): + case PI_CNVT_CODE('S','H'): + case PI_CNVT_CODE('P','X'): + case PI_CNVT_CODE('P','Y'): + s += 3; + break; + + case PI_CNVT_CODE('L','J'): + case PI_CNVT_CODE('R','J'): + // No parameters to pass over! + break; + } + break; + + default: + length += ch_width(*s++); + break; + } + } + cur_x = ((xh-xl+1)-length)/2; + centering = true; + break; + + // DRAW SHAPE ------------------------------------------------------- + // + case PI_CNVT_CODE('S','H'): + shapenum = PI_VALUE(first_ch,3); + first_ch += 3; + shape_info = &pi_shape_table[shapenum]; + switch (shape_info->shape_type) + { + short width; + + case pis_pic2x: + cur_x = ((cur_x+2) + 7) & 0xFFF8; + width=BoxAroundPic(cur_x-2,cur_y-1,shape_info->shapenum,pi); + VW_DrawPic2x(cur_x>>3,cur_y,shape_info->shapenum); + cur_x += width; + break; + } + break; + + // INIT ANIMATION --------------------------------------------------- + // + case PI_CNVT_CODE('A','N'): + shapenum = PI_VALUE(first_ch,2); + first_ch += 2; + _fmemcpy(&pi_anim_list[numanims],&pi_anim_table[shapenum],sizeof(pi_anim_info)); + anim = &pi_anim_list[numanims++]; + shape_info = &pi_shape_table[anim->baseshape+anim->frame]; + switch (shape_info->shape_type) + { + short width; + + case pis_pic2x: + cur_x = ((cur_x+2) + 7) & 0xFFF8; + width=BoxAroundPic(cur_x-2,cur_y-1,shape_info->shapenum,pi); + VW_DrawPic2x(cur_x>>3,cur_y,shape_info->shapenum); + anim->x = cur_x>>3; + anim->y = cur_y; + cur_x += width; + break; + } + break; + +#ifndef AMIGA + // FONT COLOR ------------------------------------------------------- + // + case PI_CNVT_CODE('F','C'): + fontcolor = bgcolor ^ PI_VALUE(first_ch++,1); + break; +#endif + + // BACKGROUND COLOR ------------------------------------------------- + // + case PI_CNVT_CODE('B','G'): + bgcolor = PI_VALUE(first_ch++,1); + break; + + // LEFT MARGIN ------------------------------------------------------ + // + case PI_CNVT_CODE('L','M'): + shapenum = PI_VALUE(first_ch,3); + first_ch += 3; + if (shapenum == 0xfff) + xl = cur_x; + else + xl = shapenum; + break; + + // RIGHT MARGIN ----------------------------------------------------- + // + case PI_CNVT_CODE('R','M'): + shapenum = PI_VALUE(first_ch,3); + first_ch += 3; + if (shapenum == 0xfff) + xh = cur_x; + else + xh = shapenum; + break; + + // SET X COORDINATE ------------------------------------------------- + // + case PI_CNVT_CODE('P','X'): + cur_x = PI_VALUE(first_ch,3); + first_ch += 3; + break; + + // SET Y COORDINATE ------------------------------------------------- + // + case PI_CNVT_CODE('P','Y'): + cur_y = PI_VALUE(first_ch,3); + first_ch += 3; + break; + + // LEFT JUSTIFY ----------------------------------------------------- + // + case PI_CNVT_CODE('L','J'): + justify_mode = jm_left; + break; + + // RIGHT JUSTIFY ---------------------------------------------------- + // + case PI_CNVT_CODE('R','J'): + justify_mode = jm_right; + break; + + // END OF PAGE ------------------------------------------------------ + // + case PI_CNVT_CODE('E','P'): + if (pi_recursing) + Quit("Presenter(): Can't use ^EP when recursing!"); + + endmsg.xl = cur_x; + endmsg.yl = yh-(font_height+2); + endmsg.xh = xh; + endmsg.yh = yh; + endmsg.bgcolor = bgcolor; + endmsg.ltcolor = pi->ltcolor; + endmsg.dkcolor = pi->dkcolor; + endmsg.script[0] = (char far *)"^CE^FC8- ^FC0ESC ^FC8to return to play, or ^FC0ARROW KEYS ^FC8to page through more Help -^XX"; + + pi_recursing = true; + Presenter(&endmsg); + pi_recursing = false; + +#ifndef AMIGA + if (screenfaded) + VW_FadeIn(); + VW_ColorBorder(8 | 56); +#endif + + while (1) + { +#ifndef AMIGA + long newtime; + + VW_WaitVBL(1); + newtime = TimeCount; + realtics = tics = newtime-lasttimecount; + lasttimecount = newtime; +#else + WaitVBL(1); + CALC_TICS; +#endif + AnimatePage(numanims); + IN_ReadControl(0,&control); + + if (control.button1 || Keyboard[1]) + { + presenting=false; + break; + } + else + { + if (ControlTypeUsed != ctrl_Keyboard) + control.dir = dir_None; + + if (((control.dir == dir_North) || (control.dir == dir_West)) && (PageNum)) + { + if (up_released) + { + PageNum--; + up_released = false; + break; + } + } + else + { + up_released = true; + if (((control.dir == dir_South) || (control.dir == dir_East)) && (PageNum < pi->numpages-1)) + { + if (dn_released) + { + PageNum++; + dn_released = false; + break; + } + } + else + dn_released = true; + } + } + } + + cur_x = xl; + cur_y = yl; + if (cur_y+font_height > yh) + cur_y = yh-font_height; + first_ch = pi->script[PageNum]; + + numanims = 0; + PurgeAllGfx(); + CachePage(first_ch); + + VW_Bar(xl,yl,xh-xl+1,yh-yl+1,bgcolor); + break; + + // EXIT PRESENTER --------------------------------------------------- + // + case PI_CNVT_CODE('X','X'): + presenting=false; + break; + } + + if ((first_ch[0] == ' ') && (first_ch[1] == '\n') && start_of_line) + first_ch += 2; + } + } +} + +//-------------------------------------------------------------------------- +// ResetAnims() +//-------------------------------------------------------------------------- +void ResetAnims() +{ + pi_anim_list[0].baseshape = -1; +} + +//-------------------------------------------------------------------------- +// AnimatePage() +//-------------------------------------------------------------------------- +void AnimatePage(short numanims) +{ + pi_anim_info far *anim=pi_anim_list; + pi_shape_info far *shape_info; + + while (numanims--) + { + anim->delay += tics; + if (anim->delay >= anim->maxdelay) + { + anim->delay = 0; + anim->frame++; + if (anim->frame == anim->maxframes) + anim->frame = 0; + +#if ANIM_USES_SHAPETABLE + shape_info = &pi_shape_table[anim->baseshape+anim->frame]; +#else + shape_info = &pi_shape_table[anim->baseshape]; +#endif + switch (shape_info->shape_type) + { + case pis_pic2x: +#if ANIM_USES_SHAPETABLE + VW_DrawPic2x(anim->x,anim->y,shape_info->shapenum); +#else + VW_DrawPic2x(anim->x,anim->y,shape_info->shapenum+anim->frame); +#endif + break; + } + } + anim++; + } +} + +//-------------------------------------------------------------------------- +// BoxAroundPic() +//-------------------------------------------------------------------------- +short BoxAroundPic(short x1, short y1, unsigned picnum, PresenterInfo *pi) +{ + short x2,y2; + + x2 = x1+(pictable[picnum-STARTPICS].width<<4)+2; + y2 = y1+(pictable[picnum-STARTPICS].height)+1; + VWB_Hlin(x1,x2,y1,pi->ltcolor); + VWB_Hlin(x1,x2,y2,pi->dkcolor); + VWB_Vlin(y1,y2,x1,pi->ltcolor); + VWB_Vlin(y1,y2,x1+1,pi->ltcolor); + VWB_Vlin(y1,y2,x2,pi->dkcolor); + VWB_Vlin(y1,y2,x2+1,pi->dkcolor); + + return(x2-x1+1); +} + +//-------------------------------------------------------------------------- +// PurgeAllGfx() +//-------------------------------------------------------------------------- +void PurgeAllGfx() +{ + ResetAnims(); + FreeUpMemory(); +} + +//-------------------------------------------------------------------------- +// CachePage() +//-------------------------------------------------------------------------- +void CachePage(char far *script) +{ + pi_anim_info far *anim; + short loop; + unsigned shapenum; + boolean end_of_page=false; + short numanims=0; + + while (!end_of_page) + { + switch (*script++) + { + case PI_CONTROL_CHAR: +#ifndef PI_CASE_SENSITIVE + *script=toupper(*script); + *(script+1)=toupper(*(script+1)); +#endif + switch (*((unsigned far *)script)++) + { + case PI_CNVT_CODE('S','H'): + shapenum = PI_VALUE(script,3); + script += 3; + CA_MarkGrChunk(pi_shape_table[shapenum].shapenum); + break; + + case PI_CNVT_CODE('A','N'): + shapenum = PI_VALUE(script,2); + script += 2; + + if (numanims++ == PI_MAX_ANIMS) + Quit("CachePage(): Too many anims on one page."); + + anim = &pi_anim_table[shapenum]; +#if ANIM_USES_SHAPETABLE + for (loop=anim->baseshape;loop < anim->baseshape+anim->maxframes; loop++) + CA_MarkGrChunk(pi_shape_table[loop].shapenum); +#else + shapenum = pi_shape_table[anim->baseshape].shapenum; + for (loop=0; loopmaxframes; loop++) + CA_MarkGrChunk(shapenum+loop); +#endif + break; + + case PI_CNVT_CODE('X','X'): + case PI_CNVT_CODE('E','P'): + end_of_page = true; + break; + } + break; + } + } + + CA_CacheMarks(NULL); +} + +//-------------------------------------------------------------------------- +// PI_VALUE() +//-------------------------------------------------------------------------- +unsigned PI_VALUE(char far *ptr,char num_nybbles) +{ + char ch,nybble,shift; + unsigned value=0; + + for (nybble=0; nybblescriptstart))) + return(0); + pi->script[0] = MK_FP(pi->scriptstart,0); + pi->script[0][size-1] = 0; // Last byte is trashed! + InitPresenterScript(pi); + + return(size); +#pragma warn +pia +} + +//------------------------------------------------------------------------- +// FreePresenterScript() +//------------------------------------------------------------------------- +void FreePresenterScript(PresenterInfo *pi) +{ + if (pi->script) + MM_FreePtr(&pi->scriptstart); +} + +//------------------------------------------------------------------------- +// InitPresenterScript() +//------------------------------------------------------------------------- +void InitPresenterScript(PresenterInfo *pi) +{ + char far *script = pi->script[0]; + + pi->numpages = 1; // Assume at least 1 page + while (*script) + { + switch (*script++) + { + case PI_CONTROL_CHAR: +#ifndef PI_CASE_SENSITIVE + *script=toupper(*script); + *(script+1)=toupper(*(script+1)); +#endif + switch (*((unsigned far *)script)++) + { + case PI_CNVT_CODE('E','P'): + if (pi->numpages < PI_MAX_PAGES) + pi->script[pi->numpages++] = script; + else + TrashProg("GE ERROR: Too many Presenter() pages. --> %d",pi->numpages); + break; + } + break; + + case '\r': + if (*script == '\n') + { + *(script-1) = ' '; + script++; + } + break; + } + } + + pi->numpages--; // Last page defined is not a real page. +} +#endif + + +//------------------------------------------------------------------------- +// AnimateWallList() +//------------------------------------------------------------------------- +void AnimateWallList(void) +{ + walltype *wall, *check; + unsigned i; + int tile,org_tile; + + if (wall_anim_delay>0) + { + wall_anim_delay-=realtics; + return; + } + + // + // Re-Init our counter... + // + + wall_anim_delay = wall_anim_time; + + // + // Clear all previous flags marking animation being DONE. + // + + for (i=0;icolor + wall_anim_pos[wall->color]; + + if (ANIM_FLAGS(tile)) + { + do + { + if (!(TILE_FLAGS(tile) & tf_MARKED)) + { + // + // update our offset table (0-NUMANIMS) + // + + wall_anim_pos[tile] += (char signed)ANIM_FLAGS(tile+(char signed)wall_anim_pos[tile]); + + // + // Mark tile as being already updated.. + // + + TILE_FLAGS(tile) |= tf_MARKED; + } + + // + // Check rest of tiles in this animation string... + // + + tile += (char signed)ANIM_FLAGS(tile); + + } while (tile != org_tile); + } + } +} + diff --git a/16/cawat/GELIB.H b/16/cawat/GELIB.H new file mode 100644 index 00000000..eb25be61 --- /dev/null +++ b/16/cawat/GELIB.H @@ -0,0 +1,210 @@ +/* Catacomb Armageddon Source Code + * Copyright (C) 1993-2014 Flat Rock Software + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "SL_FILE.h" + + +/////////////////////////////////////////////////////////////////////////// +// +// Defines +// + +#define ANIM_USES_SHAPE_TABLE false +#define PI_MAX_ANIMS 10 +#define PI_MAX_PAGES 40 + +#define SAVEVER_DATA "0.01" +#define FILENAME_LEN 15 + +#define GAMENAME "CATACOMB ARMAGEDDON 3-D" +#define VERSION "V1.02" +#define REVISION " rev 1 " + +//#define BOBLIST 1 //SP - Undefine if not using BOBList + +#define AUDIO_DISK (2) +#define VIDEO_DISK (1) +#define LEVEL_DISK (2) + +#define BIO_BUFFER_LEN (512) + +#define TrashProg Quit + +// #define AMIGA + + +typedef struct Sample { + char *filename; + memptr *data; +} Sample; + +typedef enum {ged_none, ged_SoundSource,ged_SoundBlaster} AudioDeviceType; + +//typedef struct { +// memptr textptr; +// char far *pages[MAX_TEXT_PAGES]; +// short totalpages; +//} textinfo; + +typedef struct { + int handle; // handle of file + memptr buffer; // pointer to buffer + word offset; // offset into buffer + word status; // read/write status +} BufferedIO; + +typedef enum ANIMINFO {at_NONE,at_INIT,at_WAIT,at_ONCE,at_CYCLE, + at_REBOUND,at_EXTRA, + at_FWD,at_REV +} ANIMINFO; + +struct BitMapHeader { + unsigned int w,h,x,y; + unsigned char d,trans,comp,pad; +}; + +struct BitMap { + unsigned int Width; + unsigned int Height; + unsigned int Depth; + unsigned int BytesPerRow; + char far *Planes[8]; +}; + +struct Shape { + memptr Data; + long size; + unsigned int BPR; + struct BitMapHeader bmHdr; +}; + +#ifdef AMIGA +typedef struct { + char *script[PI_MAX_PAGES]; + XBitMap **shapes; + XBitMap **font; + short xl,yl,xh,yh; + struct BitMap *dst; + char numpages,bgcolor; +} PresenterInfo; +#else +typedef struct { + char far *script[PI_MAX_PAGES]; + memptr scriptstart; + short xl,yl,xh,yh; + char numpages,bgcolor,ltcolor,dkcolor; +} PresenterInfo; +#endif + + +/////////////////////////////////////////////////////////////////////////// +// +// Externs +// + +extern char Filename[], ID[], VER[]; +extern boolean ge_textmode; +extern short PPT_LeftEdge,PPT_RightEdge; +//extern boolean ConserveMemory; +extern BufferedIO lzwBIO; +extern short wall_anim_delay,wall_anim_time; + + + +/////////////////////////////////////////////////////////////////////////// +// +// Function prototypes +// +void WaitKeyVBL(short key, short vbls); +void CalibrateJoystick(short joynum); +void MoveScreen(short x, short y); +void MoveGfxDst(short x, short y); +void DoPiracy(void); +void PrintPropText(char far *text); +//void DisplayText(textinfo *textinfo); +//long LoadTextFile(char *filename,textinfo *textinfo); +//void FreeTextFile(textinfo *textinfo); +//void InitTextFile(textinfo *textinfo); +long Verify(char *filename); +void GE_SaveGame(void); +boolean GE_LoadGame(void); +int GE_HardError(word errval,int ax,int bp,int si); + +#ifdef BOBLIST + + +boolean UpdateBOBList(objtype *obj,struct Simple_Shape *Shape,shapeclass Class, short priority, spriteflags sprflags); +boolean RemoveBOBShape(objtype *obj, shapeclass Class); +void RemoveBOBList(objtype *obj); +void InitBOBList(objtype *obj, struct BOB_Shape *BOB_Shape, short NumElements); +void RefreshBOBList(objtype *obj); +#endif + + +unsigned long BLoad(char *SourceFile, memptr *DstPtr); +void lzwDecompressFromRAM(byte far *SrcPtr, byte far *DstPtr, longword SrcLen); +void lzwDecompressFromFile(BufferedIO *SrcPtr, byte far *DstPtr, longword SrcLen); +byte readch(int handle); + +memptr InitBufferedIO(int handle, BufferedIO *bio); +void FreeBufferedIO(BufferedIO *bio); +byte bio_readch(BufferedIO *bio); +void bio_fillbuffer(BufferedIO *bio); + + +void SwapLong(long far *Var); +void SwapWord(unsigned int far *Var); +int LoadShape(char *Filename,struct Shape *SHP); +void FreeShape(struct Shape *shape); +int UnpackEGAShapeToScreen(struct Shape *SHP,int startx,int starty); +char GetKeyChoice(char *choices,boolean clear); +boolean AnimateObj(objtype *obj); +void AdvanceAnimFWD(objtype *obj); +void AdvanceAnimREV(objtype *obj); + +void LoadASArray(struct Sample *ASArray); +void FreeASArray(struct Sample *ASArray); +//void SelectDigiAudio(AudioDeviceType Device); +void PlaySample(unsigned SampleNum); +void GE_FreeAllDigiSounds(void); +void GE_LoadAllDigiSounds(void); +void DisplayGameList(short winx, short winy, short list_width, short list_height); +void ReadGameList(void); +void CheckStack(void); +void CenterObj(objtype *obj, unsigned x, unsigned y); +void cachein(short s,short e); +void cacheout(short s,short e); +void FizzleFade (unsigned source, unsigned dest,unsigned width,unsigned height, boolean abortable); +void mprintf(char *msg, ...); +boolean FindFile(char *filename,char *disktext,char disknum); +void CacheAV(char *title); +void BlackPalette(void); +void ColoredPalette(void); +void Presenter(PresenterInfo *pi); +unsigned PI_VALUE(char far *ptr,char num_nybbles); +long LoadPresenterScript(char *filename,PresenterInfo *pi); +void FreePresenterScript(PresenterInfo *pi); +void InitPresenterScript(PresenterInfo *pi); + +void AnimatePage(short numanims); +short BoxAroundPic(short x1, short y1, unsigned picnum, PresenterInfo *pi); +void PurgeAllGfx(void); +void CachePage(char far *script); + + +void AnimateWallList(void); diff --git a/16/cawat/GFXE_ARM.EQU b/16/cawat/GFXE_ARM.EQU new file mode 100644 index 00000000..f833b119 --- /dev/null +++ b/16/cawat/GFXE_ARM.EQU @@ -0,0 +1,571 @@ +;===================================== +; +; Graphics .EQU file for .ARM +; IGRAB-ed on Thu Dec 02 13:59:00 1993 +; +;===================================== + +FINALEPIC = 4 +STATUSPIC = 5 +FACE5PIC = 6 +FIRSTLATCHPIC = 7 +FACE1PIC = 8 +FACE2PIC = 9 +FACE3PIC = 10 +FACE4PIC = 11 +RADAR_TOPPIC = 12 +RADAR_BOTTOMPIC = 13 +RADAR_RGEMPIC = 14 +RADAR_GGEMPIC = 15 +RADAR_BGEMPIC = 16 +RADAR_YGEMPIC = 17 +RADAR_PGEMPIC = 18 +FIRSTGROUNDPIC = 19 +FIRSTSTRIPPIC = 20 +FIRSTSCALEPIC = 21 +SKELETON_1PIC = 22 +SKELETON_2PIC = 23 +SKELETON_3PIC = 24 +SKELETON_4PIC = 25 +SKELETON_ATTACK_1PIC = 26 +SKELETON_ATTACK_2PIC = 27 +SKELETON_ATTACK_3PIC = 28 +SKELETON_OUCHPIC = 29 +SKELETON_DEATH_1PIC = 30 +SKELETON_DEATH_2PIC = 31 +TOMB1PIC = 32 +TOMB2PIC = 33 +TOMB3PIC = 34 +OBJ_WARP1PIC = 35 +OBJ_WARP2PIC = 36 +OBJ_WARP3PIC = 37 +OBJ_WARP4PIC = 38 +EYE_WALK1PIC = 39 +EYE_WALK2PIC = 40 +EYE_WALK3PIC = 41 +EYE_OUCH1PIC = 42 +EYE_OUCH2PIC = 43 +EYE_DEATH1PIC = 44 +EYE_DEATH2PIC = 45 +EYE_DEATH3PIC = 46 +EYE_SCOWLPIC = 47 +EYE_SHOT1PIC = 48 +EYE_SHOT2PIC = 49 +ZOMB_APPEAR1PIC = 50 +ZOMB_APPEAR2PIC = 51 +ZOMB_APPEAR3PIC = 52 +ZOMB_APPEAR4PIC = 53 +ZOMB_WALK1PIC = 54 +ZOMB_WALK2PIC = 55 +ZOMB_WALK3PIC = 56 +ZOMB_OUCHPIC = 57 +ZOMB_ATTACKPIC = 58 +ZOMB_DIE1PIC = 59 +ZOMB_DIE2PIC = 60 +ZOMB_DIE3PIC = 61 +BOLTOBJPIC = 62 +BOLT2OBJPIC = 63 +BOLT3OBJPIC = 64 +NUKEOBJPIC = 65 +NUKE2OBJPIC = 66 +NUKE3OBJPIC = 67 +TIMEOBJ1PIC = 68 +TIMEOBJ2PIC = 69 +O_WATER_CHEST1PIC = 70 +O_WATER_CHEST2PIC = 71 +POTIONOBJPIC = 72 +RKEYOBJPIC = 73 +YKEYOBJPIC = 74 +GKEYOBJPIC = 75 +BKEYOBJPIC = 76 +RGEM1PIC = 77 +RGEM2PIC = 78 +GGEM1PIC = 79 +GGEM2PIC = 80 +BGEM1PIC = 81 +BGEM2PIC = 82 +YGEM1PIC = 83 +YGEM2PIC = 84 +PGEM1PIC = 85 +PGEM2PIC = 86 +CHESTOBJPIC = 87 +PSHOT1PIC = 88 +PSHOT2PIC = 89 +PSHOT_EXP1PIC = 90 +PSHOT_EXP2PIC = 91 +PSHOT_EXP3PIC = 92 +RED_DEMON1PIC = 93 +RED_DEMON2PIC = 94 +RED_DEMON3PIC = 95 +RED_DEMON4PIC = 96 +RED_DEMONATTACK1PIC = 97 +RED_DEMONATTACK2PIC = 98 +RED_DEMONATTACK3PIC = 99 +RED_DEMONOUCHPIC = 100 +RED_DEMONDIE1PIC = 101 +RED_DEMONDIE2PIC = 102 +RED_DEMONDIE3PIC = 103 +MAGE1PIC = 104 +MAGE2PIC = 105 +MAGEOUCHPIC = 106 +MAGEATTACKPIC = 107 +MAGEDIE1PIC = 108 +MAGEDIE2PIC = 109 +BAT1PIC = 110 +BAT2PIC = 111 +BAT3PIC = 112 +BAT4PIC = 113 +BATDIE1PIC = 114 +BATDIE2PIC = 115 +GREL1PIC = 116 +GREL2PIC = 117 +GRELATTACKPIC = 118 +GRELHITPIC = 119 +GRELDIE1PIC = 120 +GRELDIE2PIC = 121 +GRELDIE3PIC = 122 +GRELDIE4PIC = 123 +GRELDIE5PIC = 124 +GRELDIE6PIC = 125 +SKULL_SHOTPIC = 126 +GODESS_WALK1PIC = 127 +GODESS_WALK2PIC = 128 +GODESS_WALK3PIC = 129 +GODESS_ATTACK1PIC = 130 +GODESS_ATTACK2PIC = 131 +GODESS_ATTACK3PIC = 132 +GODESS_STATUEPIC = 133 +GODESS_OUCHPIC = 134 +GODESS_DEATH1PIC = 135 +GODESS_DEATH2PIC = 136 +ANT_EGG1PIC = 137 +ANT_EGG2PIC = 138 +ANT_WALK1PIC = 139 +ANT_WALK2PIC = 140 +ANT_WALK3PIC = 141 +ANT_ATTACKPIC = 142 +ANT_DEATH1PIC = 143 +ANT_DEATH2PIC = 144 +ANT_DEATH3PIC = 145 +FATDEMON_WALK1PIC = 146 +FATDEMON_WALK2PIC = 147 +FATDEMON_WALK3PIC = 148 +FATDEMON_WALK4PIC = 149 +FATDEMON_ATTACK1PIC = 150 +FATDEMON_ATTACK2PIC = 151 +FATDEMON_OUCHPIC = 152 +FATDEMON_BLOWUP1PIC = 153 +FATDEMON_BLOWUP2PIC = 154 +FATDEMON_BLOWUP3PIC = 155 +FATDEMON_EXPLODEPIC = 156 +FATDEMON_FEETPIC = 157 +SUCCUBUS_WALK1PIC = 158 +SUCCUBUS_WALK2PIC = 159 +SUCCUBUS_WALK3PIC = 160 +SUCCUBUS_WALK4PIC = 161 +SUCCUBUS_ATTACK1PIC = 162 +SUCCUBUS_ATTACK2PIC = 163 +SUCCUBUS_OUCHPIC = 164 +SUCCUBUS_DEATH1PIC = 165 +SUCCUBUS_DEATH2PIC = 166 +SUCCUBUS_SHOT1PIC = 167 +TREE_IDLEPIC = 168 +TREE_AWAKENINGPIC = 169 +TREE_WALK1PIC = 170 +TREE_WALK2PIC = 171 +TREE_ATTACK1PIC = 172 +TREE_ATTACK2PIC = 173 +TREE_ATTACK3PIC = 174 +TREE_DEATH1PIC = 175 +TREE_DEATH2PIC = 176 +TREE_DEATH3PIC = 177 +DRAGON_BUBBLES1PIC = 178 +DRAGON_BUBBLES2PIC = 179 +DRAGON_EYESPIC = 180 +DRAGON_RISE1PIC = 181 +DRAGON_RISE2PIC = 182 +DRAGON_WALK1PIC = 183 +DRAGON_WALK2PIC = 184 +DRAGON_WALK3PIC = 185 +DRAGON_WALK4PIC = 186 +DRAGON_ATTACK1PIC = 187 +DRAGON_ATTACK2PIC = 188 +DRAGON_ATTACK3PIC = 189 +DRAGON_OUCHPIC = 190 +DRAGON_DEATH1PIC = 191 +DRAGON_DEATH2PIC = 192 +DRAGON_DEATH3PIC = 193 +BUNNY_LEFT1PIC = 194 +BUNNY_LEFT2PIC = 195 +BUNNY_RIGHT1PIC = 196 +BUNNY_RIGHT2PIC = 197 +BUNNY_META1PIC = 198 +BUNNY_META2PIC = 199 +BUNNY_WALK1PIC = 200 +BUNNY_WALK2PIC = 201 +BUNNY_OUCHPIC = 202 +BUNNY_DEATH1PIC = 203 +BUNNY_DEATH2PIC = 204 +ARCH1PIC = 205 +ARCH2PIC = 206 +ARCH3PIC = 207 +ARCH4PIC = 208 +ARCH5PIC = 209 +ARCH6PIC = 210 +ARCH7PIC = 211 +ARCH8PIC = 212 +ARCH9PIC = 213 +ARCH10PIC = 214 +ARCH11PIC = 215 +ARCH12PIC = 216 +ARCH13PIC = 217 +ANT_HILLPIC = 218 +COLUMNPIC = 219 +SULPHUR_GAS_1PIC = 220 +SULPHUR_GAS_2PIC = 221 +SULPHUR_GAS_3PIC = 222 +FIRE_POT_1PIC = 223 +FIRE_POT_2PIC = 224 +SKEL_HANGPIC = 225 +FORCE_FIELD_1PIC = 226 +FORCE_FIELD_2PIC = 227 +FORCE_FIELD_3PIC = 228 +FORCE_FIELD_4PIC = 229 +WFOUNTAINPIC = 230 +FIRSTWALLPIC = 231 +CRYSTAL_LIGHT_1PIC = 232 +CRYSTAL_LIGHT_2PIC = 233 +CRYSTAL_LIGHT_3PIC = 234 +CRYSTAL_LIGHT_4PIC = 235 +CRYSTAL_DARK_1PIC = 236 +CRYSTAL_DARK_2PIC = 237 +CRYSTAL_DARK_3PIC = 238 +CRYSTAL_DARK_4PIC = 239 +FIRE_WALL_1PIC = 240 +FIRE_WALL_2PIC = 241 +FIRE_WALL_3PIC = 242 +FIRE_WALL_4PIC = 243 +BRN_STONE_GATEPIC = 244 +BRN_STONE_WALL_1PIC = 245 +BRN_STONE_WALL_2PIC = 246 +KUDZU_LIGHT_WALLPIC = 247 +KUDZU_DARK_WALLPIC = 248 +HEDGE_WALLPIC = 249 +HEDGE_EYESPIC = 250 +BRN_WINDOW_LIGHTPIC = 251 +ALTAR_LEFTPIC = 252 +ALTAR_RIGHTPIC = 253 +GRAY_LIGHT_WALLPIC = 254 +GRAY_DARK_WALLPIC = 255 +GRAY_LIGHT_SIGNPIC = 256 +GRAY_DARK_SIGNPIC = 257 +MANICLE_LIGHT_BLOODYPIC = 258 +MANICLE_DARK_BLOODYPIC = 259 +LIGHT_CURTAIN_WINDOWPIC = 260 +LIGHT_CURTAIN_WALLPIC = 261 +DARK_CURTAIN_WINDOWPIC = 262 +DARK_CURTAIN_WALLPIC = 263 +BRN_LIGHT_SIGNPIC = 264 +BRN_DARK_SIGNPIC = 265 +LIGHT_STONE_WALLPIC = 266 +DARK_STONE_WALLPIC = 267 +BRN_FLAGSTONE_LIGHT_2PIC = 268 +BRN_FLAGSTONE_DARK_2PIC = 269 +RUST_METAL_LIGHTPIC = 270 +RUST_METAL_DARKPIC = 271 +GRAY_METAL_LIGHTPIC = 272 +GRAY_METAL_DARKPIC = 273 +WEAK_STONE_LIGHTPIC = 274 +WEAK_STONE_DARKPIC = 275 +WEAK_GRAY_RFGSTN_LIGHTPIC = 276 +WEAK_GRAY_RFGSTN_DARKPIC = 277 +WEAK_CRYSTAL_LIGHTPIC = 278 +WEAK_CRYSTAL_DARKPIC = 279 +RED_MUD_LIGHTPIC = 280 +BRN_MUD_DARKPIC = 281 +RED_MUD_WEAK_LIGHTPIC = 282 +BRN_MUD_WEAK_DARKPIC = 283 +HORN_DOORPIC = 284 +CLOSED_DOOR_1PIC = 285 +DOOR_2PIC = 286 +WATER_LIGHT_WEAK_1PIC = 287 +WATER_LIGHT_WEAK_2PIC = 288 +WATER_LIGHT_WEAK_3PIC = 289 +WATER_DARK_WEAK_1PIC = 290 +WATER_DARK_WEAK_2PIC = 291 +WATER_DARK_WEAK_3PIC = 292 +WATER_LIGHT_1PIC = 293 +WATER_LIGHT_2PIC = 294 +WATER_LIGHT_3PIC = 295 +WATER_DARK_1PIC = 296 +WATER_DARK_2PIC = 297 +WATER_DARK_3PIC = 298 +TROLL_LIGHT_STONEPIC = 299 +TROLL_DARK_STONEPIC = 300 +TROLL_BLOODY_LT_STONEPIC = 301 +TROLL_BLOODY_DK_STONEPIC = 302 +LIGHT_BREATH_1PIC = 303 +LIGHT_BREATH_2PIC = 304 +LIGHT_BREATH_3PIC = 305 +DARK_BREATH_1PIC = 306 +DARK_BREATH_2PIC = 307 +DARK_BREATH_3PIC = 308 +EXP_WALL_1PIC = 309 +EXP_WALL_2PIC = 310 +EXP_WALL_3PIC = 311 +WATER_EXP_WALL_1PIC = 312 +WATER_EXP_WALL_2PIC = 313 +WATER_EXP_WALL_3PIC = 314 +W_GEN_DOOR1PIC = 315 +W_GEN_DOOR2PIC = 316 +W_CRYSTAL_DOORPIC = 317 +DMG_BRN_FSTN_LTPIC = 318 +DMG_BRN_FSTN_DKPIC = 319 +DMG_FIN_FSTN_LTPIC = 320 +DMG_FIN_FSTN_DKPIC = 321 +STEEL_DOOR1PIC = 322 +STEEL_DOOR2PIC = 323 +BRN_WINDOW_DARKPIC = 324 +GRY_DOOR_LTPIC = 325 +GRY_DOOR_DKPIC = 326 +BRN_DOOR_LTPIC = 327 +BRN_DOOR_DKPIC = 328 +GRY_FGSTN_LTPIC = 329 +GRY_FGSTN_DKPIC = 330 +KUDZU_WEAK_LIGHTPIC = 331 +KUDZU_WEAK_DARKPIC = 332 +LT_SKEL1PIC = 333 +DK_SKEL1PIC = 334 +LT_SKEL2PIC = 335 +DK_SKEL2PIC = 336 +MANICLE_LIGHT_WALLPIC = 337 +MANICLE_DARK_WALLPIC = 338 +TAP_1PIC = 339 +TAP_2PIC = 340 +TAP_3PIC = 341 +TAP_4PIC = 342 +TAP_5PIC = 343 +FINALWALLPIC = 344 +WATER_DOOR1_PIC = 345 +WATER_DOOR2_PIC = 346 +LASTWALLPIC = 347 + +HAND1PICM = 348 + +NORTHICONSPR = 349 + +LEVEL1TEXT = 640 +LEVEL2TEXT = 641 +LEVEL3TEXT = 642 +LEVEL4TEXT = 643 +LEVEL5TEXT = 644 +LEVEL6TEXT = 645 +LEVEL7TEXT = 646 +LEVEL8TEXT = 647 +LEVEL9TEXT = 648 +LEVEL10TEXT = 649 +LEVEL11TEXT = 650 +LEVEL12TEXT = 651 +LEVEL13TEXT = 652 +LEVEL14TEXT = 653 +LEVEL15TEXT = 654 +LEVEL16TEXT = 655 +LEVEL17TEXT = 656 +PIRACY = 657 + +SKELDUDE_LUMP_START = 22 +SKELDUDE_LUMP_END = 31 + +TOMBSTONES_LUMP_START = 32 +TOMBSTONES_LUMP_END = 34 + +OBJ_WARP_LUMP_START = 35 +OBJ_WARP_LUMP_END = 38 + +EYE_LUMP_START = 39 +EYE_LUMP_END = 49 + +ZOMBIE_LUMP_START = 50 +ZOMBIE_LUMP_END = 61 + +BOLT_LUMP_START = 62 +BOLT_LUMP_END = 64 + +NUKE_LUMP_START = 65 +NUKE_LUMP_END = 67 + +TIME_LUMP_START = 68 +TIME_LUMP_END = 69 + +O_WATER_CHEST_LUMP_START = 70 +O_WATER_CHEST_LUMP_END = 71 + +POTION_LUMP_START = 72 +POTION_LUMP_END = 72 + +RKEY_LUMP_START = 73 +RKEY_LUMP_END = 73 + +YKEY_LUMP_START = 74 +YKEY_LUMP_END = 74 + +GKEY_LUMP_START = 75 +GKEY_LUMP_END = 75 + +BKEY_LUMP_START = 76 +BKEY_LUMP_END = 76 + +RGEM_LUMP_START = 77 +RGEM_LUMP_END = 78 + +GGEM_LUMP_START = 79 +GGEM_LUMP_END = 80 + +BGEM_LUMP_START = 81 +BGEM_LUMP_END = 82 + +YGEM_LUMP_START = 83 +YGEM_LUMP_END = 84 + +PGEM_LUMP_START = 85 +PGEM_LUMP_END = 86 + +CHEST_LUMP_START = 87 +CHEST_LUMP_END = 87 + +PLAYER_LUMP_START = 88 +PLAYER_LUMP_END = 92 + +REDDEMON_LUMP_START = 93 +REDDEMON_LUMP_END = 103 + +MAGE_LUMP_START = 104 +MAGE_LUMP_END = 109 + +BAT_LUMP_START = 110 +BAT_LUMP_END = 115 + +GREL_LUMP_START = 116 +GREL_LUMP_END = 126 + +GODESS_LUMP_START = 127 +GODESS_LUMP_END = 136 + +ANT_LUMP_START = 137 +ANT_LUMP_END = 145 + +FATDEMON_LUMP_START = 146 +FATDEMON_LUMP_END = 157 + +SUCCUBUS_LUMP_START = 158 +SUCCUBUS_LUMP_END = 167 + +TREE_LUMP_START = 168 +TREE_LUMP_END = 177 + +DRAGON_LUMP_START = 178 +DRAGON_LUMP_END = 193 + +BUNNY_LUMP_START = 194 +BUNNY_LUMP_END = 204 + +ARCH1_LUMP_START = 205 +ARCH1_LUMP_END = 205 + +ARCH2_LUMP_START = 206 +ARCH2_LUMP_END = 206 + +ARCH3_LUMP_START = 207 +ARCH3_LUMP_END = 207 + +ARCH4_LUMP_START = 208 +ARCH4_LUMP_END = 208 + +ARCH5_LUMP_START = 209 +ARCH5_LUMP_END = 209 + +ARCH6_LUMP_START = 210 +ARCH6_LUMP_END = 210 + +ARCH7_LUMP_START = 211 +ARCH7_LUMP_END = 211 + +ARCH8_LUMP_START = 212 +ARCH8_LUMP_END = 212 + +ARCH9_LUMP_START = 213 +ARCH9_LUMP_END = 213 + +ARCH10_LUMP_START = 214 +ARCH10_LUMP_END = 214 + +ARCH11_LUMP_START = 215 +ARCH11_LUMP_END = 215 + +ARCH12_LUMP_START = 216 +ARCH12_LUMP_END = 216 + +ARCH13_LUMP_START = 217 +ARCH13_LUMP_END = 217 + +ANTHILL_LUMP_START = 218 +ANTHILL_LUMP_END = 218 + +COLUMN_LUMP_START = 219 +COLUMN_LUMP_END = 219 + +SULPHURGAS_LUMP_START = 220 +SULPHURGAS_LUMP_END = 222 + +FIREPOT_LUMP_START = 223 +FIREPOT_LUMP_END = 224 + +SKELHANG_LUMP_START = 225 +SKELHANG_LUMP_END = 225 + +FORCEFIELD_LUMP_START = 226 +FORCEFIELD_LUMP_END = 229 + +FOUNTAIN_LUMP_START = 230 +FOUNTAIN_LUMP_END = 230 + + +; +; Amount of each data item +; +NUMCHUNKS = 658 +NUMFONT = 1 +NUMFONTM = 0 +NUMPICS = 344 +NUMPICM = 1 +NUMSPRITES = 1 +NUMTILE8 = 108 +NUMTILE8M = 36 +NUMTILE16 = 216 +NUMTILE16M = 72 +NUMTILE32 = 0 +NUMTILE32M = 0 +NUMEXTERN = 18 +; +; File offsets for data items +; +STRUCTPIC = 0 +STRUCTPICM = 1 +STRUCTSPRITE = 2 + +STARTFONT = 3 +STARTFONTM = 4 +STARTPICS = 4 +STARTPICM = 348 +STARTSPRITES = 349 +STARTTILE8 = 350 +STARTTILE8M = 351 +STARTTILE16 = 352 +STARTTILE16M = 568 +STARTTILE32 = 640 +STARTTILE32M = 640 +STARTEXTERN = 640 + +; +; Thank you for using IGRAB! +; diff --git a/16/cawat/GFXE_ARM.H b/16/cawat/GFXE_ARM.H new file mode 100644 index 00000000..cbeb40a9 --- /dev/null +++ b/16/cawat/GFXE_ARM.H @@ -0,0 +1,647 @@ +/* Catacomb Armageddon Source Code + * Copyright (C) 1993-2014 Flat Rock Software + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +////////////////////////////////////// +// +// Graphics .H file for .ARM +// IGRAB-ed on Thu Dec 02 13:58:59 1993 +// +////////////////////////////////////// + +typedef enum { + FINALEPIC=4, + STATUSPIC, // 5 + FACE5PIC, // 6 + FIRSTLATCHPIC, // 7 + FACE1PIC, // 8 + FACE2PIC, // 9 + FACE3PIC, // 10 + FACE4PIC, // 11 + RADAR_TOPPIC, // 12 + RADAR_BOTTOMPIC, // 13 + RADAR_RGEMPIC, // 14 + RADAR_GGEMPIC, // 15 + RADAR_BGEMPIC, // 16 + RADAR_YGEMPIC, // 17 + RADAR_PGEMPIC, // 18 + FIRSTGROUNDPIC, // 19 + FIRSTSTRIPPIC, // 20 + FIRSTSCALEPIC, // 21 + // Lump Start + SKELETON_1PIC, // 22 + SKELETON_2PIC, // 23 + SKELETON_3PIC, // 24 + SKELETON_4PIC, // 25 + SKELETON_ATTACK_1PIC, // 26 + SKELETON_ATTACK_2PIC, // 27 + SKELETON_ATTACK_3PIC, // 28 + SKELETON_OUCHPIC, // 29 + SKELETON_DEATH_1PIC, // 30 + SKELETON_DEATH_2PIC, // 31 + // Lump Start + TOMB1PIC, // 32 + TOMB2PIC, // 33 + TOMB3PIC, // 34 + // Lump Start + OBJ_WARP1PIC, // 35 + OBJ_WARP2PIC, // 36 + OBJ_WARP3PIC, // 37 + OBJ_WARP4PIC, // 38 + // Lump Start + EYE_WALK1PIC, // 39 + EYE_WALK2PIC, // 40 + EYE_WALK3PIC, // 41 + EYE_OUCH1PIC, // 42 + EYE_OUCH2PIC, // 43 + EYE_DEATH1PIC, // 44 + EYE_DEATH2PIC, // 45 + EYE_DEATH3PIC, // 46 + EYE_SCOWLPIC, // 47 + EYE_SHOT1PIC, // 48 + EYE_SHOT2PIC, // 49 + // Lump Start + ZOMB_APPEAR1PIC, // 50 + ZOMB_APPEAR2PIC, // 51 + ZOMB_APPEAR3PIC, // 52 + ZOMB_APPEAR4PIC, // 53 + ZOMB_WALK1PIC, // 54 + ZOMB_WALK2PIC, // 55 + ZOMB_WALK3PIC, // 56 + ZOMB_OUCHPIC, // 57 + ZOMB_ATTACKPIC, // 58 + ZOMB_DIE1PIC, // 59 + ZOMB_DIE2PIC, // 60 + ZOMB_DIE3PIC, // 61 + // Lump Start + BOLTOBJPIC, // 62 + BOLT2OBJPIC, // 63 + BOLT3OBJPIC, // 64 + // Lump Start + NUKEOBJPIC, // 65 + NUKE2OBJPIC, // 66 + NUKE3OBJPIC, // 67 + // Lump Start + TIMEOBJ1PIC, // 68 + TIMEOBJ2PIC, // 69 + // Lump Start + O_WATER_CHEST1PIC, // 70 + O_WATER_CHEST2PIC, // 71 + // Lump Start + POTIONOBJPIC, // 72 + // Lump Start + RKEYOBJPIC, // 73 + // Lump Start + YKEYOBJPIC, // 74 + // Lump Start + GKEYOBJPIC, // 75 + // Lump Start + BKEYOBJPIC, // 76 + // Lump Start + RGEM1PIC, // 77 + RGEM2PIC, // 78 + // Lump Start + GGEM1PIC, // 79 + GGEM2PIC, // 80 + // Lump Start + BGEM1PIC, // 81 + BGEM2PIC, // 82 + // Lump Start + YGEM1PIC, // 83 + YGEM2PIC, // 84 + // Lump Start + PGEM1PIC, // 85 + PGEM2PIC, // 86 + // Lump Start + CHESTOBJPIC, // 87 + // Lump Start + PSHOT1PIC, // 88 + PSHOT2PIC, // 89 + PSHOT_EXP1PIC, // 90 + PSHOT_EXP2PIC, // 91 + PSHOT_EXP3PIC, // 92 + // Lump Start + RED_DEMON1PIC, // 93 + RED_DEMON2PIC, // 94 + RED_DEMON3PIC, // 95 + RED_DEMON4PIC, // 96 + RED_DEMONATTACK1PIC, // 97 + RED_DEMONATTACK2PIC, // 98 + RED_DEMONATTACK3PIC, // 99 + RED_DEMONOUCHPIC, // 100 + RED_DEMONDIE1PIC, // 101 + RED_DEMONDIE2PIC, // 102 + RED_DEMONDIE3PIC, // 103 + // Lump Start + MAGE1PIC, // 104 + MAGE2PIC, // 105 + MAGEOUCHPIC, // 106 + MAGEATTACKPIC, // 107 + MAGEDIE1PIC, // 108 + MAGEDIE2PIC, // 109 + // Lump Start + BAT1PIC, // 110 + BAT2PIC, // 111 + BAT3PIC, // 112 + BAT4PIC, // 113 + BATDIE1PIC, // 114 + BATDIE2PIC, // 115 + // Lump Start + GREL1PIC, // 116 + GREL2PIC, // 117 + GRELATTACKPIC, // 118 + GRELHITPIC, // 119 + GRELDIE1PIC, // 120 + GRELDIE2PIC, // 121 + GRELDIE3PIC, // 122 + GRELDIE4PIC, // 123 + GRELDIE5PIC, // 124 + GRELDIE6PIC, // 125 + SKULL_SHOTPIC, // 126 + // Lump Start + GODESS_WALK1PIC, // 127 + GODESS_WALK2PIC, // 128 + GODESS_WALK3PIC, // 129 + GODESS_ATTACK1PIC, // 130 + GODESS_ATTACK2PIC, // 131 + GODESS_ATTACK3PIC, // 132 + GODESS_STATUEPIC, // 133 + GODESS_OUCHPIC, // 134 + GODESS_DEATH1PIC, // 135 + GODESS_DEATH2PIC, // 136 + // Lump Start + ANT_EGG1PIC, // 137 + ANT_EGG2PIC, // 138 + ANT_WALK1PIC, // 139 + ANT_WALK2PIC, // 140 + ANT_WALK3PIC, // 141 + ANT_ATTACKPIC, // 142 + ANT_DEATH1PIC, // 143 + ANT_DEATH2PIC, // 144 + ANT_DEATH3PIC, // 145 + // Lump Start + FATDEMON_WALK1PIC, // 146 + FATDEMON_WALK2PIC, // 147 + FATDEMON_WALK3PIC, // 148 + FATDEMON_WALK4PIC, // 149 + FATDEMON_ATTACK1PIC, // 150 + FATDEMON_ATTACK2PIC, // 151 + FATDEMON_OUCHPIC, // 152 + FATDEMON_BLOWUP1PIC, // 153 + FATDEMON_BLOWUP2PIC, // 154 + FATDEMON_BLOWUP3PIC, // 155 + FATDEMON_EXPLODEPIC, // 156 + FATDEMON_FEETPIC, // 157 + // Lump Start + SUCCUBUS_WALK1PIC, // 158 + SUCCUBUS_WALK2PIC, // 159 + SUCCUBUS_WALK3PIC, // 160 + SUCCUBUS_WALK4PIC, // 161 + SUCCUBUS_ATTACK1PIC, // 162 + SUCCUBUS_ATTACK2PIC, // 163 + SUCCUBUS_OUCHPIC, // 164 + SUCCUBUS_DEATH1PIC, // 165 + SUCCUBUS_DEATH2PIC, // 166 + SUCCUBUS_SHOT1PIC, // 167 + // Lump Start + TREE_IDLEPIC, // 168 + TREE_AWAKENINGPIC, // 169 + TREE_WALK1PIC, // 170 + TREE_WALK2PIC, // 171 + TREE_ATTACK1PIC, // 172 + TREE_ATTACK2PIC, // 173 + TREE_ATTACK3PIC, // 174 + TREE_DEATH1PIC, // 175 + TREE_DEATH2PIC, // 176 + TREE_DEATH3PIC, // 177 + // Lump Start + DRAGON_BUBBLES1PIC, // 178 + DRAGON_BUBBLES2PIC, // 179 + DRAGON_EYESPIC, // 180 + DRAGON_RISE1PIC, // 181 + DRAGON_RISE2PIC, // 182 + DRAGON_WALK1PIC, // 183 + DRAGON_WALK2PIC, // 184 + DRAGON_WALK3PIC, // 185 + DRAGON_WALK4PIC, // 186 + DRAGON_ATTACK1PIC, // 187 + DRAGON_ATTACK2PIC, // 188 + DRAGON_ATTACK3PIC, // 189 + DRAGON_OUCHPIC, // 190 + DRAGON_DEATH1PIC, // 191 + DRAGON_DEATH2PIC, // 192 + DRAGON_DEATH3PIC, // 193 + // Lump Start + BUNNY_LEFT1PIC, // 194 + BUNNY_LEFT2PIC, // 195 + BUNNY_RIGHT1PIC, // 196 + BUNNY_RIGHT2PIC, // 197 + BUNNY_META1PIC, // 198 + BUNNY_META2PIC, // 199 + BUNNY_WALK1PIC, // 200 + BUNNY_WALK2PIC, // 201 + BUNNY_OUCHPIC, // 202 + BUNNY_DEATH1PIC, // 203 + BUNNY_DEATH2PIC, // 204 + // Lump Start + ARCH1PIC, // 205 + // Lump Start + ARCH2PIC, // 206 + // Lump Start + ARCH3PIC, // 207 + // Lump Start + ARCH4PIC, // 208 + // Lump Start + ARCH5PIC, // 209 + // Lump Start + ARCH6PIC, // 210 + // Lump Start + ARCH7PIC, // 211 + // Lump Start + ARCH8PIC, // 212 + // Lump Start + ARCH9PIC, // 213 + // Lump Start + ARCH10PIC, // 214 + // Lump Start + ARCH11PIC, // 215 + // Lump Start + ARCH12PIC, // 216 + // Lump Start + ARCH13PIC, // 217 + // Lump Start + ANT_HILLPIC, // 218 + // Lump Start + COLUMNPIC, // 219 + // Lump Start + SULPHUR_GAS_1PIC, // 220 + SULPHUR_GAS_2PIC, // 221 + SULPHUR_GAS_3PIC, // 222 + // Lump Start + FIRE_POT_1PIC, // 223 + FIRE_POT_2PIC, // 224 + // Lump Start + SKEL_HANGPIC, // 225 + // Lump Start + FORCE_FIELD_1PIC, // 226 + FORCE_FIELD_2PIC, // 227 + FORCE_FIELD_3PIC, // 228 + FORCE_FIELD_4PIC, // 229 + // Lump Start + WFOUNTAINPIC, // 230 + FIRSTWALLPIC, // 231 + CRYSTAL_LIGHT_1PIC, // 232 + CRYSTAL_LIGHT_2PIC, // 233 + CRYSTAL_LIGHT_3PIC, // 234 + CRYSTAL_LIGHT_4PIC, // 235 + CRYSTAL_DARK_1PIC, // 236 + CRYSTAL_DARK_2PIC, // 237 + CRYSTAL_DARK_3PIC, // 238 + CRYSTAL_DARK_4PIC, // 239 + FIRE_WALL_1PIC, // 240 + FIRE_WALL_2PIC, // 241 + FIRE_WALL_3PIC, // 242 + FIRE_WALL_4PIC, // 243 + BRN_STONE_GATEPIC, // 244 + BRN_STONE_WALL_1PIC, // 245 + BRN_STONE_WALL_2PIC, // 246 + KUDZU_LIGHT_WALLPIC, // 247 + KUDZU_DARK_WALLPIC, // 248 + HEDGE_WALLPIC, // 249 + HEDGE_EYESPIC, // 250 + BRN_WINDOW_LIGHTPIC, // 251 + ALTAR_LEFTPIC, // 252 + ALTAR_RIGHTPIC, // 253 + GRAY_LIGHT_WALLPIC, // 254 + GRAY_DARK_WALLPIC, // 255 + GRAY_LIGHT_SIGNPIC, // 256 + GRAY_DARK_SIGNPIC, // 257 + MANICLE_LIGHT_BLOODYPIC, // 258 + MANICLE_DARK_BLOODYPIC, // 259 + LIGHT_CURTAIN_WINDOWPIC, // 260 + LIGHT_CURTAIN_WALLPIC, // 261 + DARK_CURTAIN_WINDOWPIC, // 262 + DARK_CURTAIN_WALLPIC, // 263 + BRN_LIGHT_SIGNPIC, // 264 + BRN_DARK_SIGNPIC, // 265 + LIGHT_STONE_WALLPIC, // 266 + DARK_STONE_WALLPIC, // 267 + BRN_FLAGSTONE_LIGHT_2PIC, // 268 + BRN_FLAGSTONE_DARK_2PIC, // 269 + RUST_METAL_LIGHTPIC, // 270 + RUST_METAL_DARKPIC, // 271 + GRAY_METAL_LIGHTPIC, // 272 + GRAY_METAL_DARKPIC, // 273 + WEAK_STONE_LIGHTPIC, // 274 + WEAK_STONE_DARKPIC, // 275 + WEAK_GRAY_RFGSTN_LIGHTPIC, // 276 + WEAK_GRAY_RFGSTN_DARKPIC, // 277 + WEAK_CRYSTAL_LIGHTPIC, // 278 + WEAK_CRYSTAL_DARKPIC, // 279 + RED_MUD_LIGHTPIC, // 280 + BRN_MUD_DARKPIC, // 281 + RED_MUD_WEAK_LIGHTPIC, // 282 + BRN_MUD_WEAK_DARKPIC, // 283 + HORN_DOORPIC, // 284 + CLOSED_DOOR_1PIC, // 285 + DOOR_2PIC, // 286 + WATER_LIGHT_WEAK_1PIC, // 287 + WATER_LIGHT_WEAK_2PIC, // 288 + WATER_LIGHT_WEAK_3PIC, // 289 + WATER_DARK_WEAK_1PIC, // 290 + WATER_DARK_WEAK_2PIC, // 291 + WATER_DARK_WEAK_3PIC, // 292 + WATER_LIGHT_1PIC, // 293 + WATER_LIGHT_2PIC, // 294 + WATER_LIGHT_3PIC, // 295 + WATER_DARK_1PIC, // 296 + WATER_DARK_2PIC, // 297 + WATER_DARK_3PIC, // 298 + TROLL_LIGHT_STONEPIC, // 299 + TROLL_DARK_STONEPIC, // 300 + TROLL_BLOODY_LT_STONEPIC, // 301 + TROLL_BLOODY_DK_STONEPIC, // 302 + LIGHT_BREATH_1PIC, // 303 + LIGHT_BREATH_2PIC, // 304 + LIGHT_BREATH_3PIC, // 305 + DARK_BREATH_1PIC, // 306 + DARK_BREATH_2PIC, // 307 + DARK_BREATH_3PIC, // 308 + EXP_WALL_1PIC, // 309 + EXP_WALL_2PIC, // 310 + EXP_WALL_3PIC, // 311 + WATER_EXP_WALL_1PIC, // 312 + WATER_EXP_WALL_2PIC, // 313 + WATER_EXP_WALL_3PIC, // 314 + W_GEN_DOOR1PIC, // 315 + W_GEN_DOOR2PIC, // 316 + W_CRYSTAL_DOORPIC, // 317 + DMG_BRN_FSTN_LTPIC, // 318 + DMG_BRN_FSTN_DKPIC, // 319 + DMG_FIN_FSTN_LTPIC, // 320 + DMG_FIN_FSTN_DKPIC, // 321 + STEEL_DOOR1PIC, // 322 + STEEL_DOOR2PIC, // 323 + BRN_WINDOW_DARKPIC, // 324 + GRY_DOOR_LTPIC, // 325 + GRY_DOOR_DKPIC, // 326 + BRN_DOOR_LTPIC, // 327 + BRN_DOOR_DKPIC, // 328 + GRY_FGSTN_LTPIC, // 329 + GRY_FGSTN_DKPIC, // 330 + KUDZU_WEAK_LIGHTPIC, // 331 + KUDZU_WEAK_DARKPIC, // 332 + LT_SKEL1PIC, // 333 + DK_SKEL1PIC, // 334 + LT_SKEL2PIC, // 335 + DK_SKEL2PIC, // 336 + MANICLE_LIGHT_WALLPIC, // 337 + MANICLE_DARK_WALLPIC, // 338 + TAP_1PIC, // 339 + TAP_2PIC, // 340 + TAP_3PIC, // 341 + TAP_4PIC, // 342 + TAP_5PIC, // 343 + FINALWALLPIC, // 344 + WATER_DOOR1_PIC, // 345 + WATER_DOOR2_PIC, // 346 + LASTWALLPIC, // 347 + + HAND1PICM=348, + + NORTHICONSPR=349, + + LEVEL1TEXT=640, + LEVEL2TEXT, // 641 + LEVEL3TEXT, // 642 + LEVEL4TEXT, // 643 + LEVEL5TEXT, // 644 + LEVEL6TEXT, // 645 + LEVEL7TEXT, // 646 + LEVEL8TEXT, // 647 + LEVEL9TEXT, // 648 + LEVEL10TEXT, // 649 + LEVEL11TEXT, // 650 + LEVEL12TEXT, // 651 + LEVEL13TEXT, // 652 + LEVEL14TEXT, // 653 + LEVEL15TEXT, // 654 + LEVEL16TEXT, // 655 + LEVEL17TEXT, // 656 + PIRACY, // 657 + ENUMEND + } graphicnums; + +// +// Data LUMPs +// +#define SKELDUDE_LUMP_START 22 +#define SKELDUDE_LUMP_END 31 + +#define TOMBSTONES_LUMP_START 32 +#define TOMBSTONES_LUMP_END 34 + +#define OBJ_WARP_LUMP_START 35 +#define OBJ_WARP_LUMP_END 38 + +#define EYE_LUMP_START 39 +#define EYE_LUMP_END 49 + +#define ZOMBIE_LUMP_START 50 +#define ZOMBIE_LUMP_END 61 + +#define BOLT_LUMP_START 62 +#define BOLT_LUMP_END 64 + +#define NUKE_LUMP_START 65 +#define NUKE_LUMP_END 67 + +#define TIME_LUMP_START 68 +#define TIME_LUMP_END 69 + +#define O_WATER_CHEST_LUMP_START 70 +#define O_WATER_CHEST_LUMP_END 71 + +#define POTION_LUMP_START 72 +#define POTION_LUMP_END 72 + +#define RKEY_LUMP_START 73 +#define RKEY_LUMP_END 73 + +#define YKEY_LUMP_START 74 +#define YKEY_LUMP_END 74 + +#define GKEY_LUMP_START 75 +#define GKEY_LUMP_END 75 + +#define BKEY_LUMP_START 76 +#define BKEY_LUMP_END 76 + +#define RGEM_LUMP_START 77 +#define RGEM_LUMP_END 78 + +#define GGEM_LUMP_START 79 +#define GGEM_LUMP_END 80 + +#define BGEM_LUMP_START 81 +#define BGEM_LUMP_END 82 + +#define YGEM_LUMP_START 83 +#define YGEM_LUMP_END 84 + +#define PGEM_LUMP_START 85 +#define PGEM_LUMP_END 86 + +#define CHEST_LUMP_START 87 +#define CHEST_LUMP_END 87 + +#define PLAYER_LUMP_START 88 +#define PLAYER_LUMP_END 92 + +#define REDDEMON_LUMP_START 93 +#define REDDEMON_LUMP_END 103 + +#define MAGE_LUMP_START 104 +#define MAGE_LUMP_END 109 + +#define BAT_LUMP_START 110 +#define BAT_LUMP_END 115 + +#define GREL_LUMP_START 116 +#define GREL_LUMP_END 126 + +#define GODESS_LUMP_START 127 +#define GODESS_LUMP_END 136 + +#define ANT_LUMP_START 137 +#define ANT_LUMP_END 145 + +#define FATDEMON_LUMP_START 146 +#define FATDEMON_LUMP_END 157 + +#define SUCCUBUS_LUMP_START 158 +#define SUCCUBUS_LUMP_END 167 + +#define TREE_LUMP_START 168 +#define TREE_LUMP_END 177 + +#define DRAGON_LUMP_START 178 +#define DRAGON_LUMP_END 193 + +#define BUNNY_LUMP_START 194 +#define BUNNY_LUMP_END 204 + +#define ARCH1_LUMP_START 205 +#define ARCH1_LUMP_END 205 + +#define ARCH2_LUMP_START 206 +#define ARCH2_LUMP_END 206 + +#define ARCH3_LUMP_START 207 +#define ARCH3_LUMP_END 207 + +#define ARCH4_LUMP_START 208 +#define ARCH4_LUMP_END 208 + +#define ARCH5_LUMP_START 209 +#define ARCH5_LUMP_END 209 + +#define ARCH6_LUMP_START 210 +#define ARCH6_LUMP_END 210 + +#define ARCH7_LUMP_START 211 +#define ARCH7_LUMP_END 211 + +#define ARCH8_LUMP_START 212 +#define ARCH8_LUMP_END 212 + +#define ARCH9_LUMP_START 213 +#define ARCH9_LUMP_END 213 + +#define ARCH10_LUMP_START 214 +#define ARCH10_LUMP_END 214 + +#define ARCH11_LUMP_START 215 +#define ARCH11_LUMP_END 215 + +#define ARCH12_LUMP_START 216 +#define ARCH12_LUMP_END 216 + +#define ARCH13_LUMP_START 217 +#define ARCH13_LUMP_END 217 + +#define ANTHILL_LUMP_START 218 +#define ANTHILL_LUMP_END 218 + +#define COLUMN_LUMP_START 219 +#define COLUMN_LUMP_END 219 + +#define SULPHURGAS_LUMP_START 220 +#define SULPHURGAS_LUMP_END 222 + +#define FIREPOT_LUMP_START 223 +#define FIREPOT_LUMP_END 224 + +#define SKELHANG_LUMP_START 225 +#define SKELHANG_LUMP_END 225 + +#define FORCEFIELD_LUMP_START 226 +#define FORCEFIELD_LUMP_END 229 + +#define FOUNTAIN_LUMP_START 230 +#define FOUNTAIN_LUMP_END 230 + + +// +// Amount of each data item +// +#define NUMCHUNKS 658 +#define NUMFONT 1 +#define NUMFONTM 0 +#define NUMPICS 344 +#define NUMPICM 1 +#define NUMSPRITES 1 +#define NUMTILE8 108 +#define NUMTILE8M 36 +#define NUMTILE16 216 +#define NUMTILE16M 72 +#define NUMTILE32 0 +#define NUMTILE32M 0 +#define NUMEXTERNS 18 +// +// File offsets for data items +// +#define STRUCTPIC 0 +#define STRUCTPICM 1 +#define STRUCTSPRITE 2 + +#define STARTFONT 3 +#define STARTFONTM 4 +#define STARTPICS 4 +#define STARTPICM 348 +#define STARTSPRITES 349 +#define STARTTILE8 350 +#define STARTTILE8M 351 +#define STARTTILE16 352 +#define STARTTILE16M 568 +#define STARTTILE32 640 +#define STARTTILE32M 640 +#define STARTEXTERNS 640 + +// +// Thank you for using IGRAB! +// diff --git a/16/cawat/ID_ASM.EQU b/16/cawat/ID_ASM.EQU new file mode 100644 index 00000000..01fe696c --- /dev/null +++ b/16/cawat/ID_ASM.EQU @@ -0,0 +1,114 @@ +; +; Equates for all .ASM files +; + +;---------------------------------------------------------------------------- + +INCLUDE "GFXE_ARM.EQU" + +;---------------------------------------------------------------------------- + +CGAGR = 1 +EGAGR = 2 +VGAGR = 3 + +GRMODE = EGAGR +PROFILE = 0 ; 1=keep stats on tile drawing + +SC_INDEX = 03C4h +SC_RESET = 0 +SC_CLOCK = 1 +SC_MAPMASK = 2 +SC_CHARMAP = 3 +SC_MEMMODE = 4 + +CRTC_INDEX = 03D4h +CRTC_H_TOTAL = 0 +CRTC_H_DISPEND = 1 +CRTC_H_BLANK = 2 +CRTC_H_ENDBLANK = 3 +CRTC_H_RETRACE = 4 +CRTC_H_ENDRETRACE = 5 +CRTC_V_TOTAL = 6 +CRTC_OVERFLOW = 7 +CRTC_ROWSCAN = 8 +CRTC_MAXSCANLINE = 9 +CRTC_CURSORSTART = 10 +CRTC_CURSOREND = 11 +CRTC_STARTHIGH = 12 +CRTC_STARTLOW = 13 +CRTC_CURSORHIGH = 14 +CRTC_CURSORLOW = 15 +CRTC_V_RETRACE = 16 +CRTC_V_ENDRETRACE = 17 +CRTC_V_DISPEND = 18 +CRTC_OFFSET = 19 +CRTC_UNDERLINE = 20 +CRTC_V_BLANK = 21 +CRTC_V_ENDBLANK = 22 +CRTC_MODE = 23 +CRTC_LINECOMPARE = 24 + + +GC_INDEX = 03CEh +GC_SETRESET = 0 +GC_ENABLESETRESET = 1 +GC_COLORCOMPARE = 2 +GC_DATAROTATE = 3 +GC_READMAP = 4 +GC_MODE = 5 +GC_MISCELLANEOUS = 6 +GC_COLORDONTCARE = 7 +GC_BITMASK = 8 + +ATR_INDEX = 03c0h +ATR_MODE = 16 +ATR_OVERSCAN = 17 +ATR_COLORPLANEENABLE = 18 +ATR_PELPAN = 19 +ATR_COLORSELECT = 20 + +STATUS_REGISTER_1 = 03dah + + +MACRO WORDOUT + out dx,ax +ENDM + +if 0 + +MACRO WORDOUT + out dx,al + inc dx + xchg al,ah + out dx,al + dec dx + xchg al,ah +ENDM + +endif + +UPDATEWIDE = 22 +UPDATEHIGH = 13 ; hack for catacombs + +; +; tile info offsets from segment tinf +; + +SPEED = 402 +ANIM = (SPEED+NUMTILE16) + +NORTHWALL = (ANIM+NUMTILE16) +EASTWALL = (NORTHWALL+NUMTILE16M) +SOUTHWALL = (EASTWALL+NUMTILE16M) +WESTWALL = (SOUTHWALL+NUMTILE16M) +MANIM = (WESTWALL+NUMTILE16M) +INTILE = (MANIM+NUMTILE16M) +MSPEED = (INTILE+NUMTILE16M) + +IFE GRMODE-EGAGR +SCREENWIDTH = 40 +ENDIF +IFE GRMODE-CGAGR +SCREENWIDTH = 128 +ENDIF diff --git a/16/cawat/ID_CA.C b/16/cawat/ID_CA.C new file mode 100644 index 00000000..31cf7bfe --- /dev/null +++ b/16/cawat/ID_CA.C @@ -0,0 +1,2190 @@ +/* Catacomb Armageddon Source Code + * Copyright (C) 1993-2014 Flat Rock Software + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +// ID_CA.C + +/* +============================================================================= + +Id Software Caching Manager +--------------------------- + +Must be started BEFORE the memory manager, because it needs to get the headers +loaded into the data segment + +============================================================================= +*/ + +#include "LIB_HEAD.H" +#pragma hdrstop +//#include "ID_STRS.H" + +#pragma warn -pro +#pragma warn -use + +#define THREEBYTEGRSTARTS + +/* +============================================================================= + + LOCAL CONSTANTS + +============================================================================= +*/ + +typedef struct +{ + unsigned bit0,bit1; // 0-255 is a character, > is a pointer to a node +} huffnode; + + +typedef struct +{ + unsigned RLEWtag; + long headeroffsets[100]; + byte tileinfo[]; +} mapfiletype; + + +/* +============================================================================= + + GLOBAL VARIABLES + +============================================================================= +*/ + +byte _seg *tinf; +int mapon; + +unsigned _seg *mapsegs[3]; +maptype _seg *mapheaderseg[NUMMAPS]; +byte _seg *audiosegs[NUMSNDCHUNKS]; +void _seg *grsegs[NUMCHUNKS]; + +byte far grneeded[NUMCHUNKS]; +byte ca_levelbit,ca_levelnum; + +int profilehandle,debughandle; + +void (*drawcachebox) (char *title, unsigned numcache); +void (*updatecachebox) (void); +void (*finishcachebox) (void); + +/* +============================================================================= + + LOCAL VARIABLES + +============================================================================= +*/ + +extern long far CGAhead; +extern long far EGAhead; +extern byte CGAdict; +extern byte EGAdict; +extern byte far maphead; +extern byte mapdict; +extern byte far audiohead; +extern byte audiodict; + + +long _seg *grstarts; // array of offsets in egagraph, -1 for sparse +long _seg *audiostarts; // array of offsets in audio / audiot + +#ifdef GRHEADERLINKED +huffnode *grhuffman; +#else +huffnode grhuffman[255]; +#endif + +#ifdef AUDIOHEADERLINKED +huffnode *audiohuffman; +#else +huffnode audiohuffman[255]; +#endif + + +int grhandle; // handle to EGAGRAPH +int maphandle; // handle to MAPTEMP / GAMEMAPS +int audiohandle; // handle to AUDIOT / AUDIO + +long chunkcomplen,chunkexplen; + +SDMode oldsoundmode; + + + +void CAL_DialogDraw (char *title,unsigned numcache); +void CAL_DialogUpdate (void); +void CAL_DialogFinish (void); +void CAL_CarmackExpand (unsigned far *source, unsigned far *dest, + unsigned length); + + +#ifdef THREEBYTEGRSTARTS +#define FILEPOSSIZE 3 +//#define GRFILEPOS(c) (*(long far *)(((byte far *)grstarts)+(c)*3)&0xffffff) +long GRFILEPOS(int c) +{ + long value; + int offset; + + offset = c*3; + + value = *(long far *)(((byte far *)grstarts)+offset); + + value &= 0x00ffffffl; + + if (value == 0xffffffl) + value = -1; + + return value; +}; +#else +#define FILEPOSSIZE 4 +#define GRFILEPOS(c) (grstarts[c]) +#endif + +/* +============================================================================= + + LOW LEVEL ROUTINES + +============================================================================= +*/ + +/* +============================ += += CA_OpenDebug / CA_CloseDebug += += Opens a binary file with the handle "debughandle" += +============================ +*/ + +void CA_OpenDebug (void) +{ + unlink ("DEBUG.TXT"); + debughandle = open("DEBUG.TXT", O_CREAT | O_WRONLY | O_TEXT); +} + +void CA_CloseDebug (void) +{ + close (debughandle); +} + + + +/* +============================ += += CAL_GetGrChunkLength += += Gets the length of an explicit length chunk (not tiles) += The file pointer is positioned so the compressed data can be read in next. += +============================ +*/ + +void CAL_GetGrChunkLength (int chunk) +{ + lseek(grhandle,GRFILEPOS(chunk),SEEK_SET); + read(grhandle,&chunkexplen,sizeof(chunkexplen)); + chunkcomplen = GRFILEPOS(chunk+1)-GRFILEPOS(chunk)-4; +} + + +/* +========================== += += CA_FarRead += += Read from a file to a far pointer += +========================== +*/ + +boolean CA_FarRead (int handle, byte far *dest, long length) +{ + if (length>0xffffl) + Quit ("CA_FarRead doesn't support 64K reads yet!"); + +asm push ds +asm mov bx,[handle] +asm mov cx,[WORD PTR length] +asm mov dx,[WORD PTR dest] +asm mov ds,[WORD PTR dest+2] +asm mov ah,0x3f // READ w/handle +asm int 21h +asm pop ds +asm jnc good + errno = _AX; + return false; +good: +asm cmp ax,[WORD PTR length] +asm je done + errno = EINVFMT; // user manager knows this is bad read + return false; +done: + return true; +} + + +/* +========================== += += CA_SegWrite += += Write from a file to a far pointer += +========================== +*/ + +boolean CA_FarWrite (int handle, byte far *source, long length) +{ + if (length>0xffffl) + Quit ("CA_FarWrite doesn't support 64K reads yet!"); + +asm push ds +asm mov bx,[handle] +asm mov cx,[WORD PTR length] +asm mov dx,[WORD PTR source] +asm mov ds,[WORD PTR source+2] +asm mov ah,0x40 // WRITE w/handle +asm int 21h +asm pop ds +asm jnc good + errno = _AX; + return false; +good: +asm cmp ax,[WORD PTR length] +asm je done + errno = ENOMEM; // user manager knows this is bad write + return false; + +done: + return true; +} + + +/* +========================== += += CA_ReadFile += += Reads a file into an allready allocated buffer += +========================== +*/ + +boolean CA_ReadFile (char *filename, memptr *ptr) +{ + int handle; + long size; + + if ((handle = open(filename,O_RDONLY | O_BINARY, S_IREAD)) == -1) + return false; + + size = filelength (handle); + if (!CA_FarRead (handle,*ptr,size)) + { + close (handle); + return false; + } + close (handle); + return true; +} + + + +/* +========================== += += CA_LoadFile += += Allocate space for and load a file += +========================== +*/ + +boolean CA_LoadFile (char *filename, memptr *ptr) +{ + int handle; + long size; + + if ((handle = open(filename,O_RDONLY | O_BINARY, S_IREAD)) == -1) + return false; + + size = filelength (handle); + MM_GetPtr (ptr,size); + if (!CA_FarRead (handle,*ptr,size)) + { + close (handle); + return false; + } + close (handle); + return true; +} + +/* +============================================================================ + + COMPRESSION routines, see JHUFF.C for more + +============================================================================ +*/ + + + +/* +=============== += += CAL_OptimizeNodes += += Goes through a huffman table and changes the 256-511 node numbers to the += actular address of the node. Must be called before CAL_HuffExpand += +=============== +*/ + +void CAL_OptimizeNodes (huffnode *table) +{ + huffnode *node; + int i; + + node = table; + + for (i=0;i<255;i++) + { + if (node->bit0 >= 256) + node->bit0 = (unsigned)(table+(node->bit0-256)); + if (node->bit1 >= 256) + node->bit1 = (unsigned)(table+(node->bit1-256)); + node++; + } +} + + + +/* +====================== += += CAL_HuffExpand += += Length is the length of the EXPANDED data += +====================== +*/ + +void CAL_HuffExpand (byte huge *source, byte huge *dest, + long length,huffnode *hufftable) +{ +// unsigned bit,byte,node,code; + unsigned sourceseg,sourceoff,destseg,destoff,endoff; + huffnode *headptr; +// huffnode *nodeon; + + headptr = hufftable+254; // head node is allways node 254 + + source++; // normalize + source--; + dest++; + dest--; + + sourceseg = FP_SEG(source); + sourceoff = FP_OFF(source); + destseg = FP_SEG(dest); + destoff = FP_OFF(dest); + endoff = destoff+length; + +// +// ds:si source +// es:di dest +// ss:bx node pointer +// + + if (length <0xfff0) + { + +//-------------------------- +// expand less than 64k of data +//-------------------------- + +asm mov bx,[headptr] + +asm mov si,[sourceoff] +asm mov di,[destoff] +asm mov es,[destseg] +asm mov ds,[sourceseg] +asm mov ax,[endoff] + +asm mov ch,[si] // load first byte +asm inc si +asm mov cl,1 + +expandshort: +asm test ch,cl // bit set? +asm jnz bit1short +asm mov dx,[ss:bx] // take bit0 path from node +asm shl cl,1 // advance to next bit position +asm jc newbyteshort +asm jnc sourceupshort + +bit1short: +asm mov dx,[ss:bx+2] // take bit1 path +asm shl cl,1 // advance to next bit position +asm jnc sourceupshort + +newbyteshort: +asm mov ch,[si] // load next byte +asm inc si +asm mov cl,1 // back to first bit + +sourceupshort: +asm or dh,dh // if dx<256 its a byte, else move node +asm jz storebyteshort +asm mov bx,dx // next node = (huffnode *)code +asm jmp expandshort + +storebyteshort: +asm mov [es:di],dl +asm inc di // write a decopmpressed byte out +asm mov bx,[headptr] // back to the head node for next bit + +asm cmp di,ax // done? +asm jne expandshort + } + else + { + +//-------------------------- +// expand more than 64k of data +//-------------------------- + + length--; + +asm mov bx,[headptr] +asm mov cl,1 + +asm mov si,[sourceoff] +asm mov di,[destoff] +asm mov es,[destseg] +asm mov ds,[sourceseg] + +asm lodsb // load first byte + +expand: +asm test al,cl // bit set? +asm jnz bit1 +asm mov dx,[ss:bx] // take bit0 path from node +asm jmp gotcode +bit1: +asm mov dx,[ss:bx+2] // take bit1 path + +gotcode: +asm shl cl,1 // advance to next bit position +asm jnc sourceup +asm lodsb +asm cmp si,0x10 // normalize ds:si +asm jb sinorm +asm mov cx,ds +asm inc cx +asm mov ds,cx +asm xor si,si +sinorm: +asm mov cl,1 // back to first bit + +sourceup: +asm or dh,dh // if dx<256 its a byte, else move node +asm jz storebyte +asm mov bx,dx // next node = (huffnode *)code +asm jmp expand + +storebyte: +asm mov [es:di],dl +asm inc di // write a decopmpressed byte out +asm mov bx,[headptr] // back to the head node for next bit + +asm cmp di,0x10 // normalize es:di +asm jb dinorm +asm mov dx,es +asm inc dx +asm mov es,dx +asm xor di,di +dinorm: + +asm sub [WORD PTR ss:length],1 +asm jnc expand +asm dec [WORD PTR ss:length+2] +asm jns expand // when length = ffff ffff, done + + } + +asm mov ax,ss +asm mov ds,ax + +} + + +/* +====================== += += CAL_CarmackExpand += += Length is the length of the EXPANDED data += +====================== +*/ + +#define NEARTAG 0xa7 +#define FARTAG 0xa8 + +void CAL_CarmackExpand (unsigned far *source, unsigned far *dest, unsigned length) +{ + unsigned ch,chhigh,count,offset; + unsigned far *copyptr, far *inptr, far *outptr; + + length/=2; + + inptr = source; + outptr = dest; + + while (length) + { + ch = *inptr++; + chhigh = ch>>8; + if (chhigh == NEARTAG) + { + count = ch&0xff; + if (!count) + { // have to insert a word containing the tag byte + ch |= *((unsigned char far *)inptr)++; + *outptr++ = ch; + length--; + } + else + { + offset = *((unsigned char far *)inptr)++; + copyptr = outptr - offset; + length -= count; + while (count--) + *outptr++ = *copyptr++; + } + } + else if (chhigh == FARTAG) + { + count = ch&0xff; + if (!count) + { // have to insert a word containing the tag byte + ch |= *((unsigned char far *)inptr)++; + *outptr++ = ch; + length --; + } + else + { + offset = *inptr++; + copyptr = dest + offset; + length -= count; + while (count--) + *outptr++ = *copyptr++; + } + } + else + { + *outptr++ = ch; + length --; + } + } +} + + + +/* +====================== += += CA_RLEWcompress += +====================== +*/ + +long CA_RLEWCompress (unsigned huge *source, long length, unsigned huge *dest, + unsigned rlewtag) +{ + long complength; + unsigned value,count,i; + unsigned huge *start,huge *end; + + start = dest; + + end = source + (length+1)/2; + +// +// compress it +// + do + { + count = 1; + value = *source++; + while (*source == value && source3 || value == rlewtag) + { + // + // send a tag / count / value string + // + *dest++ = rlewtag; + *dest++ = count; + *dest++ = value; + } + else + { + // + // send word without compressing + // + for (i=1;i<=count;i++) + *dest++ = value; + } + + } while (source0 + MM_GetPtr(&(memptr)pictable,NUMPICS*sizeof(pictabletype)); + CAL_GetGrChunkLength(STRUCTPIC); // position file pointer + MM_GetPtr(&compseg,chunkcomplen); + CA_FarRead (grhandle,compseg,chunkcomplen); + CAL_HuffExpand (compseg, (byte huge *)pictable,NUMPICS*sizeof(pictabletype),grhuffman); + MM_FreePtr(&compseg); +#endif + +#if NUMPICM>0 + MM_GetPtr(&(memptr)picmtable,NUMPICM*sizeof(pictabletype)); + CAL_GetGrChunkLength(STRUCTPICM); // position file pointer + MM_GetPtr(&compseg,chunkcomplen); + CA_FarRead (grhandle,compseg,chunkcomplen); + CAL_HuffExpand (compseg, (byte huge *)picmtable,NUMPICS*sizeof(pictabletype),grhuffman); + MM_FreePtr(&compseg); +#endif + +#if NUMSPRITES>0 + MM_GetPtr(&(memptr)spritetable,NUMSPRITES*sizeof(spritetabletype)); + CAL_GetGrChunkLength(STRUCTSPRITE); // position file pointer + MM_GetPtr(&compseg,chunkcomplen); + CA_FarRead (grhandle,compseg,chunkcomplen); + CAL_HuffExpand (compseg, (byte huge *)spritetable,NUMSPRITES*sizeof(spritetabletype),grhuffman); + MM_FreePtr(&compseg); +#endif + +} + +//========================================================================== + + +/* +====================== += += CAL_SetupMapFile += +====================== +*/ + +void CAL_SetupMapFile (void) +{ + int handle; + long length; + +// +// load maphead.ext (offsets and tileinfo for map file) +// +#ifndef MAPHEADERLINKED + if ((handle = open("MAPHEAD."EXT, + O_RDONLY | O_BINARY, S_IREAD)) == -1) + Quit ("Can't open MAPHEAD."EXT"!"); + length = filelength(handle); + MM_GetPtr (&(memptr)tinf,length); + CA_FarRead(handle, tinf, length); + close(handle); +#else + + tinf = (byte _seg *)FP_SEG(&maphead); + +#endif + +// +// open the data file +// +#ifdef MAPHEADERLINKED + if ((maphandle = open("GAMEMAPS."EXT, + O_RDONLY | O_BINARY, S_IREAD)) == -1) + Quit ("Can't open GAMEMAPS."EXT"!"); +#else + if ((maphandle = open("MAPTEMP."EXT, + O_RDONLY | O_BINARY, S_IREAD)) == -1) + Quit ("Can't open MAPTEMP."EXT"!"); +#endif +} + +//========================================================================== + + +/* +====================== += += CAL_SetupAudioFile += +====================== +*/ + +void CAL_SetupAudioFile (void) +{ + int handle; + long length; + +// +// load maphead.ext (offsets and tileinfo for map file) +// +#ifndef AUDIOHEADERLINKED + if ((handle = open("AUDIOHED."EXT, + O_RDONLY | O_BINARY, S_IREAD)) == -1) + Quit ("Can't open AUDIOHED."EXT"!"); + length = filelength(handle); + MM_GetPtr (&(memptr)audiostarts,length); + CA_FarRead(handle, (byte far *)audiostarts, length); + close(handle); +#else + audiohuffman = (huffnode *)&audiodict; + CAL_OptimizeNodes (audiohuffman); + audiostarts = (long _seg *)FP_SEG(&audiohead); +#endif + +// +// open the data file +// +#ifndef AUDIOHEADERLINKED + if ((audiohandle = open("AUDIOT."EXT, + O_RDONLY | O_BINARY, S_IREAD)) == -1) + Quit ("Can't open AUDIOT."EXT"!"); +#else + if ((audiohandle = open("AUDIO."EXT, + O_RDONLY | O_BINARY, S_IREAD)) == -1) + Quit ("Can't open AUDIO."EXT"!"); +#endif +} + +//========================================================================== + + +/* +====================== += += CA_Startup += += Open all files and load in headers += +====================== +*/ + +void CA_Startup (void) +{ +#ifdef PROFILE + unlink ("PROFILE.TXT"); + profilehandle = open("PROFILE.TXT", O_CREAT | O_WRONLY | O_TEXT); +#endif + +// MDM begin - (GAMERS EDGE) +// + if (!FindFile("AUDIO."EXT,NULL,2)) + Quit("CA_Startup(): Can't find audio files."); +// +// MDM end + +#ifndef NOAUDIO + CAL_SetupAudioFile (); +#endif + +// MDM begin - (GAMERS EDGE) +// + if (!FindFile("GAMEMAPS."EXT,NULL,1)) + Quit("CA_Startup(): Can't find level files."); +// +// MDM end + +#ifndef NOMAPS + CAL_SetupMapFile (); +#endif + +// MDM begin - (GAMERS EDGE) +// + if (!FindFile("EGAGRAPH."EXT,NULL,2)) + Quit("CA_Startup(): Can't find graphics files."); +// +// MDM end + +#ifndef NOGRAPHICS + CAL_SetupGrFile (); +#endif + + mapon = -1; + ca_levelbit = 1; + ca_levelnum = 0; + + drawcachebox = CAL_DialogDraw; + updatecachebox = CAL_DialogUpdate; + finishcachebox = CAL_DialogFinish; +} + +//========================================================================== + + +/* +====================== += += CA_Shutdown += += Closes all files += +====================== +*/ + +void CA_Shutdown (void) +{ +#ifdef PROFILE + close (profilehandle); +#endif + + close (maphandle); + close (grhandle); + close (audiohandle); +} + +//=========================================================================== + +/* +====================== += += CA_CacheAudioChunk += +====================== +*/ + +void CA_CacheAudioChunk (int chunk) +{ + long pos,compressed; +#ifdef AUDIOHEADERLINKED + long expanded; + memptr bigbufferseg; + byte far *source; +#endif + + if (audiosegs[chunk]) + { + MM_SetPurge (&(memptr)audiosegs[chunk],0); + return; // allready in memory + } + +// MDM begin - (GAMERS EDGE) +// + if (!FindFile("AUDIO."EXT,NULL,2)) + Quit("CA_CacheAudioChunk(): Can't find audio files."); +// +// MDM end + +// +// load the chunk into a buffer, either the miscbuffer if it fits, or allocate +// a larger buffer +// + pos = audiostarts[chunk]; + compressed = audiostarts[chunk+1]-pos; + + lseek(audiohandle,pos,SEEK_SET); + +#ifndef AUDIOHEADERLINKED + + MM_GetPtr (&(memptr)audiosegs[chunk],compressed); + if (mmerror) + return; + + CA_FarRead(audiohandle,audiosegs[chunk],compressed); + +#else + + if (compressed<=BUFFERSIZE) + { + CA_FarRead(audiohandle,bufferseg,compressed); + source = bufferseg; + } + else + { + MM_GetPtr(&bigbufferseg,compressed); + if (mmerror) + return; + MM_SetLock (&bigbufferseg,true); + CA_FarRead(audiohandle,bigbufferseg,compressed); + source = bigbufferseg; + } + + expanded = *(long far *)source; + source += 4; // skip over length + MM_GetPtr (&(memptr)audiosegs[chunk],expanded); + if (mmerror) + goto done; + CAL_HuffExpand (source,audiosegs[chunk],expanded,audiohuffman); + +done: + if (compressed>BUFFERSIZE) + MM_FreePtr(&bigbufferseg); +#endif +} + +//=========================================================================== + +/* +====================== += += CA_LoadAllSounds += += Purges all sounds, then loads all new ones (mode switch) += +====================== +*/ + +void CA_LoadAllSounds (void) +{ + unsigned start,i; + + switch (oldsoundmode) + { + case sdm_Off: + goto cachein; + case sdm_PC: + start = STARTPCSOUNDS; + break; + case sdm_AdLib: + start = STARTADLIBSOUNDS; + break; + } + + for (i=0;iwidth*spr->height; + MM_GetPtr (&grsegs[chunk],smallplane*2+MAXSHIFTS*6); + if (mmerror) + return; + dest = (spritetype _seg *)grsegs[chunk]; + dest->sourceoffset[0] = MAXSHIFTS*6; // start data after 3 unsigned tables + dest->planesize[0] = smallplane; + dest->width[0] = spr->width; + +// +// expand the unshifted shape +// + CAL_HuffExpand (compressed, &dest->data[0],smallplane*2,grhuffman); + +#endif + + +#if GRMODE == EGAGR + +// +// calculate sizes +// + spr = &spritetable[chunk-STARTSPRITES]; + smallplane = spr->width*spr->height; + bigplane = (spr->width+1)*spr->height; + + shiftstarts[0] = MAXSHIFTS*6; // start data after 3 unsigned tables + shiftstarts[1] = shiftstarts[0] + smallplane*5; // 5 planes in a sprite + shiftstarts[2] = shiftstarts[1] + bigplane*5; + shiftstarts[3] = shiftstarts[2] + bigplane*5; + shiftstarts[4] = shiftstarts[3] + bigplane*5; // nothing ever put here + + expanded = shiftstarts[spr->shifts]; + MM_GetPtr (&grsegs[chunk],expanded); + if (mmerror) + return; + dest = (spritetype _seg *)grsegs[chunk]; + +// +// expand the unshifted shape +// + CAL_HuffExpand (compressed, &dest->data[0],smallplane*5,grhuffman); + +// +// make the shifts! +// + switch (spr->shifts) + { + case 1: + for (i=0;i<4;i++) + { + dest->sourceoffset[i] = shiftstarts[0]; + dest->planesize[i] = smallplane; + dest->width[i] = spr->width; + } + break; + + case 2: + for (i=0;i<2;i++) + { + dest->sourceoffset[i] = shiftstarts[0]; + dest->planesize[i] = smallplane; + dest->width[i] = spr->width; + } + for (i=2;i<4;i++) + { + dest->sourceoffset[i] = shiftstarts[1]; + dest->planesize[i] = bigplane; + dest->width[i] = spr->width+1; + } + CAL_ShiftSprite ((unsigned)grsegs[chunk],dest->sourceoffset[0], + dest->sourceoffset[2],spr->width,spr->height,4,true); + break; + + case 4: + dest->sourceoffset[0] = shiftstarts[0]; + dest->planesize[0] = smallplane; + dest->width[0] = spr->width; + + dest->sourceoffset[1] = shiftstarts[1]; + dest->planesize[1] = bigplane; + dest->width[1] = spr->width+1; + CAL_ShiftSprite ((unsigned)grsegs[chunk],dest->sourceoffset[0], + dest->sourceoffset[1],spr->width,spr->height,2,true); + + dest->sourceoffset[2] = shiftstarts[2]; + dest->planesize[2] = bigplane; + dest->width[2] = spr->width+1; + CAL_ShiftSprite ((unsigned)grsegs[chunk],dest->sourceoffset[0], + dest->sourceoffset[2],spr->width,spr->height,4,true); + + dest->sourceoffset[3] = shiftstarts[3]; + dest->planesize[3] = bigplane; + dest->width[3] = spr->width+1; + CAL_ShiftSprite ((unsigned)grsegs[chunk],dest->sourceoffset[0], + dest->sourceoffset[3],spr->width,spr->height,6,true); + + break; + + default: + Quit ("CAL_CacheSprite: Bad shifts number!"); + } + +#endif +} + +//=========================================================================== + + +/* +====================== += += CAL_ExpandGrChunk += += Does whatever is needed with a pointer to a compressed chunk += +====================== +*/ + +void CAL_ExpandGrChunk (int chunk, byte far *source) +{ + long expanded; + + + if (chunk >= STARTTILE8 && chunk < STARTEXTERNS) + { + // + // expanded sizes of tile8/16/32 are implicit + // + +#if GRMODE == EGAGR +#define BLOCK 32 +#define MASKBLOCK 40 +#endif + +#if GRMODE == CGAGR +#define BLOCK 16 +#define MASKBLOCK 32 +#endif + + if (chunk=STARTSPRITES && chunk< STARTTILE8) + CAL_CacheSprite(chunk,source); + else + { + MM_GetPtr (&grsegs[chunk],expanded); + if (mmerror) + return; + CAL_HuffExpand (source,grsegs[chunk],expanded,grhuffman); + } +} + + +/* +====================== += += CAL_ReadGrChunk += += Gets a chunk off disk, optimizing reads to general buffer += +====================== +*/ + +void CAL_ReadGrChunk (int chunk) +{ + long pos,compressed; + memptr bigbufferseg; + byte far *source; + int next; + +// +// load the chunk into a buffer, either the miscbuffer if it fits, or allocate +// a larger buffer +// + pos = GRFILEPOS(chunk); + if (pos<0) // $FFFFFFFF start is a sparse tile + return; + + next = chunk +1; + while (GRFILEPOS(next) == -1) // skip past any sparse tiles + next++; + + compressed = GRFILEPOS(next)-pos; + + lseek(grhandle,pos,SEEK_SET); + + if (compressed<=BUFFERSIZE) + { + CA_FarRead(grhandle,bufferseg,compressed); + source = bufferseg; + } + else + { + MM_GetPtr(&bigbufferseg,compressed); + if (mmerror) + return; + MM_SetLock (&bigbufferseg,true); + CA_FarRead(grhandle,bigbufferseg,compressed); + source = bigbufferseg; + } + + CAL_ExpandGrChunk (chunk,source); + + if (compressed>BUFFERSIZE) + MM_FreePtr(&bigbufferseg); +} + +/* +====================== += += CA_CacheGrChunk += += Makes sure a given chunk is in memory, loadiing it if needed += +====================== +*/ + +void CA_CacheGrChunk (int chunk) +{ + long pos,compressed; + memptr bigbufferseg; + byte far *source; + int next; + + grneeded[chunk] |= ca_levelbit; // make sure it doesn't get removed + if (grsegs[chunk]) + { + MM_SetPurge (&grsegs[chunk],0); + return; // allready in memory + } + +// MDM begin - (GAMERS EDGE) +// + if (!FindFile("EGAGRAPH."EXT,NULL,2)) + Quit("CA_CacheGrChunk(): Can't find graphics files."); +// +// MDM end + +// +// load the chunk into a buffer, either the miscbuffer if it fits, or allocate +// a larger buffer +// + pos = GRFILEPOS(chunk); + if (pos<0) // $FFFFFFFF start is a sparse tile + return; + + next = chunk +1; + while (GRFILEPOS(next) == -1) // skip past any sparse tiles + next++; + + compressed = GRFILEPOS(next)-pos; + + lseek(grhandle,pos,SEEK_SET); + + if (compressed<=BUFFERSIZE) + { + CA_FarRead(grhandle,bufferseg,compressed); + source = bufferseg; + } + else + { + MM_GetPtr(&bigbufferseg,compressed); + MM_SetLock (&bigbufferseg,true); + CA_FarRead(grhandle,bigbufferseg,compressed); + source = bigbufferseg; + } + + CAL_ExpandGrChunk (chunk,source); + + if (compressed>BUFFERSIZE) + MM_FreePtr(&bigbufferseg); +} + + + +//========================================================================== + +/* +====================== += += CA_CacheMap += +====================== +*/ + +void CA_CacheMap (int mapnum) +{ + long pos,compressed; + int plane; + memptr *dest,bigbufferseg; + unsigned size; + unsigned far *source; +#ifdef MAPHEADERLINKED + memptr buffer2seg; + long expanded; +#endif + + +// MDM begin - (GAMERS EDGE) +// + if (!FindFile("GAMEMAPS."EXT,NULL,1)) + Quit("CA_CacheMap(): Can't find level files."); +// +// MDM end + + +// +// free up memory from last map +// + if (mapon>-1 && mapheaderseg[mapon]) + MM_SetPurge (&(memptr)mapheaderseg[mapon],3); + for (plane=0;planeheaderoffsets[mapnum]; + if (pos<0) // $FFFFFFFF start is a sparse map + Quit ("CA_CacheMap: Tried to load a non existent map!"); + + MM_GetPtr(&(memptr)mapheaderseg[mapnum],sizeof(maptype)); + lseek(maphandle,pos,SEEK_SET); + CA_FarRead (maphandle,(memptr)mapheaderseg[mapnum],sizeof(maptype)); + } + else + MM_SetPurge (&(memptr)mapheaderseg[mapnum],0); + +// +// load the planes in +// If a plane's pointer still exists it will be overwritten (levels are +// allways reloaded, never cached) +// + + size = mapheaderseg[mapnum]->width * mapheaderseg[mapnum]->height * 2; + + for (plane = 0; planeplanestart[plane]; + compressed = mapheaderseg[mapnum]->planelength[plane]; + + if (!compressed) + continue; // the plane is not used in this game + + dest = &(memptr)mapsegs[plane]; + MM_GetPtr(dest,size); + + lseek(maphandle,pos,SEEK_SET); + if (compressed<=BUFFERSIZE) + source = bufferseg; + else + { + MM_GetPtr(&bigbufferseg,compressed); + MM_SetLock (&bigbufferseg,true); + source = bigbufferseg; + } + + CA_FarRead(maphandle,(byte far *)source,compressed); +#ifdef MAPHEADERLINKED + // + // unhuffman, then unRLEW + // The huffman'd chunk has a two byte expanded length first + // The resulting RLEW chunk also does, even though it's not really + // needed + // + expanded = *source; + source++; + MM_GetPtr (&buffer2seg,expanded); + CAL_CarmackExpand (source, (unsigned far *)buffer2seg,expanded); + CA_RLEWexpand (((unsigned far *)buffer2seg)+1,*dest,size, + ((mapfiletype _seg *)tinf)->RLEWtag); + MM_FreePtr (&buffer2seg); + +#else + // + // unRLEW, skipping expanded length + // + CA_RLEWexpand (source+1, *dest,size, + ((mapfiletype _seg *)tinf)->RLEWtag); +#endif + + if (compressed>BUFFERSIZE) + MM_FreePtr(&bigbufferseg); + } +} + +//=========================================================================== + +/* +====================== += += CA_UpLevel += += Goes up a bit level in the needed lists and clears it out. += Everything is made purgable += +====================== +*/ + +void CA_UpLevel (void) +{ + if (ca_levelnum==7) + Quit ("CA_UpLevel: Up past level 7!"); + + ca_levelbit<<=1; + ca_levelnum++; +} + +//=========================================================================== + +/* +====================== += += CA_DownLevel += += Goes down a bit level in the needed lists and recaches += everything from the lower level += +====================== +*/ + +void CA_DownLevel (void) +{ + if (!ca_levelnum) + Quit ("CA_DownLevel: Down past level 0!"); + ca_levelbit>>=1; + ca_levelnum--; + CA_CacheMarks(NULL); +} + +//=========================================================================== + +/* +====================== += += CA_ClearMarks += += Clears out all the marks at the current level += +====================== +*/ + +void CA_ClearMarks (void) +{ + int i; + + for (i=0;i>16; + if (xh - lastx > BARSTEP) + { + for (x=lastx;x<=xh;x++) +#if GRMODE == EGAGR + VWB_Vlin (thy,thy+13,x,14); +#endif +#if GRMODE == CGAGR + VWB_Vlin (thy,thy+13,x,SECONDCOLOR); +#endif + lastx = xh; + VW_UpdateScreen(); + } +} + +/* +====================== += += CAL_DialogFinish += +====================== +*/ + +void CAL_DialogFinish (void) +{ + unsigned x,xh; + + xh = thx + NUMBARS; + for (x=lastx;x<=xh;x++) +#if GRMODE == EGAGR + VWB_Vlin (thy,thy+13,x,14); +#endif +#if GRMODE == CGAGR + VWB_Vlin (thy,thy+13,x,SECONDCOLOR); +#endif + VW_UpdateScreen(); + +} + +//=========================================================================== + +/* +====================== += += CA_CacheMarks += +====================== +*/ +#define MAXEMPTYREAD 1024 + +void CA_CacheMarks (char *title) +{ + boolean dialog; + int i,next,numcache; + long pos,endpos,nextpos,nextendpos,compressed; + long bufferstart,bufferend; // file position of general buffer + byte far *source; + memptr bigbufferseg; + + dialog = (title!=NULL); + + numcache = 0; +// +// go through and make everything not needed purgable +// + for (i=0;i= endpos) + { + // data is allready in buffer + source = (byte _seg *)bufferseg+(pos-bufferstart); + } + else + { + // load buffer with a new block from disk + // try to get as many of the needed blocks in as possible + while ( next < NUMCHUNKS ) + { + while (next < NUMCHUNKS && + !(grneeded[next]&ca_levelbit && !grsegs[next])) + next++; + if (next == NUMCHUNKS) + continue; + + nextpos = GRFILEPOS(next); + while (GRFILEPOS(++next) == -1) // skip past any sparse tiles + ; + nextendpos = GRFILEPOS(next); + if (nextpos - endpos <= MAXEMPTYREAD + && nextendpos-pos <= BUFFERSIZE) + endpos = nextendpos; + else + next = NUMCHUNKS; // read pos to posend + } + + lseek(grhandle,pos,SEEK_SET); + CA_FarRead(grhandle,bufferseg,endpos-pos); + bufferstart = pos; + bufferend = endpos; + source = bufferseg; + } + } + else + { + // big chunk, allocate temporary buffer + MM_GetPtr(&bigbufferseg,compressed); + if (mmerror) + return; + MM_SetLock (&bigbufferseg,true); + lseek(grhandle,pos,SEEK_SET); + CA_FarRead(grhandle,bigbufferseg,compressed); + source = bigbufferseg; + } + + CAL_ExpandGrChunk (i,source); + if (mmerror) + return; + + if (compressed>BUFFERSIZE) + MM_FreePtr(&bigbufferseg); + + } + +// +// finish up any thermometer remnants +// + if (dialog && finishcachebox) + finishcachebox(); +} + diff --git a/16/cawat/ID_CA.H b/16/cawat/ID_CA.H new file mode 100644 index 00000000..c2ff0ab7 --- /dev/null +++ b/16/cawat/ID_CA.H @@ -0,0 +1,123 @@ +/* Catacomb Armageddon Source Code + * Copyright (C) 1993-2014 Flat Rock Software + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +// ID_CA.H + +/*#ifndef __TYPES__ +#include "ID_TYPES.H" +#endif + +#ifndef __ID_MM__ +#include "ID_MM.H" +#endif + +#ifndef __ID_GLOB__ +#include "ID_GLOB.H" +#endif*/ + +#define __ID_CA__ + +//=========================================================================== + +//#define NOMAPS +//#define NOGRAPHICS +//#define NOAUDIO + +#define MAPHEADERLINKED +#define GRHEADERLINKED +#define AUDIOHEADERLINKED + +#define NUMMAPS 39 +#define MAPPLANES 3 + +//=========================================================================== + +typedef struct +{ + long planestart[3]; + unsigned planelength[3]; + unsigned width,height; + char name[16]; +} maptype; + +//=========================================================================== + +extern byte /*_1seg*/ *tinf; +extern int mapon; + +extern unsigned /*_1seg*/ *mapsegs[3]; +extern maptype /*_1seg*/ *mapheaderseg[NUMMAPS]; +extern byte /*_1seg*/ *audiosegs[NUMSNDCHUNKS]; +extern void /*_1seg*/ *grsegs[NUMCHUNKS]; + +extern byte far grneeded[NUMCHUNKS]; +extern byte ca_levelbit,ca_levelnum; + +extern char *titleptr[8]; + +extern int profilehandle,debughandle; + +// +// hooks for custom cache dialogs +// +extern void (*drawcachebox) (char *title, unsigned numcache); +extern void (*updatecachebox) (void); +extern void (*finishcachebox) (void); + +//=========================================================================== + +// just for the score box reshifting + +void CAL_ShiftSprite (unsigned segment,unsigned source,unsigned dest, + unsigned width, unsigned height, unsigned pixshift, boolean domask); + +//=========================================================================== + +void CA_OpenDebug (void); +void CA_CloseDebug (void); +boolean CA_FarRead (int handle, byte far *dest, long length); +boolean CA_FarWrite (int handle, byte far *source, long length); +boolean CA_ReadFile (char *filename, memptr *ptr); +boolean CA_LoadFile (char *filename, memptr *ptr); + +long CA_RLEWCompress (unsigned huge *source, long length, unsigned huge *dest, + unsigned rlewtag); + +void CA_RLEWexpand (unsigned huge *source, unsigned huge *dest,long length, + unsigned rlewtag); + +void CA_Startup (void); +void CA_Shutdown (void); + +void CA_CacheAudioChunk (int chunk); +void CA_LoadAllSounds (void); + +void CA_UpLevel (void); +void CA_DownLevel (void); + +void CA_SetAllPurge (void); + +void CA_ClearMarks (void); +void CA_ClearAllMarks (void); + +#define CA_MarkGrChunk(chunk) grneeded[chunk]|=ca_levelbit + +void CA_CacheGrChunk (int chunk); +void CA_CacheMap (int mapnum); + +void CA_CacheMarks (char *title); diff --git a/16/cawat/ID_HEADS.H b/16/cawat/ID_HEADS.H new file mode 100644 index 00000000..88f86d35 --- /dev/null +++ b/16/cawat/ID_HEADS.H @@ -0,0 +1,146 @@ +/* Catacomb Armageddon Source Code + * Copyright (C) 1993-2014 Flat Rock Software + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +// ID_GLOB.H + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define __ID_GLOB__ + +//-------------------------------------------------------------------------- + +#define EXT "ARM" + +extern char far introscn; + +#include "GFXE_ARM.H" +#include "AUDIOARM.H" +#include "MAPSARM.H" + +//-------------------------------------------------------------------------- + +// +// DEFINES THE TILE ATTRIBUTE CHECKING CONVENTION (macros). +// +// DEFINE CUSTOM BIT-FLAG NAMES... +// + + +#define tf_SOLID 0x01 +#define tf_SPECIAL 0x02 +#define tf_EMBEDDED_KEY_COLOR 0x04 +#define tf_INVISIBLE_WALL 0x09 +#define tf_MARKED 0x80 + +#define ANIM_FLAGS(tile) (tinf[ANIM+(tile)]) +#define TILE_FLAGS(tile) (tinf[FLAGS+(tile)]) + +#define GATE_KEY_COLOR(tile) ((unsigned char)(TILE_FLAGS(tile)>>4)) + +#define CAT3D + +#define TEXTGR 0 +#define CGAGR 1 +#define EGAGR 2 +#define VGAGR 3 + +#define EGA320GR 10 // MDM (GAMERS EDGE) +#define EGA640GR 11 // MDM (GAMERS EDGE) + +#define GRMODE EGAGR + +#if GRMODE == EGAGR +#define GREXT "EGA" +#endif +#if GRMODE == CGAGR +#define GREXT "CGA" +#endif + +//#define PROFILE + +// +// ID Engine +// Types.h - Generic types, #defines, etc. +// v1.0d1 +// + +#ifndef __TYPES__ +#define __TYPES__ + +typedef enum {false,true} boolean; +typedef unsigned char byte; +typedef unsigned int word; +typedef unsigned long longword; +typedef byte * Ptr; + +typedef struct + { + int x,y; + } Point; +typedef struct + { + Point ul,lr; + } Rect; + +#define nil ((void *)0) + +#endif + +#include "ID_MM.H" +#include "ID_CA.H" +#include "ID_VW.H" +#include "ID_IN.H" +#include "ID_SD.H" +#include "ID_US.H" + + +void Quit (char *error, ...); // defined in user program + +// +// replacing refresh manager with custom routines +// + +#define PORTTILESWIDE 21 // all drawing takes place inside a +#define PORTTILESHIGH 14 // non displayed port of this size + +#define UPDATEWIDE (PORTTILESWIDE+1) +#define UPDATEHIGH PORTTILESHIGH + +#define MAXTICS 6 +#define DEMOTICS 3 + +#define UPDATETERMINATE 0x0301 + +extern unsigned mapwidth,mapheight,tics,realtics; +extern boolean compatability; + +extern byte *updateptr; +extern unsigned uwidthtable[UPDATEHIGH]; +extern unsigned blockstarts[UPDATEWIDE*UPDATEHIGH]; diff --git a/16/cawat/ID_IN.C b/16/cawat/ID_IN.C new file mode 100644 index 00000000..284a6fe4 --- /dev/null +++ b/16/cawat/ID_IN.C @@ -0,0 +1,1284 @@ +/* Catacomb Armageddon Source Code + * Copyright (C) 1993-2014 Flat Rock Software + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +// +// ID Engine +// ID_IN.c - Input Manager +// v1.0d1 +// By Jason Blochowiak +// + +// +// This module handles dealing with the various input devices +// +// Depends on: Memory Mgr (for demo recording), Sound Mgr (for timing stuff), +// User Mgr (for command line parms) +// +// Globals: +// LastScan - The keyboard scan code of the last key pressed +// LastASCII - The ASCII value of the last key pressed +// DEBUG - there are more globals +// + +//#include "ID_HEADS.H" +#include "id_in.h" +//#pragma hdrstop + +#define KeyInt 9 // The keyboard ISR number + +// Stuff for the joystick +#define JoyScaleMax 32768 +#define JoyScaleShift 8 +#define MaxJoyValue 5000 + +// Global variables + boolean JoystickCalibrated=false; // MDM (GAMERS EDGE) - added + ControlType ControlTypeUsed; // MDM (GAMERS EDGE) - added + + boolean Keyboard[NumCodes], + JoysPresent[MaxJoys], + MousePresent; + boolean Paused; + char LastASCII; + ScanCode LastScan; + KeyboardDef KbdDefs[MaxKbds] = {{0x1d,0x38,0x47,0x48,0x49,0x4b,0x4d,0x4f,0x50,0x51}}; + JoystickDef JoyDefs[MaxJoys]; + ControlType Controls[MaxPlayers]; + +// Demo DemoMode = demo_Off; +// byte /*_1seg*/ *DemoBuffer; +// word DemoOffset,DemoSize; + +// Internal variables +static boolean IN_Started; +static boolean CapsLock; +static ScanCode CurCode,LastCode; +static byte far ASCIINames[] = // Unshifted ASCII for scan codes + { +// 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0 ,27 ,'1','2','3','4','5','6','7','8','9','0','-','=',8 ,9 , // 0 + 'q','w','e','r','t','y','u','i','o','p','[',']',13 ,0 ,'a','s', // 1 + 'd','f','g','h','j','k','l',';',39 ,'`',0 ,92 ,'z','x','c','v', // 2 + 'b','n','m',',','.','/',0 ,'*',0 ,' ',0 ,0 ,0 ,0 ,0 ,0 , // 3 + 0 ,0 ,0 ,0 ,0 ,0 ,0 ,'7','8','9','-','4','5','6','+','1', // 4 + '2','3','0',127,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 , // 5 + 0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 , // 6 + 0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 // 7 + }, + far ShiftNames[] = // Shifted ASCII for scan codes + { +// 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0 ,27 ,'!','@','#','$','%','^','&','*','(',')','_','+',8 ,9 , // 0 + 'Q','W','E','R','T','Y','U','I','O','P','{','}',13 ,0 ,'A','S', // 1 + 'D','F','G','H','J','K','L',':',34 ,'~',0 ,'|','Z','X','C','V', // 2 + 'B','N','M','<','>','?',0 ,'*',0 ,' ',0 ,0 ,0 ,0 ,0 ,0 , // 3 + 0 ,0 ,0 ,0 ,0 ,0 ,0 ,'7','8','9','-','4','5','6','+','1', // 4 + '2','3','0',127,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 , // 5 + 0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 , // 6 + 0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 // 7 + }, + far SpecialNames[] = // ASCII for 0xe0 prefixed codes + { +// 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 , // 0 + 0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,13 ,0 ,0 ,0 , // 1 + 0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 , // 2 + 0 ,0 ,0 ,0 ,0 ,'/',0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 , // 3 + 0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 , // 4 + 0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 , // 5 + 0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 , // 6 + 0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 // 7 + }, + +#if 0 + *ScanNames[] = // Scan code names with single chars + { + "?","?","1","2","3","4","5","6","7","8","9","0","-","+","?","?", + "Q","W","E","R","T","Y","U","I","O","P","[","]","|","?","A","S", + "D","F","G","H","J","K","L",";","\"","?","?","?","Z","X","C","V", + "B","N","M",",",".","/","?","?","?","?","?","?","?","?","?","?", + "?","?","?","?","?","?","?","?","\xf","?","-","\x15","5","\x11","+","?", + "\x13","?","?","?","?","?","?","?","?","?","?","?","?","?","?","?", + "?","?","?","?","?","?","?","?","?","?","?","?","?","?","?","?", + "?","?","?","?","?","?","?","?","?","?","?","?","?","?","?","?" + }, // DEBUG - consolidate these +#endif + + far ExtScanCodes[] = // Scan codes with >1 char names + { + 1,0xe,0xf,0x1d,0x2a,0x39,0x3a,0x3b,0x3c,0x3d,0x3e, + 0x3f,0x40,0x41,0x42,0x43,0x44,0x57,0x59,0x46,0x1c,0x36, + 0x37,0x38,0x47,0x49,0x4f,0x51,0x52,0x53,0x45,0x48, + 0x50,0x4b,0x4d,0x00 + }; +#if 0 + *ExtScanNames[] = // Names corresponding to ExtScanCodes + { + "Esc","BkSp","Tab","Ctrl","LShft","Space","CapsLk","F1","F2","F3","F4", + "F5","F6","F7","F8","F9","F10","F11","F12","ScrlLk","Enter","RShft", + "PrtSc","Alt","Home","PgUp","End","PgDn","Ins","Del","NumLk","Up", + "Down","Left","Right","" + }; +#endif +static Direction DirTable[] = // Quick lookup for total direction + { + dir_NorthWest, dir_North, dir_NorthEast, + dir_West, dir_None, dir_East, + dir_SouthWest, dir_South, dir_SouthEast + }; + +static void (*INL_KeyHook)(void); +static void interrupt (*OldKeyVect)(void); + +static char *ParmStrings[] = {"nojoys","nomouse",nil}; + +// Internal routines + +/////////////////////////////////////////////////////////////////////////// +// +// INL_KeyService() - Handles a keyboard interrupt (key up/down) +// +/////////////////////////////////////////////////////////////////////////// +static void interrupt +INL_KeyService(void) +{ +static boolean special; + byte k,c, + temp; + + k = inportb(0x60); // Get the scan code + + // Tell the XT keyboard controller to clear the key + outportb(0x61,(temp = inportb(0x61)) | 0x80); + outportb(0x61,temp); + + if (k == 0xe0) // Special key prefix + special = true; + else if (k == 0xe1) // Handle Pause key + Paused = true; + else + { + if (k & 0x80) // Break code + { + k &= 0x7f; + +// DEBUG - handle special keys: ctl-alt-delete, print scrn + + Keyboard[k] = false; + } + else // Make code + { + LastCode = CurCode; + CurCode = LastScan = k; + Keyboard[k] = true; + + if (special) + c = SpecialNames[k]; + else + { + if (k == sc_CapsLock) + { + CapsLock ^= true; + // DEBUG - make caps lock light work + } + + if (Keyboard[sc_LShift] || Keyboard[sc_RShift]) // If shifted + { + c = ShiftNames[k]; + if ((c >= 'A') && (c <= 'Z') && CapsLock) + c += 'a' - 'A'; + } + else + { + c = ASCIINames[k]; + if ((c >= 'a') && (c <= 'z') && CapsLock) + c -= 'a' - 'A'; + } + } + if (c) + LastASCII = c; + } + + special = false; + } + + if (INL_KeyHook && !special) + INL_KeyHook(); + outportb(0x20,0x20); +} + +/////////////////////////////////////////////////////////////////////////// +// +// INL_GetMouseDelta() - Gets the amount that the mouse has moved from the +// mouse driver +// +/////////////////////////////////////////////////////////////////////////// +static void +INL_GetMouseDelta(int *x,int *y) +{ + Mouse(MDelta); + *x = _CX; + *y = _DX; +} + +/////////////////////////////////////////////////////////////////////////// +// +// INL_GetMouseButtons() - Gets the status of the mouse buttons from the +// mouse driver +// +/////////////////////////////////////////////////////////////////////////// +static word +INL_GetMouseButtons(void) +{ + word buttons; + + Mouse(MButtons); + buttons = _BX; + return(buttons); +} + +/////////////////////////////////////////////////////////////////////////// +// +// IN_GetJoyAbs() - Reads the absolute position of the specified joystick +// +/////////////////////////////////////////////////////////////////////////// +void +IN_GetJoyAbs(word joy,word *xp,word *yp) +{ + byte xb,yb, + xs,ys; + word x,y; + + x = y = 0; + xs = joy? 2 : 0; // Find shift value for x axis + xb = 1 << xs; // Use shift value to get x bit mask + ys = joy? 3 : 1; // Do the same for y axis + yb = 1 << ys; + +// Read the absolute joystick values +asm pushf // Save some registers +asm push si +asm push di +asm cli // Make sure an interrupt doesn't screw the timings + + +asm mov dx,0x201 +asm in al,dx +asm out dx,al // Clear the resistors + +asm mov ah,[xb] // Get masks into registers +asm mov ch,[yb] + +asm xor si,si // Clear count registers +asm xor di,di +asm xor bh,bh // Clear high byte of bx for later + +asm push bp // Don't mess up stack frame +asm mov bp,MaxJoyValue + +loop: +asm in al,dx // Get bits indicating whether all are finished + +asm dec bp // Check bounding register +asm jz done // We have a silly value - abort + +asm mov bl,al // Duplicate the bits +asm and bl,ah // Mask off useless bits (in [xb]) +asm add si,bx // Possibly increment count register +asm mov cl,bl // Save for testing later + +asm mov bl,al +asm and bl,ch // [yb] +asm add di,bx + +asm add cl,bl +asm jnz loop // If both bits were 0, drop out + +done: +asm pop bp + +asm mov cl,[xs] // Get the number of bits to shift +asm shr si,cl // and shift the count that many times + +asm mov cl,[ys] +asm shr di,cl + +asm mov [x],si // Store the values into the variables +asm mov [y],di + +asm pop di +asm pop si +asm popf // Restore the registers + + *xp = x; + *yp = y; +} + +/////////////////////////////////////////////////////////////////////////// +// +// INL_GetJoyDelta() - Returns the relative movement of the specified +// joystick (from +/-127, scaled adaptively) +// +/////////////////////////////////////////////////////////////////////////// +static void +INL_GetJoyDelta(word joy,int *dx,int *dy,boolean adaptive) +{ + word x,y; + longword time; + JoystickDef *def; +static longword lasttime; + + IN_GetJoyAbs(joy,&x,&y); + def = JoyDefs + joy; + + if (x < def->threshMinX) + { + if (x < def->joyMinX) + x = def->joyMinX; + + x = -(x - def->threshMinX); + x *= def->joyMultXL; + x >>= JoyScaleShift; + *dx = (x > 127)? -127 : -x; + } + else if (x > def->threshMaxX) + { + if (x > def->joyMaxX) + x = def->joyMaxX; + + x = x - def->threshMaxX; + x *= def->joyMultXH; + x >>= JoyScaleShift; + *dx = (x > 127)? 127 : x; + } + else + *dx = 0; + + if (y < def->threshMinY) + { + if (y < def->joyMinY) + y = def->joyMinY; + + y = -(y - def->threshMinY); + y *= def->joyMultYL; + y >>= JoyScaleShift; + *dy = (y > 127)? -127 : -y; + } + else if (y > def->threshMaxY) + { + if (y > def->joyMaxY) + y = def->joyMaxY; + + y = y - def->threshMaxY; + y *= def->joyMultYH; + y >>= JoyScaleShift; + *dy = (y > 127)? 127 : y; + } + else + *dy = 0; + + if (adaptive) + { + time = (TimeCount - lasttime) / 2; + if (time) + { + if (time > 8) + time = 8; + *dx *= time; + *dy *= time; + } + } + lasttime = TimeCount; +} + +/////////////////////////////////////////////////////////////////////////// +// +// INL_GetJoyButtons() - Returns the button status of the specified +// joystick +// +/////////////////////////////////////////////////////////////////////////// +static word +INL_GetJoyButtons(word joy) +{ +register word result; + + result = inportb(0x201); // Get all the joystick buttons + result >>= joy? 6 : 4; // Shift into bits 0-1 + result &= 3; // Mask off the useless bits + result ^= 3; + return(result); +} + +/////////////////////////////////////////////////////////////////////////// +// +// IN_GetJoyButtonsDB() - Returns the de-bounced button status of the +// specified joystick +// +/////////////////////////////////////////////////////////////////////////// +word +IN_GetJoyButtonsDB(word joy) +{ + longword lasttime; + word result1,result2; + + do + { + result1 = INL_GetJoyButtons(joy); + lasttime = TimeCount; + while (TimeCount == lasttime) + ; + result2 = INL_GetJoyButtons(joy); + } while (result1 != result2); + return(result1); +} + +/////////////////////////////////////////////////////////////////////////// +// +// INL_StartKbd() - Sets up my keyboard stuff for use +// +/////////////////////////////////////////////////////////////////////////// +static void +INL_StartKbd(void) +{ + INL_KeyHook = 0; // Clear key hook + + IN_ClearKeysDown(); + + OldKeyVect = getvect(KeyInt); + setvect(KeyInt,INL_KeyService); +} + +/////////////////////////////////////////////////////////////////////////// +// +// INL_ShutKbd() - Restores keyboard control to the BIOS +// +/////////////////////////////////////////////////////////////////////////// +static void +INL_ShutKbd(void) +{ + poke(0x40,0x17,peek(0x40,0x17) & 0xfaf0); // Clear ctrl/alt/shift flags + + setvect(KeyInt,OldKeyVect); +} + +/////////////////////////////////////////////////////////////////////////// +// +// INL_StartMouse() - Detects and sets up the mouse +// +/////////////////////////////////////////////////////////////////////////// +static boolean +INL_StartMouse(void) +{ + if (getvect(MouseInt)) + { + Mouse(MReset); + if (_AX == 0xffff) + return(true); + } + return(false); +} + +/////////////////////////////////////////////////////////////////////////// +// +// INL_ShutMouse() - Cleans up after the mouse +// +/////////////////////////////////////////////////////////////////////////// +static void +INL_ShutMouse(void) +{ +} + +// +// INL_SetJoyScale() - Sets up scaling values for the specified joystick +// +static void +INL_SetJoyScale(word joy) +{ + JoystickDef *def; + + def = &JoyDefs[joy]; + def->joyMultXL = JoyScaleMax / (def->threshMinX - def->joyMinX); + def->joyMultXH = JoyScaleMax / (def->joyMaxX - def->threshMaxX); + def->joyMultYL = JoyScaleMax / (def->threshMinY - def->joyMinY); + def->joyMultYH = JoyScaleMax / (def->joyMaxY - def->threshMaxY); +} + +/////////////////////////////////////////////////////////////////////////// +// +// IN_SetupJoy() - Sets up thresholding values and calls INL_SetJoyScale() +// to set up scaling values +// +/////////////////////////////////////////////////////////////////////////// +void +IN_SetupJoy(word joy,word minx,word maxx,word miny,word maxy) +{ + word d,r; + JoystickDef *def; + + def = &JoyDefs[joy]; + + def->joyMinX = minx; + def->joyMaxX = maxx; + r = maxx - minx; + d = r / 5; + def->threshMinX = ((r / 2) - d) + minx; + def->threshMaxX = ((r / 2) + d) + minx; + + def->joyMinY = miny; + def->joyMaxY = maxy; + r = maxy - miny; + d = r / 5; + def->threshMinY = ((r / 2) - d) + miny; + def->threshMaxY = ((r / 2) + d) + miny; + + INL_SetJoyScale(joy); +} + +/////////////////////////////////////////////////////////////////////////// +// +// INL_StartJoy() - Detects & auto-configures the specified joystick +// The auto-config assumes the joystick is centered +// +/////////////////////////////////////////////////////////////////////////// +static boolean +INL_StartJoy(word joy) +{ + word x,y; + + IN_GetJoyAbs(joy,&x,&y); + + if + ( + ((x == 0) || (x > MaxJoyValue - 10)) + || ((y == 0) || (y > MaxJoyValue - 10)) + ) + return(false); + else + { + IN_SetupJoy(joy,0,x * 2,0,y * 2); + return(true); + } +} + +/////////////////////////////////////////////////////////////////////////// +// +// INL_ShutJoy() - Cleans up the joystick stuff +// +/////////////////////////////////////////////////////////////////////////// +static void +INL_ShutJoy(word joy) +{ + JoysPresent[joy] = false; +} + +// Public routines + +/////////////////////////////////////////////////////////////////////////// +// +// IN_Startup() - Starts up the Input Mgr +// +/////////////////////////////////////////////////////////////////////////// +void +IN_Startup(void) +{ + boolean checkjoys,checkmouse; + word i; + + if (IN_Started) + return; + + checkjoys = true; + checkmouse = true; + for (i = 1;i < _argc;i++) + { + switch (US_CheckParm(_argv[i],ParmStrings)) + { + case 0: + checkjoys = false; + break; + case 1: + checkmouse = false; + break; + } + } + + INL_StartKbd(); + MousePresent = checkmouse? INL_StartMouse() : false; + + for (i = 0;i < MaxJoys;i++) + JoysPresent[i] = checkjoys? INL_StartJoy(i) : false; + + IN_Started = true; +} + +/////////////////////////////////////////////////////////////////////////// +// +// IN_Default() - Sets up default conditions for the Input Mgr +// +/////////////////////////////////////////////////////////////////////////// +void +IN_Default(boolean gotit,ControlType in) +{ + if + ( + (!gotit) + || ((in == ctrl_Joystick1) && !JoysPresent[0]) + || ((in == ctrl_Joystick2) && !JoysPresent[1]) + || ((in == ctrl_Mouse) && !MousePresent) + ) + in = ctrl_Keyboard1; + IN_SetControlType(0,in); +} + +/////////////////////////////////////////////////////////////////////////// +// +// IN_Shutdown() - Shuts down the Input Mgr +// +/////////////////////////////////////////////////////////////////////////// +void +IN_Shutdown(void) +{ + word i; + + if (!IN_Started) + return; + + INL_ShutMouse(); + for (i = 0;i < MaxJoys;i++) + INL_ShutJoy(i); + INL_ShutKbd(); + + IN_Started = false; +} + +/////////////////////////////////////////////////////////////////////////// +// +// IN_SetKeyHook() - Sets the routine that gets called by INL_KeyService() +// everytime a real make/break code gets hit +// +/////////////////////////////////////////////////////////////////////////// +void +IN_SetKeyHook(void (*hook)()) +{ + INL_KeyHook = hook; +} + +/////////////////////////////////////////////////////////////////////////// +// +// IN_ClearKeyDown() - Clears the keyboard array +// +/////////////////////////////////////////////////////////////////////////// +void +IN_ClearKeysDown(void) +{ + int i; + + LastScan = sc_None; + LastASCII = key_None; + for (i = 0;i < NumCodes;i++) + Keyboard[i] = false; +} + +/////////////////////////////////////////////////////////////////////////// +// +// INL_AdjustCursor() - Internal routine of common code from IN_ReadCursor() +// +/////////////////////////////////////////////////////////////////////////// +static void +INL_AdjustCursor(CursorInfo *info,word buttons,int dx,int dy) +{ + if (buttons & (1 << 0)) + info->button0 = true; + if (buttons & (1 << 1)) + info->button1 = true; + + info->x += dx; + info->y += dy; +} + +/////////////////////////////////////////////////////////////////////////// +// +// IN_ReadCursor() - Reads the input devices and fills in the cursor info +// struct +// +/////////////////////////////////////////////////////////////////////////// +void +IN_ReadCursor(CursorInfo *info) +{ + word i, + buttons; + int dx,dy; + + info->x = info->y = 0; + info->button0 = info->button1 = false; + + if (MousePresent) + { + buttons = INL_GetMouseButtons(); + INL_GetMouseDelta(&dx,&dy); + INL_AdjustCursor(info,buttons,dx,dy); + } + + for (i = 0;i < MaxJoys;i++) + { + if (!JoysPresent[i]) + continue; + + buttons = INL_GetJoyButtons(i); + INL_GetJoyDelta(i,&dx,&dy,true); + dx /= 64; + dy /= 64; + INL_AdjustCursor(info,buttons,dx,dy); + } +} + +/////////////////////////////////////////////////////////////////////////// +// +// IN_ReadControl() - Reads the device associated with the specified +// player and fills in the control info struct +// +/////////////////////////////////////////////////////////////////////////// +void +IN_ReadControl(int player,ControlInfo *info) +{ + boolean realdelta=false; // MDM (GAMERS EDGE) + byte dbyte; + word buttons; + int dx,dy; + Motion mx,my; + ControlType type; +register KeyboardDef *def; + + dx = dy = 0; + mx = my = motion_None; + buttons = 0; + +#if 0 + if (DemoMode == demo_Playback) + { + dbyte = DemoBuffer[DemoOffset + 1]; + my = (dbyte & 3) - 1; + mx = ((dbyte >> 2) & 3) - 1; + buttons = (dbyte >> 4) & 3; + + if (!(--DemoBuffer[DemoOffset])) + { + DemoOffset += 2; + if (DemoOffset >= DemoSize) + DemoMode = demo_PlayDone; + } + + realdelta = false; + } + else if (DemoMode == demo_PlayDone) + Quit("Demo playback exceeded"); + else +#endif + { + // MDM begin (GAMERS EDGE) - added this block + ControlTypeUsed = ctrl_None; + + // Handle mouse input... + // + if ((MousePresent) && (ControlTypeUsed == ctrl_None)) + { + INL_GetMouseDelta(&dx,&dy); + buttons = INL_GetMouseButtons(); + realdelta = true; + if (dx || dy || buttons) + ControlTypeUsed = ctrl_Mouse; + } + + // Handle joystick input... + // + if ((JoystickCalibrated) && (ControlTypeUsed == ctrl_None)) + { + type = ctrl_Joystick1; + INL_GetJoyDelta(type - ctrl_Joystick,&dx,&dy,false); + buttons = INL_GetJoyButtons(type - ctrl_Joystick); + realdelta = true; + if (dx || dy || buttons) + ControlTypeUsed = ctrl_Joystick; + } + + // Handle keyboard input... + // + if (ControlTypeUsed == ctrl_None) + { + type = ctrl_Keyboard1; + def = &KbdDefs[type - ctrl_Keyboard]; + + if (Keyboard[def->upleft]) + mx = motion_Left,my = motion_Up; + else if (Keyboard[def->upright]) + mx = motion_Right,my = motion_Up; + else if (Keyboard[def->downleft]) + mx = motion_Left,my = motion_Down; + else if (Keyboard[def->downright]) + mx = motion_Right,my = motion_Down; + + if (Keyboard[def->up]) + my = motion_Up; + else if (Keyboard[def->down]) + my = motion_Down; + + if (Keyboard[def->left]) + mx = motion_Left; + else if (Keyboard[def->right]) + mx = motion_Right; + + if (Keyboard[def->button0]) + buttons += 1 << 0; + if (Keyboard[def->button1]) + buttons += 1 << 1; + realdelta = false; + if (mx || my || buttons) + ControlTypeUsed = ctrl_Keyboard; + } // MDM end (GAMERS EDGE) + } + + if (realdelta) + { + mx = (dx < 0)? motion_Left : ((dx > 0)? motion_Right : motion_None); + my = (dy < 0)? motion_Up : ((dy > 0)? motion_Down : motion_None); + } + else + { + dx = mx * 127; + dy = my * 127; + } + + info->x = dx; + info->xaxis = mx; + info->y = dy; + info->yaxis = my; + info->button0 = buttons & (1 << 0); + info->button1 = buttons & (1 << 1); + info->dir = DirTable[((my + 1) * 3) + (mx + 1)]; + +#if 0 + if (DemoMode == demo_Record) + { + // Pack the control info into a byte + dbyte = (buttons << 4) | ((mx + 1) << 2) | (my + 1); + + if + ( + (DemoBuffer[DemoOffset + 1] == dbyte) + && (DemoBuffer[DemoOffset] < 255) + ) + (DemoBuffer[DemoOffset])++; + else + { + if (DemoOffset || DemoBuffer[DemoOffset]) + DemoOffset += 2; + + if (DemoOffset >= DemoSize) + Quit("Demo buffer overflow"); + + DemoBuffer[DemoOffset] = 1; + DemoBuffer[DemoOffset + 1] = dbyte; + } + } +#endif +} + +#if 0 +/////////////////////////////////////////////////////////////////////////// +// +// IN_ReadControl() - Reads the device associated with the specified +// player and fills in the control info struct +// +/////////////////////////////////////////////////////////////////////////// +void +IN_ReadControl(int player,ControlInfo *info) +{ + boolean realdelta; + byte dbyte; + word buttons; + int dx,dy; + Motion mx,my; + ControlType type; +register KeyboardDef *def; + + dx = dy = 0; + mx = my = motion_None; + buttons = 0; + +#if 0 + if (DemoMode == demo_Playback) + { + dbyte = DemoBuffer[DemoOffset + 1]; + my = (dbyte & 3) - 1; + mx = ((dbyte >> 2) & 3) - 1; + buttons = (dbyte >> 4) & 3; + + if (!(--DemoBuffer[DemoOffset])) + { + DemoOffset += 2; + if (DemoOffset >= DemoSize) + DemoMode = demo_PlayDone; + } + + realdelta = false; + } + else if (DemoMode == demo_PlayDone) + Quit("Demo playback exceeded"); + else +#endif + { + switch (type = Controls[player]) + { + case ctrl_Keyboard1: + case ctrl_Keyboard2: + def = &KbdDefs[type - ctrl_Keyboard]; + + if (Keyboard[def->upleft]) + mx = motion_Left,my = motion_Up; + else if (Keyboard[def->upright]) + mx = motion_Right,my = motion_Up; + else if (Keyboard[def->downleft]) + mx = motion_Left,my = motion_Down; + else if (Keyboard[def->downright]) + mx = motion_Right,my = motion_Down; + + if (Keyboard[def->up]) + my = motion_Up; + else if (Keyboard[def->down]) + my = motion_Down; + + if (Keyboard[def->left]) + mx = motion_Left; + else if (Keyboard[def->right]) + mx = motion_Right; + + if (Keyboard[def->button0]) + buttons += 1 << 0; + if (Keyboard[def->button1]) + buttons += 1 << 1; + realdelta = false; + break; + case ctrl_Joystick1: + case ctrl_Joystick2: + INL_GetJoyDelta(type - ctrl_Joystick,&dx,&dy,false); + buttons = INL_GetJoyButtons(type - ctrl_Joystick); + realdelta = true; + break; + case ctrl_Mouse: + INL_GetMouseDelta(&dx,&dy); + buttons = INL_GetMouseButtons(); + realdelta = true; + break; + } + } + + if (realdelta) + { + mx = (dx < 0)? motion_Left : ((dx > 0)? motion_Right : motion_None); + my = (dy < 0)? motion_Up : ((dy > 0)? motion_Down : motion_None); + } + else + { + dx = mx * 127; + dy = my * 127; + } + + info->x = dx; + info->xaxis = mx; + info->y = dy; + info->yaxis = my; + info->button0 = buttons & (1 << 0); + info->button1 = buttons & (1 << 1); + info->dir = DirTable[((my + 1) * 3) + (mx + 1)]; + +#if 0 + if (DemoMode == demo_Record) + { + // Pack the control info into a byte + dbyte = (buttons << 4) | ((mx + 1) << 2) | (my + 1); + + if + ( + (DemoBuffer[DemoOffset + 1] == dbyte) + && (DemoBuffer[DemoOffset] < 255) + ) + (DemoBuffer[DemoOffset])++; + else + { + if (DemoOffset || DemoBuffer[DemoOffset]) + DemoOffset += 2; + + if (DemoOffset >= DemoSize) + Quit("Demo buffer overflow"); + + DemoBuffer[DemoOffset] = 1; + DemoBuffer[DemoOffset + 1] = dbyte; + } + } +#endif +} +#endif + +/////////////////////////////////////////////////////////////////////////// +// +// IN_SetControlType() - Sets the control type to be used by the specified +// player +// +/////////////////////////////////////////////////////////////////////////// +void +IN_SetControlType(int player,ControlType type) +{ + // DEBUG - check that requested type is present? + Controls[player] = type; +} + +#if 0 +/////////////////////////////////////////////////////////////////////////// +// +// IN_StartDemoRecord() - Starts the demo recording, using a buffer the +// size passed. Returns if the buffer allocation was successful +// +/////////////////////////////////////////////////////////////////////////// +boolean +IN_StartDemoRecord(word bufsize) +{ + if (!bufsize) + return(false); + + MM_GetPtr((memptr *)&DemoBuffer,bufsize); + DemoMode = demo_Record; + DemoSize = bufsize & ~1; + DemoOffset = 0; + DemoBuffer[0] = DemoBuffer[1] = 0; + + return(true); +} + +/////////////////////////////////////////////////////////////////////////// +// +// IN_StartDemoPlayback() - Plays back the demo pointed to of the given size +// +/////////////////////////////////////////////////////////////////////////// +void +IN_StartDemoPlayback(byte /*_1seg*/ *buffer,word bufsize) +{ + DemoBuffer = buffer; + DemoMode = demo_Playback; + DemoSize = bufsize & ~1; + DemoOffset = 0; +} + +/////////////////////////////////////////////////////////////////////////// +// +// IN_StopDemo() - Turns off demo mode +// +/////////////////////////////////////////////////////////////////////////// +void +IN_StopDemo(void) +{ + if ((DemoMode == demo_Record) && DemoOffset) + DemoOffset += 2; + + DemoMode = demo_Off; +} + +/////////////////////////////////////////////////////////////////////////// +// +// IN_FreeDemoBuffer() - Frees the demo buffer, if it's been allocated +// +/////////////////////////////////////////////////////////////////////////// +void +IN_FreeDemoBuffer(void) +{ + if (DemoBuffer) + MM_FreePtr((memptr *)&DemoBuffer); +} +#endif + + +#if 0 +/////////////////////////////////////////////////////////////////////////// +// +// IN_GetScanName() - Returns a string containing the name of the +// specified scan code +// +/////////////////////////////////////////////////////////////////////////// +byte * +IN_GetScanName(ScanCode scan) +{ + byte **p; + ScanCode far *s; + + for (s = ExtScanCodes,p = ExtScanNames;*s;p++,s++) + if (*s == scan) + return(*p); + + return(ScanNames[scan]); +} +#endif + + +/////////////////////////////////////////////////////////////////////////// +// +// IN_WaitForKey() - Waits for a scan code, then clears LastScan and +// returns the scan code +// +/////////////////////////////////////////////////////////////////////////// +ScanCode +IN_WaitForKey(void) +{ + ScanCode result; + + while (!(result = LastScan)) + ; + LastScan = 0; + return(result); +} + +/////////////////////////////////////////////////////////////////////////// +// +// IN_WaitForASCII() - Waits for an ASCII char, then clears LastASCII and +// returns the ASCII value +// +/////////////////////////////////////////////////////////////////////////// +char +IN_WaitForASCII(void) +{ + char result; + + while (!(result = LastASCII)) + ; + LastASCII = '\0'; + return(result); +} + +/////////////////////////////////////////////////////////////////////////// +// +// IN_AckBack() - Waits for either an ASCII keypress or a button press +// +/////////////////////////////////////////////////////////////////////////// +void +IN_AckBack(void) +{ + word i; + + while (!LastScan) + { + if (MousePresent) + { + if (INL_GetMouseButtons()) + { + while (INL_GetMouseButtons()) + ; + return; + } + } + + for (i = 0;i < MaxJoys;i++) + { + if (JoysPresent[i]) + { + if (IN_GetJoyButtonsDB(i)) + { + while (IN_GetJoyButtonsDB(i)) + ; + return; + } + } + } + } + + IN_ClearKey(LastScan); + LastScan = sc_None; +} + +/////////////////////////////////////////////////////////////////////////// +// +// IN_Ack() - Clears user input & then calls IN_AckBack() +// +/////////////////////////////////////////////////////////////////////////// +void +IN_Ack(void) +{ + word i; + + IN_ClearKey(LastScan); + LastScan = sc_None; + + if (MousePresent) + while (INL_GetMouseButtons()) + ; + for (i = 0;i < MaxJoys;i++) + if (JoysPresent[i]) + while (IN_GetJoyButtonsDB(i)) + ; + + IN_AckBack(); +} + +/////////////////////////////////////////////////////////////////////////// +// +// IN_IsUserInput() - Returns true if a key has been pressed or a button +// is down +// +/////////////////////////////////////////////////////////////////////////// +boolean +IN_IsUserInput(void) +{ + boolean result; + word i; + + result = LastScan; + + if (MousePresent) + if (INL_GetMouseButtons()) + result = true; + + for (i = 0;i < MaxJoys;i++) + if (JoysPresent[i]) + if (INL_GetJoyButtons(i)) + result = true; + + return(result); +} + +/////////////////////////////////////////////////////////////////////////// +// +// IN_UserInput() - Waits for the specified delay time (in ticks) or the +// user pressing a key or a mouse button. If the clear flag is set, it +// then either clears the key or waits for the user to let the mouse +// button up. +// +/////////////////////////////////////////////////////////////////////////// +boolean +IN_UserInput(longword delay,boolean clear) +{ + longword lasttime; + + lasttime = TimeCount; + do + { + if (IN_IsUserInput()) + { + if (clear) + IN_AckBack(); + return(true); + } + } while (TimeCount - lasttime < delay); + return(false); +} diff --git a/16/cawat/ID_IN.H b/16/cawat/ID_IN.H new file mode 100644 index 00000000..f1f0ef89 --- /dev/null +++ b/16/cawat/ID_IN.H @@ -0,0 +1,214 @@ +/* Catacomb Armageddon Source Code + * Copyright (C) 1993-2014 Flat Rock Software + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +// +// ID Engine +// ID_IN.h - Header file for Input Manager +// v1.0d1 +// By Jason Blochowiak +// + +#include "lib_head.h" + +#ifndef __TYPES__ +#include "ID_Types.h" +#endif + +#ifndef __ID_IN__ +#define __ID_IN__ + +#ifdef __DEBUG__ +#define __DEBUG_InputMgr__ +#endif + +#define MaxPlayers 4 +#define MaxKbds 2 +#define MaxJoys 2 +#define NumCodes 128 + +typedef byte ScanCode; +#define sc_None 0 +#define sc_Bad 0xff +#define sc_Return 0x1c +#define sc_Enter sc_Return +#define sc_Escape 0x01 +#define sc_Space 0x39 +#define sc_BackSpace 0x0e +#define sc_Tab 0x0f +#define sc_Alt 0x38 +#define sc_Control 0x1d +#define sc_CapsLock 0x3a +#define sc_LShift 0x2a +#define sc_RShift 0x36 +#define sc_UpArrow 0x48 +#define sc_DownArrow 0x50 +#define sc_LeftArrow 0x4b +#define sc_RightArrow 0x4d +#define sc_Insert 0x52 +#define sc_Delete 0x53 +#define sc_Home 0x47 +#define sc_End 0x4f +#define sc_PgUp 0x49 +#define sc_PgDn 0x51 +#define sc_F1 0x3b +#define sc_F2 0x3c +#define sc_F3 0x3d +#define sc_F4 0x3e +#define sc_F5 0x3f +#define sc_F6 0x40 +#define sc_F7 0x41 +#define sc_F8 0x42 +#define sc_F9 0x43 +#define sc_F10 0x44 +#define sc_F11 0x57 +#define sc_F12 0x59 + +#define sc_A 0x1e +#define sc_B 0x30 +#define sc_C 0x2e +#define sc_D 0x20 +#define sc_E 0x12 +#define sc_F 0x21 +#define sc_G 0x22 +#define sc_H 0x23 +#define sc_I 0x17 +#define sc_J 0x24 +#define sc_K 0x25 +#define sc_L 0x26 +#define sc_M 0x32 +#define sc_N 0x31 +#define sc_O 0x18 +#define sc_P 0x19 +#define sc_Q 0x10 +#define sc_R 0x13 +#define sc_S 0x1f +#define sc_T 0x14 +#define sc_U 0x16 +#define sc_V 0x2f +#define sc_W 0x11 +#define sc_X 0x2d +#define sc_Y 0x15 +#define sc_Z 0x2c + +#define key_None 0 +#define key_Return 0x0d +#define key_Enter key_Return +#define key_Escape 0x1b +#define key_Space 0x20 +#define key_BackSpace 0x08 +#define key_Tab 0x09 +#define key_Delete 0x7f + +// Stuff for the mouse +#define MReset 0 +#define MButtons 3 +#define MDelta 11 + +#define MouseInt 0x33 +#define Mouse(x) _AX = x,geninterrupt(MouseInt) + +typedef enum { + demo_Off,demo_Record,demo_Playback,demo_PlayDone + } Demo; +typedef enum { + ctrl_None, // MDM (GAMERS EDGE) - added + ctrl_Keyboard, + ctrl_Keyboard1 = ctrl_Keyboard,ctrl_Keyboard2, + ctrl_Joystick, + ctrl_Joystick1 = ctrl_Joystick,ctrl_Joystick2, + ctrl_Mouse + } ControlType; +typedef enum { + motion_Left = -1,motion_Up = -1, + motion_None = 0, + motion_Right = 1,motion_Down = 1 + } Motion; +typedef enum { + dir_North,dir_NorthEast, + dir_East,dir_SouthEast, + dir_South,dir_SouthWest, + dir_West,dir_NorthWest, + dir_None + } Direction; +typedef struct { + boolean button0,button1; + int x,y; + Motion xaxis,yaxis; + Direction dir; + } CursorInfo; +typedef CursorInfo ControlInfo; +typedef struct { + ScanCode button0,button1, + upleft, up, upright, + left, right, + downleft, down, downright; + } KeyboardDef; +typedef struct { + word joyMinX,joyMinY, + threshMinX,threshMinY, + threshMaxX,threshMaxY, + joyMaxX,joyMaxY, + joyMultXL,joyMultYL, + joyMultXH,joyMultYH; + } JoystickDef; +// Global variables +extern boolean Keyboard[], + MousePresent, + JoysPresent[]; +extern boolean Paused; +extern char LastASCII; +extern ScanCode LastScan; +extern KeyboardDef KbdDefs[]; +extern JoystickDef JoyDefs[]; +extern ControlType Controls[MaxPlayers]; + +extern boolean JoystickCalibrated; // MDM (GAMERS EDGE) - added +extern ControlType ControlTypeUsed; // MDM (GAMERS EDGE) - added + +extern Demo DemoMode; +extern byte _seg *DemoBuffer; +extern word DemoOffset,DemoSize; + +// Function prototypes +#define IN_KeyDown(code) (Keyboard[(code)]) +#define IN_ClearKey(code) {Keyboard[code] = false;\ + if (code == LastScan) LastScan = sc_None;} + +// DEBUG - put names in prototypes +extern void IN_Startup(void),IN_Shutdown(void), + IN_Default(boolean gotit,ControlType in), + IN_SetKeyHook(void (*)()), + IN_ClearKeysDown(void), + IN_ReadCursor(CursorInfo *), + IN_ReadControl(int,ControlInfo *), + IN_SetControlType(int,ControlType), + IN_GetJoyAbs(word joy,word *xp,word *yp), + IN_SetupJoy(word joy,word minx,word maxx, + word miny,word maxy), + IN_StartDemoPlayback(byte _seg *buffer,word bufsize), + IN_StopDemo(void),IN_FreeDemoBuffer(void), + IN_Ack(void),IN_AckBack(void); +extern boolean IN_UserInput(longword delay,boolean clear), + IN_IsUserInput(void), + IN_StartDemoRecord(word bufsize); +extern byte *IN_GetScanName(ScanCode); +extern char IN_WaitForASCII(void); +extern ScanCode IN_WaitForKey(void); +extern word IN_GetJoyButtonsDB(word joy); + +#endif diff --git a/16/cawat/ID_MM.C b/16/cawat/ID_MM.C new file mode 100644 index 00000000..1331a0c4 --- /dev/null +++ b/16/cawat/ID_MM.C @@ -0,0 +1,1150 @@ +/* Catacomb Armageddon Source Code + * Copyright (C) 1993-2014 Flat Rock Software + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +// NEWMM.C + +/* +============================================================================= + + ID software memory manager + -------------------------- + +Primary coder: John Carmack + +RELIES ON +--------- +Quit (char *error) function + + +WORK TO DO +---------- +MM_SizePtr to change the size of a given pointer + +Multiple purge levels utilized + +EMS / XMS unmanaged routines + +============================================================================= +*/ + +//#include "LIB_HEAD.H" +#include "ID_MM.H" +//#pragma hdrstop + +//#pragma warn -pro +//#pragma warn -use + + +#if 1 // 1 == Debug/Dev ; 0 == Production/final + +#define OUT_OF_MEM_MSG "MM_GetPtr: Out of memory!\nYou were short :%ld bytes" + +#else + + +#define OUT_OF_MEM_MSG "\npee\n" + +#endif + + +/* +============================================================================= + + LOCAL INFO + +============================================================================= +*/ + +#define LOCKBIT 0x80 // if set in attributes, block cannot be moved +#define PURGEBITS 3 // 0-3 level, 0= unpurgable, 3= purge first +#define PURGEMASK 0xfffc +#define BASEATTRIBUTES 0 // unlocked, non purgable + +#define MAXUMBS 10 + +typedef struct mmblockstruct +{ + unsigned start,length; + unsigned attributes; + memptr *useptr; // pointer to the segment start + struct mmblockstruct far *next; +} mmblocktype; + + +//#define GETNEWBLOCK {if(!(mmnew=mmfree))Quit("MM_GETNEWBLOCK: No free blocks!");mmfree=mmfree->next;} + +#define GETNEWBLOCK {if(!mmfree)MML_ClearBlock();mmnew=mmfree;mmfree=mmfree->next;} + +#define FREEBLOCK(x) {*x->useptr=NULL;x->next=mmfree;mmfree=x;} + +/* +============================================================================= + + GLOBAL VARIABLES + +============================================================================= +*/ + +mminfotype mminfo; +memptr bufferseg; +boolean mmerror; + +void (* beforesort) (void); +void (* aftersort) (void); + +/* +============================================================================= + + LOCAL VARIABLES + +============================================================================= +*/ + +boolean mmstarted; + +void huge *hugeheap; +void far *farheap; +void *nearheap; + +mmblocktype far mmblocks[MAXBLOCKS] + ,far *mmhead,far *mmfree,far *mmrover,far *mmnew; + +boolean bombonerror; + +unsigned totalEMSpages,freeEMSpages,EMSpageframe,EMSpagesmapped,EMShandle; + +void (* XMSaddr) (void); // far pointer to XMS driver + +unsigned numUMBs,UMBbase[MAXUMBS]; + +//========================================================================== + +// +// local prototypes +// + +boolean MML_CheckForEMS (void); +void MML_ShutdownEMS (void); +void MM_MapEMS (void); +boolean MML_CheckForXMS (void); +void MML_ShutdownXMS (void); +void MML_UseSpace (unsigned segstart, unsigned seglength); +void MML_ClearBlock (void); + +//========================================================================== + +/* +====================== += += MML_CheckForEMS += += Routine from p36 of Extending DOS += +======================= +*/ + +boolean MML_CheckForEMS (void) +{ + boolean emmcfems; + char emmname[] = "EMMXXXX0"; + + __asm { + mov dx,OFFSET emmname[0] + mov ax,0x3d00 + int 0x21 // try to open EMMXXXX0 device + jc error + + mov bx,ax + mov ax,0x4400 + + int 0x21 // get device info + jc error + + and dx,0x80 + jz error + + mov ax,0x4407 + + int 0x21 // get status + jc error + or al,al + jz error + + mov ah,0x3e + int 0x21 // close handle + jc error + // + // EMS is good + // + mov emmcfems,1 + jmp End + error: + // + // EMS is bad + // + mov emmcfems,0 + End: + } + return(emmcfems); +} + + +/* +====================== += += MML_SetupEMS += +======================= +*/ + +void MML_SetupEMS (void) +{ + char str[80],str2[10]; + unsigned error; + + totalEMSpages = freeEMSpages = EMSpageframe = EMSpagesmapped = 0; + + __asm { + mov ah,EMS_STATUS + int EMS_INT // make sure EMS hardware is present + or ah,ah + jnz error + + mov ah,EMS_VERSION + int EMS_INT + or ah,ah + jnz error + cmp al,0x32 // only work on ems 3.2 or greater + jb error + + mov ah,EMS_GETFRAME + int EMS_INT // find the page frame address + or ah,ah + jnz error + mov [EMSpageframe],bx + + mov ah,EMS_GETPAGES + int EMS_INT // find out how much EMS is there + or ah,ah + jnz error + mov [totalEMSpages],dx + mov [freeEMSpages],bx + or bx,bx + jz noEMS // no EMS at all to allocate + + cmp bx,4 + jle getpages // there is only 1,2,3,or 4 pages + mov bx,4 // we can't use more than 4 pages + } + +getpages: +asm { + mov [EMSpagesmapped],bx + mov ah,EMS_ALLOCPAGES // allocate up to 64k of EMS + int EMS_INT + or ah,ah + jnz error + mov [EMShandle],dx + } + return; + +error: + error = _AH; + strcpy (str,"MML_SetupEMS: EMS error 0x"); + itoa(error,str2,16); + strcpy (str,str2); + Quit (str); + +noEMS: +; +} + + +/* +====================== += += MML_ShutdownEMS += +======================= +*/ + +void MML_ShutdownEMS (void) +{ + if (!EMShandle) + return; + + { + mov ah,EMS_FREEPAGES + mov dx,[EMShandle] + int EMS_INT + or ah,ah + jz ok + } + + Quit ("MML_ShutdownEMS: Error freeing EMS!"); + +ok: +; +} + +/* +==================== += += MM_MapEMS += += Maps the 64k of EMS used by memory manager into the page frame += for general use. This only needs to be called if you are keeping += other things in EMS. += +==================== +*/ + +void MM_MapEMS (void) +{ + char str[80],str2[10]; + unsigned error; + int i; + + for (i=0;istart+scan->length < segstart) + { + last = scan; + scan = scan->next; + } + +// +// take the given range out of the block +// + oldend = scan->start + scan->length; + extra = oldend - (segstart+seglength); + if (extra < 0) + Quit ("MML_UseSpace: Segment spans two blocks!"); + + if (segstart == scan->start) + { + last->next = scan->next; // unlink block + FREEBLOCK(scan); + scan = last; + } + else + scan->length = segstart-scan->start; // shorten block + + if (extra > 0) + { + GETNEWBLOCK; + mmnew->next = scan->next; + scan->next = mmnew; + mmnew->start = segstart+seglength; + mmnew->length = extra; + mmnew->attributes = LOCKBIT; + } + +} + +//========================================================================== + +/* +==================== += += MML_ClearBlock += += We are out of blocks, so free a purgable block += +==================== +*/ + +void MML_ClearBlock (void) +{ + mmblocktype far *scan,far *last; + + scan = mmhead->next; + + while (scan) + { + if (!(scan->attributes&LOCKBIT) && (scan->attributes&PURGEBITS) ) + { + MM_FreePtr(scan->useptr); + return; + } + scan = scan->next; + } + + Quit ("MM_ClearBlock: No purgable blocks!"); +} + + +//========================================================================== + +/* +=================== += += MM_Startup += += Grabs all space from turbo with malloc/farmalloc += Allocates bufferseg misc buffer += +=================== +*/ + +static char *ParmStrings[] = {"noems","noxms",""}; + +void MM_Startup (void) +{ + int i; + unsigned long length; + void far *start; + unsigned segstart,seglength,endfree; + + if (mmstarted) + MM_Shutdown (); + + + mmstarted = true; + bombonerror = true; +// +// set up the linked list (everything in the free list; +// + mmhead = NULL; + mmfree = &mmblocks[0]; + for (i=0;istart = 0; + mmnew->length = 0xffff; + mmnew->attributes = LOCKBIT; + mmnew->next = NULL; + mmrover = mmhead; + + +// +// get all available near conventional memory segments +// + length=coreleft(); + start = (void far *)(nearheap = malloc(length)); + + length -= 16-(FP_OFF(start)&15); + length -= SAVENEARHEAP; + seglength = length / 16; // now in paragraphs + segstart = FP_SEG(start)+(FP_OFF(start)+15)/16; + MML_UseSpace (segstart,seglength); + mminfo.nearheap = length; + +// +// get all available far conventional memory segments +// + length=farcoreleft(); + start = farheap = farmalloc(length); + length -= 16-(FP_OFF(start)&15); + length -= SAVEFARHEAP; + seglength = length / 16; // now in paragraphs + segstart = FP_SEG(start)+(FP_OFF(start)+15)/16; + MML_UseSpace (segstart,seglength); + mminfo.farheap = length; + mminfo.mainmem = mminfo.nearheap + mminfo.farheap; + + +// +// detect EMS and allocate up to 64K at page frame +// + mminfo.EMSmem = 0; + for (i = 1;i < _argc;i++) + { + if ( US_CheckParm(_argv[i],ParmStrings) == 0) + goto emsskip; // param NOEMS + } + + if (MML_CheckForEMS()) + { + MML_SetupEMS(); // allocate space + MML_UseSpace (EMSpageframe,EMSpagesmapped*0x400); + MM_MapEMS(); // map in used pages + mminfo.EMSmem = EMSpagesmapped*0x4000l; + } + +// +// detect XMS and get upper memory blocks +// +emsskip: + mminfo.XMSmem = 0; + for (i = 1;i < _argc;i++) + { + if ( US_CheckParm(_argv[i],ParmStrings) == 0) + goto xmsskip; // param NOXMS + } + + if (MML_CheckForXMS()) + MML_SetupXMS(); // allocate as many UMBs as possible + +// +// allocate the misc buffer +// +xmsskip: + mmrover = mmhead; // start looking for space after low block + + MM_GetPtr (&bufferseg,BUFFERSIZE); +} + +//========================================================================== + +/* +==================== += += MM_Shutdown += += Frees all conventional, EMS, and XMS allocated += +==================== +*/ + +void MM_Shutdown (void) +{ + if (!mmstarted) + return; + + farfree (farheap); + free (nearheap); + MML_ShutdownEMS (); + MML_ShutdownXMS (); +} + +//========================================================================== + +/* +==================== += += MM_GetPtr += += Allocates an unlocked, unpurgable block += +==================== +*/ + +void MM_GetPtr (memptr *baseptr,unsigned long size) +{ + mmblocktype far *scan,far *lastscan,far *endscan + ,far *purge,far *next; + int search; + unsigned needed,startseg; + + needed = (size+15)/16; // convert size from bytes to paragraphs + + GETNEWBLOCK; // fill in start and next after a spot is found + mmnew->length = needed; + mmnew->useptr = baseptr; + mmnew->attributes = BASEATTRIBUTES; + + for (search = 0; search<3; search++) + { + // + // first search: try to allocate right after the rover, then on up + // second search: search from the head pointer up to the rover + // third search: compress memory, then scan from start + if (search == 1 && mmrover == mmhead) + search++; + + switch (search) + { + case 0: + lastscan = mmrover; + scan = mmrover->next; + endscan = NULL; + break; + case 1: + lastscan = mmhead; + scan = mmhead->next; + endscan = mmrover; + break; + case 2: + MM_SortMem (); + lastscan = mmhead; + scan = mmhead->next; + endscan = NULL; + break; + } + + startseg = lastscan->start + lastscan->length; + + while (scan != endscan) + { + if (scan->start - startseg >= needed) + { + // + // got enough space between the end of lastscan and + // the start of scan, so throw out anything in the middle + // and allocate the new block + // + purge = lastscan->next; + lastscan->next = mmnew; + mmnew->start = *(unsigned *)baseptr = startseg; + mmnew->next = scan; + while ( purge != scan) + { // free the purgable block + next = purge->next; + FREEBLOCK(purge); + purge = next; // purge another if not at scan + } + mmrover = mmnew; + return; // good allocation! + } + + // + // if this block is purge level zero or locked, skip past it + // + if ( (scan->attributes & LOCKBIT) + || !(scan->attributes & PURGEBITS) ) + { + lastscan = scan; + startseg = lastscan->start + lastscan->length; + } + + + scan=scan->next; // look at next line + } + } + + if (bombonerror) + Quit (OUT_OF_MEM_MSG,(size-mminfo.nearheap)); + else + mmerror = true; +} + +//========================================================================== + +/* +==================== += += MM_FreePtr += += Allocates an unlocked, unpurgable block += +==================== +*/ + +void MM_FreePtr (memptr *baseptr) +{ + mmblocktype far *scan,far *last; + + last = mmhead; + scan = last->next; + + if (baseptr == mmrover->useptr) // removed the last allocated block + mmrover = mmhead; + + while (scan->useptr != baseptr && scan) + { + last = scan; + scan = scan->next; + } + + if (!scan) + Quit ("MM_FreePtr: Block not found!"); + + last->next = scan->next; + + FREEBLOCK(scan); +} +//========================================================================== + +/* +===================== += += MM_SetPurge += += Sets the purge level for a block (locked blocks cannot be made purgable) += +===================== +*/ + +void MM_SetPurge (memptr *baseptr, int purge) +{ + mmblocktype far *start; + + start = mmrover; + + do + { + if (mmrover->useptr == baseptr) + break; + + mmrover = mmrover->next; + + if (!mmrover) + mmrover = mmhead; + else if (mmrover == start) + Quit ("MM_SetPurge: Block not found!"); + + } while (1); + + mmrover->attributes &= ~PURGEBITS; + mmrover->attributes |= purge; +} + +//========================================================================== + +/* +===================== += += MM_SetLock += += Locks / unlocks the block += +===================== +*/ + +void MM_SetLock (memptr *baseptr, boolean locked) +{ + mmblocktype far *start; + + start = mmrover; + + do + { + if (mmrover->useptr == baseptr) + break; + + mmrover = mmrover->next; + + if (!mmrover) + mmrover = mmhead; + else if (mmrover == start) + Quit ("MM_SetLock: Block not found!"); + + } while (1); + + mmrover->attributes &= ~LOCKBIT; + mmrover->attributes |= locked*LOCKBIT; +} + +//========================================================================== + +/* +===================== += += MM_SortMem += += Throws out all purgable stuff and compresses movable blocks += +===================== +*/ + +void MM_SortMem (void) +{ + mmblocktype far *scan,far *last,far *next; + unsigned start,length,source,dest,oldborder; + int playing; + + // + // lock down a currently playing sound + // + playing = SD_SoundPlaying (); + if (playing) + { + switch (SoundMode) + { + case sdm_PC: + playing += STARTPCSOUNDS; + break; + case sdm_AdLib: + playing += STARTADLIBSOUNDS; + break; + } + MM_SetLock(&(memptr)audiosegs[playing],true); + } + + + SD_StopSound(); +// oldborder = bordercolor; +// VW_ColorBorder (15); + + if (beforesort) + beforesort(); + + scan = mmhead; + + last = NULL; // shut up compiler warning + + while (scan) + { + if (scan->attributes & LOCKBIT) + { + // + // block is locked, so try to pile later blocks right after it + // + start = scan->start + scan->length; + } + else + { + if (scan->attributes & PURGEBITS) + { + // + // throw out the purgable block + // + next = scan->next; + FREEBLOCK(scan); + last->next = next; + scan = next; + continue; + } + else + { + // + // push the non purgable block on top of the last moved block + // + if (scan->start != start) + { + length = scan->length; + source = scan->start; + dest = start; + while (length > 0xf00) + { + movedata(source,0,dest,0,0xf00*16); + length -= 0xf00; + source += 0xf00; + dest += 0xf00; + } + movedata(source,0,dest,0,length*16); + + scan->start = start; + *(unsigned *)scan->useptr = start; + } + start = scan->start + scan->length; + } + } + + last = scan; + scan = scan->next; // go to next block + } + + mmrover = mmhead; + + if (aftersort) + aftersort(); + +// VW_ColorBorder (oldborder); + + if (playing) + MM_SetLock(&(memptr)audiosegs[playing],false); +} + + +//========================================================================== + +#if 0 +/* +===================== += += MM_ShowMemory += +===================== +*/ + +void MM_ShowMemory (void) +{ + mmblocktype far *scan; + unsigned color,temp; + long end,owner; + char scratch[80],str[10]; + + VW_SetDefaultColors(); + VW_SetLineWidth(40); + temp = bufferofs; + bufferofs = 0; + VW_SetScreen (0,0); + + scan = mmhead; + + end = -1; + +//CA_OpenDebug (); + + while (scan) + { + if (scan->attributes & PURGEBITS) + color = 5; // dark purple = purgable + else + color = 9; // medium blue = non purgable + if (scan->attributes & LOCKBIT) + color = 12; // red = locked + if (scan->start<=end) + Quit ("MM_ShowMemory: Memory block order currupted!"); + end = scan->start+scan->length-1; + VW_Hlin(scan->start,(unsigned)end,0,color); + VW_Plot(scan->start,0,15); + if (scan->next->start > end+1) + VW_Hlin(end+1,scan->next->start,0,0); // black = free + +#if 0 +strcpy (scratch,"Size:"); +ltoa ((long)scan->length*16,str,10); +strcat (scratch,str); +strcat (scratch,"\tOwner:0x"); +owner = (unsigned)scan->useptr; +ultoa (owner,str,16); +strcat (scratch,str); +strcat (scratch,"\n"); +write (debughandle,scratch,strlen(scratch)); +#endif + + scan = scan->next; + } + +//CA_CloseDebug (); + + IN_Ack(); + VW_SetLineWidth(64); + bufferofs = temp; +} +#endif + +//========================================================================== + + +/* +====================== += += MM_UnusedMemory += += Returns the total free space without purging += +====================== +*/ + +long MM_UnusedMemory (void) +{ + unsigned free; + mmblocktype far *scan; + + free = 0; + scan = mmhead; + + while (scan->next) + { + free += scan->next->start - (scan->start + scan->length); + scan = scan->next; + } + + return free*16l; +} + +//========================================================================== + + +/* +====================== += += MM_TotalFree += += Returns the total free space with purging += +====================== +*/ + +long MM_TotalFree (void) +{ + unsigned free; + mmblocktype far *scan; + + free = 0; + scan = mmhead; + + while (scan->next) + { + if ((scan->attributes&PURGEBITS) && !(scan->attributes&LOCKBIT)) + free += scan->length; + free += scan->next->start - (scan->start + scan->length); + scan = scan->next; + } + + return free*16l; +} + +//========================================================================== + +/* +===================== += += MM_BombOnError += +===================== +*/ + +void MM_BombOnError (boolean bomb) +{ + bombonerror = bomb; +} + + diff --git a/16/cawat/ID_MM.H b/16/cawat/ID_MM.H new file mode 100644 index 00000000..968c5a75 --- /dev/null +++ b/16/cawat/ID_MM.H @@ -0,0 +1,110 @@ +/* Catacomb Armageddon Source Code + * Copyright (C) 1993-2014 Flat Rock Software + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +// ID_MM.H + +#ifndef __ID_EXMM__ + +#define __ID_EXMM__ + +#include "LIB_HEAD.H" + +#define SAVENEARHEAP 0x400 // space to leave in data segment +#define SAVEFARHEAP 0 // space to leave in far heap + +#define BUFFERSIZE 0x1000 // miscelanious, allways available buffer + +#define MAXBLOCKS 600 + + +//-------- + +#define EMS_INT 0x67 + +#define EMS_STATUS 0x40 +#define EMS_GETFRAME 0x41 +#define EMS_GETPAGES 0x42 +#define EMS_ALLOCPAGES 0x43 +#define EMS_MAPPAGE 0x44 +#define EMS_FREEPAGES 0x45 +#define EMS_VERSION 0x46 + +//-------- + +#define XMS_VERSION 0x00 + +#define XMS_ALLOCHMA 0x01 +#define XMS_FREEHMA 0x02 + +#define XMS_GENABLEA20 0x03 +#define XMS_GDISABLEA20 0x04 +#define XMS_LENABLEA20 0x05 +#define XMS_LDISABLEA20 0x06 +#define XMS_QUERYA20 0x07 + +#define XMS_QUERYREE 0x08 +#define XMS_ALLOC 0x09 +#define XMS_FREE 0x0A +#define XMS_MOVE 0x0B +#define XMS_LOCK 0x0C +#define XMS_UNLOCK 0x0D +#define XMS_GETINFO 0x0E +#define XMS_RESIZE 0x0F + +#define XMS_ALLOCUMB 0x10 +#define XMS_FREEUMB 0x11 + +//========================================================================== + +typedef void /*_seg*/ * memptr; + +typedef struct +{ + long nearheap,farheap,EMSmem,XMSmem,mainmem; +} mminfotype; + +//========================================================================== + +extern mminfotype mminfo; +extern memptr bufferseg; +extern boolean mmerror; + +extern void (* beforesort) (void); +extern void (* aftersort) (void); + +//========================================================================== + +void MM_Startup (void); +void MM_Shutdown (void); +void MM_MapEMS (void); + +void MM_GetPtr (memptr *baseptr,unsigned long size); +void MM_FreePtr (memptr *baseptr); + +void MM_SetPurge (memptr *baseptr, int purge); +void MM_SetLock (memptr *baseptr, boolean locked); +void MM_SortMem (void); + +void MM_ShowMemory (void); + +long MM_UnusedMemory (void); +long MM_TotalFree (void); + +void MM_BombOnError (boolean bomb); + +#endif diff --git a/16/cawat/ID_RF.C b/16/cawat/ID_RF.C new file mode 100644 index 00000000..62dfa91b --- /dev/null +++ b/16/cawat/ID_RF.C @@ -0,0 +1,2845 @@ +/* Catacomb Armageddon Source Code + * Copyright (C) 1993-2014 Flat Rock Software + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +// ID_RF.C + +/* +============================================================================= + +notes +----- + +scrolling more than one tile / refresh forces a total redraw + +two overlapping sprites of equal priority can change drawing order when +updated + +============================================================================= +*/ + +#include "ID_HEADS.H" +#pragma hdrstop + +/* +============================================================================= + + LOCAL CONSTANTS + +============================================================================= +*/ + +#define SCREENTILESWIDE 20 +#define SCREENTILESHIGH 13 + +#define SCREENSPACE (SCREENWIDTH*240) +#define FREEEGAMEM (0x10000l-3l*SCREENSPACE) + +// +// the update array must have enough space for two screens that can float +// up two two tiles each way +// +// (PORTTILESWIDE+1)*PORTTILESHIGH must be even so the arrays can be cleared +// by word width instructions + +#define UPDATESCREENSIZE (UPDATEWIDE*PORTTILESHIGH+2) +#define UPDATESPARESIZE (UPDATEWIDE*2+4) +#define UPDATESIZE (UPDATESCREENSIZE+2*UPDATESPARESIZE) + +#define G_EGASX_SHIFT 7 // global >> ?? = screen x +#define G_CGASX_SHIFT 6 // global >> ?? = screen x +#define G_SY_SHIFT 4 // global >> ?? = screen y + +unsigned SX_T_SHIFT; // screen x >> ?? = tile EGA = 1, CGA = 2; +#define SY_T_SHIFT 4 // screen y >> ?? = tile + + +#define EGAPORTSCREENWIDE 42 +#define CGAPORTSCREENWIDE 84 +#define PORTSCREENHIGH 224 + +#define UPDATESCREENSIZE (UPDATEWIDE*PORTTILESHIGH+2) +#define UPDATESPARESIZE (UPDATEWIDE*2+4) +#define UPDATESIZE (UPDATESCREENSIZE+2*UPDATESPARESIZE) + +#define MAXSCROLLEDGES 6 + +/* +============================================================================= + + LOCAL TYPES + +============================================================================= +*/ + +typedef struct spriteliststruct +{ + int screenx,screeny; + int width,height; + + unsigned grseg,sourceofs,planesize; + drawtype draw; + unsigned tilex,tiley,tilewide,tilehigh; + int priority,updatecount; + struct spriteliststruct **prevptr,*nextsprite; +} spritelisttype; + + +typedef struct +{ + int screenx,screeny; + int width,height; +} eraseblocktype; + + +typedef struct +{ + unsigned current; // foreground tiles have high bit set + int count; +} tiletype; + + +typedef struct animtilestruct +{ + unsigned x,y,tile; + tiletype *chain; + unsigned far *mapplane; + struct animtilestruct **prevptr,*nexttile; +} animtiletype; + +/* +============================================================================= + + GLOBAL VARIABLES + +============================================================================= +*/ + +unsigned tics; +long lasttimecount; + +boolean compatability; // crippled refresh for wierdo SVGAs + +unsigned mapwidth,mapheight,mapbyteswide,mapwordswide + ,mapbytesextra,mapwordsextra; +unsigned mapbwidthtable[MAXMAPHEIGHT]; + +// +// Global : Actor coordinates are in this, at 1/16 th of a pixel, to allow +// for fractional movement and acceleration. +// +// Tiles : Tile offsets from the upper left corner of the current map. +// +// Screen : Graphics level offsets from map origin, x in bytes, y in pixels. +// originxscreen is the same spot as originxtile, just with extra precision +// so graphics don't need to be done in tile boundaries. +// + +unsigned originxglobal,originyglobal; +unsigned originxtile,originytile; +unsigned originxscreen,originyscreen; +unsigned originmap; +unsigned originxmin,originxmax,originymin,originymax; + +unsigned masterofs; + +// +// Table of the offsets from bufferofs of each tile spot in the +// view port. The extra wide tile should never be drawn, but the space +// is needed to account for the extra 0 in the update arrays. Built by +// RF_Startup +// + +unsigned blockstarts[UPDATEWIDE*UPDATEHIGH]; +unsigned updatemapofs[UPDATEWIDE*UPDATEHIGH]; + +unsigned uwidthtable[PORTTILESHIGH]; // lookup instead of multiply + +byte update[2][UPDATESIZE]; +byte *updateptr,*baseupdateptr, // current start of update window + *updatestart[2], + *baseupdatestart[2]; + +/* +============================================================================= + + LOCAL VARIABLES + +============================================================================= +*/ + +static char scratch[20],str[80]; + +tiletype allanims[MAXANIMTYPES]; +unsigned numanimchains; + +void (*refreshvector) (void); + +unsigned screenstart[3] = + {0,SCREENSPACE,SCREENSPACE*2}; + +unsigned xpanmask; // prevent panning to odd pixels + +unsigned screenpage; // screen currently being displayed +unsigned otherpage; + + +spritelisttype spritearray[MAXSPRITES],*prioritystart[PRIORITIES], + *spritefreeptr; + +animtiletype animarray[MAXANIMTILES],*animhead,*animfreeptr; + +int animfreespot; + +eraseblocktype eraselist[2][MAXSPRITES],*eraselistptr[2]; + +int hscrollblocks,vscrollblocks; +int hscrolledge[MAXSCROLLEDGES],vscrolledge[MAXSCROLLEDGES]; + +/* +============================================================================= + + LOCAL PROTOTYPES + +============================================================================= +*/ + +void RFL_NewTile (unsigned updateoffset); +void RFL_MaskForegroundTiles (void); +void RFL_UpdateTiles (void); + +void RFL_BoundScroll (int x, int y); +void RFL_CalcOriginStuff (long x, long y); +void RFL_ClearScrollBlocks (void); +void RFL_InitSpriteList (void); +void RFL_InitAnimList (void); +void RFL_CheckForAnimTile (unsigned x, unsigned y); +void RFL_AnimateTiles (void); +void RFL_RemoveAnimsOnX (unsigned x); +void RFL_RemoveAnimsOnY (unsigned y); +void RFL_EraseBlocks (void); +void RFL_UpdateSprites (void); + + +/* +============================================================================= + + GRMODE INDEPENDANT ROUTINES + +============================================================================= +*/ + + +/* +===================== += += RF_Startup += +===================== +*/ + +static char *ParmStrings[] = {"comp",""}; + +void RF_Startup (void) +{ + int i,x,y; + unsigned *blockstart; + + if (grmode == EGAGR) + for (i = 1;i < _argc;i++) + if (US_CheckParm(_argv[i],ParmStrings) == 0) + { + compatability = true; + break; + } + + for (i=0;i += +===================== +*/ + +void RF_NewMap (void) +{ + int i,x,y; + unsigned spot,*table; + + mapwidth = mapheaderseg[mapon]->width; + mapbyteswide = 2*mapwidth; + mapheight = mapheaderseg[mapon]->height; + mapwordsextra = mapwidth-PORTTILESWIDE; + mapbytesextra = 2*mapwordsextra; + +// +// make a lookup table for the maps left edge +// + if (mapheight > MAXMAPHEIGHT) + Quit ("RF_NewMap: Map too tall!"); + spot = 0; + for (i=0;i=0) // <0 is a tile that is never drawn + { + CA_MarkGrChunk(STARTTILE16+tile); + if (tinf[ANIM+tile]) + { + // this tile will animated + + if (tinf[SPEED+tile]) + { + if (!tinf[ANIM+tile]) + { + strcpy (str,"RF_MarkTileGraphics: Background anim of 0:"); + itoa (tile,str2,10); + strcat (str,str2); + Quit (str); + } + for (i=0;i=MAXANIMTYPES) + Quit ("RF_MarkTileGraphics: Too many unique animated tiles!"); + allanims[i].current = tile; + allanims[i].count = tinf[SPEED+tile]; + *info = (unsigned)&allanims[i]; + numanimchains++; + } + + anims = 0; + change = (signed char)(tinf[ANIM+tile]); + next = tile+change; + while (change && next != tile) + { + CA_MarkGrChunk(STARTTILE16+next); + change = (signed char)(tinf[ANIM+next]); + next += change; + if (++anims > 20) + { + strcpy (str,"RF_MarkTileGraphics: Unending background animation:"); + itoa (next,str2,10); + strcat (str,str2); + Quit (str); + } + } + + } + } +nextback: + info++; + } while (start=0) // <0 is a tile that is never drawn + { + CA_MarkGrChunk(STARTTILE16M+tile); + if (tinf[MANIM+tile]) + { + // this tile will animated + + if (tinf[MSPEED+tile]) + { + if (!tinf[MANIM+tile]) + { + strcpy (str,"RF_MarkTileGraphics: Foreground anim of 0:"); + itoa (tile,str2,10); + strcat (str,str2); + Quit (str); + } + tilehigh = tile | 0x8000; // foreground tiles have high bit + for (i=0;i=MAXANIMTYPES) + Quit ("RF_MarkTileGraphics: Too many unique animated tiles!"); + allanims[i].current = tilehigh; + allanims[i].count = tinf[MSPEED+tile]; + + *info = (unsigned)&allanims[i]; + numanimchains++; + } + + anims = 0; + change = (signed char)(tinf[MANIM+tile]); + next = tile+change; + while (change && next != tile) + { + CA_MarkGrChunk(STARTTILE16M+next); + change = (signed char)(tinf[MANIM+next]); + next += change; + if (++anims > 20) + { + strcpy (str,"RF_MarkTileGraphics: Unending foreground animation:"); + itoa (next,str2,10); + strcat (str,str2); + Quit (str); + } + } + + } + } +nextfront: + info++; + } while (startnexttile; + next = animhead; // stick it at the start of the list + animhead = anim; + if (next) + next->prevptr = &anim->nexttile; + anim->nexttile = next; + anim->prevptr = &animhead; + + anim->x = x; + anim->y = y; + anim->tile = tile; + anim->mapplane = map; + anim->chain = (tiletype *)*(mapsegs[2]+offset); + } + +// +// foreground +// + map = mapsegs[1]+offset; + tile = *map; + if (tinf[MANIM+tile] && tinf[MSPEED+tile]) + { + if (!animfreeptr) + Quit ("RF_CheckForAnimTile: No free spots in tilearray!"); + anim = animfreeptr; + animfreeptr = animfreeptr->nexttile; + next = animhead; // stick it at the start of the list + animhead = anim; + if (next) + next->prevptr = &anim->nexttile; + anim->nexttile = next; + anim->prevptr = &animhead; + + anim->x = x; + anim->y = y; + anim->tile = tile; + anim->mapplane = map; + anim->chain = (tiletype *)*(mapsegs[2]+offset); + } + +} + + +/* +==================== += += RFL_RemoveAnimsOnX += +==================== +*/ + +void RFL_RemoveAnimsOnX (unsigned x) +{ + animtiletype *current,*next; + + current = animhead; + while (current) + { + if (current->x == x) + { + *(void **)current->prevptr = current->nexttile; + if (current->nexttile) + current->nexttile->prevptr = current->prevptr; + next = current->nexttile; + current->nexttile = animfreeptr; + animfreeptr = current; + current = next; + } + else + current = current->nexttile; + } +} + + +/* +==================== += += RFL_RemoveAnimsOnY += +==================== +*/ + +void RFL_RemoveAnimsOnY (unsigned y) +{ + animtiletype *current,*next; + + current = animhead; + while (current) + { + if (current->y == y) + { + *(void **)current->prevptr = current->nexttile; + if (current->nexttile) + current->nexttile->prevptr = current->prevptr; + next = current->nexttile; + current->nexttile = animfreeptr; + animfreeptr = current; + current = next; + } + else + current = current->nexttile; + } +} + + +/* +==================== += += RFL_RemoveAnimsInBlock += +==================== +*/ + +void RFL_RemoveAnimsInBlock (unsigned x, unsigned y, unsigned width, unsigned height) +{ + animtiletype *current,*next; + + current = animhead; + while (current) + { + if (current->x - x < width && current->y - y < height) + { + *(void **)current->prevptr = current->nexttile; + if (current->nexttile) + current->nexttile->prevptr = current->prevptr; + next = current->nexttile; + current->nexttile = animfreeptr; + animfreeptr = current; + current = next; + } + else + current = current->nexttile; + } +} + + +/* +==================== += += RFL_AnimateTiles += +==================== +*/ + +void RFL_AnimateTiles (void) +{ + animtiletype *current; + unsigned updateofs,tile,x,y; + tiletype *anim; + +// +// animate the lists of tiles +// + anim = &allanims[0]; + while (anim->current) + { + anim->count-=tics; + while ( anim->count < 1) + { + if (anim->current & 0x8000) + { + tile = anim->current & 0x7fff; + tile += (signed char)tinf[MANIM+tile]; + anim->count += tinf[MSPEED+tile]; + tile |= 0x8000; + } + else + { + tile = anim->current; + tile += (signed char)tinf[ANIM+tile]; + anim->count += tinf[SPEED+tile]; + } + anim->current = tile; + } + anim++; + } + + +// +// traverse the list of animating tiles +// + current = animhead; + while (current) + { + tile =current->chain->current; + if ( tile != current->tile) + { + // tile has animated + // + // remove tile from master screen cache, + // change a tile to its next state, set the structure up for + // next animation, and post an update region to both update pages + // + current->tile = tile; + + *(current->mapplane) = tile & 0x7fff; // change in map + + x = current->x-originxtile; + y = current->y-originytile; + + if (x>=PORTTILESWIDE || y>=PORTTILESHIGH) + Quit ("RFL_AnimateTiles: Out of bounds!"); + + updateofs = uwidthtable[y] + x; + RFL_NewTile(updateofs); // puts "1"s in both pages + } + current = current->nexttile; + } +} + + +//=========================================================================== + +/* +========================= += += RFL_InitSpriteList += += Call to clear out the entire sprite list and return all of them to += the free list. += +========================= +*/ + +void RFL_InitSpriteList (void) +{ + int i; + + spritefreeptr = &spritearray[0]; + for (i=0;i>G_T_SHIFT; + originytile = originyglobal>>G_T_SHIFT; + originxscreen = originxtile<>G_P_SHIFT) & 15; + pansx = panx & 8; + pany = pansy = (originyglobal>>G_P_SHIFT) & 15; + panadjust = panx/8 + ylookup[pany]; +#endif + +#if GRMODE == CGAGR + panx = (originxglobal>>G_P_SHIFT) & 15; + pansx = panx & 12; + pany = pansy = (originyglobal>>G_P_SHIFT) & 15; + panadjust = pansx/4 + ylookup[pansy]; +#endif + +} + + +/* +================= += += RFL_ClearScrollBlocks += +================= +*/ + +void RFL_ClearScrollBlocks (void) +{ + hscrollblocks = vscrollblocks = 0; +} + + +/* +================= += += RF_SetScrollBlock += += Sets a horizontal or vertical scroll block += a horizontal block is ----, meaning it blocks up/down movement += +================= +*/ + +void RF_SetScrollBlock (int x, int y, boolean horizontal) +{ + if (horizontal) + { + hscrolledge[hscrollblocks] = y; + if (hscrollblocks++ == MAXSCROLLEDGES) + Quit ("RF_SetScrollBlock: Too many horizontal scroll blocks"); + } + else + { + vscrolledge[vscrollblocks] = x; + if (vscrollblocks++ == MAXSCROLLEDGES) + Quit ("RF_SetScrollBlock: Too many vertical scroll blocks"); + } +} + + +/* +================= += += RFL_BoundScroll += += Bound a given x/y movement to scroll blocks += +================= +*/ + +void RFL_BoundScroll (int x, int y) +{ + int check,newxtile,newytile; + + originxglobal += x; + originyglobal += y; + + newxtile= originxglobal >> G_T_SHIFT; + newytile = originyglobal >> G_T_SHIFT; + + if (x>0) + { + newxtile+=SCREENTILESWIDE; + for (check=0;check0) + { + newytile+=SCREENTILESHIGH; + for (check=0;checkoriginxmax) + orgx=originxmax; + + if (orgyoriginymax) + orgy=originymax; + + originxtile = orgx>>G_T_SHIFT; + originytile = orgy>>G_T_SHIFT; + + for (check=0;check=originxtile && edge <=originxtile+10) + { + orgx = (edge+1)*TILEGLOBAL; + break; + } + if (edge>=originxtile+11 && edge <=originxtile+20) + { + orgx = (edge-20)*TILEGLOBAL; + break; + } + } + + for (check=0;check=originytile && edge <=originytile+6) + { + orgy = (edge+1)*TILEGLOBAL; + break; + } + if (edge>=originytile+7 && edge <=originytile+13) + { + orgy = (edge-13)*TILEGLOBAL; + break; + } + } + + + RFL_CalcOriginStuff (orgx,orgy); +} + + +//=========================================================================== + +/* +===================== += += RF_ClearBlock += += Posts erase blocks to clear a certain area of the screen to the master += screen, to erase text or something draw directly to the screen += += Parameters in pixels, but erasure is byte bounded += +===================== +*/ + +void RF_ClearBlock (int x, int y, int width, int height) +{ + eraseblocktype block; + +#if GRMODE == EGAGR + block.screenx = x/8+originxscreen; + block.screeny = y+originyscreen; + block.width = (width+(x&7)+7)/8; + block.height = height; + memcpy (eraselistptr[0]++,&block,sizeof(block)); + memcpy (eraselistptr[1]++,&block,sizeof(block)); +#endif + +#if GRMODE == CGAGR + block.screenx = x/4+originxscreen; + block.screeny = y+originyscreen; + block.width = (width+(x&3)+3)/4; + block.height = height; + memcpy (eraselistptr[0]++,&block,sizeof(block)); +#endif + +} + +//=========================================================================== + +/* +===================== += += RF_RedrawBlock += += Causes a number of tiles to be redrawn to the master screen and updated += += Parameters in pixels, but erasure is tile bounded += +===================== +*/ + +void RF_RedrawBlock (int x, int y, int width, int height) +{ + int xx,yy,xl,xh,yl,yh; + + xl=(x+panx)/16; + xh=(x+panx+width+15)/16; + yl=(y+pany)/16; + yh=(y+pany+height+15)/16; + for (yy=yl;yy<=yh;yy++) + for (xx=xl;xx<=xh;xx++) + RFL_NewTile (yy*UPDATEWIDE+xx); +} + + +//=========================================================================== + +/* +===================== += += RF_CalcTics += +===================== +*/ + +void RF_CalcTics (void) +{ + long newtime,oldtimecount; + +// +// calculate tics since last refresh for adaptive timing +// + if (lasttimecount > TimeCount) + TimeCount = lasttimecount; // if the game was paused a LONG time + + if (DemoMode) // demo recording and playback needs + { // to be constant +// +// take DEMOTICS or more tics, and modify Timecount to reflect time taken +// + oldtimecount = lasttimecount; + while (TimeCountMAXTICS) + { + TimeCount -= (tics-MAXTICS); + tics = MAXTICS; + } + } +} + +/* +============================================================================= + + EGA specific routines + +============================================================================= +*/ + +#if GRMODE == EGAGR + +/* +===================== += += RF_FindFreeBuffer += += Finds the start of unused, non visable buffer space += +===================== +*/ + +unsigned RF_FindFreeBuffer (void) +{ + unsigned spot,i,j; + boolean ok; + + for (i=0;i<3;i++) + { + spot = screenstart[i]+SCREENSPACE; + ok = true; + for (j=0;j<3;j++) + if (spot == screenstart[j]) + { + ok = false; + break; + } + if (ok) + return spot; + } + + return 0; // never get here... +} + +//=========================================================================== + +/* +===================== += += RF_NewPosition EGA += +===================== +*/ + +void RF_NewPosition (unsigned x, unsigned y) +{ + int mx,my; + byte *page0ptr,*page1ptr; + unsigned updatenum; + + RFL_BoundNewOrigin (x,y); +// +// clear out all animating tiles +// + RFL_InitAnimList (); + +// +// set up the new update arrays at base position +// + updatestart[0] = baseupdatestart[0]; + updatestart[1] = baseupdatestart[1]; + updateptr = updatestart[otherpage]; + + page0ptr = updatestart[0]+PORTTILESWIDE; // used to stick "0"s after rows + page1ptr = updatestart[1]+PORTTILESWIDE; + + updatenum = 0; // start at first visable tile + + for (my=0;my1 || absdy>1) + { + // + // scrolled more than one tile, so start from scratch + // + RF_NewPosition(originxglobal,originyglobal); + return; + } + + if (!absdx && !absdy) + return; // the screen has not scrolled an entire tile + + +// +// adjust screens and handle SVGA crippled compatability mode +// + screenmove = deltay*16*SCREENWIDTH + deltax*TILEWIDTH; + for (i=0;i<3;i++) + { + screenstart[i]+= screenmove; + if (compatability && screenstart[i] > (0x10000l-SCREENSPACE) ) + { + // + // move the screen to the opposite end of the buffer + // + screencopy = screenmove>0 ? FREEEGAMEM : -FREEEGAMEM; + oldscreen = screenstart[i] - screenmove; + newscreen = oldscreen + screencopy; + screenstart[i] = newscreen + screenmove; + VW_ScreenToScreen (oldscreen,newscreen, + PORTTILESWIDE*2,PORTTILESHIGH*16); + + if (i==screenpage) + VW_SetScreen(newscreen+oldpanadjust,oldpanx & xpanmask); + } + } + bufferofs = screenstart[otherpage]; + displayofs = screenstart[screenpage]; + masterofs = screenstart[2]; + + +// +// float the update regions +// + move = deltax; + if (deltay==1) + move += UPDATEWIDE; + else if (deltay==-1) + move -= UPDATEWIDE; + + updatestart[0]+=move; + updatestart[1]+=move; + +// +// draw the new tiles just scrolled on to the master screen, and +// mark them as needing to be copied to each screen next refreshes +// Make sure a zero is at the end of each row in update +// + + if (deltax) + { + if (deltax==1) + { + RFL_NewRow (1); // new right row + RFL_RemoveAnimsOnX (originxtile-1); + } + else + { + RFL_NewRow (3); // new left row + RFL_RemoveAnimsOnX (originxtile+PORTTILESWIDE); + } + + update0 = updatestart[0]+PORTTILESWIDE; + update1 = updatestart[1]+PORTTILESWIDE; + for (yy=0;yyupdatecount<2) + { + if (!sprite->updatecount) + memcpy (eraselistptr[otherpage]++,sprite,sizeof(eraseblocktype)); + memcpy (eraselistptr[screenpage]++,sprite,sizeof(eraseblocktype)); + } + + if (priority != sprite->priority) + { + // sprite mvoed to another priority, so unlink the old one and + // relink it in the new priority + + next = sprite->nextsprite; // cut old links + if (next) + next->prevptr = sprite->prevptr; + *sprite->prevptr = next; + goto linknewspot; + } + } + else + { + // this is a brand new sprite, so allocate a block from the array + + if (!spritefreeptr) + Quit ("RF_PlaceSprite: No free spots in spritearray!"); + + sprite = spritefreeptr; + spritefreeptr = spritefreeptr->nextsprite; + +linknewspot: + next = prioritystart[priority]; // stick it in new spot + if (next) + next->prevptr = &sprite->nextsprite; + sprite->nextsprite = next; + prioritystart[priority] = sprite; + sprite->prevptr = &prioritystart[priority]; + } + +// +// write the new info to the sprite +// + spr = &spritetable[spritenumber-STARTSPRITES]; + block = (spritetype _seg *)grsegs[spritenumber]; + + if (!block) + { + strcpy (str,"RF_PlaceSprite: Placed an uncached sprite:"); + itoa (spritenumber,str2,10); + strcat (str,str2); + Quit (str); + } + + globaly+=spr->orgy; + globalx+=spr->orgx; + + pixx = globalx >> G_SY_SHIFT; + shift = (pixx&7)/2; + + sprite->screenx = pixx >> (G_EGASX_SHIFT-G_SY_SHIFT); + sprite->screeny = globaly >> G_SY_SHIFT; + sprite->width = block->width[shift]; + sprite->height = spr->height; + sprite->grseg = spritenumber; + sprite->sourceofs = block->sourceoffset[shift]; + sprite->planesize = block->planesize[shift]; + sprite->draw = draw; + sprite->priority = priority; + sprite->tilex = sprite->screenx >> SX_T_SHIFT; + sprite->tiley = sprite->screeny >> SY_T_SHIFT; + sprite->tilewide = ( (sprite->screenx + sprite->width -1) >> SX_T_SHIFT ) + - sprite->tilex + 1; + sprite->tilehigh = ( (sprite->screeny + sprite->height -1) >> SY_T_SHIFT ) + - sprite->tiley + 1; + + sprite->updatecount = 2; // draw on next two refreshes + +// save the sprite pointer off in the user's pointer so it can be moved +// again later + + *user = sprite; +} + +//=========================================================================== + +/* +===================== += += RF_RemoveSprite EGA += +===================== +*/ + +void RF_RemoveSprite (void **user) +{ + spritelisttype *sprite,*next; + + sprite = (spritelisttype *)*user; + if (!sprite) + return; + +// +// post an erase block to both pages by copying screenx,screeny,width,height +// both pages may not need to be erased if the sprite just changed last frame +// + if (sprite->updatecount<2) + { + if (!sprite->updatecount) + memcpy (eraselistptr[otherpage]++,sprite,sizeof(eraseblocktype)); + memcpy (eraselistptr[screenpage]++,sprite,sizeof(eraseblocktype)); + } + +// +// unlink the sprite node +// + next = sprite->nextsprite; + if (next) // if (!next), sprite is last in chain + next->prevptr = sprite->prevptr; + *sprite->prevptr = next; + +// +// add it back to the free list +// + sprite->nextsprite = spritefreeptr; + spritefreeptr = sprite; + +// +// null the users pointer, so next time that actor gets placed, it will +// allocate a new block +// + + *user = 0; +} + + +//=========================================================================== + + +/* +==================== += += RFL_EraseBlocks EGA += += Write mode 1 should be set += +==================== +*/ + +void RFL_EraseBlocks (void) +{ + eraseblocktype *block,*done; + int screenxh,screenyh; + unsigned pos,xtl,ytl,xth,yth,x,y; + byte *updatespot; + unsigned updatedelta; + unsigned erasecount; + +#ifdef PROFILE + erasecount = 0; +#endif + + block = otherpage ? &eraselist[1][0] : &eraselist[0][0]; + + done = eraselistptr[otherpage]; + + while (block != done) + { + + // + // clip the block to the current screen view + // + block->screenx -= originxscreen; + block->screeny -= originyscreen; + + if (block->screenx < 0) + { + block->width += block->screenx; + if (block->width<1) + goto next; + block->screenx = 0; + } + + if (block->screeny < 0) + { + block->height += block->screeny; + if (block->height<1) + goto next; + block->screeny = 0; + } + + screenxh = block->screenx + block->width; + screenyh = block->screeny + block->height; + + if (screenxh > EGAPORTSCREENWIDE) + { + block->width = EGAPORTSCREENWIDE-block->screenx; + screenxh = block->screenx + block->width; + } + + if (screenyh > PORTSCREENHIGH) + { + block->height = PORTSCREENHIGH-block->screeny; + screenyh = block->screeny + block->height; + } + + if (block->width<1 || block->height<1) + goto next; + + // + // erase the block by copying from the master screen + // + pos = ylookup[block->screeny]+block->screenx; + VW_ScreenToScreen (masterofs+pos,bufferofs+pos, + block->width,block->height); + + // + // put 2s in update where the block was, to force sprites to update + // + xtl = block->screenx >> SX_T_SHIFT; + xth = (block->screenx+block->width-1) >> SX_T_SHIFT; + ytl = block->screeny >> SY_T_SHIFT; + yth = (block->screeny+block->height-1) >> SY_T_SHIFT; + + updatespot = updateptr + uwidthtable[ytl] + xtl; + updatedelta = UPDATEWIDE - (xth-xtl+1); + + for (y=ytl;y<=yth;y++) + { + for (x=xtl;x<=xth;x++) + *updatespot++ = 2; + updatespot += updatedelta; // down to next line + } +#ifdef PROFILE + erasecount++; +#endif + +next: + block++; + } + eraselistptr[otherpage] = otherpage ? &eraselist[1][0] : &eraselist[0][0]; +#ifdef PROFILE + strcpy (scratch,"\tErase:"); + itoa (erasecount,str,10); + strcat (scratch,str); + write (profilehandle,scratch,strlen(scratch)); +#endif + +} + + +/* +==================== += += RFL_UpdateSprites EGA += += NOTE: Implement vertical clipping! += +==================== +*/ + +void RFL_UpdateSprites (void) +{ + spritelisttype *sprite; + int portx,porty,x,y,xtl,xth,ytl,yth; + int priority; + unsigned dest; + byte *updatespot,*baseupdatespot; + unsigned updatedelta; + unsigned updatecount; + unsigned height,sourceofs; + +#ifdef PROFILE + updatecount = 0; +#endif + + for (priority=0;prioritynextsprite) + { + // + // see if the sprite has any visable area in the port + // + + portx = sprite->screenx - originxscreen; + porty = sprite->screeny - originyscreen; + xtl = portx >> SX_T_SHIFT; + xth = (portx + sprite->width-1) >> SX_T_SHIFT; + ytl = porty >> SY_T_SHIFT; + yth = (porty + sprite->height-1) >> SY_T_SHIFT; + + if (xtl<0) + xtl = 0; + if (xth>=PORTTILESWIDE) + xth = PORTTILESWIDE-1; + if (ytl<0) + ytl = 0; + if (yth>=PORTTILESHIGH) + yth = PORTTILESHIGH-1; + + if (xtl>xth || ytl>yth) + continue; + + // + // see if it's visable area covers any non 0 update tiles + // + updatespot = baseupdatespot = updateptr + uwidthtable[ytl] + xtl; + updatedelta = UPDATEWIDE - (xth-xtl+1); + + if (sprite->updatecount) + { + sprite->updatecount--; // the sprite was just placed, + goto redraw; // so draw it for sure + } + + for (y=ytl;y<=yth;y++) + { + for (x=xtl;x<=xth;x++) + if (*updatespot++) + goto redraw; + updatespot += updatedelta; // down to next line + } + continue; // no need to update + +redraw: + // + // set the tiles it covers to 3, because those tiles are being + // updated + // + updatespot = baseupdatespot; + for (y=ytl;y<=yth;y++) + { + for (x=xtl;x<=xth;x++) + *updatespot++ = 3; + updatespot += updatedelta; // down to next line + } + // + // draw it! + // + height = sprite->height; + sourceofs = sprite->sourceofs; + if (porty<0) + { + height += porty; // clip top off + sourceofs -= porty*sprite->width; + porty = 0; + } + else if (porty+height>PORTSCREENHIGH) + { + height = PORTSCREENHIGH - porty; // clip bottom off + } + + dest = bufferofs + ylookup[porty] + portx; + + switch (sprite->draw) + { + case spritedraw: + VW_MaskBlock(grsegs[sprite->grseg], sourceofs, + dest,sprite->width,height,sprite->planesize); + break; + + case maskdraw: + break; + + } +#ifdef PROFILE + updatecount++; +#endif + + + } + } +#ifdef PROFILE + strcpy (scratch,"\tSprites:"); + itoa (updatecount,str,10); + strcat (scratch,str); + write (profilehandle,scratch,strlen(scratch)); +#endif + +} + + +/* +===================== += += RF_Refresh EGA += += All routines will draw at the port at bufferofs, possibly copying from += the port at masterofs. The EGA version then page flips, while the += CGA version updates the screen from the buffer port. += += Screenpage is the currently displayed page, not the one being drawn += Otherpage is the page to be worked with now += +===================== +*/ + +void RF_Refresh (void) +{ + byte *newupdate; + + updateptr = updatestart[otherpage]; + + RFL_AnimateTiles (); // DEBUG + +// +// update newly scrolled on tiles and animated tiles from the master screen +// + EGAWRITEMODE(1); + EGAMAPMASK(15); + RFL_UpdateTiles (); + RFL_EraseBlocks (); + +// +// Update is all 0 except where sprites have changed or new area has +// been scrolled on. Go through all sprites and update the ones that cover +// a non 0 update tile +// + EGAWRITEMODE(0); + RFL_UpdateSprites (); + +// +// if the main program has a refresh hook set, call their function before +// displaying the new page +// + if (refreshvector) + refreshvector(); + +// +// display the changed screen +// + VW_SetScreen(bufferofs+panadjust,panx & xpanmask); + +// +// prepare for next refresh +// +// Set the update array to the middle position and clear it out to all "0"s +// with an UPDATETERMINATE at the end +// + updatestart[otherpage] = newupdate = baseupdatestart[otherpage]; +asm mov ax,ds +asm mov es,ax +asm xor ax,ax +asm mov cx,(UPDATESCREENSIZE-2)/2 +asm mov di,[newupdate] +asm rep stosw +asm mov [WORD PTR es:di],UPDATETERMINATE + + screenpage ^= 1; + otherpage ^= 1; + bufferofs = screenstart[otherpage]; + displayofs = screenstart[screenpage]; + +// +// calculate tics since last refresh for adaptive timing +// + RF_CalcTics (); +} + +#endif // GRMODE == EGAGR + +/* +============================================================================= + + CGA specific routines + +============================================================================= +*/ + +#if GRMODE == CGAGR + + +/* +===================== += += RF_NewPosition CGA += +===================== +*/ + +void RF_NewPosition (unsigned x, unsigned y) +{ + int mx,my; + byte *spotptr; + unsigned updatenum; + + RFL_BoundNewOrigin (x,y); + +// +// clear out all animating tiles +// + RFL_InitAnimList (); + +// +// set up the new update arrays at base position +// + updateptr = baseupdateptr; + + spotptr = updateptr + PORTTILESWIDE; // used to stick "0"s after rows + + updatenum = 0; // start at first visable tile + + for (my=0;my1 || absdy>1) + { + // + // scrolled more than one tile, so start from scratch + // + RF_NewPosition(originxglobal,originyglobal); + return; + } + + if (!absdx && !absdy) + return; // the screen has not scrolled an entire tile + + +// +// float screens +// + screenmove = deltay*16*SCREENWIDTH + deltax*TILEWIDTH; + bufferofs += screenmove; + masterofs += screenmove; + + +// +// float the update regions +// + move = deltax; + if (deltay==1) + move += UPDATEWIDE; + else if (deltay==-1) + move -= UPDATEWIDE; + + updateptr+=move; + +// +// draw the new tiles just scrolled on to the master screen, and +// mark them as needing to be copied to each screen next refreshes +// Make sure a zero is at the end of each row in update +// + + if (deltax) + { + if (deltax==1) + { + RFL_NewRow (1); // new right row + RFL_RemoveAnimsOnX (originxtile-1); + } + else + { + RFL_NewRow (3); // new left row + RFL_RemoveAnimsOnX (originxtile+PORTTILESWIDE); + } + + spotptr = updateptr+PORTTILESWIDE; + for (yy=0;yyupdatecount) // may not have been drawn at all yet + memcpy (eraselistptr[0]++,sprite,sizeof(eraseblocktype)); + + if (priority != sprite->priority) + { + // sprite moved to another priority, so unlink the old one and + // relink it in the new priority + + next = sprite->nextsprite; // cut old links + if (next) + next->prevptr = sprite->prevptr; + *sprite->prevptr = next; + goto linknewspot; + } + } + else + { + // this is a brand new sprite, so allocate a block from the array + + if (!spritefreeptr) + Quit ("RF_PlaceSprite: No free spots in spritearray!"); + + sprite = spritefreeptr; + spritefreeptr = spritefreeptr->nextsprite; + +linknewspot: + next = prioritystart[priority]; // stick it in new spot + if (next) + next->prevptr = &sprite->nextsprite; + sprite->nextsprite = next; + prioritystart[priority] = sprite; + sprite->prevptr = &prioritystart[priority]; + } + +// +// write the new info to the sprite +// + spr = &spritetable[spritenumber-STARTSPRITES]; + block = (spritetype _seg *)grsegs[spritenumber]; + + if (!block) + { + strcpy (str,"RF_PlaceSprite: Placed an uncached sprite!"); + itoa (spritenumber,str2,10); + strcat (str,str2); + Quit (str); + } + + + globaly+=spr->orgy; + globalx+=spr->orgx; + + sprite->screenx = globalx >> G_CGASX_SHIFT; + sprite->screeny = globaly >> G_SY_SHIFT; + sprite->width = block->width[0]; + sprite->height = spr->height; + sprite->grseg = spritenumber; + sprite->sourceofs = block->sourceoffset[0]; + sprite->planesize = block->planesize[0]; + sprite->draw = draw; + sprite->priority = priority; + sprite->tilex = sprite->screenx >> SX_T_SHIFT; + sprite->tiley = sprite->screeny >> SY_T_SHIFT; + sprite->tilewide = ( (sprite->screenx + sprite->width -1) >> SX_T_SHIFT ) + - sprite->tilex + 1; + sprite->tilehigh = ( (sprite->screeny + sprite->height -1) >> SY_T_SHIFT ) + - sprite->tiley + 1; + + sprite->updatecount = 1; // draw on next refresh + +// save the sprite pointer off in the user's pointer so it can be moved +// again later + + *user = sprite; +} + +//=========================================================================== + +/* +===================== += += RF_RemoveSprite CGA += +===================== +*/ + +void RF_RemoveSprite (void **user) +{ + spritelisttype *sprite,*next; + + sprite = (spritelisttype *)*user; + if (!sprite) + return; + +// +// post an erase block to erase the old position by copying +// screenx,screeny,width,height +// + if (!sprite->updatecount) + { + memcpy (eraselistptr[0]++,sprite,sizeof(eraseblocktype)); + } + +// +// unlink the sprite node +// + next = sprite->nextsprite; + if (next) // if (!next), sprite is last in chain + next->prevptr = sprite->prevptr; + *sprite->prevptr = next; + +// +// add it back to the free list +// + sprite->nextsprite = spritefreeptr; + spritefreeptr = sprite; + +// +// null the users pointer, so next time that actor gets placed, it will +// allocate a new block +// + + *user = 0; +} + + +/* +==================== += += RFL_EraseBlocks CGA += += Write mode 1 should be set += +==================== +*/ + +void RFL_EraseBlocks (void) +{ + eraseblocktype *block,*done; + int screenxh,screenyh; + unsigned pos,xtl,ytl,xth,yth,x,y; + byte *updatespot; + unsigned updatedelta; + + block = &eraselist[0][0]; + + done = eraselistptr[0]; + + while (block != done) + { + + // + // clip the block to the current screen view + // + block->screenx -= originxscreen; + block->screeny -= originyscreen; + + if (block->screenx < 0) + { + block->width += block->screenx; + if (block->width<1) + goto next; + block->screenx = 0; + } + + if (block->screeny < 0) + { + block->height += block->screeny; + if (block->height<1) + goto next; + block->screeny = 0; + } + + screenxh = block->screenx + block->width; + screenyh = block->screeny + block->height; + + if (screenxh > CGAPORTSCREENWIDE) + { + block->width = CGAPORTSCREENWIDE-block->screenx; + screenxh = block->screenx + block->width; + } + + if (screenyh > PORTSCREENHIGH) + { + block->height = PORTSCREENHIGH-block->screeny; + screenyh = block->screeny + block->height; + } + + if (block->width<1 || block->height<1) + goto next; + + // + // erase the block by copying from the master screen + // + pos = ylookup[block->screeny]+block->screenx; + block->width = (block->width + (pos&1) + 1)& ~1; + pos &= ~1; // make sure a word copy gets used + VW_ScreenToScreen (masterofs+pos,bufferofs+pos, + block->width,block->height); + + // + // put 2s in update where the block was, to force sprites to update + // + xtl = block->screenx >> SX_T_SHIFT; + xth = (block->screenx+block->width-1) >> SX_T_SHIFT; + ytl = block->screeny >> SY_T_SHIFT; + yth = (block->screeny+block->height-1) >> SY_T_SHIFT; + + updatespot = updateptr + uwidthtable[ytl] + xtl; + updatedelta = UPDATEWIDE - (xth-xtl+1); + + for (y=ytl;y<=yth;y++) + { + for (x=xtl;x<=xth;x++) + *updatespot++ = 2; + updatespot += updatedelta; // down to next line + } + +next: + block++; + } + eraselistptr[0] = &eraselist[0][0]; +} + + +/* +==================== += += RFL_UpdateSprites CGA += += NOTE: Implement vertical clipping! += +==================== +*/ + +void RFL_UpdateSprites (void) +{ + spritelisttype *sprite; + int portx,porty,x,y,xtl,xth,ytl,yth; + int priority; + unsigned dest; + byte *updatespot,*baseupdatespot; + unsigned updatedelta; + + unsigned updatecount; + unsigned height,sourceofs; + +#ifdef PROFILE + updatecount = 0; +#endif + + + for (priority=0;prioritynextsprite) + { + // + // see if the sprite has any visable area in the port + // + + portx = sprite->screenx - originxscreen; + porty = sprite->screeny - originyscreen; + xtl = portx >> SX_T_SHIFT; + xth = (portx + sprite->width-1) >> SX_T_SHIFT; + ytl = porty >> SY_T_SHIFT; + yth = (porty + sprite->height-1) >> SY_T_SHIFT; + + if (xtl<0) + xtl = 0; + if (xth>=PORTTILESWIDE) + xth = PORTTILESWIDE-1; + if (ytl<0) + ytl = 0; + if (yth>=PORTTILESHIGH) + yth = PORTTILESHIGH-1; + + if (xtl>xth || ytl>yth) + continue; + + // + // see if it's visable area covers any non 0 update tiles + // + updatespot = baseupdatespot = updateptr + uwidthtable[ytl] + xtl; + updatedelta = UPDATEWIDE - (xth-xtl+1); + + if (sprite->updatecount) + { + sprite->updatecount--; // the sprite was just placed, + goto redraw; // so draw it for sure + } + + for (y=ytl;y<=yth;y++) + { + for (x=xtl;x<=xth;x++) + if (*updatespot++) + goto redraw; + updatespot += updatedelta; // down to next line + } + continue; // no need to update + +redraw: + // + // set the tiles it covers to 3, because those tiles are being + // updated + // + updatespot = baseupdatespot; + for (y=ytl;y<=yth;y++) + { + for (x=xtl;x<=xth;x++) + *updatespot++ = 3; + updatespot += updatedelta; // down to next line + } + // + // draw it! + // + height = sprite->height; + sourceofs = sprite->sourceofs; + if (porty<0) + { + height += porty; // clip top off + sourceofs -= porty*sprite->width; + porty = 0; + } + else if (porty+height>PORTSCREENHIGH) + { + height = PORTSCREENHIGH - porty; // clip bottom off + } + + dest = bufferofs + ylookup[porty] + portx; + + switch (sprite->draw) + { + case spritedraw: + VW_MaskBlock(grsegs[sprite->grseg], sourceofs, + dest,sprite->width,height,sprite->planesize); + break; + + case maskdraw: + break; + + } +#ifdef PROFILE + updatecount++; +#endif + + + } + } +} + + +/* +===================== += += RF_Refresh CGA += += All routines will draw at the port at bufferofs, possibly copying from += the port at masterofs. The EGA version then page flips, while the += CGA version updates the screen from the buffer port. += += Screenpage is the currently displayed page, not the one being drawn += Otherpage is the page to be worked with now += +===================== +*/ + +void RF_Refresh (void) +{ + long newtime,oldtimecount; + + RFL_AnimateTiles (); + +// +// update newly scrolled on tiles and animated tiles from the master screen +// + RFL_UpdateTiles (); + RFL_EraseBlocks (); + +// +// Update is all 0 except where sprites have changed or new area has +// been scrolled on. Go through all sprites and update the ones that cover +// a non 0 update tile +// + RFL_UpdateSprites (); + +// +// if the main program has a refresh hook set, call their function before +// displaying the new page +// + if (refreshvector) + refreshvector(); + +// +// update everything to the screen +// + VW_CGAFullUpdate (); + +// +// calculate tics since last refresh for adaptive timing +// + RFL_CalcTics (); +} + +#endif // GRMODE == CGAGR diff --git a/16/cawat/ID_RF.H b/16/cawat/ID_RF.H new file mode 100644 index 00000000..8a3b0e23 --- /dev/null +++ b/16/cawat/ID_RF.H @@ -0,0 +1,153 @@ +/* Catacomb Armageddon Source Code + * Copyright (C) 1993-2014 Flat Rock Software + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +// ID_RF.H + +#define __ID_RF__ + +#ifndef __ID_MM__ +#include "ID_MM.H" +#endif + +/* +============================================================================= + + CONSTANTS + +============================================================================= +*/ + +#define MINTICS 2 +#define MAXTICS 6 +#define DEMOTICS 3 + +#define MAPBORDER 2 // map border must be at least 1 + +#define MAXSPRITES 50 // max tracked sprites +#define MAXANIMTILES 90 // max animating tiles on screen +#define MAXANIMTYPES 50 // max different unique anim tiles on map + +#define MAXMAPHEIGHT 200 + +#define PRIORITIES 4 +#define MASKEDTILEPRIORITY 3 // planes go: 0,1,2,MTILES,3 + +#define TILEGLOBAL 256 +#define PIXGLOBAL 16 + +#define G_T_SHIFT 8 // global >> ?? = tile +#define G_P_SHIFT 4 // global >> ?? = pixels +#define P_T_SHIFT 4 // pixels >> ?? = tile + +#define PORTTILESWIDE 21 // all drawing takes place inside a +#define PORTTILESHIGH 14 // non displayed port of this size + +//#define PORTGLOBALWIDE (21*TILEGLOBAL) +//#define PORTGLOBALHIGH (14*TILEGLOBAL) + +#define UPDATEWIDE (PORTTILESWIDE+1) +#define UPDATEHIGH PORTTILESHIGH + + +//=========================================================================== + +typedef enum {spritedraw,maskdraw} drawtype; + +/* +============================================================================= + + PUBLIC VARIABLES + +============================================================================= +*/ + + +extern boolean compatability; // crippled refresh for wierdo SVGAs + +extern unsigned tics; +extern long lasttimecount; + +extern unsigned originxglobal,originyglobal; +extern unsigned originxtile,originytile; +extern unsigned originxscreen,originyscreen; + +extern unsigned mapwidth,mapheight,mapbyteswide,mapwordswide + ,mapbytesextra,mapwordsextra; +extern unsigned mapbwidthtable[MAXMAPHEIGHT]; + +extern unsigned originxmin,originxmax,originymin,originymax; + +extern unsigned masterofs; + +// +// the floating update window is also used by the view manager for +// double buffer tracking +// + +extern byte *updateptr; // current start of update window + +#if GRMODE == CGAGR +extern byte *baseupdateptr; +#endif + +extern unsigned blockstarts[UPDATEWIDE*UPDATEHIGH]; +extern unsigned updatemapofs[UPDATEWIDE*UPDATEHIGH]; +extern unsigned uwidthtable[UPDATEHIGH]; // lookup instead of multiple + +#define UPDATETERMINATE 0x0301 + +/* +============================================================================= + + PUBLIC FUNCTIONS + +============================================================================= +*/ + +void RF_Startup (void); +void RF_Shutdown (void); + +void RF_FixOfs (void); +void RF_NewMap (void); +void RF_MarkTileGraphics (void); +void RF_SetScrollBlock (int x, int y, boolean horizontal); +void RF_NewPosition (unsigned x, unsigned y); +void RF_Scroll (int x, int y); + +void RF_MapToMap (unsigned srcx, unsigned srcy, + unsigned destx, unsigned desty, + unsigned width, unsigned height); +void RF_MemToMap (unsigned far *source, unsigned plane, + unsigned destx, unsigned desty, + unsigned width, unsigned height); + +void RF_ClearBlock (int x, int y, int width, int height); +void RF_RedrawBlock (int x, int y, int width, int height); + +void RF_PlaceSprite (void **user,unsigned globalx,unsigned globaly, + unsigned spritenumber, drawtype draw, int priority); +void RF_RemoveSprite (void **user); + +void RF_CalcTics (void); + +void RF_Refresh (void); +void RF_ForceRefresh (void); +void RF_SetRefreshHook (void (*func) (void) ); + +unsigned RF_FindFreeBuffer (void); + diff --git a/16/cawat/ID_RF_A.ASM b/16/cawat/ID_RF_A.ASM new file mode 100644 index 00000000..77af0e93 --- /dev/null +++ b/16/cawat/ID_RF_A.ASM @@ -0,0 +1,690 @@ +; Catacomb Armageddon Source Code +; Copyright (C) 1993-2014 Flat Rock Software +; +; This program is free software; you can redistribute it and/or modify +; it under the terms of the GNU General Public License as published by +; the Free Software Foundation; either version 2 of the License, or +; (at your option) any later version. +; +; This program is distributed in the hope that it will be useful, +; but WITHOUT ANY WARRANTY; without even the implied warranty of +; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +; GNU General Public License for more details. +; +; You should have received a copy of the GNU General Public License along +; with this program; if not, write to the Free Software Foundation, Inc., +; 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +; ID_RF_A.ASM + +IDEAL +MODEL MEDIUM,C + +INCLUDE "ID_ASM.EQU" + +;============================================================================ + +TILESWIDE = 21 +TILESHIGH = 14 + +UPDATESIZE = (TILESWIDE+1)*TILESHIGH+1 + +DATASEG + +EXTRN screenseg:WORD +EXTRN updateptr:WORD +EXTRN updatestart:WORD +EXTRN masterofs:WORD ;start of master tile port +EXTRN bufferofs:WORD ;start of current buffer port +EXTRN screenstart:WORD ;starts of three screens (0/1/master) in EGA mem +EXTRN grsegs:WORD +EXTRN mapsegs:WORD +EXTRN originmap:WORD +EXTRN updatemapofs:WORD +EXTRN tinf:WORD ;seg pointer to map header and tile info +EXTRN blockstarts:WORD ;offsets from bufferofs for each update block + +planemask db ? +planenum db ? + +CODESEG + +screenstartcs dw ? ;in code segment for accesability + + + + +IFE GRMODE-CGAGR +;============================================================================ +; +; CGA refresh routines +; +;============================================================================ + +TILEWIDTH = 4 + +;================= +; +; RFL_NewTile +; +; Draws a composit two plane tile to the master screen and sets the update +; spot to 1 in both update pages, forcing the tile to be copied to the +; view pages the next two refreshes +; +; Called to draw newlly scrolled on strips and animating tiles +; +;================= + +PROC RFL_NewTile updateoffset:WORD +PUBLIC RFL_NewTile +USES SI,DI + +; +; mark both update lists at this spot +; + mov di,[updateoffset] + + mov bx,[updateptr] ;start of update matrix + mov [BYTE bx+di],1 + + mov dx,SCREENWIDTH-TILEWIDTH ;add to get to start of next line + +; +; set di to the location in screenseg to draw the tile +; + shl di,1 + mov si,[updatemapofs+di] ;offset in map from origin + add si,[originmap] + mov di,[blockstarts+di] ;screen location for tile + add di,[masterofs] + +; +; set BX to the foreground tile number and SI to the background number +; If either BX or SI = 0xFFFF, the tile does not need to be masked together +; as one of the planes totally eclipses the other +; + mov es,[mapsegs+2] ;foreground plane + mov bx,[es:si] + mov es,[mapsegs] ;background plane + mov si,[es:si] + + mov es,[screenseg] + + or bx,bx + jz @@singletile + jmp @@maskeddraw ;draw both together + +;============= +; +; Draw single background tile from main memory +; +;============= + +@@singletile: + shl si,1 + mov ds,[grsegs+STARTTILE16*2+si] + + xor si,si ;block is segment aligned + +REPT 15 + movsw + movsw + add di,dx +ENDM + movsw + movsw + + mov ax,ss + mov ds,ax ;restore turbo's data segment + ret + + +;========= +; +; Draw a masked tile combo +; Interupts are disabled and the stack segment is reassigned +; +;========= +@@maskeddraw: + cli ; don't allow ints when SS is set + shl bx,1 + mov ss,[grsegs+STARTTILE16M*2+bx] + shl si,1 + mov ds,[grsegs+STARTTILE16*2+si] + + xor si,si ;first word of tile data + +REPT 16 + mov ax,[si] ;background tile + and ax,[ss:si] ;mask + or ax,[ss:si+64] ;masked data + stosw + mov ax,[si+2] ;background tile + and ax,[ss:si+2] ;mask + or ax,[ss:si+66] ;masked data + stosw + add si,4 + add di,dx +ENDM + + mov ax,@DATA + mov ss,ax + sti + mov ds,ax + ret +ENDP + +ENDIF + + + +IFE GRMODE-EGAGR +;=========================================================================== +; +; EGA refresh routines +; +;=========================================================================== + +TILEWIDTH = 2 + +;================= +; +; RFL_NewTile +; +; Draws a composit two plane tile to the master screen and sets the update +; spot to 1 in both update pages, forcing the tile to be copied to the +; view pages the next two refreshes +; +; Called to draw newlly scrolled on strips and animating tiles +; +; Assumes write mode 0 +; +;================= + +PROC RFL_NewTile updateoffset:WORD +PUBLIC RFL_NewTile +USES SI,DI + +; +; mark both update lists at this spot +; + mov di,[updateoffset] + + mov bx,[updatestart] ;page 0 pointer + mov [BYTE bx+di],1 + mov bx,[updatestart+2] ;page 1 pointer + mov [BYTE bx+di],1 + +; +; set screenstartcs to the location in screenseg to draw the tile +; + shl di,1 + mov si,[updatemapofs+di] ;offset in map from origin + add si,[originmap] + mov di,[blockstarts+di] ;screen location for tile + add di,[masterofs] + mov [cs:screenstartcs],di + +; +; set BX to the foreground tile number and SI to the background number +; If either BX or SI = 0xFFFF, the tile does not need to be masked together +; as one of the planes totally eclipses the other +; + mov es,[mapsegs+2] ;foreground plane + mov bx,[es:si] + mov es,[mapsegs] ;background plane + mov si,[es:si] + + mov es,[screenseg] + mov dx,SC_INDEX ;for stepping through map mask planes + + or bx,bx + jz @@singletile + jmp @@maskeddraw ;draw both together + +;========= +; +; No foreground tile, so draw a single background tile. +; +;========= +@@singletile: + + mov bx,SCREENWIDTH-2 ;add to get to start of next line + shl si,1 + + mov ax,[cs:screenstartcs] + mov ds,[grsegs+STARTTILE16*2+si] + + xor si,si ;block is segment aligned + + mov ax,SC_MAPMASK+0001b*256 ;map mask for plane 0 + + mov cx,4 ;draw four planes +@@planeloop: + mov dx,SC_INDEX + WORDOUT + + mov di,[cs:screenstartcs] ;start at same place in all planes + +REPT 15 + movsw + add di,bx +ENDM + movsw + + shl ah,1 ;shift plane mask over for next plane + loop @@planeloop + + mov ax,ss + mov ds,ax ;restore turbo's data segment + ret + + +;========= +; +; Draw a masked tile combo +; Interupts are disabled and the stack segment is reassigned +; +;========= +@@maskeddraw: + cli ; don't allow ints when SS is set + shl bx,1 + mov ss,[grsegs+STARTTILE16M*2+bx] + shl si,1 + mov ds,[grsegs+STARTTILE16*2+si] + + xor si,si ;first word of tile data + + mov ax,SC_MAPMASK+0001b*256 ;map mask for plane 0 + + mov di,[cs:screenstartcs] +@@planeloopm: + WORDOUT +tileofs = 0 +lineoffset = 0 +REPT 16 + mov bx,[si+tileofs] ;background tile + and bx,[ss:tileofs] ;mask + or bx,[ss:si+tileofs+32] ;masked data + mov [es:di+lineoffset],bx +tileofs = tileofs + 2 +lineoffset = lineoffset + SCREENWIDTH +ENDM + add si,32 + shl ah,1 ;shift plane mask over for next plane + cmp ah,10000b + je @@done ;drawn all four planes + jmp @@planeloopm + +@@done: + mov ax,@DATA + mov ss,ax + sti + mov ds,ax + ret +ENDP + +ENDIF + +IFE GRMODE-VGAGR +;============================================================================ +; +; VGA refresh routines +; +;============================================================================ + + +ENDIF + + +;============================================================================ +; +; reasonably common refresh routines +; +;============================================================================ + + +;================= +; +; RFL_UpdateTiles +; +; Scans through the update matrix pointed to by updateptr, looking for 1s. +; A 1 represents a tile that needs to be copied from the master screen to the +; current screen (a new row or an animated tiled). If more than one adjacent +; tile in a horizontal row needs to be copied, they will be copied as a group. +; +; Assumes write mode 1 +; +;================= + + +; AX 0/1 for scasb, temp for segment register transfers +; BX width for block copies +; CX REP counter +; DX line width deltas +; SI source for copies +; DI scas dest / movsb dest +; BP pointer to UPDATETERMINATE +; +; DS +; ES +; SS + +PROC RFL_UpdateTiles +PUBLIC RFL_UpdateTiles +USES SI,DI,BP + + jmp SHORT @@realstart +@@done: +; +; all tiles have been scanned +; + ret + +@@realstart: + mov di,[updateptr] + mov bp,(TILESWIDE+1)*TILESHIGH+1 + add bp,di ; when di = bx, all tiles have been scanned + push di + mov cx,-1 ; definately scan the entire thing + +; +; scan for a 1 in the update list, meaning a tile needs to be copied +; from the master screen to the current screen +; +@@findtile: + pop di ; place to continue scaning from + mov ax,ss + mov es,ax ; search in the data segment + mov ds,ax + mov al,1 + repne scasb + cmp di,bp + je @@done + + cmp [BYTE di],al + jne @@singletile + jmp @@tileblock + +;============ +; +; copy a single tile +; +;============ +EVEN +@@singletile: + inc di ; we know the next tile is nothing + push di ; save off the spot being scanned + sub di,[updateptr] + shl di,1 + mov di,[blockstarts-4+di] ; start of tile location on screen + mov si,di + add di,[bufferofs] ; dest in current screen + add si,[masterofs] ; source in master screen + + mov dx,SCREENWIDTH-TILEWIDTH + mov ax,[screenseg] + mov ds,ax + mov es,ax + +;-------------------------- + +IFE GRMODE-CGAGR + +REPT 15 + movsw + movsw + add si,dx + add di,dx +ENDM + movsw + movsw + +ENDIF + +;-------------------------- + +IFE GRMODE-EGAGR + +REPT 15 + movsb + movsb + add si,dx + add di,dx +ENDM + movsb + movsb + +ENDIF + +;-------------------------- + + jmp @@findtile + +;============ +; +; more than one tile in a row needs to be updated, so do it as a group +; +;============ +EVEN +@@tileblock: + mov dx,di ; hold starting position + 1 in dx + inc di ; we know the next tile also gets updated + repe scasb ; see how many more in a row + push di ; save off the spot being scanned + + mov bx,di + sub bx,dx ; number of tiles in a row + shl bx,1 ; number of bytes / row + + mov di,dx ; lookup position of start tile + sub di,[updateptr] + shl di,1 + mov di,[blockstarts-2+di] ; start of tile location + mov si,di + add di,[bufferofs] ; dest in current screen + add si,[masterofs] ; source in master screen + + mov dx,SCREENWIDTH + sub dx,bx ; offset to next line on screen +IFE GRMODE-CGAGR + sub dx,bx ; bx is words wide in CGA tiles +ENDIF + + mov ax,[screenseg] + mov ds,ax + mov es,ax + +REPT 15 + mov cx,bx +IFE GRMODE-CGAGR + rep movsw +ENDIF +IFE GRMODE-EGAGR + rep movsb +ENDIF + add si,dx + add di,dx +ENDM + mov cx,bx +IFE GRMODE-CGAGR + rep movsw +ENDIF +IFE GRMODE-EGAGR + rep movsb +ENDIF + + dec cx ; was 0 from last rep movsb, now $ffff for scasb + jmp @@findtile + +ENDP + + +;============================================================================ + + +;================= +; +; RFL_MaskForegroundTiles +; +; Scan through update looking for 3's. If the foreground tile there is a +; masked foreground tile, draw it to the screen +; +;================= + +PROC RFL_MaskForegroundTiles +PUBLIC RFL_MaskForegroundTiles +USES SI,DI,BP + jmp SHORT @@realstart +@@done: +; +; all tiles have been scanned +; + ret + +@@realstart: + mov di,[updateptr] + mov bp,(TILESWIDE+1)*TILESHIGH+2 + add bp,di ; when di = bx, all tiles have been scanned + push di + mov cx,-1 ; definately scan the entire thing +; +; scan for a 3 in the update list +; +@@findtile: + mov ax,ss + mov es,ax ; scan in the data segment + mov al,3 + pop di ; place to continue scaning from + repne scasb + cmp di,bp + je @@done + +;============ +; +; found a tile, see if it needs to be masked on +; +;============ + + push di + + sub di,[updateptr] + shl di,1 + mov si,[updatemapofs-2+di] ; offset from originmap + add si,[originmap] + + mov es,[mapsegs+2] ; foreground map plane segment + mov si,[es:si] ; foreground tile number + + or si,si + jz @@findtile ; 0 = no foreground tile + + mov bx,si + add bx,INTILE ;INTILE tile info table + mov es,[tinf] + test [BYTE PTR es:bx],80h ;high bit = masked tile + jz @@findtile + +;------------------- + +IFE GRMODE-CGAGR +;================= +; +; mask the tile CGA +; +;================= + + mov di,[blockstarts-2+di] + add di,[bufferofs] + mov es,[screenseg] + shl si,1 + mov ds,[grsegs+STARTTILE16M*2+si] + + mov bx,64 ;data starts 64 bytes after mask + + xor si,si + +lineoffset = 0 +REPT 16 + mov ax,[es:di+lineoffset] ;background + and ax,[si] ;mask + or ax,[si+bx] ;masked data + mov [es:di+lineoffset],ax ;background + inc si + inc si + mov ax,[es:di+lineoffset+2] ;background + and ax,[si] ;mask + or ax,[si+bx] ;masked data + mov [es:di+lineoffset+2],ax ;background + inc si + inc si +lineoffset = lineoffset + SCREENWIDTH +ENDM +ENDIF + +;------------------- + +IFE GRMODE-EGAGR +;================= +; +; mask the tile +; +;================= + + mov [BYTE planemask],1 + mov [BYTE planenum],0 + + mov di,[blockstarts-2+di] + add di,[bufferofs] + mov [cs:screenstartcs],di + mov es,[screenseg] + shl si,1 + mov ds,[grsegs+STARTTILE16M*2+si] + + mov bx,32 ;data starts 32 bytes after mask + +@@planeloopm: + mov dx,SC_INDEX + mov al,SC_MAPMASK + mov ah,[ss:planemask] + WORDOUT + mov dx,GC_INDEX + mov al,GC_READMAP + mov ah,[ss:planenum] + WORDOUT + + xor si,si + mov di,[cs:screenstartcs] +lineoffset = 0 +REPT 16 + mov cx,[es:di+lineoffset] ;background + and cx,[si] ;mask + or cx,[si+bx] ;masked data + inc si + inc si + mov [es:di+lineoffset],cx +lineoffset = lineoffset + SCREENWIDTH +ENDM + add bx,32 ;the mask is now further away + inc [ss:planenum] + shl [ss:planemask],1 ;shift plane mask over for next plane + cmp [ss:planemask],10000b ;done all four planes? + je @@drawn ;drawn all four planes + jmp @@planeloopm + +@@drawn: +ENDIF + +;------------------- + + mov ax,ss + mov ds,ax + mov cx,-1 ;definately scan the entire thing + + jmp @@findtile + +ENDP + + +END + diff --git a/16/cawat/ID_SD.C b/16/cawat/ID_SD.C new file mode 100644 index 00000000..c62a0aa1 --- /dev/null +++ b/16/cawat/ID_SD.C @@ -0,0 +1,1319 @@ +/* Catacomb Armageddon Source Code + * Copyright (C) 1993-2014 Flat Rock Software + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +// +// ID Engine +// ID_SD.c - Sound Manager +// v1.1d1 +// By Jason Blochowiak +// + +// +// This module handles dealing with generating sound on the appropriate +// hardware +// +// Depends on: User Mgr (for parm checking) +// +// Globals: +// For User Mgr: +// SoundSourcePresent - Sound Source thingie present? +// SoundBlasterPresent - SoundBlaster card present? +// AdLibPresent - AdLib card present? +// SoundMode - What device is used for sound effects +// (Use SM_SetSoundMode() to set) +// MusicMode - What device is used for music +// (Use SM_SetMusicMode() to set) +// For Cache Mgr: +// NeedsDigitized - load digitized sounds? +// NeedsMusic - load music? +// + +#define USE_MUSIC 0 + +#pragma hdrstop // Wierdo thing with MUSE + +#include + +#ifdef _MUSE_ // Will be defined in ID_Types.h +#include "ID_SD.h" +#else +#include "ID_HEADS.H" +#endif +#pragma hdrstop +#pragma warn -pia + +#define SDL_SoundFinished() {SoundNumber = SoundPriority = 0;} + +// Macros for AdLib stuff +#define selreg(n) outportb(0x388,n) +#define writereg(n) outportb(0x389,n) +#define readstat() inportb(0x388) + +// Global variables + boolean SoundSourcePresent,SoundBlasterPresent,AdLibPresent, + NeedsDigitized,NeedsMusic; + SDMode SoundMode; + SMMode MusicMode; + longword TimeCount; + word HackCount; + word *SoundTable; // Really * _seg *SoundTable, but that don't work + boolean ssIsTandy; + word ssPort = 2; + +// Internal variables +static boolean SD_Started; +static boolean TimerDone; +static word TimerVal,TimerDelay10,TimerDelay25,TimerDelay100; +static longword TimerDivisor,TimerCount; +static char *ParmStrings[] = + { + "noal", + nil + }; +static void (*SoundUserHook)(void); +static word SoundNumber,SoundPriority; +static void interrupt (*t0OldService)(void); +//static word t0CountTable[] = {8,8,8,8,40,40}; +static long LocalTime; + +// PC Sound variables +static byte pcLastSample,far *pcSound; +static longword pcLengthLeft; +static word pcSoundLookup[255]; + +// AdLib variables +static boolean alNoCheck; +static byte far *alSound; +static word alBlock; +static longword alLengthLeft; +static longword alTimeCount; +static Instrument alZeroInst; + +// This table maps channel numbers to carrier and modulator op cells +static byte carriers[9] = { 3, 4, 5,11,12,13,19,20,21}, + modifiers[9] = { 0, 1, 2, 8, 9,10,16,17,18}, +// This table maps percussive voice numbers to op cells + pcarriers[5] = {19,0xff,0xff,0xff,0xff}, + pmodifiers[5] = {16,17,18,20,21}; + +// Sequencer variables +static boolean sqActive; +static word alFXReg; +static ActiveTrack *tracks[sqMaxTracks], + mytracks[sqMaxTracks]; +static word sqMode,sqFadeStep; +static word far *sqHack,far *sqHackPtr,sqHackLen,sqHackSeqLen; +static long sqHackTime; + +// Internal routines + +/////////////////////////////////////////////////////////////////////////// +// +// SDL_SetTimer0() - Sets system timer 0 to the specified speed +// +/////////////////////////////////////////////////////////////////////////// +#pragma argsused +static void +SDL_SetTimer0(word speed) +{ +#ifndef TPROF // If using Borland's profiling, don't screw with the timer + outportb(0x43,0x36); // Change timer 0 + outportb(0x40,speed); + outportb(0x40,speed >> 8); + TimerDivisor = speed; +#else + TimerDivisor = 0x10000; +#endif +} + +/////////////////////////////////////////////////////////////////////////// +// +// SDL_SetIntsPerSec() - Uses SDL_SetTimer0() to set the number of +// interrupts generated by system timer 0 per second +// +/////////////////////////////////////////////////////////////////////////// +static void +SDL_SetIntsPerSec(word ints) +{ + SDL_SetTimer0(1192030 / ints); +} + +/////////////////////////////////////////////////////////////////////////// +// +// SDL_TimingService() - Used by SDL_InitDelay() to determine a timing +// value for the current system that we're running on +// +/////////////////////////////////////////////////////////////////////////// +static void interrupt +SDL_TimingService(void) +{ + TimerVal = _CX; + TimerDone++; + + outportb(0x20,0x20); // Ack interrupt +} + +/////////////////////////////////////////////////////////////////////////// +// +// SDL_InitDelay() - Sets up TimerDelay's for SDL_Delay() +// +/////////////////////////////////////////////////////////////////////////// +static void +SDL_InitDelay(void) +{ + int i; + word timer; + + setvect(8,SDL_TimingService); // Set to my timer 0 ISR + + SDL_SetIntsPerSec(1000); // Time 1ms + + for (i = 0,timer = 0;i < 10;i++) // Do timing test 10 times + { + asm xor dx,dx // Zero DX + asm mov cx,0xffff // Put starting value in CX + asm mov [TimerDone],cx // TimerDone = false - 1 +startloop: + asm or [TimerDone],0 + asm jnz startloop // Make sure we're at the start +loop: + asm test [TimerDone],1 // See if TimerDone flag got hit + asm jnz done // Yep - drop out of the loop + asm loop loop +done: + + if (0xffff - TimerVal > timer) + timer = 0xffff - TimerVal; + } + timer += timer / 2; // Use some slop + TimerDelay10 = timer / (1000 / 10); + TimerDelay25 = timer / (1000 / 25); + TimerDelay100 = timer / (1000 / 100); + + SDL_SetTimer0(0); // Reset timer 0 + + setvect(8,t0OldService); // Set back to old ISR +} + +/////////////////////////////////////////////////////////////////////////// +// +// SDL_Delay() - Delays the specified amount of time +// +/////////////////////////////////////////////////////////////////////////// +static void +SDL_Delay(word delay) +{ + if (!delay) + return; + +asm mov cx,[delay] +loop: +asm test [TimerDone],0 // Useless code - just for timing equivilency +asm jnz done +asm loop loop +done:; +} + +// +// PC Sound code +// + +/////////////////////////////////////////////////////////////////////////// +// +// SDL_PCPlaySound() - Plays the specified sound on the PC speaker +// +/////////////////////////////////////////////////////////////////////////// +#ifdef _MUSE_ +void +#else +static void +#endif +SDL_PCPlaySound(PCSound far *sound) +{ +asm pushf +asm cli + + pcLastSample = -1; + pcLengthLeft = sound->common.length; + pcSound = sound->data; + +asm popf +} + +/////////////////////////////////////////////////////////////////////////// +// +// SDL_PCStopSound() - Stops the current sound playing on the PC Speaker +// +/////////////////////////////////////////////////////////////////////////// +#ifdef _MUSE_ +void +#else +static void +#endif +SDL_PCStopSound(void) +{ +asm pushf +asm cli + + (long)pcSound = 0; + +asm in al,0x61 // Turn the speaker off +asm and al,0xfd // ~2 +asm out 0x61,al + +asm popf +} + +/////////////////////////////////////////////////////////////////////////// +// +// SDL_PCService() - Handles playing the next sample in a PC sound +// +/////////////////////////////////////////////////////////////////////////// +static void +SDL_PCService(void) +{ + byte s; + word t; + + if (pcSound) + { + s = *pcSound++; + if (s != pcLastSample) + { + asm pushf + asm cli + + pcLastSample = s; + if (s) // We have a frequency! + { + t = pcSoundLookup[s]; + asm mov bx,[t] + + asm mov al,0xb6 // Write to channel 2 (speaker) timer + asm out 43h,al + asm mov al,bl + asm out 42h,al // Low byte + asm mov al,bh + asm out 42h,al // High byte + + asm in al,0x61 // Turn the speaker & gate on + asm or al,3 + asm out 0x61,al + } + else // Time for some silence + { + asm in al,0x61 // Turn the speaker & gate off + asm and al,0xfc // ~3 + asm out 0x61,al + } + + asm popf + } + + if (!(--pcLengthLeft)) + { + SDL_PCStopSound(); + SDL_SoundFinished(); + } + } +} + +/////////////////////////////////////////////////////////////////////////// +// +// SDL_ShutPC() - Turns off the pc speaker +// +/////////////////////////////////////////////////////////////////////////// +static void +SDL_ShutPC(void) +{ +asm pushf +asm cli + + pcSound = 0; + +asm in al,0x61 // Turn the speaker & gate off +asm and al,0xfc // ~3 +asm out 0x61,al + +asm popf +} + +// AdLib Code + +/////////////////////////////////////////////////////////////////////////// +// +// alOut(n,b) - Puts b in AdLib card register n +// +/////////////////////////////////////////////////////////////////////////// +void +alOut(byte n,byte b) +{ +asm pushf +asm cli + +asm mov dx,0x388 +asm mov al,[n] +asm out dx,al +#if 0 + SDL_Delay(TimerDelay10); +#else +asm in al, dx +asm in al, dx +asm in al, dx +asm in al, dx +asm in al, dx +asm in al, dx +#endif + +asm mov dx,0x389 +asm mov al,[b] +asm out dx,al + +asm popf + +#if 0 + SDL_Delay(TimerDelay25); +#else +asm mov dx,0x388 +asm in al, dx +asm in al, dx +asm in al, dx +asm in al, dx +asm in al, dx +asm in al, dx +asm in al, dx +asm in al, dx +asm in al, dx +asm in al, dx + +asm in al, dx +asm in al, dx +asm in al, dx +asm in al, dx +asm in al, dx +asm in al, dx +asm in al, dx +asm in al, dx +asm in al, dx +asm in al, dx + +asm in al, dx +asm in al, dx +asm in al, dx +asm in al, dx +asm in al, dx +asm in al, dx +asm in al, dx +asm in al, dx +asm in al, dx +asm in al, dx + +asm in al, dx +asm in al, dx +asm in al, dx +asm in al, dx +asm in al, dx +#endif +} + +#if 0 +/////////////////////////////////////////////////////////////////////////// +// +// SDL_SetInstrument() - Puts an instrument into a generator +// +/////////////////////////////////////////////////////////////////////////// +static void +SDL_SetInstrument(int track,int which,Instrument far *inst,boolean percussive) +{ + byte c,m; + + if (percussive) + { + c = pcarriers[which]; + m = pmodifiers[which]; + } + else + { + c = carriers[which]; + m = modifiers[which]; + } + + tracks[track - 1]->inst = *inst; + tracks[track - 1]->percussive = percussive; + + alOut(m + alChar,inst->mChar); + alOut(m + alScale,inst->mScale); + alOut(m + alAttack,inst->mAttack); + alOut(m + alSus,inst->mSus); + alOut(m + alWave,inst->mWave); + + // Most percussive instruments only use one cell + if (c != 0xff) + { + alOut(c + alChar,inst->cChar); + alOut(c + alScale,inst->cScale); + alOut(c + alAttack,inst->cAttack); + alOut(c + alSus,inst->cSus); + alOut(c + alWave,inst->cWave); + } + + alOut(which + alFeedCon,inst->nConn); // DEBUG - I think this is right +} +#endif + +/////////////////////////////////////////////////////////////////////////// +// +// SDL_ALStopSound() - Turns off any sound effects playing through the +// AdLib card +// +/////////////////////////////////////////////////////////////////////////// +#ifdef _MUSE_ +void +#else +static void +#endif +SDL_ALStopSound(void) +{ +asm pushf +asm cli + + (long)alSound = 0; + alOut(alFreqH + 0,0); + +asm popf +} + +static void +SDL_AlSetFXInst(Instrument far *inst) +{ + byte c,m; + + m = modifiers[0]; + c = carriers[0]; + alOut(m + alChar,inst->mChar); + alOut(m + alScale,inst->mScale); + alOut(m + alAttack,inst->mAttack); + alOut(m + alSus,inst->mSus); + alOut(m + alWave,inst->mWave); + alOut(c + alChar,inst->cChar); + alOut(c + alScale,inst->cScale); + alOut(c + alAttack,inst->cAttack); + alOut(c + alSus,inst->cSus); + alOut(c + alWave,inst->cWave); + // DEBUG!!! - I just put this in +// alOut(alFeedCon,inst->nConn); +} + +/////////////////////////////////////////////////////////////////////////// +// +// SDL_ALPlaySound() - Plays the specified sound on the AdLib card +// +/////////////////////////////////////////////////////////////////////////// +#ifdef _MUSE_ +void +#else +static void +#endif +SDL_ALPlaySound(AdLibSound far *sound) +{ + Instrument far *inst; + + SDL_ALStopSound(); + +asm pushf +asm cli + + alLengthLeft = sound->common.length; + alSound = sound->data; + alBlock = ((sound->block & 7) << 2) | 0x20; + inst = &sound->inst; + + if (!(inst->mSus | inst->cSus)) + { + asm popf + Quit("SDL_ALPlaySound() - Bad instrument"); + } + + SDL_AlSetFXInst(inst); + +asm popf +} + +/////////////////////////////////////////////////////////////////////////// +// +// SDL_ALSoundService() - Plays the next sample out through the AdLib card +// +/////////////////////////////////////////////////////////////////////////// +static void +SDL_ALSoundService(void) +{ + byte s; + + if (alSound) + { + s = *alSound++; + if (!s) + alOut(alFreqH + 0,0); + else + { + alOut(alFreqL + 0,s); + alOut(alFreqH + 0,alBlock); + } + + if (!(--alLengthLeft)) + { + (long)alSound = 0; + alOut(alFreqH + 0,0); + SDL_SoundFinished(); + } + } +} + +#if 0 +/////////////////////////////////////////////////////////////////////////// +// +// SDL_SelectMeasure() - sets up sequencing variables for a given track +// +/////////////////////////////////////////////////////////////////////////// +static void +SDL_SelectMeasure(ActiveTrack *track) +{ + track->seq = track->moods[track->mood]; + track->nextevent = 0; +} +#endif + +static void +SDL_ALService(void) +{ + byte a,v; + word w; + + if (!sqActive) + return; + + while (sqHackLen && (sqHackTime <= alTimeCount)) + { + w = *sqHackPtr++; + sqHackTime = alTimeCount + *sqHackPtr++; + asm mov dx,[w] + asm mov [a],dl + asm mov [v],dh + alOut(a,v); + sqHackLen -= 4; + } + alTimeCount++; + if (!sqHackLen) + { + sqHackPtr = (word far *)sqHack; + sqHackLen = sqHackSeqLen; + alTimeCount = sqHackTime = 0; + } +} + +/////////////////////////////////////////////////////////////////////////// +// +// SDL_ShutAL() - Shuts down the AdLib card for sound effects +// +/////////////////////////////////////////////////////////////////////////// +static void +SDL_ShutAL(void) +{ +asm pushf +asm cli + + alOut(alEffects,0); + alOut(alFreqH + 0,0); + SDL_AlSetFXInst(&alZeroInst); + alSound = 0; + +asm popf +} + +/////////////////////////////////////////////////////////////////////////// +// +// SDL_CleanAL() - Totally shuts down the AdLib card +// +/////////////////////////////////////////////////////////////////////////// +static void +SDL_CleanAL(void) +{ + int i; + +asm pushf +asm cli + + alOut(alEffects,0); + for (i = 1;i < 0xf5;i++) + alOut(i,0); + +asm popf +} + +/////////////////////////////////////////////////////////////////////////// +// +// SDL_StartAL() - Starts up the AdLib card for sound effects +// +/////////////////////////////////////////////////////////////////////////// +static void +SDL_StartAL(void) +{ + alFXReg = 0; + alOut(alEffects,alFXReg); + SDL_AlSetFXInst(&alZeroInst); +} + +/////////////////////////////////////////////////////////////////////////// +// +// SDL_DetectAdLib() - Determines if there's an AdLib (or SoundBlaster +// emulating an AdLib) present +// +/////////////////////////////////////////////////////////////////////////// +static boolean +SDL_DetectAdLib(void) +{ + byte status1,status2; + int i; + + alOut(4,0x60); // Reset T1 & T2 + alOut(4,0x80); // Reset IRQ + status1 = readstat(); + alOut(2,0xff); // Set timer 1 + alOut(4,0x21); // Start timer 1 + SDL_Delay(TimerDelay100); + + status2 = readstat(); + alOut(4,0x60); + alOut(4,0x80); + + if (((status1 & 0xe0) == 0x00) && ((status2 & 0xe0) == 0xc0)) + { + for (i = 1;i <= 0xf5;i++) // Zero all the registers + alOut(i,0); + + alOut(1,0x20); // Set WSE=1 + alOut(8,0); // Set CSM=0 & SEL=0 + + return(true); + } + else + return(false); +} + +/////////////////////////////////////////////////////////////////////////// +// +// SDL_t0Service() - My timer 0 ISR which handles the different timings and +// dispatches to whatever other routines are appropriate +// +/////////////////////////////////////////////////////////////////////////// +static void interrupt +SDL_t0Service(void) +{ +static word count = 1; + +#if 0 // for debugging +asm mov dx,STATUS_REGISTER_1 +asm in al,dx +asm mov dx,ATR_INDEX +asm mov al,ATR_OVERSCAN +asm out dx,al +asm mov al,4 // red +asm out dx,al +#endif + + HackCount++; + +#if USE_MUSIC + if (MusicMode == smm_AdLib) + { + SDL_ALService(); + if (!(++count & 7)) + { + LocalTime++; + TimeCount++; + if (SoundUserHook) + SoundUserHook(); + } + if (!(count & 3)) + { + switch (SoundMode) + { + case sdm_PC: + SDL_PCService(); + break; + case sdm_AdLib: + SDL_ALSoundService(); + break; + } + } + } + else +#endif + { + if (!(++count & 1)) + { + LocalTime++; + TimeCount++; + if (SoundUserHook) + SoundUserHook(); + } + switch (SoundMode) + { + case sdm_PC: + SDL_PCService(); + break; + case sdm_AdLib: + SDL_ALSoundService(); + break; + } + } + +asm mov ax,[WORD PTR TimerCount] +asm add ax,[WORD PTR TimerDivisor] +asm mov [WORD PTR TimerCount],ax +asm jnc myack + t0OldService(); // If we overflow a word, time to call old int handler +asm jmp olddone +myack:; + outportb(0x20,0x20); // Ack the interrupt +olddone:; + +#if 0 // for debugging +asm mov dx,STATUS_REGISTER_1 +asm in al,dx +asm mov dx,ATR_INDEX +asm mov al,ATR_OVERSCAN +asm out dx,al +asm mov al,3 // blue +asm out dx,al +asm mov al,0x20 // normal +asm out dx,al +#endif +} + +//////////////////////////////////////////////////////////////////////////// +// +// SDL_ShutDevice() - turns off whatever device was being used for sound fx +// +//////////////////////////////////////////////////////////////////////////// +static void +SDL_ShutDevice(void) +{ + switch (SoundMode) + { + case sdm_PC: + SDL_ShutPC(); + break; + case sdm_AdLib: + SDL_ShutAL(); + break; + } + SoundMode = sdm_Off; +} + +/////////////////////////////////////////////////////////////////////////// +// +// SDL_CleanDevice() - totally shuts down all sound devices +// +/////////////////////////////////////////////////////////////////////////// +static void +SDL_CleanDevice(void) +{ + if ((SoundMode == sdm_AdLib) || (MusicMode == smm_AdLib)) + SDL_CleanAL(); +} + +/////////////////////////////////////////////////////////////////////////// +// +// SDL_StartDevice() - turns on whatever device is to be used for sound fx +// +/////////////////////////////////////////////////////////////////////////// +static void +SDL_StartDevice(void) +{ + switch (SoundMode) + { + case sdm_AdLib: + SDL_StartAL(); + break; + } + SoundNumber = SoundPriority = 0; +} + +static void +SDL_SetTimerSpeed(void) +{ + word rate; + +#if USE_MUSIC + if (MusicMode == smm_AdLib) + rate = TickBase * 8; + else +#endif + rate = TickBase * 2; + SDL_SetIntsPerSec(rate); +} + +// Public routines + +/////////////////////////////////////////////////////////////////////////// +// +// SD_SetSoundMode() - Sets which sound hardware to use for sound effects +// +/////////////////////////////////////////////////////////////////////////// +boolean +SD_SetSoundMode(SDMode mode) +{ + boolean result; + word tableoffset; + + SD_StopSound(); + +#ifndef _MUSE_ + switch (mode) + { + case sdm_Off: + NeedsDigitized = false; + result = true; + break; + case sdm_PC: + tableoffset = STARTPCSOUNDS; + NeedsDigitized = false; + result = true; + break; + case sdm_AdLib: + if (AdLibPresent) + { + tableoffset = STARTADLIBSOUNDS; + NeedsDigitized = false; + result = true; + } + break; + default: + result = false; + break; + } +#endif + + if (result && (mode != SoundMode)) + { + SDL_ShutDevice(); + SoundMode = mode; +#ifndef _MUSE_ + SoundTable = (word *)(&audiosegs[tableoffset]); +#endif + SDL_StartDevice(); + } + + SDL_SetTimerSpeed(); + + return(result); +} + +/////////////////////////////////////////////////////////////////////////// +// +// SD_SetMusicMode() - sets the device to use for background music +// +/////////////////////////////////////////////////////////////////////////// +boolean +SD_SetMusicMode(SMMode mode) +{ +#if USE_MUSIC + boolean result; + + SD_FadeOutMusic(); + while (SD_MusicPlaying()) + ; + + switch (mode) + { + case smm_Off: + NeedsMusic = false; + result = true; + break; + case smm_AdLib: + if (AdLibPresent) + { + NeedsMusic = true; + result = true; + } + break; + default: + result = false; + break; + } + + if (result) + MusicMode = mode; + + SDL_SetTimerSpeed(); + + return(result); +#endif +} + +/////////////////////////////////////////////////////////////////////////// +// +// SD_Startup() - starts up the Sound Mgr +// Detects all additional sound hardware and installs my ISR +// +/////////////////////////////////////////////////////////////////////////// +void +SD_Startup(void) +{ + int i; + + if (SD_Started) + return; + + ssIsTandy = false; + alNoCheck = false; +#ifndef _MUSE_ + for (i = 1;i < _argc;i++) + { + switch (US_CheckParm(_argv[i],ParmStrings)) + { + case 0: // No AdLib detection + alNoCheck = true; + break; + } + } +#endif + + SoundUserHook = 0; + + t0OldService = getvect(8); // Get old timer 0 ISR + + SDL_InitDelay(); // SDL_InitDelay() uses t0OldService + + setvect(8,SDL_t0Service); // Set to my timer 0 ISR + LocalTime = TimeCount = alTimeCount = 0; + + SD_SetSoundMode(sdm_Off); +#if USE_MUSIC + SD_SetMusicMode(smm_Off); +#endif + + if (!alNoCheck) + AdLibPresent = SDL_DetectAdLib(); + + for (i = 0;i < 255;i++) + pcSoundLookup[i] = i * 60; + + SD_Started = true; +} + +/////////////////////////////////////////////////////////////////////////// +// +// SD_Default() - Sets up the default behaviour for the Sound Mgr whether +// the config file was present or not. +// +/////////////////////////////////////////////////////////////////////////// +void +SD_Default(boolean gotit,SDMode sd,SMMode sm) +{ + boolean gotsd,gotsm; + + gotsd = gotsm = gotit; + + if (gotsd) // Make sure requested sound hardware is available + { + switch (sd) + { + case sdm_AdLib: + gotsd = AdLibPresent; + break; + } + } + if (!gotsd) + { + if (AdLibPresent) + sd = sdm_AdLib; + else + sd = sdm_PC; + } + if (sd != SoundMode) + SD_SetSoundMode(sd); + + + if (gotsm) // Make sure requested music hardware is available + { + switch (sm) + { + case sdm_AdLib: + gotsm = AdLibPresent; + break; + } + } + if (!gotsm) + { + if (AdLibPresent) + sm = smm_AdLib; + } +#if USE_MUSIC + if (sm != MusicMode) + SD_SetMusicMode(sm); +#endif +} + +/////////////////////////////////////////////////////////////////////////// +// +// SD_Shutdown() - shuts down the Sound Mgr +// Removes sound ISR and turns off whatever sound hardware was active +// +/////////////////////////////////////////////////////////////////////////// +void +SD_Shutdown(void) +{ + if (!SD_Started) + return; + +#if USE_MUSIC + SD_MusicOff(); +#endif + SDL_ShutDevice(); + SDL_CleanDevice(); + + asm pushf + asm cli + + SDL_SetTimer0(0); + + setvect(8,t0OldService); + + asm popf + + SD_Started = false; +} + +/////////////////////////////////////////////////////////////////////////// +// +// SD_SetUserHook() - sets the routine that the Sound Mgr calls every 1/70th +// of a second from its timer 0 ISR +// +/////////////////////////////////////////////////////////////////////////// +void +SD_SetUserHook(void (* hook)(void)) +{ + SoundUserHook = hook; +} + +/////////////////////////////////////////////////////////////////////////// +// +// SD_PlaySound() - plays the specified sound on the appropriate hardware +// +/////////////////////////////////////////////////////////////////////////// +void +SD_PlaySound(soundnames sound) +{ + SoundCommon far *s; + + if ((SoundMode == sdm_Off) || (sound == -1)) + return; + + s = MK_FP(SoundTable[sound],0); + if (!s) + Quit("SD_PlaySound() - Uncached sound"); + if (!s->length) + Quit("SD_PlaySound() - Zero length sound"); + if (s->priority < SoundPriority) + return; + + switch (SoundMode) + { + case sdm_PC: + SDL_PCPlaySound((void far *)s); + break; + case sdm_AdLib: + SDL_ALPlaySound((void far *)s); + break; + } + + SoundNumber = sound; + SoundPriority = s->priority; +} + +/////////////////////////////////////////////////////////////////////////// +// +// SD_SoundPlaying() - returns the sound number that's playing, or 0 if +// no sound is playing +// +/////////////////////////////////////////////////////////////////////////// +word +SD_SoundPlaying(void) +{ + boolean result = false; + + switch (SoundMode) + { + case sdm_PC: + result = pcSound? true : false; + break; + case sdm_AdLib: + result = alSound? true : false; + break; + } + + if (result) + return(SoundNumber); + else + return(false); +} + +/////////////////////////////////////////////////////////////////////////// +// +// SD_StopSound() - if a sound is playing, stops it +// +/////////////////////////////////////////////////////////////////////////// +void +SD_StopSound(void) +{ + switch (SoundMode) + { + case sdm_PC: + SDL_PCStopSound(); + break; + case sdm_AdLib: + SDL_ALStopSound(); + break; + } + + SDL_SoundFinished(); +} + +/////////////////////////////////////////////////////////////////////////// +// +// SD_WaitSoundDone() - waits until the current sound is done playing +// +/////////////////////////////////////////////////////////////////////////// +void +SD_WaitSoundDone(void) +{ + while (SD_SoundPlaying()) + ; +} + +/////////////////////////////////////////////////////////////////////////// +// +// SD_MusicOn() - turns on the sequencer +// +/////////////////////////////////////////////////////////////////////////// +void +SD_MusicOn(void) +{ +#if USE_MUSIC + sqActive = true; +#endif +} + +/////////////////////////////////////////////////////////////////////////// +// +// SD_MusicOff() - turns off the sequencer and any playing notes +// +/////////////////////////////////////////////////////////////////////////// +void +SD_MusicOff(void) +{ +#if USE_MUSIC + word i; + + + switch (MusicMode) + { + case smm_AdLib: + alFXReg = 0; + alOut(alEffects,0); + for (i = 0;i < sqMaxTracks;i++) + alOut(alFreqH + i + 1,0); + break; + } + sqActive = false; +#endif +} + +/////////////////////////////////////////////////////////////////////////// +// +// SD_StartMusic() - starts playing the music pointed to +// +/////////////////////////////////////////////////////////////////////////// +void +SD_StartMusic(MusicGroup far *music) +{ +#if USE_MUSIC + SD_MusicOff(); +asm pushf +asm cli + + if (MusicMode == smm_AdLib) + { + sqHackPtr = sqHack = music->values; + sqHackSeqLen = sqHackLen = music->length; + sqHackTime = 0; + alTimeCount = 0; + SD_MusicOn(); + } + +asm popf +#endif +} + +/////////////////////////////////////////////////////////////////////////// +// +// SD_FadeOutMusic() - starts fading out the music. Call SD_MusicPlaying() +// to see if the fadeout is complete +// +/////////////////////////////////////////////////////////////////////////// +void +SD_FadeOutMusic(void) +{ +#if USE_MUSIC + switch (MusicMode) + { + case smm_AdLib: + // DEBUG - quick hack to turn the music off + SD_MusicOff(); + break; + } +#endif +} + +/////////////////////////////////////////////////////////////////////////// +// +// SD_MusicPlaying() - returns true if music is currently playing, false if +// not +// +/////////////////////////////////////////////////////////////////////////// +boolean +SD_MusicPlaying(void) +{ +#if USE_MUSIC + boolean result; + + switch (MusicMode) + { + case smm_AdLib: + result = false; + // DEBUG - not written + break; + default: + result = false; + } + + return(result); +#endif +} diff --git a/16/cawat/ID_SD.H b/16/cawat/ID_SD.H new file mode 100644 index 00000000..d60aea30 --- /dev/null +++ b/16/cawat/ID_SD.H @@ -0,0 +1,206 @@ +/* Catacomb Armageddon Source Code + * Copyright (C) 1993-2014 Flat Rock Software + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +// +// ID Engine +// ID_SD.h - Sound Manager Header +// v1.0d1 +// By Jason Blochowiak +// + +#ifndef __TYPES__ +#include "ID_Types.h" +#endif + +#ifndef __ID_SD__ +#define __ID_SD__ + +#ifdef __DEBUG__ +#define __DEBUG_SoundMgr__ +#endif + +#define TickBase 70 // 70Hz per tick - used as a base for timer 0 + +typedef enum { + sdm_Off, + sdm_PC,sdm_AdLib, + } SDMode; +typedef enum { + smm_Off,smm_AdLib + } SMMode; + +typedef struct + { + longword length; + word priority; + } SoundCommon; + +// PC Sound stuff +#define pcTimer 0x42 +#define pcTAccess 0x43 +#define pcSpeaker 0x61 + +#define pcSpkBits 3 + +typedef struct + { + SoundCommon common; + byte data[1]; + } PCSound; + +// Registers for the Sound Blaster card - needs to be offset by n0 +#define sbReset 0x206 +#define sbReadData 0x20a +#define sbWriteCmd 0x20c +#define sbWriteData 0x20c +#define sbWriteStat 0x20c +#define sbDataAvail 0x20e + +typedef struct + { + SoundCommon common; + word hertz; + byte bits, + reference, + data[1]; + } SampledSound; + +// Registers for the AdLib card +// Operator stuff +#define alChar 0x20 +#define alScale 0x40 +#define alAttack 0x60 +#define alSus 0x80 +#define alWave 0xe0 +// Channel stuff +#define alFreqL 0xa0 +#define alFreqH 0xb0 +#define alFeedCon 0xc0 +// Global stuff +#define alEffects 0xbd + +typedef struct + { + byte mChar,cChar, + mScale,cScale, + mAttack,cAttack, + mSus,cSus, + mWave,cWave, + nConn, + + // These are only for Muse - these bytes are really unused + voice, + mode, + unused[3]; + } Instrument; + +typedef struct + { + SoundCommon common; + Instrument inst; + byte block, + data[1]; + } AdLibSound; + +// +// Sequencing stuff +// +#define sqMaxTracks 10 +#define sqMaxMoods 1 // DEBUG + +#define sev_Null 0 // Does nothing +#define sev_NoteOff 1 // Turns a note off +#define sev_NoteOn 2 // Turns a note on +#define sev_NotePitch 3 // Sets the pitch of a currently playing note +#define sev_NewInst 4 // Installs a new instrument +#define sev_NewPerc 5 // Installs a new percussive instrument +#define sev_PercOn 6 // Turns a percussive note on +#define sev_PercOff 7 // Turns a percussive note off +#define sev_SeqEnd -1 // Terminates a sequence + +// Flags for MusicGroup.flags +#define sf_Melodic 0 +#define sf_Percussive 1 + +#if 1 +typedef struct + { + word length, + values[1]; + } MusicGroup; +#else +typedef struct + { + word flags, + count, + offsets[1]; + } MusicGroup; +#endif + +typedef struct + { + /* This part needs to be set up by the user */ + word mood,far *moods[sqMaxMoods]; + + /* The rest is set up by the code */ + Instrument inst; + boolean percussive; + word far *seq; + longword nextevent; + } ActiveTrack; + +#define sqmode_Normal 0 +#define sqmode_FadeIn 1 +#define sqmode_FadeOut 2 + +#define sqMaxFade 64 // DEBUG + + +// Global variables +extern boolean AdLibPresent, + NeedsMusic; // For Caching Mgr +extern SDMode SoundMode; +extern SMMode MusicMode; +extern longword TimeCount; // Global time in ticks +extern SDMode oldsoundmode; + +// Function prototypes +extern void SD_Startup(void), + SD_Shutdown(void), + SD_Default(boolean gotit,SDMode sd,SMMode sm), + SD_PlaySound(soundnames sound), + SD_StopSound(void), + SD_WaitSoundDone(void), + SD_StartMusic(MusicGroup far *music), + SD_MusicOn(void), + SD_MusicOff(void), + SD_FadeOutMusic(void), + SD_SetUserHook(void (*hook)(void)); +extern boolean SD_MusicPlaying(void), + SD_SetSoundMode(SDMode mode), + SD_SetMusicMode(SMMode mode); +extern word SD_SoundPlaying(void); + +#ifdef _MUSE_ // MUSE Goes directly to the lower level routines +extern void SDL_PCPlaySound(PCSound far *sound), + SDL_PCStopSound(void), + SDL_ALPlaySound(AdLibSound far *sound), + SDL_ALStopSound(void); +#endif + +#endif diff --git a/16/cawat/ID_STRS.H b/16/cawat/ID_STRS.H new file mode 100644 index 00000000..c34a2459 --- /dev/null +++ b/16/cawat/ID_STRS.H @@ -0,0 +1,128 @@ +/* Catacomb Armageddon Source Code + * Copyright (C) 1993-2014 Flat Rock Software + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#define S_LOADING "Loading" +#define S_EMPTYSPOT "Empty" +#define S_SVGACOMP "SVGA Compatibility Mode Enabled." +#define S_READYPRESS " Ready - Press a Key " +#define S_NOSFX "NO SOUND EFFECTS" +#define S_PCSPKR "PC SPEAKER" +#define S_ALSB "ADLIB/SOUNDBLASTER" +#define S_QUIET "QUIET ADLIB/SOUNDBLASTER" +#define S_NOMUSIC "NO MUSIC" +#define S_BEGINE "BEGIN EASY GAME" +#define S_BEGINN "BEGIN NORMAL GAME" +#define S_BEGINH "BEGIN HARD GAME" +#define S_UPLEFT "UP & LEFT" +#define S_UP "UP" +#define S_UPRIGHT "UP & RIGHT" +#define S_RIGHT "RIGHT" +#define S_DNRIGHT "DOWN & RIGHT" +#define S_DN "DOWN" +#define S_DNLEFT "DOWN & LEFT" +#define S_LEFT "LEFT" + +#define S_JUMP "JUMP" +#define S_POGO "POGO" +#define S_FIRE "FIRE" +#define S_MOVEMENT "MOVEMENT" +#define S_BUTTONS "BUTTONS" + +#define S_SOUND "SOUND" +#define S_MUSIC "MUSIC" +#define S_OPTIONS "OPTIONS" +#define S_USEKB "USE KEYBOARD" +#define S_USEJOY1 "USE JOYSTICK #1" +#define S_USEJOY2 "USE JOYSTICK #2" +#define S_NEWGAME "NEW GAME" +#define S_LOADGAME "LOAD GAME" +#define S_SAVEGAME "SAVE GAME" +#define S_CONFIG "CONFIGURE" +#define S_ENDGAME "END GAME" +#define S_PADDLEWAR "PADDLE WAR" +#define S_QUIT "QUIT" + +#define S_ESCBACK "ESC TO BACK OUT" +#define S_ESCBACK1 "ESC to back out" +#define S_ESCQUIT "ESC to quit" +#define S_F1HELP "F1 for help" +#define S_REALLYEND "REALLY END CURRENT GAME?" +#define S_PRESSY "PRESS Y TO END IT" +#define S_REALLYQUIT "REALLY QUIT?" +#define S_PRESSYQ "PRESS Y TO QUIT" +#define S_INAGAME "YOU'RE IN A GAME" +#define S_PRESSY2L "PRESS Y TO LOAD GAME" +#define S_PRESSY4N "PRESS Y FOR NEW GAME" + +#define S_USLERROR "Error: " +#define S_USLUNKNOWN "Unknown" +#define S_USLDISKFULL "Disk is Full" +#define S_USLFILEINC "File is Incomplete" +#define S_PRESSKEY "PRESS ANY KEY" +#define S_PRESSKEY1 "Press any key" + +#define S_SBOXON "SCORE BOX (ON)" +#define S_SBOXOFF "SCORE BOX (OFF)" +#define S_SVGAON "SVGA COMPATIBILITY (ON)" +#define S_SVGAOFF "SVGA COMPATIBILITY (OFF)" +#define S_2BON "TWO-BUTTON FIRING (ON)" +#define S_2BOFF "TWO-BUTTON FIRING (OFF)" + +#define S_SBOXNOWON "Score box now on" +#define S_SBOXNOWOFF "Score box now off" +#define S_SVGANOWON "SVGA compatibility now on" +#define S_SVGANOWOFF "SVGA compatibility now off" +#define S_2BNOWON "Two-button firing now on" +#define S_2BNOWOFF "Two-button firing now off" + +#define S_KEYBOARD "KEYBOARD" +#define S_JOY1 "JOYSTICK #1" +#define S_JOY2 "JOYSTICK #2" +#define S_MOUSE "MOUSE" +#define S_CONTROL "CONTROL: " +#define S_KEYUSED "Key already used" +#define S_PB1 "and press button #1" +#define S_PB2 "and press button #2" +#define S_MJUL "Move Joystick to upper left" +#define S_MJLR "Move Joystick to lower right" + +#define S_USINGJ1 "USING "S_JOY1 +#define S_USINGJ2 "USING "S_JOY2 +#define S_TYPENAME "Type name" +#define S_ENTERACC "Enter accepts" +#define S_UNTITLED "Untitled" +#define S_SAVING "Saving" +#define S_YOULOST "You lost!" +#define S_YOUWON "You won!" +#define S_ARRMOVE "Arrows move" +#define S_ENTERSEL "Enter selects" + +#define S_RETGAME "RETURN TO GAME" +#define S_RETDEMO "RETURN TO DEMO" +#define S_CTRLPANEL "Control Panel" +#define S_QUITTING "Quitting..." + +#define S_WHATNAME "What is the name of this creature?" +#define S_SORRY "Sorry, that's not quite right." +#define S_CHECKMAN "Please check your manual and try again." + +#define S_BADCARD "Improper video card! If you really have an EGA/VGA card that I am not\n"\ + "detecting, use the -HIDDENCARD command line parameter!" +#define S_BADCARD1 "Improper video card! If you really have a CGA card that I am not\n"\ + "detecting, use the -HIDDENCARD command line parameter!" + diff --git a/16/cawat/ID_US.C b/16/cawat/ID_US.C new file mode 100644 index 00000000..082b2315 --- /dev/null +++ b/16/cawat/ID_US.C @@ -0,0 +1,3691 @@ +/* Catacomb Armageddon Source Code + * Copyright (C) 1993-2014 Flat Rock Software + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +// +// ID Engine +// ID_US.c - User Manager +// v1.0d1 +// By Jason Blochowiak +// + +// +// This module handles dealing with user input & feedback +// +// Depends on: Input Mgr, View Mgr, some variables from the Sound, Caching, +// and Refresh Mgrs, Memory Mgr for background save/restore +// +// Globals: +// ingame - Flag set by game indicating if a game is in progress +// abortgame - Flag set if the current game should be aborted (if a load +// game fails) +// loadedgame - Flag set if a game was loaded +// abortprogram - Normally nil, this points to a terminal error message +// if the program needs to abort +// restartgame - Normally set to gd_Continue, this is set to one of the +// difficulty levels if a new game should be started +// PrintX, PrintY - Where the User Mgr will print (global coords) +// WindowX,WindowY,WindowW,WindowH - The dimensions of the current +// window +// + +// DEBUG - handle LPT3 for Sound Source + +#include "ID_HEADS.H" + +#define CTL_M_ADLIBUPPIC CTL_S_ADLIBUPPIC +#define CTL_M_ADLIBDNPIC CTL_S_ADLIBDNPIC + +#pragma hdrstop + +#pragma warn -pia + +#define MaxX 320 +#define MaxY 200 + +#define MaxHelpLines 500 + +#define MaxHighName 57 +#define MaxScores 10 +typedef struct + { + char name[MaxHighName + 1]; + long score; + word completed; + } HighScore; + +#define MaxGameName 32 +#define MaxSaveGames 7 +typedef struct + { + char signature[4]; + boolean present; + char name[MaxGameName + 1]; + } SaveGame; + +// Hack import for TED launch support +extern boolean tedlevel; +extern word tedlevelnum; +extern void TEDDeath(void); +static char *ParmStrings[] = {"TEDLEVEL","NOWAIT",""}; + + +// Global variables + boolean ingame,abortgame,loadedgame; + char *abortprogram; + GameDiff restartgame = gd_Continue; + word PrintX,PrintY; + word WindowX,WindowY,WindowW,WindowH; + +// Internal variables +static boolean US_Started; +static boolean GameIsDirty, + HighScoresDirty, + QuitToDos, + ResumeGame, + NoWait; + +static memptr LineOffsets; + +static boolean Button0,Button1, + CursorBad; +static int CursorX,CursorY; + +static void (*USL_MeasureString)(char far *,word *,word *) = VW_MeasurePropString, + (*USL_DrawString)(char far *) = VWB_DrawPropString; + +static boolean (*USL_SaveGame)(int),(*USL_LoadGame)(int); +static void (*USL_ResetGame)(void); +static SaveGame Games[MaxSaveGames]; +static HighScore Scores[MaxScores] = + { + {"Sir Lancelot",500}, + {"",0}, + {"",0}, + {"",0}, + {"",0}, + {"",0}, + {"",0}, + {"",0}, + {"",0}, + {"",0} + }; + +// Internal routines + +// Public routines + +/////////////////////////////////////////////////////////////////////////// +// +// USL_HardError() - Handles the Abort/Retry/Fail sort of errors passed +// from DOS. +// +/////////////////////////////////////////////////////////////////////////// +#pragma warn -par +#pragma warn -rch +int +USL_HardError(word errval,int ax,int bp,int si) +{ +#define IGNORE 0 +#define RETRY 1 +#define ABORT 2 +extern void ShutdownId(void); + +static char buf[32]; +static WindowRec wr; +static boolean oldleavedriveon; + int di; + char c,*s,*t; + + + di = _DI; + + oldleavedriveon = LeaveDriveOn; + LeaveDriveOn = false; + + if (ax < 0) + s = "Device Error"; + else + { + if ((di & 0x00ff) == 0) + s = "Drive ~ is Write Protected"; + else + s = "Error on Drive ~"; + for (t = buf;*s;s++,t++) // Can't use sprintf() + if ((*t = *s) == '~') + *t = (ax & 0x00ff) + 'A'; + *t = '\0'; + s = buf; + } + + c = peekb(0x40,0x49); // Get the current screen mode + if ((c < 4) || (c == 7)) + goto oh_kill_me; + + // DEBUG - handle screen cleanup + + US_SaveWindow(&wr); + US_CenterWindow(30,3); + US_CPrint(s); + US_CPrint("(R)etry or (A)bort?"); + VW_UpdateScreen(); + IN_ClearKeysDown(); + +asm sti // Let the keyboard interrupts come through + + while (true) + { + switch (IN_WaitForASCII()) + { + case key_Escape: + case 'a': + case 'A': + goto oh_kill_me; + break; + case key_Return: + case key_Space: + case 'r': + case 'R': + US_ClearWindow(); + VW_UpdateScreen(); + US_RestoreWindow(&wr); + LeaveDriveOn = oldleavedriveon; + return(RETRY); + break; + } + } + +oh_kill_me: + abortprogram = s; + ShutdownId(); + fprintf(stderr,"Terminal Error: %s\n",s); + if (tedlevel) + fprintf(stderr,"You launched from TED. I suggest that you reboot...\n"); + + return(ABORT); +#undef IGNORE +#undef RETRY +#undef ABORT +} +#pragma warn +par +#pragma warn +rch + +/////////////////////////////////////////////////////////////////////////// +// +// USL_GiveSaveName() - Returns a pointer to a static buffer that contains +// the filename to use for the specified save game +// +/////////////////////////////////////////////////////////////////////////// +static char * +USL_GiveSaveName(word game) +{ +static char filename[32]; + char *s,*t; + + for (s = "SAVEGM",t = filename;*s;) + *t++ = *s++; + *t++ = game + '0'; + for (s = "."EXTENSION;*s;) + *t++ = *s++; + *t = '\0'; + + return(filename); +} + +/////////////////////////////////////////////////////////////////////////// +// +// US_SetLoadSaveHooks() - Sets the routines that the User Mgr calls after +// reading or writing the save game headers +// +/////////////////////////////////////////////////////////////////////////// +void +US_SetLoadSaveHooks(boolean (*load)(int),boolean (*save)(int),void (*reset)(void)) +{ + USL_LoadGame = load; + USL_SaveGame = save; + USL_ResetGame = reset; +} + +/////////////////////////////////////////////////////////////////////////// +// +// USL_ReadConfig() - Reads the configuration file, if present, and sets +// things up accordingly. If it's not present, uses defaults. This file +// includes the high scores. +// +/////////////////////////////////////////////////////////////////////////// +static void +USL_ReadConfig(void) +{ + boolean gotit; + int file; + SDMode sd; + SMMode sm; + ControlType ctl; + + if ((file = open("CONFIG."EXTENSION,O_BINARY | O_RDONLY)) != -1) + { + read(file,Scores,sizeof(HighScore) * MaxScores); + read(file,&sd,sizeof(sd)); + read(file,&sm,sizeof(sm)); + read(file,&ctl,sizeof(ctl)); + read(file,&(KbdDefs[0]),sizeof(KbdDefs[0])); + close(file); + + HighScoresDirty = false; + gotit = true; + } + else + { + sd = sdm_Off; + sm = smm_Off; + ctl = ctrl_Keyboard; + + gotit = false; + HighScoresDirty = true; + } + + SD_Default(gotit,sd,sm); + IN_Default(gotit,ctl); +} + +/////////////////////////////////////////////////////////////////////////// +// +// USL_WriteConfig() - Writes out the current configuration, including the +// high scores. +// +/////////////////////////////////////////////////////////////////////////// +static void +USL_WriteConfig(void) +{ + int file; + + file = open("CONFIG."EXTENSION,O_CREAT | O_BINARY | O_WRONLY, + S_IREAD | S_IWRITE | S_IFREG); + if (file != -1) + { + write(file,Scores,sizeof(HighScore) * MaxScores); + write(file,&SoundMode,sizeof(SoundMode)); + write(file,&MusicMode,sizeof(MusicMode)); + write(file,&(Controls[0]),sizeof(Controls[0])); + write(file,&(KbdDefs[0]),sizeof(KbdDefs[0])); + close(file); + } +} + +/////////////////////////////////////////////////////////////////////////// +// +// USL_CheckSavedGames() - Checks to see which saved games are present +// & valid +// +/////////////////////////////////////////////////////////////////////////// +static void +USL_CheckSavedGames(void) +{ + boolean ok; + char *filename; + word i; + int file; + SaveGame *game; + + USL_SaveGame = 0; + USL_LoadGame = 0; + + for (i = 0,game = Games;i < MaxSaveGames;i++,game++) + { + filename = USL_GiveSaveName(i); + ok = false; + if ((file = open(filename,O_BINARY | O_RDONLY)) != -1) + { + if + ( + (read(file,game,sizeof(*game)) == sizeof(*game)) + && (!strcmp(game->signature,EXTENSION)) + ) + ok = true; + + close(file); + } + + if (ok) + game->present = true; + else + { + strcpy(game->signature,EXTENSION); + game->present = false; + strcpy(game->name,"Empty"); + } + } +} + +/////////////////////////////////////////////////////////////////////////// +// +// US_Startup() - Starts the User Mgr +// +/////////////////////////////////////////////////////////////////////////// +void +US_Startup(void) +{ + if (US_Started) + return; + + harderr(USL_HardError); // Install the fatal error handler + + US_InitRndT(true); // Initialize the random number generator + + USL_ReadConfig(); // Read config file + + US_Started = true; +} + +/////////////////////////////////////////////////////////////////////////// +// +// US_Setup() - Does the disk access part of the User Mgr's startup +// +/////////////////////////////////////////////////////////////////////////// +void +US_Setup(void) +{ + USL_CheckSavedGames(); // Check which saved games are present +} + +/////////////////////////////////////////////////////////////////////////// +// +// US_Shutdown() - Shuts down the User Mgr +// +/////////////////////////////////////////////////////////////////////////// +void +US_Shutdown(void) +{ + if (!US_Started) + return; + + if (!abortprogram) + USL_WriteConfig(); + + US_Started = false; +} + +/////////////////////////////////////////////////////////////////////////// +// +// US_CheckParm() - checks to see if a string matches one of a set of +// strings. The check is case insensitive. The routine returns the +// index of the string that matched, or -1 if no matches were found +// +/////////////////////////////////////////////////////////////////////////// +int +US_CheckParm(char *parm,char **strings) +{ + char cp,cs, + *p,*s; + int i; + + while (!isalpha(*parm)) // Skip non-alphas + parm++; + + for (i = 0;*strings && **strings;i++) + { + for (s = *strings++,p = parm,cs = cp = 0;cs == cp;) + { + cs = *s++; + if (!cs) + return(i); + cp = *p++; + + if (isupper(cs)) + cs = tolower(cs); + if (isupper(cp)) + cp = tolower(cp); + } + } + return(-1); +} + +/////////////////////////////////////////////////////////////////////////// +// +// USL_ScreenDraw() - Draws a chunk of the text screen (called only by +// US_TextScreen()) +// +/////////////////////////////////////////////////////////////////////////// +static void +USL_ScreenDraw(word x,word y,char *s,byte attr) +{ + byte far *screen; + + screen = MK_FP(0xb800,(x * 2) + (y * 80 * 2)); + while (*s) + { + *screen++ = *s++; + *screen++ = attr; + } +} + +/////////////////////////////////////////////////////////////////////////// +// +// USL_ClearTextScreen() - Makes sure the screen is in text mode, clears it, +// and moves the cursor to the leftmost column of the bottom line +// +/////////////////////////////////////////////////////////////////////////// +static void +USL_ClearTextScreen(void) +{ + // Set to 80x25 color text mode + _AL = 3; // Mode 3 + _AH = 0x00; + geninterrupt(0x10); + + // Use BIOS to move the cursor to the bottom of the screen + _AH = 0x0f; + geninterrupt(0x10); // Get current video mode into _BH + _DL = 0; // Lefthand side of the screen + _DH = 24; // Bottom row + _AH = 0x02; + geninterrupt(0x10); +} + +/////////////////////////////////////////////////////////////////////////// +// +// US_TextScreen() - Puts up the startup text screen +// Note: These are the only User Manager functions that can be safely called +// before the User Mgr has been started up +// +/////////////////////////////////////////////////////////////////////////// +void +US_TextScreen(void) +{ + word i,n, + sx,sy; +extern char far introscn; + + USL_ClearTextScreen(); + + _fmemcpy(MK_FP(0xb800,0),7 + &introscn,80 * 25 * 2); + + // Check for TED launching here + for (i = 1;i < _argc;i++) + { + n = US_CheckParm(_argv[i],ParmStrings); + if (n == 0) + { + tedlevelnum = atoi(_argv[i + 1]); + if (tedlevelnum >= 0) + { + tedlevel = true; + return; + } + else + break; + } + else if (n == 1) + { + NoWait = true; + return; + } + } +} + +/////////////////////////////////////////////////////////////////////////// +// +// USL_Show() - Changes the appearance of one of the fields on the text +// screen. Possibly adds a checkmark in front of it and highlights it +// +/////////////////////////////////////////////////////////////////////////// +static void +USL_Show(word x,word y,word w,boolean show,boolean hilight) +{ + byte far *screen; + + screen = MK_FP(0xb800,((x - 1) * 2) + (y * 80 * 2)); + *screen++ = show? 251 : ' '; // Checkmark char or space + *screen = 0x48; + if (show && hilight) + { + for (w++;w--;screen += 2) + *screen = 0x4f; + } +} + +/////////////////////////////////////////////////////////////////////////// +// +// USL_ShowMem() - Right justifies a longword in one of the memory fields on +// the text screen +// +/////////////////////////////////////////////////////////////////////////// +static void +USL_ShowMem(word x,word y,long mem) +{ + char buf[16]; + word i; + + for (i = strlen(ltoa(mem,buf,10));i < 5;i++) + USL_ScreenDraw(x++,y," ",0x48); + USL_ScreenDraw(x,y,buf,0x48); +} + +/////////////////////////////////////////////////////////////////////////// +// +// US_UpdateTextScreen() - Called after the ID libraries are started up. +// Displays what hardware is present. +// +/////////////////////////////////////////////////////////////////////////// +void +US_UpdateTextScreen(void) +{ + boolean b; + byte far *screen; + word i; + longword totalmem; + + // Show video card info + b = (grmode == CGAGR); + USL_Show(21,7,4,(videocard >= CGAcard) && (videocard <= VGAcard),b); + b = (grmode == EGAGR); + USL_Show(21,8,4,(videocard >= EGAcard) && (videocard <= VGAcard),b); + b = (grmode == VGAGR); + USL_Show(21,9,4,videocard == VGAcard,b); + if (compatability) + USL_ScreenDraw(5,10,"SVGA Compatibility Mode Enabled.",0x4f); + + // Show input device info + USL_Show(60,7,8,true,true); + USL_Show(60,8,11,JoysPresent[0],true); + USL_Show(60,9,11,JoysPresent[1],true); + USL_Show(60,10,5,MousePresent,true); + + // Show sound hardware info + USL_Show(21,14,11,true,SoundMode == sdm_PC); + b = (SoundMode == sdm_AdLib) || (MusicMode == smm_AdLib); + USL_Show(21,15,5,AdLibPresent && !SoundBlasterPresent, + b && !SoundBlasterPresent); + USL_Show(21,16,13,SoundBlasterPresent, + SoundBlasterPresent && (b || (SoundMode == sdm_SoundBlaster))); + USL_Show(21,17,13,SoundSourcePresent,SoundMode == sdm_SoundSource); + + // Show memory available/used + USL_ShowMem(63,15,mminfo.mainmem / 1024); + USL_Show(53,15,23,true,true); + USL_ShowMem(63,16,mminfo.EMSmem / 1024); + USL_Show(53,16,23,mminfo.EMSmem? true : false,true); + USL_ShowMem(63,17,mminfo.XMSmem / 1024); + USL_Show(53,17,23,mminfo.XMSmem? true : false,true); + totalmem = mminfo.mainmem + mminfo.EMSmem + mminfo.XMSmem; + USL_ShowMem(63,18,totalmem / 1024); + screen = MK_FP(0xb800,1 + (((63 - 1) * 2) + (18 * 80 * 2))); + for (i = 0;i < 13;i++,screen += 2) + *screen = 0x4f; + + // Change Initializing... to Loading... + USL_ScreenDraw(27,22," Loading... ",0x9c); +} + +/////////////////////////////////////////////////////////////////////////// +// +// US_FinishTextScreen() - After the main program has finished its initial +// loading, this routine waits for a keypress and then clears the screen +// +/////////////////////////////////////////////////////////////////////////// +void +US_FinishTextScreen(void) +{ + // Change Loading... to Press a Key + USL_ScreenDraw(29,22," Ready - Press a Key ",0x9a); + + if (!(tedlevel || NoWait)) + { + IN_ClearKeysDown(); + IN_Ack(); + } + IN_ClearKeysDown(); + + USL_ClearTextScreen(); +} + +// Window/Printing routines + +/////////////////////////////////////////////////////////////////////////// +// +// US_SetPrintRoutines() - Sets the routines used to measure and print +// from within the User Mgr. Primarily provided to allow switching +// between masked and non-masked fonts +// +/////////////////////////////////////////////////////////////////////////// +void +US_SetPrintRoutines(void (*measure)(char far *,word *,word *),void (*print)(char far *)) +{ + USL_MeasureString = measure; + USL_DrawString = print; +} + +/////////////////////////////////////////////////////////////////////////// +// +// US_Print() - Prints a string in the current window. Newlines are +// supported. +// +/////////////////////////////////////////////////////////////////////////// +void +US_Print(char *s) +{ + char c,*se; + word w,h; + + while (*s) + { + se = s; + while ((c = *se) && (c != '\n')) + se++; + *se = '\0'; + + USL_MeasureString(s,&w,&h); + px = PrintX; + py = PrintY; + USL_DrawString(s); + + s = se; + if (c) + { + *se = c; + s++; + + PrintX = WindowX; + PrintY += h; + } + else + PrintX += w; + } +} + +/////////////////////////////////////////////////////////////////////////// +// +// US_PrintUnsigned() - Prints an unsigned long +// +/////////////////////////////////////////////////////////////////////////// +void +US_PrintUnsigned(longword n) +{ + char buffer[32]; + + US_Print(ultoa(n,buffer,10)); +} + +/////////////////////////////////////////////////////////////////////////// +// +// US_PrintSigned() - Prints a signed long +// +/////////////////////////////////////////////////////////////////////////// +void +US_PrintSigned(long n) +{ + char buffer[32]; + + US_Print(ltoa(n,buffer,10)); +} + +/////////////////////////////////////////////////////////////////////////// +// +// USL_PrintInCenter() - Prints a string in the center of the given rect +// +/////////////////////////////////////////////////////////////////////////// +static void +USL_PrintInCenter(char *s,Rect r) +{ + word w,h, + rw,rh; + + USL_MeasureString(s,&w,&h); + rw = r.lr.x - r.ul.x; + rh = r.lr.y - r.ul.y; + + px = r.ul.x + ((rw - w) / 2); + py = r.ul.y + ((rh - h) / 2); + USL_DrawString(s); +} + +/////////////////////////////////////////////////////////////////////////// +// +// US_PrintCentered() - Prints a string centered in the current window. +// +/////////////////////////////////////////////////////////////////////////// +void +US_PrintCentered(char *s) +{ + Rect r; + + r.ul.x = WindowX; + r.ul.y = WindowY; + r.lr.x = r.ul.x + WindowW; + r.lr.y = r.ul.y + WindowH; + + USL_PrintInCenter(s,r); +} + +/////////////////////////////////////////////////////////////////////////// +// +// US_CPrintLine() - Prints a string centered on the current line and +// advances to the next line. Newlines are not supported. +// +/////////////////////////////////////////////////////////////////////////// +void +US_CPrintLine(char *s) +{ + word w,h; + + USL_MeasureString(s,&w,&h); + + if (w > WindowW) + Quit("US_CPrintLine() - String exceeds width"); + px = WindowX + ((WindowW - w) / 2); + py = PrintY; + USL_DrawString(s); + PrintY += h; +} + +/////////////////////////////////////////////////////////////////////////// +// +// US_CPrint() - Prints a string in the current window. Newlines are +// supported. +// +/////////////////////////////////////////////////////////////////////////// +void +US_CPrint(char *s) +{ + char c,*se; + word w,h; + + while (*s) + { + se = s; + while ((c = *se) && (c != '\n')) + se++; + *se = '\0'; + + US_CPrintLine(s); + + s = se; + if (c) + { + *se = c; + s++; + } + } +} + +/////////////////////////////////////////////////////////////////////////// +// +// US_ClearWindow() - Clears the current window to white and homes the +// cursor +// +/////////////////////////////////////////////////////////////////////////// +void +US_ClearWindow(void) +{ + VWB_Bar(WindowX,WindowY,WindowW,WindowH,WHITE); + PrintX = WindowX; + PrintY = WindowY; +} + +/////////////////////////////////////////////////////////////////////////// +// +// US_DrawWindow() - Draws a frame and sets the current window parms +// +/////////////////////////////////////////////////////////////////////////// +void +US_DrawWindow(word x,word y,word w,word h) +{ + word i, + sx,sy,sw,sh; + + WindowX = x * 8; + WindowY = y * 8; + WindowW = w * 8; + WindowH = h * 8; + + PrintX = WindowX; + PrintY = WindowY; + + sx = (x - 1) * 8; + sy = (y - 1) * 8; + sw = (w + 1) * 8; + sh = (h + 1) * 8; + + US_ClearWindow(); + + VWB_DrawTile8M(sx,sy,0),VWB_DrawTile8M(sx,sy + sh,6); + for (i = sx + 8;i <= sx + sw - 8;i += 8) + VWB_DrawTile8M(i,sy,1),VWB_DrawTile8M(i,sy + sh,7); + VWB_DrawTile8M(i,sy,2),VWB_DrawTile8M(i,sy + sh,8); + + for (i = sy + 8;i <= sy + sh - 8;i += 8) + VWB_DrawTile8M(sx,i,3),VWB_DrawTile8M(sx + sw,i,5); +} + +/////////////////////////////////////////////////////////////////////////// +// +// US_CenterWindow() - Generates a window of a given width & height in the +// middle of the screen +// +/////////////////////////////////////////////////////////////////////////// +void +US_CenterWindow(word w,word h) +{ + US_DrawWindow(((MaxX / 8) - w) / 2,((MaxY / 8) - h) / 2,w,h); +} + +/////////////////////////////////////////////////////////////////////////// +// +// US_CenterSaveWindow() - Generates a window of a given width & height in +// the middle of the screen, saving the background +// +/////////////////////////////////////////////////////////////////////////// +void +US_CenterSaveWindow(word w,word h,memptr *save) +{ + word x,y, + screen; + + x = ((MaxX / 8) - w) / 2; + y = ((MaxY / 8) - h) / 2; + MM_GetPtr(save,(w * h) * CHARWIDTH); + screen = bufferofs + panadjust + ylookup[y] + (x * CHARWIDTH); + VW_ScreenToMem(screen,*save,w * CHARWIDTH,h); + US_DrawWindow(((MaxX / 8) - w) / 2,((MaxY / 8) - h) / 2,w,h); +} + +/////////////////////////////////////////////////////////////////////////// +// +// US_RestoreSaveWindow() - Restores the background of the size of the +// current window from the memory specified by save +// +/////////////////////////////////////////////////////////////////////////// +void +US_RestoreSaveWindow(memptr *save) +{ + word screen; + + screen = bufferofs + panadjust + ylookup[WindowY] + (WindowX * CHARWIDTH); + VW_MemToScreen(*save,screen,WindowW * CHARWIDTH,WindowH); + MM_FreePtr(save); +} + +/////////////////////////////////////////////////////////////////////////// +// +// US_SaveWindow() - Saves the current window parms into a record for +// later restoration +// +/////////////////////////////////////////////////////////////////////////// +void +US_SaveWindow(WindowRec *win) +{ + win->x = WindowX; + win->y = WindowY; + win->w = WindowW; + win->h = WindowH; + + win->px = PrintX; + win->py = PrintY; +} + +/////////////////////////////////////////////////////////////////////////// +// +// US_RestoreWindow() - Sets the current window parms to those held in the +// record +// +/////////////////////////////////////////////////////////////////////////// +void +US_RestoreWindow(WindowRec *win) +{ + WindowX = win->x; + WindowY = win->y; + WindowW = win->w; + WindowH = win->h; + + PrintX = win->px; + PrintY = win->py; +} + +// Cursor routines + +/////////////////////////////////////////////////////////////////////////// +// +// US_StartCursor() - Sets up the cursor for User Mgr use +// +/////////////////////////////////////////////////////////////////////////// +void +US_StartCursor(void) +{ + CursorInfo info; + + VW_SetCursor(CURSORARROWSPR); + CursorX = MaxX / 2; + CursorY = MaxY / 2; + VW_MoveCursor(CursorX,CursorY); + VW_ShowCursor(); + + IN_ReadCursor(&info); // Dispose of any accumulated movement +} + +/////////////////////////////////////////////////////////////////////////// +// +// US_ShutCursor() - Cleans up after US_StartCursor() +// +/////////////////////////////////////////////////////////////////////////// +void +US_ShutCursor(void) +{ + VW_HideCursor(); +} + +/////////////////////////////////////////////////////////////////////////// +// +// US_UpdateCursor() - Gets the new cursor position & button states from +// the Input Mgr and tells the View Mgr where the cursor is +// +/////////////////////////////////////////////////////////////////////////// +boolean +US_UpdateCursor(void) +{ + CursorInfo info; + + IN_ReadCursor(&info); + if (info.x || info.y || CursorBad) + { + CursorX += info.x; + if (CursorX >= MaxX) + CursorX = MaxX - 1; + else if (CursorX < 0) + CursorX = 0; + + CursorY += info.y; + if (CursorY >= MaxY) + CursorY = MaxY - 1; + else if (CursorY < 0) + CursorY = 0; + + VW_MoveCursor(CursorX,CursorY); + CursorBad = false; + } + Button0 = info.button0; + Button1 = info.button1; + return(Button0 || Button1); +} + +// Input routines + +/////////////////////////////////////////////////////////////////////////// +// +// USL_XORICursor() - XORs the I-bar text cursor. Used by US_LineInput() +// +/////////////////////////////////////////////////////////////////////////// +static void +USL_XORICursor(int x,int y,char *s,word cursor) +{ + char buf[MaxString]; + word w,h; + + strcpy(buf,s); + buf[cursor] = '\0'; + USL_MeasureString(buf,&w,&h); + + px = x + w - 1; + py = y; + USL_DrawString("\x80"); +} + +/////////////////////////////////////////////////////////////////////////// +// +// US_LineInput() - Gets a line of user input at (x,y), the string defaults +// to whatever is pointed at by def. Input is restricted to maxchars +// chars or maxwidth pixels wide. If the user hits escape (and escok is +// true), nothing is copied into buf, and false is returned. If the +// user hits return, the current string is copied into buf, and true is +// returned +// +/////////////////////////////////////////////////////////////////////////// +boolean +US_LineInput(int x,int y,char *buf,char *def,boolean escok, + int maxchars,int maxwidth) +{ + boolean redraw, + cursorvis,cursormoved, + done,result; + ScanCode sc; + char c, + s[MaxString],olds[MaxString]; + word i, + cursor, + w,h, + len; + longword lasttime; + + VW_HideCursor(); + + if (def) + strcpy(s,def); + else + *s = '\0'; + *olds = '\0'; + cursor = strlen(s); + cursormoved = redraw = true; + + cursorvis = done = false; + lasttime = TimeCount; + LastASCII = key_None; + LastScan = sc_None; + + while (!done) + { + if (cursorvis) + USL_XORICursor(x,y,s,cursor); + + asm pushf + asm cli + + sc = LastScan; + LastScan = sc_None; + c = LastASCII; + LastASCII = key_None; + + asm popf + + switch (sc) + { + case sc_LeftArrow: + if (cursor) + cursor--; + c = key_None; + cursormoved = true; + break; + case sc_RightArrow: + if (s[cursor]) + cursor++; + c = key_None; + cursormoved = true; + break; + case sc_Home: + cursor = 0; + c = key_None; + cursormoved = true; + break; + case sc_End: + cursor = strlen(s); + c = key_None; + cursormoved = true; + break; + + case sc_Return: + strcpy(buf,s); + done = true; + result = true; + c = key_None; + break; + case sc_Escape: + if (escok) + { + done = true; + result = false; + } + c = key_None; + break; + + case sc_BackSpace: + if (cursor) + { + strcpy(s + cursor - 1,s + cursor); + cursor--; + redraw = true; + } + c = key_None; + cursormoved = true; + break; + case sc_Delete: + if (s[cursor]) + { + strcpy(s + cursor,s + cursor + 1); + redraw = true; + } + c = key_None; + cursormoved = true; + break; + + case 0x4c: // Keypad 5 + case sc_UpArrow: + case sc_DownArrow: + case sc_PgUp: + case sc_PgDn: + case sc_Insert: + c = key_None; + break; + } + + if (c) + { + len = strlen(s); + USL_MeasureString(s,&w,&h); + + if + ( + isprint(c) + && (len < MaxString - 1) + && ((!maxchars) || (len < maxchars)) + && ((!maxwidth) || (w < maxwidth)) + ) + { + for (i = len + 1;i > cursor;i--) + s[i] = s[i - 1]; + s[cursor++] = c; + redraw = true; + } + } + + if (redraw) + { + px = x; + py = y; + USL_DrawString(olds); + strcpy(olds,s); + + px = x; + py = y; + USL_DrawString(s); + + redraw = false; + } + + if (cursormoved) + { + cursorvis = false; + lasttime = TimeCount - TickBase; + + cursormoved = false; + } + if (TimeCount - lasttime > TickBase / 2) + { + lasttime = TimeCount; + + cursorvis ^= true; + } + if (cursorvis) + USL_XORICursor(x,y,s,cursor); + + VW_UpdateScreen(); + } + + if (cursorvis) + USL_XORICursor(x,y,s,cursor); + if (!result) + { + px = x; + py = y; + USL_DrawString(olds); + } + VW_ShowCursor(); + VW_UpdateScreen(); + + IN_ClearKeysDown(); + return(result); +} + +// Control panel routines + +static boolean FlushHelp; +static WindowRec HelpWindow,BottomWindow; +typedef enum + { + uic_Draw,uic_Hit + } UserCall; +typedef enum + { + uii_Bad,uii_Button,uii_RadioButton,uii_CheckBox,uii_KeyCap + } UIType; +#define ui_Normal 0 +#define ui_Selected 1 +#define ui_Disabled 2 + + // Prototype the custom routines +static boolean USL_CtlButtonCustom(UserCall,word,word), + USL_CtlPButtonCustom(UserCall,word,word), + USL_CtlPSButtonCustom(UserCall,word,word), + USL_CtlPRButtonCustom(UserCall,word,word), + USL_CtlHButtonCustom(UserCall,word,word), + USL_CtlDButtonCustom(UserCall,word,word), + USL_CtlDEButtonCustom(UserCall,word,word), + USL_CtlDLButtonCustom(UserCall,word,word), + USL_CtlDSButtonCustom(UserCall,word,word), + USL_CtlSButtonCustom(UserCall,word,word), + USL_CtlCButtonCustom(UserCall,word,word), + USL_CtlCKbdButtonCustom(UserCall,word,word), + USL_CtlCJoyButtonCustom(UserCall,word,word); + + // The structure of a user interaction item +typedef struct { + Rect r; // The enclosing rectangle + UIType type; // The type of item + int picup,picdown; // What to draw when up/down + char *help; // Floating help string + ScanCode key; // Key equiv + word sel; // Interaction flags (ui_XXX) + boolean (*custom)(UserCall,word,word); // Custom routine + char *text; // Text for some items + } UserItem; +typedef struct { + ScanCode key; + word i,n, // Hit CtlPanels2[i][n] + toi,ton; // Move to CtlPanels2[toi][ton] + } HotKey; // MARK + +static ScanCode *KeyMaps[] = + { + &KbdDefs[0].button0,&KbdDefs[0].button1, + &KbdDefs[0].upleft,&KbdDefs[0].up,&KbdDefs[0].upright, + &KbdDefs[0].left, &KbdDefs[0].right, + &KbdDefs[0].downleft,&KbdDefs[0].down,&KbdDefs[0].downright, + }; + +// Some macros to make rectangle definition quite a bit less unpleasant +#define CtlPanelX 8 +#define CtlPanelY 4 +#define CtlPanel2X (8*8) +#define CtlPanel2Y (2*8) +#define CtlPanel3X (8*8) +#define CtlPanel3Y (7*8) + +#define CtlPanelR(n) { CtlPanelX,CtlPanelY+(32 * (n)),\ + CtlPanelX+40,CtlPanelY+(32 * (n)) + 32} +#define CtlPanel2R(x,y) { CtlPanel2X+(x)*8,CtlPanel2Y+(y)*8,\ + CtlPanel2X+32+(x)*8,CtlPanel2Y+24+(y)*8} +#define CtlPanel3R(x,y) { CtlPanel3X+(x)*8,CtlPanel3Y+(y)*8,\ + CtlPanel3X+32+(x)*8,CtlPanel3Y+24+(y)*8} +static UserItem CtlPanels[] = + { +{CtlPanelR(0),uii_RadioButton,CTL_STARTUPPIC,CTL_STARTDNPIC,"Start or Resume a Game",sc_None,ui_Normal,USL_CtlButtonCustom}, +{CtlPanelR(1),uii_RadioButton,CTL_HELPUPPIC,CTL_HELPDNPIC,"Get Help With Commander Keen",sc_None,ui_Normal,USL_CtlButtonCustom}, +{CtlPanelR(2),uii_RadioButton,CTL_DISKUPPIC,CTL_DISKDNPIC,"Load / Save / Quit",sc_None,ui_Normal,USL_CtlButtonCustom}, +{CtlPanelR(3),uii_RadioButton,CTL_CONTROLSUPPIC,CTL_CONTROLSDNPIC,"Choose Controls",sc_C,ui_Normal,USL_CtlButtonCustom}, +{CtlPanelR(4),uii_RadioButton,CTL_SOUNDUPPIC,CTL_SOUNDDNPIC,"Select Sound Device",sc_F2,ui_Normal,USL_CtlButtonCustom}, +{CtlPanelR(5),uii_RadioButton,CTL_MUSICUPPIC,CTL_MUSICDNPIC,"Turn Music On / Off",sc_F7,ui_Normal,USL_CtlButtonCustom}, +{-1,-1,-1,-1,uii_Bad} + }, + CtlPPanels[] = + { +{CtlPanel2R(10,0),uii_RadioButton,CTL_P_NEWGAMEUPPIC,CTL_P_NEWGAMEDNPIC,"Choose Difficulty for the New Game",sc_F5,ui_Normal,USL_CtlPButtonCustom}, +{CtlPanel2R(15,0),uii_RadioButton,CTL_P_RESUMEUPPIC,CTL_P_RESUMEDNPIC,"Go Back to Current Game",sc_None,ui_Normal,USL_CtlPButtonCustom}, +{-1,-1,-1,-1,uii_Bad} + }, + CtlPSPanels[] = + { +{CtlPanel3R(13,5),uii_Button,CTL_P_MEDUPPIC,CTL_P_MEDDNPIC,"Start New Game in Normal Mode",sc_None,ui_Normal,USL_CtlPSButtonCustom}, +{CtlPanel3R(8,5),uii_Button,CTL_P_EASYUPPIC,CTL_P_EASYDNPIC,"Start New Game in Easy Mode",sc_None,ui_Normal,USL_CtlPSButtonCustom}, +{CtlPanel3R(18,5),uii_Button,CTL_P_HARDUPPIC,CTL_P_HARDDNPIC,"Start New Game in Hard Mode",sc_None,ui_Normal,USL_CtlPSButtonCustom}, +{-1,-1,-1,-1,uii_Bad} + }, + CtlPRPanels[] = + { +{CtlPanel3R(13,5),uii_Button,CTL_P_GORESUMEUPPIC,CTL_P_GORESUMEDNPIC,"Resume Current Game",sc_None,ui_Normal,USL_CtlPRButtonCustom}, +{-1,-1,-1,-1,uii_Bad} + }, + CtlHPanels[] = + { +{CtlPanel2R(8,0),uii_Button,CTL_H_LOSTUPPIC,CTL_H_LOSTDNPIC,"Help Me, I'm Lost!",sc_F1,ui_Normal,USL_CtlHButtonCustom}, +{CtlPanel2R(13,0),uii_Button,CTL_H_CTRLUPPIC,CTL_H_CTRLDNPIC,"Get Help with Controls",sc_None,ui_Normal,USL_CtlHButtonCustom}, +{CtlPanel2R(18,0),uii_Button,CTL_H_STORYUPPIC,CTL_H_STORYDNPIC,"Read Story & Game Tips",sc_None,ui_Normal,USL_CtlHButtonCustom}, +{-1,-1,-1,-1,uii_Bad} + }, + CtlDPanels[] = + { +{CtlPanel2R(9,0),uii_RadioButton,CTL_D_LSGAMEUPPIC,CTL_D_LSGAMEDNPIC,"Load or Save a Game",sc_F6,ui_Normal,USL_CtlDButtonCustom}, +{CtlPanel2R(15,0),uii_RadioButton,CTL_D_DOSUPPIC,CTL_D_DOSDNPIC,"Exit to DOS",sc_Q,ui_Normal,USL_CtlDButtonCustom}, +{-1,-1,-1,-1,uii_Bad} + }, + CtlDLSPanels[] = + { +#define CtlPanel3LSR(x,y) { CtlPanel3X+(x)*8,CtlPanel3Y+(y)*8,\ + CtlPanel3X+32+(x)*8,CtlPanel3Y+16+(y)*8} +{CtlPanel3LSR(1,0),uii_Button,CTL_D_LOADUPPIC,CTL_D_LOADDNPIC,"Load This Game",sc_None,ui_Normal,USL_CtlDLButtonCustom}, +{CtlPanel3LSR(6,0),uii_Button,CTL_D_SAVEUPPIC,CTL_D_SAVEDNPIC,"Save Current Game Here",sc_None,ui_Normal,USL_CtlDSButtonCustom}, +{CtlPanel3LSR(1,2),uii_Button,CTL_D_LOADUPPIC,CTL_D_LOADDNPIC,"Load This Game",sc_None,ui_Normal,USL_CtlDLButtonCustom}, +{CtlPanel3LSR(6,2),uii_Button,CTL_D_SAVEUPPIC,CTL_D_SAVEDNPIC,"Save Current Game Here",sc_None,ui_Normal,USL_CtlDSButtonCustom}, +{CtlPanel3LSR(1,4),uii_Button,CTL_D_LOADUPPIC,CTL_D_LOADDNPIC,"Load This Game",sc_None,ui_Normal,USL_CtlDLButtonCustom}, +{CtlPanel3LSR(6,4),uii_Button,CTL_D_SAVEUPPIC,CTL_D_SAVEDNPIC,"Save Current Game Here",sc_None,ui_Normal,USL_CtlDSButtonCustom}, +{CtlPanel3LSR(1,6),uii_Button,CTL_D_LOADUPPIC,CTL_D_LOADDNPIC,"Load This Game",sc_None,ui_Normal,USL_CtlDLButtonCustom}, +{CtlPanel3LSR(6,6),uii_Button,CTL_D_SAVEUPPIC,CTL_D_SAVEDNPIC,"Save Current Game Here",sc_None,ui_Normal,USL_CtlDSButtonCustom}, +{CtlPanel3LSR(1,8),uii_Button,CTL_D_LOADUPPIC,CTL_D_LOADDNPIC,"Load This Game",sc_None,ui_Normal,USL_CtlDLButtonCustom}, +{CtlPanel3LSR(6,8),uii_Button,CTL_D_SAVEUPPIC,CTL_D_SAVEDNPIC,"Save Current Game Here",sc_None,ui_Normal,USL_CtlDSButtonCustom}, +{CtlPanel3LSR(1,10),uii_Button,CTL_D_LOADUPPIC,CTL_D_LOADDNPIC,"Load This Game",sc_None,ui_Normal,USL_CtlDLButtonCustom}, +{CtlPanel3LSR(6,10),uii_Button,CTL_D_SAVEUPPIC,CTL_D_SAVEDNPIC,"Save Current Game Here",sc_None,ui_Normal,USL_CtlDSButtonCustom}, +{CtlPanel3LSR(1,12),uii_Button,CTL_D_LOADUPPIC,CTL_D_LOADDNPIC,"Load This Game",sc_None,ui_Normal,USL_CtlDLButtonCustom}, +{CtlPanel3LSR(6,12),uii_Button,CTL_D_SAVEUPPIC,CTL_D_SAVEDNPIC,"Save Current Game Here",sc_None,ui_Normal,USL_CtlDSButtonCustom}, +{-1,-1,-1,-1,uii_Bad} + }, + CtlDEPanels[] = + { +#define CtlPanel3ER(x,y) { CtlPanel3X+(x)*8,CtlPanel3Y+(y)*8,\ + CtlPanel3X+40+(x)*8,CtlPanel3Y+24+(y)*8} +{CtlPanel3ER(12,5),uii_Button,CTL_D_EXITUPPIC,CTL_D_EXITDNPIC,"Really Exit to DOS",sc_None,ui_Normal,USL_CtlDEButtonCustom}, +{-1,-1,-1,-1,uii_Bad} + }, + CtlCPanels[] = + { +{CtlPanel2R(8,0),uii_RadioButton,CTL_C_KBDUPPIC,CTL_C_KBDDNPIC,"Use / Configure Keyboard",sc_F3,ui_Normal,USL_CtlCButtonCustom}, +{CtlPanel2R(13,0),uii_RadioButton,CTL_C_JOY1UPPIC,CTL_C_JOY1DNPIC,"Use / Configure Joystick 1",sc_None,ui_Normal,USL_CtlCButtonCustom}, +{CtlPanel2R(18,0),uii_RadioButton,CTL_C_JOY2UPPIC,CTL_C_JOY2DNPIC,"Use / Configure Joystick 2",sc_None,ui_Normal,USL_CtlCButtonCustom}, +{-1,-1,-1,-1,uii_Bad} + }, +#define CtlPanelKC3R(x,y) { CtlPanel3X+(x)*8,CtlPanel3Y+(y)*8,\ + CtlPanel3X+56+(x)*8,CtlPanel3Y+32+(y)*8} + CtlCKbdPanels[] = + { +{CtlPanelKC3R(1,2),uii_KeyCap,CTL_KEYCAPPIC,CTL_KEYCAPCURPIC,"Define Key for Jumping",sc_None,ui_Normal,USL_CtlCKbdButtonCustom}, +{CtlPanelKC3R(1,6),uii_KeyCap,CTL_KEYCAPPIC,CTL_KEYCAPCURPIC,"Define Key for Throwing",sc_None,ui_Normal,USL_CtlCKbdButtonCustom}, +{CtlPanelKC3R(8,0),uii_KeyCap,CTL_KEYCAPPIC,CTL_KEYCAPCURPIC,"Define Key to move Up & Left",sc_None,ui_Normal,USL_CtlCKbdButtonCustom}, +{CtlPanelKC3R(15,0),uii_KeyCap,CTL_KEYCAPPIC,CTL_KEYCAPCURPIC,"Define Key to move Up",sc_None,ui_Normal,USL_CtlCKbdButtonCustom}, +{CtlPanelKC3R(22,0),uii_KeyCap,CTL_KEYCAPPIC,CTL_KEYCAPCURPIC,"Define Key to move Up & Right",sc_None,ui_Normal,USL_CtlCKbdButtonCustom}, +{CtlPanelKC3R(8,4),uii_KeyCap,CTL_KEYCAPPIC,CTL_KEYCAPCURPIC,"Define Key to move Left",sc_None,ui_Normal,USL_CtlCKbdButtonCustom}, +{CtlPanelKC3R(22,4),uii_KeyCap,CTL_KEYCAPPIC,CTL_KEYCAPCURPIC,"Define Key to move Right",sc_None,ui_Normal,USL_CtlCKbdButtonCustom}, +{CtlPanelKC3R(8,8),uii_KeyCap,CTL_KEYCAPPIC,CTL_KEYCAPCURPIC,"Define Key to move Down & Left",sc_None,ui_Normal,USL_CtlCKbdButtonCustom}, +{CtlPanelKC3R(15,8),uii_KeyCap,CTL_KEYCAPPIC,CTL_KEYCAPCURPIC,"Define Key to move Down",sc_None,ui_Normal,USL_CtlCKbdButtonCustom}, +{CtlPanelKC3R(22,8),uii_KeyCap,CTL_KEYCAPPIC,CTL_KEYCAPCURPIC,"Define Key to move Down & Right",sc_None,ui_Normal,USL_CtlCKbdButtonCustom}, +{-1,-1,-1,-1,uii_Bad} + }, + CtlCJoyPanels[] = + { +{CtlPanel3R(13,5),uii_Button,CTL_C_CALIBRATEUPPIC,CTL_C_CALIBRATEDNPIC,"Configure Joystick",sc_None,ui_Normal,USL_CtlCJoyButtonCustom}, +{-1,-1,-1,-1,uii_Bad} + }, + CtlSPanels[] = + { +{CtlPanel2R(3,0),uii_RadioButton,CTL_S_NOSNDUPPIC,CTL_S_NOSNDDNPIC,"Turn Sound Off",sc_None,ui_Normal,USL_CtlSButtonCustom}, +{CtlPanel2R(8,0),uii_RadioButton,CTL_S_PCSNDUPPIC,CTL_S_PCSNDDNPIC,"Use PC Speaker",sc_None,ui_Normal,USL_CtlSButtonCustom}, +{CtlPanel2R(13,0),uii_RadioButton,CTL_S_ADLIBUPPIC,CTL_S_ADLIBDNPIC,"Use AdLib Sound Effects",sc_None,ui_Normal,USL_CtlSButtonCustom}, +{CtlPanel2R(18,0),uii_RadioButton,CTL_S_SNDBLUPPIC,CTL_S_SNDBLDNPIC,"Use SoundBlaster Sound Effects",sc_None,ui_Normal,USL_CtlSButtonCustom}, +{CtlPanel2R(23,0),uii_RadioButton,CTL_S_SNDSRCUPPIC,CTL_S_SNDSRCDNPIC,"Use Sound Source Sound Effects",sc_None,ui_Normal,USL_CtlSButtonCustom}, +{-1,-1,-1,-1,uii_Bad} + }, + CtlSSSPanels[] = + { +{CtlPanel3R(7,2),uii_CheckBox,CTL_CHECKUPPIC,CTL_CHECKDNPIC,"Turn Tandy Mode On / Off",sc_None,ui_Normal,0,"Tandy Mode"}, +{CtlPanel3R(7,6),uii_CheckBox,CTL_CHECKUPPIC,CTL_CHECKDNPIC,"Switch between LPT1 & LPT2",sc_None,ui_Normal,0,"Use LPT2"}, +{-1,-1,-1,-1,uii_Bad} + }, + CtlMPanels[] = + { +{CtlPanel2R(9,0),uii_RadioButton,CTL_M_NOMUSUPPIC,CTL_M_NOMUSDNPIC,"Background Music Off"}, +{CtlPanel2R(15,0),uii_RadioButton,CTL_M_ADLIBUPPIC,CTL_M_ADLIBDNPIC,"Use AdLib/SoundBlaster Music"}, +{-1,-1,-1,-1,uii_Bad} + }, + *CtlPanels2[] = + { + CtlPPanels, // Start + CtlHPanels, // Help + CtlDPanels, // Disk + CtlCPanels, // Controls + CtlSPanels, // Sound + CtlMPanels // Music + }, + *TheItems[4] = {CtlPanels}; +static int CtlPanelButton; + +/////////////////////////////////////////////////////////////////////////// +// +// USL_TurnOff() - Goes through a list of UserItems and sets them all to +// the normal state +// +/////////////////////////////////////////////////////////////////////////// +static void +USL_TurnOff(UserItem *ip) +{ + while (ip->type != uii_Bad) + { + ip->sel = ui_Normal; + ip++; + } +} + +/////////////////////////////////////////////////////////////////////////// +// +// USL_FindDown() - Finds which UserItem, if any, is selected in the given +// list +// +/////////////////////////////////////////////////////////////////////////// +static int +USL_FindDown(UserItem *ip) +{ + int i; + + for (i = 0;ip->type != uii_Bad;i++,ip++) + if (ip->sel & ui_Selected) + return(i); + return(-1); +} + +/////////////////////////////////////////////////////////////////////////// +// +// USL_ShowHelp() - Shows the specified string in the help window +// +/////////////////////////////////////////////////////////////////////////// +static void +USL_ShowHelp(char *s) +{ + WindowRec wr; + + if (!s) + return; + + US_SaveWindow(&wr); + US_RestoreWindow(&HelpWindow); + + US_ClearWindow(); + US_PrintCentered(s); + + US_RestoreWindow(&wr); +} + +/////////////////////////////////////////////////////////////////////////// +// +// USL_HandleError() - Handles telling the user that there's been an error +// +/////////////////////////////////////////////////////////////////////////// +static void +USL_HandleError(int num) +{ + char buf[64]; + + strcpy(buf,"Error: "); + if (num < 0) + strcat(buf,"Unknown"); + else if (num == ENOMEM) + strcat(buf,"Disk is Full"); + else if (num == EINVFMT) + strcat(buf,"File is Incomplete"); + else + strcat(buf,sys_errlist[num]); + + VW_HideCursor(); + + fontcolor = F_SECONDCOLOR; + USL_ShowHelp(buf); + fontcolor = F_BLACK; + VW_UpdateScreen(); + + IN_ClearKeysDown(); + IN_Ack(); + + VW_ShowCursor(); + VW_UpdateScreen(); +} + +/////////////////////////////////////////////////////////////////////////// +// +// USL_DrawItem() - Draws a UserItem. If there's a custom routine, this will +// call it with a uic_Draw command. If the custom routine returns true, +// then the routine handled all of the drawing. If it returns false, +// then this routine does the default drawing. +// +/////////////////////////////////////////////////////////////////////////// +static void +USL_DrawItem(word hiti,word hitn) +{ + boolean handled,centered; + char *text; + word w,h; + int picup,picdown; + Rect r; + UserItem *ip; + + ip = &TheItems[hiti][hitn]; + if (ip->custom) + handled = ip->custom(uic_Draw,hiti,hitn); + else + handled = false; + + if (!handled) + { + picup = ip->picup; + picdown = ip->picdown; + switch (ip->type) + { + case uii_CheckBox: + px = ip->r.lr.x + 8; + py = ip->r.ul.y + 8; + text = ip->text; + centered = false; + break; + case uii_KeyCap: + if (!(ip->sel & ui_Selected)) + { + text = ip->text; + if (text) + { + r = ip->r; + centered = true; + } + } + else + text = nil; + break; + default: + text = nil; + break; + } + + VWB_DrawPic(ip->r.ul.x,ip->r.ul.y, + (ip->sel & ui_Selected)? picdown : picup); + if (text) + { + if (centered) + USL_PrintInCenter(text,r); + else + { + USL_MeasureString(text,&w,&h); + VWB_Bar(px,py,w + 7,h,WHITE); + USL_DrawString(text); + } + } + if (ip->sel & ui_Disabled) + { + if ((picup == CTL_D_LOADUPPIC) || (picup == CTL_D_SAVEUPPIC)) + VWB_DrawMPic(ip->r.ul.x,ip->r.ul.y,CTL_LSMASKPICM); + else + VWB_DrawMPic(ip->r.ul.x,ip->r.ul.y,CTL_LITTLEMASKPICM); + } + } +} + +/////////////////////////////////////////////////////////////////////////// +// +// USL_DoHit() - Handles a hit on a UserItem. If there's a custom routine, +// it will be called. If it returns true, then don't do anything +// more. If it returns false, then use the standard behaviour +// +/////////////////////////////////////////////////////////////////////////// +static void +USL_DoHit(word hiti,word hitn) +{ + boolean handled; + word i; + UserItem *ip; + + ip = &TheItems[hiti][hitn]; + if (ip->custom) + handled = ip->custom(uic_Hit,hiti,hitn); + else + handled = false; + + if (!handled) + { + if (TheItems[hiti][hitn].sel & ui_Disabled) + { + fontcolor = F_SECONDCOLOR; + USL_ShowHelp("This Item is Disabled"); + fontcolor = F_BLACK; + return; + } + + FlushHelp = true; + + switch (ip->type) + { + case uii_Button: + // Must have a custom routine to handle hits - this just redraws + ip->sel ^= ui_Selected; + USL_DrawItem(hiti,hitn); + case uii_CheckBox: + ip->sel ^= ui_Selected; + USL_DrawItem(hiti,hitn); + break; + case uii_RadioButton: + for (i = 0,ip = TheItems[hiti];ip->type != uii_Bad;i++,ip++) + { + if + ( + (i != hitn) + && (ip->type == uii_RadioButton) + && (ip->sel & ui_Selected) + ) + { + ip->sel &= ~ui_Selected; + USL_DrawItem(hiti,i); + } + } + TheItems[hiti][hitn].sel |= ui_Selected; + USL_DrawItem(hiti,hitn); + break; + case uii_KeyCap: + break; + } + } +} + +/////////////////////////////////////////////////////////////////////////// +// +// USL_IsInRect() - Checks to see if the coordinates given are within any +// of the Rects in the UserItem list. If so, returns true & sets the +// index & number for lookup. If not, returns false. +// +/////////////////////////////////////////////////////////////////////////// +static boolean +USL_IsInRect(word x,word y,word *index,word *number) +{ + UserItem *item,**items; + + items = TheItems; + *index = 0; + while (*items) + { + item = *items; + *number = 0; + while (item->type != uii_Bad) + { + if + ( + (x >= item->r.ul.x) + && (x < item->r.lr.x) + && (y >= item->r.ul.y) + && (y < item->r.lr.y) + ) + return(true); + (*number)++; + item++; + } + + (*index)++; + items++; + } + return(false); +} + +/////////////////////////////////////////////////////////////////////////// +// +// USL_TrackItem() - Tracks the given item. If the cursor is inside of the +// item, it's redrawn as down. If the cursor is outside, the item is +// drawn in its original state. Returns true if the button was released +// while the cursor was inside the item, or false if it wasn't. +// +/////////////////////////////////////////////////////////////////////////// +static boolean +USL_TrackItem(word hiti,word hitn) +{ + boolean inside,last; + word ini,inn, + on, + sel,othersel; + UserItem *ip,*op; + + ip = &TheItems[hiti][hitn]; + sel = ip->sel; + if (ip->type == uii_RadioButton) + { + inside = false; + for (op = TheItems[hiti],on = 0;op->type != uii_Bad;op++,on++) + { + if (op->sel & ui_Selected) + { + inside = true; + break; + } + } + if (!inside) + op = ip; + othersel = op->sel; + } + else + op = nil; + + if (ip->sel & ui_Disabled) + { + fontcolor = F_SECONDCOLOR; + USL_ShowHelp("This item is disabled"); + fontcolor = F_BLACK; + + while (US_UpdateCursor()) + VW_UpdateScreen(); + + FlushHelp = true; + return(false); + } + + last = false; + do + { + USL_IsInRect(CursorX,CursorY,&ini,&inn); + inside = (ini == hiti) && (inn == hitn); + if (inside != last) + { + if (inside) + { + if (op) + { + op->sel &= ~ui_Selected; + ip->sel |= ui_Selected; + } + else + ip->sel = sel ^ ui_Selected; + } + else + { + if (op && (op != ip)) + { + op->sel |= ui_Selected; + ip->sel &= ~ui_Selected; + } + else + ip->sel = sel; + } + + USL_DrawItem(hiti,hitn); + if (op && (op != ip)) + USL_DrawItem(hiti,on); + + last = inside; + } + VW_UpdateScreen(); + } while (US_UpdateCursor()); + + if (op) + op->sel = othersel; + ip->sel = sel; + if (!inside) + { + if (op && (op != ip)) + USL_DrawItem(hiti,on); + USL_DrawItem(hiti,hitn); + VW_UpdateScreen(); + } + + return(inside); +} + +/////////////////////////////////////////////////////////////////////////// +// +// USL_GlideCursor() - Smoothly moves the cursor to the given location +// +/////////////////////////////////////////////////////////////////////////// +static void +USL_GlideCursor(long newx,long newy) +{ + word steps; + long x,y, + dx,dy; + + if (grmode == CGAGR) + steps = 1; + else + steps = 8; + + x = (long)CursorX << 16; + dx = ((newx << 16) - x) / steps; + y = (long)CursorY << 16; + dy = ((newy << 16) - y) / steps; + + while ((CursorX != newx) || (CursorY != newy)) + { + x += dx; + y += dy; + + CursorX = x >> 16; + CursorY = y >> 16; + VW_MoveCursor(CursorX,CursorY); + VW_UpdateScreen(); + } + CursorBad = true; +} + +/////////////////////////////////////////////////////////////////////////// +// +// USL_FindRect() - Code so ugly you'll puke! Given a Rect and direction, +// this routine will try to find a UserItem to move the cursor to +// +/////////////////////////////////////////////////////////////////////////// +static void +USL_FindRect(Rect r,Motion xd,Motion yd) +{ + word i,i1,i2,i3; + Motion m1,m2; + Point diffs[9],diff,*dp; + Rect *rp,*good,*goods[9]; + UserItem *ip,**items; + + for (m1 = motion_Up,dp = diffs;m1 <= motion_Down;m1++) + { + for (m2 = motion_Left;m2 <= motion_Right;m2++,dp++) + { + dp->x = m2 * 1024; + dp->y = m1 * 1024; + } + } + for (i = 0;i < 9;i++) + goods[i] = nil; + + // Find out which octants all of the rects (except r) are in + for (items = TheItems;*items;items++) + { + for (ip = *items;ip->type != uii_Bad;ip++) + { + rp = &ip->r; + diff.x = rp->ul.x - r.ul.x; + diff.y = rp->ul.y - r.ul.y; + if (!(diff.x || diff.y)) + continue; + + if // 1,4,7 + ( + ((rp->ul.x >= r.ul.x) && (rp->ul.x < r.lr.x)) + || ((rp->lr.x > r.ul.x) && (rp->lr.x <= r.lr.x)) + ) + { + if (rp->lr.y <= r.ul.y) + { + if (!(goods[1] && (diff.y < diffs[1].y))) + { + goods[1] = rp; + diffs[1] = diff; + } + } + else if (rp->ul.y >= r.lr.y) + { + if (!(goods[7] && (diff.y > diffs[7].y))) + { + goods[7] = rp; + diffs[7] = diff; + } + } + } + + if // 3,4,5 + ( + ((rp->ul.y >= r.ul.y) && (rp->ul.y < r.lr.y)) + || ((rp->lr.y > r.ul.y) && (rp->lr.y <= r.lr.y)) + ) + { + if (rp->lr.x <= r.ul.x) + { + if (!(goods[3] && (diff.x < diffs[3].x))) + { + goods[3] = rp; + diffs[3] = diff; + } + } + else if (rp->ul.x >= r.lr.x) + { + if (!(goods[5] && (diff.x > diffs[5].x))) + { + goods[5] = rp; + diffs[5] = diff; + } + } + } + + if (rp->ul.x < r.ul.x) // 0,6 + { + if (rp->lr.y <= r.ul.y) + { + if + ( + (!goods[0]) + || (diff.y > diffs[0].y) + || (diff.x > diffs[6].x) + ) + { + goods[0] = rp; + diffs[0] = diff; + } + } + else if (rp->ul.y >= r.lr.y) + { + if + ( + (!goods[6]) + || (diff.y < diffs[6].y) + || (diff.x > diffs[6].x) + ) + { + goods[6] = rp; + diffs[6] = diff; + } + } + } + + if (rp->lr.x > r.lr.x) // 2,8 + { + if (rp->lr.y <= r.ul.y) + { + if + ( + (!goods[2]) + || (diff.y > diffs[2].y) + || (diff.x < diffs[2].x) + ) + { + goods[2] = rp; + diffs[2] = diff; + } + } + else if (rp->ul.y >= r.lr.y) + { + if + ( + (!goods[8]) + || (diff.y < diffs[8].y) + || (diff.x < diffs[8].x) + ) + { + goods[8] = rp; + diffs[8] = diff; + } + } + } + } + } + + switch (yd) + { + case motion_Up: + i1 = 1,i2 = 0,i3 = 2; + break; + case motion_None: + switch (xd) + { + case motion_Left: + i1 = 3,i2 = 0,i3 = 6; + break; + case motion_Right: + i1 = 5,i2 = 8,i3 = 2; + break; + } + break; + case motion_Down: + i1 = 7,i2 = 8,i3 = 6; + break; + } + + ( + (good = goods[i1]) + || (good = goods[i2]) + || (good = goods[i3]) + || (good = &r) + ); +#if 0 + CursorX = good->lr.x - 8; + CursorY = good->lr.y - 8; + CursorBad = true; + US_UpdateCursor(); +#endif + USL_GlideCursor(good->lr.x - 8,good->lr.y - 8); +} + +/////////////////////////////////////////////////////////////////////////// +// +// USL_CtlButtonCustom() - The custom routine for all of the Control Panel +// (leftmost) buttons. Clears all of the other item lists, clears the +// large area, and draws the line dividing the top and bottom areas. +// Then it sets up and draws the appropriate top row of icons. +// +/////////////////////////////////////////////////////////////////////////// +static boolean +USL_CtlButtonCustom(UserCall call,word i,word n) +{ + word j; + UserItem *ip; + + if (call != uic_Hit) + return(false); + + if (n == CtlPanelButton) + return(true); + + US_ClearWindow(); + for (j = 8;j < 38;j++) + { + VWB_DrawTile8M(j * 8,6 * 8,10); + VWB_DrawTile8M(j * 8,21 * 8,10); + } + VWB_DrawTile8M(7 * 8,6 * 8,9); + VWB_DrawTile8M(38 * 8,6 * 8,11); + VWB_DrawTile8M(7 * 8,21 * 8,9); + VWB_DrawTile8M(38 * 8,21 * 8,11); + + for (j = 1;j < 4;j++) + TheItems[j] = nil; + + // Set to new button + CtlPanelButton = n; + + // Draw new items + TheItems[1] = ip = CtlPanels2[CtlPanelButton]; + j = 0; + while (ip && (ip->type != uii_Bad)) + { + USL_DrawItem(i + 1,j); + if (ip->sel & ui_Selected) + USL_DoHit(i + 1,j); + j++; + ip++; + } + + return(false); +} + +/////////////////////////////////////////////////////////////////////////// +// +// USL_CtlCKbdButtonCustom() - The custom routine for the keyboard keycaps. +// This routine gets a scancode and puts it in the appropriate +// KbdDefs[0] member. +// +/////////////////////////////////////////////////////////////////////////// +static boolean +USL_CtlCKbdButtonCustom(UserCall call,word i,word n) +{ + boolean state; + word j; + ScanCode scan; + longword time; + UserItem *ip; + + if (call != uic_Hit) + return(false); + + ip = &TheItems[i][n]; + + fontcolor = F_SECONDCOLOR; + USL_ShowHelp(ip->help); + fontcolor = F_BLACK; + VW_HideCursor(); + VWB_DrawPic(ip->r.ul.x,ip->r.ul.y,ip->picdown); + VW_UpdateScreen(); + + LastScan = sc_None; + time = TimeCount; + state = true; + do + { + if (TimeCount - time > 35) // Half-second delays + { + state ^= true; + VWB_DrawPic(ip->r.ul.x,ip->r.ul.y,state? ip->picdown : ip->picup); + VW_UpdateScreen(); + time = TimeCount; + } + if (US_UpdateCursor()) + { + while (US_UpdateCursor()) + ; + scan = sc_Escape; + break; + } + + asm pushf + asm cli + if (LastScan == sc_LShift) + LastScan = sc_None; + asm popf + } while (!(scan = LastScan)); + IN_ClearKey(scan); + if (scan != sc_Escape) + { + for (j = 0,state = false;j < 10;j++) + { + if (j == n) + continue; + if (*(KeyMaps[j]) == scan) + { + state = true; + break; + } + } + if (state) + { + fontcolor = F_SECONDCOLOR; + USL_ShowHelp("That Key is Already Used!"); + fontcolor = F_BLACK; + } + else + { + ip->text = IN_GetScanName(scan); + *(KeyMaps[n]) = scan; + FlushHelp = true; + } + } + + USL_DrawItem(i,n); + VW_ShowCursor(); + VW_UpdateScreen(); + + return(true); +} + +/////////////////////////////////////////////////////////////////////////// +// +// USL_CtlCJoyButtonCustom() - The custom button routine for joystick +// calibration +// +/////////////////////////////////////////////////////////////////////////// +static boolean +USL_CtlCJoyButtonCustom(UserCall call,word i,word n) +{ + word joy, + minx,maxx, + miny,maxy; + + i++,n++; // Shut the compiler up + + if (call != uic_Hit) + return(false); + + IN_ClearKeysDown(); + joy = USL_FindDown(CtlCPanels) - 1; + + VW_HideCursor(); + FlushHelp = true; + fontcolor = F_SECONDCOLOR; + + USL_ShowHelp("Move Joystick to the Upper-Left"); + VW_UpdateScreen(); + while ((LastScan != sc_Escape) && !IN_GetJoyButtonsDB(joy)) + ; + if (LastScan != sc_Escape) + { + IN_GetJoyAbs(joy,&minx,&miny); + while (IN_GetJoyButtonsDB(joy)) + ; + + USL_ShowHelp("Move Joystick to the Lower-Right"); + VW_UpdateScreen(); + while ((LastScan != sc_Escape) && !IN_GetJoyButtonsDB(joy)) + ; + if (LastScan != sc_Escape) + { + IN_GetJoyAbs(0,&maxx,&maxy); + IN_SetupJoy(joy,minx,maxx,miny,maxy); + } + } + + if (LastScan != sc_Escape) + while (IN_GetJoyButtonsDB(joy)) + ; + + if (LastScan) + IN_ClearKeysDown(); + + fontcolor = F_BLACK; + VW_ShowCursor(); + + return(false); +} + +/////////////////////////////////////////////////////////////////////////// +// +// USL_ClearBottom() - Clears the bottom part of the window +// +/////////////////////////////////////////////////////////////////////////// +static void +USL_ClearBottom(void) +{ + WindowRec wr; + + US_SaveWindow(&wr); + US_RestoreWindow(&BottomWindow); + + US_ClearWindow(); + + US_RestoreWindow(&wr); +} + +/////////////////////////////////////////////////////////////////////////// +// +// USL_FormatHelp() - Formats helptext. Runs through and calculates the +// number of lines, and the offset for the start of each line. Stops +// after len bytes or when it hits a tilde ('~'). Munges the text. +// +/////////////////////////////////////////////////////////////////////////// +static word +USL_FormatHelp(char far *text,long len) +{ + word line, + w,h, + far *off; + char c, + far *s,far *l,far *le; + + WindowX += 4; + WindowW -= 4; + + MM_GetPtr(&LineOffsets,MaxHelpLines * sizeof(word)); + off = (word far *)LineOffsets; + for (line = 0,le = l = s = text;(s - text < len) && (*s != '~');s++) + { + if ((c = *s) == '\n') + { + *s = '\0'; + *off++ = l - text; // Save offset of start of line + line++; // Bump line number + le = l = s + 1; // Set start of line ptr + } + + if (c == '\r') + c = *s = ' '; + if // Strip orphaned spaces + ( + (c == ' ') + && (s == l) + && (*(s - 1) == '\0') + && (*(s + 1) != ' ') + && (s > text) + ) + le = l = s + 1; + else if (c == ' ') + { + *s = '\0'; + USL_MeasureString(l,&w,&h); + if (w >= WindowW) // If string width exceeds window, + { + *s = c; // Replace null char with proper char + *le = '\0'; // Go back to last line end + *off++ = l - text; // Save offset of start of line + line++; // Bump line number + l = s = le + 1; // Start next time through after last line end + } + else + { + *s = c; // Width still ok - put char back + le = s; // And save ptr to last ok end of word + } + } + } + + WindowX -= 4; + WindowW += 4; + + return(line); +} + +/////////////////////////////////////////////////////////////////////////// +// +// USL_DrawHelp() - Draws helptext in the current window +// +/////////////////////////////////////////////////////////////////////////// +static void +USL_DrawHelp(char far *text,word start,word end,word line,word h,word far *lp) +{ + px = WindowX + 4; + py = WindowY + (line * h); + for (lp += start;start < end;start++,px = WindowX + 4,py += h) + USL_DrawString(text + *lp++); +} + +/////////////////////////////////////////////////////////////////////////// +// +// USL_DoHelp() - Formats and displays the specified help +// +/////////////////////////////////////////////////////////////////////////// +static void +USL_DoHelp(memptr text,long len) +{ + boolean done, + moved; + int scroll; + word i, + pixdiv, + w,h, + lines,cur,page, + top,num,loc, + far *lp, + base,srcbase,destbase; + ScanCode waitkey; + longword lasttime; + WindowRec wr; + CursorInfo info; + + USL_ShowHelp("Arrow Keys Move / Escape Exits"); + fontcolor = F_BLACK; + + US_SaveWindow(&wr); + US_RestoreWindow(&BottomWindow); + US_ClearWindow(); + + VW_HideCursor(); + VW_UpdateScreen(); + + lines = USL_FormatHelp((char far *)text,len); + USL_MeasureString("",&w,&h); + page = WindowH / h; + cur = 0; + lp = LineOffsets; + + IN_ClearKeysDown(); + moved = true; + lasttime = 0; + scroll = 0; + done = false; + waitkey = sc_None; + while (!done) + { + if (moved) + { + while (TimeCount - lasttime < 5) + ; + lasttime = TimeCount; + + if (scroll == -1) + { + top = cur; + num = 1; + loc = 0; + } + else if (scroll == +1) + { + num = 1; + loc = page - 1; + top = cur + loc; + } + else + { + top = cur; + num = (page < lines)? page : lines; + loc = 0; + } + if (scroll) + { + if (grmode == CGAGR) + { + pixdiv = 4; + base = bufferofs + panadjust + (WindowX / pixdiv); + } + else if (grmode == EGAGR) + { + VWB_Bar(WindowX,WindowY + (loc * h),WindowW,num * h,WHITE); + USL_DrawHelp((char far *)text,top,top + num,loc,h,lp); + + pixdiv = 8; + base = displayofs + panadjust + (WindowX / pixdiv); + } + else if (grmode == VGAGR) + pixdiv = 1; + + if (scroll == 1) + { + srcbase = base + ylookup[WindowY + h]; + destbase = base + ylookup[WindowY]; + if (grmode == EGAGR) + { + EGAWRITEMODE(1); + VW_WaitVBL(1); + } + VW_ScreenToScreen(srcbase,destbase,WindowW / pixdiv, + WindowH - h); + } + else + { + i = WindowY + (h * (page - 1)); + srcbase = base + ylookup[i - h]; + destbase = base + ylookup[i]; + base = ylookup[h]; + for (i = page - 1;i;i--,srcbase -= base,destbase -= base) + VW_ScreenToScreen(srcbase,destbase,WindowW / pixdiv,h); + } + if (grmode == CGAGR) + { + VWB_Bar(WindowX,WindowY + (loc * h),WindowW,num * h,WHITE); + USL_DrawHelp((char far *)text,top,top + num,loc,h,lp); + VW_UpdateScreen(); + } + else if (grmode == EGAGR) + { + base = panadjust + (WindowX / pixdiv) + + ylookup[WindowY + (loc * h)]; + VW_ScreenToScreen(base + bufferofs,base + displayofs, + WindowW / pixdiv,h); + } + } + else + { + US_ClearWindow(); + USL_DrawHelp((char far *)text,top,top + num,loc,h,lp); + VW_UpdateScreen(); + } + + moved = false; + scroll = 0; + } + + if (waitkey) + while (IN_KeyDown(waitkey)) + ; + waitkey = sc_None; + + IN_ReadCursor(&info); + if (info.y < 0) + { + if (cur > 0) + { + scroll = -1; + cur--; + moved = true; + } + } + else if (info.y > 0) + { + if (cur + page < lines) + { + scroll = +1; + cur++; + moved = true; + } + } + else if (info.button0 || info.button1) + done = true; + else if (IN_KeyDown(LastScan)) + { + switch (LastScan) + { + case sc_Escape: + done = true; + break; + case sc_UpArrow: + if (cur > 0) + { + scroll = -1; + cur--; + moved = true; + } + break; + case sc_DownArrow: + if (cur + page < lines) + { + scroll = +1; + cur++; + moved = true; + } + break; + case sc_PgUp: + if (cur > page) + cur -= page; + else + cur = 0; + moved = true; + waitkey = sc_PgUp; + break; + case sc_PgDn: + if (cur + page < lines) + { + cur += page; + if (cur + page >= lines) + cur = lines - page; + moved = true; + } + waitkey = sc_PgDn; + break; + } + } + } + IN_ClearKeysDown(); + do + { + IN_ReadCursor(&info); + } while (info.button0 || info.button1); + + VW_ShowCursor(); + US_ClearWindow(); + VW_UpdateScreen(); + US_RestoreWindow(&wr); +} + +/////////////////////////////////////////////////////////////////////////// +// +// USL_CtlHButtonCustom() - The custom routine for all of the help buttons +// +/////////////////////////////////////////////////////////////////////////// +static boolean +USL_CtlHButtonCustom(UserCall call,word i,word n) +{ + word j; + UserItem *ip; + + if (call != uic_Hit) + return(false); + + ip = &TheItems[i][n]; + if (ip->sel & ui_Disabled) + return(false); + + ip->sel |= ui_Selected; + USL_DrawItem(i,n); + + USL_ClearBottom(); + + fontcolor = F_SECONDCOLOR; + USL_ShowHelp("Loading & Formatting Text..."); + VW_UpdateScreen(); + +#ifdef HELPTEXTLINKED // Ugly hack because of lack of disk space... + { +extern char far gametext,far context,far story; + char far *buf; + memptr dupe; + + switch (n) + { + case 0: + buf = &gametext; + break; + case 1: + buf = &context; + break; + case 2: + buf = &story; + break; + } + + MM_GetPtr(&dupe,5000); + _fmemcpy((char far *)dupe,buf,5000); + + USL_DoHelp(dupe,5000); + + MM_FreePtr(&dupe); + if (LineOffsets) + MM_FreePtr(&LineOffsets); + } +#else + { + char *name; + int file; + long len; + memptr buf; + + switch (n) + { + case 0: + name = "GAMETEXT."EXTENSION; + break; + case 1: + name = "CONTEXT."EXTENSION; + break; + case 2: + name = "STORY."EXTENSION; + break; + default: + Quit("Bad help button number"); + } + + if ((file = open(name,O_RDONLY | O_TEXT)) == -1) + USL_HandleError(errno); + else + { + len = filelength(file); + MM_GetPtr(&buf,len); + + if (CA_FarRead(file,(byte far *)buf,len)) + USL_DoHelp(buf,len); + else + USL_HandleError(errno); + + close(file); + MM_FreePtr(&buf); + } + + if (LineOffsets) + MM_FreePtr(&LineOffsets); + } +#endif + + fontcolor = F_BLACK; + + ip->sel &= ~ui_Selected; + USL_DrawItem(i,n); + + return(false); +} + +/////////////////////////////////////////////////////////////////////////// +// +// USL_CtlDButtonCustom() - The custom routine for all of the disk buttons. +// Sets up the bottom area of the window with the appropriate buttons +// +/////////////////////////////////////////////////////////////////////////// +static boolean +USL_CtlDButtonCustom(UserCall call,word i,word n) +{ + word j; + UserItem *ip; + + if (call != uic_Hit) + return(false); + + ip = &TheItems[i][n]; + if (ip->sel & ui_Disabled) + return(false); + + USL_ClearBottom(); + + j = 0; + TheItems[i + 1] = ip = n? CtlDEPanels : CtlDLSPanels; + while (ip && (ip->type != uii_Bad)) + { + USL_DrawItem(i + 1,j++); + ip++; + } + + return(false); +} + +/////////////////////////////////////////////////////////////////////////// +// +// USL_DLSRect() - Draw the rectangle for the save game names +// +/////////////////////////////////////////////////////////////////////////// +static Rect +USL_DLSRect(UserItem *ip) +{ + Rect r; + + r.ul.x = ip->r.lr.x + 40 + 2; + r.ul.y = ip->r.ul.y + 2; + r.lr.x = WindowX + WindowW - 8 - 2; + r.lr.y = ip->r.lr.y - 2; + + VWB_Bar(r.ul.x,r.ul.y,r.lr.x - r.ul.x,r.lr.y - r.ul.y,WHITE); + + VWB_Hlin(r.ul.x,r.lr.x,r.ul.y,BLACK); + VWB_Hlin(r.ul.x,r.lr.x,r.lr.y,BLACK); + VWB_Vlin(r.ul.y,r.lr.y,r.ul.x,BLACK); + VWB_Vlin(r.ul.y,r.lr.y,r.lr.x,BLACK); + + px = r.ul.x + 2; + py = r.ul.y + 2; + + return(r); +} + +/////////////////////////////////////////////////////////////////////////// +// +// USL_CtlDLButtonCustom() - The load game custom routine +// +/////////////////////////////////////////////////////////////////////////// +static boolean +USL_CtlDLButtonCustom(UserCall call,word i,word n) +{ + char *filename, + msg[MaxGameName + 12]; + word err; + int file; + UserItem *ip; + SaveGame *game; + WindowRec wr; + + // DEBUG - deal with warning user about loading a game causing abort + + game = &Games[n / 2]; + ip = &TheItems[i][n]; + + switch (call) + { + case uic_Draw: + if (!loadedgame) + { + USL_DLSRect(ip); + fontcolor = game->present? F_BLACK : F_FIRSTCOLOR; + USL_DrawString(game->present? game->name : "Empty"); + fontcolor = F_BLACK; + } + break; + case uic_Hit: + if (ip->sel & ui_Disabled) + return(false); + + LeaveDriveOn++; + filename = USL_GiveSaveName(n / 2); + + US_SaveWindow(&wr); + US_CenterWindow(30,3); + strcpy(msg,"Loading `"); + strcat(msg,game->name); + strcat(msg,"\'"); + US_PrintCentered(msg); + VW_HideCursor(); + VW_UpdateScreen(); + + err = 0; + if ((file = open(filename,O_BINARY | O_RDONLY)) != -1) + { + if (read(file,game,sizeof(*game)) == sizeof(*game)) + { + if (USL_LoadGame) + if (!USL_LoadGame(file)) + USL_HandleError(err = errno); + } + else + USL_HandleError(err = errno); + close(file); + } + else + USL_HandleError(err = errno); + if (err) + abortgame = true; + else + loadedgame = true; + game->present = true; + + if (loadedgame) + Paused = true; + + VW_ShowCursor(); + US_RestoreWindow(&wr); + + LeaveDriveOn--; + break; + } + return(false); +} + +/////////////////////////////////////////////////////////////////////////// +// +// USL_CtlDSButtonCustom() - The save game custom routine +// +/////////////////////////////////////////////////////////////////////////// +static boolean +USL_CtlDSButtonCustom(UserCall call,word i,word n) +{ + boolean ok; + char *filename; + word err; + int file; + Rect r; + UserItem *ip; + SaveGame *game; + WindowRec wr; + + if (call != uic_Hit) + return(false); + + game = &Games[n / 2]; + ip = &TheItems[i][n]; + if (ip->sel & ui_Disabled) + return(false); + + FlushHelp = true; + fontcolor = F_SECONDCOLOR; + USL_ShowHelp("Enter Game Name / Escape Aborts"); + fontcolor = F_BLACK; + + r = USL_DLSRect(ip - 1); + ok = US_LineInput(px,py,game->name,game->present? game->name : nil,true, + MaxGameName,r.lr.x - r.ul.x - 8); + if (!strlen(game->name)) + strcpy(game->name,"Untitled"); + if (ok) + { + US_SaveWindow(&wr); + US_CenterWindow(10,3); + US_PrintCentered("Saving"); + VW_HideCursor(); + VW_UpdateScreen(); + + LeaveDriveOn++; + filename = USL_GiveSaveName(n / 2); + err = 0; + file = open(filename,O_CREAT | O_BINARY | O_WRONLY, + S_IREAD | S_IWRITE | S_IFREG); + if (file != -1) + { + if (write(file,game,sizeof(*game)) == sizeof(*game)) + { + if (USL_SaveGame) + ok = USL_SaveGame(file); + if (!ok) + USL_HandleError(err = errno); + } + else + USL_HandleError(err = ((errno == ENOENT)? ENOMEM : errno)); + close(file); + } + else + USL_HandleError(err = ((errno == ENOENT)? ENOMEM : errno)); + if (err) + { + remove(filename); + ok = false; + } + LeaveDriveOn--; + + VW_ShowCursor(); + US_RestoreWindow(&wr); + USL_DoHit(i - 1,0); + VW_UpdateScreen(); + } + + if (!game->present) + game->present = ok; + + if (ok) + { + GameIsDirty = false; + (ip - 1)->sel &= ~ui_Disabled; + } + + USL_DrawItem(i,n - 1); +// USL_CtlDLButtonCustom(uic_Draw,i,n - 1); + + return(true); +} + +/////////////////////////////////////////////////////////////////////////// +// +// USL_CtlSButtonCustom() - The custom routine for all of the sound buttons +// +/////////////////////////////////////////////////////////////////////////// +static boolean +USL_CtlSButtonCustom(UserCall call,word i,word n) +{ + word j; + UserItem *ip; + + if (call != uic_Hit) + return(false); + + ip = &TheItems[i][n]; + if (ip->sel & ui_Disabled) + return(false); + + USL_ClearBottom(); + + if (n == sdm_SoundSource) + { + j = 0; + TheItems[i + 1] = ip = CtlSSSPanels; + while (ip && (ip->type != uii_Bad)) + { + USL_DrawItem(i + 1,j++); + ip++; + } + } + else + TheItems[i + 1] = nil; + + return(false); +} + +/////////////////////////////////////////////////////////////////////////// +// +// USL_CtlPButtonCustom() - The custom routine for all of the start game btns +// +/////////////////////////////////////////////////////////////////////////// +static boolean +USL_CtlPButtonCustom(UserCall call,word i,word n) +{ + word j; + UserItem *ip; + + if (call != uic_Hit) + return(false); + + ip = &TheItems[i][n]; + if (ip->sel & ui_Disabled) + return(false); + + USL_ClearBottom(); + + j = 0; + TheItems[i + 1] = ip = n? CtlPRPanels : CtlPSPanels; + while (ip && (ip->type != uii_Bad)) + { + USL_DrawItem(i + 1,j++); + ip++; + } + + return(false); +} + +/////////////////////////////////////////////////////////////////////////// +// +// USL_GiveAbortWarning() - Draws a string that warns the user that an +// action they're about to take will abort the game in progress +// +/////////////////////////////////////////////////////////////////////////// +static void +USL_GiveAbortWarning(void) +{ + WindowRec wr; + + if (!GameIsDirty) + return; + + US_SaveWindow(&wr); + US_RestoreWindow(&BottomWindow); + US_HomeWindow(); + PrintY += 5; + + VWB_Bar(WindowX,WindowY,WindowW,30,WHITE); + fontcolor = F_SECONDCOLOR; + US_CPrint("Warning! If you do this, you'll"); + US_CPrint("abort the current game."); + fontcolor = F_BLACK; + + US_RestoreWindow(&wr); +} + +/////////////////////////////////////////////////////////////////////////// +// +// USL_CtlPSButtonCustom() - The custom routine for the start game button +// +/////////////////////////////////////////////////////////////////////////// +static boolean +USL_CtlPSButtonCustom(UserCall call,word i,word n) +{ + boolean result; + UserItem *ip; + + i++; // Shut the compiler up + + switch (call) + { + case uic_Hit: + switch (n) + { + case 0: + restartgame = gd_Normal; + break; + case 1: + restartgame = gd_Easy; + break; + case 2: + restartgame = gd_Hard; + break; + } + if (restartgame && ingame && USL_ResetGame) + USL_ResetGame(); + result = false; + break; + case uic_Draw: + USL_GiveAbortWarning(); + result = false; + break; + default: + result = false; + break; + } + return(result); +} + +/////////////////////////////////////////////////////////////////////////// +// +// USL_CtlPRButtonCustom() - The custom routine for the resume game button +// +/////////////////////////////////////////////////////////////////////////// +static boolean +USL_CtlPRButtonCustom(UserCall call,word i,word n) +{ + if (call != uic_Hit) + return(false); + + i++,n++; // Shut the compiler up + ResumeGame = true; + return(false); +} + +/////////////////////////////////////////////////////////////////////////// +// +// USL_CtlDEButtonCustom() - The custom routine for the exit to DOS button +// +/////////////////////////////////////////////////////////////////////////// +static boolean +USL_CtlDEButtonCustom(UserCall call,word i,word n) +{ + boolean result; + UserItem *ip; + + i++,n++; // Shut the compiler up + + switch (call) + { + case uic_Hit: + QuitToDos = true; + break; + case uic_Draw: + USL_GiveAbortWarning(); + default: + result = false; + break; + } + return(result); +} + +/////////////////////////////////////////////////////////////////////////// +// +// USL_CtlCButtonCustom() - The custom routine for all of the control +// buttons +// +/////////////////////////////////////////////////////////////////////////// +static boolean +USL_CtlCButtonCustom(UserCall call,word i,word n) +{ + word j; + Point p; + UserItem *ip; + + if (call != uic_Hit) + return(false); + + ip = &TheItems[i][n]; + if (ip->sel & ui_Disabled) + return(false); + + USL_ClearBottom(); + if (n == 0) // Keyboard + { + TheItems[i + 1] = ip = CtlCKbdPanels; + p = CtlCKbdPanels[2].r.lr; + VWB_DrawPic(p.x,p.y,CTL_DIRSPIC); + } + else + TheItems[i + 1] = ip = CtlCJoyPanels; + + j = 0; + while (ip && (ip->type != uii_Bad)) + { + USL_DrawItem(i + 1,j++); + ip++; + } + + return(false); +} + +/////////////////////////////////////////////////////////////////////////// +// +// USL_HitHotKey() - After a hotkey was hit, move the cursor to the first +// selected item in the group after the group containing the item +// holding the hotkey +// +/////////////////////////////////////////////////////////////////////////// +static void +USL_HitHotKey(int i,int n) +{ + UserItem *ip; + + if (ip = TheItems[++i]) + { + if ((n = USL_FindDown(TheItems[i])) == -1) + n = 0; + ip += n; + CursorX = ip->r.lr.x - 8; + CursorY = ip->r.lr.y - 8; + CursorBad = true; + } +} + +/////////////////////////////////////////////////////////////////////////// +// +// USL_CheckScan() - Checks to see if the scancode in LastScan corresponds +// to anything in the list of useritems. If so, selects the item. +// +/////////////////////////////////////////////////////////////////////////// +static boolean +USL_CheckScan(word *ci,word *cn) +{ + word i,n; + UserItem *ip; + + if (!LastScan) + return(false); + +#if 1 // DEBUG - probably kill this code + // Use 1..? for the items across the top row + if (TheItems[1] && !IN_KeyDown(sc_RShift)) + { + for (i = 0,ip = TheItems[1];(ip->type != uii_Bad) && (i < 9);i++,ip++) + ; + for (n = 0;n < i;n++) + { + if (LastScan == 2 + n) // Numbers from 1..9 + { + if (!(TheItems[1][n].sel & ui_Disabled)) + { + LastScan = sc_None; + USL_DoHit(1,n); + return(true); + } + } + } + } + + // Use Alt-1..6 for the items in the leftmost column + if (IN_KeyDown(sc_RShift)) + { + n = LastScan - 2; + if (n < 6) // Numbers from 1..6 + { + USL_DoHit(0,n); + LastScan = sc_None; + return(true); + } + } +#endif + + // Check normal hotkeys for the leftmost column + for (i = 0;CtlPanels[i].type != uii_Bad;i++) + { + if (CtlPanels[i].key == LastScan) + { + LastScan = sc_None; + USL_DoHit(0,i); + *ci = 0; + *cn = i; + USL_HitHotKey(0,i); + return(true); + } + } + + // Check normal hotkeys for the top row + for (i = 0;i < 6;i++) + { + for (n = 0,ip = CtlPanels2[i];ip && ip->type != uii_Bad;n++,ip++) + { + if ((ip->key == LastScan) && !(ip->sel & ui_Disabled)) + { + LastScan = sc_None; + USL_DoHit(0,i); + USL_DoHit(1,n); + *ci = 1; + *cn = n; + USL_HitHotKey(1,n); + return(true); + } + } + } + return(false); +} + +/////////////////////////////////////////////////////////////////////////// +// +// USL_SetUpCtlPanel() - Sets the states of the UserItems to reflect the +// values of all the appropriate variables +// +/////////////////////////////////////////////////////////////////////////// +static void +USL_SetUpCtlPanel(void) +{ + word i,j; + + GameIsDirty = ingame; + + // Set up restart game + USL_TurnOff(CtlPPanels); + CtlPPanels[0].sel = ingame? ui_Normal : ui_Selected; + CtlPPanels[1].sel = ingame? ui_Selected : ui_Disabled; + + // Set up disk stuff - default to load/save game + USL_TurnOff(CtlDPanels); + CtlDPanels[0].sel = ui_Selected; + + // Set up load/save buttons + USL_TurnOff(CtlDLSPanels); + for (i = 0;i < MaxSaveGames;i++) + { + if (!Games[i].present) + CtlDLSPanels[i * 2].sel = ui_Disabled; + if (!ingame) + CtlDLSPanels[(i * 2) + 1].sel = ui_Disabled; + } + + // Set up Controls + USL_TurnOff(CtlCPanels); + CtlCPanels[1].sel = JoysPresent[0]? ui_Normal : ui_Disabled; + CtlCPanels[2].sel = JoysPresent[1]? ui_Normal : ui_Disabled; + if (Controls[0] == ctrl_Keyboard) + i = 0; + else + i = (Controls[0] == ctrl_Joystick1)? 1 : 2; + CtlCPanels[i].sel |= ui_Selected; + if (JoysPresent[1] && !JoysPresent[0]) + CtlCPanels[2].key = sc_F4; + else + CtlCPanels[1].key = sc_F4; + + // Set up Keyboard + for (i = 0;i < 10;i++) + CtlCKbdPanels[i].text = IN_GetScanName(*(KeyMaps[i])); + + // Set up Sounds + USL_TurnOff(CtlSPanels); + CtlSPanels[sdm_AdLib].sel = AdLibPresent? ui_Normal : ui_Disabled; +#if 0 // DEBUG - hack because no space for digitized sounds on Keen Dreams + CtlSPanels[sdm_SoundBlaster].sel = + SoundBlasterPresent? ui_Normal : ui_Disabled; + CtlSPanels[sdm_SoundSource].sel = + SoundSourcePresent? ui_Normal : ui_Disabled; +#else + CtlSPanels[sdm_SoundBlaster].sel = ui_Disabled; + CtlSPanels[sdm_SoundSource].sel = ui_Disabled; +#endif + CtlSPanels[SoundMode].sel |= ui_Selected; + + // Set up SoundSource + USL_TurnOff(CtlSSSPanels); + CtlSSSPanels[0].sel = ssIsTandy? ui_Selected : ui_Normal; + CtlSSSPanels[1].sel = (ssPort == 2)? ui_Selected : ui_Normal; + + // Set up Music + USL_TurnOff(CtlMPanels); + CtlMPanels[smm_AdLib].sel = AdLibPresent? ui_Normal : ui_Disabled; + CtlMPanels[MusicMode].sel |= ui_Selected; +} + +/////////////////////////////////////////////////////////////////////////// +// +// USL_TearDownCtlPanel() - Given the state of the control panel, sets the +// modes and values as appropriate +// +/////////////////////////////////////////////////////////////////////////// +static void +USL_TearDownCtlPanel(void) +{ + int i; + + i = USL_FindDown(CtlCPanels); + if (i != -1) + { + i = i? (i == 1? ctrl_Joystick1 : ctrl_Joystick2) : ctrl_Keyboard; + IN_SetControlType(0,i); + } + + CtlCPanels[1].key = CtlCPanels[2].key = sc_None; + + i = USL_FindDown(CtlSPanels); + if (i != -1) + SD_SetSoundMode(i); + + ssIsTandy = CtlSSSPanels[0].sel & ui_Selected; + ssPort = (CtlSSSPanels[1].sel & ui_Selected)? 2 : 1; + + i = USL_FindDown(CtlMPanels); + if (i != -1) + { + SD_SetMusicMode(i); + + if (!QuitToDos) + { + US_CenterWindow(20,8); + US_CPrint("Loading"); +#if 0 + fontcolor = F_SECONDCOLOR; + US_CPrint("Sounds"); + fontcolor = F_BLACK; +#endif + VW_UpdateScreen(); + + CA_LoadAllSounds(); + } + } +} + +/////////////////////////////////////////////////////////////////////////// +// +// US_ControlPanel() - This is the main routine for the control panel +// +/////////////////////////////////////////////////////////////////////////// +void +US_ControlPanel(void) +{ + char gamename[MaxGameName + 10 + 1]; + ScanCode c; + boolean done, + buttondown,inrect; + word hiti,hitn, + i,n, + lasti,lastn, + lastx,lasty; + longword lasttime; + Point p; + Rect userect; + UserItem *ip; + + c = LastScan; + if (c == sc_Escape) // Map escape from game to Exit to DOS + c = sc_Q; + + CA_UpLevel(); + for (i = CONTROLS_LUMP_START;i <= CONTROLS_LUMP_END;i++) + CA_MarkGrChunk(i); + CA_MarkGrChunk(CTL_LITTLEMASKPICM); + CA_MarkGrChunk(CTL_LSMASKPICM); + CA_CacheMarks("Options Screen"); + + USL_SetUpCtlPanel(); + + US_SetPrintRoutines(VW_MeasurePropString,VWB_DrawPropString); + fontcolor = F_BLACK; + + VW_InitDoubleBuffer(); + + VWB_Bar(0,0,MaxX,MaxY,FIRSTCOLOR); + US_DrawWindow(8,22,30,2); + US_SaveWindow(&HelpWindow); + US_DrawWindow(8,7,30,14); + US_SaveWindow(&BottomWindow); + US_DrawWindow(8,1,30,20); + + for (ip = CtlPanels;ip->type != uii_Bad;ip++) + VWB_DrawPic(ip->r.ul.x,ip->r.ul.y,ip->picup); + + US_StartCursor(); + CursorX = (8 * 8) + ((MaxX - (8 * 8)) / 2); + CursorBad = true; + + CtlPanelButton = -1; + LastScan = c; + USL_CheckScan(&i,&n); + if (CtlPanelButton == -1) + USL_DoHit(0,0); + + ResumeGame = false; + done = false; + FlushHelp = true; + lastx = lasty = -1; + while + ( + (restartgame == gd_Continue) + && !(done || loadedgame || ResumeGame) + ) + { + VW_UpdateScreen(); + + buttondown = US_UpdateCursor(); + inrect = USL_IsInRect(CursorX,CursorY,&i,&n); + + if (FlushHelp) + { + lasti = -2; + lasttime = TimeCount; + FlushHelp = false; + } + if (inrect) + { + if ((lasti != i) || (lastn != n)) + { + // If over a Load button + if + ( + (CtlPanelButton == 2) + && (i == 2) + && (TheItems[1][0].sel & ui_Selected) + && (Games[n / 2].present) + && !(n & 1) + ) + { + strcpy(gamename,"Load `"); + strcat(gamename,Games[n / 2].name); + strcat(gamename,"'"); + USL_ShowHelp(gamename); + } + else + USL_ShowHelp(TheItems[i][n].help); + lasti = i; + lastn = n; + } + } + else if (lasti != (word)-1) + { + USL_ShowHelp("Select a Button"); + lasti = -1; + } + + hiti = i; + hitn = n; + + if (inrect) + userect = TheItems[i][n].r; + else + { + userect.ul.x = CursorX; + userect.ul.y = CursorY; + userect.lr = userect.ul; + } + + if (IN_KeyDown(sc_UpArrow)) + { + USL_FindRect(userect,motion_None,motion_Up); + buttondown = false; + IN_ClearKey(sc_UpArrow); + } + else if (IN_KeyDown(sc_DownArrow)) + { + USL_FindRect(userect,motion_None,motion_Down); + buttondown = false; + IN_ClearKey(sc_DownArrow); + } + else if (IN_KeyDown(sc_LeftArrow)) + { + USL_FindRect(userect,motion_Left,motion_None); + buttondown = false; + IN_ClearKey(sc_LeftArrow); + } + else if (IN_KeyDown(sc_RightArrow)) + { + USL_FindRect(userect,motion_Right,motion_None); + buttondown = false; + IN_ClearKey(sc_RightArrow); + } + else if + ( + IN_KeyDown(c = sc_Return) + || IN_KeyDown(c = KbdDefs[0].button0) + || IN_KeyDown(c = KbdDefs[0].button1) + ) + { + IN_ClearKey(c); + if (inrect) + { + ip = &TheItems[hiti][hitn]; + + if ((ip->type == uii_Button) && !(ip->sel & ui_Disabled)) + { + lasttime = TimeCount; + + ip->sel |= ui_Selected; + USL_DrawItem(hiti,hitn); + VW_UpdateScreen(); + + while (TimeCount - lasttime < TickBase / 4) + ; + lasttime = TimeCount; + + ip->sel &= ~ui_Selected; + USL_DrawItem(hiti,hitn); + VW_UpdateScreen(); + + while (TimeCount - lasttime < TickBase / 4) + ; + } + + USL_DoHit(hiti,hitn); + } + } + else if (USL_CheckScan(&i,&n)) + ; + else if (buttondown && inrect && USL_TrackItem(hiti,hitn)) + USL_DoHit(hiti,hitn); + + if (LastScan == sc_Escape) + { + IN_ClearKey(sc_Escape); + done = true; + } + + if (QuitToDos) + done = true; + + if ((lastx != CursorX) || (lasty != CursorY)) + { + lastx = CursorX; + lasty = CursorY; + lasttime = TimeCount; + } + if (TimeCount - lasttime > TickBase * 10) + { + if (((TimeCount - lasttime) / TickBase) & 2) + fontcolor = F_SECONDCOLOR; + USL_ShowHelp("Press F1 for Help"); + fontcolor = F_BLACK; + } + } + + US_ShutCursor(); + + USL_TearDownCtlPanel(); + + if (restartgame && USL_ResetGame) + USL_ResetGame(); + + if (QuitToDos) + { + if (tedlevel) + TEDDeath(); + else + { + US_CenterWindow(20,3); + fontcolor = F_SECONDCOLOR; + US_PrintCentered("Now Exiting to DOS..."); + fontcolor = F_BLACK; + VW_UpdateScreen(); + Quit(nil); + } + } + + CA_DownLevel(); +} + +// High score routines + +/////////////////////////////////////////////////////////////////////////// +// +// US_DisplayHighScores() - Assumes that double buffering has been started. +// If passed a -1 will just display the high scores, but if passed +// a non-negative number will display that entry in red and let the +// user type in a name +// +/////////////////////////////////////////////////////////////////////////// +void +US_DisplayHighScores(int which) +{ + char buffer[16],*str; + word i, + w,h, + x,y; + HighScore *s; + + US_CenterWindow(30,MaxScores + (MaxScores / 2)); + + x = WindowX + (WindowW / 2); + US_Print(" Name"); + PrintX = x + 20; + US_Print("Score"); + PrintX = x + 60; + US_Print("Done\n\n"); + PrintY -= 3; + + for (i = WindowX;i < WindowX + WindowW;i += 8) + VWB_DrawTile8M(i,WindowY + 8,10); + VWB_DrawTile8M(WindowX - 8,WindowY + 8,9); + VWB_DrawTile8M(WindowX + WindowW,WindowY + 8,11); + + for (i = 0,s = Scores;i < MaxScores;i++,s++) + { + fontcolor = (i == which)? F_SECONDCOLOR : F_BLACK; + + if (i != which) + { + US_Print(" "); + if (strlen(s->name)) + US_Print(s->name); + else + US_Print("-"); + } + else + y = PrintY; + + PrintX = x + (7 * 8); + ultoa(s->score,buffer,10); + for (str = buffer;*str;str++) + *str = *str + (129 - '0'); // Used fixed-width numbers (129...) + USL_MeasureString(buffer,&w,&h); + PrintX -= w; + US_Print(buffer); + + PrintX = x + 60; + if (s->completed) + US_PrintUnsigned(s->completed); + else + US_Print("-"); + + US_Print("\n"); + } + + if (which != -1) + { + fontcolor = F_SECONDCOLOR; + PrintY = y; + PrintX = WindowX; + US_Print(" "); + strcpy(Scores[which].name,""); + US_LineInput(PrintX,PrintY,Scores[which].name,nil,true,MaxHighName, + (WindowW / 2) - 8); + } + fontcolor = F_BLACK; + + VW_UpdateScreen(); +} + +/////////////////////////////////////////////////////////////////////////// +// +// US_CheckHighScore() - Checks gamestate to see if the just-ended game +// should be entered in the high score list. If so, lets the user +// enter their name +// +/////////////////////////////////////////////////////////////////////////// +void +US_CheckHighScore(long score,word other) +{ + word i,j, + n; + HighScore myscore; + + strcpy(myscore.name,""); + myscore.score = score; + myscore.completed = other; + + for (i = 0,n = -1;i < MaxScores;i++) + { + if + ( + (myscore.score > Scores[i].score) + || ( + (myscore.score == Scores[i].score) + && (myscore.completed > Scores[i].completed) + ) + ) + { + for (j = MaxScores;--j > i;) + Scores[j] = Scores[j - 1]; + Scores[i] = myscore; + + n = i; + HighScoresDirty = true; + break; + } + } + + VW_InitDoubleBuffer(); + VWB_Bar(0,0,MaxX,MaxY,FIRSTCOLOR); + + US_DisplayHighScores(n); + IN_UserInput(5 * TickBase,false); +} diff --git a/16/cawat/ID_US.H b/16/cawat/ID_US.H new file mode 100644 index 00000000..7acc2435 --- /dev/null +++ b/16/cawat/ID_US.H @@ -0,0 +1,146 @@ +/* Catacomb Armageddon Source Code + * Copyright (C) 1993-2014 Flat Rock Software + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +// +// ID Engine +// ID_US.h - Header file for the User Manager +// v1.0d1 +// By Jason Blochowiak +// + +#ifndef __TYPES__ +#include "ID_Types.h" +#endif + +#ifndef __ID_US__ +#define __ID_US__ + +#ifdef __DEBUG__ +#define __DEBUG_UserMgr__ +#endif + +//#define HELPTEXTLINKED + +#define MaxHelpLines 500 + +#define MaxHighName 57 +#define MaxScores 7 +typedef struct + { + char name[MaxHighName + 1]; + long score; + word completed; + } HighScore; + +#define MaxGameName 32 +#define MaxSaveGames 6 +typedef struct + { + char signature[4]; + word *oldtest; + boolean present; + char name[MaxGameName + 1]; + } SaveGame; + +#define MaxString 128 // Maximum input string size + +typedef struct + { + int x,y, + w,h, + px,py; + } WindowRec; // Record used to save & restore screen windows + +typedef enum + { + gd_Continue, + gd_Easy, + gd_Normal, + gd_Hard + } GameDiff; + +// Hack import for TED launch support +extern boolean tedlevel; +extern word tedlevelnum; +extern void TEDDeath(void); + +extern word MaxX,MaxY; // MDM (GAMERS EDGE) + +extern boolean ingame, // Set by game code if a game is in progress + abortgame, // Set if a game load failed + loadedgame, // Set if the current game was loaded + NoWait, + HighScoresDirty; +extern char *abortprogram; // Set to error msg if program is dying +extern GameDiff restartgame; // Normally gd_Continue, else starts game +extern word PrintX,PrintY; // Current printing location in the window +extern word WindowX,WindowY,// Current location of window + WindowW,WindowH;// Current size of window + +extern boolean Button0,Button1, + CursorBad; +extern int CursorX,CursorY; + +extern void (*USL_MeasureString)(char far *,word *,word *), + (*USL_DrawString)(char far *); + +extern boolean (*USL_SaveGame)(int),(*USL_LoadGame)(int); +extern void (*USL_ResetGame)(void); +extern SaveGame Games[MaxSaveGames]; +extern HighScore Scores[]; + +#define US_HomeWindow() {PrintX = WindowX; PrintY = WindowY;} + +extern void US_Startup(void), + US_Setup(void), + US_Shutdown(void), + US_InitRndT(boolean randomize), + US_SetLoadSaveHooks(boolean (*load)(int), + boolean (*save)(int), + void (*reset)(void)), + US_TextScreen(void), + US_UpdateTextScreen(void), + US_FinishTextScreen(void), + US_ControlPanel(void), + US_DrawWindow(word x,word y,word w,word h), + US_CenterWindow(word,word), + US_SaveWindow(WindowRec *win), + US_RestoreWindow(WindowRec *win), + US_ClearWindow(void), + US_SetPrintRoutines(void (*measure)(char far *,word *,word *), + void (*print)(char far *)), + US_PrintCentered(char *s), + US_CPrint(char *s), + US_CPrintLine(char *s), + US_Print(char *s), + US_PrintUnsigned(longword n), + US_PrintSigned(long n), + US_StartCursor(void), + US_ShutCursor(void), + US_ControlPanel(void), + US_CheckHighScore(long score,word other), + US_DisplayHighScores(int which); +extern boolean US_UpdateCursor(void), + US_LineInput(int x,int y,char *buf,char *def,boolean escok, + int maxchars,int maxwidth); +extern int US_CheckParm(char *parm,char **strings), + US_RndT(void); + + void USL_PrintInCenter(char *s,Rect r); + char *USL_GiveSaveName(word game); +#endif diff --git a/16/cawat/ID_US_1.C b/16/cawat/ID_US_1.C new file mode 100644 index 00000000..789229cc --- /dev/null +++ b/16/cawat/ID_US_1.C @@ -0,0 +1,1317 @@ +/* Catacomb Armageddon Source Code + * Copyright (C) 1993-2014 Flat Rock Software + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +// +// ID Engine +// ID_US_1.c - User Manager - General routines +// v1.1d1 +// By Jason Blochowiak +// Hacked up for Catacomb 3D +// + +// +// This module handles dealing with user input & feedback +// +// Depends on: Input Mgr, View Mgr, some variables from the Sound, Caching, +// and Refresh Mgrs, Memory Mgr for background save/restore +// +// Globals: +// ingame - Flag set by game indicating if a game is in progress +// abortgame - Flag set if the current game should be aborted (if a load +// game fails) +// loadedgame - Flag set if a game was loaded +// abortprogram - Normally nil, this points to a terminal error message +// if the program needs to abort +// restartgame - Normally set to gd_Continue, this is set to one of the +// difficulty levels if a new game should be started +// PrintX, PrintY - Where the User Mgr will print (global coords) +// WindowX,WindowY,WindowW,WindowH - The dimensions of the current +// window +// + +#include "ID_HEADS.H" + +#pragma hdrstop + +#pragma warn -pia + + +// Special imports +extern boolean showscorebox; +#ifdef KEEN +extern boolean oldshooting; +extern ScanCode firescan; +#else + ScanCode firescan; +#endif + +// Global variables + char *abortprogram; + boolean NoWait, + HighScoresDirty; + word PrintX,PrintY; + word WindowX,WindowY,WindowW,WindowH; + + word MaxX=320,MaxY=200; // MDM (GAMERS EDGE) + +// Internal variables +#define ConfigVersion 1 + +static char *ParmStrings[] = {"TEDLEVEL","NOWAIT"}, + *ParmStrings2[] = {"COMP","NOCOMP"}; +static boolean US_Started; + + boolean Button0,Button1, + CursorBad; + int CursorX,CursorY; + + void (*USL_MeasureString)(char far *,word *,word *) = VW_MeasurePropString, + (*USL_DrawString)(char far *) = VWB_DrawPropString; + + boolean (*USL_SaveGame)(int),(*USL_LoadGame)(int); + void (*USL_ResetGame)(void); + SaveGame Games[MaxSaveGames]; + HighScore Scores[MaxScores] = + { + {"Sir Lancelot",500,3}, + {"",0}, + {"",0}, + {"",0}, + {"",0}, + {"",0}, + {"",0}, + }; + +// Internal routines + +// Public routines + +/////////////////////////////////////////////////////////////////////////// +// +// USL_HardError() - Handles the Abort/Retry/Fail sort of errors passed +// from DOS. +// +/////////////////////////////////////////////////////////////////////////// +#pragma warn -par +#pragma warn -rch +int +USL_HardError(word errval,int ax,int bp,int si) +{ +#define IGNORE 0 +#define RETRY 1 +#define ABORT 2 +extern void ShutdownId(void); + +static char buf[32]; +static WindowRec wr; + int di; + char c,*s,*t; + + + di = _DI; + + if (ax < 0) + s = "Device Error"; + else + { + if ((di & 0x00ff) == 0) + s = "Drive ~ is Write Protected"; + else + s = "Error on Drive ~"; + for (t = buf;*s;s++,t++) // Can't use sprintf() + if ((*t = *s) == '~') + *t = (ax & 0x00ff) + 'A'; + *t = '\0'; + s = buf; + } + + c = peekb(0x40,0x49); // Get the current screen mode + if ((c < 4) || (c == 7)) + goto oh_kill_me; + + // DEBUG - handle screen cleanup + + US_SaveWindow(&wr); + US_CenterWindow(30,3); + US_CPrint(s); + US_CPrint("(R)etry or (A)bort?"); + VW_UpdateScreen(); + IN_ClearKeysDown(); + +asm sti // Let the keyboard interrupts come through + + while (true) + { + switch (IN_WaitForASCII()) + { + case key_Escape: + case 'a': + case 'A': + goto oh_kill_me; + break; + case key_Return: + case key_Space: + case 'r': + case 'R': + US_ClearWindow(); + VW_UpdateScreen(); + US_RestoreWindow(&wr); + return(RETRY); + break; + } + } + +oh_kill_me: + abortprogram = s; + ShutdownId(); + fprintf(stderr,"Terminal Error: %s\n",s); + if (tedlevel) + fprintf(stderr,"You launched from TED. I suggest that you reboot...\n"); + + return(ABORT); +#undef IGNORE +#undef RETRY +#undef ABORT +} +#pragma warn +par +#pragma warn +rch + +/////////////////////////////////////////////////////////////////////////// +// +// USL_GiveSaveName() - Returns a pointer to a static buffer that contains +// the filename to use for the specified save game +// +/////////////////////////////////////////////////////////////////////////// +char * +USL_GiveSaveName(word game) +{ +static char name[] = "SAVEGAMx."EXT; + + name[7] = '0' + game; + return(name); +} + +/////////////////////////////////////////////////////////////////////////// +// +// US_SetLoadSaveHooks() - Sets the routines that the User Mgr calls after +// reading or writing the save game headers +// +/////////////////////////////////////////////////////////////////////////// +void +US_SetLoadSaveHooks(boolean (*load)(int),boolean (*save)(int),void (*reset)(void)) +{ + USL_LoadGame = load; + USL_SaveGame = save; + USL_ResetGame = reset; +} + +/////////////////////////////////////////////////////////////////////////// +// +// USL_ReadConfig() - Reads the configuration file, if present, and sets +// things up accordingly. If it's not present, uses defaults. This file +// includes the high scores. +// +/////////////////////////////////////////////////////////////////////////// +static void +USL_ReadConfig(void) +{ + boolean gotit; + char sig[sizeof(EXT)]; + word version; + int file; + SDMode sd; + SMMode sm; + ControlType ctl; + + if ((file = open("CONFIG."EXT,O_BINARY | O_RDONLY)) != -1) + { + read(file,sig,sizeof(EXT)); + read(file,&version,sizeof(version)); + if (strcmp(sig,EXT) || (version != ConfigVersion)) + { + close(file); + goto rcfailed; + } + read(file,Scores,sizeof(HighScore) * MaxScores); + read(file,&sd,sizeof(sd)); + read(file,&sm,sizeof(sm)); + read(file,&ctl,sizeof(ctl)); + read(file,&(KbdDefs[0]),sizeof(KbdDefs[0])); + read(file,&showscorebox,sizeof(showscorebox)); + read(file,&compatability,sizeof(compatability)); +#ifdef KEEN + read(file,&oldshooting,sizeof(oldshooting)); + read(file,&firescan,sizeof(firescan)); +#endif + close(file); + + HighScoresDirty = false; + gotit = true; + } + else + { +rcfailed: + sd = sdm_Off; + sm = smm_Off; + ctl = ctrl_Keyboard; + showscorebox = true; +#ifdef KEEN + oldshooting = false; +#endif + + gotit = false; + HighScoresDirty = true; + } + + SD_Default(gotit,sd,sm); + IN_Default(gotit,ctl); +} + +/////////////////////////////////////////////////////////////////////////// +// +// USL_WriteConfig() - Writes out the current configuration, including the +// high scores. +// +/////////////////////////////////////////////////////////////////////////// +static void +USL_WriteConfig(void) +{ + word version; + int file; + + version = ConfigVersion; + file = open("CONFIG."EXT,O_CREAT | O_BINARY | O_WRONLY, + S_IREAD | S_IWRITE | S_IFREG); + if (file != -1) + { + write(file,EXT,sizeof(EXT)); + write(file,&version,sizeof(version)); + write(file,Scores,sizeof(HighScore) * MaxScores); + write(file,&SoundMode,sizeof(SoundMode)); + write(file,&MusicMode,sizeof(MusicMode)); + if // Hack + ( + (Controls[0] == ctrl_Joystick1) + || (Controls[0] == ctrl_Joystick2) + ) + Controls[0] = ctrl_Keyboard; + write(file,&(Controls[0]),sizeof(Controls[0])); + write(file,&(KbdDefs[0]),sizeof(KbdDefs[0])); + write(file,&showscorebox,sizeof(showscorebox)); + write(file,&compatability,sizeof(compatability)); +#ifdef KEEN + write(file,&oldshooting,sizeof(oldshooting)); + write(file,&firescan,sizeof(firescan)); +#endif + close(file); + } +} + +/////////////////////////////////////////////////////////////////////////// +// +// USL_CheckSavedGames() - Checks to see which saved games are present +// & valid +// +/////////////////////////////////////////////////////////////////////////// +static void +USL_CheckSavedGames(void) +{ + boolean ok; + char *filename; + word i; + int file; + SaveGame *game; + + USL_SaveGame = 0; + USL_LoadGame = 0; + + for (i = 0,game = Games;i < MaxSaveGames;i++,game++) + { + filename = USL_GiveSaveName(i); + ok = false; + if ((file = open(filename,O_BINARY | O_RDONLY)) != -1) + { + if + ( + (read(file,game,sizeof(*game)) == sizeof(*game)) + && (!strcmp(game->signature,EXT)) + && (game->oldtest == &PrintX) + ) + ok = true; + + close(file); + } + + if (ok) + game->present = true; + else + { + strcpy(game->signature,EXT); + game->present = false; + strcpy(game->name,"Empty"); + } + } +} + +/////////////////////////////////////////////////////////////////////////// +// +// US_Startup() - Starts the User Mgr +// +/////////////////////////////////////////////////////////////////////////// +void +US_Startup(void) +{ + int i; + + if (US_Started) + return; + + harderr(USL_HardError); // Install the fatal error handler + + US_InitRndT(true); // Initialize the random number generator + + USL_ReadConfig(); // Read config file + + for (i = 1;i < _argc;i++) + { + switch (US_CheckParm(_argv[i],ParmStrings2)) + { + case 0: + if (grmode == EGAGR) + compatability = true; + break; + case 1: + compatability = false; + break; + } + } + + US_Started = true; +} + +/////////////////////////////////////////////////////////////////////////// +// +// US_Setup() - Does the disk access part of the User Mgr's startup +// +/////////////////////////////////////////////////////////////////////////// +void +US_Setup(void) +{ + USL_CheckSavedGames(); // Check which saved games are present +} + +/////////////////////////////////////////////////////////////////////////// +// +// US_Shutdown() - Shuts down the User Mgr +// +/////////////////////////////////////////////////////////////////////////// +void +US_Shutdown(void) +{ + if (!US_Started) + return; + + if (!abortprogram) + USL_WriteConfig(); + + US_Started = false; +} + +/////////////////////////////////////////////////////////////////////////// +// +// US_CheckParm() - checks to see if a string matches one of a set of +// strings. The check is case insensitive. The routine returns the +// index of the string that matched, or -1 if no matches were found +// +/////////////////////////////////////////////////////////////////////////// +int +US_CheckParm(char *parm,char **strings) +{ + char cp,cs, + *p,*s; + int i; + + while (!isalpha(*parm)) // Skip non-alphas + parm++; + + for (i = 0;*strings && **strings;i++) + { + for (s = *strings++,p = parm,cs = cp = 0;cs == cp;) + { + cs = *s++; + if (!cs) + return(i); + cp = *p++; + + if (isupper(cs)) + cs = tolower(cs); + if (isupper(cp)) + cp = tolower(cp); + } + } + return(-1); +} + +#if 0 +/////////////////////////////////////////////////////////////////////////// +// +// USL_ScreenDraw() - Draws a chunk of the text screen (called only by +// US_TextScreen()) +// +/////////////////////////////////////////////////////////////////////////// +static void +USL_ScreenDraw(word x,word y,char *s,byte attr) +{ + byte far *screen,far *oscreen; + + screen = MK_FP(0xb800,(x * 2) + (y * 80 * 2)); + oscreen = (&introscn + 7) + ((x - 1) * 2) + (y * 80 * 2) + 1; + while (*s) + { + *screen++ = *s++; + if (attr != 0xff) + { + *screen++ = (attr & 0x8f) | (*oscreen & 0x70); + oscreen += 2; + } + else + screen++; + } +} +#endif + +/////////////////////////////////////////////////////////////////////////// +// +// USL_ClearTextScreen() - Makes sure the screen is in text mode, clears it, +// and moves the cursor to the leftmost column of the bottom line +// +/////////////////////////////////////////////////////////////////////////// +static void +USL_ClearTextScreen(void) +{ + // Set to 80x25 color text mode + _AL = 3; // Mode 3 + _AH = 0x00; + geninterrupt(0x10); + + // Use BIOS to move the cursor to the bottom of the screen + _AH = 0x0f; + geninterrupt(0x10); // Get current video mode into _BH + _DL = 0; // Lefthand side of the screen + _DH = 24; // Bottom row + _AH = 0x02; + geninterrupt(0x10); +} + +#if 0 +/////////////////////////////////////////////////////////////////////////// +// +// US_TextScreen() - Puts up the startup text screen +// Note: These are the only User Manager functions that can be safely called +// before the User Mgr has been started up +// +/////////////////////////////////////////////////////////////////////////// +void +US_TextScreen(void) +{ + word i,n; + + USL_ClearTextScreen(); + + _fmemcpy(MK_FP(0xb800,0),7 + &introscn,80 * 25 * 2); + + // Check for TED launching here + for (i = 1;i < _argc;i++) + { + n = US_CheckParm(_argv[i],ParmStrings); + if (n == 0) + { + tedlevelnum = atoi(_argv[i + 1]); + if (tedlevelnum >= 0) + { + tedlevel = true; + return; + } + else + break; + } + else if (n == 1) + { + NoWait = true; + return; + } + } +} + +/////////////////////////////////////////////////////////////////////////// +// +// USL_Show() - Changes the appearance of one of the fields on the text +// screen. Possibly adds a checkmark in front of it and highlights it +// +/////////////////////////////////////////////////////////////////////////// +static void +USL_Show(word x,word y,word w,boolean show,boolean hilight) +{ + byte far *screen,far *oscreen; + + screen = MK_FP(0xb800,((x - 1) * 2) + (y * 80 * 2)); + oscreen = (&introscn + 7) + ((x - 1) * 2) + (y * 80 * 2) - 1; + *screen++ = show? 251 : ' '; // Checkmark char or space +// *screen = 0x48; +// *screen = (*oscreen & 0xf0) | 8; + oscreen += 2; + if (show && hilight) + { + for (w++;w--;screen += 2,oscreen += 2) + *screen = (*oscreen & 0xf0) | 0x0f; + } +} + +/////////////////////////////////////////////////////////////////////////// +// +// USL_ShowMem() - Right justifies a longword in one of the memory fields on +// the text screen +// +/////////////////////////////////////////////////////////////////////////// +static void +USL_ShowMem(word x,word y,long mem) +{ + char buf[16]; + word i; + + for (i = strlen(ltoa(mem,buf,10));i < 5;i++) + USL_ScreenDraw(x++,y," ",0xff); + USL_ScreenDraw(x,y,buf,0xff); +} + +/////////////////////////////////////////////////////////////////////////// +// +// US_UpdateTextScreen() - Called after the ID libraries are started up. +// Displays what hardware is present. +// +/////////////////////////////////////////////////////////////////////////// +void +US_UpdateTextScreen(void) +{ + boolean b; + longword totalmem; + + // Show video card info + b = (grmode == CGAGR); + USL_Show(21,7,4,(videocard >= CGAcard) && (videocard <= VGAcard),b); + b = (grmode == EGAGR); + USL_Show(21,8,4,(videocard >= EGAcard) && (videocard <= VGAcard),b); + b = (grmode == VGAGR); + USL_Show(21,9,4,videocard == VGAcard,b); + if (compatability) + USL_ScreenDraw(5,10,"SVGA Compatibility Mode Enabled.",0x4f); + + // Show input device info + USL_Show(60,7,8,true,true); + USL_Show(60,8,11,JoysPresent[0],true); + USL_Show(60,9,11,JoysPresent[1],true); + USL_Show(60,10,5,MousePresent,true); + + // Show sound hardware info + USL_Show(21,14,11,true,SoundMode == sdm_PC); + b = (SoundMode == sdm_AdLib) || (MusicMode == smm_AdLib); + USL_Show(21,15,14,AdLibPresent,b); + if (b && AdLibPresent) // Hack because of two lines + { + byte far *screen,far *oscreen; + word x,y,w; + + x = 21; + y = 16; + w = 14; + screen = MK_FP(0xb800,(x * 2) + (y * 80 * 2) - 1); + oscreen = (&introscn + 7) + (x * 2) + (y * 80 * 2) - 1; + oscreen += 2; + for (w++;w--;screen += 2,oscreen += 2) + *screen = (*oscreen & 0xf0) | 0x0f; + } + + // Show memory available/used + USL_ShowMem(63,15,mminfo.mainmem / 1024); + USL_Show(53,15,23,true,true); + USL_ShowMem(63,16,mminfo.EMSmem / 1024); + USL_Show(53,16,23,mminfo.EMSmem? true : false,true); + USL_ShowMem(63,17,mminfo.XMSmem / 1024); + USL_Show(53,17,23,mminfo.XMSmem? true : false,true); + totalmem = mminfo.mainmem + mminfo.EMSmem + mminfo.XMSmem; + USL_ShowMem(63,18,totalmem / 1024); + USL_Show(53,18,23,true,true); // DEBUG + USL_ScreenDraw(52,18," ",0xff); + + // Change Initializing... to Loading... + USL_ScreenDraw(27,22," Loading... ",0x9c); +} + +/////////////////////////////////////////////////////////////////////////// +// +// US_FinishTextScreen() - After the main program has finished its initial +// loading, this routine waits for a keypress and then clears the screen +// +/////////////////////////////////////////////////////////////////////////// +void +US_FinishTextScreen(void) +{ +static byte colors[] = {4,6,13,15,15,15,15,15,15}; + boolean up; + int i,c; + + // Change Loading... to Press a Key + + if (!(tedlevel || NoWait)) + { + IN_ClearKeysDown(); + for (i = 0,up = true;!IN_UserInput(4,true);) + { + c = colors[i]; + if (up) + { + if (++i == 9) + i = 8,up = false; + } + else + { + if (--i < 0) + i = 1,up = true; + } + + USL_ScreenDraw(29,22," Ready - Press a Key ",0x00 + c); + } + } + else + USL_ScreenDraw(29,22," Ready - Press a Key ",0x9a); + IN_ClearKeysDown(); + + USL_ClearTextScreen(); +} +#endif + +// Window/Printing routines + +/////////////////////////////////////////////////////////////////////////// +// +// US_SetPrintRoutines() - Sets the routines used to measure and print +// from within the User Mgr. Primarily provided to allow switching +// between masked and non-masked fonts +// +/////////////////////////////////////////////////////////////////////////// +void +US_SetPrintRoutines(void (*measure)(char far *,word *,word *),void (*print)(char far *)) +{ + USL_MeasureString = measure; + USL_DrawString = print; +} + +/////////////////////////////////////////////////////////////////////////// +// +// US_Print() - Prints a string in the current window. Newlines are +// supported. +// +/////////////////////////////////////////////////////////////////////////// +void +US_Print(char *s) +{ + char c,*se; + word w,h; + + while (*s) + { + se = s; + while ((c = *se) && (c != '\n')) + se++; + *se = '\0'; + + USL_MeasureString(s,&w,&h); + px = PrintX; + py = PrintY; + USL_DrawString(s); + + s = se; + if (c) + { + *se = c; + s++; + + PrintX = WindowX; + PrintY += h; + } + else + PrintX += w; + } +} + +// MDM - (GAMERS EDGE) begin + +/////////////////////////////////////////////////////////////////////////// +// +// US_Printxy() +// +void US_Printxy(word x, word y, char *text) +{ + word orgx, orgy; + + orgx = PrintX; + orgy = PrintY; + +// PrintX = WindowX+x; +// PrintY = WindowY+y; + PrintX = x; + PrintY = y; + US_Print(text); + + PrintX = orgx; + PrintY = orgy; +} + +// MDM - (GAMERS EDGE) end + +/////////////////////////////////////////////////////////////////////////// +// +// US_PrintUnsigned() - Prints an unsigned long +// +/////////////////////////////////////////////////////////////////////////// +void +US_PrintUnsigned(longword n) +{ + char buffer[32]; + + US_Print(ultoa(n,buffer,10)); +} + +/////////////////////////////////////////////////////////////////////////// +// +// US_PrintSigned() - Prints a signed long +// +/////////////////////////////////////////////////////////////////////////// +void +US_PrintSigned(long n) +{ + char buffer[32]; + + US_Print(ltoa(n,buffer,10)); +} + +/////////////////////////////////////////////////////////////////////////// +// +// USL_PrintInCenter() - Prints a string in the center of the given rect +// +/////////////////////////////////////////////////////////////////////////// +void +USL_PrintInCenter(char *s,Rect r) +{ + word w,h, + rw,rh; + + USL_MeasureString(s,&w,&h); + rw = r.lr.x - r.ul.x; + rh = r.lr.y - r.ul.y; + + px = r.ul.x + ((rw - w) / 2); + py = r.ul.y + ((rh - h) / 2); + USL_DrawString(s); +} + +/////////////////////////////////////////////////////////////////////////// +// +// US_PrintCentered() - Prints a string centered in the current window. +// +/////////////////////////////////////////////////////////////////////////// +void +US_PrintCentered(char *s) +{ + Rect r; + + r.ul.x = WindowX; + r.ul.y = WindowY; + r.lr.x = r.ul.x + WindowW; + r.lr.y = r.ul.y + WindowH; + + USL_PrintInCenter(s,r); +} + +/////////////////////////////////////////////////////////////////////////// +// +// US_CPrintLine() - Prints a string centered on the current line and +// advances to the next line. Newlines are not supported. +// +/////////////////////////////////////////////////////////////////////////// +void +US_CPrintLine(char *s) +{ + word w,h; + + USL_MeasureString(s,&w,&h); + + if (w > WindowW) + Quit("US_CPrintLine() - String exceeds width"); + px = WindowX + ((WindowW - w) / 2); + py = PrintY; + USL_DrawString(s); + PrintY += h; +} + +/////////////////////////////////////////////////////////////////////////// +// +// US_CPrint() - Prints a string in the current window. Newlines are +// supported. +// +/////////////////////////////////////////////////////////////////////////// +void +US_CPrint(char *s) +{ + char c,*se; + + while (*s) + { + se = s; + while ((c = *se) && (c != '\n')) + se++; + *se = '\0'; + + US_CPrintLine(s); + + s = se; + if (c) + { + *se = c; + s++; + } + } +} + +/////////////////////////////////////////////////////////////////////////// +// +// US_ClearWindow() - Clears the current window to white and homes the +// cursor +// +/////////////////////////////////////////////////////////////////////////// +void +US_ClearWindow(void) +{ + VWB_Bar(WindowX,WindowY,WindowW,WindowH,LT_GREY); + PrintX = WindowX; + PrintY = WindowY; +} + +/////////////////////////////////////////////////////////////////////////// +// +// US_DrawWindow() - Draws a frame and sets the current window parms +// +/////////////////////////////////////////////////////////////////////////// +void +US_DrawWindow(word x,word y,word w,word h) +{ + word i, + sx,sy,sw,sh; + + WindowX = x * 8; + WindowY = y * 8; + WindowW = w * 8; + WindowH = h * 8; + + PrintX = WindowX; + PrintY = WindowY; + + sx = (x - 1) * 8; + sy = (y - 1) * 8; + sw = (w + 1) * 8; + sh = (h + 1) * 8; + + US_ClearWindow(); + + VWB_DrawTile8M(sx,sy,0),VWB_DrawTile8M(sx,sy + sh,6); + for (i = sx + 8;i <= sx + sw - 8;i += 8) + VWB_DrawTile8M(i,sy,1),VWB_DrawTile8M(i,sy + sh,7); + VWB_DrawTile8M(i,sy,2),VWB_DrawTile8M(i,sy + sh,8); + + for (i = sy + 8;i <= sy + sh - 8;i += 8) + VWB_DrawTile8M(sx,i,3),VWB_DrawTile8M(sx + sw,i,5); +} + +/////////////////////////////////////////////////////////////////////////// +// +// US_CenterWindow() - Generates a window of a given width & height in the +// middle of the screen +// +/////////////////////////////////////////////////////////////////////////// +void +US_CenterWindow(word w,word h) +{ + US_DrawWindow(((MaxX / 8) - w) / 2,((MaxY / 8) - h) / 2,w,h); +} + +/////////////////////////////////////////////////////////////////////////// +// +// US_CenterSaveWindow() - Generates a window of a given width & height in +// the middle of the screen, saving the background +// +/////////////////////////////////////////////////////////////////////////// +void +US_CenterSaveWindow(word w,word h,memptr *save) +{ + word x,y, + screen; + + x = ((MaxX / 8) - w) / 2; + y = ((MaxY / 8) - h) / 2; + MM_GetPtr(save,(w * h) * CHARWIDTH); + screen = bufferofs + panadjust + ylookup[y] + (x * CHARWIDTH); + VW_ScreenToMem(screen,*save,w * CHARWIDTH,h); + US_DrawWindow(((MaxX / 8) - w) / 2,((MaxY / 8) - h) / 2,w,h); +} + +/////////////////////////////////////////////////////////////////////////// +// +// US_RestoreSaveWindow() - Restores the background of the size of the +// current window from the memory specified by save +// +/////////////////////////////////////////////////////////////////////////// +void +US_RestoreSaveWindow(memptr *save) +{ + word screen; + + screen = bufferofs + panadjust + ylookup[WindowY] + (WindowX * CHARWIDTH); + VW_MemToScreen(*save,screen,WindowW * CHARWIDTH,WindowH); + MM_FreePtr(save); +} + +/////////////////////////////////////////////////////////////////////////// +// +// US_SaveWindow() - Saves the current window parms into a record for +// later restoration +// +/////////////////////////////////////////////////////////////////////////// +void +US_SaveWindow(WindowRec *win) +{ + win->x = WindowX; + win->y = WindowY; + win->w = WindowW; + win->h = WindowH; + + win->px = PrintX; + win->py = PrintY; +} + +/////////////////////////////////////////////////////////////////////////// +// +// US_RestoreWindow() - Sets the current window parms to those held in the +// record +// +/////////////////////////////////////////////////////////////////////////// +void +US_RestoreWindow(WindowRec *win) +{ + WindowX = win->x; + WindowY = win->y; + WindowW = win->w; + WindowH = win->h; + + PrintX = win->px; + PrintY = win->py; +} + +// Cursor routines + +#if 0 +/////////////////////////////////////////////////////////////////////////// +// +// US_StartCursor() - Sets up the cursor for User Mgr use +// +/////////////////////////////////////////////////////////////////////////// +void +US_StartCursor(void) +{ + CursorInfo info; + + VW_SetCursor(CURSORARROWSPR); + CursorX = MaxX / 2; + CursorY = MaxY / 2; + VW_MoveCursor(CursorX,CursorY); + VW_ShowCursor(); + + IN_ReadCursor(&info); // Dispose of any accumulated movement +} + +/////////////////////////////////////////////////////////////////////////// +// +// US_ShutCursor() - Cleans up after US_StartCursor() +// +/////////////////////////////////////////////////////////////////////////// +void +US_ShutCursor(void) +{ + VW_HideCursor(); +} + +/////////////////////////////////////////////////////////////////////////// +// +// US_UpdateCursor() - Gets the new cursor position & button states from +// the Input Mgr and tells the View Mgr where the cursor is +// +/////////////////////////////////////////////////////////////////////////// +boolean +US_UpdateCursor(void) +{ + CursorInfo info; + + IN_ReadCursor(&info); + if (info.x || info.y || CursorBad) + { + CursorX += info.x; + if (CursorX >= MaxX) + CursorX = MaxX - 1; + else if (CursorX < 0) + CursorX = 0; + + CursorY += info.y; + if (CursorY >= MaxY) + CursorY = MaxY - 1; + else if (CursorY < 0) + CursorY = 0; + + VW_MoveCursor(CursorX,CursorY); + CursorBad = false; + } + Button0 = info.button0; + Button1 = info.button1; + return(Button0 || Button1); +} +#endif + +// Input routines + +/////////////////////////////////////////////////////////////////////////// +// +// USL_XORICursor() - XORs the I-bar text cursor. Used by US_LineInput() +// +/////////////////////////////////////////////////////////////////////////// +static void +USL_XORICursor(int x,int y,char *s,word cursor) +{ + char buf[MaxString]; + word w,h; + + strcpy(buf,s); + buf[cursor] = '\0'; + USL_MeasureString(buf,&w,&h); + + px = x + w - 1; + py = y; + USL_DrawString("\x80"); +} + +/////////////////////////////////////////////////////////////////////////// +// +// US_LineInput() - Gets a line of user input at (x,y), the string defaults +// to whatever is pointed at by def. Input is restricted to maxchars +// chars or maxwidth pixels wide. If the user hits escape (and escok is +// true), nothing is copied into buf, and false is returned. If the +// user hits return, the current string is copied into buf, and true is +// returned +// +/////////////////////////////////////////////////////////////////////////// +boolean +US_LineInput(int x,int y,char *buf,char *def,boolean escok, + int maxchars,int maxwidth) +{ + boolean redraw, + cursorvis,cursormoved, + done,result; + ScanCode sc; + char c, + s[MaxString],olds[MaxString]; + word i, + cursor, + w,h, + len; + longword lasttime; + + VW_HideCursor(); + + if (def) + strcpy(s,def); + else + *s = '\0'; + *olds = '\0'; + cursor = strlen(s); + cursormoved = redraw = true; + + cursorvis = done = false; + lasttime = TimeCount; + LastASCII = key_None; + LastScan = sc_None; + + while (!done) + { + if (cursorvis) + USL_XORICursor(x,y,s,cursor); + + asm pushf + asm cli + + sc = LastScan; + LastScan = sc_None; + c = LastASCII; + LastASCII = key_None; + + asm popf + + switch (sc) + { + case sc_LeftArrow: + if (cursor) + cursor--; + c = key_None; + cursormoved = true; + break; + case sc_RightArrow: + if (s[cursor]) + cursor++; + c = key_None; + cursormoved = true; + break; + case sc_Home: + cursor = 0; + c = key_None; + cursormoved = true; + break; + case sc_End: + cursor = strlen(s); + c = key_None; + cursormoved = true; + break; + + case sc_Return: + strcpy(buf,s); + done = true; + result = true; + c = key_None; + break; + case sc_Escape: + if (escok) + { + done = true; + result = false; + } + c = key_None; + break; + + case sc_BackSpace: + if (cursor) + { + strcpy(s + cursor - 1,s + cursor); + cursor--; + redraw = true; + } + c = key_None; + cursormoved = true; + break; + case sc_Delete: + if (s[cursor]) + { + strcpy(s + cursor,s + cursor + 1); + redraw = true; + } + c = key_None; + cursormoved = true; + break; + + case 0x4c: // Keypad 5 + case sc_UpArrow: + case sc_DownArrow: + case sc_PgUp: + case sc_PgDn: + case sc_Insert: + c = key_None; + break; + } + + if (c) + { + len = strlen(s); + USL_MeasureString(s,&w,&h); + + if + ( + isprint(c) + && (len < MaxString - 1) + && ((!maxchars) || (len < maxchars)) + && ((!maxwidth) || (w < maxwidth)) + ) + { + for (i = len + 1;i > cursor;i--) + s[i] = s[i - 1]; + s[cursor++] = c; + redraw = true; + } + } + + if (redraw) + { + px = x; + py = y; + USL_DrawString(olds); + strcpy(olds,s); + + px = x; + py = y; + USL_DrawString(s); + + redraw = false; + } + + if (cursormoved) + { + cursorvis = false; + lasttime = TimeCount - TickBase; + + cursormoved = false; + } + if (TimeCount - lasttime > TickBase / 2) + { + lasttime = TimeCount; + + cursorvis ^= true; + } + if (cursorvis) + USL_XORICursor(x,y,s,cursor); + + VW_UpdateScreen(); + } + + if (cursorvis) + USL_XORICursor(x,y,s,cursor); + if (!result) + { + px = x; + py = y; + USL_DrawString(olds); + } + VW_ShowCursor(); + VW_UpdateScreen(); + + IN_ClearKeysDown(); + return(result); +} + diff --git a/16/cawat/ID_US_2.C b/16/cawat/ID_US_2.C new file mode 100644 index 00000000..35828037 --- /dev/null +++ b/16/cawat/ID_US_2.C @@ -0,0 +1,1822 @@ +/* Catacomb Armageddon Source Code + * Copyright (C) 1993-2014 Flat Rock Software + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +// +// ID Engine +// ID_US.c - User Manager - User interface +// v1.1d1 +// By Jason Blochowiak +// Hacked up for Catacomb 3D +// + +#include "ID_HEADS.H" +#pragma hdrstop + +#pragma warn -pia + +// Special imports +extern boolean showscorebox; +#ifdef KEEN +extern boolean oldshooting; +extern ScanCode firescan; +#else + ScanCode firescan; +#endif + +// Global variables + boolean ingame,abortgame,loadedgame; + GameDiff restartgame = gd_Continue; + +// Internal variables +static boolean GameIsDirty, + QuitToDos, + CtlPanelDone; + +// Forward reference prototypes +static void USL_SetupCard(void); + +// Control panel data + +#define CtlPanelSX 74 +#define CtlPanelSY 48 +#define CtlPanelEX 234 +#define CtlPanelEY 150 +#define CtlPanelW (CtlPanelEX - CtlPanelSX) +#define CtlPanelH (CtlPanelEY - CtlPanelSY) + +#define TileBase 92 + +// DEBUG - CGA +#define BackColor 0 +#define HiliteColor (BackColor ^ 12) +#define NohiliteColor (BackColor ^ 4) + +typedef enum + { + uc_None, + uc_Return, + uc_Abort, + uc_Quit, + uc_Loaded, + uc_SEasy, + uc_SNormal, + uc_SHard, + } UComm; +typedef enum + { + uii_Bad, + uii_Button,uii_RadioButton,uii_Folder + } UIType; +typedef enum + { + ui_Normal = 0, + ui_Pushed = 1, + ui_Selected = 2, + ui_Disabled = 4, + ui_Separated = 8 + } UIFlags; +#define UISelectFlags (ui_Pushed | ui_Selected | ui_Disabled) + +typedef enum + { + uic_SetupCard,uic_DrawCard,uic_TouchupCard, + uic_DrawIcon,uic_Draw,uic_Hit + } UserCall; + +typedef struct UserItem + { + UIType type; + UIFlags flags; + ScanCode hotkey; + char *text; + UComm comm; + void far *child; // Should be (UserItemGroup *) + + word x,y; + } UserItem; +typedef struct UserItemGroup + { + word x,y; + graphicnums title; + ScanCode hotkey; + UserItem far *items; + boolean (*custom)(UserCall,struct UserItem far *); // Custom routine + + word cursor; + struct UserItemGroup far *parent; + } UserItemGroup; + +static char *BottomS1,*BottomS2,*BottomS3; +static UComm Communication; +static ScanCode *KeyMaps[] = + { + &KbdDefs[0].button0, + &KbdDefs[0].button1, + &firescan, + &KbdDefs[0].upleft, + &KbdDefs[0].up, + &KbdDefs[0].upright, + &KbdDefs[0].right, + &KbdDefs[0].downright, + &KbdDefs[0].down, + &KbdDefs[0].downleft, + &KbdDefs[0].left + }; + +// Custom routine prototypes +static boolean USL_ConfigCustom(UserCall call,struct UserItem far *item), + USL_KeyCustom(UserCall call,struct UserItem far *item), + USL_KeySCustom(UserCall call,struct UserItem far *item), + USL_Joy1Custom(UserCall call,struct UserItem far *item), + USL_Joy2Custom(UserCall call,struct UserItem far *item), + USL_LoadCustom(UserCall call,struct UserItem far *item), + USL_SaveCustom(UserCall call,struct UserItem far *item), + USL_ScoreCustom(UserCall call,struct UserItem far *item), + USL_CompCustom(UserCall call,struct UserItem far *item); +#ifdef KEEN + USL_TwoCustom(UserCall call,struct UserItem far *item), +#endif +// USL_PongCustom(UserCall call,struct UserItem far *item); + +#define DefButton(key,text) uii_Button,ui_Normal,key,text +#define DefRButton(key,text) uii_RadioButton,ui_Normal,key,text +#define DefFolder(key,text,child) uii_Folder,ui_Normal,key,text,uc_None,child +#define CustomGroup(title,key,custom) 0,0,title,key,0,custom + UserItem far holder[] = + { + {DefButton(sc_None,"DEBUG")}, + {uii_Bad} + }; + UserItemGroup far holdergroup = {0,0,CP_MAINMENUPIC,sc_None,holder}; + + // Sound menu + UserItem far soundi[] = + { + {DefRButton(sc_N,"NO SOUND EFFECTS")}, + {DefRButton(sc_P,"PC SPEAKER")}, + {DefRButton(sc_A,"ADLIB/SOUNDBLASTER")}, + {uii_Bad} + }; + UserItemGroup far soundgroup = {8,0,CP_SOUNDMENUPIC,sc_None,soundi}; + + // Music menu + UserItem far musici[] = + { + {DefRButton(sc_N,"NO MUSIC")}, + {DefRButton(sc_A,"ADLIB/SOUNDBLASTER")}, + {uii_Bad} + }; + UserItemGroup far musicgroup = {8,0,CP_MUSICMENUPIC,sc_None,musici}; + + // New game menu + UserItem far newgamei[] = + { + {DefButton(sc_E,"BEGIN EASY GAME"),uc_SEasy}, + {DefButton(sc_N,"BEGIN NORMAL GAME"),uc_SNormal}, + {DefButton(sc_H,"BEGIN HARD GAME"),uc_SHard}, + {uii_Bad} + }; + UserItemGroup far newgamegroup = {8,0,CP_NEWGAMEMENUPIC,sc_None,newgamei,0,1}; + + // Load/Save game menu + UserItem far loadsavegamei[] = + { + {uii_Button,ui_Normal,sc_None}, + {uii_Button,ui_Normal,sc_None}, + {uii_Button,ui_Normal,sc_None}, + {uii_Button,ui_Normal,sc_None}, + {uii_Button,ui_Normal,sc_None}, + {uii_Button,ui_Normal,sc_None}, + {uii_Bad} + }; + UserItemGroup far loadgamegroup = {4,3,CP_LOADMENUPIC,sc_None,loadsavegamei,USL_LoadCustom}; + UserItemGroup far savegamegroup = {4,3,CP_SAVEMENUPIC,sc_None,loadsavegamei,USL_SaveCustom}; + + // Options menu + UserItemGroup far scoregroup = {0,0,0,sc_None,0,USL_ScoreCustom}; + UserItemGroup far compgroup = {0,0,0,sc_None,0,USL_CompCustom}; +#ifdef KEEN + UserItemGroup far twogroup = {0,0,0,sc_None,0,USL_TwoCustom}; +#endif + UserItem far optionsi[] = + { + {DefFolder(sc_S,"",&scoregroup)}, + {DefFolder(sc_C,"",&compgroup)}, +#ifdef KEEN + {DefFolder(sc_T,"",&twogroup)}, +#endif + {uii_Bad} + }; + UserItemGroup far optionsgroup = {8,0,CP_OPTIONSMENUPIC,sc_None,optionsi}; + + // Keyboard menu + UserItem far keyi[] = + { + {DefButton(sc_None,"UP & LEFT")}, + {DefButton(sc_None,"UP")}, + {DefButton(sc_None,"UP & RIGHT")}, + {DefButton(sc_None,"RIGHT")}, + {DefButton(sc_None,"DOWN & RIGHT")}, + {DefButton(sc_None,"DOWN")}, + {DefButton(sc_None,"DOWN & LEFT")}, + {DefButton(sc_None,"LEFT")}, + {uii_Bad} + }; + UserItemGroup far keygroup = {0,0,CP_KEYMOVEMENTPIC,sc_None,keyi,USL_KeyCustom}; + UserItem far keybi[] = + { +#ifdef KEEN + {DefButton(sc_J,"JUMP")}, + {DefButton(sc_P,"POGO")}, + {DefButton(sc_F,"FIRE")}, +#endif +#ifdef CAT3D + {DefButton(sc_J,"FIRE")}, + {DefButton(sc_P,"STRAFE")}, +#endif +#ifdef CPD + {DefButton(sc_J,"SHOOT")}, + {DefButton(sc_P,"BOMB")}, +#endif + {uii_Bad} + }; + UserItemGroup far keybgroup = {0,0,CP_KEYBUTTONPIC,sc_None,keybi,USL_KeyCustom}; + UserItem far keysi[] = + { + {DefFolder(sc_M,"MOVEMENT",&keygroup)}, + {DefFolder(sc_B,"BUTTONS",&keybgroup)}, + {uii_Bad} + }; + UserItemGroup far keysgroup = {8,0,CP_KEYBOARDMENUPIC,sc_None,keysi,USL_KeySCustom}; + + // Joystick #1 & #2 + UserItemGroup far joy1group = {CustomGroup(CP_JOYSTICKMENUPIC,sc_None,USL_Joy1Custom)}; + UserItemGroup far joy2group = {CustomGroup(CP_JOYSTICKMENUPIC,sc_None,USL_Joy2Custom)}; + + // Config menu + UserItem far configi[] = + { + {DefFolder(sc_S,"SOUND",&soundgroup)}, + {DefFolder(sc_M,"MUSIC",&musicgroup)}, + {uii_Folder,ui_Separated,sc_K,"USE KEYBOARD",uc_None,&keysgroup}, + {DefFolder(sc_None,"USE JOYSTICK #1",&joy1group)}, + {DefFolder(sc_None,"USE JOYSTICK #2",&joy2group)}, + {uii_Bad} + }; + UserItemGroup far configgroup = {8,0,CP_CONFIGMENUPIC,sc_None,configi,USL_ConfigCustom}; + + // Main menu +// UserItemGroup far ponggroup = {0,0,0,sc_None,0,USL_PongCustom}; + UserItem far rooti[] = + { + {DefFolder(sc_N,"NEW GAME",&newgamegroup)}, + {DefFolder(sc_L,"LOAD GAME",&loadgamegroup)}, + {DefFolder(sc_S,"SAVE GAME",&savegamegroup)}, + {DefFolder(sc_C,"CONFIGURE",&configgroup)}, + {DefButton(sc_R,nil),uc_Return}, // Return to Game/Demo + {DefButton(sc_E,"END GAME"),uc_Abort}, +// {DefFolder(sc_B,"SKULL 'N' BONES",&ponggroup)}, + {DefButton(sc_Q,"QUIT"),uc_Quit}, + {uii_Bad} + }; + UserItemGroup far rootgroup = {32,4,CP_MAINMENUPIC,sc_None,rooti}; +#undef DefButton +#undef DefFolder + +#define MaxCards 7 + word cstackptr; + UserItemGroup far *cardstack[MaxCards], + far *topcard; + +// Card stack code +static void +USL_SetupStack(void) +{ + cstackptr = 0; + cardstack[0] = topcard = &rootgroup; +} + +static void +USL_PopCard(void) +{ + if (!cstackptr) + return; + + topcard = cardstack[--cstackptr]; +} + +static void +USL_PushCard(UserItemGroup far *card) +{ + if (cstackptr == MaxCards - 1) + return; + + topcard = cardstack[++cstackptr] = card; +} + +static void +USL_DrawItemIcon(UserItem far *item) +{ + word flags,tile; + + if (topcard->custom && topcard->custom(uic_DrawIcon,item)) + return; + + flags = item->flags; + if (flags & ui_Disabled) + tile = TileBase + ((flags & ui_Selected)? 5 : 4); + else if ((item->type == uii_RadioButton) && (!(flags & ui_Pushed))) + tile = TileBase + ((flags & ui_Selected)? 3 : 2); + else + tile = TileBase + ((flags & ui_Selected)? 1 : 0); + VWB_DrawTile8(item->x,item->y,tile); +} + +static void +USL_DrawItem(UserItem far *item) +{ + if (topcard->custom && topcard->custom(uic_Draw,item)) + return; + + VWB_Bar(CtlPanelSX + 1,item->y, + CtlPanelEX - CtlPanelSX - 1,8,BackColor); // Clear out background + USL_DrawItemIcon(item); + if ((item->flags & ui_Selected) && !(item->flags & ui_Disabled)) + fontcolor = HiliteColor; + else + fontcolor = NohiliteColor; + px = item->x + 8; + py = item->y + 1; + USL_DrawString(item->text); + fontcolor = F_BLACK; +} + +#define MyLine(y) VWB_Hlin(CtlPanelSX + 3,CtlPanelEX - 3,y,12); + +static void +USL_DrawBottom(void) +{ + word w,h; + + fontcolor = NohiliteColor; + + px = CtlPanelSX + 4; + py = CtlPanelEY - 15; + USL_DrawString(BottomS1); + + USL_MeasureString(BottomS2,&w,&h); + px = CtlPanelEX - 4 - w; + USL_DrawString(BottomS2); + + USL_MeasureString(BottomS3,&w,&h); + px = CtlPanelSX + ((CtlPanelEX - CtlPanelSX - w) / 2); + py += h + 1; + USL_DrawString(BottomS3); + + fontcolor = F_WHITE; + MyLine(CtlPanelEY - 17); +} + +static void +USL_DrawCtlPanelContents(void) +{ + int x,y; + UserItem far *item; + + if (topcard->custom && topcard->custom(uic_DrawCard,nil)) + return; + + if (topcard->title) + { + // Draw the title + MyLine(CtlPanelSY + 7); + VWB_DrawPic(CtlPanelSX + 6,CtlPanelSY,topcard->title); + } + + USL_DrawBottom(); + + if (!topcard->items) + return; + + x = topcard->x + CtlPanelSX; + if (x % 8) + x += 8 - (x % 8); + y = topcard->y + CtlPanelSY + 12; + for (item = topcard->items;item->type != uii_Bad;item++) + { + if (item->flags & ui_Separated) + y += 8; + + item->x = x; + item->y = y; + USL_DrawItem(item); + y += 8; + } + if (topcard->custom) + topcard->custom(uic_TouchupCard,nil); +} + +static void +USL_DrawCtlPanel(void) +{ + if (topcard->items || topcard->title) + { + // Draw the backdrop + VWB_DrawPic(0,0,CP_MENUSCREENPIC); + + // Draw the contents + USL_DrawCtlPanelContents(); + } + + // Refresh the screen + VW_UpdateScreen(); +} + +static void +USL_DialogSetup(word w,word h,word *x,word *y) +{ + VWB_DrawMPic(CtlPanelSX,CtlPanelSY,CP_MENUMASKPICM); + + *x = CtlPanelSX + ((CtlPanelW - w) / 2); + *y = CtlPanelSY + ((CtlPanelH - h) / 2); + VWB_Bar(*x,*y,w + 1,h + 1,BackColor); + VWB_Hlin(*x - 1,*x + w + 1,*y - 1,NohiliteColor); + VWB_Hlin(*x - 1,*x + w + 1,*y + h + 1,NohiliteColor); + VWB_Vlin(*y - 1,*y + h + 1,*x - 1,NohiliteColor); + VWB_Vlin(*y - 1,*y + h + 1,*x + w + 1,NohiliteColor); +} + +static void +USL_ShowLoadSave(char *s,char *name) +{ + word x,y, + w,h, + tw,sw; + char msg[MaxGameName + 4]; + + strcpy(msg,"'"); + strcat(msg,name); + strcat(msg,"'"); + USL_MeasureString(s,&sw,&h); + USL_MeasureString(msg,&w,&h); + tw = ((sw > w)? sw : w) + 6; + USL_DialogSetup(tw,(h * 2) + 2,&x,&y); + py = y + 2; + px = x + ((tw - sw) / 2); + USL_DrawString(s); + py += h; + px = x + ((tw - w) / 2); + USL_DrawString(msg); + + VW_UpdateScreen(); + + IN_UserInput(100, true); +} + +static boolean +USL_CtlDialog(char *s1,char *s2,char *s3) +{ + word w,h,sh, + w1,w2,w3, + x,y; + ScanCode c; + CursorInfo cursorinfo; + + USL_MeasureString(s1,&w1,&h); + USL_MeasureString(s2,&w2,&h); + if (s3) + USL_MeasureString(s3,&w3,&h); + else + w3 = 0; + w = (w1 > w2)? ((w1 > w3)? w1 : w3) : ((w2 > w3)? w2 : w3); + w += 7; + sh = h; + h *= s3? 5 : 4; + + USL_DialogSetup(w,h,&x,&y); + + fontcolor = HiliteColor; + px = x + ((w - w1) / 2); + py = y + sh + 1; + USL_DrawString(s1); + py += (sh * 2) - 1; + + VWB_Hlin(x + 3,x + w - 3,py,NohiliteColor); + py += 2; + + fontcolor = NohiliteColor; + px = x + ((w - w2) / 2); + USL_DrawString(s2); + py += sh; + + if (s3) + { + px = x + ((w - w3) / 2); + USL_DrawString(s3); + } + + VW_UpdateScreen(); + + IN_ClearKeysDown(); + do + { + IN_ReadCursor(&cursorinfo); + if (cursorinfo.button0) + c = sc_Y; + else if (cursorinfo.button1) + c = sc_Escape; + else + c = LastScan; + } while (c == sc_None); + do + { + IN_ReadCursor(&cursorinfo); + } while (cursorinfo.button0 || cursorinfo.button1); + + IN_ClearKeysDown(); + USL_DrawCtlPanel(); + return(c == sc_Y); +} + +static boolean +USL_ConfirmComm(UComm comm) +{ + boolean confirm,dialog; + char *s1,*s2,*s3; + + if (!comm) + Quit("USL_ConfirmComm() - empty comm"); + + confirm = true; + dialog = false; + s3 = "ESC TO BACK OUT"; + switch (comm) + { + case uc_Abort: + s1 = "REALLY END CURRENT GAME?"; + s2 = "PRESS Y TO END IT"; + if (ingame && GameIsDirty) + dialog = true; + break; + case uc_Quit: + s1 = "REALLY QUIT?"; + s2 = "PRESS Y TO QUIT"; + dialog = true; + break; + case uc_Loaded: + s1 = "YOU'RE IN A GAME"; + s2 = "PRESS Y TO LOAD GAME"; + if (ingame && GameIsDirty) + dialog = true; + break; + case uc_SEasy: + case uc_SNormal: + case uc_SHard: + s1 = "YOU'RE IN A GAME"; + s2 = "PRESS Y FOR NEW GAME"; + if (ingame && GameIsDirty) + dialog = true; + break; + } + + confirm = dialog? USL_CtlDialog(s1,s2,s3) : true; + if (confirm) + { + Communication = comm; + CtlPanelDone = true; + } + return(confirm); +} + +/////////////////////////////////////////////////////////////////////////// +// +// USL_HandleError() - Handles telling the user that there's been an error +// +/////////////////////////////////////////////////////////////////////////// +static void +USL_HandleError(int num) +{ + char buf[64]; + + strcpy(buf,"Error: "); + if (num < 0) + strcat(buf,"Unknown"); + else if (num == ENOMEM) + strcat(buf,"Disk is Full"); + else if (num == EINVFMT) + strcat(buf,"File is Incomplete"); + else + strcat(buf,sys_errlist[num]); + + VW_HideCursor(); + + USL_CtlDialog(buf,"PRESS ANY KEY",nil); + VW_UpdateScreen(); + + IN_ClearKeysDown(); + IN_Ack(); + + VW_ShowCursor(); + VW_UpdateScreen(); +} + +// Custom routines +#if 0 +static boolean +USL_GenericCustom(UserCall call,UserItem far *item) +{ + boolean result; + + result = false; + switch (call) + { + } + return(result); +} +#endif + +static void +USL_SetOptionsText(void) +{ + optionsi[0].text = showscorebox? "SCORE BOX (ON)" : "SCORE BOX (OFF)"; + optionsi[1].text = compatability? "SVGA COMPATIBILITY (ON)" : "SVGA COMPATIBILITY (OFF)"; +#ifdef KEEN + optionsi[2].text = oldshooting? "TWO-BUTTON FIRING (ON)" : "TWO-BUTTON FIRING (OFF)"; + + keybi[2].flags &= ~ui_Disabled; + if (oldshooting) + keybi[2].flags |= ui_Disabled; +#endif +} + +#pragma argsused +static boolean +USL_ScoreCustom(UserCall call,UserItem far *item) +{ + if (call != uic_SetupCard) + return(false); + + showscorebox ^= true; + USL_CtlDialog(showscorebox? "Score box now on" : "Score box now off", + "Press any key",nil); + USL_SetOptionsText(); + return(true); +} + +#pragma argsused +static boolean +USL_CompCustom(UserCall call,UserItem far *item) +{ + if (call != uic_SetupCard) + return(false); + + compatability ^= true; + USL_CtlDialog(compatability? "SVGA compatibility now on" : "SVGA compatibility now off", + "Press any key",nil); + USL_SetOptionsText(); + return(true); +} + +#ifdef KEEN +#pragma argsused +static boolean +USL_TwoCustom(UserCall call,UserItem far *item) +{ + if (call != uic_SetupCard) + return(false); + + oldshooting ^= true; + USL_CtlDialog(oldshooting? "Two-button firing now on" : "Two-button firing now off", + "Press any key",nil); + USL_SetOptionsText(); + return(true); +} +#endif + +static boolean +USL_ConfigCustom(UserCall call,UserItem far *item) +{ +static char *CtlNames[] = {"KEYBOARD","KEYBOARD","JOYSTICK #1","JOYSTICK #2","MOUSE"}; + char *s; + word w,h, + tw; + + if (call == uic_TouchupCard) + { + s = "CONTROL: "; + USL_MeasureString(s,&w,&h); + tw = w; + USL_MeasureString(CtlNames[Controls[0]],&w,&h); + tw += w; + py = CtlPanelEY - 18 - h; + px = CtlPanelSX + ((CtlPanelW - tw) / 2); + fontcolor = NohiliteColor; + USL_DrawString(s); + USL_DrawString(CtlNames[Controls[0]]); + } + item++; // Shut the compiler up + return(false); +} + +static void +USL_CKSetKey(UserItem far *item,word i) +{ + boolean on; + word j; + ScanCode scan; + longword time; + CursorInfo cursorinfo; + + on = false; + time = 0; + LastScan = sc_None; + fontcolor = HiliteColor; + do + { + if (TimeCount >= time) + { + on ^= true; + VWB_Bar(item->x + 90,item->y,40,8,fontcolor ^ BackColor); + VWB_Bar(item->x + 90 + 1,item->y + 1,40 - 2,8 - 2,BackColor); + if (on) + VWB_DrawTile8(item->x + 90 + 16,item->y,TileBase + 8); + VW_UpdateScreen(); + + time = TimeCount + (TickBase / 2); + } + + IN_ReadCursor(&cursorinfo); + while (cursorinfo.button0 || cursorinfo.button1) + { + IN_ReadCursor(&cursorinfo); + LastScan = sc_Escape; + } + + asm pushf + asm cli + if (LastScan == sc_LShift) + LastScan = sc_None; + asm popf + } while (!(scan = LastScan)); + + if (scan != sc_Escape) + { + for (j = 0,on = false;j < 11;j++) + { + if (j == i) + continue; + if (*(KeyMaps[j]) == scan) + { + on = true; + break; + } + } + if (on) + USL_CtlDialog("Key already used","Press a key",nil); + else + *(KeyMaps[i]) = scan; + } + IN_ClearKeysDown(); +} + +#pragma argsused +static boolean +USL_KeySCustom(UserCall call,UserItem far *item) +{ + if (call == uic_SetupCard) + Controls[0] = ctrl_Keyboard; + return(false); +} + +#pragma argsused +static boolean +USL_KeyCustom(UserCall call,UserItem far *item) +{ + boolean result; + word i; + + result = false; + i = (topcard == &keygroup)? (3 + (item - keyi)) : (item - keybi); + switch (call) + { + case uic_SetupCard: + Controls[0] = ctrl_Keyboard; + break; + case uic_Draw: + VWB_Bar(CtlPanelSX + 1,item->y, + CtlPanelEX - CtlPanelSX - 1,8,BackColor); // Clear out background + USL_DrawItemIcon(item); + fontcolor = (item->flags & ui_Selected)? HiliteColor : NohiliteColor; + px = item->x + 8; + py = item->y + 1; + USL_DrawString(item->text); + VWB_Bar(item->x + 90,item->y,40,8,fontcolor ^ BackColor); + VWB_Bar(item->x + 90 + 1,item->y + 1,40 - 2,8 - 2,BackColor); + px = item->x + 90 + 6; + py = item->y + 1; + USL_DrawString(IN_GetScanName(*KeyMaps[i])); + result = true; + break; + case uic_Hit: + USL_KeyCustom(uic_Draw,item); + USL_CKSetKey(item,i); + USL_DrawCtlPanel(); + result = true; + break; + } + return(result); +} + +static void +USL_CJDraw(char *s1,char *s2) +{ + word w,h; + + USL_MeasureString(s1,&w,&h); + px = CtlPanelSX + ((CtlPanelW - w) / 2); + py = CtlPanelEY - 34; + VWB_Bar(CtlPanelSX + 1,py,CtlPanelW - 2,h * 2,BackColor); + fontcolor = HiliteColor; + USL_DrawString(s1); + py += h; + USL_MeasureString(s2,&w,&h); + px = CtlPanelSX + ((CtlPanelW - w) / 2); + USL_DrawString(s2); +} + +static boolean +USL_CJGet(word joy,word button,word x,word y,word *xaxis,word *yaxis) +{ + boolean on; + longword time; + + while (IN_GetJoyButtonsDB(joy)) + if (LastScan == sc_Escape) + return(false); + + on = false; + time = 0; + while (!(IN_GetJoyButtonsDB(joy) & (1 << button))) + { + if (TimeCount >= time) + { + on ^= true; + time = TimeCount + (TickBase / 2); + VWB_DrawTile8(x,y,TileBase + on); + VW_UpdateScreen(); + } + + if (LastScan == sc_Escape) + return(false); + } + IN_GetJoyAbs(joy,xaxis,yaxis); + return(true); +} + +static boolean +USL_ConfigJoystick(word joy) +{ + word x,y, + minx,miny, + maxx,maxy; + + BottomS1 = BottomS2 = ""; + BottomS3 = "Esc to back out"; + USL_DrawCtlPanel(); + x = CtlPanelSX + 60; + y = CtlPanelSY + 19; + VWB_DrawPic(x,y,CP_JOYSTICKPIC); + + USL_CJDraw("Move Joystick to upper left","and press button #1"); + VWB_DrawTile8(x + 24,y + 8,TileBase + 6); + VWB_DrawTile8(x + 8,y + 8,TileBase + 1); + VWB_DrawTile8(x + 8,y + 24,TileBase + 0); + VW_UpdateScreen(); + if (!USL_CJGet(joy,0,x + 8,y + 8,&minx,&miny)) + return(false); + + USL_CJDraw("Move Joystick to lower right","and press button #2"); + VWB_DrawTile8(x + 24,y + 8,TileBase - 25); + VWB_DrawTile8(x + 40,y + 24,TileBase + 7); + VWB_DrawTile8(x + 8,y + 8,TileBase + 0); + VWB_DrawTile8(x + 8,y + 24,TileBase + 1); + VW_UpdateScreen(); + if (!USL_CJGet(joy,1,x + 8,y + 24,&maxx,&maxy)) + return(false); + + while (IN_GetJoyButtonsDB(joy)) + ; + + IN_SetupJoy(joy,minx,maxx,miny,maxy); + return(true); +} + +#pragma argsused +static boolean +USL_Joy1Custom(UserCall call,UserItem far *item) +{ + if (call == uic_SetupCard) + { + if (USL_ConfigJoystick(0)) + { + Controls[0] = ctrl_Joystick1; + USL_CtlDialog("USING JOYSTICK #1","PRESS ANY KEY",nil); + } + return(true); + } + else + return(false); +} + +#pragma argsused +static boolean +USL_Joy2Custom(UserCall call,UserItem far *item) +{ + if (call == uic_SetupCard) + { + if (USL_ConfigJoystick(1)) + { + Controls[0] = ctrl_Joystick2; + USL_CtlDialog("USING JOYSTICK #2","PRESS ANY KEY",nil); + } + return(true); + } + else + return(false); +} + +static void +USL_DrawFileIcon(UserItem far *item) +{ + word color; + + item->y = topcard->y + CtlPanelSY + 12; + item->y += (item - loadsavegamei) * 11; + + fontcolor = (item->flags & ui_Selected)? HiliteColor : NohiliteColor; + color = fontcolor ^ BackColor; // Blech! + VWB_Hlin(item->x,item->x + (CtlPanelW - 12),item->y,color); + VWB_Hlin(item->x,item->x + (CtlPanelW - 12),item->y + 9,color); + VWB_Vlin(item->y,item->y + 9,item->x,color); + VWB_Vlin(item->y,item->y + 9,item->x + (CtlPanelW - 12),color); +} + +static void +USL_DoLoadGame(UserItem far *item) +{ + char *filename; + word n, + err; + int file; + SaveGame *game; + + if (!USL_ConfirmComm(uc_Loaded)) + return; + + n = item - loadsavegamei; + game = &Games[n]; + + USL_ShowLoadSave("Loading",game->name); + + err = 0; + filename = USL_GiveSaveName(n); + if ((file = open(filename,O_BINARY | O_RDONLY)) != -1) + { + if (read(file,game,sizeof(*game)) == sizeof(*game)) + { + if (USL_LoadGame) + if (!USL_LoadGame(file)) + USL_HandleError(err = errno); + } + else + USL_HandleError(err = errno); + close(file); + } + else + USL_HandleError(err = errno); + if (err) + { + abortgame = true; + Communication = uc_None; + CtlPanelDone = false; + } + else + loadedgame = true; + game->present = true; + + if (loadedgame) + Paused = true; + + USL_DrawCtlPanel(); +} + +static boolean +USL_LoadCustom(UserCall call,UserItem far *item) +{ + boolean result; + word i; + + result = false; + switch (call) + { + case uic_SetupCard: + for (i = 0;i < MaxSaveGames;i++) + { + if (Games[i].present) + loadsavegamei[i].flags &= ~ui_Disabled; + else + loadsavegamei[i].flags |= ui_Disabled; + } + break; + case uic_DrawIcon: + USL_DrawFileIcon(item); + result = true; + break; + case uic_Draw: + USL_DrawFileIcon(item); + VWB_Bar(item->x + 1,item->y + 2,CtlPanelW - 12 - 2,7,BackColor); + + i = item - loadsavegamei; + if (Games[i].present) + px = item->x + 2; + else + px = item->x + 60; + py = item->y + 2; + USL_DrawString(Games[i].present? Games[i].name : "Empty"); + result = true; + break; + case uic_Hit: + USL_DoLoadGame(item); + result = true; + break; + } + return(result); +} + +static void +USL_DoSaveGame(UserItem far *item) +{ + boolean ok; + char *filename; + word n,err; + int file; + SaveGame *game; + + BottomS1 = "Type name"; + BottomS2 = "Enter accepts"; + USL_DrawCtlPanel(); + + n = item - loadsavegamei; + game = &Games[n]; + fontcolor = HiliteColor; + VWB_Bar(item->x + 1,item->y + 2,CtlPanelW - 12 - 2,7,BackColor); + game->oldtest = &PrintX; + ok = US_LineInput(item->x + 2,item->y + 2, + game->name,game->present? game->name : nil, + true,MaxGameName, + CtlPanelW - 22); + if (!strlen(game->name)) + strcpy(game->name,"Untitled"); + if (ok) + { + USL_ShowLoadSave("Saving",game->name); + + filename = USL_GiveSaveName(n); + err = 0; + file = open(filename,O_CREAT | O_BINARY | O_WRONLY, + S_IREAD | S_IWRITE | S_IFREG); + if (file != -1) + { + if (write(file,game,sizeof(*game)) == sizeof(*game)) + { + if (USL_SaveGame) + ok = USL_SaveGame(file); + if (!ok) + USL_HandleError(err = errno); + } + else + USL_HandleError(err = ((errno == ENOENT)? ENOMEM : errno)); + close(file); + } + else + USL_HandleError(err = ((errno == ENOENT)? ENOMEM : errno)); + if (err) + { + remove(filename); + ok = false; + } + + } + + if (!game->present) + game->present = ok; + + if (ok) + GameIsDirty = false; + USL_SetupCard(); +} + +static boolean +USL_SaveCustom(UserCall call,UserItem far *item) +{ + word i; + + switch (call) + { + case uic_SetupCard: + for (i = 0;i < MaxSaveGames;i++) + loadsavegamei[i].flags &= ~ui_Disabled; + return(false); + case uic_Hit: + USL_DoSaveGame(item); + return(true); +// break; + } + return(USL_LoadCustom(call,item)); +} + +#if 0 + +#define PaddleMinX (CtlPanelSX + 3) +#define PaddleMaxX (CtlPanelEX - 15) +#define BallMinX (CtlPanelSX + 2) +#define BallMinY (CtlPanelSY + 12 + 2) +#define BallMaxX (CtlPanelEX - 6) +#define BallMaxY (CtlPanelEY - 13) +#define CPaddleY (BallMinY + 4) +#define KPaddleY (BallMaxY - 2) +void +USL_DrawPongScore(word k,word c) +{ + fontcolor = HiliteColor; + PrintY = py = CtlPanelSY + 4; + px = CtlPanelSX + 6; + VWB_Bar(px,py,42,6,BackColor); + USL_DrawString("YOU:"); + PrintX = px; + US_PrintUnsigned(k); + px = CtlPanelSX + 108; + VWB_Bar(px,py,50,6,BackColor); + USL_DrawString("COMP:"); + PrintX = px; + US_PrintUnsigned(c); +} + +void +USL_PlayPong(void) +{ + boolean ball,killball,revdir,done,lastscore; + word cycle, + x,y, + kx,cx, + rx, + bx,by, + kscore,cscore, + speedup; + int bdx,bdy; + longword balltime,waittime; + CursorInfo cursorinfo; + + kx = cx = PaddleMinX + ((PaddleMaxX - PaddleMinX) / 2); + bx = by = bdx = bdy = 0; + kscore = cscore = 0; + USL_DrawPongScore(0,0); + cycle = 0; + revdir = false; + killball = true; + done = false; + lastscore = false; + do + { + waittime = TimeCount; + + IN_ReadCursor(&cursorinfo); + if (((cursorinfo.x < 0) || IN_KeyDown(sc_LeftArrow)) && (kx > PaddleMinX)) + kx -= 2; + else if (((cursorinfo.x > 0) || IN_KeyDown(sc_RightArrow)) && (kx < PaddleMaxX)) + kx += 2; + + if (killball) + { + ball = false; + balltime = TimeCount + TickBase; + speedup = 10; + killball = false; + } + + if (ball && (cycle++ % 3)) + { + x = (bx >> 2); + if (!(x & 1)) + x += (US_RndT() & 1); + + if ((cx + 6 < x) && (cx < PaddleMaxX)) + cx += 1; + else if ((cx + 6 > x) && (cx > PaddleMinX)) + cx -= 1; + } + + VWB_Bar(BallMinX,BallMinY - 1, + BallMaxX - BallMinX + 5,BallMaxY - BallMinY + 7, + BackColor); + VWB_DrawSprite(cx,CPaddleY,PADDLESPR); + VWB_DrawSprite(kx,KPaddleY,PADDLESPR); + if (ball) + { + if + ( + (((bx + bdx) >> 2) > BallMaxX) + || (((bx + bdx) >> 2) < BallMinX) + ) + { + SD_PlaySound(BALLBOUNCESND); + bdx = -bdx; + } + bx += bdx; + + if (((by + bdy) >> 2) > BallMaxY) + { + killball = true; + lastscore = false; + cscore++; + SD_PlaySound(COMPSCOREDSND); + USL_DrawPongScore(kscore,cscore); + if (cscore == 21) + { + USL_CtlDialog("You lost!","Press any key",nil); + done = true; + continue; + } + } + else if (((by + bdy) >> 2) < BallMinY) + { + killball = true; + lastscore = true; + kscore++; + SD_PlaySound(KEENSCOREDSND); + USL_DrawPongScore(kscore,cscore); + if (kscore == 21) + { + USL_CtlDialog("You won!","Press any key",nil); + done = true; + continue; + } + } + by += bdy; + + x = bx >> 2; + y = by >> 2; + if (!killball) + { + if + ( + (bdy < 0) + && ((y >= CPaddleY) && (y < CPaddleY + 3)) + && ((x >= (cx - 5)) && (x < (cx + 11))) + ) + { + rx = cx; + revdir = true; + SD_PlaySound(COMPPADDLESND); + } + else if + ( + (bdy > 0) + && ((y >= (KPaddleY - 3)) && (y < KPaddleY)) + && ((x >= (kx - 5)) && (x < (kx + 11))) + ) + { + if (((bdy >> 2) < 3) && !(--speedup)) + { + bdy++; + speedup = 10; + } + rx = kx; + revdir = true; + SD_PlaySound(KEENPADDLESND); + } + if (revdir) + { + bdy = -bdy; + bdx = ((x + 5 - rx) >> 1) - (1 << 2); + if (!bdx) + bdx--; + revdir = false; + } + } + VWB_DrawSprite(x,y,(x & 1)? BALL1PIXELTOTHERIGHTSPR : BALLSPR); + } + else if (TimeCount >= balltime) + { + ball = true; + bdx = 1 - (US_RndT() % 3); + bdy = 2; + if (lastscore) + bdy = -bdy; + bx = (BallMinX + ((BallMaxX - BallMinX) / 2)) << 2; + by = (BallMinY + ((BallMaxY - BallMinY) / 2)) << 2; + } + VW_UpdateScreen(); + while (waittime == TimeCount) + ; // DEBUG - do adaptiveness + } while ((LastScan != sc_Escape) && !done); + IN_ClearKeysDown(); +} + +#pragma argsused +static boolean +USL_PongCustom(UserCall call,struct UserItem far *item) +{ + if (call != uic_SetupCard) + return(false); + + VWB_DrawPic(0,0,CP_MENUSCREENPIC); + VWB_DrawPic(CtlPanelSX + 56,CtlPanelSY,CP_PADDLEWARPIC); + VWB_Hlin(CtlPanelSX + 3,CtlPanelEX - 3,CtlPanelSY + 12,HiliteColor ^ BackColor); + VWB_Hlin(CtlPanelSX + 3,CtlPanelEX - 3,CtlPanelEY - 7,HiliteColor ^ BackColor); + USL_PlayPong(); + + return(true); +} + +#endif + +// Flag management stuff +static void +USL_ClearFlags(UserItemGroup far *node) +{ + UserItem far *i; + + if (!node->items) + return; + + for (i = node->items;i->type != uii_Bad;i++) + { + i->flags &= ~UISelectFlags; + if (i->child) + USL_ClearFlags((UserItemGroup far *)i->child); + } +} + +static int +USL_FindPushedItem(UserItemGroup far *group) +{ + word i; + UserItem far *item; + + for (item = group->items,i = 0;item->type != uii_Bad;item++,i++) + if (item->flags & ui_Pushed) + return(i); + return(-1); +} + +static void +USL_SelectItem(UserItemGroup far *group,word index,boolean draw) +{ + UserItem far *item; + + if (index != group->cursor) + { + item = &group->items[group->cursor]; + item->flags &= ~ui_Selected; + if (draw) + USL_DrawItem(item); + } + + group->cursor = index; + item = &group->items[group->cursor]; + group->items[group->cursor].flags |= ui_Selected; + if (draw) + USL_DrawItem(item); +} + +static void +USL_PushItem(UserItemGroup far *group,word index,boolean draw) +{ + word i; + UserItem far *item; + + USL_SelectItem(group,index,draw); + for (item = group->items,i = 0;item->type != uii_Bad;item++,i++) + { + if (item->type != uii_RadioButton) + continue; + + if (i == index) + { + item->flags |= ui_Pushed; + if (draw) + USL_DrawItem(item); + } + else if (item->flags & ui_Pushed) + { + item->flags &= ~ui_Pushed; + if (draw) + USL_DrawItem(item); + } + } +} + +static void +USL_NextItem(void) +{ + if (topcard->items[topcard->cursor + 1].type == uii_Bad) + return; + USL_SelectItem(topcard,topcard->cursor + 1,true); +} + +static void +USL_PrevItem(void) +{ + if (!topcard->cursor) + return; + USL_SelectItem(topcard,topcard->cursor - 1,true); +} + +static void +USL_SetupCard(void) +{ + BottomS1 = "Arrows move"; + BottomS2 = "Enter selects"; + BottomS3 = cstackptr? "ESC to back out" : "ESC to quit"; + + USL_SelectItem(topcard,topcard->cursor,false); + USL_DrawCtlPanel(); // Contents? +} + +static void +USL_DownLevel(UserItemGroup far *group) +{ + if (!group) + Quit("USL_DownLevel() - nil card"); + USL_PushCard(group); + if (group->custom && group->custom(uic_SetupCard,nil)) + USL_PopCard(); + USL_SetupCard(); +} + +static void +USL_UpLevel(void) +{ + if (!cstackptr) + { + USL_ConfirmComm(uc_Quit); + return; + } + + if (topcard->items) + topcard->items[topcard->cursor].flags &= ~ui_Selected; + USL_PopCard(); + USL_SetupCard(); +} + +static void +USL_DoItem(void) +{ + // DEBUG - finish this routine + UserItem far *item; + + item = &topcard->items[topcard->cursor]; + if (item->flags & ui_Disabled) + SD_PlaySound(NOWAYSND); + else + { + switch (item->type) + { + case uii_Button: + if (!(topcard->custom && topcard->custom(uic_Hit,item))) + USL_ConfirmComm(item->comm); + break; + case uii_RadioButton: + USL_PushItem(topcard,topcard->cursor,true); + break; + case uii_Folder: + USL_DownLevel(item->child); + break; + } + } +} + +static void +USL_SetControlValues(void) +{ + USL_PushItem(&soundgroup,SoundMode,false); + USL_PushItem(&musicgroup,MusicMode,false); + if (!AdLibPresent) + { + soundi[2].flags |= ui_Disabled; // AdLib sound effects + musici[1].flags |= ui_Disabled; // AdLib music + } + + if (!JoysPresent[0]) + configi[3].flags |= ui_Disabled; + if (!JoysPresent[1]) + configi[4].flags |= ui_Disabled; + + rooti[4].text = ingame? "RETURN TO GAME" : "RETURN TO DEMO"; + if (!ingame) + { + rooti[2].flags |= ui_Disabled; // Save Game + rooti[5].flags |= ui_Disabled; // End Game + } + rootgroup.cursor = ingame? 4 : 0; + USL_SetOptionsText(); + // DEBUG - write the rest of this +} + +/////////////////////////////////////////////////////////////////////////// +// +// USL_SetUpCtlPanel() - Sets the states of the UserItems to reflect the +// values of all the appropriate variables +// +/////////////////////////////////////////////////////////////////////////// +static void +USL_SetUpCtlPanel(void) +{ + int i; + + // Cache in all of the stuff for the control panel + CA_UpLevel(); + for (i = CONTROLS_LUMP_START;i <= CONTROLS_LUMP_END;i++) + CA_MarkGrChunk(i); +// for (i = PADDLE_LUMP_START;i <= PADDLE_LUMP_END;i++) +// CA_MarkGrChunk(i); + CA_MarkGrChunk(STARTFONT+1); // Little font + CA_MarkGrChunk(CP_MENUMASKPICM); // Mask for dialogs + CA_CacheMarks("Control Panel"); + CA_LoadAllSounds(); + + // Do some other setup + fontnumber = 1; + US_SetPrintRoutines(VW_MeasurePropString,VWB_DrawPropString); + fontcolor = F_BLACK; + VW_Bar (0,0,320,200,3); // CAT3D patch + RF_FixOfs(); + VW_InitDoubleBuffer(); + + Communication = uc_None; + USL_ClearFlags(&rootgroup); + USL_SetControlValues(); + USL_SetupStack(); + USL_SetupCard(); + + if (ingame) + GameIsDirty = true; + + IN_ClearKeysDown(); +} + +static void +USL_HandleComm(UComm comm) +{ + switch (comm) + { + case uc_Loaded: + case uc_Return: + break; + case uc_Abort: + abortgame = true; + break; + case uc_Quit: + QuitToDos = true; + break; + case uc_SEasy: + restartgame = gd_Easy; + break; + case uc_SNormal: + restartgame = gd_Normal; + break; + case uc_SHard: + restartgame = gd_Hard; + break; + + default: + Quit("USL_HandleComm() - unknown"); + break; + } +} + +static void +USL_GetControlValues(void) +{ + int i; + + // DEBUG - write the rest of this + i = USL_FindPushedItem(&soundgroup); + if (i != SoundMode) + SD_SetSoundMode(i); + + i = USL_FindPushedItem(&musicgroup); + if (i != MusicMode) + SD_SetMusicMode(i); +} + +/////////////////////////////////////////////////////////////////////////// +// +// USL_TearDownCtlPanel() - Given the state of the control panel, sets the +// modes and values as appropriate +// +/////////////////////////////////////////////////////////////////////////// +static void +USL_TearDownCtlPanel(void) +{ + USL_GetControlValues(); + if (Communication) + USL_HandleComm(Communication); + + fontnumber = 0; // Normal font + fontcolor = F_BLACK; + if (restartgame && USL_ResetGame) + USL_ResetGame(); + else if (QuitToDos) + { + if (tedlevel) + TEDDeath(); + else + { + US_CenterWindow(20,3); + fontcolor = F_SECONDCOLOR; + US_PrintCentered("Quitting..."); + fontcolor = F_BLACK; + VW_UpdateScreen(); + Quit(nil); + } + } + + IN_ClearKeysDown(); + SD_WaitSoundDone(); + VW_Bar (0,0,320,200,3); // CAT3D patch + CA_DownLevel(); + CA_LoadAllSounds(); +} + +/////////////////////////////////////////////////////////////////////////// +// +// US_ControlPanel() - This is the main routine for the control panel +// +/////////////////////////////////////////////////////////////////////////// +#define MoveMin 40 +void +US_ControlPanel(void) +{ +extern void HelpScreens(void); + + boolean resetitem,on; + word i; + int ydelta; + longword flashtime; + UserItem far *item; + CursorInfo cursorinfo; + +#if 0 + // DEBUG!!! + { + USL_SetUpCtlPanel(); + Communication = uc_Loaded; + CtlPanelDone = true; + loadedgame = true; + USL_TearDownCtlPanel(); + return; + } +#endif + + if ((LastScan < sc_F1) || (LastScan > sc_F10)) + IN_ClearKeysDown(); + + USL_SetUpCtlPanel(); + USL_DrawCtlPanel(); + + ydelta = 0; + for (CtlPanelDone = false,resetitem = on = true;!CtlPanelDone;) + { + item = &(topcard->items[topcard->cursor]); + + if (resetitem) + { + flashtime = TimeCount + (TickBase / 2); + resetitem = false; + } + + if (TimeCount >= flashtime) + { + on ^= true; + resetitem = true; + if (!on) + item->flags &= ~ui_Selected; + USL_DrawItemIcon(item); + item->flags |= ui_Selected; + } + + VW_UpdateScreen(); + + if (LastScan) + { + switch (LastScan) + { + case sc_UpArrow: + USL_PrevItem(); + resetitem = true; + break; + case sc_DownArrow: + USL_NextItem(); + resetitem = true; + break; + case sc_Return: + USL_DoItem(); + resetitem = true; + break; + case sc_Escape: + USL_UpLevel(); + resetitem = true; + break; +#ifndef KEEN6 + case sc_F1: + HelpScreens(); + USL_DrawCtlPanel(); + resetitem = true; + break; +#endif + } + + if + ( + (!resetitem) + && ( + (LastScan == KbdDefs[0].button0) + || (LastScan == KbdDefs[0].button1) + ) + ) + { + USL_DoItem(); + resetitem = true; + } + + if (!resetitem) + { + for (item = topcard->items,i = 0;item->type != uii_Bad;item++,i++) + { + if (item->hotkey == LastScan) + { + USL_SelectItem(topcard,i,true); + resetitem = true; + break; + } + } + } + + IN_ClearKeysDown(); + } + else + { + IN_ReadCursor(&cursorinfo); + ydelta += cursorinfo.y; + if (cursorinfo.button0) + { + do + { + IN_ReadCursor(&cursorinfo); + } while (cursorinfo.button0); + USL_DoItem(); + resetitem = true; + } + else if (cursorinfo.button1) + { + do + { + IN_ReadCursor(&cursorinfo); + } while (cursorinfo.button1); + USL_UpLevel(); + resetitem = true; + } + else if (ydelta < -MoveMin) + { + ydelta += MoveMin; + USL_PrevItem(); + resetitem = true; + } + else if (ydelta > MoveMin) + { + ydelta -= MoveMin; + USL_NextItem(); + resetitem = true; + } + } + } + + USL_TearDownCtlPanel(); +} diff --git a/16/cawat/ID_US_A.ASM b/16/cawat/ID_US_A.ASM new file mode 100644 index 00000000..a27ca547 --- /dev/null +++ b/16/cawat/ID_US_A.ASM @@ -0,0 +1,117 @@ +; Catacomb Armageddon Source Code +; Copyright (C) 1993-2014 Flat Rock Software +; +; This program is free software; you can redistribute it and/or modify +; it under the terms of the GNU General Public License as published by +; the Free Software Foundation; either version 2 of the License, or +; (at your option) any later version. +; +; This program is distributed in the hope that it will be useful, +; but WITHOUT ANY WARRANTY; without even the implied warranty of +; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +; GNU General Public License for more details. +; +; You should have received a copy of the GNU General Public License along +; with this program; if not, write to the Free Software Foundation, Inc., +; 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +IDEAL +MODEL MEDIUM,C + +; Assembly portion of the User Mgr. This is just John Carmack's table +; driven pseudo-random number generator, and we put it in the User Mgr +; because we couldn't figure out where it should go + + +;============================================================================ +; +; RANDOM ROUTINES +; +;============================================================================ + + FARDATA + +rndindex dw ? + +rndtable db 0, 8, 109, 220, 222, 241, 149, 107, 75, 248, 254, 140, 16, 66 + db 74, 21, 211, 47, 80, 242, 154, 27, 205, 128, 161, 89, 77, 36 + db 95, 110, 85, 48, 212, 140, 211, 249, 22, 79, 200, 50, 28, 188 + db 52, 140, 202, 120, 68, 145, 62, 70, 184, 190, 91, 197, 152, 224 + db 149, 104, 25, 178, 252, 182, 202, 182, 141, 197, 4, 81, 181, 242 + db 145, 42, 39, 227, 156, 198, 225, 193, 219, 93, 122, 175, 249, 0 + db 175, 143, 70, 239, 46, 246, 163, 53, 163, 109, 168, 135, 2, 235 + db 25, 92, 20, 145, 138, 77, 69, 166, 78, 176, 173, 212, 166, 113 + db 94, 161, 41, 50, 239, 49, 111, 164, 70, 60, 2, 37, 171, 75 + db 136, 156, 11, 56, 42, 146, 138, 229, 73, 146, 77, 61, 98, 196 + db 135, 106, 63, 197, 195, 86, 96, 203, 113, 101, 170, 247, 181, 113 + db 80, 250, 108, 7, 255, 237, 129, 226, 79, 107, 112, 166, 103, 241 + db 24, 223, 239, 120, 198, 58, 60, 82, 128, 3, 184, 66, 143, 224 + db 145, 224, 81, 206, 163, 45, 63, 90, 168, 114, 59, 33, 159, 95 + db 28, 139, 123, 98, 125, 196, 15, 70, 194, 253, 54, 14, 109, 226 + db 71, 17, 161, 93, 186, 87, 244, 138, 20, 52, 123, 251, 26, 36 + db 17, 46, 52, 231, 232, 76, 31, 221, 84, 37, 216, 165, 212, 106 + db 197, 242, 98, 43, 39, 175, 254, 145, 190, 84, 118, 222, 187, 136 + db 120, 163, 236, 249 + + + CODESEG + +LastRnd dw ? + +;================================================= +; +; void US_InitRndT (boolean randomize) +; Init table based RND generator +; if randomize is false, the counter is set to 0 +; +;================================================= + +PROC US_InitRndT randomize:word + uses si,di + public US_InitRndT + + mov ax,SEG rndtable + mov es,ax + mov ax,[randomize] + or ax,ax + jne @@timeit ;if randomize is true, really random + + mov dx,0 ;set to a definite value + jmp @@setit + +@@timeit: + mov ah,2ch + int 21h ;GetSystemTime + and dx,0ffh + +@@setit: + mov [es:rndindex],dx + ret + +ENDP + +;================================================= +; +; int US_RndT (void) +; Return a random # between 0-255 +; Exit : AX = value +; +;================================================= +PROC US_RndT + public US_RndT + + mov ax,SEG rndtable + mov es,ax + mov bx,[es:rndindex] + inc bx + and bx,0ffh + mov [es:rndindex],bx + mov al,[es:rndtable+BX] + xor ah,ah + + ret + +ENDP + +END + diff --git a/16/cawat/ID_VW.C b/16/cawat/ID_VW.C new file mode 100644 index 00000000..f6a00679 --- /dev/null +++ b/16/cawat/ID_VW.C @@ -0,0 +1,1591 @@ +/* Catacomb Armageddon Source Code + * Copyright (C) 1993-2014 Flat Rock Software + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +// ID_VW.C + +#include "ID_HEADS.H" + +/* +============================================================================= + + LOCAL CONSTANTS + +============================================================================= +*/ + +#define VIEWWIDTH 40 + +#define PIXTOBLOCK 4 // 16 pixels to an update block + +/* +============================================================================= + + GLOBAL VARIABLES + +============================================================================= +*/ + +cardtype videocard; // set by VW_Startup +grtype grmode; // CGAgr, EGAgr, VGAgr + +unsigned bufferofs; // hidden area to draw to before displaying +unsigned displayofs; // origin of the visable screen +unsigned panx,pany; // panning adjustments inside port in pixels +unsigned pansx,pansy; // panning adjustments inside port in screen + // block limited pixel values (ie 0/8 for ega x) +unsigned panadjust; // panx/pany adjusted by screen resolution + +unsigned screenseg; // normally 0xa000 / 0xb800 +unsigned linewidth; +unsigned ylookup[VIRTUALHEIGHT]; + +unsigned fontnumber; // 0 based font number for drawing + +boolean screenfaded; + +pictabletype _seg *pictable; +pictabletype _seg *picmtable; +spritetabletype _seg *spritetable; + +int bordercolor; + +/* +============================================================================= + + LOCAL VARIABLES + +============================================================================= +*/ + +void VWL_MeasureString (char far *string, word *width, word *height, + fontstruct _seg *font); +void VWL_DrawCursor (void); +void VWL_EraseCursor (void); +void VWL_DBSetup (void); +void VWL_UpdateScreenBlocks (void); + + +int bordercolor; +int cursorvisible; +int cursornumber,cursorwidth,cursorheight,cursorx,cursory; +memptr cursorsave; +unsigned cursorspot; + +//=========================================================================== + + +/* +======================= += += VW_Startup += +======================= +*/ + +static char *ParmStrings[] = {"HIDDENCARD",""}; + +void VW_Startup (void) +{ + int i; + + asm cld; + + videocard = 0; + + for (i = 1;i < _argc;i++) + if (US_CheckParm(_argv[i],ParmStrings) == 0) + { + videocard = EGAcard; + break; + } + + if (!videocard) + videocard = VW_VideoID (); + +#if GRMODE == EGAGR + grmode = EGAGR; + if (videocard != EGAcard && videocard != VGAcard) +Quit ("Improper video card! If you really have an EGA/VGA card that I am not \n" + "detecting, use the -HIDDENCARD command line parameter!"); + EGAWRITEMODE(0); +#endif + +#if GRMODE == CGAGR + grmode = CGAGR; + if (videocard < CGAcard || videocard > VGAcard) +Quit ("Improper video card! If you really have a CGA card that I am not \n" + "detecting, use the -HIDDENCARD command line parameter!"); + MM_GetPtr (&(memptr)screenseg,0x10000l); // grab 64k for floating screen +#endif + + cursorvisible = 0; +} + +//=========================================================================== + +/* +======================= += += VW_Shutdown += +======================= +*/ + +void VW_Shutdown (void) +{ + VW_SetScreenMode (TEXTGR); +#if GRMODE == EGAGR + VW_SetLineWidth (80); +#endif +} + +//=========================================================================== + +/* +======================== += += VW_SetScreenMode += Call BIOS to set TEXT / CGAgr / EGAgr / VGAgr += +======================== +*/ + +void VW_SetScreenMode (int grmode) +{ + switch (grmode) + { + case TEXTGR: _AX = 3; + geninterrupt (0x10); + screenseg=0xb000; + break; + case CGAGR: _AX = 4; + geninterrupt (0x10); // screenseg is actually a main mem buffer + break; + + case EGA320GR: // MDM start (GAMERS EDGE) + MaxX=320; + MaxY=200; + _AX = 0xd|128; + geninterrupt (0x10); + screenseg=0xa000; + break; + + case EGA640GR: + MaxX=640; + MaxY=200; + _AX = 0xe|128; + geninterrupt (0x10); + screenseg=0xa000; + break; // MDM end (GAMERS EDGE) + + case EGAGR: _AX = 0xd; + MaxX=320; + MaxY=200; + geninterrupt (0x10); + screenseg=0xa000; + break; +#ifdef VGAGAME + case VGAGR:{ + char extern VGAPAL; // deluxepaint vga pallet .OBJ file + void far *vgapal = &VGAPAL; + SetCool256 (); // custom 256 color mode + screenseg=0xa000; + _ES = FP_SEG(vgapal); + _DX = FP_OFF(vgapal); + _BX = 0; + _CX = 0x100; + _AX = 0x1012; + geninterrupt(0x10); // set the deluxepaint pallet + + break; +#endif + } + VW_SetLineWidth(SCREENWIDTH); +} + +/* +============================================================================= + + SCREEN FADES + +============================================================================= +*/ + +char colors[7][17]= +{{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, + {0,0,0,0,0,0,0,0,0,1,2,3,4,5,6,7,0}, + {0,0,0,0,0,0,0,0,0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f,0}, + {0,1,2,3,4,5,6,7,0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f,0}, + {0,1,2,3,4,5,6,7,0x1f,0x1f,0x1f,0x1f,0x1f,0x1f,0x1f,0x1f,0}, + {0x1f,0x1f,0x1f,0x1f,0x1f,0x1f,0x1f,0x1f,0x1f,0x1f,0x1f,0x1f,0x1f,0x1f,0x1f,0x1f,0x1f}}; + + +void VW_ColorBorder (int color) +{ + _AH=0x10; + _AL=1; + _BH=color; + geninterrupt (0x10); + bordercolor = color; +} + +void VW_SetPalette(byte *palette) +{ + byte p; + word i; + + for (i = 0;i < 15;i++) + { + p = palette[i]; + colors[0][i] = 0; + colors[1][i] = (p > 0x10)? (p & 0x0f) : 0; + colors[2][i] = (p > 0x10)? p : 0; + colors[3][i] = p; + colors[4][i] = (p > 0x10)? 0x1f : p; + colors[5][i] = 0x1f; + } +} + +void VW_SetDefaultColors(void) +{ +#if GRMODE == EGAGR + colors[3][16] = bordercolor; + _ES=FP_SEG(&colors[3]); + _DX=FP_OFF(&colors[3]); + _AX=0x1002; + geninterrupt(0x10); + screenfaded = false; +#endif +} + + +void VW_FadeOut(void) +{ +#if GRMODE == EGAGR + int i; + + for (i=3;i>=0;i--) + { + colors[i][16] = bordercolor; + _ES=FP_SEG(&colors[i]); + _DX=FP_OFF(&colors[i]); + _AX=0x1002; + geninterrupt(0x10); + VW_WaitVBL(6); + } + screenfaded = true; +#endif +} + + +void VW_FadeIn(void) +{ +#if GRMODE == EGAGR + int i; + + for (i=0;i<4;i++) + { + colors[i][16] = bordercolor; + _ES=FP_SEG(&colors[i]); + _DX=FP_OFF(&colors[i]); + _AX=0x1002; + geninterrupt(0x10); + VW_WaitVBL(6); + } + screenfaded = false; +#endif +} + +void VW_FadeUp(void) +{ +#if GRMODE == EGAGR + int i; + + for (i=3;i<6;i++) + { + colors[i][16] = bordercolor; + _ES=FP_SEG(&colors[i]); + _DX=FP_OFF(&colors[i]); + _AX=0x1002; + geninterrupt(0x10); + VW_WaitVBL(6); + } + screenfaded = true; +#endif +} + +void VW_FadeDown(void) +{ +#if GRMODE == EGAGR + int i; + + for (i=5;i>2;i--) + { + colors[i][16] = bordercolor; + _ES=FP_SEG(&colors[i]); + _DX=FP_OFF(&colors[i]); + _AX=0x1002; + geninterrupt(0x10); + VW_WaitVBL(6); + } + screenfaded = false; +#endif +} + + +/* +======================== += += VW_SetAtrReg += += Sets an attribute (pallete / border) register += Does NOT vsync! += +======================== +*/ + +void VW_SetAtrReg (int reg, int value) +{ + asm cli + asm mov dx,STATUS_REGISTER_1 + asm in al,dx + asm mov dx,ATR_INDEX + + asm mov al,BYTE PTR [reg] + asm out dx,al + asm mov al,BYTE PTR [value] + asm out dx,al + asm mov dx,0x3da + asm in al,dx + asm mov dx,ATR_INDEX + asm mov al,0x20 + asm out dx,al + asm sti +} + + + +//=========================================================================== + +/* +==================== += += VW_SetLineWidth += += Must be an even number of bytes += +==================== +*/ + +void VW_SetLineWidth (int width) +{ + int i,offset; + +#if GRMODE == EGAGR +// +// set wide virtual screen +// +asm mov dx,CRTC_INDEX +asm mov al,CRTC_OFFSET +asm mov ah,[BYTE PTR width] +asm shr ah,1 +asm out dx,ax +#endif + +// +// set up lookup tables +// + linewidth = width; + + offset = 0; + + for (i=0;i0 + +/* +==================== += += VW_DrawPic += += X in bytes, y in pixels, chunknum is the #defined picnum += +==================== +*/ + +void VW_DrawPic(unsigned x, unsigned y, unsigned chunknum) +{ + int picnum = chunknum - STARTPICS; + memptr source; + unsigned dest,width,height; + + source = grsegs[chunknum]; + dest = ylookup[y]+x+bufferofs; + width = pictable[picnum].width; + height = pictable[picnum].height; + + VW_MemToScreen(source,dest,width,height); +} + + +// MDM (GAMERS EDGE) begin +/* +==================== += += VW_DrawPic2x - Same as VW_DrawPic, but doubles pixels horizontally += (Great for drawing 320 graphics on 640 screen!) += += X in bytes, y in pixels, chunknum is the #defined picnum += +==================== +*/ + +void VW_DrawPic2x(unsigned x, unsigned y, unsigned chunknum) +{ + int picnum = chunknum - STARTPICS; + memptr source; + unsigned dest,width,height; + + source = grsegs[chunknum]; + dest = ylookup[y]+x+bufferofs; + width = pictable[picnum].width; + height = pictable[picnum].height; + + VW_MemToScreen2x(source,dest,width,height); +} +// MDM (GAMERS EDGE) end + +#endif + +#if NUMPICM>0 + +/* +==================== += += VW_DrawMPic += += X in bytes, y in pixels, chunknum is the #defined picnum += +==================== +*/ + +void VW_DrawMPic(unsigned x, unsigned y, unsigned chunknum) +{ + int picnum = chunknum - STARTPICM; + memptr source; + unsigned dest,width,height; + + source = grsegs[chunknum]; + dest = ylookup[y]+x+bufferofs; + width = picmtable[picnum].width; + height = picmtable[picnum].height; + + VW_MaskBlock(source,0,dest,width,height,width*height); +} + +void VW_ClipDrawMPic(unsigned x, int y, unsigned chunknum) +{ + int picnum = chunknum - STARTPICM; + memptr source; + unsigned dest,width,ofs,plane; + int height; + + source = grsegs[chunknum]; + width = picmtable[picnum].width; + height = picmtable[picnum].height; + plane = width*height; + + ofs = 0; + if (y<0) + { + ofs= -y*width; + height+=y; + y=0; + } + else if (y+height>216) + { + height-=(y-216); + } + dest = ylookup[y]+x+bufferofs; + if (height<1) + return; + + VW_MaskBlock(source,ofs,dest,width,height,plane); +} + + +#endif + +//=========================================================================== + +#if NUMSPRITES>0 + +/* +==================== += += VW_DrawSprite += += X and Y in pixels, it will match the closest shift possible += += To do: += Add vertical clipping! += Make the shifts act as center points, rather than break points += +==================== +*/ + +void VW_DrawSprite(int x, int y, unsigned chunknum) +{ + spritetabletype far *spr; + spritetype _seg *block; + unsigned dest,shift; + + spr = &spritetable[chunknum-STARTSPRITES]; + block = (spritetype _seg *)grsegs[chunknum]; + + y+=spr->orgy>>G_P_SHIFT; + x+=spr->orgx>>G_P_SHIFT; + +#if GRMODE == EGAGR + shift = (x&7)/2; +#endif +#if GRMODE == CGAGR + shift = 0; +#endif + + dest = bufferofs + ylookup[y]; + if (x>=0) + dest += x/SCREENXDIV; + else + dest += (x+1)/SCREENXDIV; + + VW_MaskBlock (block,block->sourceoffset[shift],dest, + block->width[shift],spr->height,block->planesize[shift]); +} + +#endif + + +/* +================== += += VW_Hlin += +================== +*/ + + +#if GRMODE == EGAGR + +unsigned char leftmask[8] = {0xff,0x7f,0x3f,0x1f,0xf,7,3,1}; +unsigned char rightmask[8] = {0x80,0xc0,0xe0,0xf0,0xf8,0xfc,0xfe,0xff}; + +void VW_Hlin(unsigned xl, unsigned xh, unsigned y, unsigned color) +{ + unsigned dest,xlb,xhb,maskleft,maskright,mid; + + xlb=xl/8; + xhb=xh/8; + + EGAWRITEMODE(2); + EGAMAPMASK(15); + + maskleft = leftmask[xl&7]; + maskright = rightmask[xh&7]; + + mid = xhb-xlb-1; + dest = bufferofs+ylookup[y]+xlb; + + if (xlb==xhb) + { + // + // entire line is in one byte + // + + maskleft&=maskright; + + asm mov es,[screenseg] + asm mov di,[dest] + + asm mov dx,GC_INDEX + asm mov al,GC_BITMASK + asm mov ah,[BYTE PTR maskleft] + asm out dx,ax // mask off pixels + + asm mov al,[BYTE PTR color] + asm xchg al,[es:di] // load latches and write pixels + + goto done; + } + +asm mov es,[screenseg] +asm mov di,[dest] +asm mov dx,GC_INDEX +asm mov bh,[BYTE PTR color] + +// +// draw left side +// +asm mov al,GC_BITMASK +asm mov ah,[BYTE PTR maskleft] +asm out dx,ax // mask off pixels + +asm mov al,bh +asm mov bl,[es:di] // load latches +asm stosb + +// +// draw middle +// +asm mov ax,GC_BITMASK + 255*256 +asm out dx,ax // no masking + +asm mov al,bh +asm mov cx,[mid] +asm rep stosb + +// +// draw right side +// +asm mov al,GC_BITMASK +asm mov ah,[BYTE PTR maskright] +asm out dx,ax // mask off pixels + +asm xchg bh,[es:di] // load latches and write pixels + +done: + EGABITMASK(255); + EGAWRITEMODE(0); +} +#endif + + +#if GRMODE == CGAGR + +unsigned char pixmask[4] = {0xc0,0x30,0x0c,0x03}; +unsigned char leftmask[4] = {0xff,0x3f,0x0f,0x03}; +unsigned char rightmask[4] = {0xc0,0xf0,0xfc,0xff}; +unsigned char colorbyte[4] = {0,0x55,0xaa,0xff}; + +// +// could be optimized for rep stosw +// +void VW_Hlin(unsigned xl, unsigned xh, unsigned y, unsigned color) +{ + unsigned dest,xlb,xhb,mid; + byte maskleft,maskright; + + color = colorbyte[color]; // expand 2 color bits to 8 + + xlb=xl/4; + xhb=xh/4; + + maskleft = leftmask[xl&3]; + maskright = rightmask[xh&3]; + + mid = xhb-xlb-1; + dest = bufferofs+ylookup[y]+xlb; +asm mov es,[screenseg] + + if (xlb==xhb) + { + // + // entire line is in one byte + // + maskleft&=maskright; + + asm mov ah,[maskleft] + asm mov bl,[BYTE PTR color] + asm and bl,[maskleft] + asm not ah + + asm mov di,[dest] + + asm mov al,[es:di] + asm and al,ah // mask out pixels + asm or al,bl // or in color + asm mov [es:di],al + return; + } + +asm mov di,[dest] +asm mov bh,[BYTE PTR color] + +// +// draw left side +// +asm mov ah,[maskleft] +asm mov bl,bh +asm and bl,[maskleft] +asm not ah +asm mov al,[es:di] +asm and al,ah // mask out pixels +asm or al,bl // or in color +asm stosb + +// +// draw middle +// +asm mov al,bh +asm mov cx,[mid] +asm rep stosb + +// +// draw right side +// +asm mov ah,[maskright] +asm mov bl,bh +asm and bl,[maskright] +asm not ah +asm mov al,[es:di] +asm and al,ah // mask out pixels +asm or al,bl // or in color +asm stosb +} +#endif + + +/* +================== += += VW_Bar += += Pixel addressable block fill routine += +================== +*/ + +#if GRMODE == CGAGR + +void VW_Bar (unsigned x, unsigned y, unsigned width, unsigned height, + unsigned color) +{ + unsigned xh = x+width-1; + + while (height--) + VW_Hlin (x,xh,y++,color); +} + +#endif + + +#if GRMODE == EGAGR + +void VW_Bar (unsigned x, unsigned y, unsigned width, unsigned height, + unsigned color) +{ + unsigned dest,xh,xlb,xhb,maskleft,maskright,mid; + + xh = x+width-1; + xlb=x/8; + xhb=xh/8; + + EGAWRITEMODE(2); + EGAMAPMASK(15); + + maskleft = leftmask[x&7]; + maskright = rightmask[xh&7]; + + mid = xhb-xlb-1; + dest = bufferofs+ylookup[y]+xlb; + + if (xlb==xhb) + { + // + // entire line is in one byte + // + + maskleft&=maskright; + + asm mov es,[screenseg] + asm mov di,[dest] + + asm mov dx,GC_INDEX + asm mov al,GC_BITMASK + asm mov ah,[BYTE PTR maskleft] + asm out dx,ax // mask off pixels + + asm mov ah,[BYTE PTR color] + asm mov dx,[linewidth] +yloop1: + asm mov al,ah + asm xchg al,[es:di] // load latches and write pixels + asm add di,dx // down to next line + asm dec [height] + asm jnz yloop1 + + goto done; + } + +asm mov es,[screenseg] +asm mov di,[dest] +asm mov bh,[BYTE PTR color] +asm mov dx,GC_INDEX +asm mov si,[linewidth] +asm sub si,[mid] // add to di at end of line to get to next scan +asm dec si + +// +// draw left side +// +yloop2: +asm mov al,GC_BITMASK +asm mov ah,[BYTE PTR maskleft] +asm out dx,ax // mask off pixels + +asm mov al,bh +asm mov bl,[es:di] // load latches +asm stosb + +// +// draw middle +// +asm mov ax,GC_BITMASK + 255*256 +asm out dx,ax // no masking + +asm mov al,bh +asm mov cx,[mid] +asm rep stosb + +// +// draw right side +// +asm mov al,GC_BITMASK +asm mov ah,[BYTE PTR maskright] +asm out dx,ax // mask off pixels + +asm mov al,bh +asm xchg al,[es:di] // load latches and write pixels + +asm add di,si // move to start of next line +asm dec [height] +asm jnz yloop2 + +done: + EGABITMASK(255); + EGAWRITEMODE(0); +} + +#endif + +//========================================================================== + +/* +================== += += VW_MeasureString += +================== +*/ + +#if NUMFONT+NUMFONTM>0 +void +VWL_MeasureString (char far *string, word *width, word *height, fontstruct _seg *font) +{ + *height = font->height-1; // MDM (GAMERS EDGE) - squeeze font vertically... + for (*width = 0;*string;string++) + *width += font->width[*((byte far *)string)]; // proportional width +} + +void VW_MeasurePropString (char far *string, word *width, word *height) +{ + VWL_MeasureString(string,width,height,(fontstruct _seg *)grsegs[STARTFONT+fontnumber]); +} + +void VW_MeasureMPropString (char far *string, word *width, word *height) +{ + VWL_MeasureString(string,width,height,(fontstruct _seg *)grsegs[STARTFONTM+fontnumber]); +} + + +#endif + + +/* +============================================================================= + + CGA stuff + +============================================================================= +*/ + +#if GRMODE == CGAGR + +#define CGACRTCWIDTH 40 + +/* +========================== += += VW_CGAFullUpdate += +========================== +*/ + +void VW_CGAFullUpdate (void) +{ + byte *update; + boolean halftile; + unsigned x,y,middlerows,middlecollumns; + + displayofs = bufferofs+panadjust; + +asm mov ax,0xb800 +asm mov es,ax + +asm mov si,[displayofs] +asm xor di,di + +asm mov bx,100 // pairs of scan lines to copy +asm mov dx,[linewidth] +asm sub dx,80 + +asm mov ds,[screenseg] +asm test si,1 +asm jz evenblock + +// +// odd source +// +asm mov ax,39 // words accross screen +copytwolineso: +asm movsb +asm mov cx,ax +asm rep movsw +asm movsb +asm add si,dx +asm add di,0x2000-80 // go to the interlaced bank +asm movsb +asm mov cx,ax +asm rep movsw +asm movsb +asm add si,dx +asm sub di,0x2000 // go to the non interlaced bank + +asm dec bx +asm jnz copytwolineso +asm jmp blitdone + +// +// even source +// +evenblock: +asm mov ax,40 // words accross screen +copytwolines: +asm mov cx,ax +asm rep movsw +asm add si,dx +asm add di,0x2000-80 // go to the interlaced bank +asm mov cx,ax +asm rep movsw +asm add si,dx +asm sub di,0x2000 // go to the non interlaced bank + +asm dec bx +asm jnz copytwolines + +blitdone: +asm mov ax,ss +asm mov ds,ax +asm mov es,ax + +asm xor ax,ax // clear out the update matrix +asm mov cx,UPDATEWIDE*UPDATEHIGH/2 + +asm mov di,[baseupdateptr] +asm rep stosw + + updateptr = baseupdateptr; + *(unsigned *)(updateptr + UPDATEWIDE*PORTTILESHIGH) = UPDATETERMINATE; +} + + +#endif + +/* +============================================================================= + + CURSOR ROUTINES + +These only work in the context of the double buffered update routines + +============================================================================= +*/ + +/* +==================== += += VWL_DrawCursor += += Background saves, then draws the cursor at cursorspot += +==================== +*/ + +void VWL_DrawCursor (void) +{ + cursorspot = bufferofs + ylookup[cursory+pansy]+(cursorx+pansx)/SCREENXDIV; + VW_ScreenToMem(cursorspot,cursorsave,cursorwidth,cursorheight); + VWB_DrawSprite(cursorx,cursory,cursornumber); +} + + +//========================================================================== + + +/* +==================== += += VWL_EraseCursor += +==================== +*/ + +void VWL_EraseCursor (void) +{ + VW_MemToScreen(cursorsave,cursorspot,cursorwidth,cursorheight); + VW_MarkUpdateBlock ((cursorx+pansx)&SCREENXMASK,cursory+pansy, + ( (cursorx+pansx)&SCREENXMASK)+cursorwidth*SCREENXDIV-1, + cursory+pansy+cursorheight-1); +} + + +//========================================================================== + + +/* +==================== += += VW_ShowCursor += +==================== +*/ + +void VW_ShowCursor (void) +{ + cursorvisible++; +} + + +//========================================================================== + +/* +==================== += += VW_HideCursor += +==================== +*/ + +void VW_HideCursor (void) +{ + cursorvisible--; +} + +//========================================================================== + +/* +==================== += += VW_MoveCursor += +==================== +*/ +#define MAXCURSORX (319-24) +#define MAXCURSORY (199-24) + +void VW_MoveCursor (int x, int y) +{ + if (x>MAXCURSORX) + x=MAXCURSORX; + if (y>MAXCURSORY) + y=MAXCURSORY; // catacombs hack to keep cursor on screen + + cursorx = x; + cursory = y; +} + +//========================================================================== + +/* +==================== += += VW_SetCursor += += Load in a sprite to be used as a cursor, and allocate background save space += +==================== +*/ + +void VW_SetCursor (int spritenum) +{ + VW_FreeCursor (); + + cursornumber = spritenum; + + CA_CacheGrChunk (spritenum); + MM_SetLock (&grsegs[spritenum],true); + + cursorwidth = spritetable[spritenum-STARTSPRITES].width+1; + cursorheight = spritetable[spritenum-STARTSPRITES].height; + + MM_GetPtr (&cursorsave,cursorwidth*cursorheight*5); + MM_SetLock (&cursorsave,true); +} + + +/* +==================== += += VW_FreeCursor += += Frees the memory used by the cursor and its background save += +==================== +*/ + +void VW_FreeCursor (void) +{ + if (cursornumber) + { + MM_SetLock (&grsegs[cursornumber],false); + MM_SetPurge (&grsegs[cursornumber],3); + MM_SetLock (&cursorsave,false); + MM_FreePtr (&cursorsave); + cursornumber = 0; + } +} + + +/* +============================================================================= + + Double buffer management routines + +============================================================================= +*/ + +/* +====================== += += VW_InitDoubleBuffer += +====================== +*/ + +void VW_InitDoubleBuffer (void) +{ +#if GRMODE == EGAGR + VW_SetScreen (displayofs+panadjust,0); // no pel pan +#endif +} + + +/* +====================== += += VW_FixRefreshBuffer += += Copies the view page to the buffer page on page flipped refreshes to += avoid a one frame shear around pop up windows += +====================== +*/ + +void VW_FixRefreshBuffer (void) +{ +#if GRMODE == EGAGR + VW_ScreenToScreen (displayofs,bufferofs,PORTTILESWIDE*4*CHARWIDTH, + (PORTTILESHIGH-1)*16); +#endif +} + + +/* +====================== += += VW_QuitDoubleBuffer += +====================== +*/ + +void VW_QuitDoubleBuffer (void) +{ +} + + +/* +======================= += += VW_MarkUpdateBlock += += Takes a pixel bounded block and marks the tiles in bufferblocks += Returns 0 if the entire block is off the buffer screen += +======================= +*/ + +int VW_MarkUpdateBlock (int x1, int y1, int x2, int y2) +{ +// MDM (GAMERS EDGE) begin - NOT NEEDED FOR 3D ENGINE +#if 0 + int x,y,xt1,yt1,xt2,yt2,nextline; + byte *mark; + + xt1 = x1>>PIXTOBLOCK; + yt1 = y1>>PIXTOBLOCK; + + xt2 = x2>>PIXTOBLOCK; + yt2 = y2>>PIXTOBLOCK; + + if (xt1<0) + xt1=0; + else if (xt1>=UPDATEWIDE-1) + return 0; + + if (yt1<0) + yt1=0; + else if (yt1>UPDATEHIGH) + return 0; + + if (xt2<0) + return 0; + else if (xt2>=UPDATEWIDE-1) + xt2 = UPDATEWIDE-2; + + if (yt2<0) + return 0; + else if (yt2>=UPDATEHIGH) + yt2 = UPDATEHIGH-1; + + mark = updateptr + uwidthtable[yt1] + xt1; + nextline = UPDATEWIDE - (xt2-xt1) - 1; + + for (y=yt1;y<=yt2;y++) + { + for (x=xt1;x<=xt2;x++) + *mark++ = 1; // this tile will need to be updated + + mark += nextline; + } +#endif +// MDM (GAMERS EDGE) end + + return 1; +} + + +/* +=========================== += += VW_UpdateScreen += += Updates any changed areas of the double buffer and displays the cursor += +=========================== +*/ + +void VW_UpdateScreen (void) +{ + if (cursorvisible>0) + VWL_DrawCursor(); + +#if GRMODE == EGAGR + VWL_UpdateScreenBlocks(); + +asm mov ax,ds +asm mov es,ax +asm mov di,[updateptr] // cat3d patch +asm xor ax,ax // clear out the update matrix +asm mov cx,UPDATEWIDE*UPDATEHIGH/2 +asm rep stosw + *(unsigned *)(updateptr + UPDATEWIDE*PORTTILESHIGH) = UPDATETERMINATE; + +asm cli +asm mov cx,[displayofs] +asm add cx,[panadjust] +asm mov dx,CRTC_INDEX +asm mov al,0ch // start address high register +asm out dx,al +asm inc dx +asm mov al,ch +asm out dx,al +asm dec dx +asm mov al,0dh // start address low register +asm out dx,al +asm mov al,cl +asm inc dx +asm out dx,al +asm sti + +#endif +#if GRMODE == CGAGR + VW_CGAFullUpdate(); +#endif + + if (cursorvisible>0) + VWL_EraseCursor(); +} + + + +void VWB_DrawTile8 (int x, int y, int tile) +{ + x+=pansx; + y+=pansy; + if (VW_MarkUpdateBlock (x&SCREENXMASK,y,(x&SCREENXMASK)+7,y+7)) + VW_DrawTile8 (x/SCREENXDIV,y,tile); +} + +void VWB_DrawTile8M (int x, int y, int tile) +{ + int xb; + + x+=pansx; + y+=pansy; + xb = x/SCREENXDIV; // use intermediate because VW_DT8M is macro +// if (VW_MarkUpdateBlock (x&SCREENXMASK,y,(x&SCREENXMASK)+7,y+7)) // MDM (GAMER EDGE) + VW_DrawTile8M (xb,y,tile); // statement prevents drawing chars past 42 +} + +void VWB_DrawTile16 (int x, int y, int tile) +{ + x+=pansx; + y+=pansy; + if (VW_MarkUpdateBlock (x&SCREENXMASK,y,(x&SCREENXMASK)+15,y+15)) + VW_DrawTile16 (x/SCREENXDIV,y,tile); +} + +void VWB_DrawTile16M (int x, int y, int tile) +{ + int xb; + + x+=pansx; + y+=pansy; + xb = x/SCREENXDIV; // use intermediate because VW_DT16M is macro + if (VW_MarkUpdateBlock (x&SCREENXMASK,y,(x&SCREENXMASK)+15,y+15)) + VW_DrawTile16M (xb,y,tile); +} + +#if NUMPICS +void VWB_DrawPic (int x, int y, int chunknum) +{ +// mostly copied from drawpic + int picnum = chunknum - STARTPICS; + memptr source; + unsigned dest,width,height; + + x+=pansx; + y+=pansy; + x/= SCREENXDIV; + + source = grsegs[chunknum]; + dest = ylookup[y]+x+bufferofs; + width = pictable[picnum].width; + height = pictable[picnum].height; + + if (VW_MarkUpdateBlock (x*SCREENXDIV,y,(x+width)*SCREENXDIV-1,y+height-1)) + VW_MemToScreen(source,dest,width,height); +} +#endif + +#if NUMPICM>0 +void VWB_DrawMPic(int x, int y, int chunknum) +{ +// mostly copied from drawmpic + int picnum = chunknum - STARTPICM; + memptr source; + unsigned dest,width,height; + + x+=pansx; + y+=pansy; + x/=SCREENXDIV; + + source = grsegs[chunknum]; + dest = ylookup[y]+x+bufferofs; + width = picmtable[picnum].width; + height = picmtable[picnum].height; + + if (VW_MarkUpdateBlock (x*SCREENXDIV,y,(x+width)*SCREENXDIV-1,y+height-1)) + VW_MaskBlock(source,0,dest,width,height,width*height); +} +#endif + + +void VWB_Bar (int x, int y, int width, int height, int color) +{ + x+=pansx; + y+=pansy; + if (VW_MarkUpdateBlock (x,y,x+width,y+height-1) ) + VW_Bar (x,y,width,height,color); +} + + +#if NUMFONT +void VWB_DrawPropString (char far *string) +{ + int x,y; + x = px+pansx; + y = py+pansy; + VW_DrawPropString (string); + VW_MarkUpdateBlock(x,y,x+bufferwidth*8-1,y+bufferheight-1); +} +#endif + + +#if NUMFONTM +void VWB_DrawMPropString (char far *string) +{ + int x,y; + x = px+pansx; + y = py+pansy; + VW_DrawMPropString (string); + VW_MarkUpdateBlock(x,y,x+bufferwidth*8-1,y+bufferheight-1); +} +#endif + +#if NUMSPRITES +void VWB_DrawSprite(int x, int y, int chunknum) +{ + spritetabletype far *spr; + spritetype _seg *block; + unsigned dest,shift,width,height; + + x+=pansx; + y+=pansy; + + spr = &spritetable[chunknum-STARTSPRITES]; + block = (spritetype _seg *)grsegs[chunknum]; + + y+=spr->orgy>>G_P_SHIFT; + x+=spr->orgx>>G_P_SHIFT; + + +#if GRMODE == EGAGR + shift = (x&7)/2; +#endif +#if GRMODE == CGAGR + shift = 0; +#endif + + dest = bufferofs + ylookup[y]; + if (x>=0) + dest += x/SCREENXDIV; + else + dest += (x+1)/SCREENXDIV; + + width = block->width[shift]; + height = spr->height; + + if (VW_MarkUpdateBlock (x&SCREENXMASK,y,(x&SCREENXMASK)+width*SCREENXDIV-1 + ,y+height-1)) + VW_MaskBlock (block,block->sourceoffset[shift],dest, + width,height,block->planesize[shift]); +} +#endif + +void VWB_Plot (int x, int y, int color) +{ + x+=pansx; + y+=pansy; + if (VW_MarkUpdateBlock (x,y,x,y)) + VW_Plot(x,y,color); +} + +void VWB_Hlin (int x1, int x2, int y, int color) +{ + x1+=pansx; + x2+=pansx; + y+=pansy; + if (VW_MarkUpdateBlock (x1,y,x2,y)) + VW_Hlin(x1,x2,y,color); +} + +void VWB_Vlin (int y1, int y2, int x, int color) +{ + x+=pansx; + y1+=pansy; + y2+=pansy; + if (VW_MarkUpdateBlock (x,y1,x,y2)) + VW_Vlin(y1,y2,x,color); +} + + +//=========================================================================== diff --git a/16/cawat/ID_VW.H b/16/cawat/ID_VW.H new file mode 100644 index 00000000..49e41d3d --- /dev/null +++ b/16/cawat/ID_VW.H @@ -0,0 +1,374 @@ +/* Catacomb Armageddon Source Code + * Copyright (C) 1993-2014 Flat Rock Software + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +// ID_VW.H + +#ifndef __TYPES__ +#include "ID_TYPES.H" +#endif + +#ifndef __ID_MM__ +#include "ID_MM.H" +#endif + +#ifndef __ID_GLOB__ +#include "ID_GLOB.H" +#endif + +#define __ID_VW__ + +//=========================================================================== + +#define G_P_SHIFT 4 // global >> ?? = pixels + +#if GRMODE == EGAGR +#define SCREENWIDTH 40 +#define CHARWIDTH 1 +#define TILEWIDTH 2 +#define GRPLANES 4 +#define BYTEPIXELS 8 +#endif + +#if GRMODE == CGAGR +#define SCREENWIDTH 128 +#define CHARWIDTH 2 +#define TILEWIDTH 4 +#define GRPLANES 1 +#define BYTEPIXELS 4 +#endif + +#define VIRTUALHEIGHT 300 +#define VIRTUALWIDTH 512 + + +#if GRMODE == CGAGR + +#define MAXSHIFTS 1 + +#define WHITE 3 // graphics mode independant colors +#define BLACK 0 +#define FIRSTCOLOR 1 +#define SECONDCOLOR 2 +#define F_WHITE 0 // for XOR font drawing +#define F_BLACK 3 +#define F_FIRSTCOLOR 2 +#define F_SECONDCOLOR 1 + +#endif + +#if GRMODE == EGAGR + +#define MAXSHIFTS 4 + +#define WHITE 15 // graphics mode independant colors +#define BLACK 0 +#define LT_GREY 7 +#define FIRSTCOLOR 1 +#define SECONDCOLOR 12 +#define F_WHITE 0 // for XOR font drawing +#define F_BLACK 15 +#define F_FIRSTCOLOR 14 +#define F_SECONDCOLOR 3 + +#endif + +#if GRMODE == EGAGR +#define SCREENXMASK (~7) +#define SCREENXPLUS (7) +#define SCREENXDIV (8) +#endif + +#if GRMODE == CGAGR +#define SCREENXMASK (~3) +#define SCREENXDIV (4) +#endif + +//=========================================================================== + + +#define SC_INDEX 0x3C4 +#define SC_RESET 0 +#define SC_CLOCK 1 +#define SC_MAPMASK 2 +#define SC_CHARMAP 3 +#define SC_MEMMODE 4 + +#define CRTC_INDEX 0x3D4 +#define CRTC_H_TOTAL 0 +#define CRTC_H_DISPEND 1 +#define CRTC_H_BLANK 2 +#define CRTC_H_ENDBLANK 3 +#define CRTC_H_RETRACE 4 +#define CRTC_H_ENDRETRACE 5 +#define CRTC_V_TOTAL 6 +#define CRTC_OVERFLOW 7 +#define CRTC_ROWSCAN 8 +#define CRTC_MAXSCANLINE 9 +#define CRTC_CURSORSTART 10 +#define CRTC_CURSOREND 11 +#define CRTC_STARTHIGH 12 +#define CRTC_STARTLOW 13 +#define CRTC_CURSORHIGH 14 +#define CRTC_CURSORLOW 15 +#define CRTC_V_RETRACE 16 +#define CRTC_V_ENDRETRACE 17 +#define CRTC_V_DISPEND 18 +#define CRTC_OFFSET 19 +#define CRTC_UNDERLINE 20 +#define CRTC_V_BLANK 21 +#define CRTC_V_ENDBLANK 22 +#define CRTC_MODE 23 +#define CRTC_LINECOMPARE 24 + + +#define GC_INDEX 0x3CE +#define GC_SETRESET 0 +#define GC_ENABLESETRESET 1 +#define GC_COLORCOMPARE 2 +#define GC_DATAROTATE 3 +#define GC_READMAP 4 +#define GC_MODE 5 +#define GC_MISCELLANEOUS 6 +#define GC_COLORDONTCARE 7 +#define GC_BITMASK 8 + +#define ATR_INDEX 0x3c0 +#define ATR_MODE 16 +#define ATR_OVERSCAN 17 +#define ATR_COLORPLANEENABLE 18 +#define ATR_PELPAN 19 +#define ATR_COLORSELECT 20 + +#define STATUS_REGISTER_1 0x3da + +//=========================================================================== + +typedef enum {NOcard,MDAcard,CGAcard,EGAcard,MCGAcard,VGAcard, + HGCcard=0x80,HGCPcard,HICcard} cardtype; + +typedef struct +{ + int width, + height, + orgx,orgy, + xl,yl,xh,yh, + shifts; +} spritetabletype; + +typedef struct +{ + unsigned sourceoffset[MAXSHIFTS]; + unsigned planesize[MAXSHIFTS]; + unsigned width[MAXSHIFTS]; + byte data[]; +} spritetype; // the memptr for each sprite points to this + +typedef struct +{ + int width,height; +} pictabletype; + + +typedef struct +{ + int height; + int location[256]; + char width[256]; +} fontstruct; + + +typedef enum {CGAgr,EGAgr,VGAgr} grtype; + +//=========================================================================== + +extern cardtype videocard; // set by VW_Startup +extern grtype grmode; // CGAgr, EGAgr, VGAgr + +extern unsigned bufferofs; // hidden port to draw to before displaying +extern unsigned displayofs; // origin of port on visable screen +extern unsigned panx,pany; // panning adjustments inside port in pixels +extern unsigned pansx,pansy; +extern unsigned panadjust; // panx/pany adjusted by screen resolution + +extern unsigned screenseg; // normally 0xa000 or buffer segment + +extern unsigned linewidth; +extern unsigned ylookup[VIRTUALHEIGHT]; + +extern boolean screenfaded; +extern char colors[7][17]; // pallets for fades + +extern pictabletype _seg *pictable; +extern pictabletype _seg *picmtable; +extern spritetabletype _seg *spritetable; + +extern unsigned fontnumber; // 0 based font number for drawing +extern int px,py; +extern byte pdrawmode,fontcolor; + +extern int bordercolor; + +// +// asm globals +// + +extern unsigned *shifttabletable[8]; +extern unsigned bufferwidth,bufferheight,screenspot; // used by font drawing stuff + + + +//=========================================================================== + + +void VW_Startup (void); +void VW_Shutdown (void); + +cardtype VW_VideoID (void); + +// +// EGA hardware routines +// + +#define EGAWRITEMODE(x) asm{cli;mov dx,GC_INDEX;mov ax,GC_MODE+256*x;out dx,ax;sti;} +#define EGABITMASK(x) asm{cli;mov dx,GC_INDEX;mov ax,GC_BITMASK+256*x;out dx,ax;sti;} +#define EGAMAPMASK(x) asm{cli;mov dx,SC_INDEX;mov ax,SC_MAPMASK+x*256;out dx,ax;sti;} +#define EGAREADMAP(x) asm{cli;mov dx,GC_INDEX;mov ax,GC_READMAP+x*256;out dx,ax;sti;} + +void VW_SetLineWidth(int width); +void VW_SetSplitScreen(int width); +void VW_SetScreen (unsigned CRTC, unsigned pelpan); + +void VW_SetScreenMode (int grmode); +void VW_ClearVideo (int color); +void VW_WaitVBL (int number); + +void VW_ColorBorder (int color); +void VW_SetPalette(byte *palette); +void VW_SetDefaultColors(void); +void VW_FadeOut(void); +void VW_FadeIn(void); +void VW_FadeUp(void); +void VW_FadeDown(void); + +void VW_SetAtrReg (int reg, int value); + +// +// block primitives +// + +void VW_MaskBlock(memptr segm,unsigned ofs,unsigned dest, + unsigned wide,unsigned height,unsigned planesize); +void VW_MemToScreen(memptr source,unsigned dest,unsigned width,unsigned height); +void VW_MemToScreen2x(memptr source,unsigned dest,unsigned width,unsigned height); +void VW_ScreenToMem(unsigned source,memptr dest,unsigned width,unsigned height); +void VW_ScreenToScreen(unsigned source,unsigned dest,unsigned width,unsigned height); + + +// +// block addressable routines +// + +void VW_DrawTile8(unsigned x, unsigned y, unsigned tile); + +#if GRMODE == EGAGR + +#define VW_DrawTile8M(x,y,t) \ + VW_MaskBlock(grsegs[STARTTILE8M],(t)*40,bufferofs+ylookup[y]+(x),1,8,8) +#define VW_DrawTile16(x,y,t) \ + VW_MemToScreen(grsegs[STARTTILE16+t],bufferofs+ylookup[y]+(x),2,16) +#define VW_DrawTile16M(x,y,t) \ + VW_MaskBlock(grsegs[STARTTILE16M],(t)*160,bufferofs+ylookup[y]+(x),2,16,32) + +#endif + +#if GRMODE == CGAGR + +#define VW_DrawTile8M(x,y,t) \ + VW_MaskBlock(grsegs[STARTTILE8M],(t)*32,bufferofs+ylookup[y]+(x),2,8,16) +#define VW_DrawTile16(x,y,t) \ + VW_MemToScreen(grsegs[STARTTILE16+t],bufferofs+ylookup[y]+(x),4,16) +#define VW_DrawTile16M(x,y,t) \ + VW_MaskBlock(grsegs[STARTTILE16M],(t)*128,bufferofs+ylookup[y]+(x),4,16,64) + +#endif + +void VW_DrawPic(unsigned x, unsigned y, unsigned chunknum); +void VW_DrawPic2x(unsigned x, unsigned y, unsigned chunknum); +void VW_DrawMPic(unsigned x, unsigned y, unsigned chunknum); +void VW_ClipDrawMPic(unsigned x, int y, unsigned chunknum); + +// +// pixel addressable routines +// +void VW_MeasurePropString (char far *string, word *width, word *height); +void VW_MeasureMPropString (char far *string, word *width, word *height); + +void VW_DrawPropString (char far *string); +void VW_DrawMPropString (char far *string); +void VW_DrawSprite(int x, int y, unsigned sprite); +void VW_Plot(unsigned x, unsigned y, unsigned color); +void VW_Hlin(unsigned xl, unsigned xh, unsigned y, unsigned color); +void VW_Vlin(unsigned yl, unsigned yh, unsigned x, unsigned color); +void VW_Bar (unsigned x, unsigned y, unsigned width, unsigned height, + unsigned color); + +//=========================================================================== + +// +// Double buffer management routines +// + +void VW_InitDoubleBuffer (void); +void VW_FixRefreshBuffer (void); +int VW_MarkUpdateBlock (int x1, int y1, int x2, int y2); +void VW_UpdateScreen (void); +void VW_CGAFullUpdate (void); + +// +// cursor +// + +void VW_ShowCursor (void); +void VW_HideCursor (void); +void VW_MoveCursor (int x, int y); +void VW_SetCursor (int spritenum); +void VW_FreeCursor (void); + +// +// mode independant routines +// coordinates in pixels, rounded to best screen res +// regions marked in double buffer +// + +void VWB_DrawTile8 (int x, int y, int tile); +void VWB_DrawTile8M (int x, int y, int tile); +void VWB_DrawTile16 (int x, int y, int tile); +void VWB_DrawTile16M (int x, int y, int tile); +void VWB_DrawPic (int x, int y, int chunknum); +void VWB_DrawMPic(int x, int y, int chunknum); +void VWB_Bar (int x, int y, int width, int height, int color); + +void VWB_DrawPropString (char far *string); +void VWB_DrawMPropString (char far *string); +void VWB_DrawSprite (int x, int y, int chunknum); +void VWB_Plot (int x, int y, int color); +void VWB_Hlin (int x1, int x2, int y, int color); +void VWB_Vlin (int y1, int y2, int x, int color); + +void VWL_MeasureString (char far *string, word *width, word *height, fontstruct _seg *font); +//=========================================================================== diff --git a/16/cawat/ID_VW_A.ASM b/16/cawat/ID_VW_A.ASM new file mode 100644 index 00000000..d6376b25 --- /dev/null +++ b/16/cawat/ID_VW_A.ASM @@ -0,0 +1,752 @@ +; Catacomb Armageddon Source Code +; Copyright (C) 1993-2014 Flat Rock Software +; +; This program is free software; you can redistribute it and/or modify +; it under the terms of the GNU General Public License as published by +; the Free Software Foundation; either version 2 of the License, or +; (at your option) any later version. +; +; This program is distributed in the hope that it will be useful, +; but WITHOUT ANY WARRANTY; without even the implied warranty of +; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +; GNU General Public License for more details. +; +; You should have received a copy of the GNU General Public License along +; with this program; if not, write to the Free Software Foundation, Inc., +; 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +; ID_VW_A.ASM + +IDEAL +MODEL MEDIUM,C + +INCLUDE "ID_ASM.EQU" + +WAITFORVBL = 1 ; setting to 0 causes setscreen and waitvbl + ; to skip waiting for VBL (for timing things) + +;============================================================================ + +DATASEG + +EXTRN screenseg :WORD +EXTRN drawofs :WORD +EXTRN bufferofs :WORD +EXTRN displayofs :WORD +EXTRN drawofs :WORD +EXTRN panadjust :WORD +EXTRN ylookup :WORD +EXTRN linewidth :WORD +EXTRN grsegs :WORD +EXTRN updateptr :WORD +EXTRN blockstarts :WORD ;offsets from drawofs for each update block +EXTRN fontspace :WORD +EXTRN fontnumber :WORD + + +planemask db ? +planenum db ? +screendest dw ? +linedelta dw ? + +LABEL shiftdata0 WORD +; dw 0 + +if 1 + dw 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13 + dw 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27 + dw 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41 + dw 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55 + dw 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69 + dw 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83 + dw 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97 + dw 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111 + dw 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125 + dw 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139 + dw 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153 + dw 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167 + dw 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181 + dw 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195 + dw 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209 + dw 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223 + dw 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237 + dw 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251 + dw 252, 253, 254, 255 +endif + +LABEL shiftdata1 WORD +; dw 0 + +if 1 + dw 0,32768, 1,32769, 2,32770, 3,32771, 4,32772, 5,32773, 6,32774 + dw 7,32775, 8,32776, 9,32777, 10,32778, 11,32779, 12,32780, 13,32781 + dw 14,32782, 15,32783, 16,32784, 17,32785, 18,32786, 19,32787, 20,32788 + dw 21,32789, 22,32790, 23,32791, 24,32792, 25,32793, 26,32794, 27,32795 + dw 28,32796, 29,32797, 30,32798, 31,32799, 32,32800, 33,32801, 34,32802 + dw 35,32803, 36,32804, 37,32805, 38,32806, 39,32807, 40,32808, 41,32809 + dw 42,32810, 43,32811, 44,32812, 45,32813, 46,32814, 47,32815, 48,32816 + dw 49,32817, 50,32818, 51,32819, 52,32820, 53,32821, 54,32822, 55,32823 + dw 56,32824, 57,32825, 58,32826, 59,32827, 60,32828, 61,32829, 62,32830 + dw 63,32831, 64,32832, 65,32833, 66,32834, 67,32835, 68,32836, 69,32837 + dw 70,32838, 71,32839, 72,32840, 73,32841, 74,32842, 75,32843, 76,32844 + dw 77,32845, 78,32846, 79,32847, 80,32848, 81,32849, 82,32850, 83,32851 + dw 84,32852, 85,32853, 86,32854, 87,32855, 88,32856, 89,32857, 90,32858 + dw 91,32859, 92,32860, 93,32861, 94,32862, 95,32863, 96,32864, 97,32865 + dw 98,32866, 99,32867, 100,32868, 101,32869, 102,32870, 103,32871, 104,32872 + dw 105,32873, 106,32874, 107,32875, 108,32876, 109,32877, 110,32878, 111,32879 + dw 112,32880, 113,32881, 114,32882, 115,32883, 116,32884, 117,32885, 118,32886 + dw 119,32887, 120,32888, 121,32889, 122,32890, 123,32891, 124,32892, 125,32893 + dw 126,32894, 127,32895 +endif + +LABEL shiftdata2 WORD + dw 0,16384,32768,49152, 1,16385,32769,49153, 2,16386,32770,49154, 3,16387 + dw 32771,49155, 4,16388,32772,49156, 5,16389,32773,49157, 6,16390,32774,49158 + dw 7,16391,32775,49159, 8,16392,32776,49160, 9,16393,32777,49161, 10,16394 + dw 32778,49162, 11,16395,32779,49163, 12,16396,32780,49164, 13,16397,32781,49165 + dw 14,16398,32782,49166, 15,16399,32783,49167, 16,16400,32784,49168, 17,16401 + dw 32785,49169, 18,16402,32786,49170, 19,16403,32787,49171, 20,16404,32788,49172 + dw 21,16405,32789,49173, 22,16406,32790,49174, 23,16407,32791,49175, 24,16408 + dw 32792,49176, 25,16409,32793,49177, 26,16410,32794,49178, 27,16411,32795,49179 + dw 28,16412,32796,49180, 29,16413,32797,49181, 30,16414,32798,49182, 31,16415 + dw 32799,49183, 32,16416,32800,49184, 33,16417,32801,49185, 34,16418,32802,49186 + dw 35,16419,32803,49187, 36,16420,32804,49188, 37,16421,32805,49189, 38,16422 + dw 32806,49190, 39,16423,32807,49191, 40,16424,32808,49192, 41,16425,32809,49193 + dw 42,16426,32810,49194, 43,16427,32811,49195, 44,16428,32812,49196, 45,16429 + dw 32813,49197, 46,16430,32814,49198, 47,16431,32815,49199, 48,16432,32816,49200 + dw 49,16433,32817,49201, 50,16434,32818,49202, 51,16435,32819,49203, 52,16436 + dw 32820,49204, 53,16437,32821,49205, 54,16438,32822,49206, 55,16439,32823,49207 + dw 56,16440,32824,49208, 57,16441,32825,49209, 58,16442,32826,49210, 59,16443 + dw 32827,49211, 60,16444,32828,49212, 61,16445,32829,49213, 62,16446,32830,49214 + dw 63,16447,32831,49215 + +LABEL shiftdata3 WORD +; dw 0 + +if 1 + dw 0, 8192,16384,24576,32768,40960,49152,57344, 1, 8193,16385,24577,32769,40961 + dw 49153,57345, 2, 8194,16386,24578,32770,40962,49154,57346, 3, 8195,16387,24579 + dw 32771,40963,49155,57347, 4, 8196,16388,24580,32772,40964,49156,57348, 5, 8197 + dw 16389,24581,32773,40965,49157,57349, 6, 8198,16390,24582,32774,40966,49158,57350 + dw 7, 8199,16391,24583,32775,40967,49159,57351, 8, 8200,16392,24584,32776,40968 + dw 49160,57352, 9, 8201,16393,24585,32777,40969,49161,57353, 10, 8202,16394,24586 + dw 32778,40970,49162,57354, 11, 8203,16395,24587,32779,40971,49163,57355, 12, 8204 + dw 16396,24588,32780,40972,49164,57356, 13, 8205,16397,24589,32781,40973,49165,57357 + dw 14, 8206,16398,24590,32782,40974,49166,57358, 15, 8207,16399,24591,32783,40975 + dw 49167,57359, 16, 8208,16400,24592,32784,40976,49168,57360, 17, 8209,16401,24593 + dw 32785,40977,49169,57361, 18, 8210,16402,24594,32786,40978,49170,57362, 19, 8211 + dw 16403,24595,32787,40979,49171,57363, 20, 8212,16404,24596,32788,40980,49172,57364 + dw 21, 8213,16405,24597,32789,40981,49173,57365, 22, 8214,16406,24598,32790,40982 + dw 49174,57366, 23, 8215,16407,24599,32791,40983,49175,57367, 24, 8216,16408,24600 + dw 32792,40984,49176,57368, 25, 8217,16409,24601,32793,40985,49177,57369, 26, 8218 + dw 16410,24602,32794,40986,49178,57370, 27, 8219,16411,24603,32795,40987,49179,57371 + dw 28, 8220,16412,24604,32796,40988,49180,57372, 29, 8221,16413,24605,32797,40989 + dw 49181,57373, 30, 8222,16414,24606,32798,40990,49182,57374, 31, 8223,16415,24607 + dw 32799,40991,49183,57375 +endif + +LABEL shiftdata4 WORD + dw 0, 4096, 8192,12288,16384,20480,24576,28672,32768,36864,40960,45056,49152,53248 + dw 57344,61440, 1, 4097, 8193,12289,16385,20481,24577,28673,32769,36865,40961,45057 + dw 49153,53249,57345,61441, 2, 4098, 8194,12290,16386,20482,24578,28674,32770,36866 + dw 40962,45058,49154,53250,57346,61442, 3, 4099, 8195,12291,16387,20483,24579,28675 + dw 32771,36867,40963,45059,49155,53251,57347,61443, 4, 4100, 8196,12292,16388,20484 + dw 24580,28676,32772,36868,40964,45060,49156,53252,57348,61444, 5, 4101, 8197,12293 + dw 16389,20485,24581,28677,32773,36869,40965,45061,49157,53253,57349,61445, 6, 4102 + dw 8198,12294,16390,20486,24582,28678,32774,36870,40966,45062,49158,53254,57350,61446 + dw 7, 4103, 8199,12295,16391,20487,24583,28679,32775,36871,40967,45063,49159,53255 + dw 57351,61447, 8, 4104, 8200,12296,16392,20488,24584,28680,32776,36872,40968,45064 + dw 49160,53256,57352,61448, 9, 4105, 8201,12297,16393,20489,24585,28681,32777,36873 + dw 40969,45065,49161,53257,57353,61449, 10, 4106, 8202,12298,16394,20490,24586,28682 + dw 32778,36874,40970,45066,49162,53258,57354,61450, 11, 4107, 8203,12299,16395,20491 + dw 24587,28683,32779,36875,40971,45067,49163,53259,57355,61451, 12, 4108, 8204,12300 + dw 16396,20492,24588,28684,32780,36876,40972,45068,49164,53260,57356,61452, 13, 4109 + dw 8205,12301,16397,20493,24589,28685,32781,36877,40973,45069,49165,53261,57357,61453 + dw 14, 4110, 8206,12302,16398,20494,24590,28686,32782,36878,40974,45070,49166,53262 + dw 57358,61454, 15, 4111, 8207,12303,16399,20495,24591,28687,32783,36879,40975,45071 + dw 49167,53263,57359,61455 + +LABEL shiftdata5 WORD +; dw 0 + +if 1 + dw 0, 2048, 4096, 6144, 8192,10240,12288,14336,16384,18432,20480,22528,24576,26624 + dw 28672,30720,32768,34816,36864,38912,40960,43008,45056,47104,49152,51200,53248,55296 + dw 57344,59392,61440,63488, 1, 2049, 4097, 6145, 8193,10241,12289,14337,16385,18433 + dw 20481,22529,24577,26625,28673,30721,32769,34817,36865,38913,40961,43009,45057,47105 + dw 49153,51201,53249,55297,57345,59393,61441,63489, 2, 2050, 4098, 6146, 8194,10242 + dw 12290,14338,16386,18434,20482,22530,24578,26626,28674,30722,32770,34818,36866,38914 + dw 40962,43010,45058,47106,49154,51202,53250,55298,57346,59394,61442,63490, 3, 2051 + dw 4099, 6147, 8195,10243,12291,14339,16387,18435,20483,22531,24579,26627,28675,30723 + dw 32771,34819,36867,38915,40963,43011,45059,47107,49155,51203,53251,55299,57347,59395 + dw 61443,63491, 4, 2052, 4100, 6148, 8196,10244,12292,14340,16388,18436,20484,22532 + dw 24580,26628,28676,30724,32772,34820,36868,38916,40964,43012,45060,47108,49156,51204 + dw 53252,55300,57348,59396,61444,63492, 5, 2053, 4101, 6149, 8197,10245,12293,14341 + dw 16389,18437,20485,22533,24581,26629,28677,30725,32773,34821,36869,38917,40965,43013 + dw 45061,47109,49157,51205,53253,55301,57349,59397,61445,63493, 6, 2054, 4102, 6150 + dw 8198,10246,12294,14342,16390,18438,20486,22534,24582,26630,28678,30726,32774,34822 + dw 36870,38918,40966,43014,45062,47110,49158,51206,53254,55302,57350,59398,61446,63494 + dw 7, 2055, 4103, 6151, 8199,10247,12295,14343,16391,18439,20487,22535,24583,26631 + dw 28679,30727,32775,34823,36871,38919,40967,43015,45063,47111,49159,51207,53255,55303 + dw 57351,59399,61447,63495 +endif + +LABEL shiftdata6 WORD + dw 0, 1024, 2048, 3072, 4096, 5120, 6144, 7168, 8192, 9216,10240,11264,12288,13312 + dw 14336,15360,16384,17408,18432,19456,20480,21504,22528,23552,24576,25600,26624,27648 + dw 28672,29696,30720,31744,32768,33792,34816,35840,36864,37888,38912,39936,40960,41984 + dw 43008,44032,45056,46080,47104,48128,49152,50176,51200,52224,53248,54272,55296,56320 + dw 57344,58368,59392,60416,61440,62464,63488,64512, 1, 1025, 2049, 3073, 4097, 5121 + dw 6145, 7169, 8193, 9217,10241,11265,12289,13313,14337,15361,16385,17409,18433,19457 + dw 20481,21505,22529,23553,24577,25601,26625,27649,28673,29697,30721,31745,32769,33793 + dw 34817,35841,36865,37889,38913,39937,40961,41985,43009,44033,45057,46081,47105,48129 + dw 49153,50177,51201,52225,53249,54273,55297,56321,57345,58369,59393,60417,61441,62465 + dw 63489,64513, 2, 1026, 2050, 3074, 4098, 5122, 6146, 7170, 8194, 9218,10242,11266 + dw 12290,13314,14338,15362,16386,17410,18434,19458,20482,21506,22530,23554,24578,25602 + dw 26626,27650,28674,29698,30722,31746,32770,33794,34818,35842,36866,37890,38914,39938 + dw 40962,41986,43010,44034,45058,46082,47106,48130,49154,50178,51202,52226,53250,54274 + dw 55298,56322,57346,58370,59394,60418,61442,62466,63490,64514, 3, 1027, 2051, 3075 + dw 4099, 5123, 6147, 7171, 8195, 9219,10243,11267,12291,13315,14339,15363,16387,17411 + dw 18435,19459,20483,21507,22531,23555,24579,25603,26627,27651,28675,29699,30723,31747 + dw 32771,33795,34819,35843,36867,37891,38915,39939,40963,41987,43011,44035,45059,46083 + dw 47107,48131,49155,50179,51203,52227,53251,54275,55299,56323,57347,58371,59395,60419 + dw 61443,62467,63491,64515 + +LABEL shiftdata7 WORD +; dw 0 + +if 1 + dw 0, 512, 1024, 1536, 2048, 2560, 3072, 3584, 4096, 4608, 5120, 5632, 6144, 6656 + dw 7168, 7680, 8192, 8704, 9216, 9728,10240,10752,11264,11776,12288,12800,13312,13824 + dw 14336,14848,15360,15872,16384,16896,17408,17920,18432,18944,19456,19968,20480,20992 + dw 21504,22016,22528,23040,23552,24064,24576,25088,25600,26112,26624,27136,27648,28160 + dw 28672,29184,29696,30208,30720,31232,31744,32256,32768,33280,33792,34304,34816,35328 + dw 35840,36352,36864,37376,37888,38400,38912,39424,39936,40448,40960,41472,41984,42496 + dw 43008,43520,44032,44544,45056,45568,46080,46592,47104,47616,48128,48640,49152,49664 + dw 50176,50688,51200,51712,52224,52736,53248,53760,54272,54784,55296,55808,56320,56832 + dw 57344,57856,58368,58880,59392,59904,60416,60928,61440,61952,62464,62976,63488,64000 + dw 64512,65024, 1, 513, 1025, 1537, 2049, 2561, 3073, 3585, 4097, 4609, 5121, 5633 + dw 6145, 6657, 7169, 7681, 8193, 8705, 9217, 9729,10241,10753,11265,11777,12289,12801 + dw 13313,13825,14337,14849,15361,15873,16385,16897,17409,17921,18433,18945,19457,19969 + dw 20481,20993,21505,22017,22529,23041,23553,24065,24577,25089,25601,26113,26625,27137 + dw 27649,28161,28673,29185,29697,30209,30721,31233,31745,32257,32769,33281,33793,34305 + dw 34817,35329,35841,36353,36865,37377,37889,38401,38913,39425,39937,40449,40961,41473 + dw 41985,42497,43009,43521,44033,44545,45057,45569,46081,46593,47105,47617,48129,48641 + dw 49153,49665,50177,50689,51201,51713,52225,52737,53249,53761,54273,54785,55297,55809 + dw 56321,56833,57345,57857,58369,58881,59393,59905,60417,60929,61441,61953,62465,62977 + dw 63489,64001,64513,65025 +endif + +shifttabletable dw shiftdata0,shiftdata1,shiftdata2,shiftdata3 + dw shiftdata4,shiftdata5,shiftdata6,shiftdata7 + +PUBLIC shifttabletable + + +;============================================================================ + +CODESEG + +IFE GRMODE-CGAGR +INCLUDE "ID_VW_AC.ASM" +ENDIF + +IFE GRMODE-EGAGR +INCLUDE "ID_VW_AE.ASM" +ENDIF + +IFE GRMODE-VGAGR +INCLUDE "ID_VW_AV.ASM" +ENDIF + +;============================================================================ +; +; MISC VIDEO ROUTINES +; +;============================================================================ + +;======== +; +; VW_WaitVBL (int number) +; +;======== + +PROC VW_WaitVBL number:WORD +PUBLIC VW_WaitVBL + +if WAITFORVBL ; skip wait if profiling + + mov dx,STATUS_REGISTER_1 + + mov cx,[number] + +waitvbl1: + in al,dx + test al,00001000b ;look for vbl + jnz waitvbl1 + +waitvbl2: + in al,dx + test al,00001000b ;look for vbl + jz waitvbl2 + + loop waitvbl1 + +endif + + ret + +ENDP + + +;=========================================================================== + + + MASM +;ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ +; +; Name: VW_VideoID +; +; Function: Detects the presence of various video subsystems +; +; int VideoID; +; +; Subsystem ID values: +; 0 = (none) +; 1 = MDA +; 2 = CGA +; 3 = EGA +; 4 = MCGA +; 5 = VGA +; 80h = HGC +; 81h = HGC+ +; 82h = Hercules InColor +; +;ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ + +;ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ +; +; Equates +; +;ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ +VIDstruct STRUC ; corresponds to C data structure + +Video0Type DB ? ; first subsystem type +Display0Type DB ? ; display attached to first subsystem + +Video1Type DB ? ; second subsystem type +Display1Type DB ? ; display attached to second subsystem + +VIDstruct ENDS + + +Device0 EQU word ptr Video0Type[di] +Device1 EQU word ptr Video1Type[di] + + +MDA EQU 1 ; subsystem types +CGA EQU 2 +EGA EQU 3 +MCGA EQU 4 +VGA EQU 5 +HGC EQU 80h +HGCPlus EQU 81h +InColor EQU 82h + +MDADisplay EQU 1 ; display types +CGADisplay EQU 2 +EGAColorDisplay EQU 3 +PS2MonoDisplay EQU 4 +PS2ColorDisplay EQU 5 + +TRUE EQU 1 +FALSE EQU 0 + +;ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ +; +; Program +; +;ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ + +Results VIDstruct <> ;results go here! + +EGADisplays DB CGADisplay ; 0000b, 0001b (EGA switch values) + DB EGAColorDisplay ; 0010b, 0011b + DB MDADisplay ; 0100b, 0101b + DB CGADisplay ; 0110b, 0111b + DB EGAColorDisplay ; 1000b, 1001b + DB MDADisplay ; 1010b, 1011b + +DCCtable DB 0,0 ; translate table for INT 10h func 1Ah + DB MDA,MDADisplay + DB CGA,CGADisplay + DB 0,0 + DB EGA,EGAColorDisplay + DB EGA,MDADisplay + DB 0,0 + DB VGA,PS2MonoDisplay + DB VGA,PS2ColorDisplay + DB 0,0 + DB MCGA,EGAColorDisplay + DB MCGA,PS2MonoDisplay + DB MCGA,PS2ColorDisplay + +TestSequence DB TRUE ; this list of flags and addresses + DW FindPS2 ; determines the order in which this + ; program looks for the various +EGAflag DB ? ; subsystems + DW FindEGA + +CGAflag DB ? + DW FindCGA + +Monoflag DB ? + DW FindMono + +NumberOfTests EQU ($-TestSequence)/3 + + +PUBLIC VW_VideoID +VW_VideoID PROC + + push bp ; preserve caller registers + mov bp,sp + push ds + push si + push di + + push cs + pop ds + ASSUME DS:@Code + +; initialize the data structure that will contain the results + + lea di,Results ; DS:DI -> start of data structure + + mov Device0,0 ; zero these variables + mov Device1,0 + +; look for the various subsystems using the subroutines whose addresses are +; tabulated in TestSequence; each subroutine sets flags in TestSequence +; to indicate whether subsequent subroutines need to be called + + mov byte ptr CGAflag,TRUE + mov byte ptr EGAflag,TRUE + mov byte ptr Monoflag,TRUE + + mov cx,NumberOfTests + mov si,offset TestSequence + +@@L01: lodsb ; AL := flag + test al,al + lodsw ; AX := subroutine address + jz @@L02 ; skip subroutine if flag is false + + push si + push cx + call ax ; call subroutine to detect subsystem + pop cx + pop si + +@@L02: loop @@L01 + +; determine which subsystem is active + + call FindActive + + mov al,Results.Video0Type + mov ah,0 ; was: Results.Display0Type + + pop di ; restore caller registers and return + pop si + pop ds + mov sp,bp + pop bp + ret + +VW_VideoID ENDP + + +; +; FindPS2 +; +; This subroutine uses INT 10H function 1Ah to determine the video BIOS +; Display Combination Code (DCC) for each video subsystem present. +; + +FindPS2 PROC near + + mov ax,1A00h + int 10h ; call video BIOS for info + + cmp al,1Ah + jne @@L13 ; exit if function not supported (i.e., + ; no MCGA or VGA in system) + +; convert BIOS DCCs into specific subsystems & displays + + mov cx,bx + xor bh,bh ; BX := DCC for active subsystem + + or ch,ch + jz @@L11 ; jump if only one subsystem present + + mov bl,ch ; BX := inactive DCC + add bx,bx + mov ax,[bx+offset DCCtable] + + mov Device1,ax + + mov bl,cl + xor bh,bh ; BX := active DCC + +@@L11: add bx,bx + mov ax,[bx+offset DCCtable] + + mov Device0,ax + +; reset flags for subsystems that have been ruled out + + mov byte ptr CGAflag,FALSE + mov byte ptr EGAflag,FALSE + mov byte ptr Monoflag,FALSE + + lea bx,Video0Type[di] ; if the BIOS reported an MDA ... + cmp byte ptr [bx],MDA + je @@L12 + + lea bx,Video1Type[di] + cmp byte ptr [bx],MDA + jne @@L13 + +@@L12: mov word ptr [bx],0 ; ... Hercules can't be ruled out + mov byte ptr Monoflag,TRUE + +@@L13: ret + +FindPS2 ENDP + + +; +; FindEGA +; +; Look for an EGA. This is done by making a call to an EGA BIOS function +; which doesn't exist in the default (MDA, CGA) BIOS. + +FindEGA PROC near ; Caller: AH = flags + ; Returns: AH = flags + ; Video0Type and + ; Display0Type updated + + mov bl,10h ; BL := 10h (return EGA info) + mov ah,12h ; AH := INT 10H function number + int 10h ; call EGA BIOS for info + ; if EGA BIOS is present, + ; BL <> 10H + ; CL = switch setting + cmp bl,10h + je @@L22 ; jump if EGA BIOS not present + + mov al,cl + shr al,1 ; AL := switches/2 + mov bx,offset EGADisplays + xlat ; determine display type from switches + mov ah,al ; AH := display type + mov al,EGA ; AL := subystem type + call FoundDevice + + cmp ah,MDADisplay + je @@L21 ; jump if EGA has a monochrome display + + mov CGAflag,FALSE ; no CGA if EGA has color display + jmp short @@L22 + +@@L21: mov Monoflag,FALSE ; EGA has a mono display, so MDA and + ; Hercules are ruled out +@@L22: ret + +FindEGA ENDP + +; +; FindCGA +; +; This is done by looking for the CGA's 6845 CRTC at I/O port 3D4H. +; +FindCGA PROC near ; Returns: VIDstruct updated + + mov dx,3D4h ; DX := CRTC address port + call Find6845 + jc @@L31 ; jump if not present + + mov al,CGA + mov ah,CGADisplay + call FoundDevice + +@@L31: ret + +FindCGA ENDP + +; +; FindMono +; +; This is done by looking for the MDA's 6845 CRTC at I/O port 3B4H. If +; a 6845 is found, the subroutine distinguishes between an MDA +; and a Hercules adapter by monitoring bit 7 of the CRT Status byte. +; This bit changes on Hercules adapters but does not change on an MDA. +; +; The various Hercules adapters are identified by bits 4 through 6 of +; the CRT Status value: +; +; 000b = HGC +; 001b = HGC+ +; 101b = InColor card +; + +FindMono PROC near ; Returns: VIDstruct updated + + mov dx,3B4h ; DX := CRTC address port + call Find6845 + jc @@L44 ; jump if not present + + mov dl,0BAh ; DX := 3BAh (status port) + in al,dx + and al,80h + mov ah,al ; AH := bit 7 (vertical sync on HGC) + + mov cx,8000h ; do this 32768 times +@@L41: in al,dx + and al,80h ; isolate bit 7 + cmp ah,al + loope @@L41 ; wait for bit 7 to change + jne @@L42 ; if bit 7 changed, it's a Hercules + + mov al,MDA ; if bit 7 didn't change, it's an MDA + mov ah,MDADisplay + call FoundDevice + jmp short @@L44 + +@@L42: in al,dx + mov dl,al ; DL := value from status port + and dl,01110000b ; mask bits 4 thru 6 + + mov ah,MDADisplay ; assume it's a monochrome display + + mov al,HGCPlus ; look for an HGC+ + cmp dl,00010000b + je @@L43 ; jump if it's an HGC+ + + mov al,HGC ; look for an InColor card or HGC + cmp dl,01010000b + jne @@L43 ; jump if it's not an InColor card + + mov al,InColor ; it's an InColor card + mov ah,EGAColorDisplay + +@@L43: call FoundDevice + +@@L44: ret + +FindMono ENDP + +; +; Find6845 +; +; This routine detects the presence of the CRTC on a MDA, CGA or HGC. +; The technique is to write and read register 0Fh of the chip (cursor +; low). If the same value is read as written, assume the chip is +; present at the specified port addr. +; + +Find6845 PROC near ; Caller: DX = port addr + ; Returns: cf set if not present + mov al,0Fh + out dx,al ; select 6845 reg 0Fh (Cursor Low) + inc dx + in al,dx ; AL := current Cursor Low value + mov ah,al ; preserve in AH + mov al,66h ; AL := arbitrary value + out dx,al ; try to write to 6845 + + mov cx,100h +@@L51: loop @@L51 ; wait for 6845 to respond + + in al,dx + xchg ah,al ; AH := returned value + ; AL := original value + out dx,al ; restore original value + + cmp ah,66h ; test whether 6845 responded + je @@L52 ; jump if it did (cf is reset) + + stc ; set carry flag if no 6845 present + +@@L52: ret + +Find6845 ENDP + + +; +; FindActive +; +; This subroutine stores the currently active device as Device0. The +; current video mode determines which subsystem is active. +; + +FindActive PROC near + + cmp word ptr Device1,0 + je @@L63 ; exit if only one subsystem + + cmp Video0Type[di],4 ; exit if MCGA or VGA present + jge @@L63 ; (INT 10H function 1AH + cmp Video1Type[di],4 ; already did the work) + jge @@L63 + + mov ah,0Fh + int 10h ; AL := current BIOS video mode + + and al,7 + cmp al,7 ; jump if monochrome + je @@L61 ; (mode 7 or 0Fh) + + cmp Display0Type[di],MDADisplay + jne @@L63 ; exit if Display0 is color + jmp short @@L62 + +@@L61: cmp Display0Type[di],MDADisplay + je @@L63 ; exit if Display0 is monochrome + +@@L62: mov ax,Device0 ; make Device0 currently active + xchg ax,Device1 + mov Device0,ax + +@@L63: ret + +FindActive ENDP + + +; +; FoundDevice +; +; This routine updates the list of subsystems. +; + +FoundDevice PROC near ; Caller: AH = display # + ; AL = subsystem # + ; Destroys: BX + lea bx,Video0Type[di] + cmp byte ptr [bx],0 + je @@L71 ; jump if 1st subsystem + + lea bx,Video1Type[di] ; must be 2nd subsystem + +@@L71: mov [bx],ax ; update list entry + ret + +FoundDevice ENDP + +IDEAL + + + +END diff --git a/16/cawat/ID_VW_AC.ASM b/16/cawat/ID_VW_AC.ASM new file mode 100644 index 00000000..97352bec --- /dev/null +++ b/16/cawat/ID_VW_AC.ASM @@ -0,0 +1,1485 @@ +; Catacomb Armageddon Source Code +; Copyright (C) 1993-2014 Flat Rock Software +; +; This program is free software; you can redistribute it and/or modify +; it under the terms of the GNU General Public License as published by +; the Free Software Foundation; either version 2 of the License, or +; (at your option) any later version. +; +; This program is distributed in the hope that it will be useful, +; but WITHOUT ANY WARRANTY; without even the implied warranty of +; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +; GNU General Public License for more details. +; +; You should have received a copy of the GNU General Public License along +; with this program; if not, write to the Free Software Foundation, Inc., +; 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +;================================= +; +; CGA view manager routines +; +;================================= + +;============================================================================ +; +; All of these routines draw into a floating virtual screen segment in main +; memory. bufferofs points to the origin of the drawing page in screenseg. +; The routines that write out words must take into account buffer wrapping +; and not write a word at 0xffff (which causes an exception on 386s). +; +; The direction flag should be clear +; +;============================================================================ + +DATASEG + +plotpixels db 0c0h,030h,0ch,03h +colorbyte db 000000b,01010101b,10101010b,11111111b +colorword dw 0,5555h,0aaaah,0ffffh + +CODESEG + +;============================================================================ +; +; VW_Plot (int x,y,color) +; +;============================================================================ + + +PROC VW_Plot x:WORD, y:WORD, color:WORD +PUBLIC VW_Plot +USES SI,DI + + mov es,[screenseg] + + mov di,[bufferofs] + mov bx,[y] + shl bx,1 + add di,[ylookup+bx] + mov bx,[x] + mov ax,bx + shr ax,1 + shr ax,1 + add di,ax ; di = byte on screen + + and bx,3 + mov ah,[plotpixels+bx] + mov bx,[color] + mov cl,[colorbyte+bx] + and cl,ah + not ah + + mov al,[es:di] + and al,ah ; mask off other pixels + or al,cl + stosb + + ret + +ENDP + + +;============================================================================ +; +; VW_Vlin (int yl,yh,x,color) +; +;============================================================================ + +PROC VW_Vlin yl:WORD, yh:WORD, x:WORD, color:WORD +PUBLIC VW_Vlin +USES SI,DI + + mov es,[screenseg] + + mov di,[bufferofs] + mov bx,[yl] + shl bx,1 + add di,[ylookup+bx] + mov bx,[x] + mov ax,bx + shr ax,1 + shr ax,1 + add di,ax ; di = byte on screen + + and bx,3 + mov ah,[plotpixels+bx] + mov bx,[color] + mov bl,[colorbyte+bx] + and bl,ah + not ah + + mov cx,[yh] + sub cx,[yl] + inc cx ;number of pixels to plot + + mov dx,[linewidth] + +@@plot: + mov al,[es:di] + and al,ah ; mask off other pixels + or al,bl + mov [es:di],al + add di,dx + loop @@plot + + ret + + ret + +ENDP + + +;============================================================================ + + +;=================== +; +; VW_DrawTile8 +; +; xcoord in bytes (8 pixels), ycoord in pixels +; All Tile8s are in one grseg, so an offset is calculated inside it +; +; DONE +; +;=================== + +PROC VW_DrawTile8 xcoord:WORD, ycoord:WORD, tile:WORD +PUBLIC VW_DrawTile8 +USES SI,DI + + mov es,[screenseg] + + mov di,[bufferofs] + add di,[xcoord] + mov bx,[ycoord] + shl bx,1 + add di,[ylookup+bx] + + mov bx,[linewidth] + sub bx,2 + + mov si,[tile] + shl si,1 + shl si,1 + shl si,1 + shl si,1 + + mov ds,[grsegs+STARTTILE8*2] ; segment for all tile8s + +; +; start drawing +; + +REPT 7 + movsb ;no word moves because of segment wrapping + movsb + add di,bx +ENDM + movsb + movsb + + mov ax,ss + mov ds,ax ;restore turbo's data segment + + ret + +ENDP + + +;============================================================================ +; +; VW_MaskBlock +; +; Draws a masked block shape to the screen. bufferofs is NOT accounted for. +; The mask comes first, then the data. Seperate unwound routines are used +; to speed drawing. +; +; Mask blocks will allways be an even width because of the way IGRAB works +; +; DONE +; +;============================================================================ + +DATASEG + +UNWOUNDMASKS = 18 + + +maskroutines dw mask0,mask0,mask2E,mask2O,mask4E,mask4O + dw mask6E,mask6O,mask8E,mask8O,mask10E,mask10O + dw mask12E,mask12O,mask14E,mask14O,mask16E,mask16O + dw mask18E,mask18O + + +routinetouse dw ? + +CODESEG + +PROC VW_MaskBlock segm:WORD, ofs:WORD, dest:WORD, wide:WORD, height:WORD, planesize:WORD +PUBLIC VW_MaskBlock +USES SI,DI + + mov es,[screenseg] + + mov di,[wide] + mov dx,[linewidth] + sub dx,di ;dx = delta to start of next line + + mov bx,[planesize] ; si+bx = data location + + cmp di,UNWOUNDMASKS + jbe @@unwoundroutine + +;============== +; +; General purpose masked block drawing. This could be optimised into +; four routines to use words, but few play loop sprites should be this big! +; +;============== + + mov [ss:linedelta],dx + mov ds,[segm] + mov si,[ofs] + mov di,[dest] + mov dx,[height] ;scan lines to draw + +@@lineloopgen: + mov cx,[wide] +@@byteloop: + mov al,[es:di] + and al,[si] + or al,[bx+si] + inc si + stosb + loop @@byteloop + + add di,[ss:linedelta] + dec dx + jnz @@lineloopgen + +mask0: + mov ax,ss + mov ds,ax + ret ;width of 0 = no drawing + + +;================= +; +; use the unwound routines +; +;================= + +@@unwoundroutine: + shr di,1 ;we only have even width unwound routines + mov cx,[dest] + shr cx,1 + rcl di,1 ;shift a 1 in if destination is odd + shl di,1 + mov ax,[maskroutines+di] ;call the right routine + + mov ds,[segm] + mov si,[ofs] + mov di,[dest] + mov cx,[height] ;scan lines to draw + + jmp ax ;draw it + +;================= +; +; Horizontally unwound routines to draw certain masked blocks faster +; +;================= + +MACRO MASKBYTE + mov al,[es:di] + and al,[si] + or al,[bx+si] + inc si + stosb +ENDM + +MACRO MASKWORD + mov ax,[es:di] + and ax,[si] + or ax,[bx+si] + inc si + inc si + stosw +ENDM + +MACRO SPRITELOOP addr + add di,dx + loop addr + mov ax,ss + mov ds,ax + ret +ENDM + + +EVEN +mask2E: + MASKWORD + SPRITELOOP mask2E + +EVEN +mask2O: + MASKBYTE + MASKBYTE + SPRITELOOP mask2O + +EVEN +mask4E: + MASKWORD + MASKWORD + SPRITELOOP mask4E + +EVEN +mask4O: + MASKBYTE + MASKWORD + MASKBYTE + SPRITELOOP mask4O + +EVEN +mask6E: + MASKWORD + MASKWORD + MASKWORD + SPRITELOOP mask6E + +EVEN +mask6O: + MASKBYTE + MASKWORD + MASKWORD + MASKBYTE + SPRITELOOP mask6O + +EVEN +mask8E: + MASKWORD + MASKWORD + MASKWORD + MASKWORD + SPRITELOOP mask8E + +EVEN +mask8O: + MASKBYTE + MASKWORD + MASKWORD + MASKWORD + MASKBYTE + SPRITELOOP mask8O + +EVEN +mask10E: + MASKWORD + MASKWORD + MASKWORD + MASKWORD + MASKWORD + SPRITELOOP mask10E + +EVEN +mask10O: + MASKBYTE + MASKWORD + MASKWORD + MASKWORD + MASKWORD + MASKBYTE + SPRITELOOP mask10O + +EVEN +mask12E: + MASKWORD + MASKWORD + MASKWORD + MASKWORD + MASKWORD + MASKWORD + SPRITELOOP mask12E + +EVEN +mask12O: + MASKBYTE + MASKWORD + MASKWORD + MASKWORD + MASKWORD + MASKWORD + MASKBYTE + SPRITELOOP mask12O + +EVEN +mask14E: + MASKWORD + MASKWORD + MASKWORD + MASKWORD + MASKWORD + MASKWORD + MASKWORD + SPRITELOOP mask14E + +EVEN +mask14O: + MASKBYTE + MASKWORD + MASKWORD + MASKWORD + MASKWORD + MASKWORD + MASKWORD + MASKBYTE + SPRITELOOP mask14O + +EVEN +mask16E: + MASKWORD + MASKWORD + MASKWORD + MASKWORD + MASKWORD + MASKWORD + MASKWORD + MASKWORD + SPRITELOOP mask16E + +EVEN +mask16O: + MASKBYTE + MASKWORD + MASKWORD + MASKWORD + MASKWORD + MASKWORD + MASKWORD + MASKWORD + MASKBYTE + SPRITELOOP mask16O + +EVEN +mask18E: + MASKWORD + MASKWORD + MASKWORD + MASKWORD + MASKWORD + MASKWORD + MASKWORD + MASKWORD + MASKWORD + SPRITELOOP mask18E + +EVEN +mask18O: + MASKBYTE + MASKWORD + MASKWORD + MASKWORD + MASKWORD + MASKWORD + MASKWORD + MASKWORD + MASKWORD + MASKBYTE + SPRITELOOP mask18O + + +ENDP + + +;============================================================================ +; +; VW_ScreenToScreen +; +; Basic block copy routine. Copies one block of screen memory to another, +; bufferofs is NOT accounted for. +; +; DONE +; +;============================================================================ + +PROC VW_ScreenToScreen source:WORD, dest:WORD, wide:WORD, height:WORD +PUBLIC VW_ScreenToScreen +USES SI,DI + + mov bx,[linewidth] + sub bx,[wide] + + mov ax,[screenseg] + mov es,ax + mov ds,ax + + mov si,[source] + mov di,[dest] ;start at same place in all planes + mov dx,[height] ;scan lines to draw + mov ax,[wide] +; +; if the width, source, and dest are all even, use word moves +; This is allways the case in the CGA refresh +; + test ax,1 + jnz @@bytelineloop + test si,1 + jnz @@bytelineloop + test di,1 + jnz @@bytelineloop + + shr ax,1 +@@wordlineloop: + mov cx,ax + rep movsw + add si,bx + add di,bx + + dec dx + jnz @@wordlineloop + + mov ax,ss + mov ds,ax ;restore turbo's data segment + + ret + +@@bytelineloop: + mov cx,ax + rep movsb + add si,bx + add di,bx + + dec dx + jnz @@bytelineloop + + mov ax,ss + mov ds,ax ;restore turbo's data segment + + ret + +ENDP + + +;============================================================================ +; +; VW_MemToScreen +; +; Basic block drawing routine. Takes a block shape at segment pointer source +; of width by height data, and draws it to dest in the virtual screen, +; based on linewidth. bufferofs is NOT accounted for. +; There are four drawing routines to provide the best optimized code while +; accounting for odd segment wrappings due to the floating screens. +; +; DONE +; +;============================================================================ + +DATASEG + +memtoscreentable dw eventoeven,eventoodd,oddtoeven,oddtoodd + +CODESEG + + +PROC VW_MemToScreen source:WORD, dest:WORD, wide:WORD, height:WORD +PUBLIC VW_MemToScreen +USES SI,DI + + mov es,[screenseg] + + mov bx,[linewidth] + sub bx,[wide] + + mov ds,[source] + + xor si,si ;block is segment aligned + + xor di,di + shr [wide],1 ;change wide to words, and see if carry is set + rcl di,1 ;1 if wide is odd + mov ax,[dest] + shr ax,1 + rcl di,1 ;shift a 1 in if destination is odd + shl di,1 ;to index into a word width table + mov dx,[height] ;scan lines to draw + mov ax,[wide] + jmp [ss:memtoscreentable+di] ;call the right routine + +;============== +; +; Copy an even width block to an even destination address +; +;============== + +eventoeven: + mov di,[dest] ;start at same place in all planes +EVEN +@@lineloopEE: + mov cx,ax + rep movsw + add di,bx + dec dx + jnz @@lineloopEE + + mov ax,ss + mov ds,ax ;restore turbo's data segment + + ret + +;============== +; +; Copy an odd width block to an even video address +; +;============== + +oddtoeven: + mov di,[dest] ;start at same place in all planes +EVEN +@@lineloopOE: + mov cx,ax + rep movsw + movsb ;copy the last byte + add di,bx + dec dx + jnz @@lineloopOE + + mov ax,ss + mov ds,ax ;restore turbo's data segment + + ret + +;============== +; +; Copy an even width block to an odd video address +; +;============== + +eventoodd: + mov di,[dest] ;start at same place in all planes + dec ax ;one word has to be handled seperately +EVEN +@@lineloopEO: + movsb + mov cx,ax + rep movsw + movsb + add di,bx + dec dx + jnz @@lineloopEO + + mov ax,ss + mov ds,ax ;restore turbo's data segment + + ret + +;============== +; +; Copy an odd width block to an odd video address +; +;============== + +oddtoodd: + mov di,[dest] ;start at same place in all planes +EVEN +@@lineloopOO: + movsb + mov cx,ax + rep movsw + add di,bx + dec dx + jnz @@lineloopOO + + mov ax,ss + mov ds,ax ;restore turbo's data segment + ret + + +ENDP + +;=========================================================================== +; +; VW_ScreenToMem +; +; Copies a block of video memory to main memory, in order from planes 0-3. +; This could be optimized along the lines of VW_MemToScreen to take advantage +; of word copies, but this is an infrequently called routine. +; +; DONE +; +;=========================================================================== + +PROC VW_ScreenToMem source:WORD, dest:WORD, wide:WORD, height:WORD +PUBLIC VW_ScreenToMem +USES SI,DI + + mov es,[dest] + + mov bx,[linewidth] + sub bx,[wide] + + mov ds,[screenseg] + + xor di,di + + mov si,[source] + mov dx,[height] ;scan lines to draw + +@@lineloop: + mov cx,[wide] + rep movsb + + add si,bx + + dec dx + jnz @@lineloop + + mov ax,ss + mov ds,ax ;restore turbo's data segment + + ret + +ENDP + + +;=========================================================================== +; +; MISC CGA ROUTINES +; +;=========================================================================== + +;============== +; +; VW_SetScreen +; +; DONE +; +;============== + +PROC VW_SetScreen crtc:WORD +PUBLIC VW_SetScreen + +; +; for some reason, my XT's EGA card doesn't like word outs to the CRTC +; index... +; + cli + + mov cx,[crtc] + mov dx,CRTC_INDEX + mov al,0ch ;start address high register + out dx,al + inc dx + mov al,ch + out dx,al + dec dx + mov al,0dh ;start address low register + out dx,al + mov al,cl + inc dx + out dx,al + + sti + + ret + +ENDP + + +if NUMFONT+NUMFONTM + +;=========================================================================== +; +; GENERAL FONT DRAWING ROUTINES +; +;=========================================================================== + +DATASEG + +px dw ? ; proportional character drawing coordinates +py dw ? +pdrawmode db 11000b ; 8 = OR, 24 = XOR, put in GC_DATAROTATE +fontcolor db 15 ;0-15 mapmask value + +PUBLIC px,py,pdrawmode,fontcolor + +; +; offsets in font structure +; +pcharheight = 0 ;lines high +charloc = 2 ;pointers to every character +charwidth = 514 ;every character's width in pixels + + +propchar dw ? ; the character number to shift +stringptr dw ?,? + +fontcolormask dw ? ; font color expands into this + +BUFFWIDTH = 100 +BUFFHEIGHT = 32 ; must be twice as high as font for masked fonts + +databuffer db BUFFWIDTH*BUFFHEIGHT dup (?) + +bufferwidth dw ? ; bytes with valid info / line +bufferheight dw ? ; number of lines currently used + +bufferbyte dw ? +bufferbit dw ? +PUBLIC bufferwidth,bufferheight,bufferbyte,bufferbit + +screenspot dw ? ; where the buffer is going + +bufferextra dw ? ; add at end of a line copy +screenextra dw ? + +CODESEG + +;====================== +; +; Macros to table shift a byte of font +; +;====================== + +MACRO SHIFTNOXOR + mov al,[es:bx] ; source + xor ah,ah + shl ax,1 + mov si,ax + mov ax,[bp+si] ; table shift into two bytes + or [di],al ; or with first byte + inc di + mov [di],ah ; replace next byte + inc bx ; next source byte +ENDM + +MACRO SHIFTWITHXOR + mov al,[es:bx] ; source + xor ah,ah + shl ax,1 + mov si,ax + mov ax,[bp+si] ; table shift into two bytes + not ax + and [di],al ; and with first byte + inc di + mov [di],ah ; replace next byte + inc bx ; next source byte +ENDM + + +;======================= +; +; VWL_XORBuffer +; +; Pass buffer start in SI (somewhere in databuffer) +; Draws the buffer to the screen buffer +; +;======================== + +PROC VWL_XORBuffer NEAR +USES BP + mov bl,[fontcolor] + xor bh,bh + shl bx,1 + mov ax,[colorword+bx] + mov [fontcolormask],ax + + mov es,[screenseg] + mov di,[screenspot] + + mov bx,[bufferwidth] ;calculate offsets for end of each line + mov [bufferwidth],bx + + or bx,bx + jnz @@isthere + ret ;nothing to draw + +@@isthere: + test bx,1 + jnz @@odd + jmp @@even +; +; clear the last byte so word draws can be used +; +@@odd: + mov al,0 +line = 0 +REPT BUFFHEIGHT + mov [BYTE databuffer+BUFFWIDTH*line+bx],al +line = line+1 +ENDM + + inc bx +@@even: + mov ax,[linewidth] + sub ax,bx + mov [screenextra],ax + mov ax,BUFFWIDTH + sub ax,bx + mov [bufferextra],ax + mov dx,bx + shr dx,1 ;word to copy + + mov bx,[bufferheight] ;lines to copy + mov bp,[fontcolormask] +@@lineloop: + mov cx,dx +@@wordloop: + lodsw ;get a word from the buffer + and ax,bp + xor [es:di],ax ;draw it + add di,2 + loop @@wordloop + + add si,[bufferextra] + add di,[screenextra] + + dec bx + jnz @@lineloop + + ret +ENDP + + +DATASEG + +;============================================================================ +; +; NON MASKED FONT DRAWING ROUTINES +; +;============================================================================ + +if numfont + +DATASEG + +shiftdrawtable dw 0,shift1wide,shift2wide,shift3wide,shift4wide + dw shift5wide,shift6wide + +CODESEG + +;================== +; +; ShiftPropChar +; +; Call with BX = character number (0-255) +; Draws one character to the buffer at bufferbyte/bufferbit, and adjusts +; them to the new position +; +;================== + +PROC ShiftPropChar NEAR + + mov es,[grsegs+STARTFONT*2] ;segment of font to use + +; +; find character location, width, and height +; + mov si,[es:charwidth+bx] + and si,0ffh ;SI hold width in pixels + shl bx,1 + mov bx,[es:charloc+bx] ;BX holds pointer to character data + +; +; look up which shift table to use, based on bufferbit +; + mov di,[bufferbit] + shl di,1 + mov bp,[shifttabletable+di] ;BP holds pointer to shift table + + mov di,OFFSET databuffer + add di,[bufferbyte] ;DI holds pointer to buffer + + mov cx,[bufferbit] + add cx,si ;add twice because pixel == two bits + add cx,si ;new bit position + mov ax,cx + and ax,7 + mov [bufferbit],ax ;new bit position + mov ax,cx + shr ax,1 + shr ax,1 + shr ax,1 + add [bufferbyte],ax ;new byte position + + add si,3 + shr si,1 + shr si,1 ;bytes the character is wide + shl si,1 ;*2 to look up in shiftdrawtable + + mov cx,[es:pcharheight] + mov dx,BUFFWIDTH + jmp [ss:shiftdrawtable+si] ;procedure to draw this width + +; +; one byte character +; +shift1wide: + dec dx +EVEN +@@loop1: + SHIFTNOXOR + add di,dx ; next line in buffer + + loop @@loop1 + + ret + +; +; two byte character +; +shift2wide: + dec dx + dec dx +EVEN +@@loop2: + SHIFTNOXOR + SHIFTNOXOR + add di,dx ; next line in buffer + + loop @@loop2 + + ret + +; +; three byte character +; +shift3wide: + sub dx,3 +EVEN +@@loop3: + SHIFTNOXOR + SHIFTNOXOR + SHIFTNOXOR + add di,dx ; next line in buffer + + loop @@loop3 + + ret + + +; +; four byte character +; +shift4wide: + sub dx,4 +EVEN +@@loop4: + SHIFTNOXOR + SHIFTNOXOR + SHIFTNOXOR + SHIFTNOXOR + add di,dx ; next line in buffer + + loop @@loop4 + + ret + + +; +; five byte character +; +shift5wide: + sub dx,5 +EVEN +@@loop5: + SHIFTNOXOR + SHIFTNOXOR + SHIFTNOXOR + SHIFTNOXOR + SHIFTNOXOR + add di,dx ; next line in buffer + + loop @@loop5 + + ret + +; +; six byte character +; +shift6wide: + sub dx,6 +EVEN +@@loop6: + SHIFTNOXOR + SHIFTNOXOR + SHIFTNOXOR + SHIFTNOXOR + SHIFTNOXOR + SHIFTNOXOR + add di,dx ; next line in buffer + + loop @@loop6 + + ret + + + +ENDP + +;============================================================================ + +;================== +; +; VW_DrawPropString +; +; Draws a C string of characters at px/py and advances px +; +;================== + +CODESEG + +PROC VW_DrawPropString string:DWORD +PUBLIC VW_DrawPropString +USES SI,DI + +; +; proportional spaceing, which clears the buffer ahead of it, so only +; clear the first collumn +; + mov al,0 +line = 0 +REPT BUFFHEIGHT + mov [BYTE databuffer+BUFFWIDTH*line],al +line = line+1 +ENDM + +; +; shift the characters into the buffer +; +@@shiftchars: + mov ax,[px] + and ax,3 + shl ax,1 ;one pixel == two bits + mov [bufferbit],ax + mov [bufferbyte],0 + + mov ax,[WORD string] + mov [stringptr],ax + mov ax,[WORD string+2] + mov [stringptr+2],ax + +@@shiftone: + mov es,[stringptr+2] + mov bx,[stringptr] + inc [stringptr] + mov bx,[es:bx] + xor bh,bh + or bl,bl + jz @@allshifted + call ShiftPropChar + jmp @@shiftone + +@@allshifted: +; +; calculate position to draw buffer on screen +; + mov bx,[py] + shl bx,1 + mov di,[ylookup+bx] + add di,[bufferofs] + add di,[panadjust] + + mov ax,[px] + shr ax,1 + shr ax,1 ;x location in bytes + add di,ax + mov [screenspot],di + +; +; advance px +; + mov ax,[bufferbyte] + shl ax,1 + shl ax,1 + mov bx,[bufferbit] + shr bx,1 ;two bits == one pixel + or ax,bx + add [px],ax + +; +; draw it +; + mov ax,[bufferbyte] + test [bufferbit],7 + jz @@go + inc ax ;so the partial byte also gets drawn +@@go: + mov [bufferwidth],ax + mov es,[grsegs+STARTFONT*2] + mov ax,[es:pcharheight] + mov [bufferheight],ax + + mov si,OFFSET databuffer + call VWL_XORBuffer + + ret + +ENDP + +endif ;numfont + +;============================================================================ +; +; MASKED FONT DRAWING ROUTINES +; +;============================================================================ + +if numfontm + +DATASEG + +mshiftdrawtable dw 0,mshift1wide,mshift2wide,mshift3wide + + +CODESEG + +;================== +; +; ShiftMPropChar +; +; Call with BX = character number (0-255) +; Draws one character to the buffer at bufferbyte/bufferbit, and adjusts +; them to the new position +; +;================== + +PROC ShiftMPropChar NEAR + + mov es,[grsegs+STARTFONTM*2] ;segment of font to use + +; +; find character location, width, and height +; + mov si,[es:charwidth+bx] + and si,0ffh ;SI hold width in pixels + shl bx,1 + mov bx,[es:charloc+bx] ;BX holds pointer to character data + +; +; look up which shift table to use, based on bufferbit +; + mov di,[bufferbit] + shl di,1 + mov bp,[shifttabletable+di] ;BP holds pointer to shift table + + mov di,OFFSET databuffer + add di,[bufferbyte] ;DI holds pointer to buffer + +; +; advance position by character width +; + mov cx,[bufferbit] + add cx,si ;new bit position + mov ax,cx + and ax,7 + mov [bufferbit],ax ;new bit position + mov ax,cx + shr ax,1 + shr ax,1 + shr ax,1 + add [bufferbyte],ax ;new byte position + + add si,7 + shr si,1 + shr si,1 + shr si,1 ;bytes the character is wide + shl si,1 ;*2 to look up in shiftdrawtable + + mov cx,[es:pcharheight] + mov dx,BUFFWIDTH + jmp [ss:mshiftdrawtable+si] ;procedure to draw this width + +; +; one byte character +; +mshift1wide: + dec dx + +EVEN +@@loop1m: + SHIFTWITHXOR + add di,dx ; next line in buffer + + loop @@loop1m + + mov cx,[es:pcharheight] + +EVEN +@@loop1: + SHIFTNOXOR + add di,dx ; next line in buffer + loop @@loop1 + + ret + +; +; two byte character +; +mshift2wide: + dec dx + dec dx +EVEN +@@loop2m: + SHIFTWITHXOR + SHIFTWITHXOR + add di,dx ; next line in buffer + + loop @@loop2m + + mov cx,[es:pcharheight] + +EVEN +@@loop2: + SHIFTNOXOR + SHIFTNOXOR + add di,dx ; next line in buffer + loop @@loop2 + + ret + +; +; three byte character +; +mshift3wide: + sub dx,3 +EVEN +@@loop3m: + SHIFTWITHXOR + SHIFTWITHXOR + SHIFTWITHXOR + add di,dx ; next line in buffer + + loop @@loop3m + + mov cx,[es:pcharheight] + +EVEN +@@loop3: + SHIFTNOXOR + SHIFTNOXOR + SHIFTNOXOR + add di,dx ; next line in buffer + loop @@loop3 + + ret + + +ENDP + +;============================================================================ + +;================== +; +; VW_DrawMPropString +; +; Draws a C string of characters at px/py and advances px +; +;================== + + + +PROC VW_DrawMPropString string:DWORD +PUBLIC VW_DrawMPropString +USES SI,DI + +; +; clear out the first byte of the buffer, the rest will automatically be +; cleared as characters are drawn into it +; + mov es,[grsegs+STARTFONTM*2] + mov dx,[es:pcharheight] + mov di,OFFSET databuffer + mov ax,ds + mov es,ax + mov bx,BUFFWIDTH-1 + + mov cx,dx + mov al,0ffh +@@maskfill: + stosb ; fill the mask part with $ff + add di,bx + loop @@maskfill + + mov cx,dx + xor al,al +@@datafill: + stosb ; fill the data part with $0 + add di,bx + loop @@datafill + +; +; shift the characters into the buffer +; + mov ax,[px] + and ax,7 + mov [bufferbit],ax + mov [bufferbyte],0 + + mov ax,[WORD string] + mov [stringptr],ax + mov ax,[WORD string+2] + mov [stringptr+2],ax + +@@shiftone: + mov es,[stringptr+2] + mov bx,[stringptr] + inc [stringptr] + mov bx,[es:bx] + xor bh,bh + or bl,bl + jz @@allshifted + call ShiftMPropChar + jmp @@shiftone + +@@allshifted: +; +; calculate position to draw buffer on screen +; + mov bx,[py] + shl bx,1 + mov di,[ylookup+bx] + add di,[bufferofs] + + mov ax,[px] + shr ax,1 + shr ax,1 + shr ax,1 ;x location in bytes + add di,ax + mov [screenspot],di + +; +; advance px +; + mov ax,[bufferbyte] + shl ax,1 + shl ax,1 + shl ax,1 + or ax,[bufferbit] + add [px],ax + +; +; draw it +; + mov ax,[bufferbyte] + test [bufferbit],7 + jz @@go + inc ax ;so the partial byte also gets drawn +@@go: + mov [bufferwidth],ax + mov es,[grsegs+STARTFONTM*2] + mov ax,[es:pcharheight] + mov [bufferheight],ax + + mov si,OFFSET databuffer + call BufferToScreen ; cut out mask + ; or in data + call BufferToScreen ; SI is still in the right position in buffer + + ret + +ENDP + +endif ; if numfontm + +endif ; if fonts diff --git a/16/cawat/ID_VW_AE.ASM b/16/cawat/ID_VW_AE.ASM new file mode 100644 index 00000000..e26c50b7 --- /dev/null +++ b/16/cawat/ID_VW_AE.ASM @@ -0,0 +1,1900 @@ +; Catacomb Armageddon Source Code +; Copyright (C) 1993-2014 Flat Rock Software +; +; This program is free software; you can redistribute it and/or modify +; it under the terms of the GNU General Public License as published by +; the Free Software Foundation; either version 2 of the License, or +; (at your option) any later version. +; +; This program is distributed in the hope that it will be useful, +; but WITHOUT ANY WARRANTY; without even the implied warranty of +; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +; GNU General Public License for more details. +; +; You should have received a copy of the GNU General Public License along +; with this program; if not, write to the Free Software Foundation, Inc., +; 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +;================================= +; +; EGA view manager routines +; +;================================= + +;============================================================================ +; +; All EGA drawing routines that write out words need to have alternate forms +; for starting on even and odd addresses, because writing a word at segment +; offset 0xffff causes an exception! To work around this, write a single +; byte out to make the address even, so it wraps cleanly at the end. +; +; All of these routines assume read/write mode 0, and will allways return +; in that state. +; The direction flag should be clear +; readmap/writemask is left in an undefined state +; +;============================================================================ + + +;============================================================================ +; +; VW_Plot (int x,y,color) +; +;============================================================================ + +DATASEG + +plotpixels db 128,64,32,16,8,4,2,1 + +CODESEG + +PROC VW_Plot x:WORD, y:WORD, color:WORD +PUBLIC VW_Plot +USES SI,DI + + mov es,[screenseg] + + mov dx,SC_INDEX + mov ax,SC_MAPMASK+15*256 + WORDOUT + + mov dx,GC_INDEX + mov ax,GC_MODE+2*256 ;write mode 2 + WORDOUT + + mov di,[bufferofs] + mov bx,[y] + shl bx,1 + add di,[ylookup+bx] + mov bx,[x] + mov ax,bx + shr ax,1 + shr ax,1 + shr ax,1 + add di,ax ; di = byte on screen + + and bx,7 + mov ah,[plotpixels+bx] + mov al,GC_BITMASK ;mask off other pixels + WORDOUT + + mov bl,[BYTE color] + xchg bl,[es:di] ; load latches and write pixel + + mov dx,GC_INDEX + mov ah,0ffh ;no bit mask + WORDOUT + mov ax,GC_MODE+0*256 ;write mode 0 + WORDOUT + + ret + +ENDP + + +;============================================================================ +; +; VW_Vlin (int yl,yh,x,color) +; +;============================================================================ + +PROC VW_Vlin yl:WORD, yh:WORD, x:WORD, color:WORD +PUBLIC VW_Vlin +USES SI,DI + + mov es,[screenseg] + + mov dx,SC_INDEX + mov ax,SC_MAPMASK+15*256 + WORDOUT + + mov dx,GC_INDEX + mov ax,GC_MODE+2*256 ;write mode 2 + WORDOUT + + mov di,[bufferofs] + mov bx,[yl] + shl bx,1 + add di,[ylookup+bx] + mov bx,[x] + mov ax,bx + shr ax,1 + shr ax,1 + shr ax,1 + add di,ax ; di = byte on screen + + and bx,7 + mov ah,[plotpixels+bx] + mov al,GC_BITMASK ;mask off other pixels + WORDOUT + + mov cx,[yh] + sub cx,[yl] + inc cx ;number of pixels to plot + + mov bh,[BYTE color] + mov dx,[linewidth] + +@@plot: + mov bl,bh + xchg bl,[es:di] ; load latches and write pixel + add di,dx + + loop @@plot + + mov dx,GC_INDEX + mov ah,0ffh ;no bit mask + WORDOUT + mov ax,GC_MODE+0*256 ;write mode 0 + WORDOUT + + ret + +ENDP + + +;============================================================================ + + +;=================== +; +; VW_DrawTile8 +; +; xcoord in bytes (8 pixels), ycoord in pixels +; All Tile8s are in one grseg, so an offset is calculated inside it +; +;=================== + +PROC VW_DrawTile8 xcoord:WORD, ycoord:WORD, tile:WORD +PUBLIC VW_DrawTile8 +USES SI,DI + + mov es,[screenseg] + + mov di,[bufferofs] + add di,[xcoord] + mov bx,[ycoord] + shl bx,1 + add di,[ylookup+bx] + mov [ss:screendest],di ;screen destination + + mov bx,[linewidth] + dec bx + + mov si,[tile] + shl si,1 + shl si,1 + shl si,1 + shl si,1 + shl si,1 + + mov ds,[grsegs+STARTTILE8*2] ; segment for all tile8s + + mov cx,4 ;planes to draw + mov ah,0001b ;map mask + + mov dx,SC_INDEX + mov al,SC_MAPMASK + +; +; start drawing +; + +@@planeloop: + WORDOUT + shl ah,1 ;shift plane mask over for next plane + + mov di,[ss:screendest] ;start at same place in all planes + +REPT 7 + movsb + add di,bx +ENDM + movsb + + loop @@planeloop + + mov ax,ss + mov ds,ax ;restore turbo's data segment + + ret + +ENDP + + +;============================================================================ +; +; VW_MaskBlock +; +; Draws a masked block shape to the screen. bufferofs is NOT accounted for. +; The mask comes first, then four planes of data. +; +;============================================================================ + +DATASEG + +UNWOUNDMASKS = 10 + + +maskroutines dw mask0,mask0,mask1E,mask1E,mask2E,mask2O,mask3E,mask3O + dw mask4E,mask4O,mask5E,mask5O,mask6E,mask6O + dw mask7E,mask7O,mask8E,mask8O,mask9E,mask9O + dw mask10E,mask10O + + +routinetouse dw ? + +CODESEG + +PROC VW_MaskBlock segm:WORD, ofs:WORD, dest:WORD, wide:WORD, height:WORD, planesize:WORD +PUBLIC VW_MaskBlock +USES SI,DI + + mov es,[screenseg] + + mov [BYTE planemask],1 + mov [BYTE planenum],0 + + mov di,[wide] + mov dx,[linewidth] + sub dx,[wide] + mov [linedelta],dx ;amount to add after drawing each line + + mov bx,[planesize] ; si+bx = data location + + cmp di,UNWOUNDMASKS + jbe @@unwoundroutine + mov [routinetouse],OFFSET generalmask + jmp NEAR @@startloop + +;================= +; +; use the unwound routines +; +;================= + +@@unwoundroutine: + mov cx,[dest] + shr cx,1 + rcl di,1 ;shift a 1 in if destination is odd + shl di,1 ;to index into a word width table + mov ax,[maskroutines+di] ;call the right routine + mov [routinetouse],ax + +@@startloop: + mov ds,[segm] + +@@drawplane: + mov dx,SC_INDEX + mov al,SC_MAPMASK + mov ah,[ss:planemask] + WORDOUT + mov dx,GC_INDEX + mov al,GC_READMAP + mov ah,[ss:planenum] + WORDOUT + + mov si,[ofs] ;start back at the top of the mask + mov di,[dest] ;start at same place in all planes + mov cx,[height] ;scan lines to draw + mov dx,[ss:linedelta] + + jmp [ss:routinetouse] ;draw one plane +planereturn: ;routine jmps back here + + add bx,[ss:planesize] ;start of mask = start of next plane + + inc [ss:planenum] + shl [ss:planemask],1 ;shift plane mask over for next plane + cmp [ss:planemask],10000b ;done all four planes? + jne @@drawplane + +mask0: + mov ax,ss + mov ds,ax + ret ;width of 0 = no drawing + +;============== +; +; General purpose masked block drawing. This could be optimised into +; four routines to use words, but few play loop sprites should be this big! +; +;============== + +generalmask: + mov dx,cx + +@@lineloopgen: + mov cx,[wide] +@@byteloop: + mov al,[es:di] + and al,[si] + or al,[bx+si] + inc si + stosb + loop @@byteloop + + add di,[ss:linedelta] + dec dx + jnz @@lineloopgen + jmp planereturn + +;================= +; +; Horizontally unwound routines to draw certain masked blocks faster +; +;================= + +MACRO MASKBYTE + mov al,[es:di] + and al,[si] + or al,[bx+si] + inc si + stosb +ENDM + +MACRO MASKWORD + mov ax,[es:di] + and ax,[si] + or ax,[bx+si] + inc si + inc si + stosw +ENDM + +MACRO SPRITELOOP addr + add di,dx + loop addr + jmp planereturn +ENDM + + +EVEN +mask1E: + MASKBYTE + SPRITELOOP mask1E + +EVEN +mask2E: + MASKWORD + SPRITELOOP mask2E + +EVEN +mask2O: + MASKBYTE + MASKBYTE + SPRITELOOP mask2O + +EVEN +mask3E: + MASKWORD + MASKBYTE + SPRITELOOP mask3E + +EVEN +mask3O: + MASKBYTE + MASKWORD + SPRITELOOP mask3O + +EVEN +mask4E: + MASKWORD + MASKWORD + SPRITELOOP mask4E + +EVEN +mask4O: + MASKBYTE + MASKWORD + MASKBYTE + SPRITELOOP mask4O + +EVEN +mask5E: + MASKWORD + MASKWORD + MASKBYTE + SPRITELOOP mask5E + +EVEN +mask5O: + MASKBYTE + MASKWORD + MASKWORD + SPRITELOOP mask5O + +EVEN +mask6E: + MASKWORD + MASKWORD + MASKWORD + SPRITELOOP mask6E + +EVEN +mask6O: + MASKBYTE + MASKWORD + MASKWORD + MASKBYTE + SPRITELOOP mask6O + +EVEN +mask7E: + MASKWORD + MASKWORD + MASKWORD + MASKBYTE + SPRITELOOP mask7E + +EVEN +mask7O: + MASKBYTE + MASKWORD + MASKWORD + MASKWORD + SPRITELOOP mask7O + +EVEN +mask8E: + MASKWORD + MASKWORD + MASKWORD + MASKWORD + SPRITELOOP mask8E + +EVEN +mask8O: + MASKBYTE + MASKWORD + MASKWORD + MASKWORD + MASKBYTE + SPRITELOOP mask8O + +EVEN +mask9E: + MASKWORD + MASKWORD + MASKWORD + MASKWORD + MASKBYTE + SPRITELOOP mask9E + +EVEN +mask9O: + MASKBYTE + MASKWORD + MASKWORD + MASKWORD + MASKWORD + SPRITELOOP mask9O + +EVEN +mask10E: + MASKWORD + MASKWORD + MASKWORD + MASKWORD + MASKWORD + SPRITELOOP mask10E + +EVEN +mask10O: + MASKBYTE + MASKWORD + MASKWORD + MASKWORD + MASKWORD + MASKBYTE + SPRITELOOP mask10O + + +ENDP + + +;============================================================================ +; +; VW_ScreenToScreen +; +; Basic block copy routine. Copies one block of screen memory to another, +; using write mode 1 (sets it and returns with write mode 0). bufferofs is +; NOT accounted for. +; +;============================================================================ + +PROC VW_ScreenToScreen source:WORD, dest:WORD, wide:WORD, height:WORD +PUBLIC VW_ScreenToScreen +USES SI,DI + + pushf + cli + + mov dx,SC_INDEX + mov ax,SC_MAPMASK+15*256 + WORDOUT + mov dx,GC_INDEX + mov ax,GC_MODE+1*256 + WORDOUT + + popf + + mov bx,[linewidth] + sub bx,[wide] + + mov ax,[screenseg] + mov es,ax + mov ds,ax + + mov si,[source] + mov di,[dest] ;start at same place in all planes + mov dx,[height] ;scan lines to draw + mov ax,[wide] + +@@lineloop: + mov cx,ax + rep movsb + add si,bx + add di,bx + + dec dx + jnz @@lineloop + + mov dx,GC_INDEX + mov ax,GC_MODE+0*256 + WORDOUT + + mov ax,ss + mov ds,ax ;restore turbo's data segment + + ret + +ENDP + + +;============================================================================ +; +; VW_MemToScreen +; +; Basic block drawing routine. Takes a block shape at segment pointer source +; with four planes of width by height data, and draws it to dest in the +; virtual screen, based on linewidth. bufferofs is NOT accounted for. +; There are four drawing routines to provide the best optimized code while +; accounting for odd segment wrappings due to the floating screens. +; +;============================================================================ + +DATASEG + +memtoscreentable dw eventoeven,eventoodd,oddtoeven,oddtoodd + +CODESEG + + +PROC VW_MemToScreen source:WORD, dest:WORD, wide:WORD, height:WORD +PUBLIC VW_MemToScreen +USES SI,DI + + mov es,[screenseg] + + mov bx,[linewidth] + sub bx,[wide] + + mov ds,[source] + + + xor si,si ;block is segment aligned + + xor di,di + shr [wide],1 ;change wide to words, and see if carry is set + rcl di,1 ;1 if wide is odd + mov ax,[dest] + shr ax,1 + rcl di,1 ;shift a 1 in if destination is odd + shl di,1 ;to index into a word width table + mov ax,SC_MAPMASK+0001b*256 ;map mask for plane 0 + jmp [ss:memtoscreentable+di] ;call the right routine + +;============== +; +; Copy an even width block to an even video address +; +;============== + +eventoeven: + mov dx,SC_INDEX + WORDOUT + + mov di,[dest] ;start at same place in all planes + mov dx,[height] ;scan lines to draw + +@@lineloopEE: + mov cx,[wide] + rep movsw + + add di,bx + + dec dx + jnz @@lineloopEE + + shl ah,1 ;shift plane mask over for next plane + cmp ah,10000b ;done all four planes? + jne eventoeven + + mov ax,ss + mov ds,ax ;restore turbo's data segment + + ret + +;============== +; +; Copy an odd width block to an even video address +; +;============== + +oddtoeven: + mov dx,SC_INDEX + WORDOUT + + mov di,[dest] ;start at same place in all planes + mov dx,[height] ;scan lines to draw + +@@lineloopOE: + mov cx,[wide] + rep movsw + movsb ;copy the last byte + + add di,bx + + dec dx + jnz @@lineloopOE + + shl ah,1 ;shift plane mask over for next plane + cmp ah,10000b ;done all four planes? + jne oddtoeven + + mov ax,ss + mov ds,ax ;restore turbo's data segment + + ret + +;============== +; +; Copy an even width block to an odd video address +; +;============== + +eventoodd: + dec [wide] ;one word has to be handled seperately +EOplaneloop: + mov dx,SC_INDEX + WORDOUT + + mov di,[dest] ;start at same place in all planes + mov dx,[height] ;scan lines to draw + +@@lineloopEO: + movsb + mov cx,[wide] + rep movsw + movsb + + add di,bx + + dec dx + jnz @@lineloopEO + + shl ah,1 ;shift plane mask over for next plane + cmp ah,10000b ;done all four planes? + jne EOplaneloop + + mov ax,ss + mov ds,ax ;restore turbo's data segment + + ret + +;============== +; +; Copy an odd width block to an odd video address +; +;============== + +oddtoodd: + mov dx,SC_INDEX + WORDOUT + + mov di,[dest] ;start at same place in all planes + mov dx,[height] ;scan lines to draw + +@@lineloopOO: + movsb + mov cx,[wide] + rep movsw + + add di,bx + + dec dx + jnz @@lineloopOO + + shl ah,1 ;shift plane mask over for next plane + cmp ah,10000b ;done all four planes? + jne oddtoodd + + mov ax,ss + mov ds,ax ;restore turbo's data segment + + ret + + +ENDP + +; MDM (GAMERS EDGE) begin + + +MACRO XPAND_BYTE + test al,128 ; handle bit 7 + jne @@over7 + or [BYTE PTR es:di],11000000b +@@over7: + + test al,64 ; handle bit 6 + jne @@over6 + or [BYTE PTR es:di],00110000b +@@over6: + + test al,32 ; handle bit 5 + jne @@over5 + or [BYTE PTR es:di],00001100b +@@over5: + + test al,16 ; handle bit 4 + jne @@over4 + or [BYTE PTR es:di],00000011b +@@over4: + + inc di ; inc destination + + test al,8 ; handle bit 3 + jne @@over3 + or [BYTE PTR es:di],11000000b +@@over3: + + test al,4 ; handle bit 2 + jne @@over2 + or [BYTE PTR es:di],00110000b +@@over2: + + test al,2 ; handle bit 1 + jne @@over1 + or [BYTE PTR es:di],00001100b +@@over1: + + test al,1 ; handle bit 0 + jne @@over0 + or [BYTE PTR es:di],00000011b +@@over0: + + inc si ; inc source + inc di ; inc destination +ENDM + + +;============================================================================ +; +; VW_MemToScreen2x +; +; Basic block drawing routine. Takes a block shape at segment pointer source +; with four planes of width by height data, and draws it to dest in the +; virtual screen, based on linewidth. bufferofs is NOT accounted for. +; There are four drawing routines to provide the best optimized code while +; accounting for odd segment wrappings due to the floating screens. +; +;============================================================================ + +DATASEG + +xpandhorz db 00000000b,00000011b,00001100b,00001111b + db 00110000b,00110011b,00111100b,00111111b + db 11000000b,11000011b,11001100b,11001111b + db 11110000b,11110011b,11111100b,11111111b + +CODESEG + + +PROC VW_MemToScreen2x source:WORD, dest:WORD, wide:WORD, height:WORD +PUBLIC VW_MemToScreen2x +USES SI,DI + + mov es,[screenseg] + + mov bx,[linewidth] + sub bx,[wide] + sub bx,[wide] + + mov ds,[source] + + + xor si,si ;block is segment aligned + + mov ah,0001b ;map mask for plane 0 + +@@depthloop: + mov al,SC_MAPMASK ;restore map mask in al + mov dx,SC_INDEX + WORDOUT + + mov di,[dest] ;start at same place in all planes + mov dx,[height] ;scan lines to draw + +@@heightloop: + mov cx,[wide] +@@widthloop: + +; handle first nybble +; + push di + mov di,[si] + shr di,1 + shr di,1 + shr di,1 + shr di,1 + and di,15 + mov al,[ss:xpandhorz+di] + pop di + mov [es:di],al + inc di + +; handle second nybble +; + push di + mov di,[si] + and di,15 + mov al,[ss:xpandhorz+di] + pop di + mov [es:di],al + inc si + inc di + + + dec cx + jne @@widthloop + + add di,bx + + dec dx + jnz @@heightloop + + shl ah,1 ;shift plane mask over for next plane + cmp ah,10000b ;done all four planes? + jne @@depthloop + + mov ax,ss + mov ds,ax ;restore turbo's data segment + + ret + +ENDP +; MDM (GAMERS EDGE) end + +;=========================================================================== +; +; VW_ScreenToMem +; +; Copies a block of video memory to main memory, in order from planes 0-3. +; This could be optimized along the lines of VW_MemToScreen to take advantage +; of word copies, but this is an infrequently called routine. +; +;=========================================================================== + +PROC VW_ScreenToMem source:WORD, dest:WORD, wide:WORD, height:WORD +PUBLIC VW_ScreenToMem +USES SI,DI + + mov es,[dest] + + mov bx,[linewidth] + sub bx,[wide] + + mov ds,[screenseg] + + mov ax,GC_READMAP ;read map for plane 0 + + xor di,di + +@@planeloop: + mov dx,GC_INDEX + WORDOUT + + mov si,[source] ;start at same place in all planes + mov dx,[height] ;scan lines to draw + +@@lineloop: + mov cx,[wide] + rep movsb + + add si,bx + + dec dx + jnz @@lineloop + + inc ah + cmp ah,4 ;done all four planes? + jne @@planeloop + + mov ax,ss + mov ds,ax ;restore turbo's data segment + + ret + +ENDP + + +;============================================================================ +; +; VWL_UpdateScreenBlocks +; +; Scans through the update matrix and copies any areas that have changed +; to the visable screen, then zeros the update array +; +;============================================================================ + + + +; AX 0/1 for scasb, temp for segment register transfers +; BX width for block copies +; CX REP counter +; DX line width deltas +; SI source for copies +; DI scas dest / movsb dest +; BP pointer to end of bufferblocks + +PROC VWL_UpdateScreenBlocks +PUBLIC VWL_UpdateScreenBlocks +USES SI,DI,BP + + jmp SHORT @@realstart +@@done: +; +; all tiles have been scanned +; + mov dx,GC_INDEX ; restore write mode 0 + mov ax,GC_MODE+0*256 + WORDOUT + + xor ax,ax ; clear out the update matrix + mov cx,UPDATEWIDE*UPDATEHIGH/2 + + mov di,[updateptr] + rep stosw + + ret + +@@realstart: + mov dx,SC_INDEX + mov ax,SC_MAPMASK+15*256 + WORDOUT + mov dx,GC_INDEX + mov ax,GC_MODE+1*256 + WORDOUT + + mov di,[updateptr] ; start of floating update screen + mov bp,di + add bp,UPDATEWIDE*UPDATEHIGH+1 ; when di = bp, all tiles have been scanned + + push di + mov cx,-1 ; definately scan the entire thing + +; +; scan for a 1 in the update list, meaning a tile needs to be copied +; from the master screen to the current screen +; +@@findtile: + pop di ; place to continue scaning from + mov ax,ss + mov es,ax ; search in the data segment + mov ds,ax + mov al,1 + repne scasb + cmp di,bp + jae @@done + + cmp [BYTE di],al + jne @@singletile + jmp @@tileblock + +;============ +; +; copy a single tile +; +;============ +@@singletile: + inc di ; we know the next tile is nothing + push di ; save off the spot being scanned + sub di,[updateptr] + shl di,1 + mov di,[blockstarts-4+di] ; start of tile location on screen + mov si,di + add si,[bufferofs] + add di,[displayofs] + + mov dx,[linewidth] + sub dx,2 + mov ax,[screenseg] + mov ds,ax + mov es,ax + +REPT 15 + movsb + movsb + add si,dx + add di,dx +ENDM + movsb + movsb + + jmp @@findtile + +;============ +; +; more than one tile in a row needs to be updated, so do it as a group +; +;============ +EVEN +@@tileblock: + mov dx,di ; hold starting position + 1 in dx + inc di ; we know the next tile also gets updated + repe scasb ; see how many more in a row + push di ; save off the spot being scanned + + mov bx,di + sub bx,dx ; number of tiles in a row + shl bx,1 ; number of bytes / row + + mov di,dx ; lookup position of start tile + sub di,[updateptr] + shl di,1 + mov di,[blockstarts-2+di] ; start of tile location + mov si,di + add si,[bufferofs] + add di,[displayofs] + + mov dx,[linewidth] + sub dx,bx ; offset to next line on screen + + mov ax,[screenseg] + mov ds,ax + mov es,ax + +REPT 15 + mov cx,bx + rep movsb + add si,dx + add di,dx +ENDM + mov cx,bx + rep movsb + + dec cx ; was 0 from last rep movsb, now $ffff for scasb + jmp @@findtile + +ENDP + + +;=========================================================================== +; +; MISC EGA ROUTINES +; +;=========================================================================== + +;============== +; +; VW_SetScreen +; +;============== + +PROC VW_SetScreen crtc:WORD, pel:WORD +PUBLIC VW_SetScreen + +if waitforvbl + + mov dx,STATUS_REGISTER_1 + +; +; wait util the CRTC just starts scaning a diplayed line to set the CRTC start +; + cli + +@@waitnodisplay: + in al,dx + test al,1 + jz @@waitnodisplay + +; the display is now disabled (in a HBL / VBL) + +@@waitdisplay: + in al,dx + test al,1 ;1 = display is disabled (HBL / VBL) + jnz @@waitdisplay + +; the display was just enabled, so a full scan line is available for CRTC set + +endif + +; +; set CRTC start +; +; for some reason, my XT's EGA card doesn't like word outs to the CRTC +; index... +; + mov cx,[crtc] + mov dx,CRTC_INDEX + mov al,0ch ;start address high register + out dx,al + inc dx + mov al,ch + out dx,al + dec dx + mov al,0dh ;start address low register + out dx,al + mov al,cl + inc dx + out dx,al + +if waitforvbl + +; +; wait for a vertical retrace to set pel panning +; + mov dx,STATUS_REGISTER_1 +@@waitvbl: + sti ;service interrupts + jmp $+2 + cli + in al,dx + test al,00001000b ;look for vertical retrace + jz @@waitvbl + +endif + +; +; set horizontal panning +; + + mov dx,ATR_INDEX + mov al,ATR_PELPAN or 20h + out dx,al + jmp $+2 + mov al,[BYTE pel] ;pel pan value + out dx,al + + sti + + ret + +ENDP + + +if NUMFONT+NUMFONTM + +;=========================================================================== +; +; GENERAL FONT DRAWING ROUTINES +; +;=========================================================================== + +DATASEG + +px dw ? ; proportional character drawing coordinates +py dw ? +pdrawmode db 11000b ; 8 = OR, 24 = XOR, put in GC_DATAROTATE +fontcolor db 15 ;0-15 mapmask value + +PUBLIC px,py,pdrawmode,fontcolor + +; +; offsets in font structure +; +pcharheight = 0 ;lines high +charloc = 2 ;pointers to every character +charwidth = 514 ;every character's width in pixels + + +propchar dw ? ; the character number to shift +stringptr dw ?,? + + +BUFFWIDTH = 82 ; MDM (GAMERS EDGE) - increased from 50 +BUFFHEIGHT = 20 ; must be twice as high as font for masked fonts + +databuffer db BUFFWIDTH*BUFFHEIGHT dup (?) + +bufferwidth dw ? ; bytes with valid info / line +bufferheight dw ? ; number of lines currently used + +bufferbyte dw ? +bufferbit dw ? + +screenspot dw ? ; where the buffer is going + +bufferextra dw ? ; add at end of a line copy +screenextra dw ? + +PUBLIC bufferwidth,bufferheight,screenspot + +CODESEG + +;====================== +; +; Macros to table shift a byte of font +; +;====================== + +MACRO SHIFTNOXOR + mov al,[es:bx] ; source + xor ah,ah + shl ax,1 + mov si,ax + mov ax,[bp+si] ; table shift into two bytes + or [di],al ; or with first byte + inc di + mov [di],ah ; replace next byte + inc bx ; next source byte +ENDM + +MACRO SHIFTWITHXOR + mov al,[es:bx] ; source + xor ah,ah + shl ax,1 + mov si,ax + mov ax,[bp+si] ; table shift into two bytes + not ax + and [di],al ; and with first byte + inc di + mov [di],ah ; replace next byte + inc bx ; next source byte +ENDM + + +;======================= +; +; BufferToScreen +; +; Pass buffer start in SI (somewhere in databuffer) +; Draws the buffer to the EGA screen in the current write mode +; +;======================== + +PROC BufferToScreen NEAR + + mov es,[screenseg] + mov di,[screenspot] + + mov bx,[bufferwidth] ;calculate offsets for end of each line + or bx,bx + jnz @@isthere + ret ;nothing to draw + +@@isthere: + mov ax,[linewidth] + sub ax,bx + mov [screenextra],ax + mov ax,BUFFWIDTH + sub ax,bx + mov [bufferextra],ax + + mov bx,[bufferheight] ;lines to copy +@@lineloop: + mov cx,[bufferwidth] ;bytes to copy +@@byteloop: + lodsb ;get a byte from the buffer + xchg [es:di],al ;load latches and store back to screen + inc di + + loop @@byteloop + + add si,[bufferextra] + add di,[screenextra] + + dec bx + jnz @@lineloop + + ret +ENDP + + +;============================================================================ +; +; NON MASKED FONT DRAWING ROUTINES +; +;============================================================================ + +if numfont + +DATASEG + +shiftdrawtable dw 0,shift1wide,shift2wide,shift3wide,shift4wide + dw shift5wide + +CODESEG + +;================== +; +; ShiftPropChar +; +; Call with BX = character number (0-255) +; Draws one character to the buffer at bufferbyte/bufferbit, and adjusts +; them to the new position +; +;================== + +PROC ShiftPropChar NEAR + + mov si,[fontnumber] + shl si,1 + mov es,[grsegs+STARTFONT*2+si] ;segment of font to use + +; +; find character location, width, and height +; + mov si,[es:charwidth+bx] + and si,0ffh ;SI hold width in pixels + shl bx,1 + mov bx,[es:charloc+bx] ;BX holds pointer to character data + +; +; look up which shift table to use, based on bufferbit +; + mov di,[bufferbit] + shl di,1 + mov bp,[shifttabletable+di] ;BP holds pointer to shift table + + mov di,OFFSET databuffer + add di,[bufferbyte] ;DI holds pointer to buffer + +; +; advance position by character width +; + mov cx,[bufferbit] + add cx,si ;new bit position + mov ax,cx + and ax,7 + mov [bufferbit],ax ;new bit position + mov ax,cx + shr ax,1 + shr ax,1 + shr ax,1 + add [bufferbyte],ax ;new byte position + + add si,7 + shr si,1 + shr si,1 + shr si,1 ;bytes the character is wide + shl si,1 ;*2 to look up in shiftdrawtable + + mov cx,[es:pcharheight] + mov dx,BUFFWIDTH + jmp [ss:shiftdrawtable+si] ;procedure to draw this width + +; +; one byte character +; +shift1wide: + dec dx +EVEN +@@loop1: + SHIFTNOXOR + add di,dx ; next line in buffer + loop @@loop1 + ret + +; +; two byte character +; +shift2wide: + dec dx + dec dx +EVEN +@@loop2: + SHIFTNOXOR + SHIFTNOXOR + add di,dx ; next line in buffer + loop @@loop2 + ret + +; +; three byte character +; +shift3wide: + sub dx,3 +EVEN +@@loop3: + SHIFTNOXOR + SHIFTNOXOR + SHIFTNOXOR + add di,dx ; next line in buffer + loop @@loop3 + ret + +; +; four byte character +; +shift4wide: + sub dx,4 +EVEN +@@loop4: + SHIFTNOXOR + SHIFTNOXOR + SHIFTNOXOR + SHIFTNOXOR + add di,dx ; next line in buffer + loop @@loop4 + ret + +; +; five byte character +; +shift5wide: + sub dx,5 +EVEN +@@loop5: + SHIFTNOXOR + SHIFTNOXOR + SHIFTNOXOR + SHIFTNOXOR + add di,dx ; next line in buffer + loop @@loop5 + ret + + + +ENDP + +;============================================================================ + +;================== +; +; VW_DrawPropString +; +; Draws a C string of characters at px/py and advances px +; +; Assumes write mode 0 +; +;================== + +CODESEG + +PROC VW_DrawPropString string:DWORD +PUBLIC VW_DrawPropString +USES SI,DI + +; +; proportional spaceing, which clears the buffer ahead of it, so only +; clear the first collumn +; + mov al,0 +line = 0 +REPT BUFFHEIGHT + mov [BYTE databuffer+BUFFWIDTH*line],al +line = line+1 +ENDM + +; +; shift the characters into the buffer +; +@@shiftchars: + mov ax,[px] + and ax,7 + mov [bufferbit],ax + mov [bufferbyte],0 + + mov ax,[WORD string] + mov [stringptr],ax + mov ax,[WORD string+2] + mov [stringptr+2],ax + +@@shiftone: + mov es,[stringptr+2] + mov bx,[stringptr] + inc [stringptr] + mov bx,[es:bx] + xor bh,bh + or bl,bl + jz @@allshifted + call ShiftPropChar + jmp @@shiftone + +@@allshifted: +; +; calculate position to draw buffer on screen +; + mov bx,[py] + shl bx,1 + mov di,[ylookup+bx] + add di,[bufferofs] + add di,[panadjust] + + mov ax,[px] + shr ax,1 + shr ax,1 + shr ax,1 ;x location in bytes + add di,ax + mov [screenspot],di + +; +; advance px +; + mov ax,[bufferbyte] + shl ax,1 + shl ax,1 + shl ax,1 + or ax,[bufferbit] + add [px],ax + +; +; draw it +; + +; set xor/or mode + mov dx,GC_INDEX + mov al,GC_DATAROTATE + mov ah,[pdrawmode] + WORDOUT + +; set mapmask to color + mov dx,SC_INDEX + mov al,SC_MAPMASK + mov ah,[fontcolor] + WORDOUT + + mov ax,[bufferbyte] + test [bufferbit],7 + jz @@go + inc ax ;so the partial byte also gets drawn +@@go: + mov [bufferwidth],ax + mov si,[fontnumber] + shl si,1 + mov es,[grsegs+STARTFONT*2+si] + mov ax,[es:pcharheight] + mov [bufferheight],ax + + mov si,OFFSET databuffer + call BufferToScreen + +; set copy mode + mov dx,GC_INDEX + mov ax,GC_DATAROTATE + WORDOUT + +; set mapmask to all + mov dx,SC_INDEX + mov ax,SC_MAPMASK + 15*256 + WORDOUT + + + ret + +ENDP + +endif ;numfont + +;============================================================================ +; +; MASKED FONT DRAWING ROUTINES +; +;============================================================================ + +if numfontm + +DATASEG + +mshiftdrawtable dw 0,mshift1wide,mshift2wide,mshift3wide + + +CODESEG + +;================== +; +; ShiftMPropChar +; +; Call with BX = character number (0-255) +; Draws one character to the buffer at bufferbyte/bufferbit, and adjusts +; them to the new position +; +;================== + +PROC ShiftMPropChar NEAR + + mov si,[fontnumber] + shl si,1 + mov es,[grsegs+STARTFONTM*2+si] ;segment of font to use + +; +; find character location, width, and height +; + mov si,[es:charwidth+bx] + and si,0ffh ;SI hold width in pixels + shl bx,1 + mov bx,[es:charloc+bx] ;BX holds pointer to character data + +; +; look up which shift table to use, based on bufferbit +; + mov di,[bufferbit] + shl di,1 + mov bp,[shifttabletable+di] ;BP holds pointer to shift table + + mov di,OFFSET databuffer + add di,[bufferbyte] ;DI holds pointer to buffer + + mov cx,[bufferbit] + add cx,si ;new bit position + mov ax,cx + and ax,7 + mov [bufferbit],ax ;new bit position + mov ax,cx + shr ax,1 + shr ax,1 + shr ax,1 + add [bufferbyte],ax ;new byte position + + add si,7 + shr si,1 + shr si,1 + shr si,1 ;bytes the character is wide + shl si,1 ;*2 to look up in shiftdrawtable + + mov cx,[es:pcharheight] + mov dx,BUFFWIDTH + jmp [ss:mshiftdrawtable+si] ;procedure to draw this width + +; +; one byte character +; +mshift1wide: + dec dx + +EVEN +@@loop1m: + SHIFTWITHXOR + add di,dx ; next line in buffer + + loop @@loop1m + + mov cx,[es:pcharheight] + +EVEN +@@loop1: + SHIFTNOXOR + add di,dx ; next line in buffer + loop @@loop1 + + ret + +; +; two byte character +; +mshift2wide: + dec dx + dec dx +EVEN +@@loop2m: + SHIFTWITHXOR + SHIFTWITHXOR + add di,dx ; next line in buffer + + loop @@loop2m + + mov cx,[es:pcharheight] + +EVEN +@@loop2: + SHIFTNOXOR + SHIFTNOXOR + add di,dx ; next line in buffer + loop @@loop2 + + ret + +; +; three byte character +; +mshift3wide: + sub dx,3 +EVEN +@@loop3m: + SHIFTWITHXOR + SHIFTWITHXOR + SHIFTWITHXOR + add di,dx ; next line in buffer + + loop @@loop3m + + mov cx,[es:pcharheight] + +EVEN +@@loop3: + SHIFTNOXOR + SHIFTNOXOR + SHIFTNOXOR + add di,dx ; next line in buffer + loop @@loop3 + + ret + + +ENDP + +;============================================================================ + +;================== +; +; VW_DrawMPropString +; +; Draws a C string of characters at px/py and advances px +; +; Assumes write mode 0 +; +;================== + + + +PROC VW_DrawMPropString string:DWORD +PUBLIC VW_DrawMPropString +USES SI,DI + +; +; clear out the first byte of the buffer, the rest will automatically be +; cleared as characters are drawn into it +; + mov si,[fontnumber] + shl si,1 + mov es,[grsegs+STARTFONTM*2+si] + mov dx,[es:pcharheight] + mov di,OFFSET databuffer + mov ax,ds + mov es,ax + mov bx,BUFFWIDTH-1 + + mov cx,dx + mov al,0ffh +@@maskfill: + stosb ; fill the mask part with $ff + add di,bx + loop @@maskfill + + mov cx,dx + xor al,al +@@datafill: + stosb ; fill the data part with $0 + add di,bx + loop @@datafill + +; +; shift the characters into the buffer +; + mov ax,[px] + and ax,7 + mov [bufferbit],ax + mov [bufferbyte],0 + + mov ax,[WORD string] + mov [stringptr],ax + mov ax,[WORD string+2] + mov [stringptr+2],ax + +@@shiftone: + mov es,[stringptr+2] + mov bx,[stringptr] + inc [stringptr] + mov bx,[es:bx] + xor bh,bh + or bl,bl + jz @@allshifted + call ShiftMPropChar + jmp @@shiftone + +@@allshifted: +; +; calculate position to draw buffer on screen +; + mov bx,[py] + shl bx,1 + mov di,[ylookup+bx] + add di,[bufferofs] + add di,[panadjust] + + mov ax,[px] + shr ax,1 + shr ax,1 + shr ax,1 ;x location in bytes + add di,ax + mov [screenspot],di + +; +; advance px +; + mov ax,[bufferbyte] + shl ax,1 + shl ax,1 + shl ax,1 + or ax,[bufferbit] + add [px],ax + +; +; draw it +; + mov ax,[bufferbyte] + test [bufferbit],7 + jz @@go + inc ax ;so the partial byte also gets drawn +@@go: + mov [bufferwidth],ax + mov es,[grsegs+STARTFONTM*2] + mov ax,[es:pcharheight] + mov [bufferheight],ax + +; set AND mode to punch out the mask + mov dx,GC_INDEX + mov ax,GC_DATAROTATE + 8*256 + WORDOUT + +; set mapmask to all + mov dx,SC_INDEX + mov ax,SC_MAPMASK + 15*256 + WORDOUT + + mov si,OFFSET databuffer + call BufferToScreen + +; set OR mode to fill in the color + mov dx,GC_INDEX + mov ax,GC_DATAROTATE + 16*256 + WORDOUT + +; set mapmask to color + mov dx,SC_INDEX + mov al,SC_MAPMASK + mov ah,[fontcolor] + WORDOUT + + call BufferToScreen ; SI is still in the right position in buffer + +; set copy mode + mov dx,GC_INDEX + mov ax,GC_DATAROTATE + WORDOUT + +; set mapmask to all + mov dx,SC_INDEX + mov ax,SC_MAPMASK + 15*256 + WORDOUT + + + ret + +ENDP + +endif ; if numfontm + +endif ; if fonts diff --git a/16/cawat/JABHACK.ASM b/16/cawat/JABHACK.ASM new file mode 100644 index 00000000..16213675 --- /dev/null +++ b/16/cawat/JABHACK.ASM @@ -0,0 +1,115 @@ +; Catacomb Armageddon Source Code +; Copyright (C) 1993-2014 Flat Rock Software +; +; This program is free software; you can redistribute it and/or modify +; it under the terms of the GNU General Public License as published by +; the Free Software Foundation; either version 2 of the License, or +; (at your option) any later version. +; +; This program is distributed in the hope that it will be useful, +; but WITHOUT ANY WARRANTY; without even the implied warranty of +; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +; GNU General Public License for more details. +; +; You should have received a copy of the GNU General Public License along +; with this program; if not, write to the Free Software Foundation, Inc., +; 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +; JABHACK.ASM + +.386C +IDEAL +MODEL MEDIUM + +EXTRN LDIV@:far + +;============================================================================ + +DATASEG + +EXTRN _intaddr:word + +;============================================================================ + +CODESEG + +; Hacked up Juan Jimenez's code a bit to just return 386/not 386 +PROC _CheckIs386 +PUBLIC _CheckIs386 + + pushf ; Save flag registers, we use them here + xor ax,ax ; Clear AX and... + push ax ; ...push it onto the stack + popf ; Pop 0 into flag registers (all bits to 0), + pushf ; attempting to set bits 12-15 of flags to 0's + pop ax ; Recover the save flags + and ax,08000h ; If bits 12-15 of flags are set to + cmp ax,08000h ; zero then it's 8088/86 or 80188/186 + jz not386 + + mov ax,07000h ; Try to set flag bits 12-14 to 1's + push ax ; Push the test value onto the stack + popf ; Pop it into the flag register + pushf ; Push it back onto the stack + pop ax ; Pop it into AX for check + and ax,07000h ; if bits 12-14 are cleared then + jz not386 ; the chip is an 80286 + + mov ax,1 ; We now assume it's a 80386 or better + popf + retf + +not386: + xor ax,ax + popf + retf + + ENDP + + +PROC _jabhack2 +PUBLIC _jabhack2 + + jmp @@skip + +@@where: + int 060h + retf + +@@skip: + push es + + mov ax,seg LDIV@ + mov es,ax + mov ax,[WORD PTR @@where] + mov [WORD FAR es:LDIV@],ax + mov ax,[WORD PTR @@where+2] + mov [WORD FAR es:LDIV@+2],ax + + mov ax,offset @@jabdiv + mov [_intaddr],ax + mov ax,seg @@jabdiv + mov [_intaddr+2],ax + + pop es + retf + +@@jabdiv: + add sp,4 ;Nuke IRET address, but leave flags + push bp + mov bp,sp ;Save BP, and set it equal to stack + cli + + mov eax,[bp+8] + cdq + idiv [DWORD PTR bp+12] + mov edx,eax + shr edx,16 + + pop bp ;Restore BP + popf ;Restore flags (from INT) + retf 8 ;Return to original caller + + ENDP + + END diff --git a/16/cawat/JAMPAK.C b/16/cawat/JAMPAK.C new file mode 100644 index 00000000..c12e0382 --- /dev/null +++ b/16/cawat/JAMPAK.C @@ -0,0 +1,1113 @@ +/* Catacomb Armageddon Source Code + * Copyright (C) 1993-2014 Flat Rock Software + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#pragma inline + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "def.h" +#include "gelib.h" +#include "jampak.h" + + + +//========================================================================= +// +// +// LOCAL DEFINATIONS +// +// +//========================================================================= + +//#define COMPRESSION_CODE // Comment define in for COMPRESS routines + + + + + + +//========================================================================= +// +// +// LOCAL VARIABLES +// +// +//========================================================================= + + +unsigned char far LZW_ring_buffer[LZW_N + LZW_F - 1]; + + // ring buffer of size LZW_N, with extra LZW_F-1 bytes to facilitate + // string comparison + + +#ifdef COMPRESSION_CODE + +int LZW_match_pos, + LZW_match_len, + + // MAtchLength of longest match. These are set by the InsertNode() + // procedure. + + // left & right children & parents -- These constitute binary search trees. */ + + far LZW_left_child[LZW_N + 1], + far LZW_right_child[LZW_N + 257], + far LZW_parent[LZW_N + 1]; + +#endif + +memptr segptr; +BufferedIO lzwBIO; + + + + + +//========================================================================= +// +// +// COMPRESSION SUPPORT ROUTINES +// +// +//========================================================================= + + +#ifdef COMPRESSION_CODE + +//--------------------------------------------------------------------------- +// InitLZWTree() +//--------------------------------------------------------------------------- +void InitLZWTree(void) /* initialize trees */ +{ + int i; + + /* For i = 0 to LZW_N - 1, LZW_right_child[i] and LZW_left_child[i] will be the right and + left children of node i. These nodes need not be initialized. + Also, LZW_parent[i] is the parent of node i. These are initialized to + LZW_NIL (= LZW_N), which stands for 'not used.' + For i = 0 to 255, LZW_right_child[LZW_N + i + 1] is the root of the tree + for strings that begin with character i. These are initialized + to LZW_NIL. Note there are 256 trees. */ + + for (i = LZW_N + 1; i <= LZW_N + 256; i++) + LZW_right_child[i] = LZW_NIL; + + for (i = 0; i < LZW_N; i++) + LZW_parent[i] = LZW_NIL; +} + + + + + +//--------------------------------------------------------------------------- +// InsertLZWNode() +//--------------------------------------------------------------------------- +void InsertLZWNode(unsigned long r) + + /* Inserts string of length LZW_F, LZW_ring_buffer[r..r+LZW_F-1], into one of the + trees (LZW_ring_buffer[r]'th tree) and returns the longest-match position + and length via the global variables LZW_match_pos and LZW_match_len. + If LZW_match_len = LZW_F, then removes the old node in favor of the new + one, because the old one will be deleted sooner. + Note r plays double role, as tree node and position in buffer. */ +{ + int i, p, cmp; + unsigned char *key; + + cmp = 1; + key = &LZW_ring_buffer[r]; + p = LZW_N + 1 + key[0]; + LZW_right_child[r] = LZW_left_child[r] = LZW_NIL; + LZW_match_len = 0; + + for ( ; ; ) + { + if (cmp >= 0) + { + if (LZW_right_child[p] != LZW_NIL) + p = LZW_right_child[p]; + else + { + LZW_right_child[p] = r; + LZW_parent[r] = p; + return; + } + } + else + { + if (LZW_left_child[p] != LZW_NIL) + p = LZW_left_child[p]; + else + { + LZW_left_child[p] = r; + LZW_parent[r] = p; + return; + } + } + + for (i = 1; i < LZW_F; i++) + if ((cmp = key[i] - LZW_ring_buffer[p + i]) != 0) + break; + + if (i > LZW_match_len) + { + LZW_match_pos = p; + if ((LZW_match_len = i) >= LZW_F) + break; + } + } + + LZW_parent[r] = LZW_parent[p]; + LZW_left_child[r] = LZW_left_child[p]; + LZW_right_child[r] = LZW_right_child[p]; + LZW_parent[LZW_left_child[p]] = r; + LZW_parent[LZW_right_child[p]] = r; + + if (LZW_right_child[LZW_parent[p]] == p) + LZW_right_child[LZW_parent[p]] = r; + else + LZW_left_child[LZW_parent[p]] = r; + + LZW_parent[p] = LZW_NIL; /* remove p */ +} + + + +//--------------------------------------------------------------------------- +// DeleteLZWNode() +//--------------------------------------------------------------------------- +void DeleteLZWNode(unsigned long p) /* deletes node p from tree */ +{ + int q; + + if (LZW_parent[p] == LZW_NIL) + return; /* not in tree */ + + if (LZW_right_child[p] == LZW_NIL) + q = LZW_left_child[p]; + else + if (LZW_left_child[p] == LZW_NIL) + q = LZW_right_child[p]; + else + { + q = LZW_left_child[p]; + if (LZW_right_child[q] != LZW_NIL) + { + do { + + q = LZW_right_child[q]; + + } while (LZW_right_child[q] != LZW_NIL); + + LZW_right_child[LZW_parent[q]] = LZW_left_child[q]; + LZW_parent[LZW_left_child[q]] = LZW_parent[q]; + LZW_left_child[q] = LZW_left_child[p]; + LZW_parent[LZW_left_child[p]] = q; + } + + LZW_right_child[q] = LZW_right_child[p]; + LZW_parent[LZW_right_child[p]] = q; + } + + LZW_parent[q] = LZW_parent[p]; + if (LZW_right_child[LZW_parent[p]] == p) + LZW_right_child[LZW_parent[p]] = q; + else + LZW_left_child[LZW_parent[p]] = q; + + LZW_parent[p] = LZW_NIL; +} +#endif + + + + +//========================================================================= +// +// +// GENERAL FILE to FILE compression routines +// +// * Mainly for example usage of PTR/PTR (de)compression routines. +// +// +//========================================================================= + + + +////////////////////////////////////////////////////////////////////// +// +// CompressFILEtoFILE() -- Compresses one file stream to another file stream +// + +#ifdef COMPRESSION_CODE + +unsigned long CompressFILEtoFILE(FILE *infile, FILE *outfile,unsigned long DataLength) +{ + unsigned long returnval; + + fwrite(COMP,4,1,outfile); + fwrite((char *)&DataLength,4,1,outfile); + + returnval = 8+lzwCompress(infile,outfile,DataLength,(SRC_FFILE|DEST_FFILE)); + + return(returnval); +} + +#endif + +#if 0 +///////////////////////////////////////////////////////////////////////////// +// +// DecompressFILEtoFILE() +// +void DecompressFILEtoFILE(FILE *infile, FILE *outfile) +{ + unsigned char Buffer[8]; + unsigned long DataLength; + + fread(Buffer,1,4,infile); + + if (strncmp(Buffer,COMP,4)) + { + printf("\nNot a JAM Compressed File!\n"); + return; + } + + fread((void *)&DataLength,1,4,infile); + + lzwDecompress(infile,outfile,DataLength,(SRC_FFILE|DEST_FFILE)); +} +#endif + + + + + +//========================================================================== +// +// +// WRITE/READ PTR ROUTINES +// +// +//========================================================================== + + + +//--------------------------------------------------------------------------- +// WritePtr() -- Outputs data to a particular ptr type +// +// PtrType MUST be of type DEST_TYPE. +// +// NOTE : For PtrTypes DEST_MEM a ZERO (0) is always returned. +// +//--------------------------------------------------------------------------- +int WritePtr(long outfile, unsigned char data, unsigned PtrType) +{ + int returnval = 0; + + switch (PtrType & DEST_TYPES) + { + case DEST_FILE: + write(*(int far *)outfile,(char *)&data,1); + break; + + case DEST_FFILE: + returnval = putc(data, *(FILE **)outfile); + break; + + case DEST_MEM: +// *(*(char far **)outfile++) = data; // Do NOT delete + *((char far *)*(char far **)outfile)++ = data; + break; + + default: + TrashProg("WritePtr() : Unknown DEST_PTR type"); + break; + } + + return(returnval); + +} + + +//--------------------------------------------------------------------------- +// ReadPtr() -- Reads data from a particular ptr type +// +// PtrType MUST be of type SRC_TYPE. +// +// RETURNS : +// The char read in or EOF for SRC_FFILE type of reads. +// +// +//--------------------------------------------------------------------------- +int ReadPtr(long infile, unsigned PtrType) +{ + int returnval = 0; + + switch (PtrType & SRC_TYPES) + { + case SRC_FILE: + read(*(int far *)infile,(char *)&returnval,1); + break; + + case SRC_FFILE: +// JIM - JTR - is the following correct? "fgetc()" uses a near pointer. +// + returnval = fgetc((FILE far *)*(FILE far **)infile); + break; + + case SRC_BFILE: + returnval = bio_readch((BufferedIO *)*(void far **)infile); + break; + + case SRC_MEM: + returnval = (char)*(*(char far **)infile++); +// returnval = *((char far *)*(char far **)infile)++; // DO NOT DELETE! + break; + + default: + TrashProg("ReadPtr() : Unknown SRC_PTR type"); + break; + } + + return(returnval); +} + + + + +//========================================================================= +// +// +// COMPRESSION & DECOMPRESSION ROUTINES +// +// +//========================================================================= + + +//-------------------------------------------------------------------------- +// +// lzwCompress() - Compresses data from an input ptr to a dest ptr +// +// PARAMS: +// infile - Pointer at the BEGINNING of the data to compress +// outfile - Pointer to the destination (no header). +// DataLength - Number of bytes to compress. +// PtrTypes - Type of pointers being used (SRC_FILE,DEST_FILE,SRC_MEM etc). +// +// RETURNS: +// Length of compressed data. +// +// COMPTYPE : ct_LZW +// +// NOTES : Does not write ANY header information! +// +#ifdef COMPRESSION_CODE +unsigned long lzwCompress(void far *infile, void far *outfile,unsigned long DataLength,unsigned PtrTypes) +{ + short i; + short c, len, r, s, last_LZW_match_len, code_buf_ptr; + unsigned char far code_buf[17], mask; + unsigned long complen = 0; + + // initialize trees + + InitLZWTree(); + + code_buf[0] = 0; + + // + // code_buf[1..16] saves eight units of code, and code_buf[0] works + // as eight flags, "1" representing that the unit is an unencoded + // letter (1 byte), "0" a position-and-length pair (2 bytes). Thus, + // eight units require at most 16 bytes of code. + // + + code_buf_ptr = mask = 1; + s = 0; + r = LZW_N - LZW_F; + + // Clear the buffer with any character that will appear often. + // + + for (i = s; i < r; i++) + LZW_ring_buffer[i] = ' '; + + // Read LZW_F bytes into the last LZW_F bytes of the buffer + // + + for (len = 0; (len < LZW_F) && DataLength; len++) + { + c = ReadPtr((long)&infile,PtrTypes); + DataLength--; + + // text of size zero + + LZW_ring_buffer[r + len] = c; + } + + if (!(len && DataLength)) + return(0); + + // + // Insert the LZW_F strings, each of which begins with one or more + // 'space' characters. Note the order in which these strings + // are inserted. This way, degenerate trees will be less likely + // to occur. + // + + for (i = 1; i <= LZW_F; i++) + InsertLZWNode(r - i); + + // + // Finally, insert the whole string just read. The global + // variables LZW_match_len and LZW_match_pos are set. */ + // + + InsertLZWNode(r); + + do { + // LZW_match_len may be spuriously long near the end of text. + // + + if (LZW_match_len > len) + LZW_match_len = len; + + if (LZW_match_len <= LZW_THRESHOLD) + { + // Not long enough match. Send one byte. + // + + LZW_match_len = 1; + + // 'send one byte' flag + // + + code_buf[0] |= mask; + + // Send uncoded. + // + + code_buf[code_buf_ptr++] = LZW_ring_buffer[r]; + } + else + { + code_buf[code_buf_ptr++] = (unsigned char) LZW_match_pos; + code_buf[code_buf_ptr++] = (unsigned char) (((LZW_match_pos >> 4) & 0xf0) | (LZW_match_len - (LZW_THRESHOLD + 1))); + + // Send position and length pair. + // Note LZW_match_len > LZW_THRESHOLD. + } + + if ((mask <<= 1) == 0) + { + // Shift mask left one bit. + // Send at most 8 units of data + + for (i = 0; i < code_buf_ptr; i++) + WritePtr((long)&outfile,code_buf[i],PtrTypes); + + complen += code_buf_ptr; + code_buf[0] = 0; + code_buf_ptr = mask = 1; + } + + last_LZW_match_len = LZW_match_len; + + for (i = 0; i < last_LZW_match_len && DataLength; i++) + { + c = ReadPtr((long)&infile,PtrTypes); + DataLength--; + + DeleteLZWNode(s); // Delete old strings and + LZW_ring_buffer[s] = c; // read new bytes + + // If the position is near the end of buffer, extend the + // buffer to make string comparison easier. + + if (s < LZW_F - 1) + LZW_ring_buffer[s + LZW_N] = c; + + // Since this is a ring buffer, inc the position modulo LZW_N. + // + + s = (s + 1) & (LZW_N - 1); + r = (r + 1) & (LZW_N - 1); + + // Register the string in LZW_ring_buffer[r..r+LZW_F-1] + // + + InsertLZWNode(r); + } + + while (i++ < last_LZW_match_len) + { + // After the end of text, + DeleteLZWNode(s); // no need to read, but + + s = (s + 1) & (LZW_N - 1); + r = (r + 1) & (LZW_N - 1); + + if (--len) + InsertLZWNode(r); // buffer may not be empty. + } + + } while (len > 0); // until length of string to be processed is zero + + + if (code_buf_ptr > 1) + { + // Send remaining code. + // + + for (i = 0; i < code_buf_ptr; i++) + WritePtr((long)&outfile,code_buf[i],PtrTypes); + + complen += code_buf_ptr; + } + + return(complen); +} +#endif + + + + +//-------------------------------------------------------------------------- +// +// lzwDecompress() - Compresses data from an input ptr to a dest ptr +// +// PARAMS: +// infile - Pointer at the BEGINNING of the compressed data (no header!) +// outfile - Pointer to the destination. +// DataLength - Length of compressed data. +// PtrTypes - Type of pointers being used (SRC_FILE,DEST_FILE,SRC_MEM etc). +// +// RETURNS: +// Length of compressed data. +// +// COMPTYPE : ct_LZW +// +// NOTES : Does not write ANY header information! +// +void far lzwDecompress(void far *infile, void far *outfile,unsigned long DataLength,unsigned PtrTypes) +{ + int i, j, k, r, c; + unsigned int flags; + + for (i = 0; i < LZW_N - LZW_F; i++) + LZW_ring_buffer[i] = ' '; + + r = LZW_N - LZW_F; + flags = 0; + + for ( ; ; ) + { + if (((flags >>= 1) & 256) == 0) + { + c = ReadPtr((long)&infile,PtrTypes); + if (!DataLength--) + return; + + flags = c | 0xff00; // uses higher byte cleverly to count 8 + } + + if (flags & 1) + { + c = ReadPtr((long)&infile,PtrTypes); // Could test for EOF iff FFILE type + if (!DataLength--) + return; + + WritePtr((long)&outfile,c,PtrTypes); + + LZW_ring_buffer[r++] = c; + r &= (LZW_N - 1); + } + else + { + i = ReadPtr((long)&infile,PtrTypes); + if (!DataLength--) + return; + + j = ReadPtr((long)&infile,PtrTypes); + if (!DataLength--) + return; + + i |= ((j & 0xf0) << 4); + j = (j & 0x0f) + LZW_THRESHOLD; + + for (k = 0; k <= j; k++) + { + c = LZW_ring_buffer[(i + k) & (LZW_N - 1)]; + + WritePtr((long)&outfile,c,PtrTypes); + + LZW_ring_buffer[r++] = c; + r &= (LZW_N - 1); + } + } + } +} + + + +#if 0 +//========================================================================= +// +// +// BUFFERED I/O ROUTINES +// +// +//========================================================================= + + +//-------------------------------------------------------------------------- +// InitBufferedIO() +//-------------------------------------------------------------------------- +memptr InitBufferedIO(int handle, BufferedIO *bio) +{ + bio->handle = handle; + bio->offset = BIO_BUFFER_LEN; + bio->status = 0; + MM_GetPtr(&bio->buffer,BIO_BUFFER_LEN); + + return(bio->buffer); +} + + +//-------------------------------------------------------------------------- +// FreeBufferedIO() +//-------------------------------------------------------------------------- +void FreeBufferedIO(BufferedIO *bio) +{ + if (bio->buffer) + MM_FreePtr(&bio->buffer); +} + + +//-------------------------------------------------------------------------- +// bio_readch() +//-------------------------------------------------------------------------- +byte bio_readch(BufferedIO *bio) +{ + byte far *buffer; + + if (bio->offset == BIO_BUFFER_LEN) + { + bio->offset = 0; + bio_fillbuffer(bio); + } + + buffer = MK_FP(bio->buffer,bio->offset++); + + return(*buffer); +} + + +//-------------------------------------------------------------------------- +// bio_fillbuffer() +// +// BUGS (Not really bugs... More like RULES!) +// +// 1) This code assumes BIO_BUFFER_LEN is no smaller than +// NEAR_BUFFER_LEN!! +// +// 2) BufferedIO.status should be altered by this code to report +// read errors, end of file, etc... If you know how big the file +// is you're reading, determining EOF should be no problem. +// +//-------------------------------------------------------------------------- +void bio_fillbuffer(BufferedIO *bio) +{ + #define NEAR_BUFFER_LEN (64) + byte near_buffer[NEAR_BUFFER_LEN]; + short bio_length,bytes_read,bytes_requested; + + bytes_read = 0; + bio_length = BIO_BUFFER_LEN; + while (bio_length) + { + if (bio_length > NEAR_BUFFER_LEN-1) + bytes_requested = NEAR_BUFFER_LEN; + else + bytes_requested = bio_length; + + read(bio->handle,near_buffer,bytes_requested); + _fmemcpy(MK_FP(bio->buffer,bytes_read),near_buffer,bytes_requested); + + bio_length -= bytes_requested; + bytes_read += bytes_requested; + } +} + + +#endif + +//========================================================================= +// +// +// GENERAL LOAD ROUTINES +// +// +//========================================================================= + + + +//-------------------------------------------------------------------------- +// BLoad() +//-------------------------------------------------------------------------- +unsigned long BLoad(char *SourceFile, memptr *DstPtr) +{ + int handle; + + memptr SrcPtr; + longword i, j, k, r, c; + word flags; + byte Buffer[8]; + longword DstLen, SrcLen; + boolean comp; + + if ((handle = open(SourceFile, O_RDONLY|O_BINARY)) == -1) + return(0); + + // Look for 'COMP' header + // + read(handle,Buffer,4); + comp = !strncmp(Buffer,COMP,4); + + // Get source and destination length. + // + if (comp) + { + SrcLen = Verify(SourceFile); + read(handle,(void *)&DstLen,4); + MM_GetPtr(DstPtr,DstLen); + if (!*DstPtr) + return(0); + } + else + DstLen = Verify(SourceFile); + + // LZW decompress OR simply load the file. + // + if (comp) + { + + if (MM_TotalFree() < SrcLen) + { + if (!InitBufferedIO(handle,&lzwBIO)) + TrashProg("No memory for buffered I/O."); + lzwDecompress(&lzwBIO,MK_FP(*DstPtr,0),SrcLen,(SRC_BFILE|DEST_MEM)); + FreeBufferedIO(&lzwBIO); + } + else + { + CA_LoadFile(SourceFile,&SrcPtr); + lzwDecompress(MK_FP(SrcPtr,8),MK_FP(*DstPtr,0),SrcLen,(SRC_MEM|DEST_MEM)); + MM_FreePtr(&SrcPtr); + } + } + else + CA_LoadFile(SourceFile,DstPtr); + + close(handle); + return(DstLen); +} + + + + +//////////////////////////////////////////////////////////////////////////// +// +// LoadLIBShape() +// +int LoadLIBShape(char *SLIB_Filename, char *Filename,struct Shape *SHP) +{ + #define CHUNK(Name) (*ptr == *Name) && \ + (*(ptr+1) == *(Name+1)) && \ + (*(ptr+2) == *(Name+2)) && \ + (*(ptr+3) == *(Name+3)) + + + int RT_CODE; + FILE *fp; + char CHUNK[5]; + char far *ptr; + memptr IFFfile = NULL; + unsigned long FileLen, size, ChunkLen; + int loop; + + + RT_CODE = 1; + + // Decompress to ram and return ptr to data and return len of data in + // passed variable... + + if (!LoadLIBFile(SLIB_Filename,Filename,&IFFfile)) + TrashProg("Error Loading Compressed lib shape!"); + + // Evaluate the file + // + ptr = MK_FP(IFFfile,0); + if (!CHUNK("FORM")) + goto EXIT_FUNC; + ptr += 4; + + FileLen = *(long far *)ptr; + SwapLong((long far *)&FileLen); + ptr += 4; + + if (!CHUNK("ILBM")) + goto EXIT_FUNC; + ptr += 4; + + FileLen += 4; + while (FileLen) + { + ChunkLen = *(long far *)(ptr+4); + SwapLong((long far *)&ChunkLen); + ChunkLen = (ChunkLen+1) & 0xFFFFFFFE; + + if (CHUNK("BMHD")) + { + ptr += 8; + SHP->bmHdr.w = ((struct BitMapHeader far *)ptr)->w; + SHP->bmHdr.h = ((struct BitMapHeader far *)ptr)->h; + SHP->bmHdr.x = ((struct BitMapHeader far *)ptr)->x; + SHP->bmHdr.y = ((struct BitMapHeader far *)ptr)->y; + SHP->bmHdr.d = ((struct BitMapHeader far *)ptr)->d; + SHP->bmHdr.trans = ((struct BitMapHeader far *)ptr)->trans; + SHP->bmHdr.comp = ((struct BitMapHeader far *)ptr)->comp; + SHP->bmHdr.pad = ((struct BitMapHeader far *)ptr)->pad; + SwapWord(&SHP->bmHdr.w); + SwapWord(&SHP->bmHdr.h); + SwapWord(&SHP->bmHdr.x); + SwapWord(&SHP->bmHdr.y); + ptr += ChunkLen; + } + else + if (CHUNK("BODY")) + { + ptr += 4; + size = *((long far *)ptr); + ptr += 4; + SwapLong((long far *)&size); + SHP->BPR = (SHP->bmHdr.w+7) >> 3; + MM_GetPtr(&SHP->Data,size); + if (!SHP->Data) + goto EXIT_FUNC; + movedata(FP_SEG(ptr),FP_OFF(ptr),FP_SEG(SHP->Data),0,size); + ptr += ChunkLen; + + break; + } + else + ptr += ChunkLen+8; + + FileLen -= ChunkLen+8; + } + + RT_CODE = 0; + +EXIT_FUNC:; + if (IFFfile) + { +// segptr = (memptr)FP_SEG(IFFfile); + MM_FreePtr(&IFFfile); + } + + return (RT_CODE); +} + + + + + +//---------------------------------------------------------------------------- +// LoadLIBFile() -- Copies a file from an existing archive to dos. +// +// PARAMETERS : +// +// LibName - Name of lib file created with SoftLib V1.0 +// +// FileName - Name of file to load from lib file. +// +// MemPtr - (IF !NULL) - Pointer to memory to load into .. +// (IF NULL) - Routine allocates necessary memory and +// returns a MEM(SEG) pointer to memory allocated. +// +// RETURN : +// +// (IF !NULL) - A pointer to the loaded data. +// (IF NULL) - Error! +// +//---------------------------------------------------------------------------- +memptr LoadLIBFile(char *LibName,char *FileName,memptr *MemPtr) +{ + int handle; + unsigned long header; + struct ChunkHeader Header; + unsigned long ChunkLen; + short x; + struct FileEntryHdr FileEntry; // Storage for file once found + struct FileEntryHdr FileEntryHeader; // Header used durring searching + struct SoftLibHdr LibraryHeader; // Library header - Version Checking + boolean FileFound = false; + unsigned long id_slib = ID_SLIB; + unsigned long id_chunk = ID_CHUNK; + + + // + // OPEN SOFTLIB FILE + // + + if ((handle = open(LibName,O_RDONLY | O_BINARY, S_IREAD)) == -1) + return(NULL); + + + // + // VERIFY it is a SOFTLIB (SLIB) file + // + + if (read(handle,&header,4) == -1) + { + close(handle); + return(NULL); + } + + if (header != id_slib) + { + close(handle); + return(NULL); + } + + + // + // CHECK LIBRARY HEADER VERSION NUMBER + // + + if (read(handle, &LibraryHeader,sizeof(struct SoftLibHdr)) == -1) + TrashProg("read error in LoadSLIBFile()\n%c",7); + + if (LibraryHeader.Version > SOFTLIB_VER) + TrashProg("Unsupported file ver %d",LibraryHeader.Version); + + + // + // MANAGE FILE ENTRY HEADERS... + // + + for (x = 1;x<=LibraryHeader.FileCount;x++) + { + if (read(handle, &FileEntryHeader,sizeof(struct FileEntryHdr)) == -1) + { + close(handle); + return(NULL); + } + + if (!stricmp(FileEntryHeader.FileName,FileName)) + { + FileEntry = FileEntryHeader; + FileFound = true; + } + } + + // + // IF FILE HAS BEEN FOUND THEN SEEK TO POSITION AND EXTRACT + // ELSE RETURN WITH ERROR CODE... + // + + if (FileFound) + { + if (lseek(handle,FileEntry.Offset,SEEK_CUR) == -1) + { + close(handle); + return(NULL); + } + + // + // READ CHUNK HEADER - Verify we are at the beginning of a chunk.. + // + + if (read(handle,(char *)&Header,sizeof(struct ChunkHeader)) == -1) + TrashProg("LIB File - Unable to read Header!"); + + if (Header.HeaderID != id_chunk) + TrashProg("LIB File - BAD HeaderID!"); + + // + // Allocate memory if Necessary... + // + + + if (!*MemPtr) + MM_GetPtr(MemPtr,FileEntry.OrginalLength); + + // + // Calculate the length of the data (without the chunk header). + // + + ChunkLen = FileEntry.ChunkLen - sizeof(struct ChunkHeader); + + + // + // Extract Data from file + // + + switch (Header.Compression) + { + case ct_LZW: + if (!InitBufferedIO(handle,&lzwBIO)) + TrashProg("No memory for buffered I/O."); + lzwDecompress(&lzwBIO,MK_FP(*MemPtr,0),ChunkLen,(SRC_BFILE|DEST_MEM)); + FreeBufferedIO(&lzwBIO); + break; + + case ct_NONE: + if (!CA_FarRead(handle,MK_FP(*MemPtr,0),ChunkLen)) + { + close(handle); + *MemPtr = NULL; + } + break; + + default: + close(handle); + TrashProg("Uknown Chunk.Compression Type!"); + break; + } + } + else + *MemPtr = NULL; + + close(handle); + return(*MemPtr); +} + + + + diff --git a/16/cawat/JAMPAK.H b/16/cawat/JAMPAK.H new file mode 100644 index 00000000..43f95ed5 --- /dev/null +++ b/16/cawat/JAMPAK.H @@ -0,0 +1,118 @@ +/* Catacomb Armageddon Source Code + * Copyright (C) 1993-2014 Flat Rock Software + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +// +// +// +// +// + + +#define LZW_N 4096 +#define LZW_F 18 + + +// LZW_THRESHOLD :encode string into position and length if match_length is +// greater than this + +#define LZW_THRESHOLD 2 + +// index for root of binary search trees +// + +#define LZW_NIL LZW_N + + +// +// FILE CHUNK IDs +// +// NOTE: The only reason for changing from COMP to CMP2 and having multi +// comp header structs is for downward compatablity. +// + +#define COMP ("COMP") // Comp type is ct_LZW ALWAYS! +#define CMP2 ("CMP2") // Comp type is determined in header. + + +// +// COMPRESSION TYPES +// + +#if 0 +// +// FILE CHUNK HEADER FORMATS +// + +struct COMPStruct +{ + unsigned long DecompLen; + +}; + + +struct CMP2Header +{ + unsigned CompType; + unsigned long DecompLen; + +}; +#endif + +memptr segptr; +extern BufferedIO lzwBIO; + + +// +// PARAMETER PASSING TYPES (POINTER TYPES) +// + +#define SRC_FILE (0x0001) // C's non-buffered file i/o +#define SRC_FFILE (0x0002) // C's buffered ffile i/o +#define SRC_MEM (0x0004) // FAR memory Ptrs +#define SRC_BFILE (0x0008) // Buffered File I/O + +#define SRC_TYPES (SRC_FILE | SRC_FFILE | SRC_MEM | SRC_BFILE) + +#define DEST_FILE (0x0100) // C's non-buffered file i/o +#define DEST_FFILE (0x0200) // C's buffered ffile i/o +#define DEST_MEM (0x0400) // FAR memory Ptrs + +#define DEST_TYPES (DEST_FILE | DEST_FFILE | DEST_MEM) + + +//--------------------------------------------------------------------------- +// FUNCTION PROTOTYPEING +//--------------------------------------------------------------------------- + +//void DecompressFILEtoFILE(FILE *infile, FILE *outfile); +//unsigned long CompressFILEtoFILE(FILE *infile, FILE *outfile,unsigned long DataLength); + + +unsigned long lzwCompress(void far *infile, void far *outfile,unsigned long DataLength,unsigned PtrTypes); +void lzwDecompress(void far *infile, void far *outfile,unsigned long DataLength,unsigned PtrTypes); + +int WritePtr(long outfile, unsigned char data, unsigned PtrType); +int ReadPtr(long infile, unsigned PtrType); + +//memptr InitBufferedIO(int handle, BufferedIO *bio); +//void FreeBufferedIO(BufferedIO *bio); +//byte bio_readch(BufferedIO *bio); + +unsigned long BLoad(char *SourceFile, memptr *DstPtr); +memptr LoadLIBFile(char *LibName,char *FileName,memptr *MemPtr); +int LoadLIBShape(char *SLIB_Filename, char *Filename,struct Shape *SHP); diff --git a/16/cawat/JAM_IO.C b/16/cawat/JAM_IO.C new file mode 100644 index 00000000..d0b019a0 --- /dev/null +++ b/16/cawat/JAM_IO.C @@ -0,0 +1,121 @@ +/* Catacomb Armageddon Source Code + * Copyright (C) 1993-2014 Flat Rock Software + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "def.h" +#include "gelib.h" +#include "jam_io.h" + +//---------------------------------------------------------------------------- +// +// PTR/PTR COMPRESSION ROUTINES +// +// +//---------------------------------------------------------------------------- + + + +//--------------------------------------------------------------------------- +// WritePtr() -- Outputs data to a particular ptr type +// +// PtrType MUST be of type DEST_TYPE. +// +// NOTE : For PtrTypes DEST_MEM a ZERO (0) is always returned. +// +//--------------------------------------------------------------------------- +char WritePtr(long outfile, unsigned char data, unsigned PtrType) +{ + int returnval = 0; + + switch (PtrType & DEST_TYPES) + { + case DEST_FILE: + write(*(int far *)outfile,(char *)&data,1); + break; + + case DEST_FFILE: + returnval = putc(data, *(FILE **)outfile); + break; + + case DEST_IMEM: + printf("WritePtr - unsupported ptr type\n"); + exit(0); + break; + + case DEST_MEM: + *((char far *)*(char far **)outfile)++ = data; + break; + } + + return(returnval); + +} + + +//--------------------------------------------------------------------------- +// ReadPtr() -- Reads data from a particular ptr type +// +// PtrType MUST be of type SRC_TYPE. +// +// RETURNS : +// The char read in or EOF for SRC_FFILE type of reads. +// +// +//--------------------------------------------------------------------------- +int ReadPtr(long infile, unsigned PtrType) +{ + int returnval = 0; + + switch (PtrType & SRC_TYPES) + { + case SRC_FILE: + read(*(int far *)infile,(char *)&returnval,1); + break; + + case SRC_FFILE: + returnval = getc(*(FILE far **)infile + ); + break; + + case SRC_BFILE: + returnval = bio_readch((BufferedIO *)*(void far **)infile); + break; + +// case SRC_IMEM: +// printf("WritePtr - unsupported ptr type\n"); +// exit(0); +// break; + + case SRC_MEM: + returnval = (unsigned char)*((char far *)*(char far **)infile)++; + break; + } + + return(returnval); +} + + + diff --git a/16/cawat/JAM_IO.H b/16/cawat/JAM_IO.H new file mode 100644 index 00000000..77db01dc --- /dev/null +++ b/16/cawat/JAM_IO.H @@ -0,0 +1,107 @@ +/* Catacomb Armageddon Source Code + * Copyright (C) 1993-2014 Flat Rock Software + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +// +// UNIT : JAM_IO.h +// +// FUNCTION : General defines, prototypes, typedefs used in all the +// supported compression techniques used in JAMPAK Ver x.x +// +// + + + + +//========================================================================== +// +// PARAMETER PASSING TYPES +// +// + // SOURCE PARAMS (LO BYTE) + +#define SRC_FILE (0x0001) // GE Buffered IO +#define SRC_FFILE (0x0002) // Stdio File IO (fwrite etc.) +#define SRC_MEM (0x0004) // Std void ptr (alloc etc) +#define SRC_BFILE (0x0008) // Buffered File I/O + +#define SRC_TYPES (SRC_FILE | SRC_FFILE | SRC_MEM | SRC_BFILE) + + // DESTINATION PARAMS (HI BYTE) + +#define DEST_FILE (0x0100) // GE Buffered IO +#define DEST_FFILE (0x0200) // Stdio File IO (fwrite etc.) +#define DEST_MEM (0x0400) // Std void ptr (alloc etc) +#define DEST_IMEM (0x0800) // ID Memory alloc + +#define DEST_TYPES (DEST_FILE | DEST_FFILE | DEST_MEM | DEST_IMEM) + + + +//========================================================================= +// +// FILE CHUNK IDs +// +// NOTE: The only reason for changing from COMP to CMP1 and having multi +// comp header structs is for downward compatablity. +// + +#define COMP ("COMP") // Comp type is ct_LZW ALWAYS! +#define CMP1 ("CMP1") // Comp type is determined in header. + + +// +// COMPRESSION TYPES +// +typedef enum ct_TYPES +{ + ct_NONE = 0, // No compression - Just data..Rarely used! + ct_LZW, // LZW data compression + ct_LZH, + +} ct_TYPES; + +// +// FILE CHUNK HEADER FORMATS +// + +struct COMPStruct +{ + unsigned long DecompLen; + +}; + + +struct CMP1Header +{ + unsigned CompType; // SEE: ct_TYPES above for list of pos. + unsigned long OrginalLen; // Orginal FileLength of compressed Data. + unsigned long CompressLen; // Length of data after compression (A MUST for LZHUFF!) +}; + + + +//--------------------------------------------------------------------------- +// +// FUNCTION PROTOTYPEING +// +//--------------------------------------------------------------------------- + +char WritePtr(long outfile, unsigned char data, unsigned PtrType); +int ReadPtr(long infile, unsigned PtrType); + + diff --git a/16/cawat/LZHUF.C b/16/cawat/LZHUF.C new file mode 100644 index 00000000..bca738e1 --- /dev/null +++ b/16/cawat/LZHUF.C @@ -0,0 +1,1069 @@ +/* Catacomb Armageddon Source Code + * Copyright (C) 1993-2014 Flat Rock Software + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +//=========================================================================== +// +// LZHUFF COMPRESSION ROUTINES +// VERSION 1.0 +// +// Compression algrythim by Haruhiko OKUMURA +// Implementation by Jim T. Row +// +// +// Copyright (c) 1992 - Softdisk Publishing inc. - All rights reserved +// +//=========================================================================== +// +// Compiler #ifdef switches +// +// LZHUFF_COMPRESSION & LZHUFF_DECOMPRESSION - not yet functional! +// +// Usage Explanition : +// +// if LZHUFF_COMPRESSION is defined then the compression code & data is +// compiled and so-forth for the decompression code. +// +//--------------------------------------------------------------------------- + + + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "lzhuff.h" +#include "jam_io.h" + + + +//=========================================================================== +// +// SWITCHES +// +// NOTE : Make sure the appropriate switches are set in SOFT.c for Softlib +// archive support. +// +//=========================================================================== + + +#define INCLUDE_LZH_COMP 0 +#define INCLUDE_LZH_DECOMP 1 + + + + + +//=========================================================================== +// +// DEFINES +// +//=========================================================================== + + +#define EXIT_OK 0 +#define EXIT_FAILED -1 + +/* LZSS Parameters */ + +#define N 4096 /* Size of string buffer */ +#define F 30 /* Size of look-ahead buffer */ +#define THRESHOLD 2 +#define NIL N /* End of tree's node */ + +/* Huffman coding parameters */ + +#define N_CHAR (256 - THRESHOLD + F) /* character code (= 0..N_CHAR-1) */ +#define T (N_CHAR * 2 - 1) /* Size of table */ +#define R (T - 1) /* root position */ +#define MAX_FREQ 0x8000 /* update when cumulative frequency */ + /* reaches to this value */ + + +//========================================================================== +// +// LOCAL PROTOTYPES +// +//========================================================================== + + +static void StartHuff(); +static void reconst(); +static void update(int c); + + +static void DeleteNode(int p); /* Deleting node from the tree */ +static void InsertNode(int r); /* Inserting node to the tree */ +static void InitTree(void); /* Initializing tree */ +static void Putcode(long outfile_ptr, int l, unsigned c,unsigned PtrTypes); /* output c bits */ +static void EncodeChar(long outfile_ptr, unsigned c, unsigned PtrTypes); +static void EncodePosition(long outfile_ptr, unsigned c, unsigned PtrTypes); +static void EncodeEnd(long outfile_ptr,unsigned PtrTypes); + + +static int GetByte(long infile_ptr, unsigned long *CompressLength, unsigned PtrTypes); +static int GetBit(long infile_ptr, unsigned long *CompressLength, unsigned PtrTypes); /* get one bit */ +static int DecodeChar(long infile_ptr, unsigned long *CompressLength, unsigned PtrTypes); +static int DecodePosition(long infile_ptr,unsigned long *CompressLength, unsigned PtrTypes); + + + + +//========================================================================== +// +// USER AVAILABLE VECTORS +// +//========================================================================== + + + + +//--------------------------------------------------------------------------- +// +// LZHUFF DISPLAY VECTORS +// +// These vectors allow you to hook up any form of display you desire for +// displaying the compression/decompression status. +// +// These routines are called inside of the compression/decompression routines +// and pass the orginal size of data and current position within that +// data. This allows for any kind of "% Done" messages. +// +// Your functions MUST have the following parameters in this order... +// +// void VectorRoutine(unsigned long OrginalSize,unsigned long CurPosition) +// +// + +#if INCLUDE_LZH_COMP +void (*LZH_CompressDisplayVector)() = NULL; +#endif + +#if INCLUDE_LZH_DECOMP +void (*LZH_DecompressDisplayVector)() = NULL; +#endif + + + + +//=========================================================================== +// +// GLOBAL VARIABLES +// +//=========================================================================== + /* pointing children nodes (son[], son[] + 1)*/ + +int far son[T]; +unsigned code, len; + + // + // pointing parent nodes. + // area [T..(T + N_CHAR - 1)] are pointers for leaves + // + +int far prnt[T + N_CHAR]; + +unsigned far freq[T + 1]; /* cumulative freq table */ + +unsigned long textsize = 0, codesize = 0, printcount = 0,datasize; +unsigned char far text_buf[N + F - 1]; + + + + // + // COMPRESSION VARIABLES + // + +#if INCLUDE_LZH_COMP + +static int match_position,match_length, lson[N + 1], rson[N + 257], dad[N + 1]; +unsigned putbuf = 0; +unsigned char putlen = 0; + + // + // Tables for encoding/decoding upper 6 bits of + // sliding dictionary pointer + // + + // + // encoder table + // + +unsigned char far p_len[64] = { + 0x03, 0x04, 0x04, 0x04, 0x05, 0x05, 0x05, 0x05, + 0x05, 0x05, 0x05, 0x05, 0x06, 0x06, 0x06, 0x06, + 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08 +}; + +unsigned char far p_code[64] = { + 0x00, 0x20, 0x30, 0x40, 0x50, 0x58, 0x60, 0x68, + 0x70, 0x78, 0x80, 0x88, 0x90, 0x94, 0x98, 0x9C, + 0xA0, 0xA4, 0xA8, 0xAC, 0xB0, 0xB4, 0xB8, 0xBC, + 0xC0, 0xC2, 0xC4, 0xC6, 0xC8, 0xCA, 0xCC, 0xCE, + 0xD0, 0xD2, 0xD4, 0xD6, 0xD8, 0xDA, 0xDC, 0xDE, + 0xE0, 0xE2, 0xE4, 0xE6, 0xE8, 0xEA, 0xEC, 0xEE, + 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, + 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF +}; +#endif + + + // + // DECOMPRESSION VARIABLES + // + + + // + // decoder table + // + +#if INCLUDE_LZH_DECOMP + +unsigned char far d_code[256] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, + 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, + 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, + 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, + 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, + 0x0C, 0x0C, 0x0C, 0x0C, 0x0D, 0x0D, 0x0D, 0x0D, + 0x0E, 0x0E, 0x0E, 0x0E, 0x0F, 0x0F, 0x0F, 0x0F, + 0x10, 0x10, 0x10, 0x10, 0x11, 0x11, 0x11, 0x11, + 0x12, 0x12, 0x12, 0x12, 0x13, 0x13, 0x13, 0x13, + 0x14, 0x14, 0x14, 0x14, 0x15, 0x15, 0x15, 0x15, + 0x16, 0x16, 0x16, 0x16, 0x17, 0x17, 0x17, 0x17, + 0x18, 0x18, 0x19, 0x19, 0x1A, 0x1A, 0x1B, 0x1B, + 0x1C, 0x1C, 0x1D, 0x1D, 0x1E, 0x1E, 0x1F, 0x1F, + 0x20, 0x20, 0x21, 0x21, 0x22, 0x22, 0x23, 0x23, + 0x24, 0x24, 0x25, 0x25, 0x26, 0x26, 0x27, 0x27, + 0x28, 0x28, 0x29, 0x29, 0x2A, 0x2A, 0x2B, 0x2B, + 0x2C, 0x2C, 0x2D, 0x2D, 0x2E, 0x2E, 0x2F, 0x2F, + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, + 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F, +}; + +unsigned char far d_len[256] = { + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, + 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, + 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, + 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, + 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, + 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, + 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, + 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, + 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, + 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, + 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, + 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, + 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, + 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, +}; + +unsigned getbuf = 0; +unsigned char getlen = 0; + +#endif + + + +//=========================================================================== +// +// COMPRESSION & DECOMPRESSION ROUTINES +// +//=========================================================================== + + + + + + + +//--------------------------------------------------------------------------- +// StartHuff /* initialize freq tree */ +//--------------------------------------------------------------------------- +static void StartHuff() +{ + int i, j; + + for (i = 0; i < N_CHAR; i++) { + freq[i] = 1; + son[i] = i + T; + prnt[i + T] = i; + } + i = 0; j = N_CHAR; + while (j <= R) { + freq[j] = freq[i] + freq[i + 1]; + son[j] = i; + prnt[i] = prnt[i + 1] = j; + i += 2; j++; + } + freq[T] = 0xffff; + prnt[R] = 0; +} + + + + + + +//--------------------------------------------------------------------------- +// reconst /* reconstruct freq tree */ +//--------------------------------------------------------------------------- +static void reconst() +{ + int i, j, k; + unsigned f, l; + + /* halven cumulative freq for leaf nodes */ + + j = 0; + + for (i = 0; i < T; i++) + { + if (son[i] >= T) + { + freq[j] = (freq[i] + 1) / 2; + son[j] = son[i]; + j++; + } + } + + /* make a tree : first, connect children nodes */ + + for (i = 0, j = N_CHAR; j < T; i += 2, j++) + { + k = i + 1; + f = freq[j] = freq[i] + freq[k]; + + for (k = j - 1;f < freq[k]; k--); + + k++; + l = (j - k) * 2; + + (void)memmove(&freq[k + 1], &freq[k], l); + freq[k] = f; + + (void)memmove(&son[k + 1], &son[k], l); + son[k] = i; + } + + /* connect parent nodes */ + + for (i = 0; i < T; i++) + { + if ((k = son[i]) >= T) + { + prnt[k] = i; + } + else + { + prnt[k] = prnt[k + 1] = i; + } + } +} + + + + + + +//--------------------------------------------------------------------------- +// update() update freq tree +//--------------------------------------------------------------------------- +static void update(int c) +{ + int i, j, k, l; + + if (freq[R] == MAX_FREQ) + { + reconst(); + } + + c = prnt[c + T]; + + do { + k = ++freq[c]; + + // + // swap nodes to keep the tree freq-ordered + // + + if (k > freq[l = c + 1]) + { + while (k > freq[++l]); + + l--; + freq[c] = freq[l]; + freq[l] = k; + + i = son[c]; + prnt[i] = l; + if (i < T) + prnt[i + 1] = l; + + j = son[l]; + son[l] = i; + + prnt[j] = c; + if (j < T) + prnt[j + 1] = c; + + son[c] = j; + + c = l; + } + } while ((c = prnt[c]) != 0); /* do it until reaching the root */ +} + + + + +//=========================================================================== +// +// COMPRESSION ROUTINES +// +//=========================================================================== + + + + + + +#if INCLUDE_LZH_COMP + + +//--------------------------------------------------------------------------- +// DeleteNode +//--------------------------------------------------------------------------- +static void DeleteNode(int p) /* Deleting node from the tree */ +{ + int q; + + if (dad[p] == NIL) + return; /* unregistered */ + + if (rson[p] == NIL) + q = lson[p]; + else + if (lson[p] == NIL) + q = rson[p]; + else + { + q = lson[p]; + if (rson[q] != NIL) + { + do { + q = rson[q]; + } while (rson[q] != NIL); + + rson[dad[q]] = lson[q]; + dad[lson[q]] = dad[q]; + lson[q] = lson[p]; + dad[lson[p]] = q; + } + + rson[q] = rson[p]; + dad[rson[p]] = q; + } + + dad[q] = dad[p]; + + if (rson[dad[p]] == p) + rson[dad[p]] = q; + else + lson[dad[p]] = q; + + dad[p] = NIL; +} + + + + + + +//--------------------------------------------------------------------------- +// InsertNode +//--------------------------------------------------------------------------- +static void InsertNode(int r) /* Inserting node to the tree */ +{ + int i, p, cmp; + unsigned char *key; + unsigned c; + + cmp = 1; + key = &text_buf[r]; + p = N + 1 + key[0]; + rson[r] = lson[r] = NIL; + match_length = 0; + for ( ; ; ) + { + if (cmp >= 0) + { + if (rson[p] != NIL) + p = rson[p]; + else + { + rson[p] = r; + dad[r] = p; + return; + } + } + else + { + if (lson[p] != NIL) + p = lson[p]; + else + { + lson[p] = r; + dad[r] = p; + return; + } + } + + + for (i = 1; i < F; i++) + if ((cmp = key[i] - text_buf[p + i]) != 0) + break; + + if (i > THRESHOLD) + { + if (i > match_length) + { + match_position = ((r - p) & (N - 1)) - 1; + if ((match_length = i) >= F) + break; + } + + if (i == match_length) + { + if ((c = ((r - p) & (N - 1)) - 1) < match_position) + { + match_position = c; + } + } + } + } + + dad[r] = dad[p]; + lson[r] = lson[p]; + rson[r] = rson[p]; + dad[lson[p]] = r; + dad[rson[p]] = r; + + if (rson[dad[p]] == p) + rson[dad[p]] = r; + else + lson[dad[p]] = r; + + dad[p] = NIL; /* remove p */ +} + + + + + +//--------------------------------------------------------------------------- +// InitTree +//--------------------------------------------------------------------------- +static void InitTree(void) /* Initializing tree */ +{ + int i; + + for (i = N + 1; i <= N + 256; i++) + rson[i] = NIL; /* root */ + + for (i = 0; i < N; i++) + dad[i] = NIL; /* node */ +} + + + + + + +//--------------------------------------------------------------------------- +// Putcode +//--------------------------------------------------------------------------- +static void Putcode(long outfile_ptr, int l, unsigned c,unsigned PtrTypes) /* output c bits */ +{ + putbuf |= c >> putlen; + + if ((putlen += l) >= 8) + { + WritePtr(outfile_ptr, putbuf >> 8, PtrTypes); + codesize++; + + if ((putlen -= 8) >= 8) + { + WritePtr(outfile_ptr, putbuf, PtrTypes); + codesize++; + + putlen -= 8; + putbuf = c << (l - putlen); + } + else + { + putbuf <<= 8; + } + } +} + + + + + + +//--------------------------------------------------------------------------- +// EncodeChar +//--------------------------------------------------------------------------- +static void EncodeChar(long outfile_ptr, unsigned c, unsigned PtrTypes) +{ + unsigned i; + int j, k; + + i = 0; + j = 0; + k = prnt[c + T]; + + /* search connections from leaf node to the root */ + + do { + i >>= 1; + + // + // if node's address is odd, output 1 else output 0 + // + + if (k & 1) + i += 0x8000; + + j++; + } while ((k = prnt[k]) != R); + + Putcode(outfile_ptr, j, i, PtrTypes); + + code = i; + len = j; + update(c); +} + + + + +//--------------------------------------------------------------------------- +// EncodePosition +//--------------------------------------------------------------------------- +static void EncodePosition(long outfile_ptr, unsigned c, unsigned PtrTypes) +{ + unsigned i; + + // + // output upper 6 bits with encoding + // + + i = c >> 6; + Putcode(outfile_ptr, p_len[i], (unsigned)p_code[i] << 8,PtrTypes); + + // + // output lower 6 bits directly + // + + Putcode(outfile_ptr, 6, (c & 0x3f) << 10,PtrTypes); +} + + + + +//--------------------------------------------------------------------------- +// EncodeEnd +//--------------------------------------------------------------------------- +static void EncodeEnd(long outfile_ptr,unsigned PtrTypes) +{ + if (putlen) + { + WritePtr(outfile_ptr,(putbuf >> 8),PtrTypes); + codesize++; + } +} + +#endif + + + + + +//=========================================================================== +// +// DECOMPRESSION ROUTINES +// +//=========================================================================== + + + +#if INCLUDE_LZH_DECOMP + +//--------------------------------------------------------------------------- +// GetByte +//--------------------------------------------------------------------------- +static int GetByte(long infile_ptr, unsigned long *CompressLength, unsigned PtrTypes) +{ + unsigned i; + + while (getlen <= 8) + { + if (*CompressLength) + { + i = ReadPtr(infile_ptr,PtrTypes); + (*CompressLength)--; + } + else + i = 0; + + getbuf |= i << (8 - getlen); + getlen += 8; + } + + i = getbuf; + getbuf <<= 8; + getlen -= 8; + return i>>8; +} + + + + + + +//--------------------------------------------------------------------------- +// GetBit +//--------------------------------------------------------------------------- +static int GetBit(long infile_ptr, unsigned long *CompressLength, unsigned PtrTypes) /* get one bit */ +{ + int i; + + while (getlen <= 8) + { + if (*CompressLength) + { + i = ReadPtr(infile_ptr,PtrTypes); + (*CompressLength)--; + } + else + i = 0; + + getbuf |= i << (8 - getlen); + getlen += 8; + } + + i = getbuf; + getbuf <<= 1; + getlen--; + return (i < 0); +} + + + + + +//--------------------------------------------------------------------------- +// DecodeChar +//--------------------------------------------------------------------------- +static int DecodeChar(long infile_ptr, unsigned long *CompressLength, unsigned PtrTypes) +{ + unsigned c; + + c = son[R]; + + /* + * start searching tree from the root to leaves. + * choose node #(son[]) if input bit == 0 + * else choose #(son[]+1) (input bit == 1) + */ + + while (c < T) + { + c += GetBit(infile_ptr,CompressLength,PtrTypes); + c = son[c]; + } + + c -= T; + update(c); + return c; +} + + + + + +//--------------------------------------------------------------------------- +// DecodePosition +//--------------------------------------------------------------------------- +static int DecodePosition(long infile_ptr,unsigned long *CompressLength, unsigned PtrTypes) +{ + unsigned i, j, c; + + // + // decode upper 6 bits from given table + // + + i = GetByte(infile_ptr, CompressLength, PtrTypes); + c = (unsigned)d_code[i] << 6; + j = d_len[i]; + + // + // input lower 6 bits directly + // + + j -= 2; + while (j--) + { + i = (i << 1) + GetBit(infile_ptr, CompressLength, PtrTypes); + } + + return c | i & 0x3f; +} + +#endif + + + + + +//=========================================================================== +// +// EXTERNAL REFERENCED +// COMPRESSION & DECOMPRESSION +// ROUTINES +// +//=========================================================================== + + + + +#if INCLUDE_LZH_DECOMP + +//--------------------------------------------------------------------------- +// lzhDecompress() +//--------------------------------------------------------------------------- +long lzhDecompress(void far *infile, void far *outfile, unsigned long OrginalLength, unsigned long CompressLength, unsigned PtrTypes) +{ + int i, j, k, r, c; + long count; + + datasize = textsize = OrginalLength; + getbuf = 0; + getlen = 0; + + if (textsize == 0) + return; + + StartHuff(); + for (i = 0; i < N - F; i++) + text_buf[i] = ' '; + + r = N - F; + + for (count = 0; count < textsize; ) + { + c = DecodeChar((long)&infile,&CompressLength,PtrTypes); + + if (c < 256) + { + WritePtr((long)&outfile,c,PtrTypes); + datasize--; // Dec # of bytes to write + + text_buf[r++] = c; + r &= (N - 1); + count++; // inc count of bytes written + } + else + { + i = (r - DecodePosition((long)&infile,&CompressLength,PtrTypes) - 1) & (N - 1); + j = c - 255 + THRESHOLD; + + for (k = 0; k < j; k++) + { + c = text_buf[(i + k) & (N - 1)]; + + WritePtr((long)&outfile,c,PtrTypes); + datasize--; // dec count of bytes to write + + text_buf[r++] = c; + r &= (N - 1); + count++; // inc count of bytes written + } + } + + if (LZH_DecompressDisplayVector && (count > printcount)) + { + LZH_DecompressDisplayVector(OrginalLength,OrginalLength-datasize); + printcount += 1024; + } + } + +// printf("%12ld\n", count); + + return(count); +} + +#endif + + + + + +#if INCLUDE_LZH_COMP + +//--------------------------------------------------------------------------- +// lzhCompress() +//--------------------------------------------------------------------------- +long lzhCompress(void far *infile, void far *outfile,unsigned long DataLength,unsigned PtrTypes) +{ + int i, c, len, r, s, last_match_length; + + textsize = DataLength; + + if (textsize == 0) + return; + + getbuf = 0; + getlen = 0; + textsize = 0; /* rewind and rescan */ + codesize = 0; + datasize = 0; // Init our counter of ReadData... + StartHuff(); + InitTree(); + + s = 0; + r = N - F; + + for (i = s; i < r; i++) + text_buf[i] = ' '; + + for (len = 0; len < F && (DataLength > datasize); len++) + { + c = ReadPtr((long)&infile,PtrTypes); + datasize++; // Dec num of bytes to compress + text_buf[r + len] = c; + } + + textsize = len; + + for (i = 1; i <= F; i++) + InsertNode(r - i); + + InsertNode(r); + + do { + if (match_length > len) + match_length = len; + + if (match_length <= THRESHOLD) + { + match_length = 1; + EncodeChar((long)&outfile,text_buf[r],PtrTypes); + } + else + { + EncodeChar((long)&outfile, 255 - THRESHOLD + match_length,PtrTypes); + EncodePosition((long)&outfile, match_position,PtrTypes); + } + + last_match_length = match_length; + + for (i = 0; i < last_match_length && (DataLength > datasize); i++) + { + c = ReadPtr((long)&infile,PtrTypes); + datasize++; + + DeleteNode(s); + text_buf[s] = c; + + if (s < F - 1) + text_buf[s + N] = c; + + s = (s + 1) & (N - 1); + r = (r + 1) & (N - 1); + InsertNode(r); + } + + if (LZH_CompressDisplayVector && ((textsize += i) > printcount)) + { + LZH_CompressDisplayVector(DataLength,datasize); + printcount += 1024; + } + + + while (i++ < last_match_length) + { + DeleteNode(s); + s = (s + 1) & (N - 1); + r = (r + 1) & (N - 1); + if (--len) + InsertNode(r); + } + + } while (len > 0); + + EncodeEnd((long)&outfile,PtrTypes); + + return(codesize); + +} + + +#endif \ No newline at end of file diff --git a/16/cawat/LZHUFF.H b/16/cawat/LZHUFF.H new file mode 100644 index 00000000..1e4395e9 --- /dev/null +++ b/16/cawat/LZHUFF.H @@ -0,0 +1,35 @@ +/* Catacomb Armageddon Source Code + * Copyright (C) 1993-2014 Flat Rock Software + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +extern void (*LZH_CompressDisplayVector)(); +extern void (*LZH_DecompressDisplayVector)(); + + + +//=========================================================================== +// +// PROTOTYPES +// +//=========================================================================== + + +long lzhCompress(void far *infile, void far *outfile,unsigned long DataLength,unsigned PtrTypes); +long lzhDecompress(void far *infile, void far *outfile, unsigned long OrginalLength, unsigned long CompressLength, unsigned PtrTypes); + + + diff --git a/16/cawat/LZW.C b/16/cawat/LZW.C new file mode 100644 index 00000000..540cc7d8 --- /dev/null +++ b/16/cawat/LZW.C @@ -0,0 +1,608 @@ +/* Catacomb Armageddon Source Code + * Copyright (C) 1993-2014 Flat Rock Software + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +//=========================================================================== +// +// LZW COMPRESSION ROUTINES +// VERSION 1.1 +// +// Compression algrythim by Haruhiko OKUMURA +// Implementation by Jim T. Row +// +// +// Copyright (c) 1992 - Softdisk Publishing inc. - All rights reserved +// +//=========================================================================== +// +// +//--------------------------------------------------------------------------- + + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "lzw.h" +#include "jam_io.h" + + +//=========================================================================== +// +// SWITCHES +// +// NOTE : Make sure the appropriate switches are set in SOFT.c for Softlib +// archive support. +// +//=========================================================================== + +#define INCLUDE_LZW_COMP 0 +#define INCLUDE_LZW_DECOMP 1 + + +//=========================================================================== +// +// DEFINES +// +//=========================================================================== + + +#define LZW_N 4096 +#define LZW_F 18 + +#define LZW_THRESHOLD 2 // encode string into position and + // length if match_length is greater + // than this + +#define LZW_NIL LZW_N // index for root of bin search trees + + +//============================================================================ +// +// GLOBAL VARIABLES NECESSARY FOR +// +// COMP/DECOMP ROUTINES. +// +//============================================================================ + + + +unsigned char far LZW_ring_buffer[LZW_N + LZW_F - 1]; // ring buffer of size + // LZW_N, with extra + // LZW_F-1 bytes to + // facilitate + // string comparison + +#if INCLUDE_LZW_COMP + +int LZW_match_pos, // MAtchLength of longest match. These are set by the + // InsertNode() procedure. + LZW_match_len, + + // left & right children & parents -- These constitute binary search trees. */ + + LZW_left_child[LZW_N + 1], + LZW_right_child[LZW_N + 257], + LZW_parent[LZW_N + 1]; + +#endif + + +//============================================================================ +// +// LZW DISPLAY VECTORS +// +// These vectors allow you to hook up any form of display you desire for +// displaying the compression/decompression status. +// +// These routines are called inside of the compression/decompression routines +// and pass the orginal size of data and current position within that +// data. This allows for any kind of "% Done" messages. +// +// Your functions MUST have the following parameters in this order... +// +// void VectorRoutine(unsigned long OrginalSize,unsigned long CurPosition) +// +// + +void (*LZW_CompressDisplayVector)() = NULL; +void (*LZW_DecompressDisplayVector)() = NULL; + + + + +//============================================================================ +// +// SUPPORT ROUTINES FOR LZW COMPRESSION +// +//============================================================================ + + +#if INCLUDE_LZW_COMP + +static void InitLZWTree(void) /* initialize trees */ +{ + int i; + + /* For i = 0 to LZW_N - 1, LZW_right_child[i] and LZW_left_child[i] will be the right and + left children of node i. These nodes need not be initialized. + Also, LZW_parent[i] is the parent of node i. These are initialized to + LZW_NIL (= LZW_N), which stands for 'not used.' + For i = 0 to 255, LZW_right_child[LZW_N + i + 1] is the root of the tree + for strings that begin with character i. These are initialized + to LZW_NIL. Note there are 256 trees. */ + + for (i = LZW_N + 1; i <= LZW_N + 256; i++) + LZW_right_child[i] = LZW_NIL; + + for (i = 0; i < LZW_N; i++) + LZW_parent[i] = LZW_NIL; +} + + +//////////////////////////////////////////////////////////////////////////// + +static void InsertLZWNode(unsigned long r) + + /* Inserts string of length LZW_F, LZW_ring_buffer[r..r+LZW_F-1], into one of the + trees (LZW_ring_buffer[r]'th tree) and returns the longest-match position + and length via the global variables LZW_match_pos and LZW_match_len. + If LZW_match_len = LZW_F, then removes the old node in favor of the new + one, because the old one will be deleted sooner. + Note r plays double role, as tree node and position in buffer. */ +{ + int i, p, cmp; + unsigned char *key; + + cmp = 1; + key = &LZW_ring_buffer[r]; + p = LZW_N + 1 + key[0]; + LZW_right_child[r] = LZW_left_child[r] = LZW_NIL; + LZW_match_len = 0; + + for ( ; ; ) + { + if (cmp >= 0) + { + if (LZW_right_child[p] != LZW_NIL) + p = LZW_right_child[p]; + else + { + LZW_right_child[p] = r; + LZW_parent[r] = p; + return; + } + } + else + { + if (LZW_left_child[p] != LZW_NIL) + p = LZW_left_child[p]; + else + { + LZW_left_child[p] = r; + LZW_parent[r] = p; + return; + } + } + + for (i = 1; i < LZW_F; i++) + if ((cmp = key[i] - LZW_ring_buffer[p + i]) != 0) + break; + + if (i > LZW_match_len) + { + LZW_match_pos = p; + if ((LZW_match_len = i) >= LZW_F) + break; + } + } + + LZW_parent[r] = LZW_parent[p]; + LZW_left_child[r] = LZW_left_child[p]; + LZW_right_child[r] = LZW_right_child[p]; + LZW_parent[LZW_left_child[p]] = r; + LZW_parent[LZW_right_child[p]] = r; + + if (LZW_right_child[LZW_parent[p]] == p) + LZW_right_child[LZW_parent[p]] = r; + else + LZW_left_child[LZW_parent[p]] = r; + + LZW_parent[p] = LZW_NIL; /* remove p */ +} + +//////////////////////////////////////////////////////////////////////////// + +static void DeleteLZWNode(unsigned long p) /* deletes node p from tree */ +{ + int q; + + if (LZW_parent[p] == LZW_NIL) + return; /* not in tree */ + + if (LZW_right_child[p] == LZW_NIL) + q = LZW_left_child[p]; + else + if (LZW_left_child[p] == LZW_NIL) + q = LZW_right_child[p]; + else + { + q = LZW_left_child[p]; + if (LZW_right_child[q] != LZW_NIL) + { + do { + + q = LZW_right_child[q]; + + } while (LZW_right_child[q] != LZW_NIL); + + LZW_right_child[LZW_parent[q]] = LZW_left_child[q]; + LZW_parent[LZW_left_child[q]] = LZW_parent[q]; + LZW_left_child[q] = LZW_left_child[p]; + LZW_parent[LZW_left_child[p]] = q; + } + + LZW_right_child[q] = LZW_right_child[p]; + LZW_parent[LZW_right_child[p]] = q; + } + + LZW_parent[q] = LZW_parent[p]; + if (LZW_right_child[LZW_parent[p]] == p) + LZW_right_child[LZW_parent[p]] = q; + else + LZW_left_child[LZW_parent[p]] = q; + + LZW_parent[p] = LZW_NIL; +} + + + +//-------------------------------------------------------------------------- +// +// lzwCompress() - Compresses data from an input ptr to a dest ptr +// +// PARAMS: +// infile - Pointer at the BEGINNING of the data to compress +// outfile - Pointer to the destination (no header). +// DataLength - Number of bytes to compress. +// PtrTypes - Type of pointers being used (SRC_FILE,DEST_FILE,SRC_MEM etc). +// +// RETURNS: +// Length of compressed data. +// +// COMPTYPE : ct_LZW +// +// NOTES : Does not write ANY header information! +// +unsigned long lzwCompress(void far *infile, void far *outfile,unsigned long DataLength,unsigned PtrTypes) +{ + short i; + short c, len, r, s, last_LZW_match_len, code_buf_ptr; + unsigned char code_buf[17], mask; + unsigned long complen = 0; + + unsigned CodeCount = 0; + unsigned long OrgDataLen = DataLength; + + // initialize trees + + InitLZWTree(); + + code_buf[0] = 0; + + // + // code_buf[1..16] saves eight units of code, and code_buf[0] works + // as eight flags, "1" representing that the unit is an unencoded + // letter (1 byte), "0" a position-and-length pair (2 bytes). Thus, + // eight units require at most 16 bytes of code. + // + + code_buf_ptr = mask = 1; + s = 0; + r = LZW_N - LZW_F; + + // Clear the buffer with any character that will appear often. + // + + for (i = s; i < r; i++) + LZW_ring_buffer[i] = ' '; + + // Read LZW_F bytes into the last LZW_F bytes of the buffer + // + + for (len = 0; (len < LZW_F) && DataLength; len++) + { + c = ReadPtr((long)&infile,PtrTypes); + DataLength--; + + // text of size zero + + LZW_ring_buffer[r + len] = c; + } + + if (!(len && DataLength)) + return(0); + + // + // Insert the LZW_F strings, each of which begins with one or more + // 'space' characters. Note the order in which these strings + // are inserted. This way, degenerate trees will be less likely + // to occur. + // + + for (i = 1; i <= LZW_F; i++) + InsertLZWNode(r - i); + + // + // Finally, insert the whole string just read. The global + // variables LZW_match_len and LZW_match_pos are set. */ + // + + InsertLZWNode(r); + + do { + // LZW_match_len may be spuriously long near the end of text. + // + + if (LZW_match_len > len) + LZW_match_len = len; + + if (LZW_match_len <= LZW_THRESHOLD) + { + // Not long enough match. Send one byte. + // + + LZW_match_len = 1; + + // 'send one byte' flag + // + + code_buf[0] |= mask; + + // Send uncoded. + // + + code_buf[code_buf_ptr++] = LZW_ring_buffer[r]; + } + else + { + code_buf[code_buf_ptr++] = (unsigned char) LZW_match_pos; + code_buf[code_buf_ptr++] = (unsigned char) (((LZW_match_pos >> 4) & 0xf0) | (LZW_match_len - (LZW_THRESHOLD + 1))); + + // Send position and length pair. + // Note LZW_match_len > LZW_THRESHOLD. + } + + if ((mask <<= 1) == 0) + { + // Shift mask left one bit. + // Send at most 8 units of data + + for (i = 0; i < code_buf_ptr; i++) + WritePtr((long)&outfile,code_buf[i],PtrTypes); + + complen += code_buf_ptr; + code_buf[0] = 0; + code_buf_ptr = mask = 1; + } + + last_LZW_match_len = LZW_match_len; + + for (i = 0; i < last_LZW_match_len && DataLength; i++) + { + c = ReadPtr((long)&infile,PtrTypes); + DataLength--; + + DeleteLZWNode(s); // Delete old strings and + LZW_ring_buffer[s] = c; // read new bytes + + // If the position is near the end of buffer, extend the + // buffer to make string comparison easier. + + if (s < LZW_F - 1) + LZW_ring_buffer[s + LZW_N] = c; + + // Since this is a ring buffer, inc the position modulo LZW_N. + // + + s = (s + 1) & (LZW_N - 1); + r = (r + 1) & (LZW_N - 1); + + // Register the string in LZW_ring_buffer[r..r+LZW_F-1] + // + + InsertLZWNode(r); + } + + + // + // MANAGE DISPLAY VECTOR + // + + if (LZW_CompressDisplayVector) + { + // Update display every 1k! + // + + if ((CodeCount += i) > 1024) + { + LZW_CompressDisplayVector(OrgDataLen,OrgDataLen-DataLength); + CodeCount = 0; + } + } + + + // + // Manage Compression tree.. + // + + while (i++ < last_LZW_match_len) + { + // After the end of text, + DeleteLZWNode(s); // no need to read, but + + s = (s + 1) & (LZW_N - 1); + r = (r + 1) & (LZW_N - 1); + + if (--len) + InsertLZWNode(r); // buffer may not be empty. + } + + } while (len > 0); // until length of string to be processed is zero + + + if (code_buf_ptr > 1) + { + // Send remaining code. + // + + for (i = 0; i < code_buf_ptr; i++) + WritePtr((long)&outfile,code_buf[i],PtrTypes); + + complen += code_buf_ptr; + } + + if (LZW_CompressDisplayVector) + { + if ((CodeCount += i) > 1024) + { + LZW_CompressDisplayVector(OrgDataLen,OrgDataLen-DataLength); + } + } + + return(complen); +} + +#endif + + + + +//============================================================================ +// +// SUPPORT ROUTINES FOR LZW DECOMPRESSION +// +//============================================================================ + +#if INCLUDE_LZW_DECOMP + +//-------------------------------------------------------------------------- +// +// lzwDecompress() - Compresses data from an input ptr to a dest ptr +// +// PARAMS: +// infile - Pointer at the BEGINNING of the compressed data (no header!) +// outfile - Pointer to the destination. +// DataLength - Number of bytes to decompress. +// PtrTypes - Type of pointers being used (SRC_FILE,DEST_FILE,SRC_MEM etc). +// +// RETURNS: +// Length of compressed data. +// +// COMPTYPE : ct_LZW +// +// NOTES : Does not write ANY header information! +// +void lzwDecompress(void far *infile, void far *outfile,unsigned long DataLength,unsigned PtrTypes) +{ + int i, j, k, r, c; + unsigned int flags; + unsigned char Buffer[8]; +// unsigned char LZW_ring_buffer[LZW_N + LZW_F - 1]; + + unsigned CodeCount = 0; + unsigned long OrgDataLen = DataLength; + + for (i = 0; i < LZW_N - LZW_F; i++) + LZW_ring_buffer[i] = ' '; + + r = LZW_N - LZW_F; + flags = 0; + + for ( ; ; ) + { + if (((flags >>= 1) & 256) == 0) + { + c = ReadPtr((long)&infile,PtrTypes); + + flags = c | 0xff00; // uses higher byte cleverly to count 8 + } + + if (flags & 1) + { + c = ReadPtr((long)&infile,PtrTypes); // Could test for EOF iff FFILE type + + WritePtr((long)&outfile,c,PtrTypes); + + if (!--DataLength) + return; + + LZW_ring_buffer[r++] = c; + r &= (LZW_N - 1); + } + else + { + i = ReadPtr((long)&infile,PtrTypes); + + j = ReadPtr((long)&infile,PtrTypes); + + i |= ((j & 0xf0) << 4); + j = (j & 0x0f) + LZW_THRESHOLD; + + for (k = 0; k <= j; k++) + { + c = LZW_ring_buffer[(i + k) & (LZW_N - 1)]; + + WritePtr((long)&outfile,c,PtrTypes); + + if (!--DataLength) + return; + + LZW_ring_buffer[r++] = c; + r &= (LZW_N - 1); + } + } + + + + // + // MANAGE DISPLAY VECTOR + // + + if (LZW_DecompressDisplayVector) + { + // + // Update DisplayVector every 1K + // + + if ((CodeCount+=k) > 1024) + { + LZW_DecompressDisplayVector(OrgDataLen,OrgDataLen-DataLength); + CodeCount = 0; + } + } + + } +} + +#endif + diff --git a/16/cawat/LZW.H b/16/cawat/LZW.H new file mode 100644 index 00000000..6279e257 --- /dev/null +++ b/16/cawat/LZW.H @@ -0,0 +1,40 @@ +/* Catacomb Armageddon Source Code + * Copyright (C) 1993-2014 Flat Rock Software + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +//-------------------------------------------------------------------------- +// +// EXTERN DEFINITIONS FOR DISPLAY VEC ROUTINES +// +//-------------------------------------------------------------------------- + + +extern void (*LZW_CompressDisplayVector)(); +extern void (*LZW_DecompressDisplayVector)(); + + +//--------------------------------------------------------------------------- +// +// FUNCTION PROTOTYPEING +// +//--------------------------------------------------------------------------- + + + +unsigned long lzwCompress(void far *infile, void far *outfile,unsigned long DataLength,unsigned PtrTypes); +void lzwDecompress(void far *infile, void far *outfile,unsigned long DataLength,unsigned PtrTypes); + diff --git a/16/cawat/MAPSARM.H b/16/cawat/MAPSARM.H new file mode 100644 index 00000000..b1de5410 --- /dev/null +++ b/16/cawat/MAPSARM.H @@ -0,0 +1,53 @@ +/* Catacomb Armageddon Source Code + * Copyright (C) 1993-2014 Flat Rock Software + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/////////////////////////////////////// +// +// TED5 Map Header for ARM +// +/////////////////////////////////////// + +// +// Map Names +// +typedef enum { + TOWN_MORBIDITY_MAP, // 0 + DARK_FOREST_MAP, // 1 + GARDEN_OF_SOULS_MAP, // 2 + LOST_CITY_DAMND_MAP, // 3 + TEMPLE_OF_VIPER_MAP, // 4 + TORTURE_CHAMBER_MAP, // 5 + DEMONS_HOLD_MAP, // 6 + COLONY_FIRE_ANT_MAP, // 7 + HALL_WRETCH_POX_MAP, // 8 + LAIR_OF_SUCUBUS_MAP, // 9 + BLOOD_CHAMB_EYE_MAP, // 10 + FLAMING_INFERNO_MAP, // 11 + SUBTERR_RIVER_MAP, // 12 + CRYSTAL_MAZE_MG_MAP, // 13 + RAMPARTS_OF_NEM_MAP, // 14 + FORTRESS_OF_NEM_MAP, // 15 + PASSAGE_TO_SURF_MAP, // 16 + LASTMAP + } mapnames; + +// +// TILEINFO offsets +// +#define ANIM 402 +#define FLAGS (ANIM+NUMTILE16) diff --git a/16/cawat/README.md b/16/cawat/README.md new file mode 100644 index 00000000..680f55ac --- /dev/null +++ b/16/cawat/README.md @@ -0,0 +1,16 @@ +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` diff --git a/16/cawat/SL_FILE.H b/16/cawat/SL_FILE.H new file mode 100644 index 00000000..24849b1a --- /dev/null +++ b/16/cawat/SL_FILE.H @@ -0,0 +1,109 @@ +/* Catacomb Armageddon Source Code + * Copyright (C) 1993-2014 Flat Rock Software + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef _SL_FILE_H +#define _SL_FILE_H + + +//========================================================================== +// +// DEFINES +// +//========================================================================== + +#ifndef MakeID +#define MakeID(a,b,c,d) (((long)(d)<<24L)|((long)(c)<<16L)|((long)(b)<<8L)|(long)(a)) +#endif + + +#define ID_SLIB MakeID('S','L','I','B') +#define SLIB ("SLIB") +#define SOFTLIB_VER 2 +#define ID_CHUNK MakeID('C','U','N','K') + + + +//========================================================================== +// +// TYPES +// +//========================================================================== + + + + +//-------------------------------------------------------------------------- +// SOFTLIB File Entry Types +//-------------------------------------------------------------------------- +typedef enum LibFileTypes +{ + lib_DATA =0, // Just streight raw data +// lib_AUDIO, // Multi chunk audio file + +} LibFileTypes; + + + +//-------------------------------------------------------------------------- +// SOFTLIB Library File header.. +// +// * This header will NEVER change! * +//-------------------------------------------------------------------------- + +typedef struct SoftLibHdr +{ + unsigned Version; // Library Version Num + unsigned FileCount; +} SoftlibHdr; + + + +//-------------------------------------------------------------------------- +// SOFTLIB Directory Entry Hdr +// +// This can change according to Version of SoftLib (Make sure we are +// always downward compatable! +//-------------------------------------------------------------------------- + +#define SL_FILENAMESIZE 16 + +typedef struct FileEntryHdr +{ + char FileName[SL_FILENAMESIZE]; // NOTE : May not be null terminated! + unsigned long Offset; + unsigned long ChunkLen; + unsigned long OrginalLength; + short Compression; // ct_TYPES +} FileEntryHdr; + + + +//-------------------------------------------------------------------------- +// SOFTLIB Entry Chunk Header +//-------------------------------------------------------------------------- + +typedef struct ChunkHeader +{ + unsigned long HeaderID; + unsigned long OrginalLength; + short Compression; // ct_TYPES +} ChunkHeader; + + + +#endif \ No newline at end of file diff --git a/16/cawat/SOFT.C b/16/cawat/SOFT.C new file mode 100644 index 00000000..f86481e4 --- /dev/null +++ b/16/cawat/SOFT.C @@ -0,0 +1,479 @@ +/* Catacomb Armageddon Source Code + * Copyright (C) 1993-2014 Flat Rock Software + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "def.h" +#include "gelib.h" +#include "soft.h" +#include "lzw.h" +#include "lzhuff.h" +#include "jam_io.h" + + + + + + +//=========================================================================== +// +// SWITCHES +// +//=========================================================================== + +#define LZH_SUPPORT 1 +#define LZW_SUPPORT 0 + + + + +//========================================================================= +// +// +// GENERAL LOAD ROUTINES +// +// +//========================================================================= + + + +//-------------------------------------------------------------------------- +// BLoad() -- THIS HAS NOT BEEN FULLY TESTED! +// +// NOTICE : This version of BLOAD is compatable with JAMPak V3.0 and the +// new fileformat... +//-------------------------------------------------------------------------- +unsigned long BLoad(char *SourceFile, memptr *DstPtr) +{ + int handle; + + memptr SrcPtr; + unsigned long i, j, k, r, c; + word flags; + byte Buffer[8]; + unsigned long SrcLen,DstLen; + struct CMP1Header CompHeader; + boolean Compressed = false; + + + memset((void *)&CompHeader,0,sizeof(struct CMP1Header)); + + // + // Open file to load.... + // + + if ((handle = open(SourceFile, O_RDONLY|O_BINARY)) == -1) + return(0); + + // + // Look for JAMPAK headers + // + + read(handle,Buffer,4); + + if (!strncmp(Buffer,COMP,4)) + { + // + // Compressed under OLD file format + // + + Compressed = true; + SrcLen = Verify(SourceFile); + + read(handle,(void *)&CompHeader.OrginalLen,4); + CompHeader.CompType = ct_LZW; + MM_GetPtr(DstPtr,CompHeader.OrginalLen); + if (!*DstPtr) + return(0); + } + else + if (!strncmp(Buffer,CMP1,4)) + { + // + // Compressed under new file format... + // + + Compressed = true; + SrcLen = Verify(SourceFile); + + read(handle,(void *)&CompHeader,sizeof(struct CMP1Header)); + MM_GetPtr(DstPtr,CompHeader.OrginalLen); + if (!*DstPtr) + return(0); + } + else + DstLen = Verify(SourceFile); + + + // + // Load the file in memory... + // + + if (Compressed) + { + DstLen = CompHeader.OrginalLen; + + if ((MM_TotalFree() < SrcLen) && (CompHeader.CompType)) + { + if (!InitBufferedIO(handle,&lzwBIO)) + TrashProg("No memory for buffered I/O."); + + switch (CompHeader.CompType) + { + #if LZW_SUPPORT + case ct_LZW: + lzwDecompress(&lzwBIO,MK_FP(*DstPtr,0),CompHeader.OrginalLen,(SRC_BFILE|DEST_MEM)); + break; + #endif + + #if LZH_SUPPORT + case ct_LZH: + lzhDecompress(&lzwBIO,MK_FP(*DstPtr,0),CompHeader.OrginalLen,CompHeader.CompressLen,(SRC_BFILE|DEST_MEM)); + break; + #endif + + default: + TrashProg("BLoad() - Unrecognized/Supported compression"); + break; + } + + FreeBufferedIO(&lzwBIO); + } + else + { + CA_LoadFile(SourceFile,&SrcPtr); + switch (CompHeader.CompType) + { + #if LZW_SUPPORT + case ct_LZW: + lzwDecompress(MK_FP(SrcPtr,8),MK_FP(*DstPtr,0),CompHeader.OrginalLen,(SRC_MEM|DEST_MEM)); + break; + #endif + + #if LZH_SUPPORT + case ct_LZH: + lzhDecompress(MK_FP(SrcPtr,8),MK_FP(*DstPtr,0),CompHeader.OrginalLen,CompHeader.CompressLen,(SRC_MEM|DEST_MEM)); + break; + #endif + + default: + TrashProg("BLoad() - Unrecognized/Supported compression"); + break; + } + MM_FreePtr(&SrcPtr); + } + } + else + CA_LoadFile(SourceFile,DstPtr); + + close(handle); + return(DstLen); +} + + + + +//////////////////////////////////////////////////////////////////////////// +// +// LoadLIBShape() +// +int LoadLIBShape(char *SLIB_Filename, char *Filename,struct Shape *SHP) +{ + #define CHUNK(Name) (*ptr == *Name) && \ + (*(ptr+1) == *(Name+1)) && \ + (*(ptr+2) == *(Name+2)) && \ + (*(ptr+3) == *(Name+3)) + + + int RT_CODE; + FILE *fp; + char CHUNK[5]; + char far *ptr; + memptr IFFfile = NULL; + unsigned long FileLen, size, ChunkLen; + int loop; + + + RT_CODE = 1; + + // Decompress to ram and return ptr to data and return len of data in + // passed variable... + + if (!LoadLIBFile(SLIB_Filename,Filename,&IFFfile)) + TrashProg("Error Loading Compressed lib shape!"); + + // Evaluate the file + // + ptr = MK_FP(IFFfile,0); + if (!CHUNK("FORM")) + goto EXIT_FUNC; + ptr += 4; + + FileLen = *(long far *)ptr; + SwapLong((long far *)&FileLen); + ptr += 4; + + if (!CHUNK("ILBM")) + goto EXIT_FUNC; + ptr += 4; + + FileLen += 4; + while (FileLen) + { + ChunkLen = *(long far *)(ptr+4); + SwapLong((long far *)&ChunkLen); + ChunkLen = (ChunkLen+1) & 0xFFFFFFFE; + + if (CHUNK("BMHD")) + { + ptr += 8; + SHP->bmHdr.w = ((struct BitMapHeader far *)ptr)->w; + SHP->bmHdr.h = ((struct BitMapHeader far *)ptr)->h; + SHP->bmHdr.x = ((struct BitMapHeader far *)ptr)->x; + SHP->bmHdr.y = ((struct BitMapHeader far *)ptr)->y; + SHP->bmHdr.d = ((struct BitMapHeader far *)ptr)->d; + SHP->bmHdr.trans = ((struct BitMapHeader far *)ptr)->trans; + SHP->bmHdr.comp = ((struct BitMapHeader far *)ptr)->comp; + SHP->bmHdr.pad = ((struct BitMapHeader far *)ptr)->pad; + SwapWord(&SHP->bmHdr.w); + SwapWord(&SHP->bmHdr.h); + SwapWord(&SHP->bmHdr.x); + SwapWord(&SHP->bmHdr.y); + ptr += ChunkLen; + } + else + if (CHUNK("BODY")) + { + ptr += 4; + size = *((long far *)ptr); + ptr += 4; + SwapLong((long far *)&size); + SHP->BPR = (SHP->bmHdr.w+7) >> 3; + MM_GetPtr(&SHP->Data,size); + if (!SHP->Data) + goto EXIT_FUNC; + movedata(FP_SEG(ptr),FP_OFF(ptr),FP_SEG(SHP->Data),0,size); + ptr += ChunkLen; + + break; + } + else + ptr += ChunkLen+8; + + FileLen -= ChunkLen+8; + } + + RT_CODE = 0; + +EXIT_FUNC:; + if (IFFfile) + { +// segptr = (memptr)FP_SEG(IFFfile); + MM_FreePtr(&IFFfile); + } + + return (RT_CODE); +} + + + + + +//---------------------------------------------------------------------------- +// LoadLIBFile() -- Copies a file from an existing archive to dos. +// +// PARAMETERS : +// +// LibName - Name of lib file created with SoftLib V1.0 +// +// FileName - Name of file to load from lib file. +// +// MemPtr - (IF !NULL) - Pointer to memory to load into .. +// (IF NULL) - Routine allocates necessary memory and +// returns a MEM(SEG) pointer to memory allocated. +// +// RETURN : +// +// (IF !NULL) - A pointer to the loaded data. +// (IF NULL) - Error! +// +//---------------------------------------------------------------------------- +memptr LoadLIBFile(char *LibName,char *FileName,memptr *MemPtr) +{ + int handle; + unsigned long header; + struct ChunkHeader Header; + unsigned long ChunkLen; + short x; + struct FileEntryHdr FileEntry; // Storage for file once found + struct FileEntryHdr FileEntryHeader; // Header used durring searching + struct SoftLibHdr LibraryHeader; // Library header - Version Checking + boolean FileFound = false; + unsigned long id_slib = ID_SLIB; + unsigned long id_chunk = ID_CHUNK; + + + // + // OPEN SOFTLIB FILE + // + + if ((handle = open(LibName,O_RDONLY|O_BINARY, S_IREAD)) == -1) + return(NULL); + + + // + // VERIFY it is a SOFTLIB (SLIB) file + // + + if (read(handle,&header,4) == -1) + { + close(handle); + return(NULL); + } + + if (header != id_slib) + { + close(handle); + return(NULL); + } + + + // + // CHECK LIBRARY HEADER VERSION NUMBER + // + + if (read(handle, &LibraryHeader,sizeof(struct SoftLibHdr)) == -1) + TrashProg("read error in LoadSLIBFile()\n%c",7); + + if (LibraryHeader.Version > SOFTLIB_VER) + TrashProg("Unsupported file ver %d",LibraryHeader.Version); + + + // + // MANAGE FILE ENTRY HEADERS... + // + + for (x = 1;x<=LibraryHeader.FileCount;x++) + { + if (read(handle, &FileEntryHeader,sizeof(struct FileEntryHdr)) == -1) + { + close(handle); + return(NULL); + } + + if (!stricmp(FileEntryHeader.FileName,FileName)) + { + FileEntry = FileEntryHeader; + FileFound = true; + } + } + + // + // IF FILE HAS BEEN FOUND THEN SEEK TO POSITION AND EXTRACT + // ELSE RETURN WITH ERROR CODE... + // + + if (FileFound) + { + if (lseek(handle,FileEntry.Offset,SEEK_CUR) == -1) + { + close(handle); + return(NULL); + } + + // + // READ CHUNK HEADER - Verify we are at the beginning of a chunk.. + // + + if (read(handle,(char *)&Header,sizeof(struct ChunkHeader)) == -1) + TrashProg("LIB File - Unable to read Header!"); + + if (Header.HeaderID != id_chunk) + TrashProg("LIB File - BAD HeaderID!"); + + // + // Allocate memory if Necessary... + // + + + if (!*MemPtr) + MM_GetPtr(MemPtr,FileEntry.OrginalLength); + + // + // Calculate the length of the data (without the chunk header). + // + + ChunkLen = FileEntry.ChunkLen - sizeof(struct ChunkHeader); + + + // + // Extract Data from file + // + + switch (Header.Compression) + { + + #if LZW_SUPPORT + case ct_LZW: + if (!InitBufferedIO(handle,&lzwBIO)) + TrashProg("No memory for buffered I/O."); + lzwDecompress(&lzwBIO,MK_FP(*MemPtr,0),FileEntry.OrginalLength,(SRC_BFILE|DEST_MEM)); + FreeBufferedIO(&lzwBIO); + break; + #endif + + #if LZH_SUPPORT + case ct_LZH: + if (!InitBufferedIO(handle,&lzwBIO)) + TrashProg("No memory for buffered I/O."); + lzhDecompress(&lzwBIO, MK_FP(*MemPtr,0), FileEntry.OrginalLength, ChunkLen, (SRC_BFILE|DEST_MEM)); + FreeBufferedIO(&lzwBIO); + break; + #endif + + case ct_NONE: + if (!CA_FarRead(handle,MK_FP(*MemPtr,0),ChunkLen)) + { +// close(handle); + *MemPtr = NULL; + } + break; + + default: + close(handle); + TrashProg("Unknown Chunk.Compression Type!"); + break; + } + } + else + *MemPtr = NULL; + + close(handle); + return(*MemPtr); +} + + + + diff --git a/16/cawat/SOFT.H b/16/cawat/SOFT.H new file mode 100644 index 00000000..2226de67 --- /dev/null +++ b/16/cawat/SOFT.H @@ -0,0 +1,25 @@ +/* Catacomb Armageddon Source Code + * Copyright (C) 1993-2014 Flat Rock Software + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +//memptr InitBufferedIO(int handle, BufferedIO *bio); +//void FreeBufferedIO(BufferedIO *bio); +//byte bio_readch(BufferedIO *bio); + +unsigned long BLoad(char *SourceFile, memptr *DstPtr); +memptr LoadLIBFile(char *LibName,char *FileName,memptr *MemPtr); +int LoadLIBShape(char *SLIB_Filename, char *Filename,struct Shape *SHP); diff --git a/16/cawat/TC0000.SWP b/16/cawat/TC0000.SWP new file mode 100644 index 0000000000000000000000000000000000000000..d2453e01ac81f7422cb25ebe807565f8a7e561b2 GIT binary patch literal 262144 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 literal 0 HcmV?d00001 diff --git a/16/cawat/TD.TR b/16/cawat/TD.TR new file mode 100644 index 0000000000000000000000000000000000000000..2e7787f7dac88de93cd892fc7285c18e32526401 GIT binary patch literal 65 zcmWG3ElSE)a7j%nO;1lPQYcC-E=epZQAo?oNtH@2S1T&#W|U%JWYE$}tw?3~|DTZo J0hpLTYyhe45B>lE literal 0 HcmV?d00001 diff --git a/16/cawat/TDCONFIG.TD b/16/cawat/TDCONFIG.TD new file mode 100644 index 0000000000000000000000000000000000000000..dfbed304bce0cf5226b6dc005bda89b61c6150fe GIT binary patch literal 691 zcmbu6Pfx-?5XI*eWQf%+i3bx;JsB@1#H;^6iYE+-AsSBx5U`50HWKdo5&R;4LwsAI z;ZOA9^sqB;-*kSv+ZrdG$Pap*@nFzP{BAVrhl6p_ehZ_K-w%hqz3yZ`nYhS_jZYC( zHT)!I8LL_q%0y#hB?fZ}SaM?KToUK)OX8o-^+Bq@49m_m!ECWYH^SqgI*{{f^#S$Y@^x&D;FQuaEIuwnklFOS?Y@NuQYA|nK5zD1jXK~$G z6x+Ty$dw)Wx`YMckirqyJgY5Y5rdh1>2!Nrc3FY)h7@vwG@R7r$pKxBX06)bn#Fbf zs@bY%B@;f5k$}lD9?`-HLfqpNXE;X%Ra~HkCp=?_7mN@gMjIVGAiyi`aEK4QL;M@u L63AZ3bSm-, or + * write to the Free Software Foundation, Inc., 51 Franklin Street, + * Fifth Floor, Boston, MA 02110-1301 USA. + * + */ +/* + * Just some handy typedefs that make it easier to think about the low + * level code + */ + +#ifndef _LIB_HEAD_H_ +#define _LIB_HEAD_H_ + +#include +#include +//#include "" +//#include "" +//#include "ID_CA.H" +//#include "AUDIOARM.H" +#include "types.h" + +#endif/*_LIB_HEAD_H_*/ diff --git a/16/cawat/port.h b/16/cawat/port.h new file mode 100644 index 00000000..1ada6a5e --- /dev/null +++ b/16/cawat/port.h @@ -0,0 +1,135 @@ +/*====================================================================== + 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 diff --git a/16/cawat/types.h b/16/cawat/types.h new file mode 100644 index 00000000..51b7d917 --- /dev/null +++ b/16/cawat/types.h @@ -0,0 +1,14 @@ +#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 -- 2.39.5