Main Page | Class Hierarchy | Alphabetical List | Data Structures | Directories | File List | Data Fields | Globals

g_items.c

Go to the documentation of this file.
00001 /*
00002 ===========================================================================
00003 Copyright (C) 1999-2005 Id Software, Inc.
00004 
00005 This file is part of Quake III Arena source code.
00006 
00007 Quake III Arena source code is free software; you can redistribute it
00008 and/or modify it under the terms of the GNU General Public License as
00009 published by the Free Software Foundation; either version 2 of the License,
00010 or (at your option) any later version.
00011 
00012 Quake III Arena source code is distributed in the hope that it will be
00013 useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
00014 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015 GNU General Public License for more details.
00016 
00017 You should have received a copy of the GNU General Public License
00018 along with Foobar; if not, write to the Free Software
00019 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
00020 ===========================================================================
00021 */
00022 //
00023 #include "g_local.h"
00024 
00025 /*
00026 
00027   Items are any object that a player can touch to gain some effect.
00028 
00029   Pickup will return the number of seconds until they should respawn.
00030 
00031   all items should pop when dropped in lava or slime
00032 
00033   Respawnable items don't actually go away when picked up, they are
00034   just made invisible and untouchable.  This allows them to ride
00035   movers and respawn apropriately.
00036 */
00037 
00038 
00039 #define RESPAWN_ARMOR       25
00040 #define RESPAWN_HEALTH      35
00041 #define RESPAWN_AMMO        40
00042 #define RESPAWN_HOLDABLE    60
00043 #define RESPAWN_MEGAHEALTH  35//120
00044 #define RESPAWN_POWERUP     120
00045 
00046 
00047 //======================================================================
00048 
00049 int Pickup_Powerup( gentity_t *ent, gentity_t *other ) {
00050     int         quantity;
00051     int         i;
00052     gclient_t   *client;
00053 
00054     if ( !other->client->ps.powerups[ent->item->giTag] ) {
00055         // round timing to seconds to make multiple powerup timers
00056         // count in sync
00057         other->client->ps.powerups[ent->item->giTag] = 
00058             level.time - ( level.time % 1000 );
00059     }
00060 
00061     if ( ent->count ) {
00062         quantity = ent->count;
00063     } else {
00064         quantity = ent->item->quantity;
00065     }
00066 
00067     other->client->ps.powerups[ent->item->giTag] += quantity * 1000;
00068 
00069     // give any nearby players a "denied" anti-reward
00070     for ( i = 0 ; i < level.maxclients ; i++ ) {
00071         vec3_t      delta;
00072         float       len;
00073         vec3_t      forward;
00074         trace_t     tr;
00075 
00076         client = &level.clients[i];
00077         if ( client == other->client ) {
00078             continue;
00079         }
00080         if ( client->pers.connected == CON_DISCONNECTED ) {
00081             continue;
00082         }
00083         if ( client->ps.stats[STAT_HEALTH] <= 0 ) {
00084             continue;
00085         }
00086 
00087     // if same team in team game, no sound
00088     // cannot use OnSameTeam as it expects to g_entities, not clients
00089     if ( g_gametype.integer >= GT_TEAM && other->client->sess.sessionTeam == client->sess.sessionTeam  ) {
00090       continue;
00091     }
00092 
00093         // if too far away, no sound
00094         VectorSubtract( ent->s.pos.trBase, client->ps.origin, delta );
00095         len = VectorNormalize( delta );
00096         if ( len > 192 ) {
00097             continue;
00098         }
00099 
00100         // if not facing, no sound
00101         AngleVectors( client->ps.viewangles, forward, NULL, NULL );
00102         if ( DotProduct( delta, forward ) < 0.4 ) {
00103             continue;
00104         }
00105 
00106         // if not line of sight, no sound
00107         trap_Trace( &tr, client->ps.origin, NULL, NULL, ent->s.pos.trBase, ENTITYNUM_NONE, CONTENTS_SOLID );
00108         if ( tr.fraction != 1.0 ) {
00109             continue;
00110         }
00111 
00112         // anti-reward
00113         client->ps.persistant[PERS_PLAYEREVENTS] ^= PLAYEREVENT_DENIEDREWARD;
00114     }
00115     return RESPAWN_POWERUP;
00116 }
00117 
00118 //======================================================================
00119 
00120 #ifdef MISSIONPACK
00121 int Pickup_PersistantPowerup( gentity_t *ent, gentity_t *other ) {
00122     int     clientNum;
00123     char    userinfo[MAX_INFO_STRING];
00124     float   handicap;
00125     int     max;
00126 
00127     other->client->ps.stats[STAT_PERSISTANT_POWERUP] = ent->item - bg_itemlist;
00128     other->client->persistantPowerup = ent;
00129 
00130     switch( ent->item->giTag ) {
00131     case PW_GUARD:
00132         clientNum = other->client->ps.clientNum;
00133         trap_GetUserinfo( clientNum, userinfo, sizeof(userinfo) );
00134         handicap = atof( Info_ValueForKey( userinfo, "handicap" ) );
00135         if( handicap<=0.0f || handicap>100.0f) {
00136             handicap = 100.0f;
00137         }
00138         max = (int)(2 *  handicap);
00139 
00140         other->health = max;
00141         other->client->ps.stats[STAT_HEALTH] = max;
00142         other->client->ps.stats[STAT_MAX_HEALTH] = max;
00143         other->client->ps.stats[STAT_ARMOR] = max;
00144         other->client->pers.maxHealth = max;
00145 
00146         break;
00147 
00148     case PW_SCOUT:
00149         clientNum = other->client->ps.clientNum;
00150         trap_GetUserinfo( clientNum, userinfo, sizeof(userinfo) );
00151         handicap = atof( Info_ValueForKey( userinfo, "handicap" ) );
00152         if( handicap<=0.0f || handicap>100.0f) {
00153             handicap = 100.0f;
00154         }
00155         other->client->pers.maxHealth = handicap;
00156         other->client->ps.stats[STAT_ARMOR] = 0;
00157         break;
00158 
00159     case PW_DOUBLER:
00160         clientNum = other->client->ps.clientNum;
00161         trap_GetUserinfo( clientNum, userinfo, sizeof(userinfo) );
00162         handicap = atof( Info_ValueForKey( userinfo, "handicap" ) );
00163         if( handicap<=0.0f || handicap>100.0f) {
00164             handicap = 100.0f;
00165         }
00166         other->client->pers.maxHealth = handicap;
00167         break;
00168     case PW_AMMOREGEN:
00169         clientNum = other->client->ps.clientNum;
00170         trap_GetUserinfo( clientNum, userinfo, sizeof(userinfo) );
00171         handicap = atof( Info_ValueForKey( userinfo, "handicap" ) );
00172         if( handicap<=0.0f || handicap>100.0f) {
00173             handicap = 100.0f;
00174         }
00175         other->client->pers.maxHealth = handicap;
00176         memset(other->client->ammoTimes, 0, sizeof(other->client->ammoTimes));
00177         break;
00178     default:
00179         clientNum = other->client->ps.clientNum;
00180         trap_GetUserinfo( clientNum, userinfo, sizeof(userinfo) );
00181         handicap = atof( Info_ValueForKey( userinfo, "handicap" ) );
00182         if( handicap<=0.0f || handicap>100.0f) {
00183             handicap = 100.0f;
00184         }
00185         other->client->pers.maxHealth = handicap;
00186         break;
00187     }
00188 
00189     return -1;
00190 }
00191 
00192 //======================================================================
00193 #endif
00194 
00195 int Pickup_Holdable( gentity_t *ent, gentity_t *other ) {
00196 
00197     other->client->ps.stats[STAT_HOLDABLE_ITEM] = ent->item - bg_itemlist;
00198 
00199     if( ent->item->giTag == HI_KAMIKAZE ) {
00200         other->client->ps.eFlags |= EF_KAMIKAZE;
00201     }
00202 
00203     return RESPAWN_HOLDABLE;
00204 }
00205 
00206 
00207 //======================================================================
00208 
00209 void Add_Ammo (gentity_t *ent, int weapon, int count)
00210 {
00211     ent->client->ps.ammo[weapon] += count;
00212     if ( ent->client->ps.ammo[weapon] > 200 ) {
00213         ent->client->ps.ammo[weapon] = 200;
00214     }
00215 }
00216 
00217 int Pickup_Ammo (gentity_t *ent, gentity_t *other)
00218 {
00219     int     quantity;
00220 
00221     if ( ent->count ) {
00222         quantity = ent->count;
00223     } else {
00224         quantity = ent->item->quantity;
00225     }
00226 
00227     Add_Ammo (other, ent->item->giTag, quantity);
00228 
00229     return RESPAWN_AMMO;
00230 }
00231 
00232 //======================================================================
00233 
00234 
00235 int Pickup_Weapon (gentity_t *ent, gentity_t *other) {
00236     int     quantity;
00237 
00238     if ( ent->count < 0 ) {
00239         quantity = 0; // None for you, sir!
00240     } else {
00241         if ( ent->count ) {
00242             quantity = ent->count;
00243         } else {
00244             quantity = ent->item->quantity;
00245         }
00246 
00247         // dropped items and teamplay weapons always have full ammo
00248         if ( ! (ent->flags & FL_DROPPED_ITEM) && g_gametype.integer != GT_TEAM ) {
00249             // respawning rules
00250             // drop the quantity if the already have over the minimum
00251             if ( other->client->ps.ammo[ ent->item->giTag ] < quantity ) {
00252                 quantity = quantity - other->client->ps.ammo[ ent->item->giTag ];
00253             } else {
00254                 quantity = 1;       // only add a single shot
00255             }
00256         }
00257     }
00258 
00259     // add the weapon
00260     other->client->ps.stats[STAT_WEAPONS] |= ( 1 << ent->item->giTag );
00261 
00262     Add_Ammo( other, ent->item->giTag, quantity );
00263 
00264     if (ent->item->giTag == WP_GRAPPLING_HOOK)
00265         other->client->ps.ammo[ent->item->giTag] = -1; // unlimited ammo
00266 
00267     // team deathmatch has slow weapon respawns
00268     if ( g_gametype.integer == GT_TEAM ) {
00269         return g_weaponTeamRespawn.integer;
00270     }
00271 
00272     return g_weaponRespawn.integer;
00273 }
00274 
00275 
00276 //======================================================================
00277 
00278 int Pickup_Health (gentity_t *ent, gentity_t *other) {
00279     int         max;
00280     int         quantity;
00281 
00282     // small and mega healths will go over the max
00283 #ifdef MISSIONPACK
00284     if( other->client && bg_itemlist[other->client->ps.stats[STAT_PERSISTANT_POWERUP]].giTag == PW_GUARD ) {
00285         max = other->client->ps.stats[STAT_MAX_HEALTH];
00286     }
00287     else
00288 #endif
00289     if ( ent->item->quantity != 5 && ent->item->quantity != 100 ) {
00290         max = other->client->ps.stats[STAT_MAX_HEALTH];
00291     } else {
00292         max = other->client->ps.stats[STAT_MAX_HEALTH] * 2;
00293     }
00294 
00295     if ( ent->count ) {
00296         quantity = ent->count;
00297     } else {
00298         quantity = ent->item->quantity;
00299     }
00300 
00301     other->health += quantity;
00302 
00303     if (other->health > max ) {
00304         other->health = max;
00305     }
00306     other->client->ps.stats[STAT_HEALTH] = other->health;
00307 
00308     if ( ent->item->quantity == 100 ) {     // mega health respawns slow
00309         return RESPAWN_MEGAHEALTH;
00310     }
00311 
00312     return RESPAWN_HEALTH;
00313 }
00314 
00315 //======================================================================
00316 
00317 int Pickup_Armor( gentity_t *ent, gentity_t *other ) {
00318 #ifdef MISSIONPACK
00319     int     upperBound;
00320 
00321     other->client->ps.stats[STAT_ARMOR] += ent->item->quantity;
00322 
00323     if( other->client && bg_itemlist[other->client->ps.stats[STAT_PERSISTANT_POWERUP]].giTag == PW_GUARD ) {
00324         upperBound = other->client->ps.stats[STAT_MAX_HEALTH];
00325     }
00326     else {
00327         upperBound = other->client->ps.stats[STAT_MAX_HEALTH] * 2;
00328     }
00329 
00330     if ( other->client->ps.stats[STAT_ARMOR] > upperBound ) {
00331         other->client->ps.stats[STAT_ARMOR] = upperBound;
00332     }
00333 #else
00334     other->client->ps.stats[STAT_ARMOR] += ent->item->quantity;
00335     if ( other->client->ps.stats[STAT_ARMOR] > other->client->ps.stats[STAT_MAX_HEALTH] * 2 ) {
00336         other->client->ps.stats[STAT_ARMOR] = other->client->ps.stats[STAT_MAX_HEALTH] * 2;
00337     }
00338 #endif
00339 
00340     return RESPAWN_ARMOR;
00341 }
00342 
00343 //======================================================================
00344 
00345 /*
00346 ===============
00347 RespawnItem
00348 ===============
00349 */
00350 void RespawnItem( gentity_t *ent ) {
00351     // randomly select from teamed entities
00352     if (ent->team) {
00353         gentity_t   *master;
00354         int count;
00355         int choice;
00356 
00357         if ( !ent->teammaster ) {
00358             G_Error( "RespawnItem: bad teammaster");
00359         }
00360         master = ent->teammaster;
00361 
00362         for (count = 0, ent = master; ent; ent = ent->teamchain, count++)
00363             ;
00364 
00365         choice = rand() % count;
00366 
00367         for (count = 0, ent = master; count < choice; ent = ent->teamchain, count++)
00368             ;
00369     }
00370 
00371     ent->r.contents = CONTENTS_TRIGGER;
00372     ent->s.eFlags &= ~EF_NODRAW;
00373     ent->r.svFlags &= ~SVF_NOCLIENT;
00374     trap_LinkEntity (ent);
00375 
00376     if ( ent->item->giType == IT_POWERUP ) {
00377         // play powerup spawn sound to all clients
00378         gentity_t   *te;
00379 
00380         // if the powerup respawn sound should Not be global
00381         if (ent->speed) {
00382             te = G_TempEntity( ent->s.pos.trBase, EV_GENERAL_SOUND );
00383         }
00384         else {
00385             te = G_TempEntity( ent->s.pos.trBase, EV_GLOBAL_SOUND );
00386         }
00387         te->s.eventParm = G_SoundIndex( "sound/items/poweruprespawn.wav" );
00388         te->r.svFlags |= SVF_BROADCAST;
00389     }
00390 
00391     if ( ent->item->giType == IT_HOLDABLE && ent->item->giTag == HI_KAMIKAZE ) {
00392         // play powerup spawn sound to all clients
00393         gentity_t   *te;
00394 
00395         // if the powerup respawn sound should Not be global
00396         if (ent->speed) {
00397             te = G_TempEntity( ent->s.pos.trBase, EV_GENERAL_SOUND );
00398         }
00399         else {
00400             te = G_TempEntity( ent->s.pos.trBase, EV_GLOBAL_SOUND );
00401         }
00402         te->s.eventParm = G_SoundIndex( "sound/items/kamikazerespawn.wav" );
00403         te->r.svFlags |= SVF_BROADCAST;
00404     }
00405 
00406     // play the normal respawn sound only to nearby clients
00407     G_AddEvent( ent, EV_ITEM_RESPAWN, 0 );
00408 
00409     ent->nextthink = 0;
00410 }
00411 
00412 
00413 /*
00414 ===============
00415 Touch_Item
00416 ===============
00417 */
00418 void Touch_Item (gentity_t *ent, gentity_t *other, trace_t *trace) {
00419     int         respawn;
00420     qboolean    predict;
00421 
00422     if (!other->client)
00423         return;
00424     if (other->health < 1)
00425         return;     // dead people can't pickup
00426 
00427     // the same pickup rules are used for client side and server side
00428     if ( !BG_CanItemBeGrabbed( g_gametype.integer, &ent->s, &other->client->ps ) ) {
00429         return;
00430     }
00431 
00432     G_LogPrintf( "Item: %i %s\n", other->s.number, ent->item->classname );
00433 
00434     predict = other->client->pers.predictItemPickup;
00435 
00436     // call the item-specific pickup function
00437     switch( ent->item->giType ) {
00438     case IT_WEAPON:
00439         respawn = Pickup_Weapon(ent, other);
00440 //      predict = qfalse;
00441         break;
00442     case IT_AMMO:
00443         respawn = Pickup_Ammo(ent, other);
00444 //      predict = qfalse;
00445         break;
00446     case IT_ARMOR:
00447         respawn = Pickup_Armor(ent, other);
00448         break;
00449     case IT_HEALTH:
00450         respawn = Pickup_Health(ent, other);
00451         break;
00452     case IT_POWERUP:
00453         respawn = Pickup_Powerup(ent, other);
00454         predict = qfalse;
00455         break;
00456 #ifdef MISSIONPACK
00457     case IT_PERSISTANT_POWERUP:
00458         respawn = Pickup_PersistantPowerup(ent, other);
00459         break;
00460 #endif
00461     case IT_TEAM:
00462         respawn = Pickup_Team(ent, other);
00463         break;
00464     case IT_HOLDABLE:
00465         respawn = Pickup_Holdable(ent, other);
00466         break;
00467     default:
00468         return;
00469     }
00470 
00471     if ( !respawn ) {
00472         return;
00473     }
00474 
00475     // play the normal pickup sound
00476     if (predict) {
00477         G_AddPredictableEvent( other, EV_ITEM_PICKUP, ent->s.modelindex );
00478     } else {
00479         G_AddEvent( other, EV_ITEM_PICKUP, ent->s.modelindex );
00480     }
00481 
00482     // powerup pickups are global broadcasts
00483     if ( ent->item->giType == IT_POWERUP || ent->item->giType == IT_TEAM) {
00484         // if we want the global sound to play
00485         if (!ent->speed) {
00486             gentity_t   *te;
00487 
00488             te = G_TempEntity( ent->s.pos.trBase, EV_GLOBAL_ITEM_PICKUP );
00489             te->s.eventParm = ent->s.modelindex;
00490             te->r.svFlags |= SVF_BROADCAST;
00491         } else {
00492             gentity_t   *te;
00493 
00494             te = G_TempEntity( ent->s.pos.trBase, EV_GLOBAL_ITEM_PICKUP );
00495             te->s.eventParm = ent->s.modelindex;
00496             // only send this temp entity to a single client
00497             te->r.svFlags |= SVF_SINGLECLIENT;
00498             te->r.singleClient = other->s.number;
00499         }
00500     }
00501 
00502     // fire item targets
00503     G_UseTargets (ent, other);
00504 
00505     // wait of -1 will not respawn
00506     if ( ent->wait == -1 ) {
00507         ent->r.svFlags |= SVF_NOCLIENT;
00508         ent->s.eFlags |= EF_NODRAW;
00509         ent->r.contents = 0;
00510         ent->unlinkAfterEvent = qtrue;
00511         return;
00512     }
00513 
00514     // non zero wait overrides respawn time
00515     if ( ent->wait ) {
00516         respawn = ent->wait;
00517     }
00518 
00519     // random can be used to vary the respawn time
00520     if ( ent->random ) {
00521         respawn += crandom() * ent->random;
00522         if ( respawn < 1 ) {
00523             respawn = 1;
00524         }
00525     }
00526 
00527     // dropped items will not respawn
00528     if ( ent->flags & FL_DROPPED_ITEM ) {
00529         ent->freeAfterEvent = qtrue;
00530     }
00531 
00532     // picked up items still stay around, they just don't
00533     // draw anything.  This allows respawnable items
00534     // to be placed on movers.
00535     ent->r.svFlags |= SVF_NOCLIENT;
00536     ent->s.eFlags |= EF_NODRAW;
00537     ent->r.contents = 0;
00538 
00539     // ZOID
00540     // A negative respawn times means to never respawn this item (but don't 
00541     // delete it).  This is used by items that are respawned by third party 
00542     // events such as ctf flags
00543     if ( respawn <= 0 ) {
00544         ent->nextthink = 0;
00545         ent->think = 0;
00546     } else {
00547         ent->nextthink = level.time + respawn * 1000;
00548         ent->think = RespawnItem;
00549     }
00550     trap_LinkEntity( ent );
00551 }
00552 
00553 
00554 //======================================================================
00555 
00556 /*
00557 ================
00558 LaunchItem
00559 
00560 Spawns an item and tosses it forward
00561 ================
00562 */
00563 gentity_t *LaunchItem( gitem_t *item, vec3_t origin, vec3_t velocity ) {
00564     gentity_t   *dropped;
00565 
00566     dropped = G_Spawn();
00567 
00568     dropped->s.eType = ET_ITEM;
00569     dropped->s.modelindex = item - bg_itemlist; // store item number in modelindex
00570     dropped->s.modelindex2 = 1; // This is non-zero is it's a dropped item
00571 
00572     dropped->classname = item->classname;
00573     dropped->item = item;
00574     VectorSet (dropped->r.mins, -ITEM_RADIUS, -ITEM_RADIUS, -ITEM_RADIUS);
00575     VectorSet (dropped->r.maxs, ITEM_RADIUS, ITEM_RADIUS, ITEM_RADIUS);
00576     dropped->r.contents = CONTENTS_TRIGGER;
00577 
00578     dropped->touch = Touch_Item;
00579 
00580     G_SetOrigin( dropped, origin );
00581     dropped->s.pos.trType = TR_GRAVITY;
00582     dropped->s.pos.trTime = level.time;
00583     VectorCopy( velocity, dropped->s.pos.trDelta );
00584 
00585     dropped->s.eFlags |= EF_BOUNCE_HALF;
00586 #ifdef MISSIONPACK
00587     if ((g_gametype.integer == GT_CTF || g_gametype.integer == GT_1FCTF)            && item->giType == IT_TEAM) { // Special case for CTF flags
00588 #else
00589     if (g_gametype.integer == GT_CTF && item->giType == IT_TEAM) { // Special case for CTF flags
00590 #endif
00591         dropped->think = Team_DroppedFlagThink;
00592         dropped->nextthink = level.time + 30000;
00593         Team_CheckDroppedItem( dropped );
00594     } else { // auto-remove after 30 seconds
00595         dropped->think = G_FreeEntity;
00596         dropped->nextthink = level.time + 30000;
00597     }
00598 
00599     dropped->flags = FL_DROPPED_ITEM;
00600 
00601     trap_LinkEntity (dropped);
00602 
00603     return dropped;
00604 }
00605 
00606 /*
00607 ================
00608 Drop_Item
00609 
00610 Spawns an item and tosses it forward
00611 ================
00612 */
00613 gentity_t *Drop_Item( gentity_t *ent, gitem_t *item, float angle ) {
00614     vec3_t  velocity;
00615     vec3_t  angles;
00616 
00617     VectorCopy( ent->s.apos.trBase, angles );
00618     angles[YAW] += angle;
00619     angles[PITCH] = 0;  // always forward
00620 
00621     AngleVectors( angles, velocity, NULL, NULL );
00622     VectorScale( velocity, 150, velocity );
00623     velocity[2] += 200 + crandom() * 50;
00624     
00625     return LaunchItem( item, ent->s.pos.trBase, velocity );
00626 }
00627 
00628 
00629 /*
00630 ================
00631 Use_Item
00632 
00633 Respawn the item
00634 ================
00635 */
00636 void Use_Item( gentity_t *ent, gentity_t *other, gentity_t *activator ) {
00637     RespawnItem( ent );
00638 }
00639 
00640 //======================================================================
00641 
00642 /*
00643 ================
00644 FinishSpawningItem
00645 
00646 Traces down to find where an item should rest, instead of letting them
00647 free fall from their spawn points
00648 ================
00649 */
00650 void FinishSpawningItem( gentity_t *ent ) {
00651     trace_t     tr;
00652     vec3_t      dest;
00653 
00654     VectorSet( ent->r.mins, -ITEM_RADIUS, -ITEM_RADIUS, -ITEM_RADIUS );
00655     VectorSet( ent->r.maxs, ITEM_RADIUS, ITEM_RADIUS, ITEM_RADIUS );
00656 
00657     ent->s.eType = ET_ITEM;
00658     ent->s.modelindex = ent->item - bg_itemlist;        // store item number in modelindex
00659     ent->s.modelindex2 = 0; // zero indicates this isn't a dropped item
00660 
00661     ent->r.contents = CONTENTS_TRIGGER;
00662     ent->touch = Touch_Item;
00663     // useing an item causes it to respawn
00664     ent->use = Use_Item;
00665 
00666     if ( ent->spawnflags & 1 ) {
00667         // suspended
00668         G_SetOrigin( ent, ent->s.origin );
00669     } else {
00670         // drop to floor
00671         VectorSet( dest, ent->s.origin[0], ent->s.origin[1], ent->s.origin[2] - 4096 );
00672         trap_Trace( &tr, ent->s.origin, ent->r.mins, ent->r.maxs, dest, ent->s.number, MASK_SOLID );
00673         if ( tr.startsolid ) {
00674             G_Printf ("FinishSpawningItem: %s startsolid at %s\n", ent->classname, vtos(ent->s.origin));
00675             G_FreeEntity( ent );
00676             return;
00677         }
00678 
00679         // allow to ride movers
00680         ent->s.groundEntityNum = tr.entityNum;
00681 
00682         G_SetOrigin( ent, tr.endpos );
00683     }
00684 
00685     // team slaves and targeted items aren't present at start
00686     if ( ( ent->flags & FL_TEAMSLAVE ) || ent->targetname ) {
00687         ent->s.eFlags |= EF_NODRAW;
00688         ent->r.contents = 0;
00689         return;
00690     }
00691 
00692     // powerups don't spawn in for a while
00693     if ( ent->item->giType == IT_POWERUP ) {
00694         float   respawn;
00695 
00696         respawn = 45 + crandom() * 15;
00697         ent->s.eFlags |= EF_NODRAW;
00698         ent->r.contents = 0;
00699         ent->nextthink = level.time + respawn * 1000;
00700         ent->think = RespawnItem;
00701         return;
00702     }
00703 
00704 
00705     trap_LinkEntity (ent);
00706 }
00707 
00708 
00709 qboolean    itemRegistered[MAX_ITEMS];
00710 
00711 /*
00712 ==================
00713 G_CheckTeamItems
00714 ==================
00715 */
00716 void G_CheckTeamItems( void ) {
00717 
00718     // Set up team stuff
00719     Team_InitGame();
00720 
00721     if( g_gametype.integer == GT_CTF ) {
00722         gitem_t *item;
00723 
00724         // check for the two flags
00725         item = BG_FindItem( "Red Flag" );
00726         if ( !item || !itemRegistered[ item - bg_itemlist ] ) {
00727             G_Printf( S_COLOR_YELLOW "WARNING: No team_CTF_redflag in map" );
00728         }
00729         item = BG_FindItem( "Blue Flag" );
00730         if ( !item || !itemRegistered[ item - bg_itemlist ] ) {
00731             G_Printf( S_COLOR_YELLOW "WARNING: No team_CTF_blueflag in map" );
00732         }
00733     }
00734 #ifdef MISSIONPACK
00735     if( g_gametype.integer == GT_1FCTF ) {
00736         gitem_t *item;
00737 
00738         // check for all three flags
00739         item = BG_FindItem( "Red Flag" );
00740         if ( !item || !itemRegistered[ item - bg_itemlist ] ) {
00741             G_Printf( S_COLOR_YELLOW "WARNING: No team_CTF_redflag in map" );
00742         }
00743         item = BG_FindItem( "Blue Flag" );
00744         if ( !item || !itemRegistered[ item - bg_itemlist ] ) {
00745             G_Printf( S_COLOR_YELLOW "WARNING: No team_CTF_blueflag in map" );
00746         }
00747         item = BG_FindItem( "Neutral Flag" );
00748         if ( !item || !itemRegistered[ item - bg_itemlist ] ) {
00749             G_Printf( S_COLOR_YELLOW "WARNING: No team_CTF_neutralflag in map" );
00750         }
00751     }
00752 
00753     if( g_gametype.integer == GT_OBELISK ) {
00754         gentity_t   *ent;
00755 
00756         // check for the two obelisks
00757         ent = NULL;
00758         ent = G_Find( ent, FOFS(classname), "team_redobelisk" );
00759         if( !ent ) {
00760             G_Printf( S_COLOR_YELLOW "WARNING: No team_redobelisk in map" );
00761         }
00762 
00763         ent = NULL;
00764         ent = G_Find( ent, FOFS(classname), "team_blueobelisk" );
00765         if( !ent ) {
00766             G_Printf( S_COLOR_YELLOW "WARNING: No team_blueobelisk in map" );
00767         }
00768     }
00769 
00770     if( g_gametype.integer == GT_HARVESTER ) {
00771         gentity_t   *ent;
00772 
00773         // check for all three obelisks
00774         ent = NULL;
00775         ent = G_Find( ent, FOFS(classname), "team_redobelisk" );
00776         if( !ent ) {
00777             G_Printf( S_COLOR_YELLOW "WARNING: No team_redobelisk in map" );
00778         }
00779 
00780         ent = NULL;
00781         ent = G_Find( ent, FOFS(classname), "team_blueobelisk" );
00782         if( !ent ) {
00783             G_Printf( S_COLOR_YELLOW "WARNING: No team_blueobelisk in map" );
00784         }
00785 
00786         ent = NULL;
00787         ent = G_Find( ent, FOFS(classname), "team_neutralobelisk" );
00788         if( !ent ) {
00789             G_Printf( S_COLOR_YELLOW "WARNING: No team_neutralobelisk in map" );
00790         }
00791     }
00792 #endif
00793 }
00794 
00795 /*
00796 ==============
00797 ClearRegisteredItems
00798 ==============
00799 */
00800 void ClearRegisteredItems( void ) {
00801     memset( itemRegistered, 0, sizeof( itemRegistered ) );
00802 
00803     // players always start with the base weapon
00804     RegisterItem( BG_FindItemForWeapon( WP_MACHINEGUN ) );
00805     RegisterItem( BG_FindItemForWeapon( WP_GAUNTLET ) );
00806 #ifdef MISSIONPACK
00807     if( g_gametype.integer == GT_HARVESTER ) {
00808         RegisterItem( BG_FindItem( "Red Cube" ) );
00809         RegisterItem( BG_FindItem( "Blue Cube" ) );
00810     }
00811 #endif
00812 }
00813 
00814 /*
00815 ===============
00816 RegisterItem
00817 
00818 The item will be added to the precache list
00819 ===============
00820 */
00821 void RegisterItem( gitem_t *item ) {
00822     if ( !item ) {
00823         G_Error( "RegisterItem: NULL" );
00824     }
00825     itemRegistered[ item - bg_itemlist ] = qtrue;
00826 }
00827 
00828 
00829 /*
00830 ===============
00831 SaveRegisteredItems
00832 
00833 Write the needed items to a config string
00834 so the client will know which ones to precache
00835 ===============
00836 */
00837 void SaveRegisteredItems( void ) {
00838     char    string[MAX_ITEMS+1];
00839     int     i;
00840     int     count;
00841 
00842     count = 0;
00843     for ( i = 0 ; i < bg_numItems ; i++ ) {
00844         if ( itemRegistered[i] ) {
00845             count++;
00846             string[i] = '1';
00847         } else {
00848             string[i] = '0';
00849         }
00850     }
00851     string[ bg_numItems ] = 0;
00852 
00853     G_Printf( "%i items registered\n", count );
00854     trap_SetConfigstring(CS_ITEMS, string);
00855 }
00856 
00857 /*
00858 ============
00859 G_ItemDisabled
00860 ============
00861 */
00862 int G_ItemDisabled( gitem_t *item ) {
00863 
00864     char name[128];
00865 
00866     Com_sprintf(name, sizeof(name), "disable_%s", item->classname);
00867     return trap_Cvar_VariableIntegerValue( name );
00868 }
00869 
00870 /*
00871 ============
00872 G_SpawnItem
00873 
00874 Sets the clipping size and plants the object on the floor.
00875 
00876 Items can't be immediately dropped to floor, because they might
00877 be on an entity that hasn't spawned yet.
00878 ============
00879 */
00880 void G_SpawnItem (gentity_t *ent, gitem_t *item) {
00881     G_SpawnFloat( "random", "0", &ent->random );
00882     G_SpawnFloat( "wait", "0", &ent->wait );
00883 
00884     RegisterItem( item );
00885     if ( G_ItemDisabled(item) )
00886         return;
00887 
00888     ent->item = item;
00889     // some movers spawn on the second frame, so delay item
00890     // spawns until the third frame so they can ride trains
00891     ent->nextthink = level.time + FRAMETIME * 2;
00892     ent->think = FinishSpawningItem;
00893 
00894     ent->physicsBounce = 0.50;      // items are bouncy
00895 
00896     if ( item->giType == IT_POWERUP ) {
00897         G_SoundIndex( "sound/items/poweruprespawn.wav" );
00898         G_SpawnFloat( "noglobalsound", "0", &ent->speed);
00899     }
00900 
00901 #ifdef MISSIONPACK
00902     if ( item->giType == IT_PERSISTANT_POWERUP ) {
00903         ent->s.generic1 = ent->spawnflags;
00904     }
00905 #endif
00906 }
00907 
00908 
00909 /*
00910 ================
00911 G_BounceItem
00912 
00913 ================
00914 */
00915 void G_BounceItem( gentity_t *ent, trace_t *trace ) {
00916     vec3_t  velocity;
00917     float   dot;
00918     int     hitTime;
00919 
00920     // reflect the velocity on the trace plane
00921     hitTime = level.previousTime + ( level.time - level.previousTime ) * trace->fraction;
00922     BG_EvaluateTrajectoryDelta( &ent->s.pos, hitTime, velocity );
00923     dot = DotProduct( velocity, trace->plane.normal );
00924     VectorMA( velocity, -2*dot, trace->plane.normal, ent->s.pos.trDelta );
00925 
00926     // cut the velocity to keep from bouncing forever
00927     VectorScale( ent->s.pos.trDelta, ent->physicsBounce, ent->s.pos.trDelta );
00928 
00929     // check for stop
00930     if ( trace->plane.normal[2] > 0 && ent->s.pos.trDelta[2] < 40 ) {
00931         trace->endpos[2] += 1.0;    // make sure it is off ground
00932         SnapVector( trace->endpos );
00933         G_SetOrigin( ent, trace->endpos );
00934         ent->s.groundEntityNum = trace->entityNum;
00935         return;
00936     }
00937 
00938     VectorAdd( ent->r.currentOrigin, trace->plane.normal, ent->r.currentOrigin);
00939     VectorCopy( ent->r.currentOrigin, ent->s.pos.trBase );
00940     ent->s.pos.trTime = level.time;
00941 }
00942 
00943 
00944 /*
00945 ================
00946 G_RunItem
00947 
00948 ================
00949 */
00950 void G_RunItem( gentity_t *ent ) {
00951     vec3_t      origin;
00952     trace_t     tr;
00953     int         contents;
00954     int         mask;
00955 
00956     // if groundentity has been set to -1, it may have been pushed off an edge
00957     if ( ent->s.groundEntityNum == -1 ) {
00958         if ( ent->s.pos.trType != TR_GRAVITY ) {
00959             ent->s.pos.trType = TR_GRAVITY;
00960             ent->s.pos.trTime = level.time;
00961         }
00962     }
00963 
00964     if ( ent->s.pos.trType == TR_STATIONARY ) {
00965         // check think function
00966         G_RunThink( ent );
00967         return;
00968     }
00969 
00970     // get current position
00971     BG_EvaluateTrajectory( &ent->s.pos, level.time, origin );
00972 
00973     // trace a line from the previous position to the current position
00974     if ( ent->clipmask ) {
00975         mask = ent->clipmask;
00976     } else {
00977         mask = MASK_PLAYERSOLID & ~CONTENTS_BODY;//MASK_SOLID;
00978     }
00979     trap_Trace( &tr, ent->r.currentOrigin, ent->r.mins, ent->r.maxs, origin, 
00980         ent->r.ownerNum, mask );
00981 
00982     VectorCopy( tr.endpos, ent->r.currentOrigin );
00983 
00984     if ( tr.startsolid ) {
00985         tr.fraction = 0;
00986     }
00987 
00988     trap_LinkEntity( ent ); // FIXME: avoid this for stationary?
00989 
00990     // check think function
00991     G_RunThink( ent );
00992 
00993     if ( tr.fraction == 1 ) {
00994         return;
00995     }
00996 
00997     // if it is in a nodrop volume, remove it
00998     contents = trap_PointContents( ent->r.currentOrigin, -1 );
00999     if ( contents & CONTENTS_NODROP ) {
01000         if (ent->item && ent->item->giType == IT_TEAM) {
01001             Team_FreeEntity(ent);
01002         } else {
01003             G_FreeEntity( ent );
01004         }
01005         return;
01006     }
01007 
01008     G_BounceItem( ent, &tr );
01009 }
01010 

Generated on Thu Aug 25 12:37:32 2005 for Quake III Arena by  doxygen 1.3.9.1