diff --git a/GameSettings.cpp b/GameSettings.cpp index 88e22ed9..009bfd2e 100644 --- a/GameSettings.cpp +++ b/GameSettings.cpp @@ -1072,6 +1072,9 @@ void LoadGameExternalOptions() gGameExternalOptions.fDisplayWeaponRestingIndicator = iniReader.ReadBoolean("Tactical Gameplay Settings","WEAPON_RESTING_DISPLAY",TRUE); gGameExternalOptions.ubProneModifierPercentage = iniReader.ReadInteger("Tactical Gameplay Settings","WEAPON_RESTING_PRONE_BONI_PERCENTAGE", 50, 0, 100); + gGameExternalOptions.fScopeModes = iniReader.ReadBoolean("Tactical Gameplay Settings","USE_SCOPE_MODES", FALSE); + gGameExternalOptions.fDisplayScopeModes = iniReader.ReadBoolean("Tactical Gameplay Settings","DISPLAY_SCOPE_MODES", FALSE); + // WANNE: Externalized grid number of new merc when they arrive with the helicopter (by Jazz) gGameExternalOptions.iInitialMercArrivalLocation = iniReader.ReadInteger("Tactical Gameplay Settings","INITIAL_MERC_ARRIVAL_LOCATION", 4870 ); diff --git a/GameSettings.h b/GameSettings.h index 899de3e7..d7f0a5be 100644 --- a/GameSettings.h +++ b/GameSettings.h @@ -1076,9 +1076,14 @@ typedef struct FLOAT iCooldownModificatorLonelyBarrel; // Cooldown modificator for barrels alone in the landscape ;-) BOOLEAN fSetZeroUponNewSector; // Should loading a new sector set the temperatures of all items in the old sector to zero? (this doesn't apply to inventories) + // Flugente: Weapon Mounting BOOLEAN fWeaponResting; // Should it be possible to rest your weapon on structures in crouched position? BOOLEAN fDisplayWeaponRestingIndicator; // Should a little indicator show if the weapon is currently rested on something? UINT8 ubProneModifierPercentage; // for boni, use ubProneModifierPercentage*boni[PRONE] + (100 - ubProneModifierPercentage)*boni[CROUCHED] + + // Flugente: Scope Modes + BOOLEAN fScopeModes; // allow the player to toggle between existing scopes/sights + BOOLEAN fDisplayScopeModes; // allow display of scope modes BOOLEAN gBriefingRoom; BOOLEAN gEncyclopedia; diff --git a/Init.cpp b/Init.cpp index 5b1c960a..f4f35d88 100644 --- a/Init.cpp +++ b/Init.cpp @@ -347,7 +347,7 @@ BOOLEAN LoadExternalGameplayData(STR directoryName) strcpy(fileName, directoryName); strcat(fileName, ATTACHMENTINFOFILENAME); SGP_THROW_IFFALSE(ReadInAttachmentInfoStats(fileName),ATTACHMENTINFOFILENAME); - + strcpy(fileName, directoryName); strcat(fileName, LAUNCHABLESFILENAME); SGP_THROW_IFFALSE(ReadInLaunchableStats(fileName),LAUNCHABLESFILENAME); diff --git a/Laptop/BobbyRGuns.cpp b/Laptop/BobbyRGuns.cpp index daba1cfe..f55d4556 100644 --- a/Laptop/BobbyRGuns.cpp +++ b/Laptop/BobbyRGuns.cpp @@ -2490,7 +2490,7 @@ void CreateMouseRegionForBigImage( UINT16 usPosY, UINT8 ubCount, INT16 *pItemNum UINT16 usAttachment; CreateItem(pItemNumbers[ i ], 100, &pObject); - UINT16 ubAttackAPs = BaseAPsToShootOrStab( APBPConstants[DEFAULT_APS], APBPConstants[DEFAULT_AIMSKILL], &pObject ); + UINT16 ubAttackAPs = BaseAPsToShootOrStab( APBPConstants[DEFAULT_APS], APBPConstants[DEFAULT_AIMSKILL], &pObject, NULL ); if ( Weapon[ pItemNumbers[ i ] ].NoSemiAuto ) swprintf( apStr, L"-" ); @@ -2499,7 +2499,7 @@ void CreateMouseRegionForBigImage( UINT16 usPosY, UINT8 ubCount, INT16 *pItemNum if (GetShotsPerBurst(&pObject) > 0) { - swprintf( apStr2, L" / %d", ubAttackAPs + CalcAPsToBurst( APBPConstants[DEFAULT_APS], &pObject ) ); + swprintf( apStr2, L" / %d", ubAttackAPs + CalcAPsToBurst( APBPConstants[DEFAULT_APS], &pObject, NULL ) ); wcscat( apStr, apStr2 ); } else @@ -2507,7 +2507,7 @@ void CreateMouseRegionForBigImage( UINT16 usPosY, UINT8 ubCount, INT16 *pItemNum if (GetAutofireShotsPerFiveAPs(&pObject) > 0) { - swprintf( apStr2, L" / %d", ubAttackAPs + CalcAPsToAutofire( APBPConstants[DEFAULT_APS], &pObject, 3 ) ); + swprintf( apStr2, L" / %d", ubAttackAPs + CalcAPsToAutofire( APBPConstants[DEFAULT_APS], &pObject, 3, NULL ) ); wcscat( apStr, apStr2 ); } else diff --git a/SaveLoadGame.cpp b/SaveLoadGame.cpp index 7886a3b9..735b048a 100644 --- a/SaveLoadGame.cpp +++ b/SaveLoadGame.cpp @@ -1971,6 +1971,7 @@ BOOLEAN SOLDIERTYPE::Load(HWFILE hFile) numBytesRead = ReadFieldByField(hFile, &this->snowCamo, sizeof(snowCamo), sizeof(INT8), numBytesRead); numBytesRead = ReadFieldByField(hFile, &this->wornSnowCamo, sizeof(wornSnowCamo), sizeof(INT8), numBytesRead); numBytesRead = ReadFieldByField(hFile, &this->sFacilityTypeOperated, sizeof(sFacilityTypeOperated), sizeof(INT16), numBytesRead); + numBytesRead = ReadFieldByField(hFile, &this->bScopeMode, sizeof(bScopeMode), sizeof(INT8), numBytesRead); #ifdef JA2UB numBytesRead = ReadFieldByField(hFile, &this->fIgnoreGetupFromCollapseCheck, sizeof(fIgnoreGetupFromCollapseCheck), sizeof(BOOLEAN), numBytesRead); numBytesRead = ReadFieldByField(hFile, &this->GetupFromJA25StartCounter, sizeof(GetupFromJA25StartCounter), sizeof(TIMECOUNTER), numBytesRead); diff --git a/Tactical/Handle Items.cpp b/Tactical/Handle Items.cpp index 61ca2b79..e60d507c 100644 --- a/Tactical/Handle Items.cpp +++ b/Tactical/Handle Items.cpp @@ -1496,8 +1496,12 @@ void HandleSoldierDropBomb( SOLDIERTYPE *pSoldier, INT32 sGridNo ) if ( iResult >= 0 ) { - // EXPLOSIVES GAIN (25): Place a bomb, or buried and armed a mine - StatChange( pSoldier, EXPLODEAMT, 25, FALSE ); + // Less explosives gain for placing tripwire + if ( Item[ pSoldier->inv[ HANDPOS ].usItem ].tripwire == 1 ) + StatChange( pSoldier, EXPLODEAMT, 5, FALSE ); + else + // EXPLOSIVES GAIN (25): Place a bomb, or buried and armed a mine + StatChange( pSoldier, EXPLODEAMT, 25, FALSE ); // SANDRO - STOMP traits - Demolitions bonus to trap level if ( gGameOptions.fNewTraitSystem && HAS_SKILL_TRAIT( pSoldier, DEMOLITIONS_NT )) @@ -4380,8 +4384,12 @@ void BombMessageBoxCallBack( UINT8 ubExitValue ) if ( iResult >= 0 ) { - // EXPLOSIVES GAIN (25): Place a bomb, or buried and armed a mine - StatChange( gpTempSoldier, EXPLODEAMT, 25, FALSE ); + // Less explosives gain for placing tripwire + if ( Item[ gpTempSoldier->inv[ HANDPOS ].usItem ].tripwire == 1 ) + StatChange( gpTempSoldier, EXPLODEAMT, 5, FALSE ); + else + // EXPLOSIVES GAIN (25): Place a bomb, or buried and armed a mine + StatChange( gpTempSoldier, EXPLODEAMT, 25, FALSE ); } else { diff --git a/Tactical/Interface Enhanced.cpp b/Tactical/Interface Enhanced.cpp index 566831f5..e5dfb9ce 100644 --- a/Tactical/Interface Enhanced.cpp +++ b/Tactical/Interface Enhanced.cpp @@ -1593,7 +1593,7 @@ void InternalInitEDBTooltipRegion( OBJECTTYPE * gpItemDescObject, UINT32 guiCurr /////////////////// OCTH MINIMUM RANGE FOR AIMING BONUS if( UsingNewCTHSystem() == false && - ( Item[gpItemDescObject->usItem].minrangeforaimbonus > 0 || GetMinRangeForAimBonus( gpItemDescObject ) > 0 ) ) + ( Item[gpItemDescObject->usItem].minrangeforaimbonus > 0 || GetMinRangeForAimBonus( NULL, gpItemDescObject ) > 0 ) ) { ubRegionOffset = 5; MSYS_EnableRegion( &gUDBFasthelpRegions[ iFirstDataRegion + ubRegionOffset ] ); @@ -2644,7 +2644,7 @@ void InternalInitEDBTooltipRegion( OBJECTTYPE * gpItemDescObject, UINT32 guiCurr ///////////////////// AIM BONUS MODIFIER if(UsingNewCTHSystem() == false) { - if ( GetAimBonus( gpItemDescObject, 100, 1 ) != 0 ) + if ( GetAimBonus( NULL, gpItemDescObject, 100, 1 ) != 0 ) { if (cnt >= sFirstLine && cnt < sLastLine) { @@ -3097,7 +3097,7 @@ void InternalInitEDBTooltipRegion( OBJECTTYPE * gpItemDescObject, UINT32 guiCurr } ///////////////////// SINGLE-ATTACK AP MODIFIER - if (GetPercentAPReduction( gpItemDescObject ) != 0 ) + if (GetPercentAPReduction( NULL, gpItemDescObject ) != 0 ) { if (cnt >= sFirstLine && cnt < sLastLine) { @@ -3955,7 +3955,7 @@ void DrawWeaponStats( OBJECTTYPE * gpItemDescObject ) //////////////////// OCTH MINIMUM RANGE FOR AIMING BONUS if( UsingNewCTHSystem() == false && - ( Item[gpItemDescObject->usItem].minrangeforaimbonus > 0 || GetMinRangeForAimBonus( gpItemDescObject ) > 0 ) ) + ( Item[gpItemDescObject->usItem].minrangeforaimbonus > 0 || GetMinRangeForAimBonus( NULL, gpItemDescObject ) > 0 ) ) { ubNumLine = 5; BltVideoObjectFromIndex( guiSAVEBUFFER, guiItemInfoWeaponIcon, 27, gItemDescGenRegions[ubNumLine][0].sLeft+sOffsetX, gItemDescGenRegions[ubNumLine][0].sTop+sOffsetY, VO_BLT_SRCTRANSPARENCY, NULL ); @@ -4793,7 +4793,7 @@ void DrawAdvancedStats( OBJECTTYPE * gpItemDescObject ) } ///////////////////// SINGLE-ATTACK AP MODIFIER - if (GetPercentAPReduction( gpItemDescObject ) != 0 ) + if (GetPercentAPReduction( NULL, gpItemDescObject ) != 0 ) { if (cnt >= sFirstLine && cnt < sLastLine) { @@ -5372,7 +5372,7 @@ void DrawWeaponValues( OBJECTTYPE * gpItemDescObject ) return; // ShotsPer4Turns -> ubAttackAPs, used later for all shot AP values - ubAttackAPs = BaseAPsToShootOrStab( APBPConstants[DEFAULT_APS], APBPConstants[DEFAULT_AIMSKILL], gpItemDescObject ); + ubAttackAPs = BaseAPsToShootOrStab( APBPConstants[DEFAULT_APS], APBPConstants[DEFAULT_AIMSKILL], gpItemDescObject, NULL ); INT16 ubBasicAttackAPs = BaseAPsToShootOrStabNoModifier( APBPConstants[DEFAULT_APS], APBPConstants[DEFAULT_AIMSKILL], gpItemDescObject ); ////////////////////////////////////////////////////////////////////////////////////////// @@ -5905,7 +5905,7 @@ void DrawWeaponValues( OBJECTTYPE * gpItemDescObject ) /////////////////// OCTH MINIMUM RANGE FOR AIMING BONUS if( UsingNewCTHSystem() == false && - ( Item[gpItemDescObject->usItem].minrangeforaimbonus > 0 || GetMinRangeForAimBonus( gpItemDescObject ) > 0 ) ) + ( Item[gpItemDescObject->usItem].minrangeforaimbonus > 0 || GetMinRangeForAimBonus( NULL, gpItemDescObject ) > 0 ) ) { // Set line to draw into ubNumLine = 5; @@ -5917,7 +5917,7 @@ void DrawWeaponValues( OBJECTTYPE * gpItemDescObject ) INT16 iMinRangeForAimBonusValue = Item[gpItemDescObject->usItem].minrangeforaimbonus / 10; // Get final Minimum Range For Aim Bonus value - INT16 iFinalMinRangeForAimBonusValue = GetMinRangeForAimBonus(gpItemDescObject) / 10; + INT16 iFinalMinRangeForAimBonusValue = GetMinRangeForAimBonus( NULL, gpItemDescObject ) / 10; // Get Minimum Range For Aim Bonus modifier INT16 iMinRangeForAimBonusModifier = iFinalMinRangeForAimBonusValue - iMinRangeForAimBonusValue; @@ -6571,7 +6571,7 @@ void DrawWeaponValues( OBJECTTYPE * gpItemDescObject ) sHeight = gItemDescGenRegions[ubNumLine][1].sBottom - sTop; // Get final Burst Cost - INT16 iFinalBurstAPCost = ubAttackAPs + CalcAPsToBurst( APBPConstants[DEFAULT_APS], gpItemDescObject ); + INT16 iFinalBurstAPCost = ubAttackAPs + CalcAPsToBurst( APBPConstants[DEFAULT_APS], gpItemDescObject, NULL ); // Get base Burst Cost INT16 iBurstAPCost = ubBasicAttackAPs + CalcAPsToBurstNoModifier( APBPConstants[DEFAULT_APS], gpItemDescObject ); @@ -6637,7 +6637,7 @@ void DrawWeaponValues( OBJECTTYPE * gpItemDescObject ) sHeight = gItemDescGenRegions[ubNumLine][1].sBottom - sTop; // Get final Autofire Cost - INT16 iFinalAutoAPCost = ubAttackAPs + CalcAPsToAutofire( APBPConstants[DEFAULT_APS], gpItemDescObject, 3 ); + INT16 iFinalAutoAPCost = ubAttackAPs + CalcAPsToAutofire( APBPConstants[DEFAULT_APS], gpItemDescObject, 3, NULL ); // Get base Autofire Cost INT16 iAutoAPCost = ubBasicAttackAPs + CalcAPsToAutofireNoModifier( APBPConstants[DEFAULT_APS], gpItemDescObject, 3 ); @@ -8578,7 +8578,7 @@ void DrawAdvancedValues( OBJECTTYPE *gpItemDescObject ) ///////////////////// AIM BONUS MODIFIER if(UsingNewCTHSystem() == false) { - iModifier[0] = GetAimBonus( gpItemDescObject, 100, 1 ); + iModifier[0] = GetAimBonus( NULL, gpItemDescObject, 100, 1 ); iModifier[1] = 0; iModifier[2] = 0; if (iModifier[0] != 0 || iModifier[1] != 0 || iModifier[2] != 0) @@ -9506,7 +9506,7 @@ void DrawAdvancedValues( OBJECTTYPE *gpItemDescObject ) } ///////////////////// SINGLE-ATTACK AP MODIFIER - iModifier[0] = GetPercentAPReduction( gpItemDescObject ); + iModifier[0] = GetPercentAPReduction( NULL, gpItemDescObject ); iModifier[1] = iModifier[0]; iModifier[2] = iModifier[0]; if (iModifier[0] != 0 ) diff --git a/Tactical/Interface Items.cpp b/Tactical/Interface Items.cpp index 32697554..944cd43f 100644 --- a/Tactical/Interface Items.cpp +++ b/Tactical/Interface Items.cpp @@ -262,6 +262,10 @@ UINT32 guiItemDescBoxBackground; UINT32 guiItemInfoLBEBackground; UINT32 guiMapItemDescBox; UINT32 guiAttachmentSlot; + +// HEADROCK HAM 4: This replaces "Misc" icons. +extern UINT32 guiItemInfoAdvancedIcon; // added here by Flugente + UINT32 guiItemGraphic; UINT32 guiMoneyGraphicsForDescBox; UINT32 guiBullet; @@ -667,8 +671,17 @@ void GenerateProsString( STR16 zItemPros, OBJECTTYPE * pObject, UINT32 uiPixLimi ubWeight = ubWeight + Item[ (*pObject)[0]->data.gun.usGunAmmoItem ].ubWeight; } + // Flugente: If overheating is allowed, an overheated gun receives a slight malus to accuracy + FLOAT accuracyheatmultiplicator = 1.0; + if ( gGameOptions.fWeaponOverheating ) + { + FLOAT overheatdamagepercentage = GetGunOverheatDamagePercentage( pObject ); + FLOAT accuracymalus = (FLOAT)((max(1.0, overheatdamagepercentage) - 1.0) * 0.1); + accuracyheatmultiplicator = (FLOAT)max(0.0, 1.0 - accuracymalus); + } + //CHRISL: TODO - This needs to be updated for NCTH - if (Weapon[usItem].bAccuracy >= EXCEPTIONAL_ACCURACY ) + if (accuracyheatmultiplicator * Weapon[usItem].bAccuracy >= EXCEPTIONAL_ACCURACY ) { zTemp = Message[STR_ACCURATE]; if ( ! AttemptToAddSubstring( zItemPros, zTemp, &uiStringLength, uiPixLimit ) ) @@ -730,7 +743,7 @@ void GenerateProsString( STR16 zItemPros, OBJECTTYPE * pObject, UINT32 uiPixLimi } } - if (BaseAPsToShootOrStab( APBPConstants[DEFAULT_APS], APBPConstants[DEFAULT_AIMSKILL], gpItemDescObject ) <= EXCEPTIONAL_AP_COST) + if (BaseAPsToShootOrStab( APBPConstants[DEFAULT_APS], APBPConstants[DEFAULT_AIMSKILL], gpItemDescObject, NULL ) <= EXCEPTIONAL_AP_COST) { zTemp = Message[STR_QUICK_FIRING]; if ( ! AttemptToAddSubstring( zItemPros, zTemp, &uiStringLength, uiPixLimit ) ) @@ -803,8 +816,17 @@ void GenerateConsString( STR16 zItemCons, OBJECTTYPE * pObject, UINT32 uiPixLimi zItemCons[0] = 0; + // Flugente: If overheating is allowed, an overheated gun receives a slight malus to accuracy + FLOAT accuracyheatmultiplicator = 1.0; + if ( gGameOptions.fWeaponOverheating ) + { + FLOAT overheatdamagepercentage = GetGunOverheatDamagePercentage( pObject ); + FLOAT accuracymalus = (FLOAT)((max(1.0, overheatdamagepercentage) - 1.0) * 0.1); + accuracyheatmultiplicator = (FLOAT)max(0.0, 1.0 - accuracymalus); + } + //CHRISL: TODO - This needs to be updated for NCTH - if (Weapon[usItem].bAccuracy <= BAD_ACCURACY) + if (accuracyheatmultiplicator * Weapon[usItem].bAccuracy <= BAD_ACCURACY) { zTemp = Message[STR_INACCURATE]; if ( ! AttemptToAddSubstring( zItemCons, zTemp, &uiStringLength, uiPixLimit ) ) @@ -858,7 +880,7 @@ void GenerateConsString( STR16 zItemCons, OBJECTTYPE * pObject, UINT32 uiPixLimi } } - if (BaseAPsToShootOrStab( APBPConstants[DEFAULT_APS], APBPConstants[DEFAULT_AIMSKILL], gpItemDescObject ) >= APBPConstants[BAD_AP_COST]) + if (BaseAPsToShootOrStab( APBPConstants[DEFAULT_APS], APBPConstants[DEFAULT_AIMSKILL], gpItemDescObject, NULL ) >= APBPConstants[BAD_AP_COST]) { zTemp = Message[STR_SLOW_FIRING]; if ( ! AttemptToAddSubstring( zItemCons, zTemp, &uiStringLength, uiPixLimit ) ) @@ -2043,7 +2065,7 @@ void addWeaponGroupsToPocketPopup( SOLDIERTYPE *pSoldier, INT16 sPocket, POPUP* void addGrenadesToPocketPopup( SOLDIERTYPE *pSoldier, INT16 sPocket, POPUP* popup ){ addItemsToPocketPopup( pSoldier, sPocket, popup, IC_GRENADE, -1, -1, 0 ); - addItemsToPocketPopup( pSoldier, sPocket, popup, IC_GRENADE, -1, -1, AC_DEFAULT1 ); + addItemsToPocketPopup( pSoldier, sPocket, popup, IC_GRENADE, -1, -1, AC_GRENADE ); } @@ -3747,22 +3769,77 @@ void INVRenderItem( UINT32 uiBuffer, SOLDIERTYPE * pSoldier, OBJECTTYPE *pObjec { SetRGBFontForeground( 95, 160, 154 ); - sNewY = sY + sHeight - 24; + sNewY = sY; swprintf( pStr, L"M" ); // Get length of string uiStringLength=StringPixLength(pStr, ITEM_FONT ); - sNewX = sX + 25; + sNewX = sX + sWidth - uiStringLength - 13; if ( uiBuffer == guiSAVEBUFFER ) { - RestoreExternBackgroundRect( sNewX, sNewY, 15, 15 ); + RestoreExternBackgroundRect( sNewX, sNewY, uiStringLength, 15 ); } mprintf( sNewX, sNewY, pStr ); gprintfinvalidate( sNewX, sNewY, pStr ); } + if ( gGameExternalOptions.fScopeModes && gGameExternalOptions.fDisplayScopeModes && pSoldier && pObject == &(pSoldier->inv[HANDPOS] ) && Item[pSoldier->inv[HANDPOS].usItem].usItemClass == IC_GUN ) + { + sNewX = sX + 5; // rather arbitrary + sNewY = sY; + + // added by Flugente + // HEADROCK HAM 4: Advanced Icons + VOBJECT_DESC VObjectDesc; + VObjectDesc.fCreateFlags = VOBJECT_CREATE_FROMFILE; + strcpy( VObjectDesc.ImageFile, "INTERFACE\\ItemInfoAdvancedIcons.STI" ); + AddStandardVideoObject( &VObjectDesc, &guiItemInfoAdvancedIcon ); + + std::map ObjList; + GetScopeLists(&pSoldier->inv[HANDPOS], ObjList); + + if ( IsAttachmentClass(ObjList[pSoldier->bScopeMode]->usItem, AC_SCOPE ) ) + { + BltVideoObjectFromIndex( guiSAVEBUFFER, guiItemInfoAdvancedIcon, 54, sNewX, sNewY, VO_BLT_TRANSSHADOW, NULL ); + + SetFontForeground( FONT_ORANGE ); + FLOAT scopemagfactor = Item[ObjList[pSoldier->bScopeMode]->usItem].scopemagfactor; + + INT16 sMagX = sNewX + 9; + + if ( scopemagfactor > (INT8)scopemagfactor ) + swprintf( pStr, L"%2.1f", scopemagfactor ); + else + { + swprintf( pStr, L"%2.0f", scopemagfactor ); + } + + // Get length of string + uiStringLength=StringPixLength(pStr, ITEM_FONT ); + + if ( uiBuffer == guiSAVEBUFFER ) + { + RestoreExternBackgroundRect( sNewX, sNewY, 20, 15 ); + } + mprintf( sMagX, sNewY, pStr ); + gprintfinvalidate( sMagX, sNewY, pStr ); + } + else if ( IsAttachmentClass(ObjList[pSoldier->bScopeMode]->usItem, AC_SIGHT ) ) + { + BltVideoObjectFromIndex( guiSAVEBUFFER, guiItemInfoAdvancedIcon, 53, sNewX, sNewY, VO_BLT_TRANSSHADOW, NULL ); + + RestoreExternBackgroundRect( sNewX, sNewY, 15, 15 ); + } + else + { + BltVideoObjectFromIndex( guiSAVEBUFFER, guiItemInfoAdvancedIcon, 52, sNewX, sNewY, VO_BLT_TRANSSHADOW, NULL ); + + RestoreExternBackgroundRect( sNewX, sNewY, 15, 15 ); + } + } + if((UsingNewInventorySystem() == true)) { // CHRISL: Display astrisk when LBENODE active @@ -6654,7 +6731,7 @@ void RenderItemDescriptionBox( ) mprintf( gODBItemDescRegions[3][6].sLeft, gODBItemDescRegions[3][6].sTop, L"%s", gWeaponStatsDesc[ 8 ] ); } - ubAttackAPs = BaseAPsToShootOrStab( APBPConstants[DEFAULT_APS], APBPConstants[DEFAULT_AIMSKILL], gpItemDescObject ); + ubAttackAPs = BaseAPsToShootOrStab( APBPConstants[DEFAULT_APS], APBPConstants[DEFAULT_AIMSKILL], gpItemDescObject, NULL ); // WANNE: Fixed CTD when trowing an item with open description box if (ubAttackAPs != -1 ) @@ -6686,14 +6763,14 @@ void RenderItemDescriptionBox( ) { SetFontForeground( 5 ); } - swprintf( pStr, L"%2d", ubAttackAPs + CalcAPsToBurst( APBPConstants[DEFAULT_APS], gpItemDescObject ) ); + swprintf( pStr, L"%2d", ubAttackAPs + CalcAPsToBurst( APBPConstants[DEFAULT_APS], gpItemDescObject, NULL ) ); FindFontRightCoordinates( gODBItemDescRegions[3][7].sLeft, gODBItemDescRegions[3][7].sTop, gODBItemDescRegions[3][7].sRight - gODBItemDescRegions[3][7].sLeft, gODBItemDescRegions[3][7].sBottom - gODBItemDescRegions[3][7].sTop ,pStr, BLOCKFONT2, &usX, &usY); mprintf( usX, usY, pStr ); } else if (GetAutofireShotsPerFiveAPs(gpItemDescObject) > 0) { SetFontForeground( 5 ); - swprintf( pStr, L"%2d", ubAttackAPs + CalcAPsToAutofire( APBPConstants[DEFAULT_APS], gpItemDescObject, 3 ) ); + swprintf( pStr, L"%2d", ubAttackAPs + CalcAPsToAutofire( APBPConstants[DEFAULT_APS], gpItemDescObject, 3, NULL ) ); FindFontRightCoordinates( gODBItemDescRegions[3][7].sLeft, gODBItemDescRegions[3][7].sTop, gODBItemDescRegions[3][7].sRight - gODBItemDescRegions[3][7].sLeft, gODBItemDescRegions[3][7].sBottom - gODBItemDescRegions[3][7].sTop ,pStr, BLOCKFONT2, &usX, &usY); mprintf( usX, usY, pStr ); } @@ -11115,7 +11192,7 @@ void GetHelpTextForItem( STR16 pzStr, OBJECTTYPE *pObject, SOLDIERTYPE *pSoldier //Calculate AP's CHAR16 apStr[20]; CHAR16 apStr2[20]; - INT16 ubAttackAPs = BaseAPsToShootOrStab( APBPConstants[DEFAULT_APS], APBPConstants[DEFAULT_AIMSKILL], pObject ); + INT16 ubAttackAPs = BaseAPsToShootOrStab( APBPConstants[DEFAULT_APS], APBPConstants[DEFAULT_AIMSKILL], pObject, pSoldier ); if ( Weapon[ usItem ].NoSemiAuto ) { @@ -11128,7 +11205,7 @@ void GetHelpTextForItem( STR16 pzStr, OBJECTTYPE *pObject, SOLDIERTYPE *pSoldier if (GetShotsPerBurst(pObject) > 0) { - swprintf( apStr2, L" / %d", ubAttackAPs + CalcAPsToBurst( APBPConstants[DEFAULT_APS], pObject ) ); + swprintf( apStr2, L" / %d", ubAttackAPs + CalcAPsToBurst( APBPConstants[DEFAULT_APS], pObject, NULL ) ); wcscat( apStr, apStr2 ); } else @@ -11138,7 +11215,7 @@ void GetHelpTextForItem( STR16 pzStr, OBJECTTYPE *pObject, SOLDIERTYPE *pSoldier if (GetAutofireShotsPerFiveAPs(pObject) > 0) { - swprintf( apStr2, L" / %d", ubAttackAPs + CalcAPsToAutofire( APBPConstants[DEFAULT_APS], pObject, 3 ) ); + swprintf( apStr2, L" / %d", ubAttackAPs + CalcAPsToAutofire( APBPConstants[DEFAULT_APS], pObject, 3, NULL ) ); wcscat( apStr, apStr2 ); } else @@ -11146,10 +11223,20 @@ void GetHelpTextForItem( STR16 pzStr, OBJECTTYPE *pObject, SOLDIERTYPE *pSoldier wcscat( apStr, L" / -" ); } + // Flugente: If overheating is allowed, an overheated gun receives a slight malus to accuracy + FLOAT accuracyheatmultiplicator = 1.0; + if ( gGameOptions.fWeaponOverheating ) + { + FLOAT overheatdamagepercentage = GetGunOverheatDamagePercentage( pObject ); + FLOAT accuracymalus = (FLOAT)((max(1.0, overheatdamagepercentage) - 1.0) * 0.1); + accuracyheatmultiplicator = (FLOAT)max(0.0, 1.0 - accuracymalus); + } + //Info for weapons //swprintf( pStr, L"%s (%s) [%d%%]\n%s %d\n%s %d\n%s %d (%d)\n%s %s\n%s %1.1f %s", INT8 accuracy = (UsingNewCTHSystem()==true?Weapon[ usItem ].nAccuracy:Weapon[ usItem ].bAccuracy); + accuracy *= accuracyheatmultiplicator; #ifdef CHINESE swprintf( pStr, ChineseSpecString4, #else @@ -11182,13 +11269,13 @@ void GetHelpTextForItem( STR16 pzStr, OBJECTTYPE *pObject, SOLDIERTYPE *pSoldier //Calculate AP's CHAR16 apStr[20]; CHAR16 apStr2[20]; - INT16 ubAttackAPs = BaseAPsToShootOrStab( APBPConstants[DEFAULT_APS], APBPConstants[DEFAULT_AIMSKILL], pObject ); + INT16 ubAttackAPs = BaseAPsToShootOrStab( APBPConstants[DEFAULT_APS], APBPConstants[DEFAULT_AIMSKILL], pObject, pSoldier ); swprintf( apStr, L"%d", ubAttackAPs ); if (GetShotsPerBurst(pObject) > 0) { - swprintf( apStr2, L" / %d", ubAttackAPs + CalcAPsToBurst( APBPConstants[DEFAULT_APS], pObject ) ); + swprintf( apStr2, L" / %d", ubAttackAPs + CalcAPsToBurst( APBPConstants[DEFAULT_APS], pObject, NULL ) ); wcscat( apStr, apStr2 ); } else @@ -11198,7 +11285,7 @@ void GetHelpTextForItem( STR16 pzStr, OBJECTTYPE *pObject, SOLDIERTYPE *pSoldier if (GetAutofireShotsPerFiveAPs(pObject) > 0) { - swprintf( apStr2, L" / %d", ubAttackAPs + CalcAPsToAutofire( APBPConstants[DEFAULT_APS], pObject, 3 ) ); + swprintf( apStr2, L" / %d", ubAttackAPs + CalcAPsToAutofire( APBPConstants[DEFAULT_APS], pObject, 3, NULL ) ); wcscat( apStr, apStr2 ); } else @@ -11206,8 +11293,18 @@ void GetHelpTextForItem( STR16 pzStr, OBJECTTYPE *pObject, SOLDIERTYPE *pSoldier wcscat( apStr, L" / -" ); } + // Flugente: If overheating is allowed, an overheated gun receives a slight malus to accuracy + FLOAT accuracyheatmultiplicator = 1.0; + if ( gGameOptions.fWeaponOverheating ) + { + FLOAT overheatdamagepercentage = GetGunOverheatDamagePercentage( pObject ); + FLOAT accuracymalus = (FLOAT)((max(1.0, overheatdamagepercentage) - 1.0) * 0.1); + accuracyheatmultiplicator = (FLOAT)max(0.0, 1.0 - accuracymalus); + } + //Info for weapons INT8 accuracy = (UsingNewCTHSystem()==true?Weapon[ usItem ].nAccuracy:Weapon[ usItem ].bAccuracy); + accuracy *= accuracyheatmultiplicator; #ifdef CHINESE swprintf( pStr, L"%s [%d%ге]\n%s %d\n%s %d\n%s %d (%d)\n%s %s\n%s %1.1f %s", @@ -11248,7 +11345,7 @@ void GetHelpTextForItem( STR16 pzStr, OBJECTTYPE *pObject, SOLDIERTYPE *pSoldier gWeaponStatsDesc[ 11 ], //Damage String GetDamage(pObject), //Melee damage gWeaponStatsDesc[ 6 ], //AP String - BaseAPsToShootOrStab( APBPConstants[DEFAULT_APS], APBPConstants[DEFAULT_AIMSKILL], pObject ), //AP's + BaseAPsToShootOrStab( APBPConstants[DEFAULT_APS], APBPConstants[DEFAULT_AIMSKILL], pObject, pSoldier ), //AP's gWeaponStatsDesc[ 12 ], //Weight String fWeight, //Weight GetWeightUnitString() //Weight units diff --git a/Tactical/Item Types.h b/Tactical/Item Types.h index e8e85a67..a2fe053b 100644 --- a/Tactical/Item Types.h +++ b/Tactical/Item Types.h @@ -620,26 +620,27 @@ extern OBJECTTYPE gTempObject; #define IC_MAPFILTER_MISC ( IC_TENTACLES | IC_KEY | IC_MISC | IC_MONEY | IC_NONE ) #define IC_MAPFILTER_ALL ( IC_MAPFILTER_GUN | IC_MAPFILTER_AMMO | IC_MAPFILTER_EXPLOSV | IC_MAPFILTER_MELEE | IC_MAPFILTER_KIT | IC_MAPFILTER_LBE | IC_MAPFILTER_ARMOR | IC_MAPFILTER_MISC ) +// redesigned by Flugente on 12-04-10 // Chrisl: Define attachment classes -#define AC_DEFAULT1 0x00000001 //1 -#define AC_BARREL 0x00000002 //2 +#define AC_BIPOD 0x00000001 //1 +#define AC_SUPPRESSOR 0x00000002 //2 #define AC_LASER 0x00000004 //4 #define AC_SIGHT 0x00000008 //8 #define AC_SCOPE 0x00000010 //16 #define AC_STOCK 0x00000020 //32 -#define AC_AMMO 0x00000040 //64 +#define AC_MAGWELL 0x00000040 //64 #define AC_INTERNAL 0x00000080 //128 #define AC_EXTERNAL 0x00000100 //256 #define AC_UNDERBARREL 0x00000200 //512 #define AC_GRENADE 0x00000400 //1024 #define AC_ROCKET 0x00000800 //2048 -#define AC_MISC1 0x00001000 //4096 -#define AC_MISC2 0x00002000 //8192 -#define AC_MISC3 0x00004000 //16384 -#define AC_MISC4 0x00008000 //32768 -#define AC_MISC5 0x00010000 //65536 -#define AC_MISC6 0x00020000 //131072 -#define AC_MISC7 0x00040000 //262144 +#define AC_FOREGRIP 0x00001000 //4096 +#define AC_HELMET 0x00002000 //8192 +#define AC_VEST 0x00004000 //16384 +#define AC_PANTS 0x00008000 //32768 +#define AC_DETONATOR 0x00010000 //65536 +#define AC_BATTERY 0x00020000 //131072 +#define AC_EXTENDER 0x00040000 //262144 #define AC_MISC8 0x00080000 //524288 #define AC_MISC9 0x00100000 //1048576 #define AC_MISC10 0x00200000 //2097152 @@ -654,6 +655,7 @@ extern OBJECTTYPE gTempObject; #define AC_MISC19 0x40000000 //1073741824 #define AC_MISC20 0x80000000 //2147483648 + // replaces candamage //#define ITEM_DAMAGEABLE 0x0001 //// replaces canrepair @@ -816,7 +818,7 @@ typedef struct BOOLEAN robotremotecontrol; BOOLEAN camouflagekit; BOOLEAN locksmithkit; - BOOLEAN mine; + BOOLEAN mine; BOOLEAN alcohol; BOOLEAN hardware; BOOLEAN medical; @@ -885,7 +887,12 @@ typedef struct FLOAT overheatTemperatureModificator; // percentage modifier of heat a shot generates (read from attachments) FLOAT overheatCooldownModificator; // percentage modifier of cooldown amount (read from attachments, applies to guns & barrels) FLOAT overheatJamThresholdModificator; // percentage modifier of a gun's jam threshold (read from attachments) - FLOAT overheatDamageThresholdModificator; // percentage modifier of a gun's damage threshold (read from attachments) + FLOAT overheatDamageThresholdModificator; // percentage modifier of a gun's damage threshold (read from attachments) + + UINT32 attachmentclass; // attachmentclass used + + BOOLEAN tripwireactivation; // item (mine) can be activated by nearby tripwire + BOOLEAN tripwire; // item is tripwire } INVTYPE; @@ -1450,7 +1457,7 @@ typedef struct UINT32 uiIndex; } AttachmentInfoStruct; -extern AttachmentInfoStruct AttachmentInfo[MAXITEMS+1]; +extern AttachmentInfoStruct AttachmentInfo[MAXITEMS+1]; extern UINT16 Launchable[MAXITEMS+1][2]; extern UINT16 CompatibleFaceItems[MAXITEMS+1][2]; diff --git a/Tactical/Items.cpp b/Tactical/Items.cpp index 5d57aa31..53b7b017 100644 --- a/Tactical/Items.cpp +++ b/Tactical/Items.cpp @@ -2220,7 +2220,7 @@ BOOLEAN ValidItemAttachmentSlot( OBJECTTYPE * pObj, UINT16 usAttachment, BOOLEAN //Search for incompatible attachments for(int i = 0;ibDoBurst = TRUE; pSoldier->bDoAutofire = TRUE; } + pSoldier->bScopeMode = USE_BEST_SCOPE; } // Lesh: end @@ -7528,6 +7529,7 @@ BOOLEAN OBJECTTYPE::RemoveAttachment( OBJECTTYPE* pAttachment, OBJECTTYPE * pNew pSoldier->bDoBurst = TRUE; pSoldier->bDoAutofire = 1; } + pSoldier->bScopeMode = USE_BEST_SCOPE; } } @@ -9036,16 +9038,30 @@ INT16 GetItemAimBonus( const INVTYPE* pItem, INT32 iRange, INT16 ubAimTime ) return(bonus); } -INT16 GetAimBonus( OBJECTTYPE * pObj, INT32 iRange, INT16 ubAimTime ) +INT16 GetAimBonus( SOLDIERTYPE * pSoldier, OBJECTTYPE * pObj, INT32 iRange, INT16 ubAimTime ) { INT16 bonus = 0; - if (pObj->exists() == true) { - bonus = BonusReduceMore( GetItemAimBonus( &Item[pObj->usItem], iRange, ubAimTime ), (*pObj)[0]->data.objectStatus ); + if (pObj->exists() == true) + { + // Flugente: check for scope mode + if ( gGameExternalOptions.fScopeModes && pSoldier && Item[pObj->usItem].usItemClass == IC_GUN ) + { + std::map ObjList; + GetScopeLists(pObj, ObjList); + + bonus = BonusReduceMore( GetItemAimBonus( &Item[ObjList[pSoldier->bScopeMode]->usItem], iRange, ubAimTime ), (*ObjList[pSoldier->bScopeMode])[0]->data.objectStatus ); + } + else + bonus = BonusReduceMore( GetItemAimBonus( &Item[pObj->usItem], iRange, ubAimTime ), (*pObj)[0]->data.objectStatus ); + bonus += GetItemAimBonus( &Item[(*pObj)[0]->data.gun.usGunAmmoItem], iRange, ubAimTime ); - for (attachmentList::iterator iter = (*pObj)[0]->attachments.begin(); iter != (*pObj)[0]->attachments.end(); ++iter) { - if(iter->exists()){ + attachmentList::iterator iterend = (*pObj)[0]->attachments.end(); + for (attachmentList::iterator iter = (*pObj)[0]->attachments.begin(); iter != iterend; ++iter) + { + if(iter->exists() && !IsAttachmentClass(iter->usItem, AC_SCOPE|AC_SIGHT ) ) + { bonus += BonusReduceMore( GetItemAimBonus( &Item[iter->usItem], iRange, ubAimTime ), (*iter)[0]->data.objectStatus ); } } @@ -9772,17 +9788,42 @@ INT16 GetMeleeDamageBonus( OBJECTTYPE * pObj ) } -INT16 GetPercentAPReduction( OBJECTTYPE * pObj ) +INT16 GetPercentAPReduction( SOLDIERTYPE * pSoldier, OBJECTTYPE * pObj ) { INT16 bonus = 0; - if (pObj->exists() == true) { - bonus = BonusReduceMore( Item[pObj->usItem].percentapreduction, (*pObj)[0]->data.objectStatus ); - bonus += Item[(*pObj)[0]->data.gun.usGunAmmoItem].percentapreduction; + if (pObj->exists() == true) + { + if ( gGameExternalOptions.fScopeModes && pSoldier && pSoldier->bTeam == gbPlayerNum ) + { + bonus += Item[(*pObj)[0]->data.gun.usGunAmmoItem].percentapreduction; - for (attachmentList::iterator iter = (*pObj)[0]->attachments.begin(); iter != (*pObj)[0]->attachments.end(); ++iter) { - if(iter->exists()){ - bonus += BonusReduceMore( Item[iter->usItem].percentapreduction, - (*iter)[0]->data.objectStatus ); + for (attachmentList::iterator iter = (*pObj)[0]->attachments.begin(); iter != (*pObj)[0]->attachments.end(); ++iter) { + if( iter->exists() && !IsAttachmentClass(iter->usItem, AC_SCOPE|AC_SIGHT ) ) + { + bonus += BonusReduceMore( Item[iter->usItem].percentapreduction, + (*iter)[0]->data.objectStatus ); + } + } + + // Flugente: check for scope mode + if ( Item[pObj->usItem].usItemClass == IC_GUN ) + { + std::map ObjList; + GetScopeLists(pObj, ObjList); + + bonus += BonusReduceMore( Item[ObjList[pSoldier->bScopeMode]->usItem].percentapreduction, (*ObjList[pSoldier->bScopeMode])[0]->data.objectStatus ); + } + } + else + { + bonus = BonusReduceMore( Item[pObj->usItem].percentapreduction, (*pObj)[0]->data.objectStatus ); + bonus += Item[(*pObj)[0]->data.gun.usGunAmmoItem].percentapreduction; + + for (attachmentList::iterator iter = (*pObj)[0]->attachments.begin(); iter != (*pObj)[0]->attachments.end(); ++iter) { + if(iter->exists()){ + bonus += BonusReduceMore( Item[iter->usItem].percentapreduction, + (*iter)[0]->data.objectStatus ); + } } } } @@ -9976,7 +10017,8 @@ INT16 GetVisionRangeBonus( SOLDIERTYPE * pSoldier ) continue; } - if (!IsWeapon(usItem) || (IsWeapon(usItem) && usingGunScope == true) ) + // Flugente: weapons are checked later on... + if (!IsWeapon(usItem) )//|| (IsWeapon(usItem) && usingGunScope == true) ) { bonus += BonusReduceMore( pItem->visionrangebonus, (*pObj)[0]->data.objectStatus ); } @@ -9991,10 +10033,38 @@ INT16 GetVisionRangeBonus( SOLDIERTYPE * pSoldier ) // SANDRO - added scouting check INT16 sScopebonus = 0; pObj = &( pSoldier->inv[HANDPOS]); - if (pObj->exists() == true) { - for (attachmentList::iterator iter = (*pObj)[0]->attachments.begin(); iter != (*pObj)[0]->attachments.end(); ++iter) { - if(iter->exists()){ - sScopebonus += BonusReduceMore( Item[iter->usItem].visionrangebonus, (*iter)[0]->data.objectStatus ); + if (pObj->exists() == true) + { + if ( gGameExternalOptions.fScopeModes && pSoldier && pSoldier->bTeam == gbPlayerNum ) + { + for (attachmentList::iterator iter = (*pObj)[0]->attachments.begin(); iter != (*pObj)[0]->attachments.end(); ++iter) + { + // add boni only from non-scope items + if( iter->exists() && !IsAttachmentClass(iter->usItem, AC_SCOPE|AC_SIGHT ) ) + { + sScopebonus += BonusReduceMore( Item[iter->usItem].visionrangebonus, (*iter)[0]->data.objectStatus ); + } + } + + // Flugente: check for scope mode + if ( Item[pObj->usItem].usItemClass == IC_GUN ) + { + std::map ObjList; + GetScopeLists(pObj, ObjList); + + // now apply the bonus from the scope we use + sScopebonus += BonusReduceMore( Item[ObjList[pSoldier->bScopeMode]->usItem].visionrangebonus, (*ObjList[pSoldier->bScopeMode])[0]->data.objectStatus ); + } + } + else + { + for (attachmentList::iterator iter = (*pObj)[0]->attachments.begin(); iter != (*pObj)[0]->attachments.end(); ++iter) + { + // add boni only from non-scope items + if( iter->exists() ) + { + sScopebonus += BonusReduceMore( Item[iter->usItem].visionrangebonus, (*iter)[0]->data.objectStatus ); + } } } } @@ -10052,7 +10122,8 @@ INT16 GetNightVisionRangeBonus( SOLDIERTYPE * pSoldier, UINT8 bLightLevel ) continue; } - if (!IsWeapon(usItem) || (IsWeapon(usItem) && usingGunScope == true ) ) + // Flugente: weapons are checked later on... + if (!IsWeapon(usItem) )//|| (IsWeapon(usItem) && usingGunScope == true ) ) { bonus += BonusReduceMore( NightBonusScale( pItem->nightvisionrangebonus, bLightLevel ), @@ -10067,12 +10138,44 @@ INT16 GetNightVisionRangeBonus( SOLDIERTYPE * pSoldier, UINT8 bLightLevel ) // SANDRO - added scouting check INT16 sScopebonus = 0; pObj = &( pSoldier->inv[HANDPOS]); - if (pObj->exists() == true) { - for (attachmentList::iterator iter = (*pObj)[0]->attachments.begin(); iter != (*pObj)[0]->attachments.end(); ++iter) { - if(iter->exists()){ + if (pObj->exists() == true) + { + if ( gGameExternalOptions.fScopeModes && pSoldier && pSoldier->bTeam == gbPlayerNum ) + { + for (attachmentList::iterator iter = (*pObj)[0]->attachments.begin(); iter != (*pObj)[0]->attachments.end(); ++iter) + { + // add boni only from non-scope items + if(iter->exists() && !IsAttachmentClass(iter->usItem, AC_SCOPE|AC_SIGHT ) ) + { + sScopebonus += BonusReduceMore( + NightBonusScale( Item[iter->usItem].nightvisionrangebonus, bLightLevel ), + (*iter)[0]->data.objectStatus ); + } + } + + // Flugente: check for scope mode + if ( Item[pObj->usItem].usItemClass == IC_GUN ) + { + std::map ObjList; + GetScopeLists(pObj, ObjList); + + // now apply the bonus from the scope we use sScopebonus += BonusReduceMore( - NightBonusScale( Item[iter->usItem].nightvisionrangebonus, bLightLevel ), - (*iter)[0]->data.objectStatus ); + NightBonusScale( Item[ObjList[pSoldier->bScopeMode]->usItem].cavevisionrangebonus, bLightLevel ), + (*ObjList[pSoldier->bScopeMode])[0]->data.objectStatus ); + } + } + else + { + for (attachmentList::iterator iter = (*pObj)[0]->attachments.begin(); iter != (*pObj)[0]->attachments.end(); ++iter) + { + // add boni only from non-scope items + if(iter->exists() ) + { + sScopebonus += BonusReduceMore( + NightBonusScale( Item[iter->usItem].nightvisionrangebonus, bLightLevel ), + (*iter)[0]->data.objectStatus ); + } } } } @@ -10117,7 +10220,8 @@ INT16 GetCaveVisionRangeBonus( SOLDIERTYPE * pSoldier, UINT8 bLightLevel ) continue; } - if (!IsWeapon(usItem) || (IsWeapon(usItem) && usingGunScope == true ) ) + // Flugente: weapons are checked later on... + if (!IsWeapon(usItem) )//|| (IsWeapon(usItem) && usingGunScope == true ) ) { bonus += BonusReduceMore( NightBonusScale( pItem->cavevisionrangebonus, bLightLevel ), @@ -10132,12 +10236,44 @@ INT16 GetCaveVisionRangeBonus( SOLDIERTYPE * pSoldier, UINT8 bLightLevel ) // SANDRO - added scouting check INT16 sScopebonus = 0; pObj = &( pSoldier->inv[HANDPOS]); - if (pObj->exists() == true) { - for (attachmentList::iterator iter = (*pObj)[0]->attachments.begin(); iter != (*pObj)[0]->attachments.end(); ++iter) { - if(iter->exists()){ + if (pObj->exists() == true) + { + if ( gGameExternalOptions.fScopeModes && pSoldier && pSoldier->bTeam == gbPlayerNum ) + { + for (attachmentList::iterator iter = (*pObj)[0]->attachments.begin(); iter != (*pObj)[0]->attachments.end(); ++iter) + { + // add boni only from non-scope items + if(iter->exists() && !IsAttachmentClass(iter->usItem, AC_SCOPE|AC_SIGHT ) ) + { + sScopebonus += BonusReduceMore( + NightBonusScale( Item[iter->usItem].cavevisionrangebonus, bLightLevel ), + (*iter)[0]->data.objectStatus ); + } + } + + // Flugente: check for scope mode + if ( Item[pObj->usItem].usItemClass == IC_GUN ) + { + std::map ObjList; + GetScopeLists(pObj, ObjList); + + // now apply the bonus from the scope we use sScopebonus += BonusReduceMore( - NightBonusScale( Item[iter->usItem].cavevisionrangebonus, bLightLevel ), - (*iter)[0]->data.objectStatus ); + NightBonusScale( Item[ObjList[pSoldier->bScopeMode]->usItem].cavevisionrangebonus, bLightLevel ), + (*ObjList[pSoldier->bScopeMode])[0]->data.objectStatus ); + } + } + else + { + for (attachmentList::iterator iter = (*pObj)[0]->attachments.begin(); iter != (*pObj)[0]->attachments.end(); ++iter) + { + // add boni only from non-scope items + if(iter->exists() ) + { + sScopebonus += BonusReduceMore( + NightBonusScale( Item[iter->usItem].cavevisionrangebonus, bLightLevel ), + (*iter)[0]->data.objectStatus ); + } } } } @@ -10157,7 +10293,7 @@ INT16 GetDayVisionRangeBonus( SOLDIERTYPE * pSoldier, UINT8 bLightLevel ) OBJECTTYPE *pObj; UINT16 usItem; INVTYPE *pItem; - + // Snap: Scale the bonus with the light level //ADB and AXP 28.03.2007: CtH bug fix: We also want to check on a firing weapon, "raised" alone is not enough ;) @@ -10184,7 +10320,8 @@ INT16 GetDayVisionRangeBonus( SOLDIERTYPE * pSoldier, UINT8 bLightLevel ) continue; } - if (!IsWeapon(usItem) || (IsWeapon(usItem) && usingGunScope == true ) ) + // Flugente: weapons are checked later on... + if (!IsWeapon(usItem) )//|| (IsWeapon(usItem) && usingGunScope == true ) ) { bonus += BonusReduceMore( idiv( pItem->dayvisionrangebonus * (NORMAL_LIGHTLEVEL_NIGHT - bLightLevel), NORMAL_LIGHTLEVEL_NIGHT ), @@ -10202,12 +10339,45 @@ INT16 GetDayVisionRangeBonus( SOLDIERTYPE * pSoldier, UINT8 bLightLevel ) // SANDRO - added scouting check INT16 sScopebonus = 0; pObj = &( pSoldier->inv[HANDPOS]); - if (pObj->exists() == true) { - for (attachmentList::iterator iter = (*pObj)[0]->attachments.begin(); iter != (*pObj)[0]->attachments.end(); ++iter) { - if(iter->exists()){ - sScopebonus += BonusReduceMore( idiv( Item[iter->usItem].dayvisionrangebonus + + if (pObj->exists() == true) + { + if ( gGameExternalOptions.fScopeModes && pSoldier && pSoldier->bTeam == gbPlayerNum ) + { + for (attachmentList::iterator iter = (*pObj)[0]->attachments.begin(); iter != (*pObj)[0]->attachments.end(); ++iter) + { + // add boni only from non-scope items + if(iter->exists() && !IsAttachmentClass(iter->usItem, AC_SCOPE|AC_SIGHT ) ) + { + sScopebonus += BonusReduceMore( idiv( Item[iter->usItem].dayvisionrangebonus * (NORMAL_LIGHTLEVEL_NIGHT - __max(bLightLevel,NORMAL_LIGHTLEVEL_DAY)), (NORMAL_LIGHTLEVEL_NIGHT-NORMAL_LIGHTLEVEL_DAY) ), (*iter)[0]->data.objectStatus ); + } + } + + // Flugente: check for scope mode + if ( Item[pObj->usItem].usItemClass == IC_GUN ) + { + std::map ObjList; + GetScopeLists(pObj, ObjList); + + // now apply the bonus from the scope we use + sScopebonus += BonusReduceMore( idiv( Item[ObjList[pSoldier->bScopeMode]->usItem].dayvisionrangebonus + * (NORMAL_LIGHTLEVEL_NIGHT - __max(bLightLevel,NORMAL_LIGHTLEVEL_DAY)), (NORMAL_LIGHTLEVEL_NIGHT-NORMAL_LIGHTLEVEL_DAY) ), + (*ObjList[pSoldier->bScopeMode])[0]->data.objectStatus ); + } + } + else + { + for (attachmentList::iterator iter = (*pObj)[0]->attachments.begin(); iter != (*pObj)[0]->attachments.end(); ++iter) + { + // add boni only from non-scope items + if(iter->exists() ) + { + sScopebonus += BonusReduceMore( idiv( Item[iter->usItem].dayvisionrangebonus + * (NORMAL_LIGHTLEVEL_NIGHT - __max(bLightLevel,NORMAL_LIGHTLEVEL_DAY)), (NORMAL_LIGHTLEVEL_NIGHT-NORMAL_LIGHTLEVEL_DAY) ), + (*iter)[0]->data.objectStatus ); + } } } } @@ -10254,7 +10424,8 @@ INT16 GetBrightLightVisionRangeBonus( SOLDIERTYPE * pSoldier, UINT8 bLightLevel continue; } - if (!IsWeapon(usItem) || (IsWeapon(usItem) && usingGunScope == true ) ) + // Flugente: weapons are checked later on... + if (!IsWeapon(usItem) )//|| (IsWeapon(usItem) && usingGunScope == true ) ) { bonus += BonusReduceMore( idiv( pItem->brightlightvisionrangebonus * (NORMAL_LIGHTLEVEL_DAY - bLightLevel), NORMAL_LIGHTLEVEL_DAY ), @@ -10269,12 +10440,44 @@ INT16 GetBrightLightVisionRangeBonus( SOLDIERTYPE * pSoldier, UINT8 bLightLevel // SANDRO - added scouting check INT16 sScopebonus = 0; pObj = &( pSoldier->inv[HANDPOS]); - if (pObj->exists() == true) { - for (attachmentList::iterator iter = (*pObj)[0]->attachments.begin(); iter != (*pObj)[0]->attachments.end(); ++iter) { - if(iter->exists()){ - sScopebonus += BonusReduceMore( idiv( Item[iter->usItem].brightlightvisionrangebonus - * (NORMAL_LIGHTLEVEL_DAY - bLightLevel), NORMAL_LIGHTLEVEL_DAY ), - (*iter)[0]->data.objectStatus ); + if (pObj->exists() == true) + { + if ( gGameExternalOptions.fScopeModes && pSoldier && pSoldier->bTeam == gbPlayerNum ) + { + for (attachmentList::iterator iter = (*pObj)[0]->attachments.begin(); iter != (*pObj)[0]->attachments.end(); ++iter) + { + // add boni only from non-scope items + if(iter->exists() && !IsAttachmentClass(iter->usItem, AC_SCOPE|AC_SIGHT ) ) + { + sScopebonus += BonusReduceMore( idiv( Item[iter->usItem].brightlightvisionrangebonus + * (NORMAL_LIGHTLEVEL_DAY - bLightLevel), NORMAL_LIGHTLEVEL_DAY ), + (*iter)[0]->data.objectStatus ); + } + } + + // Flugente: check for scope mode + if ( Item[pObj->usItem].usItemClass == IC_GUN ) + { + std::map ObjList; + GetScopeLists(pObj, ObjList); + + // now apply the bonus from the scope we use + sScopebonus += BonusReduceMore( idiv( Item[ObjList[pSoldier->bScopeMode]->usItem].brightlightvisionrangebonus + * (NORMAL_LIGHTLEVEL_DAY - bLightLevel), NORMAL_LIGHTLEVEL_DAY ), + (*ObjList[pSoldier->bScopeMode])[0]->data.objectStatus ); + } + } + else + { + for (attachmentList::iterator iter = (*pObj)[0]->attachments.begin(); iter != (*pObj)[0]->attachments.end(); ++iter) + { + // add boni only from non-scope items + if(iter->exists() ) + { + sScopebonus += BonusReduceMore( idiv( Item[iter->usItem].brightlightvisionrangebonus + * (NORMAL_LIGHTLEVEL_DAY - bLightLevel), NORMAL_LIGHTLEVEL_DAY ), + (*iter)[0]->data.objectStatus ); + } } } } @@ -10370,16 +10573,45 @@ UINT8 GetPercentTunnelVision( SOLDIERTYPE * pSoldier ) if ( usingGunScope == true ) { OBJECTTYPE *pObj = &(pSoldier->inv[HANDPOS]); - if (pObj->exists() == true) { + if (pObj->exists() == true) + { usItem = pObj->usItem; pItem = &(Item[usItem]); if ( IsWeapon(usItem) ) //if not a weapon, then it was added already above bonus += Item[usItem].percenttunnelvision; - for (attachmentList::iterator iter = (*pObj)[0]->attachments.begin(); iter != (*pObj)[0]->attachments.end(); ++iter) { - if(iter->exists()) - bonus += Item[iter->usItem].percenttunnelvision; + if ( gGameExternalOptions.fScopeModes && pSoldier && pSoldier->bTeam == gbPlayerNum ) + { + for (attachmentList::iterator iter = (*pObj)[0]->attachments.begin(); iter != (*pObj)[0]->attachments.end(); ++iter) + { + // add boni only from non-scope items + if(iter->exists() && !IsAttachmentClass(iter->usItem, AC_SCOPE|AC_SIGHT ) ) + { + bonus += Item[iter->usItem].percenttunnelvision; + } + } + + // Flugente: check for scope mode + if ( Item[pObj->usItem].usItemClass == IC_GUN ) + { + std::map ObjList; + GetScopeLists(pObj, ObjList); + + // now apply the bonus from the scope we use + bonus += Item[ObjList[pSoldier->bScopeMode]->usItem].percenttunnelvision; + } + } + else + { + for (attachmentList::iterator iter = (*pObj)[0]->attachments.begin(); iter != (*pObj)[0]->attachments.end(); ++iter) + { + // add boni only from non-scope items + if(iter->exists() ) + { + bonus += Item[iter->usItem].percenttunnelvision; + } + } } } } @@ -11385,44 +11617,73 @@ FLOAT GetHighestScopeMagnificationFactor( OBJECTTYPE * pObj ) return( BestFactor ); } -INT16 GetMinRangeForAimBonus( OBJECTTYPE * pObj ) +INT16 GetMinRangeForAimBonus( SOLDIERTYPE* pSoldier, OBJECTTYPE * pObj ) { INT16 bonus = 0; - if ( pObj->exists() == true ) { - bonus = Item[pObj->usItem].minrangeforaimbonus; - //bonus += Item[(*pObj)[0]->data.gun.usGunAmmoItem].minrangeforaimbonus; + if ( pObj->exists() == true ) + { + // Flugente: check for scope mode + if ( gGameExternalOptions.fScopeModes && pSoldier && pSoldier->bTeam == gbPlayerNum && Item[pObj->usItem].usItemClass == IC_GUN ) + { + std::map ObjList; + GetScopeLists(pObj, ObjList); + + bonus = Item[ObjList[pSoldier->bScopeMode]->usItem].minrangeforaimbonus; + } + else + bonus = Item[pObj->usItem].minrangeforaimbonus; + //bonus += Item[(*pObj)[0]->data.gun.usGunAmmoItem].minrangeforaimbonus; - for (attachmentList::iterator iter = (*pObj)[0]->attachments.begin(); iter != (*pObj)[0]->attachments.end(); ++iter) { - bonus += Item[iter->usItem].minrangeforaimbonus; + attachmentList::iterator iterend = (*pObj)[0]->attachments.end(); + for (attachmentList::iterator iter = (*pObj)[0]->attachments.begin(); iter != iterend; ++iter) + { + if ( !IsAttachmentClass(iter->usItem, AC_SCOPE|AC_SIGHT ) ) + bonus += Item[iter->usItem].minrangeforaimbonus; } } return( bonus ); } -FLOAT GetScopeMagnificationFactor( OBJECTTYPE * pObj, FLOAT uiRange ) +// altered by Flugente: uiRange is no more considered +FLOAT GetScopeMagnificationFactor( SOLDIERTYPE *pSoldier, OBJECTTYPE * pObj, FLOAT uiRange ) { FLOAT BestFactor = 1.0; + + // Flugente: if scope modes are allowed, player team uses them + if ( gGameExternalOptions.fScopeModes && pSoldier && pSoldier->bTeam == gbPlayerNum && pObj->exists() == true && Item[pObj->usItem].usItemClass == IC_GUN) + { + // Flugente: check for scope mode + std::map ObjList; + GetScopeLists(pObj, ObjList); + + // now apply the bonus from the scope we use + BestFactor = Item[ObjList[pSoldier->bScopeMode]->usItem].scopemagfactor; + + return __max(1.0f, BestFactor); + } + FLOAT CurrentFactor = 0.0; FLOAT TargetMagFactor = __max(1.0f,(FLOAT)uiRange / (FLOAT)gGameCTHConstants.NORMAL_SHOOTING_DISTANCE); FLOAT rangeModifier = gGameCTHConstants.SCOPE_RANGE_MULTIPLIER; TargetMagFactor = TargetMagFactor / rangeModifier; - + if(pObj->exists() == true && UsingNewCTHSystem() == true) { BestFactor = __max(1.0f, Item[pObj->usItem].scopemagfactor); - for (attachmentList::iterator iter = (*pObj)[0]->attachments.begin(); iter != (*pObj)[0]->attachments.end(); ++iter) + attachmentList::iterator iterend = (*pObj)[0]->attachments.end(); + for (attachmentList::iterator iter = (*pObj)[0]->attachments.begin(); iter != iterend; ++iter) { - if(iter->exists() == true) + if(iter->exists() == true ) { if(BestFactor == 1.0f) BestFactor = __max(BestFactor, Item[iter->usItem].scopemagfactor); else if(Item[iter->usItem].scopemagfactor >= TargetMagFactor) BestFactor = Item[iter->usItem].scopemagfactor; - } + } } } @@ -11431,6 +11692,17 @@ FLOAT GetScopeMagnificationFactor( OBJECTTYPE * pObj, FLOAT uiRange ) FLOAT GetBestScopeMagnificationFactor( SOLDIERTYPE *pSoldier, OBJECTTYPE * pObj, FLOAT uiRange ) { + // Flugente: if scope modes are allowed, player team uses them + if ( gGameExternalOptions.fScopeModes && pSoldier && pSoldier->bTeam == gbPlayerNum && pObj->exists() == true && Item[pObj->usItem].usItemClass == IC_GUN ) + { + // Flugente: check for scope mode + std::map ObjList; + GetScopeLists(pObj, ObjList); + + // now apply the bonus from the scope we use + return max(1.0f, Item[ObjList[pSoldier->bScopeMode]->usItem].scopemagfactor); + } + FLOAT BestFactor = 1.0; FLOAT TargetMagFactor = __max(1.0f,uiRange / (FLOAT)gGameCTHConstants.NORMAL_SHOOTING_DISTANCE); FLOAT CurrentFactor = 0.0; @@ -11527,7 +11799,7 @@ FLOAT GetBestScopeMagnificationFactor( SOLDIERTYPE *pSoldier, OBJECTTYPE * pObj, FLOAT GetProjectionFactor( OBJECTTYPE * pObj ) { FLOAT BestFactor = 1.0; - + if ( pObj->exists() == true && UsingNewCTHSystem() == true ) { BestFactor = __max((FLOAT)Item[pObj->usItem].projectionfactor, 1.0f); @@ -11548,7 +11820,7 @@ FLOAT GetScopeRangeMultiplier( SOLDIERTYPE *pSoldier, OBJECTTYPE *pObj, FLOAT d2 FLOAT iScopeFactor = 0; FLOAT rangeModifier = gGameCTHConstants.SCOPE_RANGE_MULTIPLIER; - iScopeFactor = GetScopeMagnificationFactor( pObj, d2DDistance ); + iScopeFactor = GetScopeMagnificationFactor( pSoldier, pObj, d2DDistance ); if( gGameOptions.fNewTraitSystem ) { if(iScopeFactor > 5.0f) @@ -11786,35 +12058,50 @@ UINT8 AllowedAimingLevels(SOLDIERTYPE * pSoldier, INT32 sGridNo) // don't break compatibility, let the users choose if (gGameExternalOptions.iAimLevelsCompatibilityOption != 0) + { sScopeBonus = OldWayOfCalculatingScopeBonus(pSoldier); + } + //WarmSteel - Using scope aimbonus instead, as it is used elsewhere like this too. + //Also, you won't get extra aimclicks anymore if you're too close to use your scope. + //I've externalized the scope types. + else if ( gGameExternalOptions.fAimLevelsDependOnDistance ) + { + if ( gGameExternalOptions.fScopeModes && pSoldier && pSoldier->bTeam == gbPlayerNum && (&pSoldier->inv[pSoldier->ubAttackingHand])->exists() == true && Item[(&pSoldier->inv[pSoldier->ubAttackingHand])->usItem].usItemClass == IC_GUN) + { + // Flugente: check for scope mode + std::map ObjList; + GetScopeLists(&pSoldier->inv[pSoldier->ubAttackingHand], ObjList); + + sScopeBonus = Item[ObjList[pSoldier->bScopeMode]->usItem].aimbonus; + } + else + sScopeBonus = GetBaseScopeAimBonus( &pSoldier->inv[pSoldier->ubAttackingHand], uiRange ); + } else - sScopeBonus = gGameExternalOptions.fAimLevelsDependOnDistance ? - //WarmSteel - Using scope aimbonus instead, as it is used elsewhere like this too. - //Also, you won't get extra aimclicks anymore if you're too close to use your scope. - //I've externalized the scope types. - GetBaseScopeAimBonus( &pSoldier->inv[pSoldier->ubAttackingHand], uiRange ) - : GetMinRangeForAimBonus( &pSoldier->inv[pSoldier->ubAttackingHand]); + { + sScopeBonus = GetMinRangeForAimBonus( pSoldier, &pSoldier->inv[pSoldier->ubAttackingHand]); + } + + if ( sScopeBonus >= gGameExternalOptions.sVeryHighPowerScope ) + { + aimLevels *= 2; + } - if ( sScopeBonus >= gGameExternalOptions.sVeryHighPowerScope ) - { - aimLevels *= 2; - } + else if ( sScopeBonus >= gGameExternalOptions.sHighPowerScope ) + { + aimLevels = (UINT8)((float)(aimLevels+1) * (float)1.5); + } - else if ( sScopeBonus >= gGameExternalOptions.sHighPowerScope ) - { - aimLevels = (UINT8)((float)(aimLevels+1) * (float)1.5); - } + else if ( sScopeBonus >= gGameExternalOptions.sMediumPowerScope ) + { + aimLevels = (UINT8)((float)(aimLevels+1) * (float)1.3); + } - else if ( sScopeBonus >= gGameExternalOptions.sMediumPowerScope ) - { - aimLevels = (UINT8)((float)(aimLevels+1) * (float)1.3); - } - - // Smaller scopes increase by one. - else if ( sScopeBonus > 0 ) - { - aimLevels++; - } + // Smaller scopes increase by one. + else if ( sScopeBonus > 0 ) + { + aimLevels++; + } // Make sure not over maximum allowed for weapon type. if (aimLevels > maxAimForType) @@ -11846,7 +12133,18 @@ UINT8 AllowedAimingLevels(SOLDIERTYPE * pSoldier, INT32 sGridNo) if (gGameExternalOptions.iAimLevelsCompatibilityOption != 0) sScopeBonus = OldWayOfCalculatingScopeBonus(pSoldier); else - sScopeBonus = gGameExternalOptions.fAimLevelsDependOnDistance ? GetBaseScopeAimBonus( pAttackingWeapon, uiRange ) : GetBaseScopeAimBonus( pAttackingWeapon, 25000 ); + { + if ( gGameExternalOptions.fScopeModes && pSoldier && pSoldier->bTeam == gbPlayerNum && pAttackingWeapon->exists() == true && Item[pAttackingWeapon->usItem].usItemClass == IC_GUN) + { + // Flugente: check for scope mode + std::map ObjList; + GetScopeLists(pAttackingWeapon, ObjList); + + sScopeBonus = Item[ObjList[pSoldier->bScopeMode]->usItem].aimbonus; + } + else + sScopeBonus = gGameExternalOptions.fAimLevelsDependOnDistance ? GetBaseScopeAimBonus( pAttackingWeapon, uiRange ) : GetBaseScopeAimBonus( pAttackingWeapon, 25000 ); + } if ( sScopeBonus >= gGameExternalOptions.sVeryHighPowerScope ) { @@ -12150,7 +12448,7 @@ INT16 GetBipodBonus( OBJECTTYPE * pObj ) INT16 GetItemVisionRangeBonus( OBJECTTYPE * pObj, INT16 VisionType ) { INT16 bonus = 0; - + if (VisionType == 0) { bonus += Item[ pObj->usItem ].visionrangebonus; @@ -12274,13 +12572,22 @@ INT16 GetBasicStealthBonus( OBJECTTYPE * pObj ) // HEADROCK HAM 4: Calculate a gun's accuracy, including bonuses from ammo and attachments. INT32 GetGunAccuracy( OBJECTTYPE *pObj ) { + // Flugente: If overheating is allowed, an overheated gun receives a slight malus to accuracy + FLOAT accuracyheatmultiplicator = 1.0; + if ( gGameOptions.fWeaponOverheating ) + { + FLOAT overheatdamagepercentage = GetGunOverheatDamagePercentage( pObj ); + FLOAT accuracymalus = (max(1.0, overheatdamagepercentage) - 1.0) * 0.1; + accuracyheatmultiplicator = max(0.0, 1.0 - accuracymalus); + } + if(UsingNewCTHSystem() == false) - return(Weapon[pObj->usItem].bAccuracy); + return(accuracyheatmultiplicator * Weapon[pObj->usItem].bAccuracy); INT32 bonus = 0; if ( pObj->exists() == true ) { - bonus = Weapon[Item[pObj->usItem].uiIndex].nAccuracy; + bonus = accuracyheatmultiplicator * Weapon[Item[pObj->usItem].uiIndex].nAccuracy; bonus = (bonus * (*pObj)[0]->data.gun.bGunStatus) / 100; INT32 iModifier = GetAccuracyModifier( pObj ); @@ -12598,7 +12905,7 @@ static UINT16 OldWayOfCalculatingScopeBonus(SOLDIERTYPE *pSoldier) // Yes, this may look stupid, maybe it IS stupid, but this is purely an option // to use code that was checked in before. // Please, do not trash it again. - return max(0, GetMinRangeForAimBonus(& pSoldier->inv[pSoldier->ubAttackingHand]) + return max(0, GetMinRangeForAimBonus( pSoldier, &pSoldier->inv[pSoldier->ubAttackingHand]) * gGameExternalOptions.iAimLevelsCompatibilityOption / gGameExternalOptions.ubStraightSightRange); } @@ -12627,6 +12934,64 @@ FLOAT GetItemCooldownFactor( OBJECTTYPE * pObj ) return cooldownfactor; } +void GetScopeLists( OBJECTTYPE * pObj, std::map& arScopeMap ) +{ + // build a list of all available scopes and sights ( we always have at least one: our weapon's iron sights) + arScopeMap[USE_BEST_SCOPE] = pObj; + + for (INT8 i = USE_SCOPE_2; i < NUM_SCOPE_MODES; ++i) + { + arScopeMap[i] = NULL; + } + + attachmentList::iterator iterend = (*pObj)[0]->attachments.end(); + for (attachmentList::iterator iter = (*pObj)[0]->attachments.begin(); iter != iterend; ++iter) + { + if ( iter->exists() && IsAttachmentClass(iter->usItem, AC_SCOPE|AC_SIGHT ) ) + { + FLOAT magfactor = Item[iter->usItem].scopemagfactor; + BOOLEAN isplaced = false; + + for (INT8 i = USE_BEST_SCOPE; i < NUM_SCOPE_MODES; ++i) + { + if ( arScopeMap[i] && magfactor > Item[arScopeMap[i]->usItem].scopemagfactor ) + { + // fit scope into existing list: move other entries down + for (INT8 j = NUM_SCOPE_MODES; j > i; --j) + { + arScopeMap[j] = arScopeMap[j-1]; + } + + arScopeMap[i] = &(*iter); + isplaced = true; + + break; + } + } + + // we have a scope that wasn't placed anywhere, as its magfactor is worse than everything else we have. Still, we put this thing to the end of our list + if ( !isplaced ) + { + // determine which scope this would be and insert it + for (INT8 i = USE_BEST_SCOPE; i < NUM_SCOPE_MODES; ++i) + { + if ( !arScopeMap[i] ) + { + arScopeMap[i] = &(*iter); + + break; + } + } + } + } + } +} + +BOOLEAN IsAttachmentClass( UINT16 usItem, UINT32 aFlag ) +{ + return( Item[usItem].attachmentclass & aFlag ); +} + /////////////////////////////////////////////////////////////////////////////////////////////////// // HEADROCK HAM 5: Item Transformation. // Item Transformation is a new way to interact with items. Where Merges combine two items into one, a Transformation diff --git a/Tactical/Items.h b/Tactical/Items.h index 43849251..2585339e 100644 --- a/Tactical/Items.h +++ b/Tactical/Items.h @@ -4,6 +4,7 @@ #include "Overhead Types.h" #include "Weapons.h" +#include // added by Flugente //forward declarations of common classes to eliminate includes class OBJECTTYPE; @@ -237,7 +238,7 @@ UINT16 MagazineClassIndexToItemType(UINT16 usMagIndex); BOOLEAN IsScoped( OBJECTTYPE * pObj ); BOOLEAN NCTHIsScoped( OBJECTTYPE * pObj ); INT16 GetReliability( OBJECTTYPE * pObj ); -INT16 GetAimBonus( OBJECTTYPE * pObj, INT32 iRange, INT16 ubAimTime ); +INT16 GetAimBonus( SOLDIERTYPE * pSoldier, OBJECTTYPE * pObj, INT32 iRange, INT16 ubAimTime ); //WarmSteel - Used to determine the base aim bonus of the most powerful scope. INT16 GetBaseScopeAimBonus( OBJECTTYPE * pObj, INT32 iRange ); // HEADROCK: Function to get the natural aimbonus of the weapon and its attachments @@ -256,7 +257,7 @@ INT16 GetBurstToHitBonus( OBJECTTYPE * pObj, BOOLEAN fProneStance = FALSE ); INT16 GetAutoToHitBonus( OBJECTTYPE * pObj, BOOLEAN fProneStance = FALSE ); INT16 GetGearAimBonus( SOLDIERTYPE * pSoldier, INT32 iRange, INT16 ubAimTime); INT16 GetGearToHitBonus( SOLDIERTYPE * pSoldier ); -INT16 GetMinRangeForAimBonus( OBJECTTYPE * pObj ); +INT16 GetMinRangeForAimBonus( SOLDIERTYPE* pSoldier, OBJECTTYPE * pObj ); // HEADROCK: Function to get the final loudness value of a weapon, after reduction from its own characteristics, ammo and attachments INT16 GetFinalLoudness( OBJECTTYPE * pObj ); @@ -289,7 +290,7 @@ INT8 GetStanceModifierRef( INT8 ubStance ); INT16 GetDamageBonus( OBJECTTYPE * pObj ); INT16 GetMeleeDamageBonus( OBJECTTYPE * pObj ); -INT16 GetPercentAPReduction( OBJECTTYPE * pObj ); +INT16 GetPercentAPReduction( SOLDIERTYPE * pSoldier, OBJECTTYPE * pObj ); INT16 GetPercentAutofireAPReduction( OBJECTTYPE * pObj ); INT16 GetPercentBurstFireAPReduction( OBJECTTYPE * pObj ); INT16 GetPercentReadyTimeAPReduction( OBJECTTYPE * pObj ); @@ -410,7 +411,7 @@ OBJECTTYPE* FindNightGogglesInInv( SOLDIERTYPE * pSoldier, INT8 * bSlot, BOOLEAN UINT8 AllowedAimingLevels(SOLDIERTYPE * pSoldier, INT32 sGridNo); // HEADROCK HAM 4: New functions to determine Scope and Laser factors on a weapon. FLOAT GetHighestScopeMagnificationFactor( OBJECTTYPE *pObj ); -FLOAT GetScopeMagnificationFactor( OBJECTTYPE *pObj, FLOAT uiRange ); +FLOAT GetScopeMagnificationFactor( SOLDIERTYPE *pSoldier, OBJECTTYPE *pObj, FLOAT uiRange ); FLOAT GetBestScopeMagnificationFactor( SOLDIERTYPE *pSoldier, OBJECTTYPE *pObj, FLOAT uiRange ); FLOAT GetProjectionFactor( OBJECTTYPE *pObj ); FLOAT GetScopeRangeMultiplier( SOLDIERTYPE *pSoldier, OBJECTTYPE *pObj, FLOAT d2DDistance ); @@ -445,6 +446,10 @@ BOOLEAN UseTotalMedicalKitPoints( SOLDIERTYPE * pSoldier, UINT16 usPointsToConsu // Flugente FTW 1.2 FLOAT GetItemCooldownFactor( OBJECTTYPE * pObj ); +void GetScopeLists( OBJECTTYPE * pObj, std::map& arScopeMap ); + +// check if item belongs to a specific attachemnt class +BOOLEAN IsAttachmentClass( UINT16 usItem, UINT32 aFlag ); #endif diff --git a/Tactical/LOS.cpp b/Tactical/LOS.cpp index d0f9316e..ec00edbf 100644 --- a/Tactical/LOS.cpp +++ b/Tactical/LOS.cpp @@ -7145,7 +7145,18 @@ FLOAT CalcProjectionFactor( SOLDIERTYPE *pShooter, OBJECTTYPE *pWeapon, FLOAT d2 if (ubAimTime > 0) { - iProjectionFactor = GetProjectionFactor( pWeapon ); + // Flugente: if scope modes are allowed, player team uses them + if ( gGameExternalOptions.fScopeModes && pShooter && pShooter->bTeam == gbPlayerNum && pWeapon->exists() == true && Item[pWeapon->usItem].usItemClass == IC_GUN ) + { + // Flugente: check for scope mode + std::map ObjList; + GetScopeLists(pWeapon, ObjList); + + iProjectionFactor = Item[ObjList[pShooter->bScopeMode]->usItem].projectionfactor; + } + else + iProjectionFactor = GetProjectionFactor( pWeapon ); + if (floor(iTargetMagFactor*10) > floor(iProjectionFactor*10.001)) { iProjectionFactor -= (iTargetMagFactor - iProjectionFactor); @@ -7158,22 +7169,29 @@ FLOAT CalcProjectionFactor( SOLDIERTYPE *pShooter, OBJECTTYPE *pWeapon, FLOAT d2 FLOAT CalcMagFactor( SOLDIERTYPE *pShooter, OBJECTTYPE *pWeapon, FLOAT d2DDistance, UINT8 ubAimTime ) { - FLOAT iFinalMagFactor = 0; + FLOAT iFinalMagFactor = 1.0; FLOAT iScopeFactor = 0; FLOAT iProjectionFactor = 0; - FLOAT iTargetMagFactor = d2DDistance / gGameCTHConstants.NORMAL_SHOOTING_DISTANCE; - FLOAT rangeModifier = GetScopeRangeMultiplier(pShooter, pWeapon, d2DDistance); - if (ubAimTime > 0) + // Flugente: if scope modes are allowed, player team uses them. We either use a scope or we don't, so the magnification factor isn't fitted to range (this is actually bad) + if ( gGameExternalOptions.fScopeModes && pShooter && pShooter->bTeam == gbPlayerNum && pWeapon->exists() == true && Item[pWeapon->usItem].usItemClass == IC_GUN ) { iScopeFactor = GetBestScopeMagnificationFactor( pShooter, pWeapon, d2DDistance ); - iScopeFactor = __min(iScopeFactor, __max(1.0f, iTargetMagFactor/rangeModifier)); iProjectionFactor = CalcProjectionFactor(pShooter, pWeapon, d2DDistance, ubAimTime); iFinalMagFactor = __max(iScopeFactor, iProjectionFactor); } else { - iFinalMagFactor = 1.0; + FLOAT iTargetMagFactor = d2DDistance / gGameCTHConstants.NORMAL_SHOOTING_DISTANCE; + FLOAT rangeModifier = GetScopeRangeMultiplier(pShooter, pWeapon, d2DDistance); + + if (ubAimTime > 0) + { + iScopeFactor = GetBestScopeMagnificationFactor( pShooter, pWeapon, d2DDistance ); + iScopeFactor = __min(iScopeFactor, __max(1.0f, iTargetMagFactor/rangeModifier)); + iProjectionFactor = CalcProjectionFactor(pShooter, pWeapon, d2DDistance, ubAimTime); + iFinalMagFactor = __max(iScopeFactor, iProjectionFactor); + } } return iFinalMagFactor; diff --git a/Tactical/Overhead.cpp b/Tactical/Overhead.cpp index 4106c55e..4cf922d4 100644 --- a/Tactical/Overhead.cpp +++ b/Tactical/Overhead.cpp @@ -8650,6 +8650,7 @@ SOLDIERTYPE *InternalReduceAttackBusyCount( ) pSoldier->bDoAutofire = 1; pSoldier->bDoBurst = TRUE; } + pSoldier->bScopeMode = USE_BEST_SCOPE; DirtyMercPanelInterface(pSoldier, DIRTYLEVEL2 ); } diff --git a/Tactical/Points.cpp b/Tactical/Points.cpp index 24fb12e8..4d41a128 100644 --- a/Tactical/Points.cpp +++ b/Tactical/Points.cpp @@ -1338,7 +1338,7 @@ INT16 GetBreathPerAP( SOLDIERTYPE *pSoldier, UINT16 usAnimState ) } //UINT8 CalcAPsToBurst( INT8 bBaseActionPoints, UINT16 usItem ) -INT16 CalcAPsToBurst( INT16 bBaseActionPoints, OBJECTTYPE * pObj ) +INT16 CalcAPsToBurst( INT16 bBaseActionPoints, OBJECTTYPE * pObj, SOLDIERTYPE* pSoldier ) { INT32 aps; @@ -1351,15 +1351,15 @@ INT16 CalcAPsToBurst( INT16 bBaseActionPoints, OBJECTTYPE * pObj ) aps = (aps * 100) / (100 + GetBurstFireAPReductionStatus(pObj) / (100/GetPercentBurstFireAPReduction(pObj))); } - if ( GetPercentAPReduction(pObj)>0 ) + if ( GetPercentAPReduction(pSoldier, pObj)>0 ) { - aps = (aps * 100) / (100 + GetAPReductionStatus(pObj) / (100/GetPercentAPReduction(pObj))); + aps = (aps * 100) / (100 + GetAPReductionStatus(pObj) / (100/GetPercentAPReduction(pSoldier, pObj))); }*/ - DebugMsg(TOPIC_JA2,DBG_LEVEL_3,String("CalcAPsToBurst: before bonus aps = %d, std bonus = %d, burst bonus = %d", aps,GetPercentAPReduction(pObj),GetPercentBurstFireAPReduction(pObj))); + DebugMsg(TOPIC_JA2,DBG_LEVEL_3,String("CalcAPsToBurst: before bonus aps = %d, std bonus = %d, burst bonus = %d", aps,GetPercentAPReduction(pSoldier, pObj),GetPercentBurstFireAPReduction(pObj))); // Snap: do this a little differently: % reduction means // aps <- aps * ( 100 - red ) / 100 - aps = ( aps * ( 100 - GetPercentAPReduction(pObj) ) ) / 100; + aps = ( aps * ( 100 - GetPercentAPReduction(pSoldier, pObj) ) ) / 100; // Snap: moved this up to allow burst AP reduction up to 100% aps = __max( aps, ( Weapon[ pObj->usItem ].bBurstAP + 1 ) / 2 ); @@ -1388,7 +1388,7 @@ INT16 CalcAPsToBurstNoModifier( INT16 bBaseActionPoints, OBJECTTYPE * pObj ) return (UINT8) aps; } -INT16 CalcAPsToAutofire( INT16 bBaseActionPoints, OBJECTTYPE * pObj, UINT8 bDoAutofire ) +INT16 CalcAPsToAutofire( INT16 bBaseActionPoints, OBJECTTYPE * pObj, UINT8 bDoAutofire, SOLDIERTYPE* pSoldier ) { //CHRISL: We send the actual number of rounds being fired in the bDoAutofire paramter. But that implies that it would take longer to fire the first round // in an autofire sequence then it would take to fire a single round during a single fire sequence. That doesn't make any sense. It should only cost @@ -1428,8 +1428,8 @@ INT16 CalcAPsToAutofire( INT16 bBaseActionPoints, OBJECTTYPE * pObj, UINT8 bDoAu // DebugMsg(TOPIC_JA2,DBG_LEVEL_3,String("CalcAPsToAutofire: found reflex sight, aps = %d, # shots = %d",aps,pSoldier->bDoAutofire )); //} - DebugMsg(TOPIC_JA2,DBG_LEVEL_3,String("CalcAPsToAutoFire: before bonus aps = %d, std bonus = %d, auto bonus = %d", aps,GetPercentAPReduction(pObj),GetPercentAutofireAPReduction(pObj))); - aps = ( aps * ( 100 - GetPercentAPReduction(pObj) ) ) / 100; + DebugMsg(TOPIC_JA2,DBG_LEVEL_3,String("CalcAPsToAutoFire: before bonus aps = %d, std bonus = %d, auto bonus = %d", aps,GetPercentAPReduction(pSoldier, pObj),GetPercentAutofireAPReduction(pObj))); + aps = ( aps * ( 100 - GetPercentAPReduction(pSoldier, pObj) ) ) / 100; aps = __max( aps, ( autofireaps + 1 ) / 2 ); @@ -1492,9 +1492,9 @@ INT16 CalcTotalAPsToAttack( SOLDIERTYPE *pSoldier, INT32 sGridNo, UINT8 ubAddTur if ( pSoldier->bDoBurst ) { if(pSoldier->bDoAutofire && GetAutofireShotsPerFiveAPs(AttackingWeapon) > 0 ) - sAPCost += CalcAPsToAutofire( pSoldier->CalcActionPoints( ), AttackingWeapon, pSoldier->bDoAutofire ); + sAPCost += CalcAPsToAutofire( pSoldier->CalcActionPoints( ), AttackingWeapon, pSoldier->bDoAutofire, pSoldier ); else - sAPCost += CalcAPsToBurst( pSoldier->CalcActionPoints( ), AttackingWeapon ); + sAPCost += CalcAPsToBurst( pSoldier->CalcActionPoints( ), AttackingWeapon, pSoldier ); } //else //ddd comment for aimed burst { @@ -1524,7 +1524,7 @@ INT16 CalcTotalAPsToAttack( SOLDIERTYPE *pSoldier, INT32 sGridNo, UINT8 ubAddTur // If the weapon has a scope, and the target is within eligible range for scope use - if ( (UsingNewCTHSystem() == false && IsScoped(&pSoldier->inv[HANDPOS]) && GetRangeInCellCoordsFromGridNoDiff( pSoldier->sGridNo, sGridNo ) >= GetMinRangeForAimBonus(&pSoldier->inv[HANDPOS])) + if ( (UsingNewCTHSystem() == false && IsScoped(&pSoldier->inv[HANDPOS]) && GetRangeInCellCoordsFromGridNoDiff( pSoldier->sGridNo, sGridNo ) >= GetMinRangeForAimBonus( pSoldier, &pSoldier->inv[HANDPOS])) || (UsingNewCTHSystem() == true && GetBestScopeMagnificationFactor(pSoldier, &pSoldier->inv[HANDPOS], (FLOAT)GetRangeInCellCoordsFromGridNoDiff( pSoldier->sGridNo, sGridNo ) > 1.0 )) ) { // Add an individual cost for EACH click, as necessary. @@ -1742,7 +1742,7 @@ INT8 CalcAimSkill( SOLDIERTYPE * pSoldier, UINT16 usWeapon ) return( bAimSkill ); } -INT16 BaseAPsToShootOrStab( INT16 bAPs, INT16 bAimSkill, OBJECTTYPE * pObj ) +INT16 BaseAPsToShootOrStab( INT16 bAPs, INT16 bAimSkill, OBJECTTYPE * pObj, SOLDIERTYPE* pSoldier ) { INT32 Top, Bottom; FLOAT rof; @@ -1785,7 +1785,7 @@ INT16 BaseAPsToShootOrStab( INT16 bAPs, INT16 bAimSkill, OBJECTTYPE * pObj ) //CHRISL: Resolves a problem with stackable weapons not using attachments like Reflex sight. Found by Mugsy //if (ItemSlotLimit(pObj, STACK_SIZE_LIMIT) == 1) { - Top *= ( 100 - GetPercentAPReduction(pObj) ); + Top *= ( 100 - GetPercentAPReduction(pSoldier, pObj) ); } //else //{ @@ -1990,9 +1990,9 @@ INT16 MinAPsToShootOrStab(SOLDIERTYPE *pSoldier, INT32 sGridNo, UINT8 ubAddTurni //CHRISL: When prone and using a bipod, bipod should help compensate for recoil. To reflect this, our shot AP cost should be minimially reduced if(gGameExternalOptions.ubFlatAFTHBtoPrecentMultiplier > 0 && gAnimControl[ pSoldier->usAnimState ].ubEndHeight == ANIM_PRONE && GetBipodBonus(&GrenadeLauncher) > 0){ - bAPCost += (BaseAPsToShootOrStab( bFullAPs, bAimSkill, &GrenadeLauncher ) * (100 - GetBipodBonus(&GrenadeLauncher)) / 100); + bAPCost += (BaseAPsToShootOrStab( bFullAPs, bAimSkill, &GrenadeLauncher, pSoldier ) * (100 - GetBipodBonus(&GrenadeLauncher)) / 100); } else { - bAPCost += BaseAPsToShootOrStab( bFullAPs, bAimSkill, &GrenadeLauncher ); + bAPCost += BaseAPsToShootOrStab( bFullAPs, bAimSkill, &GrenadeLauncher, pSoldier ); } // SANDRO - STOMP traits - reduced APs needed to fire underbarrel grenade launchers for Heavy Weapons trait @@ -2006,8 +2006,8 @@ INT16 MinAPsToShootOrStab(SOLDIERTYPE *pSoldier, INT32 sGridNo, UINT8 ubAddTurni // SANDRO - gunslinger check for firing speed if ( HAS_SKILL_TRAIT( pSoldier, GUNSLINGER_NT ) && gGameOptions.fNewTraitSystem ) { - INT16 bcst1 = BaseAPsToShootOrStab( bFullAPs, bAimSkill, &(pSoldier->inv[HANDPOS]) ); - INT16 bcst2 = BaseAPsToShootOrStab( bFullAPs, bAimSkill, &(pSoldier->inv[SECONDHANDPOS]) ); + INT16 bcst1 = BaseAPsToShootOrStab( bFullAPs, bAimSkill, &(pSoldier->inv[HANDPOS]), pSoldier ); + INT16 bcst2 = BaseAPsToShootOrStab( bFullAPs, bAimSkill, &(pSoldier->inv[SECONDHANDPOS]), pSoldier ); if ( Weapon[ usItem ].ubWeaponType == GUN_PISTOL ) bcst1 = (INT16)((bcst1 * ( 100 - gSkillTraitValues.ubGSFiringSpeedBonusPistols) / 100)+ 0.5); if ( Weapon[ pSoldier->inv[SECONDHANDPOS].usItem ].ubWeaponType == GUN_PISTOL ) @@ -2019,8 +2019,8 @@ INT16 MinAPsToShootOrStab(SOLDIERTYPE *pSoldier, INT32 sGridNo, UINT8 ubAddTurni { // charge the maximum of the two bAPCost += __max( - BaseAPsToShootOrStab( bFullAPs, bAimSkill, &(pSoldier->inv[HANDPOS]) ), - BaseAPsToShootOrStab( bFullAPs, bAimSkill, &(pSoldier->inv[SECONDHANDPOS]) ) ); + BaseAPsToShootOrStab( bFullAPs, bAimSkill, &(pSoldier->inv[HANDPOS]), pSoldier ), + BaseAPsToShootOrStab( bFullAPs, bAimSkill, &(pSoldier->inv[SECONDHANDPOS]), pSoldier ) ); } } else @@ -2028,11 +2028,11 @@ INT16 MinAPsToShootOrStab(SOLDIERTYPE *pSoldier, INT32 sGridNo, UINT8 ubAddTurni //CHRISL: When prone and using a bipod, bipod should help compensate for recoil. To reflect this, our shot AP cost should be minimially reduced if(gGameExternalOptions.ubFlatAFTHBtoPrecentMultiplier > 0 && gAnimControl[ pSoldier->usAnimState ].ubEndHeight == ANIM_PRONE && GetBipodBonus(&(pSoldier->inv[HANDPOS])) > 0) { - bAPCost += (BaseAPsToShootOrStab( bFullAPs, bAimSkill, pObjUsed ) * (100 - GetBipodBonus(&(pSoldier->inv[HANDPOS]))) / 100); + bAPCost += (BaseAPsToShootOrStab( bFullAPs, bAimSkill, pObjUsed, pSoldier ) * (100 - GetBipodBonus(&(pSoldier->inv[HANDPOS]))) / 100); } else { - bAPCost += BaseAPsToShootOrStab( bFullAPs, bAimSkill, pObjUsed ); + bAPCost += BaseAPsToShootOrStab( bFullAPs, bAimSkill, pObjUsed, pSoldier ); } ///////////////////////////////////////////////////////////////////////////////////// // SANDRO - STOMP traits @@ -3512,7 +3512,7 @@ INT32 CalcAPCostForAiming( SOLDIERTYPE *pSoldier, INT32 sTargetGridNo, INT8 bAim // If the weapon has a scope, and the target is within eligible range for scope use - if ( (UsingNewCTHSystem() == false && IsScoped(&pSoldier->inv[HANDPOS]) && GetRangeInCellCoordsFromGridNoDiff( pSoldier->sGridNo, sTargetGridNo ) >= GetMinRangeForAimBonus(&pSoldier->inv[HANDPOS])) + if ( (UsingNewCTHSystem() == false && IsScoped(&pSoldier->inv[HANDPOS]) && GetRangeInCellCoordsFromGridNoDiff( pSoldier->sGridNo, sTargetGridNo ) >= GetMinRangeForAimBonus(pSoldier, &pSoldier->inv[HANDPOS])) || (UsingNewCTHSystem() == true && GetBestScopeMagnificationFactor(pSoldier, &pSoldier->inv[HANDPOS], (FLOAT)GetRangeInCellCoordsFromGridNoDiff( pSoldier->sGridNo, sTargetGridNo ) > 1.0 ))) { // Add an individual cost for EACH click, as necessary. diff --git a/Tactical/Points.h b/Tactical/Points.h index 36651392..b210be34 100644 --- a/Tactical/Points.h +++ b/Tactical/Points.h @@ -275,7 +275,7 @@ //Just so the compiler will be able to use it in init.cpp, I'm a careful person. Gotthard 1/18/07 extern INT16 APBPConstants[TOTAL_APBP_VALUES]; -INT16 BaseAPsToShootOrStab( INT16 bAPs, INT16 bAimSkill, OBJECTTYPE * pObj ); +INT16 BaseAPsToShootOrStab( INT16 bAPs, INT16 bAimSkill, OBJECTTYPE * pObj, SOLDIERTYPE *pSoldier ); // HEADROCK HAM 4: Same function as above, except no modifier. INT16 BaseAPsToShootOrStabNoModifier( INT16 bAPs, INT16 bAimSkill, OBJECTTYPE * pObj ); // HEADROCK HAM 4: Same function as above, except no modifier. @@ -306,12 +306,12 @@ UINT16 GetAPsToPickupItem( SOLDIERTYPE *pSoldier, INT32 usMapPos ); INT16 MinAPsToPunch(SOLDIERTYPE *pSoldier, INT32 sGridNo, UINT8 ubAddTurningCost ); INT16 ApsToPunch( SOLDIERTYPE *pSoldier ); // SANDRO added this INT16 CalcTotalAPsToAttack( SOLDIERTYPE *pSoldier, INT32 sGridNo, UINT8 ubAddTurningCost, INT16 bAimTime ); -INT16 CalcAPsToBurst( INT16 bBaseActionPoints, OBJECTTYPE * pObj ); +INT16 CalcAPsToBurst( INT16 bBaseActionPoints, OBJECTTYPE * pObj, SOLDIERTYPE* pSoldier ); // HEADROCK HAM 4: Same as above, without percent modifiers. INT16 CalcAPsToBurstNoModifier( INT16 bBaseActionPoints, OBJECTTYPE * pObj ); // HEADROCK HAM 4: Same as above, without percent modifiers. INT16 CalcAPsToBurstNoModifier( INT16 bBaseActionPoints, OBJECTTYPE * pObj ); -INT16 CalcAPsToAutofire( INT16 bBaseActionPoints, OBJECTTYPE * pObj, UINT8 bDoAutofire ); +INT16 CalcAPsToAutofire( INT16 bBaseActionPoints, OBJECTTYPE * pObj, UINT8 bDoAutofire, SOLDIERTYPE* pSoldier ); // HEADROCK HAM 4: Same as above, without modifiers INT16 CalcAPsToAutofireNoModifier( INT16 bBaseActionPoints, OBJECTTYPE * pObj, UINT8 bDoAutofire ); // HEADROCK HAM 4: Same as above, without modifiers diff --git a/Tactical/Soldier Control.cpp b/Tactical/Soldier Control.cpp index 2ed8c46d..53e3b542 100644 --- a/Tactical/Soldier Control.cpp +++ b/Tactical/Soldier Control.cpp @@ -983,6 +983,8 @@ SOLDIERTYPE& SOLDIERTYPE::operator=(const OLDSOLDIERTYPE_101& src) this->snowCamo = src.snowCamo; this->wornSnowCamo = src.wornSnowCamo; + + this->bScopeMode = USE_BEST_SCOPE; } return *this; } @@ -12340,6 +12342,7 @@ void SOLDIERTYPE::ReLoadSoldierAnimationDueToHandItemChange( UINT16 usOldItem, U this->bWeaponMode = WM_AUTOFIRE; this->bDoAutofire = 1; } + this->bScopeMode = USE_BEST_SCOPE; if ( gAnimControl[ this->usAnimState ].uiFlags & ANIM_FIREREADY ) { diff --git a/Tactical/Soldier Control.h b/Tactical/Soldier Control.h index 1d4804ef..0240dac0 100644 --- a/Tactical/Soldier Control.h +++ b/Tactical/Soldier Control.h @@ -1151,6 +1151,9 @@ public: UINT8 ubPercentDamageInflictedByTeam[NUM_ASSIST_SLOTS]; //The percent of damage inflicted by the player team. Each element corresponds to the Soldier ID. Each element contains the percent damage inflicted by that merc #endif + // Flugente: Is this the correct position? + INT8 bScopeMode; + char endOfPOD; // marker for end of POD (plain old data) // Note: Place all non-POD items at the end (after endOfPOD) @@ -1319,7 +1322,7 @@ public: BOOLEAN IsValidSecondHandShotForReloadingPurposes( void ); BOOLEAN SoldierCarriesTwoHandedWeapon( void ); void SoldierInventoryCoolDown( void ); // Flugente FTW 1: Cool down all items in inventory - BOOLEAN IsWeaponMounted( void ); // determine if we receive a bonus for mouning our weapon on something + BOOLEAN IsWeaponMounted( void ); // determine if we receive a bonus for mounting our weapon on something OBJECTTYPE* GetUsedWeapon( OBJECTTYPE * pObj ); // if in an underbarrel fire mode, return underbarrel weapon UINT16 GetUsedWeaponNumber( OBJECTTYPE * pObj ); // if in an underbarrel fire mode, return number of underbarrel weapon diff --git a/Tactical/Turn Based Input.cpp b/Tactical/Turn Based Input.cpp index ee43e70b..6ee63d1a 100644 --- a/Tactical/Turn Based Input.cpp +++ b/Tactical/Turn Based Input.cpp @@ -220,6 +220,7 @@ void ToggleTreeTops(); void ToggleZBuffer(); void TogglePlanningMode(); void SetBurstMode(); +void SetScopeMode(); void ObliterateSector(); void RandomizeMercProfile(); void CreateNextCivType(); @@ -4403,6 +4404,24 @@ void GetKeyboardInput( UINT32 *puiNewEvent ) } //else if( gusSelectedSoldier != NOBODY ) break; + + // added by Flugente + case '.': + if ( fCtrl ) + { + + } + else if ( fAlt ) + { + + } + else + { + SetScopeMode(); + } + + break; + case 'z': if( fCtrl && fAlt ) { @@ -5175,6 +5194,17 @@ void SetBurstMode() } } +void SetScopeMode() +{ + if ( gusSelectedSoldier != NOBODY ) + { + ChangeScopeMode( MercPtrs[ gusSelectedSoldier ] ); + + // reevaluate sight + ManLooksForOtherTeams( MercPtrs[ gusSelectedSoldier ] ); + } +} + void ObliterateSector() { INT32 cnt; diff --git a/Tactical/Weapons.cpp b/Tactical/Weapons.cpp index 7dda7617..94a8ceca 100644 --- a/Tactical/Weapons.cpp +++ b/Tactical/Weapons.cpp @@ -6077,7 +6077,17 @@ UINT32 CalcChanceToHitGun(SOLDIERTYPE *pSoldier, INT32 sGridNo, INT16 ubAimTime, iMaxRange = CELL_X_SIZE; // one tile iCoverRange = __max(0,iSightRange - iRange); iMinRange = iMaxRange / 10; - iAccRangeMod = iRange * Weapon[usItemUsed].bAccuracy / 100; + + // Flugente: If overheating is allowed, an overheated gun receives a slight malus to accuracy + FLOAT accuracyheatmultiplicator = 1.0; + if ( gGameOptions.fWeaponOverheating ) + { + FLOAT overheatjampercentage = GetGunOverheatJamPercentage( pInHand ); + FLOAT accuracymalus = (FLOAT)((max(1.0, overheatjampercentage) - 1.0) * 0.1); + accuracyheatmultiplicator = (FLOAT)max(0.0, 1.0 - accuracymalus); + } + + iAccRangeMod = iRange * accuracyheatmultiplicator * Weapon[usItemUsed].bAccuracy / 100; ///////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////// @@ -6097,9 +6107,9 @@ UINT32 CalcChanceToHitGun(SOLDIERTYPE *pSoldier, INT32 sGridNo, INT16 ubAimTime, else if ( HAS_SKILL_TRAIT( pSoldier, PROF_SNIPER_OT ) ) { iSightRange -= ((gbSkillTraitBonus[ PROF_SNIPER_OT ] * NUM_SKILL_TRAITS( pSoldier, PROF_SNIPER_OT )) * iSightRange) /100; } - if (iRange < GetMinRangeForAimBonus(&(pSoldier->inv[pSoldier->ubAttackingHand])) && iScopeVisionRangeBonus > 50){ // iSightRange penalty for using a high power scope within min range due to poor focus + if (iRange < GetMinRangeForAimBonus(pSoldier, &(pSoldier->inv[pSoldier->ubAttackingHand])) && iScopeVisionRangeBonus > 50){ // iSightRange penalty for using a high power scope within min range due to poor focus iPenalty = 0; - for(UINT8 loop = 0; loop < ((GetMinRangeForAimBonus(&(pSoldier->inv[pSoldier->ubAttackingHand])) - iRange)/CELL_X_SIZE); loop++){ + for(UINT8 loop = 0; loop < ((GetMinRangeForAimBonus(pSoldier, &(pSoldier->inv[pSoldier->ubAttackingHand])) - iRange)/CELL_X_SIZE); loop++){ iPenalty += iSightRange * iScopeVisionRangeBonus / 100; } iSightRange += iPenalty; @@ -6311,15 +6321,28 @@ UINT32 CalcChanceToHitGun(SOLDIERTYPE *pSoldier, INT32 sGridNo, INT16 ubAimTime, // 25% penalty per tile closer than min range iChance -= 25 * ( ( MIN_TANK_RANGE - iRange ) / CELL_X_SIZE ); } - for(attachmentList::iterator iter = (*&(pSoldier->inv[pSoldier->ubAttackingHand]))[0]->attachments.begin(); iter != (*&(pSoldier->inv[pSoldier->ubAttackingHand]))[0]->attachments.end(); iter++) + + attachmentList::iterator iter_end = (*&(pSoldier->inv[pSoldier->ubAttackingHand]))[0]->attachments.end(); + for(attachmentList::iterator iter = (*&(pSoldier->inv[pSoldier->ubAttackingHand]))[0]->attachments.begin(); iter != iter_end; ++iter) { - if(iter->exists() && Item[iter->usItem].aimbonus >= gGameExternalOptions.sHighPowerScope && iRange > Item[iter->usItem].minrangeforaimbonus) + if(iter->exists() && !IsAttachmentClass(iter->usItem, AC_SCOPE|AC_SIGHT ) && Item[iter->usItem].aimbonus >= gGameExternalOptions.sHighPowerScope && iRange > Item[iter->usItem].minrangeforaimbonus) { iPenalty = (Item[iter->usItem].aimbonus * (iRange - Item[iter->usItem].minrangeforaimbonus)) / 1000; iPenalty = min(AIM_BONUS_PRONE, iPenalty); iChance -= iPenalty; } } + + // Flugente: check for scope mode + if ( Item[(&(pSoldier->inv[pSoldier->ubAttackingHand]))->usItem].usItemClass == IC_GUN ) + { + std::map ObjList; + GetScopeLists((&(pSoldier->inv[pSoldier->ubAttackingHand])), ObjList); + + iPenalty = (Item[ObjList[pSoldier->bScopeMode]->usItem].aimbonus * (iRange - Item[ObjList[pSoldier->bScopeMode]->usItem].minrangeforaimbonus)) / 1000; + iPenalty = min(AIM_BONUS_PRONE, iPenalty); + iChance -= iPenalty; + } ///////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////// @@ -6328,7 +6351,7 @@ UINT32 CalcChanceToHitGun(SOLDIERTYPE *pSoldier, INT32 sGridNo, INT16 ubAimTime, { // Bonus for heavy weapons moved here from above to get instant CtH bonus and not marksmanship bonus, // which is supressed by weapon condition - if (Item[usItemUsed].rocketlauncher || Item[usItemUsed].singleshotrocketlauncher) + if (Item[usInHand].rocketlauncher || Item[usInHand].singleshotrocketlauncher) { iChance += gSkillTraitValues.bCtHModifierRocketLaunchers; // -25% for untrained mercs !!! @@ -6336,7 +6359,7 @@ UINT32 CalcChanceToHitGun(SOLDIERTYPE *pSoldier, INT32 sGridNo, INT16 ubAimTime, iChance += gSkillTraitValues.ubHWBonusCtHRocketLaunchers * NUM_SKILL_TRAITS( pSoldier, HEAVY_WEAPONS_NT ); // +25% per trait } // Added CtH bonus for Gunslinger trait on pistols and machine-pistols - else if ( Weapon[usItemUsed].ubWeaponType == GUN_PISTOL ) + else if ( Weapon[usInHand].ubWeaponType == GUN_PISTOL ) { iChance += gSkillTraitValues.bCtHModifierPistols; // -5% for untrained mercs. @@ -6344,7 +6367,7 @@ UINT32 CalcChanceToHitGun(SOLDIERTYPE *pSoldier, INT32 sGridNo, INT16 ubAimTime, if ( HAS_SKILL_TRAIT( pSoldier, GUNSLINGER_NT ) && pSoldier->bDoBurst == 0 && pSoldier->bDoAutofire == 0 ) iChance += gSkillTraitValues.ubGSBonusCtHPistols * NUM_SKILL_TRAITS( pSoldier, GUNSLINGER_NT ); // +10% per trait } - else if ( Weapon[usItemUsed].ubWeaponType == GUN_M_PISTOL ) + else if ( Weapon[usInHand].ubWeaponType == GUN_M_PISTOL ) { iChance += gSkillTraitValues.bCtHModifierMachinePistols; // -5% for untrained mercs. @@ -6353,21 +6376,21 @@ UINT32 CalcChanceToHitGun(SOLDIERTYPE *pSoldier, INT32 sGridNo, INT16 ubAimTime, iChance += gSkillTraitValues.ubGSBonusCtHMachinePistols * NUM_SKILL_TRAITS( pSoldier, GUNSLINGER_NT ); // +5% per trait } // Added CtH bonus for Machinegunner skill on assault rifles, SMGs and LMGs - else if ( Weapon[usItemUsed].ubWeaponType == GUN_AS_RIFLE ) + else if ( Weapon[usInHand].ubWeaponType == GUN_AS_RIFLE ) { iChance += gSkillTraitValues.bCtHModifierAssaultRifles; // -5% for untrained mercs. if ( HAS_SKILL_TRAIT( pSoldier, AUTO_WEAPONS_NT ) ) iChance += gSkillTraitValues.ubAWBonusCtHAssaultRifles * NUM_SKILL_TRAITS( pSoldier, AUTO_WEAPONS_NT ); // +5% per trait } - else if ( Weapon[usItemUsed].ubWeaponType == GUN_SMG ) + else if ( Weapon[usInHand].ubWeaponType == GUN_SMG ) { iChance += gSkillTraitValues.bCtHModifierSMGs; // -5% for untrained mercs. if ( HAS_SKILL_TRAIT( pSoldier, AUTO_WEAPONS_NT ) ) iChance += gSkillTraitValues.ubAWBonusCtHSMGs * NUM_SKILL_TRAITS( pSoldier, AUTO_WEAPONS_NT ); // +5% per trait } - else if ( Weapon[usItemUsed].ubWeaponType == GUN_LMG ) + else if ( Weapon[usInHand].ubWeaponType == GUN_LMG ) { iChance += gSkillTraitValues.bCtHModifierLMGs; // -10% for untrained mercs. @@ -6375,7 +6398,7 @@ UINT32 CalcChanceToHitGun(SOLDIERTYPE *pSoldier, INT32 sGridNo, INT16 ubAimTime, iChance += gSkillTraitValues.ubAWBonusCtHLMGs * NUM_SKILL_TRAITS( pSoldier, AUTO_WEAPONS_NT ); // +5% per trait } // Added CtH bonus for Gunslinger trait on pistols and machine-pistols - else if ( Weapon[usItemUsed].ubWeaponType == GUN_SN_RIFLE ) + else if ( Weapon[usInHand].ubWeaponType == GUN_SN_RIFLE ) { iChance += gSkillTraitValues.bCtHModifierSniperRifles; // -5% for untrained mercs. @@ -6384,7 +6407,7 @@ UINT32 CalcChanceToHitGun(SOLDIERTYPE *pSoldier, INT32 sGridNo, INT16 ubAimTime, iChance += gSkillTraitValues.ubSNBonusCtHSniperRifles * NUM_SKILL_TRAITS( pSoldier, SNIPER_NT ); // +5% per trait } // Added CtH bonus for Ranger skill on rifles and shotguns - else if ( Weapon[usItemUsed].ubWeaponType == GUN_RIFLE ) + else if ( Weapon[usInHand].ubWeaponType == GUN_RIFLE ) { iChance += gSkillTraitValues.bCtHModifierRifles; // -5% for untrained mercs. @@ -6396,7 +6419,7 @@ UINT32 CalcChanceToHitGun(SOLDIERTYPE *pSoldier, INT32 sGridNo, INT16 ubAimTime, if ( HAS_SKILL_TRAIT( pSoldier, SNIPER_NT ) && pSoldier->bDoBurst == 0 && pSoldier->bDoAutofire == 0 ) iChance += gSkillTraitValues.ubSNBonusCtHRifles * NUM_SKILL_TRAITS( pSoldier, SNIPER_NT ); // +5% per trait } - else if ( Weapon[usItemUsed].ubWeaponType == GUN_SHOTGUN ) + else if ( Weapon[usInHand].ubWeaponType == GUN_SHOTGUN ) { iChance += gSkillTraitValues.bCtHModifierShotguns; // -5% for untrained mercs. @@ -6716,19 +6739,32 @@ UINT32 CalcChanceToHitGun(SOLDIERTYPE *pSoldier, INT32 sGridNo, INT16 ubAimTime, // progressivly less the more we aim. Everything is based on the maxBonus that a merc can possibly get which // uses the equation: 20+(MRK/20*LVL)+Accuracy+(Sniper trait * 10). This value is then split between the 8 // possible AimTime's using a max aimTime bonus of 10. - if ( gGameOptions.fNewTraitSystem ) { + + // Flugente: If overheating is allowed, an overheated gun receives a slight malus to accuracy + FLOAT accuracyheatmultiplicator = 1.0; + if ( gGameOptions.fWeaponOverheating ) + { + FLOAT overheatdamagepercentage = GetGunOverheatDamagePercentage( pInHand ); + FLOAT accuracymalus = (FLOAT)((max(1.0, overheatdamagepercentage) - 1.0) * 0.1); + accuracyheatmultiplicator = (FLOAT)max(0.0, 1.0 - accuracymalus); + } + + if ( gGameOptions.fNewTraitSystem ) + { // bonus to snipers and gunslingers if ( Weapon[usInHand].ubWeaponType == GUN_PISTOL || Weapon[usInHand].ubWeaponType == GUN_M_PISTOL ) - maxBonus = 20+((FLOAT)iMarksmanship/20*(EffectiveExpLevel(pSoldier)))+(Weapon[Item[pInHand->usItem].ubClassIndex].bAccuracy*2)+(NUM_SKILL_TRAITS( pSoldier, GUNSLINGER_NT )*gSkillTraitValues.ubGSAimingBonusPerClick); + maxBonus = 20+((FLOAT)iMarksmanship/20*(EffectiveExpLevel(pSoldier)))+(accuracyheatmultiplicator * Weapon[Item[pInHand->usItem].ubClassIndex].bAccuracy*2)+(NUM_SKILL_TRAITS( pSoldier, GUNSLINGER_NT )*gSkillTraitValues.ubGSAimingBonusPerClick); else - maxBonus = 20+((FLOAT)iMarksmanship/20*(EffectiveExpLevel(pSoldier)))+(Weapon[Item[pInHand->usItem].ubClassIndex].bAccuracy*2)+(NUM_SKILL_TRAITS( pSoldier, SNIPER_NT )*gSkillTraitValues.ubSNAimingBonusPerClick); - } else { + maxBonus = 20+((FLOAT)iMarksmanship/20*(EffectiveExpLevel(pSoldier)))+(accuracyheatmultiplicator * Weapon[Item[pInHand->usItem].ubClassIndex].bAccuracy*2)+(NUM_SKILL_TRAITS( pSoldier, SNIPER_NT )*gSkillTraitValues.ubSNAimingBonusPerClick); + } + else + { if(gGameExternalOptions.bAltAimEnabled) - maxBonus = (20*(iMarksmanship/100))+((FLOAT)iMarksmanship/20*pSoldier->stats.bExpLevel)+(Weapon[Item[pInHand->usItem].ubClassIndex].bAccuracy*2)+(NUM_SKILL_TRAITS( pSoldier, PROF_SNIPER_OT )*10); + maxBonus = (20*(iMarksmanship/100))+((FLOAT)iMarksmanship/20*pSoldier->stats.bExpLevel)+(accuracyheatmultiplicator * Weapon[Item[pInHand->usItem].ubClassIndex].bAccuracy*2)+(NUM_SKILL_TRAITS( pSoldier, PROF_SNIPER_OT )*10); else - maxBonus = 20+((FLOAT)iMarksmanship/20*pSoldier->stats.bExpLevel)+(Weapon[Item[pInHand->usItem].ubClassIndex].bAccuracy*2)+(NUM_SKILL_TRAITS( pSoldier, PROF_SNIPER_OT )*10); + maxBonus = 20+((FLOAT)iMarksmanship/20*pSoldier->stats.bExpLevel)+(accuracyheatmultiplicator * Weapon[Item[pInHand->usItem].ubClassIndex].bAccuracy*2)+(NUM_SKILL_TRAITS( pSoldier, PROF_SNIPER_OT )*10); } - iAimBonus = (float)GetAimBonus( pInHand, 100, 1 ); + iAimBonus = (float)GetAimBonus( pSoldier, pInHand, 100, 1 ); for(int i = 0; i < ubAimTime; i++) { aimTimeBonus = __min((maxBonus*bonusProgression[i]/1000),maxClickBonus); maxBonus -= aimTimeBonus; @@ -10223,7 +10259,7 @@ void HandleTacticalEffectsOfEquipmentChange( SOLDIERTYPE *pSoldier, UINT32 uiInv { pSoldier->bWeaponMode = WM_NORMAL; pSoldier->bDoBurst = FALSE; - pSoldier->bDoAutofire = 0; + pSoldier->bDoAutofire = 0; } else { @@ -10231,6 +10267,7 @@ void HandleTacticalEffectsOfEquipmentChange( SOLDIERTYPE *pSoldier, UINT32 uiInv pSoldier->bDoBurst = TRUE; pSoldier->bDoAutofire = 1; } + pSoldier->bScopeMode = USE_BEST_SCOPE; } // if he is loaded tactically @@ -10260,7 +10297,7 @@ void HandleTacticalEffectsOfEquipmentChange( SOLDIERTYPE *pSoldier, UINT32 uiInv { pSoldier->bWeaponMode = WM_NORMAL; pSoldier->bDoBurst = FALSE; - pSoldier->bDoAutofire = 0; + pSoldier->bDoAutofire = 0; } else { @@ -10268,6 +10305,7 @@ void HandleTacticalEffectsOfEquipmentChange( SOLDIERTYPE *pSoldier, UINT32 uiInv pSoldier->bDoAutofire = 1; pSoldier->bDoBurst = TRUE; } + pSoldier->bScopeMode = USE_BEST_SCOPE; } } } @@ -10710,6 +10748,37 @@ void ChangeWeaponMode( SOLDIERTYPE * pSoldier ) gfUIForceReExamineCursorData = TRUE; } +// Flugente: use a different scope +void ChangeScopeMode( SOLDIERTYPE * pSoldier ) +{ + // just to be safe... + if ( !pSoldier ) + return; + + // ATE: Don't do this if in a fire animation..... + if ( gAnimControl[ pSoldier->usAnimState ].uiFlags & ANIM_FIRE ) + return; + + if ( (&pSoldier->inv[HANDPOS])->exists() != true ) + return; + + std::map ObjList; + GetScopeLists(&pSoldier->inv[HANDPOS], ObjList); + + do + { + pSoldier->bScopeMode++; + if(pSoldier->bScopeMode == NUM_SCOPE_MODES) + { + pSoldier->bScopeMode = USE_BEST_SCOPE; + } + } + while( ObjList[pSoldier->bScopeMode] == NULL && pSoldier->bScopeMode != USE_BEST_SCOPE); + + DirtyMercPanelInterface( pSoldier, DIRTYLEVEL2 ); + gfUIForceReExamineCursorData = TRUE; +} + void DishoutQueenSwipeDamage( SOLDIERTYPE *pQueenSoldier ) { INT8 bValidDishoutDirs[3][3] = { NORTH, NORTHEAST, -1, @@ -11155,21 +11224,19 @@ void EstimateBulletsLeft( SOLDIERTYPE *pSoldier, OBJECTTYPE *pObj ) void CalcMagFactorSimple( SOLDIERTYPE *pSoldier, FLOAT d2DDistance, INT16 bAimTime ) { OBJECTTYPE *pWeapon = &(pSoldier->inv[pSoldier->ubAttackingHand]); - + FLOAT iActualMagFactor = 0; FLOAT iHighestMagFactor = 0; FLOAT iScopeFactor = 0; FLOAT iProjectionFactor = 0; - FLOAT iTargetMagFactor = d2DDistance / gGameCTHConstants.NORMAL_SHOOTING_DISTANCE; - FLOAT rangeModifier = GetScopeRangeMultiplier(pSoldier, pWeapon, d2DDistance); - if (bAimTime > 0) + // Flugente: if scope modes are allowed, player team uses them. We either use a scope or we don't, so the magnification factor isn't fitted to range (this is actually bad) + if ( gGameExternalOptions.fScopeModes && pSoldier && pSoldier->bTeam == gbPlayerNum && pWeapon->exists() == true && Item[pWeapon->usItem].usItemClass == IC_GUN ) { iScopeFactor = GetBestScopeMagnificationFactor( pSoldier, pWeapon, d2DDistance ); // Set a display variable gCTHDisplay.ScopeMagFactor = iScopeFactor; - iScopeFactor = __min(iScopeFactor, __max(1.0f, iTargetMagFactor/rangeModifier)); iProjectionFactor = CalcProjectionFactor(pSoldier, pWeapon, d2DDistance, (UINT8)bAimTime); // Set a display variable gCTHDisplay.ProjectionFactor = iProjectionFactor; @@ -11179,9 +11246,29 @@ void CalcMagFactorSimple( SOLDIERTYPE *pSoldier, FLOAT d2DDistance, INT16 bAimTi } else { - gCTHDisplay.ScopeMagFactor = 1.0; - gCTHDisplay.ProjectionFactor = 1.0; - iHighestMagFactor = 1.0; + FLOAT iTargetMagFactor = d2DDistance / gGameCTHConstants.NORMAL_SHOOTING_DISTANCE; + FLOAT rangeModifier = GetScopeRangeMultiplier(pSoldier, pWeapon, d2DDistance); + + if (bAimTime > 0) + { + iScopeFactor = GetBestScopeMagnificationFactor( pSoldier, pWeapon, d2DDistance ); + // Set a display variable + gCTHDisplay.ScopeMagFactor = iScopeFactor; + + iScopeFactor = __min(iScopeFactor, __max(1.0f, iTargetMagFactor/rangeModifier)); + iProjectionFactor = CalcProjectionFactor(pSoldier, pWeapon, d2DDistance, (UINT8)bAimTime); + // Set a display variable + gCTHDisplay.ProjectionFactor = iProjectionFactor; + + // The final factor is the largest of the two. + iHighestMagFactor = __max( iScopeFactor, iProjectionFactor ); + } + else + { + gCTHDisplay.ScopeMagFactor = 1.0; + gCTHDisplay.ProjectionFactor = 1.0; + iHighestMagFactor = 1.0; + } } gCTHDisplay.FinalMagFactor = iHighestMagFactor; diff --git a/Tactical/Weapons.h b/Tactical/Weapons.h index e7c3f7f5..86454d2e 100644 --- a/Tactical/Weapons.h +++ b/Tactical/Weapons.h @@ -18,6 +18,21 @@ enum WeaponMode NUM_WEAPON_MODES } ; +enum ScopeMode +{ + USE_BEST_SCOPE = 0, + USE_SCOPE_2, + USE_SCOPE_3, + USE_SCOPE_4, + USE_SCOPE_5, + USE_SCOPE_6, + USE_SCOPE_7, + USE_SCOPE_8, + USE_SCOPE_9, + USE_SCOPE_10, + NUM_SCOPE_MODES +}; + //ADB moved from Interface Panels.h void HandleTacticalEffectsOfEquipmentChange( SOLDIERTYPE *pSoldier, UINT32 uiInvPos, UINT16 usOldItem, UINT16 usNewItem ); @@ -416,6 +431,7 @@ extern INT32 CalcMaxTossRange( SOLDIERTYPE * pSoldier, UINT16 usItem, BOOLEAN fA extern UINT32 CalcThrownChanceToHit(SOLDIERTYPE *pSoldier, INT32 sGridNo, INT16 ubAimTime, UINT8 ubAimPos ); extern void ChangeWeaponMode( SOLDIERTYPE * pSoldier ); +extern void ChangeScopeMode( SOLDIERTYPE * pSoldier ); // Flugente: use different scope extern BOOLEAN UseHandToHand( SOLDIERTYPE *pSoldier , INT32 sTargetGridNo, BOOLEAN fStealing ); diff --git a/TacticalAI/Attacks.cpp b/TacticalAI/Attacks.cpp index 15d15112..73433983 100644 --- a/TacticalAI/Attacks.cpp +++ b/TacticalAI/Attacks.cpp @@ -167,7 +167,7 @@ void CalcBestShot(SOLDIERTYPE *pSoldier, ATTACKTYPE *pBestShot, BOOLEAN shootUns pSoldier->usAttackingWeapon = pSoldier->inv[HANDPOS].usItem; pSoldier->bWeaponMode = WM_NORMAL; - ubBurstAPs = CalcAPsToBurst( pSoldier->CalcActionPoints( ), &(pSoldier->inv[HANDPOS]) ); + ubBurstAPs = CalcAPsToBurst( pSoldier->CalcActionPoints( ), &(pSoldier->inv[HANDPOS]), pSoldier ); InitAttackType(pBestShot); // set all structure fields to defaults diff --git a/TacticalAI/DecideAction.cpp b/TacticalAI/DecideAction.cpp index 2c65449f..0077aba4 100644 --- a/TacticalAI/DecideAction.cpp +++ b/TacticalAI/DecideAction.cpp @@ -2527,7 +2527,7 @@ INT8 DecideActionRed(SOLDIERTYPE *pSoldier, UINT8 ubUnconsciousOK) INT8 bRecoilY = 0; pSoldier->bDoAutofire++; dTotalRecoil += AICalcRecoilForShot( pSoldier, &(pSoldier->inv[BestShot.bWeaponIn]), pSoldier->bDoAutofire ); - ubBurstAPs = CalcAPsToAutofire( pSoldier->CalcActionPoints(), &(pSoldier->inv[BestShot.bWeaponIn]), pSoldier->bDoAutofire ); + ubBurstAPs = CalcAPsToAutofire( pSoldier->CalcActionPoints(), &(pSoldier->inv[BestShot.bWeaponIn]), pSoldier->bDoAutofire, pSoldier ); } while( pSoldier->bActionPoints >= BestShot.ubAPCost + ubBurstAPs && pSoldier->inv[ pSoldier->ubAttackingHand ][0]->data.gun.ubGunShotsLeft >= pSoldier->bDoAutofire && dTotalRecoil <= 10.0f ); @@ -2535,7 +2535,7 @@ INT8 DecideActionRed(SOLDIERTYPE *pSoldier, UINT8 ubUnconsciousOK) do { pSoldier->bDoAutofire++; - ubBurstAPs = CalcAPsToAutofire( pSoldier->CalcActionPoints(), &(pSoldier->inv[BestShot.bWeaponIn]), pSoldier->bDoAutofire ); + ubBurstAPs = CalcAPsToAutofire( pSoldier->CalcActionPoints(), &(pSoldier->inv[BestShot.bWeaponIn]), pSoldier->bDoAutofire, pSoldier ); } while( pSoldier->bActionPoints >= BestShot.ubAPCost + ubBurstAPs && pSoldier->inv[ pSoldier->ubAttackingHand ][0]->data.gun.ubGunShotsLeft >= pSoldier->bDoAutofire && @@ -2545,7 +2545,7 @@ INT8 DecideActionRed(SOLDIERTYPE *pSoldier, UINT8 ubUnconsciousOK) pSoldier->bDoAutofire--; // Make sure we decided to fire at least one shot! - ubBurstAPs = CalcAPsToAutofire( pSoldier->CalcActionPoints(), &(pSoldier->inv[BestShot.bWeaponIn]), pSoldier->bDoAutofire ); + ubBurstAPs = CalcAPsToAutofire( pSoldier->CalcActionPoints(), &(pSoldier->inv[BestShot.bWeaponIn]), pSoldier->bDoAutofire, pSoldier ); // minimum 5 bullets //WarmSteel: 5 bullets sounds reasonable, no? // Hmmm, automatic suppression? Howcome we don't get this? @@ -4859,7 +4859,7 @@ INT16 ubMinAPCost; { DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"DecideActionBlack: ENOUGH APs TO BURST, RANDOM CHANCE OF DOING SO"); - ubBurstAPs = CalcAPsToBurst( pSoldier->CalcActionPoints(), &(pSoldier->inv[BestAttack.bWeaponIn]) ); + ubBurstAPs = CalcAPsToBurst( pSoldier->CalcActionPoints(), &(pSoldier->inv[BestAttack.bWeaponIn]), pSoldier ); // HEADROCK HAM 3.6: Use Actual Aiming Time. if (pSoldier->bActionPoints >= BestAttack.ubAPCost + sActualAimTime + ubBurstAPs ) @@ -4937,7 +4937,7 @@ INT16 ubMinAPCost; INT8 bRecoilY = 0; pSoldier->bDoAutofire++; dTotalRecoil += AICalcRecoilForShot( pSoldier, &(pSoldier->inv[BestShot.bWeaponIn]), pSoldier->bDoAutofire ); - ubBurstAPs = CalcAPsToAutofire( pSoldier->CalcActionPoints(), &(pSoldier->inv[BestShot.bWeaponIn]), pSoldier->bDoAutofire ); + ubBurstAPs = CalcAPsToAutofire( pSoldier->CalcActionPoints(), &(pSoldier->inv[BestShot.bWeaponIn]), pSoldier->bDoAutofire, pSoldier ); } while( pSoldier->bActionPoints >= BestShot.ubAPCost + ubBurstAPs + sActualAimTime && pSoldier->inv[ pSoldier->ubAttackingHand ][0]->data.gun.ubGunShotsLeft >= pSoldier->bDoAutofire && dTotalRecoil <= 10.0f ); @@ -4945,7 +4945,7 @@ INT16 ubMinAPCost; do { pSoldier->bDoAutofire++; - ubBurstAPs = CalcAPsToAutofire( pSoldier->CalcActionPoints(), &(pSoldier->inv[BestAttack.bWeaponIn]), pSoldier->bDoAutofire ); + ubBurstAPs = CalcAPsToAutofire( pSoldier->CalcActionPoints(), &(pSoldier->inv[BestAttack.bWeaponIn]), pSoldier->bDoAutofire, pSoldier ); } while( pSoldier->bActionPoints >= BestAttack.ubAPCost + ubBurstAPs && pSoldier->inv[ pSoldier->ubAttackingHand ][0]->data.gun.ubGunShotsLeft >= pSoldier->bDoAutofire && @@ -4955,7 +4955,7 @@ INT16 ubMinAPCost; pSoldier->bDoAutofire--; if (pSoldier->bDoAutofire > 0) { - ubBurstAPs = CalcAPsToAutofire( pSoldier->CalcActionPoints(), &(pSoldier->inv[BestAttack.bWeaponIn]), pSoldier->bDoAutofire ); + ubBurstAPs = CalcAPsToAutofire( pSoldier->CalcActionPoints(), &(pSoldier->inv[BestAttack.bWeaponIn]), pSoldier->bDoAutofire, pSoldier ); if (pSoldier->bActionPoints >= BestAttack.ubAPCost + ubBurstAPs ) { @@ -5009,7 +5009,7 @@ INT16 ubMinAPCost; { pSoldier->aiData.bAimTime = BestAttack.ubAimTime ; pSoldier->bDoBurst = 1; - BestAttack.ubAPCost = BestAttack.ubAPCost + CalcAPsToAutofire( pSoldier->CalcActionPoints(), &(pSoldier->inv[BestAttack.bWeaponIn]), pSoldier->bDoAutofire ) + sActualAimTime; + BestAttack.ubAPCost = BestAttack.ubAPCost + CalcAPsToAutofire( pSoldier->CalcActionPoints(), &(pSoldier->inv[BestAttack.bWeaponIn]), pSoldier->bDoAutofire, pSoldier ) + sActualAimTime; } else { @@ -5184,7 +5184,7 @@ INT16 ubMinAPCost; { DebugMsg(TOPIC_JA2, DBG_LEVEL_3, "DecideActionBlack: Doing burst calc"); - ubBurstAPs = CalcAPsToBurst( pSoldier->CalcActionPoints(), &(pSoldier->inv[BestAttack.bWeaponIn]) ); + ubBurstAPs = CalcAPsToBurst( pSoldier->CalcActionPoints(), &(pSoldier->inv[BestAttack.bWeaponIn]), pSoldier ); if ( (pSoldier->bActionPoints - BestAttack.ubAPCost) >= ubBurstAPs ) { @@ -5215,7 +5215,7 @@ INT16 ubMinAPCost; { DebugMsg(TOPIC_JA2, DBG_LEVEL_3, "DecideActionBlack: Doing GL burst"); BestAttack.ubAimTime = BURSTING; - BestAttack.ubAPCost = BestAttack.ubAPCost + CalcAPsToBurst( pSoldier->CalcActionPoints(), &(pSoldier->inv[HANDPOS]) ); + BestAttack.ubAPCost = BestAttack.ubAPCost + CalcAPsToBurst( pSoldier->CalcActionPoints(), &(pSoldier->inv[HANDPOS]), pSoldier ); // check for spread burst possibilities if (pSoldier->aiData.bAttitude != ATTACKSLAYONLY) { diff --git a/TileEngine/Explosion Control.cpp b/TileEngine/Explosion Control.cpp index 39816991..54a70827 100644 --- a/TileEngine/Explosion Control.cpp +++ b/TileEngine/Explosion Control.cpp @@ -3609,6 +3609,87 @@ void AddBombToQueue( UINT32 uiWorldBombIndex, UINT32 uiTimeStamp, BOOL fFromRemo gfExplosionQueueActive = TRUE; } +// Flugente: activate everything connected to a tripwire in the surrounding if sGridNo on level bLevel +BOOLEAN ActivateSurroundingTripwire( UINT8 ubID, INT32 sGridNo, INT8 bLevel ) +{ + UINT32 uiTimeStamp= GetJA2Clock(); + BOOLEAN fFoundMine = FALSE; + + // for every orientation + for (UINT8 ort = NORTH; ort < NUM_WORLD_DIRECTIONS; ++ort) + { + // get adjacent grid + UINT32 adjgrid = NewGridNo( sGridNo, DirectionInc( ort ) ); + + // 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 == adjgrid && gWorldItems[ gWorldBombs[uiWorldBombIndex].iItemIndex ].ubLevel == bLevel ) + { + OBJECTTYPE* pObj = &( gWorldItems[ gWorldBombs[uiWorldBombIndex].iItemIndex ].object ); + if (!((*pObj).fFlags & OBJECT_DISABLED_BOMB)) + { + // if item can be activated by tripwire, detonate it + if ( Item[pObj->usItem].tripwireactivation == 1 ) + { + // tripwire just gets activated + if ( Item[pObj->usItem].tripwire == 1 ) + { + OBJECTTYPE newtripwireObject; + CreateItem( pObj->usItem, (*pObj)[0]->data.objectStatus, &newtripwireObject ); + + // this is important: delete the tripwire, otherwise we get into an infinite loop if there are two piecs of tripwire.... + RemoveItemFromPool( adjgrid, gWorldBombs[ uiWorldBombIndex ].iItemIndex, bLevel ); + + // make sure no one thinks there is a bomb here any more! + if ( gpWorldLevelData[adjgrid].uiFlags & MAPELEMENT_PLAYER_MINE_PRESENT ) + { + RemoveBlueFlag( adjgrid, bLevel ); + } + gpWorldLevelData[adjgrid].uiFlags &= ~(MAPELEMENT_ENEMY_MINE_PRESENT); + + // no add a tripwire item to the floor, simulating that activating tripwire deactivates it + AddItemToPool( adjgrid, &newtripwireObject, 1, bLevel, 0, -1 ); + + // activate surrounding tripwires, unless tripwire is too much damaged and we are unlucky.. + if ( newtripwireObject[0]->data.objectStatus > (INT16)Random(50) ) + fFoundMine = ActivateSurroundingTripwire(ubID, adjgrid, bLevel); + } + // bombs go off + else + { + gubPersonToSetOffExplosions = ubID; + + // SANDRO - merc records + // only if we blew up somebody not in our team(no achievement for blowing our guys :)), only if owner exists and have profile + if ( (MercPtrs[ubID]->bTeam != gbPlayerNum) && ((*pObj)[0]->data.misc.ubBombOwner > 1) ) + { + if ( MercPtrs[ ((*pObj)[0]->data.misc.ubBombOwner - 2) ]->ubProfile != NO_PROFILE && MercPtrs[ ((*pObj)[0]->data.misc.ubBombOwner - 2) ]->bTeam == gbPlayerNum ) + gMercProfiles[ MercPtrs[ ((*pObj)[0]->data.misc.ubBombOwner - 2) ]->ubProfile ].records.usExpDetonated++; + } + + // put this bomb on the queue + AddBombToQueue( uiWorldBombIndex, uiTimeStamp ); + if (pObj->usItem != ACTION_ITEM || (*pObj)[0]->data.misc.bActionValue == ACTION_ITEM_BLOW_UP) + { + uiTimeStamp += BOMB_QUEUE_DELAY; + } + + if ( (*pObj)[0]->data.misc.usBombItem != NOTHING && Item[ (*pObj)[0]->data.misc.usBombItem ].usItemClass & IC_EXPLOSV ) + { + fFoundMine = TRUE; + } + } + } + } + } + } + } + + + return( fFoundMine ); +} + void HandleExplosionQueue( void ) { UINT32 uiIndex; @@ -3914,11 +3995,34 @@ BOOLEAN SetOffBombsInGridNo( UINT8 ubID, INT32 sGridNo, BOOLEAN fAllBombs, INT8 }*/ if (pObj->usItem == SWITCH) - { + { // send out a signal to detonate other bombs, rather than this which // isn't a bomb but a trigger SetOffBombsByFrequency( ubID, (*pObj)[0]->data.misc.bFrequency ); } + // Flugente: a tripwire activates all other tripwires in connection, and detonates all bombs in connection that are tripwire-activated + else if ( Item[pObj->usItem].tripwire == 1 ) + { + OBJECTTYPE newtripwireObject; + CreateItem( pObj->usItem, (*pObj)[0]->data.objectStatus, &newtripwireObject ); + + // this is important: delete the tripwire, otherwise we get into an infinite loop if there are two piecs of tripwire.... + RemoveItemFromPool( sGridNo, gWorldBombs[ uiWorldBombIndex ].iItemIndex, bLevel ); + + // make sure no one thinks there is a bomb here any more! + if ( gpWorldLevelData[sGridNo].uiFlags & MAPELEMENT_PLAYER_MINE_PRESENT ) + { + RemoveBlueFlag( sGridNo, bLevel ); + } + gpWorldLevelData[sGridNo].uiFlags &= ~(MAPELEMENT_ENEMY_MINE_PRESENT); + + // no add a tripwire item to the floor, simulating that activating tripwire deactivates it + AddItemToPool( sGridNo, &newtripwireObject, 1, bLevel, 0, -1 ); + + // activate surrounding tripwires and tripwire-activated mines, unless tripwire is too much damaged and we are unlucky.. + if ( newtripwireObject[0]->data.objectStatus > (INT16)Random(50) ) + fFoundMine = ActivateSurroundingTripwire(ubID, sGridNo, bLevel); + } else { gubPersonToSetOffExplosions = ubID; diff --git a/TileEngine/Explosion Control.h b/TileEngine/Explosion Control.h index e2971830..96fbd189 100644 --- a/TileEngine/Explosion Control.h +++ b/TileEngine/Explosion Control.h @@ -133,6 +133,9 @@ BOOLEAN DishOutGasDamage( SOLDIERTYPE * pSoldier, EXPLOSIVETYPE * pExplosive, IN void SpreadEffect( INT32 sGridNo, UINT8 ubRadius, UINT16 usItem, UINT8 ubOwner, BOOLEAN fSubsequent, INT8 bLevel, INT32 iSmokeEffectNum , BOOL fFromRemoteClient = FALSE , BOOL fNewSmokeEffect = FALSE ); void AddBombToQueue( UINT32 uiWorldBombIndex, UINT32 uiTimeStamp, BOOL fFromRemoteClient = FALSE ); +// Flugente: activate everything connected to a tripwire in the surrounding if sGridNo on level bLevel +BOOLEAN ActivateSurroundingTripwire( UINT8 ubID, INT32 sGridNo, INT8 bLevel ); + extern void ToggleActionItemsByFrequency( INT8 bFrequency ); extern void PerformItemAction( INT32 sGridNo, OBJECTTYPE * pObj ); extern BOOLEAN gfExplosionQueueMayHaveChangedSight; diff --git a/Utils/XML_Items.cpp b/Utils/XML_Items.cpp index b3886247..c9a95cbf 100644 --- a/Utils/XML_Items.cpp +++ b/Utils/XML_Items.cpp @@ -256,6 +256,9 @@ itemStartElementHandle(void *userData, const XML_Char *name, const XML_Char **at strcmp(name, "overheatCooldownModificator") == 0 || strcmp(name, "overheatJamThresholdModificator") == 0 || strcmp(name, "overheatDamageThresholdModificator") == 0 || + strcmp(name, "attachmentclass") == 0 || + strcmp(name, "tripwireactivation") == 0 || + strcmp(name, "tripwire") == 0 || strcmp(name, "fFlags") == 0 )) { @@ -1077,7 +1080,7 @@ itemEndElementHandle(void *userData, const XML_Char *name) { pData->curElement = ELEMENT; pData->curItem.mine = (BOOLEAN) atol(pData->szCharData); - } + } else if(strcmp(name, "GasCan") == 0) { pData->curElement = ELEMENT; @@ -1277,6 +1280,21 @@ itemEndElementHandle(void *userData, const XML_Char *name) pData->curElement = ELEMENT; pData->curItem.overheatDamageThresholdModificator = (FLOAT) atof(pData->szCharData); } + else if(strcmp(name, "attachmentclass") == 0) + { + pData->curElement = ELEMENT; + pData->curItem.attachmentclass = (UINT32) atol(pData->szCharData); + } + else if(strcmp(name, "tripwireactivation") == 0) + { + pData->curElement = ELEMENT; + pData->curItem.tripwireactivation = (BOOLEAN) atol(pData->szCharData); + } + else if(strcmp(name, "tripwire") == 0) + { + pData->curElement = ELEMENT; + pData->curItem.tripwire = (BOOLEAN) atol(pData->szCharData); + } pData->maxReadDepth--; } @@ -1754,7 +1772,7 @@ BOOLEAN WriteItemStats() FilePrintf(hFile,"\t\t%d\r\n", Item[cnt].grenadelauncher ); FilePrintf(hFile,"\t\t%d\r\n", Item[cnt].duckbill ); FilePrintf(hFile,"\t\t%d\r\n", Item[cnt].glgrenade ); - FilePrintf(hFile,"\t\t%d\r\n", Item[cnt].mine ); + FilePrintf(hFile,"\t\t%d\r\n", Item[cnt].mine ); FilePrintf(hFile,"\t\t%d\r\n", Item[cnt].mortar ); FilePrintf(hFile,"\t\t%d\r\n", Item[cnt].rocketlauncher ); FilePrintf(hFile,"\t\t%d\r\n", Item[cnt].singleshotrocketlauncher ); @@ -1882,6 +1900,10 @@ BOOLEAN WriteItemStats() FilePrintf(hFile,"\t\t%4.2f\r\n", Item[cnt].overheatJamThresholdModificator ); FilePrintf(hFile,"\t\t%4.2f\r\n", Item[cnt].overheatDamageThresholdModificator ); + FilePrintf(hFile,"\t\t%4.2f\r\n", Item[cnt].attachmentclass ); + + FilePrintf(hFile,"\t\t%d\r\n", Item[cnt].tripwireactivation ); + FilePrintf(hFile,"\t\t%d\r\n", Item[cnt].tripwire ); FilePrintf(hFile,"\t\r\n"); }