// Copyright (C) 1999-2000 Id Software, Inc.
//
#include "g_local.h"
// Alliance
#include "bg_battlebots.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->armor = 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->ps.stats[STAT_MAX_ARMOR] = max;
        other->client->pers.maxHealth = max;
        other->client->pers.maxArmor = 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);

    //BattleBots
    if (ent->item->giTag == WP_GRENADE_LAUNCHER) {
        Add_Ammo (other, WP_HANDGRENADE, 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 );

    //BattleBots
    if (ent->item->giTag == WP_GRENADE_LAUNCHER) {

        Add_Ammo( other, WP_HANDGRENADE, 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_Weapon (gentity_t *ent, gentity_t *other) {
    int		quantity;
 
    if ( ent->count < 0 ) {
        quantity = 0; // None for you, sir!
    }
    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
            }
        }
 
        // Hugo - added - if you don't already have the weapon, you get default ammo
 
        if (!(other->client->ps.stats[STAT_WEAPONS] & ( 1 << ent->item->giTag )))
            quantity = ent->item->quantity;
		
 
    }
 
    // add the weapon
    other->client->ps.stats[STAT_WEAPONS] |= ( 1 << ent->item->giTag );
 
    Add_Ammo( other, ent->item->giTag, quantity );
 
    // Alliance: grapple isn't a weapon
    
    	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;
}
 
*/

// Alliance
int Pickup_Grapple (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

    /*
       // Joe - make my BG_grappleEnabled() easy
       if (g_grappleGlobal.integer != 2) {
           //            G_Printf(S_COLOR_RED "gave him grapple\n");
           other->client->ps.stats[STAT_GRAPPLE] = 1;
       }
    */

    other->client->ps.stats[STAT_WEAPONS] |= ( 1 << ent->item->giTag );

    //Add_Ammo( other, ent->item->giTag, quantity );

    // Alliance: grapple isn't a weapon ---INCIN readded
    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 == 100 ) {
            max = other->client->ps.stats[STAT_MAX_HEALTH] * 2;
        } else {
            max = other->client->ps.stats[STAT_MAX_HEALTH] * 1.5;
        }

    //if ( ent->count ) {
    //quantity = ent->count;
    //} else {
    quantity = ent->item->quantity;
    //}

    other->health += quantity;

    //INCIN -- allow the pickup.. since we have a steady drain of -- 1 on health now

    if (other->health > 200 ) {
        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 ) {
    int			max;
    int			quantity;


#ifdef MISSIONPACK

    if( other->client && bg_itemlist[other->client->ps.stats[STAT_PERSISTANT_POWERUP]].giTag == PW_GUARD ) {
        max = other->client->ps.stats[STAT_MAX_ARMOR];
    } else
#endif

        if ( ent->item->quantity == 100 ) {
            max = other->client->ps.stats[STAT_MAX_HEALTH] * 2;
        } else {
            max = other->client->ps.stats[STAT_MAX_HEALTH] * 1.5;
        }

    //if ( ent->count ) {
    //quantity = ent->count;
    //} else {
    quantity = ent->item->quantity;
    //}

    other->armor += quantity;

    //INCIN -- allow the pickup.. since we have a steady drain of --1 on health now

    if (other->armor > max ) {
        other->armor = max;
        other->client->ps.stats[STAT_ARMOR] = other->armor;
    }

    if(other->armor == 50 && other->client->ps.stats[STAT_ARMOR] < 150 ) {
        other->armor = max;
        other->client->ps.stats[STAT_ARMOR] = other->armor;
    } else if(other->armor == 50 && other->client->ps.stats[STAT_ARMOR] > 150) {
        other->client->ps.stats[STAT_ARMOR] += 50;
        if(other->client->ps.stats[STAT_ARMOR] > max) {
            other->client->ps.stats[STAT_ARMOR] = max;
        }
    } else {
        other->client->ps.stats[STAT_ARMOR] = other->armor;
        if(other->client->ps.stats[STAT_ARMOR] > max) {
            other->client->ps.stats[STAT_ARMOR] = max;
        }
    }

    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_PERSISTANT_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=0;
    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 );
    //BG_Printf( "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);

        // alliance Joe health regen
        if (ent->flags & FL_DROPPED_ITEM) {
            CombatPickupDroppedWeapon(ent, other);
        }
        // end alliance

        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;

    case IT_PERSISTANT_POWERUP:
