]> icculus.org git repositories - theoddone33/hheretic.git/blob - base/p_inter.c
Initial revision
[theoddone33/hheretic.git] / base / p_inter.c
1
2 // P_inter.c
3
4 #include "doomdef.h"
5 #include "p_local.h"
6 #include "soundst.h"
7
8 #define BONUSADD 6
9
10 int WeaponValue[] =
11 {
12         1,              // staff
13         3,              // goldwand
14         4,              // crossbow
15         5,              // blaster
16         6,              // skullrod
17         7,              // phoenixrod
18         8,              // mace
19         2,              // gauntlets
20         0               // beak
21 };
22
23 int maxammo[NUMAMMO] =
24 {
25         100,    // gold wand
26         50,             // crossbow
27         200,    // blaster
28         200,    // skull rod
29         20,             // phoenix rod
30         150             // mace
31 };
32
33 static int GetWeaponAmmo[NUMWEAPONS] =
34 {
35         0,              // staff
36         25,             // gold wand
37         10,             // crossbow
38         30,             // blaster
39         50,             // skull rod
40         2,              // phoenix rod
41         50,             // mace
42         0,              // gauntlets
43         0               // beak
44 };
45
46 static weapontype_t GetAmmoChange[] =
47 {
48         wp_goldwand,
49         wp_crossbow,
50         wp_blaster,
51         wp_skullrod,
52         wp_phoenixrod,
53         wp_mace
54 };
55
56 /*
57 static boolean GetAmmoChangePL1[NUMWEAPONS][NUMAMMO] =
58 {
59         // staff
60         {wp_goldwand, wp_crossbow, wp_blaster, wp_skullrod, -1, wp_mace},
61         // gold wand
62         {-1, wp_crossbow, wp_blaster, wp_skullrod, -1, wp_mace},
63         // crossbow
64         {-1, -1, wp_blaster, wp_skullrod, -1, -1},
65         // blaster
66         {-1, -1, -1, -1, -1, -1},
67         // skull rod
68         {-1, -1, -1, -1, -1, -1},
69         // phoenix rod
70         {-1, -1, -1, -1, -1, -1},
71         // mace
72         {-1, wp_crossbow, wp_blaster, wp_skullrod, -1, -1},
73         // gauntlets
74         {-1, wp_crossbow, wp_blaster, wp_skullrod, -1, wp_mace}
75 };
76 */
77
78 /*
79 static boolean GetAmmoChangePL2[NUMWEAPONS][NUMAMMO] =
80 {
81         // staff
82         {wp_goldwand, wp_crossbow, wp_blaster, wp_skullrod, wp_phoenixrod,
83                 wp_mace},
84         // gold wand
85         {-1, wp_crossbow, wp_blaster, wp_skullrod, wp_phoenixrod, wp_mace},
86         // crossbow
87         {-1, -1, wp_blaster, wp_skullrod, wp_phoenixrod, -1},
88         // blaster
89         {-1, -1, -1, wp_skullrod, wp_phoenixrod, -1},
90         // skull rod
91         {-1, -1, -1, -1, -1, -1},
92         // phoenix rod
93         {-1, -1, -1, -1, -1, -1},
94         // mace
95         {-1, wp_crossbow, wp_blaster, wp_skullrod, -1, -1},
96         // gauntlets
97         {-1, -1, -1, wp_skullrod, wp_phoenixrod, wp_mace}
98 };
99 */
100
101 //--------------------------------------------------------------------------
102 //
103 // PROC P_SetMessage
104 //
105 //--------------------------------------------------------------------------
106
107 boolean ultimatemsg;
108
109 void P_SetMessage(player_t *player, char *message, boolean ultmsg)
110 {
111         extern boolean messageson;
112         
113         if((ultimatemsg || !messageson) && !ultmsg)
114         {
115                 return;
116         }
117         player->message = message;
118         player->messageTics = MESSAGETICS;
119         BorderTopRefresh = true;
120         if(ultmsg)
121         {
122                 ultimatemsg = true;
123         }
124 }
125
126 //--------------------------------------------------------------------------
127 //
128 // FUNC P_GiveAmmo
129 //
130 // Returns true if the player accepted the ammo, false if it was
131 // refused (player has maxammo[ammo]).
132 //
133 //--------------------------------------------------------------------------
134
135 boolean P_GiveAmmo(player_t *player, ammotype_t ammo, int count)
136 {
137         int prevAmmo;
138         //weapontype_t changeWeapon;
139
140         if(ammo == am_noammo)
141         {
142                 return(false);
143         }
144         if(ammo < 0 || ammo > NUMAMMO)
145         {
146                 I_Error("P_GiveAmmo: bad type %i", ammo);
147         }
148         if(player->ammo[ammo] == player->maxammo[ammo])
149         {
150                 return(false);
151         }
152         if(gameskill == sk_baby || gameskill == sk_nightmare)
153         { // extra ammo in baby mode and nightmare mode
154                 count += count>>1;
155         }
156         prevAmmo = player->ammo[ammo];
157
158         player->ammo[ammo] += count;
159         if(player->ammo[ammo] > player->maxammo[ammo])
160         {
161                 player->ammo[ammo] = player->maxammo[ammo];
162         }
163         if(prevAmmo)
164         {
165                 // Don't attempt to change weapons if the player already had
166                 // ammo of the type just given
167                 return(true);
168         }
169         if(player->readyweapon == wp_staff
170                 || player->readyweapon == wp_gauntlets)
171         {
172                 if(player->weaponowned[GetAmmoChange[ammo]])
173                 {
174                         player->pendingweapon = GetAmmoChange[ammo];
175                 }
176         }
177 /*
178         if(player->powers[pw_weaponlevel2])
179         {
180                 changeWeapon = GetAmmoChangePL2[player->readyweapon][ammo];
181         }
182         else
183         {
184                 changeWeapon = GetAmmoChangePL1[player->readyweapon][ammo];
185         }
186         if(changeWeapon != -1)
187         {
188                 if(player->weaponowned[changeWeapon])
189                 {
190                         player->pendingweapon = changeWeapon;
191                 }
192         }
193 */
194         return(true);
195 }
196
197 //--------------------------------------------------------------------------
198 //
199 // FUNC P_GiveWeapon
200 //
201 // Returns true if the weapon or its ammo was accepted.
202 //
203 //--------------------------------------------------------------------------
204
205 boolean P_GiveWeapon(player_t *player, weapontype_t weapon)
206 {
207         boolean gaveAmmo;
208         boolean gaveWeapon;
209
210         if(netgame && !deathmatch)
211         { // Cooperative net-game
212                 if(player->weaponowned[weapon])
213                 {
214                         return(false);
215                 }
216                 player->bonuscount += BONUSADD;
217                 player->weaponowned[weapon] = true;
218                 P_GiveAmmo(player, wpnlev1info[weapon].ammo,
219                         GetWeaponAmmo[weapon]);
220                 player->pendingweapon = weapon;
221                 if(player == &players[consoleplayer])
222                 {
223                         S_StartSound(NULL, sfx_wpnup);
224                 }
225                 return(false);
226         }
227         gaveAmmo = P_GiveAmmo(player, wpnlev1info[weapon].ammo,
228                 GetWeaponAmmo[weapon]);
229         if(player->weaponowned[weapon])
230         {
231                 gaveWeapon = false;
232         }
233         else
234         {
235                 gaveWeapon = true;
236                 player->weaponowned[weapon] = true;
237                 if(WeaponValue[weapon] > WeaponValue[player->readyweapon])
238                 { // Only switch to more powerful weapons
239                         player->pendingweapon = weapon;
240                 }
241         }
242         return(gaveWeapon || gaveAmmo);
243 }
244
245 //---------------------------------------------------------------------------
246 //
247 // FUNC P_GiveBody
248 //
249 // Returns false if the body isn't needed at all.
250 //
251 //---------------------------------------------------------------------------
252
253 boolean P_GiveBody(player_t *player, int num)
254 {
255         int max;
256
257         max = MAXHEALTH;
258         if(player->chickenTics)
259         {
260                 max = MAXCHICKENHEALTH;
261         }
262         if(player->health >= max)
263         {
264                 return(false);
265         }
266         player->health += num;
267         if(player->health > max)
268         {
269                 player->health = max;
270         }
271         player->mo->health = player->health;
272         return(true);
273 }
274
275 //---------------------------------------------------------------------------
276 //
277 // FUNC P_GiveArmor
278 //
279 // Returns false if the armor is worse than the current armor.
280 //
281 //---------------------------------------------------------------------------
282
283 boolean P_GiveArmor(player_t *player, int armortype)
284 {
285         int hits;
286
287         hits = armortype*100;
288         if(player->armorpoints >= hits)
289         {
290                 return(false);
291         }
292         player->armortype = armortype;
293         player->armorpoints = hits;
294         return(true);
295 }
296
297 //---------------------------------------------------------------------------
298 //
299 // PROC P_GiveKey
300 //
301 //---------------------------------------------------------------------------
302
303 void P_GiveKey(player_t *player, keytype_t key)
304 {
305         extern int playerkeys;
306         extern vertex_t KeyPoints[];
307         
308         if(player->keys[key])
309         {
310                 return;
311         }
312         if(player == &players[consoleplayer])
313         {
314                 playerkeys |= 1<<key;
315                 KeyPoints[key].x = 0;
316                 KeyPoints[key].y = 0;
317         }
318         player->bonuscount = BONUSADD;
319         player->keys[key] = true;
320 }
321
322 //---------------------------------------------------------------------------
323 //
324 // FUNC P_GivePower
325 //
326 // Returns true if power accepted.
327 //
328 //---------------------------------------------------------------------------
329
330 boolean P_GivePower(player_t *player, powertype_t power)
331 {
332         if(power == pw_invulnerability)
333         {
334                 if(player->powers[power] > BLINKTHRESHOLD)
335                 { // Already have it
336                         return(false);
337                 }
338                 player->powers[power] = INVULNTICS;
339                 return(true);
340         }
341         if(power == pw_weaponlevel2)
342         {
343                 if(player->powers[power] > BLINKTHRESHOLD)
344                 { // Already have it
345                         return(false);
346                 }
347                 player->powers[power] = WPNLEV2TICS;
348                 return(true);
349         }
350         if(power == pw_invisibility)
351         {
352                 if(player->powers[power] > BLINKTHRESHOLD)
353                 { // Already have it
354                         return(false);
355                 }
356                 player->powers[power] = INVISTICS;
357                 player->mo->flags |= MF_SHADOW;
358                 return(true);
359         }
360         if(power == pw_flight)
361         {
362                 if(player->powers[power] > BLINKTHRESHOLD)
363                 { // Already have it
364                         return(false);
365                 }
366                 player->powers[power] = FLIGHTTICS;
367                 player->mo->flags2 |= MF2_FLY;
368                 player->mo->flags |= MF_NOGRAVITY;
369                 if(player->mo->z <= player->mo->floorz)
370                 {
371                         player->flyheight = 10; // thrust the player in the air a bit
372                 }
373                 return(true);
374         }
375         if(power == pw_infrared)
376         {
377                 if(player->powers[power] > BLINKTHRESHOLD)
378                 { // Already have it
379                         return(false);
380                 }
381                 player->powers[power] = INFRATICS;
382                 return(true);
383         }
384 /*
385         if(power == pw_ironfeet)
386         {
387                 player->powers[power] = IRONTICS;
388                 return(true);
389         }
390         if(power == pw_strength)
391         {
392                 P_GiveBody(player, 100);
393                 player->powers[power] = 1;
394                 return(true);
395         }
396 */
397         if(player->powers[power])
398         {
399                 return(false); // already got it
400         }
401         player->powers[power] = 1;
402         return(true);
403 }
404
405 //---------------------------------------------------------------------------
406 //
407 // FUNC P_GiveArtifact
408 //
409 // Returns true if artifact accepted.
410 //
411 //---------------------------------------------------------------------------
412
413 boolean P_GiveArtifact(player_t *player, artitype_t arti, mobj_t *mo)
414 {
415         int i;
416
417         i = 0;
418         while(player->inventory[i].type != arti && i < player->inventorySlotNum)
419         {
420                 i++;
421         }
422         if(i == player->inventorySlotNum)
423         {
424                 player->inventory[i].count = 1;
425                 player->inventory[i].type = arti;
426                 player->inventorySlotNum++;
427         }
428         else
429         {
430                 if(player->inventory[i].count >= 16)
431                 { // Player already has 16 of this item
432                         return(false);
433                 }
434                 player->inventory[i].count++;
435         }
436         if(player->artifactCount == 0)
437         {
438                 player->readyArtifact = arti;
439         }
440         player->artifactCount++;
441         if(mo && (mo->flags&MF_COUNTITEM))
442         {
443                 player->itemcount++;
444         }
445         return(true);
446 }
447
448 //---------------------------------------------------------------------------
449 //
450 // PROC P_SetDormantArtifact
451 //
452 // Removes the MF_SPECIAL flag, and initiates the artifact pickup
453 // animation.
454 //
455 //---------------------------------------------------------------------------
456
457 void P_SetDormantArtifact(mobj_t *arti)
458 {
459         arti->flags &= ~MF_SPECIAL;
460         if(deathmatch && (arti->type != MT_ARTIINVULNERABILITY)
461                 && (arti->type != MT_ARTIINVISIBILITY))
462         {
463                 P_SetMobjState(arti, S_DORMANTARTI1);
464         }
465         else
466         { // Don't respawn
467                 P_SetMobjState(arti, S_DEADARTI1);
468         }
469         S_StartSound(arti, sfx_artiup);
470 }
471
472 //---------------------------------------------------------------------------
473 //
474 // PROC A_RestoreArtifact
475 //
476 //---------------------------------------------------------------------------
477
478 void A_RestoreArtifact(mobj_t *arti)
479 {
480         arti->flags |= MF_SPECIAL;
481         P_SetMobjState(arti, arti->info->spawnstate);
482         S_StartSound(arti, sfx_respawn);
483 }
484
485 //----------------------------------------------------------------------------
486 //
487 // PROC P_HideSpecialThing
488 //
489 //----------------------------------------------------------------------------
490
491 void P_HideSpecialThing(mobj_t *thing)
492 {
493         thing->flags &= ~MF_SPECIAL;
494         thing->flags2 |= MF2_DONTDRAW;
495         P_SetMobjState(thing, S_HIDESPECIAL1);
496 }
497
498 //---------------------------------------------------------------------------
499 //
500 // PROC A_RestoreSpecialThing1
501 //
502 // Make a special thing visible again.
503 //
504 //---------------------------------------------------------------------------
505
506 void A_RestoreSpecialThing1(mobj_t *thing)
507 {
508         if(thing->type == MT_WMACE)
509         { // Do random mace placement
510                 P_RepositionMace(thing);
511         }
512         thing->flags2 &= ~MF2_DONTDRAW;
513         S_StartSound(thing, sfx_respawn);
514 }
515
516 //---------------------------------------------------------------------------
517 //
518 // PROC A_RestoreSpecialThing2
519 //
520 //---------------------------------------------------------------------------
521
522 void A_RestoreSpecialThing2(mobj_t *thing)
523 {
524         thing->flags |= MF_SPECIAL;
525         P_SetMobjState(thing, thing->info->spawnstate);
526 }
527
528 //---------------------------------------------------------------------------
529 //
530 // PROC P_TouchSpecialThing
531 //
532 //---------------------------------------------------------------------------
533
534 void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher)
535 {
536         int i;
537         player_t *player;
538         fixed_t delta;
539         int sound;
540         boolean respawn;
541
542         delta = special->z-toucher->z;
543         if(delta > toucher->height || delta < -32*FRACUNIT)
544         { // Out of reach
545                 return;
546         }
547         if(toucher->health <= 0)
548         { // Toucher is dead
549                 return;
550         }
551         sound = sfx_itemup;
552         player = toucher->player;
553         respawn = true;
554         switch(special->sprite)
555         {
556                 // Items
557                 case SPR_PTN1: // Item_HealingPotion
558                         if(!P_GiveBody(player, 10))
559                         {
560                                 return;
561                         }
562                         P_SetMessage(player, TXT_ITEMHEALTH, false);
563                         break;
564                 case SPR_SHLD: // Item_Shield1
565                         if(!P_GiveArmor(player, 1))
566                         {
567                                 return;
568                         }
569                         P_SetMessage(player, TXT_ITEMSHIELD1, false);
570                         break;
571                 case SPR_SHD2: // Item_Shield2
572                         if(!P_GiveArmor(player, 2))
573                         {
574                                 return;
575                         }
576                         P_SetMessage(player, TXT_ITEMSHIELD2, false);
577                         break;
578                 case SPR_BAGH: // Item_BagOfHolding
579                         if(!player->backpack)
580                         {
581                                 for(i = 0; i < NUMAMMO; i++)
582                                 {
583                                         player->maxammo[i] *= 2;
584                                 }
585                                 player->backpack = true;
586                         }
587                         P_GiveAmmo(player, am_goldwand, AMMO_GWND_WIMPY);
588                         P_GiveAmmo(player, am_blaster, AMMO_BLSR_WIMPY);
589                         P_GiveAmmo(player, am_crossbow, AMMO_CBOW_WIMPY);
590                         P_GiveAmmo(player, am_skullrod, AMMO_SKRD_WIMPY);
591                         P_GiveAmmo(player, am_phoenixrod, AMMO_PHRD_WIMPY);
592                         P_SetMessage(player, TXT_ITEMBAGOFHOLDING, false);
593                         break;
594                 case SPR_SPMP: // Item_SuperMap
595                         if(!P_GivePower(player, pw_allmap))
596                         {
597                                 return;
598                         }
599                         P_SetMessage(player, TXT_ITEMSUPERMAP, false);
600                         break;
601
602                 // Keys
603                 case SPR_BKYY: // Key_Blue
604                         if(!player->keys[key_blue])
605                         {
606                                 P_SetMessage(player, TXT_GOTBLUEKEY, false);
607                         }
608                         P_GiveKey(player, key_blue);
609                         sound = sfx_keyup;
610                         if(!netgame)
611                         {
612                                 break;
613                         }
614                         return;
615                 case SPR_CKYY: // Key_Yellow
616                         if(!player->keys[key_yellow])
617                         {
618                                 P_SetMessage(player, TXT_GOTYELLOWKEY, false);
619                         }
620                         sound = sfx_keyup;
621                         P_GiveKey(player, key_yellow);
622                         if(!netgame)
623                         {
624                                 break;
625                         }
626                         return;
627                 case SPR_AKYY: // Key_Green
628                         if(!player->keys[key_green])
629                         {
630                                 P_SetMessage(player, TXT_GOTGREENKEY, false);
631                         }
632                         sound = sfx_keyup;
633                         P_GiveKey(player, key_green);
634                         if(!netgame)
635                         {
636                                 break;
637                         }
638                         return;
639
640                 // Artifacts
641                 case SPR_PTN2: // Arti_HealingPotion
642                         if(P_GiveArtifact(player, arti_health, special))
643                         {
644                                 P_SetMessage(player, TXT_ARTIHEALTH, false);
645                                 P_SetDormantArtifact(special);
646                         }
647                         return;
648                 case SPR_SOAR: // Arti_Fly
649                         if(P_GiveArtifact(player, arti_fly, special))
650                         {
651                                 P_SetMessage(player, TXT_ARTIFLY, false);
652                                 P_SetDormantArtifact(special);
653                         }
654                         return;
655                 case SPR_INVU: // Arti_Invulnerability
656                         if(P_GiveArtifact(player, arti_invulnerability, special))
657                         {
658                                 P_SetMessage(player, TXT_ARTIINVULNERABILITY, false);
659                                 P_SetDormantArtifact(special);
660                         }
661                         return;
662                 case SPR_PWBK: // Arti_TomeOfPower
663                         if(P_GiveArtifact(player, arti_tomeofpower, special))
664                         {
665                                 P_SetMessage(player, TXT_ARTITOMEOFPOWER, false);
666                                 P_SetDormantArtifact(special);
667                         }
668                         return;
669                 case SPR_INVS: // Arti_Invisibility
670                         if(P_GiveArtifact(player, arti_invisibility, special))
671                         {
672                                 P_SetMessage(player, TXT_ARTIINVISIBILITY, false);
673                                 P_SetDormantArtifact(special);
674                         }
675                         return;
676                 case SPR_EGGC: // Arti_Egg
677                         if(P_GiveArtifact(player, arti_egg, special))
678                         {
679                                 P_SetMessage(player, TXT_ARTIEGG, false);
680                                 P_SetDormantArtifact(special);
681                         }
682                         return;
683                 case SPR_SPHL: // Arti_SuperHealth
684                         if(P_GiveArtifact(player, arti_superhealth, special))
685                         {
686                                 P_SetMessage(player, TXT_ARTISUPERHEALTH, false);
687                                 P_SetDormantArtifact(special);
688                         }
689                         return;
690                 case SPR_TRCH: // Arti_Torch
691                         if(P_GiveArtifact(player, arti_torch, special))
692                         {
693                                 P_SetMessage(player, TXT_ARTITORCH, false);
694                                 P_SetDormantArtifact(special);
695                         }
696                         return;
697                 case SPR_FBMB: // Arti_FireBomb
698                         if(P_GiveArtifact(player, arti_firebomb, special))
699                         {
700                                 P_SetMessage(player, TXT_ARTIFIREBOMB, false);
701                                 P_SetDormantArtifact(special);
702                         }
703                         return;
704                 case SPR_ATLP: // Arti_Teleport
705                         if(P_GiveArtifact(player, arti_teleport, special))
706                         {
707                                 P_SetMessage(player, TXT_ARTITELEPORT, false);
708                                 P_SetDormantArtifact(special);
709                         }
710                         return;
711
712                 // Ammo
713                 case SPR_AMG1: // Ammo_GoldWandWimpy
714                         if(!P_GiveAmmo(player, am_goldwand, special->health))
715                         {
716                                 return;
717                         }
718                         P_SetMessage(player, TXT_AMMOGOLDWAND1, false);
719                         break;
720                 case SPR_AMG2: // Ammo_GoldWandHefty
721                         if(!P_GiveAmmo(player, am_goldwand, special->health))
722                         {
723                                 return;
724                         }
725                         P_SetMessage(player, TXT_AMMOGOLDWAND2, false);
726                         break;
727                 case SPR_AMM1: // Ammo_MaceWimpy
728                         if(!P_GiveAmmo(player, am_mace, special->health))
729                         {
730                                 return;
731                         }
732                         P_SetMessage(player, TXT_AMMOMACE1, false);
733                         break;
734                 case SPR_AMM2: // Ammo_MaceHefty
735                         if(!P_GiveAmmo(player, am_mace, special->health))
736                         {
737                                 return;
738                         }
739                         P_SetMessage(player, TXT_AMMOMACE2, false);
740                         break;
741                 case SPR_AMC1: // Ammo_CrossbowWimpy
742                         if(!P_GiveAmmo(player, am_crossbow, special->health))
743                         {
744                                 return;
745                         }
746                         P_SetMessage(player, TXT_AMMOCROSSBOW1, false);
747                         break;
748                 case SPR_AMC2: // Ammo_CrossbowHefty
749                         if(!P_GiveAmmo(player, am_crossbow, special->health))
750                         {
751                                 return;
752                         }
753                         P_SetMessage(player, TXT_AMMOCROSSBOW2, false);
754                         break;
755                 case SPR_AMB1: // Ammo_BlasterWimpy
756                         if(!P_GiveAmmo(player, am_blaster, special->health))
757                         {
758                                 return;
759                         }
760                         P_SetMessage(player, TXT_AMMOBLASTER1, false);
761                         break;
762                 case SPR_AMB2: // Ammo_BlasterHefty
763                         if(!P_GiveAmmo(player, am_blaster, special->health))
764                         {
765                                 return;
766                         }
767                         P_SetMessage(player, TXT_AMMOBLASTER2, false);
768                         break;
769                 case SPR_AMS1: // Ammo_SkullRodWimpy
770                         if(!P_GiveAmmo(player, am_skullrod, special->health))
771                         {
772                                 return;
773                         }
774                         P_SetMessage(player, TXT_AMMOSKULLROD1, false);
775                         break;
776                 case SPR_AMS2: // Ammo_SkullRodHefty
777                         if(!P_GiveAmmo(player, am_skullrod, special->health))
778                         {
779                                 return;
780                         }
781                         P_SetMessage(player, TXT_AMMOSKULLROD2, false);
782                         break;
783                 case SPR_AMP1: // Ammo_PhoenixRodWimpy
784                         if(!P_GiveAmmo(player, am_phoenixrod, special->health))
785                         {
786                                 return;
787                         }
788                         P_SetMessage(player, TXT_AMMOPHOENIXROD1, false);
789                         break;
790                 case SPR_AMP2: // Ammo_PhoenixRodHefty
791                         if(!P_GiveAmmo(player, am_phoenixrod, special->health))
792                         {
793                                 return;
794                         }
795                         P_SetMessage(player, TXT_AMMOPHOENIXROD2, false);
796                         break;
797
798                 // Weapons
799                 case SPR_WMCE: // Weapon_Mace
800                         if(!P_GiveWeapon(player, wp_mace))
801                         {
802                                 return;
803                         }
804                         P_SetMessage(player, TXT_WPNMACE, false);
805                         sound = sfx_wpnup;
806                         break;
807                 case SPR_WBOW: // Weapon_Crossbow
808                         if(!P_GiveWeapon(player, wp_crossbow))
809                         {
810                                 return;
811                         }
812                         P_SetMessage(player, TXT_WPNCROSSBOW, false);
813                         sound = sfx_wpnup;
814                         break;
815                 case SPR_WBLS: // Weapon_Blaster
816                         if(!P_GiveWeapon(player, wp_blaster))
817                         {
818                                 return;
819                         }
820                         P_SetMessage(player, TXT_WPNBLASTER, false);
821                         sound = sfx_wpnup;
822                         break;
823                 case SPR_WSKL: // Weapon_SkullRod
824                         if(!P_GiveWeapon(player, wp_skullrod))
825                         {
826                                 return;
827                         }
828                         P_SetMessage(player, TXT_WPNSKULLROD, false);
829                         sound = sfx_wpnup;
830                         break;
831                 case SPR_WPHX: // Weapon_PhoenixRod
832                         if(!P_GiveWeapon(player, wp_phoenixrod))
833                         {
834                                 return;
835                         }
836                         P_SetMessage(player, TXT_WPNPHOENIXROD, false);
837                         sound = sfx_wpnup;
838                         break;
839                 case SPR_WGNT: // Weapon_Gauntlets
840                         if(!P_GiveWeapon(player, wp_gauntlets))
841                         {
842                                 return;
843                         }
844                         P_SetMessage(player, TXT_WPNGAUNTLETS, false);
845                         sound = sfx_wpnup;
846                         break;
847                 default:
848                         I_Error("P_SpecialThing: Unknown gettable thing");
849         }
850         if(special->flags&MF_COUNTITEM)
851         {
852                 player->itemcount++;
853         }
854         if(deathmatch && respawn && !(special->flags&MF_DROPPED))
855         {
856                 P_HideSpecialThing(special);
857         }
858         else
859         {
860                 P_RemoveMobj(special);
861         }
862         player->bonuscount += BONUSADD;
863         if(player == &players[consoleplayer])
864         {
865                 S_StartSound(NULL, sound);
866                 SB_PaletteFlash();
867         }
868 }
869
870 //---------------------------------------------------------------------------
871 //
872 // PROC P_KillMobj
873 //
874 //---------------------------------------------------------------------------
875
876 void P_KillMobj(mobj_t *source, mobj_t *target)
877 {
878         target->flags &= ~(MF_SHOOTABLE|MF_FLOAT|MF_SKULLFLY|MF_NOGRAVITY);
879         target->flags |= MF_CORPSE|MF_DROPOFF;
880         target->flags2 &= ~MF2_PASSMOBJ;
881         target->height >>= 2;
882         if(source && source->player)
883         {
884                 if(target->flags&MF_COUNTKILL)
885                 { // Count for intermission
886                         source->player->killcount++;
887                 }
888                 if(target->player)
889                 { // Frag stuff
890                         if(target == source)
891                         { // Self-frag
892                                 target->player->frags[target->player-players]--;
893                         }
894                         else
895                         {
896                                 source->player->frags[target->player-players]++;
897                                 if(source->player == &players[consoleplayer])
898                                 {
899                                         S_StartSound(NULL, sfx_gfrag);
900                                 }
901                                 if(source->player->chickenTics)
902                                 { // Make a super chicken
903                                         P_GivePower(source->player, pw_weaponlevel2);
904                                 }
905                         }
906                 }
907         }
908         else if(!netgame && (target->flags&MF_COUNTKILL))
909         { // Count all monster deaths
910                 players[0].killcount++;
911         }
912         if(target->player)
913         {
914                 if(!source)
915                 { // Self-frag
916                         target->player->frags[target->player-players]--;
917                 }
918                 target->flags &= ~MF_SOLID;
919                 target->flags2 &= ~MF2_FLY;
920                 target->player->powers[pw_flight] = 0;
921                 target->player->powers[pw_weaponlevel2] = 0;
922                 target->player->playerstate = PST_DEAD;
923                 P_DropWeapon(target->player);
924                 if(target->flags2&MF2_FIREDAMAGE)
925                 { // Player flame death
926                         P_SetMobjState(target, S_PLAY_FDTH1);
927                         //S_StartSound(target, sfx_hedat1); // Burn sound
928                         return;
929                 }
930         }
931         if(target->health < -(target->info->spawnhealth>>1)
932                 && target->info->xdeathstate)
933         { // Extreme death
934                 P_SetMobjState(target, target->info->xdeathstate);
935         }
936         else
937         { // Normal death
938                 P_SetMobjState(target, target->info->deathstate);
939         }
940         target->tics -= P_Random()&3;
941 //      I_StartSound(&actor->r, actor->info->deathsound);
942 }
943
944 //---------------------------------------------------------------------------
945 //
946 // FUNC P_MinotaurSlam
947 //
948 //---------------------------------------------------------------------------
949
950 void P_MinotaurSlam(mobj_t *source, mobj_t *target)
951 {
952         angle_t angle;
953         fixed_t thrust;
954
955         angle = R_PointToAngle2(source->x, source->y, target->x, target->y);
956         angle >>= ANGLETOFINESHIFT;
957         thrust = 16*FRACUNIT+(P_Random()<<10);
958         target->momx += FixedMul(thrust, finecosine[angle]);
959         target->momy += FixedMul(thrust, finesine[angle]);
960         P_DamageMobj(target, NULL, NULL, HITDICE(6));
961         if(target->player)
962         {
963                 target->reactiontime = 14+(P_Random()&7);
964         }
965 }
966
967 //---------------------------------------------------------------------------
968 //
969 // FUNC P_TouchWhirlwind
970 //
971 //---------------------------------------------------------------------------
972
973 void P_TouchWhirlwind(mobj_t *target)
974 {
975         int randVal;
976
977         target->angle += (P_Random()-P_Random())<<20;
978         target->momx += (P_Random()-P_Random())<<10;
979         target->momy += (P_Random()-P_Random())<<10;
980         if(leveltime&16 && !(target->flags2&MF2_BOSS))
981         {
982                 randVal = P_Random();
983                 if(randVal > 160)
984                 {
985                         randVal = 160;
986                 }
987                 target->momz += randVal<<10;
988                 if(target->momz > 12*FRACUNIT)
989                 {
990                         target->momz = 12*FRACUNIT;
991                 }
992         }
993         if(!(leveltime&7))
994         {
995                 P_DamageMobj(target, NULL, NULL, 3);
996         }
997 }
998
999 //---------------------------------------------------------------------------
1000 //
1001 // FUNC P_ChickenMorphPlayer
1002 //
1003 // Returns true if the player gets turned into a chicken.
1004 //
1005 //---------------------------------------------------------------------------
1006
1007 boolean P_ChickenMorphPlayer(player_t *player)
1008 {
1009         mobj_t *pmo;
1010         mobj_t *fog;
1011         mobj_t *chicken;
1012         fixed_t x;
1013         fixed_t y;
1014         fixed_t z;
1015         angle_t angle;
1016         int oldFlags2;
1017
1018         if(player->chickenTics)
1019         {
1020                 if((player->chickenTics < CHICKENTICS-TICSPERSEC)
1021                         && !player->powers[pw_weaponlevel2])
1022                 { // Make a super chicken
1023                         P_GivePower(player, pw_weaponlevel2);
1024                 }
1025                 return(false);
1026         }
1027         if(player->powers[pw_invulnerability])
1028         { // Immune when invulnerable
1029                 return(false);
1030         }
1031         pmo = player->mo;
1032         x = pmo->x;
1033         y = pmo->y;
1034         z = pmo->z;
1035         angle = pmo->angle;
1036         oldFlags2 = pmo->flags2;
1037         P_SetMobjState(pmo, S_FREETARGMOBJ);
1038         fog = P_SpawnMobj(x, y, z+TELEFOGHEIGHT, MT_TFOG);
1039         S_StartSound(fog, sfx_telept);
1040         chicken = P_SpawnMobj(x, y, z, MT_CHICPLAYER);
1041         chicken->special1 = player->readyweapon;
1042         chicken->angle = angle;
1043         chicken->player = player;
1044         player->health = chicken->health = MAXCHICKENHEALTH;
1045         player->mo = chicken;
1046         player->armorpoints = player->armortype = 0;
1047         player->powers[pw_invisibility] = 0;
1048         player->powers[pw_weaponlevel2] = 0;
1049         if(oldFlags2&MF2_FLY)
1050         {
1051                 chicken->flags2 |= MF2_FLY;
1052         }
1053         player->chickenTics = CHICKENTICS;
1054         P_ActivateBeak(player);
1055         return(true);
1056 }
1057
1058 //---------------------------------------------------------------------------
1059 //
1060 // FUNC P_ChickenMorph
1061 //
1062 //---------------------------------------------------------------------------
1063
1064 boolean P_ChickenMorph(mobj_t *actor)
1065 {
1066         mobj_t *fog;
1067         mobj_t *chicken;
1068         mobj_t *target;
1069         mobjtype_t moType;
1070         fixed_t x;
1071         fixed_t y;
1072         fixed_t z;
1073         angle_t angle;
1074         int ghost;
1075
1076         if(actor->player)
1077         {
1078                 return(false);
1079         }
1080         moType = actor->type;
1081         switch(moType)
1082         {
1083                 case MT_POD:
1084                 case MT_CHICKEN:
1085                 case MT_HEAD:
1086                 case MT_MINOTAUR:
1087                 case MT_SORCERER1:
1088                 case MT_SORCERER2:
1089                         return(false);
1090                 default:
1091                         break;
1092         }
1093         x = actor->x;
1094         y = actor->y;
1095         z = actor->z;
1096         angle = actor->angle;
1097         ghost = actor->flags&MF_SHADOW;
1098         target = actor->target;
1099         P_SetMobjState(actor, S_FREETARGMOBJ);
1100         fog = P_SpawnMobj(x, y, z+TELEFOGHEIGHT, MT_TFOG);
1101         S_StartSound(fog, sfx_telept);
1102         chicken = P_SpawnMobj(x, y, z, MT_CHICKEN);
1103         chicken->special2 = moType;
1104         chicken->special1 = CHICKENTICS+P_Random();
1105         chicken->flags |= ghost;
1106         chicken->target = target;
1107         chicken->angle = angle;
1108         return(true);
1109 }
1110
1111 //---------------------------------------------------------------------------
1112 //
1113 // FUNC P_AutoUseChaosDevice
1114 //
1115 //---------------------------------------------------------------------------
1116
1117 boolean P_AutoUseChaosDevice(player_t *player)
1118 {
1119         int i;
1120
1121         for(i = 0; i < player->inventorySlotNum; i++)
1122         {
1123                 if(player->inventory[i].type == arti_teleport)
1124                 {
1125                         P_PlayerUseArtifact(player, arti_teleport);
1126                         player->health = player->mo->health = (player->health+1)/2;
1127                         return(true);
1128                 }
1129         }
1130         return(false);
1131 }
1132
1133 //---------------------------------------------------------------------------
1134 //
1135 // PROC P_AutoUseHealth
1136 //
1137 //---------------------------------------------------------------------------
1138
1139 void P_AutoUseHealth(player_t *player, int saveHealth)
1140 {
1141         int i;
1142         int count;
1143         int normalCount;
1144         int normalSlot=0;
1145         int superCount;
1146         int superSlot=0;
1147
1148         normalCount = superCount = 0;
1149         for(i = 0; i < player->inventorySlotNum; i++)
1150         {
1151                 if(player->inventory[i].type == arti_health)
1152                 {
1153                         normalSlot = i;
1154                         normalCount = player->inventory[i].count;
1155                 }
1156                 else if(player->inventory[i].type == arti_superhealth)
1157                 {
1158                         superSlot = i;
1159                         superCount = player->inventory[i].count;
1160                 }
1161         }
1162         if((gameskill == sk_baby) && (normalCount*25 >= saveHealth))
1163         { // Use quartz flasks
1164                 count = (saveHealth+24)/25;
1165                 for(i = 0; i < count; i++)
1166                 {
1167                         player->health += 25;
1168                         P_PlayerRemoveArtifact(player, normalSlot);
1169                 }
1170         }
1171         else if(superCount*100 >= saveHealth)
1172         { // Use mystic urns
1173                 count = (saveHealth+99)/100;
1174                 for(i = 0; i < count; i++)
1175                 {
1176                         player->health += 100;
1177                         P_PlayerRemoveArtifact(player, superSlot);
1178                 }
1179         }
1180         else if((gameskill == sk_baby)
1181                 && (superCount*100+normalCount*25 >= saveHealth))
1182         { // Use mystic urns and quartz flasks
1183                 count = (saveHealth+24)/25;
1184                 saveHealth -= count*25;
1185                 for(i = 0; i < count; i++)
1186                 {
1187                         player->health += 25;
1188                         P_PlayerRemoveArtifact(player, normalSlot);
1189                 }
1190                 count = (saveHealth+99)/100;
1191                 for(i = 0; i < count; i++)
1192                 {
1193                         player->health += 100;
1194                         P_PlayerRemoveArtifact(player, normalSlot);
1195                 }
1196         }
1197         player->mo->health = player->health;
1198 }
1199
1200 /*
1201 =================
1202 =
1203 = P_DamageMobj
1204 =
1205 = Damages both enemies and players
1206 = inflictor is the thing that caused the damage
1207 =               creature or missile, can be NULL (slime, etc)
1208 = source is the thing to target after taking damage
1209 =               creature or NULL
1210 = Source and inflictor are the same for melee attacks
1211 = source can be null for barrel explosions and other environmental stuff
1212 ==================
1213 */
1214
1215 void P_DamageMobj
1216 (
1217         mobj_t *target,
1218         mobj_t *inflictor,
1219         mobj_t *source,
1220         int     damage
1221 )
1222 {
1223         unsigned ang;
1224         int saved;
1225         player_t *player;
1226         fixed_t thrust;
1227         int temp;
1228
1229         if(!(target->flags&MF_SHOOTABLE))
1230         {
1231                 // Shouldn't happen
1232                 return;
1233         }
1234         if(target->health <= 0)
1235         {
1236                 return;
1237         }
1238         if(target->flags&MF_SKULLFLY)
1239         {
1240                 if(target->type == MT_MINOTAUR)
1241                 { // Minotaur is invulnerable during charge attack
1242                         return;
1243                 }
1244                 target->momx = target->momy = target->momz = 0;
1245         }
1246         player = target->player;
1247         if(player && gameskill == sk_baby)
1248         {
1249                 // Take half damage in trainer mode
1250                 damage >>= 1;
1251         }
1252         // Special damage types
1253         if(inflictor)
1254         {
1255                 switch(inflictor->type)
1256                 {
1257                         case MT_EGGFX:
1258                                 if(player)
1259                                 {
1260                                         P_ChickenMorphPlayer(player);
1261                                 }
1262                                 else
1263                                 {
1264                                         P_ChickenMorph(target);
1265                                 }
1266                                 return; // Always return
1267                         case MT_WHIRLWIND:
1268                                 P_TouchWhirlwind(target);
1269                                 return;
1270                         case MT_MINOTAUR:
1271                                 if(inflictor->flags&MF_SKULLFLY)
1272                                 { // Slam only when in charge mode
1273                                         P_MinotaurSlam(inflictor, target);
1274                                         return;
1275                                 }
1276                                 break;
1277                         case MT_MACEFX4: // Death ball
1278                                 if((target->flags2&MF2_BOSS) || target->type == MT_HEAD)
1279                                 { // Don't allow cheap boss kills
1280                                         break;
1281                                 }
1282                                 else if(target->player)
1283                                 { // Player specific checks
1284                                         if(target->player->powers[pw_invulnerability])
1285                                         { // Can't hurt invulnerable players
1286                                                 break;
1287                                         }
1288                                         if(P_AutoUseChaosDevice(target->player))
1289                                         { // Player was saved using chaos device
1290                                                 return;
1291                                         }
1292                                 }
1293                                 damage = 10000; // Something's gonna die
1294                                 break;
1295                         case MT_PHOENIXFX2: // Flame thrower
1296                                 if(target->player && P_Random() < 128)
1297                                 { // Freeze player for a bit
1298                                         target->reactiontime += 4;
1299                                 }
1300                                 break;
1301                         case MT_RAINPLR1: // Rain missiles
1302                         case MT_RAINPLR2:
1303                         case MT_RAINPLR3:
1304                         case MT_RAINPLR4:
1305                                 if(target->flags2&MF2_BOSS)
1306                                 { // Decrease damage for bosses
1307                                         damage = (P_Random()&7)+1;
1308                                 }
1309                                 break;
1310                         case MT_HORNRODFX2:
1311                         case MT_PHOENIXFX1:
1312                                 if(target->type == MT_SORCERER2 && P_Random() < 96)
1313                                 { // D'Sparil teleports away
1314                                         P_DSparilTeleport(target);
1315                                         return;
1316                                 }
1317                                 break;
1318                         case MT_BLASTERFX1:
1319                         case MT_RIPPER:
1320                                 if(target->type == MT_HEAD)
1321                                 { // Less damage to Ironlich bosses
1322                                         damage = P_Random()&1;
1323                                         if(!damage)
1324                                         {
1325                                                 return;
1326                                         }
1327                                 }
1328                                 break;
1329                         default:
1330                                 break;
1331                 }
1332         }
1333         // Push the target unless source is using the gauntlets
1334         if(inflictor && (!source || !source->player
1335                 || source->player->readyweapon != wp_gauntlets)
1336                 && !(inflictor->flags2&MF2_NODMGTHRUST))
1337         {
1338                 ang = R_PointToAngle2(inflictor->x, inflictor->y,
1339                         target->x, target->y);
1340                 //thrust = damage*(FRACUNIT>>3)*100/target->info->mass;
1341                 thrust = damage*(FRACUNIT>>3)*150/target->info->mass;
1342                 // make fall forwards sometimes
1343                 if((damage < 40) && (damage > target->health)
1344                         && (target->z-inflictor->z > 64*FRACUNIT) && (P_Random()&1))
1345                 {
1346                         ang += ANG180;
1347                         thrust *= 4;
1348                 }
1349                 ang >>= ANGLETOFINESHIFT;
1350                 if(source && source->player && (source == inflictor)
1351                         && source->player->powers[pw_weaponlevel2]
1352                         && source->player->readyweapon == wp_staff)
1353                 {
1354                         // Staff power level 2
1355                         target->momx += FixedMul(10*FRACUNIT, finecosine[ang]);
1356                         target->momy += FixedMul(10*FRACUNIT, finesine[ang]);
1357                         if(!(target->flags&MF_NOGRAVITY))
1358                         {
1359                                 target->momz += 5*FRACUNIT;
1360                         }
1361                 }
1362                 else
1363                 {
1364                         target->momx += FixedMul(thrust, finecosine[ang]);
1365                         target->momy += FixedMul(thrust, finesine[ang]);
1366                 }
1367         }
1368
1369         //
1370         // player specific
1371         //
1372         if(player)
1373         {
1374                 // end of game hell hack
1375                 //if(target->subsector->sector->special == 11
1376                 //      && damage >= target->health)
1377                 //{
1378                 //      damage = target->health - 1;
1379                 //}
1380
1381                 if(damage < 1000 && ((player->cheats&CF_GODMODE)
1382                         || player->powers[pw_invulnerability]))
1383                 {
1384                         return;
1385                 }
1386                 if(player->armortype)
1387                 {
1388                         if(player->armortype == 1)
1389                         {
1390                                 saved = damage>>1;
1391                         }
1392                         else
1393                         {
1394                                 saved = (damage>>1)+(damage>>2);
1395                         }
1396                         if(player->armorpoints <= saved)
1397                         {
1398                                 // armor is used up
1399                                 saved = player->armorpoints;
1400                                 player->armortype = 0;
1401                         }
1402                         player->armorpoints -= saved;
1403                         damage -= saved;
1404                 }
1405                 if(damage >= player->health
1406                         && ((gameskill == sk_baby) || deathmatch)
1407                         && !player->chickenTics)
1408                 { // Try to use some inventory health
1409                         P_AutoUseHealth(player, damage-player->health+1);
1410                 }
1411                 player->health -= damage; // mirror mobj health here for Dave
1412                 if(player->health < 0)
1413                 {
1414                         player->health = 0;
1415                 }
1416                 player->attacker = source;
1417                 player->damagecount += damage; // add damage after armor / invuln
1418                 if(player->damagecount > 100)
1419                 {
1420                         player->damagecount = 100; // teleport stomp does 10k points...
1421                 }
1422                 temp = damage < 100 ? damage : 100;
1423                 if(player == &players[consoleplayer])
1424                 {
1425                         SB_PaletteFlash();
1426                 }
1427         }
1428
1429         //
1430         // do the damage
1431         //
1432         target->health -= damage;
1433         if(target->health <= 0)
1434         { // Death
1435                 target->special1 = damage;
1436                 if(target->type == MT_POD && source && source->type != MT_POD)
1437                 { // Make sure players get frags for chain-reaction kills
1438                         target->target = source;
1439                 }
1440                 if(player && inflictor && !player->chickenTics)
1441                 { // Check for flame death
1442                         if((inflictor->flags2&MF2_FIREDAMAGE)
1443                                 || ((inflictor->type == MT_PHOENIXFX1)
1444                                 && (target->health > -50) && (damage > 25)))
1445                         {
1446                                 target->flags2 |= MF2_FIREDAMAGE;
1447                         }
1448                 }
1449                 P_KillMobj(source, target);
1450                 return;
1451         }
1452         if((P_Random() < target->info->painchance)
1453                 && !(target->flags&MF_SKULLFLY))
1454         {
1455                 target->flags |= MF_JUSTHIT; // fight back!
1456                 P_SetMobjState(target, target->info->painstate);
1457         }
1458         target->reactiontime = 0; // we're awake now...
1459         if(!target->threshold && source && !(source->flags2&MF2_BOSS)
1460                 && !(target->type == MT_SORCERER2 && source->type == MT_WIZARD))
1461         {
1462                 // Target actor is not intent on another actor,
1463                 // so make him chase after source
1464                 target->target = source;
1465                 target->threshold = BASETHRESHOLD;
1466                 if(target->state == &states[target->info->spawnstate]
1467                         && target->info->seestate != S_NULL)
1468                 {
1469                         P_SetMobjState(target, target->info->seestate);
1470                 }
1471         }
1472 }