g_items.c 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011
  1. /*
  2. ===========================================================================
  3. Copyright (C) 1999-2005 Id Software, Inc.
  4. This file is part of Quake III Arena source code.
  5. Quake III Arena source code is free software; you can redistribute it
  6. and/or modify it under the terms of the GNU General Public License as
  7. published by the Free Software Foundation; either version 2 of the License,
  8. or (at your option) any later version.
  9. Quake III Arena source code is distributed in the hope that it will be
  10. useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. GNU General Public License for more details.
  13. You should have received a copy of the GNU General Public License
  14. along with Foobar; if not, write to the Free Software
  15. Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
  16. ===========================================================================
  17. */
  18. //
  19. #include "g_local.h"
  20. /*
  21. Items are any object that a player can touch to gain some effect.
  22. Pickup will return the number of seconds until they should respawn.
  23. all items should pop when dropped in lava or slime
  24. Respawnable items don't actually go away when picked up, they are
  25. just made invisible and untouchable. This allows them to ride
  26. movers and respawn apropriately.
  27. */
  28. #define RESPAWN_ARMOR 25
  29. #define RESPAWN_HEALTH 35
  30. #define RESPAWN_AMMO 40
  31. #define RESPAWN_HOLDABLE 60
  32. #define RESPAWN_MEGAHEALTH 35//120
  33. #define RESPAWN_POWERUP 120
  34. //======================================================================
  35. int Pickup_Powerup( gentity_t *ent, gentity_t *other ) {
  36. int quantity;
  37. int i;
  38. gclient_t *client;
  39. if ( !other->client->ps.powerups[ent->item->giTag] ) {
  40. // round timing to seconds to make multiple powerup timers
  41. // count in sync
  42. other->client->ps.powerups[ent->item->giTag] =
  43. level.time - ( level.time % 1000 );
  44. }
  45. if ( ent->count ) {
  46. quantity = ent->count;
  47. } else {
  48. quantity = ent->item->quantity;
  49. }
  50. other->client->ps.powerups[ent->item->giTag] += quantity * 1000;
  51. // give any nearby players a "denied" anti-reward
  52. for ( i = 0 ; i < level.maxclients ; i++ ) {
  53. vec3_t delta;
  54. float len;
  55. vec3_t forward;
  56. trace_t tr;
  57. client = &level.clients[i];
  58. if ( client == other->client ) {
  59. continue;
  60. }
  61. if ( client->pers.connected == CON_DISCONNECTED ) {
  62. continue;
  63. }
  64. if ( client->ps.stats[STAT_HEALTH] <= 0 ) {
  65. continue;
  66. }
  67. // if same team in team game, no sound
  68. // cannot use OnSameTeam as it expects to g_entities, not clients
  69. if ( g_gametype.integer >= GT_TEAM && other->client->sess.sessionTeam == client->sess.sessionTeam ) {
  70. continue;
  71. }
  72. // if too far away, no sound
  73. VectorSubtract( ent->s.pos.trBase, client->ps.origin, delta );
  74. len = VectorNormalize( delta );
  75. if ( len > 192 ) {
  76. continue;
  77. }
  78. // if not facing, no sound
  79. AngleVectors( client->ps.viewangles, forward, NULL, NULL );
  80. if ( DotProduct( delta, forward ) < 0.4 ) {
  81. continue;
  82. }
  83. // if not line of sight, no sound
  84. trap_Trace( &tr, client->ps.origin, NULL, NULL, ent->s.pos.trBase, ENTITYNUM_NONE, CONTENTS_SOLID );
  85. if ( tr.fraction != 1.0 ) {
  86. continue;
  87. }
  88. // anti-reward
  89. client->ps.persistant[PERS_PLAYEREVENTS] ^= PLAYEREVENT_DENIEDREWARD;
  90. }
  91. return RESPAWN_POWERUP;
  92. }
  93. //======================================================================
  94. #ifdef MISSIONPACK
  95. int Pickup_PersistantPowerup( gentity_t *ent, gentity_t *other ) {
  96. int clientNum;
  97. char userinfo[MAX_INFO_STRING];
  98. float handicap;
  99. int max;
  100. other->client->ps.stats[STAT_PERSISTANT_POWERUP] = ent->item - bg_itemlist;
  101. other->client->persistantPowerup = ent;
  102. switch( ent->item->giTag ) {
  103. case PW_GUARD:
  104. clientNum = other->client->ps.clientNum;
  105. trap_GetUserinfo( clientNum, userinfo, sizeof(userinfo) );
  106. handicap = atof( Info_ValueForKey( userinfo, "handicap" ) );
  107. if( handicap<=0.0f || handicap>100.0f) {
  108. handicap = 100.0f;
  109. }
  110. max = (int)(2 * handicap);
  111. other->health = max;
  112. other->client->ps.stats[STAT_HEALTH] = max;
  113. other->client->ps.stats[STAT_MAX_HEALTH] = max;
  114. other->client->ps.stats[STAT_ARMOR] = max;
  115. other->client->pers.maxHealth = max;
  116. break;
  117. case PW_SCOUT:
  118. clientNum = other->client->ps.clientNum;
  119. trap_GetUserinfo( clientNum, userinfo, sizeof(userinfo) );
  120. handicap = atof( Info_ValueForKey( userinfo, "handicap" ) );
  121. if( handicap<=0.0f || handicap>100.0f) {
  122. handicap = 100.0f;
  123. }
  124. other->client->pers.maxHealth = handicap;
  125. other->client->ps.stats[STAT_ARMOR] = 0;
  126. break;
  127. case PW_DOUBLER:
  128. clientNum = other->client->ps.clientNum;
  129. trap_GetUserinfo( clientNum, userinfo, sizeof(userinfo) );
  130. handicap = atof( Info_ValueForKey( userinfo, "handicap" ) );
  131. if( handicap<=0.0f || handicap>100.0f) {
  132. handicap = 100.0f;
  133. }
  134. other->client->pers.maxHealth = handicap;
  135. break;
  136. case PW_AMMOREGEN:
  137. clientNum = other->client->ps.clientNum;
  138. trap_GetUserinfo( clientNum, userinfo, sizeof(userinfo) );
  139. handicap = atof( Info_ValueForKey( userinfo, "handicap" ) );
  140. if( handicap<=0.0f || handicap>100.0f) {
  141. handicap = 100.0f;
  142. }
  143. other->client->pers.maxHealth = handicap;
  144. memset(other->client->ammoTimes, 0, sizeof(other->client->ammoTimes));
  145. break;
  146. default:
  147. clientNum = other->client->ps.clientNum;
  148. trap_GetUserinfo( clientNum, userinfo, sizeof(userinfo) );
  149. handicap = atof( Info_ValueForKey( userinfo, "handicap" ) );
  150. if( handicap<=0.0f || handicap>100.0f) {
  151. handicap = 100.0f;
  152. }
  153. other->client->pers.maxHealth = handicap;
  154. break;
  155. }
  156. return -1;
  157. }
  158. //======================================================================
  159. #endif
  160. int Pickup_Holdable( gentity_t *ent, gentity_t *other ) {
  161. other->client->ps.stats[STAT_HOLDABLE_ITEM] = ent->item - bg_itemlist;
  162. if( ent->item->giTag == HI_KAMIKAZE ) {
  163. other->client->ps.eFlags |= EF_KAMIKAZE;
  164. }
  165. return RESPAWN_HOLDABLE;
  166. }
  167. //======================================================================
  168. void Add_Ammo (gentity_t *ent, int weapon, int count)
  169. {
  170. ent->client->ps.ammo[weapon] += count;
  171. if ( ent->client->ps.ammo[weapon] > 200 ) {
  172. ent->client->ps.ammo[weapon] = 200;
  173. }
  174. }
  175. int Pickup_Ammo (gentity_t *ent, gentity_t *other)
  176. {
  177. int quantity;
  178. if ( ent->count ) {
  179. quantity = ent->count;
  180. } else {
  181. quantity = ent->item->quantity;
  182. }
  183. Add_Ammo (other, ent->item->giTag, quantity);
  184. return RESPAWN_AMMO;
  185. }
  186. //======================================================================
  187. int Pickup_Weapon (gentity_t *ent, gentity_t *other) {
  188. int quantity;
  189. if ( ent->count < 0 ) {
  190. quantity = 0; // None for you, sir!
  191. } else {
  192. if ( ent->count ) {
  193. quantity = ent->count;
  194. } else {
  195. quantity = ent->item->quantity;
  196. }
  197. // dropped items and teamplay weapons always have full ammo
  198. if ( ! (ent->flags & FL_DROPPED_ITEM) && g_gametype.integer != GT_TEAM ) {
  199. // respawning rules
  200. // drop the quantity if the already have over the minimum
  201. if ( other->client->ps.ammo[ ent->item->giTag ] < quantity ) {
  202. quantity = quantity - other->client->ps.ammo[ ent->item->giTag ];
  203. } else {
  204. quantity = 1; // only add a single shot
  205. }
  206. }
  207. }
  208. // add the weapon
  209. other->client->ps.stats[STAT_WEAPONS] |= ( 1 << ent->item->giTag );
  210. Add_Ammo( other, ent->item->giTag, quantity );
  211. if (ent->item->giTag == WP_GRAPPLING_HOOK)
  212. other->client->ps.ammo[ent->item->giTag] = -1; // unlimited ammo
  213. // team deathmatch has slow weapon respawns
  214. if ( g_gametype.integer == GT_TEAM ) {
  215. return g_weaponTeamRespawn.integer;
  216. }
  217. return g_weaponRespawn.integer;
  218. }
  219. //======================================================================
  220. int Pickup_Health (gentity_t *ent, gentity_t *other) {
  221. int max;
  222. int quantity;
  223. // small and mega healths will go over the max
  224. #ifdef MISSIONPACK
  225. if( other->client && bg_itemlist[other->client->ps.stats[STAT_PERSISTANT_POWERUP]].giTag == PW_GUARD ) {
  226. max = other->client->ps.stats[STAT_MAX_HEALTH];
  227. }
  228. else
  229. #endif
  230. if ( ent->item->quantity != 5 && ent->item->quantity != 100 ) {
  231. max = other->client->ps.stats[STAT_MAX_HEALTH];
  232. } else {
  233. max = other->client->ps.stats[STAT_MAX_HEALTH] * 2;
  234. }
  235. if ( ent->count ) {
  236. quantity = ent->count;
  237. } else {
  238. quantity = ent->item->quantity;
  239. }
  240. other->health += quantity;
  241. if (other->health > max ) {
  242. other->health = max;
  243. }
  244. other->client->ps.stats[STAT_HEALTH] = other->health;
  245. if ( ent->item->quantity == 100 ) { // mega health respawns slow
  246. return RESPAWN_MEGAHEALTH;
  247. }
  248. return RESPAWN_HEALTH;
  249. }
  250. //======================================================================
  251. int Pickup_Armor( gentity_t *ent, gentity_t *other ) {
  252. #ifdef MISSIONPACK
  253. int upperBound;
  254. other->client->ps.stats[STAT_ARMOR] += ent->item->quantity;
  255. if( other->client && bg_itemlist[other->client->ps.stats[STAT_PERSISTANT_POWERUP]].giTag == PW_GUARD ) {
  256. upperBound = other->client->ps.stats[STAT_MAX_HEALTH];
  257. }
  258. else {
  259. upperBound = other->client->ps.stats[STAT_MAX_HEALTH] * 2;
  260. }
  261. if ( other->client->ps.stats[STAT_ARMOR] > upperBound ) {
  262. other->client->ps.stats[STAT_ARMOR] = upperBound;
  263. }
  264. #else
  265. other->client->ps.stats[STAT_ARMOR] += ent->item->quantity;
  266. if ( other->client->ps.stats[STAT_ARMOR] > other->client->ps.stats[STAT_MAX_HEALTH] * 2 ) {
  267. other->client->ps.stats[STAT_ARMOR] = other->client->ps.stats[STAT_MAX_HEALTH] * 2;
  268. }
  269. #endif
  270. return RESPAWN_ARMOR;
  271. }
  272. //======================================================================
  273. /*
  274. ===============
  275. RespawnItem
  276. ===============
  277. */
  278. void RespawnItem( gentity_t *ent ) {
  279. // randomly select from teamed entities
  280. if (ent->team) {
  281. gentity_t *master;
  282. int count;
  283. int choice;
  284. if ( !ent->teammaster ) {
  285. G_Error( "RespawnItem: bad teammaster");
  286. }
  287. master = ent->teammaster;
  288. for (count = 0, ent = master; ent; ent = ent->teamchain, count++)
  289. ;
  290. choice = rand() % count;
  291. for (count = 0, ent = master; count < choice; ent = ent->teamchain, count++)
  292. ;
  293. }
  294. ent->r.contents = CONTENTS_TRIGGER;
  295. ent->s.eFlags &= ~EF_NODRAW;
  296. ent->r.svFlags &= ~SVF_NOCLIENT;
  297. trap_LinkEntity (ent);
  298. if ( ent->item->giType == IT_POWERUP ) {
  299. // play powerup spawn sound to all clients
  300. gentity_t *te;
  301. // if the powerup respawn sound should Not be global
  302. if (ent->speed) {
  303. te = G_TempEntity( ent->s.pos.trBase, EV_GENERAL_SOUND );
  304. }
  305. else {
  306. te = G_TempEntity( ent->s.pos.trBase, EV_GLOBAL_SOUND );
  307. }
  308. te->s.eventParm = G_SoundIndex( "sound/items/poweruprespawn.wav" );
  309. te->r.svFlags |= SVF_BROADCAST;
  310. }
  311. if ( ent->item->giType == IT_HOLDABLE && ent->item->giTag == HI_KAMIKAZE ) {
  312. // play powerup spawn sound to all clients
  313. gentity_t *te;
  314. // if the powerup respawn sound should Not be global
  315. if (ent->speed) {
  316. te = G_TempEntity( ent->s.pos.trBase, EV_GENERAL_SOUND );
  317. }
  318. else {
  319. te = G_TempEntity( ent->s.pos.trBase, EV_GLOBAL_SOUND );
  320. }
  321. te->s.eventParm = G_SoundIndex( "sound/items/kamikazerespawn.wav" );
  322. te->r.svFlags |= SVF_BROADCAST;
  323. }
  324. // play the normal respawn sound only to nearby clients
  325. G_AddEvent( ent, EV_ITEM_RESPAWN, 0 );
  326. ent->nextthink = 0;
  327. }
  328. /*
  329. ===============
  330. Touch_Item
  331. ===============
  332. */
  333. void Touch_Item (gentity_t *ent, gentity_t *other, trace_t *trace) {
  334. int respawn;
  335. qboolean predict;
  336. if (!other->client)
  337. return;
  338. if (other->health < 1)
  339. return; // dead people can't pickup
  340. // the same pickup rules are used for client side and server side
  341. if ( !BG_CanItemBeGrabbed( g_gametype.integer, &ent->s, &other->client->ps ) ) {
  342. return;
  343. }
  344. G_LogPrintf( "Item: %i %s\n", other->s.number, ent->item->classname );
  345. predict = other->client->pers.predictItemPickup;
  346. // call the item-specific pickup function
  347. switch( ent->item->giType ) {
  348. case IT_WEAPON:
  349. respawn = Pickup_Weapon(ent, other);
  350. // predict = qfalse;
  351. break;
  352. case IT_AMMO:
  353. respawn = Pickup_Ammo(ent, other);
  354. // predict = qfalse;
  355. break;
  356. case IT_ARMOR:
  357. respawn = Pickup_Armor(ent, other);
  358. break;
  359. case IT_HEALTH:
  360. respawn = Pickup_Health(ent, other);
  361. break;
  362. case IT_POWERUP:
  363. respawn = Pickup_Powerup(ent, other);
  364. predict = qfalse;
  365. break;
  366. #ifdef MISSIONPACK
  367. case IT_PERSISTANT_POWERUP:
  368. respawn = Pickup_PersistantPowerup(ent, other);
  369. break;
  370. #endif
  371. case IT_TEAM:
  372. respawn = Pickup_Team(ent, other);
  373. break;
  374. case IT_HOLDABLE:
  375. respawn = Pickup_Holdable(ent, other);
  376. break;
  377. default:
  378. return;
  379. }
  380. if ( !respawn ) {
  381. return;
  382. }
  383. // play the normal pickup sound
  384. if (predict) {
  385. G_AddPredictableEvent( other, EV_ITEM_PICKUP, ent->s.modelindex );
  386. } else {
  387. G_AddEvent( other, EV_ITEM_PICKUP, ent->s.modelindex );
  388. }
  389. // powerup pickups are global broadcasts
  390. if ( ent->item->giType == IT_POWERUP || ent->item->giType == IT_TEAM) {
  391. // if we want the global sound to play
  392. if (!ent->speed) {
  393. gentity_t *te;
  394. te = G_TempEntity( ent->s.pos.trBase, EV_GLOBAL_ITEM_PICKUP );
  395. te->s.eventParm = ent->s.modelindex;
  396. te->r.svFlags |= SVF_BROADCAST;
  397. } else {
  398. gentity_t *te;
  399. te = G_TempEntity( ent->s.pos.trBase, EV_GLOBAL_ITEM_PICKUP );
  400. te->s.eventParm = ent->s.modelindex;
  401. // only send this temp entity to a single client
  402. te->r.svFlags |= SVF_SINGLECLIENT;
  403. te->r.singleClient = other->s.number;
  404. }
  405. }
  406. // fire item targets
  407. G_UseTargets (ent, other);
  408. // wait of -1 will not respawn
  409. if ( ent->wait == -1 ) {
  410. ent->r.svFlags |= SVF_NOCLIENT;
  411. ent->s.eFlags |= EF_NODRAW;
  412. ent->r.contents = 0;
  413. ent->unlinkAfterEvent = qtrue;
  414. return;
  415. }
  416. // non zero wait overrides respawn time
  417. if ( ent->wait ) {
  418. respawn = ent->wait;
  419. }
  420. // random can be used to vary the respawn time
  421. if ( ent->random ) {
  422. respawn += crandom() * ent->random;
  423. if ( respawn < 1 ) {
  424. respawn = 1;
  425. }
  426. }
  427. // dropped items will not respawn
  428. if ( ent->flags & FL_DROPPED_ITEM ) {
  429. ent->freeAfterEvent = qtrue;
  430. }
  431. // picked up items still stay around, they just don't
  432. // draw anything. This allows respawnable items
  433. // to be placed on movers.
  434. ent->r.svFlags |= SVF_NOCLIENT;
  435. ent->s.eFlags |= EF_NODRAW;
  436. ent->r.contents = 0;
  437. // ZOID
  438. // A negative respawn times means to never respawn this item (but don't
  439. // delete it). This is used by items that are respawned by third party
  440. // events such as ctf flags
  441. if ( respawn <= 0 ) {
  442. ent->nextthink = 0;
  443. ent->think = 0;
  444. } else {
  445. ent->nextthink = level.time + respawn * 1000;
  446. ent->think = RespawnItem;
  447. }
  448. trap_LinkEntity( ent );
  449. }
  450. //======================================================================
  451. /*
  452. ================
  453. LaunchItem
  454. Spawns an item and tosses it forward
  455. ================
  456. */
  457. gentity_t *LaunchItem( gitem_t *item, vec3_t origin, vec3_t velocity ) {
  458. gentity_t *dropped;
  459. dropped = G_Spawn();
  460. dropped->s.eType = ET_ITEM;
  461. dropped->s.modelindex = item - bg_itemlist; // store item number in modelindex
  462. dropped->s.modelindex2 = 1; // This is non-zero is it's a dropped item
  463. dropped->classname = item->classname;
  464. dropped->item = item;
  465. VectorSet (dropped->r.mins, -ITEM_RADIUS, -ITEM_RADIUS, -ITEM_RADIUS);
  466. VectorSet (dropped->r.maxs, ITEM_RADIUS, ITEM_RADIUS, ITEM_RADIUS);
  467. dropped->r.contents = CONTENTS_TRIGGER;
  468. dropped->touch = Touch_Item;
  469. G_SetOrigin( dropped, origin );
  470. dropped->s.pos.trType = TR_GRAVITY;
  471. dropped->s.pos.trTime = level.time;
  472. VectorCopy( velocity, dropped->s.pos.trDelta );
  473. dropped->s.eFlags |= EF_BOUNCE_HALF;
  474. #ifdef MISSIONPACK
  475. if ((g_gametype.integer == GT_CTF || g_gametype.integer == GT_1FCTF) && item->giType == IT_TEAM) { // Special case for CTF flags
  476. #else
  477. if (g_gametype.integer == GT_CTF && item->giType == IT_TEAM) { // Special case for CTF flags
  478. #endif
  479. dropped->think = Team_DroppedFlagThink;
  480. dropped->nextthink = level.time + 30000;
  481. Team_CheckDroppedItem( dropped );
  482. } else { // auto-remove after 30 seconds
  483. dropped->think = G_FreeEntity;
  484. dropped->nextthink = level.time + 30000;
  485. }
  486. dropped->flags = FL_DROPPED_ITEM;
  487. trap_LinkEntity (dropped);
  488. return dropped;
  489. }
  490. /*
  491. ================
  492. Drop_Item
  493. Spawns an item and tosses it forward
  494. ================
  495. */
  496. gentity_t *Drop_Item( gentity_t *ent, gitem_t *item, float angle ) {
  497. vec3_t velocity;
  498. vec3_t angles;
  499. VectorCopy( ent->s.apos.trBase, angles );
  500. angles[YAW] += angle;
  501. angles[PITCH] = 0; // always forward
  502. AngleVectors( angles, velocity, NULL, NULL );
  503. VectorScale( velocity, 150, velocity );
  504. velocity[2] += 200 + crandom() * 50;
  505. return LaunchItem( item, ent->s.pos.trBase, velocity );
  506. }
  507. /*
  508. ================
  509. Use_Item
  510. Respawn the item
  511. ================
  512. */
  513. void Use_Item( gentity_t *ent, gentity_t *other, gentity_t *activator ) {
  514. RespawnItem( ent );
  515. }
  516. //======================================================================
  517. /*
  518. ================
  519. FinishSpawningItem
  520. Traces down to find where an item should rest, instead of letting them
  521. free fall from their spawn points
  522. ================
  523. */
  524. void FinishSpawningItem( gentity_t *ent ) {
  525. trace_t tr;
  526. vec3_t dest;
  527. VectorSet( ent->r.mins, -ITEM_RADIUS, -ITEM_RADIUS, -ITEM_RADIUS );
  528. VectorSet( ent->r.maxs, ITEM_RADIUS, ITEM_RADIUS, ITEM_RADIUS );
  529. ent->s.eType = ET_ITEM;
  530. ent->s.modelindex = ent->item - bg_itemlist; // store item number in modelindex
  531. ent->s.modelindex2 = 0; // zero indicates this isn't a dropped item
  532. ent->r.contents = CONTENTS_TRIGGER;
  533. ent->touch = Touch_Item;
  534. // useing an item causes it to respawn
  535. ent->use = Use_Item;
  536. if ( ent->spawnflags & 1 ) {
  537. // suspended
  538. G_SetOrigin( ent, ent->s.origin );
  539. } else {
  540. // drop to floor
  541. VectorSet( dest, ent->s.origin[0], ent->s.origin[1], ent->s.origin[2] - 4096 );
  542. trap_Trace( &tr, ent->s.origin, ent->r.mins, ent->r.maxs, dest, ent->s.number, MASK_SOLID );
  543. if ( tr.startsolid ) {
  544. G_Printf ("FinishSpawningItem: %s startsolid at %s\n", ent->classname, vtos(ent->s.origin));
  545. G_FreeEntity( ent );
  546. return;
  547. }
  548. // allow to ride movers
  549. ent->s.groundEntityNum = tr.entityNum;
  550. G_SetOrigin( ent, tr.endpos );
  551. }
  552. // team slaves and targeted items aren't present at start
  553. if ( ( ent->flags & FL_TEAMSLAVE ) || ent->targetname ) {
  554. ent->s.eFlags |= EF_NODRAW;
  555. ent->r.contents = 0;
  556. return;
  557. }
  558. // powerups don't spawn in for a while
  559. if ( ent->item->giType == IT_POWERUP ) {
  560. float respawn;
  561. respawn = 45 + crandom() * 15;
  562. ent->s.eFlags |= EF_NODRAW;
  563. ent->r.contents = 0;
  564. ent->nextthink = level.time + respawn * 1000;
  565. ent->think = RespawnItem;
  566. return;
  567. }
  568. trap_LinkEntity (ent);
  569. }
  570. qboolean itemRegistered[MAX_ITEMS];
  571. /*
  572. ==================
  573. G_CheckTeamItems
  574. ==================
  575. */
  576. void G_CheckTeamItems( void ) {
  577. // Set up team stuff
  578. Team_InitGame();
  579. if( g_gametype.integer == GT_CTF ) {
  580. gitem_t *item;
  581. // check for the two flags
  582. item = BG_FindItem( "Red Flag" );
  583. if ( !item || !itemRegistered[ item - bg_itemlist ] ) {
  584. G_Printf( S_COLOR_YELLOW "WARNING: No team_CTF_redflag in map" );
  585. }
  586. item = BG_FindItem( "Blue Flag" );
  587. if ( !item || !itemRegistered[ item - bg_itemlist ] ) {
  588. G_Printf( S_COLOR_YELLOW "WARNING: No team_CTF_blueflag in map" );
  589. }
  590. }
  591. #ifdef MISSIONPACK
  592. if( g_gametype.integer == GT_1FCTF ) {
  593. gitem_t *item;
  594. // check for all three flags
  595. item = BG_FindItem( "Red Flag" );
  596. if ( !item || !itemRegistered[ item - bg_itemlist ] ) {
  597. G_Printf( S_COLOR_YELLOW "WARNING: No team_CTF_redflag in map" );
  598. }
  599. item = BG_FindItem( "Blue Flag" );
  600. if ( !item || !itemRegistered[ item - bg_itemlist ] ) {
  601. G_Printf( S_COLOR_YELLOW "WARNING: No team_CTF_blueflag in map" );
  602. }
  603. item = BG_FindItem( "Neutral Flag" );
  604. if ( !item || !itemRegistered[ item - bg_itemlist ] ) {
  605. G_Printf( S_COLOR_YELLOW "WARNING: No team_CTF_neutralflag in map" );
  606. }
  607. }
  608. if( g_gametype.integer == GT_OBELISK ) {
  609. gentity_t *ent;
  610. // check for the two obelisks
  611. ent = NULL;
  612. ent = G_Find( ent, FOFS(classname), "team_redobelisk" );
  613. if( !ent ) {
  614. G_Printf( S_COLOR_YELLOW "WARNING: No team_redobelisk in map" );
  615. }
  616. ent = NULL;
  617. ent = G_Find( ent, FOFS(classname), "team_blueobelisk" );
  618. if( !ent ) {
  619. G_Printf( S_COLOR_YELLOW "WARNING: No team_blueobelisk in map" );
  620. }
  621. }
  622. if( g_gametype.integer == GT_HARVESTER ) {
  623. gentity_t *ent;
  624. // check for all three obelisks
  625. ent = NULL;
  626. ent = G_Find( ent, FOFS(classname), "team_redobelisk" );
  627. if( !ent ) {
  628. G_Printf( S_COLOR_YELLOW "WARNING: No team_redobelisk in map" );
  629. }
  630. ent = NULL;
  631. ent = G_Find( ent, FOFS(classname), "team_blueobelisk" );
  632. if( !ent ) {
  633. G_Printf( S_COLOR_YELLOW "WARNING: No team_blueobelisk in map" );
  634. }
  635. ent = NULL;
  636. ent = G_Find( ent, FOFS(classname), "team_neutralobelisk" );
  637. if( !ent ) {
  638. G_Printf( S_COLOR_YELLOW "WARNING: No team_neutralobelisk in map" );
  639. }
  640. }
  641. #endif
  642. }
  643. /*
  644. ==============
  645. ClearRegisteredItems
  646. ==============
  647. */
  648. void ClearRegisteredItems( void ) {
  649. memset( itemRegistered, 0, sizeof( itemRegistered ) );
  650. // players always start with the base weapon
  651. RegisterItem( BG_FindItemForWeapon( WP_MACHINEGUN ) );
  652. RegisterItem( BG_FindItemForWeapon( WP_GAUNTLET ) );
  653. #ifdef MISSIONPACK
  654. if( g_gametype.integer == GT_HARVESTER ) {
  655. RegisterItem( BG_FindItem( "Red Cube" ) );
  656. RegisterItem( BG_FindItem( "Blue Cube" ) );
  657. }
  658. #endif
  659. }
  660. /*
  661. ===============
  662. RegisterItem
  663. The item will be added to the precache list
  664. ===============
  665. */
  666. void RegisterItem( gitem_t *item ) {
  667. if ( !item ) {
  668. G_Error( "RegisterItem: NULL" );
  669. }
  670. itemRegistered[ item - bg_itemlist ] = qtrue;
  671. }
  672. /*
  673. ===============
  674. SaveRegisteredItems
  675. Write the needed items to a config string
  676. so the client will know which ones to precache
  677. ===============
  678. */
  679. void SaveRegisteredItems( void ) {
  680. char string[MAX_ITEMS+1];
  681. int i;
  682. int count;
  683. count = 0;
  684. for ( i = 0 ; i < bg_numItems ; i++ ) {
  685. if ( itemRegistered[i] ) {
  686. count++;
  687. string[i] = '1';
  688. } else {
  689. string[i] = '0';
  690. }
  691. }
  692. string[ bg_numItems ] = 0;
  693. G_Printf( "%i items registered\n", count );
  694. trap_SetConfigstring(CS_ITEMS, string);
  695. }
  696. /*
  697. ============
  698. G_ItemDisabled
  699. ============
  700. */
  701. int G_ItemDisabled( gitem_t *item ) {
  702. char name[128];
  703. Com_sprintf(name, sizeof(name), "disable_%s", item->classname);
  704. return trap_Cvar_VariableIntegerValue( name );
  705. }
  706. /*
  707. ============
  708. G_SpawnItem
  709. Sets the clipping size and plants the object on the floor.
  710. Items can't be immediately dropped to floor, because they might
  711. be on an entity that hasn't spawned yet.
  712. ============
  713. */
  714. void G_SpawnItem (gentity_t *ent, gitem_t *item) {
  715. G_SpawnFloat( "random", "0", &ent->random );
  716. G_SpawnFloat( "wait", "0", &ent->wait );
  717. RegisterItem( item );
  718. if ( G_ItemDisabled(item) )
  719. return;
  720. ent->item = item;
  721. // some movers spawn on the second frame, so delay item
  722. // spawns until the third frame so they can ride trains
  723. ent->nextthink = level.time + FRAMETIME * 2;
  724. ent->think = FinishSpawningItem;
  725. ent->physicsBounce = 0.50; // items are bouncy
  726. if ( item->giType == IT_POWERUP ) {
  727. G_SoundIndex( "sound/items/poweruprespawn.wav" );
  728. G_SpawnFloat( "noglobalsound", "0", &ent->speed);
  729. }
  730. #ifdef MISSIONPACK
  731. if ( item->giType == IT_PERSISTANT_POWERUP ) {
  732. ent->s.generic1 = ent->spawnflags;
  733. }
  734. #endif
  735. }
  736. /*
  737. ================
  738. G_BounceItem
  739. ================
  740. */
  741. void G_BounceItem( gentity_t *ent, trace_t *trace ) {
  742. vec3_t velocity;
  743. float dot;
  744. int hitTime;
  745. // reflect the velocity on the trace plane
  746. hitTime = level.previousTime + ( level.time - level.previousTime ) * trace->fraction;
  747. BG_EvaluateTrajectoryDelta( &ent->s.pos, hitTime, velocity );
  748. dot = DotProduct( velocity, trace->plane.normal );
  749. VectorMA( velocity, -2*dot, trace->plane.normal, ent->s.pos.trDelta );
  750. // cut the velocity to keep from bouncing forever
  751. VectorScale( ent->s.pos.trDelta, ent->physicsBounce, ent->s.pos.trDelta );
  752. // check for stop
  753. if ( trace->plane.normal[2] > 0 && ent->s.pos.trDelta[2] < 40 ) {
  754. trace->endpos[2] += 1.0; // make sure it is off ground
  755. SnapVector( trace->endpos );
  756. G_SetOrigin( ent, trace->endpos );
  757. ent->s.groundEntityNum = trace->entityNum;
  758. return;
  759. }
  760. VectorAdd( ent->r.currentOrigin, trace->plane.normal, ent->r.currentOrigin);
  761. VectorCopy( ent->r.currentOrigin, ent->s.pos.trBase );
  762. ent->s.pos.trTime = level.time;
  763. }
  764. /*
  765. ================
  766. G_RunItem
  767. ================
  768. */
  769. void G_RunItem( gentity_t *ent ) {
  770. vec3_t origin;
  771. trace_t tr;
  772. int contents;
  773. int mask;
  774. // if groundentity has been set to -1, it may have been pushed off an edge
  775. if ( ent->s.groundEntityNum == -1 ) {
  776. if ( ent->s.pos.trType != TR_GRAVITY ) {
  777. ent->s.pos.trType = TR_GRAVITY;
  778. ent->s.pos.trTime = level.time;
  779. }
  780. }
  781. if ( ent->s.pos.trType == TR_STATIONARY ) {
  782. // check think function
  783. G_RunThink( ent );
  784. return;
  785. }
  786. // get current position
  787. BG_EvaluateTrajectory( &ent->s.pos, level.time, origin );
  788. // trace a line from the previous position to the current position
  789. if ( ent->clipmask ) {
  790. mask = ent->clipmask;
  791. } else {
  792. mask = MASK_PLAYERSOLID & ~CONTENTS_BODY;//MASK_SOLID;
  793. }
  794. trap_Trace( &tr, ent->r.currentOrigin, ent->r.mins, ent->r.maxs, origin,
  795. ent->r.ownerNum, mask );
  796. VectorCopy( tr.endpos, ent->r.currentOrigin );
  797. if ( tr.startsolid ) {
  798. tr.fraction = 0;
  799. }
  800. trap_LinkEntity( ent ); // FIXME: avoid this for stationary?
  801. // check think function
  802. G_RunThink( ent );
  803. if ( tr.fraction == 1 ) {
  804. return;
  805. }
  806. // if it is in a nodrop volume, remove it
  807. contents = trap_PointContents( ent->r.currentOrigin, -1 );
  808. if ( contents & CONTENTS_NODROP ) {
  809. if (ent->item && ent->item->giType == IT_TEAM) {
  810. Team_FreeEntity(ent);
  811. } else {
  812. G_FreeEntity( ent );
  813. }
  814. return;
  815. }
  816. G_BounceItem( ent, &tr );
  817. }