--- /dev/null
+; 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
--- /dev/null
+/* 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
--- /dev/null
+/* 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
--- /dev/null
+/* 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
--- /dev/null
+/* 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
--- /dev/null
+/* 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
--- /dev/null
+/* 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
--- /dev/null
+/* 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
--- /dev/null
+/* 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
--- /dev/null
+/* 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
--- /dev/null
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.
--- /dev/null
+/* 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
--- /dev/null
+/* 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
--- /dev/null
+/* 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
--- /dev/null
+/* 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
--- /dev/null
+/* 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
--- /dev/null
+/* 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
--- /dev/null
+/* 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
--- /dev/null
+/* 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
--- /dev/null
+; 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
--- /dev/null
+/* 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
--- /dev/null
+/* 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
--- /dev/null
+/* 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
--- /dev/null
+/* 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
--- /dev/null
+/* 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
--- /dev/null
+; 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
--- /dev/null
+/* 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
--- /dev/null
+/* 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
--- /dev/null
+; 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
--- /dev/null
+; 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
--- /dev/null
+; 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
--- /dev/null
+/* 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
--- /dev/null
+;=====================================\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
--- /dev/null
+/* 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
--- /dev/null
+;\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
--- /dev/null
+/* 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
--- /dev/null
+/* 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
--- /dev/null
+/* 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
--- /dev/null
+/* 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
--- /dev/null
+/* 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
--- /dev/null
+/* 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
--- /dev/null
+;=====================================\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
--- /dev/null
+/* 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
--- /dev/null
+;\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
--- /dev/null
+/* 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
--- /dev/null
+/* 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
--- /dev/null
+;=====================================\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
--- /dev/null
+/* 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
--- /dev/null
+;\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
--- /dev/null
+/* 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
--- /dev/null
+/* 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
--- /dev/null
+/* 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
--- /dev/null
+/* 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
--- /dev/null
+/* 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
--- /dev/null
+/* 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
--- /dev/null
+;=====================================\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
--- /dev/null
+/* 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
--- /dev/null
+;\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
--- /dev/null
+/* 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
--- /dev/null
+/* 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
--- /dev/null
+;=====================================\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
--- /dev/null
+/* 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
--- /dev/null
+;\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
--- /dev/null
+/* 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
--- /dev/null
+/* 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
--- /dev/null
+/* 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
--- /dev/null
+/* 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
--- /dev/null
+/* 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
--- /dev/null
+/* 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
--- /dev/null
+%file RCK6E15.exe 109058\r
+%patch $1C 0 0 0 0\r
+%end
\ No newline at end of file
--- /dev/null
+;=====================================\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
--- /dev/null
+/* 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
--- /dev/null
+;\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
--- /dev/null
+/* 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
--- /dev/null
+%file RCK6C15.exe 102166\r
+%patch $1C 0 0 0 0\r
+%end
\ No newline at end of file
--- /dev/null
+@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
--- /dev/null
+/*\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
--- /dev/null
+%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
--- /dev/null
+%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
--- /dev/null
+%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
--- /dev/null
+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
--- /dev/null
+SET PATH=%PATH%;C:\BC30\BIN\r
+cd KEEN4-6\r
+BC RCK4.PRJ\r
+cd ..
\ No newline at end of file
--- /dev/null
+SET PATH=%PATH%;C:\BC30\BIN\r
+cd KEEN4-6\r
+BC RCK4C.PRJ\r
+cd ..
\ No newline at end of file
--- /dev/null
+SET PATH=%PATH%;C:\BC31\BIN\r
+cd KEEN4-6\r
+BC RCK4GT.PRJ\r
+cd ..
\ No newline at end of file
--- /dev/null
+SET PATH=%PATH%;C:\BC30\BIN\r
+cd KEEN4-6\r
+BC RCK5.PRJ\r
+cd ..
\ No newline at end of file
--- /dev/null
+SET PATH=%PATH%;C:\BC30\BIN\r
+cd KEEN4-6\r
+BC RCK5C.PRJ\r
+cd ..
\ No newline at end of file
--- /dev/null
+SET PATH=%PATH%;C:\BC31\BIN\r
+cd KEEN4-6\r
+BC RCK5GT.PRJ\r
+cd ..
\ No newline at end of file
--- /dev/null
+SET PATH=%PATH%;C:\BC30\BIN\r
+cd KEEN4-6\r
+BC RCK6.PRJ\r
+cd ..
\ No newline at end of file
--- /dev/null
+SET PATH=%PATH%;C:\BC30\BIN\r
+cd KEEN4-6\r
+BC RCK6C.PRJ\r
+cd ..
\ No newline at end of file
--- /dev/null
+SET PATH=%PATH%;C:\BC31\BIN\r
+cd KEEN4-6\r
+BC RCK6C15.PRJ\r
+cd ..
\ No newline at end of file
--- /dev/null
+SET PATH=%PATH%;C:\BC31\BIN\r
+cd KEEN4-6\r
+BC RCK6E15.PRJ\r
+cd ..
\ No newline at end of file
--- /dev/null
+@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
--- /dev/null
+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
--- /dev/null
+@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