123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577 |
- #ifdef PRECOMPILEDHEADERS
- #include "TileEngine All.h"
- #else
- #include "worlddef.h"
- #include "Random.h"
- #include "Smell.h"
- #include "worldman.h"
- #include "renderworld.h"
- #include "SaveLoadMap.h"
- #include "gamesettings.h"
- #include "message.h"
- #include "Isometric utils.h"
- #endif
- /*
- * Smell & Blood system
- *
- * Smell and blood trails decay as time passes.
- *
- * Decay Rate Maximum Strength Decay Time: Min Max (for biggest volume)
- *
- * Smell 1 per turn 31 31 31
- * Blood 1 every 1-3 turns 7 7 21
- *
- * Smell has a much finer resolution so that creatures which track by smell
- * can do so effectively.
- */
- /*
- * Time for some crazy-ass macros!
- * The smell byte is spit as follows:
- * O \
- * O \
- * O \ Smell
- * O / Strength (only on ground)
- * O /
- * O /
- * O > Type of blood on roof
- * O > Type of smell/blood on ground
- *
- * The blood byte is split as follows:
- * O \
- * O > Blood quantity on roof
- * O /
- * O \
- * O > Blood quantity on ground
- * O /
- * O \ Blood decay
- * O / time (roof and ground decay together)
- */
- /*
- * In these defines,
- * s indicates the smell byte, b indicates the blood byte
- */
- // LUT for which graphic to use based on strength
- // 0 1, 2, 3, 4, 5, 6, 7
- UINT8 ubBloodGraphicLUT [ ] = { 3, 3, 2, 2, 1, 1, 0, 0 };
- #define SMELL_STRENGTH_MAX 63
- #define BLOOD_STRENGTH_MAX 7
- #define BLOOD_DELAY_MAX 3
- #define SMELL_TYPE_BITS( s ) (s & 0x03)
- #define BLOOD_ROOF_TYPE( s ) (s & 0x02)
- #define BLOOD_FLOOR_TYPE( s ) (s & 0x01)
- #define BLOOD_ROOF_STRENGTH( b ) (b & 0xE0)
- #define BLOOD_FLOOR_STRENGTH( b ) ( (b & 0x1C) >> 2 )
- #define BLOOD_DELAY_TIME( b ) (b & 0x03)
- #define NO_BLOOD_STRENGTH( b ) ((b & 0xFC) == 0)
- #define DECAY_SMELL_STRENGTH( s ) \
- { \
- UINT8 ubStrength = SMELL_STRENGTH( (s) ); \
- ubStrength--; \
- ubStrength = ubStrength << SMELL_TYPE_NUM_BITS; \
- (s) = SMELL_TYPE_BITS( (s) ) | ubStrength; \
- }
- // s = smell byte
- // ns = new strength
- // ntf = new type on floor
- // Note that the first part of the macro is designed to
- // preserve the type value for the blood on the roof
- #define SET_SMELL( s, ns, ntf ) \
- { \
- (s) = (BLOOD_ROOF_TYPE( s )) | SMELL_TYPE( ntf ) | (ns << SMELL_TYPE_NUM_BITS); \
- }
- #define DECAY_BLOOD_DELAY_TIME( b ) \
- { \
- (b)--; \
- }
- #define SET_BLOOD_FLOOR_STRENGTH( b, nb ) \
- { \
- (b) = ( (nb) << 2 ) | ( (b) & 0xE3); \
- }
- #define SET_BLOOD_ROOF_STRENGTH( b, nb ) \
- { \
- (b) = BLOOD_FLOOR_STRENGTH( (nb) ) << 5 | ( (b) & 0x1F); \
- }
- #define DECAY_BLOOD_FLOOR_STRENGTH( b ) \
- { \
- UINT8 ubFloorStrength; \
- ubFloorStrength = BLOOD_FLOOR_STRENGTH( (b) ); \
- ubFloorStrength--; \
- SET_BLOOD_FLOOR_STRENGTH( b, ubFloorStrength ); \
- }
- #define DECAY_BLOOD_ROOF_STRENGTH( b ) \
- { \
- UINT8 ubRoofStrength; \
- ubRoofStrength = BLOOD_ROOF_STRENGTH( (b) ); \
- ubRoofStrength--; \
- SET_BLOOD_FLOOR_STRENGTH( b, ubRoofStrength ); \
- }
- #define SET_BLOOD_DELAY_TIME( b ) \
- { \
- (b) = BLOOD_DELAY_TIME( (UINT8) Random( BLOOD_DELAY_MAX ) + 1 ) | (b & 0xFC); \
- }
- #define SET_BLOOD_FLOOR_TYPE( s, ntg ) \
- { \
- (s) = BLOOD_FLOOR_TYPE( ntg ) | (s & 0xFE); \
- }
- #define SET_BLOOD_ROOF_TYPE( s, ntr ) \
- { \
- (s) = BLOOD_ROOF_TYPE( ntr ) | (s & 0xFD); \
- }
- void RemoveBlood( INT16 sGridNo, INT8 bLevel )
- {
- gpWorldLevelData[ sGridNo ].ubBloodInfo = 0;
- gpWorldLevelData[ sGridNo ].uiFlags |= MAPELEMENT_REEVALUATEBLOOD;
- UpdateBloodGraphics( sGridNo, bLevel );
- }
- void DecaySmells( void )
- {
- UINT32 uiLoop;
- MAP_ELEMENT * pMapElement;
- //return;
- for (uiLoop = 0, pMapElement = gpWorldLevelData; uiLoop < WORLD_MAX; uiLoop++, pMapElement++)
- {
- if (pMapElement->ubSmellInfo)
- {
- // decay smell strength!
- DECAY_SMELL_STRENGTH( pMapElement->ubSmellInfo );
- // if the strength left is 0, wipe the whole byte to clear the type
- if (SMELL_STRENGTH( pMapElement->ubSmellInfo ) == 0)
- {
- pMapElement->ubSmellInfo = 0;
- }
- }
- }
- }
- void DecayBlood()
- {
- UINT32 uiLoop;
- MAP_ELEMENT * pMapElement;
- for (uiLoop = 0, pMapElement = gpWorldLevelData; uiLoop < WORLD_MAX; uiLoop++, pMapElement++)
- {
- if (pMapElement->ubBloodInfo)
- {
- // delay blood timer!
- DECAY_BLOOD_DELAY_TIME( pMapElement->ubBloodInfo );
- if (BLOOD_DELAY_TIME( pMapElement->ubBloodInfo ) == 0)
- {
- // Set re-evaluate flag
- pMapElement->uiFlags |= MAPELEMENT_REEVALUATEBLOOD;
- // reduce the floor blood strength if it is above zero
- if (BLOOD_FLOOR_STRENGTH( pMapElement->ubBloodInfo ) > 0)
- {
- DECAY_BLOOD_FLOOR_STRENGTH( pMapElement->ubBloodInfo )
- if (BLOOD_FLOOR_STRENGTH( pMapElement->ubBloodInfo ) == 0)
- {
- // delete the blood graphic on the floor!
- // then
- if (NO_BLOOD_STRENGTH( pMapElement->ubBloodInfo ))
- {
- // wipe the whole byte to zero
- pMapElement->ubBloodInfo = 0;
- }
- }
- }
- // reduce the roof blood strength if it is above zero
- if (BLOOD_ROOF_STRENGTH( pMapElement->ubBloodInfo ) > 0)
- {
- DECAY_BLOOD_ROOF_STRENGTH( pMapElement->ubBloodInfo )
- if (BLOOD_ROOF_STRENGTH( pMapElement->ubBloodInfo ) == 0)
- {
- // delete the blood graphic on the roof!
- if (NO_BLOOD_STRENGTH( pMapElement->ubBloodInfo ))
- {
- // wipe the whole byte to zero
- pMapElement->ubBloodInfo = 0;
- }
- }
- }
- // if any blood remaining, reset time
- if ( pMapElement->ubBloodInfo )
- {
- SET_BLOOD_DELAY_TIME( pMapElement->ubBloodInfo );
- }
- }
- // end of blood handling
- }
- // now go on to the next gridno
- }
- }
- void DecayBloodAndSmells( UINT32 uiTime )
- {
- UINT32 uiCheckTime;
- if ( !gfWorldLoaded )
- {
- return;
- }
-
- // period between checks, in game seconds
- switch( giTimeCompressMode )
- {
- // in time compression, let this happen every 5 REAL seconds
- case TIME_COMPRESS_5MINS: // rate of 300 seconds per real second
- uiCheckTime = 5 * 300;
- break;
- case TIME_COMPRESS_30MINS: // rate of 1800 seconds per real second
- uiCheckTime = 5 * 1800;
- break;
- case TIME_COMPRESS_60MINS: // rate of 3600 seconds per real second
- case TIME_SUPER_COMPRESS: // should not be used but just in frigging case...
- uiCheckTime = 5 * 3600;
- break;
- default: // not compressing
- uiCheckTime = 100;
- break;
- }
- // ok so "uiDecayBloodLastUpdate" is a bit of a misnomer now
- if ( ( uiTime - gTacticalStatus.uiDecayBloodLastUpdate ) > uiCheckTime )
- {
- gTacticalStatus.uiDecayBloodLastUpdate = uiTime;
- DecayBlood();
- DecaySmells();
- }
- }
- void DropSmell( SOLDIERTYPE * pSoldier )
- {
- MAP_ELEMENT * pMapElement;
- UINT8 ubOldSmell;
- UINT8 ubOldStrength;
- UINT8 ubSmell;
- UINT8 ubStrength;
- /*
- * Here we are creating a new smell on the ground. If there is blood in
- * the tile, it overrides dropping smells of any type
- */
- if (pSoldier->bLevel == 0)
- {
- pMapElement = &(gpWorldLevelData[pSoldier->sGridNo]);
- if (pMapElement->ubBloodInfo)
- {
- // blood here, don't drop any smell
- return;
- }
- if (pSoldier->bNormalSmell > pSoldier->bMonsterSmell)
- {
- ubStrength = pSoldier->bNormalSmell - pSoldier->bMonsterSmell;
- ubSmell = HUMAN;
- }
- else
- {
- ubStrength = pSoldier->bMonsterSmell - pSoldier->bNormalSmell;
- if (ubStrength == 0)
- {
- // don't drop any smell
- return;
- }
- ubSmell = CREATURE_ON_FLOOR;
- }
- if (pMapElement->ubSmellInfo)
- {
- // smell already exists here; check to see if it's the same or not
- ubOldSmell = SMELL_TYPE( pMapElement->ubSmellInfo );
- ubOldStrength = SMELL_STRENGTH( pMapElement->ubSmellInfo );
- if (ubOldSmell == ubSmell)
- {
- // same smell; increase the strength to the bigger of the two strengths,
- // plus 1/5 of the smaller
- ubStrength = __max( ubStrength, ubOldStrength ) + __min( ubStrength, ubOldStrength ) / 5;
- ubStrength = __max( ubStrength, SMELL_STRENGTH_MAX );
- }
- else
- {
- // different smell; we muddy the smell by reducing the smell strength
- if (ubOldStrength > ubStrength)
- {
- ubOldStrength -= ubStrength / 3;
- SET_SMELL( pMapElement->ubSmellInfo, ubOldStrength, ubOldSmell );
- }
- else
- {
- ubStrength -= ubOldStrength / 3;
- if (ubStrength > 0)
- {
- SET_SMELL( pMapElement->ubSmellInfo, ubStrength, ubSmell );
- }
- else
- {
- // smell reduced to 0 - wipe all info on it!
- pMapElement->ubSmellInfo = 0;
- }
- }
- }
- }
- else
- {
- // the simple case, dropping a smell in a location where there is none
- SET_SMELL( pMapElement->ubSmellInfo, ubStrength, ubSmell );
- }
- }
- // otherwise skip dropping smell
- }
- void InternalDropBlood( INT16 sGridNo, INT8 bLevel, UINT8 ubType, UINT8 ubStrength, INT8 bVisible )
- {
- MAP_ELEMENT * pMapElement;
- UINT8 ubOldStrength=0;
- UINT8 ubNewStrength=0;
-
- /*
- * Dropping some blood;
- * We can check the type of blood by consulting the type in the smell byte
- */
- // If we are in water...
- if ( GetTerrainType( sGridNo ) == DEEP_WATER || GetTerrainType( sGridNo ) == LOW_WATER || GetTerrainType( sGridNo ) == MED_WATER )
- {
- return;
- }
- // ATE: Send warning if dropping blood nowhere....
- if ( sGridNo == NOWHERE )
- {
- #ifdef JA2BETAVERSION
- ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_TESTVERSION, L"Attempting to drop blood NOWHERE" );
- #endif
- return;
- }
- // ensure max strength is okay
- ubStrength = __min( ubStrength, BLOOD_STRENGTH_MAX );
- pMapElement = &(gpWorldLevelData[ sGridNo ]);
- if ( bLevel == 0 )
- {
- // dropping blood on ground
- ubOldStrength = BLOOD_FLOOR_STRENGTH( pMapElement->ubBloodInfo );
- if (ubOldStrength > 0)
- {
- // blood already there... we'll leave the decay time as it is
- if (BLOOD_FLOOR_TYPE( pMapElement->ubBloodInfo ) == ubType)
- {
- // combine blood strengths!
- ubNewStrength = __min( ( ubOldStrength + ubStrength ), BLOOD_STRENGTH_MAX );
- SET_BLOOD_FLOOR_STRENGTH( pMapElement->ubBloodInfo, ubNewStrength );
- }
- else
- {
- // replace the existing blood if more is being dropped than exists
- if (ubStrength > ubOldStrength )
- {
- // replace!
- SET_BLOOD_FLOOR_STRENGTH( pMapElement->ubBloodInfo, ubStrength );
- }
- // else we don't drop anything at all
- }
- }
- else
- {
- // no blood on the ground yet, so drop this amount!
- // set decay time
- SET_BLOOD_DELAY_TIME( pMapElement->ubBloodInfo );
- SET_BLOOD_FLOOR_STRENGTH( pMapElement->ubBloodInfo, ubStrength );
- // NB blood floor type stored in smell byte!
- SET_BLOOD_FLOOR_TYPE( pMapElement->ubSmellInfo, ubType );
- }
- }
- else
- {
- // dropping blood on roof
- ubOldStrength = BLOOD_ROOF_STRENGTH( pMapElement->ubBloodInfo );
- if (ubOldStrength > 0)
- {
- // blood already there... we'll leave the decay time as it is
- if (BLOOD_ROOF_TYPE( pMapElement->ubSmellInfo ) == ubType)
- {
- // combine blood strengths!
- ubNewStrength = __max( ubOldStrength, ubStrength ) + 1;
- // make sure the strength is legal
- ubNewStrength = __max( ubNewStrength, BLOOD_STRENGTH_MAX );
- SET_BLOOD_ROOF_STRENGTH( pMapElement->ubBloodInfo, ubNewStrength );
- }
- else
- {
- // replace the existing blood if more is being dropped than exists
- if (ubStrength > ubOldStrength )
- {
- // replace!
- SET_BLOOD_ROOF_STRENGTH( pMapElement->ubBloodInfo, ubStrength );
- }
- // else we don't drop anything at all
- }
- }
- else
- {
- // no blood on the roof yet, so drop this amount!
- // set decay time
- SET_BLOOD_DELAY_TIME( pMapElement->ubBloodInfo );
- SET_BLOOD_ROOF_STRENGTH( pMapElement->ubBloodInfo, ubNewStrength );
- SET_BLOOD_ROOF_TYPE( pMapElement->ubSmellInfo, ubType );
- }
- }
- // Turn on flag...
- pMapElement->uiFlags |= MAPELEMENT_REEVALUATEBLOOD;
- if ( bVisible != -1 )
- {
- UpdateBloodGraphics( sGridNo, bLevel );
- }
- }
- void DropBlood( SOLDIERTYPE * pSoldier, UINT8 ubStrength, INT8 bVisible )
- {
- UINT8 ubType;
- UINT8 ubOldStrength=0;
- UINT8 ubNewStrength=0;
-
- /*
- * Dropping some blood;
- * We can check the type of blood by consulting the type in the smell byte
- */
- // figure out the type of blood that we're dropping
- if ( pSoldier->uiStatusFlags & SOLDIER_MONSTER )
- {
- if ( pSoldier->bLevel == 0 )
- {
- ubType = CREATURE_ON_FLOOR;
- }
- else
- {
- ubType = CREATURE_ON_ROOF;
- }
- }
- else
- {
- ubType = 0;
- }
- InternalDropBlood( pSoldier->sGridNo, pSoldier->bLevel, ubType, ubStrength, bVisible );
- }
- void UpdateBloodGraphics( INT16 sGridNo, INT8 bLevel )
- {
- MAP_ELEMENT * pMapElement;
- INT8 bValue;
- UINT16 usIndex, usNewIndex;
-
- // OK, based on level, type, display graphics for blood
- pMapElement = &(gpWorldLevelData[ sGridNo ]);
- // CHECK FOR BLOOD OPTION
- if ( !gGameSettings.fOptions[ TOPTION_BLOOD_N_GORE ] )
- {
- return;
- }
- if ( pMapElement->uiFlags & MAPELEMENT_REEVALUATEBLOOD )
- {
- // Turn off flag!
- pMapElement->uiFlags &= ( ~MAPELEMENT_REEVALUATEBLOOD );
- // Ground
- if ( bLevel == 0 )
- {
- bValue = BLOOD_FLOOR_STRENGTH( pMapElement->ubBloodInfo );
-
- // OK, remove tile graphic if one exists....
- if ( TypeRangeExistsInObjectLayer( sGridNo, HUMANBLOOD, CREATUREBLOOD, &usIndex ) )
- {
- //This has been removed and it is handled by the ubBloodInfo level when restoring a saved game.
- //Set a flag indicating that the following changes are to go the the maps temp file
- //ApplyMapChangesToMapTempFile( TRUE );
- // Remove
- RemoveObject( sGridNo, usIndex );
- //ApplyMapChangesToMapTempFile( FALSE );
- }
- // OK, pick new one. based on strength and randomness
- if ( bValue > 0 )
- {
- usIndex = (UINT16)( ( Random( 4 ) * 4 ) + ubBloodGraphicLUT[ bValue ] );
- if ( BLOOD_FLOOR_TYPE( pMapElement->ubSmellInfo ) == 0 )
- {
- GetTileIndexFromTypeSubIndex( HUMANBLOOD, (UINT16)(usIndex + 1), &usNewIndex );
- }
- else
- {
- GetTileIndexFromTypeSubIndex( CREATUREBLOOD, (UINT16)(usIndex + 1), &usNewIndex );
- }
- //This has been removed and it is handled by the ubBloodInfo level when restoring a saved game.
- //Set a flag indicating that the following changes are to go the the maps temp file
- //ApplyMapChangesToMapTempFile( TRUE );
- // Add!
- AddObjectToHead( sGridNo, usNewIndex );
- //ApplyMapChangesToMapTempFile( FALSE );
- // Update rendering!
- pMapElement->uiFlags|=MAPELEMENT_REDRAW;
- SetRenderFlags(RENDER_FLAG_MARKED);
- }
- }
- // Roof
- else
- {
- }
- }
- }
|