diff --git a/GameSettings.cpp b/GameSettings.cpp index b7b62e66..38910358 100644 --- a/GameSettings.cpp +++ b/GameSettings.cpp @@ -1117,6 +1117,8 @@ void LoadGameExternalOptions() gGameExternalOptions.fScopeModes = iniReader.ReadBoolean("Tactical Gameplay Settings","USE_SCOPE_MODES", FALSE); gGameExternalOptions.fDisplayScopeModes = iniReader.ReadBoolean("Tactical Gameplay Settings","DISPLAY_SCOPE_MODES", FALSE); + + gGameExternalOptions.ubExternalFeeding = iniReader.ReadInteger("Tactical Gameplay Settings","EXTERNAL_FEEDING", 2, 0, 2); // WANNE: Externalized grid number of new merc when they arrive with the helicopter (by Jazz) gGameExternalOptions.iInitialMercArrivalLocation = iniReader.ReadInteger("Tactical Gameplay Settings","INITIAL_MERC_ARRIVAL_LOCATION", 4870 ); @@ -1396,15 +1398,15 @@ void LoadGameExternalOptions() //################# Tactical Food Settings ################## gGameExternalOptions.usFoodDigestionHourlyBaseFood = iniReader.ReadInteger("Tactical Food Settings", "FOOD_DIGESTION_HOURLY_BASE_FOOD", 20, 0, 250); gGameExternalOptions.usFoodDigestionHourlyBaseDrink = iniReader.ReadInteger("Tactical Food Settings", "FOOD_DIGESTION_HOURLY_BASE_DRINK", 130, 0, 250); - gGameExternalOptions.sFoodDigestionSleep = iniReader.ReadFloat("Tactical Food Settings", "FOOD_DIGESTION_SLEEP", 0.6, 0.0, 10.0); - gGameExternalOptions.sFoodDigestionTravelVehicle = iniReader.ReadFloat("Tactical Food Settings", "FOOD_DIGESTION_TRAVEL_VEHICLE", 0.8, 0.0, 10.0); - gGameExternalOptions.sFoodDigestionTravel = iniReader.ReadFloat("Tactical Food Settings", "FOOD_DIGESTION_TRAVEL", 1.5, 0.0, 10.0); - gGameExternalOptions.sFoodDigestionAssignment = iniReader.ReadFloat("Tactical Food Settings", "FOOD_DIGESTION_ASSIGNMENT", 0.9, 0.0, 10.0); - gGameExternalOptions.sFoodDigestionOnDuty = iniReader.ReadFloat("Tactical Food Settings", "FOOD_DIGESTION_ONDUTY", 1.0, 0.0, 10.0); - gGameExternalOptions.sFoodDigestionCombat = iniReader.ReadFloat("Tactical Food Settings", "FOOD_DIGESTION_COMBAT", 2.0, 0.0, 10.0); + gGameExternalOptions.sFoodDigestionSleep = iniReader.ReadFloat("Tactical Food Settings", "FOOD_DIGESTION_SLEEP", 0.6f, 0.0f, 10.0f); + gGameExternalOptions.sFoodDigestionTravelVehicle = iniReader.ReadFloat("Tactical Food Settings", "FOOD_DIGESTION_TRAVEL_VEHICLE", 0.8f, 0.0f, 10.0f); + gGameExternalOptions.sFoodDigestionTravel = iniReader.ReadFloat("Tactical Food Settings", "FOOD_DIGESTION_TRAVEL", 1.5f, 0.0f, 10.0f); + gGameExternalOptions.sFoodDigestionAssignment = iniReader.ReadFloat("Tactical Food Settings", "FOOD_DIGESTION_ASSIGNMENT", 0.9f, 0.0f, 10.0f); + gGameExternalOptions.sFoodDigestionOnDuty = iniReader.ReadFloat("Tactical Food Settings", "FOOD_DIGESTION_ONDUTY", 1.0f, 0.0f, 10.0f); + gGameExternalOptions.sFoodDigestionCombat = iniReader.ReadFloat("Tactical Food Settings", "FOOD_DIGESTION_COMBAT", 2.0f, 0.0f, 10.0f); gGameExternalOptions.fFoodDecayInSectors = iniReader.ReadBoolean("Tactical Food Settings", "FOOD_DECAY_IN_SECTORS", TRUE); - gGameExternalOptions.sFoodDecayModificator = iniReader.ReadFloat("Tactical Food Settings", "FOOD_DECAY_MODIFICATOR", 1.0, 0.1, 10.0); + gGameExternalOptions.sFoodDecayModificator = iniReader.ReadFloat("Tactical Food Settings", "FOOD_DECAY_MODIFICATOR", 1.0f, 0.1f, 10.0f); //################# Strategic Gamestart Settings ################## diff --git a/GameSettings.h b/GameSettings.h index 9d58eb97..49b029ee 100644 --- a/GameSettings.h +++ b/GameSettings.h @@ -1135,6 +1135,9 @@ typedef struct // Flugente: Scope Modes BOOLEAN fScopeModes; // allow the player to toggle between existing scopes/sights BOOLEAN fDisplayScopeModes; // allow display of scope modes + + // Flugente: External Feeding + UINT8 ubExternalFeeding; // allow external feeding of guns (0 = no, 1 = only for others, 2 = we can also fed ourselves) BOOLEAN gBriefingRoom; BOOLEAN gEncyclopedia; diff --git a/Tactical/Faces.cpp b/Tactical/Faces.cpp index 88112e8a..675d0b0a 100644 --- a/Tactical/Faces.cpp +++ b/Tactical/Faces.cpp @@ -2302,6 +2302,21 @@ void HandleRenderFaceAdjustments( FACETYPE *pFace, BOOLEAN fDisplayBuffer, BOOLE bNumRightIcons++; } } + // Flugente: are we supplying ammo to someone else? + if ( gGameExternalOptions.ubExternalFeeding > 0 ) + { + UINT8 ubID1 = 0; + UINT16 ubGunSlot1 = 0; + UINT16 ubFaceSlot1 = 0; + UINT8 ubID2 = 0; + UINT16 ubGunSlot2 = 0; + UINT16 ubFaceSlot2 = 0; + if ( MercPtrs[ pFace->ubSoldierID ]->IsFeedingExternal( &ubID1, &ubGunSlot1, &ubFaceSlot1, &ubID2, &ubGunSlot2, &ubFaceSlot2 ) ) + { + DoRightIcon( uiRenderBuffer, pFace, sFaceX, sFaceY, bNumRightIcons, 21 ); + bNumRightIcons++; + } + } switch( pSoldier->bAssignment ) { diff --git a/Tactical/Interface Items.cpp b/Tactical/Interface Items.cpp index 26041175..256946ed 100644 --- a/Tactical/Interface Items.cpp +++ b/Tactical/Interface Items.cpp @@ -4128,6 +4128,38 @@ void INVRenderItem( UINT32 uiBuffer, SOLDIERTYPE * pSoldier, OBJECTTYPE *pObjec mprintf( sNewX, sNewY, pStr ); gprintfinvalidate( sNewX, sNewY, pStr ); } + + // Flugente: if ammo is used to feed a gun externally, show ammo count left on this ammo + if ( gGameExternalOptions.ubExternalFeeding > 0 && (Item[pObject->usItem].usItemClass & (IC_AMMO)) && ObjectIsExternalFeeder(pSoldier, pObject) ) + { + sNewY = sY + sHeight - 10; + + UINT16 usAmmoItem = pObject->usItem; + UINT16 usMagIndex = Item[usAmmoItem].ubClassIndex; + UINT16 usAmmoMagSize = Magazine[usMagIndex].ubMagSize; + UINT8 usAmmoAmmoType = Magazine[usMagIndex].ubAmmoType; + + SetFontForeground ( AmmoTypes[usAmmoAmmoType].fontColour ); + + swprintf( pStr, L"%d", (*pObject)[iter]->data.ubShotsLeft ); + + // Get length of string + uiStringLength=StringPixLength(pStr, ITEM_FONT ); + + sNewX = sX + 1; + //sNewX = sX + sWidth - uiStringLength - 4; + + if ( uiBuffer == guiSAVEBUFFER ) + { + RestoreExternBackgroundRect( sNewX, sNewY, 20, 15 ); + } + mprintf( sNewX, sNewY, pStr ); + gprintfinvalidate( sNewX, sNewY, pStr ); + + //sNewX = sX + 1; + + //SetFontForeground( FONT_MCOLOR_DKGRAY ); + } } } diff --git a/Tactical/Item Types.h b/Tactical/Item Types.h index 0edf7aeb..658ea62c 100644 --- a/Tactical/Item Types.h +++ b/Tactical/Item Types.h @@ -656,7 +656,7 @@ extern OBJECTTYPE gTempObject; #define AC_REMOTEDET 0x00100000 //1048576 // remote detonator for bombs #define AC_DEFUSE 0x00200000 //2097152 // defuse item for bombs #define AC_IRONSIGHT 0x00400000 //4194304 // for attachable Iron Sights -#define AC_MISC12 0x00800000 //8388608 +#define AC_FEEDER 0x00800000 //8388608 // allow external feeding #define AC_MISC13 0x01000000 //16777216 #define AC_MISC14 0x02000000 //33554432 #define AC_MISC15 0x04000000 //67108864 @@ -720,11 +720,11 @@ extern OBJECTTYPE gTempObject; #define WATER_DRUM 0x00000010 //16 // water drums allow to refill canteens in the sector they are in #define MEAT_BLOODCAT 0x00000020 //32 // retrieve this by gutting a bloodcat #define COW_MEAT 0x00000040 //64 // retrieve this by gutting a cow -/*#define WH40K_DISPLACER 0x00000080 //128 +#define BELT_FED 0x00000080 //128 // item can be fed externally -#define WH40K_ROSARIUS 0x00000100 //256 -#define WH40K_SEAL 0x00000200 //512 -#define WH40K_POWER_WEAPON 0x00000400 //1024 +#define AMMO_BELT 0x00000100 //256 // this item can be used to feed externally +#define AMMO_BELT_VEST 0x00000200 //512 // this is a vest that can contain AMMO_BELT items in its medium slots +/*#define unused 0x00000400 //1024 #define ENEMY_NET_4_LVL_3 0x00000800 //2048 #define ENEMY_NET_1_LVL_4 0x00001000 //4096 diff --git a/Tactical/Items.cpp b/Tactical/Items.cpp index fc658763..5b7597f9 100644 --- a/Tactical/Items.cpp +++ b/Tactical/Items.cpp @@ -13981,3 +13981,128 @@ BOOLEAN GetFirstItemWithFlag( UINT16* pusItem, UINT32 aFlag ) return( FALSE ); } + +// Flugente: check if object is currently fed from an external source (belts in inventory, other mercs) +BOOLEAN ObjectIsBeingFedExternal(SOLDIERTYPE* pSoldier, OBJECTTYPE * pObject) +{ + return( GetExternalFeedingObject(pSoldier, pObject) != NULL ); +} + +// is this object currently used to feed an externally fed object? This can be in our or someone else's inventory +BOOLEAN ObjectIsExternalFeeder(SOLDIERTYPE* pSoldier, OBJECTTYPE * pObject) +{ + if ( !pSoldier || !pObject) + return( FALSE ); + + UINT8 usSoldierFeedingTarget1 = 0; + UINT16 usGunSlot1 = 0; + UINT16 usAmmoSlot1 = 0; + UINT8 usSoldierFeedingTarget2 = 0; + UINT16 usGunSlot2 = 0; + UINT16 usAmmoSlot2 = 0; + if ( pSoldier->IsFeedingExternal(&usSoldierFeedingTarget1, &usGunSlot1, &usAmmoSlot1, &usSoldierFeedingTarget2, &usGunSlot2, &usAmmoSlot2) ) + { + SOLDIERTYPE* pTargetSoldier = MercPtrs[usSoldierFeedingTarget1]; + + if ( pTargetSoldier && &(pSoldier->inv[usAmmoSlot1]) == pObject ) + return( TRUE ); + + pTargetSoldier = MercPtrs[usSoldierFeedingTarget2]; + + if ( pTargetSoldier && &(pSoldier->inv[usAmmoSlot2]) == pObject ) + return( TRUE ); + } + + return( FALSE ); +} + +OBJECTTYPE* GetExternalFeedingObject(SOLDIERTYPE* pSoldier, OBJECTTYPE * pObject) +{ + OBJECTTYPE* pObjExtMag = NULL; + + if ( !pObject || !(pObject->exists()) || !pSoldier || !pSoldier->bActive || !pSoldier->bInSector || pSoldier->stats.bLife < OKLIFE ) + // how did we even get here? + return false; + + UINT16 usItem = pObject->usItem; + + // if item is a gun that can be belt fed and still has shots left + if ( Item[ usItem ].usItemClass == IC_GUN && ( HasItemFlag( usItem, BELT_FED ) || HasAttachmentOfClass(pObject, AC_FEEDER) ) && (*pObject)[0]->data.gun.ubGunShotsLeft > 0 ) + { + // remember the caliber, magsize (TODO: really?) and type of ammo. They all have to fit + UINT8 ubCalibre = Weapon[usItem].ubCalibre; + UINT16 ubMagSize = Weapon[usItem].ubMagSize; + UINT8 ubAmmoType = ubAmmoType = (*pObject)[0]->data.gun.ubGunAmmoType; + + // now we now that this gun CAN be belt fed in the current situation. We now have to check if it IS + // we will first check for other mercs who might feed us. Afterwards we look into our own inventory + + // loop over other members of our team in this sector. This includes ourself, as our gun can be fed from a belt in our inventory + SOLDIERTYPE* pTeamSoldier = NULL; + INT32 cnt = gTacticalStatus.Team[ pSoldier->bTeam ].bFirstID; + INT32 lastid = gTacticalStatus.Team[ pSoldier->bTeam ].bLastID; + for ( pTeamSoldier = MercPtrs[ cnt ]; cnt < lastid; ++cnt, ++pTeamSoldier) + { + // check if teamsoldier exists in this sector + if ( !pTeamSoldier || !pTeamSoldier->bActive || !pTeamSoldier->bInSector || pTeamSoldier->stats.bLife < OKLIFE || pTeamSoldier->sSectorX != pSoldier->sSectorX || pTeamSoldier->sSectorY != pSoldier->sSectorY || pTeamSoldier->bSectorZ != pSoldier->bSectorZ ) + continue; + + // check if both soldiers are on the same level + if ( pSoldier->pathing.bLevel != pTeamSoldier->pathing.bLevel ) + continue; + + // we check if that guy is feeding someone, and that someone is really us + UINT8 usTeamSoldierFeedingTarget1 = 0; + UINT16 usGunSlot1 = 0; + UINT16 usAmmoSlot1 = 0; + UINT8 usTeamSoldierFeedingTarget2 = 0; + UINT16 usGunSlot2 = 0; + UINT16 usAmmoSlot2 = 0; + if ( pTeamSoldier->IsFeedingExternal(&usTeamSoldierFeedingTarget1, &usGunSlot1, &usAmmoSlot1, &usTeamSoldierFeedingTarget2, &usGunSlot2, &usAmmoSlot2) ) + { + if ( usTeamSoldierFeedingTarget1 == pSoldier->ubID && pSoldier->inv[usGunSlot1] == (*pObject) ) + { + if ( pTeamSoldier->inv[usAmmoSlot1].exists() && Item [ pTeamSoldier->inv[usAmmoSlot1].usItem ].usItemClass != IC_AMMO || pTeamSoldier->inv[usAmmoSlot1][0]->data.ubShotsLeft > 0 ) + { + pObjExtMag = &(pTeamSoldier->inv[usAmmoSlot1]); + return( pObjExtMag ); + } + } + + if ( usTeamSoldierFeedingTarget2 == pSoldier->ubID && pSoldier->inv[usGunSlot2] == (*pObject) ) + { + if ( pTeamSoldier->inv[usAmmoSlot2].exists() && Item [ pTeamSoldier->inv[usAmmoSlot2].usItem ].usItemClass != IC_AMMO || pTeamSoldier->inv[usAmmoSlot2][0]->data.ubShotsLeft > 0 ) + { + pObjExtMag = &(pTeamSoldier->inv[usAmmoSlot2]); + return( pObjExtMag ); + } + } + } + } + } + + return( pObjExtMag ); +} + +BOOLEAN DeductBulletViaExternalFeeding(SOLDIERTYPE* pSoldier, OBJECTTYPE * pObject) +{ + if ( !pObject || !(pObject->exists()) || !pSoldier || !pSoldier->bActive || !pSoldier->bInSector ) + // how did we even get here? + return false; + + OBJECTTYPE* pObjExtMag = GetExternalFeedingObject(pSoldier, pObject); + + if ( pObjExtMag && (*pObjExtMag)[0]->data.ubShotsLeft != 0 ) + { + (*pObjExtMag)[0]->data.ubShotsLeft--; + + if ( (*pObjExtMag)[0]->data.ubShotsLeft == 0 ) + { + DeleteObj( pObjExtMag ); + } + + return( TRUE ); + } + + return( FALSE ); +} \ No newline at end of file diff --git a/Tactical/Items.h b/Tactical/Items.h index c7bdc422..620a9254 100644 --- a/Tactical/Items.h +++ b/Tactical/Items.h @@ -468,4 +468,14 @@ BOOLEAN HasItemFlag( UINT16 usItem, UINT32 aFlag ); // Flugente: get first item number that has this flag. Use with caution, as we search in all items BOOLEAN GetFirstItemWithFlag( UINT16* pusItem, UINT32 aFlag ); +// Flugente: check if this object is currently fed from an external source (belts in inventory, other mercs) +BOOLEAN ObjectIsBeingFedExternal(SOLDIERTYPE* pSoldier, OBJECTTYPE * pObject); + +// is this object currently used to feed an externally fed object? This can be in our or someone else's inventory +BOOLEAN ObjectIsExternalFeeder(SOLDIERTYPE* pSoldier, OBJECTTYPE * pObject); + +OBJECTTYPE* GetExternalFeedingObject(SOLDIERTYPE* pSoldier, OBJECTTYPE * pObject); + +BOOLEAN DeductBulletViaExternalFeeding(SOLDIERTYPE* pSoldier, OBJECTTYPE * pObject); + #endif diff --git a/Tactical/Points.cpp b/Tactical/Points.cpp index f4bdabbf..3406baf2 100644 --- a/Tactical/Points.cpp +++ b/Tactical/Points.cpp @@ -2464,11 +2464,27 @@ void DeductAmmo( SOLDIERTYPE *pSoldier, INT8 bInvPos ) // 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 ) + // Flugente: external feeding allows us to take ammo from somewhere other than our magazine, like a belt in our inventory our even another mercs + if ( gGameExternalOptions.ubExternalFeeding > 0 ) { - (*pObjUsed)[0]->data.gun.ubGunShotsLeft--; - } + if ( !DeductBulletViaExternalFeeding(pSoldier, pObj) ) + { + // bullet couldn't be fed externally -> remove bullet from the weapon's magazine + // OK, let's see, don't overrun... + if ( (*pObjUsed)[0]->data.gun.ubGunShotsLeft != 0 ) + { + (*pObjUsed)[0]->data.gun.ubGunShotsLeft--; + } + } + } + else + { + // OK, let's see, don't overrun... + if ( (*pObjUsed)[0]->data.gun.ubGunShotsLeft != 0 ) + { + (*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 ) { diff --git a/Tactical/Soldier Control.cpp b/Tactical/Soldier Control.cpp index 5e09deb5..498a3c73 100644 --- a/Tactical/Soldier Control.cpp +++ b/Tactical/Soldier Control.cpp @@ -13696,6 +13696,276 @@ void SOLDIERTYPE::SoldierInventoryFoodDecay(void) } } +// Flugente: do we currently provide ammo (pAmmoSlot) for someone else's (pubId) gun (pGunSlot)? +BOOLEAN SOLDIERTYPE::IsFeedingExternal(UINT8* pubId1, UINT16* pGunSlot1, UINT16* pAmmoSlot1, UINT8* pubId2, UINT16* pGunSlot2, UINT16* pAmmoSlot2) +{ + // make sure we have to check this... + if ( gGameExternalOptions.ubExternalFeeding == 0 ) + return( FALSE ); + + // basic check if are up to this task + if ( !this->bActive || !this->bInSector || this->stats.bLife < OKLIFE ) + return( FALSE ); + + BOOLEAN isFeeding = FALSE; + + UINT16 usGunItem = 0; + UINT8 usGunCalibre = 0; + //UINT16 usGunMagSize = 0; + UINT8 usGunAmmoType = 0; + + UINT16 usAmmoItem = 0; + UINT8 usAmmoCalibre = 0; + //UINT16 usAmmoMagSize = 0; + UINT8 usAmmoAmmoType = 0; + + UINT16 usMagIndex = 0; + + BOOLEAN firstgunfound = FALSE; + + // do this check for both hands + UINT16 firstslot = HANDPOS; + UINT16 lastslot = SECONDHANDPOS; + for (UINT16 invpos = firstslot; invpos <= lastslot; ++invpos) + { + // do we have ammo in our hands? + OBJECTTYPE* pAmmoObj = &(this->inv[invpos]); + + if ( !pAmmoObj || !(pAmmoObj->exists()) || Item [ pAmmoObj->usItem ].usItemClass != IC_AMMO || (*pAmmoObj)[0]->data.ubShotsLeft <= 0 ) + // can't use this, end + continue; + + usAmmoItem = pAmmoObj->usItem; + + if ( !HasItemFlag( usAmmoItem, AMMO_BELT ) ) + continue; + + usMagIndex = Item[usAmmoItem].ubClassIndex; + + usAmmoCalibre = Magazine[usMagIndex].ubCalibre; + //usAmmoMagSize = Magazine[usMagIndex].ubMagSize; + usAmmoAmmoType = Magazine[usMagIndex].ubAmmoType; + + // our current stance is important + UINT8 usOurStance = gAnimControl[ this->usAnimState ].ubEndHeight; + + // we will check wether one of our teammates is on the gridno we face + INT32 nextGridNoinSight = NewGridNo( this->sGridNo, DirectionInc( this->ubDirection ) ); + + SOLDIERTYPE* pTeamSoldier = NULL; + INT32 cnt = gTacticalStatus.Team[ this->bTeam ].bFirstID; + INT32 lastid = gTacticalStatus.Team[ this->bTeam ].bLastID; + for ( pTeamSoldier = MercPtrs[ cnt ]; cnt < lastid; ++cnt, ++pTeamSoldier) + { + // we ourselves don't count, we can't face ourselves + //if ( cnt == this->ubID ) + //continue; + + // check if teamsoldier exists in this sector + if ( !pTeamSoldier || !pTeamSoldier->bActive || !pTeamSoldier->bInSector || pTeamSoldier->sSectorX != this->sSectorX || pTeamSoldier->sSectorY != this->sSectorY || pTeamSoldier->bSectorZ != this->bSectorZ ) + continue; + + // check if both soldiers are on the same level + if ( this->pathing.bLevel != pTeamSoldier->pathing.bLevel ) + continue; + + // determine wether we can physically provide ammo to our teammate. + // check the stance, prone on standing (both ways) doesn't work + if ( usOurStance == ANIM_STAND ) + { + if ( gAnimControl[ pTeamSoldier->usAnimState ].ubEndHeight != ANIM_STAND && gAnimControl[ pTeamSoldier->usAnimState ].ubEndHeight != ANIM_CROUCH ) + continue; + } + else if ( usOurStance == ANIM_PRONE ) + { + if ( gAnimControl[ pTeamSoldier->usAnimState ].ubEndHeight != ANIM_PRONE && gAnimControl[ pTeamSoldier->usAnimState ].ubEndHeight != ANIM_CROUCH ) + continue; + } + + // check if we look at our teammate, or look the same way he does, or in the direction between + BOOLEAN fPositioningOkay = FALSE; + // the other person must be near + if ( SpacesAway(this->sGridNo, pTeamSoldier->sGridNo) == 0 ) + { + // same tile -> its ourself -> ok + fPositioningOkay = TRUE; + } + else if ( SpacesAway(this->sGridNo, pTeamSoldier->sGridNo) == 1 ) + { + // we look at him -> ok + if ( nextGridNoinSight == pTeamSoldier->sGridNo ) + fPositioningOkay = TRUE; + else + { + // if we look at the same tile, then that's okay too + INT32 teamsoldiernextGridNoinSight = NewGridNo( pTeamSoldier->sGridNo, DirectionInc( pTeamSoldier->ubDirection ) ); + + if ( nextGridNoinSight == teamsoldiernextGridNoinSight ) + fPositioningOkay = TRUE; + else + { + // if we both look in the same direction... + INT8 teammatedirection = pTeamSoldier->ubDirection; + INT8 ourdirection = this->ubDirection; + + if ( teammatedirection == ourdirection ) + { + // if the angle between our teammates sightline and the direct line from us to him is 90 degrees, then we are also able to supply + INT8 ourrightdirection = (ourdirection + 2) % NUM_WORLD_DIRECTIONS; + INT8 ourleftdirection = (ourdirection - 2) % NUM_WORLD_DIRECTIONS; + + if ( NewGridNo( this->sGridNo, DirectionInc( ourrightdirection ) ) == pTeamSoldier->sGridNo || NewGridNo( this->sGridNo, DirectionInc( ourleftdirection ) ) == pTeamSoldier->sGridNo ) + fPositioningOkay = TRUE; + } + } + } + } + + if ( !fPositioningOkay ) + continue; + + // ok, we are facing a teammate. Check if he has a gun in any hand that still has ammo left + UINT16 pTeamSoldierfirstslot = HANDPOS; + UINT16 pTeamSoldierlastslot = SECONDHANDPOS; + for (UINT16 teamsoldierinvpos = pTeamSoldierfirstslot; teamsoldierinvpos <= pTeamSoldierlastslot; ++teamsoldierinvpos) + { + OBJECTTYPE* pObjInHands = &(pTeamSoldier->inv[teamsoldierinvpos]); + if ( pObjInHands && pObjInHands->exists() && Item [ pObjInHands->usItem ].usItemClass == IC_GUN && (HasItemFlag( pObjInHands->usItem, BELT_FED ) || HasAttachmentOfClass(pObjInHands, AC_FEEDER) ) && (*pObjInHands)[0]->data.gun.ubGunShotsLeft > 0 ) + { + // remember the caliber, magsize (TODO: really?) and type of ammo. They all have to fit + usGunItem = pObjInHands->usItem; + + usGunCalibre = Weapon[usGunItem].ubCalibre; + //usGunMagSize = Weapon[usGunItem].ubMagSize; + usGunAmmoType = (*pObjInHands)[0]->data.gun.ubGunAmmoType; + + if ( usGunCalibre == usAmmoCalibre && /*usGunMagSize == usAmmoMagSize &&*/ usGunAmmoType == usAmmoAmmoType ) + { + // same calibre, same magsize, same ammotype. We can serve this guy + if ( !firstgunfound ) + { + firstgunfound = TRUE; + (*pubId1) = cnt; + (*pGunSlot1) = teamsoldierinvpos; + (*pAmmoSlot1) = invpos; + isFeeding = TRUE; + break; + } + else + { + (*pubId2) = cnt; + (*pGunSlot2) = teamsoldierinvpos; + (*pAmmoSlot2) = invpos; + isFeeding = TRUE; + + // we really found a second gun. we can only serve 2 guns maximum. lets end this + return( isFeeding ); + } + } + } + } + + // if we reach this point, the only soldier that we could reach doesn't fit -> stop this search + //break; + } + } + + // if set to 1, we do not wether we feed ourself from our inventory + if ( gGameExternalOptions.ubExternalFeeding < 2 ) + return( isFeeding ); + + // if we reach this point, we have checked all our teammates, and we do not provide external feeding for any of them + // it is possible that we provide external feeding for OURSELF (think of ammo belts in a dedicated LBE slot, or of a gun that requires a separate energy source) + // first, determine wether we need external feeding for our gun. We do this for both hands, as it is thinkable that someone has 2 one-handed guns with external feeding + + // this determines which slots we'll search for ammo + UINT16 firstslotforammo = MEDPOCK1POS; + UINT16 lastslotforammo = MEDPOCK2POS; + + // for robots and AI-controlled soldiers (who don't have any LBE gear), we put a change in here so that ALL their slots are checked for ammo + if ( this->bTeam != gbPlayerNum || MercPtrs[ this->ubID ]->flags.uiStatusFlags & SOLDIER_ROBOT ) + { + firstslotforammo = HANDPOS; + lastslotforammo = NUM_INV_SLOTS; + } + else + { + // as a merc, the only slots that are valid for external feeding are the 2 medium-sized slots on a vest (because I say so). And that only if the vest is allowed to do that, which we will now check: + if ( !(this->inv[VESTPOCKPOS].exists()) || !HasItemFlag( this->inv[VESTPOCKPOS].usItem, AMMO_BELT_VEST ) ) + return( isFeeding ); + } + + UINT16 searchgunfirstslot = HANDPOS; + UINT16 searchgunlastslot = SECONDHANDPOS; + for (UINT16 invpos = searchgunfirstslot; invpos <= searchgunlastslot; ++invpos) + { + // check our hands for guns + OBJECTTYPE* pObj = &(this->inv[invpos]); + + UINT16 usGunItem = pObj->usItem; + + if ( !pObj || !(pObj->exists()) || Item [usGunItem ].usItemClass != IC_GUN || !(HasItemFlag( usGunItem, BELT_FED ) || HasAttachmentOfClass(pObj, AC_FEEDER) ) || (*pObj)[0]->data.gun.ubGunShotsLeft <= 0 ) + // can't use this, end + continue; + + // remember the caliber, magsize (TODO: really?) and type of ammo. They all have to fit + usGunCalibre = Weapon[usGunItem].ubCalibre; + //usGunMagSize = Weapon[usGunItem].ubMagSize; + usGunAmmoType = (*pObj)[0]->data.gun.ubGunAmmoType; + + // now check the inventory for an ammo belt. If we are not from the player team or a robot, we will search the entire inventory + for ( UINT16 bLoop = firstslotforammo; bLoop <= lastslotforammo; ++bLoop) + { + if (this->inv[bLoop].exists() == true ) + { + OBJECTTYPE * pAmmoObj = &(this->inv[bLoop]); // ... get pointer for this item ... + + if ( pAmmoObj != NULL ) // ... if pointer is not obviously useless ... + { + if ( pAmmoObj->ubNumberOfObjects == 1 ) + { + usAmmoItem = pAmmoObj->usItem; + + if ( Item [ usAmmoItem ].usItemClass == IC_AMMO && HasItemFlag( usAmmoItem, AMMO_BELT ) ) + { + // remember the caliber, magsize (TODO: really?) and type of ammo. They all have to fit + usMagIndex = Item[usAmmoItem].ubClassIndex; + usAmmoCalibre = Magazine[usMagIndex].ubCalibre; + //usAmmoMagSize = Magazine[usMagIndex].ubMagSize; + usAmmoAmmoType = Magazine[usMagIndex].ubAmmoType; + + if ( usGunCalibre == usAmmoCalibre && /*usGunMagSize == usAmmoMagSize &&*/ usGunAmmoType == usAmmoAmmoType ) + { + // same calibre, same magsize, same ammotype. We can serve this guy + if ( !firstgunfound ) + { + firstgunfound = TRUE; + (*pubId1) = this->ubID; + (*pGunSlot1) = invpos; + (*pAmmoSlot1) = bLoop; + isFeeding = TRUE; + break; + } + else + { + (*pubId2) = this->ubID; + (*pGunSlot2) = invpos; + (*pAmmoSlot2) = bLoop; + isFeeding = TRUE; + + // we really found a second gun. we can only serve 2 guns maximum. lets end this + return( isFeeding ); + } + } + } + } + } + } + } + } + + return( isFeeding ); +} INT32 CheckBleeding( SOLDIERTYPE *pSoldier ) { diff --git a/Tactical/Soldier Control.h b/Tactical/Soldier Control.h index 2c8f3596..7cabedc3 100644 --- a/Tactical/Soldier Control.h +++ b/Tactical/Soldier Control.h @@ -1436,6 +1436,9 @@ public: // Flugente: Food decay in inventory void SoldierInventoryFoodDecay( void ); + + // Flugente: do we currently provide ammo (pAmmoSlot) for someone else's (pubId) gun (pGunSlot)? + BOOLEAN IsFeedingExternal(UINT8* pubId1, UINT16* pGunSlot1, UINT16* pAmmoSlot1, UINT8* pubId2, UINT16* pGunSlot2, UINT16* pAmmoSlot2); ////////////////////////////////////////////////////////////////////////////// }; // SOLDIERTYPE;