]> 4ch.mooo.com Git - 16.git/blob - 16/keen456/KEEN4-6/CK_KEEN.C
extrcted keen code remake
[16.git] / 16 / keen456 / KEEN4-6 / CK_KEEN.C
1 /* Reconstructed Commander Keen 4-6 Source Code\r
2  * Copyright (C) 2021 K1n9_Duk3\r
3  *\r
4  * This file is loosely based on:\r
5  * Keen Dreams Source Code\r
6  * Copyright (C) 2014 Javier M. Chavez\r
7  *\r
8  * This program is free software; you can redistribute it and/or modify\r
9  * it under the terms of the GNU General Public License as published by\r
10  * the Free Software Foundation; either version 2 of the License, or\r
11  * (at your option) any later version.\r
12  *\r
13  * This program is distributed in the hope that it will be useful,\r
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
16  * GNU General Public License for more details.\r
17  *\r
18  * You should have received a copy of the GNU General Public License along\r
19  * with this program; if not, write to the Free Software Foundation, Inc.,\r
20  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
21  */\r
22 \r
23 /*\r
24 CK_KEEN.C\r
25 =========\r
26 \r
27 Contains the following actor types (in this order):\r
28 \r
29 - Keen (regular levels)\r
30 \r
31 */\r
32 \r
33 #include "CK_DEF.H"\r
34 \r
35 /*\r
36 =============================================================================\r
37 \r
38                                                  GLOBAL VARIABLES\r
39 \r
40 =============================================================================\r
41 */\r
42 \r
43 Sint16 singlegravity;   // left over from Keen Dreams, not used in Keen 4-6\r
44 \r
45 Uint16 bounceangle [8][8] =\r
46 {\r
47         { 0,  0,  0,  0,  0,  0,  0,  0},\r
48         { 7,  6,  5,  4,  3,  2,  1,  0},\r
49         { 5,  4,  3,  2,  1,  0, 15, 14},\r
50         { 5,  4,  3,  2,  1,  0, 15, 14},\r
51         { 3,  2,  1,  0, 15, 14, 13, 12},\r
52         { 9,  8,  7,  6,  5,  4,  3,  2},\r
53         { 9,  8,  7,  6,  5,  4,  3,  2},\r
54         {11, 10,  9,  8,  7,  6,  5,  4}\r
55 };\r
56 \r
57 #ifndef KEEN4\r
58 arrowdirtype arrowflip[] = {arrow_South, arrow_West, arrow_North, arrow_East, arrow_SouthWest, arrow_NorthWest, arrow_NorthEast, arrow_SouthEast};\r
59 #endif\r
60 \r
61 statetype s_keenstand     = {KEENSTANDLSPR, KEENSTANDRSPR, stepthink, false, true, 4, 0, 32, KeenPauseThink, KeenContact, KeenStandReact, &s_keenstand};\r
62 \r
63 #ifdef KEEN5\r
64 statetype s_keenride     = {KEENONPLATSPR, KEENONPLATSPR, stepthink, false, true, 4, 0, 32, KeenStandThink, KeenContact, KeenStandReact, &s_keenride};\r
65 #endif\r
66 \r
67 statetype s_keenpauselook = {KEENLOOKUSPR, KEENLOOKUSPR, stepthink, false, true, 60, 0, 0, KeenPauseThink, KeenContact, KeenStandReact, &s_keenstand};\r
68 \r
69 statetype s_keenwait1    = {KEENWAITR2SPR, KEENWAITR2SPR, stepthink, false, true, 90, 0, 0, KeenPauseThink, KeenContact, KeenStandReact, &s_keenwait2};\r
70 statetype s_keenwait2    = {KEENWAITR1SPR, KEENWAITR1SPR, stepthink, false, true, 10, 0, 0, KeenPauseThink, KeenContact, KeenStandReact, &s_keenwait3};\r
71 statetype s_keenwait3    = {KEENWAITR2SPR, KEENWAITR2SPR, stepthink, false, true, 90, 0, 0, KeenPauseThink, KeenContact, KeenStandReact, &s_keenwait4};\r
72 statetype s_keenwait4    = {KEENWAITR1SPR, KEENWAITR1SPR, stepthink, false, true, 10, 0, 0, KeenPauseThink, KeenContact, KeenStandReact, &s_keenwait5};\r
73 statetype s_keenwait5    = {KEENWAITR2SPR, KEENWAITR2SPR, stepthink, false, true, 90, 0, 0, KeenPauseThink, KeenContact, KeenStandReact, &s_keenwait6};\r
74 statetype s_keenwait6    = {KEENWAITR3SPR, KEENWAITR3SPR, stepthink, false, true, 70, 0, 0, KeenPauseThink, KeenContact, KeenStandReact, &s_keenstand};\r
75 \r
76 #ifdef KEEN4\r
77 statetype s_keenmoon1     = {KEENMOON1SPR, KEENMOON1SPR, stepthink, false, true, 20, 0, 0, KeenPauseThink, KeenContact, KeenStandReact, &s_keenmoon2};\r
78 statetype s_keenmoon2     = {KEENMOON2SPR, KEENMOON2SPR, stepthink, false, true, 90, 0, 0, KeenPauseThink, KeenContact, KeenStandReact, &s_keenmoon3};\r
79 statetype s_keenmoon3     = {KEENMOON1SPR, KEENMOON1SPR, stepthink, false, true, 20, 0, 0, KeenPauseThink, KeenContact, KeenStandReact, &s_keenstand};\r
80 #endif\r
81 \r
82 statetype s_keenread      = {KEENSITREAD1SPR, KEENSITREAD1SPR, step, false, true, 16, 0, 0, 0, KeenContact, KeenStandReact, &s_keenread2};\r
83 statetype s_keenread2     = {KEENSITREAD2SPR, KEENSITREAD2SPR, step, false, true, 16, 0, 0, 0, KeenContact, KeenStandReact, &s_keenread3};\r
84 statetype s_keenread3     = {KEENSITREAD3SPR, KEENSITREAD3SPR, step, false, true, 16, 0, 0, 0, KeenContact, KeenStandReact, &s_keenread4};\r
85 statetype s_keenread4     = {KEENSITREAD4SPR, KEENSITREAD4SPR, step, false, true, 16, 0, 0, 0, KeenContact, KeenStandReact, &s_keenread5};\r
86 statetype s_keenread5     = {KEENREAD1SPR, KEENREAD1SPR, stepthink, false, true, 300, 0, 0, KeenReadThink, KeenContact, KeenStandReact, &s_keenread6};\r
87 statetype s_keenread6     = {KEENREAD2SPR, KEENREAD2SPR, stepthink, false, true, 16, 0, 0, KeenReadThink, KeenContact, KeenStandReact, &s_keenread7};\r
88 statetype s_keenread7     = {KEENREAD3SPR, KEENREAD3SPR, stepthink, false, true, 16, 0, 0, KeenReadThink, KeenContact, KeenStandReact, &s_keenread5};\r
89 statetype s_keenstopread  = {KEENSTOPREAD1SPR, KEENSTOPREAD1SPR, step, false, true, 12, 0, 0, 0, KeenContact, KeenStandReact, &s_keenstopread2};\r
90 statetype s_keenstopread2 = {KEENSTOPREAD2SPR, KEENSTOPREAD2SPR, step, false, true, 12, 0, 0, 0, KeenContact, KeenStandReact, &s_keenstopread3};\r
91 statetype s_keenstopread3 = {KEENSITREAD2SPR, KEENSITREAD2SPR, step, false, true, 12, 0, 0, 0, KeenContact, KeenStandReact, &s_keenstand};\r
92 \r
93 statetype s_keenlookup    = {KEENLOOKUSPR, KEENLOOKUSPR, stepthink, false, true, 30, 0, 0, KeenLookUpThink, KeenContact, KeenStandReact, &s_keenlookup2};\r
94 statetype s_keenlookup2   = {KEENLOOKUSPR, KEENLOOKUSPR, think, false, true, 0, 0, 0, KeenLookUpThink, KeenPosContact, KeenStandReact, NULL};\r
95 \r
96 statetype s_keenlookdown  = {KEENLOOKD1SPR, KEENLOOKD1SPR, stepthink, false, true, 6, 0, 0, KeenLookDownThink, KeenContact, KeenStandReact, &s_keenlookdown2};\r
97 statetype s_keenlookdown2 = {KEENLOOKD2SPR, KEENLOOKD2SPR, stepthink, false, true, 24, 0, 0, KeenLookDownThink, KeenPosContact, KeenStandReact, &s_keenlookdown3};\r
98 statetype s_keenlookdown3 = {KEENLOOKD2SPR, KEENLOOKD2SPR, think, false, true, 0, 0, 0, KeenLookDownThink, KeenPosContact, KeenStandReact, NULL};\r
99 statetype s_keenlookdown4 = {KEENLOOKD1SPR, KEENLOOKD1SPR, step, false, true, 6, 0, 0, 0, KeenContact, KeenStandReact, &s_keenstand};\r
100 \r
101 statetype s_keendrop   = {KEENLOOKD1SPR, KEENLOOKD1SPR, step, false, false, 0, 0, 0, KeenDropDownThink, KeenContact, KeenSimpleReact, &s_keenjump3};\r
102 statetype s_keendead   = {-1, -1, think, false, false, 10, 0, 0, 0, 0, R_Draw, NULL};\r
103 \r
104 statetype s_keendie1     = {KEENDIE1SPR, KEENDIE1SPR, think, false, false, 100, 0, 0, KeenDieThink, 0, R_Draw, &s_keendie1};\r
105 statetype s_keendie2     = {KEENDIE2SPR, KEENDIE2SPR, think, false, false, 100, 0, 0, KeenDieThink, 0, R_Draw, &s_keendie2};\r
106 \r
107 #ifdef KEEN4\r
108 statetype s_keensuitdie1 = {SCUBAKEENDEAD1SPR, SCUBAKEENDEAD1SPR, think, false, false, 100, 0, 0, KeenDieThink, NULL, R_Draw, &s_keensuitdie1};\r
109 statetype s_keensuitdie2 = {SCUBAKEENDEAD2SPR, SCUBAKEENDEAD2SPR, think, false, false, 100, 0, 0, KeenDieThink, NULL, R_Draw, &s_keensuitdie2};\r
110 #endif\r
111 \r
112 statetype s_keenshoot1    = {KEENSHOOTLSPR, KEENSHOOTRSPR, step, false, true, 9, 0, 0, KeenShootThink, KeenContact, KeenStandReact, &s_keenshoot2};\r
113 statetype s_keenshoot2    = {KEENSHOOTLSPR, KEENSHOOTRSPR, step, false, true, 6, 0, 0, 0, KeenContact, KeenStandReact, &s_keenstand};\r
114 \r
115 statetype s_keenshootup1  = {KEENSHOOTUSPR, KEENSHOOTUSPR, step, false, true, 9, 0, 0, KeenShootThink, KeenContact, KeenStandReact, &s_keenshootup2};\r
116 statetype s_keenshootup2  = {KEENSHOOTUSPR, KEENSHOOTUSPR, step, false, true, 6, 0, 0, 0, KeenContact, KeenStandReact, &s_keenlookup};\r
117 \r
118 statetype s_keenswitch  = {KEENENTER1SPR, KEENENTER1SPR, step, false, true, 8, 0, 0, KeenSwitchThink, NULL, KeenStandReact, &s_keenswitch2};\r
119 statetype s_keenswitch2 = {KEENENTER1SPR, KEENENTER1SPR, step, false, true, 8, 0, 0, 0, 0, KeenStandReact, &s_keenstand};\r
120 statetype s_keenkey     = {KEENENTER1SPR, KEENENTER1SPR, step, false, true, 6, 0, 0, KeenKeyThink, NULL, KeenStandReact, &s_keenswitch2};\r
121 \r
122 statetype s_keenlineup  = {KEENENTER1SPR, KEENENTER1SPR, think, false, false, 0, 0, 0, T_LineUp, 0, R_Draw, NULL};\r
123 #ifdef KEEN5\r
124 statetype s_keenenter0   = {KEENENTER1SPR, KEENENTER1SPR, step, false, false, 45, 0, -64, NULL, NULL, R_Draw, &s_keenenter1};\r
125 statetype s_keenteleport = {KEENWAITR2SPR, KEENWAITR2SPR, think, false, false, 0, 0, 0, NULL, NULL, R_Draw, NULL};\r
126 #endif\r
127 \r
128 statetype s_keenenter1 = {KEENENTER1SPR, KEENENTER1SPR, step, false, false, 9, 0, -64, WalkSound1, NULL, R_Draw, &s_keenenter2};\r
129 statetype s_keenenter2 = {KEENENTER2SPR, KEENENTER2SPR, step, false, false, 9, 0, -64, WalkSound2, NULL, R_Draw, &s_keenenter3};\r
130 statetype s_keenenter3 = {KEENENTER3SPR, KEENENTER3SPR, step, false, false, 9, 0, -64, WalkSound1, NULL, R_Draw, &s_keenenter4};\r
131 statetype s_keenenter4 = {KEENENTER4SPR, KEENENTER4SPR, step, false, false, 9, 0, -64, WalkSound2, NULL, R_Draw, &s_keenenter5};\r
132 statetype s_keenenter5 = {KEENENTER5SPR, KEENENTER5SPR, step, false, false, 9, 0, -64, KeenEnterThink, NULL, R_Draw, &s_keenstand};\r
133 #ifdef KEEN5\r
134 statetype s_keenenter6 = {-1, -1, step, false, false, 9, 0, -64, KeenEnterThink, 0, R_Draw, &s_keenstand};\r
135 #endif\r
136 \r
137 statetype s_keenpole      = {KEENSHINNYL1SPR, KEENSHINNYR1SPR, think, false, false, 0, 0, 0, KeenPoleThink, KeenPosContact, KeenSimpleReact, &s_keenpole};\r
138 \r
139 statetype s_keenclimb1 = {KEENSHINNYL1SPR, KEENSHINNYR1SPR, slidethink, false, false, 8, 0, 8, KeenClimbThink, KeenPosContact, KeenSimpleReact, &s_keenclimb2};\r
140 statetype s_keenclimb2 = {KEENSHINNYL2SPR, KEENSHINNYR2SPR, slidethink, false, false, 8, 0, 8, KeenClimbThink, KeenPosContact, KeenSimpleReact, &s_keenclimb3};\r
141 statetype s_keenclimb3 = {KEENSHINNYL3SPR, KEENSHINNYR3SPR, slidethink, false, false, 8, 0, 8, KeenClimbThink, KeenPosContact, KeenSimpleReact, &s_keenclimb1};\r
142 \r
143 statetype s_keenslide1 = {KEENSLIDED1SPR, KEENSLIDED1SPR, slide, false, false, 8, 0, 24, KeenDropThink, KeenPosContact, KeenPoleReact, &s_keenslide2};\r
144 statetype s_keenslide2 = {KEENSLIDED2SPR, KEENSLIDED2SPR, slide, false, false, 8, 0, 24, KeenDropThink, KeenPosContact, KeenPoleReact, &s_keenslide3};\r
145 statetype s_keenslide3 = {KEENSLIDED3SPR, KEENSLIDED3SPR, slide, false, false, 8, 0, 24, KeenDropThink, KeenPosContact, KeenPoleReact, &s_keenslide4};\r
146 statetype s_keenslide4 = {KEENSLIDED4SPR, KEENSLIDED4SPR, slide, false, false, 8, 0, 24, KeenDropThink, KeenPosContact, KeenPoleReact, &s_keenslide1};\r
147 \r
148 statetype s_keenpoleshoot1 = {KEENPSHOOTLSPR, KEENPSHOOTRSPR, step, false, false, 9, 0, 0, KeenShootThink, KeenPosContact, KeenSimpleReact, &s_keenpoleshoot2};\r
149 statetype s_keenpoleshoot2 = {KEENPSHOOTLSPR, KEENPSHOOTRSPR, step, false, false, 6, 0, 0, 0, KeenPosContact, KeenSimpleReact, &s_keenpole};\r
150 \r
151 statetype s_keenpoleshootup1 = {KEENPLSHOOTUSPR, KEENPRSHOOTUSPR, step, false, false, 9, 0, 0, KeenShootThink, KeenPosContact, KeenSimpleReact, &s_keenpoleshootup2};\r
152 statetype s_keenpoleshootup2 = {KEENPLSHOOTUSPR, KEENPRSHOOTUSPR, step, false, false, 6, 0, 0, 0, KeenPosContact, KeenSimpleReact, &s_keenpole};\r
153 \r
154 statetype s_keenpoleshootdown1 = {KEENPLSHOOTDSPR, KEENPRSHOOTDSPR, step, false, false, 9, 0, 0, KeenShootThink, KeenPosContact, KeenSimpleReact, &s_keenpoleshootdown2};\r
155 statetype s_keenpoleshootdown2 = {KEENPLSHOOTDSPR, KEENPRSHOOTDSPR, step, false, false, 6, 0, 0, 0, KeenPosContact, KeenSimpleReact, &s_keenpole};\r
156 \r
157 statetype s_keenwalk1     = {KEENRUNL1SPR, KEENRUNR1SPR, slidethink, true, true, 6, 24, 0, KeenWalkThink, KeenContact, KeenWalkReact, &s_keenwalk2};\r
158 statetype s_keenwalk2     = {KEENRUNL2SPR, KEENRUNR2SPR, slidethink, true, true, 6, 24, 0, KeenWalkThink, KeenContact, KeenWalkReact, &s_keenwalk3};\r
159 statetype s_keenwalk3     = {KEENRUNL3SPR, KEENRUNR3SPR, slidethink, true, true, 6, 24, 0, KeenWalkThink, KeenContact, KeenWalkReact, &s_keenwalk4};\r
160 statetype s_keenwalk4     = {KEENRUNL4SPR, KEENRUNR4SPR, slidethink, true, true, 6, 24, 0, KeenWalkThink, KeenContact, KeenWalkReact, &s_keenwalk1};\r
161 \r
162 statetype s_keenpogodown  = {KEENPOGOL2SPR, KEENPOGOR2SPR, step, true, false, 1, 0, 0, KeenBounceThink, KeenContact, KeenPogoReact, &s_keenpogo};\r
163 statetype s_keenpogo      = {KEENPOGOL2SPR, KEENPOGOR2SPR, think, true, false, 0, 0, 0, KeenPogoThink, KeenContact, KeenPogoReact, &s_keenpogo2};\r
164 statetype s_keenpogo2     = {KEENPOGOL1SPR, KEENPOGOR1SPR, think, true, false, 0, 0, 0, KeenPogoThink, KeenContact, KeenPogoReact, NULL};\r
165 \r
166 statetype s_keenjump1     = {KEENJUMPL1SPR, KEENJUMPR1SPR, think, false, false, 0, 0, 0, KeenAirThink, KeenContact, KeenAirReact, &s_keenjump2};\r
167 statetype s_keenjump2     = {KEENJUMPL2SPR, KEENJUMPR2SPR, think, false, false, 0, 0, 0, KeenAirThink, KeenContact, KeenAirReact, &s_keenjump3};\r
168 statetype s_keenjump3     = {KEENJUMPL3SPR, KEENJUMPR3SPR, stepthink, false, false, 50, 0, 0, KeenAirThink, KeenContact, KeenAirReact, &s_keenjump4};\r
169 statetype s_keenjump4     = {KEENJUMPL2SPR, KEENJUMPR2SPR, stepthink, false, false, 40, 0, 0, KeenAirThink, KeenContact, KeenAirReact, &s_keenjump3};\r
170 \r
171 statetype s_keenairshoot1 = {KEENJLSHOOTLSPR, KEENJRSHOOTRSPR, stepthink, false, false, 9, 0, 0, T_Projectile, KeenContact, KeenAirReact, &s_keenairshoot2};\r
172 statetype s_keenairshoot2 = {KEENJLSHOOTLSPR, KEENJRSHOOTRSPR, stepthink, true, false, 1, 0, 0, KeenShootThink, KeenContact, KeenAirReact, &s_keenairshoot3};\r
173 statetype s_keenairshoot3 = {KEENJLSHOOTLSPR, KEENJRSHOOTRSPR, stepthink, false, false, 6, 0, 0, T_Projectile, KeenContact, KeenAirReact, &s_keenjump3};\r
174 \r
175 statetype s_keenairshootup1 = {KEENJSHOOTUSPR, KEENJSHOOTUSPR, stepthink, false, false, 9, 0, 0, T_Projectile, KeenContact, KeenAirReact, &s_keenairshootup2};\r
176 statetype s_keenairshootup2 = {KEENJSHOOTUSPR, KEENJSHOOTUSPR, stepthink, true, false, 1, 0, 0, KeenShootThink, KeenContact, KeenAirReact, &s_keenairshootup3};\r
177 statetype s_keenairshootup3 = {KEENJSHOOTUSPR, KEENJSHOOTUSPR, stepthink, false, false, 6, 0, 0, T_Projectile, KeenContact, KeenAirReact, &s_keenjump3};\r
178 \r
179 statetype s_keenairshootdown1 = {KEENJSHOOTDSPR, KEENJSHOOTDSPR, stepthink, false, false, 9, 0, 0, T_Projectile, KeenContact, KeenAirReact, &s_keenairshootdown2};\r
180 statetype s_keenairshootdown2 = {KEENJSHOOTDSPR, KEENJSHOOTDSPR, stepthink, true, false, 1, 0, 0, KeenShootThink, KeenContact, KeenAirReact, &s_keenairshootdown3};\r
181 statetype s_keenairshootdown3 = {KEENJSHOOTDSPR, KEENJSHOOTDSPR, stepthink, false, false, 6, 0, 0, T_Projectile, KeenContact, KeenAirReact, &s_keenjump3};\r
182 \r
183 statetype s_keenholdon  = {KEENHANGLSPR, KEENHANGRSPR, step, false, false, 12, 0, 0, 0, KeenPosContact, KeenSimpleReact, &s_keenholdon2};\r
184 statetype s_keenholdon2 = {KEENHANGLSPR, KEENHANGRSPR, think, false, false, 0, 0, 0, KeenHoldThink, KeenPosContact, KeenSimpleReact, NULL};\r
185 \r
186 statetype s_keenclimbup  = {KEENCLIMBEDGEL1SPR, KEENCLIMBEDGER1SPR, step, false, false, 10, 0, 0, T_PullUp1, KeenPosContact, KeenSimpleReact, &s_keenclimbup2};\r
187 statetype s_keenclimbup2 = {KEENCLIMBEDGEL2SPR, KEENCLIMBEDGER2SPR, step, false, false, 10, 0, 0, T_PullUp2, KeenPosContact, KeenSimpleReact, &s_keenclimbup3};\r
188 statetype s_keenclimbup3 = {KEENCLIMBEDGEL3SPR, KEENCLIMBEDGER3SPR, step, false, false, 10, 0, 0, T_PullUp3, KeenPosContact, KeenSimpleReact, &s_keenclimbup4};\r
189 statetype s_keenclimbup4 = {KEENCLIMBEDGEL4SPR, KEENCLIMBEDGER4SPR, step, false, false, 10, 0, 0, T_PulledUp, KeenPosContact, KeenSimpleReact, &s_keenclimbup5};\r
190 statetype s_keenclimbup5 = {KEENSTANDLSPR, KEENSTANDRSPR, step, false, false, 6, 0, 0, 0, KeenPosContact, KeenSimpleReact, &s_keenstand};\r
191 \r
192 Sint16 slopespeed[8] = {0, 0, 4, 4, 8, -4, -4, -8};\r
193 Sint16 polexspeed[3] = {-8, 0, 8};\r
194 \r
195 Sint16 shotsinclip[4] = {0, 8, 5, 5};\r
196 Sint16 bonussound[] = {\r
197         SND_GETKEY,SND_GETKEY,SND_GETKEY,SND_GETKEY,\r
198         SND_GETPOINTS,SND_GETPOINTS,SND_GETPOINTS,\r
199         SND_GETPOINTS,SND_GETPOINTS,SND_GETPOINTS,\r
200         SND_EXTRAKEEN,\r
201         SND_GETAMMO\r
202 #ifdef KEEN5\r
203         ,SND_GETKEYCARD\r
204 #endif\r
205 };\r
206 Sint16 bonuspoints[] = {\r
207         0, 0, 0, 0,\r
208         100, 200, 500,\r
209         1000, 2000, 5000,\r
210         0,\r
211         0\r
212 #ifdef KEEN5\r
213         ,0\r
214 #endif\r
215 };\r
216 Sint16 bonussprite[] = {\r
217         BONUSGEMSPR, BONUSGEMSPR, BONUSGEMSPR, BONUSGEMSPR,\r
218         BONUS100SPR, BONUS200SPR, BONUS500SPR,\r
219         BONUS1000SPR, BONUS2000SPR, BONUS5000SPR,\r
220         BONUS1UPSPR,\r
221         BONUSCLIPSPR\r
222 #ifdef KEEN5\r
223         ,BONUSCARDSPR\r
224 #endif\r
225 };\r
226 \r
227 Uint16 zeromap = 0;\r
228 \r
229 // uninitialized variables:\r
230 \r
231 /*\r
232 =============================================================================\r
233 \r
234                                                  LOCAL VARIABLES\r
235 \r
236 =============================================================================\r
237 */\r
238 \r
239 Sint16 jumptime;\r
240 Sint32 leavepoletime;\r
241 Sint16 moonok;\r
242 \r
243 /*\r
244 =============================================================================\r
245 \r
246                                                                 KEEN\r
247 \r
248 player->temp1 = pausetime / pointer to zees when sleeping\r
249 player->temp2 = pausecount / stagecount\r
250 player->temp3 =\r
251 player->temp4 =\r
252 \r
253 =============================================================================\r
254 */\r
255 \r
256 \r
257 /*\r
258 ===============\r
259 =\r
260 = SpawnKeen\r
261 =\r
262 ===============\r
263 */\r
264 \r
265 void SpawnKeen(Sint16 x, Sint16 y, Sint16 dir)\r
266 {\r
267         player->obclass = keenobj;\r
268         player->active = ac_allways;\r
269         player->priority = 1;\r
270         player->x = CONVERT_TILE_TO_GLOBAL(x);\r
271         player->y = CONVERT_TILE_TO_GLOBAL(y) - 0xF1;   //TODO: weird\r
272 \r
273         player->xdir = dir;\r
274         player->ydir = 1;\r
275         NewState(player, &s_keenstand);\r
276 }\r
277 \r
278 //==========================================================================\r
279 \r
280 /*\r
281 ======================\r
282 =\r
283 = CheckGrabPole\r
284 =\r
285 ======================\r
286 */\r
287 \r
288 boolean CheckGrabPole(objtype *ob)\r
289 {\r
290         Uint16 far *map;\r
291 \r
292 //\r
293 // kludgy bit to not let you grab a pole the instant you jump off it\r
294 //\r
295         if (lasttimecount < leavepoletime)\r
296         {\r
297                 leavepoletime = 0;\r
298         }\r
299         else if (lasttimecount-leavepoletime < 19)\r
300         {\r
301                 return false;\r
302         }\r
303 \r
304         if (c.yaxis == -1)\r
305         {\r
306                 map = mapsegs[1] + mapbwidthtable[(ob->top+6*PIXGLOBAL)/TILEGLOBAL]/2;\r
307         }\r
308         else\r
309         {\r
310                 map = mapsegs[1] + mapbwidthtable[ob->tilebottom+1]/2;\r
311         }\r
312 \r
313         map += ob->tilemidx;\r
314 \r
315         if ((tinf[INTILE + *map] & 0x7F) == INTILE_POLE)\r
316         {\r
317                 ob->x = CONVERT_TILE_TO_GLOBAL(ob->tilemidx-1) + 8*PIXGLOBAL;\r
318                 xtry = 0;\r
319                 ytry = c.yaxis * 32;\r
320                 ob->needtoclip = cl_noclip;             // can climb through pole holes\r
321                 ob->state = &s_keenpole;\r
322                 return true;\r
323         }\r
324         return false;\r
325 }\r
326 \r
327 //==========================================================================\r
328 \r
329 /*\r
330 ======================\r
331 =\r
332 = CheckEnterHouse\r
333 =\r
334 = Checks for tiles that Keen can interact with by pressing up\r
335 =\r
336 ======================\r
337 */\r
338 \r
339 boolean CheckEnterHouse(objtype *ob)\r
340 {\r
341         Uint16 temp;\r
342 #ifdef KEEN5\r
343         Uint16 infoval;\r
344 #endif\r
345         Uint16 intile, intile2;\r
346 \r
347         intile = tinf[INTILE + *(mapsegs[1]+mapbwidthtable[ob->tiletop]/2+ob->tilemidx)];\r
348         if (intile == INTILE_SWITCH0 || intile == INTILE_SWITCH1 || intile == INTILE_BRIDGESWITCH)\r
349         {\r
350                 temp = CONVERT_TILE_TO_GLOBAL(ob->tilemidx) - 4*PIXGLOBAL;\r
351                 if (ob->x != temp)\r
352                 {\r
353                         ob->temp1 = temp;\r
354                         ob->state = &s_keenlineup;\r
355                 }\r
356                 else\r
357                 {\r
358                         ob->state = &s_keenswitch;\r
359                 }\r
360                 upheld = true;\r
361                 return true;\r
362         }\r
363         else if (intile == INTILE_DOOR || intile == INTILE_KEYCARDDOOR)\r
364         {\r
365                 temp = CONVERT_TILE_TO_GLOBAL(ob->tilemidx) + 6*PIXGLOBAL;\r
366                 intile2 = tinf[INTILE + *(mapsegs[1]+mapbwidthtable[ob->tiletop]/2+ob->tilemidx-1)];\r
367                 if (intile2 == 2 || intile2 == 32)\r
368                         temp -= TILEGLOBAL;\r
369 \r
370                 // BUG:\r
371                 //\r
372                 // The s_keenenter? states rely on Keen's ydir being set to 1,\r
373                 // which may not always be the case (e.g. if Keen was pushed off\r
374                 // a pole by another actor in the level).\r
375                 // If ydir is not 1, Keen will not move up during that animation\r
376                 // which means the teleport coordinates won't be read from the\r
377                 // intended tile position and Keen might end up teleporting to\r
378                 // position 0, 0 in the map and thus win the current level.\r
379                 // \r
380                 // That can easily be avoided by setting ob->ydir to 1 when\r
381                 // changing ob->state to s_keenenter0 or s_keenenter1.\r
382 \r
383                 if (ob->x != temp)\r
384                 {\r
385                         ob->temp1 = temp;\r
386                         ob->state = &s_keenlineup;\r
387                 }\r
388 #ifdef KEEN5\r
389                 else if (intile == INTILE_KEYCARDDOOR)\r
390                 {\r
391                         if (gamestate.keycard)\r
392                         {\r
393                                 gamestate.keycard = false;\r
394                                 SD_PlaySound(SND_OPENCARDDOOR);\r
395                                 GetNewObj(false);\r
396                                 new->x = ob->tilemidx - 2;\r
397                                 new->y = ob->tilebottom - 4;\r
398                                 new->active = ac_allways;\r
399                                 new->needtoclip = cl_noclip;\r
400                                 new->obclass = inertobj;\r
401                                 NewState(new, &s_carddoor);\r
402                                 // Note: no invincibility here - card doors were always used as level exits in Keen 5\r
403                                 ob->state = &s_keenenter0;\r
404                                 ob->priority = 0;\r
405                                 upheld = true;\r
406                                 return true;\r
407                         }\r
408                         else\r
409                         {\r
410                                 SD_PlaySound(SND_NOWAY);\r
411                                 ob->state = &s_keenstand;\r
412                                 upheld = true;\r
413                                 return false;\r
414                         }\r
415                 }\r
416 #endif\r
417                 else\r
418                 {\r
419                         invincible = 110;       //about 1.57 seconds\r
420                         ob->state = &s_keenenter1;\r
421                         ob->priority = 0;\r
422 #ifdef KEEN5\r
423                         {\r
424                                 infoval = *(mapsegs[2]+mapbwidthtable[ob->tiletop]/2+ob->tilemidx);\r
425                                 if (!infoval)\r
426                                         SpawnTeleport();\r
427                         }\r
428 #endif\r
429                 }\r
430                 upheld = true;\r
431                 return true;\r
432         }\r
433         return false;\r
434 }\r
435 \r
436 //==========================================================================\r
437 \r
438 /*\r
439 ============================\r
440 =\r
441 = WalkSound1\r
442 =\r
443 ============================\r
444 */\r
445 \r
446 void WalkSound1(objtype *ob)\r
447 {\r
448         SD_PlaySound(SND_WORLDWALK1);\r
449         ob++;                   // shut up compiler\r
450 }\r
451 \r
452 /*\r
453 ============================\r
454 =\r
455 = WalkSound2\r
456 =\r
457 ============================\r
458 */\r
459 \r
460 void WalkSound2(objtype *ob)\r
461 {\r
462         SD_PlaySound(SND_WORLDWALK2);\r
463         ob++;                   // shut up compiler\r
464 }\r
465 \r
466 //==========================================================================\r
467 \r
468 /*\r
469 ============================\r
470 =\r
471 = KeenStandThink\r
472 =\r
473 ============================\r
474 */\r
475 \r
476 void KeenStandThink(objtype *ob)\r
477 {\r
478 #ifdef KEEN5\r
479         if (ob->hitnorth == 25 && ob->state != &s_keenride)\r
480         {\r
481                 ob->state = &s_keenride;\r
482         }\r
483 #endif\r
484 \r
485         if (c.xaxis)\r
486         {\r
487         // started walking\r
488                 ob->state = &s_keenwalk1;\r
489                 KeenWalkThink(ob);\r
490                 xtry = (Sint16)(ob->xdir * ob->state->xmove * tics) / 4;\r
491         }\r
492         else if (firebutton && !fireheld)\r
493         {\r
494         // shoot current xdir\r
495                 fireheld = true;\r
496                 if (c.yaxis == -1)\r
497                 {\r
498                         ob->state = &s_keenshootup1;\r
499                 }\r
500                 else\r
501                 {\r
502                         ob->state = &s_keenshoot1;\r
503                 }\r
504         }\r
505         else if (jumpbutton && ! jumpheld)\r
506         {\r
507         // jump straight up\r
508                 jumpheld = true;\r
509                 SD_PlaySound(SND_JUMP);\r
510                 ob->xspeed = 0;\r
511                 ob->yspeed = -40;\r
512                 ytry = 0;\r
513                 jumptime = 18;\r
514                 ob->state = &s_keenjump1;\r
515         }\r
516         else if (pogobutton && !pogoheld)\r
517         {\r
518         // get on pogo\r
519                 pogoheld = true;\r
520                 SD_PlaySound(SND_JUMP);\r
521                 ob->state = &s_keenpogodown;\r
522                 ob->xspeed = 0;\r
523                 ob->yspeed = -48;\r
524                 ytry = 0;\r
525                 jumptime = 24;\r
526         }\r
527         else\r
528         {\r
529                 switch (c.yaxis)\r
530                 {\r
531                 case -1:\r
532                         if (CheckGrabPole(ob))\r
533                                 break;\r
534                         if (upheld || !CheckEnterHouse(ob))\r
535                         {\r
536                                 ob->state = &s_keenlookup;\r
537                         }\r
538                         break;\r
539                 case 1:\r
540                         if (CheckGrabPole(ob))\r
541                                 break;\r
542                         ob->state = &s_keenlookdown;\r
543                 }\r
544                 return;\r
545         }\r
546 }\r
547 \r
548 //===========================================================================\r
549 \r
550 /*\r
551 =======================\r
552 =\r
553 = KeenPauseThink\r
554 =\r
555 = Do special animations in time\r
556 =\r
557 =======================\r
558 */\r
559 \r
560 void KeenPauseThink(objtype *ob)\r
561 {\r
562 #ifdef KEEN5\r
563         if (ob->hitnorth == 25 && ob->state != &s_keenride)\r
564         {\r
565                 ob->state = &s_keenride;\r
566         }\r
567 #endif\r
568 \r
569         if (c.dir != dir_None || jumpbutton || pogobutton || firebutton)\r
570         {\r
571                 ob->temp1 = ob->temp2 = 0;                      // not paused any more\r
572                 ob->state = &s_keenstand;\r
573                 KeenStandThink(ob);\r
574         }\r
575         else\r
576         {\r
577                 //only increase idle counter when NOT standing on a sprite:\r
578                 if ((ob->hitnorth & ~7) != 0x18)\r
579                         ob->temp1 = ob->temp1 + tics;\r
580 \r
581                 switch (ob->temp2)\r
582                 {\r
583                 case 0:\r
584                         if (ob->temp1 > 200)\r
585                         {\r
586                                 ob->temp2++;\r
587                                 ob->state = &s_keenpauselook;\r
588                                 ob->temp1 = 0;\r
589                         }\r
590                         break;\r
591                 case 1:\r
592                         if (ob->temp1 > 300)\r
593                         {\r
594                                 ob->temp2++;\r
595                                 ob->temp1 = 0;\r
596 #ifdef KEEN4\r
597                                 if (moonok == 1)\r
598                                 {\r
599                                         moonok = 2;     //don't moon again unless the level is restarted\r
600                                         ob->state = &s_keenmoon1;\r
601                                 }\r
602                                 else\r
603 #endif\r
604                                 {\r
605                                         ob->state = &s_keenwait1;\r
606                                 }\r
607                         }\r
608                         break;\r
609                 case 2:\r
610                         if (ob->temp1 > 700)\r
611                         {\r
612                                 ob->temp2++;\r
613                                 ob->state = &s_keenread;\r
614                                 ob->temp1 = 0;\r
615                         }\r
616                         break;\r
617                 }\r
618         }\r
619 }\r
620 \r
621 //===========================================================================\r
622 \r
623 /*\r
624 =======================\r
625 =\r
626 = KeenReadThink\r
627 =\r
628 =======================\r
629 */\r
630 \r
631 void KeenReadThink(objtype *ob)\r
632 {\r
633         if (storedemo)\r
634         {\r
635                 playstate = ex_abortgame;\r
636                 IN_ClearKeysDown();\r
637         }\r
638         if (c.dir != dir_None || jumpbutton || pogobutton)\r
639         {\r
640                 ob->temp1 = ob->temp2 = 0;\r
641                 ob->state = &s_keenstopread;\r
642         }\r
643 }\r
644 \r
645 //===========================================================================\r
646 \r
647 /*\r
648 =======================\r
649 =\r
650 = KeenLookUpThink\r
651 =\r
652 =======================\r
653 */\r
654 \r
655 void KeenLookUpThink(objtype *ob)\r
656 {\r
657         if (c.yaxis != -1 || c.xaxis\r
658                 || (jumpbutton && !jumpheld)\r
659                 || (pogobutton && !pogoheld)\r
660                 || firebutton)\r
661         {\r
662                 ob->state = &s_keenstand;\r
663                 KeenStandThink(ob);\r
664         }\r
665 }\r
666 \r
667 //===========================================================================\r
668 \r
669 /*\r
670 =======================\r
671 =\r
672 = KeenLookDownThink\r
673 =\r
674 =======================\r
675 */\r
676 \r
677 void KeenLookDownThink(objtype *ob)\r
678 {\r
679         Uint16 far *map;\r
680         Sint16 y, ymove;\r
681         Uint16 tile;\r
682 \r
683         if (jumpbutton && ! jumpheld && (ob->hitnorth & 7) == 1)\r
684         {\r
685         //\r
686         // drop down a level\r
687         //\r
688                 jumpheld = true;\r
689 \r
690                 y = ob->tilebottom;\r
691                 map = (Uint16 far *)mapsegs[1] + mapbwidthtable[y]/2 + ob->tilemidx;\r
692                 tile = *map;\r
693                 if (tinf[WESTWALL+tile] || tinf[EASTWALL+tile] || tinf[SOUTHWALL+tile])\r
694                         return;                         // wall prevents drop down\r
695 \r
696                 map += mapwidth;\r
697                 tile = *map;\r
698                 if (tinf[WESTWALL+tile] || tinf[EASTWALL+tile] || tinf[SOUTHWALL+tile])\r
699                         return;                         // wall prevents drop down\r
700 \r
701                 ymove = max(4, tics) * PIXGLOBAL;\r
702                 if (gamestate.riding)\r
703                         ymove += gamestate.riding->ymove;\r
704                 ob->bottom += ymove;\r
705                 gamestate.riding = NULL;\r
706                 ob->y += ymove;\r
707                 xtry = ytry = 0;\r
708                 ob->state = &s_keenjump3;\r
709                 ob->xspeed = ob->yspeed = 0;\r
710                 SD_PlaySound(SND_PLUMMET);\r
711         }\r
712         else if (c.yaxis != 1 || c.xaxis\r
713                 || (jumpbutton && !jumpheld)\r
714                 || (pogobutton && !pogoheld))\r
715         {\r
716                 ob->state = &s_keenlookdown4;\r
717         }\r
718 }\r
719 \r
720 //===========================================================================\r
721 \r
722 /*\r
723 =======================\r
724 =\r
725 = KeenWalkThink\r
726 =\r
727 =======================\r
728 */\r
729 \r
730 void KeenWalkThink(objtype *ob)\r
731 {\r
732         Sint16 xmove;\r
733 \r
734         if (c.xaxis == 0)\r
735         {\r
736         //\r
737         // stopped running\r
738         //\r
739                 ob->state = &s_keenstand;\r
740                 KeenStandThink(ob);\r
741                 return;\r
742         }\r
743 \r
744         ob->xdir = c.xaxis;\r
745 \r
746         switch (c.yaxis)\r
747         {\r
748         case -1:\r
749                 if (CheckGrabPole(ob))\r
750                         return;\r
751                 if (upheld)\r
752                         return;\r
753                 if (!CheckEnterHouse(ob))\r
754                         break;;\r
755                 return;\r
756 \r
757         case 1:\r
758                 if (!CheckGrabPole(ob))\r
759                         break;\r
760                 return;\r
761         }\r
762 \r
763         if (firebutton && !fireheld)\r
764         {\r
765         //\r
766         // shoot\r
767         //\r
768                 fireheld = true;\r
769                 if (c.yaxis == -1)\r
770                 {\r
771                         ob->state = &s_keenshootup1;\r
772                 }\r
773                 else\r
774                 {\r
775                         ob->state = &s_keenshoot1;\r
776                 }\r
777                 return;\r
778         }\r
779 \r
780         if (jumpbutton && !jumpheld)\r
781         {\r
782         //\r
783         // running jump\r
784         //\r
785                 jumpheld = true;\r
786                 SD_PlaySound(SND_JUMP);\r
787                 ob->xspeed = ob->xdir * 16;\r
788                 ob->yspeed = -40;\r
789                 xtry = ytry = 0;\r
790                 jumptime = 18;\r
791                 ob->state = &s_keenjump1;\r
792         }\r
793 \r
794         if (pogobutton && !pogoheld)\r
795         {\r
796         //\r
797         // get on pogo\r
798         //\r
799                 pogoheld = true;\r
800                 ob->state = &s_keenpogodown;\r
801                 SD_PlaySound(SND_JUMP);\r
802                 ob->xspeed = ob->xdir * 16;\r
803                 ob->yspeed = -48;\r
804                 xtry = 0;\r
805                 jumptime = 24;\r
806                 return;\r
807         }\r
808 \r
809         //\r
810         // give speed for slopes\r
811         //\r
812         xmove = slopespeed[ob->hitnorth & 7] * tics;\r
813         xtry += xmove;\r
814 \r
815         //\r
816         // handle walking sounds\r
817         //\r
818         if (ob->state == &s_keenwalk1 && ob->temp3 == 0)\r
819         {\r
820                 SD_PlaySound(SND_WORLDWALK1);\r
821                 ob->temp3 = 1;\r
822         }\r
823         else if (ob->state == &s_keenwalk3 && ob->temp3 == 0)\r
824         {\r
825                 SD_PlaySound(SND_WORLDWALK2);\r
826                 ob->temp3 = 1;\r
827         }\r
828         else if (ob->state == &s_keenwalk2 ||ob->state == &s_keenwalk4)\r
829         {\r
830                 ob->temp3 = 0;\r
831         }\r
832 }\r
833 \r
834 //===========================================================================\r
835 \r
836 /*\r
837 =======================\r
838 =\r
839 = T_LineUp\r
840 =\r
841 = Lines up Keen's position for interacting with tiles (temp1 is desired x)\r
842 =\r
843 =======================\r
844 */\r
845 \r
846 void T_LineUp(objtype *ob)\r
847 {\r
848         Sint16 xmove;\r
849 \r
850         xmove = ob->temp1 - ob->x;\r
851         if (xmove < 0)\r
852         {\r
853                 xtry = xtry - tics * 16;\r
854                 if (xtry > xmove)\r
855                         return;\r
856         }\r
857         else if (xmove > 0)\r
858         {\r
859                 xtry = xtry + tics * 16;\r
860                 if (xtry < xmove)\r
861                         return;\r
862         }\r
863         xtry = xmove;\r
864         ob->temp1 = 0;\r
865         if (!CheckEnterHouse(ob))\r
866                 ob->state = &s_keenstand;\r
867 }\r
868 \r
869 //===========================================================================\r
870 \r
871 /*\r
872 =======================\r
873 =\r
874 = KeenEnterThink\r
875 =\r
876 =======================\r
877 */\r
878 \r
879 void KeenEnterThink(objtype *ob)\r
880 {\r
881         Uint16 info;\r
882         Uint16 far *map;\r
883 \r
884         map = mapsegs[2] + mapbwidthtable[ob->tilebottom]/2 + ob->tileleft;\r
885         info = *map;\r
886 #ifdef KEEN5\r
887         if (!info)\r
888         {\r
889                 playstate = ex_portout;\r
890                 ob->state = &s_keenenter6;\r
891                 return;\r
892         }\r
893         else if (info == 0xB1B1)\r
894         {\r
895                 playstate = ex_completed;\r
896                 ob->state = &s_keenenter6;\r
897                 return;\r
898         }\r
899 #endif\r
900         ob->y = (CONVERT_TILE_TO_GLOBAL(info & 0xFF) - TILEGLOBAL) + 15;\r
901         ob->x = CONVERT_TILE_TO_GLOBAL(info >> 8);\r
902         ob->priority = 1;\r
903         ob->needtoclip = cl_noclip;\r
904         ChangeState(ob, ob->state->nextstate);\r
905         ob->needtoclip = cl_midclip;\r
906         CenterActor(ob);\r
907 }\r
908 \r
909 //===========================================================================\r
910 \r
911 /*\r
912 =======================\r
913 =\r
914 = KeenSwitchThink\r
915 =\r
916 =======================\r
917 */\r
918 \r
919 void KeenSwitchThink(objtype *ob)\r
920 {\r
921         Uint16 intile, maptile, newtile, info, sx, sy, tileoff;\r
922         Uint16 far *map;\r
923         Uint16 tile, x, y;\r
924         Sint8 manim;\r
925 \r
926         tileoff = mapbwidthtable[ob->tiletop]/2 + ob->tilemidx;\r
927         maptile = mapsegs[1][tileoff];\r
928         newtile = maptile + (Sint8)tinf[MANIM + maptile];\r
929         info = mapsegs[2][tileoff];\r
930         sx = info >> 8;\r
931         sy = info & 0xFF;\r
932         intile = tinf[INTILE + maptile];\r
933 \r
934         RF_MemToMap(&newtile, 1, ob->tilemidx, ob->tiletop, 1, 1);\r
935         SD_PlaySound(SND_USESWITCH);\r
936         if (intile == INTILE_BRIDGESWITCH)\r
937         {\r
938                 //toggle bridge:\r
939                 for (y = sy; sy+2 > y; y++)\r
940                 {\r
941                         map = mapsegs[1] + mapbwidthtable[y]/2 + sx - (y != sy);\r
942                         for (x = sx - (y != sy); x < mapwidth; x++)\r
943                         {\r
944                                 tile = *(map++);\r
945                                 manim = tinf[MANIM + tile];\r
946                                 if (!manim)\r
947                                         break;\r
948 \r
949                                 tile += manim;\r
950                                 RF_MemToMap(&tile, 1, x, y, 1, 1);\r
951                         }\r
952                 }\r
953         }\r
954         else\r
955         {\r
956                 //toggle platform blocker:\r
957                 map = mapsegs[2] + mapbwidthtable[sy]/2 + sx;\r
958                 tile = *map;\r
959 #ifdef KEEN5\r
960                 if (tile >= DIRARROWSTART && tile < DIRARROWEND)\r
961                 {\r
962                         *map = arrowflip[tile-DIRARROWSTART]+DIRARROWSTART;\r
963                         return;\r
964                 }\r
965 #endif\r
966                 *map = tile ^ PLATFORMBLOCK;\r
967         }\r
968 }\r
969 \r
970 //===========================================================================\r
971 \r
972 /*\r
973 =======================\r
974 =\r
975 = KeenKeyThink\r
976 =\r
977 =======================\r
978 */\r
979 \r
980 void KeenKeyThink(objtype *ob)\r
981 {\r
982         Uint16 newtile, info, x, y, tileoff;\r
983         Uint16 far *map;\r
984         Uint16 tile, h;\r
985 \r
986         tileoff = mapbwidthtable[ob->tilebottom]/2 + ob->tilemidx;\r
987         newtile = mapsegs[1][tileoff] + 18;\r
988         info = mapsegs[2][tileoff];\r
989         x = info >> 8;\r
990         y = info & 0xFF;\r
991         RF_MemToMap(&newtile, 1, ob->tilemidx, ob->tilebottom, 1, 1);\r
992         SD_PlaySound(SND_OPENDOOR);\r
993         GetNewObj(false);\r
994         new->x = x;\r
995         new->y = y;\r
996 \r
997         if (x > mapwidth || x < 2 || y > mapheight || y < 2)\r
998                 Quit("Keyholder points to a bad spot!");\r
999 \r
1000         map = mapsegs[1] + mapbwidthtable[y]/2 + x;\r
1001         tile = *map;\r
1002         h = 1;\r
1003         map += mapwidth;\r
1004         while (*map == tile)\r
1005         {\r
1006                         h++;\r
1007                         map += mapwidth;\r
1008         }\r
1009         new->temp1 = h;\r
1010         new->active = ac_allways;\r
1011         new->needtoclip = cl_noclip;\r
1012         new->obclass = inertobj;\r
1013         NewState(new, &s_door1);\r
1014 }\r
1015 \r
1016 //===========================================================================\r
1017 \r
1018 /*\r
1019 =======================\r
1020 =\r
1021 = KeenAirThink\r
1022 =\r
1023 =======================\r
1024 */\r
1025 \r
1026 void KeenAirThink(objtype *ob)\r
1027 {\r
1028         if (jumpcheat && jumpbutton)\r
1029         {\r
1030                 ob->yspeed = -40;\r
1031                 jumptime = 18;\r
1032                 jumpheld = true;\r
1033         }\r
1034         if (jumptime)\r
1035         {\r
1036                 if (jumptime <= tics)\r
1037                 {\r
1038                         ytry = ob->yspeed * jumptime;\r
1039                         jumptime = 0;\r
1040                 }\r
1041                 else\r
1042                 {\r
1043                         ytry = ob->yspeed * tics;\r
1044                         if (!jumpcheat)\r
1045                                 jumptime = jumptime - tics;\r
1046                 }\r
1047                 if (!jumpbutton)\r
1048                         jumptime = 0;\r
1049 \r
1050                 if (jumptime == 0 && ob->state->nextstate)\r
1051                         ob->state = ob->state->nextstate;       // switch to second jump stage\r
1052         }\r
1053         else\r
1054         {\r
1055                 if (gamestate.difficulty == gd_Easy)\r
1056                 {\r
1057                         DoWeakGravity(ob);\r
1058                 }\r
1059                 else\r
1060                 {\r
1061                         DoGravity(ob);\r
1062                 }\r
1063                 if (ob->yspeed > 0 && ob->state != &s_keenjump3 && ob->state != &s_keenjump4)\r
1064                 {\r
1065                         ob->state = ob->state->nextstate;       // switch to third jump stage\r
1066                 }\r
1067         }\r
1068 \r
1069 //-------------\r
1070 \r
1071         if (c.xaxis)\r
1072         {\r
1073                 ob->xdir = c.xaxis;\r
1074                 AccelerateX(ob, c.xaxis*2, 24);\r
1075         }\r
1076         else\r
1077         {\r
1078                 FrictionX(ob);\r
1079         }\r
1080 \r
1081         if (ob->hitsouth == 17)         // going through a pole hole\r
1082         {\r
1083                 ob->xspeed = xtry = 0;\r
1084         }\r
1085 \r
1086         if (firebutton && !fireheld)\r
1087         {\r
1088                 fireheld = true;\r
1089         //\r
1090         // shoot\r
1091         //\r
1092                 switch (c.yaxis)\r
1093                 {\r
1094                 case -1:\r
1095                         ob->state = &s_keenairshootup1;\r
1096                         return;\r
1097                 case 0:\r
1098                         ob->state = &s_keenairshoot1;\r
1099                         return;\r
1100                 case 1:\r
1101                         ob->state = &s_keenairshootdown1;\r
1102                         return;\r
1103                 }\r
1104         }\r
1105 \r
1106         if (pogobutton && !pogoheld)\r
1107         {\r
1108                 pogoheld = true;\r
1109                 ob->state = &s_keenpogo;\r
1110                 jumptime = 0;\r
1111                 return;\r
1112         }\r
1113 \r
1114         if (c.yaxis == -1)\r
1115                 CheckGrabPole(ob);\r
1116 }\r
1117 \r
1118 //===========================================================================\r
1119 \r
1120 /*\r
1121 =======================\r
1122 =\r
1123 = KeenBounceThink\r
1124 =\r
1125 = Gives an extra bit of height on the first pogo bounce and creates\r
1126 = the "impossible pogo trick" when the jump key is held down\r
1127 =\r
1128 =======================\r
1129 */\r
1130 \r
1131 void KeenBounceThink(objtype *ob)\r
1132 {\r
1133         ob->yspeed = -48;\r
1134         ytry = ob->yspeed * 6;\r
1135         jumptime = 24;\r
1136         SD_PlaySound(SND_POGOBOUNCE);\r
1137 }\r
1138 \r
1139 //===========================================================================\r
1140 \r
1141 /*\r
1142 =======================\r
1143 =\r
1144 = KeenPogoThink\r
1145 =\r
1146 =======================\r
1147 */\r
1148 \r
1149 void KeenPogoThink(objtype *ob)\r
1150 {\r
1151         if (jumptime)\r
1152         {\r
1153                 if (jumpbutton || jumptime <= 9)\r
1154                 {\r
1155                         DoTinyGravity(ob);\r
1156                 }\r
1157                 else\r
1158                 {\r
1159                         DoGravity(ob);\r
1160                 }\r
1161                 if (jumptime <= tics)\r
1162                 {\r
1163                         jumptime = 0;\r
1164                 }\r
1165                 else\r
1166                 {\r
1167                         jumptime = jumptime - tics;\r
1168                 }\r
1169                 if (jumptime == 0 && ob->state->nextstate)\r
1170                         ob->state = ob->state->nextstate;\r
1171         }\r
1172         else\r
1173         {\r
1174                 if (gamestate.difficulty == gd_Easy)\r
1175                 {\r
1176                         DoWeakGravity(ob);\r
1177                 }\r
1178                 else\r
1179                 {\r
1180                         DoGravity(ob);\r
1181                 }\r
1182         }\r
1183 \r
1184         if (c.xaxis)\r
1185         {\r
1186                 if (ob->xspeed == 0)\r
1187                         ob->xdir = c.xaxis;\r
1188                 AccelerateX(ob, c.xaxis, 24);\r
1189         }\r
1190         else\r
1191         {\r
1192                 xtry = xtry + ob->xspeed * tics;\r
1193                 if (ob->xspeed > 0)\r
1194                 {\r
1195                         ob->xdir = 1;\r
1196                 }\r
1197                 else if (ob->xspeed < 0)\r
1198                 {\r
1199                         ob->xdir = -1;\r
1200                 }\r
1201         }\r
1202 \r
1203         if (ob->hitsouth == 17)         // going through a pole hole\r
1204         {\r
1205                 ob->xspeed = xtry = 0;\r
1206         }\r
1207 \r
1208         if (firebutton && !fireheld)\r
1209         {\r
1210                 fireheld = true;\r
1211                 switch (c.yaxis)\r
1212                 {\r
1213                 case -1:\r
1214                         ob->state = &s_keenairshootup1;\r
1215                         return;\r
1216                 case 0:\r
1217                         ob->state = &s_keenairshoot1;\r
1218                         return;\r
1219                 case 1:\r
1220                         ob->state = &s_keenairshootdown1;\r
1221                         return;\r
1222                 }\r
1223         }\r
1224 \r
1225         if (pogobutton && !pogoheld)\r
1226         {\r
1227                 pogoheld = true;\r
1228                 ob->state = &s_keenjump3;\r
1229         }\r
1230 }\r
1231 \r
1232 //===========================================================================\r
1233 \r
1234 /*\r
1235 =======================\r
1236 =\r
1237 = PoleActions\r
1238 =\r
1239 =======================\r
1240 */\r
1241 \r
1242 void PoleActions(objtype *ob)\r
1243 {\r
1244         if (c.xaxis)\r
1245                 ob->xdir = c.xaxis;\r
1246 \r
1247         if (firebutton && !fireheld)\r
1248         {\r
1249                 fireheld = true;\r
1250                 switch (c.yaxis)\r
1251                 {\r
1252                 case -1:\r
1253                         ob->state = &s_keenpoleshootup1;\r
1254                         break;\r
1255                 case 0:\r
1256                         ob->state = &s_keenpoleshoot1;\r
1257                         break;\r
1258                 case 1:\r
1259                         ob->state = &s_keenpoleshootdown1;\r
1260                         break;\r
1261                 }\r
1262         }\r
1263 \r
1264         if (jumpbutton && !jumpheld)            // jump off the pole\r
1265         {\r
1266                 jumpheld = true;\r
1267                 SD_PlaySound(SND_JUMP);\r
1268                 ob->xspeed = polexspeed[c.xaxis+1];\r
1269                 ob->yspeed = -20;\r
1270                 ob->needtoclip = cl_midclip;\r
1271                 jumptime = 10;\r
1272                 ob->state = &s_keenjump1;\r
1273                 ob->ydir = 1;\r
1274                 leavepoletime = lasttimecount;\r
1275         }\r
1276 }\r
1277 \r
1278 /*\r
1279 =======================\r
1280 =\r
1281 = KeenPoleThink\r
1282 =\r
1283 =======================\r
1284 */\r
1285 \r
1286 void KeenPoleThink(objtype *ob)\r
1287 {\r
1288         Uint16 tile;\r
1289         Uint16 far *map;\r
1290 \r
1291         switch (c.yaxis)\r
1292         {\r
1293         case -1:\r
1294                 ob->state = &s_keenclimb1;\r
1295                 ob->ydir = -1;\r
1296                 return;\r
1297         case 1:\r
1298                 ob->state = &s_keenslide1;\r
1299                 ob->ydir = 1;\r
1300                 KeenDropThink(ob);\r
1301                 return;\r
1302         }\r
1303 \r
1304         if (c.xaxis)\r
1305         {\r
1306         //\r
1307         // walk off pole if right next to ground\r
1308         //\r
1309                 map = mapsegs[1] + (mapbwidthtable[ob->tilebottom+1]/2 + ob->tilemidx);\r
1310                 tile = *map;\r
1311                 if (tinf[NORTHWALL+tile])\r
1312                 {\r
1313                         ob->xspeed = 0;\r
1314                         ob->yspeed = 0;\r
1315                         ob->needtoclip = cl_midclip;\r
1316                         jumptime = 0;\r
1317                         ob->state = &s_keenjump3;\r
1318                         ob->ydir = 1;\r
1319                         SD_PlaySound(SND_PLUMMET);\r
1320                         return;\r
1321                 }\r
1322         }\r
1323         PoleActions(ob);\r
1324 }\r
1325 \r
1326 /*\r
1327 =======================\r
1328 =\r
1329 = KeenClimbThink\r
1330 =\r
1331 =======================\r
1332 */\r
1333 \r
1334 void KeenClimbThink(objtype *ob)\r
1335 {\r
1336         Uint16 far *map;\r
1337 \r
1338         map = mapsegs[1] + mapbwidthtable[ob->tiletop]/2 + ob->tilemidx;\r
1339         \r
1340         if ((tinf[INTILE+*map] & 0x7F) != INTILE_POLE)\r
1341         {\r
1342                 ytry = 0;\r
1343                 ob->state = &s_keenpole;                // ran out of pole\r
1344                 PoleActions(ob);\r
1345                 return;\r
1346         }\r
1347 \r
1348         switch (c.yaxis)\r
1349         {\r
1350         case 0:\r
1351                 ob->state = &s_keenpole;\r
1352                 ob->ydir = 0;\r
1353                 break;\r
1354 \r
1355         case 1:\r
1356                 ob->state = &s_keenslide1;\r
1357                 ob->ydir = 1;\r
1358                 KeenDropThink(ob);\r
1359                 break;\r
1360         }\r
1361 \r
1362         PoleActions(ob);\r
1363 }\r
1364 \r
1365 /*\r
1366 =======================\r
1367 =\r
1368 = KeenDropThink\r
1369 =\r
1370 =======================\r
1371 */\r
1372 \r
1373 void KeenDropThink(objtype *ob)\r
1374 {\r
1375         Uint16 far *map;\r
1376         Uint16 y;\r
1377 \r
1378         y = CONVERT_GLOBAL_TO_TILE(ob->bottom - 4*PIXGLOBAL);\r
1379         map = mapsegs[1] + mapbwidthtable[y]/2 + ob->tilemidx;\r
1380 \r
1381         if ((tinf[INTILE+*map] & 0x7F) != INTILE_POLE)\r
1382         {\r
1383                 SD_PlaySound(SND_PLUMMET);\r
1384                 ob->state = &s_keenjump3;               // ran out of pole\r
1385                 jumptime = 0;\r
1386                 ob->xspeed = polexspeed[c.xaxis+1];\r
1387                 ob->yspeed = 0;\r
1388                 ob->needtoclip = cl_midclip;\r
1389                 ob->tilebottom--;\r
1390                 return;\r
1391         }\r
1392 \r
1393         switch (c.yaxis)\r
1394         {\r
1395         case -1:\r
1396                 ob->state = &s_keenclimb1;\r
1397                 ob->ydir = -1;\r
1398                 break;\r
1399 \r
1400         case 0:\r
1401                 ob->state = &s_keenpole;\r
1402                 ob->ydir = 0;\r
1403                 break;\r
1404         }\r
1405 \r
1406         PoleActions(ob);\r
1407 }\r
1408 \r
1409 //===========================================================================\r
1410 \r
1411 /*\r
1412 =======================\r
1413 =\r
1414 = KeenDropDownThink\r
1415 =\r
1416 =======================\r
1417 */\r
1418 \r
1419 void KeenDropDownThink(objtype *ob)\r
1420 {\r
1421         ob->needtoclip = cl_midclip;\r
1422 }\r
1423 \r
1424 //===========================================================================\r
1425 \r
1426 /*\r
1427 =======================\r
1428 =\r
1429 = KeenHoldThink\r
1430 =\r
1431 =======================\r
1432 */\r
1433 \r
1434 void KeenHoldThink(objtype *ob)\r
1435 {\r
1436         Uint16 tile;\r
1437 \r
1438         if (c.yaxis == -1 || ob->xdir == c.xaxis)\r
1439         {\r
1440                 ob->state = &s_keenclimbup;\r
1441                 if (ob->xdir == 1)\r
1442                 {\r
1443                         tile = *(mapsegs[1]+mapbwidthtable[ob->tiletop-1]/2+ob->tileright);\r
1444                 }\r
1445                 else\r
1446                 {\r
1447                         tile = *(mapsegs[1]+mapbwidthtable[ob->tiletop-1]/2+ob->tileleft);\r
1448                 }\r
1449                 if (ob->xdir == 1)\r
1450                 {\r
1451                         ytry = -16*PIXGLOBAL;\r
1452                 }\r
1453                 else\r
1454                 {\r
1455                         ytry = -8*PIXGLOBAL;\r
1456                 }\r
1457                 if (!(tinf[INTILE+tile] & INTILE_FOREGROUND))\r
1458                         ob->priority = 3;\r
1459         }\r
1460         else if (c.yaxis == 1 || c.xaxis && ob->xdir != c.xaxis)\r
1461         {\r
1462                 ob->state = &s_keenjump3;\r
1463                 ob->needtoclip = cl_midclip;\r
1464         }\r
1465 }\r
1466 \r
1467 //===========================================================================\r
1468 \r
1469 /*\r
1470 =======================\r
1471 =\r
1472 = KeenShootThink\r
1473 =\r
1474 =======================\r
1475 */\r
1476 \r
1477 void KeenShootThink(objtype *ob)\r
1478 {\r
1479 // can't use &<var> in a switch statement...\r
1480 \r
1481         if (ob->state == &s_keenshoot1)\r
1482         {\r
1483                 if (ob->xdir == 1)\r
1484                 {\r
1485                         SpawnShot(ob->x + 16*PIXGLOBAL, ob->y + 4*PIXGLOBAL, dir_East);\r
1486                 }\r
1487                 else\r
1488                 {\r
1489                         SpawnShot(ob->x - 8*PIXGLOBAL, ob->y + 4*PIXGLOBAL, dir_West);\r
1490                 }\r
1491         }\r
1492         if (ob->state == &s_keenairshoot2)\r
1493         {\r
1494                 if (ob->xdir == 1)\r
1495                 {\r
1496                         SpawnShot(ob->x + 16*PIXGLOBAL, ob->y + 2*PIXGLOBAL, dir_East);\r
1497                 }\r
1498                 else\r
1499                 {\r
1500                         SpawnShot(ob->x, ob->y + 2*PIXGLOBAL, dir_West);\r
1501                 }\r
1502         }\r
1503         if (ob->state == &s_keenairshootdown2)\r
1504         {\r
1505                 SpawnShot(ob->x + 8*PIXGLOBAL, ob->y + 18*PIXGLOBAL, dir_South);\r
1506         }\r
1507         if (ob->state == &s_keenairshootup2)\r
1508         {\r
1509                 SpawnShot(ob->x + 5*PIXGLOBAL, ob->y - 10*PIXGLOBAL, dir_North);\r
1510         }\r
1511         if (ob->state == &s_keenshootup1)\r
1512         {\r
1513                 SpawnShot(ob->x + 5*PIXGLOBAL, ob->y - 10*PIXGLOBAL, dir_North);\r
1514         }\r
1515         if (ob->state == &s_keenpoleshoot1)\r
1516         {\r
1517                 if (ob->xdir == 1)\r
1518                 {\r
1519                         SpawnShot(ob->x + 16*PIXGLOBAL, ob->y + 4*PIXGLOBAL, dir_East);\r
1520                 }\r
1521                 else\r
1522                 {\r
1523                         SpawnShot(ob->x - 8*PIXGLOBAL, ob->y + 4*PIXGLOBAL, dir_West);\r
1524                 }\r
1525         }\r
1526         if (ob->state == &s_keenpoleshootup1)\r
1527         {\r
1528                 if (ob->xdir == 1)\r
1529                 {\r
1530                         SpawnShot(ob->x + 6*PIXGLOBAL, ob->y + 4*PIXGLOBAL, dir_North);\r
1531                 }\r
1532                 else\r
1533                 {\r
1534                         SpawnShot(ob->x + 12*PIXGLOBAL, ob->y + 4*PIXGLOBAL, dir_North);\r
1535                 }\r
1536         }\r
1537         if (ob->state == &s_keenpoleshootdown1)\r
1538         {\r
1539                 if (ob->xdir == 1)\r
1540                 {\r
1541                         SpawnShot(ob->x + 6*PIXGLOBAL, ob->y + 24*PIXGLOBAL, dir_South);\r
1542                 }\r
1543                 else\r
1544                 {\r
1545                         SpawnShot(ob->x + 12*PIXGLOBAL, ob->y + 24*PIXGLOBAL, dir_South);\r
1546                 }\r
1547         }\r
1548 }\r
1549 \r
1550 //===========================================================================\r
1551 \r
1552 /*\r
1553 =======================\r
1554 =\r
1555 = T_PullUp1\r
1556 =\r
1557 =======================\r
1558 */\r
1559 \r
1560 void T_PullUp1(objtype *ob)\r
1561 {\r
1562         if (ob->xdir == 1)\r
1563         {\r
1564                 xtry = 8*PIXGLOBAL;\r
1565         }\r
1566         else\r
1567         {\r
1568                 ytry = -8*PIXGLOBAL;\r
1569         }\r
1570 }\r
1571 \r
1572 /*\r
1573 =======================\r
1574 =\r
1575 = T_PullUp2\r
1576 =\r
1577 =======================\r
1578 */\r
1579 \r
1580 void T_PullUp2(objtype *ob)\r
1581 {\r
1582         if (ob->xdir == 1)\r
1583         {\r
1584                 xtry = 8*PIXGLOBAL;\r
1585                 ytry = -8*PIXGLOBAL;\r
1586         }\r
1587         else\r
1588         {\r
1589                 xtry = -8*PIXGLOBAL;\r
1590                 ytry = -8*PIXGLOBAL;\r
1591         }\r
1592 }\r
1593 \r
1594 /*\r
1595 =======================\r
1596 =\r
1597 = T_PullUp3\r
1598 =\r
1599 =======================\r
1600 */\r
1601 \r
1602 #pragma argsused\r
1603 void T_PullUp3(objtype *ob)\r
1604 {\r
1605         ytry = -8*PIXGLOBAL;\r
1606 }\r
1607 \r
1608 /*\r
1609 =======================\r
1610 =\r
1611 = T_PulledUp\r
1612 =\r
1613 =======================\r
1614 */\r
1615 \r
1616 void T_PulledUp(objtype *ob)\r
1617 {\r
1618         ob->needtoclip = cl_midclip;\r
1619         ob->priority = 1;\r
1620         ytry = 8*PIXGLOBAL;\r
1621 }\r
1622 \r
1623 //===========================================================================\r
1624 \r
1625 /*\r
1626 =======================\r
1627 =\r
1628 = KeenDieThink\r
1629 =\r
1630 =======================\r
1631 */\r
1632 \r
1633 void KeenDieThink(objtype *ob)\r
1634 {\r
1635         DoWeakGravity(ob);\r
1636         xtry = ob->xspeed * tics;\r
1637         if (!OnScreen(ob))\r
1638                 playstate = ex_died;\r
1639 }\r
1640 \r
1641 /*\r
1642 =============================================================================\r
1643 \r
1644                                                 CONTACT ROUTINES\r
1645 \r
1646 =============================================================================\r
1647 */\r
1648 \r
1649 /*\r
1650 ============================\r
1651 =\r
1652 = KillKeen\r
1653 =\r
1654 ============================\r
1655 */\r
1656 \r
1657 void KillKeen(void)\r
1658 {\r
1659         if (invincible || godmode)\r
1660                 return;\r
1661 \r
1662         if (player->state != &s_keendead)\r
1663         {\r
1664 \r
1665                 moonok = 0;\r
1666                 invincible = 30;        //0.43 seconds\r
1667                 keenkilled = true;\r
1668                 player->needtoclip = cl_noclip;\r
1669                 player->priority = 3;\r
1670 #ifdef KEEN4\r
1671                 if (mapon == 17)\r
1672                 {\r
1673                         if (US_RndT() < 0x80)\r
1674                         {\r
1675                                 ChangeState(player, &s_keensuitdie1);\r
1676                         }\r
1677                         else\r
1678                         {\r
1679                                 ChangeState(player, &s_keensuitdie2);\r
1680                         }\r
1681                 }\r
1682                 else\r
1683 #endif\r
1684                 {\r
1685                         if (US_RndT() < 0x80)\r
1686                         {\r
1687                                 ChangeState(player, &s_keendie1);\r
1688                         }\r
1689                         else\r
1690                         {\r
1691                                 ChangeState(player, &s_keendie2);\r
1692                         }\r
1693                 }\r
1694                 SD_PlaySound(SND_KEENDEAD);\r
1695                 player->yspeed = -40;\r
1696                 player->xspeed = 16;\r
1697         }\r
1698 }\r
1699 \r
1700 /*\r
1701 ============================\r
1702 =\r
1703 = KeenContact\r
1704 =\r
1705 ============================\r
1706 */\r
1707 \r
1708 void KeenContact(objtype *ob, objtype *hit)\r
1709 {\r
1710         switch (hit->obclass)\r
1711         {\r
1712         case bonusobj:\r
1713                 switch (hit->temp1)\r
1714                 {\r
1715                 case 0:\r
1716                 case 1:\r
1717                 case 2:\r
1718                 case 3:\r
1719                 case 4:\r
1720                 case 5:\r
1721                 case 6:\r
1722                 case 7:\r
1723                 case 8:\r
1724                 case 9:\r
1725                 case 10:\r
1726                 case 11:\r
1727 #ifdef KEEN5\r
1728                 case 12:\r
1729 #endif\r
1730                         SD_PlaySound(bonussound[hit->temp1]);\r
1731                         hit->obclass = inertobj;\r
1732                         hit->priority = 3;\r
1733                         hit->shapenum = bonussprite[hit->temp1];\r
1734                         GivePoints(bonuspoints[hit->temp1]);\r
1735                         if (hit->temp1 < 4)\r
1736                         {\r
1737                                 gamestate.keys[hit->temp1]++;\r
1738                         }\r
1739                         else if (hit->temp1 == 10)\r
1740                         {\r
1741                                 gamestate.lives++;\r
1742                         }\r
1743                         else if (hit->temp1 == 11)\r
1744                         {\r
1745                                 gamestate.ammo += shotsinclip[gamestate.difficulty];\r
1746                         }\r
1747 #ifdef KEEN5\r
1748                         else if (hit->temp1 == 12)\r
1749                         {\r
1750                                 gamestate.keycard = true;\r
1751                         }\r
1752 #endif\r
1753                         ChangeState(hit, &s_bonusrise);\r
1754                 }\r
1755                 break;\r
1756 \r
1757 #if defined KEEN4\r
1758         case oracleobj:\r
1759                 if (!ob->hitnorth)\r
1760                         break;\r
1761 \r
1762                 if (mapon == 14)\r
1763                 {\r
1764                         RescueJanitor();\r
1765                         RF_ForceRefresh();\r
1766                         RemoveObj(hit);\r
1767                 }\r
1768                 else\r
1769                 {\r
1770                         SD_PlaySound(SND_LINDSEY);\r
1771                         playstate = ex_rescued;\r
1772                 }\r
1773                 break;\r
1774         case stunnedobj:\r
1775                 if (hit->temp4 != bounderobj)\r
1776                         break;\r
1777                 //no break here -- drop through to platformobj is intended!\r
1778         case platformobj:\r
1779                 if (!gamestate.riding)\r
1780                 {\r
1781                         ClipToSpriteTop(ob, hit);\r
1782                 }\r
1783                 else\r
1784                         return;\r
1785                 break;\r
1786         case bounderobj:\r
1787                 ClipToSprite(ob, hit, false);\r
1788                 break;\r
1789         case lindseyobj:\r
1790                 PrincessLindsey();\r
1791                 RemoveObj(hit);\r
1792                 RF_ForceRefresh();\r
1793                 break;\r
1794         case footobj:\r
1795                 playstate = ex_foot;\r
1796                 break;\r
1797 #elif defined KEEN5\r
1798         case platformobj:\r
1799                 if (!gamestate.riding)\r
1800                         ClipToSpriteTop(ob, hit);\r
1801                 break;\r
1802 #elif defined KEEN6\r
1803         case stunshotobj:\r
1804                 if (hit->temp4)\r
1805                 {\r
1806                         ExplodeShot(hit);\r
1807                         ChangeState(ob, &s_keenstun);\r
1808                 }\r
1809                 // BUG: there is no break here - this causes the impossible bullet bug\r
1810         case platformobj:\r
1811                 if (!gamestate.riding)\r
1812                         ClipToSpriteTop(ob, hit);\r
1813                 break;\r
1814 #endif\r
1815         }\r
1816 }\r
1817 \r
1818 /*\r
1819 ============================\r
1820 =\r
1821 = KeenPosContact\r
1822 =\r
1823 ============================\r
1824 */\r
1825 \r
1826 void KeenPosContact(objtype *ob, objtype *hit)\r
1827 {\r
1828         switch (hit->obclass)\r
1829         {\r
1830 #if defined KEEN4\r
1831         case platformobj:\r
1832                 // BUG: priority is not reset here\r
1833                 ob->needtoclip = cl_midclip;\r
1834                 ChangeState(ob, &s_keenjump3);\r
1835                 jumptime = ob->xspeed = ob->yspeed = 0;\r
1836                 ClipToSpriteTop(ob, hit);\r
1837                 break;\r
1838         case madmushroomobj:\r
1839         case arachnutobj:\r
1840         case berkeloidobj:\r
1841                 KillKeen();\r
1842                 break;\r
1843         case bounderobj:\r
1844                 ob->priority = 1;\r
1845                 ob->needtoclip = cl_midclip;\r
1846                 ChangeState(ob, &s_keenjump3);\r
1847                 jumptime = ob->xspeed = ob->yspeed = 0;\r
1848                 ClipToSprite(ob, hit, false);\r
1849                 break;\r
1850 #elif defined KEEN5\r
1851         case platformobj:\r
1852                 // BUG: priority is not reset here\r
1853                 ob->needtoclip = cl_midclip;\r
1854                 ChangeState(ob, &s_keenjump3);\r
1855                 jumptime = ob->xspeed = ob->yspeed = 0;\r
1856                 ClipToSpriteTop(ob, hit);\r
1857                 break;\r
1858         case amptonobj:\r
1859         case scottieobj:\r
1860                 ob->priority = 1;\r
1861                 ob->needtoclip = cl_midclip;\r
1862                 ChangeState(ob, &s_keenjump3);\r
1863                 jumptime = ob->xspeed = ob->yspeed = 0;\r
1864                 break;\r
1865 #elif defined KEEN6\r
1866         case platformobj:\r
1867         case gikobj:\r
1868         case flectobj:\r
1869         case bloogletobj:\r
1870                 ob->priority = 1;\r
1871                 ob->needtoclip = cl_midclip;\r
1872                 ChangeState(ob, &s_keenjump3);\r
1873                 jumptime = ob->xspeed = ob->yspeed = 0;\r
1874                 ClipToSpriteTop(ob, hit);       // BUG: allows Keen to stand on Blooglets and Flects\r
1875                 break;\r
1876 #endif\r
1877         }\r
1878 }\r
1879 \r
1880 /*\r
1881 ============================\r
1882 =\r
1883 = HandleRiding\r
1884 =\r
1885 ============================\r
1886 */\r
1887 \r
1888 void HandleRiding(objtype *ob)\r
1889 {\r
1890         objtype *plat;\r
1891 \r
1892         plat = gamestate.riding;\r
1893         if (ob->right < plat->left || ob->left > plat->right)\r
1894         {\r
1895                 gamestate.riding = NULL;\r
1896         }\r
1897         else if (ob->ymove < 0)\r
1898         {\r
1899                 gamestate.riding = NULL;\r
1900                 if (plat->ymove < 0)\r
1901                 {\r
1902                         xtry = 0;\r
1903                         ytry = plat->ymove;\r
1904                         PushObj(ob);\r
1905                 }\r
1906         }\r
1907         else\r
1908         {\r
1909                 xtry = plat->xmove;\r
1910                 ytry = plat->top - ob->bottom - 16;\r
1911                 PushObj(ob);\r
1912 \r
1913 #if GRMODE == CGAGR\r
1914                 if (ob->xmove == plat->xmove)\r
1915                 {\r
1916                         ob->x &= ~0x3F;\r
1917                         ob->x |= plat->x & 0x3F;\r
1918                 }\r
1919 #else\r
1920                 if (nopan)\r
1921                 {\r
1922                         ob->x &= ~0x7F;\r
1923                         ob->x |= plat->x & 0x7F;\r
1924                 }\r
1925                 else\r
1926                 {\r
1927                         ob->x |= plat->x & 0x1F;\r
1928                 }\r
1929 #endif\r
1930 \r
1931                 if (ob->hitsouth)\r
1932                 {\r
1933                         gamestate.riding = NULL;\r
1934                 }\r
1935                 else\r
1936                 {\r
1937                         ob->hitnorth = 25;\r
1938                 }\r
1939         }\r
1940 }\r
1941 \r
1942 /*\r
1943 ============================\r
1944 =\r
1945 = TileBonus\r
1946 =\r
1947 ============================\r
1948 */\r
1949 \r
1950 void TileBonus(Uint16 x, Uint16 y, Uint16 bonus)\r
1951 {\r
1952         RF_MemToMap(&zeromap, 1, x, y, 1, 1);\r
1953         SD_PlaySound(bonussound[bonus]);\r
1954         GivePoints(bonuspoints[bonus]);\r
1955         if (bonus < 4)\r
1956         {\r
1957                 gamestate.keys[bonus]++;\r
1958         }\r
1959         else if (bonus == 10)\r
1960         {\r
1961                 gamestate.lives++;\r
1962         }\r
1963         else if (bonus == 11)\r
1964         {\r
1965                 gamestate.ammo += shotsinclip[gamestate.difficulty];\r
1966         }\r
1967         GetNewObj(true);\r
1968         new->obclass = inertobj;\r
1969         new->priority = 3;\r
1970         new->x = CONVERT_TILE_TO_GLOBAL(x);\r
1971         new->y = CONVERT_TILE_TO_GLOBAL(y);\r
1972         new->ydir = -1;\r
1973         new->temp2 = new->shapenum = bonussprite[bonus];\r
1974         NewState(new, &s_bonusrise);\r
1975         new->needtoclip = cl_noclip;\r
1976 }\r
1977 \r
1978 /*\r
1979 ============================\r
1980 =\r
1981 = GiveDrop\r
1982 =\r
1983 ============================\r
1984 */\r
1985 \r
1986 void GiveDrop(Uint16 x, Uint16 y)\r
1987 {\r
1988         RF_MemToMap(&zeromap, 1, x, y, 1, 1);\r
1989         SD_PlaySound(SND_GETWATER);\r
1990         SpawnSplash(x, y);\r
1991         if (++gamestate.drops == 100)\r
1992         {\r
1993                 gamestate.drops = 0;\r
1994                 SD_PlaySound(SND_EXTRAKEEN);\r
1995                 gamestate.lives++;\r
1996                 GetNewObj(true);\r
1997                 new->obclass = inertobj;\r
1998                 new->priority = 3;\r
1999                 new->x = CONVERT_TILE_TO_GLOBAL(x);\r
2000                 new->y = CONVERT_TILE_TO_GLOBAL(y-1);\r
2001                 new->ydir = -1;\r
2002                 new->temp2 = new->shapenum = BONUS100UPSPR;\r
2003                 NewState(new, &s_bonusrise);\r
2004                 new->needtoclip = cl_noclip;\r
2005         }\r
2006 }\r
2007 \r
2008 /*\r
2009 ============================\r
2010 =\r
2011 = CheckInTiles\r
2012 =\r
2013 ============================\r
2014 */\r
2015 \r
2016 void CheckInTiles(objtype *ob)\r
2017 {\r
2018         Uint16 x, y;\r
2019         Uint16 far *map;\r
2020         Uint16 rowdelta, intile, midx;\r
2021 \r
2022         if (moonok == 1)\r
2023                 moonok = 0;\r
2024 \r
2025         map = mapsegs[1] + mapbwidthtable[ob->tiletop]/2 + ob->tileleft;\r
2026         rowdelta = mapwidth - (ob->tileright - ob->tileleft + 1);\r
2027         for (y = ob->tiletop; y <= ob->tilebottom; y++, map += rowdelta)\r
2028         {\r
2029                 for (x = ob->tileleft; x <= ob->tileright; x++, map++)\r
2030                 {\r
2031                         if ((intile = tinf[INTILE + *map] & INTILE_TYPEMASK) != 0)\r
2032                         {\r
2033                                 switch (intile)\r
2034                                 {\r
2035                                 case INTILE_DEADLY:\r
2036                                         KillKeen();\r
2037                                         break;\r
2038 \r
2039                                 case INTILE_DROP:\r
2040                                         GiveDrop(x, y);\r
2041                                         break;\r
2042 \r
2043                                 case INTILE_GEMSOCKET0:\r
2044                                 case INTILE_GEMSOCKET1:\r
2045                                 case INTILE_GEMSOCKET2:\r
2046                                 case INTILE_GEMSOCKET3:\r
2047                                         if (ob->tilebottom != y || !ob->hitnorth\r
2048                                                 || ob->state == &s_keenkey\r
2049                                                 || !gamestate.keys[intile-INTILE_GEMSOCKET0])\r
2050                                         {\r
2051                                                 return;\r
2052                                         }\r
2053 \r
2054                                         midx = CONVERT_TILE_TO_GLOBAL(x) + -4*PIXGLOBAL;\r
2055                                         if (ob->x != midx)\r
2056                                         {\r
2057                                                 ob->temp1 = midx;\r
2058                                                 ob->state = &s_keenlineup;\r
2059                                                 return;\r
2060                                         }\r
2061                                         else\r
2062                                         {\r
2063                                                 gamestate.keys[intile-INTILE_GEMSOCKET0]--;\r
2064                                                 ChangeState(ob, &s_keenkey);\r
2065                                         }\r
2066                                         break;\r
2067 \r
2068                                 case INTILE_MOON:\r
2069                                         if (moonok == 0)\r
2070                                                 moonok = 1;\r
2071                                         break;\r
2072 \r
2073                                 case INTILE_BONUS100:\r
2074                                 case INTILE_BONUS200:\r
2075                                 case INTILE_BONUS500:\r
2076                                 case INTILE_BONUS1000:\r
2077                                 case INTILE_BONUS2000:\r
2078                                 case INTILE_BONUS5000:\r
2079                                 case INTILE_EXTRALIFE:\r
2080                                 case INTILE_AMMO:\r
2081                                         TileBonus(x, y, (intile-INTILE_BONUS100)+4);\r
2082                                         break;\r
2083                                 }\r
2084                         }\r
2085                 }\r
2086         }\r
2087 }\r
2088 \r
2089 /*\r
2090 =============================================================================\r
2091 \r
2092                                                  REACTION ROUTINES\r
2093 \r
2094 =============================================================================\r
2095 */\r
2096 \r
2097 \r
2098 /*\r
2099 ============================\r
2100 =\r
2101 = KeenSimpleReact\r
2102 =\r
2103 ============================\r
2104 */\r
2105 \r
2106 void KeenSimpleReact(objtype *ob)\r
2107 {\r
2108         RF_PlaceSprite(&ob->sprite, ob->x, ob->y, ob->shapenum, spritedraw, ob->priority);\r
2109 }\r
2110 \r
2111 \r
2112 /*\r
2113 ============================\r
2114 =\r
2115 = KeenStandReact\r
2116 =\r
2117 ============================\r
2118 */\r
2119 \r
2120 void KeenStandReact(objtype *ob)\r
2121 {\r
2122         if (!ob->hitnorth)\r
2123         {\r
2124         //\r
2125         // walked off an edge\r
2126         //\r
2127                 SD_PlaySound(SND_PLUMMET);\r
2128                 ob->xspeed = ob->xdir * 8;\r
2129                 ob->yspeed = 0;\r
2130                 ChangeState(ob, &s_keenjump3);\r
2131                 jumptime = 0;\r
2132         }\r
2133         else if ((ob->hitnorth & ~7) == 8)      // deadly floor!\r
2134         {\r
2135                 KillKeen();\r
2136         }\r
2137         else if (ob->hitnorth == 41)\r
2138         {\r
2139                 xtry = tics * 8;\r
2140                 ytry = 0;\r
2141                 ob->temp1 = 0;\r
2142                 ClipToWalls(ob);\r
2143         }\r
2144         else if (ob->hitnorth == 49)\r
2145         {\r
2146                 xtry = tics * -8;\r
2147                 ytry = 0;\r
2148                 ob->temp1 = 0;\r
2149                 ClipToWalls(ob);\r
2150         }\r
2151 \r
2152         RF_PlaceSprite(&ob->sprite, ob->x, ob->y, ob->shapenum, spritedraw, ob->priority);\r
2153 }\r
2154 \r
2155 /*\r
2156 ============================\r
2157 =\r
2158 = KeenWalkReact\r
2159 =\r
2160 ============================\r
2161 */\r
2162 \r
2163 void KeenWalkReact(objtype *ob)\r
2164 {\r
2165         if (!ob->hitnorth)\r
2166         {\r
2167         //\r
2168         // walked off an edge\r
2169         //\r
2170                 SD_PlaySound(SND_PLUMMET);\r
2171                 ob->xspeed = ob->xdir * 8;\r
2172                 ob->yspeed = 0;\r
2173                 ChangeState(ob, &s_keenjump3);\r
2174                 jumptime = 0;\r
2175         }\r
2176         else if ((ob->hitnorth & ~7) == 8)      // deadly floor!\r
2177         {\r
2178                 KillKeen();\r
2179         }\r
2180         else if (ob->hitnorth == 41)\r
2181         {\r
2182                 xtry = tics * 8;\r
2183                 ytry = 0;\r
2184                 ClipToWalls(ob);\r
2185         }\r
2186         else if (ob->hitnorth == 49)\r
2187         {\r
2188                 xtry = tics * -8;\r
2189                 ytry = 0;\r
2190                 ClipToWalls(ob);\r
2191         }\r
2192         else if (ob->hiteast && ob->xdir == -1 || ob->hitwest && ob->xdir == 1)\r
2193         {\r
2194         //\r
2195         // ran into a wall\r
2196         //\r
2197                 ob->ticcount = 0;\r
2198                 ob->state = &s_keenstand;\r
2199                 ob->shapenum = ob->xdir == 1? s_keenstand.rightshapenum : s_keenstand.leftshapenum;\r
2200         }\r
2201 \r
2202         RF_PlaceSprite(&ob->sprite, ob->x, ob->y, ob->shapenum, spritedraw, ob->priority);\r
2203 }\r
2204 \r
2205 /*\r
2206 ============================\r
2207 =\r
2208 = KeenAirReact\r
2209 =\r
2210 ============================\r
2211 */\r
2212 \r
2213 void KeenAirReact(objtype *ob)\r
2214 {\r
2215         Uint16 far *map;\r
2216         Uint16 oldtop, graby, ty, tile;\r
2217 \r
2218         if (ob->hiteast && ob->xdir == -1 || ob->hitwest && ob->xdir == 1)\r
2219                 ob->xspeed = 0;\r
2220 \r
2221         if (ob->hitsouth)\r
2222         {\r
2223                 if (ob->hitsouth == 17) // jumping up through a pole hole\r
2224                 {\r
2225                         ob->y -= 2*PIXGLOBAL;\r
2226                         ob->top -= 2*PIXGLOBAL;\r
2227                         ob->xspeed = 0;\r
2228                         ob->x = CONVERT_TILE_TO_GLOBAL(ob->tilemidx) - 2*PIXGLOBAL;\r
2229                 }\r
2230                 else\r
2231                 {\r
2232 #ifdef KEEN6\r
2233                         if (ob->hitsouth == 33)\r
2234                         {\r
2235                                 FlipBigSwitch(ob, false);\r
2236                         }\r
2237 #endif\r
2238                         if (!jumpcheat)\r
2239                         {\r
2240                                 SD_PlaySound(SND_HELMETHIT);\r
2241                                 if (ob->hitsouth > 1)\r
2242                                 {\r
2243                                         ob->yspeed += 16;\r
2244                                         if (ob->yspeed < 0)     // push away from slopes\r
2245                                                 ob->yspeed = 0;\r
2246                                 }\r
2247                                 else\r
2248                                 {\r
2249                                         ob->yspeed = 0;\r
2250                                 }\r
2251                                 jumptime = 0;\r
2252                         }\r
2253                 }\r
2254         }\r
2255 \r
2256         if (ob->hitnorth)\r
2257         {\r
2258                 ob->ymove = 0;\r
2259                 if ((ob->hitnorth & ~7) == 8)   // deadly floor!\r
2260                 {\r
2261                         KillKeen();\r
2262                 }\r
2263                 else\r
2264                 {\r
2265 #if defined KEEN5\r
2266                         if (ob->hitnorth == 57)\r
2267                         {\r
2268                                 SD_PlaySound(SND_LANDONFUSE);\r
2269                         }\r
2270 #elif defined KEEN6\r
2271                         if (ob->hitnorth == 33)\r
2272                         {\r
2273                                 FlipBigSwitch(ob, true);\r
2274                         }\r
2275 #endif\r
2276                         if (ob->hitnorth != 25 || !jumptime)    // KLUDGE to allow jumping off\r
2277                         {\r
2278                                 ob->temp1 = ob->temp2 = 0;\r
2279                                 if (ob->state == &s_keenairshoot1)\r
2280                                 {\r
2281                                         ChangeState(ob, &s_keenshoot1);\r
2282                                 }\r
2283                                 else if (ob->state == &s_keenairshootup1)\r
2284                                 {\r
2285                                         ChangeState(ob, &s_keenshootup1);\r
2286                                 }\r
2287                                 else if (c.xaxis)\r
2288                                 {\r
2289                                         ChangeState(ob, &s_keenwalk1);\r
2290                                 }\r
2291                                 else\r
2292                                 {\r
2293                                         ChangeState(ob, &s_keenstand);\r
2294                                 }\r
2295                                 SD_PlaySound(SND_LAND);\r
2296                         }\r
2297                 }\r
2298         }\r
2299         else if (ob->ymove > 0)\r
2300         {\r
2301 //\r
2302 // check if there is an edge to grab\r
2303 //\r
2304                 oldtop = ob->top - ob->ymove;\r
2305                 graby = ((ob->top - 4*PIXGLOBAL) & 0xFF00) + 4*PIXGLOBAL;\r
2306                 ty = CONVERT_GLOBAL_TO_TILE(graby) - 1;\r
2307                 if (oldtop < graby && ob->top >= graby)\r
2308                 {\r
2309                         if (c.xaxis == -1)\r
2310                         {\r
2311                                 map = mapsegs[1] + mapbwidthtable[ty]/2 + ob->tileleft;\r
2312                                 if (ob->hiteast)\r
2313                                         map--;\r
2314                                 tile = *map;\r
2315                                 if (!tinf[EASTWALL + tile] && !tinf[WESTWALL + tile]\r
2316                                         && !tinf[NORTHWALL + tile] && !tinf[SOUTHWALL + tile]\r
2317                                         && tinf[EASTWALL + map[mapwidth]] && tinf[NORTHWALL + map[mapwidth]]\r
2318                                         )\r
2319                                 {\r
2320                                         ob->xdir = -1;\r
2321                                         ob->needtoclip = cl_noclip;\r
2322                                         ob->x = (ob->x & 0xFF00) + 8*PIXGLOBAL;\r
2323                                         ob->y = graby - 4*PIXGLOBAL;\r
2324                                         ob->yspeed = ob->ymove = 0;\r
2325                                         ChangeState(ob, &s_keenholdon);\r
2326                                 }\r
2327                         }\r
2328                         else if (c.xaxis == 1)\r
2329                         {\r
2330                                 map = mapsegs[1] + mapbwidthtable[ty]/2 + ob->tileright;\r
2331                                 if (ob->hitwest)\r
2332                                         map++;\r
2333                                 tile = *map;\r
2334                                 if (!tinf[EASTWALL + tile] && !tinf[WESTWALL + tile]\r
2335                                         && !tinf[NORTHWALL + tile] && !tinf[SOUTHWALL + tile]\r
2336                                         && tinf[WESTWALL + map[mapwidth]] && tinf[NORTHWALL + map[mapwidth]]\r
2337                                         )\r
2338                                 {\r
2339                                         ob->xdir = 1;\r
2340                                         ob->needtoclip = cl_noclip;\r
2341                                         ob->x = (ob->x & 0xFF00) + 16*PIXGLOBAL;\r
2342                                         ob->y = graby - 4*PIXGLOBAL;\r
2343                                         ob->yspeed = ob->ymove = 0;\r
2344                                         ChangeState(ob, &s_keenholdon);\r
2345                                 }\r
2346                         }\r
2347                 }\r
2348         }\r
2349 \r
2350         RF_PlaceSprite(&ob->sprite, ob->x, ob->y, ob->shapenum, spritedraw, ob->priority);\r
2351 }\r
2352 \r
2353 #ifdef KEEN5\r
2354 /*\r
2355 ============================\r
2356 =\r
2357 = BreakFuse\r
2358 =\r
2359 ============================\r
2360 */\r
2361 \r
2362 void BreakFuse(Uint16 tileX, Uint16 tileY)\r
2363 {\r
2364         Uint16 tiles[] = {1932, 1950};  // should be 'static' for less overhead\r
2365 \r
2366         // The original disassembly had some code here equivalent to this:\r
2367         //\r
2368         // _AX = tiles[0];\r
2369         // _DX = 4;\r
2370         //\r
2371         // As it turned out, that was just a compiler quirk.\r
2372 \r
2373         SpawnFuseFlash(tileX, tileY);\r
2374         if (--gamestate.numfuses == 0)\r
2375         {\r
2376                 SpawnDeadMachine();\r
2377         }\r
2378         RF_MemToMap(tiles, 1, tileX, tileY, 1, 2);\r
2379 }\r
2380 #endif\r
2381 \r
2382 /*\r
2383 ============================\r
2384 =\r
2385 = KeenPogoReact\r
2386 =\r
2387 ============================\r
2388 */\r
2389 \r
2390 void KeenPogoReact(objtype *ob)\r
2391 {\r
2392         if (ob->hiteast && ob->xdir == -1 || ob->hitwest && ob->xdir == 1)\r
2393                 ob->xspeed = 0;\r
2394 \r
2395         if (ob->hitsouth)\r
2396         {\r
2397                 if (ob->hitsouth == 17) // jumping up through a pole hole\r
2398                 {\r
2399                         ob->y -= 2*PIXGLOBAL;\r
2400                         ob->top -= 2*PIXGLOBAL;\r
2401                         ob->xspeed = 0;\r
2402                         ob->x = CONVERT_TILE_TO_GLOBAL(ob->tilemidx) - 2*PIXGLOBAL;\r
2403                 }\r
2404                 else\r
2405                 {\r
2406 #ifdef KEEN6\r
2407                         if (ob->hitsouth == 33)\r
2408                         {\r
2409                                 FlipBigSwitch(ob, false);\r
2410                         }\r
2411 #endif\r
2412                         if (!jumpcheat)\r
2413                         {\r
2414                                 SD_PlaySound(SND_HELMETHIT);\r
2415                                 if (ob->hitsouth > 1)\r
2416                                 {\r
2417                                         ob->yspeed += 16;\r
2418                                         if (ob->yspeed < 0)     // push away from slopes\r
2419                                                 ob->yspeed = 0;\r
2420                                 }\r
2421                                 else\r
2422                                 {\r
2423                                         ob->yspeed = 0;\r
2424                                 }\r
2425                                 jumptime = 0;\r
2426                         }\r
2427                 }\r
2428         }\r
2429 \r
2430         if (ob->hitnorth)\r
2431         {\r
2432                 ob->ymove = 0;\r
2433                 if ((ob->hitnorth & ~7) == 8)   // deadly floor!\r
2434                 {\r
2435                         KillKeen();\r
2436                 }\r
2437                 else\r
2438                 {\r
2439 #if defined KEEN5\r
2440                         if (ob->hitnorth == 57)\r
2441                         {\r
2442                                 if (ob->yspeed < 48)\r
2443                                 {\r
2444                                         SD_PlaySound(SND_LANDONFUSE);\r
2445                                 }\r
2446                                 else\r
2447                                 {\r
2448                                         BreakFuse(ob->tilemidx, ob->tilebottom);\r
2449                                         RF_PlaceSprite(&ob->sprite, ob->x, ob->y, ob->shapenum, spritedraw, ob->priority);\r
2450                                         return;\r
2451                                 }\r
2452                         }\r
2453 #elif defined KEEN6\r
2454                         if (ob->hitnorth == 33)\r
2455                         {\r
2456                                 FlipBigSwitch(ob, true);\r
2457                         }\r
2458                         else if (ob->hitnorth == 41)\r
2459                         {\r
2460                                 ob->xspeed += 8;\r
2461                                 if (ob->xspeed > 8)\r
2462                                         ob->xspeed = 8;\r
2463                         }\r
2464                         else if (ob->hitnorth == 49)\r
2465                         {\r
2466                                 ob->xspeed -= 8;\r
2467                                 if (ob->xspeed < -8)\r
2468                                         ob->xspeed = -8;\r
2469                         }\r
2470 #endif\r
2471                         if (ob->hitnorth != 25 || !jumptime)    // KLUDGE to allow jumping off\r
2472                         {\r
2473                                 ob->yspeed = -48;\r
2474                                 jumptime = 24;\r
2475                                 SD_PlaySound(SND_POGOBOUNCE);\r
2476                                 ChangeState(ob, &s_keenpogo);\r
2477                         }\r
2478                 }\r
2479         }\r
2480 \r
2481         RF_PlaceSprite(&ob->sprite, ob->x, ob->y, ob->shapenum, spritedraw, ob->priority);\r
2482 }\r
2483 \r
2484 /*\r
2485 ============================\r
2486 =\r
2487 = KeenPoleReact\r
2488 =\r
2489 ============================\r
2490 */\r
2491 \r
2492 void KeenPoleReact(objtype *ob)\r
2493 {\r
2494         Uint16 far *map;\r
2495         Uint16 ymove;\r
2496 \r
2497         map = mapsegs[1] + mapbwidthtable[ob->tilebottom]/2 + ob->tilemidx;\r
2498         if (tinf[NORTHWALL + *map] == 1)\r
2499         {\r
2500                 ymove = (ob->bottom & 0xFF) + 1;\r
2501                 ob->y -= ymove;\r
2502                 ob->bottom -= ymove;\r
2503                 ob->tilebottom--;\r
2504                 ob->needtoclip = cl_midclip;\r
2505                 ChangeState(ob, &s_keenlookdown);\r
2506         }\r
2507 \r
2508         RF_PlaceSprite(&ob->sprite, ob->x, ob->y, ob->shapenum, spritedraw, ob->priority);\r
2509 }\r