1 /* Reconstructed Commander Keen 4-6 Source Code
\r
2 * Copyright (C) 2021 K1n9_Duk3
\r
4 * This file is loosely based on:
\r
5 * Keen Dreams Source Code
\r
6 * Copyright (C) 2014 Javier M. Chavez
\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
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
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
27 Contains the following actor types (in this order):
\r
29 - Keen (regular levels)
\r
36 =============================================================================
\r
40 =============================================================================
\r
43 Sint16 singlegravity; // left over from Keen Dreams, not used in Keen 4-6
\r
45 Uint16 bounceangle [8][8] =
\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
58 arrowdirtype arrowflip[] = {arrow_South, arrow_West, arrow_North, arrow_East, arrow_SouthWest, arrow_NorthWest, arrow_NorthEast, arrow_SouthEast};
\r
61 statetype s_keenstand = {KEENSTANDLSPR, KEENSTANDRSPR, stepthink, false, true, 4, 0, 32, KeenPauseThink, KeenContact, KeenStandReact, &s_keenstand};
\r
64 statetype s_keenride = {KEENONPLATSPR, KEENONPLATSPR, stepthink, false, true, 4, 0, 32, KeenStandThink, KeenContact, KeenStandReact, &s_keenride};
\r
67 statetype s_keenpauselook = {KEENLOOKUSPR, KEENLOOKUSPR, stepthink, false, true, 60, 0, 0, KeenPauseThink, KeenContact, KeenStandReact, &s_keenstand};
\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
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
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
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
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
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
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
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
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
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
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
122 statetype s_keenlineup = {KEENENTER1SPR, KEENENTER1SPR, think, false, false, 0, 0, 0, T_LineUp, 0, R_Draw, NULL};
\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
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
134 statetype s_keenenter6 = {-1, -1, step, false, false, 9, 0, -64, KeenEnterThink, 0, R_Draw, &s_keenstand};
\r
137 statetype s_keenpole = {KEENSHINNYL1SPR, KEENSHINNYR1SPR, think, false, false, 0, 0, 0, KeenPoleThink, KeenPosContact, KeenSimpleReact, &s_keenpole};
\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
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
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
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
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
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
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
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
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
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
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
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
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
192 Sint16 slopespeed[8] = {0, 0, 4, 4, 8, -4, -4, -8};
\r
193 Sint16 polexspeed[3] = {-8, 0, 8};
\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
206 Sint16 bonuspoints[] = {
\r
216 Sint16 bonussprite[] = {
\r
217 BONUSGEMSPR, BONUSGEMSPR, BONUSGEMSPR, BONUSGEMSPR,
\r
218 BONUS100SPR, BONUS200SPR, BONUS500SPR,
\r
219 BONUS1000SPR, BONUS2000SPR, BONUS5000SPR,
\r
227 Uint16 zeromap = 0;
\r
229 // uninitialized variables:
\r
232 =============================================================================
\r
236 =============================================================================
\r
240 Sint32 leavepoletime;
\r
244 =============================================================================
\r
248 player->temp1 = pausetime / pointer to zees when sleeping
\r
249 player->temp2 = pausecount / stagecount
\r
253 =============================================================================
\r
265 void SpawnKeen(Sint16 x, Sint16 y, Sint16 dir)
\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
273 player->xdir = dir;
\r
275 NewState(player, &s_keenstand);
\r
278 //==========================================================================
\r
281 ======================
\r
285 ======================
\r
288 boolean CheckGrabPole(objtype *ob)
\r
293 // kludgy bit to not let you grab a pole the instant you jump off it
\r
295 if (lasttimecount < leavepoletime)
\r
299 else if (lasttimecount-leavepoletime < 19)
\r
306 map = mapsegs[1] + mapbwidthtable[(ob->top+6*PIXGLOBAL)/TILEGLOBAL]/2;
\r
310 map = mapsegs[1] + mapbwidthtable[ob->tilebottom+1]/2;
\r
313 map += ob->tilemidx;
\r
315 if ((tinf[INTILE + *map] & 0x7F) == INTILE_POLE)
\r
317 ob->x = CONVERT_TILE_TO_GLOBAL(ob->tilemidx-1) + 8*PIXGLOBAL;
\r
319 ytry = c.yaxis * 32;
\r
320 ob->needtoclip = cl_noclip; // can climb through pole holes
\r
321 ob->state = &s_keenpole;
\r
327 //==========================================================================
\r
330 ======================
\r
334 = Checks for tiles that Keen can interact with by pressing up
\r
336 ======================
\r
339 boolean CheckEnterHouse(objtype *ob)
\r
345 Uint16 intile, intile2;
\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
350 temp = CONVERT_TILE_TO_GLOBAL(ob->tilemidx) - 4*PIXGLOBAL;
\r
354 ob->state = &s_keenlineup;
\r
358 ob->state = &s_keenswitch;
\r
363 else if (intile == INTILE_DOOR || intile == INTILE_KEYCARDDOOR)
\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
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
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
386 ob->state = &s_keenlineup;
\r
389 else if (intile == INTILE_KEYCARDDOOR)
\r
391 if (gamestate.keycard)
\r
393 gamestate.keycard = false;
\r
394 SD_PlaySound(SND_OPENCARDDOOR);
\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
410 SD_PlaySound(SND_NOWAY);
\r
411 ob->state = &s_keenstand;
\r
419 invincible = 110; //about 1.57 seconds
\r
420 ob->state = &s_keenenter1;
\r
424 infoval = *(mapsegs[2]+mapbwidthtable[ob->tiletop]/2+ob->tilemidx);
\r
436 //==========================================================================
\r
439 ============================
\r
443 ============================
\r
446 void WalkSound1(objtype *ob)
\r
448 SD_PlaySound(SND_WORLDWALK1);
\r
449 ob++; // shut up compiler
\r
453 ============================
\r
457 ============================
\r
460 void WalkSound2(objtype *ob)
\r
462 SD_PlaySound(SND_WORLDWALK2);
\r
463 ob++; // shut up compiler
\r
466 //==========================================================================
\r
469 ============================
\r
473 ============================
\r
476 void KeenStandThink(objtype *ob)
\r
479 if (ob->hitnorth == 25 && ob->state != &s_keenride)
\r
481 ob->state = &s_keenride;
\r
488 ob->state = &s_keenwalk1;
\r
490 xtry = (Sint16)(ob->xdir * ob->state->xmove * tics) / 4;
\r
492 else if (firebutton && !fireheld)
\r
494 // shoot current xdir
\r
498 ob->state = &s_keenshootup1;
\r
502 ob->state = &s_keenshoot1;
\r
505 else if (jumpbutton && ! jumpheld)
\r
507 // jump straight up
\r
509 SD_PlaySound(SND_JUMP);
\r
514 ob->state = &s_keenjump1;
\r
516 else if (pogobutton && !pogoheld)
\r
520 SD_PlaySound(SND_JUMP);
\r
521 ob->state = &s_keenpogodown;
\r
532 if (CheckGrabPole(ob))
\r
534 if (upheld || !CheckEnterHouse(ob))
\r
536 ob->state = &s_keenlookup;
\r
540 if (CheckGrabPole(ob))
\r
542 ob->state = &s_keenlookdown;
\r
548 //===========================================================================
\r
551 =======================
\r
555 = Do special animations in time
\r
557 =======================
\r
560 void KeenPauseThink(objtype *ob)
\r
563 if (ob->hitnorth == 25 && ob->state != &s_keenride)
\r
565 ob->state = &s_keenride;
\r
569 if (c.dir != dir_None || jumpbutton || pogobutton || firebutton)
\r
571 ob->temp1 = ob->temp2 = 0; // not paused any more
\r
572 ob->state = &s_keenstand;
\r
573 KeenStandThink(ob);
\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
584 if (ob->temp1 > 200)
\r
587 ob->state = &s_keenpauselook;
\r
592 if (ob->temp1 > 300)
\r
599 moonok = 2; //don't moon again unless the level is restarted
\r
600 ob->state = &s_keenmoon1;
\r
605 ob->state = &s_keenwait1;
\r
610 if (ob->temp1 > 700)
\r
613 ob->state = &s_keenread;
\r
621 //===========================================================================
\r
624 =======================
\r
628 =======================
\r
631 void KeenReadThink(objtype *ob)
\r
635 playstate = ex_abortgame;
\r
636 IN_ClearKeysDown();
\r
638 if (c.dir != dir_None || jumpbutton || pogobutton)
\r
640 ob->temp1 = ob->temp2 = 0;
\r
641 ob->state = &s_keenstopread;
\r
645 //===========================================================================
\r
648 =======================
\r
652 =======================
\r
655 void KeenLookUpThink(objtype *ob)
\r
657 if (c.yaxis != -1 || c.xaxis
\r
658 || (jumpbutton && !jumpheld)
\r
659 || (pogobutton && !pogoheld)
\r
662 ob->state = &s_keenstand;
\r
663 KeenStandThink(ob);
\r
667 //===========================================================================
\r
670 =======================
\r
672 = KeenLookDownThink
\r
674 =======================
\r
677 void KeenLookDownThink(objtype *ob)
\r
683 if (jumpbutton && ! jumpheld && (ob->hitnorth & 7) == 1)
\r
686 // drop down a level
\r
690 y = ob->tilebottom;
\r
691 map = (Uint16 far *)mapsegs[1] + mapbwidthtable[y]/2 + ob->tilemidx;
\r
693 if (tinf[WESTWALL+tile] || tinf[EASTWALL+tile] || tinf[SOUTHWALL+tile])
\r
694 return; // wall prevents drop down
\r
698 if (tinf[WESTWALL+tile] || tinf[EASTWALL+tile] || tinf[SOUTHWALL+tile])
\r
699 return; // wall prevents drop down
\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
708 ob->state = &s_keenjump3;
\r
709 ob->xspeed = ob->yspeed = 0;
\r
710 SD_PlaySound(SND_PLUMMET);
\r
712 else if (c.yaxis != 1 || c.xaxis
\r
713 || (jumpbutton && !jumpheld)
\r
714 || (pogobutton && !pogoheld))
\r
716 ob->state = &s_keenlookdown4;
\r
720 //===========================================================================
\r
723 =======================
\r
727 =======================
\r
730 void KeenWalkThink(objtype *ob)
\r
739 ob->state = &s_keenstand;
\r
740 KeenStandThink(ob);
\r
744 ob->xdir = c.xaxis;
\r
749 if (CheckGrabPole(ob))
\r
753 if (!CheckEnterHouse(ob))
\r
758 if (!CheckGrabPole(ob))
\r
763 if (firebutton && !fireheld)
\r
771 ob->state = &s_keenshootup1;
\r
775 ob->state = &s_keenshoot1;
\r
780 if (jumpbutton && !jumpheld)
\r
786 SD_PlaySound(SND_JUMP);
\r
787 ob->xspeed = ob->xdir * 16;
\r
791 ob->state = &s_keenjump1;
\r
794 if (pogobutton && !pogoheld)
\r
800 ob->state = &s_keenpogodown;
\r
801 SD_PlaySound(SND_JUMP);
\r
802 ob->xspeed = ob->xdir * 16;
\r
810 // give speed for slopes
\r
812 xmove = slopespeed[ob->hitnorth & 7] * tics;
\r
816 // handle walking sounds
\r
818 if (ob->state == &s_keenwalk1 && ob->temp3 == 0)
\r
820 SD_PlaySound(SND_WORLDWALK1);
\r
823 else if (ob->state == &s_keenwalk3 && ob->temp3 == 0)
\r
825 SD_PlaySound(SND_WORLDWALK2);
\r
828 else if (ob->state == &s_keenwalk2 ||ob->state == &s_keenwalk4)
\r
834 //===========================================================================
\r
837 =======================
\r
841 = Lines up Keen's position for interacting with tiles (temp1 is desired x)
\r
843 =======================
\r
846 void T_LineUp(objtype *ob)
\r
850 xmove = ob->temp1 - ob->x;
\r
853 xtry = xtry - tics * 16;
\r
857 else if (xmove > 0)
\r
859 xtry = xtry + tics * 16;
\r
865 if (!CheckEnterHouse(ob))
\r
866 ob->state = &s_keenstand;
\r
869 //===========================================================================
\r
872 =======================
\r
876 =======================
\r
879 void KeenEnterThink(objtype *ob)
\r
884 map = mapsegs[2] + mapbwidthtable[ob->tilebottom]/2 + ob->tileleft;
\r
889 playstate = ex_portout;
\r
890 ob->state = &s_keenenter6;
\r
893 else if (info == 0xB1B1)
\r
895 playstate = ex_completed;
\r
896 ob->state = &s_keenenter6;
\r
900 ob->y = (CONVERT_TILE_TO_GLOBAL(info & 0xFF) - TILEGLOBAL) + 15;
\r
901 ob->x = CONVERT_TILE_TO_GLOBAL(info >> 8);
\r
903 ob->needtoclip = cl_noclip;
\r
904 ChangeState(ob, ob->state->nextstate);
\r
905 ob->needtoclip = cl_midclip;
\r
909 //===========================================================================
\r
912 =======================
\r
916 =======================
\r
919 void KeenSwitchThink(objtype *ob)
\r
921 Uint16 intile, maptile, newtile, info, sx, sy, tileoff;
\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
932 intile = tinf[INTILE + maptile];
\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
939 for (y = sy; sy+2 > y; y++)
\r
941 map = mapsegs[1] + mapbwidthtable[y]/2 + sx - (y != sy);
\r
942 for (x = sx - (y != sy); x < mapwidth; x++)
\r
945 manim = tinf[MANIM + tile];
\r
950 RF_MemToMap(&tile, 1, x, y, 1, 1);
\r
956 //toggle platform blocker:
\r
957 map = mapsegs[2] + mapbwidthtable[sy]/2 + sx;
\r
960 if (tile >= DIRARROWSTART && tile < DIRARROWEND)
\r
962 *map = arrowflip[tile-DIRARROWSTART]+DIRARROWSTART;
\r
966 *map = tile ^ PLATFORMBLOCK;
\r
970 //===========================================================================
\r
973 =======================
\r
977 =======================
\r
980 void KeenKeyThink(objtype *ob)
\r
982 Uint16 newtile, info, x, y, tileoff;
\r
986 tileoff = mapbwidthtable[ob->tilebottom]/2 + ob->tilemidx;
\r
987 newtile = mapsegs[1][tileoff] + 18;
\r
988 info = mapsegs[2][tileoff];
\r
991 RF_MemToMap(&newtile, 1, ob->tilemidx, ob->tilebottom, 1, 1);
\r
992 SD_PlaySound(SND_OPENDOOR);
\r
997 if (x > mapwidth || x < 2 || y > mapheight || y < 2)
\r
998 Quit("Keyholder points to a bad spot!");
\r
1000 map = mapsegs[1] + mapbwidthtable[y]/2 + x;
\r
1004 while (*map == tile)
\r
1010 new->active = ac_allways;
\r
1011 new->needtoclip = cl_noclip;
\r
1012 new->obclass = inertobj;
\r
1013 NewState(new, &s_door1);
\r
1016 //===========================================================================
\r
1019 =======================
\r
1023 =======================
\r
1026 void KeenAirThink(objtype *ob)
\r
1028 if (jumpcheat && jumpbutton)
\r
1036 if (jumptime <= tics)
\r
1038 ytry = ob->yspeed * jumptime;
\r
1043 ytry = ob->yspeed * tics;
\r
1045 jumptime = jumptime - tics;
\r
1050 if (jumptime == 0 && ob->state->nextstate)
\r
1051 ob->state = ob->state->nextstate; // switch to second jump stage
\r
1055 if (gamestate.difficulty == gd_Easy)
\r
1057 DoWeakGravity(ob);
\r
1063 if (ob->yspeed > 0 && ob->state != &s_keenjump3 && ob->state != &s_keenjump4)
\r
1065 ob->state = ob->state->nextstate; // switch to third jump stage
\r
1073 ob->xdir = c.xaxis;
\r
1074 AccelerateX(ob, c.xaxis*2, 24);
\r
1081 if (ob->hitsouth == 17) // going through a pole hole
\r
1083 ob->xspeed = xtry = 0;
\r
1086 if (firebutton && !fireheld)
\r
1095 ob->state = &s_keenairshootup1;
\r
1098 ob->state = &s_keenairshoot1;
\r
1101 ob->state = &s_keenairshootdown1;
\r
1106 if (pogobutton && !pogoheld)
\r
1109 ob->state = &s_keenpogo;
\r
1114 if (c.yaxis == -1)
\r
1115 CheckGrabPole(ob);
\r
1118 //===========================================================================
\r
1121 =======================
\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
1128 =======================
\r
1131 void KeenBounceThink(objtype *ob)
\r
1134 ytry = ob->yspeed * 6;
\r
1136 SD_PlaySound(SND_POGOBOUNCE);
\r
1139 //===========================================================================
\r
1142 =======================
\r
1146 =======================
\r
1149 void KeenPogoThink(objtype *ob)
\r
1153 if (jumpbutton || jumptime <= 9)
\r
1155 DoTinyGravity(ob);
\r
1161 if (jumptime <= tics)
\r
1167 jumptime = jumptime - tics;
\r
1169 if (jumptime == 0 && ob->state->nextstate)
\r
1170 ob->state = ob->state->nextstate;
\r
1174 if (gamestate.difficulty == gd_Easy)
\r
1176 DoWeakGravity(ob);
\r
1186 if (ob->xspeed == 0)
\r
1187 ob->xdir = c.xaxis;
\r
1188 AccelerateX(ob, c.xaxis, 24);
\r
1192 xtry = xtry + ob->xspeed * tics;
\r
1193 if (ob->xspeed > 0)
\r
1197 else if (ob->xspeed < 0)
\r
1203 if (ob->hitsouth == 17) // going through a pole hole
\r
1205 ob->xspeed = xtry = 0;
\r
1208 if (firebutton && !fireheld)
\r
1214 ob->state = &s_keenairshootup1;
\r
1217 ob->state = &s_keenairshoot1;
\r
1220 ob->state = &s_keenairshootdown1;
\r
1225 if (pogobutton && !pogoheld)
\r
1228 ob->state = &s_keenjump3;
\r
1232 //===========================================================================
\r
1235 =======================
\r
1239 =======================
\r
1242 void PoleActions(objtype *ob)
\r
1245 ob->xdir = c.xaxis;
\r
1247 if (firebutton && !fireheld)
\r
1253 ob->state = &s_keenpoleshootup1;
\r
1256 ob->state = &s_keenpoleshoot1;
\r
1259 ob->state = &s_keenpoleshootdown1;
\r
1264 if (jumpbutton && !jumpheld) // jump off the pole
\r
1267 SD_PlaySound(SND_JUMP);
\r
1268 ob->xspeed = polexspeed[c.xaxis+1];
\r
1270 ob->needtoclip = cl_midclip;
\r
1272 ob->state = &s_keenjump1;
\r
1274 leavepoletime = lasttimecount;
\r
1279 =======================
\r
1283 =======================
\r
1286 void KeenPoleThink(objtype *ob)
\r
1294 ob->state = &s_keenclimb1;
\r
1298 ob->state = &s_keenslide1;
\r
1300 KeenDropThink(ob);
\r
1307 // walk off pole if right next to ground
\r
1309 map = mapsegs[1] + (mapbwidthtable[ob->tilebottom+1]/2 + ob->tilemidx);
\r
1311 if (tinf[NORTHWALL+tile])
\r
1315 ob->needtoclip = cl_midclip;
\r
1317 ob->state = &s_keenjump3;
\r
1319 SD_PlaySound(SND_PLUMMET);
\r
1327 =======================
\r
1331 =======================
\r
1334 void KeenClimbThink(objtype *ob)
\r
1338 map = mapsegs[1] + mapbwidthtable[ob->tiletop]/2 + ob->tilemidx;
\r
1340 if ((tinf[INTILE+*map] & 0x7F) != INTILE_POLE)
\r
1343 ob->state = &s_keenpole; // ran out of pole
\r
1351 ob->state = &s_keenpole;
\r
1356 ob->state = &s_keenslide1;
\r
1358 KeenDropThink(ob);
\r
1366 =======================
\r
1370 =======================
\r
1373 void KeenDropThink(objtype *ob)
\r
1378 y = CONVERT_GLOBAL_TO_TILE(ob->bottom - 4*PIXGLOBAL);
\r
1379 map = mapsegs[1] + mapbwidthtable[y]/2 + ob->tilemidx;
\r
1381 if ((tinf[INTILE+*map] & 0x7F) != INTILE_POLE)
\r
1383 SD_PlaySound(SND_PLUMMET);
\r
1384 ob->state = &s_keenjump3; // ran out of pole
\r
1386 ob->xspeed = polexspeed[c.xaxis+1];
\r
1388 ob->needtoclip = cl_midclip;
\r
1396 ob->state = &s_keenclimb1;
\r
1401 ob->state = &s_keenpole;
\r
1409 //===========================================================================
\r
1412 =======================
\r
1414 = KeenDropDownThink
\r
1416 =======================
\r
1419 void KeenDropDownThink(objtype *ob)
\r
1421 ob->needtoclip = cl_midclip;
\r
1424 //===========================================================================
\r
1427 =======================
\r
1431 =======================
\r
1434 void KeenHoldThink(objtype *ob)
\r
1438 if (c.yaxis == -1 || ob->xdir == c.xaxis)
\r
1440 ob->state = &s_keenclimbup;
\r
1441 if (ob->xdir == 1)
\r
1443 tile = *(mapsegs[1]+mapbwidthtable[ob->tiletop-1]/2+ob->tileright);
\r
1447 tile = *(mapsegs[1]+mapbwidthtable[ob->tiletop-1]/2+ob->tileleft);
\r
1449 if (ob->xdir == 1)
\r
1451 ytry = -16*PIXGLOBAL;
\r
1455 ytry = -8*PIXGLOBAL;
\r
1457 if (!(tinf[INTILE+tile] & INTILE_FOREGROUND))
\r
1460 else if (c.yaxis == 1 || c.xaxis && ob->xdir != c.xaxis)
\r
1462 ob->state = &s_keenjump3;
\r
1463 ob->needtoclip = cl_midclip;
\r
1467 //===========================================================================
\r
1470 =======================
\r
1474 =======================
\r
1477 void KeenShootThink(objtype *ob)
\r
1479 // can't use &<var> in a switch statement...
\r
1481 if (ob->state == &s_keenshoot1)
\r
1483 if (ob->xdir == 1)
\r
1485 SpawnShot(ob->x + 16*PIXGLOBAL, ob->y + 4*PIXGLOBAL, dir_East);
\r
1489 SpawnShot(ob->x - 8*PIXGLOBAL, ob->y + 4*PIXGLOBAL, dir_West);
\r
1492 if (ob->state == &s_keenairshoot2)
\r
1494 if (ob->xdir == 1)
\r
1496 SpawnShot(ob->x + 16*PIXGLOBAL, ob->y + 2*PIXGLOBAL, dir_East);
\r
1500 SpawnShot(ob->x, ob->y + 2*PIXGLOBAL, dir_West);
\r
1503 if (ob->state == &s_keenairshootdown2)
\r
1505 SpawnShot(ob->x + 8*PIXGLOBAL, ob->y + 18*PIXGLOBAL, dir_South);
\r
1507 if (ob->state == &s_keenairshootup2)
\r
1509 SpawnShot(ob->x + 5*PIXGLOBAL, ob->y - 10*PIXGLOBAL, dir_North);
\r
1511 if (ob->state == &s_keenshootup1)
\r
1513 SpawnShot(ob->x + 5*PIXGLOBAL, ob->y - 10*PIXGLOBAL, dir_North);
\r
1515 if (ob->state == &s_keenpoleshoot1)
\r
1517 if (ob->xdir == 1)
\r
1519 SpawnShot(ob->x + 16*PIXGLOBAL, ob->y + 4*PIXGLOBAL, dir_East);
\r
1523 SpawnShot(ob->x - 8*PIXGLOBAL, ob->y + 4*PIXGLOBAL, dir_West);
\r
1526 if (ob->state == &s_keenpoleshootup1)
\r
1528 if (ob->xdir == 1)
\r
1530 SpawnShot(ob->x + 6*PIXGLOBAL, ob->y + 4*PIXGLOBAL, dir_North);
\r
1534 SpawnShot(ob->x + 12*PIXGLOBAL, ob->y + 4*PIXGLOBAL, dir_North);
\r
1537 if (ob->state == &s_keenpoleshootdown1)
\r
1539 if (ob->xdir == 1)
\r
1541 SpawnShot(ob->x + 6*PIXGLOBAL, ob->y + 24*PIXGLOBAL, dir_South);
\r
1545 SpawnShot(ob->x + 12*PIXGLOBAL, ob->y + 24*PIXGLOBAL, dir_South);
\r
1550 //===========================================================================
\r
1553 =======================
\r
1557 =======================
\r
1560 void T_PullUp1(objtype *ob)
\r
1562 if (ob->xdir == 1)
\r
1564 xtry = 8*PIXGLOBAL;
\r
1568 ytry = -8*PIXGLOBAL;
\r
1573 =======================
\r
1577 =======================
\r
1580 void T_PullUp2(objtype *ob)
\r
1582 if (ob->xdir == 1)
\r
1584 xtry = 8*PIXGLOBAL;
\r
1585 ytry = -8*PIXGLOBAL;
\r
1589 xtry = -8*PIXGLOBAL;
\r
1590 ytry = -8*PIXGLOBAL;
\r
1595 =======================
\r
1599 =======================
\r
1603 void T_PullUp3(objtype *ob)
\r
1605 ytry = -8*PIXGLOBAL;
\r
1609 =======================
\r
1613 =======================
\r
1616 void T_PulledUp(objtype *ob)
\r
1618 ob->needtoclip = cl_midclip;
\r
1620 ytry = 8*PIXGLOBAL;
\r
1623 //===========================================================================
\r
1626 =======================
\r
1630 =======================
\r
1633 void KeenDieThink(objtype *ob)
\r
1635 DoWeakGravity(ob);
\r
1636 xtry = ob->xspeed * tics;
\r
1637 if (!OnScreen(ob))
\r
1638 playstate = ex_died;
\r
1642 =============================================================================
\r
1646 =============================================================================
\r
1650 ============================
\r
1654 ============================
\r
1657 void KillKeen(void)
\r
1659 if (invincible || godmode)
\r
1662 if (player->state != &s_keendead)
\r
1666 invincible = 30; //0.43 seconds
\r
1667 keenkilled = true;
\r
1668 player->needtoclip = cl_noclip;
\r
1669 player->priority = 3;
\r
1673 if (US_RndT() < 0x80)
\r
1675 ChangeState(player, &s_keensuitdie1);
\r
1679 ChangeState(player, &s_keensuitdie2);
\r
1685 if (US_RndT() < 0x80)
\r
1687 ChangeState(player, &s_keendie1);
\r
1691 ChangeState(player, &s_keendie2);
\r
1694 SD_PlaySound(SND_KEENDEAD);
\r
1695 player->yspeed = -40;
\r
1696 player->xspeed = 16;
\r
1701 ============================
\r
1705 ============================
\r
1708 void KeenContact(objtype *ob, objtype *hit)
\r
1710 switch (hit->obclass)
\r
1713 switch (hit->temp1)
\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
1737 gamestate.keys[hit->temp1]++;
\r
1739 else if (hit->temp1 == 10)
\r
1741 gamestate.lives++;
\r
1743 else if (hit->temp1 == 11)
\r
1745 gamestate.ammo += shotsinclip[gamestate.difficulty];
\r
1748 else if (hit->temp1 == 12)
\r
1750 gamestate.keycard = true;
\r
1753 ChangeState(hit, &s_bonusrise);
\r
1759 if (!ob->hitnorth)
\r
1765 RF_ForceRefresh();
\r
1770 SD_PlaySound(SND_LINDSEY);
\r
1771 playstate = ex_rescued;
\r
1775 if (hit->temp4 != bounderobj)
\r
1777 //no break here -- drop through to platformobj is intended!
\r
1779 if (!gamestate.riding)
\r
1781 ClipToSpriteTop(ob, hit);
\r
1787 ClipToSprite(ob, hit, false);
\r
1790 PrincessLindsey();
\r
1792 RF_ForceRefresh();
\r
1795 playstate = ex_foot;
\r
1797 #elif defined KEEN5
\r
1799 if (!gamestate.riding)
\r
1800 ClipToSpriteTop(ob, hit);
\r
1802 #elif defined KEEN6
\r
1807 ChangeState(ob, &s_keenstun);
\r
1809 // BUG: there is no break here - this causes the impossible bullet bug
\r
1811 if (!gamestate.riding)
\r
1812 ClipToSpriteTop(ob, hit);
\r
1819 ============================
\r
1823 ============================
\r
1826 void KeenPosContact(objtype *ob, objtype *hit)
\r
1828 switch (hit->obclass)
\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
1838 case madmushroomobj:
\r
1840 case berkeloidobj:
\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
1850 #elif defined KEEN5
\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
1861 ob->needtoclip = cl_midclip;
\r
1862 ChangeState(ob, &s_keenjump3);
\r
1863 jumptime = ob->xspeed = ob->yspeed = 0;
\r
1865 #elif defined KEEN6
\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
1881 ============================
\r
1885 ============================
\r
1888 void HandleRiding(objtype *ob)
\r
1892 plat = gamestate.riding;
\r
1893 if (ob->right < plat->left || ob->left > plat->right)
\r
1895 gamestate.riding = NULL;
\r
1897 else if (ob->ymove < 0)
\r
1899 gamestate.riding = NULL;
\r
1900 if (plat->ymove < 0)
\r
1903 ytry = plat->ymove;
\r
1909 xtry = plat->xmove;
\r
1910 ytry = plat->top - ob->bottom - 16;
\r
1913 #if GRMODE == CGAGR
\r
1914 if (ob->xmove == plat->xmove)
\r
1917 ob->x |= plat->x & 0x3F;
\r
1923 ob->x |= plat->x & 0x7F;
\r
1927 ob->x |= plat->x & 0x1F;
\r
1933 gamestate.riding = NULL;
\r
1937 ob->hitnorth = 25;
\r
1943 ============================
\r
1947 ============================
\r
1950 void TileBonus(Uint16 x, Uint16 y, Uint16 bonus)
\r
1952 RF_MemToMap(&zeromap, 1, x, y, 1, 1);
\r
1953 SD_PlaySound(bonussound[bonus]);
\r
1954 GivePoints(bonuspoints[bonus]);
\r
1957 gamestate.keys[bonus]++;
\r
1959 else if (bonus == 10)
\r
1961 gamestate.lives++;
\r
1963 else if (bonus == 11)
\r
1965 gamestate.ammo += shotsinclip[gamestate.difficulty];
\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
1973 new->temp2 = new->shapenum = bonussprite[bonus];
\r
1974 NewState(new, &s_bonusrise);
\r
1975 new->needtoclip = cl_noclip;
\r
1979 ============================
\r
1983 ============================
\r
1986 void GiveDrop(Uint16 x, Uint16 y)
\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
1993 gamestate.drops = 0;
\r
1994 SD_PlaySound(SND_EXTRAKEEN);
\r
1995 gamestate.lives++;
\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
2002 new->temp2 = new->shapenum = BONUS100UPSPR;
\r
2003 NewState(new, &s_bonusrise);
\r
2004 new->needtoclip = cl_noclip;
\r
2009 ============================
\r
2013 ============================
\r
2016 void CheckInTiles(objtype *ob)
\r
2020 Uint16 rowdelta, intile, midx;
\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
2029 for (x = ob->tileleft; x <= ob->tileright; x++, map++)
\r
2031 if ((intile = tinf[INTILE + *map] & INTILE_TYPEMASK) != 0)
\r
2035 case INTILE_DEADLY:
\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
2054 midx = CONVERT_TILE_TO_GLOBAL(x) + -4*PIXGLOBAL;
\r
2055 if (ob->x != midx)
\r
2058 ob->state = &s_keenlineup;
\r
2063 gamestate.keys[intile-INTILE_GEMSOCKET0]--;
\r
2064 ChangeState(ob, &s_keenkey);
\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
2081 TileBonus(x, y, (intile-INTILE_BONUS100)+4);
\r
2090 =============================================================================
\r
2094 =============================================================================
\r
2099 ============================
\r
2103 ============================
\r
2106 void KeenSimpleReact(objtype *ob)
\r
2108 RF_PlaceSprite(&ob->sprite, ob->x, ob->y, ob->shapenum, spritedraw, ob->priority);
\r
2113 ============================
\r
2117 ============================
\r
2120 void KeenStandReact(objtype *ob)
\r
2122 if (!ob->hitnorth)
\r
2125 // walked off an edge
\r
2127 SD_PlaySound(SND_PLUMMET);
\r
2128 ob->xspeed = ob->xdir * 8;
\r
2130 ChangeState(ob, &s_keenjump3);
\r
2133 else if ((ob->hitnorth & ~7) == 8) // deadly floor!
\r
2137 else if (ob->hitnorth == 41)
\r
2144 else if (ob->hitnorth == 49)
\r
2152 RF_PlaceSprite(&ob->sprite, ob->x, ob->y, ob->shapenum, spritedraw, ob->priority);
\r
2156 ============================
\r
2160 ============================
\r
2163 void KeenWalkReact(objtype *ob)
\r
2165 if (!ob->hitnorth)
\r
2168 // walked off an edge
\r
2170 SD_PlaySound(SND_PLUMMET);
\r
2171 ob->xspeed = ob->xdir * 8;
\r
2173 ChangeState(ob, &s_keenjump3);
\r
2176 else if ((ob->hitnorth & ~7) == 8) // deadly floor!
\r
2180 else if (ob->hitnorth == 41)
\r
2186 else if (ob->hitnorth == 49)
\r
2192 else if (ob->hiteast && ob->xdir == -1 || ob->hitwest && ob->xdir == 1)
\r
2195 // ran into a wall
\r
2198 ob->state = &s_keenstand;
\r
2199 ob->shapenum = ob->xdir == 1? s_keenstand.rightshapenum : s_keenstand.leftshapenum;
\r
2202 RF_PlaceSprite(&ob->sprite, ob->x, ob->y, ob->shapenum, spritedraw, ob->priority);
\r
2206 ============================
\r
2210 ============================
\r
2213 void KeenAirReact(objtype *ob)
\r
2216 Uint16 oldtop, graby, ty, tile;
\r
2218 if (ob->hiteast && ob->xdir == -1 || ob->hitwest && ob->xdir == 1)
\r
2223 if (ob->hitsouth == 17) // jumping up through a pole hole
\r
2225 ob->y -= 2*PIXGLOBAL;
\r
2226 ob->top -= 2*PIXGLOBAL;
\r
2228 ob->x = CONVERT_TILE_TO_GLOBAL(ob->tilemidx) - 2*PIXGLOBAL;
\r
2233 if (ob->hitsouth == 33)
\r
2235 FlipBigSwitch(ob, false);
\r
2240 SD_PlaySound(SND_HELMETHIT);
\r
2241 if (ob->hitsouth > 1)
\r
2244 if (ob->yspeed < 0) // push away from slopes
\r
2259 if ((ob->hitnorth & ~7) == 8) // deadly floor!
\r
2266 if (ob->hitnorth == 57)
\r
2268 SD_PlaySound(SND_LANDONFUSE);
\r
2270 #elif defined KEEN6
\r
2271 if (ob->hitnorth == 33)
\r
2273 FlipBigSwitch(ob, true);
\r
2276 if (ob->hitnorth != 25 || !jumptime) // KLUDGE to allow jumping off
\r
2278 ob->temp1 = ob->temp2 = 0;
\r
2279 if (ob->state == &s_keenairshoot1)
\r
2281 ChangeState(ob, &s_keenshoot1);
\r
2283 else if (ob->state == &s_keenairshootup1)
\r
2285 ChangeState(ob, &s_keenshootup1);
\r
2289 ChangeState(ob, &s_keenwalk1);
\r
2293 ChangeState(ob, &s_keenstand);
\r
2295 SD_PlaySound(SND_LAND);
\r
2299 else if (ob->ymove > 0)
\r
2302 // check if there is an edge to grab
\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
2309 if (c.xaxis == -1)
\r
2311 map = mapsegs[1] + mapbwidthtable[ty]/2 + ob->tileleft;
\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
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
2328 else if (c.xaxis == 1)
\r
2330 map = mapsegs[1] + mapbwidthtable[ty]/2 + ob->tileright;
\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
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
2350 RF_PlaceSprite(&ob->sprite, ob->x, ob->y, ob->shapenum, spritedraw, ob->priority);
\r
2355 ============================
\r
2359 ============================
\r
2362 void BreakFuse(Uint16 tileX, Uint16 tileY)
\r
2364 Uint16 tiles[] = {1932, 1950}; // should be 'static' for less overhead
\r
2366 // The original disassembly had some code here equivalent to this:
\r
2368 // _AX = tiles[0];
\r
2371 // As it turned out, that was just a compiler quirk.
\r
2373 SpawnFuseFlash(tileX, tileY);
\r
2374 if (--gamestate.numfuses == 0)
\r
2376 SpawnDeadMachine();
\r
2378 RF_MemToMap(tiles, 1, tileX, tileY, 1, 2);
\r
2383 ============================
\r
2387 ============================
\r
2390 void KeenPogoReact(objtype *ob)
\r
2392 if (ob->hiteast && ob->xdir == -1 || ob->hitwest && ob->xdir == 1)
\r
2397 if (ob->hitsouth == 17) // jumping up through a pole hole
\r
2399 ob->y -= 2*PIXGLOBAL;
\r
2400 ob->top -= 2*PIXGLOBAL;
\r
2402 ob->x = CONVERT_TILE_TO_GLOBAL(ob->tilemidx) - 2*PIXGLOBAL;
\r
2407 if (ob->hitsouth == 33)
\r
2409 FlipBigSwitch(ob, false);
\r
2414 SD_PlaySound(SND_HELMETHIT);
\r
2415 if (ob->hitsouth > 1)
\r
2418 if (ob->yspeed < 0) // push away from slopes
\r
2433 if ((ob->hitnorth & ~7) == 8) // deadly floor!
\r
2440 if (ob->hitnorth == 57)
\r
2442 if (ob->yspeed < 48)
\r
2444 SD_PlaySound(SND_LANDONFUSE);
\r
2448 BreakFuse(ob->tilemidx, ob->tilebottom);
\r
2449 RF_PlaceSprite(&ob->sprite, ob->x, ob->y, ob->shapenum, spritedraw, ob->priority);
\r
2453 #elif defined KEEN6
\r
2454 if (ob->hitnorth == 33)
\r
2456 FlipBigSwitch(ob, true);
\r
2458 else if (ob->hitnorth == 41)
\r
2461 if (ob->xspeed > 8)
\r
2464 else if (ob->hitnorth == 49)
\r
2467 if (ob->xspeed < -8)
\r
2471 if (ob->hitnorth != 25 || !jumptime) // KLUDGE to allow jumping off
\r
2475 SD_PlaySound(SND_POGOBOUNCE);
\r
2476 ChangeState(ob, &s_keenpogo);
\r
2481 RF_PlaceSprite(&ob->sprite, ob->x, ob->y, ob->shapenum, spritedraw, ob->priority);
\r
2485 ============================
\r
2489 ============================
\r
2492 void KeenPoleReact(objtype *ob)
\r
2497 map = mapsegs[1] + mapbwidthtable[ob->tilebottom]/2 + ob->tilemidx;
\r
2498 if (tinf[NORTHWALL + *map] == 1)
\r
2500 ymove = (ob->bottom & 0xFF) + 1;
\r
2502 ob->bottom -= ymove;
\r
2504 ob->needtoclip = cl_midclip;
\r
2505 ChangeState(ob, &s_keenlookdown);
\r
2508 RF_PlaceSprite(&ob->sprite, ob->x, ob->y, ob->shapenum, spritedraw, ob->priority);
\r