1 // Emacs style mode select	 -*- C++ -*-
2 //-----------------------------------------------------------------------------
3 //
4 // $Id:$
5 //
6 // Copyright (C) 1993-1996 by id Software, Inc.
7 //
8 // This source is available for distribution and/or modification
9 // only under the terms of the DOOM Source Code License as
10 // published by id Software. All rights reserved.
11 //
12 // The source is distributed in the hope that it will be useful,
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of
14 // FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License
15 // for more details.
16 //
17 // $Log:$
18 //
19 // DESCRIPTION:
20 //		Player related stuff.
21 //		Bobbing POV/weapon, movement.
22 //		Pending weapon.
23 //
24 //-----------------------------------------------------------------------------
25 
26 #include "templates.h"
27 #include "doomdef.h"
28 #include "d_event.h"
29 #include "p_local.h"
30 #include "doomstat.h"
31 #include "s_sound.h"
32 #include "i_system.h"
33 #include "gi.h"
34 #include "m_random.h"
35 #include "p_pspr.h"
36 #include "p_enemy.h"
37 #include "s_sound.h"
38 #include "a_sharedglobal.h"
39 #include "a_keys.h"
40 #include "statnums.h"
41 #include "v_palette.h"
42 #include "v_video.h"
43 #include "w_wad.h"
44 #include "cmdlib.h"
45 #include "sbar.h"
46 #include "intermission/intermission.h"
47 #include "c_console.h"
48 #include "doomdef.h"
49 #include "c_dispatch.h"
50 #include "tarray.h"
51 #include "thingdef/thingdef.h"
52 #include "g_level.h"
53 #include "d_net.h"
54 #include "gstrings.h"
55 #include "farchive.h"
56 #include "r_renderer.h"
57 
58 static FRandom pr_skullpop ("SkullPop");
59 
60 // [RH] # of ticks to complete a turn180
61 #define TURN180_TICKS	((TICRATE / 4) + 1)
62 
63 // Variables for prediction
64 CVAR (Bool, cl_noprediction, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
65 CVAR(Bool, cl_predict_specials, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
66 
67 CUSTOM_CVAR(Float, cl_predict_lerpscale, 0.05f, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
68 {
69 	P_PredictionLerpReset();
70 }
71 CUSTOM_CVAR(Float, cl_predict_lerpthreshold, 2.00f, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
72 {
73 	if (self < 0.1f)
74 		self = 0.1f;
75 	P_PredictionLerpReset();
76 }
77 
78 struct PredictPos
79 {
80 	int gametic;
81 	fixed_t x;
82 	fixed_t y;
83 	fixed_t z;
84 	fixed_t pitch;
85 	fixed_t yaw;
86 } static PredictionLerpFrom, PredictionLerpResult, PredictionLast;
87 static int PredictionLerptics;
88 
89 static player_t PredictionPlayerBackup;
90 static BYTE PredictionActorBackup[sizeof(APlayerPawn)];
91 static TArray<sector_t *> PredictionTouchingSectorsBackup;
92 static TArray<AActor *> PredictionSectorListBackup;
93 static TArray<msecnode_t *> PredictionSector_sprev_Backup;
94 
95 // [GRB] Custom player classes
96 TArray<FPlayerClass> PlayerClasses;
97 
FPlayerClass()98 FPlayerClass::FPlayerClass ()
99 {
100 	Type = NULL;
101 	Flags = 0;
102 }
103 
FPlayerClass(const FPlayerClass & other)104 FPlayerClass::FPlayerClass (const FPlayerClass &other)
105 {
106 	Type = other.Type;
107 	Flags = other.Flags;
108 	Skins = other.Skins;
109 }
110 
~FPlayerClass()111 FPlayerClass::~FPlayerClass ()
112 {
113 }
114 
CheckSkin(int skin)115 bool FPlayerClass::CheckSkin (int skin)
116 {
117 	for (unsigned int i = 0; i < Skins.Size (); i++)
118 	{
119 		if (Skins[i] == skin)
120 			return true;
121 	}
122 
123 	return false;
124 }
125 
126 //===========================================================================
127 //
128 // GetDisplayName
129 //
130 //===========================================================================
131 
GetPrintableDisplayName(const PClass * cls)132 const char *GetPrintableDisplayName(const PClass *cls)
133 {
134 	// Fixme; This needs a decent way to access the string table without creating a mess.
135 	const char *name = cls->Meta.GetMetaString(APMETA_DisplayName);
136 	return name;
137 }
138 
ValidatePlayerClass(const PClass * ti,const char * name)139 bool ValidatePlayerClass(const PClass *ti, const char *name)
140 {
141 	if (!ti)
142 	{
143 		Printf ("Unknown player class '%s'\n", name);
144 		return false;
145 	}
146 	else if (!ti->IsDescendantOf (RUNTIME_CLASS (APlayerPawn)))
147 	{
148 		Printf ("Invalid player class '%s'\n", name);
149 		return false;
150 	}
151 	else if (ti->Meta.GetMetaString (APMETA_DisplayName) == NULL)
152 	{
153 		Printf ("Missing displayname for player class '%s'\n", name);
154 		return false;
155 	}
156 	return true;
157 }
158 
SetupPlayerClasses()159 void SetupPlayerClasses ()
160 {
161 	FPlayerClass newclass;
162 
163 	PlayerClasses.Clear();
164 	for (unsigned i=0; i<gameinfo.PlayerClasses.Size(); i++)
165 	{
166 		newclass.Flags = 0;
167 		newclass.Type = PClass::FindClass(gameinfo.PlayerClasses[i]);
168 
169 		if (ValidatePlayerClass(newclass.Type, gameinfo.PlayerClasses[i]))
170 		{
171 			if ((GetDefaultByType(newclass.Type)->flags6 & MF6_NOMENU))
172 			{
173 				newclass.Flags |= PCF_NOMENU;
174 			}
175 			PlayerClasses.Push (newclass);
176 		}
177 	}
178 }
179 
CCMD(clearplayerclasses)180 CCMD (clearplayerclasses)
181 {
182 	if (ParsingKeyConf)
183 	{
184 		PlayerClasses.Clear ();
185 	}
186 }
187 
CCMD(addplayerclass)188 CCMD (addplayerclass)
189 {
190 	if (ParsingKeyConf && argv.argc () > 1)
191 	{
192 		const PClass *ti = PClass::FindClass (argv[1]);
193 
194 		if (ValidatePlayerClass(ti, argv[1]))
195 		{
196 			FPlayerClass newclass;
197 
198 			newclass.Type = ti;
199 			newclass.Flags = 0;
200 
201 			int arg = 2;
202 			while (arg < argv.argc ())
203 			{
204 				if (!stricmp (argv[arg], "nomenu"))
205 				{
206 					newclass.Flags |= PCF_NOMENU;
207 				}
208 				else
209 				{
210 					Printf ("Unknown flag '%s' for player class '%s'\n", argv[arg], argv[1]);
211 				}
212 
213 				arg++;
214 			}
215 
216 			PlayerClasses.Push (newclass);
217 		}
218 	}
219 }
220 
CCMD(playerclasses)221 CCMD (playerclasses)
222 {
223 	for (unsigned int i = 0; i < PlayerClasses.Size (); i++)
224 	{
225 		Printf ("%3d: Class = %s, Name = %s\n", i,
226 			PlayerClasses[i].Type->TypeName.GetChars(),
227 			PlayerClasses[i].Type->Meta.GetMetaString (APMETA_DisplayName));
228 	}
229 }
230 
231 
232 //
233 // Movement.
234 //
235 
236 // 16 pixels of bob
237 #define MAXBOB			0x100000
238 
operator <<(FArchive & arc,player_t * & p)239 FArchive &operator<< (FArchive &arc, player_t *&p)
240 {
241 	return arc.SerializePointer (players, (BYTE **)&p, sizeof(*players));
242 }
243 
244 // The player_t constructor. Since LogText is not a POD, we cannot just
245 // memset it all to 0.
player_t()246 player_t::player_t()
247 : mo(0),
248   playerstate(0),
249   cls(0),
250   DesiredFOV(0),
251   FOV(0),
252   viewz(0),
253   viewheight(0),
254   deltaviewheight(0),
255   bob(0),
256   velx(0),
257   vely(0),
258   centering(0),
259   turnticks(0),
260   attackdown(0),
261   usedown(0),
262   oldbuttons(0),
263   health(0),
264   inventorytics(0),
265   CurrentPlayerClass(0),
266   fragcount(0),
267   lastkilltime(0),
268   multicount(0),
269   spreecount(0),
270   WeaponState(0),
271   ReadyWeapon(0),
272   PendingWeapon(0),
273   cheats(0),
274   timefreezer(0),
275   refire(0),
276   inconsistant(0),
277   killcount(0),
278   itemcount(0),
279   secretcount(0),
280   damagecount(0),
281   bonuscount(0),
282   hazardcount(0),
283   poisoncount(0),
284   poisoner(0),
285   attacker(0),
286   extralight(0),
287   morphTics(0),
288   MorphedPlayerClass(0),
289   MorphStyle(0),
290   MorphExitFlash(0),
291   PremorphWeapon(0),
292   chickenPeck(0),
293   jumpTics(0),
294   onground(0),
295   respawn_time(0),
296   camera(0),
297   air_finished(0),
298   MUSINFOactor(0),
299   MUSINFOtics(-1),
300   crouching(0),
301   crouchdir(0),
302   Bot(0),
303   BlendR(0),
304   BlendG(0),
305   BlendB(0),
306   BlendA(0),
307   LogText(),
308   crouchfactor(0),
309   crouchoffset(0),
310   crouchviewdelta(0),
311   ConversationNPC(0),
312   ConversationPC(0),
313   ConversationNPCAngle(0),
314   ConversationFaceTalker(0)
315 {
316 	memset (&cmd, 0, sizeof(cmd));
317 	memset (frags, 0, sizeof(frags));
318 	memset (psprites, 0, sizeof(psprites));
319 }
320 
operator =(const player_t & p)321 player_t &player_t::operator=(const player_t &p)
322 {
323 	mo = p.mo;
324 	playerstate = p.playerstate;
325 	cmd = p.cmd;
326 	original_cmd = p.original_cmd;
327 	original_oldbuttons = p.original_oldbuttons;
328 	// Intentionally not copying userinfo!
329 	cls = p.cls;
330 	DesiredFOV = p.DesiredFOV;
331 	FOV = p.FOV;
332 	viewz = p.viewz;
333 	viewheight = p.viewheight;
334 	deltaviewheight = p.deltaviewheight;
335 	bob = p.bob;
336 	velx = p.velx;
337 	vely = p.vely;
338 	centering = p.centering;
339 	turnticks = p.turnticks;
340 	attackdown = p.attackdown;
341 	usedown = p.usedown;
342 	oldbuttons = p.oldbuttons;
343 	health = p.health;
344 	inventorytics = p.inventorytics;
345 	CurrentPlayerClass = p.CurrentPlayerClass;
346 	memcpy(frags, &p.frags, sizeof(frags));
347 	fragcount = p.fragcount;
348 	lastkilltime = p.lastkilltime;
349 	multicount = p.multicount;
350 	spreecount = p.spreecount;
351 	WeaponState = p.WeaponState;
352 	ReadyWeapon = p.ReadyWeapon;
353 	PendingWeapon = p.PendingWeapon;
354 	cheats = p.cheats;
355 	timefreezer = p.timefreezer;
356 	refire = p.refire;
357 	inconsistant = p.inconsistant;
358 	waiting = p.waiting;
359 	killcount = p.killcount;
360 	itemcount = p.itemcount;
361 	secretcount = p.secretcount;
362 	damagecount = p.damagecount;
363 	bonuscount = p.bonuscount;
364 	hazardcount = p.hazardcount;
365 	hazardtype = p.hazardtype;
366 	hazardinterval = p.hazardinterval;
367 	poisoncount = p.poisoncount;
368 	poisontype = p.poisontype;
369 	poisonpaintype = p.poisonpaintype;
370 	poisoner = p.poisoner;
371 	attacker = p.attacker;
372 	extralight = p.extralight;
373 	fixedcolormap = p.fixedcolormap;
374 	fixedlightlevel = p.fixedlightlevel;
375 	memcpy(psprites, &p.psprites, sizeof(psprites));
376 	morphTics = p.morphTics;
377 	MorphedPlayerClass = p.MorphedPlayerClass;
378 	MorphStyle = p.MorphStyle;
379 	MorphExitFlash = p.MorphExitFlash;
380 	PremorphWeapon = p.PremorphWeapon;
381 	chickenPeck = p.chickenPeck;
382 	jumpTics = p.jumpTics;
383 	onground = p.onground;
384 	respawn_time = p.respawn_time;
385 	camera = p.camera;
386 	air_finished = p.air_finished;
387 	LastDamageType = p.LastDamageType;
388 	Bot = p.Bot;
389 	settings_controller = p.settings_controller;
390 	BlendR = p.BlendR;
391 	BlendG = p.BlendG;
392 	BlendB = p.BlendB;
393 	BlendA = p.BlendA;
394 	LogText = p.LogText;
395 	MinPitch = p.MinPitch;
396 	MaxPitch = p.MaxPitch;
397 	crouching = p.crouching;
398 	crouchdir = p.crouchdir;
399 	crouchfactor = p.crouchfactor;
400 	crouchoffset = p.crouchoffset;
401 	crouchviewdelta = p.crouchviewdelta;
402 	weapons = p.weapons;
403 	ConversationNPC = p.ConversationNPC;
404 	ConversationPC = p.ConversationPC;
405 	ConversationNPCAngle = p.ConversationNPCAngle;
406 	ConversationFaceTalker = p.ConversationFaceTalker;
407 	MUSINFOactor = p.MUSINFOactor;
408 	MUSINFOtics = p.MUSINFOtics;
409 	return *this;
410 }
411 
412 // This function supplements the pointer cleanup in dobject.cpp, because
413 // player_t is not derived from DObject. (I tried it, and DestroyScan was
414 // unable to properly determine the player object's type--possibly
415 // because it gets staticly allocated in an array.)
416 //
417 // This function checks all the DObject pointers in a player_t and NULLs any
418 // that match the pointer passed in. If you add any pointers that point to
419 // DObject (or a subclass), add them here too.
420 
FixPointers(const DObject * old,DObject * rep)421 size_t player_t::FixPointers (const DObject *old, DObject *rep)
422 {
423 	APlayerPawn *replacement = static_cast<APlayerPawn *>(rep);
424 	size_t changed = 0;
425 
426 	// The construct *& is used in several of these to avoid the read barriers
427 	// that would turn the pointer we want to check to NULL if the old object
428 	// is pending deletion.
429 	if (mo == old)					mo = replacement, changed++;
430 	if (*&poisoner == old)			poisoner = replacement, changed++;
431 	if (*&attacker == old)			attacker = replacement, changed++;
432 	if (*&camera == old)			camera = replacement, changed++;
433 	if (*&Bot == old)				Bot = static_cast<DBot *>(rep), changed++;
434 	if (ReadyWeapon == old)			ReadyWeapon = static_cast<AWeapon *>(rep), changed++;
435 	if (PendingWeapon == old)		PendingWeapon = static_cast<AWeapon *>(rep), changed++;
436 	if (*&PremorphWeapon == old)	PremorphWeapon = static_cast<AWeapon *>(rep), changed++;
437 	if (*&ConversationNPC == old)	ConversationNPC = replacement, changed++;
438 	if (*&ConversationPC == old)	ConversationPC = replacement, changed++;
439 	if (*&MUSINFOactor == old)		MUSINFOactor = replacement, changed++;
440 	return changed;
441 }
442 
PropagateMark()443 size_t player_t::PropagateMark()
444 {
445 	GC::Mark(mo);
446 	GC::Mark(poisoner);
447 	GC::Mark(attacker);
448 	GC::Mark(camera);
449 	GC::Mark(Bot);
450 	GC::Mark(ReadyWeapon);
451 	GC::Mark(ConversationNPC);
452 	GC::Mark(ConversationPC);
453 	GC::Mark(MUSINFOactor);
454 	GC::Mark(PremorphWeapon);
455 	if (PendingWeapon != WP_NOCHANGE)
456 	{
457 		GC::Mark(PendingWeapon);
458 	}
459 	return sizeof(*this);
460 }
461 
SetLogNumber(int num)462 void player_t::SetLogNumber (int num)
463 {
464 	char lumpname[16];
465 	int lumpnum;
466 
467 	mysnprintf (lumpname, countof(lumpname), "LOG%d", num);
468 	lumpnum = Wads.CheckNumForName (lumpname);
469 	if (lumpnum == -1)
470 	{
471 		// Leave the log message alone if this one doesn't exist.
472 		//SetLogText (lumpname);
473 	}
474 	else
475 	{
476 		int length=Wads.LumpLength(lumpnum);
477 		char *data= new char[length+1];
478 		Wads.ReadLump (lumpnum, data);
479 		data[length]=0;
480 		SetLogText (data);
481 		delete[] data;
482 	}
483 }
484 
SetLogText(const char * text)485 void player_t::SetLogText (const char *text)
486 {
487 	LogText = text;
488 
489 	// Print log text to console
490 	AddToConsole(-1, TEXTCOLOR_GOLD);
491 	AddToConsole(-1, LogText);
492 	AddToConsole(-1, "\n");
493 }
494 
GetSpawnClass()495 int player_t::GetSpawnClass()
496 {
497 	const PClass * type = PlayerClasses[CurrentPlayerClass].Type;
498 	return static_cast<APlayerPawn*>(GetDefaultByType(type))->SpawnMask;
499 }
500 
501 //===========================================================================
502 //
503 // player_t :: SendPitchLimits
504 //
505 // Ask the local player's renderer what pitch restrictions should be imposed
506 // and let everybody know. Only sends data for the consoleplayer, since the
507 // local player is the only one our data is valid for.
508 //
509 //===========================================================================
510 
SendPitchLimits() const511 void player_t::SendPitchLimits() const
512 {
513 	if (this - players == consoleplayer)
514 	{
515 		Net_WriteByte(DEM_SETPITCHLIMIT);
516 		Net_WriteByte(Renderer->GetMaxViewPitch(false));	// up
517 		Net_WriteByte(Renderer->GetMaxViewPitch(true));		// down
518 	}
519 }
520 
521 //===========================================================================
522 //
523 // APlayerPawn
524 //
525 //===========================================================================
526 
527 IMPLEMENT_POINTY_CLASS (APlayerPawn)
DECLARE_POINTER(InvFirst)528  DECLARE_POINTER(InvFirst)
529  DECLARE_POINTER(InvSel)
530 END_POINTERS
531 
532 IMPLEMENT_CLASS (APlayerChunk)
533 
534 void APlayerPawn::Serialize (FArchive &arc)
535 {
536 	Super::Serialize (arc);
537 
538 	arc << JumpZ
539 		<< MaxHealth
540 		<< RunHealth
541 		<< SpawnMask
542 		<< ForwardMove1
543 		<< ForwardMove2
544 		<< SideMove1
545 		<< SideMove2
546 		<< ScoreIcon
547 		<< InvFirst
548 		<< InvSel
549 		<< MorphWeapon
550 		<< DamageFade
551 		<< PlayerFlags
552 		<< FlechetteType;
553 	if (SaveVersion < 3829)
554 	{
555 		GruntSpeed = 12*FRACUNIT;
556 		FallingScreamMinSpeed = 35*FRACUNIT;
557 		FallingScreamMaxSpeed = 40*FRACUNIT;
558 	}
559 	else
560 	{
561 		arc << GruntSpeed << FallingScreamMinSpeed << FallingScreamMaxSpeed;
562 	}
563 	if (SaveVersion >= 4502)
564 	{
565 		arc << UseRange;
566 	}
567 	if (SaveVersion >= 4503)
568 	{
569 		arc << AirCapacity;
570 	}
571 	if (SaveVersion >= 4526)
572 	{
573 		arc << ViewHeight;
574 	}
575 }
576 
577 //===========================================================================
578 //
579 // APlayerPawn :: MarkPrecacheSounds
580 //
581 //===========================================================================
582 
MarkPrecacheSounds() const583 void APlayerPawn::MarkPrecacheSounds() const
584 {
585 	Super::MarkPrecacheSounds();
586 	S_MarkPlayerSounds(GetSoundClass());
587 }
588 
589 //===========================================================================
590 //
591 // APlayerPawn :: BeginPlay
592 //
593 //===========================================================================
594 
BeginPlay()595 void APlayerPawn::BeginPlay ()
596 {
597 	Super::BeginPlay ();
598 	ChangeStatNum (STAT_PLAYER);
599 
600 	// Check whether a PWADs normal sprite is to be combined with the base WADs
601 	// crouch sprite. In such a case the sprites normally don't match and it is
602 	// best to disable the crouch sprite.
603 	if (crouchsprite > 0)
604 	{
605 		// This assumes that player sprites always exist in rotated form and
606 		// that the front view is always a separate sprite. So far this is
607 		// true for anything that exists.
608 		FString normspritename = sprites[SpawnState->sprite].name;
609 		FString crouchspritename = sprites[crouchsprite].name;
610 
611 		int spritenorm = Wads.CheckNumForName(normspritename + "A1", ns_sprites);
612 		if (spritenorm==-1)
613 		{
614 			spritenorm = Wads.CheckNumForName(normspritename + "A0", ns_sprites);
615 		}
616 
617 		int spritecrouch = Wads.CheckNumForName(crouchspritename + "A1", ns_sprites);
618 		if (spritecrouch==-1)
619 		{
620 			spritecrouch = Wads.CheckNumForName(crouchspritename + "A0", ns_sprites);
621 		}
622 
623 		if (spritenorm==-1 || spritecrouch ==-1)
624 		{
625 			// Sprites do not exist so it is best to disable the crouch sprite.
626 			crouchsprite = 0;
627 			return;
628 		}
629 
630 		int wadnorm = Wads.GetLumpFile(spritenorm);
631 		int wadcrouch = Wads.GetLumpFile(spritenorm);
632 
633 		if (wadnorm > FWadCollection::IWAD_FILENUM && wadcrouch <= FWadCollection::IWAD_FILENUM)
634 		{
635 			// Question: Add an option / disable crouching or do what?
636 			crouchsprite = 0;
637 		}
638 	}
639 }
640 
641 //===========================================================================
642 //
643 // APlayerPawn :: Tick
644 //
645 //===========================================================================
646 
Tick()647 void APlayerPawn::Tick()
648 {
649 	if (player != NULL && player->mo == this && player->CanCrouch() && player->playerstate != PST_DEAD)
650 	{
651 		height = FixedMul(GetDefault()->height, player->crouchfactor);
652 	}
653 	else
654 	{
655 		if (health > 0) height = GetDefault()->height;
656 	}
657 	Super::Tick();
658 }
659 
660 //===========================================================================
661 //
662 // APlayerPawn :: PostBeginPlay
663 //
664 //===========================================================================
665 
PostBeginPlay()666 void APlayerPawn::PostBeginPlay()
667 {
668 	Super::PostBeginPlay();
669 	SetupWeaponSlots();
670 
671 	// Voodoo dolls: restore original floorz/ceilingz logic
672 	if (player == NULL || player->mo != this)
673 	{
674 		dropoffz = floorz = Sector->floorplane.ZatPoint(this);
675 		ceilingz = Sector->ceilingplane.ZatPoint(this);
676 		P_FindFloorCeiling(this, FFCF_ONLYSPAWNPOS);
677 		SetZ(floorz);
678 	}
679 	else
680 	{
681 		player->SendPitchLimits();
682 	}
683 }
684 
685 //===========================================================================
686 //
687 // APlayerPawn :: SetupWeaponSlots
688 //
689 // Sets up the default weapon slots for this player. If this is also the
690 // local player, determines local modifications and sends those across the
691 // network. Ignores voodoo dolls.
692 //
693 //===========================================================================
694 
SetupWeaponSlots()695 void APlayerPawn::SetupWeaponSlots()
696 {
697 	if (player != NULL && player->mo == this)
698 	{
699 		player->weapons.StandardSetup(GetClass());
700 		// If we're the local player, then there's a bit more work to do.
701 		// This also applies if we're a bot and this is the net arbitrator.
702 		if (player - players == consoleplayer ||
703 			(player->Bot != NULL && consoleplayer == Net_Arbitrator))
704 		{
705 			FWeaponSlots local_slots(player->weapons);
706 			if (player->Bot != NULL)
707 			{ // Bots only need weapons from KEYCONF, not INI modifications.
708 				P_PlaybackKeyConfWeapons(&local_slots);
709 			}
710 			else
711 			{
712 				local_slots.LocalSetup(GetClass());
713 			}
714 			local_slots.SendDifferences(int(player - players), player->weapons);
715 		}
716 	}
717 }
718 
719 //===========================================================================
720 //
721 // APlayerPawn :: AddInventory
722 //
723 //===========================================================================
724 
AddInventory(AInventory * item)725 void APlayerPawn::AddInventory (AInventory *item)
726 {
727 	// Adding inventory to a voodoo doll should add it to the real player instead.
728 	if (player != NULL && player->mo != this && player->mo != NULL)
729 	{
730 		player->mo->AddInventory (item);
731 		return;
732 	}
733 	Super::AddInventory (item);
734 
735 	// If nothing is selected, select this item.
736 	if (InvSel == NULL && (item->ItemFlags & IF_INVBAR))
737 	{
738 		InvSel = item;
739 	}
740 }
741 
742 //===========================================================================
743 //
744 // APlayerPawn :: RemoveInventory
745 //
746 //===========================================================================
747 
RemoveInventory(AInventory * item)748 void APlayerPawn::RemoveInventory (AInventory *item)
749 {
750 	bool pickWeap = false;
751 
752 	// Since voodoo dolls aren't supposed to have an inventory, there should be
753 	// no need to redirect them to the real player here as there is with AddInventory.
754 
755 	// If the item removed is the selected one, select something else, either the next
756 	// item, if there is one, or the previous item.
757 	if (player != NULL)
758 	{
759 		if (InvSel == item)
760 		{
761 			InvSel = item->NextInv ();
762 			if (InvSel == NULL)
763 			{
764 				InvSel = item->PrevInv ();
765 			}
766 		}
767 		if (InvFirst == item)
768 		{
769 			InvFirst = item->NextInv ();
770 			if (InvFirst == NULL)
771 			{
772 				InvFirst = item->PrevInv ();
773 			}
774 		}
775 		if (item == player->PendingWeapon)
776 		{
777 			player->PendingWeapon = WP_NOCHANGE;
778 		}
779 		if (item == player->ReadyWeapon)
780 		{
781 			// If the current weapon is removed, clear the refire counter and pick a new one.
782 			pickWeap = true;
783 			player->ReadyWeapon = NULL;
784 			player->refire = 0;
785 		}
786 	}
787 	Super::RemoveInventory (item);
788 	if (pickWeap && player->mo == this && player->PendingWeapon == WP_NOCHANGE)
789 	{
790 		PickNewWeapon (NULL);
791 	}
792 }
793 
794 //===========================================================================
795 //
796 // APlayerPawn :: UseInventory
797 //
798 //===========================================================================
799 
UseInventory(AInventory * item)800 bool APlayerPawn::UseInventory (AInventory *item)
801 {
802 	const PClass *itemtype = item->GetClass();
803 
804 	if (player->cheats & CF_TOTALLYFROZEN)
805 	{ // You can't use items if you're totally frozen
806 		return false;
807 	}
808 	if ((level.flags2 & LEVEL2_FROZEN) && (player == NULL || player->timefreezer == 0))
809 	{
810 		// Time frozen
811 		return false;
812 	}
813 
814 	if (!Super::UseInventory (item))
815 	{
816 		// Heretic and Hexen advance the inventory cursor if the use failed.
817 		// Should this behavior be retained?
818 		return false;
819 	}
820 	if (player == &players[consoleplayer])
821 	{
822 		S_Sound (this, CHAN_ITEM, item->UseSound, 1, ATTN_NORM);
823 		StatusBar->FlashItem (itemtype);
824 	}
825 	return true;
826 }
827 
828 //===========================================================================
829 //
830 // APlayerPawn :: BestWeapon
831 //
832 // Returns the best weapon a player has, possibly restricted to a single
833 // type of ammo.
834 //
835 //===========================================================================
836 
BestWeapon(const PClass * ammotype)837 AWeapon *APlayerPawn::BestWeapon (const PClass *ammotype)
838 {
839 	AWeapon *bestMatch = NULL;
840 	int bestOrder = INT_MAX;
841 	AInventory *item;
842 	AWeapon *weap;
843 	bool tomed = NULL != FindInventory (RUNTIME_CLASS(APowerWeaponLevel2), true);
844 
845 	// Find the best weapon the player has.
846 	for (item = Inventory; item != NULL; item = item->Inventory)
847 	{
848 		if (!item->IsKindOf (RUNTIME_CLASS(AWeapon)))
849 			continue;
850 
851 		weap = static_cast<AWeapon *> (item);
852 
853 		// Don't select it if it's worse than what was already found.
854 		if (weap->SelectionOrder > bestOrder)
855 			continue;
856 
857 		// Don't select it if its primary fire doesn't use the desired ammo.
858 		if (ammotype != NULL &&
859 			(weap->Ammo1 == NULL ||
860 			 weap->Ammo1->GetClass() != ammotype))
861 			continue;
862 
863 		// Don't select it if the Tome is active and this isn't the powered-up version.
864 		if (tomed && weap->SisterWeapon != NULL && weap->SisterWeapon->WeaponFlags & WIF_POWERED_UP)
865 			continue;
866 
867 		// Don't select it if it's powered-up and the Tome is not active.
868 		if (!tomed && weap->WeaponFlags & WIF_POWERED_UP)
869 			continue;
870 
871 		// Don't select it if there isn't enough ammo to use its primary fire.
872 		if (!(weap->WeaponFlags & WIF_AMMO_OPTIONAL) &&
873 			!weap->CheckAmmo (AWeapon::PrimaryFire, false))
874 			continue;
875 
876 		// Don't select if if there isn't enough ammo as determined by the weapon's author.
877 		if (weap->MinSelAmmo1 > 0 && (weap->Ammo1 == NULL || weap->Ammo1->Amount < weap->MinSelAmmo1))
878 			continue;
879 		if (weap->MinSelAmmo2 > 0 && (weap->Ammo2 == NULL || weap->Ammo2->Amount < weap->MinSelAmmo2))
880 			continue;
881 
882 		// This weapon is usable!
883 		bestOrder = weap->SelectionOrder;
884 		bestMatch = weap;
885 	}
886 	return bestMatch;
887 }
888 
889 //===========================================================================
890 //
891 // APlayerPawn :: PickNewWeapon
892 //
893 // Picks a new weapon for this player. Used mostly for running out of ammo,
894 // but it also works when an ACS script explicitly takes the ready weapon
895 // away or the player picks up some ammo they had previously run out of.
896 //
897 //===========================================================================
898 
PickNewWeapon(const PClass * ammotype)899 AWeapon *APlayerPawn::PickNewWeapon (const PClass *ammotype)
900 {
901 	AWeapon *best = BestWeapon (ammotype);
902 
903 	if (best != NULL)
904 	{
905 		player->PendingWeapon = best;
906 		if (player->ReadyWeapon != NULL)
907 		{
908 			P_DropWeapon(player);
909 		}
910 		else if (player->PendingWeapon != WP_NOCHANGE)
911 		{
912 			P_BringUpWeapon (player);
913 		}
914 	}
915 	return best;
916 }
917 
918 
919 //===========================================================================
920 //
921 // APlayerPawn :: CheckWeaponSwitch
922 //
923 // Checks if weapons should be changed after picking up ammo
924 //
925 //===========================================================================
926 
CheckWeaponSwitch(const PClass * ammotype)927 void APlayerPawn::CheckWeaponSwitch(const PClass *ammotype)
928 {
929 	if (!player->userinfo.GetNeverSwitch() &&
930 		player->PendingWeapon == WP_NOCHANGE &&
931 		(player->ReadyWeapon == NULL ||
932 		 (player->ReadyWeapon->WeaponFlags & WIF_WIMPY_WEAPON)))
933 	{
934 		AWeapon *best = BestWeapon (ammotype);
935 		if (best != NULL && (player->ReadyWeapon == NULL ||
936 			best->SelectionOrder < player->ReadyWeapon->SelectionOrder))
937 		{
938 			player->PendingWeapon = best;
939 		}
940 	}
941 }
942 
943 //===========================================================================
944 //
945 // APlayerPawn :: GiveDeathmatchInventory
946 //
947 // Gives players items they should have in addition to their default
948 // inventory when playing deathmatch. (i.e. all keys)
949 //
950 //===========================================================================
951 
GiveDeathmatchInventory()952 void APlayerPawn::GiveDeathmatchInventory()
953 {
954 	for (unsigned int i = 0; i < PClass::m_Types.Size(); ++i)
955 	{
956 		if (PClass::m_Types[i]->IsDescendantOf (RUNTIME_CLASS(AKey)))
957 		{
958 			AKey *key = (AKey *)GetDefaultByType (PClass::m_Types[i]);
959 			if (key->KeyNumber != 0)
960 			{
961 				key = static_cast<AKey *>(Spawn (PClass::m_Types[i], 0,0,0, NO_REPLACE));
962 				if (!key->CallTryPickup (this))
963 				{
964 					key->Destroy ();
965 				}
966 			}
967 		}
968 	}
969 }
970 
971 //===========================================================================
972 //
973 // APlayerPawn :: FilterCoopRespawnInventory
974 //
975 // When respawning in coop, this function is called to walk through the dead
976 // player's inventory and modify it according to the current game flags so
977 // that it can be transferred to the new live player. This player currently
978 // has the default inventory, and the oldplayer has the inventory at the time
979 // of death.
980 //
981 //===========================================================================
982 
FilterCoopRespawnInventory(APlayerPawn * oldplayer)983 void APlayerPawn::FilterCoopRespawnInventory (APlayerPawn *oldplayer)
984 {
985 	AInventory *item, *next, *defitem;
986 
987 	// If we're losing everything, this is really simple.
988 	if (dmflags & DF_COOP_LOSE_INVENTORY)
989 	{
990 		oldplayer->DestroyAllInventory();
991 		return;
992 	}
993 
994 	if (dmflags &  (DF_COOP_LOSE_KEYS |
995 					DF_COOP_LOSE_WEAPONS |
996 					DF_COOP_LOSE_AMMO |
997 					DF_COOP_HALVE_AMMO |
998 					DF_COOP_LOSE_ARMOR |
999 					DF_COOP_LOSE_POWERUPS))
1000 	{
1001 		// Walk through the old player's inventory and destroy or modify
1002 		// according to dmflags.
1003 		for (item = oldplayer->Inventory; item != NULL; item = next)
1004 		{
1005 			next = item->Inventory;
1006 
1007 			// If this item is part of the default inventory, we never want
1008 			// to destroy it, although we might want to copy the default
1009 			// inventory amount.
1010 			defitem = FindInventory (item->GetClass());
1011 
1012 			if ((dmflags & DF_COOP_LOSE_KEYS) &&
1013 				defitem == NULL &&
1014 				item->IsKindOf(RUNTIME_CLASS(AKey)))
1015 			{
1016 				item->Destroy();
1017 			}
1018 			else if ((dmflags & DF_COOP_LOSE_WEAPONS) &&
1019 				defitem == NULL &&
1020 				item->IsKindOf(RUNTIME_CLASS(AWeapon)))
1021 			{
1022 				item->Destroy();
1023 			}
1024 			else if ((dmflags & DF_COOP_LOSE_ARMOR) &&
1025 				item->IsKindOf(RUNTIME_CLASS(AArmor)))
1026 			{
1027 				if (defitem == NULL)
1028 				{
1029 					item->Destroy();
1030 				}
1031 				else if (item->IsKindOf(RUNTIME_CLASS(ABasicArmor)))
1032 				{
1033 					static_cast<ABasicArmor*>(item)->SavePercent = static_cast<ABasicArmor*>(defitem)->SavePercent;
1034 					item->Amount = defitem->Amount;
1035 				}
1036 				else if (item->IsKindOf(RUNTIME_CLASS(AHexenArmor)))
1037 				{
1038 					static_cast<AHexenArmor*>(item)->Slots[0] = static_cast<AHexenArmor*>(defitem)->Slots[0];
1039 					static_cast<AHexenArmor*>(item)->Slots[1] = static_cast<AHexenArmor*>(defitem)->Slots[1];
1040 					static_cast<AHexenArmor*>(item)->Slots[2] = static_cast<AHexenArmor*>(defitem)->Slots[2];
1041 					static_cast<AHexenArmor*>(item)->Slots[3] = static_cast<AHexenArmor*>(defitem)->Slots[3];
1042 				}
1043 			}
1044 			else if ((dmflags & DF_COOP_LOSE_POWERUPS) &&
1045 				defitem == NULL &&
1046 				item->IsKindOf(RUNTIME_CLASS(APowerupGiver)))
1047 			{
1048 				item->Destroy();
1049 			}
1050 			else if ((dmflags & (DF_COOP_LOSE_AMMO | DF_COOP_HALVE_AMMO)) &&
1051 				item->IsKindOf(RUNTIME_CLASS(AAmmo)))
1052 			{
1053 				if (defitem == NULL)
1054 				{
1055 					if (dmflags & DF_COOP_LOSE_AMMO)
1056 					{
1057 						// Do NOT destroy the ammo, because a weapon might reference it.
1058 						item->Amount = 0;
1059 					}
1060 					else if (item->Amount > 1)
1061 					{
1062 						item->Amount /= 2;
1063 					}
1064 				}
1065 				else
1066 				{
1067 					// When set to lose ammo, you get to keep all your starting ammo.
1068 					// When set to halve ammo, you won't be left with less than your starting amount.
1069 					if (dmflags & DF_COOP_LOSE_AMMO)
1070 					{
1071 						item->Amount = defitem->Amount;
1072 					}
1073 					else if (item->Amount > 1)
1074 					{
1075 						item->Amount = MAX(item->Amount / 2, defitem->Amount);
1076 					}
1077 				}
1078 			}
1079 		}
1080 	}
1081 
1082 	// Now destroy the default inventory this player is holding and move
1083 	// over the old player's remaining inventory.
1084 	DestroyAllInventory();
1085 	ObtainInventory (oldplayer);
1086 
1087 	player->ReadyWeapon = NULL;
1088 	PickNewWeapon (NULL);
1089 }
1090 
1091 //===========================================================================
1092 //
1093 // APlayerPawn :: GetSoundClass
1094 //
1095 //===========================================================================
1096 
GetSoundClass() const1097 const char *APlayerPawn::GetSoundClass() const
1098 {
1099 	if (player != NULL &&
1100 		(player->mo == NULL || !(player->mo->flags4 &MF4_NOSKIN)) &&
1101 		(unsigned int)player->userinfo.GetSkin() >= PlayerClasses.Size () &&
1102 		(size_t)player->userinfo.GetSkin() < numskins)
1103 	{
1104 		return skins[player->userinfo.GetSkin()].name;
1105 	}
1106 
1107 	// [GRB]
1108 	const char *sclass = GetClass ()->Meta.GetMetaString (APMETA_SoundClass);
1109 	return sclass != NULL ? sclass : "player";
1110 }
1111 
1112 //===========================================================================
1113 //
1114 // APlayerPawn :: GetMaxHealth
1115 //
1116 // only needed because Boom screwed up Dehacked.
1117 //
1118 //===========================================================================
1119 
GetMaxHealth() const1120 int APlayerPawn::GetMaxHealth() const
1121 {
1122 	return MaxHealth > 0? MaxHealth : ((i_compatflags&COMPATF_DEHHEALTH)? 100 : deh.MaxHealth);
1123 }
1124 
1125 //===========================================================================
1126 //
1127 // APlayerPawn :: UpdateWaterLevel
1128 //
1129 // Plays surfacing and diving sounds, as appropriate.
1130 //
1131 //===========================================================================
1132 
UpdateWaterLevel(fixed_t oldz,bool splash)1133 bool APlayerPawn::UpdateWaterLevel (fixed_t oldz, bool splash)
1134 {
1135 	int oldlevel = waterlevel;
1136 	bool retval = Super::UpdateWaterLevel (oldz, splash);
1137 	if (player != NULL)
1138 	{
1139 		if (oldlevel < 3 && waterlevel == 3)
1140 		{ // Our head just went under.
1141 			S_Sound (this, CHAN_VOICE, "*dive", 1, ATTN_NORM);
1142 		}
1143 		else if (oldlevel == 3 && waterlevel < 3)
1144 		{ // Our head just came up.
1145 			if (player->air_finished > level.time)
1146 			{ // We hadn't run out of air yet.
1147 				S_Sound (this, CHAN_VOICE, "*surface", 1, ATTN_NORM);
1148 			}
1149 			// If we were running out of air, then ResetAirSupply() will play *gasp.
1150 		}
1151 	}
1152 	return retval;
1153 }
1154 
1155 //===========================================================================
1156 //
1157 // APlayerPawn :: ResetAirSupply
1158 //
1159 // Gives the player a full "tank" of air. If they had previously completely
1160 // run out of air, also plays the *gasp sound. Returns true if the player
1161 // was drowning.
1162 //
1163 //===========================================================================
1164 
ResetAirSupply(bool playgasp)1165 bool APlayerPawn::ResetAirSupply (bool playgasp)
1166 {
1167 	bool wasdrowning = (player->air_finished < level.time);
1168 
1169 	if (playgasp && wasdrowning)
1170 	{
1171 		S_Sound (this, CHAN_VOICE, "*gasp", 1, ATTN_NORM);
1172 	}
1173 	if (level.airsupply> 0 && player->mo->AirCapacity > 0) player->air_finished = level.time + FixedMul(level.airsupply, player->mo->AirCapacity);
1174 	else player->air_finished = INT_MAX;
1175 	return wasdrowning;
1176 }
1177 
1178 //===========================================================================
1179 //
1180 // Animations
1181 //
1182 //===========================================================================
1183 
PlayIdle()1184 void APlayerPawn::PlayIdle ()
1185 {
1186 	if (InStateSequence(state, SeeState))
1187 		SetState (SpawnState);
1188 }
1189 
PlayRunning()1190 void APlayerPawn::PlayRunning ()
1191 {
1192 	if (InStateSequence(state, SpawnState) && SeeState != NULL)
1193 		SetState (SeeState);
1194 }
1195 
PlayAttacking()1196 void APlayerPawn::PlayAttacking ()
1197 {
1198 	if (MissileState != NULL) SetState (MissileState);
1199 }
1200 
PlayAttacking2()1201 void APlayerPawn::PlayAttacking2 ()
1202 {
1203 	if (MeleeState != NULL) SetState (MeleeState);
1204 }
1205 
ThrowPoisonBag()1206 void APlayerPawn::ThrowPoisonBag ()
1207 {
1208 }
1209 
1210 //===========================================================================
1211 //
1212 // APlayerPawn :: GiveDefaultInventory
1213 //
1214 //===========================================================================
1215 
GiveDefaultInventory()1216 void APlayerPawn::GiveDefaultInventory ()
1217 {
1218 	if (player == NULL) return;
1219 
1220 	// HexenArmor must always be the first item in the inventory because
1221 	// it provides player class based protection that should not affect
1222 	// any other protection item.
1223 	fixed_t hx[5];
1224 	for(int i=0;i<5;i++)
1225 	{
1226 		hx[i] = GetClass()->Meta.GetMetaFixed(APMETA_Hexenarmor0+i);
1227 	}
1228 	GiveInventoryType (RUNTIME_CLASS(AHexenArmor));
1229 	AHexenArmor *harmor = FindInventory<AHexenArmor>();
1230 	harmor->Slots[4] = hx[0];
1231 	harmor->SlotsIncrement[0] = hx[1];
1232 	harmor->SlotsIncrement[1] = hx[2];
1233 	harmor->SlotsIncrement[2] = hx[3];
1234 	harmor->SlotsIncrement[3] = hx[4];
1235 
1236 	// BasicArmor must come right after that. It should not affect any
1237 	// other protection item as well but needs to process the damage
1238 	// before the HexenArmor does.
1239 	ABasicArmor *barmor = Spawn<ABasicArmor> (0,0,0, NO_REPLACE);
1240 	barmor->BecomeItem ();
1241 	barmor->SavePercent = 0;
1242 	barmor->Amount = 0;
1243 	AddInventory (barmor);
1244 
1245 	// Now add the items from the DECORATE definition
1246 	FDropItem *di = GetDropItems();
1247 
1248 	while (di)
1249 	{
1250 		const PClass *ti = PClass::FindClass (di->Name);
1251 		if (ti)
1252 		{
1253 			AInventory *item = FindInventory (ti);
1254 			if (item != NULL)
1255 			{
1256 				item->Amount = clamp<int>(
1257 					item->Amount + (di->amount ? di->amount : ((AInventory *)item->GetDefault ())->Amount),
1258 					0, item->MaxAmount);
1259 			}
1260 			else
1261 			{
1262 				item = static_cast<AInventory *>(Spawn (ti, 0,0,0, NO_REPLACE));
1263 				item->ItemFlags|=IF_IGNORESKILL;	// no skill multiplicators here
1264 				item->Amount = di->amount;
1265 				if (item->IsKindOf (RUNTIME_CLASS (AWeapon)))
1266 				{
1267 					// To allow better control any weapon is emptied of
1268 					// ammo before being given to the player.
1269 					static_cast<AWeapon*>(item)->AmmoGive1 =
1270 					static_cast<AWeapon*>(item)->AmmoGive2 = 0;
1271 				}
1272 				AActor *check;
1273 				if (!item->CallTryPickup(this, &check))
1274 				{
1275 					if (check != this)
1276 					{
1277 						// Player was morphed. This is illegal at game start.
1278 						// This problem is only detectable when it's too late to do something about it...
1279 						I_Error("Cannot give morph items when starting a game");
1280 					}
1281 					item->Destroy ();
1282 					item = NULL;
1283 				}
1284 			}
1285 			if (item != NULL && item->IsKindOf (RUNTIME_CLASS (AWeapon)) &&
1286 				static_cast<AWeapon*>(item)->CheckAmmo(AWeapon::EitherFire, false))
1287 			{
1288 				player->ReadyWeapon = player->PendingWeapon = static_cast<AWeapon *> (item);
1289 			}
1290 		}
1291 		di = di->Next;
1292 	}
1293 }
1294 
MorphPlayerThink()1295 void APlayerPawn::MorphPlayerThink ()
1296 {
1297 }
1298 
ActivateMorphWeapon()1299 void APlayerPawn::ActivateMorphWeapon ()
1300 {
1301 	const PClass *morphweapon = PClass::FindClass (MorphWeapon);
1302 	player->PendingWeapon = WP_NOCHANGE;
1303 	player->psprites[ps_weapon].sy = WEAPONTOP;
1304 
1305 	if (morphweapon == NULL || !morphweapon->IsDescendantOf (RUNTIME_CLASS(AWeapon)))
1306 	{ // No weapon at all while morphed!
1307 		player->ReadyWeapon = NULL;
1308 		P_SetPsprite (player, ps_weapon, NULL);
1309 	}
1310 	else
1311 	{
1312 		player->ReadyWeapon = static_cast<AWeapon *>(player->mo->FindInventory (morphweapon));
1313 		if (player->ReadyWeapon == NULL)
1314 		{
1315 			player->ReadyWeapon = static_cast<AWeapon *>(player->mo->GiveInventoryType (morphweapon));
1316 			if (player->ReadyWeapon != NULL)
1317 			{
1318 				player->ReadyWeapon->GivenAsMorphWeapon = true; // flag is used only by new beastweap semantics in P_UndoPlayerMorph
1319 			}
1320 		}
1321 		if (player->ReadyWeapon != NULL)
1322 		{
1323 			P_SetPsprite (player, ps_weapon, player->ReadyWeapon->GetReadyState());
1324 		}
1325 		else
1326 		{
1327 			P_SetPsprite (player, ps_weapon, NULL);
1328 		}
1329 	}
1330 	P_SetPsprite (player, ps_flash, NULL);
1331 
1332 	player->PendingWeapon = WP_NOCHANGE;
1333 }
1334 
1335 //===========================================================================
1336 //
1337 // APlayerPawn :: Die
1338 //
1339 //===========================================================================
1340 
Die(AActor * source,AActor * inflictor,int dmgflags)1341 void APlayerPawn::Die (AActor *source, AActor *inflictor, int dmgflags)
1342 {
1343 	Super::Die (source, inflictor, dmgflags);
1344 
1345 	if (player != NULL && player->mo == this) player->bonuscount = 0;
1346 
1347 	if (player != NULL && player->mo != this)
1348 	{ // Make the real player die, too
1349 		player->mo->Die (source, inflictor, dmgflags);
1350 	}
1351 	else
1352 	{
1353 		if (player != NULL && (dmflags2 & DF2_YES_WEAPONDROP))
1354 		{ // Voodoo dolls don't drop weapons
1355 			AWeapon *weap = player->ReadyWeapon;
1356 			if (weap != NULL)
1357 			{
1358 				AInventory *item;
1359 
1360 				// kgDROP - start - modified copy from a_action.cpp
1361 				FDropItem *di = weap->GetDropItems();
1362 
1363 				if (di != NULL)
1364 				{
1365 					while (di != NULL)
1366 					{
1367 						if (di->Name != NAME_None)
1368 						{
1369 							const PClass *ti = PClass::FindClass(di->Name);
1370 							if (ti) P_DropItem (player->mo, ti, di->amount, di->probability);
1371 						}
1372 						di = di->Next;
1373 					}
1374 				} else
1375 				// kgDROP - end
1376 				if (weap->SpawnState != NULL &&
1377 					weap->SpawnState != ::GetDefault<AActor>()->SpawnState)
1378 				{
1379 					item = P_DropItem (this, weap->GetClass(), -1, 256);
1380 					if (item != NULL && item->IsKindOf(RUNTIME_CLASS(AWeapon)))
1381 					{
1382 						if (weap->AmmoGive1 && weap->Ammo1)
1383 						{
1384 							static_cast<AWeapon *>(item)->AmmoGive1 = weap->Ammo1->Amount;
1385 						}
1386 						if (weap->AmmoGive2 && weap->Ammo2)
1387 						{
1388 							static_cast<AWeapon *>(item)->AmmoGive2 = weap->Ammo2->Amount;
1389 						}
1390 						item->ItemFlags |= IF_IGNORESKILL;
1391 					}
1392 				}
1393 				else
1394 				{
1395 					item = P_DropItem (this, weap->AmmoType1, -1, 256);
1396 					if (item != NULL)
1397 					{
1398 						item->Amount = weap->Ammo1->Amount;
1399 						item->ItemFlags |= IF_IGNORESKILL;
1400 					}
1401 					item = P_DropItem (this, weap->AmmoType2, -1, 256);
1402 					if (item != NULL)
1403 					{
1404 						item->Amount = weap->Ammo2->Amount;
1405 						item->ItemFlags |= IF_IGNORESKILL;
1406 					}
1407 				}
1408 			}
1409 		}
1410 		if (!multiplayer && level.info->deathsequence != NAME_None)
1411 		{
1412 			F_StartIntermission(level.info->deathsequence, FSTATE_EndingGame);
1413 		}
1414 	}
1415 }
1416 
1417 //===========================================================================
1418 //
1419 // APlayerPawn :: TweakSpeeds
1420 //
1421 //===========================================================================
1422 
TweakSpeeds(int & forward,int & side)1423 void APlayerPawn::TweakSpeeds (int &forward, int &side)
1424 {
1425 	// Strife's player can't run when its healh is below 10
1426 	if (health <= RunHealth)
1427 	{
1428 		forward = clamp(forward, -0x1900, 0x1900);
1429 		side = clamp(side, -0x1800, 0x1800);
1430 	}
1431 
1432 	// [GRB]
1433 	if ((unsigned int)(forward + 0x31ff) < 0x63ff)
1434 	{
1435 		forward = FixedMul (forward, ForwardMove1);
1436 	}
1437 	else
1438 	{
1439 		forward = FixedMul (forward, ForwardMove2);
1440 	}
1441 	if ((unsigned int)(side + 0x27ff) < 0x4fff)
1442 	{
1443 		side = FixedMul (side, SideMove1);
1444 	}
1445 	else
1446 	{
1447 		side = FixedMul (side, SideMove2);
1448 	}
1449 
1450 	if (!player->morphTics && Inventory != NULL)
1451 	{
1452 		fixed_t factor = Inventory->GetSpeedFactor ();
1453 		forward = FixedMul(forward, factor);
1454 		side = FixedMul(side, factor);
1455 	}
1456 }
1457 
1458 //===========================================================================
1459 //
1460 // A_PlayerScream
1461 //
1462 // try to find the appropriate death sound and use suitable
1463 // replacements if necessary
1464 //
1465 //===========================================================================
1466 
DEFINE_ACTION_FUNCTION(AActor,A_PlayerScream)1467 DEFINE_ACTION_FUNCTION(AActor, A_PlayerScream)
1468 {
1469 	int sound = 0;
1470 	int chan = CHAN_VOICE;
1471 
1472 	if (self->player == NULL || self->DeathSound != 0)
1473 	{
1474 		if (self->DeathSound != 0)
1475 		{
1476 			S_Sound (self, CHAN_VOICE, self->DeathSound, 1, ATTN_NORM);
1477 		}
1478 		else
1479 		{
1480 			S_Sound (self, CHAN_VOICE, "*death", 1, ATTN_NORM);
1481 		}
1482 		return;
1483 	}
1484 
1485 	// Handle the different player death screams
1486 	if ((((level.flags >> 15) | (dmflags)) &
1487 		(DF_FORCE_FALLINGZD | DF_FORCE_FALLINGHX)) &&
1488 		self->velz <= -39*FRACUNIT)
1489 	{
1490 		sound = S_FindSkinnedSound (self, "*splat");
1491 		chan = CHAN_BODY;
1492 	}
1493 
1494 	if (!sound && self->special1<10)
1495 	{ // Wimpy death sound
1496 		sound = S_FindSkinnedSoundEx (self, "*wimpydeath", self->player->LastDamageType);
1497 	}
1498 	if (!sound && self->health <= -50)
1499 	{
1500 		if (self->health > -100)
1501 		{ // Crazy death sound
1502 			sound = S_FindSkinnedSoundEx (self, "*crazydeath", self->player->LastDamageType);
1503 		}
1504 		if (!sound)
1505 		{ // Extreme death sound
1506 			sound = S_FindSkinnedSoundEx (self, "*xdeath", self->player->LastDamageType);
1507 			if (!sound)
1508 			{
1509 				sound = S_FindSkinnedSoundEx (self, "*gibbed", self->player->LastDamageType);
1510 				chan = CHAN_BODY;
1511 			}
1512 		}
1513 	}
1514 	if (!sound)
1515 	{ // Normal death sound
1516 		sound = S_FindSkinnedSoundEx (self, "*death", self->player->LastDamageType);
1517 	}
1518 
1519 	if (chan != CHAN_VOICE)
1520 	{
1521 		for (int i = 0; i < 8; ++i)
1522 		{ // Stop most playing sounds from this player.
1523 		  // This is mainly to stop *land from messing up *splat.
1524 			if (i != CHAN_WEAPON && i != CHAN_VOICE)
1525 			{
1526 				S_StopSound (self, i);
1527 			}
1528 		}
1529 	}
1530 	S_Sound (self, chan, sound, 1, ATTN_NORM);
1531 }
1532 
1533 
1534 //----------------------------------------------------------------------------
1535 //
1536 // PROC A_SkullPop
1537 //
1538 //----------------------------------------------------------------------------
1539 
DEFINE_ACTION_FUNCTION_PARAMS(AActor,A_SkullPop)1540 DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SkullPop)
1541 {
1542 	ACTION_PARAM_START(1);
1543 	ACTION_PARAM_CLASS(spawntype, 0);
1544 
1545 	APlayerPawn *mo;
1546 	player_t *player;
1547 
1548 	// [GRB] Parameterized version
1549 	if (!spawntype || !spawntype->IsDescendantOf (RUNTIME_CLASS (APlayerChunk)))
1550 	{
1551 		spawntype = PClass::FindClass("BloodySkull");
1552 		if (spawntype == NULL) return;
1553 	}
1554 
1555 	self->flags &= ~MF_SOLID;
1556 	mo = (APlayerPawn *)Spawn (spawntype, self->PosPlusZ(48*FRACUNIT), NO_REPLACE);
1557 	//mo->target = self;
1558 	mo->velx = pr_skullpop.Random2() << 9;
1559 	mo->vely = pr_skullpop.Random2() << 9;
1560 	mo->velz = 2*FRACUNIT + (pr_skullpop() << 6);
1561 	// Attach player mobj to bloody skull
1562 	player = self->player;
1563 	self->player = NULL;
1564 	mo->ObtainInventory (self);
1565 	mo->player = player;
1566 	mo->health = self->health;
1567 	mo->angle = self->angle;
1568 	if (player != NULL)
1569 	{
1570 		player->mo = mo;
1571 		player->damagecount = 32;
1572 	}
1573 	for (int i = 0; i < MAXPLAYERS; ++i)
1574 	{
1575 		if (playeringame[i] && players[i].camera == self)
1576 		{
1577 			players[i].camera = mo;
1578 		}
1579 	}
1580 }
1581 
1582 //----------------------------------------------------------------------------
1583 //
1584 // PROC A_CheckSkullDone
1585 //
1586 //----------------------------------------------------------------------------
1587 
DEFINE_ACTION_FUNCTION(AActor,A_CheckPlayerDone)1588 DEFINE_ACTION_FUNCTION(AActor, A_CheckPlayerDone)
1589 {
1590 	if (self->player == NULL)
1591 	{
1592 		self->Destroy ();
1593 	}
1594 }
1595 
1596 //===========================================================================
1597 //
1598 // P_CheckPlayerSprites
1599 //
1600 // Here's the place where crouching sprites are handled.
1601 // R_ProjectSprite() calls this for any players.
1602 //
1603 //===========================================================================
1604 
P_CheckPlayerSprite(AActor * actor,int & spritenum,fixed_t & scalex,fixed_t & scaley)1605 void P_CheckPlayerSprite(AActor *actor, int &spritenum, fixed_t &scalex, fixed_t &scaley)
1606 {
1607 	player_t *player = actor->player;
1608 	int crouchspriteno;
1609 
1610 	if (player->userinfo.GetSkin() != 0 && !(actor->flags4 & MF4_NOSKIN))
1611 	{
1612 		// Convert from default scale to skin scale.
1613 		fixed_t defscaleY = actor->GetDefault()->scaleY;
1614 		fixed_t defscaleX = actor->GetDefault()->scaleX;
1615 		scaley = Scale(scaley, skins[player->userinfo.GetSkin()].ScaleY, defscaleY);
1616 		scalex = Scale(scalex, skins[player->userinfo.GetSkin()].ScaleX, defscaleX);
1617 	}
1618 
1619 	// Set the crouch sprite?
1620 	if (player->crouchfactor < FRACUNIT*3/4)
1621 	{
1622 		if (spritenum == actor->SpawnState->sprite || spritenum == player->mo->crouchsprite)
1623 		{
1624 			crouchspriteno = player->mo->crouchsprite;
1625 		}
1626 		else if (!(actor->flags4 & MF4_NOSKIN) &&
1627 				(spritenum == skins[player->userinfo.GetSkin()].sprite ||
1628 				 spritenum == skins[player->userinfo.GetSkin()].crouchsprite))
1629 		{
1630 			crouchspriteno = skins[player->userinfo.GetSkin()].crouchsprite;
1631 		}
1632 		else
1633 		{ // no sprite -> squash the existing one
1634 			crouchspriteno = -1;
1635 		}
1636 
1637 		if (crouchspriteno > 0)
1638 		{
1639 			spritenum = crouchspriteno;
1640 		}
1641 		else if (player->playerstate != PST_DEAD && player->crouchfactor < FRACUNIT*3/4)
1642 		{
1643 			scaley /= 2;
1644 		}
1645 	}
1646 }
1647 
1648 /*
1649 ==================
1650 =
1651 = P_Thrust
1652 =
1653 = moves the given origin along a given angle
1654 =
1655 ==================
1656 */
1657 
P_SideThrust(player_t * player,angle_t angle,fixed_t move)1658 void P_SideThrust (player_t *player, angle_t angle, fixed_t move)
1659 {
1660 	angle = (angle - ANGLE_90) >> ANGLETOFINESHIFT;
1661 
1662 	player->mo->velx += FixedMul (move, finecosine[angle]);
1663 	player->mo->vely += FixedMul (move, finesine[angle]);
1664 }
1665 
P_ForwardThrust(player_t * player,angle_t angle,fixed_t move)1666 void P_ForwardThrust (player_t *player, angle_t angle, fixed_t move)
1667 {
1668 	angle >>= ANGLETOFINESHIFT;
1669 
1670 	if ((player->mo->waterlevel || (player->mo->flags & MF_NOGRAVITY))
1671 		&& player->mo->pitch != 0)
1672 	{
1673 		angle_t pitch = (angle_t)player->mo->pitch >> ANGLETOFINESHIFT;
1674 		fixed_t zpush = FixedMul (move, finesine[pitch]);
1675 		if (player->mo->waterlevel && player->mo->waterlevel < 2 && zpush < 0)
1676 			zpush = 0;
1677 		player->mo->velz -= zpush;
1678 		move = FixedMul (move, finecosine[pitch]);
1679 	}
1680 	player->mo->velx += FixedMul (move, finecosine[angle]);
1681 	player->mo->vely += FixedMul (move, finesine[angle]);
1682 }
1683 
1684 //
1685 // P_Bob
1686 // Same as P_Thrust, but only affects bobbing.
1687 //
1688 // killough 10/98: We apply thrust separately between the real physical player
1689 // and the part which affects bobbing. This way, bobbing only comes from player
1690 // motion, nothing external, avoiding many problems, e.g. bobbing should not
1691 // occur on conveyors, unless the player walks on one, and bobbing should be
1692 // reduced at a regular rate, even on ice (where the player coasts).
1693 //
1694 
P_Bob(player_t * player,angle_t angle,fixed_t move,bool forward)1695 void P_Bob (player_t *player, angle_t angle, fixed_t move, bool forward)
1696 {
1697 	if (forward
1698 		&& (player->mo->waterlevel || (player->mo->flags & MF_NOGRAVITY))
1699 		&& player->mo->pitch != 0)
1700 	{
1701 		angle_t pitch = (angle_t)player->mo->pitch >> ANGLETOFINESHIFT;
1702 		move = FixedMul (move, finecosine[pitch]);
1703 	}
1704 
1705 	angle >>= ANGLETOFINESHIFT;
1706 
1707 	player->velx += FixedMul(move, finecosine[angle]);
1708 	player->vely += FixedMul(move, finesine[angle]);
1709 }
1710 
1711 /*
1712 ==================
1713 =
1714 = P_CalcHeight
1715 =
1716 =
1717 Calculate the walking / running height adjustment
1718 =
1719 ==================
1720 */
1721 
P_CalcHeight(player_t * player)1722 void P_CalcHeight (player_t *player)
1723 {
1724 	int 		angle;
1725 	fixed_t 	bob;
1726 	bool		still = false;
1727 
1728 	// Regular movement bobbing
1729 	// (needs to be calculated for gun swing even if not on ground)
1730 
1731 	// killough 10/98: Make bobbing depend only on player-applied motion.
1732 	//
1733 	// Note: don't reduce bobbing here if on ice: if you reduce bobbing here,
1734 	// it causes bobbing jerkiness when the player moves from ice to non-ice,
1735 	// and vice-versa.
1736 
1737 	if (player->cheats & CF_NOCLIP2)
1738 	{
1739 		player->bob = 0;
1740 	}
1741 	else if ((player->mo->flags & MF_NOGRAVITY) && !player->onground)
1742 	{
1743 		player->bob = FRACUNIT / 2;
1744 	}
1745 	else
1746 	{
1747 		player->bob = DMulScale16 (player->velx, player->velx, player->vely, player->vely);
1748 		if (player->bob == 0)
1749 		{
1750 			still = true;
1751 		}
1752 		else
1753 		{
1754 			player->bob = FixedMul (player->bob, player->userinfo.GetMoveBob());
1755 
1756 			if (player->bob > MAXBOB)
1757 				player->bob = MAXBOB;
1758 		}
1759 	}
1760 
1761 	fixed_t defaultviewheight = player->mo->ViewHeight + player->crouchviewdelta;
1762 
1763 	if (player->cheats & CF_NOVELOCITY)
1764 	{
1765 		player->viewz = player->mo->Z() + defaultviewheight;
1766 
1767 		if (player->viewz > player->mo->ceilingz-4*FRACUNIT)
1768 			player->viewz = player->mo->ceilingz-4*FRACUNIT;
1769 
1770 		return;
1771 	}
1772 
1773 	if (still)
1774 	{
1775 		if (player->health > 0)
1776 		{
1777 			angle = DivScale13 (level.time, 120*TICRATE/35) & FINEMASK;
1778 			bob = FixedMul (player->userinfo.GetStillBob(), finesine[angle]);
1779 		}
1780 		else
1781 		{
1782 			bob = 0;
1783 		}
1784 	}
1785 	else
1786 	{
1787 		// DivScale 13 because FINEANGLES == (1<<13)
1788 		angle = DivScale13 (level.time, 20*TICRATE/35) & FINEMASK;
1789 		bob = FixedMul (player->bob>>(player->mo->waterlevel > 1 ? 2 : 1), finesine[angle]);
1790 	}
1791 
1792 	// move viewheight
1793 	if (player->playerstate == PST_LIVE)
1794 	{
1795 		player->viewheight += player->deltaviewheight;
1796 
1797 		if (player->viewheight > defaultviewheight)
1798 		{
1799 			player->viewheight = defaultviewheight;
1800 			player->deltaviewheight = 0;
1801 		}
1802 		else if (player->viewheight < (defaultviewheight>>1))
1803 		{
1804 			player->viewheight = defaultviewheight>>1;
1805 			if (player->deltaviewheight <= 0)
1806 				player->deltaviewheight = 1;
1807 		}
1808 
1809 		if (player->deltaviewheight)
1810 		{
1811 			player->deltaviewheight += FRACUNIT/4;
1812 			if (!player->deltaviewheight)
1813 				player->deltaviewheight = 1;
1814 		}
1815 	}
1816 
1817 	if (player->morphTics)
1818 	{
1819 		bob = 0;
1820 	}
1821 	player->viewz = player->mo->Z() + player->viewheight + bob;
1822 	if (player->mo->floorclip && player->playerstate != PST_DEAD
1823 		&& player->mo->Z() <= player->mo->floorz)
1824 	{
1825 		player->viewz -= player->mo->floorclip;
1826 	}
1827 	if (player->viewz > player->mo->ceilingz - 4*FRACUNIT)
1828 	{
1829 		player->viewz = player->mo->ceilingz - 4*FRACUNIT;
1830 	}
1831 	if (player->viewz < player->mo->floorz + 4*FRACUNIT)
1832 	{
1833 		player->viewz = player->mo->floorz + 4*FRACUNIT;
1834 	}
1835 }
1836 
1837 /*
1838 =================
1839 =
1840 = P_MovePlayer
1841 =
1842 =================
1843 */
1844 CUSTOM_CVAR (Float, sv_aircontrol, 0.00390625f, CVAR_SERVERINFO|CVAR_NOSAVE)
1845 {
1846 	level.aircontrol = (fixed_t)(self * 65536.f);
1847 	G_AirControlChanged ();
1848 }
1849 
P_MovePlayer(player_t * player)1850 void P_MovePlayer (player_t *player)
1851 {
1852 	ticcmd_t *cmd = &player->cmd;
1853 	APlayerPawn *mo = player->mo;
1854 
1855 	// [RH] 180-degree turn overrides all other yaws
1856 	if (player->turnticks)
1857 	{
1858 		player->turnticks--;
1859 		mo->angle += (ANGLE_180 / TURN180_TICKS);
1860 	}
1861 	else
1862 	{
1863 		mo->angle += cmd->ucmd.yaw << 16;
1864 	}
1865 
1866 	player->onground = (mo->Z() <= mo->floorz) || (mo->flags2 & MF2_ONMOBJ) || (mo->BounceFlags & BOUNCE_MBF) || (player->cheats & CF_NOCLIP2);
1867 
1868 	// killough 10/98:
1869 	//
1870 	// We must apply thrust to the player and bobbing separately, to avoid
1871 	// anomalies. The thrust applied to bobbing is always the same strength on
1872 	// ice, because the player still "works just as hard" to move, while the
1873 	// thrust applied to the movement varies with 'movefactor'.
1874 
1875 	if (cmd->ucmd.forwardmove | cmd->ucmd.sidemove)
1876 	{
1877 		fixed_t forwardmove, sidemove;
1878 		int bobfactor;
1879 		int friction, movefactor;
1880 		int fm, sm;
1881 
1882 		movefactor = P_GetMoveFactor (mo, &friction);
1883 		bobfactor = friction < ORIG_FRICTION ? movefactor : ORIG_FRICTION_FACTOR;
1884 		if (!player->onground && !(player->mo->flags & MF_NOGRAVITY) && !player->mo->waterlevel)
1885 		{
1886 			// [RH] allow very limited movement if not on ground.
1887 			movefactor = FixedMul (movefactor, level.aircontrol);
1888 			bobfactor = FixedMul (bobfactor, level.aircontrol);
1889 		}
1890 
1891 		fm = cmd->ucmd.forwardmove;
1892 		sm = cmd->ucmd.sidemove;
1893 		mo->TweakSpeeds (fm, sm);
1894 		fm = FixedMul (fm, player->mo->Speed);
1895 		sm = FixedMul (sm, player->mo->Speed);
1896 
1897 		// When crouching, speed and bobbing have to be reduced
1898 		if (player->CanCrouch() && player->crouchfactor != FRACUNIT)
1899 		{
1900 			fm = FixedMul(fm, player->crouchfactor);
1901 			sm = FixedMul(sm, player->crouchfactor);
1902 			bobfactor = FixedMul(bobfactor, player->crouchfactor);
1903 		}
1904 
1905 		forwardmove = Scale (fm, movefactor * 35, TICRATE << 8);
1906 		sidemove = Scale (sm, movefactor * 35, TICRATE << 8);
1907 
1908 		if (forwardmove)
1909 		{
1910 			P_Bob (player, mo->angle, (cmd->ucmd.forwardmove * bobfactor) >> 8, true);
1911 			P_ForwardThrust (player, mo->angle, forwardmove);
1912 		}
1913 		if (sidemove)
1914 		{
1915 			P_Bob (player, mo->angle-ANG90, (cmd->ucmd.sidemove * bobfactor) >> 8, false);
1916 			P_SideThrust (player, mo->angle, sidemove);
1917 		}
1918 
1919 		if (debugfile)
1920 		{
1921 			fprintf (debugfile, "move player for pl %d%c: (%d,%d,%d) (%d,%d) %d %d w%d [", int(player-players),
1922 				player->cheats&CF_PREDICTING?'p':' ',
1923 				player->mo->X(), player->mo->Y(), player->mo->Z(),forwardmove, sidemove, movefactor, friction, player->mo->waterlevel);
1924 			msecnode_t *n = player->mo->touching_sectorlist;
1925 			while (n != NULL)
1926 			{
1927 				fprintf (debugfile, "%td ", n->m_sector-sectors);
1928 				n = n->m_tnext;
1929 			}
1930 			fprintf (debugfile, "]\n");
1931 		}
1932 
1933 		if (!(player->cheats & CF_PREDICTING) && (forwardmove|sidemove))
1934 		{
1935 			player->mo->PlayRunning ();
1936 		}
1937 
1938 		if (player->cheats & CF_REVERTPLEASE)
1939 		{
1940 			player->cheats &= ~CF_REVERTPLEASE;
1941 			player->camera = player->mo;
1942 		}
1943 	}
1944 }
1945 
1946 //==========================================================================
1947 //
1948 // P_FallingDamage
1949 //
1950 //==========================================================================
1951 
P_FallingDamage(AActor * actor)1952 void P_FallingDamage (AActor *actor)
1953 {
1954 	int damagestyle;
1955 	int damage;
1956 	fixed_t vel;
1957 
1958 	damagestyle = ((level.flags >> 15) | (dmflags)) &
1959 		(DF_FORCE_FALLINGZD | DF_FORCE_FALLINGHX);
1960 
1961 	if (damagestyle == 0)
1962 		return;
1963 
1964 	if (actor->floorsector->Flags & SECF_NOFALLINGDAMAGE)
1965 		return;
1966 
1967 	vel = abs(actor->velz);
1968 
1969 	// Since Hexen falling damage is stronger than ZDoom's, it takes
1970 	// precedence. ZDoom falling damage may not be as strong, but it
1971 	// gets felt sooner.
1972 
1973 	switch (damagestyle)
1974 	{
1975 	case DF_FORCE_FALLINGHX:		// Hexen falling damage
1976 		if (vel <= 23*FRACUNIT)
1977 		{ // Not fast enough to hurt
1978 			return;
1979 		}
1980 		if (vel >= 63*FRACUNIT)
1981 		{ // automatic death
1982 			damage = 1000000;
1983 		}
1984 		else
1985 		{
1986 			vel = FixedMul (vel, 16*FRACUNIT/23);
1987 			damage = ((FixedMul (vel, vel) / 10) >> FRACBITS) - 24;
1988 			if (actor->velz > -39*FRACUNIT && damage > actor->health
1989 				&& actor->health != 1)
1990 			{ // No-death threshold
1991 				damage = actor->health-1;
1992 			}
1993 		}
1994 		break;
1995 
1996 	case DF_FORCE_FALLINGZD:		// ZDoom falling damage
1997 		if (vel <= 19*FRACUNIT)
1998 		{ // Not fast enough to hurt
1999 			return;
2000 		}
2001 		if (vel >= 84*FRACUNIT)
2002 		{ // automatic death
2003 			damage = 1000000;
2004 		}
2005 		else
2006 		{
2007 			damage = ((MulScale23 (vel, vel*11) >> FRACBITS) - 30) / 2;
2008 			if (damage < 1)
2009 			{
2010 				damage = 1;
2011 			}
2012 		}
2013 		break;
2014 
2015 	case DF_FORCE_FALLINGST:		// Strife falling damage
2016 		if (vel <= 20*FRACUNIT)
2017 		{ // Not fast enough to hurt
2018 			return;
2019 		}
2020 		// The minimum amount of damage you take from falling in Strife
2021 		// is 52. Ouch!
2022 		damage = vel / 25000;
2023 		break;
2024 
2025 	default:
2026 		return;
2027 	}
2028 
2029 	if (actor->player)
2030 	{
2031 		S_Sound (actor, CHAN_AUTO, "*land", 1, ATTN_NORM);
2032 		P_NoiseAlert (actor, actor, true);
2033 		if (damage == 1000000 && (actor->player->cheats & (CF_GODMODE | CF_BUDDHA)))
2034 		{
2035 			damage = 999;
2036 		}
2037 	}
2038 	P_DamageMobj (actor, NULL, NULL, damage, NAME_Falling);
2039 }
2040 
2041 //==========================================================================
2042 //
2043 // P_DeathThink
2044 //
2045 //==========================================================================
2046 
P_DeathThink(player_t * player)2047 void P_DeathThink (player_t *player)
2048 {
2049 	int dir;
2050 	angle_t delta;
2051 	int lookDelta;
2052 
2053 	P_MovePsprites (player);
2054 
2055 	player->onground = (player->mo->Z() <= player->mo->floorz);
2056 	if (player->mo->IsKindOf (RUNTIME_CLASS(APlayerChunk)))
2057 	{ // Flying bloody skull or flying ice chunk
2058 		player->viewheight = 6 * FRACUNIT;
2059 		player->deltaviewheight = 0;
2060 		if (player->onground)
2061 		{
2062 			if (player->mo->pitch > -(int)ANGLE_1*19)
2063 			{
2064 				lookDelta = (-(int)ANGLE_1*19 - player->mo->pitch) / 8;
2065 				player->mo->pitch += lookDelta;
2066 			}
2067 		}
2068 	}
2069 	else if (!(player->mo->flags & MF_ICECORPSE))
2070 	{ // Fall to ground (if not frozen)
2071 		player->deltaviewheight = 0;
2072 		if (player->viewheight > 6*FRACUNIT)
2073 		{
2074 			player->viewheight -= FRACUNIT;
2075 		}
2076 		if (player->viewheight < 6*FRACUNIT)
2077 		{
2078 			player->viewheight = 6*FRACUNIT;
2079 		}
2080 		if (player->mo->pitch < 0)
2081 		{
2082 			player->mo->pitch += ANGLE_1*3;
2083 		}
2084 		else if (player->mo->pitch > 0)
2085 		{
2086 			player->mo->pitch -= ANGLE_1*3;
2087 		}
2088 		if (abs(player->mo->pitch) < ANGLE_1*3)
2089 		{
2090 			player->mo->pitch = 0;
2091 		}
2092 	}
2093 	P_CalcHeight (player);
2094 
2095 	if (player->attacker && player->attacker != player->mo)
2096 	{ // Watch killer
2097 		dir = P_FaceMobj (player->mo, player->attacker, &delta);
2098 		if (delta < ANGLE_1*10)
2099 		{ // Looking at killer, so fade damage and poison counters
2100 			if (player->damagecount)
2101 			{
2102 				player->damagecount--;
2103 			}
2104 			if (player->poisoncount)
2105 			{
2106 				player->poisoncount--;
2107 			}
2108 		}
2109 		delta /= 8;
2110 		if (delta > ANGLE_1*5)
2111 		{
2112 			delta = ANGLE_1*5;
2113 		}
2114 		if (dir)
2115 		{ // Turn clockwise
2116 			player->mo->angle += delta;
2117 		}
2118 		else
2119 		{ // Turn counter clockwise
2120 			player->mo->angle -= delta;
2121 		}
2122 	}
2123 	else
2124 	{
2125 		if (player->damagecount)
2126 		{
2127 			player->damagecount--;
2128 		}
2129 		if (player->poisoncount)
2130 		{
2131 			player->poisoncount--;
2132 		}
2133 	}
2134 
2135 	if ((player->cmd.ucmd.buttons & BT_USE ||
2136 		((multiplayer || alwaysapplydmflags) && (dmflags & DF_FORCE_RESPAWN))) && !(dmflags2 & DF2_NO_RESPAWN))
2137 	{
2138 		if (level.time >= player->respawn_time || ((player->cmd.ucmd.buttons & BT_USE) && player->Bot == NULL))
2139 		{
2140 			player->cls = NULL;		// Force a new class if the player is using a random class
2141 			player->playerstate = (multiplayer || (level.flags2 & LEVEL2_ALLOWRESPAWN)) ? PST_REBORN : PST_ENTER;
2142 			if (player->mo->special1 > 2)
2143 			{
2144 				player->mo->special1 = 0;
2145 			}
2146 		}
2147 	}
2148 }
2149 
2150 //----------------------------------------------------------------------------
2151 //
2152 // PROC P_CrouchMove
2153 //
2154 //----------------------------------------------------------------------------
2155 
P_CrouchMove(player_t * player,int direction)2156 void P_CrouchMove(player_t * player, int direction)
2157 {
2158 	fixed_t defaultheight = player->mo->GetDefault()->height;
2159 	fixed_t savedheight = player->mo->height;
2160 	fixed_t crouchspeed = direction * CROUCHSPEED;
2161 	fixed_t oldheight = player->viewheight;
2162 
2163 	player->crouchdir = (signed char) direction;
2164 	player->crouchfactor += crouchspeed;
2165 
2166 	// check whether the move is ok
2167 	player->mo->height = FixedMul(defaultheight, player->crouchfactor);
2168 	if (!P_TryMove(player->mo, player->mo->X(), player->mo->Y(), false, NULL))
2169 	{
2170 		player->mo->height = savedheight;
2171 		if (direction > 0)
2172 		{
2173 			// doesn't fit
2174 			player->crouchfactor -= crouchspeed;
2175 			return;
2176 		}
2177 	}
2178 	player->mo->height = savedheight;
2179 
2180 	player->crouchfactor = clamp<fixed_t>(player->crouchfactor, FRACUNIT/2, FRACUNIT);
2181 	player->viewheight = FixedMul(player->mo->ViewHeight, player->crouchfactor);
2182 	player->crouchviewdelta = player->viewheight - player->mo->ViewHeight;
2183 
2184 	// Check for eyes going above/below fake floor due to crouching motion.
2185 	P_CheckFakeFloorTriggers(player->mo, player->mo->Z() + oldheight, true);
2186 }
2187 
2188 //----------------------------------------------------------------------------
2189 //
2190 // PROC P_PlayerThink
2191 //
2192 //----------------------------------------------------------------------------
2193 
P_PlayerThink(player_t * player)2194 void P_PlayerThink (player_t *player)
2195 {
2196 	ticcmd_t *cmd;
2197 
2198 	if (player->mo == NULL)
2199 	{
2200 		I_Error ("No player %td start\n", player - players + 1);
2201 	}
2202 
2203 	if (debugfile && !(player->cheats & CF_PREDICTING))
2204 	{
2205 		fprintf (debugfile, "tic %d for pl %td: (%d, %d, %d, %u) b:%02x p:%d y:%d f:%d s:%d u:%d\n",
2206 			gametic, player-players, player->mo->X(), player->mo->Y(), player->mo->Z(),
2207 			player->mo->angle>>ANGLETOFINESHIFT, player->cmd.ucmd.buttons,
2208 			player->cmd.ucmd.pitch, player->cmd.ucmd.yaw, player->cmd.ucmd.forwardmove,
2209 			player->cmd.ucmd.sidemove, player->cmd.ucmd.upmove);
2210 	}
2211 
2212 	// [RH] Zoom the player's FOV
2213 	float desired = player->DesiredFOV;
2214 	// Adjust FOV using on the currently held weapon.
2215 	if (player->playerstate != PST_DEAD &&		// No adjustment while dead.
2216 		player->ReadyWeapon != NULL &&			// No adjustment if no weapon.
2217 		player->ReadyWeapon->FOVScale != 0)		// No adjustment if the adjustment is zero.
2218 	{
2219 		// A negative scale is used to prevent G_AddViewAngle/G_AddViewPitch
2220 		// from scaling with the FOV scale.
2221 		desired *= fabs(player->ReadyWeapon->FOVScale);
2222 	}
2223 	if (player->FOV != desired)
2224 	{
2225 		if (fabsf (player->FOV - desired) < 7.f)
2226 		{
2227 			player->FOV = desired;
2228 		}
2229 		else
2230 		{
2231 			float zoom = MAX(7.f, fabsf(player->FOV - desired) * 0.025f);
2232 			if (player->FOV > desired)
2233 			{
2234 				player->FOV = player->FOV - zoom;
2235 			}
2236 			else
2237 			{
2238 				player->FOV = player->FOV + zoom;
2239 			}
2240 		}
2241 	}
2242 	if (player->inventorytics)
2243 	{
2244 		player->inventorytics--;
2245 	}
2246 	// Don't interpolate the view for more than one tic
2247 	player->cheats &= ~CF_INTERPVIEW;
2248 
2249 	// No-clip cheat
2250 	if ((player->cheats & (CF_NOCLIP | CF_NOCLIP2)) == CF_NOCLIP2)
2251 	{ // No noclip2 without noclip
2252 		player->cheats &= ~CF_NOCLIP2;
2253 	}
2254 	if (player->cheats & (CF_NOCLIP | CF_NOCLIP2) || (player->mo->GetDefault()->flags & MF_NOCLIP))
2255 	{
2256 		player->mo->flags |= MF_NOCLIP;
2257 	}
2258 	else
2259 	{
2260 		player->mo->flags &= ~MF_NOCLIP;
2261 	}
2262 	if (player->cheats & CF_NOCLIP2)
2263 	{
2264 		player->mo->flags |= MF_NOGRAVITY;
2265 	}
2266 	else if (!(player->mo->flags2 & MF2_FLY) && !(player->mo->GetDefault()->flags & MF_NOGRAVITY))
2267 	{
2268 		player->mo->flags &= ~MF_NOGRAVITY;
2269 	}
2270 	cmd = &player->cmd;
2271 
2272 	// Make unmodified copies for ACS's GetPlayerInput.
2273 	player->original_oldbuttons = player->original_cmd.buttons;
2274 	player->original_cmd = cmd->ucmd;
2275 
2276 	if (player->mo->flags & MF_JUSTATTACKED)
2277 	{ // Chainsaw/Gauntlets attack auto forward motion
2278 		cmd->ucmd.yaw = 0;
2279 		cmd->ucmd.forwardmove = 0xc800/2;
2280 		cmd->ucmd.sidemove = 0;
2281 		player->mo->flags &= ~MF_JUSTATTACKED;
2282 	}
2283 
2284 	bool totallyfrozen = P_IsPlayerTotallyFrozen(player);
2285 
2286 	// [RH] Being totally frozen zeros out most input parameters.
2287 	if (totallyfrozen)
2288 	{
2289 		if (gamestate == GS_TITLELEVEL)
2290 		{
2291 			cmd->ucmd.buttons = 0;
2292 		}
2293 		else
2294 		{
2295 			cmd->ucmd.buttons &= BT_USE;
2296 		}
2297 		cmd->ucmd.pitch = 0;
2298 		cmd->ucmd.yaw = 0;
2299 		cmd->ucmd.roll = 0;
2300 		cmd->ucmd.forwardmove = 0;
2301 		cmd->ucmd.sidemove = 0;
2302 		cmd->ucmd.upmove = 0;
2303 		player->turnticks = 0;
2304 	}
2305 	else if (player->cheats & CF_FROZEN)
2306 	{
2307 		cmd->ucmd.forwardmove = 0;
2308 		cmd->ucmd.sidemove = 0;
2309 		cmd->ucmd.upmove = 0;
2310 	}
2311 
2312 	// Handle crouching
2313 	if (player->cmd.ucmd.buttons & BT_JUMP)
2314 	{
2315 		player->cmd.ucmd.buttons &= ~BT_CROUCH;
2316 	}
2317 	if (player->CanCrouch() && player->health > 0 && level.IsCrouchingAllowed())
2318 	{
2319 		if (!totallyfrozen)
2320 		{
2321 			int crouchdir = player->crouching;
2322 
2323 			if (crouchdir == 0)
2324 			{
2325 				crouchdir = (player->cmd.ucmd.buttons & BT_CROUCH) ? -1 : 1;
2326 			}
2327 			else if (player->cmd.ucmd.buttons & BT_CROUCH)
2328 			{
2329 				player->crouching = 0;
2330 			}
2331 			if (crouchdir == 1 && player->crouchfactor < FRACUNIT &&
2332 				player->mo->Top() < player->mo->ceilingz)
2333 			{
2334 				P_CrouchMove(player, 1);
2335 			}
2336 			else if (crouchdir == -1 && player->crouchfactor > FRACUNIT/2)
2337 			{
2338 				P_CrouchMove(player, -1);
2339 			}
2340 		}
2341 	}
2342 	else
2343 	{
2344 		player->Uncrouch();
2345 	}
2346 
2347 	player->crouchoffset = -FixedMul(player->mo->ViewHeight, (FRACUNIT - player->crouchfactor));
2348 
2349 	// MUSINFO stuff
2350 	if (player->MUSINFOtics >= 0 && player->MUSINFOactor != NULL)
2351 	{
2352 		if (--player->MUSINFOtics < 0)
2353 		{
2354 			if (player - players == consoleplayer)
2355 			{
2356 				if (player->MUSINFOactor->args[0] != 0)
2357 				{
2358 					FName *music = level.info->MusicMap.CheckKey(player->MUSINFOactor->args[0]);
2359 
2360 					if (music != NULL)
2361 					{
2362 						S_ChangeMusic(music->GetChars(), player->MUSINFOactor->args[1]);
2363 					}
2364 				}
2365 				else
2366 				{
2367 					S_ChangeMusic("*");
2368 				}
2369 			}
2370 			DPrintf("MUSINFO change for player %d to %d\n", (int)(player - players), player->MUSINFOactor->args[0]);
2371 		}
2372 	}
2373 
2374 	if (player->playerstate == PST_DEAD)
2375 	{
2376 		player->Uncrouch();
2377 		P_DeathThink (player);
2378 		return;
2379 	}
2380 	if (player->jumpTics != 0)
2381 	{
2382 		player->jumpTics--;
2383 		if (player->onground && player->jumpTics < -18)
2384 		{
2385 			player->jumpTics = 0;
2386 		}
2387 	}
2388 	if (player->morphTics && !(player->cheats & CF_PREDICTING))
2389 	{
2390 		player->mo->MorphPlayerThink ();
2391 	}
2392 
2393 	// [RH] Look up/down stuff
2394 	if (!level.IsFreelookAllowed())
2395 	{
2396 		player->mo->pitch = 0;
2397 	}
2398 	else
2399 	{
2400 		int look = cmd->ucmd.pitch << 16;
2401 
2402 		// The player's view pitch is clamped between -32 and +56 degrees,
2403 		// which translates to about half a screen height up and (more than)
2404 		// one full screen height down from straight ahead when view panning
2405 		// is used.
2406 		if (look)
2407 		{
2408 			if (look == -32768 << 16)
2409 			{ // center view
2410 				player->centering = true;
2411 			}
2412 			else if (!player->centering)
2413 			{
2414 				fixed_t oldpitch = player->mo->pitch;
2415 				player->mo->pitch -= look;
2416 				if (look > 0)
2417 				{ // look up
2418 					player->mo->pitch = MAX(player->mo->pitch, player->MinPitch);
2419 					if (player->mo->pitch > oldpitch)
2420 					{
2421 						player->mo->pitch = player->MinPitch;
2422 					}
2423 				}
2424 				else
2425 				{ // look down
2426 					player->mo->pitch = MIN(player->mo->pitch, player->MaxPitch);
2427 					if (player->mo->pitch < oldpitch)
2428 					{
2429 						player->mo->pitch = player->MaxPitch;
2430 					}
2431 				}
2432 			}
2433 		}
2434 	}
2435 	if (player->centering)
2436 	{
2437 		if (abs(player->mo->pitch) > 2*ANGLE_1)
2438 		{
2439 			player->mo->pitch = FixedMul(player->mo->pitch, FRACUNIT*2/3);
2440 		}
2441 		else
2442 		{
2443 			player->mo->pitch = 0;
2444 			player->centering = false;
2445 			if (player - players == consoleplayer)
2446 			{
2447 				LocalViewPitch = 0;
2448 			}
2449 		}
2450 	}
2451 
2452 	// [RH] Check for fast turn around
2453 	if (cmd->ucmd.buttons & BT_TURN180 && !(player->oldbuttons & BT_TURN180))
2454 	{
2455 		player->turnticks = TURN180_TICKS;
2456 	}
2457 
2458 	// Handle movement
2459 	if (player->mo->reactiontime)
2460 	{ // Player is frozen
2461 		player->mo->reactiontime--;
2462 	}
2463 	else
2464 	{
2465 		P_MovePlayer (player);
2466 
2467 		// [RH] check for jump
2468 		if (cmd->ucmd.buttons & BT_JUMP)
2469 		{
2470 			if (player->crouchoffset != 0)
2471 			{
2472 				// Jumping while crouching will force an un-crouch but not jump
2473 				player->crouching = 1;
2474 			}
2475 			else if (player->mo->waterlevel >= 2)
2476 			{
2477 				player->mo->velz = FixedMul(4*FRACUNIT, player->mo->Speed);
2478 			}
2479 			else if (player->mo->flags & MF_NOGRAVITY)
2480 			{
2481 				player->mo->velz = 3*FRACUNIT;
2482 			}
2483 			else if (level.IsJumpingAllowed() && player->onground && player->jumpTics == 0)
2484 			{
2485 				fixed_t jumpvelz = player->mo->JumpZ * 35 / TICRATE;
2486 
2487 				// [BC] If the player has the high jump power, double his jump velocity.
2488 				if ( player->cheats & CF_HIGHJUMP )	jumpvelz *= 2;
2489 
2490 				player->mo->velz += jumpvelz;
2491 				player->mo->flags2 &= ~MF2_ONMOBJ;
2492 				player->jumpTics = -1;
2493 				if (!(player->cheats & CF_PREDICTING))
2494 					S_Sound(player->mo, CHAN_BODY, "*jump", 1, ATTN_NORM);
2495 			}
2496 		}
2497 
2498 		if (cmd->ucmd.upmove == -32768)
2499 		{ // Only land if in the air
2500 			if ((player->mo->flags & MF_NOGRAVITY) && player->mo->waterlevel < 2)
2501 			{
2502 				//player->mo->flags2 &= ~MF2_FLY;
2503 				player->mo->flags &= ~MF_NOGRAVITY;
2504 			}
2505 		}
2506 		else if (cmd->ucmd.upmove != 0)
2507 		{
2508 			// Clamp the speed to some reasonable maximum.
2509 			int magnitude = abs (cmd->ucmd.upmove);
2510 			if (magnitude > 0x300)
2511 			{
2512 				cmd->ucmd.upmove = ksgn (cmd->ucmd.upmove) * 0x300;
2513 			}
2514 			if (player->mo->waterlevel >= 2 || (player->mo->flags2 & MF2_FLY) || (player->cheats & CF_NOCLIP2))
2515 			{
2516 				player->mo->velz = FixedMul(player->mo->Speed, cmd->ucmd.upmove << 9);
2517 				if (player->mo->waterlevel < 2 && !(player->mo->flags & MF_NOGRAVITY))
2518 				{
2519 					player->mo->flags2 |= MF2_FLY;
2520 					player->mo->flags |= MF_NOGRAVITY;
2521 					if ((player->mo->velz <= -39 * FRACUNIT) && !(player->cheats & CF_PREDICTING))
2522 					{ // Stop falling scream
2523 						S_StopSound (player->mo, CHAN_VOICE);
2524 					}
2525 				}
2526 			}
2527 			else if (cmd->ucmd.upmove > 0 && !(player->cheats & CF_PREDICTING))
2528 			{
2529 				AInventory *fly = player->mo->FindInventory (NAME_ArtiFly);
2530 				if (fly != NULL)
2531 				{
2532 					player->mo->UseInventory (fly);
2533 				}
2534 			}
2535 		}
2536 	}
2537 
2538 	P_CalcHeight (player);
2539 
2540 	if (!(player->cheats & CF_PREDICTING))
2541 	{
2542 		P_PlayerOnSpecial3DFloor (player);
2543 		P_PlayerInSpecialSector (player);
2544 
2545 		if (player->mo->Z() <= player->mo->Sector->floorplane.ZatPoint(player->mo) ||
2546 			player->mo->waterlevel)
2547 		{
2548 			// Player must be touching the floor
2549 			P_PlayerOnSpecialFlat(player, P_GetThingFloorType(player->mo));
2550 		}
2551 		if (player->mo->velz <= -player->mo->FallingScreamMinSpeed &&
2552 			player->mo->velz >= -player->mo->FallingScreamMaxSpeed && !player->morphTics &&
2553 			player->mo->waterlevel == 0)
2554 		{
2555 			int id = S_FindSkinnedSound (player->mo, "*falling");
2556 			if (id != 0 && !S_IsActorPlayingSomething (player->mo, CHAN_VOICE, id))
2557 			{
2558 				S_Sound (player->mo, CHAN_VOICE, id, 1, ATTN_NORM);
2559 			}
2560 		}
2561 		// check for use
2562 		if (cmd->ucmd.buttons & BT_USE)
2563 		{
2564 			if (!player->usedown)
2565 			{
2566 				player->usedown = true;
2567 				if (!P_TalkFacing(player->mo))
2568 				{
2569 					P_UseLines(player);
2570 				}
2571 			}
2572 		}
2573 		else
2574 		{
2575 			player->usedown = false;
2576 		}
2577 		// Morph counter
2578 		if (player->morphTics)
2579 		{
2580 			if (player->chickenPeck)
2581 			{ // Chicken attack counter
2582 				player->chickenPeck -= 3;
2583 			}
2584 			if (!--player->morphTics)
2585 			{ // Attempt to undo the chicken/pig
2586 				P_UndoPlayerMorph (player, player, MORPH_UNDOBYTIMEOUT);
2587 			}
2588 		}
2589 		// Cycle psprites
2590 		P_MovePsprites (player);
2591 
2592 		// Other Counters
2593 		if (player->damagecount)
2594 			player->damagecount--;
2595 
2596 		if (player->bonuscount)
2597 			player->bonuscount--;
2598 
2599 		if (player->hazardcount)
2600 		{
2601 			player->hazardcount--;
2602 			if (!(level.time % player->hazardinterval) && player->hazardcount > 16*TICRATE)
2603 				P_DamageMobj (player->mo, NULL, NULL, 5, player->hazardtype);
2604 		}
2605 
2606 		if (player->poisoncount && !(level.time & 15))
2607 		{
2608 			player->poisoncount -= 5;
2609 			if (player->poisoncount < 0)
2610 			{
2611 				player->poisoncount = 0;
2612 			}
2613 			P_PoisonDamage (player, player->poisoner, 1, true);
2614 		}
2615 
2616 		// Apply degeneration.
2617 		if (dmflags2 & DF2_YES_DEGENERATION)
2618 		{
2619 			if ((level.time % TICRATE) == 0 && player->health > deh.MaxHealth)
2620 			{
2621 				if (player->health - 5 < deh.MaxHealth)
2622 					player->health = deh.MaxHealth;
2623 				else
2624 					player->health--;
2625 
2626 				player->mo->health = player->health;
2627 			}
2628 		}
2629 
2630 		// Handle air supply
2631 		//if (level.airsupply > 0)
2632 		{
2633 			if (player->mo->waterlevel < 3 ||
2634 				(player->mo->flags2 & MF2_INVULNERABLE) ||
2635 				(player->cheats & (CF_GODMODE | CF_NOCLIP2)) ||
2636 				(player->cheats & CF_GODMODE2))
2637 			{
2638 				player->mo->ResetAirSupply ();
2639 			}
2640 			else if (player->air_finished <= level.time && !(level.time & 31))
2641 			{
2642 				P_DamageMobj (player->mo, NULL, NULL, 2 + ((level.time-player->air_finished)/TICRATE), NAME_Drowning);
2643 			}
2644 		}
2645 	}
2646 }
2647 
P_PredictionLerpReset()2648 void P_PredictionLerpReset()
2649 {
2650 	PredictionLerptics = PredictionLast.gametic = PredictionLerpFrom.gametic = PredictionLerpResult.gametic = 0;
2651 }
2652 
P_LerpCalculate(PredictPos from,PredictPos to,PredictPos & result,float scale)2653 bool P_LerpCalculate(PredictPos from, PredictPos to, PredictPos &result, float scale)
2654 {
2655 	FVector3 vecFrom(FIXED2DBL(from.x), FIXED2DBL(from.y), FIXED2DBL(from.z));
2656 	FVector3 vecTo(FIXED2DBL(to.x), FIXED2DBL(to.y), FIXED2DBL(to.z));
2657 	FVector3 vecResult;
2658 	vecResult = vecTo - vecFrom;
2659 	vecResult *= scale;
2660 	vecResult = vecResult + vecFrom;
2661 	FVector3 delta = vecResult - vecTo;
2662 
2663 	result.x = FLOAT2FIXED(vecResult.X);
2664 	result.y = FLOAT2FIXED(vecResult.Y);
2665 	result.z = FLOAT2FIXED(vecResult.Z);
2666 
2667 	// As a fail safe, assume extrapolation is the threshold.
2668 	return (delta.LengthSquared() > cl_predict_lerpthreshold && scale <= 1.00f);
2669 }
2670 
P_PredictPlayer(player_t * player)2671 void P_PredictPlayer (player_t *player)
2672 {
2673 	int maxtic;
2674 
2675 	if (cl_noprediction ||
2676 		singletics ||
2677 		demoplayback ||
2678 		player->mo == NULL ||
2679 		player != &players[consoleplayer] ||
2680 		player->playerstate != PST_LIVE ||
2681 		!netgame ||
2682 		/*player->morphTics ||*/
2683 		(player->cheats & CF_PREDICTING))
2684 	{
2685 		return;
2686 	}
2687 
2688 	maxtic = maketic;
2689 
2690 	if (gametic == maxtic)
2691 	{
2692 		return;
2693 	}
2694 
2695 	// Save original values for restoration later
2696 	PredictionPlayerBackup = *player;
2697 
2698 	APlayerPawn *act = player->mo;
2699 	memcpy(PredictionActorBackup, &act->snext, sizeof(APlayerPawn) - ((BYTE *)&act->snext - (BYTE *)act));
2700 
2701 	act->flags &= ~MF_PICKUP;
2702 	act->flags2 &= ~MF2_PUSHWALL;
2703 	player->cheats |= CF_PREDICTING;
2704 
2705 	// The ordering of the touching_sectorlist needs to remain unchanged
2706 	// Also store a copy of all previous sector_thinglist nodes
2707 	msecnode_t *mnode = act->touching_sectorlist;
2708 	msecnode_t *snode;
2709 	PredictionSector_sprev_Backup.Clear();
2710 	PredictionTouchingSectorsBackup.Clear ();
2711 
2712 	while (mnode != NULL)
2713 	{
2714 		PredictionTouchingSectorsBackup.Push (mnode->m_sector);
2715 
2716 		for (snode = mnode->m_sector->touching_thinglist; snode; snode = snode->m_snext)
2717 		{
2718 			if (snode->m_thing == act)
2719 			{
2720 				PredictionSector_sprev_Backup.Push(snode->m_sprev);
2721 				break;
2722 			}
2723 		}
2724 
2725 		mnode = mnode->m_tnext;
2726 	}
2727 
2728 	// Keep an ordered list off all actors in the linked sector.
2729 	PredictionSectorListBackup.Clear();
2730 	if (!(act->flags & MF_NOSECTOR))
2731 	{
2732 		AActor *link = act->Sector->thinglist;
2733 
2734 		while (link != NULL)
2735 		{
2736 			PredictionSectorListBackup.Push(link);
2737 			link = link->snext;
2738 		}
2739 	}
2740 
2741 	// Blockmap ordering also needs to stay the same, so unlink the block nodes
2742 	// without releasing them. (They will be used again in P_UnpredictPlayer).
2743 	FBlockNode *block = act->BlockNode;
2744 
2745 	while (block != NULL)
2746 	{
2747 		if (block->NextActor != NULL)
2748 		{
2749 			block->NextActor->PrevActor = block->PrevActor;
2750 		}
2751 		*(block->PrevActor) = block->NextActor;
2752 		block = block->NextBlock;
2753 	}
2754 	act->BlockNode = NULL;
2755 
2756 	// Values too small to be usable for lerping can be considered "off".
2757 	bool CanLerp = (!(cl_predict_lerpscale < 0.01f) && (ticdup == 1)), DoLerp = false, NoInterpolateOld = R_GetViewInterpolationStatus();
2758 	for (int i = gametic; i < maxtic; ++i)
2759 	{
2760 		if (!NoInterpolateOld)
2761 			R_RebuildViewInterpolation(player);
2762 
2763 		player->cmd = localcmds[i % LOCALCMDTICS];
2764 		P_PlayerThink (player);
2765 		player->mo->Tick ();
2766 
2767 		if (CanLerp && PredictionLast.gametic > 0 && i == PredictionLast.gametic && !NoInterpolateOld)
2768 		{
2769 			// Z is not compared as lifts will alter this with no apparent change
2770 			// Make lerping less picky by only testing whole units
2771 			DoLerp = ((PredictionLast.x >> 16) != (player->mo->X() >> 16) ||
2772 				(PredictionLast.y >> 16) != (player->mo->Y() >> 16));
2773 
2774 			// Aditional Debug information
2775 			if (developer && DoLerp)
2776 			{
2777 				DPrintf("Lerp! Ltic (%d) && Ptic (%d) | Lx (%d) && Px (%d) | Ly (%d) && Py (%d)\n",
2778 					PredictionLast.gametic, i,
2779 					(PredictionLast.x >> 16), (player->mo->X() >> 16),
2780 					(PredictionLast.y >> 16), (player->mo->Y() >> 16));
2781 			}
2782 		}
2783 	}
2784 
2785 	if (CanLerp)
2786 	{
2787 		if (NoInterpolateOld)
2788 			P_PredictionLerpReset();
2789 
2790 		else if (DoLerp)
2791 		{
2792 			// If lerping is already in effect, use the previous camera postion so the view doesn't suddenly snap
2793 			PredictionLerpFrom = (PredictionLerptics == 0) ? PredictionLast : PredictionLerpResult;
2794 			PredictionLerptics = 1;
2795 		}
2796 
2797 		PredictionLast.gametic = maxtic - 1;
2798 		PredictionLast.x = player->mo->X();
2799 		PredictionLast.y = player->mo->Y();
2800 		PredictionLast.z = player->mo->Z();
2801 
2802 		if (PredictionLerptics > 0)
2803 		{
2804 			if (PredictionLerpFrom.gametic > 0 &&
2805 				P_LerpCalculate(PredictionLerpFrom, PredictionLast, PredictionLerpResult, (float)PredictionLerptics * cl_predict_lerpscale))
2806 			{
2807 				PredictionLerptics++;
2808 				player->mo->SetXYZ(PredictionLerpResult.x, PredictionLerpResult.y, PredictionLerpResult.z);
2809 			}
2810 			else
2811 			{
2812 				PredictionLerptics = 0;
2813 			}
2814 		}
2815 	}
2816 }
2817 
2818 extern msecnode_t *P_AddSecnode (sector_t *s, AActor *thing, msecnode_t *nextnode);
2819 
P_UnPredictPlayer()2820 void P_UnPredictPlayer ()
2821 {
2822 	player_t *player = &players[consoleplayer];
2823 
2824 	if (player->cheats & CF_PREDICTING)
2825 	{
2826 		unsigned int i;
2827 		APlayerPawn *act = player->mo;
2828 		AActor *savedcamera = player->camera;
2829 
2830 		TObjPtr<AInventory> InvSel = act->InvSel;
2831 		int inventorytics = player->inventorytics;
2832 
2833 		*player = PredictionPlayerBackup;
2834 
2835 		// Restore the camera instead of using the backup's copy, because spynext/prev
2836 		// could cause it to change during prediction.
2837 		player->camera = savedcamera;
2838 
2839 		act->UnlinkFromWorld();
2840 		memcpy(&act->snext, PredictionActorBackup, sizeof(APlayerPawn) - ((BYTE *)&act->snext - (BYTE *)act));
2841 
2842 		// The blockmap ordering needs to remain unchanged, too.
2843 		// Restore sector links and refrences.
2844 		// [ED850] This is somewhat of a duplicate of LinkToWorld(), but we need to keep every thing the same,
2845 		// otherwise we end up fixing bugs in blockmap logic (i.e undefined behaviour with polyobject collisions),
2846 		// which we really don't want to do here.
2847 		if (!(act->flags & MF_NOSECTOR))
2848 		{
2849 			sector_t *sec = act->Sector;
2850 			AActor *me, *next;
2851 			AActor **link;// , **prev;
2852 
2853 			// The thinglist is just a pointer chain. We are restoring the exact same things, so we can NULL the head safely
2854 			sec->thinglist = NULL;
2855 
2856 			for (i = PredictionSectorListBackup.Size(); i-- > 0;)
2857 			{
2858 				me = PredictionSectorListBackup[i];
2859 				link = &sec->thinglist;
2860 				next = *link;
2861 				if ((me->snext = next))
2862 					next->sprev = &me->snext;
2863 				me->sprev = link;
2864 				*link = me;
2865 			}
2866 
2867 			// Destroy old refrences
2868 			msecnode_t *node = sector_list;
2869 			while (node)
2870 			{
2871 				node->m_thing = NULL;
2872 				node = node->m_tnext;
2873 			}
2874 
2875 			// Make the sector_list match the player's touching_sectorlist before it got predicted.
2876 			P_DelSeclist(sector_list);
2877 			sector_list = NULL;
2878 			for (i = PredictionTouchingSectorsBackup.Size(); i-- > 0;)
2879 			{
2880 				sector_list = P_AddSecnode(PredictionTouchingSectorsBackup[i], act, sector_list);
2881 			}
2882 			act->touching_sectorlist = sector_list;	// Attach to thing
2883 			sector_list = NULL;		// clear for next time
2884 
2885 			node = sector_list;
2886 			while (node)
2887 			{
2888 				if (node->m_thing == NULL)
2889 				{
2890 					if (node == sector_list)
2891 						sector_list = node->m_tnext;
2892 					node = P_DelSecnode(node);
2893 				}
2894 				else
2895 				{
2896 					node = node->m_tnext;
2897 				}
2898 			}
2899 
2900 			msecnode_t *snode;
2901 
2902 			// Restore sector thinglist order
2903 			for (i = PredictionTouchingSectorsBackup.Size(); i-- > 0;)
2904 			{
2905 				// If we were already the head node, then nothing needs to change
2906 				if (PredictionSector_sprev_Backup[i] == NULL)
2907 					continue;
2908 
2909 				for (snode = PredictionTouchingSectorsBackup[i]->touching_thinglist; snode; snode = snode->m_snext)
2910 				{
2911 					if (snode->m_thing == act)
2912 					{
2913 						if (snode->m_sprev)
2914 							snode->m_sprev->m_snext = snode->m_snext;
2915 						else
2916 							snode->m_sector->touching_thinglist = snode->m_snext;
2917 						if (snode->m_snext)
2918 							snode->m_snext->m_sprev = snode->m_sprev;
2919 
2920 						snode->m_sprev = PredictionSector_sprev_Backup[i];
2921 
2922 						// At the moment, we don't exist in the list anymore, but we do know what our previous node is, so we set its current m_snext->m_sprev to us.
2923 						if (snode->m_sprev->m_snext)
2924 							snode->m_sprev->m_snext->m_sprev = snode;
2925 						snode->m_snext = snode->m_sprev->m_snext;
2926 						snode->m_sprev->m_snext = snode;
2927 						break;
2928 					}
2929 				}
2930 			}
2931 		}
2932 
2933 		// Now fix the pointers in the blocknode chain
2934 		FBlockNode *block = act->BlockNode;
2935 
2936 		while (block != NULL)
2937 		{
2938 			*(block->PrevActor) = block;
2939 			if (block->NextActor != NULL)
2940 			{
2941 				block->NextActor->PrevActor = &block->NextActor;
2942 			}
2943 			block = block->NextBlock;
2944 		}
2945 
2946 		act->InvSel = InvSel;
2947 		player->inventorytics = inventorytics;
2948 	}
2949 }
2950 
Serialize(FArchive & arc)2951 void player_t::Serialize (FArchive &arc)
2952 {
2953 	int i;
2954 	FString skinname;
2955 
2956 	arc << cls
2957 		<< mo
2958 		<< camera
2959 		<< playerstate
2960 		<< cmd;
2961 	if (arc.IsLoading())
2962 	{
2963 		ReadUserInfo(arc, userinfo, skinname);
2964 	}
2965 	else
2966 	{
2967 		WriteUserInfo(arc, userinfo);
2968 	}
2969 	arc << DesiredFOV << FOV
2970 		<< viewz
2971 		<< viewheight
2972 		<< deltaviewheight
2973 		<< bob
2974 		<< velx
2975 		<< vely
2976 		<< centering
2977 		<< health
2978 		<< inventorytics;
2979 	if (SaveVersion < 4513)
2980 	{
2981 		bool backpack;
2982 		arc << backpack;
2983 	}
2984 	arc << fragcount
2985 		<< spreecount
2986 		<< multicount
2987 		<< lastkilltime
2988 		<< ReadyWeapon << PendingWeapon
2989 		<< cheats
2990 		<< refire
2991 		<< inconsistant
2992 		<< killcount
2993 		<< itemcount
2994 		<< secretcount
2995 		<< damagecount
2996 		<< bonuscount
2997 		<< hazardcount
2998 		<< poisoncount
2999 		<< poisoner
3000 		<< attacker
3001 		<< extralight
3002 		<< fixedcolormap << fixedlightlevel
3003 		<< morphTics
3004 		<< MorphedPlayerClass
3005 		<< MorphStyle
3006 		<< MorphExitFlash
3007 		<< PremorphWeapon
3008 		<< chickenPeck
3009 		<< jumpTics
3010 		<< respawn_time
3011 		<< air_finished
3012 		<< turnticks
3013 		<< oldbuttons;
3014 	if (SaveVersion >= 4929)
3015 	{
3016 		arc << hazardtype
3017 			<< hazardinterval;
3018 	}
3019 	bool IsBot = false;
3020 	if (SaveVersion >= 4514)
3021 	{
3022 		arc << Bot;
3023 	}
3024 	else
3025 	{
3026 		arc << IsBot;
3027 	}
3028 	arc << BlendR
3029 		<< BlendG
3030 		<< BlendB
3031 		<< BlendA;
3032 	if (SaveVersion < 3427)
3033 	{
3034 		WORD oldaccuracy, oldstamina;
3035 		arc << oldaccuracy << oldstamina;
3036 		if (mo != NULL)
3037 		{
3038 			mo->accuracy = oldaccuracy;
3039 			mo->stamina = oldstamina;
3040 		}
3041 	}
3042 	if (SaveVersion < 4041)
3043 	{
3044 		// Move weapon state flags from cheats and into WeaponState.
3045 		WeaponState = ((cheats >> 14) & 1) | ((cheats & (0x37 << 24)) >> (24 - 1));
3046 		cheats &= ~((1 << 14) | (0x37 << 24));
3047 	}
3048 	if (SaveVersion < 4527)
3049 	{
3050 		BYTE oldWeaponState;
3051 		arc << oldWeaponState;
3052 		WeaponState = oldWeaponState;
3053 	}
3054 	else
3055 	{
3056 		arc << WeaponState;
3057 	}
3058 	arc << LogText
3059 		<< ConversationNPC
3060 		<< ConversationPC
3061 		<< ConversationNPCAngle
3062 		<< ConversationFaceTalker;
3063 
3064 	for (i = 0; i < MAXPLAYERS; i++)
3065 		arc << frags[i];
3066 	for (i = 0; i < NUMPSPRITES; i++)
3067 		arc << psprites[i];
3068 
3069 	arc << CurrentPlayerClass;
3070 
3071 	arc << crouchfactor
3072 		<< crouching
3073 		<< crouchdir
3074 		<< crouchviewdelta
3075 		<< original_cmd
3076 		<< original_oldbuttons;
3077 
3078 	if (SaveVersion >= 3475)
3079 	{
3080 		arc << poisontype << poisonpaintype;
3081 	}
3082 	else if (poisoner != NULL)
3083 	{
3084 		poisontype = poisoner->DamageType;
3085 		poisonpaintype = poisoner->PainType != NAME_None ? poisoner->PainType : poisoner->DamageType;
3086 	}
3087 
3088 	if (SaveVersion >= 3599)
3089 	{
3090 		arc << timefreezer;
3091 	}
3092 	else
3093 	{
3094 		cheats &= ~(1 << 15);	// make sure old CF_TIMEFREEZE bit is cleared
3095 	}
3096 	if (SaveVersion < 3640)
3097 	{
3098 		cheats &= ~(1 << 17);	// make sure old CF_REGENERATION bit is cleared
3099 	}
3100 	if (SaveVersion >= 3780)
3101 	{
3102 		arc << settings_controller;
3103 	}
3104 	else
3105 	{
3106 		settings_controller = (this - players == Net_Arbitrator);
3107 	}
3108 	if (SaveVersion >= 4505)
3109 	{
3110 		arc << onground;
3111 	}
3112 	else
3113 	{
3114 		onground = (mo->Z() <= mo->floorz) || (mo->flags2 & MF2_ONMOBJ) || (mo->BounceFlags & BOUNCE_MBF) || (cheats & CF_NOCLIP2);
3115 	}
3116 
3117 	if (SaveVersion < 4514 && IsBot)
3118 	{
3119 		Bot = new DBot;
3120 
3121 		arc	<< Bot->angle
3122 			<< Bot->dest
3123 			<< Bot->prev
3124 			<< Bot->enemy
3125 			<< Bot->missile
3126 			<< Bot->mate
3127 			<< Bot->last_mate
3128 			<< Bot->skill
3129 			<< Bot->t_active
3130 			<< Bot->t_respawn
3131 			<< Bot->t_strafe
3132 			<< Bot->t_react
3133 			<< Bot->t_fight
3134 			<< Bot->t_roam
3135 			<< Bot->t_rocket
3136 			<< Bot->first_shot
3137 			<< Bot->sleft
3138 			<< Bot->allround
3139 			<< Bot->oldx
3140 			<< Bot->oldy;
3141 	}
3142 
3143 	if (SaveVersion < 4516 && Bot != NULL)
3144 	{
3145 		Bot->player = this;
3146 	}
3147 
3148 	if (arc.IsLoading ())
3149 	{
3150 		// If the player reloaded because they pressed +use after dying, we
3151 		// don't want +use to still be down after the game is loaded.
3152 		oldbuttons = ~0;
3153 		original_oldbuttons = ~0;
3154 	}
3155 	if (skinname.IsNotEmpty())
3156 	{
3157 		userinfo.SkinChanged(skinname, CurrentPlayerClass);
3158 	}
3159 	if (SaveVersion >= 4522)
3160 	{
3161 		arc << MUSINFOactor << MUSINFOtics;
3162 	}
3163 }
3164 
3165 
GetPlayerColors(FName classname)3166 static FPlayerColorSetMap *GetPlayerColors(FName classname)
3167 {
3168 	const PClass *cls = PClass::FindClass(classname);
3169 
3170 	if (cls != NULL)
3171 	{
3172 		FActorInfo *inf = cls->ActorInfo;
3173 
3174 		if (inf != NULL)
3175 		{
3176 			return inf->ColorSets;
3177 		}
3178 	}
3179 	return NULL;
3180 }
3181 
P_GetPlayerColorSet(FName classname,int setnum)3182 FPlayerColorSet *P_GetPlayerColorSet(FName classname, int setnum)
3183 {
3184 	FPlayerColorSetMap *map = GetPlayerColors(classname);
3185 	if (map == NULL)
3186 	{
3187 		return NULL;
3188 	}
3189 	return map->CheckKey(setnum);
3190 }
3191 
intcmp(const void * a,const void * b)3192 static int STACK_ARGS intcmp(const void *a, const void *b)
3193 {
3194 	return *(const int *)a - *(const int *)b;
3195 }
3196 
P_EnumPlayerColorSets(FName classname,TArray<int> * out)3197 void P_EnumPlayerColorSets(FName classname, TArray<int> *out)
3198 {
3199 	out->Clear();
3200 	FPlayerColorSetMap *map = GetPlayerColors(classname);
3201 	if (map != NULL)
3202 	{
3203 		FPlayerColorSetMap::Iterator it(*map);
3204 		FPlayerColorSetMap::Pair *pair;
3205 
3206 		while (it.NextPair(pair))
3207 		{
3208 			out->Push(pair->Key);
3209 		}
3210 		qsort(&(*out)[0], out->Size(), sizeof(int), intcmp);
3211 	}
3212 }
3213 
P_IsPlayerTotallyFrozen(const player_t * player)3214 bool P_IsPlayerTotallyFrozen(const player_t *player)
3215 {
3216 	return
3217 		gamestate == GS_TITLELEVEL ||
3218 		player->cheats & CF_TOTALLYFROZEN ||
3219 		((level.flags2 & LEVEL2_FROZEN) && player->timefreezer == 0);
3220 }
3221