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