g_items.cpp 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305
  1. // leave this line at the top for all g_xxxx.cpp files...
  2. #include "g_headers.h"
  3. #include "g_local.h"
  4. #include "g_functions.h"
  5. #include "g_items.h"
  6. #include "wp_saber.h"
  7. extern qboolean missionInfo_Updated;
  8. extern void CrystalAmmoSettings(gentity_t *ent);
  9. extern void ChangeWeapon( gentity_t *ent, int newWeapon );
  10. extern qboolean PM_InKnockDown( playerState_t *ps );
  11. extern qboolean PM_InGetUp( playerState_t *ps );
  12. extern void WP_SetSaber( gentity_t *ent, int saberNum, char *saberName );
  13. extern void WP_RemoveSaber( gentity_t *ent, int saberNum );
  14. extern saber_colors_t TranslateSaberColor( const char *name );
  15. extern cvar_t *g_spskill;
  16. extern cvar_t *g_sex;
  17. #define MAX_BACTA_HEAL_AMOUNT 25
  18. /*
  19. Items are any object that a player can touch to gain some effect.
  20. Pickup will return the number of seconds until they should respawn.
  21. all items should pop when dropped in lava or slime
  22. Respawnable items don't actually go away when picked up, they are
  23. just made invisible and untouchable. This allows them to ride
  24. movers and respawn apropriately.
  25. */
  26. // Item Spawn flags
  27. #define ITMSF_SUSPEND 1
  28. #define ITMSF_NOPLAYER 2
  29. #define ITMSF_ALLOWNPC 4
  30. #define ITMSF_NOTSOLID 8
  31. #define ITMSF_VERTICAL 16
  32. #define ITMSF_INVISIBLE 32
  33. #define ITMSF_NOGLOW 64
  34. //======================================================================
  35. /*
  36. ===============
  37. G_InventorySelectable
  38. ===============
  39. */
  40. qboolean G_InventorySelectable( int index,gentity_t *other)
  41. {
  42. if (other->client->ps.inventory[index])
  43. {
  44. return qtrue;
  45. }
  46. return qfalse;
  47. }
  48. extern qboolean INV_GoodieKeyGive( gentity_t *target );
  49. extern qboolean INV_SecurityKeyGive( gentity_t *target, const char *keyname );
  50. int Pickup_Holdable( gentity_t *ent, gentity_t *other )
  51. {
  52. int i,original;
  53. other->client->ps.stats[STAT_ITEMS] |= (1<<ent->item->giTag);
  54. if ( ent->item->giTag == INV_SECURITY_KEY )
  55. {//give the key
  56. //FIXME: temp message
  57. gi.SendServerCommand( NULL, "cp @SP_INGAME_YOU_TOOK_SECURITY_KEY" );
  58. INV_SecurityKeyGive( other, ent->message );
  59. }
  60. else if ( ent->item->giTag == INV_GOODIE_KEY )
  61. {//give the key
  62. //FIXME: temp message
  63. gi.SendServerCommand( NULL, "cp @SP_INGAME_YOU_TOOK_SUPPLY_KEY" );
  64. INV_GoodieKeyGive( other );
  65. }
  66. else
  67. {// Picking up a normal item?
  68. other->client->ps.inventory[ent->item->giTag]++;
  69. }
  70. // Got a security key
  71. // Set the inventory select, just in case it hasn't
  72. original = cg.inventorySelect;
  73. for ( i = 0 ; i < INV_MAX ; i++ )
  74. {
  75. if ((cg.inventorySelect < INV_ELECTROBINOCULARS) || (cg.inventorySelect >= INV_MAX))
  76. {
  77. cg.inventorySelect = (INV_MAX - 1);
  78. }
  79. if ( G_InventorySelectable( cg.inventorySelect,other ) )
  80. {
  81. return 60;
  82. }
  83. cg.inventorySelect++;
  84. }
  85. cg.inventorySelect = original;
  86. return 60;
  87. }
  88. //======================================================================
  89. int Add_Ammo2 (gentity_t *ent, int ammoType, int count)
  90. {
  91. if (ammoType != AMMO_FORCE)
  92. {
  93. ent->client->ps.ammo[ammoType] += count;
  94. // since the ammo is the weapon in this case, picking up ammo should actually give you the weapon
  95. switch( ammoType )
  96. {
  97. case AMMO_THERMAL:
  98. ent->client->ps.stats[STAT_WEAPONS] |= ( 1 << WP_THERMAL );
  99. break;
  100. case AMMO_DETPACK:
  101. ent->client->ps.stats[STAT_WEAPONS] |= ( 1 << WP_DET_PACK );
  102. break;
  103. case AMMO_TRIPMINE:
  104. ent->client->ps.stats[STAT_WEAPONS] |= ( 1 << WP_TRIP_MINE );
  105. break;
  106. }
  107. if ( ent->client->ps.ammo[ammoType] > ammoData[ammoType].max )
  108. {
  109. ent->client->ps.ammo[ammoType] = ammoData[ammoType].max;
  110. return qfalse;
  111. }
  112. }
  113. else
  114. {
  115. if ( ent->client->ps.forcePower >= ammoData[ammoType].max )
  116. {//if have full force, just get 25 extra per crystal
  117. ent->client->ps.forcePower += 25;
  118. }
  119. else
  120. {//else if don't have full charge, give full amount, up to max + 25
  121. ent->client->ps.forcePower += count;
  122. if ( ent->client->ps.forcePower >= ammoData[ammoType].max + 25 )
  123. {//cap at max + 25
  124. ent->client->ps.forcePower = ammoData[ammoType].max + 25;
  125. }
  126. }
  127. if ( ent->client->ps.forcePower >= ammoData[ammoType].max*2 )
  128. {//always cap at twice a full charge
  129. ent->client->ps.forcePower = ammoData[ammoType].max*2;
  130. return qfalse; // can't hold any more
  131. }
  132. }
  133. return qtrue;
  134. }
  135. //-------------------------------------------------------
  136. void Add_Ammo (gentity_t *ent, int weapon, int count)
  137. {
  138. Add_Ammo2(ent,weaponData[weapon].ammoIndex,count);
  139. }
  140. //-------------------------------------------------------
  141. int Pickup_Ammo (gentity_t *ent, gentity_t *other)
  142. {
  143. int quantity;
  144. if ( ent->count ) {
  145. quantity = ent->count;
  146. } else {
  147. quantity = ent->item->quantity;
  148. }
  149. Add_Ammo2 (other, ent->item->giTag, quantity);
  150. return 30;
  151. }
  152. //======================================================================
  153. void Add_Batteries( gentity_t *ent, int *count )
  154. {
  155. if ( ent->client && ent->client->ps.batteryCharge < MAX_BATTERIES && *count )
  156. {
  157. if ( *count + ent->client->ps.batteryCharge > MAX_BATTERIES )
  158. {
  159. // steal what we need, then leave the rest for later
  160. *count -= ( MAX_BATTERIES - ent->client->ps.batteryCharge );
  161. ent->client->ps.batteryCharge = MAX_BATTERIES;
  162. }
  163. else
  164. {
  165. // just drain all of the batteries
  166. ent->client->ps.batteryCharge += *count;
  167. *count = 0;
  168. }
  169. G_AddEvent( ent, EV_BATTERIES_CHARGED, 0 );
  170. }
  171. }
  172. //-------------------------------------------------------
  173. int Pickup_Battery( gentity_t *ent, gentity_t *other )
  174. {
  175. int quantity;
  176. if ( ent->count )
  177. {
  178. quantity = ent->count;
  179. }
  180. else
  181. {
  182. quantity = ent->item->quantity;
  183. }
  184. // 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
  185. Add_Batteries( other, &quantity );
  186. return 30;
  187. }
  188. //======================================================================
  189. extern void G_SetSabersFromCVars( gentity_t *ent );
  190. void Pickup_Saber( gentity_t *self, qboolean hadSaber, qboolean saberSolo, char *saberType, char *saberColor )
  191. {
  192. //G_RemoveWeaponModels( ent );//???
  193. if ( Q_stricmp( "player", saberType ) == 0 )
  194. {//"player" means use cvar info
  195. G_SetSabersFromCVars( self );
  196. }
  197. else
  198. {
  199. saberInfo_t newSaber={0};
  200. if ( WP_SaberParseParms( saberType, &newSaber ) )
  201. {//successfully found a saber .sab entry to use
  202. //FIXME: what about dual sabers?
  203. int saberNum = 0;
  204. if ( saberSolo//only supposed to use this one saber when grab this pickup
  205. || newSaber.twoHanded //new saber is two-handed
  206. || (hadSaber && self->client->ps.saber[0].twoHanded) )//old saber is two-handed
  207. {//replace the old right-hand saber and remove the left hand one
  208. WP_RemoveSaber( self, 1 );
  209. }
  210. else
  211. {//add it as a second saber
  212. saberNum = 1;
  213. }
  214. WP_SetSaber( self, saberNum, saberType );
  215. WP_SaberInitBladeData( self );
  216. if ( self->client->ps.saber[saberNum].style )
  217. {
  218. self->client->ps.saberStylesKnown |= (1<<self->client->ps.saber[saberNum].style);
  219. }
  220. if ( saberColor != NULL )
  221. {//NPC_targetname = saberColor
  222. saber_colors_t saber_color = TranslateSaberColor( saberColor );
  223. for ( int bladeNum = 0; bladeNum < MAX_BLADES; bladeNum++ )
  224. {
  225. self->client->ps.saber[saberNum].blade[bladeNum].color = saber_color;
  226. }
  227. }
  228. }
  229. WP_SaberFreeStrings(newSaber);
  230. }
  231. }
  232. extern void CG_ChangeWeapon( int num );
  233. int Pickup_Weapon (gentity_t *ent, gentity_t *other)
  234. {
  235. int quantity;
  236. qboolean hadWeapon = qfalse;
  237. /*
  238. if ( ent->count || (ent->activator && !ent->activator->s.number) )
  239. {
  240. quantity = ent->count;
  241. }
  242. else
  243. {
  244. quantity = ent->item->quantity;
  245. }
  246. */
  247. // dropped items are always picked up
  248. if ( ent->flags & FL_DROPPED_ITEM )
  249. {
  250. quantity = ent->count;
  251. }
  252. else
  253. {//wasn't dropped
  254. quantity = ent->item->quantity?ent->item->quantity:50;
  255. }
  256. // add the weapon
  257. if ( other->client->ps.stats[STAT_WEAPONS] & ( 1 << ent->item->giTag ) )
  258. {
  259. hadWeapon = qtrue;
  260. }
  261. other->client->ps.stats[STAT_WEAPONS] |= ( 1 << ent->item->giTag );
  262. if ( ent->item->giTag == WP_SABER && (!hadWeapon || ent->NPC_type != NULL) )
  263. {//didn't have a saber or it is specifying a certain kind of saber to use
  264. //NOTE: alt_fire = saberSolo, NPC_type = saberType, NPC_targetname = saberColor
  265. Pickup_Saber( other, hadWeapon, ent->alt_fire, ent->NPC_type, ent->NPC_targetname );
  266. }
  267. if ( other->s.number )
  268. {//NPC
  269. if ( other->s.weapon == WP_NONE )
  270. {//NPC with no weapon picked up a weapon, change to this weapon
  271. //FIXME: clear/set the alt-fire flag based on the picked up weapon and my class?
  272. other->client->ps.weapon = ent->item->giTag;
  273. other->client->ps.weaponstate = WEAPON_RAISING;
  274. ChangeWeapon( other, ent->item->giTag );
  275. if ( ent->item->giTag == WP_SABER )
  276. {
  277. other->client->ps.SaberActivate();
  278. WP_SaberAddG2SaberModels( other );
  279. }
  280. else
  281. {
  282. G_CreateG2AttachedWeaponModel( other, weaponData[ent->item->giTag].weaponMdl, other->handRBolt, 0 );
  283. }
  284. }
  285. }
  286. if ( quantity )
  287. {
  288. // Give ammo
  289. Add_Ammo( other, ent->item->giTag, quantity );
  290. }
  291. return 5;
  292. }
  293. //======================================================================
  294. int ITM_AddHealth (gentity_t *ent, int count)
  295. {
  296. ent->health += count;
  297. if (ent->health > ent->client->ps.stats[STAT_MAX_HEALTH]) // Past max health
  298. {
  299. ent->health = ent->client->ps.stats[STAT_MAX_HEALTH];
  300. return qfalse;
  301. }
  302. return qtrue;
  303. }
  304. int Pickup_Health (gentity_t *ent, gentity_t *other) {
  305. int max;
  306. int quantity;
  307. max = other->client->ps.stats[STAT_MAX_HEALTH];
  308. if ( ent->count ) {
  309. quantity = ent->count;
  310. } else {
  311. quantity = ent->item->quantity;
  312. }
  313. other->health += quantity;
  314. if (other->health > max ) {
  315. other->health = max;
  316. }
  317. if ( ent->item->giTag == 100 ) { // mega health respawns slow
  318. return 120;
  319. }
  320. return 30;
  321. }
  322. //======================================================================
  323. int ITM_AddArmor (gentity_t *ent, int count)
  324. {
  325. ent->client->ps.stats[STAT_ARMOR] += count;
  326. if (ent->client->ps.stats[STAT_ARMOR] > ent->client->ps.stats[STAT_MAX_HEALTH])
  327. {
  328. ent->client->ps.stats[STAT_ARMOR] = ent->client->ps.stats[STAT_MAX_HEALTH];
  329. return qfalse;
  330. }
  331. return qtrue;
  332. }
  333. int Pickup_Armor( gentity_t *ent, gentity_t *other ) {
  334. // make sure that the shield effect is on
  335. other->client->ps.powerups[PW_BATTLESUIT] = Q3_INFINITE;
  336. other->client->ps.stats[STAT_ARMOR] += ent->item->quantity;
  337. if ( other->client->ps.stats[STAT_ARMOR] > other->client->ps.stats[STAT_MAX_HEALTH] ) {
  338. other->client->ps.stats[STAT_ARMOR] = other->client->ps.stats[STAT_MAX_HEALTH];
  339. }
  340. return 30;
  341. }
  342. //======================================================================
  343. int Pickup_Holocron( gentity_t *ent, gentity_t *other )
  344. {
  345. int forcePower = ent->item->giTag;
  346. int forceLevel = ent->count;
  347. // check if out of range
  348. if( forceLevel < 0 || forceLevel >= NUM_FORCE_POWER_LEVELS )
  349. {
  350. gi.Printf(" Pickup_Holocron : count %d not in valid range\n", forceLevel );
  351. return 1;
  352. }
  353. // don't pick up if already known AND your level is higher than pickup level
  354. if ( ( other->client->ps.forcePowersKnown & ( 1 << forcePower )) )
  355. {
  356. //don't pickup if item is lower than current level
  357. if( other->client->ps.forcePowerLevel[forcePower] >= forceLevel )
  358. {
  359. return 1;
  360. }
  361. }
  362. other->client->ps.forcePowerLevel[forcePower] = forceLevel;
  363. other->client->ps.forcePowersKnown |= ( 1 << forcePower );
  364. missionInfo_Updated = qtrue; // Activate flashing text
  365. gi.cvar_set("cg_updatedDataPadForcePower1", va("%d",forcePower+1)); // The +1 is offset in the print routine.
  366. cg_updatedDataPadForcePower1.integer = forcePower+1;
  367. gi.cvar_set("cg_updatedDataPadForcePower2", "0"); // The +1 is offset in the print routine.
  368. cg_updatedDataPadForcePower2.integer = 0;
  369. gi.cvar_set("cg_updatedDataPadForcePower3", "0"); // The +1 is offset in the print routine.
  370. cg_updatedDataPadForcePower3.integer = 0;
  371. return 1;
  372. }
  373. //======================================================================
  374. /*
  375. ===============
  376. RespawnItem
  377. ===============
  378. */
  379. void RespawnItem( gentity_t *ent ) {
  380. }
  381. qboolean CheckItemCanBePickedUpByNPC( gentity_t *item, gentity_t *pickerupper )
  382. {
  383. if ( (item->flags&FL_DROPPED_ITEM)
  384. && item->activator != &g_entities[0]
  385. && pickerupper->s.number
  386. && pickerupper->s.weapon == WP_NONE
  387. && pickerupper->enemy
  388. && pickerupper->painDebounceTime < level.time
  389. && pickerupper->NPC && pickerupper->NPC->surrenderTime < level.time //not surrendering
  390. && !(pickerupper->NPC->scriptFlags&SCF_FORCED_MARCH) //not being forced to march
  391. && item->item->giTag != INV_SECURITY_KEY )
  392. {//non-player, in combat, picking up a dropped item that does NOT belong to the player and it *not* a security key
  393. if ( level.time - item->s.time < 3000 )//was 5000
  394. {
  395. return qfalse;
  396. }
  397. return qtrue;
  398. }
  399. return qfalse;
  400. }
  401. qboolean G_CanPickUpWeapons( gentity_t *other )
  402. {
  403. if ( !other || !other->client )
  404. {
  405. return qfalse;
  406. }
  407. switch ( other->client->NPC_class )
  408. {
  409. case CLASS_ATST:
  410. case CLASS_GONK:
  411. case CLASS_MARK1:
  412. case CLASS_MARK2:
  413. case CLASS_MOUSE:
  414. case CLASS_PROBE:
  415. case CLASS_PROTOCOL:
  416. case CLASS_R2D2:
  417. case CLASS_R5D2:
  418. case CLASS_SEEKER:
  419. case CLASS_REMOTE:
  420. case CLASS_RANCOR:
  421. case CLASS_WAMPA:
  422. case CLASS_JAWA: //FIXME: in some cases it's okay?
  423. case CLASS_UGNAUGHT: //FIXME: in some cases it's okay?
  424. case CLASS_SENTRY:
  425. return qfalse;
  426. break;
  427. }
  428. return qtrue;
  429. }
  430. /*
  431. ===============
  432. Touch_Item
  433. ===============
  434. */
  435. extern cvar_t *g_timescale;
  436. void Touch_Item (gentity_t *ent, gentity_t *other, trace_t *trace) {
  437. int respawn = 0;
  438. if (!other->client)
  439. return;
  440. if (other->health < 1)
  441. return; // dead people can't pickup
  442. if ( other->client->ps.pm_time > 0 )
  443. {//cant pick up when out of control
  444. return;
  445. }
  446. // NPCs can pick it up
  447. if ((ent->spawnflags & ITMSF_ALLOWNPC) && (!other->s.number))
  448. {
  449. return;
  450. }
  451. // Players cannot pick it up
  452. if ( (ent->spawnflags & ITMSF_NOPLAYER) && (other->s.number) )
  453. {
  454. return;
  455. }
  456. if ( ent->noDamageTeam != TEAM_FREE && other->client->playerTeam != ent->noDamageTeam )
  457. {//only one team can pick it up
  458. return;
  459. }
  460. if ( !G_CanPickUpWeapons( other ) )
  461. {//FIXME: some flag would be better
  462. //droids can't pick up items/weapons!
  463. return;
  464. }
  465. //FIXME: need to make them run toward a dropped weapon when fleeing without one?
  466. //FIXME: need to make them come out of flee mode when pick up their old weapon?
  467. if ( CheckItemCanBePickedUpByNPC( ent, other ) )
  468. {
  469. if ( other->NPC && other->NPC->goalEntity && other->NPC->goalEntity == ent )
  470. {//they were running to pick me up, they did, so clear goal
  471. other->NPC->goalEntity = NULL;
  472. other->NPC->squadState = SQUAD_STAND_AND_SHOOT;
  473. NPCInfo->tempBehavior = BS_DEFAULT;
  474. TIMER_Set(other, "flee", -1);
  475. }
  476. else
  477. {
  478. return;
  479. }
  480. }
  481. else if ( !(ent->spawnflags & ITMSF_ALLOWNPC) )
  482. {// NPCs cannot pick it up
  483. if ( other->s.number != 0 )
  484. {// Not the player?
  485. return;
  486. }
  487. }
  488. // the same pickup rules are used for client side and server side
  489. if ( !BG_CanItemBeGrabbed( &ent->s, &other->client->ps ) ) {
  490. return;
  491. }
  492. if ( other->client )
  493. {
  494. if ( (other->client->ps.eFlags&EF_FORCE_GRIPPED) || (other->client->ps.eFlags&EF_FORCE_DRAINED) )
  495. {//can't pick up anything while being gripped
  496. return;
  497. }
  498. if ( PM_InKnockDown( &other->client->ps ) && !PM_InGetUp( &other->client->ps ) )
  499. {//can't pick up while in a knockdown
  500. return;
  501. }
  502. }
  503. if (!ent->item) { //not an item!
  504. gi.Printf( "Touch_Item: %s is not an item!\n", ent->classname);
  505. return;
  506. }
  507. qboolean bHadWeapon = qfalse;
  508. // call the item-specific pickup function
  509. switch( ent->item->giType )
  510. {
  511. case IT_WEAPON:
  512. if ( other->NPC && other->s.weapon == WP_NONE )
  513. {//Make them duck and sit here for a few seconds
  514. int pickUpTime = Q_irand( 1000, 3000 );
  515. TIMER_Set( other, "duck", pickUpTime );
  516. TIMER_Set( other, "roamTime", pickUpTime );
  517. TIMER_Set( other, "stick", pickUpTime );
  518. TIMER_Set( other, "verifyCP", pickUpTime );
  519. TIMER_Set( other, "attackDelay", 600 );
  520. respawn = 0;
  521. }
  522. if ( other->client->ps.stats[STAT_WEAPONS] & ( 1 << ent->item->giTag ) )
  523. {
  524. bHadWeapon = qtrue;
  525. }
  526. respawn = Pickup_Weapon(ent, other);
  527. break;
  528. case IT_AMMO:
  529. respawn = Pickup_Ammo(ent, other);
  530. break;
  531. case IT_ARMOR:
  532. respawn = Pickup_Armor(ent, other);
  533. break;
  534. case IT_HEALTH:
  535. respawn = Pickup_Health(ent, other);
  536. break;
  537. case IT_HOLDABLE:
  538. respawn = Pickup_Holdable(ent, other);
  539. break;
  540. case IT_BATTERY:
  541. respawn = Pickup_Battery( ent, other );
  542. break;
  543. case IT_HOLOCRON:
  544. respawn = Pickup_Holocron( ent, other );
  545. break;
  546. default:
  547. return;
  548. }
  549. if ( !respawn )
  550. {
  551. return;
  552. }
  553. // play the normal pickup sound
  554. if ( !other->s.number && g_timescale->value < 1.0f )
  555. {//SIGH... with timescale on, you lose events left and right
  556. extern void CG_ItemPickup( int itemNum, qboolean bHadItem );
  557. // but we're SP so we'll cheat
  558. cgi_S_StartSound( NULL, other->s.number, CHAN_AUTO, cgi_S_RegisterSound( ent->item->pickup_sound ) );
  559. // show icon and name on status bar
  560. CG_ItemPickup( ent->s.modelindex, bHadWeapon );
  561. }
  562. else
  563. {
  564. if ( bHadWeapon )
  565. {
  566. G_AddEvent( other, EV_ITEM_PICKUP, -ent->s.modelindex );
  567. }
  568. else
  569. {
  570. G_AddEvent( other, EV_ITEM_PICKUP, ent->s.modelindex );
  571. }
  572. }
  573. // fire item targets
  574. G_UseTargets (ent, other);
  575. // wait of -1 will not respawn
  576. // if ( ent->wait == -1 )
  577. {
  578. //why not just remove me?
  579. G_FreeEntity( ent );
  580. /*
  581. //NOTE: used to do this: (for respawning?)
  582. ent->svFlags |= SVF_NOCLIENT;
  583. ent->s.eFlags |= EF_NODRAW;
  584. ent->contents = 0;
  585. ent->unlinkAfterEvent = qtrue;
  586. */
  587. return;
  588. }
  589. }
  590. //======================================================================
  591. /*
  592. ================
  593. LaunchItem
  594. Spawns an item and tosses it forward
  595. ================
  596. */
  597. gentity_t *LaunchItem( gitem_t *item, const vec3_t origin, const vec3_t velocity, char *target ) {
  598. gentity_t *dropped;
  599. dropped = G_Spawn();
  600. dropped->s.eType = ET_ITEM;
  601. dropped->s.modelindex = item - bg_itemlist; // store item number in modelindex
  602. dropped->s.modelindex2 = 1; // This is non-zero is it's a dropped item
  603. dropped->classname = G_NewString(item->classname); //copy it so it can be freed safely
  604. dropped->item = item;
  605. // try using the "correct" mins/maxs first
  606. VectorSet( dropped->mins, item->mins[0], item->mins[1], item->mins[2] );
  607. VectorSet( dropped->maxs, item->maxs[0], item->maxs[1], item->maxs[2] );
  608. if ((!dropped->mins[0] && !dropped->mins[1] && !dropped->mins[2]) &&
  609. (!dropped->maxs[0] && !dropped->maxs[1] && !dropped->maxs[2]))
  610. {
  611. VectorSet( dropped->maxs, ITEM_RADIUS, ITEM_RADIUS, ITEM_RADIUS );
  612. VectorScale( dropped->maxs, -1, dropped->mins );
  613. }
  614. dropped->contents = CONTENTS_TRIGGER|CONTENTS_ITEM;//CONTENTS_TRIGGER;//not CONTENTS_BODY for dropped items, don't need to ID them
  615. if ( target && target[0] )
  616. {
  617. dropped->target = G_NewString( target );
  618. }
  619. else
  620. {
  621. // if not targeting something, auto-remove after 30 seconds
  622. // only if it's NOT a security or goodie key
  623. if (dropped->item->giTag != INV_SECURITY_KEY )
  624. {
  625. dropped->e_ThinkFunc = thinkF_G_FreeEntity;
  626. dropped->nextthink = level.time + 30000;
  627. }
  628. if ( dropped->item->giType == IT_AMMO && dropped->item->giTag == AMMO_FORCE )
  629. {
  630. dropped->nextthink = -1;
  631. dropped->e_ThinkFunc = thinkF_NULL;
  632. }
  633. }
  634. dropped->e_TouchFunc = touchF_Touch_Item;
  635. if ( item->giType == IT_WEAPON )
  636. {
  637. // give weapon items zero pitch, a random yaw, and rolled onto their sides...but would be bad to do this for a bowcaster
  638. if ( item->giTag != WP_BOWCASTER
  639. && item->giTag != WP_THERMAL
  640. && item->giTag != WP_TRIP_MINE
  641. && item->giTag != WP_DET_PACK )
  642. {
  643. VectorSet( dropped->s.angles, 0, crandom() * 180, 90.0f );
  644. G_SetAngles( dropped, dropped->s.angles );
  645. }
  646. }
  647. G_SetOrigin( dropped, origin );
  648. dropped->s.pos.trType = TR_GRAVITY;
  649. dropped->s.pos.trTime = level.time;
  650. VectorCopy( velocity, dropped->s.pos.trDelta );
  651. dropped->s.eFlags |= EF_BOUNCE_HALF;
  652. dropped->flags = FL_DROPPED_ITEM;
  653. gi.linkentity (dropped);
  654. return dropped;
  655. }
  656. /*
  657. ================
  658. Drop_Item
  659. Spawns an item and tosses it forward
  660. ================
  661. */
  662. gentity_t *Drop_Item( gentity_t *ent, gitem_t *item, float angle, qboolean copytarget ) {
  663. gentity_t *dropped = NULL;
  664. vec3_t velocity;
  665. vec3_t angles;
  666. VectorCopy( ent->s.apos.trBase, angles );
  667. angles[YAW] += angle;
  668. angles[PITCH] = 0; // always forward
  669. AngleVectors( angles, velocity, NULL, NULL );
  670. VectorScale( velocity, 150, velocity );
  671. velocity[2] += 200 + crandom() * 50;
  672. if ( copytarget )
  673. {
  674. dropped = LaunchItem( item, ent->s.pos.trBase, velocity, ent->opentarget );
  675. }
  676. else
  677. {
  678. dropped = LaunchItem( item, ent->s.pos.trBase, velocity, NULL );
  679. }
  680. dropped->activator = ent;//so we know who we belonged to so they can pick it back up later
  681. dropped->s.time = level.time;//mark this time so we aren't picked up instantly by the guy who dropped us
  682. return dropped;
  683. }
  684. /*
  685. ================
  686. Use_Item
  687. Respawn the item
  688. ================
  689. */
  690. void Use_Item( gentity_t *ent, gentity_t *other, gentity_t *activator )
  691. {
  692. if ( (ent->svFlags&SVF_PLAYER_USABLE) && other && !other->s.number )
  693. {//used directly by the player, pick me up
  694. GEntity_TouchFunc( ent, other, NULL );
  695. }
  696. else
  697. {//use me
  698. if ( ent->spawnflags & 32 ) // invisible
  699. {
  700. // If it was invisible, first use makes it visible....
  701. ent->s.eFlags &= ~EF_NODRAW;
  702. ent->contents = CONTENTS_TRIGGER|CONTENTS_ITEM;
  703. ent->spawnflags &= ~32;
  704. return;
  705. }
  706. G_ActivateBehavior( ent, BSET_USE );
  707. RespawnItem( ent );
  708. }
  709. }
  710. //======================================================================
  711. /*
  712. ================
  713. FinishSpawningItem
  714. Traces down to find where an item should rest, instead of letting them
  715. free fall from their spawn points
  716. ================
  717. */
  718. extern int delayedShutDown;
  719. extern cvar_t *g_saber;
  720. void FinishSpawningItem( gentity_t *ent ) {
  721. trace_t tr;
  722. vec3_t dest;
  723. gitem_t *item;
  724. int itemNum;
  725. itemNum=1;
  726. for ( item = bg_itemlist + 1 ; item->classname ; item++,itemNum++)
  727. {
  728. if (!strcmp(item->classname,ent->classname))
  729. {
  730. break;
  731. }
  732. }
  733. // Set bounding box for item
  734. VectorSet( ent->mins, item->mins[0],item->mins[1] ,item->mins[2]);
  735. VectorSet( ent->maxs, item->maxs[0],item->maxs[1] ,item->maxs[2]);
  736. if ((!ent->mins[0] && !ent->mins[1] && !ent->mins[2]) &&
  737. (!ent->maxs[0] && !ent->maxs[1] && !ent->maxs[2]))
  738. {
  739. VectorSet (ent->mins, -ITEM_RADIUS, -ITEM_RADIUS, -2);//to match the comments in the items.dat file!
  740. VectorSet (ent->maxs, ITEM_RADIUS, ITEM_RADIUS, ITEM_RADIUS);
  741. }
  742. if ((item->quantity) && (item->giType == IT_AMMO))
  743. {
  744. ent->count = item->quantity;
  745. }
  746. if ((item->quantity) && (item->giType == IT_BATTERY))
  747. {
  748. ent->count = item->quantity;
  749. }
  750. ent->s.radius = 20;
  751. VectorSet( ent->s.modelScale, 1.0f, 1.0f, 1.0f );
  752. if ( ent->item->giType == IT_WEAPON
  753. && ent->item->giTag == WP_SABER
  754. && ent->NPC_type
  755. && ent->NPC_type[0]
  756. && Q_stricmp( "player", ent->NPC_type ) == 0
  757. && g_saber->string
  758. && g_saber->string[0]
  759. && Q_stricmp( "none", g_saber->string )
  760. && Q_stricmp( "NULL", g_saber->string ) )
  761. {//player's saber
  762. saberInfo_t itemSaber;
  763. WP_SaberParseParms( g_saber->string, &itemSaber );
  764. //NOTE: should I keep this string around for any reason? Will I ever need it later?
  765. //ent->??? = G_NewString( itemSaber.model );
  766. gi.G2API_InitGhoul2Model( ent->ghoul2, itemSaber.model, G_ModelIndex( itemSaber.model ));
  767. WP_SaberFreeStrings(itemSaber);
  768. }
  769. else
  770. {
  771. gi.G2API_InitGhoul2Model( ent->ghoul2, ent->item->world_model, G_ModelIndex( ent->item->world_model ));
  772. }
  773. // Set crystal ammo amount based on skill level
  774. /* if ((itemNum == ITM_AMMO_CRYSTAL_BORG) ||
  775. (itemNum == ITM_AMMO_CRYSTAL_DN) ||
  776. (itemNum == ITM_AMMO_CRYSTAL_FORGE) ||
  777. (itemNum == ITM_AMMO_CRYSTAL_SCAVENGER) ||
  778. (itemNum == ITM_AMMO_CRYSTAL_STASIS))
  779. {
  780. CrystalAmmoSettings(ent);
  781. }
  782. */
  783. ent->s.eType = ET_ITEM;
  784. ent->s.modelindex = ent->item - bg_itemlist; // store item number in modelindex
  785. ent->s.modelindex2 = 0; // zero indicates this isn't a dropped item
  786. ent->contents = CONTENTS_TRIGGER|CONTENTS_ITEM;//CONTENTS_BODY;//CONTENTS_TRIGGER|
  787. ent->e_TouchFunc = touchF_Touch_Item;
  788. // useing an item causes it to respawn
  789. ent->e_UseFunc = useF_Use_Item;
  790. ent->svFlags |= SVF_PLAYER_USABLE;//so player can pick it up
  791. // Hang in air?
  792. ent->s.origin[2] += 1;//just to get it off the damn ground because coplanar = insolid
  793. if ( ent->spawnflags & ITMSF_SUSPEND)
  794. {
  795. // suspended
  796. G_SetOrigin( ent, ent->s.origin );
  797. }
  798. else
  799. {
  800. // drop to floor
  801. VectorSet( dest, ent->s.origin[0], ent->s.origin[1], MIN_WORLD_COORD );
  802. gi.trace( &tr, ent->s.origin, ent->mins, ent->maxs, dest, ent->s.number, MASK_SOLID|CONTENTS_PLAYERCLIP );
  803. if ( tr.startsolid )
  804. {
  805. if ( &g_entities[tr.entityNum] != NULL )
  806. {
  807. 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 );
  808. }
  809. else
  810. {
  811. gi.Printf (S_COLOR_RED"FinishSpawningItem: removing %s startsolid at %s (in a %s)\n", ent->classname, vtos(ent->s.origin) );
  812. }
  813. assert( 0 && "item starting in solid");
  814. if (!g_entities[ENTITYNUM_WORLD].s.radius){ //not a region
  815. delayedShutDown = level.time + 100;
  816. }
  817. G_FreeEntity( ent );
  818. return;
  819. }
  820. // allow to ride movers
  821. ent->s.groundEntityNum = tr.entityNum;
  822. G_SetOrigin( ent, tr.endpos );
  823. }
  824. /* ? don't need this
  825. // team slaves and targeted items aren't present at start
  826. if ( ( ent->flags & FL_TEAMSLAVE ) || ent->targetname ) {
  827. ent->s.eFlags |= EF_NODRAW;
  828. ent->contents = 0;
  829. return;
  830. }
  831. */
  832. if ( ent->spawnflags & ITMSF_INVISIBLE ) // invisible
  833. {
  834. ent->s.eFlags |= EF_NODRAW;
  835. ent->contents = 0;
  836. }
  837. if ( ent->spawnflags & ITMSF_NOTSOLID ) // not solid
  838. {
  839. ent->contents = 0;
  840. }
  841. gi.linkentity (ent);
  842. }
  843. char itemRegistered[MAX_ITEMS+1];
  844. /*
  845. ==============
  846. ClearRegisteredItems
  847. ==============
  848. */
  849. void ClearRegisteredItems( void ) {
  850. memset( itemRegistered, '0', bg_numItems );
  851. itemRegistered[ bg_numItems ] = 0;
  852. //these are given in g_client, ClientSpawn(), but MUST be registered HERE, BEFORE cgame starts.
  853. //RegisterItem( FindItemForWeapon( WP_NONE ) ); //has no item
  854. RegisterItem( FindItemForInventory( INV_ELECTROBINOCULARS ));
  855. //RegisterItem( FindItemForInventory( INV_BACTA_CANISTER ));
  856. // saber or baton is cached in SP_info_player_deathmatch now.
  857. extern void Player_CacheFromPrevLevel(void);//g_client.cpp
  858. Player_CacheFromPrevLevel(); //reads from transition carry-over;
  859. }
  860. /*
  861. ===============
  862. RegisterItem
  863. The item will be added to the precache list
  864. ===============
  865. */
  866. void RegisterItem( gitem_t *item ) {
  867. if ( !item ) {
  868. G_Error( "RegisterItem: NULL" );
  869. }
  870. itemRegistered[ item - bg_itemlist ] = '1';
  871. gi.SetConfigstring(CS_ITEMS, itemRegistered); //Write the needed items to a config string
  872. }
  873. /*
  874. ===============
  875. SaveRegisteredItems
  876. Write the needed items to a config string
  877. so the client will know which ones to precache
  878. ===============
  879. */
  880. void SaveRegisteredItems( void ) {
  881. /* char string[MAX_ITEMS+1];
  882. int i;
  883. int count;
  884. count = 0;
  885. for ( i = 0 ; i < bg_numItems ; i++ ) {
  886. if ( itemRegistered[i] ) {
  887. count++;
  888. string[i] = '1';
  889. } else {
  890. string[i] = '0';
  891. }
  892. }
  893. string[ bg_numItems ] = 0;
  894. gi.Printf( "%i items registered\n", count );
  895. gi.SetConfigstring(CS_ITEMS, string);
  896. */
  897. gi.SetConfigstring(CS_ITEMS, itemRegistered);
  898. }
  899. /*
  900. ============
  901. item_spawn_use
  902. if an item is given a targetname, it will be spawned in when used
  903. ============
  904. */
  905. void item_spawn_use( gentity_t *self, gentity_t *other, gentity_t *activator )
  906. //-----------------------------------------------------------------------------
  907. {
  908. self->nextthink = level.time + 50;
  909. self->e_ThinkFunc = thinkF_FinishSpawningItem;
  910. // I could be fancy and add a count or something like that to be able to spawn the item numerous times...
  911. self->e_UseFunc = NULL;
  912. }
  913. /*
  914. ============
  915. G_SpawnItem
  916. Sets the clipping size and plants the object on the floor.
  917. Items can't be immediately dropped to floor, because they might
  918. be on an entity that hasn't spawned yet.
  919. ============
  920. */
  921. void G_SpawnItem (gentity_t *ent, gitem_t *item) {
  922. G_SpawnFloat( "random", "0", &ent->random );
  923. G_SpawnFloat( "wait", "0", &ent->wait );
  924. RegisterItem( item );
  925. ent->item = item;
  926. // targetname indicates they want to spawn it later
  927. if( ent->targetname )
  928. {
  929. ent->e_UseFunc = useF_item_spawn_use;
  930. }
  931. else
  932. { // some movers spawn on the second frame, so delay item
  933. // spawns until the third frame so they can ride trains
  934. ent->nextthink = level.time + START_TIME_MOVERS_SPAWNED + 50;
  935. ent->e_ThinkFunc = thinkF_FinishSpawningItem;
  936. }
  937. ent->physicsBounce = 0.50; // items are bouncy
  938. // Set a default infoString text color
  939. // NOTE: if we want to do cool cross-hair colors for items, we can just modify this, but for now, don't do it
  940. VectorSet( ent->startRGBA, 1.0f, 1.0f, 1.0f );
  941. if ( ent->team && ent->team[0] )
  942. {
  943. ent->noDamageTeam = (team_t)GetIDForString( TeamTable, ent->team );
  944. if ( ent->noDamageTeam == TEAM_FREE )
  945. {
  946. G_Error("team name %s not recognized\n", ent->team);
  947. }
  948. }
  949. ent->team = NULL;
  950. }
  951. /*
  952. ================
  953. G_BounceItem
  954. ================
  955. */
  956. void G_BounceItem( gentity_t *ent, trace_t *trace ) {
  957. vec3_t velocity;
  958. float dot;
  959. int hitTime;
  960. // reflect the velocity on the trace plane
  961. hitTime = level.previousTime + ( level.time - level.previousTime ) * trace->fraction;
  962. EvaluateTrajectoryDelta( &ent->s.pos, hitTime, velocity );
  963. dot = DotProduct( velocity, trace->plane.normal );
  964. VectorMA( velocity, -2*dot, trace->plane.normal, ent->s.pos.trDelta );
  965. // cut the velocity to keep from bouncing forever
  966. VectorScale( ent->s.pos.trDelta, ent->physicsBounce, ent->s.pos.trDelta );
  967. // check for stop
  968. if ( trace->plane.normal[2] > 0 && ent->s.pos.trDelta[2] < 40 ) {
  969. G_SetOrigin( ent, trace->endpos );
  970. ent->s.groundEntityNum = trace->entityNum;
  971. return;
  972. }
  973. VectorAdd( ent->currentOrigin, trace->plane.normal, ent->currentOrigin);
  974. VectorCopy( ent->currentOrigin, ent->s.pos.trBase );
  975. ent->s.pos.trTime = level.time;
  976. }
  977. /*
  978. ================
  979. G_RunItem
  980. ================
  981. */
  982. void G_RunItem( gentity_t *ent ) {
  983. vec3_t origin;
  984. trace_t tr;
  985. int contents;
  986. int mask;
  987. // if groundentity has been set to -1, it may have been pushed off an edge
  988. if ( ent->s.groundEntityNum == ENTITYNUM_NONE )
  989. {
  990. if ( ent->s.pos.trType != TR_GRAVITY )
  991. {
  992. ent->s.pos.trType = TR_GRAVITY;
  993. ent->s.pos.trTime = level.time;
  994. }
  995. }
  996. if ( ent->s.pos.trType == TR_STATIONARY )
  997. {
  998. // check think function
  999. G_RunThink( ent );
  1000. if ( !g_gravity->value )
  1001. {
  1002. ent->s.pos.trType = TR_GRAVITY;
  1003. ent->s.pos.trTime = level.time;
  1004. ent->s.pos.trDelta[0] += crandom() * 40.0f; // I dunno, just do this??
  1005. ent->s.pos.trDelta[1] += crandom() * 40.0f;
  1006. ent->s.pos.trDelta[2] += random() * 20.0f;
  1007. }
  1008. return;
  1009. }
  1010. // get current position
  1011. EvaluateTrajectory( &ent->s.pos, level.time, origin );
  1012. // trace a line from the previous position to the current position
  1013. if ( ent->clipmask )
  1014. {
  1015. mask = ent->clipmask;
  1016. }
  1017. else
  1018. {
  1019. mask = MASK_SOLID|CONTENTS_PLAYERCLIP;//shouldn't be able to get anywhere player can't
  1020. }
  1021. int ignore = ENTITYNUM_NONE;
  1022. if ( ent->owner )
  1023. {
  1024. ignore = ent->owner->s.number;
  1025. }
  1026. else if ( ent->activator )
  1027. {
  1028. ignore = ent->activator->s.number;
  1029. }
  1030. gi.trace( &tr, ent->currentOrigin, ent->mins, ent->maxs, origin, ignore, mask );
  1031. VectorCopy( tr.endpos, ent->currentOrigin );
  1032. if ( tr.startsolid )
  1033. {
  1034. tr.fraction = 0;
  1035. }
  1036. gi.linkentity( ent ); // FIXME: avoid this for stationary?
  1037. // check think function
  1038. G_RunThink( ent );
  1039. if ( tr.fraction == 1 )
  1040. {
  1041. if ( g_gravity->value <= 0 )
  1042. {
  1043. if ( ent->s.apos.trType != TR_LINEAR )
  1044. {
  1045. VectorCopy( ent->currentAngles, ent->s.apos.trBase );
  1046. ent->s.apos.trType = TR_LINEAR;
  1047. ent->s.apos.trDelta[1] = Q_flrand( -300, 300 );
  1048. ent->s.apos.trDelta[0] = Q_flrand( -10, 10 );
  1049. ent->s.apos.trDelta[2] = Q_flrand( -10, 10 );
  1050. ent->s.apos.trTime = level.time;
  1051. }
  1052. }
  1053. //friction in zero-G
  1054. if ( !g_gravity->value )
  1055. {
  1056. float friction = 0.975f;
  1057. /*friction -= ent->mass/1000.0f;
  1058. if ( friction < 0.1 )
  1059. {
  1060. friction = 0.1f;
  1061. }
  1062. */
  1063. VectorScale( ent->s.pos.trDelta, friction, ent->s.pos.trDelta );
  1064. VectorCopy( ent->currentOrigin, ent->s.pos.trBase );
  1065. ent->s.pos.trTime = level.time;
  1066. }
  1067. return;
  1068. }
  1069. // if it is in a nodrop volume, remove it
  1070. contents = gi.pointcontents( ent->currentOrigin, -1 );
  1071. if ( contents & CONTENTS_NODROP )
  1072. {
  1073. G_FreeEntity( ent );
  1074. return;
  1075. }
  1076. if ( !tr.startsolid )
  1077. {
  1078. G_BounceItem( ent, &tr );
  1079. }
  1080. }
  1081. /*
  1082. ================
  1083. ItemUse_Bacta
  1084. ================
  1085. */
  1086. void ItemUse_Bacta(gentity_t *ent)
  1087. {
  1088. if (!ent || !ent->client)
  1089. {
  1090. return;
  1091. }
  1092. if (ent->health >= ent->client->ps.stats[STAT_MAX_HEALTH] || !ent->client->ps.inventory[INV_BACTA_CANISTER] )
  1093. {
  1094. return;
  1095. }
  1096. ent->health += MAX_BACTA_HEAL_AMOUNT;
  1097. if (ent->health > ent->client->ps.stats[STAT_MAX_HEALTH])
  1098. {
  1099. ent->health = ent->client->ps.stats[STAT_MAX_HEALTH];
  1100. }
  1101. ent->client->ps.inventory[INV_BACTA_CANISTER]--;
  1102. G_SoundOnEnt( ent, CHAN_VOICE, va( "sound/weapons/force/heal%d_%c.mp3", Q_irand( 1, 4 ), g_sex->string[0] ) );
  1103. }