123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011 |
- /*
- ===========================================================================
- Copyright (C) 1999-2005 Id Software, Inc.
- This file is part of Quake III Arena source code.
- Quake III Arena source code is free software; you can redistribute it
- and/or modify it under the terms of the GNU General Public License as
- published by the Free Software Foundation; either version 2 of the License,
- or (at your option) any later version.
- Quake III Arena source code is distributed in the hope that it will be
- useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
- You should have received a copy of the GNU General Public License
- along with Foobar; if not, write to the Free Software
- Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- ===========================================================================
- */
- //
- #include "g_local.h"
- /*
- Items are any object that a player can touch to gain some effect.
- Pickup will return the number of seconds until they should respawn.
- all items should pop when dropped in lava or slime
- Respawnable items don't actually go away when picked up, they are
- just made invisible and untouchable. This allows them to ride
- movers and respawn apropriately.
- */
- #define RESPAWN_ARMOR 25
- #define RESPAWN_HEALTH 35
- #define RESPAWN_AMMO 40
- #define RESPAWN_HOLDABLE 60
- #define RESPAWN_MEGAHEALTH 35//120
- #define RESPAWN_POWERUP 120
- //======================================================================
- int Pickup_Powerup( gentity_t *ent, gentity_t *other ) {
- int quantity;
- int i;
- gclient_t *client;
- if ( !other->client->ps.powerups[ent->item->giTag] ) {
- // round timing to seconds to make multiple powerup timers
- // count in sync
- other->client->ps.powerups[ent->item->giTag] =
- level.time - ( level.time % 1000 );
- }
- if ( ent->count ) {
- quantity = ent->count;
- } else {
- quantity = ent->item->quantity;
- }
- other->client->ps.powerups[ent->item->giTag] += quantity * 1000;
- // give any nearby players a "denied" anti-reward
- for ( i = 0 ; i < level.maxclients ; i++ ) {
- vec3_t delta;
- float len;
- vec3_t forward;
- trace_t tr;
- client = &level.clients[i];
- if ( client == other->client ) {
- continue;
- }
- if ( client->pers.connected == CON_DISCONNECTED ) {
- continue;
- }
- if ( client->ps.stats[STAT_HEALTH] <= 0 ) {
- continue;
- }
- // if same team in team game, no sound
- // cannot use OnSameTeam as it expects to g_entities, not clients
- if ( g_gametype.integer >= GT_TEAM && other->client->sess.sessionTeam == client->sess.sessionTeam ) {
- continue;
- }
- // if too far away, no sound
- VectorSubtract( ent->s.pos.trBase, client->ps.origin, delta );
- len = VectorNormalize( delta );
- if ( len > 192 ) {
- continue;
- }
- // if not facing, no sound
- AngleVectors( client->ps.viewangles, forward, NULL, NULL );
- if ( DotProduct( delta, forward ) < 0.4 ) {
- continue;
- }
- // if not line of sight, no sound
- trap_Trace( &tr, client->ps.origin, NULL, NULL, ent->s.pos.trBase, ENTITYNUM_NONE, CONTENTS_SOLID );
- if ( tr.fraction != 1.0 ) {
- continue;
- }
- // anti-reward
- client->ps.persistant[PERS_PLAYEREVENTS] ^= PLAYEREVENT_DENIEDREWARD;
- }
- return RESPAWN_POWERUP;
- }
- //======================================================================
- #ifdef MISSIONPACK
- int Pickup_PersistantPowerup( gentity_t *ent, gentity_t *other ) {
- int clientNum;
- char userinfo[MAX_INFO_STRING];
- float handicap;
- int max;
- other->client->ps.stats[STAT_PERSISTANT_POWERUP] = ent->item - bg_itemlist;
- other->client->persistantPowerup = ent;
- switch( ent->item->giTag ) {
- case PW_GUARD:
- clientNum = other->client->ps.clientNum;
- trap_GetUserinfo( clientNum, userinfo, sizeof(userinfo) );
- handicap = atof( Info_ValueForKey( userinfo, "handicap" ) );
- if( handicap<=0.0f || handicap>100.0f) {
- handicap = 100.0f;
- }
- max = (int)(2 * handicap);
- other->health = max;
- other->client->ps.stats[STAT_HEALTH] = max;
- other->client->ps.stats[STAT_MAX_HEALTH] = max;
- other->client->ps.stats[STAT_ARMOR] = max;
- other->client->pers.maxHealth = max;
- break;
- case PW_SCOUT:
- clientNum = other->client->ps.clientNum;
- trap_GetUserinfo( clientNum, userinfo, sizeof(userinfo) );
- handicap = atof( Info_ValueForKey( userinfo, "handicap" ) );
- if( handicap<=0.0f || handicap>100.0f) {
- handicap = 100.0f;
- }
- other->client->pers.maxHealth = handicap;
- other->client->ps.stats[STAT_ARMOR] = 0;
- break;
- case PW_DOUBLER:
- clientNum = other->client->ps.clientNum;
- trap_GetUserinfo( clientNum, userinfo, sizeof(userinfo) );
- handicap = atof( Info_ValueForKey( userinfo, "handicap" ) );
- if( handicap<=0.0f || handicap>100.0f) {
- handicap = 100.0f;
- }
- other->client->pers.maxHealth = handicap;
- break;
- case PW_AMMOREGEN:
- clientNum = other->client->ps.clientNum;
- trap_GetUserinfo( clientNum, userinfo, sizeof(userinfo) );
- handicap = atof( Info_ValueForKey( userinfo, "handicap" ) );
- if( handicap<=0.0f || handicap>100.0f) {
- handicap = 100.0f;
- }
- other->client->pers.maxHealth = handicap;
- memset(other->client->ammoTimes, 0, sizeof(other->client->ammoTimes));
- break;
- default:
- clientNum = other->client->ps.clientNum;
- trap_GetUserinfo( clientNum, userinfo, sizeof(userinfo) );
- handicap = atof( Info_ValueForKey( userinfo, "handicap" ) );
- if( handicap<=0.0f || handicap>100.0f) {
- handicap = 100.0f;
- }
- other->client->pers.maxHealth = handicap;
- break;
- }
- return -1;
- }
- //======================================================================
- #endif
- int Pickup_Holdable( gentity_t *ent, gentity_t *other ) {
- other->client->ps.stats[STAT_HOLDABLE_ITEM] = ent->item - bg_itemlist;
- if( ent->item->giTag == HI_KAMIKAZE ) {
- other->client->ps.eFlags |= EF_KAMIKAZE;
- }
- return RESPAWN_HOLDABLE;
- }
- //======================================================================
- void Add_Ammo (gentity_t *ent, int weapon, int count)
- {
- ent->client->ps.ammo[weapon] += count;
- if ( ent->client->ps.ammo[weapon] > 200 ) {
- ent->client->ps.ammo[weapon] = 200;
- }
- }
- int Pickup_Ammo (gentity_t *ent, gentity_t *other)
- {
- int quantity;
- if ( ent->count ) {
- quantity = ent->count;
- } else {
- quantity = ent->item->quantity;
- }
- Add_Ammo (other, ent->item->giTag, quantity);
- return RESPAWN_AMMO;
- }
- //======================================================================
- int Pickup_Weapon (gentity_t *ent, gentity_t *other) {
- int quantity;
- if ( ent->count < 0 ) {
- quantity = 0; // None for you, sir!
- } else {
- if ( ent->count ) {
- quantity = ent->count;
- } else {
- quantity = ent->item->quantity;
- }
- // dropped items and teamplay weapons always have full ammo
- if ( ! (ent->flags & FL_DROPPED_ITEM) && g_gametype.integer != GT_TEAM ) {
- // respawning rules
- // drop the quantity if the already have over the minimum
- if ( other->client->ps.ammo[ ent->item->giTag ] < quantity ) {
- quantity = quantity - other->client->ps.ammo[ ent->item->giTag ];
- } else {
- quantity = 1; // only add a single shot
- }
- }
- }
- // add the weapon
- other->client->ps.stats[STAT_WEAPONS] |= ( 1 << ent->item->giTag );
- Add_Ammo( other, ent->item->giTag, quantity );
- if (ent->item->giTag == WP_GRAPPLING_HOOK)
- other->client->ps.ammo[ent->item->giTag] = -1; // unlimited ammo
- // team deathmatch has slow weapon respawns
- if ( g_gametype.integer == GT_TEAM ) {
- return g_weaponTeamRespawn.integer;
- }
- return g_weaponRespawn.integer;
- }
- //======================================================================
- int Pickup_Health (gentity_t *ent, gentity_t *other) {
- int max;
- int quantity;
- // small and mega healths will go over the max
- #ifdef MISSIONPACK
- if( other->client && bg_itemlist[other->client->ps.stats[STAT_PERSISTANT_POWERUP]].giTag == PW_GUARD ) {
- max = other->client->ps.stats[STAT_MAX_HEALTH];
- }
- else
- #endif
- if ( ent->item->quantity != 5 && ent->item->quantity != 100 ) {
- max = other->client->ps.stats[STAT_MAX_HEALTH];
- } else {
- max = other->client->ps.stats[STAT_MAX_HEALTH] * 2;
- }
- if ( ent->count ) {
- quantity = ent->count;
- } else {
- quantity = ent->item->quantity;
- }
- other->health += quantity;
- if (other->health > max ) {
- other->health = max;
- }
- other->client->ps.stats[STAT_HEALTH] = other->health;
- if ( ent->item->quantity == 100 ) { // mega health respawns slow
- return RESPAWN_MEGAHEALTH;
- }
- return RESPAWN_HEALTH;
- }
- //======================================================================
- int Pickup_Armor( gentity_t *ent, gentity_t *other ) {
- #ifdef MISSIONPACK
- int upperBound;
- other->client->ps.stats[STAT_ARMOR] += ent->item->quantity;
- if( other->client && bg_itemlist[other->client->ps.stats[STAT_PERSISTANT_POWERUP]].giTag == PW_GUARD ) {
- upperBound = other->client->ps.stats[STAT_MAX_HEALTH];
- }
- else {
- upperBound = other->client->ps.stats[STAT_MAX_HEALTH] * 2;
- }
- if ( other->client->ps.stats[STAT_ARMOR] > upperBound ) {
- other->client->ps.stats[STAT_ARMOR] = upperBound;
- }
- #else
- other->client->ps.stats[STAT_ARMOR] += ent->item->quantity;
- if ( other->client->ps.stats[STAT_ARMOR] > other->client->ps.stats[STAT_MAX_HEALTH] * 2 ) {
- other->client->ps.stats[STAT_ARMOR] = other->client->ps.stats[STAT_MAX_HEALTH] * 2;
- }
- #endif
- return RESPAWN_ARMOR;
- }
- //======================================================================
- /*
- ===============
- RespawnItem
- ===============
- */
- void RespawnItem( gentity_t *ent ) {
- // randomly select from teamed entities
- if (ent->team) {
- gentity_t *master;
- int count;
- int choice;
- if ( !ent->teammaster ) {
- G_Error( "RespawnItem: bad teammaster");
- }
- master = ent->teammaster;
- for (count = 0, ent = master; ent; ent = ent->teamchain, count++)
- ;
- choice = rand() % count;
- for (count = 0, ent = master; count < choice; ent = ent->teamchain, count++)
- ;
- }
- ent->r.contents = CONTENTS_TRIGGER;
- ent->s.eFlags &= ~EF_NODRAW;
- ent->r.svFlags &= ~SVF_NOCLIENT;
- trap_LinkEntity (ent);
- if ( ent->item->giType == IT_POWERUP ) {
- // play powerup spawn sound to all clients
- gentity_t *te;
- // if the powerup respawn sound should Not be global
- if (ent->speed) {
- te = G_TempEntity( ent->s.pos.trBase, EV_GENERAL_SOUND );
- }
- else {
- te = G_TempEntity( ent->s.pos.trBase, EV_GLOBAL_SOUND );
- }
- te->s.eventParm = G_SoundIndex( "sound/items/poweruprespawn.wav" );
- te->r.svFlags |= SVF_BROADCAST;
- }
- if ( ent->item->giType == IT_HOLDABLE && ent->item->giTag == HI_KAMIKAZE ) {
- // play powerup spawn sound to all clients
- gentity_t *te;
- // if the powerup respawn sound should Not be global
- if (ent->speed) {
- te = G_TempEntity( ent->s.pos.trBase, EV_GENERAL_SOUND );
- }
- else {
- te = G_TempEntity( ent->s.pos.trBase, EV_GLOBAL_SOUND );
- }
- te->s.eventParm = G_SoundIndex( "sound/items/kamikazerespawn.wav" );
- te->r.svFlags |= SVF_BROADCAST;
- }
- // play the normal respawn sound only to nearby clients
- G_AddEvent( ent, EV_ITEM_RESPAWN, 0 );
- ent->nextthink = 0;
- }
- /*
- ===============
- Touch_Item
- ===============
- */
- void Touch_Item (gentity_t *ent, gentity_t *other, trace_t *trace) {
- int respawn;
- qboolean predict;
- if (!other->client)
- return;
- if (other->health < 1)
- return; // dead people can't pickup
- // the same pickup rules are used for client side and server side
- if ( !BG_CanItemBeGrabbed( g_gametype.integer, &ent->s, &other->client->ps ) ) {
- return;
- }
- G_LogPrintf( "Item: %i %s\n", other->s.number, ent->item->classname );
- predict = other->client->pers.predictItemPickup;
- // call the item-specific pickup function
- switch( ent->item->giType ) {
- case IT_WEAPON:
- respawn = Pickup_Weapon(ent, other);
- // predict = qfalse;
- break;
- case IT_AMMO:
- respawn = Pickup_Ammo(ent, other);
- // predict = qfalse;
- break;
- case IT_ARMOR:
- respawn = Pickup_Armor(ent, other);
- break;
- case IT_HEALTH:
- respawn = Pickup_Health(ent, other);
- break;
- case IT_POWERUP:
- respawn = Pickup_Powerup(ent, other);
- predict = qfalse;
- break;
- #ifdef MISSIONPACK
- case IT_PERSISTANT_POWERUP:
- respawn = Pickup_PersistantPowerup(ent, other);
- break;
- #endif
- case IT_TEAM:
- respawn = Pickup_Team(ent, other);
- break;
- case IT_HOLDABLE:
- respawn = Pickup_Holdable(ent, other);
- break;
- default:
- return;
- }
- if ( !respawn ) {
- return;
- }
- // play the normal pickup sound
- if (predict) {
- G_AddPredictableEvent( other, EV_ITEM_PICKUP, ent->s.modelindex );
- } else {
- G_AddEvent( other, EV_ITEM_PICKUP, ent->s.modelindex );
- }
- // powerup pickups are global broadcasts
- if ( ent->item->giType == IT_POWERUP || ent->item->giType == IT_TEAM) {
- // if we want the global sound to play
- if (!ent->speed) {
- gentity_t *te;
- te = G_TempEntity( ent->s.pos.trBase, EV_GLOBAL_ITEM_PICKUP );
- te->s.eventParm = ent->s.modelindex;
- te->r.svFlags |= SVF_BROADCAST;
- } else {
- gentity_t *te;
- te = G_TempEntity( ent->s.pos.trBase, EV_GLOBAL_ITEM_PICKUP );
- te->s.eventParm = ent->s.modelindex;
- // only send this temp entity to a single client
- te->r.svFlags |= SVF_SINGLECLIENT;
- te->r.singleClient = other->s.number;
- }
- }
- // fire item targets
- G_UseTargets (ent, other);
- // wait of -1 will not respawn
- if ( ent->wait == -1 ) {
- ent->r.svFlags |= SVF_NOCLIENT;
- ent->s.eFlags |= EF_NODRAW;
- ent->r.contents = 0;
- ent->unlinkAfterEvent = qtrue;
- return;
- }
- // non zero wait overrides respawn time
- if ( ent->wait ) {
- respawn = ent->wait;
- }
- // random can be used to vary the respawn time
- if ( ent->random ) {
- respawn += crandom() * ent->random;
- if ( respawn < 1 ) {
- respawn = 1;
- }
- }
- // dropped items will not respawn
- if ( ent->flags & FL_DROPPED_ITEM ) {
- ent->freeAfterEvent = qtrue;
- }
- // picked up items still stay around, they just don't
- // draw anything. This allows respawnable items
- // to be placed on movers.
- ent->r.svFlags |= SVF_NOCLIENT;
- ent->s.eFlags |= EF_NODRAW;
- ent->r.contents = 0;
- // ZOID
- // A negative respawn times means to never respawn this item (but don't
- // delete it). This is used by items that are respawned by third party
- // events such as ctf flags
- if ( respawn <= 0 ) {
- ent->nextthink = 0;
- ent->think = 0;
- } else {
- ent->nextthink = level.time + respawn * 1000;
- ent->think = RespawnItem;
- }
- trap_LinkEntity( ent );
- }
- //======================================================================
- /*
- ================
- LaunchItem
- Spawns an item and tosses it forward
- ================
- */
- gentity_t *LaunchItem( gitem_t *item, vec3_t origin, vec3_t velocity ) {
- gentity_t *dropped;
- dropped = G_Spawn();
- dropped->s.eType = ET_ITEM;
- dropped->s.modelindex = item - bg_itemlist; // store item number in modelindex
- dropped->s.modelindex2 = 1; // This is non-zero is it's a dropped item
- dropped->classname = item->classname;
- dropped->item = item;
- VectorSet (dropped->r.mins, -ITEM_RADIUS, -ITEM_RADIUS, -ITEM_RADIUS);
- VectorSet (dropped->r.maxs, ITEM_RADIUS, ITEM_RADIUS, ITEM_RADIUS);
- dropped->r.contents = CONTENTS_TRIGGER;
- dropped->touch = Touch_Item;
- G_SetOrigin( dropped, origin );
- dropped->s.pos.trType = TR_GRAVITY;
- dropped->s.pos.trTime = level.time;
- VectorCopy( velocity, dropped->s.pos.trDelta );
- dropped->s.eFlags |= EF_BOUNCE_HALF;
- #ifdef MISSIONPACK
- if ((g_gametype.integer == GT_CTF || g_gametype.integer == GT_1FCTF) && item->giType == IT_TEAM) { // Special case for CTF flags
- #else
- if (g_gametype.integer == GT_CTF && item->giType == IT_TEAM) { // Special case for CTF flags
- #endif
- dropped->think = Team_DroppedFlagThink;
- dropped->nextthink = level.time + 30000;
- Team_CheckDroppedItem( dropped );
- } else { // auto-remove after 30 seconds
- dropped->think = G_FreeEntity;
- dropped->nextthink = level.time + 30000;
- }
- dropped->flags = FL_DROPPED_ITEM;
- trap_LinkEntity (dropped);
- return dropped;
- }
- /*
- ================
- Drop_Item
- Spawns an item and tosses it forward
- ================
- */
- gentity_t *Drop_Item( gentity_t *ent, gitem_t *item, float angle ) {
- vec3_t velocity;
- vec3_t angles;
- VectorCopy( ent->s.apos.trBase, angles );
- angles[YAW] += angle;
- angles[PITCH] = 0; // always forward
- AngleVectors( angles, velocity, NULL, NULL );
- VectorScale( velocity, 150, velocity );
- velocity[2] += 200 + crandom() * 50;
-
- return LaunchItem( item, ent->s.pos.trBase, velocity );
- }
- /*
- ================
- Use_Item
- Respawn the item
- ================
- */
- void Use_Item( gentity_t *ent, gentity_t *other, gentity_t *activator ) {
- RespawnItem( ent );
- }
- //======================================================================
- /*
- ================
- FinishSpawningItem
- Traces down to find where an item should rest, instead of letting them
- free fall from their spawn points
- ================
- */
- void FinishSpawningItem( gentity_t *ent ) {
- trace_t tr;
- vec3_t dest;
- VectorSet( ent->r.mins, -ITEM_RADIUS, -ITEM_RADIUS, -ITEM_RADIUS );
- VectorSet( ent->r.maxs, ITEM_RADIUS, ITEM_RADIUS, ITEM_RADIUS );
- ent->s.eType = ET_ITEM;
- ent->s.modelindex = ent->item - bg_itemlist; // store item number in modelindex
- ent->s.modelindex2 = 0; // zero indicates this isn't a dropped item
- ent->r.contents = CONTENTS_TRIGGER;
- ent->touch = Touch_Item;
- // useing an item causes it to respawn
- ent->use = Use_Item;
- if ( ent->spawnflags & 1 ) {
- // suspended
- G_SetOrigin( ent, ent->s.origin );
- } else {
- // drop to floor
- VectorSet( dest, ent->s.origin[0], ent->s.origin[1], ent->s.origin[2] - 4096 );
- trap_Trace( &tr, ent->s.origin, ent->r.mins, ent->r.maxs, dest, ent->s.number, MASK_SOLID );
- if ( tr.startsolid ) {
- G_Printf ("FinishSpawningItem: %s startsolid at %s\n", ent->classname, vtos(ent->s.origin));
- G_FreeEntity( ent );
- return;
- }
- // allow to ride movers
- ent->s.groundEntityNum = tr.entityNum;
- G_SetOrigin( ent, tr.endpos );
- }
- // team slaves and targeted items aren't present at start
- if ( ( ent->flags & FL_TEAMSLAVE ) || ent->targetname ) {
- ent->s.eFlags |= EF_NODRAW;
- ent->r.contents = 0;
- return;
- }
- // powerups don't spawn in for a while
- if ( ent->item->giType == IT_POWERUP ) {
- float respawn;
- respawn = 45 + crandom() * 15;
- ent->s.eFlags |= EF_NODRAW;
- ent->r.contents = 0;
- ent->nextthink = level.time + respawn * 1000;
- ent->think = RespawnItem;
- return;
- }
- trap_LinkEntity (ent);
- }
- qboolean itemRegistered[MAX_ITEMS];
- /*
- ==================
- G_CheckTeamItems
- ==================
- */
- void G_CheckTeamItems( void ) {
- // Set up team stuff
- Team_InitGame();
- if( g_gametype.integer == GT_CTF ) {
- gitem_t *item;
- // check for the two flags
- item = BG_FindItem( "Red Flag" );
- if ( !item || !itemRegistered[ item - bg_itemlist ] ) {
- G_Printf( S_COLOR_YELLOW "WARNING: No team_CTF_redflag in map" );
- }
- item = BG_FindItem( "Blue Flag" );
- if ( !item || !itemRegistered[ item - bg_itemlist ] ) {
- G_Printf( S_COLOR_YELLOW "WARNING: No team_CTF_blueflag in map" );
- }
- }
- #ifdef MISSIONPACK
- if( g_gametype.integer == GT_1FCTF ) {
- gitem_t *item;
- // check for all three flags
- item = BG_FindItem( "Red Flag" );
- if ( !item || !itemRegistered[ item - bg_itemlist ] ) {
- G_Printf( S_COLOR_YELLOW "WARNING: No team_CTF_redflag in map" );
- }
- item = BG_FindItem( "Blue Flag" );
- if ( !item || !itemRegistered[ item - bg_itemlist ] ) {
- G_Printf( S_COLOR_YELLOW "WARNING: No team_CTF_blueflag in map" );
- }
- item = BG_FindItem( "Neutral Flag" );
- if ( !item || !itemRegistered[ item - bg_itemlist ] ) {
- G_Printf( S_COLOR_YELLOW "WARNING: No team_CTF_neutralflag in map" );
- }
- }
- if( g_gametype.integer == GT_OBELISK ) {
- gentity_t *ent;
- // check for the two obelisks
- ent = NULL;
- ent = G_Find( ent, FOFS(classname), "team_redobelisk" );
- if( !ent ) {
- G_Printf( S_COLOR_YELLOW "WARNING: No team_redobelisk in map" );
- }
- ent = NULL;
- ent = G_Find( ent, FOFS(classname), "team_blueobelisk" );
- if( !ent ) {
- G_Printf( S_COLOR_YELLOW "WARNING: No team_blueobelisk in map" );
- }
- }
- if( g_gametype.integer == GT_HARVESTER ) {
- gentity_t *ent;
- // check for all three obelisks
- ent = NULL;
- ent = G_Find( ent, FOFS(classname), "team_redobelisk" );
- if( !ent ) {
- G_Printf( S_COLOR_YELLOW "WARNING: No team_redobelisk in map" );
- }
- ent = NULL;
- ent = G_Find( ent, FOFS(classname), "team_blueobelisk" );
- if( !ent ) {
- G_Printf( S_COLOR_YELLOW "WARNING: No team_blueobelisk in map" );
- }
- ent = NULL;
- ent = G_Find( ent, FOFS(classname), "team_neutralobelisk" );
- if( !ent ) {
- G_Printf( S_COLOR_YELLOW "WARNING: No team_neutralobelisk in map" );
- }
- }
- #endif
- }
- /*
- ==============
- ClearRegisteredItems
- ==============
- */
- void ClearRegisteredItems( void ) {
- memset( itemRegistered, 0, sizeof( itemRegistered ) );
- // players always start with the base weapon
- RegisterItem( BG_FindItemForWeapon( WP_MACHINEGUN ) );
- RegisterItem( BG_FindItemForWeapon( WP_GAUNTLET ) );
- #ifdef MISSIONPACK
- if( g_gametype.integer == GT_HARVESTER ) {
- RegisterItem( BG_FindItem( "Red Cube" ) );
- RegisterItem( BG_FindItem( "Blue Cube" ) );
- }
- #endif
- }
- /*
- ===============
- RegisterItem
- The item will be added to the precache list
- ===============
- */
- void RegisterItem( gitem_t *item ) {
- if ( !item ) {
- G_Error( "RegisterItem: NULL" );
- }
- itemRegistered[ item - bg_itemlist ] = qtrue;
- }
- /*
- ===============
- SaveRegisteredItems
- Write the needed items to a config string
- so the client will know which ones to precache
- ===============
- */
- void SaveRegisteredItems( void ) {
- char string[MAX_ITEMS+1];
- int i;
- int count;
- count = 0;
- for ( i = 0 ; i < bg_numItems ; i++ ) {
- if ( itemRegistered[i] ) {
- count++;
- string[i] = '1';
- } else {
- string[i] = '0';
- }
- }
- string[ bg_numItems ] = 0;
- G_Printf( "%i items registered\n", count );
- trap_SetConfigstring(CS_ITEMS, string);
- }
- /*
- ============
- G_ItemDisabled
- ============
- */
- int G_ItemDisabled( gitem_t *item ) {
- char name[128];
- Com_sprintf(name, sizeof(name), "disable_%s", item->classname);
- return trap_Cvar_VariableIntegerValue( name );
- }
- /*
- ============
- G_SpawnItem
- Sets the clipping size and plants the object on the floor.
- Items can't be immediately dropped to floor, because they might
- be on an entity that hasn't spawned yet.
- ============
- */
- void G_SpawnItem (gentity_t *ent, gitem_t *item) {
- G_SpawnFloat( "random", "0", &ent->random );
- G_SpawnFloat( "wait", "0", &ent->wait );
- RegisterItem( item );
- if ( G_ItemDisabled(item) )
- return;
- ent->item = item;
- // some movers spawn on the second frame, so delay item
- // spawns until the third frame so they can ride trains
- ent->nextthink = level.time + FRAMETIME * 2;
- ent->think = FinishSpawningItem;
- ent->physicsBounce = 0.50; // items are bouncy
- if ( item->giType == IT_POWERUP ) {
- G_SoundIndex( "sound/items/poweruprespawn.wav" );
- G_SpawnFloat( "noglobalsound", "0", &ent->speed);
- }
- #ifdef MISSIONPACK
- if ( item->giType == IT_PERSISTANT_POWERUP ) {
- ent->s.generic1 = ent->spawnflags;
- }
- #endif
- }
- /*
- ================
- G_BounceItem
- ================
- */
- void G_BounceItem( gentity_t *ent, trace_t *trace ) {
- vec3_t velocity;
- float dot;
- int hitTime;
- // reflect the velocity on the trace plane
- hitTime = level.previousTime + ( level.time - level.previousTime ) * trace->fraction;
- BG_EvaluateTrajectoryDelta( &ent->s.pos, hitTime, velocity );
- dot = DotProduct( velocity, trace->plane.normal );
- VectorMA( velocity, -2*dot, trace->plane.normal, ent->s.pos.trDelta );
- // cut the velocity to keep from bouncing forever
- VectorScale( ent->s.pos.trDelta, ent->physicsBounce, ent->s.pos.trDelta );
- // check for stop
- if ( trace->plane.normal[2] > 0 && ent->s.pos.trDelta[2] < 40 ) {
- trace->endpos[2] += 1.0; // make sure it is off ground
- SnapVector( trace->endpos );
- G_SetOrigin( ent, trace->endpos );
- ent->s.groundEntityNum = trace->entityNum;
- return;
- }
- VectorAdd( ent->r.currentOrigin, trace->plane.normal, ent->r.currentOrigin);
- VectorCopy( ent->r.currentOrigin, ent->s.pos.trBase );
- ent->s.pos.trTime = level.time;
- }
- /*
- ================
- G_RunItem
- ================
- */
- void G_RunItem( gentity_t *ent ) {
- vec3_t origin;
- trace_t tr;
- int contents;
- int mask;
- // if groundentity has been set to -1, it may have been pushed off an edge
- if ( ent->s.groundEntityNum == -1 ) {
- if ( ent->s.pos.trType != TR_GRAVITY ) {
- ent->s.pos.trType = TR_GRAVITY;
- ent->s.pos.trTime = level.time;
- }
- }
- if ( ent->s.pos.trType == TR_STATIONARY ) {
- // check think function
- G_RunThink( ent );
- return;
- }
- // get current position
- BG_EvaluateTrajectory( &ent->s.pos, level.time, origin );
- // trace a line from the previous position to the current position
- if ( ent->clipmask ) {
- mask = ent->clipmask;
- } else {
- mask = MASK_PLAYERSOLID & ~CONTENTS_BODY;//MASK_SOLID;
- }
- trap_Trace( &tr, ent->r.currentOrigin, ent->r.mins, ent->r.maxs, origin,
- ent->r.ownerNum, mask );
- VectorCopy( tr.endpos, ent->r.currentOrigin );
- if ( tr.startsolid ) {
- tr.fraction = 0;
- }
- trap_LinkEntity( ent ); // FIXME: avoid this for stationary?
- // check think function
- G_RunThink( ent );
- if ( tr.fraction == 1 ) {
- return;
- }
- // if it is in a nodrop volume, remove it
- contents = trap_PointContents( ent->r.currentOrigin, -1 );
- if ( contents & CONTENTS_NODROP ) {
- if (ent->item && ent->item->giType == IT_TEAM) {
- Team_FreeEntity(ent);
- } else {
- G_FreeEntity( ent );
- }
- return;
- }
- G_BounceItem( ent, &tr );
- }
|