~ubuntu-branches/ubuntu/precise/openarena/precise

« back to all changes in this revision

Viewing changes to code/game/g_active.c

  • Committer: Bazaar Package Importer
  • Author(s): Bruno "Fuddl" Kleinert
  • Date: 2007-01-20 12:28:09 UTC
  • Revision ID: james.westby@ubuntu.com-20070120122809-2yza5ojt7nqiyiam
Tags: upstream-0.6.0
ImportĀ upstreamĀ versionĀ 0.6.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
===========================================================================
 
3
Copyright (C) 1999-2005 Id Software, Inc.
 
4
 
 
5
This file is part of Quake III Arena source code.
 
6
 
 
7
Quake III Arena source code is free software; you can redistribute it
 
8
and/or modify it under the terms of the GNU General Public License as
 
9
published by the Free Software Foundation; either version 2 of the License,
 
10
or (at your option) any later version.
 
11
 
 
12
Quake III Arena source code is distributed in the hope that it will be
 
13
useful, 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
You should have received a copy of the GNU General Public License
 
18
along with Quake III Arena source code; if not, write to the Free Software
 
19
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 
20
===========================================================================
 
21
*/
 
22
//
 
23
 
 
24
#include "g_local.h"
 
25
 
 
26
 
 
27
/*
 
28
===============
 
29
G_DamageFeedback
 
30
 
 
31
Called just before a snapshot is sent to the given player.
 
32
Totals up all damage and generates both the player_state_t
 
33
damage values to that client for pain blends and kicks, and
 
34
global pain sound events for all clients.
 
35
===============
 
36
*/
 
37
void P_DamageFeedback( gentity_t *player ) {
 
38
        gclient_t       *client;
 
39
        float   count;
 
40
        vec3_t  angles;
 
41
 
 
42
        client = player->client;
 
43
        if ( client->ps.pm_type == PM_DEAD ) {
 
44
                return;
 
45
        }
 
46
 
 
47
        // total points of damage shot at the player this frame
 
48
        count = client->damage_blood + client->damage_armor;
 
49
        if ( count == 0 ) {
 
50
                return;         // didn't take any damage
 
51
        }
 
52
 
 
53
        if ( count > 255 ) {
 
54
                count = 255;
 
55
        }
 
56
 
 
57
        // send the information to the client
 
58
 
 
59
        // world damage (falling, slime, etc) uses a special code
 
60
        // to make the blend blob centered instead of positional
 
61
        if ( client->damage_fromWorld ) {
 
62
                client->ps.damagePitch = 255;
 
63
                client->ps.damageYaw = 255;
 
64
 
 
65
                client->damage_fromWorld = qfalse;
 
66
        } else {
 
67
                vectoangles( client->damage_from, angles );
 
68
                client->ps.damagePitch = angles[PITCH]/360.0 * 256;
 
69
                client->ps.damageYaw = angles[YAW]/360.0 * 256;
 
70
        }
 
71
 
 
72
        // play an apropriate pain sound
 
73
        if ( (level.time > player->pain_debounce_time) && !(player->flags & FL_GODMODE) ) {
 
74
                player->pain_debounce_time = level.time + 700;
 
75
                G_AddEvent( player, EV_PAIN, player->health );
 
76
                client->ps.damageEvent++;
 
77
        }
 
78
 
 
79
 
 
80
        client->ps.damageCount = count;
 
81
 
 
82
        //
 
83
        // clear totals
 
84
        //
 
85
        client->damage_blood = 0;
 
86
        client->damage_armor = 0;
 
87
        client->damage_knockback = 0;
 
88
}
 
89
 
 
90
 
 
91
 
 
92
/*
 
93
=============
 
94
P_WorldEffects
 
95
 
 
96
Check for lava / slime contents and drowning
 
97
=============
 
98
*/
 
99
void P_WorldEffects( gentity_t *ent ) {
 
100
        qboolean        envirosuit;
 
101
        int                     waterlevel;
 
102
 
 
103
        if ( ent->client->noclip ) {
 
104
                ent->client->airOutTime = level.time + 12000;   // don't need air
 
105
                return;
 
106
        }
 
107
 
 
108
        waterlevel = ent->waterlevel;
 
109
 
 
110
        envirosuit = ent->client->ps.powerups[PW_BATTLESUIT] > level.time;
 
111
 
 
112
        //
 
113
        // check for drowning
 
114
        //
 
115
        if ( waterlevel == 3 ) {
 
116
                // envirosuit give air
 
117
                if ( envirosuit ) {
 
118
                        ent->client->airOutTime = level.time + 10000;
 
119
                }
 
120
 
 
121
                // if out of air, start drowning
 
122
                if ( ent->client->airOutTime < level.time) {
 
123
                        // drown!
 
124
                        ent->client->airOutTime += 1000;
 
125
                        if ( ent->health > 0 ) {
 
126
                                // take more damage the longer underwater
 
127
                                ent->damage += 2;
 
128
                                if (ent->damage > 15)
 
129
                                        ent->damage = 15;
 
130
 
 
131
                                // play a gurp sound instead of a normal pain sound
 
132
                                if (ent->health <= ent->damage) {
 
133
                                        G_Sound(ent, CHAN_VOICE, G_SoundIndex("*drown.wav"));
 
134
                                } else if (rand()&1) {
 
135
                                        G_Sound(ent, CHAN_VOICE, G_SoundIndex("sound/player/gurp1.wav"));
 
136
                                } else {
 
137
                                        G_Sound(ent, CHAN_VOICE, G_SoundIndex("sound/player/gurp2.wav"));
 
138
                                }
 
139
 
 
140
                                // don't play a normal pain sound
 
141
                                ent->pain_debounce_time = level.time + 200;
 
142
 
 
143
                                G_Damage (ent, NULL, NULL, NULL, NULL, 
 
144
                                        ent->damage, DAMAGE_NO_ARMOR, MOD_WATER);
 
145
                        }
 
146
                }
 
147
        } else {
 
148
                ent->client->airOutTime = level.time + 12000;
 
149
                ent->damage = 2;
 
150
        }
 
151
 
 
152
        //
 
153
        // check for sizzle damage (move to pmove?)
 
154
        //
 
155
        if (waterlevel && 
 
156
                (ent->watertype&(CONTENTS_LAVA|CONTENTS_SLIME)) ) {
 
157
                if (ent->health > 0
 
158
                        && ent->pain_debounce_time <= level.time        ) {
 
159
 
 
160
                        if ( envirosuit ) {
 
161
                                G_AddEvent( ent, EV_POWERUP_BATTLESUIT, 0 );
 
162
                        } else {
 
163
                                if (ent->watertype & CONTENTS_LAVA) {
 
164
                                        G_Damage (ent, NULL, NULL, NULL, NULL, 
 
165
                                                30*waterlevel, 0, MOD_LAVA);
 
166
                                }
 
167
 
 
168
                                if (ent->watertype & CONTENTS_SLIME) {
 
169
                                        G_Damage (ent, NULL, NULL, NULL, NULL, 
 
170
                                                10*waterlevel, 0, MOD_SLIME);
 
171
                                }
 
172
                        }
 
173
                }
 
174
        }
 
175
}
 
