1 /*
2 ===========================================================================
3 
4 Return to Castle Wolfenstein single player GPL Source Code
5 Copyright (C) 1999-2010 id Software LLC, a ZeniMax Media company.
6 
7 This file is part of the Return to Castle Wolfenstein single player GPL Source Code (“RTCW SP Source Code”).
8 
9 RTCW SP Source Code is free software: you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation, either version 3 of the License, or
12 (at your option) any later version.
13 
14 RTCW SP Source Code is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 GNU General Public License for more details.
18 
19 You should have received a copy of the GNU General Public License
20 along with RTCW SP Source Code.  If not, see <http://www.gnu.org/licenses/>.
21 
22 In addition, the RTCW SP Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the RTCW SP Source Code.  If not, please request a copy in writing from id Software at the address below.
23 
24 If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
25 
26 ===========================================================================
27 */
28 
29 /*
30  * name:		g_items.c
31  *
32  * desc:		Items are any object that a player can touch to gain some effect.
33  *				Pickup will return the number of seconds until they should respawn.
34  *				all items should pop when dropped in lava or slime.
35  *				Respawnable items don't actually go away when picked up, they are
36  *				just made invisible and untouchable.  This allows them to ride
37  *				movers and respawn apropriately.
38  *
39 */
40 
41 #include "g_local.h"
42 
43 
44 
45 #define RESPAWN_SP          -1
46 #define RESPAWN_KEY         4
47 #define RESPAWN_ARMOR       25
48 #define RESPAWN_TEAM_WEAPON 30
49 #define RESPAWN_HEALTH      35
50 #define RESPAWN_AMMO        40
51 #define RESPAWN_HOLDABLE    60
52 #define RESPAWN_MEGAHEALTH  120
53 #define RESPAWN_POWERUP     120
54 #define RESPAWN_PARTIAL     998     // for multi-stage ammo/health
55 #define RESPAWN_PARTIAL_DONE 999    // for multi-stage ammo/health
56 
57 
58 //======================================================================
59 
Pickup_Powerup(gentity_t * ent,gentity_t * other)60 int Pickup_Powerup( gentity_t *ent, gentity_t *other ) {
61 	int quantity;
62 	int i;
63 	gclient_t   *client;
64 
65 	if ( !other->client->ps.powerups[ent->item->giTag] ) {
66 
67 		// some powerups are time based on how long the powerup is /used/
68 		// rather than timed from when the player picks it up.
69 		if ( ent->item->giTag == PW_NOFATIGUE ) {
70 		} else {
71 			// round timing to seconds to make multiple powerup timers
72 			// count in sync
73 			other->client->ps.powerups[ent->item->giTag] = level.time - ( level.time % 1000 );
74 		}
75 	}
76 
77 	// if an amount was specified in the ent, use it
78 	if ( ent->count ) {
79 		quantity = ent->count;
80 	} else {
81 		quantity = ent->item->quantity;
82 	}
83 
84 	other->client->ps.powerups[ent->item->giTag] += quantity * 1000;
85 
86 
87 	// brandy also gives a little health (10)
88 	if ( ent->item->giTag == PW_NOFATIGUE ) {
89 		if ( Q_stricmp( ent->item->classname, "item_stamina_brandy" ) == 0 ) {
90 			other->health += 10;
91 			if ( other->health > other->client->ps.stats[STAT_MAX_HEALTH] ) {
92 				other->health = other->client->ps.stats[STAT_MAX_HEALTH];
93 			}
94 			other->client->ps.stats[STAT_HEALTH] = other->health;
95 		}
96 
97 		// cap stamina
98 		if ( other->client->ps.powerups[PW_NOFATIGUE] > 60000 ) {
99 			other->client->ps.powerups[PW_NOFATIGUE] = 60000;
100 		}
101 	}
102 
103 
104 	// Ridah, not in single player
105 	if ( g_gametype.integer != GT_SINGLE_PLAYER ) {
106 		// done.
107 		// give any nearby players a "denied" anti-reward
108 		for ( i = 0 ; i < level.maxclients ; i++ ) {
109 			vec3_t delta;
110 			float len;
111 			vec3_t forward;
112 			trace_t tr;
113 
114 			client = &level.clients[i];
115 			if ( client == other->client ) {
116 				continue;
117 			}
118 			if ( client->pers.connected == CON_DISCONNECTED ) {
119 				continue;
120 			}
121 			if ( client->ps.stats[STAT_HEALTH] <= 0 ) {
122 				continue;
123 			}
124 
125 			// if too far away, no sound
126 			VectorSubtract( ent->s.pos.trBase, client->ps.origin, delta );
127 			len = VectorNormalize( delta );
128 			if ( len > 192 ) {
129 				continue;
130 			}
131 
132 			// if not facing, no sound
133 			AngleVectors( client->ps.viewangles, forward, NULL, NULL );
134 			if ( DotProduct( delta, forward ) < 0.4 ) {
135 				continue;
136 			}
137 
138 			// if not line of sight, no sound
139 			trap_Trace( &tr, client->ps.origin, NULL, NULL, ent->s.pos.trBase, ENTITYNUM_NONE, CONTENTS_SOLID );
140 			if ( tr.fraction != 1.0 ) {
141 				continue;
142 			}
143 
144 			// anti-reward
145 			client->ps.persistant[PERS_REWARD_COUNT]++;
146 			client->ps.persistant[PERS_REWARD] = REWARD_DENIED;
147 		}
148 		// Ridah
149 	}
150 	// done.
151 
152 	if ( ent->s.density == 2 ) {   // multi-stage health first stage
153 		return RESPAWN_PARTIAL;
154 	} else if ( ent->s.density == 1 ) {    // last stage, leave the plate
155 		return RESPAWN_PARTIAL_DONE;
156 	}
157 
158 	// single player has no respawns	(SA)
159 	if ( g_gametype.integer == GT_SINGLE_PLAYER ) {
160 		if ( !( ent->spawnflags & 8 ) ) {
161 			return RESPAWN_SP;
162 		}
163 	}
164 
165 	return RESPAWN_POWERUP;
166 }
167 
168 //----(SA) Wolf keys
169 //======================================================================
Pickup_Key(gentity_t * ent,gentity_t * other)170 int Pickup_Key( gentity_t *ent, gentity_t *other ) {
171 	other->client->ps.stats[STAT_KEYS] |= ( 1 << ent->item->giTag );
172 	if ( g_gametype.integer == GT_SINGLE_PLAYER ) {
173 		if ( !( ent->spawnflags & 8 ) ) {
174 			return RESPAWN_SP;
175 		}
176 	}
177 
178 	return RESPAWN_KEY;
179 }
180 
181 
182 
183 /*
184 ==============
185 Pickup_Clipboard
186 ==============
187 */
Pickup_Clipboard(gentity_t * ent,gentity_t * other)188 int Pickup_Clipboard( gentity_t *ent, gentity_t *other ) {
189 
190 	if ( ent->spawnflags & 4 ) {
191 		return 0;   // leave in world
192 
193 	}
194 	return -1;
195 }
196 
197 
198 /*
199 ==============
200 Pickup_Treasure
201 ==============
202 */
Pickup_Treasure(gentity_t * ent,gentity_t * other)203 int Pickup_Treasure( gentity_t *ent, gentity_t *other ) {
204 	gentity_t *player = AICast_FindEntityForName( "player" );
205 	player->numTreasureFound++;
206 	G_SendMissionStats();
207 	return RESPAWN_SP;  // no respawn
208 }
209 
210 
211 /*
212 ==============
213 UseHoldableItem
214 	server side handling of holdable item use
215 ==============
216 */
UseHoldableItem(gentity_t * ent,int item)217 void UseHoldableItem( gentity_t *ent, int item ) {
218 	switch ( item ) {
219 	case HI_WINE:           // 1921 Chateu Lafite - gives 25 pts health up to max health
220 		ent->health += 25;
221 		if ( ent->health > ent->client->ps.stats[STAT_MAX_HEALTH] ) {
222 			ent->health = ent->client->ps.stats[STAT_MAX_HEALTH];
223 		}
224 		break;
225 
226 	case HI_STAMINA:        // restores fatigue bar and sets "nofatigue" for a time period (currently forced to 60 sec)
227 		//----(SA)	NOTE:	currently only gives free nofatigue time, doesn't reset fatigue bar.
228 		//					(this is because I'd like the restore to be visually gradual (on the HUD item representing
229 		//					current status of your fatigue) rather than snapping back to 'full')
230 		ent->client->ps.powerups[PW_NOFATIGUE] = 60000;
231 		break;
232 
233 	case HI_BOOK1:
234 	case HI_BOOK2:
235 	case HI_BOOK3:
236 		G_AddEvent( ent, EV_POPUPBOOK, ( item - HI_BOOK1 ) + 1 );
237 		break;
238 	}
239 }
240 
241 
242 //======================================================================
243 
Pickup_Holdable(gentity_t * ent,gentity_t * other)244 int Pickup_Holdable( gentity_t *ent, gentity_t *other ) {
245 	gitem_t *item;
246 
247 //	item = BG_FindItemForHoldable(ent->item);
248 	item = ent->item;
249 
250 	if ( item->gameskillnumber[0] ) {  // if the item specifies an amount, give it
251 		other->client->ps.holdable[item->giTag] += item->gameskillnumber[0];
252 	} else {
253 		other->client->ps.holdable[item->giTag] += 1;   // add default of 1
254 
255 	}
256 	other->client->ps.holding = item->giTag;
257 
258 	other->client->ps.stats[STAT_HOLDABLE_ITEM] |= ( 1 << ent->item->giTag );   //----(SA)	added
259 
260 	if ( g_gametype.integer == GT_SINGLE_PLAYER ) {
261 		if ( !( ent->spawnflags & 8 ) ) {
262 			return RESPAWN_SP;
263 		}
264 	}
265 
266 	return RESPAWN_HOLDABLE;
267 }
268 
269 
270 //======================================================================
271 
272 /*
273 ==============
274 Fill_Clip
275 	push reserve ammo into available space in the clip
276 ==============
277 */
Fill_Clip(playerState_t * ps,int weapon)278 void Fill_Clip( playerState_t *ps, int weapon ) {
279 	int inclip, maxclip, ammomove;
280 	int ammoweap = BG_FindAmmoForWeapon( weapon );
281 
282 	if ( weapon < WP_LUGER || weapon >= WP_NUM_WEAPONS ) {
283 		return;
284 	}
285 
286 	if ( g_dmflags.integer & DF_NO_WEAPRELOAD ) {
287 		return;
288 	}
289 
290 	inclip  = ps->ammoclip[BG_FindClipForWeapon( weapon )];
291 	maxclip = ammoTable[weapon].maxclip;
292 
293 	ammomove = maxclip - inclip;    // max amount that can be moved into the clip
294 
295 	// cap move amount if it's more than you've got in reserve
296 	if ( ammomove > ps->ammo[ammoweap] ) {
297 		ammomove = ps->ammo[ammoweap];
298 	}
299 
300 	if ( ammomove ) {
301 		if ( !ps->aiChar || ps->ammo[ammoweap] < 999 ) {  // RF, dont take ammo away if they need unlimited supplies
302 			ps->ammo[ammoweap] -= ammomove;
303 		}
304 		ps->ammoclip[BG_FindClipForWeapon( weapon )] += ammomove;
305 	}
306 }
307 
308 /*
309 ==============
310 Add_Ammo
311 	Try to always add ammo here unless you have specific needs
312 	(like the AI "infinite ammo" where they get below 900 and force back up to 999)
313 
314 	fillClip will push the ammo straight through into the clip and leave the rest in reserve
315 ==============
316 */
Add_Ammo(gentity_t * ent,int weapon,int count,qboolean fillClip)317 void Add_Ammo( gentity_t *ent, int weapon, int count, qboolean fillClip ) {
318 	int ammoweap = BG_FindAmmoForWeapon( weapon );
319 	qboolean noPack = qfalse;       // no extra ammo in your 'pack'
320 
321 	ent->client->ps.ammo[ammoweap] += count;
322 
323 	switch ( ammoweap ) {
324 		// some weaps load straight into the 'clip' since they have no storage outside the clip
325 
326 	case WP_GRENADE_LAUNCHER:       // make sure if he picks up a grenade that he get's the "launcher" too
327 	case WP_GRENADE_PINEAPPLE:
328 	case WP_DYNAMITE:
329 		COM_BitSet( ent->client->ps.weapons, ammoweap );
330 
331 	case WP_TESLA:
332 	case WP_FLAMETHROWER:
333 		noPack = qtrue;
334 		break;
335 	default:
336 		break;
337 	}
338 
339 	if ( fillClip || noPack ) {
340 		Fill_Clip( &ent->client->ps, weapon );
341 	}
342 
343 	if ( ent->aiCharacter ) {
344 		noPack = qfalse;    // let AI's deal with their own clip/ammo handling
345 
346 	}
347 	// cap to max ammo
348 	if ( noPack ) {
349 		ent->client->ps.ammo[ammoweap] = 0;
350 	} else {
351 		if ( ent->client->ps.ammo[ammoweap] > ammoTable[ammoweap].maxammo ) {
352 			ent->client->ps.ammo[ammoweap] = ammoTable[ammoweap].maxammo;
353 		}
354 
355 		if ( count >= 999 ) { // 'really, give /all/'
356 			ent->client->ps.ammo[ammoweap] = count;
357 		}
358 	}
359 
360 	if ( ent->client->ps.ammoclip[ammoweap] > ammoTable[ammoweap].maxclip ) {
361 		ent->client->ps.ammoclip[ammoweap] = ammoTable[ammoweap].maxclip;
362 	}
363 
364 }
365 
366 
367 /*
368 ==============
369 Pickup_Ammo
370 ==============
371 */
Pickup_Ammo(gentity_t * ent,gentity_t * other)372 int Pickup_Ammo( gentity_t *ent, gentity_t *other ) {
373 	int quantity;
374 
375 	if ( ent->count ) {
376 		quantity = ent->count;
377 	} else {
378 		// quantity = ent->item->quantity;
379 
380 		quantity = ent->item->gameskillnumber[g_gameskill.integer];
381 
382 		// FIXME just for now
383 		if ( !quantity ) {
384 			quantity = ent->item->quantity;
385 		}
386 	}
387 
388 	Add_Ammo( other, ent->item->giTag, quantity, qfalse );   //----(SA)	modified
389 
390 	// single player has no respawns	(SA)
391 	if ( g_gametype.integer == GT_SINGLE_PLAYER ) {
392 		if ( !( ent->spawnflags & 8 ) ) {
393 			return RESPAWN_SP;
394 		}
395 	}
396 
397 	return RESPAWN_AMMO;
398 }
399 
400 //======================================================================
401 
402 
Pickup_Weapon(gentity_t * ent,gentity_t * other)403 int Pickup_Weapon( gentity_t *ent, gentity_t *other ) {
404 	int quantity;
405 	qboolean alreadyHave = qfalse;
406 	int weapon;
407 
408 	weapon = ent->item->giTag;
409 
410 	if ( ent->count < 0 ) {
411 		quantity = 0; // None for you, sir!
412 	} else {
413 		if ( ent->count ) {
414 			quantity = ent->count;
415 		} else {
416 //----(SA) modified
417 //			quantity = ent->item->quantity;
418 //			quantity = (random() * (ent->item->quantity - 1)) + 1;	// giving 1-<item default count>
419 			quantity = ( random() * ( ammoTable[weapon].maxclip - 4 ) ) + 4;    // giving 4-<item default count>
420 
421 		}
422 
423 		// dropped items and teamplay weapons always have full ammo
424 //		if ( ! (ent->flags & FL_DROPPED_ITEM) && g_gametype.integer != GT_TEAM ) {
425 //			// respawning rules
426 //			// drop the quantity if the already have over the minimum
427 //			if ( other->client->ps.ammo[ BG_FindAmmoForWeapon(weapon)] < quantity ) {
428 //				quantity = quantity - other->client->ps.ammo[ BG_FindAmmoForWeapon(weapon)];
429 //			} else {
430 //				quantity = 1;		// only add a single shot
431 //			}
432 //		}
433 //----(SA) end
434 	}
435 
436 
437 	//----(SA)	added
438 	// check for special colt->akimbo add (if you've got a colt already, add the second now)
439 	if ( weapon == WP_COLT ) {
440 		if ( COM_BitCheck( other->client->ps.weapons, WP_COLT ) ) {
441 			weapon = WP_AKIMBO;
442 		}
443 	}
444 //----(SA)	end
445 
446 	// check if player already had the weapon
447 	alreadyHave = COM_BitCheck( other->client->ps.weapons, weapon );
448 
449 	// add the weapon
450 	COM_BitSet( other->client->ps.weapons, weapon );
451 
452 //----(SA)	added
453 	// snooper/garand
454 	if ( weapon == WP_SNOOPERSCOPE ) {
455 		COM_BitSet( other->client->ps.weapons, WP_GARAND );
456 	} else if ( weapon == WP_GARAND ) {
457 		COM_BitSet( other->client->ps.weapons, WP_SNOOPERSCOPE );
458 	}
459 	// fg42/scope
460 	else if ( weapon == WP_FG42SCOPE ) {
461 		COM_BitSet( other->client->ps.weapons, WP_FG42 );
462 	} else if ( weapon == WP_FG42 ) {
463 		COM_BitSet( other->client->ps.weapons, WP_FG42SCOPE );
464 	} else if ( weapon == WP_SNIPERRIFLE ) {
465 		COM_BitSet( other->client->ps.weapons, WP_MAUSER );
466 	}
467 
468 //----(SA)	end
469 
470 	Add_Ammo( other, weapon, quantity, !alreadyHave );
471 
472 //----(SA) no hook
473 //	if (weapon == WP_GRAPPLING_HOOK)
474 //		other->client->ps.ammo[BG_FindAmmoForWeapon(weapon)] = -1; // unlimited ammo
475 
476 	// single player has no respawns	(SA)
477 	if ( g_gametype.integer == GT_SINGLE_PLAYER ) {
478 		if ( !( ent->spawnflags & 8 ) ) {
479 			return RESPAWN_SP;
480 		}
481 	}
482 
483 //	// team deathmatch has slow weapon respawns
484 //	if ( g_gametype.integer == GT_SINGLE_PLAYER ) {
485 //		return RESPAWN_TEAM_WEAPON;
486 //	}
487 
488 	if ( g_gametype.integer == GT_TEAM ) {
489 		return g_weaponTeamRespawn.integer;
490 	}
491 
492 	return g_weaponRespawn.integer;
493 }
494 
495 
496 //======================================================================
497 
Pickup_Health(gentity_t * ent,gentity_t * other)498 int Pickup_Health( gentity_t *ent, gentity_t *other ) {
499 	int max;
500 	int quantity = 0;
501 
502 	// small and mega healths will go over the max
503 	if ( ent->item->quantity != 5 && ent->item->quantity != 100  ) {
504 		max = other->client->ps.stats[STAT_MAX_HEALTH];
505 	} else {
506 		max = other->client->ps.stats[STAT_MAX_HEALTH] * 2;
507 	}
508 
509 	if ( ent->count ) {
510 		quantity = ent->count;
511 	} else {
512 		if ( ent->s.density ) {    // multi-stage health
513 			if ( ent->s.density == 2 ) {       // first stage (it counts down)
514 				quantity = ent->item->gameskillnumber[g_gameskill.integer];
515 			} else if ( ent->s.density == 1 )      { // second stage
516 				quantity = ent->item->quantity;
517 			}
518 		} else {
519 			quantity = ent->item->gameskillnumber[g_gameskill.integer];
520 		}
521 	}
522 
523 	other->health += quantity;
524 
525 	if ( other->health > max ) {
526 		other->health = max;
527 	}
528 	other->client->ps.stats[STAT_HEALTH] = other->health;
529 
530 	if ( ent->s.density == 2 ) {   // multi-stage health first stage
531 		return RESPAWN_PARTIAL;
532 	} else if ( ent->s.density == 1 ) {    // last stage, leave the plate
533 		return RESPAWN_PARTIAL_DONE;
534 	}
535 
536 	// single player has no respawns	(SA)
537 	if ( g_gametype.integer == GT_SINGLE_PLAYER ) {
538 		return RESPAWN_SP;
539 	}
540 
541 	if ( ent->item->giTag == 100 ) {        // mega health respawns slow
542 		return RESPAWN_MEGAHEALTH;
543 	}
544 
545 	return RESPAWN_HEALTH;
546 }
547 
548 //======================================================================
549 
Pickup_Armor(gentity_t * ent,gentity_t * other)550 int Pickup_Armor( gentity_t *ent, gentity_t *other ) {
551 	other->client->ps.stats[STAT_ARMOR] += ent->item->quantity;
552 //	if ( other->client->ps.stats[STAT_ARMOR] > other->client->ps.stats[STAT_MAX_HEALTH] * 2 ) {
553 //		other->client->ps.stats[STAT_ARMOR] = other->client->ps.stats[STAT_MAX_HEALTH] * 2;
554 	if ( other->client->ps.stats[STAT_ARMOR] > 100 ) {
555 		other->client->ps.stats[STAT_ARMOR] = 100;
556 	}
557 
558 	// single player has no respawns	(SA)
559 	if ( g_gametype.integer == GT_SINGLE_PLAYER ) {
560 		return RESPAWN_SP;
561 	}
562 
563 	return RESPAWN_ARMOR;
564 }
565 
566 //======================================================================
567 
568 /*
569 ===============
570 RespawnItem
571 ===============
572 */
RespawnItem(gentity_t * ent)573 void RespawnItem( gentity_t *ent ) {
574 	if ( !ent ) {
575 		return;
576 	}
577 
578 	// randomly select from teamed entities
579 	if ( ent->team ) {
580 		gentity_t   *master;
581 		int count;
582 		int choice;
583 
584 		if ( !ent->teammaster ) {
585 			G_Error( "RespawnItem: bad teammaster" );
586 		}
587 		master = ent->teammaster;
588 
589 		for ( count = 0, ent = master; ent; ent = ent->teamchain, count++ )
590 			;
591 
592 		choice = rand() % count;
593 
594 		for ( count = 0, ent = master; ent && count < choice; ent = ent->teamchain, count++ )
595 			;
596 	}
597 
598 	if ( !ent ) {
599 		return;
600 	}
601 
602 	ent->r.contents = CONTENTS_TRIGGER;
603 	//ent->s.eFlags &= ~EF_NODRAW;
604 	ent->flags &= ~FL_NODRAW;
605 	ent->r.svFlags &= ~SVF_NOCLIENT;
606 	trap_LinkEntity( ent );
607 
608 /*
609 	if ( ent->item->giType == IT_POWERUP && g_gametype.integer != GT_SINGLE_PLAYER) {
610 		// play powerup spawn sound to all clients
611 		gentity_t	*te;
612 
613 		te = G_TempEntity( ent->s.pos.trBase, EV_GLOBAL_SOUND );
614 		te->s.eventParm = G_SoundIndex( "sound/items/poweruprespawn.wav" );
615 		te->r.svFlags |= SVF_BROADCAST;
616 	}
617 */
618 
619 	// play the normal respawn sound only to nearby clients
620 	G_AddEvent( ent, EV_ITEM_RESPAWN, 0 );
621 
622 	ent->nextthink = 0;
623 }
624 
625 
626 /*
627 ==============
628 Touch_Item
629 	if other->client->pers.autoActivate == PICKUP_ACTIVATE	(0), he will pick up items only when using +activate
630 	if other->client->pers.autoActivate == PICKUP_TOUCH		(1), he will pickup items when touched
631 	if other->client->pers.autoActivate == PICKUP_FORCE		(2), he will pickup the next item when touched (and reset to PICKUP_ACTIVATE when done)
632 ==============
633 */
Touch_Item_Auto(gentity_t * ent,gentity_t * other,trace_t * trace)634 void Touch_Item_Auto( gentity_t *ent, gentity_t *other, trace_t *trace ) {
635 	if ( other->client->pers.autoActivate == PICKUP_ACTIVATE ) {
636 		return;
637 	}
638 
639 	ent->active = qtrue;
640 	Touch_Item( ent, other, trace );
641 
642 	if ( other->client->pers.autoActivate == PICKUP_FORCE ) {      // autoactivate probably forced by the "Cmd_Activate_f()" function
643 		other->client->pers.autoActivate = PICKUP_ACTIVATE;     // so reset it.
644 	}
645 }
646 
647 
648 /*
649 ===============
650 Touch_Item
651 ===============
652 */
Touch_Item(gentity_t * ent,gentity_t * other,trace_t * trace)653 void Touch_Item( gentity_t *ent, gentity_t *other, trace_t *trace ) {
654 	int respawn;
655 	int makenoise = EV_ITEM_PICKUP;
656 
657 	// only activated items can be picked up
658 	if ( !ent->active ) {
659 		return;
660 	} else {
661 		// need to set active to false if player is maxed out
662 		ent->active = qfalse;
663 	}
664 
665 	if ( !other->client ) {
666 		return;
667 	}
668 	if ( other->health < 1 ) {
669 		return;     // dead people can't pickup
670 
671 	}
672 	// the same pickup rules are used for client side and server side
673 	if ( !BG_CanItemBeGrabbed( &ent->s, &other->client->ps ) ) {
674 		return;
675 	}
676 
677 	G_LogPrintf( "Item: %i %s\n", other->s.number, ent->item->classname );
678 
679 	// call the item-specific pickup function
680 	switch ( ent->item->giType ) {
681 	case IT_WEAPON:
682 		respawn = Pickup_Weapon( ent, other );
683 		break;
684 	case IT_AMMO:
685 		respawn = Pickup_Ammo( ent, other );
686 		break;
687 	case IT_ARMOR:
688 		respawn = Pickup_Armor( ent, other );
689 		break;
690 	case IT_HEALTH:
691 		respawn = Pickup_Health( ent, other );
692 		break;
693 	case IT_POWERUP:
694 		respawn = Pickup_Powerup( ent, other );
695 		break;
696 	case IT_TEAM:
697 		respawn = Pickup_Team( ent, other );
698 		break;
699 	case IT_HOLDABLE:
700 		respawn = Pickup_Holdable( ent, other );
701 		break;
702 	case IT_KEY:
703 		respawn = Pickup_Key( ent, other );
704 		break;
705 	case IT_TREASURE:
706 		respawn = Pickup_Treasure( ent, other );
707 		break;
708 	case IT_CLIPBOARD:
709 		respawn = Pickup_Clipboard( ent, other );
710 		// send the event to the client to request that the UI draw a popup
711 		// (specified by the configstring in ent->s.density).
712 		G_AddEvent( other, EV_POPUP, ent->s.density );
713 		if ( ent->key ) {
714 			G_AddEvent( other, EV_GIVEPAGE, ent->key );
715 		}
716 		break;
717 	default:
718 		return;
719 	}
720 
721 	if ( !respawn ) {
722 		return;
723 	}
724 
725 	// play sounds
726 	if ( ent->noise_index ) {
727 		// (SA) a sound was specified in the entity, so play that sound
728 		// (this G_AddEvent) and send the pickup as "EV_ITEM_PICKUP_QUIET"
729 		// so it doesn't make the default pickup sound when the pickup event is recieved
730 		makenoise = EV_ITEM_PICKUP_QUIET;
731 		G_AddEvent( other, EV_GENERAL_SOUND, ent->noise_index );
732 	}
733 
734 
735 	// send the pickup event
736 	if ( other->client->pers.predictItemPickup ) {
737 		G_AddPredictableEvent( other, makenoise, ent->s.modelindex );
738 	} else {
739 		G_AddEvent( other, makenoise, ent->s.modelindex );
740 	}
741 
742 	// powerup pickups are global broadcasts
743 	if ( ent->item->giType == IT_POWERUP || ent->item->giType == IT_TEAM ) {
744 		// (SA) probably need to check for IT_KEY here too... (coop?)
745 		gentity_t   *te;
746 
747 		te = G_TempEntity( ent->s.pos.trBase, EV_GLOBAL_ITEM_PICKUP );
748 		te->s.eventParm = ent->s.modelindex;
749 		te->r.svFlags |= SVF_BROADCAST;
750 
751 		// (SA) set if we want this to only go to the pickup client
752 //		te->r.svFlags |= SVF_SINGLECLIENT;
753 //		te->r.singleClient = other->s.number;
754 
755 	}
756 
757 	// fire item targets
758 	G_UseTargets( ent, other );
759 
760 	// wait of -1 will not respawn
761 	if ( ent->wait == -1 ) {
762 		ent->flags |= FL_NODRAW;
763 		ent->r.svFlags |= SVF_NOCLIENT; // (SA) commented back in.
764 		ent->s.eFlags |= EF_NODRAW;
765 		ent->r.contents = 0;
766 		ent->unlinkAfterEvent = qtrue;
767 		return;
768 	}
769 
770 	// wait of -2 will respawn but not be available for pickup anymore
771 	// (partial use things that leave a spent modle (ex. plate for turkey)
772 	if ( respawn == RESPAWN_PARTIAL_DONE ) {
773 		ent->s.density = ( 1 << 9 );    // (10 bits of data transmission for density)
774 		ent->active = qtrue;        // re-activate
775 		trap_LinkEntity( ent );
776 		return;
777 	}
778 
779 	if ( respawn == RESPAWN_PARTIAL ) {    // multi-stage health
780 		ent->s.density--;
781 		if ( ent->s.density ) {        // still not completely used up ( (SA) this will change to == 0 and stage 1 will be a destroyable item (plate/etc.) )
782 			ent->active = qtrue;        // re-activate
783 			trap_LinkEntity( ent );
784 			return;
785 		}
786 	}
787 
788 
789 	// non zero wait overrides respawn time
790 	if ( ent->wait ) {
791 		respawn = ent->wait;
792 	}
793 
794 	// random can be used to vary the respawn time
795 	if ( ent->random ) {
796 		respawn += crandom() * ent->random;
797 		if ( respawn < 1 ) {
798 			respawn = 1;
799 		}
800 	}
801 
802 	// dropped items will not respawn
803 	if ( ent->flags & FL_DROPPED_ITEM ) {
804 		ent->freeAfterEvent = qtrue;
805 	}
806 
807 	// picked up items still stay around, they just don't
808 	// draw anything.  This allows respawnable items
809 	// to be placed on movers.
810 	ent->r.svFlags |= SVF_NOCLIENT;
811 	ent->flags |= FL_NODRAW;
812 	//ent->s.eFlags |= EF_NODRAW;
813 	ent->r.contents = 0;
814 
815 	// ZOID
816 	// A negative respawn times means to never respawn this item (but don't
817 	// delete it).  This is used by items that are respawned by third party
818 	// events such as ctf flags
819 	if ( respawn <= 0 ) {
820 		ent->nextthink = 0;
821 		ent->think = 0;
822 	} else {
823 		ent->nextthink = level.time + respawn * 1000;
824 		ent->think = RespawnItem;
825 	}
826 	trap_LinkEntity( ent );
827 }
828 
829 
830 //======================================================================
831 
832 /*
833 ================
834 LaunchItem
835 
836 Spawns an item and tosses it forward
837 ================
838 */
LaunchItem(gitem_t * item,vec3_t origin,vec3_t velocity)839 gentity_t *LaunchItem( gitem_t *item, vec3_t origin, vec3_t velocity ) {
840 	gentity_t   *dropped;
841 
842 	dropped = G_Spawn();
843 
844 	dropped->s.eType = ET_ITEM;
845 	dropped->s.modelindex = item - bg_itemlist; // store item number in modelindex
846 //	dropped->s.modelindex2 = 1; // This is non-zero is it's a dropped item	//----(SA)	commented out since I'm using modelindex2 for model indexing now
847 	dropped->s.otherEntityNum2 = 1;     // DHM - Nerve :: this is taking modelindex2's place for signaling a dropped item
848 
849 	dropped->classname = item->classname;
850 	dropped->item = item;
851 //	VectorSet (dropped->r.mins, -ITEM_RADIUS, -ITEM_RADIUS, -ITEM_RADIUS);
852 //	VectorSet (dropped->r.maxs, ITEM_RADIUS, ITEM_RADIUS, ITEM_RADIUS);
853 	VectorSet( dropped->r.mins, -ITEM_RADIUS, -ITEM_RADIUS, 0 );            //----(SA)	so items sit on the ground
854 	VectorSet( dropped->r.maxs, ITEM_RADIUS, ITEM_RADIUS, 2 * ITEM_RADIUS );  //----(SA)	so items sit on the ground
855 	dropped->r.contents = CONTENTS_TRIGGER | CONTENTS_ITEM;
856 
857 	dropped->touch = Touch_Item_Auto;
858 
859 	G_SetOrigin( dropped, origin );
860 	dropped->s.pos.trType = TR_GRAVITY;
861 	dropped->s.pos.trTime = level.time;
862 	VectorCopy( velocity, dropped->s.pos.trDelta );
863 
864 	dropped->s.eFlags |= EF_BOUNCE_HALF;
865 
866 	// (SA) TODO: FIXME: don't do this right now.  bug needs to be found.
867 //	if(item->giType == IT_WEAPON)
868 //		dropped->s.eFlags |= EF_SPINNING;	// spin the weapon as it flies from the dead player.  it will stop when it hits the ground
869 
870 
871 	if ( item->giType == IT_TEAM ) { // Special case for CTF flags
872 		dropped->think = Team_DroppedFlagThink;
873 		dropped->nextthink = level.time + 30000;
874 	} else { // auto-remove after 30 seconds
875 		dropped->think = G_FreeEntity;
876 		dropped->nextthink = level.time + 30000;
877 	}
878 
879 	dropped->flags = FL_DROPPED_ITEM;
880 
881 	trap_LinkEntity( dropped );
882 
883 	return dropped;
884 }
885 
886 /*
887 ================
888 Drop_Item
889 
890 Spawns an item and tosses it forward
891 ================
892 */
Drop_Item(gentity_t * ent,gitem_t * item,float angle,qboolean novelocity)893 gentity_t *Drop_Item( gentity_t *ent, gitem_t *item, float angle, qboolean novelocity ) {
894 	vec3_t velocity;
895 	vec3_t angles;
896 
897 	VectorCopy( ent->s.apos.trBase, angles );
898 	angles[YAW] += angle;
899 	angles[PITCH] = 0;  // always forward
900 
901 	if ( novelocity ) {
902 		VectorClear( velocity );
903 	} else
904 	{
905 		AngleVectors( angles, velocity, NULL, NULL );
906 		VectorScale( velocity, 150, velocity );
907 		velocity[2] += 200 + crandom() * 50;
908 	}
909 
910 	return LaunchItem( item, ent->s.pos.trBase, velocity );
911 }
912 
913 
914 /*
915 ================
916 Use_Item
917 
918 Respawn the item
919 ================
920 */
Use_Item(gentity_t * ent,gentity_t * other,gentity_t * activator)921 void Use_Item( gentity_t *ent, gentity_t *other, gentity_t *activator ) {
922 	RespawnItem( ent );
923 }
924 
925 //======================================================================
926 
927 /*
928 ================
929 FinishSpawningItem
930 
931 Traces down to find where an item should rest, instead of letting them
932 free fall from their spawn points
933 ================
934 */
FinishSpawningItem(gentity_t * ent)935 void FinishSpawningItem( gentity_t *ent ) {
936 	trace_t tr;
937 	vec3_t dest;
938 	vec3_t maxs;
939 
940 	if ( ent->spawnflags & 1 ) { // suspended
941 		VectorSet( ent->r.mins, -ITEM_RADIUS, -ITEM_RADIUS, -ITEM_RADIUS );
942 		VectorSet( ent->r.maxs, ITEM_RADIUS, ITEM_RADIUS, ITEM_RADIUS );
943 		VectorCopy( ent->r.maxs, maxs );
944 	} else
945 	{
946 		// Rafael
947 		// had to modify this so that items would spawn in shelves
948 		VectorSet( ent->r.mins, -ITEM_RADIUS, -ITEM_RADIUS, 0 );
949 		VectorSet( ent->r.maxs, ITEM_RADIUS, ITEM_RADIUS, ITEM_RADIUS );
950 		VectorCopy( ent->r.maxs, maxs );
951 		maxs[2] /= 2;
952 	}
953 
954 	ent->r.contents = CONTENTS_TRIGGER | CONTENTS_ITEM;
955 	ent->touch = Touch_Item_Auto;
956 	ent->s.eType = ET_ITEM;
957 	ent->s.modelindex = ent->item - bg_itemlist;        // store item number in modelindex
958 
959 	ent->s.otherEntityNum2 = 0;     // DHM - Nerve :: takes modelindex2's place in signaling a dropped item
960 //----(SA)	we don't use this (yet, anyway) so I'm taking it so you can specify a model for treasure items and clipboards
961 //	ent->s.modelindex2 = 0; // zero indicates this isn't a dropped item
962 	if ( ent->model ) {
963 		ent->s.modelindex2 = G_ModelIndex( ent->model );
964 	}
965 
966 
967 	// if clipboard, add the menu name string to the client's configstrings
968 	if ( ent->item->giType == IT_CLIPBOARD ) {
969 		if ( !ent->message ) {
970 			ent->s.density = G_FindConfigstringIndex( "clip_test", CS_CLIPBOARDS, MAX_CLIPBOARD_CONFIGSTRINGS, qtrue );
971 		} else {
972 			ent->s.density = G_FindConfigstringIndex( ent->message, CS_CLIPBOARDS, MAX_CLIPBOARD_CONFIGSTRINGS, qtrue );
973 		}
974 
975 		ent->touch = Touch_Item;    // no auto-pickup, only activate
976 	} else if ( ent->item->giType == IT_HOLDABLE )      {
977 		if ( ent->item->giTag >= HI_BOOK1 && ent->item->giTag <= HI_BOOK3 ) {
978 			G_FindConfigstringIndex( va( "hbook%d", ent->item->giTag - HI_BOOK1 ), CS_CLIPBOARDS, MAX_CLIPBOARD_CONFIGSTRINGS, qtrue );
979 		}
980 //		ent->touch = Touch_Item;	// no auto-pickup, only activate
981 	}
982 
983 
984 	// using an item causes it to respawn
985 	ent->use = Use_Item;
986 
987 //----(SA) moved this up so it happens for suspended items too (and made it a function)
988 	G_SetAngle( ent, ent->s.angles );
989 
990 	if ( ent->spawnflags & 1 ) {    // suspended
991 		G_SetOrigin( ent, ent->s.origin );
992 	} else {
993 
994 		VectorSet( dest, ent->s.origin[0], ent->s.origin[1], ent->s.origin[2] - 4096 );
995 		trap_Trace( &tr, ent->s.origin, ent->r.mins, maxs, dest, ent->s.number, MASK_SOLID );
996 
997 		if ( tr.startsolid ) {
998 			vec3_t temp;
999 
1000 			VectorCopy( ent->s.origin, temp );
1001 			temp[2] -= ITEM_RADIUS;
1002 
1003 			VectorSet( dest, ent->s.origin[0], ent->s.origin[1], ent->s.origin[2] - 4096 );
1004 			trap_Trace( &tr, temp, ent->r.mins, maxs, dest, ent->s.number, MASK_SOLID );
1005 		}
1006 
1007 #if 0
1008 		// drop to floor
1009 		VectorSet( dest, ent->s.origin[0], ent->s.origin[1], ent->s.origin[2] - 4096 );
1010 		trap_Trace( &tr, ent->s.origin, ent->r.mins, maxs, dest, ent->s.number, MASK_SOLID );
1011 #endif
1012 		if ( tr.startsolid ) {
1013 			G_Printf( "FinishSpawningItem: %s startsolid at %s\n", ent->classname, vtos( ent->s.origin ) );
1014 			G_FreeEntity( ent );
1015 			return;
1016 		}
1017 
1018 		// allow to ride movers
1019 		ent->s.groundEntityNum = tr.entityNum;
1020 
1021 		G_SetOrigin( ent, tr.endpos );
1022 	}
1023 
1024 	if ( ent->spawnflags & 2 ) {      // spin
1025 		ent->s.eFlags |= EF_SPINNING;
1026 	}
1027 
1028 
1029 	// team slaves and targeted items aren't present at start
1030 	if ( ( ent->flags & FL_TEAMSLAVE ) || ent->targetname ) {
1031 		ent->flags |= FL_NODRAW;
1032 		//ent->s.eFlags |= EF_NODRAW;
1033 		ent->r.contents = 0;
1034 		return;
1035 	}
1036 
1037 	// health/ammo can potentially be multi-stage (multiple use)
1038 	if ( ent->item->giType == IT_HEALTH || ent->item->giType == IT_AMMO || ent->item->giType == IT_POWERUP ) {
1039 		int i;
1040 
1041 		// having alternate models defined in bg_misc.c for a health or ammo item specify it as "multi-stage"
1042 		// TTimo: left-hand operand of comma expression has no effect
1043 		// was:
1044 		// for(i=0;i<4,ent->item->world_model[i];i++) {}
1045 		i = 0;
1046 		while ( i < 4 && ent->item->world_model[i] )
1047 			i++;
1048 
1049 		ent->s.density = i - 1;   // store number of stages in 'density' for client (most will have '1')
1050 	}
1051 
1052 	// powerups don't spawn in for a while
1053 	if ( ent->item->giType == IT_POWERUP && g_gametype.integer != GT_SINGLE_PLAYER ) {
1054 		float respawn;
1055 
1056 		respawn = 45 + crandom() * 15;
1057 		ent->flags |= FL_NODRAW;
1058 		//ent->s.eFlags |= EF_NODRAW;
1059 		ent->r.contents = 0;
1060 		ent->nextthink = level.time + respawn * 1000;
1061 		ent->think = RespawnItem;
1062 		return;
1063 	}
1064 
1065 	trap_LinkEntity( ent );
1066 }
1067 
1068 
1069 qboolean itemRegistered[MAX_ITEMS];
1070 
1071 /*
1072 ==================
1073 G_CheckTeamItems
1074 ==================
1075 */
G_CheckTeamItems(void)1076 void G_CheckTeamItems( void ) {
1077 	if ( g_gametype.integer == GT_CTF ) {
1078 		gitem_t *item;
1079 
1080 		// make sure we actually have two flags...
1081 		item = BG_FindItem( "Red Flag" );
1082 		if ( !item || !itemRegistered[ item - bg_itemlist ] ) {
1083 			G_Error( "No team_CTF_redflag in map\n" );
1084 		}
1085 		item = BG_FindItem( "Blue Flag" );
1086 		if ( !item || !itemRegistered[ item - bg_itemlist ] ) {
1087 			G_Error( "No team_CTF_blueflag in map\n" );
1088 		}
1089 	}
1090 }
1091 
1092 /*
1093 ==============
1094 ClearRegisteredItems
1095 ==============
1096 */
ClearRegisteredItems(void)1097 void ClearRegisteredItems( void ) {
1098 	memset( itemRegistered, 0, sizeof( itemRegistered ) );
1099 
1100 	// players always start with the base weapon
1101 	// (SA) Nope, not any more...
1102 
1103 //----(SA)	this will be determined by the level or starting position, or the savegame
1104 //			but for now, re-register the MP40 automatically
1105 //	RegisterItem( BG_FindItemForWeapon( WP_MP40 ) );
1106 	RegisterItem( BG_FindItem( "Med Health" ) );           // NERVE - SMF - this is so med packs properly display
1107 }
1108 
1109 /*
1110 ===============
1111 RegisterItem
1112 
1113 The item will be added to the precache list
1114 ===============
1115 */
RegisterItem(gitem_t * item)1116 void RegisterItem( gitem_t *item ) {
1117 	if ( !item ) {
1118 		G_Error( "RegisterItem: NULL" );
1119 	}
1120 	itemRegistered[ item - bg_itemlist ] = qtrue;
1121 }
1122 
1123 
1124 /*
1125 ===============
1126 SaveRegisteredItems
1127 
1128 Write the needed items to a config string
1129 so the client will know which ones to precache
1130 ===============
1131 */
SaveRegisteredItems(void)1132 void SaveRegisteredItems( void ) {
1133 	char string[MAX_ITEMS + 1];
1134 	int i;
1135 	int count;
1136 
1137 	count = 0;
1138 	for ( i = 0 ; i < bg_numItems ; i++ ) {
1139 		if ( itemRegistered[i] ) {
1140 			count++;
1141 			string[i] = '1';
1142 		} else {
1143 			string[i] = '0';
1144 		}
1145 	}
1146 	string[ bg_numItems ] = 0;
1147 
1148 	if ( trap_Cvar_VariableIntegerValue( "g_gametype" ) != GT_SINGLE_PLAYER ) {
1149 		G_Printf( "%i items registered\n", count );
1150 	}
1151 	trap_SetConfigstring( CS_ITEMS, string );
1152 }
1153 
1154 
1155 /*
1156 ============
1157 G_SpawnItem
1158 
1159 Sets the clipping size and plants the object on the floor.
1160 
1161 Items can't be immediately dropped to floor, because they might
1162 be on an entity that hasn't spawned yet.
1163 ============
1164 */
G_SpawnItem(gentity_t * ent,gitem_t * item)1165 void G_SpawnItem( gentity_t *ent, gitem_t *item ) {
1166 	char    *noise;
1167 	int page;
1168 
1169 	G_SpawnFloat( "random", "0", &ent->random );
1170 	G_SpawnFloat( "wait", "0", &ent->wait );
1171 
1172 	RegisterItem( item );
1173 	ent->item = item;
1174 	// some movers spawn on the second frame, so delay item
1175 	// spawns until the third frame so they can ride trains
1176 	ent->nextthink = level.time + FRAMETIME * 2;
1177 	ent->think = FinishSpawningItem;
1178 
1179 	if ( G_SpawnString( "noise", 0, &noise ) ) {
1180 		ent->noise_index = G_SoundIndex( noise );
1181 	}
1182 
1183 	ent->physicsBounce = 0.50;      // items are bouncy
1184 
1185 	if ( ent->model ) {
1186 		ent->s.modelindex2 = G_ModelIndex( ent->model );
1187 	}
1188 
1189 	if ( item->giType == IT_CLIPBOARD ) {
1190 		if ( G_SpawnInt( "notebookpage", "1", &page ) ) {
1191 			ent->key = page;
1192 		}
1193 	}
1194 
1195 	if ( item->giType == IT_POWERUP ) {
1196 		G_SoundIndex( "sound/items/poweruprespawn.wav" );
1197 	}
1198 
1199 	if ( item->giType == IT_TREASURE ) {
1200 		level.numTreasure++;
1201 		G_SendMissionStats();
1202 	}
1203 }
1204 
1205 
1206 /*
1207 ================
1208 G_BounceItem
1209 
1210 ================
1211 */
G_BounceItem(gentity_t * ent,trace_t * trace)1212 void G_BounceItem( gentity_t *ent, trace_t *trace ) {
1213 	vec3_t velocity;
1214 	float dot;
1215 	int hitTime;
1216 
1217 	// reflect the velocity on the trace plane
1218 	hitTime = level.previousTime + ( level.time - level.previousTime ) * trace->fraction;
1219 	BG_EvaluateTrajectoryDelta( &ent->s.pos, hitTime, velocity );
1220 	dot = DotProduct( velocity, trace->plane.normal );
1221 	VectorMA( velocity, -2 * dot, trace->plane.normal, ent->s.pos.trDelta );
1222 
1223 	// cut the velocity to keep from bouncing forever
1224 	VectorScale( ent->s.pos.trDelta, ent->physicsBounce, ent->s.pos.trDelta );
1225 
1226 	// check for stop
1227 	if ( trace->plane.normal[2] > 0 && ent->s.pos.trDelta[2] < 40 ) {
1228 		trace->endpos[2] += 1.0;    // make sure it is off ground
1229 		SnapVector( trace->endpos );
1230 		G_SetOrigin( ent, trace->endpos );
1231 		ent->s.groundEntityNum = trace->entityNum;
1232 		return;
1233 	}
1234 
1235 	VectorAdd( ent->r.currentOrigin, trace->plane.normal, ent->r.currentOrigin );
1236 	VectorCopy( ent->r.currentOrigin, ent->s.pos.trBase );
1237 	ent->s.pos.trTime = level.time;
1238 }
1239 
1240 /*
1241 =================
1242 G_RunItemProp
1243 =================
1244 */
1245 
G_RunItemProp(gentity_t * ent,vec3_t origin)1246 void G_RunItemProp( gentity_t *ent, vec3_t origin ) {
1247 	gentity_t   *traceEnt;
1248 	trace_t trace;
1249 	gentity_t   *owner;
1250 	vec3_t start;
1251 	vec3_t end;
1252 
1253 	owner = &g_entities[ent->r.ownerNum];
1254 
1255 	VectorCopy( ent->r.currentOrigin, start );
1256 	start[2] += 1;
1257 
1258 	VectorCopy( origin, end );
1259 	end[2] += 1;
1260 
1261 	trap_Trace( &trace, ent->r.currentOrigin, ent->r.mins, ent->r.maxs, end,
1262 				ent->r.ownerNum, MASK_SHOT );
1263 
1264 	traceEnt = &g_entities[ trace.entityNum ];
1265 
1266 	if ( traceEnt && traceEnt->takedamage && traceEnt != ent ) {
1267 		ent->enemy = traceEnt;
1268 	}
1269 
1270 	if ( owner->client && trace.startsolid && traceEnt != owner && traceEnt != ent /* && !traceEnt->active*/ ) {
1271 
1272 		ent->takedamage = qfalse;
1273 		ent->die( ent, ent, NULL, 10, 0 );
1274 		Prop_Break_Sound( ent );
1275 
1276 		return;
1277 	} else if ( trace.surfaceFlags & SURF_NOIMPACT )    {
1278 		ent->takedamage = qfalse;
1279 
1280 		Props_Chair_Skyboxtouch( ent );
1281 
1282 		return;
1283 	}
1284 }
1285 
1286 /*
1287 ================
1288 G_RunItem
1289 
1290 ================
1291 */
G_RunItem(gentity_t * ent)1292 void G_RunItem( gentity_t *ent ) {
1293 	vec3_t origin;
1294 	trace_t tr;
1295 	int contents;
1296 	int mask;
1297 
1298 	// if its groundentity has been set to none, it may have been pushed off an edge
1299 	if ( ent->s.groundEntityNum == ENTITYNUM_NONE ) {
1300 		if ( ent->s.pos.trType != TR_GRAVITY ) {
1301 			ent->s.pos.trType = TR_GRAVITY;
1302 			ent->s.pos.trTime = level.time;
1303 		}
1304 	}
1305 
1306 	if ( ent->s.pos.trType == TR_STATIONARY || ent->s.pos.trType == TR_GRAVITY_PAUSED ) { //----(SA)
1307 		// check think function
1308 		G_RunThink( ent );
1309 		return;
1310 	}
1311 
1312 	// get current position
1313 	BG_EvaluateTrajectory( &ent->s.pos, level.time, origin );
1314 
1315 	// trace a line from the previous position to the current position
1316 	if ( ent->clipmask ) {
1317 		mask = ent->clipmask;
1318 	} else {
1319 		mask = MASK_SOLID | CONTENTS_MISSILECLIP;
1320 	}
1321 	trap_Trace( &tr, ent->r.currentOrigin, ent->r.mins, ent->r.maxs, origin,
1322 				ent->r.ownerNum, mask );
1323 
1324 	if ( ent->isProp && ent->takedamage ) {
1325 		G_RunItemProp( ent, origin );
1326 	}
1327 
1328 	VectorCopy( tr.endpos, ent->r.currentOrigin );
1329 
1330 	if ( tr.startsolid ) {
1331 		tr.fraction = 0;
1332 	}
1333 
1334 	trap_LinkEntity( ent ); // FIXME: avoid this for stationary?
1335 
1336 	// check think function
1337 	G_RunThink( ent );
1338 
1339 	if ( tr.fraction == 1 ) {
1340 		return;
1341 	}
1342 
1343 	// if it is in a nodrop volume, remove it
1344 	contents = trap_PointContents( ent->r.currentOrigin, -1 );
1345 	if ( contents & CONTENTS_NODROP ) {
1346 		if ( ent->item && ent->item->giType == IT_TEAM ) {
1347 			Team_FreeEntity( ent );
1348 		} else {
1349 			G_FreeEntity( ent );
1350 		}
1351 		return;
1352 	}
1353 
1354 	G_BounceItem( ent, &tr );
1355 }
1356 
1357