1 //----------------------------------------------------------------------------
2 //  EDGE Player User Code
3 //----------------------------------------------------------------------------
4 //
5 //  Copyright (c) 1999-2009  The EDGE Team.
6 //
7 //  This program is free software; you can redistribute it and/or
8 //  modify it under the terms of the GNU General Public License
9 //  as published by the Free Software Foundation; either version 2
10 //  of the License, or (at your option) any later version.
11 //
12 //  This program is distributed in the hope that it will be useful,
13 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
14 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 //  GNU General Public License for more details.
16 //
17 //----------------------------------------------------------------------------
18 //
19 //  Based on the DOOM source code, released by Id Software under the
20 //  following copyright:
21 //
22 //    Copyright (C) 1993-1996 by id Software, Inc.
23 //
24 //----------------------------------------------------------------------------
25 
26 #include "i_defs.h"
27 
28 #include <float.h>
29 
30 #include "ddf/colormap.h"
31 
32 #include "dm_state.h"
33 #include "e_input.h"
34 #include "m_random.h"
35 #include "n_network.h"
36 #include "p_bot.h"
37 #include "p_local.h"
38 #include "rad_trig.h"
39 #include "s_sound.h"
40 #include "z_zone.h"
41 
42 
43 static void P_UpdatePowerups(player_t *player);
44 
45 #define MAXBOB  16.0f
46 
47 #define ZOOM_ANGLE_DIV  4
48 
49 static sfx_t * sfx_jpidle;
50 static sfx_t * sfx_jpmove;
51 static sfx_t * sfx_jprise;
52 static sfx_t * sfx_jpdown;
53 static sfx_t * sfx_jpflow;
54 
CalcHeight(player_t * player)55 static void CalcHeight(player_t * player)
56 {
57 	bool onground = player->mo->z <= player->mo->floorz;
58 
59 	if (player->mo->height < (player->mo->info->height + player->mo->info->crouchheight) / 2.0f)
60 		player->mo->extendedflags |= EF_CROUCHING;
61 	else
62 		player->mo->extendedflags &= ~EF_CROUCHING;
63 
64 	player->std_viewheight = player->mo->height * PERCENT_2_FLOAT(player->mo->info->viewheight);
65 
66 	// calculate the walking / running height adjustment.
67 
68 	float bob_z = 0;
69 
70 	// Regular movement bobbing
71 	// (needs to be calculated for gun swing even if not on ground).
72 	// -AJA- Moved up here, to prevent weapon jumps when running down
73 	// stairs.
74 
75 	player->bob = (player->mo->mom.x * player->mo->mom.x
76 		+ player->mo->mom.y * player->mo->mom.y) / 8;
77 
78 	if (player->bob > MAXBOB)
79 		player->bob = MAXBOB;
80 
81 	// ----CALCULATE BOB EFFECT----
82 	if (player->playerstate == PST_LIVE && onground)
83 	{
84 		angle_t angle = ANG90 / 5 * leveltime;
85 
86 		bob_z = player->bob / 2 * player->mo->info->bobbing * M_Sin(angle);
87 	}
88 
89 	// ----CALCULATE VIEWHEIGHT----
90 	if (player->playerstate == PST_LIVE)
91 	{
92 		player->viewheight += player->deltaviewheight;
93 
94 		if (player->viewheight > player->std_viewheight)
95 		{
96 			player->viewheight = player->std_viewheight;
97 			player->deltaviewheight = 0;
98 		}
99 		else if (player->viewheight < player->std_viewheight / 2)
100 		{
101 			player->viewheight = player->std_viewheight / 2;
102 
103 			if (player->deltaviewheight <= 0)
104 				player->deltaviewheight = 0.01f;
105 		}
106 
107 		if (player->deltaviewheight != 0)
108 		{
109 			// use a weird number to minimise chance of hitting
110 			// zero when deltaviewheight goes neg -> positive.
111 			player->deltaviewheight += 0.24162f;
112 		}
113 	}
114 
115 	// don't apply bobbing when jumping, but have a smooth
116 	// transition at the end of the jump.
117 	if (player->jumpwait > 0)
118 	{
119 		if (player->jumpwait >= 6)
120 			bob_z = 0;
121 		else
122 			bob_z *= (6 - player->jumpwait) / 6.0;
123 	}
124 
125 	player->viewz = player->viewheight + bob_z;
126 
127 #if 0  // DEBUG
128 I_Debugf("Jump:%d bob_z:%1.2f  z:%1.2f  height:%1.2f delta:%1.2f --> viewz:%1.3f\n",
129 		 player->jumpwait, bob_z, player->mo->z,
130 		 player->viewheight, player->deltaviewheight,
131 		 player->mo->z + player->viewz);
132 #endif
133 }
134 
135 
P_PlayerJump(player_t * pl,float dz,int wait)136 void P_PlayerJump(player_t *pl, float dz, int wait)
137 {
138 	pl->mo->mom.z += pl->mo->info->jumpheight / 1.4f;
139 
140 	if (pl->jumpwait < wait)
141 		pl->jumpwait = wait;
142 
143 	// enter the JUMP states (if present)
144 	statenum_t jump_st = P_MobjFindLabel(pl->mo, "JUMP");
145 	if (jump_st != S_NULL)
146 		P_SetMobjStateDeferred(pl->mo, jump_st, 0);
147 
148 	// -AJA- 1999/09/11: New JUMP_SOUND for ddf.
149 	if (pl->mo->info->jump_sound)
150 	{
151 		int sfx_cat;
152 
153 		if (pl == players[consoleplayer])
154 			sfx_cat = SNCAT_Player;
155 		else
156 			sfx_cat = SNCAT_Opponent;
157 
158 		S_StartFX(pl->mo->info->jump_sound, sfx_cat, pl->mo);
159 	}
160 }
161 
162 
MovePlayer(player_t * player)163 static void MovePlayer(player_t * player)
164 {
165 	ticcmd_t *cmd;
166 	mobj_t *mo = player->mo;
167 
168 	bool onground = player->mo->z <= player->mo->floorz;
169 	bool onladder = player->mo->on_ladder >= 0;
170 
171 	bool swimming  = player->swimming;
172 	bool flying    = (player->powers[PW_Jetpack] > 0) && ! swimming;
173 	bool jumping   = (player->jumpwait > 0);
174 	bool crouching = (player->mo->extendedflags & EF_CROUCHING) ? true : false;
175 
176 	float dx, dy;
177 	float eh, ev;
178 
179 	float base_xy_speed;
180 	float base_z_speed;
181 
182 	float F_vec[3], U_vec[3], S_vec[3];
183 
184 	cmd = &player->cmd;
185 
186 	if (player->zoom_fov > 0)
187 		cmd->angleturn /= ZOOM_ANGLE_DIV;
188 
189 	player->mo->angle -= (angle_t)(cmd->angleturn << 16);
190 
191 	// EDGE Feature: Vertical Look (Mlook)
192 	//
193 	// -ACB- 1998/07/02 New Code used, rerouted via Ticcmd
194 	// -ACB- 1998/07/27 Used defines for look limits.
195 	//
196 	if (level_flags.mlook)
197 	{
198 		if (player->zoom_fov > 0)
199 			cmd->mlookturn /= ZOOM_ANGLE_DIV;
200 
201 		angle_t V = player->mo->vertangle + (angle_t)(cmd->mlookturn << 16);
202 
203 		if (V < ANG180 && V > MLOOK_LIMIT)
204 			V = MLOOK_LIMIT;
205 		else if (V >= ANG180 && V < (ANG_MAX - MLOOK_LIMIT))
206 			V = (ANG_MAX - MLOOK_LIMIT);
207 
208 		player->mo->vertangle = V;
209 	}
210 	else
211 	{
212 		player->mo->vertangle = 0;
213 	}
214 
215 	// EDGE Feature: Vertical Centering
216 	//
217 	// -ACB- 1998/07/02 Re-routed via Ticcmd
218 	//
219 	if (cmd->extbuttons & EBT_CENTER)
220 		player->mo->vertangle = 0;
221 
222 	// compute XY and Z speeds, taking swimming (etc) into account
223 	// (we try to swim in view direction -- assumes no gravity).
224 
225 	base_xy_speed = player->mo->speed / 32.0f;
226 	base_z_speed  = player->mo->speed / 64.0f;
227 
228 	// Do not let the player control movement if not onground.
229 	// -MH- 1998/06/18  unless he has the JetPack!
230 
231 	if (! (onground || onladder || swimming || flying))
232 		base_xy_speed /= 16.0f;
233 
234 	if (! (onladder || swimming || flying))
235 		base_z_speed /= 16.0f;
236 
237 	// move slower when crouching
238 	if (crouching)
239 		base_xy_speed *= CROUCH_SLOWDOWN;
240 
241 	dx = M_Cos(player->mo->angle);
242 	dy = M_Sin(player->mo->angle);
243 
244 	eh = 1;
245 	ev = 0;
246 
247 	if (swimming || flying)
248 	{
249 		float slope = M_Tan(player->mo->vertangle);
250 
251 		float hyp = (float)sqrt((double)(1.0f + slope * slope));
252 
253 		eh = 1.0f  / hyp;
254 		ev = slope / hyp;
255 	}
256 
257 	// compute movement vectors
258 
259 	F_vec[0] = eh * dx * base_xy_speed;
260 	F_vec[1] = eh * dy * base_xy_speed;
261 	F_vec[2] = ev * base_z_speed;
262 
263 	S_vec[0] =  dy * base_xy_speed;
264 	S_vec[1] = -dx * base_xy_speed;
265 	S_vec[2] =  0;
266 
267 	U_vec[0] = -ev * dx * base_xy_speed;
268 	U_vec[1] = -ev * dy * base_xy_speed;
269 	U_vec[2] =  eh * base_z_speed;
270 
271 	player->mo->mom.x += F_vec[0] * cmd->forwardmove + S_vec[0] *
272 		cmd->sidemove + U_vec[0] * cmd->upwardmove;
273 
274 	player->mo->mom.y += F_vec[1] * cmd->forwardmove + S_vec[1] *
275 		cmd->sidemove + U_vec[1] * cmd->upwardmove;
276 
277 	if (flying || swimming || !onground || onladder)
278 	{
279 		player->mo->mom.z += F_vec[2] * cmd->forwardmove + S_vec[2] *
280 			cmd->sidemove + U_vec[2] * cmd->upwardmove;
281 	}
282 
283 	if (flying && !swimming)
284 	{
285         int sfx_cat;
286 
287         if (player == players[consoleplayer])
288             sfx_cat = SNCAT_Player;
289         else
290             sfx_cat = SNCAT_Opponent;
291 
292 		if (player->powers[PW_Jetpack] <= (5 * TICRATE))
293 		{
294 			if ((leveltime & 10) == 0)
295 				S_StartFX(sfx_jpflow, sfx_cat, player->mo);  // fuel low
296 		}
297 		else if (cmd->upwardmove > 0)
298 			S_StartFX(sfx_jprise, sfx_cat, player->mo);
299 		else if (cmd->upwardmove < 0)
300 			S_StartFX(sfx_jpdown, sfx_cat, player->mo);
301 		else if (cmd->forwardmove || cmd->sidemove)
302 			S_StartFX((onground ? sfx_jpidle : sfx_jpmove), sfx_cat, player->mo);
303 		else
304 			S_StartFX(sfx_jpidle, sfx_cat, player->mo);
305 	}
306 
307 	if (player->mo->state == &states[player->mo->info->idle_state])
308 	{
309 		if (!jumping && !flying && (onground || swimming) &&
310 		    (cmd->forwardmove || cmd->sidemove))
311 		{
312 			// enter the CHASE (i.e. walking) states
313 			if (player->mo->info->chase_state)
314 				P_SetMobjStateDeferred(player->mo, player->mo->info->chase_state, 0);
315 		}
316 	}
317 
318 	// EDGE Feature: Jump Code
319 	//
320 	// -ACB- 1998/08/09 Check that jumping is allowed in the currmap
321 	//                  Make player pause before jumping again
322 
323 	if (level_flags.jump && mo->info->jumpheight > 0 &&
324 	    (cmd->upwardmove > 4))
325 	{
326 		if (!jumping && !crouching && !swimming && !flying && onground && !onladder)
327 		{
328 			P_PlayerJump(player, player->mo->info->jumpheight / 1.4f,
329 			             player->mo->info->jump_delay);
330 		}
331 	}
332 
333 	// EDGE Feature: Crouching
334 
335 	if (level_flags.crouch && mo->info->crouchheight > 0 &&
336 		(player->cmd.upwardmove < -4) &&
337 		!player->wet_feet && !jumping && onground)
338 		// NB: no ladder check, onground is sufficient
339 	{
340 		if (mo->height > mo->info->crouchheight)
341 		{
342 			mo->height = MAX(mo->height - 2.0f, mo->info->crouchheight);
343 
344 			// update any things near the player
345 			P_ChangeThingSize(mo);
346 
347 			mo->player->deltaviewheight = -1.0f;
348 		}
349 	}
350 	else // STAND UP
351 	{
352 		if (mo->height < mo->info->height)
353 		{
354 			// prevent standing up inside a solid area
355 			if ((mo->flags & MF_NOCLIP) || mo->z+mo->height+2 <= mo->ceilingz)
356 			{
357 				mo->height = MIN(mo->height + 2, mo->info->height);
358 
359 				// update any things near the player
360 				P_ChangeThingSize(mo);
361 
362 				mo->player->deltaviewheight = 1.0f;
363 			}
364 		}
365 	}
366 
367 	// EDGE Feature: Zooming
368 	//
369 	if (cmd->extbuttons & EBT_ZOOM)
370 	{
371 		int fov = 0;
372 
373 		if (player->zoom_fov == 0)
374 		{
375 			if (! (player->ready_wp < 0 || player->pending_wp >= 0))
376 				fov = player->weapons[player->ready_wp].info->zoom_fov;
377 
378 			// In `LimitZoom' mode, only allow zooming if weapon supports it
379 			if (fov <= 0 && !level_flags.limit_zoom)
380 				fov = r_zoomfov.d;
381 		}
382 
383 		player->zoom_fov = fov;
384 	}
385 }
386 
387 
DeathThink(player_t * player)388 static void DeathThink(player_t * player)
389 {
390 	// fall on your face when dying.
391 
392 	float dx, dy, dz;
393 
394 	angle_t angle;
395 	angle_t delta, delta_s;
396 	float slope;
397 
398 	// -AJA- 1999/12/07: don't die mid-air.
399 	player->powers[PW_Jetpack] = 0;
400 
401 	P_MovePsprites(player);
402 
403 	// fall to the ground
404 	if (player->viewheight > player->std_viewheight)
405 		player->viewheight -= 1.0f;
406 	else if (player->viewheight < player->std_viewheight)
407 		player->viewheight = player->std_viewheight;
408 
409 	player->deltaviewheight = 0.0f;
410 	player->kick_offset = 0.0f;
411 
412 	CalcHeight(player);
413 
414 	if (player->attacker && player->attacker != player->mo)
415 	{
416 		dx = player->attacker->x - player->mo->x;
417 		dy = player->attacker->y - player->mo->y;
418 		dz = (player->attacker->z + player->attacker->height/2) -
419 			(player->mo->z + player->viewheight);
420 
421 		angle = R_PointToAngle(0, 0, dx, dy);
422 		delta = angle - player->mo->angle;
423 
424 		slope = P_ApproxSlope(dx, dy, dz);
425 		slope = MIN(1.7f, MAX(-1.7f, slope));
426 		delta_s = M_ATan(slope) - player->mo->vertangle;
427 
428 		if ((delta <= ANG1/2 || delta >= (angle_t)(0 - ANG1/2)) &&
429 			(delta_s <= ANG1/2 || delta_s >= (angle_t)(0 - ANG1/2)))
430 		{
431 			// Looking at killer, so fade damage flash down.
432 			player->mo->angle = angle;
433 			player->mo->vertangle = M_ATan(slope);
434 
435 			if (player->damagecount > 0)
436 				player->damagecount--;
437 		}
438 		else
439 		{
440 			if (delta < ANG180)
441 				delta /= 5;
442 			else
443 				delta = (angle_t)(0 - (angle_t)(0 - delta) / 5);
444 
445 			if (delta > ANG5 && delta < (angle_t)(0 - ANG5))
446 				delta = (delta < ANG180) ? ANG5 : (angle_t)(0 - ANG5);
447 
448 			if (delta_s < ANG180)
449 				delta_s /= 5;
450 			else
451 				delta_s = (angle_t)(0 - (angle_t)(0 - delta_s) / 5);
452 
453 			if (delta_s > (ANG5/2) && delta_s < (angle_t)(0 - ANG5/2))
454 				delta_s = (delta_s < ANG180) ? (ANG5/2) : (angle_t)(0 - ANG5/2);
455 
456 			player->mo->angle += delta;
457 			player->mo->vertangle += delta_s;
458 
459 			if (player->damagecount && (leveltime % 3) == 0)
460 				player->damagecount--;
461 		}
462 	}
463 	else if (player->damagecount > 0)
464 		player->damagecount--;
465 
466 	// -AJA- 1999/08/07: Fade out armor points too.
467 	if (player->bonuscount)
468 		player->bonuscount--;
469 
470 	P_UpdatePowerups(player);
471 
472 	// lose the zoom when dead
473 	player->zoom_fov = 0;
474 
475 	if (deathmatch >= 3 && player->mo->movecount > player->mo->info->respawntime)
476 		return;
477 
478 	if (player->cmd.buttons & BT_USE)
479 		player->playerstate = PST_REBORN;
480 }
481 
P_UpdatePowerups(player_t * player)482 static void P_UpdatePowerups(player_t *player)
483 {
484 	float limit = FLT_MAX;
485 	int pw;
486 
487 	if (player->playerstate == PST_DEAD)
488 		limit = 1;  // TICRATE * 5;
489 
490 	for (pw = 0; pw < NUMPOWERS; pw++)
491 	{
492 		if (player->powers[pw] < 0)  // -ACB- 2004/02/04 Negative values last a level
493 			continue;
494 
495 		float& qty_r = player->powers[pw];
496 
497 		if (qty_r > limit)
498 			qty_r = limit;
499 		else if (qty_r > 1)
500 			qty_r -= 1;
501 		else if (qty_r > 0)
502 		{
503 			if (player->keep_powers & (1 << pw))
504 				qty_r = -1;
505 			else
506 				qty_r = 0;
507 		}
508 	}
509 
510 	if (player->powers[PW_PartInvis] >= 128 ||
511 		fmod(player->powers[PW_PartInvis], 16) >= 8)
512 		player->mo->flags |=  MF_FUZZY;
513 	else
514 		player->mo->flags &= ~MF_FUZZY;
515 
516 	// Handling colourmaps.
517 	//
518 	// -AJA- 1999/07/10: Updated for colmap.ddf.
519 	//
520 	// !!! FIXME: overlap here with stuff in rgl_fx.cpp.
521 
522 	player->effect_colourmap = NULL;
523 	player->effect_left = 0;
524 
525 	if (player->powers[PW_Invulnerable] > 0)
526 	{
527 		float s = player->powers[PW_Invulnerable];
528 
529 		// -ACB- FIXME!!! Catch lookup failure!
530 		player->effect_colourmap = colourmaps.Lookup("ALLWHITE");
531 		player->effect_left = (s <= 0) ? 0 : MIN(int(s), EFFECT_MAX_TIME);
532 	}
533 	else if (player->powers[PW_Infrared] > 0)
534 	{
535 		float s = player->powers[PW_Infrared];
536 
537 		player->effect_left = (s <= 0) ? 0 : MIN(int(s), EFFECT_MAX_TIME);
538 	}
539 	else if (player->powers[PW_NightVision] > 0)	// -ACB- 1998/07/15 NightVision Code
540 	{
541 		float s = player->powers[PW_NightVision];
542 
543 		// -ACB- FIXME!!! Catch lookup failure!
544 		player->effect_colourmap = colourmaps.Lookup("ALLGREEN");
545 		player->effect_left = (s <= 0) ? 0 : MIN(int(s), EFFECT_MAX_TIME);
546 	}
547 }
548 
549 
550 // Does the thinking of the console player, i.e. read from input
P_ConsolePlayerBuilder(const player_t * pl,void * data,ticcmd_t * dest)551 void P_ConsolePlayerBuilder(const player_t *pl, void *data, ticcmd_t *dest)
552 {
553 	E_BuildTiccmd(dest);
554 
555 	dest->player_idx = pl->pnum;
556 }
557 
MakeConsistency(const player_t * pl)558 static u16_t MakeConsistency(const player_t *pl)
559 {
560 	u16_t result = 0;
561 
562 	if (pl->mo)
563 	{
564 		result = (int)(pl->mo->x - pl->mo->y + pl->mo->z);
565 	}
566 
567 	return (result ^= (u16_t)P_ReadRandomState());
568 }
569 
570 
P_PlayerSwitchWeapon(player_t * player,weapondef_c * choice)571 bool P_PlayerSwitchWeapon(player_t *player, weapondef_c *choice)
572 {
573 	int pw_index;
574 
575 	// see if player owns this kind of weapon
576 	for (pw_index=0; pw_index < MAXWEAPONS; pw_index++)
577 	{
578 		if (! player->weapons[pw_index].owned)
579 			continue;
580 
581 		if (player->weapons[pw_index].info == choice)
582 			break;
583 	}
584 
585 	if (pw_index == MAXWEAPONS)
586 		return false;
587 
588 	// ignore this choice if it the same as the current weapon
589 	if (player->ready_wp >= 0 && choice ==
590 		player->weapons[player->ready_wp].info)
591 	{
592 		return false;
593 	}
594 
595 	player->pending_wp = (weapon_selection_e) pw_index;
596 
597 	return true;
598 }
599 
600 
P_PlayerThink(player_t * player)601 void P_PlayerThink(player_t * player)
602 {
603 	ticcmd_t *cmd;
604 
605 	SYS_ASSERT(player->mo);
606 
607 #if 0  // DEBUG ONLY
608 	{
609 		touch_node_t *tn;
610 		L_WriteDebug("Player %d Touch List:\n", player->pnum);
611 		for (tn = mo->touch_sectors; tn; tn=tn->mo_next)
612 		{
613 			L_WriteDebug("  SEC %d  Other = %s\n", tn->sec - sectors,
614 				tn->sec_next ? tn->sec_next->mo->info->name :
615 			tn->sec_prev ? tn->sec_prev->mo->info->name : "(None)");
616 
617 			SYS_ASSERT(tn->mo == mo);
618 			if (tn->mo_next)
619 			{
620 				SYS_ASSERT(tn->mo_next->mo_prev == tn);
621 			}
622 		}
623 	}
624 #endif
625 
626 	if (player->attacker && player->attacker->isRemoved())
627 		player->attacker = NULL;
628 
629 	if (player->damagecount <= 0)
630 		player->damage_pain = 0;
631 
632 	player->consistency[gametic % (MP_SAVETICS*2)] = MakeConsistency(player);
633 
634 	// fixme: do this in the cheat code
635 	if (player->cheats & CF_NOCLIP)
636 		player->mo->flags |= MF_NOCLIP;
637 	else
638 		player->mo->flags &= ~MF_NOCLIP;
639 
640 	// chain saw run forward
641 	cmd = &player->cmd;
642 	if (player->mo->flags & MF_JUSTATTACKED)
643 	{
644 		cmd->angleturn = 0;
645 		cmd->forwardmove = 64;
646 		cmd->sidemove = 0;
647 		player->mo->flags &= ~MF_JUSTATTACKED;
648 	}
649 
650 	if (player->playerstate == PST_DEAD)
651 	{
652 		DeathThink(player);
653 		return;
654 	}
655 
656 	// Move/Look around.  Reactiontime is used to prevent movement for a
657 	// bit after a teleport.
658 
659 	if (player->mo->reactiontime)
660 		player->mo->reactiontime--;
661 	else
662 		MovePlayer(player);
663 
664 	CalcHeight(player);
665 
666 	if (player->mo->props->special ||
667 		player->mo->subsector->sector->exfloor_used > 0 ||
668 		player->underwater)
669 	{
670 		P_PlayerInSpecialSector(player, player->mo->subsector->sector);
671 	}
672 
673 	// Check for weapon change.
674 	if (cmd->buttons & BT_CHANGE)
675 	{
676 		// The actual changing of the weapon is done when the weapon
677 		// psprite can do it (read: not in the middle of an attack).
678 
679 		int key = (cmd->buttons & BT_WEAPONMASK) >> BT_WEAPONSHIFT;
680 
681 		if (key == BT_NEXT_WEAPON)
682 		{
683 			P_NextPrevWeapon(player, +1);
684 		}
685 		else if (key == BT_PREV_WEAPON)
686 		{
687 			P_NextPrevWeapon(player, -1);
688 		}
689 		else /* numeric key */
690 		{
691 			P_DesireWeaponChange(player, key);
692 		}
693 	}
694 
695 	// check for use
696 	if (cmd->buttons & BT_USE)
697 	{
698 		if (!player->usedown)
699 		{
700 			P_UseLines(player);
701 			player->usedown = true;
702 		}
703 	}
704 	else
705 	{
706 		player->usedown = false;
707 	}
708 
709 	player->actiondown[0] = (cmd->extbuttons & EBT_ACTION1) ? true : false;
710 	player->actiondown[1] = (cmd->extbuttons & EBT_ACTION2) ? true : false;
711 
712 	// decrement jumpwait counter
713 	if (player->jumpwait > 0)
714 		player->jumpwait--;
715 
716 	if (player->splashwait > 0)
717 		player->splashwait--;
718 
719 	// cycle psprites
720 	P_MovePsprites(player);
721 
722 	// Counters, time dependend power ups.
723 
724 	P_UpdatePowerups(player);
725 
726 	if (player->damagecount > 0)
727 		player->damagecount--;
728 
729 	if (player->bonuscount > 0)
730 		player->bonuscount--;
731 
732 	if (player->grin_count > 0)
733 		player->grin_count--;
734 
735 	if (player->attackdown[0] || player->attackdown[1])
736 		player->attackdown_count++;
737 	else
738 		player->attackdown_count = 0;
739 
740 	player->kick_offset /= 1.6f;
741 
742 }
743 
744 
P_CreatePlayer(int pnum,bool is_bot)745 void P_CreatePlayer(int pnum, bool is_bot)
746 {
747 	SYS_ASSERT(0 <= pnum && pnum < MAXPLAYERS);
748 
749 	SYS_ASSERT_MSG(! players[pnum], ("P_CreatePlayer: %d already there", pnum));
750 
751 	player_t *p = new player_t;
752 
753 	Z_Clear(p, player_t, 1);
754 
755 	p->pnum = pnum;
756 	p->playerstate = PST_DEAD;
757 
758 	players[pnum] = p;
759 
760 	numplayers++;
761 	if (is_bot)
762 		numbots++;
763 
764 	// determine name
765 	char namebuf[32];
766 	sprintf(namebuf, "Player%dName", pnum + 1);
767 
768 	if (language.IsValidRef(namebuf))
769 	{
770 		strncpy(p->playername, language[namebuf], MAX_PLAYNAME-1);
771 		p->playername[MAX_PLAYNAME-1] = '\0';
772 	}
773 	else
774 	{
775 		// -ES- Default to player##
776 		sprintf(p->playername, "Player%d", pnum + 1);
777 	}
778 
779 	if (is_bot)
780 		P_BotCreate(p, false);
781 
782 	memset(p->consistency, -1, sizeof(p->consistency));
783 
784 	if (! sfx_jpidle)
785 	{
786 		sfx_jpidle = sfxdefs.GetEffect("JPIDLE");
787 		sfx_jpmove = sfxdefs.GetEffect("JPMOVE");
788 		sfx_jprise = sfxdefs.GetEffect("JPRISE");
789 		sfx_jpdown = sfxdefs.GetEffect("JPDOWN");
790 		sfx_jpflow = sfxdefs.GetEffect("JPFLOW");
791 	}
792 }
793 
794 
P_DestroyAllPlayers(void)795 void P_DestroyAllPlayers(void)
796 {
797 	for (int pnum=0; pnum < MAXPLAYERS; pnum++)
798 	{
799 		if (! players[pnum])
800 			continue;
801 
802 		delete players[pnum];
803 
804 		players[pnum] = NULL;
805 	}
806 
807 	numplayers = 0;
808 	numbots = 0;
809 
810 	consoleplayer = -1;
811 	displayplayer = -1;
812 
813 	sfx_jpidle = sfx_jpmove = sfx_jprise = NULL;
814 	sfx_jpdown = sfx_jpflow = NULL;
815 }
816 
817 
P_UpdateAvailWeapons(player_t * p)818 void P_UpdateAvailWeapons(player_t *p)
819 {
820 	// Must be called as soon as the player has received or lost
821 	// a weapon.  Updates the status bar icons.
822 
823 	int key;
824 
825 	for (key = 0; key < WEAPON_KEYS; key++)
826 		p->avail_weapons[key] = false;
827 
828 	for (int i = 0; i < MAXWEAPONS; i++)
829 	{
830 		if (! p->weapons[i].owned)
831 			continue;
832 
833 		SYS_ASSERT(p->weapons[i].info);
834 
835 		key = p->weapons[i].info->bind_key;
836 
837 		// update the status bar icons
838 		if (0 <= key && key <= 9)
839 			p->avail_weapons[key] = true;
840 	}
841 }
842 
843 
P_UpdateTotalArmour(player_t * p)844 void P_UpdateTotalArmour(player_t *p)
845 {
846 	int i;
847 
848 	p->totalarmour = 0;
849 
850 	for (i=0; i < NUMARMOUR; i++)
851 	{
852 		p->totalarmour += p->armours[i];
853 
854 		// forget the association once fully depleted
855 		if (p->armours[i] <= 0)
856 			p->armour_types[i] = NULL;
857 	}
858 
859 	if (p->totalarmour > 999.0f)
860 		p->totalarmour = 999.0f;
861 }
862 
863 
P_AddWeapon(player_t * player,weapondef_c * info,int * index)864 bool P_AddWeapon(player_t *player, weapondef_c *info, int *index)
865 {
866 	// Returns true if player did not already have the weapon.
867 	// If successful and 'index' is non-NULL, the new index is
868 	// stored there.
869 
870 	int slot = -1;
871 	int upgrade_slot = -1;
872 
873 	// cannot own weapons if sprites are missing
874 	if (! P_CheckWeaponSprite(info))
875 		return false;
876 
877 	for (int i=0; i < MAXWEAPONS; i++)
878 	{
879 		weapondef_c *cur_info = player->weapons[i].info;
880 
881 		// skip weapons that are being removed
882 		if (player->weapons[i].flags & PLWEP_Removing)
883 			continue;
884 
885 		// find free slot
886 		if (! player->weapons[i].owned)
887 		{
888 			if (slot < 0)
889 				slot = i;
890 			continue;
891 		}
892 
893 		// check if already own this weapon
894 		if (cur_info == info)
895 			return false;
896 
897 		// don't downgrade any UPGRADED weapons
898 		if (DDF_WeaponIsUpgrade(cur_info, info))
899 			return false;
900 
901 		// check for weapon upgrades
902 		if (cur_info == info->upgrade_weap)
903 		{
904 			upgrade_slot = i;
905 			continue;
906 		}
907 	}
908 
909 	if (slot < 0)
910 		return false;
911 
912 	if (index)
913 		(*index) = slot;
914 
915 	L_WriteDebug("P_AddWeapon: [%s] @ %d\n", info->name.c_str(), slot);
916 
917 	player->weapons[slot].owned = true;
918 	player->weapons[slot].info  = info;
919 	player->weapons[slot].flags = 0;
920 	player->weapons[slot].clip_size[0] = 0;
921 	player->weapons[slot].clip_size[1] = 0;
922 	player->weapons[slot].model_skin = info->model_skin;
923 
924 	P_UpdateAvailWeapons(player);
925 
926 	// for NoAmmo+Clip weapons, always begin with a full clip
927 	for (int ATK = 0; ATK < 2; ATK++)
928 	{
929 		if (info->clip_size[ATK] > 0 && info->ammo[ATK] == AM_NoAmmo)
930 			player->weapons[slot].clip_size[ATK] = info->clip_size[ATK];
931 	}
932 
933 	// initial weapons should get a full clip
934 	if (info->autogive)
935 		P_TryFillNewWeapon(player, slot, AM_DontCare, NULL);
936 
937 	if (upgrade_slot >= 0)
938 	{
939 		player->weapons[upgrade_slot].owned = false;
940 
941 		// check and update key_choices[]
942 		for (int w=0; w <= 9; w++)
943 			if (player->key_choices[w] == upgrade_slot)
944 				player->key_choices[w] = (weapon_selection_e)slot;
945 
946 		// handle the case of holding the weapon which is being upgraded
947 		// by the new one.  We mark the old weapon for removal.
948 
949 		if (player->ready_wp == upgrade_slot)
950 		{
951 			player->weapons[upgrade_slot].flags |= PLWEP_Removing;
952 			player->pending_wp = (weapon_selection_e) slot;
953 		}
954 		else
955 			player->weapons[upgrade_slot].info = NULL;
956 
957 		if (player->pending_wp == upgrade_slot)
958 			player->pending_wp = (weapon_selection_e) slot;
959 	}
960 
961 	return true;
962 }
963 
964 
P_RemoveWeapon(player_t * player,weapondef_c * info)965 bool P_RemoveWeapon(player_t *player, weapondef_c *info)
966 {
967 	// returns true if player had the weapon.
968 
969 	int slot;
970 
971 	for (slot=0; slot < MAXWEAPONS; slot++)
972 	{
973 		if (! player->weapons[slot].owned)
974 			continue;
975 
976 		// Note: no need to check PLWEP_Removing
977 
978 		if (player->weapons[slot].info == info)
979 			break;
980 	}
981 
982 	if (slot >= MAXWEAPONS)
983 		return false;
984 
985 	L_WriteDebug("P_RemoveWeapon: [%s] @ %d\n", info->name.c_str(), slot);
986 
987 	player->weapons[slot].owned = false;
988 
989 	P_UpdateAvailWeapons(player);
990 
991 	// fix the key choices
992 	for (int w=0; w <= 9; w++)
993 		if (player->key_choices[w] == slot)
994 			player->key_choices[w] = WPSEL_None;
995 
996 	// handle the case of already holding the weapon.  We mark the
997 	// weapon as being removed (the flag is cleared once lowered).
998 
999 	if (player->ready_wp == slot)
1000 	{
1001 		player->weapons[slot].flags |= PLWEP_Removing;
1002 
1003 		if (player->pending_wp == WPSEL_NoChange)
1004 			P_DropWeapon(player);
1005 	}
1006 	else
1007 		player->weapons[slot].info = NULL;
1008 
1009 	if (player->pending_wp == slot)
1010 		P_SelectNewWeapon(player, -100, AM_DontCare);
1011 
1012 	SYS_ASSERT(player->pending_wp != slot);
1013 
1014 	return true;
1015 }
1016 
1017 
P_GiveInitialBenefits(player_t * p,const mobjtype_c * info)1018 void P_GiveInitialBenefits(player_t *p, const mobjtype_c *info)
1019 {
1020 	// Give the player the initial benefits when they start a game
1021 	// (or restart after dying).  Sets up: ammo, ammo-limits, health,
1022 	// armour, keys and weapons.
1023 
1024 	epi::array_iterator_c it;
1025 	weapondef_c *w;
1026 
1027 	p->ready_wp   = WPSEL_None;
1028 	p->pending_wp = WPSEL_NoChange;
1029 
1030 	int i;
1031 
1032 	for (i=0; i < WEAPON_KEYS; i++)
1033 		p->key_choices[i] = WPSEL_None;
1034 
1035 	// clear out ammo & ammo-limits
1036 	for (i=0; i < NUMAMMO; i++)
1037 	{
1038 		p->ammo[i].num = p->ammo[i].max = 0;
1039 	}
1040 
1041 	// set health and armour
1042 	p->health = info->spawnhealth;
1043 	p->air_in_lungs = info->lung_capacity;
1044 	p->underwater = false;
1045 
1046 	for (i = 0; i < NUMARMOUR; i++)
1047 	{
1048 		p->armours[i] = 0;
1049 		p->armour_types[i] = NULL;
1050 	}
1051 
1052 	p->totalarmour = 0;
1053 	p->cards = KF_NONE;
1054 
1055 	// give all initial benefits
1056 	P_GiveBenefitList(p, NULL, info->initial_benefits, false);
1057 
1058 	// give all free weapons.  Needs to be after ammo, so that
1059 	// clip weapons can get their clips filled.
1060 	for (it=weapondefs.GetIterator(0); it.IsValid(); it++)
1061 	{
1062 		w = ITERATOR_TO_TYPE(it, weapondef_c*);
1063 		if (!w->autogive)
1064 			continue;
1065 
1066 		int pw_index;
1067 
1068 		P_AddWeapon(p, w, &pw_index);
1069 	}
1070 
1071 	// refresh to remove all stuff from status bar
1072 	P_UpdateAvailWeapons(p);
1073 }
1074 
1075 
1076 //--- editor settings ---
1077 // vi:ts=4:sw=4:noexpandtab
1078