diff --git a/GameSettings.cpp b/GameSettings.cpp index eda6084c..dd13592f 100644 --- a/GameSettings.cpp +++ b/GameSettings.cpp @@ -1383,6 +1383,12 @@ void LoadGameExternalOptions() gGameExternalOptions.fAllowDrivingVehiclesInTactical = iniReader.ReadBoolean("Tactical Gameplay Settings","ALLOW_DRIVING_VEHICLES_IN_TACTICAL", TRUE); + gGameExternalOptions.fAllowCarsDrivingOverPeople = iniReader.ReadBoolean("Tactical Gameplay Settings","ALLOW_CARS_DRIVING_OVER_PEOPLE", TRUE); + gGameExternalOptions.fAllowTanksDrivingOverPeople = iniReader.ReadBoolean("Tactical Gameplay Settings","ALLOW_TANKS_DRIVING_OVER_PEOPLE", TRUE); + + gGameExternalOptions.ubCarsRammingMaxStructureArmour = iniReader.ReadInteger("Tactical Gameplay Settings","CARS_RAMMING_MAX_STRUCTURE_ARMOUR", 30, 0, 255); + gGameExternalOptions.ubTanksRammingMaxStructureArmour = iniReader.ReadInteger("Tactical Gameplay Settings","TANKS_RAMMING_MAX_STRUCTURE_ARMOUR", 70, 0, 255); + gGameExternalOptions.ubAPSharedAmongPassengersAndVehicleMode = iniReader.ReadInteger("Tactical Gameplay Settings","AP_SHARED_AMONG_PASSENGERS_AND_VEHICLE_MODE", 3, 0, 3); gGameExternalOptions.ubAPSharedAmongPassengersAndVehicleScale = iniReader.ReadInteger("Tactical Gameplay Settings","AP_SHARED_AMONG_PASSENGERS_AND_VEHICLE_SCALE", 100, 0, 200); diff --git a/GameSettings.h b/GameSettings.h index ff5cca57..c5b2c013 100644 --- a/GameSettings.h +++ b/GameSettings.h @@ -1477,6 +1477,12 @@ typedef struct BOOLEAN fAllowDrivingVehiclesInTactical; + BOOLEAN fAllowCarsDrivingOverPeople; + BOOLEAN fAllowTanksDrivingOverPeople; + + UINT8 ubCarsRammingMaxStructureArmour; + UINT8 ubTanksRammingMaxStructureArmour; + UINT8 ubAPSharedAmongPassengersAndVehicleMode; UINT8 ubAPSharedAmongPassengersAndVehicleScale; diff --git a/Tactical/Handle UI.cpp b/Tactical/Handle UI.cpp index a58102a1..f16ee97a 100644 --- a/Tactical/Handle UI.cpp +++ b/Tactical/Handle UI.cpp @@ -2057,6 +2057,15 @@ UINT32 UIHandleCMoveMerc( UI_EVENT *pUIEvent ) if ( pSoldier->flags.uiStatusFlags & (SOLDIER_DRIVER ) ) { pSoldier = GetSoldierStructureForVehicle( MercPtrs[ pSoldier->ubID ]->iVehicleId ); + // anv: if shift is pressed, treat is as ram + move - flag has to be set for later add structure checks + if ( _KeyDown( SHIFT ) ) + { + pSoldier->usSoldierFlagMask2 |= SOLDIER_RAM_THROUGH_OBSTACLES; + } + else + { + pSoldier->usSoldierFlagMask2 &= ~SOLDIER_RAM_THROUGH_OBSTACLES; + } } // CHRISL: This block should only run if we're running in the new inventory system diff --git a/Tactical/Overhead Types.h b/Tactical/Overhead Types.h index 60c3d20d..53c2d883 100644 --- a/Tactical/Overhead Types.h +++ b/Tactical/Overhead Types.h @@ -186,6 +186,7 @@ enum #define FIRE_WEAPON_DEAFENED 10 #define FIRE_WEAPON_BLINDED_AND_DEAFENED 11 #define FIRE_WEAPON_BLINDED_SPECIAL 12 // Flugente: works like FIRE_WEAPON_BLINDED_BY_SPIT_SPECIAL but without the damage dependency +#define FIRE_WEAPON_VEHICLE_TRAUMA 13 #define NO_INTERRUPTS 0 diff --git a/Tactical/Overhead.cpp b/Tactical/Overhead.cpp index 823f5ab6..610325da 100644 --- a/Tactical/Overhead.cpp +++ b/Tactical/Overhead.cpp @@ -4765,7 +4765,7 @@ CHAR8 *GetSceneFilename( ) return( gzLevelFilenames[ gubCurrentScene ] ); } -extern BOOLEAN InternalOkayToAddStructureToWorld( INT32 sBaseGridNo, INT8 bLevel, DB_STRUCTURE_REF * pDBStructureRef, INT16 sExclusionID, BOOLEAN fIgnorePeople ); +extern BOOLEAN InternalOkayToAddStructureToWorld( INT32 sBaseGridNo, INT8 bLevel, DB_STRUCTURE_REF * pDBStructureRef, INT16 sExclusionID, BOOLEAN fAddingForReal, INT16 sSoldierID ); // NB if making changes don't forget to update NewOKDestinationAndDirection INT16 NewOKDestination( SOLDIERTYPE * pCurrSoldier, INT32 sGridNo, BOOLEAN fPeopleToo, INT8 bLevel ) @@ -4830,7 +4830,15 @@ INT16 NewOKDestination( SOLDIERTYPE * pCurrSoldier, INT32 sGridNo, BOOLEAN fPeop usStructureID = INVALID_STRUCTURE_ID; } - fOk = InternalOkayToAddStructureToWorld( sGridNo, bLevel, &(pStructureFileRef->pDBStructureRef[bLoop]), usStructureID, (BOOLEAN)!fPeopleToo ); + // anv: allow ramming as a mean of getting to destination + if( _KeyDown( SHIFT ) && ( pCurrSoldier->flags.uiStatusFlags & SOLDIER_VEHICLE ) && pCurrSoldier->bTeam == gbPlayerNum ) + { + fOk = InternalOkayToAddStructureToWorld( sGridNo, bLevel, &(pStructureFileRef->pDBStructureRef[bLoop]), VEHICLE_IGNORE_OBSTACLES_STRUCTURE_ID, FALSE, pCurrSoldier->ubID ); + } + else + { + fOk = InternalOkayToAddStructureToWorld( sGridNo, bLevel, &(pStructureFileRef->pDBStructureRef[bLoop]), usStructureID, FALSE, pCurrSoldier->ubID ); + } if (fOk) { return( TRUE ); @@ -4951,7 +4959,7 @@ INT16 NewOKDestinationAndDirection( SOLDIERTYPE * pCurrSoldier, INT32 sGridNo, I usStructureID = pCurrSoldier->pLevelNode->pStructureData->usStructureID; } - fOk = InternalOkayToAddStructureToWorld( sGridNo, pCurrSoldier->pathing.bLevel, &(pStructureFileRef->pDBStructureRef[ gOneCDirection[ bLoop ] ]), usStructureID, (BOOLEAN)!fPeopleToo ); + fOk = InternalOkayToAddStructureToWorld( sGridNo, pCurrSoldier->pathing.bLevel, &(pStructureFileRef->pDBStructureRef[ gOneCDirection[ bLoop ] ]), usStructureID, FALSE, pCurrSoldier->ubID ); if (fOk) { return( TRUE ); diff --git a/Tactical/PATHAI.cpp b/Tactical/PATHAI.cpp index 88bff460..5eb9e686 100644 --- a/Tactical/PATHAI.cpp +++ b/Tactical/PATHAI.cpp @@ -40,6 +40,7 @@ #include "gamesettings.h" #include "Buildings.h" #include "soldier profile.h" // added by SANDRO + #include "Soldier macros.h" #endif #include "connect.h" @@ -2516,6 +2517,7 @@ b=GetJA2Clock();//return s->sGridNo+6; STRUCTURE_FILE_REF * pStructureFileRef=NULL; UINT16 usAnimSurface; //INT32 iCnt2, iCnt3; + BOOLEAN fVehicleIgnoreObstacles = FALSE; #endif INT32 iLastDir = 0; @@ -2628,6 +2630,25 @@ if(!GridNoOnVisibleWorldTile(iDestination)) } } fPathAroundPeople = ( (fFlags & PATH_THROUGH_PEOPLE) == 0 ); + + // anv: vehicles can drive through people and structures if shift is pressed + // (or rather route through can be planned, actual driving through is dependent on SOLDIER_RAM_THROUGH_OBSTACLES flag set in Handle UI) + if ( _KeyDown( SHIFT ) && fPathingForPlayer && ( s->flags.uiStatusFlags & SOLDIER_VEHICLE ) ) + { + fVehicleIgnoreObstacles = TRUE; + } + // AI can't press shift, let's assume only tanks ram through (we wouldn't want AI cars to get destroyed kamikazing through walls, no matter how funny that would be) + else if( !fPathingForPlayer && TANK(s) ) + { + fVehicleIgnoreObstacles = TRUE; + } + if( fVehicleIgnoreObstacles && + ( ( gGameExternalOptions.fAllowCarsDrivingOverPeople && !TANK(s) ) || + ( gGameExternalOptions.fAllowTanksDrivingOverPeople && TANK(s) ) ) ) + { + fPathAroundPeople = FALSE; + } + fCloseGoodEnough = ( (fFlags & PATH_CLOSE_GOOD_ENOUGH) != 0); if ( fCloseGoodEnough ) { @@ -2758,6 +2779,10 @@ if(!GridNoOnVisibleWorldTile(iDestination)) { usOKToAddStructID = INVALID_STRUCTURE_ID; } + if( fVehicleIgnoreObstacles ) + { + usOKToAddStructID = VEHICLE_IGNORE_OBSTACLES_STRUCTURE_ID; + } } else @@ -3006,7 +3031,7 @@ if(!GridNoOnVisibleWorldTile(iDestination)) { if ( iCnt != iLastDir ) { - if ( !OkayToAddStructureToWorld( curLoc, ubLevel, &(pStructureFileRef->pDBStructureRef[ iStructIndex ]), usOKToAddStructID ) ) + if ( !OkayToAddStructureToWorld( curLoc, ubLevel, &(pStructureFileRef->pDBStructureRef[ iStructIndex ]), usOKToAddStructID, FALSE, s->ubID ) ) { // we have to abort this loop and possibly reset the loop conditions to // search in the other direction (if we haven't already done the other dir) @@ -3126,7 +3151,7 @@ if(!GridNoOnVisibleWorldTile(iDestination)) } //how much is admission to the next tile - if ( gfPathAroundObstacles ) + if ( gfPathAroundObstacles && !fVehicleIgnoreObstacles ) { nextCost = gubWorldMovementCosts[ newLoc ][ iCnt ][ ubLevel ]; @@ -3351,6 +3376,10 @@ if(!GridNoOnVisibleWorldTile(iDestination)) goto NEXTDIR; } + else if( fVehicleIgnoreObstacles ) + { + nextCost = TRAVELCOST_FLAT; + } else { nextCost = TRAVELCOST_FLAT; @@ -3394,7 +3423,7 @@ if(!GridNoOnVisibleWorldTile(iDestination)) // then 0 1 2 3 4 5 6), we must subtract 1 from the direction // ATE: Send in our existing structure ID so it's ignored! - if (!OkayToAddStructureToWorld( newLoc, ubLevel, &(pStructureFileRef->pDBStructureRef[ iStructIndex ]), usOKToAddStructID ) ) + if (!OkayToAddStructureToWorld( newLoc, ubLevel, &(pStructureFileRef->pDBStructureRef[ iStructIndex ]), usOKToAddStructID, FALSE, s->ubID ) ) { goto NEXTDIR; } diff --git a/Tactical/Soldier Ani.cpp b/Tactical/Soldier Ani.cpp index 13e2cd15..012c4678 100644 --- a/Tactical/Soldier Ani.cpp +++ b/Tactical/Soldier Ani.cpp @@ -3868,8 +3868,9 @@ BOOLEAN HandleSoldierDeath( SOLDIERTYPE *pSoldier , BOOLEAN *pfMadeCorpse ) ////////////////////////////////////////////////////////////// { + // anv: note that ubAttacker can be already different from pSoldier->ubAttackerID // IF this guy has an attacker and he's a good guy, play sound - if ( ubAttacker != NOBODY ) + if ( pSoldier->ubAttackerID != NOBODY ) { if ( MercPtrs[ pSoldier->ubAttackerID ] != NULL && MercPtrs[ pSoldier->ubAttackerID ]->bTeam == gbPlayerNum && gTacticalStatus.ubAttackBusyCount > 0 ) { diff --git a/Tactical/Soldier Control.cpp b/Tactical/Soldier Control.cpp index ddf5ff44..31222925 100644 --- a/Tactical/Soldier Control.cpp +++ b/Tactical/Soldier Control.cpp @@ -1699,6 +1699,7 @@ void SoldierGotHitGunFire( SOLDIERTYPE *pSoldier, UINT16 usWeaponIndex, INT16 sD void SoldierGotHitBlade( SOLDIERTYPE *pSoldier, UINT16 usWeaponIndex, INT16 sDamage, UINT16 bDirection, UINT16 sRange, UINT8 ubAttackerID, UINT8 ubSpecial, UINT8 ubHitLocation ); void SoldierGotHitPunch( SOLDIERTYPE *pSoldier, UINT16 usWeaponIndex, INT16 sDamage, UINT16 bDirection, UINT16 sRange, UINT8 ubAttackerID, UINT8 ubSpecial, UINT8 ubHitLocation ); void SoldierGotHitExplosion( SOLDIERTYPE *pSoldier, UINT16 usWeaponIndex, INT16 sDamage, UINT16 bDirection, UINT16 sRange, UINT8 ubAttackerID, UINT8 ubSpecial, UINT8 ubHitLocation ); +void SoldierGotHitVehicle( SOLDIERTYPE *pSoldier, UINT16 usWeaponIndex, INT16 sDamage, UINT16 bDirection, UINT16 sRange, UINT8 ubAttackerID, UINT8 ubSpecial, UINT8 ubHitLocation ); UINT8 CalcScreamVolume( SOLDIERTYPE * pSoldier, UINT8 ubCombinedLoss ); void PlaySoldierFootstepSound( SOLDIERTYPE *pSoldier ); void HandleSystemNewAISituation( SOLDIERTYPE *pSoldier, BOOLEAN fResetABC ); @@ -5721,6 +5722,10 @@ void SOLDIERTYPE::EVENT_SoldierGotHit( UINT16 usWeaponIndex, INT16 sDamage, INT1 { ubReason = TAKE_DAMAGE_TENTACLES; } + else if ( ubSpecial == FIRE_WEAPON_VEHICLE_TRAUMA ) + { + ubReason = TAKE_DAMAGE_VEHICLE_TRAUMA; + } // marke take out gunfire if ammotype is explosive // callahan update start @@ -6092,6 +6097,13 @@ void SOLDIERTYPE::EVENT_SoldierGotHit( UINT16 usWeaponIndex, INT16 sDamage, INT1 } } + // anv: soldier got rammed by vehicle + if ( ubSpecial == FIRE_WEAPON_VEHICLE_TRAUMA ) + { + SoldierGotHitVehicle( this, usWeaponIndex, sDamage, bDirection, sRange, ubAttackerID, ubSpecial, ubHitLocation ); + return; + } + // CHECK FOR DOING HIT WHILE DOWN if ( ( gAnimControl[ this->usAnimState ].uiFlags & ANIM_HITSTOP ) ) { @@ -6811,6 +6823,70 @@ void SoldierGotHitPunch( SOLDIERTYPE *pSoldier, UINT16 usWeaponIndex, INT16 sDam } +void SoldierGotHitVehicle( SOLDIERTYPE *pSoldier, UINT16 usWeaponIndex, INT16 sDamage, UINT16 bDirection, UINT16 sRange, UINT8 ubAttackerID, UINT8 ubSpecial, UINT8 ubHitLocation ) +{ + INT32 sNewGridNo = 0; + // IF HERE AND GUY IS DEAD, RETURN! + if ( pSoldier->flags.uiStatusFlags & SOLDIER_DEAD ) + { + return; + } + + if( pSoldier->flags.fTryingToFall ) + { + return; + } + + switch ( gAnimControl[ pSoldier->usAnimState ].ubEndHeight ) + { + case ANIM_STAND: + + sNewGridNo = NewGridNo( pSoldier->sGridNo, DirectionInc( bDirection ) );//DirectionInc( gOppositeDirection[ bDirection ] ) ); + if( IS_MERC_BODY_TYPE( pSoldier ) && OKFallDirection( pSoldier, sNewGridNo, pSoldier->pathing.bLevel, bDirection, FLYBACK_HIT ) ) + { + pSoldier->EVENT_SetSoldierDirection( (INT8)gOppositeDirection[ bDirection ] ); + pSoldier->EVENT_SetSoldierDesiredDirection( pSoldier->ubDirection ); + pSoldier->ChangeToFallbackAnimation( (UINT8)gOppositeDirection[ bDirection ] ); + } + else if( IS_MERC_BODY_TYPE( pSoldier ) ) + { + pSoldier->EVENT_SetSoldierDirection( bDirection ); + pSoldier->EVENT_SetSoldierDesiredDirection( pSoldier->ubDirection ); + pSoldier->BeginTyingToFall( ); + pSoldier->EVENT_InitNewSoldierAnim( FALLFORWARD_FROMHIT_STAND, 0, FALSE ); + } + else + { + SoldierCollapse( pSoldier ); + } + break; + + + case ANIM_CROUCH: + + pSoldier->EVENT_SetSoldierDirection( (INT8)gOppositeDirection[ bDirection ] ); + pSoldier->EVENT_SetSoldierDesiredDirection( pSoldier->ubDirection ); + + // Check behind us! + sNewGridNo = NewGridNo( pSoldier->sGridNo, DirectionInc( bDirection ) ); + + if( IS_MERC_BODY_TYPE( pSoldier ) && OKFallDirection( pSoldier, sNewGridNo, pSoldier->pathing.bLevel, gOppositeDirection[ pSoldier->ubDirection ], FLYBACK_HIT ) ) + { + pSoldier->ChangeToFallbackAnimation( (UINT8)gOppositeDirection[ bDirection ] ); + } + else + { + SoldierCollapse( pSoldier ); + } + break; + + case ANIM_PRONE: + + SoldierCollapse( pSoldier ); + break; + } + +} BOOLEAN SOLDIERTYPE::EVENT_InternalGetNewSoldierPath( INT32 sDestGridNo, UINT16 usMovementAnim, BOOLEAN fFromUI, BOOLEAN fForceRestartAnim ) { @@ -9464,7 +9540,40 @@ void SOLDIERTYPE::BeginSoldierGetup( void ) #endif if ( this->bCollapsed ) { - if ( this->stats.bLife >= OKLIFE && this->bBreath >= OKBREATH && (this->bSleepDrugCounter == 0) ) + // anv: only get up if we're not blocked by anything (like vehicle) + BOOLEAN fEnoughPlace = TRUE; + STRUCTURE_FILE_REF *pStructureFileRef; + if ( IS_MERC_BODY_TYPE( this ) ) + { + switch( this->usAnimState ) + { + case FALLOFF_FORWARD_STOP: + case PRONE_LAYFROMHIT_STOP: + case STAND_FALLFORWARD_STOP: + pStructureFileRef = GetAnimationStructureRef( this->ubID, DetermineSoldierAnimationSurface( this, ANIM_CROUCH ), ANIM_CROUCH ); + break; + + case FALLBACKHIT_STOP: + case FALLOFF_STOP: + case FLYBACKHIT_STOP: + case FALLBACK_HIT_STAND: + case FALLOFF: + case FLYBACK_HIT: + pStructureFileRef = GetAnimationStructureRef( this->ubID, DetermineSoldierAnimationSurface( this, ROLLOVER ), ROLLOVER ); + break; + + default: + pStructureFileRef = GetAnimationStructureRef( this->ubID, DetermineSoldierAnimationSurface( this, ANIM_CROUCH ), ANIM_CROUCH ); + break; + } + fEnoughPlace = OkayToAddStructureToWorld( this->sGridNo, this->pathing.bLevel, &( pStructureFileRef->pDBStructureRef[ gOneCDirection[ this->ubDirection ] ] ), this->ubID, FALSE, NOBODY ); + } + else + { + pStructureFileRef = GetAnimationStructureRef( this->ubID, DetermineSoldierAnimationSurface( this, END_COWER ), END_COWER ); + fEnoughPlace = OkayToAddStructureToWorld( this->sGridNo, this->pathing.bLevel, &( pStructureFileRef->pDBStructureRef[ gOneCDirection[ this->ubDirection ] ] ), this->ubID, FALSE, NOBODY ); + } + if ( this->stats.bLife >= OKLIFE && this->bBreath >= OKBREATH && (this->bSleepDrugCounter == 0) && fEnoughPlace ) { // get up you hoser! @@ -15747,6 +15856,9 @@ BOOLEAN SOLDIERTYPE::RecognizeAsCombatant(UINT8 ubTargetID) if (!gGameOptions.fNewTraitSystem) return TRUE; + if ( ubTargetID == NOBODY ) + return TRUE; + SOLDIERTYPE* pSoldier = MercPtrs[ubTargetID]; if ( !pSoldier ) diff --git a/Tactical/Soldier Control.h b/Tactical/Soldier Control.h index 80296052..7c3499f0 100644 --- a/Tactical/Soldier Control.h +++ b/Tactical/Soldier Control.h @@ -58,6 +58,7 @@ extern UINT16 CivLastNames[MAXCIVLASTNAMES][10]; #define TAKE_DAMAGE_TENTACLES 9 #define TAKE_DAMAGE_STRUCTURE_EXPLOSION 10 #define TAKE_DAMAGE_OBJECT 11 +#define TAKE_DAMAGE_VEHICLE_TRAUMA 12 #define SOLDIER_UNBLIT_SIZE (75*75*2) @@ -397,6 +398,7 @@ enum // ------------------- more flags for soldiers -------------------- #define SOLDIER_SNITCHING_OFF 0x00000001 //1 // isn't allowed to snitch #define SOLDIER_PREVENT_MISBEHAVIOUR_OFF 0x00000002 //2 // isn't allowed to prevent misbehaviour +#define SOLDIER_RAM_THROUGH_OBSTACLES 0x00000004 //4 // vehicle // ---------------------------------------------------------------- // -------- added by Flugente: background property flags -------- diff --git a/Tactical/Soldier Tile.cpp b/Tactical/Soldier Tile.cpp index 58727f43..ff995015 100644 --- a/Tactical/Soldier Tile.cpp +++ b/Tactical/Soldier Tile.cpp @@ -48,6 +48,7 @@ #include "message.h" #include "Text.h" #include "NPC.h" + #include "Soldier macros.h" #endif extern INT8 gbNumMercsUntilWaitingOver; @@ -222,6 +223,16 @@ INT8 TileIsClear( SOLDIERTYPE *pSoldier, INT8 bDirection, INT32 sGridNo, INT8 b return( MOVE_TILE_CLEAR ); } + // anv: vehicles can ram people + if ( !TANK(pSoldier) && gGameExternalOptions.fAllowCarsDrivingOverPeople && pSoldier->flags.uiStatusFlags & SOLDIER_VEHICLE && pSoldier->usSoldierFlagMask2 & SOLDIER_RAM_THROUGH_OBSTACLES ) + { + return( MOVE_TILE_CLEAR ); + } + else if( TANK(pSoldier) && gGameExternalOptions.fAllowTanksDrivingOverPeople && pSoldier->flags.uiStatusFlags & SOLDIER_VEHICLE ) + { + return( MOVE_TILE_CLEAR ); + } + ubPerson = WhoIsThere2( sGridNo, bLevel ); diff --git a/Tactical/opplist.cpp b/Tactical/opplist.cpp index 92e64317..ad8f9023 100644 --- a/Tactical/opplist.cpp +++ b/Tactical/opplist.cpp @@ -451,7 +451,7 @@ void ReevaluateBestSightingPosition( SOLDIERTYPE * pSoldier, INT8 bInterruptDuel { for ( ubLoop = 0; ubLoop < gubBestToMakeSightingSize; ubLoop++ ) { - if ( pSoldier->RecognizeAsCombatant(gubBestToMakeSighting[ ubLoop ]) && (gubBestToMakeSighting[ ubLoop ] == NOBODY) || (bInterruptDuelPts > MercPtrs[ gubBestToMakeSighting[ ubLoop ] ]->aiData.bInterruptDuelPts ) ) + if ( pSoldier->RecognizeAsCombatant(gubBestToMakeSighting[ ubLoop ]) && (gubBestToMakeSighting[ ubLoop ] == NOBODY) || ( gubBestToMakeSighting[ ubLoop ] != NOBODY && bInterruptDuelPts > MercPtrs[ gubBestToMakeSighting[ ubLoop ] ]->aiData.bInterruptDuelPts ) ) { if ( gubBestToMakeSighting[ gubBestToMakeSightingSize - 1 ] != NOBODY ) { diff --git a/TileEngine/structure.cpp b/TileEngine/structure.cpp index f8f92ddc..0bbafd17 100644 --- a/TileEngine/structure.cpp +++ b/TileEngine/structure.cpp @@ -41,6 +41,13 @@ #include "Tile Animation.h" #include "Explosion Control.h" // added by Flugente + + // anv: for ramming people with vehicles + #include "Soldier macros.h" + #include "Overhead.h" + #include "Soldier Functions.h" + #include "Animation Control.h" + #include "Soldier Ani.h" #endif #ifdef COUNT_PATHS @@ -625,7 +632,7 @@ STRUCTURE * CreateStructureFromDB( DB_STRUCTURE_REF * pDBStructureRef, UINT8 ubT return( pStructure ); } -BOOLEAN OkayToAddStructureToTile( INT32 sBaseGridNo, INT16 sCubeOffset, DB_STRUCTURE_REF * pDBStructureRef, UINT8 ubTileIndex, INT16 sExclusionID, BOOLEAN fIgnorePeople ) +BOOLEAN OkayToAddStructureToTile( INT32 sBaseGridNo, INT16 sCubeOffset, DB_STRUCTURE_REF * pDBStructureRef, UINT8 ubTileIndex, INT16 sExclusionID, BOOLEAN fAddingForReal = FALSE, INT16 sSoldierID = NOBODY ) { // Verifies whether a structure is blocked from being added to the map at a particular point DB_STRUCTURE * pDBStructure; @@ -636,6 +643,18 @@ BOOLEAN OkayToAddStructureToTile( INT32 sBaseGridNo, INT16 sCubeOffset, DB_STRUC INT32 sGridNo; INT32 sOtherGridNo; + BOOLEAN fIgnorePeople = (BOOLEAN)(sExclusionID == IGNORE_PEOPLE_STRUCTURE_ID); + + BOOLEAN fVehicleIgnoreObstacles = (BOOLEAN)(sExclusionID == VEHICLE_IGNORE_OBSTACLES_STRUCTURE_ID); + if( gGameExternalOptions.ubCarsRammingMaxStructureArmour && sSoldierID != NOBODY && MercPtrs[ sSoldierID ]->usSoldierFlagMask2 & SOLDIER_RAM_THROUGH_OBSTACLES ) + { + fVehicleIgnoreObstacles = TRUE; + } + else if( gGameExternalOptions.ubTanksRammingMaxStructureArmour && sSoldierID != NOBODY && TANK( MercPtrs[ sSoldierID ] ) ) + { + fVehicleIgnoreObstacles = TRUE; + } + ppTile = pDBStructureRef->ppTile; #if 0//dnl ch83 080114 sGridNo = sBaseGridNo + ppTile[ubTileIndex]->sPosRelToBase; @@ -666,7 +685,7 @@ BOOLEAN OkayToAddStructureToTile( INT32 sBaseGridNo, INT16 sCubeOffset, DB_STRUC while (pExistingStructure != NULL) { - if (sCubeOffset == pExistingStructure->sCubeOffset) + if (pExistingStructure != NULL && sCubeOffset == pExistingStructure->sCubeOffset) { // CJC: @@ -708,6 +727,70 @@ BOOLEAN OkayToAddStructureToTile( INT32 sBaseGridNo, INT16 sCubeOffset, DB_STRUC } } + if ( fVehicleIgnoreObstacles && !(pExistingStructure->fFlags & STRUCTURE_PASSABLE) && !(ppTile[ubTileIndex]->fFlags & TILE_PASSABLE) ) + { + // anv: drive through people + if ( pExistingStructure->usStructureID < TOTAL_SOLDIERS ) + { + SOLDIERTYPE *pSoldier = MercPtrs[ pExistingStructure->usStructureID ]; + // but not monsters and such (they can't fall down due to lack of animations) + // also make sure AI won't flatten their allies + if( !( pSoldier->flags.uiStatusFlags & ( SOLDIER_VEHICLE | SOLDIER_ROBOT | SOLDIER_MONSTER ) ) && + ( ( !TANK(MercPtrs[ sSoldierID ]) && gGameExternalOptions.fAllowCarsDrivingOverPeople ) || + ( TANK(MercPtrs[ sSoldierID ]) && gGameExternalOptions.fAllowTanksDrivingOverPeople ) ) && + ( MercPtrs[ sSoldierID ]->bTeam == gbPlayerNum || MercPtrs[ sSoldierID ]->bTeam != pSoldier->bTeam ) ) + { + pExistingStructure = pExistingStructure->pNext; + // damage people when driving on them + if( fAddingForReal && ( MercPtrs[ sSoldierID ]->flags.fPastXDest || MercPtrs[ sSoldierID ]->flags.fPastYDest ) ) + { + if( TANK( MercPtrs[ sSoldierID ] ) ) + { + pSoldier->EVENT_SoldierGotHit( 0, Random(10)+5, Random(200)+Random(200), MercPtrs[ sSoldierID ]->ubDirection, 0, sSoldierID, FIRE_WEAPON_VEHICLE_TRAUMA, 0, 0, pSoldier->sGridNo ); + } + else if( gAnimControl[ pSoldier->usAnimState ].ubEndHeight == ANIM_PRONE && MercPtrs[ sSoldierID ]->flags.fUIMovementFast ) + { + pSoldier->EVENT_SoldierGotHit( 0, Random(5), Random(100)+Random(100), MercPtrs[ sSoldierID ]->ubDirection, 0, sSoldierID, FIRE_WEAPON_VEHICLE_TRAUMA, 0, 0, pSoldier->sGridNo ); + } + else + { + pSoldier->EVENT_SoldierGotHit( 0, Random(10)+5, Random(200)+Random(200), MercPtrs[ sSoldierID ]->ubDirection, 0, sSoldierID, FIRE_WEAPON_VEHICLE_TRAUMA, 0, 0, pSoldier->sGridNo ); + } + } + continue; + } + // skip if it's part of the same vehicle + else if( pExistingStructure->usStructureID == sSoldierID ) + { + pExistingStructure = pExistingStructure->pNext; + continue; + } + } + // anv: ram and destroy structures + else + { + // only if structure is weak enough + if( ( !TANK(MercPtrs[ sSoldierID ]) && gubMaterialArmour[ pExistingStructure->pDBStructureRef->pDBStructure->ubArmour ] < gGameExternalOptions.ubCarsRammingMaxStructureArmour ) || + ( TANK(MercPtrs[ sSoldierID ]) && gubMaterialArmour[ pExistingStructure->pDBStructureRef->pDBStructure->ubArmour ] < gGameExternalOptions.ubTanksRammingMaxStructureArmour ) ) + { + // when not just plotting path, really destroy structure + if( fAddingForReal ) + { + INT16 sXPos, sYPos = 0; + ConvertGridNoToXY(pExistingStructure->sGridNo, &sXPos, &sYPos); + DamageStructure( pExistingStructure, 255, STRUCTURE_DAMAGE_VEHICLE_TRAUMA, pExistingStructure->sGridNo, sXPos, sYPos, sSoldierID, 0 ); + return( TRUE ); + } + // else just skip + else + { + pExistingStructure = pExistingStructure->pNext; + continue; + } + } + } + } + // two obstacle structures aren't allowed in the same tile at the same height // ATE: There is more sophisticated logic for mobiles, so postpone this check if mobile.... if ( ( pExistingStructure->fFlags & STRUCTURE_OBSTACLE ) && !( pDBStructure->fFlags & STRUCTURE_MOBILE ) ) @@ -830,6 +913,18 @@ BOOLEAN OkayToAddStructureToTile( INT32 sBaseGridNo, INT16 sCubeOffset, DB_STRUC // ATE: Added check here - UNLESS the part we are trying to add is PASSABLE! if ( pExistingStructure->fFlags & STRUCTURE_MOBILE && !(pExistingStructure->fFlags & STRUCTURE_PASSABLE) && !(ppTile[ubTileIndex]->fFlags & TILE_PASSABLE) ) { + // anv: check if we try to add soldier hit by a vehicle under vehicle + if( sSoldierID != NOBODY && pExistingStructure->usStructureID < TOTAL_SOLDIERS ) + { + if( MercPtrs[ pExistingStructure->usStructureID ] != NULL && MercPtrs[ pExistingStructure->usStructureID ]->flags.uiStatusFlags & SOLDIER_VEHICLE ) + { + if( MercPtrs[ sSoldierID ]->flags.fInNonintAnim == TRUE || gAnimControl[ MercPtrs[ sSoldierID ]->usAnimState ].ubEndHeight == ANIM_PRONE ) + { + pExistingStructure = pExistingStructure->pNext; + continue; + } + } + } // don't allow 2 people in the same tile return( FALSE ); } @@ -840,7 +935,7 @@ BOOLEAN OkayToAddStructureToTile( INT32 sBaseGridNo, INT16 sCubeOffset, DB_STRUC // Skip! pExistingStructure = pExistingStructure->pNext; continue; - } + } // ATE: Added here - UNLESS this part is PASSABLE.... // two obstacle structures aren't allowed in the same tile at the same height @@ -868,7 +963,7 @@ BOOLEAN OkayToAddStructureToTile( INT32 sBaseGridNo, INT16 sCubeOffset, DB_STRUC return( TRUE ); } -BOOLEAN InternalOkayToAddStructureToWorld( INT32 sBaseGridNo, INT8 bLevel, DB_STRUCTURE_REF * pDBStructureRef, INT16 sExclusionID, BOOLEAN fIgnorePeople ) +BOOLEAN InternalOkayToAddStructureToWorld( INT32 sBaseGridNo, INT8 bLevel, DB_STRUCTURE_REF * pDBStructureRef, INT16 sExclusionID, BOOLEAN fAddingForReal, INT16 sSoldierID ) { UINT8 ubLoop; INT16 sCubeOffset; @@ -903,7 +998,7 @@ BOOLEAN InternalOkayToAddStructureToWorld( INT32 sBaseGridNo, INT8 bLevel, DB_ST { sCubeOffset = bLevel * PROFILE_Z_SIZE; } - if (!OkayToAddStructureToTile( sBaseGridNo, sCubeOffset, pDBStructureRef, ubLoop, sExclusionID, fIgnorePeople )) + if (!OkayToAddStructureToTile( sBaseGridNo, sCubeOffset, pDBStructureRef, ubLoop, sExclusionID, fAddingForReal, sSoldierID )) { return( FALSE ); } @@ -911,9 +1006,9 @@ BOOLEAN InternalOkayToAddStructureToWorld( INT32 sBaseGridNo, INT8 bLevel, DB_ST return( TRUE ); } -BOOLEAN OkayToAddStructureToWorld( INT32 sBaseGridNo, INT8 bLevel, DB_STRUCTURE_REF * pDBStructureRef, INT16 sExclusionID ) +BOOLEAN OkayToAddStructureToWorld( INT32 sBaseGridNo, INT8 bLevel, DB_STRUCTURE_REF * pDBStructureRef, INT16 sExclusionID, BOOLEAN fAddingForReal, INT16 sSoldierID ) { - return( InternalOkayToAddStructureToWorld( sBaseGridNo, bLevel, pDBStructureRef, sExclusionID, (BOOLEAN)(sExclusionID == IGNORE_PEOPLE_STRUCTURE_ID) ) ); + return( InternalOkayToAddStructureToWorld( sBaseGridNo, bLevel, pDBStructureRef, sExclusionID, fAddingForReal, sSoldierID ) ); } BOOLEAN AddStructureToTile( MAP_ELEMENT * pMapElement, STRUCTURE * pStructure, UINT16 usStructureID ) @@ -968,7 +1063,14 @@ STRUCTURE * InternalAddStructureToWorld( INT32 sBaseGridNo, INT8 bLevel, DB_STRU CHECKF( pDBStructure->ubNumberOfTiles > 0 ); // first check to see if the structure will be blocked - if (!OkayToAddStructureToWorld( sBaseGridNo, bLevel, pDBStructureRef, INVALID_STRUCTURE_ID ) ) + if ( ( pLevelNode->uiFlags & LEVELNODE_SOLDIER ) && pLevelNode->pSoldier ) + { + if ( !OkayToAddStructureToWorld( sBaseGridNo, bLevel, pDBStructureRef, INVALID_STRUCTURE_ID, TRUE, pLevelNode->pSoldier->ubID ) ) + { + return( NULL ); + } + } + else if ( !OkayToAddStructureToWorld( sBaseGridNo, bLevel, pDBStructureRef, INVALID_STRUCTURE_ID ) ) { return( NULL ); } @@ -1724,7 +1826,7 @@ BOOLEAN DamageStructure( STRUCTURE * pStructure, UINT8 ubDamage, UINT8 ubReason, } // OK, Let's check our reason - if ( ubReason == STRUCTURE_DAMAGE_GUNFIRE ) + if ( ubReason == STRUCTURE_DAMAGE_GUNFIRE || ubReason == STRUCTURE_DAMAGE_VEHICLE_TRAUMA ) { // If here, we have penetrated, check flags // Are we an explodable structure? @@ -1783,6 +1885,23 @@ BOOLEAN DamageStructure( STRUCTURE * pStructure, UINT8 ubDamage, UINT8 ubReason, gpWorldLevelData[ sGridNo ].uiFlags |= MAPELEMENT_STRUCTURE_DAMAGED; } + // anv: if we ram something with vehicle we have to destroy it, or else vehicle and structure will be drawn at the same tile + // max armour will be set by Player in .ini and checked before DamageStructure is called + if ( ubReason == STRUCTURE_DAMAGE_VEHICLE_TRAUMA ) + { + BOOLEAN recompile = FALSE; + ExplosiveDamageGridNo( sGridNo, 255, 10, &recompile, FALSE, -1, FALSE, ubOwner, 0 ); + + //Since the structure is being damaged, set the map element that a structure is damaged + gpWorldLevelData[ sGridNo ].uiFlags |= MAPELEMENT_STRUCTURE_DAMAGED; + + // handle structure revenge - damage to vehicle + if( ubOwner != NOBODY ) + { + MercPtrs[ ubOwner ]->SoldierTakeDamage( 0, Random(max(0,(ubBaseArmour-10)/5))+max(0,(ubBaseArmour-10)/5), 0, 0, TAKE_DAMAGE_STRUCTURE_EXPLOSION, NOBODY, MercPtrs[ ubOwner ]->sGridNo, 0, TRUE ); + } + } + // Don't update damage HPs.... return TRUE; } diff --git a/TileEngine/structure.h b/TileEngine/structure.h index fe1619f3..9415915e 100644 --- a/TileEngine/structure.h +++ b/TileEngine/structure.h @@ -21,9 +21,11 @@ // 100 == MAX_CORPSES #define INVALID_STRUCTURE_ID ( TOTAL_SOLDIERS + 100 ) #define IGNORE_PEOPLE_STRUCTURE_ID (TOTAL_SOLDIERS+101) +#define VEHICLE_IGNORE_OBSTACLES_STRUCTURE_ID (TOTAL_SOLDIERS+102) #define STRUCTURE_DAMAGE_EXPLOSION 1 #define STRUCTURE_DAMAGE_GUNFIRE 2 +#define STRUCTURE_DAMAGE_VEHICLE_TRAUMA 3 // functions at the structure database level @@ -35,7 +37,7 @@ BOOLEAN FreeStructureFile( STRUCTURE_FILE_REF * pStructureFile ); // // functions at the structure instance level // -BOOLEAN OkayToAddStructureToWorld( INT32 sBaseGridNo, INT8 bLevel, DB_STRUCTURE_REF * pDBStructureRef, INT16 sExclusionID ); +BOOLEAN OkayToAddStructureToWorld( INT32 sBaseGridNo, INT8 bLevel, DB_STRUCTURE_REF * pDBStructureRef, INT16 sExclusionID, BOOLEAN fAddingForReal = FALSE, INT16 sSoldierID = NOBODY ); // for the PTR argument of AddStructureToWorld, pass in a LEVELNODE * please! BOOLEAN AddStructureToWorld( INT32 sBaseGridNo, INT8 bLevel, DB_STRUCTURE_REF * pDBStructureRef, PTR pLevelN ); diff --git a/TileEngine/worldman.cpp b/TileEngine/worldman.cpp index f0944cc2..f9409181 100644 --- a/TileEngine/worldman.cpp +++ b/TileEngine/worldman.cpp @@ -428,6 +428,9 @@ BOOLEAN RemoveAllObjectsOfTypeRange( INT32 iMapIndex, UINT32 fStartType, UINT32 UINT32 fTileType; BOOLEAN fRetVal = FALSE; + if( iMapIndex < 0 ) + return fRetVal; + pObject = gpWorldLevelData[ iMapIndex ].pObjectHead; // Look through all objects and Search for type @@ -2996,6 +2999,9 @@ BOOLEAN RemoveAllOnRoofsOfTypeRange( INT32 iMapIndex, UINT32 fStartType, UINT32 UINT32 fTileType; BOOLEAN fRetVal = FALSE; + if( iMapIndex < 0 ) + return fRetVal; + pOnRoof = gpWorldLevelData[ iMapIndex ].pOnRoofHead; // Look through all OnRoofs and Search for type