]> 4ch.mooo.com Git - 16.git/commitdiff
extrcted keen code remake
authorsparky4 <sparky4@cock.li>
Sat, 7 Oct 2023 20:28:13 +0000 (15:28 -0500)
committersparky4 <sparky4@cock.li>
Sat, 7 Oct 2023 20:28:13 +0000 (15:28 -0500)
116 files changed:
16/keen456/KEEN4-6/-ID_VW_AE.ASM [new file with mode: 0755]
16/keen456/KEEN4-6/CK_DEF.H [new file with mode: 0755]
16/keen456/KEEN4-6/CK_DEMO.C [new file with mode: 0755]
16/keen456/KEEN4-6/CK_GAME.C [new file with mode: 0755]
16/keen456/KEEN4-6/CK_KEEN.C [new file with mode: 0755]
16/keen456/KEEN4-6/CK_KEEN2.C [new file with mode: 0755]
16/keen456/KEEN4-6/CK_MAIN.C [new file with mode: 0755]
16/keen456/KEEN4-6/CK_PLAY.C [new file with mode: 0755]
16/keen456/KEEN4-6/CK_STATE.C [new file with mode: 0755]
16/keen456/KEEN4-6/CK_TEXT.C [new file with mode: 0755]
16/keen456/KEEN4-6/COPYING [new file with mode: 0755]
16/keen456/KEEN4-6/ID_CA.C [new file with mode: 0755]
16/keen456/KEEN4-6/ID_CA.H [new file with mode: 0755]
16/keen456/KEEN4-6/ID_IN.C [new file with mode: 0755]
16/keen456/KEEN4-6/ID_IN.H [new file with mode: 0755]
16/keen456/KEEN4-6/ID_MM.C [new file with mode: 0755]
16/keen456/KEEN4-6/ID_MM.H [new file with mode: 0755]
16/keen456/KEEN4-6/ID_RF.C [new file with mode: 0755]
16/keen456/KEEN4-6/ID_RF.H [new file with mode: 0755]
16/keen456/KEEN4-6/ID_RF_A.ASM [new file with mode: 0755]
16/keen456/KEEN4-6/ID_SD.C [new file with mode: 0755]
16/keen456/KEEN4-6/ID_SD.H [new file with mode: 0755]
16/keen456/KEEN4-6/ID_US.H [new file with mode: 0755]
16/keen456/KEEN4-6/ID_US_1.C [new file with mode: 0755]
16/keen456/KEEN4-6/ID_US_2.C [new file with mode: 0755]
16/keen456/KEEN4-6/ID_US_A.ASM [new file with mode: 0755]
16/keen456/KEEN4-6/ID_VW.C [new file with mode: 0755]
16/keen456/KEEN4-6/ID_VW.H [new file with mode: 0755]
16/keen456/KEEN4-6/ID_VW_A.ASM [new file with mode: 0755]
16/keen456/KEEN4-6/ID_VW_AC.ASM [new file with mode: 0755]
16/keen456/KEEN4-6/ID_VW_AE.ASM [new file with mode: 0755]
16/keen456/KEEN4-6/KEEN4/AUDIOCK4.H [new file with mode: 0755]
16/keen456/KEEN4-6/KEEN4/GFXE_CK4.EQU [new file with mode: 0755]
16/keen456/KEEN4-6/KEEN4/GFXE_CK4.H [new file with mode: 0755]
16/keen456/KEEN4-6/KEEN4/ID_ASM.EQU [new file with mode: 0755]
16/keen456/KEEN4-6/KEEN4/ID_HEADS.H [new file with mode: 0755]
16/keen456/KEEN4-6/KEEN4/K4_ACT1.C [new file with mode: 0755]
16/keen456/KEEN4-6/KEEN4/K4_ACT2.C [new file with mode: 0755]
16/keen456/KEEN4-6/KEEN4/K4_ACT3.C [new file with mode: 0755]
16/keen456/KEEN4-6/KEEN4/K4_DEF.H [new file with mode: 0755]
16/keen456/KEEN4-6/KEEN4/K4_SPEC.C [new file with mode: 0755]
16/keen456/KEEN4-6/KEEN4C/GFXC_CK4.EQU [new file with mode: 0755]
16/keen456/KEEN4-6/KEEN4C/GFXC_CK4.H [new file with mode: 0755]
16/keen456/KEEN4-6/KEEN4C/ID_ASM.EQU [new file with mode: 0755]
16/keen456/KEEN4-6/KEEN4C/ID_HEADS.H [new file with mode: 0755]
16/keen456/KEEN4-6/KEEN5/AUDIOCK5.H [new file with mode: 0755]
16/keen456/KEEN4-6/KEEN5/GFXE_CK5.EQU [new file with mode: 0755]
16/keen456/KEEN4-6/KEEN5/GFXE_CK5.H [new file with mode: 0755]
16/keen456/KEEN4-6/KEEN5/ID_ASM.EQU [new file with mode: 0755]
16/keen456/KEEN4-6/KEEN5/ID_HEADS.H [new file with mode: 0755]
16/keen456/KEEN4-6/KEEN5/K5_ACT1.C [new file with mode: 0755]
16/keen456/KEEN4-6/KEEN5/K5_ACT2.C [new file with mode: 0755]
16/keen456/KEEN4-6/KEEN5/K5_ACT3.C [new file with mode: 0755]
16/keen456/KEEN4-6/KEEN5/K5_DEF.H [new file with mode: 0755]
16/keen456/KEEN4-6/KEEN5/K5_SPEC.C [new file with mode: 0755]
16/keen456/KEEN4-6/KEEN5C/GFXC_CK5.EQU [new file with mode: 0755]
16/keen456/KEEN4-6/KEEN5C/GFXC_CK5.H [new file with mode: 0755]
16/keen456/KEEN4-6/KEEN5C/ID_ASM.EQU [new file with mode: 0755]
16/keen456/KEEN4-6/KEEN5C/ID_HEADS.H [new file with mode: 0755]
16/keen456/KEEN4-6/KEEN6/AUDIOCK6.H [new file with mode: 0755]
16/keen456/KEEN4-6/KEEN6/GFXE_CK6.EQU [new file with mode: 0755]
16/keen456/KEEN4-6/KEEN6/GFXE_CK6.H [new file with mode: 0755]
16/keen456/KEEN4-6/KEEN6/ID_ASM.EQU [new file with mode: 0755]
16/keen456/KEEN4-6/KEEN6/ID_HEADS.H [new file with mode: 0755]
16/keen456/KEEN4-6/KEEN6/K6_ACT1.C [new file with mode: 0755]
16/keen456/KEEN4-6/KEEN6/K6_ACT2.C [new file with mode: 0755]
16/keen456/KEEN4-6/KEEN6/K6_ACT3.C [new file with mode: 0755]
16/keen456/KEEN4-6/KEEN6/K6_DEF.H [new file with mode: 0755]
16/keen456/KEEN4-6/KEEN6/K6_SPEC.C [new file with mode: 0755]
16/keen456/KEEN4-6/KEEN6/OBJ/fix_RCK6_v15.pat [new file with mode: 0755]
16/keen456/KEEN4-6/KEEN6C/GFXC_CK6.EQU [new file with mode: 0755]
16/keen456/KEEN4-6/KEEN6C/GFXC_CK6.H [new file with mode: 0755]
16/keen456/KEEN4-6/KEEN6C/ID_ASM.EQU [new file with mode: 0755]
16/keen456/KEEN4-6/KEEN6C/ID_HEADS.H [new file with mode: 0755]
16/keen456/KEEN4-6/KEEN6C/OBJ/fix_RCK6C_v15.pat [new file with mode: 0755]
16/keen456/KEEN4-6/RCK4.DSK [new file with mode: 0755]
16/keen456/KEEN4-6/RCK4.PRJ [new file with mode: 0755]
16/keen456/KEEN4-6/RCK4C.DSK [new file with mode: 0755]
16/keen456/KEEN4-6/RCK4C.PRJ [new file with mode: 0755]
16/keen456/KEEN4-6/RCK4GT.DSK [new file with mode: 0755]
16/keen456/KEEN4-6/RCK4GT.PRJ [new file with mode: 0755]
16/keen456/KEEN4-6/RCK5.DSK [new file with mode: 0755]
16/keen456/KEEN4-6/RCK5.PRJ [new file with mode: 0755]
16/keen456/KEEN4-6/RCK5C.DSK [new file with mode: 0755]
16/keen456/KEEN4-6/RCK5C.PRJ [new file with mode: 0755]
16/keen456/KEEN4-6/RCK5GT.DSK [new file with mode: 0755]
16/keen456/KEEN4-6/RCK5GT.PRJ [new file with mode: 0755]
16/keen456/KEEN4-6/RCK6.DSK [new file with mode: 0755]
16/keen456/KEEN4-6/RCK6.PRJ [new file with mode: 0755]
16/keen456/KEEN4-6/RCK6C.DSK [new file with mode: 0755]
16/keen456/KEEN4-6/RCK6C.PRJ [new file with mode: 0755]
16/keen456/KEEN4-6/RCK6C15.DSK [new file with mode: 0755]
16/keen456/KEEN4-6/RCK6C15.PRJ [new file with mode: 0755]
16/keen456/KEEN4-6/RCK6E15.DSK [new file with mode: 0755]
16/keen456/KEEN4-6/RCK6E15.PRJ [new file with mode: 0755]
16/keen456/KEEN4-6/static/MAKEOBJ.EXE [new file with mode: 0755]
16/keen456/KEEN4-6/static/make.bat [new file with mode: 0755]
16/keen456/KEEN4-6/static/makeobj.c [new file with mode: 0755]
16/keen456/KEEN4-6/static/ripck4.pat [new file with mode: 0755]
16/keen456/KEEN4-6/static/ripck5.pat [new file with mode: 0755]
16/keen456/KEEN4-6/static/ripck6.pat [new file with mode: 0755]
16/keen456/KEEN4-6/timeline.txt [new file with mode: 0755]
16/keen456/LZEXE.EXE [new file with mode: 0755]
16/keen456/RCK4.BAT [new file with mode: 0755]
16/keen456/RCK4C.BAT [new file with mode: 0755]
16/keen456/RCK4GT.BAT [new file with mode: 0755]
16/keen456/RCK5.BAT [new file with mode: 0755]
16/keen456/RCK5C.BAT [new file with mode: 0755]
16/keen456/RCK5GT.BAT [new file with mode: 0755]
16/keen456/RCK6.BAT [new file with mode: 0755]
16/keen456/RCK6C.BAT [new file with mode: 0755]
16/keen456/RCK6C15.BAT [new file with mode: 0755]
16/keen456/RCK6E15.BAT [new file with mode: 0755]
16/keen456/cleanup.bat [new file with mode: 0755]
16/keen456/readme.txt [new file with mode: 0755]
16/keen456/ripnmake.bat [new file with mode: 0755]

diff --git a/16/keen456/KEEN4-6/-ID_VW_AE.ASM b/16/keen456/KEEN4-6/-ID_VW_AE.ASM
new file mode 100755 (executable)
index 0000000..4e76e86
--- /dev/null
@@ -0,0 +1,1832 @@
+; Reconstructed Commander Keen 4-6 Source Code\r
+; Copyright (C) 2021 K1n9_Duk3\r
+;\r
+; This file is primarily based on:\r
+; Catacomb 3-D Source Code\r
+; Copyright (C) 1993-2014 Flat Rock Software\r
+;\r
+; This program is free software; you can redistribute it and/or modify\r
+; it under the terms of the GNU General Public License as published by\r
+; the Free Software Foundation; either version 2 of the License, or\r
+; (at your option) any later version.\r
+;\r
+; This program is distributed in the hope that it will be useful,\r
+; but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+; GNU General Public License for more details.\r
+;\r
+; You should have received a copy of the GNU General Public License along\r
+; with this program; if not, write to the Free Software Foundation, Inc.,\r
+; 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+\r
+;=================================\r
+;\r
+; EGA view manager routines\r
+;\r
+;=================================\r
+\r
+;============================================================================\r
+;\r
+; All EGA drawing routines that write out words need to have alternate forms\r
+; for starting on even and odd addresses, because writing a word at segment\r
+; offset 0xffff causes an exception!  To work around this, write a single\r
+; byte out to make the address even, so it wraps cleanly at the end.\r
+;\r
+; All of these routines assume read/write mode 0, and will allways return\r
+; in that state.\r
+; The direction flag should be clear\r
+; readmap/writemask is left in an undefined state\r
+;\r
+;============================================================================\r
+\r
+\r
+;============================================================================\r
+;\r
+; VW_Plot (int x,y,color)\r
+;\r
+;============================================================================\r
+\r
+DATASEG\r
+\r
+plotpixels     db      128,64,32,16,8,4,2,1\r
+\r
+CODESEG\r
+\r
+PROC   VW_Plot x:WORD, y:WORD, color:WORD\r
+PUBLIC VW_Plot\r
+USES   SI,DI\r
+\r
+       mov     es,[screenseg]\r
+\r
+       mov     dx,SC_INDEX\r
+       mov     ax,SC_MAPMASK+15*256\r
+       WORDOUT\r
+\r
+       mov     dx,GC_INDEX\r
+       mov     ax,GC_MODE+2*256        ;write mode 2\r
+       WORDOUT\r
+\r
+       mov     di,[bufferofs]\r
+       mov     bx,[y]\r
+       shl     bx,1\r
+       add     di,[ylookup+bx]\r
+       mov     bx,[x]\r
+       mov     ax,bx\r
+       shr     ax,1\r
+       shr     ax,1\r
+       shr     ax,1\r
+       add     di,ax                           ; di = byte on screen\r
+\r
+       and     bx,7\r
+       mov     ah,[plotpixels+bx]\r
+       mov     al,GC_BITMASK           ;mask off other pixels\r
+       WORDOUT\r
+\r
+       mov             bl,[BYTE color]\r
+       xchg    bl,[es:di]              ; load latches and write pixel\r
+\r
+       mov     dx,GC_INDEX\r
+       mov     ah,0ffh                         ;no bit mask\r
+       WORDOUT\r
+       mov     ax,GC_MODE+0*256        ;write mode 0\r
+       WORDOUT\r
+\r
+       ret\r
+\r
+ENDP\r
+\r
+\r
+;============================================================================\r
+;\r
+; VW_Vlin (int yl,yh,x,color)\r
+;\r
+;============================================================================\r
+\r
+PROC   VW_Vlin yl:WORD, yh:WORD, x:WORD, color:WORD\r
+PUBLIC VW_Vlin\r
+USES   SI,DI\r
+\r
+       mov     es,[screenseg]\r
+\r
+       mov     dx,SC_INDEX\r
+       mov     ax,SC_MAPMASK+15*256\r
+       WORDOUT\r
+\r
+       mov     dx,GC_INDEX\r
+       mov     ax,GC_MODE+2*256        ;write mode 2\r
+       WORDOUT\r
+\r
+       mov     di,[bufferofs]\r
+       mov     bx,[yl]\r
+       shl     bx,1\r
+       add     di,[ylookup+bx]\r
+       mov     bx,[x]\r
+       mov     ax,bx\r
+       shr     ax,1\r
+       shr     ax,1\r
+       shr     ax,1\r
+       add     di,ax                           ; di = byte on screen\r
+\r
+       and     bx,7\r
+       mov     ah,[plotpixels+bx]\r
+       mov     al,GC_BITMASK           ;mask off other pixels\r
+       WORDOUT\r
+\r
+       mov     cx,[yh]\r
+       sub     cx,[yl]\r
+       inc     cx                                      ;number of pixels to plot\r
+\r
+       mov     bh,[BYTE color]\r
+       mov     dx,[linewidth]\r
+\r
+@@plot:\r
+       mov             bl,bh\r
+       xchg    bl,[es:di]              ; load latches and write pixel\r
+       add             di,dx\r
+\r
+       loop    @@plot\r
+\r
+       mov     dx,GC_INDEX\r
+       mov     ah,0ffh                         ;no bit mask\r
+       WORDOUT\r
+       mov     ax,GC_MODE+0*256        ;write mode 0\r
+       WORDOUT\r
+\r
+       ret\r
+\r
+ENDP\r
+\r
+\r
+;============================================================================\r
+\r
+\r
+;===================\r
+;\r
+; VW_DrawTile8\r
+;\r
+; xcoord in bytes (8 pixels), ycoord in pixels\r
+; All Tile8s are in one grseg, so an offset is calculated inside it\r
+;\r
+;===================\r
+\r
+PROC   VW_DrawTile8    xcoord:WORD, ycoord:WORD, tile:WORD\r
+PUBLIC VW_DrawTile8\r
+USES   SI,DI\r
+\r
+       mov     es,[screenseg]\r
+\r
+       mov     di,[bufferofs]\r
+       add     di,[xcoord]\r
+       mov     bx,[ycoord]\r
+       shl     bx,1\r
+       add     di,[ylookup+bx]\r
+       mov     [ss:screendest],di              ;screen destination\r
+\r
+       mov     bx,[linewidth]\r
+       dec     bx\r
+\r
+       mov     si,[tile]\r
+       shl     si,1\r
+       shl     si,1\r
+       shl     si,1\r
+       shl     si,1\r
+       shl     si,1\r
+\r
+       mov     ds,[grsegs+STARTTILE8*2] ; segment for all tile8s\r
+\r
+       mov     cx,4                                    ;planes to draw\r
+       mov     ah,0001b                                ;map mask\r
+\r
+       mov     dx,SC_INDEX\r
+       mov     al,SC_MAPMASK\r
+\r
+;\r
+; start drawing\r
+;\r
+\r
+@@planeloop:\r
+       WORDOUT\r
+       shl     ah,1                                    ;shift plane mask over for next plane\r
+\r
+       mov     di,[ss:screendest]              ;start at same place in all planes\r
+\r
+REPT   7\r
+       movsb\r
+       add     di,bx\r
+ENDM\r
+       movsb\r
+\r
+       loop    @@planeloop\r
+\r
+       mov     ax,ss\r
+       mov     ds,ax                                   ;restore turbo's data segment\r
+\r
+       ret\r
+\r
+ENDP\r
+\r
+\r
+;============================================================================\r
+;\r
+; VW_MaskBlock\r
+;\r
+; Draws a masked block shape to the screen.  bufferofs is NOT accounted for.\r
+; The mask comes first, then four planes of data.\r
+;\r
+;============================================================================\r
+\r
+DATASEG\r
+\r
+UNWOUNDMASKS   =       10\r
+\r
+\r
+maskroutines   dw      mask0,mask0,mask1E,mask1E,mask2E,mask2O,mask3E,mask3O\r
+                               dw      mask4E,mask4O,mask5E,mask5O,mask6E,mask6O\r
+                               dw      mask7E,mask7O,mask8E,mask8O,mask9E,mask9O\r
+                               dw      mask10E,mask10O\r
+\r
+\r
+routinetouse   dw      ?\r
+\r
+CODESEG\r
+\r
+PROC   VW_MaskBlock    segm:WORD, ofs:WORD, dest:WORD, wide:WORD, height:WORD, planesize:WORD\r
+PUBLIC VW_MaskBlock\r
+USES   SI,DI\r
+\r
+       mov     es,[screenseg]\r
+\r
+       mov     [BYTE planemask],1\r
+       mov     [BYTE planenum],0\r
+\r
+       mov     di,[wide]\r
+       mov     dx,[linewidth]\r
+       sub     dx,[wide]\r
+       mov     [linedelta],dx                  ;amount to add after drawing each line\r
+\r
+       mov     bx,[planesize]                  ; si+bx = data location\r
+\r
+       cmp     di,UNWOUNDMASKS\r
+       jbe     @@unwoundroutine\r
+       mov     [routinetouse],OFFSET generalmask\r
+       jmp     NEAR @@startloop\r
+\r
+;=================\r
+;\r
+; use the unwound routines\r
+;\r
+;=================\r
+\r
+@@unwoundroutine:\r
+       mov     cx,[dest]\r
+       shr     cx,1\r
+       rcl     di,1                                    ;shift a 1 in if destination is odd\r
+       shl     di,1                                    ;to index into a word width table\r
+       mov     ax,[maskroutines+di]    ;call the right routine\r
+       mov     [routinetouse],ax\r
+\r
+@@startloop:\r
+       mov     ds,[segm]\r
+\r
+@@drawplane:\r
+       mov     dx,SC_INDEX\r
+       mov     al,SC_MAPMASK\r
+       mov     ah,[ss:planemask]\r
+       WORDOUT\r
+       mov     dx,GC_INDEX\r
+       mov     al,GC_READMAP\r
+       mov     ah,[ss:planenum]\r
+       WORDOUT\r
+\r
+       mov     si,[ofs]                                ;start back at the top of the mask\r
+       mov     di,[dest]                               ;start at same place in all planes\r
+       mov     cx,[height]                             ;scan lines to draw\r
+       mov dx,[ss:linedelta]\r
+\r
+       jmp [ss:routinetouse]           ;draw one plane\r
+planereturn:                                   ;routine jmps back here\r
+\r
+       add     bx,[ss:planesize]               ;start of mask = start of next plane\r
+\r
+       inc     [ss:planenum]\r
+       shl     [ss:planemask],1                ;shift plane mask over for next plane\r
+       cmp     [ss:planemask],10000b   ;done all four planes?\r
+       jne     @@drawplane\r
+\r
+mask0:\r
+       mov     ax,ss\r
+       mov     ds,ax\r
+       ret                                                     ;width of 0 = no drawing\r
+\r
+;==============\r
+;\r
+; General purpose masked block drawing.  This could be optimised into\r
+; four routines to use words, but few play loop sprites should be this big!\r
+;\r
+;==============\r
+\r
+generalmask:\r
+       mov     dx,cx\r
+\r
+@@lineloopgen:\r
+       mov     cx,[wide]\r
+@@byteloop:\r
+       mov     al,[es:di]\r
+       and     al,[si]\r
+       or      al,[bx+si]\r
+       inc     si\r
+       stosb\r
+       loop    @@byteloop\r
+\r
+       add     di,[ss:linedelta]\r
+       dec     dx\r
+       jnz     @@lineloopgen\r
+       jmp     planereturn\r
+\r
+;=================\r
+;\r
+; Horizontally unwound routines to draw certain masked blocks faster\r
+;\r
+;=================\r
+\r
+MACRO  MASKBYTE\r
+       lodsb\r
+       and     al,[es:di]\r
+       or      al,[bx+si-1]\r
+       stosb\r
+ENDM\r
+\r
+MACRO  MASKWORD\r
+       lodsw\r
+       and     ax,[es:di]\r
+       or      ax,[bx+si-2]\r
+       stosw\r
+ENDM\r
+\r
+MACRO  SPRITELOOP      addr\r
+       add     di,dx\r
+       loop    addr\r
+       jmp     planereturn\r
+ENDM\r
+\r
+\r
+EVEN\r
+mask1E:\r
+       MASKBYTE\r
+       SPRITELOOP      mask1E\r
+\r
+EVEN\r
+mask2E:\r
+       MASKWORD\r
+       SPRITELOOP      mask2E\r
+\r
+EVEN\r
+mask2O:\r
+       MASKBYTE\r
+       MASKBYTE\r
+       SPRITELOOP      mask2O\r
+\r
+EVEN\r
+mask3E:\r
+       MASKWORD\r
+       MASKBYTE\r
+       SPRITELOOP      mask3E\r
+\r
+EVEN\r
+mask3O:\r
+       MASKBYTE\r
+       MASKWORD\r
+       SPRITELOOP      mask3O\r
+\r
+EVEN\r
+mask4E:\r
+       MASKWORD\r
+       MASKWORD\r
+       SPRITELOOP      mask4E\r
+\r
+EVEN\r
+mask4O:\r
+       MASKBYTE\r
+       MASKWORD\r
+       MASKBYTE\r
+       SPRITELOOP      mask4O\r
+\r
+EVEN\r
+mask5E:\r
+       MASKWORD\r
+       MASKWORD\r
+       MASKBYTE\r
+       SPRITELOOP      mask5E\r
+\r
+EVEN\r
+mask5O:\r
+       MASKBYTE\r
+       MASKWORD\r
+       MASKWORD\r
+       SPRITELOOP      mask5O\r
+\r
+EVEN\r
+mask6E:\r
+       MASKWORD\r
+       MASKWORD\r
+       MASKWORD\r
+       SPRITELOOP      mask6E\r
+\r
+EVEN\r
+mask6O:\r
+       MASKBYTE\r
+       MASKWORD\r
+       MASKWORD\r
+       MASKBYTE\r
+       SPRITELOOP      mask6O\r
+\r
+EVEN\r
+mask7E:\r
+       MASKWORD\r
+       MASKWORD\r
+       MASKWORD\r
+       MASKBYTE\r
+       SPRITELOOP      mask7E\r
+\r
+EVEN\r
+mask7O:\r
+       MASKBYTE\r
+       MASKWORD\r
+       MASKWORD\r
+       MASKWORD\r
+       SPRITELOOP      mask7O\r
+\r
+EVEN\r
+mask8E:\r
+       MASKWORD\r
+       MASKWORD\r
+       MASKWORD\r
+       MASKWORD\r
+       SPRITELOOP      mask8E\r
+\r
+EVEN\r
+mask8O:\r
+       MASKBYTE\r
+       MASKWORD\r
+       MASKWORD\r
+       MASKWORD\r
+       MASKBYTE\r
+       SPRITELOOP      mask8O\r
+\r
+EVEN\r
+mask9E:\r
+       MASKWORD\r
+       MASKWORD\r
+       MASKWORD\r
+       MASKWORD\r
+       MASKBYTE\r
+       SPRITELOOP      mask9E\r
+\r
+EVEN\r
+mask9O:\r
+       MASKBYTE\r
+       MASKWORD\r
+       MASKWORD\r
+       MASKWORD\r
+       MASKWORD\r
+       SPRITELOOP      mask9O\r
+\r
+EVEN\r
+mask10E:\r
+       MASKWORD\r
+       MASKWORD\r
+       MASKWORD\r
+       MASKWORD\r
+       MASKWORD\r
+       SPRITELOOP      mask10E\r
+\r
+EVEN\r
+mask10O:\r
+       MASKBYTE\r
+       MASKWORD\r
+       MASKWORD\r
+       MASKWORD\r
+       MASKWORD\r
+       MASKBYTE\r
+       SPRITELOOP      mask10O\r
+\r
+\r
+ENDP\r
+\r
+\r
+;============================================================================\r
+;\r
+; VW_InverseMask\r
+;\r
+; Draws a masked block shape to the screen.  bufferofs is NOT accounted for.\r
+; The mask comes first, then four planes of data.\r
+;\r
+;============================================================================\r
+\r
+PROC   VW_InverseMask  segm:WORD, ofs:WORD, dest:WORD, wide:WORD, height:WORD\r
+PUBLIC VW_InverseMask\r
+USES   SI,DI\r
+\r
+       mov     dx,SC_INDEX\r
+       mov     ax,SC_MAPMASK+15*256\r
+       WORDOUT\r
+       mov     dx,GC_INDEX\r
+       mov     ax,GC_DATAROTATE+16*256         ;set function = OR\r
+       WORDOUT\r
+\r
+       mov     es, [screenseg]\r
+       mov     ax, [wide]\r
+       mov     dx, [linewidth]\r
+       sub     dx, ax;\r
+       mov     ds, [segm]\r
+       mov     si, [ofs]\r
+       mov     di, [dest]\r
+       mov     bx, [height]\r
+@@yloop:\r
+       mov     cx, [wide]\r
+@@xloop:\r
+       lodsb\r
+       not     al\r
+       xchg    al, [es:di]\r
+       inc     di\r
+       loop    @@xloop\r
+       add     di, dx\r
+       dec     bx\r
+       jnz     @@yloop\r
+\r
+       mov     dx,GC_INDEX\r
+       mov     ax,GC_DATAROTATE+0*256          ;set function = no change\r
+       WORDOUT\r
+\r
+       mov     ax,ss\r
+       mov     ds,ax                                   ;restore turbo's data segment\r
+\r
+       ret\r
+\r
+ENDP\r
+\r
+;============================================================================\r
+;\r
+; VW_ScreenToScreen\r
+;\r
+; Basic block copy routine.  Copies one block of screen memory to another,\r
+; using write mode 1 (sets it and returns with write mode 0).  bufferofs is\r
+; NOT accounted for.\r
+;\r
+;============================================================================\r
+\r
+PROC   VW_ScreenToScreen       source:WORD, dest:WORD, wide:WORD, height:WORD\r
+PUBLIC VW_ScreenToScreen\r
+USES   SI,DI\r
+\r
+       pushf\r
+       cli\r
+\r
+       mov     dx,SC_INDEX\r
+       mov     ax,SC_MAPMASK+15*256\r
+       WORDOUT\r
+       mov     dx,GC_INDEX\r
+       mov     ax,GC_MODE+1*256\r
+       WORDOUT\r
+\r
+       popf\r
+\r
+       mov     bx,[linewidth]\r
+       sub     bx,[wide]\r
+\r
+       mov     ax,[screenseg]\r
+       mov     es,ax\r
+       mov     ds,ax\r
+\r
+       mov     si,[source]\r
+       mov     di,[dest]                               ;start at same place in all planes\r
+       mov     dx,[height]                             ;scan lines to draw\r
+       mov     ax,[wide]\r
+\r
+@@lineloop:\r
+       mov     cx,ax\r
+       rep     movsb\r
+       add     si,bx\r
+       add     di,bx\r
+\r
+       dec     dx\r
+       jnz     @@lineloop\r
+\r
+       mov     dx,GC_INDEX\r
+       mov     ax,GC_MODE+0*256\r
+       WORDOUT\r
+\r
+       mov     ax,ss\r
+       mov     ds,ax                                   ;restore turbo's data segment\r
+\r
+       ret\r
+\r
+ENDP\r
+\r
+\r
+;============================================================================\r
+;\r
+; VW_MemToScreen\r
+;\r
+; Basic block drawing routine. Takes a block shape at segment pointer source\r
+; with four planes of width by height data, and draws it to dest in the\r
+; virtual screen, based on linewidth.  bufferofs is NOT accounted for.\r
+; There are four drawing routines to provide the best optimized code while\r
+; accounting for odd segment wrappings due to the floating screens.\r
+;\r
+;============================================================================\r
+\r
+DATASEG\r
+\r
+memtoscreentable       dw      eventoeven,eventoodd,oddtoeven,oddtoodd\r
+\r
+CODESEG\r
+\r
+\r
+PROC   VW_MemToScreen  source:WORD, dest:WORD, wide:WORD, height:WORD\r
+PUBLIC VW_MemToScreen\r
+USES   SI,DI\r
+\r
+       mov     es,[screenseg]\r
+\r
+       mov     bx,[linewidth]\r
+       sub     bx,[wide]\r
+\r
+       mov     ds,[source]\r
+\r
+\r
+       xor     si,si                                   ;block is segment aligned\r
+\r
+       xor     di,di\r
+       shr     [wide],1                                ;change wide to words, and see if carry is set\r
+       rcl     di,1                                    ;1 if wide is odd\r
+       mov     ax,[dest]\r
+       shr     ax,1\r
+       rcl     di,1                                    ;shift a 1 in if destination is odd\r
+       shl     di,1                                    ;to index into a word width table\r
+       mov     ax,SC_MAPMASK+0001b*256 ;map mask for plane 0\r
+       jmp     [ss:memtoscreentable+di]        ;call the right routine\r
+\r
+;==============\r
+;\r
+; Copy an even width block to an even video address\r
+;\r
+;==============\r
+\r
+eventoeven:\r
+       mov     dx,SC_INDEX\r
+       WORDOUT\r
+\r
+       mov     di,[dest]                               ;start at same place in all planes\r
+       mov     dx,[height]                             ;scan lines to draw\r
+\r
+@@lineloopEE:\r
+       mov     cx,[wide]\r
+       rep     movsw\r
+\r
+       add     di,bx\r
+\r
+       dec     dx\r
+       jnz     @@lineloopEE\r
+\r
+       shl     ah,1                                    ;shift plane mask over for next plane\r
+       cmp     ah,10000b                               ;done all four planes?\r
+       jne     eventoeven\r
+\r
+       mov     ax,ss\r
+       mov     ds,ax                                   ;restore turbo's data segment\r
+\r
+       ret\r
+\r
+;==============\r
+;\r
+; Copy an odd width block to an even video address\r
+;\r
+;==============\r
+\r
+oddtoeven:\r
+       mov     dx,SC_INDEX\r
+       WORDOUT\r
+\r
+       mov     di,[dest]                               ;start at same place in all planes\r
+       mov     dx,[height]                             ;scan lines to draw\r
+\r
+@@lineloopOE:\r
+       mov     cx,[wide]\r
+       rep     movsw\r
+       movsb                                           ;copy the last byte\r
+\r
+       add     di,bx\r
+\r
+       dec     dx\r
+       jnz     @@lineloopOE\r
+\r
+       shl     ah,1                                    ;shift plane mask over for next plane\r
+       cmp     ah,10000b                               ;done all four planes?\r
+       jne     oddtoeven\r
+\r
+       mov     ax,ss\r
+       mov     ds,ax                                   ;restore turbo's data segment\r
+\r
+       ret\r
+\r
+;==============\r
+;\r
+; Copy an even width block to an odd video address\r
+;\r
+;==============\r
+\r
+eventoodd:\r
+       dec     [wide]                                  ;one word has to be handled seperately\r
+EOplaneloop:\r
+       mov     dx,SC_INDEX\r
+       WORDOUT\r
+\r
+       mov     di,[dest]                               ;start at same place in all planes\r
+       mov     dx,[height]                             ;scan lines to draw\r
+\r
+@@lineloopEO:\r
+       movsb\r
+       mov     cx,[wide]\r
+       rep     movsw\r
+       movsb\r
+\r
+       add     di,bx\r
+\r
+       dec     dx\r
+       jnz     @@lineloopEO\r
+\r
+       shl     ah,1                                    ;shift plane mask over for next plane\r
+       cmp     ah,10000b                               ;done all four planes?\r
+       jne     EOplaneloop\r
+\r
+       mov     ax,ss\r
+       mov     ds,ax                                   ;restore turbo's data segment\r
+\r
+       ret\r
+\r
+;==============\r
+;\r
+; Copy an odd width block to an odd video address\r
+;\r
+;==============\r
+\r
+oddtoodd:\r
+       mov     dx,SC_INDEX\r
+       WORDOUT\r
+\r
+       mov     di,[dest]                               ;start at same place in all planes\r
+       mov     dx,[height]                             ;scan lines to draw\r
+\r
+@@lineloopOO:\r
+       movsb\r
+       mov     cx,[wide]\r
+       rep     movsw\r
+\r
+       add     di,bx\r
+\r
+       dec     dx\r
+       jnz     @@lineloopOO\r
+\r
+       shl     ah,1                                    ;shift plane mask over for next plane\r
+       cmp     ah,10000b                               ;done all four planes?\r
+       jne     oddtoodd\r
+\r
+       mov     ax,ss\r
+       mov     ds,ax                                   ;restore turbo's data segment\r
+\r
+       ret\r
+\r
+\r
+ENDP\r
+\r
+;===========================================================================\r
+;\r
+; VW_ScreenToMem\r
+;\r
+; Copies a block of video memory to main memory, in order from planes 0-3.\r
+; This could be optimized along the lines of VW_MemToScreen to take advantage\r
+; of word copies, but this is an infrequently called routine.\r
+;\r
+;===========================================================================\r
+\r
+PROC   VW_ScreenToMem  source:WORD, dest:WORD, wide:WORD, height:WORD\r
+PUBLIC VW_ScreenToMem\r
+USES   SI,DI\r
+\r
+       mov     es,[dest]\r
+\r
+       mov     bx,[linewidth]\r
+       sub     bx,[wide]\r
+\r
+       mov     ds,[screenseg]\r
+\r
+       mov     ax,GC_READMAP                   ;read map for plane 0\r
+\r
+       xor     di,di\r
+\r
+@@planeloop:\r
+       mov     dx,GC_INDEX\r
+       WORDOUT\r
+\r
+       mov     si,[source]                             ;start at same place in all planes\r
+       mov     dx,[height]                             ;scan lines to draw\r
+\r
+@@lineloop:\r
+       mov     cx,[wide]\r
+       rep     movsb\r
+\r
+       add     si,bx\r
+\r
+       dec     dx\r
+       jnz     @@lineloop\r
+\r
+       inc     ah\r
+       cmp     ah,4                                    ;done all four planes?\r
+       jne     @@planeloop\r
+\r
+       mov     ax,ss\r
+       mov     ds,ax                                   ;restore turbo's data segment\r
+\r
+       ret\r
+\r
+ENDP\r
+\r
+\r
+;============================================================================\r
+;\r
+; VWL_UpdateScreenBlocks\r
+;\r
+; Scans through the update matrix and copies any areas that have changed\r
+; to the visable screen, then zeros the update array\r
+;\r
+;============================================================================\r
+\r
+\r
+\r
+; AX   0/1 for scasb, temp for segment register transfers\r
+; BX    width for block copies\r
+; CX   REP counter\r
+; DX   line width deltas\r
+; SI   source for copies\r
+; DI   scas dest / movsb dest\r
+; BP   pointer to end of bufferblocks\r
+\r
+PROC   VWL_UpdateScreenBlocks\r
+PUBLIC VWL_UpdateScreenBlocks\r
+USES   SI,DI,BP\r
+\r
+       jmp     SHORT @@realstart\r
+@@done:\r
+;\r
+; all tiles have been scanned\r
+;\r
+       mov     dx,GC_INDEX                             ; restore write mode 0\r
+       mov     ax,GC_MODE+0*256\r
+       WORDOUT\r
+\r
+       xor     ax,ax                                   ; clear out the update matrix\r
+       mov     cx,UPDATEWIDE*UPDATEHIGH/2\r
+\r
+       mov     di,[updateptr]\r
+       rep     stosw\r
+\r
+       ret\r
+\r
+@@realstart:\r
+       mov     dx,SC_INDEX\r
+       mov     ax,SC_MAPMASK+15*256\r
+       WORDOUT\r
+       mov     dx,GC_INDEX\r
+       mov     ax,GC_MODE+1*256\r
+       WORDOUT\r
+\r
+       mov     di,[updateptr]                  ; start of floating update screen\r
+       mov     bp,di\r
+       add     bp,UPDATEWIDE*UPDATEHIGH+1 ; when di = bp, all tiles have been scanned\r
+\r
+       push    di\r
+       mov     cx,-1                                   ; definately scan the entire thing\r
+\r
+;\r
+; scan for a 1 in the update list, meaning a tile needs to be copied\r
+; from the master screen to the current screen\r
+;\r
+@@findtile:\r
+       pop     di                                              ; place to continue scaning from\r
+       mov     ax,ss\r
+       mov     es,ax                                   ; search in the data segment\r
+       mov     ds,ax\r
+       mov al,1\r
+       repne   scasb\r
+       cmp     di,bp\r
+       jae     @@done\r
+\r
+       cmp     [BYTE di],al\r
+       jne     @@singletile\r
+       jmp     @@tileblock\r
+\r
+;============\r
+;\r
+; copy a single tile\r
+;\r
+;============\r
+@@singletile:\r
+       inc     di                                              ; we know the next tile is nothing\r
+       push    di                                      ; save off the spot being scanned\r
+       sub     di,[updateptr]\r
+       shl     di,1\r
+       mov     di,[blockstarts-4+di]   ; start of tile location on screen\r
+       mov     si,di\r
+       add     si,[bufferofs]\r
+       add     di,[displayofs]\r
+\r
+       mov     dx,[linewidth]\r
+       sub     dx,2\r
+       mov     ax,[screenseg]\r
+       mov     ds,ax\r
+       mov     es,ax\r
+\r
+REPT   15\r
+       movsb\r
+       movsb\r
+       add     si,dx\r
+       add     di,dx\r
+ENDM\r
+       movsb\r
+       movsb\r
+\r
+       jmp     @@findtile\r
+\r
+;============\r
+;\r
+; more than one tile in a row needs to be updated, so do it as a group\r
+;\r
+;============\r
+EVEN\r
+@@tileblock:\r
+       mov     dx,di                                   ; hold starting position + 1 in dx\r
+       inc     di                                              ; we know the next tile also gets updated\r
+       repe    scasb                           ; see how many more in a row\r
+       push    di                                      ; save off the spot being scanned\r
+\r
+       mov     bx,di\r
+       sub     bx,dx                                   ; number of tiles in a row\r
+       shl     bx,1                                    ; number of bytes / row\r
+\r
+       mov     di,dx                                   ; lookup position of start tile\r
+       sub     di,[updateptr]\r
+       shl     di,1\r
+       mov     di,[blockstarts-2+di]   ; start of tile location\r
+       mov     si,di\r
+       add     si,[bufferofs]\r
+       add     di,[displayofs]\r
+\r
+       mov     dx,[linewidth]\r
+       sub     dx,bx                                   ; offset to next line on screen\r
+\r
+       mov     ax,[screenseg]\r
+       mov     ds,ax\r
+       mov     es,ax\r
+\r
+REPT   15\r
+       mov     cx,bx\r
+       rep     movsb\r
+       add     si,dx\r
+       add     di,dx\r
+ENDM\r
+       mov     cx,bx\r
+       rep     movsb\r
+\r
+       dec     cx                                              ; was 0 from last rep movsb, now $ffff for scasb\r
+       jmp     @@findtile\r
+\r
+ENDP\r
+\r
+\r
+;===========================================================================\r
+;\r
+;                    MISC EGA ROUTINES\r
+;\r
+;===========================================================================\r
+\r
+;=================\r
+;\r
+; VWL_WaitRetrace\r
+;\r
+;=================\r
+\r
+DATASEG\r
+\r
+EXTRN  TimeCount                       :DWORD\r
+EXTRN  jerk                    :WORD\r
+EXTRN  nopan                           :WORD\r
+\r
+CODESEG\r
+\r
+PROC   VWL_WaitRetrace NEAR\r
+       mov     dx,STATUS_REGISTER_1\r
+       mov     bx,[WORD TimeCount]\r
+@@waitloop:\r
+       sti\r
+       jmp     $+2\r
+       cli\r
+\r
+       in      al,dx\r
+       test    al,8\r
+       jnz     @@done\r
+       mov     ax,[WORD TimeCount]\r
+       sub     ax,bx\r
+       cmp     ax,1\r
+       jbe     @@waitloop\r
+\r
+@@done:\r
+       ret\r
+ENDP\r
+\r
+\r
+;==============\r
+;\r
+; VW_SetScreen\r
+;\r
+;==============\r
+\r
+PROC   VW_SetScreen  crtc:WORD, pel:WORD\r
+PUBLIC VW_SetScreen\r
+\r
+if waitforvbl\r
+\r
+       mov     dx,STATUS_REGISTER_1\r
+\r
+;\r
+; wait util the CRTC just starts scaning a diplayed line to set the CRTC start\r
+;\r
+       cli\r
+\r
+@@waitnodisplay:\r
+       in      al,dx\r
+       test    al,01b\r
+       jz      @@waitnodisplay\r
+\r
+@@waitdisplay:\r
+       in      al,dx\r
+       test    al,01b\r
+       jnz     @@waitdisplay\r
+\r
+endif\r
+\r
+;\r
+; set CRTC start\r
+;\r
+; for some reason, my XT's EGA card doesn't like word outs to the CRTC\r
+; index...\r
+;\r
+       mov     cx,[crtc]\r
+       mov     dx,CRTC_INDEX\r
+       mov     al,0ch          ;start address high register\r
+       out     dx,al\r
+       inc     dx\r
+       mov     al,ch\r
+       out     dx,al\r
+       dec     dx\r
+       mov     al,0dh          ;start address low register\r
+       out     dx,al\r
+       mov     al,cl\r
+       inc     dx\r
+       out     dx,al\r
+\r
+       test    [jerk],1\r
+       jz      @@l3\r
+       call    VWL_WaitRetrace\r
+\r
+@@l3:\r
+       test    [nopan],1\r
+       jnz     @@l4\r
+;\r
+; set horizontal panning\r
+;\r
+\r
+       mov     dx,ATR_INDEX\r
+       mov     al,ATR_PELPAN or 20h\r
+       out     dx,al\r
+       jmp     $+2\r
+       mov     al,[BYTE pel]           ;pel pan value\r
+       out     dx,al\r
+\r
+@@l4:\r
+       test    [jerk],1\r
+       jnz     @@done\r
+       call    VWL_WaitRetrace\r
+\r
+@@done:\r
+       sti\r
+\r
+       ret\r
+\r
+ENDP\r
+\r
+\r
+if NUMFONT+NUMFONTM\r
+\r
+;===========================================================================\r
+;\r
+; GENERAL FONT DRAWING ROUTINES\r
+;\r
+;===========================================================================\r
+\r
+DATASEG\r
+\r
+px     dw      ?                                       ; proportional character drawing coordinates\r
+py     dw      ?\r
+pdrawmode      db      11000b          ; 8 = OR, 24 = XOR, put in GC_DATAROTATE\r
+fontcolor      db      15              ;0-15 mapmask value\r
+\r
+PUBLIC px,py,pdrawmode,fontcolor\r
+\r
+;\r
+; offsets in font structure\r
+;\r
+pcharheight    =       0               ;lines high\r
+charloc                =       2               ;pointers to every character\r
+charwidth      =       514             ;every character's width in pixels\r
+\r
+\r
+propchar       dw      ?                       ; the character number to shift\r
+stringptr      dw      ?,?\r
+\r
+\r
+BUFFWIDTH      =       50\r
+BUFFHEIGHT     =   32                  ; must be twice as high as font for masked fonts\r
+\r
+databuffer     db      BUFFWIDTH*BUFFHEIGHT dup (?)\r
+\r
+bufferwidth    dw      ?                                               ; bytes with valid info / line\r
+bufferheight dw        ?                                               ; number of lines currently used\r
+\r
+bufferbyte     dw      ?\r
+bufferbit      dw      ?\r
+\r
+screenspot     dw      ?                                               ; where the buffer is going\r
+\r
+bufferextra    dw      ?                                               ; add at end of a line copy\r
+screenextra    dw      ?\r
+\r
+PUBLIC bufferwidth,bufferheight,screenspot\r
+\r
+CODESEG\r
+\r
+;======================\r
+;\r
+; Macros to table shift a byte of font\r
+;\r
+;======================\r
+\r
+MACRO  SHIFTNOXOR\r
+       mov     al,[es:bx]              ; source\r
+       xor     ah,ah\r
+       shl     ax,1\r
+       mov     si,ax\r
+       mov     ax,[bp+si]              ; table shift into two bytes\r
+       or      [di],al                 ; or with first byte\r
+       inc     di\r
+       mov     [di],ah                 ; replace next byte\r
+       inc     bx                              ; next source byte\r
+ENDM\r
+\r
+MACRO  SHIFTWITHXOR\r
+       mov     al,[es:bx]              ; source\r
+       xor     ah,ah\r
+       shl     ax,1\r
+       mov     si,ax\r
+       mov     ax,[bp+si]              ; table shift into two bytes\r
+       not     ax\r
+       and     [di],al                 ; and with first byte\r
+       inc     di\r
+       mov     [di],ah                 ; replace next byte\r
+       inc     bx                              ; next source byte\r
+ENDM\r
+\r
+\r
+;=======================\r
+;\r
+; BufferToScreen\r
+;\r
+; Pass buffer start in SI (somewhere in databuffer)\r
+; Draws the buffer to the EGA screen in the current write mode\r
+;\r
+;========================\r
+\r
+PROC   BufferToScreen  NEAR\r
+\r
+       mov     es,[screenseg]\r
+       mov     di,[screenspot]\r
+\r
+       mov     bx,[bufferwidth]                ;calculate offsets for end of each line\r
+       or      bx,bx\r
+       jnz     @@isthere\r
+       ret                                                     ;nothing to draw\r
+\r
+@@isthere:\r
+       mov     ax,[linewidth]\r
+       sub     ax,bx\r
+       mov     [screenextra],ax\r
+       mov     ax,BUFFWIDTH\r
+       sub     ax,bx\r
+       mov     [bufferextra],ax\r
+\r
+       mov     bx,[bufferheight]               ;lines to copy\r
+@@lineloop:\r
+       mov     cx,[bufferwidth]                ;bytes to copy\r
+@@byteloop:\r
+       lodsb                                           ;get a byte from the buffer\r
+       xchg    [es:di],al                      ;load latches and store back to screen\r
+       inc     di\r
+\r
+       loop    @@byteloop\r
+\r
+       add     si,[bufferextra]\r
+       add     di,[screenextra]\r
+\r
+       dec     bx\r
+       jnz     @@lineloop\r
+\r
+       ret\r
+ENDP\r
+\r
+\r
+;============================================================================\r
+;\r
+; NON MASKED FONT DRAWING ROUTINES\r
+;\r
+;============================================================================\r
+\r
+if numfont\r
+\r
+DATASEG\r
+\r
+shiftdrawtable dw      0,shift1wide,shift2wide,shift3wide,shift4wide\r
+                               dw              shift5wide\r
+\r
+CODESEG\r
+\r
+;==================\r
+;\r
+; ShiftPropChar\r
+;\r
+; Call with BX = character number (0-255)\r
+; Draws one character to the buffer at bufferbyte/bufferbit, and adjusts\r
+; them to the new position\r
+;\r
+;==================\r
+\r
+PROC   ShiftPropChar   NEAR\r
+\r
+       mov     si,[fontnumber]\r
+       shl     si,1\r
+       mov     es,[grsegs+STARTFONT*2+si]      ;segment of font to use\r
+\r
+;\r
+; find character location, width, and height\r
+;\r
+       mov     si,[es:charwidth+bx]\r
+       and     si,0ffh                                 ;SI hold width in pixels\r
+       shl     bx,1\r
+       mov     bx,[es:charloc+bx]              ;BX holds pointer to character data\r
+\r
+;\r
+; look up which shift table to use, based on bufferbit\r
+;\r
+       mov     di,[bufferbit]\r
+       shl     di,1\r
+       mov     bp,[shifttabletable+di] ;BP holds pointer to shift table\r
+\r
+       mov     di,OFFSET databuffer\r
+       add     di,[bufferbyte]                 ;DI holds pointer to buffer\r
+\r
+;\r
+; advance position by character width\r
+;\r
+       mov     cx,[bufferbit]\r
+       add     cx,si                                   ;new bit position\r
+       mov     ax,cx\r
+       and     ax,7\r
+       mov     [bufferbit],ax                  ;new bit position\r
+       mov     ax,cx\r
+       shr     ax,1\r
+       shr     ax,1\r
+       shr     ax,1\r
+       add     [bufferbyte],ax                 ;new byte position\r
+\r
+       add     si,7\r
+       shr     si,1\r
+       shr     si,1\r
+       shr     si,1                                    ;bytes the character is wide\r
+       shl     si,1                    ;*2 to look up in shiftdrawtable\r
+\r
+       mov     cx,[es:pcharheight]\r
+       mov     dx,BUFFWIDTH\r
+       jmp     [ss:shiftdrawtable+si]  ;procedure to draw this width\r
+\r
+;\r
+; one byte character\r
+;\r
+shift1wide:\r
+       dec     dx\r
+EVEN\r
+@@loop1:\r
+       SHIFTNOXOR\r
+       add     di,dx                   ; next line in buffer\r
+       loop    @@loop1\r
+       ret\r
+\r
+;\r
+; two byte character\r
+;\r
+shift2wide:\r
+       dec     dx\r
+       dec     dx\r
+EVEN\r
+@@loop2:\r
+       SHIFTNOXOR\r
+       SHIFTNOXOR\r
+       add     di,dx                   ; next line in buffer\r
+       loop    @@loop2\r
+       ret\r
+\r
+;\r
+; three byte character\r
+;\r
+shift3wide:\r
+       sub     dx,3\r
+EVEN\r
+@@loop3:\r
+       SHIFTNOXOR\r
+       SHIFTNOXOR\r
+       SHIFTNOXOR\r
+       add     di,dx                   ; next line in buffer\r
+       loop    @@loop3\r
+       ret\r
+\r
+;\r
+; four byte character\r
+;\r
+shift4wide:\r
+       sub     dx,4\r
+EVEN\r
+@@loop4:\r
+       SHIFTNOXOR\r
+       SHIFTNOXOR\r
+       SHIFTNOXOR\r
+       SHIFTNOXOR\r
+       add     di,dx                   ; next line in buffer\r
+       loop    @@loop4\r
+       ret\r
+\r
+;\r
+; five byte character\r
+;\r
+shift5wide:\r
+       sub     dx,5\r
+EVEN\r
+@@loop5:\r
+       SHIFTNOXOR\r
+       SHIFTNOXOR\r
+       SHIFTNOXOR\r
+       SHIFTNOXOR\r
+       add     di,dx                   ; next line in buffer\r
+       loop    @@loop5\r
+       ret\r
+\r
+\r
+\r
+ENDP\r
+\r
+;============================================================================\r
+\r
+;==================\r
+;\r
+; VW_DrawPropString\r
+;\r
+; Draws a C string of characters at px/py and advances px\r
+;\r
+; Assumes write mode 0\r
+;\r
+;==================\r
+\r
+CODESEG\r
+\r
+PROC   VW_DrawPropString       string:DWORD\r
+PUBLIC VW_DrawPropString\r
+USES   SI,DI\r
+\r
+;\r
+; proportional spaceing, which clears the buffer ahead of it, so only\r
+; clear the first collumn\r
+;\r
+       mov     al,0\r
+line   =       0\r
+REPT   BUFFHEIGHT\r
+       mov     [BYTE databuffer+BUFFWIDTH*line],al\r
+line   =       line+1\r
+ENDM\r
+\r
+;\r
+; shift the characters into the buffer\r
+;\r
+@@shiftchars:\r
+       mov     ax,[px]\r
+       and     ax,7\r
+       mov     [bufferbit],ax\r
+       mov     [bufferbyte],0\r
+\r
+       mov     ax,[WORD string]\r
+       mov     [stringptr],ax\r
+       mov     ax,[WORD string+2]\r
+       mov     [stringptr+2],ax\r
+\r
+@@shiftone:\r
+       mov     es,[stringptr+2]\r
+       mov     bx,[stringptr]\r
+       inc     [stringptr]\r
+       mov     bx,[es:bx]\r
+       xor     bh,bh\r
+       or      bl,bl\r
+       jz      @@allshifted\r
+       call    ShiftPropChar\r
+       jmp     @@shiftone\r
+\r
+@@allshifted:\r
+;\r
+; calculate position to draw buffer on screen\r
+;\r
+       mov     bx,[py]\r
+       shl     bx,1\r
+       mov     di,[ylookup+bx]\r
+       add     di,[bufferofs]\r
+       add     di,[panadjust]\r
+\r
+       mov     ax,[px]\r
+       shr     ax,1\r
+       shr     ax,1\r
+       shr     ax,1            ;x location in bytes\r
+       add     di,ax\r
+       mov     [screenspot],di\r
+\r
+;\r
+; advance px\r
+;\r
+       mov     ax,[bufferbyte]\r
+       shl     ax,1\r
+       shl     ax,1\r
+       shl     ax,1\r
+       or      ax,[bufferbit]\r
+       add     [px],ax\r
+\r
+;\r
+; draw it\r
+;\r
+\r
+; set xor/or mode\r
+       mov     dx,GC_INDEX\r
+       mov     al,GC_DATAROTATE\r
+       mov     ah,[pdrawmode]\r
+       WORDOUT\r
+\r
+; set mapmask to color\r
+       mov     dx,SC_INDEX\r
+       mov     al,SC_MAPMASK\r
+       mov     ah,[fontcolor]\r
+       WORDOUT\r
+\r
+       mov     ax,[bufferbyte]\r
+       test    [bufferbit],7\r
+       jz      @@go\r
+       inc     ax                              ;so the partial byte also gets drawn\r
+@@go:\r
+       mov     [bufferwidth],ax\r
+       mov     si,[fontnumber]\r
+       shl     si,1\r
+       mov     es,[grsegs+STARTFONT*2+si]\r
+       mov     ax,[es:pcharheight]\r
+       mov     [bufferheight],ax\r
+\r
+       mov     si,OFFSET databuffer\r
+       call    BufferToScreen\r
+\r
+; set copy mode\r
+       mov     dx,GC_INDEX\r
+       mov     ax,GC_DATAROTATE\r
+       WORDOUT\r
+\r
+; set mapmask to all\r
+       mov     dx,SC_INDEX\r
+       mov     ax,SC_MAPMASK + 15*256\r
+       WORDOUT\r
+\r
+\r
+       ret\r
+\r
+ENDP\r
+\r
+endif  ;numfont\r
+\r
+;============================================================================\r
+;\r
+; MASKED FONT DRAWING ROUTINES\r
+;\r
+;============================================================================\r
+\r
+if     numfontm\r
+\r
+DATASEG\r
+\r
+mshiftdrawtable        dw      0,mshift1wide,mshift2wide,mshift3wide\r
+\r
+\r
+CODESEG\r
+\r
+;==================\r
+;\r
+; ShiftMPropChar\r
+;\r
+; Call with BX = character number (0-255)\r
+; Draws one character to the buffer at bufferbyte/bufferbit, and adjusts\r
+; them to the new position\r
+;\r
+;==================\r
+\r
+PROC   ShiftMPropChar  NEAR\r
+\r
+       mov     si,[fontnumber]\r
+       shl     si,1\r
+       mov     es,[grsegs+STARTFONTM*2+si]     ;segment of font to use\r
+\r
+;\r
+; find character location, width, and height\r
+;\r
+       mov     si,[es:charwidth+bx]\r
+       and     si,0ffh                                 ;SI hold width in pixels\r
+       shl     bx,1\r
+       mov     bx,[es:charloc+bx]              ;BX holds pointer to character data\r
+\r
+;\r
+; look up which shift table to use, based on bufferbit\r
+;\r
+       mov     di,[bufferbit]\r
+       shl     di,1\r
+       mov     bp,[shifttabletable+di] ;BP holds pointer to shift table\r
+\r
+       mov     di,OFFSET databuffer\r
+       add     di,[bufferbyte]                 ;DI holds pointer to buffer\r
+\r
+       mov     cx,[bufferbit]\r
+       add     cx,si                                   ;new bit position\r
+       mov     ax,cx\r
+       and     ax,7\r
+       mov     [bufferbit],ax                  ;new bit position\r
+       mov     ax,cx\r
+       shr     ax,1\r
+       shr     ax,1\r
+       shr     ax,1\r
+       add     [bufferbyte],ax                 ;new byte position\r
+\r
+       add     si,7\r
+       shr     si,1\r
+       shr     si,1\r
+       shr     si,1                                    ;bytes the character is wide\r
+       shl     si,1                    ;*2 to look up in shiftdrawtable\r
+\r
+       mov     cx,[es:pcharheight]\r
+       mov     dx,BUFFWIDTH\r
+       jmp     [ss:mshiftdrawtable+si] ;procedure to draw this width\r
+\r
+;\r
+; one byte character\r
+;\r
+mshift1wide:\r
+       dec     dx\r
+\r
+EVEN\r
+@@loop1m:\r
+       SHIFTWITHXOR\r
+       add     di,dx                   ; next line in buffer\r
+\r
+       loop    @@loop1m\r
+\r
+       mov     cx,[es:pcharheight]\r
+\r
+EVEN\r
+@@loop1:\r
+       SHIFTNOXOR\r
+       add     di,dx                   ; next line in buffer\r
+       loop    @@loop1\r
+\r
+       ret\r
+\r
+;\r
+; two byte character\r
+;\r
+mshift2wide:\r
+       dec     dx\r
+       dec     dx\r
+EVEN\r
+@@loop2m:\r
+       SHIFTWITHXOR\r
+       SHIFTWITHXOR\r
+       add     di,dx                   ; next line in buffer\r
+\r
+       loop    @@loop2m\r
+\r
+       mov     cx,[es:pcharheight]\r
+\r
+EVEN\r
+@@loop2:\r
+       SHIFTNOXOR\r
+       SHIFTNOXOR\r
+       add     di,dx                   ; next line in buffer\r
+       loop    @@loop2\r
+\r
+       ret\r
+\r
+;\r
+; three byte character\r
+;\r
+mshift3wide:\r
+       sub     dx,3\r
+EVEN\r
+@@loop3m:\r
+       SHIFTWITHXOR\r
+       SHIFTWITHXOR\r
+       SHIFTWITHXOR\r
+       add     di,dx                   ; next line in buffer\r
+\r
+       loop    @@loop3m\r
+\r
+       mov     cx,[es:pcharheight]\r
+\r
+EVEN\r
+@@loop3:\r
+       SHIFTNOXOR\r
+       SHIFTNOXOR\r
+       SHIFTNOXOR\r
+       add     di,dx                   ; next line in buffer\r
+       loop    @@loop3\r
+\r
+       ret\r
+\r
+\r
+ENDP\r
+\r
+;============================================================================\r
+\r
+;==================\r
+;\r
+; VW_DrawMPropString\r
+;\r
+; Draws a C string of characters at px/py and advances px\r
+;\r
+; Assumes write mode 0\r
+;\r
+;==================\r
+\r
+\r
+\r
+PROC   VW_DrawMPropString      string:DWORD\r
+PUBLIC VW_DrawMPropString\r
+USES   SI,DI\r
+\r
+;\r
+; clear out the first byte of the buffer, the rest will automatically be\r
+; cleared as characters are drawn into it\r
+;\r
+       mov     si,[fontnumber]\r
+       shl     si,1\r
+       mov     es,[grsegs+STARTFONTM*2+si]\r
+       mov     dx,[es:pcharheight]\r
+       mov     di,OFFSET databuffer\r
+       mov     ax,ds\r
+       mov     es,ax\r
+       mov     bx,BUFFWIDTH-1\r
+\r
+       mov     cx,dx\r
+       mov     al,0ffh\r
+@@maskfill:\r
+       stosb                           ; fill the mask part with $ff\r
+       add     di,bx\r
+       loop    @@maskfill\r
+\r
+       mov     cx,dx\r
+       xor     al,al\r
+@@datafill:\r
+       stosb                           ; fill the data part with $0\r
+       add     di,bx\r
+       loop    @@datafill\r
+\r
+;\r
+; shift the characters into the buffer\r
+;\r
+       mov     ax,[px]\r
+       and     ax,7\r
+       mov     [bufferbit],ax\r
+       mov     [bufferbyte],0\r
+\r
+       mov     ax,[WORD string]\r
+       mov     [stringptr],ax\r
+       mov     ax,[WORD string+2]\r
+       mov     [stringptr+2],ax\r
+\r
+@@shiftone:\r
+       mov     es,[stringptr+2]\r
+       mov     bx,[stringptr]\r
+       inc     [stringptr]\r
+       mov     bx,[es:bx]\r
+       xor     bh,bh\r
+       or      bl,bl\r
+       jz      @@allshifted\r
+       call    ShiftMPropChar\r
+       jmp     @@shiftone\r
+\r
+@@allshifted:\r
+;\r
+; calculate position to draw buffer on screen\r
+;\r
+       mov     bx,[py]\r
+       shl     bx,1\r
+       mov     di,[ylookup+bx]\r
+       add     di,[bufferofs]\r
+       add     di,[panadjust]\r
+\r
+       mov     ax,[px]\r
+       shr     ax,1\r
+       shr     ax,1\r
+       shr     ax,1            ;x location in bytes\r
+       add     di,ax\r
+       mov     [screenspot],di\r
+\r
+;\r
+; advance px\r
+;\r
+       mov     ax,[bufferbyte]\r
+       shl     ax,1\r
+       shl     ax,1\r
+       shl     ax,1\r
+       or      ax,[bufferbit]\r
+       add     [px],ax\r
+\r
+;\r
+; draw it\r
+;\r
+       mov     ax,[bufferbyte]\r
+       test    [bufferbit],7\r
+       jz      @@go\r
+       inc     ax                              ;so the partial byte also gets drawn\r
+@@go:\r
+       mov     [bufferwidth],ax\r
+       mov     es,[grsegs+STARTFONTM*2]\r
+       mov     ax,[es:pcharheight]\r
+       mov     [bufferheight],ax\r
+\r
+; set AND mode to punch out the mask\r
+       mov     dx,GC_INDEX\r
+       mov     ax,GC_DATAROTATE + 8*256\r
+       WORDOUT\r
+\r
+; set mapmask to all\r
+       mov     dx,SC_INDEX\r
+       mov     ax,SC_MAPMASK + 15*256\r
+       WORDOUT\r
+\r
+       mov     si,OFFSET databuffer\r
+       call    BufferToScreen\r
+\r
+; set OR mode to fill in the color\r
+       mov     dx,GC_INDEX\r
+       mov     ax,GC_DATAROTATE + 16*256\r
+       WORDOUT\r
+\r
+; set mapmask to color\r
+       mov     dx,SC_INDEX\r
+       mov     al,SC_MAPMASK\r
+       mov     ah,[fontcolor]\r
+       WORDOUT\r
+\r
+       call    BufferToScreen          ; SI is still in the right position in buffer\r
+\r
+; set copy mode\r
+       mov     dx,GC_INDEX\r
+       mov     ax,GC_DATAROTATE\r
+       WORDOUT\r
+\r
+; set mapmask to all\r
+       mov     dx,SC_INDEX\r
+       mov     ax,SC_MAPMASK + 15*256\r
+       WORDOUT\r
+\r
+\r
+       ret\r
+\r
+ENDP\r
+\r
+endif          ; if numfontm\r
+\r
+endif          ; if fonts\r
diff --git a/16/keen456/KEEN4-6/CK_DEF.H b/16/keen456/KEEN4-6/CK_DEF.H
new file mode 100755 (executable)
index 0000000..62e7944
--- /dev/null
@@ -0,0 +1,775 @@
+/* Reconstructed Commander Keen 4-6 Source Code\r
+ * Copyright (C) 2021 K1n9_Duk3\r
+ *\r
+ * This file is loosely based on:\r
+ * Keen Dreams Source Code\r
+ * Copyright (C) 2014 Javier M. Chavez\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License along\r
+ * with this program; if not, write to the Free Software Foundation, Inc.,\r
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+ */\r
+\r
+#ifndef __CK_DEF__\r
+#define __CK_DEF__\r
+\r
+#include <BIOS.H>\r
+#include <CONIO.H>\r
+\r
+#include "ID_HEADS.H"\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                GLOBAL CONSTANTS & MACROS\r
+\r
+=============================================================================\r
+*/\r
+\r
+#define MAXACTORS 100\r
+\r
+#define GAMELEVELS 25\r
+\r
+#define CONVERT_GLOBAL_TO_TILE(x)  ((x)>>(G_T_SHIFT))\r
+#define CONVERT_TILE_TO_GLOBAL(x)  ((x)<<(G_T_SHIFT))\r
+#define CONVERT_GLOBAL_TO_PIXEL(x) ((x)>>(G_P_SHIFT))\r
+#define CONVERT_PIXEL_TO_GLOBAL(x) ((x)<<(G_P_SHIFT))\r
+#define CONVERT_PIXEL_TO_TILE(x)   ((x)>>(P_T_SHIFT))\r
+#define CONVERT_TILE_TO_PIXEL(x)   ((x)<<(P_T_SHIFT))\r
+\r
+#define SPAWN_ADJUST_Y(y, h) (CONVERT_TILE_TO_GLOBAL(y) + (CONVERT_PIXEL_TO_GLOBAL(16-(h))))\r
+\r
+#define ARRAYLENGTH(x) (sizeof(x)/sizeof(*(x)))\r
+\r
+#define CA_UnmarkGrChunk(num) (grneeded[num] &= ~ca_levelbit)\r
+\r
+#define SetPalette(pal) {_ES=FP_SEG(pal); _DX=FP_OFF(pal); _AX=0x1002; geninterrupt(0x10);}\r
+#define SetPaletteEx(pal) {(pal)[16] = bordercolor; SetPalette(pal);}\r
+\r
+//HACK IMPORTS:\r
+void RFL_InitAnimList(void);\r
+void CA_FreeGraphics(void);\r
+void CA_SetGrPurge(void);\r
+\r
+/*\r
+Note:\r
+\r
+The ID software memory manager doesn't care about the different purge levels.\r
+Using PURGE_FIST is identical to using PURGE_LAST.\r
+*/\r
+#define PURGE_FIRST 3\r
+#define PURGE_LAST 1\r
+\r
+#define PLATFORMBLOCK 31\r
+#define DIRARROWSTART 91\r
+#define DIRARROWEND   (DIRARROWSTART+arrow_None)\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                  GLOBAL TYPES\r
+\r
+=============================================================================\r
+*/\r
+\r
+//SDL-style integer types - just to make future SDL ports easier\r
+typedef unsigned int Uint16;\r
+typedef signed int Sint16;\r
+typedef unsigned char Uint8;\r
+typedef signed char Sint8;\r
+typedef unsigned long Uint32;\r
+typedef signed long Sint32;\r
+//Note: only the game code (CK_*.C, K?_*.C) uses these!\r
+\r
+//some compile-time checks to make sure the ints have the correct size\r
+#if (sizeof(Uint16) != 2)\r
+#error 'Uint16' has wrong size\r
+#elif (sizeof(Sint16) != 2)\r
+#error 'Sint16' has wrong size\r
+#elif (sizeof(Uint8) != 1)\r
+#error 'Uint8' has wrong size\r
+#elif (sizeof(Sint8) != 1)\r
+#error 'Sint8' has wrong size\r
+#elif (sizeof(Uint32) != 4)\r
+#error 'Uint32' has wrong size\r
+#elif (sizeof(Sint32) != 4)\r
+#error 'Sint32' has wrong size\r
+#endif\r
+\r
+typedef enum {\r
+       arrow_North,     // 0\r
+       arrow_East,      // 1\r
+       arrow_South,     // 2\r
+       arrow_West,      // 3\r
+       arrow_NorthEast, // 4\r
+       arrow_SouthEast, // 5\r
+       arrow_SouthWest, // 6\r
+       arrow_NorthWest, // 7\r
+       arrow_None       // 8\r
+} arrowdirtype;\r
+\r
+typedef enum {\r
+       ex_stillplaying, //  0\r
+       ex_died,         //  1\r
+       ex_completed,    //  2\r
+       ex_rescued,      //  3, only in Keen 4\r
+       ex_warped,       //  4\r
+       ex_resetgame,    //  5\r
+       ex_loadedgame,   //  6\r
+       ex_foot,         //  7, only in Keen 4\r
+       ex_abortgame,    //  8\r
+       ex_sandwich,     //  9, only in Keen 6\r
+       ex_hook,         // 10, only in Keen 6\r
+       ex_card,         // 11, only in Keen 6\r
+       ex_molly,        // 12, only in Keen 6\r
+       ex_portout,      // 13, only in Keen 5\r
+       ex_fusebroke,    // 14, only in Keen 5\r
+       ex_qedbroke,     // 15, only in Keen 5\r
+       NUMEXITTYPES\r
+} exittype;\r
+\r
+typedef enum\r
+{\r
+       INTILE_NOTHING,        //  0\r
+       INTILE_POLE,           //  1\r
+       INTILE_DOOR,           //  2\r
+       INTILE_DEADLY,         //  3\r
+       INTILE_DROP,           //  4\r
+       INTILE_SWITCH0,        //  5\r
+       INTILE_SWITCH1,        //  6\r
+       INTILE_GEMSOCKET0,     //  7\r
+       INTILE_GEMSOCKET1,     //  8\r
+       INTILE_GEMSOCKET2,     //  9\r
+       INTILE_GEMSOCKET3,     // 10\r
+       INTILE_SHORESOUTH,     // 11\r
+       INTILE_SHOREWEST,      // 12\r
+       INTILE_SHORENORTH,     // 13\r
+       INTILE_SHOREEAST,      // 14\r
+       INTILE_BRIDGESWITCH,   // 15\r
+       INTILE_MOON,           // 16\r
+       INTILE_DIRARROW,       // 17 (not used in the code, but assigned to tiles in Keen 5 & 6)\r
+       INTILE_BRIDGE,         // 18\r
+       INTILE_FORCEFIELD,     // 19\r
+       INTILE_TELEPORT,       // 20\r
+       INTILE_BONUS100,       // 21\r
+       INTILE_BONUS200,       // 22\r
+       INTILE_BONUS500,       // 23\r
+       INTILE_BONUS1000,      // 24\r
+       INTILE_BONUS2000,      // 25\r
+       INTILE_BONUS5000,      // 26\r
+       INTILE_EXTRALIFE,      // 27\r
+       INTILE_AMMO,           // 28\r
+       INTILE_29,             // 29 (never used)\r
+       INTILE_FORCEFIELDEND,  // 30\r
+       INTILE_AMPTONCOMPUTER, // 31\r
+       INTILE_KEYCARDDOOR,    // 32\r
+       INTILE_ELEVATORLEFT,   // 33\r
+       INTILE_ELEVATORRIGHT,  // 34\r
+\r
+       INTILE_FOREGROUND = 0x80\r
+} intiletype;\r
+\r
+#define INTILE_TYPEMASK (INTILE_FOREGROUND-1)\r
+\r
+typedef enum\r
+{\r
+       nothing,         //  0\r
+       inertobj,        //  1\r
+       keenobj,         //  2\r
+       stunshotobj,     //  3\r
+#if defined KEEN4\r
+       bonusobj,        //  4\r
+       slugobj,         //  5\r
+       oracleobj,       //  6\r
+       classtype_7,     //  7, never used\r
+       eggobj,          //  8\r
+       madmushroomobj,  //  9\r
+       arachnutobj,     // 10\r
+       skypestobj,      // 11\r
+       wormouthobj,     // 12\r
+       thundercloudobj, // 13\r
+       berkeloidobj,    // 14\r
+       bounderobj,      // 15\r
+       inchwormobj,     // 16\r
+       footobj,         // 17\r
+       lickobj,         // 18\r
+       mimrockobj,      // 19\r
+       platformobj,     // 20\r
+       dopefishobj,     // 21\r
+       schoolfishobj,   // 22\r
+       pixieobj,        // 23\r
+       lindseyobj,      // 24\r
+       lightningobj,    // 25\r
+       treasureeaterobj,// 26\r
+       eggbirdobj,      // 27\r
+       classtype_28,    // 28, never used\r
+       classtype_29,    // 29, never used\r
+       scubaobj,        // 30\r
+       mshotobj,        // 31\r
+       mineobj,         // 32\r
+       stunnedobj,      // 33\r
+       flagobj,         // 34\r
+#elif defined KEEN5\r
+       mshotobj,        //  4\r
+       bonusobj,        //  5\r
+       platformobj,     //  6\r
+       stunnedobj,      //  7\r
+       flagobj,         //  8\r
+       sparkyobj,       //  9\r
+       mineobj,         // 10\r
+       slicestarobj,    // 11\r
+       roboredobj,      // 12\r
+       spirogripobj,    // 13\r
+       amptonobj,       // 14\r
+       cannonobj,       // 15\r
+       volteobj,        // 16\r
+       shelleyobj,      // 17, never used\r
+       spindredobj,     // 18\r
+       shikadimasterobj,// 19\r
+       shikadiobj,      // 20\r
+       petobj,          // 21\r
+       spherefulobj,    // 22\r
+       scottieobj,      // 23\r
+       teleporterobj,   // 24\r
+       qedobj,          // 25\r
+#elif defined KEEN6\r
+       mshotobj,        //  4\r
+       bonusobj,        //  5\r
+       platformobj,     //  6\r
+       bloogobj,        //  7\r
+       bloogletobj,     //  8\r
+       classtype_9,     //  9, never used\r
+       fleexobj,        // 10\r
+       classtype_11,    // 11, never used\r
+       mollyobj,        // 12\r
+       babobbaobj,      // 13\r
+       bobbaobj,        // 14\r
+       classtype_15,    // 15\r
+       nospikeobj,      // 16\r
+       gikobj,          // 17\r
+       cannonobj,       // 18\r
+       orbatrixobj,     // 19\r
+       bipobj,          // 20\r
+       flectobj,        // 21\r
+       blorbobj,        // 22\r
+       ceilickobj,      // 23\r
+       blooguardobj,    // 24\r
+       stunnedobj,      // 25\r
+       bipshipobj,      // 26\r
+       sandwichobj,     // 27\r
+       hookobj,         // 28\r
+       passcardobj,     // 29\r
+       grabbiterobj,    // 30\r
+       rocketobj,       // 31\r
+       grapplespotobj,  // 32\r
+       satelliteobj,    // 33\r
+       satellitestopobj,// 34\r
+       flagobj,         // 35\r
+#endif\r
+       NUMCLASSTYPES\r
+} classtype;\r
+\r
+typedef struct statestruct\r
+{\r
+       Sint16 leftshapenum, rightshapenum;\r
+       enum {step,slide,think,stepthink,slidethink} progress;\r
+       boolean skippable;\r
+       boolean pushtofloor;\r
+       Sint16 tictime;\r
+       Sint16 xmove;\r
+       Sint16 ymove;\r
+       void (*think) (struct objstruct*);\r
+       void (*contact) (struct objstruct*, struct objstruct*);\r
+       void (*react) (struct objstruct*);\r
+       struct statestruct *nextstate;\r
+} statetype;\r
+\r
+typedef struct objstruct\r
+{\r
+       classtype obclass;\r
+       enum {ac_no, ac_yes, ac_allways, ac_removable} active;\r
+       boolean needtoreact;\r
+       enum {cl_noclip, cl_midclip, cl_fullclip} needtoclip;\r
+       Uint16 nothink;\r
+       Uint16 x, y;\r
+       Sint16 xdir, ydir;\r
+       Sint16 xmove, ymove;\r
+       Sint16 xspeed, yspeed;\r
+       Sint16 ticcount;\r
+       statetype *state;\r
+       Uint16 shapenum;\r
+       Uint16 priority;\r
+       Uint16 left, top, right, bottom, midx;\r
+       Uint16 tileleft, tiletop, tileright, tilebottom, tilemidx;\r
+       Sint16 hitnorth, hiteast, hitsouth, hitwest;\r
+       Sint16 temp1, temp2, temp3, temp4;\r
+       void *sprite;\r
+       struct objstruct *next, *prev;\r
+} objtype;\r
+\r
+typedef struct\r
+{\r
+       Uint16 worldx, worldy;\r
+       boolean leveldone[GAMELEVELS];\r
+       Sint32 score, nextextra;\r
+       Sint16 ammo, drops;\r
+#if defined KEEN4\r
+       Sint16 wetsuit;\r
+       Sint16 rescued;\r
+#elif defined KEEN5\r
+       boolean keycard;\r
+       Sint16 destroyed;       // never used\r
+       Sint16 numfuses;\r
+#elif defined KEEN6\r
+       Sint16 sandwichstate, hookstate, passcardstate, rocketstate;\r
+#endif\r
+       Sint16 keys[4];\r
+       Sint16 mapon;\r
+       Sint16 lives;\r
+       Sint16 difficulty;\r
+       objtype *riding;\r
+} gametype;\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                               CK_MAIN DEFINITIONS\r
+\r
+=============================================================================\r
+*/\r
+\r
+extern char str[80], str2[20];\r
+extern boolean storedemo;\r
+\r
+void SizeText(char *text, Uint16 *width, Uint16 *height);\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                               CK_DEMO DEFINITIONS\r
+\r
+=============================================================================\r
+*/\r
+\r
+extern boolean scorescreenkludge;\r
+\r
+void CheckLastScan(void);\r
+#if GRMODE == EGAGR\r
+void Terminator(void);\r
+void StarWars(void);\r
+#endif\r
+void ShowTitle(void);\r
+#if GRMODE == CGAGR\r
+void ShowCredits(void);\r
+#endif\r
+void RunDemo(Sint16 num);\r
+void DrawHighScores(void);\r
+void CheckHighScore(Sint32 score, Sint16 completed);\r
+void ShowHighScores(void);\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                               CK_GAME DEFINITIONS\r
+\r
+=============================================================================\r
+*/\r
+\r
+void FreeGraphics(void);\r
+void NewGame(void);\r
+boolean SaveTheGame(Sint16 handle);\r
+boolean LoadTheGame(Sint16 handle);\r
+void ResetGame(void);\r
+void SetupGameLevel(boolean loadnow);\r
+void DialogDraw(char *title, Uint16 numcache);\r
+void DialogUpdate(void);\r
+void DialogFinish(void);\r
+void StartDemoRecord(void);\r
+void EndDemoRecord(void);\r
+void GameLoop(void);\r
+void HandleDeath(void);\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                               CK_PLAY DEFINITIONS\r
+\r
+=============================================================================\r
+*/\r
+\r
+extern boolean singlestep, jumpcheat, godmode, keenkilled;\r
+extern exittype playstate;\r
+extern gametype gamestate;\r
+extern objtype *new, *check, *player, *scoreobj;\r
+extern Uint16 originxtilemax, originytilemax;\r
+extern ControlInfo c;\r
+extern boolean button2, button3;       // never used\r
+extern objtype dummyobj;\r
+extern Sint16 invincible;\r
+extern boolean oldshooting, showscorebox, joypad;\r
+extern Sint16 groundslam;\r
+extern boolean debugok;\r
+extern boolean jumpbutton, jumpheld, pogobutton, pogoheld, firebutton, fireheld, upheld;\r
+\r
+\r
+void CheckKeys(void);\r
+void StatusWindow(void);\r
+void CenterActor(objtype *ob);\r
+void WorldScrollScreen(objtype *ob);\r
+void ScrollScreen(objtype *ob);\r
+void InitObjArray(void);\r
+Sint16 GetNewObj(boolean usedummy);\r
+void RemoveObj(objtype *ob);\r
+void GivePoints(Uint16 points);\r
+void StopMusic(void);\r
+void StartMusic(Uint16 num);\r
+void PlayLoop(void);\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                               CK_TEXT DEFINITIONS\r
+\r
+=============================================================================\r
+*/\r
+\r
+void HelpScreens(void);\r
+void FinaleLayout(void);\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                               CK_STATE DEFINITIONS\r
+\r
+=============================================================================\r
+*/\r
+\r
+extern Sint16 wallclip[8][16];\r
+\r
+extern Sint16 xtry;\r
+extern Sint16 ytry;\r
+extern boolean playerkludgeclipcancel;\r
+\r
+void MoveObjVert(objtype *ob, Sint16 ymove);\r
+void MoveObjHoriz(objtype *ob, Sint16 xmove);\r
+void PlayerBottomKludge(objtype *ob);\r
+void PlayerTopKludge(objtype *ob);\r
+void ClipToEnds(objtype *ob);\r
+void ClipToSides(objtype *ob);\r
+boolean CheckPosition(objtype *ob);\r
+boolean StatePositionOk(objtype *ob, statetype *state);\r
+\r
+#ifdef KEEN5\r
+void CalcBounds(objtype *ob);\r
+#endif\r
+\r
+void ClipToWalls(objtype *ob);\r
+void FullClipToWalls(objtype *ob);\r
+void PushObj(objtype *ob);\r
+void ClipToSpriteSide(objtype *push, objtype *solid);\r
+void ClipToSpriteTop(objtype *push, objtype *solid);\r
+void ClipToSprite(objtype *push, objtype *solid, boolean squish);\r
+Sint16 DoActor(objtype *ob, Sint16 numtics);\r
+void StateMachine(objtype *ob);\r
+void NewState(objtype *ob, statetype *state);\r
+void ChangeState(objtype *ob, statetype *state);\r
+boolean OnScreen(objtype *ob);\r
+void DoGravity(objtype *ob);\r
+void DoWeakGravity(objtype *ob);\r
+void DoTinyGravity(objtype *ob);\r
+void AccelerateX(objtype *ob, Sint16 dir, Sint16 maxspeed);\r
+void AccelerateXv(objtype *ob, Sint16 dir, Sint16 maxspeed);\r
+void AccelerateY(objtype *ob, Sint16 dir, Sint16 maxspeed);\r
+void FrictionX(objtype *ob);\r
+void FrictionY(objtype *ob);\r
+void StunObj(objtype *ob, objtype *shot, statetype *stunstate);\r
+void T_Projectile(objtype *ob);\r
+void T_WeakProjectile(objtype *ob);\r
+void ProjectileThink1(objtype *ob);\r
+void T_Velocity(objtype *ob);\r
+void SetReactThink(objtype *ob);\r
+void T_Stunned(objtype *ob);\r
+void C_Lethal(objtype *ob, objtype *hit);\r
+void R_Draw(objtype *ob);\r
+void R_Walk(objtype *ob);\r
+void R_WalkNormal(objtype *ob);\r
+void BadState(void);\r
+void R_Stunned(objtype *ob);\r
+\r
+extern statetype sc_deadstate;\r
+extern statetype sc_badstate;\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                               CK_KEEN DEFINITIONS\r
+\r
+=============================================================================\r
+*/\r
+\r
+extern Uint16 bounceangle[8][8];\r
+#ifndef KEEN4\r
+extern arrowdirtype arrowflip[];\r
+#endif\r
+\r
+extern statetype s_keenstand;\r
+extern statetype s_keenpauselook;\r
+extern statetype s_keenwait1;\r
+extern statetype s_keenwait2;\r
+extern statetype s_keenwait3;\r
+extern statetype s_keenwait4;\r
+extern statetype s_keenwait5;\r
+extern statetype s_keenwait6;\r
+extern statetype s_keenmoon1;\r
+extern statetype s_keenmoon2;\r
+extern statetype s_keenmoon3;\r
+extern statetype s_keenread;\r
+extern statetype s_keenread2;\r
+extern statetype s_keenread3;\r
+extern statetype s_keenread4;\r
+extern statetype s_keenread5;\r
+extern statetype s_keenread6;\r
+extern statetype s_keenread7;\r
+extern statetype s_keenstopread;\r
+extern statetype s_keenstopread2;\r
+extern statetype s_keenstopread3;\r
+extern statetype s_keenlookup;\r
+extern statetype s_keenlookup2;\r
+extern statetype s_keenlookdown;\r
+extern statetype s_keenlookdown2;\r
+extern statetype s_keenlookdown3;\r
+extern statetype s_keenlookdown4;\r
+extern statetype s_keendrop;\r
+extern statetype s_keendead;\r
+extern statetype s_keendie1;\r
+extern statetype s_keendie2;\r
+#ifdef KEEN4\r
+extern statetype s_keensuitdie1;\r
+extern statetype s_keensuitdie2;\r
+#endif\r
+extern statetype s_keenshoot1;\r
+extern statetype s_keenshoot2;\r
+extern statetype s_keenshootup1;\r
+extern statetype s_keenshootup2;\r
+extern statetype s_keenswitch;\r
+extern statetype s_keenswitch2;\r
+extern statetype s_keenkey;\r
+extern statetype s_keenlineup;\r
+extern statetype s_keenenter1;\r
+extern statetype s_keenenter2;\r
+extern statetype s_keenenter3;\r
+extern statetype s_keenenter4;\r
+extern statetype s_keenenter5;\r
+extern statetype s_keenpole;\r
+extern statetype s_keenclimb1;\r
+extern statetype s_keenclimb2;\r
+extern statetype s_keenclimb3;\r
+extern statetype s_keenslide1;\r
+extern statetype s_keenslide2;\r
+extern statetype s_keenslide3;\r
+extern statetype s_keenslide4;\r
+extern statetype s_keenpoleshoot1;\r
+extern statetype s_keenpoleshoot2;\r
+extern statetype s_keenpoleshootup1;\r
+extern statetype s_keenpoleshootup2;\r
+extern statetype s_keenpoleshootdown1;\r
+extern statetype s_keenpoleshootdown2;\r
+extern statetype s_keenwalk1;\r
+extern statetype s_keenwalk2;\r
+extern statetype s_keenwalk3;\r
+extern statetype s_keenwalk4;\r
+extern statetype s_keenpogodown;\r
+extern statetype s_keenpogo;\r
+extern statetype s_keenpogo2;\r
+extern statetype s_keenjump1;\r
+extern statetype s_keenjump2;\r
+extern statetype s_keenjump3;\r
+extern statetype s_keenjump4;\r
+extern statetype s_keenairshoot1;\r
+extern statetype s_keenairshoot2;\r
+extern statetype s_keenairshoot3;\r
+extern statetype s_keenairshootup1;\r
+extern statetype s_keenairshootup2;\r
+extern statetype s_keenairshootup3;\r
+extern statetype s_keenairshootdown1;\r
+extern statetype s_keenairshootdown2;\r
+extern statetype s_keenairshootdown3;\r
+extern statetype s_keenholdon;\r
+extern statetype s_keenholdon2;\r
+extern statetype s_keenclimbup;\r
+extern statetype s_keenclimbup2;\r
+extern statetype s_keenclimbup3;\r
+extern statetype s_keenclimbup4;\r
+extern statetype s_keenclimbup5;\r
+\r
+extern Sint16 slopespeed[8];\r
+extern Sint16 polexspeed[3];\r
+\r
+extern Sint16 shotsinclip[4];\r
+extern Sint16 bonussound[];\r
+extern Sint16 bonuspoints[];\r
+extern Sint16 bonussprite[];\r
+\r
+extern Uint16 zeromap;\r
+\r
+extern Sint16 singlegravity;\r
+extern Sint16 jumptime;\r
+extern Sint32 leavepoletime;\r
+extern Sint16 moonok;\r
+\r
+void SpawnKeen(Sint16 x, Sint16 y, Sint16 dir);\r
+boolean CheckGrabPole(objtype *ob);\r
+boolean CheckEnterHouse(objtype *ob);\r
+void WalkSound1(objtype *ob);\r
+void WalkSound2(objtype *ob);\r
+void KeenStandThink(objtype *ob);\r
+void KeenPauseThink(objtype *ob);\r
+void KeenReadThink(objtype *ob);\r
+void KeenLookUpThink(objtype *ob);\r
+void KeenLookDownThink(objtype *ob);\r
+void KeenWalkThink(objtype *ob);\r
+void T_LineUp(objtype *ob);\r
+void KeenEnterThink(objtype *ob);\r
+void KeenSwitchThink(objtype *ob);\r
+void KeenKeyThink(objtype *ob);\r
+void KeenAirThink(objtype *ob);\r
+void KeenBounceThink(objtype *ob);\r
+void KeenPogoThink(objtype *ob);\r
+void PoleActions(objtype *ob);\r
+void KeenPoleThink(objtype *ob);\r
+void KeenClimbThink(objtype *ob);\r
+void KeenDropThink(objtype *ob);\r
+void KeenDropDownThink(objtype *ob);\r
+void KeenHoldThink(objtype *ob);\r
+void KeenShootThink(objtype *ob);\r
+void T_PullUp1(objtype *ob);\r
+void T_PullUp2(objtype *ob);\r
+void T_PullUp3(objtype *ob);\r
+void T_PulledUp(objtype *ob);\r
+void KeenDieThink(objtype *ob);\r
+void KillKeen(void);\r
+void KeenContact(objtype *ob, objtype *hit);\r
+void KeenPosContact(objtype *ob, objtype *hit);\r
+void HandleRiding(objtype *ob);\r
+void TileBonus(Uint16 x, Uint16 y, Uint16 bonus);\r
+void GiveDrop(Uint16 x, Uint16 y);\r
+void CheckInTiles(objtype *ob);\r
+void KeenSimpleReact(objtype *ob);\r
+void KeenStandReact(objtype *ob);\r
+void KeenWalkReact(objtype *ob);\r
+void KeenAirReact(objtype *ob);\r
+void KeenPogoReact(objtype *ob);\r
+void KeenPoleReact(objtype *ob);\r
+\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                               CK_KEEN2 DEFINITIONS\r
+\r
+=============================================================================\r
+*/\r
+\r
+extern statetype s_score;\r
+extern statetype s_demo;\r
+void SpawnScore(void);\r
+void UpdateScore(objtype *ob);\r
+void DrawDemoPlaque(objtype *ob);\r
+\r
+extern statetype s_worldkeen;\r
+extern statetype s_worldkeenwave1;\r
+extern statetype s_worldkeenwave2;\r
+extern statetype s_worldkeenwave3;\r
+extern statetype s_worldkeenwave4;\r
+extern statetype s_worldkeenwave5;\r
+extern statetype s_worldkeenwalk;\r
+void SpawnWorldKeen(Sint16 x, Sint16 y);\r
+#ifdef KEEN5\r
+void SpawnWorldKeenPort(Uint16 tileX, Uint16 tileY);\r
+#endif\r
+void CheckEnterLevel(objtype *ob);\r
+void T_KeenWorld(objtype *ob);\r
+void T_KeenWorldWalk(objtype *ob);\r
+void CheckWorldInTiles(objtype *ob);\r
+\r
+#ifdef KEEN4\r
+extern statetype s_keenonfoot1;\r
+extern statetype s_keenonfoot2;\r
+extern statetype s_worldswim;\r
+void T_FootFly(objtype *ob);\r
+void T_KeenWorldSwim(objtype *ob);\r
+#endif\r
+\r
+#ifdef KEEN5\r
+extern statetype s_worldelevate;\r
+void T_Elevate(objtype *ob);\r
+#endif\r
+\r
+extern statetype s_flagwave1;\r
+extern statetype s_flagwave2;\r
+extern statetype s_flagwave3;\r
+extern statetype s_flagwave4;\r
+void SpawnFlag(Sint16 x, Sint16 y);\r
+\r
+#ifndef KEEN5\r
+extern statetype s_throwflag0;\r
+extern statetype s_throwflag1;\r
+extern statetype s_throwflag2;\r
+extern statetype s_throwflag3;\r
+extern statetype s_throwflag4;\r
+extern statetype s_throwflag5;\r
+extern statetype s_throwflag6;\r
+void SpawnThrowFlag(Sint16 x, Sint16 y);\r
+void TossThink(objtype *ob);\r
+void PathThink(objtype *ob);\r
+void FlagAlign(objtype *ob);\r
+#endif\r
+\r
+extern statetype s_stunray1;\r
+extern statetype s_stunray2;\r
+extern statetype s_stunray3;\r
+extern statetype s_stunray4;\r
+extern statetype s_stunhit;\r
+extern statetype s_stunhit2;\r
+void SpawnShot(Uint16 x, Uint16 y, Direction dir);\r
+void ExplodeShot(objtype *ob);\r
+void T_Shot(objtype *ob);\r
+void R_Shot(objtype *ob);\r
+\r
+extern statetype s_door1;\r
+extern statetype s_door2;\r
+extern statetype s_door3;\r
+void DoorOpen(objtype *ob);\r
+\r
+#ifdef KEEN5\r
+extern statetype s_carddoor;\r
+void CardDoorOpen(objtype *ob);\r
+#endif\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                               OTHER DEFINITIONS\r
+\r
+=============================================================================\r
+*/\r
+\r
+#if defined KEEN4\r
+#include "K4_DEF.H"\r
+#elif defined KEEN5\r
+#include "K5_DEF.H"\r
+#elif defined KEEN6\r
+#include "K6_DEF.H"\r
+#endif\r
+\r
+#endif
\ No newline at end of file
diff --git a/16/keen456/KEEN4-6/CK_DEMO.C b/16/keen456/KEEN4-6/CK_DEMO.C
new file mode 100755 (executable)
index 0000000..5004cf3
--- /dev/null
@@ -0,0 +1,2132 @@
+/* Reconstructed Commander Keen 4-6 Source Code\r
+ * Copyright (C) 2021 K1n9_Duk3\r
+ *\r
+ * This file is loosely based on:\r
+ * Keen Dreams Source Code\r
+ * Copyright (C) 2014 Javier M. Chavez\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License along\r
+ * with this program; if not, write to the Free Software Foundation, Inc.,\r
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+ */\r
+\r
+#include "CK_DEF.H"\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                GLOBAL VARIABLES\r
+\r
+=============================================================================\r
+*/\r
+\r
+boolean scorescreenkludge;\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                LOCAL VARIABLES\r
+\r
+=============================================================================\r
+*/\r
+\r
+#if GRMODE == EGAGR\r
+\r
+Uint8 starcolors[17] = STARPALETTE;\r
+Uint16 plaquenum[4] = {IDSOFTPIC, PROGTEAMPIC, ARTISTPIC, DIRECTORPIC};\r
+Uint8 termcolors[17] = INTROPALETTE;\r
+Uint8 termcolors2[17] = SHRINKPALETTE;\r
+\r
+Uint8 ortoend[8] = {0xFF, 0x7F, 0x3F, 0x1F, 0x0F, 0x07, 0x03, 0x01};\r
+Uint8 andtoend[8] = {0x00, 0x80, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC, 0xFE};\r
+\r
+/////////////////////////////////////////////////////////////////////////////\r
+// uninitialized variables:\r
+/////////////////////////////////////////////////////////////////////////////\r
+\r
+typedef struct {\r
+       Uint16 height;\r
+       Uint16 width;\r
+       Uint16 rowofs[200];\r
+} shapehead;\r
+\r
+typedef shapehead _seg * shapeseg;\r
+\r
+// text crawl variables:\r
+memptr linecode;\r
+void far *linestarts[200];\r
+Uint16 sourceline[200];\r
+Uint16 masterlines;\r
+void far *routine;\r
+memptr sourcepic;\r
+memptr bittables;\r
+\r
+// terminator intro variables:\r
+shapeseg commander;\r
+shapeseg keen;\r
+shapeseg both;\r
+memptr scaletable;\r
+memptr cmdrshifts[8];\r
+Sint16 commanderbwide;\r
+Uint16 lastsource;\r
+Uint16 keenstart;\r
+memptr basepl[5];\r
+Uint16 baseplwidth[5];\r
+Uint16 baseplheight[5];\r
+memptr plaqueseg;\r
+Uint16 plaquewidth;\r
+Uint16 plaquewidthwords;\r
+Uint16 plaqueheight;\r
+Uint16 plaqueplane;\r
+Uint16 plaquedelta;\r
+Uint16 *shiftptr;\r
+Uint16 planeon;\r
+Sint16 drawheight;\r
+Uint16 source2;\r
+static Uint16 t_dest;\r
+static Sint16 plaque;\r
+static Sint16 plaquephase;\r
+static Sint16 plaquey;\r
+static Sint16 lastframe;\r
+static Sint16 pageon;\r
+static Sint16 prevbottom[2];\r
+Uint16 pageofs;\r
+Uint16 byteadjust;\r
+\r
+#endif // if GRMODE == EGAGR\r
+\r
+//===========================================================================\r
+\r
+/*\r
+============================\r
+=\r
+= CheckLastScan\r
+=\r
+============================\r
+*/\r
+\r
+void CheckLastScan(void)\r
+{\r
+       if (LastScan)\r
+       {\r
+               if (storedemo)\r
+               {\r
+                       playstate = ex_resetgame;\r
+                       restartgame = gd_Normal;\r
+                       IN_ClearKeysDown();\r
+                       NewGame();\r
+               }\r
+#ifndef KEEN6\r
+               else if (LastScan == sc_F1)\r
+               {\r
+                       HelpScreens();\r
+               }\r
+#endif\r
+               else\r
+               {\r
+                       US_ControlPanel();\r
+                       if (restartgame)\r
+                       {\r
+                               playstate = ex_resetgame;\r
+                       }\r
+                       else if (loadedgame)\r
+                       {\r
+                               playstate = ex_loadedgame;\r
+                       }\r
+               }\r
+       }\r
+}\r
+\r
+#if GRMODE == EGAGR\r
+/*\r
+=============================================================================\r
+\r
+                                                       TERMINATOR INTRO\r
+\r
+=============================================================================\r
+*/\r
+\r
+/*\r
+============================\r
+=\r
+= LoadPlaque\r
+=\r
+============================\r
+*/\r
+\r
+void LoadPlaque(Sint16 index)\r
+{\r
+       Sint16 LocatePlaque(Sint16 elapsed);\r
+\r
+       Uint16 chunk, picnum, width, height, planesize, i;\r
+       Uint8 far *source;\r
+       Uint16 far *dest;\r
+\r
+       //\r
+       // cache the pic and get pic size\r
+       //\r
+       chunk = plaquenum[index];\r
+       CA_CacheGrChunk(chunk);\r
+       picnum = chunk - STARTPICS;\r
+       baseplwidth[index] = width = pictable[picnum].width;\r
+       baseplheight[index] = height = pictable[picnum].height;\r
+       planesize = width * height * 2;\r
+\r
+       //\r
+       // allocate buffer and convert pic into to our format\r
+       // (convert bytes to word indices for faster shift-drawing)\r
+       //\r
+       MM_GetPtr(&basepl[index], planesize*2); // 2 planes\r
+       source = grsegs[chunk];\r
+       dest = basepl[index];\r
+       for (i=0; i<planesize; i++)\r
+       {\r
+               *dest++ = *source++ << 1;\r
+       }\r
+\r
+       //\r
+       // pic in original format is no longer needed\r
+       //\r
+       MM_FreePtr(&grsegs[chunk]);\r
+}\r
+\r
+/*\r
+============================\r
+=\r
+= DrawPlaque\r
+=\r
+============================\r
+*/\r
+\r
+void DrawPlaque(Sint16 elapsed, Uint16 x)\r
+{\r
+       Uint16 shift, xb;\r
+       Sint16 y, bottom, oldbottom;\r
+       Uint16 eraseheight, skip, screenoff;\r
+\r
+       shift = x & 7;\r
+       xb = (pageofs + (x / 8)) + (20 - (plaquewidth >> 1));\r
+\r
+       EGAMAPMASK(12); // write to "red" and "intensity" plane (for erasing old pic)\r
+\r
+       //\r
+       // update position (and pic number)\r
+       //\r
+       y = LocatePlaque(elapsed);\r
+\r
+       //\r
+       // erase leftovers of the previous frame\r
+       //\r
+       bottom = y + plaqueheight;\r
+       if (bottom < 0)\r
+               bottom = 0;\r
+       oldbottom = prevbottom[pageon];\r
+       if (bottom < 200 && oldbottom > bottom)\r
+       {\r
+               eraseheight = oldbottom - bottom;\r
+               screenoff = xb + ylookup[bottom];\r
+               asm {\r
+                       mov     es, screenseg;\r
+                       mov     bx, linewidth;\r
+                       sub     bx, plaquewidthwords;\r
+                       sub     bx, plaquewidthwords;\r
+                       mov     di, screenoff;\r
+                       mov     dx, eraseheight;\r
+                       mov     si, plaquewidthwords;\r
+                       xor     ax, ax;\r
+               }\r
+eraseloop:\r
+               asm {\r
+                       mov     cx, si;\r
+                       rep stosw;\r
+                       add     di, bx;\r
+                       dec     dx;\r
+                       jnz     eraseloop;\r
+               }\r
+       }\r
+       if (bottom > 200)\r
+               bottom = 200;\r
+       prevbottom[pageon] = bottom;\r
+\r
+       //\r
+       // draw the (new) pic at the new position\r
+       //\r
+       drawheight = plaqueheight;\r
+       skip = 0;\r
+       if (y < 0)\r
+       {\r
+               skip = -y * (plaquewidth << 1);\r
+               drawheight += y;\r
+               y = 0;\r
+       }\r
+       else if (y + plaqueheight > 200)\r
+       {\r
+               drawheight = 200 - y;\r
+       }\r
+       source2 = skip + plaqueplane;\r
+       if (drawheight > 0)\r
+       {\r
+               shiftptr = shifttabletable[shift];\r
+               t_dest = xb + ylookup[y];\r
+               asm {\r
+                       mov     bx, skip;\r
+                       push    bp;\r
+                       mov     bp, shiftptr;\r
+                       mov     es, screenseg;\r
+                       mov     ds, plaqueseg;\r
+                       mov     ah, 4;\r
+                       mov     BYTE PTR ss:planeon, ah;\r
+               }\r
+planeloop:\r
+               asm {\r
+                       mov     dx, SC_INDEX;\r
+                       mov     al, SC_MAPMASK;\r
+                       out     dx, ax;\r
+                       mov     dx, ss:drawheight;\r
+                       mov     di, ss:t_dest;\r
+               }\r
+yloop:\r
+               asm {\r
+                       mov     cx, ss:plaquewidth;\r
+                       xor     al, al;\r
+               }\r
+xloop:\r
+               asm {\r
+                       mov     si, [bx];\r
+                       add     bx, 2;\r
+                       xor     ah, ah;\r
+                       or      ax, [bp+si];\r
+                       stosb;\r
+                       mov     al, ah;\r
+                       loop    xloop;\r
+                       stosb;\r
+                       mov     WORD PTR es:[di], 0;\r
+                       add     di, ss:plaquedelta;\r
+                       dec     dx;\r
+                       jnz     yloop;\r
+                       mov     bx, ss:source2;\r
+                       mov     ah, BYTE PTR ss:planeon;\r
+                       shl     ah, 1;\r
+                       mov     BYTE PTR ss:planeon, ah;\r
+                       cmp     ah, 16;\r
+                       jnz     planeloop;\r
+                       pop     bp;\r
+                       mov     ax, ss;\r
+                       mov     ds, ax;\r
+               }\r
+       }\r
+}\r
+\r
+/*\r
+============================\r
+=\r
+= LocatePlaque\r
+=\r
+============================\r
+*/\r
+\r
+Sint16 LocatePlaque(Sint16 elapsed)\r
+{\r
+       switch (plaquephase)\r
+       {\r
+       case -1:\r
+               //\r
+               // pic starts to appear\r
+               //\r
+               plaqueseg = basepl[plaque];\r
+               plaquewidth = baseplwidth[plaque];\r
+               plaquewidthwords = (plaquewidth + 3) >> 1;\r
+               plaqueheight = baseplheight[plaque];\r
+               plaquedelta = linewidth - (plaquewidth + 1);\r
+               plaqueplane = (plaquewidth * plaqueheight) << 1;\r
+               plaquephase++;\r
+               lastframe = elapsed;\r
+               plaquey = 240;\r
+               // no break or return here!\r
+       case 0:\r
+               //\r
+               // pic is moving from the bottom to the center of the screen\r
+               //\r
+               plaquey -= (elapsed - lastframe) << 1;\r
+               if (plaquey < 100)\r
+               {\r
+                       plaquey = 100;\r
+                       plaquephase++;\r
+               }\r
+               lastframe = elapsed;\r
+               return plaquey - (plaqueheight >> 1);\r
+\r
+       case 1:\r
+               //\r
+               // pic is staying at the center position\r
+               //\r
+               if (elapsed - lastframe > 200)\r
+               {\r
+                       plaquephase++;\r
+                       lastframe = elapsed;\r
+               }\r
+               return 100 - (plaqueheight >> 1);\r
+\r
+       case 2:\r
+               //\r
+               // pic is moving up from the center to the top of the screen\r
+               //\r
+               plaquey -= (elapsed - lastframe) << 1;\r
+               if (plaquey < -40)\r
+               {\r
+                       plaquey = -40;\r
+                       if (++plaque < 4)\r
+                       {\r
+                               plaquephase = -1;\r
+                       }\r
+                       else\r
+                       {\r
+                               plaquephase = 3;\r
+                       }\r
+               }\r
+               lastframe = elapsed;\r
+               return plaquey - (plaqueheight >> 1);\r
+       }\r
+\r
+       return -40;\r
+}\r
+\r
+/*\r
+============================\r
+=\r
+= SlideLetters\r
+=\r
+============================\r
+*/\r
+\r
+void SlideLetters(void)\r
+{\r
+       Sint16 x, cPosX, screenxb;\r
+       Uint16 elapsed, totaltics, dstofs;\r
+       Sint16 cStart, cEnd, cTotalMove;\r
+       Uint16 shift, srcseg, srcofs;\r
+       Sint16 clearleft, copywidth, clearright;\r
+       Uint16 srcdif, dstdif;\r
+       Sint32 now;\r
+\r
+       //\r
+       // set up characteristics of the animation\r
+       //\r
+       EGAWRITEMODE(0);\r
+       EGAREADMAP(0);  // useless...\r
+\r
+       keenstart = keen->width + 200;\r
+       EGAREADMAP(1);  // also useless ... I think...\r
+\r
+       cEnd = 120 - commander->width;\r
+       cStart = 320;\r
+       cTotalMove = cEnd - cStart;\r
+       totaltics = abs(cTotalMove);\r
+\r
+       pageofs = pageon = 0;\r
+       lasttimecount = TimeCount;\r
+       while (TimeCount == lasttimecount);\r
+       lasttimecount = TimeCount;\r
+\r
+       for (elapsed=0; elapsed <= totaltics; elapsed += tics)\r
+       {\r
+               //\r
+               // draw the credits pic\r
+               //\r
+               x = ((Sint32)keenstart * (Sint32)(totaltics-elapsed)) / (Sint32)totaltics;\r
+               DrawPlaque(elapsed, x);\r
+\r
+               //\r
+               // get ready to draw draw the "COMMANDER" pic\r
+               //\r
+               cPosX = cStart + ((Sint32)cTotalMove * (Sint32)elapsed) / (Sint32)totaltics;\r
+               cPosX += x & 7;\r
+               screenxb = (cPosX + 0x800) / 8 + -0x100;\r
+               shift = (cPosX + 0x800) & 7;\r
+               srcseg = FP_SEG(cmdrshifts[shift]);\r
+               srcofs = 0;\r
+               dstofs = pageofs + x / 8;\r
+               if (screenxb > 0)\r
+               {\r
+                       clearleft = (screenxb + 1) / 2;\r
+                       if (screenxb & 1)\r
+                               dstofs--;\r
+                       copywidth = 21 - clearleft;\r
+                       clearright = 0;\r
+               }\r
+               else if (-commanderbwide + 40 < screenxb)\r
+               {\r
+                       clearleft = 0;\r
+                       copywidth = 21;\r
+                       clearright = 0;\r
+                       srcofs -= screenxb;\r
+               }\r
+               else\r
+               {\r
+                       clearleft = 0;\r
+                       copywidth = (commanderbwide + screenxb) / 2;\r
+                       clearright = 21 - copywidth;\r
+                       srcofs -= screenxb;\r
+               }\r
+               srcdif = commanderbwide - copywidth*2;\r
+               dstdif = 248 - (clearleft + copywidth + clearright)*2;\r
+\r
+               //\r
+               // draw "COMMANDER" pic\r
+               //\r
+               EGAMAPMASK(2);\r
+\r
+               asm {\r
+                       mov     di, dstofs;\r
+                       mov     es, screenseg;\r
+                       mov     si, srcofs;\r
+                       mov     lastsource, si;\r
+                       mov     ds, srcseg;\r
+                       mov     dx, 200;\r
+               }\r
+yloop:\r
+               asm {\r
+                       xor     ax, ax;\r
+                       mov     cx, clearleft;\r
+                       rep stosw;\r
+                       mov     cx, copywidth;\r
+                       rep movsw;\r
+                       xor     ax, ax;\r
+                       mov     cx, clearright;\r
+                       rep stosw;\r
+                       test    dx, 1;\r
+                       jnz     oddline;\r
+                       mov     si, ss:lastsource;\r
+                       jmp     nextline;\r
+               }\r
+oddline:\r
+               asm {\r
+                       add     si, srcdif;\r
+                       mov     ss:lastsource, si;\r
+               }\r
+nextline:\r
+               asm {\r
+                       add     di, dstdif;\r
+                       dec     dx;\r
+                       jnz     yloop;\r
+                       mov     ax, ss;\r
+                       mov     ds, ax;\r
+               }\r
+\r
+               //\r
+               // page flip\r
+               //\r
+               VW_SetScreen(pageofs + x / 8, x & 7);\r
+               pageon ^= 1;\r
+               if (pageon)\r
+               {\r
+                       pageofs = 124;\r
+               }\r
+               else\r
+               {\r
+                       pageofs = 0;\r
+               }\r
+\r
+               //\r
+               // handle timing\r
+               //\r
+               do\r
+               {\r
+                       now = TimeCount;\r
+                       tics = now - lasttimecount;\r
+               } while (tics < 2);\r
+               lasttimecount = now;\r
+\r
+               //\r
+               // handle input\r
+               //\r
+               if (IN_IsUserInput() && LastScan != sc_F1)\r
+               {\r
+                       LastScan = sc_Space;\r
+               }\r
+               if (LastScan)\r
+                       return;\r
+       }\r
+\r
+       byteadjust = x / 8;\r
+}\r
+\r
+/*\r
+============================\r
+=\r
+= DrawScan\r
+=\r
+============================\r
+*/\r
+\r
+void DrawScan(Sint16 far *source, Uint8 far *dest)\r
+{\r
+       register Uint16 x;\r
+       register Sint16 w;\r
+       register Uint16 val;\r
+       register Uint16 i;\r
+\r
+       val = x = 0;\r
+       for (;;)\r
+       {\r
+               //\r
+               // first part: puts black pixels (<width> pixels wide)\r
+               //\r
+               w = *source++;\r
+               if (w == -1)\r
+               {\r
+                       *dest++ = val;\r
+                       *dest = 0;\r
+                       return;\r
+               }\r
+\r
+               x += w;\r
+               if (x > 7)\r
+               {\r
+                       *dest++ = val;\r
+                       val = 0;\r
+                       i = (x / 8) - 1;\r
+                       while (i--)\r
+                       {\r
+                               *dest++ = 0;\r
+                       }\r
+                       x &= 7;\r
+               }\r
+\r
+               //\r
+               // second part: puts white pixels (<width> pixels wide)\r
+               //\r
+               w = *source++;\r
+               if (w == -1)\r
+               {\r
+                       *dest++ = val;\r
+                       *dest = 0;\r
+                       return;\r
+               }\r
+\r
+               val |= ortoend[x];\r
+               x += w;\r
+               if (x > 7)\r
+               {\r
+                       *dest++ = val;\r
+                       val = 0xFF;\r
+                       i = (x / 8) - 1;\r
+                       while (i--)\r
+                       {\r
+                               *dest++ = 0xFF;\r
+                       }\r
+                       x &= 7;\r
+               }\r
+               val &= andtoend[x];\r
+       }\r
+}\r
+\r
+/*\r
+============================\r
+=\r
+= BuildScaleShape\r
+=\r
+============================\r
+*/\r
+\r
+void BuildScaleShape(void)\r
+{\r
+       Sint16 px, w;\r
+       Sint16 far *source;\r
+       Sint16 far *dest;\r
+       Sint16 y;\r
+\r
+       MM_GetPtr((memptr*)&both, 30000);\r
+       dest = MK_FP(FP_SEG(both), sizeof(shapehead));\r
+\r
+       for (y=0; y<200; y++)\r
+       {\r
+               both->rowofs[y] = FP_OFF(dest);\r
+               px = 0;\r
+\r
+               EGAREADMAP(1);  // this is pretty useless, we're not reading from EGA memory here\r
+\r
+               source = (Sint16 far *)((byte _seg *)commander + commander->rowofs[y]);\r
+               w = *source++;\r
+               do\r
+               {\r
+                       *dest++ = px;\r
+                       px = px + w;\r
+                       w = *source++;\r
+               } while (w != -1);\r
+\r
+               //\r
+               // insert an 80 pixel gap between "COMMANDER" and "KEEN"\r
+               //\r
+               // This assumes that the rightmost column(s) of the "COMMANDER"\r
+               // shape are black. Otherwise the gap would be filled with\r
+               // white pixels and the "KEEN" image would use inverted colors\r
+               // as a result.\r
+               //\r
+               px += 80;\r
+\r
+               EGAREADMAP(0);  // this is pretty useless, we're not reading from EGA memory here\r
+\r
+               source = (Sint16 far *)((byte _seg *)keen + keen->rowofs[y]);\r
+               source++;       // kludgy bit, causes errors when left egde of "KEEN" is no rectangle\r
+               w = *source++;\r
+               do\r
+               {\r
+                       *dest++ = px;\r
+                       px = px + w;\r
+                       w = *source++;\r
+               } while (w != -1);\r
+\r
+               *dest++ = px;   // put last value\r
+               *dest++ = -1;   // put end-of-line\r
+       }\r
+}\r
+\r
+/*\r
+============================\r
+=\r
+= ScalePointScan\r
+=\r
+============================\r
+*/\r
+\r
+void ScalePointScan(Sint16 far *rowptr, Sint16 y, Sint16 toleft, Sint16 far *scaletable)\r
+{\r
+       Uint8 far *dest;\r
+       Sint16 left, endx;\r
+       Uint16 w, val, x, right;\r
+       register Sint16 px, sx;\r
+\r
+       val = x = 0;\r
+       endx = 320 - toleft;\r
+       dest = MK_FP(0xA000, pageofs + byteadjust + ylookup[y]);\r
+\r
+       if (toleft < 0)\r
+       {\r
+               left = -toleft;\r
+               val = 0;\r
+               x = 0;\r
+\r
+               for (;;)\r
+               {\r
+                       px = *rowptr++;\r
+                       sx = scaletable[px];\r
+                       if (sx > left)\r
+                               goto drawwhite;\r
+\r
+                       px = *rowptr++;\r
+                       sx = scaletable[px];\r
+                       if (sx > left)\r
+                               goto drawblack;\r
+               }\r
+       }\r
+\r
+       //\r
+       // regular\r
+       //\r
+       val = 0;\r
+       x = toleft & 7;\r
+       dest += (toleft >> 3);\r
+       left = 0;\r
+       rowptr++;       // the first value is always 0, we need the next value\r
+drawloop:\r
+       px = *rowptr++;\r
+       sx = scaletable[px];\r
+\r
+       //\r
+       // draw/add black pixels\r
+       //\r
+drawblack:\r
+       w = sx - left;\r
+       left = sx;\r
+       x += w;\r
+       if (x > 7)\r
+       {\r
+               asm {\r
+                       les     di, dest;\r
+                       mov     al, BYTE PTR val;\r
+                       stosb;\r
+                       mov     cx, x;\r
+                       shr     cx, 1;\r
+                       shr     cx, 1;\r
+                       shr     cx, 1;\r
+                       dec     cx;\r
+                       xor     al, al;\r
+                       mov     BYTE PTR val, al;\r
+                       rep stosb;\r
+                       and     x, 7;\r
+                       mov     WORD PTR dest, di;\r
+               }\r
+       }\r
+\r
+       //\r
+       // stop if the right side of the screen is reached\r
+       //\r
+       if (sx > endx)\r
+               return;\r
+\r
+       //\r
+       // stop if the end of the image row is reached\r
+       // \r
+       // This is only checked after drawing the black part, so the\r
+       // combined shape must not end with white pixels on the right.\r
+       // That means the rightmost column(s) of the "KEEN" shape must\r
+       // always be black.\r
+       //\r
+       px = *rowptr++;\r
+       if (px == -1)\r
+               goto clearright;\r
+\r
+       sx = scaletable[px];\r
+\r
+       //\r
+       // draw/add white pixels\r
+       //\r
+drawwhite:\r
+       w = sx - left;\r
+       left = sx;\r
+       val |= ortoend[x];\r
+       x += w;\r
+       if (x > 7)\r
+       {\r
+               asm {\r
+                       les     di, dest;\r
+                       mov     al, BYTE PTR val;\r
+                       stosb;\r
+                       mov     cx, x;\r
+                       shr     cx, 1;\r
+                       shr     cx, 1;\r
+                       shr     cx, 1;\r
+                       dec     cx;\r
+                       mov     al, 255;\r
+                       mov     BYTE PTR val, al;\r
+                       rep stosb;\r
+                       and     x, 7;\r
+                       mov     WORD PTR dest, di;\r
+               }\r
+       }\r
+\r
+       //\r
+       // stop if the right side of the screen is reached\r
+       //\r
+       if (sx > endx)\r
+               return;\r
+\r
+       val &= andtoend[x];\r
+       goto drawloop;\r
+\r
+       //\r
+       // clear the right side of the screen\r
+       //\r
+clearright:\r
+       w = 320 - left;\r
+       x += w;\r
+       if (x > 7)\r
+       {\r
+               *dest++ = val;\r
+               val = 0;\r
+               right = x / 8 - 1;\r
+               while (right--)\r
+               {\r
+                       *dest++ = 0;\r
+               }\r
+               x &= 7;\r
+               return;\r
+       }\r
+       return;\r
+}\r
+\r
+/*\r
+============================\r
+=\r
+= ScaleDown\r
+=\r
+============================\r
+*/\r
+\r
+void ScaleDown(void)\r
+{\r
+       Uint16 i;\r
+       Uint16 toleft, ticselapsed, ticstotal, scale, endscale, rownum, rowinc;\r
+       Sint32 now;\r
+       Sint16 far *rowptr;\r
+       Uint16 scaleheight, top, bottom, lastbottom[2];\r
+       Sint32 leftorigin;\r
+\r
+       //\r
+       // set our new palette\r
+       //\r
+       SetPalette(termcolors2);\r
+\r
+       EGAREADMAP(1);  // this is pretty useless, we're not reading from EGA memory here\r
+\r
+       leftorigin = 120l - commander->width;\r
+       BuildScaleShape();\r
+       MM_GetPtr(&scaletable, 2500*sizeof(Uint16));\r
+\r
+       scale = 0x100;          // 100%\r
+       endscale = 0x21;        // 13% (scale from 200px to 26px)\r
+       endscale = 0x21;        // redundant\r
+       lastbottom[0] = lastbottom[1] = 200;\r
+       ticselapsed = 1;\r
+       ticstotal = 30; // time for the whole shrinking animation\r
+\r
+       while (ticselapsed <= ticstotal)\r
+       {\r
+               //\r
+               // get current scaling\r
+               //\r
+               if (ticselapsed == ticstotal)\r
+               {\r
+                       scale = endscale;\r
+                       toleft = 0;\r
+                       top = 4;\r
+               }\r
+               else\r
+               {\r
+                       scale = 0x100 - ((0x100-endscale) * ticselapsed) / ticstotal;\r
+                       toleft = (leftorigin * (ticstotal - ticselapsed)) / ticstotal;\r
+                       top = (ticselapsed * 4) / ticstotal;\r
+               }\r
+\r
+               //\r
+               // build scale table:           scaletable[i] = (i*scale) / 0x100;\r
+               //\r
+               asm {\r
+                       xor     ax, ax;\r
+                       xor     dx, dx;\r
+                       mov     cx, 2500;\r
+                       mov     bx, scale;\r
+                       mov     es, scaletable;\r
+                       xor     di, di;\r
+               }\r
+l1:\r
+               asm {\r
+                       mov     es:[di], ah;\r
+                       inc     di;\r
+                       mov     es:[di], dl;\r
+                       inc     di;\r
+                       add     ax, bx;\r
+                       adc     dx, 0;\r
+                       loop    l1;\r
+               }\r
+\r
+               //\r
+               // wait... didn't we already do this?\r
+               //\r
+               if (ticselapsed == ticstotal)\r
+               {\r
+                       toleft = 0;\r
+               }\r
+               else\r
+               {\r
+                       toleft = (leftorigin * (ticstotal - ticselapsed)) / ticstotal;\r
+               }\r
+\r
+               //\r
+               // prepare scaled drawing process\r
+               //\r
+               scaleheight = ((Sint16 _seg *)scaletable)[200];\r
+               rownum = 0;\r
+               rowinc = 0x10000l / scale;\r
+               bufferofs = pageofs + byteadjust;\r
+\r
+               //\r
+               // erase stuff at the top\r
+               //\r
+               if (top > 0)\r
+               {\r
+                       VW_Bar(0, 0, 320, top, BLACK);\r
+               }\r
+\r
+               //\r
+               // draw the scaled shape\r
+               //\r
+               EGAWRITEMODE(0);\r
+               EGAMAPMASK(15);\r
+\r
+               for (i=0; i<scaleheight; i++)\r
+               {\r
+                       rowptr = (Sint16 far *)((byte _seg *)both + both->rowofs[rownum >> 8]);\r
+                       ScalePointScan(rowptr, i+top, toleft, scaletable);\r
+\r
+                       rownum += rowinc;\r
+               }\r
+\r
+               //\r
+               // erase leftovers at the bottom of the screen\r
+               //\r
+               bufferofs = pageofs + byteadjust;\r
+               bottom = scaleheight + top;\r
+               if (lastbottom[pageon] > bottom)\r
+               {\r
+                       VW_Bar(0, bottom, 320, lastbottom[pageon] - bottom, BLACK);\r
+                       lastbottom[pageon] = bottom;\r
+               }\r
+\r
+               //\r
+               // page flip\r
+               //\r
+               VW_SetScreen(pageofs+byteadjust, 0);\r
+               pageon ^= 1;\r
+               if (pageon)\r
+               {\r
+                       pageofs = 124;\r
+               }\r
+               else\r
+               {\r
+                       pageofs = 0;\r
+               }\r
+\r
+               //\r
+               // handle timing\r
+               //\r
+               now = TimeCount;\r
+               tics = now - lasttimecount;\r
+               lasttimecount = now;\r
+               if (tics > 8)\r
+                       tics = 8;       // don't skip too many frames on slow systems\r
+\r
+               if (ticselapsed == ticstotal)\r
+                       break;\r
+\r
+               ticselapsed += tics;\r
+               if (ticselapsed > ticstotal)\r
+                       ticselapsed = ticstotal;\r
+\r
+               //\r
+               // handle input\r
+               //\r
+               if (IN_IsUserInput() && LastScan != sc_F1)\r
+               {\r
+                       LastScan = sc_Space;\r
+               }\r
+               if (LastScan)\r
+                       return; // BUG: buffers aren't freed!\r
+       }\r
+\r
+       //\r
+       // free the buffers\r
+       //\r
+       MM_FreePtr(&scaletable);\r
+       MM_FreePtr((memptr*)&both);\r
+}\r
+\r
+/*\r
+============================\r
+=\r
+= FinishPage\r
+=\r
+============================\r
+*/\r
+\r
+void FinishPage(void)\r
+{\r
+       Sint16 swap, temp, i, n, x, y;\r
+       Uint16 ofs;\r
+       Sint16 top, bottom, delta;\r
+       Uint8 bitmask[] = {0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80};\r
+       Sint16 xtable[320], ytable[200];\r
+\r
+       //\r
+       // build lookup tables\r
+       //\r
+       for (i=0; i<320; i++)\r
+       {\r
+               xtable[i] = i;\r
+       }\r
+       for (i=0; i<320; i++)\r
+       {\r
+               swap = random(320);\r
+               temp = xtable[swap];\r
+               xtable[swap] = xtable[i];\r
+               xtable[i] = temp;\r
+       }\r
+       for (i=0; i<200; i++)\r
+       {\r
+               ytable[i] = xtable[i];\r
+       }\r
+\r
+       //\r
+       // set up display\r
+       //\r
+       VW_SetDefaultColors();\r
+       if (pageon)\r
+       {\r
+               bufferofs = byteadjust + 124;\r
+               displayofs = byteadjust;\r
+       }\r
+       else\r
+       {\r
+               bufferofs = byteadjust;\r
+               displayofs = byteadjust + 124;\r
+       }\r
+       VW_SetScreen(displayofs, 0);\r
+\r
+       //\r
+       // draw title pic to the non-displayed buffer\r
+       //\r
+       VW_DrawPic(0, 0, TITLEPICPIC);\r
+\r
+       //\r
+       // copy "random" pixels from the non-displayed area\r
+       // into the displayed area to create the "fizzle" effect\r
+       //\r
+       delta = displayofs - bufferofs;\r
+\r
+       //\r
+       // set ES register for the pixel copying code in the loops\r
+       //\r
+       // This is faster than setting the ES register in the loops,\r
+       // but you need to make sure nothing in the loops overwrites\r
+       // the ES register, otherwise the code won't work correctly.\r
+       //\r
+       asm     mov     es, screenseg;\r
+\r
+       for (i = 0; i< 360; i++)\r
+       {\r
+               top = i - 160;\r
+               if (top < 0)\r
+                       top = 0;\r
+\r
+               bottom = i;\r
+               if (bottom >= 200)\r
+                       bottom = 199;\r
+\r
+               for (y = top; y <= bottom; y++)\r
+               {\r
+                       ofs = bufferofs + ylookup[y];\r
+                       for (n=0; n<2; n++)\r
+                       {\r
+                               x = xtable[ytable[y]];\r
+                               if (++ytable[y] == 320)\r
+                               {\r
+                                       ytable[y] = 0;\r
+                               }\r
+\r
+                               //\r
+                               // set bitmask for our x value\r
+                               //\r
+                               asm     mov     cx, x;\r
+                               asm     mov     si, cx;\r
+                               asm     and     si, 7;\r
+                               asm     cli;\r
+                               asm     mov     dx, GC_INDEX;\r
+                               asm     mov     al, GC_BITMASK;\r
+                               asm     mov     ah, BYTE PTR bitmask[si];\r
+                               asm     out     dx, ax;\r
+                               asm     sti;\r
+\r
+                               //\r
+                               // set up source and dest index registers\r
+                               //\r
+                               asm     mov     si, ofs;\r
+                               asm     shr     cx, 1;\r
+                               asm     shr     cx, 1;\r
+                               asm     shr     cx, 1;\r
+                               asm     add     si, cx;\r
+                               asm     mov     di, si;\r
+                               asm     add     di, delta;\r
+\r
+                               //\r
+                               // copy the pixel data (all 4 planes)\r
+                               //\r
+                               // "blue" plane:\r
+                               asm     mov     dx, SC_INDEX;\r
+                               asm     mov     ax, SC_MAPMASK + 1*256;\r
+                               asm     out     dx, ax;\r
+                               asm     mov     dx, GC_INDEX;\r
+                               asm     mov     ax, GC_READMAP + 0*256;\r
+                               asm     out     dx, ax;\r
+                               asm     mov     bl, es:[si];\r
+                               asm     xchg    bl, es:[di];\r
+                               // "green" plane:\r
+                               asm     mov     dx, SC_INDEX;\r
+                               asm     mov     ax, SC_MAPMASK + 2*256;\r
+                               asm     out     dx, ax;\r
+                               asm     mov     dx, GC_INDEX;\r
+                               asm     mov     ax, GC_READMAP + 1*256;\r
+                               asm     out     dx, ax;\r
+                               asm     mov     bl, es:[si];\r
+                               asm     xchg    bl, es:[di];\r
+                               // "red" plane:\r
+                               asm     mov     dx, SC_INDEX;\r
+                               asm     mov     ax, SC_MAPMASK + 4*256;\r
+                               asm     out     dx, ax;\r
+                               asm     mov     dx, GC_INDEX;\r
+                               asm     mov     ax, GC_READMAP + 2*256;\r
+                               asm     out     dx, ax;\r
+                               asm     mov     bl, es:[si];\r
+                               asm     xchg    bl, es:[di];\r
+                               // "intensity" plane:\r
+                               asm     mov     dx, SC_INDEX;\r
+                               asm     mov     ax, SC_MAPMASK + 8*256;\r
+                               asm     out     dx, ax;\r
+                               asm     mov     dx, GC_INDEX;\r
+                               asm     mov     ax, GC_READMAP + 3*256;\r
+                               asm     out     dx, ax;\r
+                               asm     mov     bl, es:[si];\r
+                               asm     xchg    bl, es:[di];\r
+                       }\r
+               }\r
+\r
+               VW_WaitVBL(1);  // so the fizzle animation won't go super fast\r
+\r
+               if (IN_IsUserInput() && LastScan != sc_F1)\r
+               {\r
+                       LastScan = sc_Space;\r
+               }\r
+               if (LastScan)\r
+               {\r
+                       EGABITMASK(0xFF);\r
+                       EGAMAPMASK(15);\r
+                       return;\r
+               }\r
+       }\r
+\r
+       //\r
+       // clean up EGA registers\r
+       //\r
+       EGABITMASK(0xFF);\r
+       EGAMAPMASK(15);\r
+\r
+       //\r
+       // pause for 6 seconds\r
+       //\r
+       IN_UserInput(6 * TickBase, false);\r
+}\r
+\r
+/*\r
+============================\r
+=\r
+= Terminator\r
+=\r
+============================\r
+*/\r
+\r
+void Terminator(void)\r
+{\r
+       Uint16 i, shift, bufsize;\r
+       Sint16 far *source;\r
+       Uint8 far *dest;\r
+       Uint16 srcseg, destseg;\r
+       boolean pagefinished;\r
+       Uint16 rowofs[200];\r
+\r
+       pagefinished = false;\r
+       CA_SetAllPurge();\r
+       SetPaletteEx(colors[0]);        // all black\r
+       VW_ClearVideo(BLACK);\r
+       VW_SetLineWidth(248);   // 1984 pixels total, we're using 992 per "page"\r
+\r
+       CA_CacheGrChunk(TITLEPICPIC);\r
+       CA_CacheGrChunk(BIGCOMMANDER);\r
+       CA_CacheGrChunk(BIGKEEN);\r
+       keen = grsegs[BIGKEEN];\r
+       commander = grsegs[BIGCOMMANDER];\r
+\r
+       EGAMAPMASK(1);\r
+\r
+       keenstart = keen->width + 200;\r
+       VW_SetScreen((keenstart/8)+1, 0);\r
+\r
+       //\r
+       // draw the "KEEN" pic (to first "page")\r
+       //\r
+       for (i=0; i<200; i++)\r
+       {\r
+               source = (Sint16 far *)((byte _seg *)keen + keen->rowofs[i]);\r
+               dest = MK_FP(0xA000, ylookup[i]);\r
+               dest += 25;     // 25 bytes -> 200 pixels\r
+               DrawScan(source, dest);\r
+       }\r
+       //\r
+       // copy pic from first "page" to second "page"\r
+       //\r
+       VW_ScreenToScreen(0, 124, 109, 200);\r
+\r
+       //\r
+       // create pre-shifted image buffers for the "COMMANDER" pic\r
+       // (only 100 pixels high instead of 200 pixels to save memory)\r
+       //\r
+       commanderbwide = (commander->width + 7) / 8;\r
+       commanderbwide = (commanderbwide + 3) & ~1;\r
+       bufsize = commanderbwide * 100; // half height\r
+       for (shift = 0; shift < 8; shift++)\r
+       {\r
+               MM_GetPtr(&cmdrshifts[shift], bufsize);\r
+       }\r
+\r
+       //\r
+       // re-assign shape pointers (memory manager might have moved the buffers)\r
+       //\r
+       keen = grsegs[BIGKEEN];\r
+       commander = grsegs[BIGCOMMANDER];\r
+\r
+       //\r
+       // draw the first (unshifted) version of the "COMMANDER" pic to the buffer\r
+       //\r
+       for (i=0; i<100; i++)\r
+       {\r
+               rowofs[i*2] = rowofs[i*2+1] = i * commanderbwide;\r
+               source = (Sint16 far *)((byte _seg *)commander + commander->rowofs[i*2]);\r
+               dest = (Uint8 _seg *)cmdrshifts[0] + rowofs[i*2];\r
+               DrawScan(source, dest);\r
+       }\r
+\r
+       //\r
+       // create the shifted versions of the "COMMANDER" pic\r
+       //\r
+       for (shift = 1; shift < 8; shift++)\r
+       {\r
+               srcseg = FP_SEG(cmdrshifts[shift-1]);\r
+               destseg = FP_SEG(cmdrshifts[shift]);\r
+               asm {\r
+                       mov     ds, srcseg;\r
+                       mov     es, destseg;\r
+                       mov     cx, bufsize;\r
+                       clc;\r
+                       xor     si, si;\r
+                       xor     di, di;\r
+               }\r
+l1:\r
+               asm {\r
+                       lodsb;\r
+                       rcr     al, 1;\r
+                       stosb;\r
+                       loop    l1;\r
+                       mov     ax, ss;\r
+                       mov     ds, ax;\r
+               }\r
+       }\r
+\r
+       //\r
+       // prepare (and set) the palettes\r
+       //\r
+       termcolors[16] = termcolors2[16] = termcolors[16] = bordercolor;\r
+       SetPalette(termcolors);\r
+\r
+       //\r
+       // cache the credits pics (they are converted into a special\r
+       // format to make shifted drawing easier during the animation)\r
+       //\r
+       for (i=0; i<4; i++)\r
+       {\r
+               LoadPlaque(i);\r
+       }\r
+\r
+       //\r
+       // play the animation\r
+       //\r
+       plaque = lastframe = 0;\r
+       plaquephase = -1;\r
+       SlideLetters();\r
+\r
+       //\r
+       // free some of the buffers\r
+       // (shrink animation needs additional memory)\r
+       //\r
+       for (i=0; i<4; i++)\r
+       {\r
+               MM_FreePtr(&basepl[i]);\r
+       }\r
+       for (shift=0; shift<8; shift++)\r
+       {\r
+               MM_FreePtr(&cmdrshifts[shift]);\r
+       }\r
+\r
+       //\r
+       // do the shrinking and fizzle fade animations\r
+       // (if intro wasn't aborted)\r
+       //\r
+       if (!LastScan)\r
+       {\r
+               ScaleDown();\r
+       }\r
+\r
+       if (!LastScan)\r
+       {\r
+               FinishPage();\r
+               pagefinished = true;\r
+       }\r
+\r
+       //\r
+       // free the remaining buffers\r
+       //\r
+       MM_SetPurge(&grsegs[BIGCOMMANDER], 3);\r
+       MM_SetPurge(&grsegs[BIGKEEN], 3);\r
+\r
+       //\r
+       // switch back to default graphics settings\r
+       //\r
+       VW_ClearVideo(BLACK);\r
+       VW_SetLineWidth(SCREENWIDTH);\r
+       VW_SetDefaultColors();\r
+       RF_FixOfs();\r
+       CA_ClearMarks();\r
+\r
+       //\r
+       // handle input and main menu stuff\r
+       //\r
+       if (LastScan == sc_None)\r
+       {\r
+               return;\r
+       }\r
+#ifndef KEEN6\r
+       if (LastScan == sc_F1)\r
+       {\r
+               HelpScreens();\r
+               return;\r
+       }\r
+#endif\r
+       if (!pagefinished)\r
+       {\r
+               RF_FixOfs();    //redundant\r
+               CA_CacheGrChunk(TITLEPICPIC);\r
+               VW_DrawPic(0, 0, TITLEPICPIC);\r
+               VW_SetScreen(bufferofs, 0);\r
+               IN_Ack();\r
+               CA_ClearMarks();\r
+               if (storedemo)\r
+               {\r
+                       playstate = ex_resetgame;\r
+                       restartgame = gd_Normal;\r
+                       IN_ClearKeysDown();\r
+                       NewGame();\r
+                       return;\r
+               }\r
+       }\r
+\r
+       US_ControlPanel();\r
+       if (restartgame)\r
+       {\r
+               playstate = ex_resetgame;\r
+       }\r
+       else if (loadedgame)\r
+       {\r
+               playstate = ex_loadedgame;\r
+       }\r
+}\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                       STAR WARS TEXT CRAWL\r
+\r
+=============================================================================\r
+*/\r
+\r
+/*\r
+============================\r
+=\r
+= BuildBitTables\r
+=\r
+============================\r
+*/\r
+\r
+void BuildBitTables(void)\r
+{\r
+       Uint16 bit1, bit2, i;\r
+       Uint8 far *buffer;\r
+\r
+       MM_GetPtr(&bittables, 0x4000);\r
+       buffer = bittables;\r
+\r
+       //\r
+       // generate a lookup table that maps the bits of the "texture" (bit1)\r
+       // to the appropriate bit for the screen position (bit2) to make the\r
+       // scaler code faster and smaller\r
+       //\r
+       // table[((7-b1)*8+(7-b2))*256+i] = (i & (1 << (7-b1))) ? (1 << (7-b2)) : 0;\r
+       //\r
+       for (bit1 = 1; bit1 < 0x100; bit1 <<= 1)\r
+       {\r
+               for (bit2 = 1; bit2 < 0x100; bit2 <<= 1)\r
+               {\r
+                       for (i = 0; i < 0x100; i++, buffer++)\r
+                       {\r
+                               if (i & bit1)\r
+                               {\r
+                                       *buffer = bit2;\r
+                               }\r
+                               else\r
+                               {\r
+                                       *buffer = 0;\r
+                               }\r
+                       }\r
+               }\r
+       }\r
+}\r
+\r
+/*\r
+============================\r
+=\r
+= CompileSWUpdate\r
+=\r
+============================\r
+*/\r
+\r
+void CompileSWUpdate(void)\r
+{\r
+       Sint16 y;\r
+       Uint16 i, width, scalestep, step;\r
+       Sint32 scale, rowof, xpos, size;\r
+       void far *buffer;\r
+       Uint8 srcoff, srcbit, bitpos;\r
+       Uint16 destoff, srcx, left, orindex, lastoff;\r
+\r
+       BuildBitTables();\r
+       size = 190000;\r
+       MM_GetPtr(&linecode, size);\r
+       buffer = linecode;\r
+       //\r
+       // Note: You should really lock the pointer to prevent the memmory manager\r
+       // from moving the buffer around. This code stores a bunch of pointers to\r
+       // this memory block in the linestarts array. Those pointers will not be\r
+       // updated when the memory manager moves the buffer around and the game\r
+       // might end up crashing (or worse) when trying to run the "code" at the\r
+       // memory location after the data was moved. The game starts playing music\r
+       // after this function is done, which may or may not cause the memory\r
+       // manager to move memory blocks around.\r
+       //\r
+\r
+       //\r
+       // move the buffer address into ES:DI (and keep it there)\r
+       //\r
+       asm     mov     es, WORD PTR buffer+2;\r
+       asm     mov     di, WORD PTR buffer;\r
+       //\r
+       // Since the address is kept in ES:DI, we must save and restore\r
+       // the ES register when calling other functions (push es / pop es).\r
+       // The Borland C compiler always saves and restores the DI register\r
+       // when a function modifies it, so we don't need to worry about\r
+       // that register. This is a bit of an ugly hack, but it makes this\r
+       // code a little faster and smaller.\r
+       //\r
+\r
+       scale = 320l << 11;\r
+       scalestep = (((Uint32)(320-40) << 11) / 200);   // roughly 1.4 pixels per step, going from 320 pixels to 40 pixels in 200 steps\r
+       rowof = 0;\r
+\r
+       for (y=199; y >= 0; y--)\r
+       {\r
+               //\r
+               // draw a blue line for the current row\r
+               //\r
+               asm     push    es;\r
+               VW_Hlin(0, 320, y, BLUE);\r
+               asm     pop     es;\r
+\r
+               //\r
+               // update the buffer variable with the current (normalized) ES:DI address\r
+               //\r
+               asm     mov     WORD PTR buffer, di;\r
+               asm     mov     WORD PTR buffer+2, es;\r
+\r
+               //\r
+               // store the address in the scaler lookup table\r
+               //\r
+               linestarts[y] = buffer;\r
+\r
+               //\r
+               // get current scaling factors\r
+               //\r
+               width = ((Uint16)((scale/2) >> 11)) << 1;       // some trickery to make sure width is even\r
+               sourceline[y] = (rowof >> 11);\r
+               step = (336l << 11) / width;\r
+               xpos = 0;\r
+               rowof += step;\r
+               left = 160 - (width >> 1);\r
+               destoff = ylookup[y] + left / 8;\r
+               bitpos = left & 7;\r
+\r
+               //\r
+               // generate the machine code\r
+               //\r
+               //              MOV     CX, SS\r
+               //              MOV     SS, AX\r
+               //              ADD     DI, <destoff>\r
+               //              XOR     AL, AL\r
+               //\r
+               asm     mov     ax, 0D18Ch;\r
+               asm     stosw;\r
+               asm     mov     ax, 0D08Eh;\r
+               asm     stosw;\r
+               asm     mov     ax, 0C781h;\r
+               asm     stosw;\r
+               asm     mov     ax, destoff;\r
+               asm     stosw;\r
+               asm     mov     ax, 0C030h;\r
+               asm     stosw;\r
+\r
+               lastoff = -1;\r
+               for (i=0; i<width; i++)\r
+               {\r
+                       srcx = (xpos >> 11);\r
+                       srcoff = (srcx / 8);\r
+                       srcbit = srcx & 7;\r
+\r
+                       orindex = ((7-srcbit)*8 + 7-bitpos) << 8;\r
+                       if (srcoff != lastoff)\r
+                       {\r
+                               //\r
+                               //              MOV     BL, [BP + <srcoff>]\r
+                               //\r
+                               asm     mov     ax, 5E8Ah;\r
+                               asm     stosw;\r
+                               asm     mov     al, srcoff;\r
+                               asm     stosb;\r
+\r
+                               lastoff = srcoff;\r
+                       }\r
+\r
+                       //\r
+                       //              OR              AL, [BX + <orindex>]\r
+                       //\r
+                       asm     mov     ax, 870Ah;\r
+                       asm     stosw;\r
+                       asm     mov     ax, orindex;\r
+                       asm     stosw;\r
+\r
+                       bitpos++;\r
+                       if (bitpos == 8)\r
+                       {\r
+                               bitpos = 0;\r
+\r
+                               //\r
+                               //              STOSB\r
+                               //              XOR     AL, AL\r
+                               //\r
+                               asm     mov     ax, 30AAh;\r
+                               asm     stosw;\r
+                               asm     mov     al, 0C0h;\r
+                               asm     stosb;\r
+                       }\r
+\r
+                       xpos += step;\r
+               }\r
+\r
+               if (bitpos)\r
+               {\r
+                       //\r
+                       //              STOSB\r
+                       //\r
+                       asm     mov     al, 0AAh;\r
+                       asm     stosb;\r
+               }\r
+               //\r
+               // generate end of subroutine\r
+               //\r
+               //              MOV     SS, CX\r
+               //              RETF\r
+               //\r
+               asm     mov     ax, 0D18Eh;\r
+               asm     stosw;\r
+               asm     mov     al, 0CBh;\r
+               asm     stosb;\r
+\r
+               //\r
+               // normalize ES:DI\r
+               //\r
+               asm     mov     ax, di;\r
+               asm     shr     ax, 1;\r
+               asm     shr     ax, 1;\r
+               asm     shr     ax, 1;\r
+               asm     shr     ax, 1;\r
+               asm     mov     bx, es;\r
+               asm     add     ax, bx;\r
+               asm     mov     es, ax;\r
+               asm     and     di, 0Fh;\r
+\r
+               //\r
+               // update scale value for next row\r
+               //\r
+               scale -= scalestep;\r
+\r
+               //\r
+               // replace the blue line with the row from the background image\r
+               //\r
+               asm     push    es;\r
+               VW_ScreenToScreen(ylookup[y] + 0x8000, ylookup[y], 40, 1);\r
+               asm     pop     es;\r
+\r
+               if (LastScan)\r
+                       return;\r
+       }\r
+}\r
+\r
+/*\r
+============================\r
+=\r
+= TranslateString\r
+=\r
+============================\r
+*/\r
+\r
+void TranslateString(char *text)\r
+{\r
+       char c;\r
+\r
+       while (*text)\r
+       {\r
+               c = *text;\r
+\r
+               if (c >= 'A' && c <= 'Z')\r
+               {\r
+                       c = c + -33;\r
+               }\r
+               else if (c >= 'a' && c <= 'z')\r
+               {\r
+                       c = c + -39;\r
+               }\r
+               else if (c == '.')\r
+               {\r
+                       c = 84;\r
+               }\r
+               else if (c == ',')\r
+               {\r
+                       c = 85;\r
+               }\r
+               else if (c == '-')\r
+               {\r
+                       c = 86;\r
+               }\r
+               else if (c == '"')\r
+               {\r
+                       c = 87;\r
+               }\r
+               else if (c == ' ')\r
+               {\r
+                       c = 88;\r
+               }\r
+               else if (c == '!')\r
+               {\r
+                       c = 89;\r
+               }\r
+               else if (c == '\'')\r
+               {\r
+                       c = 90;\r
+               }\r
+               else if (c != '\n')\r
+               {\r
+                       c = 84; // any unhandled character is drawn as '.'\r
+               }\r
+\r
+               *text++ = c;\r
+       }\r
+}\r
+\r
+/*\r
+============================\r
+=\r
+= DrawSWText\r
+=\r
+============================\r
+*/\r
+\r
+void DrawSWText(void)\r
+{\r
+       char far *text;\r
+       char *ptr;\r
+       char c;\r
+       char strbuf[80];\r
+\r
+       WindowX = 0;\r
+       WindowW = 336;\r
+       PrintY = 1;             // always leave the first line blank\r
+       bufferofs = 0;\r
+       panadjust = 0;\r
+       text = swtext;\r
+       masterlines = 0;\r
+\r
+       //\r
+       // draw the entire text to video memory\r
+       //\r
+       while (*text)\r
+       {\r
+               ptr = strbuf;\r
+               do\r
+               {\r
+                       c = *text++;\r
+                       *ptr++ = c;\r
+               } while (c != '\n' && c != '\0');\r
+               *ptr = '\0';\r
+\r
+               TranslateString(strbuf);\r
+\r
+               US_CPrint(strbuf);\r
+\r
+               bufferofs += ylookup[PrintY];\r
+               masterlines += PrintY;\r
+               PrintY = 0;\r
+       }\r
+\r
+       //\r
+       // allocate a buffer large enough to hold the entire text image\r
+       // and move the image data from video memory into that buffer\r
+       //\r
+       MM_GetPtr(&sourcepic, bufferofs);\r
+       EGAREADMAP(1);  // read from "green" plane (doesn't really matter from which plane we read)\r
+       movedata(screenseg, 0, FP_SEG(sourcepic), 0, bufferofs);\r
+\r
+       //\r
+       // erase the (first screen of the) text from video memory.\r
+       // we're going to display this area and copy the backgound pic\r
+       // here line-by-line as the scalers are generated and we don't\r
+       // want to have parts of the text still visible at that point.\r
+       //\r
+       bufferofs = 0;\r
+       VW_Bar(0, 0, 320, 200, BLACK);\r
+}\r
+\r
+/*\r
+============================\r
+=\r
+= ScrollSWText\r
+=\r
+============================\r
+*/\r
+\r
+void ScrollSWText(void)\r
+{\r
+       Sint32 now;\r
+       Uint16 pos;\r
+       Sint16 i, rowof;\r
+\r
+       tics = TimeCount = lasttimecount = 0;\r
+\r
+       EGAWRITEMODE(0);\r
+       EGAMAPMASK(8);  // only draw to the "intensity" plane (so we don't erase the backgound pic)\r
+\r
+       pos = 0;\r
+       while (masterlines + 400 >= pos)\r
+       {\r
+               for (i = 199; i >= 0; i--)\r
+               {\r
+                       rowof = pos - sourceline[i];\r
+                       if (rowof < 0 || rowof >= masterlines)\r
+                       {\r
+                               masterofs = 0;  // draw the first (blank) line of the buffer\r
+                       }\r
+                       else\r
+                       {\r
+                               masterofs = rowof * 42;\r
+                       }\r
+                       routine = linestarts[i];\r
+                       asm {\r
+                               mov     es, screenseg;\r
+                               mov     di, pageofs;\r
+                               mov     ds, bittables;\r
+                               push    bp;\r
+                               mov     bp, ss:masterofs;\r
+                               mov     ax, ss:sourcepic;\r
+                               xor     bh, bh;\r
+                               cli;    // disable interrupts (scaler changes register SS, so interrupts would be fatal!)\r
+                               call    ss:routine;\r
+                               sti;    // enable interrupts again\r
+                               pop     bp;\r
+                               mov     ax, ss;\r
+                               mov     ds, ax;\r
+                       }\r
+               }\r
+\r
+               VW_SetScreen(pageofs, 0);\r
+               pageon ^= 1;\r
+               pageofs = pageon << 15;\r
+\r
+               now = TimeCount;\r
+               tics = tics + (now - lasttimecount);\r
+               lasttimecount = now;\r
+               if (tics > 20)\r
+                       tics = 20;\r
+\r
+               pos = pos + tics / 4;\r
+               tics &= 3;\r
+\r
+               if (IN_IsUserInput() && LastScan != sc_F1)\r
+                       LastScan = sc_Space;\r
+\r
+               if (LastScan)\r
+                       break;\r
+       }\r
+}\r
+\r
+/*\r
+============================\r
+=\r
+= StarWars\r
+=\r
+============================\r
+*/\r
+\r
+void StarWars(void)\r
+{\r
+       SetPaletteEx(colors[0]);        // all black\r
+       VW_ClearVideo(BLACK);\r
+       VW_SetLineWidth(42);    // 336 pixels\r
+       VW_SetScreen(0, 0);\r
+       pageon = pageofs = 0;\r
+       CA_SetAllPurge();\r
+       CA_CacheGrChunk(STARTFONT+2);\r
+       fontnumber = 2;\r
+       DrawSWText();\r
+       fontnumber = 0;\r
+\r
+       CA_CacheGrChunk(SW_BACKGROUNDPIC);\r
+       bufferofs = 0x8000;\r
+       VW_DrawPic(0, 0, SW_BACKGROUNDPIC);\r
+       CA_SetAllPurge();\r
+       SetPaletteEx(starcolors);\r
+       bufferofs = 0;\r
+       CompileSWUpdate();\r
+\r
+       if (!LastScan)\r
+       {\r
+               StartMusic(STARWARSMUSIC);\r
+               ScrollSWText();\r
+               StopMusic();\r
+       }\r
+\r
+       MM_FreePtr(&linecode);\r
+       MM_FreePtr(&bittables);\r
+       MM_FreePtr(&sourcepic);\r
+\r
+       VW_ClearVideo(BLACK);\r
+       VW_SetLineWidth(SCREENWIDTH);\r
+       VW_SetDefaultColors();\r
+       RF_FixOfs();\r
+       CA_ClearMarks();\r
+\r
+       CheckLastScan();\r
+}\r
+\r
+#endif // if GRMODE == EGAGR\r
+\r
+//===========================================================================\r
+\r
+/*\r
+============================\r
+=\r
+= ShowTitle\r
+=\r
+============================\r
+*/\r
+\r
+void ShowTitle(void)\r
+{\r
+       panadjust = 0;\r
+       CA_CacheGrChunk(TITLEPICPIC);\r
+       VW_DrawPic(0, 0, TITLEPICPIC);\r
+#if GRMODE == CGAGR\r
+       VW_UpdateScreen();\r
+#else\r
+       VW_SetScreen(displayofs, 0);\r
+       VW_ScreenToScreen(bufferofs, displayofs, 42, 224);\r
+#endif\r
+       IN_UserInput(6*TickBase, false);\r
+       CA_ClearMarks();\r
+       CheckLastScan();\r
+}\r
+\r
+//===========================================================================\r
+\r
+#if GRMODE == CGAGR\r
+/*\r
+============================\r
+=\r
+= ShowCredits\r
+=\r
+============================\r
+*/\r
+\r
+void ShowCredits(void)\r
+{\r
+       panadjust = 0;\r
+       CA_CacheGrChunk(SW_BACKGROUNDPIC);\r
+       VW_DrawPic(0, 0, SW_BACKGROUNDPIC);\r
+       VW_UpdateScreen();\r
+       IN_UserInput(6*TickBase, false);\r
+       CA_ClearMarks();\r
+       CheckLastScan();\r
+}\r
+#endif\r
+\r
+//===========================================================================\r
+\r
+/*\r
+============================\r
+=\r
+= RunDemo\r
+=\r
+============================\r
+*/\r
+\r
+void RunDemo(Sint16 num)\r
+{\r
+       Uint16 far *demodata;\r
+       \r
+       NewGame();\r
+       num += DEMO0;\r
+       CA_CacheGrChunk(num);\r
+       demodata = grsegs[num];\r
+       gamestate.mapon = demodata[0];\r
+       DemoSize = demodata[1];\r
+       MM_GetPtr(&(memptr)DemoBuffer, DemoSize);\r
+       MM_SetLock(&(memptr)DemoBuffer, true);\r
+       _fmemcpy(DemoBuffer, ((char _seg *)grsegs[num])+4, DemoSize);\r
+       MM_FreePtr(&grsegs[num]);\r
+       IN_StartDemoPlayback(DemoBuffer, DemoSize);\r
+       SetupGameLevel(true);\r
+       if (scorescreenkludge)\r
+       {\r
+               DrawHighScores();\r
+       }\r
+       PlayLoop();\r
+       IN_StopDemo();\r
+       MM_FreePtr(&(memptr)DemoBuffer);\r
+       VW_FixRefreshBuffer();\r
+       CA_ClearMarks();\r
+       CheckLastScan();\r
+}\r
+\r
+//===========================================================================\r
+\r
+/*\r
+============================\r
+=\r
+= DrawHighScores\r
+=\r
+============================\r
+*/\r
+\r
+void DrawHighScores(void)\r
+{\r
+       Uint16 i, n;\r
+       Uint16 width, height;\r
+       HighScore *entry;\r
+       Uint16 oldbufferofs;\r
+       char buf[16], *bufptr;\r
+       \r
+       RF_NewPosition(0, 0);\r
+       oldbufferofs = bufferofs;\r
+       bufferofs = masterofs;\r
+#ifdef KEEN5\r
+#if GRMODE == CGAGR\r
+       fontcolor = 2;\r
+#else\r
+       fontcolor = BLUE ^ LIGHTMAGENTA;        // blue text on light magenta background (XOR draw mode!)\r
+#endif\r
+#endif\r
+       for (i=0, entry=&Scores[0]; i<MaxScores; i++, entry++)\r
+       {\r
+               PrintY = i*16 + HIGHSCORE_TOP;\r
+               PrintX = HIGHSCORE_LEFT;\r
+               US_Print(entry->name);\r
+#ifdef KEEN4\r
+               PrintX = 152;\r
+               for (n=0; n<entry->completed; n++)\r
+               {\r
+                       VWB_DrawTile8(PrintX, PrintY+1, 71);\r
+                       PrintX += 8;\r
+               }\r
+#endif\r
+               ultoa(entry->score, buf, 10);\r
+               for (bufptr=buf; *bufptr; bufptr++)\r
+               {\r
+                       *bufptr = *bufptr + 81;\r
+               }\r
+               USL_MeasureString(buf, &width, &height);\r
+               PrintX = HIGHSCORE_RIGHT - width;\r
+               US_Print(buf);\r
+       }\r
+       fontcolor = WHITE;      // back to default color\r
+       bufferofs = oldbufferofs;\r
+}\r
+\r
+//===========================================================================\r
+\r
+/*\r
+============================\r
+=\r
+= CheckHighScore\r
+=\r
+============================\r
+*/\r
+\r
+void CheckHighScore(Sint32 score, Sint16 completed)\r
+{\r
+       Uint16 i, n;\r
+       Sint16 index;\r
+       HighScore entry;\r
+       \r
+       strcpy(entry.name, ""); //Note: 'entry.name[0] = 0;' would be more efficient\r
+       entry.score = score;\r
+       entry.completed = completed;\r
+       for (i=0, index=-1; i<MaxScores; i++)\r
+       {\r
+               if (Scores[i].score < entry.score ||\r
+                       (Scores[i].score == entry.score && Scores[i].completed < entry.completed))\r
+               {\r
+                       n=MaxScores;\r
+                       while (--n > i)\r
+                       {\r
+                               Scores[n] = Scores[n-1];\r
+                       }\r
+                       Scores[i] = entry;\r
+                       index = i;\r
+                       HighScoresDirty = true;\r
+                       break;\r
+               }\r
+       }\r
+       if (index != -1)\r
+       {\r
+               scorescreenkludge = true;\r
+               gamestate.mapon = HIGHSCORE_MAP;\r
+               SetupGameLevel(true);\r
+               DrawHighScores();\r
+#ifdef KEEN5\r
+#if GRMODE == CGAGR\r
+               fontcolor = 2;\r
+#else\r
+               fontcolor = BLUE ^ LIGHTMAGENTA;        // blue text on light magenta background (XOR draw mode!)\r
+#endif\r
+#endif\r
+               RF_Refresh();\r
+               RF_Refresh();\r
+               PrintY = i*16 + HIGHSCORE_TOP;\r
+               PrintX = HIGHSCORE_LEFT;\r
+               US_LineInput(PrintX, PrintY, Scores[index].name, NULL, true, MaxHighName, 112);\r
+               scorescreenkludge = false;\r
+       }\r
+#ifdef KEEN5\r
+       fontcolor = 15; // back to default color (white)\r
+#endif\r
+}\r
+\r
+//===========================================================================\r
+\r
+/*\r
+============================\r
+=\r
+= ShowHighScores\r
+=\r
+============================\r
+*/\r
+\r
+void ShowHighScores(void)\r
+{\r
+       scorescreenkludge = true;\r
+       IN_ClearKeysDown();\r
+       RunDemo(4);\r
+       scorescreenkludge = false;\r
+}\r
diff --git a/16/keen456/KEEN4-6/CK_GAME.C b/16/keen456/KEEN4-6/CK_GAME.C
new file mode 100755 (executable)
index 0000000..157bd3f
--- /dev/null
@@ -0,0 +1,1009 @@
+/* Reconstructed Commander Keen 4-6 Source Code\r
+ * Copyright (C) 2021 K1n9_Duk3\r
+ *\r
+ * This file is loosely based on:\r
+ * Keen Dreams Source Code\r
+ * Copyright (C) 2014 Javier M. Chavez\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License along\r
+ * with this program; if not, write to the Free Software Foundation, Inc.,\r
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+ */\r
+\r
+#include "CK_DEF.H"\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                GLOBAL VARIABLES\r
+\r
+=============================================================================\r
+*/\r
+\r
+Uint16 fadecount;\r
+Sint16 levelcompleted;\r
+Sint32 chunkcount, chunkmax, handpic;\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                LOCAL VARIABLES\r
+\r
+=============================================================================\r
+*/\r
+\r
+void FadeAndUnhook(void);\r
+\r
+//===========================================================================\r
+\r
+/*\r
+============================\r
+=\r
+= FreeGraphics\r
+=\r
+============================\r
+*/\r
+\r
+void FreeGraphics(void)\r
+{\r
+       Sint16 i;\r
+       for (i=STARTSPRITES; i<STARTSPRITES+NUMSPRITES; i++)\r
+       {\r
+               if (grsegs[i])\r
+               {\r
+                       MM_SetPurge(&grsegs[i], PURGE_LAST);\r
+               }\r
+       }\r
+       for (i=STARTTILE16; i<STARTEXTERNS; i++)\r
+       {\r
+               if (grsegs[i])\r
+               {\r
+                       MM_SetPurge(&grsegs[i], PURGE_LAST);\r
+               }\r
+       }\r
+}\r
+\r
+//===========================================================================\r
+\r
+/*\r
+=====================\r
+=\r
+= NewGame\r
+=\r
+= Set up new game to start from the beginning\r
+=\r
+=====================\r
+*/\r
+\r
+void NewGame(void)\r
+{\r
+       memset(&gamestate, 0, sizeof(gamestate));\r
+       gamestate.nextextra = 20000;\r
+       gamestate.lives = 3;\r
+       gamestate.ammo = 5;\r
+}\r
+\r
+//===========================================================================\r
+\r
+#ifndef KEEN5\r
+/*\r
+============================\r
+=\r
+= GameOver\r
+=\r
+============================\r
+*/\r
+\r
+void GameOver(void)\r
+{\r
+       VW_FixRefreshBuffer();\r
+       US_CenterWindow(16, 3);\r
+       US_PrintCentered("Game Over!");\r
+       VW_UpdateScreen();\r
+       IN_ClearKeysDown();\r
+       IN_UserInput(4*TickBase, false);\r
+}\r
+#endif\r
+\r
+//===========================================================================\r
+\r
+/*\r
+============================\r
+=\r
+= SaveTheGame\r
+=\r
+============================\r
+*/\r
+\r
+#define RLETAG 0xABCD\r
+\r
+boolean SaveTheGame(Sint16 handle)\r
+{\r
+       Uint16  i,compressed,expanded;\r
+       objtype *ob;\r
+       memptr  bigbuffer;\r
+\r
+       gamestate.riding = NULL;\r
+\r
+       if (!CA_FarWrite(handle, (byte far *)&gamestate, sizeof(gamestate)))\r
+               return false;\r
+\r
+       expanded = mapwidth * mapheight * 2;\r
+       MM_GetPtr(&bigbuffer, expanded);\r
+\r
+       for (i = 0; i < 3; i++)\r
+       {\r
+               compressed = CA_RLEWCompress(mapsegs[i], expanded, (Uint16 huge *)bigbuffer+1, RLETAG);\r
+               *(Uint16 huge *)bigbuffer = compressed;\r
+               if (!CA_FarWrite(handle, bigbuffer, compressed+2))\r
+               {\r
+                       MM_FreePtr(&bigbuffer);\r
+                       return false;\r
+               }\r
+       }\r
+       for (ob = player; ob; ob=ob->next)\r
+       {\r
+               if (!CA_FarWrite(handle, (byte far *)ob, sizeof(objtype)))\r
+               {\r
+                       MM_FreePtr(&bigbuffer);\r
+                       return false;\r
+               }\r
+       }\r
+       MM_FreePtr(&bigbuffer);\r
+       return true;\r
+}\r
+\r
+//===========================================================================\r
+\r
+/*\r
+============================\r
+=\r
+= LoadTheGame\r
+=\r
+============================\r
+*/\r
+\r
+boolean LoadTheGame(Sint16 handle)\r
+{\r
+       Uint16  i;\r
+       objtype *prev,*next,*followed;\r
+       Uint16  compressed,expanded;\r
+       memptr  bigbuffer;\r
+#ifdef KEEN5\r
+       Sint16  numfuses;\r
+#endif\r
+\r
+       if (!CA_FarRead(handle, (byte far *)&gamestate, sizeof(gamestate)))\r
+               return false;\r
+\r
+#ifdef KEEN5\r
+       //\r
+       // remember the fuses value for later - SetupGameLevel calls\r
+       // ScanInfoPlane, which resets this part of the gamestate\r
+       //\r
+       numfuses = gamestate.numfuses;\r
+#endif\r
+\r
+       ca_levelbit >>= 1;\r
+       ca_levelnum--;\r
+       SetupGameLevel(false);\r
+       if (mmerror)\r
+       {\r
+               mmerror = false;\r
+               US_CenterWindow(20, 8);\r
+               PrintY += 20;\r
+               US_CPrint("Not enough memory\nto load game!");\r
+               VW_UpdateScreen();\r
+               IN_Ack();\r
+               return false;\r
+       }\r
+       ca_levelbit <<= 1;\r
+       ca_levelnum++;\r
+\r
+       expanded = mapwidth * mapheight * 2;\r
+       MM_BombOnError(true);   //BUG: this should use false to avoid an instant crash\r
+       MM_GetPtr(&bigbuffer, expanded);\r
+       MM_BombOnError(false);  //BUG: this should use true to force an instant crash\r
+       if (mmerror)\r
+       {\r
+               mmerror = false;\r
+               US_CenterWindow(20, 8);\r
+               PrintY += 20;\r
+               US_CPrint("Not enough memory\nto load game!");\r
+               VW_UpdateScreen();\r
+               IN_Ack();\r
+               return false;\r
+       }\r
+       for (i = 0; i < 3; i++)\r
+       {\r
+               if (!CA_FarRead(handle, (byte far *)&compressed, sizeof(compressed)))\r
+               {\r
+                       MM_FreePtr(&bigbuffer);\r
+                       return false;\r
+               }\r
+               if (!CA_FarRead(handle, (byte far *)bigbuffer, compressed))\r
+               {\r
+                       MM_FreePtr(&bigbuffer);\r
+                       return false;\r
+               }\r
+               CA_RLEWexpand(bigbuffer, mapsegs[i], expanded, RLETAG);\r
+       }\r
+       MM_FreePtr(&bigbuffer);\r
+\r
+       InitObjArray();\r
+       new = player;\r
+       prev = new->prev;\r
+       next = new->next;\r
+       if (!CA_FarRead(handle, (byte far *)new, sizeof(objtype)))\r
+       {\r
+               return false;\r
+       }\r
+       new->prev = prev;\r
+       new->next = next;\r
+       new->needtoreact = true;\r
+       new->sprite = NULL;\r
+       new = scoreobj;\r
+       while (true)\r
+       {\r
+               prev = new->prev;\r
+               next = new->next;\r
+               if (!CA_FarRead(handle, (byte far *)new, sizeof(objtype)))\r
+               {\r
+                       return false;\r
+               }\r
+               followed = new->next;\r
+               new->prev = prev;\r
+               new->next = next;\r
+               new->needtoreact = true;\r
+               new->sprite = NULL;\r
+               if (new->obclass == stunnedobj)\r
+               {\r
+                       new->temp3 = 0; //clear sprite ptr for the stars\r
+               }\r
+#if defined KEEN4\r
+               else if (new->obclass == platformobj)\r
+               {\r
+                       new->temp2 = new->temp3 = 0;    //clear sprite ptrs\r
+               }\r
+#elif defined KEEN5\r
+               else if (new->obclass == mineobj)\r
+               {\r
+                       new->temp4 = 0; //clear sprite ptr\r
+               }\r
+               else if (new->obclass == spherefulobj)\r
+               {\r
+                       new->temp1 = new->temp2 = new->temp3 = new->temp4 = 0;  //clear sprite ptrs\r
+               }\r
+#elif defined KEEN6\r
+               else if (new->obclass == platformobj)\r
+               {\r
+                       new->temp3 = 0; //clear sprite ptr\r
+               }\r
+#endif\r
+               if (followed)\r
+               {\r
+                       GetNewObj(false);\r
+               }\r
+               else\r
+               {\r
+                       break;\r
+               }\r
+       }\r
+       scoreobj->temp2 = -1;\r
+       scoreobj->temp1 = -1;\r
+       scoreobj->temp3 = -1;\r
+       scoreobj->temp4 = -1;\r
+#ifdef KEEN5\r
+       gamestate.numfuses = numfuses;  // put value from saved game back in place \r
+#endif\r
+       return true;\r
+}\r
+\r
+//===========================================================================\r
+\r
+/*\r
+============================\r
+=\r
+= ResetGame\r
+=\r
+============================\r
+*/\r
+\r
+void ResetGame(void)\r
+{\r
+       NewGame();\r
+       ca_levelnum--;\r
+       ca_levelbit >>= 1;\r
+       CA_ClearMarks();\r
+       ca_levelbit <<= 1;\r
+       ca_levelnum++;\r
+}\r
+\r
+//===========================================================================\r
+\r
+\r
+/*\r
+==========================\r
+=\r
+= PatchWorldMap\r
+=\r
+= Takes out blocking squares and spawns flags\r
+=\r
+==========================\r
+*/\r
+\r
+void PatchWorldMap(void)\r
+{\r
+       Uint16 x, y, planeoff, info, level, tag;\r
+       Uint16 far *infoptr;\r
+\r
+       planeoff = 0;\r
+       infoptr = mapsegs[2];\r
+       for (y = 0; y < mapheight; y++)\r
+       {\r
+               for (x = 0; x < mapwidth; x++, infoptr++, planeoff++)\r
+               {\r
+                       info = *infoptr;\r
+                       level = info & 0xFF;\r
+                       if (level >= MINDONELEVEL && level <= MAXDONELEVEL && gamestate.leveldone[level])\r
+                       {\r
+                               tag = info >> 8;\r
+                               *infoptr = 0;   // BUG: infoplane value should only be set to 0 if tag == 0xC0\r
+                               if (tag == 0xD0)\r
+                               {\r
+                                       mapsegs[1][planeoff] = 0;\r
+                               }\r
+                               else if (tag == 0xF0)\r
+                               {\r
+#ifdef KEEN5\r
+                                       SpawnFlag(x, y);\r
+#else\r
+                                       if (levelcompleted == level)\r
+                                       {\r
+                                               SpawnThrowFlag(x, y);\r
+                                       }\r
+                                       else\r
+                                       {\r
+                                               SpawnFlag(x, y);\r
+                                       }\r
+#endif\r
+                               }\r
+                       }\r
+               }\r
+       }\r
+}\r
+\r
+//===========================================================================\r
+\r
+/*\r
+==========================\r
+=\r
+= DelayedFade\r
+=\r
+= Fades out and latches FadeAndUnhook onto the refresh\r
+=\r
+==========================\r
+*/\r
+\r
+void DelayedFade(void)\r
+{\r
+       VW_FadeOut();\r
+       fadecount = 0;\r
+       RF_SetRefreshHook(&FadeAndUnhook);\r
+}\r
+\r
+/*\r
+==========================\r
+=\r
+= FadeAndUnhook\r
+=\r
+= Latch this onto the refresh so the screen only gets faded in after two\r
+= refreshes.  This lets all actors draw themselves to both pages before\r
+= fading the screen in.\r
+=\r
+==========================\r
+*/\r
+\r
+void FadeAndUnhook(void)\r
+{\r
+       if (++fadecount == 2)\r
+       {\r
+               VW_FadeIn();\r
+               RF_SetRefreshHook(NULL);\r
+               TimeCount = lasttimecount;      // don't adaptively time the fade\r
+       }\r
+}\r
+\r
+//===========================================================================\r
+\r
+\r
+/*\r
+==========================\r
+=\r
+= SetupGameLevel\r
+=\r
+= Load in map mapon and cache everything needed for it\r
+=\r
+==========================\r
+*/\r
+\r
+void SetupGameLevel(boolean loadnow)\r
+{\r
+//\r
+// randomize if not a demo\r
+//\r
+       if (DemoMode)\r
+       {\r
+               US_InitRndT(false);\r
+               gamestate.difficulty = gd_Normal;\r
+       }\r
+       else\r
+       {\r
+               US_InitRndT(true);\r
+       }\r
+\r
+//\r
+// load the level header and three map planes\r
+//\r
+       CA_CacheMap(gamestate.mapon);\r
+\r
+//\r
+// let the refresh manager set up some variables\r
+//\r
+       RF_NewMap();\r
+\r
+//\r
+// decide which graphics are needed and spawn actors\r
+//\r
+       CA_ClearMarks();\r
+       ScanInfoPlane();\r
+       if (mapon == 0)\r
+       {\r
+               PatchWorldMap();\r
+       }\r
+       RF_MarkTileGraphics();\r
+\r
+//\r
+// have the caching manager load and purge stuff to make sure all marks\r
+// are in memory\r
+//\r
+       MM_BombOnError(false);\r
+       CA_LoadAllSounds();\r
+       if (loadnow)\r
+       {\r
+               if (scorescreenkludge)\r
+               {\r
+                       CA_CacheMarks(NULL);\r
+               }\r
+               else if (DemoMode)\r
+               {\r
+                       CA_CacheMarks("DEMO");\r
+               }\r
+#ifdef KEEN5\r
+               else if (mapon == 0 && player->tiletop > 100)\r
+               {\r
+                       CA_CacheMarks("Keen steps out\nonto Korath III");\r
+               }\r
+#endif\r
+               else\r
+               {\r
+                       _fstrcpy(str, levelenter[mapon]);\r
+                       CA_CacheMarks(str);\r
+               }\r
+       }\r
+       MM_BombOnError(true);\r
+\r
+       if (!mmerror && loadnow)\r
+       {\r
+               DelayedFade();\r
+       }\r
+}\r
+\r
+//===========================================================================\r
+\r
+\r
+/*\r
+==========================\r
+=\r
+= DialogDraw\r
+=\r
+==========================\r
+*/\r
+\r
+void DialogDraw(char *title, Uint16 numcache)\r
+{\r
+       Sint16 i;\r
+       Uint16 width, height;\r
+       Sint32 totalfree;\r
+\r
+       totalfree = MM_TotalFree();\r
+       if (totalfree < 2048)\r
+       {\r
+               handpic = 5;\r
+       }\r
+       else\r
+       {\r
+               handpic = 0;\r
+               for (i = 0; i < 6; i++)\r
+               {\r
+                       CA_CacheGrChunk(i+KEENCOUNT1PIC);\r
+                       CA_UnmarkGrChunk(i+KEENCOUNT1PIC);\r
+                       if (grsegs[i+KEENCOUNT1PIC])\r
+                       {\r
+                               MM_SetPurge(&grsegs[i+KEENCOUNT1PIC], PURGE_FIRST);\r
+                       }\r
+                       else\r
+                       {\r
+                               mmerror = false;\r
+                               handpic = 5;\r
+                               break;\r
+                       }\r
+               }\r
+       }\r
+       US_CenterWindow(26, 8);\r
+       if (grsegs[KEENCOUNT1PIC])\r
+       {\r
+               VWB_DrawPic(WindowX, WindowY, KEENCOUNT1PIC);\r
+       }\r
+       else\r
+       {\r
+               handpic = 5;\r
+       }\r
+       CA_UnmarkGrChunk(KEENCOUNT1PIC);        //redundant\r
+       WindowW -= 48;\r
+       WindowX += 48;\r
+       SizeText(title, &width, &height);\r
+       PrintY += (WindowH-height)/2 - 4;\r
+       US_CPrint(title);\r
+       VW_UpdateScreen();\r
+       chunkmax = chunkcount = numcache / 6;\r
+       if (!chunkmax && !handpic)\r
+       {\r
+               handpic = 5;\r
+               if (grsegs[KEENCOUNT6PIC])\r
+                       VWB_DrawPic(WindowX-24, WindowY+40, KEENCOUNT6PIC);\r
+               VW_UpdateScreen();\r
+       }\r
+}\r
+\r
+/*\r
+==========================\r
+=\r
+= DialogUpdate\r
+=\r
+==========================\r
+*/\r
+\r
+void DialogUpdate(void)\r
+{\r
+       if (--chunkcount || handpic > 4)\r
+               return;\r
+\r
+       chunkcount = chunkmax;\r
+       if (grsegs[handpic+KEENCOUNT2PIC])\r
+       {\r
+               VWB_DrawPic(WindowX-24, WindowY+40, handpic+KEENCOUNT2PIC);\r
+       }\r
+       VW_UpdateScreen();\r
+       handpic++;\r
+}\r
+\r
+/*\r
+==========================\r
+=\r
+= DialogFinish\r
+=\r
+==========================\r
+*/\r
+\r
+void DialogFinish(void)\r
+{\r
+       //this is empty\r
+}\r
+\r
+//==========================================================================\r
+\r
+/*\r
+==================\r
+=\r
+= StartDemoRecord\r
+=\r
+==================\r
+*/\r
+\r
+void StartDemoRecord(void)\r
+{\r
+       Sint16 level;\r
+       boolean esc;\r
+\r
+       VW_FixRefreshBuffer();\r
+       US_CenterWindow(30, 3);\r
+       PrintY += 6;\r
+       US_Print("  Record a demo from level(0-21):");\r
+       VW_UpdateScreen();\r
+       esc = !US_LineInput(px, py, str, NULL, true, 2, 0);\r
+       if (!esc)\r
+       {\r
+               level = atoi(str);\r
+               if (level >= 0 && level <= 21)\r
+               {\r
+                       gamestate.mapon = level;\r
+                       playstate = ex_warped;\r
+                       IN_StartDemoRecord(0x1000);\r
+               }\r
+       }\r
+}\r
+\r
+/*\r
+==================\r
+=\r
+= EndDemoRecord\r
+=\r
+==================\r
+*/\r
+\r
+void EndDemoRecord(void)\r
+{\r
+       Sint16 handle;\r
+       boolean esc;\r
+       char filename[] = "DEMO?."EXTENSION;\r
+\r
+       IN_StopDemo();\r
+       VW_FixRefreshBuffer();\r
+       US_CenterWindow(22, 3);\r
+       PrintY += 6;\r
+       US_Print("  Save as demo #(0-9):");\r
+       VW_UpdateScreen();\r
+       esc = !US_LineInput(px, py, str, NULL, true, 2, 0);\r
+       if (!esc && str[0] >= '0' && str[0] <= '9')\r
+       {\r
+               filename[4] = str[0];\r
+               handle = open(filename, O_BINARY|O_WRONLY|O_CREAT, S_IFREG|S_IREAD|S_IWRITE);\r
+               if (handle == -1)\r
+               {\r
+                       Quit("EndDemoRecord:  Cannot write demo file!");\r
+               }\r
+               write(handle, &mapon, sizeof(mapon));\r
+               write(handle, &DemoOffset, sizeof(DemoOffset));\r
+               CA_FarWrite(handle, DemoBuffer, DemoOffset);\r
+               close(handle);\r
+       }\r
+       IN_FreeDemoBuffer();\r
+}\r
+\r
+//==========================================================================\r
+\r
+/*\r
+==========================\r
+=\r
+= HandleDeath\r
+=\r
+==========================\r
+*/\r
+\r
+void HandleDeath(void)\r
+{\r
+       Uint16 y, color, top, bottom, selection, w, h;\r
+\r
+       _fstrcpy(str, levelnames[mapon]);\r
+       SizeText(str, &w, &h);\r
+\r
+       memset(gamestate.keys, 0, sizeof(gamestate.keys));\r
+       gamestate.lives--;\r
+       if (gamestate.lives >= 0)\r
+       {\r
+               VW_FixRefreshBuffer();\r
+               US_CenterWindow(20, 8);\r
+               PrintY += 3;\r
+               US_CPrint("You didn't make it past");\r
+               top = PrintY+22;\r
+               if (h < 15)\r
+                       PrintY += 4;\r
+               US_CPrint(str);\r
+               PrintY = top+2;\r
+               US_CPrint("Try Again");\r
+               PrintY += 4;\r
+               bottom = PrintY-2;\r
+               US_CPrint("Exit to "WORLDMAPNAME);\r
+\r
+               IN_ClearKeysDown();\r
+               selection = 0;\r
+               while (true)\r
+               {\r
+                       if (selection)\r
+                       {\r
+                               y = bottom;\r
+                       }\r
+                       else\r
+                       {\r
+                               y = top;\r
+                       }\r
+\r
+// draw select bar\r
+                       if ((TimeCount / 16) & 1)\r
+                       {\r
+                               color = SECONDCOLOR;\r
+                       }\r
+                       else\r
+                       {\r
+                               color = FIRSTCOLOR;\r
+                       }\r
+                       VWB_Hlin(WindowX+4, WindowX+WindowW-4, y, color);\r
+                       VWB_Hlin(WindowX+4, WindowX+WindowW-4, y+1, color);\r
+                       VWB_Hlin(WindowX+4, WindowX+WindowW-4, y+12, color);\r
+                       VWB_Hlin(WindowX+4, WindowX+WindowW-4, y+13, color);\r
+                       VWB_Vlin(y+1, y+11, WindowX+4, color);\r
+                       VWB_Vlin(y+1, y+11, WindowX+5, color);\r
+                       VWB_Vlin(y+1, y+11, WindowX+WindowW-4, color);\r
+                       VWB_Vlin(y+1, y+11, WindowX+WindowW-5, color);\r
+\r
+                       VW_UpdateScreen();\r
+\r
+// erase select bar\r
+                       VWB_Hlin(WindowX+4, WindowX+WindowW-4, y, WHITE);\r
+                       VWB_Hlin(WindowX+4, WindowX+WindowW-4, y+1, WHITE);\r
+                       VWB_Hlin(WindowX+4, WindowX+WindowW-4, y+12, WHITE);\r
+                       VWB_Hlin(WindowX+4, WindowX+WindowW-4, y+13, WHITE);\r
+                       VWB_Vlin(y+1, y+11, WindowX+4, WHITE);\r
+                       VWB_Vlin(y+1, y+11, WindowX+5, WHITE);\r
+                       VWB_Vlin(y+1, y+11, WindowX+WindowW-4, WHITE);\r
+                       VWB_Vlin(y+1, y+11, WindowX+WindowW-5, WHITE);\r
+\r
+                       if (LastScan == sc_Escape)\r
+                       {\r
+                               gamestate.mapon = 0;            // exit to world map\r
+                               IN_ClearKeysDown();\r
+                               return;\r
+                       }\r
+\r
+                       IN_ReadControl(0, &c);\r
+                       if (c.button0 || c.button1 || LastScan == sc_Return || LastScan == sc_Space)\r
+                       {\r
+                               if (selection)\r
+                                       gamestate.mapon = 0;            // exit to world map\r
+                               return;\r
+                       }\r
+                       if (c.yaxis == -1 || LastScan == sc_UpArrow)\r
+                       {\r
+                               selection = 0;\r
+                       }\r
+                       else if (c.yaxis == 1 || LastScan == sc_DownArrow)\r
+                       {\r
+                               selection = 1;\r
+                       }\r
+               }\r
+       }\r
+}\r
+\r
+//==========================================================================\r
+\r
+/*\r
+============================\r
+=\r
+= GameLoop\r
+=\r
+= A game has just started (after the cinematic or load game)\r
+=\r
+============================\r
+*/\r
+\r
+void GameLoop(void)\r
+{\r
+       Uint16 temp;\r
+#ifdef KEEN6\r
+       Uint16 i;\r
+#endif\r
+\r
+#ifdef KEEN6\r
+       if (!storedemo)\r
+       {\r
+               if (!US_ManualCheck())\r
+               {\r
+                       loadedgame = false;\r
+                       restartgame = gd_Continue;\r
+                       return;\r
+               }\r
+       }\r
+#endif\r
+\r
+       if (playstate == ex_loadedgame)\r
+       {\r
+               goto loaded;\r
+       }\r
+reset:\r
+       gamestate.difficulty = restartgame;\r
+       restartgame = gd_Continue;\r
+       do\r
+       {\r
+startlevel:\r
+               SetupGameLevel(true);\r
+               if (mmerror)\r
+               {\r
+                       if (gamestate.mapon != 0)\r
+                       {\r
+                               mmerror = false;\r
+                               US_CenterWindow(20, 8);\r
+                               PrintY += 20;\r
+                               US_CPrint("Insufficient memory\nto load level!");\r
+                               VW_UpdateScreen();\r
+                               IN_Ack();\r
+                               gamestate.mapon = 0;            // exit to world map\r
+                               SetupGameLevel(true);\r
+                       }\r
+                       if (mmerror)\r
+                       {\r
+                               Quit("GameLoop: Insufficient memory to load world map!");\r
+                       }\r
+               }\r
+loaded:\r
+               keenkilled = false;\r
+               SD_WaitSoundDone();\r
+\r
+               PlayLoop();\r
+\r
+               if (playstate != ex_loadedgame)\r
+               {\r
+                       memset(gamestate.keys, 0, sizeof(gamestate.keys));\r
+#ifdef KEEN5\r
+                       gamestate.keycard = false;\r
+#endif\r
+               }\r
+               VW_FixRefreshBuffer();\r
+\r
+               if (tedlevel)\r
+               {\r
+                       if (playstate == ex_loadedgame)\r
+                       {\r
+                               goto loaded;\r
+                       }\r
+                       else if (playstate == ex_died)\r
+                       {\r
+                               goto startlevel;\r
+                       }\r
+                       else\r
+                       {\r
+                               TEDDeath();\r
+                       }\r
+               }\r
+\r
+               levelcompleted = -1;\r
+               switch (playstate)\r
+               {\r
+               case ex_resetgame:\r
+                       goto reset;\r
+\r
+               case ex_loadedgame:\r
+                       goto loaded;\r
+\r
+               case ex_died:\r
+                       HandleDeath();\r
+                       break;\r
+\r
+#if defined KEEN4\r
+               case ex_rescued:\r
+                       if (mapon != 0)\r
+                       {\r
+                               SD_PlaySound(SND_LEVELDONE);\r
+                       }\r
+                       levelcompleted = mapon;\r
+                       gamestate.leveldone[mapon] = true;\r
+                       RescuedMember();\r
+                       if (gamestate.rescued != 8)\r
+                       {\r
+                               gamestate.mapon = 0;\r
+                       }\r
+                       else\r
+                       {\r
+                               FreeGraphics();\r
+                               RF_FixOfs();\r
+                               VW_FixRefreshBuffer();\r
+                               FinaleLayout();\r
+                               CheckHighScore(gamestate.score, gamestate.rescued);\r
+                               return;\r
+                       }\r
+                       break;\r
+\r
+#elif defined KEEN5\r
+               case ex_fusebroke:\r
+                       SD_PlaySound(SND_LEVELDONE);\r
+                       levelcompleted = mapon;\r
+                       gamestate.leveldone[mapon] = ex_fusebroke;\r
+                       FinishedFuse();\r
+                       gamestate.mapon = 0;\r
+                       break;\r
+\r
+               case ex_qedbroke:\r
+                       FreeGraphics();\r
+                       RF_FixOfs();\r
+                       VW_FixRefreshBuffer();\r
+                       FinaleLayout();\r
+                       CheckHighScore(gamestate.score, 0);\r
+                       return;\r
+\r
+#elif defined KEEN6\r
+               case ex_hook:\r
+                       GotHook();\r
+                       goto completed;\r
+\r
+               case ex_sandwich:\r
+                       GotSandwich();\r
+                       goto completed;\r
+\r
+               case ex_card:\r
+                       GotPasscard();\r
+                       goto completed;\r
+\r
+               case ex_molly:\r
+                       FreeGraphics();\r
+                       RF_FixOfs();\r
+                       VW_FixRefreshBuffer();\r
+                       FinaleLayout();\r
+                       goto check_score;\r
+\r
+#endif\r
+               case ex_completed:\r
+               case ex_foot:\r
+               case ex_portout:\r
+completed:\r
+                       if (mapon != 0)\r
+                       {\r
+                               SD_PlaySound(SND_LEVELDONE);\r
+                               gamestate.mapon = 0;\r
+                               levelcompleted = mapon;\r
+                               gamestate.leveldone[mapon] = true;\r
+                               if (storedemo && mapon == 2)\r
+                               {\r
+                                       IN_ClearKeysDown();\r
+                                       return;\r
+                               }\r
+                       }\r
+                       else\r
+                       {\r
+#if GRMODE != CGAGR\r
+                               temp = bufferofs;\r
+                               bufferofs = displayofs;\r
+#endif\r
+                               US_CenterWindow(26, 8);\r
+                               PrintY += 25;\r
+                               US_CPrint("One moment");\r
+#if GRMODE == CGAGR\r
+                               VW_UpdateScreen();\r
+#else\r
+                               bufferofs = temp;\r
+#endif\r
+                       }\r
+                       break;\r
+\r
+               case ex_abortgame:\r
+                       IN_ClearKeysDown();\r
+                       return;\r
+               }\r
+       } while (gamestate.lives >= 0);\r
+\r
+       GameOver();\r
+\r
+check_score:\r
+#if defined KEEN4\r
+       CheckHighScore(gamestate.score, gamestate.rescued);\r
+#else\r
+       temp = 0;\r
+#if defined KEEN6\r
+       for (i = 0; i < GAMELEVELS; i++)\r
+       {\r
+               if (gamestate.leveldone[i])\r
+                       temp++;\r
+       }\r
+#endif\r
+       CheckHighScore(gamestate.score, temp);\r
+#endif\r
+}
\ No newline at end of file
diff --git a/16/keen456/KEEN4-6/CK_KEEN.C b/16/keen456/KEEN4-6/CK_KEEN.C
new file mode 100755 (executable)
index 0000000..3cb12b9
--- /dev/null
@@ -0,0 +1,2509 @@
+/* Reconstructed Commander Keen 4-6 Source Code\r
+ * Copyright (C) 2021 K1n9_Duk3\r
+ *\r
+ * This file is loosely based on:\r
+ * Keen Dreams Source Code\r
+ * Copyright (C) 2014 Javier M. Chavez\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License along\r
+ * with this program; if not, write to the Free Software Foundation, Inc.,\r
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+ */\r
+\r
+/*\r
+CK_KEEN.C\r
+=========\r
+\r
+Contains the following actor types (in this order):\r
+\r
+- Keen (regular levels)\r
+\r
+*/\r
+\r
+#include "CK_DEF.H"\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                GLOBAL VARIABLES\r
+\r
+=============================================================================\r
+*/\r
+\r
+Sint16 singlegravity;  // left over from Keen Dreams, not used in Keen 4-6\r
+\r
+Uint16 bounceangle [8][8] =\r
+{\r
+       { 0,  0,  0,  0,  0,  0,  0,  0},\r
+       { 7,  6,  5,  4,  3,  2,  1,  0},\r
+       { 5,  4,  3,  2,  1,  0, 15, 14},\r
+       { 5,  4,  3,  2,  1,  0, 15, 14},\r
+       { 3,  2,  1,  0, 15, 14, 13, 12},\r
+       { 9,  8,  7,  6,  5,  4,  3,  2},\r
+       { 9,  8,  7,  6,  5,  4,  3,  2},\r
+       {11, 10,  9,  8,  7,  6,  5,  4}\r
+};\r
+\r
+#ifndef KEEN4\r
+arrowdirtype arrowflip[] = {arrow_South, arrow_West, arrow_North, arrow_East, arrow_SouthWest, arrow_NorthWest, arrow_NorthEast, arrow_SouthEast};\r
+#endif\r
+\r
+statetype s_keenstand     = {KEENSTANDLSPR, KEENSTANDRSPR, stepthink, false, true, 4, 0, 32, KeenPauseThink, KeenContact, KeenStandReact, &s_keenstand};\r
+\r
+#ifdef KEEN5\r
+statetype s_keenride     = {KEENONPLATSPR, KEENONPLATSPR, stepthink, false, true, 4, 0, 32, KeenStandThink, KeenContact, KeenStandReact, &s_keenride};\r
+#endif\r
+\r
+statetype s_keenpauselook = {KEENLOOKUSPR, KEENLOOKUSPR, stepthink, false, true, 60, 0, 0, KeenPauseThink, KeenContact, KeenStandReact, &s_keenstand};\r
+\r
+statetype s_keenwait1    = {KEENWAITR2SPR, KEENWAITR2SPR, stepthink, false, true, 90, 0, 0, KeenPauseThink, KeenContact, KeenStandReact, &s_keenwait2};\r
+statetype s_keenwait2    = {KEENWAITR1SPR, KEENWAITR1SPR, stepthink, false, true, 10, 0, 0, KeenPauseThink, KeenContact, KeenStandReact, &s_keenwait3};\r
+statetype s_keenwait3    = {KEENWAITR2SPR, KEENWAITR2SPR, stepthink, false, true, 90, 0, 0, KeenPauseThink, KeenContact, KeenStandReact, &s_keenwait4};\r
+statetype s_keenwait4    = {KEENWAITR1SPR, KEENWAITR1SPR, stepthink, false, true, 10, 0, 0, KeenPauseThink, KeenContact, KeenStandReact, &s_keenwait5};\r
+statetype s_keenwait5    = {KEENWAITR2SPR, KEENWAITR2SPR, stepthink, false, true, 90, 0, 0, KeenPauseThink, KeenContact, KeenStandReact, &s_keenwait6};\r
+statetype s_keenwait6    = {KEENWAITR3SPR, KEENWAITR3SPR, stepthink, false, true, 70, 0, 0, KeenPauseThink, KeenContact, KeenStandReact, &s_keenstand};\r
+\r
+#ifdef KEEN4\r
+statetype s_keenmoon1     = {KEENMOON1SPR, KEENMOON1SPR, stepthink, false, true, 20, 0, 0, KeenPauseThink, KeenContact, KeenStandReact, &s_keenmoon2};\r
+statetype s_keenmoon2     = {KEENMOON2SPR, KEENMOON2SPR, stepthink, false, true, 90, 0, 0, KeenPauseThink, KeenContact, KeenStandReact, &s_keenmoon3};\r
+statetype s_keenmoon3     = {KEENMOON1SPR, KEENMOON1SPR, stepthink, false, true, 20, 0, 0, KeenPauseThink, KeenContact, KeenStandReact, &s_keenstand};\r
+#endif\r
+\r
+statetype s_keenread      = {KEENSITREAD1SPR, KEENSITREAD1SPR, step, false, true, 16, 0, 0, 0, KeenContact, KeenStandReact, &s_keenread2};\r
+statetype s_keenread2     = {KEENSITREAD2SPR, KEENSITREAD2SPR, step, false, true, 16, 0, 0, 0, KeenContact, KeenStandReact, &s_keenread3};\r
+statetype s_keenread3     = {KEENSITREAD3SPR, KEENSITREAD3SPR, step, false, true, 16, 0, 0, 0, KeenContact, KeenStandReact, &s_keenread4};\r
+statetype s_keenread4     = {KEENSITREAD4SPR, KEENSITREAD4SPR, step, false, true, 16, 0, 0, 0, KeenContact, KeenStandReact, &s_keenread5};\r
+statetype s_keenread5     = {KEENREAD1SPR, KEENREAD1SPR, stepthink, false, true, 300, 0, 0, KeenReadThink, KeenContact, KeenStandReact, &s_keenread6};\r
+statetype s_keenread6     = {KEENREAD2SPR, KEENREAD2SPR, stepthink, false, true, 16, 0, 0, KeenReadThink, KeenContact, KeenStandReact, &s_keenread7};\r
+statetype s_keenread7     = {KEENREAD3SPR, KEENREAD3SPR, stepthink, false, true, 16, 0, 0, KeenReadThink, KeenContact, KeenStandReact, &s_keenread5};\r
+statetype s_keenstopread  = {KEENSTOPREAD1SPR, KEENSTOPREAD1SPR, step, false, true, 12, 0, 0, 0, KeenContact, KeenStandReact, &s_keenstopread2};\r
+statetype s_keenstopread2 = {KEENSTOPREAD2SPR, KEENSTOPREAD2SPR, step, false, true, 12, 0, 0, 0, KeenContact, KeenStandReact, &s_keenstopread3};\r
+statetype s_keenstopread3 = {KEENSITREAD2SPR, KEENSITREAD2SPR, step, false, true, 12, 0, 0, 0, KeenContact, KeenStandReact, &s_keenstand};\r
+\r
+statetype s_keenlookup    = {KEENLOOKUSPR, KEENLOOKUSPR, stepthink, false, true, 30, 0, 0, KeenLookUpThink, KeenContact, KeenStandReact, &s_keenlookup2};\r
+statetype s_keenlookup2   = {KEENLOOKUSPR, KEENLOOKUSPR, think, false, true, 0, 0, 0, KeenLookUpThink, KeenPosContact, KeenStandReact, NULL};\r
+\r
+statetype s_keenlookdown  = {KEENLOOKD1SPR, KEENLOOKD1SPR, stepthink, false, true, 6, 0, 0, KeenLookDownThink, KeenContact, KeenStandReact, &s_keenlookdown2};\r
+statetype s_keenlookdown2 = {KEENLOOKD2SPR, KEENLOOKD2SPR, stepthink, false, true, 24, 0, 0, KeenLookDownThink, KeenPosContact, KeenStandReact, &s_keenlookdown3};\r
+statetype s_keenlookdown3 = {KEENLOOKD2SPR, KEENLOOKD2SPR, think, false, true, 0, 0, 0, KeenLookDownThink, KeenPosContact, KeenStandReact, NULL};\r
+statetype s_keenlookdown4 = {KEENLOOKD1SPR, KEENLOOKD1SPR, step, false, true, 6, 0, 0, 0, KeenContact, KeenStandReact, &s_keenstand};\r
+\r
+statetype s_keendrop   = {KEENLOOKD1SPR, KEENLOOKD1SPR, step, false, false, 0, 0, 0, KeenDropDownThink, KeenContact, KeenSimpleReact, &s_keenjump3};\r
+statetype s_keendead   = {-1, -1, think, false, false, 10, 0, 0, 0, 0, R_Draw, NULL};\r
+\r
+statetype s_keendie1     = {KEENDIE1SPR, KEENDIE1SPR, think, false, false, 100, 0, 0, KeenDieThink, 0, R_Draw, &s_keendie1};\r
+statetype s_keendie2     = {KEENDIE2SPR, KEENDIE2SPR, think, false, false, 100, 0, 0, KeenDieThink, 0, R_Draw, &s_keendie2};\r
+\r
+#ifdef KEEN4\r
+statetype s_keensuitdie1 = {SCUBAKEENDEAD1SPR, SCUBAKEENDEAD1SPR, think, false, false, 100, 0, 0, KeenDieThink, NULL, R_Draw, &s_keensuitdie1};\r
+statetype s_keensuitdie2 = {SCUBAKEENDEAD2SPR, SCUBAKEENDEAD2SPR, think, false, false, 100, 0, 0, KeenDieThink, NULL, R_Draw, &s_keensuitdie2};\r
+#endif\r
+\r
+statetype s_keenshoot1    = {KEENSHOOTLSPR, KEENSHOOTRSPR, step, false, true, 9, 0, 0, KeenShootThink, KeenContact, KeenStandReact, &s_keenshoot2};\r
+statetype s_keenshoot2    = {KEENSHOOTLSPR, KEENSHOOTRSPR, step, false, true, 6, 0, 0, 0, KeenContact, KeenStandReact, &s_keenstand};\r
+\r
+statetype s_keenshootup1  = {KEENSHOOTUSPR, KEENSHOOTUSPR, step, false, true, 9, 0, 0, KeenShootThink, KeenContact, KeenStandReact, &s_keenshootup2};\r
+statetype s_keenshootup2  = {KEENSHOOTUSPR, KEENSHOOTUSPR, step, false, true, 6, 0, 0, 0, KeenContact, KeenStandReact, &s_keenlookup};\r
+\r
+statetype s_keenswitch  = {KEENENTER1SPR, KEENENTER1SPR, step, false, true, 8, 0, 0, KeenSwitchThink, NULL, KeenStandReact, &s_keenswitch2};\r
+statetype s_keenswitch2 = {KEENENTER1SPR, KEENENTER1SPR, step, false, true, 8, 0, 0, 0, 0, KeenStandReact, &s_keenstand};\r
+statetype s_keenkey     = {KEENENTER1SPR, KEENENTER1SPR, step, false, true, 6, 0, 0, KeenKeyThink, NULL, KeenStandReact, &s_keenswitch2};\r
+\r
+statetype s_keenlineup  = {KEENENTER1SPR, KEENENTER1SPR, think, false, false, 0, 0, 0, T_LineUp, 0, R_Draw, NULL};\r
+#ifdef KEEN5\r
+statetype s_keenenter0   = {KEENENTER1SPR, KEENENTER1SPR, step, false, false, 45, 0, -64, NULL, NULL, R_Draw, &s_keenenter1};\r
+statetype s_keenteleport = {KEENWAITR2SPR, KEENWAITR2SPR, think, false, false, 0, 0, 0, NULL, NULL, R_Draw, NULL};\r
+#endif\r
+\r
+statetype s_keenenter1 = {KEENENTER1SPR, KEENENTER1SPR, step, false, false, 9, 0, -64, WalkSound1, NULL, R_Draw, &s_keenenter2};\r
+statetype s_keenenter2 = {KEENENTER2SPR, KEENENTER2SPR, step, false, false, 9, 0, -64, WalkSound2, NULL, R_Draw, &s_keenenter3};\r
+statetype s_keenenter3 = {KEENENTER3SPR, KEENENTER3SPR, step, false, false, 9, 0, -64, WalkSound1, NULL, R_Draw, &s_keenenter4};\r
+statetype s_keenenter4 = {KEENENTER4SPR, KEENENTER4SPR, step, false, false, 9, 0, -64, WalkSound2, NULL, R_Draw, &s_keenenter5};\r
+statetype s_keenenter5 = {KEENENTER5SPR, KEENENTER5SPR, step, false, false, 9, 0, -64, KeenEnterThink, NULL, R_Draw, &s_keenstand};\r
+#ifdef KEEN5\r
+statetype s_keenenter6 = {-1, -1, step, false, false, 9, 0, -64, KeenEnterThink, 0, R_Draw, &s_keenstand};\r
+#endif\r
+\r
+statetype s_keenpole      = {KEENSHINNYL1SPR, KEENSHINNYR1SPR, think, false, false, 0, 0, 0, KeenPoleThink, KeenPosContact, KeenSimpleReact, &s_keenpole};\r
+\r
+statetype s_keenclimb1 = {KEENSHINNYL1SPR, KEENSHINNYR1SPR, slidethink, false, false, 8, 0, 8, KeenClimbThink, KeenPosContact, KeenSimpleReact, &s_keenclimb2};\r
+statetype s_keenclimb2 = {KEENSHINNYL2SPR, KEENSHINNYR2SPR, slidethink, false, false, 8, 0, 8, KeenClimbThink, KeenPosContact, KeenSimpleReact, &s_keenclimb3};\r
+statetype s_keenclimb3 = {KEENSHINNYL3SPR, KEENSHINNYR3SPR, slidethink, false, false, 8, 0, 8, KeenClimbThink, KeenPosContact, KeenSimpleReact, &s_keenclimb1};\r
+\r
+statetype s_keenslide1 = {KEENSLIDED1SPR, KEENSLIDED1SPR, slide, false, false, 8, 0, 24, KeenDropThink, KeenPosContact, KeenPoleReact, &s_keenslide2};\r
+statetype s_keenslide2 = {KEENSLIDED2SPR, KEENSLIDED2SPR, slide, false, false, 8, 0, 24, KeenDropThink, KeenPosContact, KeenPoleReact, &s_keenslide3};\r
+statetype s_keenslide3 = {KEENSLIDED3SPR, KEENSLIDED3SPR, slide, false, false, 8, 0, 24, KeenDropThink, KeenPosContact, KeenPoleReact, &s_keenslide4};\r
+statetype s_keenslide4 = {KEENSLIDED4SPR, KEENSLIDED4SPR, slide, false, false, 8, 0, 24, KeenDropThink, KeenPosContact, KeenPoleReact, &s_keenslide1};\r
+\r
+statetype s_keenpoleshoot1 = {KEENPSHOOTLSPR, KEENPSHOOTRSPR, step, false, false, 9, 0, 0, KeenShootThink, KeenPosContact, KeenSimpleReact, &s_keenpoleshoot2};\r
+statetype s_keenpoleshoot2 = {KEENPSHOOTLSPR, KEENPSHOOTRSPR, step, false, false, 6, 0, 0, 0, KeenPosContact, KeenSimpleReact, &s_keenpole};\r
+\r
+statetype s_keenpoleshootup1 = {KEENPLSHOOTUSPR, KEENPRSHOOTUSPR, step, false, false, 9, 0, 0, KeenShootThink, KeenPosContact, KeenSimpleReact, &s_keenpoleshootup2};\r
+statetype s_keenpoleshootup2 = {KEENPLSHOOTUSPR, KEENPRSHOOTUSPR, step, false, false, 6, 0, 0, 0, KeenPosContact, KeenSimpleReact, &s_keenpole};\r
+\r
+statetype s_keenpoleshootdown1 = {KEENPLSHOOTDSPR, KEENPRSHOOTDSPR, step, false, false, 9, 0, 0, KeenShootThink, KeenPosContact, KeenSimpleReact, &s_keenpoleshootdown2};\r
+statetype s_keenpoleshootdown2 = {KEENPLSHOOTDSPR, KEENPRSHOOTDSPR, step, false, false, 6, 0, 0, 0, KeenPosContact, KeenSimpleReact, &s_keenpole};\r
+\r
+statetype s_keenwalk1     = {KEENRUNL1SPR, KEENRUNR1SPR, slidethink, true, true, 6, 24, 0, KeenWalkThink, KeenContact, KeenWalkReact, &s_keenwalk2};\r
+statetype s_keenwalk2     = {KEENRUNL2SPR, KEENRUNR2SPR, slidethink, true, true, 6, 24, 0, KeenWalkThink, KeenContact, KeenWalkReact, &s_keenwalk3};\r
+statetype s_keenwalk3     = {KEENRUNL3SPR, KEENRUNR3SPR, slidethink, true, true, 6, 24, 0, KeenWalkThink, KeenContact, KeenWalkReact, &s_keenwalk4};\r
+statetype s_keenwalk4     = {KEENRUNL4SPR, KEENRUNR4SPR, slidethink, true, true, 6, 24, 0, KeenWalkThink, KeenContact, KeenWalkReact, &s_keenwalk1};\r
+\r
+statetype s_keenpogodown  = {KEENPOGOL2SPR, KEENPOGOR2SPR, step, true, false, 1, 0, 0, KeenBounceThink, KeenContact, KeenPogoReact, &s_keenpogo};\r
+statetype s_keenpogo      = {KEENPOGOL2SPR, KEENPOGOR2SPR, think, true, false, 0, 0, 0, KeenPogoThink, KeenContact, KeenPogoReact, &s_keenpogo2};\r
+statetype s_keenpogo2     = {KEENPOGOL1SPR, KEENPOGOR1SPR, think, true, false, 0, 0, 0, KeenPogoThink, KeenContact, KeenPogoReact, NULL};\r
+\r
+statetype s_keenjump1     = {KEENJUMPL1SPR, KEENJUMPR1SPR, think, false, false, 0, 0, 0, KeenAirThink, KeenContact, KeenAirReact, &s_keenjump2};\r
+statetype s_keenjump2     = {KEENJUMPL2SPR, KEENJUMPR2SPR, think, false, false, 0, 0, 0, KeenAirThink, KeenContact, KeenAirReact, &s_keenjump3};\r
+statetype s_keenjump3     = {KEENJUMPL3SPR, KEENJUMPR3SPR, stepthink, false, false, 50, 0, 0, KeenAirThink, KeenContact, KeenAirReact, &s_keenjump4};\r
+statetype s_keenjump4     = {KEENJUMPL2SPR, KEENJUMPR2SPR, stepthink, false, false, 40, 0, 0, KeenAirThink, KeenContact, KeenAirReact, &s_keenjump3};\r
+\r
+statetype s_keenairshoot1 = {KEENJLSHOOTLSPR, KEENJRSHOOTRSPR, stepthink, false, false, 9, 0, 0, T_Projectile, KeenContact, KeenAirReact, &s_keenairshoot2};\r
+statetype s_keenairshoot2 = {KEENJLSHOOTLSPR, KEENJRSHOOTRSPR, stepthink, true, false, 1, 0, 0, KeenShootThink, KeenContact, KeenAirReact, &s_keenairshoot3};\r
+statetype s_keenairshoot3 = {KEENJLSHOOTLSPR, KEENJRSHOOTRSPR, stepthink, false, false, 6, 0, 0, T_Projectile, KeenContact, KeenAirReact, &s_keenjump3};\r
+\r
+statetype s_keenairshootup1 = {KEENJSHOOTUSPR, KEENJSHOOTUSPR, stepthink, false, false, 9, 0, 0, T_Projectile, KeenContact, KeenAirReact, &s_keenairshootup2};\r
+statetype s_keenairshootup2 = {KEENJSHOOTUSPR, KEENJSHOOTUSPR, stepthink, true, false, 1, 0, 0, KeenShootThink, KeenContact, KeenAirReact, &s_keenairshootup3};\r
+statetype s_keenairshootup3 = {KEENJSHOOTUSPR, KEENJSHOOTUSPR, stepthink, false, false, 6, 0, 0, T_Projectile, KeenContact, KeenAirReact, &s_keenjump3};\r
+\r
+statetype s_keenairshootdown1 = {KEENJSHOOTDSPR, KEENJSHOOTDSPR, stepthink, false, false, 9, 0, 0, T_Projectile, KeenContact, KeenAirReact, &s_keenairshootdown2};\r
+statetype s_keenairshootdown2 = {KEENJSHOOTDSPR, KEENJSHOOTDSPR, stepthink, true, false, 1, 0, 0, KeenShootThink, KeenContact, KeenAirReact, &s_keenairshootdown3};\r
+statetype s_keenairshootdown3 = {KEENJSHOOTDSPR, KEENJSHOOTDSPR, stepthink, false, false, 6, 0, 0, T_Projectile, KeenContact, KeenAirReact, &s_keenjump3};\r
+\r
+statetype s_keenholdon  = {KEENHANGLSPR, KEENHANGRSPR, step, false, false, 12, 0, 0, 0, KeenPosContact, KeenSimpleReact, &s_keenholdon2};\r
+statetype s_keenholdon2 = {KEENHANGLSPR, KEENHANGRSPR, think, false, false, 0, 0, 0, KeenHoldThink, KeenPosContact, KeenSimpleReact, NULL};\r
+\r
+statetype s_keenclimbup  = {KEENCLIMBEDGEL1SPR, KEENCLIMBEDGER1SPR, step, false, false, 10, 0, 0, T_PullUp1, KeenPosContact, KeenSimpleReact, &s_keenclimbup2};\r
+statetype s_keenclimbup2 = {KEENCLIMBEDGEL2SPR, KEENCLIMBEDGER2SPR, step, false, false, 10, 0, 0, T_PullUp2, KeenPosContact, KeenSimpleReact, &s_keenclimbup3};\r
+statetype s_keenclimbup3 = {KEENCLIMBEDGEL3SPR, KEENCLIMBEDGER3SPR, step, false, false, 10, 0, 0, T_PullUp3, KeenPosContact, KeenSimpleReact, &s_keenclimbup4};\r
+statetype s_keenclimbup4 = {KEENCLIMBEDGEL4SPR, KEENCLIMBEDGER4SPR, step, false, false, 10, 0, 0, T_PulledUp, KeenPosContact, KeenSimpleReact, &s_keenclimbup5};\r
+statetype s_keenclimbup5 = {KEENSTANDLSPR, KEENSTANDRSPR, step, false, false, 6, 0, 0, 0, KeenPosContact, KeenSimpleReact, &s_keenstand};\r
+\r
+Sint16 slopespeed[8] = {0, 0, 4, 4, 8, -4, -4, -8};\r
+Sint16 polexspeed[3] = {-8, 0, 8};\r
+\r
+Sint16 shotsinclip[4] = {0, 8, 5, 5};\r
+Sint16 bonussound[] = {\r
+       SND_GETKEY,SND_GETKEY,SND_GETKEY,SND_GETKEY,\r
+       SND_GETPOINTS,SND_GETPOINTS,SND_GETPOINTS,\r
+       SND_GETPOINTS,SND_GETPOINTS,SND_GETPOINTS,\r
+       SND_EXTRAKEEN,\r
+       SND_GETAMMO\r
+#ifdef KEEN5\r
+       ,SND_GETKEYCARD\r
+#endif\r
+};\r
+Sint16 bonuspoints[] = {\r
+       0, 0, 0, 0,\r
+       100, 200, 500,\r
+       1000, 2000, 5000,\r
+       0,\r
+       0\r
+#ifdef KEEN5\r
+       ,0\r
+#endif\r
+};\r
+Sint16 bonussprite[] = {\r
+       BONUSGEMSPR, BONUSGEMSPR, BONUSGEMSPR, BONUSGEMSPR,\r
+       BONUS100SPR, BONUS200SPR, BONUS500SPR,\r
+       BONUS1000SPR, BONUS2000SPR, BONUS5000SPR,\r
+       BONUS1UPSPR,\r
+       BONUSCLIPSPR\r
+#ifdef KEEN5\r
+       ,BONUSCARDSPR\r
+#endif\r
+};\r
+\r
+Uint16 zeromap = 0;\r
+\r
+// uninitialized variables:\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                LOCAL VARIABLES\r
+\r
+=============================================================================\r
+*/\r
+\r
+Sint16 jumptime;\r
+Sint32 leavepoletime;\r
+Sint16 moonok;\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                               KEEN\r
+\r
+player->temp1 = pausetime / pointer to zees when sleeping\r
+player->temp2 = pausecount / stagecount\r
+player->temp3 =\r
+player->temp4 =\r
+\r
+=============================================================================\r
+*/\r
+\r
+\r
+/*\r
+===============\r
+=\r
+= SpawnKeen\r
+=\r
+===============\r
+*/\r
+\r
+void SpawnKeen(Sint16 x, Sint16 y, Sint16 dir)\r
+{\r
+       player->obclass = keenobj;\r
+       player->active = ac_allways;\r
+       player->priority = 1;\r
+       player->x = CONVERT_TILE_TO_GLOBAL(x);\r
+       player->y = CONVERT_TILE_TO_GLOBAL(y) - 0xF1;   //TODO: weird\r
+\r
+       player->xdir = dir;\r
+       player->ydir = 1;\r
+       NewState(player, &s_keenstand);\r
+}\r
+\r
+//==========================================================================\r
+\r
+/*\r
+======================\r
+=\r
+= CheckGrabPole\r
+=\r
+======================\r
+*/\r
+\r
+boolean CheckGrabPole(objtype *ob)\r
+{\r
+       Uint16 far *map;\r
+\r
+//\r
+// kludgy bit to not let you grab a pole the instant you jump off it\r
+//\r
+       if (lasttimecount < leavepoletime)\r
+       {\r
+               leavepoletime = 0;\r
+       }\r
+       else if (lasttimecount-leavepoletime < 19)\r
+       {\r
+               return false;\r
+       }\r
+\r
+       if (c.yaxis == -1)\r
+       {\r
+               map = mapsegs[1] + mapbwidthtable[(ob->top+6*PIXGLOBAL)/TILEGLOBAL]/2;\r
+       }\r
+       else\r
+       {\r
+               map = mapsegs[1] + mapbwidthtable[ob->tilebottom+1]/2;\r
+       }\r
+\r
+       map += ob->tilemidx;\r
+\r
+       if ((tinf[INTILE + *map] & 0x7F) == INTILE_POLE)\r
+       {\r
+               ob->x = CONVERT_TILE_TO_GLOBAL(ob->tilemidx-1) + 8*PIXGLOBAL;\r
+               xtry = 0;\r
+               ytry = c.yaxis * 32;\r
+               ob->needtoclip = cl_noclip;             // can climb through pole holes\r
+               ob->state = &s_keenpole;\r
+               return true;\r
+       }\r
+       return false;\r
+}\r
+\r
+//==========================================================================\r
+\r
+/*\r
+======================\r
+=\r
+= CheckEnterHouse\r
+=\r
+= Checks for tiles that Keen can interact with by pressing up\r
+=\r
+======================\r
+*/\r
+\r
+boolean CheckEnterHouse(objtype *ob)\r
+{\r
+       Uint16 temp;\r
+#ifdef KEEN5\r
+       Uint16 infoval;\r
+#endif\r
+       Uint16 intile, intile2;\r
+\r
+       intile = tinf[INTILE + *(mapsegs[1]+mapbwidthtable[ob->tiletop]/2+ob->tilemidx)];\r
+       if (intile == INTILE_SWITCH0 || intile == INTILE_SWITCH1 || intile == INTILE_BRIDGESWITCH)\r
+       {\r
+               temp = CONVERT_TILE_TO_GLOBAL(ob->tilemidx) - 4*PIXGLOBAL;\r
+               if (ob->x != temp)\r
+               {\r
+                       ob->temp1 = temp;\r
+                       ob->state = &s_keenlineup;\r
+               }\r
+               else\r
+               {\r
+                       ob->state = &s_keenswitch;\r
+               }\r
+               upheld = true;\r
+               return true;\r
+       }\r
+       else if (intile == INTILE_DOOR || intile == INTILE_KEYCARDDOOR)\r
+       {\r
+               temp = CONVERT_TILE_TO_GLOBAL(ob->tilemidx) + 6*PIXGLOBAL;\r
+               intile2 = tinf[INTILE + *(mapsegs[1]+mapbwidthtable[ob->tiletop]/2+ob->tilemidx-1)];\r
+               if (intile2 == 2 || intile2 == 32)\r
+                       temp -= TILEGLOBAL;\r
+\r
+               // BUG:\r
+               //\r
+               // The s_keenenter? states rely on Keen's ydir being set to 1,\r
+               // which may not always be the case (e.g. if Keen was pushed off\r
+               // a pole by another actor in the level).\r
+               // If ydir is not 1, Keen will not move up during that animation\r
+               // which means the teleport coordinates won't be read from the\r
+               // intended tile position and Keen might end up teleporting to\r
+               // position 0, 0 in the map and thus win the current level.\r
+               // \r
+               // That can easily be avoided by setting ob->ydir to 1 when\r
+               // changing ob->state to s_keenenter0 or s_keenenter1.\r
+\r
+               if (ob->x != temp)\r
+               {\r
+                       ob->temp1 = temp;\r
+                       ob->state = &s_keenlineup;\r
+               }\r
+#ifdef KEEN5\r
+               else if (intile == INTILE_KEYCARDDOOR)\r
+               {\r
+                       if (gamestate.keycard)\r
+                       {\r
+                               gamestate.keycard = false;\r
+                               SD_PlaySound(SND_OPENCARDDOOR);\r
+                               GetNewObj(false);\r
+                               new->x = ob->tilemidx - 2;\r
+                               new->y = ob->tilebottom - 4;\r
+                               new->active = ac_allways;\r
+                               new->needtoclip = cl_noclip;\r
+                               new->obclass = inertobj;\r
+                               NewState(new, &s_carddoor);\r
+                               // Note: no invincibility here - card doors were always used as level exits in Keen 5\r
+                               ob->state = &s_keenenter0;\r
+                               ob->priority = 0;\r
+                               upheld = true;\r
+                               return true;\r
+                       }\r
+                       else\r
+                       {\r
+                               SD_PlaySound(SND_NOWAY);\r
+                               ob->state = &s_keenstand;\r
+                               upheld = true;\r
+                               return false;\r
+                       }\r
+               }\r
+#endif\r
+               else\r
+               {\r
+                       invincible = 110;       //about 1.57 seconds\r
+                       ob->state = &s_keenenter1;\r
+                       ob->priority = 0;\r
+#ifdef KEEN5\r
+                       {\r
+                               infoval = *(mapsegs[2]+mapbwidthtable[ob->tiletop]/2+ob->tilemidx);\r
+                               if (!infoval)\r
+                                       SpawnTeleport();\r
+                       }\r
+#endif\r
+               }\r
+               upheld = true;\r
+               return true;\r
+       }\r
+       return false;\r
+}\r
+\r
+//==========================================================================\r
+\r
+/*\r
+============================\r
+=\r
+= WalkSound1\r
+=\r
+============================\r
+*/\r
+\r
+void WalkSound1(objtype *ob)\r
+{\r
+       SD_PlaySound(SND_WORLDWALK1);\r
+       ob++;                   // shut up compiler\r
+}\r
+\r
+/*\r
+============================\r
+=\r
+= WalkSound2\r
+=\r
+============================\r
+*/\r
+\r
+void WalkSound2(objtype *ob)\r
+{\r
+       SD_PlaySound(SND_WORLDWALK2);\r
+       ob++;                   // shut up compiler\r
+}\r
+\r
+//==========================================================================\r
+\r
+/*\r
+============================\r
+=\r
+= KeenStandThink\r
+=\r
+============================\r
+*/\r
+\r
+void KeenStandThink(objtype *ob)\r
+{\r
+#ifdef KEEN5\r
+       if (ob->hitnorth == 25 && ob->state != &s_keenride)\r
+       {\r
+               ob->state = &s_keenride;\r
+       }\r
+#endif\r
+\r
+       if (c.xaxis)\r
+       {\r
+       // started walking\r
+               ob->state = &s_keenwalk1;\r
+               KeenWalkThink(ob);\r
+               xtry = (Sint16)(ob->xdir * ob->state->xmove * tics) / 4;\r
+       }\r
+       else if (firebutton && !fireheld)\r
+       {\r
+       // shoot current xdir\r
+               fireheld = true;\r
+               if (c.yaxis == -1)\r
+               {\r
+                       ob->state = &s_keenshootup1;\r
+               }\r
+               else\r
+               {\r
+                       ob->state = &s_keenshoot1;\r
+               }\r
+       }\r
+       else if (jumpbutton && ! jumpheld)\r
+       {\r
+       // jump straight up\r
+               jumpheld = true;\r
+               SD_PlaySound(SND_JUMP);\r
+               ob->xspeed = 0;\r
+               ob->yspeed = -40;\r
+               ytry = 0;\r
+               jumptime = 18;\r
+               ob->state = &s_keenjump1;\r
+       }\r
+       else if (pogobutton && !pogoheld)\r
+       {\r
+       // get on pogo\r
+               pogoheld = true;\r
+               SD_PlaySound(SND_JUMP);\r
+               ob->state = &s_keenpogodown;\r
+               ob->xspeed = 0;\r
+               ob->yspeed = -48;\r
+               ytry = 0;\r
+               jumptime = 24;\r
+       }\r
+       else\r
+       {\r
+               switch (c.yaxis)\r
+               {\r
+               case -1:\r
+                       if (CheckGrabPole(ob))\r
+                               break;\r
+                       if (upheld || !CheckEnterHouse(ob))\r
+                       {\r
+                               ob->state = &s_keenlookup;\r
+                       }\r
+                       break;\r
+               case 1:\r
+                       if (CheckGrabPole(ob))\r
+                               break;\r
+                       ob->state = &s_keenlookdown;\r
+               }\r
+               return;\r
+       }\r
+}\r
+\r
+//===========================================================================\r
+\r
+/*\r
+=======================\r
+=\r
+= KeenPauseThink\r
+=\r
+= Do special animations in time\r
+=\r
+=======================\r
+*/\r
+\r
+void KeenPauseThink(objtype *ob)\r
+{\r
+#ifdef KEEN5\r
+       if (ob->hitnorth == 25 && ob->state != &s_keenride)\r
+       {\r
+               ob->state = &s_keenride;\r
+       }\r
+#endif\r
+\r
+       if (c.dir != dir_None || jumpbutton || pogobutton || firebutton)\r
+       {\r
+               ob->temp1 = ob->temp2 = 0;                      // not paused any more\r
+               ob->state = &s_keenstand;\r
+               KeenStandThink(ob);\r
+       }\r
+       else\r
+       {\r
+               //only increase idle counter when NOT standing on a sprite:\r
+               if ((ob->hitnorth & ~7) != 0x18)\r
+                       ob->temp1 = ob->temp1 + tics;\r
+\r
+               switch (ob->temp2)\r
+               {\r
+               case 0:\r
+                       if (ob->temp1 > 200)\r
+                       {\r
+                               ob->temp2++;\r
+                               ob->state = &s_keenpauselook;\r
+                               ob->temp1 = 0;\r
+                       }\r
+                       break;\r
+               case 1:\r
+                       if (ob->temp1 > 300)\r
+                       {\r
+                               ob->temp2++;\r
+                               ob->temp1 = 0;\r
+#ifdef KEEN4\r
+                               if (moonok == 1)\r
+                               {\r
+                                       moonok = 2;     //don't moon again unless the level is restarted\r
+                                       ob->state = &s_keenmoon1;\r
+                               }\r
+                               else\r
+#endif\r
+                               {\r
+                                       ob->state = &s_keenwait1;\r
+                               }\r
+                       }\r
+                       break;\r
+               case 2:\r
+                       if (ob->temp1 > 700)\r
+                       {\r
+                               ob->temp2++;\r
+                               ob->state = &s_keenread;\r
+                               ob->temp1 = 0;\r
+                       }\r
+                       break;\r
+               }\r
+       }\r
+}\r
+\r
+//===========================================================================\r
+\r
+/*\r
+=======================\r
+=\r
+= KeenReadThink\r
+=\r
+=======================\r
+*/\r
+\r
+void KeenReadThink(objtype *ob)\r
+{\r
+       if (storedemo)\r
+       {\r
+               playstate = ex_abortgame;\r
+               IN_ClearKeysDown();\r
+       }\r
+       if (c.dir != dir_None || jumpbutton || pogobutton)\r
+       {\r
+               ob->temp1 = ob->temp2 = 0;\r
+               ob->state = &s_keenstopread;\r
+       }\r
+}\r
+\r
+//===========================================================================\r
+\r
+/*\r
+=======================\r
+=\r
+= KeenLookUpThink\r
+=\r
+=======================\r
+*/\r
+\r
+void KeenLookUpThink(objtype *ob)\r
+{\r
+       if (c.yaxis != -1 || c.xaxis\r
+               || (jumpbutton && !jumpheld)\r
+               || (pogobutton && !pogoheld)\r
+               || firebutton)\r
+       {\r
+               ob->state = &s_keenstand;\r
+               KeenStandThink(ob);\r
+       }\r
+}\r
+\r
+//===========================================================================\r
+\r
+/*\r
+=======================\r
+=\r
+= KeenLookDownThink\r
+=\r
+=======================\r
+*/\r
+\r
+void KeenLookDownThink(objtype *ob)\r
+{\r
+       Uint16 far *map;\r
+       Sint16 y, ymove;\r
+       Uint16 tile;\r
+\r
+       if (jumpbutton && ! jumpheld && (ob->hitnorth & 7) == 1)\r
+       {\r
+       //\r
+       // drop down a level\r
+       //\r
+               jumpheld = true;\r
+\r
+               y = ob->tilebottom;\r
+               map = (Uint16 far *)mapsegs[1] + mapbwidthtable[y]/2 + ob->tilemidx;\r
+               tile = *map;\r
+               if (tinf[WESTWALL+tile] || tinf[EASTWALL+tile] || tinf[SOUTHWALL+tile])\r
+                       return;                         // wall prevents drop down\r
+\r
+               map += mapwidth;\r
+               tile = *map;\r
+               if (tinf[WESTWALL+tile] || tinf[EASTWALL+tile] || tinf[SOUTHWALL+tile])\r
+                       return;                         // wall prevents drop down\r
+\r
+               ymove = max(4, tics) * PIXGLOBAL;\r
+               if (gamestate.riding)\r
+                       ymove += gamestate.riding->ymove;\r
+               ob->bottom += ymove;\r
+               gamestate.riding = NULL;\r
+               ob->y += ymove;\r
+               xtry = ytry = 0;\r
+               ob->state = &s_keenjump3;\r
+               ob->xspeed = ob->yspeed = 0;\r
+               SD_PlaySound(SND_PLUMMET);\r
+       }\r
+       else if (c.yaxis != 1 || c.xaxis\r
+               || (jumpbutton && !jumpheld)\r
+               || (pogobutton && !pogoheld))\r
+       {\r
+               ob->state = &s_keenlookdown4;\r
+       }\r
+}\r
+\r
+//===========================================================================\r
+\r
+/*\r
+=======================\r
+=\r
+= KeenWalkThink\r
+=\r
+=======================\r
+*/\r
+\r
+void KeenWalkThink(objtype *ob)\r
+{\r
+       Sint16 xmove;\r
+\r
+       if (c.xaxis == 0)\r
+       {\r
+       //\r
+       // stopped running\r
+       //\r
+               ob->state = &s_keenstand;\r
+               KeenStandThink(ob);\r
+               return;\r
+       }\r
+\r
+       ob->xdir = c.xaxis;\r
+\r
+       switch (c.yaxis)\r
+       {\r
+       case -1:\r
+               if (CheckGrabPole(ob))\r
+                       return;\r
+               if (upheld)\r
+                       return;\r
+               if (!CheckEnterHouse(ob))\r
+                       break;;\r
+               return;\r
+\r
+       case 1:\r
+               if (!CheckGrabPole(ob))\r
+                       break;\r
+               return;\r
+       }\r
+\r
+       if (firebutton && !fireheld)\r
+       {\r
+       //\r
+       // shoot\r
+       //\r
+               fireheld = true;\r
+               if (c.yaxis == -1)\r
+               {\r
+                       ob->state = &s_keenshootup1;\r
+               }\r
+               else\r
+               {\r
+                       ob->state = &s_keenshoot1;\r
+               }\r
+               return;\r
+       }\r
+\r
+       if (jumpbutton && !jumpheld)\r
+       {\r
+       //\r
+       // running jump\r
+       //\r
+               jumpheld = true;\r
+               SD_PlaySound(SND_JUMP);\r
+               ob->xspeed = ob->xdir * 16;\r
+               ob->yspeed = -40;\r
+               xtry = ytry = 0;\r
+               jumptime = 18;\r
+               ob->state = &s_keenjump1;\r
+       }\r
+\r
+       if (pogobutton && !pogoheld)\r
+       {\r
+       //\r
+       // get on pogo\r
+       //\r
+               pogoheld = true;\r
+               ob->state = &s_keenpogodown;\r
+               SD_PlaySound(SND_JUMP);\r
+               ob->xspeed = ob->xdir * 16;\r
+               ob->yspeed = -48;\r
+               xtry = 0;\r
+               jumptime = 24;\r
+               return;\r
+       }\r
+\r
+       //\r
+       // give speed for slopes\r
+       //\r
+       xmove = slopespeed[ob->hitnorth & 7] * tics;\r
+       xtry += xmove;\r
+\r
+       //\r
+       // handle walking sounds\r
+       //\r
+       if (ob->state == &s_keenwalk1 && ob->temp3 == 0)\r
+       {\r
+               SD_PlaySound(SND_WORLDWALK1);\r
+               ob->temp3 = 1;\r
+       }\r
+       else if (ob->state == &s_keenwalk3 && ob->temp3 == 0)\r
+       {\r
+               SD_PlaySound(SND_WORLDWALK2);\r
+               ob->temp3 = 1;\r
+       }\r
+       else if (ob->state == &s_keenwalk2 ||ob->state == &s_keenwalk4)\r
+       {\r
+               ob->temp3 = 0;\r
+       }\r
+}\r
+\r
+//===========================================================================\r
+\r
+/*\r
+=======================\r
+=\r
+= T_LineUp\r
+=\r
+= Lines up Keen's position for interacting with tiles (temp1 is desired x)\r
+=\r
+=======================\r
+*/\r
+\r
+void T_LineUp(objtype *ob)\r
+{\r
+       Sint16 xmove;\r
+\r
+       xmove = ob->temp1 - ob->x;\r
+       if (xmove < 0)\r
+       {\r
+               xtry = xtry - tics * 16;\r
+               if (xtry > xmove)\r
+                       return;\r
+       }\r
+       else if (xmove > 0)\r
+       {\r
+               xtry = xtry + tics * 16;\r
+               if (xtry < xmove)\r
+                       return;\r
+       }\r
+       xtry = xmove;\r
+       ob->temp1 = 0;\r
+       if (!CheckEnterHouse(ob))\r
+               ob->state = &s_keenstand;\r
+}\r
+\r
+//===========================================================================\r
+\r
+/*\r
+=======================\r
+=\r
+= KeenEnterThink\r
+=\r
+=======================\r
+*/\r
+\r
+void KeenEnterThink(objtype *ob)\r
+{\r
+       Uint16 info;\r
+       Uint16 far *map;\r
+\r
+       map = mapsegs[2] + mapbwidthtable[ob->tilebottom]/2 + ob->tileleft;\r
+       info = *map;\r
+#ifdef KEEN5\r
+       if (!info)\r
+       {\r
+               playstate = ex_portout;\r
+               ob->state = &s_keenenter6;\r
+               return;\r
+       }\r
+       else if (info == 0xB1B1)\r
+       {\r
+               playstate = ex_completed;\r
+               ob->state = &s_keenenter6;\r
+               return;\r
+       }\r
+#endif\r
+       ob->y = (CONVERT_TILE_TO_GLOBAL(info & 0xFF) - TILEGLOBAL) + 15;\r
+       ob->x = CONVERT_TILE_TO_GLOBAL(info >> 8);\r
+       ob->priority = 1;\r
+       ob->needtoclip = cl_noclip;\r
+       ChangeState(ob, ob->state->nextstate);\r
+       ob->needtoclip = cl_midclip;\r
+       CenterActor(ob);\r
+}\r
+\r
+//===========================================================================\r
+\r
+/*\r
+=======================\r
+=\r
+= KeenSwitchThink\r
+=\r
+=======================\r
+*/\r
+\r
+void KeenSwitchThink(objtype *ob)\r
+{\r
+       Uint16 intile, maptile, newtile, info, sx, sy, tileoff;\r
+       Uint16 far *map;\r
+       Uint16 tile, x, y;\r
+       Sint8 manim;\r
+\r
+       tileoff = mapbwidthtable[ob->tiletop]/2 + ob->tilemidx;\r
+       maptile = mapsegs[1][tileoff];\r
+       newtile = maptile + (Sint8)tinf[MANIM + maptile];\r
+       info = mapsegs[2][tileoff];\r
+       sx = info >> 8;\r
+       sy = info & 0xFF;\r
+       intile = tinf[INTILE + maptile];\r
+\r
+       RF_MemToMap(&newtile, 1, ob->tilemidx, ob->tiletop, 1, 1);\r
+       SD_PlaySound(SND_USESWITCH);\r
+       if (intile == INTILE_BRIDGESWITCH)\r
+       {\r
+               //toggle bridge:\r
+               for (y = sy; sy+2 > y; y++)\r
+               {\r
+                       map = mapsegs[1] + mapbwidthtable[y]/2 + sx - (y != sy);\r
+                       for (x = sx - (y != sy); x < mapwidth; x++)\r
+                       {\r
+                               tile = *(map++);\r
+                               manim = tinf[MANIM + tile];\r
+                               if (!manim)\r
+                                       break;\r
+\r
+                               tile += manim;\r
+                               RF_MemToMap(&tile, 1, x, y, 1, 1);\r
+                       }\r
+               }\r
+       }\r
+       else\r
+       {\r
+               //toggle platform blocker:\r
+               map = mapsegs[2] + mapbwidthtable[sy]/2 + sx;\r
+               tile = *map;\r
+#ifdef KEEN5\r
+               if (tile >= DIRARROWSTART && tile < DIRARROWEND)\r
+               {\r
+                       *map = arrowflip[tile-DIRARROWSTART]+DIRARROWSTART;\r
+                       return;\r
+               }\r
+#endif\r
+               *map = tile ^ PLATFORMBLOCK;\r
+       }\r
+}\r
+\r
+//===========================================================================\r
+\r
+/*\r
+=======================\r
+=\r
+= KeenKeyThink\r
+=\r
+=======================\r
+*/\r
+\r
+void KeenKeyThink(objtype *ob)\r
+{\r
+       Uint16 newtile, info, x, y, tileoff;\r
+       Uint16 far *map;\r
+       Uint16 tile, h;\r
+\r
+       tileoff = mapbwidthtable[ob->tilebottom]/2 + ob->tilemidx;\r
+       newtile = mapsegs[1][tileoff] + 18;\r
+       info = mapsegs[2][tileoff];\r
+       x = info >> 8;\r
+       y = info & 0xFF;\r
+       RF_MemToMap(&newtile, 1, ob->tilemidx, ob->tilebottom, 1, 1);\r
+       SD_PlaySound(SND_OPENDOOR);\r
+       GetNewObj(false);\r
+       new->x = x;\r
+       new->y = y;\r
+\r
+       if (x > mapwidth || x < 2 || y > mapheight || y < 2)\r
+               Quit("Keyholder points to a bad spot!");\r
+\r
+       map = mapsegs[1] + mapbwidthtable[y]/2 + x;\r
+       tile = *map;\r
+       h = 1;\r
+       map += mapwidth;\r
+       while (*map == tile)\r
+       {\r
+                       h++;\r
+                       map += mapwidth;\r
+       }\r
+       new->temp1 = h;\r
+       new->active = ac_allways;\r
+       new->needtoclip = cl_noclip;\r
+       new->obclass = inertobj;\r
+       NewState(new, &s_door1);\r
+}\r
+\r
+//===========================================================================\r
+\r
+/*\r
+=======================\r
+=\r
+= KeenAirThink\r
+=\r
+=======================\r
+*/\r
+\r
+void KeenAirThink(objtype *ob)\r
+{\r
+       if (jumpcheat && jumpbutton)\r
+       {\r
+               ob->yspeed = -40;\r
+               jumptime = 18;\r
+               jumpheld = true;\r
+       }\r
+       if (jumptime)\r
+       {\r
+               if (jumptime <= tics)\r
+               {\r
+                       ytry = ob->yspeed * jumptime;\r
+                       jumptime = 0;\r
+               }\r
+               else\r
+               {\r
+                       ytry = ob->yspeed * tics;\r
+                       if (!jumpcheat)\r
+                               jumptime = jumptime - tics;\r
+               }\r
+               if (!jumpbutton)\r
+                       jumptime = 0;\r
+\r
+               if (jumptime == 0 && ob->state->nextstate)\r
+                       ob->state = ob->state->nextstate;       // switch to second jump stage\r
+       }\r
+       else\r
+       {\r
+               if (gamestate.difficulty == gd_Easy)\r
+               {\r
+                       DoWeakGravity(ob);\r
+               }\r
+               else\r
+               {\r
+                       DoGravity(ob);\r
+               }\r
+               if (ob->yspeed > 0 && ob->state != &s_keenjump3 && ob->state != &s_keenjump4)\r
+               {\r
+                       ob->state = ob->state->nextstate;       // switch to third jump stage\r
+               }\r
+       }\r
+\r
+//-------------\r
+\r
+       if (c.xaxis)\r
+       {\r
+               ob->xdir = c.xaxis;\r
+               AccelerateX(ob, c.xaxis*2, 24);\r
+       }\r
+       else\r
+       {\r
+               FrictionX(ob);\r
+       }\r
+\r
+       if (ob->hitsouth == 17)         // going through a pole hole\r
+       {\r
+               ob->xspeed = xtry = 0;\r
+       }\r
+\r
+       if (firebutton && !fireheld)\r
+       {\r
+               fireheld = true;\r
+       //\r
+       // shoot\r
+       //\r
+               switch (c.yaxis)\r
+               {\r
+               case -1:\r
+                       ob->state = &s_keenairshootup1;\r
+                       return;\r
+               case 0:\r
+                       ob->state = &s_keenairshoot1;\r
+                       return;\r
+               case 1:\r
+                       ob->state = &s_keenairshootdown1;\r
+                       return;\r
+               }\r
+       }\r
+\r
+       if (pogobutton && !pogoheld)\r
+       {\r
+               pogoheld = true;\r
+               ob->state = &s_keenpogo;\r
+               jumptime = 0;\r
+               return;\r
+       }\r
+\r
+       if (c.yaxis == -1)\r
+               CheckGrabPole(ob);\r
+}\r
+\r
+//===========================================================================\r
+\r
+/*\r
+=======================\r
+=\r
+= KeenBounceThink\r
+=\r
+= Gives an extra bit of height on the first pogo bounce and creates\r
+= the "impossible pogo trick" when the jump key is held down\r
+=\r
+=======================\r
+*/\r
+\r
+void KeenBounceThink(objtype *ob)\r
+{\r
+       ob->yspeed = -48;\r
+       ytry = ob->yspeed * 6;\r
+       jumptime = 24;\r
+       SD_PlaySound(SND_POGOBOUNCE);\r
+}\r
+\r
+//===========================================================================\r
+\r
+/*\r
+=======================\r
+=\r
+= KeenPogoThink\r
+=\r
+=======================\r
+*/\r
+\r
+void KeenPogoThink(objtype *ob)\r
+{\r
+       if (jumptime)\r
+       {\r
+               if (jumpbutton || jumptime <= 9)\r
+               {\r
+                       DoTinyGravity(ob);\r
+               }\r
+               else\r
+               {\r
+                       DoGravity(ob);\r
+               }\r
+               if (jumptime <= tics)\r
+               {\r
+                       jumptime = 0;\r
+               }\r
+               else\r
+               {\r
+                       jumptime = jumptime - tics;\r
+               }\r
+               if (jumptime == 0 && ob->state->nextstate)\r
+                       ob->state = ob->state->nextstate;\r
+       }\r
+       else\r
+       {\r
+               if (gamestate.difficulty == gd_Easy)\r
+               {\r
+                       DoWeakGravity(ob);\r
+               }\r
+               else\r
+               {\r
+                       DoGravity(ob);\r
+               }\r
+       }\r
+\r
+       if (c.xaxis)\r
+       {\r
+               if (ob->xspeed == 0)\r
+                       ob->xdir = c.xaxis;\r
+               AccelerateX(ob, c.xaxis, 24);\r
+       }\r
+       else\r
+       {\r
+               xtry = xtry + ob->xspeed * tics;\r
+               if (ob->xspeed > 0)\r
+               {\r
+                       ob->xdir = 1;\r
+               }\r
+               else if (ob->xspeed < 0)\r
+               {\r
+                       ob->xdir = -1;\r
+               }\r
+       }\r
+\r
+       if (ob->hitsouth == 17)         // going through a pole hole\r
+       {\r
+               ob->xspeed = xtry = 0;\r
+       }\r
+\r
+       if (firebutton && !fireheld)\r
+       {\r
+               fireheld = true;\r
+               switch (c.yaxis)\r
+               {\r
+               case -1:\r
+                       ob->state = &s_keenairshootup1;\r
+                       return;\r
+               case 0:\r
+                       ob->state = &s_keenairshoot1;\r
+                       return;\r
+               case 1:\r
+                       ob->state = &s_keenairshootdown1;\r
+                       return;\r
+               }\r
+       }\r
+\r
+       if (pogobutton && !pogoheld)\r
+       {\r
+               pogoheld = true;\r
+               ob->state = &s_keenjump3;\r
+       }\r
+}\r
+\r
+//===========================================================================\r
+\r
+/*\r
+=======================\r
+=\r
+= PoleActions\r
+=\r
+=======================\r
+*/\r
+\r
+void PoleActions(objtype *ob)\r
+{\r
+       if (c.xaxis)\r
+               ob->xdir = c.xaxis;\r
+\r
+       if (firebutton && !fireheld)\r
+       {\r
+               fireheld = true;\r
+               switch (c.yaxis)\r
+               {\r
+               case -1:\r
+                       ob->state = &s_keenpoleshootup1;\r
+                       break;\r
+               case 0:\r
+                       ob->state = &s_keenpoleshoot1;\r
+                       break;\r
+               case 1:\r
+                       ob->state = &s_keenpoleshootdown1;\r
+                       break;\r
+               }\r
+       }\r
+\r
+       if (jumpbutton && !jumpheld)            // jump off the pole\r
+       {\r
+               jumpheld = true;\r
+               SD_PlaySound(SND_JUMP);\r
+               ob->xspeed = polexspeed[c.xaxis+1];\r
+               ob->yspeed = -20;\r
+               ob->needtoclip = cl_midclip;\r
+               jumptime = 10;\r
+               ob->state = &s_keenjump1;\r
+               ob->ydir = 1;\r
+               leavepoletime = lasttimecount;\r
+       }\r
+}\r
+\r
+/*\r
+=======================\r
+=\r
+= KeenPoleThink\r
+=\r
+=======================\r
+*/\r
+\r
+void KeenPoleThink(objtype *ob)\r
+{\r
+       Uint16 tile;\r
+       Uint16 far *map;\r
+\r
+       switch (c.yaxis)\r
+       {\r
+       case -1:\r
+               ob->state = &s_keenclimb1;\r
+               ob->ydir = -1;\r
+               return;\r
+       case 1:\r
+               ob->state = &s_keenslide1;\r
+               ob->ydir = 1;\r
+               KeenDropThink(ob);\r
+               return;\r
+       }\r
+\r
+       if (c.xaxis)\r
+       {\r
+       //\r
+       // walk off pole if right next to ground\r
+       //\r
+               map = mapsegs[1] + (mapbwidthtable[ob->tilebottom+1]/2 + ob->tilemidx);\r
+               tile = *map;\r
+               if (tinf[NORTHWALL+tile])\r
+               {\r
+                       ob->xspeed = 0;\r
+                       ob->yspeed = 0;\r
+                       ob->needtoclip = cl_midclip;\r
+                       jumptime = 0;\r
+                       ob->state = &s_keenjump3;\r
+                       ob->ydir = 1;\r
+                       SD_PlaySound(SND_PLUMMET);\r
+                       return;\r
+               }\r
+       }\r
+       PoleActions(ob);\r
+}\r
+\r
+/*\r
+=======================\r
+=\r
+= KeenClimbThink\r
+=\r
+=======================\r
+*/\r
+\r
+void KeenClimbThink(objtype *ob)\r
+{\r
+       Uint16 far *map;\r
+\r
+       map = mapsegs[1] + mapbwidthtable[ob->tiletop]/2 + ob->tilemidx;\r
+       \r
+       if ((tinf[INTILE+*map] & 0x7F) != INTILE_POLE)\r
+       {\r
+               ytry = 0;\r
+               ob->state = &s_keenpole;                // ran out of pole\r
+               PoleActions(ob);\r
+               return;\r
+       }\r
+\r
+       switch (c.yaxis)\r
+       {\r
+       case 0:\r
+               ob->state = &s_keenpole;\r
+               ob->ydir = 0;\r
+               break;\r
+\r
+       case 1:\r
+               ob->state = &s_keenslide1;\r
+               ob->ydir = 1;\r
+               KeenDropThink(ob);\r
+               break;\r
+       }\r
+\r
+       PoleActions(ob);\r
+}\r
+\r
+/*\r
+=======================\r
+=\r
+= KeenDropThink\r
+=\r
+=======================\r
+*/\r
+\r
+void KeenDropThink(objtype *ob)\r
+{\r
+       Uint16 far *map;\r
+       Uint16 y;\r
+\r
+       y = CONVERT_GLOBAL_TO_TILE(ob->bottom - 4*PIXGLOBAL);\r
+       map = mapsegs[1] + mapbwidthtable[y]/2 + ob->tilemidx;\r
+\r
+       if ((tinf[INTILE+*map] & 0x7F) != INTILE_POLE)\r
+       {\r
+               SD_PlaySound(SND_PLUMMET);\r
+               ob->state = &s_keenjump3;               // ran out of pole\r
+               jumptime = 0;\r
+               ob->xspeed = polexspeed[c.xaxis+1];\r
+               ob->yspeed = 0;\r
+               ob->needtoclip = cl_midclip;\r
+               ob->tilebottom--;\r
+               return;\r
+       }\r
+\r
+       switch (c.yaxis)\r
+       {\r
+       case -1:\r
+               ob->state = &s_keenclimb1;\r
+               ob->ydir = -1;\r
+               break;\r
+\r
+       case 0:\r
+               ob->state = &s_keenpole;\r
+               ob->ydir = 0;\r
+               break;\r
+       }\r
+\r
+       PoleActions(ob);\r
+}\r
+\r
+//===========================================================================\r
+\r
+/*\r
+=======================\r
+=\r
+= KeenDropDownThink\r
+=\r
+=======================\r
+*/\r
+\r
+void KeenDropDownThink(objtype *ob)\r
+{\r
+       ob->needtoclip = cl_midclip;\r
+}\r
+\r
+//===========================================================================\r
+\r
+/*\r
+=======================\r
+=\r
+= KeenHoldThink\r
+=\r
+=======================\r
+*/\r
+\r
+void KeenHoldThink(objtype *ob)\r
+{\r
+       Uint16 tile;\r
+\r
+       if (c.yaxis == -1 || ob->xdir == c.xaxis)\r
+       {\r
+               ob->state = &s_keenclimbup;\r
+               if (ob->xdir == 1)\r
+               {\r
+                       tile = *(mapsegs[1]+mapbwidthtable[ob->tiletop-1]/2+ob->tileright);\r
+               }\r
+               else\r
+               {\r
+                       tile = *(mapsegs[1]+mapbwidthtable[ob->tiletop-1]/2+ob->tileleft);\r
+               }\r
+               if (ob->xdir == 1)\r
+               {\r
+                       ytry = -16*PIXGLOBAL;\r
+               }\r
+               else\r
+               {\r
+                       ytry = -8*PIXGLOBAL;\r
+               }\r
+               if (!(tinf[INTILE+tile] & INTILE_FOREGROUND))\r
+                       ob->priority = 3;\r
+       }\r
+       else if (c.yaxis == 1 || c.xaxis && ob->xdir != c.xaxis)\r
+       {\r
+               ob->state = &s_keenjump3;\r
+               ob->needtoclip = cl_midclip;\r
+       }\r
+}\r
+\r
+//===========================================================================\r
+\r
+/*\r
+=======================\r
+=\r
+= KeenShootThink\r
+=\r
+=======================\r
+*/\r
+\r
+void KeenShootThink(objtype *ob)\r
+{\r
+// can't use &<var> in a switch statement...\r
+\r
+       if (ob->state == &s_keenshoot1)\r
+       {\r
+               if (ob->xdir == 1)\r
+               {\r
+                       SpawnShot(ob->x + 16*PIXGLOBAL, ob->y + 4*PIXGLOBAL, dir_East);\r
+               }\r
+               else\r
+               {\r
+                       SpawnShot(ob->x - 8*PIXGLOBAL, ob->y + 4*PIXGLOBAL, dir_West);\r
+               }\r
+       }\r
+       if (ob->state == &s_keenairshoot2)\r
+       {\r
+               if (ob->xdir == 1)\r
+               {\r
+                       SpawnShot(ob->x + 16*PIXGLOBAL, ob->y + 2*PIXGLOBAL, dir_East);\r
+               }\r
+               else\r
+               {\r
+                       SpawnShot(ob->x, ob->y + 2*PIXGLOBAL, dir_West);\r
+               }\r
+       }\r
+       if (ob->state == &s_keenairshootdown2)\r
+       {\r
+               SpawnShot(ob->x + 8*PIXGLOBAL, ob->y + 18*PIXGLOBAL, dir_South);\r
+       }\r
+       if (ob->state == &s_keenairshootup2)\r
+       {\r
+               SpawnShot(ob->x + 5*PIXGLOBAL, ob->y - 10*PIXGLOBAL, dir_North);\r
+       }\r
+       if (ob->state == &s_keenshootup1)\r
+       {\r
+               SpawnShot(ob->x + 5*PIXGLOBAL, ob->y - 10*PIXGLOBAL, dir_North);\r
+       }\r
+       if (ob->state == &s_keenpoleshoot1)\r
+       {\r
+               if (ob->xdir == 1)\r
+               {\r
+                       SpawnShot(ob->x + 16*PIXGLOBAL, ob->y + 4*PIXGLOBAL, dir_East);\r
+               }\r
+               else\r
+               {\r
+                       SpawnShot(ob->x - 8*PIXGLOBAL, ob->y + 4*PIXGLOBAL, dir_West);\r
+               }\r
+       }\r
+       if (ob->state == &s_keenpoleshootup1)\r
+       {\r
+               if (ob->xdir == 1)\r
+               {\r
+                       SpawnShot(ob->x + 6*PIXGLOBAL, ob->y + 4*PIXGLOBAL, dir_North);\r
+               }\r
+               else\r
+               {\r
+                       SpawnShot(ob->x + 12*PIXGLOBAL, ob->y + 4*PIXGLOBAL, dir_North);\r
+               }\r
+       }\r
+       if (ob->state == &s_keenpoleshootdown1)\r
+       {\r
+               if (ob->xdir == 1)\r
+               {\r
+                       SpawnShot(ob->x + 6*PIXGLOBAL, ob->y + 24*PIXGLOBAL, dir_South);\r
+               }\r
+               else\r
+               {\r
+                       SpawnShot(ob->x + 12*PIXGLOBAL, ob->y + 24*PIXGLOBAL, dir_South);\r
+               }\r
+       }\r
+}\r
+\r
+//===========================================================================\r
+\r
+/*\r
+=======================\r
+=\r
+= T_PullUp1\r
+=\r
+=======================\r
+*/\r
+\r
+void T_PullUp1(objtype *ob)\r
+{\r
+       if (ob->xdir == 1)\r
+       {\r
+               xtry = 8*PIXGLOBAL;\r
+       }\r
+       else\r
+       {\r
+               ytry = -8*PIXGLOBAL;\r
+       }\r
+}\r
+\r
+/*\r
+=======================\r
+=\r
+= T_PullUp2\r
+=\r
+=======================\r
+*/\r
+\r
+void T_PullUp2(objtype *ob)\r
+{\r
+       if (ob->xdir == 1)\r
+       {\r
+               xtry = 8*PIXGLOBAL;\r
+               ytry = -8*PIXGLOBAL;\r
+       }\r
+       else\r
+       {\r
+               xtry = -8*PIXGLOBAL;\r
+               ytry = -8*PIXGLOBAL;\r
+       }\r
+}\r
+\r
+/*\r
+=======================\r
+=\r
+= T_PullUp3\r
+=\r
+=======================\r
+*/\r
+\r
+#pragma argsused\r
+void T_PullUp3(objtype *ob)\r
+{\r
+       ytry = -8*PIXGLOBAL;\r
+}\r
+\r
+/*\r
+=======================\r
+=\r
+= T_PulledUp\r
+=\r
+=======================\r
+*/\r
+\r
+void T_PulledUp(objtype *ob)\r
+{\r
+       ob->needtoclip = cl_midclip;\r
+       ob->priority = 1;\r
+       ytry = 8*PIXGLOBAL;\r
+}\r
+\r
+//===========================================================================\r
+\r
+/*\r
+=======================\r
+=\r
+= KeenDieThink\r
+=\r
+=======================\r
+*/\r
+\r
+void KeenDieThink(objtype *ob)\r
+{\r
+       DoWeakGravity(ob);\r
+       xtry = ob->xspeed * tics;\r
+       if (!OnScreen(ob))\r
+               playstate = ex_died;\r
+}\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                               CONTACT ROUTINES\r
+\r
+=============================================================================\r
+*/\r
+\r
+/*\r
+============================\r
+=\r
+= KillKeen\r
+=\r
+============================\r
+*/\r
+\r
+void KillKeen(void)\r
+{\r
+       if (invincible || godmode)\r
+               return;\r
+\r
+       if (player->state != &s_keendead)\r
+       {\r
+\r
+               moonok = 0;\r
+               invincible = 30;        //0.43 seconds\r
+               keenkilled = true;\r
+               player->needtoclip = cl_noclip;\r
+               player->priority = 3;\r
+#ifdef KEEN4\r
+               if (mapon == 17)\r
+               {\r
+                       if (US_RndT() < 0x80)\r
+                       {\r
+                               ChangeState(player, &s_keensuitdie1);\r
+                       }\r
+                       else\r
+                       {\r
+                               ChangeState(player, &s_keensuitdie2);\r
+                       }\r
+               }\r
+               else\r
+#endif\r
+               {\r
+                       if (US_RndT() < 0x80)\r
+                       {\r
+                               ChangeState(player, &s_keendie1);\r
+                       }\r
+                       else\r
+                       {\r
+                               ChangeState(player, &s_keendie2);\r
+                       }\r
+               }\r
+               SD_PlaySound(SND_KEENDEAD);\r
+               player->yspeed = -40;\r
+               player->xspeed = 16;\r
+       }\r
+}\r
+\r
+/*\r
+============================\r
+=\r
+= KeenContact\r
+=\r
+============================\r
+*/\r
+\r
+void KeenContact(objtype *ob, objtype *hit)\r
+{\r
+       switch (hit->obclass)\r
+       {\r
+       case bonusobj:\r
+               switch (hit->temp1)\r
+               {\r
+               case 0:\r
+               case 1:\r
+               case 2:\r
+               case 3:\r
+               case 4:\r
+               case 5:\r
+               case 6:\r
+               case 7:\r
+               case 8:\r
+               case 9:\r
+               case 10:\r
+               case 11:\r
+#ifdef KEEN5\r
+               case 12:\r
+#endif\r
+                       SD_PlaySound(bonussound[hit->temp1]);\r
+                       hit->obclass = inertobj;\r
+                       hit->priority = 3;\r
+                       hit->shapenum = bonussprite[hit->temp1];\r
+                       GivePoints(bonuspoints[hit->temp1]);\r
+                       if (hit->temp1 < 4)\r
+                       {\r
+                               gamestate.keys[hit->temp1]++;\r
+                       }\r
+                       else if (hit->temp1 == 10)\r
+                       {\r
+                               gamestate.lives++;\r
+                       }\r
+                       else if (hit->temp1 == 11)\r
+                       {\r
+                               gamestate.ammo += shotsinclip[gamestate.difficulty];\r
+                       }\r
+#ifdef KEEN5\r
+                       else if (hit->temp1 == 12)\r
+                       {\r
+                               gamestate.keycard = true;\r
+                       }\r
+#endif\r
+                       ChangeState(hit, &s_bonusrise);\r
+               }\r
+               break;\r
+\r
+#if defined KEEN4\r
+       case oracleobj:\r
+               if (!ob->hitnorth)\r
+                       break;\r
+\r
+               if (mapon == 14)\r
+               {\r
+                       RescueJanitor();\r
+                       RF_ForceRefresh();\r
+                       RemoveObj(hit);\r
+               }\r
+               else\r
+               {\r
+                       SD_PlaySound(SND_LINDSEY);\r
+                       playstate = ex_rescued;\r
+               }\r
+               break;\r
+       case stunnedobj:\r
+               if (hit->temp4 != bounderobj)\r
+                       break;\r
+               //no break here -- drop through to platformobj is intended!\r
+       case platformobj:\r
+               if (!gamestate.riding)\r
+               {\r
+                       ClipToSpriteTop(ob, hit);\r
+               }\r
+               else\r
+                       return;\r
+               break;\r
+       case bounderobj:\r
+               ClipToSprite(ob, hit, false);\r
+               break;\r
+       case lindseyobj:\r
+               PrincessLindsey();\r
+               RemoveObj(hit);\r
+               RF_ForceRefresh();\r
+               break;\r
+       case footobj:\r
+               playstate = ex_foot;\r
+               break;\r
+#elif defined KEEN5\r
+       case platformobj:\r
+               if (!gamestate.riding)\r
+                       ClipToSpriteTop(ob, hit);\r
+               break;\r
+#elif defined KEEN6\r
+       case stunshotobj:\r
+               if (hit->temp4)\r
+               {\r
+                       ExplodeShot(hit);\r
+                       ChangeState(ob, &s_keenstun);\r
+               }\r
+               // BUG: there is no break here - this causes the impossible bullet bug\r
+       case platformobj:\r
+               if (!gamestate.riding)\r
+                       ClipToSpriteTop(ob, hit);\r
+               break;\r
+#endif\r
+       }\r
+}\r
+\r
+/*\r
+============================\r
+=\r
+= KeenPosContact\r
+=\r
+============================\r
+*/\r
+\r
+void KeenPosContact(objtype *ob, objtype *hit)\r
+{\r
+       switch (hit->obclass)\r
+       {\r
+#if defined KEEN4\r
+       case platformobj:\r
+               // BUG: priority is not reset here\r
+               ob->needtoclip = cl_midclip;\r
+               ChangeState(ob, &s_keenjump3);\r
+               jumptime = ob->xspeed = ob->yspeed = 0;\r
+               ClipToSpriteTop(ob, hit);\r
+               break;\r
+       case madmushroomobj:\r
+       case arachnutobj:\r
+       case berkeloidobj:\r
+               KillKeen();\r
+               break;\r
+       case bounderobj:\r
+               ob->priority = 1;\r
+               ob->needtoclip = cl_midclip;\r
+               ChangeState(ob, &s_keenjump3);\r
+               jumptime = ob->xspeed = ob->yspeed = 0;\r
+               ClipToSprite(ob, hit, false);\r
+               break;\r
+#elif defined KEEN5\r
+       case platformobj:\r
+               // BUG: priority is not reset here\r
+               ob->needtoclip = cl_midclip;\r
+               ChangeState(ob, &s_keenjump3);\r
+               jumptime = ob->xspeed = ob->yspeed = 0;\r
+               ClipToSpriteTop(ob, hit);\r
+               break;\r
+       case amptonobj:\r
+       case scottieobj:\r
+               ob->priority = 1;\r
+               ob->needtoclip = cl_midclip;\r
+               ChangeState(ob, &s_keenjump3);\r
+               jumptime = ob->xspeed = ob->yspeed = 0;\r
+               break;\r
+#elif defined KEEN6\r
+       case platformobj:\r
+       case gikobj:\r
+       case flectobj:\r
+       case bloogletobj:\r
+               ob->priority = 1;\r
+               ob->needtoclip = cl_midclip;\r
+               ChangeState(ob, &s_keenjump3);\r
+               jumptime = ob->xspeed = ob->yspeed = 0;\r
+               ClipToSpriteTop(ob, hit);       // BUG: allows Keen to stand on Blooglets and Flects\r
+               break;\r
+#endif\r
+       }\r
+}\r
+\r
+/*\r
+============================\r
+=\r
+= HandleRiding\r
+=\r
+============================\r
+*/\r
+\r
+void HandleRiding(objtype *ob)\r
+{\r
+       objtype *plat;\r
+\r
+       plat = gamestate.riding;\r
+       if (ob->right < plat->left || ob->left > plat->right)\r
+       {\r
+               gamestate.riding = NULL;\r
+       }\r
+       else if (ob->ymove < 0)\r
+       {\r
+               gamestate.riding = NULL;\r
+               if (plat->ymove < 0)\r
+               {\r
+                       xtry = 0;\r
+                       ytry = plat->ymove;\r
+                       PushObj(ob);\r
+               }\r
+       }\r
+       else\r
+       {\r
+               xtry = plat->xmove;\r
+               ytry = plat->top - ob->bottom - 16;\r
+               PushObj(ob);\r
+\r
+#if GRMODE == CGAGR\r
+               if (ob->xmove == plat->xmove)\r
+               {\r
+                       ob->x &= ~0x3F;\r
+                       ob->x |= plat->x & 0x3F;\r
+               }\r
+#else\r
+               if (nopan)\r
+               {\r
+                       ob->x &= ~0x7F;\r
+                       ob->x |= plat->x & 0x7F;\r
+               }\r
+               else\r
+               {\r
+                       ob->x |= plat->x & 0x1F;\r
+               }\r
+#endif\r
+\r
+               if (ob->hitsouth)\r
+               {\r
+                       gamestate.riding = NULL;\r
+               }\r
+               else\r
+               {\r
+                       ob->hitnorth = 25;\r
+               }\r
+       }\r
+}\r
+\r
+/*\r
+============================\r
+=\r
+= TileBonus\r
+=\r
+============================\r
+*/\r
+\r
+void TileBonus(Uint16 x, Uint16 y, Uint16 bonus)\r
+{\r
+       RF_MemToMap(&zeromap, 1, x, y, 1, 1);\r
+       SD_PlaySound(bonussound[bonus]);\r
+       GivePoints(bonuspoints[bonus]);\r
+       if (bonus < 4)\r
+       {\r
+               gamestate.keys[bonus]++;\r
+       }\r
+       else if (bonus == 10)\r
+       {\r
+               gamestate.lives++;\r
+       }\r
+       else if (bonus == 11)\r
+       {\r
+               gamestate.ammo += shotsinclip[gamestate.difficulty];\r
+       }\r
+       GetNewObj(true);\r
+       new->obclass = inertobj;\r
+       new->priority = 3;\r
+       new->x = CONVERT_TILE_TO_GLOBAL(x);\r
+       new->y = CONVERT_TILE_TO_GLOBAL(y);\r
+       new->ydir = -1;\r
+       new->temp2 = new->shapenum = bonussprite[bonus];\r
+       NewState(new, &s_bonusrise);\r
+       new->needtoclip = cl_noclip;\r
+}\r
+\r
+/*\r
+============================\r
+=\r
+= GiveDrop\r
+=\r
+============================\r
+*/\r
+\r
+void GiveDrop(Uint16 x, Uint16 y)\r
+{\r
+       RF_MemToMap(&zeromap, 1, x, y, 1, 1);\r
+       SD_PlaySound(SND_GETWATER);\r
+       SpawnSplash(x, y);\r
+       if (++gamestate.drops == 100)\r
+       {\r
+               gamestate.drops = 0;\r
+               SD_PlaySound(SND_EXTRAKEEN);\r
+               gamestate.lives++;\r
+               GetNewObj(true);\r
+               new->obclass = inertobj;\r
+               new->priority = 3;\r
+               new->x = CONVERT_TILE_TO_GLOBAL(x);\r
+               new->y = CONVERT_TILE_TO_GLOBAL(y-1);\r
+               new->ydir = -1;\r
+               new->temp2 = new->shapenum = BONUS100UPSPR;\r
+               NewState(new, &s_bonusrise);\r
+               new->needtoclip = cl_noclip;\r
+       }\r
+}\r
+\r
+/*\r
+============================\r
+=\r
+= CheckInTiles\r
+=\r
+============================\r
+*/\r
+\r
+void CheckInTiles(objtype *ob)\r
+{\r
+       Uint16 x, y;\r
+       Uint16 far *map;\r
+       Uint16 rowdelta, intile, midx;\r
+\r
+       if (moonok == 1)\r
+               moonok = 0;\r
+\r
+       map = mapsegs[1] + mapbwidthtable[ob->tiletop]/2 + ob->tileleft;\r
+       rowdelta = mapwidth - (ob->tileright - ob->tileleft + 1);\r
+       for (y = ob->tiletop; y <= ob->tilebottom; y++, map += rowdelta)\r
+       {\r
+               for (x = ob->tileleft; x <= ob->tileright; x++, map++)\r
+               {\r
+                       if ((intile = tinf[INTILE + *map] & INTILE_TYPEMASK) != 0)\r
+                       {\r
+                               switch (intile)\r
+                               {\r
+                               case INTILE_DEADLY:\r
+                                       KillKeen();\r
+                                       break;\r
+\r
+                               case INTILE_DROP:\r
+                                       GiveDrop(x, y);\r
+                                       break;\r
+\r
+                               case INTILE_GEMSOCKET0:\r
+                               case INTILE_GEMSOCKET1:\r
+                               case INTILE_GEMSOCKET2:\r
+                               case INTILE_GEMSOCKET3:\r
+                                       if (ob->tilebottom != y || !ob->hitnorth\r
+                                               || ob->state == &s_keenkey\r
+                                               || !gamestate.keys[intile-INTILE_GEMSOCKET0])\r
+                                       {\r
+                                               return;\r
+                                       }\r
+\r
+                                       midx = CONVERT_TILE_TO_GLOBAL(x) + -4*PIXGLOBAL;\r
+                                       if (ob->x != midx)\r
+                                       {\r
+                                               ob->temp1 = midx;\r
+                                               ob->state = &s_keenlineup;\r
+                                               return;\r
+                                       }\r
+                                       else\r
+                                       {\r
+                                               gamestate.keys[intile-INTILE_GEMSOCKET0]--;\r
+                                               ChangeState(ob, &s_keenkey);\r
+                                       }\r
+                                       break;\r
+\r
+                               case INTILE_MOON:\r
+                                       if (moonok == 0)\r
+                                               moonok = 1;\r
+                                       break;\r
+\r
+                               case INTILE_BONUS100:\r
+                               case INTILE_BONUS200:\r
+                               case INTILE_BONUS500:\r
+                               case INTILE_BONUS1000:\r
+                               case INTILE_BONUS2000:\r
+                               case INTILE_BONUS5000:\r
+                               case INTILE_EXTRALIFE:\r
+                               case INTILE_AMMO:\r
+                                       TileBonus(x, y, (intile-INTILE_BONUS100)+4);\r
+                                       break;\r
+                               }\r
+                       }\r
+               }\r
+       }\r
+}\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                REACTION ROUTINES\r
+\r
+=============================================================================\r
+*/\r
+\r
+\r
+/*\r
+============================\r
+=\r
+= KeenSimpleReact\r
+=\r
+============================\r
+*/\r
+\r
+void KeenSimpleReact(objtype *ob)\r
+{\r
+       RF_PlaceSprite(&ob->sprite, ob->x, ob->y, ob->shapenum, spritedraw, ob->priority);\r
+}\r
+\r
+\r
+/*\r
+============================\r
+=\r
+= KeenStandReact\r
+=\r
+============================\r
+*/\r
+\r
+void KeenStandReact(objtype *ob)\r
+{\r
+       if (!ob->hitnorth)\r
+       {\r
+       //\r
+       // walked off an edge\r
+       //\r
+               SD_PlaySound(SND_PLUMMET);\r
+               ob->xspeed = ob->xdir * 8;\r
+               ob->yspeed = 0;\r
+               ChangeState(ob, &s_keenjump3);\r
+               jumptime = 0;\r
+       }\r
+       else if ((ob->hitnorth & ~7) == 8)      // deadly floor!\r
+       {\r
+               KillKeen();\r
+       }\r
+       else if (ob->hitnorth == 41)\r
+       {\r
+               xtry = tics * 8;\r
+               ytry = 0;\r
+               ob->temp1 = 0;\r
+               ClipToWalls(ob);\r
+       }\r
+       else if (ob->hitnorth == 49)\r
+       {\r
+               xtry = tics * -8;\r
+               ytry = 0;\r
+               ob->temp1 = 0;\r
+               ClipToWalls(ob);\r
+       }\r
+\r
+       RF_PlaceSprite(&ob->sprite, ob->x, ob->y, ob->shapenum, spritedraw, ob->priority);\r
+}\r
+\r
+/*\r
+============================\r
+=\r
+= KeenWalkReact\r
+=\r
+============================\r
+*/\r
+\r
+void KeenWalkReact(objtype *ob)\r
+{\r
+       if (!ob->hitnorth)\r
+       {\r
+       //\r
+       // walked off an edge\r
+       //\r
+               SD_PlaySound(SND_PLUMMET);\r
+               ob->xspeed = ob->xdir * 8;\r
+               ob->yspeed = 0;\r
+               ChangeState(ob, &s_keenjump3);\r
+               jumptime = 0;\r
+       }\r
+       else if ((ob->hitnorth & ~7) == 8)      // deadly floor!\r
+       {\r
+               KillKeen();\r
+       }\r
+       else if (ob->hitnorth == 41)\r
+       {\r
+               xtry = tics * 8;\r
+               ytry = 0;\r
+               ClipToWalls(ob);\r
+       }\r
+       else if (ob->hitnorth == 49)\r
+       {\r
+               xtry = tics * -8;\r
+               ytry = 0;\r
+               ClipToWalls(ob);\r
+       }\r
+       else if (ob->hiteast && ob->xdir == -1 || ob->hitwest && ob->xdir == 1)\r
+       {\r
+       //\r
+       // ran into a wall\r
+       //\r
+               ob->ticcount = 0;\r
+               ob->state = &s_keenstand;\r
+               ob->shapenum = ob->xdir == 1? s_keenstand.rightshapenum : s_keenstand.leftshapenum;\r
+       }\r
+\r
+       RF_PlaceSprite(&ob->sprite, ob->x, ob->y, ob->shapenum, spritedraw, ob->priority);\r
+}\r
+\r
+/*\r
+============================\r
+=\r
+= KeenAirReact\r
+=\r
+============================\r
+*/\r
+\r
+void KeenAirReact(objtype *ob)\r
+{\r
+       Uint16 far *map;\r
+       Uint16 oldtop, graby, ty, tile;\r
+\r
+       if (ob->hiteast && ob->xdir == -1 || ob->hitwest && ob->xdir == 1)\r
+               ob->xspeed = 0;\r
+\r
+       if (ob->hitsouth)\r
+       {\r
+               if (ob->hitsouth == 17) // jumping up through a pole hole\r
+               {\r
+                       ob->y -= 2*PIXGLOBAL;\r
+                       ob->top -= 2*PIXGLOBAL;\r
+                       ob->xspeed = 0;\r
+                       ob->x = CONVERT_TILE_TO_GLOBAL(ob->tilemidx) - 2*PIXGLOBAL;\r
+               }\r
+               else\r
+               {\r
+#ifdef KEEN6\r
+                       if (ob->hitsouth == 33)\r
+                       {\r
+                               FlipBigSwitch(ob, false);\r
+                       }\r
+#endif\r
+                       if (!jumpcheat)\r
+                       {\r
+                               SD_PlaySound(SND_HELMETHIT);\r
+                               if (ob->hitsouth > 1)\r
+                               {\r
+                                       ob->yspeed += 16;\r
+                                       if (ob->yspeed < 0)     // push away from slopes\r
+                                               ob->yspeed = 0;\r
+                               }\r
+                               else\r
+                               {\r
+                                       ob->yspeed = 0;\r
+                               }\r
+                               jumptime = 0;\r
+                       }\r
+               }\r
+       }\r
+\r
+       if (ob->hitnorth)\r
+       {\r
+               ob->ymove = 0;\r
+               if ((ob->hitnorth & ~7) == 8)   // deadly floor!\r
+               {\r
+                       KillKeen();\r
+               }\r
+               else\r
+               {\r
+#if defined KEEN5\r
+                       if (ob->hitnorth == 57)\r
+                       {\r
+                               SD_PlaySound(SND_LANDONFUSE);\r
+                       }\r
+#elif defined KEEN6\r
+                       if (ob->hitnorth == 33)\r
+                       {\r
+                               FlipBigSwitch(ob, true);\r
+                       }\r
+#endif\r
+                       if (ob->hitnorth != 25 || !jumptime)    // KLUDGE to allow jumping off\r
+                       {\r
+                               ob->temp1 = ob->temp2 = 0;\r
+                               if (ob->state == &s_keenairshoot1)\r
+                               {\r
+                                       ChangeState(ob, &s_keenshoot1);\r
+                               }\r
+                               else if (ob->state == &s_keenairshootup1)\r
+                               {\r
+                                       ChangeState(ob, &s_keenshootup1);\r
+                               }\r
+                               else if (c.xaxis)\r
+                               {\r
+                                       ChangeState(ob, &s_keenwalk1);\r
+                               }\r
+                               else\r
+                               {\r
+                                       ChangeState(ob, &s_keenstand);\r
+                               }\r
+                               SD_PlaySound(SND_LAND);\r
+                       }\r
+               }\r
+       }\r
+       else if (ob->ymove > 0)\r
+       {\r
+//\r
+// check if there is an edge to grab\r
+//\r
+               oldtop = ob->top - ob->ymove;\r
+               graby = ((ob->top - 4*PIXGLOBAL) & 0xFF00) + 4*PIXGLOBAL;\r
+               ty = CONVERT_GLOBAL_TO_TILE(graby) - 1;\r
+               if (oldtop < graby && ob->top >= graby)\r
+               {\r
+                       if (c.xaxis == -1)\r
+                       {\r
+                               map = mapsegs[1] + mapbwidthtable[ty]/2 + ob->tileleft;\r
+                               if (ob->hiteast)\r
+                                       map--;\r
+                               tile = *map;\r
+                               if (!tinf[EASTWALL + tile] && !tinf[WESTWALL + tile]\r
+                                       && !tinf[NORTHWALL + tile] && !tinf[SOUTHWALL + tile]\r
+                                       && tinf[EASTWALL + map[mapwidth]] && tinf[NORTHWALL + map[mapwidth]]\r
+                                       )\r
+                               {\r
+                                       ob->xdir = -1;\r
+                                       ob->needtoclip = cl_noclip;\r
+                                       ob->x = (ob->x & 0xFF00) + 8*PIXGLOBAL;\r
+                                       ob->y = graby - 4*PIXGLOBAL;\r
+                                       ob->yspeed = ob->ymove = 0;\r
+                                       ChangeState(ob, &s_keenholdon);\r
+                               }\r
+                       }\r
+                       else if (c.xaxis == 1)\r
+                       {\r
+                               map = mapsegs[1] + mapbwidthtable[ty]/2 + ob->tileright;\r
+                               if (ob->hitwest)\r
+                                       map++;\r
+                               tile = *map;\r
+                               if (!tinf[EASTWALL + tile] && !tinf[WESTWALL + tile]\r
+                                       && !tinf[NORTHWALL + tile] && !tinf[SOUTHWALL + tile]\r
+                                       && tinf[WESTWALL + map[mapwidth]] && tinf[NORTHWALL + map[mapwidth]]\r
+                                       )\r
+                               {\r
+                                       ob->xdir = 1;\r
+                                       ob->needtoclip = cl_noclip;\r
+                                       ob->x = (ob->x & 0xFF00) + 16*PIXGLOBAL;\r
+                                       ob->y = graby - 4*PIXGLOBAL;\r
+                                       ob->yspeed = ob->ymove = 0;\r
+                                       ChangeState(ob, &s_keenholdon);\r
+                               }\r
+                       }\r
+               }\r
+       }\r
+\r
+       RF_PlaceSprite(&ob->sprite, ob->x, ob->y, ob->shapenum, spritedraw, ob->priority);\r
+}\r
+\r
+#ifdef KEEN5\r
+/*\r
+============================\r
+=\r
+= BreakFuse\r
+=\r
+============================\r
+*/\r
+\r
+void BreakFuse(Uint16 tileX, Uint16 tileY)\r
+{\r
+       Uint16 tiles[] = {1932, 1950};  // should be 'static' for less overhead\r
+\r
+       // The original disassembly had some code here equivalent to this:\r
+       //\r
+       // _AX = tiles[0];\r
+       // _DX = 4;\r
+       //\r
+       // As it turned out, that was just a compiler quirk.\r
+\r
+       SpawnFuseFlash(tileX, tileY);\r
+       if (--gamestate.numfuses == 0)\r
+       {\r
+               SpawnDeadMachine();\r
+       }\r
+       RF_MemToMap(tiles, 1, tileX, tileY, 1, 2);\r
+}\r
+#endif\r
+\r
+/*\r
+============================\r
+=\r
+= KeenPogoReact\r
+=\r
+============================\r
+*/\r
+\r
+void KeenPogoReact(objtype *ob)\r
+{\r
+       if (ob->hiteast && ob->xdir == -1 || ob->hitwest && ob->xdir == 1)\r
+               ob->xspeed = 0;\r
+\r
+       if (ob->hitsouth)\r
+       {\r
+               if (ob->hitsouth == 17) // jumping up through a pole hole\r
+               {\r
+                       ob->y -= 2*PIXGLOBAL;\r
+                       ob->top -= 2*PIXGLOBAL;\r
+                       ob->xspeed = 0;\r
+                       ob->x = CONVERT_TILE_TO_GLOBAL(ob->tilemidx) - 2*PIXGLOBAL;\r
+               }\r
+               else\r
+               {\r
+#ifdef KEEN6\r
+                       if (ob->hitsouth == 33)\r
+                       {\r
+                               FlipBigSwitch(ob, false);\r
+                       }\r
+#endif\r
+                       if (!jumpcheat)\r
+                       {\r
+                               SD_PlaySound(SND_HELMETHIT);\r
+                               if (ob->hitsouth > 1)\r
+                               {\r
+                                       ob->yspeed += 16;\r
+                                       if (ob->yspeed < 0)     // push away from slopes\r
+                                               ob->yspeed = 0;\r
+                               }\r
+                               else\r
+                               {\r
+                                       ob->yspeed = 0;\r
+                               }\r
+                               jumptime = 0;\r
+                       }\r
+               }\r
+       }\r
+\r
+       if (ob->hitnorth)\r
+       {\r
+               ob->ymove = 0;\r
+               if ((ob->hitnorth & ~7) == 8)   // deadly floor!\r
+               {\r
+                       KillKeen();\r
+               }\r
+               else\r
+               {\r
+#if defined KEEN5\r
+                       if (ob->hitnorth == 57)\r
+                       {\r
+                               if (ob->yspeed < 48)\r
+                               {\r
+                                       SD_PlaySound(SND_LANDONFUSE);\r
+                               }\r
+                               else\r
+                               {\r
+                                       BreakFuse(ob->tilemidx, ob->tilebottom);\r
+                                       RF_PlaceSprite(&ob->sprite, ob->x, ob->y, ob->shapenum, spritedraw, ob->priority);\r
+                                       return;\r
+                               }\r
+                       }\r
+#elif defined KEEN6\r
+                       if (ob->hitnorth == 33)\r
+                       {\r
+                               FlipBigSwitch(ob, true);\r
+                       }\r
+                       else if (ob->hitnorth == 41)\r
+                       {\r
+                               ob->xspeed += 8;\r
+                               if (ob->xspeed > 8)\r
+                                       ob->xspeed = 8;\r
+                       }\r
+                       else if (ob->hitnorth == 49)\r
+                       {\r
+                               ob->xspeed -= 8;\r
+                               if (ob->xspeed < -8)\r
+                                       ob->xspeed = -8;\r
+                       }\r
+#endif\r
+                       if (ob->hitnorth != 25 || !jumptime)    // KLUDGE to allow jumping off\r
+                       {\r
+                               ob->yspeed = -48;\r
+                               jumptime = 24;\r
+                               SD_PlaySound(SND_POGOBOUNCE);\r
+                               ChangeState(ob, &s_keenpogo);\r
+                       }\r
+               }\r
+       }\r
+\r
+       RF_PlaceSprite(&ob->sprite, ob->x, ob->y, ob->shapenum, spritedraw, ob->priority);\r
+}\r
+\r
+/*\r
+============================\r
+=\r
+= KeenPoleReact\r
+=\r
+============================\r
+*/\r
+\r
+void KeenPoleReact(objtype *ob)\r
+{\r
+       Uint16 far *map;\r
+       Uint16 ymove;\r
+\r
+       map = mapsegs[1] + mapbwidthtable[ob->tilebottom]/2 + ob->tilemidx;\r
+       if (tinf[NORTHWALL + *map] == 1)\r
+       {\r
+               ymove = (ob->bottom & 0xFF) + 1;\r
+               ob->y -= ymove;\r
+               ob->bottom -= ymove;\r
+               ob->tilebottom--;\r
+               ob->needtoclip = cl_midclip;\r
+               ChangeState(ob, &s_keenlookdown);\r
+       }\r
+\r
+       RF_PlaceSprite(&ob->sprite, ob->x, ob->y, ob->shapenum, spritedraw, ob->priority);\r
+}\r
diff --git a/16/keen456/KEEN4-6/CK_KEEN2.C b/16/keen456/KEEN4-6/CK_KEEN2.C
new file mode 100755 (executable)
index 0000000..e30e5d7
--- /dev/null
@@ -0,0 +1,1606 @@
+/* Reconstructed Commander Keen 4-6 Source Code\r
+ * Copyright (C) 2021 K1n9_Duk3\r
+ *\r
+ * This file is loosely based on:\r
+ * Keen Dreams Source Code\r
+ * Copyright (C) 2014 Javier M. Chavez\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License along\r
+ * with this program; if not, write to the Free Software Foundation, Inc.,\r
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+ */\r
+\r
+/*\r
+CK_KEEN2.C\r
+==========\r
+\r
+Contains the following actor types (in this order):\r
+\r
+- Score Box & Demo sprites\r
+- Keen (world map)\r
+- Flags (world map)\r
+- Neural Stunner Shots\r
+- Gem Door Opener\r
+- Card Door Opener (Keen 5 only)\r
+\r
+*/\r
+\r
+#include "CK_DEF.H"\r
+\r
+Direction opposite[8] = {dir_South, dir_SouthWest, dir_West, dir_NorthWest, dir_North, dir_NorthEast, dir_East, dir_SouthEast};\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                SCORE BOX ROUTINES\r
+\r
+=============================================================================\r
+*/\r
+\r
+statetype s_score         = {  0,   0, think, false, false, 0, 0, 0, NULL, NULL, NULL, NULL};\r
+statetype s_demo          = {DEMOPLAQUESPR, DEMOPLAQUESPR, think, false, false, 0, 0, 0, NULL, NULL, NULL, NULL};\r
+\r
+/*\r
+======================\r
+=\r
+= SpawnScore\r
+=\r
+======================\r
+*/\r
+\r
+void SpawnScore(void)\r
+{\r
+       scoreobj->obclass = inertobj;\r
+       scoreobj->priority = 3;\r
+       scoreobj->active = ac_allways;\r
+       scoreobj->needtoclip = cl_noclip;\r
+       scoreobj->temp2 = -1;\r
+       scoreobj->temp1 = -1;\r
+       scoreobj->temp3 = -1;\r
+       scoreobj->temp4 = -1;\r
+       if (scorescreenkludge)\r
+       {\r
+               scoreobj->state = &sc_deadstate;\r
+       }\r
+       else if (!DemoMode)\r
+       {\r
+               NewState(scoreobj, &s_score);\r
+       }\r
+       else\r
+       {\r
+               NewState(scoreobj, &s_demo);\r
+               CA_MarkGrChunk(DEMOPLAQUESPR);\r
+       }\r
+}\r
+\r
+\r
+// Taken from Keen Dreams: MemDrawChar and ShiftScore\r
+\r
+/*\r
+======================\r
+=\r
+= MemDrawChar\r
+=\r
+======================\r
+*/\r
+\r
+#if GRMODE == EGAGR\r
+\r
+void MemDrawChar(Sint16 char8, Uint8 far *dest, Uint16 width, Uint16 planesize)\r
+{\r
+       Uint16 source = (Uint16)grsegs[STARTTILE8];     // Note: this differs from Keen Dreams source\r
+\r
+asm    mov     si,[char8]\r
+asm    shl     si,1\r
+asm    shl     si,1\r
+asm    shl     si,1\r
+asm    shl     si,1\r
+asm    shl     si,1            // index into char 8 segment\r
+\r
+asm    mov     ds,[WORD PTR source]    // Note: this differs from Keen Dreams source\r
+asm    mov     es,[WORD PTR dest+2]\r
+\r
+asm    mov     cx,4            // draw four planes\r
+asm    mov     bx,[width]\r
+asm    dec     bx\r
+\r
+planeloop:\r
+\r
+asm    mov     di,[WORD PTR dest]\r
+\r
+asm    movsb\r
+asm    add     di,bx\r
+asm    movsb\r
+asm    add     di,bx\r
+asm    movsb\r
+asm    add     di,bx\r
+asm    movsb\r
+asm    add     di,bx\r
+asm    movsb\r
+asm    add     di,bx\r
+asm    movsb\r
+asm    add     di,bx\r
+asm    movsb\r
+asm    add     di,bx\r
+asm    movsb\r
+\r
+asm    mov     ax,[planesize]\r
+asm    add     [WORD PTR dest],ax\r
+\r
+asm    loop    planeloop\r
+\r
+asm    mov     ax,ss\r
+asm    mov     ds,ax\r
+}\r
+\r
+#elif GRMODE == CGAGR\r
+\r
+void MemDrawChar (int char8,byte far *dest,unsigned width,unsigned planesize)\r
+{\r
+asm    mov     si,[char8]\r
+asm    shl     si,1\r
+asm    shl     si,1\r
+asm    shl     si,1\r
+asm    shl     si,1            // index into char 8 segment\r
+\r
+asm    mov     ds,[WORD PTR grsegs+STARTTILE8*2]\r
+asm    mov     es,[WORD PTR dest+2]\r
+\r
+asm    mov     bx,[width]\r
+asm    sub     bx,2\r
+\r
+asm    mov     di,[WORD PTR dest]\r
+\r
+asm    movsw\r
+asm    add     di,bx\r
+asm    movsw\r
+asm    add     di,bx\r
+asm    movsw\r
+asm    add     di,bx\r
+asm    movsw\r
+asm    add     di,bx\r
+asm    movsw\r
+asm    add     di,bx\r
+asm    movsw\r
+asm    add     di,bx\r
+asm    movsw\r
+asm    add     di,bx\r
+asm    movsw\r
+\r
+asm    mov     ax,ss\r
+asm    mov     ds,ax\r
+\r
+       planesize++;            // shut the compiler up\r
+}\r
+#endif\r
+\r
+/*\r
+====================\r
+=\r
+= ShiftScore\r
+=\r
+====================\r
+*/\r
+#if GRMODE == EGAGR\r
+void ShiftScore (void)\r
+{\r
+       spritetabletype far *spr;\r
+       spritetype _seg *dest;\r
+\r
+       spr = &spritetable[SCOREBOXSPR-STARTSPRITES];\r
+       dest = (spritetype _seg *)grsegs[SCOREBOXSPR];\r
+\r
+       CAL_ShiftSprite (FP_SEG(dest),dest->sourceoffset[0],\r
+               dest->sourceoffset[1],spr->width,spr->height,2);\r
+\r
+       CAL_ShiftSprite (FP_SEG(dest),dest->sourceoffset[0],\r
+               dest->sourceoffset[2],spr->width,spr->height,4);\r
+\r
+       CAL_ShiftSprite (FP_SEG(dest),dest->sourceoffset[0],\r
+               dest->sourceoffset[3],spr->width,spr->height,6);\r
+}\r
+#endif\r
+\r
+/*\r
+===============\r
+=\r
+= UpdateScore\r
+=\r
+===============\r
+*/\r
+\r
+void UpdateScore(objtype *ob)\r
+{\r
+       char            str[10],*ch;\r
+       spritetype      _seg    *block;\r
+       Uint8           far *dest;\r
+       Uint16  i, length, width, planesize, number;\r
+       boolean changed;\r
+\r
+       if (scorescreenkludge)\r
+               return;\r
+\r
+       if (DemoMode)\r
+       {\r
+               DrawDemoPlaque(ob);\r
+               return;\r
+       }\r
+\r
+       if (!showscorebox)\r
+               return;\r
+\r
+       changed = false;\r
+\r
+//code below is a combination of ScoreThink and ScoreReact from Keen Dreams with minor changes\r
+\r
+//\r
+// score changed\r
+//\r
+       if ((gamestate.score>>16) != ob->temp1\r
+               || (Uint16)gamestate.score != ob->temp2 )\r
+       {\r
+               block = (spritetype _seg *)grsegs[SCOREBOXSPR];\r
+               width = block->width[0];\r
+               planesize = block->planesize[0];\r
+               dest = (Uint8 far *)grsegs[SCOREBOXSPR]+block->sourceoffset[0]\r
+                       + planesize + width*4;\r
+\r
+               ltoa (gamestate.score,str,10);\r
+\r
+               // erase leading spaces\r
+               length = strlen(str);\r
+               for (i=9;i>length;i--)\r
+                       MemDrawChar (41,dest+=CHARWIDTH,width,planesize);\r
+\r
+               // draw digits\r
+               ch = str;\r
+               while (*ch)\r
+                       MemDrawChar (*ch++ - '0'+42,dest+=CHARWIDTH,width,planesize);\r
+\r
+#if GRMODE == EGAGR\r
+               ShiftScore ();\r
+#endif\r
+               ob->needtoreact = true;\r
+               ob->temp1 = gamestate.score>>16;\r
+               ob->temp2 = gamestate.score;\r
+\r
+               changed = true;\r
+       }\r
+\r
+//\r
+// ammo changed\r
+//\r
+       number = gamestate.ammo;\r
+       if (number != ob->temp3)\r
+       {\r
+               block = (spritetype _seg *)grsegs[SCOREBOXSPR];\r
+               width = block->width[0];\r
+               planesize = block->planesize[0];\r
+               dest = (byte far *)grsegs[SCOREBOXSPR]+block->sourceoffset[0]\r
+                       + planesize + width*20 + 7*CHARWIDTH;\r
+\r
+               if (number > 99)\r
+                       strcpy (str,"99");\r
+               else\r
+                       ltoa (number,str,10);\r
+\r
+               // erase leading spaces\r
+               length = strlen(str);\r
+               for (i=2;i>length;i--)\r
+                       MemDrawChar (41,dest+=CHARWIDTH,width,planesize);\r
+\r
+               // draw digits\r
+               ch = str;\r
+               while (*ch)\r
+                       MemDrawChar (*ch++ - '0'+42,dest+=CHARWIDTH,width,planesize);\r
+\r
+#if GRMODE == EGAGR\r
+               ShiftScore ();\r
+#endif\r
+               ob->needtoreact = true;\r
+               ob->temp3 = number;\r
+\r
+               changed = true;\r
+       }\r
+\r
+//\r
+// lives changed\r
+//\r
+       if (gamestate.lives != ob->temp4)\r
+       {\r
+               block = (spritetype _seg *)grsegs[SCOREBOXSPR];\r
+               width = block->width[0];\r
+               planesize = block->planesize[0];\r
+               dest = (byte far *)grsegs[SCOREBOXSPR]+block->sourceoffset[0]\r
+                       + planesize + width*20 + 2*CHARWIDTH;\r
+\r
+               if (gamestate.lives > 99)\r
+                       strcpy (str,"99");\r
+               else\r
+                       ltoa (gamestate.lives,str,10);\r
+\r
+               // erase leading spaces\r
+               length = strlen(str);\r
+               for (i=2;i>length;i--)\r
+                       MemDrawChar (41,dest+=CHARWIDTH,width,planesize);\r
+\r
+               // draw digits\r
+               ch = str;\r
+               while (*ch)\r
+                       MemDrawChar (*ch++ - '0'+42,dest+=CHARWIDTH,width,planesize);\r
+\r
+#if GRMODE == EGAGR\r
+               ShiftScore ();\r
+#endif\r
+               ob->needtoreact = true;\r
+               ob->temp4 = gamestate.lives;\r
+\r
+               changed = true;\r
+       }\r
+\r
+/*\r
+Note:\r
+-----\r
+\r
+It would be more efficient to use\r
+\r
+       if (changed)\r
+               ShiftScore();\r
+\r
+here instead of the individual ShiftScore() calls above. Because if the player\r
+gains a life by collecting points items, both the score and lives numbers need\r
+to be updated, which means the sprite would be shifted twice. And if the player\r
+fires a shot during the same frame, the ammo number also needs to be updated,\r
+leading to up to three shifts in one frame.\r
+*/\r
+\r
+       if (ob->x != originxglobal || ob->y != originyglobal)\r
+       {\r
+               ob->x = originxglobal;\r
+               ob->y = originyglobal;\r
+               changed = true;\r
+       }\r
+\r
+       if (changed)\r
+#if GRMODE == EGAGR\r
+               RF_PlaceSprite(&ob->sprite, ob->x+4*PIXGLOBAL, ob->y+4*PIXGLOBAL, SCOREBOXSPR, spritedraw, 3);\r
+#elif GRMODE == CGAGR\r
+               RF_PlaceSprite(&ob->sprite, ob->x+8*PIXGLOBAL, ob->y+8*PIXGLOBAL, SCOREBOXSPR, spritedraw, 3);\r
+#endif\r
+}\r
+\r
+/*\r
+===============\r
+=\r
+= DrawDemoPlaque\r
+=\r
+===============\r
+*/\r
+\r
+void DrawDemoPlaque(objtype *ob)\r
+{\r
+       if (ob->x != originxglobal || ob->y != originyglobal)\r
+       {\r
+               ob->x = originxglobal;\r
+               ob->y = originyglobal;\r
+               RF_PlaceSprite(&ob->sprite, ob->x + 160*PIXGLOBAL - 32*PIXGLOBAL, ob->y + 8*PIXGLOBAL, DEMOPLAQUESPR, spritedraw, 3);\r
+       }\r
+}\r
+\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                          MINI KEEN\r
+\r
+player->temp1 = dir\r
+player->temp2 = animation stage\r
+\r
+=============================================================================\r
+*/\r
+\r
+#ifdef KEEN4\r
+statetype s_keenonfoot1 = {WOLRDKEENRIDE1SPR, WOLRDKEENRIDE1SPR, stepthink, false, false, 30, 0, 0, T_FootFly, NULL, R_Draw, &s_keenonfoot2};\r
+statetype s_keenonfoot2 = {WOLRDKEENRIDE2SPR, WOLRDKEENRIDE2SPR, stepthink, false, false, 30, 0, 0, T_FootFly, NULL, R_Draw, &s_keenonfoot1};\r
+statetype s_worldswim = {0, 0, slide, true, false, 6, 16, 16, T_KeenWorldSwim, NULL, R_Draw, &s_worldswim};\r
+#endif\r
+\r
+#ifdef KEEN5\r
+statetype s_worldelevate = {-1, -1, think, true, false, 6, 16, 16, T_Elevate, NULL, R_Draw, NULL};\r
+#endif\r
+\r
+statetype s_worldkeen     = {0, 0, stepthink, false, false, 360, 0, 0, T_KeenWorld, NULL, R_Draw, &s_worldkeenwave1};\r
+\r
+statetype s_worldkeenwave1 = {WORLDKEENWAVE1SPR, WORLDKEENWAVE1SPR, stepthink, false, false, 20, 0, 0, T_KeenWorld, NULL, R_Draw, &s_worldkeenwave2};\r
+statetype s_worldkeenwave2 = {WORLDKEENWAVE2SPR, WORLDKEENWAVE2SPR, stepthink, false, false, 20, 0, 0, T_KeenWorld, NULL, R_Draw, &s_worldkeenwave3};\r
+statetype s_worldkeenwave3 = {WORLDKEENWAVE1SPR, WORLDKEENWAVE1SPR, stepthink, false, false, 20, 0, 0, T_KeenWorld, NULL, R_Draw, &s_worldkeenwave4};\r
+statetype s_worldkeenwave4 = {WORLDKEENWAVE2SPR, WORLDKEENWAVE2SPR, stepthink, false, false, 20, 0, 0, T_KeenWorld, NULL, R_Draw, &s_worldkeenwave5};\r
+statetype s_worldkeenwave5 = {WORLDKEENWAVE1SPR, WORLDKEENWAVE1SPR, stepthink, false, false, 20, 0, 0, T_KeenWorldWalk, NULL, R_Draw, &s_worldkeen};\r
+\r
+statetype s_worldkeenwalk = {0, 0, slide, true, false, 4, 24, 24, T_KeenWorldWalk, NULL, R_Draw, &s_worldkeenwalk};\r
+\r
+Sint16 worldshapes[8] = {WORLDKEENU1SPR-1, WORLDKEENUR1SPR-1, WORLDKEENR1SPR-1, WORLDKEENDR1SPR-1, WORLDKEEND1SPR-1, WORLDKEENDL1SPR-1, WORLDKEENL1SPR-1, WORLDKEENUL1SPR-1};  //-1 to everything because worldanims values are 1-based\r
+Sint16 worldanims[4] = {2, 3, 1, 3};\r
+#ifdef KEEN4\r
+Sint16 swimshapes[8] = {WORLDKEENSWIMU1SPR, WORLDKEENSWIMUR1SPR, WORLDKEENSWIMR1SPR, WORLDKEENSWIMDR1SPR, WORLDKEENSWIMD1SPR, WORLDKEENSWIMDL1SPR, WORLDKEENSWIML1SPR, WORLDKEENSWIMUL1SPR};\r
+#endif\r
+#ifndef KEEN6\r
+Sint16 tiledir[4] = {dir_South, dir_West, dir_North, dir_East};\r
+#endif\r
+\r
+/*\r
+======================\r
+=\r
+= SpawnWorldKeen\r
+=\r
+======================\r
+*/\r
+\r
+void SpawnWorldKeen(Sint16 x, Sint16 y)\r
+{\r
+#ifdef KEEN4\r
+       if (playstate == ex_foot)\r
+       {\r
+               player->needtoclip = cl_noclip;\r
+               player->obclass = keenobj;\r
+               player->x = gamestate.worldx;\r
+               player->y = gamestate.worldy;\r
+               player->active = ac_allways;\r
+               player->priority = 3;\r
+               player->xdir = 0;\r
+               player->ydir = 0;\r
+               if (gamestate.worldx < 20*TILEGLOBAL)\r
+               {\r
+                       player->temp1 = 280;\r
+                       player->xspeed = (30*TILEGLOBAL - player->x)/280 + 1;\r
+                       player->yspeed = (55*TILEGLOBAL - player->y)/280 + 1;\r
+               }\r
+               else\r
+               {\r
+                       player->temp1 = 140;\r
+                       player->xspeed = (Sint16)(16*TILEGLOBAL - player->x)/140 + 1;\r
+                       player->yspeed = (Sint16)(47*TILEGLOBAL - player->y)/140 + 1;\r
+               }\r
+               NewState(player, &s_keenonfoot1);\r
+               return;\r
+       }\r
+#endif\r
+\r
+       player->obclass = keenobj;\r
+       if (gamestate.worldx == 0)\r
+       {\r
+               player->x = CONVERT_TILE_TO_GLOBAL(x);\r
+               player->y = CONVERT_TILE_TO_GLOBAL(y);\r
+       }\r
+       else\r
+       {\r
+               player->x = gamestate.worldx;\r
+               player->y = gamestate.worldy;\r
+       }\r
+       player->active = ac_allways;\r
+       player->priority = 1;\r
+       player->xdir = 0;\r
+       player->ydir = 0;\r
+       player->temp1 = dir_West;\r
+       player->temp2 = 3;\r
+       player->temp3 = 0;\r
+       player->shapenum = WORLDKEENL3SPR;\r
+       NewState(player, &s_worldkeen);\r
+}\r
+\r
+#ifdef KEEN5\r
+/*\r
+======================\r
+=\r
+= SpawnWorldKeenPort\r
+=\r
+======================\r
+*/\r
+\r
+void SpawnWorldKeenPort(Uint16 tileX, Uint16 tileY)\r
+{\r
+       player->obclass = keenobj;\r
+       player->x = CONVERT_TILE_TO_GLOBAL(tileX);\r
+       player->y = CONVERT_TILE_TO_GLOBAL(tileY);\r
+       player->active = ac_allways;\r
+       player->priority = 1;\r
+       player->xdir = 0;\r
+       player->ydir = 0;\r
+       player->temp1 = dir_West;\r
+       player->temp2 = 3;\r
+       player->temp3 = 0;\r
+       player->shapenum = WORLDKEENL3SPR;\r
+       NewState(player, &s_worldkeen);\r
+}\r
+#endif\r
+\r
+\r
+/*\r
+======================\r
+=\r
+= CheckEnterLevel\r
+=\r
+======================\r
+*/\r
+\r
+void CheckEnterLevel(objtype *ob)\r
+{\r
+       Uint16 x, y, info;\r
+\r
+       for (y = ob->tiletop; y <= ob->tilebottom; y++)\r
+       {\r
+               for (x = ob->tileleft; x <= ob->tileright; x++)\r
+               {\r
+                       info = *(mapsegs[2]+mapbwidthtable[y]/2 + x);\r
+                       if (info > 0xC000 && info <= (0xC000 + 18))\r
+                       {\r
+                               gamestate.worldx = ob->x;\r
+                               gamestate.worldy = ob->y;\r
+                               gamestate.mapon = info - 0xC000;\r
+                               playstate = ex_completed;\r
+                               SD_PlaySound(SND_ENTERLEVEL);\r
+                       }\r
+               }\r
+       }\r
+}\r
+\r
+/*\r
+======================\r
+=\r
+= T_KeenWorld\r
+=\r
+======================\r
+*/\r
+\r
+void T_KeenWorld(objtype *ob)\r
+{\r
+       if (c.dir != dir_None)\r
+       {\r
+               ob->state = &s_worldkeenwalk;\r
+               ob->temp2 = 0;\r
+               T_KeenWorldWalk(ob);\r
+       }\r
+       if (jumpbutton || pogobutton || firebutton)\r
+       {\r
+               CheckEnterLevel(ob);\r
+       }\r
+}\r
+\r
+/*\r
+======================\r
+=\r
+= T_KeenWorldWalk\r
+=\r
+======================\r
+*/\r
+\r
+void T_KeenWorldWalk(objtype *ob)\r
+{\r
+       if (ob->temp3)\r
+       {\r
+               ob->temp3 -= 4;\r
+               if (ob->temp3 < 0)\r
+                       ob->temp3 = 0;\r
+       }\r
+       else\r
+       {\r
+               ob->xdir = c.xaxis;\r
+               ob->ydir = c.yaxis;\r
+               if (pogobutton || firebutton || jumpbutton)\r
+               {\r
+                       CheckEnterLevel(ob);\r
+               }\r
+               if (c.dir == dir_None)\r
+               {\r
+                       ob->state = &s_worldkeen;\r
+                       ob->shapenum = worldshapes[ob->temp1] + 3;\r
+                       return;\r
+               }\r
+               ob->temp1 = c.dir;\r
+       }\r
+       if (++ob->temp2 == 4)\r
+               ob->temp2 = 0;\r
+       ob->shapenum = worldshapes[ob->temp1] + worldanims[ob->temp2];\r
+\r
+       if (ob->temp2 == 1)\r
+       {\r
+               SD_PlaySound(SND_WORLDWALK1);\r
+       }\r
+       else if (ob->temp2 == 3)\r
+       {\r
+               SD_PlaySound(SND_WORLDWALK2);\r
+       }\r
+}\r
+\r
+#ifdef KEEN4\r
+/*\r
+======================\r
+=\r
+= T_FootFly\r
+=\r
+======================\r
+*/\r
+\r
+void T_FootFly(objtype *ob)\r
+{\r
+       ob->temp1 = ob->temp1 - tics;\r
+       xtry = ob->xspeed * tics;\r
+       ytry = ob->yspeed * tics;\r
+       if (ob->temp1 <= 0)\r
+       {\r
+               xtry -= ob->xspeed * -ob->temp1;\r
+               ytry -= ob->yspeed * -ob->temp1;\r
+               ob->priority = 1;\r
+               ob->temp1 = dir_West;\r
+               ob->temp2 = 3;\r
+               ob->temp3 = 0;\r
+               player->xdir = 0;\r
+               player->ydir = 0;\r
+               ob->state = &s_worldkeen;\r
+               ob->shapenum = WORLDKEENL3SPR;\r
+               ob->needtoclip = cl_midclip;\r
+       }\r
+}\r
+\r
+/*\r
+======================\r
+=\r
+= T_KeenWorldSwim\r
+=\r
+======================\r
+*/\r
+\r
+void T_KeenWorldSwim(objtype *ob)\r
+{\r
+       if (ob->temp3)\r
+       {\r
+               ob->temp3 -= 6;\r
+               if (ob->temp3 < 0)\r
+                       ob->temp3 = 0;\r
+       }\r
+       else\r
+       {\r
+               ob->xdir = c.xaxis;\r
+               ob->ydir = c.yaxis;\r
+               if (c.xaxis || c.yaxis)\r
+                       ob->temp1 = c.dir;\r
+       }\r
+       ob->shapenum = swimshapes[ob->temp1] + ob->temp2;\r
+       if (++ob->temp2 == 2)\r
+               ob->temp2 = 0;\r
+\r
+       if (ob->temp2 == 0)\r
+       {\r
+               SD_PlaySound(SND_SWIM1);\r
+       }\r
+       else\r
+       {\r
+               SD_PlaySound(SND_SWIM2);\r
+       }\r
+}\r
+\r
+#else  // NOT Keen 4 (i.e. Keen 5 & 6):\r
+\r
+/*\r
+======================\r
+=\r
+= Teleport\r
+=\r
+======================\r
+*/\r
+\r
+void Teleport(Uint16 tileX, Uint16 tileY)\r
+{\r
+       Uint16 tile, globalx, globaly, duration, move;\r
+       objtype *o;\r
+       objtype *ob = player;\r
+\r
+       //\r
+       // enter the teleporter\r
+       //\r
+       SD_PlaySound(SND_TELEPORT);\r
+       globalx = CONVERT_TILE_TO_GLOBAL(tileX);\r
+       globaly = CONVERT_TILE_TO_GLOBAL(tileY);\r
+\r
+#ifdef KEEN6Ev15\r
+       // We need to make the compiler "forget" that duration starts at 0\r
+       // to make sure the while-loop check is performed when entering the\r
+       // loop. Can't change compiler settings since we do need that loop\r
+       // optimization for the for-loop at the end of this routine.\r
+       if (true)\r
+               duration = 0;\r
+#else\r
+       duration = 0;\r
+#endif\r
+\r
+       while (duration < 130)\r
+       {\r
+               RF_Refresh();\r
+               move = tics*2;\r
+               duration += tics;\r
+\r
+               if (ob->x == globalx && ob->y == globaly)\r
+                       break;\r
+\r
+               if (ob->y < globaly)\r
+               {\r
+                       ob->y += move;\r
+                       if (ob->y > globaly)\r
+                               ob->y = globaly;\r
+               }\r
+               else if (ob->y > globaly)\r
+               {\r
+                       ob->y -= move;\r
+                       if (ob->y < globaly)\r
+                               ob->y = globaly;\r
+               }\r
+\r
+               if (ob->x < globalx)\r
+               {\r
+                       ob->x += move;\r
+                       if (ob->x > globalx)\r
+                               ob->x = globalx;\r
+               }\r
+               else if (ob->x > globalx)\r
+               {\r
+                       ob->x -= move;\r
+                       if (ob->x < globalx)\r
+                               ob->x = globalx;\r
+               }\r
+\r
+               ob->shapenum = ((TimeCount >> 3) % 3) + WORLDKEENU1SPR;\r
+               RF_PlaceSprite(&ob->sprite, ob->x, ob->y, ob->shapenum, spritedraw, ob->priority);\r
+\r
+               tile = ((TimeCount >> 2) & TELEPORERTILEMASK) + TELEPORTERTILE1;\r
+               RF_MemToMap(&tile, 1, tileX, tileY, 1, 1);\r
+       }\r
+\r
+       tile = TELEPORTERTILE2;\r
+       RF_MemToMap(&tile, 1, tileX, tileY, 1, 1);\r
+\r
+       //\r
+       // teleport to new location\r
+       //\r
+       tile = *(mapsegs[2]+mapbwidthtable[tileY]/2 + tileX);\r
+       tileX = tile >> 8;\r
+       tileY = tile & 0x7F;    // BUG? y coordinate is limited to 1..127\r
+       ob->x = CONVERT_TILE_TO_GLOBAL(tileX);\r
+       ob->y = CONVERT_TILE_TO_GLOBAL(tileY);\r
+       ob->xdir = 0;\r
+       ob->ydir = 1;\r
+       ob->temp1 = dir_South;\r
+       NewState(ob, ob->state);\r
+       CenterActor(ob);\r
+\r
+       //\r
+       // draw flags/signs for new location\r
+       //\r
+       for (o=player->next; o; o=o->next)\r
+       {\r
+               if (!o->active && o->obclass == flagobj\r
+                       && o->tileright >= originxtile-1 && o->tileleft <= originxtilemax+1\r
+                       && o->tiletop <= originytilemax+1 && o->tilebottom >= originytile-1)\r
+               {\r
+                       o->needtoreact = true;\r
+                       o->active = ac_yes;\r
+                       RF_PlaceSprite(&o->sprite, o->x, o->y, o->shapenum, spritedraw, o->priority);\r
+               }\r
+       }\r
+       UpdateScore(scoreobj);\r
+       RF_Refresh();\r
+       RF_Refresh();\r
+\r
+       //\r
+       // leave teleporter\r
+       //\r
+       SD_PlaySound(SND_TELEPORT);\r
+\r
+       for (duration = 0; duration < 90; )\r
+       {\r
+               RF_Refresh();\r
+               duration += tics;\r
+               ob->y += tics*2 + tics;\r
+\r
+               ob->shapenum = ((TimeCount >> 3) % 3) + WORLDKEEND1SPR;\r
+               RF_PlaceSprite(&ob->sprite, ob->x, ob->y, ob->shapenum, spritedraw, ob->priority);\r
+\r
+               tile = ((TimeCount >> 2) & TELEPORERTILEMASK) + TELEPORTERTILE3;\r
+               RF_MemToMap(&tile, 1, tileX, tileY, 1, 1);\r
+       }\r
+\r
+       tile = TELEPORTERTILE4;\r
+       RF_MemToMap(&tile, 1, tileX, tileY, 1, 1);\r
+       xtry = ytry = 0;\r
+       ClipToWalls(ob);\r
+}\r
+\r
+#ifdef KEEN5\r
+\r
+/*\r
+======================\r
+=\r
+= T_Elevate\r
+=\r
+======================\r
+*/\r
+\r
+void T_Elevate(objtype *ob)\r
+{\r
+       Sint16 i, x, y, tx, ty;\r
+       Uint16 tiles[2][2];\r
+\r
+       ytry = ob->ydir * 64 * tics;\r
+       if (ob->x != ob->temp2)\r
+       {\r
+               xtry = ob->xdir * 12 * tics;\r
+               if ( (ob->xdir == 1 && ob->x + xtry > ob->temp2)\r
+                       || (ob->xdir == -1 && ob->x + xtry < ob->temp2) )\r
+               {\r
+                       xtry = ob->temp2 - ob->x;\r
+               }\r
+       }\r
+\r
+       //\r
+       // Keen has no sprite in this state, so we need to update the hitbox manually\r
+       // to avoid issues (the screen scrolling routines use left/right/top/bottom)\r
+       //\r
+       ob->left = ob->x + xtry;\r
+       ob->right = ob->left + (TILEGLOBAL-1);\r
+       ob->top = ob->y + ytry;\r
+       ob->bottom = ob->top + (TILEGLOBAL-1);\r
+\r
+       if (ob->ydir == 1)\r
+       {\r
+               if (ob->y + ytry < ob->temp1)\r
+                       return;\r
+       }\r
+       else\r
+       {\r
+               if (ob->y + ytry > ob->temp1)\r
+                       return;\r
+       }\r
+\r
+       //\r
+       // the invisible Keen has arrived at its destination\r
+       //\r
+       ytry = 0;\r
+       xtry = 0;\r
+       ob->x = ob->temp2;\r
+       ob->y = ob->temp1;\r
+       ob->priority = 1;\r
+       ob->temp1 = 4;\r
+       ob->temp2 = 3;\r
+       ob->temp3 = 0;\r
+       player->xdir = 0;\r
+       player->ydir = 0;\r
+       ob->state = &s_worldkeen;\r
+       ob->shapenum = WORLDKEEND3SPR;\r
+       ob->needtoclip = cl_midclip;\r
+       tx = CONVERT_GLOBAL_TO_TILE(ob->x);\r
+       ty = CONVERT_GLOBAL_TO_TILE(ob->y);\r
+       WorldScrollScreen(ob);\r
+       UpdateScore(scoreobj);\r
+       RF_Refresh();\r
+       RF_Refresh();\r
+\r
+       ob->y -= TILEGLOBAL;\r
+       RF_PlaceSprite(&ob->sprite, ob->x, ob->y, ob->shapenum, spritedraw, ob->priority);\r
+\r
+       //\r
+       // open the elevator door\r
+       //\r
+       SD_PlaySound(SND_ELEVATORDOOR);\r
+       for (i=0; i<=5; i++)\r
+       {\r
+               for (y=0; y<2; y++)\r
+               {\r
+                       for (x=0; x<2; x++)\r
+                       {\r
+                               tiles[y][x] = *(mapsegs[1]+mapbwidthtable[y]/2 + i*2 + x);\r
+                       }\r
+               }\r
+               RF_MemToMap(&tiles[0][0], 1, tx, ty-2, 2, 2);\r
+               RF_Refresh();\r
+               VW_WaitVBL(8);\r
+       }\r
+\r
+       //\r
+       // make Keen walk out of the elevator\r
+       //\r
+       for (y=0; y<32; y++)\r
+       {\r
+               ob->y += 8;     // move half a pixel every frame for 32 frames -> move down 16 pixels total\r
+               ob->shapenum = (y / 4) % 3 + WORLDKEEND1SPR;\r
+               RF_PlaceSprite(&ob->sprite, ob->x, ob->y, ob->shapenum, spritedraw, ob->priority);\r
+               RF_Refresh();\r
+       }\r
+       ob->needtoclip = cl_midclip;    // redundant, but doesn't do any harm\r
+}\r
+\r
+/*\r
+======================\r
+=\r
+= Elevator\r
+=\r
+======================\r
+*/\r
+\r
+void Elevator(Uint16 tileX, Uint16 tileY, Sint16 dir)\r
+{\r
+       Uint16 info, globalx, globaly, duration, move;\r
+       Sint16 x, y, i;\r
+       Uint16 tiles[2][2];\r
+       objtype *ob = player;\r
+\r
+       globalx = CONVERT_TILE_TO_GLOBAL(tileX);\r
+       globaly = CONVERT_TILE_TO_GLOBAL(tileY);\r
+\r
+       //\r
+       // make Keen walk into the elevator\r
+       //\r
+       for (duration = 0; duration < 130; )\r
+       {\r
+               CalcBounds(ob);\r
+               WorldScrollScreen(ob);\r
+               UpdateScore(scoreobj);\r
+               RF_Refresh();\r
+\r
+               move = tics * 2;\r
+               duration += tics;\r
+\r
+               if (ob->x == globalx && ob->y == globaly)\r
+                       break;\r
+\r
+               if (ob->y < globaly)\r
+               {\r
+                       ob->y += move;\r
+                       if (ob->y > globaly)\r
+                               ob->y = globaly;\r
+               }\r
+               else if (ob->y > globaly)\r
+               {\r
+                       ob->y -= move;\r
+                       if (ob->y < globaly)\r
+                               ob->y = globaly;\r
+               }\r
+\r
+               if (ob->x < globalx)\r
+               {\r
+                       ob->x += move;\r
+                       if (ob->x > globalx)\r
+                               ob->x = globalx;\r
+               }\r
+               else if (ob->x > globalx)\r
+               {\r
+                       ob->x -= move;\r
+                       if (ob->x < globalx)\r
+                               ob->x = globalx;\r
+               }\r
+\r
+               ob->shapenum = ((duration / 8) % 3) + WORLDKEENU1SPR;\r
+               RF_PlaceSprite(&ob->sprite, ob->x, ob->y, ob->shapenum, spritedraw, ob->priority);\r
+       }\r
+\r
+       //\r
+       // close the elevator door\r
+       //\r
+       SD_PlaySound(SND_ELEVATORDOOR);\r
+       for (i=5; i >= 0; i--)\r
+       {\r
+               for (y=0; y<2; y++)\r
+               {\r
+                       for (x=0; x<2; x++)\r
+                       {\r
+                               tiles[y][x] = *(mapsegs[1]+mapbwidthtable[y]/2 + i*2 + x);\r
+                       }\r
+               }\r
+               RF_MemToMap(&tiles[0][0], 1, tileX+dir, tileY-1, 2, 2);\r
+               RF_Refresh();\r
+               VW_WaitVBL(8);\r
+       }\r
+\r
+       //\r
+       // make Keen invisible (and not clipping) and send him to the destination\r
+       //\r
+       RF_RemoveSprite(&ob->sprite);\r
+       info = *(mapsegs[2] + mapbwidthtable[tileY]/2 + tileX);\r
+       ob->temp2 = CONVERT_TILE_TO_GLOBAL(info >> 8);\r
+       ob->temp1 = CONVERT_TILE_TO_GLOBAL((info & 0x7F) + 1);  // BUG? y coordinate is limited to 1..127\r
+       if (ob->temp1 < ob->y)\r
+       {\r
+               ob->ydir = -1;\r
+       }\r
+       else\r
+       {\r
+               ob->ydir = 1;\r
+       }\r
+       if (ob->temp2 < ob->x)\r
+       {\r
+               ob->xdir = -1;\r
+       }\r
+       else\r
+       {\r
+               ob->xdir = 1;\r
+       }\r
+       ob->needtoclip = cl_noclip;\r
+       ob->state = &s_worldelevate;\r
+}\r
+\r
+#endif //ifdef KEEN5\r
+\r
+#endif //ifdef KEEN4 ... else ...\r
+\r
+/*\r
+======================\r
+=\r
+= CheckWorldInTiles\r
+=\r
+======================\r
+*/\r
+\r
+void CheckWorldInTiles(objtype *ob)\r
+{\r
+       Uint16 tx, ty, intile;\r
+\r
+       if (ob->temp3)\r
+               return;\r
+\r
+       tx = ob->tilemidx;\r
+       ty = CONVERT_GLOBAL_TO_TILE(ob->top + (ob->bottom-ob->top)/2);\r
+       intile = tinf[INTILE + *(mapsegs[1]+mapbwidthtable[ty]/2+tx)];\r
+#if defined KEEN4\r
+       if (intile == INTILE_SHORESOUTH || intile == INTILE_SHORENORTH\r
+               || intile == INTILE_SHOREEAST || intile == INTILE_SHOREWEST)\r
+       {\r
+               if (!gamestate.wetsuit)\r
+               {\r
+                       SD_PlaySound(SND_NOWAY);\r
+                       CantSwim();\r
+                       RF_ForceRefresh();\r
+                       xtry = -ob->xmove;\r
+                       ytry = -ob->ymove;\r
+                       ob->xdir = ob->ydir = 0;\r
+                       ClipToWalls(ob);\r
+               }\r
+               else\r
+               {\r
+                       ob->temp1 = tiledir[intile-INTILE_SHORESOUTH];\r
+                       if (ob->state == &s_worldswim)\r
+                       {\r
+                               ob->temp1 = opposite[ob->temp1];\r
+                       }\r
+                       switch (ob->temp1)\r
+                       {\r
+                       case dir_North:\r
+                               ob->xdir = 0;\r
+                               ob->ydir = -1;\r
+                               break;\r
+                       case dir_East:\r
+                               ob->xdir = 1;\r
+                               ob->ydir = 0;\r
+                               break;\r
+                       case dir_South:\r
+                               ob->xdir = 0;\r
+                               ob->ydir = 1;\r
+                               break;\r
+                       case dir_West:\r
+                               ob->xdir = -1;\r
+                               ob->ydir = 0;\r
+                               break;\r
+                       }\r
+                       ob->temp2 = 0;\r
+                       ob->temp3 = 18;\r
+                       if (ob->state == &s_worldswim)\r
+                       {\r
+                               ChangeState(ob, &s_worldkeenwalk);\r
+                       }\r
+                       else\r
+                       {\r
+                               ChangeState(ob, &s_worldswim);\r
+                       }\r
+               }\r
+       }\r
+#elif defined KEEN5\r
+       switch (intile)\r
+       {\r
+       case INTILE_TELEPORT:\r
+               Teleport(tx, ty);\r
+               break;\r
+       case INTILE_ELEVATORLEFT:\r
+               Elevator(tx, ty, 0);\r
+               break;\r
+       case INTILE_ELEVATORRIGHT:\r
+               Elevator(tx, ty, -1);\r
+               break;\r
+       }\r
+#elif defined KEEN6\r
+       switch (intile)\r
+       {\r
+       case INTILE_TELEPORT:\r
+               Teleport(tx, ty);\r
+               break;\r
+       }\r
+#endif\r
+}\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                          FLAGS\r
+\r
+temp1 = x destination for the thrown flag\r
+temp2 = y destination for the thrown flag\r
+temp3 = amount of time passed since flag was thrown (in tics)\r
+\r
+=============================================================================\r
+*/\r
+\r
+statetype s_flagwave1     = {FLAGFLAP1SPR, FLAGFLAP1SPR, step, false, false, 10, 0, 0, NULL, NULL, R_Draw, &s_flagwave2};\r
+statetype s_flagwave2     = {FLAGFLAP2SPR, FLAGFLAP2SPR, step, false, false, 10, 0, 0, NULL, NULL, R_Draw, &s_flagwave3};\r
+statetype s_flagwave3     = {FLAGFLAP3SPR, FLAGFLAP3SPR, step, false, false, 10, 0, 0, NULL, NULL, R_Draw, &s_flagwave4};\r
+statetype s_flagwave4     = {FLAGFLAP4SPR, FLAGFLAP4SPR, step, false, false, 10, 0, 0, NULL, NULL, R_Draw, &s_flagwave1};\r
+\r
+#ifndef KEEN5\r
+statetype s_throwflag0    = {FLAGFLIP1SPR, FLAGFLIP1SPR, think, false, false, 6, 0, 0, TossThink, NULL, R_Draw, &s_throwflag1};\r
+statetype s_throwflag1    = {FLAGFLIP1SPR, FLAGFLIP1SPR, stepthink, false, false, 12, 0, 0, PathThink, NULL, R_Draw, &s_throwflag2};\r
+statetype s_throwflag2    = {FLAGFLIP2SPR, FLAGFLIP2SPR, stepthink, false, false, 12, 0, 0, PathThink, NULL, R_Draw, &s_throwflag3};\r
+statetype s_throwflag3    = {FLAGFLIP3SPR, FLAGFLIP3SPR, stepthink, false, false, 12, 0, 0, PathThink, NULL, R_Draw, &s_throwflag4};\r
+statetype s_throwflag4    = {FLAGFLIP4SPR, FLAGFLIP4SPR, stepthink, false, false, 12, 0, 0, PathThink, NULL, R_Draw, &s_throwflag5};\r
+statetype s_throwflag5    = {FLAGFALL1SPR, FLAGFALL1SPR, stepthink, false, false, 12, 0, 0, PathThink, NULL, R_Draw, &s_throwflag6};\r
+statetype s_throwflag6    = {FLAGFALL1SPR, FLAGFALL1SPR, stepthink, true, false, 1, 0, 0, FlagAlign, NULL, R_Draw, &s_flagwave1};\r
+#endif\r
+\r
+Sint16 flagx, flagy;\r
+Point flagpath[30];\r
+\r
+/*\r
+======================\r
+=\r
+= SpawnFlag\r
+=\r
+======================\r
+*/\r
+\r
+void SpawnFlag(Sint16 x, Sint16 y)\r
+{\r
+       GetNewObj(false);\r
+       new->needtoclip = cl_noclip;\r
+       new->priority = 3;\r
+       new->obclass = flagobj;\r
+       new->active = ac_yes;\r
+#if defined KEEN4\r
+       new->x = CONVERT_TILE_TO_GLOBAL(x) + 6*PIXGLOBAL;\r
+       new->y = CONVERT_TILE_TO_GLOBAL(y) + -30*PIXGLOBAL;\r
+#elif defined KEEN5\r
+       new->x = CONVERT_TILE_TO_GLOBAL(x) + -5*PIXGLOBAL;\r
+       new->y = CONVERT_TILE_TO_GLOBAL(y) + -30*PIXGLOBAL;\r
+#elif defined KEEN6\r
+       new->x = CONVERT_TILE_TO_GLOBAL(x) + 6*PIXGLOBAL;               // useless!\r
+       new->y = CONVERT_TILE_TO_GLOBAL(y) + -30*PIXGLOBAL;     // useless!\r
+#if GRMODE == CGAGR\r
+       new->x = CONVERT_TILE_TO_GLOBAL(x) + 10*PIXGLOBAL;\r
+#else\r
+       new->x = CONVERT_TILE_TO_GLOBAL(x) + 14*PIXGLOBAL;\r
+#endif\r
+       new->y = CONVERT_TILE_TO_GLOBAL(y) + -26*PIXGLOBAL;\r
+       {\r
+               Uint16 tile = *(mapsegs[1]+mapbwidthtable[y]/2 + x) + 1;\r
+               RF_MemToMap(&tile, 1, x, y, 1, 1);\r
+       }\r
+#endif\r
+       new->ticcount = US_RndT() / 16;\r
+       NewState(new, &s_flagwave1);\r
+}\r
+\r
+#ifndef KEEN5\r
+/*\r
+======================\r
+=\r
+= SpawnThrowFlag\r
+=\r
+======================\r
+*/\r
+\r
+void SpawnThrowFlag(Sint16 x, Sint16 y)\r
+{\r
+       Sint16 i;\r
+       Sint32 xdist, ydist;\r
+\r
+       GetNewObj(false);\r
+       new->needtoclip = cl_noclip;\r
+       new->priority = 3;\r
+       new->obclass = flagobj;\r
+       new->active = ac_allways;\r
+       new->x = gamestate.worldx - 16*PIXGLOBAL;\r
+       new->y = gamestate.worldy - 16*PIXGLOBAL;\r
+#if defined KEEN4\r
+       new->temp1 = CONVERT_TILE_TO_GLOBAL(x) + 6*PIXGLOBAL;\r
+       new->temp2 = CONVERT_TILE_TO_GLOBAL(y) + -38*PIXGLOBAL;\r
+#elif defined KEEN6\r
+       flagx = x;\r
+       flagy = y;\r
+#if GRMODE == CGAGR\r
+       new->temp1 = CONVERT_TILE_TO_GLOBAL(x) + 10*PIXGLOBAL;\r
+#else\r
+       new->temp1 = CONVERT_TILE_TO_GLOBAL(x) + 14*PIXGLOBAL;\r
+#endif\r
+       new->temp2 = CONVERT_TILE_TO_GLOBAL(y) + -34*PIXGLOBAL;\r
+#endif\r
+       xdist = (Sint32)new->temp1 - (Sint32)new->x;\r
+       ydist = (Sint32)new->temp2 - (Sint32)new->y;\r
+       for (i = 0; i < 30; i++)\r
+       {\r
+               flagpath[i].x = new->x + (xdist * min(i, 24))/24;\r
+               flagpath[i].y = new->y + (ydist * i)/30;\r
+               if (i < 10)\r
+               {\r
+                       flagpath[i].y -= i*3*PIXGLOBAL;\r
+               }\r
+               else if (i < 15)\r
+               {\r
+                       flagpath[i].y -= i*PIXGLOBAL + 20*PIXGLOBAL;\r
+               }\r
+               else if (i < 20)\r
+               {\r
+                       flagpath[i].y -= (20-i)*PIXGLOBAL + 30*PIXGLOBAL;\r
+               }\r
+               else\r
+               {\r
+                       flagpath[i].y -= (29-i)*3*PIXGLOBAL;\r
+               }\r
+       }\r
+       NewState(new, &s_throwflag0);\r
+}\r
+\r
+/*\r
+======================\r
+=\r
+= TossThink\r
+=\r
+======================\r
+*/\r
+\r
+void TossThink(objtype *ob)\r
+{\r
+       if (screenfaded)\r
+               return;\r
+\r
+       SD_StopSound();\r
+       SD_PlaySound(SND_FLAGSPIN);\r
+       ob->state = ob->state->nextstate;\r
+}\r
+\r
+/*\r
+======================\r
+=\r
+= PathThink\r
+=\r
+======================\r
+*/\r
+\r
+void PathThink(objtype *ob)\r
+{\r
+       ob->temp3 = ob->temp3 + tics;\r
+       if (ob->temp3 > 58)\r
+               ob->temp3 = 58;\r
+\r
+       ob->x = flagpath[ob->temp3/2].x;\r
+       ob->y = flagpath[ob->temp3/2].y;\r
+       ob->needtoreact = true;\r
+       if (ob->temp1 == 0)\r
+       {\r
+               SD_PlaySound(SND_FLAGSPIN);\r
+       }\r
+}\r
+\r
+/*\r
+======================\r
+=\r
+= FlagAlign\r
+=\r
+======================\r
+*/\r
+\r
+void FlagAlign(objtype *ob)\r
+{\r
+       ob->x = ob->temp1;\r
+       ob->y = ob->temp2 + 8*PIXGLOBAL;\r
+       SD_PlaySound(SND_FLAGLAND);\r
+#ifdef KEEN6\r
+       {\r
+               Uint16 tile = *(mapsegs[1]+mapbwidthtable[flagy]/2 + flagx) + 1;\r
+               RF_MemToMap(&tile, 1, flagx, flagy, 1, 1);\r
+       }\r
+#endif\r
+}\r
+#endif\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                NEURAL STUNNER\r
+\r
+=============================================================================\r
+*/\r
+statetype s_stunray1 = {STUN1SPR, STUN1SPR, slide, false, false, 6, 64, 64, T_Shot, NULL, R_Shot, &s_stunray2};\r
+statetype s_stunray2 = {STUN2SPR, STUN2SPR, slide, false, false, 6, 64, 64, T_Shot, NULL, R_Shot, &s_stunray3};\r
+statetype s_stunray3 = {STUN3SPR, STUN3SPR, slide, false, false, 6, 64, 64, T_Shot, NULL, R_Shot, &s_stunray4};\r
+statetype s_stunray4 = {STUN4SPR, STUN4SPR, slide, false, false, 6, 64, 64, T_Shot, NULL, R_Shot, &s_stunray1};\r
+\r
+statetype s_stunhit  = {STUNHIT1SPR, STUNHIT1SPR, step, false, false, 12, 0, 0, NULL, NULL, R_Draw, &s_stunhit2};\r
+statetype s_stunhit2 = {STUNHIT2SPR, STUNHIT2SPR, step, false, false, 12, 0, 0, NULL, NULL, R_Draw, NULL};\r
+\r
+/*\r
+======================\r
+=\r
+= SpawnShot\r
+=\r
+======================\r
+*/\r
+\r
+void SpawnShot(Uint16 x, Uint16 y, Direction dir)\r
+{\r
+       if (!gamestate.ammo)\r
+       {\r
+               SD_PlaySound(SND_USESWITCH);\r
+               return;\r
+       }\r
+\r
+       gamestate.ammo--;\r
+       GetNewObj(true);\r
+       new->x = x;\r
+       new->y = y;\r
+       new->priority = 0;\r
+       new->obclass = stunshotobj;\r
+       new->active = ac_allways;\r
+       SD_PlaySound(SND_KEENFIRE);\r
+       switch (dir)\r
+       {\r
+       case dir_North:\r
+               new->xdir = 0;\r
+               new->ydir = -1;\r
+               break;\r
+       case dir_East:\r
+               new->xdir = 1;\r
+               new->ydir = 0;\r
+               break;\r
+       case dir_South:\r
+               new->xdir = 0;\r
+               new->ydir = 1;\r
+               break;\r
+       case dir_West:\r
+               new->xdir = -1;\r
+               new->ydir = 0;\r
+               break;\r
+       default:\r
+               Quit("SpawnShot: Bad dir!");\r
+               break;\r
+       }\r
+       NewState(new, &s_stunray1);\r
+\r
+#ifdef KEEN6\r
+       {\r
+               objtype *ob;\r
+\r
+               for (ob=player->next; ob; ob=ob->next)\r
+               {\r
+                       if (ob->active\r
+                               && new->right > ob->left && new->left < ob->right\r
+                               && new->top < ob->bottom && new->bottom > ob->top\r
+                               && ob->state->contact)\r
+                       {\r
+                               ob->state->contact(ob, new);\r
+                               return;\r
+                       }\r
+               }\r
+       }\r
+#endif\r
+}\r
+\r
+/*\r
+======================\r
+=\r
+= ExplodeShot\r
+=\r
+======================\r
+*/\r
+\r
+void ExplodeShot(objtype *ob)\r
+{\r
+       ob->obclass = inertobj;\r
+       ChangeState(ob, &s_stunhit);\r
+       SD_PlaySound(SND_SHOTEXPLODE);\r
+}\r
+\r
+/*\r
+======================\r
+=\r
+= T_Shot\r
+=\r
+======================\r
+*/\r
+\r
+void T_Shot(objtype *ob)\r
+{\r
+       objtype *ob2;\r
+\r
+       if (ob->tileright >= originxtile && ob->tilebottom >= originytile\r
+               && ob->tileleft <= originxtilemax && ob->tiletop <= originytilemax)\r
+       {\r
+               //object is visible, so do nothing\r
+               return;\r
+       }\r
+\r
+       if (ob->tileright+10 < originxtile\r
+               || ob->tileleft-10 > originxtilemax\r
+               || ob->tilebottom+6 < originytile\r
+               || ob->tiletop-6 > originytilemax)\r
+       {\r
+               //shot is off-screen by more than half a screen, so remove it\r
+               RemoveObj(ob);\r
+               return;\r
+       }\r
+\r
+       //check for collisions with INACTIVE objects\r
+       for (ob2 = player->next; ob2; ob2 = ob2->next)\r
+       {\r
+               if (!ob2->active && ob->right > ob2->left && ob->left < ob2->right\r
+                       && ob->top < ob2->bottom && ob->bottom > ob2->top)\r
+               {\r
+                       if (ob2->state->contact)\r
+                       {\r
+                               ob2->state->contact(ob2, ob);\r
+                               ob2->needtoreact = true;\r
+                               ob2->active = ac_yes;\r
+                       }\r
+\r
+                       if (ob->obclass == nothing)     //BUG: obclass is 'inertobj' for the exploded shot\r
+                               break;\r
+               }\r
+       }\r
+}\r
+\r
+/*\r
+======================\r
+=\r
+= R_Shot\r
+=\r
+======================\r
+*/\r
+\r
+void R_Shot(objtype *ob)\r
+{\r
+       Uint16 tile;\r
+\r
+       if (ob->hitnorth == 1 && ob->tileleft != ob->tileright)\r
+       {\r
+               tile = *(mapsegs[1]+mapbwidthtable[ob->tiletop-1]/2+ob->tileright);\r
+               if (tinf[NORTHWALL+tile] == 17)\r
+               {\r
+                       ob->hitnorth = 17;\r
+                       ob->x += 0x100 - (ob->x & 0xFF);\r
+               }\r
+       }\r
+       else if (ob->hitnorth == 17 && ob->tileleft != ob->tileright)\r
+       {\r
+               ob->x &= 0xFF00;\r
+       }\r
+       if (ob->hitsouth == 1 && ob->tileleft != ob->tileright)\r
+       {\r
+               tile = *(mapsegs[1]+mapbwidthtable[ob->tilebottom+1]/2+ob->tileright);\r
+               if (tinf[SOUTHWALL+tile] == 17)\r
+               {\r
+                       ob->hitsouth = 17;\r
+                       ob->x += 0x100 - (ob->x & 0xFF);\r
+               }\r
+       }\r
+       else if (ob->hitsouth == 17 && ob->tileleft != ob->tileright)\r
+       {\r
+               ob->x &= 0xFF00;\r
+       }\r
+       if (ob->hitnorth == 17 || ob->hitsouth == 17)\r
+       {\r
+               ytry = ob->state->ymove * tics * ob->ydir;\r
+               ob->y += ytry;\r
+               ob->top += ytry;\r
+               ob->bottom += ytry;\r
+               ob->tiletop = CONVERT_GLOBAL_TO_TILE(ob->top);\r
+               ob->tilebottom = CONVERT_GLOBAL_TO_TILE(ob->bottom);\r
+       }\r
+       else if (ob->hitnorth || ob->hitsouth || ob->hiteast || ob->hitwest)\r
+       {\r
+               ExplodeShot(ob);\r
+       }\r
+       RF_PlaceSprite(&ob->sprite, ob->x, ob->y, ob->shapenum, spritedraw, ob->priority);\r
+}\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                DOOR\r
+\r
+temp1 = height of the door's main section (identical tiles!), in tiles\r
+        DoorOpen changes two more tiles at the bottom end of the door\r
+                 (total door height in tiles is temp1 + 2)\r
+\r
+=============================================================================\r
+*/\r
+\r
+statetype s_door1    = {0, 0, step, false, false, 10, 0, 0, DoorOpen, NULL, NULL, &s_door2};\r
+statetype s_door2    = {0, 0, step, false, false, 10, 0, 0, DoorOpen, NULL, NULL, &s_door3};\r
+statetype s_door3    = {0, 0, step, false, false, 10, 0, 0, DoorOpen, NULL, NULL, NULL};\r
+\r
+/*\r
+======================\r
+=\r
+= DoorOpen\r
+=\r
+======================\r
+*/\r
+\r
+void DoorOpen(objtype *ob)\r
+{\r
+       Sint16 i;\r
+       Uint16 far *map;\r
+       Uint16 tiles[50];\r
+\r
+       map = mapsegs[1] + mapbwidthtable[ob->y]/2 + ob->x;\r
+       for (i=0; i < ob->temp1+2; i++, map+=mapwidth)\r
+       {\r
+               tiles[i] = *map + 1;\r
+       }\r
+       RF_MemToMap(tiles, 1, ob->x, ob->y, 1, ob->temp1+2);\r
+}\r
+\r
+#ifdef KEEN5\r
+/*\r
+=============================================================================\r
+\r
+                                                CARD DOOR\r
+\r
+temp1 = frame counter\r
+\r
+=============================================================================\r
+*/\r
+statetype s_carddoor    = {0, 0, step, false, false, 15, 0, 0, CardDoorOpen, NULL, NULL, &s_carddoor};\r
+\r
+/*\r
+======================\r
+=\r
+= CardDoorOpen\r
+=\r
+======================\r
+*/\r
+\r
+void CardDoorOpen(objtype *ob)\r
+{\r
+       Sint16 x, y;\r
+       Uint16 far *map;\r
+       Uint16 tiles[16], *tileptr;\r
+\r
+       tileptr = tiles;\r
+       map = mapsegs[1] + mapbwidthtable[ob->y]/2 + ob->x;\r
+       for (y=0; y<4; y++, map+=mapwidth)\r
+       {\r
+               for (x=0; x<4; x++)\r
+               {\r
+                       *tileptr++ = map[x]-4;\r
+               }\r
+       }\r
+       RF_MemToMap(tiles, 1, ob->x, ob->y, 4, 4);\r
+\r
+       if (++ob->temp1 == 3)\r
+               ob->state = NULL;\r
+}\r
+\r
+#endif
\ No newline at end of file
diff --git a/16/keen456/KEEN4-6/CK_MAIN.C b/16/keen456/KEEN4-6/CK_MAIN.C
new file mode 100755 (executable)
index 0000000..b8e7153
--- /dev/null
@@ -0,0 +1,531 @@
+/* Reconstructed Commander Keen 4-6 Source Code\r
+ * Copyright (C) 2021 K1n9_Duk3\r
+ *\r
+ * This file is loosely based on:\r
+ * Keen Dreams Source Code\r
+ * Copyright (C) 2014 Javier M. Chavez\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License along\r
+ * with this program; if not, write to the Free Software Foundation, Inc.,\r
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+ */\r
+\r
+// CK_MAIN.C\r
+/*\r
+=============================================================================\r
+\r
+                                                       COMMANDER KEEN\r
+\r
+                                       An Id Software production\r
+\r
+=============================================================================\r
+*/\r
+\r
+#include "CK_DEF.H"\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                GLOBAL VARIABLES\r
+\r
+=============================================================================\r
+*/\r
+\r
+boolean tedlevel;\r
+Uint16 tedlevelnum;\r
+\r
+char str[80], str2[20];\r
+boolean storedemo, jerk;\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                LOCAL VARIABLES\r
+\r
+=============================================================================\r
+*/\r
+\r
+//===========================================================================\r
+\r
+/*\r
+=====================\r
+=\r
+= SizeText\r
+=\r
+= Calculates width and height of a string that contains line breaks\r
+= (Note: only the height is ever used, width is NOT calculated correctly)\r
+=\r
+=====================\r
+*/\r
+\r
+void SizeText(char *text, Uint16 *width, Uint16 *height)\r
+{\r
+       register char *ptr;\r
+       char c;\r
+       Uint16 w, h;\r
+       char strbuf[80];\r
+\r
+       *width = *height = 0;\r
+       ptr = &strbuf[0];\r
+       while ((c=*(text++)) != '\0')\r
+       {\r
+               *(ptr++) = c;\r
+               if (c == '\n' || !*text)\r
+               {\r
+                       USL_MeasureString(strbuf, &w, &h);      // BUG: strbuf may not have a terminating '\0' at the end!\r
+                       *height += h;\r
+                       if (*width < w)\r
+                       {\r
+                               *width = w;\r
+                       }\r
+                       ptr = &strbuf[0];\r
+               }\r
+       }\r
+}\r
+\r
+//===========================================================================\r
+\r
+/*\r
+==========================\r
+=\r
+= ShutdownId\r
+=\r
+= Shuts down all ID_?? managers\r
+=\r
+==========================\r
+*/\r
+\r
+void ShutdownId(void)\r
+{\r
+       US_Shutdown();\r
+       SD_Shutdown();\r
+       IN_Shutdown();\r
+       RF_Shutdown();\r
+       VW_Shutdown();\r
+       CA_Shutdown();\r
+       MM_Shutdown();\r
+}\r
+\r
+\r
+//===========================================================================\r
+\r
+/*\r
+==========================\r
+=\r
+= InitGame\r
+=\r
+= Load a few things right away\r
+=\r
+==========================\r
+*/\r
+\r
+void InitGame(void)\r
+{\r
+       static char *ParmStrings[] = {"JERK", ""};\r
+       void MML_UseSpace (Uint16 segstart, Uint16 seglength);\r
+\r
+       Uint16 segstart,seglength;\r
+       Sint16 i;\r
+\r
+       // Note: The value of the jerk variable is replaced with the value\r
+       // read from the config file during US_Startup, which means the\r
+       // JERK parameter has absolutely no effect if a valid config file\r
+       // exists. The parameter check should be moved to some place after\r
+       // US_Startup to make it work reliably.\r
+       \r
+       for (i=1; i < _argc; i++)\r
+       {\r
+               if (US_CheckParm(_argv[i], ParmStrings) == 0)\r
+               {\r
+                       jerk = true;\r
+               }\r
+       }\r
+\r
+       US_TextScreen();\r
+\r
+       MM_Startup();\r
+       VW_Startup();\r
+       RF_Startup();\r
+       IN_Startup();\r
+       SD_Startup();\r
+       US_Startup();\r
+\r
+       US_UpdateTextScreen();\r
+\r
+       CA_Startup();\r
+       US_Setup();\r
+\r
+       US_SetLoadSaveHooks(&LoadTheGame, &SaveTheGame, &ResetGame);\r
+       drawcachebox = DialogDraw;\r
+       updatecachebox = DialogUpdate;\r
+       finishcachebox = DialogFinish;\r
+\r
+//\r
+// load in and lock down some basic chunks\r
+//\r
+\r
+       CA_ClearMarks();\r
+\r
+       CA_MarkGrChunk(STARTFONT);\r
+       CA_MarkGrChunk(STARTTILE8);\r
+       CA_MarkGrChunk(STARTTILE8M);\r
+#if GRMODE == EGAGR\r
+       CA_MarkGrChunk(CORDPICM);\r
+       CA_MarkGrChunk(METALPOLEPICM);\r
+#endif\r
+\r
+       CA_CacheMarks(NULL);\r
+\r
+       MM_SetLock(&grsegs[STARTFONT], true);\r
+       MM_SetLock(&grsegs[STARTTILE8], true);\r
+       MM_SetLock(&grsegs[STARTTILE8M], true);\r
+#if GRMODE == EGAGR\r
+       MM_SetLock(&grsegs[CORDPICM], true);\r
+       MM_SetLock(&grsegs[METALPOLEPICM], true);\r
+#endif\r
+\r
+       fontcolor = WHITE;\r
+\r
+       US_FinishTextScreen();\r
+\r
+//\r
+// reclaim the memory from the linked in text screen\r
+//\r
+       segstart = FP_SEG(&introscn);\r
+       seglength = 4000/16;\r
+       if (FP_OFF(&introscn))\r
+       {\r
+               segstart++;\r
+               seglength--;\r
+       }\r
+       MML_UseSpace (segstart,seglength);\r
+\r
+       VW_SetScreenMode(GRMODE);\r
+#if GRMODE == CGAGR\r
+       VW_ColorBorder(BROWN);\r
+#else\r
+       VW_ColorBorder(CYAN);\r
+#endif\r
+       VW_ClearVideo(BLACK);\r
+}\r
+\r
+//===========================================================================\r
+\r
+/*\r
+==========================\r
+=\r
+= Quit\r
+=\r
+==========================\r
+*/\r
+\r
+void Quit(char *error)\r
+{\r
+       Uint16 finscreen;\r
+\r
+       if (!error)\r
+       {\r
+               CA_SetAllPurge();\r
+               CA_CacheGrChunk(ORDERSCREEN);\r
+               finscreen = (Uint16)grsegs[ORDERSCREEN];\r
+       }\r
+\r
+       // BUG: VW_ClearVideo may brick the system if screenseg is 0\r
+       // (i.e. VW_SetScreenMode has not been executed) - this may\r
+       // happen if the code runs into an error during InitGame\r
+       // (EMS/XMS errors, files not found etc.)\r
+       VW_ClearVideo(BLACK);\r
+       VW_SetLineWidth(40);\r
+\r
+       ShutdownId();\r
+       if (error && *error)\r
+       {\r
+               puts(error);\r
+               if (tedlevel)\r
+               {\r
+                       getch();\r
+                       execlp("TED5.EXE", "TED5.EXE", "/LAUNCH", NULL);\r
+               }\r
+               else if (US_ParmPresent("windows"))\r
+               {\r
+                       bioskey(0);\r
+               }\r
+               exit(1);\r
+       }\r
+\r
+       if (!NoWait)\r
+       {\r
+               movedata(finscreen, 7, 0xB800, 0, 4000);\r
+               gotoxy(1, 24);\r
+               if (US_ParmPresent("windows"))\r
+               {\r
+                       bioskey(0);\r
+               }\r
+       }\r
+\r
+       exit(0);\r
+}\r
+\r
+//===========================================================================\r
+\r
+/*\r
+==================\r
+=\r
+= TEDDeath\r
+=\r
+==================\r
+*/\r
+\r
+void TEDDeath(void)\r
+{\r
+       ShutdownId();\r
+       execlp("TED5.EXE", "TED5.EXE", "/LAUNCH", NULL);\r
+       // BUG: should call exit(1); here in case starting TED5 fails\r
+}\r
+\r
+//===========================================================================\r
+\r
+/*\r
+==================\r
+=\r
+= CheckMemory\r
+=\r
+==================\r
+*/\r
+\r
+void CheckMemory(void)\r
+{\r
+       Uint16 finscreen;\r
+\r
+       if (mminfo.nearheap+mminfo.farheap+mminfo.EMSmem+mminfo.XMSmem >= MINMEMORY)\r
+               return;\r
+\r
+       CA_CacheGrChunk (OUTOFMEM);\r
+       finscreen = (Uint16)grsegs[OUTOFMEM];\r
+       ShutdownId();\r
+       movedata (finscreen,7,0xb800,0,4000);\r
+       gotoxy (1,24);\r
+       exit(1);\r
+}\r
+\r
+//===========================================================================\r
+\r
+/*\r
+=====================\r
+=\r
+= DemoLoop\r
+=\r
+=====================\r
+*/\r
+\r
+void DemoLoop(void)\r
+{\r
+       static char *ParmStrings[] = {"easy", "normal", "hard", ""};\r
+\r
+       register Sint16 i, state;\r
+       Sint16 level;\r
+\r
+//\r
+// check for launch from ted\r
+//\r
+       if (tedlevel)\r
+       {\r
+               NewGame();\r
+               CA_LoadAllSounds();\r
+               gamestate.mapon = tedlevelnum;\r
+               restartgame = gd_Normal;\r
+               for (i = 1;i < _argc;i++)\r
+               {\r
+                       if ( (level = US_CheckParm(_argv[i],ParmStrings)) == -1)\r
+                               continue;\r
+\r
+                       restartgame = level+gd_Easy;\r
+                       break;\r
+               }\r
+               GameLoop();\r
+               TEDDeath();\r
+       }\r
+\r
+//\r
+// demo loop\r
+//\r
+       state = 0;\r
+       playstate = ex_stillplaying;\r
+       while (1)\r
+       {\r
+               switch (state++)\r
+               {\r
+               case 0:\r
+#if GRMODE == CGAGR\r
+                       ShowTitle();\r
+#else\r
+                       if (nopan)\r
+                       {\r
+                               ShowTitle();\r
+                       }\r
+                       else\r
+                       {\r
+                               Terminator();\r
+                       }\r
+#endif\r
+                       break;\r
+\r
+               case 1:\r
+                       RunDemo(0);\r
+                       break;\r
+\r
+               case 2:\r
+#if GRMODE == CGAGR\r
+                       ShowCredits();\r
+#else\r
+                       StarWars();\r
+#endif\r
+                       break;\r
+\r
+               case 3:\r
+                       RunDemo(1);\r
+                       break;\r
+\r
+               case 4:\r
+                       ShowHighScores();\r
+                       break;\r
+\r
+               case 5:\r
+                       RunDemo(2);\r
+                       break;\r
+\r
+               case 6:\r
+                       state = 0;\r
+                       RunDemo(3);\r
+                       break;\r
+               }\r
+\r
+               while (playstate == ex_resetgame || playstate == ex_loadedgame)\r
+               {\r
+                       GameLoop();\r
+                       ShowHighScores();\r
+                       if (playstate == ex_resetgame || playstate == ex_loadedgame)\r
+                       {\r
+                               continue;       // don't show title screen, go directly to GameLoop();\r
+                       }\r
+                       ShowTitle();\r
+                       ///////////////\r
+                       // this is completely useless:\r
+                       if (playstate == ex_resetgame || playstate == ex_loadedgame)\r
+                       {\r
+                               continue;\r
+                       }\r
+                       ///////////////\r
+               }\r
+       }\r
+}\r
+\r
+//===========================================================================\r
+\r
+#if (GRMODE == EGAGR) && !(defined KEEN6)\r
+/*\r
+=====================\r
+=\r
+= CheckCutFile\r
+=\r
+=====================\r
+*/\r
+\r
+#define FILE_GR1 GREXT"1."EXTENSION\r
+#define FILE_GR2 GREXT"2."EXTENSION\r
+#define FILE_GRAPH GREXT"GRAPH."EXTENSION\r
+\r
+static void CheckCutFile(void)\r
+{\r
+       register Sint16 ohandle, ihandle;\r
+       Sint16 handle;\r
+       Sint32 size;\r
+       void far *buffer;\r
+\r
+       if ( (handle = open(FILE_GRAPH, O_BINARY|O_RDONLY)) != -1)\r
+       {\r
+               close(handle);\r
+               return;\r
+       }\r
+       puts("Combining "FILE_GR1" and "FILE_GR2" into "FILE_GRAPH"...");\r
+       if (rename(FILE_GR1, FILE_GRAPH) == -1)\r
+       {\r
+               puts("Can't rename "FILE_GR1"!");\r
+               exit(1);\r
+       }\r
+       if ( (ohandle = open(FILE_GRAPH, O_BINARY|O_APPEND|O_WRONLY)) == -1)\r
+       {\r
+               puts("Can't open "FILE_GRAPH"!");\r
+               exit(1);\r
+       }\r
+       lseek(ohandle, 0, SEEK_END);\r
+       if ( (ihandle = open(FILE_GR2, O_BINARY|O_RDONLY)) == -1)\r
+       {\r
+               puts("Can't find "FILE_GR2"!");\r
+               exit(1);\r
+       }\r
+       size = filelength(ihandle);\r
+       buffer = farmalloc(32000);\r
+       while (size)\r
+       {\r
+               if (size > 32000)\r
+               {\r
+                       CA_FarRead(ihandle, buffer, 32000);\r
+                       CA_FarWrite(ohandle, buffer, 32000);\r
+                       size -= 32000;\r
+               }\r
+               else\r
+               {\r
+                       CA_FarRead(ihandle, buffer, size);\r
+                       CA_FarWrite(ohandle, buffer, size);\r
+                       size = 0;\r
+               }\r
+       }\r
+       farfree(buffer);\r
+       close(ohandle);\r
+       close(ihandle);\r
+       unlink(FILE_GR2);\r
+}\r
+#endif\r
+\r
+//===========================================================================\r
+\r
+\r
+/*\r
+==========================\r
+=\r
+= main\r
+=\r
+==========================\r
+*/\r
+\r
+void main(void)\r
+{\r
+#if (GRMODE == EGAGR) && !(defined KEEN6)\r
+       CheckCutFile();\r
+#endif\r
+\r
+       if (US_ParmPresent("DEMO"))\r
+               storedemo = true;\r
+\r
+       if (US_ParmPresent("JOYPAD"))\r
+               joypad = true;  // Note: the joypad variable is never used\r
+       \r
+       InitGame();\r
+       CheckMemory();\r
+       if (NoWait || tedlevel)\r
+               debugok = true;\r
+\r
+       DemoLoop();\r
+       Quit("Demo loop exited???");\r
+}
\ No newline at end of file
diff --git a/16/keen456/KEEN4-6/CK_PLAY.C b/16/keen456/KEEN4-6/CK_PLAY.C
new file mode 100755 (executable)
index 0000000..0e08140
--- /dev/null
@@ -0,0 +1,2422 @@
+/* Reconstructed Commander Keen 4-6 Source Code\r
+ * Copyright (C) 2021 K1n9_Duk3\r
+ *\r
+ * This file is loosely based on:\r
+ * Keen Dreams Source Code\r
+ * Copyright (C) 2014 Javier M. Chavez\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License along\r
+ * with this program; if not, write to the Free Software Foundation, Inc.,\r
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+ */\r
+\r
+#include "CK_DEF.H"\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                GLOBAL VARIABLES\r
+\r
+=============================================================================\r
+*/\r
+\r
+ScanCode firescan = sc_Space;\r
+\r
+boolean singlestep, jumpcheat, godmode, keenkilled;\r
+\r
+exittype playstate;\r
+gametype gamestate;\r
+\r
+objtype *new, *check, *player, *scoreobj;\r
+\r
+Uint16 originxtilemax;\r
+Uint16 originytilemax;\r
+\r
+ControlInfo c;\r
+boolean button2, button3;      // never used\r
+\r
+objtype dummyobj;\r
+\r
+Sint16 invincible;\r
+\r
+boolean oldshooting, showscorebox, joypad;\r
+\r
+Sint16 groundslam;\r
+\r
+boolean debugok;\r
+boolean jumpbutton, jumpheld, pogobutton, pogoheld, firebutton, fireheld, upheld;\r
+\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                LOCAL VARIABLES\r
+\r
+=============================================================================\r
+*/\r
+\r
+objtype *obj;\r
+\r
+Uint16 centerlevel;\r
+\r
+Uint16 objectcount;\r
+objtype objarray[MAXACTORS];\r
+objtype *lastobj;\r
+objtype *objfreelist;\r
+\r
+Sint16 inactivateleft;\r
+Sint16 inactivateright;\r
+Sint16 inactivatetop;\r
+Sint16 inactivatebottom;\r
+\r
+#ifdef KEEN6Ev15\r
+Uint16 __dummy__;      // never used, but must be present to recreate the original EXE\r
+#endif\r
+\r
+Uint16 extravbls;\r
+\r
+Uint16 windowofs;\r
+Sint16 vislines;\r
+boolean scrollup;\r
+\r
+Sint16 oldfirecount;\r
+\r
+//===========================================================================\r
+\r
+/*\r
+==================\r
+=\r
+= CountObjects\r
+=\r
+==================\r
+*/\r
+\r
+void CountObjects(void)\r
+{\r
+       Uint16 activeobjects, inactiveobjects;\r
+       objtype *ob;\r
+\r
+       activeobjects = inactiveobjects = 0;\r
+       for (ob=player; ob; ob=ob->next)\r
+       {\r
+               if (ob->active)\r
+               {\r
+                       activeobjects++;\r
+               }\r
+               else\r
+               {\r
+                       inactiveobjects++;\r
+               }\r
+       }\r
+       VW_FixRefreshBuffer();\r
+       US_CenterWindow(18, 4);\r
+       PrintY += 7;\r
+       US_Print("Active Objects :");\r
+       US_PrintUnsigned(activeobjects);\r
+       US_Print("\nInactive Objects:");\r
+       US_PrintUnsigned(inactiveobjects);\r
+       VW_UpdateScreen();\r
+       IN_Ack();\r
+}\r
+\r
+/*\r
+==================\r
+=\r
+= DebugMemory\r
+=\r
+==================\r
+*/\r
+\r
+void DebugMemory(void)\r
+{\r
+       VW_FixRefreshBuffer();\r
+       US_CenterWindow(16, 7);\r
+       US_CPrint("Memory Usage");\r
+       US_CPrint("------------");\r
+       US_Print("Total     :");\r
+       US_PrintUnsigned((mminfo.mainmem+mminfo.EMSmem+mminfo.XMSmem)/1024);\r
+       US_Print("k\nFree      :");\r
+       US_PrintUnsigned(MM_UnusedMemory()/1024);\r
+       US_Print("k\nWith purge:");\r
+       US_PrintUnsigned(MM_TotalFree()/1024);\r
+       US_Print("k\n");\r
+       VW_UpdateScreen();\r
+       IN_Ack();\r
+#if GRMODE != CGAGR\r
+       MM_ShowMemory();\r
+#endif\r
+}\r
+\r
+/*\r
+===================\r
+=\r
+= TestSprites\r
+=\r
+===================\r
+*/\r
+\r
+void TestSprites(void)\r
+{\r
+       Uint16 infox, infoy;\r
+       Sint16 chunk, oldchunk;\r
+       Sint16 shift;\r
+       Uint16 infobottom, drawx;\r
+       spritetabletype far *info;\r
+       Uint8 _seg *block;\r
+       Uint16 size;\r
+       Uint16 scan;\r
+       Uint32 totalsize;\r
+\r
+       VW_FixRefreshBuffer();\r
+       US_CenterWindow(30, 17);\r
+       totalsize = 0;\r
+       US_CPrint("Sprite Test");\r
+       US_CPrint("-----------");\r
+       infoy = PrintY;\r
+       infox = (PrintX + 56) & ~7;\r
+       drawx = infox + 40;\r
+       US_Print("Chunk:\nWidth:\nHeight:\nOrgx:\nOrgy:\nXl:\nYl:\nXh:\nYh:\nShifts:\nMem:\n");\r
+       infobottom = PrintY;\r
+       chunk = STARTSPRITES;\r
+       shift = 0;\r
+       while (1)\r
+       {\r
+               if (chunk >= STARTSPRITES+NUMSPRITES)\r
+               {\r
+                       chunk = STARTSPRITES+NUMSPRITES-1;\r
+               }\r
+               else if (chunk < STARTSPRITES)\r
+               {\r
+                       chunk = STARTSPRITES;\r
+               }\r
+               info = &spritetable[chunk-STARTSPRITES];\r
+               block = grsegs[chunk];\r
+               VWB_Bar(infox, infoy, 40, infobottom-infoy, WHITE);\r
+               PrintX = infox;\r
+               PrintY = infoy;\r
+               US_PrintUnsigned(chunk);\r
+               US_Print("\n");\r
+               PrintX = infox;\r
+               US_PrintUnsigned(info->width);\r
+               US_Print("\n");\r
+               PrintX = infox;\r
+               US_PrintUnsigned(info->height);\r
+               US_Print("\n");\r
+               PrintX = infox;\r
+               US_PrintSigned(info->orgx);\r
+               US_Print("\n");\r
+               PrintX = infox;\r
+               US_PrintSigned(info->orgy);\r
+               US_Print("\n");\r
+               PrintX = infox;\r
+               US_PrintSigned(info->xl);\r
+               US_Print("\n");\r
+               PrintX = infox;\r
+               US_PrintSigned(info->yl);\r
+               US_Print("\n");\r
+               PrintX = infox;\r
+               US_PrintSigned(info->xh);\r
+               US_Print("\n");\r
+               PrintX = infox;\r
+               US_PrintSigned(info->yh);\r
+               US_Print("\n");\r
+               PrintX = infox;\r
+               US_PrintSigned(info->shifts);\r
+               US_Print("\n");\r
+               PrintX = infox;\r
+               if (!block)\r
+               {\r
+                       US_Print("-----");\r
+               }\r
+               else\r
+               {\r
+                       size = ((spritetype far *)block)->sourceoffset[3] + ((spritetype far *)block)->planesize[3]*5;\r
+                       size = (size + 15) & ~15;       //round up to multiples of 16\r
+                       totalsize += size;      //useless: the value stored in 'totalsize' is never used\r
+                       US_PrintUnsigned(size);\r
+                       US_Print("=");\r
+               }\r
+               oldchunk = chunk;\r
+               do\r
+               {\r
+                       VWB_Bar(drawx, infoy, 110, infobottom-infoy, WHITE);\r
+                       if (block)\r
+                       {\r
+                               PrintX = drawx;\r
+                               PrintY = infoy;\r
+                               US_Print("Shift:");\r
+                               US_PrintUnsigned(shift);\r
+                               US_Print("\n");\r
+                               VWB_DrawSprite(drawx + 2*shift + 16, PrintY, chunk);\r
+                       }\r
+                       VW_UpdateScreen();\r
+                       scan = IN_WaitForKey();\r
+                       switch (scan)\r
+                       {\r
+                       case sc_UpArrow:\r
+                               chunk++;\r
+                               break;\r
+                       case sc_DownArrow:\r
+                               chunk--;\r
+                               break;\r
+                       case sc_PgUp:\r
+                               chunk += 10;\r
+                               if (chunk >= STARTSPRITES+NUMSPRITES)\r
+                               {\r
+                                       chunk = STARTSPRITES+NUMSPRITES-1;\r
+                               }\r
+                               break;\r
+                       case sc_PgDn:\r
+                               chunk -= 10;\r
+                               if (chunk < STARTSPRITES)\r
+                               {\r
+                                       chunk = STARTSPRITES;\r
+                               }\r
+                               break;\r
+                       case sc_LeftArrow:\r
+                               if (--shift == -1)\r
+                               {\r
+                                       shift = 3;\r
+                               }\r
+                               break;\r
+                       case sc_RightArrow:\r
+                               if (++shift == 4)\r
+                               {\r
+                                       shift = 0;\r
+                               }\r
+                               break;\r
+                       case sc_Escape:\r
+                               return;\r
+                       }\r
+\r
+               } while (chunk == oldchunk);\r
+\r
+       }\r
+}\r
+\r
+/*\r
+===================\r
+=\r
+= PicturePause\r
+=\r
+===================\r
+*/\r
+\r
+void PicturePause(void)\r
+{\r
+       Uint16 source;\r
+       Sint16 y;\r
+\r
+//\r
+// wait for a key press, abort if it's not Enter\r
+//\r
+       IN_ClearKeysDown();\r
+       while (!LastScan);\r
+       if (LastScan != sc_Enter)\r
+       {\r
+               IN_ClearKeysDown();\r
+               return;\r
+       }\r
+\r
+       SD_PlaySound(SND_JUMP);\r
+       SD_WaitSoundDone();\r
+\r
+//\r
+// rearrange onscreen image into base EGA layout, so that it\r
+// can be grabbed correctly by an external screenshot tool\r
+//\r
+       source = displayofs + panadjust;\r
+\r
+       VW_ColorBorder(15);     // white (can't use WHITE as parameter, since that's defined as 3 for CGA and this must use 15)\r
+       VW_SetLineWidth(40);\r
+       VW_SetScreen(0, 0);\r
+\r
+       if (source < 0x10000l-200*64)\r
+       {\r
+       //\r
+       // copy top line first\r
+       //\r
+               for (y=0; y<200; y++)\r
+               {\r
+                       VW_ScreenToScreen(source+y*64, y*40, 40, 1);\r
+               }\r
+       }\r
+       else\r
+       {\r
+       //\r
+       // copy bottom line first\r
+       //\r
+               for (y=199; y>=0; y--)\r
+               {\r
+                       VW_ScreenToScreen(source+y*64, y*40, 40, 1);\r
+               }\r
+       }\r
+\r
+//\r
+// shut down input manager so that screenshot tool can see input again\r
+//\r
+       IN_Shutdown();\r
+\r
+       SD_PlaySound(SND_EXTRAKEEN);\r
+       SD_WaitSoundDone();\r
+\r
+//\r
+// shut down the remaining ID managers, except VW (stay in graphics mode!)\r
+//\r
+       US_Shutdown();\r
+       SD_Shutdown();\r
+       IN_Shutdown();\r
+       RF_Shutdown();\r
+       CA_Shutdown();\r
+       MM_Shutdown();\r
+\r
+//\r
+// wait until user hits Escape\r
+//\r
+       while (((bioskey(0) >> 8) & 0xFF) != sc_Escape);\r
+\r
+//\r
+// back to text mode and exit to DOS\r
+//\r
+       VW_Shutdown();\r
+       exit(0);\r
+}\r
+\r
+/*\r
+===================\r
+=\r
+= MaskOnTile\r
+=\r
+===================\r
+*/\r
+\r
+void MaskOnTile(Uint16 dest, Uint16 source)\r
+{\r
+       Sint16 i;\r
+       Uint16 _seg *sourceseg;\r
+       Uint16 _seg *destseg;\r
+       Uint16 sourceval, maskindex, sourcemask;\r
+\r
+       sourceseg = (grsegs+STARTTILE16M)[source];\r
+       destseg = (grsegs+STARTTILE16M)[dest];\r
+       for (i=0; i<64; i++)\r
+       {\r
+               maskindex = i & 15;\r
+#ifdef KEEN6Ev15\r
+               sourceval = sourceseg[16+i];\r
+#else\r
+               sourceval = (sourceseg+16)[i];\r
+#endif\r
+               sourcemask = sourceseg[maskindex];\r
+               destseg[maskindex] &= sourcemask;\r
+               destseg[16+i] &= sourcemask;\r
+               destseg[16+i] |= sourceval;\r
+       }\r
+}\r
+\r
+/*\r
+===================\r
+=\r
+= WallDebug\r
+=\r
+===================\r
+*/\r
+\r
+void WallDebug(void)\r
+{\r
+       Sint16 i, val;\r
+\r
+       VW_FixRefreshBuffer();\r
+       US_CenterWindow(24, 3);\r
+       US_PrintCentered("WORKING");\r
+       VW_UpdateScreen();\r
+       for (i=STARTTILE16M+108; i<STARTTILE16M+124; i++)\r
+       {\r
+               CA_CacheGrChunk(i);\r
+       }\r
+       for (i=0; i<NUMTILE16M; i++)\r
+       {\r
+               if (!grsegs[STARTTILE16M+i])\r
+               {\r
+                       continue;\r
+               }\r
+               val = tinf[i+NORTHWALL] & 7;\r
+               if (val)\r
+               {\r
+                       MaskOnTile(i, val+107);\r
+               }\r
+               val = tinf[i+SOUTHWALL] & 7;\r
+               if (val)\r
+               {\r
+                       MaskOnTile(i, val+115);\r
+               }\r
+               val = tinf[i+EASTWALL] & 7;\r
+               if (val > 1)\r
+               {\r
+                       strcpy(str, "WallDebug: East wall other than 1:");\r
+                       itoa(i, str2, 10);\r
+                       strcat(str, str2);\r
+                       Quit(str);\r
+               }\r
+               if (val)\r
+               {\r
+                       MaskOnTile(i, val+114); //Note: val is always 1 here, so you could use 115 as 2nd arg\r
+               }\r
+               val = tinf[i+WESTWALL] & 7;\r
+               if (val > 1)\r
+               {\r
+                       strcpy(str, "WallDebug: West wall other than 1:");\r
+                       itoa(i, str2, 10);\r
+                       strcat(str, str2);\r
+                       Quit(str);\r
+               }\r
+               if (val)\r
+               {\r
+                       MaskOnTile(i, val+122); //Note: val is always 1 here, so you could use 123 as 2nd arg\r
+               }\r
+       }\r
+}\r
+\r
+\r
+//===========================================================================\r
+\r
+/*\r
+================\r
+=\r
+= DebugKeys\r
+=\r
+================\r
+*/\r
+\r
+boolean DebugKeys(void)\r
+{\r
+       Sint16 level, i, esc;\r
+\r
+       if (Keyboard[sc_B] && ingame)           // B = border color\r
+       {\r
+               VW_FixRefreshBuffer();\r
+               US_CenterWindow(24, 3);\r
+               PrintY += 6;\r
+               US_Print(" Border color (0-15):");\r
+               VW_UpdateScreen();\r
+               esc = !US_LineInput(px, py, str, NULL, true, 2, 0);\r
+               if (!esc)\r
+               {\r
+                       level = atoi(str);\r
+                       if (level >= 0 && level <= 15)\r
+                       {\r
+                               VW_ColorBorder(level);\r
+                       }\r
+               }\r
+               return true;\r
+       }\r
+\r
+       if (Keyboard[sc_C] && ingame)           // C = count objects\r
+       {\r
+               CountObjects();\r
+               return true;\r
+       }\r
+\r
+       if (Keyboard[sc_D] && ingame)           // D = start / end demo record\r
+       {\r
+               if (DemoMode == demo_Off)\r
+               {\r
+                       StartDemoRecord();\r
+               }\r
+               else if (DemoMode == demo_Record)\r
+               {\r
+                       EndDemoRecord();\r
+                       playstate = ex_completed;\r
+               }\r
+               return true;\r
+       }\r
+\r
+       if (Keyboard[sc_E] && ingame)           // E = quit level\r
+       {\r
+               if (tedlevel)\r
+               {\r
+                       TEDDeath();\r
+               }\r
+               playstate = ex_completed;\r
+               //BUG? there is no return in this branch (should return false)\r
+       }\r
+\r
+       if (Keyboard[sc_G] && ingame)           // G = god mode\r
+       {\r
+               VW_FixRefreshBuffer();\r
+               US_CenterWindow(12, 2);\r
+               if (godmode)\r
+               {\r
+                       US_PrintCentered("God mode OFF");\r
+               }\r
+               else\r
+               {\r
+                       US_PrintCentered("God mode ON");\r
+               }\r
+               VW_UpdateScreen();\r
+               IN_Ack();\r
+               godmode ^= true;\r
+               return true;\r
+       }\r
+       else if (Keyboard[sc_I])                        // I = item cheat\r
+       {\r
+               VW_FixRefreshBuffer();\r
+               US_CenterWindow(12, 3);\r
+               US_PrintCentered("Free items!");\r
+               for (i=0; i<4; i++)\r
+               {\r
+                       gamestate.keys[i] = 99;\r
+               }\r
+               gamestate.ammo = 99;\r
+#if defined KEEN4\r
+               gamestate.wetsuit = true;\r
+#elif defined KEEN5\r
+               gamestate.keycard = true;\r
+#elif defined KEEN6\r
+               gamestate.passcardstate=gamestate.hookstate=gamestate.sandwichstate = 1;\r
+#endif\r
+               VW_UpdateScreen();\r
+               IN_Ack();\r
+               GivePoints(3000);\r
+               return true;\r
+       }\r
+       else if (Keyboard[sc_J])                        // J = jump cheat\r
+       {\r
+               jumpcheat ^= true;\r
+               VW_FixRefreshBuffer();\r
+               US_CenterWindow(18, 3);\r
+               if (jumpcheat)\r
+               {\r
+                       US_PrintCentered("Jump cheat ON");\r
+               }\r
+               else\r
+               {\r
+                       US_PrintCentered("Jump cheat OFF");\r
+               }\r
+               VW_UpdateScreen();\r
+               IN_Ack();\r
+               return true;\r
+       }\r
+       else if (Keyboard[sc_M])                        // M = memory info\r
+       {\r
+               DebugMemory();\r
+               return true;\r
+       }\r
+       else if (Keyboard[sc_N])                        // N = no clip\r
+       {\r
+               VW_FixRefreshBuffer();\r
+               US_CenterWindow(18, 3);\r
+               if (player->needtoclip)\r
+               {\r
+                       US_PrintCentered("No clipping ON");\r
+                       player->needtoclip = cl_noclip;\r
+               }\r
+               else\r
+               {\r
+                       US_PrintCentered("No clipping OFF");\r
+                       player->needtoclip = cl_midclip;\r
+               }\r
+               VW_UpdateScreen();\r
+               IN_Ack();\r
+               return true;\r
+       }\r
+       else if (Keyboard[sc_P])                        // P = pause with no screen disruptioon\r
+       {\r
+               IN_ClearKeysDown();\r
+               PicturePause();\r
+               return true;\r
+       }\r
+       else if (Keyboard[sc_S] && ingame)      // S = slow motion\r
+       {\r
+               singlestep ^= true;\r
+               VW_FixRefreshBuffer();\r
+               US_CenterWindow(18, 3);\r
+               if (singlestep)\r
+               {\r
+                       US_PrintCentered("Slow motion ON");\r
+               }\r
+               else\r
+               {\r
+                       US_PrintCentered("Slow motion OFF");\r
+               }\r
+               VW_UpdateScreen();\r
+               IN_Ack();\r
+               return true;\r
+       }\r
+       else if (Keyboard[sc_T])                        // T = sprite test\r
+       {\r
+               TestSprites();\r
+               return true;\r
+       }\r
+       else if (Keyboard[sc_V])                        // V = extra VBLs\r
+       {\r
+               VW_FixRefreshBuffer();\r
+               US_CenterWindow(30, 3);\r
+               PrintY += 6;\r
+               US_Print("  Add how many extra VBLs(0-8):");\r
+               VW_UpdateScreen();\r
+               esc = !US_LineInput(px, py, str, NULL, true, 2, 0);\r
+               if (!esc)\r
+               {\r
+                       level = atoi(str);\r
+                       if (level >= 0 && level <= 8)\r
+                       {\r
+                               extravbls = level;\r
+                       }\r
+               }\r
+               return true;\r
+       }\r
+       else if (Keyboard[sc_W] && ingame)      // W = warp to level\r
+       {\r
+               VW_FixRefreshBuffer();\r
+               US_CenterWindow(26, 3);\r
+               PrintY += 6;\r
+               US_Print("  Warp to which level(1-18):");\r
+               VW_UpdateScreen();\r
+               esc = !US_LineInput(px, py, str, NULL, true, 2, 0);\r
+               if (!esc)\r
+               {\r
+                       level = atoi(str);\r
+                       if (level > 0 && level <= 18)\r
+                       {\r
+                               gamestate.mapon = level;\r
+                               playstate = ex_warped;\r
+                       }\r
+               }\r
+               return true;\r
+       }\r
+       else if (Keyboard[sc_Y])                        // Y = wall debug\r
+       {\r
+               WallDebug();\r
+               return true;\r
+       }\r
+       else if (Keyboard[sc_Z])                        // Z = game over\r
+       {\r
+               gamestate.lives = 0;\r
+               KillKeen();\r
+               return false;\r
+       }\r
+       return false;\r
+}\r
+\r
+//===========================================================================\r
+\r
+/*\r
+================\r
+=\r
+= UserCheat\r
+=\r
+================\r
+*/\r
+\r
+void UserCheat(void)\r
+{\r
+       Sint16 i;\r
+\r
+       for (i=sc_A; i<=sc_Z; i++)      //Note: this does NOT check the keys in alphabetical order!\r
+       {\r
+               if (i != sc_B && i != sc_A && i != sc_T && Keyboard[i])\r
+               {\r
+                       return;\r
+               }\r
+       }\r
+       US_CenterWindow(20, 7);\r
+       PrintY += 2;\r
+       US_CPrint(\r
+               "Cheat Option!\n"\r
+               "\n"\r
+               "You just got all\n"\r
+               "the keys, 99 shots,\n"\r
+               "and an extra keen!");\r
+       VW_UpdateScreen();\r
+       IN_Ack();\r
+       RF_ForceRefresh();\r
+       gamestate.ammo = 99;\r
+       gamestate.lives++;\r
+#ifdef KEEN5\r
+       gamestate.keycard = true;\r
+#endif\r
+       gamestate.keys[0] = gamestate.keys[1] = gamestate.keys[2] = gamestate.keys[3] = 1;\r
+}\r
+\r
+//===========================================================================\r
+\r
+/*\r
+=====================\r
+=\r
+= CheckKeys\r
+=\r
+=====================\r
+*/\r
+\r
+void CheckKeys(void)\r
+{\r
+       if (screenfaded)                        // don't do anything with a faded screen\r
+       {\r
+               return;\r
+       }\r
+\r
+//\r
+// Enter for status screen\r
+//\r
+       if (Keyboard[sc_Enter] || (GravisGamepad && GravisAction[ga_Status]))\r
+       {\r
+               StatusWindow();\r
+               IN_ClearKeysDown();\r
+               RF_ForceRefresh();\r
+               lasttimecount = TimeCount;      // BUG: should be the other way around\r
+       }\r
+\r
+//\r
+// pause key wierdness can't be checked as a scan code\r
+//\r
+       if (Paused)\r
+       {\r
+               SD_MusicOff();\r
+               VW_FixRefreshBuffer();\r
+               US_CenterWindow(8, 3);\r
+               US_PrintCentered("PAUSED");\r
+               VW_UpdateScreen();\r
+               IN_Ack();\r
+               RF_ForceRefresh();\r
+               Paused = false;\r
+               SD_MusicOn();\r
+       }\r
+\r
+#ifndef KEEN6\r
+//\r
+// F1 to enter help screens\r
+//\r
+       if (LastScan == sc_F1)\r
+       {\r
+               StopMusic();\r
+               HelpScreens();\r
+               StartMusic(gamestate.mapon);\r
+               if (showscorebox)\r
+               {\r
+                       scoreobj->temp2 = -1;\r
+                       scoreobj->temp1 = -1;\r
+                       scoreobj->temp3 = -1;\r
+                       scoreobj->temp4 = -1;\r
+               }\r
+               RF_ForceRefresh();\r
+       }\r
+#endif\r
+\r
+       if (!storedemo)\r
+       {\r
+//\r
+// F2-F7/ESC to enter control panel\r
+//\r
+               if (LastScan >= sc_F2 && LastScan <= sc_F7 || LastScan == sc_Escape)\r
+               {\r
+                       VW_FixRefreshBuffer();\r
+                       StopMusic();\r
+                       US_ControlPanel();\r
+                       RF_FixOfs();\r
+                       StartMusic(gamestate.mapon);\r
+                       if (!showscorebox && scoreobj->sprite)\r
+                       {\r
+                               RF_RemoveSprite(&scoreobj->sprite);\r
+                       }\r
+                       if (showscorebox)\r
+                       {\r
+                               scoreobj->temp2 = -1;\r
+                               scoreobj->temp1 = -1;\r
+                               scoreobj->temp3 = -1;\r
+                               scoreobj->temp4 = -1;\r
+                       }\r
+                       IN_ClearKeysDown();\r
+                       if (restartgame)\r
+                       {\r
+                               playstate = ex_resetgame;\r
+                       }\r
+                       else if (!loadedgame)\r
+                       {\r
+                               RF_ForceRefresh();\r
+                       }\r
+                       if (abortgame)\r
+                       {\r
+                               abortgame = false;\r
+                               playstate = ex_abortgame;\r
+                       }\r
+                       if (loadedgame)\r
+                       {\r
+                               playstate = ex_loadedgame;\r
+                       }\r
+                       lasttimecount = TimeCount;      // BUG: should be the other way around\r
+               }\r
+\r
+//\r
+// F9 boss key\r
+//\r
+               if (LastScan == sc_F9)\r
+               {\r
+                       VW_Shutdown();\r
+                       SD_MusicOff();\r
+                       cputs("C:>");\r
+                       IN_ClearKeysDown();\r
+                       while (LastScan != sc_Escape);\r
+                       VW_SetScreenMode(GRMODE);\r
+                       VW_ColorBorder(bordercolor);\r
+                       RF_ForceRefresh();\r
+                       IN_ClearKeysDown();\r
+                       lasttimecount = TimeCount;      // BUG: should be the other way around\r
+                       SD_MusicOn();\r
+               }\r
+       }\r
+\r
+//\r
+// B-A-T cheat code\r
+//\r
+       if (Keyboard[sc_B] && Keyboard[sc_A] && Keyboard[sc_T])\r
+       {\r
+               UserCheat();\r
+       }\r
+\r
+//\r
+// F10-? debug keys\r
+//\r
+       if (debugok && Keyboard[sc_F10])\r
+       {\r
+               if (DebugKeys())\r
+               {\r
+                       RF_ForceRefresh();\r
+                       lasttimecount = TimeCount;      // BUG: should be the other way around\r
+               }\r
+       }\r
+\r
+//\r
+// Ctrl-S toggles sound (only in storedemo mode)\r
+//\r
+       if (storedemo && Keyboard[sc_Control] && LastScan == sc_S)\r
+       {\r
+               if (SoundMode != sdm_Off)\r
+               {\r
+                       SD_SetSoundMode(sdm_Off);\r
+                       SD_SetMusicMode(smm_Off);\r
+               }\r
+               else\r
+               {\r
+                       if (AdLibPresent)\r
+                       {\r
+                               SD_SetSoundMode(sdm_AdLib);\r
+                               QuietFX = false;\r
+                               SD_SetMusicMode(smm_AdLib);\r
+                       }\r
+                       else\r
+                       {\r
+                               SD_SetSoundMode(sdm_PC);\r
+                               SD_SetMusicMode(smm_Off);\r
+                       }\r
+                       CA_LoadAllSounds();\r
+               }\r
+       }\r
+\r
+//\r
+// Ctrl-Q quick quit\r
+//\r
+       if (Keyboard[sc_Control] && LastScan == sc_Q)\r
+       {\r
+               IN_ClearKeysDown();\r
+               Quit(NULL);\r
+       }\r
+}\r
+\r
+//===========================================================================\r
+\r
+/*\r
+==================\r
+=\r
+= PrintNumbers\r
+=\r
+==================\r
+*/\r
+\r
+void PrintNumbers(Sint16 x, Sint16 y, Sint16 maxlen, Sint16 basetile, Sint32 number)\r
+{\r
+       register Sint16 i;\r
+       Sint16 len;\r
+       char buffer[20];\r
+\r
+       ltoa(number, buffer, 10);\r
+       len = strlen(buffer);\r
+       i = maxlen;\r
+       while (i>len)\r
+       {\r
+               VWB_DrawTile8(x, y, basetile);\r
+               i--;\r
+               x += 8;\r
+       }\r
+       while (i>0)\r
+       {\r
+               VWB_DrawTile8(x, y, basetile+buffer[len-i]+(1-'0'));\r
+               i--;\r
+               x += 8;\r
+       }\r
+}\r
+\r
+/*\r
+==================\r
+=\r
+= DrawStatusWindow\r
+=\r
+==================\r
+*/\r
+\r
+#if GRMODE == CGAGR\r
+\r
+#define BACKCOLOR WHITE\r
+#define TEXTBACK BLACK\r
+#define NUMBERBACK BLACK\r
+\r
+#else\r
+\r
+#define BACKCOLOR LIGHTGRAY\r
+#define TEXTBACK WHITE\r
+#define NUMBERBACK BLACK\r
+\r
+#endif\r
+\r
+void DrawStatusWindow(void)\r
+{\r
+       Sint16 off, x, y, w, h, i;\r
+       Uint16 width, height;\r
+\r
+       x = 64;\r
+       y = 16;\r
+       w = 184;\r
+       h = 144;\r
+       VWB_DrawTile8(x, y, 54);\r
+       VWB_DrawTile8(x, y+h, 60);\r
+       for (i=x+8; i<=x+w-8; i+=8)\r
+       {\r
+               VWB_DrawTile8(i, y, 55);\r
+               VWB_DrawTile8(i, y+h, 61);\r
+       }\r
+       VWB_DrawTile8(i, y, 56);\r
+       VWB_DrawTile8(i, y+h, 62);\r
+       for (i=y+8; i<=y+h-8; i+=8)\r
+       {\r
+               VWB_DrawTile8(x, i, 57);\r
+               VWB_DrawTile8(x+w, i, 59);\r
+       }\r
+       VWB_Bar(72, 24, 176, 136, BACKCOLOR);\r
+\r
+       PrintY = 28;\r
+       WindowX = 80;\r
+       WindowW = 160;\r
+       US_CPrint("LOCATION");\r
+       VWB_Bar(79, 38, 162, 20, TEXTBACK);\r
+#ifdef KEEN5\r
+       if (mapon == 0 && player->y > 100*TILEGLOBAL)\r
+               _fstrcpy(str, levelnames[13]);\r
+       else\r
+               _fstrcpy(str, levelnames[gamestate.mapon]);\r
+#else\r
+       _fstrcpy(str, levelnames[gamestate.mapon]);\r
+#endif\r
+       SizeText(str, &width, &height);\r
+       PrintY = (20-height)/2+40-2;\r
+       US_CPrint(str);\r
+\r
+       PrintY = 61;\r
+       WindowX = 80;\r
+       WindowW = 64;\r
+       US_CPrint("SCORE");\r
+       VWB_Bar(79, 71, 66, 10, NUMBERBACK);\r
+       PrintNumbers(80, 72, 8, 41, gamestate.score);\r
+\r
+       PrintY = 61;\r
+       WindowX = 176;\r
+       WindowW = 64;\r
+       US_CPrint("EXTRA");\r
+       VWB_Bar(175, 71, 66, 10, NUMBERBACK);\r
+       PrintNumbers(176, 72, 8, 41, gamestate.nextextra);\r
+\r
+#if defined KEEN4\r
+       PrintY = 85;\r
+       WindowX = 80;\r
+       WindowW = 64;\r
+       US_CPrint("RESCUED");\r
+       VWB_Bar(79, 95, 66, 10, NUMBERBACK);\r
+       for (i = 0; i < gamestate.rescued; i++, off+=8)\r
+       {\r
+               VWB_DrawTile8(i*8 + 80, 96, 40);\r
+       }\r
+#elif defined KEEN5\r
+       PrintY = 92;\r
+       PrintX = 80;\r
+       US_Print("KEYCARD");\r
+       VWB_Bar(135, 91, 10, 10, NUMBERBACK);\r
+       if (gamestate.keycard)\r
+       {\r
+               VWB_DrawTile8(136, 92, 40);\r
+       }\r
+#endif\r
+\r
+       PrintY = 85;\r
+       WindowX = 176;\r
+       WindowW = 64;\r
+       US_CPrint("LEVEL");\r
+       VWB_Bar(175, 95, 66, 10, TEXTBACK);\r
+       PrintY = 96;\r
+       WindowX = 176;\r
+       WindowW = 64;\r
+       switch (gamestate.difficulty)\r
+       {\r
+       case gd_Easy:\r
+               US_CPrint("Easy");\r
+               break;\r
+       case gd_Normal:\r
+               US_CPrint("Normal");\r
+               break;\r
+       case gd_Hard:\r
+               US_CPrint("Hard");\r
+               break;\r
+       }\r
+\r
+#ifdef KEEN6\r
+       PrintX = 80;\r
+       PrintY = 96;\r
+       US_Print("ITEMS");\r
+       VWB_Bar(127, 95, 26, 10, NUMBERBACK);\r
+       if (gamestate.sandwichstate == 1)\r
+       {\r
+               VWB_DrawTile8(128, 96, 2);\r
+       }\r
+       else\r
+       {\r
+               VWB_DrawTile8(128, 96, 1);\r
+       }\r
+       if (gamestate.hookstate == 1)\r
+       {\r
+               VWB_DrawTile8(136, 96, 4);\r
+       }\r
+       else\r
+       {\r
+               VWB_DrawTile8(136, 96, 3);\r
+       }\r
+       if (gamestate.passcardstate == 1)\r
+       {\r
+               VWB_DrawTile8(144, 96, 6);\r
+       }\r
+       else\r
+       {\r
+               VWB_DrawTile8(144, 96, 5);\r
+       }\r
+#endif\r
+\r
+       PrintX = 80;\r
+       PrintY = 112;\r
+       US_Print("KEYS");\r
+       VWB_Bar(119, 111, 34, 10, NUMBERBACK);\r
+       for (i = 0; i < 4; i++)\r
+       {\r
+               if (gamestate.keys[i])\r
+               {\r
+                       VWB_DrawTile8(i*8+120, 112, 36+i);\r
+               }\r
+       }\r
+\r
+       PrintX = 176;\r
+       PrintY = 112;\r
+       US_Print("AMMO");\r
+       VWB_Bar(215, 111, 26, 10, NUMBERBACK);\r
+       PrintNumbers(216, 112, 3, 41, gamestate.ammo);\r
+\r
+       PrintX = 80;\r
+       PrintY = 128;\r
+       US_Print("KEENS");\r
+       VWB_Bar(127, 127, 18, 10, NUMBERBACK);\r
+       PrintNumbers(128, 128, 2, 41, gamestate.lives);\r
+\r
+       PrintX = 176;\r
+       PrintY = 128;\r
+       US_Print(DROPSNAME);\r
+       VWB_Bar(224, 127, 16, 10, NUMBERBACK);\r
+       PrintNumbers(224, 128, 2, 41, gamestate.drops);\r
+\r
+#ifdef KEEN4\r
+       VWB_Bar(79, 143, 66, 10, TEXTBACK);\r
+       PrintY = 144;\r
+       WindowX = 80;\r
+       WindowW = 64;\r
+       if (gamestate.wetsuit)\r
+       {\r
+               US_CPrint("Wetsuit");\r
+       }\r
+       else\r
+       {\r
+               US_CPrint("???");\r
+       }\r
+#endif\r
+\r
+       // draw the tiles for "PRESS A KEY":\r
+       for (i = 0; i < 10; i++)\r
+       {\r
+               VWB_DrawTile8(i*8+STATUS_PRESSKEY_X, 140, i+72);\r
+               VWB_DrawTile8(i*8+STATUS_PRESSKEY_X, 148, i+82);\r
+       }\r
+}\r
+\r
+/*\r
+==================\r
+=\r
+= ScrollStatusWindow\r
+=\r
+==================\r
+*/\r
+\r
+void ScrollStatusWindow(void)\r
+{\r
+       Uint16 source, dest;\r
+       Sint16 height;\r
+\r
+       if (vislines > 152)\r
+       {\r
+               height = vislines - 152;\r
+               source = windowofs + panadjust + 8;\r
+               dest = bufferofs + panadjust + 8;\r
+               VW_ScreenToScreen(source, dest, 192/BYTEPIXELS, height);\r
+               VW_ClipDrawMPic((pansx+136)/BYTEPIXELS, -(16-height)+pansy, METALPOLEPICM);\r
+               source = windowofs + panadjust + 16*SCREENWIDTH + 8*CHARWIDTH;\r
+               dest = bufferofs + panadjust + height*SCREENWIDTH + 8;\r
+               height = 152;\r
+       }\r
+       else\r
+       {\r
+               source = windowofs + panadjust + (152-vislines)*SCREENWIDTH + 16*SCREENWIDTH + 8*CHARWIDTH;\r
+               dest = bufferofs + panadjust + 8;\r
+               height = vislines;\r
+       }\r
+       if (height > 0)\r
+       {\r
+               VW_ScreenToScreen(source, dest, 192/BYTEPIXELS, height);\r
+       }\r
+       if (scrollup)\r
+       {\r
+               height = 168-vislines;\r
+               source = masterofs + panadjust + vislines*SCREENWIDTH + 8;\r
+               dest = bufferofs + panadjust + vislines*SCREENWIDTH + 8;\r
+               VW_ScreenToScreen(source, dest, 192/BYTEPIXELS, height);\r
+               height = vislines;\r
+               source = windowofs + panadjust + 8 - 24/BYTEPIXELS;\r
+               dest = bufferofs + panadjust + 8 - 24/BYTEPIXELS;\r
+               if (height > 0)\r
+                       VW_ScreenToScreen(source, dest, 24/BYTEPIXELS, height);\r
+       }\r
+       else\r
+       {\r
+               height = vislines + -72;\r
+               if (height > 0)\r
+               {\r
+                       source = windowofs + panadjust + 8 - 24/BYTEPIXELS;\r
+                       dest = bufferofs + panadjust + 8 - 24/BYTEPIXELS;\r
+                       if (height > 0)\r
+                               VW_ScreenToScreen(source, dest, 24/BYTEPIXELS, height);\r
+               }\r
+       }\r
+       if (vislines >= 72)\r
+       {\r
+               VW_ClipDrawMPic((pansx+40)/BYTEPIXELS, vislines-168+pansy, CORDPICM);\r
+       }\r
+       VW_UpdateScreen();\r
+}\r
+\r
+/*\r
+==================\r
+=\r
+= StatusWindow\r
+=\r
+==================\r
+*/\r
+\r
+void StatusWindow(void)\r
+{\r
+#if GRMODE == CGAGR\r
+\r
+       if (Keyboard[sc_A] && Keyboard[sc_2])\r
+       {\r
+               US_CenterWindow(20, 2);\r
+               PrintY += 2;\r
+               US_Print("Debug keys active");\r
+               VW_UpdateScreen();\r
+               IN_Ack();\r
+               debugok = true;\r
+       }\r
+\r
+       WindowX = 0;\r
+       WindowW = 320;\r
+       WindowY = 0;\r
+       WindowH = 200;\r
+       DrawStatusWindow();\r
+       VW_UpdateScreen();\r
+       IN_ClearKeysDown();\r
+       IN_Ack();\r
+\r
+#else\r
+\r
+       Uint16 oldbufferofs;\r
+\r
+       WindowX = 0;\r
+       WindowW = 320;\r
+       WindowY = 0;\r
+       WindowH = 200;\r
+\r
+       if (Keyboard[sc_A] && Keyboard[sc_2])\r
+       {\r
+               US_CenterWindow(20, 2);\r
+               PrintY += 2;\r
+               US_Print("Debug keys active");\r
+               VW_UpdateScreen();\r
+               IN_Ack();\r
+               debugok = true;\r
+       }\r
+\r
+       RF_Refresh();\r
+       RFL_InitAnimList();\r
+       oldbufferofs = bufferofs;\r
+       bufferofs = windowofs = RF_FindFreeBuffer();\r
+       VW_ScreenToScreen(displayofs, displayofs, 44, 224);     // useless (source and dest offsets are identical)\r
+       VW_ScreenToScreen(displayofs, masterofs, 44, 224);\r
+       VW_ScreenToScreen(displayofs, bufferofs, 44, 168);\r
+       DrawStatusWindow();\r
+       bufferofs = oldbufferofs;\r
+       RF_Refresh();\r
+\r
+       SD_PlaySound(SND_SHOWSTATUS);\r
+       vislines = 16;\r
+       scrollup = false;\r
+       RF_SetRefreshHook(ScrollStatusWindow);\r
+\r
+       while (true)\r
+       {\r
+               RF_Refresh();\r
+               if (vislines == 168)\r
+                       break;\r
+               vislines = vislines + tics*8;\r
+               if (vislines > 168)\r
+                       vislines = 168;\r
+       }\r
+\r
+       RF_Refresh();\r
+       RF_SetRefreshHook(NULL);\r
+       IN_ClearKeysDown();\r
+       IN_Ack();\r
+\r
+       SD_PlaySound(SND_HIDESTATUS);\r
+       vislines -= 16;\r
+       scrollup = true;\r
+       RF_SetRefreshHook(ScrollStatusWindow);\r
+\r
+       while (true)\r
+       {\r
+               RF_Refresh();\r
+               if (vislines == 0)\r
+                       break;\r
+               vislines = vislines - tics*8;\r
+               if (vislines < 0)\r
+                       vislines = 0;\r
+       }\r
+\r
+       RF_SetRefreshHook(NULL);\r
+\r
+       scoreobj->x = 0;        //force scorebox to redraw?\r
+\r
+#endif\r
+}\r
+\r
+//===========================================================================\r
+\r
+/*\r
+==================\r
+=\r
+= CenterActor\r
+=\r
+==================\r
+*/\r
+\r
+void CenterActor(objtype *ob)\r
+{\r
+       Uint16 orgx, orgy;\r
+\r
+       centerlevel = 140;\r
+       if (ob->x < 152*PIXGLOBAL)\r
+       {\r
+               orgx = 0;\r
+       }\r
+       else\r
+       {\r
+               orgx = ob->x - 152*PIXGLOBAL;\r
+       }\r
+       if (mapon == 0)\r
+       {\r
+               if (ob->y < 80*PIXGLOBAL)\r
+               {\r
+                       orgy = 0;\r
+               }\r
+               else\r
+               {\r
+                       orgy = ob->y - 80*PIXGLOBAL;\r
+               }\r
+       }\r
+       else\r
+       {\r
+               if (ob->bottom < 140*PIXGLOBAL)\r
+               {\r
+                       orgy = 0;\r
+               }\r
+               else\r
+               {\r
+                       orgy = ob->bottom - 140*PIXGLOBAL;\r
+               }\r
+       }\r
+       if (!scorescreenkludge)\r
+       {\r
+               RF_NewPosition(orgx, orgy);\r
+       }\r
+\r
+//\r
+// update limits for onscreen and inactivate checks\r
+//\r
+       originxtilemax = (originxtile + PORTTILESWIDE) - 1;\r
+       originytilemax = (originytile + PORTTILESHIGH) - 1;\r
+       inactivateleft = originxtile - INACTIVATEDIST;\r
+       if (inactivateleft < 0)\r
+       {\r
+               inactivateleft = 0;\r
+       }\r
+       inactivateright = originxtilemax + INACTIVATEDIST;\r
+       if (inactivateright < 0)\r
+       {\r
+               inactivateright = 0;\r
+       }\r
+       inactivatetop = originytile - INACTIVATEDIST;\r
+       if (inactivatetop < 0)\r
+       {\r
+               inactivatetop = 0;\r
+       }\r
+       inactivatebottom = originytilemax + INACTIVATEDIST;\r
+       if (inactivatebottom < 0)\r
+       {\r
+               inactivatebottom = 0;\r
+       }\r
+}\r
+\r
+//===========================================================================\r
+\r
+/*\r
+==================\r
+=\r
+= WorldScrollScreen\r
+=\r
+= Scroll if Keen is nearing an edge\r
+=\r
+==================\r
+*/\r
+\r
+void WorldScrollScreen(objtype *ob)\r
+{\r
+       Sint16 xscroll, yscroll;\r
+\r
+       if (keenkilled)\r
+               return;\r
+\r
+       if (ob->left < originxglobal + 9*TILEGLOBAL)\r
+       {\r
+               xscroll = ob->left - (originxglobal + 9*TILEGLOBAL);\r
+       }\r
+       else if (ob->right > originxglobal + 12*TILEGLOBAL)\r
+       {\r
+               xscroll = ob->right + 16 - (originxglobal + 12*TILEGLOBAL);\r
+       }\r
+       else\r
+       {\r
+               xscroll = 0;\r
+       }\r
+\r
+       if (ob->top < originyglobal + 5*TILEGLOBAL)\r
+       {\r
+               yscroll = ob->top - (originyglobal + 5*TILEGLOBAL);\r
+       }\r
+       else if (ob->bottom > originyglobal + 7*TILEGLOBAL)\r
+       {\r
+               yscroll = ob->bottom - (originyglobal + 7*TILEGLOBAL);\r
+       }\r
+       else\r
+       {\r
+               yscroll = 0;\r
+       }\r
+\r
+       if (!xscroll && !yscroll)\r
+               return;\r
+\r
+//\r
+// don't scroll more than one tile per frame\r
+//\r
+       if (xscroll >= 0x100)\r
+       {\r
+               xscroll = 0xFF;\r
+       }\r
+       else if (xscroll <= -0x100)\r
+       {\r
+               xscroll = -0xFF;\r
+       }\r
+       if (yscroll >= 0x100)\r
+       {\r
+               yscroll = 0xFF;\r
+       }\r
+       else if (yscroll <= -0x100)\r
+       {\r
+               yscroll = -0xFF;\r
+       }\r
+\r
+       RF_Scroll(xscroll, yscroll);\r
+\r
+//\r
+// update limits for onscreen and inactivate checks\r
+//\r
+       originxtilemax = (originxtile + PORTTILESWIDE) - 1;\r
+       originytilemax = (originytile + PORTTILESHIGH) - 1;\r
+       inactivateleft = originxtile - INACTIVATEDIST;\r
+       if (inactivateleft < 0)\r
+       {\r
+               inactivateleft = 0;\r
+       }\r
+       inactivateright = originxtilemax + INACTIVATEDIST;\r
+       if (inactivateright < 0)\r
+       {\r
+               inactivateright = 0;\r
+       }\r
+       inactivatetop = originytile - INACTIVATEDIST;\r
+       if (inactivatetop < 0)\r
+       {\r
+               inactivatetop = 0;\r
+       }\r
+       inactivatebottom = originytilemax + INACTIVATEDIST;\r
+       if (inactivatebottom < 0)\r
+       {\r
+               inactivatebottom = 0;\r
+       }\r
+}\r
+\r
+//===========================================================================\r
+\r
+/*\r
+==================\r
+=\r
+= ScrollScreen\r
+=\r
+= Scroll if Keen is nearing an edge\r
+= Set playstate to ex_completes\r
+=\r
+==================\r
+*/\r
+\r
+void ScrollScreen(objtype *ob)\r
+{\r
+       Sint16 xscroll, yscroll, pix, speed;\r
+       Uint16 bottom;\r
+\r
+       if (keenkilled)\r
+               return;\r
+\r
+//\r
+// walked off edge of map?\r
+//\r
+       if (ob->left < originxmin || ob->right > originxmax + 320*PIXGLOBAL)\r
+       {\r
+               playstate = ex_completed;\r
+               return;\r
+       }\r
+\r
+//\r
+// fallen off bottom of world?\r
+//\r
+       if (ob->bottom > originymax + 13*TILEGLOBAL)\r
+       {\r
+               ob->y -= ob->bottom - (originymax + 13*TILEGLOBAL);\r
+               SD_PlaySound(SND_PLUMMET);\r
+               godmode = false;\r
+               KillKeen();\r
+               return;\r
+       }\r
+\r
+       xscroll=yscroll=0;\r
+\r
+       if (ob->x < originxglobal + 9*TILEGLOBAL)\r
+       {\r
+               xscroll = ob->x - (originxglobal + 9*TILEGLOBAL);\r
+       }\r
+       else if (ob->x > originxglobal + 12*TILEGLOBAL)\r
+       {\r
+               xscroll = ob->x - (originxglobal + 12*TILEGLOBAL);\r
+       }\r
+\r
+       if (ob->state == &s_keenlookup2)\r
+       {\r
+               if (centerlevel+tics > 167)\r
+               {\r
+                       pix = 167-centerlevel;\r
+               }\r
+               else\r
+               {\r
+                       pix = tics;\r
+               }\r
+               centerlevel += pix;\r
+               yscroll = CONVERT_PIXEL_TO_GLOBAL(-pix);\r
+       }\r
+       else if (ob->state == &s_keenlookdown3)\r
+       {\r
+               if (centerlevel-tics < 33)\r
+               {\r
+                       pix = centerlevel + -33;\r
+               }\r
+               else\r
+               {\r
+                       pix = tics;\r
+               }\r
+               centerlevel -= pix;\r
+               yscroll = CONVERT_PIXEL_TO_GLOBAL(pix);\r
+       }\r
+\r
+#ifdef KEEN6\r
+       if (groundslam)\r
+       {\r
+               static Sint16 shaketable[] = {0,\r
+                        -64,  -64,  -64,  64,  64,  64,\r
+                       -200, -200, -200, 200, 200, 200,\r
+                       -250, -250, -250, 250, 250, 250,\r
+                       -250, -250, -250, 250, 250, 250\r
+               };\r
+               yscroll = yscroll + (bottom - (ob->bottom + shaketable[groundslam]));   // BUG: 'bottom' has not been initialized yet!\r
+       }\r
+       else\r
+#endif\r
+       if ( (ob->hitnorth || !ob->needtoclip || ob->state == &s_keenholdon))\r
+       {\r
+               if (  ob->state != &s_keenclimbup\r
+                       && ob->state != &s_keenclimbup2\r
+                       && ob->state != &s_keenclimbup3\r
+                       && ob->state != &s_keenclimbup4)\r
+               {\r
+                       yscroll += ob->ymove;\r
+                       bottom = originyglobal + yscroll + CONVERT_PIXEL_TO_GLOBAL(centerlevel);\r
+                       if (ob->bottom == bottom)\r
+                       {\r
+                               // player didn't move, no additional scrolling\r
+                       }\r
+                       else\r
+                       {\r
+                               if (ob->bottom < bottom)\r
+                               {\r
+                                       pix = bottom - ob->bottom;\r
+                               }\r
+                               else\r
+                               {\r
+                                       pix = ob->bottom - bottom;\r
+                               }\r
+                               speed = CONVERT_PIXEL_TO_GLOBAL(pix) >> 7;\r
+                               if (speed > 0x30)\r
+                               {\r
+                                       speed = 0x30;\r
+                               }\r
+                               speed *= tics;\r
+                               if (speed < 0x10)\r
+                               {\r
+                                       if (pix < 0x10)\r
+                                       {\r
+                                               speed = pix;\r
+                                       }\r
+                                       else\r
+                                       {\r
+                                               speed = 0x10;\r
+                                       }\r
+                               }\r
+                               if (ob->bottom < bottom)\r
+                               {\r
+                                       yscroll -= speed;\r
+                               }\r
+                               else\r
+                               {\r
+                                       yscroll += speed;\r
+                               }\r
+                       }\r
+               }\r
+       }\r
+       else\r
+       {\r
+               centerlevel = 140;\r
+       }\r
+\r
+       pix = (ob->bottom-32*PIXGLOBAL)-(originyglobal+yscroll);\r
+       if (pix < 0)\r
+       {\r
+               yscroll += pix;\r
+       }\r
+       pix = (ob->bottom+32*PIXGLOBAL)-(originyglobal+yscroll+200*PIXGLOBAL);\r
+       if (pix > 0)\r
+       {\r
+               yscroll += pix;\r
+       }\r
+\r
+       if (xscroll == 0 && yscroll == 0)\r
+               return;\r
+\r
+//\r
+// don't scroll more than one tile per frame\r
+//\r
+       if (xscroll >= 0x100)\r
+       {\r
+               xscroll = 0xFF;\r
+       }\r
+       else if (xscroll <= -0x100)\r
+       {\r
+               xscroll = -0xFF;\r
+       }\r
+       if (yscroll >= 0x100)\r
+       {\r
+               yscroll = 0xFF;\r
+       }\r
+       else if (yscroll <= -0x100)\r
+       {\r
+               yscroll = -0xFF;\r
+       }\r
+       RF_Scroll(xscroll, yscroll);\r
+\r
+//\r
+// update limits for onscreen and inactivate checks\r
+//\r
+       originxtilemax = (originxtile + PORTTILESWIDE) - 1;\r
+       originytilemax = (originytile + PORTTILESHIGH) - 1;\r
+       inactivateleft = originxtile - INACTIVATEDIST;\r
+       if (inactivateleft < 0)\r
+       {\r
+               inactivateleft = 0;\r
+       }\r
+       inactivateright = originxtilemax + INACTIVATEDIST;\r
+       if (inactivateright < 0)\r
+       {\r
+               inactivateright = 0;\r
+       }\r
+       inactivatetop = originytile - INACTIVATEDIST;\r
+       if (inactivatetop < 0)\r
+       {\r
+               inactivatetop = 0;\r
+       }\r
+       inactivatebottom = originytilemax + INACTIVATEDIST;\r
+       if (inactivatebottom < 0)\r
+       {\r
+               inactivatebottom = 0;\r
+       }\r
+}\r
+\r
+//===========================================================================\r
+\r
+\r
+/*\r
+#############################################################################\r
+\r
+                                 The objarray data structure\r
+\r
+#############################################################################\r
+\r
+Objarray contains structures for every actor currently playing.  The structure\r
+is accessed as a linked list starting at *player, ending when ob->next ==\r
+NULL.  GetNewObj inserts a new object at the end of the list, meaning that\r
+if an actor spawns another actor, the new one WILL get to think and react the\r
+same frame.  RemoveObj unlinks the given object and returns it to the free\r
+list, but does not damage the objects ->next pointer, so if the current object\r
+removes itself, a linked list following loop can still safely get to the\r
+next element.\r
+\r
+<backwardly linked free list>\r
+\r
+#############################################################################\r
+*/\r
+\r
+\r
+/*\r
+=========================\r
+=\r
+= InitObjArray\r
+=\r
+= Call to clear out the entire object list, returning them all to the free\r
+= list.  Allocates a special spot for the player.\r
+=\r
+=========================\r
+*/\r
+\r
+void InitObjArray(void)\r
+{\r
+       Sint16 i;\r
+\r
+       for (i=0; i<MAXACTORS; i++)\r
+       {\r
+               objarray[i].prev = &objarray[i+1];\r
+               objarray[i].next = NULL;\r
+       }\r
+\r
+       objarray[MAXACTORS-1].prev = NULL;\r
+\r
+       objfreelist = &objarray[0];\r
+       lastobj = NULL;\r
+\r
+       objectcount = 0;\r
+\r
+//\r
+// give the player and score the first free spots\r
+//\r
+       GetNewObj(false);\r
+       player = new;\r
+       GetNewObj(false);\r
+       scoreobj = new;\r
+}\r
+\r
+//===========================================================================\r
+\r
+/*\r
+=========================\r
+=\r
+= GetNewObj\r
+=\r
+= Sets the global variable new to point to a free spot in objarray.\r
+= The free spot is inserted at the end of the liked list\r
+=\r
+= When the object list is full, the caller can either have it bomb out or\r
+= return a dummy object pointer that will never get used\r
+=\r
+= Returns -1 when list was full, otherwise returns 0.\r
+=\r
+=========================\r
+*/\r
+\r
+Sint16 GetNewObj(boolean usedummy)\r
+{\r
+       if (!objfreelist)\r
+       {\r
+               if (usedummy)\r
+               {\r
+                       new = &dummyobj;\r
+                       return -1;\r
+               }\r
+               Quit("GetNewObj: No free spots in objarray!");\r
+       }\r
+       new = objfreelist;\r
+       objfreelist = new->prev;\r
+       memset(new, 0, sizeof(*new));\r
+       if (lastobj)\r
+       {\r
+               lastobj->next = new;\r
+       }\r
+       new->prev = lastobj;    // new->next is allready NULL from memset\r
+\r
+       new->active = ac_yes;\r
+       new->needtoclip = cl_midclip;\r
+       lastobj = new;\r
+\r
+       objectcount++;\r
+       return 0;\r
+}\r
+\r
+//===========================================================================\r
+\r
+/*\r
+=========================\r
+=\r
+= RemoveObj\r
+=\r
+= Add the given object back into the free list, and unlink it from it's\r
+= neighbors\r
+=\r
+=========================\r
+*/\r
+\r
+void RemoveObj(objtype *ob)\r
+{\r
+       if (ob == player)\r
+               Quit("RemoveObj: Tried to remove the player!");\r
+\r
+//\r
+// erase it from the refresh manager\r
+//\r
+       RF_RemoveSprite(&ob->sprite);\r
+       if (ob->obclass == stunnedobj)\r
+       {\r
+               RF_RemoveSprite((void **)&ob->temp3);\r
+       }\r
+\r
+//\r
+// fix the next object's back link\r
+//\r
+       if (ob == lastobj)\r
+       {\r
+               lastobj = ob->prev;\r
+       }\r
+       else\r
+       {\r
+               ob->next->prev = ob->prev;\r
+       }\r
+\r
+//\r
+// fix the previous object's forward link\r
+//\r
+       ob->prev->next = ob->next;\r
+\r
+//\r
+// add it back in to the free list\r
+//\r
+       ob->prev = objfreelist;\r
+       objfreelist = ob;\r
+}\r
+\r
+//==========================================================================\r
+\r
+/*\r
+====================\r
+=\r
+= GivePoints\r
+=\r
+= Grants extra men at 20k,40k,80k,160k,320k\r
+=\r
+====================\r
+*/\r
+\r
+void GivePoints(Uint16 points)\r
+{\r
+       gamestate.score += points;\r
+       if (!DemoMode && gamestate.score >= gamestate.nextextra)\r
+       {\r
+               SD_PlaySound(SND_EXTRAKEEN);\r
+               gamestate.lives++;\r
+               gamestate.nextextra *= 2;\r
+       }\r
+}\r
+\r
+//==========================================================================\r
+\r
+/*\r
+===================\r
+=\r
+= PollControls\r
+=\r
+===================\r
+*/\r
+\r
+void PollControls(void)\r
+{\r
+       IN_ReadControl(0, &c);\r
+       if (c.yaxis != -1)\r
+               upheld = false;\r
+\r
+       if (GravisGamepad && !DemoMode)\r
+       {\r
+               jumpbutton = GravisAction[ga_Jump];\r
+               pogobutton = GravisAction[ga_Pogo];\r
+               firebutton = GravisAction[ga_Fire];\r
+               if (!jumpbutton)\r
+                       jumpheld = false;\r
+               if (!pogobutton)\r
+                       pogoheld = false;\r
+               if (!firebutton)\r
+                       fireheld = false;\r
+       }\r
+       else if (oldshooting || DemoMode)\r
+       {\r
+               if (c.button0 && c.button1)\r
+               {\r
+                       firebutton = true;\r
+                       jumpbutton = pogobutton = jumpheld = pogoheld = false;\r
+               }\r
+               else\r
+               {\r
+                       firebutton = fireheld = false;\r
+                       if (c.button0)\r
+                       {\r
+                               jumpbutton = true;\r
+                       }\r
+                       else\r
+                       {\r
+                               jumpbutton = jumpheld = false;\r
+                       }\r
+                       if (c.button1)\r
+                       {\r
+                               if (oldfirecount <= 8)\r
+                               {\r
+                                       oldfirecount = oldfirecount + tics;\r
+                               }\r
+                               else\r
+                               {\r
+                                       pogobutton = true;\r
+                               }\r
+                       }\r
+                       else\r
+                       {\r
+                               if (oldfirecount != 0)\r
+                               {\r
+                                       pogobutton = true;\r
+                               }\r
+                               else\r
+                               {\r
+                                       pogobutton = pogoheld = false;\r
+                               }\r
+                               oldfirecount = 0;\r
+                       }\r
+               }\r
+       }\r
+       else\r
+       {\r
+               jumpbutton = c.button0;\r
+               pogobutton = c.button1;\r
+               firebutton = Keyboard[firescan];\r
+               if (!jumpbutton)\r
+                       jumpheld = false;\r
+               if (!pogobutton)\r
+                       pogoheld = false;\r
+               if (!firebutton)\r
+                       fireheld = false;\r
+       }\r
+}\r
+\r
+//==========================================================================\r
+\r
+\r
+/*\r
+=================\r
+=\r
+= StopMusic\r
+=\r
+=================\r
+*/\r
+\r
+void StopMusic(void)\r
+{\r
+       Sint16 i;\r
+\r
+       SD_MusicOff();\r
+       for (i=0; i<LASTMUSIC; i++)\r
+       {\r
+               if (audiosegs[STARTMUSIC+i])\r
+               {\r
+#ifdef FIX_MUSIC_MEMORY_ISSUES\r
+                       //unlock any music blocks so that they can be purged\r
+                       MM_SetLock(&(memptr)audiosegs[STARTMUSIC+i], false);\r
+#endif\r
+                       MM_SetPurge(&(memptr)audiosegs[STARTMUSIC+i], PURGE_FIRST);\r
+               }\r
+       }\r
+}\r
+\r
+//==========================================================================\r
+\r
+/*\r
+=================\r
+=\r
+= StartMusic\r
+=\r
+=================\r
+*/\r
+\r
+void StartMusic(Uint16 num)\r
+{\r
+       static Sint16 songs[] =\r
+       {\r
+#if defined KEEN4\r
+               SHADOWS_MUS,\r
+               KICKPANT_MUS,\r
+               OASIS_MUS,\r
+               OASIS_MUS,\r
+               TOOHOT_MUS,\r
+               TOOHOT_MUS,\r
+               KICKPANT_MUS,\r
+               OASIS_MUS,\r
+               VEGGIES_MUS,\r
+               VEGGIES_MUS,\r
+               VEGGIES_MUS,\r
+               TOOHOT_MUS,\r
+               TOOHOT_MUS,\r
+               TOOHOT_MUS,\r
+               TOOHOT_MUS,\r
+               TOOHOT_MUS,\r
+               TOOHOT_MUS,\r
+               VEGGIES_MUS,\r
+               OASIS_MUS,\r
+               -1\r
+#elif defined KEEN5\r
+               ROBOROCK_MUS,\r
+               WEDNESDY_MUS,\r
+               BREATHE_MUS,\r
+               SPHEREFUL_MUS,\r
+               TIGHTER_MUS,\r
+               SPHEREFUL_MUS,\r
+               TIGHTER_MUS,\r
+               SPHEREFUL_MUS,\r
+               TIGHTER_MUS,\r
+               SPHEREFUL_MUS,\r
+               TIGHTER_MUS,\r
+               SNOOPING_MUS,\r
+               FEARSOME_MUS,\r
+               BAGPIPES_MUS,\r
+               FANFARE_MUS,\r
+               SKATING_MUS,\r
+               ROCK_ME_MUS,\r
+               HAVING_T_MUS,\r
+               CAMEIN_MUS,\r
+               SHIKAIRE_MUS,\r
+#elif defined KEEN6\r
+               ALIENATE_MUS,\r
+               FASTER_MUS,\r
+               BRERTAR_MUS,\r
+               MAMSNAKE_MUS,\r
+               MAMSNAKE_MUS,\r
+               MAMSNAKE_MUS,\r
+               METAL_MUS,\r
+               TOFUTURE_MUS,\r
+               METAL_MUS,\r
+               BRERTAR_MUS,\r
+               FASTER_MUS,\r
+               TOFUTURE_MUS,\r
+               BRERTAR_MUS,\r
+               SPACFUNK_MUS,\r
+               SPACFUNK_MUS,\r
+               OMINOUS_MUS,\r
+               TOFUTURE_MUS,\r
+               WONDER_MUS,\r
+               WONDER_MUS,\r
+               WONDER_MUS\r
+#endif\r
+       };\r
+\r
+       Sint16 song;\r
+       boolean wasfaded;\r
+\r
+       if (num >= ARRAYLENGTH(songs) && num != 0xFFFF)\r
+       {\r
+               Quit("StartMusic() - bad level number");\r
+       }\r
+\r
+#ifdef FIX_MUSIC_MEMORY_ISSUES\r
+       StopMusic();\r
+#else\r
+       SD_MusicOff();\r
+#endif\r
+\r
+#ifdef KEEN4\r
+       if (num == 0xFFFF)\r
+       {\r
+               song = WONDER_MUS;\r
+       }\r
+       else\r
+       {\r
+               song = songs[num];\r
+       }\r
+#else\r
+       song = songs[num];\r
+#endif\r
+\r
+       if (song == -1 || MusicMode != smm_AdLib)\r
+       {\r
+               return;\r
+       }\r
+\r
+       MM_BombOnError(false);\r
+       CA_CacheAudioChunk(STARTMUSIC+song);\r
+       MM_BombOnError(true);\r
+       if (mmerror)\r
+       {\r
+               mmerror = false;\r
+               if (!DemoMode)\r
+               {\r
+                       US_CenterWindow(20, 8);\r
+                       PrintY += 20;\r
+                       US_CPrint("Insufficient memory\nfor background music!");\r
+                       VW_UpdateScreen();\r
+                       wasfaded = screenfaded;\r
+                       if (wasfaded)\r
+                       {\r
+                               VW_SetDefaultColors();\r
+                       }\r
+                       IN_ClearKeysDown();\r
+                       IN_UserInput(3*TickBase, false);\r
+                       if (wasfaded)\r
+                       {\r
+                               VW_FadeOut();\r
+                       }\r
+               }\r
+       }\r
+       else\r
+       {\r
+#ifdef FIX_MUSIC_MEMORY_ISSUES\r
+               //The current music should be locked, so the memory manager will not\r
+               //mess with it when compressing memory blocks in MM_SortMem().\r
+               MM_SetLock(&(memptr)audiosegs[STARTMUSIC+song], true);\r
+#endif\r
+               SD_StartMusic((MusicGroup far *)audiosegs[STARTMUSIC+song]);\r
+       }\r
+}\r
+\r
+//==========================================================================\r
+\r
+\r
+/*\r
+===================\r
+=\r
+= PlayLoop\r
+=\r
+===================\r
+*/\r
+\r
+void PlayLoop(void)\r
+{\r
+       objtype *check;\r
+\r
+       StartMusic(gamestate.mapon);\r
+       fireheld = pogoheld = upheld = jumpheld = false;\r
+       ingame = true;\r
+       playstate = ex_stillplaying;\r
+       invincible = keenkilled = oldfirecount = 0;\r
+\r
+       CenterActor(player);\r
+\r
+       if (DemoMode)\r
+       {\r
+               US_InitRndT(false);\r
+       }\r
+       else\r
+       {\r
+               US_InitRndT(true);\r
+       }\r
+       TimeCount = lasttimecount = tics = 3;\r
+\r
+       do\r
+       {\r
+               PollControls();\r
+\r
+//\r
+// go through state changes and propose movements\r
+//\r
+               for (obj=player; obj; obj=obj->next)\r
+               {\r
+                       if (!obj->active && obj->tileright >= originxtile-1\r
+                               && obj->tileleft <= originxtilemax+1 && obj->tiletop <= originytilemax+1\r
+                               && obj->tilebottom >= originytile-1)\r
+                       {\r
+                               obj->needtoreact = true;\r
+                               obj->active = ac_yes;\r
+                       }\r
+                       if (obj->active)\r
+                       {\r
+                               if (obj->tileright < inactivateleft\r
+                                       || obj->tileleft > inactivateright\r
+                                       || obj->tiletop > inactivatebottom\r
+                                       || obj->tilebottom < inactivatetop)\r
+                               {\r
+                                       if (obj->active == ac_removable)\r
+                                       {\r
+                                               RemoveObj(obj);\r
+                                               continue;\r
+                                       }\r
+                                       else if (obj->active != ac_allways)\r
+                                       {\r
+                                               if (US_RndT() < tics*2 || screenfaded || loadedgame)\r
+                                               {\r
+                                                       RF_RemoveSprite(&obj->sprite);\r
+                                                       if (obj->obclass == stunnedobj)\r
+                                                               RF_RemoveSprite((void **)&obj->temp3);\r
+                                                       obj->active = ac_no;\r
+                                                       continue;\r
+                                               }\r
+                                       }\r
+                               }\r
+                               StateMachine(obj);\r
+                       }\r
+               }\r
+\r
+               if (gamestate.riding)\r
+               {\r
+                       HandleRiding(player);\r
+               }\r
+\r
+//\r
+// check for and handle collisions between objects\r
+//\r
+               for (obj=player; obj; obj=obj->next)\r
+               {\r
+                       if (obj->active)\r
+                       {\r
+                               for (check=obj->next; check; check=check->next)\r
+                               {\r
+                                       if (!check->active)\r
+                                       {\r
+                                               continue;\r
+                                       }\r
+                                       if (obj->right > check->left && obj->left < check->right\r
+                                               && obj->top < check->bottom && obj->bottom > check->top)\r
+                                       {\r
+                                               if (obj->state->contact)\r
+                                               {\r
+                                                       obj->state->contact(obj, check);\r
+                                               }\r
+                                               if (check->state->contact)\r
+                                               {\r
+                                                       check->state->contact(check, obj);\r
+                                               }\r
+                                               if (obj->obclass == nothing)    //useless -- obclass is NOT set to nothing by RemoveObj\r
+                                               {\r
+                                                       break;\r
+                                               }\r
+                                       }\r
+                               }\r
+                       }\r
+               }\r
+\r
+//\r
+// check intiles\r
+//\r
+               if (mapon != 0)\r
+               {\r
+                       CheckInTiles(player);\r
+               }\r
+               else\r
+               {\r
+                       CheckWorldInTiles(player);\r
+               }\r
+\r
+//\r
+// react to whatever happened, and post sprites to the refresh manager\r
+//\r
+               for (obj=player; obj; obj=obj->next)\r
+               {\r
+                       if (!obj->active)\r
+                       {\r
+                               continue;\r
+                       }\r
+                       if (obj->tilebottom >= mapheight-1)\r
+                       {\r
+                               if (obj->obclass == keenobj)\r
+                               {\r
+                                       playstate = ex_died;\r
+                               }\r
+                               else\r
+                               {\r
+                                       RemoveObj(obj);\r
+                               }\r
+                               continue;\r
+                       }\r
+                       if (obj->needtoreact && obj->state->react)\r
+                       {\r
+                               obj->needtoreact = false;\r
+                               obj->state->react(obj);\r
+                       }\r
+               }\r
+\r
+//\r
+// scroll the screen and update the score box\r
+//\r
+#ifdef KEEN4\r
+               if (mapon != 0 && mapon != 17)\r
+#else\r
+               if (mapon != 0)\r
+#endif\r
+               {\r
+                       ScrollScreen(player);\r
+               }\r
+               else\r
+               {\r
+                       WorldScrollScreen(player);\r
+               }\r
+               UpdateScore(scoreobj);\r
+               if (loadedgame)\r
+               {\r
+                       loadedgame = false;\r
+               }\r
+\r
+//\r
+// update the screen and calculate the number of tics it took to execute\r
+// this cycle of events (for adaptive timing of next cycle)\r
+//\r
+               RF_Refresh();\r
+\r
+               if (invincible)\r
+               {\r
+                       if ((invincible = invincible - tics) < 0)\r
+                               invincible = 0;\r
+               }\r
+\r
+#ifdef KEEN6\r
+               if (groundslam)\r
+               {\r
+                       if ((groundslam = groundslam - tics) < 0)\r
+                               groundslam = 0;\r
+               }\r
+#endif\r
+//\r
+// single step debug mode\r
+//\r
+               if (singlestep)\r
+               {\r
+                       VW_WaitVBL(14); //reduces framerate to 5 fps on VGA or 4.3 fps on EGA cards\r
+                       lasttimecount = TimeCount;\r
+               }\r
+//\r
+// extra VBLs debug mode\r
+//\r
+               if (extravbls)\r
+               {\r
+                       VW_WaitVBL(extravbls);\r
+               }\r
+\r
+//\r
+// handle user inputs\r
+//\r
+               if (DemoMode == demo_Playback)\r
+               {\r
+                       if (!screenfaded && IN_IsUserInput())\r
+                       {\r
+                               playstate = ex_completed;\r
+                               if (LastScan != sc_F1)\r
+                               {\r
+                                       LastScan = sc_Space;\r
+                               }\r
+                       }\r
+               }\r
+               else if (DemoMode == demo_PlayDone)\r
+               {\r
+                       playstate = ex_completed;\r
+               }\r
+               else\r
+               {\r
+                       CheckKeys();\r
+               }\r
+\r
+//\r
+// E-N-D cheat\r
+//\r
+               if (Keyboard[sc_E] && Keyboard[sc_N] && Keyboard[sc_D])\r
+               {\r
+#if defined KEEN4\r
+                       gamestate.rescued = 7;\r
+                       playstate = ex_rescued;\r
+#elif defined KEEN5\r
+                       playstate = ex_qedbroke;\r
+#elif defined KEEN6\r
+                       playstate = ex_molly;\r
+#endif\r
+               }\r
+\r
+       } while (playstate == ex_stillplaying);\r
+\r
+       ingame = false;\r
+       StopMusic();\r
+}
\ No newline at end of file
diff --git a/16/keen456/KEEN4-6/CK_STATE.C b/16/keen456/KEEN4-6/CK_STATE.C
new file mode 100755 (executable)
index 0000000..25d3be6
--- /dev/null
@@ -0,0 +1,1968 @@
+/* Reconstructed Commander Keen 4-6 Source Code\r
+ * Copyright (C) 2021 K1n9_Duk3\r
+ *\r
+ * This file is loosely based on:\r
+ * Keen Dreams Source Code\r
+ * Copyright (C) 2014 Javier M. Chavez\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License along\r
+ * with this program; if not, write to the Free Software Foundation, Inc.,\r
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+ */\r
+\r
+#include "CK_DEF.H"\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                GLOBAL VARIABLES\r
+\r
+=============================================================================\r
+*/\r
+\r
+Sint16 wallclip[8][16] = {                     // the height of a given point in a tile\r
+{ 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256},\r
+{   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0},\r
+{   0,0x08,0x10,0x18,0x20,0x28,0x30,0x38,0x40,0x48,0x50,0x58,0x60,0x68,0x70,0x78},\r
+{0x80,0x88,0x90,0x98,0xa0,0xa8,0xb0,0xb8,0xc0,0xc8,0xd0,0xd8,0xe0,0xe8,0xf0,0xf8},\r
+{   0,0x10,0x20,0x30,0x40,0x50,0x60,0x70,0x80,0x90,0xa0,0xb0,0xc0,0xd0,0xe0,0xf0},\r
+{0x78,0x70,0x68,0x60,0x58,0x50,0x48,0x40,0x38,0x30,0x28,0x20,0x18,0x10,0x08,   0},\r
+{0xf8,0xf0,0xe8,0xe0,0xd8,0xd0,0xc8,0xc0,0xb8,0xb0,0xa8,0xa0,0x98,0x90,0x88,0x80},\r
+{0xf0,0xe0,0xd0,0xc0,0xb0,0xa0,0x90,0x80,0x70,0x60,0x50,0x40,0x30,0x20,0x10,   0}\r
+};\r
+\r
+Sint16 xtry, ytry;\r
+boolean playerkludgeclipcancel;\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                LOCAL VARIABLES\r
+\r
+=============================================================================\r
+*/\r
+\r
+Uint16 oldtileleft, oldtiletop, oldtileright, oldtilebottom, oldtilemidx;\r
+Uint16 oldleft, oldtop, oldright, oldbottom, oldmidx;\r
+Sint16 leftmoved, topmoved, rightmoved, bottommoved, midxmoved;\r
+\r
+//==========================================================================\r
+\r
+/*\r
+====================\r
+=\r
+= MoveObjVert\r
+=\r
+====================\r
+*/\r
+\r
+void MoveObjVert(objtype *ob, Sint16 ymove)\r
+{\r
+       ob->y += ymove;\r
+       ob->top += ymove;\r
+       ob->bottom += ymove;\r
+       ob->tiletop = CONVERT_GLOBAL_TO_TILE(ob->top);\r
+       ob->tilebottom = CONVERT_GLOBAL_TO_TILE(ob->bottom);\r
+}\r
+\r
+/*\r
+====================\r
+=\r
+= MoveObjHoriz\r
+=\r
+====================\r
+*/\r
+\r
+void MoveObjHoriz(objtype *ob, Sint16 xmove)\r
+{\r
+       //BUG? ob->midx is not adjusted in Keen 4 & 5\r
+       ob->x += xmove;\r
+       ob->left += xmove;\r
+       ob->right += xmove;\r
+#ifdef KEEN6\r
+       ob->midx += xmove;      //BUG? ob->tilemidx is not updated\r
+#endif\r
+       ob->tileleft = CONVERT_GLOBAL_TO_TILE(ob->left);\r
+       ob->tileright = CONVERT_GLOBAL_TO_TILE(ob->right);\r
+}\r
+\r
+//==========================================================================\r
+\r
+/*\r
+====================\r
+=\r
+= PlayerBottomKludge\r
+=\r
+====================\r
+*/\r
+\r
+void PlayerBottomKludge(objtype *ob)\r
+{\r
+       Uint16 far *map;\r
+       Uint16 wall, clip, xpix;\r
+       Sint16 xmove, ymove;\r
+\r
+       map = (Uint16 far *)mapsegs[1] + mapbwidthtable[ob->tilebottom-1]/2;\r
+       if (ob->xdir == 1)\r
+       {\r
+               xpix = 0;\r
+               map += ob->tileright;\r
+               xmove = ob->right - ob->midx;\r
+               if (tinf[*(map-mapwidth)+WESTWALL] || tinf[*map+WESTWALL])\r
+               {\r
+                       return;\r
+               }\r
+       }\r
+       else\r
+       {\r
+               xpix = 15;\r
+               map += ob->tileleft;\r
+               xmove = ob->left - ob->midx;\r
+               if (tinf[*(map-mapwidth)+EASTWALL] || tinf[*map+EASTWALL])\r
+               {\r
+                       return;\r
+               }\r
+       }\r
+       if ((_AX = tinf[*map+NORTHWALL]) != 0)  // the _AX = ... part is just to recreate the original code's quirks, feel free to delete this\r
+       {\r
+               return;\r
+       }\r
+       map += mapwidth;\r
+       if ((wall = tinf[*map+NORTHWALL]) != 1)\r
+       {\r
+               return;\r
+       }\r
+       clip = wallclip[wall&7][xpix];\r
+       ymove = CONVERT_TILE_TO_GLOBAL(ob->tilebottom) + clip - 1 -ob->bottom;\r
+       if (ymove <= 0 && ymove >= -bottommoved)\r
+       {\r
+               ob->hitnorth = wall;\r
+               MoveObjVert(ob, ymove);\r
+               MoveObjHoriz(ob, xmove);\r
+       }\r
+}\r
+\r
+/*\r
+====================\r
+=\r
+= PlayerTopKludge\r
+=\r
+====================\r
+*/\r
+\r
+void PlayerTopKludge(objtype *ob)\r
+{\r
+       Uint16 far *map;\r
+       Uint16 xpix, wall, clip;\r
+       Sint16 move;\r
+\r
+       map = (Uint16 far *)mapsegs[1] + mapbwidthtable[ob->tiletop+1]/2;\r
+       if (ob->xdir == 1)\r
+       {\r
+               xpix = 0;\r
+               map += ob->tileright;\r
+               if (tinf[*(map+mapwidth)+WESTWALL] || tinf[*(map+2*mapwidth)+WESTWALL])\r
+               {\r
+                       return;\r
+               }\r
+       }\r
+       else\r
+       {\r
+               xpix = 15;\r
+               map += ob->tileleft;\r
+               if (tinf[*(map+mapwidth)+EASTWALL] || tinf[*(map+2*mapwidth)+EASTWALL])\r
+               {\r
+                       return;\r
+               }\r
+       }\r
+       if ((_AX = tinf[*map+SOUTHWALL]) != 0)  // the _AX = ... part is just to recreate the original code's quirks, feel free to delete this\r
+       {\r
+               return;\r
+       }\r
+       map -= mapwidth;\r
+       if ((wall = tinf[*map+SOUTHWALL]) != 0)\r
+       {\r
+               clip = wallclip[wall&7][xpix];\r
+               move = CONVERT_TILE_TO_GLOBAL(ob->tiletop+1) - clip - ob->top;\r
+               if (move >= 0 && move <= -topmoved)\r
+               {\r
+                       ob->hitsouth = wall;\r
+                       MoveObjVert(ob, move);\r
+               }\r
+       }\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= ClipToEnds\r
+=\r
+===========================\r
+*/\r
+\r
+void ClipToEnds(objtype *ob)\r
+{\r
+       Uint16 far *map;\r
+       Uint16 wall, y, clip;\r
+       Sint16 totalmove, maxmove, move;\r
+       Uint16 midxpix;\r
+       \r
+       midxpix = CONVERT_GLOBAL_TO_PIXEL(ob->midx & 0xF0);\r
+       maxmove = -abs(midxmoved)-bottommoved-16;\r
+       map = (Uint16 far *)mapsegs[1] + (mapbwidthtable-1)[oldtilebottom]/2 + ob->tilemidx;\r
+       for (y=oldtilebottom-1; y <= ob->tilebottom; y++,map+=mapwidth)\r
+       {\r
+               if ((wall = tinf[*map + NORTHWALL]) != 0)\r
+               {\r
+                       clip = wallclip[wall&7][midxpix];\r
+                       move = (CONVERT_TILE_TO_GLOBAL(y) + clip)-1-ob->bottom;\r
+                       if (move < 0 && move >= maxmove)\r
+                       {\r
+                               ob->hitnorth = wall;\r
+                               MoveObjVert(ob, move);\r
+                               return;\r
+                       }\r
+               }\r
+       }\r
+       maxmove = abs(midxmoved)-topmoved+16;\r
+       map = (Uint16 far *)mapsegs[1] + (mapbwidthtable+1)[oldtiletop]/2 + ob->tilemidx;\r
+       for (y=oldtiletop+1; y >= ob->tiletop; y--,map-=mapwidth)       // BUG: unsigned comparison - loop never ends if ob->tiletop is 0\r
+       {\r
+               if ((wall = tinf[*map + SOUTHWALL]) != 0)\r
+               {\r
+                       clip = wallclip[wall&7][midxpix];\r
+                       move = CONVERT_TILE_TO_GLOBAL(y+1) - clip - ob->top;\r
+                       if (move > 0 && move <= maxmove)\r
+                       {\r
+                               totalmove = ytry+move;\r
+                               if (totalmove < 0x100 && totalmove > -0x100)\r
+                               {\r
+                                       ob->hitsouth = wall;\r
+                                       MoveObjVert(ob, move);\r
+                                       //BUG? no return here\r
+                               }\r
+                       }\r
+               }\r
+       }\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= ClipToSides\r
+=\r
+===========================\r
+*/\r
+\r
+void ClipToSides(objtype *ob)\r
+{\r
+       Sint16 move, y, top, bottom;\r
+       Uint16 far *map;\r
+       \r
+       top = ob->tiletop;\r
+       if (ob->hitsouth > 1)\r
+       {\r
+               top++;\r
+       }\r
+       bottom = ob->tilebottom;\r
+       if (ob->hitnorth > 1)\r
+       {\r
+               bottom--;\r
+       }\r
+       for (y=top; y<=bottom; y++)\r
+       {\r
+               map = (Uint16 far *)mapsegs[1] + mapbwidthtable[y]/2 + ob->tileleft;\r
+               if ((ob->hiteast = tinf[*map+EASTWALL]) != 0)\r
+               {\r
+                       move = CONVERT_TILE_TO_GLOBAL(ob->tileleft+1) - ob->left;\r
+                       MoveObjHoriz(ob, move);\r
+                       return;\r
+               }\r
+       }\r
+       for (y=top; y<=bottom; y++)\r
+       {\r
+               map = (Uint16 far *)mapsegs[1] + mapbwidthtable[y]/2 + ob->tileright;\r
+               if ((ob->hitwest = tinf[*map+WESTWALL]) != 0)\r
+               {\r
+                       move = (CONVERT_TILE_TO_GLOBAL(ob->tileright)-1)-ob->right;\r
+                       MoveObjHoriz(ob, move);\r
+                       return;\r
+               }\r
+       }\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= CheckPosition\r
+=\r
+===========================\r
+*/\r
+\r
+boolean CheckPosition(objtype *ob)\r
+{\r
+#ifdef KEEN6Ev15\r
+       // This version is pretty much a compiler-optimized version of the other\r
+       // version below, but I simply could not get the compiler to optimize it\r
+       // in exactly the same way.\r
+\r
+       Uint16 tile, x, tileright;\r
+       Uint16 far *map;\r
+       Uint16 rowdiff;\r
+       Uint16 tileleft, y, tilebottom;\r
+       \r
+       map = (Uint16 far *)mapsegs[1] + mapbwidthtable[ob->tiletop]/2 + ob->tileleft;\r
+       rowdiff = mapwidth-(ob->tileright-ob->tileleft+1);\r
+\r
+       y = ob->tiletop;\r
+       tileleft = ob->tileleft;\r
+       tileright = _AX = ob->tileright;\r
+       tilebottom = ob->tilebottom;\r
+\r
+       for (; tilebottom>=y; y++,map+=rowdiff)\r
+       {\r
+               for (x=tileleft; tileright>=x; x++)\r
+               {\r
+                       tile = *(map++);\r
+                       if (tinf[tile+NORTHWALL] || tinf[tile+EASTWALL] || tinf[tile+SOUTHWALL] || tinf[tile+WESTWALL])\r
+                       {\r
+                               return false;\r
+                       }\r
+               }\r
+       }\r
+       return true;\r
+#else\r
+       Uint16 tile, x, y;\r
+       Uint16 far *map;\r
+       Uint16 rowdiff;\r
+       \r
+       map = (Uint16 far *)mapsegs[1] + mapbwidthtable[ob->tiletop]/2 + ob->tileleft;\r
+       rowdiff = mapwidth-(ob->tileright-ob->tileleft+1);\r
+       for (y=ob->tiletop; y<=ob->tilebottom; y++,map+=rowdiff)\r
+       {\r
+               for (x=ob->tileleft; x<=ob->tileright; x++)\r
+               {\r
+                       tile = *(map++);\r
+                       if (tinf[tile+NORTHWALL] || tinf[tile+EASTWALL] || tinf[tile+SOUTHWALL] || tinf[tile+WESTWALL])\r
+                       {\r
+                               return false;\r
+                       }\r
+               }\r
+       }\r
+       return true;\r
+#endif\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= StatePositionOk\r
+=\r
+===========================\r
+*/\r
+\r
+boolean StatePositionOk(objtype *ob, statetype *state)\r
+{\r
+       spritetabletype far *shape;\r
+\r
+       if (ob->xdir > 0)\r
+       {\r
+               ob->shapenum = state->rightshapenum;\r
+       }\r
+       else\r
+       {\r
+               ob->shapenum = state->leftshapenum;\r
+       }\r
+       shape = &spritetable[ob->shapenum-STARTSPRITES];\r
+       ob->left = ob->x + shape->xl;\r
+       ob->right = ob->x + shape->xh;\r
+       ob->top = ob->y + shape->yl;\r
+       ob->bottom = ob->y + shape->yh;\r
+       ob->midx = ob->left + (ob->right-ob->left)/2;\r
+       ob->tileleft = CONVERT_GLOBAL_TO_TILE(ob->left);\r
+       ob->tileright = CONVERT_GLOBAL_TO_TILE(ob->right);\r
+       ob->tiletop = CONVERT_GLOBAL_TO_TILE(ob->top);\r
+       ob->tilebottom = CONVERT_GLOBAL_TO_TILE(ob->bottom);\r
+       ob->tilemidx = CONVERT_GLOBAL_TO_TILE(ob->midx);\r
+       return CheckPosition(ob);\r
+}\r
+\r
+#ifdef KEEN5\r
+/*\r
+===========================\r
+=\r
+= CalcBounds\r
+=\r
+===========================\r
+*/\r
+\r
+void CalcBounds(objtype *ob)   //not present in Keen 4 & 6\r
+{\r
+       spritetabletype far *shape;\r
+\r
+       shape = &spritetable[ob->shapenum-STARTSPRITES];\r
+       ob->left = ob->x + shape->xl;\r
+       ob->right = ob->x + shape->xh;\r
+       ob->top = ob->y + shape->yl;\r
+       ob->bottom = ob->y + shape->yh;\r
+       ob->midx = ob->left + (ob->right-ob->left)/2;\r
+}\r
+#endif\r
+\r
+//==========================================================================\r
+\r
+/*\r
+================\r
+=\r
+= ClipToWalls\r
+=\r
+= Moves the current object xtry/ytry units, clipping to walls\r
+=\r
+================\r
+*/\r
+\r
+void ClipToWalls(objtype *ob)\r
+{\r
+       Uint16 oldx, oldy;\r
+#ifdef KEEN6\r
+       Uint16 y;\r
+#endif\r
+       spritetabletype far *shape;\r
+       boolean pushed;\r
+\r
+       oldx = ob->x;\r
+       oldy = ob->y;\r
+       pushed = false;\r
+\r
+//\r
+// make sure it stays in contact with a 45 degree slope\r
+//\r
+       if (ob->state->pushtofloor)\r
+       {\r
+               if (ob->hitnorth == 25)\r
+               {\r
+                       ytry = 145;\r
+               }\r
+               else\r
+               {\r
+                       if (xtry > 0)\r
+                       {\r
+                               ytry = xtry+16;\r
+                       }\r
+                       else\r
+                       {\r
+                               ytry = -xtry+16;\r
+                       }\r
+                       pushed = true;\r
+               }\r
+       }\r
+\r
+//\r
+// move the shape\r
+//\r
+       if (xtry > 239)\r
+       {\r
+               xtry = 239;\r
+       }\r
+       else if (xtry < -239)\r
+       {\r
+               xtry = -239;\r
+       }\r
+       if (ytry > 255)                 // +16 for push to floor\r
+       {\r
+               ytry = 255;\r
+       }\r
+       else if (ytry < -239)\r
+       {\r
+               ytry = -239;\r
+       }\r
+\r
+       ob->x += xtry;\r
+       ob->y += ytry;\r
+\r
+       ob->needtoreact = true;\r
+\r
+       if (!ob->shapenum)                              // can't get a hit rect with no shape!\r
+       {\r
+               return;\r
+       }\r
+\r
+       shape = &spritetable[ob->shapenum-STARTSPRITES];\r
+\r
+       oldtileright = ob->tileright;\r
+       oldtiletop = ob->tiletop;\r
+       oldtileleft = ob->tileleft;\r
+       oldtilebottom = ob->tilebottom;\r
+       oldtilemidx = ob->tilemidx;\r
+\r
+       oldright = ob->right;\r
+       oldtop = ob->top;\r
+       oldleft = ob->left;\r
+       oldbottom = ob->bottom;\r
+       oldmidx = ob->midx;\r
+\r
+       ob->left = ob->x + shape->xl;\r
+       ob->right = ob->x + shape->xh;\r
+       ob->top = ob->y + shape->yl;\r
+       ob->bottom = ob->y + shape->yh;\r
+       ob->midx = ob->left + (ob->right-ob->left)/2;\r
+\r
+       ob->tileleft = CONVERT_GLOBAL_TO_TILE(ob->left);\r
+       ob->tileright = CONVERT_GLOBAL_TO_TILE(ob->right);\r
+       ob->tiletop = CONVERT_GLOBAL_TO_TILE(ob->top);\r
+       ob->tilebottom = CONVERT_GLOBAL_TO_TILE(ob->bottom);\r
+       ob->tilemidx = CONVERT_GLOBAL_TO_TILE(ob->midx);\r
+\r
+       ob->hitnorth=ob->hiteast=ob->hitsouth=ob->hitwest=0;\r
+\r
+       if (ob->needtoclip)\r
+       {\r
+               leftmoved = ob->left - oldleft;\r
+               rightmoved = ob->right - oldright;\r
+               topmoved = ob->top - oldtop;\r
+               bottommoved = ob->bottom - oldbottom;\r
+               midxmoved = ob->midx - oldmidx;\r
+\r
+       //\r
+       // clip it\r
+       //\r
+               ClipToEnds(ob);\r
+\r
+               if (ob == player && !playerkludgeclipcancel)    // zero tolerance near the edge when player gets pushed!\r
+               {\r
+                       if (!ob->hitnorth && bottommoved > 0)\r
+                       {\r
+                               PlayerBottomKludge(ob);\r
+                       }\r
+                       if (!ob->hitsouth && topmoved < 0)\r
+                       {\r
+                               PlayerTopKludge(ob);\r
+                       }\r
+               }\r
+               ClipToSides(ob);\r
+\r
+#ifdef KEEN6\r
+               //\r
+               // special hack to prevent player from getting stuck on slopes?\r
+               //\r
+               if (ob == player && (ob->hitnorth & 7) > 1 && (ob->hiteast || ob->hitwest))\r
+               {\r
+                       Uint16 far *map;\r
+                       Uint16 pixx, clip, move;\r
+                       Uint16 wall;\r
+\r
+                       pixx = CONVERT_GLOBAL_TO_PIXEL(ob->midx & (15*PIXGLOBAL));\r
+                       map = (Uint16 far *)mapsegs[1] + mapbwidthtable[oldtilebottom]/2 + ob->tilemidx;\r
+\r
+                       for (y=oldtilebottom; ob->tilebottom+1 >= y; y++, map+=mapwidth)\r
+                       {\r
+                               if ((wall = tinf[*map + NORTHWALL]) != 0)\r
+                               {\r
+                                       clip = wallclip[wall & 7][pixx];\r
+                                       move = CONVERT_TILE_TO_GLOBAL(y) + clip - 1 - ob->bottom;\r
+                                       ob->hitnorth = wall;\r
+                                       MoveObjVert(ob, move);\r
+                                       return;\r
+                               }\r
+                       }\r
+               }\r
+#endif\r
+       }\r
+       if (pushed && !ob->hitnorth)\r
+       {\r
+               ob->y = oldy;\r
+               ob->x = oldx + xtry;\r
+\r
+               ob->left = ob->x + shape->xl;\r
+               ob->right = ob->x + shape->xh;\r
+               ob->top = ob->y + shape->yl;\r
+               ob->bottom = ob->y + shape->yh;\r
+               ob->midx = ob->left + (ob->right-ob->left)/2;\r
+\r
+               ob->tileleft = CONVERT_GLOBAL_TO_TILE(ob->left);\r
+               ob->tileright = CONVERT_GLOBAL_TO_TILE(ob->right);\r
+               ob->tiletop = CONVERT_GLOBAL_TO_TILE(ob->top);\r
+               ob->tilebottom = CONVERT_GLOBAL_TO_TILE(ob->bottom);\r
+               ob->tilemidx = CONVERT_GLOBAL_TO_TILE(ob->midx);\r
+       }\r
+\r
+       ob->xmove = ob->xmove + (ob->x - oldx);\r
+       ob->ymove = ob->ymove + (ob->y - oldy);\r
+}\r
+\r
+/*\r
+================\r
+=\r
+= FullClipToWalls\r
+=\r
+= Moves the current object xtry/ytry units, clipping to walls\r
+=\r
+================\r
+*/\r
+\r
+void FullClipToWalls(objtype *ob)\r
+{\r
+       Uint16 oldx, oldy, w, h;\r
+       spritetabletype far *shape;\r
+\r
+       oldx = ob->x;\r
+       oldy = ob->y;\r
+\r
+//\r
+// move the shape\r
+//\r
+       if (xtry > 239)\r
+       {\r
+               xtry = 239;\r
+       }\r
+       else if (xtry < -239)\r
+       {\r
+               xtry = -239;\r
+       }\r
+       if (ytry > 239)\r
+       {\r
+               ytry = 239;\r
+       }\r
+       else if (ytry < -239)\r
+       {\r
+               ytry = -239;\r
+       }\r
+\r
+       ob->x += xtry;\r
+       ob->y += ytry;\r
+\r
+       ob->needtoreact = true;\r
+\r
+       shape = &spritetable[ob->shapenum-STARTSPRITES];\r
+\r
+       switch (ob->obclass)\r
+       {\r
+#if defined KEEN4\r
+       case keenobj:\r
+               w = 40*PIXGLOBAL;\r
+               h = 24*PIXGLOBAL;\r
+               break;\r
+       case eggbirdobj:\r
+               w = 64*PIXGLOBAL;\r
+               h = 32*PIXGLOBAL;\r
+               break;\r
+       case dopefishobj:\r
+               w = 88*PIXGLOBAL;\r
+               h = 64*PIXGLOBAL;\r
+               break;\r
+       case schoolfishobj:\r
+               w = 16*PIXGLOBAL;\r
+               h = 8*PIXGLOBAL;\r
+               break;\r
+#elif defined KEEN5\r
+       case slicestarobj:\r
+       case spherefulobj:\r
+               w = h = 32*PIXGLOBAL;\r
+               break;\r
+#elif defined KEEN6\r
+       case blorbobj:\r
+               w = h = 32*PIXGLOBAL;\r
+               break;\r
+#endif\r
+\r
+       default:\r
+               Quit("FullClipToWalls: Bad obclass");\r
+               break;\r
+       }\r
+\r
+       ob->right = ob->x + w;\r
+       ob->left = ob->x;\r
+       ob->top = ob->y;\r
+       ob->bottom = ob->y + h;\r
+\r
+       ob->tileleft = CONVERT_GLOBAL_TO_TILE(ob->left);\r
+       ob->tileright = CONVERT_GLOBAL_TO_TILE(ob->right);\r
+       ob->tiletop = CONVERT_GLOBAL_TO_TILE(ob->top);\r
+       ob->tilebottom = CONVERT_GLOBAL_TO_TILE(ob->bottom);\r
+\r
+       ob->hitnorth=ob->hiteast=ob->hitsouth=ob->hitwest=0;\r
+\r
+//\r
+// clip it\r
+//\r
+       if (!CheckPosition(ob))\r
+       {\r
+               MoveObjHoriz(ob, -xtry);        //undo x movement\r
+               if (CheckPosition(ob))\r
+               {\r
+                       if (xtry > 0)\r
+                       {\r
+                               ob->hitwest = 1;\r
+                       }\r
+                       else\r
+                       {\r
+                               ob->hiteast = 1;\r
+                       }\r
+               }\r
+               else\r
+               {\r
+                       if (ytry > 0)\r
+                       {\r
+                               ob->hitnorth = 1;\r
+                       }\r
+                       else\r
+                       {\r
+                               ob->hitsouth = 1;\r
+                       }\r
+                       MoveObjHoriz(ob, xtry); //redo x movement\r
+                       MoveObjVert(ob, -ytry); //undo y movement\r
+                       if (!CheckPosition(ob))\r
+                       {\r
+                               MoveObjHoriz(ob, -xtry);        //undo x movement\r
+                               if (xtry > 0)\r
+                               {\r
+                                       ob->hitwest = 1;\r
+                               }\r
+                               else\r
+                               {\r
+                                       ob->hiteast = 1;\r
+                               }\r
+                       }\r
+               }\r
+       }\r
+\r
+       ob->xmove = ob->xmove + (ob->x - oldx);\r
+       ob->ymove = ob->ymove + (ob->y - oldy);\r
+\r
+       ob->left = ob->x + shape->xl;\r
+       ob->right = ob->x + shape->xh;\r
+       ob->top = ob->y + shape->yl;\r
+       ob->bottom = ob->y + shape->yh;\r
+       ob->midx = ob->left + (ob->right-ob->left)/2;\r
+}\r
+\r
+/*\r
+================\r
+=\r
+= PushObj\r
+=\r
+= Moves the current object xtry/ytry units, clipping to walls\r
+=\r
+================\r
+*/\r
+\r
+void PushObj(objtype *ob)\r
+{\r
+       Uint16 oldx, oldy;\r
+       spritetabletype far *shape;\r
+       \r
+       oldx = ob->x;\r
+       oldy = ob->y;\r
+\r
+//\r
+// move the shape\r
+//\r
+       ob->x += xtry;\r
+       ob->y += ytry;\r
+\r
+       ob->needtoreact = true;\r
+\r
+       if (!ob->shapenum)                              // can't get a hit rect with no shape!\r
+       {\r
+               return;\r
+       }\r
+\r
+       shape = &spritetable[ob->shapenum-STARTSPRITES];\r
+\r
+       oldtileright = ob->tileright;\r
+       oldtiletop = ob->tiletop;\r
+       oldtileleft = ob->tileleft;\r
+       oldtilebottom = ob->tilebottom;\r
+       oldtilemidx = ob->tilemidx;\r
+\r
+       oldright = ob->right;\r
+       oldtop = ob->top;\r
+       oldleft = ob->left;\r
+       oldbottom = ob->bottom;\r
+       oldmidx = ob->midx;\r
+\r
+       ob->left = ob->x + shape->xl;\r
+       ob->right = ob->x + shape->xh;\r
+       ob->top = ob->y + shape->yl;\r
+       ob->bottom = ob->y + shape->yh;\r
+       ob->midx = ob->left + (ob->right-ob->left)/2;\r
+\r
+       ob->tileleft = CONVERT_GLOBAL_TO_TILE(ob->left);\r
+       ob->tileright = CONVERT_GLOBAL_TO_TILE(ob->right);\r
+       ob->tiletop = CONVERT_GLOBAL_TO_TILE(ob->top);\r
+       ob->tilebottom = CONVERT_GLOBAL_TO_TILE(ob->bottom);\r
+       ob->tilemidx = CONVERT_GLOBAL_TO_TILE(ob->midx);\r
+\r
+       if (ob->needtoclip)\r
+       {\r
+               leftmoved = ob->left - oldleft;\r
+               rightmoved = ob->right - oldright;\r
+               topmoved = ob->top - oldtop;\r
+               bottommoved = ob->bottom - oldbottom;\r
+               midxmoved = ob->midx - oldmidx;\r
+\r
+               ClipToEnds(ob);\r
+               ClipToSides(ob);\r
+       }\r
+\r
+       ob->xmove = ob->xmove + (ob->x - oldx);\r
+       ob->ymove = ob->ymove + (ob->y - oldy);\r
+}\r
+\r
+//==========================================================================\r
+\r
+\r
+/*\r
+==================\r
+=\r
+= ClipToSpriteSide\r
+=\r
+= Clips push to solid\r
+=\r
+==================\r
+*/\r
+\r
+void ClipToSpriteSide(objtype *push, objtype *solid)\r
+{\r
+       Sint16 xmove, leftinto, rightinto;\r
+\r
+       //\r
+       // amount the push shape can be pushed\r
+       //\r
+       xmove = solid->xmove - push->xmove;\r
+\r
+       //\r
+       // amount it is inside\r
+       //\r
+       leftinto = solid->right - push->left;\r
+       rightinto = push->right - solid->left;\r
+\r
+       if (leftinto > 0 && leftinto <= xmove)\r
+       {\r
+               xtry = leftinto;\r
+               if (push->state->pushtofloor)\r
+               {\r
+                       ytry = leftinto+16;\r
+               }\r
+               ClipToWalls(push);\r
+               push->hiteast = 1;\r
+       }\r
+       else if (rightinto > 0 && rightinto <= -xmove)\r
+       {\r
+               xtry = -rightinto;\r
+               if (push->state->pushtofloor)\r
+               {\r
+                       ytry = rightinto+16;\r
+               }\r
+               ClipToWalls(push);\r
+               push->hitwest = 1;\r
+       }\r
+}\r
+\r
+//==========================================================================\r
+\r
+\r
+/*\r
+==================\r
+=\r
+= ClipToSpriteTop\r
+=\r
+= Clips push to solid\r
+=\r
+==================\r
+*/\r
+\r
+void ClipToSpriteTop(objtype *push, objtype *solid)\r
+{\r
+       Sint16 temp, ymove, bottominto;\r
+\r
+       //\r
+       // amount the push shape can be pushed\r
+       //\r
+       ymove = push->ymove - solid->ymove;\r
+\r
+       //\r
+       // amount it is inside\r
+       //\r
+       bottominto = push->bottom - solid->top;\r
+\r
+       if (bottominto >= 0 && bottominto <= ymove)\r
+       {\r
+               if (push == player)\r
+               {\r
+                       gamestate.riding = solid;\r
+               }\r
+               ytry = -bottominto;\r
+               temp = push->state->pushtofloor;\r
+               push->state->pushtofloor = false;\r
+               ClipToWalls(push);\r
+               push->state->pushtofloor = temp;\r
+               if (!push->hitsouth)\r
+               {\r
+                       push->hitnorth = 25;\r
+               }\r
+       }\r
+}\r
+\r
+//==========================================================================\r
+\r
+\r
+/*\r
+==================\r
+=\r
+= ClipToSprite\r
+=\r
+= Clips push to solid\r
+=\r
+==================\r
+*/\r
+\r
+void ClipToSprite(objtype *push, objtype *solid, boolean squish)\r
+{\r
+       Sint16 xmove, ymove, leftinto, rightinto, topinto, bottominto;\r
+       \r
+       xmove = solid->xmove - push->xmove;\r
+       xtry = ytry = 0;\r
+\r
+       //\r
+       // left / right\r
+       //\r
+       leftinto = solid->right - push->left;\r
+       rightinto = push->right - solid->left;\r
+\r
+       if (leftinto > 0 && xmove+1 >= leftinto)\r
+       {\r
+               xtry = leftinto;\r
+               push->xspeed = 0;\r
+               PushObj(push);\r
+               if (squish && push->hitwest)\r
+               {\r
+                       KillKeen();\r
+               }\r
+               push->hiteast = 1;\r
+               return;\r
+       }\r
+       else if (rightinto > 0 && -xmove+1 >= rightinto)\r
+       {\r
+               xtry = -rightinto;\r
+               push->xspeed = 0;\r
+               PushObj(push);\r
+               if (squish && push->hiteast)\r
+               {\r
+                       KillKeen();\r
+               }\r
+               push->hitwest = 1;\r
+               return;\r
+       }\r
+\r
+       //\r
+       // top / bottom\r
+       //\r
+       ymove = push->ymove - solid->ymove;\r
+       topinto = solid->bottom - push->top;\r
+       bottominto = push->bottom - solid->top;\r
+       if (bottominto >= 0 && bottominto <= ymove)\r
+       {\r
+               if (push == player)\r
+               {\r
+                       gamestate.riding = solid;\r
+               }\r
+               ytry = -bottominto;\r
+               PushObj(push);\r
+               if (squish && push->hitsouth)\r
+               {\r
+                       KillKeen();\r
+               }\r
+               if (!push->hitsouth)\r
+               {\r
+                       push->hitnorth = 25;\r
+               }\r
+               return;\r
+       }\r
+       else if (topinto >= 0 && topinto <= ymove)      // BUG: should be 'topinto <= -ymove'\r
+       {\r
+               ytry = topinto;\r
+               ClipToWalls(push);\r
+               if (squish && push->hitnorth)\r
+               {\r
+                       KillKeen();\r
+               }\r
+               push->hitsouth = 25;\r
+       }\r
+}\r
+\r
+//==========================================================================\r
+\r
+\r
+/*\r
+==================\r
+=\r
+= DoActor\r
+=\r
+= Moves an actor in its current state by a given number of tics.\r
+= If that time takes it into the next state, it changes the state\r
+= and returns the number of excess tics after the state change\r
+=\r
+==================\r
+*/\r
+\r
+Sint16 DoActor(objtype *ob, Sint16 numtics)\r
+{\r
+       Sint16 ticcount, usedtics, excesstics;\r
+       statetype *state;\r
+       \r
+       state = ob->state;\r
+\r
+       if (state->progress == think)\r
+       {\r
+               if (state->think)\r
+               {\r
+                       if (ob->nothink)\r
+                       {\r
+                               ob->nothink--;\r
+                       }\r
+                       else\r
+                       {\r
+                               state->think(ob);\r
+                       }\r
+               }\r
+               return 0;\r
+       }\r
+\r
+       ticcount = ob->ticcount + numtics;\r
+\r
+       if (state->tictime > ticcount || state->tictime == 0)\r
+       {\r
+               ob->ticcount = ticcount;\r
+               if (state->progress == slide || state->progress == slidethink)\r
+               {\r
+                       if (ob->xdir)\r
+                       {\r
+                               xtry += ob->xdir == 1? numtics*state->xmove : -numtics*state->xmove;\r
+                       }\r
+                       if (ob->ydir)\r
+                       {\r
+                               ytry += ob->ydir == 1? numtics*state->ymove : -numtics*state->ymove;\r
+                       }\r
+               }\r
+               if ((state->progress == slidethink || state->progress == stepthink) && state->think)\r
+               {\r
+                       if (ob->nothink)\r
+                       {\r
+                               ob->nothink--;\r
+                       }\r
+                       else\r
+                       {\r
+                               state->think(ob);\r
+                       }\r
+               }\r
+               return 0;\r
+       }\r
+       else\r
+       {\r
+               usedtics = state->tictime - ob->ticcount;\r
+               excesstics = ticcount - state->tictime;\r
+               ob->ticcount = 0;\r
+               if (state->progress == slide || state->progress == slidethink)\r
+               {\r
+                       if (ob->xdir)\r
+                       {\r
+                               xtry += ob->xdir == 1? usedtics*state->xmove : -usedtics*state->xmove;\r
+                       }\r
+                       if (ob->ydir)\r
+                       {\r
+                               ytry += ob->ydir == 1? usedtics*state->ymove : -usedtics*state->ymove;\r
+                       }\r
+               }\r
+               else\r
+               {\r
+                       if (ob->xdir)\r
+                       {\r
+                               xtry += ob->xdir == 1? state->xmove : -state->xmove;\r
+                       }\r
+                       if (ob->ydir)\r
+                       {\r
+                               ytry += ob->ydir == 1? state->ymove : -state->ymove;\r
+                       }\r
+               }\r
+\r
+               if (state->think)\r
+               {\r
+                       if (ob->nothink)\r
+                       {\r
+                               ob->nothink--;\r
+                       }\r
+                       else\r
+                       {\r
+                               state->think(ob);\r
+                       }\r
+               }\r
+\r
+               if (state == ob->state)\r
+               {\r
+                       ob->state = state->nextstate;   // go to next state\r
+               }\r
+               else if (!ob->state)\r
+               {\r
+                       return 0;                       // object removed itself\r
+               }\r
+               return excesstics;\r
+       }\r
+}\r
+\r
+//==========================================================================\r
+\r
+\r
+/*\r
+====================\r
+=\r
+= StateMachine\r
+=\r
+= Change state and give directions\r
+=\r
+====================\r
+*/\r
+\r
+void StateMachine(objtype *ob)\r
+{\r
+       Sint16 excesstics, oldshapenum;\r
+       statetype *state;\r
+       \r
+       ob->xmove=ob->ymove=xtry=ytry=0;\r
+       oldshapenum = ob->shapenum;\r
+\r
+       state = ob->state;\r
+\r
+       excesstics = DoActor(ob, tics);\r
+       if (ob->state != state)\r
+       {\r
+               ob->ticcount = 0;               // start the new state at 0, then use excess\r
+               state = ob->state;\r
+       }\r
+\r
+       while (excesstics)\r
+       {\r
+       //\r
+       // passed through to next state\r
+       //\r
+               if (!state->skippable && state->tictime <= excesstics)\r
+               {\r
+                       excesstics = DoActor(ob, state->tictime-1);\r
+               }\r
+               else\r
+               {\r
+                       excesstics = DoActor(ob, excesstics);\r
+               }\r
+               if (ob->state != state)\r
+               {\r
+                       ob->ticcount = 0;               // start the new state at 0, then use excess\r
+                       state = ob->state;\r
+               }\r
+       }\r
+\r
+       if (!state)                     // object removed itself\r
+       {\r
+               RemoveObj(ob);\r
+               return;\r
+       }\r
+\r
+       //\r
+       // if state->rightshapenum == NULL, the state does not have a standard\r
+       // shape (the think routine should have set it)\r
+       //\r
+       if (state->rightshapenum)\r
+       {\r
+               if (ob->xdir > 0)\r
+               {\r
+                       ob->shapenum = state->rightshapenum;\r
+               }\r
+               else\r
+               {\r
+                       ob->shapenum = state->leftshapenum;\r
+               }\r
+       }\r
+       if ((Sint16)ob->shapenum == -1)\r
+       {\r
+               ob->shapenum = 0;               // make it invisable this time\r
+       }\r
+\r
+       if (xtry || ytry || ob->shapenum != oldshapenum || ob->hitnorth == 25)\r
+       {\r
+       //\r
+       // actor moved or changed shape\r
+       // make sure the movement is within limits (one tile)\r
+       //\r
+               if (ob->needtoclip == cl_fullclip)\r
+               {\r
+                       FullClipToWalls(ob);\r
+               }\r
+               else\r
+               {\r
+                       ClipToWalls(ob);\r
+               }\r
+       }\r
+}\r
+\r
+//==========================================================================\r
+\r
+\r
+/*\r
+====================\r
+=\r
+= NewState\r
+=\r
+====================\r
+*/\r
+\r
+void NewState(objtype *ob, statetype *state)\r
+{\r
+       Sint16 temp;\r
+       \r
+       ob->state = state;\r
+\r
+       if (state->rightshapenum)\r
+       {\r
+               if (ob->xdir > 0)\r
+               {\r
+                       ob->shapenum = state->rightshapenum;\r
+               }\r
+               else\r
+               {\r
+                       ob->shapenum = state->leftshapenum;\r
+               }\r
+       }\r
+\r
+       if ((Sint16)ob->shapenum == -1)\r
+       {\r
+               ob->shapenum = 0;\r
+       }\r
+\r
+       temp = ob->needtoclip;\r
+       ob->needtoclip = cl_noclip;\r
+\r
+       xtry = ytry = 0;                                        // no movement\r
+       ClipToWalls(ob);                                        // just calculate values\r
+\r
+       ob->needtoclip = temp;\r
+\r
+       if (ob->needtoclip == cl_fullclip)\r
+       {\r
+               FullClipToWalls(ob);\r
+       }\r
+       else if (ob->needtoclip == cl_midclip)\r
+       {\r
+               ClipToWalls(ob);\r
+       }\r
+}\r
+\r
+//==========================================================================\r
+\r
+\r
+/*\r
+====================\r
+=\r
+= ChangeState\r
+=\r
+====================\r
+*/\r
+\r
+void ChangeState(objtype *ob, statetype *state)\r
+{\r
+       ob->state = state;\r
+       ob->ticcount = 0;\r
+       if (state->rightshapenum)\r
+       {\r
+               if (ob->xdir > 0)\r
+               {\r
+                       ob->shapenum = state->rightshapenum;\r
+               }\r
+               else\r
+               {\r
+                       ob->shapenum = state->leftshapenum;\r
+               }\r
+       }\r
+\r
+       if ((Sint16)ob->shapenum == -1)\r
+       {\r
+               ob->shapenum = 0;\r
+       }\r
+\r
+       ob->needtoreact = true;                 // it will need to be redrawn this frame\r
+\r
+       xtry = ytry = 0;                                        // no movement\r
+\r
+       if (ob->hitnorth != 25)\r
+       {\r
+               ClipToWalls(ob);\r
+       }\r
+}\r
+\r
+//==========================================================================\r
+\r
+\r
+/*\r
+====================\r
+=\r
+= OnScreen\r
+=\r
+====================\r
+*/\r
+\r
+boolean OnScreen(objtype *ob)\r
+{\r
+       if (ob->tileright < originxtile || ob->tilebottom < originytile\r
+               || ob->tileleft > originxtilemax || ob->tiletop > originytilemax)\r
+       {\r
+               return false;\r
+       }\r
+       return true;\r
+}\r
+\r
+//==========================================================================\r
+\r
+\r
+/*\r
+====================\r
+=\r
+= DoGravity\r
+=\r
+====================\r
+*/\r
+\r
+void DoGravity(objtype *ob)\r
+{\r
+       Sint32 i;\r
+//\r
+// only accelerate on odd tics, because of limited precision\r
+//\r
+       for (i = lasttimecount-tics; i<lasttimecount; i++)\r
+       {\r
+               if (i&1)\r
+               {\r
+                       if (ob->yspeed < 0 && ob->yspeed >= -4)\r
+                       {\r
+                               ytry += ob->yspeed;\r
+                               ob->yspeed = 0;\r
+                               return;\r
+                       }\r
+                       ob->yspeed += 4;\r
+                       if (ob->yspeed > 70)\r
+                       {\r
+                               ob->yspeed = 70;\r
+                       }\r
+               }\r
+               ytry += ob->yspeed;\r
+       }\r
+}\r
+\r
+\r
+/*\r
+====================\r
+=\r
+= DoWeakGravity\r
+=\r
+====================\r
+*/\r
+\r
+void DoWeakGravity(objtype *ob)\r
+{\r
+       Sint32 i;\r
+//\r
+// only accelerate on odd tics, because of limited precision\r
+//\r
+       for (i = lasttimecount-tics; i<lasttimecount; i++)\r
+       {\r
+               if (i&1)\r
+               {\r
+                       if (ob->yspeed < 0 && ob->yspeed >= -3)\r
+                       {\r
+                               ytry += ob->yspeed;\r
+                               ob->yspeed = 0;\r
+                               return;\r
+                       }\r
+                       ob->yspeed += 3;\r
+                       if (ob->yspeed > 70)\r
+                       {\r
+                               ob->yspeed = 70;\r
+                       }\r
+               }\r
+               ytry += ob->yspeed;\r
+       }\r
+}\r
+\r
+\r
+/*\r
+====================\r
+=\r
+= DoTinyGravity\r
+=\r
+====================\r
+*/\r
+\r
+void DoTinyGravity(objtype *ob)\r
+{\r
+       Sint32 i;\r
+//\r
+// only accelerate every 4 tics, because of limited precision\r
+//\r
+       for (i = lasttimecount-tics; i<lasttimecount; i++)\r
+       {\r
+               if (!i & 3)     //BUG: this is equal to ((!i) & 3), not (!(i & 3))\r
+               {\r
+                       ob->yspeed++;\r
+                       if (ob->yspeed > 70)\r
+                       {\r
+                               ob->yspeed = 70;\r
+                       }\r
+               }\r
+               ytry += ob->yspeed;\r
+       }\r
+}\r
+\r
+\r
+/*\r
+===============\r
+=\r
+= AccelerateX\r
+=\r
+===============\r
+*/\r
+\r
+void AccelerateX(objtype *ob, Sint16 dir, Sint16 maxspeed)\r
+{\r
+       Sint32 i;\r
+       Uint16 oldsign;\r
+       \r
+       oldsign = ob->xspeed & 0x8000;\r
+//\r
+// only accelerate on odd tics, because of limited precision\r
+//\r
+       for (i=lasttimecount-tics; i<lasttimecount; i++)\r
+       {\r
+               if (i & 1)\r
+               {\r
+                       ob->xspeed += dir;\r
+                       if ((ob->xspeed & 0x8000) != oldsign)\r
+                       {\r
+                               oldsign = ob->xspeed & 0x8000;\r
+                               ob->xdir = oldsign? -1 : 1;\r
+                       }\r
+                       if (ob->xspeed > maxspeed)\r
+                       {\r
+                               ob->xspeed = maxspeed;\r
+                       }\r
+                       else if (ob->xspeed < -maxspeed)\r
+                       {\r
+                               ob->xspeed = -maxspeed;\r
+                       }\r
+               }\r
+               xtry += ob->xspeed;\r
+       }\r
+}\r
+\r
+\r
+/*\r
+===============\r
+=\r
+= AccelerateXv\r
+=\r
+= Doesn't change object's xdir\r
+=\r
+===============\r
+*/\r
+\r
+void AccelerateXv(objtype *ob, Sint16 dir, Sint16 maxspeed)\r
+{\r
+       Sint32 i;\r
+\r
+//\r
+// only accelerate on odd tics, because of limited precision\r
+//\r
+       for (i=lasttimecount-tics; i<lasttimecount; i++)\r
+       {\r
+               if (i & 1)\r
+               {\r
+                       ob->xspeed += dir;\r
+                       if (ob->xspeed > maxspeed)\r
+                       {\r
+                               ob->xspeed = maxspeed;\r
+                       }\r
+                       else if (ob->xspeed < -maxspeed)\r
+                       {\r
+                               ob->xspeed = -maxspeed;\r
+                       }\r
+               }\r
+               xtry += ob->xspeed;\r
+       }\r
+}\r
+\r
+\r
+/*\r
+===============\r
+=\r
+= AccelerateY\r
+=\r
+===============\r
+*/\r
+\r
+void AccelerateY(objtype *ob, Sint16 dir, Sint16 maxspeed)\r
+{\r
+       Sint32 i;\r
+\r
+//\r
+// only accelerate on odd tics, because of limited precision\r
+//\r
+       for (i=lasttimecount-tics; i<lasttimecount; i++)\r
+       {\r
+               if (i & 1)\r
+               {\r
+                       ob->yspeed += dir;\r
+                       if (ob->yspeed > maxspeed)\r
+                       {\r
+                               ob->yspeed = maxspeed;\r
+                       }\r
+                       else if (ob->yspeed < -maxspeed)\r
+                       {\r
+                               ob->yspeed = -maxspeed;\r
+                       }\r
+               }\r
+               ytry += ob->yspeed;\r
+       }\r
+}\r
+\r
+\r
+/*\r
+===============\r
+=\r
+= FrictionX\r
+=\r
+===============\r
+*/\r
+\r
+void FrictionX(objtype *ob)\r
+{\r
+       Sint16 friction, oldsign;\r
+       Sint32 i;\r
+\r
+       oldsign = ob->xspeed & 0x8000;\r
+       if (ob->xspeed > 0)\r
+       {\r
+               friction = -1;\r
+       }\r
+       else if (ob->xspeed < 0)\r
+       {\r
+               friction = 1;\r
+       }\r
+       else\r
+       {\r
+               friction = 0;\r
+       }\r
+//\r
+// only accelerate on odd tics, because of limited precision\r
+//\r
+\r
+       for (i=lasttimecount-tics; i<lasttimecount; i++)\r
+       {\r
+               if (i & 1)\r
+               {\r
+                       ob->xspeed += friction;\r
+                       if ((ob->xspeed & 0x8000) != oldsign)\r
+                       {\r
+                               ob->xspeed = 0;\r
+                       }\r
+               }\r
+               xtry += ob->xspeed;\r
+       }\r
+}\r
+\r
+\r
+/*\r
+===============\r
+=\r
+= FrictionY\r
+=\r
+===============\r
+*/\r
+\r
+void FrictionY(objtype *ob)\r
+{\r
+       Sint16 friction, oldsign;\r
+       Sint32 i;\r
+\r
+       if (ob->yspeed > 0)\r
+       {\r
+               friction = -1;\r
+       }\r
+       else if (ob->yspeed < 0)\r
+       {\r
+               friction = 1;\r
+       }\r
+       else\r
+       {\r
+               friction = 0;\r
+       }\r
+//\r
+// only accelerate on odd tics, because of limited precision\r
+//\r
+       for (i=lasttimecount-tics; i<lasttimecount; i++)\r
+       {\r
+               if (i & 1)\r
+               {\r
+                       ob->yspeed += friction;\r
+                       if ((ob->yspeed & 0x8000) != oldsign)   //BUG: oldsign is not initialized!\r
+                       {\r
+                               ob->yspeed = 0;\r
+                       }\r
+               }\r
+               ytry += ob->yspeed;\r
+       }\r
+}\r
+\r
+//==========================================================================\r
+\r
+/*\r
+===============\r
+=\r
+= StunObj\r
+=\r
+===============\r
+*/\r
+\r
+void StunObj(objtype *ob, objtype *shot, statetype *stunstate)\r
+{\r
+       ExplodeShot(shot);\r
+       ob->temp1 = ob->temp2 = ob->temp3 = 0;  // Note: ob->nothink should also be set to 0\r
+       ob->temp4 = ob->obclass;\r
+       ChangeState(ob, stunstate);\r
+       ob->obclass = stunnedobj;\r
+#ifndef KEEN4\r
+       ob->yspeed -= 24;\r
+       if (ob->yspeed < -48)\r
+               ob->yspeed = -48;\r
+#endif\r
+}\r
+\r
+//==========================================================================\r
+\r
+/*\r
+===============\r
+=\r
+= T_Projectile\r
+=\r
+===============\r
+*/\r
+\r
+void T_Projectile(objtype *ob)\r
+{\r
+       DoGravity(ob);\r
+       xtry = ob->xspeed*tics;\r
+}\r
+\r
+\r
+/*\r
+===============\r
+=\r
+= T_WeakProjectile\r
+=\r
+===============\r
+*/\r
+\r
+void T_WeakProjectile(objtype *ob)\r
+{\r
+       DoWeakGravity(ob);\r
+       xtry = ob->xspeed*tics;\r
+}\r
+\r
+\r
+/*\r
+===============\r
+=\r
+= T_Velocity\r
+=\r
+===============\r
+*/\r
+\r
+void T_Velocity(objtype *ob)\r
+{\r
+       xtry = ob->xspeed*tics;\r
+       ytry = ob->yspeed*tics;\r
+}\r
+\r
+\r
+/*\r
+===============\r
+=\r
+= SetReactThink\r
+=\r
+===============\r
+*/\r
+\r
+void SetReactThink(objtype *ob)\r
+{\r
+       ob->needtoreact = true;\r
+}\r
+\r
+\r
+/*\r
+===============\r
+=\r
+= T_Stunned\r
+=\r
+===============\r
+*/\r
+\r
+void T_Stunned(objtype *ob)\r
+{\r
+       ob->temp1 = 0;\r
+       ob->needtoreact = true;\r
+       if (++ob->temp2 == 3)\r
+               ob->temp2 = 0;\r
+}\r
+\r
+\r
+/*\r
+===============\r
+=\r
+= C_Lethal\r
+=\r
+===============\r
+*/\r
+\r
+void C_Lethal(objtype *ob, objtype *hit)\r
+{\r
+       ob++;                   // shut up compiler\r
+       if (hit->obclass == keenobj)\r
+       {\r
+               KillKeen();\r
+       }\r
+}\r
+\r
+\r
+/*\r
+===============\r
+=\r
+= R_Draw\r
+=\r
+===============\r
+*/\r
+\r
+void R_Draw(objtype *ob)\r
+{\r
+       RF_PlaceSprite(&ob->sprite, ob->x, ob->y, ob->shapenum, spritedraw, ob->priority);\r
+}\r
+\r
+\r
+/*\r
+===============\r
+=\r
+= R_Walk\r
+=\r
+===============\r
+*/\r
+\r
+void R_Walk(objtype *ob)\r
+{\r
+       if (ob->xdir == 1 && ob->hitwest)\r
+       {\r
+               ob->x -= ob->xmove;\r
+               ob->xdir = -1;\r
+               ob->nothink = US_RndT() >> 5;\r
+               ChangeState(ob, ob->state);\r
+       }\r
+       else if (ob->xdir == -1 && ob->hiteast)\r
+       {\r
+               ob->x -= ob->xmove;\r
+               ob->xdir = 1;\r
+               ob->nothink = US_RndT() >> 5;\r
+               ChangeState(ob, ob->state);\r
+       }\r
+       else if (!ob->hitnorth)\r
+       {\r
+               ob->x -= ob->xmove;\r
+               ob->xdir = -ob->xdir;\r
+               ob->nothink = US_RndT() >> 5;\r
+               ChangeState(ob, ob->state);\r
+       }\r
+       RF_PlaceSprite(&ob->sprite, ob->x, ob->y, ob->shapenum, spritedraw, ob->priority);\r
+}\r
+\r
+\r
+/*\r
+===============\r
+=\r
+= R_WalkNormal\r
+=\r
+= Actor will not walk onto tiles with special (e.g. deadly) north walls\r
+=\r
+===============\r
+*/\r
+\r
+void R_WalkNormal(objtype *ob)\r
+{\r
+       if (ob->xdir == 1 && ob->hitwest)\r
+       {\r
+               ob->x -= ob->xmove;\r
+               ob->xdir = -1;\r
+               ob->nothink = US_RndT() >> 5;\r
+               ChangeState(ob, ob->state);\r
+       }\r
+       else if (ob->xdir == -1 && ob->hiteast)\r
+       {\r
+               ob->x -= ob->xmove;\r
+               ob->xdir = 1;\r
+               ob->nothink = US_RndT() >> 5;\r
+               ChangeState(ob, ob->state);\r
+       }\r
+       else if (!ob->hitnorth || (ob->hitnorth & ~7))\r
+       {\r
+               ob->x -= ob->xmove*2;\r
+               ob->xdir = -ob->xdir;\r
+               ob->nothink = US_RndT() >> 5;\r
+               ChangeState(ob, ob->state);\r
+       }\r
+       RF_PlaceSprite(&ob->sprite, ob->x, ob->y, ob->shapenum, spritedraw, ob->priority);\r
+}\r
+\r
+\r
+/*\r
+===============\r
+=\r
+= BadState\r
+=\r
+===============\r
+*/\r
+\r
+void BadState(void)\r
+{\r
+       Quit("Object with bad state!");\r
+}\r
+\r
+\r
+/*\r
+===============\r
+=\r
+= R_Stunned\r
+=\r
+===============\r
+*/\r
+\r
+void R_Stunned(objtype *ob)\r
+{\r
+       Sint16 starx, stary;\r
+\r
+       if (ob->hitwest || ob->hiteast)\r
+               ob->xspeed = 0;\r
+\r
+       if (ob->hitsouth)\r
+               ob->yspeed = 0;\r
+\r
+       if (ob->hitnorth)\r
+       {\r
+               ob->xspeed = ob->yspeed = 0;\r
+               if (ob->state->nextstate)\r
+                       ChangeState(ob, ob->state->nextstate);\r
+       }\r
+\r
+       RF_PlaceSprite(&ob->sprite, ob->x, ob->y, ob->shapenum, spritedraw, ob->priority);\r
+\r
+       starx = stary = 0;\r
+       switch (ob->temp4)\r
+       {\r
+#if defined KEEN4\r
+       case mimrockobj:\r
+               stary = -4*PIXGLOBAL;\r
+               break;\r
+       case eggobj:\r
+               starx = 8*PIXGLOBAL;\r
+               stary = -8*PIXGLOBAL;\r
+               break;\r
+       case treasureeaterobj:\r
+               starx = 8*PIXGLOBAL;\r
+               break;\r
+       case bounderobj:\r
+               starx = 4*PIXGLOBAL;\r
+               stary = -8*PIXGLOBAL;\r
+               break;\r
+       case wormouthobj:\r
+               starx = 4*PIXGLOBAL;\r
+               stary = -350;   // -21.875 pixels (this one is a bit strange)\r
+               break;\r
+       case lickobj:\r
+               stary = -8*PIXGLOBAL;\r
+               break;\r
+       case inchwormobj:\r
+               starx = -4*PIXGLOBAL;\r
+               stary = -8*PIXGLOBAL;\r
+               break;\r
+       case slugobj:\r
+               stary = -8*PIXGLOBAL;\r
+               break;\r
+#elif defined KEEN5\r
+       case sparkyobj:\r
+               starx += 4*PIXGLOBAL;\r
+               break;\r
+       case amptonobj:\r
+               stary -= 8*PIXGLOBAL;\r
+               asm jmp done;           // just to recreate the original code's quirks, feel free to delete this\r
+               break;\r
+       case scottieobj:\r
+               stary -= 8*PIXGLOBAL;\r
+               break;\r
+#elif defined KEEN6\r
+       case blooguardobj:\r
+               starx = 16*PIXGLOBAL;\r
+               stary = -4*PIXGLOBAL;\r
+               break;\r
+       case flectobj:\r
+               starx = 4*PIXGLOBAL;\r
+               stary = -4*PIXGLOBAL;\r
+               break;\r
+       case bloogobj:\r
+       case nospikeobj:\r
+               starx = 8*PIXGLOBAL;\r
+               stary = -4*PIXGLOBAL;\r
+               break;\r
+       case bloogletobj:\r
+       case babobbaobj:\r
+               stary = -8*PIXGLOBAL;\r
+               break;\r
+       case fleexobj:\r
+               starx = 16*PIXGLOBAL;\r
+               stary = 8*PIXGLOBAL;\r
+               break;\r
+       case ceilickobj:\r
+               stary = 12*PIXGLOBAL;\r
+               break;\r
+       default:\r
+               Quit("No star spec for object!");\r
+#endif\r
+       }\r
+done:\r
+\r
+       ob->temp1 = ob->temp1 + tics;\r
+       if (ob->temp1 > 10)\r
+       {\r
+               ob->temp1 -= 10;\r
+               if (++ob->temp2 == 3)\r
+                       ob->temp2 = 0;\r
+       }\r
+\r
+       RF_PlaceSprite((void **)&ob->temp3, ob->x+starx, ob->y+stary, ob->temp2+STUNSTARS1SPR, spritedraw, 3);\r
+}\r
+\r
+//==========================================================================\r
+\r
+statetype sc_deadstate = {0, 0, think, false, false, 0, 0, 0, NULL, NULL, NULL, NULL};\r
+#pragma warn -sus      //BadState is not a valid think/contact/react function. Nobody cares.\r
+statetype sc_badstate  = {0, 0, think, false, false, 0, 0, 0, &BadState, &BadState, &BadState, NULL};\r
+#pragma warn +sus
\ No newline at end of file
diff --git a/16/keen456/KEEN4-6/CK_TEXT.C b/16/keen456/KEEN4-6/CK_TEXT.C
new file mode 100755 (executable)
index 0000000..d4fdf65
--- /dev/null
@@ -0,0 +1,971 @@
+/* Reconstructed Commander Keen 4-6 Source Code\r
+ * Copyright (C) 2021 K1n9_Duk3\r
+ *\r
+ * This file is primarily based on:\r
+ * Wolfenstein 3-D Source Code\r
+ * Copyright (C) 1992 id Software\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License along\r
+ * with this program; if not, write to the Free Software Foundation, Inc.,\r
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+ */\r
+\r
+#include "CK_DEF.H"\r
+\r
+/*\r
+=============================================================================\r
+\r
+TEXT FORMATTING COMMANDS\r
+------------------------\r
+^C<hex digit>                          Change text color\r
+^E[enter]                              End of layout (all pages)\r
+^G<y>,<x>,<pic>[enter] Draw a graphic and push margins\r
+^P[enter]                              start new page, must be the first chars in a layout\r
+^L<x>,<y>[ENTER]               Locate to a specific spot, x in pixels, y in lines\r
+\r
+=============================================================================\r
+*/\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                LOCAL CONSTANTS\r
+\r
+=============================================================================\r
+*/\r
+\r
+#if GRMODE == CGAGR\r
+#ifdef KEEN5\r
+#define BACKCOLOR 2    // CGA magenta\r
+#else\r
+#define BACKCOLOR WHITE\r
+#endif\r
+#else\r
+#define BACKCOLOR RED\r
+#endif\r
+\r
+#define WORDLIMIT 80\r
+#define FONTHEIGHT 10\r
+#define TOPMARGIN 10\r
+#define BOTTOMMARGIN 10\r
+#define LEFTMARGIN 10\r
+#define RIGHTMARGIN 10\r
+#define PICMARGIN 8\r
+#define SPACEWIDTH 7\r
+#define TEXTROWS ((200-TOPMARGIN-BOTTOMMARGIN)/FONTHEIGHT)\r
+#define SCREENPIXWIDTH 320\r
+#define SCREENMID (SCREENPIXWIDTH/2)\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                LOCAL VARIABLES\r
+\r
+=============================================================================\r
+*/\r
+\r
+Sint16 pagenum,numpages;\r
+Uint16 leftmargin[TEXTROWS],rightmargin[TEXTROWS];\r
+char far *text;\r
+Uint16 rowon;\r
+Sint16 picx,picy,picnum,picdelay;\r
+boolean layoutdone;\r
+\r
+Sint16 helpmenupos;\r
+\r
+//===========================================================================\r
+\r
+/*\r
+=====================\r
+=\r
+= RipToEOL\r
+=\r
+=====================\r
+*/\r
+\r
+void RipToEOL(void)\r
+{\r
+       while (*text++ != '\n');\r
+}\r
+\r
+\r
+/*\r
+=====================\r
+=\r
+= ParseNumber\r
+=\r
+=====================\r
+*/\r
+\r
+Sint16 ParseNumber(void)\r
+{\r
+       char c, buffer[80];\r
+       char *bufptr;\r
+\r
+//\r
+// scan until a number is found\r
+//\r
+       c = *text;\r
+       while (c < '0' || c > '9')\r
+               c = *++text;\r
+\r
+//\r
+// copy the number out\r
+//\r
+       bufptr = buffer;\r
+       do\r
+       {\r
+               *bufptr = c;\r
+               bufptr++;\r
+               text++;\r
+               c = *text;\r
+       } while (c >= '0' && c <= '9');\r
+       *bufptr = 0;\r
+\r
+       return atoi(buffer);\r
+}\r
+\r
+\r
+/*\r
+=====================\r
+=\r
+= ParsePicCommand\r
+=\r
+= Call with text pointing just after a ^P\r
+= Upon exit text points to the start of next line\r
+=\r
+=====================\r
+*/\r
+\r
+void ParsePicCommand(void)\r
+{\r
+       picy = ParseNumber();\r
+       picx = ParseNumber();\r
+       picnum = ParseNumber();\r
+       RipToEOL();\r
+}\r
+\r
+void ParseTimedCommand(void)\r
+{\r
+       picy = ParseNumber();\r
+       picx = ParseNumber();\r
+       picnum = ParseNumber();\r
+       picdelay = ParseNumber();\r
+       RipToEOL();\r
+}\r
+\r
+/*\r
+=====================\r
+=\r
+= TimedPicCommand\r
+=\r
+= Call with text pointing just after a ^P\r
+= Upon exit text points to the start of next line\r
+=\r
+=====================\r
+*/\r
+\r
+void TimedPicCommand(void)\r
+{\r
+       ParseTimedCommand();\r
+\r
+//\r
+// update the screen, and wait for time delay\r
+//\r
+#if GRMODE == CGAGR\r
+       VW_UpdateScreen();\r
+#else\r
+       VW_WaitVBL(1);\r
+       VW_ScreenToScreen(bufferofs, displayofs, 40, 200);\r
+#endif\r
+\r
+//\r
+// wait for time\r
+//\r
+       TimeCount = 0;\r
+       while (picdelay > TimeCount)\r
+       ;\r
+\r
+//\r
+// draw pic\r
+//\r
+       VWB_DrawPic(picx & ~7, picy, picnum);\r
+}\r
+\r
+\r
+/*\r
+=====================\r
+=\r
+= HandleCommand\r
+=\r
+=====================\r
+*/\r
+\r
+void HandleCommand(void)\r
+{\r
+       Sint16 i,margin,top,bottom;\r
+       Sint16 picwidth,picheight,picmid;\r
+\r
+       switch (toupper(*(++text)))\r
+       {\r
+       case 'B':\r
+               picy = ParseNumber();\r
+               picx = ParseNumber();\r
+               picwidth = ParseNumber();\r
+               picheight = ParseNumber();\r
+               VWB_Bar(picx, picy, picwidth, picheight, BACKCOLOR);\r
+               RipToEOL();\r
+               break;\r
+\r
+       case 'P':               // ^P is start of next page, ^E is end of file\r
+       case 'E':\r
+               layoutdone = true;\r
+               text--;\r
+               break;\r
+\r
+       case 'C':               // ^c<hex digit> changes text color\r
+               i = toupper(*(++text));\r
+               if (i >= '0' && i <= '9')\r
+               {\r
+                       fontcolor = i + 0 - '0';\r
+               }\r
+               else if (i >= 'A' && i <= 'F')\r
+               {\r
+                       fontcolor = i + 10 - 'A';\r
+               }\r
+#if GRMODE == CGAGR\r
+               {\r
+                       static Sint16 colormap[16] = {2, 0, 0, 0, 2, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0};\r
+                       // Note: This mapping is a bit problematic for Keen 5 CGA,\r
+                       // since some colors get mapped to CGA magenta, which is\r
+                       // used as the background color in that version. Luckily\r
+                       // those colors aren't used in the Keen 5 texts anyway.\r
+\r
+                       fontcolor = colormap[fontcolor];\r
+               }\r
+#endif\r
+               fontcolor ^= BACKCOLOR;\r
+               text++;\r
+               break;\r
+\r
+       case 'L':\r
+               py = ParseNumber();\r
+               rowon = (py - 10)/10;\r
+               py = rowon * 10 + 10;\r
+               px = ParseNumber();\r
+               while (*(text++) != '\n')       // scan to end of line\r
+               ;\r
+               break;\r
+\r
+       case 'T':               // ^Tyyy,xxx,ppp,ttt waits ttt tics, then draws pic\r
+               TimedPicCommand();\r
+               break;\r
+\r
+       case 'G':               // ^Gyyy,xxx,ppp draws graphic\r
+               ParsePicCommand();\r
+               VWB_DrawPic(picx & ~7, picy, picnum);\r
+               picwidth = pictable[picnum-STARTPICS].width * BYTEPIXELS;\r
+               picheight = pictable[picnum-STARTPICS].height;\r
+               picmid = picx + picwidth/2;\r
+               //\r
+               // adjust margins\r
+               //\r
+               if (picmid > SCREENMID)\r
+               {\r
+                       margin = picx-PICMARGIN;                        // new right margin\r
+               }\r
+               else\r
+               {\r
+                       margin = picx+picwidth+PICMARGIN;       // new left margin\r
+               }\r
+               top = (picy-TOPMARGIN)/FONTHEIGHT;\r
+               if (top < 0)\r
+               {\r
+                       top = 0;\r
+               }\r
+               bottom = (picy+picheight-TOPMARGIN)/FONTHEIGHT;\r
+               if (bottom >= TEXTROWS)\r
+               {\r
+                       bottom = TEXTROWS-1;\r
+               }\r
+\r
+               for (i=top; i<=bottom; i++)\r
+               {\r
+                       if (picmid > SCREENMID)\r
+                       {\r
+                               rightmargin[i] = margin;\r
+                       }\r
+                       else\r
+                       {\r
+                               leftmargin[i] = margin;\r
+                       }\r
+               }\r
+\r
+               //\r
+               // adjust this line if needed\r
+               //\r
+               if (leftmargin[rowon] > px)\r
+               {\r
+                       px = leftmargin[rowon];\r
+               }\r
+               break;\r
+       }\r
+}\r
+\r
+\r
+/*\r
+=====================\r
+=\r
+= NewLine\r
+=\r
+=====================\r
+*/\r
+\r
+void NewLine(void)\r
+{\r
+       char c;\r
+\r
+       if (++rowon == TEXTROWS)\r
+       {\r
+       //\r
+       // overflowed the page, so skip until next page break\r
+       //\r
+               layoutdone = true;\r
+               do\r
+               {\r
+                       if (*text == '^')\r
+                       {\r
+                               c = toupper(text[1]);\r
+                               if (c == 'E' || c == 'P')\r
+                               {\r
+                                       layoutdone = true;\r
+                                       return;\r
+                               }\r
+                       }\r
+                       text++;\r
+               } while (1);\r
+       }\r
+       px = leftmargin[rowon];\r
+       py += FONTHEIGHT;\r
+}\r
+\r
+\r
+/*\r
+=====================\r
+=\r
+= HandleCtrls\r
+=\r
+=====================\r
+*/\r
+\r
+void HandleCtrls(void)\r
+{\r
+       char c;\r
+\r
+       c = *(text++);                  // get the character and advance\r
+\r
+       if (c == '\n')\r
+       {\r
+               NewLine();\r
+               return;\r
+       }\r
+}\r
+\r
+/*\r
+=====================\r
+=\r
+= HandleWord\r
+=\r
+=====================\r
+*/\r
+\r
+void HandleWord(void)\r
+{\r
+       Uint16 wwidth, wheight, newpos, wordindex;\r
+       char word[WORDLIMIT];\r
+\r
+       //\r
+       // copy the next word into [word]\r
+       //\r
+       word[0] = *(text++);\r
+       wordindex = 1;\r
+       while (*text > ' ')\r
+       {\r
+               word[wordindex] = *(text++);\r
+               if (++wordindex == WORDLIMIT)\r
+               {\r
+                       Quit("PageLayout: Word limit exceeded");\r
+               }\r
+       }\r
+       word[wordindex] = 0;            // stick a null at end for C\r
+\r
+       //\r
+       // see if it fits on this line\r
+       //\r
+       VW_MeasurePropString(word, &wwidth, &wheight);\r
+       \r
+       while (rightmargin[rowon] < px+wwidth)\r
+       {\r
+               NewLine();\r
+               if (layoutdone)\r
+               {\r
+                       return;         // overflowed page\r
+               }\r
+       }\r
+\r
+       //\r
+       // print it\r
+       //\r
+       newpos = px+wwidth;\r
+       VWB_DrawPropString(word);\r
+       px = newpos;\r
+\r
+       //\r
+       // suck up any extra spaces\r
+       //\r
+       while (*text == ' ')\r
+       {\r
+               px += SPACEWIDTH;\r
+               text++;\r
+       }\r
+}\r
+\r
+/*\r
+=====================\r
+=\r
+= PageLayout\r
+=\r
+= Clears the screen, draws the pics on the page, and word wraps the text.\r
+= Returns a pointer to the terminating command\r
+=\r
+=====================\r
+*/\r
+\r
+void PageLayout(boolean shownumber)\r
+{\r
+       Sint16 oldcolor, i;\r
+       char c;\r
+\r
+       oldcolor = fontcolor;\r
+\r
+#if GRMODE == CGAGR\r
+       fontcolor = BLACK^BACKCOLOR;\r
+#else\r
+       fontcolor = YELLOW^BACKCOLOR;\r
+#endif\r
+\r
+//\r
+// clear the screen\r
+//\r
+       VWB_Bar(0, 0, 320, 200, BACKCOLOR);\r
+#ifndef KEEN6\r
+       VWB_DrawPic(  0, 0, H_TOPWINDOWPIC);\r
+       VWB_DrawPic(  0, 8, H_LEFTWINDOWPIC);\r
+       VWB_DrawPic(312, 8, H_RIGHTWINDOWPIC);\r
+       if (shownumber)\r
+       {\r
+               VWB_DrawPic(8, 176, H_BOTTOMINFOPIC);\r
+       }\r
+       else\r
+       {\r
+               VWB_DrawPic(8, 192, H_BOTTOMWINDOWPIC);\r
+       }\r
+#endif\r
+\r
+       for (i=0; i<TEXTROWS; i++)\r
+       {\r
+               leftmargin[i] = LEFTMARGIN;\r
+               rightmargin[i] = SCREENPIXWIDTH-RIGHTMARGIN;\r
+       }\r
+\r
+       px = LEFTMARGIN;\r
+       py = TOPMARGIN;\r
+       rowon = 0;\r
+       layoutdone = false;\r
+\r
+//\r
+// make sure we are starting layout text (^P first command)\r
+//\r
+       while (*text <= ' ')\r
+       {\r
+               text++;\r
+       }\r
+       if (*text != '^' || toupper(*(++text)) != 'P')\r
+       {\r
+               Quit("PageLayout: Text not headed with ^P");\r
+       }\r
+       while (*(text++) != '\n')\r
+       ;\r
+\r
+//\r
+// process text stream\r
+//\r
+       do\r
+       {\r
+               c = *text;\r
+               if (c == '^')\r
+               {\r
+                       HandleCommand();\r
+               }\r
+               else if (c <= ' ')\r
+               {\r
+                       HandleCtrls();\r
+               }\r
+               else\r
+               {\r
+                       HandleWord();\r
+               }\r
+       } while (!layoutdone);\r
+\r
+       pagenum++;\r
+\r
+       if (shownumber)\r
+       {\r
+               strcpy(str, "pg ");\r
+               itoa(pagenum, str2, 10);\r
+               strcat(str, str2);\r
+               strcat(str, " of ");\r
+               itoa(numpages, str2, 10);\r
+               strcat(str, str2);\r
+#if GRMODE == CGAGR\r
+               fontcolor = BLACK^BACKCOLOR;\r
+#else\r
+               fontcolor = LIGHTRED^BACKCOLOR;\r
+#endif\r
+               py = 186;\r
+               px = 218;\r
+               VWB_DrawPropString(str);\r
+       }\r
+\r
+       fontcolor = oldcolor;\r
+}\r
+\r
+//===========================================================================\r
+\r
+/*\r
+=====================\r
+=\r
+= BackPage\r
+=\r
+= Scans for a previous ^P\r
+=\r
+=====================\r
+*/\r
+\r
+void BackPage(void)\r
+{\r
+       pagenum--;\r
+       do\r
+       {\r
+               text--;\r
+               if (text[0] == '^' && toupper(text[1]) == 'P')\r
+               {\r
+                       return;\r
+               }\r
+       } while (1);\r
+}\r
+\r
+//===========================================================================\r
+\r
+\r
+/*\r
+=====================\r
+=\r
+= CacheLayoutGraphics\r
+=\r
+= Scans an entire layout file (until a ^E) marking all graphics used, and\r
+= counting pages, then caches the graphics in\r
+=\r
+=====================\r
+*/\r
+void CacheLayoutGraphics(void)\r
+{\r
+       char    far *bombpoint, far *textstart;\r
+       char    ch;\r
+\r
+       textstart = text;\r
+       bombpoint = text+30000;\r
+       numpages = pagenum = 0;\r
+\r
+#ifndef KEEN6\r
+       CA_MarkGrChunk(H_TOPWINDOWPIC);\r
+       CA_MarkGrChunk(H_LEFTWINDOWPIC);\r
+       CA_MarkGrChunk(H_RIGHTWINDOWPIC);\r
+       CA_MarkGrChunk(H_BOTTOMINFOPIC);\r
+       CA_MarkGrChunk(H_BOTTOMWINDOWPIC);\r
+#endif\r
+\r
+       do\r
+       {\r
+               if (*text == '^')\r
+               {\r
+                       ch = toupper(*(++text));\r
+                       if (ch == 'P')          // start of a page\r
+                       {\r
+                               numpages++;\r
+                       }\r
+                       if (ch == 'E')          // end of file, so load graphics and return\r
+                       {\r
+                               CA_CacheMarks(NULL);\r
+                               text = textstart;\r
+                               return;\r
+                       }\r
+                       if (ch == 'G')          // draw graphic command, so mark graphics\r
+                       {\r
+                               ParsePicCommand();\r
+                               CA_MarkGrChunk(picnum);\r
+                       }\r
+                       if (ch == 'T')          // timed draw graphic command, so mark graphics\r
+                       {\r
+                               ParseTimedCommand();\r
+                               CA_MarkGrChunk(picnum);\r
+                       }\r
+               }\r
+               else\r
+               {\r
+                       text++;\r
+               }\r
+\r
+       } while (text < bombpoint);\r
+\r
+       Quit("CacheLayoutGraphics: No ^E to terminate file!");\r
+}\r
+\r
+//===========================================================================\r
+\r
+#ifndef KEEN6\r
+/*\r
+=================\r
+=\r
+= HelpMenu\r
+=\r
+=================\r
+*/\r
+Sint16 HelpMenu(void)\r
+{\r
+       CursorInfo cursor;\r
+       ControlInfo control;\r
+       Sint16 ydelta;\r
+       Uint16 key;\r
+\r
+       VWB_Bar(0, 0, 320, 200, BACKCOLOR);\r
+\r
+       CA_CacheGrChunk(H_HELPPIC);\r
+       CA_CacheGrChunk(H_HANDPIC);\r
+       CA_CacheGrChunk(H_TOPWINDOWPIC);\r
+       CA_CacheGrChunk(H_LEFTWINDOWPIC);\r
+       CA_CacheGrChunk(H_RIGHTWINDOWPIC);\r
+       CA_CacheGrChunk(H_BOTTOMWINDOWPIC);\r
+\r
+       VWB_DrawPic(  0,   0, H_TOPWINDOWPIC);\r
+       VWB_DrawPic(  0,   8, H_LEFTWINDOWPIC);\r
+       VWB_DrawPic(312,   8, H_RIGHTWINDOWPIC);\r
+       VWB_DrawPic(  8, 192, H_BOTTOMWINDOWPIC);\r
+       VWB_DrawPic( 96,   8, H_HELPPIC);\r
+\r
+       ydelta = 0;\r
+       IN_ClearKeysDown();\r
+       do\r
+       {\r
+               if (helpmenupos < 0)\r
+               {\r
+                       helpmenupos = 0;\r
+               }\r
+#ifdef GOODTIMES\r
+               else if (helpmenupos > 3)\r
+               {\r
+                       helpmenupos = 3;\r
+               }\r
+#else\r
+               else if (helpmenupos > 4)\r
+               {\r
+                       helpmenupos = 4;\r
+               }\r
+#endif\r
+               VWB_DrawPic(48, 24*helpmenupos+48, H_HANDPIC);\r
+               VW_UpdateScreen();\r
+               VWB_Bar(48, 24*helpmenupos+48, 39, 24, BACKCOLOR);\r
+               IN_ReadControl(0, &control);\r
+               IN_ReadCursor(&cursor);\r
+               if (LastScan)\r
+               {\r
+                       key = LastScan;\r
+                       IN_ClearKeysDown();\r
+                       switch (key)\r
+                       {\r
+                       case sc_UpArrow:\r
+                               helpmenupos--;\r
+                               break;\r
+                       case sc_DownArrow:\r
+                               helpmenupos++;\r
+                               break;\r
+                       case sc_Enter:\r
+                               VW_ClearVideo(BACKCOLOR);\r
+                               return helpmenupos;\r
+                       case sc_Escape:\r
+                               VW_ClearVideo(BACKCOLOR);\r
+                               return -1;\r
+                       }\r
+               }\r
+               ydelta += cursor.y;\r
+               if (cursor.button0 || cursor.button1 || control.button0 || control.button1)\r
+               {\r
+                       VW_ClearVideo(BACKCOLOR);\r
+                       return helpmenupos;\r
+               }\r
+               if (ydelta < -40)\r
+               {\r
+                       ydelta += 40;\r
+                       helpmenupos--;\r
+               }\r
+               else if (ydelta > 40)\r
+               {\r
+                       ydelta -= 40;\r
+                       helpmenupos++;\r
+               }\r
+       } while (1);\r
+}\r
+\r
+/*\r
+=================\r
+=\r
+= HelpScreens\r
+=\r
+=================\r
+*/\r
+void HelpScreens(void)\r
+{\r
+       static Uint16 layouttable[5] =\r
+       {\r
+               T_HELPART,\r
+               T_CONTRART,\r
+               T_STORYART,\r
+#ifndef GOODTIMES\r
+               T_ORDERART,\r
+#endif\r
+               T_IDART\r
+       };\r
+\r
+       Uint16 olddisplayofs, oldbufferofs, oldfontnumber, temp;\r
+       Sint16 pos;\r
+       boolean newpage;\r
+\r
+       oldfontnumber = fontnumber;\r
+       olddisplayofs = displayofs;\r
+       oldbufferofs = bufferofs;\r
+       fontnumber = 0;\r
+\r
+#if GRMODE == EGAGR\r
+       EGAMAPMASK(15);\r
+#endif\r
+\r
+       CA_UpLevel();\r
+       CA_SetGrPurge();\r
+       VW_ClearVideo(BACKCOLOR);\r
+\r
+#if GRMODE == EGAGR\r
+       RF_FixOfs();\r
+       bufferofs = 0;\r
+       displayofs = 0x8000;\r
+       VW_SetScreen(displayofs, 0);\r
+#endif\r
+\r
+#ifdef KEEN5\r
+       StartMusic(19);\r
+#endif\r
+\r
+       do\r
+       {\r
+               pos = HelpMenu();\r
+\r
+               VW_ClearVideo(BACKCOLOR);\r
+\r
+               if (pos == -1)\r
+               {\r
+                       CA_DownLevel();\r
+                       IN_ClearKeysDown();\r
+                       bufferofs = oldbufferofs;\r
+                       displayofs = olddisplayofs;\r
+                       fontnumber = oldfontnumber;\r
+                       VW_ClearVideo(BACKCOLOR);\r
+                       RF_FixOfs();\r
+#ifdef KEEN5\r
+                       StopMusic();    // Note: it's safer to call StopMusic BEFORE CA_DownLevel\r
+#endif\r
+                       return;\r
+               }\r
+\r
+               pos = layouttable[pos];\r
+               CA_CacheGrChunk(pos);\r
+               text = grsegs[pos];\r
+               CacheLayoutGraphics();\r
+\r
+               newpage = true;\r
+               do\r
+               {\r
+                       if (newpage)\r
+                       {\r
+                               newpage = false;\r
+                               PageLayout(true);\r
+#if GRMODE == CGAGR\r
+                               VW_UpdateScreen();\r
+#else\r
+                               VW_SetScreen(bufferofs, 0);\r
+                               temp = displayofs;\r
+                               displayofs = bufferofs;\r
+                               bufferofs = temp;\r
+#endif\r
+                       }\r
+\r
+                       LastScan = 0;\r
+                       while (!LastScan);\r
+\r
+                       switch (LastScan)\r
+                       {\r
+                       case sc_UpArrow:\r
+                       case sc_LeftArrow:\r
+                       case sc_PgUp:\r
+                               if (pagenum > 1)\r
+                               {\r
+                                       BackPage();\r
+                                       BackPage();\r
+                                       newpage = true;\r
+                               }\r
+                               break;\r
+                       case sc_DownArrow:\r
+                       case sc_RightArrow:\r
+                       case sc_PgDn:\r
+                               if (pagenum < numpages)\r
+                               {\r
+                                       newpage = true;\r
+                               }\r
+                               break;\r
+                       }\r
+               } while (LastScan != sc_Escape);\r
+\r
+               MM_FreePtr(&grsegs[pos]);\r
+               IN_ClearKeysDown();\r
+       } while (true);\r
+}\r
+\r
+#endif\r
+\r
+//===========================================================================\r
+\r
+/*\r
+=================\r
+=\r
+= FinaleLayout\r
+=\r
+=================\r
+*/\r
+void FinaleLayout(void)\r
+{\r
+       char _seg *textseg;\r
+       Sint16 i;\r
+\r
+       VW_ClearVideo(BACKCOLOR);\r
+       RF_FixOfs();\r
+       CA_UpLevel();\r
+       CA_SetGrPurge();\r
+       CA_CacheGrChunk(H_FLASHARROW2PIC);\r
+       CA_CacheGrChunk(H_FLASHARROW1PIC);\r
+\r
+#ifdef KEEN5\r
+       if (gamestate.leveldone[13] == ex_fusebroke)\r
+       {\r
+               CA_CacheGrChunk(T_ENDART2);\r
+               textseg = grsegs[T_ENDART2];\r
+       }\r
+       else\r
+       {\r
+               CA_CacheGrChunk(T_ENDART);\r
+               textseg = grsegs[T_ENDART];\r
+       }\r
+#else\r
+       CA_CacheGrChunk(T_ENDART);\r
+       textseg = grsegs[T_ENDART];\r
+#endif\r
+\r
+       text = textseg;\r
+       CacheLayoutGraphics();\r
+\r
+       StartMusic(ENDINGMUSIC);\r
+\r
+       while (pagenum < numpages)\r
+       {\r
+               PageLayout(false);\r
+               IN_ClearKeysDown();\r
+#if GRMODE == CGAGR\r
+               VW_UpdateScreen();\r
+#else\r
+               VW_SetScreen(bufferofs, 0);\r
+#endif\r
+\r
+               do\r
+               {\r
+                       VWB_DrawPic(298, 184, H_FLASHARROW1PIC);\r
+#if GRMODE == CGAGR\r
+                       VW_UpdateScreen();\r
+#endif\r
+                       for (i=0; i<TickBase; i++)\r
+                       {\r
+                               if (IN_IsUserInput())\r
+                               {\r
+                                       goto nextpage;\r
+                               }\r
+                               VW_WaitVBL(1);\r
+                       }\r
+\r
+                       VWB_DrawPic(298, 184, H_FLASHARROW2PIC);\r
+#if GRMODE == CGAGR\r
+                       VW_UpdateScreen();\r
+#endif\r
+                       for (i=0; i<TickBase; i++)\r
+                       {\r
+                               if (IN_IsUserInput())\r
+                               {\r
+                                       goto nextpage;\r
+                               }\r
+                               VW_WaitVBL(1);\r
+                       }\r
+               } while (1);\r
+\r
+nextpage:\r
+               ;       // Borland C++ 2.0 needs a semicolon here...\r
+       }\r
+\r
+       StopMusic();\r
+\r
+#ifdef KEEN5\r
+       if (gamestate.leveldone[13] == ex_fusebroke)\r
+       {\r
+               MM_FreePtr(&grsegs[T_ENDART2]);\r
+       }\r
+       else\r
+       {\r
+               MM_FreePtr(&grsegs[H_FLASHARROW1PIC]);  // BUG! this should free T_ENDART, the arrow should be freed after the else branch!\r
+       }\r
+#else\r
+       MM_FreePtr(&grsegs[T_ENDART]);\r
+       MM_FreePtr(&grsegs[H_FLASHARROW1PIC]);\r
+#endif\r
+       MM_FreePtr(&grsegs[H_FLASHARROW2PIC]);\r
+       CA_DownLevel();\r
+       IN_ClearKeysDown();\r
+#if GRMODE != CGAGR\r
+       VW_ClearVideo(BACKCOLOR);\r
+       RF_FixOfs();\r
+#endif\r
+       CA_FreeGraphics();\r
+}
\ No newline at end of file
diff --git a/16/keen456/KEEN4-6/COPYING b/16/keen456/KEEN4-6/COPYING
new file mode 100755 (executable)
index 0000000..d159169
--- /dev/null
@@ -0,0 +1,339 @@
+                    GNU GENERAL PUBLIC LICENSE
+                       Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                            Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+                    GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+                            NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+                     END OF TERMS AND CONDITIONS
+
+            How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License along
+    with this program; if not, write to the Free Software Foundation, Inc.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) year name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.
diff --git a/16/keen456/KEEN4-6/ID_CA.C b/16/keen456/KEEN4-6/ID_CA.C
new file mode 100755 (executable)
index 0000000..a8323a2
--- /dev/null
@@ -0,0 +1,2151 @@
+/* Reconstructed Commander Keen 4-6 Source Code\r
+ * Copyright (C) 2021 K1n9_Duk3\r
+ *\r
+ * This file is primarily based on:\r
+ * Catacomb 3-D Source Code\r
+ * Copyright (C) 1993-2014 Flat Rock Software\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License along\r
+ * with this program; if not, write to the Free Software Foundation, Inc.,\r
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+ */\r
+\r
+// ID_CA.C\r
+\r
+/*\r
+=============================================================================\r
+\r
+Id Software Caching Manager\r
+---------------------------\r
+\r
+Must be started BEFORE the memory manager, because it needs to get the headers\r
+loaded into the data segment\r
+\r
+=============================================================================\r
+*/\r
+\r
+#include "ID_HEADS.H"\r
+#pragma hdrstop\r
+\r
+#pragma warn -pro\r
+#pragma warn -use\r
+\r
+#define THREEBYTEGRSTARTS\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                LOCAL CONSTANTS\r
+\r
+=============================================================================\r
+*/\r
+\r
+typedef struct\r
+{\r
+  unsigned bit0,bit1;  // 0-255 is a character, > is a pointer to a node\r
+} huffnode;\r
+\r
+\r
+typedef struct\r
+{\r
+       unsigned        RLEWtag;\r
+       long            headeroffsets[100];\r
+       byte            tileinfo[];\r
+} mapfiletype;\r
+\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                GLOBAL VARIABLES\r
+\r
+=============================================================================\r
+*/\r
+\r
+byte           _seg    *tinf;\r
+int                    mapon;\r
+\r
+unsigned       _seg    *mapsegs[3];\r
+maptype                _seg    *mapheaderseg[NUMMAPS];\r
+byte           _seg    *audiosegs[NUMSNDCHUNKS];\r
+void           _seg    *grsegs[NUMCHUNKS];\r
+\r
+byte           far     grneeded[NUMCHUNKS];\r
+byte           ca_levelbit,ca_levelnum;\r
+\r
+int                    profilehandle,debughandle;\r
+\r
+void   (*drawcachebox)         (char *title, unsigned numcache);\r
+void   (*updatecachebox)       (void);\r
+void   (*finishcachebox)       (void);\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                LOCAL VARIABLES\r
+\r
+=============================================================================\r
+*/\r
+\r
+extern long    far     CGAhead;\r
+extern long    far     EGAhead;\r
+extern byte    CGAdict;\r
+extern byte    EGAdict;\r
+extern byte    far     maphead;\r
+extern byte    mapdict;\r
+extern byte    far     audiohead;\r
+extern byte    audiodict;\r
+\r
+\r
+long           _seg *grstarts; // array of offsets in egagraph, -1 for sparse\r
+long           _seg *audiostarts;      // array of offsets in audio / audiot\r
+\r
+#ifdef GRHEADERLINKED\r
+huffnode       *grhuffman;\r
+#else\r
+huffnode       grhuffman[255];\r
+#endif\r
+\r
+#ifdef AUDIOHEADERLINKED\r
+huffnode       *audiohuffman;\r
+#else\r
+huffnode       audiohuffman[255];\r
+#endif\r
+\r
+\r
+int                    grhandle;               // handle to EGAGRAPH\r
+int                    maphandle;              // handle to MAPTEMP / GAMEMAPS\r
+int                    audiohandle;    // handle to AUDIOT / AUDIO\r
+\r
+long           chunkcomplen,chunkexplen;\r
+\r
+SDMode         oldsoundmode;\r
+\r
+\r
+\r
+void   CAL_DialogDraw (char *title,unsigned numcache);\r
+void   CAL_DialogUpdate (void);\r
+void   CAL_DialogFinish (void);\r
+void   CAL_CarmackExpand (unsigned far *source, unsigned far *dest,\r
+               unsigned length);\r
+\r
+\r
+#ifdef THREEBYTEGRSTARTS\r
+#define FILEPOSSIZE    3\r
+//#define      GRFILEPOS(c) (*(long far *)(((byte far *)grstarts)+(c)*3)&0xffffff)\r
+long GRFILEPOS(int c)\r
+{\r
+       long value;\r
+       int     offset;\r
+\r
+       offset = c*3;\r
+\r
+       value = *(long far *)(((byte far *)grstarts)+offset);\r
+\r
+       value &= 0x00ffffffl;\r
+\r
+       if (value == 0xffffffl)\r
+               value = -1;\r
+\r
+       return value;\r
+};\r
+#else\r
+#define FILEPOSSIZE    4\r
+#define        GRFILEPOS(c) (grstarts[c])\r
+#endif\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                          LOW LEVEL ROUTINES\r
+\r
+=============================================================================\r
+*/\r
+\r
+/*\r
+============================\r
+=\r
+= CA_OpenDebug / CA_CloseDebug\r
+=\r
+= Opens a binary file with the handle "debughandle"\r
+=\r
+============================\r
+*/\r
+\r
+void CA_OpenDebug (void)\r
+{\r
+       unlink ("DEBUG.TXT");\r
+       debughandle = open("DEBUG.TXT", O_CREAT | O_WRONLY | O_TEXT);\r
+}\r
+\r
+void CA_CloseDebug (void)\r
+{\r
+       close (debughandle);\r
+}\r
+\r
+\r
+\r
+/*\r
+============================\r
+=\r
+= CAL_GetGrChunkLength\r
+=\r
+= Gets the length of an explicit length chunk (not tiles)\r
+= The file pointer is positioned so the compressed data can be read in next.\r
+=\r
+============================\r
+*/\r
+\r
+void CAL_GetGrChunkLength (int chunk)\r
+{\r
+       lseek(grhandle,GRFILEPOS(chunk),SEEK_SET);\r
+       read(grhandle,&chunkexplen,sizeof(chunkexplen));\r
+       chunkcomplen = GRFILEPOS(chunk+1)-GRFILEPOS(chunk)-4;\r
+}\r
+\r
+\r
+/*\r
+==========================\r
+=\r
+= CA_FarRead\r
+=\r
+= Read from a file to a far pointer\r
+=\r
+==========================\r
+*/\r
+\r
+boolean CA_FarRead (int handle, byte far *dest, long length)\r
+{\r
+       if (length>0xffffl)\r
+               Quit ("CA_FarRead doesn't support 64K reads yet!");\r
+\r
+asm            push    ds\r
+asm            mov     bx,[handle]\r
+asm            mov     cx,[WORD PTR length]\r
+asm            mov     dx,[WORD PTR dest]\r
+asm            mov     ds,[WORD PTR dest+2]\r
+asm            mov     ah,0x3f                         // READ w/handle\r
+asm            int     21h\r
+asm            pop     ds\r
+asm            jnc     good\r
+       errno = _AX;\r
+       return  false;\r
+good:\r
+asm            cmp     ax,[WORD PTR length]\r
+asm            je      done\r
+       errno = EINVFMT;                        // user manager knows this is bad read\r
+       return  false;\r
+done:\r
+       return  true;\r
+}\r
+\r
+\r
+/*\r
+==========================\r
+=\r
+= CA_SegWrite\r
+=\r
+= Write from a file to a far pointer\r
+=\r
+==========================\r
+*/\r
+\r
+boolean CA_FarWrite (int handle, byte far *source, long length)\r
+{\r
+       if (length>0xffffl)\r
+               Quit ("CA_FarWrite doesn't support 64K reads yet!");\r
+\r
+asm            push    ds\r
+asm            mov     bx,[handle]\r
+asm            mov     cx,[WORD PTR length]\r
+asm            mov     dx,[WORD PTR source]\r
+asm            mov     ds,[WORD PTR source+2]\r
+asm            mov     ah,0x40                 // WRITE w/handle\r
+asm            int     21h\r
+asm            pop     ds\r
+asm            jnc     good\r
+       errno = _AX;\r
+       return  false;\r
+good:\r
+asm            cmp     ax,[WORD PTR length]\r
+asm            je      done\r
+       errno = ENOMEM;                         // user manager knows this is bad write\r
+       return  false;\r
+\r
+done:\r
+       return  true;\r
+}\r
+\r
+\r
+/*\r
+==========================\r
+=\r
+= CA_ReadFile\r
+=\r
+= Reads a file into an allready allocated buffer\r
+=\r
+==========================\r
+*/\r
+\r
+boolean CA_ReadFile (char *filename, memptr *ptr)\r
+{\r
+       int handle;\r
+       long size;\r
+\r
+       if ((handle = open(filename,O_RDONLY | O_BINARY, S_IREAD)) == -1)\r
+               return false;\r
+\r
+       size = filelength (handle);\r
+       if (!CA_FarRead (handle,*ptr,size))\r
+       {\r
+               close (handle);\r
+               return false;\r
+       }\r
+       close (handle);\r
+       return true;\r
+}\r
+\r
+\r
+\r
+/*\r
+==========================\r
+=\r
+= CA_LoadFile\r
+=\r
+= Allocate space for and load a file\r
+=\r
+==========================\r
+*/\r
+\r
+boolean CA_LoadFile (char *filename, memptr *ptr)\r
+{\r
+       int handle;\r
+       long size;\r
+\r
+       if ((handle = open(filename,O_RDONLY | O_BINARY, S_IREAD)) == -1)\r
+               return false;\r
+\r
+       size = filelength (handle);\r
+       MM_GetPtr (ptr,size);\r
+       if (!CA_FarRead (handle,*ptr,size))\r
+       {\r
+               close (handle);\r
+               return false;\r
+       }\r
+       close (handle);\r
+       return true;\r
+}\r
+\r
+/*\r
+============================================================================\r
+\r
+               COMPRESSION routines, see JHUFF.C for more\r
+\r
+============================================================================\r
+*/\r
+\r
+\r
+\r
+/*\r
+===============\r
+=\r
+= CAL_OptimizeNodes\r
+=\r
+= Goes through a huffman table and changes the 256-511 node numbers to the\r
+= actular address of the node.  Must be called before CAL_HuffExpand\r
+=\r
+===============\r
+*/\r
+\r
+void CAL_OptimizeNodes (huffnode *table)\r
+{\r
+  huffnode *node;\r
+  int i;\r
+\r
+  node = table;\r
+\r
+  for (i=0;i<255;i++)\r
+  {\r
+       if (node->bit0 >= 256)\r
+         node->bit0 = (unsigned)(table+(node->bit0-256));\r
+       if (node->bit1 >= 256)\r
+         node->bit1 = (unsigned)(table+(node->bit1-256));\r
+       node++;\r
+  }\r
+}\r
+\r
+\r
+\r
+/*\r
+======================\r
+=\r
+= CAL_HuffExpand\r
+=\r
+= Length is the length of the EXPANDED data\r
+=\r
+======================\r
+*/\r
+\r
+void CAL_HuffExpand (byte huge *source, byte huge *dest,\r
+  long length,huffnode *hufftable)\r
+{\r
+//  unsigned bit,byte,node,code;\r
+  unsigned sourceseg,sourceoff,destseg,destoff,endoff;\r
+  huffnode *headptr;\r
+//  huffnode *nodeon;\r
+\r
+  headptr = hufftable+254;     // head node is allways node 254\r
+\r
+  source++;    // normalize\r
+  source--;\r
+  dest++;\r
+  dest--;\r
+\r
+  sourceseg = FP_SEG(source);\r
+  sourceoff = FP_OFF(source);\r
+  destseg = FP_SEG(dest);\r
+  destoff = FP_OFF(dest);\r
+  endoff = destoff+length;\r
+\r
+//\r
+// ds:si source\r
+// es:di dest\r
+// ss:bx node pointer\r
+//\r
+\r
+       if (length <0xfff0)\r
+       {\r
+\r
+//--------------------------\r
+// expand less than 64k of data\r
+//--------------------------\r
+\r
+asm mov        bx,[headptr]\r
+\r
+asm    mov     si,[sourceoff]\r
+asm    mov     di,[destoff]\r
+asm    mov     es,[destseg]\r
+asm    mov     ds,[sourceseg]\r
+asm    mov     ax,[endoff]\r
+\r
+asm    mov     ch,[si]                         // load first byte\r
+asm    inc     si\r
+asm    mov     cl,1\r
+\r
+expandshort:\r
+asm    test    ch,cl                   // bit set?\r
+asm    jnz     bit1short\r
+asm    mov     dx,[ss:bx]                      // take bit0 path from node\r
+asm    shl     cl,1                            // advance to next bit position\r
+asm    jc      newbyteshort\r
+asm    jnc     sourceupshort\r
+\r
+bit1short:\r
+asm    mov     dx,[ss:bx+2]            // take bit1 path\r
+asm    shl     cl,1                            // advance to next bit position\r
+asm    jnc     sourceupshort\r
+\r
+newbyteshort:\r
+asm    mov     ch,[si]                         // load next byte\r
+asm    inc     si\r
+asm    mov     cl,1                            // back to first bit\r
+\r
+sourceupshort:\r
+asm    or      dh,dh                           // if dx<256 its a byte, else move node\r
+asm    jz      storebyteshort\r
+asm    mov     bx,dx                           // next node = (huffnode *)code\r
+asm    jmp     expandshort\r
+\r
+storebyteshort:\r
+asm    mov     [es:di],dl\r
+asm    inc     di                                      // write a decopmpressed byte out\r
+asm    mov     bx,[headptr]            // back to the head node for next bit\r
+\r
+asm    cmp     di,ax                           // done?\r
+asm    jne     expandshort\r
+       }\r
+       else\r
+       {\r
+\r
+//--------------------------\r
+// expand more than 64k of data\r
+//--------------------------\r
+\r
+  length--;\r
+\r
+asm mov        bx,[headptr]\r
+asm    mov     cl,1\r
+\r
+asm    mov     si,[sourceoff]\r
+asm    mov     di,[destoff]\r
+asm    mov     es,[destseg]\r
+asm    mov     ds,[sourceseg]\r
+\r
+asm    lodsb                   // load first byte\r
+\r
+expand:\r
+asm    test    al,cl           // bit set?\r
+asm    jnz     bit1\r
+asm    mov     dx,[ss:bx]      // take bit0 path from node\r
+asm    jmp     gotcode\r
+bit1:\r
+asm    mov     dx,[ss:bx+2]    // take bit1 path\r
+\r
+gotcode:\r
+asm    shl     cl,1            // advance to next bit position\r
+asm    jnc     sourceup\r
+asm    lodsb\r
+asm    cmp     si,0x10         // normalize ds:si\r
+asm    jb      sinorm\r
+asm    mov     cx,ds\r
+asm    inc     cx\r
+asm    mov     ds,cx\r
+asm    xor     si,si\r
+sinorm:\r
+asm    mov     cl,1            // back to first bit\r
+\r
+sourceup:\r
+asm    or      dh,dh           // if dx<256 its a byte, else move node\r
+asm    jz      storebyte\r
+asm    mov     bx,dx           // next node = (huffnode *)code\r
+asm    jmp     expand\r
+\r
+storebyte:\r
+asm    mov     [es:di],dl\r
+asm    inc     di              // write a decopmpressed byte out\r
+asm    mov     bx,[headptr]    // back to the head node for next bit\r
+\r
+asm    cmp     di,0x10         // normalize es:di\r
+asm    jb      dinorm\r
+asm    mov     dx,es\r
+asm    inc     dx\r
+asm    mov     es,dx\r
+asm    xor     di,di\r
+dinorm:\r
+\r
+asm    sub     [WORD PTR ss:length],1\r
+asm    jnc     expand\r
+asm    dec     [WORD PTR ss:length+2]\r
+asm    jns     expand          // when length = ffff ffff, done\r
+\r
+       }\r
+\r
+asm    mov     ax,ss\r
+asm    mov     ds,ax\r
+\r
+}\r
+\r
+\r
+/*\r
+======================\r
+=\r
+= CAL_CarmackExpand\r
+=\r
+= Length is the length of the EXPANDED data\r
+=\r
+======================\r
+*/\r
+\r
+#define NEARTAG        0xa7\r
+#define FARTAG 0xa8\r
+\r
+void CAL_CarmackExpand (unsigned far *source, unsigned far *dest, unsigned length)\r
+{\r
+       unsigned        ch,chhigh,count,offset;\r
+       unsigned        far *copyptr, far *inptr, far *outptr;\r
+\r
+       length/=2;\r
+\r
+       inptr = source;\r
+       outptr = dest;\r
+\r
+       while (length)\r
+       {\r
+               ch = *inptr++;\r
+               chhigh = ch>>8;\r
+               if (chhigh == NEARTAG)\r
+               {\r
+                       count = ch&0xff;\r
+                       if (!count)\r
+                       {                               // have to insert a word containing the tag byte\r
+                               ch |= *((unsigned char far *)inptr)++;\r
+                               *outptr++ = ch;\r
+                               length--;\r
+                       }\r
+                       else\r
+                       {\r
+                               offset = *((unsigned char far *)inptr)++;\r
+                               copyptr = outptr - offset;\r
+                               length -= count;\r
+                               while (count--)\r
+                                       *outptr++ = *copyptr++;\r
+                       }\r
+               }\r
+               else if (chhigh == FARTAG)\r
+               {\r
+                       count = ch&0xff;\r
+                       if (!count)\r
+                       {                               // have to insert a word containing the tag byte\r
+                               ch |= *((unsigned char far *)inptr)++;\r
+                               *outptr++ = ch;\r
+                               length --;\r
+                       }\r
+                       else\r
+                       {\r
+                               offset = *inptr++;\r
+                               copyptr = dest + offset;\r
+                               length -= count;\r
+                               while (count--)\r
+                                       *outptr++ = *copyptr++;\r
+                       }\r
+               }\r
+               else\r
+               {\r
+                       *outptr++ = ch;\r
+                       length --;\r
+               }\r
+       }\r
+}\r
+\r
+\r
+\r
+/*\r
+======================\r
+=\r
+= CA_RLEWcompress\r
+=\r
+======================\r
+*/\r
+\r
+long CA_RLEWCompress (unsigned huge *source, long length, unsigned huge *dest,\r
+  unsigned rlewtag)\r
+{\r
+  long complength;\r
+  unsigned value,count,i;\r
+  unsigned huge *start,huge *end;\r
+\r
+  start = dest;\r
+\r
+  end = source + (length+1)/2;\r
+\r
+//\r
+// compress it\r
+//\r
+  do\r
+  {\r
+       count = 1;\r
+       value = *source++;\r
+       while (*source == value && source<end)\r
+       {\r
+         count++;\r
+         source++;\r
+       }\r
+       if (count>3 || value == rlewtag)\r
+       {\r
+    //\r
+    // send a tag / count / value string\r
+    //\r
+      *dest++ = rlewtag;\r
+      *dest++ = count;\r
+      *dest++ = value;\r
+    }\r
+    else\r
+    {\r
+    //\r
+    // send word without compressing\r
+    //\r
+      for (i=1;i<=count;i++)\r
+       *dest++ = value;\r
+       }\r
+\r
+  } while (source<end);\r
+\r
+  complength = 2*(dest-start);\r
+  return complength;\r
+}\r
+\r
+\r
+/*\r
+======================\r
+=\r
+= CA_RLEWexpand\r
+= length is EXPANDED length\r
+=\r
+======================\r
+*/\r
+\r
+void CA_RLEWexpand (unsigned huge *source, unsigned huge *dest,long length,\r
+  unsigned rlewtag)\r
+{\r
+//  unsigned value,count,i;\r
+  unsigned huge *end;\r
+  unsigned sourceseg,sourceoff,destseg,destoff,endseg,endoff;\r
+\r
+\r
+//\r
+// expand it\r
+//\r
+#if 0\r
+  do\r
+  {\r
+       value = *source++;\r
+       if (value != rlewtag)\r
+       //\r
+       // uncompressed\r
+       //\r
+         *dest++=value;\r
+       else\r
+       {\r
+       //\r
+       // compressed string\r
+       //\r
+         count = *source++;\r
+         value = *source++;\r
+         for (i=1;i<=count;i++)\r
+       *dest++ = value;\r
+       }\r
+  } while (dest<end);\r
+#endif\r
+\r
+  end = dest + (length)/2;\r
+  sourceseg = FP_SEG(source);\r
+  sourceoff = FP_OFF(source);\r
+  destseg = FP_SEG(dest);\r
+  destoff = FP_OFF(dest);\r
+  endseg = FP_SEG(end);\r
+  endoff = FP_OFF(end);\r
+\r
+\r
+//\r
+// ax = source value\r
+// bx = tag value\r
+// cx = repeat counts\r
+// dx = scratch\r
+//\r
+// NOTE: A repeat count that produces 0xfff0 bytes can blow this!\r
+//\r
+\r
+asm    mov     bx,rlewtag\r
+asm    mov     si,sourceoff\r
+asm    mov     di,destoff\r
+asm    mov     es,destseg\r
+asm    mov     ds,sourceseg\r
+\r
+expand:\r
+asm    lodsw\r
+asm    cmp     ax,bx\r
+asm    je      repeat\r
+asm    stosw\r
+asm    jmp     next\r
+\r
+repeat:\r
+asm    lodsw\r
+asm    mov     cx,ax           // repeat count\r
+asm    lodsw                   // repeat value\r
+asm    rep stosw\r
+\r
+next:\r
+\r
+asm    cmp     si,0x10         // normalize ds:si\r
+asm    jb      sinorm\r
+asm    mov     ax,si\r
+asm    shr     ax,1\r
+asm    shr     ax,1\r
+asm    shr     ax,1\r
+asm    shr     ax,1\r
+asm    mov     dx,ds\r
+asm    add     dx,ax\r
+asm    mov     ds,dx\r
+asm    and     si,0xf\r
+sinorm:\r
+asm    cmp     di,0x10         // normalize es:di\r
+asm    jb      dinorm\r
+asm    mov     ax,di\r
+asm    shr     ax,1\r
+asm    shr     ax,1\r
+asm    shr     ax,1\r
+asm    shr     ax,1\r
+asm    mov     dx,es\r
+asm    add     dx,ax\r
+asm    mov     es,dx\r
+asm    and     di,0xf\r
+dinorm:\r
+\r
+asm    cmp     di,ss:endoff\r
+asm    jne     expand\r
+asm    mov     ax,es\r
+asm    cmp     ax,ss:endseg\r
+asm    jb      expand\r
+\r
+asm    mov     ax,ss\r
+asm    mov     ds,ax\r
+\r
+}\r
+\r
+\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                        CACHE MANAGER ROUTINES\r
+\r
+=============================================================================\r
+*/\r
+\r
+\r
+/*\r
+======================\r
+=\r
+= CAL_SetupGrFile\r
+=\r
+======================\r
+*/\r
+\r
+void CAL_SetupGrFile (void)\r
+{\r
+       int handle;\r
+       memptr compseg;\r
+\r
+#ifdef GRHEADERLINKED\r
+\r
+#if GRMODE == EGAGR\r
+       grhuffman = (huffnode *)&EGAdict;\r
+       grstarts = (long _seg *)FP_SEG(&EGAhead);\r
+#endif\r
+#if GRMODE == CGAGR\r
+       grhuffman = (huffnode *)&CGAdict;\r
+       grstarts = (long _seg *)FP_SEG(&CGAhead);\r
+#endif\r
+\r
+       CAL_OptimizeNodes (grhuffman);\r
+\r
+#else\r
+\r
+//\r
+// load ???dict.ext (huffman dictionary for graphics files)\r
+//\r
+\r
+       if ((handle = open(GREXT"DICT."EXTENSION,\r
+                O_RDONLY | O_BINARY, S_IREAD)) == -1)\r
+               Quit ("Can't open "GREXT"DICT."EXTENSION"!");\r
+\r
+       read(handle, &grhuffman, sizeof(grhuffman));\r
+       close(handle);\r
+       CAL_OptimizeNodes (grhuffman);\r
+//\r
+// load the data offsets from ???head.ext\r
+//\r
+       MM_GetPtr (&(memptr)grstarts,(NUMCHUNKS+1)*FILEPOSSIZE);\r
+\r
+       if ((handle = open(GREXT"HEAD."EXTENSION,\r
+                O_RDONLY | O_BINARY, S_IREAD)) == -1)\r
+               Quit ("Can't open "GREXT"HEAD."EXTENSION"!");\r
+\r
+       CA_FarRead(handle, (memptr)grstarts, (NUMCHUNKS+1)*FILEPOSSIZE);\r
+\r
+       close(handle);\r
+\r
+\r
+#endif\r
+\r
+//\r
+// Open the graphics file, leaving it open until the game is finished\r
+//\r
+       grhandle = open(GREXT"GRAPH."EXTENSION, O_RDONLY | O_BINARY);\r
+       if (grhandle == -1)\r
+               Quit ("Cannot open "GREXT"GRAPH."EXTENSION"!");\r
+\r
+\r
+//\r
+// load the pic and sprite headers into the arrays in the data segment\r
+//\r
+#if NUMPICS>0\r
+       MM_GetPtr(&(memptr)pictable,NUMPICS*sizeof(pictabletype));\r
+       CAL_GetGrChunkLength(STRUCTPIC);                // position file pointer\r
+       MM_GetPtr(&compseg,chunkcomplen);\r
+       CA_FarRead (grhandle,compseg,chunkcomplen);\r
+       CAL_HuffExpand (compseg, (byte huge *)pictable,NUMPICS*sizeof(pictabletype),grhuffman);\r
+       MM_FreePtr(&compseg);\r
+#endif\r
+\r
+#if NUMPICM>0\r
+       MM_GetPtr(&(memptr)picmtable,NUMPICM*sizeof(pictabletype));\r
+       CAL_GetGrChunkLength(STRUCTPICM);               // position file pointer\r
+       MM_GetPtr(&compseg,chunkcomplen);\r
+       CA_FarRead (grhandle,compseg,chunkcomplen);\r
+       CAL_HuffExpand (compseg, (byte huge *)picmtable,NUMPICS*sizeof(pictabletype),grhuffman);\r
+       MM_FreePtr(&compseg);\r
+#endif\r
+\r
+#if NUMSPRITES>0\r
+       MM_GetPtr(&(memptr)spritetable,NUMSPRITES*sizeof(spritetabletype));\r
+       CAL_GetGrChunkLength(STRUCTSPRITE);     // position file pointer\r
+       MM_GetPtr(&compseg,chunkcomplen);\r
+       CA_FarRead (grhandle,compseg,chunkcomplen);\r
+       CAL_HuffExpand (compseg, (byte huge *)spritetable,NUMSPRITES*sizeof(spritetabletype),grhuffman);\r
+       MM_FreePtr(&compseg);\r
+#endif\r
+\r
+}\r
+\r
+//==========================================================================\r
+\r
+\r
+/*\r
+======================\r
+=\r
+= CAL_SetupMapFile\r
+=\r
+======================\r
+*/\r
+\r
+void CAL_SetupMapFile (void)\r
+{\r
+       int handle;\r
+       long length;\r
+\r
+//\r
+// load maphead.ext (offsets and tileinfo for map file)\r
+//\r
+#ifndef MAPHEADERLINKED\r
+       if ((handle = open("MAPHEAD."EXTENSION,\r
+                O_RDONLY | O_BINARY, S_IREAD)) == -1)\r
+               Quit ("Can't open MAPHEAD."EXTENSION"!");\r
+       length = filelength(handle);\r
+       MM_GetPtr (&(memptr)tinf,length);\r
+       CA_FarRead(handle, tinf, length);\r
+       close(handle);\r
+#else\r
+\r
+       tinf = (byte _seg *)FP_SEG(&maphead);\r
+\r
+#endif\r
+\r
+//\r
+// open the data file\r
+//\r
+#ifdef MAPHEADERLINKED\r
+       if ((maphandle = open("GAMEMAPS."EXTENSION,\r
+                O_RDONLY | O_BINARY, S_IREAD)) == -1)\r
+               Quit ("Can't open GAMEMAPS."EXTENSION"!");\r
+#else\r
+       if ((maphandle = open("MAPTEMP."EXTENSION,\r
+                O_RDONLY | O_BINARY, S_IREAD)) == -1)\r
+               Quit ("Can't open MAPTEMP."EXTENSION"!");\r
+#endif\r
+}\r
+\r
+//==========================================================================\r
+\r
+\r
+/*\r
+======================\r
+=\r
+= CAL_SetupAudioFile\r
+=\r
+======================\r
+*/\r
+\r
+void CAL_SetupAudioFile (void)\r
+{\r
+       int handle;\r
+       long length;\r
+\r
+//\r
+// load maphead.ext (offsets and tileinfo for map file)\r
+//\r
+#ifndef AUDIOHEADERLINKED\r
+       if ((handle = open("AUDIOHED."EXTENSION,\r
+                O_RDONLY | O_BINARY, S_IREAD)) == -1)\r
+               Quit ("Can't open AUDIOHED."EXTENSION"!");\r
+       length = filelength(handle);\r
+       MM_GetPtr (&(memptr)audiostarts,length);\r
+       CA_FarRead(handle, (byte far *)audiostarts, length);\r
+       close(handle);\r
+#else\r
+       audiohuffman = (huffnode *)&audiodict;\r
+       CAL_OptimizeNodes (audiohuffman);\r
+       audiostarts = (long _seg *)FP_SEG(&audiohead);\r
+#endif\r
+\r
+//\r
+// open the data file\r
+//\r
+#ifndef AUDIOHEADERLINKED\r
+       if ((audiohandle = open("AUDIOT."EXTENSION,\r
+                O_RDONLY | O_BINARY, S_IREAD)) == -1)\r
+               Quit ("Can't open AUDIOT."EXTENSION"!");\r
+#else\r
+       if ((audiohandle = open("AUDIO."EXTENSION,\r
+                O_RDONLY | O_BINARY, S_IREAD)) == -1)\r
+               Quit ("Can't open AUDIO."EXTENSION"!");\r
+#endif\r
+}\r
+\r
+//==========================================================================\r
+\r
+\r
+/*\r
+======================\r
+=\r
+= CA_Startup\r
+=\r
+= Open all files and load in headers\r
+=\r
+======================\r
+*/\r
+\r
+void CA_Startup (void)\r
+{\r
+#ifdef PROFILE\r
+       unlink ("PROFILE.TXT");\r
+       profilehandle = open("PROFILE.TXT", O_CREAT | O_WRONLY | O_TEXT);\r
+#endif\r
+\r
+#ifndef NOMAPS\r
+       CAL_SetupMapFile ();\r
+#endif\r
+#ifndef NOGRAPHICS\r
+       CAL_SetupGrFile ();\r
+#endif\r
+#ifndef NOAUDIO\r
+       CAL_SetupAudioFile ();\r
+#endif\r
+\r
+       mapon = -1;\r
+       ca_levelbit = 1;\r
+       ca_levelnum = 0;\r
+\r
+       drawcachebox    = CAL_DialogDraw;\r
+       updatecachebox  = CAL_DialogUpdate;\r
+       finishcachebox  = CAL_DialogFinish;\r
+}\r
+\r
+//==========================================================================\r
+\r
+\r
+/*\r
+======================\r
+=\r
+= CA_Shutdown\r
+=\r
+= Closes all files\r
+=\r
+======================\r
+*/\r
+\r
+void CA_Shutdown (void)\r
+{\r
+#ifdef PROFILE\r
+       close (profilehandle);\r
+#endif\r
+\r
+       close (maphandle);\r
+       close (grhandle);\r
+       close (audiohandle);\r
+}\r
+\r
+//===========================================================================\r
+\r
+/*\r
+======================\r
+=\r
+= CA_CacheAudioChunk\r
+=\r
+======================\r
+*/\r
+\r
+void CA_CacheAudioChunk (int chunk)\r
+{\r
+       long    pos,compressed;\r
+#ifdef AUDIOHEADERLINKED\r
+       long    expanded;\r
+       memptr  bigbufferseg;\r
+       byte    far *source;\r
+#endif\r
+\r
+       if (audiosegs[chunk])\r
+       {\r
+               MM_SetPurge (&(memptr)audiosegs[chunk],0);\r
+               return;                                                 // allready in memory\r
+       }\r
+\r
+//\r
+// load the chunk into a buffer, either the miscbuffer if it fits, or allocate\r
+// a larger buffer\r
+//\r
+       pos = audiostarts[chunk];\r
+       compressed = audiostarts[chunk+1]-pos;\r
+\r
+       lseek(audiohandle,pos,SEEK_SET);\r
+\r
+#ifndef AUDIOHEADERLINKED\r
+\r
+       MM_GetPtr (&(memptr)audiosegs[chunk],compressed);\r
+       if (mmerror)\r
+               return;\r
+\r
+       CA_FarRead(audiohandle,audiosegs[chunk],compressed);\r
+\r
+#else\r
+\r
+       if (compressed<=BUFFERSIZE)\r
+       {\r
+               CA_FarRead(audiohandle,bufferseg,compressed);\r
+               source = bufferseg;\r
+       }\r
+       else\r
+       {\r
+               MM_GetPtr(&bigbufferseg,compressed);\r
+               if (mmerror)\r
+                       return;\r
+               MM_SetLock (&bigbufferseg,true);\r
+               CA_FarRead(audiohandle,bigbufferseg,compressed);\r
+               source = bigbufferseg;\r
+       }\r
+\r
+       expanded = *(long far *)source;\r
+       source += 4;                    // skip over length\r
+       MM_GetPtr (&(memptr)audiosegs[chunk],expanded);\r
+       if (mmerror)\r
+               goto done;\r
+       CAL_HuffExpand (source,audiosegs[chunk],expanded,audiohuffman);\r
+\r
+done:\r
+       if (compressed>BUFFERSIZE)\r
+               MM_FreePtr(&bigbufferseg);\r
+#endif\r
+}\r
+\r
+//===========================================================================\r
+\r
+/*\r
+======================\r
+=\r
+= CA_LoadAllSounds\r
+=\r
+= Purges all sounds, then loads all new ones (mode switch)\r
+=\r
+======================\r
+*/\r
+\r
+void CA_LoadAllSounds (void)\r
+{\r
+       unsigned        start,i;\r
+\r
+       switch (oldsoundmode)\r
+       {\r
+       case sdm_Off:\r
+               goto cachein;\r
+       case sdm_PC:\r
+               start = STARTPCSOUNDS;\r
+               break;\r
+       case sdm_AdLib:\r
+               start = STARTADLIBSOUNDS;\r
+               break;\r
+       }\r
+\r
+       for (i=0;i<NUMSOUNDS;i++,start++)\r
+               if (audiosegs[start])\r
+                       MM_SetPurge (&(memptr)audiosegs[start],3);              // make purgable\r
+\r
+cachein:\r
+\r
+       switch (SoundMode)\r
+       {\r
+       case sdm_Off:\r
+               return;\r
+       case sdm_PC:\r
+               start = STARTPCSOUNDS;\r
+               break;\r
+       case sdm_AdLib:\r
+               start = STARTADLIBSOUNDS;\r
+               break;\r
+       }\r
+\r
+       for (i=0;i<NUMSOUNDS;i++,start++)\r
+               CA_CacheAudioChunk (start);\r
+\r
+       oldsoundmode = SoundMode;\r
+}\r
+\r
+//===========================================================================\r
+\r
+#if GRMODE == EGAGR\r
+\r
+/*\r
+======================\r
+=\r
+= CAL_ShiftSprite\r
+=\r
+= Make a shifted (one byte wider) copy of a sprite into another area\r
+=\r
+======================\r
+*/\r
+\r
+unsigned       static  sheight,swidth;\r
+\r
+void CAL_ShiftSprite (unsigned segment,unsigned source,unsigned dest,\r
+       unsigned width, unsigned height, unsigned pixshift)\r
+{\r
+\r
+       sheight = height;               // because we are going to reassign bp\r
+       swidth = width;\r
+\r
+asm    mov     ax,[segment]\r
+asm    mov     ds,ax           // source and dest are in same segment, and all local\r
+\r
+asm    mov     bx,[source]\r
+asm    mov     di,[dest]\r
+\r
+asm    mov     bp,[pixshift]\r
+asm    shl     bp,1\r
+asm    mov     bp,WORD PTR [shifttabletable+bp]        // bp holds pointer to shift table\r
+\r
+//\r
+// table shift the mask\r
+//\r
+asm    mov     dx,[ss:sheight]\r
+\r
+domaskrow:\r
+\r
+asm    mov     BYTE PTR [di],255       // 0xff first byte\r
+asm    mov     cx,ss:[swidth]\r
+\r
+domaskbyte:\r
+\r
+asm    mov     al,[bx]                         // source\r
+asm    not     al\r
+asm    inc     bx                                      // next source byte\r
+asm    xor     ah,ah\r
+asm    shl     ax,1\r
+asm    mov     si,ax\r
+asm    mov     ax,[bp+si]                      // table shift into two bytes\r
+asm    not     ax\r
+asm    and     [di],al                         // and with first byte\r
+asm    inc     di\r
+asm    mov     [di],ah                         // replace next byte\r
+\r
+asm    loop    domaskbyte\r
+\r
+asm    inc     di                                      // the last shifted byte has 1s in it\r
+asm    dec     dx\r
+asm    jnz     domaskrow\r
+\r
+//\r
+// table shift the data\r
+//\r
+asm    mov     dx,ss:[sheight]\r
+asm    shl     dx,1\r
+asm    shl     dx,1                            // four planes of data\r
+\r
+dodatarow:\r
+\r
+asm    mov     BYTE PTR [di],0         // 0 first byte\r
+asm    mov     cx,ss:[swidth]\r
+\r
+dodatabyte:\r
+\r
+asm    mov     al,[bx]                         // source\r
+asm    inc     bx                                      // next source byte\r
+asm    xor     ah,ah\r
+asm    shl     ax,1\r
+asm    mov     si,ax\r
+asm    mov     ax,[bp+si]                      // table shift into two bytes\r
+asm    or      [di],al                         // or with first byte\r
+asm    inc     di\r
+asm    mov     [di],ah                         // replace next byte\r
+\r
+asm    loop    dodatabyte\r
+\r
+asm    inc     di                                      // the last shifted byte has 0s in it\r
+asm    dec     dx\r
+asm    jnz     dodatarow\r
+\r
+//\r
+// done\r
+//\r
+\r
+asm    mov     ax,ss                           // restore data segment\r
+asm    mov     ds,ax\r
+\r
+}\r
+\r
+#endif\r
+\r
+//===========================================================================\r
+\r
+/*\r
+======================\r
+=\r
+= CAL_CacheSprite\r
+=\r
+= Generate shifts and set up sprite structure for a given sprite\r
+=\r
+======================\r
+*/\r
+\r
+void CAL_CacheSprite (int chunk, byte far *compressed)\r
+{\r
+       int i;\r
+       unsigned shiftstarts[5];\r
+       unsigned smallplane,bigplane,expanded;\r
+       spritetabletype far *spr;\r
+       spritetype _seg *dest;\r
+\r
+#if GRMODE == CGAGR\r
+//\r
+// CGA has no pel panning, so shifts are never needed\r
+//\r
+       spr = &spritetable[chunk-STARTSPRITES];\r
+       smallplane = spr->width*spr->height;\r
+       MM_GetPtr (&grsegs[chunk],smallplane*2+MAXSHIFTS*6);\r
+       if (mmerror)\r
+               return;\r
+       dest = (spritetype _seg *)grsegs[chunk];\r
+       dest->sourceoffset[0] = MAXSHIFTS*6;    // start data after 3 unsigned tables\r
+       dest->planesize[0] = smallplane;\r
+       dest->width[0] = spr->width;\r
+\r
+//\r
+// expand the unshifted shape\r
+//\r
+       CAL_HuffExpand (compressed, &dest->data[0],smallplane*2,grhuffman);\r
+\r
+#endif\r
+\r
+\r
+#if GRMODE == EGAGR\r
+\r
+//\r
+// calculate sizes\r
+//\r
+       spr = &spritetable[chunk-STARTSPRITES];\r
+       smallplane = spr->width*spr->height;\r
+       bigplane = (spr->width+1)*spr->height;\r
+\r
+       shiftstarts[0] = MAXSHIFTS*6;   // start data after 3 unsigned tables\r
+       shiftstarts[1] = shiftstarts[0] + smallplane*5; // 5 planes in a sprite\r
+       shiftstarts[2] = shiftstarts[1] + bigplane*5;\r
+       shiftstarts[3] = shiftstarts[2] + bigplane*5;\r
+       shiftstarts[4] = shiftstarts[3] + bigplane*5;   // nothing ever put here\r
+\r
+       expanded = shiftstarts[spr->shifts];\r
+       MM_GetPtr (&grsegs[chunk],expanded);\r
+       if (mmerror)\r
+               return;\r
+       dest = (spritetype _seg *)grsegs[chunk];\r
+\r
+//\r
+// expand the unshifted shape\r
+//\r
+       CAL_HuffExpand (compressed, &dest->data[0],smallplane*5,grhuffman);\r
+\r
+//\r
+// make the shifts!\r
+//\r
+       switch (spr->shifts)\r
+       {\r
+       case    1:\r
+               for (i=0;i<4;i++)\r
+               {\r
+                       dest->sourceoffset[i] = shiftstarts[0];\r
+                       dest->planesize[i] = smallplane;\r
+                       dest->width[i] = spr->width;\r
+               }\r
+               break;\r
+\r
+       case    2:\r
+               for (i=0;i<2;i++)\r
+               {\r
+                       dest->sourceoffset[i] = shiftstarts[0];\r
+                       dest->planesize[i] = smallplane;\r
+                       dest->width[i] = spr->width;\r
+               }\r
+               for (i=2;i<4;i++)\r
+               {\r
+                       dest->sourceoffset[i] = shiftstarts[1];\r
+                       dest->planesize[i] = bigplane;\r
+                       dest->width[i] = spr->width+1;\r
+               }\r
+               CAL_ShiftSprite ((unsigned)grsegs[chunk],dest->sourceoffset[0],\r
+                       dest->sourceoffset[2],spr->width,spr->height,4);\r
+               break;\r
+\r
+       case    4:\r
+               dest->sourceoffset[0] = shiftstarts[0];\r
+               dest->planesize[0] = smallplane;\r
+               dest->width[0] = spr->width;\r
+\r
+               dest->sourceoffset[1] = shiftstarts[1];\r
+               dest->planesize[1] = bigplane;\r
+               dest->width[1] = spr->width+1;\r
+               CAL_ShiftSprite ((unsigned)grsegs[chunk],dest->sourceoffset[0],\r
+                       dest->sourceoffset[1],spr->width,spr->height,2);\r
+\r
+               dest->sourceoffset[2] = shiftstarts[2];\r
+               dest->planesize[2] = bigplane;\r
+               dest->width[2] = spr->width+1;\r
+               CAL_ShiftSprite ((unsigned)grsegs[chunk],dest->sourceoffset[0],\r
+                       dest->sourceoffset[2],spr->width,spr->height,4);\r
+\r
+               dest->sourceoffset[3] = shiftstarts[3];\r
+               dest->planesize[3] = bigplane;\r
+               dest->width[3] = spr->width+1;\r
+               CAL_ShiftSprite ((unsigned)grsegs[chunk],dest->sourceoffset[0],\r
+                       dest->sourceoffset[3],spr->width,spr->height,6);\r
+\r
+               break;\r
+\r
+       default:\r
+               Quit ("CAL_CacheSprite: Bad shifts number!");\r
+       }\r
+\r
+#endif\r
+}\r
+\r
+//===========================================================================\r
+\r
+\r
+/*\r
+======================\r
+=\r
+= CAL_ExpandGrChunk\r
+=\r
+= Does whatever is needed with a pointer to a compressed chunk\r
+=\r
+======================\r
+*/\r
+\r
+void CAL_ExpandGrChunk (int chunk, byte far *source)\r
+{\r
+       long    expanded;\r
+\r
+\r
+       if (chunk >= STARTTILE8 && chunk < STARTEXTERNS)\r
+       {\r
+       //\r
+       // expanded sizes of tile8/16/32 are implicit\r
+       //\r
+\r
+#if GRMODE == EGAGR\r
+#define BLOCK          32\r
+#define MASKBLOCK      40\r
+#endif\r
+\r
+#if GRMODE == CGAGR\r
+#define BLOCK          16\r
+#define MASKBLOCK      32\r
+#endif\r
+\r
+               if (chunk<STARTTILE8M)                  // tile 8s are all in one chunk!\r
+                       expanded = BLOCK*NUMTILE8;\r
+               else if (chunk<STARTTILE16)\r
+                       expanded = MASKBLOCK*NUMTILE8M;\r
+               else if (chunk<STARTTILE16M)    // all other tiles are one/chunk\r
+                       expanded = BLOCK*4;\r
+               else if (chunk<STARTTILE32)\r
+                       expanded = MASKBLOCK*4;\r
+               else if (chunk<STARTTILE32M)\r
+                       expanded = BLOCK*16;\r
+               else\r
+                       expanded = MASKBLOCK*16;\r
+       }\r
+       else\r
+       {\r
+       //\r
+       // everything else has an explicit size longword\r
+       //\r
+               expanded = *(long far *)source;\r
+               source += 4;                    // skip over length\r
+       }\r
+\r
+//\r
+// allocate final space, decompress it, and free bigbuffer\r
+// Sprites need to have shifts made and various other junk\r
+//\r
+       if (chunk>=STARTSPRITES && chunk< STARTTILE8)\r
+               CAL_CacheSprite(chunk,source);\r
+       else\r
+       {\r
+               MM_GetPtr (&grsegs[chunk],expanded);\r
+               if (mmerror)\r
+                       return;\r
+               CAL_HuffExpand (source,grsegs[chunk],expanded,grhuffman);\r
+       }\r
+}\r
+\r
+\r
+/*\r
+======================\r
+=\r
+= CAL_ReadGrChunk\r
+=\r
+= Gets a chunk off disk, optimizing reads to general buffer\r
+=\r
+======================\r
+*/\r
+\r
+void CAL_ReadGrChunk (int chunk)\r
+{\r
+       long    pos,compressed;\r
+       memptr  bigbufferseg;\r
+       byte    far *source;\r
+       int             next;\r
+\r
+//\r
+// load the chunk into a buffer, either the miscbuffer if it fits, or allocate\r
+// a larger buffer\r
+//\r
+       pos = GRFILEPOS(chunk);\r
+       if (pos<0)                                                      // $FFFFFFFF start is a sparse tile\r
+         return;\r
+\r
+       next = chunk +1;\r
+       while (GRFILEPOS(next) == -1)           // skip past any sparse tiles\r
+               next++;\r
+\r
+       compressed = GRFILEPOS(next)-pos;\r
+\r
+       lseek(grhandle,pos,SEEK_SET);\r
+\r
+       if (compressed<=BUFFERSIZE)\r
+       {\r
+               CA_FarRead(grhandle,bufferseg,compressed);\r
+               source = bufferseg;\r
+       }\r
+       else\r
+       {\r
+               MM_GetPtr(&bigbufferseg,compressed);\r
+               if (mmerror)\r
+                       return;\r
+               MM_SetLock (&bigbufferseg,true);\r
+               CA_FarRead(grhandle,bigbufferseg,compressed);\r
+               source = bigbufferseg;\r
+       }\r
+\r
+       CAL_ExpandGrChunk (chunk,source);\r
+\r
+       if (compressed>BUFFERSIZE)\r
+               MM_FreePtr(&bigbufferseg);\r
+}\r
+\r
+\r
+/*\r
+======================\r
+=\r
+= CA_CacheGrChunk\r
+=\r
+= Makes sure a given chunk is in memory, loadiing it if needed\r
+=\r
+======================\r
+*/\r
+\r
+void CA_CacheGrChunk (int chunk)\r
+{\r
+       long    pos,compressed;\r
+       memptr  bigbufferseg;\r
+       byte    far *source;\r
+       int             next;\r
+\r
+       grneeded[chunk] |= ca_levelbit;         // make sure it doesn't get removed\r
+       if (grsegs[chunk])\r
+       {\r
+               MM_SetPurge (&grsegs[chunk],0);\r
+               return;                                                 // allready in memory\r
+       }\r
+\r
+//\r
+// load the chunk into a buffer, either the miscbuffer if it fits, or allocate\r
+// a larger buffer\r
+//\r
+       pos = GRFILEPOS(chunk);\r
+       if (pos<0)                                                      // $FFFFFFFF start is a sparse tile\r
+         return;\r
+\r
+       next = chunk +1;\r
+       while (GRFILEPOS(next) == -1)           // skip past any sparse tiles\r
+               next++;\r
+\r
+       compressed = GRFILEPOS(next)-pos;\r
+\r
+       lseek(grhandle,pos,SEEK_SET);\r
+\r
+       if (compressed<=BUFFERSIZE)\r
+       {\r
+               CA_FarRead(grhandle,bufferseg,compressed);\r
+               source = bufferseg;\r
+       }\r
+       else\r
+       {\r
+               MM_GetPtr(&bigbufferseg,compressed);\r
+               MM_SetLock (&bigbufferseg,true);\r
+               CA_FarRead(grhandle,bigbufferseg,compressed);\r
+               source = bigbufferseg;\r
+       }\r
+\r
+       CAL_ExpandGrChunk (chunk,source);\r
+\r
+       if (compressed>BUFFERSIZE)\r
+               MM_FreePtr(&bigbufferseg);\r
+}\r
+\r
+\r
+\r
+//==========================================================================\r
+\r
+/*\r
+======================\r
+=\r
+= CA_CacheMap\r
+=\r
+======================\r
+*/\r
+\r
+void CA_CacheMap (int mapnum)\r
+{\r
+       long    pos,compressed;\r
+       int             plane;\r
+       memptr  *dest,bigbufferseg;\r
+       unsigned        size;\r
+       unsigned        far     *source;\r
+#ifdef MAPHEADERLINKED\r
+       memptr  buffer2seg;\r
+       long    expanded;\r
+#endif\r
+\r
+\r
+//\r
+// free up memory from last map\r
+//\r
+       if (mapon>-1 && mapheaderseg[mapon])\r
+               MM_SetPurge (&(memptr)mapheaderseg[mapon],3);\r
+       for (plane=0;plane<MAPPLANES;plane++)\r
+               if (mapsegs[plane])\r
+                       MM_FreePtr (&(memptr)mapsegs[plane]);\r
+\r
+       mapon = mapnum;\r
+\r
+\r
+//\r
+// load map header\r
+// The header will be cached if it is still around\r
+//\r
+       if (!mapheaderseg[mapnum])\r
+       {\r
+               pos = ((mapfiletype     _seg *)tinf)->headeroffsets[mapnum];\r
+               if (pos<0)                                              // $FFFFFFFF start is a sparse map\r
+                 Quit ("CA_CacheMap: Tried to load a non existent map!");\r
+\r
+               MM_GetPtr(&(memptr)mapheaderseg[mapnum],sizeof(maptype));\r
+               lseek(maphandle,pos,SEEK_SET);\r
+               CA_FarRead (maphandle,(memptr)mapheaderseg[mapnum],sizeof(maptype));\r
+       }\r
+       else\r
+               MM_SetPurge (&(memptr)mapheaderseg[mapnum],0);\r
+\r
+//\r
+// load the planes in\r
+// If a plane's pointer still exists it will be overwritten (levels are\r
+// allways reloaded, never cached)\r
+//\r
+\r
+       size = mapheaderseg[mapnum]->width * mapheaderseg[mapnum]->height * 2;\r
+\r
+       for (plane = 0; plane<MAPPLANES; plane++)\r
+       {\r
+               pos = mapheaderseg[mapnum]->planestart[plane];\r
+               compressed = mapheaderseg[mapnum]->planelength[plane];\r
+\r
+               if (!compressed)\r
+                       continue;               // the plane is not used in this game\r
+\r
+               dest = &(memptr)mapsegs[plane];\r
+               MM_GetPtr(dest,size);\r
+\r
+               lseek(maphandle,pos,SEEK_SET);\r
+               if (compressed<=BUFFERSIZE)\r
+                       source = bufferseg;\r
+               else\r
+               {\r
+                       MM_GetPtr(&bigbufferseg,compressed);\r
+                       MM_SetLock (&bigbufferseg,true);\r
+                       source = bigbufferseg;\r
+               }\r
+\r
+               CA_FarRead(maphandle,(byte far *)source,compressed);\r
+#ifdef MAPHEADERLINKED\r
+               //\r
+               // unhuffman, then unRLEW\r
+               // The huffman'd chunk has a two byte expanded length first\r
+               // The resulting RLEW chunk also does, even though it's not really\r
+               // needed\r
+               //\r
+               expanded = *source;\r
+               source++;\r
+               MM_GetPtr (&buffer2seg,expanded);\r
+               CAL_CarmackExpand (source, (unsigned far *)buffer2seg,expanded);\r
+               CA_RLEWexpand (((unsigned far *)buffer2seg)+1,*dest,size,\r
+               ((mapfiletype _seg *)tinf)->RLEWtag);\r
+               MM_FreePtr (&buffer2seg);\r
+\r
+#else\r
+               //\r
+               // unRLEW, skipping expanded length\r
+               //\r
+               CA_RLEWexpand (source+1, *dest,size,\r
+               ((mapfiletype _seg *)tinf)->RLEWtag);\r
+#endif\r
+\r
+               if (compressed>BUFFERSIZE)\r
+                       MM_FreePtr(&bigbufferseg);\r
+       }\r
+}\r
+\r
+//===========================================================================\r
+\r
+/*\r
+======================\r
+=\r
+= CA_UpLevel\r
+=\r
+= Goes up a bit level in the needed lists and clears it out.\r
+= Everything is made purgable\r
+=\r
+======================\r
+*/\r
+\r
+void CA_UpLevel (void)\r
+{\r
+       if (ca_levelnum==7)\r
+               Quit ("CA_UpLevel: Up past level 7!");\r
+\r
+       ca_levelbit<<=1;\r
+       ca_levelnum++;\r
+}\r
+\r
+//===========================================================================\r
+\r
+/*\r
+======================\r
+=\r
+= CA_DownLevel\r
+=\r
+= Goes down a bit level in the needed lists and recaches\r
+= everything from the lower level\r
+=\r
+======================\r
+*/\r
+\r
+void CA_DownLevel (void)\r
+{\r
+       if (!ca_levelnum)\r
+               Quit ("CA_DownLevel: Down past level 0!");\r
+       ca_levelbit>>=1;\r
+       ca_levelnum--;\r
+       CA_CacheMarks(NULL);\r
+}\r
+\r
+//===========================================================================\r
+\r
+/*\r
+======================\r
+=\r
+= CA_ClearMarks\r
+=\r
+= Clears out all the marks at the current level\r
+=\r
+======================\r
+*/\r
+\r
+void CA_ClearMarks (void)\r
+{\r
+       int i;\r
+\r
+       for (i=0;i<NUMCHUNKS;i++)\r
+               grneeded[i]&=~ca_levelbit;\r
+}\r
+\r
+\r
+//===========================================================================\r
+\r
+/*\r
+======================\r
+=\r
+= CA_ClearAllMarks\r
+=\r
+= Clears out all the marks on all the levels\r
+=\r
+======================\r
+*/\r
+\r
+void CA_ClearAllMarks (void)\r
+{\r
+       _fmemset (grneeded,0,sizeof(grneeded));\r
+       ca_levelbit = 1;\r
+       ca_levelnum = 0;\r
+}\r
+\r
+\r
+//===========================================================================\r
+\r
+/*\r
+======================\r
+=\r
+= CA_FreeGraphics\r
+=\r
+======================\r
+*/\r
+\r
+void CA_FreeGraphics (void)\r
+{\r
+       int     i;\r
+\r
+       for (i=0;i<NUMCHUNKS;i++)\r
+               if (grsegs[i])\r
+                       MM_SetPurge (&(memptr)grsegs[i],3);\r
+}\r
+\r
+\r
+/*\r
+======================\r
+=\r
+= CA_SetAllPurge\r
+=\r
+= Make everything possible purgable\r
+=\r
+======================\r
+*/\r
+\r
+void CA_SetAllPurge (void)\r
+{\r
+       int i;\r
+\r
+       CA_ClearMarks ();\r
+\r
+//\r
+// free cursor sprite and background save\r
+//\r
+       VW_FreeCursor ();\r
+\r
+//\r
+// free map headers and map planes\r
+//\r
+       for (i=0;i<NUMMAPS;i++)\r
+               if (mapheaderseg[i])\r
+                       MM_SetPurge (&(memptr)mapheaderseg[i],3);\r
+\r
+       for (i=0;i<3;i++)\r
+               if (mapsegs[i])\r
+                       MM_FreePtr (&(memptr)mapsegs[i]);\r
+\r
+//\r
+// free sounds\r
+//\r
+       for (i=0;i<NUMSNDCHUNKS;i++)\r
+               if (audiosegs[i])\r
+                       MM_SetPurge (&(memptr)audiosegs[i],3);\r
+\r
+//\r
+// free graphics\r
+//\r
+       CA_FreeGraphics ();\r
+}\r
+\r
+\r
+void CA_SetGrPurge (void)\r
+{\r
+       int i;\r
+\r
+//\r
+// free graphics\r
+//\r
+       for (i=0;i<NUMCHUNKS;i++)\r
+               if (grsegs[i])\r
+                       MM_SetPurge (&(memptr)grsegs[i],3);\r
+}\r
+\r
+\r
+//===========================================================================\r
+\r
+\r
+/*\r
+======================\r
+=\r
+= CAL_DialogDraw\r
+=\r
+======================\r
+*/\r
+\r
+#define NUMBARS        (17l*8)\r
+#define BARSTEP        8\r
+\r
+unsigned       thx,thy,lastx;\r
+long           barx,barstep;\r
+\r
+void   CAL_DialogDraw (char *title,unsigned numcache)\r
+{\r
+       unsigned        homex,homey,x;\r
+\r
+       barstep = (NUMBARS<<16)/numcache;\r
+\r
+//\r
+// draw dialog window (masked tiles 12 - 20 are window borders)\r
+//\r
+       US_CenterWindow (20,8);\r
+       homex = PrintX;\r
+       homey = PrintY;\r
+\r
+       US_CPrint ("Loading");\r
+       fontcolor = F_SECONDCOLOR;\r
+       US_CPrint (title);\r
+       fontcolor = F_BLACK;\r
+\r
+//\r
+// draw thermometer bar\r
+//\r
+       thx = homex + 8;\r
+       thy = homey + 32;\r
+#ifdef CAT3D\r
+       VWB_DrawTile8(thx,thy,0);               // CAT3D numbers\r
+       VWB_DrawTile8(thx,thy+8,3);\r
+       VWB_DrawTile8(thx,thy+16,6);\r
+       VWB_DrawTile8(thx+17*8,thy,2);\r
+       VWB_DrawTile8(thx+17*8,thy+8,5);\r
+       VWB_DrawTile8(thx+17*8,thy+16,8);\r
+       for (x=thx+8;x<thx+17*8;x+=8)\r
+       {\r
+               VWB_DrawTile8(x,thy,1);\r
+               VWB_DrawTile8(x,thy+8,4);\r
+               VWB_DrawTile8(x,thy+16,7);\r
+       }\r
+#else\r
+       VWB_DrawTile8(thx,thy,11);              // KEEN numbers\r
+       VWB_DrawTile8(thx,thy+8,14);\r
+       VWB_DrawTile8(thx,thy+16,17);\r
+       VWB_DrawTile8(thx+17*8,thy,13);\r
+       VWB_DrawTile8(thx+17*8,thy+8,16);\r
+       VWB_DrawTile8(thx+17*8,thy+16,19);\r
+       for (x=thx+8;x<thx+17*8;x+=8)\r
+       {\r
+               VWB_DrawTile8(x,thy,12);\r
+               VWB_DrawTile8(x,thy+8,15);\r
+               VWB_DrawTile8(x,thy+16,18);\r
+       }\r
+#endif\r
+\r
+       thx += 4;               // first line location\r
+       thy += 5;\r
+       barx = (long)thx<<16;\r
+       lastx = thx;\r
+\r
+       VW_UpdateScreen();\r
+}\r
+\r
+\r
+/*\r
+======================\r
+=\r
+= CAL_DialogUpdate\r
+=\r
+======================\r
+*/\r
+\r
+void   CAL_DialogUpdate (void)\r
+{\r
+       unsigned        x,xh;\r
+\r
+       barx+=barstep;\r
+       xh = barx>>16;\r
+       if (xh - lastx > BARSTEP)\r
+       {\r
+               for (x=lastx;x<=xh;x++)\r
+#if GRMODE == EGAGR\r
+                       VWB_Vlin (thy,thy+13,x,14);\r
+#endif\r
+#if GRMODE == CGAGR\r
+                       VWB_Vlin (thy,thy+13,x,SECONDCOLOR);\r
+#endif\r
+               lastx = xh;\r
+               VW_UpdateScreen();\r
+       }\r
+}\r
+\r
+/*\r
+======================\r
+=\r
+= CAL_DialogFinish\r
+=\r
+======================\r
+*/\r
+\r
+void   CAL_DialogFinish (void)\r
+{\r
+       unsigned        x,xh;\r
+\r
+       xh = thx + NUMBARS;\r
+       for (x=lastx;x<=xh;x++)\r
+#if GRMODE == EGAGR\r
+               VWB_Vlin (thy,thy+13,x,14);\r
+#endif\r
+#if GRMODE == CGAGR\r
+               VWB_Vlin (thy,thy+13,x,SECONDCOLOR);\r
+#endif\r
+       VW_UpdateScreen();\r
+\r
+}\r
+\r
+//===========================================================================\r
+\r
+/*\r
+======================\r
+=\r
+= CA_CacheMarks\r
+=\r
+======================\r
+*/\r
+#define MAXEMPTYREAD   1024\r
+\r
+void CA_CacheMarks (char *title)\r
+{\r
+       boolean dialog;\r
+       int     i,next,numcache;\r
+       long    pos,endpos,nextpos,nextendpos,compressed;\r
+       long    bufferstart,bufferend;  // file position of general buffer\r
+       byte    far *source;\r
+       memptr  bigbufferseg;\r
+\r
+       dialog = (title!=NULL);\r
+\r
+       numcache = 0;\r
+//\r
+// go through and make everything not needed purgable\r
+//\r
+       for (i=0;i<NUMCHUNKS;i++)\r
+               if (grneeded[i]&ca_levelbit)\r
+               {\r
+                       if (grsegs[i])                                  // its allready in memory, make\r
+                               MM_SetPurge(&grsegs[i],0);      // sure it stays there!\r
+                       else\r
+                               numcache++;\r
+               }\r
+               else\r
+               {\r
+                       if (grsegs[i])                                  // not needed, so make it purgeable\r
+                               MM_SetPurge(&grsegs[i],3);\r
+               }\r
+\r
+       if (!numcache)                  // nothing to cache!\r
+               return;\r
+\r
+       if (dialog)\r
+       {\r
+#ifdef PROFILE\r
+               write(profilehandle,title,strlen(title));\r
+               write(profilehandle,"\n",1);\r
+#endif\r
+               if (drawcachebox)\r
+                       drawcachebox(title,numcache);\r
+       }\r
+\r
+//\r
+// go through and load in anything still needed\r
+//\r
+       bufferstart = bufferend = 0;            // nothing good in buffer now\r
+\r
+       for (i=0;i<NUMCHUNKS;i++)\r
+               if ( (grneeded[i]&ca_levelbit) && !grsegs[i])\r
+               {\r
+//\r
+// update thermometer\r
+//\r
+                       if (dialog && updatecachebox)\r
+                               updatecachebox ();\r
+\r
+                       pos = GRFILEPOS(i);\r
+                       if (pos<0)\r
+                               continue;\r
+\r
+                       next = i +1;\r
+                       while (GRFILEPOS(next) == -1)           // skip past any sparse tiles\r
+                               next++;\r
+\r
+                       compressed = GRFILEPOS(next)-pos;\r
+                       endpos = pos+compressed;\r
+\r
+                       if (compressed<=BUFFERSIZE)\r
+                       {\r
+                               if (bufferstart<=pos\r
+                               && bufferend>= endpos)\r
+                               {\r
+                               // data is allready in buffer\r
+                                       source = (byte _seg *)bufferseg+(pos-bufferstart);\r
+                               }\r
+                               else\r
+                               {\r
+                               // load buffer with a new block from disk\r
+                               // try to get as many of the needed blocks in as possible\r
+                                       while ( next < NUMCHUNKS )\r
+                                       {\r
+                                               while (next < NUMCHUNKS &&\r
+                                               !(grneeded[next]&ca_levelbit && !grsegs[next]))\r
+                                                       next++;\r
+                                               if (next == NUMCHUNKS)\r
+                                                       continue;\r
+\r
+                                               nextpos = GRFILEPOS(next);\r
+                                               while (GRFILEPOS(++next) == -1) // skip past any sparse tiles\r
+                                                       ;\r
+                                               nextendpos = GRFILEPOS(next);\r
+                                               if (nextpos - endpos <= MAXEMPTYREAD\r
+                                               && nextendpos-pos <= BUFFERSIZE)\r
+                                                       endpos = nextendpos;\r
+                                               else\r
+                                                       next = NUMCHUNKS;                       // read pos to posend\r
+                                       }\r
+\r
+                                       lseek(grhandle,pos,SEEK_SET);\r
+                                       CA_FarRead(grhandle,bufferseg,endpos-pos);\r
+                                       bufferstart = pos;\r
+                                       bufferend = endpos;\r
+                                       source = bufferseg;\r
+                               }\r
+                       }\r
+                       else\r
+                       {\r
+                       // big chunk, allocate temporary buffer\r
+                               MM_GetPtr(&bigbufferseg,compressed);\r
+                               if (mmerror)\r
+                                       return;\r
+                               MM_SetLock (&bigbufferseg,true);\r
+                               lseek(grhandle,pos,SEEK_SET);\r
+                               CA_FarRead(grhandle,bigbufferseg,compressed);\r
+                               source = bigbufferseg;\r
+                       }\r
+\r
+                       CAL_ExpandGrChunk (i,source);\r
+                       if (mmerror)\r
+                               return;\r
+\r
+                       if (compressed>BUFFERSIZE)\r
+                               MM_FreePtr(&bigbufferseg);\r
+\r
+               }\r
+\r
+//\r
+// finish up any thermometer remnants\r
+//\r
+               if (dialog && finishcachebox)\r
+                       finishcachebox();\r
+}\r
+\r
diff --git a/16/keen456/KEEN4-6/ID_CA.H b/16/keen456/KEEN4-6/ID_CA.H
new file mode 100755 (executable)
index 0000000..a173787
--- /dev/null
@@ -0,0 +1,152 @@
+/* Reconstructed Commander Keen 4-6 Source Code\r
+ * Copyright (C) 2021 K1n9_Duk3\r
+ *\r
+ * This file is primarily based on:\r
+ * Catacomb 3-D Source Code\r
+ * Copyright (C) 1993-2014 Flat Rock Software\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License along\r
+ * with this program; if not, write to the Free Software Foundation, Inc.,\r
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+ */\r
+\r
+// ID_CA.H\r
+\r
+#ifndef __TYPES__\r
+#include "ID_TYPES.H"\r
+#endif\r
+\r
+#ifndef __ID_MM__\r
+#include "ID_MM.H"\r
+#endif\r
+\r
+#ifndef __ID_GLOB__\r
+#include "ID_GLOB.H"\r
+#endif\r
+\r
+#define __ID_CA__\r
+\r
+//===========================================================================\r
+\r
+//#define NOMAPS\r
+//#define NOGRAPHICS\r
+//#define NOAUDIO\r
+\r
+#define MAPHEADERLINKED\r
+#define GRHEADERLINKED\r
+#define AUDIOHEADERLINKED\r
+\r
+#define NUMMAPS                30\r
+#define MAPPLANES      3\r
+\r
+#ifndef CAT3D\r
+//\r
+// tile info defines, as bytes after tinf the table starts\r
+//\r
+\r
+\r
+//\r
+// TILEINFO offsets\r
+//\r
+#define SPEED          402\r
+#define ANIM           (SPEED+NUMTILE16)\r
+\r
+//\r
+// TILEINFOM offsets\r
+//\r
+#define NORTHWALL              (ANIM+NUMTILE16)\r
+#define EASTWALL               (NORTHWALL+NUMTILE16M)\r
+#define SOUTHWALL              (EASTWALL+NUMTILE16M)\r
+#define WESTWALL               (SOUTHWALL+NUMTILE16M)\r
+#define MANIM          (WESTWALL+NUMTILE16M)\r
+#define INTILE         (MANIM+NUMTILE16M)\r
+#define MSPEED         (INTILE+NUMTILE16M)\r
+#endif\r
+\r
+//===========================================================================\r
+\r
+typedef        struct\r
+{\r
+       long            planestart[3];\r
+       unsigned        planelength[3];\r
+       unsigned        width,height;\r
+       char            name[16];\r
+} maptype;\r
+\r
+//===========================================================================\r
+\r
+extern byte            _seg    *tinf;\r
+extern int                     mapon;\r
+\r
+extern unsigned        _seg    *mapsegs[3];\r
+extern maptype         _seg    *mapheaderseg[NUMMAPS];\r
+extern byte            _seg    *audiosegs[NUMSNDCHUNKS];\r
+extern void            _seg    *grsegs[NUMCHUNKS];\r
+\r
+extern byte            far     grneeded[NUMCHUNKS];\r
+extern byte            ca_levelbit,ca_levelnum;\r
+\r
+extern char            *titleptr[8];\r
+\r
+extern int                     profilehandle,debughandle;\r
+\r
+//\r
+// hooks for custom cache dialogs\r
+//\r
+extern void    (*drawcachebox)         (char *title, unsigned numcache);\r
+extern void    (*updatecachebox)       (void);\r
+extern void    (*finishcachebox)       (void);\r
+\r
+//===========================================================================\r
+\r
+// just for the score box reshifting\r
+\r
+void CAL_ShiftSprite (unsigned segment,unsigned source,unsigned dest,\r
+       unsigned width, unsigned height, unsigned pixshift);\r
+\r
+//===========================================================================\r
+\r
+void CA_OpenDebug (void);\r
+void CA_CloseDebug (void);\r
+boolean CA_FarRead (int handle, byte far *dest, long length);\r
+boolean CA_FarWrite (int handle, byte far *source, long length);\r
+boolean CA_ReadFile (char *filename, memptr *ptr);\r
+boolean CA_LoadFile (char *filename, memptr *ptr);\r
+\r
+long CA_RLEWCompress (unsigned huge *source, long length, unsigned huge *dest,\r
+  unsigned rlewtag);\r
+\r
+void CA_RLEWexpand (unsigned huge *source, unsigned huge *dest,long length,\r
+  unsigned rlewtag);\r
+\r
+void CA_Startup (void);\r
+void CA_Shutdown (void);\r
+\r
+void CA_CacheAudioChunk (int chunk);\r
+void CA_LoadAllSounds (void);\r
+\r
+void CA_UpLevel (void);\r
+void CA_DownLevel (void);\r
+\r
+void CA_SetAllPurge (void);\r
+\r
+void CA_ClearMarks (void);\r
+void CA_ClearAllMarks (void);\r
+\r
+#define CA_MarkGrChunk(chunk)  grneeded[chunk]|=ca_levelbit\r
+\r
+void CA_CacheGrChunk (int chunk);\r
+void CA_CacheMap (int mapnum);\r
+\r
+void CA_CacheMarks (char *title);\r
+\r
diff --git a/16/keen456/KEEN4-6/ID_IN.C b/16/keen456/KEEN4-6/ID_IN.C
new file mode 100755 (executable)
index 0000000..bb6929e
--- /dev/null
@@ -0,0 +1,1243 @@
+/* Reconstructed Commander Keen 4-6 Source Code\r
+ * Copyright (C) 2021 K1n9_Duk3\r
+ *\r
+ * This file is primarily based on:\r
+ * Catacomb 3-D Source Code\r
+ * Copyright (C) 1993-2014 Flat Rock Software\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License along\r
+ * with this program; if not, write to the Free Software Foundation, Inc.,\r
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+ */\r
+\r
+//\r
+//     ID Engine\r
+//     ID_IN.c - Input Manager\r
+//     v1.0d1\r
+//     By Jason Blochowiak\r
+//\r
+\r
+//\r
+//     This module handles dealing with the various input devices\r
+//\r
+//     Depends on: Memory Mgr (for demo recording), Sound Mgr (for timing stuff),\r
+//                             User Mgr (for command line parms)\r
+//\r
+//     Globals:\r
+//             LastScan - The keyboard scan code of the last key pressed\r
+//             LastASCII - The ASCII value of the last key pressed\r
+//     DEBUG - there are more globals\r
+//\r
+\r
+#include "ID_HEADS.H"\r
+#pragma        hdrstop\r
+\r
+#define        KeyInt  9       // The keyboard ISR number\r
+\r
+// Stuff for the joystick\r
+#define        JoyScaleMax             32768\r
+#define        JoyScaleShift   8\r
+#define        MaxJoyValue             5000\r
+\r
+//     Global variables\r
+               boolean         Keyboard[NumCodes],\r
+                                       JoysPresent[MaxJoys],\r
+                                       MousePresent;\r
+               boolean         Paused;\r
+               char            LastASCII;\r
+               ScanCode        LastScan;\r
+               KeyboardDef     KbdDefs[MaxKbds] = {{0x1d,0x38,0x47,0x48,0x49,0x4b,0x4d,0x4f,0x50,0x51}};\r
+               JoystickDef     JoyDefs[MaxJoys];\r
+               ControlType     Controls[MaxPlayers];\r
+               boolean GravisGamepad;\r
+               word            GravisAction[4];\r
+               word            GravisMap[4];\r
+\r
+               boolean Latch;\r
+               long    MouseDownCount;\r
+               boolean LatchedButton0[MaxPlayers];\r
+               boolean LatchedButton1[MaxPlayers];\r
+\r
+               Demo            DemoMode = demo_Off;\r
+               byte _seg       *DemoBuffer;\r
+               word            DemoOffset,DemoSize;\r
+\r
+//     Internal variables\r
+static boolean         IN_Started;\r
+static boolean         CapsLock;\r
+static ScanCode        CurCode,LastCode;\r
+static byte        far ASCIINames[] =          // Unshifted ASCII for scan codes\r
+                                       {\r
+//      0   1   2   3   4   5   6   7   8   9   A   B   C   D   E   F\r
+       0  ,27 ,'1','2','3','4','5','6','7','8','9','0','-','=',8  ,9  ,        // 0\r
+       'q','w','e','r','t','y','u','i','o','p','[',']',13 ,0  ,'a','s',        // 1\r
+       'd','f','g','h','j','k','l',';',39 ,'`',0  ,92 ,'z','x','c','v',        // 2\r
+       'b','n','m',',','.','/',0  ,'*',0  ,' ',0  ,0  ,0  ,0  ,0  ,0  ,        // 3\r
+       0  ,0  ,0  ,0  ,0  ,0  ,0  ,'7','8','9','-','4','5','6','+','1',        // 4\r
+       '2','3','0',127,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,        // 5\r
+       0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,        // 6\r
+       0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0           // 7\r
+                                       },\r
+                                       far ShiftNames[] =              // Shifted ASCII for scan codes\r
+                                       {\r
+//      0   1   2   3   4   5   6   7   8   9   A   B   C   D   E   F\r
+       0  ,27 ,'!','@','#','$','%','^','&','*','(',')','_','+',8  ,9  ,        // 0\r
+       'Q','W','E','R','T','Y','U','I','O','P','{','}',13 ,0  ,'A','S',        // 1\r
+       'D','F','G','H','J','K','L',':',34 ,'~',0  ,'|','Z','X','C','V',        // 2\r
+       'B','N','M','<','>','?',0  ,'*',0  ,' ',0  ,0  ,0  ,0  ,0  ,0  ,        // 3\r
+       0  ,0  ,0  ,0  ,0  ,0  ,0  ,'7','8','9','-','4','5','6','+','1',        // 4\r
+       '2','3','0',127,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,        // 5\r
+       0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,        // 6\r
+       0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0           // 7\r
+                                       },\r
+                                       far SpecialNames[] =    // ASCII for 0xe0 prefixed codes\r
+                                       {\r
+//      0   1   2   3   4   5   6   7   8   9   A   B   C   D   E   F\r
+       0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,        // 0\r
+       0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,13 ,0  ,0  ,0  ,        // 1\r
+       0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,        // 2\r
+       0  ,0  ,0  ,0  ,0  ,'/',0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,        // 3\r
+       0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,        // 4\r
+       0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,        // 5\r
+       0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,        // 6\r
+       0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0           // 7\r
+                                       },\r
+\r
+                                       *ScanNames[] =          // Scan code names with single chars\r
+                                       {\r
+       "?","?","1","2","3","4","5","6","7","8","9","0","-","+","?","?",\r
+       "Q","W","E","R","T","Y","U","I","O","P","[","]","|","?","A","S",\r
+       "D","F","G","H","J","K","L",";","\"","?","?","?","Z","X","C","V",\r
+       "B","N","M",",",".","/","?","?","?","?","?","?","?","?","?","?",\r
+       "?","?","?","?","?","?","?","?","\xf","?","-","\x15","5","\x11","+","?",\r
+       "\x13","?","?","?","?","?","?","?","?","?","?","?","?","?","?","?",\r
+       "?","?","?","?","?","?","?","?","?","?","?","?","?","?","?","?",\r
+       "?","?","?","?","?","?","?","?","?","?","?","?","?","?","?","?"\r
+                                       },      // DEBUG - consolidate these\r
+                                       far ExtScanCodes[] =    // Scan codes with >1 char names\r
+                                       {\r
+       1,0xe,0xf,0x1d,0x2a,0x39,0x3a,0x3b,0x3c,0x3d,0x3e,\r
+       0x3f,0x40,0x41,0x42,0x43,0x44,0x57,0x59,0x46,0x1c,0x36,\r
+       0x37,0x38,0x47,0x49,0x4f,0x51,0x52,0x53,0x45,0x48,\r
+       0x50,0x4b,0x4d,0x00\r
+                                       },\r
+                                       *ExtScanNames[] =       // Names corresponding to ExtScanCodes\r
+                                       {\r
+       "Esc","BkSp","Tab","Ctrl","LShft","Space","CapsLk","F1","F2","F3","F4",\r
+       "F5","F6","F7","F8","F9","F10","F11","F12","ScrlLk","Enter","RShft",\r
+       "PrtSc","Alt","Home","PgUp","End","PgDn","Ins","Del","NumLk","Up",\r
+       "Down","Left","Right",""\r
+                                       };\r
+static Direction       DirTable[] =            // Quick lookup for total direction\r
+                                       {\r
+                                               dir_NorthWest,  dir_North,      dir_NorthEast,\r
+                                               dir_West,               dir_None,       dir_East,\r
+                                               dir_SouthWest,  dir_South,      dir_SouthEast\r
+                                       };\r
+\r
+static void                    (*INL_KeyHook)(void);\r
+static void interrupt  (*OldKeyVect)(void);\r
+\r
+static char                    *ParmStrings[] = {"nojoys","nomouse",nil};\r
+\r
+//     Internal routines\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//     INL_KeyService() - Handles a keyboard interrupt (key up/down)\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static void interrupt\r
+INL_KeyService(void)\r
+{\r
+static boolean special;\r
+               byte    k,c,\r
+                               temp;\r
+               int     player;\r
+\r
+       k = inportb(0x60);      // Get the scan code\r
+\r
+       // Tell the XT keyboard controller to clear the key\r
+       outportb(0x61,(temp = inportb(0x61)) | 0x80);\r
+       outportb(0x61,temp);\r
+\r
+       if (k == 0xe0)          // Special key prefix\r
+               special = true;\r
+       else if (k == 0xe1)     // Handle Pause key\r
+               Paused = true;\r
+       else\r
+       {\r
+               if (k & 0x80)   // Break code\r
+               {\r
+                       k &= 0x7f;\r
+\r
+// DEBUG - handle special keys: ctl-alt-delete, print scrn\r
+\r
+                       Keyboard[k] = false;\r
+               }\r
+               else                    // Make code\r
+               {\r
+                       LastCode = CurCode;\r
+                       CurCode = LastScan = k;\r
+                       Keyboard[k] = true;\r
+\r
+                       if (Latch)\r
+                       {\r
+                               for (player = 0; player < MaxPlayers; player++)\r
+                               {\r
+                                       if (Controls[player] == ctrl_Keyboard1)\r
+                                       {\r
+                                               if (CurCode == KbdDefs[0].button0)\r
+                                                       LatchedButton0[player] = true;\r
+                                               else if (CurCode == KbdDefs[0].button1)\r
+                                                       LatchedButton1[player] = true;\r
+                                       }\r
+                                       else if (Controls[player] == ctrl_Keyboard1)    // BUG? should probably check for ctrl_Keyboard2 here...\r
+                                       {\r
+                                               if (CurCode == KbdDefs[1].button0)\r
+                                                       LatchedButton0[player] = true;\r
+                                               else if (CurCode == KbdDefs[1].button1)\r
+                                                       LatchedButton1[player] = true;\r
+                                       }\r
+                               }\r
+                       }\r
+\r
+                       if (special)\r
+                               c = SpecialNames[k];\r
+                       else\r
+                       {\r
+                               if (k == sc_CapsLock)\r
+                               {\r
+                                       CapsLock ^= true;\r
+                                       // DEBUG - make caps lock light work\r
+                               }\r
+\r
+                               if (Keyboard[sc_LShift] || Keyboard[sc_RShift]) // If shifted\r
+                               {\r
+                                       c = ShiftNames[k];\r
+                                       if ((c >= 'A') && (c <= 'Z') && CapsLock)\r
+                                               c += 'a' - 'A';\r
+                               }\r
+                               else\r
+                               {\r
+                                       c = ASCIINames[k];\r
+                                       if ((c >= 'a') && (c <= 'z') && CapsLock)\r
+                                               c -= 'a' - 'A';\r
+                               }\r
+                       }\r
+                       if (c)\r
+                               LastASCII = c;\r
+               }\r
+\r
+               special = false;\r
+       }\r
+\r
+       if (INL_KeyHook && !special)\r
+               INL_KeyHook();\r
+       outportb(0x20,0x20);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//     INL_GetMouseDelta() - Gets the amount that the mouse has moved from the\r
+//             mouse driver\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static void\r
+INL_GetMouseDelta(int *x,int *y)\r
+{\r
+       Mouse(MDelta);\r
+       *x = _CX;\r
+       *y = _DX;\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//     INL_GetMouseButtons() - Gets the status of the mouse buttons from the\r
+//             mouse driver\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static word\r
+INL_GetMouseButtons(void)\r
+{\r
+       word    buttons;\r
+\r
+       Mouse(MButtons);\r
+       buttons = _BX;\r
+       return(buttons);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//     IN_GetJoyAbs() - Reads the absolute position of the specified joystick\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+IN_GetJoyAbs(word joy,word *xp,word *yp)\r
+{\r
+       byte    xb,yb,\r
+                       xs,ys;\r
+       word    x,y;\r
+\r
+       x = y = 0;\r
+       xs = joy? 2 : 0;                // Find shift value for x axis\r
+       xb = 1 << xs;                   // Use shift value to get x bit mask\r
+       ys = joy? 3 : 1;                // Do the same for y axis\r
+       yb = 1 << ys;\r
+\r
+// Read the absolute joystick values\r
+asm            pushf                           // Save some registers\r
+asm            push    si\r
+asm            push    di\r
+asm            cli                                     // Make sure an interrupt doesn't screw the timings\r
+\r
+\r
+asm            mov             dx,0x201\r
+asm            in              al,dx\r
+asm            out             dx,al           // Clear the resistors\r
+\r
+asm            mov             ah,[xb]         // Get masks into registers\r
+asm            mov             ch,[yb]\r
+\r
+asm            xor             si,si           // Clear count registers\r
+asm            xor             di,di\r
+asm            xor             bh,bh           // Clear high byte of bx for later\r
+\r
+asm            push    bp                      // Don't mess up stack frame\r
+asm            mov             bp,MaxJoyValue\r
+\r
+loop:\r
+asm            in              al,dx           // Get bits indicating whether all are finished\r
+\r
+asm            dec             bp                      // Check bounding register\r
+asm            jz              done            // We have a silly value - abort\r
+\r
+asm            mov             bl,al           // Duplicate the bits\r
+asm            and             bl,ah           // Mask off useless bits (in [xb])\r
+asm            add             si,bx           // Possibly increment count register\r
+asm            mov             cl,bl           // Save for testing later\r
+\r
+asm            mov             bl,al\r
+asm            and             bl,ch           // [yb]\r
+asm            add             di,bx\r
+\r
+asm            add             cl,bl\r
+asm            jnz             loop            // If both bits were 0, drop out\r
+\r
+done:\r
+asm     pop            bp\r
+\r
+asm            mov             cl,[xs]         // Get the number of bits to shift\r
+asm            shr             si,cl           //  and shift the count that many times\r
+\r
+asm            mov             cl,[ys]\r
+asm            shr             di,cl\r
+\r
+asm            mov             [x],si          // Store the values into the variables\r
+asm            mov             [y],di\r
+\r
+asm            pop             di\r
+asm            pop             si\r
+asm            popf                            // Restore the registers\r
+\r
+       *xp = x;\r
+       *yp = y;\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//     INL_GetJoyDelta() - Returns the relative movement of the specified\r
+//             joystick (from +/-127, scaled adaptively)\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static void\r
+INL_GetJoyDelta(word joy,int *dx,int *dy,boolean adaptive)\r
+{\r
+       word            x,y;\r
+       longword        time;\r
+       JoystickDef     *def;\r
+static longword        lasttime;\r
+\r
+       IN_GetJoyAbs(joy,&x,&y);\r
+       def = JoyDefs + joy;\r
+\r
+       if (x < def->threshMinX)\r
+       {\r
+               if (x < def->joyMinX)\r
+                       x = def->joyMinX;\r
+\r
+               x = -(x - def->threshMinX);\r
+               x *= def->joyMultXL;\r
+               x >>= JoyScaleShift;\r
+               *dx = (x > 127)? -127 : -x;\r
+       }\r
+       else if (x > def->threshMaxX)\r
+       {\r
+               if (x > def->joyMaxX)\r
+                       x = def->joyMaxX;\r
+\r
+               x = x - def->threshMaxX;\r
+               x *= def->joyMultXH;\r
+               x >>= JoyScaleShift;\r
+               *dx = (x > 127)? 127 : x;\r
+       }\r
+       else\r
+               *dx = 0;\r
+\r
+       if (y < def->threshMinY)\r
+       {\r
+               if (y < def->joyMinY)\r
+                       y = def->joyMinY;\r
+\r
+               y = -(y - def->threshMinY);\r
+               y *= def->joyMultYL;\r
+               y >>= JoyScaleShift;\r
+               *dy = (y > 127)? -127 : -y;\r
+       }\r
+       else if (y > def->threshMaxY)\r
+       {\r
+               if (y > def->joyMaxY)\r
+                       y = def->joyMaxY;\r
+\r
+               y = y - def->threshMaxY;\r
+               y *= def->joyMultYH;\r
+               y >>= JoyScaleShift;\r
+               *dy = (y > 127)? 127 : y;\r
+       }\r
+       else\r
+               *dy = 0;\r
+\r
+       if (adaptive)\r
+       {\r
+               time = (TimeCount - lasttime) / 2;\r
+               if (time)\r
+               {\r
+                       if (time > 8)\r
+                               time = 8;\r
+                       *dx *= time;\r
+                       *dy *= time;\r
+               }\r
+       }\r
+       lasttime = TimeCount;\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//     INL_GetJoyButtons() - Returns the button status of the specified\r
+//             joystick\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static word\r
+INL_GetJoyButtons(word joy)\r
+{\r
+register       word    result;\r
+\r
+       result = inportb(0x201);        // Get all the joystick buttons\r
+       if (joy == 2)\r
+       {\r
+               // all 4 buttons (for Gravis Gamepad option)\r
+               result >>= 4;\r
+               result &= 15;\r
+               result ^= 15;\r
+       }\r
+       else\r
+       {\r
+               result >>= joy? 6 : 4;  // Shift into bits 0-1\r
+               result &= 3;                            // Mask off the useless bits\r
+               result ^= 3;\r
+       }\r
+       return(result);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//     IN_GetJoyButtonsDB() - Returns the de-bounced button status of the\r
+//             specified joystick\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+word\r
+IN_GetJoyButtonsDB(word joy)\r
+{\r
+       longword        endtime;\r
+       word            result1,result2;\r
+\r
+       do\r
+       {\r
+               result1 = INL_GetJoyButtons(joy);\r
+               endtime = TimeCount + 2;\r
+               while (TimeCount <= endtime)\r
+                       ;\r
+               result2 = INL_GetJoyButtons(joy);\r
+       } while (result1 != result2);\r
+       return(result1);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//     INL_StartKbd() - Sets up my keyboard stuff for use\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static void\r
+INL_StartKbd(void)\r
+{\r
+       INL_KeyHook = 0;        // Clear key hook\r
+\r
+       IN_ClearKeysDown();\r
+\r
+       OldKeyVect = getvect(KeyInt);\r
+       setvect(KeyInt,INL_KeyService);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//     INL_ShutKbd() - Restores keyboard control to the BIOS\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static void\r
+INL_ShutKbd(void)\r
+{\r
+       poke(0x40,0x17,peek(0x40,0x17) & 0xfaf0);       // Clear ctrl/alt/shift flags\r
+\r
+       setvect(KeyInt,OldKeyVect);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//     INL_StartMouse() - Detects and sets up the mouse\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static boolean\r
+INL_StartMouse(void)\r
+{\r
+       if (getvect(MouseInt))\r
+       {\r
+               Mouse(MReset);\r
+               if (_AX == 0xffff)\r
+                       return(true);\r
+       }\r
+       return(false);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//     INL_ShutMouse() - Cleans up after the mouse\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static void\r
+INL_ShutMouse(void)\r
+{\r
+}\r
+\r
+//\r
+//     INL_SetJoyScale() - Sets up scaling values for the specified joystick\r
+//\r
+static void\r
+INL_SetJoyScale(word joy)\r
+{\r
+       JoystickDef     *def;\r
+\r
+       def = &JoyDefs[joy];\r
+       def->joyMultXL = JoyScaleMax / (def->threshMinX - def->joyMinX);\r
+       def->joyMultXH = JoyScaleMax / (def->joyMaxX - def->threshMaxX);\r
+       def->joyMultYL = JoyScaleMax / (def->threshMinY - def->joyMinY);\r
+       def->joyMultYH = JoyScaleMax / (def->joyMaxY - def->threshMaxY);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//     IN_SetupJoy() - Sets up thresholding values and calls INL_SetJoyScale()\r
+//             to set up scaling values\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+IN_SetupJoy(word joy,word minx,word maxx,word miny,word maxy)\r
+{\r
+       word            d,r;\r
+       JoystickDef     *def;\r
+\r
+       def = &JoyDefs[joy];\r
+\r
+       def->joyMinX = minx;\r
+       def->joyMaxX = maxx;\r
+       r = maxx - minx;\r
+       d = r / 5;\r
+       def->threshMinX = ((r / 2) - d) + minx;\r
+       def->threshMaxX = ((r / 2) + d) + minx;\r
+\r
+       def->joyMinY = miny;\r
+       def->joyMaxY = maxy;\r
+       r = maxy - miny;\r
+       d = r / 5;\r
+       def->threshMinY = ((r / 2) - d) + miny;\r
+       def->threshMaxY = ((r / 2) + d) + miny;\r
+\r
+       INL_SetJoyScale(joy);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//     INL_StartJoy() - Detects & auto-configures the specified joystick\r
+//                                     The auto-config assumes the joystick is centered\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static boolean\r
+INL_StartJoy(word joy)\r
+{\r
+       word            x,y;\r
+\r
+       IN_GetJoyAbs(joy,&x,&y);\r
+\r
+       if\r
+       (\r
+               ((x == 0) || (x > MaxJoyValue - 10))\r
+       ||      ((y == 0) || (y > MaxJoyValue - 10))\r
+       )\r
+               return(false);\r
+       else\r
+       {\r
+               IN_SetupJoy(joy,0,x * 2,0,y * 2);\r
+               return(true);\r
+       }\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//     INL_ShutJoy() - Cleans up the joystick stuff\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static void\r
+INL_ShutJoy(word joy)\r
+{\r
+       JoysPresent[joy] = false;\r
+}\r
+\r
+//     Public routines\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//     IN_ClearButtonLatch() - Clears the button latch stuff\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+IN_ClearButtonLatch(void)\r
+{\r
+       int player;\r
+\r
+asm            pushf\r
+asm            cli\r
+\r
+       MouseDownCount = 0;\r
+\r
+       for (player = 0; player < MaxPlayers; player++)\r
+       {\r
+               LatchedButton0[player] = LatchedButton1[player] = 0;\r
+       }\r
+\r
+asm            popf\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//     LatchSndHook() - Hook routine for joystick button latch\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+LatchSndHook(void)\r
+{\r
+       int player;\r
+       ControlType ctrl;\r
+       word buttons;\r
+\r
+       for (player = 0; player < MaxPlayers; player++)\r
+       {\r
+               ctrl = Controls[player];\r
+\r
+               if (ctrl == ctrl_Joystick1 || ctrl == ctrl_Joystick2)\r
+               {\r
+                       buttons = INL_GetJoyButtons(ctrl - ctrl_Joystick1);\r
+\r
+                       if (buttons & 1)\r
+                               LatchedButton0[player] = true;\r
+                       if (buttons & 2)\r
+                               LatchedButton1[player] = true;\r
+               }\r
+       }\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//     IN_LatchButtons() - Enables or disables button latch\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void IN_LatchButtons(boolean enabled)\r
+{\r
+       if (enabled)\r
+       {\r
+               Latch = false;\r
+               IN_ClearButtonLatch();\r
+       }\r
+\r
+       Latch = enabled;\r
+       SD_SetUserHook(Latch ? LatchSndHook : NULL);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//     IN_Startup() - Starts up the Input Mgr\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+IN_Startup(void)\r
+{\r
+       boolean checkjoys,checkmouse;\r
+       word    i;\r
+\r
+       if (IN_Started)\r
+               return;\r
+\r
+       checkjoys = true;\r
+       checkmouse = true;\r
+       for (i = 1;i < _argc;i++)\r
+       {\r
+               switch (US_CheckParm(_argv[i],ParmStrings))\r
+               {\r
+               case 0:\r
+                       checkjoys = false;\r
+                       break;\r
+               case 1:\r
+                       checkmouse = false;\r
+                       break;\r
+               }\r
+       }\r
+\r
+       INL_StartKbd();\r
+       MousePresent = checkmouse? INL_StartMouse() : false;\r
+\r
+       for (i = 0;i < MaxJoys;i++)\r
+               JoysPresent[i] = checkjoys? INL_StartJoy(i) : false;\r
+\r
+       IN_Started = true;\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//     IN_Default() - Sets up default conditions for the Input Mgr\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+IN_Default(boolean gotit,ControlType in)\r
+{\r
+       if\r
+       (\r
+               (!gotit)\r
+       ||      ((in == ctrl_Joystick1) && !JoysPresent[0])\r
+       ||      ((in == ctrl_Joystick2) && !JoysPresent[1])\r
+       ||      ((in == ctrl_Mouse) && !MousePresent)\r
+       )\r
+               in = ctrl_Keyboard1;\r
+       IN_SetControlType(0,in);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//     IN_Shutdown() - Shuts down the Input Mgr\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+IN_Shutdown(void)\r
+{\r
+       word    i;\r
+\r
+       if (!IN_Started)\r
+               return;\r
+\r
+       INL_ShutMouse();\r
+       for (i = 0;i < MaxJoys;i++)\r
+               INL_ShutJoy(i);\r
+       INL_ShutKbd();\r
+\r
+       IN_Started = false;\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//     IN_SetKeyHook() - Sets the routine that gets called by INL_KeyService()\r
+//                     everytime a real make/break code gets hit\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+IN_SetKeyHook(void (*hook)())\r
+{\r
+       // BUG: interrupts should be disabled while setting INL_KeyHook!\r
+       INL_KeyHook = hook;\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//     IN_ClearKeyDown() - Clears the keyboard array\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+IN_ClearKeysDown(void)\r
+{\r
+       int     i;\r
+\r
+       LastScan = sc_None;\r
+       LastASCII = key_None;\r
+       for (i = 0;i < NumCodes;i++)\r
+               Keyboard[i] = false;\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//     INL_AdjustCursor() - Internal routine of common code from IN_ReadCursor()\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static void\r
+INL_AdjustCursor(CursorInfo *info,word buttons,int dx,int dy)\r
+{\r
+       if (buttons & (1 << 0))\r
+               info->button0 = true;\r
+       if (buttons & (1 << 1))\r
+               info->button1 = true;\r
+\r
+       info->x += dx;\r
+       info->y += dy;\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//     IN_ReadCursor() - Reads the input devices and fills in the cursor info\r
+//             struct\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+IN_ReadCursor(CursorInfo *info)\r
+{\r
+       word    i,\r
+               player,\r
+                       buttons;\r
+       int             dx,dy;\r
+\r
+       info->x = info->y = 0;\r
+       info->button0 = info->button1 = false;\r
+\r
+       if (MousePresent)\r
+       {\r
+               buttons = INL_GetMouseButtons();\r
+               INL_GetMouseDelta(&dx,&dy);\r
+               INL_AdjustCursor(info,buttons,dx,dy);\r
+       }\r
+\r
+       for (i = 0;i < MaxJoys;i++)\r
+       {\r
+               if (!JoysPresent[i])\r
+                       continue;\r
+\r
+               for (player = 0;player < MaxPlayers; player++)\r
+               {\r
+                       if (Controls[player] == ctrl_Joystick1+i)\r
+                               goto joyok;\r
+               }\r
+               continue;\r
+\r
+joyok:\r
+               buttons = INL_GetJoyButtons(i);\r
+               INL_GetJoyDelta(i,&dx,&dy,true);\r
+               dx /= 64;\r
+               dy /= 64;\r
+               INL_AdjustCursor(info,buttons,dx,dy);\r
+       }\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//     IN_ReadControl() - Reads the device associated with the specified\r
+//             player and fills in the control info struct\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+IN_ReadControl(int player,ControlInfo *info)\r
+{\r
+                       boolean         realdelta;\r
+                       byte            dbyte;\r
+                       word            buttons;\r
+                       int                     i;\r
+                       int                     dx,dy;\r
+                       Motion          mx,my;\r
+                       ControlType     type;\r
+register       KeyboardDef     *def;\r
+\r
+       dx = dy = 0;\r
+       mx = my = motion_None;\r
+       buttons = 0;\r
+\r
+       if (DemoMode == demo_Playback)\r
+       {\r
+               dbyte = DemoBuffer[DemoOffset + 1];\r
+               my = (dbyte & 3) - 1;\r
+               mx = ((dbyte >> 2) & 3) - 1;\r
+               buttons = (dbyte >> 4) & 3;\r
+\r
+               if (!(--DemoBuffer[DemoOffset]))\r
+               {\r
+                       DemoOffset += 2;\r
+                       if (DemoOffset >= DemoSize)\r
+                               DemoMode = demo_PlayDone;\r
+               }\r
+\r
+               realdelta = false;\r
+       }\r
+       else if (DemoMode == demo_PlayDone)\r
+               Quit("Demo playback exceeded");\r
+       else\r
+       {\r
+               switch (type = Controls[player])\r
+               {\r
+               case ctrl_Keyboard1:\r
+               case ctrl_Keyboard2:\r
+                       def = &KbdDefs[type - ctrl_Keyboard];\r
+\r
+                       if (Keyboard[def->upleft])\r
+                               mx = motion_Left,my = motion_Up;\r
+                       else if (Keyboard[def->upright])\r
+                               mx = motion_Right,my = motion_Up;\r
+                       else if (Keyboard[def->downleft])\r
+                               mx = motion_Left,my = motion_Down;\r
+                       else if (Keyboard[def->downright])\r
+                               mx = motion_Right,my = motion_Down;\r
+\r
+                       if (Keyboard[def->up])\r
+                               my = motion_Up;\r
+                       else if (Keyboard[def->down])\r
+                               my = motion_Down;\r
+\r
+                       if (Keyboard[def->left])\r
+                               mx = motion_Left;\r
+                       else if (Keyboard[def->right])\r
+                               mx = motion_Right;\r
+\r
+                       if (Keyboard[def->button0])\r
+                               buttons += 1 << 0;\r
+                       if (Keyboard[def->button1])\r
+                               buttons += 1 << 1;\r
+                       realdelta = false;\r
+                       break;\r
+               case ctrl_Joystick1:\r
+               case ctrl_Joystick2:\r
+                       INL_GetJoyDelta(type - ctrl_Joystick,&dx,&dy,false);\r
+                       if (GravisGamepad)\r
+                       {\r
+                               buttons = INL_GetJoyButtons(2);\r
+                               for (i=0; i<4; i++)\r
+                               {\r
+                                       GravisAction[i] = buttons & (1 << GravisMap[i]);\r
+                               }\r
+                       }\r
+                       buttons = INL_GetJoyButtons(type - ctrl_Joystick);\r
+                       realdelta = true;\r
+                       break;\r
+               case ctrl_Mouse:\r
+                       INL_GetMouseDelta(&dx,&dy);\r
+                       buttons = INL_GetMouseButtons();\r
+                       realdelta = true;\r
+                       break;\r
+               }\r
+       }\r
+\r
+       if (realdelta)\r
+       {\r
+               mx = (dx < 0)? motion_Left : ((dx > 0)? motion_Right : motion_None);\r
+               my = (dy < 0)? motion_Up : ((dy > 0)? motion_Down : motion_None);\r
+       }\r
+       else\r
+       {\r
+               dx = mx * 127;\r
+               dy = my * 127;\r
+       }\r
+\r
+       info->x = dx;\r
+       info->xaxis = mx;\r
+       info->y = dy;\r
+       info->yaxis = my;\r
+       info->button0 = buttons & (1 << 0);\r
+       info->button1 = buttons & (1 << 1);\r
+       info->dir = DirTable[((my + 1) * 3) + (mx + 1)];\r
+\r
+       if (DemoMode == demo_Record)\r
+       {\r
+               // Pack the control info into a byte\r
+               dbyte = (buttons << 4) | ((mx + 1) << 2) | (my + 1);\r
+\r
+               if\r
+               (\r
+                       (DemoBuffer[DemoOffset + 1] == dbyte)\r
+               &&      (DemoBuffer[DemoOffset] < 255)\r
+               )\r
+                       (DemoBuffer[DemoOffset])++;\r
+               else\r
+               {\r
+                       if (DemoOffset || DemoBuffer[DemoOffset])\r
+                               DemoOffset += 2;\r
+\r
+                       if (DemoOffset >= DemoSize)\r
+                               Quit("Demo buffer overflow");\r
+\r
+                       DemoBuffer[DemoOffset] = 1;\r
+                       DemoBuffer[DemoOffset + 1] = dbyte;\r
+               }\r
+       }\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//     IN_SetControlType() - Sets the control type to be used by the specified\r
+//             player\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+IN_SetControlType(int player,ControlType type)\r
+{\r
+       // DEBUG - check that requested type is present?\r
+       Controls[player] = type;\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//     IN_StartDemoRecord() - Starts the demo recording, using a buffer the\r
+//             size passed. Returns if the buffer allocation was successful\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+boolean\r
+IN_StartDemoRecord(word bufsize)\r
+{\r
+       if (!bufsize)\r
+               return(false);\r
+\r
+       MM_GetPtr((memptr *)&DemoBuffer,bufsize);\r
+       DemoMode = demo_Record;\r
+       DemoSize = bufsize & ~1;\r
+       DemoOffset = 0;\r
+       DemoBuffer[0] = DemoBuffer[1] = 0;\r
+\r
+       return(true);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//     IN_StartDemoPlayback() - Plays back the demo pointed to of the given size\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+IN_StartDemoPlayback(byte _seg *buffer,word bufsize)\r
+{\r
+       DemoBuffer = buffer;\r
+       DemoMode = demo_Playback;\r
+       DemoSize = bufsize & ~1;\r
+       DemoOffset = 0;\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//     IN_StopDemo() - Turns off demo mode\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+IN_StopDemo(void)\r
+{\r
+       if ((DemoMode == demo_Record) && DemoOffset)\r
+               DemoOffset += 2;\r
+\r
+       DemoMode = demo_Off;\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//     IN_FreeDemoBuffer() - Frees the demo buffer, if it's been allocated\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+IN_FreeDemoBuffer(void)\r
+{\r
+       if (DemoBuffer)\r
+               MM_FreePtr((memptr *)&DemoBuffer);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//     IN_GetScanName() - Returns a string containing the name of the\r
+//             specified scan code\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+byte *\r
+IN_GetScanName(ScanCode scan)\r
+{\r
+       byte            **p;\r
+       ScanCode        far *s;\r
+\r
+       for (s = ExtScanCodes,p = ExtScanNames;*s;p++,s++)\r
+               if (*s == scan)\r
+                       return(*p);\r
+\r
+       return(ScanNames[scan]);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//     IN_WaitForKey() - Waits for a scan code, then clears LastScan and\r
+//             returns the scan code\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+ScanCode\r
+IN_WaitForKey(void)\r
+{\r
+       ScanCode        result;\r
+\r
+       while (!(result = LastScan))\r
+               ;\r
+       LastScan = 0;\r
+       return(result);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//     IN_WaitForASCII() - Waits for an ASCII char, then clears LastASCII and\r
+//             returns the ASCII value\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+char\r
+IN_WaitForASCII(void)\r
+{\r
+       char            result;\r
+\r
+       while (!(result = LastASCII))\r
+               ;\r
+       LastASCII = '\0';\r
+       return(result);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//     IN_AckBack() - Waits for either an ASCII keypress or a button press\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+IN_AckBack(void)\r
+{\r
+       word    i;\r
+\r
+       while (!LastScan)\r
+       {\r
+               if (MousePresent)\r
+               {\r
+                       if (INL_GetMouseButtons())\r
+                       {\r
+                               while (INL_GetMouseButtons())\r
+                                       ;\r
+                               return;\r
+                       }\r
+               }\r
+\r
+               for (i = 0;i < MaxJoys;i++)\r
+               {\r
+                       if (JoysPresent[i] || GravisGamepad)\r
+                       {\r
+                               if (IN_GetJoyButtonsDB(i))\r
+                               {\r
+                                       while (IN_GetJoyButtonsDB(i))\r
+                                               ;\r
+                                       return;\r
+                               }\r
+                       }\r
+               }\r
+       }\r
+\r
+       IN_ClearKey(LastScan);\r
+       LastScan = sc_None;\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//     IN_Ack() - Clears user input & then calls IN_AckBack()\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+IN_Ack(void)\r
+{\r
+       word    i;\r
+\r
+       IN_ClearKey(LastScan);\r
+       LastScan = sc_None;\r
+\r
+       if (MousePresent)\r
+               while (INL_GetMouseButtons())\r
+                                       ;\r
+       for (i = 0;i < MaxJoys;i++)\r
+               if (JoysPresent[i] || GravisGamepad)\r
+                       while (IN_GetJoyButtonsDB(i))\r
+                               ;\r
+\r
+       IN_AckBack();\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//     IN_IsUserInput() - Returns true if a key has been pressed or a button\r
+//             is down\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+boolean\r
+IN_IsUserInput(void)\r
+{\r
+       boolean result;\r
+       word    i;\r
+\r
+       result = LastScan;\r
+\r
+       if (MousePresent)\r
+               if (INL_GetMouseButtons())\r
+                       result = true;\r
+\r
+       for (i = 0;i < MaxJoys;i++)\r
+               if (JoysPresent[i] || GravisGamepad)\r
+                       if (INL_GetJoyButtons(i))\r
+                               result = true;\r
+\r
+       return(result);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//     IN_UserInput() - Waits for the specified delay time (in ticks) or the\r
+//             user pressing a key or a mouse button. If the clear flag is set, it\r
+//             then either clears the key or waits for the user to let the mouse\r
+//             button up.\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+boolean\r
+IN_UserInput(longword delay,boolean clear)\r
+{\r
+       longword        lasttime;\r
+\r
+       lasttime = TimeCount;\r
+       do\r
+       {\r
+               if (IN_IsUserInput())\r
+               {\r
+                       if (clear)\r
+                               IN_AckBack();\r
+                       return(true);\r
+               }\r
+       } while (TimeCount - lasttime < delay);\r
+       return(false);\r
+}\r
diff --git a/16/keen456/KEEN4-6/ID_IN.H b/16/keen456/KEEN4-6/ID_IN.H
new file mode 100755 (executable)
index 0000000..d3ade29
--- /dev/null
@@ -0,0 +1,232 @@
+/* Reconstructed Commander Keen 4-6 Source Code\r
+ * Copyright (C) 2021 K1n9_Duk3\r
+ *\r
+ * This file is primarily based on:\r
+ * Catacomb 3-D Source Code\r
+ * Copyright (C) 1993-2014 Flat Rock Software\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License along\r
+ * with this program; if not, write to the Free Software Foundation, Inc.,\r
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+ */\r
+\r
+//\r
+//     ID Engine\r
+//     ID_IN.h - Header file for Input Manager\r
+//     v1.0d1\r
+//     By Jason Blochowiak\r
+//\r
+\r
+#ifndef        __TYPES__\r
+#include "ID_Types.h"\r
+#endif\r
+\r
+#ifndef        __ID_IN__\r
+#define        __ID_IN__\r
+\r
+#ifdef __DEBUG__\r
+#define        __DEBUG_InputMgr__\r
+#endif\r
+\r
+#define        MaxPlayers      4\r
+#define        MaxKbds         2\r
+#define        MaxJoys         2\r
+#define        NumCodes        128\r
+\r
+typedef        byte            ScanCode;\r
+#define        sc_None                 0\r
+#define        sc_Bad                  0xff\r
+#define        sc_Return               0x1c\r
+#define        sc_Enter                sc_Return\r
+#define        sc_Escape               0x01\r
+#define        sc_Space                0x39\r
+#define        sc_BackSpace    0x0e\r
+#define        sc_Tab                  0x0f\r
+#define        sc_Alt                  0x38\r
+#define        sc_Control              0x1d\r
+#define        sc_CapsLock             0x3a\r
+#define        sc_LShift               0x2a\r
+#define        sc_RShift               0x36\r
+#define        sc_UpArrow              0x48\r
+#define        sc_DownArrow    0x50\r
+#define        sc_LeftArrow    0x4b\r
+#define        sc_RightArrow   0x4d\r
+#define        sc_Insert               0x52\r
+#define        sc_Delete               0x53\r
+#define        sc_Home                 0x47\r
+#define        sc_End                  0x4f\r
+#define        sc_PgUp                 0x49\r
+#define        sc_PgDn                 0x51\r
+#define        sc_F1                   0x3b\r
+#define        sc_F2                   0x3c\r
+#define        sc_F3                   0x3d\r
+#define        sc_F4                   0x3e\r
+#define        sc_F5                   0x3f\r
+#define        sc_F6                   0x40\r
+#define        sc_F7                   0x41\r
+#define        sc_F8                   0x42\r
+#define        sc_F9                   0x43\r
+#define        sc_F10                  0x44\r
+#define        sc_F11                  0x57\r
+#define        sc_F12                  0x59    // BUG: F12 uses scan code 0x58!\r
+\r
+#define        sc_A                    0x1e\r
+#define        sc_B                    0x30\r
+#define        sc_C                    0x2e\r
+#define        sc_D                    0x20\r
+#define        sc_E                    0x12\r
+#define        sc_F                    0x21\r
+#define        sc_G                    0x22\r
+#define        sc_H                    0x23\r
+#define        sc_I                    0x17\r
+#define        sc_J                    0x24\r
+#define        sc_K                    0x25\r
+#define        sc_L                    0x26\r
+#define        sc_M                    0x32\r
+#define        sc_N                    0x31\r
+#define        sc_O                    0x18\r
+#define        sc_P                    0x19\r
+#define        sc_Q                    0x10\r
+#define        sc_R                    0x13\r
+#define        sc_S                    0x1f\r
+#define        sc_T                    0x14\r
+#define        sc_U                    0x16\r
+#define        sc_V                    0x2f\r
+#define        sc_W                    0x11\r
+#define        sc_X                    0x2d\r
+#define        sc_Y                    0x15\r
+#define        sc_Z                    0x2c\r
+\r
+#define        sc_1                    0x02\r
+#define        sc_2                    0x03\r
+#define        sc_3                    0x04\r
+#define        sc_4                    0x05\r
+#define        sc_5                    0x06\r
+#define        sc_6                    0x07\r
+#define        sc_7                    0x08\r
+#define        sc_8                    0x09\r
+#define        sc_9                    0x0a\r
+#define        sc_0                    0x0b\r
+\r
+#define        key_None                0\r
+#define        key_Return              0x0d\r
+#define        key_Enter               key_Return\r
+#define        key_Escape              0x1b\r
+#define        key_Space               0x20\r
+#define        key_BackSpace   0x08\r
+#define        key_Tab                 0x09\r
+#define        key_Delete              0x7f\r
+\r
+//     Stuff for the mouse\r
+#define        MReset          0\r
+#define        MButtons        3\r
+#define        MDelta          11\r
+\r
+#define        MouseInt        0x33\r
+#define        Mouse(x)        _AX = x,geninterrupt(MouseInt)\r
+\r
+typedef        enum            {\r
+                                               demo_Off,demo_Record,demo_Playback,demo_PlayDone\r
+                                       } Demo;\r
+typedef        enum            {\r
+                                               ctrl_Keyboard,\r
+                                                       ctrl_Keyboard1 = ctrl_Keyboard,ctrl_Keyboard2,\r
+                                               ctrl_Joystick,\r
+                                                       ctrl_Joystick1 = ctrl_Joystick,ctrl_Joystick2,\r
+                                               ctrl_Mouse\r
+                                       } ControlType;\r
+typedef        enum            {\r
+                                               motion_Left = -1,motion_Up = -1,\r
+                                               motion_None = 0,\r
+                                               motion_Right = 1,motion_Down = 1\r
+                                       } Motion;\r
+typedef        enum            {\r
+                                               dir_North,dir_NorthEast,\r
+                                               dir_East,dir_SouthEast,\r
+                                               dir_South,dir_SouthWest,\r
+                                               dir_West,dir_NorthWest,\r
+                                               dir_None\r
+                                       } Direction;\r
+typedef        enum            {\r
+                                               ga_Jump,\r
+                                               ga_Pogo,\r
+                                               ga_Fire,\r
+                                               ga_Status\r
+                                       } GravisAType;\r
+typedef        struct          {\r
+                                               boolean         button0,button1;\r
+                                               int                     x,y;\r
+                                               Motion          xaxis,yaxis;\r
+                                               Direction       dir;\r
+                                       } CursorInfo;\r
+typedef        CursorInfo      ControlInfo;\r
+typedef        struct          {\r
+                                               ScanCode        button0,button1,\r
+                                                                       upleft,         up,             upright,\r
+                                                                       left,                           right,\r
+                                                                       downleft,       down,   downright;\r
+                                       } KeyboardDef;\r
+typedef        struct          {\r
+                                               word            joyMinX,joyMinY,\r
+                                                                       threshMinX,threshMinY,\r
+                                                                       threshMaxX,threshMaxY,\r
+                                                                       joyMaxX,joyMaxY,\r
+                                                                       joyMultXL,joyMultYL,\r
+                                                                       joyMultXH,joyMultYH;\r
+                                       } JoystickDef;\r
+// Global variables\r
+extern boolean         Keyboard[],\r
+                                       MousePresent,\r
+                                       JoysPresent[];\r
+extern boolean         Paused;\r
+extern char            LastASCII;\r
+extern ScanCode        LastScan;\r
+extern KeyboardDef     KbdDefs[];\r
+extern JoystickDef     JoyDefs[];\r
+extern ControlType     Controls[MaxPlayers];\r
+extern boolean         GravisGamepad;\r
+extern word            GravisAction[4];\r
+extern word            GravisMap[4];\r
+\r
+extern Demo            DemoMode;\r
+extern byte _seg       *DemoBuffer;\r
+extern word            DemoOffset,DemoSize;\r
+\r
+// Function prototypes\r
+#define        IN_KeyDown(code)        (Keyboard[(code)])\r
+#define        IN_ClearKey(code)       {Keyboard[code] = false;\\r
+                                                       if (code == LastScan) LastScan = sc_None;}\r
+\r
+// DEBUG - put names in prototypes\r
+extern void            IN_Startup(void),IN_Shutdown(void),\r
+                                       IN_Default(boolean gotit,ControlType in),\r
+                                       IN_SetKeyHook(void (*)()),\r
+                                       IN_ClearKeysDown(void),\r
+                                       IN_ReadCursor(CursorInfo *),\r
+                                       IN_ReadControl(int,ControlInfo *),\r
+                                       IN_SetControlType(int,ControlType),\r
+                                       IN_GetJoyAbs(word joy,word *xp,word *yp),\r
+                                       IN_SetupJoy(word joy,word minx,word maxx,\r
+                                                               word miny,word maxy),\r
+                                       IN_StartDemoPlayback(byte _seg *buffer,word bufsize),\r
+                                       IN_StopDemo(void),IN_FreeDemoBuffer(void),\r
+                                       IN_Ack(void),IN_AckBack(void);\r
+extern boolean         IN_UserInput(longword delay,boolean clear),\r
+                                       IN_IsUserInput(void),\r
+                                       IN_StartDemoRecord(word bufsize);\r
+extern byte            *IN_GetScanName(ScanCode);\r
+extern char            IN_WaitForASCII(void);\r
+extern ScanCode        IN_WaitForKey(void);\r
+extern word            IN_GetJoyButtonsDB(word joy);\r
+\r
+#endif\r
diff --git a/16/keen456/KEEN4-6/ID_MM.C b/16/keen456/KEEN4-6/ID_MM.C
new file mode 100755 (executable)
index 0000000..07fdb7c
--- /dev/null
@@ -0,0 +1,1136 @@
+/* Reconstructed Commander Keen 4-6 Source Code\r
+ * Copyright (C) 2021 K1n9_Duk3\r
+ *\r
+ * This file is primarily based on:\r
+ * Catacomb 3-D Source Code\r
+ * Copyright (C) 1993-2014 Flat Rock Software\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License along\r
+ * with this program; if not, write to the Free Software Foundation, Inc.,\r
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+ */\r
+\r
+// NEWMM.C\r
+\r
+/*\r
+=============================================================================\r
+\r
+                  ID software memory manager\r
+                  --------------------------\r
+\r
+Primary coder: John Carmack\r
+\r
+RELIES ON\r
+---------\r
+Quit (char *error) function\r
+\r
+\r
+WORK TO DO\r
+----------\r
+MM_SizePtr to change the size of a given pointer\r
+\r
+Multiple purge levels utilized\r
+\r
+EMS / XMS unmanaged routines\r
+\r
+=============================================================================\r
+*/\r
+\r
+#include "ID_HEADS.H"\r
+#pragma hdrstop\r
+\r
+#pragma warn -pro\r
+#pragma warn -use\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                       LOCAL INFO\r
+\r
+=============================================================================\r
+*/\r
+\r
+#define LOCKBIT                0x80    // if set in attributes, block cannot be moved\r
+#define PURGEBITS      3               // 0-3 level, 0= unpurgable, 3= purge first\r
+#define PURGEMASK      0xfffc\r
+#define BASEATTRIBUTES 0       // unlocked, non purgable\r
+\r
+#define MAXUMBS                10\r
+\r
+typedef struct mmblockstruct\r
+{\r
+       unsigned        start,length;\r
+       unsigned        attributes;\r
+       memptr          *useptr;        // pointer to the segment start\r
+       struct mmblockstruct far *next;\r
+} mmblocktype;\r
+\r
+\r
+//#define GETNEWBLOCK {if(!(mmnew=mmfree))Quit("MM_GETNEWBLOCK: No free blocks!")\\r
+//     ;mmfree=mmfree->next;}\r
+\r
+#define GETNEWBLOCK {if(!mmfree)MML_ClearBlock();mmnew=mmfree;mmfree=mmfree->next;}\r
+\r
+#define FREEBLOCK(x) {*x->useptr=NULL;x->next=mmfree;mmfree=x;}\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                GLOBAL VARIABLES\r
+\r
+=============================================================================\r
+*/\r
+\r
+mminfotype     mminfo;\r
+memptr         bufferseg;\r
+boolean                mmerror;\r
+\r
+void           (* beforesort) (void);\r
+void           (* aftersort) (void);\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                LOCAL VARIABLES\r
+\r
+=============================================================================\r
+*/\r
+\r
+boolean                mmstarted;\r
+\r
+void far       *farheap;\r
+void           *nearheap;\r
+\r
+mmblocktype    far mmblocks[MAXBLOCKS]\r
+                       ,far *mmhead,far *mmfree,far *mmrover,far *mmnew;\r
+\r
+boolean                bombonerror;\r
+\r
+unsigned       totalEMSpages,freeEMSpages,EMSpageframe,EMSpagesmapped,EMShandle;\r
+\r
+void           (* XMSaddr) (void);             // far pointer to XMS driver\r
+\r
+unsigned       numUMBs,UMBbase[MAXUMBS];\r
+\r
+//==========================================================================\r
+\r
+//\r
+// local prototypes\r
+//\r
+\r
+boolean                MML_CheckForEMS (void);\r
+void           MML_ShutdownEMS (void);\r
+void           MM_MapEMS (void);\r
+boolean        MML_CheckForXMS (void);\r
+void           MML_ShutdownXMS (void);\r
+void           MML_UseSpace (unsigned segstart, unsigned seglength);\r
+void           MML_ClearBlock (void);\r
+\r
+//==========================================================================\r
+\r
+/*\r
+======================\r
+=\r
+= MML_CheckForEMS\r
+=\r
+= Routine from p36 of Extending DOS\r
+=\r
+=======================\r
+*/\r
+\r
+char   emmname[9] = "EMMXXXX0";\r
+\r
+boolean MML_CheckForEMS (void)\r
+{\r
+asm    mov     dx,OFFSET emmname[0]\r
+asm    mov     ax,0x3d00\r
+asm    int     0x21            // try to open EMMXXXX0 device\r
+asm    jc      error\r
+\r
+asm    mov     bx,ax\r
+asm    mov     ax,0x4400\r
+\r
+asm    int     0x21            // get device info\r
+asm    jc      error\r
+\r
+asm    and     dx,0x80\r
+asm    jz      error\r
+\r
+asm    mov     ax,0x4407\r
+\r
+asm    int     0x21            // get status\r
+asm    jc      error\r
+asm    or      al,al\r
+asm    jz      error\r
+\r
+asm    mov     ah,0x3e\r
+asm    int     0x21            // close handle\r
+asm    jc      error\r
+\r
+//\r
+// EMS is good\r
+//\r
+  return true;\r
+\r
+error:\r
+//\r
+// EMS is bad\r
+//\r
+  return false;\r
+}\r
+\r
+\r
+/*\r
+======================\r
+=\r
+= MML_SetupEMS\r
+=\r
+=======================\r
+*/\r
+\r
+void MML_SetupEMS (void)\r
+{\r
+       char    str[80],str2[10];\r
+       unsigned        error;\r
+\r
+       totalEMSpages = freeEMSpages = EMSpageframe = EMSpagesmapped = 0;\r
+\r
+asm {\r
+       mov     ah,EMS_STATUS\r
+       int     EMS_INT                                         // make sure EMS hardware is present\r
+       or      ah,ah\r
+       jnz     error\r
+\r
+       mov     ah,EMS_VERSION\r
+       int     EMS_INT\r
+       or      ah,ah\r
+       jnz     error\r
+       cmp     al,0x32                                         // only work on ems 3.2 or greater\r
+       jb      error\r
+\r
+       mov     ah,EMS_GETFRAME\r
+       int     EMS_INT                                         // find the page frame address\r
+       or      ah,ah\r
+       jnz     error\r
+       mov     [EMSpageframe],bx\r
+\r
+       mov     ah,EMS_GETPAGES\r
+       int     EMS_INT                                         // find out how much EMS is there\r
+       or      ah,ah\r
+       jnz     error\r
+       mov     [totalEMSpages],dx\r
+       mov     [freeEMSpages],bx\r
+       or      bx,bx\r
+       jz      noEMS                                           // no EMS at all to allocate\r
+\r
+       cmp     bx,4\r
+       jle     getpages                                        // there is only 1,2,3,or 4 pages\r
+       mov     bx,4                                            // we can't use more than 4 pages\r
+       }\r
+\r
+getpages:\r
+asm {\r
+       mov     [EMSpagesmapped],bx\r
+       mov     ah,EMS_ALLOCPAGES                       // allocate up to 64k of EMS\r
+       int     EMS_INT\r
+       or      ah,ah\r
+       jnz     error\r
+       mov     [EMShandle],dx\r
+       }\r
+       return;\r
+\r
+error:\r
+       error = _AH;\r
+       strcpy (str,"MML_SetupEMS: EMS error 0x");\r
+       itoa(error,str2,16);\r
+       strcpy (str,str2);\r
+       Quit (str);\r
+\r
+noEMS:\r
+;\r
+}\r
+\r
+\r
+/*\r
+======================\r
+=\r
+= MML_ShutdownEMS\r
+=\r
+=======================\r
+*/\r
+\r
+void MML_ShutdownEMS (void)\r
+{\r
+       if (!EMShandle)\r
+               return;\r
+\r
+asm    {\r
+       mov     ah,EMS_FREEPAGES\r
+       mov     dx,[EMShandle]\r
+       int     EMS_INT\r
+       or      ah,ah\r
+       jz      ok\r
+       }\r
+\r
+       Quit ("MML_ShutdownEMS: Error freeing EMS!");\r
+\r
+ok:\r
+;\r
+}\r
+\r
+/*\r
+====================\r
+=\r
+= MM_MapEMS\r
+=\r
+= Maps the 64k of EMS used by memory manager into the page frame\r
+= for general use.  This only needs to be called if you are keeping\r
+= other things in EMS.\r
+=\r
+====================\r
+*/\r
+\r
+void MM_MapEMS (void)\r
+{\r
+       char    str[80],str2[10];\r
+       unsigned        error;\r
+       int     i;\r
+\r
+       for (i=0;i<EMSpagesmapped;i++)\r
+       {\r
+       asm     {\r
+               mov     ah,EMS_MAPPAGE\r
+               mov     bx,[i]                  // logical page\r
+               mov     al,bl                   // physical page\r
+               mov     dx,[EMShandle]  // handle\r
+               int     EMS_INT\r
+               or      ah,ah\r
+               jnz     error\r
+               }\r
+       }\r
+\r
+       return;\r
+\r
+error:\r
+       error = _AH;\r
+       strcpy (str,"MM_MapEMS: EMS error 0x");\r
+       itoa(error,str2,16);\r
+       strcpy (str,str2);\r
+       Quit (str);\r
+}\r
+\r
+//==========================================================================\r
+\r
+/*\r
+======================\r
+=\r
+= MML_CheckForXMS\r
+=\r
+= Check for XMM driver\r
+=\r
+=======================\r
+*/\r
+\r
+boolean MML_CheckForXMS (void)\r
+{\r
+       numUMBs = 0;\r
+\r
+asm {\r
+       mov     ax,0x4300\r
+       int     0x2f                            // query status of installed diver\r
+       cmp     al,0x80\r
+       je      good\r
+       }\r
+       return false;\r
+good:\r
+       return true;\r
+}\r
+\r
+\r
+/*\r
+======================\r
+=\r
+= MML_SetupXMS\r
+=\r
+= Try to allocate all upper memory block\r
+=\r
+=======================\r
+*/\r
+\r
+void MML_SetupXMS (void)\r
+{\r
+       unsigned        base,size;\r
+\r
+asm    {\r
+       mov     ax,0x4310\r
+       int     0x2f\r
+       mov     [WORD PTR XMSaddr],bx\r
+       mov     [WORD PTR XMSaddr+2],es         // function pointer to XMS driver\r
+       }\r
+\r
+getmemory:\r
+asm    {\r
+       mov     ah,XMS_ALLOCUMB\r
+       mov     dx,0xffff                                       // try for largest block possible\r
+       call    [DWORD PTR XMSaddr]\r
+       or      ax,ax\r
+       jnz     gotone\r
+\r
+       cmp     bl,0xb0                                         // error: smaller UMB is available\r
+       jne     done;\r
+\r
+       mov     ah,XMS_ALLOCUMB\r
+       call    [DWORD PTR XMSaddr]             // DX holds largest available UMB\r
+       or      ax,ax\r
+       jz      done                                            // another error...\r
+       }\r
+\r
+gotone:\r
+asm    {\r
+       mov     [base],bx\r
+       mov     [size],dx\r
+       }\r
+       MML_UseSpace (base,size);\r
+       mminfo.XMSmem += size*16;\r
+       UMBbase[numUMBs] = base;\r
+       numUMBs++;\r
+       if (numUMBs < MAXUMBS)\r
+               goto getmemory;\r
+\r
+done:;\r
+}\r
+\r
+\r
+/*\r
+======================\r
+=\r
+= MML_ShutdownXMS\r
+=\r
+======================\r
+*/\r
+\r
+void MML_ShutdownXMS (void)\r
+{\r
+       int     i;\r
+       unsigned        base;\r
+\r
+       for (i=0;i<numUMBs;i++)\r
+       {\r
+               base = UMBbase[i];\r
+\r
+asm    mov     ah,XMS_FREEUMB\r
+asm    mov     dx,[base]\r
+asm    call    [DWORD PTR XMSaddr]\r
+       }\r
+}\r
+\r
+//==========================================================================\r
+\r
+/*\r
+======================\r
+=\r
+= MML_UseSpace\r
+=\r
+= Marks a range of paragraphs as usable by the memory manager\r
+= This is used to mark space for the near heap, far heap, ems page frame,\r
+= and upper memory blocks\r
+=\r
+======================\r
+*/\r
+\r
+void MML_UseSpace (unsigned segstart, unsigned seglength)\r
+{\r
+       mmblocktype far *scan,far *last;\r
+       unsigned        oldend;\r
+       long            extra;\r
+\r
+       scan = last = mmhead;\r
+       mmrover = mmhead;               // reset rover to start of memory\r
+\r
+//\r
+// search for the block that contains the range of segments\r
+//\r
+       while (scan->start+scan->length < segstart)\r
+       {\r
+               last = scan;\r
+               scan = scan->next;\r
+       }\r
+\r
+//\r
+// take the given range out of the block\r
+//\r
+       oldend = scan->start + scan->length;\r
+       extra = oldend - (segstart+seglength);\r
+       if (extra < 0)\r
+               Quit ("MML_UseSpace: Segment spans two blocks!");\r
+\r
+       if (segstart == scan->start)\r
+       {\r
+               last->next = scan->next;                        // unlink block\r
+               FREEBLOCK(scan);\r
+               scan = last;\r
+       }\r
+       else\r
+               scan->length = segstart-scan->start;    // shorten block\r
+\r
+       if (extra > 0)\r
+       {\r
+               GETNEWBLOCK;\r
+               mmnew->useptr = NULL;                   // Keen addition\r
+\r
+               mmnew->next = scan->next;\r
+               scan->next = mmnew;\r
+               mmnew->start = segstart+seglength;\r
+               mmnew->length = extra;\r
+               mmnew->attributes = LOCKBIT;\r
+       }\r
+\r
+}\r
+\r
+//==========================================================================\r
+\r
+/*\r
+====================\r
+=\r
+= MML_ClearBlock\r
+=\r
+= We are out of blocks, so free a purgable block\r
+=\r
+====================\r
+*/\r
+\r
+void MML_ClearBlock (void)\r
+{\r
+       mmblocktype far *scan,far *last;\r
+\r
+       scan = mmhead->next;\r
+\r
+       while (scan)\r
+       {\r
+               if (!(scan->attributes&LOCKBIT) && (scan->attributes&PURGEBITS) )\r
+               {\r
+                       MM_FreePtr(scan->useptr);\r
+                       return;\r
+               }\r
+               scan = scan->next;\r
+       }\r
+\r
+       Quit ("MM_ClearBlock: No purgable blocks!");\r
+}\r
+\r
+\r
+//==========================================================================\r
+\r
+/*\r
+===================\r
+=\r
+= MM_Startup\r
+=\r
+= Grabs all space from turbo with malloc/farmalloc\r
+= Allocates bufferseg misc buffer\r
+=\r
+===================\r
+*/\r
+\r
+static char *ParmStrings[] = {"noems","noxms",""};\r
+\r
+void MM_Startup (void)\r
+{\r
+       int i;\r
+       unsigned        long length;\r
+       void far        *start;\r
+       unsigned        segstart,seglength,endfree;\r
+\r
+       if (mmstarted)\r
+               MM_Shutdown ();\r
+\r
+\r
+       mmstarted = true;\r
+       bombonerror = true;\r
+//\r
+// set up the linked list (everything in the free list;\r
+//\r
+       mmhead = NULL;\r
+       mmfree = &mmblocks[0];\r
+       for (i=0;i<MAXBLOCKS-1;i++)\r
+               mmblocks[i].next = &mmblocks[i+1];\r
+       mmblocks[i].next = NULL;\r
+\r
+//\r
+// locked block of all memory until we punch out free space\r
+//\r
+       GETNEWBLOCK;\r
+       mmhead = mmnew;                         // this will allways be the first node\r
+       mmnew->start = 0;\r
+       mmnew->length = 0xffff;\r
+       mmnew->attributes = LOCKBIT;\r
+       mmnew->next = NULL;\r
+       mmrover = mmhead;\r
+\r
+\r
+//\r
+// get all available near conventional memory segments\r
+//\r
+       length=coreleft();\r
+       start = (void far *)(nearheap = malloc(length));\r
+\r
+       length -= 16-(FP_OFF(start)&15);\r
+       length -= SAVENEARHEAP;\r
+       seglength = length / 16;                        // now in paragraphs\r
+       segstart = FP_SEG(start)+(FP_OFF(start)+15)/16;\r
+       MML_UseSpace (segstart,seglength);\r
+       mminfo.nearheap = length;\r
+\r
+//\r
+// get all available far conventional memory segments\r
+//\r
+       length=farcoreleft();\r
+       start = farheap = farmalloc(length);\r
+       length -= 16-(FP_OFF(start)&15);\r
+       length -= SAVEFARHEAP;\r
+       seglength = length / 16;                        // now in paragraphs\r
+       segstart = FP_SEG(start)+(FP_OFF(start)+15)/16;\r
+       MML_UseSpace (segstart,seglength);\r
+       mminfo.farheap = length;\r
+       mminfo.mainmem = mminfo.nearheap + mminfo.farheap;\r
+\r
+\r
+//\r
+// detect EMS and allocate up to 64K at page frame\r
+//\r
+       mminfo.EMSmem = 0;\r
+       for (i = 1;i < _argc;i++)\r
+       {\r
+               if ( US_CheckParm(_argv[i],ParmStrings) == 0)\r
+                       goto emsskip;                           // param NOEMS\r
+       }\r
+\r
+       if (MML_CheckForEMS())\r
+       {\r
+               MML_SetupEMS();                                 // allocate space\r
+               MML_UseSpace (EMSpageframe,EMSpagesmapped*0x400);\r
+               MM_MapEMS();                                    // map in used pages\r
+               mminfo.EMSmem = EMSpagesmapped*0x4000l;\r
+       }\r
+\r
+//\r
+// detect XMS and get upper memory blocks\r
+//\r
+emsskip:\r
+       mminfo.XMSmem = 0;\r
+       for (i = 1;i < _argc;i++)\r
+       {\r
+               if ( US_CheckParm(_argv[i],ParmStrings) == 0)   // BUG: NOXMS is index 1, not 0\r
+                       goto xmsskip;                           // param NOXMS\r
+       }\r
+\r
+       if (MML_CheckForXMS())\r
+               MML_SetupXMS();                                 // allocate as many UMBs as possible\r
+\r
+//\r
+// allocate the misc buffer\r
+//\r
+xmsskip:\r
+       mmrover = mmhead;               // start looking for space after low block\r
+\r
+       MM_GetPtr (&bufferseg,BUFFERSIZE);\r
+}\r
+\r
+//==========================================================================\r
+\r
+/*\r
+====================\r
+=\r
+= MM_Shutdown\r
+=\r
+= Frees all conventional, EMS, and XMS allocated\r
+=\r
+====================\r
+*/\r
+\r
+void MM_Shutdown (void)\r
+{\r
+  if (!mmstarted)\r
+       return;\r
+\r
+  farfree (farheap);\r
+  free (nearheap);\r
+  MML_ShutdownEMS ();\r
+  MML_ShutdownXMS ();\r
+}\r
+\r
+//==========================================================================\r
+\r
+/*\r
+====================\r
+=\r
+= MM_GetPtr\r
+=\r
+= Allocates an unlocked, unpurgable block\r
+=\r
+====================\r
+*/\r
+\r
+void MM_GetPtr (memptr *baseptr,unsigned long size)\r
+{\r
+       mmblocktype far *scan,far *lastscan,far *endscan\r
+                               ,far *purge,far *next;\r
+       int                     search;\r
+       unsigned        needed,startseg;\r
+\r
+       needed = (size+15)/16;          // convert size from bytes to paragraphs\r
+\r
+       GETNEWBLOCK;                            // fill in start and next after a spot is found\r
+       mmnew->length = needed;\r
+       mmnew->useptr = baseptr;\r
+       mmnew->attributes = BASEATTRIBUTES;\r
+\r
+       for (search = 0; search<3; search++)\r
+       {\r
+       //\r
+       // first search:        try to allocate right after the rover, then on up\r
+       // second search:       search from the head pointer up to the rover\r
+       // third search:        compress memory, then scan from start\r
+               if (search == 1 && mmrover == mmhead)\r
+                       search++;\r
+\r
+               switch (search)\r
+               {\r
+               case 0:\r
+                       lastscan = mmrover;\r
+                       scan = mmrover->next;\r
+                       endscan = NULL;\r
+                       break;\r
+               case 1:\r
+                       lastscan = mmhead;\r
+                       scan = mmhead->next;\r
+                       endscan = mmrover;\r
+                       break;\r
+               case 2:\r
+                       MM_SortMem ();\r
+                       lastscan = mmhead;\r
+                       scan = mmhead->next;\r
+                       endscan = NULL;\r
+                       break;\r
+               }\r
+\r
+               startseg = lastscan->start + lastscan->length;\r
+\r
+               while (scan != endscan)\r
+               {\r
+                       if (scan->start - startseg >= needed)\r
+                       {\r
+                       //\r
+                       // got enough space between the end of lastscan and\r
+                       // the start of scan, so throw out anything in the middle\r
+                       // and allocate the new block\r
+                       //\r
+                               purge = lastscan->next;\r
+                               lastscan->next = mmnew;\r
+                               mmnew->start = *(unsigned *)baseptr = startseg;\r
+                               mmnew->next = scan;\r
+                               while ( purge != scan)\r
+                               {       // free the purgable block\r
+                                       next = purge->next;\r
+                                       FREEBLOCK(purge);\r
+                                       purge = next;           // purge another if not at scan\r
+                               }\r
+                               mmrover = mmnew;\r
+                               return; // good allocation!\r
+                       }\r
+\r
+                       //\r
+                       // if this block is purge level zero or locked, skip past it\r
+                       //\r
+                       if ( (scan->attributes & LOCKBIT)\r
+                               || !(scan->attributes & PURGEBITS) )\r
+                       {\r
+                               lastscan = scan;\r
+                               startseg = lastscan->start + lastscan->length;\r
+                       }\r
+\r
+\r
+                       scan=scan->next;                // look at next line\r
+               }\r
+       }\r
+\r
+       if (bombonerror)\r
+               Quit ("MM_GetPtr: Out of memory!");\r
+       else\r
+               mmerror = true;\r
+}\r
+\r
+//==========================================================================\r
+\r
+/*\r
+====================\r
+=\r
+= MM_FreePtr\r
+=\r
+= Allocates an unlocked, unpurgable block\r
+=\r
+====================\r
+*/\r
+\r
+void MM_FreePtr (memptr *baseptr)\r
+{\r
+       mmblocktype far *scan,far *last;\r
+\r
+       last = mmhead;\r
+       scan = last->next;\r
+\r
+       if (baseptr == mmrover->useptr) // removed the last allocated block\r
+               mmrover = mmhead;\r
+\r
+       while (scan->useptr != baseptr && scan)\r
+       {\r
+               last = scan;\r
+               scan = scan->next;\r
+       }\r
+\r
+       if (!scan)\r
+               Quit ("MM_FreePtr: Block not found!");\r
+\r
+       last->next = scan->next;\r
+\r
+       FREEBLOCK(scan);\r
+}\r
+//==========================================================================\r
+\r
+/*\r
+=====================\r
+=\r
+= MM_SetPurge\r
+=\r
+= Sets the purge level for a block (locked blocks cannot be made purgable)\r
+=\r
+=====================\r
+*/\r
+\r
+void MM_SetPurge (memptr *baseptr, int purge)\r
+{\r
+       mmblocktype far *start;\r
+\r
+       start = mmrover;\r
+\r
+       do\r
+       {\r
+               if (mmrover->useptr == baseptr)\r
+                       break;\r
+\r
+               mmrover = mmrover->next;\r
+\r
+               if (!mmrover)\r
+                       mmrover = mmhead;\r
+               else if (mmrover == start)\r
+                       Quit ("MM_SetPurge: Block not found!");\r
+\r
+       } while (1);\r
+\r
+       mmrover->attributes &= ~PURGEBITS;\r
+       mmrover->attributes |= purge;\r
+}\r
+\r
+//==========================================================================\r
+\r
+/*\r
+=====================\r
+=\r
+= MM_SetLock\r
+=\r
+= Locks / unlocks the block\r
+=\r
+=====================\r
+*/\r
+\r
+void MM_SetLock (memptr *baseptr, boolean locked)\r
+{\r
+       mmblocktype far *start;\r
+\r
+       start = mmrover;\r
+\r
+       do\r
+       {\r
+               if (mmrover->useptr == baseptr)\r
+                       break;\r
+\r
+               mmrover = mmrover->next;\r
+\r
+               if (!mmrover)\r
+                       mmrover = mmhead;\r
+               else if (mmrover == start)\r
+                       Quit ("MM_SetLock: Block not found!");\r
+\r
+       } while (1);\r
+\r
+       mmrover->attributes &= ~LOCKBIT;\r
+       mmrover->attributes |= locked*LOCKBIT;\r
+}\r
+\r
+//==========================================================================\r
+\r
+/*\r
+=====================\r
+=\r
+= MM_SortMem\r
+=\r
+= Throws out all purgable stuff and compresses movable blocks\r
+=\r
+=====================\r
+*/\r
+\r
+void MM_SortMem (void)\r
+{\r
+       mmblocktype far *scan,far *last,far *next;\r
+       unsigned        start,length,source,dest,oldborder;\r
+       int                     playing;\r
+\r
+       //\r
+       // lock down a currently playing sound\r
+       //\r
+       playing = SD_SoundPlaying ();\r
+       if (playing)\r
+       {\r
+               switch (SoundMode)\r
+               {\r
+               case sdm_PC:\r
+                       playing += STARTPCSOUNDS;\r
+                       break;\r
+               case sdm_AdLib:\r
+                       playing += STARTADLIBSOUNDS;\r
+                       break;\r
+               }\r
+               MM_SetLock(&(memptr)audiosegs[playing],true);\r
+       }\r
+\r
+\r
+       SD_StopSound();\r
+       oldborder = bordercolor;\r
+       VW_ColorBorder (15);\r
+\r
+       if (beforesort)\r
+               beforesort();\r
+\r
+       scan = mmhead;\r
+\r
+       last = NULL;            // shut up compiler warning\r
+\r
+       while (scan)\r
+       {\r
+               if (scan->attributes & LOCKBIT)\r
+               {\r
+               //\r
+               // block is locked, so try to pile later blocks right after it\r
+               //\r
+                       start = scan->start + scan->length;\r
+               }\r
+               else\r
+               {\r
+                       if (scan->attributes & PURGEBITS)\r
+                       {\r
+                       //\r
+                       // throw out the purgable block\r
+                       //\r
+                               next = scan->next;\r
+                               FREEBLOCK(scan);\r
+                               last->next = next;\r
+                               scan = next;\r
+                               continue;\r
+                       }\r
+                       else\r
+                       {\r
+                       //\r
+                       // push the non purgable block on top of the last moved block\r
+                       //\r
+                               if (scan->start != start)\r
+                               {\r
+                                       length = scan->length;\r
+                                       source = scan->start;\r
+                                       dest = start;\r
+                                       while (length > 0xf00)\r
+                                       {\r
+                                               movedata(source,0,dest,0,0xf00*16);\r
+                                               length -= 0xf00;\r
+                                               source += 0xf00;\r
+                                               dest += 0xf00;\r
+                                       }\r
+                                       movedata(source,0,dest,0,length*16);\r
+\r
+                                       scan->start = start;\r
+                                       *(unsigned *)scan->useptr = start;\r
+                               }\r
+                               start = scan->start + scan->length;\r
+                       }\r
+               }\r
+\r
+               last = scan;\r
+               scan = scan->next;              // go to next block\r
+       }\r
+\r
+       mmrover = mmhead;\r
+\r
+       if (aftersort)\r
+               aftersort();\r
+\r
+       VW_ColorBorder (oldborder);\r
+\r
+       if (playing)\r
+               MM_SetLock(&(memptr)audiosegs[playing],false);\r
+}\r
+\r
+\r
+//==========================================================================\r
+\r
+/*\r
+=====================\r
+=\r
+= MM_ShowMemory\r
+=\r
+=====================\r
+*/\r
+\r
+void MM_ShowMemory (void)\r
+{\r
+       mmblocktype far *scan;\r
+       unsigned color,temp;\r
+       long    end,owner;\r
+       char    scratch[80],str[10];\r
+\r
+       VW_SetDefaultColors();\r
+       VW_SetLineWidth(40);\r
+       temp = bufferofs;\r
+       bufferofs = 0;\r
+       VW_SetScreen (0,0);\r
+\r
+       scan = mmhead;\r
+\r
+       end = -1;\r
+\r
+//CA_OpenDebug ();\r
+\r
+       while (scan)\r
+       {\r
+               if (scan->attributes & PURGEBITS)\r
+                       color = 5;              // dark purple = purgable\r
+               else\r
+                       color = 9;              // medium blue = non purgable\r
+               if (scan->attributes & LOCKBIT)\r
+                       color = 12;             // red = locked\r
+               if (scan->start<=end)\r
+                       Quit ("MM_ShowMemory: Memory block order corrupted!");\r
+               end = scan->start+scan->length-1;\r
+               VW_Hlin(scan->start,(unsigned)end,0,color);\r
+               VW_Plot(scan->start,0,15);\r
+               if (scan->next->start > end+1)\r
+                       VW_Hlin(end+1,scan->next->start,0,0);   // black = free\r
+\r
+#if 0\r
+strcpy (scratch,"Size:");\r
+ltoa ((long)scan->length*16,str,10);\r
+strcat (scratch,str);\r
+strcat (scratch,"\tOwner:0x");\r
+owner = (unsigned)scan->useptr;\r
+ultoa (owner,str,16);\r
+strcat (scratch,str);\r
+strcat (scratch,"\n");\r
+write (debughandle,scratch,strlen(scratch));\r
+#endif\r
+\r
+               scan = scan->next;\r
+       }\r
+\r
+//CA_CloseDebug ();\r
+\r
+       IN_Ack();\r
+       VW_SetLineWidth(64);\r
+       bufferofs = temp;\r
+}\r
+\r
+//==========================================================================\r
+\r
+\r
+/*\r
+======================\r
+=\r
+= MM_UnusedMemory\r
+=\r
+= Returns the total free space without purging\r
+=\r
+======================\r
+*/\r
+\r
+long MM_UnusedMemory (void)\r
+{\r
+       unsigned free;\r
+       mmblocktype far *scan;\r
+\r
+       free = 0;\r
+       scan = mmhead;\r
+\r
+       while (scan->next)\r
+       {\r
+               free += scan->next->start - (scan->start + scan->length);\r
+               scan = scan->next;\r
+       }\r
+\r
+       return free*16l;\r
+}\r
+\r
+//==========================================================================\r
+\r
+\r
+/*\r
+======================\r
+=\r
+= MM_TotalFree\r
+=\r
+= Returns the total free space with purging\r
+=\r
+======================\r
+*/\r
+\r
+long MM_TotalFree (void)\r
+{\r
+       unsigned free;\r
+       mmblocktype far *scan;\r
+\r
+       free = 0;\r
+       scan = mmhead;\r
+\r
+       while (scan->next)\r
+       {\r
+               if ((scan->attributes&PURGEBITS) && !(scan->attributes&LOCKBIT))\r
+                       free += scan->length;\r
+               free += scan->next->start - (scan->start + scan->length);\r
+               scan = scan->next;\r
+       }\r
+\r
+       return free*16l;\r
+}\r
+\r
+//==========================================================================\r
+\r
+/*\r
+=====================\r
+=\r
+= MM_BombOnError\r
+=\r
+=====================\r
+*/\r
+\r
+void MM_BombOnError (boolean bomb)\r
+{\r
+       bombonerror = bomb;\r
+}\r
+\r
+\r
diff --git a/16/keen456/KEEN4-6/ID_MM.H b/16/keen456/KEEN4-6/ID_MM.H
new file mode 100755 (executable)
index 0000000..4cf2d16
--- /dev/null
@@ -0,0 +1,116 @@
+/* Reconstructed Commander Keen 4-6 Source Code\r
+ * Copyright (C) 2021 K1n9_Duk3\r
+ *\r
+ * This file is primarily based on:\r
+ * Catacomb 3-D Source Code\r
+ * Copyright (C) 1993-2014 Flat Rock Software\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License along\r
+ * with this program; if not, write to the Free Software Foundation, Inc.,\r
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+ */\r
+\r
+// ID_MM.H\r
+\r
+#ifndef __ID_CA__\r
+\r
+#define __ID_CA__\r
+\r
+#define SAVENEARHEAP   0x400           // space to leave in data segment\r
+#define SAVEFARHEAP            0                       // space to leave in far heap\r
+\r
+#define        BUFFERSIZE              0x1000          // miscelanious, allways available buffer\r
+\r
+#ifdef CAT3D\r
+#define MAXBLOCKS              600\r
+#else\r
+#define MAXBLOCKS              1200\r
+#endif\r
+\r
+\r
+//--------\r
+\r
+#define        EMS_INT                 0x67\r
+\r
+#define        EMS_STATUS              0x40\r
+#define        EMS_GETFRAME    0x41\r
+#define        EMS_GETPAGES    0x42\r
+#define        EMS_ALLOCPAGES  0x43\r
+#define        EMS_MAPPAGE             0x44\r
+#define        EMS_FREEPAGES   0x45\r
+#define        EMS_VERSION             0x46\r
+\r
+//--------\r
+\r
+#define        XMS_VERSION             0x00\r
+\r
+#define        XMS_ALLOCHMA    0x01\r
+#define        XMS_FREEHMA             0x02\r
+\r
+#define        XMS_GENABLEA20  0x03\r
+#define        XMS_GDISABLEA20 0x04\r
+#define        XMS_LENABLEA20  0x05\r
+#define        XMS_LDISABLEA20 0x06\r
+#define        XMS_QUERYA20    0x07\r
+\r
+#define        XMS_QUERYREE    0x08\r
+#define        XMS_ALLOC               0x09\r
+#define        XMS_FREE                0x0A\r
+#define        XMS_MOVE                0x0B\r
+#define        XMS_LOCK                0x0C\r
+#define        XMS_UNLOCK              0x0D\r
+#define        XMS_GETINFO             0x0E\r
+#define        XMS_RESIZE              0x0F\r
+\r
+#define        XMS_ALLOCUMB    0x10\r
+#define        XMS_FREEUMB             0x11\r
+\r
+//==========================================================================\r
+\r
+typedef void _seg * memptr;\r
+\r
+typedef struct\r
+{\r
+       long    nearheap,farheap,EMSmem,XMSmem,mainmem;\r
+} mminfotype;\r
+\r
+//==========================================================================\r
+\r
+extern mminfotype      mminfo;\r
+extern memptr          bufferseg;\r
+extern boolean         mmerror;\r
+\r
+extern void            (* beforesort) (void);\r
+extern void            (* aftersort) (void);\r
+\r
+//==========================================================================\r
+\r
+void MM_Startup (void);\r
+void MM_Shutdown (void);\r
+void MM_MapEMS (void);\r
+\r
+void MM_GetPtr (memptr *baseptr,unsigned long size);\r
+void MM_FreePtr (memptr *baseptr);\r
+\r
+void MM_SetPurge (memptr *baseptr, int purge);\r
+void MM_SetLock (memptr *baseptr, boolean locked);\r
+void MM_SortMem (void);\r
+\r
+void MM_ShowMemory (void);\r
+\r
+long MM_UnusedMemory (void);\r
+long MM_TotalFree (void);\r
+\r
+void MM_BombOnError (boolean bomb);\r
+\r
+#endif
\ No newline at end of file
diff --git a/16/keen456/KEEN4-6/ID_RF.C b/16/keen456/KEEN4-6/ID_RF.C
new file mode 100755 (executable)
index 0000000..a040c6b
--- /dev/null
@@ -0,0 +1,2965 @@
+/* Reconstructed Commander Keen 4-6 Source Code\r
+ * Copyright (C) 2021 K1n9_Duk3\r
+ *\r
+ * This file is primarily based on:\r
+ * Catacomb 3-D Source Code\r
+ * Copyright (C) 1993-2014 Flat Rock Software\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License along\r
+ * with this program; if not, write to the Free Software Foundation, Inc.,\r
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+ */\r
+\r
+// ID_RF.C\r
+\r
+/*\r
+=============================================================================\r
+\r
+notes\r
+-----\r
+\r
+scrolling more than one tile / refresh forces a total redraw\r
+\r
+two overlapping sprites of equal priority can change drawing order when\r
+updated\r
+\r
+=============================================================================\r
+*/\r
+\r
+#include "ID_HEADS.H"\r
+#pragma hdrstop\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                LOCAL CONSTANTS\r
+\r
+=============================================================================\r
+*/\r
+\r
+#define        SCREENTILESWIDE 20\r
+#define        SCREENTILESHIGH 13\r
+\r
+#define        SCREENSPACE             (SCREENWIDTH*240)\r
+#define FREEEGAMEM             (0x10000l-3l*SCREENSPACE)\r
+\r
+//\r
+// the update array must have enough space for two screens that can float\r
+// up two two tiles each way\r
+//\r
+// (PORTTILESWIDE+1)*PORTTILESHIGH must be even so the arrays can be cleared\r
+// by word width instructions\r
+\r
+#define        UPDATESCREENSIZE        (UPDATEWIDE*PORTTILESHIGH+2)\r
+#define        UPDATESPARESIZE         (UPDATEWIDE*2+4)\r
+#define UPDATESIZE                     (UPDATESCREENSIZE+2*UPDATESPARESIZE)\r
+\r
+#define G_EGASX_SHIFT  7       // global >> ?? = screen x\r
+#define G_CGASX_SHIFT  6       // global >> ?? = screen x\r
+#define G_SY_SHIFT             4       // global >> ?? = screen y\r
+\r
+unsigned       SX_T_SHIFT;             // screen x >> ?? = tile EGA = 1, CGA = 2;\r
+#define        SY_T_SHIFT              4       // screen y >> ?? = tile\r
+\r
+\r
+#define        EGAPORTSCREENWIDE       42\r
+#define        CGAPORTSCREENWIDE       84\r
+#define        PORTSCREENHIGH          224\r
+\r
+#define        UPDATESCREENSIZE        (UPDATEWIDE*PORTTILESHIGH+2)\r
+#define        UPDATESPARESIZE         (UPDATEWIDE*2+4)\r
+#define UPDATESIZE                     (UPDATESCREENSIZE+2*UPDATESPARESIZE)\r
+\r
+#define MAXSCROLLEDGES 6\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                  LOCAL TYPES\r
+\r
+=============================================================================\r
+*/\r
+\r
+typedef        struct spriteliststruct\r
+{\r
+       int                     screenx,screeny;\r
+       int                     width,height;\r
+\r
+       unsigned        grseg,sourceofs,planesize;\r
+       drawtype        draw;\r
+       unsigned        tilex,tiley,tilewide,tilehigh;\r
+       int                     priority,updatecount;\r
+       struct spriteliststruct **prevptr,*nextsprite;\r
+} spritelisttype;\r
+\r
+\r
+typedef struct\r
+{\r
+       int                     screenx,screeny;\r
+       int                     width,height;\r
+} eraseblocktype;\r
+\r
+\r
+typedef struct\r
+{\r
+       unsigned        current;                // foreground tiles have high bit set\r
+       int                     count;\r
+#ifdef KEEN6\r
+       unsigned        soundtile;\r
+       unsigned        visible;\r
+       int             sound;\r
+#endif\r
+} tiletype;\r
+\r
+\r
+typedef struct animtilestruct\r
+{\r
+       unsigned        x,y,tile;\r
+       tiletype        *chain;\r
+       unsigned        far *mapplane;\r
+       struct animtilestruct **prevptr,*nexttile;\r
+} animtiletype;\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                GLOBAL VARIABLES\r
+\r
+=============================================================================\r
+*/\r
+\r
+unsigned       tics;\r
+long           lasttimecount;\r
+\r
+boolean                compatability;                  // crippled refresh for wierdo SVGAs\r
+\r
+unsigned       mapwidth,mapheight,mapbyteswide,mapwordswide\r
+                       ,mapbytesextra,mapwordsextra;\r
+unsigned       mapbwidthtable[MAXMAPHEIGHT];\r
+\r
+//\r
+// Global : Actor coordinates are in this, at 1/16 th of a pixel, to allow\r
+// for fractional movement and acceleration.\r
+//\r
+// Tiles  : Tile offsets from the upper left corner of the current map.\r
+//\r
+// Screen : Graphics level offsets from map origin, x in bytes, y in pixels.\r
+// originxscreen is the same spot as originxtile, just with extra precision\r
+// so graphics don't need to be done in tile boundaries.\r
+//\r
+\r
+unsigned       originxglobal,originyglobal;\r
+unsigned       originxtile,originytile;\r
+unsigned       originxscreen,originyscreen;\r
+unsigned       originmap;\r
+unsigned       originxmin,originxmax,originymin,originymax;\r
+\r
+unsigned       masterofs;\r
+\r
+//\r
+// Table of the offsets from bufferofs of each tile spot in the\r
+// view port.  The extra wide tile should never be drawn, but the space\r
+// is needed to account for the extra 0 in the update arrays.  Built by\r
+// RF_Startup\r
+//\r
+\r
+unsigned       blockstarts[UPDATEWIDE*UPDATEHIGH];\r
+unsigned       updatemapofs[UPDATEWIDE*UPDATEHIGH];\r
+\r
+unsigned       uwidthtable[PORTTILESHIGH];             // lookup instead of multiply\r
+\r
+byte           update[2][UPDATESIZE];\r
+byte           *updateptr,*baseupdateptr,                                              // current start of update window\r
+                       *updatestart[2],\r
+                       *baseupdatestart[2];\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                LOCAL VARIABLES\r
+\r
+=============================================================================\r
+*/\r
+\r
+static         char    scratch[20],str[80];\r
+\r
+tiletype       allanims[MAXANIMTYPES];\r
+unsigned       numanimchains;\r
+\r
+void           (*refreshvector) (void);\r
+\r
+unsigned       screenstart[3] =\r
+       {0,SCREENSPACE,SCREENSPACE*2};\r
+\r
+unsigned       xpanmask;                       // prevent panning to odd pixels\r
+\r
+unsigned       screenpage;                     // screen currently being displayed\r
+unsigned       otherpage;\r
+\r
+\r
+spritelisttype spritearray[MAXSPRITES],*prioritystart[PRIORITIES],\r
+                               *spritefreeptr;\r
+\r
+animtiletype   animarray[MAXANIMTILES],*animhead,*animfreeptr;\r
+\r
+int                            animfreespot;\r
+\r
+eraseblocktype eraselist[2][MAXSPRITES],*eraselistptr[2];\r
+\r
+int            hscrollblocks,vscrollblocks;\r
+int            hscrolledge[MAXSCROLLEDGES],vscrolledge[MAXSCROLLEDGES];\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                LOCAL PROTOTYPES\r
+\r
+=============================================================================\r
+*/\r
+\r
+void RFL_NewTile (unsigned updateoffset);\r
+void RFL_MaskForegroundTiles (void);\r
+void RFL_UpdateTiles (void);\r
+\r
+void RFL_BoundScroll (int x, int y);\r
+void RFL_CalcOriginStuff (long x, long y);\r
+void RFL_ClearScrollBlocks (void);\r
+void RFL_InitSpriteList (void);\r
+void RFL_InitAnimList (void);\r
+void RFL_CheckForAnimTile (unsigned x, unsigned y);\r
+void RFL_AnimateTiles (void);\r
+void RFL_RemoveAnimsOnX (unsigned x);\r
+void RFL_RemoveAnimsOnY (unsigned y);\r
+void RFL_EraseBlocks (void);\r
+void RFL_UpdateSprites (void);\r
+\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                        GRMODE INDEPENDANT ROUTINES\r
+\r
+=============================================================================\r
+*/\r
+\r
+\r
+/*\r
+=====================\r
+=\r
+= RF_Startup\r
+=\r
+=====================\r
+*/\r
+\r
+static char *ParmStrings[] = {"comp",""};\r
+\r
+void RF_Startup (void)\r
+{\r
+       int i,x,y;\r
+       unsigned        *blockstart;\r
+\r
+#ifndef KEEN\r
+       //\r
+       // Keen 4-6 store the compatability setting in the game's config file.\r
+       // The setting is loaded from that file AFTER RF_Startup is executed,\r
+       // making this check useless (unless the config file doesn't exist).\r
+       // Instead, US_Startup now checks for that parameter after the config\r
+       // file has been read.\r
+       //\r
+       if (grmode == EGAGR)\r
+               for (i = 1;i < _argc;i++)\r
+                       if (US_CheckParm(_argv[i],ParmStrings) == 0)\r
+                       {\r
+                               compatability = true;\r
+                               break;\r
+                       }\r
+#endif\r
+\r
+       for (i=0;i<PORTTILESHIGH;i++)\r
+               uwidthtable[i] = UPDATEWIDE*i;\r
+\r
+       originxmin = originymin = MAPBORDER*TILEGLOBAL;\r
+\r
+       eraselistptr[0] = &eraselist[0][0];\r
+       eraselistptr[1] = &eraselist[1][0];\r
+\r
+\r
+\r
+       if (grmode == EGAGR)\r
+       {\r
+               SX_T_SHIFT = 1;\r
+\r
+               baseupdatestart[0] = &update[0][UPDATESPARESIZE];\r
+               baseupdatestart[1] = &update[1][UPDATESPARESIZE];\r
+\r
+               screenpage = 0;\r
+               otherpage = 1;\r
+               displayofs = screenstart[screenpage];\r
+               bufferofs = screenstart[otherpage];\r
+               masterofs = screenstart[2];\r
+\r
+               updateptr = baseupdatestart[otherpage];\r
+\r
+               blockstart = &blockstarts[0];\r
+               for (y=0;y<UPDATEHIGH;y++)\r
+                       for (x=0;x<UPDATEWIDE;x++)\r
+                               *blockstart++ = SCREENWIDTH*16*y+x*TILEWIDTH;\r
+\r
+               xpanmask = 6;   // dont pan to odd pixels\r
+       }\r
+\r
+       else if (grmode == CGAGR)\r
+       {\r
+               SX_T_SHIFT = 2;\r
+\r
+               updateptr = baseupdateptr = &update[0][UPDATESPARESIZE];\r
+\r
+               bufferofs = 0;\r
+               masterofs = 0x8000;\r
+\r
+               blockstart = &blockstarts[0];\r
+               for (y=0;y<UPDATEHIGH;y++)\r
+                       for (x=0;x<UPDATEWIDE;x++)\r
+                               *blockstart++ = SCREENWIDTH*16*y+x*TILEWIDTH;\r
+       }\r
+}\r
+\r
+\r
+\r
+\r
+/*\r
+=====================\r
+=\r
+= RF_Shutdown\r
+=\r
+=====================\r
+*/\r
+\r
+void RF_Shutdown (void)\r
+{\r
+\r
+}\r
+\r
+//===========================================================================\r
+\r
+\r
+/*\r
+=====================\r
+=\r
+= RF_FixOfs\r
+=\r
+= Sets bufferofs,displayofs, and masterofs to regular values, for the\r
+= occasions when you have moved them around manually\r
+=\r
+=====================\r
+*/\r
+\r
+void RF_FixOfs (void)\r
+{\r
+       screenstart[0] = 0;\r
+       screenstart[1] = SCREENSPACE;\r
+       screenstart[2] = SCREENSPACE*2;\r
+\r
+       if (grmode == EGAGR)\r
+       {\r
+               screenpage = 0;\r
+               otherpage = 1;\r
+               panx = pany = pansx = pansy = panadjust = 0;\r
+               displayofs = screenstart[screenpage];\r
+               bufferofs = screenstart[otherpage];\r
+               masterofs = screenstart[2];\r
+               VW_SetScreen (displayofs,0);\r
+       }\r
+       else\r
+       {\r
+               panx = pany = pansx = pansy = panadjust = 0;\r
+               bufferofs = 0;\r
+               masterofs = 0x8000;\r
+       }\r
+}\r
+\r
+\r
+//===========================================================================\r
+\r
+/*\r
+=====================\r
+=\r
+= RF_NewMap\r
+=\r
+= Makes some convienient calculations based on maphead->\r
+=\r
+=====================\r
+*/\r
+\r
+void RF_NewMap (void)\r
+{\r
+       int i,x,y;\r
+       unsigned spot,*table;\r
+\r
+       mapwidth = mapheaderseg[mapon]->width;\r
+       mapbyteswide = 2*mapwidth;\r
+       mapheight = mapheaderseg[mapon]->height;\r
+       mapwordsextra = mapwidth-PORTTILESWIDE;\r
+       mapbytesextra = 2*mapwordsextra;\r
+\r
+//\r
+// make a lookup table for the maps left edge\r
+//\r
+       if (mapheight > MAXMAPHEIGHT)\r
+               Quit ("RF_NewMap: Map too tall!");\r
+       spot = 0;\r
+       for (i=0;i<mapheight;i++)\r
+       {\r
+         mapbwidthtable[i] = spot;\r
+         spot += mapbyteswide;\r
+       }\r
+\r
+//\r
+// fill in updatemapofs with the new width info\r
+//\r
+       table = &updatemapofs[0];\r
+       for (y=0;y<PORTTILESHIGH;y++)\r
+               for (x=0;x<UPDATEWIDE;x++)\r
+                       *table++ = mapbwidthtable[y]+x*2;\r
+\r
+//\r
+// the y max value clips off the bottom half of a tile so a map that is\r
+// 13 + MAPBORDER*2 tile high will not scroll at all vertically\r
+//\r
+       originxmax = (mapwidth-MAPBORDER-SCREENTILESWIDE)*TILEGLOBAL;\r
+       originymax = (mapheight-MAPBORDER-SCREENTILESHIGH)*TILEGLOBAL;\r
+       if (originxmax<originxmin)              // for very small maps\r
+               originxmax=originxmin;\r
+       if (originymax<originymin)\r
+               originymax=originymin;\r
+\r
+//\r
+// clear out the lists\r
+//\r
+       RFL_InitSpriteList ();\r
+       RFL_InitAnimList ();\r
+       RFL_ClearScrollBlocks ();\r
+       RF_SetScrollBlock (0,MAPBORDER-1,true);\r
+       RF_SetScrollBlock (0,mapheight-MAPBORDER,true);\r
+       RF_SetScrollBlock (MAPBORDER-1,0,false);\r
+       RF_SetScrollBlock (mapwidth-MAPBORDER,0,false);\r
+\r
+\r
+       lasttimecount = TimeCount;              // setup for adaptive timing\r
+       tics = 1;\r
+}\r
+\r
+//===========================================================================\r
+\r
+#ifdef KEEN6\r
+/*\r
+==========================\r
+=\r
+= RFL_CheckTileSound\r
+=\r
+= Checks if the tile plays a sound and if so adds that info to the animation\r
+=\r
+==========================\r
+*/\r
+\r
+#define NUMSOUNDTILES 2\r
+typedef struct {\r
+       unsigned tilenums[NUMSOUNDTILES];\r
+       int sounds[NUMSOUNDTILES];\r
+} tilesoundtype;\r
+\r
+tilesoundtype far soundtiles = {\r
+       {2152|0x8000, 2208|0x8000},\r
+       {SND_STOMP,   SND_FLAME}\r
+};\r
+\r
+void RFL_CheckTileSound(tiletype *anim, unsigned tile)\r
+{\r
+       int i;\r
+\r
+       for (i=0; i<NUMSOUNDTILES; i++)\r
+       {\r
+               if (soundtiles.tilenums[i] == tile)\r
+               {\r
+                       anim->soundtile = tile;\r
+                       anim->sound = soundtiles.sounds[i];\r
+                       break;\r
+               }\r
+       }\r
+}\r
+\r
+#endif\r
+\r
+//===========================================================================\r
+\r
+/*\r
+==========================\r
+=\r
+= RF_MarkTileGraphics\r
+=\r
+= Goes through mapplane[0/1] and marks all background/foreground tiles\r
+= needed, then follows all animation sequences to make sure animated\r
+= tiles get all the stages.  Every unique animating tile is given an\r
+= entry in allanims[], so every instance of that tile will animate at the\r
+= same rate.  The info plane for each animating tile will hold a pointer\r
+= into allanims[], therefore you can't have both an animating foreground\r
+= and background tile in the same spot!\r
+=\r
+==========================\r
+*/\r
+\r
+void RF_MarkTileGraphics (void)\r
+{\r
+       unsigned        size;\r
+       int                     tile,next,anims,change;\r
+       unsigned        far     *start,far *end,far *info;\r
+       unsigned        i,tilehigh;\r
+       char            str[80],str2[10];\r
+\r
+       memset (allanims,0,sizeof(allanims));\r
+       numanimchains = 0;\r
+\r
+       size = mapwidth*mapheight;\r
+\r
+//\r
+// background plane\r
+//\r
+       start = mapsegs[0];\r
+       info = mapsegs[2];\r
+       end = start+size;\r
+       do\r
+       {\r
+               tile = *start++;\r
+               if (tile>=0)                    // <0 is a tile that is never drawn\r
+               {\r
+                       CA_MarkGrChunk(STARTTILE16+tile);\r
+                       if (tinf[ANIM+tile])\r
+                       {\r
+                               // this tile will animated\r
+\r
+                               if (tinf[SPEED+tile])\r
+                               {\r
+                                       if (!tinf[ANIM+tile])\r
+                                       {\r
+                                               strcpy (str,"RF_MarkTileGraphics: Background anim of 0:");\r
+                                               itoa (tile,str2,10);\r
+                                               strcat (str,str2);\r
+                                               Quit (str);\r
+                                       }\r
+                                       for (i=0;i<numanimchains;i++)\r
+                                               if (allanims[i].current == tile)\r
+                                               {\r
+                                                       *info = (unsigned)&allanims[i];\r
+                                                       goto nextback;\r
+                                               }\r
+\r
+                                       // new chain of animating tiles\r
+\r
+                                       if (i>=MAXANIMTYPES)\r
+                                               Quit ("RF_MarkTileGraphics: Too many unique animated tiles!");\r
+                                       allanims[i].current = tile;\r
+                                       allanims[i].count = tinf[SPEED+tile];\r
+#ifdef KEEN6\r
+                                       allanims[i].visible = 0;\r
+                                       allanims[i].sound = -1;\r
+#endif\r
+                                       *info = (unsigned)&allanims[i];\r
+                                       numanimchains++;\r
+                               }\r
+#ifdef KEEN6\r
+                               RFL_CheckTileSound(&allanims[i], tile);\r
+#endif\r
+\r
+                               anims = 0;\r
+                               change = (signed char)(tinf[ANIM+tile]);\r
+                               next = tile+change;\r
+                               while (change && next != tile)\r
+                               {\r
+#ifdef KEEN6\r
+                                       RFL_CheckTileSound(&allanims[i], next);\r
+#endif\r
+                                       CA_MarkGrChunk(STARTTILE16+next);\r
+                                       change = (signed char)(tinf[ANIM+next]);\r
+                                       next += change;\r
+                                       if (++anims > 20)\r
+                                       {\r
+                                               strcpy (str,"RF_MarkTileGraphics: Unending background animation:");\r
+                                               itoa (next,str2,10);\r
+                                               strcat (str,str2);\r
+                                               Quit (str);\r
+                                       }\r
+                               }\r
+\r
+                       }\r
+               }\r
+nextback:\r
+               info++;\r
+       } while (start<end);\r
+\r
+//\r
+// foreground plane\r
+//\r
+       start = mapsegs[1];\r
+       info = mapsegs[2];\r
+       end = start+size;\r
+       do\r
+       {\r
+               tile = *start++;\r
+               if (tile>=0)                    // <0 is a tile that is never drawn\r
+               {\r
+                       CA_MarkGrChunk(STARTTILE16M+tile);\r
+                       if (tinf[MANIM+tile])\r
+                       {\r
+                               // this tile will animated\r
+\r
+                               if (tinf[MSPEED+tile])\r
+                               {\r
+                                       if (!tinf[MANIM+tile])\r
+                                       {\r
+                                               strcpy (str,"RF_MarkTileGraphics: Foreground anim of 0:");\r
+                                               itoa (tile,str2,10);\r
+                                               strcat (str,str2);\r
+                                               Quit (str);\r
+                                       }\r
+                                       tilehigh = tile | 0x8000;       // foreground tiles have high bit\r
+                                       for (i=0;i<numanimchains;i++)\r
+                                               if (allanims[i].current == tilehigh)\r
+                                               {\r
+                                                       *info = (unsigned)&allanims[i];\r
+                                                       goto nextfront;\r
+                                               }\r
+\r
+                                       // new chain of animating tiles\r
+\r
+                                       if (i>=MAXANIMTYPES)\r
+                                               Quit ("RF_MarkTileGraphics: Too many unique animated tiles!");\r
+                                       allanims[i].current = tilehigh;\r
+                                       allanims[i].count = tinf[MSPEED+tile];\r
+#ifdef KEEN6\r
+                                       allanims[i].visible = 0;\r
+                                       allanims[i].sound = -1;\r
+#endif\r
+                                       *info = (unsigned)&allanims[i];\r
+                                       numanimchains++;\r
+                               }\r
+\r
+#ifdef KEEN6\r
+                               RFL_CheckTileSound(&allanims[i], tilehigh);\r
+#endif\r
+                               anims = 0;\r
+                               change = (signed char)(tinf[MANIM+tile]);\r
+                               next = tile+change;\r
+                               while (change && next != tile)\r
+                               {\r
+#ifdef KEEN6\r
+                                       RFL_CheckTileSound(&allanims[i], next | 0x8000);        // foreground tiles have high bit\r
+#endif\r
+                                       CA_MarkGrChunk(STARTTILE16M+next);\r
+                                       change = (signed char)(tinf[MANIM+next]);\r
+                                       next += change;\r
+                                       if (++anims > 20)\r
+                                       {\r
+                                               strcpy (str,"RF_MarkTileGraphics: Unending foreground animation:");\r
+                                               itoa (next,str2,10);\r
+                                               strcat (str,str2);\r
+                                               Quit (str);\r
+                                       }\r
+                               }\r
+\r
+                       }\r
+               }\r
+nextfront:\r
+               info++;\r
+       } while (start<end);\r
+}\r
+\r
+\r
+//===========================================================================\r
+\r
+\r
+/*\r
+=========================\r
+=\r
+= RFL_InitAnimList\r
+=\r
+= Call to clear out the entire animating tile list and return all of them to\r
+= the free list.\r
+=\r
+=========================\r
+*/\r
+\r
+void RFL_InitAnimList (void)\r
+{\r
+       int     i;\r
+\r
+       animfreeptr = &animarray[0];\r
+\r
+       for (i=0;i<MAXANIMTILES-1;i++)\r
+               animarray[i].nexttile = &animarray[i+1];\r
+\r
+       animarray[i].nexttile = NULL;\r
+\r
+       animhead = NULL;                        // nothing in list\r
+\r
+#ifdef KEEN6\r
+       {\r
+               tiletype *anim;\r
+\r
+               for (anim = allanims; anim->current != 0; anim++)\r
+                       anim->visible = 0;\r
+       }\r
+#endif\r
+}\r
+\r
+\r
+/*\r
+====================\r
+=\r
+= RFL_CheckForAnimTile\r
+=\r
+====================\r
+*/\r
+\r
+void RFL_CheckForAnimTile (unsigned x, unsigned y)\r
+{\r
+       unsigned        tile,offset,speed,lasttime,thistime,timemissed;\r
+       unsigned        far *map;\r
+       animtiletype    *anim,*next;\r
+\r
+// the info plane of each animating tile has a near pointer into allanims[]\r
+// which gives the current state of all concurrently animating tiles\r
+\r
+       offset = mapbwidthtable[y]/2+x;\r
+\r
+//\r
+// background\r
+//\r
+       map = mapsegs[0]+offset;\r
+       tile = *map;\r
+       if (tinf[ANIM+tile] && tinf[SPEED+tile])\r
+       {\r
+               if (!animfreeptr)\r
+                       Quit ("RF_CheckForAnimTile: No free spots in tilearray!");\r
+               anim = animfreeptr;\r
+               animfreeptr = animfreeptr->nexttile;\r
+               next = animhead;                                // stick it at the start of the list\r
+               animhead = anim;\r
+               if (next)\r
+                       next->prevptr = &anim->nexttile;\r
+               anim->nexttile = next;\r
+               anim->prevptr = &animhead;\r
+\r
+               anim->x = x;\r
+               anim->y = y;\r
+               anim->tile = tile;\r
+               anim->mapplane = map;\r
+               anim->chain = (tiletype *)*(mapsegs[2]+offset);\r
+#ifdef KEEN6\r
+               anim->chain->visible++;\r
+#endif\r
+       }\r
+\r
+//\r
+// foreground\r
+//\r
+       map = mapsegs[1]+offset;\r
+       tile = *map;\r
+       if (tinf[MANIM+tile] && tinf[MSPEED+tile])\r
+       {\r
+               if (!animfreeptr)\r
+                       Quit ("RF_CheckForAnimTile: No free spots in tilearray!");\r
+               anim = animfreeptr;\r
+               animfreeptr = animfreeptr->nexttile;\r
+               next = animhead;                                // stick it at the start of the list\r
+               animhead = anim;\r
+               if (next)\r
+                       next->prevptr = &anim->nexttile;\r
+               anim->nexttile = next;\r
+               anim->prevptr = &animhead;\r
+\r
+               anim->x = x;\r
+               anim->y = y;\r
+               anim->tile = tile;\r
+               anim->mapplane = map;\r
+               anim->chain = (tiletype *)*(mapsegs[2]+offset);\r
+#ifdef KEEN6\r
+               anim->chain->visible++;\r
+#endif\r
+       }\r
+\r
+}\r
+\r
+\r
+/*\r
+====================\r
+=\r
+= RFL_RemoveAnimsOnX\r
+=\r
+====================\r
+*/\r
+\r
+void RFL_RemoveAnimsOnX (unsigned x)\r
+{\r
+       animtiletype *current,*next;\r
+\r
+       current = animhead;\r
+       while (current)\r
+       {\r
+               if (current->x == x)\r
+               {\r
+#ifdef KEEN6\r
+                       current->chain->visible--;\r
+#endif\r
+                       *(void **)current->prevptr = current->nexttile;\r
+                       if (current->nexttile)\r
+                               current->nexttile->prevptr = current->prevptr;\r
+                       next = current->nexttile;\r
+                       current->nexttile = animfreeptr;\r
+                       animfreeptr = current;\r
+                       current = next;\r
+               }\r
+               else\r
+                       current = current->nexttile;\r
+       }\r
+}\r
+\r
+\r
+/*\r
+====================\r
+=\r
+= RFL_RemoveAnimsOnY\r
+=\r
+====================\r
+*/\r
+\r
+void RFL_RemoveAnimsOnY (unsigned y)\r
+{\r
+       animtiletype *current,*next;\r
+\r
+       current = animhead;\r
+       while (current)\r
+       {\r
+               if (current->y == y)\r
+               {\r
+#ifdef KEEN6\r
+                       current->chain->visible--;\r
+#endif\r
+                       *(void **)current->prevptr = current->nexttile;\r
+                       if (current->nexttile)\r
+                               current->nexttile->prevptr = current->prevptr;\r
+                       next = current->nexttile;\r
+                       current->nexttile = animfreeptr;\r
+                       animfreeptr = current;\r
+                       current = next;\r
+               }\r
+               else\r
+                       current = current->nexttile;\r
+       }\r
+}\r
+\r
+\r
+/*\r
+====================\r
+=\r
+= RFL_RemoveAnimsInBlock\r
+=\r
+====================\r
+*/\r
+\r
+void RFL_RemoveAnimsInBlock (unsigned x, unsigned y, unsigned width, unsigned height)\r
+{\r
+       animtiletype *current,*next;\r
+\r
+       current = animhead;\r
+       while (current)\r
+       {\r
+               if (current->x - x < width && current->y - y < height)\r
+               {\r
+#ifdef KEEN6\r
+                       current->chain->visible--;\r
+#endif\r
+                       *(void **)current->prevptr = current->nexttile;\r
+                       if (current->nexttile)\r
+                               current->nexttile->prevptr = current->prevptr;\r
+                       next = current->nexttile;\r
+                       current->nexttile = animfreeptr;\r
+                       animfreeptr = current;\r
+                       current = next;\r
+               }\r
+               else\r
+                       current = current->nexttile;\r
+       }\r
+}\r
+\r
+\r
+/*\r
+====================\r
+=\r
+= RFL_AnimateTiles\r
+=\r
+====================\r
+*/\r
+\r
+void RFL_AnimateTiles (void)\r
+{\r
+       animtiletype *current;\r
+       unsigned        updateofs,tile,x,y;\r
+       tiletype        *anim;\r
+\r
+//\r
+// animate the lists of tiles\r
+//\r
+       anim = &allanims[0];\r
+       while (anim->current)\r
+       {\r
+               anim->count-=tics;\r
+               while ( anim->count < 1)\r
+               {\r
+                       if (anim->current & 0x8000)\r
+                       {\r
+                               tile = anim->current & 0x7fff;\r
+                               tile += (signed char)tinf[MANIM+tile];\r
+                               anim->count += tinf[MSPEED+tile];\r
+                               tile |= 0x8000;\r
+                       }\r
+                       else\r
+                       {\r
+                               tile = anim->current;\r
+                               tile += (signed char)tinf[ANIM+tile];\r
+                               anim->count += tinf[SPEED+tile];\r
+                       }\r
+                       anim->current = tile;\r
+#ifdef KEEN6\r
+                       if (anim->visible && anim->current == anim->soundtile && anim->sound != -1)\r
+                       {\r
+                               SD_PlaySound(anim->sound);\r
+                       }\r
+#endif\r
+               }\r
+               anim++;\r
+       }\r
+\r
+\r
+//\r
+// traverse the list of animating tiles\r
+//\r
+       current = animhead;\r
+       while (current)\r
+       {\r
+               tile =current->chain->current;\r
+               if ( tile != current->tile)\r
+               {\r
+               // tile has animated\r
+               //\r
+               // remove tile from master screen cache,\r
+               // change a tile to its next state, set the structure up for\r
+               // next animation, and post an update region to both update pages\r
+               //\r
+                       current->tile = tile;\r
+\r
+                       *(current->mapplane) = tile & 0x7fff;           // change in map\r
+\r
+                       x = current->x-originxtile;\r
+                       y = current->y-originytile;\r
+\r
+                       if (x>=PORTTILESWIDE || y>=PORTTILESHIGH)\r
+                               Quit ("RFL_AnimateTiles: Out of bounds!");\r
+\r
+                       updateofs = uwidthtable[y] + x;\r
+                       RFL_NewTile(updateofs);                         // puts "1"s in both pages\r
+               }\r
+               current = current->nexttile;\r
+       }\r
+}\r
+\r
+\r
+//===========================================================================\r
+\r
+/*\r
+=========================\r
+=\r
+= RFL_InitSpriteList\r
+=\r
+= Call to clear out the entire sprite list and return all of them to\r
+= the free list.\r
+=\r
+=========================\r
+*/\r
+\r
+void RFL_InitSpriteList (void)\r
+{\r
+       int     i;\r
+\r
+       spritefreeptr = &spritearray[0];\r
+       for (i=0;i<MAXSPRITES-1;i++)\r
+               spritearray[i].nextsprite = &spritearray[i+1];\r
+\r
+       spritearray[i].nextsprite = NULL;\r
+\r
+// NULL in all priority levels\r
+\r
+       memset (prioritystart,0,sizeof(prioritystart));\r
+}\r
+\r
+//===========================================================================\r
+\r
+/*\r
+=================\r
+=\r
+= RFL_CalcOriginStuff\r
+=\r
+= Calculate all the global variables for a new position\r
+= Long parms so position can be clipped to a maximum near 64k\r
+=\r
+=================\r
+*/\r
+\r
+void RFL_CalcOriginStuff (long x, long y)\r
+{\r
+       originxglobal = x;\r
+       originyglobal = y;\r
+       originxtile = originxglobal>>G_T_SHIFT;\r
+       originytile = originyglobal>>G_T_SHIFT;\r
+       originxscreen = originxtile<<SX_T_SHIFT;\r
+       originyscreen = originytile<<SY_T_SHIFT;\r
+       originmap = mapbwidthtable[originytile] + originxtile*2;\r
+\r
+#if GRMODE == EGAGR\r
+       panx = (originxglobal>>G_P_SHIFT) & 15;\r
+       pansx = panx & 8;\r
+       pany = pansy = (originyglobal>>G_P_SHIFT) & 15;\r
+       panadjust = panx/8 + ylookup[pany];\r
+#endif\r
+\r
+#if GRMODE == CGAGR\r
+       panx = (originxglobal>>G_P_SHIFT) & 15;\r
+       pansx = panx & 12;\r
+       pany = pansy = (originyglobal>>G_P_SHIFT) & 15;\r
+       panadjust = pansx/4 + ylookup[pansy];\r
+#endif\r
+\r
+}\r
+\r
+\r
+/*\r
+=================\r
+=\r
+= RFL_ClearScrollBlocks\r
+=\r
+=================\r
+*/\r
+\r
+void RFL_ClearScrollBlocks (void)\r
+{\r
+       hscrollblocks = vscrollblocks = 0;\r
+}\r
+\r
+\r
+/*\r
+=================\r
+=\r
+= RF_SetScrollBlock\r
+=\r
+= Sets a horizontal or vertical scroll block\r
+= a horizontal block is ----, meaning it blocks up/down movement\r
+=\r
+=================\r
+*/\r
+\r
+void RF_SetScrollBlock (int x, int y, boolean horizontal)\r
+{\r
+       if (horizontal)\r
+       {\r
+               hscrolledge[hscrollblocks] = y;\r
+               if (hscrollblocks++ == MAXSCROLLEDGES)\r
+                       Quit ("RF_SetScrollBlock: Too many horizontal scroll blocks");\r
+       }\r
+       else\r
+       {\r
+               vscrolledge[vscrollblocks] = x;\r
+               if (vscrollblocks++ == MAXSCROLLEDGES)\r
+                       Quit ("RF_SetScrollBlock: Too many vertical scroll blocks");\r
+       }\r
+}\r
+\r
+\r
+/*\r
+=================\r
+=\r
+= RFL_BoundScroll\r
+=\r
+= Bound a given x/y movement to scroll blocks\r
+=\r
+=================\r
+*/\r
+\r
+void RFL_BoundScroll (int x, int y)\r
+{\r
+       int     check,newxtile,newytile;\r
+\r
+       originxglobal += x;\r
+       originyglobal += y;\r
+\r
+       newxtile= originxglobal >> G_T_SHIFT;\r
+       newytile = originyglobal >> G_T_SHIFT;\r
+\r
+       if (x>0)\r
+       {\r
+               newxtile+=SCREENTILESWIDE;\r
+               for (check=0;check<vscrollblocks;check++)\r
+                       if (vscrolledge[check] == newxtile)\r
+                       {\r
+                               originxglobal = originxglobal&0xff00;\r
+                               break;\r
+                       }\r
+       }\r
+       else if (x<0)\r
+       {\r
+               for (check=0;check<vscrollblocks;check++)\r
+                       if (vscrolledge[check] == newxtile)\r
+                       {\r
+                               originxglobal = (originxglobal&0xff00)+0x100;\r
+                               break;\r
+                       }\r
+       }\r
+\r
+\r
+       if (y>0)\r
+       {\r
+               newytile+=SCREENTILESHIGH;\r
+               for (check=0;check<hscrollblocks;check++)\r
+                       if (hscrolledge[check] == newytile)\r
+                       {\r
+                               originyglobal = originyglobal&0xff00;\r
+                               break;\r
+                       }\r
+       }\r
+       else if (y<0)\r
+       {\r
+               for (check=0;check<hscrollblocks;check++)\r
+                       if (hscrolledge[check] == newytile)\r
+                       {\r
+                               originyglobal = (originyglobal&0xff00)+0x100;\r
+                               break;\r
+                       }\r
+       }\r
+\r
+\r
+       RFL_CalcOriginStuff (originxglobal, originyglobal);\r
+}\r
+\r
+\r
+//===========================================================================\r
+\r
+/*\r
+=====================\r
+=\r
+= RF_SetRefreshHook\r
+=\r
+=====================\r
+*/\r
+\r
+void RF_SetRefreshHook (void (*func) (void) )\r
+{\r
+       refreshvector = func;\r
+}\r
+\r
+\r
+//===========================================================================\r
+\r
+/*\r
+=================\r
+=\r
+= RFL_NewRow\r
+=\r
+= Bring a new row of tiles onto the port, spawning animating tiles\r
+=\r
+=================\r
+*/\r
+\r
+void   RFL_NewRow (int dir)\r
+{\r
+       unsigned count,updatespot,updatestep;\r
+       int             x,y,xstep,ystep;\r
+\r
+       switch (dir)\r
+       {\r
+       case 0:         // top row\r
+               updatespot = 0;\r
+               updatestep = 1;\r
+               x = originxtile;\r
+               y = originytile;\r
+               xstep = 1;\r
+               ystep = 0;\r
+               count = PORTTILESWIDE;\r
+               break;\r
+\r
+       case 1:         // right row\r
+               updatespot = PORTTILESWIDE-1;\r
+               updatestep = UPDATEWIDE;\r
+               x = originxtile + PORTTILESWIDE-1;\r
+               y = originytile;\r
+               xstep = 0;\r
+               ystep = 1;\r
+               count = PORTTILESHIGH;\r
+               break;\r
+\r
+       case 2:         // bottom row\r
+               updatespot = UPDATEWIDE*(PORTTILESHIGH-1);\r
+               updatestep = 1;\r
+               x = originxtile;\r
+               y = originytile + PORTTILESHIGH-1;\r
+               xstep = 1;\r
+               ystep = 0;\r
+               count = PORTTILESWIDE;\r
+               break;\r
+\r
+       case 3:         // left row\r
+               updatespot = 0;\r
+               updatestep = UPDATEWIDE;\r
+               x = originxtile;\r
+               y = originytile;\r
+               xstep = 0;\r
+               ystep = 1;\r
+               count = PORTTILESHIGH;\r
+               break;\r
+       default:\r
+               Quit ("RFL_NewRow: Bad dir!");\r
+       }\r
+\r
+       while (count--)\r
+       {\r
+               RFL_NewTile(updatespot);\r
+               RFL_CheckForAnimTile (x,y);\r
+               updatespot+=updatestep;\r
+               x+=xstep;\r
+               y+=ystep;\r
+       }\r
+}\r
+\r
+//===========================================================================\r
+\r
+/*\r
+=====================\r
+=\r
+= RF_ForceRefresh\r
+=\r
+=====================\r
+*/\r
+\r
+void RF_ForceRefresh (void)\r
+{\r
+       RF_NewPosition (originxglobal,originyglobal);\r
+       RF_Refresh ();\r
+       RF_Refresh ();\r
+}\r
+\r
+//===========================================================================\r
+\r
+/*\r
+=====================\r
+=\r
+= RF_MapToMap\r
+=\r
+= Copies a block of tiles (all three planes) from one point\r
+= in the map to another, accounting for animating tiles\r
+=\r
+=====================\r
+*/\r
+\r
+void RF_MapToMap (unsigned srcx, unsigned srcy,\r
+                                 unsigned destx, unsigned desty,\r
+                                 unsigned width, unsigned height)\r
+{\r
+       int                     x,y;\r
+       unsigned        source,destofs,xspot,yspot;\r
+       unsigned        linedelta,p0,p1,p2,updatespot;\r
+       unsigned        far *source0, far *source1, far *source2;\r
+       unsigned        far *dest0, far *dest1, far *dest2;\r
+       boolean         changed;\r
+\r
+       RFL_RemoveAnimsInBlock (destx,desty,width,height);\r
+\r
+       source = mapbwidthtable[srcy]/2 + srcx;\r
+\r
+       source0 = mapsegs[0]+source;\r
+       source1 = mapsegs[1]+source;\r
+       source2 = mapsegs[2]+source;\r
+\r
+       destofs = mapbwidthtable[desty]/2 + destx;\r
+       destofs -= source;\r
+\r
+       linedelta = mapwidth - width;\r
+\r
+       for (y=0;y<height;y++,source0+=linedelta,source1+=linedelta,source2+=linedelta)\r
+               for (x=0;x<width;x++,source0++,source1++,source2++)\r
+               {\r
+                       p0 = *source0;\r
+                       p1 = *source1;\r
+                       p2 = *source2;\r
+\r
+                       dest0 = source0 + destofs;\r
+                       dest1 = source1 + destofs;\r
+                       dest2 = source2 + destofs;\r
+\r
+//\r
+// only make a new tile if it is different\r
+//\r
+                       if (p0 != *dest0 || p1 != *dest1 || p2 != *dest2)\r
+                       {\r
+                               *dest0 = p0;\r
+                               *dest1 = p1;\r
+                               *dest2 = p2;\r
+                               changed = true;\r
+                       }\r
+                       else\r
+                               changed = false;\r
+\r
+//\r
+// if tile is on the view port\r
+//\r
+                       xspot = destx+x-originxtile;\r
+                       yspot = desty+y-originytile;\r
+                       if (yspot < PORTTILESHIGH && xspot < PORTTILESWIDE)\r
+                       {\r
+                               if (changed)\r
+                               {\r
+                                       updatespot = uwidthtable[yspot]+xspot;\r
+                                       RFL_NewTile(updatespot);\r
+                               }\r
+                               RFL_CheckForAnimTile (destx+x,desty+y);\r
+                       }\r
+               }\r
+}\r
+\r
+//===========================================================================\r
+\r
+\r
+/*\r
+=====================\r
+=\r
+= RF_MemToMap\r
+=\r
+= Copies a string of tiles from main memory to the map,\r
+= accounting for animating tiles\r
+=\r
+=====================\r
+*/\r
+\r
+void RF_MemToMap (unsigned far *source, unsigned plane,\r
+                                 unsigned destx, unsigned desty,\r
+                                 unsigned width, unsigned height)\r
+{\r
+       int                     x,y;\r
+       unsigned        xspot,yspot;\r
+       unsigned        linedelta,updatespot;\r
+       unsigned        far *dest,old,new;\r
+       boolean         changed;\r
+\r
+       RFL_RemoveAnimsInBlock (destx,desty,width,height);\r
+\r
+       dest = mapsegs[plane] + mapbwidthtable[desty]/2 + destx;\r
+\r
+       linedelta = mapwidth - width;\r
+\r
+       for (y=0;y<height;y++,dest+=linedelta)\r
+               for (x=0;x<width;x++)\r
+               {\r
+                       old = *dest;\r
+                       new = *source++;\r
+                       if (old != new)\r
+                       {\r
+                               *dest = new;\r
+                               changed = true;\r
+                       }\r
+                       else\r
+                               changed = false;\r
+\r
+                       dest++;\r
+                       xspot = destx+x-originxtile;\r
+                       yspot = desty+y-originytile;\r
+                       if (yspot < PORTTILESHIGH && xspot < PORTTILESWIDE)\r
+                       {\r
+                               if (changed)\r
+                               {\r
+                                       updatespot = uwidthtable[yspot]+xspot;\r
+                                       RFL_NewTile(updatespot);\r
+                               }\r
+                               RFL_CheckForAnimTile (destx+x,desty+y);\r
+                       }\r
+               }\r
+}\r
+\r
+//===========================================================================\r
+\r
+\r
+/*\r
+=====================\r
+=\r
+= RFL_BoundNewOrigin\r
+=\r
+= Copies a string of tiles from main memory to the map,\r
+= accounting for animating tiles\r
+=\r
+=====================\r
+*/\r
+\r
+void RFL_BoundNewOrigin (unsigned orgx,unsigned orgy)\r
+{\r
+       int     check,edge;\r
+\r
+//\r
+// calculate new origin related globals\r
+//\r
+       if (orgx<originxmin)\r
+         orgx=originxmin;\r
+       else if (orgx>originxmax)\r
+         orgx=originxmax;\r
+\r
+       if (orgy<originymin)\r
+         orgy=originymin;\r
+       else if (orgy>originymax)\r
+         orgy=originymax;\r
+\r
+       originxtile = orgx>>G_T_SHIFT;\r
+       originytile = orgy>>G_T_SHIFT;\r
+\r
+       for (check=0;check<vscrollblocks;check++)\r
+       {\r
+               edge = vscrolledge[check];\r
+               if (edge>=originxtile && edge <=originxtile+10)\r
+               {\r
+                       orgx = (edge+1)*TILEGLOBAL;\r
+                       break;\r
+               }\r
+               if (edge>=originxtile+11 && edge <=originxtile+20)\r
+               {\r
+                       orgx = (edge-20)*TILEGLOBAL;\r
+                       break;\r
+               }\r
+       }\r
+\r
+       for (check=0;check<hscrollblocks;check++)\r
+       {\r
+               edge = hscrolledge[check];\r
+               if (edge>=originytile && edge <=originytile+6)\r
+               {\r
+                       orgy = (edge+1)*TILEGLOBAL;\r
+                       break;\r
+               }\r
+               if (edge>=originytile+7 && edge <=originytile+13)\r
+               {\r
+                       orgy = (edge-13)*TILEGLOBAL;\r
+                       break;\r
+               }\r
+       }\r
+\r
+\r
+       RFL_CalcOriginStuff (orgx,orgy);\r
+}\r
+\r
+\r
+//===========================================================================\r
+\r
+/*\r
+=====================\r
+=\r
+= RF_ClearBlock\r
+=\r
+= Posts erase blocks to clear a certain area of the screen to the master\r
+= screen, to erase text or something draw directly to the screen\r
+=\r
+= Parameters in pixels, but erasure is byte bounded\r
+=\r
+=====================\r
+*/\r
+\r
+void RF_ClearBlock (int        x, int y, int width, int height)\r
+{\r
+       eraseblocktype block;\r
+\r
+#if GRMODE == EGAGR\r
+       block.screenx = x/8+originxscreen;\r
+       block.screeny = y+originyscreen;\r
+       block.width = (width+(x&7)+7)/8;\r
+       block.height = height;\r
+       memcpy (eraselistptr[0]++,&block,sizeof(block));\r
+       memcpy (eraselistptr[1]++,&block,sizeof(block));\r
+#endif\r
+\r
+#if GRMODE == CGAGR\r
+       block.screenx = x/4+originxscreen;\r
+       block.screeny = y+originyscreen;\r
+       block.width = (width+(x&3)+3)/4;\r
+       block.height = height;\r
+       memcpy (eraselistptr[0]++,&block,sizeof(block));\r
+#endif\r
+\r
+}\r
+\r
+//===========================================================================\r
+\r
+/*\r
+=====================\r
+=\r
+= RF_RedrawBlock\r
+=\r
+= Causes a number of tiles to be redrawn to the master screen and updated\r
+=\r
+= Parameters in pixels, but erasure is tile bounded\r
+=\r
+=====================\r
+*/\r
+\r
+void RF_RedrawBlock (int x, int y, int width, int height)\r
+{\r
+       int     xx,yy,xl,xh,yl,yh;\r
+\r
+       xl=(x+panx)/16;\r
+       xh=(x+panx+width+15)/16;\r
+       yl=(y+pany)/16;\r
+       yh=(y+pany+height+15)/16;\r
+       for (yy=yl;yy<=yh;yy++)\r
+               for (xx=xl;xx<=xh;xx++)\r
+                       RFL_NewTile (yy*UPDATEWIDE+xx);\r
+}\r
+\r
+\r
+//===========================================================================\r
+\r
+/*\r
+=====================\r
+=\r
+= RF_CalcTics\r
+=\r
+=====================\r
+*/\r
+\r
+void RF_CalcTics (void)\r
+{\r
+       long    newtime,oldtimecount;\r
+\r
+//\r
+// calculate tics since last refresh for adaptive timing\r
+//\r
+       if (lasttimecount > TimeCount)\r
+               TimeCount = lasttimecount;              // if the game was paused a LONG time\r
+\r
+       if (DemoMode)                                   // demo recording and playback needs\r
+       {                                                               // to be constant\r
+//\r
+// take DEMOTICS or more tics, and modify Timecount to reflect time taken\r
+//\r
+               oldtimecount = lasttimecount;\r
+               while (TimeCount<oldtimecount+DEMOTICS*2)\r
+               ;\r
+               lasttimecount = oldtimecount + DEMOTICS;\r
+               TimeCount = lasttimecount + DEMOTICS;\r
+               tics = DEMOTICS;\r
+       }\r
+       else\r
+       {\r
+//\r
+// non demo, so report actual time\r
+//\r
+               do\r
+               {\r
+                       newtime = TimeCount;\r
+                       tics = newtime-lasttimecount;\r
+               } while (tics<MINTICS);\r
+               lasttimecount = newtime;\r
+\r
+#ifdef PROFILE\r
+                       strcpy (scratch,"\tTics:");\r
+                       itoa (tics,str,10);\r
+                       strcat (scratch,str);\r
+                       strcat (scratch,"\n");\r
+                       write (profilehandle,scratch,strlen(scratch));\r
+#endif\r
+\r
+               if (tics>MAXTICS)\r
+               {\r
+                       TimeCount -= (tics-MAXTICS);\r
+                       tics = MAXTICS;\r
+               }\r
+       }\r
+}\r
+\r
+//===========================================================================\r
+\r
+/*\r
+=====================\r
+=\r
+= RF_FindFreeBuffer\r
+=\r
+= Finds the start of unused, non visable buffer space\r
+=\r
+=====================\r
+*/\r
+\r
+unsigned RF_FindFreeBuffer (void)\r
+{\r
+       unsigned        spot,i,j;\r
+       boolean         ok;\r
+\r
+       for (i=0;i<3;i++)\r
+       {\r
+               spot = screenstart[i]+SCREENSPACE;\r
+               ok = true;\r
+               for (j=0;j<3;j++)\r
+                       if (spot == screenstart[j])\r
+                       {\r
+                               ok = false;\r
+                               break;\r
+                       }\r
+               if (ok)\r
+                       return spot;\r
+       }\r
+\r
+       return 0;       // never get here...\r
+}\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                       EGA specific routines\r
+\r
+=============================================================================\r
+*/\r
+\r
+#if GRMODE == EGAGR\r
+\r
+/*\r
+=====================\r
+=\r
+= RF_NewPosition EGA\r
+=\r
+=====================\r
+*/\r
+\r
+void RF_NewPosition (unsigned x, unsigned y)\r
+{\r
+       int mx,my;\r
+       byte    *page0ptr,*page1ptr;\r
+       unsigned        updatenum;\r
+\r
+       RFL_BoundNewOrigin (x,y);\r
+//\r
+// clear out all animating tiles\r
+//\r
+       RFL_InitAnimList ();\r
+\r
+//\r
+// set up the new update arrays at base position\r
+//\r
+       updatestart[0] = baseupdatestart[0];\r
+       updatestart[1] = baseupdatestart[1];\r
+       updateptr = updatestart[otherpage];\r
+\r
+       page0ptr = updatestart[0]+PORTTILESWIDE;        // used to stick "0"s after rows\r
+       page1ptr = updatestart[1]+PORTTILESWIDE;\r
+\r
+       updatenum = 0;                          // start at first visable tile\r
+\r
+       for (my=0;my<PORTTILESHIGH;my++)\r
+       {\r
+               for (mx=0;mx<PORTTILESWIDE;mx++)\r
+               {\r
+                       RFL_NewTile(updatenum);                 // puts "1"s in both pages\r
+                       RFL_CheckForAnimTile(mx+originxtile,my+originytile);\r
+                       updatenum++;\r
+               }\r
+               updatenum++;\r
+               *page0ptr = *page1ptr = 0; // set a 0 at end of a line of tiles\r
+               page0ptr+=(PORTTILESWIDE+1);\r
+               page1ptr+=(PORTTILESWIDE+1);\r
+       }\r
+       *(word *)(page0ptr-PORTTILESWIDE)\r
+               = *(word *)(page1ptr-PORTTILESWIDE) = UPDATETERMINATE;\r
+}\r
+\r
+//===========================================================================\r
+\r
+\r
+/*\r
+=====================\r
+=\r
+= RF_Scroll  EGA\r
+=\r
+= Move the origin x/y global coordinates, readjust the screen panning, and\r
+= scroll if needed.  If the scroll distance is greater than one tile, the\r
+= entire screen will be redrawn (this could be generalized, but scrolling\r
+= more than one tile per refresh is a bad idea!).\r
+=\r
+=====================\r
+*/\r
+\r
+void RF_Scroll (int x, int y)\r
+{\r
+       long            neworgx,neworgy;\r
+       int                     i,deltax,deltay,absdx,absdy;\r
+       int                     oldxt,oldyt,move,yy;\r
+       unsigned        updatespot;\r
+       byte            *update0,*update1;\r
+       unsigned        oldpanx,oldpanadjust,oldscreen,newscreen,screencopy;\r
+       int                     screenmove;\r
+\r
+       oldxt = originxtile;\r
+       oldyt = originytile;\r
+       oldpanadjust = panadjust;\r
+       oldpanx = panx;\r
+\r
+       RFL_BoundScroll (x,y);\r
+\r
+       deltax = originxtile - oldxt;\r
+       absdx = abs(deltax);\r
+       deltay = originytile - oldyt;\r
+       absdy = abs(deltay);\r
+\r
+       if (absdx>1 || absdy>1)\r
+       {\r
+       //\r
+       // scrolled more than one tile, so start from scratch\r
+       //\r
+               RF_NewPosition(originxglobal,originyglobal);\r
+               return;\r
+       }\r
+\r
+       if (!absdx && !absdy)\r
+               return;                                 // the screen has not scrolled an entire tile\r
+\r
+\r
+//\r
+// adjust screens and handle SVGA crippled compatability mode\r
+//\r
+       screenmove = deltay*16*SCREENWIDTH + deltax*TILEWIDTH;\r
+       for (i=0;i<3;i++)\r
+       {\r
+               screenstart[i]+= screenmove;\r
+               if (compatability && screenstart[i] > (0x10000l-SCREENSPACE) )\r
+               {\r
+                       //\r
+                       // move the screen to the opposite end of the buffer\r
+                       //\r
+                       screencopy = screenmove>0 ? FREEEGAMEM : -FREEEGAMEM;\r
+                       oldscreen = screenstart[i] - screenmove;\r
+                       newscreen = oldscreen + screencopy;\r
+                       screenstart[i] = newscreen + screenmove;\r
+                       VW_ScreenToScreen (oldscreen,newscreen,\r
+                               PORTTILESWIDE*2,PORTTILESHIGH*16);\r
+\r
+                       if (i==screenpage)\r
+                               VW_SetScreen(newscreen+oldpanadjust,oldpanx & xpanmask);\r
+               }\r
+       }\r
+       bufferofs = screenstart[otherpage];\r
+       displayofs = screenstart[screenpage];\r
+       masterofs = screenstart[2];\r
+\r
+\r
+//\r
+// float the update regions\r
+//\r
+       move = deltax;\r
+       if (deltay==1)\r
+         move += UPDATEWIDE;\r
+       else if (deltay==-1)\r
+         move -= UPDATEWIDE;\r
+\r
+       updatestart[0]+=move;\r
+       updatestart[1]+=move;\r
+\r
+//\r
+// draw the new tiles just scrolled on to the master screen, and\r
+// mark them as needing to be copied to each screen next refreshes\r
+// Make sure a zero is at the end of each row in update\r
+//\r
+\r
+       if (deltax)\r
+       {\r
+               if (deltax==1)\r
+               {\r
+                       RFL_NewRow (1);                 // new right row\r
+                       RFL_RemoveAnimsOnX (originxtile-1);\r
+               }\r
+               else\r
+               {\r
+                       RFL_NewRow (3);                 // new left row\r
+                       RFL_RemoveAnimsOnX (originxtile+PORTTILESWIDE);\r
+               }\r
+\r
+               update0 = updatestart[0]+PORTTILESWIDE;\r
+               update1 = updatestart[1]+PORTTILESWIDE;\r
+               for     (yy=0;yy<PORTTILESHIGH;yy++)\r
+               {\r
+                       *update0 = *update1 = 0;        // drop a 0 at end of each row\r
+                       update0+=UPDATEWIDE;\r
+                       update1+=UPDATEWIDE;\r
+               }\r
+       }\r
+\r
+//----------------\r
+\r
+       if (deltay)\r
+       {\r
+               if (deltay==1)\r
+               {\r
+                       updatespot = UPDATEWIDE*(PORTTILESHIGH-1);\r
+                       RFL_NewRow (2);                 // new bottom row\r
+                       RFL_RemoveAnimsOnY (originytile-1);\r
+               }\r
+               else\r
+               {\r
+                       updatespot = 0;\r
+                       RFL_NewRow (0);                 // new top row\r
+                       RFL_RemoveAnimsOnY (originytile+PORTTILESHIGH);\r
+               }\r
+\r
+               *(updatestart[0]+updatespot+PORTTILESWIDE) =\r
+                       *(updatestart[1]+updatespot+PORTTILESWIDE) = 0;\r
+       }\r
+\r
+//----------------\r
+\r
+       //\r
+       // place a new terminator\r
+       //\r
+       update0 = updatestart[0]+UPDATEWIDE*PORTTILESHIGH-1;\r
+       update1 = updatestart[1]+UPDATEWIDE*PORTTILESHIGH-1;\r
+       *update0++ = *update1++ = 0;\r
+       *(unsigned *)update0 = *(unsigned *)update1 = UPDATETERMINATE;\r
+}\r
+\r
+//===========================================================================\r
+\r
+/*\r
+=====================\r
+=\r
+= RF_PlaceSprite   EGA\r
+=\r
+=====================\r
+*/\r
+\r
+void RF_PlaceSprite (void **user,unsigned globalx,unsigned globaly,\r
+       unsigned spritenumber, drawtype draw, int priority)\r
+{\r
+       spritelisttype  register *sprite,*next;\r
+       spritetabletype far *spr;\r
+       spritetype _seg *block;\r
+       unsigned        shift,pixx;\r
+       char            str[80],str2[10];\r
+\r
+       if (!spritenumber || spritenumber == (unsigned)-1)\r
+       {\r
+               RF_RemoveSprite (user);\r
+               return;\r
+       }\r
+\r
+       sprite = (spritelisttype *)*user;\r
+\r
+       if      (sprite)\r
+       {\r
+       // sprite allready exists in the list, so we can use it's block\r
+\r
+       //\r
+       // post an erase block to both pages by copying screenx,screeny,width,height\r
+       // both pages may not need to be erased if the sprite just changed last frame\r
+       //\r
+               if (sprite->updatecount<2)\r
+               {\r
+                       if (!sprite->updatecount)\r
+                               memcpy (eraselistptr[otherpage]++,sprite,sizeof(eraseblocktype));\r
+                       memcpy (eraselistptr[screenpage]++,sprite,sizeof(eraseblocktype));\r
+               }\r
+\r
+               if (priority != sprite->priority)\r
+               {\r
+               // sprite mvoed to another priority, so unlink the old one and\r
+               // relink it in the new priority\r
+\r
+                       next = sprite->nextsprite;                      // cut old links\r
+                       if (next)\r
+                               next->prevptr = sprite->prevptr;\r
+                       *sprite->prevptr = next;\r
+                       goto linknewspot;\r
+               }\r
+       }\r
+       else\r
+       {\r
+       // this is a brand new sprite, so allocate a block from the array\r
+\r
+               if (!spritefreeptr)\r
+                       Quit ("RF_PlaceSprite: No free spots in spritearray!");\r
+\r
+               sprite = spritefreeptr;\r
+               spritefreeptr = spritefreeptr->nextsprite;\r
+\r
+linknewspot:\r
+               next = prioritystart[priority];         // stick it in new spot\r
+               if (next)\r
+                       next->prevptr = &sprite->nextsprite;\r
+               sprite->nextsprite = next;\r
+               prioritystart[priority] = sprite;\r
+               sprite->prevptr = &prioritystart[priority];\r
+       }\r
+\r
+//\r
+// write the new info to the sprite\r
+//\r
+       spr = &spritetable[spritenumber-STARTSPRITES];\r
+       block = (spritetype _seg *)grsegs[spritenumber];\r
+\r
+       if (!block)\r
+       {\r
+               strcpy (str,"RF_PlaceSprite: Placed an uncached sprite:");\r
+               itoa (spritenumber,str2,10);\r
+               strcat (str,str2);\r
+               Quit (str);\r
+       }\r
+\r
+       globaly+=spr->orgy;\r
+       globalx+=spr->orgx;\r
+\r
+       pixx = globalx >> G_SY_SHIFT;\r
+       if (nopan)\r
+               shift = 0;\r
+       else\r
+               shift = (pixx&7)/2;\r
+\r
+       sprite->screenx = pixx >> (G_EGASX_SHIFT-G_SY_SHIFT);\r
+       sprite->screeny = globaly >> G_SY_SHIFT;\r
+       sprite->width = block->width[shift];\r
+       sprite->height = spr->height;\r
+       sprite->grseg = spritenumber;\r
+       sprite->sourceofs = block->sourceoffset[shift];\r
+       sprite->planesize = block->planesize[shift];\r
+       sprite->draw = draw;\r
+       sprite->priority = priority;\r
+       sprite->tilex = sprite->screenx >> SX_T_SHIFT;\r
+       sprite->tiley = sprite->screeny >> SY_T_SHIFT;\r
+       sprite->tilewide = ( (sprite->screenx + sprite->width -1) >> SX_T_SHIFT )\r
+               - sprite->tilex + 1;\r
+       sprite->tilehigh = ( (sprite->screeny + sprite->height -1) >> SY_T_SHIFT )\r
+               - sprite->tiley + 1;\r
+\r
+       sprite->updatecount = 2;                // draw on next two refreshes\r
+\r
+// save the sprite pointer off in the user's pointer so it can be moved\r
+// again later\r
+\r
+       *user = sprite;\r
+}\r
+\r
+//===========================================================================\r
+\r
+/*\r
+=====================\r
+=\r
+= RF_RemoveSprite  EGA\r
+=\r
+=====================\r
+*/\r
+\r
+void RF_RemoveSprite (void **user)\r
+{\r
+       spritelisttype  *sprite,*next;\r
+\r
+       sprite = (spritelisttype *)*user;\r
+       if (!sprite)\r
+               return;\r
+\r
+//\r
+// post an erase block to both pages by copying screenx,screeny,width,height\r
+// both pages may not need to be erased if the sprite just changed last frame\r
+//\r
+       if (sprite->updatecount<2)\r
+       {\r
+               if (!sprite->updatecount)\r
+                       memcpy (eraselistptr[otherpage]++,sprite,sizeof(eraseblocktype));\r
+               memcpy (eraselistptr[screenpage]++,sprite,sizeof(eraseblocktype));\r
+       }\r
+\r
+//\r
+// unlink the sprite node\r
+//\r
+       next = sprite->nextsprite;\r
+       if (next)                                               // if (!next), sprite is last in chain\r
+               next->prevptr = sprite->prevptr;\r
+       *sprite->prevptr = next;\r
+\r
+//\r
+// add it back to the free list\r
+//\r
+       sprite->nextsprite = spritefreeptr;\r
+       spritefreeptr = sprite;\r
+\r
+//\r
+// null the users pointer, so next time that actor gets placed, it will\r
+// allocate a new block\r
+//\r
+\r
+       *user = 0;\r
+}\r
+\r
+\r
+//===========================================================================\r
+\r
+\r
+/*\r
+====================\r
+=\r
+= RFL_EraseBlocks  EGA\r
+=\r
+= Write mode 1 should be set\r
+=\r
+====================\r
+*/\r
+\r
+void RFL_EraseBlocks (void)\r
+{\r
+       eraseblocktype  *block,*done;\r
+       int                     screenxh,screenyh;\r
+       unsigned        pos,xtl,ytl,xth,yth,x,y;\r
+       byte            *updatespot;\r
+       unsigned        updatedelta;\r
+       unsigned        erasecount;\r
+\r
+#ifdef PROFILE\r
+       erasecount = 0;\r
+#endif\r
+\r
+       block = otherpage ? &eraselist[1][0] : &eraselist[0][0];\r
+\r
+       done = eraselistptr[otherpage];\r
+\r
+       while (block != done)\r
+       {\r
+\r
+       //\r
+       // clip the block to the current screen view\r
+       //\r
+               block->screenx -= originxscreen;\r
+               block->screeny -= originyscreen;\r
+\r
+               if (block->screenx < 0)\r
+               {\r
+                       block->width += block->screenx;\r
+                       if (block->width<1)\r
+                               goto next;\r
+                       block->screenx = 0;\r
+               }\r
+\r
+               if (block->screeny < 0)\r
+               {\r
+                       block->height += block->screeny;\r
+                       if (block->height<1)\r
+                               goto next;\r
+                       block->screeny = 0;\r
+               }\r
+\r
+               screenxh = block->screenx + block->width;\r
+               screenyh = block->screeny + block->height;\r
+\r
+               if (screenxh > EGAPORTSCREENWIDE)\r
+               {\r
+                       block->width = EGAPORTSCREENWIDE-block->screenx;\r
+                       screenxh = block->screenx + block->width;\r
+               }\r
+\r
+               if (screenyh > PORTSCREENHIGH)\r
+               {\r
+                       block->height = PORTSCREENHIGH-block->screeny;\r
+                       screenyh = block->screeny + block->height;\r
+               }\r
+\r
+               if (block->width<1 || block->height<1)\r
+                       goto next;\r
+\r
+       //\r
+       // erase the block by copying from the master screen\r
+       //\r
+               pos = ylookup[block->screeny]+block->screenx;\r
+               VW_ScreenToScreen (masterofs+pos,bufferofs+pos,\r
+                       block->width,block->height);\r
+\r
+       //\r
+       // put 2s in update where the block was, to force sprites to update\r
+       //\r
+               xtl = block->screenx >> SX_T_SHIFT;\r
+               xth = (block->screenx+block->width-1) >> SX_T_SHIFT;\r
+               ytl = block->screeny >> SY_T_SHIFT;\r
+               yth = (block->screeny+block->height-1) >> SY_T_SHIFT;\r
+\r
+               updatespot = updateptr + uwidthtable[ytl] + xtl;\r
+               updatedelta = UPDATEWIDE - (xth-xtl+1);\r
+\r
+               for (y=ytl;y<=yth;y++)\r
+               {\r
+                       for (x=xtl;x<=xth;x++)\r
+                               *updatespot++ = 2;\r
+                       updatespot += updatedelta;              // down to next line\r
+               }\r
+#ifdef PROFILE\r
+               erasecount++;\r
+#endif\r
+\r
+next:\r
+               block++;\r
+       }\r
+       eraselistptr[otherpage] = otherpage ? &eraselist[1][0] : &eraselist[0][0];\r
+#ifdef PROFILE\r
+       strcpy (scratch,"\tErase:");\r
+       itoa (erasecount,str,10);\r
+       strcat (scratch,str);\r
+       write (profilehandle,scratch,strlen(scratch));\r
+#endif\r
+\r
+}\r
+\r
+\r
+/*\r
+====================\r
+=\r
+= RFL_UpdateSprites EGA\r
+=\r
+= NOTE: Implement vertical clipping!\r
+=\r
+====================\r
+*/\r
+\r
+void RFL_UpdateSprites (void)\r
+{\r
+       spritelisttype  *sprite;\r
+       int     portx,porty,x,y,xtl,xth,ytl,yth;\r
+       int     priority;\r
+       unsigned dest;\r
+       byte            *updatespot,*baseupdatespot;\r
+       unsigned        updatedelta;\r
+       unsigned        updatecount;\r
+       unsigned        height,sourceofs;\r
+\r
+#ifdef PROFILE\r
+       updatecount = 0;\r
+#endif\r
+\r
+       for (priority=0;priority<PRIORITIES;priority++)\r
+       {\r
+               if (priority==MASKEDTILEPRIORITY)\r
+                       RFL_MaskForegroundTiles ();\r
+\r
+               for (sprite = prioritystart[priority]; sprite ;\r
+                       sprite = (spritelisttype *)sprite->nextsprite)\r
+               {\r
+               //\r
+               // see if the sprite has any visable area in the port\r
+               //\r
+\r
+                       portx = sprite->screenx - originxscreen;\r
+                       porty = sprite->screeny - originyscreen;\r
+                       xtl = portx >> SX_T_SHIFT;\r
+                       xth = (portx + sprite->width-1) >> SX_T_SHIFT;\r
+                       ytl = porty >> SY_T_SHIFT;\r
+                       yth = (porty + sprite->height-1) >> SY_T_SHIFT;\r
+\r
+                       if (xtl<0)\r
+                         xtl = 0;\r
+                       if (xth>=PORTTILESWIDE)\r
+                         xth = PORTTILESWIDE-1;\r
+                       if (ytl<0)\r
+                         ytl = 0;\r
+                       if (yth>=PORTTILESHIGH)\r
+                         yth = PORTTILESHIGH-1;\r
+\r
+                       if (xtl>xth || ytl>yth)\r
+                               continue;\r
+\r
+               //\r
+               // see if it's visable area covers any non 0 update tiles\r
+               //\r
+                       updatespot = baseupdatespot = updateptr + uwidthtable[ytl] + xtl;\r
+                       updatedelta = UPDATEWIDE - (xth-xtl+1);\r
+\r
+                       if (sprite->updatecount)\r
+                       {\r
+                               sprite->updatecount--;                  // the sprite was just placed,\r
+                               goto redraw;                                    // so draw it for sure\r
+                       }\r
+\r
+                       for (y=ytl;y<=yth;y++)\r
+                       {\r
+                               for (x=xtl;x<=xth;x++)\r
+                                       if (*updatespot++)\r
+                                               goto redraw;\r
+                               updatespot += updatedelta;              // down to next line\r
+                       }\r
+                       continue;                                                       // no need to update\r
+\r
+redraw:\r
+               //\r
+               // set the tiles it covers to 3, because those tiles are being\r
+               // updated\r
+               //\r
+                       updatespot = baseupdatespot;\r
+                       for (y=ytl;y<=yth;y++)\r
+                       {\r
+                               for (x=xtl;x<=xth;x++)\r
+                                       *updatespot++ = 3;\r
+                               updatespot += updatedelta;              // down to next line\r
+                       }\r
+               //\r
+               // draw it!\r
+               //\r
+                       height = sprite->height;\r
+                       sourceofs = sprite->sourceofs;\r
+                       if (porty<0)\r
+                       {\r
+                               height += porty;                                        // clip top off\r
+                               sourceofs -= porty*sprite->width;\r
+                               porty = 0;\r
+                       }\r
+                       else if (porty+height>PORTSCREENHIGH)\r
+                       {\r
+                               height = PORTSCREENHIGH - porty;    // clip bottom off\r
+                       }\r
+\r
+                       dest = bufferofs + ylookup[porty] + portx;\r
+\r
+                       switch (sprite->draw)\r
+                       {\r
+                       case spritedraw:\r
+                               VW_MaskBlock(grsegs[sprite->grseg], sourceofs,\r
+                                       dest,sprite->width,height,sprite->planesize);\r
+                               break;\r
+\r
+                       case maskdraw:\r
+                               VW_InverseMask(grsegs[sprite->grseg], sourceofs,\r
+                                       dest,sprite->width,height);\r
+                               break;\r
+\r
+                       }\r
+#ifdef PROFILE\r
+                       updatecount++;\r
+#endif\r
+\r
+\r
+               }\r
+       }\r
+#ifdef PROFILE\r
+       strcpy (scratch,"\tSprites:");\r
+       itoa (updatecount,str,10);\r
+       strcat (scratch,str);\r
+       write (profilehandle,scratch,strlen(scratch));\r
+#endif\r
+\r
+}\r
+\r
+\r
+/*\r
+=====================\r
+=\r
+= RF_Refresh   EGA\r
+=\r
+= All routines will draw at the port at bufferofs, possibly copying from\r
+= the port at masterofs.  The EGA version then page flips, while the\r
+= CGA version updates the screen from the buffer port.\r
+=\r
+= Screenpage is the currently displayed page, not the one being drawn\r
+= Otherpage is the page to be worked with now\r
+=\r
+=====================\r
+*/\r
+\r
+void RF_Refresh (void)\r
+{\r
+       byte    *newupdate;\r
+\r
+       updateptr = updatestart[otherpage];\r
+\r
+       RFL_AnimateTiles ();            // DEBUG\r
+\r
+//\r
+// update newly scrolled on tiles and animated tiles from the master screen\r
+//\r
+       EGAWRITEMODE(1);\r
+       EGAMAPMASK(15);\r
+       RFL_UpdateTiles ();\r
+       RFL_EraseBlocks ();\r
+\r
+//\r
+// Update is all 0 except where sprites have changed or new area has\r
+// been scrolled on.  Go through all sprites and update the ones that cover\r
+// a non 0 update tile\r
+//\r
+       EGAWRITEMODE(0);\r
+       RFL_UpdateSprites ();\r
+\r
+//\r
+// if the main program has a refresh hook set, call their function before\r
+// displaying the new page\r
+//\r
+       if (refreshvector)\r
+               refreshvector();\r
+\r
+//\r
+// display the changed screen\r
+//\r
+       VW_SetScreen(bufferofs+panadjust,panx & xpanmask);\r
+\r
+//\r
+// prepare for next refresh\r
+//\r
+// Set the update array to the middle position and clear it out to all "0"s\r
+// with an UPDATETERMINATE at the end\r
+//\r
+       updatestart[otherpage] = newupdate = baseupdatestart[otherpage];\r
+asm    mov     ax,ds\r
+asm    mov     es,ax\r
+asm    xor     ax,ax\r
+asm    mov     cx,(UPDATESCREENSIZE-2)/2\r
+asm    mov     di,[newupdate]\r
+asm    rep     stosw\r
+asm    mov     [WORD PTR es:di],UPDATETERMINATE\r
+\r
+       screenpage ^= 1;\r
+       otherpage ^= 1;\r
+       bufferofs = screenstart[otherpage];\r
+       displayofs = screenstart[screenpage];\r
+\r
+//\r
+// calculate tics since last refresh for adaptive timing\r
+//\r
+       RF_CalcTics ();\r
+}\r
+\r
+#endif         // GRMODE == EGAGR\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                       CGA specific routines\r
+\r
+=============================================================================\r
+*/\r
+\r
+#if GRMODE == CGAGR\r
+\r
+\r
+/*\r
+=====================\r
+=\r
+= RF_NewPosition   CGA\r
+=\r
+=====================\r
+*/\r
+\r
+void RF_NewPosition (unsigned x, unsigned y)\r
+{\r
+       int mx,my;\r
+       byte    *spotptr;\r
+       unsigned        updatenum;\r
+\r
+       RFL_BoundNewOrigin (x,y);\r
+\r
+//\r
+// clear out all animating tiles\r
+//\r
+       RFL_InitAnimList ();\r
+\r
+//\r
+// set up the new update arrays at base position\r
+//\r
+       updateptr = baseupdateptr;\r
+\r
+       spotptr = updateptr + PORTTILESWIDE;    // used to stick "0"s after rows\r
+\r
+       updatenum = 0;                          // start at first visable tile\r
+\r
+       for (my=0;my<PORTTILESHIGH;my++)\r
+       {\r
+               for (mx=0;mx<PORTTILESWIDE;mx++)\r
+               {\r
+                       RFL_NewTile(updatenum);                 // puts "1"s in both pages\r
+                       RFL_CheckForAnimTile(mx+originxtile,my+originytile);\r
+                       updatenum++;\r
+               }\r
+               updatenum++;\r
+               *spotptr = 0; // set a 0 at end of a line of tiles\r
+               spotptr +=(PORTTILESWIDE+1);\r
+       }\r
+       *(word *)(spotptr-PORTTILESWIDE) = UPDATETERMINATE;\r
+}\r
+\r
+\r
+/*\r
+=====================\r
+=\r
+= RF_Scroll       CGA\r
+=\r
+= Move the origin x/y global coordinates, readjust the screen panning, and\r
+= scroll if needed.  If the scroll distance is greater than one tile, the\r
+= entire screen will be redrawn (this could be generalized, but scrolling\r
+= more than one tile per refresh is a bad idea!).\r
+=\r
+=====================\r
+*/\r
+\r
+void RF_Scroll (int x, int y)\r
+{\r
+       long            neworgx,neworgy;\r
+       int                     i,deltax,deltay,absdx,absdy;\r
+       int                     oldxt,oldyt,move,yy;\r
+       unsigned        updatespot;\r
+       byte            *spotptr;\r
+       unsigned        oldoriginmap,oldscreen,newscreen,screencopy;\r
+       int                     screenmove;\r
+\r
+       oldxt = originxtile;\r
+       oldyt = originytile;\r
+\r
+       RFL_BoundScroll (x,y);\r
+\r
+       deltax = originxtile - oldxt;\r
+       absdx = abs(deltax);\r
+       deltay = originytile - oldyt;\r
+       absdy = abs(deltay);\r
+\r
+       if (absdx>1 || absdy>1)\r
+       {\r
+       //\r
+       // scrolled more than one tile, so start from scratch\r
+       //\r
+               RF_NewPosition(originxglobal,originyglobal);\r
+               return;\r
+       }\r
+\r
+       if (!absdx && !absdy)\r
+               return;                                 // the screen has not scrolled an entire tile\r
+\r
+\r
+//\r
+// float screens\r
+//\r
+       screenmove = deltay*16*SCREENWIDTH + deltax*TILEWIDTH;\r
+       bufferofs += screenmove;\r
+       masterofs += screenmove;\r
+\r
+\r
+//\r
+// float the update regions\r
+//\r
+       move = deltax;\r
+       if (deltay==1)\r
+         move += UPDATEWIDE;\r
+       else if (deltay==-1)\r
+         move -= UPDATEWIDE;\r
+\r
+       updateptr+=move;\r
+\r
+//\r
+// draw the new tiles just scrolled on to the master screen, and\r
+// mark them as needing to be copied to each screen next refreshes\r
+// Make sure a zero is at the end of each row in update\r
+//\r
+\r
+       if (deltax)\r
+       {\r
+               if (deltax==1)\r
+               {\r
+                       RFL_NewRow (1);                 // new right row\r
+                       RFL_RemoveAnimsOnX (originxtile-1);\r
+               }\r
+               else\r
+               {\r
+                       RFL_NewRow (3);                 // new left row\r
+                       RFL_RemoveAnimsOnX (originxtile+PORTTILESWIDE);\r
+               }\r
+\r
+               spotptr = updateptr+PORTTILESWIDE;\r
+               for     (yy=0;yy<PORTTILESHIGH;yy++)\r
+               {\r
+                       *spotptr = 0;           // drop a 0 at end of each row\r
+                       spotptr+=UPDATEWIDE;\r
+               }\r
+       }\r
+\r
+//----------------\r
+\r
+       if (deltay)\r
+       {\r
+               if (deltay==1)\r
+               {\r
+                       RFL_NewRow (2);                 // new bottom row\r
+                       *(updateptr+UPDATEWIDE*(PORTTILESHIGH-1)+PORTTILESWIDE) = 0;\r
+                       RFL_RemoveAnimsOnY (originytile-1);\r
+               }\r
+               else\r
+               {\r
+                       RFL_NewRow (0);                 // new top row\r
+                       *(updateptr+PORTTILESWIDE) = 0;\r
+                       RFL_RemoveAnimsOnY (originytile+PORTTILESHIGH);\r
+               }\r
+       }\r
+\r
+//----------------\r
+\r
+       //\r
+       // place a new terminator\r
+       //\r
+       spotptr = updateptr+UPDATEWIDE*PORTTILESHIGH-1;\r
+       *spotptr++ = 0;\r
+       *(unsigned *)spotptr = UPDATETERMINATE;\r
+}\r
+\r
+/*\r
+=====================\r
+=\r
+= RF_PlaceSprite  CGA\r
+=\r
+=====================\r
+*/\r
+\r
+void RF_PlaceSprite (void **user,unsigned globalx,unsigned globaly,\r
+       unsigned spritenumber, drawtype draw, int priority)\r
+{\r
+       spritelisttype  register *sprite,*next;\r
+       spritetabletype far *spr;\r
+       spritetype _seg *block;\r
+       unsigned        shift,pixx;\r
+       char            str[80],str2[10];\r
+\r
+       if (!spritenumber || spritenumber == (unsigned)-1)\r
+       {\r
+               RF_RemoveSprite (user);\r
+               return;\r
+       }\r
+\r
+       sprite = (spritelisttype *)*user;\r
+\r
+       if      (sprite)\r
+       {\r
+       // sprite allready exists in the list, so we can use it's block\r
+\r
+       //\r
+       // post an erase block to erase the old position by copying\r
+       // screenx,screeny,width,height\r
+       //\r
+               if (!sprite->updatecount)               // may not have been drawn at all yet\r
+                       memcpy (eraselistptr[0]++,sprite,sizeof(eraseblocktype));\r
+\r
+               if (priority != sprite->priority)\r
+               {\r
+               // sprite moved to another priority, so unlink the old one and\r
+               // relink it in the new priority\r
+\r
+                       next = sprite->nextsprite;                      // cut old links\r
+                       if (next)\r
+                               next->prevptr = sprite->prevptr;\r
+                       *sprite->prevptr = next;\r
+                       goto linknewspot;\r
+               }\r
+       }\r
+       else\r
+       {\r
+       // this is a brand new sprite, so allocate a block from the array\r
+\r
+               if (!spritefreeptr)\r
+                       Quit ("RF_PlaceSprite: No free spots in spritearray!");\r
+\r
+               sprite = spritefreeptr;\r
+               spritefreeptr = spritefreeptr->nextsprite;\r
+\r
+linknewspot:\r
+               next = prioritystart[priority];         // stick it in new spot\r
+               if (next)\r
+                       next->prevptr = &sprite->nextsprite;\r
+               sprite->nextsprite = next;\r
+               prioritystart[priority] = sprite;\r
+               sprite->prevptr = &prioritystart[priority];\r
+       }\r
+\r
+//\r
+// write the new info to the sprite\r
+//\r
+       spr = &spritetable[spritenumber-STARTSPRITES];\r
+       block = (spritetype _seg *)grsegs[spritenumber];\r
+\r
+       if (!block)\r
+       {\r
+               strcpy (str,"RF_PlaceSprite: Placed an uncached sprite!");\r
+               itoa (spritenumber,str2,10);\r
+               strcat (str,str2);\r
+               Quit (str);\r
+       }\r
+\r
+\r
+       globaly+=spr->orgy;\r
+       globalx+=spr->orgx;\r
+\r
+       sprite->screenx = globalx >> G_CGASX_SHIFT;\r
+       sprite->screeny = globaly >> G_SY_SHIFT;\r
+       sprite->width = block->width[0];\r
+       sprite->height = spr->height;\r
+       sprite->grseg = spritenumber;\r
+       sprite->sourceofs = block->sourceoffset[0];\r
+       sprite->planesize = block->planesize[0];\r
+       sprite->draw = draw;\r
+       sprite->priority = priority;\r
+       sprite->tilex = sprite->screenx >> SX_T_SHIFT;\r
+       sprite->tiley = sprite->screeny >> SY_T_SHIFT;\r
+       sprite->tilewide = ( (sprite->screenx + sprite->width -1) >> SX_T_SHIFT )\r
+               - sprite->tilex + 1;\r
+       sprite->tilehigh = ( (sprite->screeny + sprite->height -1) >> SY_T_SHIFT )\r
+               - sprite->tiley + 1;\r
+\r
+       sprite->updatecount = 1;                // draw on next refresh\r
+\r
+// save the sprite pointer off in the user's pointer so it can be moved\r
+// again later\r
+\r
+       *user = sprite;\r
+}\r
+\r
+//===========================================================================\r
+\r
+/*\r
+=====================\r
+=\r
+= RF_RemoveSprite CGA\r
+=\r
+=====================\r
+*/\r
+\r
+void RF_RemoveSprite (void **user)\r
+{\r
+       spritelisttype  *sprite,*next;\r
+\r
+       sprite = (spritelisttype *)*user;\r
+       if (!sprite)\r
+               return;\r
+\r
+//\r
+// post an erase block to erase the old position by copying\r
+// screenx,screeny,width,height\r
+//\r
+       if (!sprite->updatecount)\r
+       {\r
+               memcpy (eraselistptr[0]++,sprite,sizeof(eraseblocktype));\r
+       }\r
+\r
+//\r
+// unlink the sprite node\r
+//\r
+       next = sprite->nextsprite;\r
+       if (next)                                               // if (!next), sprite is last in chain\r
+               next->prevptr = sprite->prevptr;\r
+       *sprite->prevptr = next;\r
+\r
+//\r
+// add it back to the free list\r
+//\r
+       sprite->nextsprite = spritefreeptr;\r
+       spritefreeptr = sprite;\r
+\r
+//\r
+// null the users pointer, so next time that actor gets placed, it will\r
+// allocate a new block\r
+//\r
+\r
+       *user = 0;\r
+}\r
+\r
+\r
+/*\r
+====================\r
+=\r
+= RFL_EraseBlocks CGA\r
+=\r
+= Write mode 1 should be set\r
+=\r
+====================\r
+*/\r
+\r
+void RFL_EraseBlocks (void)\r
+{\r
+       eraseblocktype  *block,*done;\r
+       int                     screenxh,screenyh;\r
+       unsigned        pos,xtl,ytl,xth,yth,x,y;\r
+       byte            *updatespot;\r
+       unsigned        updatedelta;\r
+\r
+       block = &eraselist[0][0];\r
+\r
+       done = eraselistptr[0];\r
+\r
+       while (block != done)\r
+       {\r
+\r
+       //\r
+       // clip the block to the current screen view\r
+       //\r
+               block->screenx -= originxscreen;\r
+               block->screeny -= originyscreen;\r
+\r
+               if (block->screenx < 0)\r
+               {\r
+                       block->width += block->screenx;\r
+                       if (block->width<1)\r
+                               goto next;\r
+                       block->screenx = 0;\r
+               }\r
+\r
+               if (block->screeny < 0)\r
+               {\r
+                       block->height += block->screeny;\r
+                       if (block->height<1)\r
+                               goto next;\r
+                       block->screeny = 0;\r
+               }\r
+\r
+               screenxh = block->screenx + block->width;\r
+               screenyh = block->screeny + block->height;\r
+\r
+               if (screenxh > CGAPORTSCREENWIDE)\r
+               {\r
+                       block->width = CGAPORTSCREENWIDE-block->screenx;\r
+                       screenxh = block->screenx + block->width;\r
+               }\r
+\r
+               if (screenyh > PORTSCREENHIGH)\r
+               {\r
+                       block->height = PORTSCREENHIGH-block->screeny;\r
+                       screenyh = block->screeny + block->height;\r
+               }\r
+\r
+               if (block->width<1 || block->height<1)\r
+                       goto next;\r
+\r
+       //\r
+       // erase the block by copying from the master screen\r
+       //\r
+               pos = ylookup[block->screeny]+block->screenx;\r
+               block->width = (block->width + (pos&1) + 1)& ~1;\r
+               pos &= ~1;                              // make sure a word copy gets used\r
+               VW_ScreenToScreen (masterofs+pos,bufferofs+pos,\r
+                       block->width,block->height);\r
+\r
+       //\r
+       // put 2s in update where the block was, to force sprites to update\r
+       //\r
+               xtl = block->screenx >> SX_T_SHIFT;\r
+               xth = (block->screenx+block->width-1) >> SX_T_SHIFT;\r
+               ytl = block->screeny >> SY_T_SHIFT;\r
+               yth = (block->screeny+block->height-1) >> SY_T_SHIFT;\r
+\r
+               updatespot = updateptr + uwidthtable[ytl] + xtl;\r
+               updatedelta = UPDATEWIDE - (xth-xtl+1);\r
+\r
+               for (y=ytl;y<=yth;y++)\r
+               {\r
+                       for (x=xtl;x<=xth;x++)\r
+                               *updatespot++ = 2;\r
+                       updatespot += updatedelta;              // down to next line\r
+               }\r
+\r
+next:\r
+               block++;\r
+       }\r
+       eraselistptr[0] = &eraselist[0][0];\r
+}\r
+\r
+\r
+/*\r
+====================\r
+=\r
+= RFL_UpdateSprites      CGA\r
+=\r
+= NOTE: Implement vertical clipping!\r
+=\r
+====================\r
+*/\r
+\r
+void RFL_UpdateSprites (void)\r
+{\r
+       spritelisttype  *sprite;\r
+       int     portx,porty,x,y,xtl,xth,ytl,yth;\r
+       int     priority;\r
+       unsigned dest;\r
+       byte            *updatespot,*baseupdatespot;\r
+       unsigned        updatedelta;\r
+\r
+       unsigned        updatecount;\r
+       unsigned        height,sourceofs;\r
+\r
+#ifdef PROFILE\r
+       updatecount = 0;\r
+#endif\r
+\r
+\r
+       for (priority=0;priority<PRIORITIES;priority++)\r
+       {\r
+               if (priority==MASKEDTILEPRIORITY)\r
+                       RFL_MaskForegroundTiles ();\r
+\r
+               for (sprite = prioritystart[priority]; sprite ;\r
+                       sprite = (spritelisttype *)sprite->nextsprite)\r
+               {\r
+               //\r
+               // see if the sprite has any visable area in the port\r
+               //\r
+\r
+                       portx = sprite->screenx - originxscreen;\r
+                       porty = sprite->screeny - originyscreen;\r
+                       xtl = portx >> SX_T_SHIFT;\r
+                       xth = (portx + sprite->width-1) >> SX_T_SHIFT;\r
+                       ytl = porty >> SY_T_SHIFT;\r
+                       yth = (porty + sprite->height-1) >> SY_T_SHIFT;\r
+\r
+                       if (xtl<0)\r
+                         xtl = 0;\r
+                       if (xth>=PORTTILESWIDE)\r
+                         xth = PORTTILESWIDE-1;\r
+                       if (ytl<0)\r
+                         ytl = 0;\r
+                       if (yth>=PORTTILESHIGH)\r
+                         yth = PORTTILESHIGH-1;\r
+\r
+                       if (xtl>xth || ytl>yth)\r
+                               continue;\r
+\r
+               //\r
+               // see if it's visable area covers any non 0 update tiles\r
+               //\r
+                       updatespot = baseupdatespot = updateptr + uwidthtable[ytl] + xtl;\r
+                       updatedelta = UPDATEWIDE - (xth-xtl+1);\r
+\r
+                       if (sprite->updatecount)\r
+                       {\r
+                               sprite->updatecount--;                  // the sprite was just placed,\r
+                               goto redraw;                                    // so draw it for sure\r
+                       }\r
+\r
+                       for (y=ytl;y<=yth;y++)\r
+                       {\r
+                               for (x=xtl;x<=xth;x++)\r
+                                       if (*updatespot++)\r
+                                               goto redraw;\r
+                               updatespot += updatedelta;              // down to next line\r
+                       }\r
+                       continue;                                                       // no need to update\r
+\r
+redraw:\r
+               //\r
+               // set the tiles it covers to 3, because those tiles are being\r
+               // updated\r
+               //\r
+                       updatespot = baseupdatespot;\r
+                       for (y=ytl;y<=yth;y++)\r
+                       {\r
+                               for (x=xtl;x<=xth;x++)\r
+                                       *updatespot++ = 3;\r
+                               updatespot += updatedelta;              // down to next line\r
+                       }\r
+               //\r
+               // draw it!\r
+               //\r
+                       height = sprite->height;\r
+                       sourceofs = sprite->sourceofs;\r
+                       if (porty<0)\r
+                       {\r
+                               height += porty;                                        // clip top off\r
+                               sourceofs -= porty*sprite->width;\r
+                               porty = 0;\r
+                       }\r
+                       else if (porty+height>PORTSCREENHIGH)\r
+                       {\r
+                               height = PORTSCREENHIGH - porty;    // clip bottom off\r
+                       }\r
+\r
+                       dest = bufferofs + ylookup[porty] + portx;\r
+\r
+                       switch (sprite->draw)\r
+                       {\r
+                       case spritedraw:\r
+                               VW_MaskBlock(grsegs[sprite->grseg], sourceofs,\r
+                                       dest,sprite->width,height,sprite->planesize);\r
+                               break;\r
+\r
+                       case maskdraw:\r
+                               VW_InverseMask(grsegs[sprite->grseg], sourceofs,\r
+                                       dest,sprite->width,height);\r
+                               break;\r
+\r
+                       }\r
+#ifdef PROFILE\r
+                       updatecount++;\r
+#endif\r
+\r
+\r
+               }\r
+       }\r
+}\r
+\r
+\r
+/*\r
+=====================\r
+=\r
+= RF_Refresh        CGA\r
+=\r
+= All routines will draw at the port at bufferofs, possibly copying from\r
+= the port at masterofs.  The EGA version then page flips, while the\r
+= CGA version updates the screen from the buffer port.\r
+=\r
+= Screenpage is the currently displayed page, not the one being drawn\r
+= Otherpage is the page to be worked with now\r
+=\r
+=====================\r
+*/\r
+\r
+void RF_Refresh (void)\r
+{\r
+       long newtime,oldtimecount;\r
+\r
+       RFL_AnimateTiles ();\r
+\r
+//\r
+// update newly scrolled on tiles and animated tiles from the master screen\r
+//\r
+       RFL_UpdateTiles ();\r
+       RFL_EraseBlocks ();\r
+\r
+//\r
+// Update is all 0 except where sprites have changed or new area has\r
+// been scrolled on.  Go through all sprites and update the ones that cover\r
+// a non 0 update tile\r
+//\r
+       RFL_UpdateSprites ();\r
+\r
+//\r
+// if the main program has a refresh hook set, call their function before\r
+// displaying the new page\r
+//\r
+       if (refreshvector)\r
+               refreshvector();\r
+\r
+//\r
+// update everything to the screen\r
+//\r
+       VW_CGAFullUpdate ();\r
+\r
+//\r
+// calculate tics since last refresh for adaptive timing\r
+//\r
+       RF_CalcTics ();\r
+}\r
+\r
+#endif         // GRMODE == CGAGR\r
diff --git a/16/keen456/KEEN4-6/ID_RF.H b/16/keen456/KEEN4-6/ID_RF.H
new file mode 100755 (executable)
index 0000000..5648034
--- /dev/null
@@ -0,0 +1,169 @@
+/* Reconstructed Commander Keen 4-6 Source Code\r
+ * Copyright (C) 2021 K1n9_Duk3\r
+ *\r
+ * This file is primarily based on:\r
+ * Catacomb 3-D Source Code\r
+ * Copyright (C) 1993-2014 Flat Rock Software\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License along\r
+ * with this program; if not, write to the Free Software Foundation, Inc.,\r
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+ */\r
+\r
+// ID_RF.H\r
+\r
+#define __ID_RF__\r
+\r
+#ifndef __ID_MM__\r
+#include "ID_MM.H"\r
+#endif\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                       CONSTANTS\r
+\r
+=============================================================================\r
+*/\r
+\r
+#define        MINTICS                         2\r
+#define        MAXTICS                         5\r
+#define DEMOTICS                       3\r
+\r
+#define        MAPBORDER                       2               // map border must be at least 1\r
+\r
+#ifdef KEEN5\r
+\r
+#define        MAXSPRITES                      60              // max tracked sprites\r
+#define        MAXANIMTILES            90              // max animating tiles on screen\r
+#define MAXANIMTYPES           80              // max different unique anim tiles on map\r
+\r
+#define        MAXMAPHEIGHT            250\r
+\r
+#else\r
+\r
+#define        MAXSPRITES                      60              // max tracked sprites\r
+#define        MAXANIMTILES            90              // max animating tiles on screen\r
+#define MAXANIMTYPES           65              // max different unique anim tiles on map\r
+\r
+#define        MAXMAPHEIGHT            200\r
+\r
+#endif\r
+\r
+#define        PRIORITIES                      4\r
+#define        MASKEDTILEPRIORITY      3               // planes go: 0,1,2,MTILES,3\r
+\r
+#define TILEGLOBAL                     256\r
+#define PIXGLOBAL                      16\r
+\r
+#define        G_T_SHIFT                       8               // global >> ?? = tile\r
+#define        G_P_SHIFT                       4               // global >> ?? = pixels\r
+#define P_T_SHIFT                      4               // pixels >> ?? = tile\r
+\r
+#define        PORTTILESWIDE           21      // all drawing takes place inside a\r
+#define        PORTTILESHIGH           14              // non displayed port of this size\r
+\r
+//#define      PORTGLOBALWIDE          (21*TILEGLOBAL)\r
+//#define      PORTGLOBALHIGH          (14*TILEGLOBAL)\r
+\r
+#define UPDATEWIDE                     (PORTTILESWIDE+1)\r
+#define UPDATEHIGH                     PORTTILESHIGH\r
+\r
+\r
+//===========================================================================\r
+\r
+typedef enum {spritedraw,maskdraw} drawtype;\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                PUBLIC VARIABLES\r
+\r
+=============================================================================\r
+*/\r
+\r
+\r
+extern boolean         compatability;                  // crippled refresh for wierdo SVGAs\r
+\r
+extern unsigned        tics;\r
+extern long            lasttimecount;\r
+\r
+extern unsigned        originxglobal,originyglobal;\r
+extern unsigned        originxtile,originytile;\r
+extern unsigned        originxscreen,originyscreen;\r
+\r
+extern unsigned        mapwidth,mapheight,mapbyteswide,mapwordswide\r
+                                       ,mapbytesextra,mapwordsextra;\r
+extern unsigned        mapbwidthtable[MAXMAPHEIGHT];\r
+\r
+extern unsigned        originxmin,originxmax,originymin,originymax;\r
+\r
+extern unsigned        masterofs;\r
+\r
+//\r
+// the floating update window is also used by the view manager for\r
+// double buffer tracking\r
+//\r
+\r
+extern byte            *updateptr;                             // current start of update window\r
+\r
+#if GRMODE == CGAGR\r
+extern byte            *baseupdateptr;\r
+#endif\r
+\r
+extern unsigned        blockstarts[UPDATEWIDE*UPDATEHIGH];\r
+extern unsigned        updatemapofs[UPDATEWIDE*UPDATEHIGH];\r
+extern unsigned        uwidthtable[UPDATEHIGH];                // lookup instead of multiple\r
+\r
+#define        UPDATETERMINATE 0x0301\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                PUBLIC FUNCTIONS\r
+\r
+=============================================================================\r
+*/\r
+\r
+void RF_Startup (void);\r
+void RF_Shutdown (void);\r
+\r
+void RF_FixOfs (void);\r
+void RF_NewMap (void);\r
+void RF_MarkTileGraphics (void);\r
+void RF_SetScrollBlock (int x, int y, boolean horizontal);\r
+void RF_NewPosition (unsigned x, unsigned y);\r
+void RF_Scroll (int x, int y);\r
+\r
+void RF_MapToMap (unsigned srcx, unsigned srcy,\r
+                                 unsigned destx, unsigned desty,\r
+                                 unsigned width, unsigned height);\r
+void RF_MemToMap (unsigned far *source, unsigned plane,\r
+                                 unsigned destx, unsigned desty,\r
+                                 unsigned width, unsigned height);\r
+\r
+void RF_ClearBlock (int        x, int y, int width, int height);\r
+void RF_RedrawBlock (int x, int y, int width, int height);\r
+\r
+void RF_PlaceSprite (void **user,unsigned globalx,unsigned globaly,\r
+       unsigned spritenumber, drawtype draw, int priority);\r
+void RF_RemoveSprite (void **user);\r
+\r
+void RF_CalcTics (void);\r
+\r
+void RF_Refresh (void);\r
+void RF_ForceRefresh (void);\r
+void RF_SetRefreshHook (void (*func) (void) );\r
+\r
+unsigned RF_FindFreeBuffer (void);\r
+\r
diff --git a/16/keen456/KEEN4-6/ID_RF_A.ASM b/16/keen456/KEEN4-6/ID_RF_A.ASM
new file mode 100755 (executable)
index 0000000..56db0c7
--- /dev/null
@@ -0,0 +1,690 @@
+; Catacomb 3-D Source Code\r
+; Copyright (C) 1993-2014 Flat Rock Software\r
+;\r
+; This program is free software; you can redistribute it and/or modify\r
+; it under the terms of the GNU General Public License as published by\r
+; the Free Software Foundation; either version 2 of the License, or\r
+; (at your option) any later version.\r
+;\r
+; This program is distributed in the hope that it will be useful,\r
+; but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+; GNU General Public License for more details.\r
+;\r
+; You should have received a copy of the GNU General Public License along\r
+; with this program; if not, write to the Free Software Foundation, Inc.,\r
+; 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+\r
+; ID_RF_A.ASM\r
+\r
+IDEAL\r
+MODEL  MEDIUM,C\r
+\r
+INCLUDE        "ID_ASM.EQU"\r
+\r
+;============================================================================\r
+\r
+TILESWIDE      =       21\r
+TILESHIGH      =       14\r
+\r
+UPDATESIZE     =       (TILESWIDE+1)*TILESHIGH+1\r
+\r
+DATASEG\r
+\r
+EXTRN  screenseg:WORD\r
+EXTRN  updateptr:WORD\r
+EXTRN  updatestart:WORD\r
+EXTRN  masterofs:WORD          ;start of master tile port\r
+EXTRN  bufferofs:WORD          ;start of current buffer port\r
+EXTRN  screenstart:WORD        ;starts of three screens (0/1/master) in EGA mem\r
+EXTRN  grsegs:WORD\r
+EXTRN  mapsegs:WORD\r
+EXTRN  originmap:WORD\r
+EXTRN  updatemapofs:WORD\r
+EXTRN  tinf:WORD                       ;seg pointer to map header and tile info\r
+EXTRN  blockstarts:WORD        ;offsets from bufferofs for each update block\r
+\r
+planemask      db      ?\r
+planenum       db      ?\r
+\r
+CODESEG\r
+\r
+screenstartcs  dw      ?               ;in code segment for accesability\r
+\r
+\r
+\r
+\r
+IFE GRMODE-CGAGR\r
+;============================================================================\r
+;\r
+; CGA refresh routines\r
+;\r
+;============================================================================\r
+\r
+TILEWIDTH      =       4\r
+\r
+;=================\r
+;\r
+; RFL_NewTile\r
+;\r
+; Draws a composit two plane tile to the master screen and sets the update\r
+; spot to 1 in both update pages, forcing the tile to be copied to the\r
+; view pages the next two refreshes\r
+;\r
+; Called to draw newlly scrolled on strips and animating tiles\r
+;\r
+;=================\r
+\r
+PROC   RFL_NewTile     updateoffset:WORD\r
+PUBLIC RFL_NewTile\r
+USES   SI,DI\r
+\r
+;\r
+; mark both update lists at this spot\r
+;\r
+       mov     di,[updateoffset]\r
+\r
+       mov     bx,[updateptr]                  ;start of update matrix\r
+       mov     [BYTE bx+di],1\r
+\r
+       mov     dx,SCREENWIDTH-TILEWIDTH                ;add to get to start of next line\r
+\r
+;\r
+; set di to the location in screenseg to draw the tile\r
+;\r
+       shl     di,1\r
+       mov     si,[updatemapofs+di]    ;offset in map from origin\r
+       add     si,[originmap]\r
+       mov     di,[blockstarts+di]             ;screen location for tile\r
+       add     di,[masterofs]\r
+\r
+;\r
+; set BX to the foreground tile number and SI to the background number\r
+; If either BX or SI = 0xFFFF, the tile does not need to be masked together\r
+; as one of the planes totally eclipses the other\r
+;\r
+       mov     es,[mapsegs+2]                  ;foreground plane\r
+       mov     bx,[es:si]\r
+       mov     es,[mapsegs]                    ;background plane\r
+       mov     si,[es:si]\r
+\r
+       mov     es,[screenseg]\r
+\r
+       or      bx,bx\r
+       jz      @@singletile\r
+       jmp     @@maskeddraw                    ;draw both together\r
+\r
+;=============\r
+;\r
+; Draw single background tile from main memory\r
+;\r
+;=============\r
+\r
+@@singletile:\r
+       shl     si,1\r
+       mov     ds,[grsegs+STARTTILE16*2+si]\r
+\r
+       xor     si,si                                   ;block is segment aligned\r
+\r
+REPT   15\r
+       movsw\r
+       movsw\r
+       add     di,dx\r
+ENDM\r
+       movsw\r
+       movsw\r
+\r
+       mov     ax,ss\r
+       mov     ds,ax                                   ;restore turbo's data segment\r
+       ret\r
+\r
+\r
+;=========\r
+;\r
+; Draw a masked tile combo\r
+; Interupts are disabled and the stack segment is reassigned\r
+;\r
+;=========\r
+@@maskeddraw:\r
+       cli                                                     ; don't allow ints when SS is set\r
+       shl     bx,1\r
+       mov     ss,[grsegs+STARTTILE16M*2+bx]\r
+       shl     si,1\r
+       mov     ds,[grsegs+STARTTILE16*2+si]\r
+\r
+       xor     si,si                                   ;first word of tile data\r
+\r
+REPT   16\r
+       mov     ax,[si]                                 ;background tile\r
+       and     ax,[ss:si]                              ;mask\r
+       or      ax,[ss:si+64]                   ;masked data\r
+       stosw\r
+       mov     ax,[si+2]                               ;background tile\r
+       and     ax,[ss:si+2]                    ;mask\r
+       or      ax,[ss:si+66]                   ;masked data\r
+       stosw\r
+       add     si,4\r
+       add     di,dx\r
+ENDM\r
+\r
+       mov     ax,@DATA\r
+       mov     ss,ax\r
+       sti\r
+       mov     ds,ax\r
+       ret\r
+ENDP\r
+\r
+ENDIF\r
+\r
+\r
+\r
+IFE GRMODE-EGAGR\r
+;===========================================================================\r
+;\r
+; EGA refresh routines\r
+;\r
+;===========================================================================\r
+\r
+TILEWIDTH      =       2\r
+\r
+;=================\r
+;\r
+; RFL_NewTile\r
+;\r
+; Draws a composit two plane tile to the master screen and sets the update\r
+; spot to 1 in both update pages, forcing the tile to be copied to the\r
+; view pages the next two refreshes\r
+;\r
+; Called to draw newlly scrolled on strips and animating tiles\r
+;\r
+; Assumes write mode 0\r
+;\r
+;=================\r
+\r
+PROC   RFL_NewTile     updateoffset:WORD\r
+PUBLIC RFL_NewTile\r
+USES   SI,DI\r
+\r
+;\r
+; mark both update lists at this spot\r
+;\r
+       mov     di,[updateoffset]\r
+\r
+       mov     bx,[updatestart]                ;page 0 pointer\r
+       mov     [BYTE bx+di],1\r
+       mov     bx,[updatestart+2]              ;page 1 pointer\r
+       mov     [BYTE bx+di],1\r
+\r
+;\r
+; set screenstartcs to the location in screenseg to draw the tile\r
+;\r
+       shl     di,1\r
+       mov     si,[updatemapofs+di]    ;offset in map from origin\r
+       add     si,[originmap]\r
+       mov     di,[blockstarts+di]             ;screen location for tile\r
+       add     di,[masterofs]\r
+       mov     [cs:screenstartcs],di\r
+\r
+;\r
+; set BX to the foreground tile number and SI to the background number\r
+; If either BX or SI = 0xFFFF, the tile does not need to be masked together\r
+; as one of the planes totally eclipses the other\r
+;\r
+       mov     es,[mapsegs+2]                  ;foreground plane\r
+       mov     bx,[es:si]\r
+       mov     es,[mapsegs]                    ;background plane\r
+       mov     si,[es:si]\r
+\r
+       mov     es,[screenseg]\r
+       mov     dx,SC_INDEX                             ;for stepping through map mask planes\r
+\r
+       or      bx,bx\r
+       jz      @@singletile\r
+       jmp     @@maskeddraw                    ;draw both together\r
+\r
+;=========\r
+;\r
+; No foreground tile, so draw a single background tile.\r
+;\r
+;=========\r
+@@singletile:\r
+\r
+       mov     bx,SCREENWIDTH-2                ;add to get to start of next line\r
+       shl     si,1\r
+\r
+       mov     ax,[cs:screenstartcs]\r
+       mov     ds,[grsegs+STARTTILE16*2+si]\r
+\r
+       xor     si,si                                   ;block is segment aligned\r
+\r
+       mov     ax,SC_MAPMASK+0001b*256 ;map mask for plane 0\r
+\r
+       mov     cx,4                                    ;draw four planes\r
+@@planeloop:\r
+       mov     dx,SC_INDEX\r
+       WORDOUT\r
+\r
+       mov     di,[cs:screenstartcs]   ;start at same place in all planes\r
+\r
+REPT   15\r
+       movsw\r
+       add     di,bx\r
+ENDM\r
+       movsw\r
+\r
+       shl     ah,1                                    ;shift plane mask over for next plane\r
+       loop    @@planeloop\r
+\r
+       mov     ax,ss\r
+       mov     ds,ax                                   ;restore turbo's data segment\r
+       ret\r
+\r
+\r
+;=========\r
+;\r
+; Draw a masked tile combo\r
+; Interupts are disabled and the stack segment is reassigned\r
+;\r
+;=========\r
+@@maskeddraw:\r
+       cli                                                     ; don't allow ints when SS is set\r
+       shl     bx,1\r
+       mov     ss,[grsegs+STARTTILE16M*2+bx]\r
+       shl     si,1\r
+       mov     ds,[grsegs+STARTTILE16*2+si]\r
+\r
+       xor     si,si                                   ;first word of tile data\r
+\r
+       mov     ax,SC_MAPMASK+0001b*256 ;map mask for plane 0\r
+\r
+       mov     di,[cs:screenstartcs]\r
+@@planeloopm:\r
+       WORDOUT\r
+tileofs                =       0\r
+lineoffset     =       0\r
+REPT   16\r
+       mov     bx,[si+tileofs]                 ;background tile\r
+       and     bx,[ss:tileofs]                 ;mask\r
+       or      bx,[ss:si+tileofs+32]   ;masked data\r
+       mov     [es:di+lineoffset],bx\r
+tileofs                =       tileofs + 2\r
+lineoffset     =       lineoffset + SCREENWIDTH\r
+ENDM\r
+       add     si,32\r
+       shl     ah,1                                    ;shift plane mask over for next plane\r
+       cmp     ah,10000b\r
+       je      @@done                                  ;drawn all four planes\r
+       jmp     @@planeloopm\r
+\r
+@@done:\r
+       mov     ax,@DATA\r
+       mov     ss,ax\r
+       sti\r
+       mov     ds,ax\r
+       ret\r
+ENDP\r
+\r
+ENDIF\r
+\r
+IFE GRMODE-VGAGR\r
+;============================================================================\r
+;\r
+; VGA refresh routines\r
+;\r
+;============================================================================\r
+\r
+\r
+ENDIF\r
+\r
+\r
+;============================================================================\r
+;\r
+; reasonably common refresh routines\r
+;\r
+;============================================================================\r
+\r
+\r
+;=================\r
+;\r
+; RFL_UpdateTiles\r
+;\r
+; Scans through the update matrix pointed to by updateptr, looking for 1s.\r
+; A 1 represents a tile that needs to be copied from the master screen to the\r
+; current screen (a new row or an animated tiled).  If more than one adjacent\r
+; tile in a horizontal row needs to be copied, they will be copied as a group.\r
+;\r
+; Assumes write mode 1\r
+;\r
+;=================\r
+\r
+\r
+; AX   0/1 for scasb, temp for segment register transfers\r
+; BX    width for block copies\r
+; CX   REP counter\r
+; DX   line width deltas\r
+; SI   source for copies\r
+; DI   scas dest / movsb dest\r
+; BP   pointer to UPDATETERMINATE\r
+;\r
+; DS\r
+; ES\r
+; SS\r
+\r
+PROC   RFL_UpdateTiles\r
+PUBLIC RFL_UpdateTiles\r
+USES   SI,DI,BP\r
+\r
+       jmp     SHORT @@realstart\r
+@@done:\r
+;\r
+; all tiles have been scanned\r
+;\r
+       ret\r
+\r
+@@realstart:\r
+       mov     di,[updateptr]\r
+       mov     bp,(TILESWIDE+1)*TILESHIGH+1\r
+       add     bp,di                                   ; when di = bx, all tiles have been scanned\r
+       push    di\r
+       mov     cx,-1                                   ; definately scan the entire thing\r
+\r
+;\r
+; scan for a 1 in the update list, meaning a tile needs to be copied\r
+; from the master screen to the current screen\r
+;\r
+@@findtile:\r
+       pop     di                                              ; place to continue scaning from\r
+       mov     ax,ss\r
+       mov     es,ax                                   ; search in the data segment\r
+       mov     ds,ax\r
+       mov al,1\r
+       repne   scasb\r
+       cmp     di,bp\r
+       je      @@done\r
+\r
+       cmp     [BYTE di],al\r
+       jne     @@singletile\r
+       jmp     @@tileblock\r
+\r
+;============\r
+;\r
+; copy a single tile\r
+;\r
+;============\r
+EVEN\r
+@@singletile:\r
+       inc     di                                              ; we know the next tile is nothing\r
+       push    di                                      ; save off the spot being scanned\r
+       sub     di,[updateptr]\r
+       shl     di,1\r
+       mov     di,[blockstarts-4+di]   ; start of tile location on screen\r
+       mov     si,di\r
+       add     di,[bufferofs]                  ; dest in current screen\r
+       add     si,[masterofs]                  ; source in master screen\r
+\r
+       mov     dx,SCREENWIDTH-TILEWIDTH\r
+       mov     ax,[screenseg]\r
+       mov     ds,ax\r
+       mov     es,ax\r
+\r
+;--------------------------\r
+\r
+IFE GRMODE-CGAGR\r
+\r
+REPT   15\r
+       movsw\r
+       movsw\r
+       add     si,dx\r
+       add     di,dx\r
+ENDM\r
+       movsw\r
+       movsw\r
+\r
+ENDIF\r
+\r
+;--------------------------\r
+\r
+IFE GRMODE-EGAGR\r
+\r
+REPT   15\r
+       movsb\r
+       movsb\r
+       add     si,dx\r
+       add     di,dx\r
+ENDM\r
+       movsb\r
+       movsb\r
+\r
+ENDIF\r
+\r
+;--------------------------\r
+\r
+       jmp     @@findtile\r
+\r
+;============\r
+;\r
+; more than one tile in a row needs to be updated, so do it as a group\r
+;\r
+;============\r
+EVEN\r
+@@tileblock:\r
+       mov     dx,di                                   ; hold starting position + 1 in dx\r
+       inc     di                                              ; we know the next tile also gets updated\r
+       repe    scasb                           ; see how many more in a row\r
+       push    di                                      ; save off the spot being scanned\r
+\r
+       mov     bx,di\r
+       sub     bx,dx                                   ; number of tiles in a row\r
+       shl     bx,1                                    ; number of bytes / row\r
+\r
+       mov     di,dx                                   ; lookup position of start tile\r
+       sub     di,[updateptr]\r
+       shl     di,1\r
+       mov     di,[blockstarts-2+di]   ; start of tile location\r
+       mov     si,di\r
+       add     di,[bufferofs]                  ; dest in current screen\r
+       add     si,[masterofs]                  ; source in master screen\r
+\r
+       mov     dx,SCREENWIDTH\r
+       sub     dx,bx                                   ; offset to next line on screen\r
+IFE GRMODE-CGAGR\r
+       sub     dx,bx                                   ; bx is words wide in CGA tiles\r
+ENDIF\r
+\r
+       mov     ax,[screenseg]\r
+       mov     ds,ax\r
+       mov     es,ax\r
+\r
+REPT   15\r
+       mov     cx,bx\r
+IFE GRMODE-CGAGR\r
+       rep     movsw\r
+ENDIF\r
+IFE GRMODE-EGAGR\r
+       rep     movsb\r
+ENDIF\r
+       add     si,dx\r
+       add     di,dx\r
+ENDM\r
+       mov     cx,bx\r
+IFE GRMODE-CGAGR\r
+       rep     movsw\r
+ENDIF\r
+IFE GRMODE-EGAGR\r
+       rep     movsb\r
+ENDIF\r
+\r
+       dec     cx                                              ; was 0 from last rep movsb, now $ffff for scasb\r
+       jmp     @@findtile\r
+\r
+ENDP\r
+\r
+\r
+;============================================================================\r
+\r
+\r
+;=================\r
+;\r
+; RFL_MaskForegroundTiles\r
+;\r
+; Scan through update looking for 3's.  If the foreground tile there is a\r
+; masked foreground tile, draw it to the screen\r
+;\r
+;=================\r
+\r
+PROC   RFL_MaskForegroundTiles\r
+PUBLIC RFL_MaskForegroundTiles\r
+USES   SI,DI,BP\r
+       jmp     SHORT @@realstart\r
+@@done:\r
+;\r
+; all tiles have been scanned\r
+;\r
+       ret\r
+\r
+@@realstart:\r
+       mov     di,[updateptr]\r
+       mov     bp,(TILESWIDE+1)*TILESHIGH+2\r
+       add     bp,di                                   ; when di = bx, all tiles have been scanned\r
+       push    di\r
+       mov     cx,-1                                   ; definately scan the entire thing\r
+;\r
+; scan for a 3 in the update list\r
+;\r
+@@findtile:\r
+       mov     ax,ss\r
+       mov     es,ax                                   ; scan in the data segment\r
+       mov     al,3\r
+       pop     di                                              ; place to continue scaning from\r
+       repne   scasb\r
+       cmp     di,bp\r
+       je      @@done\r
+\r
+;============\r
+;\r
+; found a tile, see if it needs to be masked on\r
+;\r
+;============\r
+\r
+       push    di\r
+\r
+       sub     di,[updateptr]\r
+       shl     di,1\r
+       mov     si,[updatemapofs-2+di]  ; offset from originmap\r
+       add     si,[originmap]\r
+\r
+       mov     es,[mapsegs+2]                  ; foreground map plane segment\r
+       mov     si,[es:si]                              ; foreground tile number\r
+\r
+       or      si,si\r
+       jz      @@findtile                              ; 0 = no foreground tile\r
+\r
+       mov     bx,si\r
+       add     bx,INTILE                               ;INTILE tile info table\r
+       mov     es,[tinf]\r
+       test    [BYTE PTR es:bx],80h            ;high bit = masked tile\r
+       jz      @@findtile\r
+\r
+;-------------------\r
+\r
+IFE GRMODE-CGAGR\r
+;=================\r
+;\r
+; mask the tile CGA\r
+;\r
+;=================\r
+\r
+       mov     di,[blockstarts-2+di]\r
+       add     di,[bufferofs]\r
+       mov     es,[screenseg]\r
+       shl     si,1\r
+       mov     ds,[grsegs+STARTTILE16M*2+si]\r
+\r
+       mov     bx,64                                   ;data starts 64 bytes after mask\r
+\r
+       xor     si,si\r
+\r
+lineoffset     =       0\r
+REPT   16\r
+       mov     ax,[es:di+lineoffset]   ;background\r
+       and     ax,[si]                                 ;mask\r
+       or      ax,[si+bx]                              ;masked data\r
+       mov     [es:di+lineoffset],ax   ;background\r
+       inc     si\r
+       inc     si\r
+       mov     ax,[es:di+lineoffset+2] ;background\r
+       and     ax,[si]                                 ;mask\r
+       or      ax,[si+bx]                              ;masked data\r
+       mov     [es:di+lineoffset+2],ax ;background\r
+       inc     si\r
+       inc     si\r
+lineoffset     =       lineoffset + SCREENWIDTH\r
+ENDM\r
+ENDIF\r
+\r
+;-------------------\r
+\r
+IFE GRMODE-EGAGR\r
+;=================\r
+;\r
+; mask the tile\r
+;\r
+;=================\r
+\r
+       mov     [BYTE planemask],1\r
+       mov     [BYTE planenum],0\r
+\r
+       mov     di,[blockstarts-2+di]\r
+       add     di,[bufferofs]\r
+       mov     [cs:screenstartcs],di\r
+       mov     es,[screenseg]\r
+       shl     si,1\r
+       mov     ds,[grsegs+STARTTILE16M*2+si]\r
+\r
+       mov     bx,32                                   ;data starts 32 bytes after mask\r
+\r
+@@planeloopm:\r
+       mov     dx,SC_INDEX\r
+       mov     al,SC_MAPMASK\r
+       mov     ah,[ss:planemask]\r
+       WORDOUT\r
+       mov     dx,GC_INDEX\r
+       mov     al,GC_READMAP\r
+       mov     ah,[ss:planenum]\r
+       WORDOUT\r
+\r
+       xor     si,si\r
+       mov     di,[cs:screenstartcs]\r
+lineoffset     =       0\r
+REPT   16\r
+       mov     cx,[es:di+lineoffset]   ;background\r
+       and     cx,[si]                                 ;mask\r
+       or      cx,[si+bx]                              ;masked data\r
+       inc     si\r
+       inc     si\r
+       mov     [es:di+lineoffset],cx\r
+lineoffset     =       lineoffset + SCREENWIDTH\r
+ENDM\r
+       add     bx,32                                   ;the mask is now further away\r
+       inc     [ss:planenum]\r
+       shl     [ss:planemask],1                ;shift plane mask over for next plane\r
+       cmp     [ss:planemask],10000b   ;done all four planes?\r
+       je      @@drawn                                 ;drawn all four planes\r
+       jmp     @@planeloopm\r
+\r
+@@drawn:\r
+ENDIF\r
+\r
+;-------------------\r
+\r
+       mov     ax,ss\r
+       mov     ds,ax\r
+       mov     cx,-1                                   ;definately scan the entire thing\r
+\r
+       jmp     @@findtile\r
+\r
+ENDP\r
+\r
+\r
+END\r
+\r
diff --git a/16/keen456/KEEN4-6/ID_SD.C b/16/keen456/KEEN4-6/ID_SD.C
new file mode 100755 (executable)
index 0000000..91f1ba7
--- /dev/null
@@ -0,0 +1,1336 @@
+/* Reconstructed Commander Keen 4-6 Source Code\r
+ * Copyright (C) 2021 K1n9_Duk3\r
+ *\r
+ * This file is primarily based on:\r
+ * Catacomb 3-D Source Code\r
+ * Copyright (C) 1993-2014 Flat Rock Software\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License along\r
+ * with this program; if not, write to the Free Software Foundation, Inc.,\r
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+ */\r
+\r
+//\r
+//     ID Engine\r
+//     ID_SD.c - Sound Manager\r
+//     v1.1d1\r
+//     By Jason Blochowiak\r
+//\r
+\r
+//\r
+//     This module handles dealing with generating sound on the appropriate\r
+//             hardware\r
+//\r
+//     Depends on: User Mgr (for parm checking)\r
+//\r
+//     Globals:\r
+//             For User Mgr:\r
+//                     SoundSourcePresent - Sound Source thingie present?\r
+//                     SoundBlasterPresent - SoundBlaster card present?\r
+//                     AdLibPresent - AdLib card present?\r
+//                     SoundMode - What device is used for sound effects\r
+//                             (Use SM_SetSoundMode() to set)\r
+//                     MusicMode - What device is used for music\r
+//                             (Use SM_SetMusicMode() to set)\r
+//             For Cache Mgr:\r
+//                     NeedsDigitized - load digitized sounds?\r
+//                     NeedsMusic - load music?\r
+//\r
+\r
+#pragma hdrstop                // Wierdo thing with MUSE\r
+\r
+#include <dos.h>\r
+\r
+#ifdef _MUSE_      // Will be defined in ID_Types.h\r
+#include "ID_SD.h"\r
+#else\r
+#include "ID_HEADS.H"\r
+#endif\r
+#pragma        hdrstop\r
+#pragma        warn    -pia\r
+\r
+#define        SDL_SoundFinished()     {SoundNumber = SoundPriority = 0;}\r
+\r
+// Macros for AdLib stuff\r
+#define        selreg(n)       outportb(0x388,n)\r
+#define        writereg(n)     outportb(0x389,n)\r
+#define        readstat()      inportb(0x388)\r
+\r
+//     Global variables\r
+       boolean         SoundSourcePresent,SoundBlasterPresent,AdLibPresent,QuietFX,\r
+                               NeedsDigitized,NeedsMusic;\r
+       SDMode          SoundMode;\r
+       SMMode          MusicMode;\r
+       longword        TimeCount;\r
+       word            HackCount;\r
+       word            *SoundTable;    // Really * _seg *SoundTable, but that don't work\r
+       boolean         ssIsTandy;\r
+       word            ssPort = 2;\r
+\r
+//     Internal variables\r
+static boolean                 SD_Started;\r
+static boolean                 TimerDone;\r
+static word                    TimerVal,TimerDelay10,TimerDelay25,TimerDelay100;\r
+static longword                TimerDivisor,TimerCount;\r
+static char                    *ParmStrings[] =\r
+                                               {\r
+                                                       "noal",\r
+                                                       "adlib",\r
+                                                       nil\r
+                                               };\r
+static void                    (*SoundUserHook)(void);\r
+static word                    SoundNumber,SoundPriority;\r
+static void interrupt  (*t0OldService)(void);\r
+//static       word                    t0CountTable[] = {8,8,8,8,40,40};\r
+static long                    LocalTime;\r
+\r
+//     PC Sound variables\r
+static byte                    pcLastSample,far *pcSound;\r
+static longword                pcLengthLeft;\r
+static word                    pcSoundLookup[255];\r
+\r
+//     AdLib variables\r
+static boolean                 alNoCheck;\r
+static byte                    far *alSound;\r
+static word                    alBlock;\r
+static longword                alLengthLeft;\r
+static longword                alTimeCount;\r
+static Instrument              alZeroInst;\r
+\r
+// This table maps channel numbers to carrier and modulator op cells\r
+static byte                    carriers[9] =  { 3, 4, 5,11,12,13,19,20,21},\r
+                                               modifiers[9] = { 0, 1, 2, 8, 9,10,16,17,18},\r
+// This table maps percussive voice numbers to op cells\r
+                                               pcarriers[5] = {19,0xff,0xff,0xff,0xff},\r
+                                               pmodifiers[5] = {16,17,18,20,21};\r
+\r
+//     Sequencer variables\r
+static boolean                 sqActive;\r
+static word                    alFXReg;\r
+static ActiveTrack             *tracks[sqMaxTracks],\r
+                                               mytracks[sqMaxTracks];\r
+static word                    sqMode,sqFadeStep;\r
+static word                    far *sqHack,far *sqHackPtr,sqHackLen,sqHackSeqLen;\r
+static long                    sqHackTime;\r
+\r
+//     Internal routines\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//     SDL_SetTimer0() - Sets system timer 0 to the specified speed\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+#pragma        argsused\r
+static void\r
+SDL_SetTimer0(word speed)\r
+{\r
+#ifndef TPROF  // If using Borland's profiling, don't screw with the timer\r
+       outportb(0x43,0x36);                            // Change timer 0\r
+       outportb(0x40,speed);\r
+       outportb(0x40,speed >> 8);\r
+       TimerDivisor = speed;\r
+#else\r
+       TimerDivisor = 0x10000;\r
+#endif\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//     SDL_SetIntsPerSec() - Uses SDL_SetTimer0() to set the number of\r
+//             interrupts generated by system timer 0 per second\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static void\r
+SDL_SetIntsPerSec(word ints)\r
+{\r
+       SDL_SetTimer0(1192030 / ints);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//     SDL_TimingService() - Used by SDL_InitDelay() to determine a timing\r
+//             value for the current system that we're running on\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static void interrupt\r
+SDL_TimingService(void)\r
+{\r
+       TimerVal = _CX;\r
+       TimerDone++;\r
+\r
+       outportb(0x20,0x20);                            // Ack interrupt\r
+}\r
+\r
+#if 0\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//     SDL_InitDelay() - Sets up TimerDelay's for SDL_Delay()\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static void\r
+SDL_InitDelay(void)\r
+{\r
+       int             i;\r
+       word    timer;\r
+\r
+       setvect(8,SDL_TimingService);           // Set to my timer 0 ISR\r
+\r
+       SDL_SetIntsPerSec(1000);                        // Time 1ms\r
+\r
+       for (i = 0,timer = 0;i < 10;i++)        // Do timing test 10 times\r
+       {\r
+       asm     xor             dx,dx                                   // Zero DX\r
+       asm     mov             cx,0xffff                               // Put starting value in CX\r
+       asm     mov             [TimerDone],cx                  // TimerDone = false - 1\r
+startloop:\r
+       asm     or              [TimerDone],0\r
+       asm     jnz             startloop                               // Make sure we're at the start\r
+loop:\r
+       asm     test    [TimerDone],1                   // See if TimerDone flag got hit\r
+       asm     jnz             done                                    // Yep - drop out of the loop\r
+       asm     loop    loop\r
+done:\r
+\r
+               if (0xffff - TimerVal > timer)\r
+                       timer = 0xffff - TimerVal;\r
+       }\r
+       timer += timer / 2;                                     // Use some slop\r
+       TimerDelay10 =  timer / (1000 / 10);\r
+       TimerDelay25 =  timer / (1000 / 25);\r
+       TimerDelay100 = timer / (1000 / 100);\r
+\r
+       SDL_SetTimer0(0);                                       // Reset timer 0\r
+\r
+       setvect(8,t0OldService);                        // Set back to old ISR\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//     SDL_Delay() - Delays the specified amount of time\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static void\r
+SDL_Delay(word delay)\r
+{\r
+       if (!delay)\r
+               return;\r
+\r
+asm    mov             cx,[delay]\r
+loop:\r
+asm    test    [TimerDone],0   // Useless code - just for timing equivilency\r
+asm    jnz             done\r
+asm    loop    loop\r
+done:;\r
+}\r
+#endif\r
+\r
+//\r
+//     PC Sound code\r
+//\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//     SDL_PCPlaySound() - Plays the specified sound on the PC speaker\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+#ifdef _MUSE_\r
+void\r
+#else\r
+static void\r
+#endif\r
+SDL_PCPlaySound(PCSound far *sound)\r
+{\r
+asm    pushf\r
+asm    cli\r
+\r
+       pcLastSample = -1;\r
+       pcLengthLeft = sound->common.length;\r
+       pcSound = sound->data;\r
+\r
+asm    popf\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//     SDL_PCStopSound() - Stops the current sound playing on the PC Speaker\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+#ifdef _MUSE_\r
+void\r
+#else\r
+static void\r
+#endif\r
+SDL_PCStopSound(void)\r
+{\r
+asm    pushf\r
+asm    cli\r
+\r
+       (long)pcSound = 0;\r
+\r
+asm    in      al,0x61                 // Turn the speaker off\r
+asm    and     al,0xfd                 // ~2\r
+asm    out     0x61,al\r
+\r
+asm    popf\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//     SDL_PCService() - Handles playing the next sample in a PC sound\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static void\r
+SDL_PCService(void)\r
+{\r
+       byte    s;\r
+       word    t;\r
+\r
+       if (pcSound)\r
+       {\r
+               s = *pcSound++;\r
+               if (s != pcLastSample)\r
+               {\r
+               asm     pushf\r
+               asm     cli\r
+\r
+                       pcLastSample = s;\r
+                       if (s)                                  // We have a frequency!\r
+                       {\r
+                               t = pcSoundLookup[s];\r
+                       asm     mov     bx,[t]\r
+\r
+                       asm     mov     al,0xb6                 // Write to channel 2 (speaker) timer\r
+                       asm     out     43h,al\r
+                       asm     mov     al,bl\r
+                       asm     out     42h,al                  // Low byte\r
+                       asm     mov     al,bh\r
+                       asm     out     42h,al                  // High byte\r
+\r
+                       asm     in      al,0x61                 // Turn the speaker & gate on\r
+                       asm     or      al,3\r
+                       asm     out     0x61,al\r
+                       }\r
+                       else                                    // Time for some silence\r
+                       {\r
+                       asm     in      al,0x61                 // Turn the speaker & gate off\r
+                       asm     and     al,0xfc                 // ~3\r
+                       asm     out     0x61,al\r
+                       }\r
+\r
+               asm     popf\r
+               }\r
+\r
+               if (!(--pcLengthLeft))\r
+               {\r
+                       SDL_PCStopSound();\r
+                       SDL_SoundFinished();\r
+               }\r
+       }\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//     SDL_ShutPC() - Turns off the pc speaker\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static void\r
+SDL_ShutPC(void)\r
+{\r
+asm    pushf\r
+asm    cli\r
+\r
+       pcSound = 0;\r
+\r
+asm    in      al,0x61                 // Turn the speaker & gate off\r
+asm    and     al,0xfc                 // ~3\r
+asm    out     0x61,al\r
+\r
+asm    popf\r
+}\r
+\r
+//     AdLib Code\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//     alOut(n,b) - Puts b in AdLib card register n\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+alOut(byte n,byte b)\r
+{\r
+asm    pushf\r
+asm    cli\r
+\r
+asm    mov             dx,0x388\r
+asm    mov             al,[n]\r
+asm    out             dx,al\r
+#if 0\r
+       SDL_Delay(TimerDelay10);\r
+#else\r
+asm    in      al, dx\r
+asm    in      al, dx\r
+asm    in      al, dx\r
+asm    in      al, dx\r
+asm    in      al, dx\r
+asm    in      al, dx\r
+#endif\r
+\r
+asm    mov             dx,0x389\r
+asm    mov             al,[b]\r
+asm    out             dx,al\r
+\r
+asm    popf\r
+\r
+#if 0\r
+       SDL_Delay(TimerDelay25);\r
+#else\r
+asm    mov     dx,0x388\r
+asm    in      al, dx\r
+asm    in      al, dx\r
+asm    in      al, dx\r
+asm    in      al, dx\r
+asm    in      al, dx\r
+asm    in      al, dx\r
+asm    in      al, dx\r
+asm    in      al, dx\r
+asm    in      al, dx\r
+asm    in      al, dx\r
+\r
+asm    in      al, dx\r
+asm    in      al, dx\r
+asm    in      al, dx\r
+asm    in      al, dx\r
+asm    in      al, dx\r
+asm    in      al, dx\r
+asm    in      al, dx\r
+asm    in      al, dx\r
+asm    in      al, dx\r
+asm    in      al, dx\r
+\r
+asm    in      al, dx\r
+asm    in      al, dx\r
+asm    in      al, dx\r
+asm    in      al, dx\r
+asm    in      al, dx\r
+asm    in      al, dx\r
+asm    in      al, dx\r
+asm    in      al, dx\r
+asm    in      al, dx\r
+asm    in      al, dx\r
+\r
+asm    in      al, dx\r
+asm    in      al, dx\r
+asm    in      al, dx\r
+asm    in      al, dx\r
+asm    in      al, dx\r
+#endif\r
+}\r
+\r
+#if 0\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//     SDL_SetInstrument() - Puts an instrument into a generator\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static void\r
+SDL_SetInstrument(int track,int which,Instrument far *inst,boolean percussive)\r
+{\r
+       byte            c,m;\r
+\r
+       if (percussive)\r
+       {\r
+               c = pcarriers[which];\r
+               m = pmodifiers[which];\r
+       }\r
+       else\r
+       {\r
+               c = carriers[which];\r
+               m = modifiers[which];\r
+       }\r
+\r
+       tracks[track - 1]->inst = *inst;\r
+       tracks[track - 1]->percussive = percussive;\r
+\r
+       alOut(m + alChar,inst->mChar);\r
+       alOut(m + alScale,inst->mScale);\r
+       alOut(m + alAttack,inst->mAttack);\r
+       alOut(m + alSus,inst->mSus);\r
+       alOut(m + alWave,inst->mWave);\r
+\r
+       // Most percussive instruments only use one cell\r
+       if (c != 0xff)\r
+       {\r
+               alOut(c + alChar,inst->cChar);\r
+               alOut(c + alScale,inst->cScale);\r
+               alOut(c + alAttack,inst->cAttack);\r
+               alOut(c + alSus,inst->cSus);\r
+               alOut(c + alWave,inst->cWave);\r
+       }\r
+\r
+       alOut(which + alFeedCon,inst->nConn);   // DEBUG - I think this is right\r
+}\r
+#endif\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//     SDL_ALStopSound() - Turns off any sound effects playing through the\r
+//             AdLib card\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+#ifdef _MUSE_\r
+void\r
+#else\r
+static void\r
+#endif\r
+SDL_ALStopSound(void)\r
+{\r
+asm    pushf\r
+asm    cli\r
+\r
+       (long)alSound = 0;\r
+       alOut(alFreqH + 0,0);\r
+\r
+asm    popf\r
+}\r
+\r
+static void\r
+SDL_AlSetFXInst(Instrument far *inst)\r
+{\r
+       byte            c,m;\r
+       byte            scale;  // added for "quiet AdLib" mode\r
+\r
+       m = modifiers[0];\r
+       c = carriers[0];\r
+       alOut(m + alChar,inst->mChar);\r
+       alOut(m + alScale,inst->mScale);\r
+       alOut(m + alAttack,inst->mAttack);\r
+       alOut(m + alSus,inst->mSus);\r
+       alOut(m + alWave,inst->mWave);\r
+       alOut(c + alChar,inst->cChar);\r
+#if 1\r
+       // quiet AdLib code:\r
+       scale = inst->cScale;\r
+       if (QuietFX)\r
+       {\r
+               scale = 0x3F-scale;\r
+               scale = (scale>>1) + (scale>>2);        // basically 'scale *= 0.75;'\r
+               scale = 0x3F-scale;\r
+       }\r
+       alOut(c + alScale,scale);\r
+#else\r
+       // old code:\r
+       alOut(c + alScale,inst->cScale);\r
+#endif\r
+       alOut(c + alAttack,inst->cAttack);\r
+       alOut(c + alSus,inst->cSus);\r
+       alOut(c + alWave,inst->cWave);\r
+       // DEBUG!!! - I just put this in\r
+//     alOut(alFeedCon,inst->nConn);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//     SDL_ALPlaySound() - Plays the specified sound on the AdLib card\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+#ifdef _MUSE_\r
+void\r
+#else\r
+static void\r
+#endif\r
+SDL_ALPlaySound(AdLibSound far *sound)\r
+{\r
+       Instrument      far *inst;\r
+\r
+       SDL_ALStopSound();\r
+\r
+asm    pushf\r
+asm    cli\r
+\r
+       alLengthLeft = sound->common.length;\r
+       alSound = sound->data;\r
+       alBlock = ((sound->block & 7) << 2) | 0x20;\r
+       inst = &sound->inst;\r
+\r
+       if (!(inst->mSus | inst->cSus))\r
+       {\r
+       asm     popf\r
+               Quit("SDL_ALPlaySound() - Bad instrument");\r
+       }\r
+\r
+       SDL_AlSetFXInst(inst);\r
+\r
+asm    popf\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//     SDL_ALSoundService() - Plays the next sample out through the AdLib card\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static void\r
+SDL_ALSoundService(void)\r
+{\r
+       byte    s;\r
+\r
+       if (alSound)\r
+       {\r
+               s = *alSound++;\r
+               if (!s)\r
+                       alOut(alFreqH + 0,0);\r
+               else\r
+               {\r
+                       alOut(alFreqL + 0,s);\r
+                       alOut(alFreqH + 0,alBlock);\r
+               }\r
+\r
+               if (!(--alLengthLeft))\r
+               {\r
+                       (long)alSound = 0;\r
+                       alOut(alFreqH + 0,0);\r
+                       SDL_SoundFinished();\r
+               }\r
+       }\r
+}\r
+\r
+#if 0\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//     SDL_SelectMeasure() - sets up sequencing variables for a given track\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static void\r
+SDL_SelectMeasure(ActiveTrack *track)\r
+{\r
+       track->seq = track->moods[track->mood];\r
+       track->nextevent = 0;\r
+}\r
+#endif\r
+\r
+static void\r
+SDL_ALService(void)\r
+{\r
+       byte    a,v;\r
+       word    w;\r
+\r
+       if (!sqActive)\r
+               return;\r
+\r
+       while (sqHackLen && (sqHackTime <= alTimeCount))\r
+       {\r
+               w = *sqHackPtr++;\r
+               sqHackTime = alTimeCount + *sqHackPtr++;\r
+       asm     mov     dx,[w]\r
+       asm     mov     [a],dl\r
+       asm     mov     [v],dh\r
+               alOut(a,v);\r
+               sqHackLen -= 4;\r
+       }\r
+       alTimeCount++;\r
+       if (!sqHackLen)\r
+       {\r
+               sqHackPtr = (word far *)sqHack;\r
+               sqHackLen = sqHackSeqLen;\r
+               alTimeCount = sqHackTime = 0;\r
+       }\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//     SDL_ShutAL() - Shuts down the AdLib card for sound effects\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static void\r
+SDL_ShutAL(void)\r
+{\r
+asm    pushf\r
+asm    cli\r
+\r
+       alOut(alEffects,0);\r
+       alOut(alFreqH + 0,0);\r
+       SDL_AlSetFXInst(&alZeroInst);\r
+       alSound = 0;\r
+\r
+asm    popf\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//     SDL_CleanAL() - Totally shuts down the AdLib card\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static void\r
+SDL_CleanAL(void)\r
+{\r
+       int     i;\r
+\r
+asm    pushf\r
+asm    cli\r
+\r
+       alOut(alEffects,0);\r
+       for (i = 1;i < 0xf5;i++)\r
+               alOut(i,0);\r
+\r
+asm    popf\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//     SDL_StartAL() - Starts up the AdLib card for sound effects\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static void\r
+SDL_StartAL(void)\r
+{\r
+       alFXReg = 0;\r
+       alOut(alEffects,alFXReg);\r
+       SDL_AlSetFXInst(&alZeroInst);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//     SDL_DetectAdLib() - Determines if there's an AdLib (or SoundBlaster\r
+//             emulating an AdLib) present\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static boolean\r
+SDL_DetectAdLib(boolean force)\r
+{\r
+       byte    status1,status2;\r
+       int             i;\r
+\r
+       alOut(4,0x60);  // Reset T1 & T2\r
+       alOut(4,0x80);  // Reset IRQ\r
+       status1 = readstat();\r
+       alOut(2,0xff);  // Set timer 1\r
+       alOut(4,0x21);  // Start timer 1\r
+#if 0\r
+       SDL_Delay(TimerDelay100);\r
+#else\r
+       asm     mov     dx, 0x388;\r
+       asm     mov     cx, 100;\r
+waitloop:\r
+       asm     in      al, dx;\r
+       asm     jmp     here;\r
+here:\r
+       asm     loop    waitloop;\r
+#endif\r
+\r
+       status2 = readstat();\r
+       alOut(4,0x60);\r
+       alOut(4,0x80);\r
+\r
+       if (force || (((status1 & 0xe0) == 0x00) && ((status2 & 0xe0) == 0xc0)))\r
+       {\r
+               for (i = 1;i <= 0xf5;i++)       // Zero all the registers\r
+                       alOut(i,0);\r
+\r
+               alOut(1,0x20);  // Set WSE=1\r
+               alOut(8,0);             // Set CSM=0 & SEL=0\r
+\r
+               return(true);\r
+       }\r
+       else\r
+               return(false);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//     SDL_t0Service() - My timer 0 ISR which handles the different timings and\r
+//             dispatches to whatever other routines are appropriate\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static void interrupt\r
+SDL_t0Service(void)\r
+{\r
+static word    count = 1;\r
+\r
+#if 0  // for debugging\r
+asm    mov     dx,STATUS_REGISTER_1\r
+asm    in      al,dx\r
+asm    mov     dx,ATR_INDEX\r
+asm    mov     al,ATR_OVERSCAN\r
+asm    out     dx,al\r
+asm    mov     al,4    // red\r
+asm    out     dx,al\r
+#endif\r
+\r
+       HackCount++;\r
+\r
+       if (MusicMode == smm_AdLib)\r
+       {\r
+               SDL_ALService();\r
+               if (!(++count & 7))\r
+               {\r
+                       LocalTime++;\r
+                       TimeCount++;\r
+                       if (SoundUserHook)\r
+                               SoundUserHook();\r
+               }\r
+               if (!(count & 3))\r
+               {\r
+                       switch (SoundMode)\r
+                       {\r
+                       case sdm_PC:\r
+                               SDL_PCService();\r
+                               break;\r
+                       case sdm_AdLib:\r
+                               SDL_ALSoundService();\r
+                               break;\r
+                       }\r
+               }\r
+       }\r
+       else\r
+       {\r
+               if (!(++count & 1))\r
+               {\r
+                       LocalTime++;\r
+                       TimeCount++;\r
+                       if (SoundUserHook)\r
+                               SoundUserHook();\r
+               }\r
+               switch (SoundMode)\r
+               {\r
+               case sdm_PC:\r
+                       SDL_PCService();\r
+                       break;\r
+               case sdm_AdLib:\r
+                       SDL_ALSoundService();\r
+                       break;\r
+               }\r
+       }\r
+\r
+asm    mov     ax,[WORD PTR TimerCount]\r
+asm    add     ax,[WORD PTR TimerDivisor]\r
+asm    mov     [WORD PTR TimerCount],ax\r
+asm    jnc     myack\r
+       t0OldService();                 // If we overflow a word, time to call old int handler\r
+asm    jmp     olddone\r
+myack:;\r
+       outportb(0x20,0x20);    // Ack the interrupt\r
+olddone:;\r
+\r
+#if 0  // for debugging\r
+asm    mov     dx,STATUS_REGISTER_1\r
+asm    in      al,dx\r
+asm    mov     dx,ATR_INDEX\r
+asm    mov     al,ATR_OVERSCAN\r
+asm    out     dx,al\r
+asm    mov     al,3    // blue\r
+asm    out     dx,al\r
+asm    mov     al,0x20 // normal\r
+asm    out     dx,al\r
+#endif\r
+}\r
+\r
+////////////////////////////////////////////////////////////////////////////\r
+//\r
+//     SDL_ShutDevice() - turns off whatever device was being used for sound fx\r
+//\r
+////////////////////////////////////////////////////////////////////////////\r
+static void\r
+SDL_ShutDevice(void)\r
+{\r
+       switch (SoundMode)\r
+       {\r
+       case sdm_PC:\r
+               SDL_ShutPC();\r
+               break;\r
+       case sdm_AdLib:\r
+               SDL_ShutAL();\r
+               break;\r
+       }\r
+       SoundMode = sdm_Off;\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//     SDL_CleanDevice() - totally shuts down all sound devices\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static void\r
+SDL_CleanDevice(void)\r
+{\r
+       if ((SoundMode == sdm_AdLib) || (MusicMode == smm_AdLib))\r
+               SDL_CleanAL();\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//     SDL_StartDevice() - turns on whatever device is to be used for sound fx\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static void\r
+SDL_StartDevice(void)\r
+{\r
+       switch (SoundMode)\r
+       {\r
+       case sdm_AdLib:\r
+               SDL_StartAL();\r
+               break;\r
+       }\r
+       SoundNumber = SoundPriority = 0;\r
+}\r
+\r
+static void\r
+SDL_SetTimerSpeed(void)\r
+{\r
+       word    rate;\r
+\r
+       if (MusicMode == smm_AdLib)\r
+               rate = TickBase * 8;\r
+       else\r
+               rate = TickBase * 2;\r
+       SDL_SetIntsPerSec(rate);\r
+}\r
+\r
+//     Public routines\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//     SD_SetSoundMode() - Sets which sound hardware to use for sound effects\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+boolean\r
+SD_SetSoundMode(SDMode mode)\r
+{\r
+       boolean result;\r
+       word    tableoffset;\r
+\r
+       SD_StopSound();\r
+\r
+#ifndef        _MUSE_\r
+       switch (mode)\r
+       {\r
+       case sdm_Off:\r
+               NeedsDigitized = false;\r
+               result = true;\r
+               break;\r
+       case sdm_PC:\r
+               tableoffset = STARTPCSOUNDS;\r
+               NeedsDigitized = false;\r
+               result = true;\r
+               break;\r
+       case sdm_AdLib:\r
+               if (AdLibPresent)\r
+               {\r
+                       tableoffset = STARTADLIBSOUNDS;\r
+                       NeedsDigitized = false;\r
+                       result = true;\r
+               }\r
+               break;\r
+       default:\r
+               result = false;\r
+               break;\r
+       }\r
+#endif\r
+\r
+       if (result && (mode != SoundMode))\r
+       {\r
+               SDL_ShutDevice();\r
+               SoundMode = mode;\r
+#ifndef        _MUSE_\r
+               SoundTable = (word *)(&audiosegs[tableoffset]);\r
+#endif\r
+               SDL_StartDevice();\r
+       }\r
+\r
+       SDL_SetTimerSpeed();\r
+\r
+       return(result);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//     SD_SetMusicMode() - sets the device to use for background music\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+boolean\r
+SD_SetMusicMode(SMMode mode)\r
+{\r
+       boolean result;\r
+\r
+       SD_FadeOutMusic();\r
+       while (SD_MusicPlaying())\r
+               ;\r
+\r
+       switch (mode)\r
+       {\r
+       case smm_Off:\r
+               NeedsMusic = false;\r
+               result = true;\r
+               break;\r
+       case smm_AdLib:\r
+               if (AdLibPresent)\r
+               {\r
+                       NeedsMusic = true;\r
+                       result = true;\r
+               }\r
+               break;\r
+       default:\r
+               result = false;\r
+               break;\r
+       }\r
+\r
+       if (result)\r
+               MusicMode = mode;\r
+\r
+       SDL_SetTimerSpeed();\r
+\r
+       return(result);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//     SD_Startup() - starts up the Sound Mgr\r
+//             Detects all additional sound hardware and installs my ISR\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+SD_Startup(void)\r
+{\r
+       int     i;\r
+       boolean alForce;\r
+\r
+       alForce = false;\r
+\r
+       if (SD_Started)\r
+               return;\r
+\r
+       ssIsTandy = false;\r
+       alNoCheck = false;\r
+#ifndef        _MUSE_\r
+       for (i = 1;i < _argc;i++)\r
+       {\r
+               switch (US_CheckParm(_argv[i],ParmStrings))\r
+               {\r
+               case 0:                                         // No AdLib detection\r
+                       alNoCheck = true;\r
+                       break;\r
+\r
+               case 1:\r
+                       alForce = true;\r
+                       break;\r
+               }\r
+       }\r
+#endif\r
+\r
+       SoundUserHook = 0;\r
+\r
+       t0OldService = getvect(8);      // Get old timer 0 ISR\r
+\r
+       //SDL_InitDelay();                      // SDL_InitDelay() uses t0OldService\r
+\r
+       setvect(8,SDL_t0Service);       // Set to my timer 0 ISR\r
+       LocalTime = TimeCount = alTimeCount = 0;\r
+\r
+       SD_SetSoundMode(sdm_Off);\r
+       SD_SetMusicMode(smm_Off);\r
+\r
+       if (!alNoCheck)\r
+               AdLibPresent = SDL_DetectAdLib(alForce);\r
+\r
+       for (i = 0;i < 255;i++)\r
+               pcSoundLookup[i] = i * 60;\r
+\r
+       SD_Started = true;\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//     SD_Default() - Sets up the default behaviour for the Sound Mgr whether\r
+//             the config file was present or not.\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+SD_Default(boolean gotit,SDMode sd,SMMode sm)\r
+{\r
+       boolean gotsd,gotsm;\r
+\r
+       gotsd = gotsm = gotit;\r
+\r
+       if (gotsd)      // Make sure requested sound hardware is available\r
+       {\r
+               switch (sd)\r
+               {\r
+               case sdm_AdLib:\r
+                       gotsd = AdLibPresent;\r
+                       break;\r
+               }\r
+       }\r
+       if (!gotsd)\r
+       {\r
+               if (AdLibPresent)\r
+                       sd = sdm_AdLib;\r
+               else\r
+                       sd = sdm_PC;\r
+       }\r
+       if (sd != SoundMode)\r
+               SD_SetSoundMode(sd);\r
+\r
+\r
+       if (gotsm)      // Make sure requested music hardware is available\r
+       {\r
+               switch (sm)\r
+               {\r
+               case sdm_AdLib:         // BUG: this should use smm_AdLib!\r
+                       gotsm = AdLibPresent;\r
+                       break;\r
+               }\r
+       }\r
+       if (!gotsm)\r
+       {\r
+               if (AdLibPresent)\r
+                       sm = smm_AdLib;\r
+               else\r
+                       sm = smm_Off;\r
+       }\r
+       if (sm != MusicMode)\r
+               SD_SetMusicMode(sm);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//     SD_Shutdown() - shuts down the Sound Mgr\r
+//             Removes sound ISR and turns off whatever sound hardware was active\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+SD_Shutdown(void)\r
+{\r
+       if (!SD_Started)\r
+               return;\r
+\r
+       SD_MusicOff();\r
+       SDL_ShutDevice();\r
+       SDL_CleanDevice();\r
+\r
+       asm     pushf\r
+       asm     cli\r
+\r
+       SDL_SetTimer0(0);\r
+\r
+       setvect(8,t0OldService);\r
+\r
+       asm     popf\r
+\r
+       SD_Started = false;\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//     SD_SetUserHook() - sets the routine that the Sound Mgr calls every 1/70th\r
+//             of a second from its timer 0 ISR\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+SD_SetUserHook(void (* hook)(void))\r
+{\r
+       // BUG: interrupts should be disabled while setting SoundUserHook!\r
+       SoundUserHook = hook;\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//     SD_PlaySound() - plays the specified sound on the appropriate hardware\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+SD_PlaySound(soundnames sound)\r
+{\r
+       SoundCommon     far *s;\r
+\r
+       if ((SoundMode == sdm_Off) /*|| (sound == -1)*/)\r
+               return;\r
+\r
+       s = MK_FP(SoundTable[sound],0);\r
+       if (!s)\r
+               Quit("SD_PlaySound() - Uncached sound");\r
+       if (!s->length)\r
+               Quit("SD_PlaySound() - Zero length sound");\r
+       if (s->priority < SoundPriority)\r
+               return;\r
+\r
+       switch (SoundMode)\r
+       {\r
+       case sdm_PC:\r
+               SDL_PCPlaySound((void far *)s);\r
+               break;\r
+       case sdm_AdLib:\r
+               SDL_ALPlaySound((void far *)s);\r
+               break;\r
+       }\r
+\r
+       SoundNumber = sound;\r
+       SoundPriority = s->priority;\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//     SD_SoundPlaying() - returns the sound number that's playing, or 0 if\r
+//             no sound is playing\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+word\r
+SD_SoundPlaying(void)\r
+{\r
+       boolean result = false;\r
+\r
+       switch (SoundMode)\r
+       {\r
+       case sdm_PC:\r
+               result = pcSound? true : false;\r
+               break;\r
+       case sdm_AdLib:\r
+               result = alSound? true : false;\r
+               break;\r
+       }\r
+\r
+       if (result)\r
+               return(SoundNumber);\r
+       else\r
+               return(false);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//     SD_StopSound() - if a sound is playing, stops it\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+SD_StopSound(void)\r
+{\r
+       switch (SoundMode)\r
+       {\r
+       case sdm_PC:\r
+               SDL_PCStopSound();\r
+               break;\r
+       case sdm_AdLib:\r
+               SDL_ALStopSound();\r
+               break;\r
+       }\r
+\r
+       SDL_SoundFinished();\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//     SD_WaitSoundDone() - waits until the current sound is done playing\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+SD_WaitSoundDone(void)\r
+{\r
+       while (SD_SoundPlaying())\r
+               ;\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//     SD_MusicOn() - turns on the sequencer\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+SD_MusicOn(void)\r
+{\r
+       sqActive = true;\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//     SD_MusicOff() - turns off the sequencer and any playing notes\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+SD_MusicOff(void)\r
+{\r
+       word    i;\r
+\r
+\r
+       switch (MusicMode)\r
+       {\r
+       case smm_AdLib:\r
+               alFXReg = 0;\r
+               alOut(alEffects,0);\r
+               for (i = 0;i < sqMaxTracks;i++)\r
+                       alOut(alFreqH + i + 1,0);\r
+               break;\r
+       }\r
+       sqActive = false;\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//     SD_StartMusic() - starts playing the music pointed to\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+SD_StartMusic(MusicGroup far *music)\r
+{\r
+       SD_MusicOff();\r
+asm    pushf\r
+asm    cli\r
+\r
+       if (MusicMode == smm_AdLib)\r
+       {\r
+               sqHackPtr = sqHack = music->values;\r
+               sqHackSeqLen = sqHackLen = music->length;\r
+               sqHackTime = 0;\r
+               alTimeCount = 0;\r
+               SD_MusicOn();\r
+       }\r
+\r
+asm    popf\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//     SD_FadeOutMusic() - starts fading out the music. Call SD_MusicPlaying()\r
+//             to see if the fadeout is complete\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+SD_FadeOutMusic(void)\r
+{\r
+       switch (MusicMode)\r
+       {\r
+       case smm_AdLib:\r
+               // DEBUG - quick hack to turn the music off\r
+               SD_MusicOff();\r
+               break;\r
+       }\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//     SD_MusicPlaying() - returns true if music is currently playing, false if\r
+//             not\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+boolean\r
+SD_MusicPlaying(void)\r
+{\r
+       boolean result;\r
+\r
+       switch (MusicMode)\r
+       {\r
+       case smm_AdLib:\r
+               result = false;\r
+               // DEBUG - not written\r
+               break;\r
+       default:\r
+               result = false;\r
+       }\r
+\r
+       return(result);\r
+}\r
diff --git a/16/keen456/KEEN4-6/ID_SD.H b/16/keen456/KEEN4-6/ID_SD.H
new file mode 100755 (executable)
index 0000000..a48e442
--- /dev/null
@@ -0,0 +1,210 @@
+/* Reconstructed Commander Keen 4-6 Source Code\r
+ * Copyright (C) 2021 K1n9_Duk3\r
+ *\r
+ * This file is primarily based on:\r
+ * Catacomb 3-D Source Code\r
+ * Copyright (C) 1993-2014 Flat Rock Software\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License along\r
+ * with this program; if not, write to the Free Software Foundation, Inc.,\r
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+ */\r
+\r
+//\r
+//     ID Engine\r
+//     ID_SD.h - Sound Manager Header\r
+//     v1.0d1\r
+//     By Jason Blochowiak\r
+//\r
+\r
+#ifndef        __TYPES__\r
+#include "ID_Types.h"\r
+#endif\r
+\r
+#ifndef        __ID_SD__\r
+#define        __ID_SD__\r
+\r
+#ifdef __DEBUG__\r
+#define        __DEBUG_SoundMgr__\r
+#endif\r
+\r
+#define        TickBase        70              // 70Hz per tick - used as a base for timer 0\r
+\r
+typedef        enum    {\r
+                                       sdm_Off,\r
+                                       sdm_PC,sdm_AdLib,\r
+                               }       SDMode;\r
+typedef        enum    {\r
+                                       smm_Off,smm_AdLib\r
+                               }       SMMode;\r
+\r
+typedef        struct\r
+               {\r
+                       longword        length;\r
+                       word            priority;\r
+               } SoundCommon;\r
+\r
+//     PC Sound stuff\r
+#define        pcTimer         0x42\r
+#define        pcTAccess       0x43\r
+#define        pcSpeaker       0x61\r
+\r
+#define        pcSpkBits       3\r
+\r
+typedef        struct\r
+               {\r
+                       SoundCommon     common;\r
+                       byte            data[1];\r
+               } PCSound;\r
+\r
+//     Registers for the Sound Blaster card - needs to be offset by n0\r
+#define        sbReset         0x206\r
+#define        sbReadData      0x20a\r
+#define        sbWriteCmd      0x20c\r
+#define        sbWriteData     0x20c\r
+#define        sbWriteStat     0x20c\r
+#define        sbDataAvail     0x20e\r
+\r
+typedef        struct\r
+               {\r
+                       SoundCommon     common;\r
+                       word            hertz;\r
+                       byte            bits,\r
+                                               reference,\r
+                                               data[1];\r
+               } SampledSound;\r
+\r
+//     Registers for the AdLib card\r
+// Operator stuff\r
+#define        alChar          0x20\r
+#define        alScale         0x40\r
+#define        alAttack        0x60\r
+#define        alSus           0x80\r
+#define        alWave          0xe0\r
+// Channel stuff\r
+#define        alFreqL         0xa0\r
+#define        alFreqH         0xb0\r
+#define        alFeedCon       0xc0\r
+// Global stuff\r
+#define        alEffects       0xbd\r
+\r
+typedef        struct\r
+               {\r
+                       byte    mChar,cChar,\r
+                                       mScale,cScale,\r
+                                       mAttack,cAttack,\r
+                                       mSus,cSus,\r
+                                       mWave,cWave,\r
+                                       nConn,\r
+\r
+                                       // These are only for Muse - these bytes are really unused\r
+                                       voice,\r
+                                       mode,\r
+                                       unused[3];\r
+               } Instrument;\r
+\r
+typedef        struct\r
+               {\r
+                       SoundCommon     common;\r
+                       Instrument      inst;\r
+                       byte            block,\r
+                                               data[1];\r
+               } AdLibSound;\r
+\r
+//\r
+//     Sequencing stuff\r
+//\r
+#define        sqMaxTracks     10\r
+#define        sqMaxMoods      1       // DEBUG\r
+\r
+#define        sev_Null                0       // Does nothing\r
+#define        sev_NoteOff             1       // Turns a note off\r
+#define        sev_NoteOn              2       // Turns a note on\r
+#define        sev_NotePitch   3       // Sets the pitch of a currently playing note\r
+#define        sev_NewInst             4       // Installs a new instrument\r
+#define        sev_NewPerc             5       // Installs a new percussive instrument\r
+#define        sev_PercOn              6       // Turns a percussive note on\r
+#define        sev_PercOff             7       // Turns a percussive note off\r
+#define        sev_SeqEnd              -1      // Terminates a sequence\r
+\r
+//     Flags for MusicGroup.flags\r
+#define        sf_Melodic              0\r
+#define        sf_Percussive   1\r
+\r
+#if 1\r
+typedef        struct\r
+               {\r
+                       word    length,\r
+                                       values[1];\r
+               } MusicGroup;\r
+#else\r
+typedef        struct\r
+               {\r
+                       word    flags,\r
+                                       count,\r
+                                       offsets[1];\r
+               } MusicGroup;\r
+#endif\r
+\r
+typedef        struct\r
+               {\r
+                       /* This part needs to be set up by the user */\r
+                       word        mood,far *moods[sqMaxMoods];\r
+\r
+                       /* The rest is set up by the code */\r
+                       Instrument      inst;\r
+                       boolean         percussive;\r
+                       word            far *seq;\r
+                       longword        nextevent;\r
+               } ActiveTrack;\r
+\r
+#define        sqmode_Normal           0\r
+#define        sqmode_FadeIn           1\r
+#define        sqmode_FadeOut          2\r
+\r
+#define        sqMaxFade               64      // DEBUG\r
+\r
+\r
+// Global variables\r
+extern boolean         AdLibPresent,\r
+                                       NeedsMusic,     // For Caching Mgr\r
+                                       QuietFX;\r
+extern SDMode          SoundMode;\r
+extern SMMode          MusicMode;\r
+extern longword        TimeCount;                                      // Global time in ticks\r
+\r
+// Function prototypes\r
+extern void    SD_Startup(void),\r
+                               SD_Shutdown(void),\r
+                               SD_Default(boolean gotit,SDMode sd,SMMode sm),\r
+                               SD_PlaySound(soundnames sound),\r
+                               SD_StopSound(void),\r
+                               SD_WaitSoundDone(void),\r
+                               SD_StartMusic(MusicGroup far *music),\r
+                               SD_MusicOn(void),\r
+                               SD_MusicOff(void),\r
+                               SD_FadeOutMusic(void),\r
+                               SD_SetUserHook(void (*hook)(void));\r
+extern boolean SD_MusicPlaying(void),\r
+                               SD_SetSoundMode(SDMode mode),\r
+                               SD_SetMusicMode(SMMode mode);\r
+extern word    SD_SoundPlaying(void);\r
+\r
+#ifdef _MUSE_  // MUSE Goes directly to the lower level routines\r
+extern void    SDL_PCPlaySound(PCSound far *sound),\r
+                               SDL_PCStopSound(void),\r
+                               SDL_ALPlaySound(AdLibSound far *sound),\r
+                               SDL_ALStopSound(void);\r
+#endif\r
+\r
+#endif\r
diff --git a/16/keen456/KEEN4-6/ID_US.H b/16/keen456/KEEN4-6/ID_US.H
new file mode 100755 (executable)
index 0000000..2e50d2a
--- /dev/null
@@ -0,0 +1,160 @@
+/* Reconstructed Commander Keen 4-6 Source Code\r
+ * Copyright (C) 2021 K1n9_Duk3\r
+ *\r
+ * This file is primarily based on:\r
+ * Catacomb 3-D Source Code\r
+ * Copyright (C) 1993-2014 Flat Rock Software\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License along\r
+ * with this program; if not, write to the Free Software Foundation, Inc.,\r
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+ */\r
+\r
+//\r
+//     ID Engine\r
+//     ID_US.h - Header file for the User Manager\r
+//     v1.0d1\r
+//     By Jason Blochowiak\r
+//\r
+\r
+#ifndef        __TYPES__\r
+#include "ID_Types.h"\r
+#endif\r
+\r
+#ifndef        __ID_US__\r
+#define        __ID_US__\r
+\r
+#ifdef __DEBUG__\r
+#define        __DEBUG_UserMgr__\r
+#endif\r
+\r
+//#define      HELPTEXTLINKED\r
+\r
+#define        MaxX    320\r
+#define        MaxY    200\r
+\r
+#define        MaxHelpLines    500\r
+\r
+#define        MaxHighName     57\r
+#ifdef CAT3D\r
+#define        MaxScores       7\r
+#else\r
+#define        MaxScores       8\r
+#endif\r
+typedef        struct\r
+               {\r
+                       char    name[MaxHighName + 1];\r
+                       long    score;\r
+                       word    completed;\r
+               } HighScore;\r
+\r
+#define        MaxGameName             32\r
+#define        MaxSaveGames    6\r
+typedef        struct\r
+               {\r
+                       char    signature[4];\r
+                       word    *oldtest;\r
+                       boolean present;\r
+                       char    name[MaxGameName + 1];\r
+               } SaveGame;\r
+\r
+#define        MaxString       128     // Maximum input string size\r
+\r
+typedef        struct\r
+               {\r
+                       int     x,y,\r
+                               w,h,\r
+                               px,py;\r
+               } WindowRec;    // Record used to save & restore screen windows\r
+\r
+typedef        enum\r
+               {\r
+                       gd_Continue,\r
+                       gd_Easy,\r
+                       gd_Normal,\r
+                       gd_Hard\r
+               } GameDiff;\r
+\r
+//     Hack import for TED launch support\r
+extern boolean         tedlevel;\r
+extern word            tedlevelnum;\r
+extern void            TEDDeath(void);\r
+\r
+extern boolean         ingame,         // Set by game code if a game is in progress\r
+                                       abortgame,      // Set if a game load failed\r
+                                       loadedgame,     // Set if the current game was loaded\r
+#ifdef KEEN6\r
+                                       checkpassed,\r
+#endif\r
+                                       NoWait,\r
+                                       HighScoresDirty;\r
+extern char            *abortprogram;  // Set to error msg if program is dying\r
+extern GameDiff        restartgame;    // Normally gd_Continue, else starts game\r
+extern word            PrintX,PrintY;  // Current printing location in the window\r
+extern word            WindowX,WindowY,// Current location of window\r
+                                       WindowW,WindowH;// Current size of window\r
+\r
+extern boolean         Button0,Button1,\r
+                                       CursorBad;\r
+extern int                     CursorX,CursorY;\r
+\r
+extern void            (*USL_MeasureString)(char far *,word *,word *),\r
+                                       (*USL_DrawString)(char far *);\r
+\r
+extern boolean         (*USL_SaveGame)(int),(*USL_LoadGame)(int);\r
+extern void            (*USL_ResetGame)(void);\r
+extern SaveGame        Games[MaxSaveGames];\r
+extern HighScore       Scores[];\r
+\r
+#define        US_HomeWindow() {PrintX = WindowX; PrintY = WindowY;}\r
+\r
+extern void    US_Startup(void),\r
+                               US_Setup(void),\r
+                               US_Shutdown(void),\r
+                               US_InitRndT(boolean randomize),\r
+                               US_SetLoadSaveHooks(boolean (*load)(int),\r
+                                                                       boolean (*save)(int),\r
+                                                                       void (*reset)(void)),\r
+                               US_TextScreen(void),\r
+                               US_UpdateTextScreen(void),\r
+                               US_FinishTextScreen(void),\r
+                               US_ControlPanel(void),\r
+                               US_DrawWindow(word x,word y,word w,word h),\r
+                               US_CenterWindow(word,word),\r
+                               US_SaveWindow(WindowRec *win),\r
+                               US_RestoreWindow(WindowRec *win),\r
+                               US_ClearWindow(void),\r
+                               US_SetPrintRoutines(void (*measure)(char far *,word *,word *),\r
+                                                                       void (*print)(char far *)),\r
+                               US_PrintCentered(char *s),\r
+                               US_CPrint(char *s),\r
+                               US_CPrintLine(char *s),\r
+                               US_Print(char *s),\r
+                               US_PrintUnsigned(longword n),\r
+                               US_PrintSigned(long n),\r
+                               US_StartCursor(void),\r
+                               US_ShutCursor(void),\r
+                               US_ControlPanel(void),\r
+                               US_CheckHighScore(long score,word other),\r
+                               US_DisplayHighScores(int which);\r
+extern boolean US_UpdateCursor(void),\r
+                               US_LineInput(int x,int y,char *buf,char *def,boolean escok,\r
+                                                               int maxchars,int maxwidth);\r
+extern int             US_CheckParm(char *parm,char **strings),\r
+                               US_RndT(void);\r
+\r
+extern boolean US_ParmPresent(char *arg);\r
+\r
+               void    USL_PrintInCenter(char *s,Rect r);\r
+               char    *USL_GiveSaveName(word game);\r
+#endif\r
diff --git a/16/keen456/KEEN4-6/ID_US_1.C b/16/keen456/KEEN4-6/ID_US_1.C
new file mode 100755 (executable)
index 0000000..d7b27aa
--- /dev/null
@@ -0,0 +1,1358 @@
+/* Reconstructed Commander Keen 4-6 Source Code\r
+ * Copyright (C) 2021 K1n9_Duk3\r
+ *\r
+ * This file is primarily based on:\r
+ * Catacomb 3-D Source Code\r
+ * Copyright (C) 1993-2014 Flat Rock Software\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License along\r
+ * with this program; if not, write to the Free Software Foundation, Inc.,\r
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+ */\r
+\r
+//\r
+//      ID Engine\r
+//      ID_US_1.c - User Manager - General routines\r
+//      v1.1d1\r
+//      By Jason Blochowiak\r
+//      Hacked up for Catacomb 3D\r
+//\r
+\r
+//\r
+//      This module handles dealing with user input & feedback\r
+//\r
+//      Depends on: Input Mgr, View Mgr, some variables from the Sound, Caching,\r
+//              and Refresh Mgrs, Memory Mgr for background save/restore\r
+//\r
+//      Globals:\r
+//              ingame - Flag set by game indicating if a game is in progress\r
+//      abortgame - Flag set if the current game should be aborted (if a load\r
+//                      game fails)\r
+//              loadedgame - Flag set if a game was loaded\r
+//              abortprogram - Normally nil, this points to a terminal error message\r
+//                      if the program needs to abort\r
+//              restartgame - Normally set to gd_Continue, this is set to one of the\r
+//                      difficulty levels if a new game should be started\r
+//              PrintX, PrintY - Where the User Mgr will print (global coords)\r
+//              WindowX,WindowY,WindowW,WindowH - The dimensions of the current\r
+//                      window\r
+//\r
+\r
+#include "ID_HEADS.H"\r
+\r
+#pragma hdrstop\r
+\r
+#pragma warn    -pia\r
+\r
+\r
+//      Special imports\r
+extern  boolean         showscorebox;\r
+#ifdef  KEEN\r
+extern boolean         jerk;\r
+extern  boolean         oldshooting;\r
+extern  ScanCode        firescan;\r
+#else\r
+               ScanCode        firescan;\r
+#endif\r
+\r
+//      Global variables\r
+               char            *abortprogram;\r
+               boolean         NoWait,\r
+                                       HighScoresDirty;\r
+               word            PrintX,PrintY;\r
+               word            WindowX,WindowY,WindowW,WindowH;\r
+\r
+//      Internal variables\r
+#define ConfigVersion   4\r
+\r
+static  char            *ParmStrings[] = {"TEDLEVEL","NOWAIT"},\r
+                                       *ParmStrings2[] = {"COMP","NOCOMP"};\r
+static  boolean         US_Started;\r
+\r
+               boolean         Button0,Button1,\r
+                                       CursorBad;\r
+               int                     CursorX,CursorY;\r
+\r
+               void            (*USL_MeasureString)(char far *,word *,word *) = VW_MeasurePropString,\r
+                                       (*USL_DrawString)(char far *) = VWB_DrawPropString;\r
+\r
+               boolean         (*USL_SaveGame)(int),(*USL_LoadGame)(int);\r
+               void            (*USL_ResetGame)(void);\r
+               SaveGame        Games[MaxSaveGames];\r
+               HighScore       Scores[MaxScores] =\r
+                                       {\r
+#if defined CAT3D\r
+                                               {"Sir Lancelot",500,3},\r
+                                               {"",0},\r
+                                               {"",0},\r
+                                               {"",0},\r
+                                               {"",0},\r
+                                               {"",0},\r
+                                               {"",0},\r
+#elif defined GOODTIMES\r
+                                               {"Id Software",10000,0},\r
+                                               {"Adrian Carmack",10000,0},\r
+                                               {"John Carmack",10000,0},\r
+                                               {"Kevin Cloud",10000,0},\r
+                                               {"Shawn Green",10000,0},\r
+                                               {"Tom Hall",10000,0},\r
+                                               {"John Romero",10000,0},\r
+                                               {"Jay Wilbur",10000,0},\r
+#else\r
+                                               {"Id Software - '91",10000,0},\r
+                                               {"",10000,0},\r
+                                               {"Jason Blochowiak",10000,0},\r
+                                               {"Adrian Carmack",10000,0},\r
+                                               {"John Carmack",10000,0},\r
+                                               {"Tom Hall",10000,0},\r
+                                               {"John Romero",10000,0},\r
+                                               {"",10000,0},\r
+#endif\r
+                                       };\r
+\r
+//      Internal routines\r
+\r
+//      Public routines\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//      USL_HardError() - Handles the Abort/Retry/Fail sort of errors passed\r
+//                      from DOS.\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+#pragma warn    -par\r
+#pragma warn    -rch\r
+int\r
+USL_HardError(word errval,int ax,int bp,int si)\r
+{\r
+#define IGNORE  0\r
+#define RETRY   1\r
+#define ABORT   2\r
+extern  void    ShutdownId(void);\r
+\r
+static  char            buf[32];\r
+static  WindowRec       wr;\r
+               int                     di;\r
+               char            c,*s,*t;\r
+\r
+\r
+       di = _DI;\r
+\r
+       if (ax < 0)\r
+               s = "Device Error";\r
+       else\r
+       {\r
+               if ((di & 0x00ff) == 0)\r
+                       s = "Drive ~ is Write Protected";\r
+               else\r
+                       s = "Error on Drive ~";\r
+               for (t = buf;*s;s++,t++)        // Can't use sprintf()\r
+                       if ((*t = *s) == '~')\r
+                               *t = (ax & 0x00ff) + 'A';\r
+               *t = '\0';\r
+               s = buf;\r
+       }\r
+\r
+       c = peekb(0x40,0x49);   // Get the current screen mode\r
+       if ((c < 4) || (c == 7))\r
+               goto oh_kill_me;\r
+\r
+       // DEBUG - handle screen cleanup\r
+\r
+       US_SaveWindow(&wr);\r
+       US_CenterWindow(30,3);\r
+       US_CPrint(s);\r
+       US_CPrint("(R)etry or (A)bort?");\r
+       VW_UpdateScreen();\r
+       IN_ClearKeysDown();\r
+\r
+asm     sti     // Let the keyboard interrupts come through\r
+\r
+       while (true)\r
+       {\r
+               switch (IN_WaitForASCII())\r
+               {\r
+               case key_Escape:\r
+               case 'a':\r
+               case 'A':\r
+                       goto oh_kill_me;\r
+                       break;\r
+               case key_Return:\r
+               case key_Space:\r
+               case 'r':\r
+               case 'R':\r
+                       US_ClearWindow();\r
+                       VW_UpdateScreen();\r
+                       US_RestoreWindow(&wr);\r
+                       return(RETRY);\r
+                       break;\r
+               }\r
+       }\r
+\r
+oh_kill_me:\r
+       abortprogram = s;\r
+       ShutdownId();\r
+       fprintf(stderr,"Terminal Error: %s\n",s);\r
+       if (tedlevel)\r
+               fprintf(stderr,"You launched from TED. I suggest that you reboot...\n");\r
+\r
+       return(ABORT);\r
+#undef  IGNORE\r
+#undef  RETRY\r
+#undef  ABORT\r
+}\r
+#pragma warn    +par\r
+#pragma warn    +rch\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//      USL_GiveSaveName() - Returns a pointer to a static buffer that contains\r
+//              the filename to use for the specified save game\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+char *\r
+USL_GiveSaveName(word game)\r
+{\r
+static  char    name[] = "SAVEGAMx."EXTENSION;\r
+\r
+       name[7] = '0' + game;\r
+       return(name);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//      US_SetLoadSaveHooks() - Sets the routines that the User Mgr calls after\r
+//              reading or writing the save game headers\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+US_SetLoadSaveHooks(boolean (*load)(int),boolean (*save)(int),void (*reset)(void))\r
+{\r
+       USL_LoadGame = load;\r
+       USL_SaveGame = save;\r
+       USL_ResetGame = reset;\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//      USL_ReadConfig() - Reads the configuration file, if present, and sets\r
+//              things up accordingly. If it's not present, uses defaults. This file\r
+//              includes the high scores.\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static void\r
+USL_ReadConfig(void)\r
+{\r
+       boolean         gotit, hadAdLib;\r
+       char            sig[sizeof(EXTENSION)];\r
+       word            version;\r
+       int                     file;\r
+       SDMode          sd;\r
+       SMMode          sm;\r
+       ControlType     ctl;\r
+\r
+       if ((file = open("CONFIG."EXTENSION,O_BINARY | O_RDONLY)) != -1)\r
+       {\r
+               read(file,sig,sizeof(EXTENSION));\r
+               read(file,&version,sizeof(version));\r
+               if (strcmp(sig,EXTENSION) || (version != ConfigVersion))\r
+               {\r
+                       close(file);\r
+                       goto rcfailed;\r
+               }\r
+               read(file,Scores,sizeof(HighScore) * MaxScores);\r
+               read(file,&sd,sizeof(sd));\r
+               read(file,&sm,sizeof(sm));\r
+               read(file,&ctl,sizeof(ctl));\r
+               read(file,&(KbdDefs[0]),sizeof(KbdDefs[0]));\r
+               read(file,&showscorebox,sizeof(showscorebox));\r
+               read(file,&compatability,sizeof(compatability));\r
+               read(file,&QuietFX,sizeof(QuietFX));\r
+               read(file,&hadAdLib,sizeof(hadAdLib));\r
+               read(file,&jerk,sizeof(jerk));\r
+#ifdef KEEN\r
+               read(file,&oldshooting,sizeof(oldshooting));\r
+               read(file,&firescan,sizeof(firescan));\r
+#endif\r
+               read(file,&GravisGamepad,sizeof(GravisGamepad));\r
+               read(file,&GravisMap,sizeof(GravisMap));\r
+               close(file);\r
+\r
+               HighScoresDirty = false;\r
+               gotit = true;\r
+       }\r
+       else\r
+       {\r
+rcfailed:\r
+               sd = sdm_Off;\r
+               sm = smm_Off;\r
+               ctl = ctrl_Keyboard;\r
+               showscorebox = true;\r
+#ifdef KEEN\r
+               oldshooting = false;\r
+#endif\r
+\r
+               gotit = false;\r
+               HighScoresDirty = true;\r
+       }\r
+\r
+       SD_Default(gotit? (hadAdLib==AdLibPresent) : false, sd,sm);\r
+       IN_Default(gotit,ctl);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//      USL_WriteConfig() - Writes out the current configuration, including the\r
+//              high scores.\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static void\r
+USL_WriteConfig(void)\r
+{\r
+       word    version;\r
+       int             file;\r
+\r
+       version = ConfigVersion;\r
+       file = open("CONFIG."EXTENSION,O_CREAT | O_BINARY | O_WRONLY,\r
+                               S_IREAD | S_IWRITE | S_IFREG);\r
+       if (file != -1)\r
+       {\r
+               write(file,EXTENSION,sizeof(EXTENSION));\r
+               write(file,&version,sizeof(version));\r
+               write(file,Scores,sizeof(HighScore) * MaxScores);\r
+               write(file,&SoundMode,sizeof(SoundMode));\r
+               write(file,&MusicMode,sizeof(MusicMode));\r
+#ifdef CAT3D\r
+               if      // Hack\r
+               (\r
+                       (Controls[0] == ctrl_Joystick1)\r
+               ||      (Controls[0] == ctrl_Joystick2)\r
+               )\r
+                       Controls[0] = ctrl_Keyboard;\r
+#endif\r
+               write(file,&(Controls[0]),sizeof(Controls[0]));\r
+               write(file,&(KbdDefs[0]),sizeof(KbdDefs[0]));\r
+               write(file,&showscorebox,sizeof(showscorebox));\r
+               write(file,&compatability,sizeof(compatability));\r
+               write(file,&QuietFX,sizeof(QuietFX));\r
+               write(file,&AdLibPresent,sizeof(AdLibPresent));\r
+               write(file,&jerk,sizeof(jerk));\r
+#ifdef KEEN\r
+               write(file,&oldshooting,sizeof(oldshooting));\r
+               write(file,&firescan,sizeof(firescan));\r
+#endif\r
+               write(file,&GravisGamepad,sizeof(GravisGamepad));\r
+               write(file,&GravisMap,sizeof(GravisMap));\r
+               close(file);\r
+       }\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//      USL_CheckSavedGames() - Checks to see which saved games are present\r
+//              & valid\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+#ifdef KEEN\r
+void\r
+#else\r
+static void\r
+#endif\r
+USL_CheckSavedGames(void)\r
+{\r
+       boolean         ok;\r
+       char            *filename;\r
+       word            i;\r
+       int                     file;\r
+       SaveGame        *game;\r
+\r
+#ifdef CAT3D\r
+       USL_SaveGame = 0;\r
+       USL_LoadGame = 0;\r
+#endif\r
+\r
+       for (i = 0,game = Games;i < MaxSaveGames;i++,game++)\r
+       {\r
+               filename = USL_GiveSaveName(i);\r
+               ok = false;\r
+               if ((file = open(filename,O_BINARY | O_RDONLY)) != -1)\r
+               {\r
+                       if\r
+                       (\r
+                               (read(file,game,sizeof(*game)) == sizeof(*game))\r
+                       &&      (!strcmp(game->signature,EXTENSION))\r
+                       &&      (game->oldtest == &PrintX)\r
+                       )\r
+                               ok = true;\r
+\r
+                       close(file);\r
+               }\r
+\r
+               if (ok)\r
+                       game->present = true;\r
+               else\r
+               {\r
+                       strcpy(game->signature,EXTENSION);\r
+                       game->present = false;\r
+                       strcpy(game->name,"Empty");\r
+               }\r
+       }\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//      US_Startup() - Starts the User Mgr\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+US_Startup(void)\r
+{\r
+       int     i;\r
+\r
+       if (US_Started)\r
+               return;\r
+\r
+       harderr(USL_HardError); // Install the fatal error handler\r
+\r
+       US_InitRndT(true);              // Initialize the random number generator\r
+\r
+       USL_ReadConfig();               // Read config file\r
+\r
+       for (i = 1;i < _argc;i++)\r
+       {\r
+               switch (US_CheckParm(_argv[i],ParmStrings2))\r
+               {\r
+               case 0:\r
+                       if (grmode == EGAGR)\r
+                               compatability = true;\r
+                       break;\r
+               case 1:\r
+                       compatability = false;\r
+                       break;\r
+               }\r
+       }\r
+\r
+       US_Started = true;\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//      US_Setup() - Does the disk access part of the User Mgr's startup\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+US_Setup(void)\r
+{\r
+#ifndef CAT3D\r
+       USL_SaveGame = 0;\r
+       USL_LoadGame = 0;\r
+#endif\r
+       USL_CheckSavedGames();  // Check which saved games are present\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//      US_Shutdown() - Shuts down the User Mgr\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+US_Shutdown(void)\r
+{\r
+       if (!US_Started)\r
+               return;\r
+\r
+       if (!abortprogram)\r
+               USL_WriteConfig();\r
+\r
+       US_Started = false;\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//      US_CheckParm() - checks to see if a string matches one of a set of\r
+//              strings. The check is case insensitive. The routine returns the\r
+//              index of the string that matched, or -1 if no matches were found\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+int\r
+US_CheckParm(char *parm,char **strings)\r
+{\r
+       char    cp,cs,\r
+                       *p,*s;\r
+       int             i;\r
+\r
+       while (!isalpha(*parm)) // Skip non-alphas\r
+               parm++;\r
+\r
+       for (i = 0;*strings && **strings;i++)\r
+       {\r
+               for (s = *strings++,p = parm,cs = cp = 0;cs == cp;)\r
+               {\r
+                       cs = *s++;\r
+                       if (!cs)\r
+                               return(i);\r
+                       cp = *p++;\r
+\r
+                       if (isupper(cs))\r
+                               cs = tolower(cs);\r
+                       if (isupper(cp))\r
+                               cp = tolower(cp);\r
+               }\r
+       }\r
+       return(-1);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//      US_ParmPresent() - checks if a given string was passed as a command\r
+//              line parameter at startup\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+boolean\r
+US_ParmPresent(char *arg)\r
+{\r
+       int i;\r
+       char *strings[2];\r
+\r
+       strings[0] = arg;\r
+       strings[1] = NULL;\r
+\r
+       for (i=1; i<_argc; i++)\r
+       {\r
+               if (US_CheckParm(_argv[i], strings) != -1)\r
+                       return true;\r
+       }\r
+       return false;\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//      USL_ScreenDraw() - Draws a chunk of the text screen (called only by\r
+//              US_TextScreen())\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static void\r
+USL_ScreenDraw(word x,word y,char *s,byte attr)\r
+{\r
+       byte    far *screen,far *oscreen;\r
+\r
+       screen = MK_FP(0xb800,(x * 2) + (y * 80 * 2));\r
+       oscreen = (&introscn + 7) + ((x - 1) * 2) + (y * 80 * 2) + 1;\r
+       while (*s)\r
+       {\r
+               *screen++ = *s++;\r
+               if (attr != 0xff)\r
+               {\r
+                       *screen++ = (attr & 0x8f) | (*oscreen & 0x70);\r
+                       oscreen += 2;\r
+               }\r
+               else\r
+                       screen++;\r
+       }\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//      USL_ClearTextScreen() - Makes sure the screen is in text mode, clears it,\r
+//              and moves the cursor to the leftmost column of the bottom line\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static void\r
+USL_ClearTextScreen(void)\r
+{\r
+       // Set to 80x25 color text mode\r
+       _AL = 3;                                // Mode 3\r
+       _AH = 0x00;\r
+       geninterrupt(0x10);\r
+\r
+       // Use BIOS to move the cursor to the bottom of the screen\r
+       _AH = 0x0f;\r
+       geninterrupt(0x10);             // Get current video mode into _BH\r
+       _DL = 0;                                // Lefthand side of the screen\r
+       _DH = 24;                               // Bottom row\r
+       _AH = 0x02;\r
+       geninterrupt(0x10);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//      US_TextScreen() - Puts up the startup text screen\r
+//      Note: These are the only User Manager functions that can be safely called\r
+//              before the User Mgr has been started up\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+US_TextScreen(void)\r
+{\r
+       word    i,n;\r
+\r
+       USL_ClearTextScreen();\r
+\r
+       _fmemcpy(MK_FP(0xb800,0),7 + &introscn,80 * 25 * 2);\r
+\r
+       // Check for TED launching here\r
+       for (i = 1;i < _argc;i++)\r
+       {\r
+               n = US_CheckParm(_argv[i],ParmStrings);\r
+               if (n == 0)\r
+               {\r
+                       tedlevelnum = atoi(_argv[i + 1]);\r
+                       if (tedlevelnum >= 0)\r
+                       {\r
+                               tedlevel = true;\r
+                               return;\r
+                       }\r
+                       else\r
+                               break;\r
+               }\r
+               else if (n == 1)\r
+               {\r
+                       NoWait = true;\r
+                       return;\r
+               }\r
+       }\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//      USL_Show() - Changes the appearance of one of the fields on the text\r
+//              screen. Possibly adds a checkmark in front of it and highlights it\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static void\r
+USL_Show(word x,word y,word w,boolean show,boolean hilight)\r
+{\r
+       byte    far *screen,far *oscreen;\r
+\r
+       screen = MK_FP(0xb800,((x - 1) * 2) + (y * 80 * 2));\r
+       oscreen = (&introscn + 7) + ((x - 1) * 2) + (y * 80 * 2) - 1;\r
+       *screen++ = show? 251 : ' ';    // Checkmark char or space\r
+//      *screen = 0x48;\r
+//      *screen = (*oscreen & 0xf0) | 8;\r
+       oscreen += 2;\r
+       if (show && hilight)\r
+       {\r
+               for (w++;w--;screen += 2,oscreen += 2)\r
+                       *screen = (*oscreen & 0xf0) | 0x0f;\r
+       }\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//      USL_ShowMem() - Right justifies a longword in one of the memory fields on\r
+//              the text screen\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static void\r
+USL_ShowMem(word x,word y,long mem)\r
+{\r
+       char    buf[16];\r
+       word    i;\r
+\r
+       for (i = strlen(ltoa(mem,buf,10));i < 5;i++)\r
+               USL_ScreenDraw(x++,y," ",0xff);\r
+       USL_ScreenDraw(x,y,buf,0xff);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//      US_UpdateTextScreen() - Called after the ID libraries are started up.\r
+//              Displays what hardware is present.\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+US_UpdateTextScreen(void)\r
+{\r
+       boolean         b;\r
+       longword        totalmem;\r
+\r
+       // Show video card info\r
+       b = (grmode == CGAGR);\r
+       USL_Show(21,7,4,(videocard >= CGAcard) && (videocard <= VGAcard),b);\r
+       b = (grmode == EGAGR || grmode == VGAGR);\r
+       USL_Show(21,8,7,(videocard >= EGAcard) && (videocard <= VGAcard),b);\r
+//     b = (grmode == VGAGR);\r
+//     USL_Show(21,9,4,videocard == VGAcard,b);\r
+#if GRMODE != CGAGR\r
+       if (compatability)\r
+               USL_ScreenDraw(5,10,"SVGA Compatibility Mode Enabled.",0x4f);\r
+#endif\r
+\r
+       // Show input device info\r
+       USL_Show(60,7,8,true,true);\r
+       USL_Show(60,8,11,JoysPresent[0],true);\r
+       USL_Show(60,9,11,JoysPresent[1],true);\r
+       USL_Show(60,10,5,MousePresent,true);\r
+\r
+       // Show sound hardware info\r
+       USL_Show(21,14,11,true,SoundMode == sdm_PC);\r
+       b = (SoundMode == sdm_AdLib) || (MusicMode == smm_AdLib);\r
+       USL_Show(21,15,14,AdLibPresent,b);\r
+       if (b && AdLibPresent)  // Hack because of two lines\r
+       {\r
+               byte    far *screen,far *oscreen;\r
+               word    x,y,w;\r
+\r
+               x = 21;\r
+               y = 16;\r
+               w = 14;\r
+               screen = MK_FP(0xb800,(x * 2) + (y * 80 * 2) - 1);\r
+               oscreen = (&introscn + 7) + (x * 2) + (y * 80 * 2) - 1;\r
+               oscreen += 2;\r
+               for (w++;w--;screen += 2,oscreen += 2)\r
+                       *screen = (*oscreen & 0xf0) | 0x0f;\r
+       }\r
+\r
+       // Show memory available/used\r
+       USL_ShowMem(63,15,mminfo.mainmem / 1024);\r
+       USL_Show(53,15,23,true,true);\r
+       USL_ShowMem(63,16,mminfo.EMSmem / 1024);\r
+       USL_Show(53,16,23,mminfo.EMSmem? true : false,true);\r
+       USL_ShowMem(63,17,mminfo.XMSmem / 1024);\r
+       USL_Show(53,17,23,mminfo.XMSmem? true : false,true);\r
+       totalmem = mminfo.mainmem + mminfo.EMSmem + mminfo.XMSmem;\r
+       USL_ShowMem(63,18,totalmem / 1024);\r
+       USL_Show(53,18,23,true,true);   // DEBUG\r
+       USL_ScreenDraw(52,18," ",0xff);\r
+\r
+       // Change Initializing... to Loading...\r
+       USL_ScreenDraw(27,22,"  Loading...   ",0x9c);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//      US_FinishTextScreen() - After the main program has finished its initial\r
+//              loading, this routine waits for a keypress and then clears the screen\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+US_FinishTextScreen(void)\r
+{\r
+static  byte    colors[] = {4,6,13,15,15,15,15,15,15};\r
+               boolean up;\r
+               int             i,c;\r
+\r
+       // Change Loading... to Press a Key\r
+\r
+       if (!(tedlevel || NoWait))\r
+       {\r
+               IN_ClearKeysDown();\r
+               for (i = 0,up = true;!IN_UserInput(4,true);)\r
+               {\r
+                       c = colors[i];\r
+                       if (up)\r
+                       {\r
+                               if (++i == 9)\r
+                                       i = 8,up = false;\r
+                       }\r
+                       else\r
+                       {\r
+                               if (--i < 0)\r
+                                       i = 1,up = true;\r
+                       }\r
+\r
+                       USL_ScreenDraw(29,22," Ready - Press a Key     ",0x00 + c);\r
+               }\r
+       }\r
+       else\r
+               USL_ScreenDraw(29,22," Ready - Press a Key     ",0x9a);\r
+\r
+       IN_ClearKeysDown();\r
+\r
+       USL_ClearTextScreen();\r
+}\r
+\r
+//      Window/Printing routines\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//      US_SetPrintRoutines() - Sets the routines used to measure and print\r
+//              from within the User Mgr. Primarily provided to allow switching\r
+//              between masked and non-masked fonts\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+US_SetPrintRoutines(void (*measure)(char far *,word *,word *),void (*print)(char far *))\r
+{\r
+       USL_MeasureString = measure;\r
+       USL_DrawString = print;\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//      US_Print() - Prints a string in the current window. Newlines are\r
+//              supported.\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+US_Print(char *s)\r
+{\r
+       char    c,*se;\r
+       word    w,h;\r
+\r
+       while (*s)\r
+       {\r
+               se = s;\r
+               while ((c = *se) && (c != '\n'))\r
+                       se++;\r
+               *se = '\0';\r
+\r
+               USL_MeasureString(s,&w,&h);\r
+               px = PrintX;\r
+               py = PrintY;\r
+               USL_DrawString(s);\r
+\r
+               s = se;\r
+               if (c)\r
+               {\r
+                       *se = c;\r
+                       s++;\r
+\r
+                       PrintX = WindowX;\r
+                       PrintY += h;\r
+               }\r
+               else\r
+                       PrintX += w;\r
+       }\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//      US_PrintUnsigned() - Prints an unsigned long\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+US_PrintUnsigned(longword n)\r
+{\r
+       char    buffer[32];\r
+\r
+       US_Print(ultoa(n,buffer,10));\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//      US_PrintSigned() - Prints a signed long\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+US_PrintSigned(long n)\r
+{\r
+       char    buffer[32];\r
+\r
+       US_Print(ltoa(n,buffer,10));\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//      USL_PrintInCenter() - Prints a string in the center of the given rect\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+USL_PrintInCenter(char *s,Rect r)\r
+{\r
+       word    w,h,\r
+                       rw,rh;\r
+\r
+       USL_MeasureString(s,&w,&h);\r
+       rw = r.lr.x - r.ul.x;\r
+       rh = r.lr.y - r.ul.y;\r
+\r
+       px = r.ul.x + ((rw - w) / 2);\r
+       py = r.ul.y + ((rh - h) / 2);\r
+       USL_DrawString(s);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//      US_PrintCentered() - Prints a string centered in the current window.\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+US_PrintCentered(char *s)\r
+{\r
+       Rect    r;\r
+\r
+       r.ul.x = WindowX;\r
+       r.ul.y = WindowY;\r
+       r.lr.x = r.ul.x + WindowW;\r
+       r.lr.y = r.ul.y + WindowH;\r
+\r
+       USL_PrintInCenter(s,r);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//      US_CPrintLine() - Prints a string centered on the current line and\r
+//              advances to the next line. Newlines are not supported.\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+US_CPrintLine(char *s)\r
+{\r
+       word    w,h;\r
+\r
+       USL_MeasureString(s,&w,&h);\r
+\r
+       if (w > WindowW)\r
+               Quit("US_CPrintLine() - String exceeds width");\r
+       px = WindowX + ((WindowW - w) / 2);\r
+       py = PrintY;\r
+       USL_DrawString(s);\r
+       PrintY += h;\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//      US_CPrint() - Prints a string in the current window. Newlines are\r
+//              supported.\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+US_CPrint(char *s)\r
+{\r
+       char    c,*se;\r
+\r
+       while (*s)\r
+       {\r
+               se = s;\r
+               while ((c = *se) && (c != '\n'))\r
+                       se++;\r
+               *se = '\0';\r
+\r
+               US_CPrintLine(s);\r
+\r
+               s = se;\r
+               if (c)\r
+               {\r
+                       *se = c;\r
+                       s++;\r
+               }\r
+       }\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//      US_ClearWindow() - Clears the current window to white and homes the\r
+//              cursor\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+US_ClearWindow(void)\r
+{\r
+       VWB_Bar(WindowX,WindowY,WindowW,WindowH,WHITE);\r
+       PrintX = WindowX;\r
+       PrintY = WindowY;\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//      US_DrawWindow() - Draws a frame and sets the current window parms\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+US_DrawWindow(word x,word y,word w,word h)\r
+{\r
+       word    i,\r
+                       sx,sy,sw,sh;\r
+\r
+       WindowX = x * 8;\r
+       WindowY = y * 8;\r
+       WindowW = w * 8;\r
+       WindowH = h * 8;\r
+\r
+       PrintX = WindowX;\r
+       PrintY = WindowY;\r
+\r
+       sx = (x - 1) * 8;\r
+       sy = (y - 1) * 8;\r
+       sw = (w + 1) * 8;\r
+       sh = (h + 1) * 8;\r
+\r
+       US_ClearWindow();\r
+\r
+       VWB_DrawTile8M(sx,sy,0),VWB_DrawTile8M(sx,sy + sh,6);\r
+       for (i = sx + 8;i <= sx + sw - 8;i += 8)\r
+               VWB_DrawTile8M(i,sy,1),VWB_DrawTile8M(i,sy + sh,7);\r
+       VWB_DrawTile8M(i,sy,2),VWB_DrawTile8M(i,sy + sh,8);\r
+\r
+       for (i = sy + 8;i <= sy + sh - 8;i += 8)\r
+               VWB_DrawTile8M(sx,i,3),VWB_DrawTile8M(sx + sw,i,5);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//      US_CenterWindow() - Generates a window of a given width & height in the\r
+//              middle of the screen\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+US_CenterWindow(word w,word h)\r
+{\r
+       US_DrawWindow(((MaxX / 8) - w) / 2,((MaxY / 8) - h) / 2,w,h);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//      US_CenterSaveWindow() - Generates a window of a given width & height in\r
+//              the middle of the screen, saving the background\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+US_CenterSaveWindow(word w,word h,memptr *save)\r
+{\r
+       word    x,y,\r
+                       screen;\r
+\r
+       x = ((MaxX / 8) - w) / 2;\r
+       y = ((MaxY / 8) - h) / 2;\r
+       MM_GetPtr(save,(w * h) * CHARWIDTH);\r
+       screen = bufferofs + panadjust + ylookup[y] + (x * CHARWIDTH);\r
+       VW_ScreenToMem(screen,*save,w * CHARWIDTH,h);\r
+       US_DrawWindow(((MaxX / 8) - w) / 2,((MaxY / 8) - h) / 2,w,h);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//      US_RestoreSaveWindow() - Restores the background of the size of the\r
+//              current window from the memory specified by save\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+US_RestoreSaveWindow(memptr *save)\r
+{\r
+       word    screen;\r
+\r
+       screen = bufferofs + panadjust + ylookup[WindowY] + (WindowX * CHARWIDTH);\r
+       VW_MemToScreen(*save,screen,WindowW * CHARWIDTH,WindowH);\r
+       MM_FreePtr(save);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//      US_SaveWindow() - Saves the current window parms into a record for\r
+//              later restoration\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+US_SaveWindow(WindowRec *win)\r
+{\r
+       win->x = WindowX;\r
+       win->y = WindowY;\r
+       win->w = WindowW;\r
+       win->h = WindowH;\r
+\r
+       win->px = PrintX;\r
+       win->py = PrintY;\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//      US_RestoreWindow() - Sets the current window parms to those held in the\r
+//              record\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+US_RestoreWindow(WindowRec *win)\r
+{\r
+       WindowX = win->x;\r
+       WindowY = win->y;\r
+       WindowW = win->w;\r
+       WindowH = win->h;\r
+\r
+       PrintX = win->px;\r
+       PrintY = win->py;\r
+}\r
+\r
+//      Cursor routines\r
+\r
+#if 0\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//      US_StartCursor() - Sets up the cursor for User Mgr use\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+US_StartCursor(void)\r
+{\r
+       CursorInfo      info;\r
+\r
+       VW_SetCursor(CURSORARROWSPR);\r
+       CursorX = MaxX / 2;\r
+       CursorY = MaxY / 2;\r
+       VW_MoveCursor(CursorX,CursorY);\r
+       VW_ShowCursor();\r
+\r
+       IN_ReadCursor(&info);   // Dispose of any accumulated movement\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//      US_ShutCursor() - Cleans up after US_StartCursor()\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+US_ShutCursor(void)\r
+{\r
+       VW_HideCursor();\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//      US_UpdateCursor() - Gets the new cursor position & button states from\r
+//              the Input Mgr and tells the View Mgr where the cursor is\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+boolean\r
+US_UpdateCursor(void)\r
+{\r
+       CursorInfo      info;\r
+\r
+       IN_ReadCursor(&info);\r
+       if (info.x || info.y || CursorBad)\r
+       {\r
+               CursorX += info.x;\r
+               if (CursorX >= MaxX)\r
+                       CursorX = MaxX - 1;\r
+               else if (CursorX < 0)\r
+                       CursorX = 0;\r
+\r
+               CursorY += info.y;\r
+               if (CursorY >= MaxY)\r
+                       CursorY = MaxY - 1;\r
+               else if (CursorY < 0)\r
+                       CursorY = 0;\r
+\r
+               VW_MoveCursor(CursorX,CursorY);\r
+               CursorBad = false;\r
+       }\r
+       Button0 = info.button0;\r
+       Button1 = info.button1;\r
+       return(Button0 || Button1);\r
+}\r
+#endif\r
+\r
+//      Input routines\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//      USL_XORICursor() - XORs the I-bar text cursor. Used by US_LineInput()\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static void\r
+USL_XORICursor(int x,int y,char *s,word cursor)\r
+{\r
+       char    buf[MaxString];\r
+       word    w,h;\r
+\r
+       strcpy(buf,s);\r
+       buf[cursor] = '\0';\r
+       USL_MeasureString(buf,&w,&h);\r
+\r
+       px = x + w - 1;\r
+       py = y;\r
+       USL_DrawString("\x80");\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//      US_LineInput() - Gets a line of user input at (x,y), the string defaults\r
+//              to whatever is pointed at by def. Input is restricted to maxchars\r
+//              chars or maxwidth pixels wide. If the user hits escape (and escok is\r
+//              true), nothing is copied into buf, and false is returned. If the\r
+//              user hits return, the current string is copied into buf, and true is\r
+//              returned\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+boolean\r
+US_LineInput(int x,int y,char *buf,char *def,boolean escok,\r
+                               int maxchars,int maxwidth)\r
+{\r
+       boolean         redraw,\r
+                               cursorvis,cursormoved,\r
+                               done,result;\r
+       ScanCode        sc;\r
+       char            c,\r
+                               s[MaxString],olds[MaxString];\r
+       word            i,\r
+                               cursor,\r
+                               w,h,\r
+                               len;\r
+       longword        lasttime;\r
+\r
+       VW_HideCursor();\r
+\r
+       if (def)\r
+               strcpy(s,def);\r
+       else\r
+               *s = '\0';\r
+       *olds = '\0';\r
+       cursor = strlen(s);\r
+       cursormoved = redraw = true;\r
+\r
+       cursorvis = done = false;\r
+       lasttime = TimeCount;\r
+       LastASCII = key_None;\r
+       LastScan = sc_None;\r
+\r
+       while (!done)\r
+       {\r
+               if (cursorvis)\r
+                       USL_XORICursor(x,y,s,cursor);\r
+\r
+       asm     pushf\r
+       asm     cli\r
+\r
+               sc = LastScan;\r
+               LastScan = sc_None;\r
+               c = LastASCII;\r
+               LastASCII = key_None;\r
+\r
+       asm     popf\r
+\r
+               switch (sc)\r
+               {\r
+               case sc_LeftArrow:\r
+                       if (cursor)\r
+                               cursor--;\r
+                       c = key_None;\r
+                       cursormoved = true;\r
+                       break;\r
+               case sc_RightArrow:\r
+                       if (s[cursor])\r
+                               cursor++;\r
+                       c = key_None;\r
+                       cursormoved = true;\r
+                       break;\r
+               case sc_Home:\r
+                       cursor = 0;\r
+                       c = key_None;\r
+                       cursormoved = true;\r
+                       break;\r
+               case sc_End:\r
+                       cursor = strlen(s);\r
+                       c = key_None;\r
+                       cursormoved = true;\r
+                       break;\r
+\r
+               case sc_Return:\r
+                       strcpy(buf,s);\r
+                       done = true;\r
+                       result = true;\r
+                       c = key_None;\r
+                       break;\r
+               case sc_Escape:\r
+                       if (escok)\r
+                       {\r
+                               done = true;\r
+                               result = false;\r
+                       }\r
+                       c = key_None;\r
+                       break;\r
+\r
+               case sc_BackSpace:\r
+                       if (cursor)\r
+                       {\r
+                               strcpy(s + cursor - 1,s + cursor);\r
+                               cursor--;\r
+                               redraw = true;\r
+                       }\r
+                       c = key_None;\r
+                       cursormoved = true;\r
+                       break;\r
+               case sc_Delete:\r
+                       if (s[cursor])\r
+                       {\r
+                               strcpy(s + cursor,s + cursor + 1);\r
+                               redraw = true;\r
+                       }\r
+                       c = key_None;\r
+                       cursormoved = true;\r
+                       break;\r
+\r
+               case 0x4c:      // Keypad 5\r
+               case sc_UpArrow:\r
+               case sc_DownArrow:\r
+               case sc_PgUp:\r
+               case sc_PgDn:\r
+               case sc_Insert:\r
+                       c = key_None;\r
+                       break;\r
+               }\r
+\r
+               if (c)\r
+               {\r
+                       len = strlen(s);\r
+                       USL_MeasureString(s,&w,&h);\r
+\r
+                       if\r
+                       (\r
+                               isprint(c)\r
+                       &&      (len < MaxString - 1)\r
+                       &&      ((!maxchars) || (len < maxchars))\r
+                       &&      ((!maxwidth) || (w < maxwidth))\r
+                       )\r
+                       {\r
+                               for (i = len + 1;i > cursor;i--)\r
+                                       s[i] = s[i - 1];\r
+                               s[cursor++] = c;\r
+                               redraw = true;\r
+                       }\r
+               }\r
+\r
+               if (redraw)\r
+               {\r
+                       px = x;\r
+                       py = y;\r
+                       USL_DrawString(olds);\r
+                       strcpy(olds,s);\r
+\r
+                       px = x;\r
+                       py = y;\r
+                       USL_DrawString(s);\r
+\r
+                       redraw = false;\r
+               }\r
+\r
+               if (cursormoved)\r
+               {\r
+                       cursorvis = false;\r
+                       lasttime = TimeCount - TickBase;\r
+\r
+                       cursormoved = false;\r
+               }\r
+               if (TimeCount - lasttime > TickBase / 2)\r
+               {\r
+                       lasttime = TimeCount;\r
+\r
+                       cursorvis ^= true;\r
+               }\r
+               if (cursorvis)\r
+                       USL_XORICursor(x,y,s,cursor);\r
+\r
+               VW_UpdateScreen();\r
+       }\r
+\r
+       if (cursorvis)\r
+               USL_XORICursor(x,y,s,cursor);\r
+       if (!result)\r
+       {\r
+               px = x;\r
+               py = y;\r
+               USL_DrawString(olds);\r
+       }\r
+       VW_ShowCursor();\r
+       VW_UpdateScreen();\r
+\r
+       IN_ClearKeysDown();\r
+       return(result);\r
+}\r
diff --git a/16/keen456/KEEN4-6/ID_US_2.C b/16/keen456/KEEN4-6/ID_US_2.C
new file mode 100755 (executable)
index 0000000..e194861
--- /dev/null
@@ -0,0 +1,2260 @@
+/* Reconstructed Commander Keen 4-6 Source Code\r
+ * Copyright (C) 2021 K1n9_Duk3\r
+ *\r
+ * This file is primarily based on:\r
+ * Catacomb 3-D Source Code\r
+ * Copyright (C) 1993-2014 Flat Rock Software\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License along\r
+ * with this program; if not, write to the Free Software Foundation, Inc.,\r
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+ */\r
+\r
+//\r
+//      ID Engine\r
+//      ID_US.c - User Manager - User interface\r
+//      v1.1d1\r
+//      By Jason Blochowiak\r
+//      Hacked up for Catacomb 3D\r
+//\r
+\r
+#include "ID_HEADS.H"\r
+#pragma hdrstop\r
+\r
+#pragma warn    -pia\r
+\r
+//      Special imports\r
+extern  boolean         showscorebox;\r
+#ifdef  KEEN\r
+extern boolean         jerk;\r
+extern  boolean         oldshooting;\r
+extern  ScanCode        firescan;\r
+void   USL_CheckSavedGames(void);\r
+#else\r
+               ScanCode        firescan;\r
+#endif\r
+\r
+//      Global variables\r
+               boolean         ingame,abortgame,loadedgame;\r
+               GameDiff        restartgame = gd_Continue;\r
+\r
+//      Internal variables\r
+static  boolean         GameIsDirty,\r
+                                       QuitToDos,\r
+                                       CtlPanelDone;\r
+\r
+#ifdef KEEN6\r
+       int             listindex = -1;\r
+       boolean         checkpassed;\r
+#endif\r
+\r
+//      Forward reference prototypes\r
+static void     USL_SetupCard(void);\r
+\r
+//      Control panel data\r
+\r
+#define CtlPanelSX      74\r
+#define CtlPanelSY      48\r
+#define CtlPanelEX      234\r
+#define CtlPanelEY      150\r
+#define CtlPanelW       (CtlPanelEX - CtlPanelSX)\r
+#define CtlPanelH       (CtlPanelEY - CtlPanelSY)\r
+\r
+#ifdef KEEN\r
+\r
+#define TileBase        92\r
+\r
+#if GRMODE == CGAGR\r
+\r
+#define BackColor               0\r
+#define HiliteColor             (BackColor ^ 3)\r
+#define NohiliteColor   (BackColor ^ 2)\r
+\r
+#else\r
+\r
+#define BackColor               8\r
+#define HiliteColor             (BackColor ^ 10)\r
+#define NohiliteColor   (BackColor ^ 2)\r
+\r
+#endif // if GRMODE == CGAGR ... else ...\r
+\r
+#else  // ifdef KEEN\r
+\r
+#define TileBase        92\r
+\r
+// DEBUG - CGA\r
+#define BackColor               0\r
+#define HiliteColor             (BackColor ^ 12)\r
+#define NohiliteColor   (BackColor ^ 4)\r
+\r
+#endif\r
+\r
+typedef enum\r
+               {\r
+                       uc_None,\r
+                       uc_Return,\r
+                       uc_Abort,\r
+                       uc_Quit,\r
+                       uc_Loaded,\r
+                       uc_SEasy,\r
+                       uc_SNormal,\r
+                       uc_SHard,\r
+               } UComm;\r
+typedef enum\r
+               {\r
+                       uii_Bad,\r
+                       uii_Button,uii_RadioButton,uii_Folder\r
+               } UIType;\r
+typedef enum\r
+               {\r
+                       ui_Normal = 0,\r
+                       ui_Pushed = 1,\r
+                       ui_Selected = 2,\r
+                       ui_Disabled = 4,\r
+                       ui_Separated = 8\r
+               } UIFlags;\r
+#define UISelectFlags (ui_Pushed | ui_Selected | ui_Disabled)\r
+\r
+typedef enum\r
+               {\r
+                       uic_SetupCard,uic_DrawCard,uic_TouchupCard,\r
+                       uic_DrawIcon,uic_Draw,uic_Hit\r
+               } UserCall;\r
+\r
+typedef struct  UserItem\r
+               {\r
+                               UIType                  type;\r
+                               UIFlags                 flags;\r
+                               ScanCode                hotkey;\r
+                               char                    *text;\r
+                               UComm                   comm;\r
+                               void                    far *child;     // Should be (UserItemGroup *)\r
+\r
+                               word                    x,y;\r
+               } UserItem;\r
+typedef struct  UserItemGroup\r
+               {\r
+                               word                    x,y;\r
+                               graphicnums             title;\r
+                               ScanCode                hotkey;\r
+                               UserItem                far *items;\r
+                               boolean                 (*custom)(UserCall,struct UserItem far *);      // Custom routine\r
+\r
+                               word                    cursor;\r
+               struct  UserItemGroup   far *parent;\r
+               } UserItemGroup;\r
+\r
+static  char            *BottomS1,*BottomS2,*BottomS3;\r
+static  UComm           Communication;\r
+static  ScanCode        *KeyMaps[] =\r
+                                       {\r
+                                               &KbdDefs[0].button0,\r
+                                               &KbdDefs[0].button1,\r
+                                               &firescan,\r
+                                               &KbdDefs[0].upleft,\r
+                                               &KbdDefs[0].up,\r
+                                               &KbdDefs[0].upright,\r
+                                               &KbdDefs[0].right,\r
+                                               &KbdDefs[0].downright,\r
+                                               &KbdDefs[0].down,\r
+                                               &KbdDefs[0].downleft,\r
+                                               &KbdDefs[0].left\r
+                                       };\r
+\r
+// Custom routine prototypes\r
+static  boolean USL_ConfigCustom(UserCall call,struct UserItem far *item),\r
+                               USL_KeyCustom(UserCall call,struct UserItem far *item),\r
+                               USL_KeySCustom(UserCall call,struct UserItem far *item),\r
+                               USL_Joy1Custom(UserCall call,struct UserItem far *item),\r
+                               USL_Joy2Custom(UserCall call,struct UserItem far *item),\r
+                               USL_JoyGCustom(UserCall call,struct UserItem far *item),\r
+                               USL_LoadCustom(UserCall call,struct UserItem far *item),\r
+                               USL_SaveCustom(UserCall call,struct UserItem far *item),\r
+                               USL_ScoreCustom(UserCall call,struct UserItem far *item),\r
+                               USL_CompCustom(UserCall call,struct UserItem far *item),\r
+                               USL_SmoothCustom(UserCall call,struct UserItem far *item),\r
+#ifdef KEEN\r
+                               USL_TwoCustom(UserCall call,struct UserItem far *item),\r
+#endif\r
+                               USL_PongCustom(UserCall call,struct UserItem far *item);\r
+\r
+#define DefButton(key,text)                             uii_Button,ui_Normal,key,text\r
+#define DefRButton(key,text)                    uii_RadioButton,ui_Normal,key,text\r
+#define DefFolder(key,text,child)               uii_Folder,ui_Normal,key,text,uc_None,child\r
+#define CustomGroup(title,key,custom)   0,0,title,key,0,custom\r
+       UserItem far holder[] =\r
+       {\r
+               {DefButton(sc_None,"DEBUG")},\r
+               {uii_Bad}\r
+       };\r
+       UserItemGroup   far holdergroup = {0,0,CP_MAINMENUPIC,sc_None,holder};\r
+\r
+       // Sound menu\r
+       UserItem far soundi[] =\r
+       {\r
+               {DefRButton(sc_N,"NO SOUND EFFECTS")},\r
+               {DefRButton(sc_P,"PC SPEAKER")},\r
+               {DefRButton(sc_A,"ADLIB/SOUNDBLASTER")},\r
+               {DefRButton(sc_Q,"QUIET ADLIB/SOUNDBLASTER")},\r
+               {uii_Bad}\r
+       };\r
+       UserItemGroup   far soundgroup = {8,0,CP_SOUNDMENUPIC,sc_None,soundi};\r
+\r
+       // Music menu\r
+       UserItem far musici[] =\r
+       {\r
+               {DefRButton(sc_N,"NO MUSIC")},\r
+               {DefRButton(sc_A,"ADLIB/SOUNDBLASTER")},\r
+               {uii_Bad}\r
+       };\r
+       UserItemGroup   far musicgroup = {8,0,CP_MUSICMENUPIC,sc_None,musici};\r
+\r
+       // New game menu\r
+       UserItem far newgamei[] =\r
+       {\r
+               {DefButton(sc_E,"BEGIN EASY GAME"),uc_SEasy},\r
+               {DefButton(sc_N,"BEGIN NORMAL GAME"),uc_SNormal},\r
+               {DefButton(sc_H,"BEGIN HARD GAME"),uc_SHard},\r
+               {uii_Bad}\r
+       };\r
+       UserItemGroup   far newgamegroup = {8,0,CP_NEWGAMEMENUPIC,sc_None,newgamei,0,1};\r
+\r
+       // Load/Save game menu\r
+       UserItem far loadsavegamei[] =\r
+       {\r
+#ifdef CAT3D\r
+               {uii_Button,ui_Normal,sc_None},\r
+               {uii_Button,ui_Normal,sc_None},\r
+               {uii_Button,ui_Normal,sc_None},\r
+               {uii_Button,ui_Normal,sc_None},\r
+               {uii_Button,ui_Normal,sc_None},\r
+               {uii_Button,ui_Normal,sc_None},\r
+#else\r
+               {uii_Button,ui_Normal,sc_1},\r
+               {uii_Button,ui_Normal,sc_2},\r
+               {uii_Button,ui_Normal,sc_3},\r
+               {uii_Button,ui_Normal,sc_4},\r
+               {uii_Button,ui_Normal,sc_5},\r
+               {uii_Button,ui_Normal,sc_6},\r
+#endif\r
+               {uii_Bad}\r
+       };\r
+       UserItemGroup   far loadgamegroup = {4,3,CP_LOADMENUPIC,sc_None,loadsavegamei,USL_LoadCustom};\r
+       UserItemGroup   far savegamegroup = {4,3,CP_SAVEMENUPIC,sc_None,loadsavegamei,USL_SaveCustom};\r
+\r
+       // Options menu\r
+       UserItemGroup   far scoregroup = {0,0,0,sc_None,0,USL_ScoreCustom};\r
+#ifdef KEEN\r
+       UserItemGroup   far twogroup = {0,0,0,sc_None,0,USL_TwoCustom};\r
+#endif\r
+#if GRMODE != CGAGR\r
+       UserItemGroup   far smoothgroup = {0,0,0,sc_None,0,USL_SmoothCustom};\r
+       UserItemGroup   far compgroup = {0,0,0,sc_None,0,USL_CompCustom};\r
+#endif\r
+\r
+       UserItem far optionsi[] =\r
+       {\r
+               {DefFolder(sc_S,"",&scoregroup)},\r
+#ifdef KEEN\r
+               {DefFolder(sc_T,"",&twogroup)},\r
+#endif\r
+#if GRMODE != CGAGR\r
+               {DefFolder(sc_M,"",&smoothgroup)},\r
+               {DefFolder(sc_C,"",&compgroup)},\r
+#endif\r
+               {uii_Bad}\r
+       };\r
+       UserItemGroup   far optionsgroup = {8,0,CP_OPTIONSMENUPIC,sc_None,optionsi};\r
+\r
+       // Keyboard menu\r
+       UserItem far keyi[] =\r
+       {\r
+               {DefButton(sc_None,"UP & LEFT")},\r
+               {DefButton(sc_None,"UP")},\r
+               {DefButton(sc_None,"UP & RIGHT")},\r
+               {DefButton(sc_None,"RIGHT")},\r
+               {DefButton(sc_None,"DOWN & RIGHT")},\r
+               {DefButton(sc_None,"DOWN")},\r
+               {DefButton(sc_None,"DOWN & LEFT")},\r
+               {DefButton(sc_None,"LEFT")},\r
+               {uii_Bad}\r
+       };\r
+       UserItemGroup   far keygroup = {0,0,CP_KEYMOVEMENTPIC,sc_None,keyi,USL_KeyCustom};\r
+       UserItem far keybi[] =\r
+       {\r
+#ifdef  KEEN\r
+               {DefButton(sc_J,"JUMP")},\r
+               {DefButton(sc_P,"POGO")},\r
+               {DefButton(sc_F,"FIRE")},\r
+#endif\r
+#ifdef  CAT3D\r
+               {DefButton(sc_J,"FIRE")},\r
+               {DefButton(sc_P,"STRAFE")},\r
+#endif\r
+#ifdef  CPD\r
+               {DefButton(sc_J,"SHOOT")},\r
+               {DefButton(sc_P,"BOMB")},\r
+#endif\r
+               {uii_Bad}\r
+       };\r
+       UserItemGroup   far keybgroup = {0,0,CP_KEYBUTTONPIC,sc_None,keybi,USL_KeyCustom};\r
+       UserItem far keysi[] =\r
+       {\r
+               {DefFolder(sc_M,"MOVEMENT",&keygroup)},\r
+               {DefFolder(sc_B,"BUTTONS",&keybgroup)},\r
+               {uii_Bad}\r
+       };\r
+       UserItemGroup   far keysgroup = {8,0,CP_KEYBOARDMENUPIC,sc_None,keysi,USL_KeySCustom};\r
+\r
+       // Joystick #1 & #2\r
+       UserItemGroup   far joy1group = {CustomGroup(CP_JOYSTICKMENUPIC,sc_None,USL_Joy1Custom)};\r
+       UserItemGroup   far joy2group = {CustomGroup(CP_JOYSTICKMENUPIC,sc_None,USL_Joy2Custom)};\r
+       UserItemGroup   far gravisgroup = {CustomGroup(CP_JOYSTICKMENUPIC,sc_None,USL_JoyGCustom)};\r
+\r
+       // Config menu\r
+       UserItem far configi[] =\r
+       {\r
+               {DefFolder(sc_S,"SOUND",&soundgroup)},\r
+               {DefFolder(sc_M,"MUSIC",&musicgroup)},\r
+#ifndef CAT3D\r
+               {DefFolder(sc_O,"OPTIONS",&optionsgroup)},\r
+#endif\r
+               {uii_Folder,ui_Separated,sc_K,"USE KEYBOARD",uc_None,&keysgroup},\r
+               {DefFolder(sc_1,"USE JOYSTICK #1",&joy1group)},\r
+               {DefFolder(sc_2,"USE JOYSTICK #2",&joy2group)},\r
+               {DefFolder(sc_G,"",&gravisgroup)},\r
+               {uii_Bad}\r
+       };\r
+#ifdef CAT3D\r
+       UserItemGroup   far configgroup = {8,0,CP_CONFIGMENUPIC,sc_None,configi,USL_ConfigCustom};\r
+#else\r
+       UserItemGroup   far configgroup = {0,0,CP_CONFIGMENUPIC,sc_None,configi,USL_ConfigCustom};\r
+#endif\r
+\r
+       // Main menu\r
+       UserItemGroup   far ponggroup = {0,0,0,sc_None,0,USL_PongCustom};\r
+       UserItem far rooti[] =\r
+       {\r
+               {DefFolder(sc_N,"NEW GAME",&newgamegroup)},\r
+               {DefFolder(sc_L,"LOAD GAME",&loadgamegroup)},\r
+               {DefFolder(sc_S,"SAVE GAME",&savegamegroup)},\r
+               {DefFolder(sc_C,"CONFIGURE",&configgroup)},\r
+               {DefButton(sc_R,nil),uc_Return},        // Return to Game/Demo\r
+               {DefButton(sc_E,"END GAME"),uc_Abort},\r
+#ifdef KEEN\r
+               {DefFolder(sc_P,"PADDLE WAR",&ponggroup)},\r
+#elif defined CAT3D\r
+               {DefFolder(sc_B,"SKULL 'N' BONES",&ponggroup)},\r
+#endif\r
+               {DefButton(sc_Q,"QUIT"),uc_Quit},\r
+               {uii_Bad}\r
+       };\r
+       UserItemGroup   far rootgroup = {32,4,CP_MAINMENUPIC,sc_None,rooti};\r
+#undef  DefButton\r
+#undef  DefFolder\r
+\r
+#define MaxCards        7\r
+       word                    cstackptr;\r
+       UserItemGroup   far *cardstack[MaxCards],\r
+                                       far *topcard;\r
+\r
+//      Card stack code\r
+static void\r
+USL_SetupStack(void)\r
+{\r
+       cstackptr = 0;\r
+       cardstack[0] = topcard = &rootgroup;\r
+}\r
+\r
+static void\r
+USL_PopCard(void)\r
+{\r
+       if (!cstackptr)\r
+               return;\r
+\r
+       topcard = cardstack[--cstackptr];\r
+}\r
+\r
+static void\r
+USL_PushCard(UserItemGroup far *card)\r
+{\r
+       if (cstackptr == MaxCards - 1)\r
+               return;\r
+\r
+       topcard = cardstack[++cstackptr] = card;\r
+}\r
+\r
+static void\r
+USL_DrawItemIcon(UserItem far *item)\r
+{\r
+       word    flags,tile;\r
+\r
+       if (topcard->custom && topcard->custom(uic_DrawIcon,item))\r
+               return;\r
+\r
+       flags = item->flags;\r
+       if (flags & ui_Disabled)\r
+               tile = TileBase + ((flags & ui_Selected)? 5 : 4);\r
+       else if ((item->type == uii_RadioButton) && (!(flags & ui_Pushed)))\r
+               tile = TileBase + ((flags & ui_Selected)? 3 : 2);\r
+       else\r
+               tile = TileBase + ((flags & ui_Selected)? 1 : 0);\r
+       VWB_DrawTile8(item->x,item->y,tile);\r
+}\r
+\r
+static void\r
+USL_DrawItem(UserItem far *item)\r
+{\r
+       if (topcard->custom && topcard->custom(uic_Draw,item))\r
+               return;\r
+\r
+       VWB_Bar(CtlPanelSX + 1,item->y,\r
+                       CtlPanelEX - CtlPanelSX - 1,8,BackColor);       // Clear out background\r
+       USL_DrawItemIcon(item);\r
+       if ((item->flags & ui_Selected) && !(item->flags & ui_Disabled))\r
+               fontcolor = HiliteColor;\r
+       else\r
+               fontcolor = NohiliteColor;\r
+       px = item->x + 8;\r
+       py = item->y + 1;\r
+       USL_DrawString(item->text);\r
+       fontcolor = F_BLACK;\r
+}\r
+\r
+#ifdef KEEN\r
+#if GRMODE == CGAGR\r
+#define MyLine(y)       VWB_Hlin(CtlPanelSX + 3,CtlPanelEX - 3,y,3);\r
+#else\r
+#define MyLine(y)       VWB_Hlin(CtlPanelSX + 3,CtlPanelEX - 3,y,10);\r
+#endif\r
+#else\r
+#define MyLine(y)       VWB_Hlin(CtlPanelSX + 3,CtlPanelEX - 3,y,12);\r
+#endif\r
+\r
+static void\r
+USL_DrawBottom(void)\r
+{\r
+       word    w,h;\r
+\r
+       fontcolor = NohiliteColor;\r
+\r
+       px = CtlPanelSX + 4;\r
+       py = CtlPanelEY - 15;\r
+       USL_DrawString(BottomS1);\r
+\r
+       USL_MeasureString(BottomS2,&w,&h);\r
+       px = CtlPanelEX - 4 - w;\r
+       USL_DrawString(BottomS2);\r
+\r
+       USL_MeasureString(BottomS3,&w,&h);\r
+       px = CtlPanelSX + ((CtlPanelEX - CtlPanelSX - w) / 2);\r
+       py += h + 1;\r
+       USL_DrawString(BottomS3);\r
+\r
+       fontcolor = F_WHITE;\r
+       MyLine(CtlPanelEY - 17);\r
+}\r
+\r
+static void\r
+USL_DrawCtlPanelContents(void)\r
+{\r
+       int                             x,y;\r
+       UserItem                far *item;\r
+\r
+       if (topcard->custom && topcard->custom(uic_DrawCard,nil))\r
+               return;\r
+\r
+       if (topcard->title)\r
+       {\r
+               // Draw the title\r
+               MyLine(CtlPanelSY + 7);\r
+               VWB_DrawPic(CtlPanelSX + 6,CtlPanelSY,topcard->title);\r
+       }\r
+\r
+       USL_DrawBottom();\r
+\r
+       if (!topcard->items)\r
+               return;\r
+\r
+       x = topcard->x + CtlPanelSX;\r
+       if (x % 8)\r
+               x += 8 - (x % 8);\r
+       y = topcard->y + CtlPanelSY + 12;\r
+       for (item = topcard->items;item->type != uii_Bad;item++)\r
+       {\r
+               if (item->flags & ui_Separated)\r
+                       y += 8;\r
+\r
+               item->x = x;\r
+               item->y = y;\r
+               USL_DrawItem(item);\r
+               y += 8;\r
+       }\r
+       if (topcard->custom)\r
+               topcard->custom(uic_TouchupCard,nil);\r
+}\r
+\r
+static void\r
+USL_DrawCtlPanel(void)\r
+{\r
+       if (topcard->items || topcard->title)\r
+       {\r
+               // Draw the backdrop\r
+               VWB_DrawPic(0,0,CP_MENUSCREENPIC);\r
+\r
+               // Draw the contents\r
+               USL_DrawCtlPanelContents();\r
+       }\r
+\r
+       // Refresh the screen\r
+       VW_UpdateScreen();\r
+}\r
+\r
+static void\r
+USL_DialogSetup(word w,word h,word *x,word *y)\r
+{\r
+       VWB_DrawMPic(CtlPanelSX,CtlPanelSY,CP_MENUMASKPICM);\r
+\r
+       *x = CtlPanelSX + ((CtlPanelW - w) / 2);\r
+       *y = CtlPanelSY + ((CtlPanelH - h) / 2);\r
+       VWB_Bar(*x,*y,w + 1,h + 1,BackColor);\r
+       VWB_Hlin(*x - 1,*x + w + 1,*y - 1,NohiliteColor);\r
+       VWB_Hlin(*x - 1,*x + w + 1,*y + h + 1,NohiliteColor);\r
+       VWB_Vlin(*y - 1,*y + h + 1,*x - 1,NohiliteColor);\r
+       VWB_Vlin(*y - 1,*y + h + 1,*x + w + 1,NohiliteColor);\r
+}\r
+\r
+static void\r
+USL_ShowLoadSave(char *s,char *name)\r
+{\r
+       word    x,y,\r
+                       w,h,\r
+                       tw,sw;\r
+       char    msg[MaxGameName + 4];\r
+\r
+       strcpy(msg,"'");\r
+       strcat(msg,name);\r
+       strcat(msg,"'");\r
+       USL_MeasureString(s,&sw,&h);\r
+       USL_MeasureString(msg,&w,&h);\r
+       tw = ((sw > w)? sw : w) + 6;\r
+       USL_DialogSetup(tw,(h * 2) + 2,&x,&y);\r
+       py = y + 2;\r
+       px = x + ((tw - sw) / 2);\r
+       USL_DrawString(s);\r
+       py += h;\r
+       px = x + ((tw - w) / 2);\r
+       USL_DrawString(msg);\r
+\r
+       VW_UpdateScreen();\r
+#ifdef CAT3D\r
+       IN_UserInput(100, true);\r
+#endif\r
+}\r
+\r
+static boolean\r
+USL_CtlDialog(char *s1,char *s2,char *s3)\r
+{\r
+       word            w,h,sh,\r
+                               w1,w2,w3,\r
+                               x,y;\r
+       ScanCode        c;\r
+       CursorInfo      cursorinfo;\r
+\r
+       USL_MeasureString(s1,&w1,&h);\r
+       USL_MeasureString(s2,&w2,&h);\r
+       if (s3)\r
+               USL_MeasureString(s3,&w3,&h);\r
+       else\r
+               w3 = 0;\r
+       w = (w1 > w2)? ((w1 > w3)? w1 : w3) : ((w2 > w3)? w2 : w3);\r
+       w += 7;\r
+       sh = h;\r
+       h *= s3? 5 : 4;\r
+\r
+       USL_DialogSetup(w,h,&x,&y);\r
+\r
+       fontcolor = HiliteColor;\r
+       px = x + ((w - w1) / 2);\r
+       py = y + sh + 1;\r
+       USL_DrawString(s1);\r
+       py += (sh * 2) - 1;\r
+\r
+       VWB_Hlin(x + 3,x + w - 3,py,NohiliteColor);\r
+       py += 2;\r
+\r
+       fontcolor = NohiliteColor;\r
+       px = x + ((w - w2) / 2);\r
+       USL_DrawString(s2);\r
+       py += sh;\r
+\r
+       if (s3)\r
+       {\r
+               px = x + ((w - w3) / 2);\r
+               USL_DrawString(s3);\r
+       }\r
+\r
+       VW_UpdateScreen();\r
+\r
+       IN_ClearKeysDown();\r
+       do\r
+       {\r
+               IN_ReadCursor(&cursorinfo);\r
+               if (cursorinfo.button0)\r
+                       c = sc_Y;\r
+               else if (cursorinfo.button1)\r
+                       c = sc_Escape;\r
+               else\r
+                       c = LastScan;\r
+       } while (c == sc_None);\r
+       do\r
+       {\r
+               IN_ReadCursor(&cursorinfo);\r
+       } while (cursorinfo.button0 || cursorinfo.button1);\r
+\r
+       IN_ClearKeysDown();\r
+       USL_DrawCtlPanel();\r
+       return(c == sc_Y);\r
+}\r
+\r
+static boolean\r
+USL_ConfirmComm(UComm comm)\r
+{\r
+       boolean confirm,dialog;\r
+       char    *s1,*s2,*s3;\r
+\r
+       if (!comm)\r
+               Quit("USL_ConfirmComm() - empty comm");\r
+\r
+       confirm = true;\r
+       dialog = false;\r
+       s3 = "ESC TO BACK OUT";\r
+       switch (comm)\r
+       {\r
+       case uc_Abort:\r
+               s1 = "REALLY END CURRENT GAME?";\r
+               s2 = "PRESS Y TO END IT";\r
+               if (ingame && GameIsDirty)\r
+                       dialog = true;\r
+               break;\r
+       case uc_Quit:\r
+               s1 = "REALLY QUIT?";\r
+               s2 = "PRESS Y TO QUIT";\r
+               dialog = true;\r
+               break;\r
+       case uc_Loaded:\r
+               s1 = "YOU'RE IN A GAME";\r
+               s2 = "PRESS Y TO LOAD GAME";\r
+               if (ingame && GameIsDirty)\r
+                       dialog = true;\r
+               break;\r
+       case uc_SEasy:\r
+       case uc_SNormal:\r
+       case uc_SHard:\r
+               s1 = "YOU'RE IN A GAME";\r
+               s2 = "PRESS Y FOR NEW GAME";\r
+               if (ingame && GameIsDirty)\r
+                       dialog = true;\r
+               break;\r
+       }\r
+\r
+       confirm = dialog? USL_CtlDialog(s1,s2,s3) : true;\r
+       if (confirm)\r
+       {\r
+               Communication = comm;\r
+               CtlPanelDone = true;\r
+       }\r
+       return(confirm);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//      USL_HandleError() - Handles telling the user that there's been an error\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static void\r
+USL_HandleError(int num)\r
+{\r
+       char    buf[64];\r
+\r
+       strcpy(buf,"Error: ");\r
+       if (num < 0)\r
+               strcat(buf,"Unknown");\r
+       else if (num == ENOMEM)\r
+               strcat(buf,"Disk is Full");\r
+       else if (num == EINVFMT)\r
+               strcat(buf,"File is Incomplete");\r
+       else\r
+               strcat(buf,sys_errlist[num]);\r
+\r
+       VW_HideCursor();\r
+\r
+       USL_CtlDialog(buf,"PRESS ANY KEY",nil);\r
+       VW_UpdateScreen();\r
+\r
+       IN_ClearKeysDown();\r
+       IN_Ack();\r
+\r
+       VW_ShowCursor();\r
+       VW_UpdateScreen();\r
+}\r
+\r
+//      Custom routines\r
+#if 0\r
+static boolean\r
+USL_GenericCustom(UserCall call,UserItem far *item)\r
+{\r
+       boolean result;\r
+\r
+       result = false;\r
+       switch (call)\r
+       {\r
+       }\r
+       return(result);\r
+}\r
+#endif\r
+\r
+static void\r
+USL_SetOptionsText(void)\r
+{\r
+       optionsi[0].text = showscorebox? "SCORE BOX (ON)" : "SCORE BOX (OFF)";\r
+       optionsi[1].text = oldshooting? "TWO-BUTTON FIRING (ON)" : "TWO-BUTTON FIRING (OFF)";\r
+#if GRMODE != CGAGR\r
+       optionsi[2].text = jerk? "FIX JERKY MOTION (ON)" : "FIX JERKY MOTION (OFF)";\r
+       optionsi[3].text = compatability? "SVGA COMPATIBILITY (ON)" : "SVGA COMPATIBILITY (OFF)";\r
+#endif\r
+\r
+       keybi[2].flags &= ~ui_Disabled;\r
+       if (oldshooting)\r
+               keybi[2].flags |= ui_Disabled;\r
+\r
+       // gravis option is only enabled when a joystick is selected\r
+       configi[6].flags |= ui_Disabled;\r
+       if (Controls[0] == ctrl_Joystick1 || Controls[0] == ctrl_Joystick2)\r
+               configi[6].flags &= ~ui_Disabled;\r
+\r
+       configi[6].text = GravisGamepad? "USE GRAVIS GAMEPAD (ON)" : "USE GRAVIS GAMEPAD (OFF)";\r
+}\r
+\r
+#pragma argsused\r
+static boolean\r
+USL_ScoreCustom(UserCall call,UserItem far *item)\r
+{\r
+       if (call != uic_SetupCard)\r
+               return(false);\r
+\r
+       showscorebox ^= true;\r
+       USL_CtlDialog(showscorebox? "Score box now on" : "Score box now off",\r
+                                       "Press any key",nil);\r
+       USL_SetOptionsText();\r
+       return(true);\r
+}\r
+\r
+#pragma argsused\r
+static boolean\r
+USL_SmoothCustom(UserCall call,UserItem far *item)\r
+{\r
+       if (call != uic_SetupCard)\r
+               return(false);\r
+\r
+       jerk ^= true;\r
+       USL_CtlDialog(jerk? "Jerky motion fix enabled" : "Jerky motion fix disabled",\r
+                                       "Press any key",nil);\r
+       USL_SetOptionsText();\r
+       return(true);\r
+}\r
+\r
+#pragma argsused\r
+static boolean\r
+USL_CompCustom(UserCall call,UserItem far *item)\r
+{\r
+       if (call != uic_SetupCard)\r
+               return(false);\r
+\r
+       compatability ^= true;\r
+       USL_CtlDialog(compatability? "SVGA compatibility now on" : "SVGA compatibility now off",\r
+                                       "Press any key",nil);\r
+       USL_SetOptionsText();\r
+       return(true);\r
+}\r
+\r
+#ifdef  KEEN\r
+#pragma argsused\r
+static boolean\r
+USL_TwoCustom(UserCall call,UserItem far *item)\r
+{\r
+       if (call != uic_SetupCard)\r
+               return(false);\r
+\r
+       oldshooting ^= true;\r
+       USL_CtlDialog(oldshooting? "Two-button firing now on" : "Two-button firing now off",\r
+                                       "Press any key",nil);\r
+       USL_SetOptionsText();\r
+       return(true);\r
+}\r
+#endif\r
+\r
+static boolean\r
+USL_ConfigCustom(UserCall call,UserItem far *item)\r
+{\r
+static  char    *CtlNames[] = {"KEYBOARD","KEYBOARD","JOYSTICK #1","JOYSTICK #2","MOUSE"};\r
+               char    *s;\r
+               word    w,h,\r
+                               tw;\r
+\r
+       if (call == uic_TouchupCard)\r
+       {\r
+               s = "CONTROL: ";\r
+               USL_MeasureString(s,&w,&h);\r
+               tw = w;\r
+               USL_MeasureString(CtlNames[Controls[0]],&w,&h);\r
+               tw += w;\r
+               py = CtlPanelEY - 18 - h;\r
+               px = CtlPanelSX + ((CtlPanelW - tw) / 2);\r
+               fontcolor = NohiliteColor;\r
+               USL_DrawString(s);\r
+               USL_DrawString(CtlNames[Controls[0]]);\r
+       }\r
+       item++; // Shut the compiler up\r
+       return(false);\r
+}\r
+\r
+static void\r
+USL_CKSetKey(UserItem far *item,word i)\r
+{\r
+       boolean         on;\r
+       word            j;\r
+       ScanCode        scan;\r
+       longword        time;\r
+       CursorInfo      cursorinfo;\r
+\r
+       on = false;\r
+       time = 0;\r
+       LastScan = sc_None;\r
+       fontcolor = HiliteColor;\r
+       do\r
+       {\r
+               if (TimeCount >= time)\r
+               {\r
+                       on ^= true;\r
+                       VWB_Bar(item->x + 90,item->y,40,8,fontcolor ^ BackColor);\r
+                       VWB_Bar(item->x + 90 + 1,item->y + 1,40 - 2,8 - 2,BackColor);\r
+                       if (on)\r
+                               VWB_DrawTile8(item->x + 90 + 16,item->y,TileBase + 8);\r
+                       VW_UpdateScreen();\r
+\r
+                       time = TimeCount + (TickBase / 2);\r
+               }\r
+\r
+               IN_ReadCursor(&cursorinfo);\r
+               while (cursorinfo.button0 || cursorinfo.button1)\r
+               {\r
+                       IN_ReadCursor(&cursorinfo);\r
+                       LastScan = sc_Escape;\r
+               }\r
+\r
+       asm     pushf\r
+       asm     cli\r
+               if (LastScan == sc_LShift)\r
+                       LastScan = sc_None;\r
+       asm     popf\r
+       } while (!(scan = LastScan));\r
+\r
+       if (scan != sc_Escape)\r
+       {\r
+               for (j = 0,on = false;j < 11;j++)\r
+               {\r
+                       if (j == i)\r
+                               continue;\r
+                       if (*(KeyMaps[j]) == scan)\r
+                       {\r
+                               on = true;\r
+                               break;\r
+                       }\r
+               }\r
+               if (on)\r
+#ifdef KEEN\r
+                       USL_CtlDialog("Key already used","Press any key",nil);\r
+#else\r
+                       USL_CtlDialog("Key already used","Press a key",nil);\r
+#endif\r
+               else\r
+                       *(KeyMaps[i]) = scan;\r
+       }\r
+       IN_ClearKeysDown();\r
+}\r
+\r
+#pragma argsused\r
+static boolean\r
+USL_KeySCustom(UserCall call,UserItem far *item)\r
+{\r
+       if (call == uic_SetupCard)\r
+       {\r
+               Controls[0] = ctrl_Keyboard;\r
+               GravisGamepad = false;\r
+               USL_SetOptionsText();\r
+       }\r
+       return(false);\r
+}\r
+\r
+#pragma argsused\r
+static boolean\r
+USL_KeyCustom(UserCall call,UserItem far *item)\r
+{\r
+       boolean result;\r
+       word    i;\r
+\r
+       result = false;\r
+       i = (topcard == &keygroup)? (3 + (item - keyi)) : (item - keybi);\r
+       switch (call)\r
+       {\r
+       case uic_SetupCard:\r
+               Controls[0] = ctrl_Keyboard;\r
+               break;\r
+       case uic_Draw:\r
+               VWB_Bar(CtlPanelSX + 1,item->y,\r
+                               CtlPanelEX - CtlPanelSX - 1,8,BackColor);       // Clear out background\r
+               USL_DrawItemIcon(item);\r
+               fontcolor = (item->flags & ui_Selected)? HiliteColor : NohiliteColor;\r
+               px = item->x + 8;\r
+               py = item->y + 1;\r
+               USL_DrawString(item->text);\r
+               VWB_Bar(item->x + 90,item->y,40,8,fontcolor ^ BackColor);\r
+               VWB_Bar(item->x + 90 + 1,item->y + 1,40 - 2,8 - 2,BackColor);\r
+               px = item->x + 90 + 6;\r
+               py = item->y + 1;\r
+               USL_DrawString(IN_GetScanName(*KeyMaps[i]));\r
+               result = true;\r
+               break;\r
+       case uic_Hit:\r
+               USL_KeyCustom(uic_Draw,item);\r
+               USL_CKSetKey(item,i);\r
+               USL_DrawCtlPanel();\r
+               result = true;\r
+               break;\r
+       }\r
+       return(result);\r
+}\r
+\r
+static void\r
+USL_CJDraw(char *s1,char *s2)\r
+{\r
+       word    w,h;\r
+\r
+       USL_MeasureString(s1,&w,&h);\r
+       px = CtlPanelSX + ((CtlPanelW - w) / 2);\r
+       py = CtlPanelEY - 34;\r
+       VWB_Bar(CtlPanelSX + 1,py,CtlPanelW - 2,h * 2,BackColor);\r
+       fontcolor = HiliteColor;\r
+       USL_DrawString(s1);\r
+       py += h;\r
+       USL_MeasureString(s2,&w,&h);\r
+       px = CtlPanelSX + ((CtlPanelW - w) / 2);\r
+       USL_DrawString(s2);\r
+}\r
+\r
+static boolean\r
+USL_CJGet(word joy,word button,word x,word y,word *xaxis,word *yaxis)\r
+{\r
+       boolean         on;\r
+       longword        time;\r
+\r
+       while (IN_GetJoyButtonsDB(joy))\r
+               if (LastScan == sc_Escape)\r
+                       return(false);\r
+\r
+       on = false;\r
+       time = 0;\r
+       while (!(IN_GetJoyButtonsDB(joy) & (1 << button)))\r
+       {\r
+               if (TimeCount >= time)\r
+               {\r
+                       on ^= true;\r
+                       time = TimeCount + (TickBase / 2);\r
+                       VWB_DrawTile8(x,y,TileBase + on);\r
+                       VW_UpdateScreen();\r
+               }\r
+\r
+               if (LastScan == sc_Escape)\r
+                       return(false);\r
+       }\r
+       IN_GetJoyAbs(joy,xaxis,yaxis);\r
+       return(true);\r
+}\r
+\r
+static boolean\r
+USL_ConfigJoystick(word joy)\r
+{\r
+       word    x,y,\r
+                       minx,miny,\r
+                       maxx,maxy;\r
+\r
+       BottomS1 = BottomS2 = "";\r
+#ifdef KEEN\r
+       BottomS3 = "ESC to back out";\r
+#else\r
+       BottomS3 = "Esc to back out";\r
+#endif\r
+       USL_DrawCtlPanel();\r
+       x = CtlPanelSX + 60;\r
+       y = CtlPanelSY + 19;\r
+       VWB_DrawPic(x,y,CP_JOYSTICKPIC);\r
+\r
+       USL_CJDraw("Move Joystick to upper left","and press button #1");\r
+       VWB_DrawTile8(x + 24,y + 8,TileBase + 6);\r
+       VWB_DrawTile8(x + 8,y + 8,TileBase + 1);\r
+       VWB_DrawTile8(x + 8,y + 24,TileBase + 0);\r
+       VW_UpdateScreen();\r
+       if (!USL_CJGet(joy,0,x + 8,y + 8,&minx,&miny))\r
+               return(false);\r
+\r
+       USL_CJDraw("Move Joystick to lower right","and press button #2");\r
+       VWB_DrawTile8(x + 24,y + 8,TileBase - 25);\r
+       VWB_DrawTile8(x + 40,y + 24,TileBase + 7);\r
+       VWB_DrawTile8(x + 8,y + 8,TileBase + 0);\r
+       VWB_DrawTile8(x + 8,y + 24,TileBase + 1);\r
+       VW_UpdateScreen();\r
+       if (!USL_CJGet(joy,1,x + 8,y + 24,&maxx,&maxy))\r
+               return(false);\r
+\r
+       while (IN_GetJoyButtonsDB(joy))\r
+               ;\r
+\r
+#ifdef KEEN\r
+       if (minx != maxx && miny != maxy)\r
+       {\r
+               IN_SetupJoy(joy,minx,maxx,miny,maxy);\r
+               return(true);\r
+       }\r
+       return(false);\r
+#else\r
+       IN_SetupJoy(joy,minx,maxx,miny,maxy);\r
+       return(true);\r
+#endif\r
+}\r
+\r
+#pragma argsused\r
+static boolean\r
+USL_Joy1Custom(UserCall call,UserItem far *item)\r
+{\r
+       if (call == uic_SetupCard)\r
+       {\r
+               if (USL_ConfigJoystick(0))\r
+               {\r
+                       Controls[0] = ctrl_Joystick1;\r
+                       USL_CtlDialog("USING JOYSTICK #1","PRESS ANY KEY",nil);\r
+                       USL_SetOptionsText();\r
+               }\r
+               return(true);\r
+       }\r
+       else\r
+               return(false);\r
+}\r
+\r
+#pragma argsused\r
+static boolean\r
+USL_Joy2Custom(UserCall call,UserItem far *item)\r
+{\r
+       if (call == uic_SetupCard)\r
+       {\r
+               if (USL_ConfigJoystick(1))\r
+               {\r
+                       Controls[0] = ctrl_Joystick2;\r
+                       USL_CtlDialog("USING JOYSTICK #2","PRESS ANY KEY",nil);\r
+                       USL_SetOptionsText();\r
+               }\r
+               return(true);\r
+       }\r
+       else\r
+               return(false);\r
+}\r
+\r
+static void\r
+USL_CGDraw(char *s1, char *s2, int buttonsDone)\r
+{\r
+       static char *GButtonNames[4] = {"Red","Blue","Yellow","Green"};\r
+       static char *GActionNames[4] = {"Jump","Pogo","Fire","Status"};\r
+\r
+       int     i, n;\r
+       char    *actionstr;\r
+       word    w, h;\r
+\r
+       VWB_Bar(CtlPanelSX+1, CtlPanelSY+16, CtlPanelW-2, 68, BackColor);\r
+       px = CtlPanelSX+8;\r
+       py = CtlPanelSY+16;\r
+       USL_DrawString("Make sure that the button");\r
+       px = CtlPanelSX+8;\r
+       py = CtlPanelSY+24;\r
+       USL_DrawString("switch is set for 4 buttons");\r
+\r
+       for (i=0; i<4; i++)\r
+       {\r
+               px = CtlPanelSX+8;\r
+               py = i*8 + CtlPanelSY+40;\r
+               USL_DrawString(GButtonNames[i]);\r
+               USL_DrawString(":");\r
+               actionstr = "?";\r
+               for (n=0;n<buttonsDone;n++)\r
+               {\r
+                       if (GravisMap[n] == i)\r
+                               actionstr = GActionNames[n];\r
+               }\r
+               px = CtlPanelSX+56;\r
+               USL_DrawString(actionstr);\r
+       }\r
+\r
+       USL_MeasureString(s1, &w, &h);\r
+       px = w;\r
+       USL_MeasureString(s2, &w, &h);\r
+       px = (CtlPanelW-px-w)/2 + CtlPanelSX;\r
+       py = CtlPanelSY+76;\r
+       USL_DrawString(s1);\r
+       USL_DrawString(s2);\r
+       VW_UpdateScreen();\r
+}\r
+\r
+static boolean\r
+USL_CGGet(int buttonsDone, char *action)\r
+{\r
+       word    buttons, i, n;\r
+\r
+       USL_CGDraw("PRESS BUTTON FOR ", action, buttonsDone);\r
+\r
+redo:\r
+       do\r
+       {\r
+               if (LastScan == sc_Escape)\r
+               {\r
+                       Keyboard[sc_Escape] = false;\r
+                       if (LastScan == sc_Escape)\r
+                               LastScan = sc_None;\r
+                       return false;\r
+               }\r
+\r
+               buttons = IN_GetJoyButtonsDB(2);\r
+               if (!buttons)\r
+                       continue;\r
+\r
+               for(i=n=0; i<4; i++)\r
+               {\r
+                       if (buttons & (1 << i))\r
+                               n += i+1;\r
+               }\r
+\r
+               if (!n || n >= 5)\r
+                       continue;\r
+\r
+               n--;\r
+               for (i=0; i<buttonsDone; i++)\r
+               {\r
+                       if (GravisMap[i] == n)\r
+                               goto redo;\r
+               }\r
+\r
+               GravisMap[buttonsDone] = n;\r
+               return true;\r
+       } while (true);\r
+}\r
+\r
+#pragma argsused\r
+static boolean\r
+USL_JoyGCustom(UserCall call,UserItem far *item)\r
+{\r
+       if (call == uic_SetupCard)\r
+       {\r
+               if (GravisGamepad)\r
+               {\r
+                       GravisGamepad = false;\r
+               }\r
+               else\r
+               {\r
+                       BottomS1 = BottomS2 = "";\r
+                       BottomS3 = "ESC to back out";\r
+                       USL_DrawCtlPanel();\r
+                       fontcolor = HiliteColor;\r
+                       px = CtlPanelSX + 8;\r
+                       py = CtlPanelSX + 8;\r
+                       fontcolor = HiliteColor;        // redundant...\r
+                       IN_ClearKeysDown();\r
+                       if (  USL_CGGet(0, "JUMP")\r
+                               && USL_CGGet(1, "POGO")\r
+                               && USL_CGGet(2, "FIRE")\r
+                               && USL_CGGet(3, "STATUS") )\r
+                       {\r
+                               GravisGamepad = true;\r
+                               USL_CGDraw("PRESS ANY KEY", "", 4);\r
+                               IN_Ack();\r
+                       }\r
+                       else\r
+                       {\r
+                               GravisGamepad = false;\r
+                       }\r
+               }\r
+               USL_SetOptionsText();\r
+               return true;\r
+       }\r
+       return false;\r
+}\r
+\r
+static void\r
+USL_DrawFileIcon(UserItem far *item)\r
+{\r
+       word    color;\r
+\r
+       item->y = topcard->y + CtlPanelSY + 12;\r
+       item->y += (item - loadsavegamei) * 11;\r
+\r
+       fontcolor = (item->flags & ui_Selected)? HiliteColor : NohiliteColor;\r
+       color = fontcolor ^ BackColor;  // Blech!\r
+       VWB_Hlin(item->x,item->x + (CtlPanelW - 12),item->y,color);\r
+       VWB_Hlin(item->x,item->x + (CtlPanelW - 12),item->y + 9,color);\r
+       VWB_Vlin(item->y,item->y + 9,item->x,color);\r
+       VWB_Vlin(item->y,item->y + 9,item->x + (CtlPanelW - 12),color);\r
+}\r
+\r
+static void\r
+USL_DoLoadGame(UserItem far *item)\r
+{\r
+       char            *filename;\r
+       word            n,\r
+                               err;\r
+       int                     file;\r
+       SaveGame        *game;\r
+\r
+       if (!USL_ConfirmComm(uc_Loaded))\r
+               return;\r
+\r
+       n = item - loadsavegamei;\r
+       game = &Games[n];\r
+\r
+       USL_ShowLoadSave("Loading",game->name);\r
+\r
+       err = 0;\r
+       filename = USL_GiveSaveName(n);\r
+       if ((file = open(filename,O_BINARY | O_RDONLY)) != -1)\r
+       {\r
+               if (read(file,game,sizeof(*game)) == sizeof(*game))\r
+               {\r
+                       if (USL_LoadGame)\r
+                               if (!USL_LoadGame(file))\r
+                                       USL_HandleError(err = errno);\r
+               }\r
+               else\r
+                       USL_HandleError(err = errno);\r
+               close(file);\r
+       }\r
+       else\r
+               USL_HandleError(err = errno);\r
+       if (err)\r
+       {\r
+               abortgame = true;\r
+               Communication = uc_None;\r
+               CtlPanelDone = false;\r
+       }\r
+       else\r
+               loadedgame = true;\r
+       game->present = true;\r
+\r
+       if (loadedgame)\r
+               Paused = true;\r
+\r
+       USL_DrawCtlPanel();\r
+}\r
+\r
+static boolean\r
+USL_LoadCustom(UserCall call,UserItem far *item)\r
+{\r
+       boolean result;\r
+       word    i;\r
+\r
+       result = false;\r
+       switch (call)\r
+       {\r
+       case uic_SetupCard:\r
+#ifdef KEEN\r
+               if (getenv("UID"))\r
+                       USL_CheckSavedGames();\r
+#endif\r
+               for (i = 0;i < MaxSaveGames;i++)\r
+               {\r
+                       if (Games[i].present)\r
+                               loadsavegamei[i].flags &= ~ui_Disabled;\r
+                       else\r
+                               loadsavegamei[i].flags |= ui_Disabled;\r
+               }\r
+               break;\r
+       case uic_DrawIcon:\r
+               USL_DrawFileIcon(item);\r
+               result = true;\r
+               break;\r
+       case uic_Draw:\r
+               USL_DrawFileIcon(item);\r
+               VWB_Bar(item->x + 1,item->y + 2,CtlPanelW - 12 - 2,7,BackColor);\r
+\r
+               i = item - loadsavegamei;\r
+               if (Games[i].present)\r
+                       px = item->x + 2;\r
+               else\r
+                       px = item->x + 60;\r
+               py = item->y + 2;\r
+               USL_DrawString(Games[i].present? Games[i].name : "Empty");\r
+               result = true;\r
+               break;\r
+       case uic_Hit:\r
+               USL_DoLoadGame(item);\r
+               result = true;\r
+               break;\r
+       }\r
+       return(result);\r
+}\r
+\r
+static void\r
+USL_DoSaveGame(UserItem far *item)\r
+{\r
+       boolean         ok;\r
+       char            *filename;\r
+       word            n,err;\r
+       int         file;\r
+       SaveGame        *game;\r
+\r
+       BottomS1 = "Type name";\r
+       BottomS2 = "Enter accepts";\r
+       USL_DrawCtlPanel();\r
+\r
+       n = item - loadsavegamei;\r
+       game = &Games[n];\r
+       fontcolor = HiliteColor;\r
+       VWB_Bar(item->x + 1,item->y + 2,CtlPanelW - 12 - 2,7,BackColor);\r
+       game->oldtest = &PrintX;\r
+       ok = US_LineInput(item->x + 2,item->y + 2,\r
+                                               game->name,game->present? game->name : nil,\r
+                                               true,MaxGameName,\r
+                                               CtlPanelW - 22);\r
+       if (!strlen(game->name))\r
+               strcpy(game->name,"Untitled");\r
+       if (ok)\r
+       {\r
+               USL_ShowLoadSave("Saving",game->name);\r
+\r
+               filename = USL_GiveSaveName(n);\r
+               err = 0;\r
+               file = open(filename,O_CREAT | O_BINARY | O_WRONLY,\r
+                                       S_IREAD | S_IWRITE | S_IFREG);\r
+               if (file != -1)\r
+               {\r
+                       if (write(file,game,sizeof(*game)) == sizeof(*game))\r
+                       {\r
+                               if (USL_SaveGame)\r
+                                       ok = USL_SaveGame(file);\r
+                               if (!ok)\r
+                                       USL_HandleError(err = errno);\r
+                       }\r
+                       else\r
+                               USL_HandleError(err = ((errno == ENOENT)? ENOMEM : errno));\r
+                       close(file);\r
+               }\r
+               else\r
+                       USL_HandleError(err = ((errno == ENOENT)? ENOMEM : errno));\r
+               if (err)\r
+               {\r
+                       remove(filename);\r
+                       ok = false;\r
+               }\r
+\r
+       }\r
+\r
+       if (!game->present)\r
+               game->present = ok;\r
+\r
+       if (ok)\r
+               GameIsDirty = false;\r
+       USL_SetupCard();\r
+}\r
+\r
+static boolean\r
+USL_SaveCustom(UserCall call,UserItem far *item)\r
+{\r
+       word    i;\r
+\r
+       switch (call)\r
+       {\r
+       case uic_SetupCard:\r
+#ifdef KEEN\r
+               if (getenv("UID"))\r
+                       USL_CheckSavedGames();\r
+#endif\r
+               for (i = 0;i < MaxSaveGames;i++)\r
+                       loadsavegamei[i].flags &= ~ui_Disabled;\r
+               return(false);\r
+       case uic_Hit:\r
+               USL_DoSaveGame(item);\r
+               return(true);\r
+//              break;\r
+       }\r
+       return(USL_LoadCustom(call,item));\r
+}\r
+\r
+#define PaddleMinX      (CtlPanelSX + 4)\r
+#define PaddleMaxX      (CtlPanelEX - 15)\r
+#define BallMinX        (CtlPanelSX + 4)\r
+#define BallMinY        (CtlPanelSY + 12 + 2)\r
+#define BallMaxX        (CtlPanelEX - 6)\r
+#define BallMaxY        (CtlPanelEY - 13)\r
+#define CPaddleY        (BallMinY + 4)\r
+#define KPaddleY        (BallMaxY - 2)\r
+void\r
+USL_DrawPongScore(word k,word c)\r
+{\r
+       fontcolor = HiliteColor;\r
+       PrintY = py = CtlPanelSY + 4;\r
+       px = CtlPanelSX + 6;\r
+       VWB_Bar(px,py,42,6,BackColor);\r
+       USL_DrawString("KEEN:");\r
+       PrintX = px;\r
+       US_PrintUnsigned(k);\r
+       px = CtlPanelSX + 108;\r
+       VWB_Bar(px,py,50,6,BackColor);\r
+       USL_DrawString("COMP:");\r
+       PrintX = px;\r
+       US_PrintUnsigned(c);\r
+}\r
+\r
+void\r
+USL_PlayPong(void)\r
+{\r
+       boolean         ball,killball,revdir,done,lastscore;\r
+       word            cycle,\r
+                               x,y,\r
+                               kx,cx,\r
+                               rx,\r
+                               bx,by,\r
+                               oldkx,oldcx,oldbx,oldby,\r
+                               kscore,cscore,\r
+                               speedup;\r
+       int                     bdx,bdy;\r
+       longword        balltime,lasttime,waittime;\r
+       CursorInfo      cursorinfo;\r
+\r
+       kx = cx = PaddleMinX + ((PaddleMaxX - PaddleMinX) / 2);\r
+       bx = by = bdx = bdy = 0;\r
+       oldbx = oldcx = oldkx = PaddleMinX;\r
+       oldby = BallMinY;\r
+       kscore = cscore = 0;\r
+       USL_DrawPongScore(0,0);\r
+       cycle = 0;\r
+       revdir = false;\r
+       killball = true;\r
+       done = false;\r
+       lastscore = false;\r
+       lasttime = TimeCount;\r
+       do\r
+       {\r
+               while ((waittime = TimeCount - lasttime) == 0)\r
+                       ;\r
+\r
+               lasttime = TimeCount;\r
+               if (waittime > 4)\r
+                       waittime = 4;\r
+\r
+               while (waittime-- && !done && LastScan != sc_Escape)\r
+               {\r
+                       IN_ReadCursor(&cursorinfo);\r
+                       if (((cursorinfo.x < 0) || IN_KeyDown(sc_LeftArrow)) && (kx > PaddleMinX))\r
+                               kx -= 2;\r
+                       else if (((cursorinfo.x > 0) || IN_KeyDown(sc_RightArrow)) && (kx < PaddleMaxX))\r
+                               kx += 2;\r
+\r
+                       if (killball)\r
+                       {\r
+                               ball = false;\r
+                               balltime = TickBase;\r
+                               speedup = 10;\r
+                               killball = false;\r
+                               VWB_Bar(oldbx,oldby,5,5,BackColor);\r
+                       }\r
+\r
+                       if (ball && (cycle++ % 3))\r
+                       {\r
+                               x = (bx >> 2);\r
+                               if (!(x & 1))\r
+                                       x += (US_RndT() & 1);\r
+\r
+                               if ((cx + 6 < x) && (cx < PaddleMaxX))\r
+                                       cx += 1;\r
+                               else if ((cx + 6 > x) && (cx > PaddleMinX))\r
+                                       cx -= 1;\r
+                       }\r
+\r
+                       if (!ball && !--balltime)\r
+                       {\r
+                               ball = true;\r
+                               bdx = 1 - (US_RndT() % 3);\r
+                               bdy = 3;\r
+                               if (lastscore)\r
+                                       bdy = -bdy;\r
+                               bx = (BallMinX + ((BallMaxX - BallMinX) / 2)) << 2;\r
+                               by = (BallMinY + ((BallMaxY - BallMinY) / 2)) << 2;\r
+                       }\r
+\r
+                       if (ball)\r
+                       {\r
+                               if\r
+                               (\r
+                                       (((bx + bdx) >> 2) > BallMaxX)\r
+                               ||      (((bx + bdx) >> 2) < BallMinX)\r
+                               )\r
+                               {\r
+                                       SD_PlaySound(BALLBOUNCESND);\r
+                                       bdx = -bdx;\r
+                               }\r
+                               bx += bdx;\r
+\r
+                               if (((by + bdy) >> 2) > BallMaxY)\r
+                               {\r
+                                       killball = true;\r
+                                       lastscore = false;\r
+                                       cscore++;\r
+                                       SD_PlaySound(COMPSCOREDSND);\r
+                                       USL_DrawPongScore(kscore,cscore);\r
+                                       if (cscore == 21)\r
+                                       {\r
+                                               USL_CtlDialog("You lost!","Press any key",nil);\r
+                                               done = true;\r
+                                               continue;\r
+                                       }\r
+                               }\r
+                               else if (((by + bdy) >> 2) < BallMinY)\r
+                               {\r
+                                       killball = true;\r
+                                       lastscore = true;\r
+                                       kscore++;\r
+                                       SD_PlaySound(KEENSCOREDSND);\r
+                                       USL_DrawPongScore(kscore,cscore);\r
+                                       if (kscore == 21)\r
+                                       {\r
+                                               USL_CtlDialog("You won!","Press any key",nil);\r
+                                               done = true;\r
+                                               continue;\r
+                                       }\r
+                               }\r
+                               by += bdy;\r
+\r
+                               x = bx >> 2;\r
+                               y = by >> 2;\r
+                               if (!killball)\r
+                               {\r
+                                       if\r
+                                       (\r
+                                               (bdy < 0)\r
+                                       &&      ((y >= CPaddleY) && (y < CPaddleY + 3))\r
+                                       &&      ((x >= (cx - 5)) && (x < (cx + 11)))\r
+                                       )\r
+                                       {\r
+                                               rx = cx;\r
+                                               revdir = true;\r
+                                               SD_PlaySound(COMPPADDLESND);\r
+                                       }\r
+                                       else if\r
+                                       (\r
+                                               (bdy > 0)\r
+                                       &&      ((y >= (KPaddleY - 3)) && (y < KPaddleY))\r
+                                       &&      ((x >= (kx - 5)) && (x < (kx + 11)))\r
+                                       )\r
+                                       {\r
+                                               if (((bdy >> 2) < 3) && !(--speedup))\r
+                                               {\r
+                                                       bdy++;\r
+                                                       speedup = 10;\r
+                                               }\r
+                                               rx = kx;\r
+                                               revdir = true;\r
+                                               SD_PlaySound(KEENPADDLESND);\r
+                                       }\r
+                                       if (revdir)\r
+                                       {\r
+                                               bdy = -bdy;\r
+                                               bdx = ((x + 5 - rx) >> 1) - (1 << 2);\r
+                                               if (!bdx)\r
+                                                       bdx--;\r
+                                               revdir = false;\r
+                                       }\r
+                               }\r
+                       }\r
+               }\r
+\r
+               if (ball)\r
+               {\r
+                       VWB_Bar(oldbx,oldby,5,5,BackColor);\r
+                       oldbx = x;\r
+                       oldby = y;\r
+#if GRMODE == CGAGR\r
+                       {\r
+                               static int ballsprites[4] = {BALLSPR, BALL1PIXELTOTHERIGHTSPR, BALL2PIXELSTOTHERIGHTSPR, BALL3PIXELSTOTHERIGHTSPR};\r
+                               VWB_DrawSprite(x,y,ballsprites[x & 3]);\r
+                       }\r
+#else\r
+                       VWB_DrawSprite(x,y,(x & 1)? BALL1PIXELTOTHERIGHTSPR : BALLSPR);\r
+#endif\r
+               }\r
+               VWB_Bar(oldcx-3,CPaddleY,16,3,BackColor);\r
+               oldcx = cx;\r
+               VWB_DrawSprite(cx,CPaddleY,PADDLESPR);\r
+               VWB_Bar(oldkx-3,KPaddleY,16,3,BackColor);\r
+               oldkx = kx;\r
+               VWB_DrawSprite(kx,KPaddleY,PADDLESPR);\r
+\r
+               VW_UpdateScreen();\r
+       } while ((LastScan != sc_Escape) && !done);\r
+       IN_ClearKeysDown();\r
+}\r
+\r
+#pragma argsused\r
+static boolean\r
+USL_PongCustom(UserCall call,struct UserItem far *item)\r
+{\r
+       if (call != uic_SetupCard)\r
+               return(false);\r
+\r
+       VWB_DrawPic(0,0,CP_MENUSCREENPIC);\r
+       VWB_DrawPic(CtlPanelSX + 56,CtlPanelSY,CP_PADDLEWARPIC);\r
+       VWB_Hlin(CtlPanelSX + 3,CtlPanelEX - 3,CtlPanelSY + 12,HiliteColor ^ BackColor);\r
+       VWB_Hlin(CtlPanelSX + 3,CtlPanelEX - 3,CtlPanelEY - 7,HiliteColor ^ BackColor);\r
+       USL_PlayPong();\r
+\r
+       return(true);\r
+}\r
+\r
+//      Flag management stuff\r
+static void\r
+USL_ClearFlags(UserItemGroup far *node)\r
+{\r
+       UserItem        far *i;\r
+\r
+       if (!node->items)\r
+               return;\r
+\r
+       for (i = node->items;i->type != uii_Bad;i++)\r
+       {\r
+               i->flags &= ~UISelectFlags;\r
+               if (i->child)\r
+                       USL_ClearFlags((UserItemGroup far *)i->child);\r
+       }\r
+}\r
+\r
+static int\r
+USL_FindPushedItem(UserItemGroup far *group)\r
+{\r
+       word            i;\r
+       UserItem        far *item;\r
+\r
+       for (item = group->items,i = 0;item->type != uii_Bad;item++,i++)\r
+               if (item->flags & ui_Pushed)\r
+                       return(i);\r
+       return(-1);\r
+}\r
+\r
+static void\r
+USL_SelectItem(UserItemGroup far *group,word index,boolean draw)\r
+{\r
+       UserItem        far *item;\r
+\r
+       if (index != group->cursor)\r
+       {\r
+               item = &group->items[group->cursor];\r
+               item->flags &= ~ui_Selected;\r
+               if (draw)\r
+                       USL_DrawItem(item);\r
+       }\r
+\r
+       group->cursor = index;\r
+       item = &group->items[group->cursor];\r
+       group->items[group->cursor].flags |= ui_Selected;\r
+       if (draw)\r
+               USL_DrawItem(item);\r
+}\r
+\r
+static void\r
+USL_PushItem(UserItemGroup far *group,word index,boolean draw)\r
+{\r
+       word            i;\r
+       UserItem        far *item;\r
+\r
+       USL_SelectItem(group,index,draw);\r
+       for (item = group->items,i = 0;item->type != uii_Bad;item++,i++)\r
+       {\r
+               if (item->type != uii_RadioButton)\r
+                       continue;\r
+\r
+               if (i == index)\r
+               {\r
+                       item->flags |= ui_Pushed;\r
+                       if (draw)\r
+                               USL_DrawItem(item);\r
+               }\r
+               else if (item->flags & ui_Pushed)\r
+               {\r
+                       item->flags &= ~ui_Pushed;\r
+                       if (draw)\r
+                               USL_DrawItem(item);\r
+               }\r
+       }\r
+}\r
+\r
+static void\r
+USL_NextItem(void)\r
+{\r
+       if (topcard->items[topcard->cursor + 1].type == uii_Bad)\r
+               return;\r
+       USL_SelectItem(topcard,topcard->cursor + 1,true);\r
+}\r
+\r
+static void\r
+USL_PrevItem(void)\r
+{\r
+       if (!topcard->cursor)\r
+               return;\r
+       USL_SelectItem(topcard,topcard->cursor - 1,true);\r
+}\r
+\r
+static void\r
+USL_SetupCard(void)\r
+{\r
+       BottomS1 = "Arrows move";\r
+       BottomS2 = "Enter selects";\r
+       BottomS3 = cstackptr? "ESC to back out" : "ESC to quit";\r
+\r
+       USL_SelectItem(topcard,topcard->cursor,false);\r
+       USL_DrawCtlPanel();     // Contents?\r
+}\r
+\r
+static void\r
+USL_DownLevel(UserItemGroup far *group)\r
+{\r
+       if (!group)\r
+               Quit("USL_DownLevel() - nil card");\r
+       USL_PushCard(group);\r
+       if (group->custom && group->custom(uic_SetupCard,nil))\r
+               USL_PopCard();\r
+       USL_SetupCard();\r
+}\r
+\r
+static void\r
+USL_UpLevel(void)\r
+{\r
+       if (!cstackptr)\r
+       {\r
+               USL_ConfirmComm(uc_Quit);\r
+               return;\r
+       }\r
+\r
+       if (topcard->items)\r
+               topcard->items[topcard->cursor].flags &= ~ui_Selected;\r
+       USL_PopCard();\r
+       USL_SetupCard();\r
+}\r
+\r
+static void\r
+USL_DoItem(void)\r
+{\r
+       // DEBUG - finish this routine\r
+       UserItem                far *item;\r
+\r
+       item = &topcard->items[topcard->cursor];\r
+       if (item->flags & ui_Disabled)\r
+               SD_PlaySound(NOWAYSND);\r
+       else\r
+       {\r
+               switch (item->type)\r
+               {\r
+               case uii_Button:\r
+                       if (!(topcard->custom && topcard->custom(uic_Hit,item)))\r
+                               USL_ConfirmComm(item->comm);\r
+                       break;\r
+               case uii_RadioButton:\r
+                       USL_PushItem(topcard,topcard->cursor,true);\r
+                       break;\r
+               case uii_Folder:\r
+                       USL_DownLevel(item->child);\r
+                       break;\r
+               }\r
+       }\r
+}\r
+\r
+static void\r
+USL_SetControlValues(void)\r
+{\r
+       int sndindex;\r
+\r
+       sndindex = SoundMode;\r
+       if (sndindex == sdm_AdLib && QuietFX)\r
+               sndindex++;\r
+\r
+       USL_PushItem(&soundgroup,sndindex,false);\r
+       USL_PushItem(&musicgroup,MusicMode,false);\r
+       if (!AdLibPresent)\r
+       {\r
+               soundi[2].flags |= ui_Disabled; // AdLib sound effects\r
+               soundi[3].flags |= ui_Disabled; // Quiet AdLib sound effects\r
+               musici[1].flags |= ui_Disabled; // AdLib music\r
+       }\r
+\r
+#ifdef CAT3D\r
+       if (!JoysPresent[0])\r
+               configi[3].flags |= ui_Disabled;\r
+       if (!JoysPresent[1])\r
+               configi[4].flags |= ui_Disabled;\r
+#else\r
+       if (!JoysPresent[0])\r
+               configi[4].flags |= ui_Disabled;\r
+       if (!JoysPresent[1])\r
+               configi[5].flags |= ui_Disabled;\r
+       if (!JoysPresent[0] && !JoysPresent[1])\r
+               configi[6].flags |= ui_Disabled;\r
+#endif\r
+\r
+       rooti[4].text = ingame? "RETURN TO GAME" : "RETURN TO DEMO";\r
+       if (!ingame)\r
+       {\r
+               rooti[2].flags |= ui_Disabled;  // Save Game\r
+               rooti[5].flags |= ui_Disabled;  // End Game\r
+       }\r
+       rootgroup.cursor = ingame? 4 : 0;\r
+       USL_SetOptionsText();\r
+       // DEBUG - write the rest of this\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//      USL_SetUpCtlPanel() - Sets the states of the UserItems to reflect the\r
+//              values of all the appropriate variables\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static void\r
+USL_SetUpCtlPanel(void)\r
+{\r
+       int     i;\r
+\r
+       // Cache in all of the stuff for the control panel\r
+       CA_UpLevel();\r
+       for (i = CONTROLS_LUMP_START;i <= CONTROLS_LUMP_END;i++)\r
+               CA_MarkGrChunk(i);\r
+       for (i = PADDLE_LUMP_START;i <= PADDLE_LUMP_END;i++)\r
+               CA_MarkGrChunk(i);\r
+       CA_MarkGrChunk(STARTFONT+1);            // Little font\r
+       CA_MarkGrChunk(CP_MENUMASKPICM);        // Mask for dialogs\r
+       CA_CacheMarks("Control Panel");\r
+       CA_LoadAllSounds();\r
+\r
+       // Do some other setup\r
+       fontnumber = 1;\r
+       US_SetPrintRoutines(VW_MeasurePropString,VWB_DrawPropString);\r
+       fontcolor = F_BLACK;\r
+#ifdef CAT3D\r
+       VW_Bar (0,0,320,200,3); // CAT3D patch\r
+#else\r
+       VW_ClearVideo(3);\r
+#endif\r
+       RF_FixOfs();\r
+       VW_InitDoubleBuffer();\r
+\r
+       Communication = uc_None;\r
+       USL_ClearFlags(&rootgroup);\r
+       USL_SetControlValues();\r
+       USL_SetupStack();\r
+       USL_SetupCard();\r
+\r
+       if (ingame)\r
+               GameIsDirty = true;\r
+\r
+       IN_ClearKeysDown();\r
+}\r
+\r
+static void\r
+USL_HandleComm(UComm comm)\r
+{\r
+       switch (comm)\r
+       {\r
+       case uc_Loaded:\r
+       case uc_Return:\r
+               break;\r
+       case uc_Abort:\r
+               abortgame = true;\r
+               break;\r
+       case uc_Quit:\r
+               QuitToDos = true;\r
+               break;\r
+       case uc_SEasy:\r
+               restartgame = gd_Easy;\r
+               break;\r
+       case uc_SNormal:\r
+               restartgame = gd_Normal;\r
+               break;\r
+       case uc_SHard:\r
+               restartgame = gd_Hard;\r
+               break;\r
+\r
+       default:\r
+               Quit("USL_HandleComm() - unknown");\r
+               break;\r
+       }\r
+}\r
+\r
+static void\r
+USL_GetControlValues(void)\r
+{\r
+       int     i;\r
+\r
+       // DEBUG - write the rest of this\r
+       i = USL_FindPushedItem(&soundgroup);\r
+       if (i == 3)\r
+       {\r
+               QuietFX = true;\r
+               i--;\r
+       }\r
+       else\r
+       {\r
+               QuietFX = false;\r
+       }\r
+       if (i != SoundMode)\r
+               SD_SetSoundMode(i);\r
+\r
+       i = USL_FindPushedItem(&musicgroup);\r
+       if (i != MusicMode)\r
+               SD_SetMusicMode(i);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//      USL_TearDownCtlPanel() - Given the state of the control panel, sets the\r
+//              modes and values as appropriate\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static void\r
+USL_TearDownCtlPanel(void)\r
+{\r
+       USL_GetControlValues();\r
+       if (Communication)\r
+               USL_HandleComm(Communication);\r
+\r
+       fontnumber = 0; // Normal font\r
+       fontcolor = F_BLACK;\r
+       if (restartgame && USL_ResetGame)\r
+               USL_ResetGame();\r
+       else if (QuitToDos)\r
+       {\r
+               if (tedlevel)\r
+                       TEDDeath();\r
+               else\r
+               {\r
+                       US_CenterWindow(20,3);\r
+                       fontcolor = F_SECONDCOLOR;\r
+                       US_PrintCentered("Quitting...");\r
+                       fontcolor = F_BLACK;\r
+                       VW_UpdateScreen();\r
+                       Quit(nil);\r
+               }\r
+       }\r
+\r
+       IN_ClearKeysDown();\r
+       SD_WaitSoundDone();\r
+#ifdef CAT3D\r
+       VW_Bar (0,0,320,200,3); // CAT3D patch\r
+#else\r
+       VW_ClearVideo(3);\r
+#endif\r
+       CA_DownLevel();\r
+       CA_LoadAllSounds();\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//      US_ControlPanel() - This is the main routine for the control panel\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+#define MoveMin 40\r
+void\r
+US_ControlPanel(void)\r
+{\r
+extern void HelpScreens(void);\r
+\r
+       boolean         resetitem,on;\r
+       word            i;\r
+       int                     ydelta;\r
+       longword        flashtime;\r
+       UserItem        far *item;\r
+       CursorInfo      cursorinfo;\r
+\r
+#if 0\r
+       // DEBUG!!!\r
+       {\r
+               USL_SetUpCtlPanel();\r
+               Communication = uc_Loaded;\r
+               CtlPanelDone = true;\r
+               loadedgame = true;\r
+               USL_TearDownCtlPanel();\r
+               return;\r
+       }\r
+#endif\r
+\r
+       if ((LastScan < sc_F1) || (LastScan > sc_F10))\r
+               IN_ClearKeysDown();\r
+\r
+       USL_SetUpCtlPanel();\r
+       USL_DrawCtlPanel();\r
+\r
+       ydelta = 0;\r
+       for (CtlPanelDone = false,resetitem = on = true;!CtlPanelDone;)\r
+       {\r
+               item = &(topcard->items[topcard->cursor]);\r
+\r
+               if (resetitem)\r
+               {\r
+                       flashtime = TimeCount + (TickBase / 2);\r
+                       resetitem = false;\r
+               }\r
+\r
+               if (TimeCount >= flashtime)\r
+               {\r
+                       on ^= true;\r
+                       resetitem = true;\r
+                       if (!on)\r
+                               item->flags &= ~ui_Selected;\r
+                       USL_DrawItemIcon(item);\r
+                       item->flags |= ui_Selected;\r
+               }\r
+\r
+               VW_UpdateScreen();\r
+\r
+               if (LastScan)\r
+               {\r
+                       switch (LastScan)\r
+                       {\r
+                       case sc_UpArrow:\r
+                               USL_PrevItem();\r
+                               resetitem = true;\r
+                               break;\r
+                       case sc_DownArrow:\r
+                               USL_NextItem();\r
+                               resetitem = true;\r
+                               break;\r
+                       case sc_Return:\r
+                               USL_DoItem();\r
+                               resetitem = true;\r
+                               break;\r
+                       case sc_Escape:\r
+                               USL_UpLevel();\r
+                               resetitem = true;\r
+                               break;\r
+#ifndef KEEN6\r
+                       case sc_F1:\r
+                               HelpScreens();\r
+                               USL_DrawCtlPanel();\r
+                               resetitem = true;\r
+                               break;\r
+#endif\r
+                       }\r
+\r
+                       if\r
+                       (\r
+                               (!resetitem)\r
+                       &&      (\r
+                                       (LastScan == KbdDefs[0].button0)\r
+                               ||      (LastScan == KbdDefs[0].button1)\r
+                               )\r
+                       )\r
+                       {\r
+                               USL_DoItem();\r
+                               resetitem = true;\r
+                       }\r
+\r
+                       if (!resetitem)\r
+                       {\r
+                               for (item = topcard->items,i = 0;item->type != uii_Bad;item++,i++)\r
+                               {\r
+                                       if (item->hotkey == LastScan)\r
+                                       {\r
+                                               USL_SelectItem(topcard,i,true);\r
+                                               resetitem = true;\r
+                                               break;\r
+                                       }\r
+                               }\r
+                       }\r
+\r
+                       IN_ClearKeysDown();\r
+               }\r
+               else\r
+               {\r
+                       IN_ReadCursor(&cursorinfo);\r
+                       ydelta += cursorinfo.y;\r
+                       if (cursorinfo.button0)\r
+                       {\r
+                               do\r
+                               {\r
+                                       IN_ReadCursor(&cursorinfo);\r
+                               } while (cursorinfo.button0);\r
+                               USL_DoItem();\r
+                               resetitem = true;\r
+                       }\r
+                       else if (cursorinfo.button1)\r
+                       {\r
+                               do\r
+                               {\r
+                                       IN_ReadCursor(&cursorinfo);\r
+                               } while (cursorinfo.button1);\r
+                               USL_UpLevel();\r
+                               resetitem = true;\r
+                       }\r
+                       else if (ydelta < -MoveMin)\r
+                       {\r
+                               ydelta += MoveMin;\r
+                               USL_PrevItem();\r
+                               resetitem = true;\r
+                       }\r
+                       else if (ydelta > MoveMin)\r
+                       {\r
+                               ydelta -= MoveMin;\r
+                               USL_NextItem();\r
+                               resetitem = true;\r
+                       }\r
+               }\r
+       }\r
+\r
+       USL_TearDownCtlPanel();\r
+}\r
+\r
+#ifdef KEEN6\r
+\r
+boolean US_ManualCheck(void)\r
+{\r
+       typedef struct {\r
+               char far *name;\r
+               int shapenum;\r
+               int x, y;\r
+       } creatureinfo;\r
+\r
+       static creatureinfo list[] = {\r
+               {"BIP",       BIPSHIPRSPR,        -2,  0},\r
+               {"BABOBBA",   BABOBBAR1SPR,        0,  0},\r
+               {"BLORB",     BLORB1SPR,          -2,  0},\r
+               {"GIK",       GIKWALKR1SPR,       -1,  0},\r
+               {"CEILICK",   CEILICK1SPR,         0,  0},\r
+               {"BLOOGLET",  RBLOOGLETWALKR1SPR, -2,  0},\r
+               {"BLOOGUARD", BLOOGUARDWALKL1SPR, -3, -1},\r
+               {"FLECT",     FLECTSTANDSPR,      -1,  0},\r
+               {"BOBBA",     BOBBAR1SPR,         -2,  0},\r
+               {"NOSPIKE",   NOSPIKESTANDSPR,    -2,  0},\r
+               {"ORBATRIX",  ORBATRIXR1SPR,      -2,  1},\r
+               {"FLEEX",     FLEEXWALKR1SPR,     -2,  0}\r
+       };\r
+\r
+       boolean correct;\r
+       char far *name;\r
+       char c;\r
+       char *ptr;\r
+       unsigned spriteheight, spritewidth;\r
+       int x, y;\r
+       int editwidth;\r
+       creatureinfo info;\r
+       char strbuf[16];\r
+\r
+       if (checkpassed)\r
+               return true;\r
+\r
+       correct = false;\r
+       if (listindex == -1)\r
+       {\r
+               _AH = 0x2C;     // get time\r
+               geninterrupt(0x21);\r
+               x = _CH;        // store hours\r
+               _AH = 0x2A;     // get date\r
+               geninterrupt(0x21);\r
+               y = _DL;        // store day\r
+\r
+               listindex = (x + y) % (int)(sizeof(list)/sizeof(creatureinfo));\r
+       }\r
+\r
+       CA_UpLevel();\r
+       info = list[listindex];\r
+       name = info.name;\r
+       CA_ClearMarks();\r
+       CA_MarkGrChunk(info.shapenum);\r
+       CA_CacheMarks(NULL);\r
+\r
+       VWB_Bar(0, 0, 320, 200, BackColor);\r
+       spritewidth = spritetable[info.shapenum - STARTSPRITES].width;\r
+       spriteheight = spritetable[info.shapenum - STARTSPRITES].height;\r
+       US_CenterWindow(30, (spriteheight+41)/8 + 1);\r
+       PrintY = WindowY + 2;\r
+       US_CPrint("What is the name of this creature?");\r
+\r
+       x = WindowX + (WindowW-spritewidth)/2 + info.x*8;\r
+       y = WindowY + 15;\r
+       if (info.shapenum == CEILICK1SPR)\r
+       {\r
+               y++;\r
+       }\r
+       else\r
+       {\r
+               y += info.y * 8;\r
+       }\r
+       VWB_DrawSprite(x, y, info.shapenum);\r
+\r
+       y = WindowY + WindowH - 16;\r
+       editwidth = 100;\r
+       x = WindowX + (WindowW - 100) / 2;\r
+       VWB_Bar(x, y, editwidth, 14, BLACK);\r
+       VWB_Bar(x+1, y+1, editwidth-2, 12, WHITE);\r
+       x += 2;\r
+       y += 2;\r
+       editwidth -= 8;\r
+       VW_UpdateScreen();\r
+\r
+       if (US_LineInput(x, y, strbuf, NULL, true, sizeof(strbuf), editwidth))\r
+       {\r
+               ptr = strbuf;\r
+               correct = true;\r
+               while (*name)\r
+               {\r
+                       c = *ptr;\r
+                       if ((islower(c)? _toupper(c) : c) != *name)\r
+                       {\r
+                               correct = false;\r
+                       }\r
+\r
+                       ptr++;\r
+                       name++;\r
+               }\r
+               if (*ptr)\r
+               {\r
+                       correct = false;\r
+               }\r
+\r
+               if (!correct)\r
+               {\r
+                       VWB_Bar(0, 0, 320, 200, BackColor);\r
+                       US_CenterWindow(35, 5);\r
+                       PrintY += 11;\r
+                       US_CPrint("Sorry, that's not quite right.");\r
+                       US_CPrint("Please check your manual and try again.");\r
+                       VW_UpdateScreen();\r
+                       IN_Ack();\r
+               }\r
+       }\r
+\r
+       VWB_Bar(0, 0, 320, 200, BackColor);\r
+       CA_DownLevel();\r
+       checkpassed = correct;\r
+       return correct;\r
+}\r
+\r
+#endif
\ No newline at end of file
diff --git a/16/keen456/KEEN4-6/ID_US_A.ASM b/16/keen456/KEEN4-6/ID_US_A.ASM
new file mode 100755 (executable)
index 0000000..15e6f12
--- /dev/null
@@ -0,0 +1,117 @@
+; Catacomb 3-D Source Code\r
+; Copyright (C) 1993-2014 Flat Rock Software\r
+;\r
+; This program is free software; you can redistribute it and/or modify\r
+; it under the terms of the GNU General Public License as published by\r
+; the Free Software Foundation; either version 2 of the License, or\r
+; (at your option) any later version.\r
+;\r
+; This program is distributed in the hope that it will be useful,\r
+; but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+; GNU General Public License for more details.\r
+;\r
+; You should have received a copy of the GNU General Public License along\r
+; with this program; if not, write to the Free Software Foundation, Inc.,\r
+; 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+\r
+IDEAL\r
+MODEL  MEDIUM,C\r
+\r
+;      Assembly portion of the User Mgr. This is just John Carmack's table\r
+;              driven pseudo-random number generator, and we put it in the User Mgr\r
+;              because we couldn't figure out where it should go\r
+\r
+\r
+;============================================================================\r
+;\r
+;                           RANDOM ROUTINES\r
+;\r
+;============================================================================\r
+\r
+       FARDATA\r
+\r
+rndindex       dw      ?\r
+\r
+rndtable db    0,   8, 109, 220, 222, 241, 149, 107,  75, 248, 254, 140,  16,  66\r
+       db   74,  21, 211,  47,  80, 242, 154,  27, 205, 128, 161,  89,  77,  36\r
+       db   95, 110,  85,  48, 212, 140, 211, 249,  22,  79, 200,  50,  28, 188\r
+       db   52, 140, 202, 120,  68, 145,  62,  70, 184, 190,  91, 197, 152, 224\r
+       db  149, 104,  25, 178, 252, 182, 202, 182, 141, 197,   4,  81, 181, 242\r
+       db  145,  42,  39, 227, 156, 198, 225, 193, 219,  93, 122, 175, 249,   0\r
+       db  175, 143,  70, 239,  46, 246, 163,  53, 163, 109, 168, 135,   2, 235\r
+       db   25,  92,  20, 145, 138,  77,  69, 166,  78, 176, 173, 212, 166, 113\r
+       db   94, 161,  41,  50, 239,  49, 111, 164,  70,  60,   2,  37, 171,  75\r
+       db  136, 156,  11,  56,  42, 146, 138, 229,  73, 146,  77,  61,  98, 196\r
+       db  135, 106,  63, 197, 195,  86,  96, 203, 113, 101, 170, 247, 181, 113\r
+       db   80, 250, 108,   7, 255, 237, 129, 226,  79, 107, 112, 166, 103, 241\r
+       db   24, 223, 239, 120, 198,  58,  60,  82, 128,   3, 184,  66, 143, 224\r
+       db  145, 224,  81, 206, 163,  45,  63,  90, 168, 114,  59,  33, 159,  95\r
+       db   28, 139, 123,  98, 125, 196,  15,  70, 194, 253,  54,  14, 109, 226\r
+       db   71,  17, 161,  93, 186,  87, 244, 138,  20,  52, 123, 251,  26,  36\r
+       db   17,  46,  52, 231, 232,  76,  31, 221,  84,  37, 216, 165, 212, 106\r
+       db  197, 242,  98,  43,  39, 175, 254, 145, 190,  84, 118, 222, 187, 136\r
+       db  120, 163, 236, 249\r
+\r
+\r
+       CODESEG\r
+\r
+LastRnd                dw      ?\r
+\r
+;=================================================\r
+;\r
+; void US_InitRndT (boolean randomize)\r
+; Init table based RND generator\r
+; if randomize is false, the counter is set to 0\r
+;\r
+;=================================================\r
+\r
+PROC   US_InitRndT randomize:word\r
+       uses    si,di\r
+       public  US_InitRndT\r
+\r
+       mov     ax,SEG rndtable\r
+       mov     es,ax\r
+       mov     ax,[randomize]\r
+       or      ax,ax\r
+       jne     @@timeit                ;if randomize is true, really random\r
+\r
+       mov     dx,0                    ;set to a definite value\r
+       jmp     @@setit\r
+\r
+@@timeit:\r
+       mov     ah,2ch\r
+       int     21h                     ;GetSystemTime\r
+       and     dx,0ffh\r
+\r
+@@setit:\r
+       mov     [es:rndindex],dx\r
+       ret\r
+\r
+ENDP\r
+\r
+;=================================================\r
+;\r
+; int US_RndT (void)\r
+; Return a random # between 0-255\r
+; Exit : AX = value\r
+;\r
+;=================================================\r
+PROC   US_RndT\r
+       public  US_RndT\r
+\r
+       mov     ax,SEG rndtable\r
+       mov     es,ax\r
+       mov     bx,[es:rndindex]\r
+       inc     bx\r
+       and     bx,0ffh\r
+       mov     [es:rndindex],bx\r
+       mov     al,[es:rndtable+BX]\r
+       xor     ah,ah\r
+\r
+       ret\r
+\r
+ENDP\r
+\r
+END\r
+\r
diff --git a/16/keen456/KEEN4-6/ID_VW.C b/16/keen456/KEEN4-6/ID_VW.C
new file mode 100755 (executable)
index 0000000..69b54c5
--- /dev/null
@@ -0,0 +1,1548 @@
+/* Reconstructed Commander Keen 4-6 Source Code\r
+ * Copyright (C) 2021 K1n9_Duk3\r
+ *\r
+ * This file is primarily based on:\r
+ * Catacomb 3-D Source Code\r
+ * Copyright (C) 1993-2014 Flat Rock Software\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License along\r
+ * with this program; if not, write to the Free Software Foundation, Inc.,\r
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+ */\r
+\r
+// ID_VW.C\r
+\r
+#include "ID_HEADS.H"\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                LOCAL CONSTANTS\r
+\r
+=============================================================================\r
+*/\r
+\r
+#define VIEWWIDTH              40\r
+\r
+#define PIXTOBLOCK             4               // 16 pixels to an update block\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                GLOBAL VARIABLES\r
+\r
+=============================================================================\r
+*/\r
+\r
+cardtype       videocard;              // set by VW_Startup\r
+grtype         grmode;                 // CGAgr, EGAgr, VGAgr\r
+\r
+unsigned       bufferofs;              // hidden area to draw to before displaying\r
+unsigned       displayofs;             // origin of the visable screen\r
+unsigned       panx,pany;              // panning adjustments inside port in pixels\r
+unsigned       pansx,pansy;    // panning adjustments inside port in screen\r
+                                                       // block limited pixel values (ie 0/8 for ega x)\r
+unsigned       panadjust;              // panx/pany adjusted by screen resolution\r
+\r
+unsigned       screenseg;              // normally 0xa000 / 0xb800\r
+unsigned       linewidth;\r
+unsigned       ylookup[VIRTUALHEIGHT];\r
+\r
+unsigned       fontnumber;             // 0 based font number for drawing\r
+\r
+boolean                screenfaded;\r
+\r
+pictabletype   _seg *pictable;\r
+pictabletype   _seg *picmtable;\r
+spritetabletype _seg *spritetable;\r
+\r
+int                    bordercolor;\r
+boolean                        nopan;\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                LOCAL VARIABLES\r
+\r
+=============================================================================\r
+*/\r
+\r
+void   VWL_MeasureString (char far *string, word *width, word *height,\r
+               fontstruct _seg *font);\r
+void   VWL_DrawCursor (void);\r
+void   VWL_EraseCursor (void);\r
+void   VWL_DBSetup (void);\r
+void   VWL_UpdateScreenBlocks (void);\r
+\r
+\r
+int                    bordercolor;\r
+int                    cursorvisible;\r
+int                    cursornumber,cursorwidth,cursorheight,cursorx,cursory;\r
+memptr         cursorsave;\r
+unsigned       cursorspot;\r
+\r
+//===========================================================================\r
+\r
+\r
+/*\r
+=======================\r
+=\r
+= VW_Startup\r
+=\r
+=======================\r
+*/\r
+\r
+static char *ParmStrings[] = {"HIDDENCARD","NOPAN",""};\r
+\r
+void   VW_Startup (void)\r
+{\r
+       int i,n;\r
+\r
+       asm     cld;\r
+\r
+       videocard = 0;\r
+\r
+       for (i = 1;i < _argc;i++)\r
+       {\r
+               n = US_CheckParm(_argv[i],ParmStrings);\r
+               if (n == 0)\r
+               {\r
+                       videocard = EGAcard;\r
+               }\r
+               else if (n == 1)\r
+               {\r
+                       nopan = true;\r
+               }\r
+       }\r
+\r
+       if (!videocard)\r
+               videocard = VW_VideoID ();\r
+\r
+#if GRMODE == EGAGR\r
+       grmode = EGAGR;\r
+       if (videocard != EGAcard && videocard != VGAcard)\r
+#ifdef KEEN\r
+Quit ("Improper video card!  If you really have an EGA/VGA card that I am not\n"\r
+         "detecting, use the -HIDDENCARD command line parameter!");\r
+#else\r
+Quit ("Improper video card!  If you really have an EGA/VGA card that I am not \n"\r
+         "detecting, use the -HIDDENCARD command line parameter!");\r
+#endif\r
+       EGAWRITEMODE(0);\r
+#endif\r
+\r
+#if GRMODE == CGAGR\r
+       grmode = CGAGR;\r
+       if (videocard < CGAcard || videocard > VGAcard)\r
+#ifdef KEEN\r
+Quit ("Improper video card!  If you really have a CGA card that I am not\n"\r
+         "detecting, use the -HIDDENCARD command line parameter!");\r
+#else\r
+Quit ("Improper video card!  If you really have a CGA card that I am not \n"\r
+         "detecting, use the -HIDDENCARD command line parameter!");\r
+#endif\r
+       MM_GetPtr (&(memptr)screenseg,0x10000l);        // grab 64k for floating screen\r
+#endif\r
+\r
+       cursorvisible = 0;\r
+}\r
+\r
+//===========================================================================\r
+\r
+/*\r
+=======================\r
+=\r
+= VW_Shutdown\r
+=\r
+=======================\r
+*/\r
+\r
+void   VW_Shutdown (void)\r
+{\r
+       VW_SetScreenMode (TEXTGR);\r
+#if GRMODE == EGAGR\r
+       VW_SetLineWidth (80);\r
+#endif\r
+}\r
+\r
+//===========================================================================\r
+\r
+/*\r
+========================\r
+=\r
+= VW_SetScreenMode\r
+= Call BIOS to set TEXT / CGAgr / EGAgr / VGAgr\r
+=\r
+========================\r
+*/\r
+\r
+void VW_SetScreenMode (int grmode)\r
+{\r
+       switch (grmode)\r
+       {\r
+         case TEXTGR:  _AX = 3;\r
+                 geninterrupt (0x10);\r
+#ifdef CAT3D\r
+                 screenseg=0xb000;\r
+#endif\r
+                 break;\r
+         case CGAGR: _AX = 4;\r
+                 geninterrupt (0x10);          // screenseg is actually a main mem buffer\r
+                 break;\r
+         case EGAGR: _AX = 0xd;\r
+                 geninterrupt (0x10);\r
+                 screenseg=0xa000;\r
+                 break;\r
+#ifdef VGAGAME\r
+         case VGAGR:{\r
+                 char extern VGAPAL;   // deluxepaint vga pallet .OBJ file\r
+                 void far *vgapal = &VGAPAL;\r
+                 SetCool256 ();                // custom 256 color mode\r
+                 screenseg=0xa000;\r
+                 _ES = FP_SEG(vgapal);\r
+                 _DX = FP_OFF(vgapal);\r
+                 _BX = 0;\r
+                 _CX = 0x100;\r
+                 _AX = 0x1012;\r
+                 geninterrupt(0x10);                   // set the deluxepaint pallet\r
+\r
+                 break;\r
+#endif\r
+       }\r
+       VW_SetLineWidth(SCREENWIDTH);\r
+}\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                       SCREEN FADES\r
+\r
+=============================================================================\r
+*/\r
+\r
+char colors[7][17]=\r
+{{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},\r
+ {0,0,0,0,0,0,0,0,0,1,2,3,4,5,6,7,0},\r
+ {0,0,0,0,0,0,0,0,0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f,0},\r
+ {0,1,2,3,4,5,6,7,0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f,0},\r
+ {0,1,2,3,4,5,6,7,0x1f,0x1f,0x1f,0x1f,0x1f,0x1f,0x1f,0x1f,0},\r
+ {0x1f,0x1f,0x1f,0x1f,0x1f,0x1f,0x1f,0x1f,0x1f,0x1f,0x1f,0x1f,0x1f,0x1f,0x1f,0x1f,0x1f}};\r
+\r
+\r
+void VW_ColorBorder (int color)\r
+{\r
+       _AH=0x10;\r
+       _AL=1;\r
+       _BH=color;\r
+       geninterrupt (0x10);\r
+       bordercolor = color;\r
+}\r
+\r
+void VW_SetPalette(byte *palette)\r
+{\r
+       byte    p;\r
+       word    i;\r
+\r
+       for (i = 0;i < 15;i++)\r
+       {\r
+               p = palette[i];\r
+               colors[0][i] = 0;\r
+               colors[1][i] = (p > 0x10)? (p & 0x0f) : 0;\r
+               colors[2][i] = (p > 0x10)? p : 0;\r
+               colors[3][i] = p;\r
+               colors[4][i] = (p > 0x10)? 0x1f : p;\r
+               colors[5][i] = 0x1f;\r
+       }\r
+}\r
+\r
+void VW_SetDefaultColors(void)\r
+{\r
+#if GRMODE == EGAGR\r
+       colors[3][16] = bordercolor;\r
+       _ES=FP_SEG(&colors[3]);\r
+       _DX=FP_OFF(&colors[3]);\r
+       _AX=0x1002;\r
+       geninterrupt(0x10);\r
+       screenfaded = false;\r
+#endif\r
+}\r
+\r
+\r
+void VW_FadeOut(void)\r
+{\r
+#if GRMODE == EGAGR\r
+       int i;\r
+\r
+       for (i=3;i>=0;i--)\r
+       {\r
+         colors[i][16] = bordercolor;\r
+         _ES=FP_SEG(&colors[i]);\r
+         _DX=FP_OFF(&colors[i]);\r
+         _AX=0x1002;\r
+         geninterrupt(0x10);\r
+         VW_WaitVBL(6);\r
+       }\r
+       screenfaded = true;\r
+#endif\r
+}\r
+\r
+\r
+void VW_FadeIn(void)\r
+{\r
+#if GRMODE == EGAGR\r
+       int i;\r
+\r
+       for (i=0;i<4;i++)\r
+       {\r
+         colors[i][16] = bordercolor;\r
+         _ES=FP_SEG(&colors[i]);\r
+         _DX=FP_OFF(&colors[i]);\r
+         _AX=0x1002;\r
+         geninterrupt(0x10);\r
+         VW_WaitVBL(6);\r
+       }\r
+       screenfaded = false;\r
+#endif\r
+}\r
+\r
+void VW_FadeUp(void)\r
+{\r
+#if GRMODE == EGAGR\r
+       int i;\r
+\r
+       for (i=3;i<6;i++)\r
+       {\r
+         colors[i][16] = bordercolor;\r
+         _ES=FP_SEG(&colors[i]);\r
+         _DX=FP_OFF(&colors[i]);\r
+         _AX=0x1002;\r
+         geninterrupt(0x10);\r
+         VW_WaitVBL(6);\r
+       }\r
+       screenfaded = true;\r
+#endif\r
+}\r
+\r
+void VW_FadeDown(void)\r
+{\r
+#if GRMODE == EGAGR\r
+       int i;\r
+\r
+       for (i=5;i>2;i--)\r
+       {\r
+         colors[i][16] = bordercolor;\r
+         _ES=FP_SEG(&colors[i]);\r
+         _DX=FP_OFF(&colors[i]);\r
+         _AX=0x1002;\r
+         geninterrupt(0x10);\r
+         VW_WaitVBL(6);\r
+       }\r
+       screenfaded = false;\r
+#endif\r
+}\r
+\r
+\r
+/*\r
+========================\r
+=\r
+= VW_SetAtrReg\r
+=\r
+= Sets an attribute (pallete / border) register\r
+= Does NOT vsync!\r
+=\r
+========================\r
+*/\r
+\r
+void VW_SetAtrReg (int reg, int value)\r
+{\r
+  asm  cli\r
+  asm  mov     dx,STATUS_REGISTER_1\r
+  asm  in      al,dx\r
+  asm  mov     dx,ATR_INDEX\r
+\r
+  asm  mov     al,BYTE PTR [reg]\r
+  asm  out     dx,al\r
+  asm  mov     al,BYTE PTR [value]\r
+  asm  out     dx,al\r
+  asm  mov     dx,0x3da\r
+  asm  in      al,dx\r
+  asm  mov     dx,ATR_INDEX\r
+  asm  mov     al,0x20\r
+  asm  out     dx,al\r
+  asm  sti\r
+}\r
+\r
+\r
+\r
+//===========================================================================\r
+\r
+/*\r
+====================\r
+=\r
+= VW_SetLineWidth\r
+=\r
+= Must be an even number of bytes\r
+=\r
+====================\r
+*/\r
+\r
+void VW_SetLineWidth (int width)\r
+{\r
+  int i,offset;\r
+\r
+#if GRMODE == EGAGR\r
+//\r
+// set wide virtual screen\r
+//\r
+asm    mov     dx,CRTC_INDEX\r
+asm    mov     al,CRTC_OFFSET\r
+asm mov        ah,[BYTE PTR width]\r
+asm    shr     ah,1\r
+asm    out     dx,ax\r
+#endif\r
+\r
+//\r
+// set up lookup tables\r
+//\r
+  linewidth = width;\r
+\r
+  offset = 0;\r
+\r
+  for (i=0;i<VIRTUALHEIGHT;i++)\r
+  {\r
+       ylookup[i]=offset;\r
+       offset += width;\r
+  }\r
+}\r
+\r
+\r
+//===========================================================================\r
+\r
+/*\r
+====================\r
+=\r
+= VW_SetSplitScreen\r
+=\r
+====================\r
+*/\r
+#ifdef CAT3D\r
+void VW_SetSplitScreen (int linenum)\r
+{\r
+       VW_WaitVBL (1);\r
+       if (videocard==VGAcard)\r
+               linenum=linenum*2-1;\r
+       outportb (CRTC_INDEX,CRTC_LINECOMPARE);\r
+       outportb (CRTC_INDEX+1,linenum % 256);\r
+       outportb (CRTC_INDEX,CRTC_OVERFLOW);\r
+       outportb (CRTC_INDEX+1, 1+16*(linenum/256));\r
+       if (videocard==VGAcard)\r
+       {\r
+               outportb (CRTC_INDEX,CRTC_MAXSCANLINE);\r
+               outportb (CRTC_INDEX+1,inportb(CRTC_INDEX+1) & (255-64));\r
+       }\r
+}\r
+#endif\r
+//===========================================================================\r
+\r
+/*\r
+====================\r
+=\r
+= VW_ClearVideo\r
+=\r
+====================\r
+*/\r
+\r
+void   VW_ClearVideo (int color)\r
+{\r
+#if GRMODE == EGAGR\r
+       EGAWRITEMODE(2);\r
+       EGAMAPMASK(15);\r
+\r
+       color = (color << 8) & color;   //BUG: color is always 0 after this\r
+#endif\r
+\r
+#if GRMODE == CGAGR\r
+       color = (color << 12) & (color << 8) & (color << 4) & color;    //BUG: color is always 0 after this\r
+#endif\r
+\r
+       VW_WaitVBL(1);\r
+\r
+asm    mov     es, screenseg;\r
+asm    mov     di, displayofs;\r
+asm    and     di, not 1;\r
+asm    mov     cx, 8000h;\r
+asm    mov     ax, color;\r
+asm    rep stosw;\r
+\r
+#if GRMODE == EGAGR\r
+       EGAWRITEMODE(0);\r
+#endif\r
+}\r
+\r
+//===========================================================================\r
+\r
+#if NUMPICS>0\r
+\r
+/*\r
+====================\r
+=\r
+= VW_DrawPic\r
+=\r
+= X in bytes, y in pixels, chunknum is the #defined picnum\r
+=\r
+====================\r
+*/\r
+\r
+void VW_DrawPic(unsigned x, unsigned y, unsigned chunknum)\r
+{\r
+       int     picnum = chunknum - STARTPICS;\r
+       memptr source;\r
+       unsigned dest,width,height;\r
+\r
+       source = grsegs[chunknum];\r
+       dest = ylookup[y]+x+bufferofs;\r
+       width = pictable[picnum].width;\r
+       height = pictable[picnum].height;\r
+\r
+       VW_MemToScreen(source,dest,width,height);\r
+}\r
+\r
+\r
+#endif\r
+\r
+#if NUMPICM>0\r
+\r
+/*\r
+====================\r
+=\r
+= VW_DrawMPic\r
+=\r
+= X in bytes, y in pixels, chunknum is the #defined picnum\r
+=\r
+====================\r
+*/\r
+\r
+void VW_DrawMPic(unsigned x, unsigned y, unsigned chunknum)\r
+{\r
+       int     picnum = chunknum - STARTPICM;\r
+       memptr source;\r
+       unsigned dest,width,height;\r
+\r
+       source = grsegs[chunknum];\r
+       dest = ylookup[y]+x+bufferofs;\r
+       width = picmtable[picnum].width;\r
+       height = picmtable[picnum].height;\r
+\r
+       VW_MaskBlock(source,0,dest,width,height,width*height);\r
+}\r
+\r
+void VW_ClipDrawMPic(unsigned x, int y, unsigned chunknum)\r
+{\r
+       int     picnum = chunknum - STARTPICM;\r
+       memptr source;\r
+       unsigned dest,width,ofs,plane;\r
+       int             height;\r
+\r
+       source = grsegs[chunknum];\r
+       width = picmtable[picnum].width;\r
+       height = picmtable[picnum].height;\r
+       plane = width*height;\r
+\r
+       ofs = 0;\r
+       if (y<0)\r
+       {\r
+               ofs= -y*width;\r
+               height+=y;\r
+               y=0;\r
+       }\r
+       else if (y+height>216)\r
+       {\r
+               height-=(y-216);\r
+       }\r
+       dest = ylookup[y]+x+bufferofs;\r
+       if (height<1)\r
+               return;\r
+\r
+       VW_MaskBlock(source,ofs,dest,width,height,plane);\r
+}\r
+\r
+\r
+#endif\r
+\r
+//===========================================================================\r
+\r
+#if NUMSPRITES>0\r
+\r
+/*\r
+====================\r
+=\r
+= VW_DrawSprite\r
+=\r
+= X and Y in pixels, it will match the closest shift possible\r
+=\r
+= To do:\r
+= Add vertical clipping!\r
+= Make the shifts act as center points, rather than break points\r
+=\r
+====================\r
+*/\r
+\r
+void VW_DrawSprite(int x, int y, unsigned chunknum)\r
+{\r
+       spritetabletype far *spr;\r
+       spritetype _seg *block;\r
+       unsigned        dest,shift;\r
+\r
+       spr = &spritetable[chunknum-STARTSPRITES];\r
+       block = (spritetype _seg *)grsegs[chunknum];\r
+\r
+       y+=spr->orgy>>G_P_SHIFT;\r
+       x+=spr->orgx>>G_P_SHIFT;\r
+\r
+#if GRMODE == EGAGR\r
+       shift = (x&7)/2;\r
+#endif\r
+#if GRMODE == CGAGR\r
+       shift = 0;\r
+#endif\r
+\r
+       dest = bufferofs + ylookup[y];\r
+       if (x>=0)\r
+               dest += x/SCREENXDIV;\r
+       else\r
+               dest += (x+1)/SCREENXDIV;\r
+\r
+       VW_MaskBlock (block,block->sourceoffset[shift],dest,\r
+               block->width[shift],spr->height,block->planesize[shift]);\r
+}\r
+\r
+#endif\r
+\r
+\r
+/*\r
+==================\r
+=\r
+= VW_Hlin\r
+=\r
+==================\r
+*/\r
+\r
+\r
+#if GRMODE == EGAGR\r
+\r
+unsigned char leftmask[8] = {0xff,0x7f,0x3f,0x1f,0xf,7,3,1};\r
+unsigned char rightmask[8] = {0x80,0xc0,0xe0,0xf0,0xf8,0xfc,0xfe,0xff};\r
+\r
+void VW_Hlin(unsigned xl, unsigned xh, unsigned y, unsigned color)\r
+{\r
+  unsigned dest,xlb,xhb,maskleft,maskright,mid;\r
+\r
+       xlb=xl/8;\r
+       xhb=xh/8;\r
+\r
+       EGAWRITEMODE(2);\r
+       EGAMAPMASK(15);\r
+\r
+       maskleft = leftmask[xl&7];\r
+       maskright = rightmask[xh&7];\r
+\r
+       mid = xhb-xlb-1;\r
+       dest = bufferofs+ylookup[y]+xlb;\r
+\r
+  if (xlb==xhb)\r
+  {\r
+  //\r
+  // entire line is in one byte\r
+  //\r
+\r
+       maskleft&=maskright;\r
+\r
+       asm     mov     es,[screenseg]\r
+       asm     mov     di,[dest]\r
+\r
+       asm     mov     dx,GC_INDEX\r
+       asm     mov     al,GC_BITMASK\r
+       asm     mov     ah,[BYTE PTR maskleft]\r
+       asm     out     dx,ax           // mask off pixels\r
+\r
+       asm     mov     al,[BYTE PTR color]\r
+       asm     xchg    al,[es:di]      // load latches and write pixels\r
+\r
+       goto    done;\r
+  }\r
+\r
+asm    mov     es,[screenseg]\r
+asm    mov     di,[dest]\r
+asm    mov     dx,GC_INDEX\r
+asm    mov     bh,[BYTE PTR color]\r
+\r
+//\r
+// draw left side\r
+//\r
+asm    mov     al,GC_BITMASK\r
+asm    mov     ah,[BYTE PTR maskleft]\r
+asm    out     dx,ax           // mask off pixels\r
+\r
+asm    mov     al,bh\r
+asm    mov     bl,[es:di]      // load latches\r
+asm    stosb\r
+\r
+//\r
+// draw middle\r
+//\r
+asm    mov     ax,GC_BITMASK + 255*256\r
+asm    out     dx,ax           // no masking\r
+\r
+asm    mov     al,bh\r
+asm    mov     cx,[mid]\r
+asm    rep     stosb\r
+\r
+//\r
+// draw right side\r
+//\r
+asm    mov     al,GC_BITMASK\r
+asm    mov     ah,[BYTE PTR maskright]\r
+asm    out     dx,ax           // mask off pixels\r
+\r
+asm    xchg    bh,[es:di]      // load latches and write pixels\r
+\r
+done:\r
+       EGABITMASK(255);\r
+       EGAWRITEMODE(0);\r
+}\r
+#endif\r
+\r
+\r
+#if GRMODE == CGAGR\r
+\r
+unsigned char pixmask[4] = {0xc0,0x30,0x0c,0x03};\r
+unsigned char leftmask[4] = {0xff,0x3f,0x0f,0x03};\r
+unsigned char rightmask[4] = {0xc0,0xf0,0xfc,0xff};\r
+unsigned char colorbyte[4] = {0,0x55,0xaa,0xff};\r
+\r
+//\r
+// could be optimized for rep stosw\r
+//\r
+void VW_Hlin(unsigned xl, unsigned xh, unsigned y, unsigned color)\r
+{\r
+       unsigned dest,xlb,xhb,mid;\r
+       byte maskleft,maskright;\r
+\r
+       color = colorbyte[color];       // expand 2 color bits to 8\r
+\r
+       xlb=xl/4;\r
+       xhb=xh/4;\r
+\r
+       maskleft = leftmask[xl&3];\r
+       maskright = rightmask[xh&3];\r
+\r
+       mid = xhb-xlb-1;\r
+       dest = bufferofs+ylookup[y]+xlb;\r
+asm    mov     es,[screenseg]\r
+\r
+       if (xlb==xhb)\r
+       {\r
+       //\r
+       // entire line is in one byte\r
+       //\r
+               maskleft&=maskright;\r
+\r
+               asm     mov     ah,[maskleft]\r
+               asm     mov     bl,[BYTE PTR color]\r
+               asm     and     bl,[maskleft]\r
+               asm     not     ah\r
+\r
+               asm     mov     di,[dest]\r
+\r
+               asm     mov     al,[es:di]\r
+               asm     and     al,ah                   // mask out pixels\r
+               asm     or      al,bl                   // or in color\r
+               asm     mov     [es:di],al\r
+               return;\r
+       }\r
+\r
+asm    mov     di,[dest]\r
+asm    mov     bh,[BYTE PTR color]\r
+\r
+//\r
+// draw left side\r
+//\r
+asm    mov     ah,[maskleft]\r
+asm    mov     bl,bh\r
+asm    and     bl,[maskleft]\r
+asm    not     ah\r
+asm    mov     al,[es:di]\r
+asm    and     al,ah                   // mask out pixels\r
+asm    or      al,bl                   // or in color\r
+asm    stosb\r
+\r
+//\r
+// draw middle\r
+//\r
+asm    mov     al,bh\r
+asm    mov     cx,[mid]\r
+asm    rep     stosb\r
+\r
+//\r
+// draw right side\r
+//\r
+asm    mov     ah,[maskright]\r
+asm    mov     bl,bh\r
+asm    and     bl,[maskright]\r
+asm    not     ah\r
+asm    mov     al,[es:di]\r
+asm    and     al,ah                   // mask out pixels\r
+asm    or      al,bl                   // or in color\r
+asm    stosb\r
+}\r
+#endif\r
+\r
+\r
+/*\r
+==================\r
+=\r
+= VW_Bar\r
+=\r
+= Pixel addressable block fill routine\r
+=\r
+==================\r
+*/\r
+\r
+#if GRMODE == CGAGR\r
+\r
+void VW_Bar (unsigned x, unsigned y, unsigned width, unsigned height,\r
+       unsigned color)\r
+{\r
+       unsigned xh = x+width-1;\r
+\r
+       while (height--)\r
+               VW_Hlin (x,xh,y++,color);\r
+}\r
+\r
+#endif\r
+\r
+\r
+#if    GRMODE == EGAGR\r
+\r
+void VW_Bar (unsigned x, unsigned y, unsigned width, unsigned height,\r
+       unsigned color)\r
+{\r
+       unsigned dest,xh,xlb,xhb,maskleft,maskright,mid;\r
+\r
+       xh = x+width-1;\r
+       xlb=x/8;\r
+       xhb=xh/8;\r
+\r
+       EGAWRITEMODE(2);\r
+       EGAMAPMASK(15);\r
+\r
+       maskleft = leftmask[x&7];\r
+       maskright = rightmask[xh&7];\r
+\r
+       mid = xhb-xlb-1;\r
+       dest = bufferofs+ylookup[y]+xlb;\r
+\r
+       if (xlb==xhb)\r
+       {\r
+       //\r
+       // entire line is in one byte\r
+       //\r
+\r
+               maskleft&=maskright;\r
+\r
+       asm     mov     es,[screenseg]\r
+       asm     mov     di,[dest]\r
+\r
+       asm     mov     dx,GC_INDEX\r
+       asm     mov     al,GC_BITMASK\r
+       asm     mov     ah,[BYTE PTR maskleft]\r
+       asm     out     dx,ax           // mask off pixels\r
+\r
+       asm     mov     ah,[BYTE PTR color]\r
+       asm     mov     dx,[linewidth]\r
+yloop1:\r
+       asm     mov     al,ah\r
+       asm     xchg    al,[es:di]      // load latches and write pixels\r
+       asm     add     di,dx                   // down to next line\r
+       asm     dec     [height]\r
+       asm     jnz     yloop1\r
+\r
+               goto    done;\r
+       }\r
+\r
+asm    mov     es,[screenseg]\r
+asm    mov     di,[dest]\r
+asm    mov     bh,[BYTE PTR color]\r
+asm    mov     dx,GC_INDEX\r
+asm    mov     si,[linewidth]\r
+asm    sub     si,[mid]                        // add to di at end of line to get to next scan\r
+asm    dec     si\r
+\r
+//\r
+// draw left side\r
+//\r
+yloop2:\r
+asm    mov     al,GC_BITMASK\r
+asm    mov     ah,[BYTE PTR maskleft]\r
+asm    out     dx,ax           // mask off pixels\r
+\r
+asm    mov     al,bh\r
+asm    mov     bl,[es:di]      // load latches\r
+asm    stosb\r
+\r
+//\r
+// draw middle\r
+//\r
+asm    mov     ax,GC_BITMASK + 255*256\r
+asm    out     dx,ax           // no masking\r
+\r
+asm    mov     al,bh\r
+asm    mov     cx,[mid]\r
+asm    rep     stosb\r
+\r
+//\r
+// draw right side\r
+//\r
+asm    mov     al,GC_BITMASK\r
+asm    mov     ah,[BYTE PTR maskright]\r
+asm    out     dx,ax           // mask off pixels\r
+\r
+asm    mov     al,bh\r
+asm    xchg    al,[es:di]      // load latches and write pixels\r
+\r
+asm    add     di,si           // move to start of next line\r
+asm    dec     [height]\r
+asm    jnz     yloop2\r
+\r
+done:\r
+       EGABITMASK(255);\r
+       EGAWRITEMODE(0);\r
+}\r
+\r
+#endif\r
+\r
+//==========================================================================\r
+\r
+/*\r
+==================\r
+=\r
+= VW_MeasureString\r
+=\r
+==================\r
+*/\r
+\r
+#if NUMFONT+NUMFONTM>0\r
+void\r
+VWL_MeasureString (char far *string, word *width, word *height, fontstruct _seg *font)\r
+{\r
+       *height = font->height;\r
+       for (*width = 0;*string;string++)\r
+               *width += font->width[*((byte far *)string)];   // proportional width\r
+}\r
+\r
+void   VW_MeasurePropString (char far *string, word *width, word *height)\r
+{\r
+       VWL_MeasureString(string,width,height,(fontstruct _seg *)grsegs[STARTFONT+fontnumber]);\r
+}\r
+\r
+void   VW_MeasureMPropString  (char far *string, word *width, word *height)\r
+{\r
+       VWL_MeasureString(string,width,height,(fontstruct _seg *)grsegs[STARTFONTM+fontnumber]);\r
+}\r
+\r
+\r
+#endif\r
+\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                       CGA stuff\r
+\r
+=============================================================================\r
+*/\r
+\r
+#if GRMODE == CGAGR\r
+\r
+#define CGACRTCWIDTH   40\r
+\r
+/*\r
+==========================\r
+=\r
+= VW_CGAFullUpdate\r
+=\r
+==========================\r
+*/\r
+\r
+void VW_CGAFullUpdate (void)\r
+{\r
+       byte    *update;\r
+       boolean halftile;\r
+       unsigned        x,y,middlerows,middlecollumns;\r
+\r
+       displayofs = bufferofs+panadjust;\r
+\r
+asm    mov     ax,0xb800\r
+asm    mov     es,ax\r
+\r
+asm    mov     si,[displayofs]\r
+asm    xor     di,di\r
+\r
+asm    mov     bx,100                          // pairs of scan lines to copy\r
+asm    mov     dx,[linewidth]\r
+asm    sub     dx,80\r
+\r
+asm    mov     ds,[screenseg]\r
+asm    test    si,1\r
+asm    jz      evenblock\r
+\r
+//\r
+// odd source\r
+//\r
+asm    mov     ax,39                           // words accross screen\r
+copytwolineso:\r
+asm    movsb\r
+asm    mov     cx,ax\r
+asm    rep     movsw\r
+asm    movsb\r
+asm    add     si,dx\r
+asm    add     di,0x2000-80            // go to the interlaced bank\r
+asm    movsb\r
+asm    mov     cx,ax\r
+asm    rep     movsw\r
+asm    movsb\r
+asm    add     si,dx\r
+asm    sub     di,0x2000                       // go to the non interlaced bank\r
+\r
+asm    dec     bx\r
+asm    jnz     copytwolineso\r
+asm    jmp     blitdone\r
+\r
+//\r
+// even source\r
+//\r
+evenblock:\r
+asm    mov     ax,40                           // words accross screen\r
+copytwolines:\r
+asm    mov     cx,ax\r
+asm    rep     movsw\r
+asm    add     si,dx\r
+asm    add     di,0x2000-80            // go to the interlaced bank\r
+asm    mov     cx,ax\r
+asm    rep     movsw\r
+asm    add     si,dx\r
+asm    sub     di,0x2000                       // go to the non interlaced bank\r
+\r
+asm    dec     bx\r
+asm    jnz     copytwolines\r
+\r
+blitdone:\r
+asm    mov     ax,ss\r
+asm    mov     ds,ax\r
+asm    mov     es,ax\r
+\r
+asm    xor     ax,ax                           // clear out the update matrix\r
+asm    mov     cx,UPDATEWIDE*UPDATEHIGH/2\r
+\r
+asm    mov     di,[baseupdateptr]\r
+asm    rep     stosw\r
+\r
+       updateptr = baseupdateptr;\r
+       *(unsigned *)(updateptr + UPDATEWIDE*PORTTILESHIGH) = UPDATETERMINATE;\r
+}\r
+\r
+\r
+#endif\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                          CURSOR ROUTINES\r
+\r
+These only work in the context of the double buffered update routines\r
+\r
+=============================================================================\r
+*/\r
+\r
+/*\r
+====================\r
+=\r
+= VWL_DrawCursor\r
+=\r
+= Background saves, then draws the cursor at cursorspot\r
+=\r
+====================\r
+*/\r
+\r
+void VWL_DrawCursor (void)\r
+{\r
+       cursorspot = bufferofs + ylookup[cursory+pansy]+(cursorx+pansx)/SCREENXDIV;\r
+       VW_ScreenToMem(cursorspot,cursorsave,cursorwidth,cursorheight);\r
+       VWB_DrawSprite(cursorx,cursory,cursornumber);\r
+}\r
+\r
+\r
+//==========================================================================\r
+\r
+\r
+/*\r
+====================\r
+=\r
+= VWL_EraseCursor\r
+=\r
+====================\r
+*/\r
+\r
+void VWL_EraseCursor (void)\r
+{\r
+       VW_MemToScreen(cursorsave,cursorspot,cursorwidth,cursorheight);\r
+       VW_MarkUpdateBlock ((cursorx+pansx)&SCREENXMASK,cursory+pansy,\r
+               ( (cursorx+pansx)&SCREENXMASK)+cursorwidth*SCREENXDIV-1,\r
+               cursory+pansy+cursorheight-1);\r
+}\r
+\r
+\r
+//==========================================================================\r
+\r
+\r
+/*\r
+====================\r
+=\r
+= VW_ShowCursor\r
+=\r
+====================\r
+*/\r
+\r
+void VW_ShowCursor (void)\r
+{\r
+       cursorvisible++;\r
+}\r
+\r
+\r
+//==========================================================================\r
+\r
+/*\r
+====================\r
+=\r
+= VW_HideCursor\r
+=\r
+====================\r
+*/\r
+\r
+void VW_HideCursor (void)\r
+{\r
+       cursorvisible--;\r
+}\r
+\r
+//==========================================================================\r
+\r
+/*\r
+====================\r
+=\r
+= VW_MoveCursor\r
+=\r
+====================\r
+*/\r
+#define MAXCURSORX     (319-24)\r
+#define MAXCURSORY     (199-24)\r
+\r
+void VW_MoveCursor (int x, int y)\r
+{\r
+#ifdef CAT3D\r
+       if (x>MAXCURSORX)\r
+               x=MAXCURSORX;\r
+       if (y>MAXCURSORY)\r
+               y=MAXCURSORY;                   // catacombs hack to keep cursor on screen\r
+#endif\r
+\r
+       cursorx = x;\r
+       cursory = y;\r
+}\r
+\r
+//==========================================================================\r
+\r
+/*\r
+====================\r
+=\r
+= VW_SetCursor\r
+=\r
+= Load in a sprite to be used as a cursor, and allocate background save space\r
+=\r
+====================\r
+*/\r
+\r
+void VW_SetCursor (int spritenum)\r
+{\r
+       VW_FreeCursor ();\r
+\r
+       cursornumber = spritenum;\r
+\r
+       CA_CacheGrChunk (spritenum);\r
+       MM_SetLock (&grsegs[spritenum],true);\r
+\r
+       cursorwidth = spritetable[spritenum-STARTSPRITES].width+1;\r
+       cursorheight = spritetable[spritenum-STARTSPRITES].height;\r
+\r
+       MM_GetPtr (&cursorsave,cursorwidth*cursorheight*5);\r
+       MM_SetLock (&cursorsave,true);\r
+}\r
+\r
+\r
+/*\r
+====================\r
+=\r
+= VW_FreeCursor\r
+=\r
+= Frees the memory used by the cursor and its background save\r
+=\r
+====================\r
+*/\r
+\r
+void VW_FreeCursor (void)\r
+{\r
+       if (cursornumber)\r
+       {\r
+               MM_SetLock (&grsegs[cursornumber],false);\r
+               MM_SetPurge (&grsegs[cursornumber],3);\r
+               MM_SetLock (&cursorsave,false);\r
+               MM_FreePtr (&cursorsave);\r
+               cursornumber = 0;\r
+       }\r
+}\r
+\r
+\r
+/*\r
+=============================================================================\r
+\r
+                               Double buffer management routines\r
+\r
+=============================================================================\r
+*/\r
+\r
+/*\r
+======================\r
+=\r
+= VW_InitDoubleBuffer\r
+=\r
+======================\r
+*/\r
+\r
+void VW_InitDoubleBuffer (void)\r
+{\r
+#if GRMODE == EGAGR\r
+       VW_SetScreen (displayofs+panadjust,0);                  // no pel pan\r
+#endif\r
+}\r
+\r
+\r
+/*\r
+======================\r
+=\r
+= VW_FixRefreshBuffer\r
+=\r
+= Copies the view page to the buffer page on page flipped refreshes to\r
+= avoid a one frame shear around pop up windows\r
+=\r
+======================\r
+*/\r
+\r
+void VW_FixRefreshBuffer (void)\r
+{\r
+#if GRMODE == EGAGR\r
+       VW_ScreenToScreen (displayofs,bufferofs,PORTTILESWIDE*4*CHARWIDTH,\r
+               (PORTTILESHIGH-1)*16);\r
+#endif\r
+}\r
+\r
+\r
+/*\r
+======================\r
+=\r
+= VW_QuitDoubleBuffer\r
+=\r
+======================\r
+*/\r
+\r
+void VW_QuitDoubleBuffer (void)\r
+{\r
+}\r
+\r
+\r
+/*\r
+=======================\r
+=\r
+= VW_MarkUpdateBlock\r
+=\r
+= Takes a pixel bounded block and marks the tiles in bufferblocks\r
+= Returns 0 if the entire block is off the buffer screen\r
+=\r
+=======================\r
+*/\r
+\r
+int VW_MarkUpdateBlock (int x1, int y1, int x2, int y2)\r
+{\r
+       int     x,y,xt1,yt1,xt2,yt2,nextline;\r
+       byte *mark;\r
+\r
+       xt1 = x1>>PIXTOBLOCK;\r
+       yt1 = y1>>PIXTOBLOCK;\r
+\r
+       xt2 = x2>>PIXTOBLOCK;\r
+       yt2 = y2>>PIXTOBLOCK;\r
+\r
+       if (xt1<0)\r
+               xt1=0;\r
+       else if (xt1>=UPDATEWIDE-1)\r
+               return 0;\r
+\r
+       if (yt1<0)\r
+               yt1=0;\r
+       else if (yt1>UPDATEHIGH)\r
+               return 0;\r
+\r
+       if (xt2<0)\r
+               return 0;\r
+       else if (xt2>=UPDATEWIDE-1)\r
+               xt2 = UPDATEWIDE-2;\r
+\r
+       if (yt2<0)\r
+               return 0;\r
+       else if (yt2>=UPDATEHIGH)\r
+               yt2 = UPDATEHIGH-1;\r
+\r
+       mark = updateptr + uwidthtable[yt1] + xt1;\r
+       nextline = UPDATEWIDE - (xt2-xt1) - 1;\r
+\r
+       for (y=yt1;y<=yt2;y++)\r
+       {\r
+               for (x=xt1;x<=xt2;x++)\r
+                       *mark++ = 1;                    // this tile will need to be updated\r
+\r
+               mark += nextline;\r
+       }\r
+\r
+       return 1;\r
+}\r
+\r
+\r
+/*\r
+===========================\r
+=\r
+= VW_UpdateScreen\r
+=\r
+= Updates any changed areas of the double buffer and displays the cursor\r
+=\r
+===========================\r
+*/\r
+\r
+void VW_UpdateScreen (void)\r
+{\r
+       if (cursorvisible>0)\r
+               VWL_DrawCursor();\r
+\r
+#if GRMODE == EGAGR\r
+       VWL_UpdateScreenBlocks();\r
+#endif\r
+#if GRMODE == CGAGR\r
+       VW_CGAFullUpdate();\r
+#endif\r
+\r
+       if (cursorvisible>0)\r
+               VWL_EraseCursor();\r
+}\r
+\r
+\r
+\r
+void VWB_DrawTile8 (int x, int y, int tile)\r
+{\r
+       x+=pansx;\r
+       y+=pansy;\r
+       if (VW_MarkUpdateBlock (x&SCREENXMASK,y,(x&SCREENXMASK)+7,y+7))\r
+               VW_DrawTile8 (x/SCREENXDIV,y,tile);\r
+}\r
+\r
+void VWB_DrawTile8M (int x, int y, int tile)\r
+{\r
+       int xb;\r
+\r
+       x+=pansx;\r
+       y+=pansy;\r
+       xb = x/SCREENXDIV;                      // use intermediate because VW_DT8M is macro\r
+       if (VW_MarkUpdateBlock (x&SCREENXMASK,y,(x&SCREENXMASK)+7,y+7))\r
+               VW_DrawTile8M (xb,y,tile);\r
+}\r
+\r
+void VWB_DrawTile16 (int x, int y, int tile)\r
+{\r
+       x+=pansx;\r
+       y+=pansy;\r
+       if (VW_MarkUpdateBlock (x&SCREENXMASK,y,(x&SCREENXMASK)+15,y+15))\r
+               VW_DrawTile16 (x/SCREENXDIV,y,tile);\r
+}\r
+\r
+void VWB_DrawTile16M (int x, int y, int tile)\r
+{\r
+       int xb;\r
+\r
+       x+=pansx;\r
+       y+=pansy;\r
+       xb = x/SCREENXDIV;              // use intermediate because VW_DT16M is macro\r
+       if (VW_MarkUpdateBlock (x&SCREENXMASK,y,(x&SCREENXMASK)+15,y+15))\r
+               VW_DrawTile16M (xb,y,tile);\r
+}\r
+\r
+#if NUMPICS\r
+void VWB_DrawPic (int x, int y, int chunknum)\r
+{\r
+// mostly copied from drawpic\r
+       int     picnum = chunknum - STARTPICS;\r
+       memptr source;\r
+       unsigned dest,width,height;\r
+\r
+       x+=pansx;\r
+       y+=pansy;\r
+       x/= SCREENXDIV;\r
+\r
+       source = grsegs[chunknum];\r
+       dest = ylookup[y]+x+bufferofs;\r
+       width = pictable[picnum].width;\r
+       height = pictable[picnum].height;\r
+\r
+       if (VW_MarkUpdateBlock (x*SCREENXDIV,y,(x+width)*SCREENXDIV-1,y+height-1))\r
+               VW_MemToScreen(source,dest,width,height);\r
+}\r
+#endif\r
+\r
+#if NUMPICM>0\r
+void VWB_DrawMPic(int x, int y, int chunknum)\r
+{\r
+// mostly copied from drawmpic\r
+       int     picnum = chunknum - STARTPICM;\r
+       memptr source;\r
+       unsigned dest,width,height;\r
+\r
+       x+=pansx;\r
+       y+=pansy;\r
+       x/=SCREENXDIV;\r
+\r
+       source = grsegs[chunknum];\r
+       dest = ylookup[y]+x+bufferofs;\r
+       width = picmtable[picnum].width;\r
+       height = picmtable[picnum].height;\r
+\r
+       if (VW_MarkUpdateBlock (x*SCREENXDIV,y,(x+width)*SCREENXDIV-1,y+height-1))\r
+               VW_MaskBlock(source,0,dest,width,height,width*height);\r
+}\r
+#endif\r
+\r
+\r
+void VWB_Bar (int x, int y, int width, int height, int color)\r
+{\r
+       x+=pansx;\r
+       y+=pansy;\r
+       if (VW_MarkUpdateBlock (x,y,x+width,y+height-1) )\r
+               VW_Bar (x,y,width,height,color);\r
+}\r
+\r
+\r
+#if NUMFONT\r
+void VWB_DrawPropString         (char far *string)\r
+{\r
+       int x,y;\r
+       x = px+pansx;\r
+       y = py+pansy;\r
+       VW_DrawPropString (string);\r
+       VW_MarkUpdateBlock(x,y,x+bufferwidth*8-1,y+bufferheight-1);\r
+}\r
+#endif\r
+\r
+\r
+#if NUMFONTM\r
+void VWB_DrawMPropString (char far *string)\r
+{\r
+       int x,y;\r
+       x = px+pansx;\r
+       y = py+pansy;\r
+       VW_DrawMPropString (string);\r
+       VW_MarkUpdateBlock(x,y,x+bufferwidth*8-1,y+bufferheight-1);\r
+}\r
+#endif\r
+\r
+#if NUMSPRITES\r
+void VWB_DrawSprite(int x, int y, int chunknum)\r
+{\r
+       spritetabletype far *spr;\r
+       spritetype _seg *block;\r
+       unsigned        dest,shift,width,height;\r
+\r
+       x+=pansx;\r
+       y+=pansy;\r
+\r
+       spr = &spritetable[chunknum-STARTSPRITES];\r
+       block = (spritetype _seg *)grsegs[chunknum];\r
+\r
+       y+=spr->orgy>>G_P_SHIFT;\r
+       x+=spr->orgx>>G_P_SHIFT;\r
+\r
+\r
+#if GRMODE == EGAGR\r
+       shift = (x&7)/2;\r
+#endif\r
+#if GRMODE == CGAGR\r
+       shift = 0;\r
+#endif\r
+\r
+       dest = bufferofs + ylookup[y];\r
+       if (x>=0)\r
+               dest += x/SCREENXDIV;\r
+       else\r
+               dest += (x+1)/SCREENXDIV;\r
+\r
+       width = block->width[shift];\r
+       height = spr->height;\r
+\r
+       if (VW_MarkUpdateBlock (x&SCREENXMASK,y,(x&SCREENXMASK)+width*SCREENXDIV-1\r
+               ,y+height-1))\r
+               VW_MaskBlock (block,block->sourceoffset[shift],dest,\r
+                       width,height,block->planesize[shift]);\r
+}\r
+#endif\r
+\r
+void VWB_Plot (int x, int y, int color)\r
+{\r
+       x+=pansx;\r
+       y+=pansy;\r
+       if (VW_MarkUpdateBlock (x,y,x,y))\r
+               VW_Plot(x,y,color);\r
+}\r
+\r
+void VWB_Hlin (int x1, int x2, int y, int color)\r
+{\r
+       x1+=pansx;\r
+       x2+=pansx;\r
+       y+=pansy;\r
+       if (VW_MarkUpdateBlock (x1,y,x2,y))\r
+               VW_Hlin(x1,x2,y,color);\r
+}\r
+\r
+void VWB_Vlin (int y1, int y2, int x, int color)\r
+{\r
+       x+=pansx;\r
+       y1+=pansy;\r
+       y2+=pansy;\r
+       if (VW_MarkUpdateBlock (x,y1,x,y2))\r
+               VW_Vlin(y1,y2,x,color);\r
+}\r
+\r
+\r
+//===========================================================================\r
diff --git a/16/keen456/KEEN4-6/ID_VW.H b/16/keen456/KEEN4-6/ID_VW.H
new file mode 100755 (executable)
index 0000000..65e9867
--- /dev/null
@@ -0,0 +1,381 @@
+/* Reconstructed Commander Keen 4-6 Source Code\r
+ * Copyright (C) 2021 K1n9_Duk3\r
+ *\r
+ * This file is primarily based on:\r
+ * Catacomb 3-D Source Code\r
+ * Copyright (C) 1993-2014 Flat Rock Software\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License along\r
+ * with this program; if not, write to the Free Software Foundation, Inc.,\r
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+ */\r
+\r
+// ID_VW.H\r
+\r
+#ifndef __TYPES__\r
+#include "ID_TYPES.H"\r
+#endif\r
+\r
+#ifndef __ID_MM__\r
+#include "ID_MM.H"\r
+#endif\r
+\r
+#ifndef __ID_GLOB__\r
+#include "ID_GLOB.H"\r
+#endif\r
+\r
+#define __ID_VW__\r
+\r
+//===========================================================================\r
+\r
+#define        G_P_SHIFT               4       // global >> ?? = pixels\r
+\r
+#if GRMODE == EGAGR\r
+#ifdef CAT3D\r
+#define        SCREENWIDTH             40\r
+#else\r
+#define        SCREENWIDTH             64\r
+#endif\r
+#define CHARWIDTH              1\r
+#define TILEWIDTH              2\r
+#define GRPLANES               4\r
+#define BYTEPIXELS             8\r
+#endif\r
+\r
+#if GRMODE == CGAGR\r
+#define        SCREENWIDTH             128\r
+#define CHARWIDTH              2\r
+#define TILEWIDTH              4\r
+#define GRPLANES               1\r
+#define BYTEPIXELS             4\r
+#endif\r
+\r
+#define VIRTUALHEIGHT  300\r
+#define        VIRTUALWIDTH    512\r
+\r
+\r
+#if GRMODE == CGAGR\r
+\r
+#define        MAXSHIFTS               1\r
+\r
+#define WHITE                  3                       // graphics mode independant colors\r
+#define BLACK                  0\r
+#define FIRSTCOLOR             1\r
+#define SECONDCOLOR            2\r
+#define F_WHITE                        0                       // for XOR font drawing\r
+#define F_BLACK                        3\r
+#define F_FIRSTCOLOR   2\r
+#define F_SECONDCOLOR  1\r
+\r
+#endif\r
+\r
+#if GRMODE == EGAGR\r
+\r
+#define        MAXSHIFTS               4\r
+\r
+#define WHITE                  15                      // graphics mode independant colors\r
+#define BLACK                  0\r
+#define FIRSTCOLOR             1\r
+#define SECONDCOLOR            12\r
+#define F_WHITE                        0                       // for XOR font drawing\r
+#define F_BLACK                        15\r
+#define F_FIRSTCOLOR   14\r
+#define F_SECONDCOLOR  3\r
+\r
+#endif\r
+\r
+#if GRMODE == EGAGR\r
+#define SCREENXMASK            (~7)\r
+#define SCREENXPLUS            (7)\r
+#define SCREENXDIV             (8)\r
+#endif\r
+\r
+#if GRMODE == CGAGR\r
+#define SCREENXMASK            (~3)\r
+#define SCREENXDIV             (4)\r
+#endif\r
+\r
+//===========================================================================\r
+\r
+\r
+#define SC_INDEX       0x3C4\r
+#define SC_RESET       0\r
+#define SC_CLOCK       1\r
+#define SC_MAPMASK     2\r
+#define SC_CHARMAP     3\r
+#define SC_MEMMODE     4\r
+\r
+#define CRTC_INDEX     0x3D4\r
+#define CRTC_H_TOTAL   0\r
+#define CRTC_H_DISPEND 1\r
+#define CRTC_H_BLANK   2\r
+#define CRTC_H_ENDBLANK        3\r
+#define CRTC_H_RETRACE 4\r
+#define CRTC_H_ENDRETRACE 5\r
+#define CRTC_V_TOTAL   6\r
+#define CRTC_OVERFLOW  7\r
+#define CRTC_ROWSCAN   8\r
+#define CRTC_MAXSCANLINE 9\r
+#define CRTC_CURSORSTART 10\r
+#define CRTC_CURSOREND 11\r
+#define CRTC_STARTHIGH 12\r
+#define CRTC_STARTLOW  13\r
+#define CRTC_CURSORHIGH        14\r
+#define CRTC_CURSORLOW 15\r
+#define CRTC_V_RETRACE 16\r
+#define CRTC_V_ENDRETRACE 17\r
+#define CRTC_V_DISPEND 18\r
+#define CRTC_OFFSET    19\r
+#define CRTC_UNDERLINE 20\r
+#define CRTC_V_BLANK   21\r
+#define CRTC_V_ENDBLANK        22\r
+#define CRTC_MODE      23\r
+#define CRTC_LINECOMPARE 24\r
+\r
+\r
+#define GC_INDEX       0x3CE\r
+#define GC_SETRESET    0\r
+#define GC_ENABLESETRESET 1\r
+#define GC_COLORCOMPARE        2\r
+#define GC_DATAROTATE  3\r
+#define GC_READMAP     4\r
+#define GC_MODE                5\r
+#define GC_MISCELLANEOUS 6\r
+#define GC_COLORDONTCARE 7\r
+#define GC_BITMASK     8\r
+\r
+#define ATR_INDEX      0x3c0\r
+#define ATR_MODE       16\r
+#define ATR_OVERSCAN   17\r
+#define ATR_COLORPLANEENABLE 18\r
+#define ATR_PELPAN     19\r
+#define ATR_COLORSELECT        20\r
+\r
+#define        STATUS_REGISTER_1    0x3da\r
+\r
+//===========================================================================\r
+\r
+typedef enum {NOcard,MDAcard,CGAcard,EGAcard,MCGAcard,VGAcard,\r
+                 HGCcard=0x80,HGCPcard,HICcard} cardtype;\r
+\r
+typedef struct\r
+{\r
+  int  width,\r
+       height,\r
+       orgx,orgy,\r
+       xl,yl,xh,yh,\r
+       shifts;\r
+} spritetabletype;\r
+\r
+typedef        struct\r
+{\r
+       unsigned        sourceoffset[MAXSHIFTS];\r
+       unsigned        planesize[MAXSHIFTS];\r
+       unsigned        width[MAXSHIFTS];\r
+       byte            data[];\r
+} spritetype;          // the memptr for each sprite points to this\r
+\r
+typedef struct\r
+{\r
+       int width,height;\r
+} pictabletype;\r
+\r
+\r
+typedef struct\r
+{\r
+       int height;\r
+       int location[256];\r
+       char width[256];\r
+} fontstruct;\r
+\r
+\r
+typedef enum {CGAgr,EGAgr,VGAgr} grtype;\r
+\r
+//===========================================================================\r
+\r
+extern cardtype        videocard;              // set by VW_Startup\r
+extern grtype          grmode;                 // CGAgr, EGAgr, VGAgr\r
+\r
+extern unsigned        bufferofs;              // hidden port to draw to before displaying\r
+extern unsigned        displayofs;             // origin of port on visable screen\r
+extern unsigned        panx,pany;              // panning adjustments inside port in pixels\r
+extern unsigned        pansx,pansy;\r
+extern unsigned        panadjust;              // panx/pany adjusted by screen resolution\r
+\r
+extern unsigned        screenseg;              // normally 0xa000 or buffer segment\r
+\r
+extern unsigned        linewidth;\r
+extern unsigned        ylookup[VIRTUALHEIGHT];\r
+\r
+extern boolean         screenfaded;\r
+extern char            colors[7][17];  // pallets for fades\r
+\r
+extern pictabletype    _seg *pictable;\r
+extern pictabletype    _seg *picmtable;\r
+extern spritetabletype _seg *spritetable;\r
+\r
+extern unsigned        fontnumber;             // 0 based font number for drawing\r
+extern int                     px,py;\r
+extern byte            pdrawmode,fontcolor;\r
+\r
+extern int                     bordercolor;\r
+extern boolean                 nopan;\r
+\r
+//\r
+// asm globals\r
+//\r
+\r
+extern unsigned        *shifttabletable[8];\r
+extern unsigned        bufferwidth,bufferheight,screenspot;    // used by font drawing stuff\r
+\r
+\r
+\r
+//===========================================================================\r
+\r
+\r
+void   VW_Startup (void);\r
+void   VW_Shutdown (void);\r
+\r
+cardtype       VW_VideoID (void);\r
+\r
+//\r
+// EGA hardware routines\r
+//\r
+\r
+#define EGAWRITEMODE(x) asm{cli;mov dx,GC_INDEX;mov ax,GC_MODE+256*x;out dx,ax;sti;}\r
+#define EGABITMASK(x) asm{cli;mov dx,GC_INDEX;mov ax,GC_BITMASK+256*x;out dx,ax;sti;}\r
+#define EGAMAPMASK(x) asm{cli;mov dx,SC_INDEX;mov ax,SC_MAPMASK+x*256;out dx,ax;sti;}\r
+#define EGAREADMAP(x) asm{cli;mov dx,GC_INDEX;mov ax,GC_READMAP+x*256;out dx,ax;sti;}\r
+\r
+void   VW_SetLineWidth(int width);\r
+void   VW_SetSplitScreen(int width);\r
+void   VW_SetScreen (unsigned CRTC, unsigned pelpan);\r
+\r
+void   VW_SetScreenMode (int grmode);\r
+void   VW_ClearVideo (int color);\r
+void   VW_WaitVBL (int number);\r
+\r
+void   VW_ColorBorder (int color);\r
+void   VW_SetPalette(byte *palette);\r
+void   VW_SetDefaultColors(void);\r
+void   VW_FadeOut(void);\r
+void   VW_FadeIn(void);\r
+void   VW_FadeUp(void);\r
+void   VW_FadeDown(void);\r
+\r
+void   VW_SetAtrReg (int reg, int value);\r
+\r
+//\r
+// block primitives\r
+//\r
+\r
+void VW_MaskBlock(memptr segm,unsigned ofs,unsigned dest,\r
+       unsigned wide,unsigned height,unsigned planesize);\r
+void VW_InverseMask(memptr segm,unsigned ofs,unsigned dest,\r
+       unsigned wide,unsigned height);\r
+void VW_MemToScreen(memptr source,unsigned dest,unsigned width,unsigned height);\r
+void VW_ScreenToMem(unsigned source,memptr dest,unsigned width,unsigned height);\r
+void VW_ScreenToScreen(unsigned source,unsigned dest,unsigned width,unsigned height);\r
+\r
+\r
+//\r
+// block addressable routines\r
+//\r
+\r
+void VW_DrawTile8(unsigned x, unsigned y, unsigned tile);\r
+\r
+#if GRMODE == EGAGR\r
+\r
+#define VW_DrawTile8M(x,y,t) \\r
+       VW_MaskBlock(grsegs[STARTTILE8M],(t)*40,bufferofs+ylookup[y]+(x),1,8,8)\r
+#define VW_DrawTile16(x,y,t) \\r
+       VW_MemToScreen(grsegs[STARTTILE16+t],bufferofs+ylookup[y]+(x),2,16)\r
+#define VW_DrawTile16M(x,y,t) \\r
+       VW_MaskBlock(grsegs[STARTTILE16M],(t)*160,bufferofs+ylookup[y]+(x),2,16,32)\r
+\r
+#endif\r
+\r
+#if GRMODE == CGAGR\r
+\r
+#define VW_DrawTile8M(x,y,t) \\r
+       VW_MaskBlock(grsegs[STARTTILE8M],(t)*32,bufferofs+ylookup[y]+(x),2,8,16)\r
+#define VW_DrawTile16(x,y,t) \\r
+       VW_MemToScreen(grsegs[STARTTILE16+t],bufferofs+ylookup[y]+(x),4,16)\r
+#define VW_DrawTile16M(x,y,t) \\r
+       VW_MaskBlock(grsegs[STARTTILE16M],(t)*128,bufferofs+ylookup[y]+(x),4,16,64)\r
+\r
+#endif\r
+\r
+void VW_DrawPic(unsigned x, unsigned y, unsigned chunknum);\r
+void VW_DrawMPic(unsigned x, unsigned y, unsigned chunknum);\r
+void VW_ClipDrawMPic(unsigned x, int y, unsigned chunknum);\r
+\r
+//\r
+// pixel addressable routines\r
+//\r
+void   VW_MeasurePropString (char far *string, word *width, word *height);\r
+void   VW_MeasureMPropString  (char far *string, word *width, word *height);\r
+\r
+void VW_DrawPropString (char far *string);\r
+void VW_DrawMPropString (char far *string);\r
+void VW_DrawSprite(int x, int y, unsigned sprite);\r
+void VW_Plot(unsigned x, unsigned y, unsigned color);\r
+void VW_Hlin(unsigned xl, unsigned xh, unsigned y, unsigned color);\r
+void VW_Vlin(unsigned yl, unsigned yh, unsigned x, unsigned color);\r
+void VW_Bar (unsigned x, unsigned y, unsigned width, unsigned height,\r
+       unsigned color);\r
+\r
+//===========================================================================\r
+\r
+//\r
+// Double buffer management routines\r
+//\r
+\r
+void VW_InitDoubleBuffer (void);\r
+void VW_FixRefreshBuffer (void);\r
+int     VW_MarkUpdateBlock (int x1, int y1, int x2, int y2);\r
+void VW_UpdateScreen (void);\r
+void VW_CGAFullUpdate (void);\r
+\r
+//\r
+// cursor\r
+//\r
+\r
+void VW_ShowCursor (void);\r
+void VW_HideCursor (void);\r
+void VW_MoveCursor (int x, int y);\r
+void VW_SetCursor (int spritenum);\r
+void VW_FreeCursor (void);\r
+\r
+//\r
+// mode independant routines\r
+// coordinates in pixels, rounded to best screen res\r
+// regions marked in double buffer\r
+//\r
+\r
+void VWB_DrawTile8 (int x, int y, int tile);\r
+void VWB_DrawTile8M (int x, int y, int tile);\r
+void VWB_DrawTile16 (int x, int y, int tile);\r
+void VWB_DrawTile16M (int x, int y, int tile);\r
+void VWB_DrawPic (int x, int y, int chunknum);\r
+void VWB_DrawMPic(int x, int y, int chunknum);\r
+void VWB_Bar (int x, int y, int width, int height, int color);\r
+\r
+void VWB_DrawPropString         (char far *string);\r
+void VWB_DrawMPropString (char far *string);\r
+void VWB_DrawSprite (int x, int y, int chunknum);\r
+void VWB_Plot (int x, int y, int color);\r
+void VWB_Hlin (int x1, int x2, int y, int color);\r
+void VWB_Vlin (int y1, int y2, int x, int color);\r
+\r
+//===========================================================================\r
diff --git a/16/keen456/KEEN4-6/ID_VW_A.ASM b/16/keen456/KEEN4-6/ID_VW_A.ASM
new file mode 100755 (executable)
index 0000000..3c5132e
--- /dev/null
@@ -0,0 +1,732 @@
+; Catacomb 3-D Source Code\r
+; Copyright (C) 1993-2014 Flat Rock Software\r
+;\r
+; This program is free software; you can redistribute it and/or modify\r
+; it under the terms of the GNU General Public License as published by\r
+; the Free Software Foundation; either version 2 of the License, or\r
+; (at your option) any later version.\r
+;\r
+; This program is distributed in the hope that it will be useful,\r
+; but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+; GNU General Public License for more details.\r
+;\r
+; You should have received a copy of the GNU General Public License along\r
+; with this program; if not, write to the Free Software Foundation, Inc.,\r
+; 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+\r
+; ID_VW_A.ASM\r
+\r
+IDEAL\r
+MODEL  MEDIUM,C\r
+\r
+INCLUDE        "ID_ASM.EQU"\r
+\r
+WAITFORVBL     =       1                       ; setting to 0 causes setscreen and waitvbl\r
+                                                       ; to skip waiting for VBL (for timing things)\r
+\r
+;============================================================================\r
+\r
+DATASEG\r
+\r
+EXTRN  screenseg                       :WORD\r
+EXTRN  drawofs                         :WORD\r
+EXTRN  bufferofs                       :WORD\r
+EXTRN  displayofs                      :WORD\r
+EXTRN  drawofs                         :WORD\r
+EXTRN  panadjust                       :WORD\r
+EXTRN  ylookup                         :WORD\r
+EXTRN  linewidth                       :WORD\r
+EXTRN  grsegs                          :WORD\r
+EXTRN  updateptr                       :WORD\r
+EXTRN  blockstarts                     :WORD   ;offsets from drawofs for each update block\r
+EXTRN  fontspace                       :WORD\r
+EXTRN  fontnumber                      :WORD\r
+\r
+\r
+planemask      db      ?\r
+planenum       db      ?\r
+screendest     dw      ?\r
+linedelta      dw      ?\r
+\r
+LABEL shiftdata0 WORD\r
+       dw     0,    1,    2,    3,    4,    5,    6,    7,    8,    9,   10,   11,   12,   13\r
+       dw    14,   15,   16,   17,   18,   19,   20,   21,   22,   23,   24,   25,   26,   27\r
+       dw    28,   29,   30,   31,   32,   33,   34,   35,   36,   37,   38,   39,   40,   41\r
+       dw    42,   43,   44,   45,   46,   47,   48,   49,   50,   51,   52,   53,   54,   55\r
+       dw    56,   57,   58,   59,   60,   61,   62,   63,   64,   65,   66,   67,   68,   69\r
+       dw    70,   71,   72,   73,   74,   75,   76,   77,   78,   79,   80,   81,   82,   83\r
+       dw    84,   85,   86,   87,   88,   89,   90,   91,   92,   93,   94,   95,   96,   97\r
+       dw    98,   99,  100,  101,  102,  103,  104,  105,  106,  107,  108,  109,  110,  111\r
+       dw   112,  113,  114,  115,  116,  117,  118,  119,  120,  121,  122,  123,  124,  125\r
+       dw   126,  127,  128,  129,  130,  131,  132,  133,  134,  135,  136,  137,  138,  139\r
+       dw   140,  141,  142,  143,  144,  145,  146,  147,  148,  149,  150,  151,  152,  153\r
+       dw   154,  155,  156,  157,  158,  159,  160,  161,  162,  163,  164,  165,  166,  167\r
+       dw   168,  169,  170,  171,  172,  173,  174,  175,  176,  177,  178,  179,  180,  181\r
+       dw   182,  183,  184,  185,  186,  187,  188,  189,  190,  191,  192,  193,  194,  195\r
+       dw   196,  197,  198,  199,  200,  201,  202,  203,  204,  205,  206,  207,  208,  209\r
+       dw   210,  211,  212,  213,  214,  215,  216,  217,  218,  219,  220,  221,  222,  223\r
+       dw   224,  225,  226,  227,  228,  229,  230,  231,  232,  233,  234,  235,  236,  237\r
+       dw   238,  239,  240,  241,  242,  243,  244,  245,  246,  247,  248,  249,  250,  251\r
+       dw   252,  253,  254,  255\r
+\r
+LABEL shiftdata1 WORD\r
+       dw     0,32768,    1,32769,    2,32770,    3,32771,    4,32772,    5,32773,    6,32774\r
+       dw     7,32775,    8,32776,    9,32777,   10,32778,   11,32779,   12,32780,   13,32781\r
+       dw    14,32782,   15,32783,   16,32784,   17,32785,   18,32786,   19,32787,   20,32788\r
+       dw    21,32789,   22,32790,   23,32791,   24,32792,   25,32793,   26,32794,   27,32795\r
+       dw    28,32796,   29,32797,   30,32798,   31,32799,   32,32800,   33,32801,   34,32802\r
+       dw    35,32803,   36,32804,   37,32805,   38,32806,   39,32807,   40,32808,   41,32809\r
+       dw    42,32810,   43,32811,   44,32812,   45,32813,   46,32814,   47,32815,   48,32816\r
+       dw    49,32817,   50,32818,   51,32819,   52,32820,   53,32821,   54,32822,   55,32823\r
+       dw    56,32824,   57,32825,   58,32826,   59,32827,   60,32828,   61,32829,   62,32830\r
+       dw    63,32831,   64,32832,   65,32833,   66,32834,   67,32835,   68,32836,   69,32837\r
+       dw    70,32838,   71,32839,   72,32840,   73,32841,   74,32842,   75,32843,   76,32844\r
+       dw    77,32845,   78,32846,   79,32847,   80,32848,   81,32849,   82,32850,   83,32851\r
+       dw    84,32852,   85,32853,   86,32854,   87,32855,   88,32856,   89,32857,   90,32858\r
+       dw    91,32859,   92,32860,   93,32861,   94,32862,   95,32863,   96,32864,   97,32865\r
+       dw    98,32866,   99,32867,  100,32868,  101,32869,  102,32870,  103,32871,  104,32872\r
+       dw   105,32873,  106,32874,  107,32875,  108,32876,  109,32877,  110,32878,  111,32879\r
+       dw   112,32880,  113,32881,  114,32882,  115,32883,  116,32884,  117,32885,  118,32886\r
+       dw   119,32887,  120,32888,  121,32889,  122,32890,  123,32891,  124,32892,  125,32893\r
+       dw   126,32894,  127,32895\r
+\r
+LABEL shiftdata2 WORD\r
+       dw     0,16384,32768,49152,    1,16385,32769,49153,    2,16386,32770,49154,    3,16387\r
+       dw 32771,49155,    4,16388,32772,49156,    5,16389,32773,49157,    6,16390,32774,49158\r
+       dw     7,16391,32775,49159,    8,16392,32776,49160,    9,16393,32777,49161,   10,16394\r
+       dw 32778,49162,   11,16395,32779,49163,   12,16396,32780,49164,   13,16397,32781,49165\r
+       dw    14,16398,32782,49166,   15,16399,32783,49167,   16,16400,32784,49168,   17,16401\r
+       dw 32785,49169,   18,16402,32786,49170,   19,16403,32787,49171,   20,16404,32788,49172\r
+       dw    21,16405,32789,49173,   22,16406,32790,49174,   23,16407,32791,49175,   24,16408\r
+       dw 32792,49176,   25,16409,32793,49177,   26,16410,32794,49178,   27,16411,32795,49179\r
+       dw    28,16412,32796,49180,   29,16413,32797,49181,   30,16414,32798,49182,   31,16415\r
+       dw 32799,49183,   32,16416,32800,49184,   33,16417,32801,49185,   34,16418,32802,49186\r
+       dw    35,16419,32803,49187,   36,16420,32804,49188,   37,16421,32805,49189,   38,16422\r
+       dw 32806,49190,   39,16423,32807,49191,   40,16424,32808,49192,   41,16425,32809,49193\r
+       dw    42,16426,32810,49194,   43,16427,32811,49195,   44,16428,32812,49196,   45,16429\r
+       dw 32813,49197,   46,16430,32814,49198,   47,16431,32815,49199,   48,16432,32816,49200\r
+       dw    49,16433,32817,49201,   50,16434,32818,49202,   51,16435,32819,49203,   52,16436\r
+       dw 32820,49204,   53,16437,32821,49205,   54,16438,32822,49206,   55,16439,32823,49207\r
+       dw    56,16440,32824,49208,   57,16441,32825,49209,   58,16442,32826,49210,   59,16443\r
+       dw 32827,49211,   60,16444,32828,49212,   61,16445,32829,49213,   62,16446,32830,49214\r
+       dw    63,16447,32831,49215\r
+\r
+LABEL shiftdata3 WORD\r
+       dw     0, 8192,16384,24576,32768,40960,49152,57344,    1, 8193,16385,24577,32769,40961\r
+       dw 49153,57345,    2, 8194,16386,24578,32770,40962,49154,57346,    3, 8195,16387,24579\r
+       dw 32771,40963,49155,57347,    4, 8196,16388,24580,32772,40964,49156,57348,    5, 8197\r
+       dw 16389,24581,32773,40965,49157,57349,    6, 8198,16390,24582,32774,40966,49158,57350\r
+       dw     7, 8199,16391,24583,32775,40967,49159,57351,    8, 8200,16392,24584,32776,40968\r
+       dw 49160,57352,    9, 8201,16393,24585,32777,40969,49161,57353,   10, 8202,16394,24586\r
+       dw 32778,40970,49162,57354,   11, 8203,16395,24587,32779,40971,49163,57355,   12, 8204\r
+       dw 16396,24588,32780,40972,49164,57356,   13, 8205,16397,24589,32781,40973,49165,57357\r
+       dw    14, 8206,16398,24590,32782,40974,49166,57358,   15, 8207,16399,24591,32783,40975\r
+       dw 49167,57359,   16, 8208,16400,24592,32784,40976,49168,57360,   17, 8209,16401,24593\r
+       dw 32785,40977,49169,57361,   18, 8210,16402,24594,32786,40978,49170,57362,   19, 8211\r
+       dw 16403,24595,32787,40979,49171,57363,   20, 8212,16404,24596,32788,40980,49172,57364\r
+       dw    21, 8213,16405,24597,32789,40981,49173,57365,   22, 8214,16406,24598,32790,40982\r
+       dw 49174,57366,   23, 8215,16407,24599,32791,40983,49175,57367,   24, 8216,16408,24600\r
+       dw 32792,40984,49176,57368,   25, 8217,16409,24601,32793,40985,49177,57369,   26, 8218\r
+       dw 16410,24602,32794,40986,49178,57370,   27, 8219,16411,24603,32795,40987,49179,57371\r
+       dw    28, 8220,16412,24604,32796,40988,49180,57372,   29, 8221,16413,24605,32797,40989\r
+       dw 49181,57373,   30, 8222,16414,24606,32798,40990,49182,57374,   31, 8223,16415,24607\r
+       dw 32799,40991,49183,57375\r
+\r
+LABEL shiftdata4 WORD\r
+       dw     0, 4096, 8192,12288,16384,20480,24576,28672,32768,36864,40960,45056,49152,53248\r
+       dw 57344,61440,    1, 4097, 8193,12289,16385,20481,24577,28673,32769,36865,40961,45057\r
+       dw 49153,53249,57345,61441,    2, 4098, 8194,12290,16386,20482,24578,28674,32770,36866\r
+       dw 40962,45058,49154,53250,57346,61442,    3, 4099, 8195,12291,16387,20483,24579,28675\r
+       dw 32771,36867,40963,45059,49155,53251,57347,61443,    4, 4100, 8196,12292,16388,20484\r
+       dw 24580,28676,32772,36868,40964,45060,49156,53252,57348,61444,    5, 4101, 8197,12293\r
+       dw 16389,20485,24581,28677,32773,36869,40965,45061,49157,53253,57349,61445,    6, 4102\r
+       dw  8198,12294,16390,20486,24582,28678,32774,36870,40966,45062,49158,53254,57350,61446\r
+       dw     7, 4103, 8199,12295,16391,20487,24583,28679,32775,36871,40967,45063,49159,53255\r
+       dw 57351,61447,    8, 4104, 8200,12296,16392,20488,24584,28680,32776,36872,40968,45064\r
+       dw 49160,53256,57352,61448,    9, 4105, 8201,12297,16393,20489,24585,28681,32777,36873\r
+       dw 40969,45065,49161,53257,57353,61449,   10, 4106, 8202,12298,16394,20490,24586,28682\r
+       dw 32778,36874,40970,45066,49162,53258,57354,61450,   11, 4107, 8203,12299,16395,20491\r
+       dw 24587,28683,32779,36875,40971,45067,49163,53259,57355,61451,   12, 4108, 8204,12300\r
+       dw 16396,20492,24588,28684,32780,36876,40972,45068,49164,53260,57356,61452,   13, 4109\r
+       dw  8205,12301,16397,20493,24589,28685,32781,36877,40973,45069,49165,53261,57357,61453\r
+       dw    14, 4110, 8206,12302,16398,20494,24590,28686,32782,36878,40974,45070,49166,53262\r
+       dw 57358,61454,   15, 4111, 8207,12303,16399,20495,24591,28687,32783,36879,40975,45071\r
+       dw 49167,53263,57359,61455\r
+\r
+LABEL shiftdata5 WORD\r
+       dw     0, 2048, 4096, 6144, 8192,10240,12288,14336,16384,18432,20480,22528,24576,26624\r
+       dw 28672,30720,32768,34816,36864,38912,40960,43008,45056,47104,49152,51200,53248,55296\r
+       dw 57344,59392,61440,63488,    1, 2049, 4097, 6145, 8193,10241,12289,14337,16385,18433\r
+       dw 20481,22529,24577,26625,28673,30721,32769,34817,36865,38913,40961,43009,45057,47105\r
+       dw 49153,51201,53249,55297,57345,59393,61441,63489,    2, 2050, 4098, 6146, 8194,10242\r
+       dw 12290,14338,16386,18434,20482,22530,24578,26626,28674,30722,32770,34818,36866,38914\r
+       dw 40962,43010,45058,47106,49154,51202,53250,55298,57346,59394,61442,63490,    3, 2051\r
+       dw  4099, 6147, 8195,10243,12291,14339,16387,18435,20483,22531,24579,26627,28675,30723\r
+       dw 32771,34819,36867,38915,40963,43011,45059,47107,49155,51203,53251,55299,57347,59395\r
+       dw 61443,63491,    4, 2052, 4100, 6148, 8196,10244,12292,14340,16388,18436,20484,22532\r
+       dw 24580,26628,28676,30724,32772,34820,36868,38916,40964,43012,45060,47108,49156,51204\r
+       dw 53252,55300,57348,59396,61444,63492,    5, 2053, 4101, 6149, 8197,10245,12293,14341\r
+       dw 16389,18437,20485,22533,24581,26629,28677,30725,32773,34821,36869,38917,40965,43013\r
+       dw 45061,47109,49157,51205,53253,55301,57349,59397,61445,63493,    6, 2054, 4102, 6150\r
+       dw  8198,10246,12294,14342,16390,18438,20486,22534,24582,26630,28678,30726,32774,34822\r
+       dw 36870,38918,40966,43014,45062,47110,49158,51206,53254,55302,57350,59398,61446,63494\r
+       dw     7, 2055, 4103, 6151, 8199,10247,12295,14343,16391,18439,20487,22535,24583,26631\r
+       dw 28679,30727,32775,34823,36871,38919,40967,43015,45063,47111,49159,51207,53255,55303\r
+       dw 57351,59399,61447,63495\r
+\r
+LABEL shiftdata6 WORD\r
+       dw     0, 1024, 2048, 3072, 4096, 5120, 6144, 7168, 8192, 9216,10240,11264,12288,13312\r
+       dw 14336,15360,16384,17408,18432,19456,20480,21504,22528,23552,24576,25600,26624,27648\r
+       dw 28672,29696,30720,31744,32768,33792,34816,35840,36864,37888,38912,39936,40960,41984\r
+       dw 43008,44032,45056,46080,47104,48128,49152,50176,51200,52224,53248,54272,55296,56320\r
+       dw 57344,58368,59392,60416,61440,62464,63488,64512,    1, 1025, 2049, 3073, 4097, 5121\r
+       dw  6145, 7169, 8193, 9217,10241,11265,12289,13313,14337,15361,16385,17409,18433,19457\r
+       dw 20481,21505,22529,23553,24577,25601,26625,27649,28673,29697,30721,31745,32769,33793\r
+       dw 34817,35841,36865,37889,38913,39937,40961,41985,43009,44033,45057,46081,47105,48129\r
+       dw 49153,50177,51201,52225,53249,54273,55297,56321,57345,58369,59393,60417,61441,62465\r
+       dw 63489,64513,    2, 1026, 2050, 3074, 4098, 5122, 6146, 7170, 8194, 9218,10242,11266\r
+       dw 12290,13314,14338,15362,16386,17410,18434,19458,20482,21506,22530,23554,24578,25602\r
+       dw 26626,27650,28674,29698,30722,31746,32770,33794,34818,35842,36866,37890,38914,39938\r
+       dw 40962,41986,43010,44034,45058,46082,47106,48130,49154,50178,51202,52226,53250,54274\r
+       dw 55298,56322,57346,58370,59394,60418,61442,62466,63490,64514,    3, 1027, 2051, 3075\r
+       dw  4099, 5123, 6147, 7171, 8195, 9219,10243,11267,12291,13315,14339,15363,16387,17411\r
+       dw 18435,19459,20483,21507,22531,23555,24579,25603,26627,27651,28675,29699,30723,31747\r
+       dw 32771,33795,34819,35843,36867,37891,38915,39939,40963,41987,43011,44035,45059,46083\r
+       dw 47107,48131,49155,50179,51203,52227,53251,54275,55299,56323,57347,58371,59395,60419\r
+       dw 61443,62467,63491,64515\r
+\r
+LABEL shiftdata7 WORD\r
+       dw     0,  512, 1024, 1536, 2048, 2560, 3072, 3584, 4096, 4608, 5120, 5632, 6144, 6656\r
+       dw  7168, 7680, 8192, 8704, 9216, 9728,10240,10752,11264,11776,12288,12800,13312,13824\r
+       dw 14336,14848,15360,15872,16384,16896,17408,17920,18432,18944,19456,19968,20480,20992\r
+       dw 21504,22016,22528,23040,23552,24064,24576,25088,25600,26112,26624,27136,27648,28160\r
+       dw 28672,29184,29696,30208,30720,31232,31744,32256,32768,33280,33792,34304,34816,35328\r
+       dw 35840,36352,36864,37376,37888,38400,38912,39424,39936,40448,40960,41472,41984,42496\r
+       dw 43008,43520,44032,44544,45056,45568,46080,46592,47104,47616,48128,48640,49152,49664\r
+       dw 50176,50688,51200,51712,52224,52736,53248,53760,54272,54784,55296,55808,56320,56832\r
+       dw 57344,57856,58368,58880,59392,59904,60416,60928,61440,61952,62464,62976,63488,64000\r
+       dw 64512,65024,    1,  513, 1025, 1537, 2049, 2561, 3073, 3585, 4097, 4609, 5121, 5633\r
+       dw  6145, 6657, 7169, 7681, 8193, 8705, 9217, 9729,10241,10753,11265,11777,12289,12801\r
+       dw 13313,13825,14337,14849,15361,15873,16385,16897,17409,17921,18433,18945,19457,19969\r
+       dw 20481,20993,21505,22017,22529,23041,23553,24065,24577,25089,25601,26113,26625,27137\r
+       dw 27649,28161,28673,29185,29697,30209,30721,31233,31745,32257,32769,33281,33793,34305\r
+       dw 34817,35329,35841,36353,36865,37377,37889,38401,38913,39425,39937,40449,40961,41473\r
+       dw 41985,42497,43009,43521,44033,44545,45057,45569,46081,46593,47105,47617,48129,48641\r
+       dw 49153,49665,50177,50689,51201,51713,52225,52737,53249,53761,54273,54785,55297,55809\r
+       dw 56321,56833,57345,57857,58369,58881,59393,59905,60417,60929,61441,61953,62465,62977\r
+       dw 63489,64001,64513,65025\r
+\r
+shifttabletable        dw      shiftdata0,shiftdata1,shiftdata2,shiftdata3\r
+               dw      shiftdata4,shiftdata5,shiftdata6,shiftdata7\r
+\r
+PUBLIC shifttabletable\r
+\r
+\r
+;============================================================================\r
+\r
+CODESEG\r
+\r
+IFE GRMODE-CGAGR\r
+INCLUDE        "ID_VW_AC.ASM"\r
+ENDIF\r
+\r
+IFE GRMODE-EGAGR\r
+INCLUDE        "ID_VW_AE.ASM"\r
+ENDIF\r
+\r
+IFE GRMODE-VGAGR\r
+INCLUDE        "ID_VW_AV.ASM"\r
+ENDIF\r
+\r
+;============================================================================\r
+;\r
+;                           MISC VIDEO ROUTINES\r
+;\r
+;============================================================================\r
+\r
+;========\r
+;\r
+; VW_WaitVBL (int number)\r
+;\r
+;========\r
+\r
+PROC   VW_WaitVBL number:WORD\r
+PUBLIC VW_WaitVBL\r
+\r
+if WAITFORVBL                          ; skip wait if profiling\r
+\r
+       mov     dx,STATUS_REGISTER_1\r
+\r
+       mov     cx,[number]\r
+\r
+waitvbl1:\r
+       in      al,dx\r
+       test    al,00001000b    ;look for vbl\r
+       jnz     waitvbl1\r
+\r
+waitvbl2:\r
+       in      al,dx\r
+       test    al,00001000b    ;look for vbl\r
+       jz      waitvbl2\r
+\r
+       loop    waitvbl1\r
+\r
+endif\r
+\r
+       ret\r
+\r
+ENDP\r
+\r
+\r
+;===========================================================================\r
+\r
+\r
+       MASM\r
+;ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ\r
+;\r
+; Name:        VW_VideoID\r
+;\r
+; Function:    Detects the presence of various video subsystems\r
+;\r
+; int VideoID;\r
+;\r
+; Subsystem ID values:\r
+;       0  = (none)\r
+;       1  = MDA\r
+;       2  = CGA\r
+;       3  = EGA\r
+;       4  = MCGA\r
+;       5  = VGA\r
+;      80h = HGC\r
+;      81h = HGC+\r
+;      82h = Hercules InColor\r
+;\r
+;ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ\r
+\r
+;ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ\r
+;\r
+; Equates\r
+;\r
+;ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ\r
+VIDstruct      STRUC           ; corresponds to C data structure\r
+\r
+Video0Type     DB      ?       ; first subsystem type\r
+Display0Type   DB      ?       ; display attached to first subsystem\r
+\r
+Video1Type     DB      ?       ; second subsystem type\r
+Display1Type   DB      ?       ; display attached to second subsystem\r
+\r
+VIDstruct      ENDS\r
+\r
+\r
+Device0        EQU     word ptr Video0Type[di]\r
+Device1        EQU     word ptr Video1Type[di]\r
+\r
+\r
+MDA    EQU     1       ; subsystem types\r
+CGA    EQU     2\r
+EGA    EQU     3\r
+MCGA   EQU     4\r
+VGA    EQU     5\r
+HGC    EQU     80h\r
+HGCPlus        EQU     81h\r
+InColor        EQU     82h\r
+\r
+MDADisplay     EQU     1       ; display types\r
+CGADisplay     EQU     2\r
+EGAColorDisplay        EQU     3\r
+PS2MonoDisplay EQU     4\r
+PS2ColorDisplay        EQU     5\r
+\r
+TRUE   EQU     1\r
+FALSE  EQU     0\r
+\r
+;ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ\r
+;\r
+; Program\r
+;\r
+;ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ\r
+\r
+Results        VIDstruct <>    ;results go here!\r
+\r
+EGADisplays    DB      CGADisplay      ; 0000b, 0001b  (EGA switch values)\r
+       DB      EGAColorDisplay ; 0010b, 0011b\r
+       DB      MDADisplay      ; 0100b, 0101b\r
+       DB      CGADisplay      ; 0110b, 0111b\r
+       DB      EGAColorDisplay ; 1000b, 1001b\r
+       DB      MDADisplay      ; 1010b, 1011b\r
+\r
+DCCtable       DB      0,0     ; translate table for INT 10h func 1Ah\r
+       DB      MDA,MDADisplay\r
+       DB      CGA,CGADisplay\r
+       DB      0,0\r
+       DB      EGA,EGAColorDisplay\r
+       DB      EGA,MDADisplay\r
+       DB      0,0\r
+       DB      VGA,PS2MonoDisplay\r
+       DB      VGA,PS2ColorDisplay\r
+       DB      0,0\r
+       DB      MCGA,EGAColorDisplay\r
+       DB      MCGA,PS2MonoDisplay\r
+       DB      MCGA,PS2ColorDisplay\r
+\r
+TestSequence   DB      TRUE    ; this list of flags and addresses\r
+       DW      FindPS2 ;  determines the order in which this\r
+                       ;  program looks for the various\r
+EGAflag        DB      ?       ;  subsystems\r
+       DW      FindEGA\r
+\r
+CGAflag        DB      ?\r
+       DW      FindCGA\r
+\r
+Monoflag       DB      ?\r
+       DW      FindMono\r
+\r
+NumberOfTests  EQU     ($-TestSequence)/3\r
+\r
+\r
+PUBLIC VW_VideoID\r
+VW_VideoID     PROC\r
+\r
+       push    bp      ; preserve caller registers\r
+       mov     bp,sp\r
+       push    ds\r
+       push    si\r
+       push    di\r
+\r
+       push    cs\r
+       pop     ds\r
+       ASSUME  DS:@Code\r
+\r
+; initialize the data structure that will contain the results\r
+\r
+       lea     di,Results      ; DS:DI -> start of data structure\r
+\r
+       mov     Device0,0       ; zero these variables\r
+       mov     Device1,0\r
+\r
+; look for the various subsystems using the subroutines whose addresses are\r
+; tabulated in TestSequence; each subroutine sets flags in TestSequence\r
+; to indicate whether subsequent subroutines need to be called\r
+\r
+       mov     byte ptr CGAflag,TRUE\r
+       mov     byte ptr EGAflag,TRUE\r
+       mov     byte ptr Monoflag,TRUE\r
+\r
+       mov     cx,NumberOfTests\r
+       mov     si,offset TestSequence\r
+\r
+@@L01: lodsb           ; AL := flag\r
+       test    al,al\r
+       lodsw           ; AX := subroutine address\r
+       jz      @@L02   ; skip subroutine if flag is false\r
+\r
+       push    si\r
+       push    cx\r
+       call    ax      ; call subroutine to detect subsystem\r
+       pop     cx\r
+       pop     si\r
+\r
+@@L02: loop    @@L01\r
+\r
+; determine which subsystem is active\r
+\r
+       call    FindActive\r
+\r
+       mov     al,Results.Video0Type\r
+       mov     ah,0    ; was:  Results.Display0Type\r
+\r
+       pop     di      ; restore caller registers and return\r
+       pop     si\r
+       pop     ds\r
+       mov     sp,bp\r
+       pop     bp\r
+       ret\r
+\r
+VW_VideoID     ENDP\r
+\r
+\r
+;\r
+; FindPS2\r
+;\r
+; This subroutine uses INT 10H function 1Ah to determine the video BIOS\r
+; Display Combination Code (DCC) for each video subsystem present.\r
+;\r
+\r
+FindPS2        PROC    near\r
+\r
+       mov     ax,1A00h\r
+       int     10h     ; call video BIOS for info\r
+\r
+       cmp     al,1Ah\r
+       jne     @@L13   ; exit if function not supported (i.e.,\r
+                       ;  no MCGA or VGA in system)\r
+\r
+; convert BIOS DCCs into specific subsystems & displays\r
+\r
+       mov     cx,bx\r
+       xor     bh,bh   ; BX := DCC for active subsystem\r
+\r
+       or      ch,ch\r
+       jz      @@L11   ; jump if only one subsystem present\r
+\r
+       mov     bl,ch   ; BX := inactive DCC\r
+       add     bx,bx\r
+       mov     ax,[bx+offset DCCtable]\r
+\r
+       mov     Device1,ax\r
+\r
+       mov     bl,cl\r
+       xor     bh,bh   ; BX := active DCC\r
+\r
+@@L11: add     bx,bx\r
+       mov     ax,[bx+offset DCCtable]\r
+\r
+       mov     Device0,ax\r
+\r
+; reset flags for subsystems that have been ruled out\r
+\r
+       mov     byte ptr CGAflag,FALSE\r
+       mov     byte ptr EGAflag,FALSE\r
+       mov     byte ptr Monoflag,FALSE\r
+\r
+       lea     bx,Video0Type[di]  ; if the BIOS reported an MDA ...\r
+       cmp     byte ptr [bx],MDA\r
+       je      @@L12\r
+\r
+       lea     bx,Video1Type[di]\r
+       cmp     byte ptr [bx],MDA\r
+       jne     @@L13\r
+\r
+@@L12: mov     word ptr [bx],0    ; ... Hercules can't be ruled out\r
+       mov     byte ptr Monoflag,TRUE\r
+\r
+@@L13: ret\r
+\r
+FindPS2        ENDP\r
+\r
+\r
+;\r
+; FindEGA\r
+;\r
+; Look for an EGA.  This is done by making a call to an EGA BIOS function\r
+;  which doesn't exist in the default (MDA, CGA) BIOS.\r
+\r
+FindEGA        PROC    near    ; Caller:       AH = flags\r
+                       ; Returns:      AH = flags\r
+                       ;               Video0Type and\r
+                       ;                Display0Type updated\r
+\r
+       mov     bl,10h  ; BL := 10h (return EGA info)\r
+       mov     ah,12h  ; AH := INT 10H function number\r
+       int     10h     ; call EGA BIOS for info\r
+                       ; if EGA BIOS is present,\r
+                       ;  BL <> 10H\r
+                       ;  CL = switch setting\r
+       cmp     bl,10h\r
+       je      @@L22   ; jump if EGA BIOS not present\r
+\r
+       mov     al,cl\r
+       shr     al,1    ; AL := switches/2\r
+       mov     bx,offset EGADisplays\r
+       xlat            ; determine display type from switches\r
+       mov     ah,al   ; AH := display type\r
+       mov     al,EGA  ; AL := subystem type\r
+       call    FoundDevice\r
+\r
+       cmp     ah,MDADisplay\r
+       je      @@L21   ; jump if EGA has a monochrome display\r
+\r
+       mov     CGAflag,FALSE   ; no CGA if EGA has color display\r
+       jmp     short @@L22\r
+\r
+@@L21: mov     Monoflag,FALSE  ; EGA has a mono display, so MDA and\r
+                       ;  Hercules are ruled out\r
+@@L22: ret\r
+\r
+FindEGA        ENDP\r
+\r
+;\r
+; FindCGA\r
+;\r
+; This is done by looking for the CGA's 6845 CRTC at I/O port 3D4H.\r
+;\r
+FindCGA        PROC    near    ; Returns:      VIDstruct updated\r
+\r
+       mov     dx,3D4h ; DX := CRTC address port\r
+       call    Find6845\r
+       jc      @@L31   ; jump if not present\r
+\r
+       mov     al,CGA\r
+       mov     ah,CGADisplay\r
+       call    FoundDevice\r
+\r
+@@L31: ret\r
+\r
+FindCGA        ENDP\r
+\r
+;\r
+; FindMono\r
+;\r
+; This is done by looking for the MDA's 6845 CRTC at I/O port 3B4H.  If\r
+; a 6845 is found, the subroutine distinguishes between an MDA\r
+; and a Hercules adapter by monitoring bit 7 of the CRT Status byte.\r
+; This bit changes on Hercules adapters but does not change on an MDA.\r
+;\r
+; The various Hercules adapters are identified by bits 4 through 6 of\r
+; the CRT Status value:\r
+;\r
+; 000b = HGC\r
+; 001b = HGC+\r
+; 101b = InColor card\r
+;\r
+\r
+FindMono       PROC    near    ; Returns:      VIDstruct updated\r
+\r
+       mov     dx,3B4h ; DX := CRTC address port\r
+       call    Find6845\r
+       jc      @@L44   ; jump if not present\r
+\r
+       mov     dl,0BAh ; DX := 3BAh (status port)\r
+       in      al,dx\r
+       and     al,80h\r
+       mov     ah,al   ; AH := bit 7 (vertical sync on HGC)\r
+\r
+       mov     cx,8000h        ; do this 32768 times\r
+@@L41: in      al,dx\r
+       and     al,80h  ; isolate bit 7\r
+       cmp     ah,al\r
+       loope   @@L41   ; wait for bit 7 to change\r
+       jne     @@L42   ; if bit 7 changed, it's a Hercules\r
+\r
+       mov     al,MDA  ; if bit 7 didn't change, it's an MDA\r
+       mov     ah,MDADisplay\r
+       call    FoundDevice\r
+       jmp     short @@L44\r
+\r
+@@L42: in      al,dx\r
+       mov     dl,al   ; DL := value from status port\r
+       and     dl,01110000b    ; mask bits 4 thru 6\r
+\r
+       mov     ah,MDADisplay   ; assume it's a monochrome display\r
+\r
+       mov     al,HGCPlus      ; look for an HGC+\r
+       cmp     dl,00010000b\r
+       je      @@L43   ; jump if it's an HGC+\r
+\r
+       mov     al,HGC  ; look for an InColor card or HGC\r
+       cmp     dl,01010000b\r
+       jne     @@L43   ; jump if it's not an InColor card\r
+\r
+       mov     al,InColor      ; it's an InColor card\r
+       mov     ah,EGAColorDisplay\r
+\r
+@@L43: call    FoundDevice\r
+\r
+@@L44: ret\r
+\r
+FindMono       ENDP\r
+\r
+;\r
+; Find6845\r
+;\r
+; This routine detects the presence of the CRTC on a MDA, CGA or HGC.\r
+; The technique is to write and read register 0Fh of the chip (cursor\r
+; low).  If the same value is read as written, assume the chip is\r
+; present at the specified port addr.\r
+;\r
+\r
+Find6845       PROC    near    ; Caller:  DX = port addr\r
+                       ; Returns: cf set if not present\r
+       mov     al,0Fh\r
+       out     dx,al   ; select 6845 reg 0Fh (Cursor Low)\r
+       inc     dx\r
+       in      al,dx   ; AL := current Cursor Low value\r
+       mov     ah,al   ; preserve in AH\r
+       mov     al,66h  ; AL := arbitrary value\r
+       out     dx,al   ; try to write to 6845\r
+\r
+       mov     cx,100h\r
+@@L51: loop    @@L51   ; wait for 6845 to respond\r
+\r
+       in      al,dx\r
+       xchg    ah,al   ; AH := returned value\r
+                       ; AL := original value\r
+       out     dx,al   ; restore original value\r
+\r
+       cmp     ah,66h  ; test whether 6845 responded\r
+       je      @@L52   ; jump if it did (cf is reset)\r
+\r
+       stc             ; set carry flag if no 6845 present\r
+\r
+@@L52: ret\r
+\r
+Find6845       ENDP\r
+\r
+\r
+;\r
+; FindActive\r
+;\r
+; This subroutine stores the currently active device as Device0.  The\r
+; current video mode determines which subsystem is active.\r
+;\r
+\r
+FindActive     PROC    near\r
+\r
+       cmp     word ptr Device1,0\r
+       je      @@L63   ; exit if only one subsystem\r
+\r
+       cmp     Video0Type[di],4        ; exit if MCGA or VGA present\r
+       jge     @@L63   ;  (INT 10H function 1AH\r
+       cmp     Video1Type[di],4        ;  already did the work)\r
+       jge     @@L63\r
+\r
+       mov     ah,0Fh\r
+       int     10h     ; AL := current BIOS video mode\r
+\r
+       and     al,7\r
+       cmp     al,7    ; jump if monochrome\r
+       je      @@L61   ;  (mode 7 or 0Fh)\r
+\r
+       cmp     Display0Type[di],MDADisplay\r
+       jne     @@L63   ; exit if Display0 is color\r
+       jmp     short @@L62\r
+\r
+@@L61: cmp     Display0Type[di],MDADisplay\r
+       je      @@L63   ; exit if Display0 is monochrome\r
+\r
+@@L62: mov     ax,Device0      ; make Device0 currently active\r
+       xchg    ax,Device1\r
+       mov     Device0,ax\r
+\r
+@@L63: ret\r
+\r
+FindActive     ENDP\r
+\r
+\r
+;\r
+; FoundDevice\r
+;\r
+; This routine updates the list of subsystems.\r
+;\r
+\r
+FoundDevice    PROC    near    ; Caller:    AH = display #\r
+                       ;            AL = subsystem #\r
+                       ; Destroys:  BX\r
+       lea     bx,Video0Type[di]\r
+       cmp     byte ptr [bx],0\r
+       je      @@L71   ; jump if 1st subsystem\r
+\r
+       lea     bx,Video1Type[di]       ; must be 2nd subsystem\r
+\r
+@@L71: mov     [bx],ax ; update list entry\r
+       ret\r
+\r
+FoundDevice    ENDP\r
+\r
+IDEAL\r
+\r
+\r
+\r
+END\r
diff --git a/16/keen456/KEEN4-6/ID_VW_AC.ASM b/16/keen456/KEEN4-6/ID_VW_AC.ASM
new file mode 100755 (executable)
index 0000000..80523b4
--- /dev/null
@@ -0,0 +1,1536 @@
+; Reconstructed Commander Keen 4-6 Source Code\r
+; Copyright (C) 2021 K1n9_Duk3\r
+;\r
+; This file is primarily based on:\r
+; Catacomb 3-D Source Code\r
+; Copyright (C) 1993-2014 Flat Rock Software\r
+;\r
+; This program is free software; you can redistribute it and/or modify\r
+; it under the terms of the GNU General Public License as published by\r
+; the Free Software Foundation; either version 2 of the License, or\r
+; (at your option) any later version.\r
+;\r
+; This program is distributed in the hope that it will be useful,\r
+; but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+; GNU General Public License for more details.\r
+;\r
+; You should have received a copy of the GNU General Public License along\r
+; with this program; if not, write to the Free Software Foundation, Inc.,\r
+; 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+\r
+;=================================\r
+;\r
+; CGA view manager routines\r
+;\r
+;=================================\r
+\r
+;============================================================================\r
+;\r
+; All of these routines draw into a floating virtual screen segment in main\r
+; memory.  bufferofs points to the origin of the drawing page in screenseg.\r
+; The routines that write out words must take into account buffer wrapping\r
+; and not write a word at 0xffff (which causes an exception on 386s).\r
+;\r
+; The direction flag should be clear\r
+;\r
+;============================================================================\r
+\r
+DATASEG\r
+\r
+plotpixels     db      0c0h,030h,0ch,03h\r
+colorbyte      db      000000b,01010101b,10101010b,11111111b\r
+colorword      dw      0,5555h,0aaaah,0ffffh\r
+\r
+CODESEG\r
+\r
+;============================================================================\r
+;\r
+; VW_Plot (int x,y,color)\r
+;\r
+;============================================================================\r
+\r
+\r
+PROC   VW_Plot x:WORD, y:WORD, color:WORD\r
+PUBLIC VW_Plot\r
+USES   SI,DI\r
+\r
+       mov     es,[screenseg]\r
+\r
+       mov     di,[bufferofs]\r
+       mov     bx,[y]\r
+       shl     bx,1\r
+       add     di,[ylookup+bx]\r
+       mov     bx,[x]\r
+       mov     ax,bx\r
+       shr     ax,1\r
+       shr     ax,1\r
+       add     di,ax                           ; di = byte on screen\r
+\r
+       and     bx,3\r
+       mov     ah,[plotpixels+bx]\r
+       mov     bx,[color]\r
+       mov     cl,[colorbyte+bx]\r
+       and     cl,ah\r
+       not     ah\r
+\r
+       mov     al,[es:di]\r
+       and     al,ah                           ; mask off other pixels\r
+       or      al,cl\r
+       stosb\r
+\r
+       ret\r
+\r
+ENDP\r
+\r
+\r
+;============================================================================\r
+;\r
+; VW_Vlin (int yl,yh,x,color)\r
+;\r
+;============================================================================\r
+\r
+PROC   VW_Vlin yl:WORD, yh:WORD, x:WORD, color:WORD\r
+PUBLIC VW_Vlin\r
+USES   SI,DI\r
+\r
+       mov     es,[screenseg]\r
+\r
+       mov     di,[bufferofs]\r
+       mov     bx,[yl]\r
+       shl     bx,1\r
+       add     di,[ylookup+bx]\r
+       mov     bx,[x]\r
+       mov     ax,bx\r
+       shr     ax,1\r
+       shr     ax,1\r
+       add     di,ax                           ; di = byte on screen\r
+\r
+       and     bx,3\r
+       mov     ah,[plotpixels+bx]\r
+       mov     bx,[color]\r
+       mov     bl,[colorbyte+bx]\r
+       and     bl,ah\r
+       not     ah\r
+\r
+       mov     cx,[yh]\r
+       sub     cx,[yl]\r
+       inc     cx                                      ;number of pixels to plot\r
+\r
+       mov     dx,[linewidth]\r
+\r
+@@plot:\r
+       mov     al,[es:di]\r
+       and     al,ah                           ; mask off other pixels\r
+       or      al,bl\r
+       mov [es:di],al\r
+       add     di,dx\r
+       loop    @@plot\r
+\r
+       ret\r
+\r
+       ret\r
+\r
+ENDP\r
+\r
+\r
+;============================================================================\r
+\r
+\r
+;===================\r
+;\r
+; VW_DrawTile8\r
+;\r
+; xcoord in bytes (8 pixels), ycoord in pixels\r
+; All Tile8s are in one grseg, so an offset is calculated inside it\r
+;\r
+; DONE\r
+;\r
+;===================\r
+\r
+PROC   VW_DrawTile8    xcoord:WORD, ycoord:WORD, tile:WORD\r
+PUBLIC VW_DrawTile8\r
+USES   SI,DI\r
+\r
+       mov     es,[screenseg]\r
+\r
+       mov     di,[bufferofs]\r
+       add     di,[xcoord]\r
+       mov     bx,[ycoord]\r
+       shl     bx,1\r
+       add     di,[ylookup+bx]\r
+\r
+       mov     bx,[linewidth]\r
+       sub     bx,2\r
+\r
+       mov     si,[tile]\r
+       shl     si,1\r
+       shl     si,1\r
+       shl     si,1\r
+       shl     si,1\r
+\r
+       mov     ds,[grsegs+STARTTILE8*2] ; segment for all tile8s\r
+\r
+;\r
+; start drawing\r
+;\r
+\r
+REPT   7\r
+       movsb                                           ;no word moves because of segment wrapping\r
+       movsb\r
+       add     di,bx\r
+ENDM\r
+       movsb\r
+       movsb\r
+\r
+       mov     ax,ss\r
+       mov     ds,ax                                   ;restore turbo's data segment\r
+\r
+       ret\r
+\r
+ENDP\r
+\r
+\r
+;============================================================================\r
+;\r
+; VW_MaskBlock\r
+;\r
+; Draws a masked block shape to the screen.  bufferofs is NOT accounted for.\r
+; The mask comes first, then the data.  Seperate unwound routines are used\r
+; to speed drawing.\r
+;\r
+; Mask blocks will allways be an even width because of the way IGRAB works\r
+;\r
+; DONE\r
+;\r
+;============================================================================\r
+\r
+DATASEG\r
+\r
+UNWOUNDMASKS   =       18\r
+\r
+\r
+maskroutines   dw      mask0,mask0,mask2E,mask2O,mask4E,mask4O\r
+                               dw      mask6E,mask6O,mask8E,mask8O,mask10E,mask10O\r
+                               dw      mask12E,mask12O,mask14E,mask14O,mask16E,mask16O\r
+                               dw      mask18E,mask18O\r
+\r
+\r
+routinetouse   dw      ?\r
+\r
+CODESEG\r
+\r
+PROC   VW_MaskBlock    segm:WORD, ofs:WORD, dest:WORD, wide:WORD, height:WORD, planesize:WORD\r
+PUBLIC VW_MaskBlock\r
+USES   SI,DI\r
+\r
+       mov     es,[screenseg]\r
+\r
+       mov     di,[wide]\r
+       mov     dx,[linewidth]\r
+       sub     dx,di                                   ;dx = delta to start of next line\r
+\r
+       mov     bx,[planesize]                  ; si+bx = data location\r
+\r
+       cmp     di,UNWOUNDMASKS\r
+       jbe     @@unwoundroutine\r
+\r
+;==============\r
+;\r
+; General purpose masked block drawing.  This could be optimised into\r
+; four routines to use words, but few play loop sprites should be this big!\r
+;\r
+;==============\r
+\r
+       mov     [ss:linedelta],dx\r
+       mov     ds,[segm]\r
+       mov     si,[ofs]\r
+       mov     di,[dest]\r
+       mov     dx,[height]                             ;scan lines to draw\r
+\r
+@@lineloopgen:\r
+       mov     cx,[wide]\r
+@@byteloop:\r
+       mov     al,[es:di]\r
+       and     al,[si]\r
+       or      al,[bx+si]\r
+       inc     si\r
+       stosb\r
+       loop    @@byteloop\r
+\r
+       add     di,[ss:linedelta]\r
+       dec     dx\r
+       jnz     @@lineloopgen\r
+\r
+mask0:\r
+       mov     ax,ss\r
+       mov     ds,ax\r
+       ret                                                     ;width of 0 = no drawing\r
+\r
+\r
+;=================\r
+;\r
+; use the unwound routines\r
+;\r
+;=================\r
+\r
+@@unwoundroutine:\r
+       shr     di,1                                    ;we only have even width unwound routines\r
+       mov     cx,[dest]\r
+       shr     cx,1\r
+       rcl     di,1                                    ;shift a 1 in if destination is odd\r
+       shl     di,1\r
+       mov     ax,[maskroutines+di]    ;call the right routine\r
+\r
+       mov     ds,[segm]\r
+       mov     si,[ofs]\r
+       mov     di,[dest]\r
+       mov     cx,[height]                             ;scan lines to draw\r
+\r
+       jmp ax                                          ;draw it\r
+\r
+;=================\r
+;\r
+; Horizontally unwound routines to draw certain masked blocks faster\r
+;\r
+;=================\r
+\r
+MACRO  MASKBYTE\r
+       mov     al,[es:di]\r
+       and     al,[si]\r
+       or      al,[bx+si]\r
+       inc     si\r
+       stosb\r
+ENDM\r
+\r
+MACRO  MASKWORD\r
+       mov     ax,[es:di]\r
+       and     ax,[si]\r
+       or      ax,[bx+si]\r
+       inc     si\r
+       inc     si\r
+       stosw\r
+ENDM\r
+\r
+MACRO  SPRITELOOP      addr\r
+       add     di,dx\r
+       loop    addr\r
+       mov     ax,ss\r
+       mov     ds,ax\r
+       ret\r
+ENDM\r
+\r
+\r
+EVEN\r
+mask2E:\r
+       MASKWORD\r
+       SPRITELOOP      mask2E\r
+\r
+EVEN\r
+mask2O:\r
+       MASKBYTE\r
+       MASKBYTE\r
+       SPRITELOOP      mask2O\r
+\r
+EVEN\r
+mask4E:\r
+       MASKWORD\r
+       MASKWORD\r
+       SPRITELOOP      mask4E\r
+\r
+EVEN\r
+mask4O:\r
+       MASKBYTE\r
+       MASKWORD\r
+       MASKBYTE\r
+       SPRITELOOP      mask4O\r
+\r
+EVEN\r
+mask6E:\r
+       MASKWORD\r
+       MASKWORD\r
+       MASKWORD\r
+       SPRITELOOP      mask6E\r
+\r
+EVEN\r
+mask6O:\r
+       MASKBYTE\r
+       MASKWORD\r
+       MASKWORD\r
+       MASKBYTE\r
+       SPRITELOOP      mask6O\r
+\r
+EVEN\r
+mask8E:\r
+       MASKWORD\r
+       MASKWORD\r
+       MASKWORD\r
+       MASKWORD\r
+       SPRITELOOP      mask8E\r
+\r
+EVEN\r
+mask8O:\r
+       MASKBYTE\r
+       MASKWORD\r
+       MASKWORD\r
+       MASKWORD\r
+       MASKBYTE\r
+       SPRITELOOP      mask8O\r
+\r
+EVEN\r
+mask10E:\r
+       MASKWORD\r
+       MASKWORD\r
+       MASKWORD\r
+       MASKWORD\r
+       MASKWORD\r
+       SPRITELOOP      mask10E\r
+\r
+EVEN\r
+mask10O:\r
+       MASKBYTE\r
+       MASKWORD\r
+       MASKWORD\r
+       MASKWORD\r
+       MASKWORD\r
+       MASKBYTE\r
+       SPRITELOOP      mask10O\r
+\r
+EVEN\r
+mask12E:\r
+       MASKWORD\r
+       MASKWORD\r
+       MASKWORD\r
+       MASKWORD\r
+       MASKWORD\r
+       MASKWORD\r
+       SPRITELOOP      mask12E\r
+\r
+EVEN\r
+mask12O:\r
+       MASKBYTE\r
+       MASKWORD\r
+       MASKWORD\r
+       MASKWORD\r
+       MASKWORD\r
+       MASKWORD\r
+       MASKBYTE\r
+       SPRITELOOP      mask12O\r
+\r
+EVEN\r
+mask14E:\r
+       MASKWORD\r
+       MASKWORD\r
+       MASKWORD\r
+       MASKWORD\r
+       MASKWORD\r
+       MASKWORD\r
+       MASKWORD\r
+       SPRITELOOP      mask14E\r
+\r
+EVEN\r
+mask14O:\r
+       MASKBYTE\r
+       MASKWORD\r
+       MASKWORD\r
+       MASKWORD\r
+       MASKWORD\r
+       MASKWORD\r
+       MASKWORD\r
+       MASKBYTE\r
+       SPRITELOOP      mask14O\r
+\r
+EVEN\r
+mask16E:\r
+       MASKWORD\r
+       MASKWORD\r
+       MASKWORD\r
+       MASKWORD\r
+       MASKWORD\r
+       MASKWORD\r
+       MASKWORD\r
+       MASKWORD\r
+       SPRITELOOP      mask16E\r
+\r
+EVEN\r
+mask16O:\r
+       MASKBYTE\r
+       MASKWORD\r
+       MASKWORD\r
+       MASKWORD\r
+       MASKWORD\r
+       MASKWORD\r
+       MASKWORD\r
+       MASKWORD\r
+       MASKBYTE\r
+       SPRITELOOP      mask16O\r
+\r
+EVEN\r
+mask18E:\r
+       MASKWORD\r
+       MASKWORD\r
+       MASKWORD\r
+       MASKWORD\r
+       MASKWORD\r
+       MASKWORD\r
+       MASKWORD\r
+       MASKWORD\r
+       MASKWORD\r
+       SPRITELOOP      mask18E\r
+\r
+EVEN\r
+mask18O:\r
+       MASKBYTE\r
+       MASKWORD\r
+       MASKWORD\r
+       MASKWORD\r
+       MASKWORD\r
+       MASKWORD\r
+       MASKWORD\r
+       MASKWORD\r
+       MASKWORD\r
+       MASKBYTE\r
+       SPRITELOOP      mask18O\r
+\r
+\r
+ENDP\r
+\r
+\r
+;============================================================================\r
+;\r
+; VW_InverseMask\r
+;\r
+; Draws a masked block shape to the screen.  bufferofs is NOT accounted for.\r
+; The mask comes first, then the data.\r
+;\r
+; Mask blocks will allways be an even width because of the way IGRAB works\r
+;\r
+;============================================================================\r
+\r
+PROC   VW_InverseMask  segm:WORD, ofs:WORD, dest:WORD, wide:WORD, height:WORD\r
+PUBLIC VW_InverseMask\r
+USES   SI,DI\r
+\r
+       mov     es, [screenseg]\r
+       mov     ax, [wide]\r
+       mov     dx, [linewidth]\r
+       sub     dx, ax;\r
+       mov     ds, [segm]\r
+       mov     si, [ofs]\r
+       mov     di, [dest]\r
+       mov     bx, [height]\r
+       shr     [wide], 1\r
+@@yloop:\r
+       mov     cx, [wide]\r
+@@xloop:\r
+       lodsw\r
+       not     ax\r
+       or      [es:di], ax\r
+       inc     di\r
+       inc     di\r
+       loop    @@xloop\r
+       add     di, dx\r
+       dec     bx\r
+       jnz     @@yloop\r
+\r
+       mov     ax,ss\r
+       mov     ds,ax                                   ;restore turbo's data segment\r
+\r
+       ret\r
+ENDP\r
+\r
+\r
+;============================================================================\r
+;\r
+; VW_ScreenToScreen\r
+;\r
+; Basic block copy routine.  Copies one block of screen memory to another,\r
+; bufferofs is NOT accounted for.\r
+;\r
+; DONE\r
+;\r
+;============================================================================\r
+\r
+PROC   VW_ScreenToScreen       source:WORD, dest:WORD, wide:WORD, height:WORD\r
+PUBLIC VW_ScreenToScreen\r
+USES   SI,DI\r
+\r
+       mov     bx,[linewidth]\r
+       sub     bx,[wide]\r
+\r
+       mov     ax,[screenseg]\r
+       mov     es,ax\r
+       mov     ds,ax\r
+\r
+       mov     si,[source]\r
+       mov     di,[dest]                               ;start at same place in all planes\r
+       mov     dx,[height]                             ;scan lines to draw\r
+       mov     ax,[wide]\r
+;\r
+; if the width, source, and dest are all even, use word moves\r
+; This is allways the case in the CGA refresh\r
+;\r
+       test    ax,1\r
+       jnz     @@bytelineloop\r
+       test    si,1\r
+       jnz     @@bytelineloop\r
+       test    di,1\r
+       jnz     @@bytelineloop\r
+\r
+       shr     ax,1\r
+@@wordlineloop:\r
+       mov     cx,ax\r
+       rep     movsw\r
+       add     si,bx\r
+       add     di,bx\r
+\r
+       dec     dx\r
+       jnz     @@wordlineloop\r
+\r
+       mov     ax,ss\r
+       mov     ds,ax                                   ;restore turbo's data segment\r
+\r
+       ret\r
+\r
+@@bytelineloop:\r
+       mov     cx,ax\r
+       rep     movsb\r
+       add     si,bx\r
+       add     di,bx\r
+\r
+       dec     dx\r
+       jnz     @@bytelineloop\r
+\r
+       mov     ax,ss\r
+       mov     ds,ax                                   ;restore turbo's data segment\r
+\r
+       ret\r
+\r
+ENDP\r
+\r
+\r
+;============================================================================\r
+;\r
+; VW_MemToScreen\r
+;\r
+; Basic block drawing routine. Takes a block shape at segment pointer source\r
+; of width by height data, and draws it to dest in the virtual screen,\r
+; based on linewidth.  bufferofs is NOT accounted for.\r
+; There are four drawing routines to provide the best optimized code while\r
+; accounting for odd segment wrappings due to the floating screens.\r
+;\r
+; DONE\r
+;\r
+;============================================================================\r
+\r
+DATASEG\r
+\r
+memtoscreentable       dw      eventoeven,eventoodd,oddtoeven,oddtoodd\r
+\r
+CODESEG\r
+\r
+\r
+PROC   VW_MemToScreen  source:WORD, dest:WORD, wide:WORD, height:WORD\r
+PUBLIC VW_MemToScreen\r
+USES   SI,DI\r
+\r
+       mov     es,[screenseg]\r
+\r
+       mov     bx,[linewidth]\r
+       sub     bx,[wide]\r
+\r
+       mov     ds,[source]\r
+\r
+       xor     si,si                                   ;block is segment aligned\r
+\r
+       xor     di,di\r
+       shr     [wide],1                                ;change wide to words, and see if carry is set\r
+       rcl     di,1                                    ;1 if wide is odd\r
+       mov     ax,[dest]\r
+       shr     ax,1\r
+       rcl     di,1                                    ;shift a 1 in if destination is odd\r
+       shl     di,1                                    ;to index into a word width table\r
+       mov     dx,[height]                             ;scan lines to draw\r
+       mov     ax,[wide]\r
+       jmp     [ss:memtoscreentable+di]        ;call the right routine\r
+\r
+;==============\r
+;\r
+; Copy an even width block to an even destination address\r
+;\r
+;==============\r
+\r
+eventoeven:\r
+       mov     di,[dest]                               ;start at same place in all planes\r
+EVEN\r
+@@lineloopEE:\r
+       mov     cx,ax\r
+       rep     movsw\r
+       add     di,bx\r
+       dec     dx\r
+       jnz     @@lineloopEE\r
+\r
+       mov     ax,ss\r
+       mov     ds,ax                                   ;restore turbo's data segment\r
+\r
+       ret\r
+\r
+;==============\r
+;\r
+; Copy an odd width block to an even video address\r
+;\r
+;==============\r
+\r
+oddtoeven:\r
+       mov     di,[dest]                               ;start at same place in all planes\r
+EVEN\r
+@@lineloopOE:\r
+       mov     cx,ax\r
+       rep     movsw\r
+       movsb                                           ;copy the last byte\r
+       add     di,bx\r
+       dec     dx\r
+       jnz     @@lineloopOE\r
+\r
+       mov     ax,ss\r
+       mov     ds,ax                                   ;restore turbo's data segment\r
+\r
+       ret\r
+\r
+;==============\r
+;\r
+; Copy an even width block to an odd video address\r
+;\r
+;==============\r
+\r
+eventoodd:\r
+       mov     di,[dest]                               ;start at same place in all planes\r
+       dec     ax                                              ;one word has to be handled seperately\r
+EVEN\r
+@@lineloopEO:\r
+       movsb\r
+       mov     cx,ax\r
+       rep     movsw\r
+       movsb\r
+       add     di,bx\r
+       dec     dx\r
+       jnz     @@lineloopEO\r
+\r
+       mov     ax,ss\r
+       mov     ds,ax                                   ;restore turbo's data segment\r
+\r
+       ret\r
+\r
+;==============\r
+;\r
+; Copy an odd width block to an odd video address\r
+;\r
+;==============\r
+\r
+oddtoodd:\r
+       mov     di,[dest]                               ;start at same place in all planes\r
+EVEN\r
+@@lineloopOO:\r
+       movsb\r
+       mov     cx,ax\r
+       rep     movsw\r
+       add     di,bx\r
+       dec     dx\r
+       jnz     @@lineloopOO\r
+\r
+       mov     ax,ss\r
+       mov     ds,ax                                   ;restore turbo's data segment\r
+       ret\r
+\r
+\r
+ENDP\r
+\r
+;===========================================================================\r
+;\r
+; VW_ScreenToMem\r
+;\r
+; Copies a block of video memory to main memory, in order from planes 0-3.\r
+; This could be optimized along the lines of VW_MemToScreen to take advantage\r
+; of word copies, but this is an infrequently called routine.\r
+;\r
+; DONE\r
+;\r
+;===========================================================================\r
+\r
+PROC   VW_ScreenToMem  source:WORD, dest:WORD, wide:WORD, height:WORD\r
+PUBLIC VW_ScreenToMem\r
+USES   SI,DI\r
+\r
+       mov     es,[dest]\r
+\r
+       mov     bx,[linewidth]\r
+       sub     bx,[wide]\r
+\r
+       mov     ds,[screenseg]\r
+\r
+       xor     di,di\r
+\r
+       mov     si,[source]\r
+       mov     dx,[height]                             ;scan lines to draw\r
+\r
+@@lineloop:\r
+       mov     cx,[wide]\r
+       rep     movsb\r
+\r
+       add     si,bx\r
+\r
+       dec     dx\r
+       jnz     @@lineloop\r
+\r
+       mov     ax,ss\r
+       mov     ds,ax                                   ;restore turbo's data segment\r
+\r
+       ret\r
+\r
+ENDP\r
+\r
+\r
+;===========================================================================\r
+;\r
+;                    MISC CGA ROUTINES\r
+;\r
+;===========================================================================\r
+\r
+;==============\r
+;\r
+; VW_SetScreen\r
+;\r
+; DONE\r
+;\r
+;==============\r
+\r
+PROC   VW_SetScreen  crtc:WORD\r
+PUBLIC VW_SetScreen\r
+\r
+;\r
+; for some reason, my XT's EGA card doesn't like word outs to the CRTC\r
+; index...\r
+;\r
+       cli\r
+\r
+       mov     cx,[crtc]\r
+       mov     dx,CRTC_INDEX\r
+       mov     al,0ch          ;start address high register\r
+       out     dx,al\r
+       inc     dx\r
+       mov     al,ch\r
+       out     dx,al\r
+       dec     dx\r
+       mov     al,0dh          ;start address low register\r
+       out     dx,al\r
+       mov     al,cl\r
+       inc     dx\r
+       out     dx,al\r
+\r
+       sti\r
+\r
+       ret\r
+\r
+ENDP\r
+\r
+\r
+if NUMFONT+NUMFONTM\r
+\r
+;===========================================================================\r
+;\r
+; GENERAL FONT DRAWING ROUTINES\r
+;\r
+;===========================================================================\r
+\r
+DATASEG\r
+\r
+px     dw      ?                                       ; proportional character drawing coordinates\r
+py     dw      ?\r
+pdrawmode      db      11000b          ; 8 = OR, 24 = XOR, put in GC_DATAROTATE\r
+fontcolor      db      15              ;0-15 mapmask value\r
+\r
+PUBLIC px,py,pdrawmode,fontcolor\r
+\r
+;\r
+; offsets in font structure\r
+;\r
+pcharheight    =       0               ;lines high\r
+charloc                =       2               ;pointers to every character\r
+charwidth      =       514             ;every character's width in pixels\r
+\r
+\r
+propchar       dw      ?                       ; the character number to shift\r
+stringptr      dw      ?,?\r
+\r
+fontcolormask  dw      ?                       ; font color expands into this\r
+\r
+BUFFWIDTH      =       100\r
+BUFFHEIGHT     =   32                  ; must be twice as high as font for masked fonts\r
+\r
+databuffer     db      BUFFWIDTH*BUFFHEIGHT dup (?)\r
+\r
+bufferwidth    dw      ?                                               ; bytes with valid info / line\r
+bufferheight dw        ?                                               ; number of lines currently used\r
+\r
+bufferbyte     dw      ?\r
+bufferbit      dw      ?\r
+PUBLIC bufferwidth,bufferheight,bufferbyte,bufferbit\r
+\r
+screenspot     dw      ?                                               ; where the buffer is going\r
+\r
+bufferextra    dw      ?                                               ; add at end of a line copy\r
+screenextra    dw      ?\r
+\r
+CODESEG\r
+\r
+;======================\r
+;\r
+; Macros to table shift a byte of font\r
+;\r
+;======================\r
+\r
+MACRO  SHIFTNOXOR\r
+       mov     al,[es:bx]              ; source\r
+       xor     ah,ah\r
+       shl     ax,1\r
+       mov     si,ax\r
+       mov     ax,[bp+si]              ; table shift into two bytes\r
+       or      [di],al                 ; or with first byte\r
+       inc     di\r
+       mov     [di],ah                 ; replace next byte\r
+       inc     bx                              ; next source byte\r
+ENDM\r
+\r
+MACRO  SHIFTWITHXOR\r
+       mov     al,[es:bx]              ; source\r
+       xor     ah,ah\r
+       shl     ax,1\r
+       mov     si,ax\r
+       mov     ax,[bp+si]              ; table shift into two bytes\r
+       not     ax\r
+       and     [di],al                 ; and with first byte\r
+       inc     di\r
+       mov     [di],ah                 ; replace next byte\r
+       inc     bx                              ; next source byte\r
+ENDM\r
+\r
+\r
+;=======================\r
+;\r
+; VWL_XORBuffer\r
+;\r
+; Pass buffer start in SI (somewhere in databuffer)\r
+; Draws the buffer to the screen buffer\r
+;\r
+;========================\r
+\r
+PROC   VWL_XORBuffer   NEAR\r
+USES   BP\r
+       mov     bl,[fontcolor]\r
+       xor     bh,bh\r
+       shl     bx,1\r
+       mov     ax,[colorword+bx]\r
+       mov     [fontcolormask],ax\r
+\r
+       mov     es,[screenseg]\r
+       mov     di,[screenspot]\r
+\r
+       mov     bx,[bufferwidth]                ;calculate offsets for end of each line\r
+       mov     [bufferwidth],bx\r
+\r
+       or      bx,bx\r
+       jnz     @@isthere\r
+       ret                                                     ;nothing to draw\r
+\r
+@@isthere:\r
+       test    bx,1\r
+       jnz     @@odd\r
+       jmp     @@even\r
+;\r
+; clear the last byte so word draws can be used\r
+;\r
+@@odd:\r
+       mov     al,0\r
+line   =       0\r
+REPT   BUFFHEIGHT\r
+       mov     [BYTE databuffer+BUFFWIDTH*line+bx],al\r
+line   =       line+1\r
+ENDM\r
+\r
+       inc     bx\r
+@@even:\r
+       mov     ax,[linewidth]\r
+       sub     ax,bx\r
+       mov     [screenextra],ax\r
+       mov     ax,BUFFWIDTH\r
+       sub     ax,bx\r
+       mov     [bufferextra],ax\r
+       mov     dx,bx\r
+\r
+       mov     bx,[bufferheight]               ;lines to copy\r
+       mov     bp,[fontcolormask]\r
+@@lineloop:\r
+       mov     cx,dx\r
+@@byteloop:\r
+       lodsb                                           ;get a word from the buffer\r
+       and     ax,bp\r
+       xor     [es:di],al                              ;draw it\r
+       inc     di\r
+       loop    @@byteloop\r
+\r
+       add     si,[bufferextra]\r
+       add     di,[screenextra]\r
+\r
+       dec     bx\r
+       jnz     @@lineloop\r
+\r
+       ret\r
+ENDP\r
+\r
+\r
+DATASEG\r
+\r
+;============================================================================\r
+;\r
+; NON MASKED FONT DRAWING ROUTINES\r
+;\r
+;============================================================================\r
+\r
+if numfont\r
+\r
+DATASEG\r
+\r
+shiftdrawtable dw      0,shift1wide,shift2wide,shift3wide,shift4wide\r
+                               dw              shift5wide,shift6wide\r
+\r
+CODESEG\r
+\r
+;==================\r
+;\r
+; ShiftPropChar\r
+;\r
+; Call with BX = character number (0-255)\r
+; Draws one character to the buffer at bufferbyte/bufferbit, and adjusts\r
+; them to the new position\r
+;\r
+;==================\r
+\r
+PROC   ShiftPropChar   NEAR\r
+\r
+       mov     si,[fontnumber]\r
+       shl     si,1\r
+       mov     es,[grsegs+STARTFONT*2+si]      ;segment of font to use\r
+\r
+;\r
+; find character location, width, and height\r
+;\r
+       mov     si,[es:charwidth+bx]\r
+       and     si,0ffh                                 ;SI hold width in pixels\r
+       shl     bx,1\r
+       mov     bx,[es:charloc+bx]              ;BX holds pointer to character data\r
+\r
+;\r
+; look up which shift table to use, based on bufferbit\r
+;\r
+       mov     di,[bufferbit]\r
+       shl     di,1\r
+       mov     bp,[shifttabletable+di] ;BP holds pointer to shift table\r
+\r
+       mov     di,OFFSET databuffer\r
+       add     di,[bufferbyte]                 ;DI holds pointer to buffer\r
+\r
+       mov     cx,[bufferbit]\r
+       add     cx,si                                   ;add twice because pixel == two bits\r
+       add     cx,si                                   ;new bit position\r
+       mov     ax,cx\r
+       and     ax,7\r
+       mov     [bufferbit],ax                  ;new bit position\r
+       mov     ax,cx\r
+       shr     ax,1\r
+       shr     ax,1\r
+       shr     ax,1\r
+       add     [bufferbyte],ax                 ;new byte position\r
+\r
+       add     si,3\r
+       shr     si,1\r
+       shr     si,1                                    ;bytes the character is wide\r
+       shl     si,1                    ;*2 to look up in shiftdrawtable\r
+\r
+       mov     cx,[es:pcharheight]\r
+       mov     dx,BUFFWIDTH\r
+       jmp     [ss:shiftdrawtable+si]  ;procedure to draw this width\r
+\r
+;\r
+; one byte character\r
+;\r
+shift1wide:\r
+       dec     dx\r
+EVEN\r
+@@loop1:\r
+       SHIFTNOXOR\r
+       add     di,dx                   ; next line in buffer\r
+\r
+       loop    @@loop1\r
+\r
+       ret\r
+\r
+;\r
+; two byte character\r
+;\r
+shift2wide:\r
+       dec     dx\r
+       dec     dx\r
+EVEN\r
+@@loop2:\r
+       SHIFTNOXOR\r
+       SHIFTNOXOR\r
+       add     di,dx                   ; next line in buffer\r
+\r
+       loop    @@loop2\r
+\r
+       ret\r
+\r
+;\r
+; three byte character\r
+;\r
+shift3wide:\r
+       sub     dx,3\r
+EVEN\r
+@@loop3:\r
+       SHIFTNOXOR\r
+       SHIFTNOXOR\r
+       SHIFTNOXOR\r
+       add     di,dx                   ; next line in buffer\r
+\r
+       loop    @@loop3\r
+\r
+       ret\r
+\r
+\r
+;\r
+; four byte character\r
+;\r
+shift4wide:\r
+       sub     dx,4\r
+EVEN\r
+@@loop4:\r
+       SHIFTNOXOR\r
+       SHIFTNOXOR\r
+       SHIFTNOXOR\r
+       SHIFTNOXOR\r
+       add     di,dx                   ; next line in buffer\r
+\r
+       loop    @@loop4\r
+\r
+       ret\r
+\r
+\r
+;\r
+; five byte character\r
+;\r
+shift5wide:\r
+       sub     dx,5\r
+EVEN\r
+@@loop5:\r
+       SHIFTNOXOR\r
+       SHIFTNOXOR\r
+       SHIFTNOXOR\r
+       SHIFTNOXOR\r
+       SHIFTNOXOR\r
+       add     di,dx                   ; next line in buffer\r
+\r
+       loop    @@loop5\r
+\r
+       ret\r
+\r
+;\r
+; six byte character\r
+;\r
+shift6wide:\r
+       sub     dx,6\r
+EVEN\r
+@@loop6:\r
+       SHIFTNOXOR\r
+       SHIFTNOXOR\r
+       SHIFTNOXOR\r
+       SHIFTNOXOR\r
+       SHIFTNOXOR\r
+       SHIFTNOXOR\r
+       add     di,dx                   ; next line in buffer\r
+\r
+       loop    @@loop6\r
+\r
+       ret\r
+\r
+\r
+\r
+ENDP\r
+\r
+;============================================================================\r
+\r
+;==================\r
+;\r
+; VW_DrawPropString\r
+;\r
+; Draws a C string of characters at px/py and advances px\r
+;\r
+;==================\r
+\r
+CODESEG\r
+\r
+PROC   VW_DrawPropString       string:DWORD\r
+PUBLIC VW_DrawPropString\r
+USES   SI,DI\r
+\r
+;\r
+; proportional spaceing, which clears the buffer ahead of it, so only\r
+; clear the first collumn\r
+;\r
+       mov     al,0\r
+line   =       0\r
+REPT   BUFFHEIGHT\r
+       mov     [BYTE databuffer+BUFFWIDTH*line],al\r
+line   =       line+1\r
+ENDM\r
+\r
+;\r
+; shift the characters into the buffer\r
+;\r
+@@shiftchars:\r
+       mov     ax,[px]\r
+       and     ax,3\r
+       shl     ax,1                    ;one pixel == two bits\r
+       mov     [bufferbit],ax\r
+       mov     [bufferbyte],0\r
+\r
+       mov     ax,[WORD string]\r
+       mov     [stringptr],ax\r
+       mov     ax,[WORD string+2]\r
+       mov     [stringptr+2],ax\r
+\r
+@@shiftone:\r
+       mov     es,[stringptr+2]\r
+       mov     bx,[stringptr]\r
+       inc     [stringptr]\r
+       mov     bx,[es:bx]\r
+       xor     bh,bh\r
+       or      bl,bl\r
+       jz      @@allshifted\r
+       call    ShiftPropChar\r
+       jmp     @@shiftone\r
+\r
+@@allshifted:\r
+;\r
+; calculate position to draw buffer on screen\r
+;\r
+       mov     bx,[py]\r
+       shl     bx,1\r
+       mov     di,[ylookup+bx]\r
+       add     di,[bufferofs]\r
+       add     di,[panadjust]\r
+\r
+       mov     ax,[px]\r
+       shr     ax,1\r
+       shr     ax,1            ;x location in bytes\r
+       add     di,ax\r
+       mov     [screenspot],di\r
+\r
+;\r
+; advance px\r
+;\r
+       mov     ax,[bufferbyte]\r
+       shl     ax,1\r
+       shl     ax,1\r
+       mov     bx,[bufferbit]\r
+       shr     bx,1                    ;two bits == one pixel\r
+       or      ax,bx\r
+       add     [px],ax\r
+\r
+;\r
+; draw it\r
+;\r
+       mov     ax,[bufferbyte]\r
+       test    [bufferbit],7\r
+       jz      @@go\r
+       inc     ax                              ;so the partial byte also gets drawn\r
+@@go:\r
+       mov     [bufferwidth],ax\r
+       mov     si,[fontnumber]\r
+       shl     si,1\r
+       mov     es,[grsegs+STARTFONT*2+si]\r
+       mov     ax,[es:pcharheight]\r
+       mov     [bufferheight],ax\r
+\r
+       mov     si,OFFSET databuffer\r
+       call    VWL_XORBuffer\r
+\r
+       ret\r
+\r
+ENDP\r
+\r
+endif  ;numfont\r
+\r
+;============================================================================\r
+;\r
+; MASKED FONT DRAWING ROUTINES\r
+;\r
+;============================================================================\r
+\r
+if     numfontm\r
+\r
+DATASEG\r
+\r
+mshiftdrawtable        dw      0,mshift1wide,mshift2wide,mshift3wide\r
+\r
+\r
+CODESEG\r
+\r
+;==================\r
+;\r
+; ShiftMPropChar\r
+;\r
+; Call with BX = character number (0-255)\r
+; Draws one character to the buffer at bufferbyte/bufferbit, and adjusts\r
+; them to the new position\r
+;\r
+;==================\r
+\r
+PROC   ShiftMPropChar  NEAR\r
+\r
+       mov     es,[grsegs+STARTFONTM*2]        ;segment of font to use\r
+\r
+;\r
+; find character location, width, and height\r
+;\r
+       mov     si,[es:charwidth+bx]\r
+       and     si,0ffh                                 ;SI hold width in pixels\r
+       shl     bx,1\r
+       mov     bx,[es:charloc+bx]              ;BX holds pointer to character data\r
+\r
+;\r
+; look up which shift table to use, based on bufferbit\r
+;\r
+       mov     di,[bufferbit]\r
+       shl     di,1\r
+       mov     bp,[shifttabletable+di] ;BP holds pointer to shift table\r
+\r
+       mov     di,OFFSET databuffer\r
+       add     di,[bufferbyte]                 ;DI holds pointer to buffer\r
+\r
+;\r
+; advance position by character width\r
+;\r
+       mov     cx,[bufferbit]\r
+       add     cx,si                                   ;new bit position\r
+       mov     ax,cx\r
+       and     ax,7\r
+       mov     [bufferbit],ax                  ;new bit position\r
+       mov     ax,cx\r
+       shr     ax,1\r
+       shr     ax,1\r
+       shr     ax,1\r
+       add     [bufferbyte],ax                 ;new byte position\r
+\r
+       add     si,7\r
+       shr     si,1\r
+       shr     si,1\r
+       shr     si,1                                    ;bytes the character is wide\r
+       shl     si,1                    ;*2 to look up in shiftdrawtable\r
+\r
+       mov     cx,[es:pcharheight]\r
+       mov     dx,BUFFWIDTH\r
+       jmp     [ss:mshiftdrawtable+si] ;procedure to draw this width\r
+\r
+;\r
+; one byte character\r
+;\r
+mshift1wide:\r
+       dec     dx\r
+\r
+EVEN\r
+@@loop1m:\r
+       SHIFTWITHXOR\r
+       add     di,dx                   ; next line in buffer\r
+\r
+       loop    @@loop1m\r
+\r
+       mov     cx,[es:pcharheight]\r
+\r
+EVEN\r
+@@loop1:\r
+       SHIFTNOXOR\r
+       add     di,dx                   ; next line in buffer\r
+       loop    @@loop1\r
+\r
+       ret\r
+\r
+;\r
+; two byte character\r
+;\r
+mshift2wide:\r
+       dec     dx\r
+       dec     dx\r
+EVEN\r
+@@loop2m:\r
+       SHIFTWITHXOR\r
+       SHIFTWITHXOR\r
+       add     di,dx                   ; next line in buffer\r
+\r
+       loop    @@loop2m\r
+\r
+       mov     cx,[es:pcharheight]\r
+\r
+EVEN\r
+@@loop2:\r
+       SHIFTNOXOR\r
+       SHIFTNOXOR\r
+       add     di,dx                   ; next line in buffer\r
+       loop    @@loop2\r
+\r
+       ret\r
+\r
+;\r
+; three byte character\r
+;\r
+mshift3wide:\r
+       sub     dx,3\r
+EVEN\r
+@@loop3m:\r
+       SHIFTWITHXOR\r
+       SHIFTWITHXOR\r
+       SHIFTWITHXOR\r
+       add     di,dx                   ; next line in buffer\r
+\r
+       loop    @@loop3m\r
+\r
+       mov     cx,[es:pcharheight]\r
+\r
+EVEN\r
+@@loop3:\r
+       SHIFTNOXOR\r
+       SHIFTNOXOR\r
+       SHIFTNOXOR\r
+       add     di,dx                   ; next line in buffer\r
+       loop    @@loop3\r
+\r
+       ret\r
+\r
+\r
+ENDP\r
+\r
+;============================================================================\r
+\r
+;==================\r
+;\r
+; VW_DrawMPropString\r
+;\r
+; Draws a C string of characters at px/py and advances px\r
+;\r
+;==================\r
+\r
+\r
+\r
+PROC   VW_DrawMPropString      string:DWORD\r
+PUBLIC VW_DrawMPropString\r
+USES   SI,DI\r
+\r
+;\r
+; clear out the first byte of the buffer, the rest will automatically be\r
+; cleared as characters are drawn into it\r
+;\r
+       mov     es,[grsegs+STARTFONTM*2]\r
+       mov     dx,[es:pcharheight]\r
+       mov     di,OFFSET databuffer\r
+       mov     ax,ds\r
+       mov     es,ax\r
+       mov     bx,BUFFWIDTH-1\r
+\r
+       mov     cx,dx\r
+       mov     al,0ffh\r
+@@maskfill:\r
+       stosb                           ; fill the mask part with $ff\r
+       add     di,bx\r
+       loop    @@maskfill\r
+\r
+       mov     cx,dx\r
+       xor     al,al\r
+@@datafill:\r
+       stosb                           ; fill the data part with $0\r
+       add     di,bx\r
+       loop    @@datafill\r
+\r
+;\r
+; shift the characters into the buffer\r
+;\r
+       mov     ax,[px]\r
+       and     ax,7\r
+       mov     [bufferbit],ax\r
+       mov     [bufferbyte],0\r
+\r
+       mov     ax,[WORD string]\r
+       mov     [stringptr],ax\r
+       mov     ax,[WORD string+2]\r
+       mov     [stringptr+2],ax\r
+\r
+@@shiftone:\r
+       mov     es,[stringptr+2]\r
+       mov     bx,[stringptr]\r
+       inc     [stringptr]\r
+       mov     bx,[es:bx]\r
+       xor     bh,bh\r
+       or      bl,bl\r
+       jz      @@allshifted\r
+       call    ShiftMPropChar\r
+       jmp     @@shiftone\r
+\r
+@@allshifted:\r
+;\r
+; calculate position to draw buffer on screen\r
+;\r
+       mov     bx,[py]\r
+       shl     bx,1\r
+       mov     di,[ylookup+bx]\r
+       add     di,[bufferofs]\r
+\r
+       mov     ax,[px]\r
+       shr     ax,1\r
+       shr     ax,1\r
+       shr     ax,1            ;x location in bytes\r
+       add     di,ax\r
+       mov     [screenspot],di\r
+\r
+;\r
+; advance px\r
+;\r
+       mov     ax,[bufferbyte]\r
+       shl     ax,1\r
+       shl     ax,1\r
+       shl     ax,1\r
+       or      ax,[bufferbit]\r
+       add     [px],ax\r
+\r
+;\r
+; draw it\r
+;\r
+       mov     ax,[bufferbyte]\r
+       test    [bufferbit],7\r
+       jz      @@go\r
+       inc     ax                              ;so the partial byte also gets drawn\r
+@@go:\r
+       mov     [bufferwidth],ax\r
+       mov     es,[grsegs+STARTFONTM*2]\r
+       mov     ax,[es:pcharheight]\r
+       mov     [bufferheight],ax\r
+\r
+       mov     si,OFFSET databuffer\r
+       call    BufferToScreen          ; cut out mask\r
+                                                               ; or in data\r
+       call    BufferToScreen          ; SI is still in the right position in buffer\r
+\r
+       ret\r
+\r
+ENDP\r
+\r
+endif          ; if numfontm\r
+\r
+endif          ; if fonts\r
diff --git a/16/keen456/KEEN4-6/ID_VW_AE.ASM b/16/keen456/KEEN4-6/ID_VW_AE.ASM
new file mode 100755 (executable)
index 0000000..37d4e9e
--- /dev/null
@@ -0,0 +1,1824 @@
+; Reconstructed Commander Keen 4-6 Source Code\r
+; Copyright (C) 2021 K1n9_Duk3\r
+;\r
+; This file is primarily based on:\r
+; Catacomb 3-D Source Code\r
+; Copyright (C) 1993-2014 Flat Rock Software\r
+;\r
+; This program is free software; you can redistribute it and/or modify\r
+; it under the terms of the GNU General Public License as published by\r
+; the Free Software Foundation; either version 2 of the License, or\r
+; (at your option) any later version.\r
+;\r
+; This program is distributed in the hope that it will be useful,\r
+; but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+; GNU General Public License for more details.\r
+;\r
+; You should have received a copy of the GNU General Public License along\r
+; with this program; if not, write to the Free Software Foundation, Inc.,\r
+; 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+\r
+;=================================\r
+;\r
+; EGA view manager routines\r
+;\r
+;=================================\r
+\r
+;============================================================================\r
+;\r
+; All EGA drawing routines that write out words need to have alternate forms\r
+; for starting on even and odd addresses, because writing a word at segment\r
+; offset 0xffff causes an exception!  To work around this, write a single\r
+; byte out to make the address even, so it wraps cleanly at the end.\r
+;\r
+; All of these routines assume read/write mode 0, and will allways return\r
+; in that state.\r
+; The direction flag should be clear\r
+; readmap/writemask is left in an undefined state\r
+;\r
+;============================================================================\r
+\r
+\r
+;============================================================================\r
+;\r
+; VW_Plot (int x,y,color)\r
+;\r
+;============================================================================\r
+\r
+DATASEG\r
+\r
+plotpixels     db      128,64,32,16,8,4,2,1\r
+\r
+CODESEG\r
+\r
+PROC   VW_Plot x:WORD, y:WORD, color:WORD\r
+PUBLIC VW_Plot\r
+USES   SI,DI\r
+\r
+       mov     es,[screenseg]\r
+\r
+       mov     dx,SC_INDEX\r
+       mov     ax,SC_MAPMASK+15*256\r
+       WORDOUT\r
+\r
+       mov     dx,GC_INDEX\r
+       mov     ax,GC_MODE+2*256        ;write mode 2\r
+       WORDOUT\r
+\r
+       mov     di,[bufferofs]\r
+       mov     bx,[y]\r
+       shl     bx,1\r
+       add     di,[ylookup+bx]\r
+       mov     bx,[x]\r
+       mov     ax,bx\r
+       shr     ax,1\r
+       shr     ax,1\r
+       shr     ax,1\r
+       add     di,ax                           ; di = byte on screen\r
+\r
+       and     bx,7\r
+       mov     ah,[plotpixels+bx]\r
+       mov     al,GC_BITMASK           ;mask off other pixels\r
+       WORDOUT\r
+\r
+       mov             bl,[BYTE color]\r
+       xchg    bl,[es:di]              ; load latches and write pixel\r
+\r
+       mov     dx,GC_INDEX\r
+       mov     ah,0ffh                         ;no bit mask\r
+       WORDOUT\r
+       mov     ax,GC_MODE+0*256        ;write mode 0\r
+       WORDOUT\r
+\r
+       ret\r
+\r
+ENDP\r
+\r
+\r
+;============================================================================\r
+;\r
+; VW_Vlin (int yl,yh,x,color)\r
+;\r
+;============================================================================\r
+\r
+PROC   VW_Vlin yl:WORD, yh:WORD, x:WORD, color:WORD\r
+PUBLIC VW_Vlin\r
+USES   SI,DI\r
+\r
+       mov     es,[screenseg]\r
+\r
+       mov     dx,SC_INDEX\r
+       mov     ax,SC_MAPMASK+15*256\r
+       WORDOUT\r
+\r
+       mov     dx,GC_INDEX\r
+       mov     ax,GC_MODE+2*256        ;write mode 2\r
+       WORDOUT\r
+\r
+       mov     di,[bufferofs]\r
+       mov     bx,[yl]\r
+       shl     bx,1\r
+       add     di,[ylookup+bx]\r
+       mov     bx,[x]\r
+       mov     ax,bx\r
+       shr     ax,1\r
+       shr     ax,1\r
+       shr     ax,1\r
+       add     di,ax                           ; di = byte on screen\r
+\r
+       and     bx,7\r
+       mov     ah,[plotpixels+bx]\r
+       mov     al,GC_BITMASK           ;mask off other pixels\r
+       WORDOUT\r
+\r
+       mov     cx,[yh]\r
+       sub     cx,[yl]\r
+       inc     cx                                      ;number of pixels to plot\r
+\r
+       mov     bh,[BYTE color]\r
+       mov     dx,[linewidth]\r
+\r
+@@plot:\r
+       mov             bl,bh\r
+       xchg    bl,[es:di]              ; load latches and write pixel\r
+       add             di,dx\r
+\r
+       loop    @@plot\r
+\r
+       mov     dx,GC_INDEX\r
+       mov     ah,0ffh                         ;no bit mask\r
+       WORDOUT\r
+       mov     ax,GC_MODE+0*256        ;write mode 0\r
+       WORDOUT\r
+\r
+       ret\r
+\r
+ENDP\r
+\r
+\r
+;============================================================================\r
+\r
+\r
+;===================\r
+;\r
+; VW_DrawTile8\r
+;\r
+; xcoord in bytes (8 pixels), ycoord in pixels\r
+; All Tile8s are in one grseg, so an offset is calculated inside it\r
+;\r
+;===================\r
+\r
+PROC   VW_DrawTile8    xcoord:WORD, ycoord:WORD, tile:WORD\r
+PUBLIC VW_DrawTile8\r
+USES   SI,DI\r
+\r
+       mov     es,[screenseg]\r
+\r
+       mov     di,[bufferofs]\r
+       add     di,[xcoord]\r
+       mov     bx,[ycoord]\r
+       shl     bx,1\r
+       add     di,[ylookup+bx]\r
+       mov     [ss:screendest],di              ;screen destination\r
+\r
+       mov     bx,[linewidth]\r
+       dec     bx\r
+\r
+       mov     si,[tile]\r
+       shl     si,1\r
+       shl     si,1\r
+       shl     si,1\r
+       shl     si,1\r
+       shl     si,1\r
+\r
+       mov     ds,[grsegs+STARTTILE8*2] ; segment for all tile8s\r
+\r
+       mov     cx,4                                    ;planes to draw\r
+       mov     ah,0001b                                ;map mask\r
+\r
+       mov     dx,SC_INDEX\r
+       mov     al,SC_MAPMASK\r
+\r
+;\r
+; start drawing\r
+;\r
+\r
+@@planeloop:\r
+       WORDOUT\r
+       shl     ah,1                                    ;shift plane mask over for next plane\r
+\r
+       mov     di,[ss:screendest]              ;start at same place in all planes\r
+\r
+REPT   7\r
+       movsb\r
+       add     di,bx\r
+ENDM\r
+       movsb\r
+\r
+       loop    @@planeloop\r
+\r
+       mov     ax,ss\r
+       mov     ds,ax                                   ;restore turbo's data segment\r
+\r
+       ret\r
+\r
+ENDP\r
+\r
+\r
+;============================================================================\r
+;\r
+; VW_MaskBlock\r
+;\r
+; Draws a masked block shape to the screen.  bufferofs is NOT accounted for.\r
+; The mask comes first, then four planes of data.\r
+;\r
+;============================================================================\r
+\r
+DATASEG\r
+\r
+UNWOUNDMASKS   =       10\r
+\r
+\r
+maskroutines   dw      mask0,mask0,mask1E,mask1E,mask2E,mask2O,mask3E,mask3O\r
+                               dw      mask4E,mask4O,mask5E,mask5O,mask6E,mask6O\r
+                               dw      mask7E,mask7O,mask8E,mask8O,mask9E,mask9O\r
+                               dw      mask10E,mask10O\r
+\r
+\r
+routinetouse   dw      ?\r
+\r
+CODESEG\r
+\r
+PROC   VW_MaskBlock    segm:WORD, ofs:WORD, dest:WORD, wide:WORD, height:WORD, planesize:WORD\r
+PUBLIC VW_MaskBlock\r
+USES   SI,DI\r
+\r
+       mov     es,[screenseg]\r
+\r
+       mov     [BYTE planemask],1\r
+       mov     [BYTE planenum],0\r
+\r
+       mov     di,[wide]\r
+       mov     dx,[linewidth]\r
+       sub     dx,[wide]\r
+       mov     [linedelta],dx                  ;amount to add after drawing each line\r
+\r
+       mov     bx,[planesize]                  ; si+bx = data location\r
+\r
+       cmp     di,UNWOUNDMASKS\r
+       jbe     @@unwoundroutine\r
+       mov     [routinetouse],OFFSET generalmask\r
+       jmp     NEAR @@startloop\r
+\r
+;=================\r
+;\r
+; use the unwound routines\r
+;\r
+;=================\r
+\r
+@@unwoundroutine:\r
+       mov     cx,[dest]\r
+       shr     cx,1\r
+       rcl     di,1                                    ;shift a 1 in if destination is odd\r
+       shl     di,1                                    ;to index into a word width table\r
+       mov     ax,[maskroutines+di]    ;call the right routine\r
+       mov     [routinetouse],ax\r
+\r
+@@startloop:\r
+       mov     ds,[segm]\r
+\r
+@@drawplane:\r
+       mov     dx,SC_INDEX\r
+       mov     al,SC_MAPMASK\r
+       mov     ah,[ss:planemask]\r
+       WORDOUT\r
+       mov     dx,GC_INDEX\r
+       mov     al,GC_READMAP\r
+       mov     ah,[ss:planenum]\r
+       WORDOUT\r
+\r
+       mov     si,[ofs]                                ;start back at the top of the mask\r
+       mov     di,[dest]                               ;start at same place in all planes\r
+       mov     cx,[height]                             ;scan lines to draw\r
+       mov dx,[ss:linedelta]\r
+\r
+       jmp [ss:routinetouse]           ;draw one plane\r
+planereturn:                                   ;routine jmps back here\r
+\r
+       add     bx,[ss:planesize]               ;start of mask = start of next plane\r
+\r
+       inc     [ss:planenum]\r
+       shl     [ss:planemask],1                ;shift plane mask over for next plane\r
+       cmp     [ss:planemask],10000b   ;done all four planes?\r
+       jne     @@drawplane\r
+\r
+mask0:\r
+       mov     ax,ss\r
+       mov     ds,ax\r
+       ret                                                     ;width of 0 = no drawing\r
+\r
+;==============\r
+;\r
+; General purpose masked block drawing.  This could be optimised into\r
+; four routines to use words, but few play loop sprites should be this big!\r
+;\r
+;==============\r
+\r
+generalmask:\r
+       mov     dx,cx\r
+\r
+@@lineloopgen:\r
+       mov     cx,[wide]\r
+@@byteloop:\r
+       mov     al,[es:di]\r
+       and     al,[si]\r
+       or      al,[bx+si]\r
+       inc     si\r
+       stosb\r
+       loop    @@byteloop\r
+\r
+       add     di,[ss:linedelta]\r
+       dec     dx\r
+       jnz     @@lineloopgen\r
+       jmp     planereturn\r
+\r
+;=================\r
+;\r
+; Horizontally unwound routines to draw certain masked blocks faster\r
+;\r
+;=================\r
+\r
+MACRO  MASKBYTE\r
+       lodsb\r
+       and     al,[es:di]\r
+       or      al,[bx+si-1]\r
+       stosb\r
+ENDM\r
+\r
+MACRO  MASKWORD\r
+       lodsw\r
+       and     ax,[es:di]\r
+       or      ax,[bx+si-2]\r
+       stosw\r
+ENDM\r
+\r
+MACRO  SPRITELOOP      addr\r
+       add     di,dx\r
+       loop    addr\r
+       jmp     planereturn\r
+ENDM\r
+\r
+\r
+EVEN\r
+mask1E:\r
+       MASKBYTE\r
+       SPRITELOOP      mask1E\r
+\r
+EVEN\r
+mask2E:\r
+       MASKWORD\r
+       SPRITELOOP      mask2E\r
+\r
+EVEN\r
+mask2O:\r
+       MASKBYTE\r
+       MASKBYTE\r
+       SPRITELOOP      mask2O\r
+\r
+EVEN\r
+mask3E:\r
+       MASKWORD\r
+       MASKBYTE\r
+       SPRITELOOP      mask3E\r
+\r
+EVEN\r
+mask3O:\r
+       MASKBYTE\r
+       MASKWORD\r
+       SPRITELOOP      mask3O\r
+\r
+EVEN\r
+mask4E:\r
+       MASKWORD\r
+       MASKWORD\r
+       SPRITELOOP      mask4E\r
+\r
+EVEN\r
+mask4O:\r
+       MASKBYTE\r
+       MASKWORD\r
+       MASKBYTE\r
+       SPRITELOOP      mask4O\r
+\r
+EVEN\r
+mask5E:\r
+       MASKWORD\r
+       MASKWORD\r
+       MASKBYTE\r
+       SPRITELOOP      mask5E\r
+\r
+EVEN\r
+mask5O:\r
+       MASKBYTE\r
+       MASKWORD\r
+       MASKWORD\r
+       SPRITELOOP      mask5O\r
+\r
+EVEN\r
+mask6E:\r
+       MASKWORD\r
+       MASKWORD\r
+       MASKWORD\r
+       SPRITELOOP      mask6E\r
+\r
+EVEN\r
+mask6O:\r
+       MASKBYTE\r
+       MASKWORD\r
+       MASKWORD\r
+       MASKBYTE\r
+       SPRITELOOP      mask6O\r
+\r
+EVEN\r
+mask7E:\r
+       MASKWORD\r
+       MASKWORD\r
+       MASKWORD\r
+       MASKBYTE\r
+       SPRITELOOP      mask7E\r
+\r
+EVEN\r
+mask7O:\r
+       MASKBYTE\r
+       MASKWORD\r
+       MASKWORD\r
+       MASKWORD\r
+       SPRITELOOP      mask7O\r
+\r
+EVEN\r
+mask8E:\r
+       MASKWORD\r
+       MASKWORD\r
+       MASKWORD\r
+       MASKWORD\r
+       SPRITELOOP      mask8E\r
+\r
+EVEN\r
+mask8O:\r
+       MASKBYTE\r
+       MASKWORD\r
+       MASKWORD\r
+       MASKWORD\r
+       MASKBYTE\r
+       SPRITELOOP      mask8O\r
+\r
+EVEN\r
+mask9E:\r
+       MASKWORD\r
+       MASKWORD\r
+       MASKWORD\r
+       MASKWORD\r
+       MASKBYTE\r
+       SPRITELOOP      mask9E\r
+\r
+EVEN\r
+mask9O:\r
+       MASKBYTE\r
+       MASKWORD\r
+       MASKWORD\r
+       MASKWORD\r
+       MASKWORD\r
+       SPRITELOOP      mask9O\r
+\r
+EVEN\r
+mask10E:\r
+       MASKWORD\r
+       MASKWORD\r
+       MASKWORD\r
+       MASKWORD\r
+       MASKWORD\r
+       SPRITELOOP      mask10E\r
+\r
+EVEN\r
+mask10O:\r
+       MASKBYTE\r
+       MASKWORD\r
+       MASKWORD\r
+       MASKWORD\r
+       MASKWORD\r
+       MASKBYTE\r
+       SPRITELOOP      mask10O\r
+\r
+\r
+ENDP\r
+\r
+\r
+;============================================================================\r
+;\r
+; VW_InverseMask\r
+;\r
+; Draws a masked block shape to the screen.  bufferofs is NOT accounted for.\r
+; The mask comes first, then four planes of data.\r
+;\r
+;============================================================================\r
+\r
+PROC   VW_InverseMask  segm:WORD, ofs:WORD, dest:WORD, wide:WORD, height:WORD\r
+PUBLIC VW_InverseMask\r
+USES   SI,DI\r
+\r
+       mov     dx,SC_INDEX\r
+       mov     ax,SC_MAPMASK+15*256\r
+       WORDOUT\r
+       mov     dx,GC_INDEX\r
+       mov     ax,GC_DATAROTATE+16*256         ;set function = OR\r
+       WORDOUT\r
+\r
+       mov     es, [screenseg]\r
+       mov     ax, [wide]\r
+       mov     dx, [linewidth]\r
+       sub     dx, ax;\r
+       mov     ds, [segm]\r
+       mov     si, [ofs]\r
+       mov     di, [dest]\r
+       mov     bx, [height]\r
+@@yloop:\r
+       mov     cx, [wide]\r
+@@xloop:\r
+       lodsb\r
+       not     al\r
+       xchg    al, [es:di]\r
+       inc     di\r
+       loop    @@xloop\r
+       add     di, dx\r
+       dec     bx\r
+       jnz     @@yloop\r
+\r
+       mov     dx,GC_INDEX\r
+       mov     ax,GC_DATAROTATE+0*256          ;set function = no change\r
+       WORDOUT\r
+\r
+       mov     ax,ss\r
+       mov     ds,ax                                   ;restore turbo's data segment\r
+\r
+       ret\r
+\r
+ENDP\r
+\r
+;============================================================================\r
+;\r
+; VW_ScreenToScreen\r
+;\r
+; Basic block copy routine.  Copies one block of screen memory to another,\r
+; using write mode 1 (sets it and returns with write mode 0).  bufferofs is\r
+; NOT accounted for.\r
+;\r
+;============================================================================\r
+\r
+PROC   VW_ScreenToScreen       source:WORD, dest:WORD, wide:WORD, height:WORD\r
+PUBLIC VW_ScreenToScreen\r
+USES   SI,DI\r
+\r
+       pushf\r
+       cli\r
+\r
+       mov     dx,SC_INDEX\r
+       mov     ax,SC_MAPMASK+15*256\r
+       WORDOUT\r
+       mov     dx,GC_INDEX\r
+       mov     ax,GC_MODE+1*256\r
+       WORDOUT\r
+\r
+       popf\r
+\r
+       mov     bx,[linewidth]\r
+       sub     bx,[wide]\r
+\r
+       mov     ax,[screenseg]\r
+       mov     es,ax\r
+       mov     ds,ax\r
+\r
+       mov     si,[source]\r
+       mov     di,[dest]                               ;start at same place in all planes\r
+       mov     dx,[height]                             ;scan lines to draw\r
+       mov     ax,[wide]\r
+\r
+@@lineloop:\r
+       mov     cx,ax\r
+       rep     movsb\r
+       add     si,bx\r
+       add     di,bx\r
+\r
+       dec     dx\r
+       jnz     @@lineloop\r
+\r
+       mov     dx,GC_INDEX\r
+       mov     ax,GC_MODE+0*256\r
+       WORDOUT\r
+\r
+       mov     ax,ss\r
+       mov     ds,ax                                   ;restore turbo's data segment\r
+\r
+       ret\r
+\r
+ENDP\r
+\r
+\r
+;============================================================================\r
+;\r
+; VW_MemToScreen\r
+;\r
+; Basic block drawing routine. Takes a block shape at segment pointer source\r
+; with four planes of width by height data, and draws it to dest in the\r
+; virtual screen, based on linewidth.  bufferofs is NOT accounted for.\r
+; There are four drawing routines to provide the best optimized code while\r
+; accounting for odd segment wrappings due to the floating screens.\r
+;\r
+;============================================================================\r
+\r
+DATASEG\r
+\r
+memtoscreentable       dw      eventoeven,eventoodd,oddtoeven,oddtoodd\r
+\r
+CODESEG\r
+\r
+\r
+PROC   VW_MemToScreen  source:WORD, dest:WORD, wide:WORD, height:WORD\r
+PUBLIC VW_MemToScreen\r
+USES   SI,DI\r
+\r
+       mov     es,[screenseg]\r
+\r
+       mov     bx,[linewidth]\r
+       sub     bx,[wide]\r
+\r
+       mov     ds,[source]\r
+\r
+\r
+       xor     si,si                                   ;block is segment aligned\r
+\r
+       xor     di,di\r
+       shr     [wide],1                                ;change wide to words, and see if carry is set\r
+       rcl     di,1                                    ;1 if wide is odd\r
+       mov     ax,[dest]\r
+       shr     ax,1\r
+       rcl     di,1                                    ;shift a 1 in if destination is odd\r
+       shl     di,1                                    ;to index into a word width table\r
+       mov     ax,SC_MAPMASK+0001b*256 ;map mask for plane 0\r
+       jmp     [ss:memtoscreentable+di]        ;call the right routine\r
+\r
+;==============\r
+;\r
+; Copy an even width block to an even video address\r
+;\r
+;==============\r
+\r
+eventoeven:\r
+       mov     dx,SC_INDEX\r
+       WORDOUT\r
+\r
+       mov     di,[dest]                               ;start at same place in all planes\r
+       mov     dx,[height]                             ;scan lines to draw\r
+\r
+@@lineloopEE:\r
+       mov     cx,[wide]\r
+       rep     movsw\r
+\r
+       add     di,bx\r
+\r
+       dec     dx\r
+       jnz     @@lineloopEE\r
+\r
+       shl     ah,1                                    ;shift plane mask over for next plane\r
+       cmp     ah,10000b                               ;done all four planes?\r
+       jne     eventoeven\r
+\r
+       mov     ax,ss\r
+       mov     ds,ax                                   ;restore turbo's data segment\r
+\r
+       ret\r
+\r
+;==============\r
+;\r
+; Copy an odd width block to an even video address\r
+;\r
+;==============\r
+\r
+oddtoeven:\r
+       mov     dx,SC_INDEX\r
+       WORDOUT\r
+\r
+       mov     di,[dest]                               ;start at same place in all planes\r
+       mov     dx,[height]                             ;scan lines to draw\r
+\r
+@@lineloopOE:\r
+       mov     cx,[wide]\r
+       rep     movsw\r
+       movsb                                           ;copy the last byte\r
+\r
+       add     di,bx\r
+\r
+       dec     dx\r
+       jnz     @@lineloopOE\r
+\r
+       shl     ah,1                                    ;shift plane mask over for next plane\r
+       cmp     ah,10000b                               ;done all four planes?\r
+       jne     oddtoeven\r
+\r
+       mov     ax,ss\r
+       mov     ds,ax                                   ;restore turbo's data segment\r
+\r
+       ret\r
+\r
+;==============\r
+;\r
+; Copy an even width block to an odd video address\r
+;\r
+;==============\r
+\r
+eventoodd:\r
+       dec     [wide]                                  ;one word has to be handled seperately\r
+EOplaneloop:\r
+       mov     dx,SC_INDEX\r
+       WORDOUT\r
+\r
+       mov     di,[dest]                               ;start at same place in all planes\r
+       mov     dx,[height]                             ;scan lines to draw\r
+\r
+@@lineloopEO:\r
+       movsb\r
+       mov     cx,[wide]\r
+       rep     movsw\r
+       movsb\r
+\r
+       add     di,bx\r
+\r
+       dec     dx\r
+       jnz     @@lineloopEO\r
+\r
+       shl     ah,1                                    ;shift plane mask over for next plane\r
+       cmp     ah,10000b                               ;done all four planes?\r
+       jne     EOplaneloop\r
+\r
+       mov     ax,ss\r
+       mov     ds,ax                                   ;restore turbo's data segment\r
+\r
+       ret\r
+\r
+;==============\r
+;\r
+; Copy an odd width block to an odd video address\r
+;\r
+;==============\r
+\r
+oddtoodd:\r
+       mov     dx,SC_INDEX\r
+       WORDOUT\r
+\r
+       mov     di,[dest]                               ;start at same place in all planes\r
+       mov     dx,[height]                             ;scan lines to draw\r
+\r
+@@lineloopOO:\r
+       movsb\r
+       mov     cx,[wide]\r
+       rep     movsw\r
+\r
+       add     di,bx\r
+\r
+       dec     dx\r
+       jnz     @@lineloopOO\r
+\r
+       shl     ah,1                                    ;shift plane mask over for next plane\r
+       cmp     ah,10000b                               ;done all four planes?\r
+       jne     oddtoodd\r
+\r
+       mov     ax,ss\r
+       mov     ds,ax                                   ;restore turbo's data segment\r
+\r
+       ret\r
+\r
+\r
+ENDP\r
+\r
+;===========================================================================\r
+;\r
+; VW_ScreenToMem\r
+;\r
+; Copies a block of video memory to main memory, in order from planes 0-3.\r
+; This could be optimized along the lines of VW_MemToScreen to take advantage\r
+; of word copies, but this is an infrequently called routine.\r
+;\r
+;===========================================================================\r
+\r
+PROC   VW_ScreenToMem  source:WORD, dest:WORD, wide:WORD, height:WORD\r
+PUBLIC VW_ScreenToMem\r
+USES   SI,DI\r
+\r
+       mov     es,[dest]\r
+\r
+       mov     bx,[linewidth]\r
+       sub     bx,[wide]\r
+\r
+       mov     ds,[screenseg]\r
+\r
+       mov     ax,GC_READMAP                   ;read map for plane 0\r
+\r
+       xor     di,di\r
+\r
+@@planeloop:\r
+       mov     dx,GC_INDEX\r
+       WORDOUT\r
+\r
+       mov     si,[source]                             ;start at same place in all planes\r
+       mov     dx,[height]                             ;scan lines to draw\r
+\r
+@@lineloop:\r
+       mov     cx,[wide]\r
+       rep     movsb\r
+\r
+       add     si,bx\r
+\r
+       dec     dx\r
+       jnz     @@lineloop\r
+\r
+       inc     ah\r
+       cmp     ah,4                                    ;done all four planes?\r
+       jne     @@planeloop\r
+\r
+       mov     ax,ss\r
+       mov     ds,ax                                   ;restore turbo's data segment\r
+\r
+       ret\r
+\r
+ENDP\r
+\r
+\r
+;============================================================================\r
+;\r
+; VWL_UpdateScreenBlocks\r
+;\r
+; Scans through the update matrix and copies any areas that have changed\r
+; to the visable screen, then zeros the update array\r
+;\r
+;============================================================================\r
+\r
+\r
+\r
+; AX   0/1 for scasb, temp for segment register transfers\r
+; BX    width for block copies\r
+; CX   REP counter\r
+; DX   line width deltas\r
+; SI   source for copies\r
+; DI   scas dest / movsb dest\r
+; BP   pointer to end of bufferblocks\r
+\r
+PROC   VWL_UpdateScreenBlocks\r
+PUBLIC VWL_UpdateScreenBlocks\r
+USES   SI,DI,BP\r
+\r
+       jmp     SHORT @@realstart\r
+@@done:\r
+;\r
+; all tiles have been scanned\r
+;\r
+       mov     dx,GC_INDEX                             ; restore write mode 0\r
+       mov     ax,GC_MODE+0*256\r
+       WORDOUT\r
+\r
+       xor     ax,ax                                   ; clear out the update matrix\r
+       mov     cx,UPDATEWIDE*UPDATEHIGH/2\r
+\r
+       mov     di,[updateptr]\r
+       rep     stosw\r
+\r
+       ret\r
+\r
+@@realstart:\r
+       mov     dx,SC_INDEX\r
+       mov     ax,SC_MAPMASK+15*256\r
+       WORDOUT\r
+       mov     dx,GC_INDEX\r
+       mov     ax,GC_MODE+1*256\r
+       WORDOUT\r
+\r
+       mov     di,[updateptr]                  ; start of floating update screen\r
+       mov     bp,di\r
+       add     bp,UPDATEWIDE*UPDATEHIGH+1 ; when di = bp, all tiles have been scanned\r
+\r
+       push    di\r
+       mov     cx,-1                                   ; definately scan the entire thing\r
+\r
+;\r
+; scan for a 1 in the update list, meaning a tile needs to be copied\r
+; from the master screen to the current screen\r
+;\r
+@@findtile:\r
+       pop     di                                              ; place to continue scaning from\r
+       mov     ax,ss\r
+       mov     es,ax                                   ; search in the data segment\r
+       mov     ds,ax\r
+       mov al,1\r
+       repne   scasb\r
+       cmp     di,bp\r
+       jae     @@done\r
+\r
+       cmp     [BYTE di],al\r
+       jne     @@singletile\r
+       jmp     @@tileblock\r
+\r
+;============\r
+;\r
+; copy a single tile\r
+;\r
+;============\r
+@@singletile:\r
+       inc     di                                              ; we know the next tile is nothing\r
+       push    di                                      ; save off the spot being scanned\r
+       sub     di,[updateptr]\r
+       shl     di,1\r
+       mov     di,[blockstarts-4+di]   ; start of tile location on screen\r
+       mov     si,di\r
+       add     si,[bufferofs]\r
+       add     di,[displayofs]\r
+\r
+       mov     dx,[linewidth]\r
+       sub     dx,2\r
+       mov     ax,[screenseg]\r
+       mov     ds,ax\r
+       mov     es,ax\r
+\r
+REPT   15\r
+       movsb\r
+       movsb\r
+       add     si,dx\r
+       add     di,dx\r
+ENDM\r
+       movsb\r
+       movsb\r
+\r
+       jmp     @@findtile\r
+\r
+;============\r
+;\r
+; more than one tile in a row needs to be updated, so do it as a group\r
+;\r
+;============\r
+EVEN\r
+@@tileblock:\r
+       mov     dx,di                                   ; hold starting position + 1 in dx\r
+       inc     di                                              ; we know the next tile also gets updated\r
+       repe    scasb                           ; see how many more in a row\r
+       push    di                                      ; save off the spot being scanned\r
+\r
+       mov     bx,di\r
+       sub     bx,dx                                   ; number of tiles in a row\r
+       shl     bx,1                                    ; number of bytes / row\r
+\r
+       mov     di,dx                                   ; lookup position of start tile\r
+       sub     di,[updateptr]\r
+       shl     di,1\r
+       mov     di,[blockstarts-2+di]   ; start of tile location\r
+       mov     si,di\r
+       add     si,[bufferofs]\r
+       add     di,[displayofs]\r
+\r
+       mov     dx,[linewidth]\r
+       sub     dx,bx                                   ; offset to next line on screen\r
+\r
+       mov     ax,[screenseg]\r
+       mov     ds,ax\r
+       mov     es,ax\r
+\r
+REPT   15\r
+       mov     cx,bx\r
+       rep     movsb\r
+       add     si,dx\r
+       add     di,dx\r
+ENDM\r
+       mov     cx,bx\r
+       rep     movsb\r
+\r
+       dec     cx                                              ; was 0 from last rep movsb, now $ffff for scasb\r
+       jmp     @@findtile\r
+\r
+ENDP\r
+\r
+\r
+;===========================================================================\r
+;\r
+;                    MISC EGA ROUTINES\r
+;\r
+;===========================================================================\r
+\r
+;=================\r
+;\r
+; SyncVBL\r
+;\r
+;=================\r
+\r
+DATASEG\r
+\r
+EXTRN  TimeCount                       :DWORD\r
+EXTRN  jerk                    :WORD\r
+EXTRN  nopan                           :WORD\r
+\r
+CODESEG\r
+\r
+PROC SyncVBL\r
+       mov     dx,STATUS_REGISTER_1\r
+       mov     bx,[WORD TimeCount]\r
+       add     bx,3\r
+@@waitloop:\r
+       sti\r
+       jmp     $+2\r
+       cli\r
+       cmp     [WORD TimeCount],bx\r
+       je      @@done\r
+@@waitnovert:\r
+       in      al,dx\r
+       test    al,1\r
+       jnz     @@waitnovert\r
+@@waitvert:\r
+       in      al,dx\r
+       test    al,1\r
+       jz      @@waitvert\r
+\r
+REPT 5\r
+       in      al,dx\r
+       test    al,8\r
+       jnz     @@waitloop\r
+       test    al,1\r
+       jz      @@waitloop\r
+ENDM\r
+\r
+       test    [jerk],1\r
+       jz      @@done\r
+\r
+REPT 5\r
+       in      al,dx\r
+       test    al,8\r
+       jnz     @@waitloop\r
+       test    al,1\r
+       jz      @@waitloop\r
+ENDM\r
+\r
+@@done:\r
+       ret\r
+ENDP\r
+\r
+\r
+;==============\r
+;\r
+; VW_SetScreen\r
+;\r
+;==============\r
+\r
+PROC   VW_SetScreen  crtc:WORD, pel:WORD\r
+PUBLIC VW_SetScreen\r
+\r
+       call    SyncVBL\r
+;\r
+; set CRTC start\r
+;\r
+; for some reason, my XT's EGA card doesn't like word outs to the CRTC\r
+; index...\r
+;\r
+       mov     cx,[crtc]\r
+       mov     dx,CRTC_INDEX\r
+       mov     al,0ch          ;start address high register\r
+       out     dx,al\r
+       inc     dx\r
+       mov     al,ch\r
+       out     dx,al\r
+       dec     dx\r
+       mov     al,0dh          ;start address low register\r
+       out     dx,al\r
+       mov     al,cl\r
+       inc     dx\r
+       out     dx,al\r
+\r
+       test    [nopan],1\r
+       jnz     @@done\r
+;\r
+; set horizontal panning\r
+;\r
+\r
+       mov     dx,ATR_INDEX\r
+       mov     al,ATR_PELPAN or 20h\r
+       out     dx,al\r
+       jmp     $+2\r
+       mov     al,[BYTE pel]           ;pel pan value\r
+       out     dx,al\r
+\r
+@@done:\r
+       sti\r
+\r
+       ret\r
+\r
+ENDP\r
+\r
+\r
+if NUMFONT+NUMFONTM\r
+\r
+;===========================================================================\r
+;\r
+; GENERAL FONT DRAWING ROUTINES\r
+;\r
+;===========================================================================\r
+\r
+DATASEG\r
+\r
+px     dw      ?                                       ; proportional character drawing coordinates\r
+py     dw      ?\r
+pdrawmode      db      11000b          ; 8 = OR, 24 = XOR, put in GC_DATAROTATE\r
+fontcolor      db      15              ;0-15 mapmask value\r
+\r
+PUBLIC px,py,pdrawmode,fontcolor\r
+\r
+;\r
+; offsets in font structure\r
+;\r
+pcharheight    =       0               ;lines high\r
+charloc                =       2               ;pointers to every character\r
+charwidth      =       514             ;every character's width in pixels\r
+\r
+\r
+propchar       dw      ?                       ; the character number to shift\r
+stringptr      dw      ?,?\r
+\r
+\r
+BUFFWIDTH      =       50\r
+BUFFHEIGHT     =   32                  ; must be twice as high as font for masked fonts\r
+\r
+databuffer     db      BUFFWIDTH*BUFFHEIGHT dup (?)\r
+\r
+bufferwidth    dw      ?                                               ; bytes with valid info / line\r
+bufferheight dw        ?                                               ; number of lines currently used\r
+\r
+bufferbyte     dw      ?\r
+bufferbit      dw      ?\r
+\r
+screenspot     dw      ?                                               ; where the buffer is going\r
+\r
+bufferextra    dw      ?                                               ; add at end of a line copy\r
+screenextra    dw      ?\r
+\r
+PUBLIC bufferwidth,bufferheight,screenspot\r
+\r
+CODESEG\r
+\r
+;======================\r
+;\r
+; Macros to table shift a byte of font\r
+;\r
+;======================\r
+\r
+MACRO  SHIFTNOXOR\r
+       mov     al,[es:bx]              ; source\r
+       xor     ah,ah\r
+       shl     ax,1\r
+       mov     si,ax\r
+       mov     ax,[bp+si]              ; table shift into two bytes\r
+       or      [di],al                 ; or with first byte\r
+       inc     di\r
+       mov     [di],ah                 ; replace next byte\r
+       inc     bx                              ; next source byte\r
+ENDM\r
+\r
+MACRO  SHIFTWITHXOR\r
+       mov     al,[es:bx]              ; source\r
+       xor     ah,ah\r
+       shl     ax,1\r
+       mov     si,ax\r
+       mov     ax,[bp+si]              ; table shift into two bytes\r
+       not     ax\r
+       and     [di],al                 ; and with first byte\r
+       inc     di\r
+       mov     [di],ah                 ; replace next byte\r
+       inc     bx                              ; next source byte\r
+ENDM\r
+\r
+\r
+;=======================\r
+;\r
+; BufferToScreen\r
+;\r
+; Pass buffer start in SI (somewhere in databuffer)\r
+; Draws the buffer to the EGA screen in the current write mode\r
+;\r
+;========================\r
+\r
+PROC   BufferToScreen  NEAR\r
+\r
+       mov     es,[screenseg]\r
+       mov     di,[screenspot]\r
+\r
+       mov     bx,[bufferwidth]                ;calculate offsets for end of each line\r
+       or      bx,bx\r
+       jnz     @@isthere\r
+       ret                                                     ;nothing to draw\r
+\r
+@@isthere:\r
+       mov     ax,[linewidth]\r
+       sub     ax,bx\r
+       mov     [screenextra],ax\r
+       mov     ax,BUFFWIDTH\r
+       sub     ax,bx\r
+       mov     [bufferextra],ax\r
+\r
+       mov     bx,[bufferheight]               ;lines to copy\r
+@@lineloop:\r
+       mov     cx,[bufferwidth]                ;bytes to copy\r
+@@byteloop:\r
+       lodsb                                           ;get a byte from the buffer\r
+       xchg    [es:di],al                      ;load latches and store back to screen\r
+       inc     di\r
+\r
+       loop    @@byteloop\r
+\r
+       add     si,[bufferextra]\r
+       add     di,[screenextra]\r
+\r
+       dec     bx\r
+       jnz     @@lineloop\r
+\r
+       ret\r
+ENDP\r
+\r
+\r
+;============================================================================\r
+;\r
+; NON MASKED FONT DRAWING ROUTINES\r
+;\r
+;============================================================================\r
+\r
+if numfont\r
+\r
+DATASEG\r
+\r
+shiftdrawtable dw      0,shift1wide,shift2wide,shift3wide,shift4wide\r
+                               dw              shift5wide\r
+\r
+CODESEG\r
+\r
+;==================\r
+;\r
+; ShiftPropChar\r
+;\r
+; Call with BX = character number (0-255)\r
+; Draws one character to the buffer at bufferbyte/bufferbit, and adjusts\r
+; them to the new position\r
+;\r
+;==================\r
+\r
+PROC   ShiftPropChar   NEAR\r
+\r
+       mov     si,[fontnumber]\r
+       shl     si,1\r
+       mov     es,[grsegs+STARTFONT*2+si]      ;segment of font to use\r
+\r
+;\r
+; find character location, width, and height\r
+;\r
+       mov     si,[es:charwidth+bx]\r
+       and     si,0ffh                                 ;SI hold width in pixels\r
+       shl     bx,1\r
+       mov     bx,[es:charloc+bx]              ;BX holds pointer to character data\r
+\r
+;\r
+; look up which shift table to use, based on bufferbit\r
+;\r
+       mov     di,[bufferbit]\r
+       shl     di,1\r
+       mov     bp,[shifttabletable+di] ;BP holds pointer to shift table\r
+\r
+       mov     di,OFFSET databuffer\r
+       add     di,[bufferbyte]                 ;DI holds pointer to buffer\r
+\r
+;\r
+; advance position by character width\r
+;\r
+       mov     cx,[bufferbit]\r
+       add     cx,si                                   ;new bit position\r
+       mov     ax,cx\r
+       and     ax,7\r
+       mov     [bufferbit],ax                  ;new bit position\r
+       mov     ax,cx\r
+       shr     ax,1\r
+       shr     ax,1\r
+       shr     ax,1\r
+       add     [bufferbyte],ax                 ;new byte position\r
+\r
+       add     si,7\r
+       shr     si,1\r
+       shr     si,1\r
+       shr     si,1                                    ;bytes the character is wide\r
+       shl     si,1                    ;*2 to look up in shiftdrawtable\r
+\r
+       mov     cx,[es:pcharheight]\r
+       mov     dx,BUFFWIDTH\r
+       jmp     [ss:shiftdrawtable+si]  ;procedure to draw this width\r
+\r
+;\r
+; one byte character\r
+;\r
+shift1wide:\r
+       dec     dx\r
+EVEN\r
+@@loop1:\r
+       SHIFTNOXOR\r
+       add     di,dx                   ; next line in buffer\r
+       loop    @@loop1\r
+       ret\r
+\r
+;\r
+; two byte character\r
+;\r
+shift2wide:\r
+       dec     dx\r
+       dec     dx\r
+EVEN\r
+@@loop2:\r
+       SHIFTNOXOR\r
+       SHIFTNOXOR\r
+       add     di,dx                   ; next line in buffer\r
+       loop    @@loop2\r
+       ret\r
+\r
+;\r
+; three byte character\r
+;\r
+shift3wide:\r
+       sub     dx,3\r
+EVEN\r
+@@loop3:\r
+       SHIFTNOXOR\r
+       SHIFTNOXOR\r
+       SHIFTNOXOR\r
+       add     di,dx                   ; next line in buffer\r
+       loop    @@loop3\r
+       ret\r
+\r
+;\r
+; four byte character\r
+;\r
+shift4wide:\r
+       sub     dx,4\r
+EVEN\r
+@@loop4:\r
+       SHIFTNOXOR\r
+       SHIFTNOXOR\r
+       SHIFTNOXOR\r
+       SHIFTNOXOR\r
+       add     di,dx                   ; next line in buffer\r
+       loop    @@loop4\r
+       ret\r
+\r
+;\r
+; five byte character\r
+;\r
+shift5wide:\r
+       sub     dx,5\r
+EVEN\r
+@@loop5:\r
+       SHIFTNOXOR\r
+       SHIFTNOXOR\r
+       SHIFTNOXOR\r
+       SHIFTNOXOR\r
+       add     di,dx                   ; next line in buffer\r
+       loop    @@loop5\r
+       ret\r
+\r
+\r
+\r
+ENDP\r
+\r
+;============================================================================\r
+\r
+;==================\r
+;\r
+; VW_DrawPropString\r
+;\r
+; Draws a C string of characters at px/py and advances px\r
+;\r
+; Assumes write mode 0\r
+;\r
+;==================\r
+\r
+CODESEG\r
+\r
+PROC   VW_DrawPropString       string:DWORD\r
+PUBLIC VW_DrawPropString\r
+USES   SI,DI\r
+\r
+;\r
+; proportional spaceing, which clears the buffer ahead of it, so only\r
+; clear the first collumn\r
+;\r
+       mov     al,0\r
+line   =       0\r
+REPT   BUFFHEIGHT\r
+       mov     [BYTE databuffer+BUFFWIDTH*line],al\r
+line   =       line+1\r
+ENDM\r
+\r
+;\r
+; shift the characters into the buffer\r
+;\r
+@@shiftchars:\r
+       mov     ax,[px]\r
+       and     ax,7\r
+       mov     [bufferbit],ax\r
+       mov     [bufferbyte],0\r
+\r
+       mov     ax,[WORD string]\r
+       mov     [stringptr],ax\r
+       mov     ax,[WORD string+2]\r
+       mov     [stringptr+2],ax\r
+\r
+@@shiftone:\r
+       mov     es,[stringptr+2]\r
+       mov     bx,[stringptr]\r
+       inc     [stringptr]\r
+       mov     bx,[es:bx]\r
+       xor     bh,bh\r
+       or      bl,bl\r
+       jz      @@allshifted\r
+       call    ShiftPropChar\r
+       jmp     @@shiftone\r
+\r
+@@allshifted:\r
+;\r
+; calculate position to draw buffer on screen\r
+;\r
+       mov     bx,[py]\r
+       shl     bx,1\r
+       mov     di,[ylookup+bx]\r
+       add     di,[bufferofs]\r
+       add     di,[panadjust]\r
+\r
+       mov     ax,[px]\r
+       shr     ax,1\r
+       shr     ax,1\r
+       shr     ax,1            ;x location in bytes\r
+       add     di,ax\r
+       mov     [screenspot],di\r
+\r
+;\r
+; advance px\r
+;\r
+       mov     ax,[bufferbyte]\r
+       shl     ax,1\r
+       shl     ax,1\r
+       shl     ax,1\r
+       or      ax,[bufferbit]\r
+       add     [px],ax\r
+\r
+;\r
+; draw it\r
+;\r
+\r
+; set xor/or mode\r
+       mov     dx,GC_INDEX\r
+       mov     al,GC_DATAROTATE\r
+       mov     ah,[pdrawmode]\r
+       WORDOUT\r
+\r
+; set mapmask to color\r
+       mov     dx,SC_INDEX\r
+       mov     al,SC_MAPMASK\r
+       mov     ah,[fontcolor]\r
+       WORDOUT\r
+\r
+       mov     ax,[bufferbyte]\r
+       test    [bufferbit],7\r
+       jz      @@go\r
+       inc     ax                              ;so the partial byte also gets drawn\r
+@@go:\r
+       mov     [bufferwidth],ax\r
+       mov     si,[fontnumber]\r
+       shl     si,1\r
+       mov     es,[grsegs+STARTFONT*2+si]\r
+       mov     ax,[es:pcharheight]\r
+       mov     [bufferheight],ax\r
+\r
+       mov     si,OFFSET databuffer\r
+       call    BufferToScreen\r
+\r
+; set copy mode\r
+       mov     dx,GC_INDEX\r
+       mov     ax,GC_DATAROTATE\r
+       WORDOUT\r
+\r
+; set mapmask to all\r
+       mov     dx,SC_INDEX\r
+       mov     ax,SC_MAPMASK + 15*256\r
+       WORDOUT\r
+\r
+\r
+       ret\r
+\r
+ENDP\r
+\r
+endif  ;numfont\r
+\r
+;============================================================================\r
+;\r
+; MASKED FONT DRAWING ROUTINES\r
+;\r
+;============================================================================\r
+\r
+if     numfontm\r
+\r
+DATASEG\r
+\r
+mshiftdrawtable        dw      0,mshift1wide,mshift2wide,mshift3wide\r
+\r
+\r
+CODESEG\r
+\r
+;==================\r
+;\r
+; ShiftMPropChar\r
+;\r
+; Call with BX = character number (0-255)\r
+; Draws one character to the buffer at bufferbyte/bufferbit, and adjusts\r
+; them to the new position\r
+;\r
+;==================\r
+\r
+PROC   ShiftMPropChar  NEAR\r
+\r
+       mov     si,[fontnumber]\r
+       shl     si,1\r
+       mov     es,[grsegs+STARTFONTM*2+si]     ;segment of font to use\r
+\r
+;\r
+; find character location, width, and height\r
+;\r
+       mov     si,[es:charwidth+bx]\r
+       and     si,0ffh                                 ;SI hold width in pixels\r
+       shl     bx,1\r
+       mov     bx,[es:charloc+bx]              ;BX holds pointer to character data\r
+\r
+;\r
+; look up which shift table to use, based on bufferbit\r
+;\r
+       mov     di,[bufferbit]\r
+       shl     di,1\r
+       mov     bp,[shifttabletable+di] ;BP holds pointer to shift table\r
+\r
+       mov     di,OFFSET databuffer\r
+       add     di,[bufferbyte]                 ;DI holds pointer to buffer\r
+\r
+       mov     cx,[bufferbit]\r
+       add     cx,si                                   ;new bit position\r
+       mov     ax,cx\r
+       and     ax,7\r
+       mov     [bufferbit],ax                  ;new bit position\r
+       mov     ax,cx\r
+       shr     ax,1\r
+       shr     ax,1\r
+       shr     ax,1\r
+       add     [bufferbyte],ax                 ;new byte position\r
+\r
+       add     si,7\r
+       shr     si,1\r
+       shr     si,1\r
+       shr     si,1                                    ;bytes the character is wide\r
+       shl     si,1                    ;*2 to look up in shiftdrawtable\r
+\r
+       mov     cx,[es:pcharheight]\r
+       mov     dx,BUFFWIDTH\r
+       jmp     [ss:mshiftdrawtable+si] ;procedure to draw this width\r
+\r
+;\r
+; one byte character\r
+;\r
+mshift1wide:\r
+       dec     dx\r
+\r
+EVEN\r
+@@loop1m:\r
+       SHIFTWITHXOR\r
+       add     di,dx                   ; next line in buffer\r
+\r
+       loop    @@loop1m\r
+\r
+       mov     cx,[es:pcharheight]\r
+\r
+EVEN\r
+@@loop1:\r
+       SHIFTNOXOR\r
+       add     di,dx                   ; next line in buffer\r
+       loop    @@loop1\r
+\r
+       ret\r
+\r
+;\r
+; two byte character\r
+;\r
+mshift2wide:\r
+       dec     dx\r
+       dec     dx\r
+EVEN\r
+@@loop2m:\r
+       SHIFTWITHXOR\r
+       SHIFTWITHXOR\r
+       add     di,dx                   ; next line in buffer\r
+\r
+       loop    @@loop2m\r
+\r
+       mov     cx,[es:pcharheight]\r
+\r
+EVEN\r
+@@loop2:\r
+       SHIFTNOXOR\r
+       SHIFTNOXOR\r
+       add     di,dx                   ; next line in buffer\r
+       loop    @@loop2\r
+\r
+       ret\r
+\r
+;\r
+; three byte character\r
+;\r
+mshift3wide:\r
+       sub     dx,3\r
+EVEN\r
+@@loop3m:\r
+       SHIFTWITHXOR\r
+       SHIFTWITHXOR\r
+       SHIFTWITHXOR\r
+       add     di,dx                   ; next line in buffer\r
+\r
+       loop    @@loop3m\r
+\r
+       mov     cx,[es:pcharheight]\r
+\r
+EVEN\r
+@@loop3:\r
+       SHIFTNOXOR\r
+       SHIFTNOXOR\r
+       SHIFTNOXOR\r
+       add     di,dx                   ; next line in buffer\r
+       loop    @@loop3\r
+\r
+       ret\r
+\r
+\r
+ENDP\r
+\r
+;============================================================================\r
+\r
+;==================\r
+;\r
+; VW_DrawMPropString\r
+;\r
+; Draws a C string of characters at px/py and advances px\r
+;\r
+; Assumes write mode 0\r
+;\r
+;==================\r
+\r
+\r
+\r
+PROC   VW_DrawMPropString      string:DWORD\r
+PUBLIC VW_DrawMPropString\r
+USES   SI,DI\r
+\r
+;\r
+; clear out the first byte of the buffer, the rest will automatically be\r
+; cleared as characters are drawn into it\r
+;\r
+       mov     si,[fontnumber]\r
+       shl     si,1\r
+       mov     es,[grsegs+STARTFONTM*2+si]\r
+       mov     dx,[es:pcharheight]\r
+       mov     di,OFFSET databuffer\r
+       mov     ax,ds\r
+       mov     es,ax\r
+       mov     bx,BUFFWIDTH-1\r
+\r
+       mov     cx,dx\r
+       mov     al,0ffh\r
+@@maskfill:\r
+       stosb                           ; fill the mask part with $ff\r
+       add     di,bx\r
+       loop    @@maskfill\r
+\r
+       mov     cx,dx\r
+       xor     al,al\r
+@@datafill:\r
+       stosb                           ; fill the data part with $0\r
+       add     di,bx\r
+       loop    @@datafill\r
+\r
+;\r
+; shift the characters into the buffer\r
+;\r
+       mov     ax,[px]\r
+       and     ax,7\r
+       mov     [bufferbit],ax\r
+       mov     [bufferbyte],0\r
+\r
+       mov     ax,[WORD string]\r
+       mov     [stringptr],ax\r
+       mov     ax,[WORD string+2]\r
+       mov     [stringptr+2],ax\r
+\r
+@@shiftone:\r
+       mov     es,[stringptr+2]\r
+       mov     bx,[stringptr]\r
+       inc     [stringptr]\r
+       mov     bx,[es:bx]\r
+       xor     bh,bh\r
+       or      bl,bl\r
+       jz      @@allshifted\r
+       call    ShiftMPropChar\r
+       jmp     @@shiftone\r
+\r
+@@allshifted:\r
+;\r
+; calculate position to draw buffer on screen\r
+;\r
+       mov     bx,[py]\r
+       shl     bx,1\r
+       mov     di,[ylookup+bx]\r
+       add     di,[bufferofs]\r
+       add     di,[panadjust]\r
+\r
+       mov     ax,[px]\r
+       shr     ax,1\r
+       shr     ax,1\r
+       shr     ax,1            ;x location in bytes\r
+       add     di,ax\r
+       mov     [screenspot],di\r
+\r
+;\r
+; advance px\r
+;\r
+       mov     ax,[bufferbyte]\r
+       shl     ax,1\r
+       shl     ax,1\r
+       shl     ax,1\r
+       or      ax,[bufferbit]\r
+       add     [px],ax\r
+\r
+;\r
+; draw it\r
+;\r
+       mov     ax,[bufferbyte]\r
+       test    [bufferbit],7\r
+       jz      @@go\r
+       inc     ax                              ;so the partial byte also gets drawn\r
+@@go:\r
+       mov     [bufferwidth],ax\r
+       mov     es,[grsegs+STARTFONTM*2]\r
+       mov     ax,[es:pcharheight]\r
+       mov     [bufferheight],ax\r
+\r
+; set AND mode to punch out the mask\r
+       mov     dx,GC_INDEX\r
+       mov     ax,GC_DATAROTATE + 8*256\r
+       WORDOUT\r
+\r
+; set mapmask to all\r
+       mov     dx,SC_INDEX\r
+       mov     ax,SC_MAPMASK + 15*256\r
+       WORDOUT\r
+\r
+       mov     si,OFFSET databuffer\r
+       call    BufferToScreen\r
+\r
+; set OR mode to fill in the color\r
+       mov     dx,GC_INDEX\r
+       mov     ax,GC_DATAROTATE + 16*256\r
+       WORDOUT\r
+\r
+; set mapmask to color\r
+       mov     dx,SC_INDEX\r
+       mov     al,SC_MAPMASK\r
+       mov     ah,[fontcolor]\r
+       WORDOUT\r
+\r
+       call    BufferToScreen          ; SI is still in the right position in buffer\r
+\r
+; set copy mode\r
+       mov     dx,GC_INDEX\r
+       mov     ax,GC_DATAROTATE\r
+       WORDOUT\r
+\r
+; set mapmask to all\r
+       mov     dx,SC_INDEX\r
+       mov     ax,SC_MAPMASK + 15*256\r
+       WORDOUT\r
+\r
+\r
+       ret\r
+\r
+ENDP\r
+\r
+endif          ; if numfontm\r
+\r
+endif          ; if fonts\r
diff --git a/16/keen456/KEEN4-6/KEEN4/AUDIOCK4.H b/16/keen456/KEEN4-6/KEEN4/AUDIOCK4.H
new file mode 100755 (executable)
index 0000000..835b83e
--- /dev/null
@@ -0,0 +1,125 @@
+/* Reconstructed Commander Keen 4-6 Source Code\r
+ * Copyright (C) 2021 K1n9_Duk3\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License along\r
+ * with this program; if not, write to the Free Software Foundation, Inc.,\r
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+ */\r
+\r
+#ifndef __AUDIO_H__\r
+#define __AUDIO_H__\r
+\r
+//#include "VERSION.H"\r
+\r
+/////////////////////////////////////////////////\r
+//\r
+// MUSE Header for .CK4\r
+//\r
+/////////////////////////////////////////////////\r
+\r
+#define NUMSOUNDS     LASTSOUND\r
+#define NUMSNDCHUNKS  ((3*LASTSOUND)+LASTMUSIC)\r
+\r
+//\r
+// Sound names & indexes\r
+//\r
+typedef enum {\r
+       SND_WORLDWALK1,\r
+       SND_WORLDWALK2,\r
+       SND_JUMP,\r
+       SND_LAND,\r
+       SND_KEENFIRE,\r
+       SND_WORMOUTHATTACK,\r
+       SND_6,\r
+       SND_POGOBOUNCE,\r
+       SND_GETPOINTS,\r
+       SND_GETAMMO,\r
+       SND_GETWATER,\r
+       SND_11,\r
+       SND_ENTERLEVEL,\r
+       SND_LEVELDONE,\r
+       SND_NOWAY,\r
+       SND_HELMETHIT,\r
+       SND_BOUNCE2,\r
+       SND_EXTRAKEEN,\r
+       SND_OPENDOOR,\r
+       SND_GETKEY,\r
+       SND_PLUMMET,\r
+       SND_USESWITCH,\r
+       SND_SQUISH,\r
+       SND_KEENDEAD,\r
+       SND_24,\r
+       SND_SHOTEXPLODE,\r
+       SND_SWIM1,\r
+       SND_SWIM2,\r
+       SND_BOUNCE1,\r
+       SND_EATBONUS,\r
+       SND_TREASUREEATERVANISH,\r
+       SND_LINDSEY,\r
+       SND_LICKATTACK,\r
+       SND_BERKELOIDATTACK,\r
+       SND_SHOWSTATUS,\r
+       SND_HIDESTATUS,\r
+       SND_BLUB,\r
+       SND_MINEEXPLODE,\r
+       SND_SPRITEFIRE,\r
+       SND_THUNDER,\r
+       SND_FIREBALLLAND,\r
+       SND_SHOOTDART,\r
+       SND_BURP,\r
+       SND_FLAGSPIN,\r
+       SND_FLAGLAND,\r
+       SND_MAKEFOOT,\r
+       SND_SLUGPOO,\r
+       KEENPADDLESND,\r
+       BALLBOUNCESND,\r
+       COMPPADDLESND,\r
+       COMPSCOREDSND,\r
+       KEENSCOREDSND,\r
+       LASTSOUND\r
+} soundnames;\r
+\r
+#if LASTSOUND != 52\r
+#error bad sound enum!\r
+#endif\r
+\r
+#define NOWAYSND SND_NOWAY\r
+\r
+//\r
+// Base offsets\r
+//\r
+#define STARTPCSOUNDS     0\r
+#define STARTADLIBSOUNDS  (STARTPCSOUNDS+NUMSOUNDS)\r
+#define STARTDIGISOUNDS   (STARTADLIBSOUNDS+NUMSOUNDS)\r
+#define STARTMUSIC        (STARTDIGISOUNDS+NUMSOUNDS)\r
+\r
+//\r
+// Music names & indexes\r
+//\r
+typedef enum {\r
+       SHADOWS_MUS,\r
+       VEGGIES_MUS,\r
+       TOOHOT_MUS,\r
+       OASIS_MUS,\r
+       KICKPANT_MUS,\r
+       WONDER_MUS,\r
+       LASTMUSIC\r
+} musicnames;\r
+\r
+/////////////////////////////////////////////////\r
+//\r
+// Thanks for playing with MUSE!\r
+//\r
+/////////////////////////////////////////////////\r
+\r
+#endif
\ No newline at end of file
diff --git a/16/keen456/KEEN4-6/KEEN4/GFXE_CK4.EQU b/16/keen456/KEEN4-6/KEEN4/GFXE_CK4.EQU
new file mode 100755 (executable)
index 0000000..10a0c2a
--- /dev/null
@@ -0,0 +1,55 @@
+;=====================================\r
+;\r
+; Graphics .EQU file for .CK4\r
+; not IGRAB-ed :)\r
+;\r
+;=====================================\r
+\r
+;INCLUDE "VERSION.EQU"\r
+\r
+;\r
+; Amount of each data item\r
+;\r
+NUMFONT     =  3\r
+NUMFONTM    =  0\r
+NUMPICM     =  3\r
+NUMTILE8    =  108\r
+NUMTILE8M   =  36\r
+NUMTILE32   =  0\r
+NUMTILE32M  =  0\r
+\r
+;\r
+; Amount of each item in episode 4\r
+;\r
+NUMPICS     =  115\r
+NUMSPRITES  =  397\r
+NUMTILE16   =  1296\r
+NUMTILE16M  =  2916\r
+NUMEXTERN   =  16\r
+\r
+\r
+;\r
+; File offsets for data items\r
+;\r
+STRUCTPIC       =      0\r
+STRUCTPICM      =      1\r
+STRUCTSPRITE    =      2\r
+\r
+STARTFONT       =      3\r
+STARTFONTM      =      (STARTFONT+NUMFONT)\r
+STARTPICS       =      (STARTFONTM+NUMFONTM)\r
+STARTPICM       =      (STARTPICS+NUMPICS)\r
+STARTSPRITES    =      (STARTPICM+NUMPICM)\r
+STARTTILE8      =      (STARTSPRITES+NUMSPRITES)\r
+STARTTILE8M     =      (STARTTILE8+1)\r
+STARTTILE16     =      (STARTTILE8M+1)\r
+STARTTILE16M    =      (STARTTILE16+NUMTILE16)\r
+STARTTILE32     =      (STARTTILE16M+NUMTILE16M)\r
+STARTTILE32M    =      (STARTTILE32+NUMTILE32)\r
+STARTEXTERN     =      (STARTTILE32M+NUMTILE32M)\r
+\r
+NUMCHUNKS       =      (STARTEXTERN+NUMEXTERN)\r
+\r
+;\r
+; Thank you for using IGRAB!\r
+;\r
diff --git a/16/keen456/KEEN4-6/KEEN4/GFXE_CK4.H b/16/keen456/KEEN4-6/KEEN4/GFXE_CK4.H
new file mode 100755 (executable)
index 0000000..f1d92bd
--- /dev/null
@@ -0,0 +1,765 @@
+/* Reconstructed Commander Keen 4-6 Source Code\r
+ * Copyright (C) 2021 K1n9_Duk3\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License along\r
+ * with this program; if not, write to the Free Software Foundation, Inc.,\r
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+ */\r
+\r
+#ifndef __GFX_H__\r
+#define __GFX_H__\r
+\r
+//#include "VERSION.H"\r
+\r
+//////////////////////////////////////\r
+//\r
+// Graphics .H file for .CK4\r
+// not IGRAB-ed :)\r
+//\r
+//////////////////////////////////////\r
+\r
+//\r
+// Lump creation macros\r
+//\r
+\r
+#define START_LUMP(actualname, dummyname) actualname, dummyname=actualname-1,\r
+#define END_LUMP(actualname, dummyname) dummyname, actualname=dummyname-1,\r
+\r
+//\r
+// Amount of each data item\r
+//\r
+\r
+//common numbers:\r
+#define NUMCHUNKS    NUMGRCHUNKS\r
+#define NUMFONT      3\r
+#define NUMFONTM     0\r
+#define NUMPICM      3\r
+#define NUMTILE8     108       // BUG: only 104 tiles exist in EGAGRAPH!\r
+#define NUMTILE8M    36                // BUG: only 20 tiles exist in EGAGRAPH!\r
+#define NUMTILE32    0\r
+#define NUMTILE32M   0\r
+\r
+//episode-specific numbers:\r
+#define NUMPICS      115\r
+#define NUMSPRITES   397\r
+#define NUMTILE16    1296\r
+#define NUMTILE16M   2916\r
+#define NUMEXTERNS   16\r
+\r
+//\r
+// File offsets for data items\r
+//\r
+#define STRUCTPIC    0\r
+#define STRUCTPICM   1\r
+#define STRUCTSPRITE 2\r
+\r
+#define STARTFONT    3\r
+#define STARTFONTM   (STARTFONT+NUMFONT)\r
+#define STARTPICS    (STARTFONTM+NUMFONTM)\r
+#define STARTPICM    (STARTPICS+NUMPICS)\r
+#define STARTSPRITES (STARTPICM+NUMPICM)\r
+#define STARTTILE8   (STARTSPRITES+NUMSPRITES)\r
+#define STARTTILE8M  (STARTTILE8+1)\r
+#define STARTTILE16  (STARTTILE8M+1)\r
+#define STARTTILE16M (STARTTILE16+NUMTILE16)\r
+#define STARTTILE32  (STARTTILE16M+NUMTILE16M)\r
+#define STARTTILE32M (STARTTILE32+NUMTILE32)\r
+#define STARTEXTERNS (STARTTILE32M+NUMTILE32M)\r
+\r
+typedef enum {\r
+       LASTFONT=STARTPICS-1,\r
+\r
+       //\r
+       // PICS\r
+       //\r
+\r
+       H_HELPPIC,                   // 6\r
+       H_LARROWPIC,                 // 7\r
+       H_RARROWPIC,                 // 8\r
+       H_ESCPIC,                    // 9\r
+       H_ENTERPIC,                  // 10\r
+       DUMMYPIC,                    // 11\r
+       H_STORY1PIC,                 // 12\r
+       H_STORY2PIC,                 // 13\r
+       H_STORY3PIC,                 // 14\r
+       H_STORY4PIC,                 // 15\r
+       STORY5PIC,                   // 16\r
+       STORY6PIC,                   // 17\r
+       STORY7PIC,                   // 18\r
+       STORY8PIC,                   // 19\r
+       ITEM1PIC,                    // 20\r
+       ITEM2PIC,                    // 21\r
+       ITEM3PIC,                    // 22\r
+       ITEM4PIC,                    // 23\r
+       ITEM5PIC,                    // 24\r
+       ITEM6PIC,                    // 25\r
+       ITEM7PIC,                    // 26\r
+       ITEM8PIC,                    // 27\r
+       ITEM9PIC,                    // 28\r
+       ARACHNUTPIC,                 // 29\r
+       BERKELOISPIC,                // 30\r
+       BOUNDERPIC,                  // 31\r
+       COUNCILMEMBERPIC,            // 32\r
+       DOPEFISHPIC,                 // 33\r
+       INCHWORMPIC,                 // 34\r
+       LICKPIC,                     // 35\r
+       MADMUSHROOMPIC,              // 36\r
+       POISONSLIGPIC,               // 37\r
+       PRINCESSLINDSEYPIC,          // 38\r
+       SCHOOLFISHPIC,               // 39\r
+       SKYPESTPIC,                  // 40\r
+       SPRITEPIC,                   // 41\r
+       WORMOUTHPIC,                 // 42\r
+       ENDOFTEXTPIC,                // 43\r
+       H_MCPIC,                     // 44\r
+       H_HANDPIC,                   // 45\r
+       H_VISAPIC,                   // 46\r
+       H_FLASHARROW1PIC,            // 47\r
+       H_FLASHARROW2PIC,            // 48\r
+       ENDINDG1PIC,                 // 49\r
+       ENDINDG2PIC,                 // 50\r
+       ENDINDG3PIC,                 // 51\r
+       ENDINDG4PIC,                 // 52\r
+       ENDINDG5PIC,                 // 53\r
+       ENDINDG6PIC,                 // 54\r
+       ENDINDG7PIC,                 // 55\r
+       ENDINDG8PIC,                 // 56\r
+       ENDINDG9PIC,                 // 57\r
+       ENDINDG10PIC,                // 58\r
+       ENDINDG11PIC,                // 59\r
+       ENDINDG12PIC,                // 60\r
+       ENDINDG13PIC,                // 61\r
+       ENDINDG14PIC,                // 62\r
+       ENDINDG15PIC,                // 63\r
+       ENDINDG16PIC,                // 64\r
+       ENDINDG17PIC,                // 65\r
+       ENDINDG18PIC,                // 66\r
+       ENDINDG19PIC,                // 67\r
+       ENDINDG20PIC,                // 68\r
+       ENDINDG21PIC,                // 69\r
+       ENDINDG22PIC,                // 70\r
+       ENDINDG23PIC,                // 71\r
+       ENDINDG24PIC,                // 72\r
+       ENDINDG25PIC,                // 73\r
+       ENDINDG26PIC,                // 74\r
+       ENDINDG27PIC,                // 75\r
+       ENDINDG28PIC,                // 76\r
+       ENDINDG29PIC,                // 77\r
+       ENDINDG30PIC,                // 78\r
+       H_IDLOGOPIC,                 // 79\r
+       H_TOPWINDOWPIC,              // 80\r
+       H_LEFTWINDOWPIC,             // 81\r
+       H_RIGHTWINDOWPIC,            // 82\r
+       H_BOTTOMINFOPIC,             // 83\r
+       H_BOTTOMWINDOWPIC,           // 84\r
+       H_BARPIC,                    // 85\r
+       H_KEEN5PIC,                  // 86\r
+       H_KEEN6PIC,                  // 87\r
+\r
+       START_LUMP(CONTROLS_LUMP_START, __CONTROLSSTART)\r
+       CP_MAINMENUPIC,              // 88\r
+       CP_NEWGAMEMENUPIC,           // 89\r
+       CP_LOADMENUPIC,              // 90\r
+       CP_SAVEMENUPIC,              // 91\r
+       CP_CONFIGMENUPIC,            // 92\r
+       CP_SOUNDMENUPIC,             // 93\r
+       CP_MUSICMENUPIC,             // 94\r
+       CP_KEYBOARDMENUPIC,          // 95\r
+       CP_KEYMOVEMENTPIC,           // 96\r
+       CP_KEYBUTTONPIC,             // 97\r
+       CP_JOYSTICKMENUPIC,          // 98\r
+       CP_OPTIONSMENUPIC,           // 99\r
+       CP_PADDLEWARPIC,             // 100\r
+       CP_QUITPIC,                  // 101\r
+       CP_JOYSTICKPIC,              // 102\r
+       CP_MENUSCREENPIC,            // 103\r
+       END_LUMP(CONTROLS_LUMP_END, __CONTROLSEND)\r
+\r
+       IDSOFTPIC,                   // 104\r
+       PROGTEAMPIC,                 // 105\r
+       ARTISTPIC,                   // 106\r
+       DIRECTORPIC,                 // 107\r
+       SW_BACKGROUNDPIC,            // 108\r
+       TITLEPICPIC,                 // 109\r
+       ORACLEPIC,                   // 110\r
+       KEENTALK1PIC,                // 111\r
+       KEENTALK2PIC,                // 112\r
+       KEENMADPIC,                  // 113\r
+       LINDSEYPIC,                  // 114\r
+       KEENCOUNT1PIC,               // 115\r
+       KEENCOUNT2PIC,               // 116\r
+       KEENCOUNT3PIC,               // 117\r
+       KEENCOUNT4PIC,               // 118\r
+       KEENCOUNT5PIC,               // 119\r
+       KEENCOUNT6PIC,               // 120\r
+\r
+       //\r
+       // MASKED PICS\r
+       //\r
+\r
+       CP_MENUMASKPICM,             // 121\r
+       CORDPICM,                    // 122\r
+       METALPOLEPICM,               // 123\r
+\r
+       //\r
+       // SPRITES\r
+       //\r
+\r
+       START_LUMP(PADDLE_LUMP_START, __PADDLESTART)\r
+       PADDLESPR,                   // 124\r
+       BALLSPR,                     // 125\r
+       BALL1PIXELTOTHERIGHTSPR,     // 126\r
+       BALL2PIXELSTOTHERIGHTSPR,    // 127\r
+       BALL3PIXELSTOTHERIGHTSPR,    // 128\r
+       END_LUMP(PADDLE_LUMP_END, __PADDLEEND)\r
+\r
+       DEMOPLAQUESPR,               // 129\r
+\r
+       //player lump:\r
+       START_LUMP(KEEN_LUMP_START, __KEENSTART)\r
+       KEENSTANDRSPR,               // 130\r
+       KEENRUNR1SPR,                // 131\r
+       KEENRUNR2SPR,                // 132\r
+       KEENRUNR3SPR,                // 133\r
+       KEENRUNR4SPR,                // 134\r
+       KEENJUMPR1SPR,               // 135\r
+       KEENJUMPR2SPR,               // 136\r
+       KEENJUMPR3SPR,               // 137\r
+       KEENSTANDLSPR,               // 138\r
+       KEENRUNL1SPR,                // 139\r
+       KEENRUNL2SPR,                // 140\r
+       KEENRUNL3SPR,                // 141\r
+       KEENRUNL4SPR,                // 142\r
+       KEENJUMPL1SPR,               // 143\r
+       KEENJUMPL2SPR,               // 144\r
+       KEENJUMPL3SPR,               // 145\r
+       KEENLOOKUSPR,                // 146\r
+       KEENWAITR1SPR,               // 147\r
+       KEENWAITR2SPR,               // 148\r
+       KEENWAITR3SPR,               // 149\r
+       KEENSITREAD1SPR,             // 150\r
+       KEENSITREAD2SPR,             // 151\r
+       KEENSITREAD3SPR,             // 152\r
+       KEENSITREAD4SPR,             // 153\r
+       KEENREAD1SPR,                // 154\r
+       KEENREAD2SPR,                // 155\r
+       KEENREAD3SPR,                // 156\r
+       KEENSTOPREAD1SPR,            // 157\r
+       KEENSTOPREAD2SPR,            // 158\r
+       KEENWATCHSPR,                // 159\r
+       KEENLOOKD1SPR,               // 160\r
+       KEENLOOKD2SPR,               // 161\r
+       KEENDIE1SPR,                 // 162\r
+       KEENDIE2SPR,                 // 163\r
+       STUNSTARS1SPR,               // 164\r
+       STUNSTARS2SPR,               // 165\r
+       STUNSTARS3SPR,               // 166\r
+       KEENSHOOTLSPR,               // 167\r
+       KEENJLSHOOTLSPR,             // 168\r
+       KEENJSHOOTDSPR,              // 169\r
+       KEENJSHOOTUSPR,              // 170\r
+       KEENSHOOTUSPR,               // 171\r
+       KEENSHOOTRSPR,               // 172\r
+       KEENJRSHOOTRSPR,             // 173\r
+       STUN1SPR,                    // 174\r
+       STUN2SPR,                    // 175\r
+       STUN3SPR,                    // 176\r
+       STUN4SPR,                    // 177\r
+       STUNHIT1SPR,                 // 178\r
+       STUNHIT2SPR,                 // 179\r
+       KEENSHINNYR1SPR,             // 180\r
+       KEENSHINNYR2SPR,             // 181\r
+       KEENSHINNYR3SPR,             // 182\r
+       KEENSLIDED1SPR,              // 183\r
+       KEENSLIDED2SPR,              // 184\r
+       KEENSLIDED3SPR,              // 185\r
+       KEENSLIDED4SPR,              // 186\r
+       KEENSHINNYL1SPR,             // 187\r
+       KEENSHINNYL2SPR,             // 188\r
+       KEENSHINNYL3SPR,             // 189\r
+       KEENPLSHOOTUSPR,             // 190\r
+       KEENPRSHOOTUSPR,             // 191\r
+       KEENPRSHOOTDSPR,             // 192\r
+       KEENPLSHOOTDSPR,             // 193\r
+       KEENPSHOOTLSPR,              // 194\r
+       KEENPSHOOTRSPR,              // 195\r
+       KEENENTER1SPR,               // 196\r
+       KEENENTER2SPR,               // 197\r
+       KEENENTER3SPR,               // 198\r
+       KEENENTER4SPR,               // 199\r
+       KEENENTER5SPR,               // 200\r
+       KEENHANGLSPR,                // 201\r
+       KEENHANGRSPR,                // 202\r
+       KEENCLIMBEDGEL1SPR,          // 203\r
+       KEENCLIMBEDGEL2SPR,          // 204\r
+       KEENCLIMBEDGEL3SPR,          // 205\r
+       KEENCLIMBEDGEL4SPR,          // 206\r
+       KEENCLIMBEDGER1SPR,          // 207\r
+       KEENCLIMBEDGER2SPR,          // 208\r
+       KEENCLIMBEDGER3SPR,          // 209\r
+       KEENCLIMBEDGER4SPR,          // 210\r
+       KEENPOGOR1SPR,               // 211\r
+       KEENPOGOR2SPR,               // 212\r
+       KEENPOGOL1SPR,               // 213\r
+       KEENPOGOL2SPR,               // 214\r
+       DROPSPLASH1SPR,              // 215\r
+       DROPSPLASH2SPR,              // 216\r
+       DROPSPLASH3SPR,              // 217\r
+       BONUS100UPSPR,               // 218\r
+       BONUS100SPR,                 // 219\r
+       BONUS200SPR,                 // 220\r
+       BONUS500SPR,                 // 221\r
+       BONUS1000SPR,                // 222\r
+       BONUS2000SPR,                // 223\r
+       BONUS5000SPR,                // 224\r
+       BONUS1UPSPR,                 // 225\r
+       BONUSCLIPSPR,                // 226\r
+       END_LUMP(KEEN_LUMP_END, __KEENEND)\r
+\r
+       START_LUMP(SUGAR1_LUMP_START, __SUGAR1START)\r
+       SUGAR1ASPR,                  // 227\r
+       SUGAR1BSPR,                  // 228\r
+       END_LUMP(SUGAR1_LUMP_END, __SUGAR1END)\r
+\r
+       START_LUMP(SUGAR2_LUMP_START, __SUGAR2START)\r
+       SUGAR2ASPR,                  // 229\r
+       SUGAR2BSPR,                  // 230\r
+       END_LUMP(SUGAR2_LUMP_END, __SUGAR2END)\r
+\r
+       START_LUMP(SUGAR3_LUMP_START, __SUGAR3START)\r
+       SUGAR3ASPR,                  // 231\r
+       SUGAR3BSPR,                  // 232\r
+       END_LUMP(SUGAR3_LUMP_END, __SUGAR3END)\r
+\r
+       START_LUMP(SUGAR4_LUMP_START, __SUGAR4START)\r
+       SUGAR4ASPR,                  // 233\r
+       SUGAR4BSPR,                  // 234\r
+       END_LUMP(SUGAR4_LUMP_END, __SUGAR4END)\r
+\r
+       START_LUMP(SUGAR5_LUMP_START, __SUGAR5START)\r
+       SUGAR5ASPR,                  // 235\r
+       SUGAR5BSPR,                  // 236\r
+       END_LUMP(SUGAR5_LUMP_END, __SUGAR5END)\r
+\r
+       START_LUMP(SUGAR6_LUMP_START, __SUGAR6START)\r
+       SUGAR6ASPR,                  // 237\r
+       SUGAR6BSPR,                  // 238\r
+       END_LUMP(SUGAR6_LUMP_END, __SUGAR6END)\r
+\r
+       START_LUMP(ONEUP_LUMP_START, __ONEUPSTART)\r
+       ONEUPASPR,                   // 239\r
+       ONEUPBSPR,                   // 240\r
+       END_LUMP(ONEUP_LUMP_END, __ONEUPEND)\r
+\r
+       DOORSPR,                     // 241\r
+\r
+       START_LUMP(KEYGEM_LUMP_START, __KEYGEMSTART)\r
+       REDGEM1SPR,                  // 242\r
+       REDGEM2SPR,                  // 243\r
+       YELLOWGEM1SPR,               // 244\r
+       YELLOWGEM2SPR,               // 245\r
+       BLUEGEM1SPR,                 // 246\r
+       BLUEGEM2SPR,                 // 247\r
+       GREENGEM1SPR,                // 248\r
+       GREENGEM2SPR,                // 249\r
+       BONUSGEMSPR,                 // 250\r
+       END_LUMP(KEYGEM_LUMP_END, __KEYGEMEND)\r
+\r
+       START_LUMP(AMMO_LUMP_START, __AMMOSTART)\r
+       STUNCLIP1SPR,                // 251\r
+       STUNCLIP2SPR,                // 252\r
+       END_LUMP(AMMO_LUMP_END, __AMMOEND)\r
+\r
+       SCOREBOXSPR,                 // 253\r
+\r
+       START_LUMP(WORLDKEEN_LUMP_START, __WORLDKEENSTART)\r
+       WORLDKEENL1SPR,              // 254\r
+       WORLDKEENL2SPR,              // 255\r
+       WORLDKEENL3SPR,              // 256\r
+       WORLDKEENR1SPR,              // 257\r
+       WORLDKEENR2SPR,              // 258\r
+       WORLDKEENR3SPR,              // 259\r
+       WORLDKEENU1SPR,              // 260\r
+       WORLDKEENU2SPR,              // 261\r
+       WORLDKEENU3SPR,              // 262\r
+       WORLDKEEND1SPR,              // 263\r
+       WORLDKEEND2SPR,              // 264\r
+       WORLDKEEND3SPR,              // 265\r
+       WORLDKEENDR1SPR,             // 266\r
+       WORLDKEENDR2SPR,             // 267\r
+       WORLDKEENDR3SPR,             // 268\r
+       WORLDKEENDL1SPR,             // 269\r
+       WORLDKEENDL2SPR,             // 270\r
+       WORLDKEENDL3SPR,             // 271\r
+       WORLDKEENUL1SPR,             // 272\r
+       WORLDKEENUL2SPR,             // 273\r
+       WORLDKEENUL3SPR,             // 274\r
+       WORLDKEENUR1SPR,             // 275\r
+       WORLDKEENUR2SPR,             // 276\r
+       WORLDKEENUR3SPR,             // 277\r
+       WORLDKEENWAVE1SPR,           // 278\r
+       WORLDKEENWAVE2SPR,           // 279\r
+       WORLDKEENSWIMU1SPR,          // 280\r
+       WORLDKEENSWIMU2SPR,          // 281\r
+       WORLDKEENSWIMR1SPR,          // 282\r
+       WORLDKEENSWIMR2SPR,          // 283\r
+       WORLDKEENSWIMD1SPR,          // 284\r
+       WORLDKEENSWIMD2SPR,          // 285\r
+       WORLDKEENSWIML1SPR,          // 286\r
+       WORLDKEENSWIML2SPR,          // 287\r
+       WORLDKEENSWIMUR1SPR,         // 288\r
+       WORLDKEENSWIMUR2SPR,         // 289\r
+       WORLDKEENSWIMDR1SPR,         // 290\r
+       WORLDKEENSWIMDR2SPR,         // 291\r
+       WORLDKEENSWIMDL1SPR,         // 292\r
+       WORLDKEENSWIMDL2SPR,         // 293\r
+       WORLDKEENSWIMUL1SPR,         // 294\r
+       WORLDKEENSWIMUL2SPR,         // 295\r
+       WOLRDKEENRIDE1SPR,           // 296\r
+       WOLRDKEENRIDE2SPR,           // 297\r
+       FLAGFLIP1SPR,                // 298\r
+       FLAGFLIP2SPR,                // 299\r
+       FLAGFLIP3SPR,                // 300\r
+       FLAGFLIP4SPR,                // 301\r
+       FLAGFLIP5SPR,                // 302\r
+       FLAGFALL1SPR,                // 303\r
+       FLAGFALL2SPR,                // 304\r
+       FLAGFLAP1SPR,                // 305\r
+       FLAGFLAP2SPR,                // 306\r
+       FLAGFLAP3SPR,                // 307\r
+       FLAGFLAP4SPR,                // 308\r
+       END_LUMP(WORLDKEEN_LUMP_END, __WORLDKEENEND)\r
+\r
+       START_LUMP(SCUBAKEEN_LUMP_START, __SCUBAKEENSTART)\r
+       SCUBAKEENL1SPR,              // 309\r
+       SCUBAKEENL2SPR,              // 310\r
+       SCUBAKEENR1SPR,              // 311\r
+       SCUBAKEENR2SPR,              // 312\r
+       SCUBAKEENDEAD1SPR,           // 313\r
+       SCUBAKEENDEAD2SPR,           // 314\r
+       END_LUMP(SCUBAKEEN_LUMP_END, __SCUBAKEENEND)\r
+\r
+       START_LUMP(SLUG_LUMP_START, __SLUGSTART)\r
+       SLUGWALKR1SPR,               // 315\r
+       SLUGWALKR2SPR,               // 316\r
+       SLUGPISSRSPR,                 // 317\r
+       SLUGSTUN1SPR,             // 318\r
+       SLUGSTUN2SPR,             // 319\r
+       SLUGWALKL1SPR,               // 320\r
+       SLUGWALKL2SPR,               // 321\r
+       SLUGPISSLSPR,                 // 322\r
+       SLUGSLIME1SPR,               // 323\r
+       SLUGSLIME2SPR,               // 324\r
+       END_LUMP(SLUG_LUMP_END, __SLUGEND)\r
+\r
+       START_LUMP(MADMUSHROOM_LUMP_START, __MADMUSHROOMSTART)\r
+       MADMUSHROOML1SPR,            // 325\r
+       MADMUSHROOML2SPR,            // 326\r
+       MADMUSHROOMR1SPR,            // 327\r
+       MADMUSHROOMR2SPR,            // 328\r
+       END_LUMP(MADMUSHROOM_LUMP_END, __MADMUSHROOMEND)\r
+\r
+       START_LUMP(LINDSEY_LUMP_START, __LINDSEYSTART)\r
+       LINDSEY1SPR,                 // 329\r
+       LINDSEY2SPR,                 // 330\r
+       LINDSEY3SPR,                 // 331\r
+       LINDSEY4SPR,                 // 332\r
+       END_LUMP(LINDSEY_LUMP_END, __LINDSEYEND)\r
+\r
+       START_LUMP(INCHWORM_LUMP_START, __INCHWORMSTART)\r
+       INCHWORMR1SPR,               // 333\r
+       INCHWORMR2SPR,               // 334\r
+       INCHWORML1SPR,               // 335\r
+       INCHWORML2SPR,               // 336\r
+       FOOTSPR,                     // 337\r
+       END_LUMP(INCHWORM_LUMP_END, __INCHWORMEND)\r
+\r
+       START_LUMP(EATER_LUMP_START, __EATERSTART)\r
+       EATERSTAND1SPR,              // 338\r
+       EATERSTAND2SPR,              // 339\r
+       EATERJUMPR1SPR,              // 340\r
+       EATERJUMPR2SPR,              // 341\r
+       EATERJUMPR3SPR,              // 342\r
+       EATERJUMPL1SPR,              // 343\r
+       EATERJUMPL2SPR,              // 344\r
+       EATERJUMPL3SPR,              // 345\r
+       EATENBONUS1SPR,              // 346\r
+       EATENBONUS2SPR,              // 347\r
+       EATENBONUS3SPR,              // 348\r
+       EATENBONUS4SPR,              // 349\r
+       SMOKE1SPR,                   // 350\r
+       SMOKE2SPR,                   // 351\r
+       SMOKE3SPR,                   // 352\r
+       SMOKE4SPR,                   // 353\r
+       SMOKE5SPR,                   // 354\r
+       EATERSTUNSPR,                // 355\r
+       END_LUMP(EATER_LUMP_END, __EATEREND)\r
+\r
+       START_LUMP(COUNCIL_LUMP_START, __COUINCILSTART)\r
+       COUNCILWALKR1SPR,            // 356\r
+       COUNCILWALKR2SPR,            // 357\r
+       COUNCILWALKL1SPR,            // 358\r
+       COUNCILWALKL2SPR,            // 359\r
+       COUNCILTHINKLSPR,            // 360\r
+       COUNCILTHINKRSPR,            // 361\r
+       END_LUMP(COUNCIL_LUMP_END, __COUNCILEND)\r
+\r
+       START_LUMP(EGG_LUMP_START, __EGGSTART)\r
+       EGGSPR,                      // 362\r
+       EGGBROKESPR,                // 363\r
+       EGGCHIP1SPR,                // 364\r
+       EGGCHIP2SPR,                // 365\r
+       EGGCHIP3SPR,                // 366\r
+       END_LUMP(EGG_LUMP_END, __EGGEND)\r
+\r
+       START_LUMP(EGGBIRD_LUMP_START, __EGGBIRDSTART)\r
+       BIRDWALKR1SPR,               // 367\r
+       BIRDWALKR2SPR,               // 368\r
+       BIRDWALKR3SPR,               // 369\r
+       BIRDWALKR4SPR,               // 370\r
+       BIRDWALKL1SPR,               // 371\r
+       BIRDWALKL2SPR,               // 372\r
+       BIRDWALKL3SPR,               // 373\r
+       BIRDWALKL4SPR,               // 374\r
+       BIRDFLY1SPR,                 // 375\r
+       BIRDFLY2SPR,                 // 376\r
+       BIRDFLY3SPR,                 // 377\r
+       BIRDFLY4SPR,                 // 378\r
+       BIRDSTUNSPR,                 // 379\r
+       END_LUMP(EGGBIRD_LUMP_END, __EGGBIRDEND)\r
+\r
+       START_LUMP(DARTS_LUMP_START, __DARTSSTART)\r
+       DARTU1SPR,                   // 380\r
+       DARTU2SPR,                   // 381\r
+       DARTD1SPR,                   // 382\r
+       DARTD2SPR,                   // 383\r
+       DARTR1SPR,                   // 384\r
+       DARTR2SPR,                   // 385\r
+       DARTL1SPR,                   // 386\r
+       DARTL2SPR,                   // 387\r
+       END_LUMP(DARTS_LUMP_END, __DARTSEND)\r
+\r
+       START_LUMP(MIMROCK_LUMP_START, __MIMROCKSTART)\r
+       MIMROCKSPR,                  // 388\r
+       MIMROCKWALKL1SPR,            // 389\r
+       MIMROCKWALKL2SPR,            // 390\r
+       MIMROCKWALKL3SPR,            // 391\r
+       MIMROCKWALKL4SPR,            // 392\r
+       MIMROCKWALKR1SPR,            // 393\r
+       MIMROCKWALKR2SPR,            // 394\r
+       MIMROCKWALKR3SPR,            // 395\r
+       MIMROCKWALKR4SPR,            // 396\r
+       MIMROCKJUMPR1SPR,            // 397\r
+       MIMROCKJUMPR2SPR,            // 398\r
+       MIMROCKJUMPR3SPR,            // 399\r
+       MIMROCKJUMPL1SPR,            // 400\r
+       MIMROCKJUMPL2SPR,            // 401\r
+       MIMROCKJUMPL3SPR,            // 402\r
+       MINROCKSTUNSPR,              // 403\r
+       END_LUMP(MIMROCK_LUMP_END, __MIMROCKEND)\r
+\r
+       START_LUMP(DOPEFISH_LUMP_START, __DOPEFISHSTART)\r
+       DOPEFISHSWIMR1SPR,           // 404\r
+       DOPEFISHSWIMR2SPR,           // 405\r
+       DOPEFISHHUNGRYRSPR,          // 406\r
+       DOPEFISHBURP1SPR,            // 407\r
+       DOPEFISHBURP2SPR,            // 408\r
+       BIGBUBBLE1SPR,               // 409\r
+       BIGBUBBLE2SPR,               // 410\r
+       BIGBUBBLE3SPR,               // 411\r
+       BIGBUBBLE4SPR,               // 412\r
+       SMALLBUBBLE1SPR,             // 413\r
+       SMALLBUBBLE2SPR,             // 414\r
+       SMALLBUBBLE3SPR,             // 415\r
+       SMALLBUBBLE4SPR,             // 416\r
+       MEDIUMBUBBLESPR,             // 417\r
+       DOPEFISHSWIML1SPR,           // 418\r
+       DOPEFISHSWIML2SPR,           // 419\r
+       DOPEFISHHUNGRYLSPR,          // 420\r
+       END_LUMP(DOPEFISH_LUMP_END, __DOPEFISHEND)\r
+\r
+       START_LUMP(SCHOOLFISH_LUMP_START, __SCHOOLFISHSTART)\r
+       SCHOOLFISHL1SPR,             // 421\r
+       SCHOOLFISHL2SPR,             // 422\r
+       SCHOOLFISHR1SPR,             // 423\r
+       SCHOOLFISHR2SPR,             // 424\r
+       END_LUMP(SCHOOLFISH_LUMP_END, __SCHOOLFISHEND)\r
+\r
+       START_LUMP(ARACHNUT_LUMP_START, __ARACHNUTSTART)\r
+       ARACHNUTWALK1SPR,            // 425\r
+       ARACHNUTWALK2SPR,            // 426\r
+       ARACHNUTWALK3SPR,            // 427\r
+       ARACHNUTWALK4SPR,            // 428\r
+       ARACHNUTSTUNSPR,             // 429\r
+       END_LUMP(ARACHNUT_LUMP_END, __ARACHNUTEND)\r
+\r
+       SCUBASPR,                    // 430\r
+\r
+       START_LUMP(SPRITE_LUMP_START, __SPRITESTART)\r
+       SPRITEFLOATSPR,              // 431\r
+       SPRITEAIMLSPR,               // 432\r
+       SPRITESHOOTLSPR,             // 433\r
+       SPRITEAIMRSPR,               // 434\r
+       SPRITESHOOTRSPR,             // 435\r
+       SPRITESHOT1SPR,              // 436\r
+       SPRITESHOT2SPR,              // 437\r
+       SPRITESHOT3SPR,              // 438\r
+       SPRITESHOT4SPR,              // 439\r
+       END_LUMP(SPRITE_LUMP_END, __SPRITEEND)\r
+\r
+       START_LUMP(MINE_LUMP_START, __MINESTART)\r
+       MINESPR,                     // 440\r
+       MINEEXPLODE1SPR,             // 441\r
+       MINEEXPLODE2SPR,             // 442\r
+       END_LUMP(MINE_LUMP_END, __MINEEND)\r
+\r
+       START_LUMP(SKYPEST_LUMP_START, __SKYPESTSTART)\r
+       SKYPESTFLYL1SPR,             // 443\r
+       SKYPESTFLYL2SPR,             // 444\r
+       SKYPESTFLYR1SPR,             // 445\r
+       SKYPESTFLYR2SPR,             // 446\r
+       SKYPESTSIT1SPR,              // 447\r
+       SKYPESTSIT2SPR,              // 448\r
+       SKYPESTSIT3SPR,              // 449\r
+       SKYPESTSIT4SPR,              // 450\r
+       SKYPESTSIT5SPR,              // 451\r
+       SKYPESTSIT6SPR,              // 452\r
+       SKYPESTSIT7SPR,              // 453\r
+       SKYPESTSIT8SPR,              // 454\r
+       SKYPESTSIT9SPR,              // 455\r
+       SKYPESTSQUASHEDSPR,          // 456\r
+       END_LUMP(SKYPEST_LUMP_END, __SKYPESTEND)\r
+\r
+       START_LUMP(WORMOUTH_LUMP_START, __WORMOUTHSTART)\r
+       WORMOUTHSPR,                 // 457\r
+       WORMOUTHPEEKR1SPR,           // 458\r
+       WORMOUTHPEEKR2SPR,           // 459\r
+       WORMOUTHPEEKL1SPR,           // 460\r
+       WORMOUTHPEEKL2SPR,           // 461\r
+       WORMOUTHBITER1SPR,           // 462\r
+       WORMOUTHBITER2SPR,           // 463\r
+       WORMOUTHBITER3SPR,           // 464\r
+       WORMOUTHBITEL1SPR,           // 465\r
+       WORMOUTHBITEL2SPR,           // 466\r
+       WORMOUTHBITEL3SPR,           // 467\r
+       WORMOUTHSTUNSPR,             // 468\r
+       END_LUMP(WORMOUTH_LUMP_END, __WORMOUTHEND)\r
+\r
+       START_LUMP(LICK_LUMP_START, __LICKSTART)\r
+       LICKMOVER1SPR,               // 469\r
+       LICKMOVER2SPR,               // 470\r
+       LICKMOVER3SPR,               // 471\r
+       LICKMOVER4SPR,               // 472\r
+       LICKMOVEL1SPR,               // 473\r
+       LICKMOVEL2SPR,               // 474\r
+       LICKMOVEL3SPR,               // 475\r
+       LICKMOVEL4SPR,               // 476\r
+       LICKATTACKR1SPR,             // 477\r
+       LICKATTACKR2SPR,             // 478\r
+       LICKATTACKR3SPR,             // 479\r
+       LICKATTACKL1SPR,             // 480\r
+       LICKATTACKL2SPR,             // 481\r
+       LICKATTACKL3SPR,             // 482\r
+       LICKSTUNSPR,                 // 483\r
+       END_LUMP(LICK_LUMP_END, __LICKEND)\r
+\r
+       START_LUMP(PLATFORM_LUMP_START, __PLATFORMSTART)\r
+       PLATFORMSPR,                 // 484\r
+       PLATSIDETHRUST1SPR,          // 485\r
+       PLATSIDETHRUST2SPR,          // 486\r
+       PLATRTHRUST1SPR,             // 487\r
+       PLATRTHRUST2SPR,             // 488\r
+       PLATLTHRUST1SPR,             // 489\r
+       PLATLTHRUST2SPR,             // 490\r
+       END_LUMP(PLATFORM_LUMP_END, __PLATFORMEND)\r
+\r
+       START_LUMP(BOUNDER_LUMP_START, __BOUNDERSTART)\r
+       BOUNDERL1SPR,                // 491\r
+       BOUNDERL2SPR,                // 492\r
+       BOUNDERR1SPR,                // 493\r
+       BOUNDERR2SPR,                // 494\r
+       BOUNDERC1SPR,                // 495\r
+       BOUNDERC2SPR,                // 496\r
+       BOUNDERSTUNSPR,           // 497\r
+       END_LUMP(BOUNDER_LUMP_END, __BOUNDEREND)\r
+\r
+       START_LUMP(THUNDERCLOUD_LUMP_START, __THUNDERCLOUDSTART)\r
+       CLOUDSPR,                    // 498\r
+       CLOUDACTIVESPR,              // 499\r
+       CLOUDCHARGESPR,              // 500\r
+       BOLT1SPR,               // 501\r
+       BOLT2SPR,               // 502\r
+       END_LUMP(THUNDERCLOUD_LUMP_END, __THUNDERCLOUDEND)\r
+\r
+       START_LUMP(BERKELOID_LUMP_START, __BERKELOIDSTART)\r
+       BERKEWALKL1SPR,              // 503\r
+       BERKEWALKL2SPR,              // 504\r
+       BERKEWALKL3SPR,              // 505\r
+       BERKEWALKL4SPR,              // 506\r
+       BERKEWALKR1SPR,              // 507\r
+       BERKEWALKR2SPR,              // 508\r
+       BERKEWALKR3SPR,              // 509\r
+       BERKEWALKR4SPR,              // 510\r
+       BERKETHROWL1SPR,             // 511\r
+       BERKETHROWL2SPR,             // 512\r
+       BERKETHROWR1SPR,             // 513\r
+       BERKETHROWR2SPR,             // 514\r
+       FIREBALL1SPR,                // 515\r
+       FIREBALL2SPR,                // 516\r
+       FIREBALL3SPR,                // 517\r
+       FIREBALL4SPR,                // 518\r
+       END_LUMP(BERKELOID_LUMP_END, __BERKELOIDEND)\r
+\r
+       START_LUMP(MOON_LUMP_START, __MOONSTART)\r
+       KEENMOON1SPR,                // 519\r
+       KEENMOON2SPR,                // 520\r
+       END_LUMP(MOON_LUMP_END, __MOONEND)\r
+\r
+       //\r
+       // TILES (these don't need names)\r
+       //\r
+\r
+       LASTTILE=STARTEXTERNS-1,\r
+\r
+       //\r
+       // EXTERNS\r
+       //\r
+\r
+       ORDERSCREEN,                 // 4735\r
+       BIGCOMMANDER,                // 4736\r
+       BIGKEEN,                     // 4737\r
+       OUTOFMEM,                    // 4738\r
+\r
+       //texts\r
+       T_HELPART,                   // 4739\r
+       T_STORYART,                  // 4740\r
+       T_CONTRART,                  // 4741\r
+       T_IDART,                     // 4742\r
+       T_ENDART,                    // 4743\r
+       T_DEMOART,                   // 4744\r
+       T_ORDERART,                  // 4745\r
+\r
+       //demos\r
+       DEMO0,                       // 4746\r
+       DEMO1,                       // 4747\r
+       DEMO2,                       // 4748\r
+       DEMO3,                       // 4749\r
+       DEMO4,                       // 4750\r
+\r
+       NUMGRCHUNKS\r
+} graphicnums;\r
+\r
+#undef START_LUMP\r
+#undef END_LUMP\r
+\r
+#endif //__GFX_H__
\ No newline at end of file
diff --git a/16/keen456/KEEN4-6/KEEN4/ID_ASM.EQU b/16/keen456/KEEN4-6/KEEN4/ID_ASM.EQU
new file mode 100755 (executable)
index 0000000..6f75567
--- /dev/null
@@ -0,0 +1,115 @@
+;\r
+; Equates for all .ASM files\r
+;\r
+\r
+;----------------------------------------------------------------------------\r
+\r
+INCLUDE        "GFXE_CK4.EQU"\r
+\r
+;----------------------------------------------------------------------------\r
+\r
+CGAGR          =       1\r
+EGAGR          =       2\r
+VGAGR          =       3\r
+\r
+GRMODE         =       EGAGR\r
+PROFILE                =       0                       ; 1=keep stats on tile drawing\r
+\r
+SC_INDEX       =       03C4h\r
+SC_RESET       =       0\r
+SC_CLOCK       =       1\r
+SC_MAPMASK     =       2\r
+SC_CHARMAP     =       3\r
+SC_MEMMODE     =       4\r
+\r
+CRTC_INDEX     =       03D4h\r
+CRTC_H_TOTAL   =       0\r
+CRTC_H_DISPEND =       1\r
+CRTC_H_BLANK   =       2\r
+CRTC_H_ENDBLANK        =       3\r
+CRTC_H_RETRACE =       4\r
+CRTC_H_ENDRETRACE =    5\r
+CRTC_V_TOTAL   =       6\r
+CRTC_OVERFLOW  =       7\r
+CRTC_ROWSCAN   =       8\r
+CRTC_MAXSCANLINE =     9\r
+CRTC_CURSORSTART =     10\r
+CRTC_CURSOREND =       11\r
+CRTC_STARTHIGH =       12\r
+CRTC_STARTLOW  =       13\r
+CRTC_CURSORHIGH        =       14\r
+CRTC_CURSORLOW =       15\r
+CRTC_V_RETRACE =       16\r
+CRTC_V_ENDRETRACE =    17\r
+CRTC_V_DISPEND =       18\r
+CRTC_OFFSET    =       19\r
+CRTC_UNDERLINE =       20\r
+CRTC_V_BLANK   =       21\r
+CRTC_V_ENDBLANK        =       22\r
+CRTC_MODE      =       23\r
+CRTC_LINECOMPARE =     24\r
+\r
+\r
+GC_INDEX       =       03CEh\r
+GC_SETRESET    =       0\r
+GC_ENABLESETRESET =    1\r
+GC_COLORCOMPARE        =       2\r
+GC_DATAROTATE  =       3\r
+GC_READMAP     =       4\r
+GC_MODE                =       5\r
+GC_MISCELLANEOUS =     6\r
+GC_COLORDONTCARE =     7\r
+GC_BITMASK     =       8\r
+\r
+ATR_INDEX      =       03c0h\r
+ATR_MODE       =       16\r
+ATR_OVERSCAN   =       17\r
+ATR_COLORPLANEENABLE = 18\r
+ATR_PELPAN     =       19\r
+ATR_COLORSELECT        =       20\r
+\r
+STATUS_REGISTER_1     =        03dah\r
+\r
+\r
+MACRO  WORDOUT\r
+       out     dx,ax\r
+ENDM\r
+\r
+if 0\r
+\r
+MACRO  WORDOUT\r
+       out     dx,al\r
+       inc     dx\r
+       xchg    al,ah\r
+       out     dx,al\r
+       dec     dx\r
+       xchg    al,ah\r
+ENDM\r
+\r
+endif\r
+\r
+UPDATEWIDE     =       22\r
+UPDATEHIGH     =       14\r
+\r
+;\r
+; tile info offsets from segment tinf\r
+;\r
+\r
+ANIM           =       402\r
+SPEED          =       (ANIM+NUMTILE16)\r
+\r
+NORTHWALL      =       (SPEED+NUMTILE16)\r
+EASTWALL       =       (NORTHWALL+NUMTILE16M)\r
+SOUTHWALL   =  (EASTWALL+NUMTILE16M)\r
+WESTWALL    =  (SOUTHWALL+NUMTILE16M)\r
+MANIM       =  (WESTWALL+NUMTILE16M)\r
+INTILE      =  (MANIM+NUMTILE16M)\r
+MSPEED      =  (INTILE+NUMTILE16M)\r
+\r
+\r
+IFE GRMODE-EGAGR\r
+SCREENWIDTH    =       64\r
+ENDIF\r
+IFE GRMODE-CGAGR\r
+SCREENWIDTH    =       128\r
+ENDIF\r
diff --git a/16/keen456/KEEN4-6/KEEN4/ID_HEADS.H b/16/keen456/KEEN4-6/KEEN4/ID_HEADS.H
new file mode 100755 (executable)
index 0000000..1c42a6b
--- /dev/null
@@ -0,0 +1,109 @@
+/* Reconstructed Commander Keen 4-6 Source Code\r
+ * Copyright (C) 2021 K1n9_Duk3\r
+ *\r
+ * This file is primarily based on:\r
+ * Catacomb 3-D Source Code\r
+ * Copyright (C) 1993-2014 Flat Rock Software\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License along\r
+ * with this program; if not, write to the Free Software Foundation, Inc.,\r
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+ */\r
+\r
+// ID_GLOB.H\r
+\r
+\r
+#include <ALLOC.H>\r
+#include <CTYPE.H>\r
+#include <DOS.H>\r
+#include <ERRNO.H>\r
+#include <FCNTL.H>\r
+#include <IO.H>\r
+#include <MEM.H>\r
+#include <PROCESS.H>\r
+#include <STDIO.H>\r
+#include <STDLIB.H>\r
+#include <STRING.H>\r
+#include <SYS\STAT.H>\r
+\r
+#define __ID_GLOB__\r
+\r
+//--------------------------------------------------------------------------\r
+\r
+#define KEEN\r
+#define KEEN4\r
+\r
+#define        EXTENSION       "CK4"\r
+\r
+extern char far introscn;\r
+\r
+#include "GFXE_CK4.H"\r
+#include "AUDIOCK4.H"\r
+\r
+//--------------------------------------------------------------------------\r
+\r
+#define        TEXTGR  0\r
+#define        CGAGR   1\r
+#define        EGAGR   2\r
+#define        VGAGR   3\r
+\r
+#define GRMODE EGAGR\r
+\r
+#if GRMODE == EGAGR\r
+#define GREXT  "EGA"\r
+#endif\r
+#if GRMODE == CGAGR\r
+#define GREXT  "CGA"\r
+#endif\r
+\r
+//#define PROFILE\r
+\r
+//\r
+//     ID Engine\r
+//     Types.h - Generic types, #defines, etc.\r
+//     v1.0d1\r
+//\r
+\r
+#ifndef        __TYPES__\r
+#define        __TYPES__\r
+\r
+typedef        enum    {false,true}    boolean;\r
+typedef        unsigned        char            byte;\r
+typedef        unsigned        int                     word;\r
+typedef        unsigned        long            longword;\r
+typedef        byte *                                  Ptr;\r
+\r
+typedef        struct\r
+               {\r
+                       int     x,y;\r
+               } Point;\r
+typedef        struct\r
+               {\r
+                       Point   ul,lr;\r
+               } Rect;\r
+\r
+#define        nil     ((void *)0)\r
+\r
+#endif\r
+\r
+#include "ID_MM.H"\r
+#include "ID_CA.H"\r
+#include "ID_VW.H"\r
+#include "ID_RF.H"\r
+#include "ID_IN.H"\r
+#include "ID_SD.H"\r
+#include "ID_US.H"\r
+\r
+\r
+void   Quit (char *error);             // defined in user program\r
+\r
diff --git a/16/keen456/KEEN4-6/KEEN4/K4_ACT1.C b/16/keen456/KEEN4-6/KEEN4/K4_ACT1.C
new file mode 100755 (executable)
index 0000000..f26104b
--- /dev/null
@@ -0,0 +1,1221 @@
+/* Reconstructed Commander Keen 4-6 Source Code\r
+ * Copyright (C) 2021 K1n9_Duk3\r
+ *\r
+ * This file is loosely based on:\r
+ * Keen Dreams Source Code\r
+ * Copyright (C) 2014 Javier M. Chavez\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License along\r
+ * with this program; if not, write to the Free Software Foundation, Inc.,\r
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+ */\r
+\r
+/*\r
+K4_ACT1.C\r
+=========\r
+\r
+Contains the following actor types (in this order):\r
+\r
+- Miragia (world map)\r
+- Bonus Items\r
+- Council Member\r
+- Poison Slug\r
+- Mad Mushroom\r
+- Egg & Eggbird\r
+- Arachnut\r
+- Skypest\r
+\r
+*/\r
+\r
+#include "CK_DEF.H"\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                 MIRAGIA\r
+\r
+=============================================================================\r
+*/\r
+\r
+statetype s_miragia0 = {0, 0, step, false, false, 300, 0, 0, T_Miragia0, NULL, NULL, &s_miragia1};\r
+statetype s_miragia1 = {0, 0, step, false, false,  30, 0, 0, T_Miragia1, NULL, NULL, &s_miragia2};\r
+statetype s_miragia2 = {0, 0, step, false, false,  30, 0, 0, T_Miragia2, NULL, NULL, &s_miragia3};\r
+statetype s_miragia3 = {0, 0, step, false, false,  30, 0, 0, T_Miragia3, NULL, NULL, &s_miragia4};\r
+statetype s_miragia4 = {0, 0, step, false, false, 300, 0, 0, T_Miragia4, NULL, NULL, &s_miragia5};\r
+statetype s_miragia5 = {0, 0, step, false, false,  30, 0, 0, T_Miragia5, NULL, NULL, &s_miragia6};\r
+statetype s_miragia6 = {0, 0, step, false, false,  30, 0, 0, T_Miragia6, NULL, NULL, &s_miragia7};\r
+statetype s_miragia7 = {0, 0, step, false, false,  30, 0, 0, T_Miragia7, NULL, NULL, &s_miragia0};\r
+\r
+/*\r
+===========================\r
+=\r
+= SpawnMiragia\r
+=\r
+===========================\r
+*/\r
+\r
+void SpawnMiragia(Sint16 x, Sint16 y)\r
+{\r
+       GetNewObj(false);\r
+       new->obclass = inertobj;\r
+       new->active = ac_allways;\r
+       new->x = x;\r
+       new->y = y;\r
+       new->state = &s_miragia0;\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= T_Miragia0\r
+=\r
+===========================\r
+*/\r
+\r
+void T_Miragia0(objtype *ob)\r
+{\r
+       if (player->tileright >= ob->x && player->tileleft <= ob->x+5\r
+               && player->tiletop >= ob->y && player->tilebottom <= ob->y+4)\r
+       {\r
+               //don't let miragia appear if player is in the area, so player won't get stuck\r
+               ob->state = &s_miragia7;\r
+       }\r
+       else\r
+       {\r
+               RF_MapToMap(8, 60, ob->x, ob->y, 6, 4);\r
+       }\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= T_Miragia1\r
+=\r
+===========================\r
+*/\r
+\r
+void T_Miragia1(objtype *ob)\r
+{\r
+       RF_MapToMap(14, 60, ob->x, ob->y, 6, 4);\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= T_Miragia2\r
+=\r
+===========================\r
+*/\r
+\r
+void T_Miragia2(objtype *ob)\r
+{\r
+       RF_MapToMap(20, 60, ob->x, ob->y, 6, 4);\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= T_Miragia3\r
+=\r
+===========================\r
+*/\r
+\r
+void T_Miragia3(objtype *ob)\r
+{\r
+       RF_MapToMap(26, 60, ob->x, ob->y, 6, 4);\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= T_Miragia4\r
+=\r
+===========================\r
+*/\r
+\r
+void T_Miragia4(objtype *ob)\r
+{\r
+       RF_MapToMap(20, 60, ob->x, ob->y, 6, 4);\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= T_Miragia5\r
+=\r
+===========================\r
+*/\r
+\r
+void T_Miragia5(objtype *ob)\r
+{\r
+       RF_MapToMap(14, 60, ob->x, ob->y, 6, 4);\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= T_Miragia6\r
+=\r
+===========================\r
+*/\r
+\r
+void T_Miragia6(objtype *ob)\r
+{\r
+       RF_MapToMap(8, 60, ob->x, ob->y, 6, 4);\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= T_Miragia7\r
+=\r
+===========================\r
+*/\r
+\r
+void T_Miragia7(objtype *ob)\r
+{\r
+       RF_MapToMap(2, 60, ob->x, ob->y, 6, 4);\r
+}\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                 BONUS ITEMS\r
+\r
+temp1 = bonus type\r
+temp2 = base shape number\r
+temp3 = last animated shape number +1\r
+\r
+=============================================================================\r
+*/\r
+\r
+statetype s_bonus1 = {0, 0, step, false, false, 20, 0, 0, T_Bonus, NULL, R_Draw, &s_bonus2};\r
+statetype s_bonus2 = {0, 0, step, false, false, 20, 0, 0, T_Bonus, NULL, R_Draw, &s_bonus1};\r
+statetype s_bonusrise = {0, 0, slide, false, false, 40, 0, 8, NULL, NULL, R_Draw, NULL};\r
+\r
+statetype s_splash1 = {DROPSPLASH1SPR, DROPSPLASH1SPR, step, false, false, 10, 0, 0, NULL, NULL, R_Draw, &s_splash2};\r
+statetype s_splash2 = {DROPSPLASH2SPR, DROPSPLASH2SPR, step, false, false, 10, 0, 0, NULL, NULL, R_Draw, &s_splash3};\r
+statetype s_splash3 = {DROPSPLASH3SPR, DROPSPLASH3SPR, step, false, false, 10, 0, 0, NULL, NULL, R_Draw, NULL};\r
+\r
+Uint16 bonusshape[] = {REDGEM1SPR, YELLOWGEM1SPR, BLUEGEM1SPR, GREENGEM1SPR, SUGAR1ASPR, SUGAR2ASPR, SUGAR3ASPR, SUGAR4ASPR, SUGAR5ASPR, SUGAR6ASPR, ONEUPASPR, STUNCLIP1SPR};\r
+\r
+/*\r
+===========================\r
+=\r
+= SpawnBonus\r
+=\r
+===========================\r
+*/\r
+\r
+void SpawnBonus(Sint16 x, Sint16 y, Sint16 type)\r
+{\r
+       GetNewObj(false);\r
+       new->needtoclip = cl_noclip;\r
+       new->priority = 2;\r
+       new->obclass = bonusobj;\r
+       new->x = CONVERT_TILE_TO_GLOBAL(x);\r
+       new->y = CONVERT_TILE_TO_GLOBAL(y);\r
+       new->ydir = -1;\r
+       new->temp1 = type;\r
+       new->temp2 = new->shapenum = bonusshape[type];\r
+       new->temp3 = new->temp2 + 2;\r
+       NewState(new, &s_bonus1);\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= SpawnSplash\r
+=\r
+===========================\r
+*/\r
+\r
+void SpawnSplash(Sint16 x, Sint16 y)\r
+{\r
+       GetNewObj(true);\r
+       new->needtoclip = cl_noclip;\r
+       new->priority = 3;\r
+       new->obclass = inertobj;\r
+       new->x = CONVERT_TILE_TO_GLOBAL(x);\r
+       new->y = CONVERT_TILE_TO_GLOBAL(y);\r
+       NewState(new, &s_splash1);\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= T_Bonus\r
+=\r
+===========================\r
+*/\r
+\r
+void T_Bonus(objtype *ob)\r
+{\r
+       ob->shapenum++;\r
+       if (ob->shapenum == ob->temp3)\r
+               ob->shapenum = ob->temp2;\r
+}\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                 COUNCIL MEMBER\r
+\r
+=============================================================================\r
+*/\r
+\r
+statetype s_councilwalk1 = {COUNCILWALKL1SPR, COUNCILWALKR1SPR, step, false, true,   10,  64, 0, T_Council, NULL, R_Walk, &s_councilwalk2};\r
+statetype s_councilwalk2 = {COUNCILWALKL2SPR, COUNCILWALKR2SPR, step, false, true,   10,  64, 0, T_Council, NULL, R_Walk, &s_councilwalk1};\r
+statetype s_councilstand = {COUNCILTHINKLSPR, COUNCILTHINKRSPR, step, false, false, 120, 128, 0, NULL, NULL, R_Draw, &s_councilwalk1};\r
+\r
+/*\r
+===========================\r
+=\r
+= SpawnCouncil\r
+=\r
+===========================\r
+*/\r
+\r
+void SpawnCouncil(Sint16 x, Sint16 y)\r
+{\r
+       GetNewObj(false);\r
+       new->obclass = oracleobj;\r
+       new->active = ac_yes;\r
+       new->priority = 0;\r
+       new->x = CONVERT_TILE_TO_GLOBAL(x);\r
+       new->y = CONVERT_TILE_TO_GLOBAL(y) -0x171;      //TODO: wierd\r
+       if (US_RndT() < 0x80)\r
+       {\r
+               new->xdir = 1;\r
+       }\r
+       else\r
+       {\r
+               new->xdir = -1;\r
+       }\r
+       new->ydir = 1;\r
+       NewState(new, &s_councilwalk1);\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= T_Council\r
+=\r
+===========================\r
+*/\r
+\r
+void T_Council(objtype *ob)\r
+{\r
+       Uint16 randnum;\r
+\r
+       randnum = US_RndT();\r
+       if (tics*8 > randnum)\r
+       {\r
+               // BUG: might be about to move off a ledge, causing it to get stuck\r
+               // after standing (the stand state doesn't use R_Walk)!\r
+               // To avoid that, set xtry to 0 here.\r
+               ob->state = &s_councilstand;\r
+       }\r
+}\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                 POINSON SLUG\r
+\r
+=============================================================================\r
+*/\r
+\r
+statetype s_slugwalk1   = {SLUGWALKL1SPR,   SLUGWALKR1SPR,   step,  false, true,    8, 64, 0, NULL, C_Slug, R_WalkNormal, &s_slugwalk2};\r
+statetype s_slugwalk2   = {SLUGWALKL2SPR,   SLUGWALKR2SPR,   step,  false, true,    8, 64, 0, T_Slug, C_Slug, R_WalkNormal, &s_slugwalk1};\r
+statetype s_slugpiss1   = {SLUGPISSLSPR,    SLUGPISSRSPR,    step,  false, true,   60, 64, 0, T_SlugPiss, C_Slug, R_WalkNormal, &s_slugwalk1};\r
+statetype s_slugstun    = {SLUGSTUN1SPR,    SLUGSTUN1SPR,    think, false, false,   0,  0, 0, T_Projectile, NULL, R_Stunned, NULL};\r
+statetype s_slugstunalt = {SLUGSTUN2SPR,    SLUGSTUN2SPR,    think, false, false,   0,  0, 0, T_Projectile, NULL, R_Stunned, NULL};\r
+statetype s_slugslime   = {SLUGSLIME1SPR,   SLUGSLIME1SPR,   step,  false, false, 300,  0, 0, NULL, C_Lethal, R_Draw, &s_slugslime2};\r
+statetype s_slugslime2  = {SLUGSLIME2SPR,   SLUGSLIME2SPR,   step,  false, false,  60,  0, 0, NULL, NULL, R_Draw, NULL};\r
+\r
+/*\r
+===========================\r
+=\r
+= SpawnSlug\r
+=\r
+===========================\r
+*/\r
+\r
+void SpawnSlug(Sint16 x, Sint16 y)\r
+{\r
+       GetNewObj(false);\r
+       new->obclass = slugobj;\r
+       new->active = ac_yes;\r
+       new->priority = 2;\r
+       new->x = CONVERT_TILE_TO_GLOBAL(x);\r
+       new->y = CONVERT_TILE_TO_GLOBAL(y) - 0x71;\r
+       if (US_RndT() < 0x80)\r
+       {\r
+               new->xdir = 1;\r
+       }\r
+       else\r
+       {\r
+               new->xdir = -1;\r
+       }\r
+       new->ydir = 1;\r
+       NewState(new, &s_slugwalk1);\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= T_Slug\r
+=\r
+===========================\r
+*/\r
+\r
+void T_Slug(objtype *ob)\r
+{\r
+       if (US_RndT() < 16)\r
+       {\r
+               if (ob->x < player->x)\r
+               {\r
+                       ob->xdir = 1;\r
+               }\r
+               else\r
+               {\r
+                       ob->xdir = -1;\r
+               }\r
+               ob->state = &s_slugpiss1;\r
+               SD_PlaySound(SND_SLUGPOO);\r
+               // Note: might be a good idea to set xtry to 0 here\r
+       }\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= T_SlugPiss\r
+=\r
+===========================\r
+*/\r
+\r
+void T_SlugPiss(objtype *ob)\r
+{\r
+       GetNewObj(true);\r
+       new->obclass = inertobj;\r
+       new->active = ac_removable;\r
+       new->priority = 0;\r
+       new->x = ob->x;\r
+       new->y = ob->bottom - 8*PIXGLOBAL;\r
+       NewState(new, &s_slugslime);\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= C_Slug\r
+=\r
+===========================\r
+*/\r
+\r
+void C_Slug(objtype *ob, objtype *hit)\r
+{\r
+       if (hit->obclass == keenobj)\r
+       {\r
+               KillKeen();\r
+       }\r
+       else if (hit->obclass == stunshotobj)\r
+       {\r
+               if (US_RndT() < 0x80)\r
+               {\r
+                       StunObj(ob, hit, &s_slugstun);\r
+               }\r
+               else\r
+               {\r
+                       StunObj(ob, hit, &s_slugstunalt);\r
+               }\r
+               ob->yspeed = -24;\r
+               ob->xspeed = ob->xdir*8;\r
+       }\r
+}\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                 MAD MUSHROOM\r
+\r
+temp1 = jump counter\r
+\r
+=============================================================================\r
+*/\r
+\r
+statetype s_mushroom1 = {MADMUSHROOML1SPR, MADMUSHROOMR1SPR, stepthink, false, false, 8, 0, 0, T_Mushroom, C_Mushroom, R_Mushroom, &s_mushroom2};\r
+statetype s_mushroom2 = {MADMUSHROOML2SPR, MADMUSHROOMR2SPR, stepthink, false, false, 8, 0, 0, T_Mushroom, C_Mushroom, R_Mushroom, &s_mushroom1};\r
+\r
+/*\r
+===========================\r
+=\r
+= SpawnMadMushroom\r
+=\r
+===========================\r
+*/\r
+\r
+void SpawnMadMushroom(Sint16 x, Sint16 y)\r
+{\r
+       GetNewObj(false);\r
+       new->obclass = madmushroomobj;\r
+       new->active = ac_yes;\r
+       new->priority = 0;\r
+       new->x = CONVERT_TILE_TO_GLOBAL(x);\r
+       new->y = CONVERT_TILE_TO_GLOBAL(y) - 0xF1;\r
+       new->xdir = 1;\r
+       new->ydir = 1;\r
+       NewState(new, &s_mushroom1);\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= T_Mushroom\r
+=\r
+===========================\r
+*/\r
+\r
+void T_Mushroom(objtype *ob)\r
+{\r
+       if (player->x < ob->x)\r
+       {\r
+               ob->xdir = -1;\r
+       }\r
+       else\r
+       {\r
+               ob->xdir = 1;\r
+       }\r
+\r
+       // BUG: this might be executed twice during the same frame if the\r
+       // object's animation/state changed during that frame, causing the\r
+       // object to move at twice the speed during that frame!\r
+       // To avoid that, return if ytry is not 0.\r
+       DoWeakGravity(ob);\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= C_Mushroom\r
+=\r
+===========================\r
+*/\r
+\r
+void C_Mushroom(objtype *ob, objtype *hit)\r
+{\r
+       ob++;                   // shut up compiler\r
+       if (hit->obclass == stunshotobj)\r
+       {\r
+               ExplodeShot(hit);\r
+       }\r
+       else if (hit->obclass == keenobj)\r
+       {\r
+               KillKeen();\r
+       }\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= R_Mushroom\r
+=\r
+===========================\r
+*/\r
+\r
+void R_Mushroom(objtype *ob)\r
+{\r
+       if (ob->hitsouth)\r
+               ob->yspeed = 0;\r
+\r
+       if (ob->hitnorth)\r
+       {\r
+               ob->yspeed = 0;\r
+               if (++ob->temp1 == 3)\r
+               {\r
+                       ob->temp1 = 0;\r
+                       ob->yspeed = -68;\r
+                       SD_PlaySound(SND_BOUNCE2);\r
+               }\r
+               else\r
+               {\r
+                       SD_PlaySound(SND_BOUNCE1);\r
+                       ob->yspeed = -40;\r
+               }\r
+       }\r
+       RF_PlaceSprite(&ob->sprite, ob->x, ob->y, ob->shapenum, spritedraw, ob->priority);\r
+}\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                 EGGBIRD\r
+\r
+temp1 = blocked flag for flying eggbird (cannot change xdir to chase Keen\r
+        while this is non-zero)\r
+\r
+=============================================================================\r
+*/\r
+\r
+statetype s_egg      = {EGGSPR,      EGGSPR,      think, false, true,      8, 0, 0, NULL, C_Egg, R_Draw, NULL};\r
+statetype s_eggbroke = {EGGBROKESPR, EGGBROKESPR, step,  false, false, 30000, 0, 0, NULL, NULL, R_Draw, NULL};\r
+statetype s_eggchip1 = {EGGCHIP1SPR, EGGCHIP1SPR, think, false, false,     0, 0, 0, T_Projectile, NULL, R_Chip, NULL};\r
+statetype s_eggchip2 = {EGGCHIP2SPR, EGGCHIP2SPR, think, false, false,     0, 0, 0, T_Projectile, NULL, R_Chip, NULL};\r
+statetype s_eggchip3 = {EGGCHIP3SPR, EGGCHIP3SPR, think, false, false,     0, 0, 0, T_Projectile, NULL, R_Chip, NULL};\r
+\r
+statetype s_eggbirdpause = {BIRDWALKL1SPR, BIRDWALKR1SPR, step,       false, true,  120, 128, 0, NULL, C_EggbirdStun, R_Eggbird, &s_eggbirdwalk2};\r
+statetype s_eggbirdwalk1 = {BIRDWALKL1SPR, BIRDWALKR1SPR, step,       false, true,    7, 128, 0, NULL, C_Eggbird, R_Eggbird, &s_eggbirdwalk2};\r
+statetype s_eggbirdwalk2 = {BIRDWALKL2SPR, BIRDWALKR2SPR, step,       false, true,    7, 128, 0, NULL, C_Eggbird, R_Eggbird, &s_eggbirdwalk3};\r
+statetype s_eggbirdwalk3 = {BIRDWALKL3SPR, BIRDWALKR3SPR, step,       false, true,    7, 128, 0, NULL, C_Eggbird, R_Eggbird, &s_eggbirdwalk4};\r
+statetype s_eggbirdwalk4 = {BIRDWALKL4SPR, BIRDWALKR4SPR, step,       false, true,    7, 128, 0, T_Eggbird, C_Eggbird, R_Eggbird, &s_eggbirdwalk1};\r
+statetype s_eggbirdfly1  = {BIRDFLY1SPR,   BIRDFLY1SPR,   slidethink, false, false,   8,   0, 0, T_EggbirdFly, C_Eggbird, R_Eggbirdfly, &s_eggbirdfly2};\r
+statetype s_eggbirdfly2  = {BIRDFLY2SPR,   BIRDFLY2SPR,   slidethink, false, false,   8,   0, 0, T_EggbirdFly, C_Eggbird, R_Eggbirdfly, &s_eggbirdfly3};\r
+statetype s_eggbirdfly3  = {BIRDFLY3SPR,   BIRDFLY3SPR,   slidethink, false, false,   8,   0, 0, T_EggbirdFly, C_Eggbird, R_Eggbirdfly, &s_eggbirdfly4};\r
+statetype s_eggbirdfly4  = {BIRDFLY4SPR,   BIRDFLY4SPR,   slidethink, false, false,   8,   0, 0, T_EggbirdFly, C_Eggbird, R_Eggbirdfly, &s_eggbirdfly1};\r
+statetype s_eggbirddrop  = {BIRDFLY4SPR,   BIRDFLY4SPR,   think,      false, false,   8, 128, 0, T_WeakProjectile, C_Eggbird, R_EggbirdDrop, NULL};\r
+statetype s_eggbirdstun  = {BIRDSTUNSPR,   BIRDSTUNSPR,   stepthink,  false, false, 240,   0, 0, T_Projectile, C_EggbirdStun, R_Draw, &s_eggbirdstun2};\r
+statetype s_eggbirdstun2 = {BIRDWALKL1SPR, BIRDWALKR1SPR, step,       false, true,   20,   0, 0, NULL, C_EggbirdStun, R_Draw, &s_eggbirdstun3};\r
+statetype s_eggbirdstun3 = {BIRDSTUNSPR,   BIRDSTUNSPR,   step,       false, true,   20,   0, 0, NULL, C_EggbirdStun, R_Draw, &s_eggbirdstun4};\r
+statetype s_eggbirdstun4 = {BIRDWALKL1SPR, BIRDWALKR1SPR, step,       false, true,   20,   0, 0, NULL, C_EggbirdStun, R_Draw, &s_eggbirdstun5};\r
+statetype s_eggbirdstun5 = {BIRDSTUNSPR,   BIRDSTUNSPR,   step,       false, true,   20,   0, 0, T_EggUnstun, C_EggbirdStun, R_Draw, &s_eggbirdwalk1};\r
+\r
+/*\r
+===========================\r
+=\r
+= SpawnEggbird\r
+=\r
+===========================\r
+*/\r
+\r
+void SpawnEggbird(Sint16 x, Sint16 y)\r
+{\r
+       GetNewObj(false);\r
+       new->obclass = eggobj;\r
+       new->active = ac_yes;\r
+       new->priority = 2;\r
+       new->x = CONVERT_TILE_TO_GLOBAL(x);\r
+       new->y = CONVERT_TILE_TO_GLOBAL(y) - 0x71;\r
+       new->xdir = 1;\r
+       new->ydir = 1;\r
+       NewState(new, &s_egg);\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= T_EggUnstun\r
+=\r
+===========================\r
+*/\r
+\r
+void T_EggUnstun(objtype *ob)\r
+{\r
+       ob->obclass = eggbirdobj;\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= SpawnEggbirdOut\r
+=\r
+===========================\r
+*/\r
+\r
+void SpawnEggbirdOut(Sint16 x, Sint16 y)\r
+{\r
+       GetNewObj(true);\r
+       new->obclass = eggbirdobj;\r
+       new->active = ac_yes;\r
+       new->priority = 2;\r
+       new->x = CONVERT_TILE_TO_GLOBAL(x);\r
+       new->y = CONVERT_TILE_TO_GLOBAL(y) - 0xF1;\r
+       if (new->x < player->x)\r
+       {\r
+               new->xdir = 1;\r
+       }\r
+       else\r
+       {\r
+               new->xdir = -1;\r
+       }\r
+       new->ydir = 1;\r
+       NewState(new, &s_eggbirdpause);\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= C_Egg\r
+=\r
+===========================\r
+*/\r
+\r
+void C_Egg(objtype *ob, objtype *hit)\r
+{\r
+       if (hit->obclass == stunshotobj || hit->obclass == keenobj)\r
+       {\r
+               if (hit->obclass == stunshotobj)\r
+                       ExplodeShot(hit);\r
+\r
+               ob->obclass = inertobj;\r
+               ob->active = ac_removable;\r
+               ChangeState(ob, &s_eggbroke);\r
+\r
+               GetNewObj(true);\r
+               new->obclass = eggbirdobj;\r
+               new->active = ac_yes;\r
+               new->x = ob->x;\r
+               new->y = ob->y - 8*PIXGLOBAL;\r
+               if (ob->x < player->x)\r
+               {\r
+                       new->xdir = 1;\r
+               }\r
+               else\r
+               {\r
+                       new->xdir = -1;\r
+               }\r
+               new->ydir = 1;\r
+               NewState(new, &s_eggbirdpause);\r
+\r
+               GetNewObj(true);\r
+               new->obclass = inertobj;\r
+               new->active = ac_removable;\r
+               new->x = ob->x;\r
+               new->y = ob->y;\r
+               new->xspeed = -28;\r
+               new->yspeed = -40;\r
+               NewState(new, &s_eggchip1);\r
+\r
+               GetNewObj(true);\r
+               new->obclass = inertobj;\r
+               new->active = ac_removable;\r
+               new->x = ob->x;\r
+               new->y = ob->y;\r
+               new->xspeed = 28;\r
+               new->yspeed = -40;\r
+               NewState(new, &s_eggchip2);\r
+\r
+               GetNewObj(true);\r
+               new->obclass = inertobj;\r
+               new->active = ac_removable;\r
+               new->x = ob->x;\r
+               new->y = ob->y;\r
+               new->xspeed = 0;\r
+               new->yspeed = -56;\r
+               NewState(new, &s_eggchip3);\r
+       }\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= T_Eggbird\r
+=\r
+===========================\r
+*/\r
+\r
+void T_Eggbird(objtype *ob)\r
+{\r
+       if (ob->x < player->x)\r
+       {\r
+               ob->xdir = 1;\r
+       }\r
+       else\r
+       {\r
+               ob->xdir = -1;\r
+       }\r
+       if (ob->bottom >= player->bottom + 3*TILEGLOBAL && player->hitnorth\r
+               && StatePositionOk(ob, &s_eggbirdfly1)) // BUG: StatePositionOk() only works for normal clipping, not for full clipping\r
+       {\r
+               // Note: might be a good idea to set xtry to 0 here\r
+               ob->state = &s_eggbirdfly1;\r
+               ob->needtoclip = cl_fullclip;\r
+               ob->yspeed = -8;\r
+               ob->temp1 = 0;\r
+       }\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= T_EggbirdFly\r
+=\r
+===========================\r
+*/\r
+\r
+void T_EggbirdFly(objtype *ob)\r
+{\r
+       if (ob->temp1 == 0)\r
+       {\r
+               if (ob->x < player->x)\r
+               {\r
+                       ob->xdir = 1;\r
+               }\r
+               else\r
+               {\r
+                       ob->xdir = -1;\r
+               }\r
+       }\r
+       AccelerateXv(ob, ob->xdir, 16);\r
+       if (ob->y < player->y)\r
+       {\r
+               AccelerateY(ob, 1, 16);\r
+       }\r
+       else\r
+       {\r
+               AccelerateY(ob, -1, 16);\r
+       }\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= C_Eggbird\r
+=\r
+===========================\r
+*/\r
+\r
+void C_Eggbird(objtype *ob, objtype *hit)\r
+{\r
+       if (hit->obclass == keenobj)\r
+       {\r
+               KillKeen();\r
+       }\r
+       else if (hit->obclass == stunshotobj)\r
+       {\r
+               ob->xspeed = 0;\r
+               ob->needtoclip = cl_midclip;\r
+               StunObj(ob, hit, &s_eggbirdstun);\r
+       }\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= C_EggbirdStun\r
+=\r
+===========================\r
+*/\r
+\r
+void C_EggbirdStun(objtype *ob, objtype *hit)\r
+{\r
+       if (hit->obclass == stunshotobj)\r
+       {\r
+               ob->xspeed = 0;\r
+               StunObj(ob, hit, &s_eggbirdstun);\r
+       }\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= R_Eggbird\r
+=\r
+===========================\r
+*/\r
+\r
+void R_Eggbird(objtype *ob)\r
+{\r
+       if (ob->xdir == 1 && ob->hitwest)\r
+       {\r
+               ob->x -= ob->xmove;\r
+               ob->xdir = -1;\r
+               ob->nothink = US_RndT() >> 5;\r
+               ChangeState(ob, ob->state);\r
+       }\r
+       else if (ob->xdir == -1 && ob->hiteast)\r
+       {\r
+               ob->x -= ob->xmove;\r
+               ob->xdir = 1;\r
+               ob->nothink = US_RndT() >> 5;\r
+               ChangeState(ob, ob->state);\r
+       }\r
+       if (!ob->hitnorth)\r
+       {\r
+               ob->yspeed = -16;\r
+               ob->needtoclip = cl_fullclip;\r
+               ChangeState(ob, &s_eggbirdfly1);\r
+       }\r
+       RF_PlaceSprite(&ob->sprite, ob->x, ob->y, ob->shapenum, spritedraw, ob->priority);\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= R_EggbirdDrop\r
+=\r
+===========================\r
+*/\r
+\r
+void R_EggbirdDrop(objtype *ob)        //never actually used\r
+{\r
+       if (ob->hitnorth)\r
+       {\r
+               ChangeState(ob, &s_eggbirdwalk1);\r
+       }\r
+       RF_PlaceSprite(&ob->sprite, ob->x, ob->y, ob->shapenum, spritedraw, ob->priority);\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= R_Chip\r
+=\r
+===========================\r
+*/\r
+\r
+void R_Chip(objtype *ob)\r
+{\r
+       if (ob->hitnorth)\r
+               ob->xspeed = ob->yspeed = 0;\r
+\r
+       RF_PlaceSprite(&ob->sprite, ob->x, ob->y, ob->shapenum, spritedraw, ob->priority);\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= R_Eggbirdfly\r
+=\r
+===========================\r
+*/\r
+\r
+void R_Eggbirdfly(objtype *ob)\r
+{\r
+       statetype *oldstate;\r
+\r
+       if ((ob->hitsouth || ob->hitnorth) && !ob->temp1)\r
+               ob->temp1++;\r
+\r
+       if (ob->hiteast && ob->xspeed < 0 || ob->hitwest && ob->xspeed > 0)\r
+       {\r
+               ob->xspeed = 0;\r
+               ob->xdir = -ob->xdir;\r
+       }\r
+       if (ob->hitnorth == 1 && player->bottom - ob->bottom < 8*PIXGLOBAL)     // BUG? unsigned comparison!\r
+       {\r
+               oldstate = ob->state;\r
+               ob->needtoclip = cl_midclip;\r
+               ChangeState(ob, &s_eggbirdwalk1);\r
+               xtry = 0;\r
+               ytry = 8*PIXGLOBAL;\r
+               PushObj(ob);\r
+               if (!ob->hitnorth)\r
+               {\r
+                       ob->needtoclip = cl_fullclip;\r
+                       ChangeState(ob, oldstate);\r
+               }\r
+               return; // BUG: sprite isn't updated\r
+       }\r
+\r
+       if (!ob->hitsouth && !ob->hitnorth)\r
+               ob->temp1 = 0;\r
+\r
+       RF_PlaceSprite(&ob->sprite, ob->x, ob->y, ob->shapenum, spritedraw, ob->priority);\r
+}\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                 ARACHNUT\r
+\r
+=============================================================================\r
+*/\r
+\r
+statetype s_arach1     = {ARACHNUTWALK1SPR, ARACHNUTWALK4SPR, step, false, true,   6, 128, 0, NULL, C_Arach, R_Walk, &s_arach2};\r
+statetype s_arach2     = {ARACHNUTWALK2SPR, ARACHNUTWALK3SPR, step, false, true,   6, 128, 0, NULL, C_Arach, R_Walk, &s_arach3};\r
+statetype s_arach3     = {ARACHNUTWALK3SPR, ARACHNUTWALK2SPR, step, false, true,   6, 128, 0, NULL, C_Arach, R_Walk, &s_arach4};\r
+statetype s_arach4     = {ARACHNUTWALK4SPR, ARACHNUTWALK1SPR, step, false, true,   6, 128, 0, T_Arach, C_Arach, R_Walk, &s_arach1};\r
+statetype s_arachstun  = {ARACHNUTSTUNSPR,  ARACHNUTSTUNSPR,  step, false, true, 240,   0, 0, NULL, C_ArachStun, R_Draw, &s_arachstun2};\r
+statetype s_arachstun2 = {ARACHNUTWALK1SPR, ARACHNUTWALK1SPR, step, false, true,  20,   0, 0, NULL, C_ArachStun, R_Draw, &s_arachstun3};\r
+statetype s_arachstun3 = {ARACHNUTSTUNSPR,  ARACHNUTSTUNSPR,  step, false, true,  20,   0, 0, NULL, C_ArachStun, R_Draw, &s_arachstun4};\r
+statetype s_arachstun4 = {ARACHNUTWALK1SPR, ARACHNUTWALK1SPR, step, false, true,  20,   0, 0, NULL, C_ArachStun, R_Draw, &s_arachstun5};\r
+statetype s_arachstun5 = {ARACHNUTSTUNSPR,  ARACHNUTSTUNSPR,  step, false, true,  20,   0, 0, NULL, C_ArachStun, R_Draw, &s_arach1};\r
+\r
+/*\r
+===========================\r
+=\r
+= SpawnArachnut\r
+=\r
+===========================\r
+*/\r
+\r
+void SpawnArachnut(Sint16 x, Sint16 y)\r
+{\r
+       GetNewObj(false);\r
+       new->obclass = arachnutobj;\r
+       new->active = ac_yes;\r
+       new->priority = 0;\r
+       new->x = CONVERT_TILE_TO_GLOBAL(x);\r
+       new->y = CONVERT_TILE_TO_GLOBAL(y) - 0x171;\r
+       if (US_RndT() < 0x80)\r
+       {\r
+               new->xdir = 1;\r
+       }\r
+       else\r
+       {\r
+               new->xdir = -1;\r
+       }\r
+       new->ydir = 1;\r
+       NewState(new, &s_arach1);\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= T_Arach\r
+=\r
+===========================\r
+*/\r
+\r
+void T_Arach(objtype *ob)\r
+{\r
+       if (ob->x > player->x)\r
+       {\r
+               ob->xdir = -1;\r
+       }\r
+       else\r
+       {\r
+               ob->xdir = 1;\r
+       }\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= C_Arach\r
+=\r
+===========================\r
+*/\r
+\r
+void C_Arach(objtype *ob, objtype *hit)\r
+{\r
+       if (hit->obclass == stunshotobj)\r
+       {\r
+               StunObj(ob, hit, &s_arachstun);\r
+       }\r
+       else if (hit->obclass == keenobj)\r
+       {\r
+               KillKeen();\r
+       }\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= C_ArachStun\r
+=\r
+===========================\r
+*/\r
+\r
+void C_ArachStun(objtype *ob, objtype *hit)\r
+{\r
+       if (hit->obclass == stunshotobj)\r
+       {\r
+               StunObj(ob, hit, &s_arachstun);\r
+       }\r
+}\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                 SKYPEST\r
+\r
+=============================================================================\r
+*/\r
+\r
+statetype s_pestfly1 = {SKYPESTFLYL1SPR, SKYPESTFLYR1SPR, stepthink, true, false, 5, 0, 0, T_PestFly, C_PestFly, R_Pest, &s_pestfly2};\r
+statetype s_pestfly2 = {SKYPESTFLYL2SPR, SKYPESTFLYR2SPR, stepthink, true, false, 5, 0, 0, T_PestFly, C_PestFly, R_Pest, &s_pestfly1};\r
+statetype s_squashedpest = {SKYPESTSQUASHEDSPR, SKYPESTSQUASHEDSPR, think, false, false, 0, 0, 0, NULL, NULL, R_Draw, &s_squashedpest};\r
+statetype s_pestrest1  = {SKYPESTSIT9SPR, SKYPESTSIT9SPR, step, false, false, 100, 0, 0, NULL, C_Squashable, R_Draw, &s_pestrest2};\r
+statetype s_pestrest2  = {SKYPESTSIT1SPR, SKYPESTSIT1SPR, step, false, false, 10, 0, 0, NULL, C_Squashable, R_Draw, &s_pestrest3};\r
+statetype s_pestrest3  = {SKYPESTSIT2SPR, SKYPESTSIT2SPR, step, false, false, 10, 0, 0, NULL, C_Squashable, R_Draw, &s_pestrest4};\r
+statetype s_pestrest4  = {SKYPESTSIT3SPR, SKYPESTSIT3SPR, step, false, false, 10, 0, 0, NULL, C_Squashable, R_Draw, &s_pestrest5};\r
+statetype s_pestrest5  = {SKYPESTSIT4SPR, SKYPESTSIT4SPR, step, false, false, 10, 0, 0, NULL, C_Squashable, R_Draw, &s_pestrest6};\r
+statetype s_pestrest6  = {SKYPESTSIT3SPR, SKYPESTSIT3SPR, step, false, false, 10, 0, 0, NULL, C_Squashable, R_Draw, &s_pestrest7};\r
+statetype s_pestrest7  = {SKYPESTSIT2SPR, SKYPESTSIT2SPR, step, false, false, 10, 0, 0, NULL, C_Squashable, R_Draw, &s_pestrest8};\r
+statetype s_pestrest8  = {SKYPESTSIT1SPR, SKYPESTSIT1SPR, step, false, false, 10, 0, 0, NULL, C_Squashable, R_Draw, &s_pestrest9};\r
+statetype s_pestrest9  = {SKYPESTSIT9SPR, SKYPESTSIT9SPR, step, false, false, 60, 0, 0, NULL, C_Squashable, R_Draw, &s_pestrest10};\r
+statetype s_pestrest10 = {SKYPESTSIT5SPR, SKYPESTSIT5SPR, step, false, false, 10, 0, 0, NULL, C_Squashable, R_Draw, &s_pestrest11};\r
+statetype s_pestrest11 = {SKYPESTSIT6SPR, SKYPESTSIT6SPR, step, false, false, 10, 0, 0, NULL, C_Squashable, R_Draw, &s_pestrest12};\r
+statetype s_pestrest12 = {SKYPESTSIT7SPR, SKYPESTSIT7SPR, step, false, false, 10, 0, 0, NULL, C_Squashable, R_Draw, &s_pestrest13};\r
+statetype s_pestrest13 = {SKYPESTSIT8SPR, SKYPESTSIT8SPR, step, false, false, 10, 0, 0, NULL, C_Squashable, R_Draw, &s_pestrest14};\r
+statetype s_pestrest14 = {SKYPESTSIT7SPR, SKYPESTSIT7SPR, step, false, false, 10, 0, 0, NULL, C_Squashable, R_Draw, &s_pestrest15};\r
+statetype s_pestrest15 = {SKYPESTSIT6SPR, SKYPESTSIT6SPR, step, false, false, 10, 0, 0, NULL, C_Squashable, R_Draw, &s_pestrest16};\r
+statetype s_pestrest16 = {SKYPESTSIT5SPR, SKYPESTSIT5SPR, step, false, false, 10, 0, 0, NULL, C_Squashable, R_Draw, &s_pestrest17};\r
+statetype s_pestrest17 = {SKYPESTSIT9SPR, SKYPESTSIT9SPR, step, false, false, 100, 0, 0, T_PestRest, C_Squashable, R_Draw, &s_pestfly1};\r
+\r
+/*\r
+===========================\r
+=\r
+= SpawnSkypest\r
+=\r
+===========================\r
+*/\r
+\r
+void SpawnSkypest(Sint16 x, Sint16 y)\r
+{\r
+       GetNewObj(false);\r
+       new->obclass = skypestobj;\r
+       new->active = ac_yes;\r
+       new->priority = 0;\r
+       new->x = CONVERT_TILE_TO_GLOBAL(x);\r
+       new->y = CONVERT_TILE_TO_GLOBAL(y);\r
+       if (US_RndT() < 0x80)\r
+       {\r
+               new->xdir = 1;\r
+       }\r
+       else\r
+       {\r
+               new->xdir = -1;\r
+       }\r
+       if (US_RndT() < 0x80)\r
+       {\r
+               new->ydir = 1;\r
+       }\r
+       else\r
+       {\r
+               new->ydir = -1;\r
+       }\r
+       NewState(new, &s_pestfly1);\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= T_PestFly\r
+=\r
+===========================\r
+*/\r
+\r
+void T_PestFly(objtype *ob)\r
+{\r
+       // BUG: this might be executed twice during the same frame if the\r
+       // object's animation/state changed during that frame, causing the\r
+       // object to move at twice the speed during that frame!\r
+       // To avoid that, return if xtry is not 0 or ytry is not 0.\r
+\r
+       if (US_RndT() < tics)\r
+               ob->xdir = -ob->xdir;\r
+\r
+       if (ob->ydir == -1 && US_RndT() < tics)\r
+               ob->ydir = 1;\r
+\r
+       if (ob->ydir == 1 && US_RndT() < tics*2)\r
+               ob->ydir = -ob->ydir;\r
+\r
+       AccelerateX(ob, ob->xdir, 20);\r
+       AccelerateY(ob, ob->ydir, 20);\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= C_PestFly\r
+=\r
+===========================\r
+*/\r
+\r
+void C_PestFly(objtype *ob, objtype *hit)\r
+{\r
+       if (hit->obclass == keenobj)\r
+               KillKeen();\r
+\r
+       if (hit->obclass == stunshotobj)\r
+       {\r
+               if (hit->xdir == 1)\r
+               {\r
+                       ob->xspeed = 20;\r
+               }\r
+               else if (hit->xdir == -1)\r
+               {\r
+                       ob->xspeed = -20;\r
+               }\r
+               else if (hit->ydir == 1)\r
+               {\r
+                       ob->yspeed = 20;\r
+               }\r
+               else if (hit->ydir == -1)\r
+               {\r
+                       ob->yspeed = -20;\r
+               }\r
+               ExplodeShot(hit);\r
+       }\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= C_Squashable\r
+=\r
+===========================\r
+*/\r
+\r
+void C_Squashable(objtype *ob, objtype *hit)\r
+{\r
+       if (hit->state == &s_keenpogodown || hit->state == &s_keenpogo || hit->state == &s_keenpogo2)\r
+       {\r
+               ChangeState(ob, &s_squashedpest);\r
+               SD_PlaySound(SND_SQUISH);\r
+               ob->obclass = inertobj;\r
+       }\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= T_PestRest\r
+=\r
+===========================\r
+*/\r
+\r
+void T_PestRest(objtype *ob)\r
+{\r
+       ob->ydir = -1;\r
+       ob->yspeed = -16;\r
+       ytry = -144;\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= R_Pest\r
+=\r
+===========================\r
+*/\r
+\r
+void R_Pest(objtype *ob)\r
+{\r
+       if (ob->hitsouth)\r
+       {\r
+               ob->yspeed = 8;\r
+               ob->ydir = 1;\r
+       }\r
+       if (ob->hitnorth && !ob->hiteast && !ob->hitwest)\r
+       {\r
+               ob->y += 8*PIXGLOBAL;\r
+               ChangeState(ob, &s_pestrest1);\r
+       }\r
+       if (ob->hitwest)\r
+       {\r
+               ob->xspeed = 0;\r
+               ob->xdir = -1;\r
+       }\r
+       if (ob->hiteast)\r
+       {\r
+               ob->xspeed = 0;\r
+               ob->xdir = 1;\r
+       }\r
+       RF_PlaceSprite(&ob->sprite, ob->x, ob->y, ob->shapenum, spritedraw, ob->priority);\r
+}\r
diff --git a/16/keen456/KEEN4-6/KEEN4/K4_ACT2.C b/16/keen456/KEEN4-6/KEEN4/K4_ACT2.C
new file mode 100755 (executable)
index 0000000..41b0ad9
--- /dev/null
@@ -0,0 +1,1392 @@
+/* Reconstructed Commander Keen 4-6 Source Code\r
+ * Copyright (C) 2021 K1n9_Duk3\r
+ *\r
+ * This file is loosely based on:\r
+ * Keen Dreams Source Code\r
+ * Copyright (C) 2014 Javier M. Chavez\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License along\r
+ * with this program; if not, write to the Free Software Foundation, Inc.,\r
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+ */\r
+\r
+/*\r
+K4_ACT2.C\r
+=========\r
+\r
+Contains the following actor types (in this order):\r
+\r
+- Wormouth\r
+- Thundercloud & Lightning\r
+- Berkeloid & Fireball\r
+- Inchworm & Foot (in-level)\r
+- Bounder\r
+- Lick\r
+- Platform\r
+- Dropping Platform\r
+\r
+*/\r
+\r
+#include "CK_DEF.H"\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                 WORMOUTH\r
+\r
+=============================================================================\r
+*/\r
+\r
+statetype s_worm      = {WORMOUTHSPR,       WORMOUTHSPR,       slide, true,  true,  4, 16, 0, T_Worm,          NULL,                  R_Walk,    &s_worm};\r
+statetype s_wormpeek1 = {WORMOUTHPEEKL1SPR, WORMOUTHPEEKL1SPR, step,  false, false, 20,  0, 0, NULL,                   C_Worm,   R_Draw,    &s_wormpeek2};\r
+statetype s_wormpeek2 = {WORMOUTHPEEKL2SPR, WORMOUTHPEEKL2SPR, step,  false, false,  8,  0, 0, NULL,                   C_Worm,   R_Draw,    &s_wormpeek3};\r
+statetype s_wormpeek3 = {WORMOUTHPEEKL1SPR, WORMOUTHPEEKL1SPR, step,  false, false, 20,  0, 0, T_WormLookLeft,  C_Worm,   R_Draw,    &s_wormpeek4};\r
+statetype s_wormpeek4 = {WORMOUTHSPR,       WORMOUTHSPR,       step,  false, false,  8,  0, 0, NULL,                   C_Worm,   R_Draw,    &s_wormpeek5};\r
+statetype s_wormpeek5 = {WORMOUTHPEEKR1SPR, WORMOUTHPEEKR1SPR, step,  false, false, 20,  0, 0, NULL,                   C_Worm,   R_Draw,    &s_wormpeek6};\r
+statetype s_wormpeek6 = {WORMOUTHPEEKR2SPR, WORMOUTHPEEKR2SPR, step,  false, false,  8,  0, 0, NULL,                   C_Worm,   R_Draw,    &s_wormpeek7};\r
+statetype s_wormpeek7 = {WORMOUTHPEEKR1SPR, WORMOUTHPEEKR1SPR, step,  false, false, 20,  0, 0, T_WormLookRight, C_Worm,   R_Draw,    &s_wormpeek8};\r
+statetype s_wormpeek8 = {WORMOUTHSPR,       WORMOUTHSPR,       step,  false, false,  8,  0, 0, T_WormLook,      NULL,                  R_Draw,    &s_worm};\r
+statetype s_wormbite1 = {WORMOUTHBITEL1SPR, WORMOUTHBITER1SPR, step,  false, false,  8,  0, 0, NULL,                   C_WormKill, R_Draw,    &s_wormbite2};\r
+statetype s_wormbite2 = {WORMOUTHBITEL2SPR, WORMOUTHBITER2SPR, step,  false, false,  8,  0, 0, NULL,                   C_WormKill, R_Draw,    &s_wormbite3};\r
+statetype s_wormbite3 = {WORMOUTHBITEL3SPR, WORMOUTHBITER3SPR, step,  false, false, 16,  0, 0, NULL,                   C_WormKill, R_Draw,    &s_wormbite4};\r
+statetype s_wormbite4 = {WORMOUTHBITEL2SPR, WORMOUTHBITER2SPR, step,  false, false,  8,  0, 0, NULL,                   C_WormKill, R_Draw,    &s_wormbite5};\r
+statetype s_wormbite5 = {WORMOUTHBITEL1SPR, WORMOUTHBITER1SPR, step,  false, false,  8,  0, 0, NULL,                   C_WormKill, R_Draw,    &s_worm};\r
+statetype s_wormstun  = {WORMOUTHSTUNSPR,   WORMOUTHSTUNSPR,   think, false, false, 10,  0, 0, T_Projectile,       NULL,                  R_Stunned, NULL};\r
+\r
+/*\r
+===========================\r
+=\r
+= SpawnWormMouth\r
+=\r
+===========================\r
+*/\r
+\r
+void SpawnWormMouth(Sint16 x, Sint16 y)\r
+{\r
+       GetNewObj(false);\r
+       new->obclass = wormouthobj;\r
+       new->active = ac_yes;\r
+       new->priority = 0;\r
+       new->x = CONVERT_TILE_TO_GLOBAL(x);\r
+       new->y = CONVERT_TILE_TO_GLOBAL(y) + 0x8F;\r
+       if (US_RndT() < 0x80)\r
+       {\r
+               new->xdir = 1;\r
+       }\r
+       else\r
+       {\r
+               new->xdir = -1;\r
+       }\r
+       new->ydir = 1;\r
+       NewState(new, &s_worm);\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= T_WormLookRight\r
+=\r
+===========================\r
+*/\r
+\r
+void T_WormLookRight(objtype *ob)\r
+{\r
+       if (player->x > ob->x)\r
+       {\r
+               ob->xdir = 1;\r
+               ob->state = &s_worm;\r
+       }\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= T_WormLook\r
+=\r
+===========================\r
+*/\r
+\r
+void T_WormLook(objtype *ob)\r
+{\r
+       if (player->x > ob->x)\r
+       {\r
+               ob->xdir = 1;\r
+       }\r
+       else\r
+       {\r
+               ob->xdir = -1;\r
+       }\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= T_WormLookLeft\r
+=\r
+===========================\r
+*/\r
+\r
+void T_WormLookLeft(objtype *ob)\r
+{\r
+       if (player->x < ob->x)\r
+       {\r
+               ob->xdir = -1;\r
+               ob->state = &s_worm;\r
+       }\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= T_Worm\r
+=\r
+===========================\r
+*/\r
+\r
+void T_Worm(objtype *ob)\r
+{\r
+       Sint16 xdist, ydist;\r
+\r
+       xdist = player->x - ob->x;\r
+       ydist = player->bottom - ob->bottom;\r
+       if ((xdist < -3*TILEGLOBAL || xdist > 3*TILEGLOBAL) && US_RndT() < 6)\r
+       {\r
+               ob->state = &s_wormpeek1;\r
+       }\r
+       else if (ydist >= -TILEGLOBAL && ydist <= TILEGLOBAL)\r
+       {\r
+               if (ob->xdir == 1 && xdist > 8*PIXGLOBAL && xdist < 24*PIXGLOBAL\r
+                       || ob->xdir == -1 && xdist < -8*PIXGLOBAL && xdist > -32*PIXGLOBAL)\r
+               {\r
+                       SD_PlaySound(SND_WORMOUTHATTACK);\r
+                       ob->state = &s_wormbite1;\r
+               }\r
+       }\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= C_Worm\r
+=\r
+===========================\r
+*/\r
+\r
+void C_Worm(objtype *ob, objtype *hit)\r
+{\r
+       if (hit->obclass == stunshotobj)\r
+       {\r
+               StunObj(ob, hit, &s_wormstun);\r
+       }\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= C_WormKill\r
+=\r
+===========================\r
+*/\r
+\r
+void C_WormKill(objtype *ob, objtype *hit)\r
+{\r
+       if (hit->obclass == keenobj)\r
+       {\r
+               if (!(ob->xdir == 1 && ob->x > hit->x || ob->xdir == -1 && ob->x < hit->x))\r
+               {\r
+                       KillKeen();\r
+               }\r
+               return;\r
+       }\r
+       else\r
+       {\r
+               C_Worm(ob, hit);\r
+       }\r
+}\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                 THUNDERCLOUD\r
+\r
+=============================================================================\r
+*/\r
+\r
+statetype s_cloudsleep   = {CLOUDSPR,       CLOUDSPR,       think,     false, false,  20, 0, 0, NULL,         C_CloudSleep, R_Draw,  NULL};\r
+statetype s_cloudwake    = {CLOUDACTIVESPR, CLOUDACTIVESPR, step,      false, false, 100, 0, 0, NULL,         NULL,         R_Cloud, &s_cloud};\r
+statetype s_cloud        = {CLOUDACTIVESPR, CLOUDACTIVESPR, think,     false, false,  20, 0, 0, T_Cloud,      NULL,         R_Cloud, NULL};\r
+statetype s_cloudalign   = {CLOUDACTIVESPR, CLOUDACTIVESPR, think,     false, false,  20, 0, 0, T_CloudAlign, NULL,         R_Cloud, NULL};\r
+statetype s_cloudcharge  = {CLOUDACTIVESPR, CLOUDACTIVESPR, stepthink, false, false,  60, 0, 0, T_Velocity,   NULL,         R_Cloud, &s_cloud};\r
+statetype s_cloudattack1 = {CLOUDCHARGESPR, CLOUDCHARGESPR, step,      false, false,  10, 0, 0, NULL,         NULL,         R_Draw,  &s_cloudattack2};\r
+statetype s_cloudattack2 = {CLOUDACTIVESPR, CLOUDACTIVESPR, step,      false, false,  10, 0, 0, NULL,         NULL,         R_Draw,  &s_cloudattack3};\r
+statetype s_cloudattack3 = {CLOUDCHARGESPR, CLOUDCHARGESPR, step,      false, false,  10, 0, 0, NULL,         NULL,         R_Draw,  &s_cloudattack4};\r
+statetype s_cloudattack4 = {CLOUDACTIVESPR, CLOUDACTIVESPR, step,      false, false,  10, 0, 0, T_CloudShoot, NULL,         R_Draw,  &s_cloudattack5};\r
+statetype s_cloudattack5 = {CLOUDCHARGESPR, CLOUDCHARGESPR, step,      false, false,  10, 0, 0, NULL,         NULL,         R_Draw,  &s_cloudattack6};\r
+statetype s_cloudattack6 = {CLOUDACTIVESPR, CLOUDACTIVESPR, step,      false, false,  10, 0, 0, NULL,         NULL,         R_Draw,  &s_cloudattack7};\r
+statetype s_cloudattack7 = {CLOUDCHARGESPR, CLOUDCHARGESPR, step,      false, false,  10, 0, 0, NULL,         NULL,         R_Draw,  &s_cloudattack8};\r
+statetype s_cloudattack8 = {CLOUDACTIVESPR, CLOUDACTIVESPR, step,      false, false,  10, 0, 0, NULL,         NULL,         R_Draw,  &s_cloudattack9};\r
+statetype s_cloudattack9 = {CLOUDCHARGESPR, CLOUDCHARGESPR, step,      false, false,  48, 0, 0, NULL,         NULL,         R_Draw,  &s_cloudcharge};\r
+\r
+statetype s_bolt1 = {BOLT1SPR, BOLT1SPR, step, false, false, 8, 0, 0, NULL, C_Lethal, R_Draw, &s_bolt2};\r
+statetype s_bolt2 = {BOLT2SPR, BOLT2SPR, step, false, false, 8, 0, 0, NULL, C_Lethal, R_Draw, &s_bolt3};\r
+statetype s_bolt3 = {BOLT1SPR, BOLT1SPR, step, false, false, 8, 0, 0, NULL, C_Lethal, R_Draw, &s_bolt4};\r
+statetype s_bolt4 = {BOLT2SPR, BOLT2SPR, step, false, false, 8, 0, 0, NULL, C_Lethal, R_Draw, &s_bolt5};\r
+statetype s_bolt5 = {BOLT1SPR, BOLT1SPR, step, false, false, 8, 0, 0, NULL, C_Lethal, R_Draw, &s_bolt6};\r
+statetype s_bolt6 = {BOLT2SPR, BOLT2SPR, step, false, false, 8, 0, 0, NULL, C_Lethal, R_Draw, NULL};\r
+\r
+/*\r
+===========================\r
+=\r
+= SpawnCloudster\r
+=\r
+===========================\r
+*/\r
+\r
+void SpawnCloudster(Sint16 x, Sint16 y)\r
+{\r
+       GetNewObj(false);\r
+       new->obclass = thundercloudobj;\r
+       new->active = ac_yes;\r
+       new->priority = 2;\r
+       new->x = CONVERT_TILE_TO_GLOBAL(x);\r
+       new->y = CONVERT_TILE_TO_GLOBAL(y);\r
+       new->ydir = new->xdir = 1;\r
+       NewState(new, &s_cloudsleep);\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= T_Cloud\r
+=\r
+===========================\r
+*/\r
+\r
+void T_Cloud(objtype *ob)\r
+{\r
+       if (US_RndT() < tics)\r
+               ob->xdir = ob->x > player->x? -1 : 1;\r
+\r
+       AccelerateX(ob, ob->xdir, 10);\r
+       if (player->bottom < ob->bottom || (Sint16)(player->top - ob->bottom) > 64*PIXGLOBAL)\r
+               return;\r
+       if (ob->left < player->right && ob->right > player->left)\r
+       {\r
+               ob->state = &s_cloudalign;\r
+       }\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= T_CloudAlign\r
+=\r
+===========================\r
+*/\r
+\r
+void T_CloudAlign(objtype *ob)\r
+{\r
+       AccelerateX(ob, ob->xdir, 10);\r
+       if (xtry < 0 && (Sint16)((ob->x & 0x7F) + xtry) <= 0)\r
+       {\r
+               xtry = -(ob->x & 0x7F);\r
+               ob->state = &s_cloudattack1;\r
+       }\r
+       if (xtry > 0 && (ob->x & 0x7F) + xtry >= 8*PIXGLOBAL)\r
+       {\r
+               xtry = 8*PIXGLOBAL - (ob->x & 0x7F);\r
+               ob->state = &s_cloudattack1;\r
+       }\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= R_Cloud\r
+=\r
+===========================\r
+*/\r
+\r
+void R_Cloud(objtype *ob)\r
+{\r
+       if (ob->hitwest)\r
+       {\r
+               ob->xspeed = 0;\r
+               ob->xdir = -1;\r
+       }\r
+       else if (ob->hiteast)\r
+       {\r
+               ob->xspeed = 0;\r
+               ob->xdir = 1;\r
+       }\r
+       RF_PlaceSprite(&ob->sprite, ob->x, ob->y, ob->shapenum, spritedraw, ob->priority);\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= T_CloudShoot\r
+=\r
+===========================\r
+*/\r
+\r
+void T_CloudShoot(objtype *ob)\r
+{\r
+       GetNewObj(true);\r
+       new->obclass = lightningobj;\r
+       new->active = ac_removable;\r
+       new->needtoclip = cl_noclip;\r
+       new->x = ob->x + TILEGLOBAL;\r
+       new->y = ob->y + TILEGLOBAL;\r
+       NewState(new, &s_bolt1);\r
+       SD_PlaySound(SND_THUNDER);\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= C_CloudSleep\r
+=\r
+===========================\r
+*/\r
+\r
+void C_CloudSleep(objtype *ob, objtype *hit)\r
+{\r
+       if (hit->obclass == keenobj)\r
+       {\r
+               ChangeState(ob, &s_cloudwake);\r
+       }\r
+}\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                 BERKELOID\r
+\r
+temp1 = float offset, in global units (added to ob->y when drawing the sprite)\r
+temp2 = float speed, in global units per tic (added to temp1 for every tic)\r
+\r
+=============================================================================\r
+*/\r
+\r
+statetype s_berkefloat1  = {BERKEWALKL1SPR,  BERKEWALKR1SPR,  slide, false, true,  6, 8, 0, BerkeThink, C_Berke, BerkeWalkReact, &s_berkefloat2};\r
+statetype s_berkefloat2  = {BERKEWALKL2SPR,  BERKEWALKR2SPR,  slide, false, true,  6, 8, 0, BerkeThink, C_Berke, BerkeWalkReact, &s_berkefloat3};\r
+statetype s_berkefloat3  = {BERKEWALKL3SPR,  BERKEWALKR3SPR,  slide, false, true,  6, 8, 0, BerkeThink, C_Berke, BerkeWalkReact, &s_berkefloat4};\r
+statetype s_berkefloat4  = {BERKEWALKL4SPR,  BERKEWALKR4SPR,  slide, false, true,  6, 8, 0, BerkeThink, C_Berke, BerkeWalkReact, &s_berkefloat1};\r
+statetype s_berkethrow1  = {BERKETHROWL1SPR, BERKETHROWR1SPR, step,  false, false, 6, 0, 0, SetReactThink, C_Berke, BerkeDrawReact, &s_berkethrow2};\r
+statetype s_berkethrow2  = {BERKETHROWL1SPR, BERKETHROWR1SPR, step,  false, false, 6, 0, 0, SetReactThink, C_Berke, BerkeDrawReact, &s_berkethrow3};\r
+statetype s_berkethrow3  = {BERKETHROWL1SPR, BERKETHROWR1SPR, step,  false, false, 6, 0, 0, SetReactThink, C_Berke, BerkeDrawReact, &s_berkethrow4};\r
+statetype s_berkethrow4  = {BERKETHROWL1SPR, BERKETHROWR1SPR, step,  false, false, 6, 0, 0, SetReactThink, C_Berke, BerkeDrawReact, &s_berkethrow5};\r
+statetype s_berkethrow5  = {BERKETHROWL1SPR, BERKETHROWR1SPR, step,  false, false, 6, 0, 0, SetReactThink, C_Berke, BerkeDrawReact, &s_berkethrow6};\r
+statetype s_berkethrow6  = {BERKETHROWL1SPR, BERKETHROWR1SPR, step,  false, false, 6, 0, 0, BerkeThrowThink, C_Berke, BerkeDrawReact, &s_berkethrow7};\r
+statetype s_berkethrow7  = {BERKETHROWL2SPR, BERKETHROWR2SPR, step,  false, false, 6, 0, 0, SetReactThink, C_Berke, BerkeDrawReact, &s_berkethrow8};\r
+statetype s_berkethrow8  = {BERKETHROWL2SPR, BERKETHROWR2SPR, step,  false, false, 6, 0, 0, SetReactThink, C_Berke, BerkeDrawReact, &s_berkethrow9};\r
+statetype s_berkethrow9  = {BERKETHROWL2SPR, BERKETHROWR2SPR, step,  false, false, 6, 0, 0, SetReactThink, C_Berke, BerkeDrawReact, &s_berkethrow10};\r
+statetype s_berkethrow10 = {BERKETHROWL2SPR, BERKETHROWR2SPR, step,  false, false, 6, 0, 0, SetReactThink, C_Berke, BerkeDrawReact, &s_berkethrow11};\r
+statetype s_berkethrow11 = {BERKETHROWL2SPR, BERKETHROWR2SPR, step,  false, false, 6, 0, 0, SetReactThink, C_Berke, BerkeDrawReact, &s_berkethrow12};\r
+statetype s_berkethrow12 = {BERKETHROWL2SPR, BERKETHROWR2SPR, step,  false, false, 6, 0, 0, BerkeThrowDone, C_Berke, BerkeDrawReact, &s_berkefloat1};\r
+\r
+statetype s_fire1     = {FIREBALL1SPR, FIREBALL1SPR, stepthink, false, false, 6, 0, 0, T_WeakProjectile, C_Lethal, FireReact, &s_fire2};\r
+statetype s_fire2     = {FIREBALL2SPR, FIREBALL2SPR, stepthink, false, false, 6, 0, 0, T_WeakProjectile, C_Lethal, FireReact, &s_fire1};\r
+statetype s_fireland1 = {FIREBALL1SPR, FIREBALL1SPR, step, false, false,  6, 0, 0, NULL, C_Berke, R_Draw, &s_fireland2};\r
+statetype s_fireland2 = {FIREBALL3SPR, FIREBALL3SPR, step, false, false, 12, 0, 0, NULL, C_Berke, R_Draw, &s_fireland3};\r
+statetype s_fireland3 = {FIREBALL4SPR, FIREBALL4SPR, step, false, false, 12, 0, 0, NULL, C_Berke, R_Draw, &s_fireland4};\r
+statetype s_fireland4 = {FIREBALL3SPR, FIREBALL3SPR, step, false, false, 12, 0, 0, NULL, C_Berke, R_Draw, &s_fireland5};\r
+statetype s_fireland5 = {FIREBALL4SPR, FIREBALL4SPR, step, false, false, 12, 0, 0, NULL, C_Berke, R_Draw, &s_fireland6};\r
+statetype s_fireland6 = {FIREBALL1SPR, FIREBALL1SPR, step, false, false, 12, 0, 0, NULL, C_Berke, R_Draw, &s_fireland7};\r
+statetype s_fireland7 = {FIREBALL2SPR, FIREBALL2SPR, step, false, false, 12, 0, 0, NULL, C_Berke, R_Draw, &s_fireland8};\r
+statetype s_fireland8 = {FIREBALL1SPR, FIREBALL1SPR, step, false, false, 12, 0, 0, NULL, C_Berke, R_Draw, &s_fireland9};\r
+statetype s_fireland9 = {FIREBALL2SPR, FIREBALL2SPR, step, false, false, 12, 0, 0, NULL, C_Berke, R_Draw, NULL};\r
+\r
+/*\r
+===========================\r
+=\r
+= SpawnBerkeloid\r
+=\r
+===========================\r
+*/\r
+\r
+void SpawnBerkeloid(Sint16 x, Sint16 y)\r
+{\r
+       GetNewObj(false);\r
+       new->obclass = berkeloidobj;\r
+       new->active = ac_yes;\r
+       new->priority = 2;\r
+       new->x = CONVERT_TILE_TO_GLOBAL(x);\r
+       new->y = CONVERT_TILE_TO_GLOBAL(y) + -2*TILEGLOBAL;\r
+       if (US_RndT() < 0x80)\r
+       {\r
+               new->xdir = 1;\r
+       }\r
+       else\r
+       {\r
+               new->xdir = -1;\r
+       }\r
+       new->ydir = 1;\r
+       new->temp2 = 8;\r
+       NewState(new, &s_berkefloat1);\r
+}\r
+\r
+\r
+/*\r
+===========================\r
+=\r
+= BerkeThink\r
+=\r
+===========================\r
+*/\r
+\r
+void BerkeThink(objtype *ob)\r
+{\r
+       Sint16 xdist, ydist;\r
+\r
+       if (US_RndT() < 0x20)\r
+               ob->xdir = player->x < ob->x? -1 : 1;\r
+\r
+       if (US_RndT() < 8)\r
+       {\r
+               // BUG: might be about to move off a ledge, causing it to get stuck\r
+               // after throwing (the throw states don't use BerkeWalkReact)!\r
+               // To avoid that, set xtry to 0 here.\r
+\r
+               ob->state = &s_berkethrow1;\r
+               // BUG? this doesn't play the attack sound\r
+       }\r
+       else if (US_RndT() <= 0x40)\r
+       {\r
+               xdist = player->x - ob->x;\r
+               ydist = player->y - ob->y;\r
+               if (ydist >= -TILEGLOBAL && ydist <= TILEGLOBAL\r
+                       && (ob->xdir == 1 && xdist > 0 || ob->xdir == -1 && xdist < 0))\r
+               {\r
+                       // BUG: might be about to move off a ledge, causing it to get stuck\r
+                       // after throwing (the throw states don't use BerkeWalkReact)!\r
+                       // To avoid that, set xtry to 0 here.\r
+\r
+                       ob->state = &s_berkethrow1;\r
+                       SD_PlaySound(SND_BERKELOIDATTACK);\r
+               }\r
+       }\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= BerkeThrowThink\r
+=\r
+===========================\r
+*/\r
+\r
+void BerkeThrowThink(objtype *ob)\r
+{\r
+       GetNewObj(true);\r
+       new->active = ac_removable;\r
+       new->obclass = berkeloidobj;\r
+       new->y = ob->y + 8*PIXGLOBAL;\r
+       new->yspeed = -8;\r
+       if (ob->xdir == 1)\r
+       {\r
+               new->xspeed = 48;\r
+               new->x = ob->x + 32*PIXGLOBAL;\r
+               new->xdir = 1;\r
+       }\r
+       else\r
+       {\r
+               new->xspeed = -48;\r
+               new->x = ob->x - 16*PIXGLOBAL;\r
+               new->xdir = -1;\r
+       }\r
+       NewState(new, &s_fire1);\r
+       ob->needtoreact = true;\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= BerkeThrowDone\r
+=\r
+===========================\r
+*/\r
+\r
+void BerkeThrowDone(objtype *ob)\r
+{\r
+       ob->nothink = 4;\r
+       ob->needtoreact = true;\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= C_Berke\r
+=\r
+===========================\r
+*/\r
+\r
+void C_Berke(objtype *ob, objtype *hit)\r
+{\r
+       ob++;                   // shut up compiler\r
+       if (hit->obclass == stunshotobj)\r
+       {\r
+               ExplodeShot(hit);\r
+       }\r
+       else if (hit->obclass == keenobj)\r
+       {\r
+               KillKeen();\r
+       }\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= FireReact\r
+=\r
+===========================\r
+*/\r
+\r
+void FireReact(objtype *ob)\r
+{\r
+       if (ob->hiteast || ob->hitwest)\r
+               ob->xspeed = 0;\r
+\r
+       if (ob->hitnorth)\r
+       {\r
+               SD_PlaySound(SND_FIREBALLLAND);\r
+               ChangeState(ob, &s_fireland1);\r
+       }\r
+       RF_PlaceSprite(&ob->sprite, ob->x, ob->y, ob->shapenum, spritedraw, ob->priority);\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= BerkeDrawReact\r
+=\r
+===========================\r
+*/\r
+\r
+void BerkeDrawReact(objtype *ob)\r
+{\r
+       //float up & down:\r
+       ob->temp1 = ob->temp1 + ob->temp2 * tics;\r
+       if (ob->temp1 > 0)\r
+       {\r
+               ob->temp1 = 0;\r
+               ob->temp2 = -8;\r
+       }\r
+       else if (ob->temp1 < -TILEGLOBAL)\r
+       {\r
+               ob->temp1 = -TILEGLOBAL;\r
+               ob->temp2 = 8;\r
+       }\r
+\r
+       RF_PlaceSprite(&ob->sprite, ob->x, ob->y+ob->temp1, ob->shapenum, spritedraw, 0);\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= BerkeWalkReact\r
+=\r
+===========================\r
+*/\r
+\r
+void BerkeWalkReact(objtype *ob)\r
+{\r
+       if (ob->xdir == 1 && ob->hitwest)\r
+       {\r
+               ob->x -= ob->xmove;\r
+               ob->xdir = -1;\r
+               ob->nothink = US_RndT() >> 5;\r
+               ChangeState(ob, ob->state);\r
+       }\r
+       else if (ob->xdir == -1 && ob->hiteast)\r
+       {\r
+               ob->x -= ob->xmove;\r
+               ob->xdir = 1;\r
+               ob->nothink = US_RndT() >> 5;\r
+               ChangeState(ob, ob->state);\r
+       }\r
+       else if (!ob->hitnorth)\r
+       {\r
+               ob->x -= ob->xmove*2;\r
+               ob->xdir = -ob->xdir;\r
+               ob->nothink = US_RndT() >> 5;\r
+               ChangeState(ob, ob->state);\r
+       }\r
+       BerkeDrawReact(ob);\r
+}\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                 INCHWORM & FOOT\r
+\r
+temp1 = last lasttimecount (for resetting the touch counter after each frame)\r
+temp2 = touch counter\r
+\r
+=============================================================================\r
+*/\r
+\r
+statetype s_footsmoke1 = {SMOKE1SPR, SMOKE1SPR, step, false, false, 12, 0, 0, NULL, NULL, R_Draw, &s_footsmoke2};\r
+statetype s_footsmoke2 = {SMOKE2SPR, SMOKE2SPR, step, false, false, 12, 0, 0, NULL, NULL, R_Draw, &s_footsmoke3};\r
+statetype s_footsmoke3 = {SMOKE3SPR, SMOKE3SPR, step, false, false, 12, 0, 0, NULL, NULL, R_Draw, &s_footsmoke4};\r
+statetype s_footsmoke4 = {SMOKE4SPR, SMOKE4SPR, step, false, false, 12, 0, 0, NULL, NULL, R_Draw, NULL};\r
+statetype s_inch1      = {INCHWORML1SPR, INCHWORMR1SPR, step, false, true, 30, 128, 0, InchThink, InchContact, R_Walk, &s_inch2};\r
+statetype s_inch2      = {INCHWORML2SPR, INCHWORMR2SPR, step, false, true, 30, 128, 0, InchThink, InchContact, R_Walk, &s_inch1};\r
+statetype s_footchange = { -1,  -1, step, false, false, 48, 0, 0, NULL, NULL, R_Draw, &s_footwait};    //never used!\r
+statetype s_footwait   = {FOOTSPR, FOOTSPR, think, false, false, 30000, 0, 0, NULL, FootContact, R_Draw, NULL};\r
+\r
+/*\r
+===========================\r
+=\r
+= SpawnInchworm\r
+=\r
+===========================\r
+*/\r
+\r
+void SpawnInchworm(Sint16 x, Sint16 y)\r
+{\r
+       GetNewObj(false);\r
+       new->obclass = inchwormobj;\r
+       new->active = ac_yes;\r
+       new->priority = 2;\r
+       new->x = CONVERT_TILE_TO_GLOBAL(x);\r
+       new->y = CONVERT_TILE_TO_GLOBAL(y);\r
+       if (US_RndT() < 0x80)\r
+       {\r
+               new->xdir = 1;\r
+       }\r
+       else\r
+       {\r
+               new->xdir = -1;\r
+       }\r
+       new->ydir = 1;\r
+       NewState(new, &s_inch1);\r
+       new->ticcount = US_RndT() / 32;\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= SpawnFoot\r
+=\r
+===========================\r
+*/\r
+\r
+void SpawnFoot(Sint16 x, Sint16 y)\r
+{\r
+       GetNewObj(false);\r
+       new->obclass = footobj;\r
+       new->active = ac_yes;\r
+       new->priority = 0;\r
+       new->x = CONVERT_TILE_TO_GLOBAL(x);\r
+       new->y = CONVERT_TILE_TO_GLOBAL(y-3);\r
+       NewState(new, &s_footwait);\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= InchThink\r
+=\r
+===========================\r
+*/\r
+\r
+void InchThink(objtype *ob)\r
+{\r
+       if (ob->x > player->x)\r
+       {\r
+               ob->xdir = -1;\r
+       }\r
+       else\r
+       {\r
+               ob->xdir = 1;\r
+       }\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= InchContact\r
+=\r
+===========================\r
+*/\r
+\r
+void InchContact(objtype *ob, objtype *hit)\r
+{\r
+       objtype *ob2;\r
+\r
+       if (hit->obclass != inchwormobj)\r
+               return;\r
+\r
+       if (ob->temp1 != (Sint16)lasttimecount)\r
+       {\r
+               ob->temp1 = (Sint16)lasttimecount;\r
+               ob->temp2 = 0;\r
+       }\r
+\r
+       if (++ob->temp2 != 11)  //11 instead of 12 since the object can't contact itself\r
+               return;\r
+\r
+       //change current inchworm into a foot:\r
+       SD_PlaySound(SND_MAKEFOOT);\r
+       ob->y -= 5*TILEGLOBAL;\r
+       ob->obclass = footobj;\r
+       ChangeState(ob, &s_footwait);\r
+\r
+       //Note: It would make more sense to remove the remaining inchworm BEFORE\r
+       //spawning the smoke, just in case there are not enough free spots in the\r
+       //objarray to spawn the smoke. The game won't crash either way, though.\r
+\r
+       //spawn smoke:\r
+       GetNewObj(true);\r
+       new->x = ob->x -  8*PIXGLOBAL;\r
+       new->y = ob->y + 16*PIXGLOBAL;\r
+       new->priority = 3;\r
+       NewState(new, &s_footsmoke1);\r
+\r
+       GetNewObj(true);\r
+       new->x = ob->x + 16*PIXGLOBAL;\r
+       new->y = ob->y + 24*PIXGLOBAL;\r
+       new->priority = 3;\r
+       NewState(new, &s_footsmoke1);\r
+\r
+       GetNewObj(true);\r
+       new->x = ob->x + 40*PIXGLOBAL;\r
+       new->y = ob->y + 16*PIXGLOBAL;\r
+       new->priority = 3;\r
+       NewState(new, &s_footsmoke1);\r
+\r
+       GetNewObj(true);\r
+       new->x = ob->x;\r
+       new->y = ob->y - 8*PIXGLOBAL;\r
+       new->priority = 3;\r
+       NewState(new, &s_footsmoke1);\r
+\r
+       //remove ALL inchworm from the level:\r
+       for (ob2 = player->next; ob2; ob2=ob2->next)\r
+       {\r
+               if (ob2->obclass == inchwormobj)\r
+                       RemoveObj(ob2);\r
+       }\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= FootContact\r
+=\r
+===========================\r
+*/\r
+\r
+void FootContact(objtype *ob, objtype *hit)    //completely useless\r
+{\r
+       ob++;                   // shut up compiler\r
+       hit++;                  // shut up compiler\r
+}\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                 BOUNDER\r
+\r
+temp1 = if non-zero, pick a new (random) direction when hitting the ground\r
+        Makes the Bounder jump straight up at least once after having jumped\r
+                 left/right (unless Keen is riding the Bounder)\r
+temp2 = jump counter to make the Bounder jump straight up at least twice\r
+        after Keen has fallen/jumped off\r
+\r
+=============================================================================\r
+*/\r
+\r
+statetype s_bounderup1   = {BOUNDERC1SPR,   BOUNDERC1SPR,   stepthink, false, false, 20, 0, 0, T_Projectile, C_Bounder, R_Bounder, &s_bounderup2};\r
+statetype s_bounderup2   = {BOUNDERC2SPR,   BOUNDERC2SPR,   stepthink, false, false, 20, 0, 0, T_Projectile, C_Bounder, R_Bounder, &s_bounderup1};\r
+statetype s_bounderside1 = {BOUNDERL1SPR,   BOUNDERR1SPR,   stepthink, false, false, 20, 0, 0, T_Projectile, C_Bounder, R_Bounder, &s_bounderside2};\r
+statetype s_bounderside2 = {BOUNDERL2SPR,   BOUNDERR2SPR,   stepthink, false, false, 20, 0, 0, T_Projectile, C_Bounder, R_Bounder, &s_bounderside1};\r
+statetype s_bounderstun  = {BOUNDERC1SPR,   BOUNDERC1SPR,   think,     false, false,  0, 0, 0, T_Projectile, NULL, R_Stunned, &s_bounderstun2};\r
+statetype s_bounderstun2 = {BOUNDERSTUNSPR, BOUNDERSTUNSPR, think,     false, false,  0, 0, 0, T_Projectile, NULL, R_Stunned, NULL};\r
+\r
+/*\r
+===========================\r
+=\r
+= SpawnBounder\r
+=\r
+===========================\r
+*/\r
+\r
+void SpawnBounder(Sint16 x, Sint16 y)\r
+{\r
+       GetNewObj(false);\r
+       new->obclass = bounderobj;\r
+       new->active = ac_yes;\r
+       new->priority = 0;\r
+       new->x = CONVERT_TILE_TO_GLOBAL(x);\r
+       new->y = CONVERT_TILE_TO_GLOBAL(y) + -8*PIXGLOBAL;\r
+       new->ydir = 1;\r
+       new->xdir = 0;\r
+       NewState(new, &s_bounderup1);\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= C_Bounder\r
+=\r
+===========================\r
+*/\r
+\r
+void C_Bounder(objtype *ob, objtype *hit)\r
+{\r
+       if (hit->obclass == stunshotobj)\r
+       {\r
+               //basically StunObj(), but in different order:\r
+               ob->temp1 = 0;\r
+               ob->temp2 = 0;\r
+               ob->temp3 = 0;\r
+               ob->temp4 = ob->obclass;\r
+               ExplodeShot(hit);\r
+               ChangeState(ob, &s_bounderstun);\r
+               ob->obclass = stunnedobj;\r
+\r
+               ob->yspeed -= 32;\r
+       }\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= R_Bounder\r
+=\r
+===========================\r
+*/\r
+\r
+void R_Bounder(objtype *ob)\r
+{\r
+       Sint16 randnum;\r
+\r
+       if (ob->hitsouth)\r
+               ob->yspeed = 0;\r
+\r
+       if (ob->hitnorth)\r
+       {\r
+               ob->temp2++;\r
+               if (OnScreen(ob))\r
+                       SD_PlaySound(SND_BOUNCE2);\r
+\r
+               ob->yspeed = -50;\r
+               if (gamestate.riding == ob)\r
+               {\r
+                       ob->temp2 = 0;\r
+                       if (player->left < ob->left-4*PIXGLOBAL)\r
+                       {\r
+                               ob->xdir = -1;\r
+                       }\r
+                       else if (player->right > ob->right+4*PIXGLOBAL)\r
+                       {\r
+                               ob->xdir = 1;\r
+                       }\r
+                       else\r
+                       {\r
+                               ob->xdir = 0;\r
+                       }\r
+                       ob->xspeed = ob->xdir * 24;\r
+               }\r
+               else if (ob->temp2 <= 2 || ob->temp1 == 0)\r
+               {\r
+                       ob->temp1 = 1;\r
+                       ob->xdir = ob->xspeed = 0;\r
+                       ChangeState(ob, &s_bounderup1);\r
+               }\r
+               else\r
+               {\r
+                       ob->temp1 = 0;\r
+                       randnum = US_RndT();\r
+                       if (randnum < 100)\r
+                       {\r
+                               ob->xdir = -1;\r
+                       }\r
+                       else if (randnum < 200)\r
+                       {\r
+                               ob->xdir = 1;\r
+                       }\r
+                       else\r
+                       {\r
+                               ob->xdir = 0;\r
+                       }\r
+                       ob->xspeed = ob->xdir * 24;\r
+               }\r
+\r
+               if (ob->xdir)\r
+               {\r
+                       ChangeState(ob, &s_bounderside1);\r
+               }\r
+               else\r
+               {\r
+                       ChangeState(ob, &s_bounderup1);\r
+               }\r
+       }\r
+\r
+       if (ob->hiteast || ob->hitwest)\r
+       {\r
+               ob->xdir = -ob->xdir;\r
+               ob->xspeed = -ob->xspeed;\r
+       }\r
+\r
+       RF_PlaceSprite(&ob->sprite, ob->x, ob->y, ob->shapenum, spritedraw, ob->priority);\r
+}\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                 LICK\r
+\r
+=============================================================================\r
+*/\r
+\r
+statetype s_lick1     = {LICKMOVEL1SPR,   LICKMOVER1SPR,   step,  false, false, 10, 0, 0, LickJumpThink, LickContact, R_Draw, &s_lick2};\r
+statetype s_lick2     = {LICKMOVEL2SPR,   LICKMOVER2SPR,   think, false, false,  0, 0, 0, T_Projectile, LickContact, LickAirReact, &s_lick3};\r
+statetype s_lick3     = {LICKMOVEL3SPR,   LICKMOVER3SPR,   think, false, false,  0, 0, 0, T_Projectile, LickContact, LickAirReact, NULL};\r
+statetype s_lick4     = {LICKMOVEL4SPR,   LICKMOVER4SPR,   step,  false, false, 10, 0, 0, NULL, LickContact, R_Draw, &s_lick1};\r
+statetype s_licklick1 = {LICKATTACKL1SPR, LICKATTACKR1SPR, step,  true,  false,  4, 0, 0, NULL, LickKillContact, R_Draw, &s_licklick2};\r
+statetype s_licklick2 = {LICKATTACKL2SPR, LICKATTACKR2SPR, step,  true,  false,  4, 0, 0, NULL, LickKillContact, R_Draw, &s_licklick3};\r
+statetype s_licklick3 = {LICKATTACKL3SPR, LICKATTACKR3SPR, step,  true,  false,  4, 0, 0, NULL, LickKillContact, R_Draw, &s_licklick4};\r
+statetype s_licklick4 = {LICKATTACKL2SPR, LICKATTACKR2SPR, step,  true,  false,  4, 0, 0, NULL, LickKillContact, R_Draw, &s_licklick5};\r
+statetype s_licklick5 = {LICKATTACKL1SPR, LICKATTACKR1SPR, step,  true,  false,  4, 0, 0, NULL, LickKillContact, R_Draw, &s_licklick6};\r
+statetype s_licklick6 = {LICKATTACKL2SPR, LICKATTACKR2SPR, step,  true,  false,  4, 0, 0, NULL, LickKillContact, R_Draw, &s_licklick7};\r
+statetype s_licklick7 = {LICKATTACKL3SPR, LICKATTACKR3SPR, step,  true,  false,  4, 0, 0, NULL, LickKillContact, R_Draw, &s_licklick8};\r
+statetype s_licklick8 = {LICKATTACKL2SPR, LICKATTACKR2SPR, step,  true,  false,  4, 0, 0, NULL, LickKillContact, R_Draw, &s_lick3};\r
+statetype s_lickstun  = {LICKSTUNSPR,     LICKSTUNSPR,     think, false, false,  0, 0, 0, T_Projectile, NULL, R_Stunned, &s_lickstun2};\r
+statetype s_lickstun2 = {LICKSTUNSPR,     LICKSTUNSPR,     think, false, false,  0, 0, 0, T_Projectile, NULL, R_Stunned, NULL};\r
+\r
+/*\r
+===========================\r
+=\r
+= SpawnLick\r
+=\r
+===========================\r
+*/\r
+\r
+void SpawnLick(Sint16 x, Sint16 y)\r
+{\r
+       GetNewObj(false);\r
+       new->obclass = lickobj;\r
+       new->active = ac_yes;\r
+       new->priority = 2;\r
+       new->x = CONVERT_TILE_TO_GLOBAL(x);\r
+       new->y = CONVERT_TILE_TO_GLOBAL(y);\r
+       if (US_RndT() < 0x80)\r
+       {\r
+               new->xdir = 1;\r
+       }\r
+       else\r
+       {\r
+               new->xdir = -1;\r
+       }\r
+       new->ydir = 1;\r
+       new->nothink = US_RndT() / 64;\r
+       NewState(new, &s_lick3);\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= LickJumpThink\r
+=\r
+===========================\r
+*/\r
+\r
+void LickJumpThink(objtype *ob)\r
+{\r
+       Sint16 xdist, ydist;\r
+\r
+       if (ob->x > player->x)\r
+       {\r
+               ob->xdir = -1;\r
+       }\r
+       else\r
+       {\r
+               ob->xdir = 1;\r
+       }\r
+\r
+       xdist = player->x - ob->x;\r
+       ydist = player->y - ob->y;\r
+\r
+       if (ydist >= -TILEGLOBAL && ydist <= TILEGLOBAL && \r
+               ( ob->xdir == 1 && xdist > -2*PIXGLOBAL && xdist < 24*PIXGLOBAL\r
+                || ob->xdir == -1 && xdist < 2*PIXGLOBAL && xdist > -32*PIXGLOBAL ) )\r
+       {\r
+               SD_PlaySound(SND_LICKATTACK);\r
+               ob->state = &s_licklick1;\r
+       }\r
+       else if (abs(xdist) > 3*TILEGLOBAL)\r
+       {\r
+               ob->xspeed = ob->xdir * 32;\r
+               ob->yspeed = -32;\r
+       }\r
+       else\r
+       {\r
+               ob->xspeed = (ob->xdir * 32)/2;\r
+               ob->yspeed = -16;\r
+       }\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= LickContact\r
+=\r
+===========================\r
+*/\r
+\r
+void LickContact(objtype *ob, objtype *hit)\r
+{\r
+       if (hit->obclass == stunshotobj)\r
+       {\r
+               StunObj(ob, hit, &s_lickstun);\r
+               ob->yspeed -= 16;\r
+       }\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= LickKillContact\r
+=\r
+===========================\r
+*/\r
+\r
+void LickKillContact(objtype *ob, objtype *hit)\r
+{\r
+       if (hit->obclass == keenobj)\r
+       {\r
+               if (ob->xdir == 1 && player->x > ob->x \r
+                       || ob->xdir == -1 && player->x < ob->x)\r
+               {\r
+                       KillKeen();\r
+               }\r
+       }\r
+       else\r
+       {\r
+               LickContact(ob, hit);\r
+       }\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= LickAirReact\r
+=\r
+===========================\r
+*/\r
+\r
+void LickAirReact(objtype *ob)\r
+{\r
+       if (ob->hitnorth)\r
+               ChangeState(ob, &s_lick4);\r
+\r
+       RF_PlaceSprite(&ob->sprite, ob->x, ob->y, ob->shapenum, spritedraw, ob->priority);\r
+}\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                 PLATFORM\r
+\r
+temp2 = additional sprite pointer for thruster sprites\r
+temp3 = additional sprite pointer for thruster sprites\r
+\r
+=============================================================================\r
+*/\r
+\r
+statetype s_platform = {PLATFORMSPR, PLATFORMSPR, think, false, false, 0, 0, 0, T_Platform, NULL, R_Platform, NULL};\r
+\r
+/*\r
+===========================\r
+=\r
+= SpawnPlatform\r
+=\r
+===========================\r
+*/\r
+\r
+void SpawnPlatform(Sint16 x, Sint16 y, Sint16 dir)\r
+{\r
+       GetNewObj(false);\r
+       new->obclass = platformobj;\r
+       new->active = ac_allways;\r
+       new->priority = 0;\r
+       new->x = CONVERT_TILE_TO_GLOBAL(x);\r
+       new->y = CONVERT_TILE_TO_GLOBAL(y);\r
+       switch (dir)\r
+       {\r
+       case 0:\r
+               new->xdir = 0;\r
+               new->ydir = -1;\r
+               break;\r
+       case 1:\r
+               new->xdir = 1;\r
+               new->ydir = 0;\r
+               break;\r
+       case 2:\r
+               new->xdir = 0;\r
+               new->ydir = 1;\r
+               break;\r
+       case 3:\r
+               new->xdir = -1;\r
+               new->ydir = 0;\r
+               break;\r
+       }\r
+       NewState(new, &s_platform);\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= T_Platform\r
+=\r
+===========================\r
+*/\r
+\r
+void T_Platform(objtype *ob)\r
+{\r
+       Uint16 newpos, newtile;\r
+\r
+       xtry = ob->xdir * 12 * tics;\r
+       ytry = ob->ydir * 12 * tics;\r
+\r
+       if (ob->xdir == 1)\r
+       {\r
+               newpos = ob->right + xtry;\r
+               newtile = CONVERT_GLOBAL_TO_TILE(newpos);\r
+               if (ob->tileright != newtile)\r
+               {\r
+                       if (*(mapsegs[2]+mapbwidthtable[ob->tiletop]/2+newtile) == 31)\r
+                       {\r
+                               ob->xdir = -1;\r
+                               xtry = xtry - (newpos & 0xFF);\r
+                       }\r
+               }\r
+       }\r
+       else if (ob->xdir == -1)\r
+       {\r
+               newpos = ob->left + xtry;\r
+               newtile = CONVERT_GLOBAL_TO_TILE(newpos);\r
+               if (ob->tileleft != newtile)\r
+               {\r
+                       if (*(mapsegs[2]+mapbwidthtable[ob->tiletop]/2+newtile) == 31)\r
+                       {\r
+                               ob->xdir = 1;\r
+                               xtry = xtry + (0x100 - (newpos & 0xFF));\r
+                       }\r
+               }\r
+       }\r
+       else if (ob->ydir == 1)\r
+       {\r
+               newpos = ob->bottom + ytry;\r
+               newtile = CONVERT_GLOBAL_TO_TILE(newpos);\r
+               if (ob->tilebottom != newtile)\r
+               {\r
+                       if (*(mapsegs[2]+mapbwidthtable[newtile]/2+ob->tileleft) == 31)\r
+                       {\r
+                               if (*(mapsegs[2]+mapbwidthtable[newtile-2]/2+ob->tileleft) == 31)\r
+                               {\r
+                                       ytry = 0;\r
+                                       ob->needtoreact = true;\r
+                               }\r
+                               else\r
+                               {\r
+                                       ob->ydir = -1;\r
+                                       ytry = ytry - (newpos & 0xFF);\r
+                               }\r
+                       }\r
+               }\r
+       }\r
+       else if (ob->ydir == -1)\r
+       {\r
+               newpos = ob->top + ytry;\r
+               newtile = CONVERT_GLOBAL_TO_TILE(newpos);\r
+               if (ob->tiletop != newtile)\r
+               {\r
+                       if (*(mapsegs[2]+mapbwidthtable[newtile]/2+ob->tileleft) == 31)\r
+                       {\r
+                               if (*(mapsegs[2]+mapbwidthtable[newtile+2]/2+ob->tileleft) == 31)\r
+                               {\r
+                                       ytry = 0;\r
+                                       ob->needtoreact = true;\r
+                               }\r
+                               else\r
+                               {\r
+                                       ob->ydir = 1;\r
+                                       ytry = ytry + (0x100 - (newpos & 0xFF));\r
+                               }\r
+                       }\r
+               }\r
+       }\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= R_Platform\r
+=\r
+===========================\r
+*/\r
+\r
+void R_Platform(objtype *ob)\r
+{\r
+       Uint16 frame;\r
+\r
+       //place platform sprite:\r
+       RF_PlaceSprite(&ob->sprite, ob->x, ob->y, ob->shapenum, spritedraw, ob->priority);\r
+       \r
+       //place (or remove) thruster sprites:\r
+       frame = (lasttimecount >> 2) & 1;\r
+       if (ob->xdir == 1)\r
+       {\r
+               RF_PlaceSprite((void**)&ob->temp2, ob->x-1*PIXGLOBAL, ob->y+3*PIXGLOBAL, frame+PLATSIDETHRUST1SPR, spritedraw, 0);\r
+               if (ob->temp3)\r
+                       RF_RemoveSprite((void**)&ob->temp3);\r
+       }\r
+       else if (ob->xdir == -1)\r
+       {\r
+               if (ob->temp2)\r
+                       RF_RemoveSprite((void**)&ob->temp2);\r
+               RF_PlaceSprite((void**)&ob->temp3, ob->x+48*PIXGLOBAL, ob->y+5*PIXGLOBAL, frame+PLATSIDETHRUST1SPR, spritedraw, 1);\r
+       }\r
+       else if (ob->ydir == -1)\r
+       {\r
+               RF_PlaceSprite((void**)&ob->temp2, ob->x+2*PIXGLOBAL, ob->y+9*PIXGLOBAL, frame+PLATLTHRUST1SPR, spritedraw, 0);\r
+               RF_PlaceSprite((void**)&ob->temp3, ob->x+46*PIXGLOBAL, ob->y+8*PIXGLOBAL, frame+PLATRTHRUST1SPR, spritedraw, 0);\r
+       }\r
+       else if (ob->ydir == 1)\r
+       {\r
+               if (frame)\r
+               {\r
+                       RF_PlaceSprite((void**)&ob->temp2, ob->x+2*PIXGLOBAL, ob->y+9*PIXGLOBAL, frame+PLATLTHRUST1SPR, spritedraw, 0);\r
+                       RF_PlaceSprite((void**)&ob->temp3, ob->x+46*PIXGLOBAL, ob->y+8*PIXGLOBAL, frame+PLATRTHRUST1SPR, spritedraw, 0);\r
+               }\r
+               else\r
+               {\r
+                       if (ob->temp2)\r
+                               RF_RemoveSprite((void**)&ob->temp2);\r
+                       if (ob->temp3)\r
+                               RF_RemoveSprite((void**)&ob->temp3);\r
+               }\r
+       }\r
+}\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                 DROPPING PLATFORM\r
+\r
+temp1 = initial y position\r
+\r
+=============================================================================\r
+*/\r
+\r
+statetype s_dropplatsit  = {PLATFORMSPR, PLATFORMSPR, think,      false, false, 0, 0,   0, T_DropPlatSit, NULL, R_Draw, NULL};\r
+statetype s_dropplatfall = {PLATFORMSPR, PLATFORMSPR, think,      false, false, 0, 0,   0, T_DropPlatFall, NULL, R_Draw, NULL};\r
+statetype s_dropplatrise = {PLATFORMSPR, PLATFORMSPR, slidethink, false, false, 0, 0, -32, T_DropPlatRise, NULL, R_Draw, NULL};\r
+\r
+/*\r
+===========================\r
+=\r
+= SpawnDropPlat\r
+=\r
+===========================\r
+*/\r
+\r
+void SpawnDropPlat(Sint16 x, Sint16 y)\r
+{\r
+       GetNewObj(false);\r
+       new->obclass = platformobj;\r
+       new->active = ac_allways;\r
+       new->priority = 0;\r
+       new->x = CONVERT_TILE_TO_GLOBAL(x);\r
+       new->y = new->temp1 = CONVERT_TILE_TO_GLOBAL(y);\r
+       new->xdir = 0;\r
+       new->ydir = 1;\r
+       new->needtoclip = cl_noclip;\r
+       NewState(new, &s_dropplatsit);\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= T_DropPlatSit\r
+=\r
+===========================\r
+*/\r
+\r
+void T_DropPlatSit(objtype *ob)\r
+{\r
+       if (gamestate.riding == ob)\r
+       {\r
+               ytry = tics * 16;\r
+               ob->yspeed = 0;\r
+               if (ob->y + ytry - ob->temp1 >= 8*PIXGLOBAL)\r
+                       ob->state = &s_dropplatfall;\r
+       }\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= T_DropPlatFall\r
+=\r
+===========================\r
+*/\r
+\r
+void T_DropPlatFall(objtype *ob)\r
+{\r
+       Uint16 newy, ty;\r
+\r
+       DoGravity(ob);\r
+\r
+       if (ytry >= 15*PIXGLOBAL)\r
+               ytry = 15*PIXGLOBAL;\r
+\r
+       newy = ob->bottom + ytry;\r
+       ty = CONVERT_GLOBAL_TO_TILE(newy);\r
+       if (ob->tilebottom != ty)\r
+       {\r
+               if (*(mapsegs[2]+mapbwidthtable[ty]/2+ob->tileleft) == 31)\r
+               {\r
+                       ytry = 0xFF - (ob->bottom & 0xFF);\r
+                       if (gamestate.riding != ob)\r
+                               ob->state = &s_dropplatrise;\r
+               }\r
+       }\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= T_DropPlatRise\r
+=\r
+===========================\r
+*/\r
+\r
+void T_DropPlatRise(objtype *ob)\r
+{\r
+       if (gamestate.riding == ob)\r
+       {\r
+               ob->yspeed = 0;\r
+               ob->state = &s_dropplatfall;\r
+       }\r
+       else if (ob->y <= ob->temp1)\r
+       {\r
+               ytry = ob->temp1 - ob->y;\r
+               ob->state = &s_dropplatsit;\r
+       }\r
+}\r
diff --git a/16/keen456/KEEN4-6/KEEN4/K4_ACT3.C b/16/keen456/KEEN4-6/KEEN4/K4_ACT3.C
new file mode 100755 (executable)
index 0000000..287a66f
--- /dev/null
@@ -0,0 +1,1317 @@
+/* Reconstructed Commander Keen 4-6 Source Code\r
+ * Copyright (C) 2021 K1n9_Duk3\r
+ *\r
+ * This file is loosely based on:\r
+ * Keen Dreams Source Code\r
+ * Copyright (C) 2014 Javier M. Chavez\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License along\r
+ * with this program; if not, write to the Free Software Foundation, Inc.,\r
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+ */\r
+\r
+/*\r
+K4_ACT3.C\r
+=========\r
+\r
+Contains the following actor types (in this order):\r
+\r
+- Treasure Eater\r
+- Mimrock\r
+- Dopefish\r
+- Schoolfish\r
+- Sprite\r
+- Mine\r
+- Lindsey\r
+- Dart Shooter & Dart\r
+- Wetsuit\r
+\r
+*/\r
+\r
+#include "CK_DEF.H"\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                 TREASURE EATER\r
+\r
+temp1 = turn counter\r
+\r
+=============================================================================\r
+*/\r
+\r
+statetype s_eaterstand1 = {EATERSTAND1SPR, EATERSTAND1SPR, step, false, false, 20, 0, 0, NULL, C_Eater, R_Draw, &s_eaterstand2};\r
+statetype s_eaterstand2 = {EATERSTAND2SPR, EATERSTAND2SPR, step, false, false, 20, 0, 0, T_EaterJump, C_Eater, R_Draw, NULL};\r
+statetype s_eatertport1 = {SMOKE1SPR, SMOKE1SPR, step, false, false, 20, 0, 0, NULL, C_Eater, R_Draw, &s_eatertport2};\r
+statetype s_eatertport2 = {SMOKE2SPR, SMOKE2SPR, step, false, false, 20, 0, 0, NULL, C_Eater, R_Draw, &s_eatertport3};\r
+statetype s_eatertport3 = {SMOKE3SPR, SMOKE3SPR, step, false, false, 20, 0, 0, NULL, C_Eater, R_Draw, &s_eatertport4};\r
+statetype s_eatertport4 = {SMOKE4SPR, SMOKE4SPR, step, false, false, 20, 0, 0, T_EaterTeleport, C_Eater, R_Draw, &s_eatertport5};\r
+statetype s_eatertport5 = {SMOKE4SPR, SMOKE4SPR, step, false, false, 20, 0, 0, NULL, C_Eater, R_Draw, &s_eatertport6};\r
+statetype s_eatertport6 = {SMOKE3SPR, SMOKE3SPR, step, false, false, 20, 0, 0, NULL, C_Eater, R_Draw, &s_eatertport7};\r
+statetype s_eatertport7 = {SMOKE2SPR, SMOKE2SPR, step, false, false, 20, 0, 0, NULL, C_Eater, R_Draw, &s_eatertport8};\r
+statetype s_eatertport8 = {SMOKE1SPR, SMOKE1SPR, step, false, false, 20, 0, 0, NULL, C_Eater, R_Draw, &s_eaterjump1};\r
+statetype s_eaterjump1  = {EATERJUMPL1SPR, EATERJUMPR1SPR, stepthink, false, false, 6, 0, 0, T_WeakProjectile, C_Eater, R_EaterAir, &s_eaterjump2};\r
+statetype s_eaterjump2  = {EATERJUMPL2SPR, EATERJUMPR2SPR, stepthink, false, false, 6, 0, 0, T_WeakProjectile, C_Eater, R_EaterAir, &s_eaterjump3};\r
+statetype s_eaterjump3  = {EATERJUMPL3SPR, EATERJUMPR3SPR, stepthink, false, false, 6, 0, 0, T_WeakProjectile, C_Eater, R_EaterAir, &s_eaterjump4};\r
+statetype s_eaterjump4  = {EATERJUMPL2SPR, EATERJUMPR2SPR, stepthink, false, false, 6, 0, 0, T_WeakProjectile, C_Eater, R_EaterAir, &s_eaterjump1};\r
+statetype s_eaterstun   = {EATERJUMPL1SPR, EATERJUMPL1SPR, think, false, false, 0, 0, 0, T_Projectile, 0, R_Stunned, &s_eaterstun2};\r
+statetype s_eaterstun2  = {EATERSTUNSPR, EATERSTUNSPR, think, false, false, 0, 0, 0, T_Projectile, 0, R_Stunned, NULL};\r
+\r
+statetype s_eatenbonus1 = {EATENBONUS1SPR, EATENBONUS1SPR, slide, false, false, 10, 0, 8, NULL, NULL, R_Draw, &s_eatenbonus2};\r
+statetype s_eatenbonus2 = {EATENBONUS2SPR, EATENBONUS2SPR, slide, false, false, 10, 0, 8, NULL, NULL, R_Draw, &s_eatenbonus3};\r
+statetype s_eatenbonus3 = {EATENBONUS3SPR, EATENBONUS3SPR, slide, false, false, 10, 0, 8, NULL, NULL, R_Draw, &s_eatenbonus4};\r
+statetype s_eatenbonus4 = {EATENBONUS4SPR, EATENBONUS4SPR, slide, false, false, 10, 0, 8, NULL, NULL, R_Draw, NULL};\r
+\r
+/*\r
+===========================\r
+=\r
+= SpawnEater\r
+=\r
+===========================\r
+*/\r
+\r
+void SpawnEater(Sint16 x, Sint16 y)\r
+{\r
+       GetNewObj(false);\r
+       new->obclass = treasureeaterobj;\r
+       new->active = ac_yes;\r
+       new->priority = 3;\r
+       new->x = CONVERT_TILE_TO_GLOBAL(x);\r
+       new->y = CONVERT_TILE_TO_GLOBAL(y) - 24*PIXGLOBAL;\r
+       if (US_RndT() < 0x80)\r
+       {\r
+               new->xdir = 1;\r
+       }\r
+       else\r
+       {\r
+               new->xdir = -1;\r
+       }\r
+       new->ydir = 1;\r
+       NewState(new, &s_eaterstand1);\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= T_EaterJump\r
+=\r
+===========================\r
+*/\r
+\r
+void T_EaterJump(objtype *ob)\r
+{\r
+       objtype *ob2;\r
+       Uint16 x;\r
+       Sint16 y;\r
+       Uint16 far *map;\r
+       Uint16 intile, rowdiff, width;\r
+\r
+       ob->state = &s_eaterjump1;\r
+\r
+       //jump straight up if below bonus object:\r
+       for (ob2 = player->next; ob2; ob2 = ob2->next)\r
+       {\r
+               if (ob2->obclass == bonusobj && ob2->active == ac_yes\r
+                       && ob2->right > ob->left && ob2->left < ob->right\r
+                       && ob2->bottom < ob->top && ob2->bottom + 3*TILEGLOBAL > ob->top)\r
+               {\r
+                       ob->xspeed = 0;\r
+                       ob->yspeed = -48;\r
+                       return;\r
+               }\r
+       }\r
+\r
+       //jump straight up if below bonus tile:\r
+       map = mapsegs[1] + mapbwidthtable[ob->tiletop-3]/2 + ob->tileleft;\r
+       width = ob->tileright-ob->tileleft+1;\r
+       rowdiff = mapwidth-width;\r
+       for (y=0; y<3; y++, map+=rowdiff)\r
+       {\r
+               for (x=0; x<width; x++, map++)\r
+               {\r
+                       intile = tinf[INTILE+*map];\r
+                       if (intile == INTILE_DROP || intile >= INTILE_BONUS100 && intile <= INTILE_AMMO)\r
+                       {\r
+                               ob->xspeed = 0;\r
+                               ob->yspeed = -48;\r
+                               return;\r
+                       }\r
+               }\r
+       }\r
+\r
+       //vanish after having checked both directions:\r
+       if (ob->temp1 >= 2)\r
+       {\r
+               // BUG? this doesn't play a sound\r
+               ob->state = &s_eatertport1;\r
+               return;\r
+       }\r
+\r
+       //jump in current direction if there is a floor in that direction:\r
+       map = mapsegs[1] + mapbwidthtable[ob->tilebottom-2]/2 + ob->tilemidx;\r
+       map += ob->xdir * 4;\r
+       for (y=0; y<4; y++, map+=mapwidth)\r
+       {\r
+               if (tinf[NORTHWALL+*map])\r
+               {\r
+                       ob->xspeed = ob->xdir * 20;\r
+                       ob->yspeed = -24;\r
+                       return;\r
+               }\r
+       }\r
+\r
+       //couldn't jump in current direction, so turn around:\r
+       if (++ob->temp1 == 2)\r
+       {\r
+               SD_PlaySound(SND_TREASUREEATERVANISH);\r
+               ob->state = &s_eatertport1;\r
+               return;\r
+       }\r
+\r
+       //jump in opposite direction:\r
+       ob->xdir = -ob->xdir;\r
+       ob->xspeed = ob->xdir * 20;\r
+       ob->yspeed = -24;\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= T_EaterTeleport\r
+=\r
+===========================\r
+*/\r
+\r
+void T_EaterTeleport(objtype *ob)\r
+{\r
+       objtype *ob2;\r
+\r
+       ob->temp1 = 0;\r
+       for (ob2=player->next; ob2; ob2=ob2->next)\r
+       {\r
+               if (ob2->obclass == bonusobj)\r
+               {\r
+                       ob->x = ob2->x - 8*PIXGLOBAL;\r
+                       ob->y = ob2->y;\r
+                       NewState(ob, &s_eatertport5);\r
+                       return;\r
+               }\r
+       }\r
+       RemoveObj(ob);\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= C_Eater\r
+=\r
+===========================\r
+*/\r
+\r
+void C_Eater(objtype *ob, objtype *hit)\r
+{\r
+       if (hit->obclass == bonusobj)\r
+       {\r
+               //BUG? bonus object might be a key, and eating a key makes a level unwinnable\r
+               hit->obclass = inertobj;\r
+               hit->priority = 3;\r
+               ChangeState(hit, &s_eatenbonus1);\r
+               SD_PlaySound(SND_EATBONUS);\r
+       }\r
+       else if (hit->obclass == stunshotobj)\r
+       {\r
+               //basically StunObj(), but in different order:\r
+               ob->temp1 = 0;\r
+               ob->temp2 = 0;\r
+               ob->temp3 = 0;\r
+               ob->temp4 = ob->obclass;\r
+               ob->obclass = stunnedobj;\r
+               ExplodeShot(hit);\r
+               ChangeState(ob, &s_eaterstun);\r
+\r
+               ob->yspeed -= 16;\r
+       }\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= EaterInTile\r
+=\r
+===========================\r
+*/\r
+\r
+void EaterInTile(objtype *ob)\r
+{\r
+       Uint16 x, y;\r
+       Uint16 far *map;\r
+       Uint16 rowdiff, intile;\r
+\r
+       map = mapsegs[1] + mapbwidthtable[ob->tiletop]/2 + ob->tileleft;\r
+       rowdiff = mapwidth-(ob->tileright-ob->tileleft+1);\r
+       for (y=ob->tiletop; y<=ob->tilebottom; y++, map+=rowdiff)\r
+       {\r
+               for (x=ob->tileleft; x<=ob->tileright; x++, map++)\r
+               {\r
+                       intile = tinf[INTILE + *map] & INTILE_TYPEMASK;\r
+                       if (intile == INTILE_DROP || intile >= INTILE_BONUS100 && intile <= INTILE_AMMO)\r
+                       {\r
+                               RF_MemToMap(&zeromap, 1, x, y, 1, 1);\r
+                               GetNewObj(true);\r
+                               new->obclass = inertobj;\r
+                               new->priority = 3;\r
+                               new->needtoclip = cl_noclip;\r
+                               new->x = CONVERT_TILE_TO_GLOBAL(x);\r
+                               new->y = CONVERT_TILE_TO_GLOBAL(y);\r
+                               new->active = ac_removable;\r
+                               ChangeState(new, &s_eatenbonus1);       //using ChangeState and not NewState is fine for noclipping objects\r
+                               //BUG? this doesn't play a sound\r
+                               break;\r
+                       }\r
+               }\r
+       }\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= R_EaterAir\r
+=\r
+===========================\r
+*/\r
+\r
+void R_EaterAir(objtype *ob)\r
+{\r
+       EaterInTile(ob);\r
+\r
+       if (ob->hitnorth)\r
+               ChangeState(ob, &s_eaterstand1);\r
+\r
+       if (ob->hiteast || ob->hitwest)\r
+       {\r
+               ob->temp1++;\r
+               ob->xdir = -ob->xdir;\r
+               ob->xspeed = 0;\r
+       }\r
+\r
+       if (ob->hitnorth)       //BUG? maybe this was supposed to check hitsouth as well?\r
+               ob->yspeed = 0;\r
+\r
+       RF_PlaceSprite(&ob->sprite, ob->x, ob->y, ob->shapenum, spritedraw, ob->priority);\r
+}\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                 MIMROCK\r
+\r
+=============================================================================\r
+*/\r
+\r
+statetype s_mimrock   = {MIMROCKSPR, MIMROCKSPR, step, false, true, 20, 0, 0, T_MimrockWait, NULL, R_Walk, &s_mimrock};\r
+statetype s_mimsneak1 = {MIMROCKWALKR1SPR, MIMROCKWALKL1SPR, step, false, true, 6, 64, 0, T_MimrockSneak, C_Mimrock, R_Walk, &s_mimsneak2};\r
+statetype s_mimsneak2 = {MIMROCKWALKR2SPR, MIMROCKWALKL2SPR, step, false, true, 6, 64, 0, T_MimrockSneak, C_Mimrock, R_Walk, &s_mimsneak3};\r
+statetype s_mimsneak3 = {MIMROCKWALKR3SPR, MIMROCKWALKL3SPR, step, false, true, 6, 64, 0, T_MimrockSneak, C_Mimrock, R_Walk, &s_mimsneak4};\r
+statetype s_mimsneak4 = {MIMROCKWALKR4SPR, MIMROCKWALKL4SPR, step, false, true, 6, 64, 0, T_MimrockSneak, C_Mimrock, R_Walk, &s_mimsneak5};\r
+statetype s_mimsneak5 = {MIMROCKWALKR1SPR, MIMROCKWALKL1SPR, step, false, true, 6, 64, 0, T_MimrockSneak, C_Mimrock, R_Walk, &s_mimsneak6};\r
+statetype s_mimsneak6 = {MIMROCKWALKR2SPR, MIMROCKWALKL2SPR, step, false, true, 6, 64, 0, T_MimrockSneak, C_Mimrock, R_Walk, &s_mimrock};\r
+statetype s_mimbonk1  = {MIMROCKJUMPL1SPR, MIMROCKJUMPR1SPR, stepthink, false, false, 24, 0, 0, T_WeakProjectile, C_MimLethal, R_MimAir, &s_mimbonk2};\r
+statetype s_mimbonk2  = {MIMROCKJUMPL2SPR, MIMROCKJUMPR2SPR, stepthink, false, false, 10, 0, 0, T_WeakProjectile, C_MimLethal, R_MimAir, &s_mimbonk3};\r
+statetype s_mimbonk3  = {MIMROCKJUMPL3SPR, MIMROCKJUMPR3SPR, think, false, false, 10, 0, 0, T_WeakProjectile, C_MimLethal, R_MimAir, &s_mimbonk2};\r
+statetype s_mimbounce = {MIMROCKJUMPL3SPR, MIMROCKJUMPR3SPR, think, false, false, 10, 0, 0, T_Projectile, C_Mimrock, R_MimBounce, NULL};\r
+statetype s_mimstun   = {MIMROCKJUMPL3SPR, MIMROCKJUMPL3SPR, think, false, false, 12, 0, 0, T_Projectile, NULL, R_Stunned, &s_mimstun2};\r
+statetype s_mimstun2  = {MINROCKSTUNSPR, MINROCKSTUNSPR, think, false, false, 12, 0, 0, T_Projectile, NULL, R_Stunned, NULL};\r
+\r
+/*\r
+===========================\r
+=\r
+= SpawnMimrock\r
+=\r
+===========================\r
+*/\r
+\r
+void SpawnMimrock(Sint16 x, Sint16 y)\r
+{\r
+       GetNewObj(false);\r
+       new->obclass = mimrockobj;\r
+       new->active = ac_yes;\r
+       new->priority = 3;\r
+       new->x = CONVERT_TILE_TO_GLOBAL(x);\r
+       new->y = CONVERT_TILE_TO_GLOBAL(y)+ -13*PIXGLOBAL;\r
+       new->ydir = new->xdir = 1;\r
+       NewState(new, &s_mimrock);\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= T_MimrockWait\r
+=\r
+===========================\r
+*/\r
+\r
+void T_MimrockWait(objtype *ob)\r
+{\r
+       if (abs(ob->bottom - player->bottom) > 5*TILEGLOBAL)\r
+               return;\r
+\r
+       if (abs(ob->x - player->x) > 3*TILEGLOBAL)\r
+       {\r
+               if (player->x < ob->x)\r
+               {\r
+                       if (player->xdir == -1)\r
+                       {\r
+                               ob->xdir = -1;\r
+                               ob->state = &s_mimsneak1;\r
+                       }\r
+               }\r
+               else\r
+               {\r
+                       if (player->xdir == 1)\r
+                       {\r
+                               ob->xdir = 1;\r
+                               ob->state = &s_mimsneak1;\r
+                       }\r
+               }\r
+       }\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= T_MimrockSneak\r
+=\r
+===========================\r
+*/\r
+\r
+void T_MimrockSneak(objtype *ob)\r
+{\r
+       if (abs(ob->bottom - player->bottom) > 5*TILEGLOBAL\r
+               || ob->xdir != player->xdir)\r
+       {\r
+               ob->state = &s_mimrock;\r
+       }\r
+       else if (abs(ob->x - player->x) < 4*TILEGLOBAL)\r
+       {\r
+               ob->xspeed = ob->xdir * 20;\r
+               ob->yspeed = -40;\r
+               ytry = ob->yspeed * tics;\r
+               ob->state = &s_mimbonk1;\r
+       }\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= C_Mimrock\r
+=\r
+===========================\r
+*/\r
+\r
+void C_Mimrock(objtype *ob, objtype *hit)\r
+{\r
+       if (hit->obclass == stunshotobj)\r
+       {\r
+               //basically StunObj(), but in different order:\r
+               ob->temp1 = 0;\r
+               ob->temp2 = 0;\r
+               ob->temp3 = 0;\r
+               ob->temp4 = ob->obclass;\r
+               ob->obclass = stunnedobj;\r
+               ExplodeShot(hit);\r
+               ChangeState(ob, &s_mimstun);\r
+\r
+               ob->yspeed -= 16;\r
+       }\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= C_MimLethal\r
+=\r
+===========================\r
+*/\r
+\r
+void C_MimLethal(objtype *ob, objtype *hit)\r
+{\r
+       if (hit->obclass == keenobj)\r
+       {\r
+               KillKeen();\r
+       }\r
+       else\r
+       {\r
+               C_Mimrock(ob, hit);\r
+       }\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= R_MimAir\r
+=\r
+===========================\r
+*/\r
+\r
+void R_MimAir(objtype *ob)\r
+{\r
+       if (ob->hitnorth)\r
+       {\r
+               SD_PlaySound(SND_HELMETHIT);\r
+               ob->yspeed = -20;\r
+               ChangeState(ob, &s_mimbounce);\r
+       }\r
+\r
+       if (ob->hiteast || ob->hitwest)\r
+               ob->xspeed = 0;\r
+\r
+       if (ob->hitnorth || ob->hitsouth)\r
+               ob->yspeed = 0;\r
+\r
+       RF_PlaceSprite(&ob->sprite, ob->x, ob->y, ob->shapenum, spritedraw, ob->priority);\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= R_MimBounce\r
+=\r
+===========================\r
+*/\r
+\r
+void R_MimBounce(objtype *ob)\r
+{\r
+       if (ob->hitnorth)\r
+       {\r
+               SD_PlaySound(SND_HELMETHIT);\r
+               ChangeState(ob, &s_mimrock);\r
+       }\r
+\r
+       if (ob->hiteast || ob->hitwest)\r
+               ob->xspeed = 0;\r
+\r
+       if (ob->hitnorth)\r
+               ob->yspeed = 0;\r
+\r
+       RF_PlaceSprite(&ob->sprite, ob->x, ob->y, ob->shapenum, spritedraw, ob->priority);\r
+}\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                 DOPEFISH\r
+\r
+temp1 = blocked (cannot change xdir to chase Keen while this is non-zero)\r
+temp2 = old x position\r
+temp3 = old y position\r
+temp4 = pointer to object being eaten\r
+        (BUG: pointer may be invalid after loading a saved game!)\r
+\r
+=============================================================================\r
+*/\r
+\r
+statetype s_dopefish1  = {DOPEFISHSWIML1SPR, DOPEFISHSWIMR1SPR, stepthink, false, false, 20, 0, 0, T_Dope, C_Dope, R_Fish, &s_dopefish2};\r
+statetype s_dopefish2  = {DOPEFISHSWIML2SPR, DOPEFISHSWIMR2SPR, stepthink, false, false, 20, 0, 0, T_Dope, C_Dope, R_Fish, &s_dopefish1};\r
+statetype s_dopeattack = {DOPEFISHHUNGRYLSPR, DOPEFISHHUNGRYRSPR, think, false, false, 0, 0, 0, T_DopeHunt, NULL, R_Draw, NULL};\r
+statetype s_dopeeat    = {DOPEFISHSWIML1SPR, DOPEFISHSWIMR1SPR, step, false, false, 60, 0, 0, NULL, NULL, R_Draw, &s_dopeburp1};\r
+statetype s_dopeburp1  = {DOPEFISHBURP1SPR, DOPEFISHBURP1SPR, step, false, false, 60, 0, 0, T_Burp, NULL, R_Draw, &s_dopeburp2};\r
+statetype s_dopeburp2  = {DOPEFISHBURP2SPR, DOPEFISHBURP2SPR, step, false, false, 60, 0, 0, NULL, NULL, R_Draw, &s_dopereturn};\r
+statetype s_dopereturn = {DOPEFISHSWIML1SPR, DOPEFISHSWIMR1SPR, think, false, false, 0, 0, 0, T_DopeReturn, NULL, R_Draw, &s_dopefish1};\r
+\r
+statetype s_dopefood     = {SCHOOLFISHL1SPR, SCHOOLFISHR1SPR, think, false, false, 0, 0, 0, NULL, NULL, R_Draw, NULL};\r
+statetype s_keendopefood = {SCUBAKEENDEAD1SPR, SCUBAKEENDEAD1SPR, think, false, false, 0, 0, 0, NULL, NULL, R_Draw, &s_keendieslow};\r
+statetype s_keendieslow  = {-1, -1, step, false, false, 180, 0, 0, T_EatenKeen, NULL, R_Draw, &s_keendieslow};\r
+\r
+statetype s_bubble1 = {BIGBUBBLE1SPR, BIGBUBBLE1SPR, think, false, false, 20, 0, 20, T_Bubble, NULL, R_Draw, &s_bubble2};\r
+statetype s_bubble2 = {BIGBUBBLE2SPR, BIGBUBBLE2SPR, think, false, false, 20, 0, 20, T_Bubble, NULL, R_Draw, &s_bubble3};\r
+statetype s_bubble3 = {BIGBUBBLE3SPR, BIGBUBBLE3SPR, think, false, false, 20, 0, 20, T_Bubble, NULL, R_Draw, &s_bubble4};\r
+statetype s_bubble4 = {BIGBUBBLE4SPR, BIGBUBBLE4SPR, think, false, false, 20, 0, 20, T_Bubble, NULL, R_Draw, &s_bubble1};\r
+\r
+/*\r
+===========================\r
+=\r
+= SpawnDopefish\r
+=\r
+===========================\r
+*/\r
+\r
+void SpawnDopefish(Sint16 x, Sint16 y)\r
+{\r
+       GetNewObj(false);\r
+       new->obclass = dopefishobj;\r
+       new->active = ac_yes;\r
+       new->priority = 2;\r
+       new->needtoclip = cl_fullclip;\r
+       new->x = CONVERT_TILE_TO_GLOBAL(x);\r
+       new->y = CONVERT_TILE_TO_GLOBAL(y) + -3*TILEGLOBAL;\r
+       if (US_RndT() < 0x80)\r
+       {\r
+               new->xdir = 1;\r
+       }\r
+       else\r
+       {\r
+               new->xdir = -1;\r
+       }\r
+       new->ydir = 1;\r
+       NewState(new, &s_dopefish1);\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= T_EatenKeen\r
+=\r
+===========================\r
+*/\r
+\r
+void T_EatenKeen(objtype *ob)\r
+{\r
+       ob++;                   // shut up compiler\r
+       playstate = ex_died;\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= T_Dope\r
+=\r
+===========================\r
+*/\r
+\r
+void T_Dope(objtype *ob)\r
+{\r
+       if (ob->temp1 == 0)\r
+       {\r
+               if (ob->x < player->x)\r
+               {\r
+                       ob->xdir = 1;\r
+               }\r
+               else\r
+               {\r
+                       ob->xdir = -1;\r
+               }\r
+       }\r
+       AccelerateXv(ob, ob->xdir, 10);\r
+\r
+       if (ob->y < player->y)\r
+       {\r
+               AccelerateY(ob, 1, 10);\r
+       }\r
+       else\r
+       {\r
+               AccelerateY(ob, -1, 10);\r
+       }\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= T_DopeHunt\r
+=\r
+===========================\r
+*/\r
+\r
+void T_DopeHunt(objtype *ob)\r
+{\r
+       objtype *target;\r
+       Sint16 xdist, ydist;\r
+\r
+       target = (objtype *)(ob->temp4);\r
+       ydist = target->y - TILEGLOBAL - ob->y;\r
+       if (ob->xdir == 1)\r
+       {\r
+               xdist = target->right + 2*PIXGLOBAL - ob->right;\r
+       }\r
+       else\r
+       {\r
+               xdist = target->left - 2*PIXGLOBAL - ob->left;\r
+       }\r
+\r
+       if (xdist < 0)\r
+       {\r
+               xtry = tics * -32;\r
+               if (xtry < xdist)\r
+                       xtry = xdist;\r
+       }\r
+       else\r
+       {\r
+               xtry = tics * 32;\r
+               if (xtry > xdist)\r
+                       xtry = xdist;\r
+       }\r
+\r
+       if (ydist < 0)\r
+       {\r
+               ytry = tics * -32;\r
+               if (ytry < ydist)\r
+                       ytry = ydist;\r
+       }\r
+       else\r
+       {\r
+               ytry = tics * 32;\r
+               if (ytry > ydist)\r
+                       ytry = ydist;\r
+       }\r
+\r
+       if (xtry == xdist && ytry == ydist)\r
+       {\r
+               if (target == player)\r
+               {\r
+                       ChangeState(target, &s_keendieslow);\r
+               }\r
+               else if (target->state->nextstate)\r
+               {\r
+                       ChangeState(target, target->state->nextstate);\r
+               }\r
+               else\r
+               {\r
+                       RemoveObj(target);\r
+               }\r
+               ob->state = &s_dopeeat;\r
+       }\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= T_DopeReturn\r
+=\r
+===========================\r
+*/\r
+\r
+void T_DopeReturn(objtype *ob)\r
+{\r
+       Sint16 xdist, ydist;\r
+\r
+       ydist = ob->temp3 - ob->y;\r
+       xdist = ob->temp2 - ob->x;\r
+\r
+       if (xdist < 0)\r
+       {\r
+               xtry = tics * -32;\r
+               if (xtry < xdist)\r
+                       xtry = xdist;\r
+       }\r
+       else\r
+       {\r
+               xtry = tics * 32;\r
+               if (xtry > xdist)\r
+                       xtry = xdist;\r
+       }\r
+\r
+       if (ydist < 0)\r
+       {\r
+               ytry = tics * -32;\r
+               if (ytry < ydist)\r
+                       ytry = ydist;\r
+       }\r
+       else\r
+       {\r
+               ytry = tics * 32;\r
+               if (ytry > ydist)\r
+                       ytry = ydist;\r
+       }\r
+\r
+       if (xtry == xdist && ytry == ydist)\r
+       {\r
+               ob->state = ob->state->nextstate;\r
+               ob->needtoclip = cl_fullclip;\r
+       }\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= T_Burp\r
+=\r
+===========================\r
+*/\r
+\r
+void T_Burp(objtype *ob)\r
+{\r
+       GetNewObj(true);\r
+       new->x = ob->x + 56*PIXGLOBAL;\r
+       new->y = ob->y + 32*PIXGLOBAL;\r
+       new->obclass = inertobj;\r
+       new->priority = 3;\r
+       new->active = ac_removable;\r
+       new->needtoclip = cl_noclip;\r
+       new->yspeed = -20;\r
+       new->xspeed = 4;\r
+       NewState(new, &s_bubble1);\r
+       SD_PlaySound(SND_BURP);\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= T_Bubble\r
+=\r
+===========================\r
+*/\r
+\r
+void T_Bubble(objtype *ob)\r
+{\r
+       T_Velocity(ob);\r
+       if (US_RndT() < tics * 16)\r
+               ob->xspeed = -ob->xspeed;\r
+\r
+       if (ob->y < 3*TILEGLOBAL)\r
+               RemoveObj(ob);\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= C_Dope\r
+=\r
+===========================\r
+*/\r
+\r
+void C_Dope(objtype *ob, objtype *hit)\r
+{\r
+       if (hit->obclass == schoolfishobj)\r
+       {\r
+               ChangeState(hit, &s_dopefood);\r
+       }\r
+       else if (hit->obclass == keenobj && !godmode)\r
+       {\r
+               hit->obclass = inertobj;        //prevents other objects from killing Keen before he is fully swallowed\r
+               hit->needtoclip = cl_noclip;\r
+               SD_PlaySound(SND_KEENDEAD);\r
+               ChangeState(hit, &s_keendopefood);\r
+       }\r
+       else\r
+       {\r
+               return;\r
+       }\r
+\r
+       ob->temp2 = ob->x;\r
+       ob->temp3 = ob->y;\r
+       ob->temp4 = (Sint16)hit;\r
+       if (hit->midx < ob->midx)\r
+       {\r
+               ob->xdir = -1;\r
+       }\r
+       else\r
+       {\r
+               ob->xdir = 1;\r
+       }\r
+       ChangeState(ob, &s_dopeattack);\r
+       ob->needtoclip = cl_noclip;\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= R_Fish\r
+=\r
+===========================\r
+*/\r
+\r
+void R_Fish(objtype *ob)       //for Dopefish and Schoolfish\r
+{\r
+       if ((ob->hitsouth || ob->hitnorth) && ob->temp1 == 0)\r
+               ob->temp1++;\r
+\r
+       if (ob->hiteast || ob->hitwest)\r
+       {\r
+               ob->xspeed = 0;\r
+               ob->xdir = -ob->xdir;\r
+               ob->temp1 = 1;\r
+       }\r
+\r
+       if (!ob->hitsouth && !ob->hitnorth)\r
+               ob->temp1 = 0;\r
+\r
+       RF_PlaceSprite(&ob->sprite, ob->x, ob->y, ob->shapenum, spritedraw, ob->priority);\r
+}\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                 SCHOOLFISH\r
+\r
+temp1 = blocked (cannot change xdir to chase Keen while this is non-zero)\r
+\r
+=============================================================================\r
+*/\r
+\r
+statetype s_schoolfish1 = {SCHOOLFISHL1SPR, SCHOOLFISHR1SPR, stepthink, false, false, 20, 0, 0, T_SchoolFish, NULL, R_Fish, &s_schoolfish2};\r
+statetype s_schoolfish2 = {SCHOOLFISHL2SPR, SCHOOLFISHR2SPR, stepthink, false, false, 20, 0, 0, T_SchoolFish, NULL, R_Fish, &s_schoolfish1};\r
+\r
+/*\r
+===========================\r
+=\r
+= SpawnSchoolfish\r
+=\r
+===========================\r
+*/\r
+\r
+void SpawnSchoolfish(Sint16 x, Sint16 y)\r
+{\r
+       GetNewObj(false);\r
+       new->obclass = schoolfishobj;\r
+       new->active = ac_yes;\r
+       new->needtoclip = cl_fullclip;\r
+       new->priority = 0;\r
+       new->x = CONVERT_TILE_TO_GLOBAL(x);\r
+       new->y = CONVERT_TILE_TO_GLOBAL(y);\r
+       new->ydir = new->xdir = 1;\r
+       NewState(new, &s_schoolfish1);\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= T_SchoolFish\r
+=\r
+===========================\r
+*/\r
+\r
+void T_SchoolFish(objtype *ob)\r
+{\r
+       if (ob->temp1 == 0)\r
+       {\r
+               if (ob->x < player->x)\r
+               {\r
+                       ob->xdir = 1;\r
+               }\r
+               else\r
+               {\r
+                       ob->xdir = -1;\r
+               }\r
+       }\r
+       AccelerateXv(ob, ob->xdir, 10);\r
+\r
+       if (ob->y < player->y)\r
+       {\r
+               AccelerateY(ob, 1, 10);\r
+       }\r
+       else\r
+       {\r
+               AccelerateY(ob, -1, 10);\r
+       }\r
+}\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                 PIXIE (a.k.a. SPRITE)\r
+\r
+=============================================================================\r
+*/\r
+\r
+statetype s_pixie       = {SPRITEFLOATSPR,  SPRITEFLOATSPR,  think, false, false, 10, 0, 0, T_Pixie, C_Lethal, R_Draw, &s_pixie};\r
+statetype s_pixielook   = {SPRITEAIMLSPR,   SPRITEAIMRSPR,   step, false, false, 40, 0, 0, T_PixieCheck, C_Lethal, R_Draw, &s_pixie};\r
+statetype s_pixieshoot  = {SPRITESHOOTLSPR, SPRITESHOOTRSPR, step, false, false, 40, 0, 0, T_PixieShoot, C_Lethal, R_Draw, &s_pixieshoot2};\r
+statetype s_pixieshoot2 = {SPRITESHOOTLSPR, SPRITESHOOTRSPR, step, false, false, 30, 0, 0, NULL, C_Lethal, R_Draw, &s_pixie};\r
+statetype s_pixiefire1  = {SPRITESHOT1SPR,  SPRITESHOT1SPR,  slide, false, false, 10, 64, 0, NULL, C_Lethal, R_Mshot, &s_pixiefire2};\r
+statetype s_pixiefire2  = {SPRITESHOT2SPR,  SPRITESHOT2SPR,  slide, false, false, 10, 64, 0, NULL, C_Lethal, R_Mshot, &s_pixiefire3};\r
+statetype s_pixiefire3  = {SPRITESHOT3SPR,  SPRITESHOT3SPR,  slide, false, false, 10, 64, 0, NULL, C_Lethal, R_Mshot, &s_pixiefire4};\r
+statetype s_pixiefire4  = {SPRITESHOT4SPR,  SPRITESHOT4SPR,  slide, false, false, 10, 64, 0, NULL, C_Lethal, R_Mshot, &s_pixiefire1};\r
+\r
+/*\r
+===========================\r
+=\r
+= SpawnPixie\r
+=\r
+===========================\r
+*/\r
+\r
+void SpawnPixie(Sint16 x, Sint16 y)\r
+{\r
+       GetNewObj(false);\r
+       new->obclass = pixieobj;\r
+       new->active = ac_yes;\r
+       new->priority = 0;\r
+       new->needtoclip = cl_noclip;\r
+       new->x = CONVERT_TILE_TO_GLOBAL(x);\r
+       new->y = new->temp1 = CONVERT_TILE_TO_GLOBAL(y);\r
+       new->ydir = new->xdir = 1;\r
+       NewState(new, &s_pixie);\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= T_Pixie\r
+=\r
+===========================\r
+*/\r
+\r
+void T_Pixie(objtype *ob)\r
+{\r
+       AccelerateY(ob, ob->ydir, 8);\r
+       if ((Sint16)(ob->temp1 - ob->y) > 2*PIXGLOBAL)\r
+       {\r
+               ob->ydir = 1;\r
+       }\r
+       if ((Sint16)(ob->y - ob->temp1) > 2*PIXGLOBAL)\r
+       {\r
+               ob->ydir = -1;\r
+       }\r
+\r
+       if (player->top < ob->bottom && player->bottom > ob->top)\r
+       {\r
+               if (player->x < ob->x)\r
+               {\r
+                       ob->xdir = -1;\r
+               }\r
+               else\r
+               {\r
+                       ob->xdir = 1;\r
+               }\r
+               ob->state = &s_pixielook;\r
+       }\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= T_PixieCheck\r
+=\r
+===========================\r
+*/\r
+\r
+void T_PixieCheck(objtype *ob)\r
+{\r
+       if (player->top < ob->bottom && player->bottom > ob->top)\r
+               ob->state = &s_pixieshoot;\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= T_PixieShoot\r
+=\r
+===========================\r
+*/\r
+\r
+void T_PixieShoot(objtype *ob)\r
+{\r
+       GetNewObj(true);\r
+       new->x = ob->x;\r
+       new->y = ob->y + 8*PIXGLOBAL;\r
+       new->priority = 0;\r
+       new->obclass = mshotobj;\r
+       new->active = ac_removable;\r
+       SD_PlaySound(SND_KEENFIRE);     //BUG?\r
+       new->xdir = ob->xdir;\r
+       NewState(new, &s_pixiefire1);\r
+       SD_PlaySound(SND_SPRITEFIRE);\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= R_Mshot\r
+=\r
+===========================\r
+*/\r
+\r
+void R_Mshot(objtype *ob)\r
+{\r
+       if (ob->hitnorth || ob->hiteast || ob->hitsouth || ob->hitwest)\r
+       {\r
+               RemoveObj(ob);\r
+       }\r
+       else\r
+       {\r
+               RF_PlaceSprite(&ob->sprite, ob->x, ob->y, ob->shapenum, spritedraw, ob->priority);\r
+       }\r
+}\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                 MINE\r
+\r
+=============================================================================\r
+*/\r
+statetype s_mine      = {MINESPR,         MINESPR,         think, false, false, 10, 0, 0, T_Platform, C_Mine, R_Draw, &s_mine};\r
+statetype s_mineboom1 = {MINEEXPLODE1SPR, MINEEXPLODE1SPR, step, false, false, 30, 0, 0, NULL, NULL, R_Draw, &s_mineboom2};\r
+statetype s_mineboom2 = {MINEEXPLODE2SPR, MINEEXPLODE2SPR, step, false, false, 30, 0, 0, NULL, NULL, R_Draw, NULL};\r
+\r
+/*\r
+===========================\r
+=\r
+= SpawnMine\r
+=\r
+===========================\r
+*/\r
+\r
+void SpawnMine(Sint16 x, Sint16 y, Sint16 dir)\r
+{\r
+       GetNewObj(false);\r
+       new->obclass = mineobj;\r
+       new->active = ac_allways;\r
+       new->priority = 0;\r
+       new->x = CONVERT_TILE_TO_GLOBAL(x);\r
+       new->y = CONVERT_TILE_TO_GLOBAL(y);\r
+       switch (dir)\r
+       {\r
+       case 0:\r
+               new->xdir = 0;\r
+               new->ydir = -1;\r
+               break;\r
+       case 1:\r
+               new->xdir = 1;\r
+               new->ydir = 0;\r
+               break;\r
+       case 2:\r
+               new->xdir = 0;\r
+               new->ydir = 1;\r
+               break;\r
+       case 3:\r
+               new->xdir = -1;\r
+               new->ydir = 0;\r
+               break;\r
+       }\r
+       NewState(new, &s_mine);\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= C_Mine\r
+=\r
+===========================\r
+*/\r
+\r
+void C_Mine(objtype *ob, objtype *hit)\r
+{\r
+       if (hit->obclass == keenobj)\r
+       {\r
+               ChangeState(ob, &s_mineboom1);\r
+               SD_PlaySound(SND_MINEEXPLODE);\r
+               KillKeen();\r
+       }\r
+}\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                 PRINCESS LINDSEY\r
+\r
+temp1 = initial y position\r
+\r
+=============================================================================\r
+*/\r
+statetype s_lindsey1 = {LINDSEY1SPR, LINDSEY1SPR, stepthink, false, false, 20, 0, 0, T_Lindsey, NULL, R_Draw, &s_lindsey2};\r
+statetype s_lindsey2 = {LINDSEY2SPR, LINDSEY2SPR, stepthink, false, false, 20, 0, 0, T_Lindsey, NULL, R_Draw, &s_lindsey3};\r
+statetype s_lindsey3 = {LINDSEY3SPR, LINDSEY3SPR, stepthink, false, false, 20, 0, 0, T_Lindsey, NULL, R_Draw, &s_lindsey4};\r
+statetype s_lindsey4 = {LINDSEY4SPR, LINDSEY4SPR, stepthink, false, false, 20, 0, 0, T_Lindsey, NULL, R_Draw, &s_lindsey1};\r
+\r
+/*\r
+===========================\r
+=\r
+= SpawnLindsey\r
+=\r
+===========================\r
+*/\r
+\r
+void SpawnLindsey(Sint16 x, Sint16 y)\r
+{\r
+       GetNewObj(false);\r
+       new->obclass = lindseyobj;\r
+       new->active = ac_yes;\r
+       new->priority = 0;\r
+       new->x = CONVERT_TILE_TO_GLOBAL(x);\r
+       new->y = new->temp1 = CONVERT_TILE_TO_GLOBAL(y) - TILEGLOBAL;\r
+       new->ydir = 1;\r
+       NewState(new, &s_lindsey1);\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= T_Lindsey\r
+=\r
+===========================\r
+*/\r
+\r
+void T_Lindsey(objtype *ob)\r
+{\r
+       AccelerateY(ob, ob->ydir, 8);\r
+       if (ob->temp1 - (Sint16)ob->y > 2*PIXGLOBAL)\r
+       {\r
+               ob->ydir = 1;\r
+       }\r
+       if ((Sint16)ob->y - ob->temp1 > 2*PIXGLOBAL)\r
+       {\r
+               ob->ydir = -1;\r
+       }\r
+}\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                 DARTS\r
+\r
+temp1 = direction\r
+\r
+=============================================================================\r
+*/\r
+\r
+statetype s_dartthrower = {0, 0, step, false, false, 150, 0, 0, T_DartShoot, NULL, NULL, &s_dartthrower};\r
+statetype s_dart1       = {DARTL1SPR, DARTR1SPR, slide, false, false, 6, 64, 0, NULL, C_Lethal, R_Mshot, &s_dart2};\r
+statetype s_dart2       = {DARTL2SPR, DARTR2SPR, slide, false, false, 6, 64, 0, NULL, C_Lethal, R_Mshot, &s_dart1};\r
+statetype s_dartup1     = {DARTU1SPR, DARTU1SPR, slide, false, false, 6, 0, 64, NULL, C_Lethal, R_Mshot, &s_dartup2};\r
+statetype s_dartup2     = {DARTU2SPR, DARTU2SPR, slide, false, false, 6, 0, 64, NULL, C_Lethal, R_Mshot, &s_dartup1};\r
+statetype s_dartdown1   = {DARTD1SPR, DARTD1SPR, slide, false, false, 6, 0, 64, NULL, C_Lethal, R_Mshot, &s_dartdown2};\r
+statetype s_dartdown2   = {DARTD2SPR, DARTD2SPR, slide, false, false, 6, 0, 64, NULL, C_Lethal, R_Mshot, &s_dartdown1};\r
+\r
+/*\r
+===========================\r
+=\r
+= SpawnDartShooter\r
+=\r
+===========================\r
+*/\r
+\r
+void SpawnDartShooter(Sint16 x, Sint16 y, Sint16 dir)\r
+{\r
+       GetNewObj(false);\r
+       new->obclass = inertobj;\r
+       new->active = ac_yes;\r
+       new->x = CONVERT_TILE_TO_GLOBAL(x);\r
+       new->needtoclip = cl_noclip;\r
+       new->y = CONVERT_TILE_TO_GLOBAL(y);\r
+       new->temp1 = dir;\r
+       switch (dir)\r
+       {\r
+       case 0:\r
+               new->y -= 3*PIXGLOBAL;\r
+               new->x += 9*PIXGLOBAL;\r
+               new->shapenum = DARTU1SPR;\r
+               break;\r
+       case 1:\r
+               new->x += 8*PIXGLOBAL;\r
+               new->y += 5*PIXGLOBAL;\r
+               new->shapenum = DARTR1SPR;\r
+               break;\r
+       case 2:\r
+               new->x += 9*PIXGLOBAL;\r
+               new->shapenum = DARTD1SPR;\r
+               break;\r
+       case 3:\r
+               new->y += 7*PIXGLOBAL;\r
+               new->x -= 3*PIXGLOBAL;\r
+               new->shapenum = DARTL1SPR;\r
+               break;\r
+       }\r
+       NewState(new, &s_dartthrower);\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= T_DartShoot\r
+=\r
+===========================\r
+*/\r
+\r
+void T_DartShoot(objtype *ob)\r
+{\r
+       GetNewObj(true);\r
+       new->x = ob->x;\r
+       new->y = ob->y;\r
+       new->obclass = mshotobj;\r
+       new->active = ac_removable;\r
+       switch (ob->temp1)\r
+       {\r
+       case 0:\r
+               new->xdir = 0;\r
+               new->ydir = -1;\r
+               NewState(new, &s_dartup1);\r
+               break;\r
+       case 1:\r
+               new->xdir = 1;\r
+               new->ydir = 0;\r
+               NewState(new, &s_dart1);\r
+               break;\r
+       case 2:\r
+               new->xdir = 0;\r
+               new->ydir = 1;\r
+               NewState(new, &s_dartdown1);\r
+               break;\r
+       case 3:\r
+               new->xdir = -1;\r
+               new->ydir = 0;\r
+               NewState(new, &s_dart1);\r
+               break;\r
+       }\r
+       SD_PlaySound(SND_SHOOTDART);\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= R_DartThrower\r
+=\r
+===========================\r
+*/\r
+\r
+void R_DartThrower(objtype *ob)        //never used\r
+{\r
+       RF_PlaceSprite(&ob->sprite, ob->x, ob->y, ob->shapenum, spritedraw, ob->priority);\r
+}\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                 SCUBA GEAR\r
+\r
+=============================================================================\r
+*/\r
+statetype s_scuba = {SCUBASPR, SCUBASPR, step, false, false, 30000, 0, 0, NULL, C_Scuba, R_Draw, &s_scuba};\r
+\r
+/*\r
+===========================\r
+=\r
+= SpawnScuba\r
+=\r
+===========================\r
+*/\r
+\r
+void SpawnScuba(Sint16 x, Sint16 y)\r
+{\r
+       GetNewObj(false);\r
+       new->obclass = scubaobj;\r
+       new->active = ac_yes;\r
+       new->x = CONVERT_TILE_TO_GLOBAL(x);\r
+       new->y = CONVERT_TILE_TO_GLOBAL(y) + -TILEGLOBAL;\r
+       NewState(new, &s_scuba);\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= C_Scuba\r
+=\r
+===========================\r
+*/\r
+\r
+void C_Scuba(objtype *ob, objtype *hit)\r
+{\r
+       if (hit->obclass == keenobj && hit->hitnorth)\r
+       {\r
+               gamestate.wetsuit = true;\r
+               SD_PlaySound(SND_MAKEFOOT);\r
+               GotScuba();\r
+               RF_ForceRefresh();\r
+               playstate = ex_completed;\r
+               ob++;                   // shut up compiler\r
+       }\r
+}
\ No newline at end of file
diff --git a/16/keen456/KEEN4-6/KEEN4/K4_DEF.H b/16/keen456/KEEN4-6/KEEN4/K4_DEF.H
new file mode 100755 (executable)
index 0000000..ee7ced0
--- /dev/null
@@ -0,0 +1,511 @@
+/* Reconstructed Commander Keen 4-6 Source Code\r
+ * Copyright (C) 2021 K1n9_Duk3\r
+ *\r
+ * This file is loosely based on:\r
+ * Keen Dreams Source Code\r
+ * Copyright (C) 2014 Javier M. Chavez\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License along\r
+ * with this program; if not, write to the Free Software Foundation, Inc.,\r
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+ */\r
+\r
+#ifndef __K4_DEF__\r
+#define __K4_DEF__\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                               GLOBAL CONSTANTS\r
+\r
+=============================================================================\r
+*/\r
+\r
+#if GRMODE == CGAGR\r
+#define MINMEMORY 280000l\r
+#else\r
+#define MINMEMORY 310000l\r
+#endif\r
+\r
+#define STARPALETTE   {0, 1, 2, 3, 4, 16, 6, 7, 31, 31, 31, 31, 31, 31, 31, 31, 0}\r
+#define INTROPALETTE  {0, 24, 24, 7, 1, 1, 1, 1, 17, 17, 17, 17, 19, 19, 19, 19, 0}\r
+#define SHRINKPALETTE {0, 24, 24, 7, 1, 1, 1, 1, 17, 17, 17, 17, 19, 19, 19, 24, 0}\r
+\r
+#define HIGHSCORE_LEFT 24\r
+#define HIGHSCORE_TOP  51\r
+#define HIGHSCORE_RIGHT        296\r
+#define HIGHSCORE_MAP  19\r
+\r
+#define STATUS_PRESSKEY_X 160\r
+\r
+#define WORLDMAPNAME "Shadowlands"\r
+#define DROPSNAME "DROPS"\r
+\r
+#define STARWARSMUSIC  12\r
+#define ENDINGMUSIC    7\r
+\r
+// levels in this range can NOT be re-entered (BWB level should be > MAXDONELEVEL)\r
+#define MINDONELEVEL 1\r
+#define MAXDONELEVEL 17\r
+\r
+#define INACTIVATEDIST 4\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                               K4_SPEC DEFINITIONS\r
+\r
+=============================================================================\r
+*/\r
+\r
+extern char far swtext[];\r
+extern char far *levelnames[GAMELEVELS];\r
+extern char far *levelenter[GAMELEVELS];\r
+\r
+void ScanInfoPlane(void);\r
+void PrincessLindsey(void);\r
+void RescueJanitor(void);\r
+void CantSwim(void);\r
+void GotScuba(void);\r
+void RescuedMember(void);\r
+\r
+extern statetype s_keenswimslow1;\r
+extern statetype s_keenswimslow2;\r
+extern statetype s_keenswim1;\r
+extern statetype s_keenswim2;\r
+extern statetype s_kbubble1;\r
+extern statetype s_kbubble2;\r
+extern statetype s_kbubble3;\r
+extern statetype s_kbubble4;\r
+void SpawnSwimKeen(Sint16 x, Sint16 y);\r
+void SpawnKbubble(objtype *ob);\r
+void T_KeenSwimSlow(objtype *ob);\r
+void T_KeenSwim(objtype *ob);\r
+void C_KeenSwim(objtype *ob, objtype *hit);\r
+void R_KeenSwim(objtype *ob);\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                               K4_ACT1 DEFINITIONS\r
+\r
+=============================================================================\r
+*/\r
+\r
+extern statetype s_miragia0;\r
+extern statetype s_miragia1;\r
+extern statetype s_miragia2;\r
+extern statetype s_miragia3;\r
+extern statetype s_miragia4;\r
+extern statetype s_miragia5;\r
+extern statetype s_miragia6;\r
+extern statetype s_miragia7;\r
+void SpawnMiragia(Sint16 x, Sint16 y);\r
+void T_Miragia0(objtype *ob);\r
+void T_Miragia1(objtype *ob);\r
+void T_Miragia2(objtype *ob);\r
+void T_Miragia3(objtype *ob);\r
+void T_Miragia4(objtype *ob);\r
+void T_Miragia5(objtype *ob);\r
+void T_Miragia6(objtype *ob);\r
+void T_Miragia7(objtype *ob);\r
+\r
+extern statetype s_bonus1;\r
+extern statetype s_bonus2;\r
+extern statetype s_bonusrise;\r
+extern statetype s_splash1;\r
+extern statetype s_splash2;\r
+extern statetype s_splash3;\r
+extern Uint16 bonusshape[];\r
+void SpawnBonus(Sint16 x, Sint16 y, Sint16 type);\r
+void SpawnSplash(Sint16 x, Sint16 y);\r
+void T_Bonus(objtype *ob);\r
+\r
+extern statetype s_councilwalk1;\r
+extern statetype s_councilwalk2;\r
+extern statetype s_councilstand;\r
+void SpawnCouncil(Sint16 x, Sint16 y);\r
+void T_Council(objtype *ob);\r
+\r
+extern statetype s_slugwalk1;\r
+extern statetype s_slugwalk2;\r
+extern statetype s_slugpiss1;\r
+extern statetype s_slugstun;\r
+extern statetype s_slugstunalt;\r
+extern statetype s_slugslime;\r
+extern statetype s_slugslime2;\r
+void SpawnSlug(Sint16 x, Sint16 y);\r
+void T_Slug(objtype *ob);\r
+void T_SlugPiss(objtype *ob);\r
+void C_Slug(objtype *ob, objtype *hit);\r
+\r
+extern statetype s_mushroom1;\r
+extern statetype s_mushroom2;\r
+void SpawnMadMushroom(Sint16 x, Sint16 y);\r
+void T_Mushroom(objtype *ob);\r
+void C_Mushroom(objtype *ob, objtype *hit);\r
+void R_Mushroom(objtype *ob);\r
+\r
+extern statetype s_egg;\r
+extern statetype s_eggbroke;\r
+extern statetype s_eggchip1;\r
+extern statetype s_eggchip2;\r
+extern statetype s_eggchip3;\r
+extern statetype s_eggbirdpause;\r
+extern statetype s_eggbirdwalk1;\r
+extern statetype s_eggbirdwalk2;\r
+extern statetype s_eggbirdwalk3;\r
+extern statetype s_eggbirdwalk4;\r
+extern statetype s_eggbirdfly1;\r
+extern statetype s_eggbirdfly2;\r
+extern statetype s_eggbirdfly3;\r
+extern statetype s_eggbirdfly4;\r
+extern statetype s_eggbirddrop;\r
+extern statetype s_eggbirdstun;\r
+extern statetype s_eggbirdstun2;\r
+extern statetype s_eggbirdstun3;\r
+extern statetype s_eggbirdstun4;\r
+extern statetype s_eggbirdstun5;\r
+void SpawnEggbird(Sint16 x, Sint16 y);\r
+void T_EggUnstun(objtype *ob);\r
+void SpawnEggbirdOut(Sint16 x, Sint16 y);\r
+void C_Egg(objtype *ob, objtype *hit);\r
+void T_Eggbird(objtype *ob);\r
+void T_EggbirdFly(objtype *ob);\r
+void C_Eggbird(objtype *ob, objtype *hit);\r
+void C_EggbirdStun(objtype *ob, objtype *hit);\r
+void R_Eggbird(objtype *ob);\r
+void R_EggbirdDrop(objtype *ob);\r
+void R_Chip(objtype *ob);\r
+void R_Eggbirdfly(objtype *ob);\r
+\r
+extern statetype s_arach1;\r
+extern statetype s_arach2;\r
+extern statetype s_arach3;\r
+extern statetype s_arach4;\r
+extern statetype s_arachstun;\r
+extern statetype s_arachstun2;\r
+extern statetype s_arachstun3;\r
+extern statetype s_arachstun4;\r
+extern statetype s_arachstun5;\r
+void SpawnArachnut(Sint16 x, Sint16 y);\r
+void T_Arach(objtype *ob);\r
+void C_Arach(objtype *ob, objtype *hit);\r
+void C_ArachStun(objtype *ob, objtype *hit);\r
+\r
+extern statetype s_pestfly1;\r
+extern statetype s_pestfly2;\r
+extern statetype s_squashedpest;\r
+extern statetype s_pestrest1;\r
+extern statetype s_pestrest2;\r
+extern statetype s_pestrest3;\r
+extern statetype s_pestrest4;\r
+extern statetype s_pestrest5;\r
+extern statetype s_pestrest6;\r
+extern statetype s_pestrest7;\r
+extern statetype s_pestrest8;\r
+extern statetype s_pestrest9;\r
+extern statetype s_pestrest10;\r
+extern statetype s_pestrest11;\r
+extern statetype s_pestrest12;\r
+extern statetype s_pestrest13;\r
+extern statetype s_pestrest14;\r
+extern statetype s_pestrest15;\r
+extern statetype s_pestrest16;\r
+extern statetype s_pestrest17;\r
+void SpawnSkypest(Sint16 x, Sint16 y);\r
+void T_PestFly(objtype *ob);\r
+void C_PestFly(objtype *ob, objtype *hit);\r
+void C_Squashable(objtype *ob, objtype *hit);\r
+void T_PestRest(objtype *ob);\r
+void R_Pest(objtype *ob);\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                               K4_ACT2 DEFINITIONS\r
+\r
+=============================================================================\r
+*/\r
+\r
+extern statetype s_worm;\r
+extern statetype s_wormpeek1;\r
+extern statetype s_wormpeek2;\r
+extern statetype s_wormpeek3;\r
+extern statetype s_wormpeek4;\r
+extern statetype s_wormpeek5;\r
+extern statetype s_wormpeek6;\r
+extern statetype s_wormpeek7;\r
+extern statetype s_wormpeek8;\r
+extern statetype s_wormbite1;\r
+extern statetype s_wormbite2;\r
+extern statetype s_wormbite3;\r
+extern statetype s_wormbite4;\r
+extern statetype s_wormbite5;\r
+extern statetype s_wormstun;\r
+void SpawnWormMouth(Sint16 x, Sint16 y);\r
+void T_WormLookRight(objtype *ob);\r
+void T_WormLook(objtype *ob);\r
+void T_WormLookLeft(objtype *ob);\r
+void T_Worm(objtype *ob);\r
+void C_Worm(objtype *ob, objtype *hit);\r
+void C_WormKill(objtype *ob, objtype *hit);\r
+\r
+extern statetype s_cloudsleep;\r
+extern statetype s_cloudwake;\r
+extern statetype s_cloud;\r
+extern statetype s_cloudalign;\r
+extern statetype s_cloudcharge;\r
+extern statetype s_cloudattack1;\r
+extern statetype s_cloudattack2;\r
+extern statetype s_cloudattack3;\r
+extern statetype s_cloudattack4;\r
+extern statetype s_cloudattack5;\r
+extern statetype s_cloudattack6;\r
+extern statetype s_cloudattack7;\r
+extern statetype s_cloudattack8;\r
+extern statetype s_cloudattack9;\r
+extern statetype s_bolt1;\r
+extern statetype s_bolt2;\r
+extern statetype s_bolt3;\r
+extern statetype s_bolt4;\r
+extern statetype s_bolt5;\r
+extern statetype s_bolt6;\r
+void SpawnCloudster(Sint16 x, Sint16 y);\r
+void T_Cloud(objtype *ob);\r
+void T_CloudAlign(objtype *ob);\r
+void R_Cloud(objtype *ob);\r
+void T_CloudShoot(objtype *ob);\r
+void C_CloudSleep(objtype *ob, objtype *hit);\r
+\r
+extern statetype s_berkefloat1;\r
+extern statetype s_berkefloat2;\r
+extern statetype s_berkefloat3;\r
+extern statetype s_berkefloat4;\r
+extern statetype s_berkethrow1;\r
+extern statetype s_berkethrow2;\r
+extern statetype s_berkethrow3;\r
+extern statetype s_berkethrow4;\r
+extern statetype s_berkethrow5;\r
+extern statetype s_berkethrow6;\r
+extern statetype s_berkethrow7;\r
+extern statetype s_berkethrow8;\r
+extern statetype s_berkethrow9;\r
+extern statetype s_berkethrow10;\r
+extern statetype s_berkethrow11;\r
+extern statetype s_berkethrow12;\r
+extern statetype s_fire1;\r
+extern statetype s_fire2;\r
+extern statetype s_fireland1;\r
+extern statetype s_fireland2;\r
+extern statetype s_fireland3;\r
+extern statetype s_fireland4;\r
+extern statetype s_fireland5;\r
+extern statetype s_fireland6;\r
+extern statetype s_fireland7;\r
+extern statetype s_fireland8;\r
+extern statetype s_fireland9;\r
+void SpawnBerkeloid(Sint16 x, Sint16 y);\r
+void BerkeThink(objtype *ob);\r
+void BerkeThrowThink(objtype *ob);\r
+void BerkeThrowDone(objtype *ob);\r
+void C_Berke(objtype *ob, objtype *hit);\r
+void FireReact(objtype *ob);\r
+void BerkeDrawReact(objtype *ob);\r
+void BerkeWalkReact(objtype *ob);\r
+\r
+extern statetype s_footsmoke1;\r
+extern statetype s_footsmoke2;\r
+extern statetype s_footsmoke3;\r
+extern statetype s_footsmoke4;\r
+extern statetype s_inch1;\r
+extern statetype s_inch2;\r
+extern statetype s_footchange; //never used!\r
+extern statetype s_footwait;\r
+void SpawnInchworm(Sint16 x, Sint16 y);\r
+void SpawnFoot(Sint16 x, Sint16 y);\r
+void InchThink(objtype *ob);\r
+void InchContact(objtype *ob, objtype *hit);\r
+void FootContact(objtype *ob, objtype *hit);\r
+\r
+extern statetype s_bounderup1;\r
+extern statetype s_bounderup2;\r
+extern statetype s_bounderside1;\r
+extern statetype s_bounderside2;\r
+extern statetype s_bounderstun;\r
+extern statetype s_bounderstun2;\r
+void SpawnBounder(Sint16 x, Sint16 y);\r
+void C_Bounder(objtype *ob, objtype *hit);\r
+void R_Bounder(objtype *ob);\r
+\r
+extern statetype s_lick1;\r
+extern statetype s_lick2;\r
+extern statetype s_lick3;\r
+extern statetype s_lick4;\r
+extern statetype s_licklick1;\r
+extern statetype s_licklick2;\r
+extern statetype s_licklick3;\r
+extern statetype s_licklick4;\r
+extern statetype s_licklick5;\r
+extern statetype s_licklick6;\r
+extern statetype s_licklick7;\r
+extern statetype s_licklick8;\r
+extern statetype s_lickstun;\r
+extern statetype s_lickstun2;\r
+void SpawnLick(Sint16 x, Sint16 y);\r
+void LickJumpThink(objtype *ob);\r
+void LickContact(objtype *ob, objtype *hit);\r
+void LickKillContact(objtype *ob, objtype *hit);\r
+void LickAirReact(objtype *ob);\r
+\r
+extern statetype s_platform;\r
+void SpawnPlatform(Sint16 x, Sint16 y, Sint16 dir);\r
+void T_Platform(objtype *ob);\r
+void R_Platform(objtype *ob);\r
+\r
+extern statetype s_dropplatsit;\r
+extern statetype s_dropplatfall;\r
+extern statetype s_dropplatrise;\r
+void SpawnDropPlat(Sint16 x, Sint16 y);\r
+void T_DropPlatSit(objtype *ob);\r
+void T_DropPlatFall(objtype *ob);\r
+void T_DropPlatRise(objtype *ob);\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                               K4_ACT3 DEFINITIONS\r
+\r
+=============================================================================\r
+*/\r
+\r
+extern statetype s_eaterstand1;\r
+extern statetype s_eaterstand2;\r
+extern statetype s_eatertport1;\r
+extern statetype s_eatertport2;\r
+extern statetype s_eatertport3;\r
+extern statetype s_eatertport4;\r
+extern statetype s_eatertport5;\r
+extern statetype s_eatertport6;\r
+extern statetype s_eatertport7;\r
+extern statetype s_eatertport8;\r
+extern statetype s_eaterjump1;\r
+extern statetype s_eaterjump2;\r
+extern statetype s_eaterjump3;\r
+extern statetype s_eaterjump4;\r
+extern statetype s_eaterstun;\r
+extern statetype s_eaterstun2;\r
+extern statetype s_eatenbonus1;\r
+extern statetype s_eatenbonus2;\r
+extern statetype s_eatenbonus3;\r
+extern statetype s_eatenbonus4;\r
+void SpawnEater(Sint16 x, Sint16 y);\r
+void T_EaterJump(objtype *ob);\r
+void T_EaterTeleport(objtype *ob);\r
+void C_Eater(objtype *ob, objtype *hit);\r
+void EaterInTile(objtype *ob);\r
+void R_EaterAir(objtype *ob);\r
+\r
+extern statetype s_mimrock;\r
+extern statetype s_mimsneak1;\r
+extern statetype s_mimsneak2;\r
+extern statetype s_mimsneak3;\r
+extern statetype s_mimsneak4;\r
+extern statetype s_mimsneak5;\r
+extern statetype s_mimsneak6;\r
+extern statetype s_mimbonk1;\r
+extern statetype s_mimbonk2;\r
+extern statetype s_mimbonk3;\r
+extern statetype s_mimbounce;\r
+extern statetype s_mimstun;\r
+extern statetype s_mimstun2;\r
+void SpawnMimrock(Sint16 x, Sint16 y);\r
+void T_MimrockWait(objtype *ob);\r
+void T_MimrockSneak(objtype *ob);\r
+void C_Mimrock(objtype *ob, objtype *hit);\r
+void C_MimLethal(objtype *ob, objtype *hit);\r
+void R_MimAir(objtype *ob);\r
+void R_MimBounce(objtype *ob);\r
+\r
+extern statetype s_dopefish1;\r
+extern statetype s_dopefish2;\r
+extern statetype s_dopeattack;\r
+extern statetype s_dopeeat;\r
+extern statetype s_dopeburp1;\r
+extern statetype s_dopeburp2;\r
+extern statetype s_dopereturn;\r
+extern statetype s_dopefood;\r
+extern statetype s_keendopefood;\r
+extern statetype s_keendieslow;\r
+extern statetype s_bubble1;\r
+extern statetype s_bubble2;\r
+extern statetype s_bubble3;\r
+extern statetype s_bubble4;\r
+void SpawnDopefish(Sint16 x, Sint16 y);\r
+void T_EatenKeen(objtype *ob);\r
+void T_Dope(objtype *ob);\r
+void T_DopeHunt(objtype *ob);\r
+void T_DopeReturn(objtype *ob);\r
+void T_Burp(objtype *ob);\r
+void T_Bubble(objtype *ob);\r
+void C_Dope(objtype *ob, objtype *hit);\r
+void R_Fish(objtype *ob);\r
+\r
+extern statetype s_schoolfish1;\r
+extern statetype s_schoolfish2;\r
+void SpawnSchoolfish(Sint16 x, Sint16 y);\r
+void T_SchoolFish(objtype *ob);\r
+\r
+extern statetype s_pixie;\r
+extern statetype s_pixielook;\r
+extern statetype s_pixieshoot;\r
+extern statetype s_pixieshoot2;\r
+extern statetype s_pixiefire1;\r
+extern statetype s_pixiefire2;\r
+extern statetype s_pixiefire3;\r
+extern statetype s_pixiefire4;\r
+void SpawnPixie(Sint16 x, Sint16 y);\r
+void T_Pixie(objtype *ob);\r
+void T_PixieCheck(objtype *ob);\r
+void T_PixieShoot(objtype *ob);\r
+void R_Mshot(objtype *ob);\r
+\r
+extern statetype s_mine;\r
+extern statetype s_mineboom1;\r
+extern statetype s_mineboom2;\r
+void SpawnMine(Sint16 x, Sint16 y, Sint16 dir);\r
+void C_Mine(objtype *ob, objtype *hit);\r
+\r
+extern statetype s_lindsey1;\r
+extern statetype s_lindsey2;\r
+extern statetype s_lindsey3;\r
+extern statetype s_lindsey4;\r
+void SpawnLindsey(Sint16 x, Sint16 y);\r
+void T_Lindsey(objtype *ob);\r
+\r
+extern statetype s_dartthrower;\r
+extern statetype s_dart1;\r
+extern statetype s_dart2;\r
+extern statetype s_dartup1;\r
+extern statetype s_dartup2;\r
+extern statetype s_dartdown1;\r
+extern statetype s_dartdown2;\r
+void SpawnDartShooter(Sint16 x, Sint16 y, Sint16 dir);\r
+void T_DartShoot(objtype *ob);\r
+void R_DartThrower(objtype *ob);\r
+\r
+extern statetype s_scuba;\r
+void SpawnScuba(Sint16 x, Sint16 y);\r
+void C_Scuba(objtype *ob, objtype *hit);\r
+\r
+#endif
\ No newline at end of file
diff --git a/16/keen456/KEEN4-6/KEEN4/K4_SPEC.C b/16/keen456/KEEN4-6/KEEN4/K4_SPEC.C
new file mode 100755 (executable)
index 0000000..45c2861
--- /dev/null
@@ -0,0 +1,1305 @@
+/* Reconstructed Commander Keen 4-6 Source Code\r
+ * Copyright (C) 2021 K1n9_Duk3\r
+ *\r
+ * This file is loosely based on:\r
+ * Keen Dreams Source Code\r
+ * Copyright (C) 2014 Javier M. Chavez\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License along\r
+ * with this program; if not, write to the Free Software Foundation, Inc.,\r
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+ */\r
+\r
+/*\r
+K4_SPEC.C\r
+=========\r
+\r
+Contains (in this order):\r
+\r
+- lump definition\r
+- "Star Wars" crawl text\r
+- level names & messages\r
+- ScanInfoPlane() - for spawning the level objects and marking required sprites\r
+- messages for Lindsey, Janitor, Oracle Members and more\r
+\r
+- actor states & implementation for swimming Keen\r
+\r
+*/\r
+\r
+#include "CK_DEF.H"\r
+\r
+enum {\r
+       CONTROLS_LUMP,     //  0\r
+       KEEN_LUMP,         //  1\r
+       SUGAR1_LUMP,       //  2\r
+       SUGAR2_LUMP,       //  3\r
+       SUGAR3_LUMP,       //  4\r
+       SUGAR4_LUMP,       //  5\r
+       SUGAR5_LUMP,       //  6\r
+       SUGAR6_LUMP,       //  7\r
+       ONEUP_LUMP,        //  8\r
+       AMMO_LUMP,         //  9\r
+       WOLRDKEEN_LUMP,    // 10\r
+       SLUG_LUMP,         // 11\r
+       MADMUSHROOM_LUMP,  // 12\r
+       UNUSED1_LUMP,      // 13\r
+       LINDSEY_LUMP,      // 14\r
+       INCHWORM_LUMP,     // 15\r
+       EATER_LUMP,        // 16\r
+       COUNCIL_LUMP,      // 17\r
+       EGGBIRD_LUMP,      // 18\r
+       MIMROCK_LUMP,      // 19\r
+       DOPEFISH_LUMP,     // 20\r
+       SCHOOLFISH_LUMP,   // 21\r
+       ARACHNUT_LUMP,     // 22\r
+       SKYPEST_LUMP,      // 23\r
+       WORMOUTH_LUMP,     // 24\r
+       LICK_LUMP,         // 25\r
+       PLATFORM_LUMP,     // 26\r
+       BOUNDER_LUMP,      // 27\r
+       THUNDERCLOUD_LUMP, // 28\r
+       BERKELOID_LUMP,    // 29\r
+       KEYGEM_LUMP,       // 30\r
+       DARTS_LUMP,        // 31\r
+       SCUBAKEEN_LUMP,    // 32\r
+       SPRITE_LUMP,       // 33\r
+       MINE_LUMP,         // 34\r
+       MOON_LUMP,         // 35\r
+       EGG_LUMP,          // 36\r
+       NUMLUMPS           // 37\r
+};\r
+\r
+Uint16 lumpstart[NUMLUMPS] = {\r
+       CONTROLS_LUMP_START,\r
+       KEEN_LUMP_START,\r
+       SUGAR1_LUMP_START,\r
+       SUGAR2_LUMP_START,\r
+       SUGAR3_LUMP_START,\r
+       SUGAR4_LUMP_START,\r
+       SUGAR5_LUMP_START,\r
+       SUGAR6_LUMP_START,\r
+       ONEUP_LUMP_START,\r
+       AMMO_LUMP_START,\r
+       WORLDKEEN_LUMP_START,\r
+       SLUG_LUMP_START,\r
+       MADMUSHROOM_LUMP_START,\r
+       0,\r
+       LINDSEY_LUMP_START,\r
+       INCHWORM_LUMP_START,\r
+       EATER_LUMP_START,\r
+       COUNCIL_LUMP_START,\r
+       EGGBIRD_LUMP_START,\r
+       MIMROCK_LUMP_START,\r
+       DOPEFISH_LUMP_START,\r
+       SCHOOLFISH_LUMP_START,\r
+       ARACHNUT_LUMP_START,\r
+       SKYPEST_LUMP_START,\r
+       WORMOUTH_LUMP_START,\r
+       LICK_LUMP_START,\r
+       PLATFORM_LUMP_START,\r
+       BOUNDER_LUMP_START,\r
+       THUNDERCLOUD_LUMP_START,\r
+       BERKELOID_LUMP_START,\r
+       KEYGEM_LUMP_START,\r
+       DARTS_LUMP_START,\r
+       SCUBAKEEN_LUMP_START,\r
+       SPRITE_LUMP_START,\r
+       MINE_LUMP_START,\r
+       MOON_LUMP_START,\r
+       EGG_LUMP_START\r
+};\r
+\r
+Uint16 lumpend[NUMLUMPS] = {\r
+       CONTROLS_LUMP_END,\r
+       KEEN_LUMP_END,\r
+       SUGAR1_LUMP_END,\r
+       SUGAR2_LUMP_END,\r
+       SUGAR3_LUMP_END,\r
+       SUGAR4_LUMP_END,\r
+       SUGAR5_LUMP_END,\r
+       SUGAR6_LUMP_END,\r
+       ONEUP_LUMP_END,\r
+       AMMO_LUMP_END,\r
+       WORLDKEEN_LUMP_END,\r
+       SLUG_LUMP_END,\r
+       MADMUSHROOM_LUMP_END,\r
+       0,\r
+       LINDSEY_LUMP_END,\r
+       INCHWORM_LUMP_END,\r
+       EATER_LUMP_END,\r
+       COUNCIL_LUMP_END,\r
+       EGGBIRD_LUMP_END,\r
+       MIMROCK_LUMP_END,\r
+       DOPEFISH_LUMP_END,\r
+       SCHOOLFISH_LUMP_END,\r
+       ARACHNUT_LUMP_END,\r
+       SKYPEST_LUMP_END,\r
+       WORMOUTH_LUMP_END,\r
+       LICK_LUMP_END,\r
+       PLATFORM_LUMP_END,\r
+       BOUNDER_LUMP_END,\r
+       THUNDERCLOUD_LUMP_END,\r
+       BERKELOID_LUMP_END,\r
+       KEYGEM_LUMP_END,\r
+       DARTS_LUMP_END,\r
+       SCUBAKEEN_LUMP_END,\r
+       SPRITE_LUMP_END,\r
+       MINE_LUMP_END,\r
+       MOON_LUMP_END,\r
+       EGG_LUMP_END\r
+};\r
+\r
+boolean lumpneeded[NUMLUMPS];\r
+\r
+#if GRMODE == EGAGR\r
+\r
+char far swtext[] =\r
+       "Episode Four\n"\r
+       "\n"\r
+       "Secret of the Oracle\n"\r
+       "\n"\r
+       "After delivering a\n"\r
+       "crippling blow to the\n"\r
+       "plans of Mortimer\n"\r
+       "McMire and receiving\n"\r
+       "the praise of the\n"\r
+       "Vorticon race,\n"\r
+       "Commander Keen\n"\r
+       "returned to his home in\n"\r
+       "the suburbs.\n"\r
+       "\n"\r
+       "Here he was forced to\n"\r
+       "go to bed at an early\n"\r
+       "hour, and to eat mashed\n"\r
+       "potatoes.\n"\r
+       "\n"\r
+       "Months later, Billy\n"\r
+       "tinkered around with\n"\r
+       "his latest invention,\n"\r
+       "the Photachyon\n"\r
+       "Transceiver, or faster-\n"\r
+       "than-light radio. After\n"\r
+       "picking up a lot of bad\n"\r
+       "alien sitcoms, he\n"\r
+       "stumbled upon a strange\n"\r
+       "message of terrible\n"\r
+       "importance....\n";\r
+\r
+#endif\r
+\r
+char far l0n[] = "Shadowlands";\r
+char far l1n[] = "Border Village";\r
+char far l2n[] = "Slug Village";\r
+char far l3n[] = "The Perilous Pit";\r
+char far l4n[] = "Cave of the Descendents";\r
+char far l5n[] = "Chasm of Chills";\r
+char far l6n[] = "Crystalus";\r
+char far l7n[] = "Hillville";\r
+char far l8n[] = "Sand Yego";\r
+char far l9n[] = "Miragia";\r
+char far l10n[] = "Lifewater Oasis";\r
+char far l11n[] = "Pyramid of the Moons";\r
+char far l12n[] = "Pyramid of Shadows";\r
+char far l13n[] = "Pyramid of the\nGnosticene Ancients";\r
+char far l14n[] = "Pyramid of the Forbidden";\r
+char far l15n[] = "Isle of Tar";\r
+char far l16n[] = "Isle of Fire";\r
+char far l17n[] = "Well of Wishes";\r
+char far l18n[] = "Bean-with-Bacon\nMegarocket";\r
+\r
+char far l0e[] = "Keen enters the\nShadowlands";\r
+char far l1e[] = "Keen makes a run for\nthe Border Village";\r
+char far l2e[] = "Keen slips into\nSlug Village";\r
+char far l3e[] = "Keen plummets into\nthe The Perilous Pit";   // sic!\r
+char far l4e[] = "Keen plods down into\nthe Cave of the\nDescendents";\r
+char far l5e[] = "Keen shivers along\nthe Chasm of Chills";\r
+char far l6e[] = "Keen reflects upon\nentering Crystalus";\r
+char far l7e[] = "Keen stumbles upon\nHillville";\r
+char far l8e[] = "Keen grits his teeth\nand enters Sand Yego";\r
+char far l9e[] = "Keen disappears into\nMiragia";\r
+char far l10e[] = "Keen crawls into\nLifewater Oasis";\r
+char far l11e[] = "Keen backs into the\nPyramid of the Moons";\r
+char far l12e[] = "Keen move silently in\nthe Pyramid of Shadows";     // sic!\r
+char far l13e[] = "Keen reverently enters\nthe Pyramid of the\nGnosticene Ancients";\r
+char far l14e[] = "Keen hesitantly crosses\ninto the Pyramid of the\nForbidden";\r
+char far l15e[] = "Keen mucks along the\nIsle of Tar";\r
+char far l16e[] = "Keen blazes across the\nIsle of Fire";\r
+char far l17e[] = "Keen hopefully enters\nthe Well of Wishes";\r
+char far l18e[] = "Keen launches into the\nBean-with-Bacon\nMegarocket";\r
+\r
+char far *levelnames[GAMELEVELS] = {\r
+       l0n,\r
+       l1n,\r
+       l2n,\r
+       l3n,\r
+       l4n,\r
+       l5n,\r
+       l6n,\r
+       l7n,\r
+       l8n,\r
+       l9n,\r
+       l10n,\r
+       l11n,\r
+       l12n,\r
+       l13n,\r
+       l14n,\r
+       l15n,\r
+       l16n,\r
+       l17n,\r
+       l18n\r
+};\r
+\r
+char far *levelenter[GAMELEVELS] = {\r
+       l0e,\r
+       l1e,\r
+       l2e,\r
+       l3e,\r
+       l4e,\r
+       l5e,\r
+       l6e,\r
+       l7e,\r
+       l8e,\r
+       l9e,\r
+       l10e,\r
+       l11e,\r
+       l12e,\r
+       l13e,\r
+       l14e,\r
+       l15e,\r
+       l16e,\r
+       l17e,\r
+       l18e\r
+};\r
+\r
+Uint16 bonuslump[] = {\r
+       KEYGEM_LUMP, KEYGEM_LUMP, KEYGEM_LUMP, KEYGEM_LUMP,\r
+       SUGAR1_LUMP, SUGAR2_LUMP, SUGAR3_LUMP,\r
+       SUGAR4_LUMP, SUGAR5_LUMP, SUGAR6_LUMP,\r
+       ONEUP_LUMP, AMMO_LUMP\r
+};\r
+\r
+//==========================================================================\r
+\r
+/*\r
+==========================\r
+=\r
+= ScanInfoPlane\r
+=\r
+= Spawn all actors and mark down special places\r
+=\r
+==========================\r
+*/\r
+\r
+void ScanInfoPlane(void)\r
+{\r
+       objtype *ob;\r
+       Uint16 i, x, y, chunk;\r
+       Sint16 info;\r
+       Uint16 far *map;\r
+\r
+       InitObjArray();                  // start spawning things with a clean slate\r
+\r
+       memset(lumpneeded, 0, sizeof(lumpneeded));\r
+       map = mapsegs[2];\r
+\r
+       for (y=0; y<mapheight; y++)\r
+       {\r
+               for (x=0; x<mapwidth; x++)\r
+               {\r
+                       info = *map++;\r
+\r
+                       if (info == 0)\r
+                               continue;\r
+\r
+                       switch (info)\r
+                       {\r
+                       case 1:\r
+                               SpawnKeen(x, y, 1);\r
+                               SpawnScore();\r
+                               lumpneeded[KEEN_LUMP] = true;\r
+                               CA_MarkGrChunk(SCOREBOXSPR);\r
+                               break;\r
+\r
+                       case 2:\r
+                               SpawnKeen(x, y, -1);\r
+                               SpawnScore();\r
+                               lumpneeded[KEEN_LUMP] = true;\r
+                               CA_MarkGrChunk(SCOREBOXSPR);\r
+                               break;\r
+\r
+                       case 3:\r
+                               SpawnWorldKeen(x, y);\r
+                               SpawnScore();\r
+                               lumpneeded[WOLRDKEEN_LUMP] = true;\r
+                               CA_MarkGrChunk(SCOREBOXSPR);\r
+                               break;\r
+\r
+                       case 4:\r
+                               SpawnCouncil(x, y);\r
+                               lumpneeded[COUNCIL_LUMP] = true;\r
+                               break;\r
+\r
+                       case 50:\r
+                               if (gamestate.difficulty < gd_Hard)\r
+                                       break;\r
+                       case 49:\r
+                               if (gamestate.difficulty < gd_Normal)\r
+                                       break;\r
+                       case 5:\r
+                               SpawnBerkeloid(x, y);\r
+                               lumpneeded[BERKELOID_LUMP] = true;\r
+                               break;\r
+\r
+                       case 6:\r
+                               SpawnLindsey(x, y);\r
+                               lumpneeded[LINDSEY_LUMP] = true;\r
+                               break;\r
+\r
+                       case 52:\r
+                               if (gamestate.difficulty < gd_Hard)\r
+                                       break;\r
+                       case 51:\r
+                               if (gamestate.difficulty < gd_Normal)\r
+                                       break;\r
+                       case 7:\r
+                               SpawnWormMouth(x, y);\r
+                               lumpneeded[WORMOUTH_LUMP] = true;\r
+                               break;\r
+\r
+                       case 46:\r
+                               if (gamestate.difficulty < gd_Hard)\r
+                                       break;\r
+                       case 45:\r
+                               if (gamestate.difficulty < gd_Normal)\r
+                                       break;\r
+                       case 8:\r
+                               SpawnSkypest(x, y);\r
+                               lumpneeded[SKYPEST_LUMP] = true;\r
+                               break;\r
+\r
+                       case 9:\r
+                               SpawnCloudster(x, y);\r
+                               lumpneeded[THUNDERCLOUD_LUMP] = true;\r
+                               break;\r
+\r
+                       case 10:\r
+                               SpawnFoot(x, y);\r
+                               lumpneeded[INCHWORM_LUMP] = true;       // lump includes the foot sprite\r
+                               // Note: The smoke sprites aren't actually required for the foot!\r
+                               for (i=SMOKE1SPR; i<=SMOKE4SPR; i++)\r
+                               {\r
+                                       CA_MarkGrChunk(i);\r
+                               }\r
+                               break;\r
+\r
+                       case 11:\r
+                               SpawnInchworm(x, y);\r
+                               lumpneeded[INCHWORM_LUMP] = true;\r
+                               for (i=SMOKE1SPR; i<=SMOKE4SPR; i++)\r
+                               {\r
+                                       CA_MarkGrChunk(i);\r
+                               }\r
+                               break;\r
+\r
+                       case 12:\r
+                               SpawnBounder(x, y);\r
+                               lumpneeded[BOUNDER_LUMP] = true;\r
+                               break;\r
+\r
+                       case 13:\r
+                               SpawnEggbird(x, y);\r
+                               lumpneeded[EGGBIRD_LUMP] = true;\r
+                               lumpneeded[EGG_LUMP] = true;\r
+                               break;\r
+\r
+                       case 48:\r
+                               if (gamestate.difficulty < gd_Hard)\r
+                                       break;\r
+                       case 47:\r
+                               if (gamestate.difficulty < gd_Normal)\r
+                                       break;\r
+                       case 14:\r
+                               SpawnLick(x, y);\r
+                               lumpneeded[LICK_LUMP] = true;\r
+                               break;\r
+\r
+                       case 88:\r
+                               if (gamestate.difficulty < gd_Hard)\r
+                                       break;\r
+                       case 87:\r
+                               if (gamestate.difficulty < gd_Normal)\r
+                                       break;\r
+                       case 15:\r
+                               SpawnDopefish(x, y);\r
+                               lumpneeded[DOPEFISH_LUMP] = true;\r
+                               break;\r
+\r
+                       case 16:\r
+                               SpawnSchoolfish(x, y);\r
+                               lumpneeded[SCHOOLFISH_LUMP] = true;\r
+                               break;\r
+\r
+                       case 24:\r
+                               if (gamestate.difficulty < gd_Hard)\r
+                                       break;\r
+                       case 23:\r
+                               if (gamestate.difficulty < gd_Normal)\r
+                                       break;\r
+                       case 17:\r
+                               SpawnPixie(x, y);\r
+                               lumpneeded[SPRITE_LUMP] = true;\r
+                               break;\r
+\r
+                       case 18:\r
+                               SpawnEater(x, y);\r
+                               lumpneeded[EATER_LUMP] = true;\r
+                               break;\r
+\r
+                       case 19:\r
+                               SpawnMimrock(x, y);\r
+                               lumpneeded[MIMROCK_LUMP] = true;\r
+                               break;\r
+\r
+                       case 74:\r
+                               if (gamestate.difficulty < gd_Hard)\r
+                                       break;\r
+                       case 73:\r
+                               if (gamestate.difficulty < gd_Normal)\r
+                                       break;\r
+                       case 20:\r
+                               SpawnArachnut(x, y);\r
+                               lumpneeded[ARACHNUT_LUMP] = true;\r
+                               break;\r
+\r
+                       case 21:\r
+                               SpawnMadMushroom(x, y);\r
+                               lumpneeded[MADMUSHROOM_LUMP] = true;\r
+                               break;\r
+\r
+                       case 44:\r
+                               if (gamestate.difficulty < gd_Hard)\r
+                                       break;\r
+                       case 43:\r
+                               if (gamestate.difficulty < gd_Normal)\r
+                                       break;\r
+                       case 22:\r
+                               SpawnSlug(x, y);\r
+                               lumpneeded[SLUG_LUMP] = true;\r
+                               break;\r
+\r
+                       case 25:\r
+                               RF_SetScrollBlock(x, y, 1);\r
+                               break;\r
+\r
+                       case 26:\r
+                               RF_SetScrollBlock(x, y, 0);\r
+                               break;\r
+\r
+                       case 27:\r
+                       case 28:\r
+                       case 29:\r
+                       case 30:\r
+                               SpawnPlatform(x, y, info-27);\r
+                               lumpneeded[PLATFORM_LUMP] = true;;\r
+                               break;\r
+\r
+                       case 32:\r
+                               SpawnDropPlat(x, y);\r
+                               lumpneeded[PLATFORM_LUMP] = true;\r
+                               break;\r
+\r
+                       case 33:\r
+                               SpawnMiragia(x, y);\r
+                               break;\r
+\r
+                       case 34:\r
+                               if (gamestate.ammo < 5)\r
+                               {\r
+                                       SpawnBonus(x, y, 11);\r
+                                       lumpneeded[bonuslump[11]] = true;\r
+                               }\r
+                               break;\r
+\r
+                       case 35:\r
+                               SpawnScuba(x, y);\r
+                               CA_MarkGrChunk(SCUBASPR);\r
+                               break;\r
+\r
+                       case 42:\r
+                               SpawnSwimKeen(x, y);\r
+                               SpawnScore();\r
+                               lumpneeded[SCUBAKEEN_LUMP] = true;\r
+                               //mark pickup shapes:\r
+                               for (i=BONUS100SPR; i<=BONUSCLIPSPR; i++)\r
+                               {\r
+                                       CA_MarkGrChunk(i);\r
+                               }\r
+                               CA_MarkGrChunk(SCOREBOXSPR);\r
+                               break;\r
+\r
+                       case 83:\r
+                       case 84:\r
+                       case 85:\r
+                       case 86:\r
+                               if (gamestate.difficulty < gd_Hard)\r
+                                       break;\r
+                               SpawnDartShooter(x, y, info-83);\r
+                               lumpneeded[DARTS_LUMP] = true;\r
+                               break;\r
+\r
+                       case 79:\r
+                       case 80:\r
+                       case 81:\r
+                       case 82:\r
+                               if (gamestate.difficulty < gd_Normal)\r
+                                       break;\r
+                               SpawnDartShooter(x, y, info-79);\r
+                               lumpneeded[DARTS_LUMP] = true;\r
+                               break;\r
+\r
+                       case 53:\r
+                       case 54:\r
+                       case 55:\r
+                       case 56:\r
+                               SpawnDartShooter(x, y, info-53);\r
+                               lumpneeded[DARTS_LUMP] = true;\r
+                               break;\r
+\r
+                       case 57:\r
+                       case 58:\r
+                       case 59:\r
+                       case 60:\r
+                       case 61:\r
+                       case 62:\r
+                       case 63:\r
+                       case 64:\r
+                       case 65:\r
+                       case 66:\r
+                       case 67:\r
+                       case 68:\r
+                               SpawnBonus(x, y, info-57);\r
+                               lumpneeded[bonuslump[info-57]] = true;\r
+                               break;\r
+\r
+                       case 69:\r
+                       case 70:\r
+                       case 71:\r
+                       case 72:\r
+                               SpawnMine(x, y, info-69);\r
+                               lumpneeded[MINE_LUMP] = true;\r
+                               break;\r
+\r
+                       case 75:\r
+                               lumpneeded[MOON_LUMP] = true;\r
+                               break;\r
+\r
+                       case 78:\r
+                               if (gamestate.difficulty < gd_Hard)\r
+                                       break;\r
+                       case 77:\r
+                               if (gamestate.difficulty < gd_Normal)\r
+                                       break;\r
+                       case 76:\r
+                               SpawnEggbirdOut(x, y);\r
+                               lumpneeded[EGGBIRD_LUMP] = true;\r
+                               break;\r
+                       }\r
+               }\r
+       }\r
+\r
+       for (ob = player; ob; ob = ob->next)\r
+       {\r
+               if (ob->active != ac_allways)\r
+                       ob->active = ac_no;\r
+       }\r
+\r
+       for (i = 0; i < NUMLUMPS; i++)\r
+       {\r
+               if (lumpneeded[i])\r
+               {\r
+                       for (chunk = lumpstart[i]; chunk <= lumpend[i]; chunk++)\r
+                       {\r
+                               CA_MarkGrChunk(chunk);\r
+                       }\r
+               }\r
+       }\r
+}\r
+\r
+//============================================================================\r
+\r
+/*\r
+===========================\r
+=\r
+= PrincessLindsey\r
+=\r
+===========================\r
+*/\r
+\r
+char *lindseytext[2] =\r
+{\r
+       "There's gear to help\n"\r
+       "you swim in Three-Tooth\n"\r
+       "Lake. It is hidden in\n"\r
+       "Miragia.\n"\r
+       ,\r
+       "The way to the Pyramid\n"\r
+       "of the Forbidden lies\n"\r
+       "under the Pyramid of\n"\r
+       "Moons.\n"\r
+};\r
+\r
+char *klindseytext[2] =\r
+{\r
+       "Thanks, your Highness!"\r
+       ,\r
+       "Thanks for the\n"\r
+       "mysterious clue,\n"\r
+       "Princess!\n"\r
+};\r
+\r
+void PrincessLindsey(void)\r
+{\r
+       SD_WaitSoundDone();\r
+       StopMusic();\r
+       CA_UpLevel();\r
+       CA_MarkGrChunk(LINDSEYPIC);\r
+       CA_MarkGrChunk(KEENTALK1PIC);\r
+       CA_MarkGrChunk(KEENTALK2PIC);\r
+       CA_CacheMarks(NULL);\r
+       VW_FixRefreshBuffer();\r
+\r
+       US_CenterWindow(26, 8);\r
+       VWB_DrawPic(WindowX, WindowY, LINDSEYPIC);\r
+       PrintY += 6;\r
+       WindowW -= 48;\r
+       WindowX += 48;\r
+       US_CPrint("Princess Lindsey says:\n");\r
+       if (mapon == 7)\r
+       {\r
+               US_CPrint(lindseytext[0]);\r
+       }\r
+       else\r
+       {\r
+               US_CPrint(lindseytext[1]);\r
+       }\r
+       VW_UpdateScreen();\r
+       SD_PlaySound(SND_MAKEFOOT);\r
+       VW_WaitVBL(60);\r
+       IN_ClearKeysDown();\r
+       IN_Ack();\r
+\r
+       US_CenterWindow(26, 8);\r
+       VWB_DrawPic(WindowX+WindowW-48, WindowY, KEENTALK1PIC);\r
+       WindowW -= 48;\r
+       PrintY += 12;\r
+       if (mapon == 7)\r
+       {\r
+               US_CPrint(klindseytext[0]);\r
+       }\r
+       else\r
+       {\r
+               US_CPrint(klindseytext[1]);\r
+       }\r
+       VW_UpdateScreen();\r
+       VW_WaitVBL(30);\r
+       IN_ClearKeysDown();\r
+       IN_Ack();\r
+\r
+       VWB_DrawPic(WindowX+WindowW, WindowY, KEENTALK2PIC);\r
+       VW_UpdateScreen();\r
+       VW_WaitVBL(30);\r
+       IN_ClearKeysDown();\r
+       IN_Ack();\r
+\r
+       CA_DownLevel();\r
+       StartMusic(gamestate.mapon);\r
+\r
+       //reset scorebox (sprite may have been re-cached by CA_DownLevel)\r
+       scoreobj->temp2 = -1;\r
+       scoreobj->temp1 = -1;\r
+       scoreobj->temp3 = -1;\r
+       scoreobj->temp4 = -1;\r
+}\r
+\r
+//============================================================================\r
+\r
+/*\r
+===========================\r
+=\r
+= RescueJanitor\r
+=\r
+===========================\r
+*/\r
+\r
+char far jantext1[] =\r
+       "Thanks for going to all\n"\r
+       "that trouble, but I'm\n"\r
+       "just the janitor for the\n"\r
+       "High Council.";\r
+\r
+char far jantext2[] =\r
+       "I tried to tell the\n"\r
+       "Shikadi that but they\n"\r
+       "just wouldn't listen...";\r
+\r
+char far keenjantext[] =\r
+       "This had better\n"\r
+       "be a joke.";\r
+\r
+char far jantext3[] =\r
+       "Sorry.  You aren't\n"\r
+       "mad, are you?";\r
+\r
+void RescueJanitor(void)\r
+{\r
+       char str[200];\r
+\r
+       SD_WaitSoundDone();\r
+       CA_UpLevel();\r
+       CA_MarkGrChunk(ORACLEPIC);\r
+       CA_MarkGrChunk(KEENTALK1PIC);\r
+       CA_MarkGrChunk(KEENMADPIC);\r
+       CA_CacheMarks(NULL);\r
+       VW_FixRefreshBuffer();\r
+       StartMusic(-1);\r
+       \r
+       US_CenterWindow(26, 8);\r
+       VWB_DrawPic(WindowX, WindowY, ORACLEPIC);\r
+       PrintY += 6;\r
+       WindowW -= 48;\r
+       WindowX += 48;\r
+       _fstrcpy(str, jantext1);\r
+       US_CPrint(str);\r
+       VW_UpdateScreen();\r
+       VW_WaitVBL(60);\r
+       IN_ClearKeysDown();\r
+       IN_Ack();\r
+\r
+       US_CenterWindow(26, 8);\r
+       VWB_DrawPic(WindowX, WindowY, ORACLEPIC);\r
+       PrintY += 6;\r
+       WindowW -= 48;\r
+       WindowX += 48;\r
+       _fstrcpy(str, jantext2);\r
+       US_CPrint(str);\r
+       VW_UpdateScreen();\r
+       VW_WaitVBL(60);\r
+       IN_ClearKeysDown();\r
+       IN_Ack();\r
+\r
+       US_CenterWindow(26, 8);\r
+       VWB_DrawPic(WindowX+WindowW-48, WindowY, KEENTALK1PIC);\r
+       WindowW -= 48;\r
+       PrintY += 12;\r
+       _fstrcpy(str, keenjantext);\r
+       US_CPrint(str);\r
+       VW_UpdateScreen();\r
+       VW_WaitVBL(60);\r
+       IN_ClearKeysDown();\r
+       IN_Ack();\r
+\r
+       US_CenterWindow(26, 8);\r
+       VWB_DrawPic(WindowX, WindowY, ORACLEPIC);\r
+       PrintY += 6;\r
+       WindowW -= 48;\r
+       WindowX += 48;\r
+       _fstrcpy(str, jantext3);\r
+       US_CPrint(str);\r
+       VW_UpdateScreen();\r
+       VW_WaitVBL(60);\r
+       IN_ClearKeysDown();\r
+       IN_Ack();\r
+\r
+       US_CenterWindow(26, 8);\r
+       VWB_DrawPic(WindowX+WindowW-48, WindowY, KEENTALK1PIC);\r
+       VWB_DrawPic(WindowX+WindowW-40, WindowY+24, KEENMADPIC);\r
+       VW_UpdateScreen();\r
+       VW_WaitVBL(30);\r
+       IN_ClearKeysDown();\r
+       IN_Ack();\r
+\r
+       StopMusic();\r
+       CA_DownLevel();\r
+       StartMusic(gamestate.mapon);\r
+\r
+       //BUG: scorebox needs to be reset here (sprite may have been re-cached by CA_DownLevel)\r
+}\r
+\r
+//============================================================================\r
+\r
+/*\r
+===========================\r
+=\r
+= CanitSwim\r
+=\r
+===========================\r
+*/\r
+\r
+void CantSwim(void)\r
+{\r
+       SD_WaitSoundDone();\r
+       CA_UpLevel();   // kinda useless without CA_CacheMarks or CA_SetGrPurge\r
+       // BUG: haven't made anything purgable here, caching the pic may cause an "out of memory" crash\r
+       CA_CacheGrChunk(KEENTALK1PIC);\r
+\r
+       US_CenterWindow(26, 8);\r
+       VWB_DrawPic(WindowX+WindowW-48, WindowY, KEENTALK1PIC);\r
+       WindowW -= 48;\r
+       PrintY += 12;\r
+       US_CPrint("I can't swim!");\r
+       VW_UpdateScreen();\r
+       VW_WaitVBL(30);\r
+       IN_ClearKeysDown();\r
+       IN_Ack();\r
+       CA_DownLevel();\r
+\r
+       //Note: scorebox sprite has not been re-cached here (didn't use CA_CacheMarks or anything else that would have made the sprite purgable)\r
+}\r
+\r
+//============================================================================\r
+\r
+/*\r
+===========================\r
+=\r
+= GotScuba\r
+=\r
+===========================\r
+*/\r
+\r
+void GotScuba(void)\r
+{\r
+       SD_WaitSoundDone();\r
+       CA_UpLevel();\r
+       CA_MarkGrChunk(KEENTALK1PIC);\r
+       CA_MarkGrChunk(KEENTALK2PIC);\r
+       CA_CacheMarks(NULL);\r
+\r
+       US_CenterWindow(26, 8);\r
+       VWB_DrawPic(WindowX+WindowW-48, WindowY, KEENTALK1PIC);\r
+       WindowW -= 48;\r
+       PrintY += 12;\r
+       US_CPrint(\r
+               "Cool!  I can breathe\n"\r
+               "under water now!"\r
+               );\r
+       VW_UpdateScreen();\r
+       VW_WaitVBL(30);\r
+       IN_ClearKeysDown();\r
+       IN_Ack();\r
+\r
+       VWB_DrawPic(WindowX+WindowW, WindowY, KEENTALK2PIC);\r
+       VW_UpdateScreen();\r
+       VW_WaitVBL(30);\r
+       IN_ClearKeysDown();\r
+       IN_Ack();\r
+\r
+       CA_DownLevel();\r
+\r
+       //Note: scorebox sprite may have been re-cached by CA_DownLevel, but the level ends after this anyway\r
+}\r
+\r
+//============================================================================\r
+\r
+/*\r
+===========================\r
+=\r
+= RescuedMember\r
+=\r
+===========================\r
+*/\r
+\r
+char *keentext[] = {\r
+       "No sweat, oh guardian\n"\r
+       "of wisdom!"\r
+       ,\r
+       "Sounds like a plan,\n"\r
+       "bearded one!"\r
+       ,\r
+       "No problemo."\r
+       ,\r
+       "Great.  You know, you\n"\r
+       "look a lot like the\n"\r
+       "last guy I rescued..."\r
+       ,\r
+       "Good idea, Gramps."\r
+       ,\r
+       "May the road rise\n"\r
+       "to meet your feet,\n"\r
+       "Mr. Member."\r
+       ,\r
+       "Wise plan of action,\n"\r
+       "your ancientness."\r
+       ,\r
+       "You're the last one,\n"\r
+       "fella.  Let's both\n"\r
+       "get back to the\n"\r
+       "Oracle chamber!"\r
+};\r
+\r
+void RescuedMember(void)\r
+{\r
+       SD_WaitSoundDone();\r
+       CA_UpLevel();\r
+       CA_MarkGrChunk(ORACLEPIC);\r
+       CA_MarkGrChunk(KEENTALK1PIC);\r
+       CA_MarkGrChunk(KEENTALK2PIC);\r
+       CA_CacheMarks(NULL);\r
+       StartMusic(-1);\r
+       VW_FixRefreshBuffer();\r
+\r
+       US_CenterWindow(26, 8);\r
+       VWB_DrawPic(WindowX, WindowY, ORACLEPIC);\r
+       PrintY += 6;\r
+       WindowW -= 48;\r
+       WindowX += 48;\r
+       if (mapon == 17)\r
+       {\r
+               US_CPrint(\r
+                       "Ggoh thig you sogh mg\n"\r
+                       "fgor regscuing mgge!\n"\r
+                       "I'gll regur tgo the\n"\r
+                       "Goracle chagber\n"\r
+                       "igmediatggely. Blub."\r
+                       );\r
+       }\r
+       else\r
+       {\r
+               US_CPrint(\r
+                       "Oh thank you so much\n"\r
+                       "for rescuing me!\n"\r
+                       "I'll return to the\n"\r
+                       "Oracle chamber\n"\r
+                       "immediately."\r
+                       );\r
+       }\r
+       VW_UpdateScreen();\r
+       VW_WaitVBL(60);\r
+       IN_ClearKeysDown();\r
+       IN_Ack();\r
+\r
+       US_CenterWindow(26, 8);\r
+       VWB_DrawPic(WindowX+WindowW-48, WindowY, KEENTALK1PIC);\r
+       WindowW -= 48;\r
+       PrintY += 12;\r
+       US_CPrint(keentext[gamestate.rescued]);\r
+       VW_UpdateScreen();\r
+       VW_WaitVBL(30);\r
+       IN_ClearKeysDown();\r
+       IN_Ack();\r
+\r
+       VWB_DrawPic(WindowX+WindowW, WindowY, KEENTALK2PIC);\r
+       VW_UpdateScreen();\r
+       VW_WaitVBL(30);\r
+       IN_ClearKeysDown();\r
+       IN_Ack();\r
+\r
+       gamestate.rescued++;\r
+       CA_DownLevel();\r
+       StopMusic();\r
+\r
+       //Note: scorebox sprite may have been re-cached by CA_DownLevel, but the level ends after this anyway\r
+}\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                 SWIMMING KEEN\r
+\r
+temp4 = counter for spawning bubbles\r
+\r
+=============================================================================\r
+*/\r
+\r
+statetype s_keenswimslow1 = {SCUBAKEENL1SPR, SCUBAKEENR1SPR, stepthink, false, false, 50, 0, 0, T_KeenSwimSlow, C_KeenSwim, R_KeenSwim, &s_keenswimslow2};\r
+statetype s_keenswimslow2 = {SCUBAKEENL2SPR, SCUBAKEENR2SPR, stepthink, false, false, 50, 0, 0, T_KeenSwimSlow, C_KeenSwim, R_KeenSwim, &s_keenswimslow1};\r
+statetype s_keenswim1     = {SCUBAKEENL1SPR, SCUBAKEENR1SPR, stepthink, false, false, 50, 0, 0, T_KeenSwim, C_KeenSwim, R_KeenSwim, &s_keenswimslow2};\r
+statetype s_keenswim2     = {SCUBAKEENL2SPR, SCUBAKEENR2SPR, stepthink, false, false, 50, 0, 0, T_KeenSwim, C_KeenSwim, R_KeenSwim, &s_keenswimslow1};\r
+//Note: the die states for swimming Keen are in CK_KEEN.C and K4_ACT3.C (dopefish section)\r
+\r
+statetype s_kbubble1  = {SMALLBUBBLE1SPR, SMALLBUBBLE1SPR, think, false, false, 20, 0, 24, T_Bubble, NULL, R_Draw, &s_kbubble1};\r
+statetype s_kbubble2  = {SMALLBUBBLE2SPR, SMALLBUBBLE2SPR, think, false, false, 20, 0, 24, T_Bubble, NULL, R_Draw, &s_kbubble2};\r
+statetype s_kbubble3  = {SMALLBUBBLE3SPR, SMALLBUBBLE3SPR, think, false, false, 20, 0, 24, T_Bubble, NULL, R_Draw, &s_kbubble3};\r
+statetype s_kbubble4  = {SMALLBUBBLE4SPR, SMALLBUBBLE4SPR, think, false, false, 20, 0, 24, T_Bubble, NULL, R_Draw, &s_kbubble4};\r
+\r
+/*\r
+===========================\r
+=\r
+= SpawnSwimKeen\r
+=\r
+===========================\r
+*/\r
+\r
+void SpawnSwimKeen(Sint16 x, Sint16 y)\r
+{\r
+       player->obclass = keenobj;\r
+       player->active = ac_allways;\r
+       player->priority = 1;\r
+       player->x = CONVERT_TILE_TO_GLOBAL(x);\r
+       player->y = CONVERT_TILE_TO_GLOBAL(y);\r
+       player->xdir = 1;\r
+       player->ydir = 1;\r
+       player->needtoclip = cl_fullclip;\r
+       NewState(player, &s_keenswimslow1);\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= SpawnKbubble\r
+=\r
+===========================\r
+*/\r
+\r
+void SpawnKbubble(objtype *ob)\r
+{\r
+       ob->temp4 = 0;\r
+       GetNewObj(true);\r
+       if (ob->xdir == -1)\r
+       {\r
+               new->x = ob->x;\r
+       }\r
+       else\r
+       {\r
+               new->x = ob->x + 24*PIXGLOBAL;\r
+       }\r
+       new->y = ob->y;\r
+       new->obclass = inertobj;\r
+       new->priority = 3;\r
+       new->active = ac_removable;\r
+       new->needtoclip = cl_noclip;\r
+       new->yspeed = -24;\r
+       new->xspeed = 4;\r
+       switch (US_RndT() / 64)\r
+       {\r
+       case 0:\r
+               NewState(new, &s_kbubble1);\r
+               break;\r
+       case 1:\r
+               NewState(new, &s_kbubble2);\r
+               break;\r
+       case 2:\r
+               NewState(new, &s_kbubble3);\r
+               break;\r
+       case 3:\r
+               NewState(new, &s_kbubble4);\r
+               break;\r
+       }\r
+       SD_PlaySound(SND_BLUB);\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= T_KeenSwimSlow\r
+=\r
+===========================\r
+*/\r
+\r
+void T_KeenSwimSlow(objtype *ob)\r
+{\r
+       Sint32 i;\r
+       Sint16 vx, vy, xc, yc;\r
+\r
+       xc = ob->xspeed < 0;\r
+       yc = ob->yspeed < 4;\r
+\r
+       ob->temp4 = ob->temp4 + tics;\r
+       if (ob->temp4 > 60)\r
+               SpawnKbubble(ob);\r
+\r
+       if (jumpbutton && !jumpheld)\r
+       {\r
+               jumpheld = true;\r
+               if (c.xaxis)\r
+                       ob->xspeed = c.xaxis * 18;\r
+               if (c.yaxis)\r
+                       ob->yspeed = c.yaxis * 18;\r
+               ob->state = ob->state->nextstate;\r
+       }\r
+       if (c.xaxis)\r
+               ob->xdir = c.xaxis;\r
+\r
+       for (i = lasttimecount-tics; i < lasttimecount; i++)\r
+       {\r
+               if ((i & 7) == 0)\r
+               {\r
+                       if (ob->xspeed > 12)\r
+                       {\r
+                               vx = -3;\r
+                       }\r
+                       else if (ob->xspeed > 0)\r
+                       {\r
+                               vx = -1;\r
+                       }\r
+                       else if (ob->xspeed > -12)\r
+                       {\r
+                               vx = 1;\r
+                       }\r
+                       else\r
+                       {\r
+                               vx = 3;\r
+                       }\r
+                       vx += c.xaxis;\r
+                       vx += c.xaxis;\r
+                       ob->xspeed += vx;\r
+\r
+                       if (c.xaxis == 0 && (ob->xspeed < 0) != xc)\r
+                               ob->xspeed = 0;\r
+\r
+                       if (ob->yspeed > 12)\r
+                       {\r
+                               vy = -3;\r
+                       }\r
+                       else if (ob->yspeed > 4)\r
+                       {\r
+                               vy = -1;\r
+                       }\r
+                       else if (ob->yspeed > -12)\r
+                       {\r
+                               vy = 1;\r
+                       }\r
+                       else\r
+                       {\r
+                               vy = 3;\r
+                       }\r
+                       vy += c.yaxis;\r
+                       vy += c.yaxis;\r
+                       ob->yspeed += vy;\r
+\r
+                       if (c.yaxis == 0 && ob->yspeed > 4 && yc)\r
+                               ob->yspeed = 0;\r
+               }\r
+               xtry += ob->xspeed;\r
+               ytry += ob->yspeed;\r
+       }\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= T_KeenSwim\r
+=\r
+===========================\r
+*/\r
+\r
+void T_KeenSwim(objtype *ob)   //never actually used\r
+{\r
+       ob->temp4 = ob->temp4 + tics;\r
+       if (ob->temp4 > 60)\r
+               SpawnKbubble(ob);\r
+\r
+       if (jumpbutton && !jumpheld)\r
+       {\r
+               jumpheld = true;\r
+               ob->xspeed = c.xaxis * 18;\r
+               if (c.yaxis)\r
+                       ob->yspeed = c.yaxis * 18;\r
+\r
+               if (ob->state == &s_keenswim1)\r
+               {\r
+                       ob->state = &s_keenswim2;\r
+               }\r
+               else\r
+               {\r
+                       ob->state = &s_keenswim1;\r
+               }\r
+       }\r
+\r
+       xtry = xtry + ob->xspeed * tics;\r
+       ytry = ytry + ob->yspeed * tics;\r
+       if (xtry > 0)\r
+       {\r
+               ob->xdir = 1;\r
+       }\r
+       else if (xtry < 0)\r
+       {\r
+               ob->xdir = -1;\r
+       }\r
+\r
+       ytry = ytry + tics*4;\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= C_KeenSwim\r
+=\r
+===========================\r
+*/\r
+\r
+void C_KeenSwim(objtype *ob, objtype *hit)\r
+{\r
+       switch (hit->obclass)\r
+       {\r
+       case bonusobj:\r
+               switch (hit->temp1)\r
+               {\r
+               case 0:\r
+               case 1:\r
+               case 2:\r
+               case 3:\r
+               case 4:\r
+               case 5:\r
+               case 6:\r
+               case 7:\r
+               case 8:\r
+               case 9:\r
+               case 10:\r
+               case 11:\r
+                       SD_PlaySound(bonussound[hit->temp1]);\r
+                       hit->obclass = inertobj;\r
+                       hit->priority = 3;\r
+                       hit->shapenum = bonussprite[hit->temp1];\r
+                       GivePoints(bonuspoints[hit->temp1]);\r
+                       if (hit->temp1 < 4)\r
+                       {\r
+                               gamestate.keys[hit->temp1]++;\r
+                       }\r
+                       else if (hit->temp1 == 10)\r
+                       {\r
+                               gamestate.lives++;\r
+                       }\r
+                       else if (hit->temp1 == 11)\r
+                       {\r
+                               gamestate.ammo += shotsinclip[gamestate.difficulty];\r
+                       }\r
+                       ChangeState(hit, &s_bonusrise);\r
+                       break;\r
+               }\r
+               break;\r
+\r
+       case oracleobj:\r
+               playstate = ex_rescued;\r
+               break;\r
+       }\r
+       ob++;                   // shut up compiler\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= R_KeenSwim\r
+=\r
+===========================\r
+*/\r
+\r
+void R_KeenSwim(objtype *ob)\r
+{\r
+       if (ob->hiteast && ob->xspeed < 0 || ob->hitwest && ob->xspeed > 0)\r
+               ob->xspeed = 0;\r
+\r
+       if (ob->hitnorth && ob->yspeed > 0 || ob->hitsouth && ob->yspeed < 0)\r
+               ob->yspeed = 0;\r
+\r
+       RF_PlaceSprite(&ob->sprite, ob->x, ob->y, ob->shapenum, spritedraw, ob->priority);\r
+}\r
diff --git a/16/keen456/KEEN4-6/KEEN4C/GFXC_CK4.EQU b/16/keen456/KEEN4-6/KEEN4C/GFXC_CK4.EQU
new file mode 100755 (executable)
index 0000000..4444039
--- /dev/null
@@ -0,0 +1,55 @@
+;=====================================\r
+;\r
+; Graphics .EQU file for .CK4\r
+; not IGRAB-ed :)\r
+;\r
+;=====================================\r
+\r
+;INCLUDE "VERSION.EQU"\r
+\r
+;\r
+; Amount of each data item\r
+;\r
+NUMFONT     =  2\r
+NUMFONTM    =  0\r
+NUMPICM     =  3\r
+NUMTILE8    =  108\r
+NUMTILE8M   =  36\r
+NUMTILE32   =  0\r
+NUMTILE32M  =  0\r
+\r
+;\r
+; Amount of each item in episode 4\r
+;\r
+NUMPICS     =  116\r
+NUMSPRITES  =  397\r
+NUMTILE16   =  1296\r
+NUMTILE16M  =  2916\r
+NUMEXTERN   =  16\r
+\r
+\r
+;\r
+; File offsets for data items\r
+;\r
+STRUCTPIC       =      0\r
+STRUCTPICM      =      1\r
+STRUCTSPRITE    =      2\r
+\r
+STARTFONT       =      3\r
+STARTFONTM      =      (STARTFONT+NUMFONT)\r
+STARTPICS       =      (STARTFONTM+NUMFONTM)\r
+STARTPICM       =      (STARTPICS+NUMPICS)\r
+STARTSPRITES    =      (STARTPICM+NUMPICM)\r
+STARTTILE8      =      (STARTSPRITES+NUMSPRITES)\r
+STARTTILE8M     =      (STARTTILE8+1)\r
+STARTTILE16     =      (STARTTILE8M+1)\r
+STARTTILE16M    =      (STARTTILE16+NUMTILE16)\r
+STARTTILE32     =      (STARTTILE16M+NUMTILE16M)\r
+STARTTILE32M    =      (STARTTILE32+NUMTILE32)\r
+STARTEXTERN     =      (STARTTILE32M+NUMTILE32M)\r
+\r
+NUMCHUNKS       =      (STARTEXTERN+NUMEXTERN)\r
+\r
+;\r
+; Thank you for using IGRAB!\r
+;\r
diff --git a/16/keen456/KEEN4-6/KEEN4C/GFXC_CK4.H b/16/keen456/KEEN4-6/KEEN4C/GFXC_CK4.H
new file mode 100755 (executable)
index 0000000..0258564
--- /dev/null
@@ -0,0 +1,767 @@
+/* Reconstructed Commander Keen 4-6 Source Code\r
+ * Copyright (C) 2021 K1n9_Duk3\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License along\r
+ * with this program; if not, write to the Free Software Foundation, Inc.,\r
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+ */\r
+\r
+#ifndef __GFX_H__\r
+#define __GFX_H__\r
+\r
+//#include "VERSION.H"\r
+\r
+//////////////////////////////////////\r
+//\r
+// Graphics .H file for .CK4\r
+// not IGRAB-ed :)\r
+//\r
+//////////////////////////////////////\r
+\r
+//\r
+// Lump creation macros\r
+//\r
+\r
+#define START_LUMP(actualname, dummyname) actualname, dummyname=actualname-1,\r
+#define END_LUMP(actualname, dummyname) dummyname, actualname=dummyname-1,\r
+\r
+//\r
+// Amount of each data item\r
+//\r
+\r
+//common numbers:\r
+#define NUMCHUNKS    NUMGRCHUNKS\r
+#define NUMFONT      2\r
+#define NUMFONTM     0\r
+#define NUMPICM      3\r
+#define NUMTILE8     108       // BUG: only 104 tiles exist in EGAGRAPH!\r
+#define NUMTILE8M    36                // BUG: only 20 tiles exist in EGAGRAPH!\r
+#define NUMTILE32    0\r
+#define NUMTILE32M   0\r
+\r
+//episode-specific numbers:\r
+#define NUMPICS      116\r
+#define NUMSPRITES   397\r
+#define NUMTILE16    1296\r
+#define NUMTILE16M   2916\r
+#define NUMEXTERNS   16\r
+\r
+//\r
+// File offsets for data items\r
+//\r
+#define STRUCTPIC    0\r
+#define STRUCTPICM   1\r
+#define STRUCTSPRITE 2\r
+\r
+#define STARTFONT    3\r
+#define STARTFONTM   (STARTFONT+NUMFONT)\r
+#define STARTPICS    (STARTFONTM+NUMFONTM)\r
+#define STARTPICM    (STARTPICS+NUMPICS)\r
+#define STARTSPRITES (STARTPICM+NUMPICM)\r
+#define STARTTILE8   (STARTSPRITES+NUMSPRITES)\r
+#define STARTTILE8M  (STARTTILE8+1)\r
+#define STARTTILE16  (STARTTILE8M+1)\r
+#define STARTTILE16M (STARTTILE16+NUMTILE16)\r
+#define STARTTILE32  (STARTTILE16M+NUMTILE16M)\r
+#define STARTTILE32M (STARTTILE32+NUMTILE32)\r
+#define STARTEXTERNS (STARTTILE32M+NUMTILE32M)\r
+\r
+typedef enum {\r
+       LASTFONT=STARTPICS-1,\r
+\r
+       //\r
+       // PICS\r
+       //\r
+\r
+       PADDINGPIC,                  // 5 (compensate for the missing Star Wars font to give the other pics the correct chunk numbers)\r
+\r
+       H_HELPPIC,                   // 6\r
+       H_LARROWPIC,                 // 7\r
+       H_RARROWPIC,                 // 8\r
+       H_ESCPIC,                    // 9\r
+       H_ENTERPIC,                  // 10\r
+       DUMMYPIC,                    // 11\r
+       H_STORY1PIC,                 // 12\r
+       H_STORY2PIC,                 // 13\r
+       H_STORY3PIC,                 // 14\r
+       H_STORY4PIC,                 // 15\r
+       STORY5PIC,                   // 16\r
+       STORY6PIC,                   // 17\r
+       STORY7PIC,                   // 18\r
+       STORY8PIC,                   // 19\r
+       ITEM1PIC,                    // 20\r
+       ITEM2PIC,                    // 21\r
+       ITEM3PIC,                    // 22\r
+       ITEM4PIC,                    // 23\r
+       ITEM5PIC,                    // 24\r
+       ITEM6PIC,                    // 25\r
+       ITEM7PIC,                    // 26\r
+       ITEM8PIC,                    // 27\r
+       ITEM9PIC,                    // 28\r
+       ARACHNUTPIC,                 // 29\r
+       BERKELOISPIC,                // 30\r
+       BOUNDERPIC,                  // 31\r
+       COUNCILMEMBERPIC,            // 32\r
+       DOPEFISHPIC,                 // 33\r
+       INCHWORMPIC,                 // 34\r
+       LICKPIC,                     // 35\r
+       MADMUSHROOMPIC,              // 36\r
+       POISONSLIGPIC,               // 37\r
+       PRINCESSLINDSEYPIC,          // 38\r
+       SCHOOLFISHPIC,               // 39\r
+       SKYPESTPIC,                  // 40\r
+       SPRITEPIC,                   // 41\r
+       WORMOUTHPIC,                 // 42\r
+       ENDOFTEXTPIC,                // 43\r
+       H_MCPIC,                     // 44\r
+       H_HANDPIC,                   // 45\r
+       H_VISAPIC,                   // 46\r
+       H_FLASHARROW1PIC,            // 47\r
+       H_FLASHARROW2PIC,            // 48\r
+       ENDINDG1PIC,                 // 49\r
+       ENDINDG2PIC,                 // 50\r
+       ENDINDG3PIC,                 // 51\r
+       ENDINDG4PIC,                 // 52\r
+       ENDINDG5PIC,                 // 53\r
+       ENDINDG6PIC,                 // 54\r
+       ENDINDG7PIC,                 // 55\r
+       ENDINDG8PIC,                 // 56\r
+       ENDINDG9PIC,                 // 57\r
+       ENDINDG10PIC,                // 58\r
+       ENDINDG11PIC,                // 59\r
+       ENDINDG12PIC,                // 60\r
+       ENDINDG13PIC,                // 61\r
+       ENDINDG14PIC,                // 62\r
+       ENDINDG15PIC,                // 63\r
+       ENDINDG16PIC,                // 64\r
+       ENDINDG17PIC,                // 65\r
+       ENDINDG18PIC,                // 66\r
+       ENDINDG19PIC,                // 67\r
+       ENDINDG20PIC,                // 68\r
+       ENDINDG21PIC,                // 69\r
+       ENDINDG22PIC,                // 70\r
+       ENDINDG23PIC,                // 71\r
+       ENDINDG24PIC,                // 72\r
+       ENDINDG25PIC,                // 73\r
+       ENDINDG26PIC,                // 74\r
+       ENDINDG27PIC,                // 75\r
+       ENDINDG28PIC,                // 76\r
+       ENDINDG29PIC,                // 77\r
+       ENDINDG30PIC,                // 78\r
+       H_IDLOGOPIC,                 // 79\r
+       H_TOPWINDOWPIC,              // 80\r
+       H_LEFTWINDOWPIC,             // 81\r
+       H_RIGHTWINDOWPIC,            // 82\r
+       H_BOTTOMINFOPIC,             // 83\r
+       H_BOTTOMWINDOWPIC,           // 84\r
+       H_BARPIC,                    // 85\r
+       H_KEEN5PIC,                  // 86\r
+       H_KEEN6PIC,                  // 87\r
+\r
+       START_LUMP(CONTROLS_LUMP_START, __CONTROLSSTART)\r
+       CP_MAINMENUPIC,              // 88\r
+       CP_NEWGAMEMENUPIC,           // 89\r
+       CP_LOADMENUPIC,              // 90\r
+       CP_SAVEMENUPIC,              // 91\r
+       CP_CONFIGMENUPIC,            // 92\r
+       CP_SOUNDMENUPIC,             // 93\r
+       CP_MUSICMENUPIC,             // 94\r
+       CP_KEYBOARDMENUPIC,          // 95\r
+       CP_KEYMOVEMENTPIC,           // 96\r
+       CP_KEYBUTTONPIC,             // 97\r
+       CP_JOYSTICKMENUPIC,          // 98\r
+       CP_OPTIONSMENUPIC,           // 99\r
+       CP_PADDLEWARPIC,             // 100\r
+       CP_QUITPIC,                  // 101\r
+       CP_JOYSTICKPIC,              // 102\r
+       CP_MENUSCREENPIC,            // 103\r
+       END_LUMP(CONTROLS_LUMP_END, __CONTROLSEND)\r
+\r
+       IDSOFTPIC,                   // 104\r
+       PROGTEAMPIC,                 // 105\r
+       ARTISTPIC,                   // 106\r
+       DIRECTORPIC,                 // 107\r
+       SW_BACKGROUNDPIC,            // 108\r
+       TITLEPICPIC,                 // 109\r
+       ORACLEPIC,                   // 110\r
+       KEENTALK1PIC,                // 111\r
+       KEENTALK2PIC,                // 112\r
+       KEENMADPIC,                  // 113\r
+       LINDSEYPIC,                  // 114\r
+       KEENCOUNT1PIC,               // 115\r
+       KEENCOUNT2PIC,               // 116\r
+       KEENCOUNT3PIC,               // 117\r
+       KEENCOUNT4PIC,               // 118\r
+       KEENCOUNT5PIC,               // 119\r
+       KEENCOUNT6PIC,               // 120\r
+\r
+       //\r
+       // MASKED PICS\r
+       //\r
+\r
+       CP_MENUMASKPICM,             // 121\r
+       CORDPICM,                    // 122\r
+       METALPOLEPICM,               // 123\r
+\r
+       //\r
+       // SPRITES\r
+       //\r
+\r
+       START_LUMP(PADDLE_LUMP_START, __PADDLESTART)\r
+       PADDLESPR,                   // 124\r
+       BALLSPR,                     // 125\r
+       BALL1PIXELTOTHERIGHTSPR,     // 126\r
+       BALL2PIXELSTOTHERIGHTSPR,    // 127\r
+       BALL3PIXELSTOTHERIGHTSPR,    // 128\r
+       END_LUMP(PADDLE_LUMP_END, __PADDLEEND)\r
+\r
+       DEMOPLAQUESPR,               // 129\r
+\r
+       //player lump:\r
+       START_LUMP(KEEN_LUMP_START, __KEENSTART)\r
+       KEENSTANDRSPR,               // 130\r
+       KEENRUNR1SPR,                // 131\r
+       KEENRUNR2SPR,                // 132\r
+       KEENRUNR3SPR,                // 133\r
+       KEENRUNR4SPR,                // 134\r
+       KEENJUMPR1SPR,               // 135\r
+       KEENJUMPR2SPR,               // 136\r
+       KEENJUMPR3SPR,               // 137\r
+       KEENSTANDLSPR,               // 138\r
+       KEENRUNL1SPR,                // 139\r
+       KEENRUNL2SPR,                // 140\r
+       KEENRUNL3SPR,                // 141\r
+       KEENRUNL4SPR,                // 142\r
+       KEENJUMPL1SPR,               // 143\r
+       KEENJUMPL2SPR,               // 144\r
+       KEENJUMPL3SPR,               // 145\r
+       KEENLOOKUSPR,                // 146\r
+       KEENWAITR1SPR,               // 147\r
+       KEENWAITR2SPR,               // 148\r
+       KEENWAITR3SPR,               // 149\r
+       KEENSITREAD1SPR,             // 150\r
+       KEENSITREAD2SPR,             // 151\r
+       KEENSITREAD3SPR,             // 152\r
+       KEENSITREAD4SPR,             // 153\r
+       KEENREAD1SPR,                // 154\r
+       KEENREAD2SPR,                // 155\r
+       KEENREAD3SPR,                // 156\r
+       KEENSTOPREAD1SPR,            // 157\r
+       KEENSTOPREAD2SPR,            // 158\r
+       KEENWATCHSPR,                // 159\r
+       KEENLOOKD1SPR,               // 160\r
+       KEENLOOKD2SPR,               // 161\r
+       KEENDIE1SPR,                 // 162\r
+       KEENDIE2SPR,                 // 163\r
+       STUNSTARS1SPR,               // 164\r
+       STUNSTARS2SPR,               // 165\r
+       STUNSTARS3SPR,               // 166\r
+       KEENSHOOTLSPR,               // 167\r
+       KEENJLSHOOTLSPR,             // 168\r
+       KEENJSHOOTDSPR,              // 169\r
+       KEENJSHOOTUSPR,              // 170\r
+       KEENSHOOTUSPR,               // 171\r
+       KEENSHOOTRSPR,               // 172\r
+       KEENJRSHOOTRSPR,             // 173\r
+       STUN1SPR,                    // 174\r
+       STUN2SPR,                    // 175\r
+       STUN3SPR,                    // 176\r
+       STUN4SPR,                    // 177\r
+       STUNHIT1SPR,                 // 178\r
+       STUNHIT2SPR,                 // 179\r
+       KEENSHINNYR1SPR,             // 180\r
+       KEENSHINNYR2SPR,             // 181\r
+       KEENSHINNYR3SPR,             // 182\r
+       KEENSLIDED1SPR,              // 183\r
+       KEENSLIDED2SPR,              // 184\r
+       KEENSLIDED3SPR,              // 185\r
+       KEENSLIDED4SPR,              // 186\r
+       KEENSHINNYL1SPR,             // 187\r
+       KEENSHINNYL2SPR,             // 188\r
+       KEENSHINNYL3SPR,             // 189\r
+       KEENPLSHOOTUSPR,             // 190\r
+       KEENPRSHOOTUSPR,             // 191\r
+       KEENPRSHOOTDSPR,             // 192\r
+       KEENPLSHOOTDSPR,             // 193\r
+       KEENPSHOOTLSPR,              // 194\r
+       KEENPSHOOTRSPR,              // 195\r
+       KEENENTER1SPR,               // 196\r
+       KEENENTER2SPR,               // 197\r
+       KEENENTER3SPR,               // 198\r
+       KEENENTER4SPR,               // 199\r
+       KEENENTER5SPR,               // 200\r
+       KEENHANGLSPR,                // 201\r
+       KEENHANGRSPR,                // 202\r
+       KEENCLIMBEDGEL1SPR,          // 203\r
+       KEENCLIMBEDGEL2SPR,          // 204\r
+       KEENCLIMBEDGEL3SPR,          // 205\r
+       KEENCLIMBEDGEL4SPR,          // 206\r
+       KEENCLIMBEDGER1SPR,          // 207\r
+       KEENCLIMBEDGER2SPR,          // 208\r
+       KEENCLIMBEDGER3SPR,          // 209\r
+       KEENCLIMBEDGER4SPR,          // 210\r
+       KEENPOGOR1SPR,               // 211\r
+       KEENPOGOR2SPR,               // 212\r
+       KEENPOGOL1SPR,               // 213\r
+       KEENPOGOL2SPR,               // 214\r
+       DROPSPLASH1SPR,              // 215\r
+       DROPSPLASH2SPR,              // 216\r
+       DROPSPLASH3SPR,              // 217\r
+       BONUS100UPSPR,               // 218\r
+       BONUS100SPR,                 // 219\r
+       BONUS200SPR,                 // 220\r
+       BONUS500SPR,                 // 221\r
+       BONUS1000SPR,                // 222\r
+       BONUS2000SPR,                // 223\r
+       BONUS5000SPR,                // 224\r
+       BONUS1UPSPR,                 // 225\r
+       BONUSCLIPSPR,                // 226\r
+       END_LUMP(KEEN_LUMP_END, __KEENEND)\r
+\r
+       START_LUMP(SUGAR1_LUMP_START, __SUGAR1START)\r
+       SUGAR1ASPR,                  // 227\r
+       SUGAR1BSPR,                  // 228\r
+       END_LUMP(SUGAR1_LUMP_END, __SUGAR1END)\r
+\r
+       START_LUMP(SUGAR2_LUMP_START, __SUGAR2START)\r
+       SUGAR2ASPR,                  // 229\r
+       SUGAR2BSPR,                  // 230\r
+       END_LUMP(SUGAR2_LUMP_END, __SUGAR2END)\r
+\r
+       START_LUMP(SUGAR3_LUMP_START, __SUGAR3START)\r
+       SUGAR3ASPR,                  // 231\r
+       SUGAR3BSPR,                  // 232\r
+       END_LUMP(SUGAR3_LUMP_END, __SUGAR3END)\r
+\r
+       START_LUMP(SUGAR4_LUMP_START, __SUGAR4START)\r
+       SUGAR4ASPR,                  // 233\r
+       SUGAR4BSPR,                  // 234\r
+       END_LUMP(SUGAR4_LUMP_END, __SUGAR4END)\r
+\r
+       START_LUMP(SUGAR5_LUMP_START, __SUGAR5START)\r
+       SUGAR5ASPR,                  // 235\r
+       SUGAR5BSPR,                  // 236\r
+       END_LUMP(SUGAR5_LUMP_END, __SUGAR5END)\r
+\r
+       START_LUMP(SUGAR6_LUMP_START, __SUGAR6START)\r
+       SUGAR6ASPR,                  // 237\r
+       SUGAR6BSPR,                  // 238\r
+       END_LUMP(SUGAR6_LUMP_END, __SUGAR6END)\r
+\r
+       START_LUMP(ONEUP_LUMP_START, __ONEUPSTART)\r
+       ONEUPASPR,                   // 239\r
+       ONEUPBSPR,                   // 240\r
+       END_LUMP(ONEUP_LUMP_END, __ONEUPEND)\r
+\r
+       DOORSPR,                     // 241\r
+\r
+       START_LUMP(KEYGEM_LUMP_START, __KEYGEMSTART)\r
+       REDGEM1SPR,                  // 242\r
+       REDGEM2SPR,                  // 243\r
+       YELLOWGEM1SPR,               // 244\r
+       YELLOWGEM2SPR,               // 245\r
+       BLUEGEM1SPR,                 // 246\r
+       BLUEGEM2SPR,                 // 247\r
+       GREENGEM1SPR,                // 248\r
+       GREENGEM2SPR,                // 249\r
+       BONUSGEMSPR,                 // 250\r
+       END_LUMP(KEYGEM_LUMP_END, __KEYGEMEND)\r
+\r
+       START_LUMP(AMMO_LUMP_START, __AMMOSTART)\r
+       STUNCLIP1SPR,                // 251\r
+       STUNCLIP2SPR,                // 252\r
+       END_LUMP(AMMO_LUMP_END, __AMMOEND)\r
+\r
+       SCOREBOXSPR,                 // 253\r
+\r
+       START_LUMP(WORLDKEEN_LUMP_START, __WORLDKEENSTART)\r
+       WORLDKEENL1SPR,              // 254\r
+       WORLDKEENL2SPR,              // 255\r
+       WORLDKEENL3SPR,              // 256\r
+       WORLDKEENR1SPR,              // 257\r
+       WORLDKEENR2SPR,              // 258\r
+       WORLDKEENR3SPR,              // 259\r
+       WORLDKEENU1SPR,              // 260\r
+       WORLDKEENU2SPR,              // 261\r
+       WORLDKEENU3SPR,              // 262\r
+       WORLDKEEND1SPR,              // 263\r
+       WORLDKEEND2SPR,              // 264\r
+       WORLDKEEND3SPR,              // 265\r
+       WORLDKEENDR1SPR,             // 266\r
+       WORLDKEENDR2SPR,             // 267\r
+       WORLDKEENDR3SPR,             // 268\r
+       WORLDKEENDL1SPR,             // 269\r
+       WORLDKEENDL2SPR,             // 270\r
+       WORLDKEENDL3SPR,             // 271\r
+       WORLDKEENUL1SPR,             // 272\r
+       WORLDKEENUL2SPR,             // 273\r
+       WORLDKEENUL3SPR,             // 274\r
+       WORLDKEENUR1SPR,             // 275\r
+       WORLDKEENUR2SPR,             // 276\r
+       WORLDKEENUR3SPR,             // 277\r
+       WORLDKEENWAVE1SPR,           // 278\r
+       WORLDKEENWAVE2SPR,           // 279\r
+       WORLDKEENSWIMU1SPR,          // 280\r
+       WORLDKEENSWIMU2SPR,          // 281\r
+       WORLDKEENSWIMR1SPR,          // 282\r
+       WORLDKEENSWIMR2SPR,          // 283\r
+       WORLDKEENSWIMD1SPR,          // 284\r
+       WORLDKEENSWIMD2SPR,          // 285\r
+       WORLDKEENSWIML1SPR,          // 286\r
+       WORLDKEENSWIML2SPR,          // 287\r
+       WORLDKEENSWIMUR1SPR,         // 288\r
+       WORLDKEENSWIMUR2SPR,         // 289\r
+       WORLDKEENSWIMDR1SPR,         // 290\r
+       WORLDKEENSWIMDR2SPR,         // 291\r
+       WORLDKEENSWIMDL1SPR,         // 292\r
+       WORLDKEENSWIMDL2SPR,         // 293\r
+       WORLDKEENSWIMUL1SPR,         // 294\r
+       WORLDKEENSWIMUL2SPR,         // 295\r
+       WOLRDKEENRIDE1SPR,           // 296\r
+       WOLRDKEENRIDE2SPR,           // 297\r
+       FLAGFLIP1SPR,                // 298\r
+       FLAGFLIP2SPR,                // 299\r
+       FLAGFLIP3SPR,                // 300\r
+       FLAGFLIP4SPR,                // 301\r
+       FLAGFLIP5SPR,                // 302\r
+       FLAGFALL1SPR,                // 303\r
+       FLAGFALL2SPR,                // 304\r
+       FLAGFLAP1SPR,                // 305\r
+       FLAGFLAP2SPR,                // 306\r
+       FLAGFLAP3SPR,                // 307\r
+       FLAGFLAP4SPR,                // 308\r
+       END_LUMP(WORLDKEEN_LUMP_END, __WORLDKEENEND)\r
+\r
+       START_LUMP(SCUBAKEEN_LUMP_START, __SCUBAKEENSTART)\r
+       SCUBAKEENL1SPR,              // 309\r
+       SCUBAKEENL2SPR,              // 310\r
+       SCUBAKEENR1SPR,              // 311\r
+       SCUBAKEENR2SPR,              // 312\r
+       SCUBAKEENDEAD1SPR,           // 313\r
+       SCUBAKEENDEAD2SPR,           // 314\r
+       END_LUMP(SCUBAKEEN_LUMP_END, __SCUBAKEENEND)\r
+\r
+       START_LUMP(SLUG_LUMP_START, __SLUGSTART)\r
+       SLUGWALKR1SPR,               // 315\r
+       SLUGWALKR2SPR,               // 316\r
+       SLUGPISSRSPR,                 // 317\r
+       SLUGSTUN1SPR,             // 318\r
+       SLUGSTUN2SPR,             // 319\r
+       SLUGWALKL1SPR,               // 320\r
+       SLUGWALKL2SPR,               // 321\r
+       SLUGPISSLSPR,                 // 322\r
+       SLUGSLIME1SPR,               // 323\r
+       SLUGSLIME2SPR,               // 324\r
+       END_LUMP(SLUG_LUMP_END, __SLUGEND)\r
+\r
+       START_LUMP(MADMUSHROOM_LUMP_START, __MADMUSHROOMSTART)\r
+       MADMUSHROOML1SPR,            // 325\r
+       MADMUSHROOML2SPR,            // 326\r
+       MADMUSHROOMR1SPR,            // 327\r
+       MADMUSHROOMR2SPR,            // 328\r
+       END_LUMP(MADMUSHROOM_LUMP_END, __MADMUSHROOMEND)\r
+\r
+       START_LUMP(LINDSEY_LUMP_START, __LINDSEYSTART)\r
+       LINDSEY1SPR,                 // 329\r
+       LINDSEY2SPR,                 // 330\r
+       LINDSEY3SPR,                 // 331\r
+       LINDSEY4SPR,                 // 332\r
+       END_LUMP(LINDSEY_LUMP_END, __LINDSEYEND)\r
+\r
+       START_LUMP(INCHWORM_LUMP_START, __INCHWORMSTART)\r
+       INCHWORMR1SPR,               // 333\r
+       INCHWORMR2SPR,               // 334\r
+       INCHWORML1SPR,               // 335\r
+       INCHWORML2SPR,               // 336\r
+       FOOTSPR,                     // 337\r
+       END_LUMP(INCHWORM_LUMP_END, __INCHWORMEND)\r
+\r
+       START_LUMP(EATER_LUMP_START, __EATERSTART)\r
+       EATERSTAND1SPR,              // 338\r
+       EATERSTAND2SPR,              // 339\r
+       EATERJUMPR1SPR,              // 340\r
+       EATERJUMPR2SPR,              // 341\r
+       EATERJUMPR3SPR,              // 342\r
+       EATERJUMPL1SPR,              // 343\r
+       EATERJUMPL2SPR,              // 344\r
+       EATERJUMPL3SPR,              // 345\r
+       EATENBONUS1SPR,              // 346\r
+       EATENBONUS2SPR,              // 347\r
+       EATENBONUS3SPR,              // 348\r
+       EATENBONUS4SPR,              // 349\r
+       SMOKE1SPR,                   // 350\r
+       SMOKE2SPR,                   // 351\r
+       SMOKE3SPR,                   // 352\r
+       SMOKE4SPR,                   // 353\r
+       SMOKE5SPR,                   // 354\r
+       EATERSTUNSPR,                // 355\r
+       END_LUMP(EATER_LUMP_END, __EATEREND)\r
+\r
+       START_LUMP(COUNCIL_LUMP_START, __COUINCILSTART)\r
+       COUNCILWALKR1SPR,            // 356\r
+       COUNCILWALKR2SPR,            // 357\r
+       COUNCILWALKL1SPR,            // 358\r
+       COUNCILWALKL2SPR,            // 359\r
+       COUNCILTHINKLSPR,            // 360\r
+       COUNCILTHINKRSPR,            // 361\r
+       END_LUMP(COUNCIL_LUMP_END, __COUNCILEND)\r
+\r
+       START_LUMP(EGG_LUMP_START, __EGGSTART)\r
+       EGGSPR,                      // 362\r
+       EGGBROKESPR,                // 363\r
+       EGGCHIP1SPR,                // 364\r
+       EGGCHIP2SPR,                // 365\r
+       EGGCHIP3SPR,                // 366\r
+       END_LUMP(EGG_LUMP_END, __EGGEND)\r
+\r
+       START_LUMP(EGGBIRD_LUMP_START, __EGGBIRDSTART)\r
+       BIRDWALKR1SPR,               // 367\r
+       BIRDWALKR2SPR,               // 368\r
+       BIRDWALKR3SPR,               // 369\r
+       BIRDWALKR4SPR,               // 370\r
+       BIRDWALKL1SPR,               // 371\r
+       BIRDWALKL2SPR,               // 372\r
+       BIRDWALKL3SPR,               // 373\r
+       BIRDWALKL4SPR,               // 374\r
+       BIRDFLY1SPR,                 // 375\r
+       BIRDFLY2SPR,                 // 376\r
+       BIRDFLY3SPR,                 // 377\r
+       BIRDFLY4SPR,                 // 378\r
+       BIRDSTUNSPR,                 // 379\r
+       END_LUMP(EGGBIRD_LUMP_END, __EGGBIRDEND)\r
+\r
+       START_LUMP(DARTS_LUMP_START, __DARTSSTART)\r
+       DARTU1SPR,                   // 380\r
+       DARTU2SPR,                   // 381\r
+       DARTD1SPR,                   // 382\r
+       DARTD2SPR,                   // 383\r
+       DARTR1SPR,                   // 384\r
+       DARTR2SPR,                   // 385\r
+       DARTL1SPR,                   // 386\r
+       DARTL2SPR,                   // 387\r
+       END_LUMP(DARTS_LUMP_END, __DARTSEND)\r
+\r
+       START_LUMP(MIMROCK_LUMP_START, __MIMROCKSTART)\r
+       MIMROCKSPR,                  // 388\r
+       MIMROCKWALKL1SPR,            // 389\r
+       MIMROCKWALKL2SPR,            // 390\r
+       MIMROCKWALKL3SPR,            // 391\r
+       MIMROCKWALKL4SPR,            // 392\r
+       MIMROCKWALKR1SPR,            // 393\r
+       MIMROCKWALKR2SPR,            // 394\r
+       MIMROCKWALKR3SPR,            // 395\r
+       MIMROCKWALKR4SPR,            // 396\r
+       MIMROCKJUMPR1SPR,            // 397\r
+       MIMROCKJUMPR2SPR,            // 398\r
+       MIMROCKJUMPR3SPR,            // 399\r
+       MIMROCKJUMPL1SPR,            // 400\r
+       MIMROCKJUMPL2SPR,            // 401\r
+       MIMROCKJUMPL3SPR,            // 402\r
+       MINROCKSTUNSPR,              // 403\r
+       END_LUMP(MIMROCK_LUMP_END, __MIMROCKEND)\r
+\r
+       START_LUMP(DOPEFISH_LUMP_START, __DOPEFISHSTART)\r
+       DOPEFISHSWIMR1SPR,           // 404\r
+       DOPEFISHSWIMR2SPR,           // 405\r
+       DOPEFISHHUNGRYRSPR,          // 406\r
+       DOPEFISHBURP1SPR,            // 407\r
+       DOPEFISHBURP2SPR,            // 408\r
+       BIGBUBBLE1SPR,               // 409\r
+       BIGBUBBLE2SPR,               // 410\r
+       BIGBUBBLE3SPR,               // 411\r
+       BIGBUBBLE4SPR,               // 412\r
+       SMALLBUBBLE1SPR,             // 413\r
+       SMALLBUBBLE2SPR,             // 414\r
+       SMALLBUBBLE3SPR,             // 415\r
+       SMALLBUBBLE4SPR,             // 416\r
+       MEDIUMBUBBLESPR,             // 417\r
+       DOPEFISHSWIML1SPR,           // 418\r
+       DOPEFISHSWIML2SPR,           // 419\r
+       DOPEFISHHUNGRYLSPR,          // 420\r
+       END_LUMP(DOPEFISH_LUMP_END, __DOPEFISHEND)\r
+\r
+       START_LUMP(SCHOOLFISH_LUMP_START, __SCHOOLFISHSTART)\r
+       SCHOOLFISHL1SPR,             // 421\r
+       SCHOOLFISHL2SPR,             // 422\r
+       SCHOOLFISHR1SPR,             // 423\r
+       SCHOOLFISHR2SPR,             // 424\r
+       END_LUMP(SCHOOLFISH_LUMP_END, __SCHOOLFISHEND)\r
+\r
+       START_LUMP(ARACHNUT_LUMP_START, __ARACHNUTSTART)\r
+       ARACHNUTWALK1SPR,            // 425\r
+       ARACHNUTWALK2SPR,            // 426\r
+       ARACHNUTWALK3SPR,            // 427\r
+       ARACHNUTWALK4SPR,            // 428\r
+       ARACHNUTSTUNSPR,             // 429\r
+       END_LUMP(ARACHNUT_LUMP_END, __ARACHNUTEND)\r
+\r
+       SCUBASPR,                    // 430\r
+\r
+       START_LUMP(SPRITE_LUMP_START, __SPRITESTART)\r
+       SPRITEFLOATSPR,              // 431\r
+       SPRITEAIMLSPR,               // 432\r
+       SPRITESHOOTLSPR,             // 433\r
+       SPRITEAIMRSPR,               // 434\r
+       SPRITESHOOTRSPR,             // 435\r
+       SPRITESHOT1SPR,              // 436\r
+       SPRITESHOT2SPR,              // 437\r
+       SPRITESHOT3SPR,              // 438\r
+       SPRITESHOT4SPR,              // 439\r
+       END_LUMP(SPRITE_LUMP_END, __SPRITEEND)\r
+\r
+       START_LUMP(MINE_LUMP_START, __MINESTART)\r
+       MINESPR,                     // 440\r
+       MINEEXPLODE1SPR,             // 441\r
+       MINEEXPLODE2SPR,             // 442\r
+       END_LUMP(MINE_LUMP_END, __MINEEND)\r
+\r
+       START_LUMP(SKYPEST_LUMP_START, __SKYPESTSTART)\r
+       SKYPESTFLYL1SPR,             // 443\r
+       SKYPESTFLYL2SPR,             // 444\r
+       SKYPESTFLYR1SPR,             // 445\r
+       SKYPESTFLYR2SPR,             // 446\r
+       SKYPESTSIT1SPR,              // 447\r
+       SKYPESTSIT2SPR,              // 448\r
+       SKYPESTSIT3SPR,              // 449\r
+       SKYPESTSIT4SPR,              // 450\r
+       SKYPESTSIT5SPR,              // 451\r
+       SKYPESTSIT6SPR,              // 452\r
+       SKYPESTSIT7SPR,              // 453\r
+       SKYPESTSIT8SPR,              // 454\r
+       SKYPESTSIT9SPR,              // 455\r
+       SKYPESTSQUASHEDSPR,          // 456\r
+       END_LUMP(SKYPEST_LUMP_END, __SKYPESTEND)\r
+\r
+       START_LUMP(WORMOUTH_LUMP_START, __WORMOUTHSTART)\r
+       WORMOUTHSPR,                 // 457\r
+       WORMOUTHPEEKR1SPR,           // 458\r
+       WORMOUTHPEEKR2SPR,           // 459\r
+       WORMOUTHPEEKL1SPR,           // 460\r
+       WORMOUTHPEEKL2SPR,           // 461\r
+       WORMOUTHBITER1SPR,           // 462\r
+       WORMOUTHBITER2SPR,           // 463\r
+       WORMOUTHBITER3SPR,           // 464\r
+       WORMOUTHBITEL1SPR,           // 465\r
+       WORMOUTHBITEL2SPR,           // 466\r
+       WORMOUTHBITEL3SPR,           // 467\r
+       WORMOUTHSTUNSPR,             // 468\r
+       END_LUMP(WORMOUTH_LUMP_END, __WORMOUTHEND)\r
+\r
+       START_LUMP(LICK_LUMP_START, __LICKSTART)\r
+       LICKMOVER1SPR,               // 469\r
+       LICKMOVER2SPR,               // 470\r
+       LICKMOVER3SPR,               // 471\r
+       LICKMOVER4SPR,               // 472\r
+       LICKMOVEL1SPR,               // 473\r
+       LICKMOVEL2SPR,               // 474\r
+       LICKMOVEL3SPR,               // 475\r
+       LICKMOVEL4SPR,               // 476\r
+       LICKATTACKR1SPR,             // 477\r
+       LICKATTACKR2SPR,             // 478\r
+       LICKATTACKR3SPR,             // 479\r
+       LICKATTACKL1SPR,             // 480\r
+       LICKATTACKL2SPR,             // 481\r
+       LICKATTACKL3SPR,             // 482\r
+       LICKSTUNSPR,                 // 483\r
+       END_LUMP(LICK_LUMP_END, __LICKEND)\r
+\r
+       START_LUMP(PLATFORM_LUMP_START, __PLATFORMSTART)\r
+       PLATFORMSPR,                 // 484\r
+       PLATSIDETHRUST1SPR,          // 485\r
+       PLATSIDETHRUST2SPR,          // 486\r
+       PLATRTHRUST1SPR,             // 487\r
+       PLATRTHRUST2SPR,             // 488\r
+       PLATLTHRUST1SPR,             // 489\r
+       PLATLTHRUST2SPR,             // 490\r
+       END_LUMP(PLATFORM_LUMP_END, __PLATFORMEND)\r
+\r
+       START_LUMP(BOUNDER_LUMP_START, __BOUNDERSTART)\r
+       BOUNDERL1SPR,                // 491\r
+       BOUNDERL2SPR,                // 492\r
+       BOUNDERR1SPR,                // 493\r
+       BOUNDERR2SPR,                // 494\r
+       BOUNDERC1SPR,                // 495\r
+       BOUNDERC2SPR,                // 496\r
+       BOUNDERSTUNSPR,           // 497\r
+       END_LUMP(BOUNDER_LUMP_END, __BOUNDEREND)\r
+\r
+       START_LUMP(THUNDERCLOUD_LUMP_START, __THUNDERCLOUDSTART)\r
+       CLOUDSPR,                    // 498\r
+       CLOUDACTIVESPR,              // 499\r
+       CLOUDCHARGESPR,              // 500\r
+       BOLT1SPR,               // 501\r
+       BOLT2SPR,               // 502\r
+       END_LUMP(THUNDERCLOUD_LUMP_END, __THUNDERCLOUDEND)\r
+\r
+       START_LUMP(BERKELOID_LUMP_START, __BERKELOIDSTART)\r
+       BERKEWALKL1SPR,              // 503\r
+       BERKEWALKL2SPR,              // 504\r
+       BERKEWALKL3SPR,              // 505\r
+       BERKEWALKL4SPR,              // 506\r
+       BERKEWALKR1SPR,              // 507\r
+       BERKEWALKR2SPR,              // 508\r
+       BERKEWALKR3SPR,              // 509\r
+       BERKEWALKR4SPR,              // 510\r
+       BERKETHROWL1SPR,             // 511\r
+       BERKETHROWL2SPR,             // 512\r
+       BERKETHROWR1SPR,             // 513\r
+       BERKETHROWR2SPR,             // 514\r
+       FIREBALL1SPR,                // 515\r
+       FIREBALL2SPR,                // 516\r
+       FIREBALL3SPR,                // 517\r
+       FIREBALL4SPR,                // 518\r
+       END_LUMP(BERKELOID_LUMP_END, __BERKELOIDEND)\r
+\r
+       START_LUMP(MOON_LUMP_START, __MOONSTART)\r
+       KEENMOON1SPR,                // 519\r
+       KEENMOON2SPR,                // 520\r
+       END_LUMP(MOON_LUMP_END, __MOONEND)\r
+\r
+       //\r
+       // TILES (these don't need names)\r
+       //\r
+\r
+       LASTTILE=STARTEXTERNS-1,\r
+\r
+       //\r
+       // EXTERNS\r
+       //\r
+\r
+       ORDERSCREEN,                 // 4735\r
+       BIGCOMMANDER,                // 4736\r
+       BIGKEEN,                     // 4737\r
+       OUTOFMEM,                    // 4738\r
+\r
+       //texts\r
+       T_HELPART,                   // 4739\r
+       T_STORYART,                  // 4740\r
+       T_CONTRART,                  // 4741\r
+       T_IDART,                     // 4742\r
+       T_ENDART,                    // 4743\r
+       T_DEMOART,                   // 4744\r
+       T_ORDERART,                  // 4745\r
+\r
+       //demos\r
+       DEMO0,                       // 4746\r
+       DEMO1,                       // 4747\r
+       DEMO2,                       // 4748\r
+       DEMO3,                       // 4749\r
+       DEMO4,                       // 4750\r
+\r
+       NUMGRCHUNKS\r
+} graphicnums;\r
+\r
+#undef START_LUMP\r
+#undef END_LUMP\r
+\r
+#endif //__GFX_H__
\ No newline at end of file
diff --git a/16/keen456/KEEN4-6/KEEN4C/ID_ASM.EQU b/16/keen456/KEEN4-6/KEEN4C/ID_ASM.EQU
new file mode 100755 (executable)
index 0000000..2c3c92e
--- /dev/null
@@ -0,0 +1,115 @@
+;\r
+; Equates for all .ASM files\r
+;\r
+\r
+;----------------------------------------------------------------------------\r
+\r
+INCLUDE        "GFXC_CK4.EQU"\r
+\r
+;----------------------------------------------------------------------------\r
+\r
+CGAGR          =       1\r
+EGAGR          =       2\r
+VGAGR          =       3\r
+\r
+GRMODE         =       CGAGR\r
+PROFILE                =       0                       ; 1=keep stats on tile drawing\r
+\r
+SC_INDEX       =       03C4h\r
+SC_RESET       =       0\r
+SC_CLOCK       =       1\r
+SC_MAPMASK     =       2\r
+SC_CHARMAP     =       3\r
+SC_MEMMODE     =       4\r
+\r
+CRTC_INDEX     =       03D4h\r
+CRTC_H_TOTAL   =       0\r
+CRTC_H_DISPEND =       1\r
+CRTC_H_BLANK   =       2\r
+CRTC_H_ENDBLANK        =       3\r
+CRTC_H_RETRACE =       4\r
+CRTC_H_ENDRETRACE =    5\r
+CRTC_V_TOTAL   =       6\r
+CRTC_OVERFLOW  =       7\r
+CRTC_ROWSCAN   =       8\r
+CRTC_MAXSCANLINE =     9\r
+CRTC_CURSORSTART =     10\r
+CRTC_CURSOREND =       11\r
+CRTC_STARTHIGH =       12\r
+CRTC_STARTLOW  =       13\r
+CRTC_CURSORHIGH        =       14\r
+CRTC_CURSORLOW =       15\r
+CRTC_V_RETRACE =       16\r
+CRTC_V_ENDRETRACE =    17\r
+CRTC_V_DISPEND =       18\r
+CRTC_OFFSET    =       19\r
+CRTC_UNDERLINE =       20\r
+CRTC_V_BLANK   =       21\r
+CRTC_V_ENDBLANK        =       22\r
+CRTC_MODE      =       23\r
+CRTC_LINECOMPARE =     24\r
+\r
+\r
+GC_INDEX       =       03CEh\r
+GC_SETRESET    =       0\r
+GC_ENABLESETRESET =    1\r
+GC_COLORCOMPARE        =       2\r
+GC_DATAROTATE  =       3\r
+GC_READMAP     =       4\r
+GC_MODE                =       5\r
+GC_MISCELLANEOUS =     6\r
+GC_COLORDONTCARE =     7\r
+GC_BITMASK     =       8\r
+\r
+ATR_INDEX      =       03c0h\r
+ATR_MODE       =       16\r
+ATR_OVERSCAN   =       17\r
+ATR_COLORPLANEENABLE = 18\r
+ATR_PELPAN     =       19\r
+ATR_COLORSELECT        =       20\r
+\r
+STATUS_REGISTER_1     =        03dah\r
+\r
+\r
+MACRO  WORDOUT\r
+       out     dx,ax\r
+ENDM\r
+\r
+if 0\r
+\r
+MACRO  WORDOUT\r
+       out     dx,al\r
+       inc     dx\r
+       xchg    al,ah\r
+       out     dx,al\r
+       dec     dx\r
+       xchg    al,ah\r
+ENDM\r
+\r
+endif\r
+\r
+UPDATEWIDE     =       22\r
+UPDATEHIGH     =       14\r
+\r
+;\r
+; tile info offsets from segment tinf\r
+;\r
+\r
+ANIM           =       402\r
+SPEED          =       (ANIM+NUMTILE16)\r
+\r
+NORTHWALL      =       (SPEED+NUMTILE16)\r
+EASTWALL       =       (NORTHWALL+NUMTILE16M)\r
+SOUTHWALL   =  (EASTWALL+NUMTILE16M)\r
+WESTWALL    =  (SOUTHWALL+NUMTILE16M)\r
+MANIM       =  (WESTWALL+NUMTILE16M)\r
+INTILE      =  (MANIM+NUMTILE16M)\r
+MSPEED      =  (INTILE+NUMTILE16M)\r
+\r
+\r
+IFE GRMODE-EGAGR\r
+SCREENWIDTH    =       64\r
+ENDIF\r
+IFE GRMODE-CGAGR\r
+SCREENWIDTH    =       128\r
+ENDIF\r
diff --git a/16/keen456/KEEN4-6/KEEN4C/ID_HEADS.H b/16/keen456/KEEN4-6/KEEN4C/ID_HEADS.H
new file mode 100755 (executable)
index 0000000..a73a926
--- /dev/null
@@ -0,0 +1,109 @@
+/* Reconstructed Commander Keen 4-6 Source Code\r
+ * Copyright (C) 2021 K1n9_Duk3\r
+ *\r
+ * This file is primarily based on:\r
+ * Catacomb 3-D Source Code\r
+ * Copyright (C) 1993-2014 Flat Rock Software\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License along\r
+ * with this program; if not, write to the Free Software Foundation, Inc.,\r
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+ */\r
+\r
+// ID_GLOB.H\r
+\r
+\r
+#include <ALLOC.H>\r
+#include <CTYPE.H>\r
+#include <DOS.H>\r
+#include <ERRNO.H>\r
+#include <FCNTL.H>\r
+#include <IO.H>\r
+#include <MEM.H>\r
+#include <PROCESS.H>\r
+#include <STDIO.H>\r
+#include <STDLIB.H>\r
+#include <STRING.H>\r
+#include <SYS\STAT.H>\r
+\r
+#define __ID_GLOB__\r
+\r
+//--------------------------------------------------------------------------\r
+\r
+#define KEEN\r
+#define KEEN4\r
+\r
+#define        EXTENSION       "CK4"\r
+\r
+extern char far introscn;\r
+\r
+#include "GFXC_CK4.H"\r
+#include "AUDIOCK4.H"\r
+\r
+//--------------------------------------------------------------------------\r
+\r
+#define        TEXTGR  0\r
+#define        CGAGR   1\r
+#define        EGAGR   2\r
+#define        VGAGR   3\r
+\r
+#define GRMODE CGAGR\r
+\r
+#if GRMODE == EGAGR\r
+#define GREXT  "EGA"\r
+#endif\r
+#if GRMODE == CGAGR\r
+#define GREXT  "CGA"\r
+#endif\r
+\r
+//#define PROFILE\r
+\r
+//\r
+//     ID Engine\r
+//     Types.h - Generic types, #defines, etc.\r
+//     v1.0d1\r
+//\r
+\r
+#ifndef        __TYPES__\r
+#define        __TYPES__\r
+\r
+typedef        enum    {false,true}    boolean;\r
+typedef        unsigned        char            byte;\r
+typedef        unsigned        int                     word;\r
+typedef        unsigned        long            longword;\r
+typedef        byte *                                  Ptr;\r
+\r
+typedef        struct\r
+               {\r
+                       int     x,y;\r
+               } Point;\r
+typedef        struct\r
+               {\r
+                       Point   ul,lr;\r
+               } Rect;\r
+\r
+#define        nil     ((void *)0)\r
+\r
+#endif\r
+\r
+#include "ID_MM.H"\r
+#include "ID_CA.H"\r
+#include "ID_VW.H"\r
+#include "ID_RF.H"\r
+#include "ID_IN.H"\r
+#include "ID_SD.H"\r
+#include "ID_US.H"\r
+\r
+\r
+void   Quit (char *error);             // defined in user program\r
+\r
diff --git a/16/keen456/KEEN4-6/KEEN5/AUDIOCK5.H b/16/keen456/KEEN4-6/KEEN5/AUDIOCK5.H
new file mode 100755 (executable)
index 0000000..d7710a0
--- /dev/null
@@ -0,0 +1,145 @@
+/* Reconstructed Commander Keen 4-6 Source Code\r
+ * Copyright (C) 2021 K1n9_Duk3\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License along\r
+ * with this program; if not, write to the Free Software Foundation, Inc.,\r
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+ */\r
+\r
+#ifndef __AUDIO_H__\r
+#define __AUDIO_H__\r
+\r
+//#include "VERSION.H"\r
+\r
+/////////////////////////////////////////////////\r
+//\r
+// MUSE Header for .CK5\r
+//\r
+/////////////////////////////////////////////////\r
+\r
+#define NUMSOUNDS     LASTSOUND\r
+#define NUMSNDCHUNKS  ((3*LASTSOUND)+LASTMUSIC)\r
+\r
+//\r
+// Sound names & indexes\r
+//\r
+typedef enum {\r
+       SND_WORLDWALK1,        // 0\r
+       SND_WORLDWALK2,        // 1\r
+       SND_JUMP,              // 2\r
+       SND_LAND,              // 3\r
+       SND_KEENFIRE,          // 4\r
+       SND_MINEEXPLODE,       // 5\r
+       SND_SLICESTARBOUNCE,   // 6\r
+       SND_POGOBOUNCE,        // 7\r
+       SND_GETPOINTS,         // 8\r
+       SND_GETAMMO,           // 9\r
+       SND_GETWATER,          // 10\r
+       SND_11,                // 11\r
+       SND_ENTERLEVEL,        // 12\r
+       SND_LEVELDONE,         // 13\r
+       SND_NOWAY,             // 14\r
+       SND_HELMETHIT,         // 15\r
+       SND_BOUNCE,            // 16\r
+       SND_EXTRAKEEN,         // 17\r
+       SND_OPENCARDDOOR,      // 18\r
+       SND_GETKEY,            // 19\r
+       SND_PLUMMET,           // 20\r
+       SND_USESWITCH,         // 21\r
+       SND_22,                // 22\r
+       SND_KEENDEAD,          // 23\r
+       SND_24,                // 24\r
+       SND_SHOTEXPLODE,       // 25\r
+       SND_26,                // 26\r
+       SND_SPIROGRAB,         // 27\r
+       SND_SPINDREDBOUNCE,    // 28\r
+       SND_ENEMYSHOT,         // 29\r
+       SND_ENEMYSHOTEXPLODE,  // 30\r
+       SND_AMPTONWALK1,       // 31\r
+       SND_AMPTONWALK2,       // 32\r
+       SND_AMPTONDIE,         // 33\r
+       SND_SHOWSTATUS,        // 34\r
+       SND_HIDESTATUS,        // 35\r
+       SND_SHELLEYEXPLODE,    // 36\r
+       SND_SPINDREDFLIP,      // 37\r
+       SND_MASTERATTACK,      // 38\r
+       SND_MASTERBLAST,       // 39\r
+       SND_SHIKADIATTACK,     // 40\r
+       SND_TELEPORT,          // 41\r
+       SND_SHOCKSHUNDBARK,    // 42\r
+       SND_FLAGSPIN,          // 43\r
+       SND_FLAGLAND,          // 44\r
+       SND_SHOCKBALLEXPLODE,  // 45\r
+       KEENPADDLESND,         // 46\r
+       BALLBOUNCESND,         // 47\r
+       COMPPADDLESND,         // 48\r
+       COMPSCOREDSND,         // 49\r
+       KEENSCOREDSND,         // 50\r
+       SND_51,                // 51\r
+       SND_BIGSPARK,          // 52\r
+       SND_GAMEOVER1,         // 53\r
+       SND_GAMEOVER2,         // 54\r
+       SND_GETKEYCARD,        // 55\r
+       SND_56,                // 56\r
+       SND_LANDONFUSE,        // 57\r
+       SND_SPARKYCHARGE,      // 58\r
+       SND_SPHEREFULBOUNCE,   // 59\r
+       SND_OPENDOOR,          // 60\r
+       SND_SPIROFLY,          // 61\r
+       SND_62,                // 62\r
+       SND_ELEVATORDOOR,      // 63\r
+       LASTSOUND\r
+} soundnames;\r
+\r
+#if LASTSOUND != 64\r
+#error bad sound enum!\r
+#endif\r
+\r
+#define NOWAYSND SND_NOWAY\r
+\r
+//\r
+// Base offsets\r
+//\r
+#define STARTPCSOUNDS     0\r
+#define STARTADLIBSOUNDS  (STARTPCSOUNDS+NUMSOUNDS)\r
+#define STARTDIGISOUNDS   (STARTADLIBSOUNDS+NUMSOUNDS)\r
+#define STARTMUSIC        (STARTDIGISOUNDS+NUMSOUNDS)\r
+\r
+//\r
+// Music names & indexes\r
+//\r
+typedef enum {\r
+       CAMEIN_MUS,\r
+       HAVING_T_MUS,\r
+       SKATING_MUS,\r
+       SNOOPING_MUS,\r
+       BAGPIPES_MUS,\r
+       WEDNESDY_MUS,\r
+       ROCK_ME_MUS,\r
+       BREATHE_MUS,\r
+       SHIKAIRE_MUS,\r
+       SPHEREFUL_MUS,\r
+       TIGHTER_MUS,\r
+       ROBOROCK_MUS,\r
+       FANFARE_MUS,\r
+       FEARSOME_MUS,\r
+       LASTMUSIC\r
+} musicnames;\r
+\r
+/////////////////////////////////////////////////\r
+//\r
+// Thanks for playing with MUSE!\r
+//\r
+/////////////////////////////////////////////////\r
+\r
+#endif
\ No newline at end of file
diff --git a/16/keen456/KEEN4-6/KEEN5/GFXE_CK5.EQU b/16/keen456/KEEN4-6/KEEN5/GFXE_CK5.EQU
new file mode 100755 (executable)
index 0000000..1123798
--- /dev/null
@@ -0,0 +1,55 @@
+;=====================================\r
+;\r
+; Graphics .EQU file for .CK5\r
+; not IGRAB-ed :)\r
+;\r
+;=====================================\r
+\r
+;INCLUDE "VERSION.EQU"\r
+\r
+;\r
+; Amount of each data item\r
+;\r
+NUMFONT     =  3\r
+NUMFONTM    =  0\r
+NUMPICM     =  3\r
+NUMTILE8    =  108\r
+NUMTILE8M   =  36\r
+NUMTILE32   =  0\r
+NUMTILE32M  =  0\r
+\r
+;\r
+; Amount of each item in episode 5\r
+;\r
+NUMPICS     =  93\r
+NUMSPRITES  =  346\r
+NUMTILE16   =  1512\r
+NUMTILE16M  =  2952\r
+NUMEXTERN   =  15\r
+\r
+\r
+;\r
+; File offsets for data items\r
+;\r
+STRUCTPIC       =      0\r
+STRUCTPICM      =      1\r
+STRUCTSPRITE    =      2\r
+\r
+STARTFONT       =      3\r
+STARTFONTM      =      (STARTFONT+NUMFONT)\r
+STARTPICS       =      (STARTFONTM+NUMFONTM)\r
+STARTPICM       =      (STARTPICS+NUMPICS)\r
+STARTSPRITES    =      (STARTPICM+NUMPICM)\r
+STARTTILE8      =      (STARTSPRITES+NUMSPRITES)\r
+STARTTILE8M     =      (STARTTILE8+1)\r
+STARTTILE16     =      (STARTTILE8M+1)\r
+STARTTILE16M    =      (STARTTILE16+NUMTILE16)\r
+STARTTILE32     =      (STARTTILE16M+NUMTILE16M)\r
+STARTTILE32M    =      (STARTTILE32+NUMTILE32)\r
+STARTEXTERN     =      (STARTTILE32M+NUMTILE32M)\r
+\r
+NUMCHUNKS       =      (STARTEXTERN+NUMEXTERN)\r
+\r
+;\r
+; Thank you for using IGRAB!\r
+;\r
diff --git a/16/keen456/KEEN4-6/KEEN5/GFXE_CK5.H b/16/keen456/KEEN4-6/KEEN5/GFXE_CK5.H
new file mode 100755 (executable)
index 0000000..f35848c
--- /dev/null
@@ -0,0 +1,690 @@
+/* Reconstructed Commander Keen 4-6 Source Code\r
+ * Copyright (C) 2021 K1n9_Duk3\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License along\r
+ * with this program; if not, write to the Free Software Foundation, Inc.,\r
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+ */\r
+\r
+#ifndef __GFX_H__\r
+#define __GFX_H__\r
+\r
+//#include "VERSION.H"\r
+\r
+//////////////////////////////////////\r
+//\r
+// Graphics .H file for .CK5\r
+// not IGRAB-ed :)\r
+//\r
+//////////////////////////////////////\r
+\r
+//\r
+// Lump creation macros\r
+//\r
+\r
+#define START_LUMP(actualname, dummyname) actualname, dummyname=actualname-1,\r
+#define END_LUMP(actualname, dummyname) dummyname, actualname=dummyname-1,\r
+\r
+//\r
+// Amount of each data item\r
+//\r
+\r
+//common numbers:\r
+#define NUMCHUNKS    NUMGRCHUNKS\r
+#define NUMFONT      3\r
+#define NUMFONTM     0\r
+#define NUMPICM      3\r
+#define NUMTILE8     108       // BUG: only 104 tiles exist in EGAGRAPH!\r
+#define NUMTILE8M    36                // BUG: only 20 tiles exist in EGAGRAPH!\r
+#define NUMTILE32    0\r
+#define NUMTILE32M   0\r
+\r
+//episode-specific numbers:\r
+#define NUMPICS      93\r
+#define NUMSPRITES   346\r
+#define NUMTILE16    1512\r
+#define NUMTILE16M   2952\r
+#define NUMEXTERNS   17\r
+\r
+//\r
+// File offsets for data items\r
+//\r
+#define STRUCTPIC    0\r
+#define STRUCTPICM   1\r
+#define STRUCTSPRITE 2\r
+\r
+#define STARTFONT    3\r
+#define STARTFONTM   (STARTFONT+NUMFONT)\r
+#define STARTPICS    (STARTFONTM+NUMFONTM)\r
+#define STARTPICM    (STARTPICS+NUMPICS)\r
+#define STARTSPRITES (STARTPICM+NUMPICM)\r
+#define STARTTILE8   (STARTSPRITES+NUMSPRITES)\r
+#define STARTTILE8M  (STARTTILE8+1)\r
+#define STARTTILE16  (STARTTILE8M+1)\r
+#define STARTTILE16M (STARTTILE16+NUMTILE16)\r
+#define STARTTILE32  (STARTTILE16M+NUMTILE16M)\r
+#define STARTTILE32M (STARTTILE32+NUMTILE32)\r
+#define STARTEXTERNS (STARTTILE32M+NUMTILE32M)\r
+\r
+typedef enum {\r
+       // Lump Start\r
+\r
+       LASTFONT=STARTPICS-1,\r
+\r
+       START_LUMP(HELP_LUMP_START, __HELPSTART)\r
+       H_HELPPIC,                   // 6\r
+       H_LARROWPIC,                 // 7\r
+       H_RARROWPIC,                 // 8\r
+       H_ESCPIC,                    // 9\r
+       H_ENTERPIC,                  // 10\r
+       H_BOTTOMINSTRPIC,            // 11\r
+       H_GUMPIC,                    // 12\r
+       H_MARSHMALLOWPIC,            // 13\r
+       H_CHOCMILKPIC,               // 14\r
+       H_TARTSTIXPIC,               // 15\r
+       H_STOOPIESPIC,               // 16\r
+       H_SUGARPIC,                  // 17\r
+       H_VITALINPIC,                // 18\r
+       H_STUNNERPIC,                // 19\r
+       H_GEMPIC,                    // 20\r
+       H_KEGPIC,                    // 21\r
+       H_ENDOFTEXTPIC,              // 22\r
+       H_HELPMENUPIC,               // 23\r
+       H_HANDPIC,                   // 24\r
+       H_ARROWSENTERESCPIC,         // 25\r
+       H_FLASHARROW1PIC,            // 26\r
+       H_FLASHARROW2PIC,            // 27\r
+       H_TOPWINDOWPIC,              // 28\r
+       H_LEFTWINDOWPIC,             // 29\r
+       H_RIGHTWINDOWPIC,            // 30\r
+       H_BOTTOMINFOPIC,             // 31\r
+       H_BOTTOMWINDOWPIC,           // 32\r
+       H_BARPIC,                    // 33\r
+       H_SPARKYPIC,                 // 34\r
+       H_AMPTONPIC,                 // 35\r
+       H_SLICESTARPIC,              // 36\r
+       H_VOLTEFACEPIC,              // 37\r
+       H_ROBOREDPIC,                // 38\r
+       H_SHELLEYPIC,                // 39\r
+       H_SPIROGRIPPIC,              // 40\r
+       H_MINEPIC,                   // 41\r
+       H_SPINDREDPIC,               // 42\r
+       H_SHIKADIPIC,                // 43\r
+       H_SPHEREFULPIC,              // 44\r
+       H_PETPIC,                    // 45\r
+       H_MASTERPIC,                 // 46\r
+       H_IDLOGOPIC,                 // 47\r
+       H_STORY1PIC,                 // 48\r
+       H_STORY2PIC,                 // 49\r
+       H_STORY3PIC,                 // 50\r
+       H_STORY4PIC,                 // 51\r
+       H_VISAPIC,                   // 52\r
+       H_MCPIC,                     // 53\r
+       H_KEENTHUMBSUPPIC,           // 54\r
+       H_END1PIC,                   // 55\r
+       H_END2PIC,                   // 56\r
+       H_END3PIC,                   // 57\r
+       H_END4PIC,                   // 58\r
+       H_END5PIC,                   // 59\r
+       H_END6PIC,                   // 60\r
+       H_END7PIC,                   // 61\r
+       H_END8PIC,                   // 62\r
+       H_CONGRATSPIC,               // 63\r
+       H_KEENFEEDSPIC,              // 64\r
+       H_DOORCARDPIC,               // 65\r
+       H_KEEN6PIC,                  // 66\r
+       END_LUMP(HELP_LUMP_END, __HELPEND)\r
+\r
+       START_LUMP(CONTROLS_LUMP_START, __CONTROLSSTART)\r
+       CP_MAINMENUPIC,              // 67\r
+       CP_NEWGAMEMENUPIC,           // 68\r
+       CP_LOADMENUPIC,              // 69\r
+       CP_SAVEMENUPIC,              // 70\r
+       CP_CONFIGMENUPIC,            // 71\r
+       CP_SOUNDMENUPIC,             // 72\r
+       CP_MUSICMENUPIC,             // 73\r
+       CP_KEYBOARDMENUPIC,          // 74\r
+       CP_KEYMOVEMENTPIC,           // 75\r
+       CP_KEYBUTTONPIC,             // 76\r
+       CP_JOYSTICKMENUPIC,          // 77\r
+       CP_OPTIONSMENUPIC,           // 78\r
+       CP_PADDLEWARPIC,             // 79\r
+       CP_QUITPIC,                  // 80\r
+       CP_JOYSTICKPIC,              // 81\r
+       CP_MENUSCREENPIC,            // 82\r
+       END_LUMP(CONTROLS_LUMP_END, __COLTROLSEND)\r
+\r
+       START_LUMP(_LUMP_START, __START)\r
+       IDSOFTPIC,                   // 83\r
+       PROGTEAMPIC,                 // 84\r
+       ARTISTPIC,                   // 85\r
+       DIRECTORPIC,                 // 86\r
+       SW_BACKGROUNDPIC,            // 87\r
+       TITLEPICPIC,                 // 88\r
+       MILKYWAYPIC,                 // 89\r
+       END_LUMP(_LUMP_END, __END)\r
+\r
+       START_LUMP(KEENTALK_LUMP_START, __KEENTALKSTART)\r
+       KEENTALK1PIC,                // 90\r
+       KEENTALK2PIC,                // 91\r
+       END_LUMP(KEENTALK_LUMP_END, __KEENTALKEND)\r
+\r
+       START_LUMP(LOADING_LUMP_START, __LOADINGSTART)\r
+       KEENCOUNT1PIC,               // 92\r
+       KEENCOUNT2PIC,               // 93\r
+       KEENCOUNT3PIC,               // 94\r
+       KEENCOUNT4PIC,               // 95\r
+       KEENCOUNT5PIC,               // 96\r
+       KEENCOUNT6PIC,               // 97\r
+       END_LUMP(LOADING_LUMP_END, __LOADINGEND)\r
+\r
+       GAMEOVERPIC,                 // 98\r
+\r
+       CP_MENUMASKPICM,             // 99\r
+       CORDPICM,                    // 100\r
+       METALPOLEPICM,               // 101\r
+\r
+       //\r
+       // SPRITES\r
+       //\r
+\r
+       START_LUMP(PADDLE_LUMP_START, __PADDLESTART)\r
+       PADDLESPR,                   // 102\r
+       BALLSPR,                     // 103\r
+       BALL1PIXELTOTHERIGHTSPR,     // 104\r
+       BALL2PIXELSTOTHERIGHTSPR,    // 105\r
+       BALL3PIXELSTOTHERIGHTSPR,    // 106\r
+       END_LUMP(PADDLE_LUMP_END, __PADDLEEND)\r
+\r
+       DEMOPLAQUESPR,               // 107\r
+\r
+       START_LUMP(KEEN_LUMP_START, __KEENSTART)\r
+       KEENSTANDRSPR,               // 108\r
+       KEENRUNR1SPR,                // 109\r
+       KEENRUNR2SPR,                // 110\r
+       KEENRUNR3SPR,                // 111\r
+       KEENRUNR4SPR,                // 112\r
+       KEENJUMPR1SPR,               // 113\r
+       KEENJUMPR2SPR,               // 114\r
+       KEENJUMPR3SPR,               // 115\r
+       KEENSTANDLSPR,               // 116\r
+       KEENRUNL1SPR,                // 117\r
+       KEENRUNL2SPR,                // 118\r
+       KEENRUNL3SPR,                // 119\r
+       KEENRUNL4SPR,                // 120\r
+       KEENJUMPL1SPR,               // 121\r
+       KEENJUMPL2SPR,               // 122\r
+       KEENJUMPL3SPR,               // 123\r
+       KEENLOOKUSPR,                // 124\r
+       KEENWAITR1SPR,               // 125\r
+       KEENWAITR2SPR,               // 126\r
+       KEENWAITR3SPR,               // 127\r
+       KEENSITREAD1SPR,             // 128\r
+       KEENSITREAD2SPR,             // 129\r
+       KEENSITREAD3SPR,             // 130\r
+       KEENSITREAD4SPR,             // 131\r
+       KEENREAD1SPR,                // 132\r
+       KEENREAD2SPR,                // 133\r
+       KEENREAD3SPR,                // 134\r
+       KEENSTOPREAD1SPR,            // 135\r
+       KEENSTOPREAD2SPR,            // 136\r
+       KEENLOOKD1SPR,               // 137\r
+       KEENLOOKD2SPR,               // 138\r
+       KEENONPLATSPR,               // 139\r
+       KEENDIE1SPR,                 // 140\r
+       KEENDIE2SPR,                 // 141\r
+       KEENSTUNSPR,                 // 142\r
+       STUNSTARS1SPR,               // 143\r
+       STUNSTARS2SPR,               // 144\r
+       STUNSTARS3SPR,               // 145\r
+       KEENSHOOTLSPR,               // 146\r
+       KEENJLSHOOTLSPR,             // 147\r
+       KEENJSHOOTDSPR,              // 148\r
+       KEENJSHOOTUSPR,              // 149\r
+       KEENSHOOTUSPR,               // 150\r
+       KEENSHOOTRSPR,               // 151\r
+       KEENJRSHOOTRSPR,             // 152\r
+       STUN1SPR,                    // 153\r
+       STUN2SPR,                    // 154\r
+       STUN3SPR,                    // 155\r
+       STUN4SPR,                    // 156\r
+       STUNHIT1SPR,                 // 157\r
+       STUNHIT2SPR,                 // 158\r
+       KEENSHINNYR1SPR,             // 159\r
+       KEENSHINNYR2SPR,             // 160\r
+       KEENSHINNYR3SPR,             // 161\r
+       KEENSLIDED1SPR,              // 162\r
+       KEENSLIDED2SPR,              // 163\r
+       KEENSLIDED3SPR,              // 164\r
+       KEENSLIDED4SPR,              // 165\r
+       KEENSHINNYL1SPR,             // 166\r
+       KEENSHINNYL2SPR,             // 167\r
+       KEENSHINNYL3SPR,             // 168\r
+       KEENPLSHOOTUSPR,             // 169\r
+       KEENPRSHOOTUSPR,             // 170\r
+       KEENPRSHOOTDSPR,             // 171\r
+       KEENPLSHOOTDSPR,             // 172\r
+       KEENPSHOOTLSPR,              // 173\r
+       KEENPSHOOTRSPR,              // 174\r
+       KEENENTER1SPR,               // 175\r
+       KEENENTER2SPR,               // 176\r
+       KEENENTER3SPR,               // 177\r
+       KEENENTER4SPR,               // 178\r
+       KEENENTER5SPR,               // 179\r
+       KEENHANGLSPR,                // 180\r
+       KEENHANGRSPR,                // 181\r
+       KEENCLIMBEDGEL1SPR,          // 182\r
+       KEENCLIMBEDGEL2SPR,          // 183\r
+       KEENCLIMBEDGEL3SPR,          // 184\r
+       KEENCLIMBEDGEL4SPR,          // 185\r
+       KEENCLIMBEDGER1SPR,          // 186\r
+       KEENCLIMBEDGER2SPR,          // 187\r
+       KEENCLIMBEDGER3SPR,          // 188\r
+       KEENCLIMBEDGER4SPR,          // 189\r
+       KEENPOGOR1SPR,               // 190\r
+       KEENPOGOR2SPR,               // 191\r
+       KEENPOGOL1SPR,               // 192\r
+       KEENPOGOL2SPR,               // 193\r
+       BONUS100UPSPR,               // 194\r
+       BONUS100SPR,                 // 195\r
+       BONUS200SPR,                 // 196\r
+       BONUS500SPR,                 // 197\r
+       BONUS1000SPR,                // 198\r
+       BONUS2000SPR,                // 199\r
+       BONUS5000SPR,                // 200\r
+       BONUS1UPSPR,                 // 201\r
+       BONUSCLIPSPR,                // 202\r
+       VIVAPOOF1SPR,                // 203\r
+       VIVAPOOF2SPR,                // 204\r
+       VIVAPOOF3SPR,                // 205\r
+       VIVAPOOF4SPR,                // 206\r
+       END_LUMP(KEEN_LUMP_END, __KEENEND)\r
+\r
+       START_LUMP(KEYCARD_LUMP_START, __KEYCARDSTART)\r
+       DOORCARD1SPR,                // 207\r
+       DOORCARD2SPR,                // 208\r
+       BONUSCARDSPR,                // 209\r
+       END_LUMP(KEYCARD_LUMP_END, __KEYCARDEND)\r
+\r
+       START_LUMP(SUGAR1_LUMP_START, __SUGAR1START)\r
+       SUGAR1ASPR,                  // 210\r
+       SUGAR1BSPR,                  // 211\r
+       END_LUMP(SUGAR1_LUMP_END, __SUGAR1END)\r
+\r
+       START_LUMP(SUGAR2_LUMP_START, __SUGAR2START)\r
+       SUGAR2ASPR,                  // 212\r
+       SUGAR2BSPR,                  // 213\r
+       END_LUMP(SUGAR2_LUMP_END, __SUGAR2END)\r
+\r
+       START_LUMP(SUGAR3_LUMP_START, __SUGAR3START)\r
+       SUGAR3ASPR,                  // 214\r
+       SUGAR3BSPR,                  // 215\r
+       END_LUMP(SUGAR3_LUMP_END, __SUGAR3END)\r
+\r
+       START_LUMP(SUGAR4_LUMP_START, __SUGAR4START)\r
+       SUGAR4ASPR,                  // 216\r
+       SUGAR4BSPR,                  // 217\r
+       END_LUMP(SUGAR4_LUMP_END, __SUGAR4END)\r
+\r
+       START_LUMP(SUGAR5_LUMP_START, __SUGAR5START)\r
+       SUGAR5ASPR,                  // 218\r
+       SUGAR5BSPR,                  // 219\r
+       END_LUMP(SUGAR5_LUMP_END, __SUGAR5END)\r
+\r
+       START_LUMP(SUGAR6_LUMP_START, __SUGAR6START)\r
+       SUGAR6ASPR,                  // 220\r
+       SUGAR6BSPR,                  // 221\r
+       END_LUMP(SUGAR6_LUMP_END, __SUGAR6END)\r
+\r
+       START_LUMP(ONEUP_LUMP_START, __ONEUPSTART)\r
+       ONEUPASPR,                   // 222\r
+       ONEUPBSPR,                   // 223\r
+       END_LUMP(ONEUP_LUMP_END, __ONEUPEND)\r
+\r
+       START_LUMP(KEYGEM_LUMP_START, __KEYGEMSTART)\r
+       REDGEM1SPR,                  // 224\r
+       REDGEM2SPR,                  // 225\r
+       YELLOWGEM1SPR,               // 226\r
+       YELLOWGEM2SPR,               // 227\r
+       BLUEGEM1SPR,                 // 228\r
+       BLUEGEM2SPR,                 // 229\r
+       GREENGEM1SPR,                // 230\r
+       GREENGEM2SPR,                // 231\r
+       BONUSGEMSPR,                 // 232\r
+       END_LUMP(KEYGEM_LUMP_END, __KEYGEMEND)\r
+\r
+       START_LUMP(AMMO_LUMP_START, __AMMOSTART)\r
+       STUNCLIP1SPR,                // 233\r
+       STUNCLIP2SPR,                // 234\r
+       END_LUMP(AMMO_LUMP_END, __AMMOEND)\r
+\r
+       SCOREBOXSPR,                 // 235\r
+\r
+       START_LUMP(LASER_LUMP_START, __LASERSTART)\r
+       LASER1SPR,                   // 236\r
+       LASER2SPR,                   // 237\r
+       LASER3SPR,                   // 238\r
+       LASER4SPR,                   // 239\r
+       LASERHIT1SPR,                // 240\r
+       LASERHIT2SPR,                // 241\r
+       END_LUMP(LASER_LUMP_END, __LASEREND)\r
+\r
+       START_LUMP(WORLDKEEN_LUMP_START, __WORLDKEENSTART)\r
+       WORLDKEENL1SPR,              // 242\r
+       WORLDKEENL2SPR,              // 243\r
+       WORLDKEENL3SPR,              // 244\r
+       WORLDKEENR1SPR,              // 245\r
+       WORLDKEENR2SPR,              // 246\r
+       WORLDKEENR3SPR,              // 247\r
+       WORLDKEENU1SPR,              // 248\r
+       WORLDKEENU2SPR,              // 249\r
+       WORLDKEENU3SPR,              // 250\r
+       WORLDKEEND1SPR,              // 251\r
+       WORLDKEEND2SPR,              // 252\r
+       WORLDKEEND3SPR,              // 253\r
+       WORLDKEENDR1SPR,             // 254\r
+       WORLDKEENDR2SPR,             // 255\r
+       WORLDKEENDR3SPR,             // 256\r
+       WORLDKEENDL1SPR,             // 257\r
+       WORLDKEENDL2SPR,             // 258\r
+       WORLDKEENDL3SPR,             // 259\r
+       WORLDKEENUL1SPR,             // 260\r
+       WORLDKEENUL2SPR,             // 261\r
+       WORLDKEENUL3SPR,             // 262\r
+       WORLDKEENUR1SPR,             // 263\r
+       WORLDKEENUR2SPR,             // 264\r
+       WORLDKEENUR3SPR,             // 265\r
+       WORLDKEENWAVE1SPR,           // 266\r
+       WORLDKEENWAVE2SPR,           // 267\r
+       FLAGFLIP1SPR,                // 268\r
+       FLAGFLIP2SPR,                // 269\r
+       FLAGFLIP3SPR,                // 270\r
+       FLAGFLIP4SPR,                // 271\r
+       FLAGFLIP5SPR,                // 272\r
+       FLAGFALL1SPR,                // 273\r
+       FLAGFALL2SPR,                // 274\r
+       FLAGFLAP1SPR,                // 275\r
+       FLAGFLAP2SPR,                // 276\r
+       FLAGFLAP3SPR,                // 277\r
+       FLAGFLAP4SPR,                // 278\r
+       SHOOTINGSTAR1SPR,            // 279\r
+       SHOOTINGSTAR2SPR,            // 280\r
+       WORLDTELSPARK1SPR,           // 281\r
+       WORLDTELSPARK2SPR,           // 282\r
+       END_LUMP(WORLDKEEN_LUMP_END, __WORLDKEENEND)\r
+\r
+       START_LUMP(FUSE_LUMP_START, __FUSESTART)\r
+       FUSEFLASH1SPR,               // 283\r
+       FUSEFLASH2SPR,               // 284\r
+       FUSEFLASH3SPR,               // 285\r
+       END_LUMP(FUSE_LUMP_END, __FUSEEND)\r
+\r
+       START_LUMP(STAREXPLODE_LUMP_START, __SMALLSPARKSTART)\r
+       STAREXPLODE1SPR,             // 286\r
+       STAREXPLODE2SPR,             // 287\r
+       STAREXPLODE3SPR,             // 288\r
+       STAREXPLODE4SPR,             // 289\r
+       END_LUMP(STAREXPLODE_LUMP_END, __SMALLSPARKEND)\r
+\r
+       START_LUMP(TELEPORT_LUMP_START, __TELEPORTSTART)\r
+       TELEPORTSPARK1SPR,           // 290\r
+       TELEPORTSPARK2SPR,           // 291\r
+       TELEPORTZAP1SPR,             // 292\r
+       TELEPORTZAP2SPR,             // 293\r
+       END_LUMP(TELEPORT_LUMP_END, __TELEPORTEND)\r
+\r
+       START_LUMP(SCOTTIE_LUMP_START, __KORATHSTART)\r
+       SCOTTIEWALKL1SPR,            // 294\r
+       SCOTTIEWALKL2SPR,            // 295\r
+       SCOTTIEWALKL3SPR,            // 296\r
+       SCOTTIEWALKL4SPR,            // 297\r
+       SCOTTIEWALKR1SPR,            // 298\r
+       SCOTTIEWALKR2SPR,            // 299\r
+       SCOTTIEWALKR3SPR,            // 300\r
+       SCOTTIEWALKR4SPR,            // 301\r
+       SCOTTIEFACESPR,              // 302\r
+       SCOTTIESTUNSPR,              // 303\r
+       END_LUMP(SCOTTIE_LUMP_END, __KORATHEND)\r
+\r
+       START_LUMP(MASTER_LUMP_START, __MASTERSTART)\r
+       MASTER1SPR,                  // 304\r
+       MASTER2SPR,                  // 305\r
+       MASTER3SPR,                  // 306\r
+       MASTER4SPR,                  // 307\r
+       MASTERTELEPORT1SPR,          // 308\r
+       MASTERTELEPORT2SPR,          // 309\r
+       SHIKMASTERCASTRSPR,          // 310\r
+       SHIKMASTERCASTLSPR,          // 311\r
+       MASTERFLOORSPARK1SPR,        // 312\r
+       MASTERFLOORSPARK2SPR,        // 313\r
+       MASTERFLOORSPARK3SPR,        // 314\r
+       MASTERFLOORSPARK4SPR,        // 315\r
+       MASTERSHOT1SPR,              // 316\r
+       MASTERSHOT2SPR,              // 317\r
+       MASTERSHOT3SPR,              // 318\r
+       MASTERSHOT4SPR,              // 319\r
+       END_LUMP(MASTER_LUMP_END, __MASTEREND)\r
+\r
+       START_LUMP(SHIKADI_LUMP_START, __SHIKADISTART)\r
+       SHIKADI1SPR,                 // 320\r
+       SHIKADI2SPR,                 // 321\r
+       SHIKADI3SPR,                 // 322\r
+       SHIKADI4SPR,                 // 323\r
+       SHIKADIGRABRSPR,             // 324\r
+       SHIKADIGRABLSPR,             // 325\r
+       SHIKADIPOLESPARK1SPR,        // 326\r
+       SHIKADIPOLESPARK2SPR,        // 327\r
+       SHIKADIWALKR1SPR,            // 328\r
+       SHIKADIWALKR2SPR,            // 329\r
+       SHIKADIWALKR3SPR,            // 330\r
+       SHIKADIWALKR4SPR,            // 331\r
+       SHIKADIWALKL1SPR,            // 332\r
+       SHIKADIWALKL2SPR,            // 333\r
+       SHIKADIWALKL3SPR,            // 334\r
+       SHIKADIWALKL4SPR,            // 335\r
+       SHIKADISTUNSPR,              // 336\r
+       END_LUMP(SHIKADI_LUMP_END, __SHIKADIEND)\r
+\r
+       START_LUMP(SHOCKSHUND_LUMP_START, __SHOCKSHUNDSTART)\r
+       PETSIT1SPR,                  // 337\r
+       PETSIT2SPR,                  // 338\r
+       PETRUNR1SPR,                 // 339\r
+       PETRUNR2SPR,                 // 340\r
+       PETRUNR3SPR,                 // 341\r
+       PETRUNR4SPR,                 // 342\r
+       PETRUNL1SPR,                 // 343\r
+       PETRUNL2SPR,                 // 344\r
+       PETRUNL3SPR,                 // 345\r
+       PETRUNL4SPR,                 // 346\r
+       PETJUMPLSPR,                 // 347\r
+       PETJUMPRSPR,                 // 348\r
+       PETBARKR1SPR,                // 349\r
+       PETBARKR2SPR,                // 350\r
+       PETBARKL1SPR,                // 351\r
+       PETBARKL2SPR,                // 352\r
+       PETSTUNSPR,                  // 353\r
+       PETSPARK1SPR,                // 354\r
+       PETSPARK2SPR,                // 355\r
+       PETSPARKHIT1SPR,             // 356\r
+       PETSPARKHIT2SPR,             // 357\r
+       END_LUMP(SHOCKSHUND_LUMP_END, __SHOCKSHUNDEND)\r
+\r
+       START_LUMP(SPHEREFUL_LUMP_START, __SPHEREFULSTART)\r
+       SPHEREFUL1SPR,               // 358\r
+       SPHEREFUL2SPR,               // 359\r
+       SPHEREFUL3SPR,               // 360\r
+       SPHEREFUL4SPR,               // 361\r
+       SPHEREGUARD1SPR,             // 362\r
+       SPHEREGUARD2SPR,             // 363\r
+       SPHEREGUARD3SPR,             // 364\r
+       SPHEREGUARD4SPR,             // 365\r
+       END_LUMP(SPHEREFUL_LUMP_END, __SPHEREFULEND)\r
+\r
+       START_LUMP(SPARKY_LUMP_START, __SPARKYSTART)\r
+       SPARKYWALKL1SPR,             // 366\r
+       SPARKYWALKL2SPR,             // 367\r
+       SPARKYWALKL3SPR,             // 368\r
+       SPARKYWALKL4SPR,             // 369\r
+       SPARKYTURN1SPR,              // 370\r
+       SPARKYTURN2SPR,              // 371\r
+       SPARKYTURN3SPR,              // 372\r
+       SPARKYWALKR1SPR,             // 373\r
+       SPARKYWALKR2SPR,             // 374\r
+       SPARKYWALKR3SPR,             // 375\r
+       SPARKYWALKR4SPR,             // 376\r
+       SPARKYSTUNSPR,               // 377\r
+       END_LUMP(SPARKY_LUMP_END, __SPARKYEND)\r
+\r
+       START_LUMP(MINE_LUMP_START, __MINESTART)\r
+       SHIKADIMINESPR,              // 378\r
+       SHIKADIMINEEYESPR,           // 379\r
+       SHIKADIMINEPULSE1SPR,        // 380\r
+       SHIKADIMINEPULSE2SPR,        // 381\r
+       SHIKADIMINEBOOM1SPR,         // 382\r
+       SHIKADIMINEBOOM2SPR,         // 383\r
+       SHIKADIMINEPIECESPR,         // 384\r
+       END_LUMP(MINE_LUMP_END, __MINEEND)\r
+\r
+       START_LUMP(SLICESTAR_LUMP_START, __SLICESTARSTART)\r
+       SLICESTARSPR,                // 385\r
+       SLICESTARBOOMSPR,            // 386\r
+       END_LUMP(SLICESTAR_LUMP_END, __SLICASTAREND)\r
+\r
+       START_LUMP(ROBORED_LUMP_START, __ROBOREDSTART)\r
+       ROBOREDRSPR,                 // 387\r
+       ROBOREDLSPR,                 // 388\r
+       ROBOSHOT1SPR,                // 389\r
+       ROBOSHOT2SPR,                // 390\r
+       ROBOSHOTHIT1SPR,             // 391\r
+       ROBOSHOTHIT2SPR,             // 392\r
+       END_LUMP(ROBORED_LUMP_END, __ROBOREDEND)\r
+\r
+       START_LUMP(SPIRO_LUMP_START, __SPIROSTART)\r
+       SPIROSITDSPR,                // 393\r
+       SPIROSITLSPR,                // 394\r
+       SPIROSITUSPR,                // 395\r
+       SPIROSITRSPR,                // 396\r
+       SPIROSPINULSPR,              // 397\r
+       SPIROSPINURSPR,              // 398\r
+       SPIROSPINDRSPR,              // 399\r
+       SPIROSPINDLSPR,              // 400\r
+       SPIROSPINDSPR,               // 401\r
+       SPIROSPINLSPR,               // 402\r
+       SPIROSPINUSPR,               // 403\r
+       SPIROSPINRSPR,               // 404\r
+       END_LUMP(SPIRO_LUMP_END, __SPIROEND)\r
+\r
+       START_LUMP(AMPTON_LUMP_START, __AMPTONSTART)\r
+       AMPTONWALKR1SPR,             // 405\r
+       AMPTONWALKR2SPR,             // 406\r
+       AMPTONWALKR3SPR,             // 407\r
+       AMPTONWALKR4SPR,             // 408\r
+       AMPTONFACESPR,               // 409\r
+       AMPTONGRAB1SPR,              // 410\r
+       AMPTONGRAB2SPR,              // 411\r
+       AMTONWALKL1SPR,              // 412\r
+       AMTONWALKL2SPR,              // 413\r
+       AMTONWALKL3SPR,              // 414\r
+       AMTONWALKL4SPR,              // 415\r
+       AMPTONSTUNSPR,               // 416\r
+       END_LUMP(AMPTON_LUMP_END, __AMPTONEND)\r
+\r
+       START_LUMP(VOLTE_LUMP_START, __VOLTESTART)\r
+       VOLTEFACE1SPR,               // 417\r
+       VOLTEFACE2SPR,               // 418\r
+       VOLTEFACE3SPR,               // 419\r
+       VOLTEFACE4SPR,               // 420\r
+       VOLTEFACESTUNSPR,            // 421\r
+       END_LUMP(VOLTE_LUMP_END, __VOLTEEND)\r
+\r
+       START_LUMP(SLOTPLAT_LUMP_START, __PINKPLATSTART)\r
+       SLOTPLAT1SPR,                // 422\r
+       SLOTPLAT2SPR,                // 423\r
+       END_LUMP(SLOTPLAT_LUMP_END, __PINKPLATEND)\r
+\r
+       START_LUMP(SPINDRED_LUMP_START, __SPINDREDSTART)\r
+       SPINDRED1SPR,                // 424\r
+       SPINDRED2SPR,                // 425\r
+       SPINDRED3SPR,                // 426\r
+       SPINDRED4SPR,                // 427\r
+       END_LUMP(SPINDRED_LUMP_END, __SPINDREDEND)\r
+\r
+       START_LUMP(SHELLEY_LUMP_START, __SHELLEYSTART)\r
+       SHELLEYR1SPR,                // 428\r
+       SHELLEYR2SPR,                // 429\r
+       SHELLEYR3SPR,                // 430\r
+       SHELLEYR4SPR,                // 431\r
+       SHELLEYL1SPR,                // 432\r
+       SHELLEYL2SPR,                // 433\r
+       SHELLEYL3SPR,                // 434\r
+       SHELLEYL4SPR,                // 435\r
+       SHELLEYJUMPRSPR,             // 436\r
+       SHELLEYFALLRSPR,             // 437\r
+       SHELLEYJUMPLSPR,             // 438\r
+       SHELLEYFALLLSPR,             // 439\r
+       SHELLEYBOOM1SPR,             // 440\r
+       SHELLEYBOOM2SPR,             // 441\r
+       SHELLEYBOOM3SPR,             // 442\r
+       SHELLEYBOOM4SPR,             // 443\r
+       SHELLEYPIECE1SPR,            // 444\r
+       SHELLEYPIECE2SPR,            // 445\r
+       END_LUMP(SHELLEY_LUMP_END, __SHELLEYEND)\r
+\r
+       START_LUMP(PLATFORM_LUMP_START, __PLATFORMSTART)\r
+       PLATFORMSPR,                 // 446\r
+       END_LUMP(PLATFORM_LUMP_END, __PLATFORMEND)\r
+\r
+       START_LUMP(MINIPLAT_LUMP_START, __MINIPLATSTART)\r
+       MINIPLATSPR,                // 447\r
+       END_LUMP(MINIPLAT_LUMP_END, __MINIPLATEND)\r
+\r
+\r
+       //\r
+       // TILES (these don't need names)\r
+       //\r
+\r
+       LASTTILE=STARTEXTERNS-1,\r
+\r
+       //\r
+       // EXTERNS\r
+       //\r
+\r
+       //texts\r
+       T_HELPART,                   // 4914\r
+       T_CONTRART,                  // 4915\r
+       T_STORYART,                  // 4916\r
+       T_IDART,                     // 4917\r
+       T_ENDART,                    // 4918\r
+       T_ENDART2,                   // 4919\r
+       T_ORDERART,                  // 4920\r
+\r
+       ORDERSCREEN,                 // 4921\r
+       BIGCOMMANDER,                // 4922\r
+       BIGKEEN,                     // 4923\r
+       OUTOFMEM,                    // 4924\r
+       GALAXY,                      // 4925\r
+\r
+       //demos\r
+       DEMO0,                       // 4926\r
+       DEMO1,                       // 4927\r
+       DEMO2,                       // 4928\r
+       DEMO3,                       // 4929\r
+       DEMO4,                       // 4930\r
+\r
+       NUMGRCHUNKS\r
+} graphicnums;\r
+\r
+#undef START_LUMP\r
+#undef END_LUMP\r
+\r
+#endif //__GFX_H__
\ No newline at end of file
diff --git a/16/keen456/KEEN4-6/KEEN5/ID_ASM.EQU b/16/keen456/KEEN4-6/KEEN5/ID_ASM.EQU
new file mode 100755 (executable)
index 0000000..932d6f8
--- /dev/null
@@ -0,0 +1,115 @@
+;\r
+; Equates for all .ASM files\r
+;\r
+\r
+;----------------------------------------------------------------------------\r
+\r
+INCLUDE        "GFXE_CK5.EQU"\r
+\r
+;----------------------------------------------------------------------------\r
+\r
+CGAGR          =       1\r
+EGAGR          =       2\r
+VGAGR          =       3\r
+\r
+GRMODE         =       EGAGR\r
+PROFILE                =       0                       ; 1=keep stats on tile drawing\r
+\r
+SC_INDEX       =       03C4h\r
+SC_RESET       =       0\r
+SC_CLOCK       =       1\r
+SC_MAPMASK     =       2\r
+SC_CHARMAP     =       3\r
+SC_MEMMODE     =       4\r
+\r
+CRTC_INDEX     =       03D4h\r
+CRTC_H_TOTAL   =       0\r
+CRTC_H_DISPEND =       1\r
+CRTC_H_BLANK   =       2\r
+CRTC_H_ENDBLANK        =       3\r
+CRTC_H_RETRACE =       4\r
+CRTC_H_ENDRETRACE =    5\r
+CRTC_V_TOTAL   =       6\r
+CRTC_OVERFLOW  =       7\r
+CRTC_ROWSCAN   =       8\r
+CRTC_MAXSCANLINE =     9\r
+CRTC_CURSORSTART =     10\r
+CRTC_CURSOREND =       11\r
+CRTC_STARTHIGH =       12\r
+CRTC_STARTLOW  =       13\r
+CRTC_CURSORHIGH        =       14\r
+CRTC_CURSORLOW =       15\r
+CRTC_V_RETRACE =       16\r
+CRTC_V_ENDRETRACE =    17\r
+CRTC_V_DISPEND =       18\r
+CRTC_OFFSET    =       19\r
+CRTC_UNDERLINE =       20\r
+CRTC_V_BLANK   =       21\r
+CRTC_V_ENDBLANK        =       22\r
+CRTC_MODE      =       23\r
+CRTC_LINECOMPARE =     24\r
+\r
+\r
+GC_INDEX       =       03CEh\r
+GC_SETRESET    =       0\r
+GC_ENABLESETRESET =    1\r
+GC_COLORCOMPARE        =       2\r
+GC_DATAROTATE  =       3\r
+GC_READMAP     =       4\r
+GC_MODE                =       5\r
+GC_MISCELLANEOUS =     6\r
+GC_COLORDONTCARE =     7\r
+GC_BITMASK     =       8\r
+\r
+ATR_INDEX      =       03c0h\r
+ATR_MODE       =       16\r
+ATR_OVERSCAN   =       17\r
+ATR_COLORPLANEENABLE = 18\r
+ATR_PELPAN     =       19\r
+ATR_COLORSELECT        =       20\r
+\r
+STATUS_REGISTER_1     =        03dah\r
+\r
+\r
+MACRO  WORDOUT\r
+       out     dx,ax\r
+ENDM\r
+\r
+if 0\r
+\r
+MACRO  WORDOUT\r
+       out     dx,al\r
+       inc     dx\r
+       xchg    al,ah\r
+       out     dx,al\r
+       dec     dx\r
+       xchg    al,ah\r
+ENDM\r
+\r
+endif\r
+\r
+UPDATEWIDE     =       22\r
+UPDATEHIGH     =       14\r
+\r
+;\r
+; tile info offsets from segment tinf\r
+;\r
+\r
+ANIM           =       402\r
+SPEED          =       (ANIM+NUMTILE16)\r
+\r
+NORTHWALL      =       (SPEED+NUMTILE16)\r
+EASTWALL       =       (NORTHWALL+NUMTILE16M)\r
+SOUTHWALL   =  (EASTWALL+NUMTILE16M)\r
+WESTWALL    =  (SOUTHWALL+NUMTILE16M)\r
+MANIM       =  (WESTWALL+NUMTILE16M)\r
+INTILE      =  (MANIM+NUMTILE16M)\r
+MSPEED      =  (INTILE+NUMTILE16M)\r
+\r
+\r
+IFE GRMODE-EGAGR\r
+SCREENWIDTH    =       64\r
+ENDIF\r
+IFE GRMODE-CGAGR\r
+SCREENWIDTH    =       128\r
+ENDIF\r
diff --git a/16/keen456/KEEN4-6/KEEN5/ID_HEADS.H b/16/keen456/KEEN4-6/KEEN5/ID_HEADS.H
new file mode 100755 (executable)
index 0000000..72b1b72
--- /dev/null
@@ -0,0 +1,109 @@
+/* Reconstructed Commander Keen 4-6 Source Code\r
+ * Copyright (C) 2021 K1n9_Duk3\r
+ *\r
+ * This file is primarily based on:\r
+ * Catacomb 3-D Source Code\r
+ * Copyright (C) 1993-2014 Flat Rock Software\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License along\r
+ * with this program; if not, write to the Free Software Foundation, Inc.,\r
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+ */\r
+\r
+// ID_GLOB.H\r
+\r
+\r
+#include <ALLOC.H>\r
+#include <CTYPE.H>\r
+#include <DOS.H>\r
+#include <ERRNO.H>\r
+#include <FCNTL.H>\r
+#include <IO.H>\r
+#include <MEM.H>\r
+#include <PROCESS.H>\r
+#include <STDIO.H>\r
+#include <STDLIB.H>\r
+#include <STRING.H>\r
+#include <SYS\STAT.H>\r
+\r
+#define __ID_GLOB__\r
+\r
+//--------------------------------------------------------------------------\r
+\r
+#define KEEN\r
+#define KEEN5\r
+\r
+#define        EXTENSION       "CK5"\r
+\r
+extern char far introscn;\r
+\r
+#include "GFXE_CK5.H"\r
+#include "AUDIOCK5.H"\r
+\r
+//--------------------------------------------------------------------------\r
+\r
+#define        TEXTGR  0\r
+#define        CGAGR   1\r
+#define        EGAGR   2\r
+#define        VGAGR   3\r
+\r
+#define GRMODE EGAGR\r
+\r
+#if GRMODE == EGAGR\r
+#define GREXT  "EGA"\r
+#endif\r
+#if GRMODE == CGAGR\r
+#define GREXT  "CGA"\r
+#endif\r
+\r
+//#define PROFILE\r
+\r
+//\r
+//     ID Engine\r
+//     Types.h - Generic types, #defines, etc.\r
+//     v1.0d1\r
+//\r
+\r
+#ifndef        __TYPES__\r
+#define        __TYPES__\r
+\r
+typedef        enum    {false,true}    boolean;\r
+typedef        unsigned        char            byte;\r
+typedef        unsigned        int                     word;\r
+typedef        unsigned        long            longword;\r
+typedef        byte *                                  Ptr;\r
+\r
+typedef        struct\r
+               {\r
+                       int     x,y;\r
+               } Point;\r
+typedef        struct\r
+               {\r
+                       Point   ul,lr;\r
+               } Rect;\r
+\r
+#define        nil     ((void *)0)\r
+\r
+#endif\r
+\r
+#include "ID_MM.H"\r
+#include "ID_CA.H"\r
+#include "ID_VW.H"\r
+#include "ID_RF.H"\r
+#include "ID_IN.H"\r
+#include "ID_SD.H"\r
+#include "ID_US.H"\r
+\r
+\r
+void   Quit (char *error);             // defined in user program\r
+\r
diff --git a/16/keen456/KEEN4-6/KEEN5/K5_ACT1.C b/16/keen456/KEEN4-6/KEEN5/K5_ACT1.C
new file mode 100755 (executable)
index 0000000..9157bac
--- /dev/null
@@ -0,0 +1,1524 @@
+/* Reconstructed Commander Keen 4-6 Source Code\r
+ * Copyright (C) 2021 K1n9_Duk3\r
+ *\r
+ * This file is loosely based on:\r
+ * Keen Dreams Source Code\r
+ * Copyright (C) 2014 Javier M. Chavez\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License along\r
+ * with this program; if not, write to the Free Software Foundation, Inc.,\r
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+ */\r
+\r
+/*\r
+K5_ACT1.C\r
+=========\r
+\r
+Contains the following actor types (in this order):\r
+\r
+- some shared routines\r
+- Bonus Items\r
+- Teleport and Fuse effects\r
+- Platforms\r
+- falling platforms\r
+- static platforms\r
+- Goplat platforms\r
+- Volte Face\r
+- sneaky platforms\r
+- Turrets\r
+\r
+*/\r
+\r
+#include "CK_DEF.H"\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                 SHARED STUFF\r
+\r
+=============================================================================\r
+*/\r
+\r
+Sint16 pdirx[] = {0, 1, 0, -1, 1, 1, -1, -1};\r
+Sint16 pdiry[] = {-1, 0, 1, 0, -1, 1, 1, -1};\r
+\r
+/*\r
+===========================\r
+=\r
+= CheckSpawnShot\r
+=\r
+===========================\r
+*/\r
+\r
+Sint16 CheckSpawnShot(Uint16 x, Uint16 y, statetype *state)\r
+{\r
+       if (GetNewObj(true) == -1)\r
+               return -1;\r
+       new->x = x;\r
+       new->y = y;\r
+       new->obclass = mshotobj;\r
+       new->active = ac_removable;\r
+       NewState(new, state);\r
+       if (!CheckPosition(new))\r
+       {\r
+               RemoveObj(new);\r
+               return -1;\r
+       }\r
+       return 0;\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= C_ClipSide\r
+=\r
+===========================\r
+*/\r
+\r
+void C_ClipSide(objtype *ob, objtype *hit)\r
+{\r
+       if (hit->obclass == keenobj)\r
+       {\r
+               playerkludgeclipcancel = true;\r
+               ClipToSpriteSide(hit, ob);\r
+               playerkludgeclipcancel = false;\r
+       }\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= C_ClipTop\r
+=\r
+===========================\r
+*/\r
+\r
+void C_ClipTop(objtype *ob, objtype *hit)\r
+{\r
+       if (hit->obclass == keenobj)\r
+               ClipToSpriteTop(hit, ob);\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= R_Land\r
+=\r
+===========================\r
+*/\r
+\r
+void R_Land(objtype *ob)\r
+{\r
+       if (ob->hiteast || ob->hitwest)\r
+               ob->xspeed = 0;\r
+\r
+       if (ob->hitsouth)\r
+               ob->yspeed = 0;\r
+\r
+       if (ob->hitnorth)\r
+       {\r
+               ob->yspeed = 0;\r
+               if (ob->state->nextstate)\r
+               {\r
+                       ChangeState(ob, ob->state->nextstate);\r
+               }\r
+               else\r
+               {\r
+                       RemoveObj(ob);\r
+                       return;\r
+               }\r
+       }\r
+\r
+       RF_PlaceSprite(&ob->sprite, ob->x, ob->y, ob->shapenum, spritedraw, ob->priority);\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= R_Bounce\r
+=\r
+===========================\r
+*/\r
+\r
+void R_Bounce(objtype *ob)\r
+{\r
+       Uint16 wall,absx,absy,angle,newangle;\r
+       Uint32 speed;\r
+\r
+\r
+       RF_PlaceSprite(&ob->sprite, ob->x, ob->y, ob->shapenum, spritedraw, ob->priority);\r
+\r
+       if (ob->hiteast || ob->hitwest)\r
+               ob->xspeed = -ob->xspeed/2;\r
+\r
+       if (ob->hitsouth)\r
+       {\r
+               ob->yspeed = -ob->yspeed/2;\r
+               return;\r
+       }\r
+\r
+       wall = ob->hitnorth;\r
+       if (wall)\r
+       {\r
+               if (ob->yspeed < 0)\r
+                       ob->yspeed = 0;\r
+\r
+               absx = abs(ob->xspeed);\r
+               absy = ob->yspeed;\r
+               if (absx>absy)\r
+               {\r
+                       if (absx>absy*2)        // 22 degrees\r
+                       {\r
+                               angle = 0;\r
+                               speed = absx*286;       // x*sqrt(5)/2\r
+                       }\r
+                       else                            // 45 degrees\r
+                       {\r
+                               angle = 1;\r
+                               speed = absx*362;       // x*sqrt(2)\r
+                       }\r
+               }\r
+               else\r
+               {\r
+                       if (absy>absx*2)        // 90 degrees\r
+                       {\r
+                               angle = 3;\r
+                               speed = absy*256;\r
+                       }\r
+                       else\r
+                       {\r
+                               angle = 2;              // 67 degrees\r
+                               speed = absy*286;       // y*sqrt(5)/2\r
+                       }\r
+               }\r
+               if (ob->xspeed > 0)\r
+                       angle = 7-angle;\r
+\r
+               speed >>= 1;\r
+               newangle = bounceangle[ob->hitnorth][angle];\r
+               switch (newangle)\r
+               {\r
+               case 0:\r
+                       ob->xspeed = speed / 286;\r
+                       ob->yspeed = -ob->xspeed / 2;\r
+                       break;\r
+               case 1:\r
+                       ob->xspeed = speed / 362;\r
+                       ob->yspeed = -ob->xspeed;\r
+                       break;\r
+               case 2:\r
+                       ob->yspeed = -(speed / 286);\r
+                       ob->xspeed = -ob->yspeed / 2;\r
+                       break;\r
+               case 3:\r
+\r
+               case 4:\r
+                       ob->xspeed = 0;\r
+                       ob->yspeed = -(speed / 256);\r
+                       break;\r
+               case 5:\r
+                       ob->yspeed = -(speed / 286);\r
+                       ob->xspeed = ob->yspeed / 2;\r
+                       break;\r
+               case 6:\r
+                       ob->xspeed = ob->yspeed = -(speed / 362);\r
+                       break;\r
+               case 7:\r
+                       ob->xspeed = -(speed / 286);\r
+                       ob->yspeed = ob->xspeed / 2;\r
+                       break;\r
+\r
+               case 8:\r
+                       ob->xspeed = -(speed / 286);\r
+                       ob->yspeed = -ob->xspeed / 2;\r
+                       break;\r
+               case 9:\r
+                       ob->xspeed = -(speed / 362);\r
+                       ob->yspeed = -ob->xspeed;\r
+                       break;\r
+               case 10:\r
+                       ob->yspeed = speed / 286;\r
+                       ob->xspeed = -ob->yspeed / 2;\r
+                       break;\r
+               case 11:\r
+\r
+               case 12:\r
+                       ob->xspeed = 0;\r
+                       ob->yspeed = -(speed / 256);\r
+                       break;\r
+               case 13:\r
+                       ob->yspeed = speed / 286;\r
+                       ob->xspeed = ob->yspeed / 2;\r
+                       break;\r
+               case 14:\r
+                       ob->xspeed = speed / 362;\r
+                       ob->yspeed = speed / 362;\r
+                       break;\r
+               case 15:\r
+                       ob->xspeed = speed / 286;\r
+                       ob->yspeed = ob->xspeed / 2;\r
+                       break;\r
+               }\r
+\r
+               if (speed < 256*16)\r
+               {\r
+                       ChangeState(ob, ob->state->nextstate);\r
+               }\r
+       }\r
+}\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                 BONUS ITEMS\r
+temp1 = bonus type\r
+temp2 = base shape number\r
+temp3 = last animated shape number +1\r
+\r
+=============================================================================\r
+*/\r
+\r
+statetype s_bonus1    = {0,            0,            step,      false, false, 20, 0, 0, T_Bonus, NULL, R_Draw, &s_bonus2};\r
+statetype s_bonus2    = {0,            0,            step,      false, false, 20, 0, 0, T_Bonus, NULL, R_Draw, &s_bonus1};\r
+statetype s_bonusfly1 = {0,            0,            stepthink, false, false, 20, 0, 0, T_FlyBonus, NULL, R_Draw, &s_bonusfly2};\r
+statetype s_bonusfly2 = {0,            0,            stepthink, false, false, 20, 0, 0, T_FlyBonus, NULL, R_Draw, &s_bonusfly1};\r
+statetype s_bonusrise = {0,            0,            slide,     false, false, 40, 0, 8, NULL, NULL, R_Draw, NULL};\r
+statetype s_splash1   = {VIVAPOOF1SPR, VIVAPOOF1SPR, step,      false, false,  8, 0, 0, NULL, NULL, R_Draw, &s_splash2};\r
+statetype s_splash2   = {VIVAPOOF2SPR, VIVAPOOF2SPR, step,      false, false,  8, 0, 0, NULL, NULL, R_Draw, &s_splash3};\r
+statetype s_splash3   = {VIVAPOOF3SPR, VIVAPOOF3SPR, step,      false, false,  8, 0, 0, NULL, NULL, R_Draw, &s_splash4};\r
+statetype s_splash4   = {VIVAPOOF4SPR, VIVAPOOF4SPR, step,      false, false,  8, 0, 0, NULL, NULL, R_Draw, NULL};\r
+\r
+Uint16 bonusshape[] = {\r
+       REDGEM1SPR, YELLOWGEM1SPR, BLUEGEM1SPR, GREENGEM1SPR,\r
+       SUGAR1ASPR, SUGAR2ASPR, SUGAR3ASPR,\r
+       SUGAR4ASPR, SUGAR5ASPR, SUGAR6ASPR,\r
+       ONEUPASPR, STUNCLIP1SPR, DOORCARD1SPR\r
+};\r
+\r
+/*\r
+===========================\r
+=\r
+= SpawnBonus\r
+=\r
+===========================\r
+*/\r
+\r
+void SpawnBonus(Uint16 tileX, Uint16 tileY, Uint16 type)\r
+{\r
+       GetNewObj(false);\r
+       new->needtoclip = cl_noclip;\r
+       new->priority = 2;\r
+       new->obclass = bonusobj;\r
+       new->x = CONVERT_TILE_TO_GLOBAL(tileX);\r
+       new->y = CONVERT_TILE_TO_GLOBAL(tileY);\r
+       new->ydir = -1;\r
+       new->temp1 = type;\r
+       new->temp2=new->shapenum = bonusshape[type];\r
+       new->temp3 = new->temp2+2;\r
+       NewState(new, &s_bonus1);\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= SpawnSplash\r
+=\r
+===========================\r
+*/\r
+\r
+void SpawnSplash(Uint16 tileX, Uint16 tileY)\r
+{\r
+       GetNewObj(true);\r
+       new->needtoclip = cl_noclip;\r
+       new->priority = 3;\r
+       new->obclass = inertobj;\r
+       new->x = CONVERT_TILE_TO_GLOBAL(tileX);\r
+       new->y = CONVERT_TILE_TO_GLOBAL(tileY);\r
+       NewState(new, &s_splash1);\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= T_Bonus\r
+=\r
+===========================\r
+*/\r
+\r
+void T_Bonus(objtype *ob)\r
+{\r
+       if (++ob->shapenum == ob->temp3)\r
+               ob->shapenum = ob->temp2;\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= T_FlyBonus\r
+=\r
+===========================\r
+*/\r
+\r
+void T_FlyBonus(objtype *ob)\r
+{\r
+       if (ob->hitnorth)\r
+               ob->state = &s_bonus1;\r
+\r
+       if (++ob->shapenum == ob->temp3)\r
+               ob->shapenum = ob->temp2;\r
+\r
+       DoGravity(ob);\r
+}\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                 TELEPORT EFFECTS\r
+\r
+=============================================================================\r
+*/\r
+\r
+statetype s_teleport1     = {TELEPORTSPARK1SPR, TELEPORTSPARK1SPR, step, false, false, 6, 0, 0, NULL, NULL, R_Draw, &s_teleport2};\r
+statetype s_teleport2     = {TELEPORTSPARK2SPR, TELEPORTSPARK2SPR, step, false, false, 6, 0, 0, NULL, NULL, R_Draw, &s_teleport1};\r
+statetype s_teleportzap1  = {TELEPORTZAP1SPR,   TELEPORTZAP1SPR,   step, false, false, 6, 0, 0, NULL, NULL, R_Draw, &s_teleportzap2};\r
+statetype s_teleportzap2  = {TELEPORTZAP2SPR,   TELEPORTZAP2SPR,   step, false, false, 6, 0, 0, NULL, NULL, R_Draw, &s_teleportzap1};\r
+\r
+/*\r
+===========================\r
+=\r
+= SpawnTeleport\r
+=\r
+===========================\r
+*/\r
+\r
+void SpawnTeleport(void)\r
+{\r
+       GetNewObj(true);\r
+       new->priority = 3;\r
+       new->needtoclip = cl_noclip;\r
+       new->obclass = teleporterobj;\r
+       new->x = CONVERT_TILE_TO_GLOBAL(player->tileleft) - 8*PIXGLOBAL;\r
+       new->y = CONVERT_TILE_TO_GLOBAL(player->tilebottom) - 5*TILEGLOBAL;\r
+       NewState(new, &s_teleport1);\r
+\r
+       GetNewObj(true);\r
+       new->priority = 3;\r
+       new->needtoclip = cl_noclip;\r
+       new->obclass = teleporterobj;\r
+       new->x = CONVERT_TILE_TO_GLOBAL(player->tileleft);\r
+       new->y = CONVERT_TILE_TO_GLOBAL(player->tiletop) - 8*PIXGLOBAL;\r
+       NewState(new, &s_teleportzap1);\r
+\r
+       SD_PlaySound(SND_TELEPORT);\r
+}\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                 FUSE FLASH\r
+\r
+=============================================================================\r
+*/\r
+\r
+statetype s_fuseflash1    = {FUSEFLASH1SPR, FUSEFLASH1SPR, step, false, false, 10, 0, 0, NULL, NULL, R_Draw, &s_fuseflash2};\r
+statetype s_fuseflash2    = {FUSEFLASH2SPR, FUSEFLASH2SPR, step, false, false, 20, 0, 0, NULL, NULL, R_Draw, &s_fuseflash3};\r
+statetype s_fuseflash3    = {FUSEFLASH3SPR, FUSEFLASH3SPR, step, false, false, 10, 0, 0, NULL, NULL, R_Draw, NULL};\r
+\r
+/*\r
+===========================\r
+=\r
+= SpawnFuseFlash\r
+=\r
+===========================\r
+*/\r
+\r
+void SpawnFuseFlash(Uint16 tileX, Uint16 tileY)\r
+{\r
+       GetNewObj(true);\r
+       new->priority = 3;\r
+       new->needtoclip = cl_noclip;\r
+       new->obclass = teleporterobj;\r
+       new->x = CONVERT_TILE_TO_GLOBAL(tileX-1);\r
+       new->y = CONVERT_TILE_TO_GLOBAL(tileY);\r
+       NewState(new, &s_fuseflash1);\r
+       SD_PlaySound(SND_BIGSPARK);\r
+}\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                 DEAD MACHINE\r
+\r
+=============================================================================\r
+*/\r
+\r
+statetype s_deadmachine   = {-1, -1, step, false, false, 60, 0, 0, T_DeadMachine, NULL, R_Draw, NULL};\r
+\r
+/*\r
+===========================\r
+=\r
+= SpawnDeadMachine\r
+=\r
+===========================\r
+*/\r
+\r
+void SpawnDeadMachine(void)\r
+{\r
+       GetNewObj(false);\r
+       new->active = ac_allways;\r
+       new->needtoclip = cl_noclip;\r
+       NewState(new, &s_deadmachine);\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= T_DeadMachine\r
+=\r
+===========================\r
+*/\r
+\r
+#pragma argsused\r
+void T_DeadMachine(objtype *ob)\r
+{\r
+       if (mapon == 12)\r
+       {\r
+               playstate = ex_qedbroke;\r
+       }\r
+       else\r
+       {\r
+               playstate = ex_fusebroke;\r
+       }\r
+}\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                 PLATFORMS\r
+\r
+=============================================================================\r
+*/\r
+\r
+statetype s_platform      = {PLATFORMSPR,  PLATFORMSPR,  think,     false, false, 0, 0, 0, T_Platform, NULL, R_Draw, NULL};\r
+statetype s_slotplat1     = {SLOTPLAT1SPR, SLOTPLAT1SPR, stepthink, false, false, 0, 0, 0, T_Slotplat, NULL, R_Draw, &s_slotplat2};\r
+statetype s_slotplat2     = {SLOTPLAT2SPR, SLOTPLAT2SPR, stepthink, false, false, 0, 0, 0, T_Slotplat, NULL, R_Draw, &s_slotplat1};\r
+// BUG? the slotplat states have a tictime of 0, so they never transition to the next state\r
+\r
+/*\r
+===========================\r
+=\r
+= SpawnPlatform\r
+=\r
+===========================\r
+*/\r
+\r
+void SpawnPlatform(Uint16 tileX, Uint16 tileY, Sint16 dir, Sint16 type)\r
+{\r
+       GetNewObj(false);\r
+       new->obclass = platformobj;\r
+       new->active = ac_allways;\r
+       new->priority = 0;\r
+       new->x = CONVERT_TILE_TO_GLOBAL(tileX);\r
+       new->y = CONVERT_TILE_TO_GLOBAL(tileY);\r
+       switch (dir)\r
+       {\r
+       case 0:\r
+               new->xdir = 0;\r
+               new->ydir = -1;\r
+               break;\r
+       case 1:\r
+               new->xdir = 1;\r
+               new->ydir = 0;\r
+               break;\r
+       case 2:\r
+               new->xdir = 0;\r
+               new->ydir = 1;\r
+               break;\r
+       case 3:\r
+               new->xdir = -1;\r
+               new->ydir = 0;\r
+       }\r
+       if (type)\r
+       {\r
+               new->x += 4*PIXGLOBAL;\r
+               new->y += 4*PIXGLOBAL;\r
+               NewState(new, &s_slotplat1);\r
+       }\r
+       else\r
+       {\r
+               NewState(new, &s_platform);\r
+       }\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= T_Platform\r
+=\r
+===========================\r
+*/\r
+\r
+void T_Platform(objtype *ob)\r
+{\r
+       Uint16 newpos, newtile;\r
+\r
+       //\r
+       // this code could be executed twice during the same frame because the\r
+       // object's animation/state changed during that frame, so don't update\r
+       // anything if we already have movement for the current frame i.e. the\r
+       // update code has already been executed this frame\r
+       //\r
+       if (!xtry && !ytry)\r
+       {\r
+               xtry = ob->xdir * 12 * tics;\r
+               ytry = ob->ydir * 12 * tics;\r
+\r
+               if (ob->xdir == 1)\r
+               {\r
+                       newpos = ob->right + xtry;\r
+                       newtile = CONVERT_GLOBAL_TO_TILE(newpos);\r
+                       if (ob->tileright != newtile)\r
+                       {\r
+                               if (*(mapsegs[2]+mapbwidthtable[ob->tiletop]/2 + newtile) == PLATFORMBLOCK)\r
+                               {\r
+                                       ob->xdir = -1;\r
+                                       xtry = xtry - (newpos & 0xFF);\r
+                               }\r
+                       }\r
+               }\r
+               else if (ob->xdir == -1)\r
+               {\r
+                       newpos = ob->left + xtry;\r
+                       newtile = CONVERT_GLOBAL_TO_TILE(newpos);\r
+                       if (ob->tileleft != newtile)\r
+                       {\r
+                               if (*(mapsegs[2]+mapbwidthtable[ob->tiletop]/2 + newtile) == PLATFORMBLOCK)\r
+                               {\r
+                                       ob->xdir = 1;\r
+                                       xtry = xtry + (TILEGLOBAL - (newpos & 0xFF));\r
+                               }\r
+                       }\r
+               }\r
+               else if (ob->ydir == 1)\r
+               {\r
+                       newpos = ob->bottom + ytry;\r
+                       newtile = CONVERT_GLOBAL_TO_TILE(newpos);\r
+                       if (ob->tilebottom != newtile)\r
+                       {\r
+                               if (*(mapsegs[2]+mapbwidthtable[newtile]/2 + ob->tileleft) == PLATFORMBLOCK)\r
+                               {\r
+                                       if (*(mapsegs[2]+mapbwidthtable[newtile-2]/2 + ob->tileleft) == PLATFORMBLOCK)\r
+                                       {\r
+                                               ytry = 0;\r
+                                               ob->needtoreact = true;\r
+                                       }\r
+                                       else\r
+                                       {\r
+                                               ob->ydir = -1;\r
+                                               ytry = ytry - (newpos & 0xFF);\r
+                                       }\r
+                               }\r
+                       }\r
+               }\r
+               else if (ob->ydir == -1)\r
+               {\r
+                       newpos = ob->top + ytry;\r
+                       newtile = CONVERT_GLOBAL_TO_TILE(newpos);\r
+                       if (ob->tiletop != newtile)\r
+                       {\r
+                               if (*(mapsegs[2]+mapbwidthtable[newtile]/2 + ob->tileleft) == PLATFORMBLOCK)\r
+                               {\r
+                                       if (*(mapsegs[2]+mapbwidthtable[newtile+2]/2 + ob->tileleft) == PLATFORMBLOCK)\r
+                                       {\r
+                                               ytry = 0;\r
+                                               ob->needtoreact = true;\r
+                                       }\r
+                                       else\r
+                                       {\r
+                                               ob->ydir = 1;\r
+                                               ytry = ytry + (TILEGLOBAL - (newpos & 0xFF));\r
+                                       }\r
+                               }\r
+                       }\r
+               }\r
+       }\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= T_Slotplat\r
+=\r
+===========================\r
+*/\r
+\r
+void T_Slotplat(objtype *ob)\r
+{\r
+       Uint16 newpos, newtile;\r
+\r
+       //\r
+       // this code could be executed twice during the same frame because the\r
+       // object's animation/state changed during that frame, so don't update\r
+       // anything if we already have movement for the current frame i.e. the\r
+       // update code has already been executed this frame\r
+       //\r
+       if (!xtry && !ytry)\r
+       {\r
+               xtry = ob->xdir * 12 * tics;\r
+               ytry = ob->ydir * 12 * tics;\r
+\r
+               if (ob->xdir == 1)\r
+               {\r
+                       newpos = ob->right + xtry;\r
+                       newtile = CONVERT_GLOBAL_TO_TILE(newpos);\r
+                       if (ob->tileright != newtile)\r
+                       {\r
+                               if (*(mapsegs[2]+mapbwidthtable[ob->tiletop]/2 + newtile) == PLATFORMBLOCK)\r
+                               {\r
+                                       ob->xdir = -1;\r
+                                       xtry = xtry - (newpos & 0xFF);\r
+                               }\r
+                       }\r
+               }\r
+               else if (ob->xdir == -1)\r
+               {\r
+                       newpos = ob->left + xtry;\r
+                       newtile = CONVERT_GLOBAL_TO_TILE(newpos);\r
+                       if (ob->tileleft != newtile)\r
+                       {\r
+                               if (*(mapsegs[2]+mapbwidthtable[ob->tiletop]/2 + newtile) == PLATFORMBLOCK)\r
+                               {\r
+                                       ob->xdir = 1;\r
+                                       xtry = xtry + (TILEGLOBAL - (newpos & 0xFF));\r
+                               }\r
+                       }\r
+               }\r
+               else if (ob->ydir == 1)\r
+               {\r
+                       newpos = ob->bottom + ytry;\r
+                       newtile = CONVERT_GLOBAL_TO_TILE(newpos);\r
+                       if (ob->tilebottom != newtile)\r
+                       {\r
+                               if (*(mapsegs[2]+mapbwidthtable[newtile]/2 + ob->tileleft + 1) == PLATFORMBLOCK)\r
+                               {\r
+                                       if (*(mapsegs[2]+mapbwidthtable[newtile-2]/2 + ob->tileleft) == PLATFORMBLOCK)  // BUG? '+ 1' is missing after 'tileleft'\r
+                                       {\r
+                                               ytry = 0;\r
+                                               ob->needtoreact = true;\r
+                                       }\r
+                                       else\r
+                                       {\r
+                                               ob->ydir = -1;\r
+                                               ytry = ytry - (newpos & 0xFF);\r
+                                       }\r
+                               }\r
+                       }\r
+               }\r
+               else if (ob->ydir == -1)\r
+               {\r
+                       newpos = ob->top + ytry;\r
+                       newtile = CONVERT_GLOBAL_TO_TILE(newpos);\r
+                       if (ob->tiletop != newtile)\r
+                       {\r
+                               if (*(mapsegs[2]+mapbwidthtable[newtile]/2 + ob->tileleft + 1) == PLATFORMBLOCK)\r
+                               {\r
+                                       if (*(mapsegs[2]+mapbwidthtable[newtile+2]/2 + ob->tileleft + 1) == PLATFORMBLOCK)\r
+                                       {\r
+                                               ytry = 0;\r
+                                               ob->needtoreact = true;\r
+                                       }\r
+                                       else\r
+                                       {\r
+                                               ob->ydir = 1;\r
+                                               ytry = ytry + (TILEGLOBAL - (newpos & 0xFF));\r
+                                       }\r
+                               }\r
+                       }\r
+               }\r
+       }\r
+}\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                 DROPPING PLATFORM\r
+\r
+temp1 = initial y position\r
+\r
+=============================================================================\r
+*/\r
+\r
+statetype s_dropplatsit  = {PLATFORMSPR, PLATFORMSPR, think,      false, false, 0, 0,   0, T_DropPlatSit, NULL, R_Draw, NULL};\r
+statetype s_dropplatfall = {PLATFORMSPR, PLATFORMSPR, think,      false, false, 0, 0,   0, T_DropPlatFall, NULL, R_Draw, NULL};\r
+statetype s_dropplatrise = {PLATFORMSPR, PLATFORMSPR, slidethink, false, false, 0, 0, -32, T_DropPlatRise, NULL, R_Draw, NULL};\r
+\r
+/*\r
+===========================\r
+=\r
+= SpawnDropPlat\r
+=\r
+===========================\r
+*/\r
+\r
+void SpawnDropPlat(Uint16 tileX, Uint16 tileY)\r
+{\r
+       GetNewObj(false);\r
+       new->obclass = platformobj;\r
+       new->active = ac_allways;\r
+       new->priority = 0;\r
+       new->x = CONVERT_TILE_TO_GLOBAL(tileX);\r
+       new->y=new->temp1 = CONVERT_TILE_TO_GLOBAL(tileY);\r
+       new->xdir = 0;\r
+       new->ydir = 1;\r
+       new->needtoclip = cl_noclip;\r
+       NewState(new, &s_dropplatsit);\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= T_DropPlatSit\r
+=\r
+===========================\r
+*/\r
+\r
+void T_DropPlatSit(objtype *ob)\r
+{\r
+       if (gamestate.riding == ob)\r
+       {\r
+               ytry = tics << 4;       //tics * 16;\r
+               ob->yspeed = 0;\r
+               if (ob->y + ytry - ob->temp1 >= 8*PIXGLOBAL)\r
+                       ob->state = &s_dropplatfall;\r
+       }\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= T_DropPlatFall\r
+=\r
+===========================\r
+*/\r
+\r
+void T_DropPlatFall(objtype *ob)\r
+{\r
+       Uint16 newpos, newtile;\r
+\r
+       DoGravity(ob);\r
+\r
+#if 0\r
+       // bugfix: don't skip a tile (this is present in Keen 4, but missing in 5 & 6)\r
+       if (ytry >= 15*PIXGLOBAL)\r
+               ytry = 15*PIXGLOBAL;\r
+#endif\r
+\r
+       newpos = ob->bottom + ytry;\r
+       newtile = CONVERT_GLOBAL_TO_TILE(newpos);\r
+       if (ob->tilebottom != newtile)\r
+       {\r
+               if (*(mapsegs[2]+mapbwidthtable[newtile]/2 + ob->tileleft) == PLATFORMBLOCK)\r
+               {\r
+                       ytry = 0xFF - (ob->bottom & 0xFF);\r
+                       if (gamestate.riding != ob)\r
+                               ob->state = &s_dropplatrise;\r
+               }\r
+       }\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= T_DropPlatRise\r
+=\r
+===========================\r
+*/\r
+\r
+void T_DropPlatRise(objtype *ob)\r
+{\r
+       if (gamestate.riding == ob)\r
+       {\r
+               ob->yspeed = 0;\r
+               ob->state = &s_dropplatfall;\r
+       }\r
+       else if (ob->y <= ob->temp1)\r
+       {\r
+               ytry = ob->temp1 - ob->y;\r
+               ob->state = &s_dropplatsit;\r
+       }\r
+}\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                 STATIC PLATFORM\r
+\r
+temp1 = initial y position (is set but never used)\r
+\r
+=============================================================================\r
+*/\r
+\r
+statetype s_statplat    = {PLATFORMSPR, PLATFORMSPR, step, false, false, 32000, 0, 0, NULL, NULL, R_Draw, &s_statplat};\r
+\r
+/*\r
+===========================\r
+=\r
+= SpawnStaticPlat\r
+=\r
+===========================\r
+*/\r
+\r
+void SpawnStaticPlat(Uint16 tileX, Uint16 tileY)\r
+{\r
+       GetNewObj(false);\r
+       new->obclass = platformobj;\r
+       new->active = ac_yes;\r
+       new->priority = 0;\r
+       new->x = CONVERT_TILE_TO_GLOBAL(tileX);\r
+       new->y=new->temp1 = CONVERT_TILE_TO_GLOBAL(tileY);\r
+       new->xdir = 0;\r
+       new->ydir = 1;\r
+       new->needtoclip = cl_noclip;\r
+       NewState(new, &s_statplat);\r
+}\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                 GO PLATFORMS\r
+\r
+temp1 = direction\r
+temp2 = countdown to next dir check\r
+\r
+=============================================================================\r
+*/\r
+\r
+statetype s_goplat        = {PLATFORMSPR,  PLATFORMSPR,  think,     false, false, 0, 0, 0, T_GoPlat, NULL, R_Draw, NULL};\r
+statetype s_slotgoplat1   = {SLOTPLAT1SPR, SLOTPLAT1SPR, stepthink, false, false, 0, 0, 0, T_GoSlotPlat, NULL, R_Draw, &s_slotgoplat2};\r
+statetype s_slotgoplat2   = {SLOTPLAT2SPR, SLOTPLAT2SPR, stepthink, false, false, 0, 0, 0, T_GoSlotPlat, NULL, R_Draw, &s_slotgoplat1};\r
+// BUG? the slotgoplat states have a tictime of 0, so they never transition to the next state\r
+\r
+/*\r
+===========================\r
+=\r
+= SpawnGoPlat\r
+=\r
+===========================\r
+*/\r
+\r
+void SpawnGoPlat(Uint16 tileX, Uint16 tileY, Sint16 dir, Sint16 type)\r
+{\r
+       GetNewObj(false);\r
+       new->obclass = platformobj;\r
+       new->active = ac_allways;\r
+       new->priority = 0;\r
+       new->x = CONVERT_TILE_TO_GLOBAL(tileX);\r
+       new->y = CONVERT_TILE_TO_GLOBAL(tileY);\r
+       new->xdir = 0;\r
+       new->ydir = 1;\r
+       new->needtoclip = cl_noclip;\r
+       if (type)\r
+       {\r
+               new->x += 4*PIXGLOBAL;\r
+               new->y += 4*PIXGLOBAL;\r
+               NewState(new, &s_slotgoplat1);\r
+       }\r
+       else\r
+       {\r
+               NewState(new, &s_goplat);\r
+       }\r
+       *(mapsegs[2]+mapbwidthtable[tileY]/2 + tileX) = DIRARROWSTART + dir;\r
+       new->temp1 = dir;\r
+       new->temp2 = TILEGLOBAL;\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= T_GoPlat\r
+=\r
+===========================\r
+*/\r
+\r
+void T_GoPlat(objtype *ob)\r
+{\r
+       Uint16 move;\r
+       Uint16 tx, ty;\r
+       Sint16 dir;\r
+\r
+       //\r
+       // this code could be executed twice during the same frame because the\r
+       // object's animation/state changed during that frame, so don't update\r
+       // anything if we already have movement for the current frame i.e. the\r
+       // update code has already been executed this frame\r
+       //\r
+       if (!xtry && !ytry)\r
+       {\r
+               move = tics * 12;\r
+               if (ob->temp2 > move)\r
+               {\r
+                       ob->temp2 = ob->temp2 - move;\r
+\r
+                       dir = pdirx[ob->temp1];\r
+                       if (dir == 1)\r
+                       {\r
+                               xtry = xtry + move;\r
+                       }\r
+                       else if (dir == -1)\r
+                       {\r
+                               xtry = xtry + -move;\r
+                       }\r
+\r
+                       dir = pdiry[ob->temp1];\r
+                       if (dir == 1)\r
+                       {\r
+                               ytry = ytry + move;\r
+                       }\r
+                       else if (dir == -1)\r
+                       {\r
+                               ytry = ytry + -move;\r
+                       }\r
+               }\r
+               else\r
+               {\r
+                       dir = pdirx[ob->temp1];\r
+                       if (dir == 1)\r
+                       {\r
+                               xtry += ob->temp2;\r
+                       }\r
+                       else if (dir == -1)\r
+                       {\r
+                               xtry += -ob->temp2;\r
+                       }\r
+\r
+                       dir = pdiry[ob->temp1];\r
+                       if (dir == 1)\r
+                       {\r
+                               ytry += ob->temp2;\r
+                       }\r
+                       else if (dir == -1)\r
+                       {\r
+                               ytry += -ob->temp2;\r
+                       }\r
+\r
+                       tx = CONVERT_GLOBAL_TO_TILE(ob->x + xtry);\r
+                       ty = CONVERT_GLOBAL_TO_TILE(ob->y + ytry);\r
+                       ob->temp1 = *(mapsegs[2]+mapbwidthtable[ty]/2 + tx) - DIRARROWSTART;\r
+                       if (ob->temp1 < arrow_North || ob->temp1 > arrow_None)\r
+                       {\r
+                               char error[60] = "Goplat moved to a bad spot: ";\r
+                               char buf[5] = "";\r
+\r
+                               strcat(error, itoa(ob->x, buf, 16));\r
+                               strcat(error, ",");\r
+                               strcat(error, itoa(ob->y, buf, 16));\r
+                               Quit(error);\r
+                       }\r
+\r
+                       move -= ob->temp2;\r
+                       ob->temp2 = TILEGLOBAL - move;\r
+\r
+                       dir = pdirx[ob->temp1];\r
+                       if (dir == 1)\r
+                       {\r
+                               xtry = xtry + move;\r
+                       }\r
+                       else if (dir == -1)\r
+                       {\r
+                               xtry = xtry - move;\r
+                       }\r
+\r
+                       dir = pdiry[ob->temp1];\r
+                       if (dir == 1)\r
+                       {\r
+                               ytry = ytry + move;\r
+                       }\r
+                       else if (dir == -1)\r
+                       {\r
+                               ytry = ytry - move;\r
+                       }\r
+               }\r
+       }\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= T_GoSlotPlat\r
+=\r
+===========================\r
+*/\r
+\r
+void T_GoSlotPlat(objtype *ob)\r
+{\r
+       Uint16 move;\r
+       Uint16 tx, ty;\r
+       Sint16 dir;\r
+\r
+       //\r
+       // this code could be executed twice during the same frame because the\r
+       // object's animation/state changed during that frame, so don't update\r
+       // anything if we already have movement for the current frame i.e. the\r
+       // update code has already been executed this frame\r
+       //\r
+       if (!xtry && !ytry)\r
+       {\r
+               move = tics * 12;\r
+               if (ob->temp2 > move)\r
+               {\r
+                       ob->temp2 = ob->temp2 - move;\r
+\r
+                       dir = pdirx[ob->temp1];\r
+                       if (dir == 1)\r
+                       {\r
+                               xtry = xtry + move;\r
+                       }\r
+                       else if (dir == -1)\r
+                       {\r
+                               xtry = xtry + -move;\r
+                       }\r
+\r
+                       dir = pdiry[ob->temp1];\r
+                       if (dir == 1)\r
+                       {\r
+                               ytry = ytry + move;\r
+                       }\r
+                       else if (dir == -1)\r
+                       {\r
+                               ytry = ytry + -move;\r
+                       }\r
+               }\r
+               else\r
+               {\r
+                       dir = pdirx[ob->temp1];\r
+                       if (dir == 1)\r
+                       {\r
+                               xtry += ob->temp2;\r
+                       }\r
+                       else if (dir == -1)\r
+                       {\r
+                               xtry += -ob->temp2;\r
+                       }\r
+\r
+                       dir = pdiry[ob->temp1];\r
+                       if (dir == 1)\r
+                       {\r
+                               ytry += ob->temp2;\r
+                       }\r
+                       else if (dir == -1)\r
+                       {\r
+                               ytry += -ob->temp2;\r
+                       }\r
+\r
+                       tx = CONVERT_GLOBAL_TO_TILE(ob->x + xtry + 4*PIXGLOBAL);\r
+                       ty = CONVERT_GLOBAL_TO_TILE(ob->y + ytry + 4*PIXGLOBAL);\r
+                       ob->temp1 = *(mapsegs[2]+mapbwidthtable[ty]/2 + tx) - DIRARROWSTART;\r
+                       if (ob->temp1 < arrow_North || ob->temp1 > arrow_None)\r
+                       {\r
+                               Quit("Goplat moved to a bad spot!");\r
+                       }\r
+\r
+                       move -= ob->temp2;\r
+                       ob->temp2 = TILEGLOBAL - move;\r
+\r
+                       dir = pdirx[ob->temp1];\r
+                       if (dir == 1)\r
+                       {\r
+                               xtry = xtry + move;\r
+                       }\r
+                       else if (dir == -1)\r
+                       {\r
+                               xtry = xtry - move;\r
+                       }\r
+\r
+                       dir = pdiry[ob->temp1];\r
+                       if (dir == 1)\r
+                       {\r
+                               ytry = ytry + move;\r
+                       }\r
+                       else if (dir == -1)\r
+                       {\r
+                               ytry = ytry - move;\r
+                       }\r
+               }\r
+       }\r
+}\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                 VOLTEFACE\r
+\r
+temp1 = direction\r
+temp2 = countdown to next dir check\r
+\r
+=============================================================================\r
+*/\r
+\r
+statetype s_volte1     = {VOLTEFACE1SPR,    VOLTEFACE1SPR,    stepthink, false, false,   6, 0, 0, T_Volte, C_Volte, R_Draw, &s_volte2};\r
+statetype s_volte2     = {VOLTEFACE2SPR,    VOLTEFACE2SPR,    stepthink, false, false,   6, 0, 0, T_Volte, C_Volte, R_Draw, &s_volte3};\r
+statetype s_volte3     = {VOLTEFACE3SPR,    VOLTEFACE3SPR,    stepthink, false, false,   6, 0, 0, T_Volte, C_Volte, R_Draw, &s_volte4};\r
+statetype s_volte4     = {VOLTEFACE4SPR,    VOLTEFACE4SPR,    stepthink, false, false,   6, 0, 0, T_Volte, C_Volte, R_Draw, &s_volte1};\r
+statetype s_voltestun  = {VOLTEFACESTUNSPR, VOLTEFACESTUNSPR, step,      false, false, 300, 0, 0, NULL, NULL, R_Draw, &s_volte1};\r
+\r
+/*\r
+===========================\r
+=\r
+= SpawnVolte\r
+=\r
+===========================\r
+*/\r
+\r
+void SpawnVolte(Uint16 tileX, Uint16 tileY)\r
+{\r
+       Uint16 dir;\r
+       Uint16 far *map;\r
+\r
+       GetNewObj(false);\r
+       new->obclass = volteobj;\r
+       new->active = ac_allways;\r
+       new->priority = 2;\r
+       new->x = CONVERT_TILE_TO_GLOBAL(tileX);\r
+       new->y = CONVERT_TILE_TO_GLOBAL(tileY);\r
+       new->needtoclip = cl_noclip;\r
+       NewState(new, &s_volte1);\r
+       map = mapsegs[2] + mapbwidthtable[tileY]/2 + tileX;\r
+       if (map[-1] == DIRARROWSTART + arrow_East)\r
+       {\r
+               dir = arrow_East;\r
+       }\r
+       else if (map[1] == DIRARROWSTART + arrow_West)\r
+       {\r
+               dir = arrow_West;\r
+       }\r
+       else if (*(map-mapwidth) == DIRARROWSTART + arrow_South)\r
+       {\r
+               dir = arrow_South;\r
+       }\r
+       else if (*(map+mapwidth) == DIRARROWSTART + arrow_North)\r
+       {\r
+               dir = arrow_North;\r
+       }\r
+       map[0] = dir + DIRARROWSTART;\r
+       new->temp1 = dir;\r
+       new->temp2 = TILEGLOBAL;\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= T_Volte\r
+=\r
+===========================\r
+*/\r
+\r
+void T_Volte(objtype *ob)\r
+{\r
+       Uint16 move;\r
+       Uint16 tx, ty;\r
+       Sint16 dir;\r
+\r
+       //\r
+       // this code could be executed twice during the same frame because the\r
+       // object's animation/state changed during that frame, so don't update\r
+       // anything if we already have movement for the current frame i.e. the\r
+       // update code has already been executed this frame\r
+       //\r
+       if (!xtry && !ytry)\r
+       {\r
+               move = tics << 5;\r
+               if (ob->temp2 > move)\r
+               {\r
+                       ob->temp2 = ob->temp2 - move;\r
+\r
+                       dir = pdirx[ob->temp1];\r
+                       if (dir == 1)\r
+                       {\r
+                               xtry = xtry + move;\r
+                       }\r
+                       else if (dir == -1)\r
+                       {\r
+                               xtry = xtry + -move;\r
+                       }\r
+\r
+                       dir = pdiry[ob->temp1];\r
+                       if (dir == 1)\r
+                       {\r
+                               ytry = ytry + move;\r
+                       }\r
+                       else if (dir == -1)\r
+                       {\r
+                               ytry = ytry + -move;\r
+                       }\r
+               }\r
+               else\r
+               {\r
+                       dir = pdirx[ob->temp1];\r
+                       if (dir == 1)\r
+                       {\r
+                               xtry += ob->temp2;\r
+                       }\r
+                       else if (dir == -1)\r
+                       {\r
+                               xtry += -ob->temp2;\r
+                       }\r
+\r
+                       dir = pdiry[ob->temp1];\r
+                       if (dir == 1)\r
+                       {\r
+                               ytry += ob->temp2;\r
+                       }\r
+                       else if (dir == -1)\r
+                       {\r
+                               ytry += -ob->temp2;\r
+                       }\r
+\r
+                       tx = CONVERT_GLOBAL_TO_TILE(ob->x + xtry);\r
+                       ty = CONVERT_GLOBAL_TO_TILE(ob->y + ytry);\r
+                       ob->temp1 = *(mapsegs[2]+mapbwidthtable[ty]/2 + tx) - DIRARROWSTART;\r
+                       if (ob->temp1 < arrow_North || ob->temp1 > arrow_None)\r
+                       {\r
+                               char error[60] = "Volte moved to a bad spot: ";\r
+                               char buf[5] = "";\r
+\r
+                               strcat(error, itoa(ob->x, buf, 16));\r
+                               strcat(error, ",");\r
+                               strcat(error, itoa(ob->y, buf, 16));\r
+                               Quit(error);\r
+                       }\r
+\r
+                       move -= ob->temp2;\r
+                       ob->temp2 = TILEGLOBAL - move;\r
+\r
+                       dir = pdirx[ob->temp1];\r
+                       if (dir == 1)\r
+                       {\r
+                               xtry = xtry + move;\r
+                       }\r
+                       else if (dir == -1)\r
+                       {\r
+                               xtry = xtry - move;\r
+                       }\r
+\r
+                       dir = pdiry[ob->temp1];\r
+                       if (dir == 1)\r
+                       {\r
+                               ytry = ytry + move;\r
+                       }\r
+                       else if (dir == -1)\r
+                       {\r
+                               ytry = ytry - move;\r
+                       }\r
+               }\r
+       }\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= C_Volte\r
+=\r
+===========================\r
+*/\r
+\r
+void C_Volte(objtype *ob, objtype *hit)\r
+{\r
+       if (hit->obclass == keenobj)\r
+       {\r
+               KillKeen();\r
+       }\r
+       else if (hit->obclass == stunshotobj)\r
+       {\r
+               ExplodeShot(hit);\r
+               ChangeState(ob, &s_voltestun);\r
+       }\r
+}\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                 SNEAKY PLATFORM\r
+\r
+temp1 = initial x position (is set but never used)\r
+\r
+=============================================================================\r
+*/\r
+\r
+statetype s_sneakplatsit    = {PLATFORMSPR, PLATFORMSPR, think, false, false,  0,   0, 0, T_SneakPlat, NULL, R_Draw, NULL};\r
+statetype s_sneakplatdodge  = {PLATFORMSPR, PLATFORMSPR, slide, false, false, 48,  32, 0, NULL, NULL, R_Draw, &s_sneakplatreturn};\r
+statetype s_sneakplatreturn = {PLATFORMSPR, PLATFORMSPR, slide, false, false, 96, -16, 0, NULL, NULL, R_Draw, &s_sneakplatsit};\r
+\r
+/*\r
+===========================\r
+=\r
+= SpawnSneakPlat\r
+=\r
+===========================\r
+*/\r
+\r
+void SpawnSneakPlat(Uint16 tileX, Uint16 tileY)\r
+{\r
+       GetNewObj(false);\r
+       new->obclass = platformobj;\r
+       new->active = ac_allways;\r
+       new->priority = 0;\r
+       new->x=new->temp1 = CONVERT_TILE_TO_GLOBAL(tileX);\r
+       new->y = CONVERT_TILE_TO_GLOBAL(tileY);\r
+       new->xdir = 0;\r
+       new->ydir = 1;\r
+       new->needtoclip = cl_noclip;\r
+       NewState(new, &s_sneakplatsit);\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= T_SneakPlat\r
+=\r
+===========================\r
+*/\r
+\r
+void T_SneakPlat(objtype *ob)\r
+{\r
+       Sint16 dist;\r
+\r
+       if (player->state != &s_keenjump1)\r
+               return;\r
+\r
+       if (player->xdir == 1)\r
+       {\r
+               dist = ob->left-player->right;\r
+               if (dist > 4*TILEGLOBAL || dist < 0)\r
+                       return;\r
+       }\r
+       else\r
+       {\r
+               dist = player->left-ob->right;\r
+               if (dist > 4*TILEGLOBAL || dist < 0)\r
+                       return;\r
+       }\r
+       dist = player->y - ob->y;\r
+       if (dist < -6*TILEGLOBAL || dist > 6*TILEGLOBAL)\r
+               return;\r
+\r
+       ob->xdir = player->xdir;\r
+       ob->state = &s_sneakplatdodge;\r
+}\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                 CANNON\r
+\r
+temp1 = direction\r
+\r
+=============================================================================\r
+*/\r
+\r
+statetype s_cannon     = {0,            0,            step,      false, false, 120, 0, 0, NULL, NULL, R_Draw, &s_cannonfire};\r
+statetype s_cannonfire = {0,            0,            step,      true,  false,   1, 0, 0, T_Cannon, NULL, R_Draw, &s_cannon};\r
+statetype s_cshot1     = {LASER1SPR,    LASER1SPR,    stepthink, false, false,   8, 0, 0, T_Velocity, C_CShot, R_CShot, &s_cshot2};\r
+statetype s_cshot2     = {LASER2SPR,    LASER2SPR,    stepthink, false, false,   8, 0, 0, T_Velocity, C_CShot, R_CShot, &s_cshot3};\r
+statetype s_cshot3     = {LASER3SPR,    LASER3SPR,    stepthink, false, false,   8, 0, 0, T_Velocity, C_CShot, R_CShot, &s_cshot4};\r
+statetype s_cshot4     = {LASER4SPR,    LASER4SPR,    stepthink, false, false,   8, 0, 0, T_Velocity, C_CShot, R_CShot, &s_cshot1};\r
+statetype s_cshothit1  = {LASERHIT1SPR, LASERHIT1SPR, step,      false, false,  10, 0, 0, NULL, NULL, R_Draw, &s_cshothit2};\r
+statetype s_cshothit2  = {LASERHIT2SPR, LASERHIT2SPR, step,      false, false,  10, 0, 0, NULL, NULL, R_Draw, NULL};\r
+\r
+/*\r
+===========================\r
+=\r
+= SpawnCannon\r
+=\r
+===========================\r
+*/\r
+\r
+void SpawnCannon(Uint16 tileX, Uint16 tileY, Sint16 dir)\r
+{\r
+       GetNewObj(false);\r
+       new->obclass = cannonobj;\r
+       new->active = ac_yes;\r
+       new->tileright = new->tileleft = tileX;\r
+       new->tiletop = new->tilebottom = tileY;\r
+       new->x = new->left = new->right = CONVERT_TILE_TO_GLOBAL(tileX);\r
+       new->y = new->top = new->bottom = CONVERT_TILE_TO_GLOBAL(tileY);\r
+       new->temp1 = dir;\r
+       NewState(new, &s_cannon);\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= T_Cannon\r
+=\r
+===========================\r
+*/\r
+\r
+void T_Cannon(objtype *ob)\r
+{\r
+       GetNewObj(true);\r
+       new->obclass = mshotobj;\r
+       new->active = ac_removable;\r
+       new->x = ob->x;\r
+       new->y = ob->y;\r
+       switch (ob->temp1)\r
+       {\r
+       case 0:\r
+               new->yspeed = -64;\r
+               break;\r
+       case 1:\r
+               new->xspeed = 64;\r
+               break;\r
+       case 2:\r
+               new->yspeed = 64;\r
+               break;\r
+       case 3:\r
+               new->xspeed = -64;\r
+       }\r
+       NewState(new, &s_cshot1);\r
+       SD_PlaySound(SND_ENEMYSHOT);\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= C_CShot\r
+=\r
+===========================\r
+*/\r
+\r
+void C_CShot(objtype *ob, objtype *hit)\r
+{\r
+       if (hit->obclass == keenobj)\r
+       {\r
+               KillKeen();\r
+               ChangeState(ob, &s_cshothit1);\r
+       }\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= R_CShot\r
+=\r
+===========================\r
+*/\r
+\r
+void R_CShot(objtype *ob)\r
+{\r
+       if (ob->hitnorth || ob->hiteast || ob->hitsouth || ob->hitwest)\r
+       {\r
+               SD_PlaySound(SND_ENEMYSHOTEXPLODE);\r
+               ChangeState(ob, &s_cshothit1);\r
+       }\r
+       RF_PlaceSprite(&ob->sprite, ob->x, ob->y, ob->shapenum, spritedraw, ob->priority);\r
+}
\ No newline at end of file
diff --git a/16/keen456/KEEN4-6/KEEN5/K5_ACT2.C b/16/keen456/KEEN4-6/KEEN5/K5_ACT2.C
new file mode 100755 (executable)
index 0000000..7148d46
--- /dev/null
@@ -0,0 +1,906 @@
+/* Reconstructed Commander Keen 4-6 Source Code\r
+ * Copyright (C) 2021 K1n9_Duk3\r
+ *\r
+ * This file is loosely based on:\r
+ * Keen Dreams Source Code\r
+ * Copyright (C) 2014 Javier M. Chavez\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License along\r
+ * with this program; if not, write to the Free Software Foundation, Inc.,\r
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+ */\r
+\r
+/*\r
+K5_ACT2.C\r
+=========\r
+\r
+Contains the following actor types (in this order):\r
+\r
+- Sparky\r
+- Little Ampton\r
+- Slicestar\r
+- Shelley\r
+\r
+*/\r
+\r
+#include "CK_DEF.H"\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                 SPARKY\r
+\r
+temp1 = charge countdown\r
+\r
+=============================================================================\r
+*/\r
+\r
+statetype s_sparkywalk1   = {SPARKYWALKL1SPR, SPARKYWALKR1SPR, step,  false, true,  8, 128, 0, T_Sparky, C_Sparky, R_Sparky, &s_sparkywalk2};\r
+statetype s_sparkywalk2   = {SPARKYWALKL2SPR, SPARKYWALKR2SPR, step,  false, true,  8, 128, 0, NULL, C_Sparky, R_Sparky, &s_sparkywalk3};\r
+statetype s_sparkywalk3   = {SPARKYWALKL3SPR, SPARKYWALKR3SPR, step,  false, true,  8, 128, 0, NULL, C_Sparky, R_Sparky, &s_sparkywalk4};\r
+statetype s_sparkywalk4   = {SPARKYWALKL4SPR, SPARKYWALKR4SPR, step,  false, true,  8, 128, 0, NULL, C_Sparky, R_Sparky, &s_sparkywalk1};\r
+statetype s_sparkylook1   = {SPARKYTURN1SPR,  SPARKYTURN1SPR,  step,  false, true,  6,   0, 0, NULL, C_Sparky, R_Draw, &s_sparkylook2};\r
+statetype s_sparkylook2   = {SPARKYTURN2SPR,  SPARKYTURN2SPR,  step,  false, true,  6,   0, 0, NULL, C_Sparky, R_Draw, &s_sparkylook3};\r
+statetype s_sparkylook3   = {SPARKYTURN3SPR,  SPARKYTURN3SPR,  step,  false, true,  6,   0, 0, NULL, C_Sparky, R_Draw, &s_sparkylook4};\r
+statetype s_sparkylook4   = {SPARKYWALKR1SPR, SPARKYWALKR1SPR, step,  false, true,  6,   0, 0, T_SparkyLookL, C_Sparky, R_Sparky, &s_sparkylook5};\r
+statetype s_sparkylook5   = {SPARKYTURN3SPR,  SPARKYTURN3SPR,  step,  false, true,  6,   0, 0, NULL, C_Sparky, R_Draw, &s_sparkylook6};\r
+statetype s_sparkylook6   = {SPARKYTURN2SPR,  SPARKYTURN3SPR,  step,  false, true,  6,   0, 0, NULL, C_Sparky, R_Draw, &s_sparkylook7};\r
+statetype s_sparkylook7   = {SPARKYTURN1SPR,  SPARKYTURN3SPR,  step,  false, true,  6,   0, 0, NULL, C_Sparky, R_Draw, &s_sparkylook8};\r
+statetype s_sparkylook8   = {SPARKYWALKL1SPR, SPARKYWALKR1SPR, step,  false, true,  6,   0, 0, T_SparkyLookR, C_Sparky, R_Sparky, &s_sparkywalk2};\r
+statetype s_sparkyspeed1  = {SPARKYWALKL1SPR, SPARKYWALKR1SPR, step,  true,  true,  4,   0, 0, NULL, C_Sparky, R_Sparky, &s_sparkyspeed2};\r
+statetype s_sparkyspeed2  = {SPARKYWALKL2SPR, SPARKYWALKR2SPR, step,  true,  true,  4,   0, 0, NULL, C_Sparky, R_Sparky, &s_sparkyspeed3};\r
+statetype s_sparkyspeed3  = {SPARKYWALKL3SPR, SPARKYWALKR3SPR, step,  true,  true,  4,   0, 0, NULL, C_Sparky, R_Sparky, &s_sparkyspeed4};\r
+statetype s_sparkyspeed4  = {SPARKYWALKL4SPR, SPARKYWALKR4SPR, step,  true,  true,  4,   0, 0, T_ChargeCount, C_Sparky, R_Sparky, &s_sparkyspeed1};\r
+statetype s_sparkycharge1 = {SPARKYWALKL1SPR, SPARKYWALKR1SPR, step,  true,  true,  4, 128, 0, NULL, C_Sparky, R_Sparky, &s_sparkycharge2};\r
+statetype s_sparkycharge2 = {SPARKYWALKL2SPR, SPARKYWALKR2SPR, step,  true,  true,  4, 128, 0, T_RunSnd1, C_Sparky, R_Sparky, &s_sparkycharge3};\r
+statetype s_sparkycharge3 = {SPARKYWALKL3SPR, SPARKYWALKR3SPR, step,  true,  true,  4, 128, 0, NULL, C_Sparky, R_Sparky, &s_sparkycharge4};\r
+statetype s_sparkycharge4 = {SPARKYWALKL4SPR, SPARKYWALKR4SPR, step,  true,  true,  4, 128, 0, T_RunSnd2, C_Sparky, R_Sparky, &s_sparkycharge1};\r
+statetype s_sparkyturn1   = {SPARKYTURN3SPR,  SPARKYTURN1SPR,  step,  false, true,  8,   0, 0, NULL, C_Sparky, R_Draw, &s_sparkyturn2};\r
+statetype s_sparkyturn2   = {SPARKYTURN2SPR,  SPARKYTURN2SPR,  step,  false, true,  8,   0, 0, NULL, C_Sparky, R_Draw, &s_sparkyturn3};\r
+statetype s_sparkyturn3   = {SPARKYTURN1SPR,  SPARKYTURN3SPR,  step,  false, true,  8,   0, 0, NULL, C_Sparky, R_Draw, &s_sparkywalk1};\r
+statetype s_sparkystun    = {SPARKYSTUNSPR,   SPARKYSTUNSPR,   think, false, false, 0,   0, 0, T_Projectile, NULL, R_Stunned, NULL};\r
+\r
+/*\r
+===========================\r
+=\r
+= SpawnSparky\r
+=\r
+===========================\r
+*/\r
+\r
+void SpawnSparky(Uint16 tileX, Uint16 tileY)\r
+{\r
+       GetNewObj(false);\r
+       new->obclass = sparkyobj;\r
+       new->active = ac_yes;\r
+       new->priority = 0;\r
+       new->x = CONVERT_TILE_TO_GLOBAL(tileX);\r
+       new->y = CONVERT_TILE_TO_GLOBAL(tileY) + -1*TILEGLOBAL;\r
+       if (US_RndT() < 0x80)\r
+       {\r
+               new->xdir = 1;\r
+       }\r
+       else\r
+       {\r
+               new->xdir = -1;\r
+       }\r
+       new->ydir = 1;\r
+       NewState(new, &s_sparkywalk1);\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= T_Sparky\r
+=\r
+===========================\r
+*/\r
+\r
+void T_Sparky(objtype *ob)\r
+{\r
+       if (US_RndT() < 0x40)\r
+       {\r
+               ob->state = &s_sparkylook1;\r
+               xtry = 0;\r
+       }\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= T_ChargeCount\r
+=\r
+===========================\r
+*/\r
+\r
+void T_ChargeCount(objtype *ob)\r
+{\r
+       if (--ob->temp1 == 0)\r
+               ob->state = &s_sparkycharge1;\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= T_SparkyLookL\r
+=\r
+===========================\r
+*/\r
+\r
+void T_SparkyLookL(objtype *ob)\r
+{\r
+       Uint16 dist = player->bottom + TILEGLOBAL - ob->bottom;\r
+       if (dist > 2*TILEGLOBAL)\r
+               return;\r
+\r
+       if (player->x < ob->x)\r
+       {       \r
+               ob->xdir = -1;\r
+               SD_PlaySound(SND_SPARKYCHARGE);\r
+               ob->state = &s_sparkyspeed1;\r
+               ob->temp1 = 3;\r
+       }\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= T_SparkyLookR\r
+=\r
+===========================\r
+*/\r
+\r
+void T_SparkyLookR(objtype *ob)\r
+{\r
+       Uint16 dist = player->bottom + TILEGLOBAL - ob->bottom;\r
+       if (dist > 2*TILEGLOBAL)\r
+               return;\r
+\r
+       if (player->x > ob->x)\r
+       {\r
+               ob->xdir = 1;\r
+               SD_PlaySound(SND_SPARKYCHARGE);\r
+               ob->state = &s_sparkyspeed1;\r
+               ob->temp1 = 3;\r
+       }\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= T_RunSnd1\r
+=\r
+===========================\r
+*/\r
+\r
+#pragma argsused\r
+void T_RunSnd1(objtype *ob)\r
+{\r
+       SD_PlaySound(SND_WORLDWALK1);\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= T_RunSnd2\r
+=\r
+===========================\r
+*/\r
+\r
+#pragma argsused\r
+void T_RunSnd2(objtype *ob)\r
+{\r
+       SD_PlaySound(SND_WORLDWALK1);\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= C_Sparky\r
+=\r
+===========================\r
+*/\r
+\r
+void C_Sparky(objtype *ob, objtype *hit)\r
+{\r
+       if (hit->obclass == keenobj)\r
+       {\r
+               KillKeen();\r
+       }\r
+       else if (hit->obclass == stunshotobj)\r
+       {\r
+               StunObj(ob, hit, &s_sparkystun);\r
+       }\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= R_Sparky\r
+=\r
+===========================\r
+*/\r
+\r
+void R_Sparky(objtype *ob)\r
+{\r
+       if (ob->xdir == 1 && ob->hitwest)\r
+       {\r
+               ob->x -= ob->xmove;\r
+               ob->xdir = -1;\r
+               ob->nothink = US_RndT() >> 5;\r
+               ChangeState(ob, &s_sparkyturn1);\r
+       }\r
+       else if (ob->xdir == -1 && ob->hiteast)\r
+       {\r
+               ob->x -= ob->xmove;\r
+               ob->xdir = 1;\r
+               ob->nothink = US_RndT() >> 5;\r
+               ChangeState(ob, &s_sparkyturn1);\r
+       }\r
+       else if (!ob->hitnorth)\r
+       {\r
+               ob->x -= ob->xmove;\r
+               ob->xdir = -ob->xdir;\r
+               ob->nothink = US_RndT() >> 5;\r
+               ChangeState(ob, &s_sparkyturn1);\r
+       }\r
+       RF_PlaceSprite(&ob->sprite, ob->x, ob->y, ob->shapenum, spritedraw, ob->priority);\r
+}\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                 LITTLE AMPTON\r
+\r
+=============================================================================\r
+*/\r
+\r
+statetype s_amptonwalk1    = {AMTONWALKL1SPR, AMPTONWALKR1SPR, step,       false, true,  8, 128,  0, T_Ampton, C_Ampton, R_Ampton, &s_amptonwalk2};\r
+statetype s_amptonwalk2    = {AMTONWALKL2SPR, AMPTONWALKR2SPR, step,       false, true,  8, 128,  0, T_Ampton, C_Ampton, R_Ampton, &s_amptonwalk3};\r
+statetype s_amptonwalk3    = {AMTONWALKL3SPR, AMPTONWALKR3SPR, step,       false, true,  8, 128,  0, T_Ampton, C_Ampton, R_Ampton, &s_amptonwalk4};\r
+statetype s_amptonwalk4    = {AMTONWALKL4SPR, AMPTONWALKR4SPR, step,       false, true,  8, 128,  0, T_Ampton, C_Ampton, R_Ampton, &s_amptonwalk1};\r
+statetype s_amptonturn     = {AMPTONFACESPR,  AMPTONFACESPR,   step,       false, true,  8,   0,  0, NULL, C_Ampton, R_Draw, &s_amptonwalk1};\r
+statetype s_amptongrab1    = {AMPTONGRAB1SPR, AMPTONGRAB1SPR,  step,       false, true,  8,   0,  0, NULL, C_Ampton, R_Draw, &s_amptongrab2};\r
+statetype s_amptongrab2    = {AMPTONGRAB2SPR, AMPTONGRAB2SPR,  step,       false, true,  8,   0,  0, NULL, C_Ampton, R_Draw, &s_amptonclimb};\r
+statetype s_amptonclimb    = {AMPTONGRAB2SPR, AMPTONGRAB2SPR,  slidethink, false, false, 0,   0, 32, T_AmptonClimb, C_Ampton, R_Draw, NULL};\r
+statetype s_amptonrelease1 = {AMPTONGRAB2SPR, AMPTONGRAB2SPR,  step,       false, false, 8,   0,  0, NULL, C_Ampton, R_Draw, &s_amptonrelease2};\r
+statetype s_amptonrelease2 = {AMPTONGRAB1SPR, AMPTONGRAB1SPR,  step,       false, false, 8,   0,  0, T_SetNoThink, C_Ampton, R_Draw, &s_amptonwalk1};\r
+statetype s_amptonfiddle1  = {AMPTONGRAB1SPR, AMPTONGRAB1SPR,  step,       false, true, 12,   0,  0, NULL, C_Ampton, R_Draw, &s_amptonfiddle2};\r
+statetype s_amptonfiddle2  = {AMPTONGRAB2SPR, AMPTONGRAB2SPR,  step,       false, true, 12,   0,  0, NULL, C_Ampton, R_Draw, &s_amptonfiddle3};\r
+statetype s_amptonfiddle3  = {AMPTONGRAB1SPR, AMPTONGRAB1SPR,  step,       false, true, 12,   0,  0, NULL, C_Ampton, R_Draw, &s_amptonfiddle4};\r
+statetype s_amptonfiddle4  = {AMPTONGRAB2SPR, AMPTONGRAB2SPR,  step,       false, true, 12,   0,  0, NULL, C_Ampton, R_Draw, &s_amptonfiddle5};\r
+statetype s_amptonfiddle5  = {AMPTONGRAB1SPR, AMPTONGRAB1SPR,  step,       false, true, 12,   0,  0, T_SetNoThink, C_Ampton, R_Draw, &s_amptonwalk1};\r
+statetype s_amptonstun     = {AMPTONSTUNSPR,  AMPTONSTUNSPR,   think,      false, false, 0,   0,  0, T_Projectile, NULL, R_Stunned, NULL};\r
+\r
+/*\r
+===========================\r
+=\r
+= SpawnAmpton\r
+=\r
+===========================\r
+*/\r
+\r
+void SpawnAmpton(Uint16 tileX, Uint16 tileY)\r
+{\r
+       GetNewObj(false);\r
+       new->obclass = amptonobj;\r
+       new->active = ac_yes;\r
+       new->priority = 0;\r
+       new->x = CONVERT_TILE_TO_GLOBAL(tileX);\r
+       new->y = CONVERT_TILE_TO_GLOBAL(tileY) + -8*PIXGLOBAL;\r
+       if (US_RndT() < 0x80)\r
+       {\r
+               new->xdir = 1;\r
+       }\r
+       else\r
+       {\r
+               new->xdir = -1;\r
+       }\r
+       new->ydir = 1;\r
+       NewState(new, &s_amptonwalk1);\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= T_Ampton\r
+=\r
+===========================\r
+*/\r
+\r
+void T_Ampton(objtype *ob)\r
+{\r
+       Uint16 far *map;\r
+       Uint16 intile, var8;\r
+       boolean poleup, poledown;\r
+\r
+       if (ob->state == &s_amptonwalk1)\r
+       {\r
+               SD_PlaySound(SND_AMPTONWALK1);\r
+       }\r
+       else if (ob->state == &s_amptonwalk3)\r
+       {\r
+               SD_PlaySound(SND_AMPTONWALK2);\r
+       }\r
+       if (ob->x & 0xFF)\r
+       {\r
+               map = mapsegs[1] + mapbwidthtable[ob->tiletop]/2 + ob->tileleft + 1;\r
+               intile = tinf[*map + INTILE] & INTILE_TYPEMASK;\r
+               if (intile == INTILE_AMPTONCOMPUTER)\r
+               {\r
+                       ob->state = &s_amptonfiddle1;\r
+               }\r
+               else if (intile == INTILE_POLE && US_RndT() < 196)\r
+               {\r
+                       if ((tinf[*(map + mapwidth*2) + INTILE] & INTILE_TYPEMASK) == INTILE_POLE)\r
+                       {\r
+                               poledown = true;\r
+                       }\r
+                       else\r
+                       {\r
+                               poledown = false;\r
+                       }\r
+                       if ((tinf[*(map - mapwidth*2) + INTILE] & INTILE_TYPEMASK) == INTILE_POLE)\r
+                       {\r
+                               poleup = true;\r
+                       }\r
+                       else\r
+                       {\r
+                               poleup = false;\r
+                       }\r
+                       if (poleup && poledown)\r
+                       {\r
+                               if (US_RndT() < 0x80)\r
+                                       poleup = false;\r
+                               else\r
+                                       poledown = false;\r
+                       }\r
+\r
+                       if (poleup)\r
+                       {\r
+                               ob->ydir = -1;\r
+                               ob->state = &s_amptongrab1;\r
+                               ob->needtoclip = cl_noclip;\r
+                               ob->nothink = 6;\r
+                               xtry = 0;\r
+                       }\r
+                       else if (poledown)\r
+                       {\r
+                               ob->ydir = 1;\r
+                               ob->state = &s_amptongrab1;\r
+                               ob->needtoclip = cl_noclip;\r
+                               ob->nothink = 6;\r
+                               xtry = 0;\r
+                       }\r
+               }\r
+       }\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= T_AmptonClimb\r
+=\r
+===========================\r
+*/\r
+\r
+void T_AmptonClimb(objtype *ob)\r
+{\r
+       Uint16 newtile;\r
+       Uint16 far *map;\r
+       Uint16 move;\r
+\r
+       newtile = CONVERT_GLOBAL_TO_TILE(ob->bottom + ytry);\r
+       if (ob->tilebottom != newtile)\r
+       {\r
+               if (ob->ydir == -1)\r
+               {\r
+                       map = mapsegs[1] + mapbwidthtable[newtile]/2 + ob->tileleft + 1;\r
+                       if (!tinf[map[0] + NORTHWALL] && tinf[map[mapwidth]+NORTHWALL])\r
+                       {\r
+                               if ((tinf[*(map-4*mapwidth)+INTILE] & INTILE_TYPEMASK) == INTILE_POLE && US_RndT() < 0x80)\r
+                                       return;\r
+\r
+                               move = (ob->bottom & 0xFF) + 1;\r
+                               ob->y -= move;\r
+                               ob->bottom -= move;\r
+                               ob->needtoclip = cl_midclip;\r
+                               ob->state = &s_amptonrelease1;\r
+                               ytry = PIXGLOBAL;\r
+                               ob->ydir = 1;\r
+                               ClipToWalls(ob);\r
+                               ob->nothink = 4;\r
+                               return;\r
+                       }\r
+                       if ((tinf[*(map-mapwidth)+INTILE] & INTILE_TYPEMASK) != INTILE_POLE)\r
+                       {\r
+                               ytry = 0;\r
+                               ob->ydir = 1;\r
+                       }\r
+               }\r
+               else\r
+               {\r
+                       map = mapsegs[1] + mapbwidthtable[newtile]/2 + ob->tileleft + 1;\r
+                       if (tinf[map[0] + NORTHWALL] && !tinf[*(map-mapwidth)+NORTHWALL])\r
+                       {\r
+                               if ((tinf[map[2*mapwidth] + INTILE] & INTILE_TYPEMASK) == INTILE_POLE && US_RndT() < 0x80)\r
+                                       return;\r
+\r
+                               move = 0xFF - (ob->bottom & 0xFF);\r
+                               ob->y += move;\r
+                               ob->bottom += move;\r
+                               ob->needtoclip = cl_midclip;\r
+                               ob->state = &s_amptonrelease1;\r
+                               ytry = PIXGLOBAL;\r
+                               ClipToWalls(ob);\r
+                               ob->nothink = 4;\r
+                               return;\r
+                       }\r
+                       if ((tinf[map[0] + INTILE] & INTILE_TYPEMASK) != INTILE_POLE)\r
+                       {\r
+                               ytry = 0;\r
+                               ob->ydir = -1;\r
+                       }\r
+               }\r
+       }\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= T_SetNoThink\r
+=\r
+===========================\r
+*/\r
+\r
+void T_SetNoThink(objtype *ob)\r
+{\r
+       ob->nothink = 4;\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= C_Ampton\r
+=\r
+===========================\r
+*/\r
+\r
+void C_Ampton(objtype *ob, objtype *hit)\r
+{\r
+       if (hit->obclass == keenobj)\r
+       {\r
+               if (ob->state == &s_amptonclimb)\r
+               {\r
+                       KillKeen();\r
+               }\r
+               else\r
+               {\r
+                       ClipToSpriteSide(hit, ob);\r
+               }\r
+       }\r
+       else if (hit->obclass == stunshotobj)\r
+       {\r
+               ob->needtoclip = cl_midclip;\r
+               ob->ydir = 1;\r
+               ob->yspeed = 0;\r
+               SD_PlaySound(SND_AMPTONDIE);\r
+               StunObj(ob, hit, &s_amptonstun);\r
+       }\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= R_Ampton\r
+=\r
+===========================\r
+*/\r
+\r
+void R_Ampton(objtype *ob)\r
+{\r
+       if (ob->xdir == 1 && ob->hitwest)\r
+       {\r
+               ob->x -= ob->xmove;\r
+               ob->xdir = -1;\r
+               ChangeState(ob, &s_amptonturn);\r
+       }\r
+       else if (ob->xdir == -1 && ob->hiteast)\r
+       {\r
+               ob->x -= ob->xmove;\r
+               ob->xdir = 1;\r
+               ChangeState(ob, &s_amptonturn);\r
+       }\r
+       else if (!ob->hitnorth)\r
+       {\r
+               ob->x -= ob->xmove;\r
+               ob->xdir = -ob->xdir;\r
+               ChangeState(ob, &s_amptonturn);\r
+       }\r
+       RF_PlaceSprite(&ob->sprite, ob->x, ob->y, ob->shapenum, spritedraw, ob->priority);\r
+}\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                 SLICESTAR\r
+\r
+temp4 = health\r
+\r
+=============================================================================\r
+*/\r
+\r
+statetype s_slicestarslide  = {SLICESTARSPR,     SLICESTARSPR,     think, false, false,  0,  0,  0, T_Platform, C_Slicestar, R_Draw, NULL};\r
+statetype s_slicestarbounce = {SLICESTARSPR,     SLICESTARSPR,     slide, false, false,  0, 24, 24, NULL, C_Slicestar, R_Slicestar, &s_slicestarbounce};\r
+statetype s_slicestarboom   = {SLICESTARBOOMSPR, SLICESTARBOOMSPR, step,  false, false, 20,  0,  0, NULL, NULL, R_Draw, NULL};\r
+\r
+/*\r
+===========================\r
+=\r
+= SpawnSlicestarSlide\r
+=\r
+===========================\r
+*/\r
+\r
+void SpawnSlicestarSlide(Uint16 tileX, Uint16 tileY, Sint16 dir)\r
+{\r
+       GetNewObj(false);\r
+       new->obclass = slicestarobj;\r
+       new->active = ac_yes;\r
+       new->priority = 2;\r
+       new->x = CONVERT_TILE_TO_GLOBAL(tileX);\r
+       new->y = CONVERT_TILE_TO_GLOBAL(tileY);\r
+       new->temp4 = 20;        // health!\r
+       switch (dir)\r
+       {\r
+       case 0:\r
+               new->xdir = 0;\r
+               new->ydir = -1;\r
+               break;\r
+       case 1:\r
+               new->xdir = 1;\r
+               new->ydir = 0;\r
+               break;\r
+       case 2:\r
+               new->xdir = 0;\r
+               new->ydir = 1;\r
+               break;\r
+       case 3:\r
+               new->xdir = -1;\r
+               new->ydir = 0;\r
+       }\r
+       NewState(new, &s_slicestarslide);\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= SpawnSlicestarBounce\r
+=\r
+===========================\r
+*/\r
+\r
+void SpawnSlicestarBounce(Uint16 tileX, Uint16 tileY)\r
+{\r
+       GetNewObj(false);\r
+       new->obclass = slicestarobj;\r
+       new->active = ac_yes;\r
+       new->priority = 2;\r
+       new->needtoclip = cl_fullclip;\r
+       new->x = CONVERT_TILE_TO_GLOBAL(tileX);\r
+       new->y = CONVERT_TILE_TO_GLOBAL(tileY);\r
+       new->temp4 = 50;        // health!\r
+       switch (US_RndT() / 0x40)\r
+       {\r
+       case 0:\r
+               new->xdir = -1;\r
+               new->ydir = -1;\r
+               break;\r
+       case 1:\r
+               new->xdir = 1;\r
+               new->ydir = 1;\r
+               break;\r
+       case 2:\r
+               new->xdir = -1;\r
+               new->ydir = 1;\r
+               break;\r
+       case 3:\r
+               new->xdir = 1;\r
+               new->ydir = -1;\r
+       }\r
+       NewState(new, &s_slicestarbounce);\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= C_Slicestar\r
+=\r
+===========================\r
+*/\r
+\r
+void C_Slicestar(objtype *ob, objtype *hit)\r
+{\r
+       if (hit->obclass == keenobj)\r
+       {\r
+               KillKeen();\r
+       }\r
+       else if (hit->obclass == stunshotobj)\r
+       {\r
+               ExplodeShot(hit);\r
+               if (--ob->temp4 == 0)\r
+               {\r
+                       ChangeState(ob, &s_slicestarboom);\r
+               }\r
+       }\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= R_Slicestar\r
+=\r
+===========================\r
+*/\r
+\r
+void R_Slicestar(objtype *ob)\r
+{\r
+       if (ob->hitnorth)\r
+       {\r
+               ob->ydir = -1;\r
+               SD_PlaySound(SND_SLICESTARBOUNCE);\r
+       }\r
+       else if (ob->hitsouth)\r
+       {\r
+               ob->ydir = 1;\r
+               SD_PlaySound(SND_SLICESTARBOUNCE);\r
+       }\r
+       if (ob->hitwest)\r
+       {\r
+               ob->xdir = -1;\r
+               SD_PlaySound(SND_SLICESTARBOUNCE);\r
+       }\r
+       else if (ob->hiteast)\r
+       {\r
+               ob->xdir = 1;\r
+               SD_PlaySound(SND_SLICESTARBOUNCE);\r
+       }\r
+       RF_PlaceSprite(&ob->sprite, ob->x, ob->y, ob->shapenum, spritedraw, ob->priority);\r
+}\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                 SHELLEY\r
+\r
+=============================================================================\r
+*/\r
+\r
+statetype s_shellywalk1  = {SHELLEYL1SPR,     SHELLEYR1SPR,     step,      false, true,    8, 128, 0, NULL, C_Shelly, R_Shelly, &s_shellywalk2};\r
+statetype s_shellywalk2  = {SHELLEYL2SPR,     SHELLEYR2SPR,     step,      false, true,    8, 128, 0, NULL, C_Shelly, R_Shelly, &s_shellywalk3};\r
+statetype s_shellywalk3  = {SHELLEYL3SPR,     SHELLEYR3SPR,     step,      false, true,    8, 128, 0, NULL, C_Shelly, R_Shelly, &s_shellywalk4};\r
+statetype s_shellywalk4  = {SHELLEYL4SPR,     SHELLEYR4SPR,     step,      false, true,    8, 128, 0, NULL, C_Shelly, R_Shelly, &s_shellywalk1};\r
+statetype s_shellylook   = {SHELLEYL2SPR,     SHELLEYR2SPR,     stepthink, false, true,  100,   0, 0, T_ShellyLook, C_Shelly, R_Draw, &s_shellylook2};\r
+statetype s_shellylook2  = {SHELLEYL2SPR,     SHELLEYR2SPR,     step,      true,  true,    1,   0, 0, T_Turn, C_Shelly, R_Draw, &s_shellywalk1};\r
+statetype s_shellyjump1  = {SHELLEYJUMPLSPR,  SHELLEYJUMPRSPR,  stepthink, false, false,   8,   0, 0, T_Projectile, C_Shelly, R_Shell, &s_shellyjump2};\r
+statetype s_shellyjump2  = {SHELLEYFALLLSPR,  SHELLEYFALLRSPR,  think,     false, false,   8,   0, 0, T_Projectile, C_Shelly, R_Shell, NULL};\r
+statetype s_shellydie    = {SHELLEYFALLLSPR,  SHELLEYFALLRSPR,  step,      false, false,   8,   0, 0, T_ShellyFrag, NULL, R_Shell, NULL};\r
+statetype s_shellydieup  = {SHELLEYL2SPR,     SHELLEYR2SPR,     step,      false, false,   8,   0, 0, T_ShellyFrag, NULL, R_Shell, NULL};\r
+statetype s_shellyboom1  = {SHELLEYBOOM1SPR,  SHELLEYBOOM1SPR,  step,      false, false,  20,   0, 0, NULL, C_Lethal, R_Draw, &s_shellyboom2};\r
+statetype s_shellyboom2  = {SHELLEYBOOM2SPR,  SHELLEYBOOM2SPR,  step,      false, false,  20,   0, 0, NULL, C_Lethal, R_Draw, &s_shellyboom3};\r
+statetype s_shellyboom3  = {SHELLEYBOOM3SPR,  SHELLEYBOOM3SPR,  step,      false, false,  20,   0, 0, NULL, C_Lethal, R_Draw, &s_shellyboom4};\r
+statetype s_shellyboom4  = {SHELLEYBOOM4SPR,  SHELLEYBOOM4SPR,  step,      false, false,  20,   0, 0, NULL, C_Lethal, R_Draw, NULL};\r
+statetype s_shellypiece1 = {SHELLEYPIECE1SPR, SHELLEYPIECE1SPR, think,     false, false,   8,   0, 0, T_Projectile, C_Lethal, R_Bounce, NULL};\r
+statetype s_shellypiece2 = {SHELLEYPIECE2SPR, SHELLEYPIECE2SPR, think,     false, false,   8,   0, 0, T_Projectile, C_Lethal, R_Bounce, NULL};\r
+\r
+/*\r
+===========================\r
+=\r
+= SpawnShelly\r
+=\r
+===========================\r
+*/\r
+\r
+void SpawnShelly(Uint16 tileX, Uint16 tileY)\r
+{\r
+       GetNewObj(false);\r
+       new->obclass = sparkyobj;       // BUG: should use shelleyobj\r
+       new->active = ac_yes;\r
+       new->priority = 0;\r
+       new->x = CONVERT_TILE_TO_GLOBAL(tileX);\r
+       new->y = CONVERT_TILE_TO_GLOBAL(tileY);\r
+       if (US_RndT() < 0x80)\r
+       {\r
+               new->xdir = 1;\r
+       }\r
+       else\r
+       {\r
+               new->xdir = -1;\r
+       }\r
+       new->ydir = 1;\r
+       NewState(new, &s_shellywalk1);\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= T_ShellyLook\r
+=\r
+===========================\r
+*/\r
+\r
+void T_ShellyLook(objtype *ob)\r
+{\r
+       Sint16 xdist;\r
+\r
+       if (player->top < ob->bottom)\r
+               return;\r
+\r
+       xdist = player->midx - ob->midx;\r
+       if (ob->xdir == 1)\r
+       {\r
+               if (xdist > 1*TILEGLOBAL && xdist < 3*TILEGLOBAL)\r
+               {\r
+                       ob->xspeed = 16;\r
+                       ob->yspeed = -24;\r
+                       ob->state = &s_shellyjump1;\r
+                       xtry = ytry = 0;\r
+               }\r
+       }\r
+       else\r
+       {\r
+               if (xdist < -1*TILEGLOBAL && xdist > -3*TILEGLOBAL)\r
+               {\r
+                       ob->xspeed = -16;\r
+                       ob->yspeed = -24;\r
+                       ob->state = &s_shellyjump1;\r
+                       xtry = ytry = 0;\r
+               }\r
+       }\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= T_Turn\r
+=\r
+===========================\r
+*/\r
+\r
+void T_Turn(objtype *ob)\r
+{\r
+       ob->xdir = -ob->xdir;\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= T_ShellyFrag\r
+=\r
+===========================\r
+*/\r
+\r
+void T_ShellyFrag(objtype *ob)\r
+{\r
+       GetNewObj(true);\r
+       new->x = ob->x;\r
+       new->y = ob->y;\r
+       new->xspeed = 32;\r
+       new->yspeed = -24;\r
+       NewState(new, &s_shellypiece1);\r
+\r
+       GetNewObj(true);\r
+       new->x = ob->x;\r
+       new->y = ob->y;\r
+       new->xspeed = -32;\r
+       new->yspeed = -24;\r
+       NewState(new, &s_shellypiece2);\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= C_Shelly\r
+=\r
+===========================\r
+*/\r
+\r
+void C_Shelly(objtype *ob, objtype *hit)\r
+{\r
+       if (hit->obclass == keenobj)\r
+       {\r
+               ClipToSpriteSide(hit, ob);\r
+               if (player->midx < ob->left || player->midx > ob->right)\r
+                       return;\r
+       }\r
+       else if (hit->obclass == stunshotobj)\r
+       {\r
+               ExplodeShot(hit);\r
+       }\r
+       else if (hit->obclass == mshotobj)\r
+       {\r
+               RemoveObj(hit);\r
+       }\r
+       else\r
+               return;\r
+\r
+explode:\r
+       SD_PlaySound(SND_SHELLEYEXPLODE);\r
+       if (ob->hitnorth)\r
+       {\r
+               ChangeState(ob, &s_shellydieup);\r
+       }\r
+       else\r
+       {\r
+               ChangeState(ob, &s_shellydie);\r
+       }\r
+       GetNewObj(true);\r
+       new->x = ob->x;\r
+       new->y = ob->y;\r
+       NewState(new, &s_shellyboom1);\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= R_Shelly\r
+=\r
+===========================\r
+*/\r
+\r
+void R_Shelly(objtype *ob)\r
+{\r
+       if (ob->xdir == 1 && ob->hitwest)\r
+       {\r
+               ob->x -= ob->xmove;\r
+               ob->xdir = -1;\r
+       }\r
+       else if (ob->xdir == -1 && ob->hiteast)\r
+       {\r
+               ob->x -= ob->xmove;\r
+               ob->xdir = 1;\r
+       }\r
+       else if (!ob->hitnorth)\r
+       {\r
+               ob->x -= ob->xmove;\r
+               ChangeState(ob, &s_shellylook);\r
+       }\r
+       RF_PlaceSprite(&ob->sprite, ob->x, ob->y, ob->shapenum, spritedraw, ob->priority);\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= R_Shell\r
+=\r
+===========================\r
+*/\r
+\r
+void R_Shell(objtype *ob)\r
+{\r
+       if (ob->hiteast || ob->hitwest)\r
+       {\r
+               ob->xspeed = 0;\r
+       }\r
+       if (ob->hitnorth)\r
+       {\r
+               SD_PlaySound(SND_SHELLEYEXPLODE);\r
+               ChangeState(ob, &s_shellydie);\r
+\r
+               GetNewObj(true);\r
+               new->x = ob->x;\r
+               new->y = ob->y;\r
+               NewState(new, &s_shellyboom1);\r
+       }\r
+       RF_PlaceSprite(&ob->sprite, ob->x, ob->y, ob->shapenum, spritedraw, ob->priority);\r
+}
\ No newline at end of file
diff --git a/16/keen456/KEEN4-6/KEEN5/K5_ACT3.C b/16/keen456/KEEN4-6/KEEN5/K5_ACT3.C
new file mode 100755 (executable)
index 0000000..a0fa9c7
--- /dev/null
@@ -0,0 +1,2142 @@
+/* Reconstructed Commander Keen 4-6 Source Code\r
+ * Copyright (C) 2021 K1n9_Duk3\r
+ *\r
+ * This file is loosely based on:\r
+ * Keen Dreams Source Code\r
+ * Copyright (C) 2014 Javier M. Chavez\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License along\r
+ * with this program; if not, write to the Free Software Foundation, Inc.,\r
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+ */\r
+\r
+/*\r
+K5_ACT3.C\r
+=========\r
+\r
+Contains the following actor types (in this order):\r
+\r
+- Shikadi Mine\r
+- Robo Red\r
+- Spirogrip\r
+- Spindred\r
+- Shikadi Master\r
+- Shikadi\r
+- Shockshund\r
+- Sphereful\r
+- Scottie\r
+- QED\r
+\r
+*/\r
+\r
+#include "CK_DEF.H"\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                 SHIKADI MINE\r
+\r
+temp2 = x position of the "eye", in global units (relative to the mine sprite)\r
+temp3 = y position of the "eye", in global units (relative to the mine sprite)\r
+temp4 = sprite pointer for the "eye"\r
+\r
+=============================================================================\r
+*/\r
+\r
+static Uint16 dopposite[] = {2, 3, 0, 1, 6, 7, 4, 5, 8};\r
+\r
+statetype s_mine       = {SHIKADIMINESPR,       SHIKADIMINESPR,       think, false, false,  8, 0, 0, T_Mine, C_Solid, R_Mine, NULL};\r
+statetype s_minecenter = {SHIKADIMINESPR,       SHIKADIMINESPR,       think, false, false,  0, 0, 0, T_MineCenter, C_Solid, R_Mine, &s_mineshift};\r
+statetype s_mineshift  = {SHIKADIMINESPR,       SHIKADIMINESPR,       think, false, false,  0, 0, 0, T_MineShift, C_Solid, R_Mine, &s_mine};\r
+statetype s_mineboom1  = {SHIKADIMINEPULSE1SPR, SHIKADIMINEPULSE1SPR, step,  false, false, 10, 0, 0, NULL, C_Solid, R_Draw, &s_mineboom2};\r
+statetype s_mineboom2  = {SHIKADIMINEPULSE2SPR, SHIKADIMINEPULSE2SPR, step,  false, false, 10, 0, 0, NULL, C_Solid, R_Draw, &s_mineboom3};\r
+statetype s_mineboom3  = {SHIKADIMINEPULSE1SPR, SHIKADIMINEPULSE1SPR, step,  false, false, 10, 0, 0, NULL, C_Solid, R_Draw, &s_mineboom4};\r
+statetype s_mineboom4  = {SHIKADIMINEPULSE2SPR, SHIKADIMINEPULSE2SPR, step,  false, false, 10, 0, 0, T_MineFrag, C_Solid, R_Draw, &s_mineboom5};\r
+statetype s_mineboom5  = {SHIKADIMINEBOOM1SPR,  SHIKADIMINEBOOM1SPR,  step,  false, false, 20, 0, 0, NULL, C_Spindread, R_Draw, &s_mineboom6};\r
+statetype s_mineboom6  = {SHIKADIMINEBOOM2SPR,  SHIKADIMINEBOOM2SPR,  step,  false, false, 20, 0, 0, NULL, C_Spindread, R_Draw, NULL};\r
+statetype s_minepiece  = {SHIKADIMINEPIECESPR,  SHIKADIMINEPIECESPR,  think, false, false,  8, 0, 0, T_Projectile, C_MineFrag, R_Bounce, NULL};\r
+\r
+/*\r
+===========================\r
+=\r
+= SpawnMine\r
+=\r
+===========================\r
+*/\r
+\r
+void SpawnMine(Uint16 tileX, Uint16 tileY)\r
+{\r
+       Sint16 i;\r
+\r
+       GetNewObj(false);\r
+       new->obclass = mineobj;\r
+       new->active = ac_yes;\r
+       new->needtoclip = cl_noclip;\r
+       new->x = CONVERT_TILE_TO_GLOBAL(tileX);\r
+       new->y = CONVERT_TILE_TO_GLOBAL(tileY) + -(31*PIXGLOBAL + 1);\r
+       new->temp2 = 16*PIXGLOBAL;\r
+       new->temp3 = 13*PIXGLOBAL;\r
+       NewState(new, &s_mineshift);\r
+       new->xspeed = TILEGLOBAL;\r
+\r
+       for (i=0; i<=3; i++)\r
+       {\r
+               if (Walk(new, i))\r
+                       break;\r
+       }\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= MinePosCheck\r
+=\r
+===========================\r
+*/\r
+\r
+boolean MinePosCheck(Uint16 tileX, Uint16 tileY)\r
+{\r
+       Uint16 far *map;\r
+       Uint16 x, y, tile;\r
+\r
+       map = mapsegs[1] + mapbwidthtable[tileY]/2 + tileX;\r
+       for (y=0; y<2; y++)\r
+       {\r
+               for (x=0; x<3; x++)\r
+               {\r
+                       tile = *(map + y*mapwidth + x);\r
+                       if (tinf[tile+NORTHWALL] || tinf[tile+EASTWALL] || tinf[tile+SOUTHWALL] || tinf[tile+WESTWALL])\r
+                               return false;\r
+               }\r
+       }\r
+       return true;\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= Walk\r
+=\r
+===========================\r
+*/\r
+\r
+boolean Walk(objtype *ob, Sint16 dir)\r
+{\r
+       Uint16 tileX, tileY;\r
+\r
+       tileX = CONVERT_GLOBAL_TO_TILE(ob->x + xtry);\r
+       tileY = CONVERT_GLOBAL_TO_TILE(ob->y + ytry);\r
+\r
+       switch (dir)\r
+       {\r
+       case 0:\r
+               if (MinePosCheck(tileX, tileY-1))\r
+               {\r
+                       ob->xdir = 0;\r
+                       ob->ydir = -1;\r
+                       return true;\r
+               }\r
+               return false;\r
+       case 1:\r
+               if (MinePosCheck(tileX+1, tileY))\r
+               {\r
+                       ob->xdir = 1;\r
+                       ob->ydir = 0;\r
+                       return true;\r
+               }\r
+               return false;\r
+       case 2:\r
+               if (MinePosCheck(tileX, tileY+1))\r
+               {\r
+                       ob->xdir = 0;\r
+                       ob->ydir = 1;\r
+                       return true;\r
+               }\r
+               return false;\r
+       case 3:\r
+               if (MinePosCheck(tileX-1, tileY))\r
+               {\r
+                       ob->xdir = -1;\r
+                       ob->ydir = 0;\r
+                       return true;\r
+               }\r
+               return false;\r
+       default:\r
+               Quit("Walk: Bad dir");\r
+       }\r
+       return false;\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= ChaseThink\r
+=\r
+===========================\r
+*/\r
+\r
+void ChaseThink(objtype *ob)\r
+{\r
+       Sint16 temp;\r
+       Sint16 xdist, ydist, ydir, xdir;\r
+       Sint16 olddir[2], oppdir;\r
+\r
+       if (ob->xdir == 1)\r
+       {\r
+               olddir[0] = 1;\r
+       }\r
+       else if (ob->xdir == -1)\r
+       {\r
+               olddir[0] = 3;\r
+       }\r
+       else if (ob->ydir == -1)\r
+       {\r
+               olddir[0] = 0;\r
+       }\r
+       else if (ob->ydir == 1)\r
+       {\r
+               olddir[0] = 2;\r
+       }\r
+       oppdir = dopposite[olddir[0]];\r
+       xdist = player->x - (ob->x + xtry);\r
+       ydist = player->y - (ob->y + ytry);\r
+       xdir = 8;\r
+       ydir = 8;\r
+       if (xdist > 0)\r
+       {\r
+               xdir = 1;\r
+       }\r
+       if (xdist < 0)\r
+       {\r
+               xdir = 3;\r
+       }\r
+       if (ydist > 0)\r
+       {\r
+               ydir = 2;\r
+       }\r
+       if (ydist < 0)\r
+       {\r
+               ydir = 0;\r
+       }\r
+       if (abs(ydist) > abs(xdist))\r
+       {\r
+               temp = xdir;\r
+               xdir = ydir;\r
+               ydir = temp;\r
+       }\r
+       if (xdir == oppdir)\r
+       {\r
+               xdir = 8;\r
+       }\r
+       if (ydir == oppdir)\r
+       {\r
+               ydir = 8;\r
+       }\r
+       if (ydir != 8 && Walk(ob, ydir))\r
+       {\r
+               return;\r
+       }\r
+       if (xdir != 8 && Walk(ob, xdir))\r
+       {\r
+               return;\r
+       }\r
+       if (Walk(ob, olddir[0]))\r
+       {\r
+               return;\r
+       }\r
+       if (US_RndT() > 0x80)\r
+       {\r
+               for (temp=0; temp<=3; temp++)\r
+               {\r
+                       if (temp != oppdir && Walk(ob, temp))\r
+                               return;\r
+               }\r
+       }\r
+       else\r
+       {\r
+               for (temp=3; temp>=0; temp--)\r
+               {\r
+                       if (temp != oppdir && Walk(ob, temp))\r
+                               return;\r
+               }\r
+       }\r
+       Walk(ob, oppdir);\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= T_Mine\r
+=\r
+===========================\r
+*/\r
+\r
+void T_Mine(objtype *ob)\r
+{\r
+       Sint16 oldxdir, oldydir;\r
+       Sint16 xdist, ydist;\r
+       Sint16 speed;\r
+\r
+       xdist = ob->x - player->x;\r
+       ydist = ob->y - player->y;\r
+       if (xdist <= 2*TILEGLOBAL && xdist >= -5*TILEGLOBAL && ydist <= 3*TILEGLOBAL && ydist >= -5*PIXGLOBAL)\r
+       {\r
+               SD_PlaySound(SND_MINEEXPLODE);\r
+               ob->state = &s_mineboom1;\r
+               RF_RemoveSprite((void**)(&ob->temp4));\r
+       }\r
+       else\r
+       {\r
+               speed = tics * 10;\r
+               if (ob->xspeed <= speed)\r
+               {\r
+                       xtry = ob->xdir * ob->xspeed;\r
+                       ytry = ob->ydir * ob->xspeed;   // yes, this uses xspeed!\r
+                       speed -= ob->xspeed;\r
+                       oldxdir = ob->xdir;\r
+                       oldydir = ob->ydir;\r
+                       ChaseThink(ob);\r
+                       ob->xspeed = TILEGLOBAL;\r
+                       if (ob->xdir != oldxdir || ob->ydir != oldydir)\r
+                       {\r
+                               ob->state = &s_minecenter;\r
+                               return;\r
+                       }\r
+               }\r
+               ob->xspeed -= speed;\r
+               xtry += ob->xdir * speed;\r
+               ytry += ob->ydir * speed;\r
+       }\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= C_Solid\r
+=\r
+===========================\r
+*/\r
+\r
+#pragma argsused\r
+void C_Solid(objtype *ob, objtype *hit)\r
+{\r
+       if (hit->obclass == stunshotobj)\r
+       {\r
+               ExplodeShot(hit);\r
+       }\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= C_MineFrag\r
+=\r
+===========================\r
+*/\r
+\r
+#pragma argsused\r
+void C_MineFrag(objtype *ob, objtype *hit)\r
+{\r
+       if (hit->obclass == stunshotobj)\r
+       {\r
+               ExplodeShot(hit);\r
+       }\r
+       else if (hit->obclass == keenobj)\r
+       {\r
+               KillKeen();\r
+       }\r
+       else if (hit->obclass == qedobj)\r
+       {\r
+               SpawnFuseFlash(hit->tileleft, hit->tiletop);\r
+               SpawnFuseFlash(hit->tileright, hit->tiletop);\r
+               SpawnFuseFlash(hit->tileleft, hit->tilebottom);\r
+               SpawnFuseFlash(hit->tileright, hit->tilebottom);\r
+               RF_MapToMap(0, 0, 16, 11, 4, 2);\r
+               RF_MapToMap(4, 0, 16, 13, 4, 2);\r
+               SpawnDeadMachine();\r
+               RemoveObj(hit);\r
+       }\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= T_MineCenter\r
+=\r
+===========================\r
+*/\r
+\r
+void T_MineCenter(objtype *ob)\r
+{\r
+       Sint16 px, py, xdist, ydist;\r
+\r
+       xdist = ob->x - player->x;\r
+       ydist = ob->y - player->y;\r
+\r
+       if (xdist <= 2*TILEGLOBAL && xdist >= -3*TILEGLOBAL && ydist <= 3*TILEGLOBAL && ydist >= -3*TILEGLOBAL)\r
+       {\r
+               //BUG? this doesn't play a sound\r
+               ob->state = &s_mineboom1;\r
+               RF_RemoveSprite((void**)&ob->temp4);\r
+       }\r
+       else\r
+       {\r
+               ob->needtoreact = true;\r
+\r
+               px = 16*PIXGLOBAL;\r
+               py = 13*PIXGLOBAL;\r
+\r
+               if (ob->temp2 < px)\r
+               {\r
+                       ob->temp2 = ob->temp2 + tics*4;\r
+                       if (ob->temp2 >= px)\r
+                       {\r
+                               ob->temp2 = px;\r
+                               ob->state = ob->state->nextstate;\r
+                       }\r
+               }\r
+               else if (ob->temp2 > px)\r
+               {\r
+                       ob->temp2 = ob->temp2 - tics*4;\r
+                       if (ob->temp2 <= px)\r
+                       {\r
+                               ob->temp2 = px;\r
+                               ob->state = ob->state->nextstate;\r
+                       }\r
+               }\r
+               if (ob->temp3 < py)\r
+               {\r
+                       ob->temp3 = ob->temp3 + tics*4;\r
+                       if (ob->temp3 >= py)\r
+                       {\r
+                               ob->temp3 = py;\r
+                               ob->state = ob->state->nextstate;\r
+                       }\r
+               }\r
+               else if (ob->temp3 > py)\r
+               {\r
+                       ob->temp3 = ob->temp3 - tics*4;\r
+                       if (ob->temp3 <= py)\r
+                       {\r
+                               ob->temp3 = py;\r
+                               ob->state = ob->state->nextstate;\r
+                       }\r
+               }\r
+       }\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= T_MineShift\r
+=\r
+===========================\r
+*/\r
+\r
+void T_MineShift(objtype *ob)\r
+{\r
+       Sint16 px, py, xdist, ydist;\r
+\r
+       xdist = ob->x - player->x;\r
+       ydist = ob->y - player->y;\r
+\r
+       if (xdist <= 2*TILEGLOBAL && xdist >= -3*TILEGLOBAL && ydist <= 3*TILEGLOBAL && ydist >= -3*TILEGLOBAL)\r
+       {\r
+               //BUG? this doesn't play a sound\r
+               ob->state = &s_mineboom1;\r
+               RF_RemoveSprite((void**)&ob->temp4);\r
+       }\r
+       else\r
+       {\r
+               ob->needtoreact = true;\r
+\r
+               switch (ob->xdir)\r
+               {\r
+               case -1:\r
+                       px = 8*PIXGLOBAL;\r
+                       break;\r
+               case 0:\r
+                       px = 16*PIXGLOBAL;\r
+                       break;\r
+               case 1:\r
+                       px = 24*PIXGLOBAL;\r
+               }\r
+               switch (ob->ydir)\r
+               {\r
+               case -1:\r
+                       py = 5*PIXGLOBAL;\r
+                       break;\r
+               case 0:\r
+                       py = 13*PIXGLOBAL;\r
+                       break;\r
+               case 1:\r
+                       py = 21*PIXGLOBAL;\r
+               }\r
+\r
+               if (ob->temp2 < px)\r
+               {\r
+                       ob->temp2 = ob->temp2 + tics*4;\r
+                       if (ob->temp2 >= px)\r
+                       {\r
+                               ob->temp2 = px;\r
+                               ob->state = ob->state->nextstate;\r
+                       }\r
+               }\r
+               else if (ob->temp2 > px)\r
+               {\r
+                       ob->temp2 = ob->temp2 - tics*4;\r
+                       if (ob->temp2 <= px)\r
+                       {\r
+                               ob->temp2 = px;\r
+                               ob->state = ob->state->nextstate;\r
+                       }\r
+               }\r
+               if (ob->temp3 < py)\r
+               {\r
+                       ob->temp3 = ob->temp3 + tics*4;\r
+                       if (ob->temp3 >= py)\r
+                       {\r
+                               ob->temp3 = py;\r
+                               ob->state = ob->state->nextstate;\r
+                       }\r
+               }\r
+               else if (ob->temp3 > py)\r
+               {\r
+                       ob->temp3 = ob->temp3 - tics*4;\r
+                       if (ob->temp3 <= py)\r
+                       {\r
+                               ob->temp3 = py;\r
+                               ob->state = ob->state->nextstate;\r
+                       }\r
+               }\r
+       }\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= T_MineFrag\r
+=\r
+===========================\r
+*/\r
+\r
+void T_MineFrag(objtype *ob)\r
+{\r
+       SD_PlaySound(SND_MINEEXPLODE);\r
+\r
+       GetNewObj(true);\r
+       new->x = ob->x;\r
+       new->y = ob->y;\r
+       new->xspeed = -(US_RndT()>>3);\r
+       new->yspeed = -48;\r
+       NewState(new, &s_minepiece);\r
+\r
+       GetNewObj(true);\r
+       new->x = ob->x + TILEGLOBAL;\r
+       new->y = ob->y;\r
+       new->xspeed = (US_RndT()>>3);\r
+       new->yspeed = -48;\r
+       NewState(new, &s_minepiece);\r
+\r
+       GetNewObj(true);\r
+       new->x = ob->x;\r
+       new->y = ob->y;\r
+       new->xspeed = (US_RndT()>>4) + 40;\r
+       new->yspeed = -24;\r
+       NewState(new, &s_minepiece);\r
+\r
+       GetNewObj(true);\r
+       new->x = ob->x + TILEGLOBAL;\r
+       new->y = ob->y;\r
+       new->xspeed = -40 - (US_RndT()>>4);\r
+       new->yspeed = -24;\r
+       NewState(new, &s_minepiece);\r
+\r
+       GetNewObj(true);\r
+       new->x = ob->x;\r
+       new->y = ob->y;\r
+       new->xspeed = 24;\r
+       new->yspeed = 16;\r
+       NewState(new, &s_minepiece);\r
+\r
+       GetNewObj(true);\r
+       new->x = ob->x + TILEGLOBAL;\r
+       new->y = ob->y;\r
+       new->xspeed = 24;\r
+       new->yspeed = 16;\r
+       NewState(new, &s_minepiece);\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= R_Mine\r
+=\r
+===========================\r
+*/\r
+\r
+void R_Mine(objtype *ob)\r
+{\r
+       RF_PlaceSprite(&ob->sprite, ob->x, ob->y, ob->shapenum, spritedraw, ob->priority);\r
+       RF_PlaceSprite((void**)&ob->temp4, ob->x+ob->temp2, ob->y+ob->temp3, SHIKADIMINEEYESPR, spritedraw, 2);\r
+}\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                 ROBO RED\r
+\r
+temp1 = number of shots to fire\r
+\r
+=============================================================================\r
+*/\r
+\r
+statetype s_robored      = {ROBOREDLSPR,     ROBOREDRSPR,     step,      false, true,  6, 64, 0, T_RoboRed, C_RoboRed, R_Walk, &s_robored};\r
+statetype s_roboredfire0 = {ROBOREDLSPR,     ROBOREDRSPR,     step,      true,  true, 40,  0, 0, NULL, C_Spindread, R_Draw, &s_roboredfire1};\r
+statetype s_roboredfire1 = {ROBOREDLSPR,     ROBOREDRSPR,     step,      true,  true,  4, 64, 0, NULL, C_Spindread, R_Draw, &s_roboredfire2};\r
+statetype s_roboredfire2 = {ROBOREDLSPR,     ROBOREDRSPR,     step,      false, true,  6,  0, 0, T_RoboShoot, C_Spindread, R_Draw, &s_roboredfire1};\r
+statetype s_rshot1       = {ROBOSHOT1SPR,    ROBOSHOT1SPR,    stepthink, false, false,  8,  0, 0, T_Velocity, C_RShot, R_RShot, &s_rshot2};\r
+statetype s_rshot2       = {ROBOSHOT2SPR,    ROBOSHOT2SPR,    stepthink, false, false,  8,  0, 0, T_Velocity, C_RShot, R_RShot, &s_rshot1};\r
+statetype s_rshothit1    = {ROBOSHOTHIT1SPR, ROBOSHOTHIT1SPR, step,      false, false, 10,  0, 0, NULL, NULL, R_Draw, &s_rshothit2};\r
+statetype s_rshothit2    = {ROBOSHOTHIT2SPR, ROBOSHOTHIT2SPR, step,      false, false, 10,  0, 0, NULL, NULL, R_Draw, NULL};\r
+\r
+/*\r
+===========================\r
+=\r
+= SpawnRoboRed\r
+=\r
+===========================\r
+*/\r
+\r
+void SpawnRoboRed(Uint16 tileX, Uint16 tileY)\r
+{\r
+       GetNewObj(false);\r
+       new->obclass = roboredobj;\r
+       new->active = ac_yes;\r
+       new->x = CONVERT_TILE_TO_GLOBAL(tileX);\r
+       new->y = CONVERT_TILE_TO_GLOBAL(tileY) + -4*TILEGLOBAL;\r
+       if (US_RndT() < 0x80)\r
+       {\r
+               new->xdir = 1;\r
+       }\r
+       else\r
+       {\r
+               new->xdir = -1;\r
+       }\r
+       NewState(new, &s_robored);\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= T_RoboRed\r
+=\r
+===========================\r
+*/\r
+\r
+void T_RoboRed(objtype *ob)\r
+{\r
+       if (!(ob->x & (4*PIXGLOBAL)) && player->bottom > ob->top && player->top < ob->bottom && US_RndT() < 16)\r
+       {\r
+               if (ob->x > player->x)\r
+               {\r
+                       ob->xdir = -1;\r
+               }\r
+               else\r
+               {\r
+                       ob->xdir = 1;\r
+               }\r
+               ob->temp1 = 10; // shoot 10 times\r
+               ob->state = &s_roboredfire0;\r
+       }\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= C_RoboRed\r
+=\r
+===========================\r
+*/\r
+\r
+void C_RoboRed(objtype *ob, objtype *hit)\r
+{\r
+       if (hit->obclass == stunshotobj)\r
+       {\r
+               ExplodeShot(hit);\r
+               ob->xdir = (player->x > ob->x)? 1 : -1;\r
+               ob->temp1 = 10; // shoot 10 times\r
+               ChangeState(ob, &s_roboredfire0);\r
+       }\r
+       else if (hit->obclass == keenobj)\r
+       {\r
+               KillKeen();\r
+       }\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= T_RoboShoot\r
+=\r
+===========================\r
+*/\r
+\r
+void T_RoboShoot(objtype *ob)\r
+{\r
+       Uint16 x;\r
+\r
+       if (--ob->temp1 == 0)\r
+       {\r
+               ob->state = &s_robored;\r
+       }\r
+       if (ob->xdir == 1)\r
+       {\r
+               x = ob->x + 56*PIXGLOBAL;\r
+       }\r
+       else\r
+       {\r
+               x = ob->x;\r
+       }\r
+       if (CheckSpawnShot(x, ob->y + 32*PIXGLOBAL, &s_rshot1) != -1)\r
+       {\r
+               new->xspeed = ob->xdir * 60;\r
+               if (ob->temp1 & 1)\r
+               {\r
+                       new->yspeed = 8;\r
+               }\r
+               else\r
+               {\r
+                       new->yspeed = -8;\r
+               }\r
+               SD_PlaySound(SND_ENEMYSHOT);\r
+               xtry = (ob->xdir == 1)? -4*PIXGLOBAL : 4*PIXGLOBAL;\r
+       }\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= C_RShot\r
+=\r
+===========================\r
+*/\r
+\r
+void C_RShot(objtype *ob, objtype *hit)\r
+{\r
+       if (hit->obclass == keenobj)\r
+       {\r
+               KillKeen();\r
+               ChangeState(ob, &s_rshothit1);\r
+       }\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= R_RShot\r
+=\r
+===========================\r
+*/\r
+\r
+void R_RShot(objtype *ob)\r
+{\r
+       if (ob->hitnorth || ob->hiteast || ob->hitsouth || ob->hitwest)\r
+       {\r
+               SD_PlaySound(SND_ENEMYSHOTEXPLODE);\r
+               ChangeState(ob, &s_rshothit1);\r
+       }\r
+       RF_PlaceSprite(&ob->sprite, ob->x, ob->y, ob->shapenum, spritedraw, ob->priority);\r
+}\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                 SPIROGRIP\r
+\r
+=============================================================================\r
+*/\r
+\r
+statetype s_gripsitd  = {SPIROSITDSPR,   SPIROSITDSPR,   step,  false, false, 150,   0,   0, NULL, C_Spindread, R_Draw, &s_gripjumpd};\r
+statetype s_gripjumpd = {SPIROSITDSPR,   SPIROSITDSPR,   slide, false, false,  64,   0, -16, NULL, C_Spindread, R_Draw, &s_gripspin7};\r
+statetype s_gripsitl  = {SPIROSITLSPR,   SPIROSITLSPR,   step,  false, false, 150,   0,   0, NULL, C_Spindread, R_Draw, &s_gripjumpl};\r
+statetype s_gripjumpl = {SPIROSITLSPR,   SPIROSITLSPR,   slide, false, false,  64,  16,   0, NULL, C_Spindread, R_Draw, &s_gripspin1};\r
+statetype s_gripsitr  = {SPIROSITRSPR,   SPIROSITRSPR,   step,  false, false, 150,   0,   0, NULL, C_Spindread, R_Draw, &s_gripjumpr};\r
+statetype s_gripjumpr = {SPIROSITRSPR,   SPIROSITRSPR,   slide, false, false,  64, -16,   0, NULL, C_Spindread, R_Draw, &s_gripspin5};\r
+statetype s_gripsitu  = {SPIROSITUSPR,   SPIROSITUSPR,   step,  false, false, 150,   0,   0, NULL, C_Spindread, R_Draw, &s_gripjumpu};\r
+statetype s_gripjumpu = {SPIROSITUSPR,   SPIROSITUSPR,   slide, false, false,  64,   0,  16, NULL, C_Spindread, R_Draw, &s_gripspin3};\r
+statetype s_gripspin1 = {SPIROSPINULSPR, SPIROSPINULSPR, step,  false, false,   8,   0,   0, NULL, C_Spindread, R_Draw, &s_gripspin2};\r
+statetype s_gripspin2 = {SPIROSPINUSPR,  SPIROSPINUSPR,  step,  false, false,   8,   0,   0, T_SpiroLaunch, C_Spindread, R_Draw, &s_gripspin3};\r
+statetype s_gripspin3 = {SPIROSPINURSPR, SPIROSPINURSPR, step,  false, false,   8,   0,   0, NULL, C_Spindread, R_Draw, &s_gripspin4};\r
+statetype s_gripspin4 = {SPIROSPINRSPR,  SPIROSPINRSPR,  step,  false, false,   8,   0,   0, T_SpiroLaunch, C_Spindread, R_Draw, &s_gripspin5};\r
+statetype s_gripspin5 = {SPIROSPINDRSPR, SPIROSPINDRSPR, step,  false, false,   8,   0,   0, NULL, C_Spindread, R_Draw, &s_gripspin6};\r
+statetype s_gripspin6 = {SPIROSPINDSPR,  SPIROSPINDSPR,  step,  false, false,   8,   0,   0, T_SpiroLaunch, C_Spindread, R_Draw, &s_gripspin7};\r
+statetype s_gripspin7 = {SPIROSPINDLSPR, SPIROSPINDLSPR, step,  false, false,   8,   0,   0, NULL, C_Spindread, R_Draw, &s_gripspin8};\r
+statetype s_gripspin8 = {SPIROSPINLSPR,  SPIROSPINLSPR,  step,  false, false,   8,   0,   0, T_SpiroLaunch, C_Spindread, R_Draw, &s_gripspin1};\r
+statetype s_gripflyd  = {SPIROSITDSPR,   SPIROSITDSPR,   slide, false, false,   0,   0,  48, NULL, C_Spindread, R_SpiroFly, &s_gripsitd};\r
+statetype s_gripflyl  = {SPIROSITLSPR,   SPIROSITLSPR,   slide, false, false,   0, -48,   0, NULL, C_Spindread, R_SpiroFly, &s_gripsitl};\r
+statetype s_gripflyr  = {SPIROSITRSPR,   SPIROSITRSPR,   slide, false, false,   0,  48,   0, NULL, C_Spindread, R_SpiroFly, &s_gripsitr};\r
+statetype s_gripflyu  = {SPIROSITUSPR,   SPIROSITUSPR,   slide, false, false,   0,   0, -48, NULL, C_Spindread, R_SpiroFly, &s_gripsitu};\r
+\r
+/*\r
+===========================\r
+=\r
+= SpawnSpirogrip\r
+=\r
+===========================\r
+*/\r
+\r
+void SpawnSpirogrip(Uint16 tileX, Uint16 tileY)\r
+{\r
+       GetNewObj(false);\r
+       new->obclass = spirogripobj;\r
+       new->active = ac_yes;\r
+       new->x = CONVERT_TILE_TO_GLOBAL(tileX);\r
+       new->y = CONVERT_TILE_TO_GLOBAL(tileY) + -1*TILEGLOBAL;\r
+       new->xdir = 1;\r
+       new->ydir = 1;\r
+       NewState(new, &s_gripsitd);\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= T_SpiroLaunch\r
+=\r
+===========================\r
+*/\r
+\r
+void T_SpiroLaunch(objtype *ob)\r
+{\r
+       if (US_RndT() <= 20)\r
+       {\r
+               SD_PlaySound(SND_SPIROFLY);\r
+               if (ob->state == &s_gripspin2)\r
+               {\r
+                       ob->state = &s_gripflyu;\r
+               }\r
+               else if (ob->state == &s_gripspin4)\r
+               {\r
+                       ob->state = &s_gripflyr;\r
+               }\r
+               else if (ob->state == &s_gripspin6)\r
+               {\r
+                       ob->state = &s_gripflyd;\r
+               }\r
+               else if (ob->state == &s_gripspin8)\r
+               {\r
+                       ob->state = &s_gripflyl;\r
+               }\r
+       }\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= R_SpiroFly\r
+=\r
+===========================\r
+*/\r
+\r
+void R_SpiroFly(objtype *ob)\r
+{\r
+       RF_PlaceSprite(&ob->sprite, ob->x, ob->y, ob->shapenum, spritedraw, ob->priority);\r
+       if (ob->hitnorth || ob->hiteast || ob->hitsouth || ob->hitwest)\r
+       {\r
+               ChangeState(ob, ob->state->nextstate);\r
+               SD_PlaySound(SND_SPIROGRAB);\r
+       }\r
+}\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                 SPINDRED\r
+\r
+temp1 = bounce counter\r
+\r
+=============================================================================\r
+*/\r
+\r
+statetype s_spindred1     = {SPINDRED1SPR, SPINDRED1SPR, stepthink, false, false, 8, 0, 0, T_Spindread, C_Spindread, R_Spindread, &s_spindred2};\r
+statetype s_spindred2     = {SPINDRED2SPR, SPINDRED2SPR, stepthink, false, false, 8, 0, 0, T_Spindread, C_Spindread, R_Spindread, &s_spindred3};\r
+statetype s_spindred3     = {SPINDRED3SPR, SPINDRED3SPR, stepthink, false, false, 8, 0, 0, T_Spindread, C_Spindread, R_Spindread, &s_spindred4};\r
+statetype s_spindred4     = {SPINDRED4SPR, SPINDRED4SPR, stepthink, false, false, 8, 0, 0, T_Spindread, C_Spindread, R_Spindread, &s_spindred1};\r
+\r
+/*\r
+===========================\r
+=\r
+= SpawnSpindread\r
+=\r
+===========================\r
+*/\r
+\r
+void SpawnSpindread(Uint16 tileX, Uint16 tileY)\r
+{\r
+       GetNewObj(false);\r
+       new->obclass = spindredobj;\r
+       new->active = ac_yes;\r
+       new->priority = 0;\r
+       new->x = CONVERT_TILE_TO_GLOBAL(tileX);\r
+       new->y = CONVERT_TILE_TO_GLOBAL(tileY) + -8*PIXGLOBAL;\r
+       new->ydir = 1;\r
+       NewState(new, &s_spindred1);\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= T_Spindread\r
+=\r
+===========================\r
+*/\r
+\r
+void T_Spindread(objtype *ob)\r
+{\r
+       Sint32 i;\r
+\r
+       // BUG: this might be executed twice during the same frame if the\r
+       // object's animation/state changed during that frame, causing the\r
+       // object to move at twice the speed during that frame!\r
+       // To avoid that, return if ytry is not 0.\r
+\r
+       for (i=lasttimecount-tics; i<lasttimecount; i++)\r
+       {\r
+               if (i & 1)\r
+               {\r
+                       if (ob->ydir == 1)\r
+                       {\r
+                               if (ob->yspeed < 0 && ob->yspeed >= -3)\r
+                               {\r
+                                       ytry += ob->yspeed;\r
+                                       ob->yspeed = 0;\r
+                                       return;\r
+                               }\r
+                               else\r
+                               {\r
+                                       ob->yspeed += 3;\r
+                                       if (ob->yspeed > 70)\r
+                                       {\r
+                                               ob->yspeed = 70;\r
+                                       }\r
+                               }\r
+                       }\r
+                       else\r
+                       {\r
+                               if (ob->yspeed > 0 && ob->yspeed <= 3)\r
+                               {\r
+                                       ytry += ob->yspeed;\r
+                                       ob->yspeed = 0;\r
+                                       return;\r
+                               }\r
+                               else\r
+                               {\r
+                                       ob->yspeed -= 3;\r
+                                       if (ob->yspeed < -70)\r
+                                       {\r
+                                               ob->yspeed = -70;\r
+                                       }\r
+                               }\r
+                       }\r
+               }\r
+               ytry += ob->yspeed;\r
+       }\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= C_Spindread\r
+=\r
+===========================\r
+*/\r
+\r
+#pragma argsused\r
+void C_Spindread(objtype *ob, objtype *hit)\r
+{\r
+       if (hit->obclass == stunshotobj)\r
+       {\r
+               ExplodeShot(hit);\r
+       }\r
+       else if (hit->obclass == keenobj)\r
+       {\r
+               KillKeen();\r
+       }\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= R_Spindread\r
+=\r
+===========================\r
+*/\r
+\r
+void R_Spindread(objtype *ob)\r
+{\r
+       if (ob->hitsouth)\r
+       {\r
+               ob->yspeed = 0;\r
+               if (ob->ydir == -1)\r
+               {\r
+                       if (++ob->temp1 == 3)\r
+                       {\r
+                               ob->temp1 = 0;\r
+                               ob->yspeed = 68;\r
+                               ob->ydir = -ob->ydir;\r
+                               SD_PlaySound(SND_SPINDREDFLIP);\r
+                       }\r
+                       else\r
+                       {\r
+                               SD_PlaySound(SND_SPINDREDBOUNCE);\r
+                               ob->yspeed = 40;\r
+                       }\r
+               }\r
+       }\r
+       if (ob->hitnorth)\r
+       {\r
+               ob->yspeed = 0;\r
+               if (ob->ydir == 1)\r
+               {\r
+                       if (++ob->temp1 == 3)\r
+                       {\r
+                               ob->temp1 = 0;\r
+                               ob->yspeed = -68;\r
+                               ob->ydir = -ob->ydir;\r
+                               SD_PlaySound(SND_BOUNCE);\r
+                       }\r
+                       else\r
+                       {\r
+                               SD_PlaySound(SND_SPINDREDBOUNCE);\r
+                               ob->yspeed = -40;\r
+                       }\r
+               }\r
+       }\r
+       RF_PlaceSprite(&ob->sprite, ob->x, ob->y, ob->shapenum, spritedraw, ob->priority);\r
+}\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                 SHIKADI MASTER\r
+\r
+temp1 = defines next action (0 = shoot, 1 = teleport)\r
+\r
+=============================================================================\r
+*/\r
+\r
+statetype s_master1      = {MASTER1SPR,           MASTER1SPR,           step,      false, false,  8, 0, 0, NULL, C_Master, R_Draw, &s_master2};\r
+statetype s_master2      = {MASTER2SPR,           MASTER2SPR,           step,      false, false,  8, 0, 0, NULL, C_Master, R_Draw, &s_master3};\r
+statetype s_master3      = {MASTER3SPR,           MASTER3SPR,           step,      false, false,  8, 0, 0, NULL, C_Master, R_Draw, &s_master4};\r
+statetype s_master4      = {MASTER4SPR,           MASTER4SPR,           step,      false, false,  8, 0, 0, T_Master, C_Master, R_Draw, &s_master1};\r
+statetype s_mastershoot1 = {SHIKMASTERCASTLSPR,   SHIKMASTERCASTRSPR,   step,      false, false, 30, 0, 0, T_MasterShoot, C_Spindread, R_Draw, &s_mastershoot2};\r
+statetype s_mastershoot2 = {SHIKMASTERCASTLSPR,   SHIKMASTERCASTRSPR,   step,      false, false,  8, 0, 0, NULL, C_Spindread, R_Draw, &s_master1};\r
+statetype s_mastertport1 = {MASTERTELEPORT1SPR,   MASTERTELEPORT1SPR,   step,      false, true,  20, 0, 0, NULL, C_Spindread, R_Draw, &s_mastertport2};\r
+statetype s_mastertport2 = {MASTERTELEPORT2SPR,   MASTERTELEPORT2SPR,   step,      false, true,  20, 0, 0, T_MasterTPort, C_Spindread, R_Draw, &s_mastertport3};\r
+statetype s_mastertport3 = {MASTERTELEPORT2SPR,   MASTERTELEPORT2SPR,   think,     false, false,  0, 0, 0, T_Projectile, NULL, R_Land, &s_mastertport4};\r
+statetype s_mastertport4 = {MASTERTELEPORT2SPR,   MASTERTELEPORT2SPR,   step,      false, false, 60, 0, 0, NULL, C_Spindread, R_Draw, &s_master1};\r
+statetype s_mshot1       = {MASTERSHOT4SPR,       MASTERSHOT1SPR,       stepthink, false, false,  8, 0, 0, T_WeakProjectile, C_MShot, R_MShot, &s_mshot2};\r
+statetype s_mshot2       = {MASTERSHOT3SPR,       MASTERSHOT2SPR,       stepthink, false, false,  8, 0, 0, T_WeakProjectile, C_MShot, R_MShot, &s_mshot3};\r
+statetype s_mshot3       = {MASTERSHOT2SPR,       MASTERSHOT3SPR,       stepthink, false, false,  8, 0, 0, T_WeakProjectile, C_MShot, R_MShot, &s_mshot4};\r
+statetype s_mshot4       = {MASTERSHOT1SPR,       MASTERSHOT4SPR,       stepthink, false, false,  8, 0, 0, T_WeakProjectile, C_MShot, R_MShot, &s_mshot1};\r
+statetype s_mspray1      = {MASTERFLOORSPARK1SPR, MASTERFLOORSPARK1SPR, stepthink, false, false,  6, 0, 0, T_Projectile, C_MShot, R_MSpray, &s_mspray2};\r
+statetype s_mspray2      = {MASTERFLOORSPARK2SPR, MASTERFLOORSPARK2SPR, stepthink, false, false,  6, 0, 0, T_Projectile, C_MShot, R_MSpray, &s_mspray3};\r
+statetype s_mspray3      = {MASTERFLOORSPARK3SPR, MASTERFLOORSPARK3SPR, stepthink, false, false,  6, 0, 0, T_Projectile, C_MShot, R_MSpray, &s_mspray4};\r
+statetype s_mspray4      = {MASTERFLOORSPARK4SPR, MASTERFLOORSPARK4SPR, stepthink, false, false,  6, 0, 0, T_Projectile, C_MShot, R_MSpray, &s_mspray1};\r
+\r
+/*\r
+===========================\r
+=\r
+= SpawnMaster\r
+=\r
+===========================\r
+*/\r
+\r
+void SpawnMaster(Uint16 tileX, Uint16 tileY)\r
+{\r
+       GetNewObj(false);\r
+       new->obclass = shikadimasterobj;\r
+       new->active = ac_yes;\r
+       new->x = CONVERT_TILE_TO_GLOBAL(tileX);\r
+       new->y = CONVERT_TILE_TO_GLOBAL(tileY) + -24*PIXGLOBAL;\r
+       NewState(new, &s_master1);\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= T_Master\r
+=\r
+===========================\r
+*/\r
+\r
+void T_Master(objtype *ob)\r
+{\r
+       Sint16 randval;\r
+\r
+       randval = US_RndT();\r
+       if (randval < 0x40)\r
+       {\r
+               if (ob->temp1)\r
+               {\r
+                       ob->state = &s_mastertport1;\r
+                       ob->temp1 = 0;\r
+               }\r
+               else\r
+               {\r
+                       ob->temp1 = 1;\r
+                       if (player->x > ob->x)\r
+                       {\r
+                               ob->xdir = 1;\r
+                       }\r
+                       else\r
+                       {\r
+                               ob->xdir = -1;\r
+                       }\r
+                       ob->state = &s_mastershoot1;\r
+               }\r
+       }\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= T_MasterShoot\r
+=\r
+===========================\r
+*/\r
+\r
+void T_MasterShoot(objtype *ob)\r
+{\r
+       Uint16 x;\r
+\r
+       if (ob->xdir == 1)\r
+       {\r
+               x = ob->x + 16*PIXGLOBAL;\r
+       }\r
+       else\r
+       {\r
+               x = ob->x;\r
+       }\r
+       if (CheckSpawnShot(x, ob->y+8*PIXGLOBAL, &s_mshot1) != -1)\r
+       {\r
+               new->xspeed = ob->xdir * 48;\r
+               new->yspeed = -16;\r
+               SD_PlaySound(SND_MASTERATTACK);\r
+       }\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= C_Master\r
+=\r
+===========================\r
+*/\r
+\r
+void C_Master(objtype *ob, objtype *hit)\r
+{\r
+       if (hit->obclass == stunshotobj)\r
+       {\r
+               ExplodeShot(hit);\r
+               ob->xdir = (player->x > ob->x)? 1 : -1;\r
+               ob->temp1 = 1;\r
+               ChangeState(ob, &s_mastershoot1);\r
+       }\r
+       else if (hit->obclass == keenobj)\r
+       {\r
+               KillKeen();\r
+       }\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= T_MasterTPort\r
+=\r
+===========================\r
+*/\r
+\r
+void T_MasterTPort(objtype *ob)\r
+{\r
+       Uint16 tile;\r
+       Sint16 tx, ty, redos, oldx, oldy;\r
+\r
+       oldx = ob->x;\r
+       oldy = ob->y;\r
+\r
+       GetNewObj(true);\r
+       new->x = ob->x;\r
+       new->y = ob->y;\r
+       new->xspeed = 48;\r
+       NewState(new, &s_mspray1);      // BUG? new object is not made removable\r
+\r
+       GetNewObj(true);\r
+       new->x = ob->x;\r
+       new->y = ob->y;\r
+       new->xspeed = -48;\r
+       NewState(new, &s_mspray1);      // BUG? new object is not made removable\r
+\r
+       SD_PlaySound(SND_MASTERBLAST);\r
+\r
+       redos = 0;\r
+redo:\r
+       if (++redos == 10)\r
+       {\r
+               US_RndT();      // call it, but ignore the result\r
+               // probably to avoid repeatedly getting the same same "random" values\r
+               // due to having an even number of US_RndT() calls in this routine and\r
+               // an even number of elements in the random table.\r
+\r
+               ob->state = &s_master1;\r
+               ob->x = oldx - 1;\r
+               ob->y = oldy;\r
+               xtry = 1;\r
+               ytry = 0;\r
+       }\r
+       else\r
+       {\r
+               tx = player->tilemidx - 16 + (US_RndT()>>3);\r
+               ty = player->tilebottom - 16 + (US_RndT()>>3);\r
+               if (tx < 2 || ty < 2 || tx > mapwidth-5 || ty > mapheight-5)\r
+                       goto redo;\r
+\r
+\r
+               ob->x = CONVERT_TILE_TO_GLOBAL(tx);\r
+               ob->y = CONVERT_TILE_TO_GLOBAL(ty);\r
+               ob->tileleft = tx-1;\r
+               ob->tileright = tx+4;\r
+               ob->tiletop = ty-1;\r
+               ob->tilebottom = ty+4;\r
+\r
+               {\r
+                       Uint16 x, y;\r
+                       Uint16 far *map;\r
+                       Uint16 mapdelta;\r
+\r
+                       map = (Uint16 far *)mapsegs[1] + mapbwidthtable[ob->tiletop]/2 + ob->tileleft;\r
+                       mapdelta = mapwidth - (ob->tileright - ob->tileleft + 1);\r
+\r
+                       for (y = ob->tiletop; ob->tilebottom >= y; y++, map+=mapdelta)\r
+                       {\r
+                               for (x = ob->tileleft; ob->tileright >= x; x++)\r
+                               {\r
+                                       tile = *map++;\r
+                                       if ((tinf[tile+INTILE] & INTILE_FOREGROUND) || tinf[tile+NORTHWALL] || tinf[tile+EASTWALL]\r
+                                               || tinf[tile+SOUTHWALL] || tinf[tile+WESTWALL])\r
+                                       {\r
+                                               goto redo;\r
+                                       }\r
+                               }\r
+                       }\r
+                       xtry = ytry = 0;\r
+               }\r
+       }\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= C_MShot\r
+=\r
+===========================\r
+*/\r
+\r
+void C_MShot(objtype *ob, objtype *hit)\r
+{\r
+       if (hit->obclass == keenobj)\r
+       {\r
+               KillKeen();\r
+               RemoveObj(ob);\r
+       }\r
+       else if (hit->obclass == stunshotobj)\r
+       {\r
+               ExplodeShot(hit);\r
+               RemoveObj(ob);\r
+       }\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= R_MShot\r
+=\r
+===========================\r
+*/\r
+\r
+void R_MShot(objtype *ob)\r
+{\r
+       if (ob->hiteast || ob->hitwest)\r
+       {\r
+               ob->xspeed = -ob->xspeed;\r
+       }\r
+       if (ob->hitsouth)\r
+       {\r
+               ob->yspeed = 0;\r
+       }\r
+       if (ob->hitnorth)\r
+       {\r
+               SD_PlaySound(SND_MASTERATTACK);\r
+               ob->xspeed = 48;\r
+               ChangeState(ob, &s_mspray1);\r
+\r
+               GetNewObj(true);\r
+               new->x = ob->x;\r
+               new->y = ob->y;\r
+               new->xspeed = -48;\r
+               NewState(new, &s_mspray1);      // BUG? new object is not made removable\r
+       }\r
+       RF_PlaceSprite(&ob->sprite, ob->x, ob->y, ob->shapenum, spritedraw, ob->priority);\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= R_MSpray\r
+=\r
+===========================\r
+*/\r
+\r
+void R_MSpray(objtype *ob)\r
+{\r
+       if (ob->hiteast || ob->hitwest)\r
+       {\r
+               RemoveObj(ob);\r
+       }\r
+       else\r
+       {\r
+               RF_PlaceSprite(&ob->sprite, ob->x, ob->y, ob->shapenum, spritedraw, ob->priority);\r
+       }\r
+}\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                 SHIKADI\r
+\r
+temp1 = x tile position of the pole being grabbed (is set but never used)\r
+temp2 = health\r
+temp3 = flash countdown\r
+\r
+=============================================================================\r
+*/\r
+\r
+statetype s_shikadi1     = {SHIKADI1SPR,      SHIKADI1SPR,      step,  false, true,  8,   0, 0, NULL, C_Shikadi, R_Shikadi, &s_shikadi2};\r
+statetype s_shikadi2     = {SHIKADI2SPR,      SHIKADI2SPR,      step,  false, true,  8,   0, 0, NULL, C_Shikadi, R_Shikadi, &s_shikadi3};\r
+statetype s_shikadi3     = {SHIKADI3SPR,      SHIKADI3SPR,      step,  false, true,  8,   0, 0, NULL, C_Shikadi, R_Shikadi, &s_shikadi4};\r
+statetype s_shikadi4     = {SHIKADI4SPR,      SHIKADI4SPR,      step,  false, true,  8,   0, 0, NULL, C_Shikadi, R_Shikadi, &s_shikadiwalk1};\r
+statetype s_shikadiwalk1 = {SHIKADIWALKL1SPR, SHIKADIWALKR1SPR, step,  false, true,  8, 128, 0, T_Shikadi, C_Shikadi, R_Shikadi, &s_shikadiwalk2};\r
+statetype s_shikadiwalk2 = {SHIKADIWALKL2SPR, SHIKADIWALKR2SPR, step,  false, true,  8, 128, 0, T_Shikadi, C_Shikadi, R_Shikadi, &s_shikadiwalk3};\r
+statetype s_shikadiwalk3 = {SHIKADIWALKL3SPR, SHIKADIWALKR3SPR, step,  false, true,  8, 128, 0, T_Shikadi, C_Shikadi, R_Shikadi, &s_shikadiwalk4};\r
+statetype s_shikadiwalk4 = {SHIKADIWALKL4SPR, SHIKADIWALKR4SPR, step,  false, true,  8, 128, 0, T_Shikadi, C_Shikadi, R_Shikadi, &s_shikadiwalk1};\r
+statetype s_shikadigrab  = {SHIKADIGRABLSPR,  SHIKADIGRABRSPR,  step,  false, true, 20,   0, 0, T_PoleShock, C_Shikadi, R_Shikadi, &s_shikadigrab2};\r
+statetype s_shikadigrab2 = {SHIKADIGRABLSPR,  SHIKADIGRABRSPR,  step,  false, true, 20,   0, 0, NULL, C_Shikadi, R_Shikadi, &s_shikadiwalk1};\r
+statetype s_shikadistun  = {SHIKADISTUNSPR,   SHIKADISTUNSPR,   think, false, false, 0,   0, 0, T_Projectile, NULL, R_Stunned, NULL};\r
+\r
+statetype s_polespark1   = {SHIKADIPOLESPARK1SPR, SHIKADIPOLESPARK1SPR, stepthink, false, false,  0,   0, 0, T_PoleSpark, C_Lethal, R_Draw, &s_polespark2};\r
+statetype s_polespark2   = {SHIKADIPOLESPARK1SPR, SHIKADIPOLESPARK1SPR, stepthink, false, false,  0,   0, 0, T_PoleSpark, C_Lethal, R_Draw, &s_polespark1};\r
+\r
+/*\r
+===========================\r
+=\r
+= SpawnShikadi\r
+=\r
+===========================\r
+*/\r
+\r
+void SpawnShikadi(Uint16 tileX, Uint16 tileY)\r
+{\r
+       GetNewObj(false);\r
+       new->obclass = shikadiobj;\r
+       new->active = ac_yes;\r
+       new->x = CONVERT_TILE_TO_GLOBAL(tileX);\r
+       new->y = CONVERT_TILE_TO_GLOBAL(tileY) + -1*TILEGLOBAL;\r
+       new->temp2 = 4; // health\r
+       if (US_RndT() < 0x80)\r
+       {\r
+               new->xdir = 1;\r
+       }\r
+       else\r
+       {\r
+               new->xdir = -1;\r
+       }\r
+       NewState(new, &s_shikadi1);\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= T_Shikadi\r
+=\r
+===========================\r
+*/\r
+\r
+void T_Shikadi(objtype *ob)\r
+{\r
+       Uint16 tx, tile;\r
+\r
+       if (player->state->contact == &KeenPosContact\r
+               || ob->bottom - player->bottom + TILEGLOBAL <= 2*TILEGLOBAL)\r
+       {\r
+               if (ob->x > player->x + TILEGLOBAL)\r
+               {\r
+                       ob->xdir = -1;\r
+               }\r
+               else if (ob->x < player->x - TILEGLOBAL)\r
+               {\r
+                       ob->xdir = 1;\r
+               }\r
+               if (ob->xdir == 1)\r
+               {\r
+                       tx = ob->tileright;\r
+               }\r
+               else\r
+               {\r
+                       tx = ob->tileleft;\r
+               }\r
+\r
+               if (player->tilemidx != tx)\r
+                       return;\r
+       }\r
+       else\r
+       {\r
+               if (US_RndT() < 0x10)\r
+               {\r
+                       xtry = 0;\r
+                       ob->state = &s_shikadi1;\r
+                       return;\r
+               }\r
+               if ((ob->x & 0xFF) || !OnScreen(ob))\r
+                       return;\r
+\r
+               if (ob->xdir == 1)\r
+               {\r
+                       tx = ob->tileright;\r
+               }\r
+               else\r
+               {\r
+                       tx = ob->tileleft;\r
+               }\r
+       }\r
+\r
+       tile = *(mapsegs[1]+mapbwidthtable[ob->tiletop]/2 + tx);\r
+       if (tinf[tile+INTILE] == INTILE_POLE)\r
+       {\r
+               ob->temp1 = tx;\r
+               ob->state = &s_shikadigrab;\r
+               xtry = 0;\r
+               SD_PlaySound(SND_SHIKADIATTACK);\r
+       }\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= C_Shikadi\r
+=\r
+===========================\r
+*/\r
+\r
+void C_Shikadi(objtype *ob, objtype *hit)\r
+{\r
+       if (hit->obclass == keenobj)\r
+       {\r
+               KillKeen();\r
+       }\r
+       if (hit->obclass == stunshotobj)\r
+       {\r
+               if (--ob->temp2 == 0)   // handle health\r
+               {\r
+                       ob->xspeed = 0;\r
+                       ob->yspeed = 0;\r
+                       StunObj(ob, hit, &s_shikadistun);\r
+               }\r
+               else\r
+               {\r
+                       ob->temp3 = 2;  // draw white two times\r
+                       ob->needtoreact = true;\r
+                       ExplodeShot(hit);\r
+               }\r
+       }\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= T_PoleShock\r
+=\r
+===========================\r
+*/\r
+\r
+void T_PoleShock(objtype *ob)\r
+{\r
+       Uint16 x;\r
+\r
+       ob->nothink = 2;\r
+       if (ob->xdir == 1)\r
+       {\r
+               x = CONVERT_TILE_TO_GLOBAL(ob->tileright);\r
+       }\r
+       else\r
+       {\r
+               x = CONVERT_TILE_TO_GLOBAL(ob->tileleft);\r
+       }\r
+\r
+       GetNewObj(true);\r
+       new->x = x;\r
+       new->y = ob->y + 8*PIXGLOBAL;\r
+       new->obclass = mshotobj;\r
+       new->active = ac_removable;\r
+       new->needtoclip = cl_noclip;\r
+       NewState(new, &s_polespark1);\r
+       if (ob->y < player->y)\r
+       {\r
+               new->ydir = 1;\r
+       }\r
+       else\r
+       {\r
+               new->ydir = -1;\r
+       }\r
+       SD_PlaySound(SND_SHIKADIATTACK);\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= T_PoleSpark\r
+=\r
+===========================\r
+*/\r
+\r
+void T_PoleSpark(objtype *ob)\r
+{\r
+       Uint16 tile;\r
+\r
+       if (ytry == 0)\r
+       {\r
+               ytry = ob->ydir * 48;\r
+               tile = *(mapsegs[1]+mapbwidthtable[ob->tiletop]/2 + ob->tilemidx);\r
+               if (tinf[tile+INTILE] != INTILE_POLE)\r
+               {\r
+                       ob->state = NULL;\r
+               }\r
+       }\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= R_Shikadi\r
+=\r
+===========================\r
+*/\r
+\r
+void R_Shikadi(objtype *ob)\r
+{\r
+       if (ob->xdir == 1 && ob->hitwest)\r
+       {\r
+               ob->x -= ob->xmove;\r
+               ob->xdir = -1;\r
+               ob->nothink = US_RndT() >> 5;\r
+               ChangeState(ob, ob->state);\r
+       }\r
+       else if (ob->xdir == -1 && ob->hiteast)\r
+       {\r
+               ob->x -= ob->xmove;\r
+               ob->xdir = 1;\r
+               ob->nothink = US_RndT() >> 5;\r
+               ChangeState(ob, ob->state);\r
+       }\r
+       else if (!ob->hitnorth)\r
+       {\r
+               ob->x -= ob->xmove;\r
+               ob->xdir = -ob->xdir;\r
+               ob->nothink = US_RndT() >> 5;\r
+               ChangeState(ob, ob->state);\r
+       }\r
+       if (ob->temp3)\r
+       {\r
+               ob->temp3--;\r
+               RF_PlaceSprite(&ob->sprite, ob->x, ob->y, ob->shapenum, maskdraw, ob->priority);\r
+       }\r
+       else\r
+       {\r
+               RF_PlaceSprite(&ob->sprite, ob->x, ob->y, ob->shapenum, spritedraw, ob->priority);\r
+       }\r
+}\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                 PET (a.k.a. SHOCKSHUND)\r
+\r
+temp1 = countdown for sit animation\r
+temp2 = health\r
+temp3 = flash countdown\r
+\r
+=============================================================================\r
+*/\r
+\r
+statetype s_petsit1   = {PETSIT1SPR,      PETSIT1SPR,      step,      false, true,   8,   0, 0, NULL, C_Pet, R_Pet, &s_petsit2};\r
+statetype s_petsit2   = {PETSIT2SPR,      PETSIT2SPR,      step,      false, true,   8,   0, 0, T_PetSit, C_Pet, R_Pet, &s_petsit1};\r
+statetype s_petbark1  = {PETBARKL1SPR,    PETBARKR1SPR,    step,      false, true,   8,   0, 0, NULL, C_Pet, R_Pet, &s_petbark2};\r
+statetype s_petbark2  = {PETBARKL2SPR,    PETBARKR2SPR,    step,      false, true,   8,   0, 0, T_PetBark, C_Pet, R_Pet, &s_pet1};\r
+statetype s_pet1      = {PETRUNL1SPR,     PETRUNR1SPR,     step,      false, true,   8, 128, 0, NULL, C_Pet, R_Pet, &s_pet2};\r
+statetype s_pet2      = {PETRUNL2SPR,     PETRUNR2SPR,     step,      false, true,   8, 128, 0, NULL, C_Pet, R_Pet, &s_pet3};\r
+statetype s_pet3      = {PETRUNL3SPR,     PETRUNR3SPR,     step,      false, true,   8, 128, 0, NULL, C_Pet, R_Pet, &s_pet4};\r
+statetype s_pet4      = {PETRUNL4SPR,     PETRUNR4SPR,     step,      false, true,   8, 128, 0, T_Pet, C_Pet, R_Pet, &s_pet1};\r
+statetype s_petjump   = {PETJUMPLSPR,     PETJUMPRSPR,     think,     false, false,  8, 128, 0, T_Projectile, C_Pet, R_PetJump, &s_pet2};\r
+statetype s_pshot1    = {PETSPARK1SPR,    PETSPARK1SPR,    stepthink, false, false,  8,   0, 0, T_Velocity, C_PShot, R_PShot, &s_pshot2};\r
+statetype s_pshot2    = {PETSPARK2SPR,    PETSPARK2SPR,    stepthink, false, false,  8,   0, 0, T_Velocity, C_PShot, R_PShot, &s_pshot1};\r
+statetype s_pshothot1 = {PETSPARKHIT1SPR, PETSPARKHIT1SPR, step,      false, false, 10,   0, 0, NULL, NULL, R_Draw, &s_pshothot2};\r
+statetype s_pshothot2 = {PETSPARKHIT2SPR, PETSPARKHIT2SPR, step,      false, false, 10,   0, 0, NULL, NULL, R_Draw, NULL};\r
+statetype s_petstun   = {PETSTUNSPR,      PETSTUNSPR,      think,     false, false,  0,   0, 0, T_Projectile, NULL, R_Stunned, NULL};\r
+\r
+/*\r
+===========================\r
+=\r
+= SpawnPet\r
+=\r
+===========================\r
+*/\r
+\r
+void SpawnPet(Uint16 tileX, Uint16 tileY)\r
+{\r
+       GetNewObj(false);\r
+       new->obclass = petobj;\r
+       new->active = ac_yes;\r
+       new->x = CONVERT_TILE_TO_GLOBAL(tileX);\r
+       new->y = CONVERT_TILE_TO_GLOBAL(tileY) + -8*PIXGLOBAL;\r
+       new->temp2 = 2; // health\r
+       new->xdir = new->ydir = 1;\r
+       NewState(new, &s_pet1);\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= T_Pet\r
+=\r
+===========================\r
+*/\r
+\r
+void T_Pet(objtype *ob)\r
+{\r
+       if (ob->x > player->x)\r
+       {\r
+               ob->xdir = -1;\r
+       }\r
+       else\r
+       {\r
+               ob->xdir = 1;\r
+       }\r
+       if (US_RndT() < 0x10)\r
+       {\r
+               xtry = 0;\r
+               ob->state = &s_petsit1;\r
+               ob->temp1 = 10; // repeat animation 10 times;\r
+       }\r
+       else\r
+       {\r
+               if (ob->bottom != player->bottom)\r
+               {\r
+                       ob->state = &s_petjump;\r
+                       if (ob->xdir == 1)\r
+                       {\r
+                               ob->xspeed = 40;\r
+                       }\r
+                       else\r
+                       {\r
+                               ob->xspeed = -40;\r
+                       }\r
+                       ob->yspeed = -40;\r
+               }\r
+               if (US_RndT() < 0x80)\r
+               {\r
+                       xtry = 0;\r
+                       ob->state = &s_petbark1;\r
+               }\r
+       }\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= T_PetSit\r
+=\r
+===========================\r
+*/\r
+\r
+void T_PetSit(objtype *ob)\r
+{\r
+       if (--ob->temp1 == 0)\r
+       {\r
+               ob->state = &s_pet1;\r
+       }\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= T_PetBark\r
+=\r
+===========================\r
+*/\r
+\r
+void T_PetBark(objtype *ob)\r
+{\r
+       Uint16 x;\r
+\r
+       if (ob->xdir == 1)\r
+       {\r
+               x = ob->x + 7*PIXGLOBAL;\r
+       }\r
+       else\r
+       {\r
+               x = ob->x;\r
+       }\r
+       if (CheckSpawnShot(x, ob->y+4*PIXGLOBAL, &s_pshot1) != -1)\r
+       {\r
+               new->xspeed = ob->xdir * 60;\r
+               new->xdir = ob->xdir;\r
+               SD_PlaySound(SND_SHOCKSHUNDBARK);\r
+       }\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= C_Pet\r
+=\r
+===========================\r
+*/\r
+\r
+void C_Pet(objtype *ob, objtype *hit)\r
+{\r
+       if (hit->obclass == keenobj)\r
+       {\r
+               KillKeen();\r
+       }\r
+       if (hit->obclass == stunshotobj)        // no 'else if' in the original code!\r
+       {\r
+               if (--ob->temp2 == 0)   // handle health\r
+               {\r
+                       ob->xspeed = 0;\r
+                       ob->yspeed = 0;\r
+                       StunObj(ob, hit, &s_petstun);\r
+               }\r
+               else\r
+               {\r
+                       ob->temp3 = 2;  // draw white two times\r
+                       ob->needtoreact = true;\r
+                       ExplodeShot(hit);\r
+               }\r
+       }\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= R_Pet\r
+=\r
+===========================\r
+*/\r
+\r
+void R_Pet(objtype *ob)\r
+{\r
+       if (ob->xdir == 1 && ob->hitwest)\r
+       {\r
+               ob->x -= ob->xmove;\r
+               ob->xdir = -1;\r
+               ob->nothink = US_RndT() >> 5;\r
+               ChangeState(ob, ob->state);\r
+       }\r
+       else if (ob->xdir == -1 && ob->hiteast)\r
+       {\r
+               ob->x -= ob->xmove;\r
+               ob->xdir = 1;\r
+               ob->nothink = US_RndT() >> 5;\r
+               ChangeState(ob, ob->state);\r
+       }\r
+       else if (!ob->hitnorth)\r
+       {\r
+               if ((ob->xdir == 1 && player->x > ob->x) || (ob->xdir == -1 && player->x < ob->x))\r
+               {\r
+                       ChangeState(ob, &s_petjump);\r
+                       if (ob->xdir == 1)\r
+                       {\r
+                               ob->xspeed = 40;\r
+                       }\r
+                       else\r
+                       {\r
+                               ob->xspeed = -40;\r
+                       }\r
+                       ob->yspeed = -40;\r
+               }\r
+               else\r
+               {\r
+                       ob->x -= ob->xmove*2;\r
+                       ob->xdir = -ob->xdir;\r
+                       ob->nothink = US_RndT() >> 5;\r
+                       ChangeState(ob, ob->state);\r
+               }\r
+       }\r
+       if (ob->temp3)\r
+       {\r
+               ob->temp3--;\r
+               RF_PlaceSprite(&ob->sprite, ob->x, ob->y, ob->shapenum, maskdraw, ob->priority);\r
+       }\r
+       else\r
+       {\r
+               RF_PlaceSprite(&ob->sprite, ob->x, ob->y, ob->shapenum, spritedraw, ob->priority);\r
+       }\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= R_PetJump\r
+=\r
+===========================\r
+*/\r
+\r
+void R_PetJump(objtype *ob)\r
+{\r
+       if (ob->hitnorth)\r
+       {\r
+               ob->nothink = US_RndT() >> 5;\r
+               ChangeState(ob, &s_pet1);\r
+       }\r
+       if (ob->temp3)\r
+       {\r
+               ob->temp3--;\r
+               RF_PlaceSprite(&ob->sprite, ob->x, ob->y, ob->shapenum, maskdraw, ob->priority);\r
+       }\r
+       else\r
+       {\r
+               RF_PlaceSprite(&ob->sprite, ob->x, ob->y, ob->shapenum, spritedraw, ob->priority);\r
+       }\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= C_PShot\r
+=\r
+===========================\r
+*/\r
+\r
+void C_PShot(objtype *ob, objtype *hit)\r
+{\r
+       if (hit->obclass == keenobj)\r
+       {\r
+               KillKeen();\r
+               ChangeState(ob, &s_pshothot1);\r
+       }\r
+       else if (hit->obclass == stunshotobj)\r
+       {\r
+               ExplodeShot(hit);\r
+               ChangeState(ob, &s_pshothot1);\r
+       }\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= R_PShot\r
+=\r
+===========================\r
+*/\r
+\r
+void R_PShot(objtype *ob)\r
+{\r
+       if (ob->hitnorth || ob->hiteast || ob->hitsouth || ob->hitwest)\r
+       {\r
+               SD_PlaySound(SND_SHOCKBALLEXPLODE);\r
+               ChangeState(ob, &s_pshothot1);\r
+       }\r
+       RF_PlaceSprite(&ob->sprite, ob->x, ob->y, ob->shapenum, spritedraw, ob->priority);\r
+}\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                 SPHEREFUL\r
+\r
+temp1 ... temp4 = sprite pointers for the guards circling around the sphere\r
+\r
+=============================================================================\r
+*/\r
+\r
+statetype s_sphereful1    = {SPHEREFUL1SPR, SPHEREFUL1SPR, stepthink, false, false, 6, 0, 0, T_Sphereful, C_Spindread, R_Sphereful, &s_sphereful2};\r
+statetype s_sphereful2    = {SPHEREFUL2SPR, SPHEREFUL2SPR, stepthink, false, false, 6, 0, 0, T_Sphereful, C_Spindread, R_Sphereful, &s_sphereful3};\r
+statetype s_sphereful3    = {SPHEREFUL3SPR, SPHEREFUL3SPR, stepthink, false, false, 6, 0, 0, T_Sphereful, C_Spindread, R_Sphereful, &s_sphereful4};\r
+statetype s_sphereful4    = {SPHEREFUL4SPR, SPHEREFUL4SPR, stepthink, false, false, 6, 0, 0, T_Sphereful, C_Spindread, R_Sphereful, &s_sphereful1};\r
+\r
+/*\r
+===========================\r
+=\r
+= SpawnSphereful\r
+=\r
+===========================\r
+*/\r
+\r
+void SpawnSphereful(Uint16 tileX, Uint16 tileY)\r
+{\r
+       GetNewObj(false);\r
+       new->obclass = spherefulobj;\r
+       new->needtoclip = cl_fullclip;\r
+       new->active = ac_yes;\r
+       new->x = CONVERT_TILE_TO_GLOBAL(tileX);\r
+       new->y = CONVERT_TILE_TO_GLOBAL(tileY) + -1*TILEGLOBAL;\r
+       new->priority = 1;\r
+       NewState(new, &s_sphereful1);\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= T_Sphereful\r
+=\r
+===========================\r
+*/\r
+\r
+void T_Sphereful(objtype *ob)\r
+{\r
+       Sint32 i;\r
+       ob->needtoreact = true;\r
+\r
+       //\r
+       // this code could be executed twice during the same frame because the\r
+       // object's animation/state changed during that frame, so don't update\r
+       // anything if we already have movement for the current frame i.e. the\r
+       // update code has already been executed this frame\r
+       //\r
+       if (xtry == 0 && ytry == 0)\r
+       {\r
+               for (i=lasttimecount-tics; i<lasttimecount; i++)\r
+               {\r
+                       if (!(i & 0xF))\r
+                       {\r
+                               if (ob->yspeed < 8)\r
+                                       ob->yspeed++;\r
+\r
+                               if (ob->x < player->x && ob->xspeed < 8)\r
+                                       ob->xspeed++;\r
+\r
+                               if (ob->x > player->x && ob->xspeed > -8)\r
+                                       ob->xspeed--;\r
+                       }\r
+                       ytry += ob->yspeed;\r
+                       xtry += ob->xspeed;\r
+               }\r
+       }\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= R_Sphereful\r
+=\r
+===========================\r
+*/\r
+\r
+void R_Sphereful(objtype *ob)\r
+{\r
+       static Uint16 circle[16] = {\r
+               384, 369, 328, 265, 192, 119,  58,  15,\r
+                 0,  15,  58, 119, 192, 265, 328, 369\r
+       };\r
+\r
+       Uint16 index, shapenum, priority;\r
+\r
+       if (ob->hitwest || ob->hiteast)\r
+       {\r
+               ob->xspeed = -ob->xspeed;\r
+               SD_PlaySound(SND_SPHEREFULBOUNCE);\r
+       }\r
+       if (ob->hitsouth)\r
+       {\r
+               ob->yspeed = -ob->yspeed;\r
+               SD_PlaySound(SND_SPHEREFULBOUNCE);\r
+       }\r
+\r
+       if (ob->hitnorth)\r
+       {\r
+               ob->yspeed = -ob->yspeed;\r
+               if (player->y < ob->y)\r
+               {\r
+                       ob->yspeed--;\r
+               }\r
+               else\r
+               {\r
+                       ob->yspeed++;\r
+               }\r
+               if (ob->yspeed > -4)\r
+               {\r
+                       ob->yspeed = -4;\r
+               }\r
+               else if (ob->yspeed < -12)\r
+               {\r
+                       ob->yspeed = -12;\r
+               }\r
+               SD_PlaySound(SND_BOUNCE);\r
+       }\r
+       RF_PlaceSprite(&ob->sprite, ob->x, ob->y, ob->shapenum, spritedraw, ob->priority);\r
+\r
+       index = ((Uint16)lasttimecount / 8) & 0xF;\r
+       shapenum = index / 4 + SPHEREGUARD1SPR;\r
+       if (index >= 8)\r
+       {\r
+               priority = 2;\r
+       }\r
+       else\r
+       {\r
+               priority = 0;\r
+       }\r
+       RF_PlaceSprite((void**)&ob->temp1, ob->x+circle[index], ob->y+circle[index], shapenum, spritedraw, priority);\r
+       RF_PlaceSprite((void**)&ob->temp2, ob->x+24*PIXGLOBAL-circle[index], ob->y+circle[index], shapenum, spritedraw, priority);\r
+\r
+       index += 8;\r
+       index &= 0xF;\r
+       if (index >= 8)\r
+       {\r
+               priority = 2;\r
+       }\r
+       else\r
+       {\r
+               priority = 0;\r
+       }\r
+       RF_PlaceSprite((void**)&ob->temp3, ob->x+circle[index], ob->y+circle[index], shapenum, spritedraw, priority);\r
+       RF_PlaceSprite((void**)&ob->temp4, ob->x+24*PIXGLOBAL-circle[index], ob->y+circle[index], shapenum, spritedraw, priority);\r
+}\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                 SCOTTIE\r
+\r
+=============================================================================\r
+*/\r
+\r
+statetype s_scottie1    = {SCOTTIEWALKL1SPR, SCOTTIEWALKR1SPR, step,  false, true,  8, 128, 0, T_Scottie, C_Scottie, R_Walk, &s_scottie2};\r
+statetype s_scottie2    = {SCOTTIEWALKL2SPR, SCOTTIEWALKR2SPR, step,  false, true,  8, 128, 0, T_Scottie, C_Scottie, R_Walk, &s_scottie3};\r
+statetype s_scottie3    = {SCOTTIEWALKL3SPR, SCOTTIEWALKR3SPR, step,  false, true,  8, 128, 0, T_Scottie, C_Scottie, R_Walk, &s_scottie4};\r
+statetype s_scottie4    = {SCOTTIEWALKL4SPR, SCOTTIEWALKR4SPR, step,  false, true,  8, 128, 0, T_Scottie, C_Scottie, R_Walk, &s_scottie1};\r
+statetype s_scottieface = {SCOTTIEFACESPR,   SCOTTIEFACESPR,   step,  false, true, 30,   0, 0, NULL, C_Scottie, R_Walk, &s_scottie1};\r
+statetype s_scottiestun = {SCOTTIESTUNSPR,   SCOTTIESTUNSPR,   think, false, false, 0,   0, 0, T_Projectile, NULL, R_Stunned, NULL};\r
+\r
+/*\r
+===========================\r
+=\r
+= SpawnScottie\r
+=\r
+===========================\r
+*/\r
+\r
+void SpawnScottie(Uint16 tileX, Uint16 tileY)\r
+{\r
+       GetNewObj(false);\r
+       new->obclass = scottieobj;\r
+       new->active = ac_yes;\r
+       new->x = CONVERT_TILE_TO_GLOBAL(tileX);\r
+       new->y = CONVERT_TILE_TO_GLOBAL(tileY) + -8*PIXGLOBAL;\r
+       if (US_RndT() < 0x80)\r
+       {\r
+               new->xdir = 1;\r
+       }\r
+       else\r
+       {\r
+               new->xdir = -1;\r
+       }\r
+       NewState(new, &s_scottie1);\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= T_Scottie\r
+=\r
+===========================\r
+*/\r
+\r
+void T_Scottie(objtype *ob)\r
+{\r
+       if (US_RndT() < 0x10)\r
+       {\r
+               xtry = 0;\r
+               if (US_RndT() < 0x80)\r
+               {\r
+                       ob->xdir = 1;\r
+               }\r
+               else\r
+               {\r
+                       ob->xdir = -1;\r
+               }\r
+               ob->state = &s_scottieface;\r
+       }\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= C_Scottie\r
+=\r
+===========================\r
+*/\r
+\r
+void C_Scottie(objtype *ob, objtype *hit)\r
+{\r
+       if (hit->obclass == keenobj && hit->state->contact)\r
+       {\r
+               ClipToSpriteSide(hit, ob);\r
+       }\r
+       else if (hit->obclass == stunshotobj)\r
+       {\r
+               StunObj(ob, hit, &s_scottiestun);\r
+       }\r
+}\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                 QED\r
+\r
+=============================================================================\r
+*/\r
+\r
+statetype s_qed = {-1, -1, step, false, true, 8, 128, 0, NULL, NULL, NULL, &s_qed};\r
+\r
+/*\r
+===========================\r
+=\r
+= SpawnQed\r
+=\r
+===========================\r
+*/\r
+\r
+void SpawnQed(Uint16 tileX, Uint16 tileY)\r
+{\r
+       GetNewObj(false);\r
+       new->obclass = qedobj;\r
+       new->active = ac_yes;\r
+       new->tileleft = tileX;\r
+       new->tiletop = tileY;\r
+       new->tileright = new->tileleft + 1;\r
+       new->tilebottom = new->tiletop + 1;\r
+       new->x = new->left = CONVERT_TILE_TO_GLOBAL(tileX) + -1*PIXGLOBAL;\r
+       new->y = new->top = CONVERT_TILE_TO_GLOBAL(tileY) + -1*PIXGLOBAL;\r
+       new->right = new->left + 34*PIXGLOBAL;\r
+       new->bottom = new->top + 34*PIXGLOBAL;\r
+       NewState(new, &s_qed);\r
+}
\ No newline at end of file
diff --git a/16/keen456/KEEN4-6/KEEN5/K5_DEF.H b/16/keen456/KEEN4-6/KEEN5/K5_DEF.H
new file mode 100755 (executable)
index 0000000..b6fc078
--- /dev/null
@@ -0,0 +1,485 @@
+/* Reconstructed Commander Keen 4-6 Source Code\r
+ * Copyright (C) 2021 K1n9_Duk3\r
+ *\r
+ * This file is loosely based on:\r
+ * Keen Dreams Source Code\r
+ * Copyright (C) 2014 Javier M. Chavez\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License along\r
+ * with this program; if not, write to the Free Software Foundation, Inc.,\r
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+ */\r
+\r
+#ifndef __K5_DEF__\r
+#define __K5_DEF__\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                               GLOBAL CONSTANTS\r
+\r
+=============================================================================\r
+*/\r
+\r
+#if GRMODE == CGAGR\r
+#define MINMEMORY 255000l\r
+#else\r
+#define MINMEMORY 310000l\r
+#endif\r
+\r
+#define STARPALETTE   {0, 1, 24, 30, 31, 28, 6, 7, 19, 19, 19, 19, 19, 19, 19, 19, 0}\r
+#define INTROPALETTE  {0, 4, 4, 28, 1, 1, 1, 1, 17, 17, 17, 17, 19, 19, 19, 19, 0}\r
+#define SHRINKPALETTE {0, 4, 4, 28, 1, 1, 1, 1, 17, 17, 17, 17, 19, 19, 19,  4, 0}\r
+\r
+#define HIGHSCORE_LEFT 40\r
+#define HIGHSCORE_TOP  35\r
+#define HIGHSCORE_RIGHT        280\r
+#define HIGHSCORE_MAP  15\r
+\r
+#define STATUS_PRESSKEY_X 120\r
+\r
+#define WORLDMAPNAME   "Armageddon"\r
+#define DROPSNAME      "VITALIN"\r
+\r
+#define STARWARSMUSIC  17\r
+#define ENDINGMUSIC    14\r
+\r
+// levels in this range can NOT be re-entered (BWB level should be > MAXDONELEVEL)\r
+#define MINDONELEVEL 1\r
+#define MAXDONELEVEL 17\r
+\r
+#define INACTIVATEDIST 6\r
+\r
+//\r
+// tiles for worldmap teleporters\r
+//\r
+#define TELEPORTERTILE1 2687   // tile animation for teleporting out\r
+#define TELEPORTERTILE2 1063   // tile after teleporting out\r
+#define TELEPORTERTILE3 TELEPORTERTILE1        // tile animation for teleporting in\r
+#define TELEPORTERTILE4 0      // tile after teleporting in\r
+\r
+#define TELEPORERTILEMASK 1    // animation has 2 frames\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                               K5_SPEC DEFINITIONS\r
+\r
+=============================================================================\r
+*/\r
+\r
+extern char far swtext[];\r
+extern char far *levelnames[GAMELEVELS];\r
+extern char far *levelenter[GAMELEVELS];\r
+\r
+void OpenMapDoor(Sint16 tileX, Sint16 tileY);\r
+void CloseMapDoor(Sint16 tileX, Sint16 tileY);\r
+void ScanInfoPlane(void);\r
+void GameOver(void);\r
+\r
+void FinishedFuse(void);\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                               K5_ACT1 DEFINITIONS\r
+\r
+=============================================================================\r
+*/\r
+\r
+extern Sint16 pdirx[];\r
+extern Sint16 pdiry[];\r
+\r
+Sint16 CheckSpawnShot(Uint16 x, Uint16 y, statetype *state);\r
+void C_ClipSide(objtype *ob, objtype *hit);\r
+void C_ClipTop(objtype *ob, objtype *hit);\r
+void R_Land(objtype *ob);\r
+void R_Bounce(objtype *ob);\r
+\r
+extern statetype s_bonus1;\r
+extern statetype s_bonus2;\r
+extern statetype s_bonusfly1;\r
+extern statetype s_bonusfly2;\r
+extern statetype s_bonusrise;\r
+extern statetype s_splash1;\r
+extern statetype s_splash2;\r
+extern statetype s_splash3;\r
+extern statetype s_splash4;\r
+\r
+extern Uint16 bonusshape[];\r
+\r
+void SpawnBonus(Uint16 tileX, Uint16 tileY, Uint16 type);\r
+void SpawnSplash(Uint16 tileX, Uint16 tileY);\r
+void T_Bonus(objtype *ob);\r
+void T_FlyBonus(objtype *ob);\r
+\r
+extern statetype s_teleport1;\r
+extern statetype s_teleport2;\r
+extern statetype s_teleportzap1;\r
+extern statetype s_teleportzap2;\r
+\r
+void SpawnTeleport(void);\r
+\r
+extern statetype s_fuseflash1;\r
+extern statetype s_fuseflash2;\r
+extern statetype s_fuseflash3;\r
+\r
+void SpawnFuseFlash(Uint16 tileX, Uint16 tileY);\r
+\r
+extern statetype s_deadmachine;\r
+\r
+void SpawnDeadMachine(void);\r
+void T_DeadMachine(objtype *ob);\r
+\r
+extern statetype s_platform;\r
+extern statetype s_slotplat1;\r
+extern statetype s_slotplat2;\r
+\r
+void SpawnPlatform(Uint16 tileX, Uint16 tileY, Sint16 dir, Sint16 type);\r
+void T_Platform(objtype *ob);\r
+void T_Slotplat(objtype *ob);\r
+\r
+extern statetype s_dropplatsit;\r
+extern statetype s_dropplatfall;\r
+extern statetype s_dropplatrise;\r
+\r
+void SpawnDropPlat(Uint16 tileX, Uint16 tileY);\r
+void T_DropPlatSit(objtype *ob);\r
+void T_DropPlatFall(objtype *ob);\r
+void T_DropPlatRise(objtype *ob);\r
+\r
+extern statetype s_statplat;\r
+\r
+void SpawnStaticPlat(Uint16 tileX, Uint16 tileY);\r
+\r
+extern statetype s_goplat;\r
+extern statetype s_slotgoplat1;\r
+extern statetype s_slotgoplat2;\r
+\r
+void SpawnGoPlat(Uint16 tileX, Uint16 tileY, Sint16 dir, Sint16 type);\r
+void T_GoPlat(objtype *ob);\r
+void T_GoSlotPlat(objtype *ob);\r
+\r
+extern statetype s_volte1;\r
+extern statetype s_volte2;\r
+extern statetype s_volte3;\r
+extern statetype s_volte4;\r
+extern statetype s_voltestun;\r
+\r
+void SpawnVolte(Uint16 tileX, Uint16 tileY);\r
+void T_Volte(objtype *ob);\r
+void C_Volte(objtype *ob, objtype *hit);\r
+\r
+extern statetype s_sneakplatsit;\r
+extern statetype s_sneakplatdodge;\r
+extern statetype s_sneakplatreturn;\r
+\r
+void SpawnSneakPlat(Uint16 tileX, Uint16 tileY);\r
+void T_SneakPlat(objtype *ob);\r
+\r
+extern statetype s_cannon;\r
+extern statetype s_cannonfire;\r
+extern statetype s_cshot1;\r
+extern statetype s_cshot2;\r
+extern statetype s_cshot3;\r
+extern statetype s_cshot4;\r
+extern statetype s_cshothit1;\r
+extern statetype s_cshothit2;\r
+\r
+void SpawnCannon(Uint16 tileX, Uint16 tileY, Sint16 dir);\r
+void T_Cannon(objtype *ob);\r
+void C_CShot(objtype *ob, objtype *hit);\r
+void R_CShot(objtype *ob);\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                               K5_ACT2 DEFINITIONS\r
+\r
+=============================================================================\r
+*/\r
+\r
+extern statetype s_sparkywalk1;\r
+extern statetype s_sparkywalk2;\r
+extern statetype s_sparkywalk3;\r
+extern statetype s_sparkywalk4;\r
+extern statetype s_sparkylook1;\r
+extern statetype s_sparkylook2;\r
+extern statetype s_sparkylook3;\r
+extern statetype s_sparkylook4;\r
+extern statetype s_sparkylook5;\r
+extern statetype s_sparkylook6;\r
+extern statetype s_sparkylook7;\r
+extern statetype s_sparkylook8;\r
+extern statetype s_sparkyspeed1;\r
+extern statetype s_sparkyspeed2;\r
+extern statetype s_sparkyspeed3;\r
+extern statetype s_sparkyspeed4;\r
+extern statetype s_sparkycharge1;\r
+extern statetype s_sparkycharge2;\r
+extern statetype s_sparkycharge3;\r
+extern statetype s_sparkycharge4;\r
+extern statetype s_sparkyturn1;\r
+extern statetype s_sparkyturn2;\r
+extern statetype s_sparkyturn3;\r
+extern statetype s_sparkystun;\r
+\r
+void SpawnSparky(Uint16 tileX, Uint16 tileY);\r
+void T_Sparky(objtype *ob);\r
+void T_ChargeCount(objtype *ob);\r
+void T_SparkyLookL(objtype *ob);\r
+void T_SparkyLookR(objtype *ob);\r
+void T_RunSnd1(objtype *ob);\r
+void T_RunSnd2(objtype *ob);\r
+void C_Sparky(objtype *ob, objtype *hit);\r
+void R_Sparky(objtype *ob);\r
+\r
+extern statetype s_amptonwalk1;\r
+extern statetype s_amptonwalk2;\r
+extern statetype s_amptonwalk3;\r
+extern statetype s_amptonwalk4;\r
+extern statetype s_amptonturn;\r
+extern statetype s_amptongrab1;\r
+extern statetype s_amptongrab2;\r
+extern statetype s_amptonclimb;\r
+extern statetype s_amptonrelease1;\r
+extern statetype s_amptonrelease2;\r
+extern statetype s_amptonfiddle1;\r
+extern statetype s_amptonfiddle2;\r
+extern statetype s_amptonfiddle3;\r
+extern statetype s_amptonfiddle4;\r
+extern statetype s_amptonfiddle5;\r
+extern statetype s_amptonstun;\r
+\r
+void SpawnAmpton(Uint16 tileX, Uint16 tileY);\r
+void T_Ampton(objtype *ob);\r
+void T_AmptonClimb(objtype *ob);\r
+void T_SetNoThink(objtype *ob);\r
+void C_Ampton(objtype *ob, objtype *hit);\r
+void R_Ampton(objtype *ob);\r
+\r
+extern statetype s_slicestarslide;\r
+extern statetype s_slicestarbounce;\r
+extern statetype s_slicestarboom;\r
+\r
+void SpawnSlicestarSlide(Uint16 tileX, Uint16 tileY, Sint16 dir);\r
+void SpawnSlicestarBounce(Uint16 tileX, Uint16 tileY);\r
+void C_Slicestar(objtype *ob, objtype *hit);\r
+void R_Slicestar(objtype *ob);\r
+\r
+extern statetype s_shellywalk1;\r
+extern statetype s_shellywalk2;\r
+extern statetype s_shellywalk3;\r
+extern statetype s_shellywalk4;\r
+extern statetype s_shellylook;\r
+extern statetype s_shellylook2;\r
+extern statetype s_shellyjump1;\r
+extern statetype s_shellyjump2;\r
+extern statetype s_shellydie;\r
+extern statetype s_shellydieup;\r
+extern statetype s_shellyboom1;\r
+extern statetype s_shellyboom2;\r
+extern statetype s_shellyboom3;\r
+extern statetype s_shellyboom4;\r
+extern statetype s_shellypiece1;\r
+extern statetype s_shellypiece2;\r
+\r
+void SpawnShelly(Uint16 tileX, Uint16 tileY);\r
+void T_ShellyLook(objtype *ob);\r
+void T_Turn(objtype *ob);\r
+void T_ShellyFrag(objtype *ob);\r
+void C_Shelly(objtype *ob, objtype *hit);\r
+void R_Shelly(objtype *ob);\r
+void R_Shell(objtype *ob);\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                               K5_ACT3 DEFINITIONS\r
+\r
+=============================================================================\r
+*/\r
+\r
+extern statetype s_mine;\r
+extern statetype s_minecenter;\r
+extern statetype s_mineshift;\r
+extern statetype s_mineboom1;\r
+extern statetype s_mineboom2;\r
+extern statetype s_mineboom3;\r
+extern statetype s_mineboom4;\r
+extern statetype s_mineboom5;\r
+extern statetype s_mineboom6;\r
+extern statetype s_minepiece;\r
+\r
+void SpawnMine(Uint16 tileX, Uint16 tileY);\r
+boolean MinePosCheck(Uint16 tileX, Uint16 tileY);\r
+boolean Walk(objtype *ob, Sint16 dir);\r
+void ChaseThink(objtype *ob);\r
+void T_Mine(objtype *ob);\r
+void C_Solid(objtype *ob, objtype *hit);\r
+void C_MineFrag(objtype *ob, objtype *hit);\r
+void T_MineCenter(objtype *ob);\r
+void T_MineShift(objtype *ob);\r
+void T_MineFrag(objtype *ob);\r
+void R_Mine(objtype *ob);\r
+\r
+extern statetype s_robored;\r
+extern statetype s_roboredfire0;\r
+extern statetype s_roboredfire1;\r
+extern statetype s_roboredfire2;\r
+extern statetype s_rshot1;\r
+extern statetype s_rshot2;\r
+extern statetype s_rshothit1;\r
+extern statetype s_rshothit2;\r
+\r
+void SpawnRoboRed(Uint16 tileX, Uint16 tileY);\r
+void T_RoboRed(objtype *ob);\r
+void C_RoboRed(objtype *ob, objtype *hit);\r
+void T_RoboShoot(objtype *ob);\r
+void C_RShot(objtype *ob, objtype *hit);\r
+void R_RShot(objtype *ob);\r
+\r
+extern statetype s_gripsitd;\r
+extern statetype s_gripjumpd;\r
+extern statetype s_gripsitl;\r
+extern statetype s_gripjumpl;\r
+extern statetype s_gripsitr;\r
+extern statetype s_gripjumpr;\r
+extern statetype s_gripsitu;\r
+extern statetype s_gripjumpu;\r
+extern statetype s_gripspin1;\r
+extern statetype s_gripspin2;\r
+extern statetype s_gripspin3;\r
+extern statetype s_gripspin4;\r
+extern statetype s_gripspin5;\r
+extern statetype s_gripspin6;\r
+extern statetype s_gripspin7;\r
+extern statetype s_gripspin8;\r
+extern statetype s_gripflyd;\r
+extern statetype s_gripflyl;\r
+extern statetype s_gripflyr;\r
+extern statetype s_gripflyu;\r
+\r
+void SpawnSpirogrip(Uint16 tileX, Uint16 tileY);\r
+void T_SpiroLaunch(objtype *ob);\r
+void R_SpiroFly(objtype *ob);\r
+\r
+extern statetype s_spindred1;\r
+extern statetype s_spindred2;\r
+extern statetype s_spindred3;\r
+extern statetype s_spindred4;\r
+\r
+void SpawnSpindread(Uint16 tileX, Uint16 tileY);\r
+void T_Spindread(objtype *ob);\r
+void C_Spindread(objtype *ob, objtype *hit);\r
+void R_Spindread(objtype *ob);\r
+\r
+extern statetype s_master1;\r
+extern statetype s_master2;\r
+extern statetype s_master3;\r
+extern statetype s_master4;\r
+extern statetype s_mastershoot1;\r
+extern statetype s_mastershoot2;\r
+extern statetype s_mastertport1;\r
+extern statetype s_mastertport2;\r
+extern statetype s_mastertport3;\r
+extern statetype s_mastertport4;\r
+extern statetype s_mshot1;\r
+extern statetype s_mshot2;\r
+extern statetype s_mshot3;\r
+extern statetype s_mshot4;\r
+extern statetype s_mspray1;\r
+extern statetype s_mspray2;\r
+extern statetype s_mspray3;\r
+extern statetype s_mspray4;\r
+\r
+void SpawnMaster(Uint16 tileX, Uint16 tileY);\r
+void T_Master(objtype *ob);\r
+void T_MasterShoot(objtype *ob);\r
+void C_Master(objtype *ob, objtype *hit);\r
+void T_MasterTPort(objtype *ob);\r
+void C_MShot(objtype *ob, objtype *hit);\r
+void R_MShot(objtype *ob);\r
+void R_MSpray(objtype *ob);\r
+\r
+extern statetype s_shikadi1;\r
+extern statetype s_shikadi2;\r
+extern statetype s_shikadi3;\r
+extern statetype s_shikadi4;\r
+extern statetype s_shikadiwalk1;\r
+extern statetype s_shikadiwalk2;\r
+extern statetype s_shikadiwalk3;\r
+extern statetype s_shikadiwalk4;\r
+extern statetype s_shikadigrab;\r
+extern statetype s_shikadigrab2;\r
+extern statetype s_shikadistun;\r
+extern statetype s_polespark1;\r
+extern statetype s_polespark2;\r
+\r
+void SpawnShikadi(Uint16 tileX, Uint16 tileY);\r
+void T_Shikadi(objtype *ob);\r
+void C_Shikadi(objtype *ob, objtype *hit);\r
+void T_PoleShock(objtype *ob);\r
+void T_PoleSpark(objtype *ob);\r
+void R_Shikadi(objtype *ob);\r
+\r
+extern statetype s_petsit1;\r
+extern statetype s_petsit2;\r
+extern statetype s_petbark1;\r
+extern statetype s_petbark2;\r
+extern statetype s_pet1;\r
+extern statetype s_pet2;\r
+extern statetype s_pet3;\r
+extern statetype s_pet4;\r
+extern statetype s_petjump;\r
+extern statetype s_pshot1;\r
+extern statetype s_pshot2;\r
+extern statetype s_pshothot1;\r
+extern statetype s_pshothot2;\r
+extern statetype s_petstun;\r
+\r
+void SpawnPet(Uint16 tileX, Uint16 tileY);\r
+void T_Pet(objtype *ob);\r
+void T_PetSit(objtype *ob);\r
+void T_PetBark(objtype *ob);\r
+void C_Pet(objtype *ob, objtype *hit);\r
+void R_Pet(objtype *ob);\r
+void R_PetJump(objtype *ob);\r
+void C_PShot(objtype *ob, objtype *hit);\r
+void R_PShot(objtype *ob);\r
+\r
+extern statetype s_sphereful1;\r
+extern statetype s_sphereful2;\r
+extern statetype s_sphereful3;\r
+extern statetype s_sphereful4;\r
+\r
+void SpawnSphereful(Uint16 tileX, Uint16 tileY);\r
+void T_Sphereful(objtype *ob);\r
+void R_Sphereful(objtype *ob);\r
+\r
+extern statetype s_scottie1;\r
+extern statetype s_scottie2;\r
+extern statetype s_scottie3;\r
+extern statetype s_scottie4;\r
+extern statetype s_scottieface;\r
+extern statetype s_scottiestun;\r
+\r
+void SpawnScottie(Uint16 tileX, Uint16 tileY);\r
+void T_Scottie(objtype *ob);\r
+void C_Scottie(objtype *ob, objtype *hit);\r
+\r
+extern statetype s_qed;\r
+\r
+void SpawnQed(Uint16 tileX, Uint16 tileY);\r
+\r
+#endif
\ No newline at end of file
diff --git a/16/keen456/KEEN4-6/KEEN5/K5_SPEC.C b/16/keen456/KEEN4-6/KEEN5/K5_SPEC.C
new file mode 100755 (executable)
index 0000000..4248954
--- /dev/null
@@ -0,0 +1,1158 @@
+/* Reconstructed Commander Keen 4-6 Source Code\r
+ * Copyright (C) 2021 K1n9_Duk3\r
+ *\r
+ * This file is loosely based on:\r
+ * Keen Dreams Source Code\r
+ * Copyright (C) 2014 Javier M. Chavez\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License along\r
+ * with this program; if not, write to the Free Software Foundation, Inc.,\r
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+ */\r
+\r
+/*\r
+K5_SPEC.C\r
+=========\r
+\r
+Contains (in this order):\r
+\r
+- lump definition\r
+- "Star Wars" crawl text\r
+- level names & messages\r
+- Airlock opening & closing code\r
+- ScanInfoPlane() - for spawning the level objects and marking required sprites\r
+- game over animation\r
+- messages for breaking fuses\r
+*/\r
+\r
+#include "CK_DEF.H"\r
+\r
+enum {\r
+       HELP_LUMP,        //  0\r
+       CONTROLS_LUMP,    //  1\r
+       KEENTALK_LUMP,    //  2\r
+       LOADING_LUMP,     //  3\r
+       PADDLE_LUMP,      //  4\r
+       KEEN_LUMP,        //  5\r
+       SUGAR1_LUMP,      //  6\r
+       SUGAR2_LUMP,      //  7\r
+       SUGAR3_LUMP,      //  8\r
+       SUGAR4_LUMP,      //  9\r
+       SUGAR5_LUMP,      // 10\r
+       SUGAR6_LUMP,      // 11\r
+       ONEUP_LUMP,       // 12\r
+       KEYGEM_LUMP,      // 13\r
+       AMMO_LUMP,        // 14\r
+       LASER_LUMP,       // 15\r
+       WORLDKEEN_LUMP,   // 16\r
+       MASTER_LUMP,      // 17\r
+       SHIKADI_LUMP,     // 18\r
+       SHOCKSHUND_LUMP,  // 19\r
+       SPHEREFUL_LUMP,   // 20\r
+       SPARKY_LUMP,      // 21\r
+       MINE_LUMP,        // 22\r
+       SLICESTAR_LUMP,   // 23\r
+       ROBORED_LUMP,     // 24\r
+       SPIRO_LUMP,       // 25\r
+       AMPTON_LUMP,      // 26\r
+       VOLTE_LUMP,       // 27\r
+       SLOTPLAT_LUMP,    // 28\r
+       SPINDRED_LUMP,    // 29\r
+       SHELLEY_LUMP,     // 30\r
+       PLATFORM_LUMP,    // 31\r
+       SMALLPLAT_LUMP,   // 32\r
+       KEYCARD_LUMP,     // 33\r
+       SCOTTIE_LUMP,     // 34\r
+       FUSE_LUMP,        // 35\r
+       STAREXPLODE_LUMP, // 36\r
+       TELEPORT_LUMP,    // 37\r
+\r
+       NUMLUMPS=42       // Keen 5 has 4 unused lumps at the end\r
+};\r
+\r
+Uint16 lumpstart[NUMLUMPS] = {\r
+       HELP_LUMP_START,\r
+       CONTROLS_LUMP_START,\r
+       KEENTALK_LUMP_START,\r
+       LOADING_LUMP_START,\r
+       PADDLE_LUMP_START,\r
+       KEEN_LUMP_START,\r
+       SUGAR1_LUMP_START,\r
+       SUGAR2_LUMP_START,\r
+       SUGAR3_LUMP_START,\r
+       SUGAR4_LUMP_START,\r
+       SUGAR5_LUMP_START,\r
+       SUGAR6_LUMP_START,\r
+       ONEUP_LUMP_START,\r
+       KEYGEM_LUMP_START,\r
+       AMMO_LUMP_START,\r
+       LASER_LUMP_START,\r
+       WORLDKEEN_LUMP_START,\r
+       MASTER_LUMP_START,\r
+       SHIKADI_LUMP_START,\r
+       SHOCKSHUND_LUMP_START,\r
+       SPHEREFUL_LUMP_START,\r
+       SPARKY_LUMP_START,\r
+       MINE_LUMP_START,\r
+       SLICESTAR_LUMP_START,\r
+       ROBORED_LUMP_START,\r
+       SPIRO_LUMP_START,\r
+       AMPTON_LUMP_START,\r
+       VOLTE_LUMP_START,\r
+       SLOTPLAT_LUMP_START,\r
+       SPINDRED_LUMP_START,\r
+       SHELLEY_LUMP_START,\r
+       PLATFORM_LUMP_START,\r
+       MINIPLAT_LUMP_START,\r
+       KEYCARD_LUMP_START,\r
+       SCOTTIE_LUMP_START,\r
+       FUSE_LUMP_START,\r
+       STAREXPLODE_LUMP_START,\r
+       TELEPORT_LUMP_START\r
+};\r
+\r
+Uint16 lumpend[NUMLUMPS] = {\r
+       HELP_LUMP_END,\r
+       CONTROLS_LUMP_END,\r
+       KEENTALK_LUMP_END,\r
+       LOADING_LUMP_END,\r
+       PADDLE_LUMP_END,\r
+       KEEN_LUMP_END,\r
+       SUGAR1_LUMP_END,\r
+       SUGAR2_LUMP_END,\r
+       SUGAR3_LUMP_END,\r
+       SUGAR4_LUMP_END,\r
+       SUGAR5_LUMP_END,\r
+       SUGAR6_LUMP_END,\r
+       ONEUP_LUMP_END,\r
+       KEYGEM_LUMP_END,\r
+       AMMO_LUMP_END,\r
+       LASER_LUMP_END,\r
+       WORLDKEEN_LUMP_END,\r
+       MASTER_LUMP_END,\r
+       SHIKADI_LUMP_END,\r
+       SHOCKSHUND_LUMP_END,\r
+       SPHEREFUL_LUMP_END,\r
+       SPARKY_LUMP_END,\r
+       MINE_LUMP_END,\r
+       SLICESTAR_LUMP_END,\r
+       ROBORED_LUMP_END,\r
+       SPIRO_LUMP_END,\r
+       AMPTON_LUMP_END,\r
+       VOLTE_LUMP_END,\r
+       SLOTPLAT_LUMP_END,\r
+       SPINDRED_LUMP_END,\r
+       SHELLEY_LUMP_END,\r
+       PLATFORM_LUMP_END,\r
+       MINIPLAT_LUMP_END,\r
+       KEYCARD_LUMP_END,\r
+       SCOTTIE_LUMP_END,\r
+       FUSE_LUMP_END,\r
+       STAREXPLODE_LUMP_END,\r
+       TELEPORT_LUMP_END\r
+};\r
+\r
+boolean lumpneeded[NUMLUMPS];\r
+\r
+#if GRMODE == EGAGR\r
+\r
+char far swtext[] =\r
+       "Episode Five\n"\r
+       "\n"\r
+       "The Armageddon Machine\n"\r
+       "\n"\r
+       "After learning the\n"\r
+       "location of the secret\n"\r
+       "Shikadi base, Keen\n"\r
+       "jumped in the trusty\n"\r
+       "Bean-with-Bacon\n"\r
+       "Megarocket and blasted\n"\r
+       "across interstellar\n"\r
+       "space.\n"\r
+       "\n"\r
+       "Seventy-five furious\n"\r
+       "games of Paddle War\n"\r
+       "later, Keen dropped\n"\r
+       "out of lightspeed near\n"\r
+       " the Korath system.\n"\r
+       "\n"\r
+       "He flew toward the\n"\r
+       "planet, keeping it\n"\r
+       "between him and the\n"\r
+       "base.\n"\r
+       "\n"\r
+       "Pulling up underside\n"\r
+       "and docking at the\n"\r
+       "Ion Ventilation System,\n"\r
+       "Keen must destroy the\n"\r
+       "Shikadi Armageddon\n"\r
+       "Machine before it\n"\r
+       "explodes and destroys\n"\r
+       "the Milky Way!  He\n"\r
+       "steps into the dark\n"\r
+       "ventilation duct and\n"\r
+       "begins his most\n"\r
+       "dangerous adventure\n"\r
+       "yet...\n";\r
+\r
+#endif\r
+\r
+char far l0n[] = "Omegamatic";\r
+char far l1n[] = "Ion Ventilation System";\r
+char far l2n[] = "Security Center";\r
+char far l3n[] = "Defense Tunnel Vlook";\r
+char far l4n[] = "Energy Flow Systems";\r
+char far l5n[] = "Defense Tunnel Burrh";\r
+char far l6n[] = "Regulation\nControl Center";\r
+char far l7n[] = "Defense Tunnel Sorra";\r
+char far l8n[] = "Neutrino\nBurst Injector";\r
+char far l9n[] = "Defense Tunnel Teln";\r
+char far l10n[] = "Brownian\nMotion Inducer";\r
+char far l11n[] = "Gravitational\nDamping Hub";\r
+char far l12n[] = "Quantum\nExplosion Dynamo";\r
+char far l13n[] = "Korath III Base";\r
+char far l14n[] = "BWBMegarocket";\r
+char far l15n[] = "High Scores";\r
+\r
+char far l0e[] = "Keen purposefully\nwanders about the\nOmegamatic";\r
+char far l1e[] = "Keen investigates the\nIon Ventilation System";\r
+char far l2e[] = "Keen struts through\nthe Security Center";\r
+char far l3e[] = "Keen invades\nDefense Tunnel Vlook";\r
+char far l4e[] = "Keen engages\nEnergy Flow Systems";\r
+char far l5e[] = "Keen barrels into\nDefense Tunnel Burrh";\r
+char far l6e[] = "Keen goes nuts in\nthe Regulation\nControl Center";\r
+char far l7e[] = "Keen regrets entering\nDefense Tunnel Sorra";\r
+char far l8e[] = "Keen blows through\nthe Neutrino\nBurst Injector";\r
+char far l9e[] = "Keen trots through\nDefense Tunnel Teln";\r
+char far l10e[] = "Keen breaks into\nthe Brownian\nMotion Inducer";\r
+char far l11e[] = "Keen hurries through\nthe Gravitational\nDamping Hub";\r
+char far l12e[] = "Keen explodes into\nthe Quantum\nExplosion Dynamo";\r
+char far l13e[] = "Keen faces danger\nin the secret\nKorath III Base";\r
+char far l14e[] = "Keen will not be\nin the BWBMegarocket";\r
+char far l15e[] = "Keen unexplainedly\nfinds himself by\ntheHigh Scores";      // sic!\r
+\r
+char far *levelnames[GAMELEVELS] = {\r
+       l0n,\r
+       l1n,\r
+       l2n,\r
+       l3n,\r
+       l4n,\r
+       l5n,\r
+       l6n,\r
+       l7n,\r
+       l8n,\r
+       l9n,\r
+       l10n,\r
+       l11n,\r
+       l12n,\r
+       l13n,\r
+       l14n,\r
+       l15n\r
+};\r
+\r
+char far *levelenter[GAMELEVELS] = {\r
+       l0e,\r
+       l1e,\r
+       l2e,\r
+       l3e,\r
+       l4e,\r
+       l5e,\r
+       l6e,\r
+       l7e,\r
+       l8e,\r
+       l9e,\r
+       l10e,\r
+       l11e,\r
+       l12e,\r
+       l13e,\r
+       l14e,\r
+       l15e\r
+};\r
+\r
+Uint16 bonuslump[] = {\r
+       KEYGEM_LUMP, KEYGEM_LUMP, KEYGEM_LUMP, KEYGEM_LUMP,\r
+       SUGAR1_LUMP, SUGAR2_LUMP, SUGAR3_LUMP,\r
+       SUGAR4_LUMP, SUGAR5_LUMP, SUGAR6_LUMP,\r
+       ONEUP_LUMP, AMMO_LUMP, KEYCARD_LUMP, 0, 0\r
+};\r
+\r
+//============================================================================\r
+\r
+/*\r
+===========================\r
+=\r
+= OpenMapDoor\r
+=\r
+===========================\r
+*/\r
+\r
+void OpenMapDoor(Sint16 tileX, Sint16 tileY)\r
+{\r
+       Sint16 x, y;\r
+       Uint16 tiles[2][2];\r
+\r
+       for (y=0; y<2; y++)\r
+       {\r
+               for (x=0; x<2; x++)\r
+               {\r
+                       tiles[y][x] = *(mapsegs[1]+mapbwidthtable[y]/2 + x + 10);\r
+               }\r
+       }\r
+       RF_MemToMap(&tiles[0][0], 1, tileX, tileY, 2, 2);\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= CloseMapDoor\r
+=\r
+===========================\r
+*/\r
+\r
+void CloseMapDoor(Sint16 tileX, Sint16 tileY)\r
+{\r
+       Sint16 x, y;\r
+       Uint16 tiles[2][2];\r
+\r
+       for (y=0; y<2; y++)\r
+       {\r
+               for (x=0; x<2; x++)\r
+               {\r
+                       tiles[y][x] = *(mapsegs[1]+mapbwidthtable[y]/2 + x);\r
+               }\r
+       }\r
+       RF_MemToMap(&tiles[0][0], 1, tileX, tileY, 2, 2);\r
+}\r
+\r
+//============================================================================\r
+\r
+/*\r
+==========================\r
+=\r
+= ScanInfoPlane\r
+=\r
+= Spawn all actors and mark down special places\r
+=\r
+==========================\r
+*/\r
+\r
+void ScanInfoPlane(void)\r
+{\r
+       Uint16 i, x, y, chunk;\r
+       Sint16 info;\r
+       Uint16 far *map;\r
+       objtype *ob;\r
+\r
+       InitObjArray();                  // start spawning things with a clean slate\r
+\r
+       memset(lumpneeded, 0, sizeof(lumpneeded));\r
+       gamestate.numfuses = 0;\r
+\r
+       map = mapsegs[2];\r
+\r
+       for (y=0; y<mapheight; y++)\r
+       {\r
+               for (x=0; x<mapwidth; x++)\r
+               {\r
+                       info = *map++;\r
+\r
+                       if (info == 0)\r
+                               continue;\r
+\r
+                       switch (info)\r
+                       {\r
+                       case 1:\r
+                               SpawnKeen(x, y, 1);\r
+                               SpawnScore();\r
+                               lumpneeded[KEEN_LUMP] = true;\r
+                               CA_MarkGrChunk(SCOREBOXSPR);\r
+                               break;\r
+\r
+                       case 2:\r
+                               SpawnKeen(x, y, -1);\r
+                               SpawnScore();\r
+                               lumpneeded[KEEN_LUMP] = true;\r
+                               CA_MarkGrChunk(SCOREBOXSPR);\r
+                               break;\r
+\r
+                       case 3:\r
+                               SpawnScore();\r
+                               lumpneeded[WORLDKEEN_LUMP] = true;\r
+                               CA_MarkGrChunk(SCOREBOXSPR);\r
+                               if (playstate == ex_portout)\r
+                                       break;\r
+                               SpawnWorldKeen(x, y);\r
+                               break;\r
+\r
+                       case 26:\r
+                               if (playstate != ex_portout)\r
+                                       break;\r
+                               SpawnWorldKeenPort(x, y);\r
+                               break;\r
+\r
+                       case 6:\r
+                               if (gamestate.difficulty < gd_Hard)\r
+                                       break;\r
+                       case 5:\r
+                               if (gamestate.difficulty < gd_Normal)\r
+                                       break;\r
+                       case 4:\r
+                               SpawnSparky(x, y);\r
+                               lumpneeded[SPARKY_LUMP] = true;\r
+                               break;\r
+\r
+                       case 9:\r
+                               if (gamestate.difficulty < gd_Hard)\r
+                                       break;\r
+                       case 8:\r
+                               if (gamestate.difficulty < gd_Normal)\r
+                                       break;\r
+                       case 7:\r
+                               SpawnMine(x, y);\r
+                               lumpneeded[MINE_LUMP] = true;\r
+                               break;\r
+\r
+                       case 12:\r
+                               if (gamestate.difficulty < gd_Hard)\r
+                                       break;\r
+                       case 11:\r
+                               if (gamestate.difficulty < gd_Normal)\r
+                                       break;\r
+                       case 10:\r
+                               SpawnSlicestarSlide(x, y, 0);\r
+                               lumpneeded[SLICESTAR_LUMP] = true;\r
+                               break;\r
+\r
+                       case 15:\r
+                               if (gamestate.difficulty < gd_Hard)\r
+                                       break;\r
+                       case 14:\r
+                               if (gamestate.difficulty < gd_Normal)\r
+                                       break;\r
+                       case 13:\r
+                               SpawnRoboRed(x, y);\r
+                               lumpneeded[ROBORED_LUMP] = true;\r
+                               break;\r
+\r
+                       case 18:\r
+                               if (gamestate.difficulty < gd_Hard)\r
+                                       break;\r
+                       case 17:\r
+                               if (gamestate.difficulty < gd_Normal)\r
+                                       break;\r
+                       case 16:\r
+                               SpawnSpirogrip(x, y);\r
+                               lumpneeded[SPIRO_LUMP] = true;\r
+                               break;\r
+\r
+                       case 21:\r
+                               if (gamestate.difficulty < gd_Hard)\r
+                                       break;\r
+                       case 20:\r
+                               if (gamestate.difficulty < gd_Normal)\r
+                                       break;\r
+                       case 19:\r
+                               SpawnSlicestarBounce(x, y);\r
+                               lumpneeded[SLICESTAR_LUMP] = true;\r
+                               break;\r
+\r
+                       case 24:\r
+                               if (gamestate.difficulty < gd_Hard)\r
+                                       break;\r
+                       case 23:\r
+                               if (gamestate.difficulty < gd_Normal)\r
+                                       break;\r
+                       case 22:\r
+                               SpawnSlicestarSlide(x, y, 1);\r
+                               lumpneeded[SLICESTAR_LUMP] = true;\r
+                               break;\r
+\r
+                       case 25:\r
+                               RF_SetScrollBlock(x, y, true);\r
+                               break;\r
+\r
+                       // case 26 (teleported map keen) is handled above\r
+\r
+                       case 27:\r
+                       case 28:\r
+                       case 29:\r
+                       case 30:\r
+                               SpawnPlatform(x, y, info-27, 0);\r
+                               lumpneeded[PLATFORM_LUMP] = true;\r
+                               break;\r
+\r
+                       // case 31 is the block icon\r
+\r
+                       case 32:\r
+                               SpawnDropPlat(x, y);\r
+                               lumpneeded[PLATFORM_LUMP] = true;\r
+                               break;\r
+\r
+                       case 35:\r
+                               SpawnStaticPlat(x, y);\r
+                               lumpneeded[PLATFORM_LUMP] = true;\r
+                               break;\r
+                       case 34:\r
+                               if (gamestate.difficulty > gd_Normal)\r
+                                       break;\r
+                               SpawnStaticPlat(x, y);\r
+                               lumpneeded[PLATFORM_LUMP] = true;\r
+                               break;\r
+                       case 33:\r
+                               if (gamestate.difficulty > gd_Easy)\r
+                                       break;\r
+                               SpawnStaticPlat(x, y);\r
+                               lumpneeded[PLATFORM_LUMP] = true;\r
+                               break;\r
+\r
+                       case 36:\r
+                       case 37:\r
+                       case 38:\r
+                       case 39:\r
+                               SpawnGoPlat(x, y, info-36, 0);\r
+                               lumpneeded[PLATFORM_LUMP] = true;\r
+                               break;\r
+\r
+                       case 40:\r
+                               SpawnSneakPlat(x, y);\r
+                               lumpneeded[PLATFORM_LUMP] = true;\r
+                               break;\r
+\r
+                       case 41:\r
+                               if (gamestate.mapon == 12)\r
+                               {\r
+                                       gamestate.numfuses = 4;\r
+                                       SpawnQed(x, y);\r
+                               }\r
+                               else\r
+                               {\r
+                                       gamestate.numfuses++;\r
+                               }\r
+                               lumpneeded[FUSE_LUMP] = true;\r
+                               break;\r
+\r
+                       case 44:\r
+                               if (gamestate.difficulty < gd_Hard)\r
+                                       break;\r
+                       case 43:\r
+                               if (gamestate.difficulty < gd_Normal)\r
+                                       break;\r
+                       case 42:\r
+                               SpawnAmpton(x, y);\r
+                               lumpneeded[AMPTON_LUMP] = true;\r
+                               break;\r
+\r
+                       case 53:\r
+                               if (gamestate.difficulty < gd_Hard)\r
+                                       break;\r
+                       case 49:\r
+                               if (gamestate.difficulty < gd_Normal)\r
+                                       break;\r
+                       case 45:\r
+                               SpawnCannon(x, y, 0);\r
+                               lumpneeded[LASER_LUMP] = true;\r
+                               break;\r
+\r
+                       case 54:\r
+                               if (gamestate.difficulty < gd_Hard)\r
+                                       break;\r
+                       case 50:\r
+                               if (gamestate.difficulty < gd_Normal)\r
+                                       break;\r
+                       case 46:\r
+                               SpawnCannon(x, y, 1);\r
+                               lumpneeded[LASER_LUMP] = true;\r
+                               break;\r
+\r
+                       case 55:\r
+                               if (gamestate.difficulty < gd_Hard)\r
+                                       break;\r
+                       case 51:\r
+                               if (gamestate.difficulty < gd_Normal)\r
+                                       break;\r
+                       case 47:\r
+                               SpawnCannon(x, y, 2);\r
+                               lumpneeded[LASER_LUMP] = true;\r
+                               break;\r
+\r
+                       case 56:\r
+                               if (gamestate.difficulty < gd_Hard)\r
+                                       break;\r
+                       case 52:\r
+                               if (gamestate.difficulty < gd_Normal)\r
+                                       break;\r
+                       case 48:\r
+                               SpawnCannon(x, y, 3);\r
+                               lumpneeded[LASER_LUMP] = true;\r
+                               break;\r
+\r
+                       case 69:\r
+                               if (gamestate.ammo >= 5)\r
+                                       break;\r
+                               info = 68;      // spawn ammo\r
+                               //no break here!\r
+                       case 57:\r
+                       case 58:\r
+                       case 59:\r
+                       case 60:\r
+                       case 61:\r
+                       case 62:\r
+                       case 63:\r
+                       case 64:\r
+                       case 65:\r
+                       case 66:\r
+                       case 67:\r
+                       case 68:\r
+                               SpawnBonus(x, y, info-57);\r
+                               lumpneeded[bonuslump[info-57]] = true;\r
+                               break;\r
+                       case 70:\r
+                               SpawnBonus(x, y, info-58);\r
+                               lumpneeded[bonuslump[info-58]] = true;\r
+                               break;\r
+\r
+                       case 73:\r
+                               if (gamestate.difficulty < gd_Hard)\r
+                                       break;\r
+                       case 72:\r
+                               if (gamestate.difficulty < gd_Normal)\r
+                                       break;\r
+                       case 71:\r
+                               SpawnVolte(x, y);\r
+                               lumpneeded[VOLTE_LUMP] = true;\r
+                               break;\r
+\r
+                       case 76:\r
+                               if (gamestate.difficulty < gd_Hard)\r
+                                       break;\r
+                       case 75:\r
+                               if (gamestate.difficulty < gd_Normal)\r
+                                       break;\r
+                       case 74:\r
+                               SpawnShelly(x, y);\r
+                               lumpneeded[SHELLEY_LUMP] = true;\r
+                               break;\r
+\r
+                       case 79:\r
+                               if (gamestate.difficulty < gd_Hard)\r
+                                       break;\r
+                       case 78:\r
+                               if (gamestate.difficulty < gd_Normal)\r
+                                       break;\r
+                       case 77:\r
+                               SpawnSpindread(x, y);\r
+                               lumpneeded[SPINDRED_LUMP] = true;\r
+                               break;\r
+\r
+                       case 80:\r
+                       case 81:\r
+                       case 82:\r
+                       case 83:\r
+                               SpawnGoPlat(x, y, info-80, 1);\r
+                               lumpneeded[SLOTPLAT_LUMP] = true;\r
+                               break;\r
+\r
+                       case 84:\r
+                       case 85:\r
+                       case 86:\r
+                       case 87:\r
+                               SpawnPlatform(x, y, info-84, 1);\r
+                               lumpneeded[SLOTPLAT_LUMP] = true;\r
+                               break;\r
+\r
+                       case 90:\r
+                               if (gamestate.difficulty < gd_Hard)\r
+                                       break;\r
+                       case 89:\r
+                               if (gamestate.difficulty < gd_Normal)\r
+                                       break;\r
+                       case 88:\r
+                               SpawnMaster(x, y);\r
+                               lumpneeded[MASTER_LUMP] = true;\r
+                               break;\r
+\r
+                       // cases 91 to 98 are direction arrows\r
+\r
+                       case 101:\r
+                               if (gamestate.difficulty < gd_Hard)\r
+                                       break;\r
+                       case 100:\r
+                               if (gamestate.difficulty < gd_Normal)\r
+                                       break;\r
+                       case 99:\r
+                               SpawnShikadi(x, y);\r
+                               lumpneeded[SHIKADI_LUMP] = true;\r
+                               break;\r
+\r
+                       case 104:\r
+                               if (gamestate.difficulty < gd_Hard)\r
+                                       break;\r
+                       case 103:\r
+                               if (gamestate.difficulty < gd_Normal)\r
+                                       break;\r
+                       case 102:\r
+                               SpawnPet(x, y);\r
+                               lumpneeded[SHOCKSHUND_LUMP] = true;\r
+                               break;\r
+\r
+                       case 107:\r
+                               if (gamestate.difficulty < gd_Hard)\r
+                                       break;\r
+                       case 106:\r
+                               if (gamestate.difficulty < gd_Normal)\r
+                                       break;\r
+                       case 105:\r
+                               SpawnSphereful(x, y);\r
+                               lumpneeded[SPHEREFUL_LUMP] = true;\r
+                               break;\r
+\r
+                       // cases 108 to 123 are wall collision icons for the WallDebug cheat\r
+\r
+                       case 124:\r
+                               SpawnScottie(x, y);\r
+                               lumpneeded[SCOTTIE_LUMP] = true;\r
+                               break;\r
+\r
+                       case 125:\r
+                               lumpneeded[TELEPORT_LUMP] = true;\r
+\r
+                       }\r
+               }\r
+       }\r
+\r
+       for (ob = player; ob; ob = ob->next)\r
+       {\r
+               if (ob->active != ac_allways)\r
+                       ob->active = ac_no;\r
+       }\r
+\r
+       for (i = 0; i < NUMLUMPS; i++)\r
+       {\r
+               if (lumpneeded[i])\r
+               {\r
+                       for (chunk = lumpstart[i]; chunk <= lumpend[i]; chunk++)\r
+                       {\r
+                               CA_MarkGrChunk(chunk);\r
+                       }\r
+               }\r
+       }\r
+\r
+       // Keen 5 addition to PatchWorldMap (PatchWorldMap is shared across Keen 4-6)\r
+       if (gamestate.mapon == 0)\r
+       {\r
+               info = CONVERT_GLOBAL_TO_TILE(player->y);\r
+               if (info < 75 || info > 100)\r
+               {\r
+                       CloseMapDoor(24, 76);\r
+                       OpenMapDoor(22, 55);\r
+               }\r
+               if (gamestate.leveldone[4] && gamestate.leveldone[6] && gamestate.leveldone[8] && gamestate.leveldone[10]\r
+                       && (info > 39 || info > 100) )\r
+               {\r
+                       OpenMapDoor(26, 55);\r
+               }\r
+               if (info <= 39 || info > 100)\r
+               {\r
+                       OpenMapDoor(24, 30);\r
+               }\r
+       }\r
+}\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                 GAME OVER SEQUENCE\r
+\r
+=============================================================================\r
+*/\r
+\r
+/*\r
+------------------------------------------------------------------------------\r
+The galaxy explosion chunk contains data in the following format:\r
+\r
+struct {\r
+       Uint16 posx[4000];\r
+       Uint16 velx[4000];\r
+       Uint16 posy[4000];\r
+       Uint16 vely[4000];\r
+};\r
+\r
+The values are stored as "fixed point" numbers (divide each number by 128 to\r
+get the amount in pixel units). The code in MoveStars basically does the\r
+following:\r
+\r
+1.     set all pixels on the screen to black\r
+\r
+2.     for each of the 4000 entries do:\r
+\r
+2.1    x := posx[i] + velx[i]\r
+\r
+2.2    if x (in pixels) is < 0 or > 320 then go to 2.8\r
+\r
+2.3    posx[i] := x\r
+\r
+2.4    y := posy[i] + vely[i]\r
+\r
+2.5    if y (in pixels) is < 0 or > 200 then go to 2.8\r
+\r
+2.6    posy[i] := y\r
+\r
+2.7    draw a white pixel at position (x, y) on the screen\r
+\r
+2.8    continue with the next entry\r
+\r
+------------------------------------------------------------------------------\r
+*/\r
+\r
+#if GRMODE == CGAGR\r
+\r
+Uint8 plotpixels[8] = {0xC0, 0x30, 0x0C, 0x03};\r
+\r
+/*\r
+===========================\r
+=\r
+= MoveStars   CGA\r
+=\r
+===========================\r
+*/\r
+\r
+void MoveStars(void)\r
+{\r
+       asm {\r
+               mov     ax, screenseg;\r
+               mov     es, ax;\r
+               mov     ds, word ptr grsegs + 2*GALAXY;\r
+               mov     cx, 2000h;\r
+               xor     ax, ax;\r
+               xor     di, di;\r
+               rep stosw;\r
+               mov     si, 7998;\r
+       }\r
+l1:\r
+       asm {\r
+               mov     ax, [si];               // get posx\r
+               add     ax, [si+8000];          // add velx\r
+               cmp     ax, 40960;              // check if new posx is on screen\r
+               ja      l2;\r
+               mov     [si], ax;               // set new posx\r
+               mov     bx, [si+16000];         // get posy\r
+               add     bx, [si+24000];         // add vely\r
+               cmp     bx, 25600;              // check if new posy is on screen\r
+               ja      l2;\r
+               mov     [si+16000], bx;         // set new posy\r
+               mov     cl, 7;\r
+               shr     bx, cl;\r
+               shl     bx, 1;\r
+               mov     di, word ptr ss:ylookup[bx];\r
+               mov     bx, ax;\r
+               mov     cl, 9;\r
+               shr     ax, cl;\r
+               add     di, ax;\r
+               mov     cl, 7;\r
+               shr     bx, cl;\r
+               and     bx, 3;\r
+               mov     al, BYTE PTR ss:plotpixels[bx];\r
+               or              es:[di], al;            // draw the pixel\r
+       }\r
+l2:\r
+       asm {\r
+               sub     si, 2;\r
+               jns     l1;\r
+               mov     ax, ss;\r
+               mov     ds, ax;\r
+       }\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= GameOver   CGA\r
+=\r
+===========================\r
+*/\r
+\r
+void GameOver(void)\r
+{\r
+       Sint16 i;\r
+\r
+       FreeGraphics();\r
+       CA_CacheGrChunk(MILKYWAYPIC);\r
+       CA_CacheGrChunk(GALAXY);\r
+       CA_CacheGrChunk(GAMEOVERPIC);\r
+       RF_FixOfs();\r
+       VW_ClearVideo(BLACK);\r
+       VWB_DrawPic(0, 0, MILKYWAYPIC);\r
+       VW_UpdateScreen();\r
+\r
+       SD_WaitSoundDone();\r
+       SD_PlaySound(SND_GAMEOVER2);\r
+       SD_WaitSoundDone();\r
+       SD_PlaySound(SND_GAMEOVER1);\r
+\r
+       IN_ClearKeysDown();\r
+       VW_SetLineWidth(80);\r
+\r
+       for (i=0; i<60; i++)\r
+       {\r
+               lasttimecount = TimeCount;\r
+               MoveStars();\r
+               VW_UpdateScreen();\r
+\r
+               do {} while (TimeCount-lasttimecount < 4);\r
+\r
+               if (LastScan)\r
+                       break;\r
+       }\r
+\r
+       VW_SetLineWidth(SCREENWIDTH);\r
+       VW_ClearVideo(BLACK);\r
+       StartMusic(18);\r
+       VWB_DrawPic(32, 80, GAMEOVERPIC);\r
+       VW_UpdateScreen();\r
+       IN_UserInput(24*TickBase, false);\r
+       StopMusic();\r
+}\r
+\r
+//============================================================================\r
+\r
+#else\r
+\r
+Uint16 dim[18] = {8, 8, 7, 15, 7, 8, 0, 8, 7, 15, 7, 8, 0, 0, 0, 0, 0, 0};\r
+Uint16 bright[18] = {7, 7, 7, 7, 7, 15, 7, 8, 0, 7, 15, 7, 8, 0, 0, 0, 0, 0};\r
+Uint8 galaxycolors[17] = {0, 1, 2, 3, 4, 5, 6, 7, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 3};\r
+Uint8 plotpixels[8] = {0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01};\r
+\r
+/*\r
+===========================\r
+=\r
+= MoveStars   EGA\r
+=\r
+===========================\r
+*/\r
+\r
+void MoveStars(Uint16 dest)\r
+{\r
+       asm {\r
+               mov     dx, GC_INDEX;\r
+               mov     al, GC_BITMASK;\r
+               out     dx, al;\r
+               inc     dx;\r
+               mov     ds, word ptr grsegs + 2*GALAXY;\r
+               mov     ax, 0A000h;\r
+               mov     es, ax;\r
+               mov     cx, 1000h;\r
+               xor     ax, ax;\r
+               mov     di, dest;\r
+               rep stosw;\r
+               mov     si, 7998;\r
+       }\r
+l1:\r
+       asm {\r
+               mov     ax, [si];               // get posx\r
+               add     ax, [si+8000];          // add velx\r
+               cmp     ax, 40960;              // check if new posx is on screen\r
+               ja      l2;\r
+               mov     [si], ax;               // set new posx\r
+               mov     bx, [si+16000];         // get posy\r
+               add     bx, [si+24000];         // add vely\r
+               cmp     bx, 25600;              // check if new posy is on screen\r
+               ja      l2;\r
+               mov     [si+16000], bx;         // set new posy\r
+               mov     cl, 7;\r
+               shr     bx, cl;\r
+               shl     bx, 1;\r
+               mov     di, word ptr ss:ylookup[bx];\r
+               add     di, dest;\r
+               mov     bx, ax;\r
+               mov     cl, 10;\r
+               shr     ax, cl;\r
+               add     di, ax;\r
+               mov     cl, 7;\r
+               shr     bx, cl;\r
+               and     bx, 7;\r
+               mov     al, BYTE PTR ss:plotpixels[bx];\r
+               out     dx, al;\r
+               mov     al, 0Fh;\r
+               xchg    al, es:[di];            // draw the pixel\r
+       }\r
+l2:\r
+       asm {\r
+               sub     si, 2;\r
+               jns     l1;\r
+               mov     ax, ss;\r
+               mov     ds, ax;\r
+               mov     al, 0FFh;\r
+               out     dx, al;\r
+       }\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= SetCrtc   EGA\r
+=\r
+===========================\r
+*/\r
+\r
+void SetCrtc(Uint16 addr)\r
+{\r
+       asm {\r
+               cli;\r
+               mov     cx, addr;\r
+               mov     dx, CRTC_INDEX;\r
+               mov     al, CRTC_STARTHIGH;\r
+               out     dx, al;\r
+               inc     dx;\r
+               mov     al, ch;\r
+               out     dx, al;\r
+               dec     dx;\r
+               mov     al, CRTC_STARTLOW;\r
+               out     dx, al;\r
+               inc     dx;\r
+               mov     al, cl;\r
+               out     dx, al;\r
+               sti;\r
+       }\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= GameOver   EGA\r
+=\r
+===========================\r
+*/\r
+\r
+void GameOver(void)\r
+{\r
+       Sint16 i;\r
+\r
+       FreeGraphics();\r
+       VW_FadeOut();\r
+       CA_CacheGrChunk(MILKYWAYPIC);\r
+       CA_CacheGrChunk(GALAXY);\r
+       CA_CacheGrChunk(GAMEOVERPIC);\r
+       VW_SetLineWidth(40);\r
+       VW_SetScreen(0, 0);\r
+       bufferofs = 0;\r
+       VW_ClearVideo(0);\r
+       VW_DrawPic(0, 0, MILKYWAYPIC);\r
+       VW_ScreenToScreen(0, 0x2000, 40, 200);\r
+       VW_FadeIn();\r
+       IN_ClearKeysDown();\r
+       SD_PlaySound(SND_GAMEOVER2);\r
+\r
+       for (i=0; i<18; i++)\r
+       {\r
+               galaxycolors[8] = dim[i];\r
+               galaxycolors[7] = bright[i];\r
+\r
+               SetPalette(galaxycolors);\r
+\r
+               VW_WaitVBL(10);\r
+               if (LastScan)\r
+                       goto gameover;\r
+       }\r
+\r
+       EGAWRITEMODE(2);\r
+       EGAMAPMASK(15);\r
+       SD_PlaySound(SND_GAMEOVER1);\r
+\r
+       for (i=0; i<30; i++)\r
+       {\r
+               lasttimecount = TimeCount;\r
+               MoveStars(0x2000);\r
+               SetCrtc(0x2000);\r
+               do {} while (TimeCount-lasttimecount < 4);\r
+\r
+               lasttimecount = TimeCount;\r
+               MoveStars(0);\r
+               SetCrtc(0);\r
+               do {} while (TimeCount-lasttimecount < 4);\r
+\r
+               if (LastScan)\r
+                       goto gameover;\r
+       }\r
+\r
+gameover:\r
+       EGAWRITEMODE(0);\r
+       VW_ClearVideo(BLACK);\r
+       VW_SetLineWidth(SCREENWIDTH);\r
+       VW_SetDefaultColors();\r
+       RF_FixOfs();\r
+       StartMusic(18);\r
+       VWB_DrawPic(32, 80, GAMEOVERPIC);\r
+       VW_UpdateScreen();\r
+       IN_UserInput(24*TickBase, false);\r
+       StopMusic();\r
+}\r
+\r
+#endif\r
+\r
+//============================================================================\r
+\r
+/*\r
+===========================\r
+=\r
+= FinishedFuse\r
+=\r
+===========================\r
+*/\r
+\r
+void FinishedFuse(void)\r
+{\r
+       SD_WaitSoundDone();\r
+       CA_UpLevel();\r
+#if 0\r
+       // bugfix:\r
+       CA_ClearMarks();        // don't cache more than we actually need here\r
+#endif\r
+       CA_MarkGrChunk(KEENTALK1PIC);\r
+       CA_MarkGrChunk(KEENTALK2PIC);\r
+       CA_CacheMarks(NULL);\r
+\r
+       VW_FixRefreshBuffer();\r
+       US_CenterWindow(26, 8);\r
+       WindowW -= 48;\r
+       VWB_DrawPic(WindowX+WindowW, WindowY, KEENTALK1PIC);\r
+       PrintY += 12;\r
+       if (gamestate.mapon == 13)\r
+       {\r
+               US_CPrint(\r
+                       "I wonder what that\n"\r
+                       "fuse was for....\n"\r
+                       );\r
+       }\r
+       else\r
+       {\r
+               US_CPrint(\r
+                       "One of the four\n"\r
+                       "machines protecting the\n"\r
+                       "main elevator shaft--\n"\r
+                       "toast!\n"\r
+                       );\r
+       }\r
+       VW_UpdateScreen();\r
+       VW_WaitVBL(30);\r
+       IN_ClearKeysDown();\r
+       IN_Ack();\r
+\r
+       VWB_DrawPic(WindowX+WindowW, WindowY, KEENTALK2PIC);\r
+       VW_UpdateScreen();\r
+       VW_WaitVBL(30);\r
+       IN_ClearKeysDown();\r
+       IN_Ack();\r
+\r
+       CA_DownLevel();\r
+       StopMusic();\r
+}
\ No newline at end of file
diff --git a/16/keen456/KEEN4-6/KEEN5C/GFXC_CK5.EQU b/16/keen456/KEEN4-6/KEEN5C/GFXC_CK5.EQU
new file mode 100755 (executable)
index 0000000..9cb6e6b
--- /dev/null
@@ -0,0 +1,55 @@
+;=====================================\r
+;\r
+; Graphics .EQU file for .CK5\r
+; not IGRAB-ed :)\r
+;\r
+;=====================================\r
+\r
+;INCLUDE "VERSION.EQU"\r
+\r
+;\r
+; Amount of each data item\r
+;\r
+NUMFONT     =  2\r
+NUMFONTM    =  0\r
+NUMPICM     =  3\r
+NUMTILE8    =  108\r
+NUMTILE8M   =  36\r
+NUMTILE32   =  0\r
+NUMTILE32M  =  0\r
+\r
+;\r
+; Amount of each item in episode 5\r
+;\r
+NUMPICS     =  94\r
+NUMSPRITES  =  346\r
+NUMTILE16   =  1512\r
+NUMTILE16M  =  2952\r
+NUMEXTERN   =  17\r
+\r
+\r
+;\r
+; File offsets for data items\r
+;\r
+STRUCTPIC       =      0\r
+STRUCTPICM      =      1\r
+STRUCTSPRITE    =      2\r
+\r
+STARTFONT       =      3\r
+STARTFONTM      =      (STARTFONT+NUMFONT)\r
+STARTPICS       =      (STARTFONTM+NUMFONTM)\r
+STARTPICM       =      (STARTPICS+NUMPICS)\r
+STARTSPRITES    =      (STARTPICM+NUMPICM)\r
+STARTTILE8      =      (STARTSPRITES+NUMSPRITES)\r
+STARTTILE8M     =      (STARTTILE8+1)\r
+STARTTILE16     =      (STARTTILE8M+1)\r
+STARTTILE16M    =      (STARTTILE16+NUMTILE16)\r
+STARTTILE32     =      (STARTTILE16M+NUMTILE16M)\r
+STARTTILE32M    =      (STARTTILE32+NUMTILE32)\r
+STARTEXTERN     =      (STARTTILE32M+NUMTILE32M)\r
+\r
+NUMCHUNKS       =      (STARTEXTERN+NUMEXTERN)\r
+\r
+;\r
+; Thank you for using IGRAB!\r
+;\r
diff --git a/16/keen456/KEEN4-6/KEEN5C/GFXC_CK5.H b/16/keen456/KEEN4-6/KEEN5C/GFXC_CK5.H
new file mode 100755 (executable)
index 0000000..95d639e
--- /dev/null
@@ -0,0 +1,690 @@
+/* Reconstructed Commander Keen 4-6 Source Code\r
+ * Copyright (C) 2021 K1n9_Duk3\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License along\r
+ * with this program; if not, write to the Free Software Foundation, Inc.,\r
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+ */\r
+\r
+#ifndef __GFX_H__\r
+#define __GFX_H__\r
+\r
+//#include "VERSION.H"\r
+\r
+//////////////////////////////////////\r
+//\r
+// Graphics .H file for .CK5\r
+// not IGRAB-ed :)\r
+//\r
+//////////////////////////////////////\r
+\r
+//\r
+// Lump creation macros\r
+//\r
+\r
+#define START_LUMP(actualname, dummyname) actualname, dummyname=actualname-1,\r
+#define END_LUMP(actualname, dummyname) dummyname, actualname=dummyname-1,\r
+\r
+//\r
+// Amount of each data item\r
+//\r
+\r
+//common numbers:\r
+#define NUMCHUNKS    NUMGRCHUNKS\r
+#define NUMFONT      2\r
+#define NUMFONTM     0\r
+#define NUMPICM      3\r
+#define NUMTILE8     108       // BUG: only 104 tiles exist in EGAGRAPH!\r
+#define NUMTILE8M    36                // BUG: only 20 tiles exist in EGAGRAPH!\r
+#define NUMTILE32    0\r
+#define NUMTILE32M   0\r
+\r
+//episode-specific numbers:\r
+#define NUMPICS      94\r
+#define NUMSPRITES   346\r
+#define NUMTILE16    1512\r
+#define NUMTILE16M   2952\r
+#define NUMEXTERNS   15\r
+\r
+//\r
+// File offsets for data items\r
+//\r
+#define STRUCTPIC    0\r
+#define STRUCTPICM   1\r
+#define STRUCTSPRITE 2\r
+\r
+#define STARTFONT    3\r
+#define STARTFONTM   (STARTFONT+NUMFONT)\r
+#define STARTPICS    (STARTFONTM+NUMFONTM)\r
+#define STARTPICM    (STARTPICS+NUMPICS)\r
+#define STARTSPRITES (STARTPICM+NUMPICM)\r
+#define STARTTILE8   (STARTSPRITES+NUMSPRITES)\r
+#define STARTTILE8M  (STARTTILE8+1)\r
+#define STARTTILE16  (STARTTILE8M+1)\r
+#define STARTTILE16M (STARTTILE16+NUMTILE16)\r
+#define STARTTILE32  (STARTTILE16M+NUMTILE16M)\r
+#define STARTTILE32M (STARTTILE32+NUMTILE32)\r
+#define STARTEXTERNS (STARTTILE32M+NUMTILE32M)\r
+\r
+typedef enum {\r
+       // Lump Start\r
+\r
+       LASTFONT=STARTPICS-1,\r
+\r
+       PADDINGPIC,                  // 5 (compensate for the missing Star Wars font to give the other pics the correct chunk numbers)\r
+\r
+       START_LUMP(HELP_LUMP_START, __HELPSTART)\r
+       H_HELPPIC,                   // 6\r
+       H_LARROWPIC,                 // 7\r
+       H_RARROWPIC,                 // 8\r
+       H_ESCPIC,                    // 9\r
+       H_ENTERPIC,                  // 10\r
+       H_BOTTOMINSTRPIC,            // 11\r
+       H_GUMPIC,                    // 12\r
+       H_MARSHMALLOWPIC,            // 13\r
+       H_CHOCMILKPIC,               // 14\r
+       H_TARTSTIXPIC,               // 15\r
+       H_STOOPIESPIC,               // 16\r
+       H_SUGARPIC,                  // 17\r
+       H_VITALINPIC,                // 18\r
+       H_STUNNERPIC,                // 19\r
+       H_GEMPIC,                    // 20\r
+       H_KEGPIC,                    // 21\r
+       H_ENDOFTEXTPIC,              // 22\r
+       H_HELPMENUPIC,               // 23\r
+       H_HANDPIC,                   // 24\r
+       H_ARROWSENTERESCPIC,         // 25\r
+       H_FLASHARROW1PIC,            // 26\r
+       H_FLASHARROW2PIC,            // 27\r
+       H_TOPWINDOWPIC,              // 28\r
+       H_LEFTWINDOWPIC,             // 29\r
+       H_RIGHTWINDOWPIC,            // 30\r
+       H_BOTTOMINFOPIC,             // 31\r
+       H_BOTTOMWINDOWPIC,           // 32\r
+       H_BARPIC,                    // 33\r
+       H_SPARKYPIC,                 // 34\r
+       H_AMPTONPIC,                 // 35\r
+       H_SLICESTARPIC,              // 36\r
+       H_VOLTEFACEPIC,              // 37\r
+       H_ROBOREDPIC,                // 38\r
+       H_SHELLEYPIC,                // 39\r
+       H_SPIROGRIPPIC,              // 40\r
+       H_MINEPIC,                   // 41\r
+       H_SPINDREDPIC,               // 42\r
+       H_SHIKADIPIC,                // 43\r
+       H_SPHEREFULPIC,              // 44\r
+       H_PETPIC,                    // 45\r
+       H_MASTERPIC,                 // 46\r
+       H_IDLOGOPIC,                 // 47\r
+       H_STORY1PIC,                 // 48\r
+       H_STORY2PIC,                 // 49\r
+       H_STORY3PIC,                 // 50\r
+       H_STORY4PIC,                 // 51\r
+       H_VISAPIC,                   // 52\r
+       H_MCPIC,                     // 53\r
+       H_KEENTHUMBSUPPIC,           // 54\r
+       H_END1PIC,                   // 55\r
+       H_END2PIC,                   // 56\r
+       H_END3PIC,                   // 57\r
+       H_END4PIC,                   // 58\r
+       H_END5PIC,                   // 59\r
+       H_END6PIC,                   // 60\r
+       H_END7PIC,                   // 61\r
+       H_END8PIC,                   // 62\r
+       H_CONGRATSPIC,               // 63\r
+       H_KEENFEEDSPIC,              // 64\r
+       H_DOORCARDPIC,               // 65\r
+       H_KEEN6PIC,                  // 66\r
+       END_LUMP(HELP_LUMP_END, __HELPEND)\r
+\r
+       START_LUMP(CONTROLS_LUMP_START, __CONTROLSSTART)\r
+       CP_MAINMENUPIC,              // 67\r
+       CP_NEWGAMEMENUPIC,           // 68\r
+       CP_LOADMENUPIC,              // 69\r
+       CP_SAVEMENUPIC,              // 70\r
+       CP_CONFIGMENUPIC,            // 71\r
+       CP_SOUNDMENUPIC,             // 72\r
+       CP_MUSICMENUPIC,             // 73\r
+       CP_KEYBOARDMENUPIC,          // 74\r
+       CP_KEYMOVEMENTPIC,           // 75\r
+       CP_KEYBUTTONPIC,             // 76\r
+       CP_JOYSTICKMENUPIC,          // 77\r
+       CP_OPTIONSMENUPIC,           // 78\r
+       CP_PADDLEWARPIC,             // 79\r
+       CP_QUITPIC,                  // 80\r
+       CP_JOYSTICKPIC,              // 81\r
+       CP_MENUSCREENPIC,            // 82\r
+       END_LUMP(CONTROLS_LUMP_END, __COLTROLSEND)\r
+\r
+       START_LUMP(_LUMP_START, __START)\r
+       IDSOFTPIC,                   // 83\r
+       PROGTEAMPIC,                 // 84\r
+       ARTISTPIC,                   // 85\r
+       DIRECTORPIC,                 // 86\r
+       SW_BACKGROUNDPIC,            // 87\r
+       TITLEPICPIC,                 // 88\r
+       MILKYWAYPIC,                 // 89\r
+       END_LUMP(_LUMP_END, __END)\r
+\r
+       START_LUMP(KEENTALK_LUMP_START, __KEENTALKSTART)\r
+       KEENTALK1PIC,                // 90\r
+       KEENTALK2PIC,                // 91\r
+       END_LUMP(KEENTALK_LUMP_END, __KEENTALKEND)\r
+\r
+       START_LUMP(LOADING_LUMP_START, __LOADINGSTART)\r
+       KEENCOUNT1PIC,               // 92\r
+       KEENCOUNT2PIC,               // 93\r
+       KEENCOUNT3PIC,               // 94\r
+       KEENCOUNT4PIC,               // 95\r
+       KEENCOUNT5PIC,               // 96\r
+       KEENCOUNT6PIC,               // 97\r
+       END_LUMP(LOADING_LUMP_END, __LOADINGEND)\r
+\r
+       GAMEOVERPIC,                 // 98\r
+\r
+       CP_MENUMASKPICM,             // 99\r
+       CORDPICM,                    // 100\r
+       METALPOLEPICM,               // 101\r
+\r
+       //\r
+       // SPRITES\r
+       //\r
+\r
+       START_LUMP(PADDLE_LUMP_START, __PADDLESTART)\r
+       PADDLESPR,                   // 102\r
+       BALLSPR,                     // 103\r
+       BALL1PIXELTOTHERIGHTSPR,     // 104\r
+       BALL2PIXELSTOTHERIGHTSPR,    // 105\r
+       BALL3PIXELSTOTHERIGHTSPR,    // 106\r
+       END_LUMP(PADDLE_LUMP_END, __PADDLEEND)\r
+\r
+       DEMOPLAQUESPR,               // 107\r
+\r
+       START_LUMP(KEEN_LUMP_START, __KEENSTART)\r
+       KEENSTANDRSPR,               // 108\r
+       KEENRUNR1SPR,                // 109\r
+       KEENRUNR2SPR,                // 110\r
+       KEENRUNR3SPR,                // 111\r
+       KEENRUNR4SPR,                // 112\r
+       KEENJUMPR1SPR,               // 113\r
+       KEENJUMPR2SPR,               // 114\r
+       KEENJUMPR3SPR,               // 115\r
+       KEENSTANDLSPR,               // 116\r
+       KEENRUNL1SPR,                // 117\r
+       KEENRUNL2SPR,                // 118\r
+       KEENRUNL3SPR,                // 119\r
+       KEENRUNL4SPR,                // 120\r
+       KEENJUMPL1SPR,               // 121\r
+       KEENJUMPL2SPR,               // 122\r
+       KEENJUMPL3SPR,               // 123\r
+       KEENLOOKUSPR,                // 124\r
+       KEENWAITR1SPR,               // 125\r
+       KEENWAITR2SPR,               // 126\r
+       KEENWAITR3SPR,               // 127\r
+       KEENSITREAD1SPR,             // 128\r
+       KEENSITREAD2SPR,             // 129\r
+       KEENSITREAD3SPR,             // 130\r
+       KEENSITREAD4SPR,             // 131\r
+       KEENREAD1SPR,                // 132\r
+       KEENREAD2SPR,                // 133\r
+       KEENREAD3SPR,                // 134\r
+       KEENSTOPREAD1SPR,            // 135\r
+       KEENSTOPREAD2SPR,            // 136\r
+       KEENLOOKD1SPR,               // 137\r
+       KEENLOOKD2SPR,               // 138\r
+       KEENONPLATSPR,               // 139\r
+       KEENDIE1SPR,                 // 140\r
+       KEENDIE2SPR,                 // 141\r
+       KEENSTUNSPR,                 // 142\r
+       STUNSTARS1SPR,               // 143\r
+       STUNSTARS2SPR,               // 144\r
+       STUNSTARS3SPR,               // 145\r
+       KEENSHOOTLSPR,               // 146\r
+       KEENJLSHOOTLSPR,             // 147\r
+       KEENJSHOOTDSPR,              // 148\r
+       KEENJSHOOTUSPR,              // 149\r
+       KEENSHOOTUSPR,               // 150\r
+       KEENSHOOTRSPR,               // 151\r
+       KEENJRSHOOTRSPR,             // 152\r
+       STUN1SPR,                    // 153\r
+       STUN2SPR,                    // 154\r
+       STUN3SPR,                    // 155\r
+       STUN4SPR,                    // 156\r
+       STUNHIT1SPR,                 // 157\r
+       STUNHIT2SPR,                 // 158\r
+       KEENSHINNYR1SPR,             // 159\r
+       KEENSHINNYR2SPR,             // 160\r
+       KEENSHINNYR3SPR,             // 161\r
+       KEENSLIDED1SPR,              // 162\r
+       KEENSLIDED2SPR,              // 163\r
+       KEENSLIDED3SPR,              // 164\r
+       KEENSLIDED4SPR,              // 165\r
+       KEENSHINNYL1SPR,             // 166\r
+       KEENSHINNYL2SPR,             // 167\r
+       KEENSHINNYL3SPR,             // 168\r
+       KEENPLSHOOTUSPR,             // 169\r
+       KEENPRSHOOTUSPR,             // 170\r
+       KEENPRSHOOTDSPR,             // 171\r
+       KEENPLSHOOTDSPR,             // 172\r
+       KEENPSHOOTLSPR,              // 173\r
+       KEENPSHOOTRSPR,              // 174\r
+       KEENENTER1SPR,               // 175\r
+       KEENENTER2SPR,               // 176\r
+       KEENENTER3SPR,               // 177\r
+       KEENENTER4SPR,               // 178\r
+       KEENENTER5SPR,               // 179\r
+       KEENHANGLSPR,                // 180\r
+       KEENHANGRSPR,                // 181\r
+       KEENCLIMBEDGEL1SPR,          // 182\r
+       KEENCLIMBEDGEL2SPR,          // 183\r
+       KEENCLIMBEDGEL3SPR,          // 184\r
+       KEENCLIMBEDGEL4SPR,          // 185\r
+       KEENCLIMBEDGER1SPR,          // 186\r
+       KEENCLIMBEDGER2SPR,          // 187\r
+       KEENCLIMBEDGER3SPR,          // 188\r
+       KEENCLIMBEDGER4SPR,          // 189\r
+       KEENPOGOR1SPR,               // 190\r
+       KEENPOGOR2SPR,               // 191\r
+       KEENPOGOL1SPR,               // 192\r
+       KEENPOGOL2SPR,               // 193\r
+       BONUS100UPSPR,               // 194\r
+       BONUS100SPR,                 // 195\r
+       BONUS200SPR,                 // 196\r
+       BONUS500SPR,                 // 197\r
+       BONUS1000SPR,                // 198\r
+       BONUS2000SPR,                // 199\r
+       BONUS5000SPR,                // 200\r
+       BONUS1UPSPR,                 // 201\r
+       BONUSCLIPSPR,                // 202\r
+       VIVAPOOF1SPR,                // 203\r
+       VIVAPOOF2SPR,                // 204\r
+       VIVAPOOF3SPR,                // 205\r
+       VIVAPOOF4SPR,                // 206\r
+       END_LUMP(KEEN_LUMP_END, __KEENEND)\r
+\r
+       START_LUMP(KEYCARD_LUMP_START, __KEYCARDSTART)\r
+       DOORCARD1SPR,                // 207\r
+       DOORCARD2SPR,                // 208\r
+       BONUSCARDSPR,                // 209\r
+       END_LUMP(KEYCARD_LUMP_END, __KEYCARDEND)\r
+\r
+       START_LUMP(SUGAR1_LUMP_START, __SUGAR1START)\r
+       SUGAR1ASPR,                  // 210\r
+       SUGAR1BSPR,                  // 211\r
+       END_LUMP(SUGAR1_LUMP_END, __SUGAR1END)\r
+\r
+       START_LUMP(SUGAR2_LUMP_START, __SUGAR2START)\r
+       SUGAR2ASPR,                  // 212\r
+       SUGAR2BSPR,                  // 213\r
+       END_LUMP(SUGAR2_LUMP_END, __SUGAR2END)\r
+\r
+       START_LUMP(SUGAR3_LUMP_START, __SUGAR3START)\r
+       SUGAR3ASPR,                  // 214\r
+       SUGAR3BSPR,                  // 215\r
+       END_LUMP(SUGAR3_LUMP_END, __SUGAR3END)\r
+\r
+       START_LUMP(SUGAR4_LUMP_START, __SUGAR4START)\r
+       SUGAR4ASPR,                  // 216\r
+       SUGAR4BSPR,                  // 217\r
+       END_LUMP(SUGAR4_LUMP_END, __SUGAR4END)\r
+\r
+       START_LUMP(SUGAR5_LUMP_START, __SUGAR5START)\r
+       SUGAR5ASPR,                  // 218\r
+       SUGAR5BSPR,                  // 219\r
+       END_LUMP(SUGAR5_LUMP_END, __SUGAR5END)\r
+\r
+       START_LUMP(SUGAR6_LUMP_START, __SUGAR6START)\r
+       SUGAR6ASPR,                  // 220\r
+       SUGAR6BSPR,                  // 221\r
+       END_LUMP(SUGAR6_LUMP_END, __SUGAR6END)\r
+\r
+       START_LUMP(ONEUP_LUMP_START, __ONEUPSTART)\r
+       ONEUPASPR,                   // 222\r
+       ONEUPBSPR,                   // 223\r
+       END_LUMP(ONEUP_LUMP_END, __ONEUPEND)\r
+\r
+       START_LUMP(KEYGEM_LUMP_START, __KEYGEMSTART)\r
+       REDGEM1SPR,                  // 224\r
+       REDGEM2SPR,                  // 225\r
+       YELLOWGEM1SPR,               // 226\r
+       YELLOWGEM2SPR,               // 227\r
+       BLUEGEM1SPR,                 // 228\r
+       BLUEGEM2SPR,                 // 229\r
+       GREENGEM1SPR,                // 230\r
+       GREENGEM2SPR,                // 231\r
+       BONUSGEMSPR,                 // 232\r
+       END_LUMP(KEYGEM_LUMP_END, __KEYGEMEND)\r
+\r
+       START_LUMP(AMMO_LUMP_START, __AMMOSTART)\r
+       STUNCLIP1SPR,                // 233\r
+       STUNCLIP2SPR,                // 234\r
+       END_LUMP(AMMO_LUMP_END, __AMMOEND)\r
+\r
+       SCOREBOXSPR,                 // 235\r
+\r
+       START_LUMP(LASER_LUMP_START, __LASERSTART)\r
+       LASER1SPR,                   // 236\r
+       LASER2SPR,                   // 237\r
+       LASER3SPR,                   // 238\r
+       LASER4SPR,                   // 239\r
+       LASERHIT1SPR,                // 240\r
+       LASERHIT2SPR,                // 241\r
+       END_LUMP(LASER_LUMP_END, __LASEREND)\r
+\r
+       START_LUMP(WORLDKEEN_LUMP_START, __WORLDKEENSTART)\r
+       WORLDKEENL1SPR,              // 242\r
+       WORLDKEENL2SPR,              // 243\r
+       WORLDKEENL3SPR,              // 244\r
+       WORLDKEENR1SPR,              // 245\r
+       WORLDKEENR2SPR,              // 246\r
+       WORLDKEENR3SPR,              // 247\r
+       WORLDKEENU1SPR,              // 248\r
+       WORLDKEENU2SPR,              // 249\r
+       WORLDKEENU3SPR,              // 250\r
+       WORLDKEEND1SPR,              // 251\r
+       WORLDKEEND2SPR,              // 252\r
+       WORLDKEEND3SPR,              // 253\r
+       WORLDKEENDR1SPR,             // 254\r
+       WORLDKEENDR2SPR,             // 255\r
+       WORLDKEENDR3SPR,             // 256\r
+       WORLDKEENDL1SPR,             // 257\r
+       WORLDKEENDL2SPR,             // 258\r
+       WORLDKEENDL3SPR,             // 259\r
+       WORLDKEENUL1SPR,             // 260\r
+       WORLDKEENUL2SPR,             // 261\r
+       WORLDKEENUL3SPR,             // 262\r
+       WORLDKEENUR1SPR,             // 263\r
+       WORLDKEENUR2SPR,             // 264\r
+       WORLDKEENUR3SPR,             // 265\r
+       WORLDKEENWAVE1SPR,           // 266\r
+       WORLDKEENWAVE2SPR,           // 267\r
+       FLAGFLIP1SPR,                // 268\r
+       FLAGFLIP2SPR,                // 269\r
+       FLAGFLIP3SPR,                // 270\r
+       FLAGFLIP4SPR,                // 271\r
+       FLAGFLIP5SPR,                // 272\r
+       FLAGFALL1SPR,                // 273\r
+       FLAGFALL2SPR,                // 274\r
+       FLAGFLAP1SPR,                // 275\r
+       FLAGFLAP2SPR,                // 276\r
+       FLAGFLAP3SPR,                // 277\r
+       FLAGFLAP4SPR,                // 278\r
+       SHOOTINGSTAR1SPR,            // 279\r
+       SHOOTINGSTAR2SPR,            // 280\r
+       WORLDTELSPARK1SPR,           // 281\r
+       WORLDTELSPARK2SPR,           // 282\r
+       END_LUMP(WORLDKEEN_LUMP_END, __WORLDKEENEND)\r
+\r
+       START_LUMP(FUSE_LUMP_START, __FUSESTART)\r
+       FUSEFLASH1SPR,               // 283\r
+       FUSEFLASH2SPR,               // 284\r
+       FUSEFLASH3SPR,               // 285\r
+       END_LUMP(FUSE_LUMP_END, __FUSEEND)\r
+\r
+       START_LUMP(STAREXPLODE_LUMP_START, __SMALLSPARKSTART)\r
+       STAREXPLODE1SPR,             // 286\r
+       STAREXPLODE2SPR,             // 287\r
+       STAREXPLODE3SPR,             // 288\r
+       STAREXPLODE4SPR,             // 289\r
+       END_LUMP(STAREXPLODE_LUMP_END, __SMALLSPARKEND)\r
+\r
+       START_LUMP(TELEPORT_LUMP_START, __TELEPORTSTART)\r
+       TELEPORTSPARK1SPR,           // 290\r
+       TELEPORTSPARK2SPR,           // 291\r
+       TELEPORTZAP1SPR,             // 292\r
+       TELEPORTZAP2SPR,             // 293\r
+       END_LUMP(TELEPORT_LUMP_END, __TELEPORTEND)\r
+\r
+       START_LUMP(SCOTTIE_LUMP_START, __KORATHSTART)\r
+       SCOTTIEWALKL1SPR,            // 294\r
+       SCOTTIEWALKL2SPR,            // 295\r
+       SCOTTIEWALKL3SPR,            // 296\r
+       SCOTTIEWALKL4SPR,            // 297\r
+       SCOTTIEWALKR1SPR,            // 298\r
+       SCOTTIEWALKR2SPR,            // 299\r
+       SCOTTIEWALKR3SPR,            // 300\r
+       SCOTTIEWALKR4SPR,            // 301\r
+       SCOTTIEFACESPR,              // 302\r
+       SCOTTIESTUNSPR,              // 303\r
+       END_LUMP(SCOTTIE_LUMP_END, __KORATHEND)\r
+\r
+       START_LUMP(MASTER_LUMP_START, __MASTERSTART)\r
+       MASTER1SPR,                  // 304\r
+       MASTER2SPR,                  // 305\r
+       MASTER3SPR,                  // 306\r
+       MASTER4SPR,                  // 307\r
+       MASTERTELEPORT1SPR,          // 308\r
+       MASTERTELEPORT2SPR,          // 309\r
+       SHIKMASTERCASTRSPR,          // 310\r
+       SHIKMASTERCASTLSPR,          // 311\r
+       MASTERFLOORSPARK1SPR,        // 312\r
+       MASTERFLOORSPARK2SPR,        // 313\r
+       MASTERFLOORSPARK3SPR,        // 314\r
+       MASTERFLOORSPARK4SPR,        // 315\r
+       MASTERSHOT1SPR,              // 316\r
+       MASTERSHOT2SPR,              // 317\r
+       MASTERSHOT3SPR,              // 318\r
+       MASTERSHOT4SPR,              // 319\r
+       END_LUMP(MASTER_LUMP_END, __MASTEREND)\r
+\r
+       START_LUMP(SHIKADI_LUMP_START, __SHIKADISTART)\r
+       SHIKADI1SPR,                 // 320\r
+       SHIKADI2SPR,                 // 321\r
+       SHIKADI3SPR,                 // 322\r
+       SHIKADI4SPR,                 // 323\r
+       SHIKADIGRABRSPR,             // 324\r
+       SHIKADIGRABLSPR,             // 325\r
+       SHIKADIPOLESPARK1SPR,        // 326\r
+       SHIKADIPOLESPARK2SPR,        // 327\r
+       SHIKADIWALKR1SPR,            // 328\r
+       SHIKADIWALKR2SPR,            // 329\r
+       SHIKADIWALKR3SPR,            // 330\r
+       SHIKADIWALKR4SPR,            // 331\r
+       SHIKADIWALKL1SPR,            // 332\r
+       SHIKADIWALKL2SPR,            // 333\r
+       SHIKADIWALKL3SPR,            // 334\r
+       SHIKADIWALKL4SPR,            // 335\r
+       SHIKADISTUNSPR,              // 336\r
+       END_LUMP(SHIKADI_LUMP_END, __SHIKADIEND)\r
+\r
+       START_LUMP(SHOCKSHUND_LUMP_START, __SHOCKSHUNDSTART)\r
+       PETSIT1SPR,                  // 337\r
+       PETSIT2SPR,                  // 338\r
+       PETRUNR1SPR,                 // 339\r
+       PETRUNR2SPR,                 // 340\r
+       PETRUNR3SPR,                 // 341\r
+       PETRUNR4SPR,                 // 342\r
+       PETRUNL1SPR,                 // 343\r
+       PETRUNL2SPR,                 // 344\r
+       PETRUNL3SPR,                 // 345\r
+       PETRUNL4SPR,                 // 346\r
+       PETJUMPLSPR,                 // 347\r
+       PETJUMPRSPR,                 // 348\r
+       PETBARKR1SPR,                // 349\r
+       PETBARKR2SPR,                // 350\r
+       PETBARKL1SPR,                // 351\r
+       PETBARKL2SPR,                // 352\r
+       PETSTUNSPR,                  // 353\r
+       PETSPARK1SPR,                // 354\r
+       PETSPARK2SPR,                // 355\r
+       PETSPARKHIT1SPR,             // 356\r
+       PETSPARKHIT2SPR,             // 357\r
+       END_LUMP(SHOCKSHUND_LUMP_END, __SHOCKSHUNDEND)\r
+\r
+       START_LUMP(SPHEREFUL_LUMP_START, __SPHEREFULSTART)\r
+       SPHEREFUL1SPR,               // 358\r
+       SPHEREFUL2SPR,               // 359\r
+       SPHEREFUL3SPR,               // 360\r
+       SPHEREFUL4SPR,               // 361\r
+       SPHEREGUARD1SPR,             // 362\r
+       SPHEREGUARD2SPR,             // 363\r
+       SPHEREGUARD3SPR,             // 364\r
+       SPHEREGUARD4SPR,             // 365\r
+       END_LUMP(SPHEREFUL_LUMP_END, __SPHEREFULEND)\r
+\r
+       START_LUMP(SPARKY_LUMP_START, __SPARKYSTART)\r
+       SPARKYWALKL1SPR,             // 366\r
+       SPARKYWALKL2SPR,             // 367\r
+       SPARKYWALKL3SPR,             // 368\r
+       SPARKYWALKL4SPR,             // 369\r
+       SPARKYTURN1SPR,              // 370\r
+       SPARKYTURN2SPR,              // 371\r
+       SPARKYTURN3SPR,              // 372\r
+       SPARKYWALKR1SPR,             // 373\r
+       SPARKYWALKR2SPR,             // 374\r
+       SPARKYWALKR3SPR,             // 375\r
+       SPARKYWALKR4SPR,             // 376\r
+       SPARKYSTUNSPR,               // 377\r
+       END_LUMP(SPARKY_LUMP_END, __SPARKYEND)\r
+\r
+       START_LUMP(MINE_LUMP_START, __MINESTART)\r
+       SHIKADIMINESPR,              // 378\r
+       SHIKADIMINEEYESPR,           // 379\r
+       SHIKADIMINEPULSE1SPR,        // 380\r
+       SHIKADIMINEPULSE2SPR,        // 381\r
+       SHIKADIMINEBOOM1SPR,         // 382\r
+       SHIKADIMINEBOOM2SPR,         // 383\r
+       SHIKADIMINEPIECESPR,         // 384\r
+       END_LUMP(MINE_LUMP_END, __MINEEND)\r
+\r
+       START_LUMP(SLICESTAR_LUMP_START, __SLICESTARSTART)\r
+       SLICESTARSPR,                // 385\r
+       SLICESTARBOOMSPR,            // 386\r
+       END_LUMP(SLICESTAR_LUMP_END, __SLICASTAREND)\r
+\r
+       START_LUMP(ROBORED_LUMP_START, __ROBOREDSTART)\r
+       ROBOREDRSPR,                 // 387\r
+       ROBOREDLSPR,                 // 388\r
+       ROBOSHOT1SPR,                // 389\r
+       ROBOSHOT2SPR,                // 390\r
+       ROBOSHOTHIT1SPR,             // 391\r
+       ROBOSHOTHIT2SPR,             // 392\r
+       END_LUMP(ROBORED_LUMP_END, __ROBOREDEND)\r
+\r
+       START_LUMP(SPIRO_LUMP_START, __SPIROSTART)\r
+       SPIROSITDSPR,                // 393\r
+       SPIROSITLSPR,                // 394\r
+       SPIROSITUSPR,                // 395\r
+       SPIROSITRSPR,                // 396\r
+       SPIROSPINULSPR,              // 397\r
+       SPIROSPINURSPR,              // 398\r
+       SPIROSPINDRSPR,              // 399\r
+       SPIROSPINDLSPR,              // 400\r
+       SPIROSPINDSPR,               // 401\r
+       SPIROSPINLSPR,               // 402\r
+       SPIROSPINUSPR,               // 403\r
+       SPIROSPINRSPR,               // 404\r
+       END_LUMP(SPIRO_LUMP_END, __SPIROEND)\r
+\r
+       START_LUMP(AMPTON_LUMP_START, __AMPTONSTART)\r
+       AMPTONWALKR1SPR,             // 405\r
+       AMPTONWALKR2SPR,             // 406\r
+       AMPTONWALKR3SPR,             // 407\r
+       AMPTONWALKR4SPR,             // 408\r
+       AMPTONFACESPR,               // 409\r
+       AMPTONGRAB1SPR,              // 410\r
+       AMPTONGRAB2SPR,              // 411\r
+       AMTONWALKL1SPR,              // 412\r
+       AMTONWALKL2SPR,              // 413\r
+       AMTONWALKL3SPR,              // 414\r
+       AMTONWALKL4SPR,              // 415\r
+       AMPTONSTUNSPR,               // 416\r
+       END_LUMP(AMPTON_LUMP_END, __AMPTONEND)\r
+\r
+       START_LUMP(VOLTE_LUMP_START, __VOLTESTART)\r
+       VOLTEFACE1SPR,               // 417\r
+       VOLTEFACE2SPR,               // 418\r
+       VOLTEFACE3SPR,               // 419\r
+       VOLTEFACE4SPR,               // 420\r
+       VOLTEFACESTUNSPR,            // 421\r
+       END_LUMP(VOLTE_LUMP_END, __VOLTEEND)\r
+\r
+       START_LUMP(SLOTPLAT_LUMP_START, __PINKPLATSTART)\r
+       SLOTPLAT1SPR,                // 422\r
+       SLOTPLAT2SPR,                // 423\r
+       END_LUMP(SLOTPLAT_LUMP_END, __PINKPLATEND)\r
+\r
+       START_LUMP(SPINDRED_LUMP_START, __SPINDREDSTART)\r
+       SPINDRED1SPR,                // 424\r
+       SPINDRED2SPR,                // 425\r
+       SPINDRED3SPR,                // 426\r
+       SPINDRED4SPR,                // 427\r
+       END_LUMP(SPINDRED_LUMP_END, __SPINDREDEND)\r
+\r
+       START_LUMP(SHELLEY_LUMP_START, __SHELLEYSTART)\r
+       SHELLEYR1SPR,                // 428\r
+       SHELLEYR2SPR,                // 429\r
+       SHELLEYR3SPR,                // 430\r
+       SHELLEYR4SPR,                // 431\r
+       SHELLEYL1SPR,                // 432\r
+       SHELLEYL2SPR,                // 433\r
+       SHELLEYL3SPR,                // 434\r
+       SHELLEYL4SPR,                // 435\r
+       SHELLEYJUMPRSPR,             // 436\r
+       SHELLEYFALLRSPR,             // 437\r
+       SHELLEYJUMPLSPR,             // 438\r
+       SHELLEYFALLLSPR,             // 439\r
+       SHELLEYBOOM1SPR,             // 440\r
+       SHELLEYBOOM2SPR,             // 441\r
+       SHELLEYBOOM3SPR,             // 442\r
+       SHELLEYBOOM4SPR,             // 443\r
+       SHELLEYPIECE1SPR,            // 444\r
+       SHELLEYPIECE2SPR,            // 445\r
+       END_LUMP(SHELLEY_LUMP_END, __SHELLEYEND)\r
+\r
+       START_LUMP(PLATFORM_LUMP_START, __PLATFORMSTART)\r
+       PLATFORMSPR,                 // 446\r
+       END_LUMP(PLATFORM_LUMP_END, __PLATFORMEND)\r
+\r
+       START_LUMP(MINIPLAT_LUMP_START, __MINIPLATSTART)\r
+       MINIPLATSPR,                // 447\r
+       END_LUMP(MINIPLAT_LUMP_END, __MINIPLATEND)\r
+\r
+\r
+       //\r
+       // TILES (these don't need names)\r
+       //\r
+\r
+       LASTTILE=STARTEXTERNS-1,\r
+\r
+       //\r
+       // EXTERNS\r
+       //\r
+\r
+       //texts\r
+       T_HELPART,                   // 4914\r
+       T_CONTRART,                  // 4915\r
+       T_STORYART,                  // 4916\r
+       T_IDART,                     // 4917\r
+       T_ENDART,                    // 4918\r
+       T_ENDART2,                   // 4919\r
+       T_ORDERART,                  // 4920\r
+\r
+       ORDERSCREEN,                 // 4921\r
+       OUTOFMEM,                    // 4922\r
+       GALAXY,                      // 4923\r
+\r
+       //demos\r
+       DEMO0,                       // 4924\r
+       DEMO1,                       // 4925\r
+       DEMO2,                       // 4926\r
+       DEMO3,                       // 4927\r
+       DEMO4,                       // 4928\r
+\r
+       NUMGRCHUNKS\r
+} graphicnums;\r
+\r
+#undef START_LUMP\r
+#undef END_LUMP\r
+\r
+#endif //__GFX_H__
\ No newline at end of file
diff --git a/16/keen456/KEEN4-6/KEEN5C/ID_ASM.EQU b/16/keen456/KEEN4-6/KEEN5C/ID_ASM.EQU
new file mode 100755 (executable)
index 0000000..b45d7ca
--- /dev/null
@@ -0,0 +1,115 @@
+;\r
+; Equates for all .ASM files\r
+;\r
+\r
+;----------------------------------------------------------------------------\r
+\r
+INCLUDE        "GFXC_CK5.EQU"\r
+\r
+;----------------------------------------------------------------------------\r
+\r
+CGAGR          =       1\r
+EGAGR          =       2\r
+VGAGR          =       3\r
+\r
+GRMODE         =       CGAGR\r
+PROFILE                =       0                       ; 1=keep stats on tile drawing\r
+\r
+SC_INDEX       =       03C4h\r
+SC_RESET       =       0\r
+SC_CLOCK       =       1\r
+SC_MAPMASK     =       2\r
+SC_CHARMAP     =       3\r
+SC_MEMMODE     =       4\r
+\r
+CRTC_INDEX     =       03D4h\r
+CRTC_H_TOTAL   =       0\r
+CRTC_H_DISPEND =       1\r
+CRTC_H_BLANK   =       2\r
+CRTC_H_ENDBLANK        =       3\r
+CRTC_H_RETRACE =       4\r
+CRTC_H_ENDRETRACE =    5\r
+CRTC_V_TOTAL   =       6\r
+CRTC_OVERFLOW  =       7\r
+CRTC_ROWSCAN   =       8\r
+CRTC_MAXSCANLINE =     9\r
+CRTC_CURSORSTART =     10\r
+CRTC_CURSOREND =       11\r
+CRTC_STARTHIGH =       12\r
+CRTC_STARTLOW  =       13\r
+CRTC_CURSORHIGH        =       14\r
+CRTC_CURSORLOW =       15\r
+CRTC_V_RETRACE =       16\r
+CRTC_V_ENDRETRACE =    17\r
+CRTC_V_DISPEND =       18\r
+CRTC_OFFSET    =       19\r
+CRTC_UNDERLINE =       20\r
+CRTC_V_BLANK   =       21\r
+CRTC_V_ENDBLANK        =       22\r
+CRTC_MODE      =       23\r
+CRTC_LINECOMPARE =     24\r
+\r
+\r
+GC_INDEX       =       03CEh\r
+GC_SETRESET    =       0\r
+GC_ENABLESETRESET =    1\r
+GC_COLORCOMPARE        =       2\r
+GC_DATAROTATE  =       3\r
+GC_READMAP     =       4\r
+GC_MODE                =       5\r
+GC_MISCELLANEOUS =     6\r
+GC_COLORDONTCARE =     7\r
+GC_BITMASK     =       8\r
+\r
+ATR_INDEX      =       03c0h\r
+ATR_MODE       =       16\r
+ATR_OVERSCAN   =       17\r
+ATR_COLORPLANEENABLE = 18\r
+ATR_PELPAN     =       19\r
+ATR_COLORSELECT        =       20\r
+\r
+STATUS_REGISTER_1     =        03dah\r
+\r
+\r
+MACRO  WORDOUT\r
+       out     dx,ax\r
+ENDM\r
+\r
+if 0\r
+\r
+MACRO  WORDOUT\r
+       out     dx,al\r
+       inc     dx\r
+       xchg    al,ah\r
+       out     dx,al\r
+       dec     dx\r
+       xchg    al,ah\r
+ENDM\r
+\r
+endif\r
+\r
+UPDATEWIDE     =       22\r
+UPDATEHIGH     =       14\r
+\r
+;\r
+; tile info offsets from segment tinf\r
+;\r
+\r
+ANIM           =       402\r
+SPEED          =       (ANIM+NUMTILE16)\r
+\r
+NORTHWALL      =       (SPEED+NUMTILE16)\r
+EASTWALL       =       (NORTHWALL+NUMTILE16M)\r
+SOUTHWALL   =  (EASTWALL+NUMTILE16M)\r
+WESTWALL    =  (SOUTHWALL+NUMTILE16M)\r
+MANIM       =  (WESTWALL+NUMTILE16M)\r
+INTILE      =  (MANIM+NUMTILE16M)\r
+MSPEED      =  (INTILE+NUMTILE16M)\r
+\r
+\r
+IFE GRMODE-EGAGR\r
+SCREENWIDTH    =       64\r
+ENDIF\r
+IFE GRMODE-CGAGR\r
+SCREENWIDTH    =       128\r
+ENDIF\r
diff --git a/16/keen456/KEEN4-6/KEEN5C/ID_HEADS.H b/16/keen456/KEEN4-6/KEEN5C/ID_HEADS.H
new file mode 100755 (executable)
index 0000000..210a411
--- /dev/null
@@ -0,0 +1,109 @@
+/* Reconstructed Commander Keen 4-6 Source Code\r
+ * Copyright (C) 2021 K1n9_Duk3\r
+ *\r
+ * This file is primarily based on:\r
+ * Catacomb 3-D Source Code\r
+ * Copyright (C) 1993-2014 Flat Rock Software\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License along\r
+ * with this program; if not, write to the Free Software Foundation, Inc.,\r
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+ */\r
+\r
+// ID_GLOB.H\r
+\r
+\r
+#include <ALLOC.H>\r
+#include <CTYPE.H>\r
+#include <DOS.H>\r
+#include <ERRNO.H>\r
+#include <FCNTL.H>\r
+#include <IO.H>\r
+#include <MEM.H>\r
+#include <PROCESS.H>\r
+#include <STDIO.H>\r
+#include <STDLIB.H>\r
+#include <STRING.H>\r
+#include <SYS\STAT.H>\r
+\r
+#define __ID_GLOB__\r
+\r
+//--------------------------------------------------------------------------\r
+\r
+#define KEEN\r
+#define KEEN5\r
+\r
+#define        EXTENSION       "CK5"\r
+\r
+extern char far introscn;\r
+\r
+#include "GFXC_CK5.H"\r
+#include "AUDIOCK5.H"\r
+\r
+//--------------------------------------------------------------------------\r
+\r
+#define        TEXTGR  0\r
+#define        CGAGR   1\r
+#define        EGAGR   2\r
+#define        VGAGR   3\r
+\r
+#define GRMODE CGAGR\r
+\r
+#if GRMODE == EGAGR\r
+#define GREXT  "EGA"\r
+#endif\r
+#if GRMODE == CGAGR\r
+#define GREXT  "CGA"\r
+#endif\r
+\r
+//#define PROFILE\r
+\r
+//\r
+//     ID Engine\r
+//     Types.h - Generic types, #defines, etc.\r
+//     v1.0d1\r
+//\r
+\r
+#ifndef        __TYPES__\r
+#define        __TYPES__\r
+\r
+typedef        enum    {false,true}    boolean;\r
+typedef        unsigned        char            byte;\r
+typedef        unsigned        int                     word;\r
+typedef        unsigned        long            longword;\r
+typedef        byte *                                  Ptr;\r
+\r
+typedef        struct\r
+               {\r
+                       int     x,y;\r
+               } Point;\r
+typedef        struct\r
+               {\r
+                       Point   ul,lr;\r
+               } Rect;\r
+\r
+#define        nil     ((void *)0)\r
+\r
+#endif\r
+\r
+#include "ID_MM.H"\r
+#include "ID_CA.H"\r
+#include "ID_VW.H"\r
+#include "ID_RF.H"\r
+#include "ID_IN.H"\r
+#include "ID_SD.H"\r
+#include "ID_US.H"\r
+\r
+\r
+void   Quit (char *error);             // defined in user program\r
+\r
diff --git a/16/keen456/KEEN4-6/KEEN6/AUDIOCK6.H b/16/keen456/KEEN4-6/KEEN6/AUDIOCK6.H
new file mode 100755 (executable)
index 0000000..d3407ce
--- /dev/null
@@ -0,0 +1,136 @@
+/* Reconstructed Commander Keen 4-6 Source Code\r
+ * Copyright (C) 2021 K1n9_Duk3\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License along\r
+ * with this program; if not, write to the Free Software Foundation, Inc.,\r
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+ */\r
+\r
+#ifndef __AUDIO_H__\r
+#define __AUDIO_H__\r
+\r
+//#include "VERSION.H"\r
+\r
+/////////////////////////////////////////////////\r
+//\r
+// MUSE Header for .CK6\r
+//\r
+/////////////////////////////////////////////////\r
+\r
+#define NUMSOUNDS     LASTSOUND\r
+#define NUMSNDCHUNKS  ((3*LASTSOUND)+LASTMUSIC)\r
+\r
+//\r
+// Sound names & indexes\r
+//\r
+typedef enum {\r
+       SND_WORLDWALK1,        // 0\r
+       SND_WORLDWALK2,        // 1\r
+       SND_JUMP,              // 2\r
+       SND_LAND,              // 3\r
+       SND_KEENFIRE,          // 4\r
+       SND_DROPKEY,           // 5\r
+       SND_BLORBBOUNCE,       // 6\r
+       SND_POGOBOUNCE,        // 7\r
+       SND_GETPOINTS,         // 8\r
+       SND_GETAMMO,           // 9\r
+       SND_GETWATER,          // 10\r
+       SND_11,                // 11\r
+       SND_ENTERLEVEL,        // 12\r
+       SND_LEVELDONE,         // 13\r
+       SND_NOWAY,             // 14\r
+       SND_HELMETHIT,         // 15\r
+       SND_16,                // 16\r
+       SND_EXTRAKEEN,         // 17\r
+       SND_OPENDOOR,          // 18\r
+       SND_GETKEY,            // 19\r
+       SND_PLUMMET,           // 20\r
+       SND_USESWITCH,         // 21\r
+       SND_BIPSQUISH,         // 22\r
+       SND_KEENDEAD,          // 23\r
+       SND_BIPSHIPEXPLODE,    // 24\r
+       SND_SHOTEXPLODE,       // 25\r
+       SND_BOBBAJUMP,         // 26\r
+       SND_BOBBALAND,         // 27\r
+       SND_28,                // 28\r
+       SND_ENEMYSHOT,         // 29\r
+       SND_ENEMYSHOTEXPLODE,  // 30\r
+       SND_BOBBASHOT,         // 31\r
+       SND_32,                // 32\r
+       SND_GRABSATELLITE,     // 33\r
+       SND_SHOWSTATUS,        // 34\r
+       SND_HIDESTATUS,        // 35\r
+       SND_GIKJUMP,           // 36\r
+       SND_GIKLAND,           // 37\r
+       SND_ORBATRIXBOUNCE,    // 38\r
+       SND_39,                // 39\r
+       SND_40,                // 40\r
+       SND_TELEPORT,          // 41\r
+       SND_SHOTBOUNCE,        // 42\r
+       SND_FLAGSPIN,          // 43\r
+       SND_FLAGLAND,          // 44\r
+       SND_QUESTITEM,         // 45\r
+       KEENPADDLESND,         // 46\r
+       BALLBOUNCESND,         // 47\r
+       COMPPADDLESND,         // 48\r
+       COMPSCOREDSND,         // 49\r
+       KEENSCOREDSND,         // 50\r
+       SND_CEILICKATTACK,     // 51\r
+       SND_SMASH,             // 52\r
+       SND_THROWROPE,         // 53\r
+       SND_ROCKETFLY,         // 54\r
+       SND_CEILICKLAUGH,      // 55\r
+       SND_ROCKETSTART,       // 56\r
+       SND_GRABBITER,         // 57\r
+       SND_STOMP,             // 58\r
+       SND_FLAME,             // 59\r
+       LASTSOUND\r
+} soundnames;\r
+\r
+#if LASTSOUND != 60\r
+#error bad sound enum!\r
+#endif\r
+\r
+#define NOWAYSND SND_NOWAY\r
+\r
+//\r
+// Base offsets\r
+//\r
+#define STARTPCSOUNDS     0\r
+#define STARTADLIBSOUNDS  (STARTPCSOUNDS+NUMSOUNDS)\r
+#define STARTDIGISOUNDS   (STARTADLIBSOUNDS+NUMSOUNDS)\r
+#define STARTMUSIC        (STARTDIGISOUNDS+NUMSOUNDS)\r
+\r
+//\r
+// Music names & indexes\r
+//\r
+typedef enum {\r
+       WONDER_MUS,\r
+       BRERTAR_MUS,\r
+       TOFUTURE_MUS,\r
+       FASTER_MUS,\r
+       SPACFUNK_MUS,\r
+       ALIENATE_MUS,\r
+       OMINOUS_MUS,\r
+       METAL_MUS,\r
+       MAMSNAKE_MUS,\r
+       LASTMUSIC\r
+} musicnames;\r
+\r
+/////////////////////////////////////////////////\r
+//\r
+// Thanks for playing with MUSE!\r
+//\r
+/////////////////////////////////////////////////\r
+\r
+#endif
\ No newline at end of file
diff --git a/16/keen456/KEEN4-6/KEEN6/GFXE_CK6.EQU b/16/keen456/KEEN4-6/KEEN6/GFXE_CK6.EQU
new file mode 100755 (executable)
index 0000000..d3b3d6d
--- /dev/null
@@ -0,0 +1,55 @@
+;=====================================\r
+;\r
+; Graphics .EQU file for .CK6\r
+; not IGRAB-ed :)\r
+;\r
+;=====================================\r
+\r
+;INCLUDE "VERSION.EQU"\r
+\r
+;\r
+; Amount of each data item\r
+;\r
+NUMFONT     =  3\r
+NUMFONTM    =  0\r
+NUMPICM     =  3\r
+NUMTILE8    =  108\r
+NUMTILE8M   =  36\r
+NUMTILE32   =  0\r
+NUMTILE32M  =  0\r
+\r
+;\r
+; Amount of each item in episode 6\r
+;\r
+NUMPICS     =  37\r
+NUMSPRITES  =  390\r
+NUMTILE16   =  2376\r
+NUMTILE16M  =  2736\r
+NUMEXTERN   =  11\r
+\r
+\r
+;\r
+; File offsets for data items\r
+;\r
+STRUCTPIC       =      0\r
+STRUCTPICM      =      1\r
+STRUCTSPRITE    =      2\r
+\r
+STARTFONT       =      3\r
+STARTFONTM      =      (STARTFONT+NUMFONT)\r
+STARTPICS       =      (STARTFONTM+NUMFONTM)\r
+STARTPICM       =      (STARTPICS+NUMPICS)\r
+STARTSPRITES    =      (STARTPICM+NUMPICM)\r
+STARTTILE8      =      (STARTSPRITES+NUMSPRITES)\r
+STARTTILE8M     =      (STARTTILE8+1)\r
+STARTTILE16     =      (STARTTILE8M+1)\r
+STARTTILE16M    =      (STARTTILE16+NUMTILE16)\r
+STARTTILE32     =      (STARTTILE16M+NUMTILE16M)\r
+STARTTILE32M    =      (STARTTILE32+NUMTILE32)\r
+STARTEXTERN     =      (STARTTILE32M+NUMTILE32M)\r
+\r
+NUMCHUNKS       =      (STARTEXTERN+NUMEXTERN)\r
+\r
+;\r
+; Thank you for using IGRAB!\r
+;\r
diff --git a/16/keen456/KEEN4-6/KEEN6/GFXE_CK6.H b/16/keen456/KEEN4-6/KEEN6/GFXE_CK6.H
new file mode 100755 (executable)
index 0000000..344690a
--- /dev/null
@@ -0,0 +1,670 @@
+/* Reconstructed Commander Keen 4-6 Source Code\r
+ * Copyright (C) 2021 K1n9_Duk3\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License along\r
+ * with this program; if not, write to the Free Software Foundation, Inc.,\r
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+ */\r
+\r
+#ifndef __GFX_H__\r
+#define __GFX_H__\r
+\r
+//#include "VERSION.H"\r
+\r
+//////////////////////////////////////\r
+//\r
+// Graphics .H file for .CK6\r
+// not IGRAB-ed :)\r
+//\r
+//////////////////////////////////////\r
+\r
+//\r
+// Lump creation macros\r
+//\r
+\r
+#define START_LUMP(actualname, dummyname) actualname, dummyname=actualname-1,\r
+#define END_LUMP(actualname, dummyname) dummyname, actualname=dummyname-1,\r
+\r
+//\r
+// Amount of each data item\r
+//\r
+\r
+//common numbers:\r
+#define NUMCHUNKS    NUMGRCHUNKS\r
+#define NUMFONT      3\r
+#define NUMFONTM     0\r
+#define NUMPICM      3\r
+#define NUMTILE8     108       // BUG: only 104 tiles exist in EGAGRAPH!\r
+#define NUMTILE8M    36                // BUG: only 12 tiles exist in EGAGRAPH!\r
+#define NUMTILE32    0\r
+#define NUMTILE32M   0\r
+\r
+//episode-specific numbers:\r
+#define NUMPICS      37\r
+#define NUMSPRITES   390\r
+#define NUMTILE16    2376\r
+#define NUMTILE16M   2736\r
+#define NUMEXTERNS   10\r
+\r
+//\r
+// File offsets for data items\r
+//\r
+#define STRUCTPIC    0\r
+#define STRUCTPICM   1\r
+#define STRUCTSPRITE 2\r
+\r
+#define STARTFONT    3\r
+#define STARTFONTM   (STARTFONT+NUMFONT)\r
+#define STARTPICS    (STARTFONTM+NUMFONTM)\r
+#define STARTPICM    (STARTPICS+NUMPICS)\r
+#define STARTSPRITES (STARTPICM+NUMPICM)\r
+#define STARTTILE8   (STARTSPRITES+NUMSPRITES)\r
+#define STARTTILE8M  (STARTTILE8+1)\r
+#define STARTTILE16  (STARTTILE8M+1)\r
+#define STARTTILE16M (STARTTILE16+NUMTILE16)\r
+#define STARTTILE32  (STARTTILE16M+NUMTILE16M)\r
+#define STARTTILE32M (STARTTILE32+NUMTILE32)\r
+#define STARTEXTERNS (STARTTILE32M+NUMTILE32M)\r
+\r
+typedef enum {\r
+       LASTFONT=STARTPICS-1,\r
+\r
+       //\r
+       // PICS\r
+       //\r
+\r
+       H_END1PIC,                   // 6\r
+       H_END2PIC,                   // 7\r
+       H_END3PIC,                   // 8\r
+       H_END4PIC,                   // 9\r
+       H_END5PIC,                   // 10\r
+\r
+       START_LUMP(CONTROLS_LUMP_START, __CONTROLSSTART)\r
+       CP_MAINMENUPIC,              // 11\r
+       CP_NEWGAMEMENUPIC,           // 12\r
+       CP_LOADMENUPIC,              // 13\r
+       CP_SAVEMENUPIC,              // 14\r
+       CP_CONFIGMENUPIC,            // 15\r
+       CP_SOUNDMENUPIC,             // 16\r
+       CP_MUSICMENUPIC,             // 17\r
+       CP_KEYBOARDMENUPIC,          // 18\r
+       CP_KEYMOVEMENTPIC,           // 19\r
+       CP_KEYBUTTONPIC,             // 20\r
+       CP_JOYSTICKMENUPIC,          // 21\r
+       CP_OPTIONSMENUPIC,           // 22\r
+       CP_PADDLEWARPIC,             // 23\r
+       CP_QUITPIC,                  // 24\r
+       CP_JOYSTICKPIC,              // 25\r
+       CP_MENUSCREENPIC,            // 26\r
+       END_LUMP(CONTROLS_LUMP_END, __CONTROLSEND)\r
+\r
+       H_FLASHARROW1PIC,            // 27\r
+       H_FLASHARROW2PIC,            // 28\r
+       IDSOFTPIC,                   // 29\r
+       PROGTEAMPIC,                 // 30\r
+       ARTISTPIC,                   // 31\r
+       DIRECTORPIC,                 // 32\r
+       SW_BACKGROUNDPIC,            // 33\r
+       TITLEPICPIC,                 // 34\r
+       KEENTALK1PIC,                // 35\r
+       KEENTALK2PIC,                // 36\r
+       KEENCOUNT1PIC,               // 37\r
+       KEENCOUNT2PIC,               // 38\r
+       KEENCOUNT3PIC,               // 39\r
+       KEENCOUNT4PIC,               // 40\r
+       KEENCOUNT5PIC,               // 41\r
+       KEENCOUNT6PIC,               // 42\r
+\r
+       //\r
+       // MASKED PICS\r
+       //\r
+\r
+       CP_MENUMASKPICM,             // 43\r
+       CORDPICM,                    // 44\r
+       METALPOLEPICM,               // 45\r
+\r
+       //\r
+       // SPRITES\r
+       //\r
+\r
+       START_LUMP(PADDLE_LUMP_START, __PADDLESTART)\r
+       PADDLESPR,                   // 46\r
+       BALLSPR,                     // 47\r
+       BALL1PIXELTOTHERIGHTSPR,     // 48\r
+       BALL2PIXELSTOTHERIGHTSPR,    // 49\r
+       BALL3PIXELSTOTHERIGHTSPR,    // 50\r
+       END_LUMP(PADDLE_LUMP_END, __PADDLEEND)\r
+\r
+       DEMOPLAQUESPR,               // 51\r
+\r
+       START_LUMP(KEEN_LUMP_START, __KEENSTART)\r
+       KEENSTANDRSPR,               // 52\r
+       KEENRUNR1SPR,                // 53\r
+       KEENRUNR2SPR,                // 54\r
+       KEENRUNR3SPR,                // 55\r
+       KEENRUNR4SPR,                // 56\r
+       KEENJUMPR1SPR,               // 57\r
+       KEENJUMPR2SPR,               // 58\r
+       KEENJUMPR3SPR,               // 59\r
+       KEENSTANDLSPR,               // 60\r
+       KEENRUNL1SPR,                // 61\r
+       KEENRUNL2SPR,                // 62\r
+       KEENRUNL3SPR,                // 63\r
+       KEENRUNL4SPR,                // 64\r
+       KEENJUMPL1SPR,               // 65\r
+       KEENJUMPL2SPR,               // 66\r
+       KEENJUMPL3SPR,               // 67\r
+       KEENLOOKUSPR,                // 68\r
+       KEENWAITR1SPR,               // 69\r
+       KEENWAITR2SPR,               // 70\r
+       KEENWAITR3SPR,               // 71\r
+       KEENSITREAD1SPR,             // 72\r
+       KEENSITREAD2SPR,             // 73\r
+       KEENSITREAD3SPR,             // 74\r
+       KEENSITREAD4SPR,             // 75\r
+       KEENREAD1SPR,                // 76\r
+       KEENREAD2SPR,                // 77\r
+       KEENREAD3SPR,                // 78\r
+       KEENSTOPREAD1SPR,            // 79\r
+       KEENSTOPREAD2SPR,            // 80\r
+       KEENLOOKD1SPR,               // 81\r
+       KEENLOOKD2SPR,               // 82\r
+       KEENDIE1SPR,                 // 83\r
+       KEENDIE2SPR,                 // 84\r
+       KEENSTUNSPR,                 // 85\r
+       STUNSTARS1SPR,               // 86\r
+       STUNSTARS2SPR,               // 87\r
+       STUNSTARS3SPR,               // 88\r
+       KEENSHOOTLSPR,               // 89\r
+       KEENJLSHOOTLSPR,             // 90\r
+       KEENJSHOOTDSPR,              // 91\r
+       KEENJSHOOTUSPR,              // 92\r
+       KEENSHOOTUSPR,               // 93\r
+       KEENSHOOTRSPR,               // 94\r
+       KEENJRSHOOTRSPR,             // 95\r
+       STUN1SPR,                    // 96\r
+       STUN2SPR,                    // 97\r
+       STUN3SPR,                    // 98\r
+       STUN4SPR,                    // 99\r
+       STUNHIT1SPR,                 // 100\r
+       STUNHIT2SPR,                 // 101\r
+       KEENSHINNYR1SPR,             // 102\r
+       KEENSHINNYR2SPR,             // 103\r
+       KEENSHINNYR3SPR,             // 104\r
+       KEENSLIDED1SPR,              // 105\r
+       KEENSLIDED2SPR,              // 106\r
+       KEENSLIDED3SPR,              // 107\r
+       KEENSLIDED4SPR,              // 108\r
+       KEENSHINNYL1SPR,             // 109\r
+       KEENSHINNYL2SPR,             // 110\r
+       KEENSHINNYL3SPR,             // 111\r
+       KEENPLSHOOTUSPR,             // 112\r
+       KEENPRSHOOTUSPR,             // 113\r
+       KEENPRSHOOTDSPR,             // 114\r
+       KEENPLSHOOTDSPR,             // 115\r
+       KEENPSHOOTLSPR,              // 116\r
+       KEENPSHOOTRSPR,              // 117\r
+       KEENENTER1SPR,               // 118\r
+       KEENENTER2SPR,               // 119\r
+       KEENENTER3SPR,               // 120\r
+       KEENENTER4SPR,               // 121\r
+       KEENENTER5SPR,               // 122\r
+       KEENHANGLSPR,                // 123\r
+       KEENHANGRSPR,                // 124\r
+       KEENCLIMBEDGEL1SPR,          // 125\r
+       KEENCLIMBEDGEL2SPR,          // 126\r
+       KEENCLIMBEDGEL3SPR,          // 127\r
+       KEENCLIMBEDGEL4SPR,          // 128\r
+       KEENCLIMBEDGER1SPR,          // 129\r
+       KEENCLIMBEDGER2SPR,          // 130\r
+       KEENCLIMBEDGER3SPR,          // 131\r
+       KEENCLIMBEDGER4SPR,          // 132\r
+       KEENPOGOR1SPR,               // 133\r
+       KEENPOGOR2SPR,               // 134\r
+       KEENPOGOL1SPR,               // 135\r
+       KEENPOGOL2SPR,               // 136\r
+       BONUS100UPSPR,               // 137\r
+       BONUS100SPR,                 // 138\r
+       BONUS200SPR,                 // 139\r
+       BONUS500SPR,                 // 140\r
+       BONUS1000SPR,                // 141\r
+       BONUS2000SPR,                // 142\r
+       BONUS5000SPR,                // 143\r
+       BONUS1UPSPR,                 // 144\r
+       BONUSCLIPSPR,                // 145\r
+       VIVASPLASH1SPR,              // 146\r
+       VIVASPLASH2SPR,              // 147\r
+       VIVASPLASH3SPR,              // 148\r
+       VIVASPLASH4SPR,              // 149\r
+       END_LUMP(KEEN_LUMP_END, __KEENEND)\r
+\r
+       START_LUMP(SUGAR1_LUMP_START, __SUGAR1START)\r
+       SUGAR1ASPR,                  // 150\r
+       SUGAR1BSPR,                  // 151\r
+       END_LUMP(SUGAR1_LUMP_END, __SUGAR1END)\r
+\r
+       START_LUMP(SUGAR2_LUMP_START, __SUGAR2START)\r
+       SUGAR2ASPR,                  // 152\r
+       SUGAR2BSPR,                  // 153\r
+       END_LUMP(SUGAR2_LUMP_END, __SUGAR2END)\r
+\r
+       START_LUMP(SUGAR3_LUMP_START, __SUGAR3START)\r
+       SUGAR3ASPR,                  // 154\r
+       SUGAR3BSPR,                  // 155\r
+       END_LUMP(SUGAR3_LUMP_END, __SUGAR3END)\r
+\r
+       START_LUMP(SUGAR4_LUMP_START, __SUGAR4START)\r
+       SUGAR4ASPR,                  // 156\r
+       SUGAR4BSPR,                  // 157\r
+       END_LUMP(SUGAR4_LUMP_END, __SUGAR4END)\r
+\r
+       START_LUMP(SUGAR5_LUMP_START, __SUGAR5START)\r
+       SUGAR5ASPR,                  // 158\r
+       SUGAR5BSPR,                  // 159\r
+       END_LUMP(SUGAR5_LUMP_END, __SUGAR5END)\r
+\r
+       START_LUMP(SUGAR6_LUMP_START, __SUGAR6START)\r
+       SUGAR6ASPR,                  // 160\r
+       SUGAR6BSPR,                  // 161\r
+       END_LUMP(SUGAR6_LUMP_END, __SUGAR6END)\r
+\r
+       START_LUMP(ONEUP_LUMP_START, __ONEUPSTART)\r
+       ONEUPASPR,                   // 162\r
+       ONEUPBSPR,                   // 163\r
+       END_LUMP(ONEUP_LUMP_END, __ONEUPEND)\r
+\r
+       START_LUMP(KEYGEM_LUMP_START, __KEYGEMSTART)\r
+       REDGEM1SPR,                  // 164\r
+       REDGEM2SPR,                  // 165\r
+       YELLOWGEM1SPR,               // 166\r
+       YELLOWGEM2SPR,               // 167\r
+       BLUEGEM1SPR,                 // 168\r
+       BLUEGEM2SPR,                 // 169\r
+       GREENGEM1SPR,                // 170\r
+       GREENGEM2SPR,                // 171\r
+       BONUSGEMSPR,                 // 172\r
+       END_LUMP(KEYGEM_LUMP_END, __KEYGEMEND)\r
+\r
+       START_LUMP(AMMO_LUMP_START, __AMMOSTART)\r
+       STUNCLIP1SPR,                // 173\r
+       STUNCLIP2SPR,                // 174\r
+       END_LUMP(AMMO_LUMP_END, __AMMOEND)\r
+\r
+       SCOREBOXSPR,                 // 175\r
+\r
+       START_LUMP(LASER_LUMP_START, __LASERSTART)\r
+       LASER1SPR,                   // 176\r
+       LASER2SPR,                   // 177\r
+       LASER3SPR,                   // 178\r
+       LASER4SPR,                   // 179\r
+       LASERHIT1SPR,                // 180\r
+       LASERHIT2SPR,                // 181\r
+       END_LUMP(LASER_LUMP_END, __LASEREND)\r
+\r
+       START_LUMP(SANDWICH_LUMP_START, __SANDWICHSTART)\r
+       SANDWICHSPR,                 // 182\r
+       END_LUMP(SANDWICH_LUMP_END, __SANDWICHEND)\r
+\r
+       START_LUMP(HOOK_LUMP_START, __ROPESTART)\r
+       HOOKSPR,                     // 183\r
+       END_LUMP(HOOK_LUMP_END, __ROPEEND)\r
+\r
+       START_LUMP(WORLDKEEN_LUMP_START, __WORLDKEENSTART)\r
+       WORLDKEENL1SPR,              // 184\r
+       WORLDKEENL2SPR,              // 185\r
+       WORLDKEENL3SPR,              // 186\r
+       WORLDKEENR1SPR,              // 187\r
+       WORLDKEENR2SPR,              // 188\r
+       WORLDKEENR3SPR,              // 189\r
+       WORLDKEENU1SPR,              // 190\r
+       WORLDKEENU2SPR,              // 191\r
+       WORLDKEENU3SPR,              // 192\r
+       WORLDKEEND1SPR,              // 193\r
+       WORLDKEEND2SPR,              // 194\r
+       WORLDKEEND3SPR,              // 195\r
+       WORLDKEENDR1SPR,             // 196\r
+       WORLDKEENDR2SPR,             // 197\r
+       WORLDKEENDR3SPR,             // 198\r
+       WORLDKEENDL1SPR,             // 199\r
+       WORLDKEENDL2SPR,             // 200\r
+       WORLDKEENDL3SPR,             // 201\r
+       WORLDKEENUL1SPR,             // 202\r
+       WORLDKEENUL2SPR,             // 203\r
+       WORLDKEENUL3SPR,             // 204\r
+       WORLDKEENUR1SPR,             // 205\r
+       WORLDKEENUR2SPR,             // 206\r
+       WORLDKEENUR3SPR,             // 207\r
+       WORLDKEENWAVE1SPR,           // 208\r
+       WORLDKEENWAVE2SPR,           // 209\r
+       ROCKETSPR,                   // 210\r
+       ROCKETFLY1SPR,               // 211\r
+       ROCKETFLY2SPR,               // 212\r
+       SATELLITE1SPR,               // 213\r
+       SATELLITE2SPR,               // 214\r
+       SATELLITE3SPR,               // 215\r
+       SATELLITE4SPR,               // 216\r
+       GRABBITER1SPR,               // 217\r
+       GRABBITER2SPR,               // 218\r
+       GRABBITERSLEEP1SPR,          // 219\r
+       GRABBITERSLEEP2SPR,          // 220\r
+       WORLDKEENTRHOW1SPR,          // 221\r
+       WORLDKEENTRHOW2SPR,          // 222\r
+       WORLDKEENCLIMB1SPR,          // 223\r
+       WORLDKEENCLIMB2SPR,          // 224\r
+       ROPETHROW1SPR,               // 225\r
+       ROPETHROW2SPR,               // 226\r
+       WORLDKEENHANGSPR,            // 227\r
+       FLAGFLIP1SPR,                // 228\r
+       FLAGFLIP2SPR,                // 229\r
+       FLAGFLIP3SPR,                // 230\r
+       FLAGFLIP4SPR,                // 231\r
+       FLAGFLIP5SPR,                // 232\r
+       FLAGFALL1SPR,                // 233\r
+       FLAGFALL2SPR,                // 234\r
+       FLAGFLAP1SPR,                // 235\r
+       FLAGFLAP2SPR,                // 236\r
+       FLAGFLAP3SPR,                // 237\r
+       FLAGFLAP4SPR,                // 238\r
+       END_LUMP(WORLDKEEN_LUMP_END, __WORLDKEENEND)\r
+\r
+       START_LUMP(FLEEX_LUMP_START, __FLEEXSTART)\r
+       FLEEXWALKR1SPR,              // 239\r
+       FLEEXWALKR2SPR,              // 240\r
+       FLEEXWALKL1SPR,              // 241\r
+       FLEEXWALKL2SPR,              // 242\r
+       FLEEXLOOK1SPR,               // 243\r
+       FLEEXLOOK2SPR,               // 244\r
+       FLEEXSTUNSPR,             // 245\r
+       END_LUMP(FLEEX_LUMP_END, __FLEEXEND)\r
+\r
+       START_LUMP(CEILICK_LUMP_START, __CEILICKSTART)\r
+       CEILICK1SPR,                 // 246\r
+       CEILICK2SPR,                 // 247\r
+       TONGUE1SPR,                  // 248\r
+       TONGUE2SPR,                  // 249\r
+       TONGUE3SPR,                  // 250\r
+       TONGUE4SPR,                  // 251\r
+       TONGUE5SPR,                  // 252\r
+       CEILICKSTUNSPR,           // 253\r
+       END_LUMP(CEILICK_LUMP_END, __CEILICKEND)\r
+\r
+       START_LUMP(BLOOGUARD_LUMP_START, __BLOOGUARDSTART)\r
+       BLOOGUARDWALKL1SPR,          // 254\r
+       BLOOGUARDWALKL2SPR,          // 255\r
+       BLOOGUARDWALKL3SPR,          // 256\r
+       BLOOGUARDWALKL4SPR,          // 257\r
+       BLOOGUARDWALKR1SPR,          // 258\r
+       BLOOGUARDWALKR2SPR,          // 259\r
+       BLOOGUARDWALKR3SPR,          // 260\r
+       BLOOGUARDWALKR4SPR,          // 261\r
+       BLOOGUARDSWINGL1SPR,         // 262\r
+       BLOOGUARDSWINGL2SPR,         // 263\r
+       BLOOGUARDSWINGL3SPR,         // 264\r
+       BLOOGUARDSWINGR1SPR,         // 265\r
+       BLOOGUARDSWINGR2SPR,         // 266\r
+       BLOOGUARDSWINGR3SPR,         // 267\r
+       BLOOGUARDSTUNSPR,         // 268\r
+       END_LUMP(BLOOGUARD_LUMP_END, __BLOOGUARDEND)\r
+\r
+       START_LUMP(BIPSHIP_LUMP_START, __BIPSHIPSTART)\r
+       BIPSHIPRSPR,                 // 269\r
+       BIPSHIPRTURN1SPR,            // 270\r
+       BIPSHIPRTURN2SPR,            // 271\r
+       BIPSHIPRTURN3SPR,            // 272\r
+       BIPSHIPRTURN4SPR,            // 273\r
+       BIPSHIPLSPR,                 // 274\r
+       BIPSHIPLTURN1SPR,            // 275\r
+       BIPSHIPLTURN2SPR,            // 276\r
+       BIPSHIPLTURN3SPR,            // 277\r
+       BIPSHIPLTURN4SPR,            // 278\r
+       BIPSHIPEXPLODE1SPR,          // 279\r
+       BIPSHIPEXPLODE2SPR,          // 280\r
+       BIPSHIPEXPLODE3SPR,          // 281\r
+       BIPSHIPEXPLODE4SPR,          // 282\r
+       BIPSHIPEXPLODE5SPR,          // 283\r
+       BIPSHIPSHOTSPR,              // 284\r
+       END_LUMP(BIPSHIP_LUMP_END, __BIPSHIPEND)\r
+\r
+       START_LUMP(BABOBBA_LUMP_START, __BABOBBASTART)\r
+       BABOBBAL1SPR,                // 285\r
+       BABOBBAL2SPR,                // 286\r
+       BABOBBAL3SPR,                // 287\r
+       BABOBBAR1SPR,                // 288\r
+       BABOBBAR2SPR,                // 289\r
+       BABOBBAR3SPR,                // 290\r
+       BABOBBASHOT1SPR,             // 291\r
+       BABOBBASHOT2SPR,             // 292\r
+       BABOBBASTUNSPR,           // 293\r
+       BABOBBASLEEP1SPR,            // 294\r
+       BABOBBASLEEP2SPR,            // 295\r
+       BABOBBASLEEP3SPR,            // 296\r
+       BABOBBASLEEP4SPR,            // 297\r
+       END_LUMP(BABOBBA_LUMP_END, __BABOBBAEND)\r
+\r
+       START_LUMP(NOSPIKE_LUMP_START, __NOSPIKESTART)\r
+       NOSPIKESTANDSPR,             // 298\r
+       NOSPIKERUNR1SPR,             // 299\r
+       NOSPIKERUNR2SPR,             // 300\r
+       NOSPIKERUNR3SPR,             // 301\r
+       NOSPIKERUNR4SPR,             // 302\r
+       NOSPIKERUNL1SPR,             // 303\r
+       NOSPIKERUNL2SPR,             // 304\r
+       NOSPIKERUNL3SPR,             // 305\r
+       NOSPIKERUNL4SPR,             // 306\r
+       NOSPIKEWALKR1SPR,            // 307\r
+       NOSPIKEWALKR2SPR,            // 308\r
+       NOSPIKEWALKR3SPR,            // 309\r
+       NOSPIKEWALKR4SPR,            // 310\r
+       NOSPIKEWALKL1SPR,            // 311\r
+       NOSPIKEWALKL2SPR,            // 312\r
+       NOSPIKEWALKL3SPR,            // 313\r
+       NOSPIKEWALKL4SPR,            // 314\r
+       NOSPIKESTUNSPR,           // 315\r
+       QUESTIONMARKSPR,             // 316\r
+       END_LUMP(NOSPIKE_LUMP_END, __NOSPIKEEND)\r
+\r
+       START_LUMP(FLECT_LUMP_START, __FLECTSTART)\r
+       FLECTSTANDSPR,               // 317\r
+       FLECTSTANDRSPR,              // 318\r
+       FLECTWALKR1SPR,              // 319\r
+       FLECTWALKR2SPR,              // 320\r
+       FLECTWALKR3SPR,              // 321\r
+       FLECTWALKR4SPR,              // 322\r
+       FLECTSTANDLSPR,              // 323\r
+       FLECTWALKL1SPR,              // 324\r
+       FLECTWALKL2SPR,              // 325\r
+       FLECTWALKL3SPR,              // 326\r
+       FLECTWALKL4SPR,              // 327\r
+       FLECTSTUNSPR,             // 328\r
+       END_LUMP(FLECT_LUMP_END, __FLECTEND)\r
+\r
+       START_LUMP(ORBATRIX_LUMP_START, __ORBATRIXSTART)\r
+       ORBATRIX1SPR,                // 329\r
+       ORBATRIX2SPR,                // 330\r
+       ORBATRIX3SPR,                // 331\r
+       ORBATRIX4SPR,                // 332\r
+       ORBATRIXL1SPR,               // 333\r
+       ORBATRIXL2SPR,               // 334\r
+       ORBATRIXR1SPR,               // 335\r
+       ORBATRIXR2SPR,               // 336\r
+       ORBATRIXSPIN1SPR,            // 337\r
+       ORBATRIXSPIN2SPR,            // 338\r
+       ORBATRIXSPIN3SPR,            // 339\r
+       ORBATRIXSPIN4SPR,            // 340\r
+       ORBATRIXCURLSPR,             // 341\r
+       END_LUMP(ORBATRIX_LUMP_END, __ORBATRIXEND)\r
+\r
+       START_LUMP(BLOOG_LUMP_START, __BLOOGSTART)\r
+       BLOOGWALKR1SPR,              // 342\r
+       BLOOGWALKR2SPR,              // 343\r
+       BLOOGWALKR3SPR,              // 344\r
+       BLOOGWALKR4SPR,              // 345\r
+       BLOOGWALKL1SPR,              // 346\r
+       BLOOGWALKL2SPR,              // 347\r
+       BLOOGWALKL3SPR,              // 348\r
+       BLOOGWALKL4SPR,              // 349\r
+       BLOOGSTUNSPR,             // 350\r
+       END_LUMP(BLOOG_LUMP_END, __BLOOGEND)\r
+\r
+       START_LUMP(RBLOOGLET_LUMP_START, __RBLOOGLETSTART)\r
+       RBLOOGLETWALKR1SPR,          // 351\r
+       RBLOOGLETWALKR2SPR,          // 352\r
+       RBLOOGLETWALKR3SPR,          // 353\r
+       RBLOOGLETWALKR4SPR,          // 354\r
+       RBLOOGLETWALKL1SPR,          // 355\r
+       RBLOOGLETWALKL2SPR,          // 356\r
+       RBLOOGLETWALKL3SPR,          // 357\r
+       RBLOOGLETWALKL4SPR,          // 358\r
+       RBLOOGLETSTUNSPR,         // 359\r
+       END_LUMP(RBLOOGLET_LUMP_END, __RBLOOGLETEND)\r
+\r
+       START_LUMP(YBLOOGLET_LUMP_START, __YBLOOGLETSTART)\r
+       YBLOOGLETWALKR1SPR,          // 360\r
+       YBLOOGLETWALKR2SPR,          // 361\r
+       YBLOOGLETWALKR3SPR,          // 362\r
+       YBLOOGLETWALKR4SPR,          // 363\r
+       YBLOOGLETWALKL1SPR,          // 364\r
+       YBLOOGLETWALKL2SPR,          // 365\r
+       YBLOOGLETWALKL3SPR,          // 366\r
+       YBLOOGLETWALKL4SPR,          // 367\r
+       YBLOOGLETSTUNSPR,         // 368\r
+       END_LUMP(YBLOOGLET_LUMP_END, __YBLOOGLETEND)\r
+\r
+       START_LUMP(BBLOOGLET_LUMP_START, __BBLOOGLETSTART)\r
+       BBLOOGLETWALKR1SPR,          // 369\r
+       BBLOOGLETWALKR2SPR,          // 370\r
+       BBLOOGLETWALKR3SPR,          // 371\r
+       BBLOOGLETWALKR4SPR,          // 372\r
+       BBLOOGLETWALKL1SPR,          // 373\r
+       BBLOOGLETWALKL2SPR,          // 374\r
+       BBLOOGLETWALKL3SPR,          // 375\r
+       BBLOOGLETWALKL4SPR,          // 376\r
+       BBLOOGLETSTUNSPR,         // 377\r
+       END_LUMP(BBLOOGLET_LUMP_END, __BBLOOGLETEND)\r
+\r
+       START_LUMP(GBLOOGLET_LUMP_START, __GBLOOGLETSTART)\r
+       GBLOOGLETWALKR1SPR,          // 378\r
+       GBLOOGLETWALKR2SPR,          // 379\r
+       GBLOOGLETWALKR3SPR,          // 380\r
+       GBLOOGLETWALKR4SPR,          // 381\r
+       GBLOOGLETWALKL1SPR,          // 382\r
+       GBLOOGLETWALKL2SPR,          // 383\r
+       GBLOOGLETWALKL3SPR,          // 384\r
+       GBLOOGLETWALKL4SPR,          // 385\r
+       GBLOOGLETSTUNSPR,         // 386\r
+       END_LUMP(GBLOOGLET_LUMP_END, __GBLOOGLETEND)\r
+\r
+       START_LUMP(GIK_LUMP_START, __GIKSTART)\r
+       GIKWALKR1SPR,                // 387\r
+       GIKWALKR2SPR,                // 388\r
+       GIKWALKR3SPR,                // 389\r
+       GIKWALKL1SPR,                // 390\r
+       GIKWALKL2SPR,                // 391\r
+       GIKWALKL3SPR,                // 392\r
+       GIKJUMPLSPR,                 // 393\r
+       GIKJUMPRSPR,                 // 394\r
+       GIKSLIDER1SPR,               // 395\r
+       GIKSLIDER2SPR,               // 396\r
+       GIKSLIDEL1SPR,               // 397\r
+       GIKSLIDEL2SPR,               // 398\r
+       END_LUMP(GIK_LUMP_END, __GIKEND)\r
+\r
+       START_LUMP(BLORB_LUMP_START, __BLORBSTART)\r
+       BLORB1SPR,                   // 399\r
+       BLORB2SPR,                   // 400\r
+       BLORB3SPR,                   // 401\r
+       END_LUMP(BLORB_LUMP_END, __BLORBEND)\r
+\r
+       START_LUMP(BOBBA_LUMP_START, __BOBBASTART)\r
+       BOBBAL1SPR,                  // 402\r
+       BOBBAL2SPR,                  // 403\r
+       BOBBAL3SPR,                  // 404\r
+       BOBBAR1SPR,                  // 405\r
+       BOBBAR2SPR,                  // 406\r
+       BOBBAR3SPR,                  // 407\r
+       BOBBASHOT1SPR,               // 408\r
+       BOBBASHOT2SPR,               // 409\r
+       BOBBASHOT3SPR,               // 410\r
+       BOBBASHOT4SPR,               // 411\r
+       BOBBASHOT5SPR,               // 412\r
+       BOBBASHOT6SPR,               // 413\r
+       END_LUMP(BOBBA_LUMP_END, __BOBBAEND)\r
+\r
+       START_LUMP(BIP_LUMP_START, __BIPSTART)\r
+       BIPSTANDSPR,                 // 414\r
+       BIPWALKR1SPR,                // 415\r
+       BIPWALKR2SPR,                // 416\r
+       BIPWALKR3SPR,                // 417\r
+       BIPWALKR4SPR,                // 418\r
+       BIPWALKL1SPR,                // 419\r
+       BIPWALKL2SPR,                // 420\r
+       BIPWALKL3SPR,                // 421\r
+       BIPWALKL4SPR,                // 422\r
+       END_LUMP(BIP_LUMP_END, __BIPEND)\r
+\r
+       START_LUMP(BIPSQUISHED_LUMP_START, __BIPSQUISHEDSTART)\r
+       BIPSQUISHEDSPR,              // 423\r
+       END_LUMP(BIPSQUISHED_LUMP_END, __BIPSQUISHEDEND)\r
+\r
+       START_LUMP(PLATFORM_LUMP_START, __PLATFORMSTART)\r
+       PLATFORMSPR,                 // 424\r
+       PLATBIP1SPR,                 // 425\r
+       PLATBIP2SPR,                 // 426\r
+       PLATBIP3SPR,                 // 427\r
+       PLATBIP4SPR,                 // 428\r
+       PLATBIP5SPR,                 // 429\r
+       PLATBIP6SPR,                 // 430\r
+       PLATBIP7SPR,                 // 431\r
+       PLATBIP8SPR,                 // 432\r
+       END_LUMP(PLATFORM_LUMP_END, __PLATFORMEND)\r
+\r
+       START_LUMP(MOLLY_LUMP_START, __MOLLYSTART)\r
+       MOLLY1SPR,                   // 433\r
+       MOLLY2SPR,                   // 434\r
+       END_LUMP(MOLLY_LUMP_END, __MOLLYEND)\r
+\r
+       START_LUMP(PASSCARD_LUMP_START, __PASSCARDSTART)\r
+       PASSCARDSPR,                 // 435\r
+       END_LUMP(PASSCARD_LUMP_END, __PASSCARDEND)\r
+\r
+       //\r
+       // TILES (these don't need names)\r
+       //\r
+\r
+       LASTTILE=STARTEXTERNS-1,\r
+\r
+       //\r
+       // EXTERNS\r
+       //\r
+\r
+       T_ENDART,                    // 5550\r
+\r
+       ORDERSCREEN,                 // 5551\r
+       BIGCOMMANDER,                // 5552\r
+       BIGKEEN,                     // 5553\r
+       OUTOFMEM,                    // 5554\r
+\r
+       //demos\r
+       DEMO0,                       // 5555\r
+       DEMO1,                       // 5556\r
+       DEMO2,                       // 5557\r
+       DEMO3,                       // 5558\r
+       DEMO4,                       // 5559\r
+\r
+       NUMGRCHUNKS\r
+} graphicnums;\r
+\r
+#undef START_LUMP\r
+#undef END_LUMP\r
+\r
+#endif //__GFX_H__
\ No newline at end of file
diff --git a/16/keen456/KEEN4-6/KEEN6/ID_ASM.EQU b/16/keen456/KEEN4-6/KEEN6/ID_ASM.EQU
new file mode 100755 (executable)
index 0000000..5989f12
--- /dev/null
@@ -0,0 +1,115 @@
+;\r
+; Equates for all .ASM files\r
+;\r
+\r
+;----------------------------------------------------------------------------\r
+\r
+INCLUDE        "GFXE_CK6.EQU"\r
+\r
+;----------------------------------------------------------------------------\r
+\r
+CGAGR          =       1\r
+EGAGR          =       2\r
+VGAGR          =       3\r
+\r
+GRMODE         =       EGAGR\r
+PROFILE                =       0                       ; 1=keep stats on tile drawing\r
+\r
+SC_INDEX       =       03C4h\r
+SC_RESET       =       0\r
+SC_CLOCK       =       1\r
+SC_MAPMASK     =       2\r
+SC_CHARMAP     =       3\r
+SC_MEMMODE     =       4\r
+\r
+CRTC_INDEX     =       03D4h\r
+CRTC_H_TOTAL   =       0\r
+CRTC_H_DISPEND =       1\r
+CRTC_H_BLANK   =       2\r
+CRTC_H_ENDBLANK        =       3\r
+CRTC_H_RETRACE =       4\r
+CRTC_H_ENDRETRACE =    5\r
+CRTC_V_TOTAL   =       6\r
+CRTC_OVERFLOW  =       7\r
+CRTC_ROWSCAN   =       8\r
+CRTC_MAXSCANLINE =     9\r
+CRTC_CURSORSTART =     10\r
+CRTC_CURSOREND =       11\r
+CRTC_STARTHIGH =       12\r
+CRTC_STARTLOW  =       13\r
+CRTC_CURSORHIGH        =       14\r
+CRTC_CURSORLOW =       15\r
+CRTC_V_RETRACE =       16\r
+CRTC_V_ENDRETRACE =    17\r
+CRTC_V_DISPEND =       18\r
+CRTC_OFFSET    =       19\r
+CRTC_UNDERLINE =       20\r
+CRTC_V_BLANK   =       21\r
+CRTC_V_ENDBLANK        =       22\r
+CRTC_MODE      =       23\r
+CRTC_LINECOMPARE =     24\r
+\r
+\r
+GC_INDEX       =       03CEh\r
+GC_SETRESET    =       0\r
+GC_ENABLESETRESET =    1\r
+GC_COLORCOMPARE        =       2\r
+GC_DATAROTATE  =       3\r
+GC_READMAP     =       4\r
+GC_MODE                =       5\r
+GC_MISCELLANEOUS =     6\r
+GC_COLORDONTCARE =     7\r
+GC_BITMASK     =       8\r
+\r
+ATR_INDEX      =       03c0h\r
+ATR_MODE       =       16\r
+ATR_OVERSCAN   =       17\r
+ATR_COLORPLANEENABLE = 18\r
+ATR_PELPAN     =       19\r
+ATR_COLORSELECT        =       20\r
+\r
+STATUS_REGISTER_1     =        03dah\r
+\r
+\r
+MACRO  WORDOUT\r
+       out     dx,ax\r
+ENDM\r
+\r
+if 0\r
+\r
+MACRO  WORDOUT\r
+       out     dx,al\r
+       inc     dx\r
+       xchg    al,ah\r
+       out     dx,al\r
+       dec     dx\r
+       xchg    al,ah\r
+ENDM\r
+\r
+endif\r
+\r
+UPDATEWIDE     =       22\r
+UPDATEHIGH     =       14\r
+\r
+;\r
+; tile info offsets from segment tinf\r
+;\r
+\r
+ANIM           =       402\r
+SPEED          =       (ANIM+NUMTILE16)\r
+\r
+NORTHWALL      =       (SPEED+NUMTILE16)\r
+EASTWALL       =       (NORTHWALL+NUMTILE16M)\r
+SOUTHWALL   =  (EASTWALL+NUMTILE16M)\r
+WESTWALL    =  (SOUTHWALL+NUMTILE16M)\r
+MANIM       =  (WESTWALL+NUMTILE16M)\r
+INTILE      =  (MANIM+NUMTILE16M)\r
+MSPEED      =  (INTILE+NUMTILE16M)\r
+\r
+\r
+IFE GRMODE-EGAGR\r
+SCREENWIDTH    =       64\r
+ENDIF\r
+IFE GRMODE-CGAGR\r
+SCREENWIDTH    =       128\r
+ENDIF\r
diff --git a/16/keen456/KEEN4-6/KEEN6/ID_HEADS.H b/16/keen456/KEEN4-6/KEEN6/ID_HEADS.H
new file mode 100755 (executable)
index 0000000..3a837a4
--- /dev/null
@@ -0,0 +1,109 @@
+/* Reconstructed Commander Keen 4-6 Source Code\r
+ * Copyright (C) 2021 K1n9_Duk3\r
+ *\r
+ * This file is primarily based on:\r
+ * Catacomb 3-D Source Code\r
+ * Copyright (C) 1993-2014 Flat Rock Software\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License along\r
+ * with this program; if not, write to the Free Software Foundation, Inc.,\r
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+ */\r
+\r
+// ID_GLOB.H\r
+\r
+\r
+#include <ALLOC.H>\r
+#include <CTYPE.H>\r
+#include <DOS.H>\r
+#include <ERRNO.H>\r
+#include <FCNTL.H>\r
+#include <IO.H>\r
+#include <MEM.H>\r
+#include <PROCESS.H>\r
+#include <STDIO.H>\r
+#include <STDLIB.H>\r
+#include <STRING.H>\r
+#include <SYS\STAT.H>\r
+\r
+#define __ID_GLOB__\r
+\r
+//--------------------------------------------------------------------------\r
+\r
+#define KEEN\r
+#define KEEN6\r
+\r
+#define        EXTENSION       "CK6"\r
+\r
+extern char far introscn;\r
+\r
+#include "GFXE_CK6.H"\r
+#include "AUDIOCK6.H"\r
+\r
+//--------------------------------------------------------------------------\r
+\r
+#define        TEXTGR  0\r
+#define        CGAGR   1\r
+#define        EGAGR   2\r
+#define        VGAGR   3\r
+\r
+#define GRMODE EGAGR\r
+\r
+#if GRMODE == EGAGR\r
+#define GREXT  "EGA"\r
+#endif\r
+#if GRMODE == CGAGR\r
+#define GREXT  "CGA"\r
+#endif\r
+\r
+//#define PROFILE\r
+\r
+//\r
+//     ID Engine\r
+//     Types.h - Generic types, #defines, etc.\r
+//     v1.0d1\r
+//\r
+\r
+#ifndef        __TYPES__\r
+#define        __TYPES__\r
+\r
+typedef        enum    {false,true}    boolean;\r
+typedef        unsigned        char            byte;\r
+typedef        unsigned        int                     word;\r
+typedef        unsigned        long            longword;\r
+typedef        byte *                                  Ptr;\r
+\r
+typedef        struct\r
+               {\r
+                       int     x,y;\r
+               } Point;\r
+typedef        struct\r
+               {\r
+                       Point   ul,lr;\r
+               } Rect;\r
+\r
+#define        nil     ((void *)0)\r
+\r
+#endif\r
+\r
+#include "ID_MM.H"\r
+#include "ID_CA.H"\r
+#include "ID_VW.H"\r
+#include "ID_RF.H"\r
+#include "ID_IN.H"\r
+#include "ID_SD.H"\r
+#include "ID_US.H"\r
+\r
+\r
+void   Quit (char *error);             // defined in user program\r
+\r
diff --git a/16/keen456/KEEN4-6/KEEN6/K6_ACT1.C b/16/keen456/KEEN4-6/KEEN6/K6_ACT1.C
new file mode 100755 (executable)
index 0000000..a2004bd
--- /dev/null
@@ -0,0 +1,2137 @@
+/* Reconstructed Commander Keen 4-6 Source Code\r
+ * Copyright (C) 2021 K1n9_Duk3\r
+ *\r
+ * This file is loosely based on:\r
+ * Keen Dreams Source Code\r
+ * Copyright (C) 2014 Javier M. Chavez\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License along\r
+ * with this program; if not, write to the Free Software Foundation, Inc.,\r
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+ */\r
+\r
+/*\r
+K6_ACT1.C\r
+=========\r
+\r
+Contains the following actor types (in this order):\r
+\r
+- some shared routines\r
+- Bonus Items\r
+- Grabbiter\r
+- Rocket\r
+- Grapple spots\r
+- Satellite\r
+- Quest Items (Sandwich, Grappling Hook, Passcard, Molly)\r
+- Platforms\r
+- falling platforms\r
+- static platforms\r
+- Goplat platforms\r
+- Trick platforms\r
+- Bloog\r
+- Blooguard\r
+- Blooglet\r
+\r
+*/\r
+\r
+#include "CK_DEF.H"\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                 SHARED STUFF\r
+\r
+=============================================================================\r
+*/\r
+\r
+Sint16 pdirx[] = {0, 1, 0, -1, 1, 1, -1, -1};\r
+Sint16 pdiry[] = {-1, 0, 1, 0, -1, 1, 1, -1};\r
+\r
+/*\r
+===========================\r
+=\r
+= C_ClipSide\r
+=\r
+===========================\r
+*/\r
+\r
+void C_ClipSide(objtype *ob, objtype *hit)\r
+{\r
+       if (hit->obclass == keenobj)\r
+       {\r
+               playerkludgeclipcancel = true;\r
+               ClipToSpriteSide(hit, ob);\r
+               playerkludgeclipcancel = false;\r
+       }\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= C_ClipTop\r
+=\r
+===========================\r
+*/\r
+\r
+void C_ClipTop(objtype *ob, objtype *hit)\r
+{\r
+       if (hit->obclass == keenobj)\r
+               ClipToSpriteTop(hit, ob);\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= R_Land\r
+=\r
+===========================\r
+*/\r
+\r
+void R_Land(objtype *ob)\r
+{\r
+       if (ob->hiteast || ob->hitwest)\r
+               ob->xspeed = 0;\r
+\r
+       if (ob->hitsouth)\r
+               ob->yspeed = 0;\r
+\r
+       if (ob->hitnorth)\r
+       {\r
+               ob->yspeed = 0;\r
+               if (ob->state->nextstate)\r
+               {\r
+                       ChangeState(ob, ob->state->nextstate);\r
+               }\r
+               else\r
+               {\r
+                       RemoveObj(ob);\r
+                       return;\r
+               }\r
+       }\r
+\r
+       RF_PlaceSprite(&ob->sprite, ob->x, ob->y, ob->shapenum, spritedraw, ob->priority);\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= R_Bounce\r
+=\r
+===========================\r
+*/\r
+\r
+void R_Bounce(objtype *ob)\r
+{\r
+       Uint16 wall,absx,absy,angle,newangle;\r
+       Uint32 speed;\r
+\r
+\r
+       RF_PlaceSprite(&ob->sprite, ob->x, ob->y, ob->shapenum, spritedraw, ob->priority);\r
+\r
+       if (ob->hiteast || ob->hitwest)\r
+               ob->xspeed = -ob->xspeed/2;\r
+\r
+       if (ob->hitsouth)\r
+       {\r
+               ob->yspeed = -ob->yspeed/2;\r
+               return;\r
+       }\r
+\r
+       wall = ob->hitnorth;\r
+#ifdef KEEN6Ev15\r
+       if (!wall)\r
+       {\r
+               return;\r
+       }\r
+       else\r
+#else\r
+       if (wall)\r
+#endif\r
+       {\r
+               if (ob->yspeed < 0)\r
+                       ob->yspeed = 0;\r
+\r
+               absx = abs(ob->xspeed);\r
+               absy = ob->yspeed;\r
+               if (absx>absy)\r
+               {\r
+                       if (absx>absy*2)        // 22 degrees\r
+                       {\r
+                               angle = 0;\r
+                               speed = absx*286;       // x*sqrt(5)/2\r
+                       }\r
+                       else                            // 45 degrees\r
+                       {\r
+                               angle = 1;\r
+                               speed = absx*362;       // x*sqrt(2)\r
+                       }\r
+               }\r
+               else\r
+               {\r
+                       if (absy>absx*2)        // 90 degrees\r
+                       {\r
+                               angle = 3;\r
+                               speed = absy*256;\r
+                       }\r
+                       else\r
+                       {\r
+                               angle = 2;              // 67 degrees\r
+                               speed = absy*286;       // y*sqrt(5)/2\r
+                       }\r
+               }\r
+               if (ob->xspeed > 0)\r
+                       angle = 7-angle;\r
+\r
+               speed >>= 1;\r
+               newangle = bounceangle[ob->hitnorth][angle];\r
+               switch (newangle)\r
+               {\r
+               case 0:\r
+                       ob->xspeed = speed / 286;\r
+                       ob->yspeed = -ob->xspeed / 2;\r
+                       break;\r
+               case 1:\r
+                       ob->xspeed = speed / 362;\r
+                       ob->yspeed = -ob->xspeed;\r
+                       break;\r
+               case 2:\r
+                       ob->yspeed = -(speed / 286);\r
+                       ob->xspeed = -ob->yspeed / 2;\r
+                       break;\r
+               case 3:\r
+\r
+               case 4:\r
+                       ob->xspeed = 0;\r
+                       ob->yspeed = -(speed / 256);\r
+                       break;\r
+               case 5:\r
+                       ob->yspeed = -(speed / 286);\r
+                       ob->xspeed = ob->yspeed / 2;\r
+                       break;\r
+               case 6:\r
+                       ob->xspeed = ob->yspeed = -(speed / 362);\r
+                       break;\r
+               case 7:\r
+                       ob->xspeed = -(speed / 286);\r
+                       ob->yspeed = ob->xspeed / 2;\r
+                       break;\r
+\r
+               case 8:\r
+                       ob->xspeed = -(speed / 286);\r
+                       ob->yspeed = -ob->xspeed / 2;\r
+                       break;\r
+               case 9:\r
+                       ob->xspeed = -(speed / 362);\r
+                       ob->yspeed = -ob->xspeed;\r
+                       break;\r
+               case 10:\r
+                       ob->yspeed = speed / 286;\r
+                       ob->xspeed = -ob->yspeed / 2;\r
+                       break;\r
+               case 11:\r
+\r
+               case 12:\r
+                       ob->xspeed = 0;\r
+                       ob->yspeed = -(speed / 256);\r
+                       break;\r
+               case 13:\r
+                       ob->yspeed = speed / 286;\r
+                       ob->xspeed = ob->yspeed / 2;\r
+                       break;\r
+               case 14:\r
+                       ob->xspeed = speed / 362;\r
+                       ob->yspeed = speed / 362;\r
+                       break;\r
+               case 15:\r
+                       ob->xspeed = speed / 286;\r
+                       ob->yspeed = ob->xspeed / 2;\r
+                       break;\r
+               }\r
+\r
+               if (speed < 256*16)\r
+               {\r
+                       ChangeState(ob, ob->state->nextstate);\r
+               }\r
+       }\r
+}\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                 BONUS ITEMS\r
+\r
+temp1 = bonus type\r
+temp2 = base shape number\r
+temp3 = last animated shape number +1\r
+\r
+=============================================================================\r
+*/\r
+\r
+statetype s_bonus1    = {0, 0, step,      false, false, 20, 0, 0, T_Bonus, NULL, R_Draw, &s_bonus2};\r
+statetype s_bonus2    = {0, 0, step,      false, false, 20, 0, 0, T_Bonus, NULL, R_Draw, &s_bonus1};\r
+statetype s_bonusfly1 = {0, 0, stepthink, false, false, 20, 0, 0, T_FlyBonus, NULL, R_Draw, &s_bonusfly2};\r
+statetype s_bonusfly2 = {0, 0, stepthink, false, false, 20, 0, 0, T_FlyBonus, NULL, R_Draw, &s_bonusfly1};\r
+statetype s_bonusrise = {0, 0, slide,     false, false, 40, 0, 8, NULL, NULL, R_Draw, NULL};\r
+\r
+statetype s_splash1   = {VIVASPLASH1SPR, VIVASPLASH1SPR, step, false, false, 8, 0, 0, NULL, NULL, R_Draw, &s_splash2};\r
+statetype s_splash2   = {VIVASPLASH2SPR, VIVASPLASH2SPR, step, false, false, 8, 0, 0, NULL, NULL, R_Draw, &s_splash3};\r
+statetype s_splash3   = {VIVASPLASH3SPR, VIVASPLASH3SPR, step, false, false, 8, 0, 0, NULL, NULL, R_Draw, &s_splash4};\r
+statetype s_splash4   = {VIVASPLASH4SPR, VIVASPLASH4SPR, step, false, false, 8, 0, 0, NULL, NULL, R_Draw, NULL};\r
+\r
+Uint16 bonusshape[] = {\r
+       REDGEM1SPR, YELLOWGEM1SPR, BLUEGEM1SPR, GREENGEM1SPR,\r
+       SUGAR1ASPR, SUGAR2ASPR, SUGAR3ASPR,\r
+       SUGAR4ASPR, SUGAR5ASPR, SUGAR6ASPR,\r
+       ONEUPASPR, STUNCLIP1SPR\r
+};\r
+\r
+/*\r
+===========================\r
+=\r
+= SpawnBonus\r
+=\r
+===========================\r
+*/\r
+\r
+void SpawnBonus(Uint16 tileX, Uint16 tileY, Uint16 type)\r
+{\r
+       GetNewObj(false);\r
+       new->needtoclip = cl_noclip;\r
+       new->priority = 2;\r
+       new->obclass = bonusobj;\r
+       new->x = CONVERT_TILE_TO_GLOBAL(tileX);\r
+       new->y = CONVERT_TILE_TO_GLOBAL(tileY);\r
+       new->ydir = -1;\r
+       new->temp1 = type;\r
+       new->temp2=new->shapenum = bonusshape[type];\r
+       new->temp3 = new->temp2+2;\r
+       NewState(new, &s_bonus1);\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= SpawnSplash\r
+=\r
+===========================\r
+*/\r
+\r
+void SpawnSplash(Uint16 tileX, Uint16 tileY)\r
+{\r
+       GetNewObj(true);\r
+       new->needtoclip = cl_noclip;\r
+       new->priority = 3;\r
+       new->obclass = inertobj;\r
+       new->x = CONVERT_TILE_TO_GLOBAL(tileX);\r
+       new->y = CONVERT_TILE_TO_GLOBAL(tileY);\r
+       NewState(new, &s_splash1);\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= T_Bonus\r
+=\r
+===========================\r
+*/\r
+\r
+void T_Bonus(objtype *ob)\r
+{\r
+       if (++ob->shapenum == ob->temp3)\r
+               ob->shapenum = ob->temp2;\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= T_FlyBonus\r
+=\r
+===========================\r
+*/\r
+\r
+void T_FlyBonus(objtype *ob)\r
+{\r
+       if (ob->hitnorth)\r
+               ob->state = &s_bonus1;\r
+\r
+       if (++ob->shapenum == ob->temp3)\r
+               ob->shapenum = ob->temp2;\r
+\r
+       DoGravity(ob);\r
+}\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                 GRABBITER\r
+\r
+=============================================================================\r
+*/\r
+\r
+statetype s_grabbiter1      = {GRABBITER1SPR,      GRABBITER1SPR,      step, false, false, 12, 0, 0, NULL, C_Grabbiter, R_Draw, &s_grabbiter2};\r
+statetype s_grabbiter2      = {GRABBITER2SPR,      GRABBITER2SPR,      step, false, false, 12, 0, 0, NULL, C_Grabbiter, R_Draw, &s_grabbiter1};\r
+statetype s_grabbitersleep1 = {GRABBITERSLEEP1SPR, GRABBITERSLEEP1SPR, step, false, false, 12, 0, 0, NULL, NULL, R_Draw, &s_grabbitersleep2};\r
+statetype s_grabbitersleep2 = {GRABBITERSLEEP2SPR, GRABBITERSLEEP2SPR, step, false, false, 12, 0, 0, NULL, NULL, R_Draw, &s_grabbitersleep1};\r
+\r
+/*\r
+===========================\r
+=\r
+= SpawnGrabbiter\r
+=\r
+===========================\r
+*/\r
+\r
+void SpawnGrabbiter(Uint16 tileX, Uint16 tileY)\r
+{\r
+       GetNewObj(false);\r
+       new->active = ac_yes;\r
+       new->needtoclip = cl_noclip;\r
+       new->priority = 2;\r
+       new->obclass = grabbiterobj;\r
+       new->x = CONVERT_TILE_TO_GLOBAL(tileX);\r
+       new->y = CONVERT_TILE_TO_GLOBAL(tileY);\r
+       if (gamestate.sandwichstate == 2)\r
+       {\r
+               NewState(new, &s_grabbitersleep1);\r
+       }\r
+       else\r
+       {\r
+               NewState(new, &s_grabbiter1);\r
+       }\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= C_Grabbiter\r
+=\r
+===========================\r
+*/\r
+\r
+void C_Grabbiter(objtype *ob, objtype *hit)\r
+{\r
+       // BUG: this is executed for every object, not just (Map-)Keen!\r
+       switch (gamestate.sandwichstate)\r
+       {\r
+       case 0:\r
+               CA_CacheGrChunk(KEENTALK1PIC);\r
+               SD_PlaySound(SND_GRABBITER);\r
+               VW_FixRefreshBuffer();\r
+               US_CenterWindow(26, 8);\r
+               VWB_DrawPic(WindowX+WindowW-48, WindowY, KEENTALK1PIC);\r
+               WindowW -= 48;\r
+               PrintY += 5;\r
+               US_CPrint(\r
+                       "Oh, no!\n"\r
+                       "It's a slavering\n"\r
+                       "Grabbiter! He says,\n"\r
+                       "\"Get me lunch and\n"\r
+                       "I'll tell ya a secret!\""\r
+                       );\r
+               VW_UpdateScreen();\r
+               SD_PlaySound(SND_NOWAY);\r
+               VW_WaitVBL(30);\r
+               IN_ClearKeysDown();\r
+               IN_Ack();\r
+               RF_ForceRefresh();\r
+\r
+               //push Keen back\r
+               xtry = -hit->xmove;\r
+               ytry = -hit->ymove;\r
+               hit->xdir = hit->ydir = 0;\r
+               ClipToWalls(hit);\r
+               break;\r
+\r
+       case 1:\r
+               gamestate.sandwichstate++;\r
+               CA_CacheGrChunk(KEENTALK1PIC);\r
+               VW_FixRefreshBuffer();\r
+               US_CenterWindow(26, 8);\r
+               VWB_DrawPic(WindowX+WindowW-48, WindowY, KEENTALK1PIC);\r
+               WindowW -= 48;\r
+               PrintY += 2;\r
+               US_CPrint(\r
+                       "The Grabbiter grabs\n"\r
+                       "the gigantic sandwich,\n"\r
+                       "downs it in one bite,\n"\r
+                       "and says,\"Here's your\n"\r
+                       "secret. Big meals\n"\r
+                       "make me sleepy!\n"     // BUG: quote is missing at the end\r
+                       );\r
+               VW_UpdateScreen();\r
+               VW_WaitVBL(30);\r
+               IN_ClearKeysDown();\r
+               IN_Ack();\r
+               ChangeState(ob, &s_grabbitersleep1);\r
+               RF_ForceRefresh();\r
+       }\r
+}\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                 ROCKET\r
+\r
+temp1 = direction\r
+temp2 = countdown to next dir check\r
+\r
+=============================================================================\r
+*/\r
+\r
+statetype s_rocket        = {ROCKETSPR,     ROCKETSPR,     think,     false, false, 0, 0, 0, NULL, C_Rocket, R_Draw, NULL};\r
+statetype s_rocketfly1    = {ROCKETFLY1SPR, ROCKETFLY1SPR, stepthink, false, false, 8, 0, 0, T_RocketFly, C_RocketFly, R_Draw, &s_rocketfly2};\r
+statetype s_rocketfly2    = {ROCKETFLY2SPR, ROCKETFLY2SPR, stepthink, false, false, 8, 0, 0, T_RocketFly, C_RocketFly, R_Draw, &s_rocketfly1};\r
+statetype s_keenrocket    = {0,             0,             think,     false, false, 0, 0, 0, T_Rocket, NULL, R_Draw, NULL};\r
+\r
+/*\r
+===========================\r
+=\r
+= SpawnRocket\r
+=\r
+===========================\r
+*/\r
+\r
+void SpawnRocket(Uint16 tileX, Uint16 tileY, Uint16 state)\r
+{\r
+       if (gamestate.rocketstate == state)\r
+       {\r
+               GetNewObj(false);\r
+               new->active = ac_yes;\r
+               new->needtoclip = cl_noclip;\r
+               new->priority = 3;\r
+               new->obclass = rocketobj;\r
+               new->x = CONVERT_TILE_TO_GLOBAL(tileX);\r
+               new->y = CONVERT_TILE_TO_GLOBAL(tileY);\r
+               NewState(new, &s_rocket);\r
+       }\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= T_Rocket\r
+=\r
+===========================\r
+*/\r
+\r
+void T_Rocket(objtype *ob)\r
+{\r
+       ob->needtoreact = true;\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= C_Rocket\r
+=\r
+===========================\r
+*/\r
+\r
+void C_Rocket(objtype *ob, objtype *hit)\r
+{\r
+       // BUG: this is executed for every object, not just (Map-)Keen!\r
+       switch (gamestate.passcardstate)\r
+       {\r
+       case 0:\r
+               CA_CacheGrChunk(KEENTALK1PIC);\r
+               VW_FixRefreshBuffer();\r
+               US_CenterWindow(26, 8);\r
+               VWB_DrawPic(WindowX+WindowW-48, WindowY, KEENTALK1PIC);\r
+               WindowW -= 48;\r
+               PrintY += 5;\r
+               US_CPrint(\r
+                       "The door makes a loud\n"\r
+                       "blooping noise.\n"\r
+                       "It says,\n"\r
+                       "\"Passcard required\n"\r
+                       "for entry.\""\r
+                       );\r
+               VW_UpdateScreen();\r
+               SD_PlaySound(SND_NOWAY);\r
+               VW_WaitVBL(30);\r
+               IN_ClearKeysDown();\r
+               IN_Ack();\r
+               RF_ForceRefresh();\r
+\r
+               //push Keen back\r
+               xtry = -hit->xmove;\r
+               ytry = -hit->ymove;\r
+               hit->xdir = hit->ydir = 0;\r
+               ClipToWalls(hit);\r
+               break;\r
+\r
+       case 1:\r
+               ob->temp1 = arrow_North;\r
+               ob->temp2 = TILEGLOBAL;\r
+               ChangeState(ob, &s_rocketfly1);\r
+\r
+               hit->x = ob->x;\r
+               hit->y = ob->y + TILEGLOBAL;\r
+               hit->needtoclip = cl_noclip;\r
+               ChangeState(hit, &s_keenrocket);\r
+               SD_PlaySound(SND_ROCKETSTART);\r
+               SD_WaitSoundDone();\r
+       }\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= C_RocketFly\r
+=\r
+===========================\r
+*/\r
+\r
+void C_RocketFly(objtype *ob, objtype *hit)\r
+{\r
+       if (hit->obclass == keenobj)\r
+       {\r
+               hit->x = ob->x;\r
+               hit->y = ob->y+TILEGLOBAL;\r
+               ChangeState(hit, hit->state);\r
+       }\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= T_RocketFly\r
+=\r
+===========================\r
+*/\r
+\r
+void T_RocketFly(objtype *ob)\r
+{\r
+       Uint16 move, tx, ty;\r
+       Sint16 dir;\r
+\r
+       //\r
+       // this code could be executed twice during the same frame because the\r
+       // object's animation/state changed during that frame, so don't update\r
+       // anything if we already have movement for the current frame i.e. the\r
+       // update code has already been executed this frame\r
+       //\r
+       if (xtry == 0 && ytry == 0)\r
+       {\r
+               if (!SD_SoundPlaying())\r
+                       SD_PlaySound(SND_ROCKETFLY);\r
+\r
+               move = tics << 5;\r
+               if (ob->temp2 > move)\r
+               {\r
+                       ob->temp2 = ob->temp2 - move;\r
+                       dir = pdirx[ob->temp1];\r
+                       if (dir == 1)\r
+                       {\r
+                               xtry = move;\r
+                       }\r
+                       else if (dir == -1)\r
+                       {\r
+                               xtry = -move;\r
+                       }\r
+                       dir = pdiry[ob->temp1];\r
+                       if (dir == 1)\r
+                       {\r
+                               ytry = move;\r
+\r
+                       }\r
+                       else if (dir == -1)\r
+                       {\r
+                               ytry = -move;\r
+                       }\r
+               }\r
+               else\r
+               {\r
+                       dir = pdirx[ob->temp1];\r
+                       if (dir == 1)\r
+                       {\r
+                               xtry = ob->temp2;\r
+                       }\r
+                       else if (dir == -1)\r
+                       {\r
+                               xtry = -ob->temp2;\r
+                       }\r
+                       dir = pdiry[ob->temp1];\r
+                       if (dir == 1)\r
+                       {\r
+                               ytry = ob->temp2;\r
+                       }\r
+                       else if (dir == -1)\r
+                       {\r
+                               ytry = -ob->temp2;\r
+                       }\r
+\r
+                       tx = CONVERT_GLOBAL_TO_TILE(ob->x + xtry);\r
+                       ty = CONVERT_GLOBAL_TO_TILE(ob->y + ytry);\r
+                       ob->temp1 = *(mapsegs[2]+mapbwidthtable[ty]/2 + tx)-DIRARROWSTART;\r
+                       if (ob->temp1 < arrow_North || ob->temp1 > arrow_None)\r
+                       {\r
+                               ob->x += xtry;\r
+                               ob->y += ytry;\r
+                               ChangeState(ob, &s_rocket);\r
+\r
+                               player->x = CONVERT_TILE_TO_GLOBAL(tx+1) + 1*PIXGLOBAL;\r
+                               player->y = CONVERT_TILE_TO_GLOBAL(ty+1);\r
+                               player->obclass = keenobj;\r
+                               player->shapenum = WORLDKEENR3SPR;\r
+                               player->needtoclip = cl_midclip;\r
+                               NewState(player, &s_worldkeen);\r
+                               gamestate.rocketstate ^= 1;\r
+                       }\r
+                       else\r
+                       {\r
+                               move -= ob->temp2;\r
+                               ob->temp2 = TILEGLOBAL - move;\r
+                               dir = pdirx[ob->temp1];\r
+                               if (dir == 1)\r
+                               {\r
+                                       xtry = xtry + move;\r
+                               }\r
+                               else if (dir == -1)\r
+                               {\r
+                                       xtry = xtry - move;\r
+                               }\r
+                               dir = pdiry[ob->temp1];\r
+                               if (dir == 1)\r
+                               {\r
+                                       ytry = ytry + move;\r
+                               }\r
+                               else if (dir == -1)\r
+                               {\r
+                                       ytry = ytry - move;\r
+                               }\r
+                       }\r
+               }\r
+       }\r
+}\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                 GRAPPLE SPOT\r
+\r
+temp1 = type (0 = top of cliff, 1 = bottom of cliff)\r
+\r
+=============================================================================\r
+*/\r
+\r
+statetype s_grapplespot   = {-1,                 -1,                 think, false, false,  0, 0,  0, NULL, C_GrappleSpot, R_Draw, NULL};\r
+statetype s_throwrope1    = {WORLDKEENTRHOW1SPR, WORLDKEENTRHOW1SPR, step,  false, false, 10, 0,  0, NULL, NULL, R_Draw, &s_throwrope2};\r
+statetype s_throwrope2    = {WORLDKEENTRHOW2SPR, WORLDKEENTRHOW2SPR, step,  false, false,  8, 0,  0, T_ThrowRope, NULL, R_Draw, &s_worldkeen};\r
+statetype s_climbrope1    = {WORLDKEENCLIMB1SPR, WORLDKEENCLIMB1SPR, slide, true,  false,  4, 0, 16, NULL, NULL, R_Draw, &s_climbrope2};\r
+statetype s_climbrope2    = {WORLDKEENCLIMB2SPR, WORLDKEENCLIMB2SPR, slide, true,  false,  4, 0, 16, T_ClimbRope, NULL, R_Draw, &s_climbrope1};\r
+statetype s_maprope       = {ROPETHROW2SPR,      ROPETHROW2SPR,      think, false, false,  0, 0,  0, NULL, NULL, R_Draw, NULL};\r
+statetype s_mapropeshort  = {ROPETHROW1SPR,      ROPETHROW1SPR,      step,  false, false, 10, 0,  0, NULL, NULL, R_Draw, &s_mapropeshort};\r
+\r
+/*\r
+===========================\r
+=\r
+= SpawnGrappleSpot\r
+=\r
+===========================\r
+*/\r
+\r
+void SpawnGrappleSpot(Uint16 tileX, Uint16 tileY, Uint16 type)\r
+{\r
+       GetNewObj(false);\r
+       new->active = ac_yes;\r
+       new->needtoclip = cl_noclip;\r
+       new->priority = 2;\r
+       new->obclass = grapplespotobj;\r
+       new->tileleft = new->tileright = tileX;\r
+       new->tiletop = new->tilebottom = tileY;\r
+       new->temp1 = type;\r
+       new->x = new->left = CONVERT_TILE_TO_GLOBAL(tileX);\r
+       new->right = new->left + TILEGLOBAL;\r
+       if (type)\r
+       {\r
+               new->y = new->top = CONVERT_TILE_TO_GLOBAL(tileY+1)-1;\r
+       }\r
+       else\r
+       {\r
+               new->y = new->top = CONVERT_TILE_TO_GLOBAL(tileY);\r
+       }\r
+       new->bottom = new->top + 1;\r
+       NewState(new, &s_grapplespot);\r
+\r
+       if (gamestate.hookstate == 2 && type)\r
+       {\r
+               GetNewObj(false);\r
+               new->active = ac_yes;\r
+               new->needtoclip = cl_noclip;\r
+               new->priority = 0;\r
+               new->obclass = inertobj;\r
+               new->x = CONVERT_TILE_TO_GLOBAL(tileX);\r
+               new->y = CONVERT_TILE_TO_GLOBAL(tileY+1);\r
+               NewState(new, &s_maprope);\r
+       }\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= T_ThrowRope\r
+=\r
+===========================\r
+*/\r
+\r
+void T_ThrowRope(objtype *ob)\r
+{\r
+       GetNewObj(false);\r
+       new->active = ac_yes;\r
+       new->needtoclip = cl_noclip;\r
+       new->priority = 0;\r
+       new->obclass = inertobj;\r
+       new->x = CONVERT_TILE_TO_GLOBAL(ob->tileright);\r
+       new->y = ob->y - 2*TILEGLOBAL;\r
+       NewState(new, &s_maprope);\r
+\r
+       ob->obclass = keenobj;\r
+       ob->shapenum = WORLDKEENU3SPR;\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= T_ClimbRope\r
+=\r
+===========================\r
+*/\r
+\r
+void T_ClimbRope(objtype *ob)\r
+{\r
+       if (--ob->temp4 == 0)\r
+       {\r
+               if (ob->ydir == 1)\r
+               {\r
+                       ob->y += 3*PIXGLOBAL;\r
+                       ob->shapenum = WORLDKEEND3SPR;\r
+               }\r
+               else\r
+               {\r
+                       ob->y -= 3*PIXGLOBAL;\r
+                       ob->shapenum = WORLDKEENU3SPR;\r
+               }\r
+               ob->obclass = keenobj;\r
+               NewState(ob, &s_worldkeen);\r
+       }\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= C_GrappleSpot\r
+=\r
+===========================\r
+*/\r
+\r
+void C_GrappleSpot(objtype *ob, objtype *hit)\r
+{\r
+       if (hit->obclass == keenobj)\r
+       {\r
+               switch (gamestate.hookstate)\r
+               {\r
+               case 0:\r
+                       CA_CacheGrChunk(KEENTALK1PIC);\r
+                       VW_FixRefreshBuffer();\r
+                       US_CenterWindow(26, 8);\r
+                       VWB_DrawPic(WindowX+WindowW-48, WindowY, KEENTALK1PIC);\r
+                       WindowW -= 48;\r
+                       PrintY += 15;\r
+                       US_CPrint(\r
+                               "What a tall cliff!\n"\r
+                               "Wish I had a rope\n"\r
+                               "and grappling hook.\n"\r
+                               );\r
+                       VW_UpdateScreen();\r
+                       SD_PlaySound(SND_NOWAY);\r
+                       VW_WaitVBL(30);\r
+                       IN_ClearKeysDown();\r
+                       IN_Ack();\r
+                       RF_ForceRefresh();\r
+\r
+                       //push Keen back\r
+                       xtry = -hit->xmove;\r
+                       ytry = -hit->ymove;\r
+                       hit->xdir = hit->ydir = 0;\r
+                       ClipToWalls(hit);\r
+                       break;\r
+\r
+               case 1:\r
+                       gamestate.hookstate++;\r
+                       SD_PlaySound(SND_THROWROPE);\r
+                       ChangeState(hit, &s_throwrope1);\r
+                       hit->obclass = inertobj;\r
+                       break;\r
+\r
+               case 2:\r
+                       if (ob->temp1)\r
+                       {\r
+                               hit->y += 4*PIXGLOBAL;\r
+                               hit->temp4 = 6;\r
+                               hit->ydir = 1;\r
+                       }\r
+                       else\r
+                       {\r
+                               hit->y -= 4*PIXGLOBAL;\r
+                               hit->temp4 = 6;\r
+                               hit->ydir = -1;\r
+                       }\r
+                       NewState(hit, &s_climbrope1);\r
+                       hit->obclass = inertobj;\r
+               }\r
+       }\r
+}\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                 SATELLITE\r
+\r
+temp1 = direction (satellite) / type (stop points)\r
+temp2 = countdown to next dir check\r
+temp3 = type of stop point touched (low byte: current; high byte: previous)\r
+        is updated every frame and resets to 0 when no longer touching a spot\r
+temp4 = type of last stop point passed over (1 or 2, never 0)\r
+        is updated when no longer touching the spot\r
+\r
+=============================================================================\r
+*/\r
+\r
+statetype s_satellitestopspot  = {-1,               -1,               think,     false, false,  0, 0, 0, NULL, NULL, NULL, NULL};\r
+statetype s_worldkeensatellite = {WORLDKEENHANGSPR, WORLDKEENHANGSPR, think,     false, false,  0, 0, 0, NULL, NULL, R_WorldKeenSatellite, NULL};\r
+statetype s_satellite1         = {SATELLITE1SPR,    SATELLITE1SPR,    stepthink, false, false, 10, 0, 0, T_Satellite, C_Satellite, R_Draw, &s_satellite2};\r
+statetype s_satellite2         = {SATELLITE2SPR,    SATELLITE2SPR,    stepthink, false, false, 10, 0, 0, T_Satellite, C_Satellite, R_Draw, &s_satellite3};\r
+statetype s_satellite3         = {SATELLITE3SPR,    SATELLITE3SPR,    stepthink, false, false, 10, 0, 0, T_Satellite, C_Satellite, R_Draw, &s_satellite4};\r
+statetype s_satellite4         = {SATELLITE4SPR,    SATELLITE4SPR,    stepthink, false, false, 10, 0, 0, T_Satellite, C_Satellite, R_Draw, &s_satellite1};\r
+\r
+/*\r
+===========================\r
+=\r
+= SpawnSatelliteStop\r
+=\r
+===========================\r
+*/\r
+\r
+void SpawnSatelliteStop(Uint16 tileX, Uint16 tileY, Uint16 type)\r
+{\r
+       GetNewObj(false);\r
+       new->active = ac_allways;\r
+       new->needtoclip = cl_noclip;\r
+       new->priority = 2;\r
+       new->obclass = satellitestopobj;\r
+       new->tileleft=new->tileright=tileX;\r
+       new->tiletop=new->tilebottom=tileY;\r
+       new->temp1 = (type ^ 1) + 1;    // type is either 0 or 1, so this just maps 0 to 2 and 1 to 1\r
+       new->x=new->left = CONVERT_TILE_TO_GLOBAL(tileX);\r
+       new->right = new->left + TILEGLOBAL;\r
+       new->y=new->top = CONVERT_TILE_TO_GLOBAL(tileY);\r
+       new->bottom = new->top + TILEGLOBAL;\r
+       NewState(new, &s_satellitestopspot);\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= SpawnSatellite\r
+=\r
+===========================\r
+*/\r
+\r
+void SpawnSatellite(Uint16 tileX, Uint16 tileY)\r
+{\r
+       Sint16 dir;\r
+\r
+       GetNewObj(false);\r
+       new->needtoclip = cl_noclip;\r
+       new->priority = 2;\r
+       new->active = ac_allways;\r
+       new->obclass = satelliteobj;\r
+       new->x = CONVERT_TILE_TO_GLOBAL(tileX);\r
+       new->y = CONVERT_TILE_TO_GLOBAL(tileY);\r
+       new->xdir = 0;\r
+       new->ydir = 1;\r
+       NewState(new, &s_satellite1);\r
+\r
+       dir = arrow_West;\r
+       (mapsegs[2]+mapbwidthtable[tileY]/2)[tileX] = (dir+DIRARROWSTART);\r
+       new->temp1 = dir;\r
+       new->temp2 = TILEGLOBAL;\r
+       new->temp4 = 2;\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= T_Satellite\r
+=\r
+===========================\r
+*/\r
+\r
+void T_Satellite(objtype *ob)\r
+{\r
+       //\r
+       // this code could be executed twice during the same frame because the\r
+       // object's animation/state changed during that frame, so don't update\r
+       // anything if we already have movement for the current frame i.e. the\r
+       // update code has already been executed this frame\r
+       //\r
+       if (xtry == 0 && ytry == 0)\r
+       {\r
+               //\r
+               // if current stop spot type is 0 (not touching a spot), but previous\r
+               // type is not 0, then set temp4 to the previous type\r
+               //\r
+               if (!(ob->temp3 & 0xFF) && (ob->temp3 & 0xFF00))\r
+               {\r
+                       ob->temp4 = ob->temp3 >> 8;\r
+               }\r
+               //\r
+               // move current type into previous type and set current stop type to 0\r
+               //\r
+               ob->temp3 <<= 8;\r
+\r
+               //\r
+               // follow the arrow path like a GoPlat\r
+               //\r
+               T_GoPlat(ob);\r
+       }\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= C_Satellite\r
+=\r
+===========================\r
+*/\r
+\r
+void C_Satellite(objtype *ob, objtype *hit)\r
+{\r
+       Sint16 temp;\r
+       objtype *o;\r
+\r
+       if (hit->state == &s_satellitestopspot)\r
+       {\r
+               ob->temp3 |= hit->temp1;\r
+       }\r
+       else if (hit->obclass == keenobj)\r
+       {\r
+               //\r
+               // check if satellite has reaced a new stop spot\r
+               //\r
+               temp = ob->temp3 >> 8;\r
+               if (temp && temp != ob->temp4)\r
+               {\r
+                       SD_PlaySound(SND_GRABSATELLITE);\r
+                       //\r
+                       // update last spot value (don't grab or drop Keen until moved to the next spot)\r
+                       //\r
+                       ob->temp4 = temp;\r
+                       if (player->state == &s_worldkeensatellite)\r
+                       {\r
+                               //\r
+                               // drop Keen off at the current stop spot\r
+                               //\r
+                               for (o=player->next; o; o=o->next)\r
+                               {\r
+                                       if (o->obclass == satellitestopobj && o->temp1 == temp)\r
+                                       {\r
+                                               hit->x = o->x;\r
+                                               hit->y = o->y;\r
+                                               hit->shapenum = WORLDKEENU3SPR;\r
+                                               ChangeState(player, &s_worldkeen);\r
+                                               hit->needtoclip = cl_midclip;\r
+                                               return;\r
+                                       }\r
+                               }\r
+                       }\r
+                       else\r
+                       {\r
+                               //\r
+                               // grab and carry Keen\r
+                               //\r
+                               hit->x = ob->x + 12*PIXGLOBAL;\r
+                               hit->y = ob->y + 16*PIXGLOBAL;\r
+                               hit->needtoclip = cl_noclip;\r
+                               ChangeState(player, &s_worldkeensatellite);\r
+                       }\r
+               }\r
+               else if (hit->state == &s_worldkeensatellite)\r
+               {\r
+                       //\r
+                       // move Keen along with the satellite\r
+                       //\r
+                       hit->x = ob->x + 12*PIXGLOBAL;\r
+                       hit->y = ob->y + 16*PIXGLOBAL;\r
+                       ChangeState(hit, hit->state);\r
+               }\r
+       }\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= R_WorldKeenSatellite\r
+=\r
+===========================\r
+*/\r
+\r
+void R_WorldKeenSatellite(objtype *ob)\r
+{\r
+       RF_PlaceSprite(&ob->sprite, ob->x + 4*PIXGLOBAL, ob->y + 8*PIXGLOBAL, WORLDKEENHANGSPR, spritedraw, 1);\r
+}\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                 SANDWICH\r
+\r
+=============================================================================\r
+*/\r
+\r
+statetype s_sandwich      = {SANDWICHSPR, SANDWICHSPR, think, false, false, 0, 0, 0, NULL, C_Molly, R_Draw, NULL};\r
+\r
+/*\r
+===========================\r
+=\r
+= SpawnSandwich\r
+=\r
+===========================\r
+*/\r
+\r
+void SpawnSandwich(Uint16 tileX, Uint16 tileY)\r
+{\r
+       GetNewObj(false);\r
+       new->needtoclip = cl_noclip;\r
+       new->priority = 2;\r
+       new->obclass = sandwichobj;\r
+       new->x = CONVERT_TILE_TO_GLOBAL(tileX);\r
+       new->y = CONVERT_TILE_TO_GLOBAL(tileY);\r
+       NewState(new, &s_sandwich);\r
+}\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                 GRAPPLING HOOK\r
+\r
+=============================================================================\r
+*/\r
+\r
+statetype s_hook          = {HOOKSPR, HOOKSPR, think, false, false, 0, 0, 0, NULL, C_Molly, R_Draw, NULL};\r
+\r
+/*\r
+===========================\r
+=\r
+= SpawnHook\r
+=\r
+===========================\r
+*/\r
+\r
+void SpawnHook(Uint16 tileX, Uint16 tileY)\r
+{\r
+       GetNewObj(false);\r
+       new->needtoclip = cl_noclip;\r
+       new->priority = 2;\r
+       new->obclass = hookobj;\r
+       new->x = CONVERT_TILE_TO_GLOBAL(tileX);\r
+       new->y = CONVERT_TILE_TO_GLOBAL(tileY);\r
+       NewState(new, &s_hook);\r
+}\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                 PASSCARD\r
+\r
+=============================================================================\r
+*/\r
+\r
+statetype s_passcard      = {PASSCARDSPR, PASSCARDSPR, think, false, false, 0, 0, 0, NULL, C_Molly, R_Draw, NULL};\r
+\r
+/*\r
+===========================\r
+=\r
+= SpawnPasscard\r
+=\r
+===========================\r
+*/\r
+\r
+void SpawnPasscard(Uint16 tileX, Uint16 tileY)\r
+{\r
+       GetNewObj(false);\r
+       new->needtoclip = cl_noclip;\r
+       new->priority = 2;\r
+       new->obclass = passcardobj;\r
+       new->x = CONVERT_TILE_TO_GLOBAL(tileX);\r
+       new->y = CONVERT_TILE_TO_GLOBAL(tileY);\r
+       NewState(new, &s_passcard);\r
+}\r
+\r
+//============================================================================\r
+\r
+/*\r
+===========================\r
+=\r
+= C_Molly\r
+=\r
+===========================\r
+*/\r
+\r
+void C_Molly(objtype *ob, objtype *hit)\r
+{\r
+       if (hit->obclass == keenobj)\r
+       {\r
+               switch (ob->obclass)\r
+               {\r
+               case sandwichobj:\r
+                       playstate = ex_sandwich;\r
+                       break;\r
+\r
+               case hookobj:\r
+                       playstate = ex_hook;\r
+                       break;\r
+\r
+               case passcardobj:\r
+                       playstate = ex_card;\r
+                       break;\r
+\r
+               case mollyobj:\r
+                       playstate = ex_molly;\r
+               }\r
+       }\r
+}\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                 MOLLY\r
+\r
+=============================================================================\r
+*/\r
+\r
+statetype s_molly1        = {MOLLY1SPR, MOLLY1SPR, step, false, false, 20, 0, 0, NULL, C_Molly, R_Draw, &s_molly2};\r
+statetype s_molly2        = {MOLLY2SPR, MOLLY2SPR, step, false, false, 40, 0, 0, NULL, C_Molly, R_Draw, &s_molly3};\r
+statetype s_molly3        = {MOLLY1SPR, MOLLY1SPR, step, false, false, 40, 0, 0, NULL, C_Molly, R_Draw, &s_molly4};\r
+statetype s_molly4        = {MOLLY2SPR, MOLLY2SPR, step, false, false, 20, 0, 0, NULL, C_Molly, R_Draw, &s_molly1};\r
+\r
+/*\r
+===========================\r
+=\r
+= SpawnMolly\r
+=\r
+===========================\r
+*/\r
+\r
+void SpawnMolly(Uint16 tileX, Uint16 tileY)\r
+{\r
+       GetNewObj(false);\r
+       new->obclass = mollyobj;\r
+       new->active = ac_yes;\r
+       new->priority = 0;\r
+       new->x = CONVERT_TILE_TO_GLOBAL(tileX);\r
+       new->y = CONVERT_TILE_TO_GLOBAL(tileY) + -8*PIXGLOBAL;\r
+       if (US_RndT() < 0x80)\r
+       {\r
+               new->xdir = 1;\r
+       }\r
+       else\r
+       {\r
+               new->xdir = -1;\r
+       }\r
+       new->ydir = 1;\r
+       NewState(new, &s_molly1);\r
+}\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                 PLATFORM\r
+\r
+=============================================================================\r
+*/\r
+\r
+statetype s_platform      = {PLATFORMSPR, PLATFORMSPR, think, false, false, 0, 0, 0, T_Platform, NULL, R_Draw, NULL};\r
+\r
+/*\r
+===========================\r
+=\r
+= SpawnPlatform\r
+=\r
+===========================\r
+*/\r
+\r
+void SpawnPlatform(Uint16 tileX, Uint16 tileY, Sint16 dir)\r
+{\r
+       GetNewObj(false);\r
+       new->obclass = platformobj;\r
+       new->active = ac_allways;\r
+       new->priority = 0;\r
+       new->x = CONVERT_TILE_TO_GLOBAL(tileX);\r
+       new->y = CONVERT_TILE_TO_GLOBAL(tileY);\r
+       switch (dir)\r
+       {\r
+       case 0:\r
+               new->xdir = 0;\r
+               new->ydir = -1;\r
+               break;\r
+       case 1:\r
+               new->xdir = 1;\r
+               new->ydir = 0;\r
+               break;\r
+       case 2:\r
+               new->xdir = 0;\r
+               new->ydir = 1;\r
+               break;\r
+       case 3:\r
+               new->xdir = -1;\r
+               new->ydir = 0;\r
+       }\r
+       NewState(new, &s_platform);\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= T_Platform\r
+=\r
+===========================\r
+*/\r
+\r
+void T_Platform(objtype *ob)\r
+{\r
+       Uint16 newpos, newtile;\r
+\r
+       xtry = ob->xdir * 12 * tics;\r
+       ytry = ob->ydir * 12 * tics;\r
+\r
+       if (ob->xdir == 1)\r
+       {\r
+               newpos = ob->right + xtry;\r
+               newtile = CONVERT_GLOBAL_TO_TILE(newpos);\r
+               if (ob->tileright != newtile)\r
+               {\r
+                       if (*(mapsegs[2]+mapbwidthtable[ob->tiletop]/2 + newtile) == PLATFORMBLOCK)\r
+                       {\r
+                               ob->xdir = -1;\r
+                               xtry = xtry - (newpos & 0xFF);\r
+                       }\r
+               }\r
+       }\r
+       else if (ob->xdir == -1)\r
+       {\r
+               newpos = ob->left + xtry;\r
+               newtile = CONVERT_GLOBAL_TO_TILE(newpos);\r
+               if (ob->tileleft != newtile)\r
+               {\r
+                       if (*(mapsegs[2]+mapbwidthtable[ob->tiletop]/2 + newtile) == PLATFORMBLOCK)\r
+                       {\r
+                               ob->xdir = 1;\r
+                               xtry = xtry + (TILEGLOBAL - (newpos & 0xFF));\r
+                       }\r
+               }\r
+       }\r
+       else if (ob->ydir == 1)\r
+       {\r
+               newpos = ob->bottom + ytry;\r
+               newtile = CONVERT_GLOBAL_TO_TILE(newpos);\r
+               if (ob->tilebottom != newtile)\r
+               {\r
+                       if (*(mapsegs[2]+mapbwidthtable[newtile]/2 + ob->tileleft) == PLATFORMBLOCK)\r
+                       {\r
+                               if (*(mapsegs[2]+mapbwidthtable[newtile-2]/2 + ob->tileleft) == PLATFORMBLOCK)\r
+                               {\r
+                                       ytry = 0;\r
+                                       ob->needtoreact = true;\r
+                               }\r
+                               else\r
+                               {\r
+                                       ob->ydir = -1;\r
+                                       ytry = ytry - (newpos & 0xFF);\r
+                               }\r
+                       }\r
+               }\r
+       }\r
+       else if (ob->ydir == -1)\r
+       {\r
+               newpos = ob->top + ytry;\r
+               newtile = CONVERT_GLOBAL_TO_TILE(newpos);\r
+               if (ob->tiletop != newtile)\r
+               {\r
+                       if (*(mapsegs[2]+mapbwidthtable[newtile]/2 + ob->tileleft) == PLATFORMBLOCK)\r
+                       {\r
+                               if (*(mapsegs[2]+mapbwidthtable[newtile+2]/2 + ob->tileleft) == PLATFORMBLOCK)\r
+                               {\r
+                                       ytry = 0;\r
+                                       ob->needtoreact = true;\r
+                               }\r
+                               else\r
+                               {\r
+                                       ob->ydir = 1;\r
+                                       ytry = ytry + (TILEGLOBAL - (newpos & 0xFF));\r
+                               }\r
+                       }\r
+               }\r
+       }\r
+}\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                 DROPPING PLATFORM\r
+\r
+temp1 = initial y position\r
+\r
+=============================================================================\r
+*/\r
+\r
+statetype s_dropplatsit  = {PLATFORMSPR, PLATFORMSPR, think,      false, false, 0, 0,   0, T_DropPlatSit, NULL, R_Draw, NULL};\r
+statetype s_fallplatfall = {PLATFORMSPR, PLATFORMSPR, think,      false, false, 0, 0,   0, T_DropPlatFall, NULL, R_Draw, NULL};\r
+statetype s_fallplatrise = {PLATFORMSPR, PLATFORMSPR, slidethink, false, false, 0, 0, -32, T_DropPlatRise, NULL, R_Draw, NULL};\r
+\r
+/*\r
+===========================\r
+=\r
+= SpawnDropPlat\r
+=\r
+===========================\r
+*/\r
+\r
+void SpawnDropPlat(Uint16 tileX, Uint16 tileY)\r
+{\r
+       GetNewObj(false);\r
+       new->obclass = platformobj;\r
+       new->active = ac_allways;\r
+       new->priority = 0;\r
+       new->x = CONVERT_TILE_TO_GLOBAL(tileX);\r
+       new->y=new->temp1 = CONVERT_TILE_TO_GLOBAL(tileY);\r
+       new->xdir = 0;\r
+       new->ydir = 1;\r
+       new->needtoclip = cl_noclip;\r
+       NewState(new, &s_dropplatsit);\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= T_DropPlatSit\r
+=\r
+===========================\r
+*/\r
+\r
+void T_DropPlatSit(objtype *ob)\r
+{\r
+       if (gamestate.riding == ob)\r
+       {\r
+               ytry = tics << 4;       //tics * 16;\r
+               ob->yspeed = 0;\r
+               if (ob->y + ytry - ob->temp1 >= 8*PIXGLOBAL)\r
+                       ob->state = &s_fallplatfall;\r
+       }\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= T_DropPlatFall\r
+=\r
+===========================\r
+*/\r
+\r
+void T_DropPlatFall(objtype *ob)\r
+{\r
+       Uint16 newpos, newtile;\r
+\r
+       DoGravity(ob);\r
+\r
+#if 0\r
+       // bugfix: don't skip a tile (this is present in Keen 4, but missing in 5 & 6)\r
+       if (ytry >= 15*PIXGLOBAL)\r
+               ytry = 15*PIXGLOBAL;\r
+#endif\r
+\r
+       newpos = ob->bottom + ytry;\r
+       newtile = CONVERT_GLOBAL_TO_TILE(newpos);\r
+       if (ob->tilebottom != newtile)\r
+       {\r
+               if (*(mapsegs[2]+mapbwidthtable[newtile]/2 + ob->tileleft) == PLATFORMBLOCK)\r
+               {\r
+                       ytry = 0xFF - (ob->bottom & 0xFF);\r
+                       if (gamestate.riding != ob)\r
+                               ob->state = &s_fallplatrise;\r
+               }\r
+       }\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= T_DropPlatRise\r
+=\r
+===========================\r
+*/\r
+\r
+void T_DropPlatRise(objtype *ob)\r
+{\r
+       if (gamestate.riding == ob)\r
+       {\r
+               ob->yspeed = 0;\r
+               ob->state = &s_fallplatfall;\r
+       }\r
+       else if (ob->y <= ob->temp1)\r
+       {\r
+               ytry = ob->temp1 - ob->y;\r
+               ob->state = &s_dropplatsit;\r
+       }\r
+}\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                 STATIC PLATFORM\r
+\r
+temp1 = initial y position (is set but never used)\r
+\r
+=============================================================================\r
+*/\r
+\r
+statetype s_staticplatform = {PLATFORMSPR, PLATFORMSPR, step, false, false, 32000, 0, 0, NULL, NULL, R_Draw, &s_staticplatform};\r
+\r
+/*\r
+===========================\r
+=\r
+= SpawnStaticPlat\r
+=\r
+===========================\r
+*/\r
+\r
+void SpawnStaticPlat(Uint16 tileX, Uint16 tileY)\r
+{\r
+       GetNewObj(false);\r
+       new->obclass = platformobj;\r
+       new->active = ac_yes;\r
+       new->priority = 0;\r
+       new->x = CONVERT_TILE_TO_GLOBAL(tileX);\r
+       new->y=new->temp1 = CONVERT_TILE_TO_GLOBAL(tileY);\r
+       new->xdir = 0;\r
+       new->ydir = 1;\r
+       new->needtoclip = cl_noclip;\r
+       NewState(new, &s_staticplatform);\r
+}\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                 GO PLATFORM\r
+\r
+temp1 = direction\r
+temp2 = countdown to next dir check\r
+temp3 = sprite pointer for the Bip sprite\r
+\r
+=============================================================================\r
+*/\r
+\r
+statetype s_goplat        = {PLATFORMSPR, PLATFORMSPR, think, false, false, 0, 0, 0, T_GoPlat, NULL, R_GoPlat, NULL};\r
+\r
+/*\r
+===========================\r
+=\r
+= SpawnGoPlat\r
+=\r
+===========================\r
+*/\r
+\r
+void SpawnGoPlat(Uint16 tileX, Uint16 tileY, Sint16 dir)\r
+{\r
+       GetNewObj(false);\r
+       new->obclass = platformobj;\r
+       new->active = ac_allways;\r
+       new->priority = 0;\r
+       new->x = CONVERT_TILE_TO_GLOBAL(tileX);\r
+       new->y = CONVERT_TILE_TO_GLOBAL(tileY);\r
+       new->xdir = 0;\r
+       new->ydir = 1;\r
+       new->needtoclip = cl_noclip;\r
+       NewState(new, &s_goplat);\r
+       *(mapsegs[2]+mapbwidthtable[tileY]/2 + tileX) = dir + DIRARROWSTART;\r
+       new->temp1 = dir;\r
+       new->temp2 = TILEGLOBAL;\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= T_GoPlat\r
+=\r
+===========================\r
+*/\r
+\r
+void T_GoPlat(objtype *ob)\r
+{\r
+       Uint16 move;\r
+       Sint16 dir;\r
+       Uint16 tx, ty;\r
+\r
+       move = tics * 12;\r
+       if (ob->temp2 > move)\r
+       {\r
+               ob->temp2 = ob->temp2 - move;\r
+\r
+               dir = pdirx[ob->temp1];\r
+               if (dir == 1)\r
+               {\r
+                       xtry = xtry + move;\r
+               }\r
+               else if (dir == -1)\r
+               {\r
+                       xtry = xtry + -move;\r
+               }\r
+\r
+               dir = pdiry[ob->temp1];\r
+               if (dir == 1)\r
+               {\r
+                       ytry = ytry + move;\r
+               }\r
+               else if (dir == -1)\r
+               {\r
+                       ytry = ytry + -move;\r
+               }\r
+       }\r
+       else\r
+       {\r
+               dir = pdirx[ob->temp1];\r
+               if (dir == 1)\r
+               {\r
+                       xtry += ob->temp2;\r
+               }\r
+               else if (dir == -1)\r
+               {\r
+                       xtry += -ob->temp2;\r
+               }\r
+\r
+               dir = pdiry[ob->temp1];\r
+               if (dir == 1)\r
+               {\r
+                       ytry += ob->temp2;\r
+               }\r
+               else if (dir == -1)\r
+               {\r
+                       ytry += -ob->temp2;\r
+               }\r
+\r
+               tx = CONVERT_GLOBAL_TO_TILE(ob->x + xtry);\r
+               ty = CONVERT_GLOBAL_TO_TILE(ob->y + ytry);\r
+               ob->temp1 = *(mapsegs[2]+mapbwidthtable[ty]/2 + tx) - DIRARROWSTART;\r
+               if (ob->temp1 < arrow_North || ob->temp1 > arrow_None)\r
+               {\r
+                       Quit("Goplat moved to a bad spot!");\r
+               }\r
+\r
+               move -= ob->temp2;\r
+               ob->temp2 = TILEGLOBAL - move;\r
+\r
+               dir = pdirx[ob->temp1];\r
+               if (dir == 1)\r
+               {\r
+                       xtry = xtry + move;\r
+               }\r
+               else if (dir == -1)\r
+               {\r
+                       xtry = xtry - move;\r
+               }\r
+\r
+               dir = pdiry[ob->temp1];\r
+               if (dir == 1)\r
+               {\r
+                       ytry = ytry + move;\r
+               }\r
+               else if (dir == -1)\r
+               {\r
+                       ytry = ytry - move;\r
+               }\r
+       }\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= R_GoPlat\r
+=\r
+===========================\r
+*/\r
+\r
+void R_GoPlat(objtype *ob)\r
+{\r
+       RF_PlaceSprite(&ob->sprite, ob->x, ob->y, ob->shapenum, spritedraw, ob->priority);\r
+       RF_PlaceSprite((void**)&ob->temp3, ob->x+TILEGLOBAL, ob->y+TILEGLOBAL, ob->temp1+PLATBIP1SPR, spritedraw, ob->priority);\r
+}\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                 SNEAKY PLATFORM\r
+\r
+temp1 = initial x position (is set but never used)\r
+\r
+=============================================================================\r
+*/\r
+\r
+statetype s_sneakplatsit    = {PLATFORMSPR, PLATFORMSPR, think, false, false,  0,   0, 0, T_SneakPlat, NULL, R_Draw, NULL};\r
+statetype s_sneakplatdodge  = {PLATFORMSPR, PLATFORMSPR, slide, false, false, 48,  32, 0, NULL, NULL, R_Draw, &s_sneakplatreturn};\r
+statetype s_sneakplatreturn = {PLATFORMSPR, PLATFORMSPR, slide, false, false, 96, -16, 0, NULL, NULL, R_Draw, &s_sneakplatsit};\r
+\r
+/*\r
+===========================\r
+=\r
+= SpawnSneakPlat\r
+=\r
+===========================\r
+*/\r
+\r
+void SpawnSneakPlat(Uint16 tileX, Uint16 tileY)\r
+{\r
+       GetNewObj(false);\r
+       new->obclass = platformobj;\r
+       new->active = ac_allways;\r
+       new->priority = 0;\r
+       new->x=new->temp1 = CONVERT_TILE_TO_GLOBAL(tileX);\r
+       new->y = CONVERT_TILE_TO_GLOBAL(tileY);\r
+       new->xdir = 0;\r
+       new->ydir = 1;\r
+       new->needtoclip = cl_noclip;\r
+       NewState(new, &s_sneakplatsit);\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= T_SneakPlat\r
+=\r
+===========================\r
+*/\r
+\r
+void T_SneakPlat(objtype *ob)\r
+{\r
+       Sint16 dist;\r
+\r
+       if (player->state != &s_keenjump1)\r
+               return;\r
+\r
+       if (player->xdir == 1)\r
+       {\r
+               dist = ob->left-player->right;\r
+               if (dist > 4*TILEGLOBAL || dist < 0)\r
+                       return;\r
+       }\r
+       else\r
+       {\r
+               dist = player->left-ob->right;\r
+               if (dist > 4*TILEGLOBAL || dist < 0)\r
+                       return;\r
+       }\r
+\r
+       dist = player->y - ob->y;\r
+       if (dist < -6*TILEGLOBAL || dist > 6*TILEGLOBAL)\r
+               return;\r
+\r
+       ob->xdir = player->xdir;\r
+       ob->state = &s_sneakplatdodge;\r
+}\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                 BLOOG\r
+\r
+=============================================================================\r
+*/\r
+\r
+statetype s_bloogwalk1 = {BLOOGWALKL1SPR, BLOOGWALKR1SPR, step,  false, true, 10, 128, 0, T_BloogWalk, C_Bloog, R_Walk, &s_bloogwalk2};\r
+statetype s_bloogwalk2 = {BLOOGWALKL2SPR, BLOOGWALKR2SPR, step,  false, true, 10, 128, 0, T_BloogWalk, C_Bloog, R_Walk, &s_bloogwalk3};\r
+statetype s_bloogwalk3 = {BLOOGWALKL3SPR, BLOOGWALKR3SPR, step,  false, true, 10, 128, 0, T_BloogWalk, C_Bloog, R_Walk, &s_bloogwalk4};\r
+statetype s_bloogwalk4 = {BLOOGWALKL4SPR, BLOOGWALKR4SPR, step,  false, true, 10, 128, 0, T_BloogWalk, C_Bloog, R_Walk, &s_bloogwalk1};\r
+statetype s_bloogstun  = {BLOOGSTUNSPR,   BLOOGSTUNSPR,   think, false, false, 0,   0, 0, T_Projectile, NULL, R_Stunned, &s_bloogstun};\r
+\r
+/*\r
+===========================\r
+=\r
+= SpawnBloog\r
+=\r
+===========================\r
+*/\r
+\r
+void SpawnBloog(Uint16 tileX, Uint16 tileY)\r
+{\r
+       GetNewObj(false);\r
+       new->obclass = bloogobj;\r
+       new->active = ac_yes;\r
+       new->priority = 0;\r
+       new->x = CONVERT_TILE_TO_GLOBAL(tileX);\r
+       new->y = CONVERT_TILE_TO_GLOBAL(tileY) + -2*TILEGLOBAL;\r
+       if (US_RndT() < 0x80)\r
+       {\r
+               new->xdir = 1;\r
+       }\r
+       else\r
+       {\r
+               new->xdir = -1;\r
+       }\r
+       new->ydir = 1;\r
+       NewState(new, &s_bloogwalk1);\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= T_BloogWalk\r
+=\r
+===========================\r
+*/\r
+\r
+void T_BloogWalk(objtype *ob)\r
+{\r
+       if (US_RndT() < 0x20)\r
+       {\r
+               if (ob->x < player->x)\r
+               {\r
+                       ob->xdir = 1;\r
+               }\r
+               else\r
+               {\r
+                       ob->xdir = -1;\r
+               }\r
+       }\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= C_Bloog\r
+=\r
+===========================\r
+*/\r
+\r
+void C_Bloog(objtype *ob, objtype *hit)\r
+{\r
+       if (hit->obclass == keenobj)\r
+       {\r
+               KillKeen();\r
+       }\r
+       else if (hit->obclass == stunshotobj)\r
+       {\r
+               StunObj(ob, hit, &s_bloogstun);\r
+       }\r
+}\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                 BLOOGUARD\r
+\r
+temp1 = flash countdown\r
+temp2 = health\r
+\r
+=============================================================================\r
+*/\r
+\r
+statetype s_blooguardwalk1   = {BLOOGUARDWALKL1SPR,  BLOOGUARDWALKR1SPR,  step,  false, true,  9, 128, 0, T_BlooguardWalk, C_Blooguard, R_Blooguard, &s_blooguardwalk2};\r
+statetype s_blooguardwalk2   = {BLOOGUARDWALKL2SPR,  BLOOGUARDWALKR2SPR,  step,  false, true,  9, 128, 0, T_BlooguardWalk, C_Blooguard, R_Blooguard, &s_blooguardwalk3};\r
+statetype s_blooguardwalk3   = {BLOOGUARDWALKL3SPR,  BLOOGUARDWALKR3SPR,  step,  false, true,  9, 128, 0, T_BlooguardWalk, C_Blooguard, R_Blooguard, &s_blooguardwalk4};\r
+statetype s_blooguardwalk4   = {BLOOGUARDWALKL4SPR,  BLOOGUARDWALKR4SPR,  step,  false, true,  9, 128, 0, T_BlooguardWalk, C_Blooguard, R_Blooguard, &s_blooguardwalk1};\r
+statetype s_blooguardattack1 = {BLOOGUARDSWINGL1SPR, BLOOGUARDSWINGR1SPR, step,  false, true, 30,   0, 0, NULL, C_Blooguard, R_Blooguard, &s_blooguardattack2};\r
+statetype s_blooguardattack2 = {BLOOGUARDSWINGL2SPR, BLOOGUARDSWINGR2SPR, step,  false, true,  9,   0, 0, NULL, C_Blooguard, R_Blooguard, &s_blooguardattack3};\r
+statetype s_blooguardattack3 = {BLOOGUARDSWINGL3SPR, BLOOGUARDSWINGR3SPR, step,  true,  true,  1,   0, 0, T_BlooguardAttack, C_Blooguard, R_Blooguard, &s_blooguardattack4};\r
+statetype s_blooguardattack4 = {BLOOGUARDSWINGL3SPR, BLOOGUARDSWINGR3SPR, step,  false, true,  9,   0, 0, NULL, C_Blooguard, R_Blooguard, &s_blooguardwalk1};\r
+statetype s_blooguardstun    = {BLOOGUARDSTUNSPR,    BLOOGUARDSTUNSPR,    think, false, false, 0,   0, 0, T_Projectile, NULL, R_Stunned, &s_blooguardstun};\r
+\r
+/*\r
+===========================\r
+=\r
+= SpawnBlooguard\r
+=\r
+===========================\r
+*/\r
+\r
+void SpawnBlooguard(Uint16 tileX, Uint16 tileY)\r
+{\r
+       GetNewObj(false);\r
+       new->obclass = blooguardobj;\r
+       new->active = ac_yes;\r
+       new->priority = 0;\r
+       new->x = CONVERT_TILE_TO_GLOBAL(tileX);\r
+       new->y = CONVERT_TILE_TO_GLOBAL(tileY) + -40*PIXGLOBAL;\r
+       if (US_RndT() < 0x80)\r
+       {\r
+               new->xdir = 1;\r
+       }\r
+       else\r
+       {\r
+               new->xdir = -1;\r
+       }\r
+       new->ydir = 1;\r
+       new->temp2 = 3; // health\r
+       NewState(new, &s_blooguardwalk1);\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= T_BlooguardWalk\r
+=\r
+===========================\r
+*/\r
+\r
+void T_BlooguardWalk(objtype *ob)\r
+{\r
+       if (US_RndT() < 0x20)\r
+       {\r
+               if (ob->x < player->x)\r
+               {\r
+                       ob->xdir = 1;\r
+               }\r
+               else\r
+               {\r
+                       ob->xdir = -1;\r
+               }\r
+       }\r
+       if ( ((ob->xdir == 1 && ob->x < player->x) || (ob->xdir == -1 && ob->x > player->x))\r
+               && ob->bottom == player->bottom && US_RndT() < 0x20)\r
+       {\r
+               ob->state = &s_blooguardattack1;\r
+       }\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= T_BlooguardAttack\r
+=\r
+===========================\r
+*/\r
+\r
+#pragma argsused\r
+void T_BlooguardAttack(objtype *ob)\r
+{\r
+       SD_PlaySound(SND_SMASH);\r
+       groundslam = 23;\r
+       if (player->hitnorth)\r
+       {\r
+               ChangeState(player, &s_keenstun);\r
+       }\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= C_Blooguard\r
+=\r
+===========================\r
+*/\r
+\r
+void C_Blooguard(objtype *ob, objtype *hit)\r
+{\r
+       if (hit->obclass == keenobj)\r
+       {\r
+               KillKeen();\r
+       }\r
+       if (hit->obclass == stunshotobj)        // not 'else if' in the original code\r
+       {\r
+               if (--ob->temp2 == 0)   // handle health\r
+               {\r
+                       StunObj(ob, hit, &s_blooguardstun);\r
+               }\r
+               else\r
+               {\r
+                       ob->temp1 = 2;  // draw white twice\r
+                       ob->needtoreact = true;\r
+                       ExplodeShot(hit);\r
+               }\r
+       }\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= R_Blooguard\r
+=\r
+===========================\r
+*/\r
+\r
+void R_Blooguard(objtype *ob)\r
+{\r
+       if (ob->xdir == 1 && ob->hitwest)\r
+       {\r
+               ob->x -= ob->xmove;\r
+               ob->xdir = -1;\r
+               ob->nothink = US_RndT() >> 5;\r
+               ChangeState(ob, ob->state);\r
+       }\r
+       else if (ob->xdir == -1 && ob->hiteast)\r
+       {\r
+               ob->x -= ob->xmove;\r
+               ob->xdir = 1;\r
+               ob->nothink = US_RndT() >> 5;\r
+               ChangeState(ob, ob->state);\r
+       }\r
+       else if (!ob->hitnorth)\r
+       {\r
+               ob->x -= ob->xmove*2;\r
+               ob->xdir = -ob->xdir;\r
+               ob->nothink = US_RndT() >> 5;\r
+               ChangeState(ob, ob->state);\r
+       }\r
+       if (ob->temp1)\r
+       {\r
+               ob->temp1--;\r
+               RF_PlaceSprite(&ob->sprite, ob->x, ob->y, ob->shapenum, maskdraw, ob->priority);\r
+       }\r
+       else\r
+       {\r
+               RF_PlaceSprite(&ob->sprite, ob->x, ob->y, ob->shapenum, spritedraw, ob->priority);\r
+       }\r
+}\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                 BLOOGLET\r
+\r
+temp1 = type\r
+\r
+=============================================================================\r
+*/\r
+\r
+// red Blooglet:\r
+statetype s_rbloogletwalk1 = {RBLOOGLETWALKL1SPR, RBLOOGLETWALKR1SPR, step,  false, true,  5, 128, 0, T_BloogWalk, C_Blooglet, R_Walk, &s_rbloogletwalk2};\r
+statetype s_rbloogletwalk2 = {RBLOOGLETWALKL2SPR, RBLOOGLETWALKR2SPR, step,  false, true,  5, 128, 0, T_BloogWalk, C_Blooglet, R_Walk, &s_rbloogletwalk3};\r
+statetype s_rbloogletwalk3 = {RBLOOGLETWALKL3SPR, RBLOOGLETWALKR3SPR, step,  false, true,  5, 128, 0, T_BloogWalk, C_Blooglet, R_Walk, &s_rbloogletwalk4};\r
+statetype s_rbloogletwalk4 = {RBLOOGLETWALKL4SPR, RBLOOGLETWALKR4SPR, step,  false, true,  5, 128, 0, T_BloogWalk, C_Blooglet, R_Walk, &s_rbloogletwalk1};\r
+statetype s_rbloogletstun  = {RBLOOGLETSTUNSPR,   RBLOOGLETSTUNSPR,   think, false, false, 0,   0, 0, T_Projectile, NULL, R_Stunned, NULL};\r
+\r
+// yellow Blooglet:\r
+statetype s_ybloogletwalk1 = {YBLOOGLETWALKL1SPR, YBLOOGLETWALKR1SPR, step,  false, true,  5, 128, 0, T_BloogWalk, C_Blooglet, R_Walk, &s_ybloogletwalk2};\r
+statetype s_ybloogletwalk2 = {YBLOOGLETWALKL2SPR, YBLOOGLETWALKR2SPR, step,  false, true,  5, 128, 0, T_BloogWalk, C_Blooglet, R_Walk, &s_ybloogletwalk3};\r
+statetype s_ybloogletwalk3 = {YBLOOGLETWALKL3SPR, YBLOOGLETWALKR3SPR, step,  false, true,  5, 128, 0, T_BloogWalk, C_Blooglet, R_Walk, &s_ybloogletwalk4};\r
+statetype s_ybloogletwalk4 = {YBLOOGLETWALKL4SPR, YBLOOGLETWALKR4SPR, step,  false, true,  5, 128, 0, T_BloogWalk, C_Blooglet, R_Walk, &s_ybloogletwalk1};\r
+statetype s_ybloogletstun  = {YBLOOGLETSTUNSPR,   YBLOOGLETSTUNSPR,   think, false, false, 0,   0, 0, T_Projectile, NULL, R_Stunned, NULL};\r
+\r
+// blue Blooglet:\r
+statetype s_bbloogletwalk1 = {BBLOOGLETWALKL1SPR, BBLOOGLETWALKR1SPR, step,  false, true,  5, 128, 0, T_BloogWalk, C_Blooglet, R_Walk, &s_bbloogletwalk2};\r
+statetype s_bbloogletwalk2 = {BBLOOGLETWALKL2SPR, BBLOOGLETWALKR2SPR, step,  false, true,  5, 128, 0, T_BloogWalk, C_Blooglet, R_Walk, &s_bbloogletwalk3};\r
+statetype s_bbloogletwalk3 = {BBLOOGLETWALKL3SPR, BBLOOGLETWALKR3SPR, step,  false, true,  5, 128, 0, T_BloogWalk, C_Blooglet, R_Walk, &s_bbloogletwalk4};\r
+statetype s_bbloogletwalk4 = {BBLOOGLETWALKL4SPR, BBLOOGLETWALKR4SPR, step,  false, true,  5, 128, 0, T_BloogWalk, C_Blooglet, R_Walk, &s_bbloogletwalk1};\r
+statetype s_bbloogletstun  = {BBLOOGLETSTUNSPR,   BBLOOGLETSTUNSPR,   think, false, false, 0,   0, 0, T_Projectile, NULL, R_Stunned, NULL};\r
+\r
+// green Blooglet:\r
+statetype s_gbloogletwalk1 = {GBLOOGLETWALKL1SPR, GBLOOGLETWALKR1SPR, step,  false, true,  5, 128, 0, T_BloogWalk, C_Blooglet, R_Walk, &s_gbloogletwalk2};\r
+statetype s_gbloogletwalk2 = {GBLOOGLETWALKL2SPR, GBLOOGLETWALKR2SPR, step,  false, true,  5, 128, 0, T_BloogWalk, C_Blooglet, R_Walk, &s_gbloogletwalk3};\r
+statetype s_gbloogletwalk3 = {GBLOOGLETWALKL3SPR, GBLOOGLETWALKR3SPR, step,  false, true,  5, 128, 0, T_BloogWalk, C_Blooglet, R_Walk, &s_gbloogletwalk4};\r
+statetype s_gbloogletwalk4 = {GBLOOGLETWALKL4SPR, GBLOOGLETWALKR4SPR, step,  false, true,  5, 128, 0, T_BloogWalk, C_Blooglet, R_Walk, &s_gbloogletwalk1};\r
+statetype s_gbloogletstun  = {GBLOOGLETSTUNSPR,   GBLOOGLETSTUNSPR,   think, false, false, 0,   0, 0, T_Projectile, NULL, R_Stunned, NULL};\r
+\r
+/*\r
+===========================\r
+=\r
+= SpawnBlooglet\r
+=\r
+===========================\r
+*/\r
+\r
+void SpawnBlooglet(Uint16 tileX, Uint16 tileY, Sint16 type)\r
+{\r
+       GetNewObj(false);\r
+       new->obclass = bloogletobj;\r
+       new->active = ac_yes;\r
+       new->priority = 0;\r
+       new->x = CONVERT_TILE_TO_GLOBAL(tileX);\r
+       new->y = CONVERT_TILE_TO_GLOBAL(tileY) + -8*PIXGLOBAL;\r
+       if (US_RndT() < 0x80)\r
+       {\r
+               new->xdir = 1;\r
+       }\r
+       else\r
+       {\r
+               new->xdir = -1;\r
+       }\r
+       new->ydir = 1;\r
+       new->temp1 = type;\r
+\r
+       switch (type % 4)\r
+       {\r
+       case 0:\r
+               NewState(new, &s_rbloogletwalk1);\r
+               break;\r
+\r
+       case 1:\r
+               NewState(new, &s_ybloogletwalk1);\r
+               break;\r
+\r
+       case 2:\r
+               NewState(new, &s_bbloogletwalk1);\r
+               break;\r
+\r
+       case 3:\r
+               NewState(new, &s_gbloogletwalk1);\r
+       }\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= C_Blooglet\r
+=\r
+===========================\r
+*/\r
+\r
+void C_Blooglet(objtype *ob, objtype *hit)\r
+{\r
+       static statetype *stunnedstate[4] = {\r
+               &s_rbloogletstun,\r
+               &s_ybloogletstun,\r
+               &s_bbloogletstun,\r
+               &s_gbloogletstun\r
+       };\r
+       Sint16 color;\r
+\r
+       if (hit->obclass == keenobj && hit->state->contact)\r
+       {\r
+               playerkludgeclipcancel = true;\r
+               ClipToSpriteSide(hit, ob);\r
+               playerkludgeclipcancel = false;\r
+       }\r
+       else if (hit->obclass == stunshotobj)\r
+       {\r
+               color = ob->temp1 & 3;\r
+               if (ob->temp1 > 3)\r
+               {\r
+                       //\r
+                       // spawn a key gem\r
+                       //\r
+                       GetNewObj(false);\r
+                       new->needtoclip = cl_midclip;\r
+                       new->priority = 2;\r
+                       new->obclass = bonusobj;\r
+                       new->x = ob->x;\r
+                       new->y = ob->y;\r
+                       new->ydir = -1;\r
+                       new->yspeed = -40;\r
+                       new->temp1 = color;\r
+                       new->temp2=new->shapenum = bonusshape[color];\r
+                       new->temp3 = new->temp2 + 2;\r
+                       NewState(new, &s_bonusfly1);\r
+                       SD_PlaySound(SND_DROPKEY);\r
+               }\r
+               StunObj(ob, hit, stunnedstate[color]);\r
+       }\r
+}
\ No newline at end of file
diff --git a/16/keen456/KEEN4-6/KEEN6/K6_ACT2.C b/16/keen456/KEEN4-6/KEEN6/K6_ACT2.C
new file mode 100755 (executable)
index 0000000..ddea36f
--- /dev/null
@@ -0,0 +1,1311 @@
+/* Reconstructed Commander Keen 4-6 Source Code\r
+ * Copyright (C) 2021 K1n9_Duk3\r
+ *\r
+ * This file is loosely based on:\r
+ * Keen Dreams Source Code\r
+ * Copyright (C) 2014 Javier M. Chavez\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License along\r
+ * with this program; if not, write to the Free Software Foundation, Inc.,\r
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+ */\r
+\r
+/*\r
+K6_ACT2.C\r
+=========\r
+\r
+Contains the following actor types (in this order):\r
+\r
+- Nospike\r
+- Gik\r
+- Turrets\r
+- Orbatrix\r
+- Bip & Bipship\r
+- Flect\r
+\r
+*/\r
+\r
+#include "CK_DEF.H"\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                 NOSPIKE\r
+\r
+temp1 = step counter for running on thin air\r
+temp2 = low byte: running flag; high byte: flash countdown\r
+temp3 = sprite pointer for the question mark\r
+temp4 = health\r
+\r
+=============================================================================\r
+*/\r
+\r
+statetype s_nospikestand     = {NOSPIKESTANDSPR,   NOSPIKESTANDSPR,   step,  false, true,  90,   0, 0, NULL, C_Nospike, R_Walk, &s_nospikewalk1};\r
+statetype s_nospikewalk1     = {NOSPIKEWALKL1SPR,  NOSPIKEWALKR1SPR,  step,  false, true,  10, 128, 0, T_NospikeWalk, C_Nospike, R_Walk, &s_nospikewalk2};\r
+statetype s_nospikewalk2     = {NOSPIKEWALKL2SPR,  NOSPIKEWALKR2SPR,  step,  false, true,  10, 128, 0, T_NospikeWalk, C_Nospike, R_Walk, &s_nospikewalk3};\r
+statetype s_nospikewalk3     = {NOSPIKEWALKL3SPR,  NOSPIKEWALKR3SPR,  step,  false, true,  10, 128, 0, T_NospikeWalk, C_Nospike, R_Walk, &s_nospikewalk4};\r
+statetype s_nospikewalk4     = {NOSPIKEWALKL4SPR,  NOSPIKEWALKR4SPR,  step,  false, true,  10, 128, 0, T_NospikeWalk, C_Nospike, R_Walk, &s_nospikewalk1};\r
+statetype s_nospikerun1      = {NOSPIKERUNL1SPR,   NOSPIKERUNR1SPR,   step,  false, true,   4, 128, 0, T_NospikeRun, C_Nospike, R_NospikeRun, &s_nospikerun2};\r
+statetype s_nospikerun2      = {NOSPIKERUNL2SPR,   NOSPIKERUNR2SPR,   step,  false, true,   4, 128, 0, T_NospikeRun, C_Nospike, R_NospikeRun, &s_nospikerun3};\r
+statetype s_nospikerun3      = {NOSPIKERUNL3SPR,   NOSPIKERUNR3SPR,   step,  false, true,   4, 128, 0, T_NospikeRun, C_Nospike, R_NospikeRun, &s_nospikerun4};\r
+statetype s_nospikerun4      = {NOSPIKERUNL4SPR,   NOSPIKERUNR4SPR,   step,  false, true,   4, 128, 0, T_NospikeRun, C_Nospike, R_NospikeRun, &s_nospikerun1};\r
+statetype s_nospikeconfused1 = {NOSPIKESTANDSPR,   NOSPIKESTANDSPR,   step,  false, false, 20,   0, 1, NULL, NULL, R_Draw, &s_nospikeconfused2};\r
+statetype s_nospikeconfused2 = {NOSPIKESTANDSPR,   NOSPIKESTANDSPR,   step,  false, false, 90,   0, 0, T_NospikeConfused, NULL, R_NospikeConfused, &s_nospikeconfused3};\r
+statetype s_nospikeconfused3 = {NOSPIKESTANDSPR,   NOSPIKESTANDSPR,   step,  false, false, 20,   0, 0, NULL, NULL, R_Draw, &s_nospikefall};\r
+statetype s_nospikefall      = {NOSPIKESTANDSPR,   NOSPIKESTANDSPR,   think, false, false,  0,   0, 0, T_Projectile, NULL, R_NospikeFall, NULL};\r
+statetype s_nospikestun      = {NOSPIKESTUNSPR,    NOSPIKESTUNSPR,    think, false, false,  0,   0, 0, T_Projectile, NULL, R_Stunned, &s_nospikestun};\r
+\r
+/*\r
+===========================\r
+=\r
+= SpawnNospike\r
+=\r
+===========================\r
+*/\r
+\r
+void SpawnNospike(Uint16 tileX, Uint16 tileY)\r
+{\r
+       GetNewObj(false);\r
+       new->obclass = nospikeobj;\r
+       new->active = ac_yes;\r
+       new->priority = 0;\r
+       new->x = CONVERT_TILE_TO_GLOBAL(tileX);\r
+       new->y = CONVERT_TILE_TO_GLOBAL(tileY) + -24*PIXGLOBAL;\r
+       if (US_RndT() < 0x80)\r
+       {\r
+               new->xdir = 1;\r
+       }\r
+       else\r
+       {\r
+               new->xdir = -1;\r
+       }\r
+       new->ydir = 1;\r
+       NewState(new, &s_nospikestand);\r
+       new->temp4 = 4; // health\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= T_NospikeWalk\r
+=\r
+===========================\r
+*/\r
+\r
+void T_NospikeWalk(objtype *ob)\r
+{\r
+       if (US_RndT() < 0x10)\r
+       {\r
+               ob->state = &s_nospikestand;\r
+       }\r
+       else if (ob->bottom == player->bottom && US_RndT() <= 0x20)\r
+       {\r
+               //\r
+               // start running towards player\r
+               //\r
+               if (player->x > ob->x)\r
+               {\r
+                       ob->xdir = 1;\r
+               }\r
+               else\r
+               {\r
+                       ob->xdir = -1;\r
+               }\r
+               ob->temp1 = 0;  // nospike is still on solid ground (should already be 0 anyway)\r
+               ob->temp2 = 1;  // nospike is running\r
+               if (ob->state == &s_nospikewalk1)\r
+               {\r
+                       ob->state = &s_nospikerun2;\r
+               }\r
+               else if (ob->state == &s_nospikewalk2)\r
+               {\r
+                       ob->state = &s_nospikerun3;\r
+               }\r
+               else if (ob->state == &s_nospikewalk3)\r
+               {\r
+                       ob->state = &s_nospikerun4;\r
+               }\r
+               else if (ob->state == &s_nospikewalk4)\r
+               {\r
+                       ob->state = &s_nospikerun1;\r
+               }\r
+       }\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= T_NospikeRun\r
+=\r
+===========================\r
+*/\r
+\r
+void T_NospikeRun(objtype *ob)\r
+{\r
+       if (ob->temp1)\r
+               return; // nospike is running on thin air, so we'd better not stop\r
+\r
+       if ( ( ( player->bottom != ob->bottom   // not on same ground level as Keen?\r
+                        || (ob->xdir == -1 && ob->x < player->x)\r
+                        || (ob->xdir == 1 && ob->x > player->x) ) // already ran past Keen?\r
+                 && US_RndT() < 8 )\r
+               || !OnScreen(ob) )      // always stop running when off-screen\r
+       {\r
+               //\r
+               // stop running\r
+               //\r
+               ob->temp2 = 0;\r
+               if (ob->state == &s_nospikerun1)\r
+               {\r
+                       ob->state = &s_nospikewalk2;\r
+               }\r
+               else if (ob->state == &s_nospikerun2)\r
+               {\r
+                       ob->state = &s_nospikewalk3;\r
+               }\r
+               else if (ob->state == &s_nospikerun3)\r
+               {\r
+                       ob->state = &s_nospikewalk4;\r
+               }\r
+               else if (ob->state == &s_nospikerun4)\r
+               {\r
+                       ob->state = &s_nospikewalk1;\r
+               }\r
+       }\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= C_Nospike\r
+=\r
+===========================\r
+*/\r
+\r
+void C_Nospike(objtype *ob, objtype *hit)\r
+{\r
+       if (hit->obclass == keenobj)\r
+       {\r
+               KillKeen();\r
+       }\r
+       else if (hit->obclass == stunshotobj)\r
+       {\r
+               if (--ob->temp4 == 0)   // handle health\r
+               {\r
+                       StunObj(ob, hit, &s_nospikestun);\r
+                       ob->yspeed = -24;\r
+               }\r
+               else\r
+               {\r
+                       if (player->x > ob->x)\r
+                       {\r
+                               ob->xdir = 1;\r
+                       }\r
+                       else\r
+                       {\r
+                               ob->xdir = -1;\r
+                       }\r
+                       ob->temp2 |= 0x400;     // draw white 4 times\r
+                       ob->needtoreact = true;\r
+                       if (ob->state == &s_nospikestand || ob->state == &s_nospikewalk1)\r
+                       {\r
+                               ChangeState(ob, &s_nospikerun2);\r
+                       }\r
+                       else if (ob->state == &s_nospikewalk2)\r
+                       {\r
+                               ChangeState(ob, &s_nospikerun3);\r
+                       }\r
+                       else if (ob->state == &s_nospikewalk3)\r
+                       {\r
+                               ChangeState(ob, &s_nospikerun4);\r
+                       }\r
+                       else if (ob->state == &s_nospikewalk4)\r
+                       {\r
+                               ChangeState(ob, &s_nospikerun1);\r
+                       }\r
+                       ExplodeShot(hit);\r
+               }\r
+       }\r
+       else if (hit->obclass == nospikeobj\r
+               && (hit->temp2 & 0xFF) && (ob->temp2 & 0xFF)    // both nospikes are running?\r
+               && hit->xdir != ob->xdir)       // running in opposite directions?\r
+       {\r
+               //\r
+               // stun both nospikes\r
+               //\r
+               ob->temp1=ob->temp2=ob->temp3=hit->temp1=hit->temp2=hit->temp3 = 0;\r
+               ob->temp4 = hit->temp4 = ob->obclass;\r
+               ChangeState(ob, &s_nospikestun);\r
+               ChangeState(hit, &s_nospikestun);\r
+               SD_PlaySound(SND_SMASH);\r
+               ob->obclass = hit->obclass = stunnedobj;\r
+               ob->yspeed = hit->yspeed = -24;\r
+       }\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= T_NospikeConfused\r
+=\r
+===========================\r
+*/\r
+\r
+void T_NospikeConfused(objtype* ob)\r
+{\r
+       RF_RemoveSprite((void**)&ob->temp3);\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= R_NospikeConfused\r
+=\r
+===========================\r
+*/\r
+\r
+void R_NospikeConfused(objtype *ob)\r
+{\r
+       RF_PlaceSprite(&ob->sprite, ob->x, ob->y, ob->shapenum, spritedraw, ob->priority);\r
+       RF_PlaceSprite((void**)&ob->temp3, ob->x+TILEGLOBAL, ob->y-8*PIXGLOBAL, QUESTIONMARKSPR, spritedraw, 3);\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= R_NospikeFall\r
+=\r
+===========================\r
+*/\r
+\r
+void R_NospikeFall(objtype *ob)\r
+{\r
+       RF_PlaceSprite(&ob->sprite, ob->x, ob->y, ob->shapenum, spritedraw, ob->priority);\r
+       if (ob->hitnorth)\r
+       {\r
+               ob->temp1=ob->temp2=ob->temp3 = 0;\r
+               ob->temp4 = ob->obclass;\r
+               ChangeState(ob, &s_nospikestun);\r
+               SD_PlaySound(SND_SMASH);\r
+               ob->obclass = stunnedobj;\r
+               ob->yspeed = -24;\r
+       }\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= R_NospikeRun\r
+=\r
+===========================\r
+*/\r
+\r
+void R_NospikeRun(objtype *ob)\r
+{\r
+       if (ob->hitnorth)\r
+       {\r
+               ob->temp1 = 0;  // on solid ground\r
+               if (ob->hiteast || ob->hitwest)\r
+               {\r
+                       ob->x -= ob->xdir << 7;\r
+                       NewState(ob, &s_nospikestand);\r
+                       RF_PlaceSprite(&ob->sprite, ob->x, ob->y, ob->shapenum, spritedraw, ob->priority);\r
+                       ob->temp2 = 0;  // no longer running or flashing white\r
+                       return;\r
+               }\r
+       }\r
+       else\r
+       {\r
+               if (++ob->temp1 == 6)   // not on solid ground for 6 steps?\r
+               {\r
+                       ChangeState(ob, &s_nospikeconfused1);\r
+#if 0\r
+                       // bugfix:\r
+                       ob->nothink = 0;        // to make sure T_NospikeConfused can remove the question mark sprite\r
+#endif\r
+               }\r
+       }\r
+       if (ob->temp2 & 0xFF00)\r
+       {\r
+               ob->temp2 -= 0x100;\r
+               RF_PlaceSprite(&ob->sprite, ob->x, ob->y, ob->shapenum, maskdraw, ob->priority);\r
+       }\r
+       else\r
+       {\r
+               RF_PlaceSprite(&ob->sprite, ob->x, ob->y, ob->shapenum, spritedraw, ob->priority);\r
+       }\r
+}\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                 GIK\r
+\r
+=============================================================================\r
+*/\r
+\r
+statetype s_gikwalk1      = {GIKWALKL1SPR,  GIKWALKR1SPR,  step,      false, true,  10, 128, 0, T_GikWalk, C_ClipTop, R_Walk, &s_gikwalk2};\r
+statetype s_gikwalk2      = {GIKWALKL2SPR,  GIKWALKR2SPR,  step,      false, true,  10, 128, 0, T_GikWalk, C_ClipTop, R_Walk, &s_gikwalk3};\r
+statetype s_gikwalk3      = {GIKWALKL3SPR,  GIKWALKR3SPR,  step,      false, true,  10, 128, 0, T_GikWalk, C_ClipTop, R_Walk, &s_gikwalk1};\r
+statetype s_gikjump       = {GIKJUMPLSPR,   GIKJUMPRSPR,   think,     false, false,  0,   0, 0, T_Projectile, C_ClipSide, R_GikJump, &s_gikslide1};\r
+statetype s_gikslide1     = {GIKSLIDEL1SPR, GIKSLIDER1SPR, stepthink, false, false,  6,   0, 0, T_GikSlide, C_Lethal, R_GikSlide, &s_gikslide2};\r
+statetype s_gikslide2     = {GIKSLIDEL2SPR, GIKSLIDER2SPR, stepthink, false, false,  6,   0, 0, T_GikSlide, C_Lethal, R_GikSlide, &s_gikslide1};\r
+statetype s_gikstand      = {GIKSLIDEL1SPR, GIKSLIDER1SPR, step,      false, true,  20,   0, 0, NULL, C_Lethal, R_Walk, &s_gikwalk1};\r
+\r
+/*\r
+===========================\r
+=\r
+= SpawnGik\r
+=\r
+===========================\r
+*/\r
+\r
+void SpawnGik(Uint16 tileX, Uint16 tileY)\r
+{\r
+       GetNewObj(false);\r
+       new->obclass = gikobj;\r
+       new->active = ac_yes;\r
+       new->priority = 0;\r
+       new->x = CONVERT_TILE_TO_GLOBAL(tileX);\r
+       new->y = CONVERT_TILE_TO_GLOBAL(tileY);\r
+       if (US_RndT() < 0x80)\r
+       {\r
+               new->xdir = 1;\r
+       }\r
+       else\r
+       {\r
+               new->xdir = -1;\r
+       }\r
+       new->ydir = 1;\r
+       NewState(new, &s_gikwalk1);\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= T_GikWalk\r
+=\r
+===========================\r
+*/\r
+\r
+void T_GikWalk(objtype *ob)\r
+{\r
+       Sint16 xdist;\r
+\r
+       if (ob->hitnorth != 9)  // if NOT on flat ground that kills Keen\r
+       {\r
+               xdist = player->x - ob->x;\r
+               if (player->bottom <= ob->bottom && ob->bottom - player->bottom <= 4*TILEGLOBAL)\r
+               {\r
+                       if (xdist < 0)\r
+                       {\r
+                               ob->xdir = -1;\r
+                       }\r
+                       else\r
+                       {\r
+                               ob->xdir = 1;\r
+                       }\r
+                       if (xdist >= -7*TILEGLOBAL && xdist <= 7*TILEGLOBAL\r
+                               && (xdist <= -TILEGLOBAL || xdist >= TILEGLOBAL) )\r
+                       {\r
+                               if (xdist < 0)\r
+                               {\r
+                                       ob->xspeed = -40;\r
+                               }\r
+                               else\r
+                               {\r
+                                       ob->xspeed = 40;\r
+                               }\r
+                               ob->yspeed = -28;\r
+                               ob->state = &s_gikjump;\r
+                               SD_PlaySound(SND_GIKJUMP);\r
+                       }\r
+               }\r
+       }\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= T_GikSlide\r
+=\r
+===========================\r
+*/\r
+\r
+void T_GikSlide(objtype *ob)\r
+{\r
+       // tic masks for friction, based on slope type and direction\r
+       // 0 - no friction\r
+       // 7 - lowest friction (speed decreases every 8 tics)\r
+       // 3 - medium friction (speed decreases every 4 tics)\r
+       // 1 - highest friction (speed decreases every 2 tics)\r
+       static Sint16 rticmask[8] = {0, 7, 0, 0, 0, 3, 3, 1};\r
+       static Sint16 lticmask[8] = {0, 7, 3, 3, 1, 0, 0, 0};\r
+\r
+       Sint16 ticmask;\r
+       Sint16 slope;\r
+       Sint32 i;\r
+\r
+       DoGravity(ob);\r
+\r
+       slope = ob->hitnorth & 7;\r
+       if (ob->xdir == 1)\r
+       {\r
+               ticmask = rticmask[slope];\r
+       }\r
+       else\r
+       {\r
+               ticmask = lticmask[slope];\r
+       }\r
+\r
+       if (ob->xspeed == 0 && ob->hitnorth)\r
+       {\r
+               ob->state = &s_gikstand;\r
+       }\r
+       else\r
+       {\r
+               for (i = lasttimecount-tics; i < lasttimecount; i++)\r
+               {\r
+                       if (ticmask && !(i & ticmask))\r
+                       {\r
+                               if ((ob->xspeed < 0 && ++ob->xspeed == 0)\r
+                                       || (ob-> xspeed > 0 && --ob->xspeed == 0))\r
+                               {\r
+                                       ob->state = &s_gikstand;\r
+                                       return;\r
+                               }\r
+                       }\r
+                       xtry += ob->xspeed;\r
+               }\r
+       }\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= R_GikJump\r
+=\r
+===========================\r
+*/\r
+\r
+void R_GikJump(objtype *ob)\r
+{\r
+       if (ob->hiteast || ob->hitwest)\r
+               ob->xspeed = 0;\r
+\r
+       if (ob->hitsouth)\r
+               ob->yspeed = 0;\r
+\r
+       if (ob->hitnorth)\r
+       {\r
+               ob->yspeed = 0;\r
+               SD_PlaySound(SND_GIKLAND);\r
+               ChangeState(ob, ob->state->nextstate);\r
+       }\r
+       RF_PlaceSprite(&ob->sprite, ob->x, ob->y, ob->shapenum, spritedraw, ob->priority);\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= R_GikSlide\r
+=\r
+===========================\r
+*/\r
+\r
+void R_GikSlide(objtype *ob)\r
+{\r
+       if ((ob->hiteast && ob->xspeed < 0) || (ob->hitwest && ob->xspeed > 0))\r
+               ob->xspeed = 0;\r
+\r
+       RF_PlaceSprite(&ob->sprite, ob->x, ob->y, ob->shapenum, spritedraw, ob->priority);\r
+}\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                 CANNON\r
+\r
+temp1 = direction\r
+\r
+=============================================================================\r
+*/\r
+\r
+statetype s_cannon     = {0,            0,            step,      false, false, 120, 0, 0, NULL, NULL, R_Draw, &s_cannonfire};\r
+statetype s_cannonfire = {0,            0,            step,      true,  false,   1, 0, 0, T_Cannon, NULL, R_Draw, &s_cannon};\r
+statetype s_cshot1     = {LASER1SPR,    LASER1SPR,    stepthink, false, false,   8, 0, 0, T_Velocity, C_CShot, R_CShot, &s_cshot2};\r
+statetype s_cshot2     = {LASER2SPR,    LASER2SPR,    stepthink, false, false,   8, 0, 0, T_Velocity, C_CShot, R_CShot, &s_cshot3};\r
+statetype s_cshot3     = {LASER3SPR,    LASER3SPR,    stepthink, false, false,   8, 0, 0, T_Velocity, C_CShot, R_CShot, &s_cshot4};\r
+statetype s_cshot4     = {LASER4SPR,    LASER4SPR,    stepthink, false, false,   8, 0, 0, T_Velocity, C_CShot, R_CShot, &s_cshot1};\r
+statetype s_cshothit1  = {LASERHIT1SPR, LASERHIT1SPR, step,      false, false,  10, 0, 0, NULL, NULL, R_Draw, &s_cshothit2};\r
+statetype s_cshothit2  = {LASERHIT2SPR, LASERHIT2SPR, step,      false, false,  10, 0, 0, NULL, NULL, R_Draw, NULL};\r
+\r
+/*\r
+===========================\r
+=\r
+= SpawnCannon\r
+=\r
+===========================\r
+*/\r
+\r
+void SpawnCannon(Uint16 tileX, Uint16 tileY, Sint16 dir)\r
+{\r
+       GetNewObj(false);\r
+       new->obclass = cannonobj;\r
+       new->active = ac_yes;\r
+       new->tileright = new->tileleft = tileX;\r
+       new->tiletop = new->tilebottom = tileY;\r
+       new->x = new->left = new->right = CONVERT_TILE_TO_GLOBAL(tileX);\r
+       new->y = new->top = new->bottom = CONVERT_TILE_TO_GLOBAL(tileY);\r
+       new->temp1 = dir;\r
+       NewState(new, &s_cannon);\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= T_Cannon\r
+=\r
+===========================\r
+*/\r
+\r
+void T_Cannon(objtype *ob)\r
+{\r
+       GetNewObj(true);\r
+       new->obclass = mshotobj;\r
+       new->active = ac_yes;   // BUG? NOT removable in Keen 6 (checked v1.0, v1.4 and v1.5)\r
+       new->x = ob->x;\r
+       new->y = ob->y;\r
+       switch (ob->temp1)\r
+       {\r
+       case 0:\r
+               new->yspeed = -64;\r
+               break;\r
+       case 1:\r
+               new->xspeed = 64;\r
+               break;\r
+       case 2:\r
+               new->yspeed = 64;\r
+               break;\r
+       case 3:\r
+               new->xspeed = -64;\r
+       }\r
+       NewState(new, &s_cshot1);\r
+       SD_PlaySound(SND_ENEMYSHOT);\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= C_CShot\r
+=\r
+===========================\r
+*/\r
+\r
+void C_CShot(objtype *ob, objtype *hit)\r
+{\r
+       if (hit->obclass == keenobj)\r
+       {\r
+               KillKeen();\r
+               ChangeState(ob, &s_cshothit1);\r
+       }\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= R_CShot\r
+=\r
+===========================\r
+*/\r
+\r
+void R_CShot(objtype *ob)\r
+{\r
+       if (ob->hitnorth || ob->hiteast || ob->hitsouth || ob->hitwest)\r
+       {\r
+               SD_PlaySound(SND_ENEMYSHOTEXPLODE);\r
+               ChangeState(ob, &s_cshothit1);\r
+       }\r
+       RF_PlaceSprite(&ob->sprite, ob->x, ob->y, ob->shapenum, spritedraw, ob->priority);\r
+}\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                 ORBATRIX\r
+\r
+temp1 = bounce counter\r
+temp2 = amount to move up during uncurl animation\r
+temp3 = float offset\r
+temp4 = float direction (up or down)\r
+\r
+=============================================================================\r
+*/\r
+\r
+statetype s_orbatrix1       = {ORBATRIXL1SPR,    ORBATRIXR1SPR,    slide,     false, true,  12, 16, 0, T_OrbatrixFly, C_Orbatrix, R_Orbatrix, &s_orbatrix2};\r
+statetype s_orbatrix2       = {ORBATRIXL2SPR,    ORBATRIXR2SPR,    slide,     false, true,  12, 16, 0, T_OrbatrixFly, C_Orbatrix, R_Orbatrix, &s_orbatrix1};\r
+statetype s_orbatrixcurl1   = {ORBATRIX1SPR,     ORBATRIX1SPR,     stepthink, false, true,  12,  0, 0, NULL, C_Orbatrix, R_Orbatrix, &s_orbatrixcurl2};\r
+statetype s_orbatrixcurl2   = {ORBATRIXCURLSPR,  ORBATRIXCURLSPR,  stepthink, false, true,  12,  0, 0, NULL, C_Orbatrix, R_Orbatrix, &s_orbatrixcurl3};\r
+statetype s_orbatrixcurl3   = {ORBATRIXCURLSPR,  ORBATRIXCURLSPR,  think,     false, true,  12,  0, 0, T_OrbatrixCurl, C_Orbatrix, R_Orbatrix, &s_orbatrixbounce1};\r
+statetype s_orbatrixuncurl1 = {ORBATRIXSPIN1SPR, ORBATRIXSPIN1SPR, think,     false, false, 12,  0, 0, T_OrbatrixUncurl, C_OrbatrixBounce, R_Draw, &s_orbatrixuncurl2};\r
+statetype s_orbatrixuncurl2 = {ORBATRIXCURLSPR,  ORBATRIXCURLSPR,  step,      false, false, 12,  0, 0, NULL, C_OrbatrixBounce, R_Draw, &s_orbatrixidle1};\r
+statetype s_orbatrixidle1   = {ORBATRIX1SPR,     ORBATRIX1SPR,     stepthink, false, true,  12,  0, 0, NULL, C_Orbatrix, R_Orbatrix, &s_orbatrixidle2};\r
+statetype s_orbatrixidle2   = {ORBATRIX2SPR,     ORBATRIX2SPR,     stepthink, false, true,  12,  0, 0, NULL, C_Orbatrix, R_Orbatrix, &s_orbatrixidle3};\r
+statetype s_orbatrixidle3   = {ORBATRIX3SPR,     ORBATRIX3SPR,     stepthink, false, true,  12,  0, 0, NULL, C_Orbatrix, R_Orbatrix, &s_orbatrixidle4};\r
+statetype s_orbatrixidle4   = {ORBATRIX4SPR,     ORBATRIX4SPR,     stepthink, false, true,  12,  0, 0, NULL, C_Orbatrix, R_Orbatrix, &s_orbatrix1};\r
+statetype s_orbatrixbounce1 = {ORBATRIXSPIN4SPR, ORBATRIXSPIN1SPR, stepthink, false, false,  6,  0, 0, T_Projectile, C_OrbatrixBounce, R_OrbatrixBounce, &s_orbatrixbounce2};\r
+statetype s_orbatrixbounce2 = {ORBATRIXSPIN3SPR, ORBATRIXSPIN2SPR, stepthink, false, false,  6,  0, 0, T_Projectile, C_OrbatrixBounce, R_OrbatrixBounce, &s_orbatrixbounce3};\r
+statetype s_orbatrixbounce3 = {ORBATRIXSPIN2SPR, ORBATRIXSPIN3SPR, stepthink, false, false,  6,  0, 0, T_Projectile, C_OrbatrixBounce, R_OrbatrixBounce, &s_orbatrixbounce4};\r
+statetype s_orbatrixbounce4 = {ORBATRIXSPIN1SPR, ORBATRIXSPIN4SPR, stepthink, false, false,  6,  0, 0, T_Projectile, C_OrbatrixBounce, R_OrbatrixBounce, &s_orbatrixbounce1};\r
+\r
+/*\r
+===========================\r
+=\r
+= SpawnOrbatrix\r
+=\r
+===========================\r
+*/\r
+\r
+void SpawnOrbatrix(Uint16 tileX, Uint16 tileY)\r
+{\r
+       GetNewObj(false);\r
+       new->obclass = orbatrixobj;\r
+       new->active = ac_yes;\r
+       new->priority = 0;\r
+       new->x = CONVERT_TILE_TO_GLOBAL(tileX);\r
+       new->y = CONVERT_TILE_TO_GLOBAL(tileY) + -24*PIXGLOBAL;\r
+       if (US_RndT() < 0x80)\r
+       {\r
+               new->xdir = 1;\r
+       }\r
+       else\r
+       {\r
+               new->xdir = -1;\r
+       }\r
+       new->ydir = 1;\r
+       new->temp4 = 1;\r
+       NewState(new, &s_orbatrix1);\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= T_OrbatrixFly\r
+=\r
+===========================\r
+*/\r
+\r
+void T_OrbatrixFly(objtype *ob)\r
+{\r
+       Sint16 dist;\r
+\r
+       if (US_RndT() < 0x20)\r
+       {\r
+               ob->state = &s_orbatrixidle1;\r
+               return;\r
+       }\r
+\r
+       if (ob->bottom != player->bottom)\r
+       {\r
+               return;\r
+       }\r
+\r
+       dist = player->x - ob->x;\r
+       ob->xdir = (dist < 0)? -1 : 1;\r
+       if (dist > -5*TILEGLOBAL && dist < 5*TILEGLOBAL)\r
+       {\r
+               ob->state = &s_orbatrixcurl1;\r
+       }\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= C_Orbatrix\r
+=\r
+===========================\r
+*/\r
+\r
+void C_Orbatrix(objtype *ob, objtype *hit)\r
+{\r
+       if (hit->obclass == stunshotobj)\r
+       {\r
+               ExplodeShot(hit);\r
+               ChangeState(ob, &s_orbatrixidle1);\r
+       }\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= R_Orbatrix\r
+=\r
+===========================\r
+*/\r
+\r
+void R_Orbatrix(objtype *ob)\r
+{\r
+       //\r
+       // ugly hack: apply float offset before drawing the sprite\r
+       // (it's ugly because the sprite moves up/down, but the hitbox doesn't)\r
+       //\r
+       ob->y -= ob->temp3;\r
+       R_Walk(ob);\r
+       ob->y += ob->temp3;\r
+\r
+       //\r
+       // update the float offset\r
+       //\r
+       ob->temp3 = ob->temp3 + ob->temp4 * tics * 4;\r
+       if (ob->temp3 > 8*PIXGLOBAL)\r
+       {\r
+               ob->temp3 = 8*PIXGLOBAL;\r
+               ob->temp4 = -1;\r
+       }\r
+       else if (ob->temp3 < -8*PIXGLOBAL)\r
+       {\r
+               ob->temp3 = -8*PIXGLOBAL;\r
+               ob->temp4 = 1;\r
+       }\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= R_OrbatrixBounce\r
+=\r
+===========================\r
+*/\r
+\r
+void R_OrbatrixBounce(objtype *ob)\r
+{\r
+       RF_PlaceSprite(&ob->sprite, ob->x, ob->y, ob->shapenum, spritedraw, ob->priority);\r
+\r
+       if (ob->hitnorth)\r
+       {\r
+               ob->yspeed = -ob->yspeed;\r
+       }\r
+       if (ob->hitnorth || ob->hitwest || ob->hiteast)\r
+       {\r
+               ob->xspeed = -ob->xspeed;\r
+               SD_PlaySound(SND_ORBATRIXBOUNCE);\r
+               if (ob->hitnorth && --ob->temp1 == 0)\r
+               {\r
+                       ChangeState(ob, &s_orbatrixuncurl1);\r
+                       ob->temp2 = 24*PIXGLOBAL;\r
+               }\r
+       }\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= T_OrbatrixCurl\r
+=\r
+===========================\r
+*/\r
+\r
+void T_OrbatrixCurl(objtype *ob)\r
+{\r
+       if (ob->temp3 >= 16)\r
+       {\r
+               ob->xspeed = ob->xdir * 60;\r
+               ob->yspeed = -32;\r
+               ob->y -= ob->temp3;\r
+               ob->temp1 = 5;  // bounce 5 times\r
+               ob->state = ob->state->nextstate;\r
+       }\r
+       ob->needtoreact = true;\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= T_OrbatrixUncurl\r
+=\r
+===========================\r
+*/\r
+\r
+void T_OrbatrixUncurl(objtype *ob)\r
+{\r
+       ob->temp2 += (ytry = tics * -8);\r
+       if (ob->temp2 <= 0)\r
+       {\r
+               ytry -= ob->temp2;\r
+               ob->state = ob->state->nextstate;\r
+       }\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= C_OrbatrixBounce\r
+=\r
+===========================\r
+*/\r
+\r
+void C_OrbatrixBounce(objtype *ob, objtype *hit)\r
+{\r
+       if (hit->obclass == keenobj)\r
+       {\r
+               KillKeen();\r
+       }\r
+       else if (hit->obclass == stunshotobj)\r
+       {\r
+               ExplodeShot(hit);\r
+               ob->xspeed = 0;\r
+       }\r
+}\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                 BIP\r
+\r
+=============================================================================\r
+*/\r
+\r
+statetype s_bipstand      = {BIPSTANDSPR,    BIPSTANDSPR,    step,  false, true, 30,  0, 0, NULL, C_Bip, R_Walk, &s_bipwalk1};\r
+statetype s_bipwalk1      = {BIPWALKL1SPR,   BIPWALKR1SPR,   step,  true,  true,  4, 32, 0, T_BipWalk, C_Bip, R_Walk, &s_bipwalk2};\r
+statetype s_bipwalk2      = {BIPWALKL2SPR,   BIPWALKR2SPR,   step,  true,  true,  4, 32, 0, T_BipWalk, C_Bip, R_Walk, &s_bipwalk3};\r
+statetype s_bipwalk3      = {BIPWALKL3SPR,   BIPWALKR3SPR,   step,  true,  true,  4, 32, 0, T_BipWalk, C_Bip, R_Walk, &s_bipwalk4};\r
+statetype s_bipwalk4      = {BIPWALKL4SPR,   BIPWALKR4SPR,   step,  true,  true,  4, 32, 0, T_BipWalk, C_Bip, R_Walk, &s_bipwalk1};\r
+statetype s_bipsquished   = {BIPSQUISHEDSPR, BIPSQUISHEDSPR, think, false, true,  0,  0, 0, T_Projectile, NULL, R_Draw, NULL};\r
+\r
+/*\r
+===========================\r
+=\r
+= T_BipWalk\r
+=\r
+===========================\r
+*/\r
+\r
+void T_BipWalk(objtype *ob)\r
+{\r
+       if (ob->bottom == player->bottom)\r
+       {\r
+               if (ob->right < player->left - 4*PIXGLOBAL)\r
+                       ob->xdir = 1;\r
+\r
+               if (ob->left > player->right + 4*PIXGLOBAL)\r
+                       ob->xdir = -1;\r
+       }\r
+       else if (US_RndT() < 0x10)\r
+       {\r
+               ob->xdir = -ob->xdir;\r
+               ob->state = &s_bipstand;\r
+       }\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= C_Bip\r
+=\r
+===========================\r
+*/\r
+\r
+void C_Bip(objtype *ob, objtype *hit)\r
+{\r
+       if (hit->obclass == keenobj && hit->ymove > 0)\r
+       {\r
+               SD_PlaySound(SND_BIPSQUISH);\r
+               ob->obclass = inertobj;\r
+               ChangeState(ob, &s_bipsquished);\r
+       }\r
+}\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                 BIPSHIP\r
+\r
+=============================================================================\r
+*/\r
+\r
+statetype s_bipship         = {BIPSHIPLSPR,        BIPSHIPRSPR,        think,     false, true,      0, 0, 0, T_BipshipFly, C_Bipship, R_Draw, &s_bipship};\r
+statetype s_bipshipshot     = {BIPSHIPSHOTSPR,     BIPSHIPSHOTSPR,     think,     false, false,     0, 0, 0, T_Velocity, C_Lethal, R_BipShot, NULL};\r
+statetype s_bipshipturn1    = {BIPSHIPRTURN1SPR,   BIPSHIPLTURN1SPR,   stepthink, false, true,     10, 0, 0, T_BipshipTurn, C_Bipship, R_Draw, &s_bipshipturn2};\r
+statetype s_bipshipturn2    = {BIPSHIPRTURN2SPR,   BIPSHIPLTURN2SPR,   stepthink, false, true,     10, 0, 0, T_BipshipTurn, C_Bipship, R_Draw, &s_bipshipturn3};\r
+statetype s_bipshipturn3    = {BIPSHIPRTURN3SPR,   BIPSHIPLTURN3SPR,   stepthink, false, true,     10, 0, 0, T_BipshipTurn, C_Bipship, R_Draw, &s_bipshipturn4};\r
+statetype s_bipshipturn4    = {BIPSHIPRTURN4SPR,   BIPSHIPLTURN4SPR,   stepthink, false, true,     10, 0, 0, T_BipshipTurn, C_Bipship, R_Draw, &s_bipship};\r
+statetype s_bipshipexplode1 = {BIPSHIPEXPLODE2SPR, BIPSHIPEXPLODE1SPR, think,     false, false,     0, 0, 0, T_Projectile, NULL, R_Land, &s_bipshipexplode2};\r
+statetype s_bipshipexplode2 = {BIPSHIPEXPLODE2SPR, BIPSHIPEXPLODE1SPR, step,      true,  false,     1, 0, 0, T_BipshipExplode, NULL, R_Land, &s_bipshipexplode3};\r
+statetype s_bipshipexplode3 = {BIPSHIPEXPLODE5SPR, BIPSHIPEXPLODE5SPR, step,      true,  false, 30000, 0, 0, NULL, NULL, R_Land, &s_bipshipexplode3};\r
+statetype s_bipshipsmoke1   = {BIPSHIPEXPLODE3SPR, BIPSHIPEXPLODE3SPR, step,      true,  false,    10, 0, 0, NULL, NULL, R_Draw, &s_bipshipsmoke2};\r
+statetype s_bipshipsmoke2   = {BIPSHIPEXPLODE4SPR, BIPSHIPEXPLODE4SPR, step,      true,  false,    10, 0, 0, NULL, NULL, R_Draw, NULL};\r
+\r
+/*\r
+===========================\r
+=\r
+= SpawnBipship\r
+=\r
+===========================\r
+*/\r
+\r
+void SpawnBipship(Uint16 tileX, Uint16 tileY)\r
+{\r
+       GetNewObj(false);\r
+       new->obclass = bipshipobj;\r
+       new->active = ac_yes;\r
+       new->x = CONVERT_TILE_TO_GLOBAL(tileX);\r
+       new->y = CONVERT_TILE_TO_GLOBAL(tileY)+ -24*PIXGLOBAL;\r
+       if (US_RndT() < 0x80)\r
+       {\r
+               new->xdir = 1;\r
+       }\r
+       else\r
+       {\r
+               new->xdir = -1;\r
+       }\r
+       new->xspeed = new->xdir * 20;\r
+       NewState(new, &s_bipship);\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= R_BipShot\r
+=\r
+===========================\r
+*/\r
+\r
+void R_BipShot(objtype *ob)\r
+{\r
+       if (ob->hitnorth || ob->hitsouth || ob->hiteast || ob->hitwest)\r
+       {\r
+               RemoveObj(ob);\r
+       }\r
+       else\r
+       {\r
+               RF_PlaceSprite(&ob->sprite, ob->x, ob->y, ob->shapenum, spritedraw, ob->priority);\r
+       }\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= T_BipshipTurn\r
+=\r
+===========================\r
+*/\r
+\r
+void T_BipshipTurn(objtype *ob)\r
+{\r
+       AccelerateX(ob, ob->xdir, 20);\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= T_BipshipFly\r
+=\r
+===========================\r
+*/\r
+\r
+void T_BipshipFly(objtype *ob)\r
+{\r
+       Uint16 far *map;\r
+       Sint16 dir;\r
+       Uint16 tile, tx, ty;\r
+\r
+       AccelerateX(ob, ob->xdir, 20);\r
+       dir = ob->xdir;\r
+       if (player->bottom + TILEGLOBAL - ob->bottom <= 2*TILEGLOBAL)\r
+       {\r
+               if (player->x < ob->x)\r
+               {\r
+                       dir = -1;\r
+               }\r
+               else\r
+               {\r
+                       dir = 1;\r
+               }\r
+               if (ob->xdir == dir && US_RndT() < tics*4)\r
+               {\r
+                       SD_PlaySound(SND_KEENFIRE);\r
+                       GetNewObj(true);\r
+                       new->obclass = mshotobj;\r
+                       new->active = ac_removable;\r
+                       new->priority = 1;\r
+                       if (ob->xdir == 1)\r
+                       {\r
+                               new->x = ob->x + TILEGLOBAL;\r
+                               new->xspeed = 64;\r
+                       }\r
+                       else\r
+                       {\r
+                               new->x = ob->x;\r
+                               new->xspeed = -64;\r
+                       }\r
+                       new->y = ob->y + 10*PIXGLOBAL;\r
+                       new->yspeed = 16;\r
+                       NewState(new, &s_bipshipshot);\r
+               }\r
+       }\r
+\r
+       tx = ob->tilemidx + dir*4;\r
+       map = mapsegs[1] + mapbwidthtable[ob->tiletop]/2 + tx;\r
+\r
+       for (ty = ob->tiletop; ty <= ob->tilebottom; ty++, map += mapwidth)\r
+       {\r
+               tile = *map;\r
+               if (tinf[tile+EASTWALL] || tinf[tile+WESTWALL])\r
+               {\r
+                       dir = -dir;\r
+                       goto check_turn;\r
+               }\r
+       }\r
+       tile = *map;\r
+       if (!tinf[tile+NORTHWALL])\r
+       {\r
+               dir = -dir;\r
+       }\r
+check_turn:\r
+       if (dir != ob->xdir)\r
+       {\r
+               ob->xdir = dir;\r
+               ChangeState(ob, &s_bipshipturn1);\r
+       }\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= T_BipshipExplode\r
+=\r
+===========================\r
+*/\r
+\r
+void T_BipshipExplode(objtype *ob)\r
+{\r
+       SD_PlaySound(SND_BIPSHIPEXPLODE);\r
+\r
+       GetNewObj(true);\r
+       new->obclass = inertobj;\r
+       new->active = ac_yes;\r
+       new->priority = 2;\r
+       new->x = ob->x;\r
+       new->y = ob->y - 24*PIXGLOBAL;\r
+       NewState(new, &s_bipshipsmoke1);\r
+\r
+       GetNewObj(true);\r
+       new->obclass = bipobj;\r
+       new->active = ac_yes;\r
+       new->priority = 0;\r
+       new->x = ob->x;\r
+       new->y = ob->y - 8*PIXGLOBAL;\r
+       if (US_RndT() < 0x80)\r
+       {\r
+               new->xdir = 1;\r
+       }\r
+       else\r
+       {\r
+               new->xdir = -1;\r
+       }\r
+       NewState(new, &s_bipstand);\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= C_Bipship\r
+=\r
+===========================\r
+*/\r
+\r
+void C_Bipship(objtype *ob, objtype *hit)\r
+{\r
+       if (hit->obclass == stunshotobj)\r
+       {\r
+               ExplodeShot(hit);\r
+               ChangeState(ob, &s_bipshipexplode1);\r
+       }\r
+}\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                 FLECT\r
+\r
+=============================================================================\r
+*/\r
+\r
+statetype s_flectstand = {FLECTSTANDLSPR, FLECTSTANDRSPR, think, false, true, 60,   0, 0, T_FlectStand, C_Flect, R_Flect, &s_flectwalk1};\r
+statetype s_flectturn  = {FLECTSTANDSPR,  FLECTSTANDSPR,  step,  false, true,  8,   0, 0, NULL, C_Flect, R_Flect, &s_flectwalk1};\r
+statetype s_flectwalk1 = {FLECTWALKL1SPR, FLECTWALKR1SPR, step,  false, true, 10, 128, 0, T_FlectWalk, C_Flect, R_Flect, &s_flectwalk2};\r
+statetype s_flectwalk2 = {FLECTWALKL2SPR, FLECTWALKR2SPR, step,  false, true, 10, 128, 0, T_FlectWalk, C_Flect, R_Flect, &s_flectwalk3};\r
+statetype s_flectwalk3 = {FLECTWALKL3SPR, FLECTWALKR3SPR, step,  false, true, 10, 128, 0, T_FlectWalk, C_Flect, R_Flect, &s_flectwalk4};\r
+statetype s_flectwalk4 = {FLECTWALKL4SPR, FLECTWALKR4SPR, step,  false, true, 10, 128, 0, T_FlectWalk, C_Flect, R_Flect, &s_flectwalk1};\r
+statetype s_flectstun  = {FLECTSTUNSPR,   FLECTSTUNSPR,   think, false, false, 0,   0, 0, T_Projectile, NULL, R_Stunned, &s_flectstun};\r
+\r
+/*\r
+===========================\r
+=\r
+= SpawnFlect\r
+=\r
+===========================\r
+*/\r
+\r
+void SpawnFlect(Uint16 tileX, Uint16 tileY)\r
+{\r
+       GetNewObj(false);\r
+       new->obclass = flectobj;\r
+       new->active = ac_yes;\r
+       new->priority = 0;\r
+       new->x = CONVERT_TILE_TO_GLOBAL(tileX);\r
+       new->y = CONVERT_TILE_TO_GLOBAL(tileY) + -1*TILEGLOBAL;\r
+       if (US_RndT() < 0x80)\r
+       {\r
+               new->xdir = 1;\r
+       }\r
+       else\r
+       {\r
+               new->xdir = -1;\r
+       }\r
+       new->ydir = 1;\r
+       NewState(new, &s_flectwalk1);\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= T_FlectStand\r
+=\r
+===========================\r
+*/\r
+\r
+void T_FlectStand(objtype *ob)\r
+{\r
+       if (player->x < ob->x)\r
+       {\r
+               if (ob->xdir != -1)\r
+               {\r
+                       ob->state = &s_flectturn;\r
+                       ob->xdir = -1;\r
+               }\r
+               else\r
+               {\r
+                       ob->state = &s_flectwalk1;\r
+               }\r
+       }\r
+       else\r
+       {\r
+               if (ob->xdir != 1)\r
+               {\r
+                       ob->state = &s_flectturn;\r
+                       ob->xdir = 1;\r
+               }\r
+               else\r
+               {\r
+                       ob->state = &s_flectwalk1;\r
+               }\r
+       }\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= T_FlectWalk\r
+=\r
+===========================\r
+*/\r
+\r
+void T_FlectWalk(objtype *ob)\r
+{\r
+       if (player->x < ob->x && ob->xdir == 1)\r
+       {\r
+               if (ob->xdir != -1)     // always true here!\r
+               {\r
+                       ob->state = &s_flectturn;\r
+               }\r
+               ob->xdir = -1;\r
+       }\r
+\r
+       if (player->x > ob->x && ob->xdir == -1)\r
+       {\r
+               if (ob->xdir != 1)      // always true here!\r
+               {\r
+                       ob->state = &s_flectturn;\r
+               }\r
+               ob->xdir = 1;\r
+       }\r
+\r
+       if (US_RndT() < 0x20)\r
+       {\r
+               ob->state = &s_flectstand;\r
+       }\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= C_Flect\r
+=\r
+===========================\r
+*/\r
+\r
+void C_Flect(objtype *ob, objtype *hit)\r
+{\r
+       if (hit->obclass == keenobj)\r
+       {\r
+               ClipToSpriteSide(hit, ob);\r
+       }\r
+       else if (hit->obclass == stunshotobj)\r
+       {\r
+               if (hit->xdir == 0)\r
+               {\r
+                       StunObj(ob, hit, &s_flectstun);\r
+               }\r
+               else if (hit->xdir != ob->xdir)\r
+               {\r
+                       // reflect shot:\r
+                       hit->xdir = ob->xdir;\r
+                       hit->temp4 = true;      // shot can now stun Keen\r
+                       SD_PlaySound(SND_SHOTBOUNCE);\r
+               }\r
+       }\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= R_Flect\r
+=\r
+===========================\r
+*/\r
+\r
+void R_Flect(objtype *ob)\r
+{\r
+       if (ob->xdir == 1 && ob->hitwest)\r
+       {\r
+               ob->x -= ob->xmove;\r
+               ob->xdir = -1;\r
+               ob->nothink = US_RndT() >> 5;\r
+               ChangeState(ob, ob->state);\r
+       }\r
+       else if (ob->xdir == -1 && ob->hiteast)\r
+       {\r
+               ob->x -= ob->xmove;\r
+               ob->xdir = 1;\r
+               ob->nothink = US_RndT() >> 5;\r
+               ChangeState(ob, ob->state);\r
+       }\r
+       else if (!ob->hitnorth)\r
+       {\r
+               ob->x -= ob->xmove;\r
+               ob->xdir = -ob->xdir;\r
+               ChangeState(ob, ob->state);\r
+       }\r
+       RF_PlaceSprite(&ob->sprite, ob->x, ob->y, ob->shapenum, spritedraw, ob->priority);\r
+}\r
diff --git a/16/keen456/KEEN4-6/KEEN6/K6_ACT3.C b/16/keen456/KEEN4-6/KEEN6/K6_ACT3.C
new file mode 100755 (executable)
index 0000000..81efc99
--- /dev/null
@@ -0,0 +1,765 @@
+/* Reconstructed Commander Keen 4-6 Source Code\r
+ * Copyright (C) 2021 K1n9_Duk3\r
+ *\r
+ * This file is loosely based on:\r
+ * Keen Dreams Source Code\r
+ * Copyright (C) 2014 Javier M. Chavez\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License along\r
+ * with this program; if not, write to the Free Software Foundation, Inc.,\r
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+ */\r
+\r
+/*\r
+K6_ACT3.C\r
+=========\r
+\r
+Contains the following actor types (in this order):\r
+\r
+- Fleex\r
+- Bobba\r
+- Babobba\r
+- Blorb\r
+- Ceilick\r
+\r
+*/\r
+\r
+#include "CK_DEF.H"\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                 FLEEX\r
+\r
+temp1 = flash countdown\r
+temp2 = health\r
+\r
+=============================================================================\r
+*/\r
+\r
+statetype s_fleexwalk1 = {FLEEXWALKL1SPR, FLEEXWALKR1SPR, step,  false, true,  7, 128, 0, T_FleexWalk, C_Fleex, R_Blooguard, &s_fleexwalk2};\r
+statetype s_fleexwalk2 = {FLEEXWALKL2SPR, FLEEXWALKR2SPR, step,  false, true,  7, 128, 0, T_FleexWalk, C_Fleex, R_Blooguard, &s_fleexwalk1};\r
+statetype s_fleexrun1  = {FLEEXWALKL1SPR, FLEEXWALKR1SPR, step,  false, true,  7, 128, 0, NULL, C_Fleex, R_Blooguard, &s_fleexrun2};\r
+statetype s_fleexrun2  = {FLEEXWALKL2SPR, FLEEXWALKR2SPR, step,  false, true,  7, 128, 0, NULL, C_Fleex, R_Blooguard, &s_fleexrun3};\r
+statetype s_fleexrun3  = {FLEEXWALKL1SPR, FLEEXWALKR1SPR, step,  false, true,  7, 128, 0, NULL, C_Fleex, R_Blooguard, &s_fleexrun4};\r
+statetype s_fleexrun4  = {FLEEXWALKL2SPR, FLEEXWALKR2SPR, step,  false, true,  7, 128, 0, NULL, C_Fleex, R_Blooguard, &s_fleexwalk1};\r
+statetype s_fleexlook1 = {FLEEXLOOK1SPR,  FLEEXLOOK1SPR,  step,  false, true, 60,   0, 0, NULL, C_Fleex, R_Blooguard, &s_fleexlook2};\r
+statetype s_fleexlook2 = {FLEEXLOOK2SPR,  FLEEXLOOK2SPR,  step,  false, true, 60,   0, 0, T_FleexLook, C_Fleex, R_Blooguard, &s_fleexrun1};\r
+statetype s_fleexstun  = {FLEEXSTUNSPR,   FLEEXSTUNSPR,   think, false, false, 0,   0, 0, T_Projectile, NULL, R_Stunned, NULL};\r
+\r
+/*\r
+===========================\r
+=\r
+= SpawnFleex\r
+=\r
+===========================\r
+*/\r
+\r
+void SpawnFleex(Uint16 tileX, Uint16 tileY)\r
+{\r
+       GetNewObj(false);\r
+       new->obclass = fleexobj;\r
+       new->active = ac_yes;\r
+       new->priority = 0;\r
+       new->x = CONVERT_TILE_TO_GLOBAL(tileX);\r
+       new->y = CONVERT_TILE_TO_GLOBAL(tileY) + -40*PIXGLOBAL;\r
+       if (US_RndT() < 0x80)\r
+       {\r
+               new->xdir = 1;\r
+       }\r
+       else\r
+       {\r
+               new->xdir = -1;\r
+       }\r
+       new->ydir = 1;\r
+       NewState(new, &s_fleexwalk1);\r
+       new->temp2 = 4; // health\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= T_FleexWalk\r
+=\r
+===========================\r
+*/\r
+\r
+void T_FleexWalk(objtype *ob)\r
+{\r
+       if (!player->xmove && !ob->temp1)\r
+       {\r
+               ob->state = &s_fleexlook1;\r
+       }\r
+       else\r
+       {\r
+               ob->xdir = (ob->x < player->x)? 1 : -1;\r
+       }\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= T_FleexLook\r
+=\r
+===========================\r
+*/\r
+\r
+void T_FleexLook(objtype *ob)\r
+{\r
+       ob->xdir = (ob->x < player->x)? 1 : -1;\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= C_Fleex\r
+=\r
+===========================\r
+*/\r
+\r
+void C_Fleex(objtype *ob, objtype *hit)\r
+{\r
+       if (hit->obclass == keenobj)\r
+       {\r
+               KillKeen();\r
+       }\r
+       if (hit->obclass == stunshotobj)        // this is not 'else if' in the original code\r
+       {\r
+               if (--ob->temp2 == 0)\r
+               {\r
+                       StunObj(ob, hit, &s_fleexstun);\r
+                       ob->yspeed = -20;\r
+               }\r
+               else\r
+               {\r
+                       ob->temp1 = 2;  // draw white twice\r
+                       ob->needtoreact = true;\r
+                       ExplodeShot(hit);\r
+                       if (ob->state == &s_fleexlook1 || ob->state == &s_fleexlook2)\r
+                       {\r
+                               ob->xdir = (ob->x < player->x)? 1 : -1;\r
+                               ChangeState(ob, &s_fleexwalk1);\r
+                       }\r
+               }\r
+       }\r
+}\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                 BOBBA\r
+\r
+temp1 = jump counter\r
+\r
+=============================================================================\r
+*/\r
+\r
+statetype s_bobbajump1       = {BOBBAL2SPR,    BOBBAR2SPR,    stepthink, false, false,  8,  0, 0, T_Projectile, C_Bobba, R_Bobba, &s_bobbajump2};\r
+statetype s_bobbajump2       = {BOBBAL3SPR,    BOBBAR3SPR,    think,     false, false,  8,  0, 0, T_Projectile, C_Bobba, R_Bobba, &s_bobbajump2};\r
+statetype s_bobbastand       = {BOBBAL1SPR,    BOBBAR1SPR,    step,      false, false, 20,  0, 0, T_BobbaStand, C_Bobba, R_Draw, &s_bobbajump1};\r
+statetype s_bobbaattack      = {BOBBAL1SPR,    BOBBAR1SPR,    step,      false, false, 40,  0, 0, NULL, C_Bobba, R_Draw, &s_bobbajump1};\r
+statetype s_bobbashot1       = {BOBBASHOT1SPR, BOBBASHOT1SPR, step,      false, false,  8,  0, 0, NULL, NULL, R_Draw, &s_bobbashot2};\r
+statetype s_bobbashot2       = {BOBBASHOT2SPR, BOBBASHOT2SPR, step,      false, false,  8,  0, 0, NULL, NULL, R_Draw, &s_bobbashot3};\r
+statetype s_bobbashot3       = {BOBBASHOT1SPR, BOBBASHOT1SPR, step,      false, false,  8,  0, 0, NULL, NULL, R_Draw, &s_bobbashot4};\r
+statetype s_bobbashot4       = {BOBBASHOT2SPR, BOBBASHOT2SPR, step,      false, false,  8,  0, 0, T_BobbaShot, NULL, R_Draw, &s_bobbashot5};\r
+statetype s_bobbashot5       = {BOBBASHOT3SPR, BOBBASHOT3SPR, slide,     false, false,  8, 48, 0, NULL, C_Lethal, R_BobbaShot, &s_bobbashot6};\r
+statetype s_bobbashot6       = {BOBBASHOT4SPR, BOBBASHOT4SPR, slide,     false, false,  8, 48, 0, NULL, C_Lethal, R_BobbaShot, &s_bobbashot7};\r
+statetype s_bobbashot7       = {BOBBASHOT5SPR, BOBBASHOT5SPR, slide,     false, false,  8, 48, 0, NULL, C_Lethal, R_BobbaShot, &s_bobbashot8};\r
+statetype s_bobbashot8       = {BOBBASHOT6SPR, BOBBASHOT6SPR, slide,     false, false,  8, 48, 0, NULL, C_Lethal, R_BobbaShot, &s_bobbashot5};\r
+statetype s_bobbashotvanish1 = {BOBBASHOT6SPR, BOBBASHOT6SPR, step,      false, false,  8,  0, 0, NULL, NULL, R_Draw, &s_bobbashotvanish2};\r
+statetype s_bobbashotvanish2 = {BOBBASHOT6SPR, BOBBASHOT6SPR, step,      false, false,  8,  0, 0, NULL, NULL, R_Draw, &s_bobbashotvanish3};\r
+statetype s_bobbashotvanish3 = {BOBBASHOT6SPR, BOBBASHOT6SPR, step,      false, false,  8,  0, 0, NULL, NULL, R_Draw, NULL};\r
+\r
+/*\r
+===========================\r
+=\r
+= SpawnBobba\r
+=\r
+===========================\r
+*/\r
+\r
+void SpawnBobba(Uint16 tileX, Uint16 tileY)\r
+{\r
+       GetNewObj(false);\r
+       new->obclass = bobbaobj;\r
+       new->active = ac_yes;\r
+       new->priority = 0;\r
+       new->x = CONVERT_TILE_TO_GLOBAL(tileX);\r
+       new->y = CONVERT_TILE_TO_GLOBAL(tileY) + -2*TILEGLOBAL;\r
+       if (US_RndT() < 0x80)\r
+       {\r
+               new->xdir = 1;\r
+       }\r
+       else\r
+       {\r
+               new->xdir = -1;\r
+       }\r
+       new->ydir = 1;\r
+       NewState(new, &s_bobbajump1);\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= T_BobbaShot\r
+=\r
+===========================\r
+*/\r
+\r
+#pragma argsused\r
+void T_BobbaShot(objtype *ob)\r
+{\r
+       SD_PlaySound(SND_BOBBASHOT);\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= T_BobbaStand\r
+=\r
+===========================\r
+*/\r
+\r
+void T_BobbaStand(objtype *ob)\r
+{\r
+       Sint16 i;\r
+       Uint16 far *map;\r
+\r
+       if (++ob->temp1 == 3)\r
+       {\r
+               ob->temp1 = 0;\r
+               GetNewObj(true);\r
+               new->active = ac_removable;\r
+               new->obclass = mshotobj;\r
+               new->y = ob->y + 11*PIXGLOBAL;\r
+               new->xdir = ob->xdir;\r
+               if (ob->xdir == 1)\r
+               {\r
+                       new->x = ob->x + 16*PIXGLOBAL;\r
+               }\r
+               else\r
+               {\r
+                       new->x = ob->x + 11*PIXGLOBAL;\r
+               }\r
+               NewState(new, &s_bobbashot1);\r
+               new->priority = 2;\r
+               ob->state = &s_bobbaattack;\r
+       }\r
+       else\r
+       {\r
+               map = mapsegs[1] + mapbwidthtable[ob->tilebottom+1]/2 + ob->tilemidx;\r
+               for (i=0; i<4; map += ob->xdir, i++)\r
+               {\r
+                       if ( !tinf[*map+NORTHWALL]\r
+                               && !tinf[*(map-mapwidth)+NORTHWALL]\r
+                               && !tinf[*(map+mapwidth)+NORTHWALL] )\r
+                       {\r
+                               ob->xdir = -ob->xdir;\r
+                               break;\r
+                       }\r
+               }\r
+               ob->xspeed = ob->xdir << 5;\r
+               ob->yspeed = -32;\r
+               SD_PlaySound(SND_BOBBAJUMP);\r
+       }\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= C_Bobba\r
+=\r
+===========================\r
+*/\r
+\r
+void C_Bobba(objtype *ob, objtype *hit)\r
+{\r
+       if (hit->obclass == keenobj)\r
+       {\r
+               KillKeen();\r
+       }\r
+       else if (hit->obclass == stunshotobj)\r
+       {\r
+               ExplodeShot(hit);\r
+               if (hit->xdir != 0)\r
+               {\r
+                       ob->xdir = -hit->xdir;\r
+               }\r
+       }\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= R_Bobba\r
+=\r
+===========================\r
+*/\r
+\r
+void R_Bobba(objtype *ob)\r
+{\r
+       if (ob->hiteast)\r
+       {\r
+               ob->xdir = 1;\r
+               ob->xspeed = -ob->xspeed;\r
+       }\r
+       else if (ob->hitwest)\r
+       {\r
+               ob->xdir = -1;\r
+               ob->xspeed = -ob->xspeed;\r
+       }\r
+\r
+       if (ob->hitsouth)\r
+               ob->yspeed = 0;\r
+\r
+       if (ob->hitnorth)\r
+       {\r
+               SD_PlaySound(SND_BOBBALAND);\r
+               ChangeState(ob, &s_bobbastand);\r
+       }\r
+       RF_PlaceSprite(&ob->sprite, ob->x, ob->y, ob->shapenum, spritedraw, ob->priority);\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= R_BobbaShot\r
+=\r
+===========================\r
+*/\r
+\r
+void R_BobbaShot(objtype *ob)\r
+{\r
+       if (ob->hitnorth || ob->hitsouth || ob->hiteast || ob->hitwest)\r
+       {\r
+               ChangeState(ob, &s_bobbashotvanish1);\r
+       }\r
+       RF_PlaceSprite(&ob->sprite, ob->x, ob->y, ob->shapenum, spritedraw, ob->priority);\r
+}\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                 BABOBBA\r
+\r
+temp1 = jump counter (Babobba) / animation counter (Shot)\r
+\r
+=============================================================================\r
+*/\r
+\r
+statetype s_babobbajump1       = {BABOBBAL2SPR,     BABOBBAR2SPR,     stepthink, false, false,   8, 0, 0, T_Projectile, C_Babobba, R_Babobba, &s_babobbajump2};\r
+statetype s_babobbajump2       = {BABOBBAL3SPR,     BABOBBAR3SPR,     think,     false, false,   8, 0, 0, T_Projectile, C_Babobba, R_Babobba, &s_babobbajump2};\r
+statetype s_babobbastand       = {BABOBBAL1SPR,     BABOBBAR1SPR,     step,      false, false,  20, 0, 0, T_BabobbaStand, C_Babobba, R_Draw, &s_babobbajump1};\r
+statetype s_babobbaattack      = {BABOBBAL1SPR,     BABOBBAR1SPR,     step,      false, false,  70, 0, 0, NULL, C_Babobba, R_Draw, &s_babobbastand};\r
+statetype s_babobbastun1       = {BABOBBAL2SPR,     BABOBBAR2SPR,     think,     false, false,   0, 0, 0, T_Projectile, NULL, R_Stunned, &s_babobbastun2};\r
+statetype s_babobbastun2       = {BABOBBASTUNSPR,   BABOBBASTUNSPR,   think,     false, false,   0, 0, 0, T_Projectile, NULL, R_Stunned, NULL};\r
+statetype s_babobbasleep1      = {BABOBBASLEEP1SPR, BABOBBASLEEP1SPR, step,      false, false,  15, 0, 0, NULL, C_BabobbaSleep, R_Draw, &s_babobbasleep2};\r
+statetype s_babobbasleep2      = {BABOBBASLEEP2SPR, BABOBBASLEEP2SPR, step,      false, false,  15, 0, 0, NULL, C_BabobbaSleep, R_Draw, &s_babobbasleep3};\r
+statetype s_babobbasleep3      = {BABOBBASLEEP3SPR, BABOBBASLEEP3SPR, step,      false, false,  15, 0, 0, NULL, C_BabobbaSleep, R_Draw, &s_babobbasleep4};\r
+statetype s_babobbasleep4      = {BABOBBASLEEP4SPR, BABOBBASLEEP4SPR, step,      false, false, 500, 0, 0, NULL, C_BabobbaSleep, R_Draw, &s_babobbasleep5};\r
+statetype s_babobbasleep5      = {BABOBBASLEEP3SPR, BABOBBASLEEP3SPR, step,      false, false,  15, 0, 0, NULL, C_BabobbaSleep, R_Draw, &s_babobbasleep6};\r
+statetype s_babobbasleep6      = {BABOBBASLEEP2SPR, BABOBBASLEEP2SPR, step,      false, false,  15, 0, 0, NULL, C_BabobbaSleep, R_Draw, &s_babobbasleep7};\r
+statetype s_babobbasleep7      = {BABOBBASLEEP1SPR, BABOBBASLEEP1SPR, step,      false, false,  15, 0, 0, NULL, C_BabobbaSleep, R_Draw, &s_babobbastand};\r
+statetype s_babobbashot1       = {BABOBBASHOT1SPR,  BABOBBASHOT1SPR,  think,     false, false,   0, 0, 0, T_Projectile, C_Lethal, R_Bounce, &s_babobbashot2};\r
+statetype s_babobbashot2       = {BABOBBASHOT2SPR,  BABOBBASHOT2SPR,  step,      false, false,   8, 0, 0, T_BabobbaShot, C_Lethal, R_Draw, &s_babobbashot3};\r
+statetype s_babobbashot3       = {BABOBBASHOT1SPR,  BABOBBASHOT1SPR,  step,      false, false,   8, 0, 0, NULL, C_Lethal, R_Draw, &s_babobbashot2};\r
+statetype s_babobbashotvanish1 = {BABOBBASHOT2SPR,  BABOBBASHOT2SPR,  step,      false, false,   8, 0, 0, T_BabobbaShotVanish, C_Lethal, R_Draw, &s_babobbashotvanish2};\r
+statetype s_babobbashotvanish2 = {-1,               -1,               step,      false, false,   8, 0, 0, NULL, NULL, R_Draw, &s_babobbashotvanish1};\r
+\r
+/*\r
+===========================\r
+=\r
+= SpawnBabobba\r
+=\r
+===========================\r
+*/\r
+\r
+void SpawnBabobba(Uint16 tileX, Uint16 tileY)\r
+{\r
+       GetNewObj(false);\r
+       new->obclass = babobbaobj;\r
+       new->active = ac_yes;\r
+       new->priority = 0;\r
+       new->x = CONVERT_TILE_TO_GLOBAL(tileX);\r
+       new->y = CONVERT_TILE_TO_GLOBAL(tileY) + -2*TILEGLOBAL;\r
+       if (US_RndT() < 0x80)\r
+       {\r
+               new->xdir = 1;\r
+       }\r
+       else\r
+       {\r
+               new->xdir = -1;\r
+       }\r
+       new->ydir = 1;\r
+       NewState(new, &s_babobbajump1);\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= T_BabobbaStand\r
+=\r
+===========================\r
+*/\r
+\r
+void T_BabobbaStand(objtype *ob)\r
+{\r
+       Sint16 i;\r
+       Uint16 far *map;\r
+\r
+       if (US_RndT() < 4)\r
+       {\r
+               ob->temp1 = 0;\r
+               ob->state = &s_babobbasleep1;\r
+       }\r
+       else if (++ob->temp1 == 3)\r
+       {\r
+               ob->temp1 = 0;\r
+\r
+               GetNewObj(true);\r
+               new->active = ac_removable;\r
+               new->obclass = mshotobj;\r
+               new->y = ob->y + 4*PIXGLOBAL;\r
+               new->xdir = ob->xdir;\r
+               if (ob->xdir == 1)\r
+               {\r
+                       new->x = ob->x + 16*PIXGLOBAL;\r
+               }\r
+               else\r
+               {\r
+                       new->x = ob->x + 11*PIXGLOBAL;\r
+               }\r
+               new->xspeed = new->xdir << 5;\r
+               NewState(new, &s_babobbashot1);\r
+               new->priority = 2;\r
+               ob->state = &s_babobbaattack;\r
+       }\r
+       else\r
+       {\r
+               map = mapsegs[1] + mapbwidthtable[ob->tilebottom+1]/2 + ob->tilemidx;\r
+               for (i=0; i<4; map += ob->xdir, i++)\r
+               {\r
+                       if ( !tinf[*map+NORTHWALL]\r
+                               && !tinf[*(map-mapwidth)+NORTHWALL]\r
+                               && !tinf[*(map+mapwidth)+NORTHWALL] )\r
+                       {\r
+                               ob->xdir = -ob->xdir;\r
+                               break;\r
+                       }\r
+               }\r
+               ob->xspeed = ob->xdir *24;\r
+               ob->yspeed = -32;\r
+       }\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= C_Babobba\r
+=\r
+===========================\r
+*/\r
+\r
+void C_Babobba(objtype *ob, objtype *hit)\r
+{\r
+       if (hit->obclass == keenobj)\r
+       {\r
+               KillKeen();\r
+       }\r
+       else if (hit->obclass == stunshotobj)\r
+       {\r
+               StunObj(ob, hit, &s_babobbastun1);\r
+       }\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= C_BabobbaSleep\r
+=\r
+===========================\r
+*/\r
+\r
+void C_BabobbaSleep(objtype *ob, objtype *hit)\r
+{\r
+       if (hit->obclass == stunshotobj)\r
+       {\r
+               StunObj(ob, hit, &s_babobbastun1);\r
+       }\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= R_Babobba\r
+=\r
+===========================\r
+*/\r
+\r
+void R_Babobba(objtype *ob)\r
+{\r
+       if (ob->hiteast)\r
+       {\r
+               ob->xdir = 1;\r
+               ob->xspeed = -ob->xspeed;\r
+       }\r
+       else if (ob->hitwest)\r
+       {\r
+               ob->xdir = -1;\r
+               ob->xspeed = -ob->xspeed;\r
+       }\r
+\r
+       if (ob->hitsouth)\r
+               ob->yspeed = 0;\r
+\r
+       if (ob->hitnorth)\r
+       {\r
+               ChangeState(ob, &s_babobbastand);\r
+       }\r
+       RF_PlaceSprite(&ob->sprite, ob->x, ob->y, ob->shapenum, spritedraw, ob->priority);\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= T_BabobbaShot\r
+=\r
+===========================\r
+*/\r
+\r
+void T_BabobbaShot(objtype *ob)\r
+{\r
+       if (++ob->temp1 == 10)\r
+       {\r
+               ob->temp1 = 0;\r
+               ob->state = &s_babobbashotvanish1;\r
+       }\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= T_BabobbaShotVanish\r
+=\r
+===========================\r
+*/\r
+\r
+void T_BabobbaShotVanish(objtype *ob)\r
+{\r
+       if (++ob->temp1 == 5)\r
+       {\r
+               RemoveObj(ob);\r
+       }\r
+}\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                 BLORB\r
+\r
+=============================================================================\r
+*/\r
+\r
+statetype s_blorb1        = {BLORB1SPR, BLORB1SPR, slide, false, false, 20, 8, 8, 0, C_Lethal, R_Blorb, &s_blorb2};\r
+statetype s_blorb2        = {BLORB2SPR, BLORB2SPR, slide, false, false, 20, 8, 8, 0, C_Lethal, R_Blorb, &s_blorb3};\r
+statetype s_blorb3        = {BLORB3SPR, BLORB3SPR, slide, false, false, 20, 8, 8, 0, C_Lethal, R_Blorb, &s_blorb1};\r
+\r
+/*\r
+===========================\r
+=\r
+= SpawnBlorb\r
+=\r
+===========================\r
+*/\r
+\r
+void SpawnBlorb(Uint16 tileX, Uint16 tileY)\r
+{\r
+       GetNewObj(false);\r
+       new->obclass = blorbobj;\r
+       new->active = ac_yes;\r
+       new->priority = 0;\r
+       new->x = CONVERT_TILE_TO_GLOBAL(tileX);\r
+       new->y = CONVERT_TILE_TO_GLOBAL(tileY) + -8*PIXGLOBAL;\r
+       if (US_RndT() < 0x80)\r
+       {\r
+               new->xdir = 1;\r
+       }\r
+       else\r
+       {\r
+               new->xdir = -1;\r
+       }\r
+       if (US_RndT() < 0x80)\r
+       {\r
+               new->ydir = 1;\r
+       }\r
+       else\r
+       {\r
+               new->ydir = -1;\r
+       }\r
+       new->needtoclip = cl_fullclip;\r
+       NewState(new, &s_blorb1);\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= R_Blorb\r
+=\r
+===========================\r
+*/\r
+\r
+void R_Blorb(objtype *ob)\r
+{\r
+       if (ob->hitnorth)\r
+       {\r
+               ob->ydir = -1;\r
+               SD_PlaySound(SND_BLORBBOUNCE);\r
+       }\r
+       else if (ob->hitsouth)\r
+       {\r
+               ob->ydir = 1;\r
+               SD_PlaySound(SND_BLORBBOUNCE);\r
+       }\r
+       if (ob->hitwest)\r
+       {\r
+               ob->xdir = -1;\r
+               SD_PlaySound(SND_BLORBBOUNCE);\r
+       }\r
+       else if (ob->hiteast)\r
+       {\r
+               ob->xdir = 1;\r
+               SD_PlaySound(SND_BLORBBOUNCE);\r
+       }\r
+       RF_PlaceSprite(&ob->sprite, ob->x, ob->y, ob->shapenum, spritedraw, ob->priority);\r
+}\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                 CEILICK\r
+\r
+temp1 = initial y position\r
+\r
+=============================================================================\r
+*/\r
+\r
+statetype s_ceilickhidden   = {TONGUE1SPR,     TONGUE1SPR,     think, false, false, 20, 0,   0, T_CeilickHidden, NULL, R_Draw, NULL};\r
+statetype s_ceilickattack1  = {TONGUE1SPR,     TONGUE1SPR,     step,  false, false,  6, 0,   0, NULL, NULL, R_Draw, &s_ceilickattack2};\r
+statetype s_ceilickattack2  = {TONGUE2SPR,     TONGUE2SPR,     step,  false, false,  6, 0,   0, NULL, C_Lethal, R_Draw, &s_ceilickattack3};\r
+statetype s_ceilickattack3  = {TONGUE3SPR,     TONGUE3SPR,     step,  false, false,  6, 0,   0, NULL, C_Lethal, R_Draw, &s_ceilickattack4};\r
+statetype s_ceilickattack4  = {TONGUE4SPR,     TONGUE4SPR,     step,  false, false,  6, 0,   0, NULL, C_Lethal, R_Draw, &s_ceilickattack5};\r
+statetype s_ceilickattack5  = {TONGUE5SPR,     TONGUE5SPR,     step,  false, false,  6, 0,   0, NULL, C_Lethal, R_Draw, &s_ceilickattack6};\r
+statetype s_ceilickattack6  = {TONGUE4SPR,     TONGUE4SPR,     step,  false, false,  6, 0,   0, NULL, C_Lethal, R_Draw, &s_ceilickattack7};\r
+statetype s_ceilickattack7  = {TONGUE3SPR,     TONGUE3SPR,     step,  false, false,  6, 0,   0, NULL, C_Lethal, R_Draw, &s_ceilickattack8};\r
+statetype s_ceilickattack8  = {TONGUE4SPR,     TONGUE4SPR,     step,  false, false,  6, 0,   0, NULL, C_Lethal, R_Draw, &s_ceilickattack9};\r
+statetype s_ceilickattack9  = {TONGUE5SPR,     TONGUE5SPR,     step,  false, false,  6, 0,   0, NULL, C_Lethal, R_Draw, &s_ceilickattack10};\r
+statetype s_ceilickattack10 = {TONGUE2SPR,     TONGUE2SPR,     step,  false, false, 10, 0,   0, NULL, C_Lethal, R_Draw, &s_ceilickattack11};\r
+statetype s_ceilickattack11 = {TONGUE1SPR,     TONGUE1SPR,     step,  false, false, 10, 0,   0, NULL, NULL, R_Draw, &s_ceilicklaugh1};\r
+statetype s_ceilicklaugh1   = {CEILICK1SPR,    CEILICK1SPR,    slide, true,  false, 16, 0,  16, T_CeilickLaugh, C_Ceilick, R_Draw, &s_ceilicklaugh2};\r
+statetype s_ceilicklaugh2   = {CEILICK2SPR,    CEILICK2SPR,    step,  true,  false, 10, 0,   0, NULL, C_Ceilick, R_Draw, &s_ceilicklaugh3};\r
+statetype s_ceilicklaugh3   = {CEILICK1SPR,    CEILICK1SPR,    step,  true,  false, 10, 0,   0, NULL, C_Ceilick, R_Draw, &s_ceilicklaugh4};\r
+statetype s_ceilicklaugh4   = {CEILICK2SPR,    CEILICK2SPR,    step,  true,  false, 10, 0,   0, NULL, C_Ceilick, R_Draw, &s_ceilicklaugh5};\r
+statetype s_ceilicklaugh5   = {CEILICK1SPR,    CEILICK1SPR,    step,  true,  false, 10, 0,   0, NULL, C_Ceilick, R_Draw, &s_ceilicklaugh6};\r
+statetype s_ceilicklaugh6   = {CEILICK2SPR,    CEILICK2SPR,    step,  true,  false, 10, 0,   0, NULL, C_Ceilick, R_Draw, &s_ceilicklaugh7};\r
+statetype s_ceilicklaugh7   = {CEILICK1SPR,    CEILICK1SPR,    step,  true,  false, 10, 0,   0, NULL, C_Ceilick, R_Draw, &s_ceilicklaugh8};\r
+statetype s_ceilicklaugh8   = {CEILICK1SPR,    CEILICK1SPR,    slide, true,  false, 16, 0, -16, NULL, C_Ceilick, R_Draw, &s_ceilicklaugh9};\r
+statetype s_ceilicklaugh9   = {-1,             -1,             step,  true,  false, 60, 0,   0, NULL, C_Ceilick, R_Draw, &s_ceilickhidden};\r
+statetype s_ceilickstun     = {CEILICKSTUNSPR, CEILICKSTUNSPR, think, true,  false,  0, 0,   0, T_CeilickStunned, NULL, R_Stunned, NULL};\r
+\r
+/*\r
+===========================\r
+=\r
+= SpawnCeilick\r
+=\r
+===========================\r
+*/\r
+\r
+void SpawnCeilick(Uint16 tileX, Uint16 tileY)\r
+{\r
+       GetNewObj(false);\r
+       new->obclass = ceilickobj;\r
+       new->active = ac_yes;\r
+       new->priority = 0;\r
+       new->needtoclip = cl_noclip;\r
+       new->x = CONVERT_TILE_TO_GLOBAL(tileX);\r
+       new->y = new->temp1 = CONVERT_TILE_TO_GLOBAL(tileY);\r
+       new->ydir = 1;\r
+       NewState(new, &s_ceilickhidden);\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= T_CeilickHidden\r
+=\r
+===========================\r
+*/\r
+\r
+void T_CeilickHidden(objtype *ob)\r
+{\r
+       if ( player->y - ob->y <= 40*PIXGLOBAL\r
+               && player->left < ob->right+PIXGLOBAL\r
+               && player->right > ob->left-PIXGLOBAL )\r
+       {\r
+               SD_PlaySound(SND_CEILICKATTACK);\r
+               ob->state = &s_ceilickattack1;\r
+       }\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= T_CeilickLaugh\r
+=\r
+===========================\r
+*/\r
+\r
+#pragma argsused\r
+void T_CeilickLaugh(objtype *ob)\r
+{\r
+       SD_PlaySound(SND_CEILICKLAUGH);\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= T_CeilickStunned\r
+=\r
+===========================\r
+*/\r
+\r
+void T_CeilickStunned(objtype *ob)\r
+{\r
+       ob->needtoreact = true; // to make sure the stunned stars animate\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= C_Ceilick\r
+=\r
+===========================\r
+*/\r
+\r
+void C_Ceilick(objtype *ob, objtype *hit)\r
+{\r
+       if (hit->obclass == stunshotobj)\r
+       {\r
+               ob->y = ob->temp1;\r
+               ExplodeShot(hit);\r
+               ob->temp1 = ob->temp2 = ob->temp3 = 0;\r
+               ob->temp4 = ob->obclass;\r
+               ChangeState(ob, &s_ceilickstun);\r
+               ob->obclass = stunnedobj;\r
+       }\r
+}
\ No newline at end of file
diff --git a/16/keen456/KEEN4-6/KEEN6/K6_DEF.H b/16/keen456/KEEN4-6/KEEN6/K6_DEF.H
new file mode 100755 (executable)
index 0000000..9453b52
--- /dev/null
@@ -0,0 +1,518 @@
+/* Reconstructed Commander Keen 4-6 Source Code\r
+ * Copyright (C) 2021 K1n9_Duk3\r
+ *\r
+ * This file is loosely based on:\r
+ * Keen Dreams Source Code\r
+ * Copyright (C) 2014 Javier M. Chavez\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License along\r
+ * with this program; if not, write to the Free Software Foundation, Inc.,\r
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+ */\r
+\r
+#ifndef __K6_DEF__\r
+#define __K6_DEF__\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                               GLOBAL CONSTANTS\r
+\r
+=============================================================================\r
+*/\r
+\r
+#if GRMODE == CGAGR\r
+#define MINMEMORY 255000l\r
+#else\r
+#define MINMEMORY 300000l\r
+#endif\r
+\r
+#define STARPALETTE   {0, 1, 24, 25, 4, 28, 6, 7, 31, 31, 31, 31, 31, 31, 31, 31, 0}\r
+#define INTROPALETTE  {0, 5, 5, 21, 1, 1, 1, 1, 17, 17, 17, 17, 19, 19, 19, 19, 0}\r
+#define SHRINKPALETTE {0, 5, 5, 21, 1, 1, 1, 1, 17, 17, 17, 17, 19, 19, 19,  5, 0}\r
+\r
+#define HIGHSCORE_LEFT 40\r
+#define HIGHSCORE_TOP  51\r
+#define HIGHSCORE_RIGHT        280\r
+#define HIGHSCORE_MAP  18\r
+\r
+#define STATUS_PRESSKEY_X 120\r
+\r
+#define WORLDMAPNAME   "Fribbulus Xax"\r
+#define DROPSNAME      "VIVAS"\r
+\r
+#define STARWARSMUSIC  13\r
+#define ENDINGMUSIC    1\r
+\r
+// levels in this range can NOT be re-entered (BWB level should be > MAXDONELEVEL)\r
+#define MINDONELEVEL 1\r
+#define MAXDONELEVEL 16\r
+\r
+#define INACTIVATEDIST 4\r
+\r
+//\r
+// tiles for worldmap teleporters\r
+//\r
+#define TELEPORTERTILE1 2613   // tile animation for teleporting out\r
+#define TELEPORTERTILE2 2629   // tile after teleporting out\r
+#define TELEPORTERTILE3 TELEPORTERTILE1        // tile animation for teleporting in\r
+#define TELEPORTERTILE4 TELEPORTERTILE2        // tile after teleporting in\r
+\r
+#define TELEPORERTILEMASK 3    // animation has 4 frames\r
+\r
+extern Sint16 groundslam;\r
+\r
+//HACKs:\r
+//#define US_ManualCheck() true\r
+boolean US_ManualCheck(void);\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                               K6_SPEC DEFINITIONS\r
+\r
+=============================================================================\r
+*/\r
+\r
+extern char far swtext[];\r
+extern char far *levelnames[GAMELEVELS];\r
+extern char far *levelenter[GAMELEVELS];\r
+\r
+void ScanInfoPlane(void);\r
+\r
+extern statetype s_keenstun;\r
+\r
+void FlipBigSwitch(objtype *ob, boolean isup);\r
+void GotSandwich(void);\r
+void GotHook(void);\r
+void GotPasscard(void);\r
+\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                               K6_ACT1 DEFINITIONS\r
+\r
+=============================================================================\r
+*/\r
+\r
+extern Sint16 pdirx[];\r
+extern Sint16 pdiry[];\r
+\r
+void C_ClipSide(objtype *ob, objtype *hit);\r
+void C_ClipTop(objtype *ob, objtype *hit);\r
+void R_Land(objtype *ob);\r
+void R_Bounce(objtype *ob);\r
+\r
+extern statetype s_bonus1;\r
+extern statetype s_bonus2;\r
+extern statetype s_bonusfly1;\r
+extern statetype s_bonusfly2;\r
+extern statetype s_bonusrise;\r
+\r
+extern statetype s_splash1;\r
+extern statetype s_splash2;\r
+extern statetype s_splash3;\r
+extern statetype s_splash4;\r
+\r
+extern Uint16 bonusshape[];\r
+\r
+void SpawnBonus(Uint16 tileX, Uint16 tileY, Uint16 type);\r
+void SpawnSplash(Uint16 tileX, Uint16 tileY);\r
+void T_Bonus(objtype *ob);\r
+void T_FlyBonus(objtype *ob);\r
+\r
+extern statetype s_grabbiter1;\r
+extern statetype s_grabbiter2;\r
+extern statetype s_grabbitersleep1;\r
+extern statetype s_grabbitersleep2;\r
+\r
+void SpawnGrabbiter(Uint16 tileX, Uint16 tileY);\r
+void C_Grabbiter(objtype *ob, objtype *hit);\r
+\r
+extern statetype s_rocket;\r
+extern statetype s_rocketfly1;\r
+extern statetype s_rocketfly2;\r
+extern statetype s_keenrocket;\r
+\r
+void SpawnRocket(Uint16 tileX, Uint16 tileY, Uint16 state);\r
+void T_Rocket(objtype *ob);\r
+void C_Rocket(objtype *ob, objtype *hit);\r
+void C_RocketFly(objtype *ob, objtype *hit);\r
+void T_RocketFly(objtype *ob);\r
+\r
+extern statetype s_grapplespot;\r
+extern statetype s_throwrope1;\r
+extern statetype s_throwrope2;\r
+extern statetype s_climbrope1;\r
+extern statetype s_climbrope2;\r
+extern statetype s_maprope;\r
+extern statetype s_mapropeshort;\r
+\r
+void SpawnGrappleSpot(Uint16 tileX, Uint16 tileY, Uint16 type);\r
+void T_ThrowRope(objtype *ob);\r
+void T_ClimbRope(objtype *ob);\r
+void C_GrappleSpot(objtype *ob, objtype *hit);\r
+\r
+extern statetype s_satellitestopspot;\r
+extern statetype s_worldkeensatellite;\r
+extern statetype s_satellite1;\r
+extern statetype s_satellite2;\r
+extern statetype s_satellite3;\r
+extern statetype s_satellite4;\r
+\r
+void SpawnSatelliteStop(Uint16 tileX, Uint16 tileY, Uint16 type);\r
+void SpawnSatellite(Uint16 tileX, Uint16 tileY);\r
+void T_Satellite(objtype *ob);\r
+void C_Satellite(objtype *ob, objtype *hit);\r
+void R_WorldKeenSatellite(objtype *ob);\r
+\r
+extern statetype s_sandwich;\r
+\r
+void SpawnSandwich(Uint16 tileX, Uint16 tileY);\r
+\r
+extern statetype s_hook;\r
+\r
+void SpawnHook(Uint16 tileX, Uint16 tileY);\r
+\r
+extern statetype s_passcard;\r
+\r
+void SpawnPasscard(Uint16 tileX, Uint16 tileY);\r
+void C_Molly(objtype *ob, objtype *hit);\r
+\r
+extern statetype s_molly1;\r
+extern statetype s_molly2;\r
+extern statetype s_molly3;\r
+extern statetype s_molly4;\r
+\r
+void SpawnMolly(Uint16 tileX, Uint16 tileY);\r
+\r
+extern statetype s_platform;\r
+\r
+void SpawnPlatform(Uint16 tileX, Uint16 tileY, Sint16 dir);\r
+void T_Platform(objtype *ob);\r
+\r
+extern statetype s_dropplatsit;\r
+extern statetype s_fallplatfall;\r
+extern statetype s_fallplatrise;\r
+\r
+void SpawnDropPlat(Uint16 tileX, Uint16 tileY);\r
+void T_DropPlatSit(objtype *ob);\r
+void T_DropPlatFall(objtype *ob);\r
+void T_DropPlatRise(objtype *ob);\r
+\r
+extern statetype s_staticplatform;\r
+\r
+void SpawnStaticPlat(Uint16 tileX, Uint16 tileY);\r
+\r
+extern statetype s_goplat;\r
+\r
+void SpawnGoPlat(Uint16 tileX, Uint16 tileY, Sint16 dir);\r
+void T_GoPlat(objtype *ob);\r
+void R_GoPlat(objtype *ob);\r
+\r
+extern statetype s_sneakplatsit;\r
+extern statetype s_sneakplatdodge;\r
+extern statetype s_sneakplatreturn;\r
+\r
+void SpawnSneakPlat(Uint16 tileX, Uint16 tileY);\r
+void T_SneakPlat(objtype *ob);\r
+\r
+extern statetype s_bloogwalk1;\r
+extern statetype s_bloogwalk2;\r
+extern statetype s_bloogwalk3;\r
+extern statetype s_bloogwalk4;\r
+extern statetype s_bloogstun;\r
+\r
+void SpawnBloog(Uint16 tileX, Uint16 tileY);\r
+void T_BloogWalk(objtype *ob);\r
+void C_Bloog(objtype *ob, objtype *hit);\r
+\r
+extern statetype s_blooguardwalk1;\r
+extern statetype s_blooguardwalk2;\r
+extern statetype s_blooguardwalk3;\r
+extern statetype s_blooguardwalk4;\r
+extern statetype s_blooguardattack1;\r
+extern statetype s_blooguardattack2;\r
+extern statetype s_blooguardattack3;\r
+extern statetype s_blooguardattack4;\r
+extern statetype s_blooguardstun;\r
+\r
+void SpawnBlooguard(Uint16 tileX, Uint16 tileY);\r
+void T_BlooguardWalk(objtype *ob);\r
+void T_BlooguardAttack(objtype *ob);\r
+void C_Blooguard(objtype *ob, objtype *hit);\r
+void R_Blooguard(objtype *ob);\r
+\r
+extern statetype s_rbloogletwalk1;\r
+extern statetype s_rbloogletwalk2;\r
+extern statetype s_rbloogletwalk3;\r
+extern statetype s_rbloogletwalk4;\r
+extern statetype s_rbloogletstun;\r
+extern statetype s_ybloogletwalk1;\r
+extern statetype s_ybloogletwalk2;\r
+extern statetype s_ybloogletwalk3;\r
+extern statetype s_ybloogletwalk4;\r
+extern statetype s_ybloogletstun;\r
+extern statetype s_bbloogletwalk1;\r
+extern statetype s_bbloogletwalk2;\r
+extern statetype s_bbloogletwalk3;\r
+extern statetype s_bbloogletwalk4;\r
+extern statetype s_bbloogletstun;\r
+extern statetype s_gbloogletwalk1;\r
+extern statetype s_gbloogletwalk2;\r
+extern statetype s_gbloogletwalk3;\r
+extern statetype s_gbloogletwalk4;\r
+extern statetype s_gbloogletstun;\r
+\r
+void SpawnBlooglet(Uint16 tileX, Uint16 tileY, Sint16 type);\r
+void C_Blooglet(objtype *ob, objtype *hit);\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                               K6_ACT2 DEFINITIONS\r
+\r
+=============================================================================\r
+*/\r
+\r
+extern statetype s_nospikestand;\r
+extern statetype s_nospikewalk1;\r
+extern statetype s_nospikewalk2;\r
+extern statetype s_nospikewalk3;\r
+extern statetype s_nospikewalk4;\r
+extern statetype s_nospikerun1;\r
+extern statetype s_nospikerun2;\r
+extern statetype s_nospikerun3;\r
+extern statetype s_nospikerun4;\r
+extern statetype s_nospikeconfused1;\r
+extern statetype s_nospikeconfused2;\r
+extern statetype s_nospikeconfused3;\r
+extern statetype s_nospikefall;\r
+extern statetype s_nospikestun;\r
+\r
+void SpawnNospike(Uint16 tileX, Uint16 tileY);\r
+void T_NospikeWalk(objtype *ob);\r
+void T_NospikeRun(objtype *ob);\r
+void C_Nospike(objtype *ob, objtype *hit);\r
+void T_NospikeConfused(objtype* ob);\r
+void R_NospikeConfused(objtype *ob);\r
+void R_NospikeFall(objtype *ob);\r
+void R_NospikeRun(objtype *ob);\r
+\r
+extern statetype s_gikwalk1;\r
+extern statetype s_gikwalk2;\r
+extern statetype s_gikwalk3;\r
+extern statetype s_gikjump;\r
+extern statetype s_gikslide1;\r
+extern statetype s_gikslide2;\r
+extern statetype s_gikstand;\r
+\r
+void SpawnGik(Uint16 tileX, Uint16 tileY);\r
+void T_GikWalk(objtype *ob);\r
+void T_GikSlide(objtype *ob);\r
+void R_GikJump(objtype *ob);\r
+void R_GikSlide(objtype *ob);\r
+\r
+extern statetype s_cannon;\r
+extern statetype s_cannonfire;\r
+extern statetype s_cshot1;\r
+extern statetype s_cshot2;\r
+extern statetype s_cshot3;\r
+extern statetype s_cshot4;\r
+extern statetype s_cshothit1;\r
+extern statetype s_cshothit2;\r
+\r
+void SpawnCannon(Uint16 tileX, Uint16 tileY, Sint16 dir);\r
+void T_Cannon(objtype *ob);\r
+void C_CShot(objtype *ob, objtype *hit);\r
+void R_CShot(objtype *ob);\r
+\r
+extern statetype s_orbatrix1;\r
+extern statetype s_orbatrix2;\r
+extern statetype s_orbatrixcurl1;\r
+extern statetype s_orbatrixcurl2;\r
+extern statetype s_orbatrixcurl3;\r
+extern statetype s_orbatrixuncurl1;\r
+extern statetype s_orbatrixuncurl2;\r
+extern statetype s_orbatrixidle1;\r
+extern statetype s_orbatrixidle2;\r
+extern statetype s_orbatrixidle3;\r
+extern statetype s_orbatrixidle4;\r
+extern statetype s_orbatrixbounce1;\r
+extern statetype s_orbatrixbounce2;\r
+extern statetype s_orbatrixbounce3;\r
+extern statetype s_orbatrixbounce4;\r
+\r
+void SpawnOrbatrix(Uint16 tileX, Uint16 tileY);\r
+void T_OrbatrixFly(objtype *ob);\r
+void C_Orbatrix(objtype *ob, objtype *hit);\r
+void R_Orbatrix(objtype *ob);\r
+void R_OrbatrixBounce(objtype *ob);\r
+void T_OrbatrixCurl(objtype *ob);\r
+void T_OrbatrixUncurl(objtype *ob);\r
+void C_OrbatrixBounce(objtype *ob, objtype *hit);\r
+\r
+extern statetype s_bipstand;\r
+extern statetype s_bipwalk1;\r
+extern statetype s_bipwalk2;\r
+extern statetype s_bipwalk3;\r
+extern statetype s_bipwalk4;\r
+extern statetype s_bipsquished;\r
+\r
+void T_BipWalk(objtype *ob);\r
+void C_Bip(objtype *ob, objtype *hit);\r
+\r
+extern statetype s_bipship;\r
+extern statetype s_bipshipshot;\r
+extern statetype s_bipshipturn1;\r
+extern statetype s_bipshipturn2;\r
+extern statetype s_bipshipturn3;\r
+extern statetype s_bipshipturn4;\r
+extern statetype s_bipshipexplode1;\r
+extern statetype s_bipshipexplode2;\r
+extern statetype s_bipshipexplode3;\r
+extern statetype s_bipshipsmoke1;\r
+extern statetype s_bipshipsmoke2;\r
+\r
+void SpawnBipship(Uint16 tileX, Uint16 tileY);\r
+void R_BipShot(objtype *ob);\r
+void T_BipshipTurn(objtype *ob);\r
+void T_BipshipFly(objtype *ob);\r
+void T_BipshipExplode(objtype *ob);\r
+void C_Bipship(objtype *ob, objtype *hit);\r
+\r
+extern statetype s_flectstand;\r
+extern statetype s_flectturn;\r
+extern statetype s_flectwalk1;\r
+extern statetype s_flectwalk2;\r
+extern statetype s_flectwalk3;\r
+extern statetype s_flectwalk4;\r
+extern statetype s_flectstun;\r
+\r
+void SpawnFlect(Uint16 tileX, Uint16 tileY);\r
+void T_FlectStand(objtype *ob);\r
+void T_FlectWalk(objtype *ob);\r
+void C_Flect(objtype *ob, objtype *hit);\r
+void R_Flect(objtype *ob);\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                               K6_ACT3 DEFINITIONS\r
+\r
+=============================================================================\r
+*/\r
+\r
+extern statetype s_fleexwalk1;\r
+extern statetype s_fleexwalk2;\r
+extern statetype s_fleexrun1;\r
+extern statetype s_fleexrun2;\r
+extern statetype s_fleexrun3;\r
+extern statetype s_fleexrun4;\r
+extern statetype s_fleexlook1;\r
+extern statetype s_fleexlook2;\r
+extern statetype s_fleexstun;\r
+\r
+void SpawnFleex(Uint16 tileX, Uint16 tileY);\r
+void T_FleexWalk(objtype *ob);\r
+void T_FleexLook(objtype *ob);\r
+void C_Fleex(objtype *ob, objtype *hit);\r
+\r
+extern statetype s_bobbajump1;\r
+extern statetype s_bobbajump2;\r
+extern statetype s_bobbastand;\r
+extern statetype s_bobbaattack;\r
+extern statetype s_bobbashot1;\r
+extern statetype s_bobbashot2;\r
+extern statetype s_bobbashot3;\r
+extern statetype s_bobbashot4;\r
+extern statetype s_bobbashot5;\r
+extern statetype s_bobbashot6;\r
+extern statetype s_bobbashot7;\r
+extern statetype s_bobbashot8;\r
+extern statetype s_bobbashotvanish1;\r
+extern statetype s_bobbashotvanish2;\r
+extern statetype s_bobbashotvanish3;\r
+\r
+void SpawnBobba(Uint16 tileX, Uint16 tileY);\r
+void T_BobbaShot(objtype *ob);\r
+void T_BobbaStand(objtype *ob);\r
+void C_Bobba(objtype *ob, objtype *hit);\r
+void R_Bobba(objtype *ob);\r
+void R_BobbaShot(objtype *ob);\r
+\r
+extern statetype s_babobbajump1;\r
+extern statetype s_babobbajump2;\r
+extern statetype s_babobbastand;\r
+extern statetype s_babobbaattack;\r
+extern statetype s_babobbastun1;\r
+extern statetype s_babobbastun2;\r
+extern statetype s_babobbasleep1;\r
+extern statetype s_babobbasleep2;\r
+extern statetype s_babobbasleep3;\r
+extern statetype s_babobbasleep4;\r
+extern statetype s_babobbasleep5;\r
+extern statetype s_babobbasleep6;\r
+extern statetype s_babobbasleep7;\r
+extern statetype s_babobbashot1;\r
+extern statetype s_babobbashot2;\r
+extern statetype s_babobbashot3;\r
+extern statetype s_babobbashotvanish1;\r
+extern statetype s_babobbashotvanish2;\r
+\r
+void SpawnBabobba(Uint16 tileX, Uint16 tileY);\r
+void T_BabobbaStand(objtype *ob);\r
+void C_Babobba(objtype *ob, objtype *hit);\r
+void C_BabobbaSleep(objtype *ob, objtype *hit);\r
+void R_Babobba(objtype *ob);\r
+void T_BabobbaShot(objtype *ob);\r
+void T_BabobbaShotVanish(objtype *ob);\r
+\r
+extern statetype s_blorb1;\r
+extern statetype s_blorb2;\r
+extern statetype s_blorb3;\r
+\r
+void SpawnBlorb(Uint16 tileX, Uint16 tileY);\r
+void R_Blorb(objtype *ob);\r
+\r
+extern statetype s_ceilickhidden;\r
+extern statetype s_ceilickattack1;\r
+extern statetype s_ceilickattack2;\r
+extern statetype s_ceilickattack3;\r
+extern statetype s_ceilickattack4;\r
+extern statetype s_ceilickattack5;\r
+extern statetype s_ceilickattack6;\r
+extern statetype s_ceilickattack7;\r
+extern statetype s_ceilickattack8;\r
+extern statetype s_ceilickattack9;\r
+extern statetype s_ceilickattack10;\r
+extern statetype s_ceilickattack11;\r
+extern statetype s_ceilicklaugh1;\r
+extern statetype s_ceilicklaugh2;\r
+extern statetype s_ceilicklaugh3;\r
+extern statetype s_ceilicklaugh4;\r
+extern statetype s_ceilicklaugh5;\r
+extern statetype s_ceilicklaugh6;\r
+extern statetype s_ceilicklaugh7;\r
+extern statetype s_ceilicklaugh8;\r
+extern statetype s_ceilicklaugh9;\r
+extern statetype s_ceilickstun;\r
+\r
+void SpawnCeilick(Uint16 tileX, Uint16 tileY);\r
+void T_CeilickHidden(objtype *ob);\r
+void T_CeilickLaugh(objtype *ob);\r
+void T_CeilickStunned(objtype *ob);\r
+void C_Ceilick(objtype *ob, objtype *hit);\r
+\r
+#endif
\ No newline at end of file
diff --git a/16/keen456/KEEN4-6/KEEN6/K6_SPEC.C b/16/keen456/KEEN4-6/KEEN6/K6_SPEC.C
new file mode 100755 (executable)
index 0000000..ab7c1d1
--- /dev/null
@@ -0,0 +1,887 @@
+/* Reconstructed Commander Keen 4-6 Source Code\r
+ * Copyright (C) 2021 K1n9_Duk3\r
+ *\r
+ * This file is loosely based on:\r
+ * Keen Dreams Source Code\r
+ * Copyright (C) 2014 Javier M. Chavez\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License along\r
+ * with this program; if not, write to the Free Software Foundation, Inc.,\r
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+ */\r
+\r
+/*\r
+K6_SPEC.C\r
+=========\r
+\r
+Contains (in this order):\r
+\r
+- lump definition\r
+- "Star Wars" crawl text\r
+- level names & messages\r
+- ScanInfoPlane() - for spawning the level objects and marking required sprites\r
+- stunned state for Keen\r
+- code to flip the big yellow switches\r
+- messages for sandwich, rope and passcard\r
+*/\r
+\r
+#include "CK_DEF.H"\r
+\r
+enum {\r
+       CONTROLS_LUMP,    //  0\r
+       KEEN_LUMP,        //  1\r
+       SUGAR1_LUMP,      //  2\r
+       SUGAR2_LUMP,      //  3\r
+       SUGAR3_LUMP,      //  4\r
+       SUGAR4_LUMP,      //  5\r
+       SUGAR5_LUMP,      //  6\r
+       SUGAR6_LUMP,      //  7\r
+       ONEUP_LUMP,       //  8\r
+       KEYGEM_LUMP,      //  9\r
+       AMMO_LUMP,        // 10\r
+       WORLDKEEN_LUMP,   // 11\r
+       UNUSED1_LUMP,     // 12\r
+       BLOOG_LUMP,       // 13\r
+       RBLOOGLET_LUMP,   // 14\r
+       YBLOOGLET_LUMP,   // 15\r
+       BBLOOGLET_LUMP,   // 16\r
+       GBLOOGLET_LUMP,   // 17\r
+       PLATFORM_LUMP,    // 18\r
+       GIK_LUMP,         // 19\r
+       BLORB_LUMP,       // 20\r
+       BOBBA_LUMP,       // 21\r
+       BABOBBA_LUMP,     // 22\r
+       BLOOGUARD_LUMP,   // 23\r
+       FLECT_LUMP,       // 24\r
+       BIP_LUMP,         // 25\r
+       BIPSQUISHED_LUMP, // 26\r
+       BIPSHIP_LUMP,     // 27\r
+       NOSPIKE_LUMP,     // 28\r
+       ORBATRIX_LUMP,    // 29\r
+       CEILICK_LUMP,     // 30\r
+       FLEEX_LUMP,       // 31\r
+       HOOK_LUMP,        // 32\r
+       SANDWICH_LUMP,    // 33\r
+       LASER_LUMP,       // 34\r
+       PASSCARD_LUMP,    // 35\r
+       MOLLY_LUMP,       // 36\r
+\r
+       NUMLUMPS=40       // Keen 6 has 3 unused lumps at the end\r
+};\r
+\r
+Uint16 lumpstart[NUMLUMPS] = {\r
+       CONTROLS_LUMP_START,\r
+       KEEN_LUMP_START,\r
+       SUGAR1_LUMP_START,\r
+       SUGAR2_LUMP_START,\r
+       SUGAR3_LUMP_START,\r
+       SUGAR4_LUMP_START,\r
+       SUGAR5_LUMP_START,\r
+       SUGAR6_LUMP_START,\r
+       ONEUP_LUMP_START,\r
+       KEYGEM_LUMP_START,\r
+       AMMO_LUMP_START,\r
+       WORLDKEEN_LUMP_START,\r
+       0,\r
+       BLOOG_LUMP_START,\r
+       RBLOOGLET_LUMP_START,\r
+       YBLOOGLET_LUMP_START,\r
+       BBLOOGLET_LUMP_START,\r
+       GBLOOGLET_LUMP_START,\r
+       PLATFORM_LUMP_START,\r
+       GIK_LUMP_START,\r
+       BLORB_LUMP_START,\r
+       BOBBA_LUMP_START,\r
+       BABOBBA_LUMP_START,\r
+       BLOOGUARD_LUMP_START,\r
+       FLECT_LUMP_START,\r
+       BIP_LUMP_START,\r
+       BIPSQUISHED_LUMP_START,\r
+       BIPSHIP_LUMP_START,\r
+       NOSPIKE_LUMP_START,\r
+       ORBATRIX_LUMP_START,\r
+       CEILICK_LUMP_START,\r
+       FLEEX_LUMP_START,\r
+       HOOK_LUMP_START,\r
+       SANDWICH_LUMP_START,\r
+       LASER_LUMP_START,\r
+       PASSCARD_LUMP_START,\r
+       MOLLY_LUMP_START\r
+};\r
+\r
+Uint16 lumpend[NUMLUMPS] = {\r
+       CONTROLS_LUMP_END,\r
+       KEEN_LUMP_END,\r
+       SUGAR1_LUMP_END,\r
+       SUGAR2_LUMP_END,\r
+       SUGAR3_LUMP_END,\r
+       SUGAR4_LUMP_END,\r
+       SUGAR5_LUMP_END,\r
+       SUGAR6_LUMP_END,\r
+       ONEUP_LUMP_END,\r
+       KEYGEM_LUMP_END,\r
+       AMMO_LUMP_END,\r
+       WORLDKEEN_LUMP_END,\r
+       0,\r
+       BLOOG_LUMP_END,\r
+       RBLOOGLET_LUMP_END,\r
+       YBLOOGLET_LUMP_END,\r
+       BBLOOGLET_LUMP_END,\r
+       GBLOOGLET_LUMP_END,\r
+       PLATFORM_LUMP_END,\r
+       GIK_LUMP_END,\r
+       BLORB_LUMP_END,\r
+       BOBBA_LUMP_END,\r
+       BABOBBA_LUMP_END,\r
+       BLOOGUARD_LUMP_END,\r
+       FLECT_LUMP_END,\r
+       BIP_LUMP_END,\r
+       BIPSQUISHED_LUMP_END,\r
+       BIPSHIP_LUMP_END,\r
+       NOSPIKE_LUMP_END,\r
+       ORBATRIX_LUMP_END,\r
+       CEILICK_LUMP_END,\r
+       FLEEX_LUMP_END,\r
+       HOOK_LUMP_END,\r
+       SANDWICH_LUMP_END,\r
+       LASER_LUMP_END,\r
+       PASSCARD_LUMP_END,\r
+       MOLLY_LUMP_END\r
+};\r
+\r
+boolean lumpneeded[NUMLUMPS];\r
+\r
+#if GRMODE == EGAGR\r
+\r
+char far swtext[] =\r
+       "Episode Six\n"\r
+       "\n"\r
+       "Aliens Ate My\n"\r
+       "Baby Sitter!\n"\r
+       "\n"\r
+       "While out in his\n"\r
+       "backyard clubhouse,\n"\r
+       "Billy's baby sitter\n"\r
+       "Molly calls him for\n"\r
+       "dinner. He continues\n"\r
+       "working on his new\n"\r
+       "wrist computer.\n"\r
+       "\n"\r
+       "Suddenly, there is a\n"\r
+       "loud noise outside.\n"\r
+       "\n"\r
+       "Rushing out, Keen finds\n"\r
+       "his baby sitter gone\n"\r
+       "and a note on a patch\n"\r
+       "of scorched grass.  The\n"\r
+       "Bloogs of Fribbulus Xax\n"\r
+       "are going to make a\n"\r
+       "meal out of Molly!\n"\r
+       "\n"\r
+       "You've got to rescue\n"\r
+       "her, because your\n"\r
+       "parents will never\n"\r
+       "believe you when you\n"\r
+       "tell them...\n"\r
+       "\n"\r
+       "\"Aliens Ate My\n"\r
+       "Baby Sitter!\"\n";\r
+\r
+#endif\r
+\r
+char far l0n[] = "Fribbulus Xax";\r
+char far l1n[] = "Bloogwaters\nCrossing";\r
+char far l2n[] = "Guard Post One";\r
+char far l3n[] = "First Dome\nof Darkness";\r
+char far l4n[] = "Second Dome\nof Darkness";\r
+char far l5n[] = "The Bloogdome";\r
+char far l6n[] = "Bloogton Mfg.,\nIncorporated";\r
+char far l7n[] = "Bloogton Tower";\r
+char far l8n[] = "Bloogfoods, Inc.";\r
+char far l9n[] = "Guard Post Two";\r
+char far l10n[] = "Bloogville";\r
+char far l11n[] = "BASA";\r
+char far l12n[] = "Guard Post Three";\r
+char far l13n[] = "Bloogbase Rec\nDistrict";\r
+char far l14n[] = "Bloogbase Mgmt.\nDistrict";\r
+char far l15n[] = "Bloog Control Center";\r
+char far l16n[] = "Blooglab";\r
+char far l17n[] = "Bean-with-Bacon\nMegarocket";\r
+char far l18n[] = "High Scores";\r
+\r
+char far l0e[] = "Keen attacks\nFribbulus Xax";\r
+char far l1e[] = "Keen hops across\nBloogwaters\nCrossing";\r
+char far l2e[] = "Keen fights his way\nthrough Guard Post One";\r
+char far l3e[] = "Keen crosses into the\nFirst Dome of Darkness";\r
+char far l4e[] = "Keen dares to enter the\nSecond Dome of Darkness";\r
+char far l5e[] = "Keen foolishly enters\nthe Bloogdome";\r
+char far l6e[] = "Keen makes his way\ninto Bloogton\nManufacturing";\r
+char far l7e[] = "Keen ascends\nBloogton Tower";\r
+char far l8e[] = "Keen hungrily enters\nBloogfoods, Inc.";\r
+char far l9e[] = "Keen smashes through\nGuard Post Two";\r
+char far l10e[] = "Keen seeks thrills\nin Bloogville";\r
+char far l11e[] = "Keen rockets into the\nBloog Aeronautics and\nSpace Administration";\r
+char far l12e[] = "Keen boldly assaults\nGuard Post Three";\r
+char far l13e[] = "Keen whoops it up in\nthe Bloogbae\nRecreational District"; // sic!\r
+char far l14e[] = "Keen purposefully struts\ninto the Bloogbase\nManagement District";\r
+char far l15e[] = "Keen bravely enters the\nBloog Control Center,\nlooking for Molly";\r
+char far l16e[] = "Keen warily enters\nBlooglab Space\nStation";\r
+char far l17e[] = "Keen returns to the\nBean-with-Bacon\nMegarocket";\r
+char far l18e[] = "Keen is in the High\nScore screen. Call Id!";\r
+\r
+char far *levelnames[GAMELEVELS] = {\r
+       l0n,\r
+       l1n,\r
+       l2n,\r
+       l3n,\r
+       l4n,\r
+       l5n,\r
+       l6n,\r
+       l7n,\r
+       l8n,\r
+       l9n,\r
+       l10n,\r
+       l11n,\r
+       l12n,\r
+       l13n,\r
+       l14n,\r
+       l15n,\r
+       l16n,\r
+       l17n,\r
+       l18n\r
+};\r
+\r
+char far *levelenter[GAMELEVELS] = {\r
+       l0e,\r
+       l1e,\r
+       l2e,\r
+       l3e,\r
+       l4e,\r
+       l5e,\r
+       l6e,\r
+       l7e,\r
+       l8e,\r
+       l9e,\r
+       l10e,\r
+       l11e,\r
+       l12e,\r
+       l13e,\r
+       l14e,\r
+       l15e,\r
+       l16e,\r
+       l17e,\r
+       l18e\r
+};\r
+\r
+Uint16 bonuslump[] = {\r
+       KEYGEM_LUMP, KEYGEM_LUMP, KEYGEM_LUMP, KEYGEM_LUMP,\r
+       SUGAR1_LUMP, SUGAR2_LUMP, SUGAR3_LUMP,\r
+       SUGAR4_LUMP, SUGAR5_LUMP, SUGAR6_LUMP,\r
+       ONEUP_LUMP, AMMO_LUMP, AMMO_LUMP, 0, 0\r
+};\r
+\r
+//============================================================================\r
+\r
+/*\r
+==========================\r
+=\r
+= ScanInfoPlane\r
+=\r
+= Spawn all actors and mark down special places\r
+=\r
+==========================\r
+*/\r
+\r
+void ScanInfoPlane(void)\r
+{\r
+       Uint16 i, x, y, chunk;\r
+       Sint16 info;\r
+       Uint16 far *map;\r
+       objtype *ob;\r
+\r
+       InitObjArray();                  // start spawning things with a clean slate\r
+\r
+       memset(lumpneeded, 0, sizeof(lumpneeded));\r
+       map = mapsegs[2];\r
+\r
+       for (y=0; y<mapheight; y++)\r
+       {\r
+               for (x=0; x<mapwidth; x++)\r
+               {\r
+                       info = *map++;\r
+\r
+                       if (info == 0)\r
+                               continue;\r
+\r
+                       switch (info)\r
+                       {\r
+                       case 1:\r
+                               SpawnKeen(x, y, 1);\r
+                               SpawnScore();\r
+                               lumpneeded[KEEN_LUMP] = true;\r
+                               CA_MarkGrChunk(SCOREBOXSPR);\r
+                               break;\r
+\r
+                       case 2:\r
+                               SpawnKeen(x, y, -1);\r
+                               SpawnScore();\r
+                               lumpneeded[KEEN_LUMP] = true;\r
+                               CA_MarkGrChunk(SCOREBOXSPR);\r
+                               break;\r
+\r
+                       case 3:\r
+                               SpawnWorldKeen(x, y);\r
+                               SpawnScore();\r
+                               lumpneeded[WORLDKEEN_LUMP] = true;\r
+                               CA_MarkGrChunk(SCOREBOXSPR);\r
+                               break;\r
+\r
+                       case 6:\r
+                               if (gamestate.difficulty < gd_Hard)\r
+                                       break;\r
+                       case 5:\r
+                               if (gamestate.difficulty < gd_Normal)\r
+                                       break;\r
+                       case 4:\r
+                               SpawnBloog(x, y);\r
+                               lumpneeded[BLOOG_LUMP] = true;\r
+                               break;\r
+\r
+                       case 7:\r
+                       case 8:\r
+                       case 9:\r
+                       case 10:\r
+                       case 11:\r
+                       case 12:\r
+                       case 13:\r
+                       case 14:\r
+                               SpawnBlooglet(x, y, info-7);\r
+                               lumpneeded[(info-7) % 4 + RBLOOGLET_LUMP] = true;\r
+                               if (info > 10)\r
+                                       lumpneeded[KEYGEM_LUMP] = true;\r
+                               break;\r
+\r
+                       case 15:\r
+                       case 16:\r
+                               SpawnGrappleSpot(x, y, info-15);\r
+                               break;\r
+\r
+                       // case 17 is not used\r
+\r
+                       case 20:\r
+                               if (gamestate.difficulty < gd_Hard)\r
+                                       break;\r
+                       case 19:\r
+                               if (gamestate.difficulty < gd_Normal)\r
+                                       break;\r
+                       case 18:\r
+                               SpawnFleex(x, y);\r
+                               lumpneeded[FLEEX_LUMP] = true;\r
+                               break;\r
+\r
+                       case 21:\r
+                       case 22:\r
+                       case 23:\r
+                       case 24:\r
+                               SpawnMolly(x, y);\r
+                               lumpneeded[MOLLY_LUMP] = true;\r
+                               break;\r
+\r
+                       case 25:\r
+                               RF_SetScrollBlock(x, y, true);\r
+                               break;\r
+\r
+                       case 26:\r
+                               RF_SetScrollBlock(x, y, false);\r
+                               break;\r
+\r
+                       case 27:\r
+                       case 28:\r
+                       case 29:\r
+                       case 30:\r
+                               SpawnPlatform(x, y, info-27);\r
+                               lumpneeded[PLATFORM_LUMP] = true;\r
+                               break;\r
+\r
+                       // case 31 is the block icon\r
+\r
+                       case 32:\r
+                               SpawnDropPlat(x, y);\r
+                               lumpneeded[PLATFORM_LUMP] = true;\r
+                               break;\r
+\r
+                       case 35:\r
+                               SpawnStaticPlat(x, y);\r
+                               lumpneeded[PLATFORM_LUMP] = true;\r
+                               break;\r
+                       case 34:\r
+                               if (gamestate.difficulty > gd_Normal)\r
+                                       break;\r
+                               SpawnStaticPlat(x, y);\r
+                               lumpneeded[PLATFORM_LUMP] = true;\r
+                               break;\r
+                       case 33:\r
+                               if (gamestate.difficulty > gd_Easy)\r
+                                       break;\r
+                               SpawnStaticPlat(x, y);\r
+                               lumpneeded[PLATFORM_LUMP] = true;\r
+                               break;\r
+\r
+                       case 36:\r
+                       case 37:\r
+                       case 38:\r
+                       case 39:\r
+                               SpawnGoPlat(x, y, info-36);\r
+                               lumpneeded[PLATFORM_LUMP] = true;\r
+                               lumpneeded[BIPSQUISHED_LUMP] = true;    // why?\r
+                               break;\r
+\r
+                       case 40:\r
+                               SpawnSneakPlat(x, y);\r
+                               lumpneeded[PLATFORM_LUMP] = true;\r
+                               break;\r
+\r
+                       case 43:\r
+                               if (gamestate.difficulty < gd_Hard)\r
+                                       break;\r
+                       case 42:\r
+                               if (gamestate.difficulty < gd_Normal)\r
+                                       break;\r
+                       case 41:\r
+                               SpawnBobba(x, y);\r
+                               lumpneeded[BOBBA_LUMP] = true;\r
+                               break;\r
+\r
+                       case 44:\r
+                       case 45:\r
+                               SpawnSatelliteStop(x, y, info-44);\r
+                               break;\r
+\r
+                       // case 46 is not used\r
+\r
+                       case 49:\r
+                               if (gamestate.difficulty < gd_Hard)\r
+                                       break;\r
+                       case 48:\r
+                               if (gamestate.difficulty < gd_Normal)\r
+                                       break;\r
+                       case 47:\r
+                               SpawnNospike(x, y);\r
+                               lumpneeded[NOSPIKE_LUMP] = true;\r
+                               break;\r
+\r
+                       case 52:\r
+                               if (gamestate.difficulty < gd_Hard)\r
+                                       break;\r
+                       case 51:\r
+                               if (gamestate.difficulty < gd_Normal)\r
+                                       break;\r
+                       case 50:\r
+                               SpawnGik(x, y);\r
+                               lumpneeded[GIK_LUMP] = true;\r
+                               break;\r
+\r
+                       case 53:\r
+                       case 54:\r
+                       case 55:\r
+                       case 56:\r
+                               SpawnCannon(x, y, info-53);\r
+                               lumpneeded[LASER_LUMP] = true;\r
+                               break;\r
+\r
+                       case 69:\r
+                               if (gamestate.ammo >= 5)\r
+                                       break;\r
+                               info = 68;\r
+                               // no break here!\r
+                       case 57:\r
+                       case 58:\r
+                       case 59:\r
+                       case 60:\r
+                       case 61:\r
+                       case 62:\r
+                       case 63:\r
+                       case 64:\r
+                       case 65:\r
+                       case 66:\r
+                       case 67:\r
+                       case 68:\r
+                               SpawnBonus(x, y, info-57);\r
+                               lumpneeded[bonuslump[info-57]] = true;\r
+                               break;\r
+\r
+                       case 72:\r
+                               if (gamestate.difficulty < gd_Hard)\r
+                                       break;\r
+                       case 71:\r
+                               if (gamestate.difficulty < gd_Normal)\r
+                                       break;\r
+                       case 70:\r
+                               SpawnOrbatrix(x, y);\r
+                               lumpneeded[ORBATRIX_LUMP] = true;\r
+                               break;\r
+\r
+                       case 75:\r
+                               if (gamestate.difficulty < gd_Hard)\r
+                                       break;\r
+                       case 74:\r
+                               if (gamestate.difficulty < gd_Normal)\r
+                                       break;\r
+                       case 73:\r
+                               SpawnBipship(x, y);\r
+                               lumpneeded[BIP_LUMP]=lumpneeded[BIPSHIP_LUMP]=lumpneeded[BIPSQUISHED_LUMP] = true;\r
+                               break;\r
+\r
+                       case 78:\r
+                               if (gamestate.difficulty < gd_Hard)\r
+                                       break;\r
+                       case 77:\r
+                               if (gamestate.difficulty < gd_Normal)\r
+                                       break;\r
+                       case 76:\r
+                               SpawnFlect(x, y);\r
+                               lumpneeded[FLECT_LUMP] = true;\r
+                               break;\r
+\r
+                       case 81:\r
+                               if (gamestate.difficulty < gd_Hard)\r
+                                       break;\r
+                       case 80:\r
+                               if (gamestate.difficulty < gd_Normal)\r
+                                       break;\r
+                       case 79:\r
+                               SpawnBlorb(x, y);\r
+                               lumpneeded[BLORB_LUMP] = true;\r
+                               break;\r
+\r
+                       case 84:\r
+                               if (gamestate.difficulty < gd_Hard)\r
+                                       break;\r
+                       case 83:\r
+                               if (gamestate.difficulty < gd_Normal)\r
+                                       break;\r
+                       case 82:\r
+                               SpawnCeilick(x, y);\r
+                               lumpneeded[CEILICK_LUMP] = true;\r
+                               break;\r
+\r
+                       case 87:\r
+                               if (gamestate.difficulty < gd_Hard)\r
+                                       break;\r
+                       case 86:\r
+                               if (gamestate.difficulty < gd_Normal)\r
+                                       break;\r
+                       case 85:\r
+                               SpawnBlooguard(x, y);\r
+                               lumpneeded[BLOOGUARD_LUMP] = true;\r
+                               break;\r
+\r
+                       case 88:\r
+                               SpawnGrabbiter(x, y);\r
+                               // no additional lump needed - sprites are in WORLDKEEN_LUMP\r
+                               break;\r
+\r
+                       case 89:\r
+                               SpawnSatellite(x, y);\r
+                               // no additional lump needed - sprites are in WORLDKEEN_LUMP\r
+                               break;\r
+\r
+                       // case 90 is not used\r
+                       // cases 91 to 98 are direction arrows\r
+\r
+                       case 99:\r
+                               SpawnHook(x, y);\r
+                               lumpneeded[HOOK_LUMP] = true;\r
+                               break;\r
+\r
+                       case 100:\r
+                               SpawnSandwich(x, y);\r
+                               lumpneeded[SANDWICH_LUMP] = true;\r
+                               break;\r
+\r
+                       case 101:\r
+                               SpawnPasscard(x, y);\r
+                               lumpneeded[PASSCARD_LUMP] = true;\r
+                               break;\r
+\r
+                       case 104:\r
+                               if (gamestate.difficulty < gd_Hard)\r
+                                       break;\r
+                       case 103:\r
+                               if (gamestate.difficulty < gd_Normal)\r
+                                       break;\r
+                       case 102:\r
+                               SpawnBabobba(x, y);\r
+                               lumpneeded[BABOBBA_LUMP] = true;\r
+                               break;\r
+\r
+                       case 105:\r
+                       case 106:\r
+                               SpawnRocket(x, y, info-105);\r
+                               // no additional lump needed - sprites are in WORLDKEEN_LUMP\r
+                               break;\r
+                       }\r
+               }\r
+       }\r
+\r
+       for (ob = player; ob; ob = ob->next)\r
+       {\r
+               if (ob->active != ac_allways)\r
+                       ob->active = ac_no;\r
+       }\r
+\r
+       for (i = 0; i < NUMLUMPS; i++)\r
+       {\r
+               if (lumpneeded[i])\r
+               {\r
+                       for (chunk = lumpstart[i]; chunk <= lumpend[i]; chunk++)\r
+                       {\r
+                               CA_MarkGrChunk(chunk);\r
+                       }\r
+               }\r
+       }\r
+}\r
+\r
+//============================================================================\r
+\r
+statetype s_keenstun = {KEENSTUNSPR, KEENSTUNSPR, step, false, true, 60, 0, 0, T_Projectile, KeenContact, KeenStandReact, &s_keenstand};\r
+\r
+//============================================================================\r
+\r
+/*\r
+===========================\r
+=\r
+= FlipBigSwitch\r
+=\r
+===========================\r
+*/\r
+\r
+void FlipBigSwitch(objtype *ob, boolean isup)\r
+{\r
+       Uint16 x, y;\r
+       Uint16 far *map;\r
+       Uint16 top, mid, bot;\r
+       Uint16 *tileptr;\r
+       Uint16 tile, tx, ty, xi, yi, offset, anim;\r
+       Uint16 tiles[6];\r
+\r
+       //\r
+       // handle flipping the switch itself:\r
+       //\r
+       if (isup)\r
+       {\r
+               ty = ob->tilebottom;\r
+       }\r
+       else\r
+       {\r
+               ty = ob->tiletop - 2;\r
+       }\r
+       tx = ob->tileleft - 1;\r
+       map = mapsegs[2] + mapbwidthtable[ty+1]/2 + tx + 1;\r
+       while (*map == 0)\r
+       {\r
+               map++;\r
+               tx++;\r
+       }\r
+       map = mapsegs[1] + mapbwidthtable[ty]/2 + tx;\r
+       tileptr = tiles;\r
+       for (y = 0; y < 3; y++, map += mapwidth)\r
+       {\r
+               for (x = 0; x < 2; tileptr++, x++)\r
+               {\r
+                       tile = map[x];\r
+                       *tileptr = tile + (Sint8)tinf[tile+MANIM];\r
+               }\r
+       }\r
+       RF_MemToMap(tiles, 1, tx, ty, 2, 3);\r
+\r
+       tile = *(mapsegs[2]+mapbwidthtable[ty+1]/2 + tx + 1);\r
+       x = tile >> 8;\r
+       y = tile & 0xFF;\r
+       SD_PlaySound(SND_USESWITCH);\r
+\r
+       //\r
+       // toggle whatever was linked to the switch (at tile x, y):\r
+       //\r
+       offset = mapbwidthtable[y]/2 + x;\r
+       map = mapsegs[2] + offset;\r
+       tile = *map;\r
+\r
+       if (tile >= DIRARROWSTART && tile < DIRARROWEND)\r
+       {\r
+               // turn direction arrow:\r
+               *map = arrowflip[tile-DIRARROWSTART] + DIRARROWSTART;\r
+       }\r
+       else\r
+       {\r
+               map = mapsegs[1] + offset;\r
+               tile = *map;\r
+               switch (tinf[tile+INTILE] & INTILE_TYPEMASK)\r
+               {\r
+               case INTILE_NOTHING:    // no special tiles\r
+                       mapsegs[2][offset] ^= PLATFORMBLOCK;\r
+                       break;\r
+\r
+               case INTILE_BRIDGE:     // bridge\r
+                       for (yi=y; y+2 > yi; yi++)\r
+                       {\r
+                               map = mapsegs[1] + mapbwidthtable[yi]/2 + x - (yi != y);\r
+                               for (xi = x - (yi != y); xi < mapwidth; xi++)\r
+                               {\r
+                                       tile = *map;\r
+                                       map++;\r
+                                       anim = tinf[tile + MANIM];\r
+                                       if (!anim)\r
+                                               break;\r
+                                       tile += (Sint8)anim;\r
+                                       RF_MemToMap(&tile, 1, xi, yi, 1, 1);\r
+                               }\r
+                       }\r
+                       break;\r
+\r
+               case INTILE_FORCEFIELD: // active force field\r
+                       map = mapsegs[1];\r
+                       top = *map;\r
+                       mid = *++map;\r
+                       bot = *++map;\r
+                       map = mapsegs[1] + mapbwidthtable[y+1]/2 + x;\r
+\r
+                       RF_MemToMap(&top, 1, x, y++, 1, 1);\r
+                       while (tinf[*map+INTILE] == INTILE_DEADLY)\r
+                       {\r
+                               RF_MemToMap(&mid, 1, x, y++, 1, 1);\r
+                               map += mapwidth;\r
+                       }\r
+                       RF_MemToMap(&bot, 1, x, y, 1, 1);\r
+                       break;\r
+\r
+               case INTILE_FORCEFIELDEND:      // inactive force field\r
+                       map = mapsegs[1] + 3;\r
+                       top = *map;\r
+                       mid = *++map;\r
+                       bot = *++map;\r
+                       map = mapsegs[1] + mapbwidthtable[y+1]/2 + x;\r
+\r
+                       RF_MemToMap(&top, 1, x, y++, 1, 1);\r
+                       while (tinf[*map+INTILE] != INTILE_FORCEFIELDEND)\r
+                       {\r
+                               RF_MemToMap(&mid, 1, x, y++, 1, 1);\r
+                               map += mapwidth;\r
+                       }\r
+                       RF_MemToMap(&bot, 1, x, y, 1, 1);\r
+               }\r
+       }\r
+}\r
+\r
+//============================================================================\r
+\r
+/*\r
+===========================\r
+=\r
+= GotSandwich\r
+=\r
+===========================\r
+*/\r
+\r
+void GotSandwich(void)\r
+{\r
+       SD_WaitSoundDone();\r
+       SD_PlaySound(SND_QUESTITEM);\r
+       CA_UpLevel();   // kinda useless without CA_CacheMarks or CA_SetGrPurge\r
+       // BUG: haven't made anything purgable here, caching the pic may cause an "out of memory" crash\r
+       CA_CacheGrChunk(KEENTALK1PIC);\r
+\r
+       US_CenterWindow(26, 8);\r
+       VWB_DrawPic(WindowX+WindowW-48, WindowY, KEENTALK1PIC);\r
+       WindowW -= 48;\r
+       PrintY += 12;\r
+       US_CPrint(\r
+               "This is the second\n"\r
+               "biggest sandwich\n"\r
+               "I ever saw!\n"\r
+               );\r
+       VW_UpdateScreen();\r
+       VW_WaitVBL(30);\r
+       IN_ClearKeysDown();\r
+       IN_Ack();\r
+       CA_DownLevel();\r
+       gamestate.sandwichstate = 1;\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= GotHook\r
+=\r
+===========================\r
+*/\r
+\r
+void GotHook(void)\r
+{\r
+       SD_WaitSoundDone();\r
+       SD_PlaySound(SND_QUESTITEM);\r
+       CA_UpLevel();   // kinda useless without CA_CacheMarks or CA_SetGrPurge\r
+       // BUG: haven't made anything purgable here, caching the pic may cause an "out of memory" crash\r
+       CA_CacheGrChunk(KEENTALK1PIC);\r
+\r
+       US_CenterWindow(26, 8);\r
+       VWB_DrawPic(WindowX+WindowW-48, WindowY, KEENTALK1PIC);\r
+       WindowW -= 48;\r
+       PrintY += 12;\r
+       US_CPrint(\r
+               "Wow! A rope and\n"\r
+               "grappling hook!\n"\r
+               "They look useful!\n"\r
+               );\r
+       VW_UpdateScreen();\r
+       VW_WaitVBL(30);\r
+       IN_ClearKeysDown();\r
+       IN_Ack();\r
+       CA_DownLevel();\r
+       gamestate.hookstate = 1;\r
+}\r
+\r
+/*\r
+===========================\r
+=\r
+= GotPasscard\r
+=\r
+===========================\r
+*/\r
+\r
+void GotPasscard(void)\r
+{\r
+       SD_WaitSoundDone();\r
+       SD_PlaySound(SND_QUESTITEM);\r
+       CA_UpLevel();   // kinda useless without CA_CacheMarks or CA_SetGrPurge\r
+       // BUG: haven't made anything purgable here, caching the pic may cause an "out of memory" crash\r
+       CA_CacheGrChunk(KEENTALK1PIC);\r
+\r
+       US_CenterWindow(26, 8);\r
+       VWB_DrawPic(WindowX+WindowW-48, WindowY, KEENTALK1PIC);\r
+       WindowW -= 48;\r
+       PrintY += 4;\r
+       US_CPrint(\r
+               "What's this? Cool!\n"\r
+               "A passcard for\n"\r
+               "the Bloogstar Rocket!\n"\r
+               "(It can fly through\n"\r
+               "their force field.)"\r
+               );\r
+       VW_UpdateScreen();\r
+       VW_WaitVBL(30);\r
+       IN_ClearKeysDown();\r
+       IN_Ack();\r
+       CA_DownLevel();\r
+       gamestate.passcardstate = 1;\r
+}\r
diff --git a/16/keen456/KEEN4-6/KEEN6/OBJ/fix_RCK6_v15.pat b/16/keen456/KEEN4-6/KEEN6/OBJ/fix_RCK6_v15.pat
new file mode 100755 (executable)
index 0000000..9fb9c33
--- /dev/null
@@ -0,0 +1,3 @@
+%file RCK6E15.exe 109058\r
+%patch $1C 0 0 0 0\r
+%end
\ No newline at end of file
diff --git a/16/keen456/KEEN4-6/KEEN6C/GFXC_CK6.EQU b/16/keen456/KEEN4-6/KEEN6C/GFXC_CK6.EQU
new file mode 100755 (executable)
index 0000000..3e71cb7
--- /dev/null
@@ -0,0 +1,55 @@
+;=====================================\r
+;\r
+; Graphics .EQU file for .CK6\r
+; not IGRAB-ed :)\r
+;\r
+;=====================================\r
+\r
+;INCLUDE "VERSION.EQU"\r
+\r
+;\r
+; Amount of each data item\r
+;\r
+NUMFONT     =  2\r
+NUMFONTM    =  0\r
+NUMPICM     =  3\r
+NUMTILE8    =  108\r
+NUMTILE8M   =  36\r
+NUMTILE32   =  0\r
+NUMTILE32M  =  0\r
+\r
+;\r
+; Amount of each item in episode 6\r
+;\r
+NUMPICS     =  34\r
+NUMSPRITES  =  390\r
+NUMTILE16   =  2376\r
+NUMTILE16M  =  2736\r
+NUMEXTERN   =  8\r
+\r
+\r
+;\r
+; File offsets for data items\r
+;\r
+STRUCTPIC       =      0\r
+STRUCTPICM      =      1\r
+STRUCTSPRITE    =      2\r
+\r
+STARTFONT       =      3\r
+STARTFONTM      =      (STARTFONT+NUMFONT)\r
+STARTPICS       =      (STARTFONTM+NUMFONTM)\r
+STARTPICM       =      (STARTPICS+NUMPICS)\r
+STARTSPRITES    =      (STARTPICM+NUMPICM)\r
+STARTTILE8      =      (STARTSPRITES+NUMSPRITES)\r
+STARTTILE8M     =      (STARTTILE8+1)\r
+STARTTILE16     =      (STARTTILE8M+1)\r
+STARTTILE16M    =      (STARTTILE16+NUMTILE16)\r
+STARTTILE32     =      (STARTTILE16M+NUMTILE16M)\r
+STARTTILE32M    =      (STARTTILE32+NUMTILE32)\r
+STARTEXTERN     =      (STARTTILE32M+NUMTILE32M)\r
+\r
+NUMCHUNKS       =      (STARTEXTERN+NUMEXTERN)\r
+\r
+;\r
+; Thank you for using IGRAB!\r
+;\r
diff --git a/16/keen456/KEEN4-6/KEEN6C/GFXC_CK6.H b/16/keen456/KEEN4-6/KEEN6C/GFXC_CK6.H
new file mode 100755 (executable)
index 0000000..d4c6455
--- /dev/null
@@ -0,0 +1,667 @@
+/* Reconstructed Commander Keen 4-6 Source Code\r
+ * Copyright (C) 2021 K1n9_Duk3\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License along\r
+ * with this program; if not, write to the Free Software Foundation, Inc.,\r
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+ */\r
+\r
+#ifndef __GFX_H__\r
+#define __GFX_H__\r
+\r
+//#include "VERSION.H"\r
+\r
+//////////////////////////////////////\r
+//\r
+// Graphics .H file for .CK6\r
+// not IGRAB-ed :)\r
+//\r
+//////////////////////////////////////\r
+\r
+//\r
+// Lump creation macros\r
+//\r
+\r
+#define START_LUMP(actualname, dummyname) actualname, dummyname=actualname-1,\r
+#define END_LUMP(actualname, dummyname) dummyname, actualname=dummyname-1,\r
+\r
+//\r
+// Amount of each data item\r
+//\r
+\r
+//common numbers:\r
+#define NUMCHUNKS    NUMGRCHUNKS\r
+#define NUMFONT      2\r
+#define NUMFONTM     0\r
+#define NUMPICM      3\r
+#define NUMTILE8     108       // BUG: only 104 tiles exist in EGAGRAPH!\r
+#define NUMTILE8M    36                // BUG: only 12 tiles exist in EGAGRAPH!\r
+#define NUMTILE32    0\r
+#define NUMTILE32M   0\r
+\r
+//episode-specific numbers:\r
+#define NUMPICS      34\r
+#define NUMSPRITES   390\r
+#define NUMTILE16    2376\r
+#define NUMTILE16M   2736\r
+#define NUMEXTERNS   8\r
+\r
+//\r
+// File offsets for data items\r
+//\r
+#define STRUCTPIC    0\r
+#define STRUCTPICM   1\r
+#define STRUCTSPRITE 2\r
+\r
+#define STARTFONT    3\r
+#define STARTFONTM   (STARTFONT+NUMFONT)\r
+#define STARTPICS    (STARTFONTM+NUMFONTM)\r
+#define STARTPICM    (STARTPICS+NUMPICS)\r
+#define STARTSPRITES (STARTPICM+NUMPICM)\r
+#define STARTTILE8   (STARTSPRITES+NUMSPRITES)\r
+#define STARTTILE8M  (STARTTILE8+1)\r
+#define STARTTILE16  (STARTTILE8M+1)\r
+#define STARTTILE16M (STARTTILE16+NUMTILE16)\r
+#define STARTTILE32  (STARTTILE16M+NUMTILE16M)\r
+#define STARTTILE32M (STARTTILE32+NUMTILE32)\r
+#define STARTEXTERNS (STARTTILE32M+NUMTILE32M)\r
+\r
+typedef enum {\r
+       LASTFONT=STARTPICS-1,\r
+\r
+       //\r
+       // PICS\r
+       //\r
+\r
+       PADDINGPIC,                  // 5 (compensate for the missing Star Wars font to give the other pics the correct chunk numbers)\r
+\r
+       H_END1PIC,                   // 6\r
+       H_END2PIC,                   // 7\r
+       H_END3PIC,                   // 8\r
+       H_END4PIC,                   // 9\r
+       H_END5PIC,                   // 10\r
+\r
+       START_LUMP(CONTROLS_LUMP_START, __CONTROLSSTART)\r
+       CP_MAINMENUPIC,              // 11\r
+       CP_NEWGAMEMENUPIC,           // 12\r
+       CP_LOADMENUPIC,              // 13\r
+       CP_SAVEMENUPIC,              // 14\r
+       CP_CONFIGMENUPIC,            // 15\r
+       CP_SOUNDMENUPIC,             // 16\r
+       CP_MUSICMENUPIC,             // 17\r
+       CP_KEYBOARDMENUPIC,          // 18\r
+       CP_KEYMOVEMENTPIC,           // 19\r
+       CP_KEYBUTTONPIC,             // 20\r
+       CP_JOYSTICKMENUPIC,          // 21\r
+       CP_OPTIONSMENUPIC,           // 22\r
+       CP_PADDLEWARPIC,             // 23\r
+       CP_QUITPIC,                  // 24\r
+       CP_JOYSTICKPIC,              // 25\r
+       CP_MENUSCREENPIC,            // 26\r
+       END_LUMP(CONTROLS_LUMP_END, __CONTROLSEND)\r
+\r
+       H_FLASHARROW1PIC,            // 27\r
+       H_FLASHARROW2PIC,            // 28\r
+\r
+       SW_BACKGROUNDPIC,            // 33\r
+       TITLEPICPIC,                 // 34\r
+       KEENTALK1PIC,                // 35\r
+       KEENTALK2PIC,                // 36\r
+       KEENCOUNT1PIC,               // 37\r
+       KEENCOUNT2PIC,               // 38\r
+       KEENCOUNT3PIC,               // 39\r
+       KEENCOUNT4PIC,               // 40\r
+       KEENCOUNT5PIC,               // 41\r
+       KEENCOUNT6PIC,               // 42\r
+\r
+       //\r
+       // MASKED PICS\r
+       //\r
+\r
+       CP_MENUMASKPICM,             // 43\r
+       CORDPICM,                    // 44\r
+       METALPOLEPICM,               // 45\r
+\r
+       //\r
+       // SPRITES\r
+       //\r
+\r
+       START_LUMP(PADDLE_LUMP_START, __PADDLESTART)\r
+       PADDLESPR,                   // 46\r
+       BALLSPR,                     // 47\r
+       BALL1PIXELTOTHERIGHTSPR,     // 48\r
+       BALL2PIXELSTOTHERIGHTSPR,    // 49\r
+       BALL3PIXELSTOTHERIGHTSPR,    // 50\r
+       END_LUMP(PADDLE_LUMP_END, __PADDLEEND)\r
+\r
+       DEMOPLAQUESPR,               // 51\r
+\r
+       START_LUMP(KEEN_LUMP_START, __KEENSTART)\r
+       KEENSTANDRSPR,               // 52\r
+       KEENRUNR1SPR,                // 53\r
+       KEENRUNR2SPR,                // 54\r
+       KEENRUNR3SPR,                // 55\r
+       KEENRUNR4SPR,                // 56\r
+       KEENJUMPR1SPR,               // 57\r
+       KEENJUMPR2SPR,               // 58\r
+       KEENJUMPR3SPR,               // 59\r
+       KEENSTANDLSPR,               // 60\r
+       KEENRUNL1SPR,                // 61\r
+       KEENRUNL2SPR,                // 62\r
+       KEENRUNL3SPR,                // 63\r
+       KEENRUNL4SPR,                // 64\r
+       KEENJUMPL1SPR,               // 65\r
+       KEENJUMPL2SPR,               // 66\r
+       KEENJUMPL3SPR,               // 67\r
+       KEENLOOKUSPR,                // 68\r
+       KEENWAITR1SPR,               // 69\r
+       KEENWAITR2SPR,               // 70\r
+       KEENWAITR3SPR,               // 71\r
+       KEENSITREAD1SPR,             // 72\r
+       KEENSITREAD2SPR,             // 73\r
+       KEENSITREAD3SPR,             // 74\r
+       KEENSITREAD4SPR,             // 75\r
+       KEENREAD1SPR,                // 76\r
+       KEENREAD2SPR,                // 77\r
+       KEENREAD3SPR,                // 78\r
+       KEENSTOPREAD1SPR,            // 79\r
+       KEENSTOPREAD2SPR,            // 80\r
+       KEENLOOKD1SPR,               // 81\r
+       KEENLOOKD2SPR,               // 82\r
+       KEENDIE1SPR,                 // 83\r
+       KEENDIE2SPR,                 // 84\r
+       KEENSTUNSPR,                 // 85\r
+       STUNSTARS1SPR,               // 86\r
+       STUNSTARS2SPR,               // 87\r
+       STUNSTARS3SPR,               // 88\r
+       KEENSHOOTLSPR,               // 89\r
+       KEENJLSHOOTLSPR,             // 90\r
+       KEENJSHOOTDSPR,              // 91\r
+       KEENJSHOOTUSPR,              // 92\r
+       KEENSHOOTUSPR,               // 93\r
+       KEENSHOOTRSPR,               // 94\r
+       KEENJRSHOOTRSPR,             // 95\r
+       STUN1SPR,                    // 96\r
+       STUN2SPR,                    // 97\r
+       STUN3SPR,                    // 98\r
+       STUN4SPR,                    // 99\r
+       STUNHIT1SPR,                 // 100\r
+       STUNHIT2SPR,                 // 101\r
+       KEENSHINNYR1SPR,             // 102\r
+       KEENSHINNYR2SPR,             // 103\r
+       KEENSHINNYR3SPR,             // 104\r
+       KEENSLIDED1SPR,              // 105\r
+       KEENSLIDED2SPR,              // 106\r
+       KEENSLIDED3SPR,              // 107\r
+       KEENSLIDED4SPR,              // 108\r
+       KEENSHINNYL1SPR,             // 109\r
+       KEENSHINNYL2SPR,             // 110\r
+       KEENSHINNYL3SPR,             // 111\r
+       KEENPLSHOOTUSPR,             // 112\r
+       KEENPRSHOOTUSPR,             // 113\r
+       KEENPRSHOOTDSPR,             // 114\r
+       KEENPLSHOOTDSPR,             // 115\r
+       KEENPSHOOTLSPR,              // 116\r
+       KEENPSHOOTRSPR,              // 117\r
+       KEENENTER1SPR,               // 118\r
+       KEENENTER2SPR,               // 119\r
+       KEENENTER3SPR,               // 120\r
+       KEENENTER4SPR,               // 121\r
+       KEENENTER5SPR,               // 122\r
+       KEENHANGLSPR,                // 123\r
+       KEENHANGRSPR,                // 124\r
+       KEENCLIMBEDGEL1SPR,          // 125\r
+       KEENCLIMBEDGEL2SPR,          // 126\r
+       KEENCLIMBEDGEL3SPR,          // 127\r
+       KEENCLIMBEDGEL4SPR,          // 128\r
+       KEENCLIMBEDGER1SPR,          // 129\r
+       KEENCLIMBEDGER2SPR,          // 130\r
+       KEENCLIMBEDGER3SPR,          // 131\r
+       KEENCLIMBEDGER4SPR,          // 132\r
+       KEENPOGOR1SPR,               // 133\r
+       KEENPOGOR2SPR,               // 134\r
+       KEENPOGOL1SPR,               // 135\r
+       KEENPOGOL2SPR,               // 136\r
+       BONUS100UPSPR,               // 137\r
+       BONUS100SPR,                 // 138\r
+       BONUS200SPR,                 // 139\r
+       BONUS500SPR,                 // 140\r
+       BONUS1000SPR,                // 141\r
+       BONUS2000SPR,                // 142\r
+       BONUS5000SPR,                // 143\r
+       BONUS1UPSPR,                 // 144\r
+       BONUSCLIPSPR,                // 145\r
+       VIVASPLASH1SPR,              // 146\r
+       VIVASPLASH2SPR,              // 147\r
+       VIVASPLASH3SPR,              // 148\r
+       VIVASPLASH4SPR,              // 149\r
+       END_LUMP(KEEN_LUMP_END, __KEENEND)\r
+\r
+       START_LUMP(SUGAR1_LUMP_START, __SUGAR1START)\r
+       SUGAR1ASPR,                  // 150\r
+       SUGAR1BSPR,                  // 151\r
+       END_LUMP(SUGAR1_LUMP_END, __SUGAR1END)\r
+\r
+       START_LUMP(SUGAR2_LUMP_START, __SUGAR2START)\r
+       SUGAR2ASPR,                  // 152\r
+       SUGAR2BSPR,                  // 153\r
+       END_LUMP(SUGAR2_LUMP_END, __SUGAR2END)\r
+\r
+       START_LUMP(SUGAR3_LUMP_START, __SUGAR3START)\r
+       SUGAR3ASPR,                  // 154\r
+       SUGAR3BSPR,                  // 155\r
+       END_LUMP(SUGAR3_LUMP_END, __SUGAR3END)\r
+\r
+       START_LUMP(SUGAR4_LUMP_START, __SUGAR4START)\r
+       SUGAR4ASPR,                  // 156\r
+       SUGAR4BSPR,                  // 157\r
+       END_LUMP(SUGAR4_LUMP_END, __SUGAR4END)\r
+\r
+       START_LUMP(SUGAR5_LUMP_START, __SUGAR5START)\r
+       SUGAR5ASPR,                  // 158\r
+       SUGAR5BSPR,                  // 159\r
+       END_LUMP(SUGAR5_LUMP_END, __SUGAR5END)\r
+\r
+       START_LUMP(SUGAR6_LUMP_START, __SUGAR6START)\r
+       SUGAR6ASPR,                  // 160\r
+       SUGAR6BSPR,                  // 161\r
+       END_LUMP(SUGAR6_LUMP_END, __SUGAR6END)\r
+\r
+       START_LUMP(ONEUP_LUMP_START, __ONEUPSTART)\r
+       ONEUPASPR,                   // 162\r
+       ONEUPBSPR,                   // 163\r
+       END_LUMP(ONEUP_LUMP_END, __ONEUPEND)\r
+\r
+       START_LUMP(KEYGEM_LUMP_START, __KEYGEMSTART)\r
+       REDGEM1SPR,                  // 164\r
+       REDGEM2SPR,                  // 165\r
+       YELLOWGEM1SPR,               // 166\r
+       YELLOWGEM2SPR,               // 167\r
+       BLUEGEM1SPR,                 // 168\r
+       BLUEGEM2SPR,                 // 169\r
+       GREENGEM1SPR,                // 170\r
+       GREENGEM2SPR,                // 171\r
+       BONUSGEMSPR,                 // 172\r
+       END_LUMP(KEYGEM_LUMP_END, __KEYGEMEND)\r
+\r
+       START_LUMP(AMMO_LUMP_START, __AMMOSTART)\r
+       STUNCLIP1SPR,                // 173\r
+       STUNCLIP2SPR,                // 174\r
+       END_LUMP(AMMO_LUMP_END, __AMMOEND)\r
+\r
+       SCOREBOXSPR,                 // 175\r
+\r
+       START_LUMP(LASER_LUMP_START, __LASERSTART)\r
+       LASER1SPR,                   // 176\r
+       LASER2SPR,                   // 177\r
+       LASER3SPR,                   // 178\r
+       LASER4SPR,                   // 179\r
+       LASERHIT1SPR,                // 180\r
+       LASERHIT2SPR,                // 181\r
+       END_LUMP(LASER_LUMP_END, __LASEREND)\r
+\r
+       START_LUMP(SANDWICH_LUMP_START, __SANDWICHSTART)\r
+       SANDWICHSPR,                 // 182\r
+       END_LUMP(SANDWICH_LUMP_END, __SANDWICHEND)\r
+\r
+       START_LUMP(HOOK_LUMP_START, __ROPESTART)\r
+       HOOKSPR,                     // 183\r
+       END_LUMP(HOOK_LUMP_END, __ROPEEND)\r
+\r
+       START_LUMP(WORLDKEEN_LUMP_START, __WORLDKEENSTART)\r
+       WORLDKEENL1SPR,              // 184\r
+       WORLDKEENL2SPR,              // 185\r
+       WORLDKEENL3SPR,              // 186\r
+       WORLDKEENR1SPR,              // 187\r
+       WORLDKEENR2SPR,              // 188\r
+       WORLDKEENR3SPR,              // 189\r
+       WORLDKEENU1SPR,              // 190\r
+       WORLDKEENU2SPR,              // 191\r
+       WORLDKEENU3SPR,              // 192\r
+       WORLDKEEND1SPR,              // 193\r
+       WORLDKEEND2SPR,              // 194\r
+       WORLDKEEND3SPR,              // 195\r
+       WORLDKEENDR1SPR,             // 196\r
+       WORLDKEENDR2SPR,             // 197\r
+       WORLDKEENDR3SPR,             // 198\r
+       WORLDKEENDL1SPR,             // 199\r
+       WORLDKEENDL2SPR,             // 200\r
+       WORLDKEENDL3SPR,             // 201\r
+       WORLDKEENUL1SPR,             // 202\r
+       WORLDKEENUL2SPR,             // 203\r
+       WORLDKEENUL3SPR,             // 204\r
+       WORLDKEENUR1SPR,             // 205\r
+       WORLDKEENUR2SPR,             // 206\r
+       WORLDKEENUR3SPR,             // 207\r
+       WORLDKEENWAVE1SPR,           // 208\r
+       WORLDKEENWAVE2SPR,           // 209\r
+       ROCKETSPR,                   // 210\r
+       ROCKETFLY1SPR,               // 211\r
+       ROCKETFLY2SPR,               // 212\r
+       SATELLITE1SPR,               // 213\r
+       SATELLITE2SPR,               // 214\r
+       SATELLITE3SPR,               // 215\r
+       SATELLITE4SPR,               // 216\r
+       GRABBITER1SPR,               // 217\r
+       GRABBITER2SPR,               // 218\r
+       GRABBITERSLEEP1SPR,          // 219\r
+       GRABBITERSLEEP2SPR,          // 220\r
+       WORLDKEENTRHOW1SPR,          // 221\r
+       WORLDKEENTRHOW2SPR,          // 222\r
+       WORLDKEENCLIMB1SPR,          // 223\r
+       WORLDKEENCLIMB2SPR,          // 224\r
+       ROPETHROW1SPR,               // 225\r
+       ROPETHROW2SPR,               // 226\r
+       WORLDKEENHANGSPR,            // 227\r
+       FLAGFLIP1SPR,                // 228\r
+       FLAGFLIP2SPR,                // 229\r
+       FLAGFLIP3SPR,                // 230\r
+       FLAGFLIP4SPR,                // 231\r
+       FLAGFLIP5SPR,                // 232\r
+       FLAGFALL1SPR,                // 233\r
+       FLAGFALL2SPR,                // 234\r
+       FLAGFLAP1SPR,                // 235\r
+       FLAGFLAP2SPR,                // 236\r
+       FLAGFLAP3SPR,                // 237\r
+       FLAGFLAP4SPR,                // 238\r
+       END_LUMP(WORLDKEEN_LUMP_END, __WORLDKEENEND)\r
+\r
+       START_LUMP(FLEEX_LUMP_START, __FLEEXSTART)\r
+       FLEEXWALKR1SPR,              // 239\r
+       FLEEXWALKR2SPR,              // 240\r
+       FLEEXWALKL1SPR,              // 241\r
+       FLEEXWALKL2SPR,              // 242\r
+       FLEEXLOOK1SPR,               // 243\r
+       FLEEXLOOK2SPR,               // 244\r
+       FLEEXSTUNSPR,             // 245\r
+       END_LUMP(FLEEX_LUMP_END, __FLEEXEND)\r
+\r
+       START_LUMP(CEILICK_LUMP_START, __CEILICKSTART)\r
+       CEILICK1SPR,                 // 246\r
+       CEILICK2SPR,                 // 247\r
+       TONGUE1SPR,                  // 248\r
+       TONGUE2SPR,                  // 249\r
+       TONGUE3SPR,                  // 250\r
+       TONGUE4SPR,                  // 251\r
+       TONGUE5SPR,                  // 252\r
+       CEILICKSTUNSPR,           // 253\r
+       END_LUMP(CEILICK_LUMP_END, __CEILICKEND)\r
+\r
+       START_LUMP(BLOOGUARD_LUMP_START, __BLOOGUARDSTART)\r
+       BLOOGUARDWALKL1SPR,          // 254\r
+       BLOOGUARDWALKL2SPR,          // 255\r
+       BLOOGUARDWALKL3SPR,          // 256\r
+       BLOOGUARDWALKL4SPR,          // 257\r
+       BLOOGUARDWALKR1SPR,          // 258\r
+       BLOOGUARDWALKR2SPR,          // 259\r
+       BLOOGUARDWALKR3SPR,          // 260\r
+       BLOOGUARDWALKR4SPR,          // 261\r
+       BLOOGUARDSWINGL1SPR,         // 262\r
+       BLOOGUARDSWINGL2SPR,         // 263\r
+       BLOOGUARDSWINGL3SPR,         // 264\r
+       BLOOGUARDSWINGR1SPR,         // 265\r
+       BLOOGUARDSWINGR2SPR,         // 266\r
+       BLOOGUARDSWINGR3SPR,         // 267\r
+       BLOOGUARDSTUNSPR,         // 268\r
+       END_LUMP(BLOOGUARD_LUMP_END, __BLOOGUARDEND)\r
+\r
+       START_LUMP(BIPSHIP_LUMP_START, __BIPSHIPSTART)\r
+       BIPSHIPRSPR,                 // 269\r
+       BIPSHIPRTURN1SPR,            // 270\r
+       BIPSHIPRTURN2SPR,            // 271\r
+       BIPSHIPRTURN3SPR,            // 272\r
+       BIPSHIPRTURN4SPR,            // 273\r
+       BIPSHIPLSPR,                 // 274\r
+       BIPSHIPLTURN1SPR,            // 275\r
+       BIPSHIPLTURN2SPR,            // 276\r
+       BIPSHIPLTURN3SPR,            // 277\r
+       BIPSHIPLTURN4SPR,            // 278\r
+       BIPSHIPEXPLODE1SPR,          // 279\r
+       BIPSHIPEXPLODE2SPR,          // 280\r
+       BIPSHIPEXPLODE3SPR,          // 281\r
+       BIPSHIPEXPLODE4SPR,          // 282\r
+       BIPSHIPEXPLODE5SPR,          // 283\r
+       BIPSHIPSHOTSPR,              // 284\r
+       END_LUMP(BIPSHIP_LUMP_END, __BIPSHIPEND)\r
+\r
+       START_LUMP(BABOBBA_LUMP_START, __BABOBBASTART)\r
+       BABOBBAL1SPR,                // 285\r
+       BABOBBAL2SPR,                // 286\r
+       BABOBBAL3SPR,                // 287\r
+       BABOBBAR1SPR,                // 288\r
+       BABOBBAR2SPR,                // 289\r
+       BABOBBAR3SPR,                // 290\r
+       BABOBBASHOT1SPR,             // 291\r
+       BABOBBASHOT2SPR,             // 292\r
+       BABOBBASTUNSPR,           // 293\r
+       BABOBBASLEEP1SPR,            // 294\r
+       BABOBBASLEEP2SPR,            // 295\r
+       BABOBBASLEEP3SPR,            // 296\r
+       BABOBBASLEEP4SPR,            // 297\r
+       END_LUMP(BABOBBA_LUMP_END, __BABOBBAEND)\r
+\r
+       START_LUMP(NOSPIKE_LUMP_START, __NOSPIKESTART)\r
+       NOSPIKESTANDSPR,             // 298\r
+       NOSPIKERUNR1SPR,             // 299\r
+       NOSPIKERUNR2SPR,             // 300\r
+       NOSPIKERUNR3SPR,             // 301\r
+       NOSPIKERUNR4SPR,             // 302\r
+       NOSPIKERUNL1SPR,             // 303\r
+       NOSPIKERUNL2SPR,             // 304\r
+       NOSPIKERUNL3SPR,             // 305\r
+       NOSPIKERUNL4SPR,             // 306\r
+       NOSPIKEWALKR1SPR,            // 307\r
+       NOSPIKEWALKR2SPR,            // 308\r
+       NOSPIKEWALKR3SPR,            // 309\r
+       NOSPIKEWALKR4SPR,            // 310\r
+       NOSPIKEWALKL1SPR,            // 311\r
+       NOSPIKEWALKL2SPR,            // 312\r
+       NOSPIKEWALKL3SPR,            // 313\r
+       NOSPIKEWALKL4SPR,            // 314\r
+       NOSPIKESTUNSPR,           // 315\r
+       QUESTIONMARKSPR,             // 316\r
+       END_LUMP(NOSPIKE_LUMP_END, __NOSPIKEEND)\r
+\r
+       START_LUMP(FLECT_LUMP_START, __FLECTSTART)\r
+       FLECTSTANDSPR,               // 317\r
+       FLECTSTANDRSPR,              // 318\r
+       FLECTWALKR1SPR,              // 319\r
+       FLECTWALKR2SPR,              // 320\r
+       FLECTWALKR3SPR,              // 321\r
+       FLECTWALKR4SPR,              // 322\r
+       FLECTSTANDLSPR,              // 323\r
+       FLECTWALKL1SPR,              // 324\r
+       FLECTWALKL2SPR,              // 325\r
+       FLECTWALKL3SPR,              // 326\r
+       FLECTWALKL4SPR,              // 327\r
+       FLECTSTUNSPR,             // 328\r
+       END_LUMP(FLECT_LUMP_END, __FLECTEND)\r
+\r
+       START_LUMP(ORBATRIX_LUMP_START, __ORBATRIXSTART)\r
+       ORBATRIX1SPR,                // 329\r
+       ORBATRIX2SPR,                // 330\r
+       ORBATRIX3SPR,                // 331\r
+       ORBATRIX4SPR,                // 332\r
+       ORBATRIXL1SPR,               // 333\r
+       ORBATRIXL2SPR,               // 334\r
+       ORBATRIXR1SPR,               // 335\r
+       ORBATRIXR2SPR,               // 336\r
+       ORBATRIXSPIN1SPR,            // 337\r
+       ORBATRIXSPIN2SPR,            // 338\r
+       ORBATRIXSPIN3SPR,            // 339\r
+       ORBATRIXSPIN4SPR,            // 340\r
+       ORBATRIXCURLSPR,             // 341\r
+       END_LUMP(ORBATRIX_LUMP_END, __ORBATRIXEND)\r
+\r
+       START_LUMP(BLOOG_LUMP_START, __BLOOGSTART)\r
+       BLOOGWALKR1SPR,              // 342\r
+       BLOOGWALKR2SPR,              // 343\r
+       BLOOGWALKR3SPR,              // 344\r
+       BLOOGWALKR4SPR,              // 345\r
+       BLOOGWALKL1SPR,              // 346\r
+       BLOOGWALKL2SPR,              // 347\r
+       BLOOGWALKL3SPR,              // 348\r
+       BLOOGWALKL4SPR,              // 349\r
+       BLOOGSTUNSPR,             // 350\r
+       END_LUMP(BLOOG_LUMP_END, __BLOOGEND)\r
+\r
+       START_LUMP(RBLOOGLET_LUMP_START, __RBLOOGLETSTART)\r
+       RBLOOGLETWALKR1SPR,          // 351\r
+       RBLOOGLETWALKR2SPR,          // 352\r
+       RBLOOGLETWALKR3SPR,          // 353\r
+       RBLOOGLETWALKR4SPR,          // 354\r
+       RBLOOGLETWALKL1SPR,          // 355\r
+       RBLOOGLETWALKL2SPR,          // 356\r
+       RBLOOGLETWALKL3SPR,          // 357\r
+       RBLOOGLETWALKL4SPR,          // 358\r
+       RBLOOGLETSTUNSPR,         // 359\r
+       END_LUMP(RBLOOGLET_LUMP_END, __RBLOOGLETEND)\r
+\r
+       START_LUMP(YBLOOGLET_LUMP_START, __YBLOOGLETSTART)\r
+       YBLOOGLETWALKR1SPR,          // 360\r
+       YBLOOGLETWALKR2SPR,          // 361\r
+       YBLOOGLETWALKR3SPR,          // 362\r
+       YBLOOGLETWALKR4SPR,          // 363\r
+       YBLOOGLETWALKL1SPR,          // 364\r
+       YBLOOGLETWALKL2SPR,          // 365\r
+       YBLOOGLETWALKL3SPR,          // 366\r
+       YBLOOGLETWALKL4SPR,          // 367\r
+       YBLOOGLETSTUNSPR,         // 368\r
+       END_LUMP(YBLOOGLET_LUMP_END, __YBLOOGLETEND)\r
+\r
+       START_LUMP(BBLOOGLET_LUMP_START, __BBLOOGLETSTART)\r
+       BBLOOGLETWALKR1SPR,          // 369\r
+       BBLOOGLETWALKR2SPR,          // 370\r
+       BBLOOGLETWALKR3SPR,          // 371\r
+       BBLOOGLETWALKR4SPR,          // 372\r
+       BBLOOGLETWALKL1SPR,          // 373\r
+       BBLOOGLETWALKL2SPR,          // 374\r
+       BBLOOGLETWALKL3SPR,          // 375\r
+       BBLOOGLETWALKL4SPR,          // 376\r
+       BBLOOGLETSTUNSPR,         // 377\r
+       END_LUMP(BBLOOGLET_LUMP_END, __BBLOOGLETEND)\r
+\r
+       START_LUMP(GBLOOGLET_LUMP_START, __GBLOOGLETSTART)\r
+       GBLOOGLETWALKR1SPR,          // 378\r
+       GBLOOGLETWALKR2SPR,          // 379\r
+       GBLOOGLETWALKR3SPR,          // 380\r
+       GBLOOGLETWALKR4SPR,          // 381\r
+       GBLOOGLETWALKL1SPR,          // 382\r
+       GBLOOGLETWALKL2SPR,          // 383\r
+       GBLOOGLETWALKL3SPR,          // 384\r
+       GBLOOGLETWALKL4SPR,          // 385\r
+       GBLOOGLETSTUNSPR,         // 386\r
+       END_LUMP(GBLOOGLET_LUMP_END, __GBLOOGLETEND)\r
+\r
+       START_LUMP(GIK_LUMP_START, __GIKSTART)\r
+       GIKWALKR1SPR,                // 387\r
+       GIKWALKR2SPR,                // 388\r
+       GIKWALKR3SPR,                // 389\r
+       GIKWALKL1SPR,                // 390\r
+       GIKWALKL2SPR,                // 391\r
+       GIKWALKL3SPR,                // 392\r
+       GIKJUMPLSPR,                 // 393\r
+       GIKJUMPRSPR,                 // 394\r
+       GIKSLIDER1SPR,               // 395\r
+       GIKSLIDER2SPR,               // 396\r
+       GIKSLIDEL1SPR,               // 397\r
+       GIKSLIDEL2SPR,               // 398\r
+       END_LUMP(GIK_LUMP_END, __GIKEND)\r
+\r
+       START_LUMP(BLORB_LUMP_START, __BLORBSTART)\r
+       BLORB1SPR,                   // 399\r
+       BLORB2SPR,                   // 400\r
+       BLORB3SPR,                   // 401\r
+       END_LUMP(BLORB_LUMP_END, __BLORBEND)\r
+\r
+       START_LUMP(BOBBA_LUMP_START, __BOBBASTART)\r
+       BOBBAL1SPR,                  // 402\r
+       BOBBAL2SPR,                  // 403\r
+       BOBBAL3SPR,                  // 404\r
+       BOBBAR1SPR,                  // 405\r
+       BOBBAR2SPR,                  // 406\r
+       BOBBAR3SPR,                  // 407\r
+       BOBBASHOT1SPR,               // 408\r
+       BOBBASHOT2SPR,               // 409\r
+       BOBBASHOT3SPR,               // 410\r
+       BOBBASHOT4SPR,               // 411\r
+       BOBBASHOT5SPR,               // 412\r
+       BOBBASHOT6SPR,               // 413\r
+       END_LUMP(BOBBA_LUMP_END, __BOBBAEND)\r
+\r
+       START_LUMP(BIP_LUMP_START, __BIPSTART)\r
+       BIPSTANDSPR,                 // 414\r
+       BIPWALKR1SPR,                // 415\r
+       BIPWALKR2SPR,                // 416\r
+       BIPWALKR3SPR,                // 417\r
+       BIPWALKR4SPR,                // 418\r
+       BIPWALKL1SPR,                // 419\r
+       BIPWALKL2SPR,                // 420\r
+       BIPWALKL3SPR,                // 421\r
+       BIPWALKL4SPR,                // 422\r
+       END_LUMP(BIP_LUMP_END, __BIPEND)\r
+\r
+       START_LUMP(BIPSQUISHED_LUMP_START, __BIPSQUISHEDSTART)\r
+       BIPSQUISHEDSPR,              // 423\r
+       END_LUMP(BIPSQUISHED_LUMP_END, __BIPSQUISHEDEND)\r
+\r
+       START_LUMP(PLATFORM_LUMP_START, __PLATFORMSTART)\r
+       PLATFORMSPR,                 // 424\r
+       PLATBIP1SPR,                 // 425\r
+       PLATBIP2SPR,                 // 426\r
+       PLATBIP3SPR,                 // 427\r
+       PLATBIP4SPR,                 // 428\r
+       PLATBIP5SPR,                 // 429\r
+       PLATBIP6SPR,                 // 430\r
+       PLATBIP7SPR,                 // 431\r
+       PLATBIP8SPR,                 // 432\r
+       END_LUMP(PLATFORM_LUMP_END, __PLATFORMEND)\r
+\r
+       START_LUMP(MOLLY_LUMP_START, __MOLLYSTART)\r
+       MOLLY1SPR,                   // 433\r
+       MOLLY2SPR,                   // 434\r
+       END_LUMP(MOLLY_LUMP_END, __MOLLYEND)\r
+\r
+       START_LUMP(PASSCARD_LUMP_START, __PASSCARDSTART)\r
+       PASSCARDSPR,                 // 435\r
+       END_LUMP(PASSCARD_LUMP_END, __PASSCARDEND)\r
+\r
+       //\r
+       // TILES (these don't need names)\r
+       //\r
+\r
+       LASTTILE=STARTEXTERNS-1,\r
+\r
+       //\r
+       // EXTERNS\r
+       //\r
+\r
+       T_ENDART,                    // 5550\r
+\r
+       ORDERSCREEN,                 // 5551\r
+       OUTOFMEM,                    // 5554\r
+\r
+       //demos\r
+       DEMO0,                       // 5555\r
+       DEMO1,                       // 5556\r
+       DEMO2,                       // 5557\r
+       DEMO3,                       // 5558\r
+       DEMO4,                       // 5559\r
+\r
+       NUMGRCHUNKS\r
+} graphicnums;\r
+\r
+#undef START_LUMP\r
+#undef END_LUMP\r
+\r
+#endif //__GFX_H__
\ No newline at end of file
diff --git a/16/keen456/KEEN4-6/KEEN6C/ID_ASM.EQU b/16/keen456/KEEN4-6/KEEN6C/ID_ASM.EQU
new file mode 100755 (executable)
index 0000000..d27d272
--- /dev/null
@@ -0,0 +1,115 @@
+;\r
+; Equates for all .ASM files\r
+;\r
+\r
+;----------------------------------------------------------------------------\r
+\r
+INCLUDE        "GFXC_CK6.EQU"\r
+\r
+;----------------------------------------------------------------------------\r
+\r
+CGAGR          =       1\r
+EGAGR          =       2\r
+VGAGR          =       3\r
+\r
+GRMODE         =       CGAGR\r
+PROFILE                =       0                       ; 1=keep stats on tile drawing\r
+\r
+SC_INDEX       =       03C4h\r
+SC_RESET       =       0\r
+SC_CLOCK       =       1\r
+SC_MAPMASK     =       2\r
+SC_CHARMAP     =       3\r
+SC_MEMMODE     =       4\r
+\r
+CRTC_INDEX     =       03D4h\r
+CRTC_H_TOTAL   =       0\r
+CRTC_H_DISPEND =       1\r
+CRTC_H_BLANK   =       2\r
+CRTC_H_ENDBLANK        =       3\r
+CRTC_H_RETRACE =       4\r
+CRTC_H_ENDRETRACE =    5\r
+CRTC_V_TOTAL   =       6\r
+CRTC_OVERFLOW  =       7\r
+CRTC_ROWSCAN   =       8\r
+CRTC_MAXSCANLINE =     9\r
+CRTC_CURSORSTART =     10\r
+CRTC_CURSOREND =       11\r
+CRTC_STARTHIGH =       12\r
+CRTC_STARTLOW  =       13\r
+CRTC_CURSORHIGH        =       14\r
+CRTC_CURSORLOW =       15\r
+CRTC_V_RETRACE =       16\r
+CRTC_V_ENDRETRACE =    17\r
+CRTC_V_DISPEND =       18\r
+CRTC_OFFSET    =       19\r
+CRTC_UNDERLINE =       20\r
+CRTC_V_BLANK   =       21\r
+CRTC_V_ENDBLANK        =       22\r
+CRTC_MODE      =       23\r
+CRTC_LINECOMPARE =     24\r
+\r
+\r
+GC_INDEX       =       03CEh\r
+GC_SETRESET    =       0\r
+GC_ENABLESETRESET =    1\r
+GC_COLORCOMPARE        =       2\r
+GC_DATAROTATE  =       3\r
+GC_READMAP     =       4\r
+GC_MODE                =       5\r
+GC_MISCELLANEOUS =     6\r
+GC_COLORDONTCARE =     7\r
+GC_BITMASK     =       8\r
+\r
+ATR_INDEX      =       03c0h\r
+ATR_MODE       =       16\r
+ATR_OVERSCAN   =       17\r
+ATR_COLORPLANEENABLE = 18\r
+ATR_PELPAN     =       19\r
+ATR_COLORSELECT        =       20\r
+\r
+STATUS_REGISTER_1     =        03dah\r
+\r
+\r
+MACRO  WORDOUT\r
+       out     dx,ax\r
+ENDM\r
+\r
+if 0\r
+\r
+MACRO  WORDOUT\r
+       out     dx,al\r
+       inc     dx\r
+       xchg    al,ah\r
+       out     dx,al\r
+       dec     dx\r
+       xchg    al,ah\r
+ENDM\r
+\r
+endif\r
+\r
+UPDATEWIDE     =       22\r
+UPDATEHIGH     =       14\r
+\r
+;\r
+; tile info offsets from segment tinf\r
+;\r
+\r
+ANIM           =       402\r
+SPEED          =       (ANIM+NUMTILE16)\r
+\r
+NORTHWALL      =       (SPEED+NUMTILE16)\r
+EASTWALL       =       (NORTHWALL+NUMTILE16M)\r
+SOUTHWALL   =  (EASTWALL+NUMTILE16M)\r
+WESTWALL    =  (SOUTHWALL+NUMTILE16M)\r
+MANIM       =  (WESTWALL+NUMTILE16M)\r
+INTILE      =  (MANIM+NUMTILE16M)\r
+MSPEED      =  (INTILE+NUMTILE16M)\r
+\r
+\r
+IFE GRMODE-EGAGR\r
+SCREENWIDTH    =       64\r
+ENDIF\r
+IFE GRMODE-CGAGR\r
+SCREENWIDTH    =       128\r
+ENDIF\r
diff --git a/16/keen456/KEEN4-6/KEEN6C/ID_HEADS.H b/16/keen456/KEEN4-6/KEEN6C/ID_HEADS.H
new file mode 100755 (executable)
index 0000000..d019e5b
--- /dev/null
@@ -0,0 +1,109 @@
+/* Reconstructed Commander Keen 4-6 Source Code\r
+ * Copyright (C) 2021 K1n9_Duk3\r
+ *\r
+ * This file is primarily based on:\r
+ * Catacomb 3-D Source Code\r
+ * Copyright (C) 1993-2014 Flat Rock Software\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License along\r
+ * with this program; if not, write to the Free Software Foundation, Inc.,\r
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+ */\r
+\r
+// ID_GLOB.H\r
+\r
+\r
+#include <ALLOC.H>\r
+#include <CTYPE.H>\r
+#include <DOS.H>\r
+#include <ERRNO.H>\r
+#include <FCNTL.H>\r
+#include <IO.H>\r
+#include <MEM.H>\r
+#include <PROCESS.H>\r
+#include <STDIO.H>\r
+#include <STDLIB.H>\r
+#include <STRING.H>\r
+#include <SYS\STAT.H>\r
+\r
+#define __ID_GLOB__\r
+\r
+//--------------------------------------------------------------------------\r
+\r
+#define KEEN\r
+#define KEEN6\r
+\r
+#define        EXTENSION       "CK6"\r
+\r
+extern char far introscn;\r
+\r
+#include "GFXC_CK6.H"\r
+#include "AUDIOCK6.H"\r
+\r
+//--------------------------------------------------------------------------\r
+\r
+#define        TEXTGR  0\r
+#define        CGAGR   1\r
+#define        EGAGR   2\r
+#define        VGAGR   3\r
+\r
+#define GRMODE CGAGR\r
+\r
+#if GRMODE == EGAGR\r
+#define GREXT  "EGA"\r
+#endif\r
+#if GRMODE == CGAGR\r
+#define GREXT  "CGA"\r
+#endif\r
+\r
+//#define PROFILE\r
+\r
+//\r
+//     ID Engine\r
+//     Types.h - Generic types, #defines, etc.\r
+//     v1.0d1\r
+//\r
+\r
+#ifndef        __TYPES__\r
+#define        __TYPES__\r
+\r
+typedef        enum    {false,true}    boolean;\r
+typedef        unsigned        char            byte;\r
+typedef        unsigned        int                     word;\r
+typedef        unsigned        long            longword;\r
+typedef        byte *                                  Ptr;\r
+\r
+typedef        struct\r
+               {\r
+                       int     x,y;\r
+               } Point;\r
+typedef        struct\r
+               {\r
+                       Point   ul,lr;\r
+               } Rect;\r
+\r
+#define        nil     ((void *)0)\r
+\r
+#endif\r
+\r
+#include "ID_MM.H"\r
+#include "ID_CA.H"\r
+#include "ID_VW.H"\r
+#include "ID_RF.H"\r
+#include "ID_IN.H"\r
+#include "ID_SD.H"\r
+#include "ID_US.H"\r
+\r
+\r
+void   Quit (char *error);             // defined in user program\r
+\r
diff --git a/16/keen456/KEEN4-6/KEEN6C/OBJ/fix_RCK6C_v15.pat b/16/keen456/KEEN4-6/KEEN6C/OBJ/fix_RCK6C_v15.pat
new file mode 100755 (executable)
index 0000000..272ca2d
--- /dev/null
@@ -0,0 +1,3 @@
+%file RCK6C15.exe 102166\r
+%patch $1C 0 0 0 0\r
+%end
\ No newline at end of file
diff --git a/16/keen456/KEEN4-6/RCK4.DSK b/16/keen456/KEEN4-6/RCK4.DSK
new file mode 100755 (executable)
index 0000000..385701f
Binary files /dev/null and b/16/keen456/KEEN4-6/RCK4.DSK differ
diff --git a/16/keen456/KEEN4-6/RCK4.PRJ b/16/keen456/KEEN4-6/RCK4.PRJ
new file mode 100755 (executable)
index 0000000..0bad23e
Binary files /dev/null and b/16/keen456/KEEN4-6/RCK4.PRJ differ
diff --git a/16/keen456/KEEN4-6/RCK4C.DSK b/16/keen456/KEEN4-6/RCK4C.DSK
new file mode 100755 (executable)
index 0000000..885d88e
Binary files /dev/null and b/16/keen456/KEEN4-6/RCK4C.DSK differ
diff --git a/16/keen456/KEEN4-6/RCK4C.PRJ b/16/keen456/KEEN4-6/RCK4C.PRJ
new file mode 100755 (executable)
index 0000000..eb76ec7
Binary files /dev/null and b/16/keen456/KEEN4-6/RCK4C.PRJ differ
diff --git a/16/keen456/KEEN4-6/RCK4GT.DSK b/16/keen456/KEEN4-6/RCK4GT.DSK
new file mode 100755 (executable)
index 0000000..5832d5f
Binary files /dev/null and b/16/keen456/KEEN4-6/RCK4GT.DSK differ
diff --git a/16/keen456/KEEN4-6/RCK4GT.PRJ b/16/keen456/KEEN4-6/RCK4GT.PRJ
new file mode 100755 (executable)
index 0000000..9e319fe
Binary files /dev/null and b/16/keen456/KEEN4-6/RCK4GT.PRJ differ
diff --git a/16/keen456/KEEN4-6/RCK5.DSK b/16/keen456/KEEN4-6/RCK5.DSK
new file mode 100755 (executable)
index 0000000..359c3fe
Binary files /dev/null and b/16/keen456/KEEN4-6/RCK5.DSK differ
diff --git a/16/keen456/KEEN4-6/RCK5.PRJ b/16/keen456/KEEN4-6/RCK5.PRJ
new file mode 100755 (executable)
index 0000000..e25c09b
Binary files /dev/null and b/16/keen456/KEEN4-6/RCK5.PRJ differ
diff --git a/16/keen456/KEEN4-6/RCK5C.DSK b/16/keen456/KEEN4-6/RCK5C.DSK
new file mode 100755 (executable)
index 0000000..3facc34
Binary files /dev/null and b/16/keen456/KEEN4-6/RCK5C.DSK differ
diff --git a/16/keen456/KEEN4-6/RCK5C.PRJ b/16/keen456/KEEN4-6/RCK5C.PRJ
new file mode 100755 (executable)
index 0000000..2750b1c
Binary files /dev/null and b/16/keen456/KEEN4-6/RCK5C.PRJ differ
diff --git a/16/keen456/KEEN4-6/RCK5GT.DSK b/16/keen456/KEEN4-6/RCK5GT.DSK
new file mode 100755 (executable)
index 0000000..15491b4
Binary files /dev/null and b/16/keen456/KEEN4-6/RCK5GT.DSK differ
diff --git a/16/keen456/KEEN4-6/RCK5GT.PRJ b/16/keen456/KEEN4-6/RCK5GT.PRJ
new file mode 100755 (executable)
index 0000000..9090a1c
Binary files /dev/null and b/16/keen456/KEEN4-6/RCK5GT.PRJ differ
diff --git a/16/keen456/KEEN4-6/RCK6.DSK b/16/keen456/KEEN4-6/RCK6.DSK
new file mode 100755 (executable)
index 0000000..10074f9
Binary files /dev/null and b/16/keen456/KEEN4-6/RCK6.DSK differ
diff --git a/16/keen456/KEEN4-6/RCK6.PRJ b/16/keen456/KEEN4-6/RCK6.PRJ
new file mode 100755 (executable)
index 0000000..089532f
Binary files /dev/null and b/16/keen456/KEEN4-6/RCK6.PRJ differ
diff --git a/16/keen456/KEEN4-6/RCK6C.DSK b/16/keen456/KEEN4-6/RCK6C.DSK
new file mode 100755 (executable)
index 0000000..5fa64a3
Binary files /dev/null and b/16/keen456/KEEN4-6/RCK6C.DSK differ
diff --git a/16/keen456/KEEN4-6/RCK6C.PRJ b/16/keen456/KEEN4-6/RCK6C.PRJ
new file mode 100755 (executable)
index 0000000..984a492
Binary files /dev/null and b/16/keen456/KEEN4-6/RCK6C.PRJ differ
diff --git a/16/keen456/KEEN4-6/RCK6C15.DSK b/16/keen456/KEEN4-6/RCK6C15.DSK
new file mode 100755 (executable)
index 0000000..dfa41f4
Binary files /dev/null and b/16/keen456/KEEN4-6/RCK6C15.DSK differ
diff --git a/16/keen456/KEEN4-6/RCK6C15.PRJ b/16/keen456/KEEN4-6/RCK6C15.PRJ
new file mode 100755 (executable)
index 0000000..a93509d
Binary files /dev/null and b/16/keen456/KEEN4-6/RCK6C15.PRJ differ
diff --git a/16/keen456/KEEN4-6/RCK6E15.DSK b/16/keen456/KEEN4-6/RCK6E15.DSK
new file mode 100755 (executable)
index 0000000..29dc676
Binary files /dev/null and b/16/keen456/KEEN4-6/RCK6E15.DSK differ
diff --git a/16/keen456/KEEN4-6/RCK6E15.PRJ b/16/keen456/KEEN4-6/RCK6E15.PRJ
new file mode 100755 (executable)
index 0000000..6c59aee
Binary files /dev/null and b/16/keen456/KEEN4-6/RCK6E15.PRJ differ
diff --git a/16/keen456/KEEN4-6/static/MAKEOBJ.EXE b/16/keen456/KEEN4-6/static/MAKEOBJ.EXE
new file mode 100755 (executable)
index 0000000..94fedaf
Binary files /dev/null and b/16/keen456/KEEN4-6/static/MAKEOBJ.EXE differ
diff --git a/16/keen456/KEEN4-6/static/make.bat b/16/keen456/KEEN4-6/static/make.bat
new file mode 100755 (executable)
index 0000000..2bc31c5
--- /dev/null
@@ -0,0 +1,30 @@
+@echo off\r
+rem bcc makeobj.c\r
+\r
+makeobj c AUDIODCT.CK4 ..\keen4\ck4adict.obj _audiodict\r
+makeobj f AUDIOHED.CK4 ..\keen4\ck4ahead.obj _AudioHeader _audiohead\r
+makeobj c EGADICT.CK4  ..\keen4\ck4edict.obj _EGAdict\r
+makeobj f EGAHEAD.CK4  ..\keen4\ck4ehead.obj EGA_grafixheader _EGAhead\r
+makeobj c CGADICT.CK4  ..\keen4\ck4cdict.obj _CGAdict\r
+makeobj f CGAHEAD.CK4  ..\keen4\ck4chead.obj CGA_grafixheader _CGAhead\r
+makeobj f MAPHEAD.CK4  ..\keen4\ck4mhead.obj MapHeader _maphead\r
+makeobj f introscn.CK4 ..\keen4\ck4intro.obj _introscn\r
+\r
+makeobj c AUDIODCT.CK5 ..\keen5\ck5adict.obj _audiodict\r
+makeobj f AUDIOHED.CK5 ..\keen5\ck5ahead.obj _AudioHeader _audiohead\r
+makeobj c EGADICT.CK5  ..\keen5\ck5edict.obj _EGAdict\r
+makeobj f EGAHEAD.CK5  ..\keen5\ck5ehead.obj EGA_grafixheader _EGAhead\r
+makeobj c CGADICT.CK5  ..\keen5\ck5cdict.obj _CGAdict\r
+makeobj f CGAHEAD.CK5  ..\keen5\ck5chead.obj CGA_grafixheader _CGAhead\r
+makeobj f MAPHEAD.CK5  ..\keen5\ck5mhead.obj MapHeader _maphead\r
+makeobj f introscn.CK5 ..\keen5\ck5intro.obj _introscn\r
+\r
+makeobj c AUDIODCT.CK6 ..\keen6\ck6adict.obj _audiodict\r
+makeobj f AUDIOHED.CK6 ..\keen6\ck6ahead.obj _AudioHeader _audiohead\r
+makeobj c EGADICT.CK6  ..\keen6\ck6edict.obj _EGAdict\r
+makeobj f EGAHEAD.CK6  ..\keen6\ck6ehead.obj EGA_grafixheader _EGAhead\r
+makeobj c CGADICT.CK6  ..\keen6\ck6cdict.obj _CGAdict\r
+makeobj f CGAHEAD.CK6  ..\keen6\ck6chead.obj CGA_grafixheader _CGAhead\r
+makeobj f MAPHEAD.CK6  ..\keen6\ck6mhead.obj MapHeader _maphead\r
+makeobj f introscn.CK6 ..\keen6\ck6intro.obj _introscn\r
+makeobj f orderscn.CK6 ..\keen6\ck6order.obj _orderscn\r
diff --git a/16/keen456/KEEN4-6/static/makeobj.c b/16/keen456/KEEN4-6/static/makeobj.c
new file mode 100755 (executable)
index 0000000..fe85a9d
--- /dev/null
@@ -0,0 +1,470 @@
+/*\r
+** makeobj.c\r
+**\r
+**---------------------------------------------------------------------------\r
+** Copyright 2014 Braden Obrzut\r
+** All rights reserved.\r
+**\r
+** Redistribution and use in source and binary forms, with or without\r
+** modification, are permitted provided that the following conditions\r
+** are met:\r
+**\r
+** 1. Redistributions of source code must retain the above copyright\r
+**    notice, this list of conditions and the following disclaimer.\r
+** 2. Redistributions in binary form must reproduce the above copyright\r
+**    notice, this list of conditions and the following disclaimer in the\r
+**    documentation and/or other materials provided with the distribution.\r
+** 3. The name of the author may not be used to endorse or promote products\r
+**    derived from this software without specific prior written permission.\r
+**\r
+** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR\r
+** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES\r
+** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.\r
+** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,\r
+** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT\r
+** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\r
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\r
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\r
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF\r
+** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\r
+**---------------------------------------------------------------------------\r
+**\r
+** This is a throwaway program to create OMF object files for DOS. It also\r
+** extracts the object files.  It should be compatible with MakeOBJ by John\r
+** Romero except where we calculate the checksum correctly.\r
+**\r
+*/\r
+\r
+#include <stdio.h>\r
+#include <malloc.h>\r
+#include <string.h>\r
+#include <ctype.h>\r
+#include <stdlib.h>\r
+\r
+#pragma pack(1)\r
+typedef struct\r
+{\r
+       unsigned char type;\r
+       unsigned short len;\r
+} SegHeader;\r
+\r
+typedef struct\r
+{\r
+       unsigned short len;\r
+       unsigned char name;\r
+       unsigned char classname;\r
+       unsigned char overlayname;\r
+} SegDef;\r
+#pragma pack()\r
+\r
+const char* ReadFile(const char* fn, int *size)\r
+{\r
+       char* out;\r
+\r
+       FILE* f = fopen(fn, "rb");\r
+       fseek(f, 0, SEEK_END);\r
+       *size = ftell(f);\r
+       out = (char*)malloc(*size);\r
+       fseek(f, 0, SEEK_SET);\r
+\r
+       fread(out, *size, 1, f);\r
+\r
+       fclose(f);\r
+\r
+       return out;\r
+}\r
+\r
+void WriteFile(const char* fn, const char *data, int size)\r
+{\r
+       FILE* f = fopen(fn, "wb");\r
+       fwrite(data, size, 1, f);\r
+       fclose(f);\r
+}\r
+\r
+void Extract(const char* infn)\r
+{\r
+       const char* in;\r
+       const char* start;\r
+       const char* p;\r
+       char outfn[16];\r
+       char str[256];\r
+       char *outdata;\r
+       int outsize;\r
+       int insize;\r
+       SegHeader head;\r
+\r
+       outdata = NULL;\r
+\r
+       start = in = ReadFile(infn, &insize);\r
+\r
+       while(in < start + insize)\r
+       {\r
+               head = *(SegHeader*)in;\r
+\r
+               switch(head.type)\r
+               {\r
+                       case 0x80: /* THEADR */\r
+                               memcpy(outfn, in+4, in[3]);\r
+                               outfn[in[3]] = 0;\r
+                               printf("Output: %s\n", outfn);\r
+                               {\r
+                                       int i;\r
+                                       for(i = 0;i < 16;++i)\r
+                                       {\r
+                                               if(outfn[i] == ' ')\r
+                                                       outfn[i] = 0;\r
+                                       }\r
+                               }\r
+                               break;\r
+                       case 0x88: /* COMENT */\r
+                               switch(in[3])\r
+                               {\r
+                                       case 0:\r
+                                               memcpy(str, in+5, head.len-2);\r
+                                               str[head.len-3] = 0;\r
+                                               printf("Comment: %s\n", str);\r
+                                               break;\r
+                                       default:\r
+                                               printf("Unknown comment type %X @ %x ignored.\n", (unsigned char)in[3], (unsigned int)(in - start));\r
+                                               break;\r
+                               }\r
+                               break;\r
+                       case 0x96: /* LNAMES */\r
+                               p = in+3;\r
+                               while(p < in+head.len+2)\r
+                               {\r
+                                       memcpy(str, p+1, (unsigned char)*p);\r
+                                       str[(unsigned char)*p] = 0;\r
+                                       printf("Name: %s\n", str);\r
+\r
+                                       p += (unsigned char)*p+1;\r
+                               }\r
+                               break;\r
+                       case 0x98: /* SEGDEF */\r
+                       {\r
+                               SegDef *sd;\r
+\r
+                               sd = *(in+3) ? (SegDef*)(in+4) : (SegDef*)(in+7);\r
+                               printf("Segment Length: %d\n", sd->len);\r
+\r
+                               outdata = (char*)malloc(sd->len);\r
+                               outsize = sd->len;\r
+                               break;\r
+                       }\r
+                       case 0x90: /* PUBDEF */\r
+                               p = in+5;\r
+                               if(in[5] == 0)\r
+                                       p += 2;\r
+                               while(p < in+head.len+2)\r
+                               {\r
+                                       memcpy(str, p+1, (unsigned char)*p);\r
+                                       str[(unsigned char)*p] = 0;\r
+                                       printf("Public Name: %s\n", str);\r
+\r
+                                       p += (unsigned char)*p+4;\r
+                               }\r
+                               break;\r
+                       case 0xA0: /* LEDATA */\r
+                               printf("Writing data at %d (%d)\n", *(unsigned short*)(in+4), head.len-4);\r
+                               memcpy(outdata+*(unsigned short*)(in+4), in+6, head.len-4);\r
+                               break;\r
+                       case 0x8A: /* MODEND */\r
+                               /* Ignore */\r
+                               break;\r
+                       default:\r
+                               printf("Unknown header type %X @ %x ignored.\n", head.type, (unsigned int)(in - start));\r
+                               break;\r
+               }\r
+\r
+               in += 3 + head.len;\r
+       }\r
+\r
+       WriteFile(outfn, outdata, outsize);\r
+\r
+       free((char*)start);\r
+       free(outdata);\r
+}\r
+\r
+void CheckSum(char *s, unsigned short len)\r
+{\r
+       int sum;\r
+\r
+       len += 3;\r
+\r
+       sum = 0;\r
+       while(len > 1)\r
+       {\r
+               sum += *(unsigned char*)s;\r
+               ++s;\r
+               --len;\r
+       }\r
+       *s = (unsigned char)(0x100-(sum&0xFF));\r
+}\r
+\r
+void MakeDataObj(const char* infn, const char* outfn, const char* segname, const char* symname, int altmode)\r
+{\r
+#define Flush() fwrite(d.buf, d.head.len+3, 1, f)\r
+       union\r
+       {\r
+               char buf[4096];\r
+               SegHeader head;\r
+       } d;\r
+       int i;\r
+       FILE *f;\r
+       int insize;\r
+       const char *in;\r
+       const char *infn_stripped = strrchr(infn, '/');\r
+       if(strrchr(infn, '\\') > infn_stripped)\r
+               infn_stripped = strrchr(infn, '\\');\r
+       if(infn_stripped == NULL)\r
+               infn_stripped = infn;\r
+       else\r
+               ++infn_stripped;\r
+\r
+       f = fopen(outfn, "wb");\r
+\r
+       in = ReadFile(infn, &insize);\r
+\r
+       d.head.type = 0x80;\r
+       d.head.len = 14;\r
+       d.buf[3] = 12;\r
+       if(d.buf[3] > 12)\r
+               d.buf[3] = 12;\r
+       sprintf(&d.buf[4], "%-12s", infn_stripped);\r
+       for(i = 0;i < strlen(infn_stripped) && i < 12;++i)\r
+               d.buf[4+i] = toupper(d.buf[4+i]);\r
+       /* CheckSum(d.buf, d.head.len); */\r
+       d.buf[17] = 0; /* For some reason this one isn't checksummed by MakeOBJ */\r
+       Flush();\r
+\r
+       d.head.type = 0x88;\r
+       d.head.len = 15;\r
+       d.buf[3] = 0;\r
+       d.buf[4] = 0;\r
+       /* We're not really MakeOBJ v1.1, but to allow us to verify with md5sums */\r
+       memcpy(&d.buf[5], "MakeOBJ v1.1", 12);\r
+       CheckSum(d.buf, d.head.len);\r
+       Flush();\r
+\r
+       d.head.type = 0x96;\r
+       d.head.len = strlen(infn_stripped)+40;\r
+       d.buf[3] = 6;\r
+       memcpy(&d.buf[4], "DGROUP", 6);\r
+       d.buf[10] = 5;\r
+       memcpy(&d.buf[11], "_DATA", 5);\r
+       d.buf[16] = 4;\r
+       memcpy(&d.buf[17], "DATA", 4);\r
+       d.buf[21] = 0;\r
+       d.buf[22] = 5;\r
+       memcpy(&d.buf[23], "_TEXT", 5);\r
+       d.buf[28] = 4;\r
+       memcpy(&d.buf[29], "CODE", 4);\r
+       d.buf[33] = 8;\r
+       memcpy(&d.buf[34], "FAR_DATA", 8);\r
+       if(!segname)\r
+       {\r
+               if(!altmode)\r
+               {\r
+                       d.buf[42] = strlen(infn_stripped)-1;\r
+                       for(i = 0;i < strlen(infn_stripped)-4;++i)\r
+                       {\r
+                               if(i == 0)\r
+                                       d.buf[43] = toupper(infn_stripped[0]);\r
+                               else\r
+                                       d.buf[43+i] = tolower(infn_stripped[i]);\r
+                       }\r
+                       memcpy(&d.buf[43+i], "Seg", 3);\r
+               }\r
+               else\r
+               {\r
+                       d.head.len = 40;\r
+               }\r
+       }\r
+       else\r
+       {\r
+               d.head.len = strlen(segname)+41;\r
+               d.buf[42] = strlen(segname);\r
+               strcpy(&d.buf[43], segname);\r
+       }\r
+       CheckSum(d.buf, d.head.len);\r
+       Flush();\r
+\r
+       d.head.type = 0x98;\r
+       d.head.len = 7;\r
+       *(unsigned short*)(d.buf+4) = insize;\r
+       if(altmode == 0)\r
+       {\r
+               d.buf[3] = (char)((unsigned char)0x60);\r
+               d.buf[6] = 8;\r
+               d.buf[7] = 7;\r
+               d.buf[8] = 4;\r
+       }\r
+       else\r
+       {\r
+               d.buf[3] = (char)((unsigned char)0x48);\r
+               d.buf[6] = 2;\r
+               d.buf[7] = 3;\r
+               d.buf[8] = 4;\r
+       }\r
+       CheckSum(d.buf, d.head.len);\r
+       Flush();\r
+\r
+       if(altmode)\r
+       {\r
+               d.head.type = 0x9A;\r
+               d.head.len = 4;\r
+               d.buf[3] = 1;\r
+               d.buf[4] = (char)((unsigned char)0xFF);\r
+               d.buf[5] = 1;\r
+               CheckSum(d.buf, d.head.len);\r
+               Flush();\r
+       }\r
+\r
+       d.head.type = 0x90;\r
+       d.head.len = strlen(infn_stripped)+4;\r
+       d.buf[3] = 1;\r
+       d.buf[4] = 1;\r
+       if(!symname)\r
+       {\r
+               d.buf[5] = strlen(infn_stripped)-3;\r
+               d.buf[6] = '_';\r
+               for(i = 0;i < strlen(infn_stripped)-4;++i)\r
+                       d.buf[7+i] = tolower(infn_stripped[i]);\r
+       }\r
+       else\r
+       {\r
+               d.head.len = strlen(symname)+7;\r
+               d.buf[5] = strlen(symname);\r
+               strcpy(&d.buf[6], symname);\r
+               i = strlen(symname)-1;\r
+       }\r
+       d.buf[7+i] = 0;\r
+       d.buf[8+i] = 0;\r
+       d.buf[9+i] = 0;\r
+       /* This checksum is calculated wrong in MakeOBJ, although I don't know in what way. */\r
+       CheckSum(d.buf, d.head.len);\r
+       Flush();\r
+\r
+#define LEDATA_LEN 1024\r
+       for(i = 0;i < insize;i += LEDATA_LEN)\r
+       {\r
+               d.head.type = 0xA0;\r
+               d.head.len = insize - i > LEDATA_LEN ? LEDATA_LEN+4 : insize - i + 4;\r
+               d.buf[3] = 1;\r
+               *(unsigned short*)(d.buf+4) = i;\r
+               memcpy(&d.buf[6], &in[i], d.head.len-4);\r
+               CheckSum(d.buf, d.head.len);\r
+               Flush();\r
+       }\r
+\r
+       d.head.type = 0x8A;\r
+       d.head.len = 2;\r
+       d.buf[3] = 0;\r
+       d.buf[4] = 0;\r
+       CheckSum(d.buf, d.head.len);\r
+       Flush();\r
+\r
+       fclose(f);\r
+       free((char*)in);\r
+}\r
+\r
+void DumpData(const char* infn, const char* outfn, int skip)\r
+{\r
+       FILE *f;\r
+       int i;\r
+       int insize;\r
+       char symname[9];\r
+       const char *in;\r
+       const char *infn_stripped = strrchr(infn, '/');\r
+       if(strrchr(infn, '\\') > infn_stripped)\r
+               infn_stripped = strrchr(infn, '\\');\r
+       if(infn_stripped == NULL)\r
+               infn_stripped = infn;\r
+       else\r
+               ++infn_stripped;\r
+\r
+       f = fopen(outfn, "wb");\r
+\r
+       memset(symname, 0, 9);\r
+       memcpy(symname, infn_stripped, strlen(infn_stripped)-4);\r
+       fprintf(f, "char far %s[] ={\r\n", symname);\r
+\r
+       in = ReadFile(infn, &insize);\r
+\r
+       for(i = skip;i < insize;++i)\r
+       {\r
+               fprintf(f, "%d", (unsigned char)in[i]);\r
+               if(i != insize-1)\r
+                       fprintf(f, ",\r\n");\r
+       }\r
+       fprintf(f, " };\r\n");\r
+\r
+       fclose(f);\r
+       free((char*)in);\r
+}\r
+\r
+int main(int argc, char* argv[])\r
+{\r
+       if(argc < 3)\r
+       {\r
+               printf("Converts file to OMF.\nUseage:\n  ./makeobj [fx] <input> ...\n");\r
+               return 0;\r
+       }\r
+\r
+       switch(argv[1][0])\r
+       {\r
+               case 'c':\r
+                       if(argc < 4)\r
+                       {\r
+                               printf("Need an output location. (Extra parms: <output> [<symbol>])\n");\r
+                               return 0;\r
+                       }\r
+                       else\r
+                       {\r
+                               const char *symname = NULL;\r
+                               if(argc >= 5)\r
+                                       symname = argv[4];\r
+                               MakeDataObj(argv[2], argv[3], NULL, symname, 1);\r
+                       }\r
+                       break;\r
+               default:\r
+               case 'f':\r
+                       if(argc < 4)\r
+                       {\r
+                               printf("Need an output location. (Extra parms: <output> [<segname> <symbol>])\n");\r
+                               return 0;\r
+                       }\r
+                       else\r
+                       {\r
+                               const char *segname = NULL, *symname = NULL;\r
+                               if(argc >= 6)\r
+                               {\r
+                                       segname = argv[4];\r
+                                       symname = argv[5];\r
+                               }\r
+                               MakeDataObj(argv[2], argv[3], segname, symname, 0);\r
+                       }\r
+                       break;\r
+               case 'x':\r
+                       Extract(argv[2]);\r
+                       break;\r
+               case 's':\r
+                       if(argc < 4)\r
+                       {\r
+                               printf("Need an output location. (Extra parms: <output> [<skip>])\n");\r
+                               return 0;\r
+                       }\r
+                       else\r
+                       {\r
+                               int skip = 0;\r
+                               if(argc >= 5)\r
+                               {\r
+                                       skip = atoi(argv[4]);\r
+                               }\r
+                               DumpData(argv[2], argv[3], skip);\r
+                       }\r
+                       break;\r
+                       break;\r
+       }\r
+       return 0;\r
+}\r
diff --git a/16/keen456/KEEN4-6/static/ripck4.pat b/16/keen456/KEEN4-6/static/ripck4.pat
new file mode 100755 (executable)
index 0000000..9e2b675
--- /dev/null
@@ -0,0 +1,44 @@
+%ext ck4\r
+%version 1.4\r
+\r
+%dump audiodct.ck4 $354F6 1024\r
+%dump audiohed.ck4 $20DF0 652\r
+\r
+%dump egadict.ck4 $358F6 1024\r
+%dump egahead.ck4 $21080 14256\r
+\r
+%dump introscn.ck4 $1FE40 4008\r
+\r
+%dump maphead.ck4 $24830 23406\r
+\r
+\r
+%version cga-1.4\r
+\r
+%dump audiodct.ck4 $3335C 1024\r
+%dump audiohed.ck4 $1E7A0 652\r
+\r
+%dump cgadict.ck4 $3375C 1024\r
+%dump cgahead.ck4 $1EA30 14256\r
+\r
+%dump introscn.ck4 $1D7F0 4008\r
+\r
+%dump maphead.ck4 $221E0 23406\r
+\r
+\r
+%version 1.4gt # Note: not supported by CK4PATCH v0.11.3\r
+\r
+%dump audiodct.ck4 $359D6 1024\r
+%dump audiohed.ck4 $212D0 652\r
+\r
+%dump egadict.ck4 $35DD6 1024\r
+%dump egahead.ck4 $21560 14256\r
+\r
+%dump introscn.ck4 $20320 4008\r
+\r
+%dump maphead.ck4 $24D10 23406\r
+\r
+\r
+%version all\r
+\r
+%abort\r
+%end
\ No newline at end of file
diff --git a/16/keen456/KEEN4-6/static/ripck5.pat b/16/keen456/KEEN4-6/static/ripck5.pat
new file mode 100755 (executable)
index 0000000..cac264c
--- /dev/null
@@ -0,0 +1,44 @@
+%ext ck5\r
+%version 1.4\r
+\r
+%dump audiodct.ck5 $35EC4 1024\r
+%dump audiohed.ck5 $21C80 828\r
+\r
+%dump egadict.ck5 $362C4 1024\r
+%dump egahead.ck5 $21FC0 14796\r
+\r
+%dump introscn.ck5 $20CD0 4008\r
+\r
+%dump maphead.ck5 $25990 24090\r
+\r
+\r
+%version cga-1.4\r
+\r
+%dump audiodct.ck5 $33B88 1024\r
+%dump audiohed.ck5 $1F4C0 828\r
+\r
+%dump cgadict.ck5 $33F88 1024\r
+%dump cgahead.ck5 $1F800 14790\r
+\r
+%dump introscn.ck5 $1E510 4008\r
+\r
+%dump maphead.ck5 $231D0 24090\r
+\r
+\r
+%version 1.4gt # Note: not supported by CK4PATCH v0.11.3\r
+\r
+%dump audiodct.ck5 $36424 1024\r
+%dump audiohed.ck5 $221E0 828\r
+\r
+%dump egadict.ck5 $36824 1024\r
+%dump egahead.ck5 $22520 14796\r
+\r
+%dump introscn.ck5 $21230 4008\r
+\r
+%dump maphead.ck5 $25EF0 24090\r
+\r
+\r
+%version all\r
+\r
+%abort\r
+%end
\ No newline at end of file
diff --git a/16/keen456/KEEN4-6/static/ripck6.pat b/16/keen456/KEEN4-6/static/ripck6.pat
new file mode 100755 (executable)
index 0000000..9d5b51d
--- /dev/null
@@ -0,0 +1,65 @@
+%ext ck6\r
+%version 1.4\r
+\r
+%dump audiodct.ck6 $36EEE 1024\r
+%dump audiohed.ck6 $20C50 760\r
+\r
+%dump egadict.ck6 $372EE 1024\r
+%dump egahead.ck6 $20F50 16683\r
+\r
+%dump introscn.ck6 $1FCA0 4008\r
+\r
+%dump maphead.ck6 $25080 24306\r
+\r
+%dump orderscn.ck6 $2AF80 4008\r
+\r
+\r
+%version 1.5\r
+\r
+%dump audiodct.ck6 $36B4E 1024\r
+%dump audiohed.ck6 $256B0 760\r
+\r
+%dump egadict.ck6 $36F4E 1024\r
+%dump egahead.ck6 $259B0 16683\r
+\r
+%dump introscn.ck6 $24700 4008\r
+\r
+%dump maphead.ck6 $29AE0 24306\r
+\r
+%dump orderscn.ck6 $2F9E0 4008\r
+\r
+\r
+%version cga-1.4\r
+\r
+%dump audiodct.ck6 $35030 1024\r
+%dump audiohed.ck6 $1E830 760\r
+\r
+%dump cgadict.ck6 $35430 1024\r
+%dump cgahead.ck6 $1EB30 16665\r
+\r
+%dump introscn.ck6 $1D880 4008\r
+\r
+%dump maphead.ck6 $22C50 24306\r
+\r
+%dump orderscn.ck6 $28B50 4008\r
+\r
+\r
+%version cga-1.5\r
+\r
+%dump audiodct.ck6 $34DA0 1024\r
+%dump audiohed.ck6 $1E5A0 760\r
+\r
+%dump cgadict.ck6 $351A0 1024\r
+%dump cgahead.ck6 $1E8A0 16665\r
+\r
+%dump introscn.ck6 $1D5F0 4008\r
+\r
+%dump maphead.ck6 $229C0 24306\r
+\r
+%dump orderscn.ck6 $288C0 4008\r
+\r
+\r
+%version all\r
+\r
+%abort\r
+%end
\ No newline at end of file
diff --git a/16/keen456/KEEN4-6/timeline.txt b/16/keen456/KEEN4-6/timeline.txt
new file mode 100755 (executable)
index 0000000..353fa4d
--- /dev/null
@@ -0,0 +1,352 @@
+KEEN 4 RECREATION TIMELINE:\r
+---------------------------\r
+\r
+2019-02-16:\r
+\r
+- started implementation\r
+- started & finished CK_MAIN.C (except for variables)\r
+- started & finished CK_DEMO.C (except for variables and fancy intro stuff)\r
+- started CK_GAME.C, CK_PLAY.C, CK_TEXT.C, CK_STATE.C\r
+\r
+\r
+2019-02-17:\r
+\r
+- finished CK_GAME.C (except for variables)\r
+- finished CK_PLAY.C (except for variables)\r
+- finished CK_TEXT.C (except for variables)\r
+- finished CK_STATE.C (except for variables)\r
+- started CK_KEEN.C\r
+\r
+\r
+2019-02-18:\r
+\r
+- finished CK_KEEN.C (except for variables)\r
+- started & finished CK_KEEN2.C (except for variables)\r
+- started & finished K4_LEVEL.C (except for variables)\r
+- started K4_ACT1.C, K4_ACT2.C\r
+\r
+\r
+2019-02-19:\r
+\r
+- finished K4_ACT1.C (except for variables)\r
+- finished K4_ACT2.C (except for variables)\r
+- started & finished K4_ACT3.C (except for variables)\r
+- added crawl text in K4_LEVEL.C\r
+- added states in K4_ACT1.C\r
+\r
+\r
+2019-02-20:\r
+\r
+- added states in K4_LEVEL.C\r
+- added states and initialized variables in CK_KEEN.C\r
+- added states and initialized variables in CK_KEEN2.C\r
+- added states in K4_ACT2.C\r
+- added states in K4_ACT3.C\r
+\r
+\r
+2019-02-22:\r
+\r
+- added ID Engine (taken from Catacomb 3D)\r
+- started creating header files to put it all together\r
+- fixed tons of typos...\r
+\r
+\r
+2019-02-23:\r
+\r
+- finished header files\r
+- fixed remaining typos & compiler errors\r
+- code compiles, but linking fails because of missing data that needs to be\r
+  included in EXE (AUDIOHED, AUDIODICT, EGAHEAD, EGADICT, MAPHEAD)\r
+- first builds - lots of memory(?) issues, quitting freezes DOSBox\r
+- fixed menu colors in ID_US_2.C\r
+- renamed "SKULL 'N' BONES" to "PADDLE WAR" (still need to fix speed issues in pong)\r
+- fixed error in GFXINFO (wrong number of tile16's) now help texts and quitting work\r
+- fixed memory issue by increasing number of memory block to 1200 (Cat3D used only 600)\r
+- first level loads now, but RF_Refresh() locks up/takes forever\r
+\r
+\r
+2019-02-27:\r
+\r
+- modified VW_ClearVideo to match Keen4 behavior (also adding in the bug)\r
+- modified USL_TearDownCtlPanel to use VW_ClearVideo instead of VW_Bar\r
+- fixed error in DrawCacheBox -> animation plays correctly now\r
+  (but RF_Refresh still locks up/takes forever)\r
+- fixed error in ID_CA.H (Keen4 uses tile info order SPEED, ANIM, NORTHWALL, ...)\r
+  -> RF_Refresh doesn't lock up anymore\r
+- fixed error in GameLoop -> can now enter sub-levels from world map\r
+- fixed error in KeenAirReact -> can now land\r
+- fixed bug in score box (used ammo number for lives)\r
+- status freezes the game\r
+- fixed signed/unsigned division bug in KeenStandThink\r
+- fixed spawn position for slugstain\r
+- fixed error in PlatformThink (platforms got stuck at the top/right end of the path)\r
+- added Keen4 Highscore defaults\r
+- fixed error in StatusWindow -> status window doesn't cause an endless loop anymore\r
+- fixed error in DrawStatus (bad positions for a few things)\r
+- fixed bug in CheckGrabPole -> can climb on poles now\r
+- fixed error in KeenAirReact -> grab edge with correct y position\r
+- fixed error in KeenClimbEdge1Think -> climbing left works correctly now\r
+- fixed bugs in SpawnFlag (darn typecasts...)\r
+- fixed error in ScrollScreenWorld\r
+- fixed error in KeenPoleTihink -> won't fall off when pressing left/right\r
+\r
+\r
+2019-02-28:\r
+\r
+- fixed lindsey floating bug (typecasts)\r
+- fixed mimrock walking\r
+- fixed arachnut contact\r
+- fixed riding the foot\r
+- fixed thundercloud not randomly turning towards player\r
+- fixed missing VW_UpdateScreen in LindseyDialog\r
+- fixed KeenAirReact\r
+- fixed EggContact\r
+- fixed controls for scuba keen\r
+\r
+\r
+2019-03-01:\r
+\r
+- fixed Sprite, Dopefish and Schoolfish\r
+- seems fully playable now...\r
+\r
+\r
+2019-03-16:\r
+\r
+- renamed PlayDemo to RunDemo, since that's the name that is used in the Keen 4 Demo\r
+- fixed Dopefish contact code (used to check 'ob' instead of 'hit' for a keenobj)\r
+\r
+\r
+2019-04-10:\r
+\r
+- fixed bug in PatchWorldMap (info layer value wasn't set to 0 for completed levels)\r
+\r
+\r
+2019-05-04:\r
+\r
+- replaced all sprite numbers with enum names\r
+- fixed error in WormouthLookLeftThink -> changes state to s_wormouth now\r
+\r
+\r
+2019-05-06:\r
+\r
+- replaced some more numbers with enum names\r
+\r
+\r
+2019-05-11:\r
+\r
+- fixed error in MergeTile16M\r
+\r
+\r
+2019-05-17:\r
+\r
+- fixed bug in WormouthThink (used PIXGLOBAL instead of TILEGLOBAL for xdist)\r
+\r
+\r
+2019-05-19:\r
+\r
+- actually moved all far strings into far memory\r
+\r
+\r
+2019-09-27:\r
+\r
+- fixed a minor error in PageLayout (skip anything <= space character, not just space char)\r
+\r
+\r
+2019-09-28:\r
+\r
+- fixed a bug in control panel (had to adjust indices because options menu was added back in)\r
+\r
+\r
+2019-11-26:\r
+\r
+- started implementation of Keen 5 stuff (K5_*.C)\r
+\r
+\r
+2019-11-28:\r
+\r
+- started implementation of Keen 6 stuff (K6_*.C)\r
+\r
+\r
+2019-11-30:\r
+\r
+- finished implementation of Keen 5 & 6 stuff\r
+\r
+\r
+2019-12-09:\r
+\r
+- reorganized KEEN4 project to add support for Keen 5 & 6 without too much redunancy\r
+- added code specific to Keen 5 and Keen 6 to the main codebase (CK_*.C)\r
+- excluded code specific to Keen 4 from the other builds (lots of #if & #ifdef)\r
+- Keen 4 still compiles fine, 5 & 6 still need some header files\r
+\r
+\r
+2019-12-10:\r
+\r
+- created header files for Keen 5 and Keen 6\r
+- all 3 projects compile, but still some bugfixing to do and features to add:\r
+  - refresh manager constants must be adjusted\r
+  - copy protection for Keen 6 isn't implemented\r
+  - sounds for animated tiles (Keen 6) aren't implemented\r
+- fixed a few bugs, first couple of levels in Keen 6 are playable now\r
+\r
+\r
+2019-12-11:\r
+\r
+- adjusted refresh manager constants (now Keen 5 levels are playable, too)\r
+- adjusted position of the "PRESS A KEY" graphic in the status window \r
+  (position in Keen 4 differs from 5 & 6 because of the wetsuit box)\r
+- added RF_MaskBlockWhite and a few other things\r
+- studied Terminator-related code in depth, trying to understand it all and\r
+  find names for all those variables\r
+\r
+\r
+2019-12-12:\r
+\r
+- finally found and fixed a bug with the turret shots in Keen 5\r
+- fixed wrong pole shooting sprites (left and right sprites were swapped)\r
+- fixed demo-breaking bugs in Spindred and RoboRed (Keen 5)\r
+- fixed worldmap elevator door code (Keen 5)\r
+- fixed worldmap rope and rocket bugs (Keen 6)\r
+- still a demo-breaking bug somewhere (Nospike / Bloogdome, Keen 6 obviously)\r
+- fixed pole check for shikadi pole sparks\r
+\r
+\r
+2019-12-15:\r
+\r
+- FUCLING FINALLY found and fixed the demo-breaking bug in Keen 6\r
+  (was in NospikeRunThink)\r
+- fixed bug in Shikadi Master spawn (y offset used tile units instead of pixels)\r
+- started implementation of the Terminator intro and the Star Wars text crawl\r
+\r
+\r
+2019-12-16:\r
+\r
+- finished implementation of the Terminator and Star Wars stuff, but now the\r
+  compiler crashes...\r
+- found and fixed the issue that crashed the compiler\r
+- fixed all bugs in the Staw Wars text crawl\r
+- fixed all bugs in the Terminator intro\r
+\r
+2019-12-17:\r
+\r
+- added tile animation sounds for Keen 6\r
+\r
+\r
+2019-12-19:\r
+\r
+- implemented Keen 4-6 version of VW_SetScreen (in _ID_VW_AE.ASM) and my\r
+  customized version of the routine (in ID_VW_AE.ASM). swap the files if you\r
+  want to use the original code\r
+- added support for the NOPAN parameter\r
+- added US_CheckArg in ID_US_1.C\r
+- added Quiet AdLib mode\r
+- added Gravis Gamepad support\r
+\r
+\r
+2019-12-20:\r
+\r
+- fixed CheckHighScore bug (each episode uses a different map number)\r
+- fixed shockshund bugs (blast direction, stun jump)\r
+\r
+\r
+2019-12-21:\r
+\r
+- fixed EagleWalkThink (condition for starting to fly, Keen 4 obviously)\r
+- fixed tile-based item stealing for TreasureEater (Keen 4)\r
+- fixed force field toggling (Keen 6)\r
+\r
+\r
+2019-12-22:\r
+\r
+- fixed bug in EagleFlyReact (Keen 4)\r
+- changed ConfigVersion in ID_US_1.C for full compatibility with version 1.4\r
+  (saved games are still incompatible, though)\r
+\r
+\r
+2020-01-05:\r
+\r
+- modified PaddleWar code to match the code from Keen 4-6\r
+\r
+\r
+2020-01-06:\r
+\r
+- moved minimum memory requirement into the episode headers\r
+  (Keen 4 & 5 need 310,000 bytes, Keen 6 needs 300,000 bytes)\r
+- added missing code in SD_Default() (also marked the bug in there)\r
+\r
+\r
+2020-01-08:\r
+\r
+- fixed a minor issue in ID_RF.C related to the tile animation sounds in Keen 6\r
+\r
+\r
+2020-02-21:\r
+\r
+- fixed a bug in Bipship movement code (Keen 6)\r
+- modified sound engine to avoid SDL_Delay entirely and read port 0x388 instead\r
+  (which is exactly how version 1.4 and above work)\r
+\r
+\r
+2020-03-05:\r
+\r
+- fixed bug in Ampton walk code\r
+\r
+\r
+2020-04-24:\r
+\r
+- added the title screen in DemoLoop() (after high scores)\r
+- replaced some chunk numbers with their enum names in K4_LEVEL.C (smoke sprites)\r
+\r
+\r
+2020-05-20:\r
+\r
+- SpawnEnemyShot now makes the object removable (KEEN5)\r
+\r
+\r
+2021-05-05:\r
+\r
+- fixed bug in PageLayout (negative top row index)\r
+\r
+\r
+2021-05-05:\r
+\r
+- fixed bug in JanitorDialog (bad picture position for KEENMADPIC)\r
+\r
+\r
+2021-06-06 to 2021-06-23:\r
+\r
+- renamed things in Keen 4 & 5 to what is believed to be the original naming\r
+  scheme - Keen 6 stuff is still guesswork\r
+- adapted code to make it compile with Borland C++ 2.0 and 3.0\r
+- adjusted compiler settings and code to recreate the original executables as\r
+  closely as possible (for automated comparison and verification)\r
+- found and fixed a few bugs along the way\r
+\r
+\r
+2021-06-24:\r
+\r
+- tracked down and fixed any remaining differences\r
+- compiling (with source debugging enabled) and then compressing the EXE files\r
+  with LZEXE Version 0.91 creates EXACTLY the same files as the original v1.4\r
+  releases shipped with\r
+- Mission accomplished, I guess...\r
+\r
+\r
+2021-07-01:\r
+\r
+- first public release of this source code\r
+\r
+\r
+2021-07-03:\r
+\r
+- Keen 6 EGA v1.5 can now be recreated with this source code thanks to NY00123\r
+- added some more comments to the source code, mostly explaining what the temp\r
+  variables are used for in each actor's code\r
+- changed the function names in the Keen 6 code from ...Think, ...Contact and\r
+  ...React to T_..., C_... and R_... for more consistency with the Keen 4 and\r
+  Keen 5 code, also changed some state and sprite names for more consistency\r
+\r
+2021-07-08:\r
+- all CGA executables of versions 1.4 and 1.5 can now be recreated with this\r
+  source code\r
+- added a few more comments\r
diff --git a/16/keen456/LZEXE.EXE b/16/keen456/LZEXE.EXE
new file mode 100755 (executable)
index 0000000..727c203
Binary files /dev/null and b/16/keen456/LZEXE.EXE differ
diff --git a/16/keen456/RCK4.BAT b/16/keen456/RCK4.BAT
new file mode 100755 (executable)
index 0000000..dbb8217
--- /dev/null
@@ -0,0 +1,4 @@
+SET PATH=%PATH%;C:\BC30\BIN\r
+cd KEEN4-6\r
+BC RCK4.PRJ\r
+cd ..
\ No newline at end of file
diff --git a/16/keen456/RCK4C.BAT b/16/keen456/RCK4C.BAT
new file mode 100755 (executable)
index 0000000..c0b4664
--- /dev/null
@@ -0,0 +1,4 @@
+SET PATH=%PATH%;C:\BC30\BIN\r
+cd KEEN4-6\r
+BC RCK4C.PRJ\r
+cd ..
\ No newline at end of file
diff --git a/16/keen456/RCK4GT.BAT b/16/keen456/RCK4GT.BAT
new file mode 100755 (executable)
index 0000000..a34d6ac
--- /dev/null
@@ -0,0 +1,4 @@
+SET PATH=%PATH%;C:\BC31\BIN\r
+cd KEEN4-6\r
+BC RCK4GT.PRJ\r
+cd ..
\ No newline at end of file
diff --git a/16/keen456/RCK5.BAT b/16/keen456/RCK5.BAT
new file mode 100755 (executable)
index 0000000..24f70e1
--- /dev/null
@@ -0,0 +1,4 @@
+SET PATH=%PATH%;C:\BC30\BIN\r
+cd KEEN4-6\r
+BC RCK5.PRJ\r
+cd ..
\ No newline at end of file
diff --git a/16/keen456/RCK5C.BAT b/16/keen456/RCK5C.BAT
new file mode 100755 (executable)
index 0000000..e794cb9
--- /dev/null
@@ -0,0 +1,4 @@
+SET PATH=%PATH%;C:\BC30\BIN\r
+cd KEEN4-6\r
+BC RCK5C.PRJ\r
+cd ..
\ No newline at end of file
diff --git a/16/keen456/RCK5GT.BAT b/16/keen456/RCK5GT.BAT
new file mode 100755 (executable)
index 0000000..37c160f
--- /dev/null
@@ -0,0 +1,4 @@
+SET PATH=%PATH%;C:\BC31\BIN\r
+cd KEEN4-6\r
+BC RCK5GT.PRJ\r
+cd ..
\ No newline at end of file
diff --git a/16/keen456/RCK6.BAT b/16/keen456/RCK6.BAT
new file mode 100755 (executable)
index 0000000..1f04465
--- /dev/null
@@ -0,0 +1,4 @@
+SET PATH=%PATH%;C:\BC30\BIN\r
+cd KEEN4-6\r
+BC RCK6.PRJ\r
+cd ..
\ No newline at end of file
diff --git a/16/keen456/RCK6C.BAT b/16/keen456/RCK6C.BAT
new file mode 100755 (executable)
index 0000000..31a281d
--- /dev/null
@@ -0,0 +1,4 @@
+SET PATH=%PATH%;C:\BC30\BIN\r
+cd KEEN4-6\r
+BC RCK6C.PRJ\r
+cd ..
\ No newline at end of file
diff --git a/16/keen456/RCK6C15.BAT b/16/keen456/RCK6C15.BAT
new file mode 100755 (executable)
index 0000000..5ad44a3
--- /dev/null
@@ -0,0 +1,4 @@
+SET PATH=%PATH%;C:\BC31\BIN\r
+cd KEEN4-6\r
+BC RCK6C15.PRJ\r
+cd ..
\ No newline at end of file
diff --git a/16/keen456/RCK6E15.BAT b/16/keen456/RCK6E15.BAT
new file mode 100755 (executable)
index 0000000..50a3bb4
--- /dev/null
@@ -0,0 +1,4 @@
+SET PATH=%PATH%;C:\BC31\BIN\r
+cd KEEN4-6\r
+BC RCK6E15.PRJ\r
+cd ..
\ No newline at end of file
diff --git a/16/keen456/cleanup.bat b/16/keen456/cleanup.bat
new file mode 100755 (executable)
index 0000000..dbf734f
--- /dev/null
@@ -0,0 +1,7 @@
+@echo off\r
+del KEEN4-6\KEEN4\OBJ\*.obj\r
+del KEEN4-6\KEEN4C\OBJ\*.obj\r
+del KEEN4-6\KEEN5\OBJ\*.obj\r
+del KEEN4-6\KEEN5C\OBJ\*.obj\r
+del KEEN4-6\KEEN6\OBJ\*.obj\r
+del KEEN4-6\KEEN6C\OBJ\*.obj
\ No newline at end of file
diff --git a/16/keen456/readme.txt b/16/keen456/readme.txt
new file mode 100755 (executable)
index 0000000..b8aeb87
--- /dev/null
@@ -0,0 +1,376 @@
+Reconstructed Commander Keen 4-6 Source Code\r
+Copyright (C) 2021 K1n9_Duk3\r
+===============================================================================\r
+\r
+This is an UNOFFICIAL reconstruction of the original Keen 4-6 source code. More\r
+specifically, it is a reconstruction of the version 1.4 (and 1.5) EGA and CGA\r
+releases.\r
+\r
+The code is primarily based on the Catacomb 3-D source code (ID Engine files).\r
+The text view code (CK_TEXT.C) is based on Wolfenstein 3-D, and the main game\r
+and actor code is loosely based on Keen Dreams.\r
+\r
+Catacomb 3-D Source Code\r
+Copyright (C) 1993-2014 Flat Rock Software\r
+\r
+Wolfenstein 3-D Source Code\r
+Copyright (C) 1992 id Software\r
+\r
+Keen Dreams Source Code\r
+Copyright (C) 2014 Javier M. Chavez\r
+\r
+This program is free software; you can redistribute it and/or modify\r
+it under the terms of the GNU General Public License as published by\r
+the Free Software Foundation; either version 2 of the License, or\r
+(at your option) any later version.\r
+\r
+This program is distributed in the hope that it will be useful,\r
+but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+GNU General Public License for more details.\r
+\r
+You should have received a copy of the GNU General Public License along\r
+with this program; if not, write to the Free Software Foundation, Inc.,\r
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+\r
+\r
+Getting this code to compile\r
+============================\r
+\r
+You will need Borland C++ 2.0, 3.0 or 3.1 to compile this code. Newer versions\r
+may work, but have not been tested.\r
+\r
+In addition to the compiler and this source code, you will also need to extract\r
+some data files from the original executables. I have provided patch scripts to\r
+extract that data from the original executables. To use these patch scripts,\r
+you will need one of the following tools:\r
+\r
+CKPatch v0.11.3 (unofficial) - 16 bit DOS application\r
+http://ny.duke4.net/files.html\r
+\r
+K1n9_Duk3's Patching Utility v2.3 - Win32 application\r
+http://k1n9duk3.shikadi.net/patcher.html\r
+\r
+Copy the patching programs into the "static" subdirectory, along with copies of\r
+the original Keen 4-6 executables. The Keen 4-6 executables should be named\r
+KEEN4*.EXE, KEEN5*.EXE and KEEN6*.EXE respectively, otherwise the patching\r
+programs might have trouble recognizing the files.\r
+\r
+If you are going to use CKPatch, you should only copy one KEEN4*.EXE file, one\r
+KEEN5*.EXE and one KEEN6*.EXE file into the "static" directory. CKPatch will\r
+always use the first file matching this pattern, so if you have both KEEN4C.EXE\r
+and KEEN4E.EXE in that directory, for example, CK4PATCH will always operate on\r
+KEEN4C.EXE and ignore KEEN4E.EXE.\r
+\r
+These patches will only work on very specific versions of the original game's\r
+executables (v1.4/v1.5 CGA/EGA). The GT versions are not supported by CKPatch,\r
+so you will need K1n9_Duk3's Patching Utility to extract their data with the\r
+patch scripts included in the "static" directory. CKPatch may also be unable to\r
+open the FormGen release of Keen 4 (and Keen 5, if it exists). Decompressing\r
+the game's executable with UNLZEXE may fix that problem.\r
+\r
+If you are using K1n9_Duk3's Patching Utility, simply run it and open the patch\r
+file (*.PAT) you want to use. If K1n9_Duk3's Patching Utility found more than\r
+one supported version of the executables (or none at all), it will ask you to\r
+open the executable manually. Save the extracted files into the "static"\r
+directory using the suggested file names.\r
+\r
+If you are going to use CKPatch, just copy the files as described above, then\r
+use the provided batch files as described in the following section.\r
+\r
+\r
+Setting up a working environment in DOSBox\r
+------------------------------------------\r
+\r
+The Borland C++ compilers, as well as CKPATCH, MAKEOBJ and LZEXE, are all DOS\r
+programs, so you will need a system that is capable of running DOS programs or\r
+you will have to use an emulator like DOSBox to run these programs.\r
+\r
+If you are going to use DOSBox, start out by preparing a base directory on your\r
+system that you are going to mount as drive C: in DOSBox (mounting your real C:\r
+drive as C: in DOSBox is NOT recommended). Let's assume your base directory is\r
+"C:\BASE". Extract the contents of this package into that directory. Also copy\r
+the Borland C++ compiler(s) you are going to use into that directory,\r
+preferably into subdirectories named "BC20", "BC30" and "BC31" to make things\r
+easier for you. You can use different names, but then you will have to edit a\r
+couple of files and settings later on.\r
+\r
+You could just start DOSBox and manually mount your base directory as the C:\r
+drive in DOSBox, but this project comes with a couple of batch files that make\r
+the process much easier, as long as things are set up correctly.\r
+\r
+In case you didn't know, dragging and dropping a file onto DOSBox.exe will\r
+start DOSBox and mount the directory in which that file is located in as the C:\r
+drive in DOSBox, then it will try to execute that file in DOSBox.\r
+\r
+At this point, your base directory should have the following contents:\r
+\r
+       BC20         - Borland C++ 2.0\r
+       BC30         - Borland C++ 3.0\r
+       BC31         - Borland C++ 3.1\r
+       KEEN4-6      - main source directory\r
+\r
+       lzexe.exe    - for compressing executables\r
+       rck4.bat     - opens RCK4.PRJ with the correct compiler version\r
+       rck4c.bat    - opens RCK4C.PRJ with the correct compiler version\r
+       rck5.bat     - opens RCK5.PRJ with the correct compiler version\r
+       rck5c.bat    - opens RCK5C.PRJ with the correct compiler version\r
+       rck6.bat     - opens RCK6.PRJ with the correct compiler version\r
+       rck6c.bat    - opens RCK6C.PRJ with the correct compiler version\r
+       rck4gt.bat   - opens RCK4GT.PRJ with the correct compiler version\r
+       rck5gt.bat   - opens RCK5GT.PRJ with the correct compiler version\r
+       rck6e15.bat  - opens RCK6E15.PRJ with the correct compiler version\r
+       rck6c15.bat  - opens RCK6C15.PRJ with the correct compiler version\r
+       ripnmake.bat - extracts data files and converts them into .OBJ files\r
+       readme.txt   - this file\r
+\r
+The first order of business is to drag and drop RIPNMAKE.BAT onto DOSBox.exe or\r
+onto a shortcut to DOSBox.exe. This will try to extract the required data files\r
+from the original executables via CKPatch and then convert the data files into\r
+.OBJ files that the compiler can include when generating the new executables.\r
+The .OBJ files will be created in the KEEN4, KEEN5 and KEEN6 subdirectories.\r
+\r
+Note that you should do this step even if you already extracted the data files\r
+using K1n9_Duk3's Patching Utility. The RIPNMAKE.BAT file may not be able to\r
+run the CKPatch programs in that case, but as long as the extracted data files\r
+are present in the "KEEN4-6\static" directory, it will still convert them into\r
+.OBJ files and place the .OBJ files into the correct directories.\r
+\r
+If you are using CKPatch and you want to extract the data for both the EGA and\r
+the CGA versions, you need to delete the KEEN*.EXE files from the "static"\r
+directory after running RIPNMAKE.BAT and then copy the other executables into\r
+that directory and run RIPNMAKE.BAT again.\r
+\r
+\r
+Check the KEEN4, KEEN5 and KEEN6 directories and make sure the following files\r
+are in them:\r
+\r
+       CK?ADICT.OBJ\r
+       CK?AHEAD.OBJ\r
+       CK?CDICT.OBJ (for the CGA version)\r
+       CK?CHEAD.OBJ (for the CGA version)\r
+       CK?EDICT.OBJ (for the EGA version)\r
+       CK?EHEAD.OBJ (for the EGA version)\r
+       CK?INTRO.OBJ\r
+       CK?MHEAD.OBJ\r
+       CK6ORDER.OBJ (only for Keen 6)\r
+\r
+You can exit from DOSBox after this is done. Simply type "exit" at the prompt\r
+and hit Enter.\r
+\r
+\r
+Compiling the code:\r
+-------------------\r
+\r
+The other batch files in your base directory (RCK*.BAT) are provided to make\r
+compiling the code easy. Simply drag and drop the batch file onto DOSBox.exe\r
+(or onto a shortcut to DOSBox.exe) and it will open the respective project in\r
+the correct version of the compiler.\r
+\r
+       RCK4       - EGA version 1.4 - Apogee and FormGen release\r
+       RCK5       - EGA version 1.4 - Apogee (and FormGen?) release)\r
+       RCK6       - EGA version 1.4 - FormGen release\r
+       RCK4C      - CGA version 1.4 - Apogee and FormGen release\r
+       RCK5C      - CGA version 1.4 - Apogee (and FormGen?) release)\r
+       RCK6C      - CGA version 1.4 - FormGen release\r
+\r
+       RCK4GT     - EGA version 1.4 - GT release\r
+       RCK5GT     - EGA version 1.4 - GT release\r
+       RCK6E15    - EGA version 1.5 - FormGen release\r
+       RCK6C15    - CGA version 1.5 - FormGen release\r
+\r
+The first six are set up for use with Borland C++ 3.0 by default, the later\r
+four are set up for use with Borland C++ 3.1. If you want to compile them with\r
+a different version of the compiler, edit the batch file and change the\r
+compiler directory (in the "SET PATH=" line) to the one you wish to use. Then\r
+open the project (drag and drop the batch file onto DOSBox.exe) and then select\r
+"Options" -> "Directories" from the main menu. Make sure that the Include and\r
+Library directory settings point to a version that you actually have installed.\r
+\r
+Note that RCK4, RCK4C, RCK5, RCK5C, RCK6 and RCK6C are set up to compile with\r
+BC30, but using the Library directory from BC20. This is required for\r
+recreating the original executables, but if you don't have both of these\r
+versions and you don't care about creating 100% identical copies, you can just\r
+change the directory settings to point to the compiler you have.\r
+\r
+To actually compile the code, press F9 or select "Compile" -> "Make" from the\r
+menu.\r
+\r
+Compiling all of the files may take a while, depending on your CPU cycles\r
+settings in DOSBox. By default, DOSBox should automatically switch to maximum\r
+cycles mode when Borland C++ 3.0 or 3.1 are started, but not when Borland C++\r
+2.0 is started. You can simply enter the command "cycles max" at the DOSBox\r
+prompt (or add it to the batch files) to switch DOSBox into maximum cycles mode\r
+if the automatic switch doesn't work for you.\r
+\r
+With the current code base, it is completely normal to get 3 or 4 warnings as\r
+the code is compiled. One may come from CK_KEEN2.C ("Condition is always true")\r
+when compiling Keen 6 v1.5. The other three of them should come from ID_US_1.C\r
+(2x "Condition is always true" and 1x "Unreachable code"). You can ignore these\r
+warnings.\r
+\r
+Once the code has been compiled, simply press ALT+X or select "File" -> "Quit"\r
+from the menu. Don't just close DOSBox while Borland C++ is still running, you\r
+would just end up with lots of useless swap files in your project directory.\r
+\r
+Type "exit" at the DOS prompt to quit DOSBox.\r
+\r
+Please note that you should always quit DOSBox after compiling a project.\r
+Trying to compile a second project after the first one may cause issues with\r
+the provided batch files and the way they adjust the PATH environment variable.\r
+For example, DOSBox may end up starting the wrong version of the compiler.\r
+\r
+\r
+Recreating the original executables\r
+===================================\r
+\r
+Here's the TL;DR for advanced users:\r
+\r
+RCK4.PRJ, RCK5.PRJ, RCK6.PRJ, RCK4C.PRJ, RCK5C.PRJ, RCK6C.PRJ:\r
+- Use same compiler settings as in the provided RCK?.PRJ files\r
+- Use LIB directory from Borland C++ 2.0\r
+- Use INCLUDE directory from Borland C++ 3.0\r
+- Compile with Borland C++ 3.0\r
+- Compress compiled EXE file with LZEXE version 0.91\r
+\r
+RCK4GT.PJR, RCK5GT.PRJ, RCK6E15.PRJ, RCK6C15.PRJ:\r
+- Use same compiler settings as in the provided RCK?GT.PRJ/RCK6?15.PRJ files\r
+- Use LIB and INCLUDE directories from Borland C++ 3.1\r
+- Compile with Borland C++ 3.1\r
+- Compress compiled EXE file with LZEXE version 0.91\r
+\r
+To create 100% identical copies of the original v1.4 EGA executables, you will\r
+need a copy of Borland C++ 3.0 as well as a copy of Borland C++ 2.0 (or at\r
+least the LIB directory from Borland C++ 2.0).\r
+\r
+Make sure to start with the original project files included in this package.\r
+Those have all of the compiler options set to the correct values. Different\r
+settings may produce slightly different code and the whole point of this is\r
+to get code that's 100% identical to the original executables.\r
+\r
+Unlike Borland C++ 3.1, version 3.0 will not recompile every file when you\r
+select "Build all" from the "Compile" menu if neither that file nor the header\r
+files used by that file have changed since the last time that file was\r
+compiled. Therefore I recommend that you delete all *.OBJ files from the\r
+"KEEN*\OBJ" directory to make sure everything gets recompiled. The CLEANUP.BAT\r
+file will take care of that (can be run in Windows as well as in DOSBox).\r
+\r
+Open the project in BC30 and select "Options" -> "Directories" from the menu.\r
+Change the "Include Directories" path to the INCLUDE directory from BC30 and\r
+change the "Library Directories" path to the LIB directory from BC20.\r
+\r
+Compile the code (select either "Make" or "Build all" from the "Compile" menu)\r
+and once the compiler is done, quit to DOS(Box) and compress the new executable\r
+with LZEXE. To compress RCK4.EXE, you must type "LZEXE RCK4.EXE" and hit Enter.\r
+The program will display a message in French about additional information at\r
+the end of the executable that will be lost after compression and ask if you\r
+want to abort. Type "N" and hit Enter to compress the file.\r
+\r
+\r
+For reference, these are the results you should be getting after compression:\r
+\r
+Keen 4 EGA version 1.4 (Apogee)  : size = 105108 bytes, CRC = 6646B983\r
+Keen 4 EGA version 1.4 (FormGen) : size = 105140 bytes, CRC = F91E558B\r
+Keen 4 EGA version 1.4 (GT)      : size = 106178 bytes, CRC = 0A05442E\r
+Keen 4 CGA version 1.4 (Apogee)  : size =  98007 bytes, CRC = F544DD41\r
+Keen 4 CGA version 1.4 (FormGen) : size =  98007 bytes, CRC = 018FA365\r
+\r
+Keen 5 EGA version 1.4 (Apogee)  : size = 106417 bytes, CRC = 2A45589A\r
+(No FormGen release of Keen 5 EGA version 1.4 is known at this time.)\r
+Keen 5 EGA version 1.4 (GT)      : size = 107611 bytes, CRC = 5E450B12\r
+Keen 5 CGA version 1.4 (Apogee)  : size =  98880 bytes, CRC = FB9EB429\r
+(No FormGen release of Keen 5 CGA version 1.4 is known at this time.)\r
+\r
+Keen 6 EGA version 1.4 (FormGen) : size = 107719 bytes, CRC = 9CDACDAE\r
+Keen 6 EGA version 1.5 (FormGen) : size = 109058 bytes, CRC = 5B828EE2\r
+Keen 6 CGA version 1.4 (FormGen) : size = 100964 bytes, CRC = F36A4C51\r
+Keen 6 CGA version 1.5 (FormGen) : size = 102166 bytes, CRC = D2F379B8\r
+\r
+The GT versions appear to have been compiled with Borland C++ 3.1 and its LIB\r
+directory, but otherwise using the same compiler and optimization settings as\r
+in the earlier Apogee/FormGen versions.\r
+\r
+The only difference between the Apogee/FormGen and the GT versions of the Keen\r
+4 and 5 executables (obvious differences in the included OBJ files aside) is\r
+that the GT version has only four entries in the help menu instead of five\r
+(the Order Info section has been removed) and that the GT version has a\r
+different set of default high scores. You must have GOODTIMES defined to\r
+compile these versions (already set in the RCK?GT.PRJ files).\r
+\r
+Keen 6 EGA/CGA version 1.5 was also compiled with Borland C++ 3.1 and its LIB\r
+directory, but it uses completely different compiler and optimization settings\r
+and also has one new variable in CK_PLAY.C that is never used but still has to\r
+be present to recreate the original code. It appears that somebody just pressed\r
+the "Fastest code" button in the compiler optimizations window before version\r
+1.5 was compiled, which was a bad idea for this code. None of the variables in\r
+the ID Engine are marked as "volatile", not even the ones that may be changed\r
+by an interrupt. That means the optimizations may end up generating code that\r
+leads to endless loops, as is the case with the "while (!LastScan);" loop in\r
+the PicturePause() routine.\r
+\r
+To recreate the exact same files as the original Keen 6 v1.5 executables, you\r
+need to compress the generated executables with LZEXE and then hex-edit the\r
+compressed files to replace the "LZ91" signature at offset 0x1C in the EXE file\r
+with four 0 bytes. If you don't have a hex editor, you can use K1n9_Duk3's\r
+Patching Utility for that step. Simply open the "fix_RCK6_v15.pat" or the\r
+"fix_RCK6C_v15.pat" patch file (located in the KEEN4-6\KEEN6\OBJ and\r
+KEEN4-6\KEEN6C\OBJ directories, respectively) with K1n9_Duk3's Patching Utility\r
+and let it patch the compressed executable for you.\r
+\r
+\r
+Borland C++ 2.0 issues?\r
+=======================\r
+\r
+Any versions of Keen 4-6 prior to version 1.4 appear to have been compiled with\r
+Borland C++ 2.0. Version 1.4 is where they switched from 2.0 to 3.0 (without\r
+changing the Library directory to the one from 3.0 for some reason).\r
+\r
+The code in this package can be built with Borland C++ 2.0 and the compiled\r
+executables appear to be working perfectly fine. But when that version of the\r
+compiler was used to compile the "Return to the Shadowlands" source code (which\r
+is based on an earlier incarnation of this source code recreation), it caused\r
+problems. The code compiled without errors, but the compiled executable would\r
+always quit with the error message "Abnormal program termination".\r
+\r
+The reason for this error was that the compiler appeared to have forced the\r
+"grneeded" array (declared as "far" in ID_CA.C) into the main data segment\r
+instead of giving it its own far data segment. With this additional data in the\r
+main data segment, there was simply not enough space left for the stack and\r
+that is why the program aborted with an error message.\r
+\r
+It is unclear what caused this problem. The same source code compiles perfectly\r
+fine with Borland C++ 3.1 and produces an executable that actually works. If\r
+similar issues arise when working on mods based on this source code, try using\r
+Borland C++ 3.1 instead of whatever version you were using before.\r
+\r
+\r
+Special Thanks\r
+==============\r
+\r
+I would like to thank Jason Blochowiak, Adrian Carmack, John Carmack, Tom Hall,\r
+John Romero and Robert Prince for creating Commander Keen 4-6 in the first\r
+place.\r
+\r
+Special thanks to John Carmack (and the rest of id Software) for releasing the\r
+Wolfenstein 3-D and Spear of Destiny source code to the public in July 1995.\r
+\r
+Extra special thanks to the late Richard Mandel of Flat Rock Software and\r
+everybody else involved in the process of getting the source code of some of\r
+the games id Software made for Softdisk (Catacomb series, Hovertank, Keen\r
+Dreams) released to the public. I have learned a lot from the source code of\r
+these games and this project certainly would not have been possible without it.\r
+\r
+Thanks to PCKF user Multimania for supplying additional information regarding\r
+the names of functions and variables for Keen 4 and Keen 5.\r
+\r
+And last, but not least, I would like to thank NY00123 for figuring out how to\r
+get the compiler to recreate Keen 6 v1.5 and also for sharing a lot of valuable\r
+information about the "gamesrc-ver-recreation" project in various public posts\r
+on the RGB Classic Games Forum (among other places). That's where I first heard\r
+about the TDUMP utility, which is certainly a much better way to extract names\r
+from the debugging information that some executables came with. And using IDA\r
+to open executables and then make IDA generate ASM files that can be compared\r
+more easily using tools like FC in Windows/DOS is pretty much the best way to\r
+track down differences between those two executables without going insane.\r
+\r
+[END OF FILE]
\ No newline at end of file
diff --git a/16/keen456/ripnmake.bat b/16/keen456/ripnmake.bat
new file mode 100755 (executable)
index 0000000..ab8b8db
--- /dev/null
@@ -0,0 +1,20 @@
+@echo off\r
+\r
+cd keen4-6\static\r
+\r
+echo Trying to extract data from KEEN 4 ...\r
+ck4patch ripck4.pat\r
+echo.\r
+\r
+echo Trying to extract data from KEEN 5 ...\r
+ck5patch ripck5.pat\r
+echo.\r
+\r
+echo Trying to extract data from KEEN 6 ...\r
+ck6patch ripck6.pat\r
+echo.\r
+\r
+echo Converting data files to .OBJ ...\r
+call make.bat\r
+\r
+cd ..\..
\ No newline at end of file