176
 
 
177
 
 
178
 
 
179
/*
 
180
===============
 
181
G_SetClientSound
 
182
===============
 
183
*/
 
184
void G_SetClientSound( gentity_t *ent ) {
 
185
#ifdef MISSIONPACK
 
186
        if( ent->s.eFlags & EF_TICKING ) {
 
187
                ent->client->ps.loopSound = G_SoundIndex( "sound/weapons/proxmine/wstbtick.wav");
 
188
        }
 
189
        else
 
190
#endif
 
191
        if (ent->waterlevel && (ent->watertype&(CONTENTS_LAVA|CONTENTS_SLIME)) ) {
 
192
                ent->client->ps.loopSound = level.snd_fry;
 
193
        } else {
 
194
                ent->client->ps.loopSound = 0;
 
195
        }
 
196
}
 
197
 
 
198
 
 
199
 
 
200
//==============================================================
 
201
 
 
202
/*
 
203
==============
 
204
ClientImpacts
 
205
==============
 
206
*/
 
207
void ClientImpacts( gentity_t *ent, pmove_t *pm ) {
 
208
        int             i, j;
 
209
        trace_t trace;
 
210
        gentity_t       *other;
 
211
 
 
212
        memset( &trace, 0, sizeof( trace ) );
 
213
        for (i=0 ; i<pm->numtouch ; i++) {
 
214
                for (j=0 ; j<i ; j++) {
 
215
                        if (pm->touchents[j] == pm->touchents[i] ) {
 
216
                                break;
 
217
                        }
 
218
                }
 
219
                if (j != i) {
 
220
                        continue;       // duplicated
 
221
                }
 
222
                other = &g_entities[ pm->touchents[i] ];
 
223
 
 
224
                if ( ( ent->r.svFlags & SVF_BOT ) && ( ent->touch ) ) {
 
225
                        ent->touch( ent, other, &trace );
 
226
                }
 
227
 
 
228
                if ( !other->touch ) {
 
229
                        continue;
 
230
                }
 
231
 
 
232
                other->touch( other, ent, &trace );
 
233
        }
 
234
 
 
235
}
 
236
 
 
237
/*
 
238
============
 
239
G_TouchTriggers
 
240
 
 
241
Find all trigger entities that ent's current position touches.
 
242
Spectators will only interact with teleporters.
 
243
============
 
244
*/
 
245
void    G_TouchTriggers( gentity_t *ent ) {
 
246
        int                     i, num;
 
247
        int                     touch[MAX_GENTITIES];
 
248
        gentity_t       *hit;
 
249
        trace_t         trace;
 
250
        vec3_t          mins, maxs;
 
251
        static vec3_t   range = { 40, 40, 52 };
 
252
 
 
253
        if ( !ent->client ) {
 
254
                return;
 
255
        }
 
256
 
 
257
        // dead clients don't activate triggers!
 
258
        if ( ent->client->ps.stats[STAT_HEALTH] <= 0 ) {
 
259
                return;
 
260
        }
 
261
 
 
262
        VectorSubtract( ent->client->ps.origin, range, mins );
 
263
        VectorAdd( ent->client->ps.origin, range, maxs );
 
264
 
 
265
        num = trap_EntitiesInBox( mins, maxs, touch, MAX_GENTITIES );
 
266
 
 
267
        // can't use ent->absmin, because that has a one unit pad
 
268
        VectorAdd( ent->client->ps.origin, ent->r.mins, mins );
 
269
        VectorAdd( ent->client->ps.origin, ent->r.maxs, maxs );
 
270
 
 
271
        for ( i=0 ; i<num ; i++ ) {
 
272
                hit = &g_entities[touch[i]];
 
273
 
 
274
                if ( !hit->touch && !ent->touch ) {
 
275
                        continue;
 
276
                }
 
277
                if ( !( hit->r.contents & CONTENTS_TRIGGER ) ) {
 
278
                        continue;
 
279
                }
 
280
 
 
281
                // ignore most entities if a spectator
 
282
                if ( ent->client->sess.sessionTeam == TEAM_SPECTATOR ) {
 
283
                        if ( hit->s.eType != ET_TELEPORT_TRIGGER &&
 
284
                                // this is ugly but adding a new ET_? type will
 
285
                                // most likely cause network incompatibilities
 
286
                                hit->touch != Touch_DoorTrigger) {
 
287
                                continue;
 
288
                        }
 
289
                }
 
290
 
 
291
                // use seperate code for determining if an item is picked up
 
292
                // so you don't have to actually contact its bounding box
 
293
                if ( hit->s.eType == ET_ITEM ) {
 
294
                        if ( !BG_PlayerTouchesItem( &ent->client->ps, &hit->s, level.time ) ) {
 
295
                                continue;
 
296
                        }
 
297
                } else {
 
298
                        if ( !trap_EntityContact( mins, maxs, hit ) ) {
 
299
                                continue;
 
300
                        }
 
301
                }
 
302
 
 
303
                memset( &trace, 0, sizeof(trace) );
 
304
 
 
305
                if ( hit->touch ) {
 
306
                        hit->touch (hit, ent, &trace);
 
307
                }
 
308
 
 
309
                if ( ( ent->r.svFlags & SVF_BOT ) && ( ent->touch ) ) {
 
310
                        ent->touch( ent, hit, &trace );
 
311
                }
 
312
        }
 
313
 
 
314
        // if we didn't touch a jump pad this pmove frame
 
315
        if ( ent->client->ps.jumppad_frame != ent->client->ps.pmove_framecount ) {
 
316
                ent->client->ps.jumppad_frame = 0;
 
317
                ent->client->ps.jumppad_ent = 0;
 
318
        }
 
319
}
 
320
 
 
321
/*
 
322
=================
 
323
SpectatorThink
 
324
=================
 
325
*/
 
