123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305 |
- // 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 saber_colors_t TranslateSaberColor( const char *name );
- extern cvar_t *g_spskill;
- extern cvar_t *g_sex;
- #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
- //======================================================================
- /*
- ===============
- 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;
- }
- //======================================================================
- extern void G_SetSabersFromCVars( gentity_t *ent );
- void Pickup_Saber( gentity_t *self, qboolean hadSaber, qboolean saberSolo, char *saberType, char *saberColor )
- {
- //G_RemoveWeaponModels( ent );//???
- if ( Q_stricmp( "player", saberType ) == 0 )
- {//"player" means use cvar info
- G_SetSabersFromCVars( self );
- }
- else
- {
- saberInfo_t newSaber={0};
- if ( WP_SaberParseParms( saberType, &newSaber ) )
- {//successfully found a saber .sab entry to use
- //FIXME: what about dual sabers?
- int saberNum = 0;
- if ( saberSolo//only supposed to use this one saber when grab this pickup
- || newSaber.twoHanded //new saber is two-handed
- || (hadSaber && self->client->ps.saber[0].twoHanded) )//old saber is two-handed
- {//replace the old right-hand saber and remove the left hand one
- WP_RemoveSaber( self, 1 );
- }
- else
- {//add it as a second saber
- saberNum = 1;
- }
- WP_SetSaber( self, saberNum, saberType );
- WP_SaberInitBladeData( self );
- if ( self->client->ps.saber[saberNum].style )
- {
- self->client->ps.saberStylesKnown |= (1<<self->client->ps.saber[saberNum].style);
- }
- if ( saberColor != NULL )
- {//NPC_targetname = saberColor
- saber_colors_t saber_color = TranslateSaberColor( saberColor );
- for ( int bladeNum = 0; bladeNum < MAX_BLADES; bladeNum++ )
- {
- self->client->ps.saber[saberNum].blade[bladeNum].color = saber_color;
- }
- }
- }
- WP_SaberFreeStrings(newSaber);
- }
- }
- 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
- //NOTE: alt_fire = saberSolo, NPC_type = saberType, NPC_targetname = saberColor
- Pickup_Saber( other, hadWeapon, ent->alt_fire, ent->NPC_type, ent->NPC_targetname );
- }
- if ( other->s.number )
- {//NPC
- if ( other->s.weapon == WP_NONE )
- {//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 ( 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;
- }
- 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);
- // 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
- 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]
- && 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
- saberInfo_t itemSaber;
- WP_SaberParseParms( g_saber->string, &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)
- {
- // 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;
- }
- 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);
- }
- }
- ent->team = NULL;
- }
- /*
- ================
- 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;
- 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 ) {
- G_SetOrigin( ent, trace->endpos );
- ent->s.groundEntityNum = trace->entityNum;
- return;
- }
- 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;
- }
- return;
- }
- // get current position
- 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_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] ) );
- }
|