bullets.c 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523
  1. #ifdef PRECOMPILEDHEADERS
  2. #include "Tactical All.h"
  3. #else
  4. #include "math.h"
  5. #include <stdio.h>
  6. #include <errno.h>
  7. #include "worlddef.h"
  8. #include "renderworld.h"
  9. #include "vsurface.h"
  10. #include "Render Dirty.h"
  11. #include "sysutil.h"
  12. #include "container.h"
  13. #include "wcheck.h"
  14. #include "video.h"
  15. #include "vobject_blitters.h"
  16. #include "faces.h"
  17. #include "utilities.h"
  18. #include "overhead.h"
  19. #include "gap.h"
  20. #include "Soldier Profile.h"
  21. #include "Bullets.h"
  22. #include "los.h"
  23. #include "worldman.h"
  24. #include "random.h"
  25. #include "GameSettings.h"
  26. #include "FileMan.h"
  27. #endif
  28. // Defines
  29. #define NUM_BULLET_SLOTS 50
  30. // GLOBAL FOR FACES LISTING
  31. BULLET gBullets[ NUM_BULLET_SLOTS ];
  32. UINT32 guiNumBullets = 0;
  33. INT32 GetFreeBullet(void)
  34. {
  35. UINT32 uiCount;
  36. for(uiCount=0; uiCount < guiNumBullets; uiCount++)
  37. {
  38. if((gBullets[uiCount].fAllocated==FALSE) )
  39. return((INT32)uiCount);
  40. }
  41. if(guiNumBullets < NUM_BULLET_SLOTS )
  42. return((INT32)guiNumBullets++);
  43. return(-1);
  44. }
  45. void RecountBullets(void)
  46. {
  47. INT32 uiCount;
  48. for(uiCount=guiNumBullets-1; (uiCount >=0) ; uiCount--)
  49. {
  50. if( ( gBullets[uiCount].fAllocated ) )
  51. {
  52. guiNumBullets=(UINT32)(uiCount+1);
  53. return;
  54. }
  55. }
  56. guiNumBullets = 0;
  57. }
  58. INT32 CreateBullet( UINT8 ubFirerID, BOOLEAN fFake, UINT16 usFlags )
  59. {
  60. INT32 iBulletIndex;
  61. BULLET *pBullet;
  62. if( ( iBulletIndex = GetFreeBullet() )==(-1) )
  63. return(-1);
  64. memset(&gBullets[ iBulletIndex ], 0, sizeof( BULLET ) );
  65. pBullet = &gBullets[ iBulletIndex ];
  66. pBullet->iBullet = iBulletIndex;
  67. pBullet->fAllocated = TRUE;
  68. pBullet->fLocated = FALSE;
  69. pBullet->ubFirerID = ubFirerID;
  70. pBullet->usFlags = usFlags;
  71. pBullet->usLastStructureHit = 0;
  72. if (fFake)
  73. {
  74. pBullet->fReal = FALSE;
  75. }
  76. else
  77. {
  78. pBullet->fReal = TRUE;
  79. }
  80. return( iBulletIndex );
  81. }
  82. void HandleBulletSpecialFlags( INT32 iBulletIndex )
  83. {
  84. BULLET *pBullet;
  85. ANITILE_PARAMS AniParams;
  86. FLOAT dX, dY;
  87. UINT8 ubDirection;
  88. pBullet = &( gBullets[ iBulletIndex ] );
  89. memset( &AniParams, 0, sizeof( ANITILE_PARAMS ) );
  90. if ( pBullet->fReal )
  91. {
  92. // Create ani tile if this is a spit!
  93. if ( pBullet->usFlags & ( BULLET_FLAG_KNIFE ) )
  94. {
  95. AniParams.sGridNo = (INT16)pBullet->sGridNo;
  96. AniParams.ubLevelID = ANI_STRUCT_LEVEL;
  97. AniParams.sDelay = 100;
  98. AniParams.sStartFrame = 3;
  99. AniParams.uiFlags = ANITILE_CACHEDTILE | ANITILE_FORWARD | ANITILE_LOOPING | ANITILE_USE_DIRECTION_FOR_START_FRAME;
  100. AniParams.sX = FIXEDPT_TO_INT32( pBullet->qCurrX );
  101. AniParams.sY = FIXEDPT_TO_INT32( pBullet->qCurrY );
  102. AniParams.sZ = CONVERT_HEIGHTUNITS_TO_PIXELS( FIXEDPT_TO_INT32( pBullet->qCurrZ ) );
  103. if ( pBullet->usFlags & ( BULLET_FLAG_CREATURE_SPIT ) )
  104. {
  105. strcpy( AniParams.zCachedFile, "TILECACHE\\SPIT2.STI" );
  106. }
  107. else if ( pBullet->usFlags & ( BULLET_FLAG_KNIFE ) )
  108. {
  109. strcpy( AniParams.zCachedFile, "TILECACHE\\KNIFING.STI" );
  110. pBullet->ubItemStatus = pBullet->pFirer->inv[ HANDPOS ].bStatus[0];
  111. }
  112. // Get direction to use for this guy....
  113. dX = ( (FLOAT)( pBullet->qIncrX ) / FIXEDPT_FRACTIONAL_RESOLUTION );
  114. dY = ( (FLOAT)( pBullet->qIncrY ) / FIXEDPT_FRACTIONAL_RESOLUTION );
  115. ubDirection = atan8( 0, 0, (INT16)( dX * 100 ), (INT16)( dY * 100 ) );
  116. AniParams.uiUserData3 = ubDirection;
  117. pBullet->pAniTile = CreateAnimationTile( &AniParams );
  118. // IF we are anything that needs a shadow.. set it here....
  119. if ( pBullet->usFlags & ( BULLET_FLAG_KNIFE ) )
  120. {
  121. AniParams.ubLevelID = ANI_SHADOW_LEVEL;
  122. AniParams.sZ = 0;
  123. pBullet->pShadowAniTile = CreateAnimationTile( &AniParams );
  124. }
  125. }
  126. }
  127. }
  128. void RemoveBullet( INT32 iBullet )
  129. {
  130. CHECKV( iBullet < NUM_BULLET_SLOTS );
  131. // decrease soldier's bullet count
  132. if (gBullets[ iBullet ].fReal)
  133. {
  134. // set to be deleted at next update
  135. gBullets[ iBullet ].fToDelete = TRUE;
  136. // decrement reference to bullet in the firer
  137. gBullets[ iBullet ].pFirer->bBulletsLeft--;
  138. DebugMsg( TOPIC_JA2, DBG_LEVEL_3, String("!!!!!!! Ending bullet, bullets left %d", gBullets[ iBullet ].pFirer->bBulletsLeft ) );
  139. if ( gBullets[ iBullet ].usFlags & ( BULLET_FLAG_KNIFE ) )
  140. {
  141. // Delete ani tile
  142. if ( gBullets[ iBullet ].pAniTile != NULL )
  143. {
  144. DeleteAniTile( gBullets[ iBullet ].pAniTile );
  145. gBullets[ iBullet ].pAniTile = NULL;
  146. }
  147. // Delete shadow
  148. if ( gBullets[ iBullet ].usFlags & ( BULLET_FLAG_KNIFE ) )
  149. {
  150. if ( gBullets[ iBullet ].pShadowAniTile != NULL )
  151. {
  152. DeleteAniTile( gBullets[ iBullet ].pShadowAniTile );
  153. gBullets[ iBullet ].pShadowAniTile = NULL;
  154. }
  155. }
  156. }
  157. }
  158. else
  159. {
  160. // delete this fake bullet right away!
  161. gBullets[ iBullet ].fAllocated = FALSE;
  162. RecountBullets();
  163. }
  164. }
  165. void LocateBullet( INT32 iBulletIndex )
  166. {
  167. if ( gGameSettings.fOptions[ TOPTION_SHOW_MISSES ] )
  168. {
  169. // Check if a bad guy fired!
  170. if ( gBullets[ iBulletIndex ].ubFirerID != NOBODY )
  171. {
  172. if ( MercPtrs[ gBullets[ iBulletIndex ].ubFirerID ]->bSide == gbPlayerNum )
  173. {
  174. if ( !gBullets[ iBulletIndex ].fLocated )
  175. {
  176. gBullets[ iBulletIndex ].fLocated = TRUE;
  177. //Only if we are in turnbased and noncombat
  178. if ( gTacticalStatus.uiFlags & TURNBASED && (gTacticalStatus.uiFlags & INCOMBAT) )
  179. {
  180. LocateGridNo( (INT16)gBullets[ iBulletIndex ].sGridNo );
  181. }
  182. }
  183. }
  184. }
  185. }
  186. }
  187. void UpdateBullets( )
  188. {
  189. UINT32 uiCount;
  190. LEVELNODE *pNode;
  191. BOOLEAN fDeletedSome = FALSE;
  192. for ( uiCount = 0; uiCount < guiNumBullets; uiCount++ )
  193. {
  194. if ( gBullets[ uiCount ].fAllocated)
  195. {
  196. if (gBullets[ uiCount ].fReal && !( gBullets[ uiCount ].usFlags & BULLET_STOPPED ) )
  197. {
  198. // there are duplicate checks for deletion in case the bullet is deleted by shooting
  199. // someone at point blank range, in the first MoveBullet call in the FireGun code
  200. if ( gBullets[ uiCount ].fToDelete )
  201. {
  202. // Remove from old position
  203. gBullets[ uiCount ].fAllocated = FALSE;
  204. fDeletedSome = TRUE;
  205. continue;
  206. }
  207. //if ( !( gGameSettings.fOptions[ TOPTION_HIDE_BULLETS ] ) )
  208. {
  209. // ALRIGHTY, CHECK WHAT TYPE OF BULLET WE ARE
  210. if ( gBullets[ uiCount ].usFlags & ( BULLET_FLAG_CREATURE_SPIT | BULLET_FLAG_KNIFE | BULLET_FLAG_MISSILE | BULLET_FLAG_SMALL_MISSILE | BULLET_FLAG_TANK_CANNON | BULLET_FLAG_FLAME ) )
  211. {
  212. }
  213. else
  214. {
  215. RemoveStruct( gBullets[ uiCount ].sGridNo, BULLETTILE1 );
  216. RemoveStruct( gBullets[ uiCount ].sGridNo, BULLETTILE2 );
  217. }
  218. }
  219. MoveBullet( uiCount );
  220. if ( gBullets[ uiCount ].fToDelete )
  221. {
  222. // Remove from old position
  223. gBullets[ uiCount ].fAllocated = FALSE;
  224. fDeletedSome = TRUE;
  225. continue;
  226. }
  227. if ( gBullets[ uiCount ].usFlags & BULLET_STOPPED )
  228. {
  229. continue;
  230. }
  231. // Display bullet
  232. //if ( !( gGameSettings.fOptions[ TOPTION_HIDE_BULLETS ] ) )
  233. {
  234. if ( gBullets[ uiCount ].usFlags & ( BULLET_FLAG_KNIFE ) )
  235. {
  236. if ( gBullets[ uiCount ].pAniTile != NULL )
  237. {
  238. gBullets[ uiCount ].pAniTile->sRelativeX = (INT16) FIXEDPT_TO_INT32( gBullets[ uiCount ].qCurrX );
  239. gBullets[ uiCount ].pAniTile->sRelativeY = (INT16) FIXEDPT_TO_INT32( gBullets[ uiCount ].qCurrY );
  240. gBullets[ uiCount ].pAniTile->pLevelNode->sRelativeZ = (INT16) CONVERT_HEIGHTUNITS_TO_PIXELS( FIXEDPT_TO_INT32( gBullets[ uiCount ].qCurrZ ) );
  241. if ( gBullets[ uiCount ].usFlags & ( BULLET_FLAG_KNIFE ) )
  242. {
  243. gBullets[ uiCount ].pShadowAniTile->sRelativeX = (INT16) FIXEDPT_TO_INT32( gBullets[ uiCount ].qCurrX );
  244. gBullets[ uiCount ].pShadowAniTile->sRelativeY = (INT16) FIXEDPT_TO_INT32( gBullets[ uiCount ].qCurrY );
  245. }
  246. }
  247. }
  248. // Are we a missle?
  249. else if ( gBullets[ uiCount ].usFlags & ( BULLET_FLAG_MISSILE | BULLET_FLAG_SMALL_MISSILE | BULLET_FLAG_TANK_CANNON | BULLET_FLAG_FLAME | BULLET_FLAG_CREATURE_SPIT ) )
  250. {
  251. }
  252. else
  253. {
  254. pNode = AddStructToTail( gBullets[ uiCount ].sGridNo, BULLETTILE1 );
  255. pNode->ubShadeLevel=DEFAULT_SHADE_LEVEL;
  256. pNode->ubNaturalShadeLevel=DEFAULT_SHADE_LEVEL;
  257. pNode->uiFlags |= ( LEVELNODE_USEABSOLUTEPOS | LEVELNODE_IGNOREHEIGHT );
  258. pNode->sRelativeX = (INT16) FIXEDPT_TO_INT32( gBullets[ uiCount ].qCurrX );
  259. pNode->sRelativeY = (INT16) FIXEDPT_TO_INT32( gBullets[ uiCount ].qCurrY );
  260. pNode->sRelativeZ = (INT16) CONVERT_HEIGHTUNITS_TO_PIXELS( FIXEDPT_TO_INT32( gBullets[ uiCount ].qCurrZ ) );
  261. // Display shadow
  262. pNode = AddStructToTail( gBullets[ uiCount ].sGridNo, BULLETTILE2 );
  263. pNode->ubShadeLevel=DEFAULT_SHADE_LEVEL;
  264. pNode->ubNaturalShadeLevel=DEFAULT_SHADE_LEVEL;
  265. pNode->uiFlags |= ( LEVELNODE_USEABSOLUTEPOS | LEVELNODE_IGNOREHEIGHT );
  266. pNode->sRelativeX = (INT16) FIXEDPT_TO_INT32( gBullets[ uiCount ].qCurrX );
  267. pNode->sRelativeY = (INT16) FIXEDPT_TO_INT32( gBullets[ uiCount ].qCurrY );
  268. pNode->sRelativeZ = (INT16)gpWorldLevelData[ gBullets[ uiCount ].sGridNo ].sHeight;
  269. }
  270. }
  271. }
  272. else
  273. {
  274. if ( gBullets[ uiCount ].fToDelete )
  275. {
  276. gBullets[ uiCount ].fAllocated = FALSE;
  277. fDeletedSome = TRUE;
  278. }
  279. }
  280. }
  281. }
  282. if ( fDeletedSome )
  283. {
  284. RecountBullets( );
  285. }
  286. }
  287. BULLET *GetBulletPtr( INT32 iBullet )
  288. {
  289. BULLET *pBullet;
  290. CHECKN( iBullet < NUM_BULLET_SLOTS );
  291. pBullet = &gBullets[ iBullet ];
  292. return( pBullet );
  293. }
  294. void AddMissileTrail( BULLET *pBullet, FIXEDPT qCurrX, FIXEDPT qCurrY, FIXEDPT qCurrZ )
  295. {
  296. ANITILE_PARAMS AniParams;
  297. // If we are a small missle, don't show
  298. if ( pBullet->usFlags & ( BULLET_FLAG_SMALL_MISSILE | BULLET_FLAG_FLAME | BULLET_FLAG_CREATURE_SPIT ) )
  299. {
  300. if ( pBullet->iLoop < 5 )
  301. {
  302. return;
  303. }
  304. }
  305. // If we are a small missle, don't show
  306. if ( pBullet->usFlags & ( BULLET_FLAG_TANK_CANNON ) )
  307. {
  308. //if ( pBullet->iLoop < 40 )
  309. //{
  310. return;
  311. //}
  312. }
  313. memset( &AniParams, 0, sizeof( ANITILE_PARAMS ) );
  314. AniParams.sGridNo = (INT16)pBullet->sGridNo;
  315. AniParams.ubLevelID = ANI_STRUCT_LEVEL;
  316. AniParams.sDelay = (INT16)( 100 + Random( 100 ) );
  317. AniParams.sStartFrame = 0;
  318. AniParams.uiFlags = ANITILE_CACHEDTILE | ANITILE_FORWARD | ANITILE_ALWAYS_TRANSLUCENT;
  319. AniParams.sX = FIXEDPT_TO_INT32( qCurrX );
  320. AniParams.sY = FIXEDPT_TO_INT32( qCurrY );
  321. AniParams.sZ = CONVERT_HEIGHTUNITS_TO_PIXELS( FIXEDPT_TO_INT32( qCurrZ ) );
  322. if ( pBullet->usFlags & ( BULLET_FLAG_MISSILE | BULLET_FLAG_TANK_CANNON ) )
  323. {
  324. strcpy( AniParams.zCachedFile, "TILECACHE\\MSLE_SMK.STI" );
  325. }
  326. else if ( pBullet->usFlags & ( BULLET_FLAG_SMALL_MISSILE ) )
  327. {
  328. strcpy( AniParams.zCachedFile, "TILECACHE\\MSLE_SMA.STI" );
  329. }
  330. else if ( pBullet->usFlags & ( BULLET_FLAG_CREATURE_SPIT ) )
  331. {
  332. strcpy( AniParams.zCachedFile, "TILECACHE\\MSLE_SPT.STI" );
  333. }
  334. else if ( pBullet->usFlags & ( BULLET_FLAG_FLAME ) )
  335. {
  336. strcpy( AniParams.zCachedFile, "TILECACHE\\FLMTHR2.STI" );
  337. AniParams.sDelay = (INT16)( 100 );
  338. }
  339. CreateAnimationTile( &AniParams );
  340. }
  341. BOOLEAN SaveBulletStructureToSaveGameFile( HWFILE hFile )
  342. {
  343. UINT32 uiNumBytesWritten;
  344. UINT16 usCnt;
  345. UINT32 uiBulletCount=0;
  346. //loop through and count the number of bullets
  347. for( usCnt=0; usCnt<NUM_BULLET_SLOTS; usCnt++ )
  348. {
  349. //if the bullet is active, save it
  350. if( gBullets[ usCnt ].fAllocated )
  351. {
  352. uiBulletCount++;
  353. }
  354. }
  355. //Save the number of Bullets in the array
  356. FileWrite( hFile, &uiBulletCount, sizeof( UINT32 ), &uiNumBytesWritten );
  357. if( uiNumBytesWritten != sizeof( UINT32 ) )
  358. {
  359. return( FALSE );
  360. }
  361. if( uiBulletCount != 0 )
  362. {
  363. for( usCnt=0; usCnt<NUM_BULLET_SLOTS; usCnt++ )
  364. {
  365. //if the bullet is active, save it
  366. if( gBullets[ usCnt ].fAllocated )
  367. {
  368. //Save the the Bullet structure
  369. FileWrite( hFile, &gBullets[usCnt], sizeof( BULLET ), &uiNumBytesWritten );
  370. if( uiNumBytesWritten != sizeof( BULLET ) )
  371. {
  372. return( FALSE );
  373. }
  374. }
  375. }
  376. }
  377. return( TRUE );
  378. }
  379. BOOLEAN LoadBulletStructureFromSavedGameFile( HWFILE hFile )
  380. {
  381. UINT32 uiNumBytesRead;
  382. UINT16 usCnt;
  383. //make sure the bullets are not allocated
  384. memset( gBullets, 0, NUM_BULLET_SLOTS * sizeof( BULLET ) );
  385. //Load the number of Bullets in the array
  386. FileRead( hFile, &guiNumBullets, sizeof( UINT32 ), &uiNumBytesRead );
  387. if( uiNumBytesRead != sizeof( UINT32 ) )
  388. {
  389. return( FALSE );
  390. }
  391. for( usCnt=0; usCnt<guiNumBullets; usCnt++ )
  392. {
  393. //Load the the Bullet structure
  394. FileRead( hFile, &gBullets[usCnt], sizeof( BULLET ), &uiNumBytesRead );
  395. if( uiNumBytesRead != sizeof( BULLET ) )
  396. {
  397. return( FALSE );
  398. }
  399. //Set some parameters
  400. gBullets[usCnt].uiLastUpdate = 0;
  401. if( gBullets[usCnt].ubFirerID != NOBODY )
  402. gBullets[usCnt].pFirer = &Menptr[ gBullets[usCnt].ubFirerID ];
  403. else
  404. gBullets[usCnt].pFirer = NULL;
  405. gBullets[usCnt].pAniTile = NULL;
  406. gBullets[usCnt].pShadowAniTile = NULL;
  407. gBullets[usCnt].iBullet = usCnt;
  408. HandleBulletSpecialFlags( gBullets[usCnt].iBullet );
  409. }
  410. return( TRUE );
  411. }
  412. void StopBullet( INT32 iBullet )
  413. {
  414. gBullets[ iBullet ].usFlags |= BULLET_STOPPED;
  415. RemoveStruct( gBullets[ iBullet ].sGridNo, BULLETTILE1 );
  416. RemoveStruct( gBullets[ iBullet ].sGridNo, BULLETTILE2 );
  417. }
  418. void DeleteAllBullets( )
  419. {
  420. UINT32 uiCount;
  421. for ( uiCount = 0; uiCount < guiNumBullets; uiCount++ )
  422. {
  423. if ( gBullets[ uiCount ].fAllocated)
  424. {
  425. // Remove from old position
  426. RemoveBullet( uiCount );
  427. gBullets[ uiCount ].fAllocated = FALSE;
  428. }
  429. }
  430. RecountBullets( );
  431. }