Files
source/Tactical/DisplayCover.cpp
T
Wanne f200ac8d3d New Feature: Traps made out of guns and a piece of tripwire (by Flugente)
- guns can now be attached to tripwire and then be used as tripwire-activatd traps
o Attach any firearm (any gun, no RPGs, Mortars or GLs) to a piece of tripwire. You will need the newest xmls for that.
o You can plant the tripwire as you usually do.
o Once activated, the gun that is attached to the wire will fire once. It will fire in the direction that you planted the bomb, just like claymore mines do (the direction that the merc planting the wire was looking).
o The bullet fired has the range and damage it would have if fired from the gun. 
o The gun correctly heats, jams, degrades and whatever else guns do when fired. If the gun is jammed, not pumped (pumpguns) or does not have ammo, it will not fire.
o After firing, the gun lies on the floor, just where the trp was planted.
o This can be applied to any tripwire. You can ave multiple of these traps. Just put them into your normal trap setup however you like.
o At the moment, only single shots are possible, even for guns that do not have a single shot mode (MG3 etc.)
o Although I did not experience any problems with attachments, it might be a good idea to take any attachments off a gun if you attach it to tripwire.
o When the gun is attached, it is possible to attach gun attachments (lasers, scopes etc.) to the explosive. Do not attach any attachments to the explosive. They will be lost if the wire is activated.
o see: http://www.bears-pit.com/board/ubbthreads.php/topics/305634.html#Post305634
-> To support this feature, some (already existing) xml-tags have been added to the weapons in Data-1.13\TableData\Items.xml (<nasAttachmentClass>, <AttachmentPoint>, <AttachToPointAPCost>, <Attachment>)



git-svn-id: https://ja2svn.mooo.com/source/ja2/trunk/GameSource/ja2_v1.13/Build@5331 3b4a5df2-a311-0410-b5c6-a8a6f20db521
2012-06-06 08:27:12 +00:00

