diff --git a/Tactical/Item Types.h b/Tactical/Item Types.h
index 7bae7326..e1aafee9 100644
--- a/Tactical/Item Types.h
+++ b/Tactical/Item Types.h
@@ -383,6 +383,7 @@ typedef struct
INT16 percentburstfireapreduction;
INT16 camobonus;
+ INT16 stealthbonus;
UINT16 defaultattachment;
} INVTYPE;
diff --git a/Tactical/Items.cpp b/Tactical/Items.cpp
index 7532dca3..0396dd4f 100644
--- a/Tactical/Items.cpp
+++ b/Tactical/Items.cpp
@@ -7177,4 +7177,34 @@ UINT8 AllowedAimingLevels(SOLDIERTYPE * pSoldier)
return aimLevels;
}
+//Madd: added
+INT16 GetStealthBonus( OBJECTTYPE * pObj )
+{
+ INT8 bLoop;
+ INT16 bns=0;
+ bns = (INT16) BonusReduce(Item[pObj->usItem].stealthbonus,pObj->bStatus[0]);
+
+ for (bLoop = 0; bLoop < MAX_ATTACHMENTS; bLoop++)
+ {
+ bns += (INT16) BonusReduce(Item[pObj->usAttachItem[bLoop]].stealthbonus,pObj->bAttachStatus[bLoop]);
+ }
+ return( bns );
+}
+INT16 GetWornStealth( SOLDIERTYPE * pSoldier )
+{
+ //note: Stealth bonus is capped at 100
+ //note: Stealth is not a perk! Stealth bonus only applies to equipment, and stacks with camouflage
+ //note: stealth bonus is not affected by terrain like the camo bonus, otherwise they're very similar
+ //note: stealth bonus also affects noise made by characters walking
+ INT8 bLoop;
+ INT16 ttl=0;
+
+ for (bLoop = HELMETPOS; bLoop <= LEGPOS; bLoop++)
+ {
+ if ( pSoldier->inv[bLoop].usItem > NONE )
+ ttl += GetStealthBonus(&pSoldier->inv[bLoop]);
+ }
+
+ return __min( ttl, 100 );
+}
diff --git a/Tactical/Items.h b/Tactical/Items.h
index 2b4c184d..1979116b 100644
--- a/Tactical/Items.h
+++ b/Tactical/Items.h
@@ -277,6 +277,8 @@ INT8 FindTrigger( SOLDIERTYPE * pSoldier );
INT8 FindRemoteControl( SOLDIERTYPE * pSoldier );
INT16 GetWornCamo( SOLDIERTYPE * pSoldier );
INT16 GetCamoBonus( OBJECTTYPE * pObj );
+INT16 GetWornStealth( SOLDIERTYPE * pSoldier );
+INT16 GetStealthBonus( OBJECTTYPE * pObj );
void ApplyEquipmentBonuses(SOLDIERTYPE * pSoldier);
diff --git a/Tactical/LOS.cpp b/Tactical/LOS.cpp
index c735e637..582827fd 100644
--- a/Tactical/LOS.cpp
+++ b/Tactical/LOS.cpp
@@ -630,7 +630,7 @@ BOOLEAN ResolveHitOnWall( STRUCTURE * pStructure, INT32 iGridNo, INT8 bLOSIndexX
* - stops at other obstacles
*
*/
-INT32 LineOfSightTest( FLOAT dStartX, FLOAT dStartY, FLOAT dStartZ, FLOAT dEndX, FLOAT dEndY, FLOAT dEndZ, UINT8 ubTileSightLimit, UINT8 ubTreeSightReduction, INT8 bAware, INT8 bCamouflage, BOOLEAN fSmell, INT16 * psWindowGridNo )
+INT32 LineOfSightTest( FLOAT dStartX, FLOAT dStartY, FLOAT dStartZ, FLOAT dEndX, FLOAT dEndY, FLOAT dEndZ, UINT8 ubTileSightLimit, UINT8 ubTreeSightReduction, INT8 bAware, INT32 bCamouflage, BOOLEAN fSmell, INT16 * psWindowGridNo )
{
// Parameters...
// the X,Y,Z triplets should be obvious
@@ -1548,8 +1548,10 @@ INT32 SoldierToSoldierLineOfSightTest( SOLDIERTYPE * pStartSoldier, SOLDIERTYPE
FLOAT dStartZPos, dEndZPos;
BOOLEAN fOk;
BOOLEAN fSmell;
- INT8 bEffectiveCamo;
+ INT16 bEffectiveCamo;
+ INT16 bEffectiveStealth;
UINT8 ubTreeReduction;
+ INT32 iTemp;
// TO ADD: if target is camouflaged and in cover, reduce sight distance by 30%
// TO ADD: if in tear gas, reduce sight limit to 2 tiles
@@ -1613,8 +1615,6 @@ INT32 SoldierToSoldierLineOfSightTest( SOLDIERTYPE * pStartSoldier, SOLDIERTYPE
if ( ( pEndSoldier->bCamo + pEndSoldier->wornCamo ) > 0 && !bAware )
{
- INT32 iTemp;
-
// reduce effects of camo of 5% per tile moved last turn
if ( pEndSoldier->ubBodyType == BLOODCAT )
{
@@ -1648,6 +1648,26 @@ INT32 SoldierToSoldierLineOfSightTest( SOLDIERTYPE * pStartSoldier, SOLDIERTYPE
bEffectiveCamo = 0;
}
+ //Madd: added - note stealth is capped at 100 just like camo
+ if ( ( GetWornStealth(pEndSoldier) ) > 0 && !bAware )
+ {
+
+ // reduce effects of stealth by 5% per tile moved last turn
+ bEffectiveStealth = max(0,min(100,(GetWornStealth(pEndSoldier)))) * (100 - pEndSoldier->bTilesMoved * 5) / 100;
+
+ bEffectiveStealth = __max( bEffectiveStealth, 0 );
+
+ // reduce visibility by up to a third for Stealth!
+ // stance and terrain don't matter
+ iTemp = ubTileSightLimit;
+ iTemp -= iTemp * (bEffectiveStealth / 3) / 100;
+ ubTileSightLimit = (UINT8) iTemp;
+ }
+ else
+ {
+ bEffectiveStealth = 0;
+ }
+
if ( TANK( pEndSoldier ) )
{
ubTreeReduction = 0;
@@ -1657,7 +1677,7 @@ INT32 SoldierToSoldierLineOfSightTest( SOLDIERTYPE * pStartSoldier, SOLDIERTYPE
ubTreeReduction = gubTreeSightReduction[ gAnimControl[pEndSoldier->usAnimState].ubEndHeight ];
}
- return( LineOfSightTest( (FLOAT) CenterX( pStartSoldier->sGridNo ), (FLOAT) CenterY( pStartSoldier->sGridNo ), dStartZPos, (FLOAT) CenterX( pEndSoldier->sGridNo ), (FLOAT) CenterY( pEndSoldier->sGridNo ), dEndZPos, ubTileSightLimit, ubTreeReduction, bAware, bEffectiveCamo, fSmell, NULL ) );
+ return( LineOfSightTest( (FLOAT) CenterX( pStartSoldier->sGridNo ), (FLOAT) CenterY( pStartSoldier->sGridNo ), dStartZPos, (FLOAT) CenterX( pEndSoldier->sGridNo ), (FLOAT) CenterY( pEndSoldier->sGridNo ), dEndZPos, ubTileSightLimit, ubTreeReduction, bAware, bEffectiveCamo + bEffectiveStealth, fSmell, NULL ) );
}
INT16 SoldierToLocationWindowTest( SOLDIERTYPE * pStartSoldier, INT16 sEndGridNo )
diff --git a/Tactical/Weapons.cpp b/Tactical/Weapons.cpp
index 1551372b..fddc4410 100644
--- a/Tactical/Weapons.cpp
+++ b/Tactical/Weapons.cpp
@@ -4144,6 +4144,17 @@ INT32 TotalArmourProtection( SOLDIERTYPE *pFirer, SOLDIERTYPE * pTarget, UINT8 u
iTotalProtection += ArmourProtection( pTarget, Item[pArmour->usItem].ubClassIndex, &(pArmour->bStatus[0]), iImpact, ubAmmoType );
if ( pArmour->bStatus[ 0 ] < USABLE )
{
+ //Madd: put any attachments that someone might have added to the armour in the merc's inventory
+ for (int bLoop = 0; bLoop < MAX_ATTACHMENTS; bLoop++)
+ {
+ OBJECTTYPE newObj;
+ CreateItem(pArmour->usAttachItem[bLoop], pArmour->bAttachStatus[bLoop], &newObj);
+ if ( !AutoPlaceObject( pTarget, &newObj, FALSE ) )
+ { // put it on the ground
+ AddItemToPool( pTarget->sGridNo, &newObj, 1, pTarget->bLevel, 0 , -1 );
+ }
+ }
+
DeleteObj( pArmour );
DirtyMercPanelInterface( pTarget, DIRTYLEVEL2 );
}
diff --git a/Tactical/opplist.cpp b/Tactical/opplist.cpp
index c63ece69..4f9b85d1 100644
--- a/Tactical/opplist.cpp
+++ b/Tactical/opplist.cpp
@@ -4671,6 +4671,9 @@ UINT8 MovementNoise( SOLDIERTYPE *pSoldier )
iStealthSkill += 25 * NUM_SKILL_TRAITS( pSoldier, STEALTHY );
}
+ if ( GetWornStealth(pSoldier) > 0 )
+ iStealthSkill += GetWornStealth(pSoldier) / 2;
+
//NumMessage("Base Stealth = ",stealthSkill);
diff --git a/Utils/XML_Items.cpp b/Utils/XML_Items.cpp
index 7a17e001..d48736a5 100644
--- a/Utils/XML_Items.cpp
+++ b/Utils/XML_Items.cpp
@@ -197,6 +197,7 @@ itemStartElementHandle(void *userData, const char *name, const char **atts)
strcmp(name, "PercentTunnelVision") == 0 ||
strcmp(name, "DefaultAttachment") == 0 ||
strcmp(name, "CamoBonus") == 0 ||
+ strcmp(name, "StealthBonus") == 0 ||
strcmp(name, "fFlags") == 0 ))
{
@@ -514,6 +515,11 @@ itemEndElementHandle(void *userData, const char *name)
pData->curElement = ELEMENT;
pData->curItem.camobonus = (INT16) atol(pData->szCharData);
}
+ else if(strcmp(name, "StealthBonus") == 0)
+ {
+ pData->curElement = ELEMENT;
+ pData->curItem.stealthbonus = (INT16) atol(pData->szCharData);
+ }
else if(strcmp(name, "PercentNoiseReduction") == 0)
{
pData->curElement = ELEMENT;
@@ -1385,6 +1391,7 @@ BOOLEAN WriteItemStats()
FilePrintf(hFile,"\t\t%d\r\n", Item[cnt].rock );
FilePrintf(hFile,"\t\t%d\r\n", Item[cnt].camobonus );
+ FilePrintf(hFile,"\t\t%d\r\n", Item[cnt].stealthbonus );
FilePrintf(hFile,"\t\t%d\r\n", Item[cnt].flakjacket );
FilePrintf(hFile,"\t\t%d\r\n", Item[cnt].leatherjacket );