Smell.c 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577
  1. #ifdef PRECOMPILEDHEADERS
  2. #include "TileEngine All.h"
  3. #else
  4. #include "worlddef.h"
  5. #include "Random.h"
  6. #include "Smell.h"
  7. #include "worldman.h"
  8. #include "renderworld.h"
  9. #include "SaveLoadMap.h"
  10. #include "gamesettings.h"
  11. #include "message.h"
  12. #include "Isometric utils.h"
  13. #endif
  14. /*
  15. * Smell & Blood system
  16. *
  17. * Smell and blood trails decay as time passes.
  18. *
  19. * Decay Rate Maximum Strength Decay Time: Min Max (for biggest volume)
  20. *
  21. * Smell 1 per turn 31 31 31
  22. * Blood 1 every 1-3 turns 7 7 21
  23. *
  24. * Smell has a much finer resolution so that creatures which track by smell
  25. * can do so effectively.
  26. */
  27. /*
  28. * Time for some crazy-ass macros!
  29. * The smell byte is spit as follows:
  30. * O \
  31. * O \
  32. * O \ Smell
  33. * O / Strength (only on ground)
  34. * O /
  35. * O /
  36. * O > Type of blood on roof
  37. * O > Type of smell/blood on ground
  38. *
  39. * The blood byte is split as follows:
  40. * O \
  41. * O > Blood quantity on roof
  42. * O /
  43. * O \
  44. * O > Blood quantity on ground
  45. * O /
  46. * O \ Blood decay
  47. * O / time (roof and ground decay together)
  48. */
  49. /*
  50. * In these defines,
  51. * s indicates the smell byte, b indicates the blood byte
  52. */
  53. // LUT for which graphic to use based on strength
  54. // 0 1, 2, 3, 4, 5, 6, 7
  55. UINT8 ubBloodGraphicLUT [ ] = { 3, 3, 2, 2, 1, 1, 0, 0 };
  56. #define SMELL_STRENGTH_MAX 63
  57. #define BLOOD_STRENGTH_MAX 7
  58. #define BLOOD_DELAY_MAX 3
  59. #define SMELL_TYPE_BITS( s ) (s & 0x03)
  60. #define BLOOD_ROOF_TYPE( s ) (s & 0x02)
  61. #define BLOOD_FLOOR_TYPE( s ) (s & 0x01)
  62. #define BLOOD_ROOF_STRENGTH( b ) (b & 0xE0)
  63. #define BLOOD_FLOOR_STRENGTH( b ) ( (b & 0x1C) >> 2 )
  64. #define BLOOD_DELAY_TIME( b ) (b & 0x03)
  65. #define NO_BLOOD_STRENGTH( b ) ((b & 0xFC) == 0)
  66. #define DECAY_SMELL_STRENGTH( s ) \
  67. { \
  68. UINT8 ubStrength = SMELL_STRENGTH( (s) ); \
  69. ubStrength--; \
  70. ubStrength = ubStrength << SMELL_TYPE_NUM_BITS; \
  71. (s) = SMELL_TYPE_BITS( (s) ) | ubStrength; \
  72. }
  73. // s = smell byte
  74. // ns = new strength
  75. // ntf = new type on floor
  76. // Note that the first part of the macro is designed to
  77. // preserve the type value for the blood on the roof
  78. #define SET_SMELL( s, ns, ntf ) \
  79. { \
  80. (s) = (BLOOD_ROOF_TYPE( s )) | SMELL_TYPE( ntf ) | (ns << SMELL_TYPE_NUM_BITS); \
  81. }
  82. #define DECAY_BLOOD_DELAY_TIME( b ) \
  83. { \
  84. (b)--; \
  85. }
  86. #define SET_BLOOD_FLOOR_STRENGTH( b, nb ) \
  87. { \
  88. (b) = ( (nb) << 2 ) | ( (b) & 0xE3); \
  89. }
  90. #define SET_BLOOD_ROOF_STRENGTH( b, nb ) \
  91. { \
  92. (b) = BLOOD_FLOOR_STRENGTH( (nb) ) << 5 | ( (b) & 0x1F); \
  93. }
  94. #define DECAY_BLOOD_FLOOR_STRENGTH( b ) \
  95. { \
  96. UINT8 ubFloorStrength; \
  97. ubFloorStrength = BLOOD_FLOOR_STRENGTH( (b) ); \
  98. ubFloorStrength--; \
  99. SET_BLOOD_FLOOR_STRENGTH( b, ubFloorStrength ); \
  100. }
  101. #define DECAY_BLOOD_ROOF_STRENGTH( b ) \
  102. { \
  103. UINT8 ubRoofStrength; \
  104. ubRoofStrength = BLOOD_ROOF_STRENGTH( (b) ); \
  105. ubRoofStrength--; \
  106. SET_BLOOD_FLOOR_STRENGTH( b, ubRoofStrength ); \
  107. }
  108. #define SET_BLOOD_DELAY_TIME( b ) \
  109. { \
  110. (b) = BLOOD_DELAY_TIME( (UINT8) Random( BLOOD_DELAY_MAX ) + 1 ) | (b & 0xFC); \
  111. }
  112. #define SET_BLOOD_FLOOR_TYPE( s, ntg ) \
  113. { \
  114. (s) = BLOOD_FLOOR_TYPE( ntg ) | (s & 0xFE); \
  115. }
  116. #define SET_BLOOD_ROOF_TYPE( s, ntr ) \
  117. { \
  118. (s) = BLOOD_ROOF_TYPE( ntr ) | (s & 0xFD); \
  119. }
  120. void RemoveBlood( INT16 sGridNo, INT8 bLevel )
  121. {
  122. gpWorldLevelData[ sGridNo ].ubBloodInfo = 0;
  123. gpWorldLevelData[ sGridNo ].uiFlags |= MAPELEMENT_REEVALUATEBLOOD;
  124. UpdateBloodGraphics( sGridNo, bLevel );
  125. }
  126. void DecaySmells( void )
  127. {
  128. UINT32 uiLoop;
  129. MAP_ELEMENT * pMapElement;
  130. //return;
  131. for (uiLoop = 0, pMapElement = gpWorldLevelData; uiLoop < WORLD_MAX; uiLoop++, pMapElement++)
  132. {
  133. if (pMapElement->ubSmellInfo)
  134. {
  135. // decay smell strength!
  136. DECAY_SMELL_STRENGTH( pMapElement->ubSmellInfo );
  137. // if the strength left is 0, wipe the whole byte to clear the type
  138. if (SMELL_STRENGTH( pMapElement->ubSmellInfo ) == 0)
  139. {
  140. pMapElement->ubSmellInfo = 0;
  141. }
  142. }
  143. }
  144. }
  145. void DecayBlood()
  146. {
  147. UINT32 uiLoop;
  148. MAP_ELEMENT * pMapElement;
  149. for (uiLoop = 0, pMapElement = gpWorldLevelData; uiLoop < WORLD_MAX; uiLoop++, pMapElement++)
  150. {
  151. if (pMapElement->ubBloodInfo)
  152. {
  153. // delay blood timer!
  154. DECAY_BLOOD_DELAY_TIME( pMapElement->ubBloodInfo );
  155. if (BLOOD_DELAY_TIME( pMapElement->ubBloodInfo ) == 0)
  156. {
  157. // Set re-evaluate flag
  158. pMapElement->uiFlags |= MAPELEMENT_REEVALUATEBLOOD;
  159. // reduce the floor blood strength if it is above zero
  160. if (BLOOD_FLOOR_STRENGTH( pMapElement->ubBloodInfo ) > 0)
  161. {
  162. DECAY_BLOOD_FLOOR_STRENGTH( pMapElement->ubBloodInfo )
  163. if (BLOOD_FLOOR_STRENGTH( pMapElement->ubBloodInfo ) == 0)
  164. {
  165. // delete the blood graphic on the floor!
  166. // then
  167. if (NO_BLOOD_STRENGTH( pMapElement->ubBloodInfo ))
  168. {
  169. // wipe the whole byte to zero
  170. pMapElement->ubBloodInfo = 0;
  171. }
  172. }
  173. }
  174. // reduce the roof blood strength if it is above zero
  175. if (BLOOD_ROOF_STRENGTH( pMapElement->ubBloodInfo ) > 0)
  176. {
  177. DECAY_BLOOD_ROOF_STRENGTH( pMapElement->ubBloodInfo )
  178. if (BLOOD_ROOF_STRENGTH( pMapElement->ubBloodInfo ) == 0)
  179. {
  180. // delete the blood graphic on the roof!
  181. if (NO_BLOOD_STRENGTH( pMapElement->ubBloodInfo ))
  182. {
  183. // wipe the whole byte to zero
  184. pMapElement->ubBloodInfo = 0;
  185. }
  186. }
  187. }
  188. // if any blood remaining, reset time
  189. if ( pMapElement->ubBloodInfo )
  190. {
  191. SET_BLOOD_DELAY_TIME( pMapElement->ubBloodInfo );
  192. }
  193. }
  194. // end of blood handling
  195. }
  196. // now go on to the next gridno
  197. }
  198. }
  199. void DecayBloodAndSmells( UINT32 uiTime )
  200. {
  201. UINT32 uiCheckTime;
  202. if ( !gfWorldLoaded )
  203. {
  204. return;
  205. }
  206. // period between checks, in game seconds
  207. switch( giTimeCompressMode )
  208. {
  209. // in time compression, let this happen every 5 REAL seconds
  210. case TIME_COMPRESS_5MINS: // rate of 300 seconds per real second
  211. uiCheckTime = 5 * 300;
  212. break;
  213. case TIME_COMPRESS_30MINS: // rate of 1800 seconds per real second
  214. uiCheckTime = 5 * 1800;
  215. break;
  216. case TIME_COMPRESS_60MINS: // rate of 3600 seconds per real second
  217. case TIME_SUPER_COMPRESS: // should not be used but just in frigging case...
  218. uiCheckTime = 5 * 3600;
  219. break;
  220. default: // not compressing
  221. uiCheckTime = 100;
  222. break;
  223. }
  224. // ok so "uiDecayBloodLastUpdate" is a bit of a misnomer now
  225. if ( ( uiTime - gTacticalStatus.uiDecayBloodLastUpdate ) > uiCheckTime )
  226. {
  227. gTacticalStatus.uiDecayBloodLastUpdate = uiTime;
  228. DecayBlood();
  229. DecaySmells();
  230. }
  231. }
  232. void DropSmell( SOLDIERTYPE * pSoldier )
  233. {
  234. MAP_ELEMENT * pMapElement;
  235. UINT8 ubOldSmell;
  236. UINT8 ubOldStrength;
  237. UINT8 ubSmell;
  238. UINT8 ubStrength;
  239. /*
  240. * Here we are creating a new smell on the ground. If there is blood in
  241. * the tile, it overrides dropping smells of any type
  242. */
  243. if (pSoldier->bLevel == 0)
  244. {
  245. pMapElement = &(gpWorldLevelData[pSoldier->sGridNo]);
  246. if (pMapElement->ubBloodInfo)
  247. {
  248. // blood here, don't drop any smell
  249. return;
  250. }
  251. if (pSoldier->bNormalSmell > pSoldier->bMonsterSmell)
  252. {
  253. ubStrength = pSoldier->bNormalSmell - pSoldier->bMonsterSmell;
  254. ubSmell = HUMAN;
  255. }
  256. else
  257. {
  258. ubStrength = pSoldier->bMonsterSmell - pSoldier->bNormalSmell;
  259. if (ubStrength == 0)
  260. {
  261. // don't drop any smell
  262. return;
  263. }
  264. ubSmell = CREATURE_ON_FLOOR;
  265. }
  266. if (pMapElement->ubSmellInfo)
  267. {
  268. // smell already exists here; check to see if it's the same or not
  269. ubOldSmell = SMELL_TYPE( pMapElement->ubSmellInfo );
  270. ubOldStrength = SMELL_STRENGTH( pMapElement->ubSmellInfo );
  271. if (ubOldSmell == ubSmell)
  272. {
  273. // same smell; increase the strength to the bigger of the two strengths,
  274. // plus 1/5 of the smaller
  275. ubStrength = __max( ubStrength, ubOldStrength ) + __min( ubStrength, ubOldStrength ) / 5;
  276. ubStrength = __max( ubStrength, SMELL_STRENGTH_MAX );
  277. }
  278. else
  279. {
  280. // different smell; we muddy the smell by reducing the smell strength
  281. if (ubOldStrength > ubStrength)
  282. {
  283. ubOldStrength -= ubStrength / 3;
  284. SET_SMELL( pMapElement->ubSmellInfo, ubOldStrength, ubOldSmell );
  285. }
  286. else
  287. {
  288. ubStrength -= ubOldStrength / 3;
  289. if (ubStrength > 0)
  290. {
  291. SET_SMELL( pMapElement->ubSmellInfo, ubStrength, ubSmell );
  292. }
  293. else
  294. {
  295. // smell reduced to 0 - wipe all info on it!
  296. pMapElement->ubSmellInfo = 0;
  297. }
  298. }
  299. }
  300. }
  301. else
  302. {
  303. // the simple case, dropping a smell in a location where there is none
  304. SET_SMELL( pMapElement->ubSmellInfo, ubStrength, ubSmell );
  305. }
  306. }
  307. // otherwise skip dropping smell
  308. }
  309. void InternalDropBlood( INT16 sGridNo, INT8 bLevel, UINT8 ubType, UINT8 ubStrength, INT8 bVisible )
  310. {
  311. MAP_ELEMENT * pMapElement;
  312. UINT8 ubOldStrength=0;
  313. UINT8 ubNewStrength=0;
  314. /*
  315. * Dropping some blood;
  316. * We can check the type of blood by consulting the type in the smell byte
  317. */
  318. // If we are in water...
  319. if ( GetTerrainType( sGridNo ) == DEEP_WATER || GetTerrainType( sGridNo ) == LOW_WATER || GetTerrainType( sGridNo ) == MED_WATER )
  320. {
  321. return;
  322. }
  323. // ATE: Send warning if dropping blood nowhere....
  324. if ( sGridNo == NOWHERE )
  325. {
  326. #ifdef JA2BETAVERSION
  327. ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_TESTVERSION, L"Attempting to drop blood NOWHERE" );
  328. #endif
  329. return;
  330. }
  331. // ensure max strength is okay
  332. ubStrength = __min( ubStrength, BLOOD_STRENGTH_MAX );
  333. pMapElement = &(gpWorldLevelData[ sGridNo ]);
  334. if ( bLevel == 0 )
  335. {
  336. // dropping blood on ground
  337. ubOldStrength = BLOOD_FLOOR_STRENGTH( pMapElement->ubBloodInfo );
  338. if (ubOldStrength > 0)
  339. {
  340. // blood already there... we'll leave the decay time as it is
  341. if (BLOOD_FLOOR_TYPE( pMapElement->ubBloodInfo ) == ubType)
  342. {
  343. // combine blood strengths!
  344. ubNewStrength = __min( ( ubOldStrength + ubStrength ), BLOOD_STRENGTH_MAX );
  345. SET_BLOOD_FLOOR_STRENGTH( pMapElement->ubBloodInfo, ubNewStrength );
  346. }
  347. else
  348. {
  349. // replace the existing blood if more is being dropped than exists
  350. if (ubStrength > ubOldStrength )
  351. {
  352. // replace!
  353. SET_BLOOD_FLOOR_STRENGTH( pMapElement->ubBloodInfo, ubStrength );
  354. }
  355. // else we don't drop anything at all
  356. }
  357. }
  358. else
  359. {
  360. // no blood on the ground yet, so drop this amount!
  361. // set decay time
  362. SET_BLOOD_DELAY_TIME( pMapElement->ubBloodInfo );
  363. SET_BLOOD_FLOOR_STRENGTH( pMapElement->ubBloodInfo, ubStrength );
  364. // NB blood floor type stored in smell byte!
  365. SET_BLOOD_FLOOR_TYPE( pMapElement->ubSmellInfo, ubType );
  366. }
  367. }
  368. else
  369. {
  370. // dropping blood on roof
  371. ubOldStrength = BLOOD_ROOF_STRENGTH( pMapElement->ubBloodInfo );
  372. if (ubOldStrength > 0)
  373. {
  374. // blood already there... we'll leave the decay time as it is
  375. if (BLOOD_ROOF_TYPE( pMapElement->ubSmellInfo ) == ubType)
  376. {
  377. // combine blood strengths!
  378. ubNewStrength = __max( ubOldStrength, ubStrength ) + 1;
  379. // make sure the strength is legal
  380. ubNewStrength = __max( ubNewStrength, BLOOD_STRENGTH_MAX );
  381. SET_BLOOD_ROOF_STRENGTH( pMapElement->ubBloodInfo, ubNewStrength );
  382. }
  383. else
  384. {
  385. // replace the existing blood if more is being dropped than exists
  386. if (ubStrength > ubOldStrength )
  387. {
  388. // replace!
  389. SET_BLOOD_ROOF_STRENGTH( pMapElement->ubBloodInfo, ubStrength );
  390. }
  391. // else we don't drop anything at all
  392. }
  393. }
  394. else
  395. {
  396. // no blood on the roof yet, so drop this amount!
  397. // set decay time
  398. SET_BLOOD_DELAY_TIME( pMapElement->ubBloodInfo );
  399. SET_BLOOD_ROOF_STRENGTH( pMapElement->ubBloodInfo, ubNewStrength );
  400. SET_BLOOD_ROOF_TYPE( pMapElement->ubSmellInfo, ubType );
  401. }
  402. }
  403. // Turn on flag...
  404. pMapElement->uiFlags |= MAPELEMENT_REEVALUATEBLOOD;
  405. if ( bVisible != -1 )
  406. {
  407. UpdateBloodGraphics( sGridNo, bLevel );
  408. }
  409. }
  410. void DropBlood( SOLDIERTYPE * pSoldier, UINT8 ubStrength, INT8 bVisible )
  411. {
  412. UINT8 ubType;
  413. UINT8 ubOldStrength=0;
  414. UINT8 ubNewStrength=0;
  415. /*
  416. * Dropping some blood;
  417. * We can check the type of blood by consulting the type in the smell byte
  418. */
  419. // figure out the type of blood that we're dropping
  420. if ( pSoldier->uiStatusFlags & SOLDIER_MONSTER )
  421. {
  422. if ( pSoldier->bLevel == 0 )
  423. {
  424. ubType = CREATURE_ON_FLOOR;
  425. }
  426. else
  427. {
  428. ubType = CREATURE_ON_ROOF;
  429. }
  430. }
  431. else
  432. {
  433. ubType = 0;
  434. }
  435. InternalDropBlood( pSoldier->sGridNo, pSoldier->bLevel, ubType, ubStrength, bVisible );
  436. }
  437. void UpdateBloodGraphics( INT16 sGridNo, INT8 bLevel )
  438. {
  439. MAP_ELEMENT * pMapElement;
  440. INT8 bValue;
  441. UINT16 usIndex, usNewIndex;
  442. // OK, based on level, type, display graphics for blood
  443. pMapElement = &(gpWorldLevelData[ sGridNo ]);
  444. // CHECK FOR BLOOD OPTION
  445. if ( !gGameSettings.fOptions[ TOPTION_BLOOD_N_GORE ] )
  446. {
  447. return;
  448. }
  449. if ( pMapElement->uiFlags & MAPELEMENT_REEVALUATEBLOOD )
  450. {
  451. // Turn off flag!
  452. pMapElement->uiFlags &= ( ~MAPELEMENT_REEVALUATEBLOOD );
  453. // Ground
  454. if ( bLevel == 0 )
  455. {
  456. bValue = BLOOD_FLOOR_STRENGTH( pMapElement->ubBloodInfo );
  457. // OK, remove tile graphic if one exists....
  458. if ( TypeRangeExistsInObjectLayer( sGridNo, HUMANBLOOD, CREATUREBLOOD, &usIndex ) )
  459. {
  460. //This has been removed and it is handled by the ubBloodInfo level when restoring a saved game.
  461. //Set a flag indicating that the following changes are to go the the maps temp file
  462. //ApplyMapChangesToMapTempFile( TRUE );
  463. // Remove
  464. RemoveObject( sGridNo, usIndex );
  465. //ApplyMapChangesToMapTempFile( FALSE );
  466. }
  467. // OK, pick new one. based on strength and randomness
  468. if ( bValue > 0 )
  469. {
  470. usIndex = (UINT16)( ( Random( 4 ) * 4 ) + ubBloodGraphicLUT[ bValue ] );
  471. if ( BLOOD_FLOOR_TYPE( pMapElement->ubSmellInfo ) == 0 )
  472. {
  473. GetTileIndexFromTypeSubIndex( HUMANBLOOD, (UINT16)(usIndex + 1), &usNewIndex );
  474. }
  475. else
  476. {
  477. GetTileIndexFromTypeSubIndex( CREATUREBLOOD, (UINT16)(usIndex + 1), &usNewIndex );
  478. }
  479. //This has been removed and it is handled by the ubBloodInfo level when restoring a saved game.
  480. //Set a flag indicating that the following changes are to go the the maps temp file
  481. //ApplyMapChangesToMapTempFile( TRUE );
  482. // Add!
  483. AddObjectToHead( sGridNo, usNewIndex );
  484. //ApplyMapChangesToMapTempFile( FALSE );
  485. // Update rendering!
  486. pMapElement->uiFlags|=MAPELEMENT_REDRAW;
  487. SetRenderFlags(RENDER_FLAG_MARKED);
  488. }
  489. }
  490. // Roof
  491. else
  492. {
  493. }
  494. }
  495. }