2013-04-19 02:52:48 +00:00
// leave this line at the top for all g_xxxx.cpp files...
# include "g_headers.h"
# include "g_local.h"
# include "g_functions.h"
# include "g_items.h"
# include "wp_saber.h"
extern qboolean missionInfo_Updated ;
extern void CrystalAmmoSettings ( gentity_t * ent ) ;
extern void ChangeWeapon ( gentity_t * ent , int newWeapon ) ;
extern qboolean PM_InKnockDown ( playerState_t * ps ) ;
extern qboolean PM_InGetUp ( playerState_t * ps ) ;
extern void WP_SetSaber ( gentity_t * ent , int saberNum , char * saberName ) ;
extern void WP_RemoveSaber ( gentity_t * ent , int saberNum ) ;
extern void WP_SaberFallSound ( gentity_t * owner , gentity_t * saber ) ;
extern saber_colors_t TranslateSaberColor ( const char * name ) ;
extern cvar_t * g_spskill ;
extern cvar_t * g_sex ;
extern cvar_t * g_saberPickuppableDroppedSabers ;
# define MAX_BACTA_HEAL_AMOUNT 25
/*
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 .
*/
// Item Spawn flags
# define ITMSF_SUSPEND 1
# define ITMSF_NOPLAYER 2
# define ITMSF_ALLOWNPC 4
# define ITMSF_NOTSOLID 8
# define ITMSF_VERTICAL 16
# define ITMSF_INVISIBLE 32
# define ITMSF_NOGLOW 64
# define ITMSF_USEPICKUP 128
# define ITMSF_STATIONARY 2048
//======================================================================
/*
= = = = = = = = = = = = = = =
G_InventorySelectable
= = = = = = = = = = = = = = =
*/
qboolean G_InventorySelectable ( int index , gentity_t * other )
{
if ( other - > client - > ps . inventory [ index ] )
{
return qtrue ;
}
return qfalse ;
}
extern qboolean INV_GoodieKeyGive ( gentity_t * target ) ;
extern qboolean INV_SecurityKeyGive ( gentity_t * target , const char * keyname ) ;
int Pickup_Holdable ( gentity_t * ent , gentity_t * other )
{
int i , original ;
other - > client - > ps . stats [ STAT_ITEMS ] | = ( 1 < < ent - > item - > giTag ) ;
if ( ent - > item - > giTag = = INV_SECURITY_KEY )
{ //give the key
//FIXME: temp message
gi . SendServerCommand ( NULL , " cp @SP_INGAME_YOU_TOOK_SECURITY_KEY " ) ;
INV_SecurityKeyGive ( other , ent - > message ) ;
}
else if ( ent - > item - > giTag = = INV_GOODIE_KEY )
{ //give the key
//FIXME: temp message
gi . SendServerCommand ( NULL , " cp @SP_INGAME_YOU_TOOK_SUPPLY_KEY " ) ;
INV_GoodieKeyGive ( other ) ;
}
else
{ // Picking up a normal item?
other - > client - > ps . inventory [ ent - > item - > giTag ] + + ;
}
// Got a security key
// Set the inventory select, just in case it hasn't
original = cg . inventorySelect ;
for ( i = 0 ; i < INV_MAX ; i + + )
{
if ( ( cg . inventorySelect < INV_ELECTROBINOCULARS ) | | ( cg . inventorySelect > = INV_MAX ) )
{
cg . inventorySelect = ( INV_MAX - 1 ) ;
}
if ( G_InventorySelectable ( cg . inventorySelect , other ) )
{
return 60 ;
}
cg . inventorySelect + + ;
}
cg . inventorySelect = original ;
return 60 ;
}
//======================================================================
int Add_Ammo2 ( gentity_t * ent , int ammoType , int count )
{
if ( ammoType ! = AMMO_FORCE )
{
ent - > client - > ps . ammo [ ammoType ] + = count ;
// since the ammo is the weapon in this case, picking up ammo should actually give you the weapon
switch ( ammoType )
{
case AMMO_THERMAL :
ent - > client - > ps . stats [ STAT_WEAPONS ] | = ( 1 < < WP_THERMAL ) ;
break ;
case AMMO_DETPACK :
ent - > client - > ps . stats [ STAT_WEAPONS ] | = ( 1 < < WP_DET_PACK ) ;
break ;
case AMMO_TRIPMINE :
ent - > client - > ps . stats [ STAT_WEAPONS ] | = ( 1 < < WP_TRIP_MINE ) ;
break ;
}
if ( ent - > client - > ps . ammo [ ammoType ] > ammoData [ ammoType ] . max )
{
ent - > client - > ps . ammo [ ammoType ] = ammoData [ ammoType ] . max ;
return qfalse ;
}
}
else
{
if ( ent - > client - > ps . forcePower > = ammoData [ ammoType ] . max )
{ //if have full force, just get 25 extra per crystal
ent - > client - > ps . forcePower + = 25 ;
}
else
{ //else if don't have full charge, give full amount, up to max + 25
ent - > client - > ps . forcePower + = count ;
if ( ent - > client - > ps . forcePower > = ammoData [ ammoType ] . max + 25 )
{ //cap at max + 25
ent - > client - > ps . forcePower = ammoData [ ammoType ] . max + 25 ;
}
}
if ( ent - > client - > ps . forcePower > = ammoData [ ammoType ] . max * 2 )
{ //always cap at twice a full charge
ent - > client - > ps . forcePower = ammoData [ ammoType ] . max * 2 ;
return qfalse ; // can't hold any more
}
}
return qtrue ;
}
//-------------------------------------------------------
void Add_Ammo ( gentity_t * ent , int weapon , int count )
{
Add_Ammo2 ( ent , weaponData [ weapon ] . ammoIndex , count ) ;
}
//-------------------------------------------------------
int Pickup_Ammo ( gentity_t * ent , gentity_t * other )
{
int quantity ;
if ( ent - > count ) {
quantity = ent - > count ;
} else {
quantity = ent - > item - > quantity ;
}
Add_Ammo2 ( other , ent - > item - > giTag , quantity ) ;
return 30 ;
}
//======================================================================
void Add_Batteries ( gentity_t * ent , int * count )
{
if ( ent - > client & & ent - > client - > ps . batteryCharge < MAX_BATTERIES & & * count )
{
if ( * count + ent - > client - > ps . batteryCharge > MAX_BATTERIES )
{
// steal what we need, then leave the rest for later
* count - = ( MAX_BATTERIES - ent - > client - > ps . batteryCharge ) ;
ent - > client - > ps . batteryCharge = MAX_BATTERIES ;
}
else
{
// just drain all of the batteries
ent - > client - > ps . batteryCharge + = * count ;
* count = 0 ;
}
G_AddEvent ( ent , EV_BATTERIES_CHARGED , 0 ) ;
}
}
//-------------------------------------------------------
int Pickup_Battery ( gentity_t * ent , gentity_t * other )
{
int quantity ;
if ( ent - > count )
{
quantity = ent - > count ;
}
else
{
quantity = ent - > item - > quantity ;
}
// There may be some left over in quantity if the player is close to full, but with pickup items, this amount will just be lost
Add_Batteries ( other , & quantity ) ;
return 30 ;
}
//======================================================================
void G_CopySaberItemValues ( gentity_t * pickUpSaber , gentity_t * oldSaber )
{
if ( oldSaber & & pickUpSaber )
{
oldSaber - > spawnflags = pickUpSaber - > spawnflags ;
oldSaber - > random = pickUpSaber - > random ;
oldSaber - > flags = pickUpSaber - > flags ;
}
}
gentity_t * G_DropSaberItem ( const char * saberType , saber_colors_t saberColor , vec3_t saberPos , vec3_t saberVel , vec3_t saberAngles , gentity_t * copySaber )
{ //turn it into a pick-uppable item!
gentity_t * newItem = NULL ;
if ( saberType
& & saberType [ 0 ] )
{ //have a valid string to use for saberType
newItem = G_Spawn ( ) ;
if ( newItem )
{
newItem - > classname = G_NewString ( " weapon_saber " ) ;
VectorCopy ( saberPos , newItem - > s . origin ) ;
G_SetOrigin ( newItem , newItem - > s . origin ) ;
VectorCopy ( saberAngles , newItem - > s . angles ) ;
G_SetAngles ( newItem , newItem - > s . angles ) ;
newItem - > spawnflags = 128 ; /*ITMSF_USEPICKUP*/
newItem - > spawnflags | = 64 ; /*ITMSF_NOGLOW*/
newItem - > NPC_type = G_NewString ( saberType ) ; //saberType
//FIXME: transfer per-blade color somehow?
newItem - > NPC_targetname = saberColorStringForColor [ saberColor ] ;
newItem - > count = 1 ;
newItem - > flags = FL_DROPPED_ITEM ;
G_SpawnItem ( newItem , FindItemForWeapon ( WP_SABER ) ) ;
newItem - > s . pos . trType = TR_GRAVITY ;
newItem - > s . pos . trTime = level . time ;
VectorCopy ( saberVel , newItem - > s . pos . trDelta ) ;
//newItem->s.eFlags |= EF_BOUNCE_HALF;
//copy some values from another saber, if provided:
G_CopySaberItemValues ( copySaber , newItem ) ;
//don't *think* about calling FinishSpawningItem, just do it!
newItem - > e_ThinkFunc = thinkF_NULL ;
newItem - > nextthink = - 1 ;
FinishSpawningItem ( newItem ) ;
newItem - > delay = level . time + 500 ; //so you can't pick it back up right away
}
}
return newItem ;
}
extern void G_SetSabersFromCVars ( gentity_t * ent ) ;
qboolean Pickup_Saber ( gentity_t * self , qboolean hadSaber , gentity_t * pickUpSaber )
{
//NOTE: loopAnim = saberSolo, alt_fire = saberLeftHand, NPC_type = saberType, NPC_targetname = saberColor
qboolean foundIt = qfalse ;
if ( ! pickUpSaber | | ! self | | ! self - > client )
{
return qfalse ;
}
//G_RemoveWeaponModels( ent );//???
if ( Q_stricmp ( " player " , pickUpSaber - > NPC_type ) = = 0 )
{ //"player" means use cvar info
G_SetSabersFromCVars ( self ) ;
foundIt = qtrue ;
}
else
{
saberInfo_t newSaber = { 0 } ;
qboolean swapSabers = qfalse ;
if ( self - > client - > ps . weapon = = WP_SABER
& & self - > client - > ps . weaponTime > 0 )
{ //can't pick up a new saber while the old one is busy (also helps to work as a debouncer so you don't swap out sabers rapidly when touching more than one at a time)
return qfalse ;
}
if ( pickUpSaber - > count = = 1
& & g_saberPickuppableDroppedSabers - > integer )
{
swapSabers = qtrue ;
}
if ( WP_SaberParseParms ( pickUpSaber - > NPC_type , & newSaber ) )
{ //successfully found a saber .sab entry to use
int saberNum = 0 ;
qboolean removeLeftSaber = qfalse ;
if ( pickUpSaber - > alt_fire )
{ //always go in the left hand
if ( ! hadSaber )
{ //can't have a saber only in your left hand!
return qfalse ;
}
saberNum = 1 ;
//just in case...
removeLeftSaber = qtrue ;
}
else if ( ! hadSaber )
{ //don't have a saber at all yet, put it in our right hand
saberNum = 0 ;
//just in case...
removeLeftSaber = qtrue ;
}
else if ( pickUpSaber - > loopAnim //only supposed to use this one saber when grab this pickup
| | ( newSaber . saberFlags & SFL_TWO_HANDED ) //new saber is two-handed
| | ( hadSaber & & ( self - > client - > ps . saber [ 0 ] . saberFlags & SFL_TWO_HANDED ) ) ) //old saber is two-handed
{ //replace the old right-hand saber and remove the left hand one
saberNum = 0 ;
removeLeftSaber = qtrue ;
}
else
{ //have, at least, a saber in our right hand and the new one could go in either left or right hand
if ( self - > client - > ps . dualSabers )
{ //I already have 2 sabers
vec3_t dir2Saber , rightDir ;
//to determine which one to replace, see which side of me it's on
VectorSubtract ( pickUpSaber - > currentOrigin , self - > currentOrigin , dir2Saber ) ;
dir2Saber [ 2 ] = 0 ;
AngleVectors ( self - > currentAngles , NULL , rightDir , NULL ) ;
rightDir [ 2 ] = 0 ;
if ( DotProduct ( rightDir , dir2Saber ) > 0 )
{
saberNum = 0 ;
}
else
{
saberNum = 1 ;
//just in case...
removeLeftSaber = qtrue ;
}
}
else
{ //just add it as a second saber
saberNum = 1 ;
//just in case...
removeLeftSaber = qtrue ;
}
}
if ( saberNum = = 0 )
{ //want to reach out with right hand
if ( self - > client - > ps . torsoAnim = = BOTH_BUTTON_HOLD )
{ //but only if already playing the pickup with left hand anim...
NPC_SetAnim ( self , SETANIM_TORSO , BOTH_SABERPULL , SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD ) ;
}
if ( swapSabers )
{ //drop first one where the one we're picking up is
G_DropSaberItem ( self - > client - > ps . saber [ saberNum ] . name , self - > client - > ps . saber [ saberNum ] . blade [ 0 ] . color , pickUpSaber - > currentOrigin , ( float * ) vec3_origin , pickUpSaber - > currentAngles , pickUpSaber ) ;
if ( removeLeftSaber )
{ //drop other one at my origin
G_DropSaberItem ( self - > client - > ps . saber [ 1 ] . name , self - > client - > ps . saber [ 1 ] . blade [ 0 ] . color , self - > currentOrigin , ( float * ) vec3_origin , self - > currentAngles , pickUpSaber ) ;
}
}
}
else
{
if ( swapSabers )
{
G_DropSaberItem ( self - > client - > ps . saber [ saberNum ] . name , self - > client - > ps . saber [ saberNum ] . blade [ 0 ] . color , pickUpSaber - > currentOrigin , ( float * ) vec3_origin , pickUpSaber - > currentAngles , pickUpSaber ) ;
}
}
if ( removeLeftSaber )
{
WP_RemoveSaber ( self , 1 ) ;
}
WP_SetSaber ( self , saberNum , pickUpSaber - > NPC_type ) ;
WP_SaberInitBladeData ( self ) ;
if ( self - > client - > ps . saber [ saberNum ] . stylesLearned )
{
self - > client - > ps . saberStylesKnown | = self - > client - > ps . saber [ saberNum ] . stylesLearned ;
}
if ( self - > client - > ps . saber [ saberNum ] . singleBladeStyle )
{
self - > client - > ps . saberStylesKnown | = self - > client - > ps . saber [ saberNum ] . singleBladeStyle ;
}
if ( pickUpSaber - > NPC_targetname ! = NULL )
{ //NPC_targetname = saberColor
saber_colors_t saber_color = TranslateSaberColor ( pickUpSaber - > NPC_targetname ) ;
for ( int bladeNum = 0 ; bladeNum < MAX_BLADES ; bladeNum + + )
{
self - > client - > ps . saber [ saberNum ] . blade [ bladeNum ] . color = saber_color ;
}
}
if ( self - > client - > ps . torsoAnim = = BOTH_BUTTON_HOLD
| | self - > client - > ps . torsoAnim = = BOTH_SABERPULL )
{ //don't let them attack right away, force them to finish the anim
self - > client - > ps . weaponTime = self - > client - > ps . torsoAnimTimer ;
}
foundIt = qtrue ;
}
WP_SaberFreeStrings ( newSaber ) ;
}
return foundIt ;
}
extern void CG_ChangeWeapon ( int num ) ;
int Pickup_Weapon ( gentity_t * ent , gentity_t * other )
{
int quantity ;
qboolean hadWeapon = qfalse ;
/*
if ( ent - > count | | ( ent - > activator & & ! ent - > activator - > s . number ) )
{
quantity = ent - > count ;
}
else
{
quantity = ent - > item - > quantity ;
}
*/
// dropped items are always picked up
if ( ent - > flags & FL_DROPPED_ITEM )
{
quantity = ent - > count ;
}
else
{ //wasn't dropped
quantity = ent - > item - > quantity ? ent - > item - > quantity : 50 ;
}
// add the weapon
if ( other - > client - > ps . stats [ STAT_WEAPONS ] & ( 1 < < ent - > item - > giTag ) )
{
hadWeapon = qtrue ;
}
other - > client - > ps . stats [ STAT_WEAPONS ] | = ( 1 < < ent - > item - > giTag ) ;
if ( ent - > item - > giTag = = WP_SABER & & ( ! hadWeapon | | ent - > NPC_type ! = NULL ) )
{ //didn't have a saber or it is specifying a certain kind of saber to use
if ( ! Pickup_Saber ( other , hadWeapon , ent ) )
{
return 0 ;
}
}
if ( other - > s . number )
{ //NPC
if ( other - > s . weapon = = WP_NONE
| | ent - > item - > giTag = = WP_SABER )
{ //NPC with no weapon picked up a weapon, change to this weapon
//FIXME: clear/set the alt-fire flag based on the picked up weapon and my class?
other - > client - > ps . weapon = ent - > item - > giTag ;
other - > client - > ps . weaponstate = WEAPON_RAISING ;
ChangeWeapon ( other , ent - > item - > giTag ) ;
if ( ent - > item - > giTag = = WP_SABER )
{
other - > client - > ps . SaberActivate ( ) ;
WP_SaberAddG2SaberModels ( other ) ;
}
else
{
G_CreateG2AttachedWeaponModel ( other , weaponData [ ent - > item - > giTag ] . weaponMdl , other - > handRBolt , 0 ) ;
}
}
}
if ( ent - > item - > giTag = = WP_SABER )
{ //picked up a saber
if ( other - > s . weapon ! = WP_SABER )
{ //player picking up saber
other - > client - > ps . weapon = WP_SABER ;
other - > client - > ps . weaponstate = WEAPON_RAISING ;
if ( other - > s . number < MAX_CLIENTS )
{ //make sure the cgame-side knows this
CG_ChangeWeapon ( WP_SABER ) ;
}
else
{ //make sure the cgame-side knows this
ChangeWeapon ( other , WP_SABER ) ;
}
}
if ( ! other - > client - > ps . SaberActive ( ) )
{ //turn it/them on!
other - > client - > ps . SaberActivate ( ) ;
}
}
if ( quantity )
{
// Give ammo
Add_Ammo ( other , ent - > item - > giTag , quantity ) ;
}
return 5 ;
}
//======================================================================
int ITM_AddHealth ( gentity_t * ent , int count )
{
ent - > health + = count ;
if ( ent - > health > ent - > client - > ps . stats [ STAT_MAX_HEALTH ] ) // Past max health
{
ent - > health = ent - > client - > ps . stats [ STAT_MAX_HEALTH ] ;
return qfalse ;
}
return qtrue ;
}
int Pickup_Health ( gentity_t * ent , gentity_t * other ) {
int max ;
int quantity ;
max = other - > client - > ps . stats [ STAT_MAX_HEALTH ] ;
if ( ent - > count ) {
quantity = ent - > count ;
} else {
quantity = ent - > item - > quantity ;
}
other - > health + = quantity ;
if ( other - > health > max ) {
other - > health = max ;
}
if ( ent - > item - > giTag = = 100 ) { // mega health respawns slow
return 120 ;
}
return 30 ;
}
//======================================================================
int ITM_AddArmor ( gentity_t * ent , int count )
{
ent - > client - > ps . stats [ STAT_ARMOR ] + = count ;
if ( ent - > client - > ps . stats [ STAT_ARMOR ] > ent - > client - > ps . stats [ STAT_MAX_HEALTH ] )
{
ent - > client - > ps . stats [ STAT_ARMOR ] = ent - > client - > ps . stats [ STAT_MAX_HEALTH ] ;
return qfalse ;
}
return qtrue ;
}
int Pickup_Armor ( gentity_t * ent , gentity_t * other ) {
// make sure that the shield effect is on
other - > client - > ps . powerups [ PW_BATTLESUIT ] = Q3_INFINITE ;
other - > client - > ps . stats [ STAT_ARMOR ] + = ent - > item - > quantity ;
if ( other - > client - > ps . stats [ STAT_ARMOR ] > other - > client - > ps . stats [ STAT_MAX_HEALTH ] ) {
other - > client - > ps . stats [ STAT_ARMOR ] = other - > client - > ps . stats [ STAT_MAX_HEALTH ] ;
}
return 30 ;
}
//======================================================================
int Pickup_Holocron ( gentity_t * ent , gentity_t * other )
{
int forcePower = ent - > item - > giTag ;
int forceLevel = ent - > count ;
// check if out of range
if ( forceLevel < 0 | | forceLevel > = NUM_FORCE_POWER_LEVELS )
{
gi . Printf ( " Pickup_Holocron : count %d not in valid range \n " , forceLevel ) ;
return 1 ;
}
// don't pick up if already known AND your level is higher than pickup level
if ( ( other - > client - > ps . forcePowersKnown & ( 1 < < forcePower ) ) )
{
//don't pickup if item is lower than current level
if ( other - > client - > ps . forcePowerLevel [ forcePower ] > = forceLevel )
{
return 1 ;
}
}
other - > client - > ps . forcePowerLevel [ forcePower ] = forceLevel ;
other - > client - > ps . forcePowersKnown | = ( 1 < < forcePower ) ;
missionInfo_Updated = qtrue ; // Activate flashing text
gi . cvar_set ( " cg_updatedDataPadForcePower1 " , va ( " %d " , forcePower + 1 ) ) ; // The +1 is offset in the print routine.
cg_updatedDataPadForcePower1 . integer = forcePower + 1 ;
gi . cvar_set ( " cg_updatedDataPadForcePower2 " , " 0 " ) ; // The +1 is offset in the print routine.
cg_updatedDataPadForcePower2 . integer = 0 ;
gi . cvar_set ( " cg_updatedDataPadForcePower3 " , " 0 " ) ; // The +1 is offset in the print routine.
cg_updatedDataPadForcePower3 . integer = 0 ;
return 1 ;
}
//======================================================================
/*
= = = = = = = = = = = = = = =
RespawnItem
= = = = = = = = = = = = = = =
*/
void RespawnItem ( gentity_t * ent ) {
}
qboolean CheckItemCanBePickedUpByNPC ( gentity_t * item , gentity_t * pickerupper )
{
if ( ( item - > flags & FL_DROPPED_ITEM )
& & item - > activator ! = & g_entities [ 0 ]
& & pickerupper - > s . number
& & pickerupper - > s . weapon = = WP_NONE
& & pickerupper - > enemy
& & pickerupper - > painDebounceTime < level . time
& & pickerupper - > NPC & & pickerupper - > NPC - > surrenderTime < level . time //not surrendering
& & ! ( pickerupper - > NPC - > scriptFlags & SCF_FORCED_MARCH ) //not being forced to march
& & item - > item - > giTag ! = INV_SECURITY_KEY )
{ //non-player, in combat, picking up a dropped item that does NOT belong to the player and it *not* a security key
if ( level . time - item - > s . time < 3000 ) //was 5000
{
return qfalse ;
}
return qtrue ;
}
return qfalse ;
}
qboolean G_CanPickUpWeapons ( gentity_t * other )
{
if ( ! other | | ! other - > client )
{
return qfalse ;
}
switch ( other - > client - > NPC_class )
{
case CLASS_ATST :
case CLASS_GONK :
case CLASS_MARK1 :
case CLASS_MARK2 :
case CLASS_MOUSE :
case CLASS_PROBE :
case CLASS_PROTOCOL :
case CLASS_R2D2 :
case CLASS_R5D2 :
case CLASS_SEEKER :
case CLASS_REMOTE :
case CLASS_RANCOR :
case CLASS_WAMPA :
case CLASS_JAWA : //FIXME: in some cases it's okay?
case CLASS_UGNAUGHT : //FIXME: in some cases it's okay?
case CLASS_SENTRY :
return qfalse ;
break ;
}
return qtrue ;
}
/*
= = = = = = = = = = = = = = =
Touch_Item
= = = = = = = = = = = = = = =
*/
extern cvar_t * g_timescale ;
void Touch_Item ( gentity_t * ent , gentity_t * other , trace_t * trace ) {
int respawn = 0 ;
if ( ! other - > client )
return ;
if ( other - > health < 1 )
return ; // dead people can't pickup
if ( other - > client - > ps . pm_time > 0 )
{ //cant pick up when out of control
return ;
}
// NPCs can pick it up
if ( ( ent - > spawnflags & ITMSF_ALLOWNPC ) & & ( ! other - > s . number ) )
{
return ;
}
// Players cannot pick it up
if ( ( ent - > spawnflags & ITMSF_NOPLAYER ) & & ( other - > s . number ) )
{
return ;
}
if ( ent - > noDamageTeam ! = TEAM_FREE & & other - > client - > playerTeam ! = ent - > noDamageTeam )
{ //only one team can pick it up
return ;
}
if ( ! G_CanPickUpWeapons ( other ) )
{ //FIXME: some flag would be better
//droids can't pick up items/weapons!
return ;
}
//FIXME: need to make them run toward a dropped weapon when fleeing without one?
//FIXME: need to make them come out of flee mode when pick up their old weapon?
if ( CheckItemCanBePickedUpByNPC ( ent , other ) )
{
if ( other - > NPC & & other - > NPC - > goalEntity & & other - > NPC - > goalEntity = = ent )
{ //they were running to pick me up, they did, so clear goal
other - > NPC - > goalEntity = NULL ;
other - > NPC - > squadState = SQUAD_STAND_AND_SHOOT ;
NPCInfo - > tempBehavior = BS_DEFAULT ;
TIMER_Set ( other , " flee " , - 1 ) ;
}
else
{
return ;
}
}
else if ( ! ( ent - > spawnflags & ITMSF_ALLOWNPC ) )
{ // NPCs cannot pick it up
if ( other - > s . number ! = 0 )
{ // Not the player?
return ;
}
}
// the same pickup rules are used for client side and server side
if ( ! BG_CanItemBeGrabbed ( & ent - > s , & other - > client - > ps ) ) {
return ;
}
if ( other - > client )
{
if ( ( other - > client - > ps . eFlags & EF_FORCE_GRIPPED ) | | ( other - > client - > ps . eFlags & EF_FORCE_DRAINED ) )
{ //can't pick up anything while being gripped
return ;
}
if ( PM_InKnockDown ( & other - > client - > ps ) & & ! PM_InGetUp ( & other - > client - > ps ) )
{ //can't pick up while in a knockdown
return ;
}
}
if ( ! ent - > item ) { //not an item!
gi . Printf ( " Touch_Item: %s is not an item! \n " , ent - > classname ) ;
return ;
}
if ( ent - > item - > giType = = IT_WEAPON
& & ent - > item - > giTag = = WP_SABER )
{ //a saber
if ( ent - > delay > level . time )
{ //just picked it up, don't pick up again right away
return ;
}
}
if ( other - > s . number < MAX_CLIENTS
& & ( ent - > spawnflags & ITMSF_USEPICKUP ) )
{ //only if player is holing use button
if ( ! ( other - > client - > usercmd . buttons & BUTTON_USE ) )
{ //not holding use?
return ;
}
}
qboolean bHadWeapon = qfalse ;
// call the item-specific pickup function
switch ( ent - > item - > giType )
{
case IT_WEAPON :
if ( other - > NPC & & other - > s . weapon = = WP_NONE )
{ //Make them duck and sit here for a few seconds
int pickUpTime = Q_irand ( 1000 , 3000 ) ;
TIMER_Set ( other , " duck " , pickUpTime ) ;
TIMER_Set ( other , " roamTime " , pickUpTime ) ;
TIMER_Set ( other , " stick " , pickUpTime ) ;
TIMER_Set ( other , " verifyCP " , pickUpTime ) ;
TIMER_Set ( other , " attackDelay " , 600 ) ;
respawn = 0 ;
}
if ( other - > client - > ps . stats [ STAT_WEAPONS ] & ( 1 < < ent - > item - > giTag ) )
{
bHadWeapon = qtrue ;
}
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_HOLDABLE :
respawn = Pickup_Holdable ( ent , other ) ;
break ;
case IT_BATTERY :
respawn = Pickup_Battery ( ent , other ) ;
break ;
case IT_HOLOCRON :
respawn = Pickup_Holocron ( ent , other ) ;
break ;
default :
return ;
}
if ( ! respawn )
{
return ;
}
// play the normal pickup sound
if ( ! other - > s . number & & g_timescale - > value < 1.0f )
{ //SIGH... with timescale on, you lose events left and right
extern void CG_ItemPickup ( int itemNum , qboolean bHadItem ) ;
// but we're SP so we'll cheat
cgi_S_StartSound ( NULL , other - > s . number , CHAN_AUTO , cgi_S_RegisterSound ( ent - > item - > pickup_sound ) ) ;
// show icon and name on status bar
CG_ItemPickup ( ent - > s . modelindex , bHadWeapon ) ;
}
else
{
if ( bHadWeapon )
{
G_AddEvent ( other , EV_ITEM_PICKUP , - ent - > s . modelindex ) ;
}
else
{
G_AddEvent ( other , EV_ITEM_PICKUP , ent - > s . modelindex ) ;
}
}
// fire item targets
G_UseTargets ( ent , other ) ;
if ( ent - > item - > giType = = IT_WEAPON
& & ent - > item - > giTag = = WP_SABER )
{ //a saber that was picked up
if ( ent - > count < 0 )
{ //infinite supply
ent - > delay = level . time + 500 ;
return ;
}
ent - > count - - ;
if ( ent - > count > 0 )
{ //still have more to pick up
ent - > delay = level . time + 500 ;
return ;
}
}
// wait of -1 will not respawn
// if ( ent->wait == -1 )
{
//why not just remove me?
G_FreeEntity ( ent ) ;
/*
//NOTE: used to do this: (for respawning?)
ent - > svFlags | = SVF_NOCLIENT ;
ent - > s . eFlags | = EF_NODRAW ;
ent - > contents = 0 ;
ent - > unlinkAfterEvent = qtrue ;
*/
return ;
}
}
//======================================================================
/*
= = = = = = = = = = = = = = = =
LaunchItem
Spawns an item and tosses it forward
= = = = = = = = = = = = = = = =
*/
gentity_t * LaunchItem ( gitem_t * item , const vec3_t origin , const vec3_t velocity , char * target ) {
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 = G_NewString ( item - > classname ) ; //copy it so it can be freed safely
dropped - > item = item ;
// try using the "correct" mins/maxs first
VectorSet ( dropped - > mins , item - > mins [ 0 ] , item - > mins [ 1 ] , item - > mins [ 2 ] ) ;
VectorSet ( dropped - > maxs , item - > maxs [ 0 ] , item - > maxs [ 1 ] , item - > maxs [ 2 ] ) ;
if ( ( ! dropped - > mins [ 0 ] & & ! dropped - > mins [ 1 ] & & ! dropped - > mins [ 2 ] ) & &
( ! dropped - > maxs [ 0 ] & & ! dropped - > maxs [ 1 ] & & ! dropped - > maxs [ 2 ] ) )
{
VectorSet ( dropped - > maxs , ITEM_RADIUS , ITEM_RADIUS , ITEM_RADIUS ) ;
VectorScale ( dropped - > maxs , - 1 , dropped - > mins ) ;
}
dropped - > contents = CONTENTS_TRIGGER | CONTENTS_ITEM ; //CONTENTS_TRIGGER;//not CONTENTS_BODY for dropped items, don't need to ID them
if ( target & & target [ 0 ] )
{
dropped - > target = G_NewString ( target ) ;
}
else
{
// if not targeting something, auto-remove after 30 seconds
// only if it's NOT a security or goodie key
if ( dropped - > item - > giTag ! = INV_SECURITY_KEY )
{
dropped - > e_ThinkFunc = thinkF_G_FreeEntity ;
dropped - > nextthink = level . time + 30000 ;
}
if ( dropped - > item - > giType = = IT_AMMO & & dropped - > item - > giTag = = AMMO_FORCE )
{
dropped - > nextthink = - 1 ;
dropped - > e_ThinkFunc = thinkF_NULL ;
}
}
dropped - > e_TouchFunc = touchF_Touch_Item ;
if ( item - > giType = = IT_WEAPON )
{
// give weapon items zero pitch, a random yaw, and rolled onto their sides...but would be bad to do this for a bowcaster
if ( item - > giTag ! = WP_BOWCASTER
& & item - > giTag ! = WP_THERMAL
& & item - > giTag ! = WP_TRIP_MINE
& & item - > giTag ! = WP_DET_PACK )
{
VectorSet ( dropped - > s . angles , 0 , crandom ( ) * 180 , 90.0f ) ;
G_SetAngles ( dropped , dropped - > s . angles ) ;
}
}
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 ;
dropped - > flags = FL_DROPPED_ITEM ;
gi . 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 , qboolean copytarget ) {
gentity_t * dropped = NULL ;
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 ;
if ( copytarget )
{
dropped = LaunchItem ( item , ent - > s . pos . trBase , velocity , ent - > opentarget ) ;
}
else
{
dropped = LaunchItem ( item , ent - > s . pos . trBase , velocity , NULL ) ;
}
dropped - > activator = ent ; //so we know who we belonged to so they can pick it back up later
dropped - > s . time = level . time ; //mark this time so we aren't picked up instantly by the guy who dropped us
return dropped ;
}
/*
= = = = = = = = = = = = = = = =
Use_Item
Respawn the item
= = = = = = = = = = = = = = = =
*/
void Use_Item ( gentity_t * ent , gentity_t * other , gentity_t * activator )
{
if ( ( ent - > svFlags & SVF_PLAYER_USABLE ) & & other & & ! other - > s . number )
{ //used directly by the player, pick me up
if ( ( ent - > spawnflags & ITMSF_USEPICKUP ) )
{ //player has to be touching me and hit use to pick it up, so don't allow this
if ( ! G_BoundsOverlap ( ent - > absmin , ent - > absmax , other - > absmin , other - > absmax ) )
{ //not touching
return ;
}
}
GEntity_TouchFunc ( ent , other , NULL ) ;
}
else
{ //use me
if ( ent - > spawnflags & 32 ) // invisible
{
// If it was invisible, first use makes it visible....
ent - > s . eFlags & = ~ EF_NODRAW ;
ent - > contents = CONTENTS_TRIGGER | CONTENTS_ITEM ;
ent - > spawnflags & = ~ 32 ;
return ;
}
G_ActivateBehavior ( ent , BSET_USE ) ;
RespawnItem ( ent ) ;
}
}
//======================================================================
/*
= = = = = = = = = = = = = = = =
FinishSpawningItem
Traces down to find where an item should rest , instead of letting them
free fall from their spawn points
= = = = = = = = = = = = = = = =
*/
extern int delayedShutDown ;
extern cvar_t * g_saber ;
void FinishSpawningItem ( gentity_t * ent ) {
trace_t tr ;
vec3_t dest ;
gitem_t * item ;
int itemNum ;
itemNum = 1 ;
for ( item = bg_itemlist + 1 ; item - > classname ; item + + , itemNum + + )
{
if ( ! strcmp ( item - > classname , ent - > classname ) )
{
break ;
}
}
// Set bounding box for item
VectorSet ( ent - > mins , item - > mins [ 0 ] , item - > mins [ 1 ] , item - > mins [ 2 ] ) ;
VectorSet ( ent - > maxs , item - > maxs [ 0 ] , item - > maxs [ 1 ] , item - > maxs [ 2 ] ) ;
if ( ( ! ent - > mins [ 0 ] & & ! ent - > mins [ 1 ] & & ! ent - > mins [ 2 ] ) & &
( ! ent - > maxs [ 0 ] & & ! ent - > maxs [ 1 ] & & ! ent - > maxs [ 2 ] ) )
{
VectorSet ( ent - > mins , - ITEM_RADIUS , - ITEM_RADIUS , - 2 ) ; //to match the comments in the items.dat file!
VectorSet ( ent - > maxs , ITEM_RADIUS , ITEM_RADIUS , ITEM_RADIUS ) ;
}
if ( ( item - > quantity ) & & ( item - > giType = = IT_AMMO ) )
{
ent - > count = item - > quantity ;
}
if ( ( item - > quantity ) & & ( item - > giType = = IT_BATTERY ) )
{
ent - > count = item - > quantity ;
}
ent - > s . radius = 20 ;
VectorSet ( ent - > s . modelScale , 1.0f , 1.0f , 1.0f ) ;
if ( ent - > item - > giType = = IT_WEAPON
& & ent - > item - > giTag = = WP_SABER
& & ent - > NPC_type
& & ent - > NPC_type [ 0 ] )
{
saberInfo_t itemSaber ;
if ( Q_stricmp ( " player " , ent - > NPC_type ) = = 0
& & g_saber - > string
& & g_saber - > string [ 0 ]
& & Q_stricmp ( " none " , g_saber - > string )
& & Q_stricmp ( " NULL " , g_saber - > string ) )
{ //player's saber
WP_SaberParseParms ( g_saber - > string , & itemSaber ) ;
}
else
{ //specific saber
WP_SaberParseParms ( ent - > NPC_type , & itemSaber ) ;
}
//NOTE: should I keep this string around for any reason? Will I ever need it later?
//ent->??? = G_NewString( itemSaber.model );
gi . G2API_InitGhoul2Model ( ent - > ghoul2 , itemSaber . model , G_ModelIndex ( itemSaber . model ) ) ;
WP_SaberFreeStrings ( itemSaber ) ;
}
else
{
gi . G2API_InitGhoul2Model ( ent - > ghoul2 , ent - > item - > world_model , G_ModelIndex ( ent - > item - > world_model ) ) ;
}
// Set crystal ammo amount based on skill level
/* if ((itemNum == ITM_AMMO_CRYSTAL_BORG) ||
( itemNum = = ITM_AMMO_CRYSTAL_DN ) | |
( itemNum = = ITM_AMMO_CRYSTAL_FORGE ) | |
( itemNum = = ITM_AMMO_CRYSTAL_SCAVENGER ) | |
( itemNum = = ITM_AMMO_CRYSTAL_STASIS ) )
{
CrystalAmmoSettings ( ent ) ;
}
*/
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 - > contents = CONTENTS_TRIGGER | CONTENTS_ITEM ; //CONTENTS_BODY;//CONTENTS_TRIGGER|
ent - > e_TouchFunc = touchF_Touch_Item ;
// useing an item causes it to respawn
ent - > e_UseFunc = useF_Use_Item ;
ent - > svFlags | = SVF_PLAYER_USABLE ; //so player can pick it up
// Hang in air?
ent - > s . origin [ 2 ] + = 1 ; //just to get it off the damn ground because coplanar = insolid
if ( ( ent - > spawnflags & ITMSF_SUSPEND )
| | ( ent - > flags & FL_DROPPED_ITEM ) )
{
// suspended
G_SetOrigin ( ent , ent - > s . origin ) ;
}
else
{
// drop to floor
VectorSet ( dest , ent - > s . origin [ 0 ] , ent - > s . origin [ 1 ] , MIN_WORLD_COORD ) ;
gi . trace ( & tr , ent - > s . origin , ent - > mins , ent - > maxs , dest , ent - > s . number , MASK_SOLID | CONTENTS_PLAYERCLIP ) ;
if ( tr . startsolid )
{
if ( & g_entities [ tr . entityNum ] ! = NULL )
{
gi . Printf ( S_COLOR_RED " FinishSpawningItem: removing %s startsolid at %s (in a %s) \n " , ent - > classname , vtos ( ent - > s . origin ) , g_entities [ tr . entityNum ] . classname ) ;
}
else
{
gi . Printf ( S_COLOR_RED " FinishSpawningItem: removing %s startsolid at %s (in a %s) \n " , ent - > classname , vtos ( ent - > s . origin ) ) ;
}
assert ( 0 & & " item starting in solid " ) ;
if ( ! g_entities [ ENTITYNUM_WORLD ] . s . radius ) { //not a region
delayedShutDown = level . time + 100 ;
}
G_FreeEntity ( ent ) ;
return ;
}
// allow to ride movers
ent - > s . groundEntityNum = tr . entityNum ;
G_SetOrigin ( ent , tr . endpos ) ;
}
/* ? don't need this
// team slaves and targeted items aren't present at start
if ( ( ent - > flags & FL_TEAMSLAVE ) | | ent - > targetname ) {
ent - > s . eFlags | = EF_NODRAW ;
ent - > contents = 0 ;
return ;
}
*/
if ( ent - > spawnflags & ITMSF_INVISIBLE ) // invisible
{
ent - > s . eFlags | = EF_NODRAW ;
ent - > contents = 0 ;
}
if ( ent - > spawnflags & ITMSF_NOTSOLID ) // not solid
{
ent - > contents = 0 ;
}
if ( ( ent - > spawnflags & ITMSF_STATIONARY ) )
{ //can't be pushed around
ent - > flags | = FL_NO_KNOCKBACK ;
}
if ( ( ent - > flags & FL_DROPPED_ITEM ) )
{ //go away after 30 seconds
ent - > e_ThinkFunc = thinkF_G_FreeEntity ;
ent - > nextthink = level . time + 30000 ;
}
gi . linkentity ( ent ) ;
}
char itemRegistered [ MAX_ITEMS + 1 ] ;
/*
= = = = = = = = = = = = = =
ClearRegisteredItems
= = = = = = = = = = = = = =
*/
void ClearRegisteredItems ( void ) {
memset ( itemRegistered , ' 0 ' , bg_numItems ) ;
itemRegistered [ bg_numItems ] = 0 ;
//these are given in g_client, ClientSpawn(), but MUST be registered HERE, BEFORE cgame starts.
//RegisterItem( FindItemForWeapon( WP_NONE ) ); //has no item
RegisterItem ( FindItemForInventory ( INV_ELECTROBINOCULARS ) ) ;
//RegisterItem( FindItemForInventory( INV_BACTA_CANISTER ));
// saber or baton is cached in SP_info_player_deathmatch now.
extern void Player_CacheFromPrevLevel ( void ) ; //g_client.cpp
Player_CacheFromPrevLevel ( ) ; //reads from transition carry-over;
}
/*
= = = = = = = = = = = = = = =
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 ] = ' 1 ' ;
gi . SetConfigstring ( CS_ITEMS , itemRegistered ) ; //Write the needed items to a config string
}
/*
= = = = = = = = = = = = = = =
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 ;
gi . Printf ( " %i items registered \n " , count ) ;
gi . SetConfigstring ( CS_ITEMS , string ) ;
*/
gi . SetConfigstring ( CS_ITEMS , itemRegistered ) ;
}
/*
= = = = = = = = = = = =
item_spawn_use
if an item is given a targetname , it will be spawned in when used
= = = = = = = = = = = =
*/
void item_spawn_use ( gentity_t * self , gentity_t * other , gentity_t * activator )
//-----------------------------------------------------------------------------
{
self - > nextthink = level . time + 50 ;
self - > e_ThinkFunc = thinkF_FinishSpawningItem ;
// I could be fancy and add a count or something like that to be able to spawn the item numerous times...
self - > e_UseFunc = NULL ;
}
/*
= = = = = = = = = = = =
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 ) ;
ent - > item = item ;
// targetname indicates they want to spawn it later
if ( ent - > targetname )
{
ent - > e_UseFunc = useF_item_spawn_use ;
}
else
{ // some movers spawn on the second frame, so delay item
// spawns until the third frame so they can ride trains
ent - > nextthink = level . time + START_TIME_MOVERS_SPAWNED + 50 ;
ent - > e_ThinkFunc = thinkF_FinishSpawningItem ;
}
ent - > physicsBounce = 0.50 ; // items are bouncy
// Set a default infoString text color
// NOTE: if we want to do cool cross-hair colors for items, we can just modify this, but for now, don't do it
VectorSet ( ent - > startRGBA , 1.0f , 1.0f , 1.0f ) ;
if ( ent - > team & & ent - > team [ 0 ] )
{
ent - > noDamageTeam = ( team_t ) GetIDForString ( TeamTable , ent - > team ) ;
if ( ent - > noDamageTeam = = TEAM_FREE )
{
G_Error ( " team name %s not recognized \n " , ent - > team ) ;
}
}
if ( ent - > item
& & ent - > item - > giType = = IT_WEAPON
& & ent - > item - > giTag = = WP_SABER )
{ //weapon_saber item
if ( ! ent - > count )
{ //can only pick up once
ent - > count = 1 ;
}
}
ent - > team = NULL ;
}
/*
= = = = = = = = = = = = = = = =
G_BounceItem
= = = = = = = = = = = = = = = =
*/
void G_BounceItem ( gentity_t * ent , trace_t * trace ) {
vec3_t velocity ;
float dot ;
int hitTime ;
qboolean droppedSaber = qtrue ;
if ( ent - > item
& & ent - > item - > giType = = IT_WEAPON
& & ent - > item - > giTag = = WP_SABER
& & ( ent - > flags & FL_DROPPED_ITEM ) )
{
droppedSaber = qtrue ;
}
// reflect the velocity on the trace plane
hitTime = level . previousTime + ( level . time - level . previousTime ) * trace - > fraction ;
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 ) ;
if ( droppedSaber )
{ //a dropped saber item
//FIXME: use NPC_type (as saberType) to get proper bounce sound?
WP_SaberFallSound ( NULL , ent ) ;
}
// check for stop
if ( trace - > plane . normal [ 2 ] > 0 & & ent - > s . pos . trDelta [ 2 ] < 40 )
{ //stop
G_SetOrigin ( ent , trace - > endpos ) ;
ent - > s . groundEntityNum = trace - > entityNum ;
if ( droppedSaber )
{ //a dropped saber item
//stop rotation
VectorClear ( ent - > s . apos . trDelta ) ;
ent - > currentAngles [ PITCH ] = SABER_PITCH_HACK ;
ent - > currentAngles [ ROLL ] = 0 ;
if ( ent - > NPC_type
& & ent - > NPC_type [ 0 ] )
{ //we have a valid saber for this
saberInfo_t saber ;
if ( WP_SaberParseParms ( ent - > NPC_type , & saber ) )
{
if ( ( saber . saberFlags & SFL_BOLT_TO_WRIST ) )
{
ent - > currentAngles [ PITCH ] = 0 ;
}
}
}
pitch_roll_for_slope ( ent , trace - > plane . normal , ent - > currentAngles , qtrue ) ;
G_SetAngles ( ent , ent - > currentAngles ) ;
}
return ;
}
//bounce
if ( droppedSaber )
{ //a dropped saber item
//change rotation
VectorCopy ( ent - > currentAngles , ent - > s . apos . trBase ) ;
ent - > s . apos . trType = TR_LINEAR ;
ent - > s . apos . trTime = level . time ;
VectorSet ( ent - > s . apos . trDelta , Q_irand ( - 300 , 300 ) , Q_irand ( - 300 , 300 ) , Q_irand ( - 300 , 300 ) ) ;
}
VectorAdd ( ent - > currentOrigin , trace - > plane . normal , ent - > currentOrigin ) ;
VectorCopy ( ent - > 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 = = ENTITYNUM_NONE )
{
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 ) ;
if ( ! g_gravity - > value )
{
ent - > s . pos . trType = TR_GRAVITY ;
ent - > s . pos . trTime = level . time ;
ent - > s . pos . trDelta [ 0 ] + = crandom ( ) * 40.0f ; // I dunno, just do this??
ent - > s . pos . trDelta [ 1 ] + = crandom ( ) * 40.0f ;
ent - > s . pos . trDelta [ 2 ] + = random ( ) * 20.0f ;
}
else if ( ( ent - > flags & FL_DROPPED_ITEM )
& & ent - > item
& & ent - > item - > giType = = IT_WEAPON
& & ent - > item - > giTag = = WP_SABER )
{ //a dropped saber item, check below, just in case
int ignore = ENTITYNUM_NONE ;
if ( ent - > clipmask )
{
mask = ent - > clipmask ;
}
else
{
mask = MASK_SOLID | CONTENTS_PLAYERCLIP ; //shouldn't be able to get anywhere player can't
}
if ( ent - > owner )
{
ignore = ent - > owner - > s . number ;
}
else if ( ent - > activator )
{
ignore = ent - > activator - > s . number ;
}
VectorSet ( origin , ent - > currentOrigin [ 0 ] , ent - > currentOrigin [ 1 ] , ent - > currentOrigin [ 2 ] - 1 ) ;
gi . trace ( & tr , ent - > currentOrigin , ent - > mins , ent - > maxs , origin , ignore , mask ) ;
if ( ! tr . allsolid
& & ! tr . startsolid
& & tr . fraction > 0.001f )
{ //wha? fall....
ent - > s . pos . trType = TR_GRAVITY ;
ent - > s . pos . trTime = level . time ;
}
}
return ;
}
// get current position
EvaluateTrajectory ( & ent - > s . pos , level . time , origin ) ;
if ( ent - > s . apos . trType ! = TR_STATIONARY )
{
EvaluateTrajectory ( & ent - > s . apos , level . time , ent - > currentAngles ) ;
G_SetAngles ( ent , ent - > currentAngles ) ;
}
// trace a line from the previous position to the current position
if ( ent - > clipmask )
{
mask = ent - > clipmask ;
}
else
{
mask = MASK_SOLID | CONTENTS_PLAYERCLIP ; //shouldn't be able to get anywhere player can't
}
int ignore = ENTITYNUM_NONE ;
if ( ent - > owner )
{
ignore = ent - > owner - > s . number ;
}
else if ( ent - > activator )
{
ignore = ent - > activator - > s . number ;
}
gi . trace ( & tr , ent - > currentOrigin , ent - > mins , ent - > maxs , origin , ignore , mask ) ;
VectorCopy ( tr . endpos , ent - > currentOrigin ) ;
if ( tr . startsolid )
{
tr . fraction = 0 ;
}
gi . linkentity ( ent ) ; // FIXME: avoid this for stationary?
// check think function
G_RunThink ( ent ) ;
if ( tr . fraction = = 1 )
{
if ( g_gravity - > value < = 0 )
{
if ( ent - > s . apos . trType ! = TR_LINEAR )
{
VectorCopy ( ent - > currentAngles , ent - > s . apos . trBase ) ;
ent - > s . apos . trType = TR_LINEAR ;
ent - > s . apos . trDelta [ 1 ] = Q_flrand ( - 300 , 300 ) ;
ent - > s . apos . trDelta [ 0 ] = Q_flrand ( - 10 , 10 ) ;
ent - > s . apos . trDelta [ 2 ] = Q_flrand ( - 10 , 10 ) ;
ent - > s . apos . trTime = level . time ;
}
}
//friction in zero-G
if ( ! g_gravity - > value )
{
float friction = 0.975f ;
/*friction -= ent->mass/1000.0f;
if ( friction < 0.1 )
{
friction = 0.1f ;
}
*/
VectorScale ( ent - > s . pos . trDelta , friction , ent - > s . pos . trDelta ) ;
VectorCopy ( ent - > currentOrigin , ent - > s . pos . trBase ) ;
ent - > s . pos . trTime = level . time ;
}
return ;
}
// if it is in a nodrop volume, remove it
contents = gi . pointcontents ( ent - > currentOrigin , - 1 ) ;
if ( contents & CONTENTS_NODROP )
{
G_FreeEntity ( ent ) ;
return ;
}
if ( ! tr . startsolid )
{
G_BounceItem ( ent , & tr ) ;
}
}
/*
= = = = = = = = = = = = = = = =
ItemUse_Bacta
= = = = = = = = = = = = = = = =
*/
void ItemUse_Bacta ( gentity_t * ent )
{
if ( ! ent | | ! ent - > client )
{
return ;
}
if ( ent - > health > = ent - > client - > ps . stats [ STAT_MAX_HEALTH ] | | ! ent - > client - > ps . inventory [ INV_BACTA_CANISTER ] )
{
return ;
}
ent - > health + = MAX_BACTA_HEAL_AMOUNT ;
if ( ent - > health > ent - > client - > ps . stats [ STAT_MAX_HEALTH ] )
{
ent - > health = ent - > client - > ps . stats [ STAT_MAX_HEALTH ] ;
}
ent - > client - > ps . inventory [ INV_BACTA_CANISTER ] - - ;
G_SoundOnEnt ( ent , CHAN_VOICE , va ( " sound/weapons/force/heal%d_%c.mp3 " , Q_irand ( 1 , 4 ) , g_sex - > string [ 0 ] ) ) ;
}