diff options
Diffstat (limited to 'apps/plugins/doom/p_inter.c')
-rw-r--r-- | apps/plugins/doom/p_inter.c | 904 |
1 files changed, 904 insertions, 0 deletions
diff --git a/apps/plugins/doom/p_inter.c b/apps/plugins/doom/p_inter.c new file mode 100644 index 0000000000..f4a0f80c8f --- /dev/null +++ b/apps/plugins/doom/p_inter.c | |||
@@ -0,0 +1,904 @@ | |||
1 | /* Emacs style mode select -*- C++ -*- | ||
2 | *----------------------------------------------------------------------------- | ||
3 | * | ||
4 | * | ||
5 | * PrBoom a Doom port merged with LxDoom and LSDLDoom | ||
6 | * based on BOOM, a modified and improved DOOM engine | ||
7 | * Copyright (C) 1999 by | ||
8 | * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman | ||
9 | * Copyright (C) 1999-2000 by | ||
10 | * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or | ||
13 | * modify it under the terms of the GNU General Public License | ||
14 | * as published by the Free Software Foundation; either version 2 | ||
15 | * of the License, or (at your option) any later version. | ||
16 | * | ||
17 | * This program is distributed in the hope that it will be useful, | ||
18 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
20 | * GNU General Public License for more details. | ||
21 | * | ||
22 | * You should have received a copy of the GNU General Public License | ||
23 | * along with this program; if not, write to the Free Software | ||
24 | * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA | ||
25 | * 02111-1307, USA. | ||
26 | * | ||
27 | * DESCRIPTION: | ||
28 | * Handling interactions (i.e., collisions). | ||
29 | * | ||
30 | *-----------------------------------------------------------------------------*/ | ||
31 | |||
32 | #include "doomstat.h" | ||
33 | #include "dstrings.h" | ||
34 | #include "m_random.h" | ||
35 | #include "am_map.h" | ||
36 | #include "r_main.h" | ||
37 | #include "s_sound.h" | ||
38 | #include "sounds.h" | ||
39 | //#include "d_deh.h" // Ty 03/22/98 - externalized strings | ||
40 | #include "p_tick.h" | ||
41 | #include "i_system.h" | ||
42 | #include "p_inter.h" | ||
43 | #include "p_enemy.h" | ||
44 | |||
45 | #ifdef __GNUG__ | ||
46 | #pragma implementation "p_inter.h" | ||
47 | #endif | ||
48 | #include "p_inter.h" | ||
49 | |||
50 | #define BONUSADD 6 | ||
51 | |||
52 | // Ty 03/07/98 - add deh externals | ||
53 | // Maximums and such were hardcoded values. Need to externalize those for | ||
54 | // dehacked support (and future flexibility). Most var names came from the key | ||
55 | // strings used in dehacked. | ||
56 | |||
57 | int initial_health = 100; | ||
58 | int initial_bullets = 50; | ||
59 | int maxhealth = 100; // was MAXHEALTH as a #define, used only in this module | ||
60 | int max_armor = 200; | ||
61 | int green_armor_class = 1; // these are involved with armortype below | ||
62 | int blue_armor_class = 2; | ||
63 | int max_soul = 200; | ||
64 | int soul_health = 100; | ||
65 | int mega_health = 200; | ||
66 | int god_health = 100; // these are used in cheats (see st_stuff.c) | ||
67 | int idfa_armor = 200; | ||
68 | int idfa_armor_class = 2; | ||
69 | // not actually used due to pairing of cheat_k and cheat_fa | ||
70 | int idkfa_armor = 200; | ||
71 | int idkfa_armor_class = 2; | ||
72 | |||
73 | int bfgcells = 40; // used in p_pspr.c | ||
74 | // Ty 03/07/98 - end deh externals | ||
75 | |||
76 | // a weapon is found with two clip loads, | ||
77 | // a big item has five clip loads | ||
78 | int maxammo[NUMAMMO] = {200, 50, 300, 50}; | ||
79 | int clipammo[NUMAMMO] = { 10, 4, 20, 1}; | ||
80 | |||
81 | // | ||
82 | // GET STUFF | ||
83 | // | ||
84 | |||
85 | // | ||
86 | // P_GiveAmmo | ||
87 | // Num is the number of clip loads, | ||
88 | // not the individual count (0= 1/2 clip). | ||
89 | // Returns false if the ammo can't be picked up at all | ||
90 | // | ||
91 | |||
92 | boolean P_GiveAmmo(player_t *player, ammotype_t ammo, int num) | ||
93 | { | ||
94 | int oldammo; | ||
95 | |||
96 | if (ammo == am_noammo) | ||
97 | return false; | ||
98 | |||
99 | #ifdef RANGECHECK | ||
100 | if (ammo < 0 || ammo > NUMAMMO) | ||
101 | I_Error ("P_GiveAmmo: bad type %i", ammo); | ||
102 | #endif | ||
103 | |||
104 | if ( player->ammo[ammo] == player->maxammo[ammo] ) | ||
105 | return false; | ||
106 | |||
107 | if (num) | ||
108 | num *= clipammo[ammo]; | ||
109 | else | ||
110 | num = clipammo[ammo]/2; | ||
111 | |||
112 | // give double ammo in trainer mode, you'll need in nightmare | ||
113 | if (gameskill == sk_baby || gameskill == sk_nightmare) | ||
114 | num <<= 1; | ||
115 | |||
116 | oldammo = player->ammo[ammo]; | ||
117 | player->ammo[ammo] += num; | ||
118 | |||
119 | if (player->ammo[ammo] > player->maxammo[ammo]) | ||
120 | player->ammo[ammo] = player->maxammo[ammo]; | ||
121 | |||
122 | // If non zero ammo, don't change up weapons, player was lower on purpose. | ||
123 | if (oldammo) | ||
124 | return true; | ||
125 | |||
126 | // We were down to zero, so select a new weapon. | ||
127 | // Preferences are not user selectable. | ||
128 | |||
129 | switch (ammo) | ||
130 | { | ||
131 | case am_clip: | ||
132 | if (player->readyweapon == wp_fist) { | ||
133 | if (player->weaponowned[wp_chaingun]) | ||
134 | player->pendingweapon = wp_chaingun; | ||
135 | else | ||
136 | player->pendingweapon = wp_pistol; | ||
137 | } | ||
138 | break; | ||
139 | |||
140 | case am_shell: | ||
141 | if (player->readyweapon == wp_fist || player->readyweapon == wp_pistol) | ||
142 | if (player->weaponowned[wp_shotgun]) | ||
143 | player->pendingweapon = wp_shotgun; | ||
144 | break; | ||
145 | |||
146 | case am_cell: | ||
147 | if (player->readyweapon == wp_fist || player->readyweapon == wp_pistol) | ||
148 | if (player->weaponowned[wp_plasma]) | ||
149 | player->pendingweapon = wp_plasma; | ||
150 | break; | ||
151 | |||
152 | case am_misl: | ||
153 | if (player->readyweapon == wp_fist) | ||
154 | if (player->weaponowned[wp_missile]) | ||
155 | player->pendingweapon = wp_missile; | ||
156 | default: | ||
157 | break; | ||
158 | } | ||
159 | return true; | ||
160 | } | ||
161 | |||
162 | // | ||
163 | // P_GiveWeapon | ||
164 | // The weapon name may have a MF_DROPPED flag ored in. | ||
165 | // | ||
166 | |||
167 | boolean P_GiveWeapon(player_t *player, weapontype_t weapon, boolean dropped) | ||
168 | { | ||
169 | boolean gaveammo; | ||
170 | boolean gaveweapon; | ||
171 | |||
172 | if (netgame && deathmatch!=2 && !dropped) | ||
173 | { | ||
174 | // leave placed weapons forever on net games | ||
175 | if (player->weaponowned[weapon]) | ||
176 | return false; | ||
177 | |||
178 | player->bonuscount += BONUSADD; | ||
179 | player->weaponowned[weapon] = true; | ||
180 | |||
181 | P_GiveAmmo(player, weaponinfo[weapon].ammo, deathmatch ? 5 : 2); | ||
182 | |||
183 | player->pendingweapon = weapon; | ||
184 | /* cph 20028/10 - for old-school DM addicts, allow old behavior | ||
185 | * where only consoleplayer's pickup sounds are heard */ | ||
186 | if (!comp[comp_sound] || player == &players[consoleplayer]) | ||
187 | S_StartSound (player->mo, sfx_wpnup|PICKUP_SOUND); // killough 4/25/98 | ||
188 | return false; | ||
189 | } | ||
190 | |||
191 | if (weaponinfo[weapon].ammo != am_noammo) | ||
192 | { | ||
193 | // give one clip with a dropped weapon, | ||
194 | // two clips with a found weapon | ||
195 | gaveammo = P_GiveAmmo (player, weaponinfo[weapon].ammo, dropped ? 1 : 2); | ||
196 | } | ||
197 | else | ||
198 | gaveammo = false; | ||
199 | |||
200 | if (player->weaponowned[weapon]) | ||
201 | gaveweapon = false; | ||
202 | else | ||
203 | { | ||
204 | gaveweapon = true; | ||
205 | player->weaponowned[weapon] = true; | ||
206 | player->pendingweapon = weapon; | ||
207 | } | ||
208 | return gaveweapon || gaveammo; | ||
209 | } | ||
210 | |||
211 | // | ||
212 | // P_GiveBody | ||
213 | // Returns false if the body isn't needed at all | ||
214 | // | ||
215 | |||
216 | boolean P_GiveBody(player_t *player, int num) | ||
217 | { | ||
218 | if (player->health >= maxhealth) | ||
219 | return false; // Ty 03/09/98 externalized MAXHEALTH to maxhealth | ||
220 | player->health += num; | ||
221 | if (player->health > maxhealth) | ||
222 | player->health = maxhealth; | ||
223 | player->mo->health = player->health; | ||
224 | return true; | ||
225 | } | ||
226 | |||
227 | // | ||
228 | // P_GiveArmor | ||
229 | // Returns false if the armor is worse | ||
230 | // than the current armor. | ||
231 | // | ||
232 | |||
233 | boolean P_GiveArmor(player_t *player, int armortype) | ||
234 | { | ||
235 | int hits = armortype*100; | ||
236 | if (player->armorpoints >= hits) | ||
237 | return false; // don't pick up | ||
238 | player->armortype = armortype; | ||
239 | player->armorpoints = hits; | ||
240 | return true; | ||
241 | } | ||
242 | |||
243 | // | ||
244 | // P_GiveCard | ||
245 | // | ||
246 | |||
247 | void P_GiveCard(player_t *player, card_t card) | ||
248 | { | ||
249 | if (player->cards[card]) | ||
250 | return; | ||
251 | player->bonuscount = BONUSADD; | ||
252 | player->cards[card] = 1; | ||
253 | } | ||
254 | |||
255 | // | ||
256 | // P_GivePower | ||
257 | // | ||
258 | // Rewritten by Lee Killough | ||
259 | // | ||
260 | |||
261 | boolean P_GivePower(player_t *player, int power) | ||
262 | { | ||
263 | static const int tics[NUMPOWERS] = { | ||
264 | INVULNTICS, 1 /* strength */, INVISTICS, | ||
265 | IRONTICS, 1 /* allmap */, INFRATICS, | ||
266 | }; | ||
267 | |||
268 | switch (power) | ||
269 | { | ||
270 | case pw_invisibility: | ||
271 | player->mo->flags |= MF_SHADOW; | ||
272 | break; | ||
273 | case pw_allmap: | ||
274 | if (player->powers[pw_allmap]) | ||
275 | return false; | ||
276 | break; | ||
277 | case pw_strength: | ||
278 | P_GiveBody(player,100); | ||
279 | break; | ||
280 | } | ||
281 | |||
282 | // Unless player has infinite duration cheat, set duration (killough) | ||
283 | |||
284 | if (player->powers[power] >= 0) | ||
285 | player->powers[power] = tics[power]; | ||
286 | return true; | ||
287 | } | ||
288 | |||
289 | // | ||
290 | // P_TouchSpecialThing | ||
291 | // | ||
292 | |||
293 | void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher) | ||
294 | { | ||
295 | player_t *player; | ||
296 | int i; | ||
297 | int sound; | ||
298 | fixed_t delta = special->z - toucher->z; | ||
299 | |||
300 | if (delta > toucher->height || delta < -8*FRACUNIT) | ||
301 | return; // out of reach | ||
302 | |||
303 | sound = sfx_itemup; | ||
304 | player = toucher->player; | ||
305 | |||
306 | // Dead thing touching. | ||
307 | // Can happen with a sliding player corpse. | ||
308 | if (toucher->health <= 0) | ||
309 | return; | ||
310 | |||
311 | // Identify by sprite. | ||
312 | switch (special->sprite) | ||
313 | { | ||
314 | // armor | ||
315 | case SPR_ARM1: | ||
316 | if (!P_GiveArmor (player, green_armor_class)) | ||
317 | return; | ||
318 | player->message = GOTARMOR; // Ty 03/22/98 - externalized | ||
319 | break; | ||
320 | |||
321 | case SPR_ARM2: | ||
322 | if (!P_GiveArmor (player, blue_armor_class)) | ||
323 | return; | ||
324 | player->message = GOTMEGA; // Ty 03/22/98 - externalized | ||
325 | break; | ||
326 | |||
327 | // bonus items | ||
328 | case SPR_BON1: | ||
329 | player->health++; // can go over 100% | ||
330 | if (player->health > (maxhealth * 2)) | ||
331 | player->health = (maxhealth * 2); | ||
332 | player->mo->health = player->health; | ||
333 | player->message = GOTHTHBONUS; // Ty 03/22/98 - externalized | ||
334 | break; | ||
335 | |||
336 | case SPR_BON2: | ||
337 | player->armorpoints++; // can go over 100% | ||
338 | if (player->armorpoints > max_armor) | ||
339 | player->armorpoints = max_armor; | ||
340 | if (!player->armortype) | ||
341 | player->armortype = green_armor_class; | ||
342 | player->message = GOTARMBONUS; // Ty 03/22/98 - externalized | ||
343 | break; | ||
344 | |||
345 | case SPR_SOUL: | ||
346 | player->health += soul_health; | ||
347 | if (player->health > max_soul) | ||
348 | player->health = max_soul; | ||
349 | player->mo->health = player->health; | ||
350 | player->message = GOTSUPER; // Ty 03/22/98 - externalized | ||
351 | sound = sfx_getpow; | ||
352 | break; | ||
353 | |||
354 | case SPR_MEGA: | ||
355 | if (gamemode != commercial) | ||
356 | return; | ||
357 | player->health = mega_health; | ||
358 | player->mo->health = player->health; | ||
359 | P_GiveArmor (player,blue_armor_class); | ||
360 | player->message = GOTMSPHERE; // Ty 03/22/98 - externalized | ||
361 | sound = sfx_getpow; | ||
362 | break; | ||
363 | |||
364 | // cards | ||
365 | // leave cards for everyone | ||
366 | case SPR_BKEY: | ||
367 | if (!player->cards[it_bluecard]) | ||
368 | player->message = GOTBLUECARD; // Ty 03/22/98 - externalized | ||
369 | P_GiveCard (player, it_bluecard); | ||
370 | if (!netgame) | ||
371 | break; | ||
372 | return; | ||
373 | |||
374 | case SPR_YKEY: | ||
375 | if (!player->cards[it_yellowcard]) | ||
376 | player->message = GOTYELWCARD; // Ty 03/22/98 - externalized | ||
377 | P_GiveCard (player, it_yellowcard); | ||
378 | if (!netgame) | ||
379 | break; | ||
380 | return; | ||
381 | |||
382 | case SPR_RKEY: | ||
383 | if (!player->cards[it_redcard]) | ||
384 | player->message = GOTREDCARD; // Ty 03/22/98 - externalized | ||
385 | P_GiveCard (player, it_redcard); | ||
386 | if (!netgame) | ||
387 | break; | ||
388 | return; | ||
389 | |||
390 | case SPR_BSKU: | ||
391 | if (!player->cards[it_blueskull]) | ||
392 | player->message = GOTBLUESKUL; // Ty 03/22/98 - externalized | ||
393 | P_GiveCard (player, it_blueskull); | ||
394 | if (!netgame) | ||
395 | break; | ||
396 | return; | ||
397 | |||
398 | case SPR_YSKU: | ||
399 | if (!player->cards[it_yellowskull]) | ||
400 | player->message = GOTYELWSKUL; // Ty 03/22/98 - externalized | ||
401 | P_GiveCard (player, it_yellowskull); | ||
402 | if (!netgame) | ||
403 | break; | ||
404 | return; | ||
405 | |||
406 | case SPR_RSKU: | ||
407 | if (!player->cards[it_redskull]) | ||
408 | player->message = GOTREDSKULL; // Ty 03/22/98 - externalized | ||
409 | P_GiveCard (player, it_redskull); | ||
410 | if (!netgame) | ||
411 | break; | ||
412 | return; | ||
413 | |||
414 | // medikits, heals | ||
415 | case SPR_STIM: | ||
416 | if (!P_GiveBody (player, 10)) | ||
417 | return; | ||
418 | player->message = GOTSTIM; // Ty 03/22/98 - externalized | ||
419 | break; | ||
420 | |||
421 | case SPR_MEDI: | ||
422 | if (!P_GiveBody (player, 25)) | ||
423 | return; | ||
424 | |||
425 | if (player->health < 50) // cph - 25 + the 25 just added, thanks to Quasar for reporting this bug | ||
426 | player->message = GOTMEDINEED; // Ty 03/22/98 - externalized | ||
427 | else | ||
428 | player->message = GOTMEDIKIT; // Ty 03/22/98 - externalized | ||
429 | break; | ||
430 | |||
431 | |||
432 | // power ups | ||
433 | case SPR_PINV: | ||
434 | if (!P_GivePower (player, pw_invulnerability)) | ||
435 | return; | ||
436 | player->message = GOTINVUL; // Ty 03/22/98 - externalized | ||
437 | sound = sfx_getpow; | ||
438 | break; | ||
439 | |||
440 | case SPR_PSTR: | ||
441 | if (!P_GivePower (player, pw_strength)) | ||
442 | return; | ||
443 | player->message = GOTBERSERK; // Ty 03/22/98 - externalized | ||
444 | if (player->readyweapon != wp_fist) | ||
445 | player->pendingweapon = wp_fist; | ||
446 | sound = sfx_getpow; | ||
447 | break; | ||
448 | |||
449 | case SPR_PINS: | ||
450 | if (!P_GivePower (player, pw_invisibility)) | ||
451 | return; | ||
452 | player->message = GOTINVIS; // Ty 03/22/98 - externalized | ||
453 | sound = sfx_getpow; | ||
454 | break; | ||
455 | |||
456 | case SPR_SUIT: | ||
457 | if (!P_GivePower (player, pw_ironfeet)) | ||
458 | return; | ||
459 | player->message = GOTSUIT; // Ty 03/22/98 - externalized | ||
460 | sound = sfx_getpow; | ||
461 | break; | ||
462 | |||
463 | case SPR_PMAP: | ||
464 | if (!P_GivePower (player, pw_allmap)) | ||
465 | return; | ||
466 | player->message = GOTMAP; // Ty 03/22/98 - externalized | ||
467 | sound = sfx_getpow; | ||
468 | break; | ||
469 | |||
470 | case SPR_PVIS: | ||
471 | if (!P_GivePower (player, pw_infrared)) | ||
472 | return; | ||
473 | player->message = GOTVISOR; // Ty 03/22/98 - externalized | ||
474 | sound = sfx_getpow; | ||
475 | break; | ||
476 | |||
477 | // ammo | ||
478 | case SPR_CLIP: | ||
479 | if (special->flags & MF_DROPPED) | ||
480 | { | ||
481 | if (!P_GiveAmmo (player,am_clip,0)) | ||
482 | return; | ||
483 | } | ||
484 | else | ||
485 | { | ||
486 | if (!P_GiveAmmo (player,am_clip,1)) | ||
487 | return; | ||
488 | } | ||
489 | player->message = GOTCLIP; // Ty 03/22/98 - externalized | ||
490 | break; | ||
491 | |||
492 | case SPR_AMMO: | ||
493 | if (!P_GiveAmmo (player, am_clip,5)) | ||
494 | return; | ||
495 | player->message = GOTCLIPBOX; // Ty 03/22/98 - externalized | ||
496 | break; | ||
497 | |||
498 | case SPR_ROCK: | ||
499 | if (!P_GiveAmmo (player, am_misl,1)) | ||
500 | return; | ||
501 | player->message = GOTROCKET; // Ty 03/22/98 - externalized | ||
502 | break; | ||
503 | |||
504 | case SPR_BROK: | ||
505 | if (!P_GiveAmmo (player, am_misl,5)) | ||
506 | return; | ||
507 | player->message = GOTROCKBOX; // Ty 03/22/98 - externalized | ||
508 | break; | ||
509 | |||
510 | case SPR_CELL: | ||
511 | if (!P_GiveAmmo (player, am_cell,1)) | ||
512 | return; | ||
513 | player->message = GOTCELL; // Ty 03/22/98 - externalized | ||
514 | break; | ||
515 | |||
516 | case SPR_CELP: | ||
517 | if (!P_GiveAmmo (player, am_cell,5)) | ||
518 | return; | ||
519 | player->message = GOTCELLBOX; // Ty 03/22/98 - externalized | ||
520 | break; | ||
521 | |||
522 | case SPR_SHEL: | ||
523 | if (!P_GiveAmmo (player, am_shell,1)) | ||
524 | return; | ||
525 | player->message = GOTSHELLS; // Ty 03/22/98 - externalized | ||
526 | break; | ||
527 | |||
528 | case SPR_SBOX: | ||
529 | if (!P_GiveAmmo (player, am_shell,5)) | ||
530 | return; | ||
531 | player->message = GOTSHELLBOX; // Ty 03/22/98 - externalized | ||
532 | break; | ||
533 | |||
534 | case SPR_BPAK: | ||
535 | if (!player->backpack) | ||
536 | { | ||
537 | for (i=0 ; i<NUMAMMO ; i++) | ||
538 | player->maxammo[i] *= 2; | ||
539 | player->backpack = true; | ||
540 | } | ||
541 | for (i=0 ; i<NUMAMMO ; i++) | ||
542 | P_GiveAmmo (player, i, 1); | ||
543 | player->message = GOTBACKPACK; // Ty 03/22/98 - externalized | ||
544 | break; | ||
545 | |||
546 | // weapons | ||
547 | case SPR_BFUG: | ||
548 | if (!P_GiveWeapon (player, wp_bfg, false) ) | ||
549 | return; | ||
550 | player->message = GOTBFG9000; // Ty 03/22/98 - externalized | ||
551 | sound = sfx_wpnup; | ||
552 | break; | ||
553 | |||
554 | case SPR_MGUN: | ||
555 | if (!P_GiveWeapon (player, wp_chaingun, (special->flags&MF_DROPPED)!=0) ) | ||
556 | return; | ||
557 | player->message = GOTCHAINGUN; // Ty 03/22/98 - externalized | ||
558 | sound = sfx_wpnup; | ||
559 | break; | ||
560 | |||
561 | case SPR_CSAW: | ||
562 | if (!P_GiveWeapon (player, wp_chainsaw, false) ) | ||
563 | return; | ||
564 | player->message = GOTCHAINSAW; // Ty 03/22/98 - externalized | ||
565 | sound = sfx_wpnup; | ||
566 | break; | ||
567 | |||
568 | case SPR_LAUN: | ||
569 | if (!P_GiveWeapon (player, wp_missile, false) ) | ||
570 | return; | ||
571 | player->message = GOTLAUNCHER; // Ty 03/22/98 - externalized | ||
572 | sound = sfx_wpnup; | ||
573 | break; | ||
574 | |||
575 | case SPR_PLAS: | ||
576 | if (!P_GiveWeapon (player, wp_plasma, false) ) | ||
577 | return; | ||
578 | player->message = GOTPLASMA; // Ty 03/22/98 - externalized | ||
579 | sound = sfx_wpnup; | ||
580 | break; | ||
581 | |||
582 | case SPR_SHOT: | ||
583 | if (!P_GiveWeapon (player, wp_shotgun, (special->flags&MF_DROPPED)!=0 ) ) | ||
584 | return; | ||
585 | player->message = GOTSHOTGUN; // Ty 03/22/98 - externalized | ||
586 | sound = sfx_wpnup; | ||
587 | break; | ||
588 | |||
589 | case SPR_SGN2: | ||
590 | if (!P_GiveWeapon(player, wp_supershotgun, (special->flags&MF_DROPPED)!=0)) | ||
591 | return; | ||
592 | player->message = GOTSHOTGUN2; // Ty 03/22/98 - externalized | ||
593 | sound = sfx_wpnup; | ||
594 | break; | ||
595 | |||
596 | default: | ||
597 | I_Error ("P_SpecialThing: Unknown gettable thing"); | ||
598 | } | ||
599 | |||
600 | if (special->flags & MF_COUNTITEM) | ||
601 | player->itemcount++; | ||
602 | P_RemoveMobj (special); | ||
603 | player->bonuscount += BONUSADD; | ||
604 | |||
605 | /* cph 20028/10 - for old-school DM addicts, allow old behavior | ||
606 | * where only consoleplayer's pickup sounds are heard */ | ||
607 | if (!comp[comp_sound] || player == &players[consoleplayer]) | ||
608 | S_StartSound (player->mo, sound | PICKUP_SOUND); // killough 4/25/98 | ||
609 | } | ||
610 | |||
611 | // | ||
612 | // KillMobj | ||
613 | // | ||
614 | // killough 11/98: make static | ||
615 | static void P_KillMobj(mobj_t *source, mobj_t *target) | ||
616 | { | ||
617 | mobjtype_t item; | ||
618 | mobj_t *mo; | ||
619 | |||
620 | target->flags &= ~(MF_SHOOTABLE|MF_FLOAT|MF_SKULLFLY); | ||
621 | |||
622 | if (target->type != MT_SKULL) | ||
623 | target->flags &= ~MF_NOGRAVITY; | ||
624 | |||
625 | target->flags |= MF_CORPSE|MF_DROPOFF; | ||
626 | target->height >>= 2; | ||
627 | |||
628 | if (!((target->flags ^ MF_COUNTKILL) & (MF_FRIEND | MF_COUNTKILL))) | ||
629 | totallive--; | ||
630 | |||
631 | if (source && source->player) | ||
632 | { | ||
633 | // count for intermission | ||
634 | if (target->flags & MF_COUNTKILL) | ||
635 | source->player->killcount++; | ||
636 | if (target->player) | ||
637 | source->player->frags[target->player-players]++; | ||
638 | } | ||
639 | else | ||
640 | if (target->flags & MF_COUNTKILL) { /* Add to kills tally */ | ||
641 | if ((compatibility_level < lxdoom_1_compatibility) || !netgame) { | ||
642 | if (!netgame) | ||
643 | // count all monster deaths, | ||
644 | // even those caused by other monsters | ||
645 | players[0].killcount++; | ||
646 | } else | ||
647 | if (!deathmatch) { | ||
648 | // try and find a player to give the kill to, otherwise give the | ||
649 | // kill to a random player. this fixes the missing monsters bug | ||
650 | // in coop - rain | ||
651 | // CPhipps - not a bug as such, but certainly an inconsistency. | ||
652 | if (target->lastenemy && target->lastenemy->health > 0 | ||
653 | && target->lastenemy->player) // Fighting a player | ||
654 | target->lastenemy->player->killcount++; | ||
655 | else { | ||
656 | // cph - randomely choose a player in the game to be credited | ||
657 | // and do it uniformly between the active players | ||
658 | unsigned int activeplayers = 0, player, i; | ||
659 | |||
660 | for (player = 0; player<MAXPLAYERS; player++) | ||
661 | if (playeringame[player]) | ||
662 | activeplayers++; | ||
663 | |||
664 | if (activeplayers) { | ||
665 | player = P_Random(pr_friends) % activeplayers; | ||
666 | |||
667 | for (i=0; i<MAXPLAYERS; i++) | ||
668 | if (playeringame[i]) | ||
669 | if (!player--) | ||
670 | players[i].killcount++; | ||
671 | } | ||
672 | } | ||
673 | } | ||
674 | } | ||
675 | |||
676 | if (target->player) | ||
677 | { | ||
678 | // count environment kills against you | ||
679 | if (!source) | ||
680 | target->player->frags[target->player-players]++; | ||
681 | |||
682 | target->flags &= ~MF_SOLID; | ||
683 | target->player->playerstate = PST_DEAD; | ||
684 | P_DropWeapon (target->player); | ||
685 | |||
686 | if (target->player == &players[consoleplayer] && (automapmode & am_active)) | ||
687 | AM_Stop(); // don't die in auto map; switch view prior to dying | ||
688 | } | ||
689 | |||
690 | if (target->health < -target->info->spawnhealth && target->info->xdeathstate) | ||
691 | P_SetMobjState (target, target->info->xdeathstate); | ||
692 | else | ||
693 | P_SetMobjState (target, target->info->deathstate); | ||
694 | |||
695 | target->tics -= P_Random(pr_killtics)&3; | ||
696 | |||
697 | if (target->tics < 1) | ||
698 | target->tics = 1; | ||
699 | |||
700 | // Drop stuff. | ||
701 | // This determines the kind of object spawned | ||
702 | // during the death frame of a thing. | ||
703 | |||
704 | switch (target->type) | ||
705 | { | ||
706 | case MT_WOLFSS: | ||
707 | case MT_POSSESSED: | ||
708 | item = MT_CLIP; | ||
709 | break; | ||
710 | |||
711 | case MT_SHOTGUY: | ||
712 | item = MT_SHOTGUN; | ||
713 | break; | ||
714 | |||
715 | case MT_CHAINGUY: | ||
716 | item = MT_CHAINGUN; | ||
717 | break; | ||
718 | |||
719 | default: | ||
720 | return; | ||
721 | } | ||
722 | |||
723 | mo = P_SpawnMobj (target->x,target->y,ONFLOORZ, item); | ||
724 | mo->flags |= MF_DROPPED; // special versions of items | ||
725 | } | ||
726 | |||
727 | // | ||
728 | // P_DamageMobj | ||
729 | // Damages both enemies and players | ||
730 | // "inflictor" is the thing that caused the damage | ||
731 | // creature or missile, can be NULL (slime, etc) | ||
732 | // "source" is the thing to target after taking damage | ||
733 | // creature or NULL | ||
734 | // Source and inflictor are the same for melee attacks. | ||
735 | // Source can be NULL for slime, barrel explosions | ||
736 | // and other environmental stuff. | ||
737 | // | ||
738 | |||
739 | void P_DamageMobj(mobj_t *target,mobj_t *inflictor, mobj_t *source, int damage) | ||
740 | { | ||
741 | player_t *player; | ||
742 | boolean justhit; /* killough 11/98 */ | ||
743 | |||
744 | /* killough 8/31/98: allow bouncers to take damage */ | ||
745 | if (!(target->flags & (MF_SHOOTABLE | MF_BOUNCES))) | ||
746 | return; // shouldn't happen... | ||
747 | |||
748 | if (target->health <= 0) | ||
749 | return; | ||
750 | |||
751 | /* proff 11/22/98: Andy Baker's Stealth monsters */ | ||
752 | if (target->flags & MF_STEALTH) | ||
753 | P_BecomeVisible(target); | ||
754 | |||
755 | if (target->flags & MF_SKULLFLY) | ||
756 | target->momx = target->momy = target->momz = 0; | ||
757 | |||
758 | player = target->player; | ||
759 | if (player && gameskill == sk_baby) | ||
760 | damage >>= 1; // take half damage in trainer mode | ||
761 | |||
762 | // Some close combat weapons should not | ||
763 | // inflict thrust and push the victim out of reach, | ||
764 | // thus kick away unless using the chainsaw. | ||
765 | |||
766 | if (inflictor && !(target->flags & MF_NOCLIP) && | ||
767 | (!source || !source->player || | ||
768 | source->player->readyweapon != wp_chainsaw)) | ||
769 | { | ||
770 | unsigned ang = R_PointToAngle2 (inflictor->x, inflictor->y, | ||
771 | target->x, target->y); | ||
772 | |||
773 | fixed_t thrust = damage*(FRACUNIT>>3)*100/target->info->mass; | ||
774 | |||
775 | // make fall forwards sometimes | ||
776 | if ( damage < 40 && damage > target->health | ||
777 | && target->z - inflictor->z > 64*FRACUNIT | ||
778 | && P_Random(pr_damagemobj) & 1) | ||
779 | { | ||
780 | ang += ANG180; | ||
781 | thrust *= 4; | ||
782 | } | ||
783 | |||
784 | ang >>= ANGLETOFINESHIFT; | ||
785 | target->momx += FixedMul (thrust, finecosine[ang]); | ||
786 | target->momy += FixedMul (thrust, finesine[ang]); | ||
787 | |||
788 | /* killough 11/98: thrust objects hanging off ledges */ | ||
789 | if (target->intflags & MIF_FALLING && target->gear >= MAXGEAR) | ||
790 | target->gear = 0; | ||
791 | } | ||
792 | |||
793 | // player specific | ||
794 | if (player) | ||
795 | { | ||
796 | // end of game hell hack | ||
797 | if (target->subsector->sector->special == 11 && damage >= target->health) | ||
798 | damage = target->health - 1; | ||
799 | |||
800 | // Below certain threshold, | ||
801 | // ignore damage in GOD mode, or with INVUL power. | ||
802 | // killough 3/26/98: make god mode 100% god mode in non-compat mode | ||
803 | |||
804 | if ((damage < 1000 || (!comp[comp_god] && (player->cheats&CF_GODMODE))) && | ||
805 | (player->cheats&CF_GODMODE || player->powers[pw_invulnerability])) | ||
806 | return; | ||
807 | |||
808 | if (player->armortype) | ||
809 | { | ||
810 | int saved = player->armortype == 1 ? damage/3 : damage/2; | ||
811 | if (player->armorpoints <= saved) | ||
812 | { | ||
813 | // armor is used up | ||
814 | saved = player->armorpoints; | ||
815 | player->armortype = 0; | ||
816 | } | ||
817 | player->armorpoints -= saved; | ||
818 | damage -= saved; | ||
819 | } | ||
820 | |||
821 | player->health -= damage; // mirror mobj health here for Dave | ||
822 | if (player->health < 0) | ||
823 | player->health = 0; | ||
824 | |||
825 | player->attacker = source; | ||
826 | player->damagecount += damage; // add damage after armor / invuln | ||
827 | |||
828 | if (player->damagecount > 100) | ||
829 | player->damagecount = 100; // teleport stomp does 10k points... | ||
830 | } | ||
831 | |||
832 | // do the damage | ||
833 | target->health -= damage; | ||
834 | if (target->health <= 0) | ||
835 | { | ||
836 | P_KillMobj (source, target); | ||
837 | return; | ||
838 | } | ||
839 | |||
840 | // killough 9/7/98: keep track of targets so that friends can help friends | ||
841 | if (mbf_features) | ||
842 | { | ||
843 | /* If target is a player, set player's target to source, | ||
844 | * so that a friend can tell who's hurting a player | ||
845 | */ | ||
846 | if (player) | ||
847 | P_SetTarget(&target->target, source); | ||
848 | |||
849 | /* killough 9/8/98: | ||
850 | * If target's health is less than 50%, move it to the front of its list. | ||
851 | * This will slightly increase the chances that enemies will choose to | ||
852 | * "finish it off", but its main purpose is to alert friends of danger. | ||
853 | */ | ||
854 | if (target->health*2 < target->info->spawnhealth) | ||
855 | { | ||
856 | thinker_t *cap = &thinkerclasscap[target->flags & MF_FRIEND ? | ||
857 | th_friends : th_enemies]; | ||
858 | (target->thinker.cprev->cnext = target->thinker.cnext)->cprev = | ||
859 | target->thinker.cprev; | ||
860 | (target->thinker.cnext = cap->cnext)->cprev = &target->thinker; | ||
861 | (target->thinker.cprev = cap)->cnext = &target->thinker; | ||
862 | } | ||
863 | } | ||
864 | |||
865 | if ((justhit = (P_Random (pr_painchance) < target->info->painchance && | ||
866 | !(target->flags & MF_SKULLFLY)))) //killough 11/98: see below | ||
867 | P_SetMobjState(target, target->info->painstate); | ||
868 | |||
869 | target->reactiontime = 0; // we're awake now... | ||
870 | |||
871 | /* killough 9/9/98: cleaned up, made more consistent: */ | ||
872 | |||
873 | if (source && source != target && source->type != MT_VILE && | ||
874 | (!target->threshold || target->type == MT_VILE) && | ||
875 | ((source->flags ^ target->flags) & MF_FRIEND || | ||
876 | monster_infighting || | ||
877 | !mbf_features)) | ||
878 | { | ||
879 | /* if not intent on another player, chase after this one | ||
880 | * | ||
881 | * killough 2/15/98: remember last enemy, to prevent | ||
882 | * sleeping early; 2/21/98: Place priority on players | ||
883 | * killough 9/9/98: cleaned up, made more consistent: | ||
884 | */ | ||
885 | |||
886 | if (!target->lastenemy || target->lastenemy->health <= 0 || | ||
887 | (!mbf_features ? | ||
888 | !target->lastenemy->player : | ||
889 | !((target->flags ^ target->lastenemy->flags) & MF_FRIEND) && | ||
890 | target->target != source)) // remember last enemy - killough | ||
891 | P_SetTarget(&target->lastenemy, target->target); | ||
892 | |||
893 | P_SetTarget(&target->target, source); // killough 11/98 | ||
894 | target->threshold = BASETHRESHOLD; | ||
895 | if (target->state == &states[target->info->spawnstate] | ||
896 | && target->info->seestate != S_NULL) | ||
897 | P_SetMobjState (target, target->info->seestate); | ||
898 | } | ||
899 | |||
900 | /* killough 11/98: Don't attack a friend, unless hit by that friend. */ | ||
901 | if (justhit && (target->target == source || !target->target || | ||
902 | !(target->flags & target->target->flags & MF_FRIEND))) | ||
903 | target->flags |= MF_JUSTHIT; // fight back! | ||
904 | } | ||