Item.cpp 51 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139
  1. /*
  2. ===========================================================================
  3. Doom 3 BFG Edition GPL Source Code
  4. Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company.
  5. This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code").
  6. Doom 3 BFG Edition Source Code is free software: you can redistribute it and/or modify
  7. it under the terms of the GNU General Public License as published by
  8. the Free Software Foundation, either version 3 of the License, or
  9. (at your option) any later version.
  10. Doom 3 BFG Edition Source Code is distributed in the hope that it will be useful,
  11. but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. GNU General Public License for more details.
  14. You should have received a copy of the GNU General Public License
  15. along with Doom 3 BFG Edition Source Code. If not, see <http://www.gnu.org/licenses/>.
  16. In addition, the Doom 3 BFG Edition Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 BFG Edition Source Code. If not, please request a copy in writing from id Software at the address below.
  17. If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
  18. ===========================================================================
  19. */
  20. #include "../idlib/precompiled.h"
  21. #pragma hdrstop
  22. #include "Game_local.h"
  23. /*
  24. ===============================================================================
  25. idItem
  26. ===============================================================================
  27. */
  28. const idEventDef EV_DropToFloor( "<dropToFloor>" );
  29. const idEventDef EV_RespawnItem( "respawn" );
  30. const idEventDef EV_RespawnFx( "<respawnFx>" );
  31. const idEventDef EV_GetPlayerPos( "<getplayerpos>" );
  32. const idEventDef EV_HideObjective( "<hideobjective>", "e" );
  33. CLASS_DECLARATION( idEntity, idItem )
  34. EVENT( EV_DropToFloor, idItem::Event_DropToFloor )
  35. EVENT( EV_Touch, idItem::Event_Touch )
  36. EVENT( EV_Activate, idItem::Event_Trigger )
  37. EVENT( EV_RespawnItem, idItem::Event_Respawn )
  38. EVENT( EV_RespawnFx, idItem::Event_RespawnFx )
  39. END_CLASS
  40. /*
  41. ================
  42. idItem::idItem
  43. ================
  44. */
  45. idItem::idItem() :
  46. clientPredictPickupMilliseconds( 0 ) {
  47. spin = false;
  48. inView = false;
  49. inViewTime = 0;
  50. lastCycle = 0;
  51. lastRenderViewTime = -1;
  52. itemShellHandle = -1;
  53. shellMaterial = NULL;
  54. orgOrigin.Zero();
  55. canPickUp = true;
  56. fl.networkSync = true;
  57. }
  58. /*
  59. ================
  60. idItem::~idItem
  61. ================
  62. */
  63. idItem::~idItem() {
  64. // remove the highlight shell
  65. if ( itemShellHandle != -1 ) {
  66. gameRenderWorld->FreeEntityDef( itemShellHandle );
  67. }
  68. }
  69. /*
  70. ================
  71. idItem::Save
  72. ================
  73. */
  74. void idItem::Save( idSaveGame *savefile ) const {
  75. savefile->WriteVec3( orgOrigin );
  76. savefile->WriteBool( spin );
  77. savefile->WriteBool( pulse );
  78. savefile->WriteBool( canPickUp );
  79. savefile->WriteMaterial( shellMaterial );
  80. savefile->WriteBool( inView );
  81. savefile->WriteInt( inViewTime );
  82. savefile->WriteInt( lastCycle );
  83. savefile->WriteInt( lastRenderViewTime );
  84. }
  85. /*
  86. ================
  87. idItem::Restore
  88. ================
  89. */
  90. void idItem::Restore( idRestoreGame *savefile ) {
  91. savefile->ReadVec3( orgOrigin );
  92. savefile->ReadBool( spin );
  93. savefile->ReadBool( pulse );
  94. savefile->ReadBool( canPickUp );
  95. savefile->ReadMaterial( shellMaterial );
  96. savefile->ReadBool( inView );
  97. savefile->ReadInt( inViewTime );
  98. savefile->ReadInt( lastCycle );
  99. savefile->ReadInt( lastRenderViewTime );
  100. itemShellHandle = -1;
  101. }
  102. /*
  103. ================
  104. idItem::UpdateRenderEntity
  105. ================
  106. */
  107. bool idItem::UpdateRenderEntity( renderEntity_s *renderEntity, const renderView_t *renderView ) const {
  108. if ( lastRenderViewTime == renderView->time[timeGroup] ) {
  109. return false;
  110. }
  111. lastRenderViewTime = renderView->time[timeGroup];
  112. // check for glow highlighting if near the center of the view
  113. idVec3 dir = renderEntity->origin - renderView->vieworg;
  114. dir.Normalize();
  115. float d = dir * renderView->viewaxis[0];
  116. // two second pulse cycle
  117. float cycle = ( renderView->time[timeGroup] - inViewTime ) / 2000.0f;
  118. if ( d > 0.94f ) {
  119. if ( !inView ) {
  120. inView = true;
  121. if ( cycle > lastCycle ) {
  122. // restart at the beginning
  123. inViewTime = renderView->time[timeGroup];
  124. cycle = 0.0f;
  125. }
  126. }
  127. } else {
  128. if ( inView ) {
  129. inView = false;
  130. lastCycle = ceil( cycle );
  131. }
  132. }
  133. // fade down after the last pulse finishes
  134. if ( !inView && cycle > lastCycle ) {
  135. renderEntity->shaderParms[4] = 0.0f;
  136. } else {
  137. // pulse up in 1/4 second
  138. cycle -= (int)cycle;
  139. if ( cycle < 0.1f ) {
  140. renderEntity->shaderParms[4] = cycle * 10.0f;
  141. } else if ( cycle < 0.2f ) {
  142. renderEntity->shaderParms[4] = 1.0f;
  143. } else if ( cycle < 0.3f ) {
  144. renderEntity->shaderParms[4] = 1.0f - ( cycle - 0.2f ) * 10.0f;
  145. } else {
  146. // stay off between pulses
  147. renderEntity->shaderParms[4] = 0.0f;
  148. }
  149. }
  150. // update every single time this is in view
  151. return true;
  152. }
  153. /*
  154. ================
  155. idItem::ModelCallback
  156. ================
  157. */
  158. bool idItem::ModelCallback( renderEntity_t *renderEntity, const renderView_t *renderView ) {
  159. const idItem *ent;
  160. // this may be triggered by a model trace or other non-view related source
  161. if ( !renderView ) {
  162. return false;
  163. }
  164. ent = static_cast<idItem *>(gameLocal.entities[ renderEntity->entityNum ]);
  165. if ( ent == NULL ) {
  166. gameLocal.Error( "idItem::ModelCallback: callback with NULL game entity" );
  167. return false;
  168. }
  169. return ent->UpdateRenderEntity( renderEntity, renderView );
  170. }
  171. /*
  172. ================
  173. idItem::Think
  174. ================
  175. */
  176. void idItem::Think() {
  177. if ( thinkFlags & TH_THINK ) {
  178. if ( spin ) {
  179. idAngles ang;
  180. idVec3 org;
  181. ang.pitch = ang.roll = 0.0f;
  182. ang.yaw = ( gameLocal.time & 4095 ) * 360.0f / -4096.0f;
  183. SetAngles( ang );
  184. float scale = 0.005f + entityNumber * 0.00001f;
  185. org = orgOrigin;
  186. org.z += 4.0f + cos( ( gameLocal.time + 2000 ) * scale ) * 4.0f;
  187. SetOrigin( org );
  188. }
  189. }
  190. Present();
  191. }
  192. /*
  193. ================
  194. idItem::Present
  195. ================
  196. */
  197. void idItem::Present() {
  198. idEntity::Present();
  199. if ( !fl.hidden && pulse ) {
  200. // also add a highlight shell model
  201. renderEntity_t shell;
  202. shell = renderEntity;
  203. // we will mess with shader parms when the item is in view
  204. // to give the "item pulse" effect
  205. shell.callback = idItem::ModelCallback;
  206. shell.entityNum = entityNumber;
  207. shell.customShader = shellMaterial;
  208. if ( itemShellHandle == -1 ) {
  209. itemShellHandle = gameRenderWorld->AddEntityDef( &shell );
  210. } else {
  211. gameRenderWorld->UpdateEntityDef( itemShellHandle, &shell );
  212. }
  213. }
  214. }
  215. /*
  216. ================
  217. idItem::Spawn
  218. ================
  219. */
  220. void idItem::Spawn() {
  221. idStr giveTo;
  222. idEntity * ent;
  223. float tsize;
  224. if ( spawnArgs.GetBool( "dropToFloor" ) ) {
  225. PostEventMS( &EV_DropToFloor, 0 );
  226. }
  227. if ( spawnArgs.GetFloat( "triggersize", "0", tsize ) ) {
  228. GetPhysics()->GetClipModel()->LoadModel( idTraceModel( idBounds( vec3_origin ).Expand( tsize ) ) );
  229. GetPhysics()->GetClipModel()->Link( gameLocal.clip );
  230. }
  231. if ( spawnArgs.GetBool( "start_off" ) ) {
  232. GetPhysics()->SetContents( 0 );
  233. Hide();
  234. } else {
  235. GetPhysics()->SetContents( CONTENTS_TRIGGER );
  236. }
  237. giveTo = spawnArgs.GetString( "owner" );
  238. if ( giveTo.Length() ) {
  239. ent = gameLocal.FindEntity( giveTo );
  240. if ( !ent ) {
  241. gameLocal.Error( "Item couldn't find owner '%s'", giveTo.c_str() );
  242. }
  243. PostEventMS( &EV_Touch, 0, ent, NULL );
  244. }
  245. // idItemTeam does not rotate and bob
  246. if ( spawnArgs.GetBool( "spin" ) || (common->IsMultiplayer() && !this->IsType( idItemTeam::Type ) ) ) {
  247. spin = true;
  248. BecomeActive( TH_THINK );
  249. }
  250. //pulse = !spawnArgs.GetBool( "nopulse" );
  251. //temp hack for tim
  252. pulse = false;
  253. orgOrigin = GetPhysics()->GetOrigin();
  254. canPickUp = !( spawnArgs.GetBool( "triggerFirst" ) || spawnArgs.GetBool( "no_touch" ) );
  255. inViewTime = -1000;
  256. lastCycle = -1;
  257. itemShellHandle = -1;
  258. shellMaterial = declManager->FindMaterial( "itemHighlightShell" );
  259. }
  260. /*
  261. ================
  262. idItem::GetAttributes
  263. ================
  264. */
  265. void idItem::GetAttributes( idDict &attributes ) const {
  266. int i;
  267. const idKeyValue *arg;
  268. for( i = 0; i < spawnArgs.GetNumKeyVals(); i++ ) {
  269. arg = spawnArgs.GetKeyVal( i );
  270. if ( arg->GetKey().Left( 4 ) == "inv_" ) {
  271. attributes.Set( arg->GetKey().Right( arg->GetKey().Length() - 4 ), arg->GetValue() );
  272. }
  273. }
  274. }
  275. /*
  276. ================
  277. idItem::GiveToPlayer
  278. ================
  279. */
  280. bool idItem::GiveToPlayer( idPlayer *player, unsigned int giveFlags ) {
  281. if ( player == NULL ) {
  282. return false;
  283. }
  284. if ( spawnArgs.GetBool( "inv_carry" ) ) {
  285. return player->GiveInventoryItem( &spawnArgs, giveFlags );
  286. }
  287. return player->GiveItem( this, giveFlags );
  288. }
  289. /*
  290. ================
  291. idItem::Pickup
  292. ================
  293. */
  294. bool idItem::Pickup( idPlayer *player ) {
  295. const bool didGiveSucceed = GiveToPlayer( player, ITEM_GIVE_FEEDBACK );
  296. if ( !didGiveSucceed ) {
  297. return false;
  298. }
  299. // Store the time so clients know when to stop predicting and let snapshots overwrite.
  300. if ( player->IsLocallyControlled() ) {
  301. clientPredictPickupMilliseconds = gameLocal.time;
  302. } else {
  303. clientPredictPickupMilliseconds = 0;
  304. }
  305. // play pickup sound
  306. StartSound( "snd_acquire", SND_CHANNEL_ITEM, 0, false, NULL );
  307. // clear our contents so the object isn't picked up twice
  308. GetPhysics()->SetContents( 0 );
  309. // hide the model
  310. Hide();
  311. // remove the highlight shell
  312. if ( itemShellHandle != -1 ) {
  313. gameRenderWorld->FreeEntityDef( itemShellHandle );
  314. itemShellHandle = -1;
  315. }
  316. // Clients need to bail out after some feedback, but
  317. // before actually changing any values. The values
  318. // will be updated in the next snapshot.
  319. if ( common->IsClient() ) {
  320. return didGiveSucceed;
  321. }
  322. if ( !GiveToPlayer( player, ITEM_GIVE_UPDATE_STATE ) ) {
  323. return false;
  324. }
  325. // trigger our targets
  326. ActivateTargets( player );
  327. float respawn = spawnArgs.GetFloat( "respawn" );
  328. bool dropped = spawnArgs.GetBool( "dropped" );
  329. bool no_respawn = spawnArgs.GetBool( "no_respawn" );
  330. if ( common->IsMultiplayer() && respawn == 0.0f ) {
  331. respawn = 20.0f;
  332. }
  333. if ( respawn && !dropped && !no_respawn ) {
  334. const char *sfx = spawnArgs.GetString( "fxRespawn" );
  335. if ( sfx != NULL && *sfx != NULL ) {
  336. PostEventSec( &EV_RespawnFx, respawn - 0.5f );
  337. }
  338. PostEventSec( &EV_RespawnItem, respawn );
  339. } else if ( !spawnArgs.GetBool( "inv_objective" ) && !no_respawn ) {
  340. // give some time for the pickup sound to play
  341. // FIXME: Play on the owner
  342. if ( !spawnArgs.GetBool( "inv_carry" ) ) {
  343. PostEventMS( &EV_Remove, 5000 );
  344. }
  345. }
  346. BecomeInactive( TH_THINK );
  347. return true;
  348. }
  349. /*
  350. ================
  351. idItem::ClientThink
  352. ================
  353. */
  354. void idItem::ClientThink( const int curTime, const float fraction, const bool predict ) {
  355. // only think forward because the state is not synced through snapshots
  356. if ( !gameLocal.isNewFrame ) {
  357. return;
  358. }
  359. Think();
  360. }
  361. /*
  362. ================
  363. idItem::ClientPredictionThink
  364. ================
  365. */
  366. void idItem::ClientPredictionThink() {
  367. // only think forward because the state is not synced through snapshots
  368. if ( !gameLocal.isNewFrame ) {
  369. return;
  370. }
  371. Think();
  372. }
  373. /*
  374. ================
  375. idItem::WriteFromSnapshot
  376. ================
  377. */
  378. void idItem::WriteToSnapshot( idBitMsg &msg ) const {
  379. msg.WriteBits( IsHidden(), 1 );
  380. }
  381. /*
  382. ================
  383. idItem::ReadFromSnapshot
  384. ================
  385. */
  386. void idItem::ReadFromSnapshot( const idBitMsg &msg ) {
  387. if ( msg.ReadBits( 1 ) ) {
  388. Hide();
  389. } else if ( clientPredictPickupMilliseconds != 0 ) {
  390. // Fix mispredictions
  391. if ( gameLocal.GetLastClientUsercmdMilliseconds( gameLocal.GetLocalClientNum() ) >= clientPredictPickupMilliseconds ) {
  392. if ( GetPhysics()->GetContents() == 0 ) {
  393. GetPhysics()->SetContents( CONTENTS_TRIGGER );
  394. }
  395. Show();
  396. }
  397. }
  398. }
  399. /*
  400. ================
  401. idItem::ClientReceiveEvent
  402. ================
  403. */
  404. bool idItem::ClientReceiveEvent( int event, int time, const idBitMsg &msg ) {
  405. switch( event ) {
  406. case EVENT_RESPAWN: {
  407. Event_Respawn();
  408. return true;
  409. }
  410. case EVENT_RESPAWNFX: {
  411. Event_RespawnFx();
  412. return true;
  413. }
  414. default: {
  415. return idEntity::ClientReceiveEvent( event, time, msg );
  416. }
  417. }
  418. }
  419. /*
  420. ================
  421. idItem::Event_DropToFloor
  422. ================
  423. */
  424. void idItem::Event_DropToFloor() {
  425. trace_t trace;
  426. // don't drop the floor if bound to another entity
  427. if ( GetBindMaster() != NULL && GetBindMaster() != this ) {
  428. return;
  429. }
  430. gameLocal.clip.TraceBounds( trace, renderEntity.origin, renderEntity.origin - idVec3( 0, 0, 64 ), renderEntity.bounds, MASK_SOLID | CONTENTS_CORPSE, this );
  431. SetOrigin( trace.endpos );
  432. }
  433. /*
  434. ================
  435. idItem::Event_Touch
  436. ================
  437. */
  438. void idItem::Event_Touch( idEntity *other, trace_t *trace ) {
  439. if ( !other->IsType( idPlayer::Type ) ) {
  440. return;
  441. }
  442. if ( !canPickUp ) {
  443. return;
  444. }
  445. Pickup( static_cast<idPlayer *>(other) );
  446. }
  447. /*
  448. ================
  449. idItem::Event_Trigger
  450. ================
  451. */
  452. void idItem::Event_Trigger( idEntity *activator ) {
  453. if ( !canPickUp && spawnArgs.GetBool( "triggerFirst" ) ) {
  454. canPickUp = true;
  455. return;
  456. }
  457. if ( activator && activator->IsType( idPlayer::Type ) ) {
  458. Pickup( static_cast<idPlayer *>( activator ) );
  459. }
  460. }
  461. /*
  462. ================
  463. idItem::Event_Respawn
  464. ================
  465. */
  466. void idItem::Event_Respawn() {
  467. if ( common->IsServer() ) {
  468. ServerSendEvent( EVENT_RESPAWN, NULL, false );
  469. }
  470. BecomeActive( TH_THINK );
  471. Show();
  472. inViewTime = -1000;
  473. lastCycle = -1;
  474. GetPhysics()->SetContents( CONTENTS_TRIGGER );
  475. SetOrigin( orgOrigin );
  476. StartSound( "snd_respawn", SND_CHANNEL_ITEM, 0, false, NULL );
  477. CancelEvents( &EV_RespawnItem ); // don't double respawn
  478. }
  479. /*
  480. ================
  481. idItem::Event_RespawnFx
  482. ================
  483. */
  484. void idItem::Event_RespawnFx() {
  485. if ( common->IsServer() ) {
  486. ServerSendEvent( EVENT_RESPAWNFX, NULL, false );
  487. }
  488. const char *sfx = spawnArgs.GetString( "fxRespawn" );
  489. if ( sfx != NULL && *sfx != NULL ) {
  490. idEntityFx::StartFx( sfx, NULL, NULL, this, true );
  491. }
  492. }
  493. /*
  494. ===============================================================================
  495. idItemPowerup
  496. ===============================================================================
  497. */
  498. /*
  499. ===============
  500. idItemPowerup
  501. ===============
  502. */
  503. CLASS_DECLARATION( idItem, idItemPowerup )
  504. END_CLASS
  505. /*
  506. ================
  507. idItemPowerup::idItemPowerup
  508. ================
  509. */
  510. idItemPowerup::idItemPowerup() {
  511. time = 0;
  512. type = 0;
  513. }
  514. /*
  515. ================
  516. idItemPowerup::Save
  517. ================
  518. */
  519. void idItemPowerup::Save( idSaveGame *savefile ) const {
  520. savefile->WriteInt( time );
  521. savefile->WriteInt( type );
  522. }
  523. /*
  524. ================
  525. idItemPowerup::Restore
  526. ================
  527. */
  528. void idItemPowerup::Restore( idRestoreGame *savefile ) {
  529. savefile->ReadInt( time );
  530. savefile->ReadInt( type );
  531. }
  532. /*
  533. ================
  534. idItemPowerup::Spawn
  535. ================
  536. */
  537. void idItemPowerup::Spawn() {
  538. time = spawnArgs.GetInt( "time", "30" );
  539. type = spawnArgs.GetInt( "type", "0" );
  540. }
  541. /*
  542. ================
  543. idItemPowerup::GiveToPlayer
  544. ================
  545. */
  546. bool idItemPowerup::GiveToPlayer( idPlayer *player, unsigned int giveFlags ) {
  547. if ( player->spectating ) {
  548. return false;
  549. }
  550. player->GivePowerUp( type, time * 1000, giveFlags );
  551. return true;
  552. }
  553. /*
  554. ===============================================================================
  555. idItemTeam
  556. Used for flags in Capture the Flag
  557. ===============================================================================
  558. */
  559. // temporarely removed these events
  560. const idEventDef EV_FlagReturn( "flagreturn", "e" );
  561. const idEventDef EV_TakeFlag( "takeflag", "e" );
  562. const idEventDef EV_DropFlag( "dropflag", "d" );
  563. const idEventDef EV_FlagCapture( "flagcapture" );
  564. CLASS_DECLARATION( idItem, idItemTeam )
  565. EVENT( EV_FlagReturn, idItemTeam::Event_FlagReturn )
  566. EVENT( EV_TakeFlag, idItemTeam::Event_TakeFlag )
  567. EVENT( EV_DropFlag, idItemTeam::Event_DropFlag )
  568. EVENT( EV_FlagCapture, idItemTeam::Event_FlagCapture )
  569. END_CLASS
  570. /*
  571. ===============
  572. idItemTeam::idItemTeam
  573. ===============
  574. */
  575. idItemTeam::idItemTeam() {
  576. team = -1;
  577. carried = false;
  578. dropped = false;
  579. lastDrop = 0;
  580. itemGlowHandle = -1;
  581. skinDefault = NULL;
  582. skinCarried = NULL;
  583. scriptTaken = NULL;
  584. scriptDropped = NULL;
  585. scriptReturned = NULL;
  586. scriptCaptured = NULL;
  587. lastNuggetDrop = 0;
  588. nuggetName = 0;
  589. }
  590. /*
  591. ===============
  592. idItemTeam::~idItemTeam
  593. ===============
  594. */
  595. idItemTeam::~idItemTeam() {
  596. FreeLightDef();
  597. }
  598. /*
  599. ===============
  600. idItemTeam::Spawn
  601. ===============
  602. */
  603. void idItemTeam::Spawn() {
  604. team = spawnArgs.GetInt( "team" );
  605. returnOrigin = GetPhysics()->GetOrigin() + idVec3( 0, 0, 20 );
  606. returnAxis = GetPhysics()->GetAxis();
  607. BecomeActive( TH_THINK );
  608. const char * skinName;
  609. skinName = spawnArgs.GetString( "skin", "" );
  610. if ( skinName[0] )
  611. skinDefault = declManager->FindSkin( skinName );
  612. skinName = spawnArgs.GetString( "skin_carried", "" );
  613. if ( skinName[0] )
  614. skinCarried = declManager->FindSkin( skinName );
  615. nuggetName = spawnArgs.GetString( "nugget_name", "" );
  616. if ( !nuggetName[0] ) {
  617. nuggetName = NULL;
  618. }
  619. scriptTaken = LoadScript( "script_taken" );
  620. scriptDropped = LoadScript( "script_dropped" );
  621. scriptReturned = LoadScript( "script_returned" );
  622. scriptCaptured = LoadScript( "script_captured" );
  623. /* Spawn attached dlight */
  624. /*
  625. idDict args;
  626. idVec3 lightOffset( 0.0f, 20.0f, 0.0f );
  627. // Set up the flag's dynamic light
  628. memset( &itemGlow, 0, sizeof( itemGlow ) );
  629. itemGlow.axis = mat3_identity;
  630. itemGlow.lightRadius.x = 128.0f;
  631. itemGlow.lightRadius.y = itemGlow.lightRadius.z = itemGlow.lightRadius.x;
  632. itemGlow.noShadows = true;
  633. itemGlow.pointLight = true;
  634. itemGlow.shaderParms[ SHADERPARM_RED ] = 0.0f;
  635. itemGlow.shaderParms[ SHADERPARM_GREEN ] = 0.0f;
  636. itemGlow.shaderParms[ SHADERPARM_BLUE ] = 0.0f;
  637. itemGlow.shaderParms[ SHADERPARM_ALPHA ] = 0.0f;
  638. // Select a shader based on the team
  639. if ( team == 0 )
  640. itemGlow.shader = declManager->FindMaterial( "lights/redflag" );
  641. else
  642. itemGlow.shader = declManager->FindMaterial( "lights/blueflag" );
  643. */
  644. idMoveableItem::Spawn();
  645. physicsObj.SetContents( 0 );
  646. physicsObj.SetClipMask( MASK_SOLID | CONTENTS_MOVEABLECLIP );
  647. physicsObj.SetGravity( idVec3( 0, 0, spawnArgs.GetInt("gravity", "-30" ) ) );
  648. }
  649. /*
  650. ===============
  651. idItemTeam::LoadScript
  652. ===============
  653. */
  654. function_t * idItemTeam::LoadScript( char * script ) {
  655. function_t * function = NULL;
  656. idStr funcname = spawnArgs.GetString( script, "" );
  657. if ( funcname.Length() ) {
  658. function = gameLocal.program.FindFunction( funcname );
  659. if ( function == NULL ) {
  660. #ifdef _DEBUG
  661. gameLocal.Warning( "idItemTeam '%s' at (%s) calls unknown function '%s'", name.c_str(), GetPhysics()->GetOrigin().ToString(0), funcname.c_str() );
  662. #endif
  663. }
  664. }
  665. return function;
  666. }
  667. /*
  668. ===============
  669. idItemTeam::Think
  670. ===============
  671. */
  672. void idItemTeam::Think() {
  673. idMoveableItem::Think();
  674. TouchTriggers();
  675. // TODO : only update on updatevisuals
  676. /*idVec3 offset( 0.0f, 0.0f, 20.0f );
  677. itemGlow.origin = GetPhysics()->GetOrigin() + offset;
  678. if ( itemGlowHandle == -1 ) {
  679. itemGlowHandle = gameRenderWorld->AddLightDef( &itemGlow );
  680. } else {
  681. gameRenderWorld->UpdateLightDef( itemGlowHandle, &itemGlow );
  682. }*/
  683. #if 1
  684. // should only the server do this?
  685. if ( common->IsServer() && nuggetName && carried && ( !lastNuggetDrop || (gameLocal.time - lastNuggetDrop) > spawnArgs.GetInt("nugget_frequency") ) ) {
  686. SpawnNugget( GetPhysics()->GetOrigin() );
  687. lastNuggetDrop = gameLocal.time;
  688. }
  689. #endif
  690. // return dropped flag after si_flagDropTimeLimit seconds
  691. if ( dropped && !carried && lastDrop != 0 && (gameLocal.time - lastDrop) > ( si_flagDropTimeLimit.GetInteger()*1000 ) ) {
  692. Return(); // return flag after 30 seconds on ground
  693. return;
  694. }
  695. }
  696. /*
  697. ===============
  698. idItemTeam::Pickup
  699. ===============
  700. */
  701. bool idItemTeam::Pickup( idPlayer *player ) {
  702. if ( !gameLocal.mpGame.IsGametypeFlagBased() ) /* CTF */
  703. return false;
  704. if ( gameLocal.mpGame.GetGameState() == idMultiplayerGame::WARMUP ||
  705. gameLocal.mpGame.GetGameState() == idMultiplayerGame::COUNTDOWN )
  706. return false;
  707. // wait 2 seconds after drop before beeing picked up again
  708. if ( lastDrop != 0 && (gameLocal.time - lastDrop) < spawnArgs.GetInt("pickupDelay", "500") )
  709. return false;
  710. if ( carried == false && player->team != this->team ) {
  711. PostEventMS( &EV_TakeFlag, 0, player );
  712. return true;
  713. } else if ( carried == false && dropped == true && player->team == this->team ) {
  714. gameLocal.mpGame.PlayerScoreCTF( player->entityNumber, 5 );
  715. // return flag
  716. PostEventMS( &EV_FlagReturn, 0, player );
  717. return false;
  718. }
  719. return false;
  720. }
  721. /*
  722. ===============
  723. idItemTeam::ClientReceiveEvent
  724. ===============
  725. */
  726. bool idItemTeam::ClientReceiveEvent( int event, int time, const idBitMsg &msg ) {
  727. gameLocal.DPrintf("ClientRecieveEvent: %i\n", event );
  728. switch ( event ) {
  729. case EVENT_TAKEFLAG: {
  730. idPlayer * player = static_cast<idPlayer *>(gameLocal.entities[ msg.ReadBits( GENTITYNUM_BITS ) ]);
  731. if ( player == NULL ) {
  732. gameLocal.Warning( "NULL player takes flag?\n" );
  733. return false;
  734. }
  735. Event_TakeFlag( player );
  736. }
  737. return true;
  738. case EVENT_DROPFLAG : {
  739. bool death = bool( msg.ReadBits( 1 ) == 1 );
  740. Event_DropFlag( death );
  741. }
  742. return true;
  743. case EVENT_FLAGRETURN : {
  744. Hide();
  745. FreeModelDef();
  746. FreeLightDef();
  747. Event_FlagReturn();
  748. }
  749. return true;
  750. case EVENT_FLAGCAPTURE : {
  751. Hide();
  752. FreeModelDef();
  753. FreeLightDef();
  754. Event_FlagCapture();
  755. }
  756. return true;
  757. };
  758. return false;
  759. }
  760. /*
  761. ================
  762. idItemTeam::Drop
  763. ================
  764. */
  765. void idItemTeam::Drop( bool death )
  766. {
  767. // PostEventMS( &EV_DropFlag, 0, int(death == true) );
  768. // had to remove the delayed drop because of drop flag on disconnect
  769. Event_DropFlag( death );
  770. }
  771. /*
  772. ================
  773. idItemTeam::Return
  774. ================
  775. */
  776. void idItemTeam::Return( idPlayer * player )
  777. {
  778. if ( team != 0 && team != 1 )
  779. return;
  780. // PostEventMS( &EV_FlagReturn, 0 );
  781. Event_FlagReturn();
  782. }
  783. /*
  784. ================
  785. idItemTeam::Capture
  786. ================
  787. */
  788. void idItemTeam::Capture()
  789. {
  790. if ( team != 0 && team != 1 )
  791. return;
  792. PostEventMS( &EV_FlagCapture, 0 );
  793. }
  794. /*
  795. ================
  796. idItemTeam::PrivateReturn
  797. ================
  798. */
  799. void idItemTeam::PrivateReturn()
  800. {
  801. Unbind();
  802. if ( common->IsServer() && carried && !dropped ) {
  803. int playerIdx = gameLocal.mpGame.GetFlagCarrier( 1-team );
  804. if ( playerIdx != -1 ) {
  805. idPlayer * player = static_cast<idPlayer*>( gameLocal.entities[ playerIdx ] );
  806. player->carryingFlag = false;
  807. } else {
  808. gameLocal.Warning( "BUG: carried flag has no carrier before return" );
  809. }
  810. }
  811. dropped = false;
  812. carried = false;
  813. SetOrigin( returnOrigin );
  814. SetAxis( returnAxis );
  815. trigger->Link( gameLocal.clip, this, 0, GetPhysics()->GetOrigin(), mat3_identity );
  816. SetSkin( skinDefault );
  817. // Turn off the light
  818. /*itemGlow.shaderParms[ SHADERPARM_RED ] = 0.0f;
  819. itemGlow.shaderParms[ SHADERPARM_GREEN ] = 0.0f;
  820. itemGlow.shaderParms[ SHADERPARM_BLUE ] = 0.0f;
  821. itemGlow.shaderParms[ SHADERPARM_ALPHA ] = 0.0f;
  822. if ( itemGlowHandle != -1 )
  823. gameRenderWorld->UpdateLightDef( itemGlowHandle, &itemGlow );*/
  824. GetPhysics()->SetLinearVelocity( idVec3(0, 0, 0) );
  825. GetPhysics()->SetAngularVelocity( idVec3(0, 0, 0) );
  826. }
  827. /*
  828. ================
  829. idItemTeam::Event_TakeFlag
  830. ================
  831. */
  832. void idItemTeam::Event_TakeFlag( idPlayer * player ) {
  833. gameLocal.DPrintf("Event_TakeFlag()!\n");
  834. assert( player != NULL );
  835. if ( player->carryingFlag ) {
  836. // Don't do anything if the player is already carrying the flag.
  837. // Prevents duplicate messages.
  838. return;
  839. }
  840. if ( common->IsServer() ) {
  841. idBitMsg msg;
  842. byte msgBuf[MAX_EVENT_PARAM_SIZE];
  843. // Send the event
  844. msg.InitWrite( msgBuf, sizeof( msgBuf ) );
  845. msg.BeginWriting();
  846. msg.WriteBits( player->entityNumber, GENTITYNUM_BITS );
  847. ServerSendEvent( EVENT_TAKEFLAG, &msg, false );
  848. gameLocal.mpGame.PlayTeamSound( player->team, SND_FLAG_TAKEN_THEIRS );
  849. gameLocal.mpGame.PlayTeamSound( team, SND_FLAG_TAKEN_YOURS );
  850. gameLocal.mpGame.PrintMessageEvent( idMultiplayerGame::MSG_FLAGTAKEN, team, player->entityNumber );
  851. // dont drop a nugget RIGHT away
  852. lastNuggetDrop = gameLocal.time - gameLocal.random.RandomInt( 1000 );
  853. }
  854. BindToJoint( player, g_flagAttachJoint.GetString(), true );
  855. idVec3 origin( g_flagAttachOffsetX.GetFloat(), g_flagAttachOffsetY.GetFloat(), g_flagAttachOffsetZ.GetFloat() );
  856. idAngles angle( g_flagAttachAngleX.GetFloat(), g_flagAttachAngleY.GetFloat(), g_flagAttachAngleZ.GetFloat() );
  857. SetAngles( angle );
  858. SetOrigin( origin );
  859. // Turn the light on
  860. /*itemGlow.shaderParms[ SHADERPARM_RED ] = 1.0f;
  861. itemGlow.shaderParms[ SHADERPARM_GREEN ] = 1.0f;
  862. itemGlow.shaderParms[ SHADERPARM_BLUE ] = 1.0f;
  863. itemGlow.shaderParms[ SHADERPARM_ALPHA ] = 1.0f;
  864. if ( itemGlowHandle != -1 )
  865. gameRenderWorld->UpdateLightDef( itemGlowHandle, &itemGlow );*/
  866. if ( scriptTaken ) {
  867. idThread *thread = new idThread();
  868. thread->CallFunction( scriptTaken, false );
  869. thread->DelayedStart( 0 );
  870. }
  871. dropped = false;
  872. carried = true;
  873. player->carryingFlag = true;
  874. SetSkin( skinCarried );
  875. UpdateVisuals();
  876. UpdateGuis();
  877. if ( common->IsServer() ) {
  878. if ( team == 0 )
  879. gameLocal.mpGame.player_red_flag = player->entityNumber;
  880. else
  881. gameLocal.mpGame.player_blue_flag = player->entityNumber;
  882. }
  883. }
  884. /*
  885. ================
  886. idItemTeam::Event_DropFlag
  887. ================
  888. */
  889. void idItemTeam::Event_DropFlag( bool death ) {
  890. gameLocal.DPrintf("Event_DropFlag()!\n");
  891. if ( common->IsServer() ) {
  892. idBitMsg msg;
  893. byte msgBuf[MAX_EVENT_PARAM_SIZE];
  894. // Send the event
  895. msg.InitWrite( msgBuf, sizeof( msgBuf ) );
  896. msg.BeginWriting();
  897. msg.WriteBits( death, 1 );
  898. ServerSendEvent( EVENT_DROPFLAG, &msg, false );
  899. if ( gameLocal.mpGame.IsFlagMsgOn() ) {
  900. gameLocal.mpGame.PlayTeamSound( 1-team, SND_FLAG_DROPPED_THEIRS );
  901. gameLocal.mpGame.PlayTeamSound( team, SND_FLAG_DROPPED_YOURS );
  902. gameLocal.mpGame.PrintMessageEvent( idMultiplayerGame::MSG_FLAGDROP, team );
  903. }
  904. }
  905. lastDrop = gameLocal.time;
  906. BecomeActive( TH_THINK );
  907. Show();
  908. if ( death )
  909. GetPhysics()->SetLinearVelocity( idVec3(0, 0, 0) );
  910. else
  911. GetPhysics()->SetLinearVelocity( idVec3(0, 0, 20) );
  912. GetPhysics()->SetAngularVelocity( idVec3(0, 0, 0) );
  913. // GetPhysics()->SetLinearVelocity( ( GetPhysics()->GetLinearVelocity() * GetBindMaster()->GetPhysics()->GetAxis() ) + GetBindMaster()->GetPhysics()->GetLinearVelocity() );
  914. if ( GetBindMaster() ) {
  915. const idBounds bounds = GetPhysics()->GetBounds();
  916. idVec3 origin = GetBindMaster()->GetPhysics()->GetOrigin() + idVec3(0, 0, ( bounds[1].z-bounds[0].z )*0.6f );
  917. Unbind();
  918. SetOrigin( origin );
  919. }
  920. idAngles angle = GetPhysics()->GetAxis().ToAngles();
  921. angle.roll = 0;
  922. angle.pitch = 0;
  923. SetAxis( angle.ToMat3() );
  924. dropped = true;
  925. carried = false;
  926. if ( scriptDropped ) {
  927. idThread *thread = new idThread();
  928. thread->CallFunction( scriptDropped, false );
  929. thread->DelayedStart( 0 );
  930. }
  931. SetSkin( skinDefault );
  932. UpdateVisuals();
  933. UpdateGuis();
  934. if ( common->IsServer() ) {
  935. if ( team == 0 )
  936. gameLocal.mpGame.player_red_flag = -1;
  937. else
  938. gameLocal.mpGame.player_blue_flag = -1;
  939. }
  940. }
  941. /*
  942. ================
  943. idItemTeam::Event_FlagReturn
  944. ================
  945. */
  946. void idItemTeam::Event_FlagReturn( idPlayer * player ) {
  947. gameLocal.DPrintf("Event_FlagReturn()!\n");
  948. if ( common->IsServer() ) {
  949. ServerSendEvent( EVENT_FLAGRETURN, NULL, false );
  950. if ( gameLocal.mpGame.IsFlagMsgOn() ) {
  951. gameLocal.mpGame.PlayTeamSound( 1-team, SND_FLAG_RETURN );
  952. gameLocal.mpGame.PlayTeamSound( team, SND_FLAG_RETURN );
  953. int entitynum = 255;
  954. if ( player ) {
  955. entitynum = player->entityNumber;
  956. }
  957. gameLocal.mpGame.PrintMessageEvent( idMultiplayerGame::MSG_FLAGRETURN, team, entitynum );
  958. }
  959. }
  960. BecomeActive( TH_THINK );
  961. Show();
  962. PrivateReturn();
  963. if ( scriptReturned ) {
  964. idThread *thread = new idThread();
  965. thread->CallFunction( scriptReturned, false );
  966. thread->DelayedStart( 0 );
  967. }
  968. UpdateVisuals();
  969. UpdateGuis();
  970. // Present();
  971. if ( common->IsServer() ) {
  972. if ( team == 0 )
  973. gameLocal.mpGame.player_red_flag = -1;
  974. else
  975. gameLocal.mpGame.player_blue_flag = -1;
  976. }
  977. }
  978. /*
  979. ================
  980. idItemTeam::Event_FlagCapture
  981. ================
  982. */
  983. void idItemTeam::Event_FlagCapture() {
  984. gameLocal.DPrintf("Event_FlagCapture()!\n");
  985. if ( common->IsServer() ) {
  986. int playerIdx = gameLocal.mpGame.GetFlagCarrier( 1-team );
  987. if ( playerIdx != -1 ) {
  988. ServerSendEvent( EVENT_FLAGCAPTURE, NULL, false );
  989. gameLocal.mpGame.PlayTeamSound( 1-team, SND_FLAG_CAPTURED_THEIRS );
  990. gameLocal.mpGame.PlayTeamSound( team, SND_FLAG_CAPTURED_YOURS );
  991. gameLocal.mpGame.TeamScoreCTF( 1-team, 1 );
  992. gameLocal.mpGame.PlayerScoreCTF( playerIdx, 10 );
  993. gameLocal.mpGame.PrintMessageEvent( idMultiplayerGame::MSG_FLAGCAPTURE, team, playerIdx );
  994. } else {
  995. playerIdx = 255;
  996. }
  997. }
  998. BecomeActive( TH_THINK );
  999. Show();
  1000. PrivateReturn();
  1001. if ( scriptCaptured ) {
  1002. idThread *thread = new idThread();
  1003. thread->CallFunction( scriptCaptured, false );
  1004. thread->DelayedStart( 0 );
  1005. }
  1006. UpdateVisuals();
  1007. UpdateGuis();
  1008. if ( common->IsServer() ) {
  1009. if ( team == 0 )
  1010. gameLocal.mpGame.player_red_flag = -1;
  1011. else
  1012. gameLocal.mpGame.player_blue_flag = -1;
  1013. }
  1014. }
  1015. /*
  1016. ================
  1017. idItemTeam::FreeLightDef
  1018. ================
  1019. */
  1020. void idItemTeam::FreeLightDef() {
  1021. if ( itemGlowHandle != -1 ) {
  1022. gameRenderWorld->FreeLightDef( itemGlowHandle );
  1023. itemGlowHandle = -1;
  1024. }
  1025. }
  1026. /*
  1027. ================
  1028. idItemTeam::SpawnNugget
  1029. ================
  1030. */
  1031. void idItemTeam::SpawnNugget( idVec3 pos ) {
  1032. idAngles angle( gameLocal.random.RandomInt(spawnArgs.GetInt("nugget_pitch", "30")), gameLocal.random.RandomInt(spawnArgs.GetInt("nugget_yaw", "360" )), 0 );
  1033. float velocity = float(gameLocal.random.RandomInt( 40 )+15);
  1034. velocity *= spawnArgs.GetFloat("nugget_velocity", "1" );
  1035. idEntity * ent = idMoveableItem::DropItem( nuggetName, pos, GetPhysics()->GetAxis(), angle.ToMat3()*idVec3(velocity, velocity, velocity), 0, spawnArgs.GetInt("nugget_removedelay") );
  1036. idPhysics_RigidBody * physics = static_cast<idPhysics_RigidBody *>( ent->GetPhysics() );
  1037. if ( physics != NULL && physics->IsType( idPhysics_RigidBody::Type ) ) {
  1038. physics->DisableImpact();
  1039. }
  1040. }
  1041. /*
  1042. ================
  1043. idItemTeam::Event_FlagCapture
  1044. ================
  1045. */
  1046. void idItemTeam::WriteToSnapshot( idBitMsg &msg ) const {
  1047. msg.WriteBits( carried, 1 );
  1048. msg.WriteBits( dropped, 1 );
  1049. WriteBindToSnapshot( msg );
  1050. idMoveableItem::WriteToSnapshot( msg );
  1051. }
  1052. /*
  1053. ================
  1054. idItemTeam::ReadFromSnapshot
  1055. ================
  1056. */
  1057. void idItemTeam::ReadFromSnapshot( const idBitMsg &msg ) {
  1058. carried = msg.ReadBits( 1 ) == 1;
  1059. dropped = msg.ReadBits( 1 ) == 1;
  1060. ReadBindFromSnapshot( msg );
  1061. if ( msg.HasChanged() )
  1062. {
  1063. UpdateGuis();
  1064. if ( carried == true )
  1065. SetSkin( skinCarried );
  1066. else
  1067. SetSkin( skinDefault );
  1068. }
  1069. idMoveableItem::ReadFromSnapshot( msg );
  1070. }
  1071. /*
  1072. ================
  1073. idItemTeam::UpdateGuis
  1074. Update all client's huds wrt the flag status.
  1075. ================
  1076. */
  1077. void idItemTeam::UpdateGuis() {
  1078. idPlayer *player;
  1079. for ( int i = 0; i < gameLocal.numClients; i++ ) {
  1080. player = static_cast<idPlayer *>( gameLocal.entities[ i ] );
  1081. if ( player && player->hud ) {
  1082. player->hud->SetFlagState( 0, gameLocal.mpGame.GetFlagStatus( 0 ) );
  1083. player->hud->SetFlagState( 1, gameLocal.mpGame.GetFlagStatus( 1 ) );
  1084. player->hud->SetTeamScore( 0, gameLocal.mpGame.GetFlagPoints( 0 ) );
  1085. player->hud->SetTeamScore( 1, gameLocal.mpGame.GetFlagPoints( 1 ) );
  1086. }
  1087. }
  1088. }
  1089. /*
  1090. ================
  1091. idItemTeam::Present
  1092. ================
  1093. */
  1094. void idItemTeam::Present() {
  1095. // hide the flag for localplayer if in first person
  1096. if ( carried && GetBindMaster() ) {
  1097. idPlayer * player = static_cast<idPlayer *>( GetBindMaster() );
  1098. if ( player == gameLocal.GetLocalPlayer() && !pm_thirdPerson.GetBool() ) {
  1099. FreeModelDef();
  1100. BecomeActive( TH_UPDATEVISUALS );
  1101. return;
  1102. }
  1103. }
  1104. idEntity::Present();
  1105. }
  1106. /*
  1107. ===============================================================================
  1108. idObjective
  1109. ===============================================================================
  1110. */
  1111. CLASS_DECLARATION( idItem, idObjective )
  1112. EVENT( EV_Activate, idObjective::Event_Trigger )
  1113. EVENT( EV_HideObjective, idObjective::Event_HideObjective )
  1114. EVENT( EV_GetPlayerPos, idObjective::Event_GetPlayerPos )
  1115. END_CLASS
  1116. /*
  1117. ================
  1118. idObjective::idObjective
  1119. ================
  1120. */
  1121. idObjective::idObjective() {
  1122. playerPos.Zero();
  1123. }
  1124. /*
  1125. ================
  1126. idObjective::Save
  1127. ================
  1128. */
  1129. void idObjective::Save( idSaveGame *savefile ) const {
  1130. savefile->WriteVec3( playerPos );
  1131. savefile->WriteMaterial( screenshot );
  1132. }
  1133. /*
  1134. ================
  1135. idObjective::Restore
  1136. ================
  1137. */
  1138. void idObjective::Restore( idRestoreGame *savefile ) {
  1139. savefile->ReadVec3( playerPos );
  1140. savefile->ReadMaterial( screenshot );
  1141. }
  1142. /*
  1143. ================
  1144. idObjective::Spawn
  1145. ================
  1146. */
  1147. void idObjective::Spawn() {
  1148. Hide();
  1149. idStr shotName;
  1150. shotName = gameLocal.GetMapName();
  1151. shotName.StripFileExtension();
  1152. shotName += "/";
  1153. shotName += spawnArgs.GetString( "screenshot" );
  1154. shotName.SetFileExtension( ".tga" );
  1155. screenshot = declManager->FindMaterial( shotName );
  1156. }
  1157. /*
  1158. ================
  1159. idObjective::Event_Trigger
  1160. ================
  1161. */
  1162. void idObjective::Event_Trigger( idEntity *activator ) {
  1163. idPlayer *player = gameLocal.GetLocalPlayer();
  1164. if ( player ) {
  1165. //Pickup( player );
  1166. if ( spawnArgs.GetString( "inv_objective", NULL ) ) {
  1167. if ( player ) {
  1168. player->GiveObjective( spawnArgs.GetString( "objectivetitle" ), spawnArgs.GetString( "objectivetext" ), screenshot );
  1169. // a tad slow but keeps from having to update all objectives in all maps with a name ptr
  1170. for( int i = 0; i < gameLocal.num_entities; i++ ) {
  1171. if ( gameLocal.entities[ i ] && gameLocal.entities[ i ]->IsType( idObjectiveComplete::Type ) ) {
  1172. if ( idStr::Icmp( spawnArgs.GetString( "objectivetitle" ), gameLocal.entities[ i ]->spawnArgs.GetString( "objectivetitle" ) ) == 0 ){
  1173. gameLocal.entities[ i ]->spawnArgs.SetBool( "objEnabled", true );
  1174. break;
  1175. }
  1176. }
  1177. }
  1178. PostEventMS( &EV_GetPlayerPos, 2000 );
  1179. }
  1180. }
  1181. }
  1182. }
  1183. /*
  1184. ================
  1185. idObjective::Event_GetPlayerPos
  1186. ================
  1187. */
  1188. void idObjective::Event_GetPlayerPos() {
  1189. idPlayer *player = gameLocal.GetLocalPlayer();
  1190. if ( player ) {
  1191. playerPos = player->GetPhysics()->GetOrigin();
  1192. PostEventMS( &EV_HideObjective, 100, player );
  1193. }
  1194. }
  1195. /*
  1196. ================
  1197. idObjective::Event_HideObjective
  1198. ================
  1199. */
  1200. void idObjective::Event_HideObjective(idEntity *e) {
  1201. idPlayer *player = gameLocal.GetLocalPlayer();
  1202. if ( player ) {
  1203. idVec3 v = player->GetPhysics()->GetOrigin() - playerPos;
  1204. if ( v.Length() > 64.0f ) {
  1205. player->HideObjective();
  1206. PostEventMS( &EV_Remove, 0 );
  1207. } else {
  1208. PostEventMS( &EV_HideObjective, 100, player );
  1209. }
  1210. }
  1211. }
  1212. /*
  1213. ===============================================================================
  1214. idVideoCDItem
  1215. ===============================================================================
  1216. */
  1217. CLASS_DECLARATION( idItem, idVideoCDItem )
  1218. END_CLASS
  1219. /*
  1220. ================
  1221. idVideoCDItem::GiveToPlayer
  1222. ================
  1223. */
  1224. bool idVideoCDItem::GiveToPlayer( idPlayer * player, unsigned int giveFlags ) {
  1225. if ( player == NULL ) {
  1226. return false;
  1227. }
  1228. const idDeclVideo * video = static_cast<const idDeclVideo * >( declManager->FindType( DECL_VIDEO, spawnArgs.GetString( "video" ), false ) );
  1229. if ( video == NULL ) {
  1230. return false;
  1231. }
  1232. if ( giveFlags & ITEM_GIVE_UPDATE_STATE ) {
  1233. player->GiveVideo( video, spawnArgs.GetString( "inv_name" ) );
  1234. }
  1235. return true;
  1236. }
  1237. /*
  1238. ===============================================================================
  1239. idPDAItem
  1240. ===============================================================================
  1241. */
  1242. CLASS_DECLARATION( idItem, idPDAItem )
  1243. END_CLASS
  1244. /*
  1245. ================
  1246. idPDAItem::GiveToPlayer
  1247. ================
  1248. */
  1249. bool idPDAItem::GiveToPlayer( idPlayer *player, unsigned int giveFlags ) {
  1250. if ( player == NULL ) {
  1251. return false;
  1252. }
  1253. const char * pdaName = spawnArgs.GetString( "pda_name" );
  1254. const char * invName = spawnArgs.GetString( "inv_name" );
  1255. const idDeclPDA * pda = NULL;
  1256. if ( pdaName != NULL && pdaName[0] != 0 ) {
  1257. // An empty PDA name is legitimate, it means the personal PDA
  1258. // But if the PDA name is not empty, it should be valid
  1259. pda = static_cast<const idDeclPDA *>( declManager->FindType( DECL_PDA, pdaName, false ) );
  1260. if ( pda == NULL ) {
  1261. idLib::Warning( "PDA Item '%s' references unknown PDA %s", GetName(), pdaName );
  1262. return false;
  1263. }
  1264. }
  1265. if ( giveFlags & ITEM_GIVE_UPDATE_STATE ) {
  1266. player->GivePDA( pda, invName );
  1267. }
  1268. return true;
  1269. }
  1270. /*
  1271. ===============================================================================
  1272. idMoveableItem
  1273. ===============================================================================
  1274. */
  1275. CLASS_DECLARATION( idItem, idMoveableItem )
  1276. EVENT( EV_DropToFloor, idMoveableItem::Event_DropToFloor )
  1277. EVENT( EV_Gib, idMoveableItem::Event_Gib )
  1278. END_CLASS
  1279. /*
  1280. ================
  1281. idMoveableItem::idMoveableItem
  1282. ================
  1283. */
  1284. idMoveableItem::idMoveableItem() {
  1285. trigger = NULL;
  1286. smoke = NULL;
  1287. smokeTime = 0;
  1288. nextSoundTime = 0;
  1289. repeatSmoke = false;
  1290. }
  1291. /*
  1292. ================
  1293. idMoveableItem::~idMoveableItem
  1294. ================
  1295. */
  1296. idMoveableItem::~idMoveableItem() {
  1297. if ( trigger ) {
  1298. delete trigger;
  1299. }
  1300. }
  1301. /*
  1302. ================
  1303. idMoveableItem::Save
  1304. ================
  1305. */
  1306. void idMoveableItem::Save( idSaveGame *savefile ) const {
  1307. savefile->WriteStaticObject( physicsObj );
  1308. savefile->WriteClipModel( trigger );
  1309. savefile->WriteParticle( smoke );
  1310. savefile->WriteInt( smokeTime );
  1311. savefile->WriteInt( nextSoundTime );
  1312. }
  1313. /*
  1314. ================
  1315. idMoveableItem::Restore
  1316. ================
  1317. */
  1318. void idMoveableItem::Restore( idRestoreGame *savefile ) {
  1319. savefile->ReadStaticObject( physicsObj );
  1320. RestorePhysics( &physicsObj );
  1321. savefile->ReadClipModel( trigger );
  1322. savefile->ReadParticle( smoke );
  1323. savefile->ReadInt( smokeTime );
  1324. savefile->ReadInt( nextSoundTime );
  1325. }
  1326. /*
  1327. ================
  1328. idMoveableItem::Spawn
  1329. ================
  1330. */
  1331. void idMoveableItem::Spawn() {
  1332. idTraceModel trm;
  1333. float density, friction, bouncyness, tsize;
  1334. idStr clipModelName;
  1335. idBounds bounds;
  1336. SetTimeState ts( timeGroup );
  1337. // create a trigger for item pickup
  1338. spawnArgs.GetFloat( "triggersize", "16.0", tsize );
  1339. trigger = new (TAG_PHYSICS_CLIP_ENTITY) idClipModel( idTraceModel( idBounds( vec3_origin ).Expand( tsize ) ) );
  1340. trigger->Link( gameLocal.clip, this, 0, GetPhysics()->GetOrigin(), GetPhysics()->GetAxis() );
  1341. trigger->SetContents( CONTENTS_TRIGGER );
  1342. // check if a clip model is set
  1343. spawnArgs.GetString( "clipmodel", "", clipModelName );
  1344. if ( !clipModelName[0] ) {
  1345. clipModelName = spawnArgs.GetString( "model" ); // use the visual model
  1346. }
  1347. // load the trace model
  1348. if ( !collisionModelManager->TrmFromModel( clipModelName, trm ) ) {
  1349. gameLocal.Error( "idMoveableItem '%s': cannot load collision model %s", name.c_str(), clipModelName.c_str() );
  1350. return;
  1351. }
  1352. // if the model should be shrinked
  1353. if ( spawnArgs.GetBool( "clipshrink" ) ) {
  1354. trm.Shrink( CM_CLIP_EPSILON );
  1355. }
  1356. // get rigid body properties
  1357. spawnArgs.GetFloat( "density", "0.5", density );
  1358. density = idMath::ClampFloat( 0.001f, 1000.0f, density );
  1359. spawnArgs.GetFloat( "friction", "0.05", friction );
  1360. friction = idMath::ClampFloat( 0.0f, 1.0f, friction );
  1361. spawnArgs.GetFloat( "bouncyness", "0.6", bouncyness );
  1362. bouncyness = idMath::ClampFloat( 0.0f, 1.0f, bouncyness );
  1363. // setup the physics
  1364. physicsObj.SetSelf( this );
  1365. physicsObj.SetClipModel( new (TAG_PHYSICS_CLIP_ENTITY) idClipModel( trm ), density );
  1366. physicsObj.SetOrigin( GetPhysics()->GetOrigin() );
  1367. physicsObj.SetAxis( GetPhysics()->GetAxis() );
  1368. physicsObj.SetBouncyness( bouncyness );
  1369. physicsObj.SetFriction( 0.6f, 0.6f, friction );
  1370. physicsObj.SetGravity( gameLocal.GetGravity() );
  1371. physicsObj.SetContents( CONTENTS_RENDERMODEL );
  1372. physicsObj.SetClipMask( MASK_SOLID | CONTENTS_MOVEABLECLIP );
  1373. SetPhysics( &physicsObj );
  1374. if ( spawnArgs.GetBool( "nodrop" ) ) {
  1375. physicsObj.PutToRest();
  1376. }
  1377. smoke = NULL;
  1378. smokeTime = 0;
  1379. nextSoundTime = 0;
  1380. const char *smokeName = spawnArgs.GetString( "smoke_trail" );
  1381. if ( *smokeName != '\0' ) {
  1382. smoke = static_cast<const idDeclParticle *>( declManager->FindType( DECL_PARTICLE, smokeName ) );
  1383. smokeTime = gameLocal.time;
  1384. BecomeActive( TH_UPDATEPARTICLES );
  1385. }
  1386. repeatSmoke = spawnArgs.GetBool( "repeatSmoke", "0" );
  1387. }
  1388. /*
  1389. ================
  1390. idItem::ClientThink
  1391. ================
  1392. */
  1393. void idMoveableItem::ClientThink( const int curTime, const float fraction, const bool predict ) {
  1394. InterpolatePhysicsOnly( fraction );
  1395. if ( thinkFlags & TH_PHYSICS ) {
  1396. // update trigger position
  1397. trigger->Link( gameLocal.clip, this, 0, GetPhysics()->GetOrigin(), mat3_identity );
  1398. }
  1399. Present();
  1400. }
  1401. /*
  1402. ================
  1403. idMoveableItem::Think
  1404. ================
  1405. */
  1406. void idMoveableItem::Think() {
  1407. RunPhysics();
  1408. if ( thinkFlags & TH_PHYSICS ) {
  1409. // update trigger position
  1410. trigger->Link( gameLocal.clip, this, 0, GetPhysics()->GetOrigin(), mat3_identity );
  1411. }
  1412. if ( thinkFlags & TH_UPDATEPARTICLES ) {
  1413. if ( !gameLocal.smokeParticles->EmitSmoke( smoke, smokeTime, gameLocal.random.CRandomFloat(), GetPhysics()->GetOrigin(), GetPhysics()->GetAxis(), timeGroup /*_D3XP*/ ) ) {
  1414. if ( !repeatSmoke ) {
  1415. smokeTime = 0;
  1416. BecomeInactive( TH_UPDATEPARTICLES );
  1417. } else {
  1418. smokeTime = gameLocal.time;
  1419. }
  1420. }
  1421. }
  1422. Present();
  1423. }
  1424. /*
  1425. =================
  1426. idMoveableItem::Collide
  1427. =================
  1428. */
  1429. bool idMoveableItem::Collide( const trace_t &collision, const idVec3 &velocity ) {
  1430. float v, f;
  1431. v = -( velocity * collision.c.normal );
  1432. if ( v > 80 && gameLocal.time > nextSoundTime ) {
  1433. f = v > 200 ? 1.0f : idMath::Sqrt( v - 80 ) * 0.091f;
  1434. if ( StartSound( "snd_bounce", SND_CHANNEL_ANY, 0, false, NULL ) ) {
  1435. // don't set the volume unless there is a bounce sound as it overrides the entire channel
  1436. // which causes footsteps on ai's to not honor their shader parms
  1437. SetSoundVolume( f );
  1438. }
  1439. nextSoundTime = gameLocal.time + 500;
  1440. }
  1441. return false;
  1442. }
  1443. /*
  1444. ================
  1445. idMoveableItem::Pickup
  1446. ================
  1447. */
  1448. bool idMoveableItem::Pickup( idPlayer *player ) {
  1449. bool ret = idItem::Pickup( player );
  1450. if ( ret ) {
  1451. trigger->SetContents( 0 );
  1452. }
  1453. return ret;
  1454. }
  1455. /*
  1456. ================
  1457. idMoveableItem::DropItem
  1458. ================
  1459. */
  1460. idEntity *idMoveableItem::DropItem( const char *classname, const idVec3 &origin, const idMat3 &axis, const idVec3 &velocity, int activateDelay, int removeDelay ) {
  1461. idDict args;
  1462. idEntity *item;
  1463. args.Set( "classname", classname );
  1464. args.Set( "dropped", "1" );
  1465. // we sometimes drop idMoveables here, so set 'nodrop' to 1 so that it doesn't get put on the floor
  1466. args.Set( "nodrop", "1" );
  1467. if ( activateDelay ) {
  1468. args.SetBool( "triggerFirst", true );
  1469. }
  1470. gameLocal.SpawnEntityDef( args, &item );
  1471. if ( item ) {
  1472. // set item position
  1473. item->GetPhysics()->SetOrigin( origin );
  1474. item->GetPhysics()->SetAxis( axis );
  1475. item->GetPhysics()->SetLinearVelocity( velocity );
  1476. item->UpdateVisuals();
  1477. if ( activateDelay ) {
  1478. item->PostEventMS( &EV_Activate, activateDelay, item );
  1479. }
  1480. if ( !removeDelay ) {
  1481. removeDelay = 5 * 60 * 1000;
  1482. }
  1483. // always remove a dropped item after 5 minutes in case it dropped to an unreachable location
  1484. item->PostEventMS( &EV_Remove, removeDelay );
  1485. }
  1486. return item;
  1487. }
  1488. /*
  1489. ================
  1490. idMoveableItem::DropItems
  1491. The entity should have the following key/value pairs set:
  1492. "def_drop<type>Item" "item def"
  1493. "drop<type>ItemJoint" "joint name"
  1494. "drop<type>ItemRotation" "pitch yaw roll"
  1495. "drop<type>ItemOffset" "x y z"
  1496. "skin_drop<type>" "skin name"
  1497. To drop multiple items the following key/value pairs can be used:
  1498. "def_drop<type>Item<X>" "item def"
  1499. "drop<type>Item<X>Joint" "joint name"
  1500. "drop<type>Item<X>Rotation" "pitch yaw roll"
  1501. "drop<type>Item<X>Offset" "x y z"
  1502. where <X> is an aribtrary string.
  1503. ================
  1504. */
  1505. void idMoveableItem::DropItems( idAnimatedEntity *ent, const char *type, idList<idEntity *> *list ) {
  1506. const idKeyValue *kv;
  1507. const char *skinName, *c, *jointName;
  1508. idStr key, key2;
  1509. idVec3 origin;
  1510. idMat3 axis;
  1511. idAngles angles;
  1512. const idDeclSkin *skin;
  1513. jointHandle_t joint;
  1514. idEntity *item;
  1515. // drop all items
  1516. kv = ent->spawnArgs.MatchPrefix( va( "def_drop%sItem", type ), NULL );
  1517. while ( kv ) {
  1518. c = kv->GetKey().c_str() + kv->GetKey().Length();
  1519. if ( idStr::Icmp( c - 5, "Joint" ) != 0 && idStr::Icmp( c - 8, "Rotation" ) != 0 ) {
  1520. key = kv->GetKey().c_str() + 4;
  1521. key2 = key;
  1522. key += "Joint";
  1523. key2 += "Offset";
  1524. jointName = ent->spawnArgs.GetString( key );
  1525. joint = ent->GetAnimator()->GetJointHandle( jointName );
  1526. if ( !ent->GetJointWorldTransform( joint, gameLocal.time, origin, axis ) ) {
  1527. gameLocal.Warning( "%s refers to invalid joint '%s' on entity '%s'\n", key.c_str(), jointName, ent->name.c_str() );
  1528. origin = ent->GetPhysics()->GetOrigin();
  1529. axis = ent->GetPhysics()->GetAxis();
  1530. }
  1531. if ( g_dropItemRotation.GetString()[0] ) {
  1532. angles.Zero();
  1533. sscanf( g_dropItemRotation.GetString(), "%f %f %f", &angles.pitch, &angles.yaw, &angles.roll );
  1534. } else {
  1535. key = kv->GetKey().c_str() + 4;
  1536. key += "Rotation";
  1537. ent->spawnArgs.GetAngles( key, "0 0 0", angles );
  1538. }
  1539. axis = angles.ToMat3() * axis;
  1540. origin += ent->spawnArgs.GetVector( key2, "0 0 0" );
  1541. item = DropItem( kv->GetValue(), origin, axis, vec3_origin, 0, 0 );
  1542. if ( list && item ) {
  1543. list->Append( item );
  1544. }
  1545. }
  1546. kv = ent->spawnArgs.MatchPrefix( va( "def_drop%sItem", type ), kv );
  1547. }
  1548. // change the skin to hide all items
  1549. skinName = ent->spawnArgs.GetString( va( "skin_drop%s", type ) );
  1550. if ( skinName[0] ) {
  1551. skin = declManager->FindSkin( skinName );
  1552. ent->SetSkin( skin );
  1553. }
  1554. }
  1555. /*
  1556. ======================
  1557. idMoveableItem::WriteToSnapshot
  1558. ======================
  1559. */
  1560. void idMoveableItem::WriteToSnapshot( idBitMsg &msg ) const {
  1561. physicsObj.WriteToSnapshot( msg );
  1562. msg.WriteBool( IsHidden() );
  1563. }
  1564. /*
  1565. ======================
  1566. idMoveableItem::ReadFromSnapshot
  1567. ======================
  1568. */
  1569. void idMoveableItem::ReadFromSnapshot( const idBitMsg &msg ) {
  1570. physicsObj.ReadFromSnapshot( msg );
  1571. const bool snapshotHidden = msg.ReadBool();
  1572. if ( snapshotHidden ) {
  1573. Hide();
  1574. } else if ( GetPredictPickupMilliseconds() != 0 ) {
  1575. if ( gameLocal.GetLastClientUsercmdMilliseconds( gameLocal.GetLocalClientNum() ) >= GetPredictPickupMilliseconds() ) {
  1576. if ( trigger->GetContents() == 0 ) {
  1577. trigger->SetContents( CONTENTS_TRIGGER );
  1578. }
  1579. Show();
  1580. }
  1581. }
  1582. if ( msg.HasChanged() ) {
  1583. UpdateVisuals();
  1584. }
  1585. }
  1586. /*
  1587. ============
  1588. idMoveableItem::Gib
  1589. ============
  1590. */
  1591. void idMoveableItem::Gib( const idVec3 &dir, const char *damageDefName ) {
  1592. // spawn smoke puff
  1593. const char *smokeName = spawnArgs.GetString( "smoke_gib" );
  1594. if ( *smokeName != '\0' ) {
  1595. const idDeclParticle *smoke = static_cast<const idDeclParticle *>( declManager->FindType( DECL_PARTICLE, smokeName ) );
  1596. gameLocal.smokeParticles->EmitSmoke( smoke, gameLocal.time, gameLocal.random.CRandomFloat(), renderEntity.origin, renderEntity.axis, timeGroup /*_D3XP*/ );
  1597. }
  1598. // remove the entity
  1599. PostEventMS( &EV_Remove, 0 );
  1600. }
  1601. /*
  1602. ================
  1603. idMoveableItem::Event_DropToFloor
  1604. ================
  1605. */
  1606. void idMoveableItem::Event_DropToFloor() {
  1607. // the physics will drop the moveable to the floor
  1608. }
  1609. /*
  1610. ============
  1611. idMoveableItem::Event_Gib
  1612. ============
  1613. */
  1614. void idMoveableItem::Event_Gib( const char *damageDefName ) {
  1615. Gib( idVec3( 0, 0, 1 ), damageDefName );
  1616. }
  1617. /*
  1618. ===============================================================================
  1619. idMoveablePDAItem
  1620. ===============================================================================
  1621. */
  1622. CLASS_DECLARATION( idMoveableItem, idMoveablePDAItem )
  1623. END_CLASS
  1624. /*
  1625. ================
  1626. idMoveablePDAItem::GiveToPlayer
  1627. ================
  1628. */
  1629. bool idMoveablePDAItem::GiveToPlayer( idPlayer * player, unsigned int giveFlags ) {
  1630. if ( player == NULL ) {
  1631. return false;
  1632. }
  1633. const char * pdaName = spawnArgs.GetString( "pda_name" );
  1634. const char * invName = spawnArgs.GetString( "inv_name" );
  1635. const idDeclPDA * pda = NULL;
  1636. if ( pdaName != NULL && pdaName[0] != 0 ) {
  1637. // An empty PDA name is legitimate, it means the personal PDA
  1638. // But if the PDA name is not empty, it should be valid
  1639. pda = static_cast<const idDeclPDA *>( declManager->FindType( DECL_PDA, pdaName, false ) );
  1640. if ( pda == NULL ) {
  1641. idLib::Warning( "PDA Item '%s' references unknown PDA %s", GetName(), pdaName );
  1642. return false;
  1643. }
  1644. }
  1645. if ( giveFlags & ITEM_GIVE_UPDATE_STATE ) {
  1646. player->GivePDA( pda, invName );
  1647. }
  1648. return true;
  1649. }
  1650. /*
  1651. ===============================================================================
  1652. idItemRemover
  1653. ===============================================================================
  1654. */
  1655. CLASS_DECLARATION( idEntity, idItemRemover )
  1656. EVENT( EV_Activate, idItemRemover::Event_Trigger )
  1657. END_CLASS
  1658. /*
  1659. ================
  1660. idItemRemover::Spawn
  1661. ================
  1662. */
  1663. void idItemRemover::Spawn() {
  1664. }
  1665. /*
  1666. ================
  1667. idItemRemover::RemoveItem
  1668. ================
  1669. */
  1670. void idItemRemover::RemoveItem( idPlayer *player ) {
  1671. const char *remove;
  1672. remove = spawnArgs.GetString( "remove" );
  1673. player->RemoveInventoryItem( remove );
  1674. }
  1675. /*
  1676. ================
  1677. idItemRemover::Event_Trigger
  1678. ================
  1679. */
  1680. void idItemRemover::Event_Trigger( idEntity *activator ) {
  1681. if ( activator->IsType( idPlayer::Type ) ) {
  1682. RemoveItem( static_cast<idPlayer *>(activator) );
  1683. }
  1684. }
  1685. /*
  1686. ===============================================================================
  1687. idObjectiveComplete
  1688. ===============================================================================
  1689. */
  1690. CLASS_DECLARATION( idItemRemover, idObjectiveComplete )
  1691. EVENT( EV_Activate, idObjectiveComplete::Event_Trigger )
  1692. EVENT( EV_HideObjective, idObjectiveComplete::Event_HideObjective )
  1693. EVENT( EV_GetPlayerPos, idObjectiveComplete::Event_GetPlayerPos )
  1694. END_CLASS
  1695. /*
  1696. ================
  1697. idObjectiveComplete::idObjectiveComplete
  1698. ================
  1699. */
  1700. idObjectiveComplete::idObjectiveComplete() {
  1701. playerPos.Zero();
  1702. }
  1703. /*
  1704. ================
  1705. idObjectiveComplete::Save
  1706. ================
  1707. */
  1708. void idObjectiveComplete::Save( idSaveGame *savefile ) const {
  1709. savefile->WriteVec3( playerPos );
  1710. }
  1711. /*
  1712. ================
  1713. idObjectiveComplete::Restore
  1714. ================
  1715. */
  1716. void idObjectiveComplete::Restore( idRestoreGame *savefile ) {
  1717. savefile->ReadVec3( playerPos );
  1718. }
  1719. /*
  1720. ================
  1721. idObjectiveComplete::Spawn
  1722. ================
  1723. */
  1724. void idObjectiveComplete::Spawn() {
  1725. spawnArgs.SetBool( "objEnabled", false );
  1726. Hide();
  1727. }
  1728. /*
  1729. ================
  1730. idObjectiveComplete::Event_Trigger
  1731. ================
  1732. */
  1733. void idObjectiveComplete::Event_Trigger( idEntity *activator ) {
  1734. if ( !spawnArgs.GetBool( "objEnabled" ) ) {
  1735. return;
  1736. }
  1737. idPlayer *player = gameLocal.GetLocalPlayer();
  1738. if ( player ) {
  1739. RemoveItem( player );
  1740. if ( spawnArgs.GetString( "inv_objective", NULL ) ) {
  1741. player->CompleteObjective( spawnArgs.GetString( "objectivetitle" ) );
  1742. PostEventMS( &EV_GetPlayerPos, 2000 );
  1743. }
  1744. }
  1745. }
  1746. /*
  1747. ================
  1748. idObjectiveComplete::Event_GetPlayerPos
  1749. ================
  1750. */
  1751. void idObjectiveComplete::Event_GetPlayerPos() {
  1752. idPlayer *player = gameLocal.GetLocalPlayer();
  1753. if ( player ) {
  1754. playerPos = player->GetPhysics()->GetOrigin();
  1755. PostEventMS( &EV_HideObjective, 100, player );
  1756. }
  1757. }
  1758. /*
  1759. ================
  1760. idObjectiveComplete::Event_HideObjective
  1761. ================
  1762. */
  1763. void idObjectiveComplete::Event_HideObjective( idEntity *e ) {
  1764. idPlayer *player = gameLocal.GetLocalPlayer();
  1765. if ( player ) {
  1766. idVec3 v = player->GetPhysics()->GetOrigin();
  1767. v -= playerPos;
  1768. if ( v.Length() > 64.0f ) {
  1769. player->HideObjective();
  1770. PostEventMS( &EV_Remove, 0 );
  1771. } else {
  1772. PostEventMS( &EV_HideObjective, 100, player );
  1773. }
  1774. }
  1775. }