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