From dbc852ea74c4e74ca6f90a58d7e835529c4f632a Mon Sep 17 00:00:00 2001 From: Wanne Date: Sat, 21 Jun 2014 10:07:11 +0000 Subject: [PATCH] Vehicle Update: Roadkill with vehicles and ramming (by anv) - new feature: ramming people - set with Tactical Gameplay Settings > ALLOW_CARS_DRIVING_OVER_PEOPLE and Tactical Gameplay Settings > ALLOW_TANKS_DRIVING_OVER_PEOPLE - when shift is pressed planned vehicle route will go through people. Those who will stay on its path will be hit, causing wounds and knock-outs. - new feature: ramming structures - when shift is pressed planned vehicle route will go through obstacles. When vehicle hits the structure on its path it will be destroyed, causing small damage to vehicle too (with exception of tank). Tactical Gameplay Settings > CARS_RAMMING_MAX_STRUCTURE_ARMOUR and Tactical Gameplay Settings > TANKS_RAMMING_MAX_STRUCTURE_ARMOUR settings control how well armoured structure can be driven through and amount of potential damage for vehicle. - see: http://www.ja-galaxy-forum.com/ubbthreads.php/topics/333829/Drivable_Vehicles#Post333829 git-svn-id: https://ja2svn.mooo.com/source/ja2/trunk/GameSource/ja2_v1.13/Build@7284 3b4a5df2-a311-0410-b5c6-a8a6f20db521 --- GameSettings.cpp | 6 ++ GameSettings.h | 6 ++ Tactical/Handle UI.cpp | 9 +++ Tactical/Overhead Types.h | 1 + Tactical/Overhead.cpp | 14 +++- Tactical/PATHAI.cpp | 35 ++++++++- Tactical/Soldier Ani.cpp | 3 +- Tactical/Soldier Control.cpp | 114 ++++++++++++++++++++++++++++- Tactical/Soldier Control.h | 2 + Tactical/Soldier Tile.cpp | 11 +++ Tactical/opplist.cpp | 2 +- TileEngine/structure.cpp | 137 ++++++++++++++++++++++++++++++++--- TileEngine/structure.h | 4 +- TileEngine/worldman.cpp | 6 ++ 14 files changed, 331 insertions(+), 19 deletions(-) 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