g_roff.cpp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647
  1. // leave this line at the top for all g_xxxx.cpp files...
  2. #include "g_headers.h"
  3. #include "g_local.h"
  4. #include "g_roff.h"
  5. #include "Q3_Interface.h"
  6. // The list of precached ROFFs
  7. roff_list_t roffs[MAX_ROFFS];
  8. int num_roffs = 0;
  9. qboolean g_bCollidableRoffs = qfalse;
  10. extern void Q3_TaskIDComplete( gentity_t *ent, taskID_t taskType );
  11. static void G_RoffNotetrackCallback( gentity_t *cent, const char *notetrack)
  12. {
  13. int i = 0, r = 0, r2 = 0, objectID = 0, anglesGathered = 0, posoffsetGathered = 0;
  14. char type[256];
  15. char argument[512];
  16. char addlArg[512];
  17. char errMsg[256];
  18. char t[64];
  19. char teststr[256];
  20. int addlArgs = 0;
  21. vec3_t parsedAngles, parsedOffset, useAngles, useOrigin, forward, right, up;
  22. if (!cent || !notetrack)
  23. {
  24. return;
  25. }
  26. //notetrack = "effect effects/explosion1.efx 0+0+64 0-0-1";
  27. while (notetrack[i] && notetrack[i] != ' ')
  28. {
  29. type[i] = notetrack[i];
  30. i++;
  31. }
  32. type[i] = '\0';
  33. if (notetrack[i] != ' ')
  34. { //didn't pass in a valid notetrack type, or forgot the argument for it
  35. return;
  36. }
  37. i++;
  38. while (notetrack[i] && notetrack[i] != ' ')
  39. {
  40. if (notetrack[i] != '\n' && notetrack[i] != '\r')
  41. { //don't read line ends for an argument
  42. argument[r] = notetrack[i];
  43. r++;
  44. }
  45. i++;
  46. }
  47. argument[r] = '\0';
  48. if (!r)
  49. {
  50. return;
  51. }
  52. if (notetrack[i] == ' ')
  53. { //additional arguments...
  54. addlArgs = 1;
  55. i++;
  56. r = 0;
  57. while (notetrack[i])
  58. {
  59. addlArg[r] = notetrack[i];
  60. r++;
  61. i++;
  62. }
  63. addlArg[r] = '\0';
  64. }
  65. if (strcmp(type, "effect") == 0)
  66. {
  67. if (!addlArgs)
  68. {
  69. VectorClear(parsedOffset);
  70. goto defaultoffsetposition;
  71. }
  72. i = 0;
  73. while (posoffsetGathered < 3)
  74. {
  75. r = 0;
  76. while (addlArg[i] && addlArg[i] != '+' && addlArg[i] != ' ')
  77. {
  78. t[r] = addlArg[i];
  79. r++;
  80. i++;
  81. }
  82. t[r] = '\0';
  83. i++;
  84. if (!r)
  85. { //failure..
  86. VectorClear(parsedOffset);
  87. i = 0;
  88. goto defaultoffsetposition;
  89. }
  90. parsedOffset[posoffsetGathered] = atof(t);
  91. posoffsetGathered++;
  92. }
  93. if (posoffsetGathered < 3)
  94. {
  95. sprintf(errMsg, "Offset position argument for 'effect' type is invalid.");
  96. goto functionend;
  97. }
  98. i--;
  99. if (addlArg[i] != ' ')
  100. {
  101. addlArgs = 0;
  102. }
  103. defaultoffsetposition:
  104. r = 0;
  105. if (argument[r] == '/')
  106. {
  107. r++;
  108. }
  109. while (argument[r] && argument[r] != '/')
  110. {
  111. teststr[r2] = argument[r];
  112. r2++;
  113. r++;
  114. }
  115. teststr[r2] = '\0';
  116. if (r2 && strstr(teststr, "effects"))
  117. { //get rid of the leading "effects" since it's auto-inserted
  118. r++;
  119. r2 = 0;
  120. while (argument[r])
  121. {
  122. teststr[r2] = argument[r];
  123. r2++;
  124. r++;
  125. }
  126. teststr[r2] = '\0';
  127. strcpy(argument, teststr);
  128. }
  129. objectID = G_EffectIndex(argument);
  130. r = 0;
  131. if (objectID)
  132. {
  133. if (addlArgs)
  134. { //if there is an additional argument for an effect it is expected to be XANGLE-YANGLE-ZANGLE
  135. i++;
  136. while (anglesGathered < 3)
  137. {
  138. r = 0;
  139. while (addlArg[i] && addlArg[i] != '-')
  140. {
  141. t[r] = addlArg[i];
  142. r++;
  143. i++;
  144. }
  145. t[r] = '\0';
  146. i++;
  147. if (!r)
  148. { //failed to get a new part of the vector
  149. anglesGathered = 0;
  150. break;
  151. }
  152. parsedAngles[anglesGathered] = atof(t);
  153. anglesGathered++;
  154. }
  155. if (anglesGathered)
  156. {
  157. VectorCopy(parsedAngles, useAngles);
  158. }
  159. else
  160. { //failed to parse angles from the extra argument provided..
  161. VectorCopy(cent->s.apos.trBase, useAngles);
  162. }
  163. }
  164. else
  165. { //if no constant angles, play in direction entity is facing
  166. VectorCopy(cent->s.apos.trBase, useAngles);
  167. }
  168. AngleVectors(useAngles, forward, right, up);
  169. VectorCopy(cent->s.pos.trBase, useOrigin);
  170. //forward
  171. useOrigin[0] += forward[0]*parsedOffset[0];
  172. useOrigin[1] += forward[1]*parsedOffset[0];
  173. useOrigin[2] += forward[2]*parsedOffset[0];
  174. //right
  175. useOrigin[0] += right[0]*parsedOffset[1];
  176. useOrigin[1] += right[1]*parsedOffset[1];
  177. useOrigin[2] += right[2]*parsedOffset[1];
  178. //up
  179. useOrigin[0] += up[0]*parsedOffset[2];
  180. useOrigin[1] += up[1]*parsedOffset[2];
  181. useOrigin[2] += up[2]*parsedOffset[2];
  182. G_PlayEffect(objectID, useOrigin, useAngles);
  183. }
  184. }
  185. else if (strcmp(type, "sound") == 0)
  186. {
  187. objectID = G_SoundIndex(argument);
  188. cgi_S_StartSound(cent->s.pos.trBase, cent->s.number, CHAN_BODY, objectID);
  189. }
  190. //else if ...
  191. else
  192. {
  193. if (type[0])
  194. {
  195. Com_Printf("Warning: \"%s\" is an invalid ROFF notetrack function\n", type);
  196. }
  197. else
  198. {
  199. Com_Printf("Warning: Notetrack is missing function and/or arguments\n");
  200. }
  201. }
  202. return;
  203. functionend:
  204. Com_Printf("Type-specific notetrack error: %s\n", errMsg);
  205. return;
  206. }
  207. static qboolean G_ValidRoff( roff_hdr2_t *header )
  208. {
  209. if ( !strncmp( header->mHeader, "ROFF", 4 ))
  210. {
  211. if ( header->mCount > 0 && header->mVersion == ROFF_VERSION2 )
  212. {
  213. return qtrue;
  214. }
  215. else if ( header->mVersion == ROFF_VERSION && ((roff_hdr_t*)header)->mCount > 0.0f )
  216. { // version 1 defines the count as a float, so we best do the count check as a float or we'll get bogus results
  217. return qtrue;
  218. }
  219. }
  220. return qfalse;
  221. }
  222. static void G_FreeRoff(int index)
  223. {
  224. if(roffs[index].mNumNoteTracks) {
  225. delete [] roffs[index].mNoteTrackIndexes[0];
  226. delete [] roffs[index].mNoteTrackIndexes;
  227. }
  228. }
  229. static qboolean G_InitRoff( char *file, unsigned char *data )
  230. {
  231. roff_hdr_t *header = (roff_hdr_t *)data;
  232. int count = (int)header->mCount;
  233. roffs[num_roffs].fileName = G_NewString( file );
  234. if ( header->mVersion == ROFF_VERSION )
  235. {
  236. // We are Old School(tm)
  237. roffs[num_roffs].type = 1;
  238. roffs[num_roffs].data = (void *) G_Alloc( count * sizeof( move_rotate_t ) );
  239. move_rotate_t *mem = (move_rotate_t *)roffs[num_roffs].data;
  240. roffs[num_roffs].mFrameTime = 100; // old school ones have a hard-coded frame time
  241. roffs[num_roffs].mLerp = 10;
  242. roffs[num_roffs].mNumNoteTracks = 0;
  243. roffs[num_roffs].mNoteTrackIndexes = NULL;
  244. if ( mem )
  245. {
  246. // The allocation worked, so stash this stuff off so we can reference the data later if needed
  247. roffs[num_roffs].frames = count;
  248. // Step past the header to get to the goods
  249. move_rotate_t *roff_data = ( move_rotate_t *)&header[1];
  250. // Copy all of the goods into our ROFF cache
  251. for ( int i = 0; i < count; i++, roff_data++, mem++ )
  252. {
  253. // Copy just the delta position and orientation which can be applied to anything at a later point
  254. VectorCopy( roff_data->origin_delta, mem->origin_delta );
  255. VectorCopy( roff_data->rotate_delta, mem->rotate_delta );
  256. }
  257. return qtrue;
  258. }
  259. }
  260. else if ( header->mVersion == ROFF_VERSION2 )
  261. {
  262. // Version 2.0, heck yeah!
  263. roff_hdr2_t *hdr = (roff_hdr2_t *)data;
  264. count = hdr->mCount;
  265. roffs[num_roffs].frames = count;
  266. roffs[num_roffs].data = (void *) G_Alloc( count * sizeof( move_rotate2_t ));
  267. move_rotate2_t *mem = (move_rotate2_t *)roffs[num_roffs].data;
  268. if ( mem )
  269. {
  270. roffs[num_roffs].mFrameTime = hdr->mFrameRate;
  271. roffs[num_roffs].mLerp = 1000 / hdr->mFrameRate;
  272. roffs[num_roffs].mNumNoteTracks = hdr->mNumNotes;
  273. if (roffs[num_roffs].mFrameTime < 50)
  274. {
  275. Com_Printf(S_COLOR_RED"Error: \"%s\" has an invalid ROFF framerate (%d < 50)\n", file, roffs[num_roffs].mFrameTime);
  276. }
  277. assert( roffs[num_roffs].mFrameTime >= 50 );//HAS to be at least 50 to be reliable
  278. // Step past the header to get to the goods
  279. move_rotate2_t *roff_data = ( move_rotate2_t *)&hdr[1];
  280. roffs[num_roffs].type = 2; //rww - any reason this wasn't being set already?
  281. // Copy all of the goods into our ROFF cache
  282. for ( int i = 0; i < count; i++ )
  283. {
  284. VectorCopy( roff_data[i].origin_delta, mem[i].origin_delta );
  285. VectorCopy( roff_data[i].rotate_delta, mem[i].rotate_delta );
  286. mem[i].mStartNote = roff_data[i].mStartNote;
  287. mem[i].mNumNotes = roff_data[i].mNumNotes;
  288. }
  289. if ( hdr->mNumNotes )
  290. {
  291. int size;
  292. char *ptr, *start;
  293. ptr = start = (char *)&roff_data[i];
  294. size = 0;
  295. for( i = 0; i < hdr->mNumNotes; i++ )
  296. {
  297. size += strlen(ptr) + 1;
  298. ptr += strlen(ptr) + 1;
  299. }
  300. // ? Get rid of dynamic memory ?
  301. roffs[num_roffs].mNoteTrackIndexes = new char *[hdr->mNumNotes];
  302. ptr = roffs[num_roffs].mNoteTrackIndexes[0] = new char[size];
  303. memcpy(roffs[num_roffs].mNoteTrackIndexes[0], start, size);
  304. for( i = 1; i < hdr->mNumNotes; i++ )
  305. {
  306. ptr += strlen(ptr) + 1;
  307. roffs[num_roffs].mNoteTrackIndexes[i] = ptr;
  308. }
  309. }
  310. return qtrue;
  311. }
  312. }
  313. return false;
  314. }
  315. //-------------------------------------------------------
  316. // G_LoadRoff
  317. //
  318. // Does the fun work of loading and caching a roff file
  319. // If the file is already cached, it just returns an
  320. // ID to the cached file.
  321. //-------------------------------------------------------
  322. int G_LoadRoff( const char *fileName )
  323. {
  324. char file[MAX_QPATH];
  325. byte *data;
  326. int len, i, roff_id = 0;
  327. // Before even bothering with all of this, make sure we have a place to store it.
  328. if ( num_roffs >= MAX_ROFFS )
  329. {
  330. Com_Printf( S_COLOR_RED"MAX_ROFFS count exceeded. Skipping load of .ROF '%s'\n", fileName );
  331. return roff_id;
  332. }
  333. // The actual path
  334. sprintf( file, "%s/%s.rof", Q3_SCRIPT_DIR, fileName );
  335. // See if I'm already precached
  336. for ( i = 0; i < num_roffs; i++ )
  337. {
  338. if ( stricmp( file, roffs[i].fileName ) == 0 )
  339. {
  340. // Good, just return me...avoid zero index
  341. return i + 1;
  342. }
  343. }
  344. #ifdef _DEBUG
  345. // Com_Printf( S_COLOR_GREEN"Caching ROF: '%s'\n", file );
  346. #endif
  347. // Read the file in one fell swoop
  348. len = gi.FS_ReadFile( file, (void**) &data);
  349. if ( len <= 0 )
  350. {
  351. Com_Printf( S_COLOR_RED"Could not open .ROF file '%s'\n", fileName );
  352. return roff_id;
  353. }
  354. // Now let's check the header info...
  355. roff_hdr2_t *header = (roff_hdr2_t *)data;
  356. // ..and make sure it's reasonably valid
  357. if ( !G_ValidRoff( header ))
  358. {
  359. Com_Printf( S_COLOR_RED"Invalid roff format '%s'\n", fileName );
  360. }
  361. else
  362. {
  363. G_InitRoff( file, data );
  364. // Done loading this roff, so save off an id to it..increment first to avoid zero index
  365. roff_id = ++num_roffs;
  366. }
  367. gi.FS_FreeFile( data );
  368. return roff_id;
  369. }
  370. void G_FreeRoffs(void)
  371. {
  372. while(num_roffs) {
  373. G_FreeRoff(num_roffs - 1);
  374. num_roffs--;
  375. }
  376. }
  377. //-------------------------------------------------------
  378. // G_Roff
  379. //
  380. // Handles applying the roff data to the specified ent
  381. //-------------------------------------------------------
  382. void G_Roff( gentity_t *ent )
  383. {
  384. if ( !ent->next_roff_time )
  385. {
  386. return;
  387. }
  388. if ( ent->next_roff_time > level.time )
  389. {// either I don't think or it's just not time to have me think yet
  390. return;
  391. }
  392. const int roff_id = G_LoadRoff( ent->roff );
  393. if ( !roff_id )
  394. { // Couldn't cache this rof
  395. return;
  396. }
  397. // The ID is one higher than the array index
  398. const roff_list_t * roff = &roffs[ roff_id - 1 ];
  399. vec3_t org, ang;
  400. if ( roff->type == 2 )
  401. {
  402. move_rotate2_t *data = &((move_rotate2_t *)roff->data)[ ent->roff_ctr ];
  403. VectorCopy( data->origin_delta, org );
  404. VectorCopy( data->rotate_delta, ang );
  405. if (data->mStartNote != -1 || data->mNumNotes)
  406. {
  407. G_RoffNotetrackCallback(ent, roffs[roff_id - 1].mNoteTrackIndexes[data->mStartNote]);
  408. }
  409. }
  410. else
  411. {
  412. move_rotate_t *data = &((move_rotate_t *)roff->data)[ ent->roff_ctr ];
  413. VectorCopy( data->origin_delta, org );
  414. VectorCopy( data->rotate_delta, ang );
  415. }
  416. #ifdef _DEBUG
  417. if ( g_developer->integer )
  418. {
  419. Com_Printf( S_COLOR_GREEN"ROFF dat: num: %d o:<%.2f %.2f %.2f> a:<%.2f %.2f %.2f>\n",
  420. ent->roff_ctr,
  421. org[0], org[1], org[2],
  422. ang[0], ang[1], ang[2] );
  423. }
  424. #endif
  425. if ( ent->client )
  426. {
  427. // Set up the angle interpolation
  428. //-------------------------------------
  429. VectorAdd( ent->s.apos.trBase, ang, ent->s.apos.trBase );
  430. ent->s.apos.trTime = level.time;
  431. ent->s.apos.trType = TR_INTERPOLATE;
  432. // Store what the next apos->trBase should be
  433. VectorCopy( ent->s.apos.trBase, ent->client->ps.viewangles );
  434. VectorCopy( ent->s.apos.trBase, ent->currentAngles );
  435. VectorCopy( ent->s.apos.trBase, ent->s.angles );
  436. if ( ent->NPC )
  437. {
  438. //ent->NPC->desiredPitch = ent->s.apos.trBase[PITCH];
  439. ent->NPC->desiredYaw = ent->s.apos.trBase[YAW];
  440. }
  441. // Set up the origin interpolation
  442. //-------------------------------------
  443. VectorAdd( ent->s.pos.trBase, org, ent->s.pos.trBase );
  444. ent->s.pos.trTime = level.time;
  445. ent->s.pos.trType = TR_INTERPOLATE;
  446. // Store what the next pos->trBase should be
  447. VectorCopy( ent->s.pos.trBase, ent->client->ps.origin );
  448. VectorCopy( ent->s.pos.trBase, ent->currentOrigin );
  449. //VectorCopy( ent->s.pos.trBase, ent->s.origin );
  450. }
  451. else
  452. {
  453. // Set up the angle interpolation
  454. //-------------------------------------
  455. VectorScale( ang, roff->mLerp, ent->s.apos.trDelta );
  456. VectorCopy( ent->pos2, ent->s.apos.trBase );
  457. ent->s.apos.trTime = level.time;
  458. ent->s.apos.trType = TR_LINEAR;
  459. // Store what the next apos->trBase should be
  460. VectorAdd( ent->pos2, ang, ent->pos2 );
  461. // Set up the origin interpolation
  462. //-------------------------------------
  463. VectorScale( org, roff->mLerp, ent->s.pos.trDelta );
  464. VectorCopy( ent->pos1, ent->s.pos.trBase );
  465. ent->s.pos.trTime = level.time;
  466. ent->s.pos.trType = TR_LINEAR;
  467. // Store what the next apos->trBase should be
  468. VectorAdd( ent->pos1, org, ent->pos1 );
  469. //make it true linear... FIXME: sticks around after ROFF is done, but do we really care?
  470. ent->alt_fire = qtrue;
  471. if ( ent->e_ThinkFunc == thinkF_TieFighterThink || ent->e_ThinkFunc == thinkF_TieBomberThink ||
  472. ( !ent->e_ThinkFunc
  473. && ent->s.eType != ET_MISSILE
  474. && ent->s.eType != ET_ITEM
  475. && ent->s.eType != ET_MOVER ) )
  476. {//will never set currentAngles & currentOrigin itself ( why do we limit which one's get set?, just set all the time? )
  477. EvaluateTrajectory( &ent->s.apos, level.time, ent->currentAngles );
  478. EvaluateTrajectory( &ent->s.pos, level.time, ent->currentOrigin );
  479. }
  480. }
  481. // Link just in case.
  482. gi.linkentity( ent );
  483. // See if the ROFF playback is done
  484. //-------------------------------------
  485. if ( ++ent->roff_ctr >= roff->frames )
  486. {
  487. // We are done, so let me think no more, then tell the task that we're done.
  488. ent->next_roff_time = 0;
  489. // Stop any rotation or movement.
  490. VectorClear( ent->s.pos.trDelta );
  491. VectorClear( ent->s.apos.trDelta );
  492. Q3_TaskIDComplete( ent, TID_MOVE_NAV );
  493. return;
  494. }
  495. ent->next_roff_time = level.time + roff->mFrameTime;
  496. }
  497. //-------------------------------------------------------
  498. // G_SaveCachedRoffs
  499. //
  500. // Really fun savegame stuff
  501. //-------------------------------------------------------
  502. void G_SaveCachedRoffs()
  503. {
  504. int i, len;
  505. // Write out the number of cached ROFFs
  506. gi.AppendToSaveGame( 'ROFF', (void *)&num_roffs, sizeof(num_roffs) );
  507. // Now dump out the cached ROFF file names in order so they can be loaded on the other end
  508. for ( i = 0; i < num_roffs; i++ )
  509. {
  510. // Dump out the string length to make things a bit easier on the other end...heh heh.
  511. len = strlen( roffs[i].fileName ) + 1;
  512. gi.AppendToSaveGame( 'SLEN', (void *)&len, sizeof(len) );
  513. gi.AppendToSaveGame( 'RSTR', (void *)(*roffs[i].fileName), len );
  514. }
  515. }
  516. //-------------------------------------------------------
  517. // G_LoadCachedRoffs
  518. //
  519. // Really fun loadgame stuff
  520. //-------------------------------------------------------
  521. void G_LoadCachedRoffs()
  522. {
  523. int i, count, len;
  524. char buffer[MAX_QPATH];
  525. // Get the count of goodies we need to revive
  526. gi.ReadFromSaveGame( 'ROFF', (void *)&count, sizeof(count) );
  527. // Now bring 'em back to life
  528. for ( i = 0; i < count; i++ )
  529. {
  530. gi.ReadFromSaveGame( 'SLEN', (void *)&len, sizeof(len) );
  531. gi.ReadFromSaveGame( 'RSTR', (void *)(buffer), len );
  532. G_LoadRoff( buffer );
  533. }
  534. }