2012-08-04 10:54:37 +00:00
// Copyright (C) 1999-2000 Id Software, Inc.
2012-01-22 21:34:33 +00:00
//
# 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 .
*/
2012-08-04 10:54:37 +00:00
# define RESPAWN_ARMOR 20
# define RESPAWN_TEAM_WEAPON 30
# define RESPAWN_HEALTH 30
2012-01-22 21:34:33 +00:00
# define RESPAWN_AMMO 40
# define RESPAWN_HOLDABLE 60
2012-08-04 10:54:37 +00:00
# define RESPAWN_MEGAHEALTH 120
2012-01-22 21:34:33 +00:00
# define RESPAWN_POWERUP 120
2012-08-04 10:54:37 +00:00
//Replacement for Max_Ammo table
vmCvar_t * Max_Weapons [ WP_NUM_WEAPONS ] = {
0 ,
0 ,
& rpg_maxTricorders ,
& rpg_maxPADDs ,
& rpg_maxCups ,
& rpg_maxPhasers ,
& rpg_maxRifles ,
& rpg_maxTR116s ,
& rpg_maxAdminguns ,
& rpg_maxPhotonbursts ,
& rpg_maxDisruptors ,
& rpg_maxMedkits ,
& rpg_maxHyposprays ,
& rpg_maxRegenerators ,
& rpg_maxToolkits ,
& rpg_maxHyperSpanners
} ;
vmCvar_t * Min_Weapons [ WP_NUM_WEAPONS ] = {
0 ,
0 ,
& rpg_minTricorders ,
& rpg_minPADDs ,
& rpg_minCups ,
& rpg_minPhasers ,
& rpg_minRifles ,
& rpg_minTR116s ,
& rpg_minAdminguns ,
& rpg_minPhotonbursts ,
& rpg_minDisruptors ,
& rpg_minMedkits ,
& rpg_minHyposprays ,
& rpg_minRegenerators ,
& rpg_minToolkits ,
& rpg_minHyperSpanners
} ;
// RPG-X: Marcin: Definitions of the PADD messaging system data structures - 06/12/2008
paddData_t paddData [ PADD_DATA_MAX ] ;
int paddDataNum = 0 ;
int numTotalDropped = 0 ;
/*
= = = = = = = = = = = = = = =
Max_Weapon
RPG - X | Marcin | 06 / 12 / 2008
= = = = = = = = = = = = = = =
*/
/**
* Get the maximum ammount of weapons the player can carry for this weapon .
* \ num weapn num ( WP_ . . )
* \ author Ubergames - Marcin
* \ date 06 / 12 / 2008
*/
int Max_Weapon ( int num )
{
if ( Max_Weapons [ num ] = = NULL ) {
return 1 ;
} else {
return Max_Weapons [ num ] - > integer ;
}
}
/*
= = = = = = = = = = = = = = =
Min_Weapon
RPG - X | Marcin | 06 / 12 / 2008
= = = = = = = = = = = = = = =
*/
/**
* Get the minimum ammount of weapons a player can carry for a weapon .
* \ param num weapon number ( WP_ . . . )
* \ author Ubergames - Marcin
* \ date 06 / 12 / 2008
*/
int Min_Weapon ( int num )
{
if ( Min_Weapons [ num ] = = NULL ) {
return 1 ;
} else {
return Min_Weapons [ num ] - > integer ;
}
}
void Padd_Add ( gentity_t * key , gentity_t * who , char * txt )
{
int i = 0 ;
char * txtp ;
paddData_t * empty = 0 ;
DPRINTF ( ( S_COLOR_YELLOW " in Padd_Add: txt = %s and last = %s \n " , txt , who - > client - > lastPaddMsg ) ) ;
while ( ! empty ) {
if ( i > = PADD_DATA_MAX ) {
G_Printf ( S_COLOR_RED " Padd_Add: Too much PADDs! \n " S_COLOR_WHITE ) ;
return ;
} else if ( paddData [ i ] . key = = 0 ) {
empty = & paddData [ i ] ;
}
+ + i ;
}
DPRINTF ( ( S_COLOR_YELLOW " added: %i with %s on nr %i \n " S_COLOR_WHITE , key , txt , i - 1 ) ) ;
empty - > key = key ;
if ( txt & & txt [ 0 ] ) {
txtp = txt ;
Q_strncpyz ( who - > client - > lastPaddMsg , txt , sizeof ( who - > client - > lastPaddMsg ) ) ;
} else if ( who - > client - > lastPaddMsg & & who - > client - > lastPaddMsg [ 0 ] ) {
txtp = who - > client - > lastPaddMsg ;
} else {
txtp = 0 ;
}
if ( txtp ) {
Q_strncpyz ( empty - > value , txtp , sizeof ( empty - > value ) ) ;
}
Q_strncpyz ( empty - > owner , who - > client - > pers . netname , sizeof ( empty - > owner ) ) ;
+ + paddDataNum ;
}
char * Padd_Get ( gentity_t * key , gentity_t * who )
{
int i , j ;
for ( i = 0 ; i < PADD_DATA_MAX ; + + i ) {
if ( paddData [ i ] . key = = key ) {
DPRINTF ( ( " ^3got: %i with %s on nr %i \n " , key , paddData [ i ] . value , i ) ) ;
//Inform admins
for ( j = 0 ; j < level . maxclients ; + + j ) {
gentity_t * player = & g_entities [ j ] ;
if ( ! player - > client - > noAdminChat & & IsAdmin ( player ) & & rpg_respectPrivacy . integer = = 0 & & player ! = who & & paddData [ i ] . value & & paddData [ i ] . value [ 0 ] ) {
trap_SendServerCommand ( player - g_entities , va ( " print \" %s " S_COLOR_CYAN " (picked up by %s " S_COLOR_CYAN " ) " S_COLOR_WHITE " %s \n \" " , paddData [ i ] . owner , who - > client - > pers . netname , paddData [ i ] . value ) ) ;
}
}
//Store string
Q_strncpyz ( who - > client - > lastPaddMsg , paddData [ i ] . value , sizeof ( who - > client - > lastPaddMsg ) ) ;
return paddData [ i ] . value ;
}
}
G_Printf ( S_COLOR_RED " Padd_Get: Unable to find the text for this PADD! \n " S_COLOR_WHITE ) ;
return 0 ;
}
void Padd_Remove ( gentity_t * key )
{
int i = 0 ;
while ( qtrue ) {
if ( paddData [ i ] . key = = key ) {
paddData [ i ] . key = 0 ;
paddData [ i ] . value [ 0 ] = ' \0 ' ;
paddData [ i ] . owner [ 0 ] = ' \0 ' ;
DPRINTF ( ( S_COLOR_YELLOW " deleting: %i on %i \n " , key , i ) ) ;
- - paddDataNum ;
return ;
} else if ( i > = PADD_DATA_MAX ) {
G_Printf ( S_COLOR_RED " Padd_Remove: Can not delete PADD! \n " S_COLOR_WHITE ) ;
return ;
}
+ + i ;
}
}
// For more than four players, adjust the respawn times, up to 1/4.
int adjustRespawnTime ( float respawnTime )
{
if ( ! g_adaptRespawn . integer )
{
return ( ( int ) respawnTime ) ;
}
if ( level . numPlayingClients > 4 )
{ // Start scaling the respawn times.
if ( level . numPlayingClients > 32 )
{ // 1/4 time minimum.
respawnTime * = 0.25 ;
}
else if ( level . numPlayingClients > 12 )
{ // From 12-32, scale from 0.5 to 0.25;
respawnTime * = 20.0 / ( float ) ( level . numPlayingClients + 8 ) ;
}
else
{ // From 4-12, scale from 1.0 to 0.5;
respawnTime * = 8.0 / ( float ) ( level . numPlayingClients + 4 ) ;
}
}
if ( respawnTime < 1.0 )
{ // No matter what, don't go lower than 1 second, or the pickups become very noisy!
respawnTime = 1.0 ;
}
return ( ( int ) respawnTime ) ;
}
2012-01-22 21:34:33 +00:00
//======================================================================
int Pickup_Powerup ( gentity_t * ent , gentity_t * other ) {
int quantity ;
int i ;
gclient_t * client ;
2012-08-04 10:54:37 +00:00
playerState_t * ps = & other - > client - > ps ;
2012-01-22 21:34:33 +00:00
2012-08-04 10:54:37 +00:00
if ( ! ps - > powerups [ ent - > item - > giTag ] ) {
2012-01-22 21:34:33 +00:00
// round timing to seconds to make multiple powerup timers
// count in sync
2012-08-04 10:54:37 +00:00
ps - > powerups [ ent - > item - > giTag ] =
2012-01-22 21:34:33 +00:00
level . time - ( level . time % 1000 ) ;
2012-08-04 10:54:37 +00:00
// kef -- log the fact that we picked up this powerup
G_LogWeaponPowerup ( other - > s . number , ent - > item - > giTag ) ;
2012-01-22 21:34:33 +00:00
}
if ( ent - > count ) {
quantity = ent - > count ;
} else {
quantity = ent - > item - > quantity ;
}
2012-08-04 10:54:37 +00:00
ps - > powerups [ ent - > item - > giTag ] + = quantity * 1000 ;
2012-01-22 21:34:33 +00:00
// 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
2012-08-04 10:54:37 +00:00
client - > ps . persistant [ PERS_REWARD_COUNT ] + + ;
client - > ps . persistant [ PERS_REWARD ] = REWARD_DENIED ;
2012-01-22 21:34:33 +00:00
}
2012-08-04 10:54:37 +00:00
return RESPAWN_POWERUP ;
2012-01-22 21:34:33 +00:00
}
//======================================================================
2012-08-04 10:54:37 +00:00
int Pickup_Holdable ( gentity_t * ent , gentity_t * other )
{
int nItem = ent - > item - bg_itemlist ;
2012-01-22 21:34:33 +00:00
2012-08-04 10:54:37 +00:00
other - > client - > ps . stats [ STAT_HOLDABLE_ITEM ] = nItem ;
2012-01-22 21:34:33 +00:00
2012-08-04 10:54:37 +00:00
// if we just picked up the detpack, indicate that it has not been placed yet
if ( HI_DETPACK = = bg_itemlist [ nItem ] . giTag )
{
other - > client - > ps . stats [ STAT_USEABLE_PLACED ] = 0 ;
2012-01-22 21:34:33 +00:00
}
2012-08-04 10:54:37 +00:00
// kef -- log the fact that we picked up this item
G_LogWeaponItem ( other - > s . number , bg_itemlist [ nItem ] . giTag ) ;
2012-01-22 21:34:33 +00:00
2012-08-04 10:54:37 +00:00
return adjustRespawnTime ( RESPAWN_HOLDABLE ) ;
2012-01-22 21:34:33 +00:00
}
//======================================================================
void Add_Ammo ( gentity_t * ent , int weapon , int count )
{
2012-08-04 10:54:37 +00:00
playerState_t * ps = & ent - > client - > ps ;
ps - > ammo [ weapon ] + = count ;
if ( ps - > ammo [ weapon ] > Max_Weapon ( weapon ) ) {
ps - > ammo [ weapon ] = Max_Weapon ( weapon ) ;
2012-01-22 21:34:33 +00:00
}
}
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 ) ;
2012-08-04 10:54:37 +00:00
return adjustRespawnTime ( RESPAWN_AMMO ) ;
2012-01-22 21:34:33 +00:00
}
//======================================================================
int Pickup_Weapon ( gentity_t * ent , gentity_t * other ) {
2012-08-04 10:54:37 +00:00
char * msg ;
2012-01-22 21:34:33 +00:00
// add the weapon
other - > client - > ps . stats [ STAT_WEAPONS ] | = ( 1 < < ent - > item - > giTag ) ;
2012-08-04 10:54:37 +00:00
Add_Ammo ( other , ent - > item - > giTag , 1 ) ;
2012-01-22 21:34:33 +00:00
2012-08-04 10:54:37 +00:00
// RPG-X: Marcin: print PADD message - 06/12/2008
2012-01-22 21:34:33 +00:00
2012-08-04 10:54:37 +00:00
if ( ent - > item - > giTag = = WP_3 ) {
msg = Padd_Get ( ent , other ) ;
if ( msg ) {
trap_SendServerCommand ( other - g_entities , va ( " print \" " S_COLOR_CYAN " (padd) " S_COLOR_WHITE " %s \n \" " , msg ) ) ;
}
}
G_LogWeaponPickup ( other - > s . number , ent - > item - > giTag ) ;
2012-01-22 21:34:33 +00:00
// team deathmatch has slow weapon respawns
2012-08-04 10:54:37 +00:00
if ( g_gametype . integer = = GT_TEAM )
{
return adjustRespawnTime ( RESPAWN_TEAM_WEAPON ) ;
2012-01-22 21:34:33 +00:00
}
2012-08-04 10:54:37 +00:00
return adjustRespawnTime ( g_weaponRespawn . integer ) ;
2012-01-22 21:34:33 +00:00
}
//======================================================================
int Pickup_Health ( gentity_t * ent , gentity_t * other ) {
int max ;
int quantity ;
2012-08-04 10:54:37 +00:00
playerState_t * ps = & other - > client - > ps ;
2012-01-22 21:34:33 +00:00
// small and mega healths will go over the max
2012-08-04 10:54:37 +00:00
if ( ent - > item - > quantity ! = 5 & & ent - > item - > quantity ! = 100 ) {
max = ps - > stats [ STAT_MAX_HEALTH ] ;
2012-01-22 21:34:33 +00:00
} else {
2012-08-04 10:54:37 +00:00
max = ps - > stats [ STAT_MAX_HEALTH ] * 2 ;
2012-01-22 21:34:33 +00:00
}
if ( ent - > count ) {
quantity = ent - > count ;
} else {
quantity = ent - > item - > quantity ;
}
other - > health + = quantity ;
if ( other - > health > max ) {
other - > health = max ;
}
2012-08-04 10:54:37 +00:00
ps - > stats [ STAT_HEALTH ] = other - > health ;
2012-01-22 21:34:33 +00:00
2012-08-04 10:54:37 +00:00
if ( ent - > item - > giTag = = 100 ) { // mega health respawns slow
return RESPAWN_MEGAHEALTH ; // It also does not adapt like other health pickups.
2012-01-22 21:34:33 +00:00
}
2012-08-04 10:54:37 +00:00
return adjustRespawnTime ( RESPAWN_HEALTH ) ;
2012-01-22 21:34:33 +00:00
}
//======================================================================
int Pickup_Armor ( gentity_t * ent , gentity_t * other ) {
2012-08-04 10:54:37 +00:00
playerState_t * ps = & other - > client - > ps ;
ps - > stats [ STAT_ARMOR ] + = ent - > item - > quantity ;
if ( ps - > stats [ STAT_ARMOR ] > ps - > stats [ STAT_MAX_HEALTH ] * 2 ) {
ps - > stats [ STAT_ARMOR ] = ps - > stats [ STAT_MAX_HEALTH ] * 2 ;
2012-01-22 21:34:33 +00:00
}
2012-08-04 10:54:37 +00:00
return adjustRespawnTime ( RESPAWN_ARMOR ) ;
2012-01-22 21:34:33 +00:00
}
//======================================================================
void RespawnItem ( gentity_t * ent ) {
2012-08-04 10:54:37 +00:00
if ( ! ent ) return ;
2012-01-22 21:34:33 +00:00
// 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 ;
2012-08-04 10:54:37 +00:00
for ( count = 0 , ent = master ; count < choice & & ent ; ent = ent - > teamchain , count + + )
2012-01-22 21:34:33 +00:00
;
}
2012-08-04 10:54:37 +00:00
if ( ! ent ) return ;
2012-01-22 21:34:33 +00:00
ent - > r . contents = CONTENTS_TRIGGER ;
2012-08-04 10:54:37 +00:00
ent - > s . eFlags & = ~ ( EF_NODRAW | EF_ITEMPLACEHOLDER ) ;
2012-01-22 21:34:33 +00:00
ent - > r . svFlags & = ~ SVF_NOCLIENT ;
trap_LinkEntity ( ent ) ;
if ( ent - > item - > giType = = IT_POWERUP ) {
// play powerup spawn sound to all clients
gentity_t * te ;
2012-08-04 10:54:37 +00:00
te = G_TempEntity ( ent - > s . pos . trBase , EV_GLOBAL_SOUND ) ;
te - > s . eventParm = G_SoundIndex ( " sound/items/poweruprespawn.wav " ) ; //cgs.media.poweruprespawn
2012-01-22 21:34:33 +00:00
te - > r . svFlags | = SVF_BROADCAST ;
}
// play the normal respawn sound only to nearby clients
G_AddEvent ( ent , EV_ITEM_RESPAWN , 0 ) ;
ent - > nextthink = 0 ;
}
void Touch_Item ( gentity_t * ent , gentity_t * other , trace_t * trace ) {
int respawn ;
if ( ! other - > client )
return ;
if ( other - > health < 1 )
return ; // dead people can't pickup
2012-08-04 10:54:37 +00:00
// RPG-X: Marcin: Press USE to pick up items. - 03/12/2008
if ( ! ( other - > client - > pers . cmd . buttons & BUTTON_USE ) | | other - > client - > pressedUse = = qtrue ) {
return ;
} else {
other - > client - > pressedUse = qtrue ;
}
2012-01-22 21:34:33 +00:00
// the same pickup rules are used for client side and server side
2012-08-04 10:54:37 +00:00
if ( ! BG_CanItemBeGrabbed ( & ent - > s , & other - > client - > ps , Max_Weapon ( other - > client - > ps . weapon ) )
& & IsAdmin ( other ) = = qfalse )
{
2012-01-22 21:34:33 +00:00
return ;
}
2012-08-04 10:54:37 +00:00
numTotalDropped - - ;
2012-01-22 21:34:33 +00:00
2012-08-04 10:54:37 +00:00
G_LogPrintf ( " Item: %i %s \n " , other - > s . number , ent - > item - > classname ) ;
2012-01-22 21:34:33 +00:00
// call the item-specific pickup function
2012-08-04 10:54:37 +00:00
switch ( ent - > item - > giType )
{
2012-01-22 21:34:33 +00:00
case IT_WEAPON :
respawn = Pickup_Weapon ( ent , other ) ;
break ;
case IT_AMMO :
respawn = Pickup_Ammo ( ent , other ) ;
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 ) ;
break ;
case IT_TEAM :
2012-11-15 22:43:56 +00:00
respawn = 0 ; // TODO remove?
2012-01-22 21:34:33 +00:00
break ;
case IT_HOLDABLE :
respawn = Pickup_Holdable ( ent , other ) ;
break ;
default :
return ;
}
2012-08-04 10:54:37 +00:00
if ( ! respawn ) {
2012-01-22 21:34:33 +00:00
return ;
}
// play the normal pickup sound
2012-08-04 10:54:37 +00:00
if ( other - > client - > pers . predictItemPickup ) {
2012-01-22 21:34:33 +00:00
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 ) {
2012-08-04 10:54:37 +00:00
gentity_t * te ;
2012-01-22 21:34:33 +00:00
2012-08-04 10:54:37 +00:00
te = G_TempEntity ( ent - > s . pos . trBase , EV_GLOBAL_ITEM_PICKUP ) ;
te - > s . eventParm = ent - > s . modelindex ;
// tell us which client fired off this global sound
te - > s . otherEntityNum = other - > s . number ;
te - > r . svFlags | = SVF_BROADCAST ;
2012-01-22 21:34:33 +00:00
}
// fire item targets
G_UseTargets ( ent , other ) ;
2012-08-04 10:54:37 +00:00
if ( rpg_weaponsStay . integer = = 1 & & IsAdmin ( ent - > parent ) = = qtrue & & IsAdmin ( other ) = = qfalse ) {
return ;
}
if ( ent - > item - > giTag = = WP_3 ) {
Padd_Remove ( ent ) ;
}
// wait of -1 will not respawn
2012-01-22 21:34:33 +00:00
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.
2012-08-04 10:54:37 +00:00
if ( ent - > item - > giType = = IT_WEAPON | | ent - > item - > giType = = IT_POWERUP )
{
ent - > s . eFlags | = EF_ITEMPLACEHOLDER ;
}
else
{
// this line used to prevent items that were picked up from being drawn, but we now want to draw the techy grid thing instead
ent - > s . eFlags | = EF_NODRAW ;
ent - > r . svFlags | = SVF_NOCLIENT ;
}
2012-01-22 21:34:33 +00:00
ent - > r . contents = 0 ;
2012-08-04 10:54:37 +00:00
// ***************
2012-01-22 21:34:33 +00:00
// 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 {
2012-08-04 10:54:37 +00:00
ent - > nextthink = level . time + respawn * 1000 ;
2012-01-22 21:34:33 +00:00
ent - > think = RespawnItem ;
}
trap_LinkEntity ( ent ) ;
}
//======================================================================
/*
= = = = = = = = = = = = = = = =
LaunchItem
Spawns an item and tosses it forward
= = = = = = = = = = = = = = = =
*/
2012-08-04 10:54:37 +00:00
gentity_t * LaunchItem ( gitem_t * item , gentity_t * who , vec3_t origin , vec3_t velocity , int flags , char * txt ) // RPG-X: Marcin: for ThrowWeapon 03/12/2008
{ // and for PADD stuff too 06/12/2008, 08/12/2008
2012-01-22 21:34:33 +00:00
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 ;
2012-08-04 10:54:37 +00:00
dropped - > s . eFlags | = EF_DEAD ; // Yes, this is totally lame, but we use it bg_misc to check
// if the item has been droped, and if so, make it pick-up-able
// cdr
if ( item - > giTag = = WP_3 ) {
Padd_Add ( dropped , who , txt ) ;
}
2012-11-15 22:43:56 +00:00
dropped - > think = G_FreeEntity ;
dropped - > nextthink = level . time + 6000000 ; //30000; // RPG-X: Marcin: increased - 03/12/2008
2012-01-22 21:34:33 +00:00
2012-08-04 10:54:37 +00:00
dropped - > flags = flags ; // FL_DROPPED_ITEM; // RPG-X: Marcin: for ThrowWeapon - 03/12/2008
if ( flags & FL_THROWN_ITEM ) {
dropped - > clipmask = MASK_SHOT ;
dropped - > s . pos . trTime = level . time - 50 ;
VectorScale ( velocity , 300 , dropped - > s . pos . trDelta ) ;
SnapVector ( dropped - > s . pos . trDelta ) ;
dropped - > physicsBounce = 0.2 ;
}
2012-01-22 21:34:33 +00:00
trap_LinkEntity ( dropped ) ;
return dropped ;
}
2012-08-04 10:54:37 +00:00
gentity_t * DropWeapon ( gentity_t * ent , gitem_t * item , float angle , int flags , char * txt ) {
vec3_t velocity ;
vec3_t origin ;
VectorCopy ( ent - > s . pos . trBase , origin ) ;
// set aiming directions
AngleVectors ( ent - > client - > ps . viewangles , velocity , NULL , NULL ) ;
origin [ 2 ] + = ent - > client - > ps . viewheight + 10 ;
VectorMA ( origin , 14 , velocity , origin ) ;
// snap to integer coordinates for more efficient network bandwidth usage
SnapVector ( origin ) ;
// extra vertical velocity
velocity [ 2 ] + = 0.2 ;
VectorNormalize ( velocity ) ;
return LaunchItem ( item , ent , origin , velocity , flags , txt ) ;
}
2012-01-22 21:34:33 +00:00
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 ) ;
2012-08-04 10:54:37 +00:00
VectorScale ( velocity , 300 , velocity ) ;
velocity [ 2 ] + = 75 + crandom ( ) * 50 ;
return LaunchItem ( item , ent , ent - > s . pos . trBase , velocity , FL_DROPPED_ITEM , 0 ) ; // RPG-X: Marcin: for ThrowWeapon - 03/12/2008
2012-01-22 21:34:33 +00:00
}
/*
= = = = = = = = = = = = = = = =
Use_Item
Respawn the item
= = = = = = = = = = = = = = = =
*/
void Use_Item ( gentity_t * ent , gentity_t * other , gentity_t * activator ) {
RespawnItem ( ent ) ;
}
//======================================================================
void FinishSpawningItem ( gentity_t * ent ) {
trace_t tr ;
vec3_t dest ;
2012-08-04 10:54:37 +00:00
if ( ! ent )
{
Com_Printf ( " print \" Ent Missing \" " ) ;
return ;
}
if ( ! ent - > item )
{
Com_Printf ( " print \" Ent->item Missing \" " ) ;
return ;
}
2012-01-22 21:34:33 +00:00
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 ;
2012-08-04 10:54:37 +00:00
2012-09-10 10:55:24 +00:00
Com_Printf ( " print \" giType %i! \n \" " , ent - > item - > giType ) ;
2012-08-04 10:54:37 +00:00
2012-01-22 21:34:33 +00:00
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 ) {
2012-08-04 10:54:37 +00:00
G_Printf ( " FinishSpawningItem: removing %s startsolid at %s \n " , ent - > classname , vtos ( ent - > s . origin ) ) ;
# ifndef FINAL_BUILD
G_Error ( " FinishSpawningItem: removing %s startsolid at %s \n " , ent - > classname , vtos ( ent - > s . origin ) ) ;
# endif
2012-01-22 21:34:33 +00:00
G_Printf ( " FinishSpawningItem: %s startsolid at %s \n " , ent - > classname , vtos ( ent - > s . origin ) ) ;
2012-08-04 10:54:37 +00:00
2012-01-22 21:34:33 +00:00
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 ;
}
2012-08-04 10:54:37 +00:00
trap_LinkEntity ( ent ) ;
}
/*
= = = = = = = = = = = = = = = =
FinishSpawningDetpack
Traces down to find where an item should rest , instead of letting them
free fall from their spawn points
= = = = = = = = = = = = = = = =
*/
extern void detpack_shot ( gentity_t * self , gentity_t * inflictor , gentity_t * attacker , int damage , int meansOfDeath ) ;
qboolean FinishSpawningDetpack ( gentity_t * ent , int itemIndex )
{
trace_t tr ;
vec3_t dest ;
VectorSet ( ent - > r . mins , - ITEM_RADIUS , - ITEM_RADIUS , 0 ) ;
VectorSet ( ent - > r . maxs , ITEM_RADIUS , ITEM_RADIUS , ITEM_RADIUS ) ;
ent - > s . eType = ET_USEABLE ;
ent - > s . modelindex = bg_itemlist [ itemIndex ] . giTag ; // this'll be used in CG_Useable()
ent - > s . modelindex2 = itemIndex ; // store item number in modelindex
ent - > classname = bg_itemlist [ itemIndex ] . classname ;
ent - > r . contents = CONTENTS_CORPSE ; //CONTENTS_TRIGGER;
2012-11-11 22:11:37 +00:00
ent - > takedamage = qtrue ;
2012-08-04 10:54:37 +00:00
ent - > health = 5 ;
ent - > touch = 0 ;
ent - > die = detpack_shot ;
// useing an item causes it to respawn
ent - > use = Use_Item ;
// 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_FreeEntity ( ent ) ;
return qfalse ;
2012-01-22 21:34:33 +00:00
}
2012-08-04 10:54:37 +00:00
// allow to ride movers
ent - > physicsObject = qtrue ;
ent - > s . groundEntityNum = tr . entityNum ;
G_SetOrigin ( ent , tr . endpos ) ;
ent - > s . eFlags & = ~ EF_NODRAW ;
ent - > r . svFlags & = ~ SVF_NOCLIENT ;
2012-01-22 21:34:33 +00:00
trap_LinkEntity ( ent ) ;
2012-08-04 10:54:37 +00:00
ent - > noise_index = G_SoundIndex ( " sound/weapons/detpacklatch.wav " ) ;
G_AddEvent ( ent , EV_GENERAL_SOUND , ent - > noise_index ) ;
return qtrue ;
2012-01-22 21:34:33 +00:00
}
2012-08-04 10:54:37 +00:00
/*
= = = = = = = = = = = = = = = =
FinishSpawningDecoy
Traces down to find where an item should rest , instead of letting them
free fall from their spawn points
= = = = = = = = = = = = = = = =
*/
qboolean FinishSpawningDecoy ( gentity_t * ent , int itemIndex )
{
trace_t tr ;
vec3_t dest ;
ent - > classname = bg_itemlist [ itemIndex ] . classname ;
ent - > touch = 0 ; // null touch function pointer
// useing an item causes it to respawn
ent - > use = Use_Item ;
// drop to floor
VectorSet ( dest , ent - > s . origin [ 0 ] , ent - > s . origin [ 1 ] , ent - > s . origin [ 2 ] - 1 ) ; //4096
trap_Trace ( & tr , ent - > s . origin , ent - > r . mins , ent - > r . maxs , dest , ent - > s . number , MASK_SOLID ) ;
if ( tr . startsolid )
{ // If stuck in a solid, give up and go home
G_FreeEntity ( ent ) ;
return qfalse ;
}
G_SetOrigin ( ent , tr . endpos ) ;
// allow to ride movers
if ( tr . contents & CONTENTS_SOLID ) { //TiM - only if u spawn them ON the elevator. Otherwise, leave them in the air
ent - > physicsObject = qtrue ;
ent - > s . pos . trType = TR_GRAVITY ; //have to do this because it thinks it's an ET_PLAYER
ent - > s . groundEntityNum = tr . entityNum ;
}
// Turn off the NODRAW and NOCLIENT flags
ent - > s . eFlags & = ~ EF_NODRAW ;
ent - > r . svFlags & = ~ SVF_NOCLIENT ;
trap_LinkEntity ( ent ) ;
return qtrue ;
}
2012-01-22 21:34:33 +00:00
qboolean itemRegistered [ MAX_ITEMS ] ;
void ClearRegisteredItems ( void ) {
memset ( itemRegistered , 0 , sizeof ( itemRegistered ) ) ;
// players always start with the base weapon
2012-08-04 10:54:37 +00:00
RegisterItem ( BG_FindItemForWeapon ( WP_5 ) ) ;
RegisterItem ( BG_FindItemForWeapon ( WP_6 ) ) ; //this is for the podium at the end, make sure we have the model
RegisterItem ( BG_FindItemForWeapon ( WP_1 ) ) ;
RegisterItem ( BG_FindItemForWeapon ( WP_10 ) ) ;
RegisterItem ( BG_FindItemForWeapon ( WP_13 ) ) ;
RegisterItem ( BG_FindItemForWeapon ( WP_12 ) ) ;
RegisterItem ( BG_FindItemForWeapon ( WP_14 ) ) ;
RegisterItem ( BG_FindItemForWeapon ( WP_11 ) ) ;
RegisterItem ( BG_FindItemForWeapon ( WP_2 ) ) ;
RegisterItem ( BG_FindItemForWeapon ( WP_3 ) ) ;
RegisterItem ( BG_FindItemForWeapon ( WP_15 ) ) ;
RegisterItem ( BG_FindItemForWeapon ( WP_7 ) ) ;
2012-01-22 21:34:33 +00:00
}
2012-08-04 10:54:37 +00:00
void RegisterItem ( gitem_t * item ) {
2012-01-22 21:34:33 +00:00
if ( ! item ) {
G_Error ( " RegisterItem: NULL " ) ;
2012-08-04 10:54:37 +00:00
//RPG-X
Com_Printf ( " Missing Item In RegisterItem \n " ) ;
2012-01-22 21:34:33 +00:00
}
itemRegistered [ item - bg_itemlist ] = qtrue ;
}
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 ) ;
}
2012-08-04 10:54:37 +00:00
qboolean G_ItemSuppressed ( int itemType , int itemTag )
{
if ( rpg_rpg . integer ! = 0 )
{
switch ( itemType )
{
case IT_HEALTH :
case IT_ARMOR :
case IT_HOLDABLE :
case IT_POWERUP :
case IT_AMMO :
case IT_WEAPON :
return qtrue ;
break ;
}
}
return qfalse ;
}
qboolean G_ItemClassnameSuppressed ( char * itemname )
{
gitem_t * item = NULL ;
int itemType = 0 ;
int itemTag = 0 ;
2012-01-22 21:34:33 +00:00
2012-08-04 10:54:37 +00:00
item = BG_FindItemWithClassname ( itemname ) ;
if ( ! item )
{
return qfalse ;
}
itemType = item - > giType ;
itemTag = item - > giTag ;
return G_ItemSuppressed ( itemType , itemTag ) ;
2012-01-22 21:34:33 +00:00
}
void G_SpawnItem ( gentity_t * ent , gitem_t * item ) {
2012-08-04 10:54:37 +00:00
if ( G_ItemSuppressed ( item - > giType , item - > giTag ) )
{
return ;
}
2012-01-22 21:34:33 +00:00
G_SpawnFloat ( " random " , " 0 " , & ent - > random ) ;
G_SpawnFloat ( " wait " , " 0 " , & ent - > wait ) ;
RegisterItem ( item ) ;
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 ) {
2012-08-04 10:54:37 +00:00
G_SoundIndex ( " sound/items/poweruprespawn.wav " ) ; //cgs.media.poweruprespawn
2012-01-22 21:34:33 +00:00
}
}
/*
= = = = = = = = = = = = = = = =
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 ;
}
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 ) {
2012-11-15 22:43:56 +00:00
G_FreeEntity ( ent ) ;
2012-01-22 21:34:33 +00:00
return ;
}
G_BounceItem ( ent , & tr ) ;
}