1 //-------------------------------------------------------------------------
2 /*
3 Copyright (C) 2010-2019 EDuke32 developers and contributors
4 Copyright (C) 2019 Nuke.YKT
5 
6 This file is part of NBlood.
7 
8 NBlood is free software; you can redistribute it and/or
9 modify it under the terms of the GNU General Public License version 2
10 as published by the Free Software Foundation.
11 
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
15 
16 See the GNU General Public License for more details.
17 
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
21 */
22 //-------------------------------------------------------------------------
23 #include <stdlib.h>
24 #include <string.h>
25 #include "compat.h"
26 #include "build.h"
27 #include "mmulti.h"
28 #include "actor.h"
29 #include "blood.h"
30 #include "callback.h"
31 #include "config.h"
32 #include "controls.h"
33 #include "demo.h"
34 #include "eventq.h"
35 #include "fx.h"
36 #include "gib.h"
37 #include "globals.h"
38 #include "levels.h"
39 #include "loadsave.h"
40 #include "map2d.h"
41 #include "network.h"
42 #include "player.h"
43 #include "seq.h"
44 #include "sfx.h"
45 #include "sound.h"
46 #include "tile.h"
47 #include "triggers.h"
48 #include "trig.h"
49 #include "view.h"
50 #include "warp.h"
51 #include "weapon.h"
52 #include "common_game.h"
53 #include "messages.h"
54 #ifdef NOONE_EXTENSIONS
55 #include "nnexts.h"
56 #endif
57 
58 PROFILE gProfile[kMaxPlayers];
59 
60 PLAYER gPlayer[kMaxPlayers];
61 PLAYER *gMe, *gView;
62 
63 bool gBlueFlagDropped = false;
64 bool gRedFlagDropped = false;
65 
66 // V = has effect in game, X = no effect in game
67 POWERUPINFO gPowerUpInfo[kMaxPowerUps] = {
68     { -1, 1, 1, 1 },            // 00: V keys
69     { -1, 1, 1, 1 },            // 01: V keys
70     { -1, 1, 1, 1 },            // 02: V keys
71     { -1, 1, 1, 1 },            // 03: V keys
72     { -1, 1, 1, 1 },            // 04: V keys
73     { -1, 1, 1, 1 },            // 05: V keys
74     { -1, 1, 1, 1 },            // 06: V keys
75     { -1, 0, 100, 100 },        // 07: V doctor's bag
76     { -1, 0, 50, 100 },         // 08: V medicine pouch
77     { -1, 0, 20, 100 },         // 09: V life essense
78     { -1, 0, 100, 200 },        // 10: V life seed
79     { -1, 0, 2, 200 },          // 11: V red potion
80     { 783, 0, 3600, 432000 },   // 12: V feather fall
81     { 896, 0, 3600, 432000 },   // 13: V cloak of invisibility
82     { 825, 1, 3600, 432000 },   // 14: V death mask (invulnerability)
83     { 827, 0, 3600, 432000 },   // 15: V jump boots
84     { 828, 0, 3600, 432000 },   // 16: X raven flight
85     { 829, 0, 3600, 1728000 },  // 17: V guns akimbo
86     { 830, 0, 3600, 432000 },   // 18: V diving suit
87     { 831, 0, 3600, 432000 },   // 19: V gas mask
88     { -1, 0, 3600, 432000 },    // 20: X clone
89     { 2566, 0, 3600, 432000 },  // 21: V crystal ball
90     { 836, 0, 3600, 432000 },   // 22: X decoy
91     { 853, 0, 3600, 432000 },   // 23: V doppleganger
92     { 2428, 0, 3600, 432000 },  // 24: V reflective shots
93     { 839, 0, 3600, 432000 },   // 25: V beast vision
94     { 768, 0, 3600, 432000 },   // 26: X cloak of shadow (useless)
95     { 840, 0, 3600, 432000 },   // 27: X rage shroom
96     { 841, 0, 900, 432000 },    // 28: V delirium shroom
97     { 842, 0, 3600, 432000 },   // 29: V grow shroom (gModernMap only)
98     { 843, 0, 3600, 432000 },   // 30: V shrink shroom (gModernMap only)
99     { -1, 0, 3600, 432000 },    // 31: X death mask (useless)
100     { -1, 0, 3600, 432000 },    // 32: X wine goblet
101     { -1, 0, 3600, 432000 },    // 33: X wine bottle
102     { -1, 0, 3600, 432000 },    // 34: X skull grail
103     { -1, 0, 3600, 432000 },    // 35: X silver grail
104     { -1, 0, 3600, 432000 },    // 36: X tome
105     { -1, 0, 3600, 432000 },    // 37: X black chest
106     { -1, 0, 3600, 432000 },    // 38: X wooden chest
107     { 837, 1, 3600, 432000 },   // 39: V asbestos armor
108     { -1, 0, 1, 432000 },       // 40: V basic armor
109     { -1, 0, 1, 432000 },       // 41: V body armor
110     { -1, 0, 1, 432000 },       // 42: V fire armor
111     { -1, 0, 1, 432000 },       // 43: V spirit armor
112     { -1, 0, 1, 432000 },       // 44: V super armor
113     { 0, 0, 0, 0 },             // 45: ? unknown
114     { 0, 0, 0, 0 },             // 46: ? unknown
115     { 0, 0, 0, 0 },             // 47: ? unknown
116     { 0, 0, 0, 0 },             // 48: ? unknown
117     { 0, 0, 0, 0 },             // 49: X dummy
118     { 833, 1, 1, 1 }            // 50: V kModernItemLevelMap (gModernMap only)
119 };
120 
121 int Handicap[] = {
122     144, 208, 256, 304, 368
123 };
124 
125 POSTURE gPostureDefaults[kModeMax][kPostureMax] = {
126 
127     // normal human
128     {
129         { 0x4000, 0x4000, 0x4000, 14, 17, 24, 16, 32, 80, 0x1600, 0x1200, 0xc00, 0x90, -0xbaaaa, -0x175555 },
130         { 0x1200, 0x1200, 0x1200, 14, 17, 24, 16, 32, 80, 0x1400, 0x1000, -0x600, 0xb0, 0x5b05, 0 },
131         { 0x2000, 0x2000, 0x2000, 22, 28, 24, 16, 16, 40, 0x800, 0x600, -0x600, 0xb0, 0, 0 },
132     },
133 
134     // normal beast
135     {
136         { 0x4000, 0x4000, 0x4000, 14, 17, 24, 16, 32, 80, 0x1600, 0x1200, 0xc00, 0x90, -0xbaaaa, -0x175555 },
137         { 0x1200, 0x1200, 0x1200, 14, 17, 24, 16, 32, 80, 0x1400, 0x1000, -0x600, 0xb0, 0x5b05, 0 },
138         { 0x2000, 0x2000, 0x2000, 22, 28, 24, 16, 16, 40, 0x800, 0x600, -0x600, 0xb0, 0, 0 },
139     },
140 
141     // shrink human
142     {
143         { 10384, 10384, 10384, 14, 17, 24, 16, 32, 80, 5632, 4608, 3072, 144, -564586, -1329173 },
144         { 2108, 2108, 2108, 14, 17, 24, 16, 32, 80, 5120, 4096, -1536, 176, 0x5b05, 0 },
145         { 2192, 2192, 2192, 22, 28, 24, 16, 16, 40, 2048, 1536, -1536, 176, 0, 0 },
146     },
147 
148     // grown human
149     {
150         { 19384, 19384, 19384, 14, 17, 24, 16, 32, 80, 5632, 4608, 3072, 144, -1014586, -1779173 },
151         { 5608, 5608, 5608, 14, 17, 24, 16, 32, 80, 5120, 4096, -1536, 176, 0x5b05, 0 },
152         { 11192, 11192, 11192, 22, 28, 24, 16, 16, 40, 2048, 1536, -1536, 176, 0, 0 },
153     },
154 };
155 
156 AMMOINFO gAmmoInfo[] = {
157     { 0, -1 },
158     { 100, -1 },
159     { 100, 4 },
160     { 500, 5 },
161     { 100, -1 },
162     { 50, -1 },
163     { 2880, -1 },
164     { 250, -1 },
165     { 100, -1 },
166     { 100, -1 },
167     { 50, -1 },
168     { 50, -1 },
169 };
170 
171 struct ARMORDATA {
172     int at0;
173     int at4;
174     int at8;
175     int atc;
176     int at10;
177     int at14;
178 };
179 ARMORDATA armorData[5] = {
180     { 0x320, 0x640, 0x320, 0x640, 0x320, 0x640 },
181     { 0x640, 0x640, 0, 0x640, 0, 0x640 },
182     { 0, 0x640, 0x640, 0x640, 0, 0x640 },
183     { 0, 0x640, 0, 0x640, 0x640, 0x640 },
184     { 0xc80, 0xc80, 0xc80, 0xc80, 0xc80, 0xc80 }
185 };
186 
187 void PlayerSurvive(int, int);
188 void PlayerKneelsOver(int, int);
189 
190 int nPlayerSurviveClient = seqRegisterClient(PlayerSurvive);
191 int nPlayerKneelClient = seqRegisterClient(PlayerKneelsOver);
192 
193 struct VICTORY {
194     const char *at0;
195     int at4;
196 };
197 
198 VICTORY gVictory[] = {
199     { "%s boned %s like a fish", 4100 },
200     { "%s castrated %s", 4101 },
201     { "%s creamed %s", 4102 },
202     { "%s destroyed %s", 4103 },
203     { "%s diced %s", 4104 },
204     { "%s disemboweled %s", 4105 },
205     { "%s flattened %s", 4106 },
206     { "%s gave %s Anal Justice", 4107 },
207     { "%s gave AnAl MaDnEsS to %s", 4108 },
208     { "%s hurt %s real bad", 4109 },
209     { "%s killed %s", 4110 },
210     { "%s made mincemeat out of %s", 4111 },
211     { "%s massacred %s", 4112 },
212     { "%s mutilated %s", 4113 },
213     { "%s reamed %s", 4114 },
214     { "%s ripped %s a new orifice", 4115 },
215     { "%s slaughtered %s", 4116 },
216     { "%s sliced %s", 4117 },
217     { "%s smashed %s", 4118 },
218     { "%s sodomized %s", 4119 },
219     { "%s splattered %s", 4120 },
220     { "%s squashed %s", 4121 },
221     { "%s throttled %s", 4122 },
222     { "%s wasted %s", 4123 },
223     { "%s body bagged %s", 4124 },
224 };
225 
226 struct SUICIDE {
227     const char *at0;
228     int at4;
229 };
230 
231 SUICIDE gSuicide[] = {
232     { "%s is excrement", 4202 },
233     { "%s is hamburger", 4203 },
234     { "%s suffered scrotum separation", 4204 },
235     { "%s volunteered for population control", 4206 },
236     { "%s has suicided", 4207 },
237 };
238 
239 struct DAMAGEINFO {
240     int at0;
241     int at4[3];
242     int at10[3];
243 };
244 
245 DAMAGEINFO damageInfo[7] = {
246     { -1, 731, 732, 733, 710, 710, 710 },
247     { 1, 742, 743, 744, 711, 711, 711 },
248     { 0, 731, 732, 733, 712, 712, 712 },
249     { 1, 731, 732, 733, 713, 713, 713 },
250     { -1, 724, 724, 724, 714, 714, 714 },
251     { 2, 731, 732, 733, 715, 715, 715 },
252     { 0, 0, 0, 0, 0, 0, 0 }
253 };
254 
powerupCheck(PLAYER * pPlayer,int nPowerUp)255 int powerupCheck(PLAYER *pPlayer, int nPowerUp)
256 {
257     dassert(pPlayer != NULL);
258     dassert(nPowerUp >= 0 && nPowerUp < kMaxPowerUps);
259     int nPack = powerupToPackItem(nPowerUp);
260     if (nPack >= 0 && !packItemActive(pPlayer, nPack))
261         return 0;
262     return pPlayer->pwUpTime[nPowerUp];
263 }
264 
265 
powerupActivate(PLAYER * pPlayer,int nPowerUp)266 char powerupActivate(PLAYER *pPlayer, int nPowerUp)
267 {
268     if (powerupCheck(pPlayer, nPowerUp) > 0 && gPowerUpInfo[nPowerUp].pickupOnce)
269         return 0;
270     if (!pPlayer->pwUpTime[nPowerUp])
271         pPlayer->pwUpTime[nPowerUp] = gPowerUpInfo[nPowerUp].bonusTime;
272     int nPack = powerupToPackItem(nPowerUp);
273     if (nPack >= 0)
274         pPlayer->packSlots[nPack].isActive = 1;
275 
276     switch (nPowerUp + kItemBase) {
277         #ifdef NOONE_EXTENSIONS
278         case kItemModernMapLevel:
279             if (gModernMap) gFullMap = true;
280             break;
281         case kItemShroomShrink:
282             if (!gModernMap) break;
283             else if (isGrown(pPlayer->pSprite)) playerDeactivateShrooms(pPlayer);
284             else playerSizeShrink(pPlayer, 2);
285             break;
286         case kItemShroomGrow:
287             if (!gModernMap) break;
288             else if (isShrinked(pPlayer->pSprite)) playerDeactivateShrooms(pPlayer);
289             else {
290                 playerSizeGrow(pPlayer, 2);
291                 if (powerupCheck(&gPlayer[pPlayer->pSprite->type - kDudePlayer1], kPwUpShadowCloak) > 0) {
292                     powerupDeactivate(pPlayer, kPwUpShadowCloak);
293                     pPlayer->pwUpTime[kPwUpShadowCloak] = 0;
294                 }
295 
296                 if (ceilIsTooLow(pPlayer->pSprite))
297                     actDamageSprite(pPlayer->pSprite->index, pPlayer->pSprite, kDamageExplode, 65535);
298             }
299             break;
300         #endif
301         case kItemFeatherFall:
302         case kItemJumpBoots:
303             pPlayer->damageControl[0]++;
304             break;
305         case kItemReflectShots: // reflective shots
306             if (pPlayer == gMe && gGameOptions.nGameType == 0)
307                 sfxSetReverb2(1);
308             break;
309         case kItemDeathMask:
310             for (int i = 0; i < 7; i++)
311                 pPlayer->damageControl[i]++;
312             break;
313         case kItemDivingSuit: // diving suit
314             pPlayer->damageControl[4]++;
315             if (pPlayer == gMe && gGameOptions.nGameType == 0)
316                 sfxSetReverb(1);
317             break;
318         case kItemGasMask:
319             pPlayer->damageControl[4]++;
320             break;
321         case kItemArmorAsbest:
322             pPlayer->damageControl[1]++;
323             break;
324         case kItemTwoGuns:
325             pPlayer->input.newWeapon = pPlayer->curWeapon;
326             WeaponRaise(pPlayer);
327             break;
328     }
329     sfxPlay3DSound(pPlayer->pSprite, 776, -1, 0);
330     return 1;
331 }
332 
powerupDeactivate(PLAYER * pPlayer,int nPowerUp)333 void powerupDeactivate(PLAYER *pPlayer, int nPowerUp)
334 {
335     int nPack = powerupToPackItem(nPowerUp);
336     if (nPack >= 0)
337         pPlayer->packSlots[nPack].isActive = 0;
338 
339     switch (nPowerUp + kItemBase) {
340         #ifdef NOONE_EXTENSIONS
341         case kItemShroomShrink:
342             if (gModernMap) {
343                 playerSizeReset(pPlayer);
344                 if (ceilIsTooLow(pPlayer->pSprite))
345                     actDamageSprite(pPlayer->pSprite->index, pPlayer->pSprite, kDamageExplode, 65535);
346             }
347             break;
348         case kItemShroomGrow:
349             if (gModernMap) playerSizeReset(pPlayer);
350             break;
351         #endif
352         case kItemFeatherFall:
353         case kItemJumpBoots:
354             pPlayer->damageControl[0]--;
355             break;
356         case kItemDeathMask:
357             for (int i = 0; i < 7; i++)
358                 pPlayer->damageControl[i]--;
359             break;
360         case kItemDivingSuit:
361             pPlayer->damageControl[4]--;
362             if (pPlayer == gMe && VanillaMode() ? true : pPlayer->pwUpTime[24] == 0)
363                 sfxSetReverb(0);
364             break;
365         case kItemReflectShots:
366             if (pPlayer == gMe && VanillaMode() ? true : pPlayer->packSlots[1].isActive == 0)
367                 sfxSetReverb(0);
368             break;
369         case kItemGasMask:
370             pPlayer->damageControl[4]--;
371             break;
372         case kItemArmorAsbest:
373             pPlayer->damageControl[1]--;
374             break;
375         case kItemTwoGuns:
376             pPlayer->input.newWeapon = pPlayer->curWeapon;
377             WeaponRaise(pPlayer);
378             break;
379     }
380 }
381 
powerupSetState(PLAYER * pPlayer,int nPowerUp,char bState)382 void powerupSetState(PLAYER *pPlayer, int nPowerUp, char bState)
383 {
384     if (!bState)
385         powerupActivate(pPlayer, nPowerUp);
386     else
387         powerupDeactivate(pPlayer, nPowerUp);
388 }
389 
powerupProcess(PLAYER * pPlayer)390 void powerupProcess(PLAYER *pPlayer)
391 {
392     pPlayer->packItemTime = ClipLow(pPlayer->packItemTime-4, 0);
393     for (int i = kMaxPowerUps-1; i >= 0; i--)
394     {
395         int nPack = powerupToPackItem(i);
396         if (nPack >= 0)
397         {
398             if (pPlayer->packSlots[nPack].isActive)
399             {
400                 pPlayer->pwUpTime[i] = ClipLow(pPlayer->pwUpTime[i]-4, 0);
401                 if (pPlayer->pwUpTime[i])
402                     pPlayer->packSlots[nPack].curAmount = (100*pPlayer->pwUpTime[i])/gPowerUpInfo[i].bonusTime;
403                 else
404                 {
405                     powerupDeactivate(pPlayer, i);
406                     if (pPlayer->packItemId == nPack)
407                         pPlayer->packItemId = 0;
408                 }
409             }
410         }
411         else if (pPlayer->pwUpTime[i] > 0)
412         {
413             pPlayer->pwUpTime[i] = ClipLow(pPlayer->pwUpTime[i]-4, 0);
414             if (!pPlayer->pwUpTime[i])
415                 powerupDeactivate(pPlayer, i);
416         }
417     }
418 }
419 
powerupClear(PLAYER * pPlayer)420 void powerupClear(PLAYER *pPlayer)
421 {
422     for (int i = kMaxPowerUps-1; i >= 0; i--)
423     {
424         pPlayer->pwUpTime[i] = 0;
425     }
426 }
427 
powerupInit(void)428 void powerupInit(void)
429 {
430 }
431 
packItemToPowerup(int nPack)432 int packItemToPowerup(int nPack)
433 {
434     int nPowerUp = -1;
435     switch (nPack) {
436         case 0:
437             break;
438         case 1:
439             nPowerUp = kPwUpDivingSuit;
440             break;
441         case 2:
442             nPowerUp = kPwUpCrystalBall;
443             break;
444         case 3:
445             nPowerUp = kPwUpBeastVision;
446             break;
447         case 4:
448             nPowerUp = kPwUpJumpBoots;
449             break;
450         default:
451             ThrowError("Unhandled pack item %d", nPack);
452             break;
453     }
454     return nPowerUp;
455 }
456 
powerupToPackItem(int nPowerUp)457 int powerupToPackItem(int nPowerUp)
458 {
459     switch (nPowerUp) {
460         case kPwUpDivingSuit:
461             return 1;
462         case kPwUpCrystalBall:
463             return 2;
464         case kPwUpBeastVision:
465             return 3;
466         case kPwUpJumpBoots:
467             return 4;
468     }
469     return -1;
470 }
471 
packAddItem(PLAYER * pPlayer,unsigned int nPack)472 char packAddItem(PLAYER *pPlayer, unsigned int nPack)
473 {
474     if (nPack <= 4)
475     {
476         if (pPlayer->packSlots[nPack].curAmount >= 100)
477             return 0;
478         pPlayer->packSlots[nPack].curAmount = 100;
479         int nPowerUp = packItemToPowerup(nPack);
480         if (nPowerUp >= 0)
481             pPlayer->pwUpTime[nPowerUp] = gPowerUpInfo[nPowerUp].bonusTime;
482         if (pPlayer->packItemId == -1)
483             pPlayer->packItemId = nPack;
484         if (!pPlayer->packSlots[pPlayer->packItemId].curAmount)
485             pPlayer->packItemId = nPack;
486     }
487     else
488         ThrowError("Unhandled pack item %d", nPack);
489     return 1;
490 }
491 
packCheckItem(PLAYER * pPlayer,int nPack)492 int packCheckItem(PLAYER *pPlayer, int nPack)
493 {
494     return pPlayer->packSlots[nPack].curAmount;
495 }
496 
packItemActive(PLAYER * pPlayer,int nPack)497 char packItemActive(PLAYER *pPlayer, int nPack)
498 {
499     return pPlayer->packSlots[nPack].isActive;
500 }
501 
packUseItem(PLAYER * pPlayer,int nPack)502 void packUseItem(PLAYER *pPlayer, int nPack)
503 {
504     char v4 = 0;
505     int nPowerUp = -1;
506     if (pPlayer->packSlots[nPack].curAmount > 0)
507     {
508         switch (nPack)
509         {
510         case 0:
511         {
512             XSPRITE *pXSprite = pPlayer->pXSprite;
513             unsigned int health = pXSprite->health>>4;
514             if (health < 100)
515             {
516                 int heal = ClipHigh(100-health, pPlayer->packSlots[0].curAmount);
517                 actHealDude(pXSprite, heal, 100);
518                 pPlayer->packSlots[0].curAmount -= heal;
519             }
520             break;
521         }
522         case 1:
523             v4 = 1;
524             nPowerUp = kPwUpDivingSuit;
525             break;
526         case 2:
527             v4 = 1;
528             nPowerUp = kPwUpCrystalBall;
529             break;
530         case 3:
531             v4 = 1;
532             nPowerUp = kPwUpBeastVision;
533             break;
534         case 4:
535             v4 = 1;
536             nPowerUp = kPwUpJumpBoots;
537             break;
538         default:
539             ThrowError("Unhandled pack item %d", nPack);
540             return;
541         }
542     }
543     pPlayer->packItemTime = 0;
544     if (v4)
545         powerupSetState(pPlayer, nPowerUp, pPlayer->packSlots[nPack].isActive);
546 }
547 
packPrevItem(PLAYER * pPlayer)548 void packPrevItem(PLAYER *pPlayer)
549 {
550     if (pPlayer->packItemTime > 0)
551     {
552         for (int nPrev = ClipLow(pPlayer->packItemId-1,0); nPrev >= 0; nPrev--)
553         {
554             if (pPlayer->packSlots[nPrev].curAmount)
555             {
556                 pPlayer->packItemId = nPrev;
557                 break;
558             }
559         }
560     }
561     pPlayer->packItemTime = 600;
562 }
563 
packNextItem(PLAYER * pPlayer)564 void packNextItem(PLAYER *pPlayer)
565 {
566     if (pPlayer->packItemTime > 0)
567     {
568         for (int nNext = ClipHigh(pPlayer->packItemId+1,5); nNext < 5; nNext++)
569         {
570             if (pPlayer->packSlots[nNext].curAmount)
571             {
572                 pPlayer->packItemId = nNext;
573                 break;
574             }
575         }
576     }
577     pPlayer->packItemTime = 600;
578 }
579 
playerSeqPlaying(PLAYER * pPlayer,int nSeq)580 char playerSeqPlaying(PLAYER * pPlayer, int nSeq)
581 {
582     int nCurSeq = seqGetID(3, pPlayer->pSprite->extra);
583     if (pPlayer->pDudeInfo->seqStartID+nSeq == nCurSeq && seqGetStatus(3,pPlayer->pSprite->extra) >= 0)
584         return 1;
585     return 0;
586 }
587 
playerSetRace(PLAYER * pPlayer,int nLifeMode)588 void playerSetRace(PLAYER *pPlayer, int nLifeMode)
589 {
590     dassert(nLifeMode >= kModeHuman && nLifeMode <= kModeHumanGrown);
591     DUDEINFO *pDudeInfo = pPlayer->pDudeInfo;
592     *pDudeInfo = gPlayerTemplate[nLifeMode];
593     pPlayer->lifeMode = nLifeMode;
594 
595     // By NoOne: don't forget to change clipdist for grow and shrink modes
596     pPlayer->pSprite->clipdist = pDudeInfo->clipdist;
597 
598     for (int i = 0; i < 7; i++)
599         pDudeInfo->at70[i] = mulscale8(Handicap[gProfile[pPlayer->nPlayer].skill], pDudeInfo->startDamage[i]);
600 }
601 
playerSetGodMode(PLAYER * pPlayer,char bGodMode)602 void playerSetGodMode(PLAYER *pPlayer, char bGodMode)
603 {
604     if (bGodMode)
605     {
606         for (int i = 0; i < 7; i++)
607             pPlayer->damageControl[i]++;
608     }
609     else
610     {
611         for (int i = 0; i < 7; i++)
612             pPlayer->damageControl[i]--;
613     }
614     pPlayer->godMode = bGodMode;
615 }
616 
playerResetInertia(PLAYER * pPlayer)617 void playerResetInertia(PLAYER *pPlayer)
618 {
619     POSTURE *pPosture = &pPlayer->pPosture[pPlayer->lifeMode][pPlayer->posture];
620     pPlayer->zView = pPlayer->pSprite->z-pPosture->eyeAboveZ;
621     pPlayer->zWeapon = pPlayer->pSprite->z-pPosture->weaponAboveZ;
622     viewBackupView(pPlayer->nPlayer);
623 }
624 
playerCorrectInertia(PLAYER * pPlayer,vec3_t const * oldpos)625 void playerCorrectInertia(PLAYER* pPlayer, vec3_t const *oldpos)
626 {
627     pPlayer->zView += pPlayer->pSprite->z-oldpos->z;
628     pPlayer->zWeapon += pPlayer->pSprite->z-oldpos->z;
629     viewCorrectViewOffsets(pPlayer->nPlayer, oldpos);
630 }
631 
playerResetPowerUps(PLAYER * pPlayer)632 void playerResetPowerUps(PLAYER* pPlayer)
633 {
634     for (int i = 0; i < kMaxPowerUps; i++) {
635         if (!VanillaMode() && (i == kPwUpJumpBoots || i == kPwUpDivingSuit || i == kPwUpCrystalBall || i == kPwUpBeastVision))
636             continue;
637         pPlayer->pwUpTime[i] = 0;
638     }
639 }
640 
playerResetPosture(PLAYER * pPlayer)641 void playerResetPosture(PLAYER* pPlayer) {
642     memcpy(pPlayer->pPosture, gPostureDefaults, sizeof(gPostureDefaults));
643 }
644 
playerStart(int nPlayer,int bNewLevel)645 void playerStart(int nPlayer, int bNewLevel)
646 {
647     PLAYER* pPlayer = &gPlayer[nPlayer];
648     GINPUT* pInput = &pPlayer->input;
649     ZONE* pStartZone = NULL;
650 
651     // normal start position
652     if (gGameOptions.nGameType <= 1)
653         pStartZone = &gStartZone[nPlayer];
654 
655     #ifdef NOONE_EXTENSIONS
656     // let's check if there is positions of teams is specified
657     // if no, pick position randomly, just like it works in vanilla.
658     else if (gModernMap && gGameOptions.nGameType == 3 && gTeamsSpawnUsed == true) {
659         int maxRetries = 5;
660         while (maxRetries-- > 0) {
661             if (pPlayer->teamId == 0) pStartZone = &gStartZoneTeam1[Random(3)];
662             else pStartZone = &gStartZoneTeam2[Random(3)];
663 
664             if (maxRetries != 0) {
665                 // check if there is no spawned player in selected zone
666                 for (int i = headspritesect[pStartZone->sectnum]; i >= 0; i = nextspritesect[i]) {
667                     spritetype* pSprite = &sprite[i];
668                     if (pStartZone->x == pSprite->x && pStartZone->y == pSprite->y && IsPlayerSprite(pSprite)) {
669                         pStartZone = NULL;
670                         break;
671                     }
672                 }
673             }
674 
675             if (pStartZone != NULL)
676                 break;
677         }
678 
679     }
680     #endif
681     else {
682         pStartZone = &gStartZone[Random(8)];
683     }
684 
685     if (!VanillaMode())
686         sfxKillSpriteSounds(pPlayer->pSprite);
687 
688     spritetype *pSprite = actSpawnSprite(pStartZone->sectnum, pStartZone->x, pStartZone->y, pStartZone->z, 6, 1);
689     dassert(pSprite->extra > 0 && pSprite->extra < kMaxXSprites);
690     XSPRITE *pXSprite = &xsprite[pSprite->extra];
691     pPlayer->pSprite = pSprite;
692     pPlayer->pXSprite = pXSprite;
693     pPlayer->nSprite = pSprite->index;
694     DUDEINFO *pDudeInfo = &dudeInfo[kDudePlayer1 + nPlayer - kDudeBase];
695     pPlayer->pDudeInfo = pDudeInfo;
696     playerSetRace(pPlayer, kModeHuman);
697     playerResetPosture(pPlayer);
698     seqSpawn(pDudeInfo->seqStartID, 3, pSprite->extra, -1);
699     if (pPlayer == gMe)
700         SetBitString(show2dsprite, pSprite->index);
701     int top, bottom;
702     GetSpriteExtents(pSprite, &top, &bottom);
703     pSprite->z -= bottom - pSprite->z;
704     pSprite->pal = 11+(pPlayer->teamId&3);
705     pPlayer->angold = pSprite->ang = pStartZone->ang;
706     pPlayer->q16ang = fix16_from_int(pSprite->ang);
707     pSprite->type = kDudePlayer1+nPlayer;
708     pSprite->clipdist = pDudeInfo->clipdist;
709     pSprite->flags = 15;
710     pXSprite->burnTime = 0;
711     pXSprite->burnSource = -1;
712     pPlayer->pXSprite->health = pDudeInfo->startHealth<<4;
713     pPlayer->pSprite->cstat &= (unsigned short)~32768;
714     pPlayer->bloodlust = 0;
715     pPlayer->q16horiz = 0;
716     pPlayer->q16slopehoriz = 0;
717     pPlayer->q16look = 0;
718     pPlayer->slope = 0;
719     pPlayer->fraggerId = -1;
720     pPlayer->underwaterTime = 1200;
721     pPlayer->bloodTime = 0;
722     pPlayer->gooTime = 0;
723     pPlayer->wetTime = 0;
724     pPlayer->bubbleTime = 0;
725     pPlayer->at306 = 0;
726     pPlayer->restTime = 0;
727     pPlayer->kickPower = 0;
728     pPlayer->laughCount = 0;
729     pPlayer->spin = 0;
730     pPlayer->posture = 0;
731     pPlayer->voodooTarget = -1;
732     pPlayer->voodooTargets = 0;
733     pPlayer->voodooVar1 = 0;
734     pPlayer->vodooVar2 = 0;
735     playerResetInertia(pPlayer);
736     pPlayer->zWeaponVel = 0;
737     pPlayer->relAim.dx = 0x4000;
738     pPlayer->relAim.dy = 0;
739     pPlayer->relAim.dz = 0;
740     pPlayer->aimTarget = -1;
741     pPlayer->zViewVel = pPlayer->zWeaponVel;
742     if (!(gGameOptions.nGameType == 1 && gGameOptions.bKeepKeysOnRespawn && !bNewLevel))
743         for (int i = 0; i < 8; i++)
744             pPlayer->hasKey[i] = gGameOptions.nGameType >= 2;
745     pPlayer->hasFlag = 0;
746     for (int i = 0; i < 8; i++)
747         pPlayer->used2[i] = -1;
748     for (int i = 0; i < 7; i++)
749         pPlayer->damageControl[i] = 0;
750     if (pPlayer->godMode)
751         playerSetGodMode(pPlayer, 1);
752     gInfiniteAmmo = 0;
753     gFullMap = 0;
754     pPlayer->throwPower = 0;
755     pPlayer->deathTime = 0;
756     pPlayer->nextWeapon = 0;
757     xvel[pSprite->index] = yvel[pSprite->index] = zvel[pSprite->index] = 0;
758     pInput->q16turn = 0;
759     pInput->keyFlags.word = 0;
760     pInput->forward = 0;
761     pInput->strafe = 0;
762     pInput->q16mlook = 0;
763     pInput->buttonFlags.byte = 0;
764     pInput->useFlags.byte = 0;
765     pPlayer->flickerEffect = 0;
766     pPlayer->quakeEffect = 0;
767     pPlayer->tiltEffect = 0;
768     pPlayer->visibility = 0;
769     pPlayer->painEffect = 0;
770     pPlayer->blindEffect = 0;
771     pPlayer->chokeEffect = 0;
772     pPlayer->handTime = 0;
773     pPlayer->weaponTimer = 0;
774     pPlayer->weaponState = 0;
775     pPlayer->weaponQav = -1;
776     #ifdef NOONE_EXTENSIONS
777     playerQavSceneReset(pPlayer); // reset qav scene
778 
779     // assign or update player's sprite index for conditions
780     if (gModernMap) {
781 
782         for (int nSprite = headspritestat[kStatModernPlayerLinker]; nSprite >= 0; nSprite = nextspritestat[nSprite]) {
783             XSPRITE* pXCtrl = &xsprite[sprite[nSprite].extra];
784             if (pXCtrl->data1 == pPlayer->nPlayer + 1) {
785                 int nSpriteOld = pXCtrl->sysData1;
786                 trPlayerCtrlLink(pXCtrl, pPlayer, (nSpriteOld < 0) ? true : false);
787                 if (nSpriteOld > 0)
788                     condUpdateObjectIndex(OBJ_SPRITE, nSpriteOld, pXCtrl->sysData1);
789             }
790         }
791 
792     }
793 
794     #endif
795     pPlayer->hand = 0;
796     pPlayer->nWaterPal = 0;
797     playerResetPowerUps(pPlayer);
798 
799     if (pPlayer == gMe)
800     {
801         viewInitializePrediction();
802         gViewLook = pPlayer->q16look;
803         gViewAngle = pPlayer->q16ang;
804         gViewMap.x = pPlayer->pSprite->x;
805         gViewMap.y = pPlayer->pSprite->y;
806         gViewMap.angle = pPlayer->pSprite->ang;
807     }
808     if (IsUnderwaterSector(pSprite->sectnum))
809     {
810         pPlayer->posture = 1;
811         pPlayer->pXSprite->medium = kMediumWater;
812     }
813 }
814 
playerReset(PLAYER * pPlayer)815 void playerReset(PLAYER *pPlayer)
816 {
817     static int dword_136400[] = {
818         3, 4, 2, 8, 9, 10, 7, 1, 1, 1, 1, 1, 1, 1
819     };
820     static int dword_136438[] = {
821         3, 4, 2, 8, 9, 10, 7, 1, 1, 1, 1, 1, 1, 1
822     };
823     dassert(pPlayer != NULL);
824     for (int i = 0; i < 14; i++)
825     {
826         pPlayer->hasWeapon[i] = gInfiniteAmmo;
827         pPlayer->weaponMode[i] = 0;
828     }
829     pPlayer->hasWeapon[1] = 1;
830     pPlayer->curWeapon = 0;
831     pPlayer->qavCallback = -1;
832     pPlayer->input.newWeapon = 1;
833     for (int i = 0; i < 14; i++)
834     {
835         pPlayer->weaponOrder[0][i] = dword_136400[i];
836         pPlayer->weaponOrder[1][i] = dword_136438[i];
837     }
838     for (int i = 0; i < 12; i++)
839     {
840         if (gInfiniteAmmo)
841             pPlayer->ammoCount[i] = gAmmoInfo[i].max;
842         else
843             pPlayer->ammoCount[i] = 0;
844     }
845     for (int i = 0; i < 3; i++)
846         pPlayer->armor[i] = 0;
847     pPlayer->weaponTimer = 0;
848     pPlayer->weaponState = 0;
849     pPlayer->weaponQav = -1;
850     pPlayer->qavLoop = 0;
851     pPlayer->packItemId = -1;
852 
853     for (int i = 0; i < 5; i++) {
854         pPlayer->packSlots[i].isActive = 0;
855         pPlayer->packSlots[i].curAmount = 0;
856     }
857     #ifdef NOONE_EXTENSIONS
858     playerQavSceneReset(pPlayer);
859     #endif
860     // reset posture (mainly required for resetting movement speed and jump height)
861     playerResetPosture(pPlayer);
862 
863 }
864 
865 int dword_21EFB0[8];
866 ClockTicks dword_21EFD0[8];
867 
playerInit(int nPlayer,unsigned int a2)868 void playerInit(int nPlayer, unsigned int a2)
869 {
870     PLAYER *pPlayer = &gPlayer[nPlayer];
871     if (!(a2&1))
872         memset(pPlayer, 0, sizeof(PLAYER));
873     pPlayer->nPlayer = nPlayer;
874     pPlayer->teamId = nPlayer;
875     if (gGameOptions.nGameType == 3)
876         pPlayer->teamId = nPlayer&1;
877     pPlayer->fragCount = 0;
878     memset(dword_21EFB0, 0, sizeof(dword_21EFB0));
879     memset(dword_21EFD0, 0, sizeof(dword_21EFD0));
880     memset(pPlayer->fragInfo, 0, sizeof(pPlayer->fragInfo));
881 
882     if (!(a2&1))
883         playerReset(pPlayer);
884 }
885 
sub_3A158(PLAYER * a1,spritetype * a2)886 char sub_3A158(PLAYER *a1, spritetype *a2)
887 {
888     for (int nSprite = headspritestat[kStatThing]; nSprite >= 0; nSprite = nextspritestat[nSprite])
889     {
890         if (a2 && a2->index == nSprite)
891             continue;
892         spritetype *pSprite = &sprite[nSprite];
893         if (pSprite->type == kThingDroppedLifeLeech && actOwnerIdToSpriteId(pSprite->owner) == a1->nSprite)
894             return 1;
895     }
896     return 0;
897 }
898 
PickupItem(PLAYER * pPlayer,spritetype * pItem)899 char PickupItem(PLAYER *pPlayer, spritetype *pItem) {
900 
901     spritetype *pSprite = pPlayer->pSprite; XSPRITE *pXSprite = pPlayer->pXSprite;
902     char buffer[80]; int pickupSnd = 775; int nType = pItem->type - kItemBase;
903 
904     switch (pItem->type) {
905         case kItemShadowCloak:
906             #ifdef NOONE_EXTENSIONS
907             if (isGrown(pPlayer->pSprite) || !powerupActivate(pPlayer, nType)) return false;
908             #else
909             if (!powerupActivate(pPlayer, nType)) return false;
910             #endif
911             break;
912         #ifdef NOONE_EXTENSIONS
913         case kItemShroomShrink:
914         case kItemShroomGrow:
915 
916             if (gModernMap) {
917                 switch (pItem->type) {
918                     case kItemShroomShrink:
919                         if (isShrinked(pSprite)) return false;
920                         break;
921                     case kItemShroomGrow:
922                         if (isGrown(pSprite)) return false;
923                         break;
924                 }
925 
926                 powerupActivate(pPlayer, nType);
927             }
928 
929             break;
930         #endif
931         case kItemFlagABase:
932         case kItemFlagBBase: {
933             if (gGameOptions.nGameType != 3 || pItem->extra <= 0) return 0;
934             XSPRITE * pXItem = &xsprite[pItem->extra];
935             if (pItem->type == kItemFlagABase) {
936                 if (pPlayer->teamId == 1) {
937                     if ((pPlayer->hasFlag & 1) == 0 && pXItem->state) {
938                         pPlayer->hasFlag |= 1;
939                         pPlayer->used2[0] = pItem->index;
940                         trTriggerSprite(pItem->index, pXItem, kCmdOff);
941                         sprintf(buffer, "%s stole Blue Flag", gProfile[pPlayer->nPlayer].name);
942                         sndStartSample(8007, 255, 2, 0);
943                         viewSetMessage(buffer);
944                     }
945                 }
946 
947                 if (pPlayer->teamId == 0) {
948 
949                     if ((pPlayer->hasFlag & 1) != 0 && !pXItem->state) {
950                         pPlayer->hasFlag &= ~1;
951                         pPlayer->used2[0] = -1;
952                         trTriggerSprite(pItem->index, pXItem, kCmdOn);
953                         sprintf(buffer, "%s returned Blue Flag", gProfile[pPlayer->nPlayer].name);
954                         sndStartSample(8003, 255, 2, 0);
955                         viewSetMessage(buffer);
956                     }
957 
958                     if ((pPlayer->hasFlag & 2) != 0 && pXItem->state) {
959                         pPlayer->hasFlag &= ~2;
960                         pPlayer->used2[1] = -1;
961                         dword_21EFB0[pPlayer->teamId] += 10;
962                         dword_21EFD0[pPlayer->teamId] += 240;
963                         evSend(0, 0, 81, kCmdOn);
964                         sprintf(buffer, "%s captured Red Flag!", gProfile[pPlayer->nPlayer].name);
965                         sndStartSample(8001, 255, 2, 0);
966                         viewSetMessage(buffer);
967 #if 0
968                         if (dword_28E3D4 == 3 && myconnectindex == connecthead)
969                         {
970                             sprintf(buffer, "frag A killed B\n");
971                             sub_7AC28(buffer);
972                         }
973 #endif
974                     }
975                 }
976 
977             }
978             else if (pItem->type == kItemFlagBBase) {
979 
980                 if (pPlayer->teamId == 0) {
981                     if ((pPlayer->hasFlag & 2) == 0 && pXItem->state) {
982                         pPlayer->hasFlag |= 2;
983                         pPlayer->used2[1] = pItem->index;
984                         trTriggerSprite(pItem->index, pXItem, kCmdOff);
985                         sprintf(buffer, "%s stole Red Flag", gProfile[pPlayer->nPlayer].name);
986                         sndStartSample(8006, 255, 2, 0);
987                         viewSetMessage(buffer);
988                     }
989                 }
990 
991                 if (pPlayer->teamId == 1) {
992                     if ((pPlayer->hasFlag & 2) != 0 && !pXItem->state)
993                     {
994                         pPlayer->hasFlag &= ~2;
995                         pPlayer->used2[1] = -1;
996                         trTriggerSprite(pItem->index, pXItem, kCmdOn);
997                         sprintf(buffer, "%s returned Red Flag", gProfile[pPlayer->nPlayer].name);
998                         sndStartSample(8002, 255, 2, 0);
999                         viewSetMessage(buffer);
1000                     }
1001                     if ((pPlayer->hasFlag & 1) != 0 && pXItem->state)
1002                     {
1003                         pPlayer->hasFlag &= ~1;
1004                         pPlayer->used2[0] = -1;
1005                         dword_21EFB0[pPlayer->teamId] += 10;
1006                         dword_21EFD0[pPlayer->teamId] += 240;
1007                         evSend(0, 0, 80, kCmdOn);
1008                         sprintf(buffer, "%s captured Blue Flag!", gProfile[pPlayer->nPlayer].name);
1009                         sndStartSample(8000, 255, 2, 0);
1010                         viewSetMessage(buffer);
1011 #if 0
1012                         if (dword_28E3D4 == 3 && myconnectindex == connecthead)
1013                         {
1014                             sprintf(buffer, "frag B killed A\n");
1015                             sub_7AC28(buffer);
1016                         }
1017 #endif
1018                     }
1019                 }
1020             }
1021         }
1022         return 0;
1023         case kItemFlagA:
1024             if (gGameOptions.nGameType != 3) return 0;
1025             evKill(pItem->index, 3, kCallbackReturnFlag);
1026             pPlayer->hasFlag |= 1;
1027             pPlayer->used2[0] = pItem->index;
1028             gBlueFlagDropped = false;
1029             break;
1030         case kItemFlagB:
1031             if (gGameOptions.nGameType != 3) return 0;
1032             evKill(pItem->index, 3, kCallbackReturnFlag);
1033             pPlayer->hasFlag |= 2;
1034             pPlayer->used2[1] = pItem->index;
1035             gRedFlagDropped = false;
1036             break;
1037         case kItemArmorBasic:
1038         case kItemArmorBody:
1039         case kItemArmorFire:
1040         case kItemArmorSpirit:
1041         case kItemArmorSuper: {
1042             ARMORDATA *pArmorData = &armorData[pItem->type - kItemArmorBasic]; bool pickedUp = false;
1043             if (pPlayer->armor[1] < pArmorData->atc) {
1044                 pPlayer->armor[1] = ClipHigh(pPlayer->armor[1]+pArmorData->at8, pArmorData->atc);
1045                 pickedUp = true;
1046             }
1047 
1048             if (pPlayer->armor[0] < pArmorData->at4) {
1049                 pPlayer->armor[0] = ClipHigh(pPlayer->armor[0]+pArmorData->at0, pArmorData->at4);
1050                 pickedUp = true;
1051             }
1052 
1053             if (pPlayer->armor[2] < pArmorData->at14) {
1054                 pPlayer->armor[2] = ClipHigh(pPlayer->armor[2]+pArmorData->at10, pArmorData->at14);
1055                 pickedUp = true;
1056             }
1057 
1058             if (!pickedUp) return 0;
1059             pickupSnd = 779;
1060             break;
1061         }
1062         case kItemCrystalBall:
1063             if (gGameOptions.nGameType == 0 || !packAddItem(pPlayer, gItemData[nType].packSlot)) return 0;
1064             break;
1065         case kItemKeySkull:
1066         case kItemKeyEye:
1067         case kItemKeyFire:
1068         case kItemKeyDagger:
1069         case kItemKeySpider:
1070         case kItemKeyMoon:
1071         case kItemKeyKey7:
1072             if (pPlayer->hasKey[pItem->type-99]) return 0;
1073             pPlayer->hasKey[pItem->type-99] = 1;
1074             pickupSnd = 781;
1075             break;
1076         case kItemHealthMedPouch:
1077         case kItemHealthLifeEssense:
1078         case kItemHealthLifeSeed:
1079         case kItemHealthRedPotion:  {
1080             int addPower = gPowerUpInfo[nType].bonusTime;
1081             #ifdef NOONE_EXTENSIONS
1082             // allow custom amount for item
1083             if (gModernMap && sprite[pItem->index].extra >= 0 && xsprite[sprite[pItem->index].extra].data1 > 0)
1084                 addPower = xsprite[sprite[pItem->index].extra].data1;
1085             #endif
1086 
1087             if (!actHealDude(pXSprite, addPower, gPowerUpInfo[nType].maxTime)) return 0;
1088             return 1;
1089         }
1090         case kItemHealthDoctorBag:
1091         case kItemJumpBoots:
1092         case kItemDivingSuit:
1093         case kItemBeastVision:
1094             if (!packAddItem(pPlayer, gItemData[nType].packSlot)) return 0;
1095             break;
1096         default:
1097             if (!powerupActivate(pPlayer, nType)) return 0;
1098             return 1;
1099     }
1100 
1101     sfxPlay3DSound(pSprite->x, pSprite->y, pSprite->z, pickupSnd, pSprite->sectnum);
1102     return 1;
1103 }
1104 
PickupAmmo(PLAYER * pPlayer,spritetype * pAmmo)1105 char PickupAmmo(PLAYER* pPlayer, spritetype* pAmmo) {
1106     AMMOITEMDATA* pAmmoItemData = &gAmmoItemData[pAmmo->type - kItemAmmoBase];
1107     int nAmmoType = pAmmoItemData->type;
1108 
1109     if (pPlayer->ammoCount[nAmmoType] >= gAmmoInfo[nAmmoType].max) return 0;
1110     #ifdef NOONE_EXTENSIONS
1111     else if (gModernMap && pAmmo->extra >= 0 && xsprite[pAmmo->extra].data1 > 0) // allow custom amount for item
1112         pPlayer->ammoCount[nAmmoType] = ClipHigh(pPlayer->ammoCount[nAmmoType] + xsprite[pAmmo->extra].data1, gAmmoInfo[nAmmoType].max);
1113     #endif
1114     else
1115         pPlayer->ammoCount[nAmmoType] = ClipHigh(pPlayer->ammoCount[nAmmoType]+pAmmoItemData->count, gAmmoInfo[nAmmoType].max);
1116 
1117     if (pAmmoItemData->weaponType)  pPlayer->hasWeapon[pAmmoItemData->weaponType] = 1;
1118     sfxPlay3DSound(pPlayer->pSprite, 782, -1, 0);
1119     return 1;
1120 }
1121 
PickupWeapon(PLAYER * pPlayer,spritetype * pWeapon)1122 char PickupWeapon(PLAYER *pPlayer, spritetype *pWeapon) {
1123     WEAPONITEMDATA *pWeaponItemData = &gWeaponItemData[pWeapon->type - kItemWeaponBase];
1124     int nWeaponType = pWeaponItemData->type;
1125     int nAmmoType = pWeaponItemData->ammoType;
1126     if (!pPlayer->hasWeapon[nWeaponType] || gGameOptions.nWeaponSettings == 2 || gGameOptions.nWeaponSettings == 3) {
1127         if (pWeapon->type == kItemWeaponLifeLeech && gGameOptions.nGameType > 1 && sub_3A158(pPlayer, NULL))
1128             return 0;
1129         pPlayer->hasWeapon[nWeaponType] = 1;
1130         if (nAmmoType == -1) return 0;
1131         // allow to set custom ammo count for weapon pickups
1132         #ifdef NOONE_EXTENSIONS
1133         else if (gModernMap && pWeapon->extra >= 0 && xsprite[pWeapon->extra].data1 > 0)
1134             pPlayer->ammoCount[nAmmoType] = ClipHigh(pPlayer->ammoCount[nAmmoType] + xsprite[pWeapon->extra].data1, gAmmoInfo[nAmmoType].max);
1135         #endif
1136         else
1137             pPlayer->ammoCount[nAmmoType] = ClipHigh(pPlayer->ammoCount[nAmmoType] + pWeaponItemData->count, gAmmoInfo[nAmmoType].max);
1138 
1139         int nNewWeapon = WeaponUpgrade(pPlayer, nWeaponType);
1140         if (nNewWeapon != pPlayer->curWeapon) {
1141             pPlayer->weaponState = 0;
1142             pPlayer->nextWeapon = nNewWeapon;
1143         }
1144         sfxPlay3DSound(pPlayer->pSprite, 777, -1, 0);
1145         return 1;
1146     }
1147 
1148     if (!actGetRespawnTime(pWeapon) || nAmmoType == -1 || pPlayer->ammoCount[nAmmoType] >= gAmmoInfo[nAmmoType].max) return 0;
1149     #ifdef NOONE_EXTENSIONS
1150         else if (gModernMap && pWeapon->extra >= 0 && xsprite[pWeapon->extra].data1 > 0)
1151             pPlayer->ammoCount[nAmmoType] = ClipHigh(pPlayer->ammoCount[nAmmoType] + xsprite[pWeapon->extra].data1, gAmmoInfo[nAmmoType].max);
1152     #endif
1153     else
1154         pPlayer->ammoCount[nAmmoType] = ClipHigh(pPlayer->ammoCount[nAmmoType]+pWeaponItemData->count, gAmmoInfo[nAmmoType].max);
1155 
1156     sfxPlay3DSound(pPlayer->pSprite, 777, -1, 0);
1157     return 1;
1158 }
1159 
PickUp(PLAYER * pPlayer,spritetype * pSprite)1160 void PickUp(PLAYER *pPlayer, spritetype *pSprite)
1161 {
1162     char buffer[80];
1163     int nType = pSprite->type;
1164     char pickedUp = 0;
1165     int customMsg = -1;
1166     #ifdef NOONE_EXTENSIONS
1167         if (gModernMap) { // allow custom INI message instead "Picked up"
1168             XSPRITE* pXSprite = (pSprite->extra >= 0) ? &xsprite[pSprite->extra] : NULL;
1169             if (pXSprite != NULL && pXSprite->txID != 3 && pXSprite->lockMsg > 0)
1170                 customMsg = pXSprite->lockMsg;
1171         }
1172     #endif
1173 
1174     if (nType >= kItemBase && nType <= kItemMax) {
1175         pickedUp = PickupItem(pPlayer, pSprite);
1176         if (pickedUp && customMsg == -1) sprintf(buffer, "Picked up %s", gItemText[nType - kItemBase]);
1177 
1178     } else if (nType >= kItemAmmoBase && nType < kItemAmmoMax) {
1179         pickedUp = PickupAmmo(pPlayer, pSprite);
1180         if (pickedUp && customMsg == -1) sprintf(buffer, "Picked up %s", gAmmoText[nType - kItemAmmoBase]);
1181 
1182     } else if (nType >= kItemWeaponBase && nType < kItemWeaponMax) {
1183         pickedUp = PickupWeapon(pPlayer, pSprite);
1184         if (pickedUp && customMsg == -1) sprintf(buffer, "Picked up %s", gWeaponText[nType - kItemWeaponBase]);
1185     }
1186 
1187     if (!pickedUp) return;
1188     else if (pSprite->extra > 0) {
1189         XSPRITE *pXSprite = &xsprite[pSprite->extra];
1190         if (pXSprite->Pickup)
1191             trTriggerSprite(pSprite->index, pXSprite, kCmdSpritePickup);
1192     }
1193 
1194     if (!actCheckRespawn(pSprite))
1195         actPostSprite(pSprite->index, kStatFree);
1196 
1197     pPlayer->pickupEffect = 30;
1198     if (pPlayer == gMe) {
1199         if (customMsg > 0) trTextOver(customMsg - 1);
1200         else viewSetMessage(buffer, 0, MESSAGE_PRIORITY_PICKUP);
1201     }
1202 }
1203 
CheckPickUp(PLAYER * pPlayer)1204 void CheckPickUp(PLAYER *pPlayer)
1205 {
1206     spritetype *pSprite = pPlayer->pSprite;
1207     int x = pSprite->x;
1208     int y = pSprite->y;
1209     int z = pSprite->z;
1210     int nSector = pSprite->sectnum;
1211     int nNextSprite;
1212     for (int nSprite = headspritestat[kStatItem]; nSprite >= 0; nSprite = nNextSprite) {
1213         spritetype *pItem = &sprite[nSprite];
1214         nNextSprite = nextspritestat[nSprite];
1215         if (pItem->flags&32)
1216             continue;
1217         int dx = klabs(x-pItem->x)>>4;
1218         if (dx > 48)
1219             continue;
1220         int dy = klabs(y-pItem->y)>>4;
1221         if (dy > 48)
1222             continue;
1223         int top, bottom;
1224         GetSpriteExtents(pSprite, &top, &bottom);
1225         int vb = 0;
1226         if (pItem->z < top)
1227             vb = (top-pItem->z)>>8;
1228         else if (pItem->z > bottom)
1229             vb = (pItem->z-bottom)>>8;
1230         if (vb > 32)
1231             continue;
1232         if (approxDist(dx,dy) > 48)
1233             continue;
1234         GetSpriteExtents(pItem, &top, &bottom);
1235         if (cansee(x, y, z, nSector, pItem->x, pItem->y, pItem->z, pItem->sectnum)
1236          || cansee(x, y, z, nSector, pItem->x, pItem->y, top, pItem->sectnum)
1237          || cansee(x, y, z, nSector, pItem->x, pItem->y, bottom, pItem->sectnum))
1238             PickUp(pPlayer, pItem);
1239     }
1240 }
1241 
ActionScan(PLAYER * pPlayer,int * a2,int * a3)1242 int ActionScan(PLAYER *pPlayer, int *a2, int *a3)
1243 {
1244     *a2 = 0;
1245     *a3 = 0;
1246     spritetype *pSprite = pPlayer->pSprite;
1247     int x = Cos(pSprite->ang)>>16;
1248     int y = Sin(pSprite->ang)>>16;
1249     int z = pPlayer->slope;
1250     int hit = HitScan(pSprite, pPlayer->zView, x, y, z, 0x10000040, 128);
1251     int hitDist = approxDist(pSprite->x-gHitInfo.hitx, pSprite->y-gHitInfo.hity)>>4;
1252     if (hitDist < 64)
1253     {
1254         switch (hit)
1255         {
1256         case 3:
1257             *a2 = gHitInfo.hitsprite;
1258             *a3 = sprite[*a2].extra;
1259             if (*a3 > 0 && sprite[*a2].statnum == kStatThing)
1260             {
1261                 spritetype *pSprite = &sprite[*a2];
1262                 XSPRITE *pXSprite = &xsprite[*a3];
1263                 if (pSprite->type == kThingDroppedLifeLeech)
1264                 {
1265                     if (gGameOptions.nGameType > 1 && sub_3A158(pPlayer, pSprite))
1266                         return -1;
1267                     pXSprite->data4 = pPlayer->nPlayer;
1268                     pXSprite->isTriggered = 0;
1269                 }
1270             }
1271             if (*a3 > 0 && xsprite[*a3].Push)
1272                 return 3;
1273             if (sprite[*a2].statnum == kStatDude)
1274             {
1275                 spritetype *pSprite = &sprite[*a2];
1276                 XSPRITE *pXSprite = &xsprite[*a3];
1277                 int nMass = getDudeInfo(pSprite->type)->mass;
1278                 if (nMass)
1279                 {
1280                     int t2 = divscale(0xccccc, nMass, 8);
1281                     xvel[*a2] += mulscale16(x, t2);
1282                     yvel[*a2] += mulscale16(y, t2);
1283                     zvel[*a2] += mulscale16(z, t2);
1284                 }
1285                 if (pXSprite->Push && !pXSprite->state && !pXSprite->isTriggered)
1286                     trTriggerSprite(*a2, pXSprite, kCmdSpritePush);
1287             }
1288             break;
1289         case 0:
1290         case 4:
1291             *a2 = gHitInfo.hitwall;
1292             *a3 = wall[*a2].extra;
1293             if (*a3 > 0 && xwall[*a3].triggerPush)
1294                 return 0;
1295             if (wall[*a2].nextsector >= 0)
1296             {
1297                 *a2 = wall[*a2].nextsector;
1298                 *a3 = sector[*a2].extra;
1299                 if (*a3 > 0 && xsector[*a3].Wallpush)
1300                     return 6;
1301             }
1302             break;
1303         case 1:
1304         case 2:
1305             *a2 = gHitInfo.hitsect;
1306             *a3 = sector[*a2].extra;
1307             if (*a3 > 0 && xsector[*a3].Push)
1308                 return 6;
1309             break;
1310         }
1311     }
1312     *a2 = pSprite->sectnum;
1313     *a3 = sector[*a2].extra;
1314     if (*a3 > 0 && xsector[*a3].Push)
1315         return 6;
1316     return -1;
1317 }
1318 
ProcessInput(PLAYER * pPlayer)1319 void ProcessInput(PLAYER *pPlayer)
1320 {
1321     spritetype *pSprite = pPlayer->pSprite;
1322     XSPRITE *pXSprite = pPlayer->pXSprite;
1323     int nSprite = pPlayer->nSprite;
1324     POSTURE *pPosture = &pPlayer->pPosture[pPlayer->lifeMode][pPlayer->posture];
1325     GINPUT *pInput = &pPlayer->input;
1326 
1327     if (pPlayer == gMe && numplayers == 1)
1328     {
1329         gViewAngleAdjust = 0.f;
1330         gViewLookRecenter = false;
1331         gViewLookAdjust = 0.f;
1332     }
1333 
1334     pPlayer->isRunning = pInput->syncFlags.run;
1335     if (pInput->buttonFlags.byte || pInput->forward || pInput->strafe || pInput->q16turn)
1336         pPlayer->restTime = 0;
1337     else if (pPlayer->restTime >= 0)
1338         pPlayer->restTime += 4;
1339     WeaponProcess(pPlayer);
1340     if (pXSprite->health == 0)
1341     {
1342         char bSeqStat = playerSeqPlaying(pPlayer, 16);
1343         if (pPlayer->fraggerId != -1)
1344         {
1345             pPlayer->angold = pSprite->ang = getangle(sprite[pPlayer->fraggerId].x - pSprite->x, sprite[pPlayer->fraggerId].y - pSprite->y);
1346             pPlayer->q16ang = fix16_from_int(pSprite->ang);
1347         }
1348         pPlayer->deathTime += 4;
1349         if (!bSeqStat)
1350         {
1351             if (bVanilla)
1352                 pPlayer->q16horiz = fix16_from_int(mulscale16(0x8000-(Cos(ClipHigh(pPlayer->deathTime*8, 1024))>>15), 120));
1353             else
1354                 pPlayer->q16horiz = mulscale16(0x8000-(Cos(ClipHigh(pPlayer->deathTime*8, 1024))>>15), F16(120));
1355         }
1356         if (pPlayer->curWeapon)
1357             pInput->newWeapon = pPlayer->curWeapon;
1358         if (pInput->keyFlags.action)
1359         {
1360             if (bSeqStat)
1361             {
1362                 if (pPlayer->deathTime > 360)
1363                     seqSpawn(pPlayer->pDudeInfo->seqStartID+14, 3, pPlayer->pSprite->extra, nPlayerSurviveClient);
1364             }
1365             else if (seqGetStatus(3, pPlayer->pSprite->extra) < 0)
1366             {
1367                 if (pPlayer->pSprite)
1368                     pPlayer->pSprite->type = kThingBloodChunks;
1369                 actPostSprite(pPlayer->nSprite, kStatThing);
1370                 seqSpawn(pPlayer->pDudeInfo->seqStartID+15, 3, pPlayer->pSprite->extra, -1);
1371                 playerReset(pPlayer);
1372                 if (gGameOptions.nGameType == 0 && numplayers == 1)
1373                 {
1374                     if (gDemo.at0)
1375                         gDemo.Close();
1376                     pInput->keyFlags.restart = 1;
1377                 }
1378                 else
1379                     playerStart(pPlayer->nPlayer);
1380             }
1381             pInput->keyFlags.action = 0;
1382         }
1383         return;
1384     }
1385     if (pPlayer->posture == 1)
1386     {
1387         int x = Cos(pSprite->ang);
1388         int y = Sin(pSprite->ang);
1389         if (pInput->forward)
1390         {
1391             int forward = pInput->forward;
1392             if (forward > 0)
1393                 forward = mulscale8(pPosture->frontAccel, forward);
1394             else
1395                 forward = mulscale8(pPosture->backAccel, forward);
1396             xvel[nSprite] += mulscale30(forward, x);
1397             yvel[nSprite] += mulscale30(forward, y);
1398         }
1399         if (pInput->strafe)
1400         {
1401             int strafe = pInput->strafe;
1402             strafe = mulscale8(pPosture->sideAccel, strafe);
1403             xvel[nSprite] += mulscale30(strafe, y);
1404             yvel[nSprite] -= mulscale30(strafe, x);
1405         }
1406     }
1407     else if (pXSprite->height < 256)
1408     {
1409         int speed = 0x10000;
1410         if (pXSprite->height > 0)
1411             speed -= divscale16(pXSprite->height, 256);
1412         int x = Cos(pSprite->ang);
1413         int y = Sin(pSprite->ang);
1414         if (pInput->forward)
1415         {
1416             int forward = pInput->forward;
1417             if (forward > 0)
1418                 forward = mulscale8(pPosture->frontAccel, forward);
1419             else
1420                 forward = mulscale8(pPosture->backAccel, forward);
1421             if (pXSprite->height)
1422                 forward = mulscale16(forward, speed);
1423             xvel[nSprite] += mulscale30(forward, x);
1424             yvel[nSprite] += mulscale30(forward, y);
1425         }
1426         if (pInput->strafe)
1427         {
1428             int strafe = pInput->strafe;
1429             strafe = mulscale8(pPosture->sideAccel, strafe);
1430             if (pXSprite->height)
1431                 strafe = mulscale16(strafe, speed);
1432             xvel[nSprite] += mulscale30(strafe, y);
1433             yvel[nSprite] -= mulscale30(strafe, x);
1434         }
1435     }
1436     if (pInput->q16turn)
1437         pPlayer->q16ang = (pPlayer->q16ang+pInput->q16turn)&0x7ffffff;
1438     if (pInput->keyFlags.spin180)
1439     {
1440         if (!pPlayer->spin)
1441             pPlayer->spin = -1024;
1442         pInput->keyFlags.spin180 = 0;
1443     }
1444     if (pPlayer->spin < 0)
1445     {
1446         int speed;
1447         if (pPlayer->posture == 1)
1448             speed = 64;
1449         else
1450             speed = 128;
1451         pPlayer->spin = min(pPlayer->spin+speed, 0);
1452         pPlayer->q16ang += fix16_from_int(speed);
1453         if (pPlayer == gMe && numplayers == 1)
1454             gViewAngleAdjust += float(speed);
1455     }
1456     if (pPlayer == gMe && numplayers == 1)
1457         gViewAngleAdjust += float(pSprite->ang - pPlayer->angold);
1458     pPlayer->q16ang = (pPlayer->q16ang+fix16_from_int(pSprite->ang-pPlayer->angold))&0x7ffffff;
1459     pPlayer->angold = pSprite->ang = fix16_to_int(pPlayer->q16ang);
1460     if (!pInput->buttonFlags.jump)
1461         pPlayer->cantJump = 0;
1462 
1463     switch (pPlayer->posture) {
1464     case 1:
1465         if (pInput->buttonFlags.jump)
1466             zvel[nSprite] -= pPosture->normalJumpZ;//0x5b05;
1467         if (pInput->buttonFlags.crouch)
1468             zvel[nSprite] += pPosture->normalJumpZ;//0x5b05;
1469         break;
1470     case 2:
1471         if (!pInput->buttonFlags.crouch)
1472             pPlayer->posture = 0;
1473         break;
1474     default:
1475         if (!pPlayer->cantJump && pInput->buttonFlags.jump && pXSprite->height == 0) {
1476             #ifdef NOONE_EXTENSIONS
1477             if ((packItemActive(pPlayer, 4) && pPosture->pwupJumpZ != 0) || pPosture->normalJumpZ != 0)
1478             #endif
1479                 sfxPlay3DSound(pSprite, 700, 0, 0);
1480 
1481             if (packItemActive(pPlayer, 4)) zvel[nSprite] = pPosture->pwupJumpZ; //-0x175555;
1482             else zvel[nSprite] = pPosture->normalJumpZ; //-0xbaaaa;
1483             pPlayer->cantJump = 1;
1484         }
1485 
1486         if (pInput->buttonFlags.crouch)
1487             pPlayer->posture = 2;
1488         break;
1489     }
1490     if (pInput->keyFlags.action)
1491     {
1492         int a2, a3;
1493         int hit = ActionScan(pPlayer, &a2, &a3);
1494         switch (hit)
1495         {
1496         case 6:
1497             if (a3 > 0 && a3 < kMaxXSectors)
1498             {
1499                 XSECTOR *pXSector = &xsector[a3];
1500                 int key = pXSector->Key;
1501                 if (pXSector->locked && pPlayer == gMe)
1502                 {
1503                     viewSetMessage("It's locked");
1504                     sndStartSample(3062, 255, 2, 0);
1505                 }
1506                 if (!key || pPlayer->hasKey[key])
1507                     trTriggerSector(a2, pXSector, kCmdSpritePush);
1508                 else if (pPlayer == gMe)
1509                 {
1510                     viewSetMessage("That requires a key.");
1511                     sndStartSample(3063, 255, 2, 0);
1512                 }
1513             }
1514             break;
1515         case 0:
1516         {
1517             XWALL *pXWall = &xwall[a3];
1518             int key = pXWall->key;
1519             if (pXWall->locked && pPlayer == gMe)
1520             {
1521                 viewSetMessage("It's locked");
1522                 sndStartSample(3062, 255, 2, 0);
1523             }
1524             if (!key || pPlayer->hasKey[key])
1525                 trTriggerWall(a2, pXWall, kCmdWallPush);
1526             else if (pPlayer == gMe)
1527             {
1528                 viewSetMessage("That requires a key.");
1529                 sndStartSample(3063, 255, 2, 0);
1530             }
1531             break;
1532         }
1533         case 3:
1534         {
1535             XSPRITE *pXSprite = &xsprite[a3];
1536             int key = pXSprite->key;
1537             if (pXSprite->locked && pPlayer == gMe && pXSprite->lockMsg)
1538                 trTextOver(pXSprite->lockMsg);
1539             if (!key || pPlayer->hasKey[key])
1540                 trTriggerSprite(a2, pXSprite, kCmdSpritePush);
1541             else if (pPlayer == gMe)
1542             {
1543                 viewSetMessage("That requires a key.");
1544                 sndStartSample(3063, 255, 2, 0);
1545             }
1546             break;
1547         }
1548         }
1549         if (pPlayer->handTime > 0)
1550             pPlayer->handTime = ClipLow(pPlayer->handTime-4*(6-gGameOptions.nDifficulty), 0);
1551         if (pPlayer->handTime <= 0 && pPlayer->hand)
1552         {
1553             spritetype *pSprite2 = actSpawnDude(pPlayer->pSprite, kDudeHand, pPlayer->pSprite->clipdist<<1, 0);
1554             pSprite2->ang = (pPlayer->pSprite->ang+1024)&2047;
1555             int nSprite = pPlayer->pSprite->index;
1556             int x = Cos(pPlayer->pSprite->ang)>>16;
1557             int y = Sin(pPlayer->pSprite->ang)>>16;
1558             xvel[pSprite2->index] = xvel[nSprite] + mulscale14(0x155555, x);
1559             yvel[pSprite2->index] = yvel[nSprite] + mulscale14(0x155555, y);
1560             zvel[pSprite2->index] = zvel[nSprite];
1561             pPlayer->hand = 0;
1562         }
1563         pInput->keyFlags.action = 0;
1564     }
1565     if (bVanilla)
1566     {
1567         if (pInput->keyFlags.lookCenter && !pInput->buttonFlags.lookUp && !pInput->buttonFlags.lookDown)
1568         {
1569             if (pPlayer->q16look < 0)
1570                 pPlayer->q16look = fix16_min(pPlayer->q16look+F16(4), F16(0));
1571             if (pPlayer->q16look > 0)
1572                 pPlayer->q16look = fix16_max(pPlayer->q16look-F16(4), F16(0));
1573             if (!pPlayer->q16look)
1574                 pInput->keyFlags.lookCenter = 0;
1575         }
1576         else
1577         {
1578             if (pInput->buttonFlags.lookUp)
1579                 pPlayer->q16look = fix16_min(pPlayer->q16look+F16(4), F16(60));
1580             if (pInput->buttonFlags.lookDown)
1581                 pPlayer->q16look = fix16_max(pPlayer->q16look-F16(4), F16(-60));
1582         }
1583         pPlayer->q16look = fix16_clamp(pPlayer->q16look+pInput->q16mlook, F16(-60), F16(60));
1584         if (pPlayer->q16look > 0)
1585             pPlayer->q16horiz = fix16_from_int(mulscale30(120, Sin(fix16_to_int(pPlayer->q16look)<<3)));
1586         else if (pPlayer->q16look < 0)
1587             pPlayer->q16horiz = fix16_from_int(mulscale30(180, Sin(fix16_to_int(pPlayer->q16look)<<3)));
1588         else
1589             pPlayer->q16horiz = 0;
1590     }
1591     else
1592     {
1593         CONSTEXPR int upAngle = 289;
1594         CONSTEXPR int downAngle = -347;
1595         CONSTEXPR double lookStepUp = 4.0*upAngle/60.0;
1596         CONSTEXPR double lookStepDown = -4.0*downAngle/60.0;
1597         if (pInput->keyFlags.lookCenter && !pInput->buttonFlags.lookUp && !pInput->buttonFlags.lookDown)
1598         {
1599             if (pPlayer->q16look < 0)
1600                 pPlayer->q16look = fix16_min(pPlayer->q16look+F16(lookStepDown), F16(0));
1601             if (pPlayer->q16look > 0)
1602                 pPlayer->q16look = fix16_max(pPlayer->q16look-F16(lookStepUp), F16(0));
1603             if (!pPlayer->q16look)
1604                 pInput->keyFlags.lookCenter = 0;
1605         }
1606         else
1607         {
1608             if (pInput->buttonFlags.lookUp)
1609                 pPlayer->q16look = fix16_min(pPlayer->q16look+F16(lookStepUp), F16(upAngle));
1610             if (pInput->buttonFlags.lookDown)
1611                 pPlayer->q16look = fix16_max(pPlayer->q16look-F16(lookStepDown), F16(downAngle));
1612         }
1613         if (pPlayer == gMe && numplayers == 1)
1614         {
1615             if (pInput->buttonFlags.lookUp)
1616             {
1617                 gViewLookAdjust += float(lookStepUp);
1618             }
1619             if (pInput->buttonFlags.lookDown)
1620             {
1621                 gViewLookAdjust -= float(lookStepDown);
1622             }
1623             gViewLookRecenter = pInput->keyFlags.lookCenter && !pInput->buttonFlags.lookUp && !pInput->buttonFlags.lookDown;
1624         }
1625         pPlayer->q16look = fix16_clamp(pPlayer->q16look+(pInput->q16mlook<<3), F16(downAngle), F16(upAngle));
1626         pPlayer->q16horiz = fix16_from_float(100.f*tanf(fix16_to_float(pPlayer->q16look)*fPI/1024.f));
1627     }
1628     int nSector = pSprite->sectnum;
1629     int florhit = gSpriteHit[pSprite->extra].florhit & 0xc000;
1630     char va;
1631     if (pXSprite->height < 16 && (florhit == 0x4000 || florhit == 0))
1632         va = 1;
1633     else
1634         va = 0;
1635     if (va && (sector[nSector].floorstat&2))
1636     {
1637         int z1 = getflorzofslope(nSector, pSprite->x, pSprite->y);
1638         int x2 = pSprite->x+mulscale30(64, Cos(pSprite->ang));
1639         int y2 = pSprite->y+mulscale30(64, Sin(pSprite->ang));
1640         short nSector2 = nSector;
1641         updatesector(x2, y2, &nSector2);
1642         if (nSector2 == nSector)
1643         {
1644             int z2 = getflorzofslope(nSector2, x2, y2);
1645             pPlayer->q16slopehoriz = interpolate(pPlayer->q16slopehoriz, fix16_from_int(z1-z2)>>3, 0x4000);
1646         }
1647     }
1648     else
1649     {
1650         pPlayer->q16slopehoriz = interpolate(pPlayer->q16slopehoriz, F16(0), 0x4000);
1651         if (klabs(pPlayer->q16slopehoriz) < 4)
1652             pPlayer->q16slopehoriz = 0;
1653     }
1654     pPlayer->slope = (-fix16_to_int(pPlayer->q16horiz))<<7;
1655     if (pInput->keyFlags.prevItem)
1656     {
1657         pInput->keyFlags.prevItem = 0;
1658         packPrevItem(pPlayer);
1659     }
1660     if (pInput->keyFlags.nextItem)
1661     {
1662         pInput->keyFlags.nextItem = 0;
1663         packNextItem(pPlayer);
1664     }
1665     if (pInput->keyFlags.useItem)
1666     {
1667         pInput->keyFlags.useItem = 0;
1668         if (pPlayer->packSlots[pPlayer->packItemId].curAmount > 0)
1669             packUseItem(pPlayer, pPlayer->packItemId);
1670     }
1671     if (pInput->useFlags.useBeastVision)
1672     {
1673         pInput->useFlags.useBeastVision = 0;
1674         if (pPlayer->packSlots[3].curAmount > 0)
1675             packUseItem(pPlayer, 3);
1676     }
1677     if (pInput->useFlags.useCrystalBall)
1678     {
1679         pInput->useFlags.useCrystalBall = 0;
1680         if (pPlayer->packSlots[2].curAmount > 0)
1681             packUseItem(pPlayer, 2);
1682     }
1683     if (pInput->useFlags.useJumpBoots)
1684     {
1685         pInput->useFlags.useJumpBoots = 0;
1686         if (pPlayer->packSlots[4].curAmount > 0)
1687             packUseItem(pPlayer, 4);
1688     }
1689     if (pInput->useFlags.useMedKit)
1690     {
1691         pInput->useFlags.useMedKit = 0;
1692         if (pPlayer->packSlots[0].curAmount > 0)
1693             packUseItem(pPlayer, 0);
1694     }
1695     if (pInput->keyFlags.holsterWeapon)
1696     {
1697         pInput->keyFlags.holsterWeapon = 0;
1698         if (pPlayer->curWeapon)
1699         {
1700             WeaponLower(pPlayer);
1701             viewSetMessage("Holstering weapon");
1702         }
1703     }
1704     CheckPickUp(pPlayer);
1705 }
1706 
playerProcess(PLAYER * pPlayer)1707 void playerProcess(PLAYER *pPlayer)
1708 {
1709     spritetype *pSprite = pPlayer->pSprite;
1710     int nSprite = pPlayer->nSprite;
1711     int nXSprite = pSprite->extra;
1712     XSPRITE *pXSprite = pPlayer->pXSprite;
1713     POSTURE* pPosture = &pPlayer->pPosture[pPlayer->lifeMode][pPlayer->posture];
1714     powerupProcess(pPlayer);
1715     int top, bottom;
1716     GetSpriteExtents(pSprite, &top, &bottom);
1717     int dzb = (bottom-pSprite->z)/4;
1718     int dzt = (pSprite->z-top)/4;
1719     int dw = pSprite->clipdist<<2;
1720     if (!gNoClip)
1721     {
1722         short nSector = pSprite->sectnum;
1723         if (pushmove_old(&pSprite->x, &pSprite->y, &pSprite->z, &nSector, dw, dzt, dzb, CLIPMASK0) == -1)
1724             actDamageSprite(nSprite, pSprite, kDamageFall, 500<<4);
1725         if (pSprite->sectnum != nSector)
1726         {
1727             if (nSector == -1)
1728             {
1729                 nSector = pSprite->sectnum;
1730                 actDamageSprite(nSprite, pSprite, kDamageFall, 500<<4);
1731             }
1732             dassert(nSector >= 0 && nSector < kMaxSectors);
1733             ChangeSpriteSect(nSprite, nSector);
1734         }
1735     }
1736     ProcessInput(pPlayer);
1737     int nSpeed = approxDist(xvel[nSprite], yvel[nSprite]);
1738     pPlayer->zViewVel = interpolate(pPlayer->zViewVel, zvel[nSprite], 0x7000);
1739     int dz = pPlayer->pSprite->z-pPosture->eyeAboveZ-pPlayer->zView;
1740     if (dz > 0)
1741         pPlayer->zViewVel += mulscale16(dz<<8, 0xa000);
1742     else
1743         pPlayer->zViewVel += mulscale16(dz<<8, 0x1800);
1744     pPlayer->zView += pPlayer->zViewVel>>8;
1745     pPlayer->zWeaponVel = interpolate(pPlayer->zWeaponVel, zvel[nSprite], 0x5000);
1746     dz = pPlayer->pSprite->z-pPosture->weaponAboveZ-pPlayer->zWeapon;
1747     if (dz > 0)
1748         pPlayer->zWeaponVel += mulscale16(dz<<8, 0x8000);
1749     else
1750         pPlayer->zWeaponVel += mulscale16(dz<<8, 0xc00);
1751     pPlayer->zWeapon += pPlayer->zWeaponVel>>8;
1752     pPlayer->bobPhase = ClipLow(pPlayer->bobPhase-4, 0);
1753     nSpeed >>= 16;
1754     if (pPlayer->posture == 1)
1755     {
1756         pPlayer->bobAmp = (pPlayer->bobAmp+17)&2047;
1757         pPlayer->swayAmp = (pPlayer->swayAmp+17)&2047;
1758         pPlayer->bobHeight = mulscale30(pPosture->bobV*10, Sin(pPlayer->bobAmp*2));
1759         pPlayer->bobWidth = mulscale30(pPosture->bobH*pPlayer->bobPhase, Sin(pPlayer->bobAmp-256));
1760         pPlayer->swayHeight = mulscale30(pPosture->swayV*pPlayer->bobPhase, Sin(pPlayer->swayAmp*2));
1761         pPlayer->swayWidth = mulscale30(pPosture->swayH*pPlayer->bobPhase, Sin(pPlayer->swayAmp-0x155));
1762     }
1763     else
1764     {
1765         if (pXSprite->height < 256)
1766         {
1767             pPlayer->bobAmp = (pPlayer->bobAmp+pPosture->pace[pPlayer->isRunning]*4) & 2047;
1768             pPlayer->swayAmp = (pPlayer->swayAmp+(pPosture->pace[pPlayer->isRunning]*4)/2) & 2047;
1769             if (pPlayer->isRunning)
1770             {
1771                 if (pPlayer->bobPhase < 60)
1772                     pPlayer->bobPhase = ClipHigh(pPlayer->bobPhase+nSpeed, 60);
1773             }
1774             else
1775             {
1776                 if (pPlayer->bobPhase < 30)
1777                     pPlayer->bobPhase = ClipHigh(pPlayer->bobPhase+nSpeed, 30);
1778             }
1779         }
1780         pPlayer->bobHeight = mulscale30(pPosture->bobV*pPlayer->bobPhase, Sin(pPlayer->bobAmp*2));
1781         pPlayer->bobWidth = mulscale30(pPosture->bobH*pPlayer->bobPhase, Sin(pPlayer->bobAmp-256));
1782         pPlayer->swayHeight = mulscale30(pPosture->swayV*pPlayer->bobPhase, Sin(pPlayer->swayAmp*2));
1783         pPlayer->swayWidth = mulscale30(pPosture->swayH*pPlayer->bobPhase, Sin(pPlayer->swayAmp-0x155));
1784     }
1785     pPlayer->flickerEffect = 0;
1786     pPlayer->quakeEffect = ClipLow(pPlayer->quakeEffect-4, 0);
1787     pPlayer->tiltEffect = ClipLow(pPlayer->tiltEffect-4, 0);
1788     pPlayer->visibility = ClipLow(pPlayer->visibility-4, 0);
1789     pPlayer->painEffect = ClipLow(pPlayer->painEffect-4, 0);
1790     pPlayer->blindEffect = ClipLow(pPlayer->blindEffect-4, 0);
1791     pPlayer->pickupEffect = ClipLow(pPlayer->pickupEffect-4, 0);
1792     if (pPlayer == gMe && gMe->pXSprite->health == 0)
1793         pPlayer->hand = 0;
1794     if (!pXSprite->health)
1795         return;
1796     pPlayer->isUnderwater = 0;
1797     if (pPlayer->posture == 1)
1798     {
1799         pPlayer->isUnderwater = 1;
1800         int nSector = pSprite->sectnum;
1801         int nLink = gLowerLink[nSector];
1802         if (nLink > 0 && (sprite[nLink].type == kMarkerLowGoo || sprite[nLink].type == kMarkerLowWater))
1803         {
1804             if (getceilzofslope(nSector, pSprite->x, pSprite->y) > pPlayer->zView)
1805                 pPlayer->isUnderwater = 0;
1806         }
1807     }
1808     if (!pPlayer->isUnderwater)
1809     {
1810         pPlayer->underwaterTime = 1200;
1811         pPlayer->chokeEffect = 0;
1812         if (packItemActive(pPlayer, 1))
1813             packUseItem(pPlayer, 1);
1814     }
1815     int nType = kDudePlayer1-kDudeBase;
1816     switch (pPlayer->posture)
1817     {
1818     case 1:
1819         seqSpawn(dudeInfo[nType].seqStartID+9, 3, nXSprite, -1);
1820         break;
1821     case 2:
1822         seqSpawn(dudeInfo[nType].seqStartID+10, 3, nXSprite, -1);
1823         break;
1824     default:
1825         if (!nSpeed)
1826             seqSpawn(dudeInfo[nType].seqStartID, 3, nXSprite, -1);
1827         else
1828             seqSpawn(dudeInfo[nType].seqStartID+8, 3, nXSprite, -1);
1829         break;
1830     }
1831 }
1832 
playerFireMissile(PLAYER * pPlayer,int a2,int a3,int a4,int a5,int a6)1833 spritetype *playerFireMissile(PLAYER *pPlayer, int a2, int a3, int a4, int a5, int a6)
1834 {
1835     return actFireMissile(pPlayer->pSprite, a2, pPlayer->zWeapon-pPlayer->pSprite->z, a3, a4, a5, a6);
1836 }
1837 
playerFireThing(PLAYER * pPlayer,int a2,int a3,int thingType,int a5)1838 spritetype * playerFireThing(PLAYER *pPlayer, int a2, int a3, int thingType, int a5)
1839 {
1840     dassert(thingType >= kThingBase && thingType < kThingMax);
1841     return actFireThing(pPlayer->pSprite, a2, pPlayer->zWeapon-pPlayer->pSprite->z, pPlayer->slope+a3, thingType, a5);
1842 }
1843 
playerFrag(PLAYER * pKiller,PLAYER * pVictim)1844 void playerFrag(PLAYER *pKiller, PLAYER *pVictim)
1845 {
1846     dassert(pKiller != NULL);
1847     dassert(pVictim != NULL);
1848 
1849     char buffer[128] = "";
1850     int nKiller = pKiller->pSprite->type-kDudePlayer1;
1851     dassert(nKiller >= 0 && nKiller < kMaxPlayers);
1852     int nVictim = pVictim->pSprite->type-kDudePlayer1;
1853     dassert(nVictim >= 0 && nVictim < kMaxPlayers);
1854     if (myconnectindex == connecthead)
1855     {
1856         sprintf(buffer, "frag %d killed %d\n", pKiller->nPlayer+1, pVictim->nPlayer+1);
1857         sub_7AC28(buffer);
1858         buffer[0] = 0;
1859     }
1860     if (nKiller == nVictim)
1861     {
1862         pVictim->fraggerId = -1;
1863         if (VanillaMode() || gGameOptions.nGameType != 1)
1864         {
1865             pVictim->fragCount--;
1866             pVictim->fragInfo[nVictim]--;
1867         }
1868         if (gGameOptions.nGameType == 3)
1869             dword_21EFB0[pVictim->teamId]--;
1870         int nMessage = Random(5);
1871         int nSound = gSuicide[nMessage].at4;
1872         if (pVictim == gMe && gMe->handTime <= 0)
1873         {
1874             sprintf(buffer, "You killed yourself!");
1875             if (gGameOptions.nGameType > 0 && nSound >= 0)
1876                 sndStartSample(nSound, 255, 2, 0);
1877         }
1878         else
1879         {
1880             sprintf(buffer, gSuicide[nMessage].at0, gProfile[nVictim].name);
1881         }
1882     }
1883     else
1884     {
1885         if (VanillaMode() || gGameOptions.nGameType != 1)
1886         {
1887             pKiller->fragCount++;
1888             pKiller->fragInfo[nKiller]++;
1889         }
1890         if (gGameOptions.nGameType == 3)
1891         {
1892             if (pKiller->teamId == pVictim->teamId)
1893                 dword_21EFB0[pKiller->teamId]--;
1894             else
1895             {
1896                 dword_21EFB0[pKiller->teamId]++;
1897                 dword_21EFD0[pKiller->teamId]+=120;
1898             }
1899         }
1900         int nMessage = Random(25);
1901         int nSound = gVictory[nMessage].at4;
1902         const char* pzMessage = gVictory[nMessage].at0;
1903         sprintf(buffer, pzMessage, gProfile[nKiller].name, gProfile[nVictim].name);
1904         if (gGameOptions.nGameType > 0 && nSound >= 0 && pKiller == gMe)
1905             sndStartSample(nSound, 255, 2, 0);
1906     }
1907     viewSetMessage(buffer);
1908 }
1909 
FragPlayer(PLAYER * pPlayer,int nSprite)1910 void FragPlayer(PLAYER *pPlayer, int nSprite)
1911 {
1912     spritetype *pSprite = NULL;
1913     if (nSprite >= 0)
1914         pSprite = &sprite[nSprite];
1915     if (pSprite && IsPlayerSprite(pSprite))
1916     {
1917         PLAYER *pKiller = &gPlayer[pSprite->type - kDudePlayer1];
1918         playerFrag(pKiller, pPlayer);
1919         int nTeam1 = pKiller->teamId&1;
1920         int nTeam2 = pPlayer->teamId&1;
1921         if (nTeam1 == 0)
1922         {
1923             if (nTeam1 != nTeam2)
1924                 evSend(0, 0, 15, kCmdToggle);
1925             else
1926                 evSend(0, 0, 16, kCmdToggle);
1927         }
1928         else
1929         {
1930             if (nTeam1 == nTeam2)
1931                 evSend(0, 0, 16, kCmdToggle);
1932             else
1933                 evSend(0, 0, 15, kCmdToggle);
1934         }
1935     }
1936 }
1937 
playerDamageArmor(PLAYER * pPlayer,DAMAGE_TYPE nType,int nDamage)1938 int playerDamageArmor(PLAYER *pPlayer, DAMAGE_TYPE nType, int nDamage)
1939 {
1940     DAMAGEINFO *pDamageInfo = &damageInfo[nType];
1941     int nArmorType = pDamageInfo->at0;
1942     if (nArmorType >= 0 && pPlayer->armor[nArmorType])
1943     {
1944 #if 0
1945         int vbp = (nDamage*7)/8-nDamage/4;
1946         int v8 = pPlayer->at33e[nArmorType];
1947         int t = nDamage/4 + vbp * v8 / 3200;
1948         v8 -= t;
1949 #endif
1950         int v8 = pPlayer->armor[nArmorType];
1951         int t = scale(v8, 0, 3200, nDamage/4, (nDamage*7)/8);
1952         v8 -= t;
1953         nDamage -= t;
1954         pPlayer->armor[nArmorType] = ClipLow(v8, 0);
1955     }
1956     return nDamage;
1957 }
1958 
sub_40A94(PLAYER * pPlayer,int a2)1959 spritetype *sub_40A94(PLAYER *pPlayer, int a2)
1960 {
1961     char buffer[80];
1962     spritetype *pSprite = NULL;
1963     switch (a2)
1964     {
1965     case kItemFlagA:
1966         pPlayer->hasFlag &= ~1;
1967         pSprite = actDropObject(pPlayer->pSprite, kItemFlagA);
1968         if (pSprite)
1969             pSprite->owner = pPlayer->used2[0];
1970         gBlueFlagDropped = true;
1971         sprintf(buffer, "%s dropped Blue Flag", gProfile[pPlayer->nPlayer].name);
1972         sndStartSample(8005, 255, 2, 0);
1973         viewSetMessage(buffer);
1974         break;
1975     case kItemFlagB:
1976         pPlayer->hasFlag &= ~2;
1977         pSprite = actDropObject(pPlayer->pSprite, kItemFlagB);
1978         if (pSprite)
1979             pSprite->owner = pPlayer->used2[1];
1980         gRedFlagDropped = true;
1981         sprintf(buffer, "%s dropped Red Flag", gProfile[pPlayer->nPlayer].name);
1982         sndStartSample(8004, 255, 2, 0);
1983         viewSetMessage(buffer);
1984         break;
1985     }
1986     return pSprite;
1987 }
1988 
playerDamageSprite(int nSource,PLAYER * pPlayer,DAMAGE_TYPE nDamageType,int nDamage)1989 int playerDamageSprite(int nSource, PLAYER *pPlayer, DAMAGE_TYPE nDamageType, int nDamage)
1990 {
1991     dassert(nSource < kMaxSprites);
1992     dassert(pPlayer != NULL);
1993     if (pPlayer->damageControl[nDamageType])
1994         return 0;
1995     nDamage = playerDamageArmor(pPlayer, nDamageType, nDamage);
1996     pPlayer->painEffect = ClipHigh(pPlayer->painEffect+(nDamage>>3), 600);
1997 
1998     spritetype *pSprite = pPlayer->pSprite;
1999     XSPRITE *pXSprite = pPlayer->pXSprite;
2000     int nXSprite = pSprite->extra;
2001     int nXSector = sector[pSprite->sectnum].extra;
2002     DUDEINFO *pDudeInfo = getDudeInfo(pSprite->type);
2003     int nDeathSeqID = -1;
2004     int nKneelingPlayer = -1;
2005     int nSprite = pSprite->index;
2006     char va = playerSeqPlaying(pPlayer, 16);
2007     if (!pXSprite->health)
2008     {
2009         if (va)
2010         {
2011             switch (nDamageType)
2012             {
2013             case kDamageSpirit:
2014                 nDeathSeqID = 18;
2015                 sfxPlay3DSound(pSprite, 716, 0, 0);
2016                 break;
2017             case kDamageExplode:
2018                 GibSprite(pSprite, GIBTYPE_7, NULL, NULL);
2019                 GibSprite(pSprite, GIBTYPE_15, NULL, NULL);
2020                 pPlayer->pSprite->cstat |= 32768;
2021                 nDeathSeqID = 17;
2022                 break;
2023             default:
2024             {
2025                 int top, bottom;
2026                 GetSpriteExtents(pSprite, &top, &bottom);
2027                 CGibPosition gibPos(pSprite->x, pSprite->y, top);
2028                 CGibVelocity gibVel(xvel[pSprite->index]>>1, yvel[pSprite->index]>>1, -0xccccc);
2029                 GibSprite(pSprite, GIBTYPE_27, &gibPos, &gibVel);
2030                 GibSprite(pSprite, GIBTYPE_7, NULL, NULL);
2031                 fxSpawnBlood(pSprite, nDamage<<4);
2032                 fxSpawnBlood(pSprite, nDamage<<4);
2033                 nDeathSeqID = 17;
2034                 break;
2035             }
2036             }
2037         }
2038     }
2039     else
2040     {
2041         int nHealth = pXSprite->health-nDamage;
2042         pXSprite->health = ClipLow(nHealth, 0);
2043         if (pXSprite->health > 0 && pXSprite->health < 16)
2044         {
2045             nDamageType = kDamageBullet;
2046             pXSprite->health = 0;
2047             nHealth = -25;
2048         }
2049         if (pXSprite->health > 0)
2050         {
2051             DAMAGEINFO *pDamageInfo = &damageInfo[nDamageType];
2052             int nSound;
2053             if (nDamage >= (10<<4))
2054                 nSound = pDamageInfo->at4[0];
2055             else
2056                 nSound = pDamageInfo->at4[Random(3)];
2057             if (nDamageType == kDamageDrown && pXSprite->medium == kMediumWater && !pPlayer->hand)
2058                 nSound = 714;
2059             sfxPlay3DSound(pSprite, nSound, 0, 6);
2060             return nDamage;
2061         }
2062         sfxKill3DSound(pPlayer->pSprite, -1, 441);
2063         if (gGameOptions.nGameType == 3 && pPlayer->hasFlag) {
2064             if (pPlayer->hasFlag&1) sub_40A94(pPlayer, kItemFlagA);
2065             if (pPlayer->hasFlag&2) sub_40A94(pPlayer, kItemFlagB);
2066         }
2067         pPlayer->deathTime = 0;
2068         pPlayer->qavLoop = 0;
2069         pPlayer->curWeapon = 0;
2070         pPlayer->fraggerId = nSource;
2071         pPlayer->voodooTargets = 0;
2072         if (nDamageType == kDamageExplode && nDamage < (9<<4))
2073             nDamageType = kDamageFall;
2074         switch (nDamageType)
2075         {
2076         case kDamageExplode:
2077             sfxPlay3DSound(pSprite, 717, 0, 0);
2078             GibSprite(pSprite, GIBTYPE_7, NULL, NULL);
2079             GibSprite(pSprite, GIBTYPE_15, NULL, NULL);
2080             pPlayer->pSprite->cstat |= 32768;
2081             nDeathSeqID = 2;
2082             break;
2083         case kDamageBurn:
2084             sfxPlay3DSound(pSprite, 718, 0, 0);
2085             nDeathSeqID = 3;
2086             break;
2087         case kDamageDrown:
2088             nDeathSeqID = 1;
2089             break;
2090         default:
2091             if (nHealth < -20 && gGameOptions.nGameType >= 2 && Chance(0x4000))
2092             {
2093                 DAMAGEINFO *pDamageInfo = &damageInfo[nDamageType];
2094                 sfxPlay3DSound(pSprite, pDamageInfo->at10[0], 0, 2);
2095                 nDeathSeqID = 16;
2096                 nKneelingPlayer = nPlayerKneelClient;
2097                 powerupActivate(pPlayer, kPwUpDeliriumShroom);
2098                 pXSprite->target = nSource;
2099                 evPost(pSprite->index, 3, 15, kCallbackFinishHim);
2100             }
2101             else
2102             {
2103                 sfxPlay3DSound(pSprite, 716, 0, 0);
2104                 nDeathSeqID = 1;
2105             }
2106             break;
2107         }
2108     }
2109     if (nDeathSeqID < 0)
2110         return nDamage;
2111     if (nDeathSeqID != 16)
2112     {
2113         powerupClear(pPlayer);
2114         if (nXSector > 0 && xsector[nXSector].Exit)
2115             trTriggerSector(pSprite->sectnum, &xsector[nXSector], kCmdSectorExit);
2116         pSprite->flags |= 7;
2117         for (int p = connecthead; p >= 0; p = connectpoint2[p])
2118         {
2119             if (gPlayer[p].fraggerId == nSprite && gPlayer[p].deathTime > 0)
2120                 gPlayer[p].fraggerId = -1;
2121         }
2122         FragPlayer(pPlayer, nSource);
2123         trTriggerSprite(nSprite, pXSprite, kCmdOff);
2124 
2125         #ifdef NOONE_EXTENSIONS
2126         // allow drop items and keys in multiplayer
2127         if (gModernMap && gGameOptions.nGameType != 0 && pPlayer->pXSprite->health <= 0) {
2128 
2129             spritetype* pItem = NULL;
2130             if (pPlayer->pXSprite->dropMsg && (pItem = actDropItem(pPlayer->pSprite, pPlayer->pXSprite->dropMsg)) != NULL)
2131                 evPost(pItem->index, OBJ_SPRITE, 500, kCallbackRemove);
2132 
2133             if (pPlayer->pXSprite->key) {
2134 
2135                 int i; // if all players have this key, don't drop it
2136                 for (i = connecthead; i >= 0; i = connectpoint2[i]) {
2137                     if (!gPlayer[i].hasKey[pPlayer->pXSprite->key])
2138                         break;
2139                 }
2140 
2141                 if (i == 0 && (pItem = actDropKey(pPlayer->pSprite, (pPlayer->pXSprite->key + kItemKeyBase) - 1)) != NULL)
2142                     evPost(pItem->index, OBJ_SPRITE, 500, kCallbackRemove);
2143 
2144             }
2145 
2146 
2147         }
2148         #endif
2149 
2150     }
2151     dassert(gSysRes.Lookup(pDudeInfo->seqStartID + nDeathSeqID, "SEQ") != NULL);
2152     seqSpawn(pDudeInfo->seqStartID+nDeathSeqID, 3, nXSprite, nKneelingPlayer);
2153     return nDamage;
2154 }
2155 
UseAmmo(PLAYER * pPlayer,int nAmmoType,int nDec)2156 int UseAmmo(PLAYER *pPlayer, int nAmmoType, int nDec)
2157 {
2158     if (gInfiniteAmmo)
2159         return 9999;
2160     if (nAmmoType == -1)
2161         return 9999;
2162     pPlayer->ammoCount[nAmmoType] = ClipLow(pPlayer->ammoCount[nAmmoType]-nDec, 0);
2163     return pPlayer->ammoCount[nAmmoType];
2164 }
2165 
sub_41250(PLAYER * pPlayer)2166 void sub_41250(PLAYER *pPlayer)
2167 {
2168     int v4 = pPlayer->aim.dz;
2169     int dz = pPlayer->zWeapon-pPlayer->pSprite->z;
2170     if (UseAmmo(pPlayer, 9, 0) < 8)
2171     {
2172         pPlayer->voodooTargets = 0;
2173         return;
2174     }
2175     for (int i = 0; i < 4; i++)
2176     {
2177         int ang1 = (pPlayer->voodooVar1+pPlayer->vodooVar2)&2047;
2178         actFireVector(pPlayer->pSprite, 0, dz, Cos(ang1)>>16, Sin(ang1)>>16, v4, kVectorVoodoo10);
2179         int ang2 = (pPlayer->voodooVar1+2048-pPlayer->vodooVar2)&2047;
2180         actFireVector(pPlayer->pSprite, 0, dz, Cos(ang2)>>16, Sin(ang2)>>16, v4, kVectorVoodoo10);
2181     }
2182     pPlayer->voodooTargets = ClipLow(pPlayer->voodooTargets-1, 0);
2183 }
2184 
playerLandingSound(PLAYER * pPlayer)2185 void playerLandingSound(PLAYER *pPlayer)
2186 {
2187     static int surfaceSound[] = {
2188         -1,
2189         600,
2190         601,
2191         602,
2192         603,
2193         604,
2194         605,
2195         605,
2196         605,
2197         600,
2198         605,
2199         605,
2200         605,
2201         604,
2202         603
2203     };
2204     spritetype *pSprite = pPlayer->pSprite;
2205     SPRITEHIT *pHit = &gSpriteHit[pSprite->extra];
2206     if (pHit->florhit)
2207     {
2208         if (!gGameOptions.bFriendlyFire && IsTargetTeammate(pPlayer, &sprite[pHit->florhit & 0x3fff]))
2209             return;
2210         char nSurf = tileGetSurfType(pHit->florhit);
2211         if (nSurf)
2212             sfxPlay3DSound(pSprite, surfaceSound[nSurf], -1, 0);
2213     }
2214 }
2215 
PlayerSurvive(int,int nXSprite)2216 void PlayerSurvive(int, int nXSprite)
2217 {
2218     char buffer[80];
2219     XSPRITE *pXSprite = &xsprite[nXSprite];
2220     int nSprite = pXSprite->reference;
2221     spritetype *pSprite = &sprite[nSprite];
2222     actHealDude(pXSprite, 1, 2);
2223     if (gGameOptions.nGameType > 0 && numplayers > 1)
2224     {
2225         sfxPlay3DSound(pSprite, 3009, 0, 6);
2226         if (IsPlayerSprite(pSprite))
2227         {
2228             PLAYER *pPlayer = &gPlayer[pSprite->type-kDudePlayer1];
2229             if (pPlayer == gMe)
2230                 viewSetMessage("I LIVE...AGAIN!!");
2231             else
2232             {
2233                 sprintf(buffer, "%s lives again!", gProfile[pPlayer->nPlayer].name);
2234                 viewSetMessage(buffer);
2235             }
2236             pPlayer->input.newWeapon = 1;
2237         }
2238     }
2239 }
2240 
PlayerKneelsOver(int,int nXSprite)2241 void PlayerKneelsOver(int, int nXSprite)
2242 {
2243     XSPRITE *pXSprite = &xsprite[nXSprite];
2244     for (int p = connecthead; p >= 0; p = connectpoint2[p])
2245     {
2246         if (gPlayer[p].pXSprite == pXSprite)
2247         {
2248             PLAYER *pPlayer = &gPlayer[p];
2249             playerDamageSprite(pPlayer->fraggerId, pPlayer, kDamageSpirit, 500<<4);
2250             return;
2251         }
2252     }
2253 }
2254 
2255 class PlayerLoadSave : public LoadSave
2256 {
2257 public:
2258     virtual void Load(void);
2259     virtual void Save(void);
2260 };
2261 
Load(void)2262 void PlayerLoadSave::Load(void)
2263 {
2264 
2265     Read(dword_21EFB0, sizeof(dword_21EFB0));
2266     Read(&gNetPlayers, sizeof(gNetPlayers));
2267     Read(&gProfile, sizeof(gProfile));
2268     Read(&gPlayer, sizeof(gPlayer));
2269     #ifdef NOONE_EXTENSIONS
2270         Read(&gPlayerCtrl, sizeof(gPlayerCtrl));
2271     #endif
2272     for (int i = 0; i < gNetPlayers; i++) {
2273         gPlayer[i].pSprite = &sprite[gPlayer[i].nSprite];
2274         gPlayer[i].pXSprite = &xsprite[gPlayer[i].pSprite->extra];
2275         gPlayer[i].pDudeInfo = &dudeInfo[gPlayer[i].pSprite->type-kDudeBase];
2276 
2277     #ifdef NOONE_EXTENSIONS
2278         // load qav scene
2279         if (gPlayer[i].sceneQav != -1) {
2280             if (gPlayerCtrl[i].qavScene.qavResrc == NULL)
2281                 gPlayer[i].sceneQav = -1;
2282             else {
2283                 QAV* pQav = playerQavSceneLoad(gPlayer[i].sceneQav);
2284                 if (pQav) {
2285                     gPlayerCtrl[i].qavScene.qavResrc = pQav;
2286                     gPlayerCtrl[i].qavScene.qavResrc->Preload();
2287                 } else {
2288                     gPlayer[i].sceneQav = -1;
2289                 }
2290             }
2291         }
2292     #endif
2293 
2294     }
2295 }
2296 
Save(void)2297 void PlayerLoadSave::Save(void)
2298 {
2299     Write(dword_21EFB0, sizeof(dword_21EFB0));
2300     Write(&gNetPlayers, sizeof(gNetPlayers));
2301     Write(&gProfile, sizeof(gProfile));
2302     Write(&gPlayer, sizeof(gPlayer));
2303 
2304     #ifdef NOONE_EXTENSIONS
2305     Write(&gPlayerCtrl, sizeof(gPlayerCtrl));
2306     #endif
2307 }
2308 
2309 static PlayerLoadSave *myLoadSave;
2310 
PlayerLoadSaveConstruct(void)2311 void PlayerLoadSaveConstruct(void)
2312 {
2313     myLoadSave = new PlayerLoadSave();
2314 }
2315