326
void SpectatorThink( gentity_t *ent, usercmd_t *ucmd ) {
 
327
        pmove_t pm;
 
328
        gclient_t       *client;
 
329
 
 
330
        client = ent->client;
 
331
 
 
332
        if ( client->sess.spectatorState != SPECTATOR_FOLLOW ) {
 
333
                client->ps.pm_type = PM_SPECTATOR;
 
334
                client->ps.speed = 400; // faster than normal
 
335
 
 
336
                // set up for pmove
 
337
                memset (&pm, 0, sizeof(pm));
 
338
                pm.ps = &client->ps;
 
339
                pm.cmd = *ucmd;
 
340
                pm.tracemask = MASK_PLAYERSOLID & ~CONTENTS_BODY;       // spectators can fly through bodies
 
341
                pm.trace = trap_Trace;
 
342
                pm.pointcontents = trap_PointContents;
 
343
 
 
344
                // perform a pmove
 
345
                Pmove (&pm);
 
346
                // save results of pmove
 
347
                VectorCopy( client->ps.origin, ent->s.origin );
 
348
 
 
349
                G_TouchTriggers( ent );
 
350
                trap_UnlinkEntity( ent );
 
351
        }
 
352
 
 
353
        client->oldbuttons = client->buttons;
 
354
        client->buttons = ucmd->buttons;
 
355
 
 
356
        // attack button cycles through spectators
 
357
        if ( ( client->buttons & BUTTON_ATTACK ) && ! ( client->oldbuttons & BUTTON_ATTACK ) ) {
 
358
                Cmd_FollowCycle_f( ent, 1 );
 
359
        }
 
360
}
 
361
 
 
362
 
 
363
 
 
364
/*
 
365
=================
 
366
ClientInactivityTimer
 
367
 
 
368
Returns qfalse if the client is dropped
 
369
=================
 
370
*/
 
371
qboolean ClientInactivityTimer( gclient_t *client ) {
 
372
        if ( ! g_inactivity.integer ) {
 
373
                // give everyone some time, so if the operator sets g_inactivity during
 
374
                // gameplay, everyone isn't kicked
 
375
                client->inactivityTime = level.time + 60 * 1000;
 
376
                client->inactivityWarning = qfalse;
 
377
        } else if ( client->pers.cmd.forwardmove || 
 
378
                client->pers.cmd.rightmove || 
 
379
                client->pers.cmd.upmove ||
 
380
                (client->pers.cmd.buttons & BUTTON_ATTACK) ) {
 
381
                client->inactivityTime = level.time + g_inactivity.integer * 1000;
 
382
                client->inactivityWarning = qfalse;
 
383
        } else if ( !client->pers.localClient ) {
 
384
                if ( level.time > client->inactivityTime ) {
 
385
                        trap_DropClient( client - level.clients, "Dropped due to inactivity" );
 
386
                        return qfalse;
 
387
                }
 
388
                if ( level.time > client->inactivityTime - 10000 && !client->inactivityWarning ) {
 
389
                        client->inactivityWarning = qtrue;
 
390
                        trap_SendServerCommand( client - level.clients, "cp \"Ten seconds until inactivity drop!\n\"" );
 
391
                }
 
392
        }
 
393
        return qtrue;
 
394
}
 
395
 
 
396
/*
 
397
==================
 
398
ClientTimerActions
 
399
 
 
400
Actions that happen once a second
 
401
==================
 
402
*/
 
