From 7a40ecd777daaf66db1b88fcaf35059f133ed349 Mon Sep 17 00:00:00 2001 From: Wanne Date: Sun, 1 Apr 2012 15:36:55 +0000 Subject: [PATCH] - New Feature: Allow attaching and using underbarrel weapons (by Flugente & JMich) o You can now attach weapons to other weapons (only one at a time). Comes with additional fire modes (by cycling in tactical with key 'b') o A new item, the KAC masterkey, demonstrates this. Raider and Kelly have one in their starting kit 4. (pics by smeagol) o works both in OCTh and NCTH. Aiming is done via the main gun, but all physical values of the attached gun are considered. o reload an underbarrel gun while it is attached by dropping the ammo on the main gun while an underbarrel fire mode is active. Cycle firemodes in tactical by pressing 'b' o Attachments to underbarrel weapons are inseparable while weapon is attached, but they do work. - Overheating weapons additions (by Flugente) o New option OVERHEATING_DISPLAY_THERMOMETER_RED_OFFSET controls how red the temperature bar is at the beginning. o New option OVERHEATING_SET_ZERO_UPON_NEW_SECTOR sets temperature of items on the floor to zero if loading a new sector git-svn-id: https://ja2svn.mooo.com/source/ja2/trunk/GameSource/ja2_v1.13/Build@5133 3b4a5df2-a311-0410-b5c6-a8a6f20db521 --- GameSettings.cpp | 4 +- GameSettings.h | 4 +- Strategic/strategicmap.cpp | 2 + Tactical/DisplayCover.cpp | 4 +- Tactical/Handle UI.cpp | 19 +- Tactical/Interface Items.cpp | 153 ++++++----- Tactical/Interface Panels.cpp | 14 +- Tactical/Items.cpp | 189 ++++++++++++- Tactical/Items.h | 12 + Tactical/LOS.cpp | 60 +++-- Tactical/Points.cpp | 114 ++++---- Tactical/Soldier Ani.cpp | 341 +++++++++++++----------- Tactical/Soldier Control.cpp | 39 ++- Tactical/Soldier Control.h | 4 +- Tactical/Tactical Turns.cpp | 2 +- Tactical/Turn Based Input.cpp | 41 ++- Tactical/UI Cursors.cpp | 3 +- Tactical/Weapons.cpp | 489 ++++++++++++++++++---------------- Tactical/Weapons.h | 3 + Tactical/World Items.cpp | 35 ++- Tactical/World Items.h | 2 +- Utils/Text.h | 3 + Utils/_ChineseText.cpp | 3 + Utils/_DutchText.cpp | 3 + Utils/_EnglishText.cpp | 3 + Utils/_FrenchText.cpp | 3 + Utils/_GermanText.cpp | 3 + Utils/_ItalianText.cpp | 3 + Utils/_PolishText.cpp | 3 + Utils/_RussianText.cpp | 3 + Utils/_TaiwaneseText.cpp | 3 + 31 files changed, 984 insertions(+), 580 deletions(-) diff --git a/GameSettings.cpp b/GameSettings.cpp index 97cf93a0..91925d86 100644 --- a/GameSettings.cpp +++ b/GameSettings.cpp @@ -1320,8 +1320,10 @@ void LoadGameExternalOptions() // Flugente FTW 1: These settings control the behavior of Weapon Overheating, its severity, and its display. gGameExternalOptions.fDisplayOverheatThermometer = iniReader.ReadBoolean("Tactical Weapon Overheating Settings","OVERHEATING_DISPLAY_THERMOMETER",TRUE); + gGameExternalOptions.ubOverheatThermometerRedOffset = iniReader.ReadInteger("Tactical Weapon Overheating Settings","OVERHEATING_DISPLAY_THERMOMETER_RED_OFFSET", 100, 0, 255); gGameExternalOptions.iCooldownModificatorLonelyBarrel = iniReader.ReadFloat ("Tactical Weapon Overheating Settings","OVERHEATING_COOLDOWN_MODIFICATOR_LONELYBARREL", 1.15f, 1.0f, 10.0f); - + gGameExternalOptions.fSetZeroUponNewSector = iniReader.ReadBoolean("Tactical Weapon Overheating Settings","OVERHEATING_SET_ZERO_UPON_NEW_SECTOR",TRUE); + //################# Strategic Gamestart Settings ################## //Lalien: Game starting time diff --git a/GameSettings.h b/GameSettings.h index 8e252265..64fc5bbe 100644 --- a/GameSettings.h +++ b/GameSettings.h @@ -1072,8 +1072,10 @@ typedef struct // Flugente FTW 1: Weapon Overheating BOOLEAN fDisplayOverheatThermometer; // Should a 'thermometer' for guns and replacable barrels be displayed? + UINT8 ubOverheatThermometerRedOffset; // amount of red colour while temperature is below threshold 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) + 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] diff --git a/Strategic/strategicmap.cpp b/Strategic/strategicmap.cpp index 0daae333..09be06c2 100644 --- a/Strategic/strategicmap.cpp +++ b/Strategic/strategicmap.cpp @@ -6113,6 +6113,8 @@ BOOLEAN HandleDefiniteUnloadingOfWorld( UINT8 ubUnloadCode ) //if we arent loading a saved game if( !(gTacticalStatus.uiFlags & LOADING_SAVED_GAME ) ) { + // Flugente: Set temperares of all items in the old sector to zero before entering a new sector + CoolDownWorldItems( TRUE ); // Save the current sectors Item list to a temporary file, if its not the first time in SaveCurrentSectorsInformationToTempItemFile(); diff --git a/Tactical/DisplayCover.cpp b/Tactical/DisplayCover.cpp index c91adf51..237e35ef 100644 --- a/Tactical/DisplayCover.cpp +++ b/Tactical/DisplayCover.cpp @@ -594,7 +594,9 @@ void DisplayRangeToTarget( SOLDIERTYPE *pSoldier, INT32 sTargetGridNo ) pSoldier->bTargetLevel = bTempTargetLevel; // HEADROCK HAM 3.6: Calculate Gun Range using formula. - UINT16 usGunRange = GunRange(&pSoldier->inv[HANDPOS], pSoldier ); // SANDRO - added argument + // Flugente: we might be equipped with an underbarrel gun.... + OBJECTTYPE* pObjhand = pSoldier->GetUsedWeapon(&pSoldier->inv[HANDPOS]); + UINT16 usGunRange = GunRange(pObjhand, pSoldier ); // SANDRO - added argument swprintf( zOutputString, gzDisplayCoverText[title], usRange / 10, usGunRange / 10, uiHitChance ); //Display the msg diff --git a/Tactical/Handle UI.cpp b/Tactical/Handle UI.cpp index c7e12d60..08ffe7aa 100644 --- a/Tactical/Handle UI.cpp +++ b/Tactical/Handle UI.cpp @@ -2531,15 +2531,18 @@ void UIHandleMercAttack( SOLDIERTYPE *pSoldier , SOLDIERTYPE *pTargetSoldier, IN INT32 iHandleReturn; INT32 sTargetGridNo; INT8 bTargetLevel; - UINT16 usItem; - LEVELNODE *pIntNode; - STRUCTURE *pStructure; - INT32 sGridNo, sNewGridNo; - UINT8 ubItemCursor; + UINT16 usItem; + LEVELNODE* pIntNode; + STRUCTURE* pStructure; + INT32 sGridNo, sNewGridNo; + UINT8 ubItemCursor; // get cursor ubItemCursor = GetActionModeCursor( pSoldier ); + OBJECTTYPE* pObj = pSoldier->GetUsedWeapon(&pSoldier->inv[HANDPOS]); + usItem = pSoldier->GetUsedWeaponNumber(&pSoldier->inv[HANDPOS]); + if ( !(gTacticalStatus.uiFlags & INCOMBAT) && pTargetSoldier && Item[ pSoldier->inv[ HANDPOS ].usItem ].usItemClass & IC_WEAPON ) { if ( NPCFirstDraw( pSoldier, pTargetSoldier ) ) @@ -2555,8 +2558,6 @@ void UIHandleMercAttack( SOLDIERTYPE *pSoldier , SOLDIERTYPE *pTargetSoldier, IN // Set aim time to one in UI pSoldier->aiData.bAimTime = (pSoldier->aiData.bShownAimTime ); - usItem = pSoldier->inv[ HANDPOS ].usItem; - // ATE: Check if we are targeting an interactive tile, and adjust gridno accordingly... pIntNode = GetCurInteractiveTileGridNoAndStructure( &sGridNo, &pStructure ); @@ -2642,11 +2643,11 @@ void UIHandleMercAttack( SOLDIERTYPE *pSoldier , SOLDIERTYPE *pTargetSoldier, IN if (pSoldier->bWeaponMode == WM_ATTACHED_GL || pSoldier->bWeaponMode == WM_ATTACHED_GL_BURST || pSoldier->bWeaponMode == WM_ATTACHED_GL_AUTO ) { - iHandleReturn = HandleItem( pSoldier, sTargetGridNo, bTargetLevel, GetAttachedGrenadeLauncher(&pSoldier->inv[ HANDPOS ]), TRUE ); + iHandleReturn = HandleItem( pSoldier, sTargetGridNo, bTargetLevel, GetAttachedGrenadeLauncher(pObj), TRUE ); } else { - iHandleReturn = HandleItem( pSoldier, sTargetGridNo, bTargetLevel, pSoldier->inv[ HANDPOS ].usItem, TRUE ); + iHandleReturn = HandleItem( pSoldier, sTargetGridNo, bTargetLevel, usItem, TRUE ); } if ( iHandleReturn < 0 ) diff --git a/Tactical/Interface Items.cpp b/Tactical/Interface Items.cpp index b618a5d5..12c49a35 100644 --- a/Tactical/Interface Items.cpp +++ b/Tactical/Interface Items.cpp @@ -2646,7 +2646,7 @@ void INVRenderSilhouette( UINT32 uiBuffer, INT16 PocketIndex, INT16 SilIndex, IN // Flugente FTW 1: Function to get the number of the item condition string UINT8 GetTemperatureString( FLOAT overheatpercentage, UINT32* apRed, UINT32* apGreen, UINT32* abBlue ) { - *apRed = (UINT32) ( 100 + 155 * ( (max(1.0, overheatpercentage) - 1.0)/(max(1.0, overheatpercentage)) ) ); + *apRed = (UINT32) ( gGameExternalOptions.ubOverheatThermometerRedOffset + (255 - gGameExternalOptions.ubOverheatThermometerRedOffset) * ( (max(1.0, overheatpercentage) - 1.0)/(max(1.0, overheatpercentage)) ) ); *apGreen = 0; *abBlue = 0; @@ -2769,7 +2769,7 @@ void INVRenderItem( UINT32 uiBuffer, SOLDIERTYPE * pSoldier, OBJECTTYPE *pObjec } #endif - // FIRST DISPLAY FREE ROUNDS REMIANING + if ( pItem->usItemClass == IC_GUN && !Item[pObject->usItem].rocketlauncher ) { sNewY = sY + sHeight - 10; @@ -2779,75 +2779,76 @@ void INVRenderItem( UINT32 uiBuffer, SOLDIERTYPE * pSoldier, OBJECTTYPE *pObjec if ( gGameOptions.fWeaponOverheating == TRUE && gGameExternalOptions.fDisplayOverheatThermometer == TRUE ) sNewX = sX + 6; - SetFontForeground ( AmmoTypes[(*pObject)[iter]->data.gun.ubGunAmmoType].fontColour ); - //switch ((*pObject)[iter]->data.gun.ubGunAmmoType) - //{ - // case AMMO_AP: - // case AMMO_SUPER_AP: - // SetFontForeground( ITEMDESC_FONTAPFORE ); - // break; - // case AMMO_HP: - // SetFontForeground( ITEMDESC_FONTHPFORE ); - // break; - // case AMMO_BUCKSHOT: - // SetFontForeground( ITEMDESC_FONTBSFORE ); - // break; - // case AMMO_HE: - // case AMMO_GRENADE: - // SetFontForeground( ITEMDESC_FONTHEFORE ); - // break; - // case AMMO_HEAT: - // SetFontForeground( ITEMDESC_FONTHEAPFORE ); - // break; - // default: - // SetFontForeground( FONT_MCOLOR_DKGRAY ); - // break; - //} - - // HEADROCK HAM 3.4: Get estimate of bullets left. - if ( (gTacticalStatus.uiFlags & TURNBASED) && (gTacticalStatus.uiFlags & INCOMBAT) ) + // Flugente: check for underbarrel weapons and use that object if necessary + OBJECTTYPE* pObjShown = pObject; + INVTYPE *pItemShown = pItem; + if ( pItem->usItemClass == IC_GUN && pSoldier && ( pSoldier->bWeaponMode == WM_ATTACHED_UB || pSoldier->bWeaponMode == WM_ATTACHED_UB_BURST || pSoldier->bWeaponMode == WM_ATTACHED_UB_AUTO ) ) { - // Soldier doesn't know. - EstimateBulletsLeft( pSoldier, pObject ); - swprintf( pStr, L"%s", gBulletCount ); - } - else - { - swprintf( pStr, L"%d", (*pObject)[iter]->data.gun.ubGunShotsLeft ); - } + OBJECTTYPE *pObjectUnderBarrel = FindAttachment_UnderBarrel( pObject ); - //swprintf( pStr, L"%d", (*pObject)[iter]->data.gun.ubGunShotsLeft ); - //swprintf( pStr, L"%d", GetEstimateBulletsLeft(pSoldier, pObject) ); - - if ( uiBuffer == guiSAVEBUFFER ) - { - RestoreExternBackgroundRect( sNewX, sNewY, 20, 15 ); - } - mprintf( sNewX, sNewY, pStr ); - gprintfinvalidate( sNewX, sNewY, pStr ); - - sNewX = sX + 1; - - SetFontForeground( FONT_MCOLOR_DKGRAY ); - - // Display 'JAMMED' if we are jammed - if ( (*pObject)[iter]->data.gun.bGunAmmoStatus < 0 ) - { - SetFontForeground( FONT_MCOLOR_RED ); - - if ( sWidth >= ( BIG_INV_SLOT_WIDTH - 10 ) ) + if ( pObjectUnderBarrel ) { - swprintf( pStr, TacticalStr[ JAMMED_ITEM_STR ] ); + INVTYPE *pItemUnderBarrel; + if ( ubStatusIndex < RENDER_ITEM_ATTACHMENT1 ) + { + pItemUnderBarrel = &Item[ pObjectUnderBarrel->usItem ]; + } + else + { + pItemUnderBarrel = &Item[ (*pObjectUnderBarrel)[iter]->GetAttachmentAtIndex( ubStatusIndex - RENDER_ITEM_ATTACHMENT1 )->usItem ]; + } + + pObjShown = pObjectUnderBarrel; + pItemShown = pItemUnderBarrel; + } + } + + if ( pItemShown->usItemClass == IC_GUN && !Item[pObjShown->usItem].rocketlauncher ) + { + SetFontForeground ( AmmoTypes[(*pObjShown)[iter]->data.gun.ubGunAmmoType].fontColour ); + + // HEADROCK HAM 3.4: Get estimate of bullets left. + if ( (gTacticalStatus.uiFlags & TURNBASED) && (gTacticalStatus.uiFlags & INCOMBAT) ) + { + // Soldier doesn't know. + EstimateBulletsLeft( pSoldier, pObjShown ); + swprintf( pStr, L"%s", gBulletCount ); } else { - swprintf( pStr, TacticalStr[ SHORT_JAMMED_GUN ] ); + swprintf( pStr, L"%d", (*pObjShown)[iter]->data.gun.ubGunShotsLeft ); + } + + if ( uiBuffer == guiSAVEBUFFER ) + { + RestoreExternBackgroundRect( sNewX, sNewY, 20, 15 ); } - - VarFindFontCenterCoordinates( sX, sY, sWidth, sHeight , ITEM_FONT, &sNewX, &sNewY, pStr ); - mprintf( sNewX, sNewY, pStr ); gprintfinvalidate( sNewX, sNewY, pStr ); + + sNewX = sX + 1; + + SetFontForeground( FONT_MCOLOR_DKGRAY ); + + // Display 'JAMMED' if we are jammed + if ( (*pObjShown)[iter]->data.gun.bGunAmmoStatus < 0 ) + { + SetFontForeground( FONT_MCOLOR_RED ); + + if ( sWidth >= ( BIG_INV_SLOT_WIDTH - 10 ) ) + { + swprintf( pStr, TacticalStr[ JAMMED_ITEM_STR ] ); + } + else + { + swprintf( pStr, TacticalStr[ SHORT_JAMMED_GUN ] ); + } + + VarFindFontCenterCoordinates( sX, sY, sWidth, sHeight , ITEM_FONT, &sNewX, &sNewY, pStr ); + + mprintf( sNewX, sNewY, pStr ); + gprintfinvalidate( sNewX, sNewY, pStr ); + } } } #if 0 @@ -2908,20 +2909,25 @@ void INVRenderItem( UINT32 uiBuffer, SOLDIERTYPE * pSoldier, OBJECTTYPE *pObjec // Flugente FTW 1 if ( gGameOptions.fWeaponOverheating == TRUE && gGameExternalOptions.fDisplayOverheatThermometer == TRUE && ( pItem->usItemClass & (IC_GUN | IC_LAUNCHER) || Item[pObject->usItem].barrel == TRUE ) ) - { - FLOAT overheatjampercentage = GetGunOverheatJamPercentage( pObject); + { + OBJECTTYPE* pObjShown = pObject; + + if ( pSoldier ) + pObjShown = pSoldier->GetUsedWeapon(pObject); + + FLOAT overheatjampercentage = GetGunOverheatJamPercentage( pObjShown ); UINT32 red, green, blue; UINT8 TemperatureStringNum = GetTemperatureString( overheatjampercentage, &red, &green, &blue ); UINT16 colour = Get16BPPColor( FROMRGB( red, green, blue ) ); - DrawItemUIBarEx( pObject, DRAW_ITEM_TEMPERATURE, sX, sY + sHeight-1, ITEMDESC_ITEM_STATUS_WIDTH, sHeight-1, colour, colour, TRUE, guiSAVEBUFFER ); + DrawItemUIBarEx( pObjShown, DRAW_ITEM_TEMPERATURE, sX, sY + sHeight-1, ITEMDESC_ITEM_STATUS_WIDTH, sHeight-1, colour, colour, TRUE, guiSAVEBUFFER ); } // display symbol if we are leaning our weapon on something // display only if eapon resting is allowed, display is allowed, item is a gun/launcher, we are a person, we hold the gun in our hand, and we are resting the gun - if ( gGameExternalOptions.fWeaponResting && gGameExternalOptions.fDisplayWeaponRestingIndicator && pItem->usItemClass & (IC_GUN | IC_LAUNCHER) && pSoldier && pSoldier->bActive && pSoldier->bInSector && &(pSoldier->inv[pSoldier->ubAttackingHand]) == pObject && pSoldier->IsWeaponMounted() ) + if ( gGameExternalOptions.fWeaponResting && gGameExternalOptions.fDisplayWeaponRestingIndicator && pItem->usItemClass & (IC_GUN | IC_LAUNCHER) && pSoldier && &(pSoldier->inv[pSoldier->ubAttackingHand]) == pObject && pSoldier->IsWeaponMounted() ) { SetRGBFontForeground( 95, 160, 154 ); @@ -2994,6 +3000,21 @@ void INVRenderItem( UINT32 uiBuffer, SOLDIERTYPE * pSoldier, OBJECTTYPE *pObjec swprintf( pStr, New113Message[MSG113_GL_AUTO] ); SetFontForeground( FONT_YELLOW ); } + else if(pSoldier->bWeaponMode == WM_ATTACHED_UB) + { + swprintf( pStr, New113Message[MSG113_UB] ); + SetFontForeground( FONT_ORANGE ); + } + else if(pSoldier->bWeaponMode == WM_ATTACHED_UB_BURST) + { + swprintf( pStr, New113Message[MSG113_UB_BRST] ); + SetFontForeground( FONT_ORANGE ); + } + else if(pSoldier->bWeaponMode == WM_ATTACHED_UB_AUTO) + { + swprintf( pStr, New113Message[MSG113_UB_AUTO] ); + SetFontForeground( FONT_ORANGE ); + } // Get length of string uiStringLength=StringPixLength(pStr, ITEM_FONT ); diff --git a/Tactical/Interface Panels.cpp b/Tactical/Interface Panels.cpp index 0ad8c1f0..2a0f7dbd 100644 --- a/Tactical/Interface Panels.cpp +++ b/Tactical/Interface Panels.cpp @@ -2167,12 +2167,15 @@ BOOLEAN CreateSMPanelButtons( ) iSMPanelImages[ OPTIONS_IMAGES ] = UseLoadedButtonImage( iSMPanelImages[ STANCEUP_IMAGES ] ,-1,24,-1,25,-1 ); - iBurstButtonImages[ WM_NORMAL ] = UseLoadedButtonImage( iSMPanelImages[ STANCEUP_IMAGES ], -1, 7, -1, -1, -1 ); + iBurstButtonImages[ WM_NORMAL ] = UseLoadedButtonImage( iSMPanelImages[ STANCEUP_IMAGES ], -1, 7, -1, -1, -1 ); iBurstButtonImages[ WM_BURST ] = UseLoadedButtonImage( iSMPanelImages[ STANCEUP_IMAGES ], -1, 17, -1, -1, -1 ); iBurstButtonImages[ WM_AUTOFIRE ] = UseLoadedButtonImage( iSMPanelImages[ STANCEUP_IMAGES ], -1, 17, -1, -1, -1 ); - iBurstButtonImages[ WM_ATTACHED_GL ] = UseLoadedButtonImage( iSMPanelImages[ STANCEUP_IMAGES ], -1, 26, -1, -1, -1 ); - iBurstButtonImages[ WM_ATTACHED_GL_BURST ] = UseLoadedButtonImage( iSMPanelImages[ STANCEUP_IMAGES ], -1, 17, -1, -1, -1 ); - iBurstButtonImages[ WM_ATTACHED_GL_AUTO ] = UseLoadedButtonImage( iSMPanelImages[ STANCEUP_IMAGES ], -1, 17, -1, -1, -1 ); + iBurstButtonImages[ WM_ATTACHED_GL ] = UseLoadedButtonImage( iSMPanelImages[ STANCEUP_IMAGES ], -1, 26, -1, -1, -1 ); + iBurstButtonImages[ WM_ATTACHED_GL_BURST ] = UseLoadedButtonImage( iSMPanelImages[ STANCEUP_IMAGES ], -1, 17, -1, -1, -1 ); + iBurstButtonImages[ WM_ATTACHED_GL_AUTO ] = UseLoadedButtonImage( iSMPanelImages[ STANCEUP_IMAGES ], -1, 17, -1, -1, -1 ); + iBurstButtonImages[ WM_ATTACHED_UB ] = UseLoadedButtonImage( iSMPanelImages[ STANCEUP_IMAGES ], -1, 7, -1, -1, -1 ); + iBurstButtonImages[ WM_ATTACHED_UB_BURST ] = UseLoadedButtonImage( iSMPanelImages[ STANCEUP_IMAGES ], -1, 17, -1, -1, -1 ); + iBurstButtonImages[ WM_ATTACHED_UB_AUTO ] = UseLoadedButtonImage( iSMPanelImages[ STANCEUP_IMAGES ], -1, 17, -1, -1, -1 ); FilenameForBPP("INTERFACE\\invadd-ons.sti", ubString); // Load button Graphics @@ -2396,6 +2399,9 @@ void RemoveSMPanelButtons( ) UnloadButtonImage( iBurstButtonImages[ WM_ATTACHED_GL ] ); UnloadButtonImage( iBurstButtonImages[ WM_ATTACHED_GL_BURST ] ); UnloadButtonImage( iBurstButtonImages[ WM_ATTACHED_GL_AUTO ] ); + UnloadButtonImage( iBurstButtonImages[ WM_ATTACHED_UB ] ); + UnloadButtonImage( iBurstButtonImages[ WM_ATTACHED_UB_BURST ] ); + UnloadButtonImage( iBurstButtonImages[ WM_ATTACHED_UB_AUTO ] ); } diff --git a/Tactical/Items.cpp b/Tactical/Items.cpp index 4865542d..29e41174 100644 --- a/Tactical/Items.cpp +++ b/Tactical/Items.cpp @@ -3587,7 +3587,9 @@ INT8 FindAmmoToReload( SOLDIERTYPE * pSoldier, INT8 bWeaponIn, INT8 bExcludeSlot { return( NO_SLOT ); } - pObj = &(pSoldier->inv[bWeaponIn]); + + // Flugente: check for underbarrel weapons and use that object if necessary + pObj = pSoldier->GetUsedWeapon( &(pSoldier->inv[bWeaponIn]) ); // manual recharge if ((*pObj)[0]->data.gun.ubGunShotsLeft && !((*pObj)[0]->data.gun.ubGunState & GS_CARTRIDGE_IN_CHAMBER) ) @@ -3656,7 +3658,9 @@ BOOLEAN AutoReload( SOLDIERTYPE * pSoldier ) BOOLEAN fRet; CHECKF( pSoldier ); - pObj = &(pSoldier->inv[HANDPOS]); + + // Flugente: check for underbarrel weapons and use that object if necessary + pObj = pSoldier->GetUsedWeapon( &(pSoldier->inv[HANDPOS]) ); // manual recharge if ((*pObj)[0]->data.gun.ubGunShotsLeft && !((*pObj)[0]->data.gun.ubGunState & GS_CARTRIDGE_IN_CHAMBER) ) @@ -3746,7 +3750,9 @@ BOOLEAN AutoReload( SOLDIERTYPE * pSoldier ) // then do a reload of both guns! if ( (fRet == TRUE) && pSoldier->IsValidSecondHandShotForReloadingPurposes( ) ) { - pObj = &(pSoldier->inv[SECONDHANDPOS]); + // Flugente: check for underbarrel weapons and use that object if necessary + pObj = pSoldier->GetUsedWeapon( &(pSoldier->inv[SECONDHANDPOS]) ); + bSlot = FindAmmoToReload( pSoldier, SECONDHANDPOS, NO_SLOT ); if (bSlot != NO_SLOT) { @@ -4612,7 +4618,9 @@ BOOLEAN OBJECTTYPE::AttachObjectNAS( SOLDIERTYPE * pSoldier, OBJECTTYPE * pAttac { //Make sure it's actually on that gun.. //if(FindAttachment_GrenadeLauncher(this)->usItem == attachmentObject.usItem){ - if(FindAttachment(this, attachmentObject.usItem, subObject)->usItem == attachmentObject.usItem){ + // Flugente: if we attach a gun to another gun, do not transfer attachments + if( Item[attachmentObject.usItem].usItemClass != IC_GUN && FindAttachment(this, attachmentObject.usItem, subObject)->usItem == attachmentObject.usItem) + { // transfer the grenade from the grenade launcher to the gun //To know wether this grenade is valid, we need to correct the slots, because they may have changed when attaching the UGL @@ -5861,7 +5869,10 @@ BOOLEAN PlaceObject( SOLDIERTYPE * pSoldier, INT8 bPos, OBJECTTYPE * pObj ) case IC_GUN: if (Item[pObj->usItem].usItemClass == IC_AMMO) { - if (Weapon[pInSlot->usItem].ubCalibre == Magazine[Item[pObj->usItem].ubClassIndex].ubCalibre) + // Flugente: if we have an underbarrel weapon, we can reload that + OBJECTTYPE* pObjUsed = pSoldier->GetUsedWeapon(pInSlot); + + if (Weapon[pObjUsed->usItem].ubCalibre == Magazine[Item[pObj->usItem].ubClassIndex].ubCalibre) { //CHRISL: Work differently with ammo crates but only when not in combat if(Magazine[Item[pObj->usItem].ubClassIndex].ubMagType >= AMMO_BOX) @@ -5881,7 +5892,7 @@ BOOLEAN PlaceObject( SOLDIERTYPE * pSoldier, INT8 bPos, OBJECTTYPE * pObj ) { if(Item[loop].usItemClass == IC_AMMO) { - if(Magazine[Item[loop].ubClassIndex].ubCalibre == Weapon[pInSlot->usItem].ubCalibre && Magazine[Item[loop].ubClassIndex].ubAmmoType == Magazine[Item[pObj->usItem].ubClassIndex].ubAmmoType && Magazine[Item[loop].ubClassIndex].ubMagSize == GetMagSize(pInSlot)) + if(Magazine[Item[loop].ubClassIndex].ubCalibre == Weapon[pObjUsed->usItem].ubCalibre && Magazine[Item[loop].ubClassIndex].ubAmmoType == Magazine[Item[pObj->usItem].ubClassIndex].ubAmmoType && Magazine[Item[loop].ubClassIndex].ubMagSize == GetMagSize(pObjUsed)) newItem = loop; } } @@ -5891,7 +5902,7 @@ BOOLEAN PlaceObject( SOLDIERTYPE * pSoldier, INT8 bPos, OBJECTTYPE * pObj ) ubShotsLeft = (*pObj)[0]->data.ubShotsLeft; for(UINT8 clip = 0; clip < 5; clip++) { - magSize = GetMagSize(pInSlot); + magSize = GetMagSize(pObjUsed); if(ubShotsLeft < magSize) magSize = ubShotsLeft; if(CreateAmmo(newItem, &tempClip, magSize)) @@ -5963,7 +5974,7 @@ BOOLEAN PlaceObject( SOLDIERTYPE * pSoldier, INT8 bPos, OBJECTTYPE * pObj ) } } else - return( ReloadGun( pSoldier, pInSlot, pObj ) ); + return( ReloadGun( pSoldier, pObjUsed, pObj ) ); } } break; @@ -7470,6 +7481,24 @@ BOOLEAN OBJECTTYPE::RemoveAttachment( OBJECTTYPE* pAttachment, OBJECTTYPE * pNew } } + // if in attached weapon mode and don't have weapon with GL attached in hand, reset weapon mode + if ( ( (pSoldier->bWeaponMode == WM_ATTACHED_GL || pSoldier->bWeaponMode == WM_ATTACHED_GL_BURST || pSoldier->bWeaponMode == WM_ATTACHED_GL_AUTO )&& !IsGrenadeLauncherAttached( &(pSoldier->inv[ HANDPOS ]) ) ) || + ( (pSoldier->bWeaponMode == WM_ATTACHED_UB || pSoldier->bWeaponMode == WM_ATTACHED_UB_BURST || pSoldier->bWeaponMode == WM_ATTACHED_UB_AUTO )&& !IsUnderBarrelAttached( &(pSoldier->inv[ HANDPOS ] ) ) ) ) + { + if ( !Weapon[pSoldier->inv[ HANDPOS ].usItem].NoSemiAuto ) + { + pSoldier->bWeaponMode = WM_NORMAL; + pSoldier->bDoBurst = FALSE; + pSoldier->bDoAutofire = 0; + } + else + { + pSoldier->bWeaponMode = WM_AUTOFIRE; + pSoldier->bDoBurst = TRUE; + pSoldier->bDoAutofire = 1; + } + } + if(Item[this->usItem].usItemClass == IC_GUN && oldMagSize != GetMagSize(this, subObject)){ fInterfacePanelDirty = DIRTYLEVEL2; RenderBulletIcon(this, subObject); @@ -9477,9 +9506,13 @@ void GetRecoil( SOLDIERTYPE *pSoldier, OBJECTTYPE *pObj, INT8 *bRecoilX, INT8 *b { *bRecoilX = 0; *bRecoilY = 0; + + // Flugente: get weapon actually used (might be an underbarrel shotgun) + OBJECTTYPE* pObjUsed = pSoldier->GetUsedWeapon(pObj); + OBJECTTYPE* pObjUsedInHand = pSoldier->GetUsedWeapon( &(pSoldier->inv[HANDPOS]) ); //if (ubNumBullet < 2) - if (ubNumBullet < Weapon[pObj->usItem].ubRecoilDelay) + if (ubNumBullet < Weapon[pObjUsed->usItem].ubRecoilDelay) { // The first bullet in a volley never has recoil - it hasn't "set in" yet. Only the second+ bullets // will have any recoil. @@ -9488,13 +9521,15 @@ void GetRecoil( SOLDIERTYPE *pSoldier, OBJECTTYPE *pObj, INT8 *bRecoilX, INT8 *b return; } - *bRecoilX = Weapon[pObj->usItem].bRecoilX; - *bRecoilY = Weapon[pObj->usItem].bRecoilY; + *bRecoilX = Weapon[pObjUsed->usItem].bRecoilX; + *bRecoilY = Weapon[pObjUsed->usItem].bRecoilY; // Apply a percentage-based modifier. This can increase or decrease BOTH axes. At most, it can eliminate // recoil on the gun. - INT16 sPercentRecoilModifier = GetPercentRecoilModifier( pObj ); + //INT16 sPercentRecoilModifier = GetPercentRecoilModifier( pObj ); + INT16 sPercentRecoilModifier = __max(-100, (GetBasePercentRecoilModifier( pObjUsed ) + GetAttachmentPercentRecoilModifier( pObjUsedInHand ) )); + *bRecoilX += (*bRecoilX * sPercentRecoilModifier ) / 100; *bRecoilY += (*bRecoilY * sPercentRecoilModifier ) / 100; @@ -9505,7 +9540,10 @@ void GetRecoil( SOLDIERTYPE *pSoldier, OBJECTTYPE *pObj, INT8 *bRecoilX, INT8 *b INT8 bRecoilAdjustX = 0; INT8 bRecoilAdjustY = 0; - GetFlatRecoilModifier( pObj, &bRecoilAdjustX, &bRecoilAdjustY ); + //GetFlatRecoilModifier( pObj, &bRecoilAdjustX, &bRecoilAdjustY ); + GetBaseFlatRecoilModifier( pObj, &bRecoilAdjustX, &bRecoilAdjustY ); + GetAttachmentFlatRecoilModifier( &pSoldier->inv[HANDPOS], &bRecoilAdjustX, &bRecoilAdjustY); + //JMich TODO: Currently no check for dual wielding *bRecoilX = __max(0, *bRecoilX + bRecoilAdjustX); *bRecoilY = __max(0, *bRecoilY + bRecoilAdjustY); @@ -9517,6 +9555,44 @@ void GetRecoil( SOLDIERTYPE *pSoldier, OBJECTTYPE *pObj, INT8 *bRecoilX, INT8 *b // HEADROCK HAM 4: This function calculates the flat recoil adjustment for a gun. Flat adjustment increases // or decreases recoil by a specific number of points in either the vertical or horizontal axes (or both). // It can potentially cause a weapon it reverse its recoil direction. +void GetBaseFlatRecoilModifier( OBJECTTYPE *pObj, INT8 *bRecoilModifierX, INT8 *bRecoilModifierY ) +{ + INT8 bRecoilAdjustX = 0; + INT8 bRecoilAdjustY = 0; + if (pObj->exists() == true && UsingNewCTHSystem() == true) + { + // Inherent item modifiers + bRecoilAdjustX += BonusReduceMore( Item[pObj->usItem].RecoilModifierX, (*pObj)[0]->data.objectStatus ); + bRecoilAdjustY += BonusReduceMore( Item[pObj->usItem].RecoilModifierY, (*pObj)[0]->data.objectStatus ); + + // Ammo item modifiers + bRecoilAdjustX += Item[(*pObj)[0]->data.gun.usGunAmmoItem].RecoilModifierX; + bRecoilAdjustY += Item[(*pObj)[0]->data.gun.usGunAmmoItem].RecoilModifierY; + } + + *bRecoilModifierX = bRecoilAdjustX; + *bRecoilModifierY = bRecoilAdjustY; +} +void GetAttachmentFlatRecoilModifier( OBJECTTYPE *pObj, INT8 *bRecoilModifierX, INT8 *bRecoilModifierY ) +{ + INT8 bRecoilAdjustX = 0; + INT8 bRecoilAdjustY = 0; + if (pObj->exists() == true && UsingNewCTHSystem() == true) + { + // Attachment item modifiers + for (attachmentList::iterator iter = (*pObj)[0]->attachments.begin(); iter != (*pObj)[0]->attachments.end(); ++iter) + { + if (iter->exists()) + { + bRecoilAdjustX += BonusReduceMore( Item[iter->usItem].RecoilModifierX, (*iter)[0]->data.objectStatus ); + bRecoilAdjustY += BonusReduceMore( Item[iter->usItem].RecoilModifierY, (*iter)[0]->data.objectStatus ); + } + } + } + + *bRecoilModifierX += bRecoilAdjustX; + *bRecoilModifierY += bRecoilAdjustY; +} void GetFlatRecoilModifier( OBJECTTYPE *pObj, INT8 *bRecoilModifierX, INT8 *bRecoilModifierY ) { @@ -9553,6 +9629,39 @@ void GetFlatRecoilModifier( OBJECTTYPE *pObj, INT8 *bRecoilModifierX, INT8 *bRec // This adjustment either increases or decreases the gun's vertical and horizontal recoil at the same time. Due to // the percentage-based nature of this modifier, it cannot cause a gun to reverse its recoil - only diminish it to // zero. +INT16 GetBasePercentRecoilModifier( OBJECTTYPE *pObj) +{ + INT16 sRecoilAdjust = 0; + + if (pObj->exists() == true && UsingNewCTHSystem() == true) + { + // Inherent item modifiers + sRecoilAdjust += BonusReduceMore( Item[pObj->usItem].PercentRecoilModifier, (*pObj)[0]->data.objectStatus ); + + // Ammo item modifiers + sRecoilAdjust += Item[(*pObj)[0]->data.gun.usGunAmmoItem].PercentRecoilModifier; + } + + return (sRecoilAdjust); +} +INT16 GetAttachmentPercentRecoilModifier( OBJECTTYPE *pObj) +{ + INT16 sRecoilAdjust = 0; + + if (pObj->exists() == true && UsingNewCTHSystem() == true) + { + for (attachmentList::iterator iter = (*pObj)[0]->attachments.begin(); iter != (*pObj)[0]->attachments.end(); ++iter) + { + if (iter->exists()) + { + sRecoilAdjust += BonusReduceMore( Item[iter->usItem].PercentRecoilModifier, (*iter)[0]->data.objectStatus ); + } + } + } + + return (sRecoilAdjust); + +} INT16 GetPercentRecoilModifier( OBJECTTYPE *pObj ) { INT16 sRecoilAdjust = 0; @@ -10550,6 +10659,60 @@ UINT16 GetAttachedGrenadeLauncher( OBJECTTYPE * pObj ) return( NONE ); } +UINT16 GetAttachedUnderBarrel( OBJECTTYPE * pObj ) +{ + if (pObj->exists() == true) { + + for (attachmentList::iterator iter = (*pObj)[0]->attachments.begin(); iter != (*pObj)[0]->attachments.end(); ++iter) { + if ((Item[iter->usItem].usItemClass & (IC_GUN | IC_BLADE) ) && iter->exists()) + { + return( (UINT16) Item[iter->usItem].uiIndex ); + } + } + } + return( NONE ); +} +BOOLEAN IsUnderBarrelAttached( OBJECTTYPE * pObj, UINT8 subObject ) +{ + if (pObj->exists() == true) { + + for (attachmentList::iterator iter = (*pObj)[subObject]->attachments.begin(); iter != (*pObj)[subObject]->attachments.end(); ++iter) { + if (Item[iter->usItem].usItemClass & (IC_GUN | IC_BLADE) && iter->exists() ) + { + return TRUE; + } + } + } + return FALSE; +} + +OBJECTTYPE* FindAttachment_UnderBarrel( OBJECTTYPE * pObj ) +{ + if (pObj->exists() == true) { + + for (attachmentList::iterator iter = (*pObj)[0]->attachments.begin(); iter != (*pObj)[0]->attachments.end(); ++iter) { + if (Item[iter->usItem].usItemClass & (IC_GUN | IC_BLADE) && iter->exists() ) + { + return( &(*iter) ); + } + } + } + return( NULL ); +} + +INT16 GetUnderBarrelStatus( OBJECTTYPE * pObj ) +{ + if (pObj->exists() == true) { + + for (attachmentList::iterator iter = (*pObj)[0]->attachments.begin(); iter != (*pObj)[0]->attachments.end(); ++iter) { + if (Item[iter->usItem].usItemClass & (IC_GUN | IC_BLADE) && iter->exists()) + { + return( (*iter)[0]->data.objectStatus ); + } + } + } + return( ITEM_NOT_FOUND ); +} INT16 GetAttachedArmourBonus( OBJECTTYPE * pObj ) { diff --git a/Tactical/Items.h b/Tactical/Items.h index 7e81f868..d8f00945 100644 --- a/Tactical/Items.h +++ b/Tactical/Items.h @@ -300,7 +300,11 @@ INT16 GetMagSizeBonus( OBJECTTYPE * pObj, UINT8 subObject = 0 ); // HEADROCK HAM 4: This function now calculates and returns the weapon's recoil as X/Y offsets. void GetRecoil( SOLDIERTYPE *pSoldier, OBJECTTYPE *pObj, INT8 *bRecoilX, INT8 *bRecoilY, UINT8 ubNumBullet ); +void GetBaseFlatRecoilModifier( OBJECTTYPE *pObj, INT8 *bRecoilModifierX, INT8 *bRecoilModifierY); +void GetAttachmentFlatRecoilModifier( OBJECTTYPE *pObj, INT8 *bRecoilModifierX, INT8 *bRecoilModifierY); void GetFlatRecoilModifier( OBJECTTYPE *pObj, INT8 *bRecoilModifierX, INT8 *bRecoilModifierY ); +INT16 GetBasePercentRecoilModifier( OBJECTTYPE *pObj ); +INT16 GetAttachmentPercentRecoilModifier( OBJECTTYPE *pObj); INT16 GetPercentRecoilModifier( OBJECTTYPE *pObj ); // HEADROCK HAM 4: This function returns whether the last bullet in a burst/autofire volley was a tracer. BOOLEAN WasPrevBulletATracer( SOLDIERTYPE *pSoldier, OBJECTTYPE *pWeapon ); @@ -337,6 +341,14 @@ INT16 GetGrenadeLauncherStatus( OBJECTTYPE * pObj ); BOOLEAN IsGrenadeLauncherAttached( OBJECTTYPE * pObj, UINT8 subObject = 0 ); OBJECTTYPE* FindAttachment_GrenadeLauncher( OBJECTTYPE * pObj ); UINT16 GetAttachedGrenadeLauncher( OBJECTTYPE * pObj ); + +// JMich & Flugente: functions for underbarrel weapons +INT16 GetUnderBarrelStatus( OBJECTTYPE * pObj ); +BOOLEAN IsUnderBarrelAttached( OBJECTTYPE * pObj, UINT8 subObject = 0 ); +OBJECTTYPE* FindAttachment_UnderBarrel( OBJECTTYPE * pObj ); +UINT16 GetAttachedUnderBarrel( OBJECTTYPE * pObj ); +OBJECTTYPE* GetUsedWeapon( OBJECTTYPE * pObj ); + INT8 FindRocketLauncher( SOLDIERTYPE * pSoldier ); INT8 FindRocketLauncherOrCannon( SOLDIERTYPE * pSoldier ); INT8 FindNonSmokeLaunchable( SOLDIERTYPE * pSoldier, UINT16 usWeapon ); diff --git a/Tactical/LOS.cpp b/Tactical/LOS.cpp index 959be69a..4dcaeec9 100644 --- a/Tactical/LOS.cpp +++ b/Tactical/LOS.cpp @@ -3988,12 +3988,15 @@ INT8 FireBullet( SOLDIERTYPE * pFirer, BULLET * pBullet, BOOLEAN fFake ) } else { + OBJECTTYPE* pObjAttHand = pFirer->GetUsedWeapon( &pFirer->inv[pFirer->ubAttackingHand] ); + UINT16 usItemUsed = pFirer->GetUsedWeaponNumber( &pFirer->inv[pFirer->ubAttackingHand] ); + //afp-start //always a fast bullet if ( pBullet->usFlags & ( BULLET_FLAG_CREATURE_SPIT | BULLET_FLAG_KNIFE | BULLET_FLAG_MISSILE | BULLET_FLAG_SMALL_MISSILE | BULLET_FLAG_TANK_CANNON | BULLET_FLAG_FLAME /*| BULLET_FLAG_TRACER*/ ) ) - pBullet->usClockTicksPerUpdate = (Weapon[ pFirer->usAttackingWeapon ].ubBulletSpeed + GetBulletSpeedBonus(&pFirer->inv[pFirer->ubAttackingHand]) ) / 10; + pBullet->usClockTicksPerUpdate = (Weapon[ usItemUsed ].ubBulletSpeed + GetBulletSpeedBonus( pObjAttHand ) ) / 10; else if (gGameSettings.fOptions[TOPTION_ALTERNATE_BULLET_GRAPHICS]) - pBullet->usClockTicksPerUpdate = (Weapon[ pFirer->usAttackingWeapon ].ubBulletSpeed + GetBulletSpeedBonus(&pFirer->inv[pFirer->ubAttackingHand]) ) / 10; + pBullet->usClockTicksPerUpdate = (Weapon[ usItemUsed ].ubBulletSpeed + GetBulletSpeedBonus( pObjAttHand ) ) / 10; else pBullet->usClockTicksPerUpdate = 1; //afp-end @@ -4068,6 +4071,8 @@ INT8 FireBulletGivenTargetNCTH( SOLDIERTYPE * pFirer, FLOAT dEndX, FLOAT dEndY, UINT16 usBulletFlags = 0; int n=0; + OBJECTTYPE* pObjAttHand = pFirer->GetUsedWeapon( &(pFirer->inv[pFirer->ubAttackingHand]) ); + //DebugMsg(TOPIC_JA2,DBG_LEVEL_3,String("FireBulletGivenTarget")); CalculateSoldierZPos( pFirer, FIRING_POS, &dStartZ ); @@ -4132,11 +4137,11 @@ INT8 FireBulletGivenTargetNCTH( SOLDIERTYPE * pFirer, FLOAT dEndX, FLOAT dEndY, // HEADROCK HAM B2.5: Set tracer effect on/off for individual bullets in a Tracer Magazine, as part of the // New Tracer System. else if (gGameExternalOptions.ubRealisticTracers > 0 && gGameExternalOptions.ubNumBulletsPerTracer > 0 && (pFirer->bDoAutofire > 0 || pFirer->bDoBurst > 0) - && AmmoTypes[ pFirer->inv[pFirer->ubAttackingHand][0]->data.gun.ubGunAmmoType ].tracerEffect ) + && AmmoTypes[ (*pObjAttHand)[0]->data.gun.ubGunAmmoType ].tracerEffect ) { UINT16 iBulletsLeft, iBulletsPerTracer; iBulletsPerTracer = gGameExternalOptions.ubNumBulletsPerTracer; - iBulletsLeft = pFirer->inv[pFirer->ubAttackingHand][0]->data.gun.ubGunShotsLeft + pFirer->bDoBurst; + iBulletsLeft = (*pObjAttHand)[0]->data.gun.ubGunShotsLeft + pFirer->bDoBurst; if ((((iBulletsLeft - (pFirer->bDoBurst - 1)) / iBulletsPerTracer) - ((iBulletsLeft - pFirer->bDoBurst) / iBulletsPerTracer)) == 1) { @@ -4147,7 +4152,7 @@ INT8 FireBulletGivenTargetNCTH( SOLDIERTYPE * pFirer, FLOAT dEndX, FLOAT dEndY, fTracer = FALSE; } } - else if ( AmmoTypes[ pFirer->inv[pFirer->ubAttackingHand][0]->data.gun.ubGunAmmoType ].tracerEffect && (pFirer->bDoBurst || gGameSettings.fOptions[ TOPTION_TRACERS_FOR_SINGLE_FIRE ]) ) + else if ( AmmoTypes[ (*pObjAttHand)[0]->data.gun.ubGunAmmoType ].tracerEffect && (pFirer->bDoBurst || gGameSettings.fOptions[ TOPTION_TRACERS_FOR_SINGLE_FIRE ]) ) { //usBulletFlags |= BULLET_FLAG_TRACER; fTracer = TRUE; @@ -4156,9 +4161,9 @@ INT8 FireBulletGivenTargetNCTH( SOLDIERTYPE * pFirer, FLOAT dEndX, FLOAT dEndY, ubImpact =(UINT8) GetDamage(&pFirer->inv[pFirer->ubAttackingHand]); //zilpin: Begin new code block for spread patterns, number of projectiles, impact adjustment, etc. { - ObjectData *weapon = &(pFirer->inv[pFirer->ubAttackingHand][0]->data); + ObjectData *weapon = &((*pObjAttHand)[0]->data); ubShots = AmmoTypes[ weapon->gun.ubGunAmmoType].numberOfBullets; - ubSpreadIndex = GetSpreadPattern( &pFirer->inv[pFirer->ubAttackingHand] ); + ubSpreadIndex = GetSpreadPattern( pObjAttHand ); if( ubShots>1 && !fFake ) { fBuckshot = true; @@ -4407,7 +4412,7 @@ INT8 FireBulletGivenTargetNCTH( SOLDIERTYPE * pFirer, FLOAT dEndX, FLOAT dEndY, if ( pBullet->usFlags & BULLET_FLAG_KNIFE ) { - pBullet->ubItemStatus = pFirer->inv[pFirer->ubAttackingHand][0]->data.objectStatus; + pBullet->ubItemStatus = (*pObjAttHand)[0]->data.objectStatus; } // apply increments for first move @@ -4435,7 +4440,7 @@ INT8 FireBulletGivenTargetNCTH( SOLDIERTYPE * pFirer, FLOAT dEndX, FLOAT dEndY, pBullet->iImpact = ubImpact; - pBullet->iRange = GunRange( &(pFirer->inv[pFirer->ubAttackingHand]), pFirer ); // SANDRO - added argument + pBullet->iRange = GunRange( pObjAttHand, pFirer ); // SANDRO - added argument pBullet->sTargetGridNo = ((INT32)dEndX) / CELL_X_SIZE + ((INT32)dEndY) / CELL_Y_SIZE * WORLD_COLS; pBullet->bStartCubesAboveLevelZ = (INT8) CONVERT_HEIGHTUNITS_TO_INDEX( (INT32)dStartZ - CONVERT_PIXELS_TO_HEIGHTUNITS( gpWorldLevelData[ pFirer->sGridNo ].sHeight ) ); @@ -4447,11 +4452,11 @@ INT8 FireBulletGivenTargetNCTH( SOLDIERTYPE * pFirer, FLOAT dEndX, FLOAT dEndY, // HEADROCK HAM BETA2.5: New method for signifying whether a bullet is a tracer or not, using an individual // bullet structure flag. Hehehehe, I think this is kind of reverting to old code, isn't it? if (gGameExternalOptions.ubRealisticTracers > 0 && gGameExternalOptions.ubNumBulletsPerTracer > 0 && (pFirer->bDoAutofire > 0 || pFirer->bDoBurst > 0) - && AmmoTypes[ pFirer->inv[pFirer->ubAttackingHand][0]->data.gun.ubGunAmmoType ].tracerEffect ) + && AmmoTypes[ (*pObjAttHand)[0]->data.gun.ubGunAmmoType ].tracerEffect ) { UINT16 iBulletsLeft, iBulletsPerTracer; iBulletsPerTracer = gGameExternalOptions.ubNumBulletsPerTracer; - iBulletsLeft = pFirer->inv[pFirer->ubAttackingHand][0]->data.gun.ubGunShotsLeft + pFirer->bDoBurst; + iBulletsLeft = (*pObjAttHand)[0]->data.gun.ubGunShotsLeft + pFirer->bDoBurst; // Is this specific bullet a tracer? - based on how many tracers there are per regular bullets in // a tracer magazine (INI-settable). @@ -4540,6 +4545,8 @@ INT8 FireBulletGivenTarget( SOLDIERTYPE * pFirer, FLOAT dEndX, FLOAT dEndY, FLOA UINT16 usBulletFlags = 0; int n=0; + OBJECTTYPE* pObjAttHand = pFirer->GetUsedWeapon( &(pFirer->inv[pFirer->ubAttackingHand]) ); + //DebugMsg(TOPIC_JA2,DBG_LEVEL_3,String("FireBulletGivenTarget")); CalculateSoldierZPos( pFirer, FIRING_POS, &dStartZ ); @@ -4574,7 +4581,7 @@ INT8 FireBulletGivenTarget( SOLDIERTYPE * pFirer, FLOAT dEndX, FLOAT dEndY, FLOA ubShots = 1; fTracer = FALSE; - + // Check if we have spit as a weapon! if ( Weapon[ usHandItem ].ubWeaponClass == MONSTERCLASS ) { @@ -4604,11 +4611,11 @@ INT8 FireBulletGivenTarget( SOLDIERTYPE * pFirer, FLOAT dEndX, FLOAT dEndY, FLOA // HEADROCK HAM B2.5: Set tracer effect on/off for individual bullets in a Tracer Magazine, as part of the // New Tracer System. else if (gGameExternalOptions.ubRealisticTracers > 0 && gGameExternalOptions.ubNumBulletsPerTracer > 0 && (pFirer->bDoAutofire > 0 || pFirer->bDoBurst > 0) - && AmmoTypes[ pFirer->inv[pFirer->ubAttackingHand][0]->data.gun.ubGunAmmoType ].tracerEffect ) + && AmmoTypes[ (*pObjAttHand)[0]->data.gun.ubGunAmmoType ].tracerEffect ) { UINT16 iBulletsLeft, iBulletsPerTracer; iBulletsPerTracer = gGameExternalOptions.ubNumBulletsPerTracer; - iBulletsLeft = pFirer->inv[pFirer->ubAttackingHand][0]->data.gun.ubGunShotsLeft + pFirer->bDoBurst; + iBulletsLeft = (*pObjAttHand)[0]->data.gun.ubGunShotsLeft + pFirer->bDoBurst; if ((((iBulletsLeft - (pFirer->bDoBurst - 1)) / iBulletsPerTracer) - ((iBulletsLeft - pFirer->bDoBurst) / iBulletsPerTracer)) == 1) { @@ -4625,7 +4632,7 @@ INT8 FireBulletGivenTarget( SOLDIERTYPE * pFirer, FLOAT dEndX, FLOAT dEndY, FLOA fTracer = TRUE; } - ubImpact =(UINT8) GetDamage(&pFirer->inv[pFirer->ubAttackingHand]); + ubImpact =(UINT8) GetDamage(pObjAttHand); //zilpin: pellet spread patterns externalized in XML /* zilpin: The section below, including line comments, is the original adjustment made to multiple projectile stats. Left in comments for reference, but the new handling is after it. @@ -4661,9 +4668,9 @@ INT8 FireBulletGivenTarget( SOLDIERTYPE * pFirer, FLOAT dEndX, FLOAT dEndY, FLOA */ //zilpin: Begin new code block for spread patterns, number of projectiles, impact adjustment, etc. { - ObjectData *weapon = &(pFirer->inv[pFirer->ubAttackingHand][0]->data); + ObjectData *weapon = &((*pObjAttHand)[0]->data); ubShots = AmmoTypes[ weapon->gun.ubGunAmmoType].numberOfBullets; - ubSpreadIndex = GetSpreadPattern( &pFirer->inv[pFirer->ubAttackingHand] ); + ubSpreadIndex = GetSpreadPattern( pObjAttHand ); if( ubShots>1 && !fFake ) { fBuckshot = true; @@ -4905,7 +4912,7 @@ INT8 FireBulletGivenTarget( SOLDIERTYPE * pFirer, FLOAT dEndX, FLOAT dEndY, FLOA if ( pBullet->usFlags & BULLET_FLAG_KNIFE ) { - pBullet->ubItemStatus = pFirer->inv[pFirer->ubAttackingHand][0]->data.objectStatus; + pBullet->ubItemStatus = (*pObjAttHand)[0]->data.objectStatus; } // apply increments for first move @@ -4933,7 +4940,7 @@ INT8 FireBulletGivenTarget( SOLDIERTYPE * pFirer, FLOAT dEndX, FLOAT dEndY, FLOA pBullet->iImpact = ubImpact; - pBullet->iRange = GunRange( &(pFirer->inv[pFirer->ubAttackingHand]), pFirer ); // SANDRO - added argument + pBullet->iRange = GunRange( pObjAttHand, pFirer ); // SANDRO - added argument pBullet->sTargetGridNo = ((INT32)dEndX) / CELL_X_SIZE + ((INT32)dEndY) / CELL_Y_SIZE * WORLD_COLS; pBullet->bStartCubesAboveLevelZ = (INT8) CONVERT_HEIGHTUNITS_TO_INDEX( (INT32)dStartZ - CONVERT_PIXELS_TO_HEIGHTUNITS( gpWorldLevelData[ pFirer->sGridNo ].sHeight ) ); @@ -4945,11 +4952,11 @@ INT8 FireBulletGivenTarget( SOLDIERTYPE * pFirer, FLOAT dEndX, FLOAT dEndY, FLOA // HEADROCK HAM BETA2.5: New method for signifying whether a bullet is a tracer or not, using an individual // bullet structure flag. Hehehehe, I think this is kind of reverting to old code, isn't it? if (gGameExternalOptions.ubRealisticTracers > 0 && gGameExternalOptions.ubNumBulletsPerTracer > 0 && (pFirer->bDoAutofire > 0 || pFirer->bDoBurst > 0) - && AmmoTypes[ pFirer->inv[pFirer->ubAttackingHand][0]->data.gun.ubGunAmmoType ].tracerEffect ) + && AmmoTypes[ (*pObjAttHand)[0]->data.gun.ubGunAmmoType ].tracerEffect ) { UINT16 iBulletsLeft, iBulletsPerTracer; iBulletsPerTracer = gGameExternalOptions.ubNumBulletsPerTracer; - iBulletsLeft = pFirer->inv[pFirer->ubAttackingHand][0]->data.gun.ubGunShotsLeft + pFirer->bDoBurst; + iBulletsLeft = (*pObjAttHand)[0]->data.gun.ubGunShotsLeft + pFirer->bDoBurst; // Is this specific bullet a tracer? - based on how many tracers there are per regular bullets in // a tracer magazine (INI-settable). @@ -5008,10 +5015,12 @@ INT8 ChanceToGetThrough( SOLDIERTYPE * pFirer, FLOAT dEndX, FLOAT dEndY, FLOAT d { BOOLEAN fBuckShot = FALSE; + OBJECTTYPE* pObjHand = pFirer->GetUsedWeapon( &pFirer->inv[HANDPOS] ); + // if shotgun, shotgun would have to be in main hand - if ( pFirer->inv[ HANDPOS ].usItem == pFirer->usAttackingWeapon ) + if ( pObjHand->usItem == pFirer->usAttackingWeapon ) { - if ( AmmoTypes[pFirer->inv[ HANDPOS ][0]->data.gun.ubGunAmmoType].numberOfBullets > 1 ) + if ( AmmoTypes[ (*pObjHand)[0]->data.gun.ubGunAmmoType].numberOfBullets > 1 ) { fBuckShot = TRUE; } @@ -7332,8 +7341,11 @@ FLOAT CalcBulletDeviation( SOLDIERTYPE *pShooter, FLOAT *dShotOffsetX, FLOAT *dS // We start by reading the gun's Accuracy value. We'll use that as the basis for everything else. + // Flugente: determine used gun + OBJECTTYPE* pObjAttHand = pShooter->GetUsedWeapon( &pShooter->inv[ pShooter->ubAttackingHand ] ); + INT16 sAccuracy = GetGunAccuracy( pWeapon ); - UINT16 sEffRange = Weapon[Item[pShooter->inv[pShooter->ubAttackingHand].usItem].ubClassIndex].usRange + GetRangeBonus(&(pShooter->inv[ pShooter->ubAttackingHand ])); + UINT16 sEffRange = Weapon[Item[pObjAttHand->usItem].ubClassIndex].usRange + GetRangeBonus( pObjAttHand ); // WANNE: I got a CTD in a multiplayer test game, because sEffRange was 0 (division to zero). // I don't know why this happend? diff --git a/Tactical/Points.cpp b/Tactical/Points.cpp index 506c4bb3..22436577 100644 --- a/Tactical/Points.cpp +++ b/Tactical/Points.cpp @@ -1478,8 +1478,12 @@ INT16 CalcTotalAPsToAttack( SOLDIERTYPE *pSoldier, INT32 sGridNo, UINT8 ubAddTur BOOLEAN fAddingRaiseGunCost = FALSE; // LOOK IN BUDDY'S HAND TO DETERMINE WHAT TO DO HERE + // Flugente: check for underbarrel weapons and use that object if necessary + OBJECTTYPE* AttackingWeapon = pSoldier->GetUsedWeapon( &(pSoldier->inv[HANDPOS]) ); + UINT16 usUBItemNum = pSoldier->GetUsedWeaponNumber( &(pSoldier->inv[HANDPOS]) ); + usItemNum = pSoldier->inv[HANDPOS].usItem; - uiItemClass = Item[ usItemNum ].usItemClass; + uiItemClass = Item[ usUBItemNum ].usItemClass; if ( uiItemClass == IC_GUN || uiItemClass == IC_LAUNCHER || uiItemClass == IC_TENTACLES || uiItemClass == IC_THROWING_KNIFE ) { @@ -1487,10 +1491,10 @@ INT16 CalcTotalAPsToAttack( SOLDIERTYPE *pSoldier, INT32 sGridNo, UINT8 ubAddTur if ( pSoldier->bDoBurst ) { - if(pSoldier->bDoAutofire && GetAutofireShotsPerFiveAPs(&pSoldier->inv[HANDPOS]) > 0 ) - sAPCost += CalcAPsToAutofire( pSoldier->CalcActionPoints( ), &(pSoldier->inv[HANDPOS]), pSoldier->bDoAutofire ); + if(pSoldier->bDoAutofire && GetAutofireShotsPerFiveAPs(AttackingWeapon) > 0 ) + sAPCost += CalcAPsToAutofire( pSoldier->CalcActionPoints( ), AttackingWeapon, pSoldier->bDoAutofire ); else - sAPCost += CalcAPsToBurst( pSoldier->CalcActionPoints( ), &(pSoldier->inv[HANDPOS]) ); + sAPCost += CalcAPsToBurst( pSoldier->CalcActionPoints( ), AttackingWeapon ); } //else //ddd comment for aimed burst { @@ -1513,7 +1517,7 @@ INT16 CalcTotalAPsToAttack( SOLDIERTYPE *pSoldier, INT32 sGridNo, UINT8 ubAddTur UINT16 usWeaponReadyTime; UINT8 ubReadyTimeDivisor; - usWeaponReadyTime = Weapon[ usItemNum ].ubReadyTime * (100 - GetPercentReadyTimeAPReduction(&pSoldier->inv[HANDPOS])) / 100; + usWeaponReadyTime = Weapon[ pSoldier->inv[HANDPOS].usItem ].ubReadyTime * (100 - GetPercentReadyTimeAPReduction(&pSoldier->inv[HANDPOS])) / 100; ubReadyTimeDivisor = gGameExternalOptions.ubFirstAimReadyCostDivisor; sAPCost += usWeaponReadyTime / ubReadyTimeDivisor; } @@ -1696,8 +1700,10 @@ INT16 MinAPsToAttack(SOLDIERTYPE *pSoldier, INT32 sGridno, UINT8 ubAddTurningCos } else { + UINT16 undbarItem = pSoldier->GetUsedWeaponNumber( &(pSoldier->inv[ HANDPOS ]) ); + // LOOK IN BUDDY'S HAND TO DETERMINE WHAT TO DO HERE - uiItemClass = Item[ pSoldier->inv[HANDPOS].usItem ].usItemClass; + uiItemClass = Item[ undbarItem ].usItemClass; } if ( uiItemClass == IC_BLADE || uiItemClass == IC_GUN || uiItemClass == IC_LAUNCHER || uiItemClass == IC_TENTACLES || uiItemClass == IC_THROWING_KNIFE ) @@ -1936,16 +1942,21 @@ INT16 MinAPsToShootOrStab(SOLDIERTYPE *pSoldier, INT32 sGridNo, UINT8 ubAddTurni UINT16 usRaiseGunCost = 0; UINT16 usTurningCost = 0; - + UINT16 usUBItem = 0; if ( pSoldier->bWeaponMode == WM_ATTACHED_GL || pSoldier->bWeaponMode == WM_ATTACHED_GL_BURST || pSoldier->bWeaponMode == WM_ATTACHED_GL_AUTO ) { usItem = GetAttachedGrenadeLauncher(&pSoldier->inv[HANDPOS] );//UNDER_GLAUNCHER; + usUBItem = usItem; } else - { + { usItem = pSoldier->inv[ HANDPOS ].usItem; + + // Flugente: we need a secon temnr in case we are using an underbarrel weapon. Not all checks should apply for that one, as aiming is still done with the main weapon + usUBItem = pSoldier->GetUsedWeaponNumber(&pSoldier->inv[HANDPOS]); } + OBJECTTYPE* pObjUsed = pSoldier->GetUsedWeapon( &(pSoldier->inv[HANDPOS]) ); GetAPChargeForShootOrStabWRTGunRaises( pSoldier, sGridNo, ubAddTurningCost, &fAddingTurningCost, &fAddingRaiseGunCost ); @@ -2013,51 +2024,53 @@ INT16 MinAPsToShootOrStab(SOLDIERTYPE *pSoldier, INT32 sGridNo, UINT8 ubAddTurni } } else - { + { //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, &(pSoldier->inv[HANDPOS]) ) * (100 - GetBipodBonus(&(pSoldier->inv[HANDPOS]))) / 100); - } else { - bAPCost += BaseAPsToShootOrStab( bFullAPs, bAimSkill, &(pSoldier->inv[HANDPOS]) ); + 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); + } + else + { + bAPCost += BaseAPsToShootOrStab( bFullAPs, bAimSkill, pObjUsed ); } - ///////////////////////////////////////////////////////////////////////////////////// // SANDRO - STOMP traits //////////////////////////////////////////////////// if ( gGameOptions.fNewTraitSystem ) { // Decreased APs needed for LMG - Auto Weapons - if (Weapon[usItem].ubWeaponType == GUN_LMG && (pSoldier->bDoBurst || pSoldier->bDoAutofire) && ( HAS_SKILL_TRAIT( pSoldier, AUTO_WEAPONS_NT ) ) ) + if (Weapon[usUBItem].ubWeaponType == GUN_LMG && (pSoldier->bDoBurst || pSoldier->bDoAutofire) && ( HAS_SKILL_TRAIT( pSoldier, AUTO_WEAPONS_NT ) ) ) { bAPCost = (INT16)((bAPCost * (100 - gSkillTraitValues.ubAWFiringSpeedBonusLMGs ) / 100)+ 0.5); } // Decreased APs needed for melee attacks - Melee - if ( Item[ usItem ].usItemClass == IC_BLADE && ( HAS_SKILL_TRAIT( pSoldier, MELEE_NT ) ) ) + if ( Item[ usUBItem ].usItemClass == IC_BLADE && ( HAS_SKILL_TRAIT( pSoldier, MELEE_NT ) ) ) { bAPCost = (INT16)((bAPCost * (100 - gSkillTraitValues.ubMEBladesAPsReduction ) / 100)+ 0.5); } // Decreased APs needed for throwing knives - Throwing - else if ( Item[ usItem ].usItemClass == IC_THROWING_KNIFE && ( HAS_SKILL_TRAIT( pSoldier, THROWING_NT ) ) ) + else if ( Item[ usUBItem ].usItemClass == IC_THROWING_KNIFE && ( HAS_SKILL_TRAIT( pSoldier, THROWING_NT ) ) ) { bAPCost = (INT16)((bAPCost * (100 - gSkillTraitValues.ubTHBladesAPsReduction ) / 100)+ 0.5); } // Decreased APs needed for launchers - Heavy Eeapons - else if ( (Item[ usItem ].usItemClass == IC_LAUNCHER || Item[usItem].grenadelauncher ) && !(Item[usItem].rocketlauncher) && !(Item[usItem].mortar) && ( HAS_SKILL_TRAIT( pSoldier, HEAVY_WEAPONS_NT ) )) + else if ( (Item[ usUBItem ].usItemClass == IC_LAUNCHER || Item[usUBItem].grenadelauncher ) && !(Item[usUBItem].rocketlauncher) && !(Item[usUBItem].mortar) && ( HAS_SKILL_TRAIT( pSoldier, HEAVY_WEAPONS_NT ) )) { bAPCost = (INT16)((bAPCost * (100 - gSkillTraitValues.ubHWGrenadeLaunchersAPsReduction * NUM_SKILL_TRAITS( pSoldier, HEAVY_WEAPONS_NT ) ) / 100)+ 0.5); } // Decreased APs needed for launchers - Heavy Eeapons - else if (( Item[usItem].rocketlauncher || Item[usItem].singleshotrocketlauncher ) && !(Item[usItem].mortar) && ( HAS_SKILL_TRAIT( pSoldier, HEAVY_WEAPONS_NT ) )) + else if (( Item[usUBItem].rocketlauncher || Item[usUBItem].singleshotrocketlauncher ) && !(Item[usUBItem].mortar) && ( HAS_SKILL_TRAIT( pSoldier, HEAVY_WEAPONS_NT ) )) { bAPCost = (INT16)((bAPCost * (100 - gSkillTraitValues.ubHWRocketLaunchersAPsReduction * NUM_SKILL_TRAITS( pSoldier, HEAVY_WEAPONS_NT ) ) / 100)+ 0.5); } // Decreased APs needed for mortar - Heavy Eeapons - else if ( Item[usItem].mortar && HAS_SKILL_TRAIT( pSoldier, HEAVY_WEAPONS_NT ) ) + else if ( Item[usUBItem].mortar && HAS_SKILL_TRAIT( pSoldier, HEAVY_WEAPONS_NT ) ) { bAPCost = (INT16)((bAPCost * (100 - gSkillTraitValues.ubHWMortarAPsReduction * NUM_SKILL_TRAITS( pSoldier, HEAVY_WEAPONS_NT ) ) / 100)+ 0.5); } // Decreased APs needed for pistols and machine pistols - Gunslinger - else if (Weapon[ usItem ].ubWeaponType == GUN_PISTOL && HAS_SKILL_TRAIT( pSoldier, GUNSLINGER_NT ) ) + else if (Weapon[ usUBItem ].ubWeaponType == GUN_PISTOL && HAS_SKILL_TRAIT( pSoldier, GUNSLINGER_NT ) ) { bAPCost = (INT16)((bAPCost * (100 - gSkillTraitValues.ubGSFiringSpeedBonusPistols * NUM_SKILL_TRAITS( pSoldier, GUNSLINGER_NT ) ) / 100)+ 0.5); } @@ -2073,7 +2086,7 @@ INT16 MinAPsToShootOrStab(SOLDIERTYPE *pSoldier, INT32 sGridNo, UINT8 ubAddTurni fAddingRaiseGunCost = TRUE; } - if ( Item[ usItem ].usItemClass == IC_THROWING_KNIFE ) + if ( Item[ usUBItem ].usItemClass == IC_THROWING_KNIFE ) { // Do we need to stand up? bAPCost += GetAPsToChangeStance( pSoldier, ANIM_STAND ); @@ -2120,7 +2133,7 @@ INT16 MinAPsToShootOrStab(SOLDIERTYPE *pSoldier, INT32 sGridNo, UINT8 ubAddTurni // if attacking a new target (or if the specific target is uncertain) // Added check if the weapon is a throwing knife - otherwise it would add APs for change taregt on cursor but not actually deduct them afterwards - SANDRO - if (ubForceRaiseGunCost || (( sGridNo != pSoldier->sLastTarget ) && !Item[usItem].rocketlauncher && (Item[ usItem ].usItemClass != IC_THROWING_KNIFE) )) + if (ubForceRaiseGunCost || (( sGridNo != pSoldier->sLastTarget ) && !Item[usUBItem].rocketlauncher && (Item[ usUBItem ].usItemClass != IC_THROWING_KNIFE) )) { bAPCost += APBPConstants[AP_CHANGE_TARGET]; } @@ -2134,7 +2147,7 @@ INT16 MinAPsToShootOrStab(SOLDIERTYPE *pSoldier, INT32 sGridNo, UINT8 ubAddTurni if ( bAPCost < 1 ) bAPCost = 1; - if ( Item[pSoldier->inv[HANDPOS].usItem].rocketlauncher ) + if ( Item[(*pObjUsed).usItem].rocketlauncher ) { bAPCost += GetAPsToChangeStance( pSoldier, ANIM_STAND ); } @@ -2347,33 +2360,36 @@ BOOLEAN EnoughAmmo( SOLDIERTYPE *pSoldier, BOOLEAN fDisplay, INT8 bInvPos ) } else { - if ( Item[pSoldier->inv[ bInvPos ].usItem].singleshotrocketlauncher ) + OBJECTTYPE* pObjUsed = pSoldier->GetUsedWeapon( &(pSoldier->inv[bInvPos]) ); + UINT16 usItemUsed = pSoldier->GetUsedWeaponNumber( &(pSoldier->inv[bInvPos]) ); + + if ( Item[usItemUsed].singleshotrocketlauncher ) { // hack... they turn empty afterwards anyways return( TRUE ); } - if (Item[ pSoldier->inv[ bInvPos ].usItem ].usItemClass == IC_LAUNCHER || Item[pSoldier->inv[ bInvPos ].usItem].cannon ) + if (Item[ usItemUsed ].usItemClass == IC_LAUNCHER || Item[usItemUsed].cannon ) { - if ( FindAttachmentByClass( &(pSoldier->inv[ bInvPos ]), IC_GRENADE ) != 0 ) + if ( FindAttachmentByClass( pObjUsed, IC_GRENADE ) != 0 ) { return( TRUE ); } - if (Item[pSoldier->inv[bInvPos].usItem].usItemClass == IC_BOMB) + if (Item[usItemUsed].usItemClass == IC_BOMB) { return (TRUE); } // ATE: Did an else if here... - if ( FindAttachmentByClass( &(pSoldier->inv[ bInvPos ]), IC_BOMB ) != 0 ) + if ( FindAttachmentByClass( pObjUsed, IC_BOMB ) != 0 ) { return( TRUE ); } // WANNE: If there is a tank, it always have ammo to shoot, no check! - if (Item[pSoldier->inv[bInvPos].usItem].cannon) + if (Item[usItemUsed].cannon) { return ( TRUE ); } @@ -2385,9 +2401,9 @@ BOOLEAN EnoughAmmo( SOLDIERTYPE *pSoldier, BOOLEAN fDisplay, INT8 bInvPos ) return( FALSE ); } - else if (Item[ pSoldier->inv[ bInvPos ].usItem ].usItemClass == IC_GUN) + else if (Item[ usItemUsed ].usItemClass == IC_GUN) { - if ( pSoldier->inv[ bInvPos ][0]->data.gun.ubGunShotsLeft == 0 ) + if ( (*pObjUsed)[0]->data.gun.ubGunShotsLeft == 0 ) { if ( fDisplay ) { @@ -2399,7 +2415,7 @@ BOOLEAN EnoughAmmo( SOLDIERTYPE *pSoldier, BOOLEAN fDisplay, INT8 bInvPos ) // manual recharge if( pSoldier->bTeam == OUR_TEAM ) { - if ( !( pSoldier->inv[ bInvPos ][0]->data.gun.ubGunState & GS_CARTRIDGE_IN_CHAMBER ) ) + if ( !( (*pObjUsed)[0]->data.gun.ubGunState & GS_CARTRIDGE_IN_CHAMBER ) ) { return( FALSE ); } @@ -2435,24 +2451,14 @@ void DeductAmmo( SOLDIERTYPE *pSoldier, INT8 bInvPos ) } else if ( Item[ pObj->usItem ].usItemClass == IC_GUN && !Item[pObj->usItem].cannon && pSoldier->bWeaponMode != WM_ATTACHED_GL && pSoldier->bWeaponMode != WM_ATTACHED_GL_BURST && pSoldier->bWeaponMode != WM_ATTACHED_GL_AUTO ) { - if ( pSoldier->usAttackingWeapon == pObj->usItem) + // Flugente: check for underbarrel weapons and use that object if necessary + OBJECTTYPE* pObjUsed = pSoldier->GetUsedWeapon( &(pSoldier->inv[bInvPos]) ); + + // OK, let's see, don't overrun... + if ( (*pObjUsed)[0]->data.gun.ubGunShotsLeft != 0 ) { - // OK, let's see, don't overrun... - if ( (*pObj)[0]->data.gun.ubGunShotsLeft != 0 ) - { - (*pObj)[0]->data.gun.ubGunShotsLeft--; - //Pulmu: Update weight after firing gun to account for bullets fired - if( gGameExternalOptions.fAmmoDynamicWeight == TRUE) - { - //ADB ubWeight has been removed, see comments in OBJECTTYPE - //pSoldier->inv[HANDPOS].ubWeight = CalculateObjectWeight( &(pSoldier->inv[HANDPOS])); - } - } - } - else - { - // firing an attachment? - } + (*pObjUsed)[0]->data.gun.ubGunShotsLeft--; + } } else if ( Item[ pObj->usItem ].usItemClass == IC_LAUNCHER || Item[pObj->usItem].cannon || pSoldier->bWeaponMode == WM_ATTACHED_GL || pSoldier->bWeaponMode == WM_ATTACHED_GL_BURST || pSoldier->bWeaponMode == WM_ATTACHED_GL_AUTO ) { @@ -2645,7 +2651,9 @@ INT16 GetAPsToAutoReload( SOLDIERTYPE * pSoldier ) INT16 bAPCost = 0, bAPCost2 = 0;; CHECKF( pSoldier ); - pObj = &(pSoldier->inv[HANDPOS]); + + // Flugente: check for underbarrel weapons and use that object if necessary + pObj = pSoldier->GetUsedWeapon( &(pSoldier->inv[HANDPOS]) ); // manual recharge if ((*pObj)[0]->data.gun.ubGunShotsLeft && !((*pObj)[0]->data.gun.ubGunState & GS_CARTRIDGE_IN_CHAMBER) ) @@ -2682,7 +2690,9 @@ INT16 GetAPsToAutoReload( SOLDIERTYPE * pSoldier ) if ( pSoldier->IsValidSecondHandShotForReloadingPurposes( ) ) { - pObj = &(pSoldier->inv[SECONDHANDPOS]); + // Flugente: check for underbarrel weapons and use that object if necessary + pObj = pSoldier->GetUsedWeapon( &(pSoldier->inv[SECONDHANDPOS]) ); + bExcludeSlot = NO_SLOT; bSlot2 = NO_SLOT; diff --git a/Tactical/Soldier Ani.cpp b/Tactical/Soldier Ani.cpp index ff0570ff..bb1f5eb6 100644 --- a/Tactical/Soldier Ani.cpp +++ b/Tactical/Soldier Ani.cpp @@ -358,67 +358,77 @@ BOOLEAN AdjustToNextAnimationFrame( SOLDIERTYPE *pSoldier ) break; case 430: - - DebugMsg(TOPIC_JA2,DBG_LEVEL_3,"AdjustToNextAnimationFrame: case 430"); - // SHOOT GUN - // MAKE AN EVENT, BUT ONLY DO STUFF IF WE OWN THE GUY! - SFireWeapon.usSoldierID = pSoldier->ubID; - SFireWeapon.uiUniqueId = pSoldier->uiUniqueSoldierIdValue; - SFireWeapon.sTargetGridNo = pSoldier->sTargetGridNo; - SFireWeapon.bTargetLevel = pSoldier->bTargetLevel; - SFireWeapon.bTargetCubeLevel= pSoldier->bTargetCubeLevel; - if((is_server && pSoldier->ubID<120) || (!is_server && is_client && pSoldier->ubID<20) || (!is_server && !is_client) ) { - //only carry on if own werc - AddGameEvent( S_FIREWEAPON, 0, &SFireWeapon ); + DebugMsg(TOPIC_JA2,DBG_LEVEL_3,"AdjustToNextAnimationFrame: case 430"); + // SHOOT GUN + // MAKE AN EVENT, BUT ONLY DO STUFF IF WE OWN THE GUY! + SFireWeapon.usSoldierID = pSoldier->ubID; + SFireWeapon.uiUniqueId = pSoldier->uiUniqueSoldierIdValue; + SFireWeapon.sTargetGridNo = pSoldier->sTargetGridNo; + SFireWeapon.bTargetLevel = pSoldier->bTargetLevel; + SFireWeapon.bTargetCubeLevel= pSoldier->bTargetCubeLevel; + if((is_server && pSoldier->ubID<120) || (!is_server && is_client && pSoldier->ubID<20) || (!is_server && !is_client) ) + { + //only carry on if own werc + AddGameEvent( S_FIREWEAPON, 0, &SFireWeapon ); - //hayden - if(is_server || (is_client && pSoldier->ubID <20) ) - send_fireweapon( &SFireWeapon ); - } - //DIGICRAB: Burst UnCap - //Loop around in the animation if we still have burst rounds to fire - if (pSoldier->bDoBurst && (pSoldier->bDoBurst <= ((pSoldier->bDoAutofire)?(pSoldier->bDoAutofire):(GetShotsPerBurst(&pSoldier->inv[HANDPOS]))) || (( pSoldier->bWeaponMode == WM_ATTACHED_GL_BURST && pSoldier->bDoBurst <= Weapon[GetAttachedGrenadeLauncher(&pSoldier->inv[HANDPOS])].ubShotsPerBurst)) )) - { - if(pSoldier->usAnimState == 44 || pSoldier->usAnimState == 109 || pSoldier->usAnimState == 108 && pSoldier->usAniCode == 33) //we are standing, crounching or prone, firing the fast shot - pSoldier->usAniCode = 3; - else if(pSoldier->usAnimState == 175 && pSoldier->usAniCode == 37) //we are firing down to something very close, last shot - pSoldier->usAniCode = 14; - } - - //DIGICRAB: Burst Sound - //This code is stolen from Tactical\Weapons.c - UseGun(...) - if (pSoldier->bDoBurst && pSoldier->iBurstSoundID == NO_SAMPLE && Weapon[ pSoldier->usAttackingWeapon ].sSound != 0 && Item[ pSoldier->usAttackingWeapon ].usItemClass != IC_THROWING_KNIFE ) - { - // Switch on silencer... - INT16 noisefactor = GetPercentNoiseVolume( &pSoldier->inv[ pSoldier->ubAttackingHand ] ); - if( noisefactor < MAX_PERCENT_NOISE_VOLUME_FOR_SILENCED_SOUND || Weapon[ pSoldier->usAttackingWeapon ].ubAttackVolume <= 10 ) - { - INT32 uiSound; - - uiSound = Weapon [ pSoldier->usAttackingWeapon ].silencedSound; - //if ( Weapon[ pSoldier->usAttackingWeapon ].ubCalibre == AMMO9 || Weapon[ pSoldier->usAttackingWeapon ].ubCalibre == AMMO38 || Weapon[ pSoldier->usAttackingWeapon ].ubCalibre == AMMO57 ) - //{ - // uiSound = S_SILENCER_1; - //} - //else - //{ - // uiSound = S_SILENCER_2; - //} - - //randomize the rate a bit so that the sound is more believable - PlayJA2Sample( uiSound, 44100-Random(5000)-Random(5000)-Random(5000), SoundVolume( HIGHVOLUME, pSoldier->sGridNo ), 1, SoundDir( pSoldier->sGridNo ) ); - + //hayden + if(is_server || (is_client && pSoldier->ubID <20) ) + send_fireweapon( &SFireWeapon ); } - else + + OBJECTTYPE* pObjHand = pSoldier->GetUsedWeapon( &pSoldier->inv[HANDPOS] ); + + //DIGICRAB: Burst UnCap + //Loop around in the animation if we still have burst rounds to fire + if (pSoldier->bDoBurst + && ( + pSoldier->bDoBurst <= ( (pSoldier->bDoAutofire)?(pSoldier->bDoAutofire):(GetShotsPerBurst( pObjHand )) ) + || (( pSoldier->bWeaponMode == WM_ATTACHED_GL_BURST && pSoldier->bDoBurst <= Weapon[GetAttachedGrenadeLauncher(&pSoldier->inv[HANDPOS])].ubShotsPerBurst)) + ) + ) { - INT8 volume = HIGHVOLUME; - if ( noisefactor < 100 ) volume = (volume * noisefactor) / 100; - //randomize the rate a bit so that the sound is more believable - PlayJA2Sample( Weapon[ pSoldier->usAttackingWeapon ].sSound, 44100-Random(5000)-Random(5000)-Random(5000), SoundVolume( volume, pSoldier->sGridNo ), 1, SoundDir( pSoldier->sGridNo ) ); + if(pSoldier->usAnimState == 44 || pSoldier->usAnimState == 109 || pSoldier->usAnimState == 108 && pSoldier->usAniCode == 33) //we are standing, crounching or prone, firing the fast shot + pSoldier->usAniCode = 3; + else if(pSoldier->usAnimState == 175 && pSoldier->usAniCode == 37) //we are firing down to something very close, last shot + pSoldier->usAniCode = 14; + } + + OBJECTTYPE* pObjUsed = pSoldier->GetUsedWeapon( &pSoldier->inv[ pSoldier->ubAttackingHand ] ); + UINT16 usedGun = pSoldier->GetUsedWeaponNumber( &pSoldier->inv[ pSoldier->ubAttackingHand ] ); + //DIGICRAB: Burst Sound + //This code is stolen from Tactical\Weapons.c - UseGun(...) + if (pSoldier->bDoBurst && pSoldier->iBurstSoundID == NO_SAMPLE && Weapon[ usedGun ].sSound != 0 && Item[ usedGun ].usItemClass != IC_THROWING_KNIFE ) + { + // Switch on silencer... + INT16 noisefactor = GetPercentNoiseVolume( pObjUsed ); + if( noisefactor < MAX_PERCENT_NOISE_VOLUME_FOR_SILENCED_SOUND || Weapon[ usedGun ].ubAttackVolume <= 10 ) + { + INT32 uiSound; + + uiSound = Weapon [ usedGun ].silencedSound; + //if ( Weapon[ pSoldier->usAttackingWeapon ].ubCalibre == AMMO9 || Weapon[ pSoldier->usAttackingWeapon ].ubCalibre == AMMO38 || Weapon[ pSoldier->usAttackingWeapon ].ubCalibre == AMMO57 ) + //{ + // uiSound = S_SILENCER_1; + //} + //else + //{ + // uiSound = S_SILENCER_2; + //} + + //randomize the rate a bit so that the sound is more believable + PlayJA2Sample( uiSound, 44100-Random(5000)-Random(5000)-Random(5000), SoundVolume( HIGHVOLUME, pSoldier->sGridNo ), 1, SoundDir( pSoldier->sGridNo ) ); + + } + else + { + INT8 volume = HIGHVOLUME; + if ( noisefactor < 100 ) volume = (volume * noisefactor) / 100; + //randomize the rate a bit so that the sound is more believable + PlayJA2Sample( Weapon[ usedGun ].sSound, 44100-Random(5000)-Random(5000)-Random(5000), SoundVolume( volume, pSoldier->sGridNo ), 1, SoundDir( pSoldier->sGridNo ) ); + } } } - break; case 431: @@ -764,133 +774,137 @@ BOOLEAN AdjustToNextAnimationFrame( SOLDIERTYPE *pSoldier ) case 448: - - // CODE: HANDLE BURST - // FIRST CHECK IF WE'VE REACHED MAX FOR GUN - fStop = FALSE; - - if ( ( pSoldier->bWeaponMode == WM_ATTACHED_GL_BURST && pSoldier->bDoBurst > Weapon[GetAttachedGrenadeLauncher(&pSoldier->inv[HANDPOS])].ubShotsPerBurst) || (pSoldier->bWeaponMode != WM_ATTACHED_GL_BURST && pSoldier->bDoBurst > ((pSoldier->bDoAutofire)?(pSoldier->bDoAutofire):(GetShotsPerBurst(&pSoldier->inv[HANDPOS])))) ) { - DebugMsg(TOPIC_JA2,DBG_LEVEL_3,"AdjustToNextAnimationFrame: Burst case 448, stopping because burst size too large"); - fStop = TRUE; - fFreeUpAttacker = TRUE; - } + // CODE: HANDLE BURST + // FIRST CHECK IF WE'VE REACHED MAX FOR GUN + fStop = FALSE; - // CHECK IF WE HAVE AMMO LEFT, IF NOT, END ANIMATION! - if ( !EnoughAmmo( pSoldier, FALSE, pSoldier->ubAttackingHand ) ) - { - DebugMsg(TOPIC_JA2,DBG_LEVEL_3,"AdjustToNextAnimationFrame: Burst case 448, stopping because not enough ammo"); - fStop = TRUE; - fFreeUpAttacker = TRUE; - if ( pSoldier->bTeam == gbPlayerNum ) + OBJECTTYPE* pObjHand = pSoldier->GetUsedWeapon( &pSoldier->inv[HANDPOS] ); + + if ( ( pSoldier->bWeaponMode == WM_ATTACHED_GL_BURST && pSoldier->bDoBurst > Weapon[GetAttachedGrenadeLauncher(&pSoldier->inv[HANDPOS])].ubShotsPerBurst) + || (pSoldier->bWeaponMode != WM_ATTACHED_GL_BURST && pSoldier->bDoBurst > ((pSoldier->bDoAutofire)?(pSoldier->bDoAutofire):(GetShotsPerBurst( pObjHand )))) ) { - ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, TacticalStr[ BURST_FIRE_DEPLETED_CLIP_STR ] ); - } - } - // Why only check for a jam on the first bullet? - else if (pSoldier->bDoBurst == 1) - { - // CHECK FOR GUN JAM - bWeaponJammed = CheckForGunJam( pSoldier ); - if ( bWeaponJammed == TRUE ) - { - DebugMsg(TOPIC_JA2,DBG_LEVEL_3,"AdjustToNextAnimationFrame: Burst case 448, stopping because weapon jammed"); + DebugMsg(TOPIC_JA2,DBG_LEVEL_3,"AdjustToNextAnimationFrame: Burst case 448, stopping because burst size too large"); fStop = TRUE; fFreeUpAttacker = TRUE; - // stop shooting! - // Yeah, but what does this have to do with that? - // pSoldier->bBulletsLeft = 0; - - // OK, Stop burst sound... - if ( pSoldier->iBurstSoundID != NO_SAMPLE ) - { - SoundStop( pSoldier->iBurstSoundID ); - } + } + // CHECK IF WE HAVE AMMO LEFT, IF NOT, END ANIMATION! + if ( !EnoughAmmo( pSoldier, FALSE, pSoldier->ubAttackingHand ) ) + { + DebugMsg(TOPIC_JA2,DBG_LEVEL_3,"AdjustToNextAnimationFrame: Burst case 448, stopping because not enough ammo"); + fStop = TRUE; + fFreeUpAttacker = TRUE; if ( pSoldier->bTeam == gbPlayerNum ) { - PlayJA2Sample( S_DRYFIRE1, RATE_11025, SoundVolume( MIDVOLUME, pSoldier->sGridNo ), 1, SoundDir( pSoldier->sGridNo ) ); - //ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"Gun jammed!" ); + ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, TacticalStr[ BURST_FIRE_DEPLETED_CLIP_STR ] ); + } + } + // Why only check for a jam on the first bullet? + else if (pSoldier->bDoBurst == 1) + { + // CHECK FOR GUN JAM + bWeaponJammed = CheckForGunJam( pSoldier ); + if ( bWeaponJammed == TRUE ) + { + DebugMsg(TOPIC_JA2,DBG_LEVEL_3,"AdjustToNextAnimationFrame: Burst case 448, stopping because weapon jammed"); + fStop = TRUE; + fFreeUpAttacker = TRUE; + // stop shooting! + // Yeah, but what does this have to do with that? + // pSoldier->bBulletsLeft = 0; + + // OK, Stop burst sound... + if ( pSoldier->iBurstSoundID != NO_SAMPLE ) + { + SoundStop( pSoldier->iBurstSoundID ); + } + + if ( pSoldier->bTeam == gbPlayerNum ) + { + PlayJA2Sample( S_DRYFIRE1, RATE_11025, SoundVolume( MIDVOLUME, pSoldier->sGridNo ), 1, SoundDir( pSoldier->sGridNo ) ); + //ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"Gun jammed!" ); + } + + DebugMsg( TOPIC_JA2, DBG_LEVEL_3, String("@@@@@@@ Freeing up attacker - aborting start of attack due to burst gun jam") ); + FreeUpAttacker( ); + } + else if ( bWeaponJammed == 255 ) + { + // Play intermediate animation... + if ( HandleUnjamAnimation( pSoldier ) ) + { + return( TRUE ); + } + } + } + + if ( fStop ) + { + if(pSoldier->bDoAutofire) //reset the autofire cursor after firing + { + pSoldier->flags.autofireLastStep = FALSE; + pSoldier->bDoAutofire = 1; } - DebugMsg( TOPIC_JA2, DBG_LEVEL_3, String("@@@@@@@ Freeing up attacker - aborting start of attack due to burst gun jam") ); - FreeUpAttacker( ); - } - else if ( bWeaponJammed == 255 ) - { - // Play intermediate animation... - if ( HandleUnjamAnimation( pSoldier ) ) + pSoldier->flags.fDoSpread = FALSE; + pSoldier->bDoBurst = 1; + // pSoldier->flags.fBurstCompleted = TRUE; + if ( fFreeUpAttacker ) + { + // DebugMsg( TOPIC_JA2, DBG_LEVEL_3, String("@@@@@@@ Freeing up attacker - aborting start of attack") ); + // FreeUpAttacker( pSoldier->ubID ); + } + + // ATE; Reduce it due to animation being stopped... + // 0verhaul: No longer necessary or desired + // DebugMsg( TOPIC_JA2, DBG_LEVEL_3, String("@@@@@@@ Freeing up attacker - Burst animation ended") ); + // ReduceAttackBusyCount( pSoldier->ubID, FALSE ); + + + if ( CheckForImproperFireGunEnd( pSoldier ) ) { return( TRUE ); } - } - } - if ( fStop ) - { - if(pSoldier->bDoAutofire) //reset the autofire cursor after firing - { - pSoldier->flags.autofireLastStep = FALSE; - pSoldier->bDoAutofire = 1; - } + // END: GOTO AIM STANCE BASED ON HEIGHT + // If we are a robot - we need to do stuff different here + // 0verhaul: Ya know, if the robot simply used the same animation for standing and rifle standing, + // we probably wouldn't need this special case code. + if ( AM_A_ROBOT( pSoldier ) ) + { + pSoldier->ChangeSoldierState( STANDING, 0 , FALSE ); + } + else + { + switch ( gAnimControl[ pSoldier->usAnimState ].ubEndHeight ) + { + case ANIM_STAND: + pSoldier->ChangeSoldierState( AIM_RIFLE_STAND, 0 , FALSE ); + break; - pSoldier->flags.fDoSpread = FALSE; - pSoldier->bDoBurst = 1; - // pSoldier->flags.fBurstCompleted = TRUE; - if ( fFreeUpAttacker ) - { - // DebugMsg( TOPIC_JA2, DBG_LEVEL_3, String("@@@@@@@ Freeing up attacker - aborting start of attack") ); - // FreeUpAttacker( pSoldier->ubID ); - } + case ANIM_PRONE: + pSoldier->ChangeSoldierState( AIM_RIFLE_PRONE, 0 , FALSE ); + break; - // ATE; Reduce it due to animation being stopped... - // 0verhaul: No longer necessary or desired - // DebugMsg( TOPIC_JA2, DBG_LEVEL_3, String("@@@@@@@ Freeing up attacker - Burst animation ended") ); - // ReduceAttackBusyCount( pSoldier->ubID, FALSE ); + case ANIM_CROUCH: + pSoldier->ChangeSoldierState( AIM_RIFLE_CROUCH, 0 , FALSE ); + break; - - if ( CheckForImproperFireGunEnd( pSoldier ) ) - { + } + } return( TRUE ); } - // END: GOTO AIM STANCE BASED ON HEIGHT - // If we are a robot - we need to do stuff different here - // 0verhaul: Ya know, if the robot simply used the same animation for standing and rifle standing, - // we probably wouldn't need this special case code. - if ( AM_A_ROBOT( pSoldier ) ) + // MOVETO CURRENT SPREAD LOCATION + if ( pSoldier->flags.fDoSpread ) { - pSoldier->ChangeSoldierState( STANDING, 0 , FALSE ); - } - else - { - switch ( gAnimControl[ pSoldier->usAnimState ].ubEndHeight ) + if ( pSoldier->sSpreadLocations[ pSoldier->flags.fDoSpread - 1 ] != 0 ) { - case ANIM_STAND: - pSoldier->ChangeSoldierState( AIM_RIFLE_STAND, 0 , FALSE ); - break; - - case ANIM_PRONE: - pSoldier->ChangeSoldierState( AIM_RIFLE_PRONE, 0 , FALSE ); - break; - - case ANIM_CROUCH: - pSoldier->ChangeSoldierState( AIM_RIFLE_CROUCH, 0 , FALSE ); - break; - + pSoldier->EVENT_SetSoldierDirection( (INT8)GetDirectionToGridNoFromGridNo( pSoldier->sGridNo, pSoldier->sSpreadLocations[ pSoldier->flags.fDoSpread - 1 ] ) ); + pSoldier->EVENT_SetSoldierDesiredDirection( pSoldier->ubDirection ); } } - return( TRUE ); - } - - // MOVETO CURRENT SPREAD LOCATION - if ( pSoldier->flags.fDoSpread ) - { - if ( pSoldier->sSpreadLocations[ pSoldier->flags.fDoSpread - 1 ] != 0 ) - { - pSoldier->EVENT_SetSoldierDirection( (INT8)GetDirectionToGridNoFromGridNo( pSoldier->sGridNo, pSoldier->sSpreadLocations[ pSoldier->flags.fDoSpread - 1 ] ) ); - pSoldier->EVENT_SetSoldierDesiredDirection( pSoldier->ubDirection ); - } } break; @@ -2381,9 +2395,12 @@ BOOLEAN AdjustToNextAnimationFrame( SOLDIERTYPE *pSoldier ) usItem = pSoldier->inv[ HANDPOS ].usItem; - if ( pSoldier->inv[ HANDPOS ].exists() == true ) + OBJECTTYPE* pObjUsed = pSoldier->GetUsedWeapon( &pSoldier->inv[ HANDPOS ] ); + UINT16 usItemUsemUsed = pSoldier->GetUsedWeaponNumber( &pSoldier->inv[ HANDPOS ] ); + + if ( pObjUsed->exists() == true ) { - usSoundID = Weapon[ usItem ].sLocknLoadSound; + usSoundID = Weapon[ usItemUsemUsed ].sLocknLoadSound; if ( usSoundID != 0 ) { @@ -3977,8 +3994,10 @@ BOOLEAN CheckForImproperFireGunEnd( SOLDIERTYPE *pSoldier ) return( FALSE ); } + OBJECTTYPE* pObjHand = pSoldier->GetUsedWeapon( &pSoldier->inv[ HANDPOS ] ); + // Check single hand for jammed status, ( or ammo is out.. ) - if ( pSoldier->inv[ HANDPOS ][0]->data.gun.bGunAmmoStatus < 0 || pSoldier->inv[ HANDPOS ][0]->data.gun.ubGunShotsLeft == 0 ) + if ( (*pObjHand)[0]->data.gun.bGunAmmoStatus < 0 || (*pObjHand)[0]->data.gun.ubGunShotsLeft == 0 ) { // If we have 2 pistols, donot go back! if ( Item[ pSoldier->inv[ SECONDHANDPOS ].usItem ].usItemClass != IC_GUN ) diff --git a/Tactical/Soldier Control.cpp b/Tactical/Soldier Control.cpp index 2794f462..2ed8c46d 100644 --- a/Tactical/Soldier Control.cpp +++ b/Tactical/Soldier Control.cpp @@ -12911,7 +12911,7 @@ void SOLDIERTYPE::SoldierInventoryCoolDown(void) attachmentList::iterator iterend = (*pObj)[i]->attachments.end(); for (attachmentList::iterator iter = (*pObj)[i]->attachments.begin(); iter != iterend; ++iter) { - if ( iter->exists() && Item[ iter->usItem ].usItemClass & (IC_LAUNCHER|IC_LAUNCHER) ) + if ( iter->exists() && Item[ iter->usItem ].usItemClass & (IC_GUN|IC_LAUNCHER) ) { FLOAT temperature = (*iter)[i]->data.bTemperature; // ... get temperature of item ... @@ -12934,11 +12934,19 @@ void SOLDIERTYPE::SoldierInventoryCoolDown(void) } } -// Flugente: determien if we can rest our weapon on something. This can only happen when STANDING/CROUCHED. As a result, we get superior handling modifiers (we apply the PRONE modfiers) +// Flugente: determine if we can rest our weapon on something. This can only happen when STANDING/CROUCHED. As a result, we get superior handling modifiers (we apply the PRONE modfiers) BOOLEAN SOLDIERTYPE::IsWeaponMounted( void ) { BOOLEAN applybipod = FALSE; + // we must be active + if ( !bActive) + return( FALSE ); + + // we must be in a sector (not travelling) + if ( !bInSector ) + return( FALSE ); + // not possible if already prone if ( gAnimControl[ this->usAnimState ].ubEndHeight == ANIM_PRONE ) return( FALSE ); @@ -13022,6 +13030,33 @@ BOOLEAN SOLDIERTYPE::IsWeaponMounted( void ) return( applybipod ); } +// Flugente: return weapon currently used +OBJECTTYPE* SOLDIERTYPE::GetUsedWeapon( OBJECTTYPE * pObj ) +{ + if ( bWeaponMode == WM_ATTACHED_UB || bWeaponMode == WM_ATTACHED_UB_BURST || bWeaponMode == WM_ATTACHED_UB_AUTO ) + { + OBJECTTYPE* pObjUnderBarrel = FindAttachment_UnderBarrel(pObj); + + if ( pObjUnderBarrel ) + return( pObjUnderBarrel ); + } + + return( pObj ); +} + +UINT16 SOLDIERTYPE::GetUsedWeaponNumber( OBJECTTYPE * pObj ) +{ + if ( bWeaponMode == WM_ATTACHED_UB || bWeaponMode == WM_ATTACHED_UB_BURST || bWeaponMode == WM_ATTACHED_UB_AUTO ) + { + UINT16 weaponnr = GetAttachedUnderBarrel(pObj); + + if ( weaponnr != NONE ) + return( weaponnr ); + } + + return( pObj->usItem ); +} + INT32 CheckBleeding( SOLDIERTYPE *pSoldier ) { INT8 bBandaged; //,savedOurTurn; diff --git a/Tactical/Soldier Control.h b/Tactical/Soldier Control.h index 8851cee6..1d4804ef 100644 --- a/Tactical/Soldier Control.h +++ b/Tactical/Soldier Control.h @@ -1319,7 +1319,9 @@ public: BOOLEAN IsValidSecondHandShotForReloadingPurposes( void ); BOOLEAN SoldierCarriesTwoHandedWeapon( void ); void SoldierInventoryCoolDown( void ); // Flugente FTW 1: Cool down all items in inventory - BOOLEAN IsWeaponMounted( void ); + BOOLEAN IsWeaponMounted( void ); // determine if we receive a bonus for mouning 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 }; // SOLDIERTYPE; diff --git a/Tactical/Tactical Turns.cpp b/Tactical/Tactical Turns.cpp index c9edd937..7dd1be47 100644 --- a/Tactical/Tactical Turns.cpp +++ b/Tactical/Tactical Turns.cpp @@ -272,7 +272,7 @@ void HandleTacticalEndTurn( ) #endif // Flugente FTW 1: Cool down all items not in a soldier's inventory - CoolDownWorldItems(); + CoolDownWorldItems( FALSE ); } diff --git a/Tactical/Turn Based Input.cpp b/Tactical/Turn Based Input.cpp index dc1e9513..e9c35d95 100644 --- a/Tactical/Turn Based Input.cpp +++ b/Tactical/Turn Based Input.cpp @@ -3959,6 +3959,44 @@ void GetKeyboardInput( UINT32 *puiNewEvent ) } } } + if (IsUnderBarrelAttached(pGun)) + { + OBJECTTYPE *pGun2 = FindAttachment_UnderBarrel(pGun); + if ( (*pGun2)[0]->data.gun.ubGunShotsLeft < GetMagSize( pGun2 ) ) + { + + // Search for ammo in sector + for ( UINT32 uiLoop = 0; uiLoop < guiNumWorldItems; uiLoop++ ) + { + if ( (gWorldItems[ uiLoop ].bVisible == TRUE) && (gWorldItems[ uiLoop ].fExists) && (gWorldItems[ uiLoop ].usFlags & WORLD_ITEM_REACHABLE) && !(gWorldItems[ uiLoop ].usFlags & WORLD_ITEM_ARMED_BOMB) )//item exists, is reachable, is visible and is not trapped + { + if ( ( Item[ gWorldItems[ uiLoop ].object.usItem ].usItemClass & IC_AMMO ) ) // the item is ammo + { + pAmmo = &( gWorldItems[ uiLoop ].object ); + + if ( CompatibleAmmoForGun( pAmmo, pGun2 ) ) // can use the ammo with this gun + { + // same ammo type in gun and magazine + if ( Magazine[Item[(*pGun2)[0]->data.gun.usGunAmmoItem].ubClassIndex].ubAmmoType == Magazine[Item[pAmmo->usItem].ubClassIndex].ubAmmoType ) + { + ReloadGun( pTeamSoldier, pGun2, pAmmo ); + } + + if ((*pAmmo)[0]->data.ubShotsLeft == 0) + { + RemoveItemFromPool( gWorldItems[ uiLoop ].sGridNo, uiLoop, gWorldItems[ uiLoop ].ubLevel ); + } + } + } + } + } + } + //CHRISL: if not enough ammo in sector, reload using ammo carried in inventory + if ( (*pGun2)[0]->data.gun.ubGunShotsLeft < GetMagSize( pGun2 ) ) + { + AutoReload( pTeamSoldier ); + } + } } } } @@ -3978,7 +4016,8 @@ void GetKeyboardInput( UINT32 *puiNewEvent ) { if ( ( gTacticalStatus.uiFlags & INCOMBAT ) ) { - pGun = &(pTeamSoldier->inv[HANDPOS]); + // Flugente: check for underbarrel weapons and use that object if necessary + pGun = pTeamSoldier->GetUsedWeapon( &(pTeamSoldier->inv[HANDPOS]) ); //magazine is not full if ( (*pGun)[0]->data.gun.ubGunShotsLeft < GetMagSize( pGun ) ) diff --git a/Tactical/UI Cursors.cpp b/Tactical/UI Cursors.cpp index 8f61c090..3136dcac 100644 --- a/Tactical/UI Cursors.cpp +++ b/Tactical/UI Cursors.cpp @@ -2574,8 +2574,7 @@ UINT8 GetActionModeCursor( SOLDIERTYPE *pSoldier ) return ( TRAJECTORYCURS ); } - usInHand = pSoldier->inv[HANDPOS].usItem; - + usInHand = pSoldier->GetUsedWeaponNumber( &pSoldier->inv[HANDPOS] ); // Start off with what is in our hand ubCursor = Item[ usInHand ].ubCursor; diff --git a/Tactical/Weapons.cpp b/Tactical/Weapons.cpp index 6d85613a..4e416dd0 100644 --- a/Tactical/Weapons.cpp +++ b/Tactical/Weapons.cpp @@ -1160,7 +1160,8 @@ BOOLEAN CheckForGunJam( SOLDIERTYPE * pSoldier ) { if ( Item[pSoldier->usAttackingWeapon].usItemClass == IC_GUN && !EXPLOSIVE_GUN( pSoldier->usAttackingWeapon ) && !(pSoldier->bWeaponMode == WM_ATTACHED_GL || pSoldier->bWeaponMode == WM_ATTACHED_GL_BURST || pSoldier->bWeaponMode == WM_ATTACHED_GL_AUTO) ) { - pObj = &(pSoldier->inv[pSoldier->ubAttackingHand]); + pObj = pSoldier->GetUsedWeapon(&pSoldier->inv[pSoldier->ubAttackingHand]); + if ((*pObj)[0]->data.gun.bGunAmmoStatus > 0) { // Algorithm for jamming @@ -1610,8 +1611,13 @@ BOOLEAN UseGunNCTH( SOLDIERTYPE *pSoldier , INT32 sTargetGridNo ) usItemNum = pSoldier->usAttackingWeapon; + // Flugente: check for underbarrel weapons and use that object if necessary + OBJECTTYPE* pObjHand = pSoldier->GetUsedWeapon( &(pSoldier->inv[HANDPOS]) ); + OBJECTTYPE* pObjAttHand = pSoldier->GetUsedWeapon( &pSoldier->inv[ pSoldier->ubAttackingHand ] ); + UINT16 usUBItem = pSoldier->GetUsedWeaponNumber( &pSoldier->inv[ pSoldier->ubAttackingHand ] ); + // CALC MUZZLE SWAY - if ( Item[ usItemNum ].usItemClass == IC_THROWING_KNIFE ) + if ( Item[ usUBItem ].usItemClass == IC_THROWING_KNIFE ) { uiMuzzleSway = 100 - CalcThrownChanceToHit( pSoldier, sTargetGridNo, pSoldier->aiData.bAimTime, pSoldier->bAimShotLocation ); } @@ -1638,18 +1644,18 @@ BOOLEAN UseGunNCTH( SOLDIERTYPE *pSoldier , INT32 sTargetGridNo ) // Only deduct points once for Burst and Autofire (on firing the first bullet). if ( pSoldier->bDoBurst == 1 ) { - INT8 bShotsToFire = pSoldier->bDoAutofire ? - pSoldier->bDoAutofire : - GetShotsPerBurst(&pSoldier->inv[HANDPOS]); - - if ( Weapon[ usItemNum ].sBurstSound != NO_WEAPON_SOUND ) + INT8 bShotsToFire = pSoldier->bDoAutofire ? pSoldier->bDoAutofire : GetShotsPerBurst(pObjHand); + + if ( Weapon[ usUBItem ].sBurstSound != NO_WEAPON_SOUND ) { + UINT16 noisefactor; // IF we are silenced? - UINT16 noisefactor = GetPercentNoiseVolume( &pSoldier->inv[ pSoldier->ubAttackingHand ] ); - if( noisefactor < MAX_PERCENT_NOISE_VOLUME_FOR_SILENCED_SOUND || Weapon[ usItemNum ].ubAttackVolume <= 10 ) + noisefactor = GetPercentNoiseVolume( pObjAttHand ); + + if( noisefactor < MAX_PERCENT_NOISE_VOLUME_FOR_SILENCED_SOUND || Weapon[ usUBItem ].ubAttackVolume <= 10 ) { // Pick sound file baed on how many bullets we are going to fire... - sprintf( zBurstString, gzBurstSndStrings[ Weapon[ usItemNum ].sSilencedBurstSound ], bShotsToFire ); + sprintf( zBurstString, gzBurstSndStrings[ Weapon[ usUBItem ].sSilencedBurstSound ], bShotsToFire ); // Try playing sound... pSoldier->iBurstSoundID = PlayJA2SampleFromFile( zBurstString, RATE_11025, SoundVolume( HIGHVOLUME, pSoldier->sGridNo ), 1, SoundDir( pSoldier->sGridNo ) ); @@ -1658,7 +1664,7 @@ BOOLEAN UseGunNCTH( SOLDIERTYPE *pSoldier , INT32 sTargetGridNo ) { // Pick sound file baed on how many bullets we are going to fire... // Lesh: changed next line - sprintf( zBurstString, gzBurstSndStrings[ Weapon[ usItemNum ].sBurstSound ], bShotsToFire ); + sprintf( zBurstString, gzBurstSndStrings[ Weapon[ usUBItem ].sBurstSound ], bShotsToFire ); INT8 volume = HIGHVOLUME; if ( noisefactor < 100 ) volume = (INT8) ((volume * noisefactor) / 100); @@ -1684,7 +1690,7 @@ BOOLEAN UseGunNCTH( SOLDIERTYPE *pSoldier , INT32 sTargetGridNo ) else { // ONLY DEDUCT FOR THE FIRST HAND when doing two-pistol attacks - if ( pSoldier->IsValidSecondHandShot( ) && pSoldier->inv[ HANDPOS ][0]->data.gun.bGunStatus >= USABLE && pSoldier->inv[HANDPOS][0]->data.gun.bGunAmmoStatus > 0 ) + if ( pSoldier->IsValidSecondHandShot( ) && (*pObjHand)[0]->data.gun.bGunStatus >= USABLE && (*pObjHand)[0]->data.gun.bGunAmmoStatus > 0 ) { // only deduct APs when the main gun fires if ( pSoldier->ubAttackingHand == HANDPOS ) @@ -1699,15 +1705,15 @@ BOOLEAN UseGunNCTH( SOLDIERTYPE *pSoldier , INT32 sTargetGridNo ) //PLAY SOUND // ( For throwing knife.. it's earlier in the animation - if ( Weapon[ usItemNum ].sSound != NO_WEAPON_SOUND && Item[ usItemNum ].usItemClass != IC_THROWING_KNIFE ) + if ( Weapon[ usUBItem ].sSound != NO_WEAPON_SOUND && Item[ usUBItem ].usItemClass != IC_THROWING_KNIFE ) { // Switch on silencer... - UINT16 noisefactor = GetPercentNoiseVolume( &pSoldier->inv[ pSoldier->ubAttackingHand ] ); - if( noisefactor < MAX_PERCENT_NOISE_VOLUME_FOR_SILENCED_SOUND || Weapon[ usItemNum ].ubAttackVolume <= 10 ) + UINT16 noisefactor = GetPercentNoiseVolume( pObjAttHand ); + if( noisefactor < MAX_PERCENT_NOISE_VOLUME_FOR_SILENCED_SOUND || Weapon[ usUBItem ].ubAttackVolume <= 10 ) { INT32 uiSound; - uiSound = Weapon [ usItemNum ].silencedSound ; + uiSound = Weapon [ usUBItem ].silencedSound ; //if ( Weapon[ usItemNum ].ubCalibre == AMMO9 || Weapon[ usItemNum ].ubCalibre == AMMO38 || Weapon[ usItemNum ].ubCalibre == AMMO57 ) //{ // uiSound = S_SILENCER_1; @@ -1724,28 +1730,27 @@ BOOLEAN UseGunNCTH( SOLDIERTYPE *pSoldier , INT32 sTargetGridNo ) { INT8 volume = HIGHVOLUME; if ( noisefactor < 100 ) volume = (volume * noisefactor) / 100; - PlayJA2Sample( Weapon[ usItemNum ].sSound, RATE_11025, SoundVolume( volume, pSoldier->sGridNo ), 1, SoundDir( pSoldier->sGridNo ) ); + PlayJA2Sample( Weapon[ usUBItem ].sSound, RATE_11025, SoundVolume( volume, pSoldier->sGridNo ), 1, SoundDir( pSoldier->sGridNo ) ); } } } //ddd{ . silencer - if ( (Item[ usItemNum ].usItemClass == IC_GUN) && gGameExternalOptions.bAllowWearSuppressor) + if ( (Item[ usUBItem ].usItemClass == IC_GUN) && gGameExternalOptions.bAllowWearSuppressor ) { - OBJECTTYPE * pInHand = &(pSoldier->inv[pSoldier->ubAttackingHand]); - if ( IsFlashSuppressor(&pSoldier->inv[ pSoldier->ubAttackingHand ], pSoldier ) ) + if ( IsFlashSuppressor(pObjAttHand, pSoldier ) ) { - for (attachmentList::iterator iter = (*pInHand)[0]->attachments.begin(); iter != (*pInHand)[0]->attachments.end(); ++iter) + for (attachmentList::iterator iter = (*pObjAttHand)[0]->attachments.begin(); iter != (*pObjAttHand)[0]->attachments.end(); ++iter) { if (Item[iter->usItem].hidemuzzleflash ) { OBJECTTYPE* pA= &(*iter); if ( (*pA)[0]->data.objectStatus >=USABLE) { - INT8 bAmmoReliability = Item[(*pInHand)[0]->data.gun.usGunAmmoItem].bReliability; + INT8 bAmmoReliability = Item[(*pObjAttHand)[0]->data.gun.usGunAmmoItem].bReliability; uiDepreciateTest = max( BASIC_DEPRECIATE_CHANCE + 3 * (Item[ iter->usItem ].bReliability + bAmmoReliability), 0); - if ( !PreRandom( uiDepreciateTest ) && ( pSoldier->inv[ pSoldier->ubAttackingHand ][0]->data.objectStatus > 1) ) + if ( !PreRandom( uiDepreciateTest ) && ( (*pObjAttHand)[0]->data.objectStatus > 1) ) (*pA)[0]->data.objectStatus--; //ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"rel =%d ",Item[ iter->usItem ].bReliability ); } @@ -1757,12 +1762,10 @@ BOOLEAN UseGunNCTH( SOLDIERTYPE *pSoldier , INT32 sTargetGridNo ) //DIGICRAB: Barrel extender wear code // Relocated from CalcChanceToHitGun - if ( Item[ usItemNum ].usItemClass == IC_GUN ) + if ( Item[ usUBItem ].usItemClass == IC_GUN ) { - OBJECTTYPE * pInHand = &(pSoldier->inv[pSoldier->ubAttackingHand]); - // lalien: search for barrel extender not for any item with range bonus. (else barrel extender will fall off even when none is attached) - OBJECTTYPE* pAttachment = FindAttachment( pInHand, GUN_BARREL_EXTENDER ); + OBJECTTYPE* pAttachment = FindAttachment( pObjHand, GUN_BARREL_EXTENDER ); if ( pAttachment ) { @@ -1784,13 +1787,13 @@ BOOLEAN UseGunNCTH( SOLDIERTYPE *pSoldier , INT32 sTargetGridNo ) //CHRISL: Instead of the above, use this function which is basially redundant to what remove() does, but includes // a failsafe so we don't cause an item duplication. - for(std::list::iterator iter = (*pInHand)[0]->attachments.begin(); - iter != (*pInHand)[0]->attachments.end(); ++iter){ + for(std::list::iterator iter = (*pObjHand)[0]->attachments.begin(); + iter != (*pObjHand)[0]->attachments.end(); ++iter){ if(*iter == *pAttachment) { AddItemToPool( pSoldier->sGridNo, pAttachment, 1, pSoldier->pathing.bLevel, 0, -1 ); - iter = (*pInHand)[0]->RemoveAttachmentAtIter(iter); + iter = (*pObjHand)[0]->RemoveAttachmentAtIter(iter); break; } @@ -1819,14 +1822,14 @@ BOOLEAN UseGunNCTH( SOLDIERTYPE *pSoldier , INT32 sTargetGridNo ) } // Some things we don't do for knives... - if ( Item[ usItemNum ].usItemClass != IC_THROWING_KNIFE ) + if ( Item[ usUBItem ].usItemClass != IC_THROWING_KNIFE ) { // If realtime - set counter to freeup from attacking once done if ( ( ( gTacticalStatus.uiFlags & REALTIME ) || !( gTacticalStatus.uiFlags & INCOMBAT ) ) ) { // Set delay based on stats, weapon type, etc - pSoldier->sReloadDelay = (INT16)( Weapon[ usItemNum ].usReloadDelay + MANDATORY_WEAPON_DELAY ); + pSoldier->sReloadDelay = (INT16)( Weapon[ usUBItem ].usReloadDelay + MANDATORY_WEAPON_DELAY ); // If a bad guy, double the delay! if ( (pSoldier->flags.uiStatusFlags & SOLDIER_ENEMY ) ) @@ -1849,40 +1852,13 @@ BOOLEAN UseGunNCTH( SOLDIERTYPE *pSoldier , INT32 sTargetGridNo ) DeductAmmo( pSoldier, pSoldier->ubAttackingHand ); // ATE: Check if we should say quote... - if ( pSoldier->inv[ pSoldier->ubAttackingHand ][0]->data.gun.ubGunShotsLeft == 0 && !Item[pSoldier->usAttackingWeapon].rocketlauncher ) + if ( (*pObjHand)[0]->data.gun.ubGunShotsLeft == 0 && !Item[usUBItem].rocketlauncher ) { if ( pSoldier->bTeam == gbPlayerNum ) { pSoldier->flags.fSayAmmoQuotePending = TRUE; } } - - // set buckshot and muzzle flash - fBuckshot = FALSE; - if (!CREATURE_OR_BLOODCAT( pSoldier ) ) - { - if ( IsFlashSuppressor(&pSoldier->inv[ pSoldier->ubAttackingHand ], pSoldier ) ) - pSoldier->flags.fMuzzleFlash = FALSE; - else - pSoldier->flags.fMuzzleFlash = TRUE; - - DebugMsg(TOPIC_JA2,DBG_LEVEL_3,String("UseGun: Muzzle flash = %d",pSoldier->flags.fMuzzleFlash)); - - if ( AmmoTypes[pSoldier->inv[ pSoldier->ubAttackingHand ][0]->data.gun.ubGunAmmoType].numberOfBullets > 1 ) - fBuckshot = TRUE; - - //switch ( pSoldier->inv[ pSoldier->ubAttackingHand ][0]->data.gun.ubGunAmmoType ) - //{ - // case AMMO_BUCKSHOT: - // fBuckshot = TRUE; - // break; - // case AMMO_SLEEP_DART: - // pSoldier->flags.fMuzzleFlash = FALSE; - // break; - // default: - // break; - //} - } } else // throwing knife { @@ -1892,21 +1868,21 @@ BOOLEAN UseGunNCTH( SOLDIERTYPE *pSoldier , INT32 sTargetGridNo ) // Deduct knife from inv! (not here, later?) } - if ( Item[usItemNum].rocketlauncher ) + if ( Item[usUBItem].rocketlauncher ) { - if ( WillExplosiveWeaponFail( pSoldier, &( pSoldier->inv[ HANDPOS ] ) ) ) + if ( WillExplosiveWeaponFail( pSoldier, pObjHand ) ) { - if ( Item[usItemNum].singleshotrocketlauncher ) + if ( Item[usUBItem].singleshotrocketlauncher ) { - CreateItem( Item[usItemNum].discardedlauncheritem , pSoldier->inv[ HANDPOS ][0]->data.objectStatus,&(pSoldier->inv[ HANDPOS ] ) ); + CreateItem( Item[usItemNum].discardedlauncheritem , (*pObjHand)[0]->data.objectStatus, pObjHand ); DirtyMercPanelInterface( pSoldier, DIRTYLEVEL2 ); IgniteExplosion( pSoldier->ubID, CenterX( pSoldier->sGridNo ), CenterY( pSoldier->sGridNo ), 0, pSoldier->sGridNo, C1, pSoldier->pathing.bLevel ); } else { - DebugMsg( TOPIC_JA2, DBG_LEVEL_3, String("StructureHit: RPG7 item: %d, Ammo: %d",pSoldier->inv[HANDPOS].usItem , pSoldier->inv[HANDPOS][0]->data.gun.usGunAmmoItem ) ); + DebugMsg( TOPIC_JA2, DBG_LEVEL_3, String("StructureHit: RPG7 item: %d, Ammo: %d", usUBItem , (*pObjHand)[0]->data.gun.usGunAmmoItem ) ); - IgniteExplosion( pSoldier->ubID, CenterX( pSoldier->sGridNo ), CenterY( pSoldier->sGridNo ), 0, pSoldier->sGridNo, pSoldier->inv[pSoldier->ubAttackingHand ][0]->data.gun.usGunAmmoItem, pSoldier->pathing.bLevel ); + IgniteExplosion( pSoldier->ubID, CenterX( pSoldier->sGridNo ), CenterY( pSoldier->sGridNo ), 0, pSoldier->sGridNo, (*pObjHand)[0]->data.gun.usGunAmmoItem, pSoldier->pathing.bLevel ); pSoldier->inv[pSoldier->ubAttackingHand ][0]->data.gun.usGunAmmoItem = NONE; } // Reduce again for attack end 'cause it has been incremented for a normal attack @@ -1942,7 +1918,7 @@ BOOLEAN UseGunNCTH( SOLDIERTYPE *pSoldier , INT32 sTargetGridNo ) INT16 sApertureRatio = 0; if ( !AreInMeanwhile() ) { - AdjustTargetCenterPoint( pSoldier, sTargetGridNo, &dTargetX, &dTargetY, &dTargetZ, &pSoldier->inv[ pSoldier->ubAttackingHand ], uiMuzzleSway, &sApertureRatio ); + AdjustTargetCenterPoint( pSoldier, sTargetGridNo, &dTargetX, &dTargetY, &dTargetZ, pObjAttHand, uiMuzzleSway, &sApertureRatio ); } ////////////////////////////////////////////////////////////////////////////////////////////// @@ -1954,7 +1930,7 @@ BOOLEAN UseGunNCTH( SOLDIERTYPE *pSoldier , INT32 sTargetGridNo ) // All points given here are FAILURE type, and cannot cause level gain. // In addition, once the target is HIT, the shooter receives an additional number of points. - if ( Item[ usItemNum ].usItemClass != IC_THROWING_KNIFE ) + if ( Item[ usUBItem ].usItemClass != IC_THROWING_KNIFE ) { // NB bDoBurst will be 2 at this point for the first shot since it was incremented // above @@ -1966,7 +1942,7 @@ BOOLEAN UseGunNCTH( SOLDIERTYPE *pSoldier , INT32 sTargetGridNo ) // add base pts for taking a shot, whether it hits or misses dExpGain = 2.0f; - if ( pSoldier->IsValidSecondHandShot( ) && pSoldier->inv[ HANDPOS ][0]->data.gun.bGunStatus >= USABLE && pSoldier->inv[HANDPOS][0]->data.gun.bGunAmmoStatus > 0 ) + if ( pSoldier->IsValidSecondHandShot( ) && (*pObjHand)[0]->data.gun.bGunStatus >= USABLE && (*pObjHand)[0]->data.gun.bGunAmmoStatus > 0 ) { // reduce exp gain for two pistol shooting since both shots give xp dExpGain = (dExpGain * 2) / 3; @@ -2043,15 +2019,15 @@ BOOLEAN UseGunNCTH( SOLDIERTYPE *pSoldier , INT32 sTargetGridNo ) //hayden if((is_server && pSoldier->ubID<120) || (!is_server && is_client && pSoldier->ubID<20) || (!is_server && !is_client) ) { - FireBulletGivenTarget( pSoldier, dTargetX, dTargetY, dTargetZ, pSoldier->usAttackingWeapon, sApertureRatio, fBuckshot, FALSE ); + FireBulletGivenTarget( pSoldier, dTargetX, dTargetY, dTargetZ, usUBItem, sApertureRatio, fBuckshot, FALSE ); } else { - FireBulletGivenTarget( pSoldier, dTargetX, dTargetY, dTargetZ, pSoldier->usAttackingWeapon, sApertureRatio, fBuckshot, TRUE ); + FireBulletGivenTarget( pSoldier, dTargetX, dTargetY, dTargetZ, usUBItem, sApertureRatio, fBuckshot, TRUE ); } //bottom one is fake (ie not in my control) - ubVolume = Weapon[ pSoldier->usAttackingWeapon ].ubAttackVolume; + ubVolume = Weapon[ usUBItem ].ubAttackVolume; if ( Item[ usItemNum ].usItemClass == IC_THROWING_KNIFE ) { @@ -2059,11 +2035,11 @@ BOOLEAN UseGunNCTH( SOLDIERTYPE *pSoldier , INT32 sTargetGridNo ) pSoldier->inv[ HANDPOS ].RemoveObjectsFromStack(1); DirtyMercPanelInterface( pSoldier, DIRTYLEVEL2 ); } - else if ( Item[usItemNum].rocketlauncher ) + else if ( Item[usUBItem].rocketlauncher ) { - if ( Item[usItemNum].singleshotrocketlauncher ) + if ( Item[usUBItem].singleshotrocketlauncher ) { - CreateItem( Item[usItemNum].discardedlauncheritem, pSoldier->inv[ HANDPOS ][0]->data.objectStatus, &(pSoldier->inv[ HANDPOS ] ) ); + CreateItem( Item[usUBItem].discardedlauncheritem, (*pObjHand)[0]->data.objectStatus, pObjHand ); DirtyMercPanelInterface( pSoldier, DIRTYLEVEL2 ); } @@ -2090,14 +2066,14 @@ BOOLEAN UseGunNCTH( SOLDIERTYPE *pSoldier , INT32 sTargetGridNo ) else { // Snap: get cumulative noise reduction from the weapon and its attachments - UINT16 noisefactor = GetPercentNoiseVolume( &pSoldier->inv[ pSoldier->ubAttackingHand ] ); + UINT16 noisefactor = GetPercentNoiseVolume( pObjAttHand ); if ( ubVolume * noisefactor > 25000 ) { // Snap: hack this to prevent overflow (damn miserly programmers!) ubVolume = 250; } else { - ubVolume = __max( 1, ( ubVolume * GetPercentNoiseVolume( &pSoldier->inv[ pSoldier->ubAttackingHand ] ) ) / 100 ); + ubVolume = __max( 1, ( ubVolume * GetPercentNoiseVolume( pObjAttHand ) ) / 100 ); } } @@ -2113,14 +2089,12 @@ BOOLEAN UseGunNCTH( SOLDIERTYPE *pSoldier , INT32 sTargetGridNo ) // Flugente FTW 1: Increase Weapon Temperature if ( gGameOptions.fWeaponOverheating ) { - FLOAT overheatjampercentage = GetGunOverheatDamagePercentage( &(pSoldier->inv[ pSoldier->ubAttackingHand ]) ); // ... how much above the gun's usOverheatingDamageThreshold are we? ... + FLOAT overheatjampercentage = GetGunOverheatDamagePercentage( pObjAttHand ); // ... how much above the gun's usOverheatingDamageThreshold are we? ... if ( overheatjampercentage > 1.0 ) - { iOverheatReliabilityMalus = (INT16)floor(overheatjampercentage*overheatjampercentage); - } - GunIncreaseHeat( &(pSoldier->inv[ pSoldier->ubAttackingHand ]) ); + GunIncreaseHeat( pObjAttHand ); } // CJC: since jamming is no longer affected by reliability, increase chance of status going down for really unreliabile guns @@ -2133,11 +2107,11 @@ BOOLEAN UseGunNCTH( SOLDIERTYPE *pSoldier , INT32 sTargetGridNo ) //} // Flugente FTW 1: Added a malus to reliability for overheated guns - uiDepreciateTest = max( BASIC_DEPRECIATE_CHANCE + 3 * GetReliability( &(pSoldier->inv[pSoldier->ubAttackingHand]) ) - iOverheatReliabilityMalus, 0); + uiDepreciateTest = max( BASIC_DEPRECIATE_CHANCE + 3 * GetReliability( pObjAttHand ) - iOverheatReliabilityMalus, 0); - if ( !PreRandom( uiDepreciateTest ) && ( pSoldier->inv[ pSoldier->ubAttackingHand ][0]->data.objectStatus > 1) ) + if ( !PreRandom( uiDepreciateTest ) && ( (*pObjAttHand)[0]->data.objectStatus > 1) ) { - pSoldier->inv[ pSoldier->ubAttackingHand ][0]->data.objectStatus--; + (*pObjAttHand)[0]->data.objectStatus--; } // reduce monster smell (gunpowder smell) @@ -2147,8 +2121,8 @@ BOOLEAN UseGunNCTH( SOLDIERTYPE *pSoldier , INT32 sTargetGridNo ) } // manual recharge - if (Weapon[Item[usItemNum].ubClassIndex].APsToReloadManually > 0) - pSoldier->inv[ pSoldier->ubAttackingHand ][0]->data.gun.ubGunState &= ~GS_CARTRIDGE_IN_CHAMBER; + if (Weapon[Item[usUBItem].ubClassIndex].APsToReloadManually > 0) + (*pObjAttHand)[0]->data.gun.ubGunState &= ~GS_CARTRIDGE_IN_CHAMBER; // DebugMsg(TOPIC_JA2,DBG_LEVEL_3,String("UseGun: done")); return( TRUE ); @@ -2180,6 +2154,10 @@ BOOLEAN UseGun( SOLDIERTYPE *pSoldier , INT32 sTargetGridNo ) // Deduct points! sAPCost = CalcTotalAPsToAttack( pSoldier, sTargetGridNo, FALSE, pSoldier->aiData.bAimTime ); + // Flugente: check for underbarrel weapons and use that object if necessary + OBJECTTYPE* pObjUsed = pSoldier->GetUsedWeapon( &pSoldier->inv[pSoldier->ubAttackingHand] ); + UINT16 usUBItem = pSoldier->GetUsedWeaponNumber( &pSoldier->inv[pSoldier->ubAttackingHand] ); + usItemNum = pSoldier->usAttackingWeapon; if ( pSoldier->bDoBurst ) @@ -2187,18 +2165,17 @@ BOOLEAN UseGun( SOLDIERTYPE *pSoldier , INT32 sTargetGridNo ) // ONly deduct points once if ( pSoldier->bDoBurst == 1 ) { - INT8 bShotsToFire = pSoldier->bDoAutofire ? - pSoldier->bDoAutofire : - GetShotsPerBurst(&pSoldier->inv[HANDPOS]); - - if ( Weapon[ usItemNum ].sBurstSound != NO_WEAPON_SOUND ) + INT8 bShotsToFire = pSoldier->bDoAutofire ? pSoldier->bDoAutofire : GetShotsPerBurst(pObjUsed); + + if ( Weapon[ usUBItem ].sBurstSound != NO_WEAPON_SOUND ) { // IF we are silenced? - UINT16 noisefactor = GetPercentNoiseVolume( &pSoldier->inv[ pSoldier->ubAttackingHand ] ); - if( noisefactor < MAX_PERCENT_NOISE_VOLUME_FOR_SILENCED_SOUND || Weapon[ usItemNum ].ubAttackVolume <= 10 ) + UINT16 noisefactor = GetPercentNoiseVolume( pObjUsed ); + + if( noisefactor < MAX_PERCENT_NOISE_VOLUME_FOR_SILENCED_SOUND || Weapon[ usUBItem ].ubAttackVolume <= 10 ) { // Pick sound file baed on how many bullets we are going to fire... - sprintf( zBurstString, gzBurstSndStrings[ Weapon[ usItemNum ].sSilencedBurstSound ], bShotsToFire ); + sprintf( zBurstString, gzBurstSndStrings[ Weapon[ usUBItem ].sSilencedBurstSound ], bShotsToFire ); // Try playing sound... pSoldier->iBurstSoundID = PlayJA2SampleFromFile( zBurstString, RATE_11025, SoundVolume( HIGHVOLUME, pSoldier->sGridNo ), 1, SoundDir( pSoldier->sGridNo ) ); @@ -2207,7 +2184,7 @@ BOOLEAN UseGun( SOLDIERTYPE *pSoldier , INT32 sTargetGridNo ) { // Pick sound file baed on how many bullets we are going to fire... // Lesh: changed next line - sprintf( zBurstString, gzBurstSndStrings[ Weapon[ usItemNum ].sBurstSound ], bShotsToFire ); + sprintf( zBurstString, gzBurstSndStrings[ Weapon[ usUBItem ].sBurstSound ], bShotsToFire ); INT8 volume = HIGHVOLUME; if ( noisefactor < 100 ) volume = (INT8) ((volume * noisefactor) / 100); @@ -2233,7 +2210,7 @@ BOOLEAN UseGun( SOLDIERTYPE *pSoldier , INT32 sTargetGridNo ) else { // ONLY DEDUCT FOR THE FIRST HAND when doing two-pistol attacks - if ( pSoldier->IsValidSecondHandShot( ) && pSoldier->inv[ HANDPOS ][0]->data.gun.bGunStatus >= USABLE && pSoldier->inv[HANDPOS][0]->data.gun.bGunAmmoStatus > 0 ) + if ( pSoldier->IsValidSecondHandShot( ) && (*pObjUsed)[0]->data.gun.bGunStatus >= USABLE && (*pObjUsed)[0]->data.gun.bGunAmmoStatus > 0 ) { // only deduct APs when the main gun fires if ( pSoldier->ubAttackingHand == HANDPOS ) @@ -2248,15 +2225,15 @@ BOOLEAN UseGun( SOLDIERTYPE *pSoldier , INT32 sTargetGridNo ) //PLAY SOUND // ( For throwing knife.. it's earlier in the animation - if ( Weapon[ usItemNum ].sSound != NO_WEAPON_SOUND && Item[ usItemNum ].usItemClass != IC_THROWING_KNIFE ) + if ( Weapon[ usUBItem ].sSound != NO_WEAPON_SOUND && Item[ usUBItem ].usItemClass != IC_THROWING_KNIFE ) { // Switch on silencer... - UINT16 noisefactor = GetPercentNoiseVolume( &pSoldier->inv[ pSoldier->ubAttackingHand ] ); - if( noisefactor < MAX_PERCENT_NOISE_VOLUME_FOR_SILENCED_SOUND || Weapon[ usItemNum ].ubAttackVolume <= 10 ) + UINT16 noisefactor = GetPercentNoiseVolume( pObjUsed ); + if( noisefactor < MAX_PERCENT_NOISE_VOLUME_FOR_SILENCED_SOUND || Weapon[ usUBItem ].ubAttackVolume <= 10 ) { INT32 uiSound; - uiSound = Weapon [ usItemNum ].silencedSound ; + uiSound = Weapon [ usUBItem ].silencedSound ; //if ( Weapon[ usItemNum ].ubCalibre == AMMO9 || Weapon[ usItemNum ].ubCalibre == AMMO38 || Weapon[ usItemNum ].ubCalibre == AMMO57 ) //{ // uiSound = S_SILENCER_1; @@ -2273,14 +2250,14 @@ BOOLEAN UseGun( SOLDIERTYPE *pSoldier , INT32 sTargetGridNo ) { INT8 volume = HIGHVOLUME; if ( noisefactor < 100 ) volume = (volume * noisefactor) / 100; - PlayJA2Sample( Weapon[ usItemNum ].sSound, RATE_11025, SoundVolume( volume, pSoldier->sGridNo ), 1, SoundDir( pSoldier->sGridNo ) ); + PlayJA2Sample( Weapon[ usUBItem ].sSound, RATE_11025, SoundVolume( volume, pSoldier->sGridNo ), 1, SoundDir( pSoldier->sGridNo ) ); } } } // CALC CHANCE TO HIT - if ( Item[ usItemNum ].usItemClass == IC_THROWING_KNIFE ) + if ( Item[ usUBItem ].usItemClass == IC_THROWING_KNIFE ) { uiHitChance = CalcThrownChanceToHit( pSoldier, sTargetGridNo, pSoldier->aiData.bAimTime, pSoldier->bAimShotLocation ); } @@ -2296,22 +2273,21 @@ BOOLEAN UseGun( SOLDIERTYPE *pSoldier , INT32 sTargetGridNo ) fCalculateCTHDuringGunfire = FALSE; //ddd{ . silencer - if ( (Item[ usItemNum ].usItemClass == IC_GUN) && gGameExternalOptions.bAllowWearSuppressor) + if ( (Item[ usUBItem ].usItemClass == IC_GUN) && gGameExternalOptions.bAllowWearSuppressor ) { - OBJECTTYPE * pInHand = &(pSoldier->inv[pSoldier->ubAttackingHand]); - if ( IsFlashSuppressor(&pSoldier->inv[ pSoldier->ubAttackingHand ], pSoldier ) ) + if ( IsFlashSuppressor(pObjUsed, pSoldier ) ) { - for (attachmentList::iterator iter = (*pInHand)[0]->attachments.begin(); iter != (*pInHand)[0]->attachments.end(); ++iter) + for (attachmentList::iterator iter = (*pObjUsed)[0]->attachments.begin(); iter != (*pObjUsed)[0]->attachments.end(); ++iter) { if (Item[iter->usItem].hidemuzzleflash ) { OBJECTTYPE* pA= &(*iter); if ( (*pA)[0]->data.objectStatus >=USABLE) { - INT8 bAmmoReliability = Item[(*pInHand)[0]->data.gun.usGunAmmoItem].bReliability; + INT8 bAmmoReliability = Item[(*pObjUsed)[0]->data.gun.usGunAmmoItem].bReliability; uiDepreciateTest = max( BASIC_DEPRECIATE_CHANCE + 3 * (Item[ iter->usItem ].bReliability + bAmmoReliability), 0); - if ( !PreRandom( uiDepreciateTest ) && ( pSoldier->inv[ pSoldier->ubAttackingHand ][0]->data.objectStatus > 1) ) + if ( !PreRandom( uiDepreciateTest ) && ( (*pObjUsed)[0]->data.objectStatus > 1) ) (*pA)[0]->data.objectStatus--; //ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"rel =%d ",Item[ iter->usItem ].bReliability ); } @@ -2323,12 +2299,10 @@ BOOLEAN UseGun( SOLDIERTYPE *pSoldier , INT32 sTargetGridNo ) //DIGICRAB: Barrel extender wear code // Relocated from CalcChanceToHitGun - if ( Item[ usItemNum ].usItemClass == IC_GUN ) + if ( Item[ usUBItem ].usItemClass == IC_GUN ) { - OBJECTTYPE * pInHand = &(pSoldier->inv[pSoldier->ubAttackingHand]); - // lalien: search for barrel extender not for any item with range bonus. (else barrel extender will fall off even when none is attached) - OBJECTTYPE* pAttachment = FindAttachment( pInHand, GUN_BARREL_EXTENDER ); + OBJECTTYPE* pAttachment = FindAttachment( pObjUsed, GUN_BARREL_EXTENDER ); if ( pAttachment->exists() ) { @@ -2350,13 +2324,13 @@ BOOLEAN UseGun( SOLDIERTYPE *pSoldier , INT32 sTargetGridNo ) //CHRISL: Instead of the above, use this function which is basially redundant to what remove() does, but includes // a failsafe so we don't cause an item duplication. - for(std::list::iterator iter = (*pInHand)[0]->attachments.begin(); - iter != (*pInHand)[0]->attachments.end(); ++iter){ + for(std::list::iterator iter = (*pObjUsed)[0]->attachments.begin(); + iter != (*pObjUsed)[0]->attachments.end(); ++iter){ if(*iter == *pAttachment) { AddItemToPool( pSoldier->sGridNo, pAttachment, 1, pSoldier->pathing.bLevel, 0, -1 ); - iter = (*pInHand)[0]->RemoveAttachmentAtIter(iter); + iter = (*pObjUsed)[0]->RemoveAttachmentAtIter(iter); break; } @@ -2412,21 +2386,21 @@ BOOLEAN UseGun( SOLDIERTYPE *pSoldier , INT32 sTargetGridNo ) fGonnaHit = uiDiceRoll < uiHitChance; // GET TARGET XY VALUES - ConvertGridNoToCenterCellXY( sTargetGridNo, &sXMapPos, &sYMapPos ); + ConvertGridNoToCenterCellXY( sTargetGridNo, &sXMapPos, &sYMapPos ); // ATE; Moved a whole blotch if logic code for finding target positions to a function // so other places can use it GetTargetWorldPositions( pSoldier, sTargetGridNo, &dTargetX, &dTargetY, &dTargetZ ); // Some things we don't do for knives... - if ( Item[ usItemNum ].usItemClass != IC_THROWING_KNIFE ) + if ( Item[ usUBItem ].usItemClass != IC_THROWING_KNIFE ) { // If realtime - set counter to freeup from attacking once done if ( ( ( gTacticalStatus.uiFlags & REALTIME ) || !( gTacticalStatus.uiFlags & INCOMBAT ) ) ) { // Set delay based on stats, weapon type, etc - pSoldier->sReloadDelay = (INT16)( Weapon[ usItemNum ].usReloadDelay + MANDATORY_WEAPON_DELAY ); + pSoldier->sReloadDelay = (INT16)( Weapon[ usUBItem ].usReloadDelay + MANDATORY_WEAPON_DELAY ); // If a bad guy, double the delay! if ( (pSoldier->flags.uiStatusFlags & SOLDIER_ENEMY ) ) @@ -2448,15 +2422,14 @@ BOOLEAN UseGun( SOLDIERTYPE *pSoldier , INT32 sTargetGridNo ) // Deduct AMMO! DeductAmmo( pSoldier, pSoldier->ubAttackingHand ); - // ATE: Check if we should say quote... - if ( pSoldier->inv[ pSoldier->ubAttackingHand ][0]->data.gun.ubGunShotsLeft == 0 && !Item[pSoldier->usAttackingWeapon].rocketlauncher ) + // ATE: Check if we should say quote... + if ( (*pObjUsed)[0]->data.gun.ubGunShotsLeft == 0 && !Item[usUBItem].rocketlauncher ) { if ( pSoldier->bTeam == gbPlayerNum ) { pSoldier->flags.fSayAmmoQuotePending = TRUE; } } - // NB bDoBurst will be 2 at this point for the first shot since it was incremented // above if ( PTR_OURTEAM && pSoldier->ubTargetID != NOBODY && (!pSoldier->bDoBurst || pSoldier->bDoBurst == 2 ) && (gTacticalStatus.uiFlags & INCOMBAT ) && ( SoldierToSoldierBodyPartChanceToGetThrough( pSoldier, MercPtrs[ pSoldier->ubTargetID ], pSoldier->bAimShotLocation ) > 0 ) ) @@ -2480,7 +2453,7 @@ BOOLEAN UseGun( SOLDIERTYPE *pSoldier , INT32 sTargetGridNo ) // add base pts for taking a shot, whether it hits or misses usExpGain += 3; - if ( pSoldier->IsValidSecondHandShot( ) && pSoldier->inv[ HANDPOS ][0]->data.gun.bGunStatus >= USABLE && pSoldier->inv[HANDPOS][0]->data.gun.bGunAmmoStatus > 0 ) + if ( pSoldier->IsValidSecondHandShot( ) && pSoldier->inv[ HANDPOS ][0]->data.gun.bGunStatus >= USABLE && (*pObjUsed)[0]->data.gun.bGunAmmoStatus > 0 ) { // reduce exp gain for two pistol shooting since both shots give xp usExpGain = (usExpGain * 2) / 3; @@ -2504,14 +2477,14 @@ BOOLEAN UseGun( SOLDIERTYPE *pSoldier , INT32 sTargetGridNo ) fBuckshot = FALSE; if (!CREATURE_OR_BLOODCAT( pSoldier ) ) { - if ( IsFlashSuppressor(&pSoldier->inv[ pSoldier->ubAttackingHand ], pSoldier ) ) + if ( IsFlashSuppressor( pObjUsed, pSoldier ) ) pSoldier->flags.fMuzzleFlash = FALSE; else pSoldier->flags.fMuzzleFlash = TRUE; DebugMsg(TOPIC_JA2,DBG_LEVEL_3,String("UseGun: Muzzle flash = %d",pSoldier->flags.fMuzzleFlash)); - - if ( AmmoTypes[pSoldier->inv[ pSoldier->ubAttackingHand ][0]->data.gun.ubGunAmmoType].numberOfBullets > 1 ) + + if ( AmmoTypes[(*pObjUsed)[0]->data.gun.ubGunAmmoType].numberOfBullets > 1 ) fBuckshot = TRUE; //switch ( pSoldier->inv[ pSoldier->ubAttackingHand ][0]->data.gun.ubGunAmmoType ) @@ -2572,63 +2545,66 @@ BOOLEAN UseGun( SOLDIERTYPE *pSoldier , INT32 sTargetGridNo ) } } - if ( Item[usItemNum].rocketlauncher ) + if ( Item[usUBItem].rocketlauncher ) { - if ( WillExplosiveWeaponFail( pSoldier, &( pSoldier->inv[ HANDPOS ] ) ) ) - { - if ( Item[usItemNum].singleshotrocketlauncher ) + if ( WillExplosiveWeaponFail( pSoldier, pObjUsed ) ) { - CreateItem( Item[usItemNum].discardedlauncheritem , pSoldier->inv[ HANDPOS ][0]->data.objectStatus,&(pSoldier->inv[ HANDPOS ] ) ); - DirtyMercPanelInterface( pSoldier, DIRTYLEVEL2 ); - IgniteExplosion( pSoldier->ubID, CenterX( pSoldier->sGridNo ), CenterY( pSoldier->sGridNo ), 0, pSoldier->sGridNo, C1, pSoldier->pathing.bLevel ); - } - else - { - DebugMsg( TOPIC_JA2, DBG_LEVEL_3, String("StructureHit: RPG7 item: %d, Ammo: %d",pSoldier->inv[HANDPOS].usItem , pSoldier->inv[HANDPOS][0]->data.gun.usGunAmmoItem ) ); - - IgniteExplosion( pSoldier->ubID, CenterX( pSoldier->sGridNo ), CenterY( pSoldier->sGridNo ), 0, pSoldier->sGridNo, pSoldier->inv[pSoldier->ubAttackingHand ][0]->data.gun.usGunAmmoItem, pSoldier->pathing.bLevel ); - - OBJECTTYPE * pLaunchable = FindLaunchableAttachment( &(pSoldier->inv[pSoldier->ubAttackingHand ]), pSoldier->inv[pSoldier->ubAttackingHand ].usItem ); - if(pLaunchable){ - pSoldier->inv[pSoldier->ubAttackingHand ][0]->data.gun.usGunAmmoItem = pLaunchable->usItem; - } else { - pSoldier->inv[pSoldier->ubAttackingHand ][0]->data.gun.usGunAmmoItem = NONE; + if ( Item[usUBItem].singleshotrocketlauncher ) + { + CreateItem( Item[usUBItem].discardedlauncheritem , (*pObjUsed)[0]->data.objectStatus, pObjUsed ); + DirtyMercPanelInterface( pSoldier, DIRTYLEVEL2 ); + IgniteExplosion( pSoldier->ubID, CenterX( pSoldier->sGridNo ), CenterY( pSoldier->sGridNo ), 0, pSoldier->sGridNo, C1, pSoldier->pathing.bLevel ); } - } - // Reduce again for attack end 'cause it has been incremented for a normal attack - // - // Not anymore. Only the attack animation was increased, and it will decrease itself. - DebugMsg( TOPIC_JA2, DBG_LEVEL_3, String("@@@@@@@ Freeing up attacker - ATTACK ANIMATION %s ENDED BY BAD EXPLOSIVE CHECK, Now %d", gAnimControl[ pSoldier->usAnimState ].zAnimStr, gTacticalStatus.ubAttackBusyCount ) ); - DebugAttackBusy( String("@@@@@@@ Freeing up attacker - ATTACK ANIMATION %s ENDED BY BAD EXPLOSIVE CHECK\n", gAnimControl[ pSoldier->usAnimState ].zAnimStr ) ); -// ReduceAttackBusyCount( pSoldier->ubID, FALSE ); + else + { + DebugMsg( TOPIC_JA2, DBG_LEVEL_3, String("StructureHit: RPG7 item: %d, Ammo: %d", usUBItem , (*pObjUsed)[0]->data.gun.usGunAmmoItem ) ); - return( FALSE ); - } - } + IgniteExplosion( pSoldier->ubID, CenterX( pSoldier->sGridNo ), CenterY( pSoldier->sGridNo ), 0, pSoldier->sGridNo, (*pObjUsed)[0]->data.gun.usGunAmmoItem, pSoldier->pathing.bLevel ); + + OBJECTTYPE * pLaunchable = FindLaunchableAttachment( pObjUsed, usUBItem ); + if(pLaunchable) + { + (*pObjUsed)[0]->data.gun.usGunAmmoItem = pLaunchable->usItem; + } + else + { + (*pObjUsed)[0]->data.gun.usGunAmmoItem = NONE; + } + } + // Reduce again for attack end 'cause it has been incremented for a normal attack + // + // Not anymore. Only the attack animation was increased, and it will decrease itself. + DebugMsg( TOPIC_JA2, DBG_LEVEL_3, String("@@@@@@@ Freeing up attacker - ATTACK ANIMATION %s ENDED BY BAD EXPLOSIVE CHECK, Now %d", gAnimControl[ pSoldier->usAnimState ].zAnimStr, gTacticalStatus.ubAttackBusyCount ) ); + DebugAttackBusy( String("@@@@@@@ Freeing up attacker - ATTACK ANIMATION %s ENDED BY BAD EXPLOSIVE CHECK\n", gAnimControl[ pSoldier->usAnimState ].zAnimStr ) ); + //ReduceAttackBusyCount( pSoldier->ubID, FALSE ); + + return( FALSE ); + } + } //hayden if((is_server && pSoldier->ubID<120) || (!is_server && is_client && pSoldier->ubID<20) || (!is_server && !is_client) ) { - FireBulletGivenTarget( pSoldier, dTargetX, dTargetY, dTargetZ, pSoldier->usAttackingWeapon, (UINT16) (uiHitChance - uiDiceRoll), fBuckshot, FALSE ); + FireBulletGivenTarget( pSoldier, dTargetX, dTargetY, dTargetZ, usUBItem, (UINT16) (uiHitChance - uiDiceRoll), fBuckshot, FALSE ); } else { - FireBulletGivenTarget( pSoldier, dTargetX, dTargetY, dTargetZ, pSoldier->usAttackingWeapon, (UINT16) (uiHitChance - uiDiceRoll), fBuckshot, TRUE ); + FireBulletGivenTarget( pSoldier, dTargetX, dTargetY, dTargetZ, usUBItem, (UINT16) (uiHitChance - uiDiceRoll), fBuckshot, TRUE ); } //bottom one is fake (ie not in my control) - ubVolume = Weapon[ pSoldier->usAttackingWeapon ].ubAttackVolume; + ubVolume = Weapon[ usUBItem ].ubAttackVolume; - if ( Item[ usItemNum ].usItemClass == IC_THROWING_KNIFE ) + if ( Item[ usUBItem ].usItemClass == IC_THROWING_KNIFE ) { // Here, remove the knife... or (for now) rocket launcher pSoldier->inv[ HANDPOS ].RemoveObjectsFromStack(1); DirtyMercPanelInterface( pSoldier, DIRTYLEVEL2 ); } - else if ( Item[usItemNum].rocketlauncher ) + else if ( Item[usUBItem].rocketlauncher ) { - if ( Item[usItemNum].singleshotrocketlauncher ) + if ( Item[usUBItem].singleshotrocketlauncher ) { - CreateItem( Item[usItemNum].discardedlauncheritem, pSoldier->inv[ HANDPOS ][0]->data.objectStatus, &(pSoldier->inv[ HANDPOS ] ) ); + CreateItem( Item[usUBItem].discardedlauncheritem, (*pObjUsed)[0]->data.objectStatus, pObjUsed ); DirtyMercPanelInterface( pSoldier, DIRTYLEVEL2 ); } @@ -2655,15 +2631,15 @@ BOOLEAN UseGun( SOLDIERTYPE *pSoldier , INT32 sTargetGridNo ) else { // Snap: get cumulative noise reduction from the weapon and its attachments - UINT16 noisefactor = GetPercentNoiseVolume( &pSoldier->inv[ pSoldier->ubAttackingHand ] ); + UINT16 noisefactor = GetPercentNoiseVolume( pObjUsed ); if ( ubVolume * noisefactor > 25000 ) { // Snap: hack this to prevent overflow (damn miserly programmers!) ubVolume = 250; } else { - ubVolume = __max( 1, ( ubVolume * GetPercentNoiseVolume( &pSoldier->inv[ pSoldier->ubAttackingHand ] ) ) / 100 ); - } + ubVolume = __max( 1, ( ubVolume * GetPercentNoiseVolume( pObjUsed ) ) / 100 ); + } } MakeNoise( pSoldier->ubID, pSoldier->sGridNo, pSoldier->pathing.bLevel, pSoldier->bOverTerrainType, ubVolume, NOISE_GUNFIRE ); @@ -2678,14 +2654,12 @@ BOOLEAN UseGun( SOLDIERTYPE *pSoldier , INT32 sTargetGridNo ) // Flugente FTW 1: Increase Weapon Temperature if ( gGameOptions.fWeaponOverheating ) { - FLOAT overheatjampercentage = GetGunOverheatDamagePercentage( &(pSoldier->inv[ pSoldier->ubAttackingHand ])); // ... how much above the gun's usOverheatingDamageThreshold are we? ... + FLOAT overheatjampercentage = GetGunOverheatDamagePercentage( pObjUsed ); // ... how much above the gun's usOverheatingDamageThreshold are we? ... if ( overheatjampercentage > 1.0 ) - { iOverheatReliabilityMalus = (INT16)floor(overheatjampercentage*overheatjampercentage); - } - GunIncreaseHeat( &(pSoldier->inv[ pSoldier->ubAttackingHand ]) ); + GunIncreaseHeat( pObjUsed ); } /* //WarmSteel - Replaced with GetReliability( pObj ) @@ -2701,11 +2675,11 @@ BOOLEAN UseGun( SOLDIERTYPE *pSoldier , INT32 sTargetGridNo ) */ // Flugente FTW 1: Added a malus to reliability for overheated guns - uiDepreciateTest = max( BASIC_DEPRECIATE_CHANCE + 3 * ( GetReliability( &(pSoldier->inv[ pSoldier->ubAttackingHand ]) ) ) - iOverheatReliabilityMalus, 0); + uiDepreciateTest = max( BASIC_DEPRECIATE_CHANCE + 3 * ( GetReliability( pObjUsed ) ) - iOverheatReliabilityMalus, 0); - if ( !PreRandom( uiDepreciateTest ) && ( pSoldier->inv[ pSoldier->ubAttackingHand ][0]->data.objectStatus > 1) ) + if ( !PreRandom( uiDepreciateTest ) && ( (*pObjUsed)[0]->data.objectStatus > 1) ) { - pSoldier->inv[ pSoldier->ubAttackingHand ][0]->data.objectStatus--; + (*pObjUsed)[0]->data.objectStatus--; } // reduce monster smell (gunpowder smell) @@ -2715,8 +2689,10 @@ BOOLEAN UseGun( SOLDIERTYPE *pSoldier , INT32 sTargetGridNo ) } // manual recharge - if (Weapon[Item[usItemNum].ubClassIndex].APsToReloadManually > 0) - pSoldier->inv[ pSoldier->ubAttackingHand ][0]->data.gun.ubGunState &= ~GS_CARTRIDGE_IN_CHAMBER; + if (Weapon[Item[usUBItem].ubClassIndex].APsToReloadManually > 0) + { + (*pObjUsed)[0]->data.gun.ubGunState &= ~GS_CARTRIDGE_IN_CHAMBER; + } // DebugMsg(TOPIC_JA2,DBG_LEVEL_3,String("UseGun: done")); return( TRUE ); @@ -2789,9 +2765,12 @@ BOOLEAN UseBlade( SOLDIERTYPE *pSoldier , INT32 sTargetGridNo ) // attack HITS, calculate damage (base damage is 1-maximum knife sImpact) iImpact = HTHImpact( pSoldier, pTargetSoldier, (iHitChance - iDiceRoll), TRUE ); - // modify this by the knife's condition (if it's dull, not much good) - iImpact = ( iImpact * WEAPON_STATUS_MOD(pSoldier->inv[pSoldier->ubAttackingHand][0]->data.objectStatus) ) / 100; + // Flugente: check for underbarrel weapons and use that object if necessary (think of bayonets) + OBJECTTYPE* pObj = pSoldier->GetUsedWeapon( &pSoldier->inv[pSoldier->ubAttackingHand] ); + // modify this by the knife's condition (if it's dull, not much good) + iImpact = ( iImpact * WEAPON_STATUS_MOD( (*pObj)[0]->data.objectStatus) ) / 100; + // modify by hit location AdjustImpactByHitLocation( iImpact, pSoldier->bAimShotLocation, &iImpact, &iImpactForCrits ); @@ -2806,18 +2785,18 @@ BOOLEAN UseBlade( SOLDIERTYPE *pSoldier , INT32 sTargetGridNo ) { iImpact = 1; } - - if ( pSoldier->inv[ pSoldier->ubAttackingHand ][0]->data.objectStatus > USABLE ) + + if ( (*pObj)[0]->data.objectStatus > USABLE ) { bMaxDrop = (iImpact / 20); // the duller they get, the slower they get any worse... - bMaxDrop = __min( bMaxDrop, pSoldier->inv[ pSoldier->ubAttackingHand ][0]->data.objectStatus / 10 ); + bMaxDrop = __min( bMaxDrop, (*pObj)[0]->data.objectStatus / 10 ); // as long as its still > USABLE, it drops another point 1/2 the time bMaxDrop = __max( bMaxDrop, 2 ); - pSoldier->inv[ pSoldier->ubAttackingHand ][0]->data.objectStatus -= (INT8) Random( bMaxDrop ); // 0 to (maxDrop - 1) + (*pObj)[0]->data.objectStatus -= (INT8) Random( bMaxDrop ); // 0 to (maxDrop - 1) } // SANDRO - new merc records - times wounded (stabbed) @@ -3852,10 +3831,13 @@ BOOLEAN DoSpecialEffectAmmoMiss( UINT8 ubAttackerID, INT32 sGridNo, INT16 sXPos, { ANITILE_PARAMS AniParams; UINT8 ubAmmoType; - UINT16 usItem; + UINT16 usItem; - ubAmmoType = MercPtrs[ ubAttackerID ]->inv[ MercPtrs[ ubAttackerID ]->ubAttackingHand ][0]->data.gun.ubGunAmmoType; - usItem = MercPtrs[ ubAttackerID ]->inv[ MercPtrs[ ubAttackerID ]->ubAttackingHand ].usItem; + // Flugente: check for underbarrel weapons and use that object if necessary + OBJECTTYPE* pObj = MercPtrs[ ubAttackerID ]->GetUsedWeapon( &MercPtrs[ ubAttackerID ]->inv[ MercPtrs[ ubAttackerID ]->ubAttackingHand ] ); + + ubAmmoType = (*pObj)[0]->data.gun.ubGunAmmoType; + usItem = (*pObj).usItem; memset( &AniParams, 0, sizeof( ANITILE_PARAMS ) ); @@ -4008,24 +3990,27 @@ BOOLEAN DoSpecialEffectAmmoMiss( UINT8 ubAttackerID, INT32 sGridNo, INT16 sXPos, void WeaponHit( UINT16 usSoldierID, UINT16 usWeaponIndex, INT16 sDamage, INT16 sBreathLoss, UINT16 usDirection, INT16 sXPos, INT16 sYPos, INT16 sZPos, INT16 sRange , UINT8 ubAttackerID, BOOLEAN fHit, UINT8 ubSpecial, UINT8 ubHitLocation ) { SOLDIERTYPE *pTargetSoldier, *pSoldier; - + OBJECTTYPE *pObj; // Get attacker pSoldier = MercPtrs[ ubAttackerID ]; + // Flugente: check for underbarrel weapons and use that object if necessary + pObj = pSoldier->GetUsedWeapon( &pSoldier->inv[pSoldier->ubAttackingHand] ); + // Get Target pTargetSoldier = MercPtrs[ usSoldierID ]; MakeNoise( ubAttackerID, pTargetSoldier->sGridNo, pTargetSoldier->pathing.bLevel, gpWorldLevelData[pTargetSoldier->sGridNo].ubTerrainID, Weapon[ usWeaponIndex ].ubHitVolume, NOISE_BULLET_IMPACT ); // CALLAHAN START BUGFIX - if ( EXPLOSIVE_GUN( usWeaponIndex ) || AmmoTypes[pSoldier->inv[pSoldier->ubAttackingHand ][0]->data.gun.ubGunAmmoType].explosionSize > 1) + if ( EXPLOSIVE_GUN( usWeaponIndex ) || AmmoTypes[(*pObj)[0]->data.gun.ubGunAmmoType].explosionSize > 1) // CALLAHAN END BUGFIX { // Reduce attacker count! //TODO: Madd --- I don't think this code will ever get called for the HE ammo -- the EXPLOSIVE_GUN check filters out regular guns // marke test mag ammo type: pSoldier->inv[pSoldier->ubAttackingHand ][0]->data.gun.ubGunAmmoType // 2cond 'or' added - if ( Item[usWeaponIndex].rocketlauncher || AmmoTypes[pSoldier->inv[pSoldier->ubAttackingHand ][0]->data.gun.ubGunAmmoType].explosionSize > 1 ) + if ( Item[usWeaponIndex].rocketlauncher || AmmoTypes[(*pObj)[0]->data.gun.ubGunAmmoType].explosionSize > 1 ) { if ( Item[usWeaponIndex].singleshotrocketlauncher ) { @@ -4033,6 +4018,7 @@ void WeaponHit( UINT16 usSoldierID, UINT16 usWeaponIndex, INT16 sDamage, INT16 s } // changed rpg type to work only with two flags matching else if ( !Item[usWeaponIndex].singleshotrocketlauncher && Item[usWeaponIndex].rocketlauncher) + //we shouldn't be able to have an underbarrel firing mode in this step, so we keep the original code :JMich { DebugMsg( TOPIC_JA2, DBG_LEVEL_3, String("WeaponHit: RPG7 item: %d, Ammo: %d",pSoldier->inv[HANDPOS].usItem , pSoldier->inv[HANDPOS][0]->data.gun.usGunAmmoItem ) ); @@ -4046,10 +4032,10 @@ void WeaponHit( UINT16 usSoldierID, UINT16 usWeaponIndex, INT16 sDamage, INT16 s pSoldier->inv[pSoldier->ubAttackingHand ][0]->data.gun.usGunAmmoItem = NONE; } } - else if ( AmmoTypes[pSoldier->inv[pSoldier->ubAttackingHand ][0]->data.gun.ubGunAmmoType].explosionSize > 1) + else if ( AmmoTypes[(*pObj)[0]->data.gun.ubGunAmmoType].explosionSize > 1) { // re-routed the Highexplosive value to define exposion type - IgniteExplosion( ubAttackerID, sXPos, sYPos, 0, GETWORLDINDEXFROMWORLDCOORDS( sYPos, sXPos ), AmmoTypes[pSoldier->inv[pSoldier->ubAttackingHand ][0]->data.gun.ubGunAmmoType].highExplosive , pTargetSoldier->pathing.bLevel ); + IgniteExplosion( ubAttackerID, sXPos, sYPos, 0, GETWORLDINDEXFROMWORLDCOORDS( sYPos, sXPos ), AmmoTypes[(*pObj)[0]->data.gun.ubGunAmmoType].highExplosive , pTargetSoldier->pathing.bLevel ); // pSoldier->inv[pSoldier->ubAttackingHand ][0]->data.gun.usGunAmmoItem = NONE; } } @@ -4095,9 +4081,13 @@ void StructureHit( INT32 iBullet, UINT16 usWeaponIndex, INT16 bWeaponStatus, UIN BOOLEAN fHitSameStructureAsBefore; BULLET * pBullet; SOLDIERTYPE * pAttacker = NULL; + OBJECTTYPE * pObj; pBullet = GetBulletPtr( iBullet ); + // Flugente: check for underbarrel weapons and use that object if necessary + pObj = MercPtrs[ ubAttackerID ]->GetUsedWeapon( &MercPtrs [ ubAttackerID ]->inv[MercPtrs[ubAttackerID]->ubAttackingHand] ); + if ( fStopped && ubAttackerID != NOBODY ) { pAttacker = MercPtrs[ ubAttackerID ]; @@ -4151,7 +4141,7 @@ void StructureHit( INT32 iBullet, UINT16 usWeaponIndex, INT16 bWeaponStatus, UIN // Get attacker pSoldier = MercPtrs[ ubAttackerID ]; // marke added one 'or' to get this working with HE ammo - if ( Item[usWeaponIndex].rocketlauncher || AmmoTypes[pSoldier->inv[pSoldier->ubAttackingHand ][0]->data.gun.ubGunAmmoType].explosionSize > 1) + if ( Item[usWeaponIndex].rocketlauncher || AmmoTypes[ (*pObj)[0]->data.gun.ubGunAmmoType].explosionSize > 1) { // Reduce attacker count! DebugMsg( TOPIC_JA2, DBG_LEVEL_3, String("@@@@@@@ Freeing up attacker - end of LAW fire") ); @@ -4162,6 +4152,7 @@ void StructureHit( INT32 iBullet, UINT16 usWeaponIndex, INT16 bWeaponStatus, UIN } // changed too to use 2 flag to determine else if ( !Item[usWeaponIndex].singleshotrocketlauncher && Item[usWeaponIndex].rocketlauncher) + //there shouldn't be a way to enter here with an UnderBarrel weapon, so retaining original code :JMich { DebugMsg( TOPIC_JA2, DBG_LEVEL_3, String("StructureHit: RPG7 item: %d, Ammo: %d",pAttacker->inv[HANDPOS].usItem , pAttacker->inv[HANDPOS][0]->data.gun.usGunAmmoItem ) ); IgniteExplosion( ubAttackerID, CenterX( sGridNo ), CenterY( sGridNo ), 0, sGridNo, pAttacker->inv[pAttacker->ubAttackingHand ][0]->data.gun.usGunAmmoItem , (INT8)( sZPos >= WALL_HEIGHT ) ); @@ -4174,10 +4165,10 @@ void StructureHit( INT32 iBullet, UINT16 usWeaponIndex, INT16 bWeaponStatus, UIN pAttacker->inv[pAttacker->ubAttackingHand ][0]->data.gun.usGunAmmoItem = NONE; } } - else if ( AmmoTypes[pSoldier->inv[pSoldier->ubAttackingHand ][0]->data.gun.ubGunAmmoType].explosionSize > 1) + else if ( AmmoTypes[(*pObj)[0]->data.gun.ubGunAmmoType].explosionSize > 1) { // re-routed the Highexplosive value to define exposion type - IgniteExplosion( ubAttackerID, CenterX( sGridNo ), CenterY( sGridNo ), 0, sGridNo, AmmoTypes[pSoldier->inv[pSoldier->ubAttackingHand ][0]->data.gun.ubGunAmmoType].highExplosive , (INT8)( sZPos >= WALL_HEIGHT ) ); + IgniteExplosion( ubAttackerID, CenterX( sGridNo ), CenterY( sGridNo ), 0, sGridNo, AmmoTypes[ (*pObj)[0]->data.gun.ubGunAmmoType].highExplosive , (INT8)( sZPos >= WALL_HEIGHT ) ); // pSoldier->inv[pSoldier->ubAttackingHand ][0]->data.gun.usGunAmmoItem = NONE; } @@ -4525,9 +4516,13 @@ BOOLEAN InRange( SOLDIERTYPE *pSoldier, INT32 sGridNo ) { INT32 sRange; UINT16 usInHand; - + OBJECTTYPE *pObj; DebugMsg(TOPIC_JA2,DBG_LEVEL_3,String("InRange")); - usInHand = pSoldier->inv[HANDPOS].usItem; + + // Flugente: check for underbarrel weapons and use that object if necessary + pObj = pSoldier->GetUsedWeapon( &pSoldier->inv[HANDPOS] ); + + usInHand = (*pObj).usItem; INVTYPE* pItemInHand = &Item[ usInHand ]; if ( pItemInHand->usItemClass == IC_GUN || pItemInHand->usItemClass == IC_THROWING_KNIFE || (pItemInHand->rocketlauncher && !pItemInHand->singleshotrocketlauncher)) @@ -4546,7 +4541,7 @@ BOOLEAN InRange( SOLDIERTYPE *pSoldier, INT32 sGridNo ) else { // For given weapon, check range - if ( sRange <= GunRange( &(pSoldier->inv[HANDPOS]), pSoldier ) ) // SANDRO - added argument + if ( sRange <= GunRange( pObj, pSoldier ) ) // SANDRO - added argument { return( TRUE ); } @@ -4584,6 +4579,11 @@ UINT32 CalcNewChanceToHitGun(SOLDIERTYPE *pSoldier, INT32 sGridNo, INT16 ubAimTi // make sure the guy's actually got a weapon in his hand! pInHand = &(pSoldier->inv[pSoldier->ubAttackingHand]); + + // Flugente: check for underbarrel weapons and use that object if necessary + OBJECTTYPE* pObjAttHand = pSoldier->GetUsedWeapon( &(pSoldier->inv[pSoldier->ubAttackingHand]) ); + UINT16 usItemAttHand = pSoldier->GetUsedWeaponNumber( &(pSoldier->inv[pSoldier->ubAttackingHand]) ); + usInHand = pSoldier->usAttackingWeapon; gCTHDisplay.fMaxAimReached = FALSE; @@ -4741,7 +4741,7 @@ UINT32 CalcNewChanceToHitGun(SOLDIERTYPE *pSoldier, INT32 sGridNo, INT16 ubAimTi iTraitModifier += 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[usInHand].ubWeaponType == GUN_PISTOL ) + else if ( Weapon[usItemAttHand].ubWeaponType == GUN_PISTOL ) { iTraitModifier += gSkillTraitValues.bCtHModifierPistols; // -5% for untrained mercs. @@ -4749,7 +4749,7 @@ UINT32 CalcNewChanceToHitGun(SOLDIERTYPE *pSoldier, INT32 sGridNo, INT16 ubAimTi if ( HAS_SKILL_TRAIT( pSoldier, GUNSLINGER_NT ) && pSoldier->bDoBurst == 0 && pSoldier->bDoAutofire == 0 ) iTraitModifier += gSkillTraitValues.ubGSBonusCtHPistols * NUM_SKILL_TRAITS( pSoldier, GUNSLINGER_NT ); // +10% per trait } - else if ( Weapon[usInHand].ubWeaponType == GUN_M_PISTOL ) + else if ( Weapon[usItemAttHand].ubWeaponType == GUN_M_PISTOL ) { iTraitModifier += gSkillTraitValues.bCtHModifierMachinePistols; // -5% for untrained mercs. @@ -4758,21 +4758,21 @@ UINT32 CalcNewChanceToHitGun(SOLDIERTYPE *pSoldier, INT32 sGridNo, INT16 ubAimTi iTraitModifier += 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[usInHand].ubWeaponType == GUN_AS_RIFLE ) + else if ( Weapon[usItemAttHand].ubWeaponType == GUN_AS_RIFLE ) { iTraitModifier += gSkillTraitValues.bCtHModifierAssaultRifles; // -5% for untrained mercs. if ( HAS_SKILL_TRAIT( pSoldier, AUTO_WEAPONS_NT ) ) iTraitModifier += gSkillTraitValues.ubAWBonusCtHAssaultRifles * NUM_SKILL_TRAITS( pSoldier, AUTO_WEAPONS_NT ); // +5% per trait } - else if ( Weapon[usInHand].ubWeaponType == GUN_SMG ) + else if ( Weapon[usItemAttHand].ubWeaponType == GUN_SMG ) { iTraitModifier += gSkillTraitValues.bCtHModifierSMGs; // -5% for untrained mercs. if ( HAS_SKILL_TRAIT( pSoldier, AUTO_WEAPONS_NT ) ) iTraitModifier += gSkillTraitValues.ubAWBonusCtHSMGs * NUM_SKILL_TRAITS( pSoldier, AUTO_WEAPONS_NT ); // +5% per trait } - else if ( Weapon[usInHand].ubWeaponType == GUN_LMG ) + else if ( Weapon[usItemAttHand].ubWeaponType == GUN_LMG ) { iTraitModifier += gSkillTraitValues.bCtHModifierLMGs; // -10% for untrained mercs. @@ -4780,7 +4780,7 @@ UINT32 CalcNewChanceToHitGun(SOLDIERTYPE *pSoldier, INT32 sGridNo, INT16 ubAimTi iTraitModifier += 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[usInHand].ubWeaponType == GUN_SN_RIFLE ) + else if ( Weapon[usItemAttHand].ubWeaponType == GUN_SN_RIFLE ) { iTraitModifier += gSkillTraitValues.bCtHModifierSniperRifles; // -10% for untrained mercs. @@ -4899,7 +4899,7 @@ UINT32 CalcNewChanceToHitGun(SOLDIERTYPE *pSoldier, INT32 sGridNo, INT16 ubAimTi // Gun Difficulty Modifiers // FIRING 1-HANDED WEAPONS - if ( !(Item[ usInHand ].twohanded ) ) + if ( !(Item[ usInHand ].twohanded ) ) //JMich todo: underbarrel { if (pSoldier->inv[SECONDHANDPOS].exists() != false) { @@ -5149,7 +5149,7 @@ UINT32 CalcNewChanceToHitGun(SOLDIERTYPE *pSoldier, INT32 sGridNo, INT16 ubAimTi { // Are we using a scope? If so, what's the range factor? - FLOAT iScopeMagFactor = GetBestScopeMagnificationFactor( pSoldier, pInHand, d2DDistance ); + FLOAT iScopeMagFactor = GetBestScopeMagnificationFactor( pSoldier, &(pSoldier->inv[pSoldier->ubAttackingHand]), d2DDistance ); //CHRISL: This does make sense but it effectively makes high powered scopes worthless if a target is actually visible. As an example, a Battle Scope // is going to have a iScopeMagFactor of 7. With a "NORMAL_SHOOTING_DISTANCE" also of 7, we're going to end up with uiBestScopeRange of 49. That's // effectilvey saying that any target within 490m is "too close" for the scope to be effective. That by itself isn't realistic. But in JA2 it's also @@ -5163,15 +5163,15 @@ UINT32 CalcNewChanceToHitGun(SOLDIERTYPE *pSoldier, INT32 sGridNo, INT16 ubAimTi // uiBestScopeRange value in half. This should allow a Battle Scope to reach full effeciency at 24 tiles and a Sniper scope will be fully effecient at // 35 tiles. ACOG becomes fully effecient at 14 tiles and 2x is fully effeciency at 7 tiles (compared to 28 and 14 respectively). This does mean that a // 2x scope reaches full effeciency at the same point as "scopeless" shooting, but I don't think this will be a serious problem. - FLOAT rangeModifier = GetScopeRangeMultiplier(pSoldier, pInHand, (FLOAT)iRange); + FLOAT rangeModifier = GetScopeRangeMultiplier(pSoldier, &(pSoldier->inv[pSoldier->ubAttackingHand]), (FLOAT)iRange); UINT32 uiBestScopeRange = (UINT32)(iScopeMagFactor * gGameCTHConstants.NORMAL_SHOOTING_DISTANCE * rangeModifier); FLOAT iAimModifier = 0; // WEAPON CONDITION - if ( pSoldier->inv[HANDPOS][0]->data.objectStatus < 50 ) + if ( (*pInHand)[0]->data.objectStatus < 50 ) { - iAimModifier += gGameCTHConstants.AIM_GUN_CONDITION * (50 - pSoldier->inv[HANDPOS][0]->data.objectStatus); + iAimModifier += gGameCTHConstants.AIM_GUN_CONDITION * (50 - (*pInHand)[0]->data.objectStatus); } // MORALE @@ -5217,7 +5217,7 @@ UINT32 CalcNewChanceToHitGun(SOLDIERTYPE *pSoldier, INT32 sGridNo, INT16 ubAimTi } // FIRING 1-HANDED WEAPONS - if ( !(Item[ usInHand ].twohanded ) ) + if ( !(Item[ usInHand ].twohanded ) ) //JMich Todo: fix for UnderBarrel firing { if (pSoldier->inv[SECONDHANDPOS].exists() != false) { @@ -5491,10 +5491,10 @@ UINT32 CalcNewChanceToHitGun(SOLDIERTYPE *pSoldier, INT32 sGridNo, INT16 ubAimTi INT32 iScopePenalty = (INT32)(dScopePenaltyRatio * gGameCTHConstants.AIM_TOO_CLOSE_SCOPE * (iScopeMagFactor /2)); iMaxAimBonus += iScopePenalty; } - else if (iScopeMagFactor == 1.0f && GetHighestScopeMagnificationFactor( pInHand ) > 1.0f ) + else if (iScopeMagFactor == 1.0f && GetHighestScopeMagnificationFactor( &(pSoldier->inv[pSoldier->ubAttackingHand]) ) > 1.0f ) { // Not using a scope, but it's still there. Give half the penalty based on the size of the scope. - INT32 iScopePenalty = (INT32)(((GetHighestScopeMagnificationFactor( pInHand )/2) * gGameCTHConstants.AIM_TOO_CLOSE_SCOPE)/2); + INT32 iScopePenalty = (INT32)(((GetHighestScopeMagnificationFactor( &(pSoldier->inv[pSoldier->ubAttackingHand]) )/2) * gGameCTHConstants.AIM_TOO_CLOSE_SCOPE)/2); iMaxAimBonus += iScopePenalty; } @@ -5931,8 +5931,11 @@ UINT32 CalcChanceToHitGun(SOLDIERTYPE *pSoldier, INT32 sGridNo, INT16 ubAimTime, ///////////////////////////////////////////////////////////////////////////////////// // Assign basic variables + // Flugente: check for underbarrel weapons and use that object if necessary + pInHand = pSoldier->GetUsedWeapon( &(pSoldier->inv[pSoldier->ubAttackingHand]) ); + UINT16 usItemUsed = pSoldier->GetUsedWeaponNumber( &(pSoldier->inv[pSoldier->ubAttackingHand]) ); + pTarget = SimpleFindSoldier( sGridNo, pSoldier->bTargetLevel ); - pInHand = &(pSoldier->inv[pSoldier->ubAttackingHand]); iGunCondition = WEAPON_STATUS_MOD( (*pInHand)[0]->data.gun.bGunStatus ); usInHand = pSoldier->usAttackingWeapon; ubTargetID = WhoIsThere2( sGridNo, pSoldier->bTargetLevel ); @@ -5973,13 +5976,13 @@ UINT32 CalcChanceToHitGun(SOLDIERTYPE *pSoldier, INT32 sGridNo, INT16 ubAimTime, gbForceWeaponNotReady = false; scopeRangeMod = (float)sDistVis / (float)sDistVisNoScope; // percentage DistVis has been enhanced due to an attached scope iMaxNormRange = MaxNormalDistanceVisible() * CELL_X_SIZE; - if ( Item[ usInHand ].usItemClass == IC_GUN || Item[ usInHand ].usItemClass == IC_LAUNCHER) + if ( Item[ usItemUsed ].usItemClass == IC_GUN || Item[ usItemUsed ].usItemClass == IC_LAUNCHER) iMaxRange = GunRange( pInHand, pSoldier ); // SANDRO - added argument else iMaxRange = CELL_X_SIZE; // one tile iCoverRange = __max(0,iSightRange - iRange); iMinRange = iMaxRange / 10; - iAccRangeMod = iRange * Weapon[usInHand].bAccuracy / 100; + iAccRangeMod = iRange * Weapon[usItemUsed].bAccuracy / 100; ///////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////// @@ -5999,9 +6002,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(pInHand) && iScopeVisionRangeBonus > 50){ // iSightRange penalty for using a high power scope within min range due to poor focus + if (iRange < GetMinRangeForAimBonus(&(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(pInHand) - iRange)/CELL_X_SIZE); loop++){ + for(UINT8 loop = 0; loop < ((GetMinRangeForAimBonus(&(pSoldier->inv[pSoldier->ubAttackingHand])) - iRange)/CELL_X_SIZE); loop++){ iPenalty += iSightRange * iScopeVisionRangeBonus / 100; } iSightRange += iPenalty; @@ -6016,7 +6019,7 @@ UINT32 CalcChanceToHitGun(SOLDIERTYPE *pSoldier, INT32 sGridNo, INT16 ubAimTime, ///////////////////////////////////////////////////////////////////////////////////// // Determine iMarksmanship and Base CTH - if (Item[usInHand].rocketlauncher ){ + if (Item[usItemUsed].rocketlauncher ){ // use the same calculation as for mechanical thrown weapons iMarksmanship = ( EffectiveDexterity( pSoldier ) + EffectiveMarksmanship( pSoldier ) + EffectiveWisdom( pSoldier ) + (10 * EffectiveExpLevel( pSoldier ) )) / 4; // heavy weapons trait helps out @@ -6213,7 +6216,7 @@ 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 = (*pInHand)[0]->attachments.begin(); iter != (*pInHand)[0]->attachments.end(); iter++) + for(attachmentList::iterator iter = (*&(pSoldier->inv[pSoldier->ubAttackingHand]))[0]->attachments.begin(); iter != (*&(pSoldier->inv[pSoldier->ubAttackingHand]))[0]->attachments.end(); iter++) { if(iter->exists() && Item[iter->usItem].aimbonus >= gGameExternalOptions.sHighPowerScope && iRange > Item[iter->usItem].minrangeforaimbonus) { @@ -6230,7 +6233,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[usInHand].rocketlauncher || Item[usInHand].singleshotrocketlauncher) + if (Item[usItemUsed].rocketlauncher || Item[usItemUsed].singleshotrocketlauncher) { iChance += gSkillTraitValues.bCtHModifierRocketLaunchers; // -25% for untrained mercs !!! @@ -6238,7 +6241,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[usInHand].ubWeaponType == GUN_PISTOL ) + else if ( Weapon[usItemUsed].ubWeaponType == GUN_PISTOL ) { iChance += gSkillTraitValues.bCtHModifierPistols; // -5% for untrained mercs. @@ -6246,7 +6249,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[usInHand].ubWeaponType == GUN_M_PISTOL ) + else if ( Weapon[usItemUsed].ubWeaponType == GUN_M_PISTOL ) { iChance += gSkillTraitValues.bCtHModifierMachinePistols; // -5% for untrained mercs. @@ -6255,21 +6258,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[usInHand].ubWeaponType == GUN_AS_RIFLE ) + else if ( Weapon[usItemUsed].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[usInHand].ubWeaponType == GUN_SMG ) + else if ( Weapon[usItemUsed].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[usInHand].ubWeaponType == GUN_LMG ) + else if ( Weapon[usItemUsed].ubWeaponType == GUN_LMG ) { iChance += gSkillTraitValues.bCtHModifierLMGs; // -10% for untrained mercs. @@ -6277,7 +6280,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[usInHand].ubWeaponType == GUN_SN_RIFLE ) + else if ( Weapon[usItemUsed].ubWeaponType == GUN_SN_RIFLE ) { iChance += gSkillTraitValues.bCtHModifierSniperRifles; // -5% for untrained mercs. @@ -6286,7 +6289,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[usInHand].ubWeaponType == GUN_RIFLE ) + else if ( Weapon[usItemUsed].ubWeaponType == GUN_RIFLE ) { iChance += gSkillTraitValues.bCtHModifierRifles; // -5% for untrained mercs. @@ -6298,7 +6301,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[usInHand].ubWeaponType == GUN_SHOTGUN ) + else if ( Weapon[usItemUsed].ubWeaponType == GUN_SHOTGUN ) { iChance += gSkillTraitValues.bCtHModifierShotguns; // -5% for untrained mercs. @@ -6447,6 +6450,7 @@ UINT32 CalcChanceToHitGun(SOLDIERTYPE *pSoldier, INT32 sGridNo, INT16 ubAimTime, ///////////////////////////////////////////////////////////////////////////////////// // Modify for using one hand if ( !(Item[ usInHand ].twohanded ) ) + //check for 2weapon, retaining original code :JMich { if (pSoldier->inv[SECONDHANDPOS].exists() == false) { @@ -8202,8 +8206,13 @@ UINT32 AICalcChanceToHitGun(SOLDIERTYPE *pSoldier, INT32 sGridNo, INT16 ubAimTim uiChance = (UINT32)__min(100, (dTargetArea / dApertureArea) * 100); } + FLOAT dGunRange; + + // Flugente: check for underbarrel weapons and use that object if necessary + OBJECTTYPE* pObjAttHand = pSoldier->GetUsedWeapon( &(pSoldier->inv[pSoldier->ubAttackingHand]) ); + + dGunRange = (FLOAT)(GunRange( pObjAttHand, pSoldier ) ); - FLOAT dGunRange = (FLOAT)(GunRange( &(pSoldier->inv[pSoldier->ubAttackingHand]), pSoldier ) ); FLOAT dMaxGunRange = dGunRange * gGameCTHConstants.MAX_EFFECTIVE_RANGE_MULTIPLIER; if ( dMaxGunRange < d2DDistance) { @@ -8449,7 +8458,10 @@ INT32 BulletImpact( SOLDIERTYPE *pFirer, SOLDIERTYPE * pTarget, UINT8 ubHitLocat } else { - ubAmmoType = pFirer->inv[pFirer->ubAttackingHand][0]->data.gun.ubGunAmmoType; + // Flugente: check for underbarrel weapons and use that object if necessary + OBJECTTYPE* pObj = pFirer->GetUsedWeapon( &pFirer->inv[pFirer->ubAttackingHand] ); + + ubAmmoType = (*pObj)[0]->data.gun.ubGunAmmoType; } if ( TANK( pTarget ) ) @@ -9044,7 +9056,12 @@ INT32 HTHImpact( SOLDIERTYPE * pSoldier, SOLDIERTYPE * pTarget, INT32 iHitBy, BO if (fBladeAttack) { iImpact = ( EffectiveExpLevel( pSoldier ) / 2); // 0 to 4 for level - iImpact += GetDamage(&pSoldier->inv[HANDPOS]); + + // Flugente: check for underbarrel weapons and use that object if necessary (think of bayonets) + OBJECTTYPE* pObj = pSoldier->GetUsedWeapon( &(pSoldier->inv[HANDPOS]) ); + + iImpact += GetDamage(pObj); + iImpact += EffectiveStrength( pSoldier ) / 20; // 0 to 5 for strength, adjusted by damage taken if ( AM_A_ROBOT( pTarget ) ) @@ -9933,6 +9950,10 @@ void ReloadWeapon( SOLDIERTYPE *pSoldier, UINT8 ubHandPos ) { pSoldier->inv[ ubHandPos ][0]->data.gun.ubGunShotsLeft = GetMagSize(&pSoldier->inv[ ubHandPos ]); // Dirty Bars + if ( IsUnderBarrelAttached(&pSoldier->inv[ ubHandPos ])) + { + (*FindAttachment_UnderBarrel(&pSoldier->inv[ ubHandPos ]))[0]->data.gun.ubGunShotsLeft = GetMagSize(FindAttachment_UnderBarrel(&pSoldier->inv[ ubHandPos ])); + } DirtyMercPanelInterface( pSoldier, DIRTYLEVEL1 ); } } @@ -9971,6 +9992,15 @@ BOOLEAN IsGunWeaponModeCapable( OBJECTTYPE* pObject, WeaponMode bWpnMode, SOLDIE return FALSE; // return (!Item[pSoldier->inv[ubHandPos].usItem].grenadelauncher && Weapon[GetAttachedGrenadeLauncher(&pSoldier->inv[ubHandPos])].bAutofireShotsPerFiveAP > 0 && FindLaunchableAttachment( &(pSoldier->inv[ubHandPos]), GetAttachedGrenadeLauncher( &(pSoldier->inv[ubHandPos]) ) != ITEM_NOT_FOUND ); + case WM_ATTACHED_UB: + return (IsUnderBarrelAttached( pObject ) && !Weapon[FindAttachment_UnderBarrel(pObject)->usItem].NoSemiAuto ); + + case WM_ATTACHED_UB_BURST: + return (IsUnderBarrelAttached( pObject ) && IsGunBurstCapable(FindAttachment_UnderBarrel(pObject), FALSE, pSoldier)); + + case WM_ATTACHED_UB_AUTO: + return (IsUnderBarrelAttached( pObject ) && (IsGunAutofireCapable(FindAttachment_UnderBarrel(pObject)) || Weapon[FindAttachment_UnderBarrel(pObject)->usItem].NoSemiAuto)); + default: return FALSE; } @@ -10037,7 +10067,8 @@ void HandleTacticalEffectsOfEquipmentChange( SOLDIERTYPE *pSoldier, UINT32 uiInv SetBurstAndAutoFireMode(pSoldier, GetWeaponMode(&pSoldier->inv[uiInvPos])); #endif // if in attached weapon mode and don't have weapon with GL attached in hand, reset weapon mode - if ( (pSoldier->bWeaponMode == WM_ATTACHED_GL || pSoldier->bWeaponMode == WM_ATTACHED_GL_BURST || pSoldier->bWeaponMode == WM_ATTACHED_GL_AUTO )&& !IsGrenadeLauncherAttached( &(pSoldier->inv[ HANDPOS ]) ) ) + if ( ( (pSoldier->bWeaponMode == WM_ATTACHED_GL || pSoldier->bWeaponMode == WM_ATTACHED_GL_BURST || pSoldier->bWeaponMode == WM_ATTACHED_GL_AUTO )&& !IsGrenadeLauncherAttached( &(pSoldier->inv[ HANDPOS ]) ) ) || + ( (pSoldier->bWeaponMode == WM_ATTACHED_UB || pSoldier->bWeaponMode == WM_ATTACHED_UB_BURST || pSoldier->bWeaponMode == WM_ATTACHED_UB_AUTO )&& !IsUnderBarrelAttached( &(pSoldier->inv[ HANDPOS ] ) ) ) ) { if ( !Weapon[pSoldier->inv[ HANDPOS ].usItem].NoSemiAuto ) { @@ -10507,12 +10538,12 @@ void ChangeWeaponMode( SOLDIERTYPE * pSoldier ) //while(IsGunWeaponModeCapable( pSoldier, HANDPOS, pSoldier->bWeaponMode ) == FALSE && pSoldier->bWeaponMode != WM_NORMAL); while(IsGunWeaponModeCapable( &pSoldier->inv[HANDPOS], static_cast(pSoldier->bWeaponMode), pSoldier ) == FALSE && pSoldier->bWeaponMode != WM_NORMAL); - if (pSoldier->bWeaponMode == WM_AUTOFIRE || pSoldier->bWeaponMode == WM_ATTACHED_GL_AUTO ) + if (pSoldier->bWeaponMode == WM_AUTOFIRE || pSoldier->bWeaponMode == WM_ATTACHED_GL_AUTO || pSoldier->bWeaponMode == WM_ATTACHED_UB_AUTO) { pSoldier->bDoAutofire = 1; pSoldier->bDoBurst = 1; } - else if(pSoldier->bWeaponMode == WM_BURST || pSoldier->bWeaponMode == WM_ATTACHED_GL_BURST ) + else if(pSoldier->bWeaponMode == WM_BURST || pSoldier->bWeaponMode == WM_ATTACHED_GL_BURST || pSoldier->bWeaponMode == WM_ATTACHED_UB_BURST) { pSoldier->bDoAutofire = 0; pSoldier->bDoBurst = 1; diff --git a/Tactical/Weapons.h b/Tactical/Weapons.h index 1abb1a0e..bb5e0b74 100644 --- a/Tactical/Weapons.h +++ b/Tactical/Weapons.h @@ -12,6 +12,9 @@ enum WeaponMode WM_ATTACHED_GL, WM_ATTACHED_GL_BURST, WM_ATTACHED_GL_AUTO, + WM_ATTACHED_UB, + WM_ATTACHED_UB_BURST, + WM_ATTACHED_UB_AUTO, NUM_WEAPON_MODES } ; diff --git a/Tactical/World Items.cpp b/Tactical/World Items.cpp index 5934145e..f34c7362 100644 --- a/Tactical/World Items.cpp +++ b/Tactical/World Items.cpp @@ -812,7 +812,8 @@ void RefreshWorldItemsIntoItemPools( WORLDITEM * pItemList, INT32 iNumberOfItems // Flugente FTW 1: Cool down all items in the world (no, really) -void CoolDownWorldItems( void ) +// fSetZero sets all temperatures to 0 (used in old sector upon loading a new sector) +void CoolDownWorldItems( BOOLEAN fSetZero ) { if ( gGameOptions.fWeaponOverheating ) // if Overheating is used ... { @@ -829,15 +830,21 @@ void CoolDownWorldItems( void ) { for(INT16 i = 0; i < pObj->ubNumberOfObjects; ++i) // ... there might be multiple items here (item stack), so for each one ... { - FLOAT guntemperature = (*pObj)[i]->data.bTemperature; // ... get temperature ... + FLOAT newguntemperature = 0.0; - FLOAT cooldownfactor = GetItemCooldownFactor(pObj); // ... get item cooldown factor provided of attachments ... + if ( !fSetZero && gGameExternalOptions.fSetZeroUponNewSector ) + { + FLOAT guntemperature = (*pObj)[i]->data.bTemperature; // ... get temperature ... - if ( Item[gWorldItems[ uiCount ].object.usItem].barrel == TRUE ) // ... a barrel lying around cools down a bit faster ... - cooldownfactor *= gGameExternalOptions.iCooldownModificatorLonelyBarrel; + FLOAT cooldownfactor = GetItemCooldownFactor(pObj); // ... get item cooldown factor provided of attachments ... - FLOAT newguntemperatre = max(0.0, guntemperature - cooldownfactor); // ... calculate new temperature ... - (*pObj)[i]->data.bTemperature = newguntemperatre; // ... set new temperature + if ( Item[gWorldItems[ uiCount ].object.usItem].barrel == TRUE ) // ... a barrel lying around cools down a bit faster ... + cooldownfactor *= gGameExternalOptions.iCooldownModificatorLonelyBarrel; + + newguntemperature = max(0.0, guntemperature - cooldownfactor); // ... calculate new temperature ... + } + + (*pObj)[i]->data.bTemperature = newguntemperature; // ... set new temperature #if 0//def JA2TESTVERSION ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"World: Item temperature lowered from %4.2f to %4.2f", guntemperature, newguntemperatre ); @@ -846,13 +853,19 @@ void CoolDownWorldItems( void ) attachmentList::iterator iterend = (*pObj)[i]->attachments.end(); for (attachmentList::iterator iter = (*pObj)[i]->attachments.begin(); iter != iterend; ++iter) { - if ( iter->exists() && Item[ iter->usItem ].usItemClass & (IC_LAUNCHER|IC_LAUNCHER) ) + if ( iter->exists() && Item[ iter->usItem ].usItemClass & (IC_GUN|IC_LAUNCHER) ) { - FLOAT temperature = (*iter)[i]->data.bTemperature; // ... get temperature of item ... + FLOAT newtemperature = 0.0; - FLOAT cooldownfactor = GetItemCooldownFactor( &(*iter) ); // ... get cooldown factor ... + if ( !fSetZero && gGameExternalOptions.fSetZeroUponNewSector ) + { + FLOAT temperature = (*iter)[i]->data.bTemperature; // ... get temperature of item ... + + FLOAT cooldownfactor = GetItemCooldownFactor( &(*iter) ); // ... get cooldown factor ... + + newtemperature = max(0.0, temperature - cooldownfactor); // ... calculate new temperature ... + } - FLOAT newtemperature = max(0.0, temperature - cooldownfactor); // ... calculate new temperature ... (*iter)[i]->data.bTemperature = newtemperature; // ... set new temperature #if 0//def JA2TESTVERSION diff --git a/Tactical/World Items.h b/Tactical/World Items.h index 2274cdef..c483c1a8 100644 --- a/Tactical/World Items.h +++ b/Tactical/World Items.h @@ -119,6 +119,6 @@ extern void FindPanicBombsAndTriggers( void ); extern INT32 FindWorldItemForBombInGridNo( INT32 sGridNo, INT8 bLevel); void RefreshWorldItemsIntoItemPools( WORLDITEM * pItemList, INT32 iNumberOfItems ); -void CoolDownWorldItems( void ); // Flugente FTW 1: Cool down all items in the world (no, really) +void CoolDownWorldItems( BOOLEAN fSetZero = FALSE ); // Flugente FTW 1: Cool down all items in the world (no, really) #endif \ No newline at end of file diff --git a/Utils/Text.h b/Utils/Text.h index 95f97a01..f86a8cff 100644 --- a/Utils/Text.h +++ b/Utils/Text.h @@ -2060,6 +2060,9 @@ enum MSG113_GL, MSG113_GL_BRST, MSG113_GL_AUTO, + MSG113_UB, + MSG113_UB_BRST, + MSG113_UB_AUTO, MSG113_SNIPER, MSG113_UNABLETOSPLITMONEY, MSG113_ARRIVINGREROUTED, diff --git a/Utils/_ChineseText.cpp b/Utils/_ChineseText.cpp index 85a89657..8c4a56cd 100644 --- a/Utils/_ChineseText.cpp +++ b/Utils/_ChineseText.cpp @@ -6051,6 +6051,9 @@ STR16 New113Message[] = L"榴弹", L"榴弹点射", L"榴弹自动", + L"UB", // TODO.Translate, INFO: UB = Under Barrel + L"UBRST", + L"UAUTO", L"狙击手!", L"已经点选物品,此时无法分钱。", L"新兵的会合地被挪至 %s, 因降落地点 %s 目前由敌人占据。", diff --git a/Utils/_DutchText.cpp b/Utils/_DutchText.cpp index 7a6d5487..e99327b8 100644 --- a/Utils/_DutchText.cpp +++ b/Utils/_DutchText.cpp @@ -6053,6 +6053,9 @@ STR16 New113Message[] = L"GL", L"GL BRST", L"GL AUTO", + L"UB", + L"UBRST", + L"UAUTO", L"Sniper!", L"Unable to split money due to having an item on your cursor.", L"Arrival of new recruits is being rerouted to sector %s, as scheduled drop-off point of sector %s is enemy occupied.", diff --git a/Utils/_EnglishText.cpp b/Utils/_EnglishText.cpp index d56d89d9..459d1b73 100644 --- a/Utils/_EnglishText.cpp +++ b/Utils/_EnglishText.cpp @@ -6051,6 +6051,9 @@ STR16 New113Message[] = L"GL", L"GL BRST", L"GL AUTO", + L"UB", + L"UBRST", + L"UAUTO", L"Sniper!", L"Unable to split money due to having an item on your cursor.", L"Arrival of new recruits is being rerouted to sector %s, as scheduled drop-off point of sector %s is enemy occupied.", diff --git a/Utils/_FrenchText.cpp b/Utils/_FrenchText.cpp index d816b41c..6605ddb8 100644 --- a/Utils/_FrenchText.cpp +++ b/Utils/_FrenchText.cpp @@ -6035,6 +6035,9 @@ STR16 New113Message[] = L"LG", L"RAF. LG", L"LG AUTO", + L"UB", // TODO.Translate + L"UBRST", + L"UAUTO", L"Tireur embusqué !", L"Unable to split money due to having an item on your cursor.", L"Arrivée de nouvelles recrues est déroutée au secteur %s, car le point d'arrivée prévu %s est sous contrôle ennemi.", diff --git a/Utils/_GermanText.cpp b/Utils/_GermanText.cpp index 73fd8b55..fbc4a31e 100644 --- a/Utils/_GermanText.cpp +++ b/Utils/_GermanText.cpp @@ -5880,6 +5880,9 @@ STR16 New113Message[] = L"GL", L"GL BRST", L"GL AUTO", + L"UB", + L"UBRST", + L"UAUTO", L"Scharfschütze!", L"Geld kann nicht aufgeteilt werden, weil ein Gegenstand am Cursor ist.", L"Ankunft der neuen Söldner wurde in den Sektor %s verlegt, weil der geplante Sektor %s von Feinden belagert ist.", diff --git a/Utils/_ItalianText.cpp b/Utils/_ItalianText.cpp index 23c72cd3..472c801f 100644 --- a/Utils/_ItalianText.cpp +++ b/Utils/_ItalianText.cpp @@ -6042,6 +6042,9 @@ STR16 New113Message[] = L"GL", L"GL BRST", L"GL AUTO", + L"UB", + L"UBRST", + L"UAUTO", L"Sniper!", L"Unable to split money due to having an item on your cursor.", L"Arrival of new recruits is being rerouted to sector %s, as scheduled drop-off point of sector %s is enemy occupied.", diff --git a/Utils/_PolishText.cpp b/Utils/_PolishText.cpp index e0798512..f1023d45 100644 --- a/Utils/_PolishText.cpp +++ b/Utils/_PolishText.cpp @@ -6048,6 +6048,9 @@ STR16 New113Message[] = L"GL", L"GL BRST", L"GL AUTO", + L"UB", // TODO.Translate + L"UBRST", + L"UAUTO", L"Snajper!", L"Nie można podzielić pieniędzy z powodu przedmiotu na kursorze.", L"Przybycie nowych rekrutów zostało przekierowane do sektora %s , z uwagi na to, że poprzedni punkt zrzutu w sektorze %s jest zajęty przez wroga.", diff --git a/Utils/_RussianText.cpp b/Utils/_RussianText.cpp index f8cdb22b..0f94aa74 100644 --- a/Utils/_RussianText.cpp +++ b/Utils/_RussianText.cpp @@ -6028,6 +6028,9 @@ STR16 New113Message[] = L"ГР", L"ГР *", L"ГР ***", + L"UB", // TODO.Translate + L"UBRST", + L"UAUTO", L"Снайпер!", L"Невозможно разделить деньги из-за предмета на курсоре.", L"Точка высадки новых наемников перенесена в %s, так как предыдущая точка высадки %s захвачена противником.", diff --git a/Utils/_TaiwaneseText.cpp b/Utils/_TaiwaneseText.cpp index 43a40dd8..79a6f548 100644 --- a/Utils/_TaiwaneseText.cpp +++ b/Utils/_TaiwaneseText.cpp @@ -6055,6 +6055,9 @@ STR16 New113Message[] = L"GL", L"GL BRST", L"GL AUTO", + L"UB", + L"UBRST", + L"UAUTO", L"Sniper!", L"Unable to split money due to having an item on your cursor.", L"Arrival of new recruits is being rerouted to sector %s, as scheduled drop-off point of sector %s is enemy occupied.",