mirror of
https://github.com/1dot13/source.git
synced 2026-07-22 13:40:22 +02:00
- Feature can be completely disabled in .ini as COVER_SYSTEM_ADDITIONAL_TILE_PROPERTIES under Tactical Cover System Settings. - With Additional Tile Properties it's possible to supply all tiles with additional data, just like with .jsd files, except in XML. Files need to have same name as tile's .sti and .jsd files, and be put in \tilesets\AdditionalProperties\, or in specific \tilesets\xx\. - With additional properties it's possible to specify which camo type, and how effectively will work on any tile. Only snow camo will work on snow covered roofs, red carpet in palace won't perfectly cooperate with urban camo anymore, thick grass in the middle of the forest will provide better camouflage than sparse grass near the road. - It's now possible to specify footstep volume and stealth difficulty modifier of any tile. Carpets will absorb noise, tile floors amplify it, twigs laying on the tile will make sneaking harder. Choose your path carefully, taffer. - More infos: http://www.bears-pit.com/board/ubbthreads.php/topics/330942.html#Post330942 git-svn-id: https://ja2svn.mooo.com/source/ja2/trunk/GameSource/ja2_v1.13/Build@7001 3b4a5df2-a311-0410-b5c6-a8a6f20db521
1613 lines
43 KiB
C++
1613 lines
43 KiB
C++
#ifdef PRECOMPILEDHEADERS
|
|
#include "AI All.h"
|
|
#include "DisplayCover.h"
|
|
#include "Interface.h"
|
|
#include "opplist.h"
|
|
#include "_Ja25Englishtext.h"
|
|
//#include "Ja25 Strategic Ai.h"
|
|
#else
|
|
#include "builddefines.h"
|
|
#include "Types.h"
|
|
#include "Isometric Utils.h"
|
|
//#include "Soldier Control.h"
|
|
#include "Overhead.h"
|
|
#include "displaycover.h"
|
|
#include "Font Control.h"
|
|
#include "_Ja25EnglishText.h"
|
|
#include "message.h"
|
|
#include "GameSettings.h"
|
|
#include "renderworld.h"
|
|
#include "Interface.h"
|
|
#include "Debug.h"
|
|
#include "PATHAI.H"
|
|
#include "worldman.h"
|
|
#include "opplist.h"
|
|
#include "los.h"
|
|
#include "weapons.h"
|
|
#include "Game Clock.h"
|
|
#include "Animation Control.h"
|
|
#include "lighting.h"
|
|
#include "Text.h"
|
|
#include "strategicmap.h"
|
|
#include "Render Fun.h"
|
|
// HEADROCK HAM B2.7: Allow calling a CTH approximation function for the CTH display ("F" key)
|
|
#include "UI Cursors.h"
|
|
#include "soldier profile type.h"
|
|
#endif
|
|
|
|
//forward declarations of common classes to eliminate includes
|
|
class OBJECTTYPE;
|
|
class SOLDIERTYPE;
|
|
|
|
//******* Local Defines **************************************************
|
|
|
|
enum COVER_VALUES
|
|
{
|
|
INVALID_COVER=-1,
|
|
NO_COVER=0,
|
|
MIN_COVER=1,
|
|
MED_COVER=2,
|
|
MAX_COVER=3
|
|
};
|
|
|
|
// Flugente: mines display - stuff needs to be here
|
|
enum MINES_VALUES
|
|
{
|
|
MINES_ALL = MAX_COVER + 1,
|
|
MINE_BOMB,
|
|
MINE_WIRE,
|
|
MINE_BOMB_AND_WIRE,
|
|
MINES_NET_1,
|
|
MINES_NET_2,
|
|
MINES_NET_3,
|
|
MINES_NET_4,
|
|
MINES_LVL_1,
|
|
MINES_LVL_2,
|
|
MINES_LVL_3,
|
|
MINES_LVL_4,
|
|
MAX_MINES
|
|
};
|
|
|
|
// Flugente: trait display
|
|
enum TRAIT_VALUES
|
|
{
|
|
TRAIT_ALL = MAX_MINES + 1,
|
|
TRAIT_1,
|
|
TRAIT_2,
|
|
MAX_TRAIT
|
|
};
|
|
|
|
|
|
struct CoverCell
|
|
{
|
|
INT32 sGridNo;
|
|
INT8 bCover;
|
|
BOOLEAN fInverseColor;
|
|
|
|
INT8 bMines;
|
|
INT8 bTrait;
|
|
|
|
CoverCell() : sGridNo(NOWHERE), bCover(INVALID_COVER), fInverseColor(FALSE), bMines(MINES_ALL), bTrait(TRAIT_ALL) {}
|
|
|
|
};
|
|
|
|
const UINT8 animArr[3] = {
|
|
ANIM_PRONE,
|
|
ANIM_CROUCH,
|
|
ANIM_STAND
|
|
};
|
|
|
|
// yea way too big... but some bytes more memory is cheap
|
|
#define COVER_X_CELLS WORLD_COLS_MAX
|
|
#define COVER_Y_CELLS WORLD_ROWS_MAX
|
|
#define COVER_Z_CELLS 2 // roof or no roof
|
|
|
|
//****** Local Variables *********************************************
|
|
|
|
INT16 gsMinCellX, gsMinCellY, gsMaxCellX, gsMaxCellY = -1;
|
|
|
|
CoverCell gCoverViewArea[ COVER_X_CELLS ][ COVER_Y_CELLS ][ COVER_Z_CELLS ];
|
|
|
|
DWORD guiCoverNextUpdateTime = 0;
|
|
|
|
COVER_DRAW_MODE gubDrawMode = COVER_DRAW_OFF;
|
|
|
|
MINES_DRAW_MODE gubDrawModeMine = MINES_DRAW_OFF; // Flugente: mines display
|
|
|
|
TRAIT_DRAW_MODE gubDrawModeTrait = TRAIT_DRAW_OFF; // Flugente: mines display
|
|
|
|
|
|
//******* Local Function Prototypes ***********************************
|
|
|
|
CHAR16* GetTerrainName( const UINT8& ubTerrainType );
|
|
|
|
// anv: additional tile properties
|
|
CHAR16* GetDetailedTerrainName( ADDITIONAL_TILE_PROPERTIES_VALUES zGivenTileProperties );
|
|
|
|
TileDefines GetTileCoverIndex( const INT8& bCover );
|
|
|
|
void AddCoverObjectToWorld( const INT32& sGridNo, const UINT16& usGraphic, const BOOLEAN& fRoof );
|
|
void RemoveCoverObjectFromWorld( const INT32 sGridNo, const UINT16& usGraphic, const BOOLEAN& fRoof );
|
|
|
|
void AddCoverObjectsToViewArea();
|
|
void RemoveCoverObjectsFromViewArea();
|
|
|
|
void RemoveMinesObjectsFromViewArea(); // added by Flugente, has to be declared here
|
|
void RemoveTraitObjectsFromViewArea();
|
|
|
|
void CalculateCover();
|
|
void CalculateCoverForSoldier( SOLDIERTYPE* pForSoldier, const INT32& sTargetGridNo, const BOOLEAN& fRoof, INT8& bCover );
|
|
void CalculateCoverFromSoldier( SOLDIERTYPE* pFromSoldier, const INT32& sTargetGridNo, const BOOLEAN& fRoof, INT8& bCover, SOLDIERTYPE* pToSoldier=NULL );
|
|
|
|
void GetGridNoForViewPort( const INT32& ubX, const INT32& ubY, INT32& sGridNo );
|
|
|
|
BOOLEAN GridNoOnScreenAndAround( const INT32& sGridNo, const UINT8& ubRadius=2 );
|
|
|
|
BOOLEAN IsTheRoofVisible( const INT32& sGridNo );
|
|
BOOLEAN HasAdjTile( const INT32& ubX, const INT32& ubY );
|
|
|
|
//******* Functions **************************************************
|
|
|
|
///BEGIN key binding functions
|
|
void SwitchToMercView()
|
|
{
|
|
if (gubDrawMode == COVER_DRAW_MERC_VIEW)
|
|
return;
|
|
|
|
gubDrawMode = COVER_DRAW_MERC_VIEW;
|
|
ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, gzDisplayCoverText[DC_MSG__COVER_DRAW_MERC_VIEW]);
|
|
DisplayCover(TRUE);
|
|
}
|
|
|
|
void SwitchToEnemyView()
|
|
{
|
|
if (gubDrawMode == COVER_DRAW_ENEMY_VIEW)
|
|
return;
|
|
|
|
gubDrawMode = COVER_DRAW_ENEMY_VIEW;
|
|
ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, gzDisplayCoverText[DC_MSG__COVER_DRAW_ENEMY_VIEW]);
|
|
DisplayCover(TRUE);
|
|
}
|
|
|
|
void SwitchViewOff()
|
|
{
|
|
if (gubDrawMode == COVER_DRAW_OFF)
|
|
return;
|
|
|
|
gubDrawMode = COVER_DRAW_OFF;
|
|
ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, gzDisplayCoverText[DC_MSG__COVER_DRAW_OFF]);
|
|
DisplayCover(TRUE);
|
|
}
|
|
|
|
void ToggleEnemyView()
|
|
{
|
|
if (gubDrawMode == COVER_DRAW_ENEMY_VIEW) {
|
|
SwitchViewOff();
|
|
} else {
|
|
SwitchToEnemyView();
|
|
}
|
|
}
|
|
|
|
void ToggleMercView()
|
|
{
|
|
if (gubDrawMode == COVER_DRAW_MERC_VIEW) {
|
|
SwitchViewOff();
|
|
} else {
|
|
SwitchToMercView();
|
|
}
|
|
}
|
|
|
|
void SwitchCoverDrawMode()
|
|
{
|
|
switch(gubDrawMode)
|
|
{
|
|
case COVER_DRAW_OFF:
|
|
SwitchToMercView();
|
|
break;
|
|
case COVER_DRAW_MERC_VIEW:
|
|
SwitchToEnemyView();
|
|
break;
|
|
default:
|
|
SwitchViewOff();
|
|
break;
|
|
}
|
|
}
|
|
///END key binding functions
|
|
|
|
void GetGridNoForViewPort( const INT32& ubX, const INT32& ubY, INT32& sGridNo )
|
|
{
|
|
sGridNo = MAPROWCOLTOPOS( ubY, ubX );
|
|
}
|
|
|
|
CHAR16* GetTerrainName( const UINT8& ubTerrainType )
|
|
{
|
|
switch(ubTerrainType) {
|
|
case LOW_GRASS:
|
|
case HIGH_GRASS:
|
|
return gzDisplayCoverText[DC_TTI__WOOD];
|
|
case FLAT_FLOOR:
|
|
case PAVED_ROAD:
|
|
return gzDisplayCoverText[DC_TTI__URBAN];
|
|
case DIRT_ROAD:
|
|
case TRAIN_TRACKS:
|
|
return gzDisplayCoverText[DC_TTI__DESERT];
|
|
case FLAT_GROUND:
|
|
return gzDisplayCoverText[DC_TTI__WOOD_AND_DESERT];
|
|
default:
|
|
return gzDisplayCoverText[DC_TTI__UNKNOWN];
|
|
}
|
|
}
|
|
|
|
CHAR16* GetDetailedTerrainName( ADDITIONAL_TILE_PROPERTIES_VALUES zGivenTileProperties )
|
|
{
|
|
if(zGivenTileProperties.bWoodCamoAffinity >= 50)
|
|
{
|
|
if(zGivenTileProperties.bDesertCamoAffinity >= 50)
|
|
{
|
|
return gzDisplayCoverText[DC_TTI__WOOD_AND_DESERT];
|
|
}
|
|
else
|
|
{
|
|
if(zGivenTileProperties.bUrbanCamoAffinity >= 50)
|
|
{
|
|
return gzDisplayCoverText[DC_TTI__WOOD_AND_URBAN];
|
|
}
|
|
else
|
|
{
|
|
if(zGivenTileProperties.bSnowCamoAffinity >= 50)
|
|
{
|
|
return gzDisplayCoverText[DC_TTI__WOOD_AND_SNOW];
|
|
}
|
|
else
|
|
{
|
|
return gzDisplayCoverText[DC_TTI__WOOD];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if(zGivenTileProperties.bDesertCamoAffinity >= 50)
|
|
{
|
|
if(zGivenTileProperties.bUrbanCamoAffinity >= 50)
|
|
{
|
|
return gzDisplayCoverText[DC_TTI__DESERT_AND_URBAN];
|
|
}
|
|
else
|
|
{
|
|
if(zGivenTileProperties.bSnowCamoAffinity >= 50)
|
|
{
|
|
return gzDisplayCoverText[DC_TTI__DESERT_AND_SNOW];
|
|
}
|
|
else
|
|
{
|
|
return gzDisplayCoverText[DC_TTI__DESERT];
|
|
}
|
|
}
|
|
}
|
|
else if(zGivenTileProperties.bUrbanCamoAffinity >= 50)
|
|
{
|
|
if(zGivenTileProperties.bSnowCamoAffinity >= 50)
|
|
{
|
|
return gzDisplayCoverText[DC_TTI__URBAN_AND_SNOW];
|
|
}
|
|
else
|
|
{
|
|
return gzDisplayCoverText[DC_TTI__URBAN];
|
|
}
|
|
}
|
|
else if(zGivenTileProperties.bSnowCamoAffinity >= 50)
|
|
{
|
|
return gzDisplayCoverText[DC_TTI__SNOW];
|
|
}
|
|
else
|
|
{
|
|
return gzDisplayCoverText[DC_TTI__UNKNOWN];
|
|
}
|
|
}
|
|
|
|
TileDefines GetTileCoverIndex( const INT8& bCover )
|
|
{
|
|
switch(bCover) {
|
|
case NO_COVER:
|
|
return SPECIALTILE_COVER_1; // red
|
|
case MIN_COVER:
|
|
return SPECIALTILE_COVER_2; // orange
|
|
case MED_COVER:
|
|
return SPECIALTILE_COVER_3; // yellow
|
|
case MAX_COVER:
|
|
return SPECIALTILE_COVER_5; // green
|
|
default:
|
|
return SPECIALTILE_COVER_4; // light green, can be used to denote that you just don't know.
|
|
}
|
|
}
|
|
|
|
void AddCoverObjectToWorld( const INT32& sGridNo, const UINT16& usGraphic, const BOOLEAN& fRoof )
|
|
{
|
|
LEVELNODE *pNode;
|
|
|
|
if( fRoof )
|
|
{
|
|
AddOnRoofToHead( sGridNo, usGraphic );
|
|
pNode = gpWorldLevelData[ sGridNo ].pOnRoofHead;
|
|
}
|
|
else
|
|
{
|
|
AddObjectToHead( sGridNo, usGraphic );
|
|
pNode = gpWorldLevelData[ sGridNo ].pObjectHead;
|
|
}
|
|
|
|
pNode->uiFlags |= LEVELNODE_REVEAL;
|
|
|
|
if( NightTime() )
|
|
{
|
|
pNode->ubShadeLevel=DEFAULT_SHADE_LEVEL;
|
|
pNode->ubNaturalShadeLevel=DEFAULT_SHADE_LEVEL;
|
|
}
|
|
}
|
|
|
|
void RemoveCoverObjectFromWorld( const INT32 sGridNo, const UINT16& usGraphic, const BOOLEAN& fRoof )
|
|
{
|
|
if( fRoof )
|
|
{
|
|
RemoveOnRoof( sGridNo, usGraphic );
|
|
}
|
|
else
|
|
{
|
|
RemoveObject( sGridNo, usGraphic );
|
|
}
|
|
}
|
|
|
|
BOOLEAN HasAdjTile( const INT32& ubX, const INT32& ubY, const INT32& ubZ )
|
|
{
|
|
INT32 ubTX, ubTY;
|
|
|
|
for ( ubTX = ubX-1; ubTX <= ubX+1; ++ubTX )
|
|
{
|
|
if ( ubTX < 0 || ubTX > WORLD_COLS )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
for ( ubTY = ubY-1; ubTY <= ubY+1; ++ubTY )
|
|
{
|
|
if ( ubTY < 0 || ubTY > WORLD_ROWS )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
INT8& bCover = gCoverViewArea[ ubTX ][ ubTY ][ ubZ ].bCover;
|
|
|
|
if ( bCover > -1 && bCover < MAX_COVER )
|
|
{
|
|
return TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
void AddCoverObjectsToViewArea()
|
|
{
|
|
if (gsMaxCellY == -1)
|
|
{
|
|
return;
|
|
}
|
|
|
|
register INT32 ubX, ubY, ubZ;
|
|
BOOLEAN fChanged = FALSE;
|
|
|
|
for ( ubX=gsMinCellX; ubX<=gsMaxCellX; ++ubX )
|
|
{
|
|
for ( ubY=gsMinCellY; ubY<=gsMaxCellY; ++ubY )
|
|
{
|
|
for ( ubZ=0; ubZ<COVER_Z_CELLS; ++ubZ )
|
|
{
|
|
INT8& bCover = gCoverViewArea[ ubX ][ ubY ][ ubZ ].bCover;
|
|
|
|
if ( bCover != -1 && ( bCover != MAX_COVER || HasAdjTile( ubX, ubY, ubZ ) ) )
|
|
{
|
|
INT32& sGridNo = gCoverViewArea[ ubX ][ ubY ][ ubZ ].sGridNo;
|
|
BOOLEAN& fInverseColor = gCoverViewArea[ ubX ][ ubY ][ ubZ ].fInverseColor;
|
|
|
|
TileDefines tile = GetTileCoverIndex(!fInverseColor ? bCover : MAX_COVER - bCover);
|
|
AddCoverObjectToWorld( sGridNo, tile, (BOOLEAN) ubZ );
|
|
fChanged = TRUE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Re-render the scene!
|
|
if ( fChanged )
|
|
{
|
|
SetRenderFlags( RENDER_FLAG_FULL );
|
|
}
|
|
}
|
|
|
|
void RemoveCoverObjectsFromViewArea()
|
|
{
|
|
if (gsMaxCellY == -1)
|
|
{
|
|
return;
|
|
}
|
|
|
|
register INT32 ubX, ubY, ubZ;
|
|
BOOLEAN fChanged = FALSE;
|
|
|
|
for ( ubX=gsMinCellX; ubX<=gsMaxCellX; ++ubX )
|
|
{
|
|
for ( ubY=gsMinCellY; ubY<=gsMaxCellY; ++ubY )
|
|
{
|
|
for ( ubZ=0; ubZ<COVER_Z_CELLS; ++ubZ )
|
|
{
|
|
INT8& bCover = gCoverViewArea[ ubX ][ ubY ][ ubZ ].bCover;
|
|
|
|
if ( bCover != -1 )
|
|
{
|
|
INT32& sGridNo = gCoverViewArea[ ubX ][ ubY ][ ubZ ].sGridNo;
|
|
BOOLEAN& fInverseColor = gCoverViewArea[ ubX ][ ubY ][ ubZ ].fInverseColor;
|
|
|
|
TileDefines tile = GetTileCoverIndex(!fInverseColor ? bCover : MAX_COVER - bCover);
|
|
RemoveCoverObjectFromWorld( sGridNo, tile, (BOOLEAN) ubZ );
|
|
bCover = -1;
|
|
fChanged = TRUE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Re-render the scene!
|
|
if ( fChanged )
|
|
{
|
|
SetRenderFlags( RENDER_FLAG_FULL );
|
|
}
|
|
}
|
|
|
|
// ubRadius in times of y or x cell sizes
|
|
BOOLEAN GridNoOnScreenAndAround( const INT32& sGridNo, const UINT8& ubRadius )
|
|
{
|
|
INT16 sNewCenterWorldX, sNewCenterWorldY;
|
|
INT16 sWorldX;
|
|
INT16 sWorldY;
|
|
|
|
UINT16 usXDiff = ubRadius*CELL_X_SIZE;
|
|
UINT16 usYDiff = ubRadius*CELL_Y_SIZE;
|
|
|
|
ConvertGridNoToXY( sGridNo, &sNewCenterWorldX, &sNewCenterWorldY );
|
|
|
|
// Get screen coordinates for current position of soldier
|
|
GetWorldXYAbsoluteScreenXY( (INT16)(sNewCenterWorldX), (INT16)(sNewCenterWorldY), &sWorldX, &sWorldY);
|
|
|
|
if ( sWorldX >= ( gsTopLeftWorldX - usXDiff )
|
|
&& sWorldX <= ( gsBottomRightWorldX + usXDiff )
|
|
&& sWorldY >= ( gsTopLeftWorldY - usYDiff + gsVIEWPORT_WINDOW_START_Y )
|
|
&& sWorldY <= ( gsBottomRightWorldY + usYDiff ) )
|
|
{
|
|
return( TRUE );
|
|
}
|
|
return( FALSE );
|
|
}
|
|
|
|
void DisplayCover( const BOOLEAN& forceUpdate )
|
|
{
|
|
if ( gGameExternalOptions.ubCoverDisplayUpdateWait == (UINT16)-1 )
|
|
{
|
|
return;
|
|
}
|
|
|
|
if ( forceUpdate || ( !gfScrollPending && !gfScrollInertia && GetTickCount() > guiCoverNextUpdateTime ) )
|
|
{
|
|
CalculateCover();
|
|
guiCoverNextUpdateTime = GetTickCount() + gGameExternalOptions.ubCoverDisplayUpdateWait;
|
|
}
|
|
}
|
|
|
|
void CalculateCover()
|
|
{
|
|
register INT32 ubX, ubY;
|
|
register INT8 ubZ;
|
|
SOLDIERTYPE* pSoldier;
|
|
|
|
RemoveCoverObjectsFromViewArea();
|
|
|
|
if( gubDrawMode == COVER_DRAW_OFF )
|
|
{
|
|
return;
|
|
}
|
|
|
|
if( gusSelectedSoldier == NOBODY )
|
|
{
|
|
return;
|
|
}
|
|
|
|
// at we're here, we want to display cover, so remove the mines display
|
|
if ( gubDrawModeMine != MINES_DRAW_OFF )
|
|
RemoveMinesObjectsFromViewArea();
|
|
|
|
GetSoldier( &pSoldier, gusSelectedSoldier );
|
|
|
|
INT16 usTmp;
|
|
GetScreenXYWorldCell( gsVIEWPORT_START_X, gsVIEWPORT_START_Y, &gsMinCellX, &usTmp );
|
|
GetScreenXYWorldCell( gsVIEWPORT_END_X, gsVIEWPORT_END_Y, &gsMaxCellX, &usTmp );
|
|
|
|
GetScreenXYWorldCell( gsVIEWPORT_END_X, gsVIEWPORT_START_Y, &usTmp, &gsMinCellY );
|
|
GetScreenXYWorldCell( gsVIEWPORT_START_X, gsVIEWPORT_END_Y, &usTmp, &gsMaxCellY );
|
|
for ( ubX=gsMinCellX; ubX<=gsMaxCellX; ++ubX )
|
|
{
|
|
for ( ubY=gsMinCellY; ubY<=gsMaxCellY; ++ubY )
|
|
{
|
|
for ( ubZ=0; ubZ<COVER_Z_CELLS; ++ubZ )
|
|
{
|
|
INT32& sGridNo = gCoverViewArea[ ubX ][ ubY ][ ubZ ].sGridNo;
|
|
|
|
GetGridNoForViewPort( ubX, ubY, sGridNo );
|
|
|
|
if( !GridNoOnScreenAndAround( sGridNo, 2 ) )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// do not show stuff on ground if roof is shown
|
|
if ( IsTheRoofVisible( sGridNo ) )
|
|
{
|
|
if ( ubZ == I_GROUND_LEVEL )
|
|
continue;
|
|
}
|
|
// do not show stuff on roofs if ground is shown
|
|
else if ( ubZ == I_ROOF_LEVEL )
|
|
continue;
|
|
|
|
if ( !NewOKDestination( pSoldier, sGridNo, false, ubZ ) )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
INT8& bCover = gCoverViewArea[ ubX ][ ubY ][ ubZ ].bCover;
|
|
BOOLEAN& fInverseColor = gCoverViewArea[ ubX ][ ubY ][ ubZ ].fInverseColor;
|
|
|
|
if ( gubDrawMode == COVER_DRAW_ENEMY_VIEW ) // view of enemies against your selected merc
|
|
{
|
|
// reset cover value
|
|
bCover = MAX_COVER;
|
|
CalculateCoverForSoldier( pSoldier, sGridNo, ubZ, bCover );
|
|
fInverseColor = FALSE;
|
|
}
|
|
else if ( gubDrawMode == COVER_DRAW_MERC_VIEW )
|
|
{
|
|
// reset cover value
|
|
bCover = MAX_COVER;
|
|
if ( gTacticalStatus.fAtLeastOneGuyOnMultiSelect ) // view of selected mercs
|
|
{
|
|
// OK, loop through all guys who are 'multi-selected' and
|
|
INT32 cnt;
|
|
cnt = gTacticalStatus.Team[ gbPlayerNum ].bFirstID;
|
|
for ( pSoldier = MercPtrs[ cnt ]; cnt <= gTacticalStatus.Team[ gbPlayerNum ].bLastID; cnt++, pSoldier++ )
|
|
{
|
|
if ( pSoldier->bActive && pSoldier->bInSector )
|
|
{
|
|
if ( pSoldier->flags.uiStatusFlags & SOLDIER_MULTI_SELECTED )
|
|
{
|
|
CalculateCoverFromSoldier( pSoldier, sGridNo, ubZ, bCover );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else // single view from your merc
|
|
CalculateCoverFromSoldier( pSoldier, sGridNo, ubZ, bCover );
|
|
|
|
fInverseColor = TRUE; // we don't want to show the cover for the enemy
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
AddCoverObjectsToViewArea();
|
|
}
|
|
|
|
void CalculateCoverForSoldier( SOLDIERTYPE* pSoldier, const INT32& sTargetGridNo, const BOOLEAN& fRoof, INT8& bCover )
|
|
{
|
|
UINT32 uiLoop;
|
|
SOLDIERTYPE *pOpponent;
|
|
INT8 *pbPersOL;
|
|
INT8 *pbPublOL;
|
|
|
|
//loop through all the enemies and determine the cover
|
|
for (uiLoop = 0; uiLoop<guiNumMercSlots; ++uiLoop)
|
|
{
|
|
pOpponent = MercSlots[ uiLoop ];
|
|
|
|
// if this merc is inactive, at base, on assignment, dead, unconscious
|
|
if (!pOpponent || pOpponent->stats.bLife < OKLIFE)
|
|
{
|
|
continue; // next merc
|
|
}
|
|
|
|
// if this man is neutral / on the same side, he's not an opponent
|
|
if( CONSIDERED_NEUTRAL( pSoldier, pOpponent ) || (pSoldier->bSide == pOpponent->bSide))
|
|
{
|
|
continue; // next merc
|
|
}
|
|
|
|
pbPersOL = pSoldier->aiData.bOppList + pOpponent->ubID;
|
|
pbPublOL = gbPublicOpplist[ OUR_TEAM ] + pOpponent->ubID;
|
|
|
|
// if this opponent is unknown personally and publicly
|
|
if( *pbPersOL != SEEN_CURRENTLY && *pbPublOL != SEEN_CURRENTLY )
|
|
{
|
|
continue; // next merc
|
|
}
|
|
|
|
CalculateCoverFromSoldier( pOpponent, sTargetGridNo, fRoof, bCover, pSoldier );
|
|
|
|
// if we got no cover, just return, can't get any worse than that. Helldorados lets you count
|
|
if (bCover == NO_COVER)
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
void CalculateCoverFromSoldier( SOLDIERTYPE* pFromSoldier, const INT32& sTargetGridNo, const BOOLEAN& fRoof, INT8& bCover, SOLDIERTYPE* pToSoldier )
|
|
{
|
|
UINT16 usSightLimit = pFromSoldier->GetMaxDistanceVisible(sTargetGridNo, (INT8)fRoof, CALC_FROM_WANTED_DIR);
|
|
|
|
for ( int i = 0; i<sizeof(animArr); ++i )
|
|
{
|
|
const UINT8& ubStance = animArr[i];
|
|
|
|
INT32 usAdjustedSight;
|
|
|
|
if (pToSoldier == NULL) {
|
|
usAdjustedSight = usSightLimit;
|
|
} else {
|
|
usAdjustedSight = usSightLimit + usSightLimit * GetSightAdjustment( pToSoldier, sTargetGridNo, (INT8) fRoof, ubStance ) /100;
|
|
}
|
|
|
|
if ( SoldierToVirtualSoldierLineOfSightTest( pFromSoldier, sTargetGridNo, (INT8) fRoof, ubStance, FALSE, usAdjustedSight ) != 0 )
|
|
{
|
|
if (bCover > i) bCover = i;
|
|
break; // we go from prone to stand, if soldier can see someone crouching, he can also see someone standing
|
|
}
|
|
}
|
|
}
|
|
|
|
void DisplayRangeToTarget( SOLDIERTYPE *pSoldier, INT32 sTargetGridNo )
|
|
{
|
|
UINT16 usRange=0;
|
|
CHAR16 zOutputString[512];
|
|
UINT8 title = (UsingNewCTHSystem() == true ? DC_MSG__NCTH_GUN_RANGE_INFORMATION : DC_MSG__GUN_RANGE_INFORMATION);
|
|
|
|
if( sTargetGridNo == NOWHERE || sTargetGridNo == 0 )
|
|
{
|
|
return;
|
|
}
|
|
|
|
{
|
|
UINT8 ubLightLevel = LightTrueLevel(sTargetGridNo, gsInterfaceLevel);
|
|
UINT8 ubBrightness = 100 - 100 * (ubLightLevel-SHADE_MAX)/(SHADE_MIN-SHADE_MAX); // percentage
|
|
|
|
UINT8 ubTerrainType = NO_TERRAIN;
|
|
|
|
// anv: additional tile properties
|
|
ADDITIONAL_TILE_PROPERTIES_VALUES zGivenTileProperties;
|
|
memset(&zGivenTileProperties,0,sizeof(zGivenTileProperties));
|
|
if(gGameExternalOptions.fAdditionalTileProperties)
|
|
{
|
|
zGivenTileProperties = GetAllAdditonalTilePropertiesForGrid(sTargetGridNo, gsInterfaceLevel);
|
|
}
|
|
else
|
|
{
|
|
ubTerrainType = GetTerrainTypeForGrid(sTargetGridNo, gsInterfaceLevel);
|
|
}
|
|
|
|
INT8 ubCover = - GetSightAdjustment(pSoldier, sTargetGridNo, gsInterfaceLevel);
|
|
|
|
//display a string with cover value of current selected merc and brightness
|
|
//swprintf( zOutputString, gzDisplayCoverText[DC_MSG__COVER_INFORMATION], ubCover, GetTerrainName(ubTerrainType), ubBrightness );
|
|
|
|
//Display the msg
|
|
//ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, zOutputString );
|
|
|
|
if(gGameExternalOptions.fAdditionalTileProperties)
|
|
{
|
|
ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE,
|
|
gzDisplayCoverText[DC_MSG__COVER_INFORMATION],
|
|
ubCover, GetDetailedTerrainName(zGivenTileProperties), ubBrightness );
|
|
}
|
|
else
|
|
{
|
|
ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE,
|
|
gzDisplayCoverText[DC_MSG__COVER_INFORMATION],
|
|
ubCover, GetTerrainName(ubTerrainType), ubBrightness );
|
|
}
|
|
}
|
|
|
|
//Get the range to the target location
|
|
usRange = GetRangeInCellCoordsFromGridNoDiff( pSoldier->sGridNo, sTargetGridNo );
|
|
|
|
//if the soldier has a weapon in hand, display gun range and chance to hit
|
|
if( WeaponInHand( pSoldier ) )
|
|
{
|
|
UINT32 uiHitChance;
|
|
//AXP 30.03.2007: Fix CtH calculation for first shot after changing aim level (roof/ground)
|
|
INT8 bTempTargetLevel = pSoldier->bTargetLevel;
|
|
pSoldier->bTargetLevel = (INT8)gsInterfaceLevel;
|
|
uiHitChance = CalcChanceToHitGun( pSoldier, sTargetGridNo, (INT8)(pSoldier->aiData.bShownAimTime ), pSoldier->bAimShotLocation );
|
|
// HEADROCK HAM B2.7: CTH approximation?
|
|
if (gGameExternalOptions.fApproximateCTH)
|
|
{
|
|
uiHitChance = ChanceToHitApproximation( pSoldier, uiHitChance );
|
|
}
|
|
pSoldier->bTargetLevel = bTempTargetLevel;
|
|
|
|
// HEADROCK HAM 3.6: Calculate Gun Range using formula.
|
|
// Flugente: we might be equipped with an underbarrel gun....
|
|
OBJECTTYPE* pObjhand = pSoldier->GetUsedWeapon(&pSoldier->inv[HANDPOS]);
|
|
UINT16 usGunRange = GunRange(pObjhand, pSoldier ); // SANDRO - added argument
|
|
|
|
swprintf( zOutputString, gzDisplayCoverText[title], usRange / 10, usGunRange / 10, uiHitChance );
|
|
//Display the msg
|
|
ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, zOutputString );
|
|
}
|
|
else
|
|
{
|
|
swprintf( zOutputString, gzDisplayCoverText[title], usRange / 10, 0, 0 );
|
|
|
|
//Display the msg
|
|
ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, zOutputString );
|
|
}
|
|
|
|
//increment the display gun range counter ( just seeing how many times people use it )
|
|
//gJa25SaveStruct.uiDisplayGunRangeCounter++;
|
|
}
|
|
|
|
BOOLEAN IsTheRoofVisible( const INT32& sGridNo )
|
|
{
|
|
if( FlatRoofAboveGridNo( sGridNo ) )
|
|
{
|
|
if( gpWorldLevelData[ sGridNo ].uiFlags & MAPELEMENT_REVEALED )
|
|
{
|
|
if( gTacticalStatus.uiFlags & SHOW_ALL_ROOFS )
|
|
return( TRUE );
|
|
else
|
|
return( FALSE );
|
|
}
|
|
else
|
|
{
|
|
return( TRUE );
|
|
}
|
|
}
|
|
|
|
return( FALSE );
|
|
}
|
|
|
|
|
|
// ----------------------------- Mines display after this ----------------------------------------
|
|
// added by Flugente
|
|
|
|
//****** Local Variables *********************************************
|
|
|
|
INT16 gsMineMinCellX, gsMineMinCellY, gsMineMaxCellX, gsMineMaxCellY = -1;
|
|
|
|
DWORD guiMinesNextUpdateTime = 0;
|
|
|
|
//******* Local Function Prototypes ***********************************
|
|
|
|
TileDefines GetTileMinesIndex( const INT8& bMines );
|
|
|
|
void AddMinesObjectsToViewArea();
|
|
|
|
void CalculateMines();
|
|
void DetermineMineDisplayInTile( INT32 sGridNo, INT8 bLevel, INT8& bMines, BOOLEAN fWithMineDetector = FALSE );
|
|
|
|
BOOLEAN MineTileHasAdjTile( const INT32& ubX, const INT32& ubY, const INT32& ubZ );
|
|
|
|
//******* Functions **************************************************
|
|
|
|
///BEGIN key binding functions
|
|
void SwitchToTrapNetworkView()
|
|
{
|
|
if (gubDrawModeMine == MINES_DRAW_PLAYERTEAM_NETWORKS)
|
|
return;
|
|
|
|
gubDrawModeMine = MINES_DRAW_PLAYERTEAM_NETWORKS;
|
|
ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"Display trap network");
|
|
DisplayMines(TRUE);
|
|
}
|
|
|
|
void SwitchToHostileTrapsView()
|
|
{
|
|
if (gubDrawModeMine == MINES_DRAW_DETECT_ENEMY)
|
|
return;
|
|
|
|
gubDrawModeMine = MINES_DRAW_DETECT_ENEMY;
|
|
ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"Display nearby traps");
|
|
DisplayMines(TRUE);
|
|
}
|
|
|
|
void SwitchMineViewOff()
|
|
{
|
|
if (gubDrawModeMine == MINES_DRAW_OFF)
|
|
return;
|
|
|
|
gubDrawModeMine = MINES_DRAW_OFF;
|
|
ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"Turning off trap display");
|
|
DisplayMines(TRUE);
|
|
}
|
|
|
|
void ToggleHostileTrapsView()
|
|
{
|
|
if (gubDrawModeMine == MINES_DRAW_DETECT_ENEMY) {
|
|
SwitchMineViewOff();
|
|
} else {
|
|
SwitchToHostileTrapsView();
|
|
}
|
|
}
|
|
|
|
void ToggleTrapNetworkView()
|
|
{
|
|
SwitchMinesDrawModeForNetworks();
|
|
}
|
|
|
|
void SwitchMinesDrawModeForNetworks()
|
|
{
|
|
switch ( gubDrawModeMine )
|
|
{
|
|
case MINES_DRAW_OFF:
|
|
case MINES_DRAW_DETECT_ENEMY:
|
|
gubDrawModeMine = MINES_DRAW_PLAYERTEAM_NETWORKS;
|
|
ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"Display trap network");
|
|
DisplayMines(TRUE);
|
|
break;
|
|
|
|
case MINES_DRAW_PLAYERTEAM_NETWORKS:
|
|
gubDrawModeMine = MINES_DRAW_NETWORKCOLOURING;
|
|
ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"Display trap network colouring");
|
|
DisplayMines(TRUE);
|
|
break;
|
|
case MINES_DRAW_NETWORKCOLOURING:
|
|
gubDrawModeMine = MINES_DRAW_NET_A;
|
|
ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"Display trap network A");
|
|
DisplayMines(TRUE);
|
|
break;
|
|
case MINES_DRAW_NET_A:
|
|
gubDrawModeMine = MINES_DRAW_NET_B;
|
|
ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"Display trap network B");
|
|
DisplayMines(TRUE);
|
|
break;
|
|
case MINES_DRAW_NET_B:
|
|
gubDrawModeMine = MINES_DRAW_NET_C;
|
|
ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"Display trap network C");
|
|
DisplayMines(TRUE);
|
|
break;
|
|
case MINES_DRAW_NET_C:
|
|
gubDrawModeMine = MINES_DRAW_NET_D;
|
|
ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"Display trap network D");
|
|
DisplayMines(TRUE);
|
|
break;
|
|
case MINES_DRAW_NET_D:
|
|
case MINES_DRAW_MAX:
|
|
default:
|
|
gubDrawModeMine = MINES_DRAW_OFF;
|
|
ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"Turning off trap display");
|
|
DisplayMines(TRUE);
|
|
break;
|
|
}
|
|
}
|
|
///END key binding functions
|
|
|
|
TileDefines GetTileMinesIndex( const INT8& bMines )
|
|
{
|
|
switch(bMines)
|
|
{
|
|
case MINES_ALL:
|
|
case MINE_BOMB:
|
|
case MINES_NET_1:
|
|
case MINES_LVL_4:
|
|
return SPECIALTILE_COVER_1; // red
|
|
case MINE_BOMB_AND_WIRE:
|
|
case MINES_NET_2:
|
|
case MINES_LVL_3:
|
|
return SPECIALTILE_COVER_2; // orange
|
|
case MINE_WIRE:
|
|
case MINES_NET_3:
|
|
case MINES_LVL_2:
|
|
return SPECIALTILE_COVER_3; // yellow
|
|
case MINES_NET_4:
|
|
case MINES_LVL_1:
|
|
return SPECIALTILE_COVER_4; // green
|
|
case MAX_MINES:
|
|
default:
|
|
return SPECIALTILE_COVER_5; // light green, can be used to denote that you just don't know. looks just like normal green, though...
|
|
}
|
|
}
|
|
|
|
BOOLEAN MineTileHasAdjTile( const INT32& ubX, const INT32& ubY, const INT32& ubZ )
|
|
{
|
|
// no search for adjacent tiles wehn looking at a specific network (we have only 4 colours, need them all :-)
|
|
if ( gubDrawModeMine == MINES_DRAW_NETWORKCOLOURING || gubDrawModeMine == MINES_DRAW_NET_A || gubDrawModeMine == MINES_DRAW_NET_B || gubDrawModeMine == MINES_DRAW_NET_C || gubDrawModeMine == MINES_DRAW_NET_D )
|
|
return FALSE;
|
|
|
|
INT32 ubTX, ubTY;
|
|
|
|
for ( ubTX = ubX-1; ubTX <= ubX+1; ++ubTX )
|
|
{
|
|
if ( ubTX < 0 || ubTX > WORLD_COLS )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
for ( ubTY = ubY-1; ubTY <= ubY+1; ++ubTY )
|
|
{
|
|
if ( ubTY < 0 || ubTY > WORLD_ROWS )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
INT8& bMines = gCoverViewArea[ ubTX ][ ubTY ][ ubZ ].bMines;
|
|
|
|
if ( bMines > MINES_ALL && bMines < MAX_MINES )
|
|
{
|
|
return TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
void AddMinesObjectsToViewArea()
|
|
{
|
|
if (gsMineMaxCellY == -1)
|
|
{
|
|
return;
|
|
}
|
|
INT32 ubX, ubY, ubZ;
|
|
BOOLEAN fChanged = FALSE;
|
|
|
|
for ( ubX=gsMineMinCellX; ubX<=gsMineMaxCellX; ++ubX )
|
|
{
|
|
for ( ubY=gsMineMinCellY; ubY<=gsMineMaxCellY; ++ubY )
|
|
{
|
|
for ( ubZ=0; ubZ<COVER_Z_CELLS; ++ubZ )
|
|
{
|
|
INT8& bMines = gCoverViewArea[ ubX ][ ubY ][ ubZ ].bMines;
|
|
|
|
if ( bMines != MINES_ALL && ( bMines != MAX_MINES || MineTileHasAdjTile( ubX, ubY, ubZ ) ) )
|
|
{
|
|
INT32& sGridNo = gCoverViewArea[ ubX ][ ubY ][ ubZ ].sGridNo;
|
|
|
|
TileDefines tile = GetTileMinesIndex( bMines );
|
|
AddCoverObjectToWorld( sGridNo, tile, (BOOLEAN) ubZ );
|
|
fChanged = TRUE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Re-render the scene!
|
|
if ( fChanged )
|
|
{
|
|
SetRenderFlags( RENDER_FLAG_FULL );
|
|
}
|
|
}
|
|
|
|
void RemoveMinesObjectsFromViewArea()
|
|
{
|
|
if (gsMineMaxCellY == -1)
|
|
{
|
|
return;
|
|
}
|
|
INT32 ubX, ubY, ubZ;
|
|
BOOLEAN fChanged = FALSE;
|
|
|
|
for ( ubX=gsMineMinCellX; ubX<=gsMineMaxCellX; ++ubX )
|
|
{
|
|
for ( ubY=gsMineMinCellY; ubY<=gsMineMaxCellY; ++ubY )
|
|
{
|
|
for ( ubZ=0; ubZ<COVER_Z_CELLS; ++ubZ )
|
|
{
|
|
INT8& bMines = gCoverViewArea[ ubX ][ ubY ][ ubZ ].bMines;
|
|
|
|
if ( bMines != MINES_ALL )
|
|
{
|
|
INT32& sGridNo = gCoverViewArea[ ubX ][ ubY ][ ubZ ].sGridNo;
|
|
|
|
TileDefines tile = GetTileMinesIndex( bMines );
|
|
RemoveCoverObjectFromWorld( sGridNo, tile, (BOOLEAN) ubZ );
|
|
bMines = MINES_ALL;
|
|
fChanged = TRUE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Re-render the scene!
|
|
if ( fChanged )
|
|
{
|
|
SetRenderFlags( RENDER_FLAG_FULL );
|
|
}
|
|
}
|
|
|
|
void DisplayMines( const BOOLEAN& forceUpdate )
|
|
{
|
|
if ( gGameExternalOptions.ubCoverDisplayUpdateWait == (UINT16)-1 )
|
|
{
|
|
return;
|
|
}
|
|
|
|
if ( forceUpdate || ( !gfScrollPending && !gfScrollInertia && GetTickCount() > guiMinesNextUpdateTime ) )
|
|
{
|
|
CalculateMines();
|
|
guiMinesNextUpdateTime = GetTickCount() + gGameExternalOptions.ubCoverDisplayUpdateWait;
|
|
}
|
|
}
|
|
|
|
void CalculateMines()
|
|
{
|
|
INT32 ubX, ubY, ubZ;
|
|
SOLDIERTYPE* pSoldier;
|
|
|
|
RemoveMinesObjectsFromViewArea();
|
|
|
|
if( gubDrawModeMine == MINES_DRAW_OFF )
|
|
{
|
|
return;
|
|
}
|
|
|
|
if( gusSelectedSoldier == NOBODY )
|
|
{
|
|
return;
|
|
}
|
|
|
|
// at we're here, we want to display mines, so remove the cover display
|
|
if ( gubDrawMode != COVER_DRAW_OFF )
|
|
RemoveCoverObjectsFromViewArea();
|
|
|
|
GetSoldier( &pSoldier, gusSelectedSoldier );
|
|
|
|
// if we want to detect hostile mines and we have an metal detector in our hands, allow seeking
|
|
BOOLEAN fWithMineDetector = FALSE;
|
|
if ( pSoldier && gubDrawModeMine == MINES_DRAW_DETECT_ENEMY )
|
|
{
|
|
if ( FindMetalDetectorInHand(pSoldier) != NO_SLOT )
|
|
fWithMineDetector = TRUE;
|
|
|
|
// TODO: perhaps even consume batteries one day...
|
|
}
|
|
|
|
// if we are looking for mines via mine detector, but don't have one equipped, return, we won't detect anything
|
|
if ( gubDrawModeMine == MINES_DRAW_DETECT_ENEMY && !fWithMineDetector )
|
|
{
|
|
return;
|
|
}
|
|
|
|
const INT32& sSelectedSoldierGridNo = MercPtrs[ gusSelectedSoldier ]->sGridNo;
|
|
|
|
INT16 usTmp;
|
|
GetScreenXYWorldCell( gsVIEWPORT_START_X, gsVIEWPORT_START_Y, &gsMineMinCellX, &usTmp );
|
|
GetScreenXYWorldCell( gsVIEWPORT_END_X, gsVIEWPORT_END_Y, &gsMineMaxCellX, &usTmp );
|
|
|
|
GetScreenXYWorldCell( gsVIEWPORT_END_X, gsVIEWPORT_START_Y, &usTmp, &gsMineMinCellY );
|
|
GetScreenXYWorldCell( gsVIEWPORT_START_X, gsVIEWPORT_END_Y, &usTmp, &gsMineMaxCellY );
|
|
for ( ubX=gsMineMinCellX; ubX<=gsMineMaxCellX; ++ubX )
|
|
{
|
|
for ( ubY=gsMineMinCellY; ubY<=gsMineMaxCellY; ++ubY )
|
|
{
|
|
for ( ubZ=0; ubZ<COVER_Z_CELLS; ++ubZ )
|
|
{
|
|
INT32& sGridNo = gCoverViewArea[ ubX ][ ubY ][ ubZ ].sGridNo;
|
|
|
|
GetGridNoForViewPort( ubX, ubY, sGridNo );
|
|
|
|
if( !GridNoOnScreenAndAround( sGridNo, 2 ) )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if ( IsTheRoofVisible( sGridNo ) )
|
|
{
|
|
if ( ubZ == I_GROUND_LEVEL )
|
|
continue;
|
|
}
|
|
else if ( ubZ == I_ROOF_LEVEL )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// if we are looking for hostile mines, but the tile is out of our' detectors range, skip looking for mines
|
|
if ( gubDrawModeMine == MINES_DRAW_DETECT_ENEMY && fWithMineDetector )
|
|
{
|
|
if ( PythSpacesAway(sSelectedSoldierGridNo, sGridNo) > 4 )
|
|
continue;
|
|
}
|
|
|
|
if ( !NewOKDestination( pSoldier, sGridNo, false, (INT8) ubZ ) )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
INT8& bMines = gCoverViewArea[ ubX ][ ubY ][ ubZ ].bMines;
|
|
|
|
bMines = MAX_MINES;
|
|
|
|
DetermineMineDisplayInTile( sGridNo, ubZ, bMines, fWithMineDetector );
|
|
}
|
|
}
|
|
}
|
|
|
|
AddMinesObjectsToViewArea();
|
|
}
|
|
|
|
|
|
void DetermineMineDisplayInTile( INT32 sGridNo, INT8 bLevel, INT8& bMines, BOOLEAN fWithMineDetector )
|
|
{
|
|
// if there is a bomb at that grid and level, and it isn't disabled
|
|
for (UINT32 uiWorldBombIndex = 0; uiWorldBombIndex < guiNumWorldBombs; ++uiWorldBombIndex)
|
|
{
|
|
if (gWorldBombs[uiWorldBombIndex].fExists && gWorldItems[ gWorldBombs[uiWorldBombIndex].iItemIndex ].sGridNo == sGridNo && gWorldItems[ gWorldBombs[uiWorldBombIndex].iItemIndex ].ubLevel == bLevel )
|
|
{
|
|
OBJECTTYPE* pObj = &( gWorldItems[ gWorldBombs[uiWorldBombIndex].iItemIndex ].object );
|
|
if (!((*pObj).fFlags & OBJECT_DISABLED_BOMB))
|
|
{
|
|
// we are looking for hostile mines and have got an detector equipped
|
|
// some bombs cannot be found via metal detector
|
|
if ( gubDrawModeMine == MINES_DRAW_DETECT_ENEMY && fWithMineDetector && !( HasItemFlag(pObj->usItem, NO_METAL_DETECTION) || HasItemFlag((*pObj)[0]->data.misc.usBombItem, NO_METAL_DETECTION) ) )
|
|
{
|
|
// display all mines
|
|
bMines = MINE_BOMB;
|
|
}
|
|
else
|
|
{
|
|
// look for mines from our own team
|
|
if ( (*pObj)[0]->data.misc.ubBombOwner > 1 )
|
|
{
|
|
switch ( gubDrawModeMine )
|
|
{
|
|
case MINES_DRAW_PLAYERTEAM_NETWORKS:
|
|
{
|
|
if ( Item[pObj->usItem].tripwire == 1 )
|
|
{
|
|
// if we're already marked as MINE_BOMB, switch to MINE_BOMB_AND_WIRE
|
|
if ( bMines == MINE_BOMB )
|
|
bMines = MINE_BOMB_AND_WIRE;
|
|
else if ( bMines == MINE_BOMB_AND_WIRE )
|
|
;
|
|
else
|
|
{
|
|
// check if the tripwire has a gun attached
|
|
BOOLEAN fgunfound = FALSE;
|
|
attachmentList::iterator iterend = (*pObj)[0]->attachments.end();
|
|
for (attachmentList::iterator iter = (*pObj)[0]->attachments.begin(); iter != iterend; ++iter)
|
|
{
|
|
if ( iter->exists() && Item[iter->usItem].usItemClass == IC_GUN )
|
|
{
|
|
fgunfound = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ( fgunfound )
|
|
bMines = MINE_BOMB_AND_WIRE;
|
|
else
|
|
bMines = MINE_WIRE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// if we're already marked as MINE_WIRE, switch to MINE_BOMB_AND_WIRE
|
|
if ( bMines == MINE_WIRE )
|
|
bMines = MINE_BOMB_AND_WIRE;
|
|
else
|
|
bMines = MINE_BOMB;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case MINES_DRAW_NETWORKCOLOURING:
|
|
{
|
|
if ( Item[pObj->usItem].tripwire == 1 )
|
|
{
|
|
// determine if wire is of the network we're searching for
|
|
// determine this tripwire's flag
|
|
UINT32 ubWireNetworkFlag = (*pObj)[0]->data.ubWireNetworkFlag;
|
|
|
|
// only if its one of our networks
|
|
if ( (ubWireNetworkFlag & TRIPWIRE_NETWORK_OWNER_PLAYER ) != 0 )
|
|
{
|
|
// correct network?
|
|
if ( (ubWireNetworkFlag & TRIPWIRE_NETWORK_NET_1 ) != 0 )
|
|
bMines = MINES_NET_1;
|
|
else if ( (ubWireNetworkFlag & TRIPWIRE_NETWORK_NET_2 ) != 0 )
|
|
bMines = MINES_NET_2;
|
|
else if ( (ubWireNetworkFlag & TRIPWIRE_NETWORK_NET_3 ) != 0 )
|
|
bMines = MINES_NET_3;
|
|
else if ( (ubWireNetworkFlag & TRIPWIRE_NETWORK_NET_4 ) != 0 )
|
|
bMines = MINES_NET_4;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case MINES_DRAW_NET_A:
|
|
{
|
|
if ( Item[pObj->usItem].tripwire == 1 )
|
|
{
|
|
// determine if wire is of the network we're searching for
|
|
// determine this tripwire's flag
|
|
UINT32 ubWireNetworkFlag = (*pObj)[0]->data.ubWireNetworkFlag;
|
|
|
|
// correct network?
|
|
if ( (ubWireNetworkFlag & TRIPWIRE_NETWORK_OWNER_PLAYER ) != 0 && (ubWireNetworkFlag & TRIPWIRE_NETWORK_NET_1 ) != 0 )
|
|
{
|
|
bMines = MINES_LVL_1;
|
|
|
|
if ( (ubWireNetworkFlag & ( TRIPWIRE_NETWORK_LVL_2 ) ) != 0 )
|
|
bMines = MINES_LVL_2;
|
|
else if ( (ubWireNetworkFlag & ( TRIPWIRE_NETWORK_LVL_3 ) ) != 0 )
|
|
bMines = MINES_LVL_3;
|
|
else if ( (ubWireNetworkFlag & ( TRIPWIRE_NETWORK_LVL_4 ) ) != 0 )
|
|
bMines = MINES_LVL_4;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case MINES_DRAW_NET_B:
|
|
{
|
|
if ( Item[pObj->usItem].tripwire == 1 )
|
|
{
|
|
// determine if wire is of the network we're searching for
|
|
// determine this tripwire's flag
|
|
UINT32 ubWireNetworkFlag = (*pObj)[0]->data.ubWireNetworkFlag;
|
|
|
|
// correct network?
|
|
if ( (ubWireNetworkFlag & TRIPWIRE_NETWORK_OWNER_PLAYER ) != 0 && (ubWireNetworkFlag & TRIPWIRE_NETWORK_NET_2 ) != 0 )
|
|
{
|
|
bMines = MINES_LVL_1;
|
|
|
|
if ( (ubWireNetworkFlag & ( TRIPWIRE_NETWORK_LVL_2 ) ) != 0 )
|
|
bMines = MINES_LVL_2;
|
|
else if ( (ubWireNetworkFlag & ( TRIPWIRE_NETWORK_LVL_3 ) ) != 0 )
|
|
bMines = MINES_LVL_3;
|
|
else if ( (ubWireNetworkFlag & ( TRIPWIRE_NETWORK_LVL_4 ) ) != 0 )
|
|
bMines = MINES_LVL_4;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case MINES_DRAW_NET_C:
|
|
{
|
|
if ( Item[pObj->usItem].tripwire == 1 )
|
|
{
|
|
// determine if wire is of the network we're searching for
|
|
// determine this tripwire's flag
|
|
UINT32 ubWireNetworkFlag = (*pObj)[0]->data.ubWireNetworkFlag;
|
|
|
|
// correct network?
|
|
if ( (ubWireNetworkFlag & TRIPWIRE_NETWORK_OWNER_PLAYER ) != 0 && (ubWireNetworkFlag & TRIPWIRE_NETWORK_NET_3 ) != 0 )
|
|
{
|
|
bMines = MINES_LVL_1;
|
|
|
|
if ( (ubWireNetworkFlag & ( TRIPWIRE_NETWORK_LVL_2 ) ) != 0 )
|
|
bMines = MINES_LVL_2;
|
|
else if ( (ubWireNetworkFlag & ( TRIPWIRE_NETWORK_LVL_3 ) ) != 0 )
|
|
bMines = MINES_LVL_3;
|
|
else if ( (ubWireNetworkFlag & ( TRIPWIRE_NETWORK_LVL_4 ) ) != 0 )
|
|
bMines = MINES_LVL_4;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case MINES_DRAW_NET_D:
|
|
{
|
|
if ( Item[pObj->usItem].tripwire == 1 )
|
|
{
|
|
// determine if wire is of the network we're searching for
|
|
// determine this tripwire's flag
|
|
UINT32 ubWireNetworkFlag = (*pObj)[0]->data.ubWireNetworkFlag;
|
|
|
|
// correct network?
|
|
if ( (ubWireNetworkFlag & TRIPWIRE_NETWORK_OWNER_PLAYER ) != 0 && (ubWireNetworkFlag & TRIPWIRE_NETWORK_NET_4 ) != 0 )
|
|
{
|
|
bMines = MINES_LVL_1;
|
|
|
|
if ( (ubWireNetworkFlag & ( TRIPWIRE_NETWORK_LVL_2 ) ) != 0 )
|
|
bMines = MINES_LVL_2;
|
|
else if ( (ubWireNetworkFlag & ( TRIPWIRE_NETWORK_LVL_3 ) ) != 0 )
|
|
bMines = MINES_LVL_3;
|
|
else if ( (ubWireNetworkFlag & ( TRIPWIRE_NETWORK_LVL_4 ) ) != 0 )
|
|
bMines = MINES_LVL_4;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case MINES_DRAW_DETECT_ENEMY:
|
|
case MINES_DRAW_MAX:
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// ----------------------------- trait range display after this ----------------------------------------
|
|
// added by Flugente
|
|
|
|
//******* Local Function Prototypes ***********************************
|
|
|
|
TileDefines GetTileTraitIndex( const INT8& bTrait );
|
|
|
|
void AddTraitObjectsToViewArea();
|
|
|
|
void CalculateTraitRange();
|
|
|
|
BOOLEAN TraitTileHasAdjTile( const INT32& ubX, const INT32& ubY, const INT32& ubZ );
|
|
|
|
UINT32 usDisplayTrait = NO_SKILLTRAIT_NT;
|
|
INT32 sTraitgridNo = NOWHERE;
|
|
|
|
///BEGIN key binding functions
|
|
void ToggleTraitRangeView(BOOLEAN fOn)
|
|
{
|
|
if ( fOn )
|
|
{
|
|
gubDrawModeTrait = TRAIT_DRAW_RANGE;
|
|
gubDrawMode = COVER_DRAW_OFF;
|
|
gubDrawModeMine = MINES_DRAW_OFF;
|
|
}
|
|
else
|
|
{
|
|
gubDrawModeTrait = TRAIT_DRAW_OFF;
|
|
SetTraitToDisplay( NO_SKILLTRAIT_NT );
|
|
SetGridNoForTraitDisplay( NOWHERE );
|
|
}
|
|
|
|
//ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"Display trait ranges");
|
|
DisplayTraitRange(TRUE);
|
|
}
|
|
|
|
void SetTraitToDisplay( UINT32 aTrait )
|
|
{
|
|
usDisplayTrait = aTrait;
|
|
}
|
|
|
|
void SetGridNoForTraitDisplay( INT32 sGridNo )
|
|
{
|
|
sTraitgridNo = sGridNo;
|
|
}
|
|
|
|
void DisplayTraitRange( const BOOLEAN& forceUpdate )
|
|
{
|
|
if ( gGameExternalOptions.ubCoverDisplayUpdateWait == -1 )
|
|
{
|
|
return;
|
|
}
|
|
|
|
if ( forceUpdate || ( !gfScrollPending && !gfScrollInertia && GetTickCount() > guiMinesNextUpdateTime ) )
|
|
{
|
|
CalculateTraitRange();
|
|
guiMinesNextUpdateTime = GetTickCount() + gGameExternalOptions.ubCoverDisplayUpdateWait;
|
|
}
|
|
}
|
|
|
|
void CalculateTraitRange()
|
|
{
|
|
INT32 ubX, ubY, ubZ;
|
|
SOLDIERTYPE* pSoldier;
|
|
|
|
RemoveTraitObjectsFromViewArea();
|
|
|
|
if ( gubDrawModeTrait == TRAIT_DRAW_OFF )
|
|
return;
|
|
|
|
if( gusSelectedSoldier == NOBODY || !GetSoldier( &pSoldier, gusSelectedSoldier ) )
|
|
return;
|
|
|
|
UINT16 range1 = 0;
|
|
UINT16 range2 = 0;
|
|
switch ( usDisplayTrait )
|
|
{
|
|
case RADIO_OPERATOR_NT:
|
|
{
|
|
// we 'mark' the map position with which we called the menu, so that the player sees where he is targetting
|
|
|
|
}
|
|
break;
|
|
|
|
case VARIOUSSKILLS:
|
|
{
|
|
range1 = gGameExternalOptions.usSpotterRange;
|
|
range2 = gGameExternalOptions.usSpotterRange * 2;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
return;
|
|
}
|
|
|
|
//remove other displays
|
|
if( gubDrawModeMine != MINES_DRAW_OFF )
|
|
RemoveMinesObjectsFromViewArea();
|
|
|
|
if ( gubDrawMode != COVER_DRAW_OFF )
|
|
RemoveCoverObjectsFromViewArea();
|
|
|
|
const INT32& sSelectedSoldierGridNo = MercPtrs[ gusSelectedSoldier ]->sGridNo;
|
|
|
|
INT16 usTmp;
|
|
GetScreenXYWorldCell( gsVIEWPORT_START_X, gsVIEWPORT_START_Y, &gsMineMinCellX, &usTmp );
|
|
GetScreenXYWorldCell( gsVIEWPORT_END_X, gsVIEWPORT_END_Y, &gsMineMaxCellX, &usTmp );
|
|
|
|
GetScreenXYWorldCell( gsVIEWPORT_END_X, gsVIEWPORT_START_Y, &usTmp, &gsMineMinCellY );
|
|
GetScreenXYWorldCell( gsVIEWPORT_START_X, gsVIEWPORT_END_Y, &usTmp, &gsMineMaxCellY );
|
|
for ( ubX=gsMineMinCellX; ubX<=gsMineMaxCellX; ++ubX )
|
|
{
|
|
for ( ubY=gsMineMinCellY; ubY<=gsMineMaxCellY; ++ubY )
|
|
{
|
|
for ( ubZ=0; ubZ<COVER_Z_CELLS; ++ubZ )
|
|
{
|
|
INT32& sGridNo = gCoverViewArea[ ubX ][ ubY ][ ubZ ].sGridNo;
|
|
|
|
GetGridNoForViewPort( ubX, ubY, sGridNo );
|
|
|
|
if( !GridNoOnScreenAndAround( sGridNo, 2 ) )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if ( !NewOKDestination( pSoldier, sGridNo, false, (INT8) ubZ ) )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// do not show stuff on roofs if ground is shown
|
|
if ( ubZ == I_ROOF_LEVEL && !IsTheRoofVisible( sGridNo ) )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// do not show stuff on ground if roof is shown
|
|
if ( ubZ == I_GROUND_LEVEL && IsTheRoofVisible( sGridNo ) )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if ( range1 && PythSpacesAway(sSelectedSoldierGridNo, sGridNo) == range1 )
|
|
gCoverViewArea[ ubX ][ ubY ][ ubZ ].bTrait = TRAIT_1;
|
|
else if ( range2 && PythSpacesAway(sSelectedSoldierGridNo, sGridNo) == range2 )
|
|
gCoverViewArea[ ubX ][ ubY ][ ubZ ].bTrait = TRAIT_2;
|
|
// mark the gridno we are targetting
|
|
else if ( PythSpacesAway(sTraitgridNo, sGridNo) == 1 )
|
|
gCoverViewArea[ ubX ][ ubY ][ ubZ ].bTrait = TRAIT_1;
|
|
else
|
|
gCoverViewArea[ ubX ][ ubY ][ ubZ ].bTrait = MAX_TRAIT;
|
|
}
|
|
}
|
|
}
|
|
|
|
AddTraitObjectsToViewArea();
|
|
}
|
|
|
|
void AddTraitObjectsToViewArea()
|
|
{
|
|
if (gsMineMaxCellY == -1)
|
|
{
|
|
return;
|
|
}
|
|
INT32 ubX, ubY, ubZ;
|
|
BOOLEAN fChanged = FALSE;
|
|
|
|
for ( ubX=gsMineMinCellX; ubX<=gsMineMaxCellX; ++ubX )
|
|
{
|
|
for ( ubY=gsMineMinCellY; ubY<=gsMineMaxCellY; ++ubY )
|
|
{
|
|
for ( ubZ=0; ubZ<COVER_Z_CELLS; ++ubZ )
|
|
{
|
|
INT32& sGridNo = gCoverViewArea[ ubX ][ ubY ][ ubZ ].sGridNo;
|
|
INT8& bTrait = gCoverViewArea[ ubX ][ ubY ][ ubZ ].bTrait;
|
|
|
|
if ( bTrait != -1 && ( bTrait == TRAIT_1 || bTrait == TRAIT_2 ) )
|
|
{
|
|
TileDefines tile = GetTileTraitIndex( bTrait );
|
|
AddCoverObjectToWorld( sGridNo, tile, (BOOLEAN) ubZ );
|
|
fChanged = TRUE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Re-render the scene!
|
|
if ( fChanged )
|
|
{
|
|
SetRenderFlags( RENDER_FLAG_FULL );
|
|
}
|
|
}
|
|
|
|
TileDefines GetTileTraitIndex( const INT8& bTrait )
|
|
{
|
|
switch(bTrait)
|
|
{
|
|
case TRAIT_1:
|
|
return SPECIALTILE_COVER_5; // green
|
|
case TRAIT_2:
|
|
return SPECIALTILE_COVER_2; // orange
|
|
case TRAIT_ALL:
|
|
return SPECIALTILE_COVER_3; // yellow
|
|
case MAX_TRAIT:
|
|
default:
|
|
return SPECIALTILE_COVER_1; // red
|
|
}
|
|
}
|
|
|
|
BOOLEAN TraitTileHasAdjTile( const INT32& ubX, const INT32& ubY, const INT32& ubZ )
|
|
{
|
|
INT32 ubTX, ubTY;
|
|
|
|
for ( ubTX = ubX-1; ubTX <= ubX+1; ++ubTX )
|
|
{
|
|
if ( ubTX < 0 || ubTX > WORLD_COLS )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
for ( ubTY = ubY-1; ubTY <= ubY+1; ++ubTY )
|
|
{
|
|
if ( ubTY < 0 || ubTY > WORLD_ROWS )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
INT8& bTrait = gCoverViewArea[ ubTX ][ ubTY ][ ubZ ].bTrait;
|
|
|
|
if ( bTrait == TRAIT_1 || bTrait == TRAIT_2 )
|
|
{
|
|
return TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
void RemoveTraitObjectsFromViewArea()
|
|
{
|
|
if (gsMineMaxCellY == -1)
|
|
{
|
|
return;
|
|
}
|
|
INT32 ubX, ubY, ubZ;
|
|
BOOLEAN fChanged = FALSE;
|
|
|
|
for ( ubX=gsMineMinCellX; ubX<=gsMineMaxCellX; ++ubX )
|
|
{
|
|
for ( ubY=gsMineMinCellY; ubY<=gsMineMaxCellY; ++ubY )
|
|
{
|
|
for ( ubZ=0; ubZ<COVER_Z_CELLS; ++ubZ )
|
|
{
|
|
INT32& sGridNo = gCoverViewArea[ ubX ][ ubY ][ ubZ ].sGridNo;
|
|
INT8& bTrait = gCoverViewArea[ ubX ][ ubY ][ ubZ ].bTrait;
|
|
|
|
if ( bTrait != -1 || TraitTileHasAdjTile( ubX, ubY, ubZ ))
|
|
{
|
|
TileDefines tile = GetTileTraitIndex( bTrait );
|
|
RemoveCoverObjectFromWorld( sGridNo, tile, (BOOLEAN) ubZ );
|
|
bTrait = -1;
|
|
fChanged = TRUE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Re-render the scene!
|
|
if ( fChanged )
|
|
{
|
|
SetRenderFlags( RENDER_FLAG_FULL );
|
|
}
|
|
} |