1241 lines
34 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"
#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
};
struct CoverCell
{
INT32 sGridNo;
INT8 bCover;
BOOLEAN fInverseColor;
INT8 bMines;
CoverCell() : sGridNo(NOWHERE), bCover(INVALID_COVER), fInverseColor(FALSE), bMines(MINES_ALL) {}
};
enum COVER_DRAW_MODE {
COVER_DRAW_OFF,
COVER_DRAW_MERC_VIEW,
COVER_DRAW_ENEMY_VIEW
};
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
// Flugente: mines display - stuff needs to be here
enum MINES_DRAW_MODE {
MINES_DRAW_OFF,
MINES_DRAW_DETECT_ENEMY,
MINES_DRAW_PLAYERTEAM_NETWORKS,
MINES_DRAW_NETWORKCOLOURING,
MINES_DRAW_NET_A,
MINES_DRAW_NET_B,
MINES_DRAW_NET_C,
MINES_DRAW_NET_D,
MINES_DRAW_MAX
};
//****** 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
//******* Local Function Prototypes ***********************************
CHAR16* GetTerrainName( const UINT8& ubTerrainType );
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 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];
}
}
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;
}
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 )
{
INT32& sGridNo = gCoverViewArea[ ubX ][ ubY ][ ubZ ].sGridNo;
INT8& bCover = gCoverViewArea[ ubX ][ ubY ][ ubZ ].bCover;
BOOLEAN& fInverseColor = gCoverViewArea[ ubX ][ ubY ][ ubZ ].fInverseColor;
if ( bCover != -1 && ( bCover != MAX_COVER || HasAdjTile( ubX, ubY, ubZ ) ) )
{
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;
}
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 )
{
INT32& sGridNo = gCoverViewArea[ ubX ][ ubY ][ ubZ ].sGridNo;
INT8& bCover = gCoverViewArea[ ubX ][ ubY ][ ubZ ].bCover;
BOOLEAN& fInverseColor = gCoverViewArea[ ubX ][ ubY ][ ubZ ].fInverseColor;
if ( bCover != -1 )
{
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 == -1 )
{
return;
}
if ( forceUpdate || ( !gfScrollPending && !gfScrollInertia && GetTickCount() > guiCoverNextUpdateTime ) )
{
CalculateCover();
guiCoverNextUpdateTime = GetTickCount() + gGameExternalOptions.ubCoverDisplayUpdateWait;
}
}
void CalculateCover()
{
INT32 ubX, ubY, 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 );
const INT32& sSelectedSoldierGridNo = MercPtrs[ gusSelectedSoldier ]->sGridNo;
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;
INT8& bCover = gCoverViewArea[ ubX ][ ubY ][ ubZ ].bCover;
BOOLEAN& fInverseColor = gCoverViewArea[ ubX ][ ubY ][ ubZ ].fInverseColor;
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 (BehindSoldier( x, y ))
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 ) // single view from your merc
{
// reset cover value
bCover = MAX_COVER;
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
INT8 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 );
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();
/*if (gubDrawModeMine == MINES_DRAW_PLAYERTEAM_NETWORKS) {
SwitchMineViewOff();
} else {
SwitchToTrapNetworkView();
}*/
}
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;
}
/*switch(gubDrawModeMine)
{
case MINES_DRAW_OFF:
SwitchToTrapNetworkView();
break;
case MINES_DRAW_PLAYERTEAM_NETWORKS:
SwitchToHostileTrapsView();
break;
default:
SwitchMineViewOff();
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 )
{
INT32& sGridNo = gCoverViewArea[ ubX ][ ubY ][ ubZ ].sGridNo;
INT8& bMines = gCoverViewArea[ ubX ][ ubY ][ ubZ ].bMines;
if ( bMines != MINES_ALL && ( bMines != MAX_MINES || MineTileHasAdjTile( ubX, ubY, ubZ ) ) )
{
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 )
{
INT32& sGridNo = gCoverViewArea[ ubX ][ ubY ][ ubZ ].sGridNo;
INT8& bMines = gCoverViewArea[ ubX ][ ubY ][ ubZ ].bMines;
if ( bMines != MINES_ALL )
{
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 == -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) )
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;
INT8& bMines = gCoverViewArea[ ubX ][ ubY ][ ubZ ].bMines;
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;
}
bMines = MAX_MINES;
// 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;
}
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
if ( gubDrawModeMine == MINES_DRAW_DETECT_ENEMY && fWithMineDetector )
{
// 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;
// correct network?
if ( (ubWireNetworkFlag & ( PLAYER_NET_1_LVL_1 | PLAYER_NET_1_LVL_2 | PLAYER_NET_1_LVL_3 | PLAYER_NET_1_LVL_4 ) ) != 0 )
bMines = MINES_NET_1;
else if ( (ubWireNetworkFlag & ( PLAYER_NET_2_LVL_1 | PLAYER_NET_2_LVL_2 | PLAYER_NET_2_LVL_3 | PLAYER_NET_2_LVL_4 ) ) != 0 )
bMines = MINES_NET_2;
else if ( (ubWireNetworkFlag & ( PLAYER_NET_3_LVL_1 | PLAYER_NET_3_LVL_2 | PLAYER_NET_3_LVL_3 | PLAYER_NET_3_LVL_4 ) ) != 0 )
bMines = MINES_NET_3;
else if ( (ubWireNetworkFlag & ( PLAYER_NET_4_LVL_1 | PLAYER_NET_4_LVL_2 | PLAYER_NET_4_LVL_3 | PLAYER_NET_4_LVL_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 & ( PLAYER_NET_1_LVL_1 | PLAYER_NET_1_LVL_2 | PLAYER_NET_1_LVL_3 | PLAYER_NET_1_LVL_4 ) ) != 0 )
{
bMines = MINES_LVL_1;
if ( (ubWireNetworkFlag & ( PLAYER_NET_1_LVL_2 ) ) != 0 )
bMines = MINES_LVL_2;
else if ( (ubWireNetworkFlag & ( PLAYER_NET_1_LVL_3 ) ) != 0 )
bMines = MINES_LVL_3;
else if ( (ubWireNetworkFlag & ( PLAYER_NET_1_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 & ( PLAYER_NET_2_LVL_1 | PLAYER_NET_2_LVL_2 | PLAYER_NET_2_LVL_3 | PLAYER_NET_2_LVL_4 ) ) != 0 )
{
bMines = MINES_LVL_1;
if ( (ubWireNetworkFlag & ( PLAYER_NET_2_LVL_2 ) ) != 0 )
bMines = MINES_LVL_2;
else if ( (ubWireNetworkFlag & ( PLAYER_NET_2_LVL_3 ) ) != 0 )
bMines = MINES_LVL_3;
else if ( (ubWireNetworkFlag & ( PLAYER_NET_2_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 & ( PLAYER_NET_3_LVL_1 | PLAYER_NET_3_LVL_2 | PLAYER_NET_3_LVL_3 | PLAYER_NET_3_LVL_4 ) ) != 0 )
{
bMines = MINES_LVL_1;
if ( (ubWireNetworkFlag & ( PLAYER_NET_3_LVL_2 ) ) != 0 )
bMines = MINES_LVL_2;
else if ( (ubWireNetworkFlag & ( PLAYER_NET_3_LVL_3 ) ) != 0 )
bMines = MINES_LVL_3;
else if ( (ubWireNetworkFlag & ( PLAYER_NET_3_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 & ( PLAYER_NET_4_LVL_1 | PLAYER_NET_4_LVL_2 | PLAYER_NET_4_LVL_3 | PLAYER_NET_4_LVL_4 ) ) != 0 )
{
bMines = MINES_LVL_1;
if ( (ubWireNetworkFlag & ( PLAYER_NET_4_LVL_2 ) ) != 0 )
bMines = MINES_LVL_2;
else if ( (ubWireNetworkFlag & ( PLAYER_NET_4_LVL_3 ) ) != 0 )
bMines = MINES_LVL_3;
else if ( (ubWireNetworkFlag & ( PLAYER_NET_4_LVL_4 ) ) != 0 )
bMines = MINES_LVL_4;
}
}
}
break;
case MINES_DRAW_DETECT_ENEMY:
case MINES_DRAW_MAX:
default:
break;
}
}
}
}
}
}
}