#ifdef MISSIONPACK

        respawn = Pickup_PersistantPowerup(ent, other);
#else
		if (g_battlePowerups.integer) {
			Pickup_Rune(ent, other, trace);
			G_AddEvent( other, EV_ITEM_PICKUP, ent->s.modelindex );
        } else
            respawn = Pickup_Powerup(ent, other);
        predict = qfalse;
#endif

        break;

    case IT_TEAM:
        respawn = Pickup_Team(ent, other);
        break;
    case IT_HOLDABLE:
        respawn = Pickup_Holdable(ent, other);
        break;
        // Alliance: grapple isn't a weapon
		//grapples are never dropped or spawned in maps
    case IT_GRAPPLE:
        //respawn = Pickup_Grapple(ent, other);
        break;

    default:
        return;
    }
    //incin --- local variable 'respawn' may be used without having been initialized
    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_PERSISTANT_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 ) {
        // Hugo
        ent->r.svFlags |= SVF_NOCLIENT;
        ent->s.eFlags |= EF_NODRAW;
        ent->r.contents = 0;
        //
        ent->freeAfterEvent = qtrue;
    }

    // Alliance: if weapons stay is on, don't mess with the item at all -
    // don't mod its flags/contents, change its think, etc.
    // Also allow Zoid's respawn<=0 case into this conditional
    if (!weaponsStayEnabled() || respawn <= 0 || ent->item->giType != IT_WEAPON) {

        // 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;
	
	//INCIN -- test for flag pull with lightning gun -- Really works
	/*
	if(	Q_stricmp(dropped->classname,"team_CTF_blueflag") == 0 ||
		Q_stricmp(dropped->classname,"team_CTF_redflag") == 0 ||
		Q_stricmp(dropped->classname,"team_CTF_neutralflag") == 0)
	{
		
		dropped->r.contents |= CONTENTS_TRIGGER | CONTENTS_SOLID;
		dropped->clipmask = MASK_SHOT;
							//MASK_SHOT;(works but not well)
							//MASK_PLAYERSOLID(works); 
							//MASK_ALL ; 
							//MASK_ALL(works);
		dropped->takedamage = qtrue;
		dropped->health = INT_MAX;
		//dropped->physicsBounce = 0.50;
		dropped->nextthink = level.time + 1;
        //end test 
	}
	else
	{
		dropped->r.contents = CONTENTS_TRIGGER;
	}
	*/
	dropped->r.contents = CONTENTS_TRIGGER;
    VectorSet (dropped->r.mins, -ITEM_RADIUS, -ITEM_RADIUS, -ITEM_RADIUS);
    VectorSet (dropped->r.maxs, ITEM_RADIUS, ITEM_RADIUS, ITEM_RADIUS);
    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;

        if (g_gametype.integer == GT_CTF)
            dropped->nextthink = level.time + 120000;

        if (g_gametype.integer == GT_RTF)
            dropped->nextthink = level.time + 300000;

        Team_CheckDroppedItem( dropped );

    } else { // auto-remove after 30 seconds

        //BB DROP CODE
        //dropped->think = G_FreeEntity;
        //dropped->nextthink = level.time + 30000;
        // alliance - Hugo - Runes are never freed and move every 20 seconds
#ifdef MISSIONPACK
		//add code for handling ta powerups
        //if ( ( item->giType == IT_POWERUP ) ) {
			//dropped->touch = Pickup_Rune;
			//dropped->takedamage = qtrue;
			//dropped->health = INT_MAX;
			//dropped->physicsBounce = 0.50;		// runes are bouncy
			//dropped->think = RespawnRunes;
			//dropped->nextthink = level.time + 30000;
#else
        if( ( item->giType == IT_PERSISTANT_POWERUP ) ) {
            dropped->touch = Pickup_Rune;
            dropped->takedamage = qtrue;
            dropped->health = INT_MAX;
            dropped->physicsBounce = 0.50;		// runes are bouncy
            dropped->think = RespawnRunes;
            dropped->nextthink = level.time + 10000;
		} else
#endif

		if ( g_gamemode.integer == GM_BATTLE && !g_healthRestore.integer && 
                    (item->giType == IT_HEALTH || item->giType == IT_ARMOR) ) {
            dropped->think = G_FreeEntity;
            dropped->nextthink = level.time + 5000;
        } else {
            dropped->think = G_FreeEntity;
            dropped->nextthink = level.time + 30000;
        }
        //BB END DROP CODE
    }

    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 );
    }


    // Hugo
    // Alliance, inhibit by item
    // just make em invisible instead of completely discarding em. bots need em to move around.

    //G_Printf ("FinishSpawningItem: %s\n", ent->classname);

    if ( allianceInhibitSpawn( ent->item ) ) {
        ent->r.svFlags = SVF_NOCLIENT; // not sent to clients. bot code has been hacked to support instagib and combat
        ent->s.eFlags |= EF_NODRAW; // invisible
        ent->r.contents = 0;
    } else { // Hugo

        // 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_PERSISTANT_POWERUP || 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();

#ifdef MISSIONPACK
    if( g_gametype.integer == GT_CTF || g_gametype.integer == GT_RTF || g_gametype.integer == GT_1FCTF ) {
#else
	if( g_gametype.integer == GT_CTF || g_gametype.integer == GT_RTF) {
#endif
        gitem_t	*item;
        gitem_t	*item2;

        // 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\n" );
        }
        //BB - check for the two bases
        item2 = BG_FindItem( "Red Flagbase" );

        if (item && item2 && itemRegistered[ item - bg_itemlist ]) {

            BB_SpawnFlagbases( TEAM_RED );
            //G_Printf( S_COLOR_YELLOW "WARNING: No team_CTF_redflag in map\n" );
        }


        item = BG_FindItem( "Blue Flag" );
        if ( !item || !itemRegistered[ item - bg_itemlist ] ) {
            G_Printf( S_COLOR_YELLOW "WARNING: No team_CTF_blueflag in map\n" );
        }

        item2 = BG_FindItem( "Blue Flagbase" );
        //if ( item2 && !itemRegistered[ item - bg_itemlist ] ) {
        if (item && item2 && itemRegistered[ item - bg_itemlist ]) {

            BB_SpawnFlagbases( TEAM_BLUE );
            //G_Printf( S_COLOR_YELLOW "WARNING: No team_CTF_bluebase in map\n" );
        }




    }
#ifdef MISSIONPACK

    if( g_gametype.integer == GT_1FCTF ) {
        gitem_t	*item;
	//	gitem_t	*item2;

        // 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\n" );
        //}
        //item = BG_FindItem( "Blue Flag" );
        //if ( !item || !itemRegistered[ item - bg_itemlist ] ) {
        //    G_Printf( S_COLOR_YELLOW "WARNING: No team_CTF_blueflag in map\n" );
       // }
        item = BG_FindItem( "Neutral Flag" );
        if ( !item || !itemRegistered[ item - bg_itemlist ] ) {
            G_Printf( S_COLOR_YELLOW "WARNING: No team_CTF_neutralflag in map\n" );
        }
		//item2 = BG_FindItem( "Neutral Flagbase" );
		if (item && itemRegistered[ item - bg_itemlist ]) {
			gentity_t	*ent;
			ent = NULL;
			ent = G_Find( ent, FOFS(classname), "team_CTF_neutralbase" );    
			//BB_SpawnFlagbases( TEAM_FREE );
            //G_Printf( S_COLOR_YELLOW "WARNING: No team_CTF_bluebase in map\n" );
        }

    }

    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
    if (freeMachinegun())
        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
    // Alliance: precache grapple here
    // XXX only needed if the grapple isn't in the entmap
    //INCIN -- testing this
    if (freeOnhandGrapple() && globalGrapple()) {
        RegisterItem(BG_Find_Grapple());//INCIN need new cvar for this item
        //RegisterItem( BG_FindItemForWeapon( WP_GRAPPLING_HOOK ) );
        //RegisterItem( BG_FindItemForWeapon( WP_LIGHTNING ) );  // for hook effect?
        //RegisterItem (BG_FindItemByClass("ammo_lightning"));
    }
    else if (!freeOnhandGrapple() && globalGrapple()) {
        RegisterItem(BG_Find_Offhand_Grapple());
        //GRAPPLE 2
    }

    // Alliance: Register free-geared weapons
    if (freeGear())
        precacheFreeGear();

}

/*
===============
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 || item->giType == IT_PERSISTANT_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 );
}

