1 /** @file p_inter.c
2 *
3 * @authors Copyright © 2003-2017 Jaakko Keränen <jaakko.keranen@iki.fi>
4 * @authors Copyright © 2005-2013 Daniel Swanson <danij@dengine.net>
5 * @authors Copyright © 1999 Activision
6 *
7 * @par License
8 * GPL: http://www.gnu.org/licenses/gpl.html
9 *
10 * <small>This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by the
12 * Free Software Foundation; either version 2 of the License, or (at your
13 * option) any later version. This program is distributed in the hope that it
14 * will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
15 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
16 * Public License for more details. You should have received a copy of the GNU
17 * General Public License along with this program; if not, write to the Free
18 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
19 * 02110-1301 USA</small>
20 */
21
22 #include <string.h>
23 #include <math.h>
24
25 #include "jhexen.h"
26 #include "p_inter.h"
27
28 #include "d_netsv.h"
29 #include "g_common.h"
30 #include "hu_inventory.h"
31 #include "mobj.h"
32 #include "p_inventory.h"
33 #include "p_map.h"
34 #include "p_user.h"
35 #include "player.h"
36
37 #define BONUSADD (6)
38
39 typedef enum {
40 IT_NONE = -1,
41 IT_HEALTH_VIAL,
42 IT_ARMOR_MESH,
43 IT_ARMOR_SHIELD,
44 IT_ARMOR_HELMET,
45 IT_ARMOR_AMULET,
46 IT_KEY_STEEL,
47 IT_KEY_CAVE,
48 IT_KEY_AXE,
49 IT_KEY_FIRE,
50 IT_KEY_EMERALD,
51 IT_KEY_DUNGEON,
52 IT_KEY_SILVER,
53 IT_KEY_RUSTED,
54 IT_KEY_HORN,
55 IT_KEY_SWAMP,
56 IT_KEY_CASTLE,
57 IT_ITEM_QUARTZFLASK,
58 IT_ITEM_WINGS,
59 IT_ITEM_DEFENDER,
60 IT_ITEM_SERVANT,
61 IT_ITEM_PORKALATOR,
62 IT_ITEM_MYSTICURN,
63 IT_ITEM_AMBITINCANT,
64 IT_ITEM_TORCH,
65 IT_ITEM_CHAOSDEVICE,
66 IT_ITEM_BANISHDEVICE,
67 IT_ITEM_FLETCHETTE,
68 IT_ITEM_BOOTSOFSPEED,
69 IT_ITEM_KRATEROFMIGHT,
70 IT_ITEM_BRACERS,
71 IT_ITEM_REPULSION,
72 IT_PUZZLE_SKULL,
73 IT_PUZZLE_BIGGEM,
74 IT_PUZZLE_REDGEM,
75 IT_PUZZLE_GREENGEM1,
76 IT_PUZZLE_GREENGEM2,
77 IT_PUZZLE_BLUEGEM1,
78 IT_PUZZLE_BLUEGEM2,
79 IT_PUZZLE_BOOK1,
80 IT_PUZZLE_BOOK2,
81 IT_PUZZLE_SKULL2,
82 IT_PUZZLE_FWEAPON,
83 IT_PUZZLE_CWEAPON,
84 IT_PUZZLE_MWEAPON,
85 IT_PUZZLE_GEAR1,
86 IT_PUZZLE_GEAR2,
87 IT_PUZZLE_GEAR3,
88 IT_PUZZLE_GEAR4,
89 IT_MANA_BLUE,
90 IT_MANA_GREEN,
91 IT_MANA_COMBINED,
92 IT_WEAPON_FROSTSHARDS,
93 IT_WEAPON_ARCOFDEATH,
94 IT_WEAPON_AXE,
95 IT_WEAPON_HAMMER,
96 IT_WEAPON_SERPENTSTAFF,
97 IT_WEAPON_FIRESTORM,
98 IT_WEAPON_QUIETUS1,
99 IT_WEAPON_QUIETUS2,
100 IT_WEAPON_QUIETUS3,
101 IT_WEAPON_WRAITHVERGE1,
102 IT_WEAPON_WRAITHVERGE2,
103 IT_WEAPON_WRAITHVERGE3,
104 IT_WEAPON_BLOODSCOURGE1,
105 IT_WEAPON_BLOODSCOURGE2,
106 IT_WEAPON_BLOODSCOURGE3
107 } itemtype_t;
108
109 // Item Info Flags:
110 #define IIF_LEAVE_COOP 0x1 // Leave for others in Cooperative games.
111 #define IIF_LEAVE_DEATHMATCH 0x2 // Leave for others in Deathmatch games.
112
113 typedef struct iteminfo_s {
114 itemtype_t type;
115 short flags; // IIF_* flags.
116 dd_bool (*giveFunc) (player_t*);
117 textenum_t pickupMsg;
118 sfxenum_t pickupSound;
119 } iteminfo_t;
120
121 static void setDormantItem(mobj_t* mo);
122
123 static dd_bool pickupHealthVial(player_t* plr);
124 static dd_bool pickupMesh(player_t* plr);
125 static dd_bool pickupShield(player_t* plr);
126 static dd_bool pickupHelmet(player_t* plr);
127 static dd_bool pickupAmulet(player_t* plr);
128 static dd_bool pickupSteelKey(player_t* plr);
129 static dd_bool pickupCaveKey(player_t* plr);
130 static dd_bool pickupAxeKey(player_t* plr);
131 static dd_bool pickupFireKey(player_t* plr);
132 static dd_bool pickupEmeraldKey(player_t* plr);
133 static dd_bool pickupDungeonKey(player_t* plr);
134 static dd_bool pickupSilverKey(player_t* plr);
135 static dd_bool pickupRustedKey(player_t* plr);
136 static dd_bool pickupHornKey(player_t* plr);
137 static dd_bool pickupSwampKey(player_t* plr);
138 static dd_bool pickupCastleKey(player_t* plr);
139 static dd_bool pickupQuartzFlask(player_t* plr);
140 static dd_bool pickupWings(player_t* plr);
141 static dd_bool pickupDefender(player_t* plr);
142 static dd_bool pickupServant(player_t* plr);
143 static dd_bool pickupPorkalator(player_t* plr);
144 static dd_bool pickupMysticUrn(player_t* plr);
145 static dd_bool pickupAmbitIncant(player_t* plr);
146 static dd_bool pickupTorch(player_t* plr);
147 static dd_bool pickupChaosDevice(player_t* plr);
148 static dd_bool pickupBanishDevice(player_t* plr);
149 static dd_bool pickupFletchette(player_t* plr);
150 static dd_bool pickupBootsOfSpeed(player_t* plr);
151 static dd_bool pickupKraterOfMight(player_t* plr);
152 static dd_bool pickupBracers(player_t* plr);
153 static dd_bool pickupRepulsion(player_t* plr);
154 static dd_bool pickupSkull(player_t* plr);
155 static dd_bool pickupBigGem(player_t* plr);
156 static dd_bool pickupRedGem(player_t* plr);
157 static dd_bool pickupGreenGem1(player_t* plr);
158 static dd_bool pickupGreenGem2(player_t* plr);
159 static dd_bool pickupBlueGem1(player_t* plr);
160 static dd_bool pickupBlueGem2(player_t* plr);
161 static dd_bool pickupBook1(player_t* plr);
162 static dd_bool pickupBook2(player_t* plr);
163 static dd_bool pickupSkull2(player_t* plr);
164 static dd_bool pickupFWeapon(player_t* plr);
165 static dd_bool pickupCWeapon(player_t* plr);
166 static dd_bool pickupMWeapon(player_t* plr);
167 static dd_bool pickupGear1(player_t* plr);
168 static dd_bool pickupGear2(player_t* plr);
169 static dd_bool pickupGear3(player_t* plr);
170 static dd_bool pickupGear4(player_t* plr);
171 static dd_bool pickupBlueMana(player_t* plr);
172 static dd_bool pickupGreenMana(player_t* plr);
173 static dd_bool pickupCombinedMana(player_t* plr);
174 static dd_bool pickupFrostShards(player_t* plr);
175 static dd_bool pickupArcOfDeath(player_t* plr);
176 static dd_bool pickupAxe(player_t* plr);
177 static dd_bool pickupHammer(player_t* plr);
178 static dd_bool pickupSerpentStaff(player_t* plr);
179 static dd_bool pickupFireStorm(player_t* plr);
180 static dd_bool pickupQuietus1(player_t* plr);
181 static dd_bool pickupQuietus2(player_t* plr);
182 static dd_bool pickupQuietus3(player_t* plr);
183 static dd_bool pickupWraithVerge1(player_t* plr);
184 static dd_bool pickupWraithVerge2(player_t* plr);
185 static dd_bool pickupWraithVerge3(player_t* plr);
186 static dd_bool pickupBloodScourge1(player_t* plr);
187 static dd_bool pickupBloodScourge2(player_t* plr);
188 static dd_bool pickupBloodScourge3(player_t* plr);
189
190 int TextKeyMessages[] = {
191 TXT_TXT_KEY_STEEL,
192 TXT_TXT_KEY_CAVE,
193 TXT_TXT_KEY_AXE,
194 TXT_TXT_KEY_FIRE,
195 TXT_TXT_KEY_EMERALD,
196 TXT_TXT_KEY_DUNGEON,
197 TXT_TXT_KEY_SILVER,
198 TXT_TXT_KEY_RUSTED,
199 TXT_TXT_KEY_HORN,
200 TXT_TXT_KEY_SWAMP,
201 TXT_TXT_KEY_CASTLE
202 };
203
204 // Index using itemtype_t - 1;
205 static const iteminfo_t items[] = {
206 { IT_HEALTH_VIAL, 0, pickupHealthVial, TXT_TXT_ITEMHEALTH, SFX_PICKUP_PUZZ },
207 { IT_ARMOR_MESH, 0, pickupMesh, TXT_TXT_ARMOR1, SFX_PICKUP_PUZZ },
208 { IT_ARMOR_SHIELD, 0, pickupShield, TXT_TXT_ARMOR2, SFX_PICKUP_PUZZ },
209 { IT_ARMOR_HELMET, 0, pickupHelmet, TXT_TXT_ARMOR3, SFX_PICKUP_PUZZ },
210 { IT_ARMOR_AMULET, 0, pickupAmulet, TXT_TXT_ARMOR4, SFX_PICKUP_PUZZ },
211 { IT_KEY_STEEL, IIF_LEAVE_COOP | IIF_LEAVE_DEATHMATCH, pickupSteelKey, TXT_TXT_KEY_STEEL, SFX_PICKUP_KEY },
212 { IT_KEY_CAVE, IIF_LEAVE_COOP | IIF_LEAVE_DEATHMATCH, pickupCaveKey, TXT_TXT_KEY_CAVE, SFX_PICKUP_KEY },
213 { IT_KEY_AXE, IIF_LEAVE_COOP | IIF_LEAVE_DEATHMATCH, pickupAxeKey, TXT_TXT_KEY_AXE, SFX_PICKUP_KEY },
214 { IT_KEY_FIRE, IIF_LEAVE_COOP | IIF_LEAVE_DEATHMATCH, pickupFireKey, TXT_TXT_KEY_FIRE, SFX_PICKUP_KEY },
215 { IT_KEY_EMERALD, IIF_LEAVE_COOP | IIF_LEAVE_DEATHMATCH, pickupEmeraldKey, TXT_TXT_KEY_EMERALD, SFX_PICKUP_KEY },
216 { IT_KEY_DUNGEON, IIF_LEAVE_COOP | IIF_LEAVE_DEATHMATCH, pickupDungeonKey, TXT_TXT_KEY_DUNGEON, SFX_PICKUP_KEY },
217 { IT_KEY_SILVER, IIF_LEAVE_COOP | IIF_LEAVE_DEATHMATCH, pickupSilverKey, TXT_TXT_KEY_SILVER, SFX_PICKUP_KEY },
218 { IT_KEY_RUSTED, IIF_LEAVE_COOP | IIF_LEAVE_DEATHMATCH, pickupRustedKey, TXT_TXT_KEY_RUSTED, SFX_PICKUP_KEY },
219 { IT_KEY_HORN, IIF_LEAVE_COOP | IIF_LEAVE_DEATHMATCH, pickupHornKey, TXT_TXT_KEY_HORN, SFX_PICKUP_KEY },
220 { IT_KEY_SWAMP, IIF_LEAVE_COOP | IIF_LEAVE_DEATHMATCH, pickupSwampKey, TXT_TXT_KEY_SWAMP, SFX_PICKUP_KEY },
221 { IT_KEY_CASTLE, IIF_LEAVE_COOP | IIF_LEAVE_DEATHMATCH, pickupCastleKey, TXT_TXT_KEY_CASTLE, SFX_PICKUP_KEY },
222 { IT_ITEM_QUARTZFLASK, 0, pickupQuartzFlask, TXT_TXT_INV_HEALTH, SFX_PICKUP_ITEM },
223 { IT_ITEM_WINGS, 0, pickupWings, TXT_TXT_INV_FLY, SFX_PICKUP_ITEM },
224 { IT_ITEM_DEFENDER, 0, pickupDefender, TXT_TXT_INV_INVULNERABILITY, SFX_PICKUP_ITEM },
225 { IT_ITEM_SERVANT, 0, pickupServant, TXT_TXT_INV_SUMMON, SFX_PICKUP_ITEM },
226 { IT_ITEM_PORKALATOR, 0, pickupPorkalator, TXT_TXT_INV_EGG, SFX_PICKUP_ITEM },
227 { IT_ITEM_MYSTICURN, 0, pickupMysticUrn, TXT_TXT_INV_SUPERHEALTH, SFX_PICKUP_ITEM },
228 { IT_ITEM_AMBITINCANT, 0, pickupAmbitIncant, TXT_TXT_INV_HEALINGRADIUS, SFX_PICKUP_ITEM },
229 { IT_ITEM_TORCH, 0, pickupTorch, TXT_TXT_INV_TORCH, SFX_PICKUP_ITEM },
230 { IT_ITEM_CHAOSDEVICE, 0, pickupChaosDevice, TXT_TXT_INV_TELEPORT, SFX_PICKUP_ITEM },
231 { IT_ITEM_BANISHDEVICE, 0, pickupBanishDevice, TXT_TXT_INV_TELEPORTOTHER, SFX_PICKUP_ITEM },
232 { IT_ITEM_FLETCHETTE, 0, pickupFletchette, TXT_TXT_INV_POISONBAG, SFX_PICKUP_ITEM },
233 { IT_ITEM_BOOTSOFSPEED, 0, pickupBootsOfSpeed, TXT_TXT_INV_SPEED, SFX_PICKUP_ITEM },
234 { IT_ITEM_KRATEROFMIGHT, 0, pickupKraterOfMight, TXT_TXT_INV_BOOSTMANA, SFX_PICKUP_ITEM },
235 { IT_ITEM_BRACERS, 0, pickupBracers, TXT_TXT_INV_BOOSTARMOR, SFX_PICKUP_ITEM },
236 { IT_ITEM_REPULSION, 0, pickupRepulsion, TXT_TXT_INV_BLASTRADIUS, SFX_PICKUP_ITEM },
237 { IT_PUZZLE_SKULL, IIF_LEAVE_COOP, pickupSkull, TXT_TXT_INV_PUZZSKULL, SFX_PICKUP_PUZZ },
238 { IT_PUZZLE_BIGGEM, IIF_LEAVE_COOP, pickupBigGem, TXT_TXT_INV_PUZZGEMBIG, SFX_PICKUP_PUZZ },
239 { IT_PUZZLE_REDGEM, IIF_LEAVE_COOP, pickupRedGem, TXT_TXT_INV_PUZZGEMRED, SFX_PICKUP_PUZZ },
240 { IT_PUZZLE_GREENGEM1, IIF_LEAVE_COOP, pickupGreenGem1, TXT_TXT_INV_PUZZGEMGREEN1, SFX_PICKUP_PUZZ },
241 { IT_PUZZLE_GREENGEM2, IIF_LEAVE_COOP, pickupGreenGem2, TXT_TXT_INV_PUZZGEMGREEN2, SFX_PICKUP_PUZZ },
242 { IT_PUZZLE_BLUEGEM1, IIF_LEAVE_COOP, pickupBlueGem1, TXT_TXT_INV_PUZZGEMBLUE1, SFX_PICKUP_PUZZ },
243 { IT_PUZZLE_BLUEGEM2, IIF_LEAVE_COOP, pickupBlueGem2, TXT_TXT_INV_PUZZGEMBLUE2, SFX_PICKUP_PUZZ },
244 { IT_PUZZLE_BOOK1, IIF_LEAVE_COOP, pickupBook1, TXT_TXT_INV_PUZZBOOK1, SFX_PICKUP_PUZZ },
245 { IT_PUZZLE_BOOK2, IIF_LEAVE_COOP, pickupBook2, TXT_TXT_INV_PUZZBOOK2, SFX_PICKUP_PUZZ },
246 { IT_PUZZLE_SKULL2, IIF_LEAVE_COOP, pickupSkull2, TXT_TXT_INV_PUZZSKULL2, SFX_PICKUP_PUZZ },
247 { IT_PUZZLE_FWEAPON, IIF_LEAVE_COOP, pickupFWeapon, TXT_TXT_INV_PUZZFWEAPON, SFX_PICKUP_PUZZ },
248 { IT_PUZZLE_CWEAPON, IIF_LEAVE_COOP, pickupCWeapon, TXT_TXT_INV_PUZZCWEAPON, SFX_PICKUP_PUZZ },
249 { IT_PUZZLE_MWEAPON, IIF_LEAVE_COOP, pickupMWeapon, TXT_TXT_INV_PUZZMWEAPON, SFX_PICKUP_PUZZ },
250 { IT_PUZZLE_GEAR1, IIF_LEAVE_COOP, pickupGear1, TXT_TXT_INV_PUZZGEAR1, SFX_PICKUP_PUZZ },
251 { IT_PUZZLE_GEAR2, IIF_LEAVE_COOP, pickupGear2, TXT_TXT_INV_PUZZGEAR2, SFX_PICKUP_PUZZ },
252 { IT_PUZZLE_GEAR3, IIF_LEAVE_COOP, pickupGear3, TXT_TXT_INV_PUZZGEAR3, SFX_PICKUP_PUZZ },
253 { IT_PUZZLE_GEAR4, IIF_LEAVE_COOP, pickupGear4, TXT_TXT_INV_PUZZGEAR4, SFX_PICKUP_PUZZ },
254 { IT_MANA_BLUE, 0, pickupBlueMana, TXT_TXT_MANA_1, SFX_PICKUP_PUZZ },
255 { IT_MANA_GREEN, 0, pickupGreenMana, TXT_TXT_MANA_2, SFX_PICKUP_PUZZ },
256 { IT_MANA_COMBINED, 0, pickupCombinedMana, TXT_TXT_MANA_BOTH, SFX_PICKUP_PUZZ },
257 { IT_WEAPON_FROSTSHARDS, IIF_LEAVE_COOP, pickupFrostShards, TXT_TXT_WEAPON_M2, SFX_PICKUP_WEAPON },
258 { IT_WEAPON_ARCOFDEATH, IIF_LEAVE_COOP, pickupArcOfDeath, TXT_TXT_WEAPON_M3, SFX_PICKUP_WEAPON },
259 { IT_WEAPON_AXE, IIF_LEAVE_COOP, pickupAxe, TXT_TXT_WEAPON_F2, SFX_PICKUP_WEAPON },
260 { IT_WEAPON_HAMMER, IIF_LEAVE_COOP, pickupHammer, TXT_TXT_WEAPON_F3, SFX_PICKUP_WEAPON },
261 { IT_WEAPON_SERPENTSTAFF, IIF_LEAVE_COOP, pickupSerpentStaff, TXT_TXT_WEAPON_C2, SFX_PICKUP_WEAPON },
262 { IT_WEAPON_FIRESTORM, IIF_LEAVE_COOP, pickupFireStorm, TXT_TXT_WEAPON_C3, SFX_PICKUP_WEAPON },
263 { IT_WEAPON_QUIETUS1, IIF_LEAVE_COOP, pickupQuietus1, TXT_TXT_QUIETUS_PIECE, SFX_PICKUP_WEAPON },
264 { IT_WEAPON_QUIETUS2, IIF_LEAVE_COOP, pickupQuietus2, TXT_TXT_QUIETUS_PIECE, SFX_PICKUP_WEAPON },
265 { IT_WEAPON_QUIETUS3, IIF_LEAVE_COOP, pickupQuietus3, TXT_TXT_QUIETUS_PIECE, SFX_PICKUP_WEAPON },
266 { IT_WEAPON_WRAITHVERGE1, IIF_LEAVE_COOP, pickupWraithVerge1, TXT_TXT_WRAITHVERGE_PIECE, SFX_PICKUP_WEAPON },
267 { IT_WEAPON_WRAITHVERGE2, IIF_LEAVE_COOP, pickupWraithVerge2, TXT_TXT_WRAITHVERGE_PIECE, SFX_PICKUP_WEAPON },
268 { IT_WEAPON_WRAITHVERGE3, IIF_LEAVE_COOP, pickupWraithVerge3, TXT_TXT_WRAITHVERGE_PIECE, SFX_PICKUP_WEAPON },
269 { IT_WEAPON_BLOODSCOURGE1, IIF_LEAVE_COOP, pickupBloodScourge1, TXT_TXT_BLOODSCOURGE_PIECE, SFX_PICKUP_WEAPON },
270 { IT_WEAPON_BLOODSCOURGE2, IIF_LEAVE_COOP, pickupBloodScourge2, TXT_TXT_BLOODSCOURGE_PIECE, SFX_PICKUP_WEAPON },
271 { IT_WEAPON_BLOODSCOURGE3, IIF_LEAVE_COOP, pickupBloodScourge3, TXT_TXT_BLOODSCOURGE_PIECE, SFX_PICKUP_WEAPON }
272 };
273
P_HideSpecialThing(mobj_t * thing)274 void P_HideSpecialThing(mobj_t *thing)
275 {
276 thing->flags &= ~MF_SPECIAL;
277 thing->flags2 |= MF2_DONTDRAW;
278 P_MobjChangeState(thing, S_HIDESPECIAL1);
279 }
280
giveOneAmmo(player_t * plr,ammotype_t ammoType,int numRounds)281 static dd_bool giveOneAmmo(player_t *plr, ammotype_t ammoType, int numRounds)
282 {
283 int oldAmmo;
284
285 DENG_ASSERT(plr != 0);
286 DENG_ASSERT(ammoType >= AT_FIRST && ammoType < NUM_AMMO_TYPES);
287
288 // Giving the special 'unlimited ammo' type always succeeds.
289 if(ammoType == AT_NOAMMO)
290 return true;
291
292 // Already fully stocked?
293 if(plr->ammo[ammoType].owned >= MAX_MANA)
294 return false;
295
296 oldAmmo = plr->ammo[ammoType].owned;
297
298 if(numRounds == 0)
299 {
300 return false;
301 }
302 else if(numRounds < 0)
303 {
304 // Fully replenish.
305 numRounds = MAX_MANA;
306 }
307
308 // Give extra rounds at easy/nightmare skill levels.
309 if(gfw_Rule(skill) == SM_BABY ||
310 gfw_Rule(skill) == SM_NIGHTMARE)
311 {
312 numRounds += numRounds / 2;
313 }
314
315 // Given the new ammo the player may want to change weapon automatically.
316 P_MaybeChangeWeapon(plr, WT_NOCHANGE, ammoType, false);
317
318 // Restock the player.
319 plr->ammo[ammoType].owned = MIN_OF(MAX_MANA,
320 plr->ammo[ammoType].owned + numRounds);
321 plr->update |= PSF_AMMO;
322
323 /// @todo fixme: This shouldn't be actioned from here.
324 if(plr->class_ == PCLASS_FIGHTER && plr->readyWeapon == WT_SECOND &&
325 ammoType == AT_BLUEMANA && oldAmmo <= 0)
326 {
327 P_SetPsprite(plr, ps_weapon, S_FAXEREADY_G);
328 }
329
330 // Maybe unhide the HUD?
331 ST_HUDUnHide(plr - players, HUE_ON_PICKUP_AMMO);
332
333 return true;
334 }
335
P_GiveAmmo(player_t * plr,ammotype_t ammoType,int numRounds)336 dd_bool P_GiveAmmo(player_t *plr, ammotype_t ammoType, int numRounds)
337 {
338 int gaveAmmos = 0;
339
340 if(ammoType == NUM_AMMO_TYPES)
341 {
342 // Give all ammos.
343 int i = 0;
344 for(i = 0; i < NUM_AMMO_TYPES; ++i)
345 {
346 gaveAmmos |= (int)giveOneAmmo(plr, (ammotype_t) i, numRounds) << i;
347 }
348 }
349 else
350 {
351 // Give a single ammo.
352 gaveAmmos |= (int)giveOneAmmo(plr, ammoType, numRounds) << (int)ammoType;
353 }
354
355 return gaveAmmos != 0;
356 }
357
giveOneWeapon(player_t * plr,weapontype_t weaponType,playerclass_t matchClass)358 static dd_bool giveOneWeapon(player_t *plr, weapontype_t weaponType,
359 playerclass_t matchClass)
360 {
361 ammotype_t ammoType = (weaponType == WT_SECOND)? AT_BLUEMANA : AT_GREENMANA;
362 dd_bool gaveWeapon = false, gaveAmmo = false;
363
364 DENG_ASSERT(plr != 0);
365 DENG_ASSERT(weaponType >= WT_FIRST && weaponType < NUM_WEAPON_TYPES);
366
367 if(plr->class_ != matchClass)
368 {
369 return P_GiveAmmo(plr, ammoType, 25);
370 }
371
372 // Always attempt to give mana unless this a cooperative game and the
373 // player already has this weapon piece.
374 if(!(IS_NETGAME && !gfw_Rule(deathmatch) && plr->weapons[weaponType].owned))
375 {
376 if(P_GiveAmmo(plr, ammoType, 25))
377 {
378 gaveAmmo = true;
379 }
380 }
381
382 if(!plr->weapons[weaponType].owned)
383 {
384 gaveWeapon = true;
385
386 plr->weapons[weaponType].owned = true;
387 plr->update |= PSF_OWNED_WEAPONS;
388
389 // Given the new weapon the player may want to change automatically.
390 P_MaybeChangeWeapon(plr, weaponType, AT_NOAMMO, false /*don't force*/);
391
392 // Maybe unhide the HUD?
393 ST_HUDUnHide(plr - players, HUE_ON_PICKUP_WEAPON);
394 }
395
396 return (gaveWeapon || gaveAmmo);
397 }
398
P_GiveWeapon2(player_t * plr,weapontype_t weaponType,playerclass_t matchClass)399 dd_bool P_GiveWeapon2(player_t *plr, weapontype_t weaponType, playerclass_t matchClass)
400 {
401 int gaveWeapons = 0;
402
403 if(weaponType == NUM_WEAPON_TYPES)
404 {
405 // Give all weapons.
406 int i = 0;
407 for(; i < NUM_WEAPON_TYPES; ++i)
408 {
409 gaveWeapons |= (int)giveOneWeapon(plr, (weapontype_t) i, matchClass) << i;
410 }
411 }
412 else
413 {
414 // Give a single weapon.
415 gaveWeapons |= (int)giveOneWeapon(plr, weaponType, matchClass) << (int)weaponType;
416 }
417
418 // Leave placed weapons forever on net games.
419 if(IS_NETGAME && !gfw_Rule(deathmatch))
420 return false;
421
422 return gaveWeapons != 0;
423 }
424
P_GiveWeapon(player_t * plr,weapontype_t weaponType)425 dd_bool P_GiveWeapon(player_t *plr, weapontype_t weaponType)
426 {
427 DENG_ASSERT(plr != 0);
428 return P_GiveWeapon2(plr, weaponType, plr->class_);
429 }
430
P_GiveWeaponPiece2(player_t * plr,int piece,playerclass_t matchClass)431 dd_bool P_GiveWeaponPiece2(player_t *plr, int piece, playerclass_t matchClass)
432 {
433 dd_bool gaveAmmo = false;
434
435 // Give all pieces?
436 if(piece < 0 || piece >= WEAPON_FOURTH_PIECE_COUNT)
437 {
438 int gavePieces = 0;
439 int i = 0;
440 for(; i < WEAPON_FOURTH_PIECE_COUNT; ++i)
441 {
442 gavePieces |= (int)P_GiveWeaponPiece2(plr, i, matchClass);
443 }
444 return gavePieces != 0;
445 }
446
447 if(plr->class_ != matchClass)
448 {
449 // Can't pick up wrong-class weapons in coop netplay.
450 if(IS_NETGAME && !gfw_Rule(deathmatch))
451 return false;
452
453 return P_GiveAmmo(plr, AT_BLUEMANA, 20) | P_GiveAmmo(plr, AT_GREENMANA, 20);
454 }
455
456 // Always attempt to give mana unless this a cooperative game and the
457 // player already has this weapon piece.
458 if(!((plr->pieces & (1 << piece)) && IS_NETGAME && !gfw_Rule(deathmatch)))
459 {
460 gaveAmmo = P_GiveAmmo(plr, AT_BLUEMANA, 20) | P_GiveAmmo(plr, AT_GREENMANA, 20);
461 }
462
463 if(plr->pieces & (1 << piece))
464 {
465 // Already has the piece.
466 if(IS_NETGAME && !gfw_Rule(deathmatch)) // Cooperative net-game.
467 return false;
468
469 // Deathmatch or single player.
470
471 if(!gaveAmmo) // Didn't need the ammo so don't pick it up.
472 return false;
473 }
474
475 // Give the specified weapon piece.
476 plr->pieces |= (1 << piece);
477
478 // In a cooperative net-game, give the "lesser" pieces also.
479 if(IS_NETGAME && !gfw_Rule(deathmatch))
480 {
481 for(int i = 0; i < piece; ++i)
482 {
483 plr->pieces |= (1 << i);
484 }
485 }
486
487 // Can we now assemble the fourth-weapon?
488 if(plr->pieces == WEAPON_FOURTH_COMPLETE)
489 {
490 // Bestow the fourth-weapon.
491 /// @todo Should use @ref P_GiveWeapon() here.
492 plr->weapons[WT_FOURTH].owned = true;
493 plr->pendingWeapon = WT_FOURTH;
494 plr->update |= PSF_WEAPONS | PSF_OWNED_WEAPONS;
495
496 // Should we change weapon automatically?
497 P_MaybeChangeWeapon(plr, WT_FOURTH, AT_NOAMMO, false);
498 }
499
500 // Maybe unhide the HUD?
501 ST_HUDUnHide(plr - players, HUE_ON_PICKUP_WEAPON);
502
503 return true;
504 }
505
P_GiveWeaponPiece(player_t * plr,int pieceValue)506 dd_bool P_GiveWeaponPiece(player_t *plr, int pieceValue)
507 {
508 DENG_ASSERT(plr != 0);
509 return P_GiveWeaponPiece2(plr, pieceValue, plr->class_);
510 }
511
maxPlayerHealth(dd_bool morphed)512 static int maxPlayerHealth(dd_bool morphed)
513 {
514 return morphed? MAXMORPHHEALTH : maxHealth;
515 }
516
P_GiveHealth(player_t * player,int amount)517 dd_bool P_GiveHealth(player_t *player, int amount)
518 {
519 int healthLimit = maxPlayerHealth(player->morphTics != 0);
520
521 // Already at capacity?
522 if(player->health >= healthLimit)
523 return false;
524
525 if(amount < 0)
526 {
527 // Fully replenish.
528 amount = healthLimit;
529 }
530
531 player->health =
532 player->plr->mo->health = MIN_OF(player->health + amount, healthLimit);
533 player->update |= PSF_HEALTH;
534
535 // Maybe unhide the HUD?
536 ST_HUDUnHide(player - players, HUE_ON_PICKUP_HEALTH);
537
538 return true;
539 }
540
giveOneArmor(player_t * plr,armortype_t armorType)541 static dd_bool giveOneArmor(player_t *plr, armortype_t armorType)
542 {
543 int points;
544
545 DENG_ASSERT(plr != 0);
546 DENG_ASSERT(armorType >= ARMOR_FIRST && armorType < NUMARMOR);
547
548 points = PCLASS_INFO(plr->class_)->armorIncrement[armorType];
549 if(plr->armorPoints[armorType] >= points)
550 return false;
551
552 P_PlayerGiveArmorBonus(plr, armorType, points - plr->armorPoints[armorType]);
553
554 // Maybe unhide the HUD?
555 ST_HUDUnHide(plr - players, HUE_ON_PICKUP_ARMOR);
556
557 return true;
558 }
559
P_GiveArmor(player_t * plr,armortype_t armorType)560 dd_bool P_GiveArmor(player_t *plr, armortype_t armorType)
561 {
562 int gaveArmors = 0;
563
564 if(armorType == NUMARMOR)
565 {
566 // Give all armors.
567 int i = 0;
568 for(i = 0; i < NUMARMOR; ++i)
569 {
570 gaveArmors |= (int)giveOneArmor(plr, (armortype_t) i) << i;
571 }
572 }
573 else
574 {
575 // Give a single armor.
576 gaveArmors |= (int)giveOneArmor(plr, armorType) << (int)armorType;
577 }
578
579 return gaveArmors != 0;
580 }
581
P_GiveArmorAlt(player_t * plr,armortype_t armorType,int amount)582 dd_bool P_GiveArmorAlt(player_t *plr, armortype_t armorType, int amount)
583 {
584 int hits, totalArmor;
585
586 hits = amount * 5 * FRACUNIT;
587 totalArmor =
588 plr->armorPoints[ARMOR_ARMOR] +
589 plr->armorPoints[ARMOR_SHIELD] +
590 plr->armorPoints[ARMOR_HELMET] +
591 plr->armorPoints[ARMOR_AMULET] +
592 PCLASS_INFO(plr->class_)->autoArmorSave;
593
594 if(totalArmor >= PCLASS_INFO(plr->class_)->maxArmor * 5 * FRACUNIT)
595 {
596 return false;
597 }
598
599 plr->armorPoints[armorType] += hits;
600 plr->update |= PSF_ARMOR;
601
602 // Maybe unhide the HUD?
603 ST_HUDUnHide(plr - players, HUE_ON_PICKUP_ARMOR);
604
605 return true;
606 }
607
giveOneKey(player_t * plr,keytype_t keyType)608 static dd_bool giveOneKey(player_t *plr, keytype_t keyType)
609 {
610 DENG_ASSERT(plr != 0);
611 DENG_ASSERT(keyType >= KT_FIRST && keyType < NUM_KEY_TYPES);
612
613 // Already owned?
614 if(plr->keys & (1 << keyType)) return false;
615
616 plr->keys |= 1 << keyType;
617 plr->bonusCount += BONUSADD;
618 plr->update |= PSF_KEYS;
619
620 // Maybe unhide the HUD?
621 ST_HUDUnHide(plr - players, HUE_ON_PICKUP_KEY);
622
623 return true;
624 }
625
P_GiveKey(player_t * plr,keytype_t keyType)626 dd_bool P_GiveKey(player_t *plr, keytype_t keyType)
627 {
628 int gaveKeys = 0;
629
630 if(keyType == NUM_KEY_TYPES)
631 {
632 // Give all keys.
633 int i = 0;
634 for(i = 0; i < NUM_KEY_TYPES; ++i)
635 {
636 gaveKeys |= (int)giveOneKey(plr, (keytype_t) i) << i;
637 }
638 }
639 else
640 {
641 // Give a single key.
642 gaveKeys |= (int)giveOneKey(plr, keyType) << (int)keyType;
643 }
644
645 return gaveKeys != 0;
646 }
647
P_GivePower(player_t * plr,powertype_t power)648 dd_bool P_GivePower(player_t *plr, powertype_t power)
649 {
650 dd_bool retval = false;
651
652 plr->update |= PSF_POWERS;
653
654 switch(power)
655 {
656 case PT_INVULNERABILITY:
657 if(!(plr->powers[power] > BLINKTHRESHOLD))
658 {
659 plr->powers[power] = INVULNTICS;
660 plr->plr->mo->flags2 |= MF2_INVULNERABLE;
661 if(plr->class_ == PCLASS_MAGE)
662 {
663 plr->plr->mo->flags2 |= MF2_REFLECTIVE;
664 }
665 retval = true;
666 }
667 break;
668
669 case PT_FLIGHT:
670 if(!(plr->powers[power] > BLINKTHRESHOLD))
671 {
672 plr->powers[power] = FLIGHTTICS;
673 plr->plr->mo->flags2 |= MF2_FLY;
674 plr->plr->mo->flags |= MF_NOGRAVITY;
675 if(plr->plr->mo->origin[VZ] <= plr->plr->mo->floorZ)
676 {
677 plr->flyHeight = 10; // Thrust the plr in the air a bit.
678 plr->plr->flags |= DDPF_FIXMOM;
679 }
680 retval = true;
681 }
682 break;
683
684 case PT_INFRARED:
685 if(!(plr->powers[power] > BLINKTHRESHOLD))
686 {
687 plr->powers[power] = INFRATICS;
688 retval = true;
689 }
690 break;
691
692 case PT_SPEED:
693 if(!(plr->powers[power] > BLINKTHRESHOLD))
694 {
695 plr->powers[power] = SPEEDTICS;
696 retval = true;
697 }
698 break;
699
700 case PT_MINOTAUR:
701 // Doesn't matter if already have power, renew ticker.
702 plr->powers[power] = MAULATORTICS;
703 retval = true;
704 break;
705
706 default:
707 if(!(plr->powers[power]))
708 {
709 plr->powers[power] = 1;
710 retval = true;
711 }
712 break;
713 }
714
715 if(retval)
716 {
717 // Maybe unhide the HUD?
718 ST_HUDUnHide(plr - players, HUE_ON_PICKUP_POWER);
719 }
720
721 return retval;
722 }
723
P_GiveItem(player_t * plr,inventoryitemtype_t item)724 dd_bool P_GiveItem(player_t *plr, inventoryitemtype_t item)
725 {
726 if(plr)
727 return P_InventoryGive(plr - players, item, false);
728
729 return false;
730 }
731
732 /**
733 * Removes the MF_SPECIAL flag and initiates the item pickup animation.
734 */
setDormantItem(mobj_t * mo)735 static void setDormantItem(mobj_t *mo)
736 {
737 mo->flags &= ~MF_SPECIAL;
738 if(gfw_Rule(deathmatch) && !(mo->flags2 & MF2_DROPPED))
739 {
740 if(mo->type == MT_ARTIINVULNERABILITY)
741 {
742 P_MobjChangeState(mo, S_DORMANTARTI3_1);
743 }
744 else if(mo->type == MT_SUMMONMAULATOR || mo->type == MT_ARTIFLY)
745 {
746 P_MobjChangeState(mo, S_DORMANTARTI2_1);
747 }
748 else
749 {
750 P_MobjChangeState(mo, S_DORMANTARTI1_1);
751 }
752 }
753 else
754 { // Don't respawn.
755 P_MobjChangeState(mo, S_DEADARTI1);
756 }
757 }
758
A_RestoreArtifact(mobj_t * mo)759 void C_DECL A_RestoreArtifact(mobj_t* mo)
760 {
761 mo->flags |= MF_SPECIAL;
762 P_MobjChangeState(mo, P_GetState(mo->type, SN_SPAWN));
763 S_StartSound(SFX_RESPAWN, mo);
764 }
765
766 /**
767 * Make a special thing visible again.
768 */
A_RestoreSpecialThing1(mobj_t * thing)769 void C_DECL A_RestoreSpecialThing1(mobj_t* thing)
770 {
771 thing->flags2 &= ~MF2_DONTDRAW;
772 S_StartSound(SFX_RESPAWN, thing);
773 }
774
A_RestoreSpecialThing2(mobj_t * thing)775 void C_DECL A_RestoreSpecialThing2(mobj_t* thing)
776 {
777 thing->flags |= MF_SPECIAL;
778 P_MobjChangeState(thing, P_GetState(thing->type, SN_SPAWN));
779 }
780
getItemTypeBySprite(spritetype_e sprite)781 static itemtype_t getItemTypeBySprite(spritetype_e sprite)
782 {
783 static const struct item_s {
784 itemtype_t type;
785 spritetype_e sprite;
786 } items[] = {
787 { IT_HEALTH_VIAL, SPR_PTN1 },
788 { IT_ARMOR_MESH, SPR_ARM1 },
789 { IT_ARMOR_SHIELD, SPR_ARM2 },
790 { IT_ARMOR_HELMET, SPR_ARM3 },
791 { IT_ARMOR_AMULET, SPR_ARM4 },
792 { IT_KEY_STEEL, SPR_KEY1 },
793 { IT_KEY_CAVE, SPR_KEY2 },
794 { IT_KEY_AXE, SPR_KEY3 },
795 { IT_KEY_FIRE, SPR_KEY4 },
796 { IT_KEY_EMERALD, SPR_KEY5 },
797 { IT_KEY_DUNGEON, SPR_KEY6 },
798 { IT_KEY_SILVER, SPR_KEY7 },
799 { IT_KEY_RUSTED, SPR_KEY8 },
800 { IT_KEY_HORN, SPR_KEY9 },
801 { IT_KEY_SWAMP, SPR_KEYA },
802 { IT_KEY_CASTLE, SPR_KEYB },
803 { IT_ITEM_QUARTZFLASK, SPR_PTN2 },
804 { IT_ITEM_WINGS, SPR_SOAR },
805 { IT_ITEM_DEFENDER, SPR_INVU },
806 { IT_ITEM_SERVANT, SPR_SUMN },
807 { IT_ITEM_PORKALATOR, SPR_PORK },
808 { IT_ITEM_MYSTICURN, SPR_SPHL },
809 { IT_ITEM_AMBITINCANT, SPR_HRAD },
810 { IT_ITEM_TORCH, SPR_TRCH },
811 { IT_ITEM_CHAOSDEVICE, SPR_ATLP },
812 { IT_ITEM_BANISHDEVICE, SPR_TELO },
813 { IT_ITEM_FLETCHETTE, SPR_PSBG },
814 { IT_ITEM_BOOTSOFSPEED, SPR_SPED },
815 { IT_ITEM_KRATEROFMIGHT, SPR_BMAN },
816 { IT_ITEM_BRACERS, SPR_BRAC },
817 { IT_ITEM_REPULSION, SPR_BLST },
818 { IT_PUZZLE_SKULL, SPR_ASKU },
819 { IT_PUZZLE_BIGGEM, SPR_ABGM },
820 { IT_PUZZLE_REDGEM, SPR_AGMR },
821 { IT_PUZZLE_GREENGEM1, SPR_AGMG },
822 { IT_PUZZLE_GREENGEM2, SPR_AGG2 },
823 { IT_PUZZLE_BLUEGEM1, SPR_AGMB },
824 { IT_PUZZLE_BLUEGEM2, SPR_AGB2 },
825 { IT_PUZZLE_BOOK1, SPR_ABK1 },
826 { IT_PUZZLE_BOOK2, SPR_ABK2 },
827 { IT_PUZZLE_SKULL2, SPR_ASK2 },
828 { IT_PUZZLE_FWEAPON, SPR_AFWP },
829 { IT_PUZZLE_CWEAPON, SPR_ACWP },
830 { IT_PUZZLE_MWEAPON, SPR_AMWP },
831 { IT_PUZZLE_GEAR1, SPR_AGER },
832 { IT_PUZZLE_GEAR2, SPR_AGR2 },
833 { IT_PUZZLE_GEAR3, SPR_AGR3 },
834 { IT_PUZZLE_GEAR4, SPR_AGR4 },
835 { IT_MANA_BLUE, SPR_MAN1 },
836 { IT_MANA_GREEN, SPR_MAN2 },
837 { IT_MANA_COMBINED, SPR_MAN3 },
838 { IT_WEAPON_FROSTSHARDS, SPR_WMCS },
839 { IT_WEAPON_ARCOFDEATH, SPR_WMLG },
840 { IT_WEAPON_AXE, SPR_WFAX },
841 { IT_WEAPON_HAMMER, SPR_WFHM },
842 { IT_WEAPON_SERPENTSTAFF, SPR_WCSS },
843 { IT_WEAPON_FIRESTORM, SPR_WCFM },
844 { IT_WEAPON_QUIETUS1, SPR_WFR1 },
845 { IT_WEAPON_QUIETUS2, SPR_WFR2 },
846 { IT_WEAPON_QUIETUS3, SPR_WFR3 },
847 { IT_WEAPON_WRAITHVERGE1, SPR_WCH1 },
848 { IT_WEAPON_WRAITHVERGE2, SPR_WCH2 },
849 { IT_WEAPON_WRAITHVERGE3, SPR_WCH3 },
850 { IT_WEAPON_BLOODSCOURGE1, SPR_WMS1 },
851 { IT_WEAPON_BLOODSCOURGE2, SPR_WMS2 },
852 { IT_WEAPON_BLOODSCOURGE3, SPR_WMS3 },
853 { IT_NONE, 0 }
854 };
855 uint i;
856
857 for(i = 0; items[i].type != IT_NONE; ++i)
858 if(items[i].sprite == sprite)
859 return items[i].type;
860
861 return IT_NONE;
862 }
863
pickupHealthVial(player_t * plr)864 static dd_bool pickupHealthVial(player_t *plr)
865 {
866 return P_GiveHealth(plr, 10);
867 }
868
pickupMesh(player_t * plr)869 static dd_bool pickupMesh(player_t *plr)
870 {
871 return P_GiveArmor(plr, ARMOR_ARMOR);
872 }
873
pickupShield(player_t * plr)874 static dd_bool pickupShield(player_t *plr)
875 {
876 return P_GiveArmor(plr, ARMOR_SHIELD);
877 }
878
pickupHelmet(player_t * plr)879 static dd_bool pickupHelmet(player_t *plr)
880 {
881 return P_GiveArmor(plr, ARMOR_HELMET);
882 }
883
pickupAmulet(player_t * plr)884 static dd_bool pickupAmulet(player_t *plr)
885 {
886 return P_GiveArmor(plr, ARMOR_AMULET);
887 }
888
pickupSteelKey(player_t * plr)889 static dd_bool pickupSteelKey(player_t *plr)
890 {
891 return P_GiveKey(plr, KT_KEY1);
892 }
893
pickupCaveKey(player_t * plr)894 static dd_bool pickupCaveKey(player_t *plr)
895 {
896 return P_GiveKey(plr, KT_KEY2);
897 }
898
pickupAxeKey(player_t * plr)899 static dd_bool pickupAxeKey(player_t *plr)
900 {
901 return P_GiveKey(plr, KT_KEY3);
902 }
903
pickupFireKey(player_t * plr)904 static dd_bool pickupFireKey(player_t *plr)
905 {
906 return P_GiveKey(plr, KT_KEY4);
907 }
908
pickupEmeraldKey(player_t * plr)909 static dd_bool pickupEmeraldKey(player_t *plr)
910 {
911 return P_GiveKey(plr, KT_KEY5);
912 }
913
pickupDungeonKey(player_t * plr)914 static dd_bool pickupDungeonKey(player_t *plr)
915 {
916 return P_GiveKey(plr, KT_KEY6);
917 }
918
pickupSilverKey(player_t * plr)919 static dd_bool pickupSilverKey(player_t *plr)
920 {
921 return P_GiveKey(plr, KT_KEY7);
922 }
923
pickupRustedKey(player_t * plr)924 static dd_bool pickupRustedKey(player_t *plr)
925 {
926 return P_GiveKey(plr, KT_KEY8);
927 }
928
pickupHornKey(player_t * plr)929 static dd_bool pickupHornKey(player_t *plr)
930 {
931 return P_GiveKey(plr, KT_KEY9);
932 }
933
pickupSwampKey(player_t * plr)934 static dd_bool pickupSwampKey(player_t *plr)
935 {
936 return P_GiveKey(plr, KT_KEYA);
937 }
938
pickupCastleKey(player_t * plr)939 static dd_bool pickupCastleKey(player_t *plr)
940 {
941 return P_GiveKey(plr, KT_KEYB);
942 }
943
pickupQuartzFlask(player_t * plr)944 static dd_bool pickupQuartzFlask(player_t *plr)
945 {
946 return P_GiveItem(plr, IIT_HEALTH);
947 }
948
pickupWings(player_t * plr)949 static dd_bool pickupWings(player_t *plr)
950 {
951 return P_GiveItem(plr, IIT_FLY);
952 }
953
pickupDefender(player_t * plr)954 static dd_bool pickupDefender(player_t *plr)
955 {
956 return P_GiveItem(plr, IIT_INVULNERABILITY);
957 }
958
pickupServant(player_t * plr)959 static dd_bool pickupServant(player_t *plr)
960 {
961 return P_GiveItem(plr, IIT_SUMMON);
962 }
963
pickupPorkalator(player_t * plr)964 static dd_bool pickupPorkalator(player_t *plr)
965 {
966 return P_GiveItem(plr, IIT_EGG);
967 }
968
pickupMysticUrn(player_t * plr)969 static dd_bool pickupMysticUrn(player_t *plr)
970 {
971 return P_GiveItem(plr, IIT_SUPERHEALTH);
972 }
973
pickupAmbitIncant(player_t * plr)974 static dd_bool pickupAmbitIncant(player_t *plr)
975 {
976 return P_GiveItem(plr, IIT_HEALINGRADIUS);
977 }
978
pickupTorch(player_t * plr)979 static dd_bool pickupTorch(player_t *plr)
980 {
981 return P_GiveItem(plr, IIT_TORCH);
982 }
983
pickupChaosDevice(player_t * plr)984 static dd_bool pickupChaosDevice(player_t *plr)
985 {
986 return P_GiveItem(plr, IIT_TELEPORT);
987 }
988
pickupBanishDevice(player_t * plr)989 static dd_bool pickupBanishDevice(player_t *plr)
990 {
991 return P_GiveItem(plr, IIT_TELEPORTOTHER);
992 }
993
pickupFletchette(player_t * plr)994 static dd_bool pickupFletchette(player_t *plr)
995 {
996 return P_GiveItem(plr, IIT_POISONBAG);
997 }
998
pickupBootsOfSpeed(player_t * plr)999 static dd_bool pickupBootsOfSpeed(player_t *plr)
1000 {
1001 return P_GiveItem(plr, IIT_SPEED);
1002 }
1003
pickupKraterOfMight(player_t * plr)1004 static dd_bool pickupKraterOfMight(player_t *plr)
1005 {
1006 return P_GiveItem(plr, IIT_BOOSTMANA);
1007 }
1008
pickupBracers(player_t * plr)1009 static dd_bool pickupBracers(player_t *plr)
1010 {
1011 return P_GiveItem(plr, IIT_BOOSTARMOR);
1012 }
1013
pickupRepulsion(player_t * plr)1014 static dd_bool pickupRepulsion(player_t *plr)
1015 {
1016 return P_GiveItem(plr, IIT_BLASTRADIUS);
1017 }
1018
pickupSkull(player_t * plr)1019 static dd_bool pickupSkull(player_t *plr)
1020 {
1021 return P_GiveItem(plr, IIT_PUZZSKULL);
1022 }
1023
pickupBigGem(player_t * plr)1024 static dd_bool pickupBigGem(player_t *plr)
1025 {
1026 return P_GiveItem(plr, IIT_PUZZGEMBIG);
1027 }
1028
pickupRedGem(player_t * plr)1029 static dd_bool pickupRedGem(player_t *plr)
1030 {
1031 return P_GiveItem(plr, IIT_PUZZGEMRED);
1032 }
1033
pickupGreenGem1(player_t * plr)1034 static dd_bool pickupGreenGem1(player_t *plr)
1035 {
1036 return P_GiveItem(plr, IIT_PUZZGEMGREEN1);
1037 }
1038
pickupGreenGem2(player_t * plr)1039 static dd_bool pickupGreenGem2(player_t *plr)
1040 {
1041 return P_GiveItem(plr, IIT_PUZZGEMGREEN2);
1042 }
1043
pickupBlueGem1(player_t * plr)1044 static dd_bool pickupBlueGem1(player_t *plr)
1045 {
1046 return P_GiveItem(plr, IIT_PUZZGEMBLUE1);
1047 }
1048
pickupBlueGem2(player_t * plr)1049 static dd_bool pickupBlueGem2(player_t *plr)
1050 {
1051 return P_GiveItem(plr, IIT_PUZZGEMBLUE2);
1052 }
1053
pickupBook1(player_t * plr)1054 static dd_bool pickupBook1(player_t *plr)
1055 {
1056 return P_GiveItem(plr, IIT_PUZZBOOK1);
1057 }
1058
pickupBook2(player_t * plr)1059 static dd_bool pickupBook2(player_t *plr)
1060 {
1061 return P_GiveItem(plr, IIT_PUZZBOOK2);
1062 }
1063
pickupSkull2(player_t * plr)1064 static dd_bool pickupSkull2(player_t *plr)
1065 {
1066 return P_GiveItem(plr, IIT_PUZZSKULL2);
1067 }
1068
pickupFWeapon(player_t * plr)1069 static dd_bool pickupFWeapon(player_t *plr)
1070 {
1071 return P_GiveItem(plr, IIT_PUZZFWEAPON);
1072 }
1073
pickupCWeapon(player_t * plr)1074 static dd_bool pickupCWeapon(player_t *plr)
1075 {
1076 return P_GiveItem(plr, IIT_PUZZCWEAPON);
1077 }
1078
pickupMWeapon(player_t * plr)1079 static dd_bool pickupMWeapon(player_t *plr)
1080 {
1081 return P_GiveItem(plr, IIT_PUZZMWEAPON);
1082 }
1083
pickupGear1(player_t * plr)1084 static dd_bool pickupGear1(player_t *plr)
1085 {
1086 return P_GiveItem(plr, IIT_PUZZGEAR1);
1087 }
1088
pickupGear2(player_t * plr)1089 static dd_bool pickupGear2(player_t *plr)
1090 {
1091 return P_GiveItem(plr, IIT_PUZZGEAR2);
1092 }
1093
pickupGear3(player_t * plr)1094 static dd_bool pickupGear3(player_t *plr)
1095 {
1096 return P_GiveItem(plr, IIT_PUZZGEAR3);
1097 }
1098
pickupGear4(player_t * plr)1099 static dd_bool pickupGear4(player_t *plr)
1100 {
1101 return P_GiveItem(plr, IIT_PUZZGEAR4);
1102 }
1103
pickupBlueMana(player_t * plr)1104 static dd_bool pickupBlueMana(player_t *plr)
1105 {
1106 return P_GiveAmmo(plr, AT_BLUEMANA, 15);
1107 }
1108
pickupGreenMana(player_t * plr)1109 static dd_bool pickupGreenMana(player_t *plr)
1110 {
1111 return P_GiveAmmo(plr, AT_GREENMANA, 15);
1112 }
1113
pickupCombinedMana(player_t * plr)1114 static dd_bool pickupCombinedMana(player_t *plr)
1115 {
1116 if(!P_GiveAmmo(plr, AT_BLUEMANA, 20))
1117 {
1118 if(!P_GiveAmmo(plr, AT_GREENMANA, 20))
1119 return false;
1120 }
1121 else
1122 {
1123 P_GiveAmmo(plr, AT_GREENMANA, 20);
1124 }
1125
1126 return true;
1127 }
1128
pickupWeapon(player_t * plr,weapontype_t weaponType,playerclass_t matchClass)1129 static dd_bool pickupWeapon(player_t *plr, weapontype_t weaponType,
1130 playerclass_t matchClass)
1131 {
1132 DENG_ASSERT(plr != 0);
1133 DENG_ASSERT(weaponType >= WT_FIRST && weaponType < NUM_WEAPON_TYPES);
1134
1135 // Depending on the game rules the player should ignore the weapon.
1136 if(plr->class_ != matchClass)
1137 {
1138 // Leave placed weapons forever on net games.
1139 if(IS_NETGAME && !gfw_Rule(deathmatch))
1140 return false;
1141 }
1142
1143 // Attempt the pickup.
1144 return P_GiveWeapon2(plr, weaponType, matchClass);
1145 }
1146
pickupFrostShards(player_t * plr)1147 static dd_bool pickupFrostShards(player_t *plr)
1148 {
1149 return pickupWeapon(plr, WT_SECOND, PCLASS_MAGE);
1150 }
1151
pickupArcOfDeath(player_t * plr)1152 static dd_bool pickupArcOfDeath(player_t *plr)
1153 {
1154 return pickupWeapon(plr, WT_THIRD, PCLASS_MAGE);
1155 }
1156
pickupAxe(player_t * plr)1157 static dd_bool pickupAxe(player_t *plr)
1158 {
1159 return pickupWeapon(plr, WT_SECOND, PCLASS_FIGHTER);
1160 }
1161
pickupHammer(player_t * plr)1162 static dd_bool pickupHammer(player_t *plr)
1163 {
1164 return pickupWeapon(plr, WT_THIRD, PCLASS_FIGHTER);
1165 }
1166
pickupSerpentStaff(player_t * plr)1167 static dd_bool pickupSerpentStaff(player_t *plr)
1168 {
1169 return pickupWeapon(plr, WT_SECOND, PCLASS_CLERIC);
1170 }
1171
pickupFireStorm(player_t * plr)1172 static dd_bool pickupFireStorm(player_t *plr)
1173 {
1174 return pickupWeapon(plr, WT_THIRD, PCLASS_CLERIC);
1175 }
1176
pickupQuietus1(player_t * plr)1177 static dd_bool pickupQuietus1(player_t *plr)
1178 {
1179 return P_GiveWeaponPiece2(plr, 0, PCLASS_FIGHTER);
1180 }
1181
pickupQuietus2(player_t * plr)1182 static dd_bool pickupQuietus2(player_t *plr)
1183 {
1184 return P_GiveWeaponPiece2(plr, 1, PCLASS_FIGHTER);
1185 }
1186
pickupQuietus3(player_t * plr)1187 static dd_bool pickupQuietus3(player_t *plr)
1188 {
1189 return P_GiveWeaponPiece2(plr, 2, PCLASS_FIGHTER);
1190 }
1191
pickupWraithVerge1(player_t * plr)1192 static dd_bool pickupWraithVerge1(player_t *plr)
1193 {
1194 return P_GiveWeaponPiece2(plr, 0, PCLASS_CLERIC);
1195 }
1196
pickupWraithVerge2(player_t * plr)1197 static dd_bool pickupWraithVerge2(player_t *plr)
1198 {
1199 return P_GiveWeaponPiece2(plr, 1, PCLASS_CLERIC);
1200 }
1201
pickupWraithVerge3(player_t * plr)1202 static dd_bool pickupWraithVerge3(player_t *plr)
1203 {
1204 return P_GiveWeaponPiece2(plr, 2, PCLASS_CLERIC);
1205 }
1206
pickupBloodScourge1(player_t * plr)1207 static dd_bool pickupBloodScourge1(player_t *plr)
1208 {
1209 return P_GiveWeaponPiece2(plr, 0, PCLASS_MAGE);
1210 }
1211
pickupBloodScourge2(player_t * plr)1212 static dd_bool pickupBloodScourge2(player_t *plr)
1213 {
1214 return P_GiveWeaponPiece2(plr, 1, PCLASS_MAGE);
1215 }
1216
pickupBloodScourge3(player_t * plr)1217 static dd_bool pickupBloodScourge3(player_t *plr)
1218 {
1219 return P_GiveWeaponPiece2(plr, 2, PCLASS_MAGE);
1220 }
1221
giveItem(player_t * plr,itemtype_t item)1222 static dd_bool giveItem(player_t *plr, itemtype_t item)
1223 {
1224 iteminfo_t const *info = &items[item];
1225 int oldPieces = plr->pieces;
1226
1227 if(!plr)
1228 return false;
1229
1230 // Attempt to pickup the item.
1231 if(!info->giveFunc(plr))
1232 return false; // Did not make use of it.
1233
1234 switch(item)
1235 {
1236 case IT_WEAPON_QUIETUS1:
1237 case IT_WEAPON_QUIETUS2:
1238 case IT_WEAPON_QUIETUS3:
1239 case IT_WEAPON_WRAITHVERGE1:
1240 case IT_WEAPON_WRAITHVERGE2:
1241 case IT_WEAPON_WRAITHVERGE3:
1242 case IT_WEAPON_BLOODSCOURGE1:
1243 case IT_WEAPON_BLOODSCOURGE2:
1244 case IT_WEAPON_BLOODSCOURGE3:
1245 if(plr->pieces != oldPieces &&
1246 plr->pieces == WEAPON_FOURTH_COMPLETE)
1247 {
1248 int msg;
1249
1250 switch(item)
1251 {
1252 default:
1253 Con_Error("Internal Error: Item type %i not handled in giveItem.", (int) item);
1254 break; // Unreachable.
1255
1256 case IT_WEAPON_QUIETUS1:
1257 case IT_WEAPON_QUIETUS2:
1258 case IT_WEAPON_QUIETUS3:
1259 msg = TXT_TXT_WEAPON_F4;
1260 break;
1261
1262 case IT_WEAPON_WRAITHVERGE1:
1263 case IT_WEAPON_WRAITHVERGE2:
1264 case IT_WEAPON_WRAITHVERGE3:
1265 msg = TXT_TXT_WEAPON_C4;
1266 break;
1267
1268 case IT_WEAPON_BLOODSCOURGE1:
1269 case IT_WEAPON_BLOODSCOURGE2:
1270 case IT_WEAPON_BLOODSCOURGE3:
1271 msg = TXT_TXT_WEAPON_M4;
1272 break;
1273 }
1274
1275 P_SetMessage(plr, GET_TXT(msg));
1276 // Play the build-sound full volume for all players.
1277 S_StartSound(SFX_WEAPON_BUILD, NULL);
1278 break;
1279 }
1280 // Fall-through:
1281
1282 default:
1283 S_StartSound(info->pickupSound, plr->plr->mo);
1284 P_SetMessage(plr, GET_TXT(info->pickupMsg));
1285 break;
1286 }
1287
1288 return true;
1289 }
1290
P_TouchSpecialMobj(mobj_t * special,mobj_t * toucher)1291 void P_TouchSpecialMobj(mobj_t* special, mobj_t* toucher)
1292 {
1293 player_t *player;
1294 coord_t delta;
1295 itemtype_t item;
1296 dd_bool wasUsed = false, removeItem = false;
1297
1298 if(IS_CLIENT) return;
1299
1300 delta = special->origin[VZ] - toucher->origin[VZ];
1301 if(delta > toucher->height || delta < -32)
1302 {
1303 // Out of reach.
1304 return;
1305 }
1306
1307 // Dead thing touching (can happen with a sliding player corpse).
1308 if(toucher->health <= 0) return;
1309
1310 player = toucher->player;
1311
1312 // Identify by sprite.
1313 if((item = getItemTypeBySprite(special->sprite)) != IT_NONE)
1314 {
1315 iteminfo_t const *info = &items[item];
1316
1317 if((wasUsed = giveItem(player, item)))
1318 {
1319 // Should we leave this item for others?
1320 if(!((info->flags & IIF_LEAVE_COOP) && IS_NETGAME && !gfw_Rule(deathmatch)) &&
1321 !((info->flags & IIF_LEAVE_DEATHMATCH) && IS_NETGAME && gfw_Rule(deathmatch)))
1322 removeItem = true;
1323 }
1324 }
1325 else
1326 {
1327 App_Log(DE2_MAP_WARNING, "P_TouchSpecialMobj: Unknown gettable thing %i.",
1328 (int) special->type);
1329 }
1330
1331 if(wasUsed && special->special)
1332 {
1333 P_ExecuteLineSpecial(special->special, special->args, NULL, 0,
1334 toucher);
1335 special->special = 0;
1336 }
1337
1338 if(removeItem)
1339 {
1340 player->bonusCount += BONUSADD;
1341
1342 /**
1343 * Taken items are handled differently depending upon the type of
1344 * item: inventory, puzzle or other.
1345 */
1346 switch(item)
1347 {
1348 // Inventory:
1349 case IT_ITEM_QUARTZFLASK:
1350 case IT_ITEM_WINGS:
1351 case IT_ITEM_DEFENDER:
1352 case IT_ITEM_SERVANT:
1353 case IT_ITEM_PORKALATOR:
1354 case IT_ITEM_MYSTICURN:
1355 case IT_ITEM_AMBITINCANT:
1356 case IT_ITEM_TORCH:
1357 case IT_ITEM_CHAOSDEVICE:
1358 case IT_ITEM_BANISHDEVICE:
1359 case IT_ITEM_FLETCHETTE:
1360 case IT_ITEM_BOOTSOFSPEED:
1361 case IT_ITEM_KRATEROFMIGHT:
1362 case IT_ITEM_BRACERS:
1363 case IT_ITEM_REPULSION:
1364 setDormantItem(special);
1365 break;
1366
1367 // Puzzle items:
1368 case IT_PUZZLE_SKULL:
1369 case IT_PUZZLE_BIGGEM:
1370 case IT_PUZZLE_REDGEM:
1371 case IT_PUZZLE_GREENGEM1:
1372 case IT_PUZZLE_GREENGEM2:
1373 case IT_PUZZLE_BLUEGEM1:
1374 case IT_PUZZLE_BLUEGEM2:
1375 case IT_PUZZLE_BOOK1:
1376 case IT_PUZZLE_BOOK2:
1377 case IT_PUZZLE_SKULL2:
1378 case IT_PUZZLE_FWEAPON:
1379 case IT_PUZZLE_CWEAPON:
1380 case IT_PUZZLE_MWEAPON:
1381 case IT_PUZZLE_GEAR1:
1382 case IT_PUZZLE_GEAR2:
1383 case IT_PUZZLE_GEAR3:
1384 case IT_PUZZLE_GEAR4:
1385 P_MobjRemove(special, false);
1386 break;
1387
1388 default:
1389 if(gfw_Rule(deathmatch) && !(special->flags2 & MF2_DROPPED))
1390 P_HideSpecialThing(special);
1391 else
1392 P_MobjRemove(special, false);
1393 break;
1394 }
1395 }
1396 }
1397
1398 typedef struct {
1399 player_t* master;
1400 mobj_t* foundMobj;
1401 } findactiveminotaurparams_t;
1402
findActiveMinotaur(thinker_t * th,void * context)1403 static int findActiveMinotaur(thinker_t* th, void* context)
1404 {
1405 findactiveminotaurparams_t* params =
1406 (findactiveminotaurparams_t*) context;
1407 mobj_t* mo = (mobj_t*) th;
1408
1409 if(mo->type != MT_MINOTAUR)
1410 return false; // Continue iteration.
1411 if(mo->health <= 0)
1412 return false; // Continue iteration.
1413 if(!(mo->flags & MF_COUNTKILL)) // For morphed minotaurs.
1414 return false; // Continue iteration.
1415 if(mo->flags & MF_CORPSE)
1416 return false; // Continue iteration.
1417
1418 if(mapTime - mo->argsUInt >= MAULATORTICS)
1419 return false; // Continue iteration.
1420
1421 if(mo->tracer->player == params->master)
1422 { // Found it!
1423 params->foundMobj = mo;
1424 return true; // Stop iteration.
1425 }
1426
1427 return false; // Continue iteration.
1428 }
1429
ActiveMinotaur(player_t * master)1430 mobj_t* ActiveMinotaur(player_t* master)
1431 {
1432 findactiveminotaurparams_t params;
1433
1434 params.master = master;
1435 params.foundMobj = NULL;
1436
1437 if(Thinker_Iterate(P_MobjThinker, findActiveMinotaur, ¶ms))
1438 return params.foundMobj;
1439
1440 return NULL;
1441 }
1442
P_KillMobj(mobj_t * source,mobj_t * target)1443 void P_KillMobj(mobj_t *source, mobj_t *target)
1444 {
1445 statenum_t state;
1446
1447 // Nothing to kill?
1448 if(!target) return;
1449
1450 target->flags &= ~(MF_SHOOTABLE | MF_FLOAT | MF_SKULLFLY | MF_NOGRAVITY);
1451 target->flags |= MF_CORPSE | MF_DROPOFF;
1452 target->flags2 &= ~MF2_PASSMOBJ;
1453 target->height /= 2 * 2;
1454
1455 if ((target->flags & MF_COUNTKILL || target->type == MT_ZBELL) && target->special)
1456 {
1457 // Initiate monster death actions.
1458 if(target->type == MT_SORCBOSS)
1459 {
1460 P_StartACScript(target->special, NULL, target, NULL, 0);
1461 }
1462 else
1463 {
1464 P_ExecuteLineSpecial(target->special, target->args, NULL, 0, target);
1465 }
1466 }
1467
1468 Mobj_RunScriptOnDeath(target, source);
1469
1470 if(source && source->player)
1471 {
1472 // Check for frag changes.
1473 if(target->player && gfw_Rule(deathmatch))
1474 {
1475 if(target == source)
1476 {
1477 // Self-frag.
1478 target->player->frags[target->player - players]--;
1479 NetSv_FragsForAll(target->player);
1480 }
1481 else
1482 {
1483 source->player->frags[target->player - players]++;
1484 NetSv_FragsForAll(source->player);
1485 }
1486 }
1487 }
1488
1489 if(target->player)
1490 {
1491 // Player death.
1492 if(!source)
1493 {
1494 // Self-frag
1495 target->player->frags[target->player - players]--;
1496 NetSv_FragsForAll(target->player);
1497 }
1498
1499 target->flags &= ~MF_SOLID;
1500 target->flags2 &= ~MF2_FLY;
1501 target->player->powers[PT_FLIGHT] = 0;
1502 target->player->playerState = PST_DEAD;
1503 target->player->rebornWait = PLAYER_REBORN_TICS;
1504 target->player->update |= PSF_STATE | PSF_POWERS;
1505
1506 // Let the engine know about this, too. The DEAD flag will be
1507 // cleared when the player is reborn.
1508 target->player->plr->flags |= DDPF_DEAD;
1509 P_DropWeapon(target->player);
1510 if(target->flags2 & MF2_FIREDAMAGE)
1511 {
1512 // Player flame death.
1513 /// @todo Should be pulled from the player class definition.
1514 switch(target->player->class_)
1515 {
1516 case PCLASS_FIGHTER:
1517 S_StartSound(SFX_PLAYER_FIGHTER_BURN_DEATH, target);
1518 P_MobjChangeState(target, S_PLAY_F_FDTH1);
1519 return;
1520
1521 case PCLASS_CLERIC:
1522 S_StartSound(SFX_PLAYER_CLERIC_BURN_DEATH, target);
1523 P_MobjChangeState(target, S_PLAY_C_FDTH1);
1524 return;
1525
1526 case PCLASS_MAGE:
1527 S_StartSound(SFX_PLAYER_MAGE_BURN_DEATH, target);
1528 P_MobjChangeState(target, S_PLAY_M_FDTH1);
1529 return;
1530
1531 default:
1532 break;
1533 }
1534 }
1535
1536 if(target->flags2 & MF2_ICEDAMAGE)
1537 {
1538 // Player ice death.
1539 target->flags &= ~MF_TRANSLATION; // no translation
1540 target->flags |= MF_ICECORPSE;
1541 /// @todo Should be pulled from the player class definition.
1542 switch(target->player->class_)
1543 {
1544 case PCLASS_FIGHTER:
1545 P_MobjChangeState(target, S_FPLAY_ICE);
1546 return;
1547
1548 case PCLASS_CLERIC:
1549 P_MobjChangeState(target, S_CPLAY_ICE);
1550 return;
1551
1552 case PCLASS_MAGE:
1553 P_MobjChangeState(target, S_MPLAY_ICE);
1554 return;
1555
1556 case PCLASS_PIG:
1557 P_MobjChangeState(target, S_PIG_ICE);
1558 return;
1559
1560 default:
1561 break;
1562 }
1563 }
1564
1565 // Don't die with the automap open.
1566 ST_CloseAll(target->player - players, false);
1567 }
1568 else
1569 { // Target is some monster or an object.
1570
1571 /**
1572 * mobj death, record as player's kill in netgame + coop could not
1573 * find MF_ targets->flags that indicated *only* enemies (not trees,
1574 * pots, etc), so built a list.
1575 *
1576 * @todo This should be a Thing definition flag.
1577 */
1578 if(IS_NETGAME && !gfw_Rule(deathmatch) && source && source->player &&
1579 source->player->plr && (target->type == MT_CENTAUR ||
1580 target->type == MT_CENTAURLEADER ||
1581 target->type == MT_DEMON ||
1582 target->type == MT_DEMON2 ||
1583 target->type == MT_ETTIN ||
1584 target->type == MT_PIG ||
1585 target->type == MT_FIREDEMON ||
1586 target->type == MT_SERPENT ||
1587 target->type == MT_SERPENTLEADER ||
1588 target->type == MT_WRAITH ||
1589 target->type == MT_WRAITHB ||
1590 target->type == MT_BISHOP ||
1591 target->type == MT_ICEGUY ||
1592 target->type == MT_FIGHTER_BOSS ||
1593 target->type == MT_CLERIC_BOSS ||
1594 target->type == MT_MAGE_BOSS ||
1595 target->type == MT_MINOTAUR))
1596 {
1597 source->player->frags[0]++;
1598 }
1599 }
1600
1601 if(target->flags2 & MF2_FIREDAMAGE)
1602 {
1603 if(target->type == MT_FIGHTER_BOSS || target->type == MT_CLERIC_BOSS ||
1604 target->type == MT_MAGE_BOSS)
1605 {
1606 switch(target->type)
1607 {
1608 case MT_FIGHTER_BOSS:
1609 S_StartSound(SFX_PLAYER_FIGHTER_BURN_DEATH, target);
1610 P_MobjChangeState(target, S_PLAY_F_FDTH1);
1611 return;
1612
1613 case MT_CLERIC_BOSS:
1614 S_StartSound(SFX_PLAYER_CLERIC_BURN_DEATH, target);
1615 P_MobjChangeState(target, S_PLAY_C_FDTH1);
1616 return;
1617
1618 case MT_MAGE_BOSS:
1619 S_StartSound(SFX_PLAYER_MAGE_BURN_DEATH, target);
1620 P_MobjChangeState(target, S_PLAY_M_FDTH1);
1621 return;
1622
1623 default:
1624 break;
1625 }
1626 }
1627 else if(target->type == MT_TREEDESTRUCTIBLE)
1628 {
1629 P_MobjChangeState(target, S_ZTREEDES_X1);
1630 target->height = 24;
1631 S_StartSound(SFX_TREE_EXPLODE, target);
1632 return;
1633 }
1634 }
1635
1636 if(target->flags2 & MF2_ICEDAMAGE)
1637 {
1638 target->flags |= MF_ICECORPSE;
1639 switch(target->type)
1640 {
1641 case MT_BISHOP:
1642 P_MobjChangeState(target, S_BISHOP_ICE);
1643 return;
1644
1645 case MT_CENTAUR:
1646 case MT_CENTAURLEADER:
1647 P_MobjChangeState(target, S_CENTAUR_ICE);
1648 return;
1649
1650 case MT_DEMON:
1651 case MT_DEMON2:
1652 P_MobjChangeState(target, S_DEMON_ICE);
1653 return;
1654
1655 case MT_SERPENT:
1656 case MT_SERPENTLEADER:
1657 P_MobjChangeState(target, S_SERPENT_ICE);
1658 return;
1659
1660 case MT_WRAITH:
1661 case MT_WRAITHB:
1662 P_MobjChangeState(target, S_WRAITH_ICE);
1663 return;
1664
1665 case MT_ETTIN:
1666 P_MobjChangeState(target, S_ETTIN_ICE1);
1667 return;
1668
1669 case MT_FIREDEMON:
1670 P_MobjChangeState(target, S_FIRED_ICE1);
1671 return;
1672
1673 case MT_FIGHTER_BOSS:
1674 P_MobjChangeState(target, S_FIGHTER_ICE);
1675 return;
1676
1677 case MT_CLERIC_BOSS:
1678 P_MobjChangeState(target, S_CLERIC_ICE);
1679 return;
1680
1681 case MT_MAGE_BOSS:
1682 P_MobjChangeState(target, S_MAGE_ICE);
1683 return;
1684
1685 case MT_PIG:
1686 P_MobjChangeState(target, S_PIG_ICE);
1687 return;
1688
1689 default:
1690 target->flags &= ~MF_ICECORPSE;
1691 break;
1692 }
1693 }
1694
1695 if(target->type == MT_MINOTAUR)
1696 {
1697 mobj_t *master = target->tracer;
1698 if(master && master->health > 0)
1699 {
1700 if(!ActiveMinotaur(master->player))
1701 {
1702 master->player->powers[PT_MINOTAUR] = 0;
1703 }
1704 }
1705 }
1706 else if(target->type == MT_TREEDESTRUCTIBLE)
1707 {
1708 target->height = 24;
1709 }
1710
1711 if((state = P_GetState(target->type, SN_XDEATH)) != S_NULL &&
1712 target->health < -(target->info->spawnHealth / 2))
1713 {
1714 // Extreme death.
1715 P_MobjChangeState(target, state);
1716 }
1717 else
1718 {
1719 // Normal death.
1720 if((state = P_GetState(target->type, SN_XDEATH)) != S_NULL &&
1721 target->type == MT_FIREDEMON &&
1722 target->origin[VZ] <= target->floorZ + 2)
1723 {
1724 // This is to fix the imps' staying in fall state.
1725 P_MobjChangeState(target, state);
1726 }
1727 else
1728 {
1729 P_MobjChangeState(target, P_GetState(target->type, SN_DEATH));
1730 }
1731 }
1732
1733 target->tics -= P_Random() & 3;
1734 }
1735
1736 /**
1737 * @return @c true, if the player gets turned into a pig.
1738 */
P_MorphPlayer(player_t * player)1739 dd_bool P_MorphPlayer(player_t* player)
1740 {
1741 mobj_t* pmo, *fog, *beastMo;
1742 coord_t pos[3];
1743 angle_t angle;
1744 int oldFlags2;
1745
1746 if(player->powers[PT_INVULNERABILITY])
1747 return false; // Immune when invulnerable.
1748
1749 if(player->morphTics)
1750 return false; // Player is already morphed.
1751
1752 pmo = player->plr->mo;
1753
1754 pos[VX] = pmo->origin[VX];
1755 pos[VY] = pmo->origin[VY];
1756 pos[VZ] = pmo->origin[VZ];
1757 angle = pmo->angle;
1758 oldFlags2 = pmo->flags2;
1759
1760 if(!(beastMo = P_SpawnMobj(MT_PIGPLAYER, pos, angle, 0)))
1761 return false;
1762
1763 P_MobjChangeState(pmo, S_FREETARGMOBJ);
1764
1765 if((fog = P_SpawnMobjXYZ(MT_TFOG, pos[VX], pos[VY],
1766 pos[VZ] + TELEFOGHEIGHT, angle + ANG180, 0)))
1767 S_StartSound(SFX_TELEPORT, fog);
1768
1769 beastMo->special1 = player->readyWeapon;
1770 beastMo->player = player;
1771 beastMo->dPlayer = player->plr;
1772
1773 player->health = beastMo->health = MAXMORPHHEALTH;
1774 player->plr->mo = beastMo;
1775 memset(&player->armorPoints[0], 0, NUMARMOR * sizeof(int));
1776 player->class_ = PCLASS_PIG;
1777
1778 if(oldFlags2 & MF2_FLY)
1779 beastMo->flags2 |= MF2_FLY;
1780
1781 player->morphTics = MORPHTICS;
1782 player->update |= PSF_MORPH_TIME | PSF_HEALTH;
1783 player->plr->flags |= DDPF_FIXORIGIN | DDPF_FIXMOM;
1784 P_ActivateMorphWeapon(player);
1785 return true;
1786 }
1787
P_MorphMonster(mobj_t * actor)1788 dd_bool P_MorphMonster(mobj_t *actor)
1789 {
1790 mobj_t * master, *monster, *fog;
1791 mobjtype_t moType;
1792 coord_t pos[3];
1793 mobj_t oldMonster;
1794 angle_t oldAngle;
1795
1796 if (actor->player) return (false);
1797 if (!(actor->flags & MF_COUNTKILL)) return false;
1798 if (actor->flags2 & MF2_BOSS) return false;
1799
1800 // Originally hardcoded to specific mobj types.
1801 if (actor->flags3 & MF3_NOMORPH) return false;
1802
1803 moType = actor->type;
1804
1805 /// @todo Do this properly!
1806 oldMonster = *actor;
1807
1808 pos[VX] = actor->origin[VX];
1809 pos[VY] = actor->origin[VY];
1810 pos[VZ] = actor->origin[VZ];
1811 oldAngle = actor->angle;
1812
1813 if (!(monster = P_SpawnMobj(MT_PIG, pos, oldMonster.angle, 0))) return false;
1814
1815 P_MobjRemoveFromTIDList(actor);
1816 P_MobjChangeState(actor, S_FREETARGMOBJ);
1817
1818 if ((fog = P_SpawnMobjXYZ(
1819 MT_TFOG, pos[VX], pos[VY], pos[VZ] + TELEFOGHEIGHT, oldAngle + ANG180, 0)))
1820 S_StartSound(SFX_TELEPORT, fog);
1821
1822 monster->special2 = moType;
1823 monster->special1 = MORPHTICS + P_Random();
1824 monster->flags |= (oldMonster.flags & MF_SHADOW);
1825 monster->target = oldMonster.target;
1826 monster->tid = oldMonster.tid;
1827 monster->special = oldMonster.special;
1828 P_MobjInsertIntoTIDList(monster, oldMonster.tid);
1829 memcpy(monster->args, oldMonster.args, 5);
1830
1831 // Check for turning off minotaur power for active icon.
1832 if (moType == MT_MINOTAUR)
1833 {
1834 master = oldMonster.tracer;
1835 if (master && master->health > 0)
1836 {
1837 if (!ActiveMinotaur(master->player))
1838 {
1839 master->player->powers[PT_MINOTAUR] = 0;
1840 }
1841 }
1842 }
1843
1844 return true;
1845 }
1846
P_AutoUseHealth(player_t * player,int saveHealth)1847 void P_AutoUseHealth(player_t* player, int saveHealth)
1848 {
1849 uint i, count;
1850 int plrnum = player - players;
1851 int normalCount = P_InventoryCount(plrnum, IIT_HEALTH);
1852 int superCount = P_InventoryCount(plrnum, IIT_SUPERHEALTH);
1853
1854 if(!player->plr->mo) return;
1855
1856 /// @todo Do this in the inventory code?
1857 if(gfw_Rule(skill) == SM_BABY && normalCount * 25 >= saveHealth)
1858 {
1859 // Use quartz flasks.
1860 count = (saveHealth + 24) / 25;
1861 for(i = 0; i < count; ++i)
1862 {
1863 player->health += 25;
1864 P_InventoryTake(plrnum, IIT_HEALTH, false);
1865 }
1866 }
1867 else if(superCount * 100 >= saveHealth)
1868 {
1869 // Use mystic urns.
1870 count = (saveHealth + 99) / 100;
1871 for(i = 0; i < count; ++i)
1872 {
1873 player->health += 100;
1874 P_InventoryTake(plrnum, IIT_SUPERHEALTH, false);
1875 }
1876 }
1877 else if(gfw_Rule(skill) == SM_BABY &&
1878 superCount * 100 + normalCount * 25 >= saveHealth)
1879 {
1880 // Use mystic urns and quartz flasks.
1881 count = (saveHealth + 24) / 25;
1882 saveHealth -= count * 25;
1883 for(i = 0; i < count; ++i)
1884 {
1885 player->health += 25;
1886 P_InventoryTake(plrnum, IIT_HEALTH, false);
1887 }
1888
1889 count = (saveHealth + 99) / 100;
1890 for(i = 0; i < count; ++i)
1891 {
1892 player->health += 100;
1893 P_InventoryTake(plrnum, IIT_SUPERHEALTH, false);
1894 }
1895 }
1896
1897 player->plr->mo->health = player->health;
1898 }
1899
1900 /**
1901 * Sets up all data concerning poisoning.
1902 */
P_PoisonPlayer(player_t * player,mobj_t * poisoner,int poison)1903 void P_PoisonPlayer(player_t *player, mobj_t *poisoner, int poison)
1904 {
1905 if((P_GetPlayerCheats(player) & CF_GODMODE) ||
1906 player->powers[PT_INVULNERABILITY])
1907 return;
1908
1909 player->poisonCount += poison;
1910 player->poisoner = poisoner;
1911
1912 if(player->poisonCount > 100)
1913 player->poisonCount = 100;
1914 }
1915
P_DamageMobj(mobj_t * target,mobj_t * inflictor,mobj_t * source,int damageP,dd_bool stomping)1916 int P_DamageMobj(mobj_t* target, mobj_t* inflictor, mobj_t* source,
1917 int damageP, dd_bool stomping)
1918 {
1919 return P_DamageMobj2(target, inflictor, source, damageP, stomping, false);
1920 }
1921
1922 /**
1923 * Damages both enemies and players
1924 * @note 'source' and 'inflictor' are the same for melee attacks.
1925 * 'source' can be NULL for slime, barrel explosions and other environmental
1926 * stuff.
1927 *
1928 * @param inflictor Is the mobj that caused the damage, creature or
1929 * missile, can be @c NULL (slime, etc).
1930 * @param source Is the mobj to target after taking damage
1931 * creature or @c NULL.
1932 */
P_DamageMobj2(mobj_t * target,mobj_t * inflictor,mobj_t * source,int damageP,dd_bool stomping,dd_bool skipNetworkCheck)1933 int P_DamageMobj2(mobj_t *target, mobj_t *inflictor, mobj_t *source, int damageP,
1934 dd_bool stomping, dd_bool skipNetworkCheck)
1935 {
1936 uint an;
1937 angle_t angle;
1938 int i, /*temp,*/ originalHealth;
1939 coord_t thrust;
1940 float saved, savedPercent;
1941 player_t *player;
1942 mobj_t *master;
1943 int damage;
1944
1945 if(!target) return 0; // Wha?
1946
1947 originalHealth = target->health;
1948
1949 // The actual damage (== damageP * netMobDamageModifier for any
1950 // non-player mobj).
1951 damage = damageP;
1952
1953 if(!skipNetworkCheck)
1954 {
1955 if(IS_NETGAME && !stomping && D_NetDamageMobj(target, inflictor, source, damage))
1956 { // We're done here.
1957 return 0;
1958 }
1959 // Clients can't harm anybody.
1960 if(IS_CLIENT)
1961 return 0;
1962 }
1963
1964 if(!(target->flags & MF_SHOOTABLE))
1965 return 0; // Shouldn't happen.
1966
1967 if(target->health <= 0)
1968 {
1969 if(!(inflictor && (inflictor->flags2 & MF2_ICEDAMAGE)) &&
1970 (target->flags & MF_ICECORPSE))
1971 { // Frozen.
1972 target->tics = 1;
1973 target->mom[MX] = target->mom[MY] = 0;
1974 }
1975
1976 return 0;
1977 }
1978
1979 if((target->flags2 & MF2_INVULNERABLE) && damage < 10000)
1980 { // mobj is invulnerable.
1981 if(target->player)
1982 return 0; // for player, no exceptions.
1983
1984 if(!inflictor)
1985 return 0;
1986
1987 switch(inflictor->type)
1988 {
1989 // These inflictors aren't foiled by invulnerability.
1990 case MT_HOLY_FX:
1991 case MT_POISONCLOUD:
1992 case MT_FIREBOMB:
1993 break;
1994
1995 default:
1996 return 0;
1997 }
1998 }
1999
2000 if(target->player) // Player specific.
2001 {
2002 if(damage < 1000 &&
2003 ((P_GetPlayerCheats(target->player) & CF_GODMODE) ||
2004 target->player->powers[PT_INVULNERABILITY]))
2005 {
2006 return 0;
2007 }
2008
2009 // Check if player-player damage is disabled.
2010 if(source && source->player && source->player != target->player)
2011 {
2012 #if 0
2013 // Co-op damage disabled?
2014 if(IS_NETGAME && !deathmatch && cfg.noCoopDamage)
2015 return 0;
2016
2017 // Same color, no damage?
2018 if(cfg.noTeamDamage &&
2019 cfg.playerColor[target->player - players] ==
2020 cfg.playerColor[source->player - players])
2021 return 0;
2022 #endif
2023 }
2024 }
2025
2026 if(target->flags & MF_SKULLFLY)
2027 {
2028 target->mom[MX] = target->mom[MY] = target->mom[MZ] = 0;
2029 }
2030
2031 if(target->flags2 & MF2_DORMANT)
2032 return 0; // Invulnerable, and won't wake up.
2033
2034 player = target->player;
2035 if(player && gfw_Rule(skill) == SM_BABY)
2036 damage /= 2; // Take half damage in trainer mode.
2037
2038 // Use the cvar damage multiplier netMobDamageModifier only if the
2039 // inflictor is not a player.
2040 if(inflictor && !inflictor->player &&
2041 (!source || (source && !source->player)))
2042 {
2043 // damage = (int) ((float) damage * netMobDamageModifier);
2044 if(IS_NETGAME)
2045 damage *= cfg.common.netMobDamageModifier;
2046 }
2047
2048 // Special damage types.
2049 if(inflictor)
2050 {
2051 switch(inflictor->type)
2052 {
2053 case MT_EGGFX:
2054 if(player)
2055 {
2056 P_MorphPlayer(player);
2057 }
2058 else
2059 {
2060 P_MorphMonster(target);
2061 }
2062 return 0; // Does no actual "damage" but health IS modified.
2063
2064 case MT_TELOTHER_FX1:
2065 case MT_TELOTHER_FX2:
2066 case MT_TELOTHER_FX3:
2067 case MT_TELOTHER_FX4:
2068 case MT_TELOTHER_FX5:
2069 if((target->flags & MF_COUNTKILL) && (target->type != MT_SERPENT)
2070 && (target->type != MT_SERPENTLEADER) &&
2071 (!(target->flags2 & MF2_BOSS)))
2072 {
2073 if(target->player)
2074 {
2075 if(gfw_Rule(deathmatch))
2076 P_TeleportToDeathmatchStarts(target);
2077 else
2078 P_TeleportToPlayerStarts(target);
2079 }
2080 else
2081 {
2082 // If death action, run it upon teleport.
2083 if(target->flags & MF_COUNTKILL && target->special)
2084 {
2085 P_MobjRemoveFromTIDList(target);
2086 P_ExecuteLineSpecial(target->special, target->args, NULL, 0,
2087 target);
2088 target->special = 0;
2089 }
2090
2091 // Send all monsters to deathmatch spots.
2092 P_TeleportToDeathmatchStarts(target);
2093 }
2094 }
2095 return 0;
2096
2097 case MT_MINOTAUR:
2098 if(inflictor->flags & MF_SKULLFLY)
2099 {
2100 // Slam only when in charge mode.
2101 uint an;
2102 int damageDone;
2103 angle_t angle;
2104 coord_t thrust;
2105
2106 angle = M_PointToAngle2(inflictor->origin, target->origin);
2107
2108 an = angle >> ANGLETOFINESHIFT;
2109 thrust = 16 + FIX2FLT(P_Random() << 10);
2110 target->mom[MX] += thrust * FIX2FLT(finecosine[an]);
2111 target->mom[MY] += thrust * FIX2FLT(finesine[an]);
2112 damageDone = P_DamageMobj(target, NULL, inflictor, HITDICE(4), false);
2113 if(target->player)
2114 {
2115 target->reactionTime = 14 + (P_Random() & 7);
2116 }
2117
2118 inflictor->args[0] = 0; // Stop charging.
2119 return damageDone;
2120 }
2121 break;
2122
2123 case MT_BISH_FX:
2124 // Bishops are just too nasty.
2125 damage /= 2;
2126 break;
2127
2128 case MT_SHARDFX1:
2129 switch(inflictor->special2)
2130 {
2131 case 3:
2132 damage *= 8;
2133 break;
2134
2135 case 2:
2136 damage *= 4;
2137 break;
2138
2139 case 1:
2140 damage *= 2;
2141 break;
2142
2143 default:
2144 break;
2145 }
2146 break;
2147
2148 case MT_CSTAFF_MISSILE:
2149 // Cleric Serpent Staff does poison damage.
2150 if(target->player)
2151 {
2152 P_PoisonPlayer(target->player, source, 20);
2153 damage /= 2;
2154 }
2155 break;
2156
2157 case MT_ICEGUY_FX2:
2158 damage /= 2;
2159 break;
2160
2161 case MT_POISONDART:
2162 if(target->player)
2163 {
2164 P_PoisonPlayer(target->player, source, 20);
2165 damage /= 2;
2166 }
2167 break;
2168
2169 case MT_POISONCLOUD:
2170 if(target->player)
2171 {
2172 int damageDone = 0;
2173 if(target->player->poisonCount < 4)
2174 {
2175 damageDone = P_PoisonDamage(target->player, source, 15 + (P_Random() & 15), false); // Don't play painsound
2176 P_PoisonPlayer(target->player, source, 50);
2177 S_StartSound(SFX_PLAYER_POISONCOUGH, target);
2178 }
2179 return damageDone;
2180 }
2181 else if(!(target->flags & MF_COUNTKILL))
2182 {
2183 // Only damage monsters/players with the poison cloud.
2184 return 0;
2185 }
2186 break;
2187
2188 case MT_FSWORD_MISSILE:
2189 if(target->player)
2190 {
2191 damage -= damage / 4;
2192 }
2193 break;
2194
2195 default:
2196 break;
2197 }
2198 }
2199
2200 // Some close combat weapons should not inflict thrust and push the
2201 // victim out of reach, thus kick away unless using a melee weapon.
2202 if(inflictor && (!source || !source->player) &&
2203 !(inflictor->flags2 & MF2_NODMGTHRUST))
2204 {
2205 angle = M_PointToAngle2(inflictor->origin, target->origin);
2206
2207 if (target->info->mass)
2208 {
2209 thrust = FIX2FLT(damage * (FRACUNIT>>3) * 100 / target->info->mass);
2210 }
2211 else
2212 {
2213 thrust = 0;
2214 }
2215
2216 // Make fall forwards sometimes.
2217 if((damage < 40) && (damage > target->health) &&
2218 (target->origin[VZ] - inflictor->origin[VZ] > 64) && (P_Random() & 1))
2219 {
2220 angle += ANG180;
2221 thrust *= 4;
2222 }
2223
2224 an = angle >>ANGLETOFINESHIFT;
2225 target->mom[MX] += thrust * FIX2FLT(finecosine[an]);
2226 target->mom[MY] += thrust * FIX2FLT(finesine[an]);
2227 NetSv_PlayerMobjImpulse(target, thrust * FIX2FLT(finecosine[an]), thrust * FIX2FLT(finesine[an]), 0);
2228 }
2229
2230 // Player specific.
2231 if(player)
2232 {
2233 target->player->update |= PSF_HEALTH;
2234
2235 /*if(damage < 1000 &&
2236 ((P_GetPlayerCheats(target->player) & CF_GODMODE) ||
2237 target->player->powers[PT_INVULNERABILITY]))
2238 {
2239 return 0;
2240 }*/
2241
2242 savedPercent = FIX2FLT(
2243 PCLASS_INFO(player->class_)->autoArmorSave + player->armorPoints[ARMOR_ARMOR] +
2244 player->armorPoints[ARMOR_SHIELD] +
2245 player->armorPoints[ARMOR_HELMET] +
2246 player->armorPoints[ARMOR_AMULET]);
2247 if(savedPercent)
2248 { // Armor absorbed some damage.
2249 if(savedPercent > 100)
2250 {
2251 savedPercent = 100;
2252 }
2253
2254 for(i = 0; i < NUMARMOR; ++i)
2255 {
2256 if(player->armorPoints[i])
2257 {
2258 player->armorPoints[i] -=
2259 FLT2FIX(((float) damage * FIX2FLT(PCLASS_INFO(player->class_)->armorIncrement[i])) /
2260 300);
2261
2262 if(player->armorPoints[i] < 2 * FRACUNIT)
2263 {
2264 player->armorPoints[i] = 0;
2265 }
2266 }
2267 }
2268 saved = ((float) damage * savedPercent) / 100;
2269
2270 if(saved > savedPercent * 2)
2271 saved = savedPercent * 2;
2272
2273 damage -= (int) saved;
2274 }
2275
2276 if(damage >= player->health &&
2277 ((gfw_Rule(skill) == SM_BABY) || gfw_Rule(deathmatch)) &&
2278 !player->morphTics)
2279 {
2280 // Try to use some inventory health.
2281 P_AutoUseHealth(player, damage - player->health + 1);
2282 }
2283
2284 player->health -= damage;
2285
2286 if(player->health < 0)
2287 player->health = 0;
2288
2289 player->attacker = source;
2290 player->damageCount += damage; // Add damage after armor / invuln.
2291
2292 if(player->damageCount > 100)
2293 player->damageCount = 100; // Teleport stomp does 10k points...
2294
2295 // temp = (damage < 100 ? damage : 100); Unused?
2296
2297 // Maybe unhide the HUD?
2298 ST_HUDUnHide(player - players, HUE_ON_DAMAGE);
2299
2300 R_UpdateViewFilter(player - players);
2301 }
2302
2303 Mobj_InflictDamage(target, inflictor, damage);
2304
2305 if(target->health > 0)
2306 { // Still alive, phew!
2307 if((P_Random() < target->info->painChance) &&
2308 !(target->flags & MF_SKULLFLY))
2309 {
2310 if(inflictor &&
2311 (inflictor->type >= MT_LIGHTNING_FLOOR &&
2312 inflictor->type <= MT_LIGHTNING_ZAP))
2313 {
2314 if(P_Random() < 96)
2315 {
2316 statenum_t state;
2317
2318 target->flags |= MF_JUSTHIT; // fight back!
2319
2320 if((state = P_GetState(target->type, SN_PAIN)) != S_NULL)
2321 P_MobjChangeState(target, state);
2322 }
2323 else
2324 { // "electrocute" the target.
2325 //// @todo make fullbright for this frame -->
2326 //target->frame |= FF_FULLBRIGHT;
2327 // <-- fixme
2328 if((target->flags & MF_COUNTKILL) && P_Random() < 128 &&
2329 !S_IsPlaying(SFX_PUPPYBEAT, target))
2330 {
2331 if((target->type == MT_CENTAUR) ||
2332 (target->type == MT_CENTAURLEADER) ||
2333 (target->type == MT_ETTIN))
2334 {
2335 S_StartSound(SFX_PUPPYBEAT, target);
2336 }
2337 }
2338 }
2339 }
2340 else
2341 {
2342 statenum_t state;
2343
2344 target->flags |= MF_JUSTHIT; // fight back!
2345
2346 if((state = P_GetState(target->type, SN_PAIN)) != S_NULL)
2347 P_MobjChangeState(target, state);
2348
2349 if(inflictor && inflictor->type == MT_POISONCLOUD)
2350 {
2351 if(target->flags & MF_COUNTKILL && P_Random() < 128 &&
2352 !S_IsPlaying(SFX_PUPPYBEAT, target))
2353 {
2354 if((target->type == MT_CENTAUR) ||
2355 (target->type == MT_CENTAURLEADER) ||
2356 (target->type == MT_ETTIN))
2357 {
2358 S_StartSound(SFX_PUPPYBEAT, target);
2359 }
2360 }
2361 }
2362 }
2363 }
2364
2365 target->reactionTime = 0; // We're awake now...
2366
2367 if(!target->threshold && source && !(source->flags3 & MF3_NOINFIGHT) &&
2368 !(target->type == MT_BISHOP) && !(target->type == MT_MINOTAUR))
2369 {
2370 // Target is not intent on another, so make it chase source.
2371 if(!((target->type == MT_CENTAUR && source->type == MT_CENTAURLEADER) ||
2372 (target->type == MT_CENTAURLEADER && source->type == MT_CENTAUR)))
2373 {
2374 statenum_t state;
2375
2376 target->target = source;
2377 target->threshold = BASETHRESHOLD;
2378
2379 if((state = P_GetState(target->type, SN_SEE)) != S_NULL &&
2380 target->state == &STATES[P_GetState(target->type, SN_SPAWN)])
2381 {
2382 P_MobjChangeState(target, state);
2383 }
2384 }
2385 }
2386 }
2387 else
2388 { // Death.
2389 if(inflictor)
2390 { // Check for special fire damage or ice damage deaths.
2391 if(inflictor->flags2 & MF2_FIREDAMAGE)
2392 {
2393 if(player && !player->morphTics)
2394 { // Check for flame death.
2395 if(target->health > -50 && damage > 25)
2396 {
2397 target->flags2 |= MF2_FIREDAMAGE;
2398 }
2399 }
2400 else
2401 {
2402 target->flags2 |= MF2_FIREDAMAGE;
2403 }
2404 }
2405 else if(inflictor->flags2 & MF2_ICEDAMAGE)
2406 {
2407 target->flags2 |= MF2_ICEDAMAGE;
2408 }
2409 }
2410
2411 if(source && (source->type == MT_MINOTAUR))
2412 {
2413 // Minotaur's kills go to his master.
2414 master = source->tracer;
2415
2416 // Make sure still alive and not a pointer to fighter head.
2417 if(master && master->player && (master->player->plr->mo == master))
2418 {
2419 source = master;
2420 }
2421 }
2422
2423 if(source && (source->player) &&
2424 (source->player->readyWeapon == WT_FOURTH))
2425 {
2426 // Always extreme death from fourth weapon.
2427 target->health = -5000;
2428 }
2429
2430 P_KillMobj(source, target);
2431 }
2432
2433 return originalHealth - target->health;
2434 }
2435
P_FallingDamage(player_t * player)2436 int P_FallingDamage(player_t* player)
2437 {
2438 int damage;
2439 coord_t mom, dist;
2440
2441 mom = fabs(player->plr->mo->mom[MZ]);
2442 dist = mom * (16.0f / 23);
2443
2444 if(mom >= 63)
2445 { // Automatic death.
2446 return P_DamageMobj(player->plr->mo, NULL, NULL, 10000, false);
2447 }
2448
2449 damage = ((dist * dist) / 10) - 24;
2450 if(player->plr->mo->mom[MZ] > -39 && damage > player->plr->mo->health &&
2451 player->plr->mo->health != 1)
2452 { // No-death threshold.
2453 damage = player->plr->mo->health - 1;
2454 }
2455
2456 S_StartSound(SFX_PLAYER_LAND, player->plr->mo);
2457
2458 return P_DamageMobj(player->plr->mo, NULL, NULL, damage, false);
2459 }
2460
P_PoisonDamage(player_t * player,mobj_t * source,int damage,dd_bool playPainSound)2461 int P_PoisonDamage(player_t *player, mobj_t *source, int damage, dd_bool playPainSound)
2462 {
2463 int originalHealth;
2464 mobj_t *target, *inflictor;
2465
2466 target = player->plr->mo;
2467 originalHealth = target->health;
2468 inflictor = source;
2469
2470 if(target->health <= 0)
2471 return 0; // Already dead.
2472
2473 if((target->flags2 & MF2_INVULNERABLE) && damage < 10000)
2474 return 0; // mobj is invulnerable.
2475
2476 if(gfw_Rule(skill) == SM_BABY)
2477 {
2478 // Take half damage in trainer mode
2479 damage /= 2;
2480 }
2481
2482 if(damage < 1000 &&
2483 ((P_GetPlayerCheats(player) & CF_GODMODE) || player->powers[PT_INVULNERABILITY]))
2484 {
2485 return 0;
2486 }
2487
2488 if(damage >= player->health &&
2489 (gfw_Rule(skill) == SM_BABY || gfw_Rule(deathmatch)) &&
2490 !player->morphTics)
2491 {
2492 // Try to use some inventory health.
2493 P_AutoUseHealth(player, damage - player->health + 1);
2494 }
2495
2496 // Maybe unhide the HUD?
2497 ST_HUDUnHide(player - players, HUE_ON_DAMAGE);
2498
2499 player->health -= damage;
2500 if(player->health < 0)
2501 {
2502 player->health = 0;
2503 }
2504 player->attacker = source;
2505
2506 // Do the damage.
2507 target->health -= damage;
2508 if(target->health > 0)
2509 { // Still alive, phew!
2510 if(!(mapTime & 63) && playPainSound)
2511 {
2512 statenum_t state;
2513
2514 if((state = P_GetState(target->type, SN_PAIN)) != S_NULL)
2515 P_MobjChangeState(target, state);
2516 }
2517 }
2518 else
2519 { // Death
2520 target->special1 = damage;
2521 if(player && inflictor && !player->morphTics)
2522 { // Check for flame death.
2523 if((inflictor->flags2 & MF2_FIREDAMAGE) &&
2524 (target->health > -50) && (damage > 25))
2525 {
2526 target->flags2 |= MF2_FIREDAMAGE;
2527 }
2528
2529 if(inflictor->flags2 & MF2_ICEDAMAGE)
2530 {
2531 target->flags2 |= MF2_ICEDAMAGE;
2532 }
2533 }
2534
2535 P_KillMobj(source, target);
2536 }
2537
2538 return originalHealth - target->health;
2539 }
2540