403
void ClientTimerActions( gentity_t *ent, int msec ) {
 
404
        gclient_t       *client;
 
405
#ifdef MISSIONPACK
 
406
        int                     maxHealth;
 
407
#endif
 
408
 
 
409
        client = ent->client;
 
410
        client->timeResidual += msec;
 
411
 
 
412
        while ( client->timeResidual >= 1000 ) {
 
413
                client->timeResidual -= 1000;
 
414
 
 
415
                // regenerate
 
416
#ifdef MISSIONPACK
 
417
                if( bg_itemlist[client->ps.stats[STAT_PERSISTANT_POWERUP]].giTag == PW_GUARD ) {
 
418
                        maxHealth = client->ps.stats[STAT_MAX_HEALTH] / 2;
 
419
                }
 
420
                else if ( client->ps.powerups[PW_REGEN] ) {
 
421
                        maxHealth = client->ps.stats[STAT_MAX_HEALTH];
 
422
                }
 
423
                else {
 
424
                        maxHealth = 0;
 
425
                }
 
426
                if( maxHealth ) {
 
427
                        if ( ent->health < maxHealth ) {
 
428
                                ent->health += 15;
 
429
                                if ( ent->health > maxHealth * 1.1 ) {
 
430
                                        ent->health = maxHealth * 1.1;
 
431
                                }
 
432
                                G_AddEvent( ent, EV_POWERUP_REGEN, 0 );
 
433
                        } else if ( ent->health < maxHealth * 2) {
 
434
                                ent->health += 5;
 
435
                                if ( ent->health > maxHealth * 2 ) {
 
436
                                        ent->health = maxHealth * 2;
 
437
                                }
 
438
                                G_AddEvent( ent, EV_POWERUP_REGEN, 0 );
 
439
                        }
 
440
#else
 
441
                if ( client->ps.powerups[PW_REGEN] ) {
 
442
                        if ( ent->health < client->ps.stats[STAT_MAX_HEALTH]) {
 
443
                                ent->health += 15;
 
444
                                if ( ent->health > client->ps.stats[STAT_MAX_HEALTH] * 1.1 ) {
 
445
                                        ent->health = client->ps.stats[STAT_MAX_HEALTH] * 1.1;
 
446
                                }
 
447
                                G_AddEvent( ent, EV_POWERUP_REGEN, 0 );
 
448
                        } else if ( ent->health < client->ps.stats[STAT_MAX_HEALTH] * 2) {
 
449
                                ent->health += 5;
 
450
                                if ( ent->health > client->ps.stats[STAT_MAX_HEALTH] * 2 ) {
 
451
                                        ent->health = client->ps.stats[STAT_MAX_HEALTH] * 2;
 
452
                                }
 
453
                                G_AddEvent( ent, EV_POWERUP_REGEN, 0 );
 
454
                        }
 
455
#endif
 
456
                } else {
 
457
                        // count down health when over max
 
458
                        if ( ent->health > client->ps.stats[STAT_MAX_HEALTH] ) {
 
459
                                ent->health--;
 
460
                        }
 
461
                }
 
462
 
 
463
                // count down armor when over max
 
464
                if ( client->ps.stats[STAT_ARMOR] > client->ps.stats[STAT_MAX_HEALTH] ) {
 
465
                        client->ps.stats[STAT_ARMOR]--;
 
466
                }
 
467
        }
 
468
#ifdef MISSIONPACK
 
469
        if( bg_itemlist[client->ps.stats[STAT_PERSISTANT_POWERUP]].giTag == PW_AMMOREGEN ) {
 
470
                int w, max, inc, t, i;
 
471
    int weapList[]={WP_MACHINEGUN,WP_SHOTGUN,WP_GRENADE_LAUNCHER,WP_ROCKET_LAUNCHER,WP_LIGHTNING,WP_RAILGUN,WP_PLASMAGUN,WP_BFG,WP_NAILGUN,WP_PROX_LAUNCHER,WP_CHAINGUN};
 
472
    int weapCount = sizeof(weapList) / sizeof(int);
 
473
                //
 
474
    for (i = 0; i < weapCount; i++) {
 
475
                  w = weapList[i];
 
476
 
 
477
                  switch(w) {
 
478
                          case WP_MACHINEGUN: max = 50; inc = 4; t = 1000; break;
 
479
                          case WP_SHOTGUN: max = 10; inc = 1; t = 1500; break;
 
480
                          case WP_GRENADE_LAUNCHER: max = 10; inc = 1; t = 2000; break;
 
481
                          case WP_ROCKET_LAUNCHER: max = 10; inc = 1; t = 1750; break;
 
482
                          case WP_LIGHTNING: max = 50; inc = 5; t = 1500; break;
 
483
                          case WP_RAILGUN: max = 10; inc = 1; t = 1750; break;
 
484
                          case WP_PLASMAGUN: max = 50; inc = 5; t = 1500; break;
 
485
                          case WP_BFG: max = 10; inc = 1; t = 4000; break;
 
486
                          case WP_NAILGUN: max = 10; inc = 1; t = 1250; break;
 
487
                          case WP_PROX_LAUNCHER: max = 5; inc = 1; t = 2000; break;
 
488
                          case WP_CHAINGUN: max = 100; inc = 5; t = 1000; break;
 
489
                          default: max = 0; inc = 0; t = 1000; break;
 
490
                  }
 
491
                  client->ammoTimes[w] += msec;
 
492
                  if ( client->ps.ammo[w] >= max ) {
 
493
                          client->ammoTimes[w] = 0;
 
494
                  }
 
495
                  if ( client->ammoTimes[w] >= t ) {
 
496
                          while ( client->ammoTimes[w] >= t )
 
497
                                  client->ammoTimes[w] -= t;
 
498
                          client->ps.ammo[w] += inc;
 
499
                          if ( client->ps.ammo[w] > max ) {
 
500
                                  client->ps.ammo[w] = max;
 
501
                          }
 
502
                  }
 
503
    }
 
504
        }
 
505
#endif
 
506
}
 
507
 
 
508
/*
 
509
====================
 
510
ClientIntermissionThink
 
511
====================
 
512
*/
 
513
void ClientIntermissionThink( gclient_t *client ) {
 
514
        client->ps.eFlags &= ~EF_TALK;
 
515
        client->ps.eFlags &= ~EF_FIRING;
 
516
 
 
517
        // the level will exit when everyone wants to or after timeouts
 
518
 
 
519
        // swap and latch button actions
 
520
        client->oldbuttons = client->buttons;
 
521
        client->buttons = client->pers.cmd.buttons;
 
522
        if ( client->buttons & ( BUTTON_ATTACK | BUTTON_USE_HOLDABLE ) & ( client->oldbuttons ^ client->buttons ) ) {
 
523
                // this used to be an ^1 but once a player says ready, it should stick
 
524
                client->readyToExit = 1;
 
525
        }
 
526
}
 
527
 
 
528
 
 
529
/*
 
530
================
 
531
ClientEvents
 
532
 
 
533
Events will be passed on to the clients for presentation,
 
534
but any server game effects are handled here
 
535
================
 
536
*/
 
537
void ClientEvents( gentity_t *ent, int oldEventSequence ) {
 
538
        int             i, j;
 
539
        int             event;
 
540
        gclient_t *client;
 
541
        int             damage;
 
542
        vec3_t  dir;
 
543
        vec3_t  origin, angles;
 
544
//      qboolean        fired;
 
545
        gitem_t *item;
 
546
        gentity_t *drop;
 
547
 
 
548
        client = ent->client;
 
549
 
 
550
        if ( oldEventSequence < client->ps.eventSequence - MAX_PS_EVENTS ) {
 
551
                oldEventSequence = client->ps.eventSequence - MAX_PS_EVENTS;
 
552
        }
 
553
        for ( i = oldEventSequence ; i < client->ps.eventSequence ; i++ ) {
 
554
                event = client->ps.events[ i & (MAX_PS_EVENTS-1) ];
 
555
 
 
556
                switch ( event ) {
 
557
                case EV_FALL_MEDIUM:
 
558
                case EV_FALL_FAR:
 
559
                        if ( ent->s.eType != ET_PLAYER ) {
 
560
                                break;          // not in the player model
 
561
                        }
 
562
                        if ( g_dmflags.integer & DF_NO_FALLING ) {
 
563
                                break;
 
564
                        }
 
565
                        if ( event == EV_FALL_FAR ) {
 
566
                                damage = 10;
 
567
                        } else {
 
568
                                damage = 5;
 
569
                        }
 
570
                        VectorSet (dir, 0, 0, 1);
 
571
                        ent->pain_debounce_time = level.time + 200;     // no normal pain sound
 
572
                        G_Damage (ent, NULL, NULL, NULL, NULL, damage, 0, MOD_FALLING);
 
573
                        break;
 
574
 
 
575
                case EV_FIRE_WEAPON:
 
576
                        FireWeapon( ent );
 
577
                        break;
 
578
 
 
579
                case EV_USE_ITEM1:              // teleporter
 
580
                        // drop flags in CTF
 
581
                        item = NULL;
 
582
                        j = 0;
 
583
 
 
584
                        if ( ent->client->ps.powerups[ PW_REDFLAG ] ) {
 
585
                                item = BG_FindItemForPowerup( PW_REDFLAG );
 
586
                                j = PW_REDFLAG;
 
587
                        } else if ( ent->client->ps.powerups[ PW_BLUEFLAG ] ) {
 
588
                                item = BG_FindItemForPowerup( PW_BLUEFLAG );
 
589
                                j = PW_BLUEFLAG;
 
590
                        } else if ( ent->client->ps.powerups[ PW_NEUTRALFLAG ] ) {
 
591
                                item = BG_FindItemForPowerup( PW_NEUTRALFLAG );
 
592
                                j = PW_NEUTRALFLAG;
 
593
                        }
 
594
 
 
595
                        if ( item ) {
 
596
                                drop = Drop_Item( ent, item, 0 );
 
597
                                // decide how many seconds it has left
 
598
                                drop->count = ( ent->client->ps.powerups[ j ] - level.time ) / 1000;
 
599
                                if ( drop->count < 1 ) {
 
600
                                        drop->count = 1;
 
601
                                }
 
602
 
 
603
                                ent->client->ps.powerups[ j ] = 0;
 
604
                        }
 
605
 
 
606
#ifdef MISSIONPACK
 
607
                        if ( g_gametype.integer == GT_HARVESTER ) {
 
608
                                if ( ent->client->ps.generic1 > 0 ) {
 
609
                                        if ( ent->client->sess.sessionTeam == TEAM_RED ) {
 
610
                                                item = BG_FindItem( "Blue Cube" );
 
611
                                        } else {
 
612
                                                item = BG_FindItem( "Red Cube" );
 
613
                                        }
 
614
                                        if ( item ) {
 
615
                                                for ( j = 0; j < ent->client->ps.generic1; j++ ) {
 
616
                                                        drop = Drop_Item( ent, item, 0 );
 
617
                                                        if ( ent->client->sess.sessionTeam == TEAM_RED ) {
 
618
                                                                drop->spawnflags = TEAM_BLUE;
 
619
                                                        } else {
 
620
                                                                drop->spawnflags = TEAM_RED;
 
621
                                                        }
 
622
                                                }
 
623
                                        }
 
624
                                        ent->client->ps.generic1 = 0;
 
625
                                }
 
626
                        }
 
627
#endif
 
628
                        SelectSpawnPoint( ent->client->ps.origin, origin, angles );
 
629
                        TeleportPlayer( ent, origin, angles );
 
630
                        break;
 
631
 
 
632
                case EV_USE_ITEM2:              // medkit
 
633
                        ent->health = ent->client->ps.stats[STAT_MAX_HEALTH] + 25;
 
634
 
 
635
                        break;
 
636
 
 
637
#ifdef MISSIONPACK
 
638
                case EV_USE_ITEM3:              // kamikaze
 
639
                        // make sure the invulnerability is off
 
640
                        ent->client->invulnerabilityTime = 0;
 
641
                        // start the kamikze
 
642
                        G_StartKamikaze( ent );
 
643
                        break;
 
644
 
 
645
                case EV_USE_ITEM4:              // portal
 
646
                        if( ent->client->portalID ) {
 
647
                                DropPortalSource( ent );
 
648
                        }
 
649
                        else {
 
650
                                DropPortalDestination( ent );
 
651
                        }
 
652
                        break;
 
653
                case EV_USE_ITEM5:              // invulnerability
 
654
                        ent->client->invulnerabilityTime = level.time + 10000;
 
655
                        break;
 
656
#endif
 
657
 
 
658
                default:
 
659
                        break;
 
660
                }
 
661
        }
 
662
 
 
663
}
 
664
 
 
665
#ifdef MISSIONPACK
 
666
/*
 
667
==============
 
668
StuckInOtherClient
 
669
==============
 
670
*/
 
671
static int StuckInOtherClient(gentity_t *ent) {
 
672
        int i;
 
673
        gentity_t       *ent2;
 
674
 
 
675
        ent2 = &g_entities[0];
 
676
        for ( i = 0; i < MAX_CLIENTS; i++, ent2++ ) {
 
677
                if ( ent2 == ent ) {
 
678
                        continue;
 
679
                }
 
680
                if ( !ent2->inuse ) {
 
681
                        continue;
 
682
                }
 
683
                if ( !ent2->client ) {
 
684
                        continue;
 
685
                }
 
686
                if ( ent2->health <= 0 ) {
 
687
                        continue;
 
688
                }
 
689
                //
 
690
                if (ent2->r.absmin[0] > ent->r.absmax[0])
 
691
                        continue;
 
692
                if (ent2->r.absmin[1] > ent->r.absmax[1])
 
693
                        continue;
 
694
                if (ent2->r.absmin[2] > ent->r.absmax[2])
 
695
                        continue;
 
696
                if (ent2->r.absmax[0] < ent->r.absmin[0])
 
697
                        continue;
 
698
                if (ent2->r.absmax[1] < ent->r.absmin[1])
 
699
                        continue;
 
700
                if (ent2->r.absmax[2] < ent->r.absmin[2])
 
701
                        continue;
 
702
                return qtrue;
 
703
        }
 
704
        return qfalse;
 
705
}
 
706
#endif
 
707
 
 
708
void BotTestSolid(vec3_t origin);
 
709
 
 
710
/*
 
711
==============
 
712
SendPendingPredictableEvents
 
713
==============
 
714
*/
 
715
void SendPendingPredictableEvents( playerState_t *ps ) {
 
716
        gentity_t *t;
 
717
        int event, seq;
 
718
        int extEvent, number;
 
719
 
 
720
        // if there are still events pending
 
721
        if ( ps->entityEventSequence < ps->eventSequence ) {
 
722
                // create a temporary entity for this event which is sent to everyone
 
723
                // except the client who generated the event
 
724
                seq = ps->entityEventSequence & (MAX_PS_EVENTS-1);
 
725
                event = ps->events[ seq ] | ( ( ps->entityEventSequence & 3 ) << 8 );
 
726
                // set external event to zero before calling BG_PlayerStateToEntityState
 
727
                extEvent = ps->externalEvent;
 
728
                ps->externalEvent = 0;
 
729
                // create temporary entity for event
 
730
                t = G_TempEntity( ps->origin, event );
 
731
                number = t->s.number;
 
732
                BG_PlayerStateToEntityState( ps, &t->s, qtrue );
 
733
                t->s.number = number;
 
734
                t->s.eType = ET_EVENTS + event;
 
735
                t->s.eFlags |= EF_PLAYER_EVENT;
 
736
                t->s.otherEntityNum = ps->clientNum;
 
737
                // send to everyone except the client who generated the event
 
738
                t->r.svFlags |= SVF_NOTSINGLECLIENT;
 
739
                t->r.singleClient = ps->clientNum;
 
740
                // set back external event
 
741
                ps->externalEvent = extEvent;
 
742
        }
 
743
}
 
744
 
 
745
/*
 
746
==============
 
747
ClientThink
 
748
 
 
749
This will be called once for each client frame, which will
 
750
usually be a couple times for each server frame on fast clients.
 
751
 
 
752
If "g_synchronousClients 1" is set, this will be called exactly
 
753
once for each server frame, which makes for smooth demo recording.
 
754
==============
 
755
*/
 
756
void ClientThink_real( gentity_t *ent ) {
 
757
        gclient_t       *client;
 
758
        pmove_t         pm;
 
759
        int                     oldEventSequence;
 
760
        int                     msec;
 
761
        usercmd_t       *ucmd;
 
762
 
 
763
        client = ent->client;
 
764
 
 
765
        // don't think if the client is not yet connected (and thus not yet spawned in)
 
766
        if (client->pers.connected != CON_CONNECTED) {
 
767
                return;
 
768
        }
 
769
        // mark the time, so the connection sprite can be removed
 
770
        ucmd = &ent->client->pers.cmd;
 
771
 
 
772
        // sanity check the command time to prevent speedup cheating
 
773
        if ( ucmd->serverTime > level.time + 200 ) {
 
774
                ucmd->serverTime = level.time + 200;
 
775
//              G_Printf("serverTime <<<<<\n" );
 
776
        }
 
777
        if ( ucmd->serverTime < level.time - 1000 ) {
 
778
                ucmd->serverTime = level.time - 1000;
 
779
//              G_Printf("serverTime >>>>>\n" );
 
780
        } 
 
781
 
 
782
        msec = ucmd->serverTime - client->ps.commandTime;
 
783
        // following others may result in bad times, but we still want
 
784
        // to check for follow toggles
 
785
        if ( msec < 1 && client->sess.spectatorState != SPECTATOR_FOLLOW ) {
 
786
                return;
 
787
        }
 
788
        if ( msec > 200 ) {
 
789
                msec = 200;
 
790
        }
 
791
 
 
792
        if ( pmove_msec.integer < 8 ) {
 
793
                trap_Cvar_Set("pmove_msec", "8");
 
794
        }
 
795
        else if (pmove_msec.integer > 33) {
 
796
                trap_Cvar_Set("pmove_msec", "33");
 
797
        }
 
798
 
 
799
        if ( pmove_fixed.integer || client->pers.pmoveFixed ) {
 
800
                ucmd->serverTime = ((ucmd->serverTime + pmove_msec.integer-1) / pmove_msec.integer) * pmove_msec.integer;
 
801
                //if (ucmd->serverTime - client->ps.commandTime <= 0)
 
802
                //      return;
 
803
        }
 
804
 
 
805
        //
 
806
        // check for exiting intermission
 
807
        //
 
808
        if ( level.intermissiontime ) {
 
809
                ClientIntermissionThink( client );
 
810
                return;
 
811
        }
 
812
 
 
813
        // spectators don't do much
 
814
        if ( client->sess.sessionTeam == TEAM_SPECTATOR ) {
 
815
                if ( client->sess.spectatorState == SPECTATOR_SCOREBOARD ) {
 
816
                        return;
 
817
                }
 
818
                SpectatorThink( ent, ucmd );
 
819
                return;
 
820
        }
 
821
 
 
822
        // check for inactivity timer, but never drop the local client of a non-dedicated server
 
823
        if ( !ClientInactivityTimer( client ) ) {
 
824
                return;
 
825
        }
 
826
 
 
827
        // clear the rewards if time
 
828
        if ( level.time > client->rewardTime ) {
 
829
                client->ps.eFlags &= ~(EF_AWARD_IMPRESSIVE | EF_AWARD_EXCELLENT | EF_AWARD_GAUNTLET | EF_AWARD_ASSIST | EF_AWARD_DEFEND | EF_AWARD_CAP );
 
830
        }
 
831
 
 
832
        if ( client->noclip ) {
 
833
                client->ps.pm_type = PM_NOCLIP;
 
834
        } else if ( client->ps.stats[STAT_HEALTH] <= 0 ) {
 
835
                client->ps.pm_type = PM_DEAD;
 
836
        } else {
 
837
                client->ps.pm_type = PM_NORMAL;
 
838
        }
 
839
 
 
840
        client->ps.gravity = g_gravity.value;
 
841
 
 
842
        // set speed
 
843
        client->ps.speed = g_speed.value;
 
844
 
 
845
#ifdef MISSIONPACK
 
846
        if( bg_itemlist[client->ps.stats[STAT_PERSISTANT_POWERUP]].giTag == PW_SCOUT ) {
 
847
                client->ps.speed *= 1.5;
 
848
        }
 
849
        else
 
850
#endif
 
851
        if ( client->ps.powerups[PW_HASTE] ) {
 
852
                client->ps.speed *= 1.3;
 
853
        }
 
854
 
 
855
        // Let go of the hook if we aren't firing
 
856
        if ( client->ps.weapon == WP_GRAPPLING_HOOK &&
 
857
                client->hook && !( ucmd->buttons & BUTTON_ATTACK ) ) {
 
858
                Weapon_HookFree(client->hook);
 
859
        }
 
860
 
 
861
        // set up for pmove
 
862
        oldEventSequence = client->ps.eventSequence;
 
863
 
 
864
        memset (&pm, 0, sizeof(pm));
 
865
 
 
866
        // check for the hit-scan gauntlet, don't let the action
 
867
        // go through as an attack unless it actually hits something
 
868
        if ( client->ps.weapon == WP_GAUNTLET && !( ucmd->buttons & BUTTON_TALK ) &&
 
869
                ( ucmd->buttons & BUTTON_ATTACK ) && client->ps.weaponTime <= 0 ) {
 
870
                pm.gauntletHit = CheckGauntletAttack( ent );
 
871
        }
 
872
 
 
873
        if ( ent->flags & FL_FORCE_GESTURE ) {
 
874
                ent->flags &= ~FL_FORCE_GESTURE;
 
875
                ent->client->pers.cmd.buttons |= BUTTON_GESTURE;
 
876
        }
 
877
 
 
878
#ifdef MISSIONPACK
 
879
        // check for invulnerability expansion before doing the Pmove
 
880
        if (client->ps.powerups[PW_INVULNERABILITY] ) {
 
881
                if ( !(client->ps.pm_flags & PMF_INVULEXPAND) ) {
 
882
                        vec3_t mins = { -42, -42, -42 };
 
883
                        vec3_t maxs = { 42, 42, 42 };
 
884
                        vec3_t oldmins, oldmaxs;
 
885
 
 
886
                        VectorCopy (ent->r.mins, oldmins);
 
887
                        VectorCopy (ent->r.maxs, oldmaxs);
 
888
                        // expand
 
889
                        VectorCopy (mins, ent->r.mins);
 
890
                        VectorCopy (maxs, ent->r.maxs);
 
891
                        trap_LinkEntity(ent);
 
892
                        // check if this would get anyone stuck in this player
 
893
                        if ( !StuckInOtherClient(ent) ) {
 
894
                                // set flag so the expanded size will be set in PM_CheckDuck
 
895
                                client->ps.pm_flags |= PMF_INVULEXPAND;
 
896
                        }
 
897
                        // set back
 
898
                        VectorCopy (oldmins, ent->r.mins);
 
899
                        VectorCopy (oldmaxs, ent->r.maxs);
 
900
                        trap_LinkEntity(ent);
 
901
                }
 
902
        }
 
903
#endif
 
904
 
 
905
        pm.ps = &client->ps;
 
906
        pm.cmd = *ucmd;
 
907
        if ( pm.ps->pm_type == PM_DEAD ) {
 
908
                pm.tracemask = MASK_PLAYERSOLID & ~CONTENTS_BODY;
 
909
        }
 
910
        else if ( ent->r.svFlags & SVF_BOT ) {
 
911
                pm.tracemask = MASK_PLAYERSOLID | CONTENTS_BOTCLIP;
 
912
        }
 
913
        else {
 
914
                pm.tracemask = MASK_PLAYERSOLID;
 
915
        }
 
916
        pm.trace = trap_Trace;
 
917
        pm.pointcontents = trap_PointContents;
 
918
        pm.debugLevel = g_debugMove.integer;
 
919
        pm.noFootsteps = ( g_dmflags.integer & DF_NO_FOOTSTEPS ) > 0;
 
920
 
 
921
        pm.pmove_fixed = pmove_fixed.integer | client->pers.pmoveFixed;
 
922
        pm.pmove_msec = pmove_msec.integer;
 
923
 
 
924
        VectorCopy( client->ps.origin, client->oldOrigin );
 
925
 
 
926
#ifdef MISSIONPACK
 
927
                if (level.intermissionQueued != 0 && g_singlePlayer.integer) {
 
928
                        if ( level.time - level.intermissionQueued >= 1000  ) {
 
929
                                pm.cmd.buttons = 0;
 
930
                                pm.cmd.forwardmove = 0;
 
931
                                pm.cmd.rightmove = 0;
 
932
                                pm.cmd.upmove = 0;
 
933
                                if ( level.time - level.intermissionQueued >= 2000 && level.time - level.intermissionQueued <= 2500 ) {
 
934
                                        trap_SendConsoleCommand( EXEC_APPEND, "centerview\n");
 
935
                                }
 
936
                                ent->client->ps.pm_type = PM_SPINTERMISSION;
 
937
                        }
 
938
                }
 
939
                Pmove (&pm);
 
940
#else
 
941
                Pmove (&pm);
 
942
#endif
 
943
 
 
944
        // save results of pmove
 
945
        if ( ent->client->ps.eventSequence != oldEventSequence ) {
 
946
                ent->eventTime = level.time;
 
947
        }
 
948
        if (g_smoothClients.integer) {
 
949
                BG_PlayerStateToEntityStateExtraPolate( &ent->client->ps, &ent->s, ent->client->ps.commandTime, qtrue );
 
950
        }
 
951
        else {
 
952
                BG_PlayerStateToEntityState( &ent->client->ps, &ent->s, qtrue );
 
953
        }
 
954
        SendPendingPredictableEvents( &ent->client->ps );
 
955
 
 
956
        if ( !( ent->client->ps.eFlags & EF_FIRING ) ) {
 
957
                client->fireHeld = qfalse;              // for grapple
 
958
        }
 
959
 
 
960
        // use the snapped origin for linking so it matches client predicted versions
 
961
        VectorCopy( ent->s.pos.trBase, ent->r.currentOrigin );
 
962
 
 
963
        VectorCopy (pm.mins, ent->r.mins);
 
964
        VectorCopy (pm.maxs, ent->r.maxs);
 
965
 
 
966
        ent->waterlevel = pm.waterlevel;
 
967
        ent->watertype = pm.watertype;
 
968
 
 
969
        // execute client events
 
970
        ClientEvents( ent, oldEventSequence );
 
971
 
 
972
        // link entity now, after any personal teleporters have been used
 
973
        trap_LinkEntity (ent);
 
974
        if ( !ent->client->noclip ) {
 
975
                G_TouchTriggers( ent );
 
976
        }
 
977
 
 
978
        // NOTE: now copy the exact origin over otherwise clients can be snapped into solid
 
979
        VectorCopy( ent->client->ps.origin, ent->r.currentOrigin );
 
980
 
 
981
        //test for solid areas in the AAS file
 
982
        BotTestAAS(ent->r.currentOrigin);
 
983
 
 
984
        // touch other objects
 
985
        ClientImpacts( ent, &pm );
 
986
 
 
987
        // save results of triggers and client events
 
988
        if (ent->client->ps.eventSequence != oldEventSequence) {
 
989
                ent->eventTime = level.time;
 
990
        }
 
991
 
 
992
        // swap and latch button actions
 
993
        client->oldbuttons = client->buttons;
 
994
        client->buttons = ucmd->buttons;
 
995
        client->latched_buttons |= client->buttons & ~client->oldbuttons;
 
996
 
 
997
        // check for respawning
 
998
        if ( client->ps.stats[STAT_HEALTH] <= 0 ) {
 
999
                // wait for the attack button to be pressed
 
1000
                if ( level.time > client->respawnTime ) {
 
1001
                        // forcerespawn is to prevent users from waiting out powerups
 
1002
                        if ( g_forcerespawn.integer > 0 && 
 
1003
                                ( level.time - client->respawnTime ) > g_forcerespawn.integer * 1000 ) {
 
1004
                                respawn( ent );
 
1005
                                return;
 
1006
                        }
 
1007
                
 
1008
                        // pressing attack or use is the normal respawn method
 
1009
                        if ( ucmd->buttons & ( BUTTON_ATTACK | BUTTON_USE_HOLDABLE ) ) {
 
1010
                                respawn( ent );
 
1011
                        }
 
1012
                }
 
1013
                return;
 
1014
        }
 
1015
 
 
1016
        // perform once-a-second actions
 
1017
        ClientTimerActions( ent, msec );
 
1018
}
 
1019
 
 
1020
/*
 
1021
==================
 
1022
ClientThink
 
1023
 
 
1024
A new command has arrived from the client
 
1025
==================
 
1026
*/
 
1027
void ClientThink( int clientNum ) {
 
1028
        gentity_t *ent;
 
1029
 
 
1030
        ent = g_entities + clientNum;
 
1031
        trap_GetUsercmd( clientNum, &ent->client->pers.cmd );
 
1032
 
 
1033
        // mark the time we got info, so we can display the
 
1034
        // phone jack if they don't get any for a while
 
1035
        ent->client->lastCmdTime = level.time;
 
1036
 
 
1037
        if ( !(ent->r.svFlags & SVF_BOT) && !g_synchronousClients.integer ) {
 
1038
                ClientThink_real( ent );
 
1039
        }
 
1040
}
 
1041
 
 
1042
 
 
1043
void G_RunClient( gentity_t *ent ) {
 
1044
        if ( !(ent->r.svFlags & SVF_BOT) && !g_synchronousClients.integer ) {
 
1045
                return;
 
1046
        }
 
1047
        ent->client->pers.cmd.serverTime = level.time;
 
1048
        ClientThink_real( ent );
 
1049
}
 
1050
 
 
1051
 
 
1052
/*
 
1053
==================
 
1054
SpectatorClientEndFrame
 
1055
 
 
1056
==================
 
1057
*/
 
1058
void SpectatorClientEndFrame( gentity_t *ent ) {
 
1059
        gclient_t       *cl;
 
1060
 
 
1061
        // if we are doing a chase cam or a remote view, grab the latest info
 
1062
        if ( ent->client->sess.spectatorState == SPECTATOR_FOLLOW ) {
 
1063
                int             clientNum, flags;
 
1064
 
 
1065
                clientNum = ent->client->sess.spectatorClient;
 
1066
 
 
1067
                // team follow1 and team follow2 go to whatever clients are playing
 
1068
                if ( clientNum == -1 ) {
 
1069
                        clientNum = level.follow1;
 
1070
                } else if ( clientNum == -2 ) {
 
1071
                        clientNum = level.follow2;
 
1072
                }
 
1073
                if ( clientNum >= 0 ) {
 
1074
                        cl = &level.clients[ clientNum ];
 
1075
                        if ( cl->pers.connected == CON_CONNECTED && cl->sess.sessionTeam != TEAM_SPECTATOR ) {
 
1076
                                flags = (cl->ps.eFlags & ~(EF_VOTED | EF_TEAMVOTED)) | (ent->client->ps.eFlags & (EF_VOTED | EF_TEAMVOTED));
 
1077
                                ent->client->ps = cl->ps;
 
1078
                                ent->client->ps.pm_flags |= PMF_FOLLOW;
 
1079
                                ent->client->ps.eFlags = flags;
 
1080
                                return;
 
1081
                        } else {
 
1082
                                // drop them to free spectators unless they are dedicated camera followers
 
1083
                                if ( ent->client->sess.spectatorClient >= 0 ) {
 
1084
                                        ent->client->sess.spectatorState = SPECTATOR_FREE;
 
1085
                                        ClientBegin( ent->client - level.clients );
 
1086
                                }
 
1087
                        }
 
1088
                }
 
1089
        }
 
1090
 
 
1091
        if ( ent->client->sess.spectatorState == SPECTATOR_SCOREBOARD ) {
 
1092
                ent->client->ps.pm_flags |= PMF_SCOREBOARD;
 
1093
        } else {
 
1094
                ent->client->ps.pm_flags &= ~PMF_SCOREBOARD;
 
1095
        }
 
1096
}
 
1097
 
 
1098
/*
 
1099
==============
 
1100
ClientEndFrame
 
1101
 
 
1102
Called at the end of each server frame for each connected client
 
1103
A fast client will have multiple ClientThink for each ClientEdFrame,
 
1104
while a slow client may have multiple ClientEndFrame between ClientThink.
 
1105
==============
 
1106
*/
 
1107
void ClientEndFrame( gentity_t *ent ) {
 
1108
        int                     i;
 
1109
        clientPersistant_t      *pers;
 
1110
 
 
1111
        if ( ent->client->sess.sessionTeam == TEAM_SPECTATOR ) {
 
1112
                SpectatorClientEndFrame( ent );
 
1113
                return;
 
1114
        }
 
1115
 
 
1116
        pers = &ent->client->pers;
 
1117
 
 
1118
        // turn off any expired powerups
 
1119
        for ( i = 0 ; i < MAX_POWERUPS ; i++ ) {
 
1120
                if ( ent->client->ps.powerups[ i ] < level.time ) {
 
1121
                        ent->client->ps.powerups[ i ] = 0;
 
1122
                }
 
1123
        }
 
1124
 
 
1125
#ifdef MISSIONPACK
 
1126
        // set powerup for player animation
 
1127
        if( bg_itemlist[ent->client->ps.stats[STAT_PERSISTANT_POWERUP]].giTag == PW_GUARD ) {
 
1128
                ent->client->ps.powerups[PW_GUARD] = level.time;
 
1129
        }
 
1130
        if( bg_itemlist[ent->client->ps.stats[STAT_PERSISTANT_POWERUP]].giTag == PW_SCOUT ) {
 
1131
                ent->client->ps.powerups[PW_SCOUT] = level.time;
 
1132
        }
 
1133
        if( bg_itemlist[ent->client->ps.stats[STAT_PERSISTANT_POWERUP]].giTag == PW_DOUBLER ) {
 
1134
                ent->client->ps.powerups[PW_DOUBLER] = level.time;
 
1135
        }
 
1136
        if( bg_itemlist[ent->client->ps.stats[STAT_PERSISTANT_POWERUP]].giTag == PW_AMMOREGEN ) {
 
1137
                ent->client->ps.powerups[PW_AMMOREGEN] = level.time;
 
1138
        }
 
1139
        if ( ent->client->invulnerabilityTime > level.time ) {
 
1140
                ent->client->ps.powerups[PW_INVULNERABILITY] = level.time;
 
1141
        }
 
1142
#endif
 
1143
 
 
1144
        // save network bandwidth
 
1145
#if 0
 
1146
        if ( !g_synchronousClients->integer && ent->client->ps.pm_type == PM_NORMAL ) {
 
1147
                // FIXME: this must change eventually for non-sync demo recording
 
1148
                VectorClear( ent->client->ps.viewangles );
 
1149
        }
 
1150
#endif
 
1151
 
 
1152
        //
 
1153
        // If the end of unit layout is displayed, don't give
 
1154
        // the player any normal movement attributes
 
1155
        //
 
1156
        if ( level.intermissiontime ) {
 
1157
                return;
 
1158
        }
 
1159
 
 
1160
        // burn from lava, etc
 
1161
        P_WorldEffects (ent);
 
1162
 
 
1163
        // apply all the damage taken this frame
 
1164
        P_DamageFeedback (ent);
 
1165
 
 
1166
        // add the EF_CONNECTION flag if we haven't gotten commands recently
 
1167
        if ( level.time - ent->client->lastCmdTime > 1000 ) {
 
1168
                ent->s.eFlags |= EF_CONNECTION;
 
1169
        } else {
 
1170
                ent->s.eFlags &= ~EF_CONNECTION;
 
1171
        }
 
1172
 
 
1173
        ent->client->ps.stats[STAT_HEALTH] = ent->health;       // FIXME: get rid of ent->health...
 
1174
 
 
1175
        G_SetClientSound (ent);
 
1176
 
 
1177
        // set the latest infor
 
1178
        if (g_smoothClients.integer) {
 
1179
                BG_PlayerStateToEntityStateExtraPolate( &ent->client->ps, &ent->s, ent->client->ps.commandTime, qtrue );
 
1180
        }
 
1181
        else {
 
1182
                BG_PlayerStateToEntityState( &ent->client->ps, &ent->s, qtrue );
 
1183
        }
 
1184
        SendPendingPredictableEvents( &ent->client->ps );
 
1185
 
 
1186
        // set the bit for the reachability area the client is currently in
 
1187
//      i = trap_AAS_PointReachabilityAreaIndex( ent->client->ps.origin );
 
1188
//      ent->client->areabits[i >> 3] |= 1 << (i & 7);
 
1189
}
 
1190
 
 
1191