home *** CD-ROM | disk | FTP | other *** search
/ Maximum CD 2000 March / maximum-cd-2000-03.iso / Quake3 Game Source / Q3AGameSource.exe / Main / g_items.c < prev    next >
Encoding:
C/C++ Source or Header  |  2000-01-18  |  18.2 KB  |  759 lines

  1. // Copyright (C) 1999-2000 Id Software, Inc.
  2. //
  3. #include "g_local.h"
  4.  
  5. /*
  6.  
  7.   Items are any object that a player can touch to gain some effect.
  8.  
  9.   Pickup will return the number of seconds until they should respawn.
  10.  
  11.   all items should pop when dropped in lava or slime
  12.  
  13.   Respawnable items don't actually go away when picked up, they are
  14.   just made invisible and untouchable.  This allows them to ride
  15.   movers and respawn apropriately.
  16. */
  17.  
  18.  
  19. #define    RESPAWN_ARMOR        25
  20. #define    RESPAWN_TEAM_WEAPON    30
  21. #define    RESPAWN_HEALTH        35
  22. #define    RESPAWN_AMMO        40
  23. #define    RESPAWN_HOLDABLE    60
  24. #define    RESPAWN_MEGAHEALTH    120
  25. #define    RESPAWN_POWERUP        120
  26.  
  27.  
  28. //======================================================================
  29.  
  30. int Pickup_Powerup( gentity_t *ent, gentity_t *other ) {
  31.     int            quantity;
  32.     int            i;
  33.     gclient_t    *client;
  34.  
  35.     if ( !other->client->ps.powerups[ent->item->giTag] ) {
  36.         // round timing to seconds to make multiple powerup timers
  37.         // count in sync
  38.         other->client->ps.powerups[ent->item->giTag] = 
  39.             level.time - ( level.time % 1000 );
  40.     }
  41.  
  42.     if ( ent->count ) {
  43.         quantity = ent->count;
  44.     } else {
  45.         quantity = ent->item->quantity;
  46.     }
  47.  
  48.     other->client->ps.powerups[ent->item->giTag] += quantity * 1000;
  49.  
  50.     // give any nearby players a "denied" anti-reward
  51.     for ( i = 0 ; i < level.maxclients ; i++ ) {
  52.         vec3_t        delta;
  53.         float        len;
  54.         vec3_t        forward;
  55.         trace_t        tr;
  56.  
  57.         client = &level.clients[i];
  58.         if ( client == other->client ) {
  59.             continue;
  60.         }
  61.         if ( client->pers.connected == CON_DISCONNECTED ) {
  62.             continue;
  63.         }
  64.         if ( client->ps.stats[STAT_HEALTH] <= 0 ) {
  65.             continue;
  66.         }
  67.  
  68.     // if same team in team game, no sound
  69.     // cannot use OnSameTeam as it expects to g_entities, not clients
  70.       if ( g_gametype.integer >= GT_TEAM && other->client->sess.sessionTeam == client->sess.sessionTeam  ) {
  71.       continue;
  72.     }
  73.  
  74.         // if too far away, no sound
  75.         VectorSubtract( ent->s.pos.trBase, client->ps.origin, delta );
  76.         len = VectorNormalize( delta );
  77.         if ( len > 192 ) {
  78.             continue;
  79.         }
  80.  
  81.         // if not facing, no sound
  82.         AngleVectors( client->ps.viewangles, forward, NULL, NULL );
  83.         if ( DotProduct( delta, forward ) < 0.4 ) {
  84.             continue;
  85.         }
  86.  
  87.         // if not line of sight, no sound
  88.         trap_Trace( &tr, client->ps.origin, NULL, NULL, ent->s.pos.trBase, ENTITYNUM_NONE, CONTENTS_SOLID );
  89.         if ( tr.fraction != 1.0 ) {
  90.             continue;
  91.         }
  92.  
  93.         // anti-reward
  94.         client->ps.persistant[PERS_REWARD_COUNT]++;
  95.         client->ps.persistant[PERS_REWARD] = REWARD_DENIED;
  96.     }
  97.  
  98.     return RESPAWN_POWERUP;
  99. }
  100.  
  101. //======================================================================
  102.  
  103. int Pickup_Holdable( gentity_t *ent, gentity_t *other ) {
  104.  
  105.     other->client->ps.stats[STAT_HOLDABLE_ITEM] = ent->item - bg_itemlist;
  106.  
  107.     return RESPAWN_HOLDABLE;
  108. }
  109.  
  110.  
  111. //======================================================================
  112.  
  113. void Add_Ammo (gentity_t *ent, int weapon, int count)
  114. {
  115.     ent->client->ps.ammo[weapon] += count;
  116.     if ( ent->client->ps.ammo[weapon] > 200 ) {
  117.         ent->client->ps.ammo[weapon] = 200;
  118.     }
  119. }
  120.  
  121. int Pickup_Ammo (gentity_t *ent, gentity_t *other)
  122. {
  123.     int        quantity;
  124.  
  125.     if ( ent->count ) {
  126.         quantity = ent->count;
  127.     } else {
  128.         quantity = ent->item->quantity;
  129.     }
  130.  
  131.     Add_Ammo (other, ent->item->giTag, quantity);
  132.  
  133.     return RESPAWN_AMMO;
  134. }
  135.  
  136. //======================================================================
  137.  
  138.  
  139. int Pickup_Weapon (gentity_t *ent, gentity_t *other) {
  140.     int        quantity;
  141.  
  142.     if ( ent->count < 0 ) {
  143.         quantity = 0; // None for you, sir!
  144.     } else {
  145.         if ( ent->count ) {
  146.             quantity = ent->count;
  147.         } else {
  148.             quantity = ent->item->quantity;
  149.         }
  150.  
  151.         // dropped items and teamplay weapons always have full ammo
  152.         if ( ! (ent->flags & FL_DROPPED_ITEM) && g_gametype.integer != GT_TEAM ) {
  153.             // respawning rules
  154.             // drop the quantity if the already have over the minimum
  155.             if ( other->client->ps.ammo[ ent->item->giTag ] < quantity ) {
  156.                 quantity = quantity - other->client->ps.ammo[ ent->item->giTag ];
  157.             } else {
  158.                 quantity = 1;        // only add a single shot
  159.             }
  160.         }
  161.     }
  162.  
  163.     // add the weapon
  164.     other->client->ps.stats[STAT_WEAPONS] |= ( 1 << ent->item->giTag );
  165.  
  166.     Add_Ammo( other, ent->item->giTag, quantity );
  167.  
  168.     if (ent->item->giTag == WP_GRAPPLING_HOOK)
  169.         other->client->ps.ammo[ent->item->giTag] = -1; // unlimited ammo
  170.  
  171.     // team deathmatch has slow weapon respawns
  172.     if ( g_gametype.integer == GT_TEAM ) {
  173.         return RESPAWN_TEAM_WEAPON;
  174.     }
  175.  
  176.     return g_weaponRespawn.integer;
  177. }
  178.  
  179.  
  180. //======================================================================
  181.  
  182. int Pickup_Health (gentity_t *ent, gentity_t *other) {
  183.     int            max;
  184.     int            quantity;
  185.  
  186.     // small and mega healths will go over the max
  187.     if ( ent->item->quantity != 5 && ent->item->quantity != 100  ) {
  188.         max = other->client->ps.stats[STAT_MAX_HEALTH];
  189.     } else {
  190.         max = other->client->ps.stats[STAT_MAX_HEALTH] * 2;
  191.     }
  192.  
  193.     if ( ent->count ) {
  194.         quantity = ent->count;
  195.     } else {
  196.         quantity = ent->item->quantity;
  197.     }
  198.  
  199.     other->health += quantity;
  200.  
  201.     if (other->health > max ) {
  202.         other->health = max;
  203.     }
  204.     other->client->ps.stats[STAT_HEALTH] = other->health;
  205.  
  206.     if ( ent->item->giTag == 100 ) {        // mega health respawns slow
  207.         return RESPAWN_MEGAHEALTH;
  208.     }
  209.  
  210.     return RESPAWN_HEALTH;
  211. }
  212.  
  213. //======================================================================
  214.  
  215. int Pickup_Armor( gentity_t *ent, gentity_t *other ) {
  216.     other->client->ps.stats[STAT_ARMOR] += ent->item->quantity;
  217.     if ( other->client->ps.stats[STAT_ARMOR] > other->client->ps.stats[STAT_MAX_HEALTH] * 2 ) {
  218.         other->client->ps.stats[STAT_ARMOR] = other->client->ps.stats[STAT_MAX_HEALTH] * 2;
  219.     }
  220.  
  221.     return RESPAWN_ARMOR;
  222. }
  223.  
  224. //======================================================================
  225.  
  226. /*
  227. ===============
  228. RespawnItem
  229. ===============
  230. */
  231. void RespawnItem( gentity_t *ent ) {
  232.     // randomly select from teamed entities
  233.     if (ent->team) {
  234.         gentity_t    *master;
  235.         int    count;
  236.         int choice;
  237.  
  238.         if ( !ent->teammaster ) {
  239.             G_Error( "RespawnItem: bad teammaster");
  240.         }
  241.         master = ent->teammaster;
  242.  
  243.         for (count = 0, ent = master; ent; ent = ent->teamchain, count++)
  244.             ;
  245.  
  246.         choice = rand() % count;
  247.  
  248.         for (count = 0, ent = master; count < choice; ent = ent->teamchain, count++)
  249.             ;
  250.     }
  251.  
  252.     ent->r.contents = CONTENTS_TRIGGER;
  253.     ent->s.eFlags &= ~EF_NODRAW;
  254.     ent->r.svFlags &= ~SVF_NOCLIENT;
  255.     trap_LinkEntity (ent);
  256.  
  257.     if ( ent->item->giType == IT_POWERUP ) {
  258.         // play powerup spawn sound to all clients
  259.         gentity_t    *te;
  260.  
  261.         te = G_TempEntity( ent->s.pos.trBase, EV_GLOBAL_SOUND );
  262.         te->s.eventParm = G_SoundIndex( "sound/items/poweruprespawn.wav" );
  263.         te->r.svFlags |= SVF_BROADCAST;
  264.     }
  265.  
  266.     // play the normal respawn sound only to nearby clients
  267.     G_AddEvent( ent, EV_ITEM_RESPAWN, 0 );
  268.  
  269.     ent->nextthink = 0;
  270. }
  271.  
  272.  
  273. /*
  274. ===============
  275. Touch_Item
  276. ===============
  277. */
  278. void Touch_Item (gentity_t *ent, gentity_t *other, trace_t *trace) {
  279.     int            respawn;
  280.  
  281.     if (!other->client)
  282.         return;
  283.     if (other->health < 1)
  284.         return;        // dead people can't pickup
  285.  
  286.     // the same pickup rules are used for client side and server side
  287.     if ( !BG_CanItemBeGrabbed( &ent->s, &other->client->ps ) ) {
  288.         return;
  289.     }
  290.  
  291.     G_LogPrintf( "Item: %i %s\n", other->s.number, ent->item->classname );
  292.  
  293.     // call the item-specific pickup function
  294.     switch( ent->item->giType ) {
  295.     case IT_WEAPON:
  296.         respawn = Pickup_Weapon(ent, other);
  297.         break;
  298.     case IT_AMMO:
  299.         respawn = Pickup_Ammo(ent, other);
  300.         break;
  301.     case IT_ARMOR:
  302.         respawn = Pickup_Armor(ent, other);
  303.         break;
  304.     case IT_HEALTH:
  305.         respawn = Pickup_Health(ent, other);
  306.         break;
  307.     case IT_POWERUP:
  308.         respawn = Pickup_Powerup(ent, other);
  309.         break;
  310.     case IT_TEAM:
  311.         respawn = Pickup_Team(ent, other);
  312.         break;
  313.     case IT_HOLDABLE:
  314.         respawn = Pickup_Holdable(ent, other);
  315.         break;
  316.     default:
  317.         return;
  318.     }
  319.  
  320.     if ( !respawn ) {
  321.         return;
  322.     }
  323.  
  324.     // play the normal pickup sound
  325.     if ( other->client->pers.predictItemPickup ) {
  326.         G_AddPredictableEvent( other, EV_ITEM_PICKUP, ent->s.modelindex );
  327.     } else {
  328.         G_AddEvent( other, EV_ITEM_PICKUP, ent->s.modelindex );
  329.     }
  330.  
  331.     // powerup pickups are global broadcasts
  332.     if ( ent->item->giType == IT_POWERUP || ent->item->giType == IT_TEAM) {
  333.         gentity_t    *te;
  334.  
  335.         te = G_TempEntity( ent->s.pos.trBase, EV_GLOBAL_ITEM_PICKUP );
  336.         te->s.eventParm = ent->s.modelindex;
  337.         te->r.svFlags |= SVF_BROADCAST;
  338.     }
  339.  
  340.     // fire item targets
  341.     G_UseTargets (ent, other);
  342.  
  343.     // wait of -1 will not respawn
  344.     if ( ent->wait == -1 ) {
  345.         ent->r.svFlags |= SVF_NOCLIENT;
  346.         ent->s.eFlags |= EF_NODRAW;
  347.         ent->r.contents = 0;
  348.         ent->unlinkAfterEvent = qtrue;
  349.         return;
  350.     }
  351.  
  352.     // non zero wait overrides respawn time
  353.     if ( ent->wait ) {
  354.         respawn = ent->wait;
  355.     }
  356.  
  357.     // random can be used to vary the respawn time
  358.     if ( ent->random ) {
  359.         respawn += crandom() * ent->random;
  360.         if ( respawn < 1 ) {
  361.             respawn = 1;
  362.         }
  363.     }
  364.  
  365.     // dropped items will not respawn
  366.     if ( ent->flags & FL_DROPPED_ITEM ) {
  367.         ent->freeAfterEvent = qtrue;
  368.     }
  369.  
  370.     // picked up items still stay around, they just don't
  371.     // draw anything.  This allows respawnable items
  372.     // to be placed on movers.
  373.     ent->r.svFlags |= SVF_NOCLIENT;
  374.     ent->s.eFlags |= EF_NODRAW;
  375.     ent->r.contents = 0;
  376.  
  377.     // ZOID
  378.     // A negative respawn times means to never respawn this item (but don't 
  379.     // delete it).  This is used by items that are respawned by third party 
  380.     // events such as ctf flags
  381.     if ( respawn <= 0 ) {
  382.         ent->nextthink = 0;
  383.         ent->think = 0;
  384.     } else {
  385.         ent->nextthink = level.time + respawn * 1000;
  386.         ent->think = RespawnItem;
  387.     }
  388.     trap_LinkEntity( ent );
  389. }
  390.  
  391.  
  392. //======================================================================
  393.  
  394. /*
  395. ================
  396. LaunchItem
  397.  
  398. Spawns an item and tosses it forward
  399. ================
  400. */
  401. gentity_t *LaunchItem( gitem_t *item, vec3_t origin, vec3_t velocity ) {
  402.     gentity_t    *dropped;
  403.  
  404.     dropped = G_Spawn();
  405.  
  406.     dropped->s.eType = ET_ITEM;
  407.     dropped->s.modelindex = item - bg_itemlist;    // store item number in modelindex
  408.     dropped->s.modelindex2 = 1; // This is non-zero is it's a dropped item
  409.  
  410.     dropped->classname = item->classname;
  411.     dropped->item = item;
  412.     VectorSet (dropped->r.mins, -ITEM_RADIUS, -ITEM_RADIUS, -ITEM_RADIUS);
  413.     VectorSet (dropped->r.maxs, ITEM_RADIUS, ITEM_RADIUS, ITEM_RADIUS);
  414.     dropped->r.contents = CONTENTS_TRIGGER;
  415.  
  416.     dropped->touch = Touch_Item;
  417.  
  418.     G_SetOrigin( dropped, origin );
  419.     dropped->s.pos.trType = TR_GRAVITY;
  420.     dropped->s.pos.trTime = level.time;
  421.     VectorCopy( velocity, dropped->s.pos.trDelta );
  422.  
  423.     dropped->s.eFlags |= EF_BOUNCE_HALF;
  424.  
  425.     if (item->giType == IT_TEAM) { // Special case for CTF flags
  426.         dropped->think = Team_DroppedFlagThink;
  427.         dropped->nextthink = level.time + 30000;
  428.         Team_CheckDroppedItem( dropped );
  429.     } else { // auto-remove after 30 seconds
  430.         dropped->think = G_FreeEntity;
  431.         dropped->nextthink = level.time + 30000;
  432.     }
  433.  
  434.     dropped->flags = FL_DROPPED_ITEM;
  435.  
  436.     trap_LinkEntity (dropped);
  437.  
  438.     return dropped;
  439. }
  440.  
  441. /*
  442. ================
  443. Drop_Item
  444.  
  445. Spawns an item and tosses it forward
  446. ================
  447. */
  448. gentity_t *Drop_Item( gentity_t *ent, gitem_t *item, float angle ) {
  449.     vec3_t    velocity;
  450.     vec3_t    angles;
  451.  
  452.     VectorCopy( ent->s.apos.trBase, angles );
  453.     angles[YAW] += angle;
  454.     angles[PITCH] = 0;    // always forward
  455.  
  456.     AngleVectors( angles, velocity, NULL, NULL );
  457.     VectorScale( velocity, 150, velocity );
  458.     velocity[2] += 200 + crandom() * 50;
  459.     
  460.     return LaunchItem( item, ent->s.pos.trBase, velocity );
  461. }
  462.  
  463.  
  464. /*
  465. ================
  466. Use_Item
  467.  
  468. Respawn the item
  469. ================
  470. */
  471. void Use_Item( gentity_t *ent, gentity_t *other, gentity_t *activator ) {
  472.     RespawnItem( ent );
  473. }
  474.  
  475. //======================================================================
  476.  
  477. /*
  478. ================
  479. FinishSpawningItem
  480.  
  481. Traces down to find where an item should rest, instead of letting them
  482. free fall from their spawn points
  483. ================
  484. */
  485. void FinishSpawningItem( gentity_t *ent ) {
  486.     trace_t        tr;
  487.     vec3_t        dest;
  488.  
  489.     VectorSet( ent->r.mins, -ITEM_RADIUS, -ITEM_RADIUS, -ITEM_RADIUS );
  490.     VectorSet( ent->r.maxs, ITEM_RADIUS, ITEM_RADIUS, ITEM_RADIUS );
  491.  
  492.     ent->s.eType = ET_ITEM;
  493.     ent->s.modelindex = ent->item - bg_itemlist;        // store item number in modelindex
  494.     ent->s.modelindex2 = 0; // zero indicates this isn't a dropped item
  495.  
  496.     ent->r.contents = CONTENTS_TRIGGER;
  497.     ent->touch = Touch_Item;
  498.     // useing an item causes it to respawn
  499.     ent->use = Use_Item;
  500.  
  501.     if ( ent->spawnflags & 1 ) {
  502.         // suspended
  503.         G_SetOrigin( ent, ent->s.origin );
  504.     } else {
  505.         // drop to floor
  506.         VectorSet( dest, ent->s.origin[0], ent->s.origin[1], ent->s.origin[2] - 4096 );
  507.         trap_Trace( &tr, ent->s.origin, ent->r.mins, ent->r.maxs, dest, ent->s.number, MASK_SOLID );
  508.         if ( tr.startsolid ) {
  509.             G_Printf ("FinishSpawningItem: %s startsolid at %s\n", ent->classname, vtos(ent->s.origin));
  510.             G_FreeEntity( ent );
  511.             return;
  512.         }
  513.  
  514.         // allow to ride movers
  515.         ent->s.groundEntityNum = tr.entityNum;
  516.  
  517.         G_SetOrigin( ent, tr.endpos );
  518.     }
  519.  
  520.     // team slaves and targeted items aren't present at start
  521.     if ( ( ent->flags & FL_TEAMSLAVE ) || ent->targetname ) {
  522.         ent->s.eFlags |= EF_NODRAW;
  523.         ent->r.contents = 0;
  524.         return;
  525.     }
  526.  
  527.     // powerups don't spawn in for a while
  528.     if ( ent->item->giType == IT_POWERUP ) {
  529.         float    respawn;
  530.  
  531.         respawn = 45 + crandom() * 15;
  532.         ent->s.eFlags |= EF_NODRAW;
  533.         ent->r.contents = 0;
  534.         ent->nextthink = level.time + respawn * 1000;
  535.         ent->think = RespawnItem;
  536.         return;
  537.     }
  538.  
  539.  
  540.     trap_LinkEntity (ent);
  541. }
  542.  
  543.  
  544. qboolean    itemRegistered[MAX_ITEMS];
  545.  
  546. /*
  547. ==================
  548. G_CheckTeamItems
  549. ==================
  550. */
  551. void G_CheckTeamItems( void ) {
  552.  
  553.     // Set up team stuff
  554.     Team_InitGame();
  555.  
  556.     if ( g_gametype.integer == GT_CTF ) {
  557.         gitem_t    *item;
  558.  
  559.         // make sure we actually have two flags...
  560.         item = BG_FindItem( "Red Flag" );
  561.         if ( !item || !itemRegistered[ item - bg_itemlist ] ) {
  562.             G_Printf( "^1WARNING: No team_CTF_redflag in map" );
  563.         }
  564.         item = BG_FindItem( "Blue Flag" );
  565.         if ( !item || !itemRegistered[ item - bg_itemlist ] ) {
  566.             G_Printf( "^1WARNING: No team_CTF_blueflag in map" );
  567.         }
  568.     }
  569. }
  570.  
  571. /*
  572. ==============
  573. ClearRegisteredItems
  574. ==============
  575. */
  576. void ClearRegisteredItems( void ) {
  577.     memset( itemRegistered, 0, sizeof( itemRegistered ) );
  578.  
  579.     // players always start with the base weapon
  580.     RegisterItem( BG_FindItemForWeapon( WP_MACHINEGUN ) );
  581.     RegisterItem( BG_FindItemForWeapon( WP_GAUNTLET ) );
  582. }
  583.  
  584. /*
  585. ===============
  586. RegisterItem
  587.  
  588. The item will be added to the precache list
  589. ===============
  590. */
  591. void RegisterItem( gitem_t *item ) {
  592.     if ( !item ) {
  593.         G_Error( "RegisterItem: NULL" );
  594.     }
  595.     itemRegistered[ item - bg_itemlist ] = qtrue;
  596. }
  597.  
  598.  
  599. /*
  600. ===============
  601. SaveRegisteredItems
  602.  
  603. Write the needed items to a config string
  604. so the client will know which ones to precache
  605. ===============
  606. */
  607. void SaveRegisteredItems( void ) {
  608.     char    string[MAX_ITEMS+1];
  609.     int        i;
  610.     int        count;
  611.  
  612.     count = 0;
  613.     for ( i = 0 ; i < bg_numItems ; i++ ) {
  614.         if ( itemRegistered[i] ) {
  615.             count++;
  616.             string[i] = '1';
  617.         } else {
  618.             string[i] = '0';
  619.         }
  620.     }
  621.     string[ bg_numItems ] = 0;
  622.  
  623.     G_Printf( "%i items registered\n", count );
  624.     trap_SetConfigstring(CS_ITEMS, string);
  625. }
  626.  
  627.  
  628. /*
  629. ============
  630. G_SpawnItem
  631.  
  632. Sets the clipping size and plants the object on the floor.
  633.  
  634. Items can't be immediately dropped to floor, because they might
  635. be on an entity that hasn't spawned yet.
  636. ============
  637. */
  638. void G_SpawnItem (gentity_t *ent, gitem_t *item) {
  639.     G_SpawnFloat( "random", "0", &ent->random );
  640.     G_SpawnFloat( "wait", "0", &ent->wait );
  641.  
  642.     RegisterItem( item );
  643.     ent->item = item;
  644.     // some movers spawn on the second frame, so delay item
  645.     // spawns until the third frame so they can ride trains
  646.     ent->nextthink = level.time + FRAMETIME * 2;
  647.     ent->think = FinishSpawningItem;
  648.  
  649.     ent->physicsBounce = 0.50;        // items are bouncy
  650.  
  651.     if ( item->giType == IT_POWERUP ) {
  652.         G_SoundIndex( "sound/items/poweruprespawn.wav" );
  653.     }
  654. }
  655.  
  656.  
  657. /*
  658. ================
  659. G_BounceItem
  660.  
  661. ================
  662. */
  663. void G_BounceItem( gentity_t *ent, trace_t *trace ) {
  664.     vec3_t    velocity;
  665.     float    dot;
  666.     int        hitTime;
  667.  
  668.     // reflect the velocity on the trace plane
  669.     hitTime = level.previousTime + ( level.time - level.previousTime ) * trace->fraction;
  670.     BG_EvaluateTrajectoryDelta( &ent->s.pos, hitTime, velocity );
  671.     dot = DotProduct( velocity, trace->plane.normal );
  672.     VectorMA( velocity, -2*dot, trace->plane.normal, ent->s.pos.trDelta );
  673.  
  674.     // cut the velocity to keep from bouncing forever
  675.     VectorScale( ent->s.pos.trDelta, ent->physicsBounce, ent->s.pos.trDelta );
  676.  
  677.     // check for stop
  678.     if ( trace->plane.normal[2] > 0 && ent->s.pos.trDelta[2] < 40 ) {
  679.         trace->endpos[2] += 1.0;    // make sure it is off ground
  680.         SnapVector( trace->endpos );
  681.         G_SetOrigin( ent, trace->endpos );
  682.         ent->s.groundEntityNum = trace->entityNum;
  683.         return;
  684.     }
  685.  
  686.     VectorAdd( ent->r.currentOrigin, trace->plane.normal, ent->r.currentOrigin);
  687.     VectorCopy( ent->r.currentOrigin, ent->s.pos.trBase );
  688.     ent->s.pos.trTime = level.time;
  689. }
  690.  
  691.  
  692. /*
  693. ================
  694. G_RunItem
  695.  
  696. ================
  697. */
  698. void G_RunItem( gentity_t *ent ) {
  699.     vec3_t        origin;
  700.     trace_t        tr;
  701.     int            contents;
  702.     int            mask;
  703.  
  704.     // if groundentity has been set to -1, it may have been pushed off an edge
  705.     if ( ent->s.groundEntityNum == -1 ) {
  706.         if ( ent->s.pos.trType != TR_GRAVITY ) {
  707.             ent->s.pos.trType = TR_GRAVITY;
  708.             ent->s.pos.trTime = level.time;
  709.         }
  710.     }
  711.  
  712.     if ( ent->s.pos.trType == TR_STATIONARY ) {
  713.         // check think function
  714.         G_RunThink( ent );
  715.         return;
  716.     }
  717.  
  718.     // get current position
  719.     BG_EvaluateTrajectory( &ent->s.pos, level.time, origin );
  720.  
  721.     // trace a line from the previous position to the current position
  722.     if ( ent->clipmask ) {
  723.         mask = ent->clipmask;
  724.     } else {
  725.         mask = MASK_PLAYERSOLID & ~CONTENTS_BODY;//MASK_SOLID;
  726.     }
  727.     trap_Trace( &tr, ent->r.currentOrigin, ent->r.mins, ent->r.maxs, origin, 
  728.         ent->r.ownerNum, mask );
  729.  
  730.     VectorCopy( tr.endpos, ent->r.currentOrigin );
  731.  
  732.     if ( tr.startsolid ) {
  733.         tr.fraction = 0;
  734.     }
  735.  
  736.     trap_LinkEntity( ent );    // FIXME: avoid this for stationary?
  737.  
  738.     // check think function
  739.     G_RunThink( ent );
  740.  
  741.     if ( tr.fraction == 1 ) {
  742.         return;
  743.     }
  744.  
  745.     // if it is in a nodrop volume, remove it
  746.     contents = trap_PointContents( ent->r.currentOrigin, -1 );
  747.     if ( contents & CONTENTS_NODROP ) {
  748.         if (ent->item && ent->item->giType == IT_TEAM) {
  749.             Team_FreeEntity(ent);
  750.         } else {
  751.             G_FreeEntity( ent );
  752.         }
  753.         return;
  754.     }
  755.  
  756.     G_BounceItem( ent, &tr );
  757. }
  758.  
  759.