From 2e9c6965b07f138112c91a0939faa88e5bacbaf5 Mon Sep 17 00:00:00 2001 From: Flugente Date: Sat, 25 Oct 2014 23:46:45 +0000 Subject: [PATCH] - New Feature: roof tiles can be destroyed. Requires GameDir >= r2174. - Fix: Owner ID of player-requested artillery shells was wrong. git-svn-id: https://ja2svn.mooo.com/source/ja2/trunk/GameSource/ja2_v1.13/Build@7614 3b4a5df2-a311-0410-b5c6-a8a6f20db521 --- GameVersion.cpp | 18 +- Tactical/Handle Items.cpp | 10 +- Tactical/Item Types.h | 2 +- Tactical/Soldier Control.cpp | 2 +- TileEngine/Explosion Control.cpp | 417 ++++++++++++++++++++++++++++++- TileEngine/Explosion Control.h | 5 + TileEngine/SaveLoadMap.cpp | 48 +++- TileEngine/SaveLoadMap.h | 3 + TileEngine/worldman.cpp | 93 ++++++- TileEngine/worldman.h | 8 + 10 files changed, 578 insertions(+), 28 deletions(-) diff --git a/GameVersion.cpp b/GameVersion.cpp index ce8b13b1..1d5d320c 100644 --- a/GameVersion.cpp +++ b/GameVersion.cpp @@ -15,9 +15,9 @@ #ifdef JA2EDITOR #ifdef JA2UB - CHAR16 zVersionLabel[256] = { L"Unfinished Business - Map Editor v1.13.7599 (Development Build)" }; + CHAR16 zVersionLabel[256] = { L"Unfinished Business - Map Editor v1.13.7614 (Development Build)" }; #else - CHAR16 zVersionLabel[256] = { L"Map Editor v1.13.7599 (Development Build)" }; + CHAR16 zVersionLabel[256] = { L"Map Editor v1.13.7614 (Development Build)" }; #endif // ------------------------------ @@ -27,11 +27,11 @@ //DEBUG BUILD VERSION #ifdef JA2UB - CHAR16 zVersionLabel[256] = { L"Debug: Unfinished Business - v1.13.7599 (Development Build)" }; + CHAR16 zVersionLabel[256] = { L"Debug: Unfinished Business - v1.13.7614 (Development Build)" }; #elif defined (JA113DEMO) - CHAR16 zVersionLabel[256] = { L"Debug: JA2 Demo - v1.13.7599 (Development Build)" }; + CHAR16 zVersionLabel[256] = { L"Debug: JA2 Demo - v1.13.7614 (Development Build)" }; #else - CHAR16 zVersionLabel[256] = { L"Debug: v1.13.7599 (Development Build)" }; + CHAR16 zVersionLabel[256] = { L"Debug: v1.13.7614 (Development Build)" }; #endif #elif defined CRIPPLED_VERSION @@ -46,16 +46,16 @@ //RELEASE BUILD VERSION #ifdef JA2UB - CHAR16 zVersionLabel[256] = { L"Release Unfinished Business - v1.13.7599 (Development Build)" }; + CHAR16 zVersionLabel[256] = { L"Release Unfinished Business - v1.13.7614 (Development Build)" }; #elif defined (JA113DEMO) - CHAR16 zVersionLabel[256] = { L"Release JA2 Demo - v1.13.7599 (Development Build)" }; + CHAR16 zVersionLabel[256] = { L"Release JA2 Demo - v1.13.7614 (Development Build)" }; #else - CHAR16 zVersionLabel[256] = { L"Release v1.13.7599 (Development Build)" }; + CHAR16 zVersionLabel[256] = { L"Release v1.13.7614 (Development Build)" }; #endif #endif -CHAR8 czVersionNumber[16] = { "Build 14.10.19" }; //YY.MM.DD +CHAR8 czVersionNumber[16] = { "Build 14.10.26" }; //YY.MM.DD CHAR16 zTrackingNumber[16] = { L"Z" }; // SAVE_GAME_VERSION is defined in header, change it there diff --git a/Tactical/Handle Items.cpp b/Tactical/Handle Items.cpp index 6f01e2f1..bdcd5e99 100644 --- a/Tactical/Handle Items.cpp +++ b/Tactical/Handle Items.cpp @@ -1198,7 +1198,7 @@ INT32 HandleItem( SOLDIERTYPE *pSoldier, INT32 sGridNo, INT8 bLevel, UINT16 usHa // we are at pSoldier->sGridNo and the action takes place at sGridNo // we need to find out in which direction we need to look // sActionGridNo is the gridno we are supposed to sit - ubDirection = GetDirectionToGridNoFromGridNo( sGridNo, pSoldier->sGridNo ); + ubDirection = (UINT8)GetDirectionToGridNoFromGridNo( sGridNo, pSoldier->sGridNo ); sActionGridNo = FindAdjacentGridEx( pSoldier, sGridNo, &ubDirection, &sAdjustedGridNo, FALSE, FALSE ); @@ -5164,9 +5164,7 @@ void TacticalFunctionSelectionMessageBoxCallBack( UINT8 ubExitValue ) { if (gpTempSoldier) { - INT32 nextGridNoinSight = gpTempSoldier->sGridNo; - nextGridNoinSight = NewGridNo( nextGridNoinSight, DirectionInc( gpTempSoldier->ubDirection ) ); - + INT32 nextGridNoinSight = NewGridNo( gpTempSoldier->sGridNo, DirectionInc( gpTempSoldier->ubDirection ) ); INT8 level = gpTempSoldier->bTargetLevel; switch (ubExitValue) @@ -5175,8 +5173,8 @@ void TacticalFunctionSelectionMessageBoxCallBack( UINT8 ubExitValue ) SectorFillCanteens(); break; case 2: - // undisguise or take off custom clothes - Strip(gpTempSoldier); + // undisguise or take off custom clothes + Strip(gpTempSoldier); break; case 3: // clean weapons of selected merc diff --git a/Tactical/Item Types.h b/Tactical/Item Types.h index 18fe1bba..afeb1d73 100644 --- a/Tactical/Item Types.h +++ b/Tactical/Item Types.h @@ -765,7 +765,7 @@ extern OBJECTTYPE gTempObject; #define SIGNAL_SHELL 0x04000000 //67108864 // this is a signal shell that precedes artillery barrages #define POWER_PACK 0x08000000 //134217728 // item continously powers an item it is attached to -//#define WOODENPLANK 0x10000000 //268435456 // wooden planks +#define ROOF_COLLAPSE_ITEM 0x10000000 //268435456 // this item is required in the collapsing of roof tiles. It is used internally and should never be seen by the player #define DISEASEPROTECTION_FACE 0x20000000 //536870912 // this item protects us from getting diseases by human contact if worn in a face slot #define DISEASEPROTECTION_HAND 0x40000000 //1073741824 // this item protects us from getting diseases by human contact if kept in inventory /*#define PLAYER_NET_4_LVL_4 0x80000000 //2147483648*/ diff --git a/Tactical/Soldier Control.cpp b/Tactical/Soldier Control.cpp index bbd1a89b..ab1f40cd 100644 --- a/Tactical/Soldier Control.cpp +++ b/Tactical/Soldier Control.cpp @@ -17935,7 +17935,7 @@ BOOLEAN SOLDIERTYPE::OrderArtilleryStrike( UINT32 usSectorNr, INT32 sTargetGridN // send a signal shell at first. This marks the area that the shells will come in static UINT16 usSignalShellIndex = 1700; if ( HasItemFlag( usSignalShellIndex, SIGNAL_SHELL ) || GetFirstItemWithFlag( &usSignalShellIndex, SIGNAL_SHELL ) ) - ArtilleryStrike( usSignalShellIndex, this->ubID, sStartingGridNo, sTargetGridNo ); + ArtilleryStrike( usSignalShellIndex, this->ubID + 2, sStartingGridNo, sTargetGridNo ); else { ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, New113Message[MSG113_NO_SIGNAL_SHELL] ); diff --git a/TileEngine/Explosion Control.cpp b/TileEngine/Explosion Control.cpp index e02f03c3..f4b5ec6b 100644 --- a/TileEngine/Explosion Control.cpp +++ b/TileEngine/Explosion Control.cpp @@ -240,6 +240,12 @@ void HandleSwitchToOpenFortifiedDoor( UINT32 sGridNo ); void HandleSeeingPowerGenFan( UINT32 sGridNo ); #endif +// Flugente: for roof destruction +extern UINT8 gubMaterialArmour[]; + +// defines how much of a roofs tiles armour partially counts for neighbouring tiles - this is used in calculating the 'structural integrity' of a roof +#define WALLSTRENGTH_NEIGHBOURING_PER_TILE 0.75f + INT32 GetFreeExplosion( void ) { UINT32 uiCount; @@ -929,7 +935,7 @@ BOOLEAN ExplosiveDamageStructureAtGridNo( STRUCTURE * pCurrent, STRUCTURE **ppNe // Use tile database for this as apposed to stuct data RemoveAllStructsOfTypeRange( pBase->sGridNo, FIRSTWALLDECAL, FOURTHWALLDECAL ); RemoveAllStructsOfTypeRange( pBase->sGridNo, FIFTHWALLDECAL, EIGTHWALLDECAL ); - + // Alrighty, now do this // Get orientation // based on orientation, go either x or y dir @@ -2342,6 +2348,24 @@ BOOLEAN ExpAffect( INT32 sBombGridNo, INT32 sGridNo, UINT32 uiDist, UINT16 usIte sStructDmgAmt = sWoundAmt; } + // Flugente: walls are attributed to a gridno. As a result, sometimes the outer wall of a house is located at a gridno that does not 'belong' to the house. + // This gridno will then have no roof over it, which HandleRoofDestruction(..) requires. + // For this reason, we call it also at neighbouring gridnos. + // If the explosion, however, is on a roof itself, no need for these shenanigans. + if ( bLevel ) + { + HandleRoofDestruction( sGridNo, sWoundAmt ); + } + // Only do this at the source of the explosion (uiDist == 0). + else if ( !uiDist ) + { + HandleRoofDestruction( sGridNo, sWoundAmt * 0.75f ); + HandleRoofDestruction( NewGridNo( sGridNo, DirectionInc( NORTH )), sWoundAmt * 0.75f ); + HandleRoofDestruction( NewGridNo( sGridNo, DirectionInc( EAST )), sWoundAmt * 0.75f ); + HandleRoofDestruction( NewGridNo( sGridNo, DirectionInc( WEST )), sWoundAmt * 0.75f ); + HandleRoofDestruction( NewGridNo( sGridNo, DirectionInc( SOUTH )), sWoundAmt * 0.75f ); + } + ExplosiveDamageGridNo( sGridNo, sStructDmgAmt, uiDist, &fRecompileMovementCosts, FALSE, -1, 0 , ubOwner, bLevel ); // ATE: Look for damage to walls ONLY for next two gridnos @@ -2359,7 +2383,6 @@ BOOLEAN ExpAffect( INT32 sBombGridNo, INT32 sGridNo, UINT32 uiDist, UINT16 usIte { ExplosiveDamageGridNo( sNewGridNo, sStructDmgAmt, uiDist, &fRecompileMovementCosts, TRUE, -1, 0, ubOwner, bLevel ); } - } // Add burn marks to ground randomly.... @@ -4049,11 +4072,13 @@ void HandleExplosionQueue( void ) // determine gridno to attack - smoke signal required. Otherwise, it is assumed the radio operator ordered the bombing of his OWN position // if we cannot even find a radio operator, all bets are off - target a random gridno + // the usual +/- 2 shenanigans + UINT16 usOwner = max( 0, (*pObj)[0]->data.misc.ubBombOwner - 2 ); INT32 sTargetGridNo = -1; - if ( GetRandomSignalSmokeGridNo(&sTargetGridNo) || GetRadioOperatorSignal((*pObj)[0]->data.misc.ubBombOwner, &sTargetGridNo) || (sTargetGridNo = RandomGridNo()) ) + if ( GetRandomSignalSmokeGridNo( &sTargetGridNo ) || GetRadioOperatorSignal( usOwner, &sTargetGridNo ) || (sTargetGridNo = RandomGridNo( )) ) { for ( UINT8 i = 0; i < cnt; ++i) - ArtilleryStrike( pObj->usItem, (*pObj)[0]->data.misc.ubBombOwner, sGridNo, sTargetGridNo ); + ArtilleryStrike( pObj->usItem, usOwner, sGridNo, sTargetGridNo ); } // not needed anymore @@ -5617,3 +5642,387 @@ BOOLEAN CheckExplosiveTypeAsDetonator(UINT16 ubType) // attached explosives are allowed only for EXPLOSV_NORMAL, EXPLOSV_STUN and EXPLOSV_FLASHBANG types return ( ubType == EXPLOSV_NORMAL || ubType == EXPLOSV_STUN || ubType == EXPLOSV_FLASHBANG ); } + +typedef std::pair gridnoarmourpair; +typedef std::vector< gridnoarmourpair > gridnoarmourvector; +typedef std::map gridnoarmournetworkmap; + +void SoldierDropThroughRoof( SOLDIERTYPE* pSoldier, INT32 sGridNo ) +{ + if ( !pSoldier || TileIsOutOfBounds( sGridNo ) ) + return; + + pSoldier->SetSoldierHeight( 0.0 ); + TeleportSoldier( pSoldier, sGridNo, TRUE ); + pSoldier->EVENT_StopMerc( pSoldier->sGridNo, pSoldier->ubDirection ); + + // if we play the animation of falling roof tiles over this, it will look like the merc fell really painful (there's even blood) + SoldierCollapse( pSoldier ); + + // take damage + UINT32 damage = 15 + Random( 5 ) + Random( 23 ); + pSoldier->SoldierTakeDamage( ANIM_CROUCH, damage, 0, damage * 100, TAKE_DAMAGE_FALLROOF, NOBODY, NOWHERE, 0, TRUE ); +} + +gridnoarmourvector GetConnectedRoofGridnoArmours( INT32 sGridNo ) +{ + gridnoarmourvector vec; + + // this shouldn't even happen + if ( TileIsOutOfBounds( sGridNo ) || !IsRoofPresentAtGridNo( sGridNo ) ) + return vec; + + UINT8 armour = 0; + STRUCTURE* pStruct = FindStructure( sGridNo, (STRUCTURE_WALL) ); + + if ( pStruct ) + armour = gubMaterialArmour[pStruct->pDBStructureRef->pDBStructure->ubArmour]; + + vec.push_back( std::make_pair( sGridNo, armour ) ); + + // starting from sGridNo, we inspect neighbouring gridnos to detect tiles of the same roof, and detect wether there is a wall below them + // if there is indeed a wall, note its armour level + // if we already intersected a roof by deleting roof tiles, this function will only detect the connected roof part + + UINT32 cnt = 0; + while ( cnt < vec.size( ) ) + { + INT32 currentgridno = vec[cnt++].first; + + // determine all neighbouring gridnos. If they have a roof, add them to the list + { + INT32 nextgridno = currentgridno + 1; + if ( !TileIsOutOfBounds( nextgridno ) && IsRoofPresentAtGridNo( nextgridno ) ) + { + UINT8 armour = 0; + STRUCTURE* pStruct = FindStructure( nextgridno, (STRUCTURE_WALL) ); + + if ( pStruct ) + armour = gubMaterialArmour[pStruct->pDBStructureRef->pDBStructure->ubArmour]; + + gridnoarmourpair pair( nextgridno, armour ); + + if ( std::find( vec.begin( ), vec.end( ), pair ) == vec.end( ) ) + vec.push_back( pair ); + } + } + + { + INT32 nextgridno = currentgridno - 1; + if ( !TileIsOutOfBounds( nextgridno ) && IsRoofPresentAtGridNo( nextgridno ) ) + { + UINT8 armour = 0; + STRUCTURE* pStruct = FindStructure( nextgridno, (STRUCTURE_WALL) ); + + if ( pStruct ) + armour = gubMaterialArmour[pStruct->pDBStructureRef->pDBStructure->ubArmour]; + + std::pair pair( nextgridno, armour ); + + if ( std::find( vec.begin( ), vec.end( ), pair ) == vec.end( ) ) + vec.push_back( pair ); + } + } + + { + INT32 nextgridno = currentgridno + WORLD_COLS; + if ( !TileIsOutOfBounds( nextgridno ) && IsRoofPresentAtGridNo( nextgridno ) ) + { + UINT8 armour = 0; + STRUCTURE* pStruct = FindStructure( nextgridno, (STRUCTURE_WALL) ); + + if ( pStruct ) + armour = gubMaterialArmour[pStruct->pDBStructureRef->pDBStructure->ubArmour]; + + gridnoarmourpair pair( nextgridno, armour ); + + if ( std::find( vec.begin( ), vec.end( ), pair ) == vec.end( ) ) + vec.push_back( pair ); + } + } + + { + INT32 nextgridno = currentgridno - WORLD_COLS; + if ( !TileIsOutOfBounds( nextgridno ) && IsRoofPresentAtGridNo( nextgridno ) ) + { + UINT8 armour = 0; + STRUCTURE* pStruct = FindStructure( nextgridno, (STRUCTURE_WALL) ); + + if ( pStruct ) + armour = gubMaterialArmour[pStruct->pDBStructureRef->pDBStructure->ubArmour]; + + gridnoarmourpair pair( nextgridno, armour ); + + if ( std::find( vec.begin( ), vec.end( ), pair ) == vec.end( ) ) + vec.push_back( pair ); + } + } + } + + return vec; +} + +// after roof tiles have collapsed, the connected roof tile network can degenerate into several unconnected networks +// we now collect all these networks +gridnoarmournetworkmap GetConnectedRoofNetworks( gridnoarmourvector& arNetwork ) +{ + gridnoarmournetworkmap map; + + // we need a copy of the thing + gridnoarmourvector networkcpy = arNetwork; + + UINT16 networkcounter = 0; + + // at first, each nod is its own network. Ignore nodes that have already been destroyed (those that don't have a roof anymore) + for ( gridnoarmourvector::iterator it = networkcpy.begin( ); it != networkcpy.end( ); ++it ) + { + gridnoarmourpair pair = (*it); + + if ( IsRoofPresentAtGridNo( pair.first ) ) + { + gridnoarmourvector vect = GetConnectedRoofGridnoArmours( pair.first ); + + // delete all nodes further down in networkcpy that are also present in vect + for ( gridnoarmourvector::iterator tmpit = vect.begin( ); tmpit != vect.end( ); ++tmpit ) + { + networkcpy.erase( std::remove( it + 1, networkcpy.end( ), (*tmpit) ), networkcpy.end( ) ); + } + + map[networkcounter++] = vect; + + if ( it == networkcpy.end( ) ) + break; + } + } + + return map; +} + +// for a given network, return the same network with updated armour values: wall armour partially extends to neighbouring nodes +gridnoarmourvector GetArmourSharedRoofNetwork( gridnoarmourvector& arNetwork ) +{ + // we return a new network + gridnoarmourvector vec = arNetwork; + + BOOLEAN fChangeApplied = TRUE; + + while ( fChangeApplied ) + { + fChangeApplied = FALSE; + + gridnoarmourvector::iterator itend = vec.end( ); + for ( gridnoarmourvector::iterator it1 = vec.begin( ); it1 != itend; ++it1 ) + { + gridnoarmourpair& pair1 = (*it1); + + if ( !IsRoofPresentAtGridNo( pair1.first ) ) + continue; + + // loop over all other nodes + for ( gridnoarmourvector::iterator it2 = vec.begin( ); it2 != itend; ++it2 ) + { + gridnoarmourpair& pair2 = (*it2); + + if ( !IsRoofPresentAtGridNo( pair2.first ) ) + continue; + + if ( PythSpacesAway( pair1.first, pair2.first ) == 1 ) + { + UINT8 armour1 = pair1.second; + UINT8 armour2 = pair2.second; + + UINT8 sharedarmour1 = max( armour1, armour2 * WALLSTRENGTH_NEIGHBOURING_PER_TILE ); + UINT8 sharedarmour2 = max( armour1 * WALLSTRENGTH_NEIGHBOURING_PER_TILE, armour2 ); + + if ( armour1 < sharedarmour1 ) + { + pair1.second = sharedarmour1; + fChangeApplied = TRUE; + } + + if ( armour2 < sharedarmour2 ) + { + pair2.second = sharedarmour2; + fChangeApplied = TRUE; + } + } + } + } + } + + return vec; +} + + +// handle destroying if a single roof tile +void RoofDestruction( INT32 sGridNo ) +{ + SOLDIERTYPE* pSoldier = NULL; + + if ( TileIsOutOfBounds( sGridNo ) || !IsRoofPresentAtGridNo( sGridNo ) ) + return; + + // any items on this roof drop to the floor below + // if there is already a structure here, we need to drop the gear next to it... + INT32 geardropoffgridno = sGridNo; + if ( !IsLocationSittableExcludingPeople( geardropoffgridno, FALSE ) ) + { + for ( INT32 i = -1; i <= 1; ++i ) + { + for ( INT32 j = -1; j <= 1; ++j ) + { + INT32 tmpgridno = sGridNo + i + j * WORLD_COLS; + + // if this gridno has place for gear, and is in the same room as the original sGridNo (or both are not in a room) + if ( IsLocationSittableExcludingPeople( tmpgridno, FALSE ) && gusWorldRoomInfo[sGridNo] == gusWorldRoomInfo[tmpgridno] ) + { + geardropoffgridno = tmpgridno; + break; + } + } + } + } + + MoveItemPools( sGridNo, geardropoffgridno, 1, 0 ); + + // if there is anybody at the floor level, damage them with the debris + // we have to do this BEFORE people drop down. It would otherwise be possible for a falling person to avoid this damage, depending on order of collapsing and possible gridno-shifts. + UINT16 ubId = WhoIsThere2( sGridNo, 0 ); + if ( ubId != NOBODY ) + { + pSoldier = MercPtrs[ubId]; + + pSoldier->EVENT_StopMerc( pSoldier->sGridNo, pSoldier->ubDirection ); + + // if we play the animation of falling roof tiles over this, it will look like the merc fell really painful (there's even blood) + SoldierCollapse( pSoldier ); + + // take damage + UINT32 damage = 10 + Random( 3 ) + Random( 10 ); + pSoldier->SoldierTakeDamage( ANIM_CROUCH, damage, 0, damage * 100, TAKE_DAMAGE_FALLROOF, NOBODY, NOWHERE, 0, TRUE ); + } + + // if there is a person here, drop them to the ground... + ubId = WhoIsThere2( sGridNo, 1 ); + if ( ubId != NOBODY ) + { + pSoldier = MercPtrs[ubId]; + + INT32 soldierdropoffgridno = sGridNo; + if ( !IsLocationSittable( soldierdropoffgridno, FALSE ) ) + { + for ( INT32 i = -1; i <= 1; ++i ) + { + for ( INT32 j = -1; j <= 1; ++j ) + { + INT32 tmpgridno = sGridNo + i + j * WORLD_COLS; + + // if this gridno has place for gear, and is in the same room as the original sGridNo (or both are not in a room) + if ( IsLocationSittable( tmpgridno, FALSE ) && gusWorldRoomInfo[sGridNo] == gusWorldRoomInfo[tmpgridno] ) + { + soldierdropoffgridno = tmpgridno; + break; + } + } + } + } + + SoldierDropThroughRoof( pSoldier, soldierdropoffgridno ); + } + + // play an animation of falling roof tiles + // if we merely collected the tiles that collapse and then call them all together, it would be possible to have animations for multi-tile collapses + static UINT16 usRoofCollapseExplosionIndex = 1727; + if ( HasItemFlag( usRoofCollapseExplosionIndex, ROOF_COLLAPSE_ITEM ) || GetFirstItemWithFlag( &usRoofCollapseExplosionIndex, ROOF_COLLAPSE_ITEM ) ) + { + InternalIgniteExplosion( NOBODY, CenterX( sGridNo ), CenterY( sGridNo ), 0, sGridNo, usRoofCollapseExplosionIndex, FALSE, 0 ); + } + + RemoveAllRoofsOfTypeRangeAdjustSaveFile( sGridNo, FIRSTTEXTURE, WIREFRAMES ); +} + +void HandleRoofDestruction( INT32 sGridNo, INT16 sDamage ) +{ + // only if there is significant damage done + if ( sDamage < 1 || TileIsOutOfBounds( sGridNo ) || !IsRoofPresentAtGridNo( sGridNo ) ) + return; + + ApplyMapChangesToMapTempFile( TRUE ); + + gridnoarmourvector floorarmourvector = GetConnectedRoofGridnoArmours( sGridNo ); + + INT16 bestarmour = 0; + gridnoarmourvector::iterator itend = floorarmourvector.end( ); + for ( gridnoarmourvector::iterator it = floorarmourvector.begin( ); it != itend; ++it ) + bestarmour = max( bestarmour, (INT16)(*it).second ); + + for ( gridnoarmourvector::iterator it = floorarmourvector.begin( ); it != itend; ++it ) + { + INT32 sNewGridno = (*it).first; + + INT16 distance = PythSpacesAway( sGridNo, sNewGridno ); + + // only remove tile if enough damage has been done + // it might be necessary to tweak the damage formula here + // armour above 127 is deemed indestructable + if ( bestarmour < 127 && sDamage > distance * bestarmour ) + { + RoofDestruction( sNewGridno ); + (*it).second = 0; + } + } + + // Nodes might have been removed, and the roof might have degenerated into several networks + gridnoarmournetworkmap roofnetworkmap = GetConnectedRoofNetworks( floorarmourvector ); + + // for each remaining node, determine the distance to the closest node with a wall-connection inside the remaining network. If the distance is high enough, the roof will come down + gridnoarmournetworkmap::iterator roofnetworkitend = roofnetworkmap.end( ); + for ( gridnoarmournetworkmap::iterator roofnetworkit = roofnetworkmap.begin( ); roofnetworkit != roofnetworkitend; ++roofnetworkit ) + { + // sadly the calculating time is somewhat high if there are many nodes connected to the roof, like Alma prison. + // therefore this part is commented out. Can be commented in once the speed issues have been resolved + // properly done, this would simulat the 'structural integrity' of the building + /*// for each remaining node, determine the distance to the closest node with a wall-connection inside the remaining network. If the distance is high enough, the roof will come down + gridnoarmourvector roofarmoursharednetwork = GetArmourSharedRoofNetwork( (*roofnetworkit).second ); + + for ( gridnoarmourvector::iterator it = roofarmoursharednetwork.begin( ); it != roofarmoursharednetwork.end( ); ++it ) + { + gridnoarmourpair pair = (*it); + + // if it does not have a roof, ignore + if ( !IsRoofPresentAtGridNo( pair.first ) ) + { + pair.second = 0; + continue; + } + + if ( pair.second < 1 ) + { + RoofDestruction( pair.first ); + pair.second = 0; + } + }*/ + + // for now, determine the best armour for each remaining network, and collapse it if there is no armou - and thus no wall connection - left + gridnoarmourvector roofnetwork = (*roofnetworkit).second; + + UINT8 bestarmour = 0; + for ( gridnoarmourvector::iterator it = roofnetwork.begin( ); it != roofnetwork.end( ); ++it ) + bestarmour = max( bestarmour, (*it).second ); + + // if a network has no wall connection at all, collapse the entire thing + if ( bestarmour < 1 ) + { + for ( gridnoarmourvector::iterator it = roofnetwork.begin( ); it != roofnetwork.end( ); ++it ) + { + RoofDestruction( (*it).first ); + } + } + } + + ApplyMapChangesToMapTempFile( FALSE ); + + // FOR THE NEXT RENDER LOOP, RE-EVALUATE REDUNDENT TILES + InvalidateWorldRedundency( ); + SetRenderFlags( RENDER_FLAG_FULL ); +} diff --git a/TileEngine/Explosion Control.h b/TileEngine/Explosion Control.h index 2f67f3b9..bdfd9825 100644 --- a/TileEngine/Explosion Control.h +++ b/TileEngine/Explosion Control.h @@ -58,6 +58,7 @@ enum EXPLOSION_TYPES BURN_EXP, THERMOBARIC_EXP, FLASHBANG_EXP, // Lesh: enum added + ROOF_COLLAPSE, // Flugente: play an animation of roof tiles crashing down NUM_EXP_TYPES=50 } ; @@ -171,4 +172,8 @@ void CheckForBuriedBombsAndRemoveFlags( INT32 sGridNo, INT8 bLevel ); UINT16 CalcTotalVolatility(OBJECTTYPE * pObj); BOOLEAN FindBinderAttachment (OBJECTTYPE * pObj); BOOLEAN CheckExplosiveTypeAsDetonator(UINT16 ubType); + +// Flugente: destroy rooftops +void HandleRoofDestruction( INT32 sGridNo, INT16 sDamage ); + #endif diff --git a/TileEngine/SaveLoadMap.cpp b/TileEngine/SaveLoadMap.cpp index afcce698..6857881e 100644 --- a/TileEngine/SaveLoadMap.cpp +++ b/TileEngine/SaveLoadMap.cpp @@ -366,7 +366,7 @@ BOOLEAN LoadAllMapChangesFromMapTempFileAndApplyThem( ) uiNumberOfElements = uiFileSize / sizeof( MODIFY_MAP ); - for( cnt=0; cnt< uiNumberOfElements; cnt++ ) + for( cnt=0; cnt< uiNumberOfElements; ++cnt ) { pMap = &pTempArrayOfMaps[ cnt ]; @@ -436,13 +436,29 @@ BOOLEAN LoadAllMapChangesFromMapTempFileAndApplyThem( ) SaveModifiedMapStructToMapTempFile( pMap, gWorldSectorX, gWorldSectorY, gbWorldSectorZ ); //Since the element is being saved back to the temp file, increment the # - uiNumberOfElementsSavedBackToFile++; + ++uiNumberOfElementsSavedBackToFile; break; case SLM_REMOVE_SHADOW: break; case SLM_REMOVE_MERC: break; case SLM_REMOVE_ROOF: + if ( pMap->usImageType >= FIRSTTEXTURE && pMap->usImageType <= WIREFRAMES ) + { + RemoveAllRoofsOfTypeRange( pMap->usGridNo, FIRSTTEXTURE, WIREFRAMES ); + } + else + { + GetTileIndexFromTypeSubIndex( pMap->usImageType, pMap->usSubImageIndex, &usIndex ); + + RemoveRoofAdjustSavefile( pMap->usGridNo, usIndex ); + } + + // Save this struct back to the temp file + SaveModifiedMapStructToMapTempFile( pMap, gWorldSectorX, gWorldSectorY, gbWorldSectorZ ); + + //Since the element is being saved back to the temp file, increment the # + ++uiNumberOfElementsSavedBackToFile; break; case SLM_REMOVE_ONROOF: break; @@ -661,7 +677,33 @@ void RemoveStructFromMapTempFile( INT32 uiMapIndex, UINT16 usIndex ) Map.ubType = SLM_REMOVE_STRUCT; SaveModifiedMapStructToMapTempFile( &Map, gWorldSectorX, gWorldSectorY, gbWorldSectorZ ); +} +void RemoveRoofFromMapTempFile( INT32 uiMapIndex, UINT16 usIndex ) +{ + MODIFY_MAP Map; + UINT32 uiType; + UINT16 usSubIndex; + + if ( !gfApplyChangesToTempFile ) + return; + + if ( gTacticalStatus.uiFlags & LOADING_SAVED_GAME ) + return; + + GetTileType( usIndex, &uiType ); + GetSubIndexFromTileIndex( usIndex, &usSubIndex ); + + memset( &Map, 0, sizeof(MODIFY_MAP) ); + + Map.usGridNo = uiMapIndex; + // Map.usIndex = usIndex; + Map.usImageType = (UINT16)uiType; + Map.usSubImageIndex = usSubIndex; + + Map.ubType = SLM_REMOVE_ROOF; + + SaveModifiedMapStructToMapTempFile( &Map, gWorldSectorX, gWorldSectorY, gbWorldSectorZ ); } @@ -701,7 +743,7 @@ void SaveBloodSmellAndRevealedStatesFromMapToTempFile() memset( gpRevealedMap, 0, NUM_REVEALED_BYTES ); //Loop though all the map elements - for ( cnt = 0; cnt < WORLD_MAX; cnt++ ) + for ( cnt = 0; cnt < WORLD_MAX; ++cnt ) { //if there is either blood or a smell on the tile, save it if( gpWorldLevelData[cnt].ubBloodInfo || gpWorldLevelData[cnt].ubSmellInfo ) diff --git a/TileEngine/SaveLoadMap.h b/TileEngine/SaveLoadMap.h index f0298532..0c5ad8ba 100644 --- a/TileEngine/SaveLoadMap.h +++ b/TileEngine/SaveLoadMap.h @@ -82,6 +82,9 @@ BOOLEAN LoadAllMapChangesFromMapTempFileAndApplyThem( ); void RemoveStructFromMapTempFile( INT32 uiMapIndex, UINT16 usIndex ); +// Flugente: permanently remove roof tiles +void RemoveRoofFromMapTempFile( INT32 uiMapIndex, UINT16 usIndex ); + void AddRemoveObjectToMapTempFile( INT32 uiMapIndex, UINT16 usIndex ); void SaveBloodSmellAndRevealedStatesFromMapToTempFile(); diff --git a/TileEngine/worldman.cpp b/TileEngine/worldman.cpp index f9409181..3aed4a13 100644 --- a/TileEngine/worldman.cpp +++ b/TileEngine/worldman.cpp @@ -1479,6 +1479,54 @@ BOOLEAN RemoveStruct( INT32 iMapIndex, UINT16 usIndex ) } +// Flugente: permanently remove roof tiles +BOOLEAN RemoveRoofAdjustSavefile( INT32 iMapIndex, UINT16 usIndex ) +{ + LEVELNODE *pStruct = NULL; + LEVELNODE *pOldStruct = NULL; + + pStruct = gpWorldLevelData[iMapIndex].pRoofHead; + + // Look through all structs and remove index if found + + while ( pStruct != NULL ) + { + if ( pStruct->usIndex == usIndex ) + { + // OK, set links + // Check for head or tail + if ( pOldStruct == NULL ) + { + // It's the head + gpWorldLevelData[iMapIndex].pRoofHead = pStruct->pNext; + } + else + { + pOldStruct->pNext = pStruct->pNext; + } + + // Delete memory assosiated with item + DeleteStructureFromWorld( pStruct->pStructureData ); + + //If we have to, make sure to remove this node when we reload the map from a saved game + RemoveRoofFromMapTempFile( iMapIndex, usIndex ); + + MemFree( pStruct ); + --guiLevelNodes; + + return(TRUE); + } + + pOldStruct = pStruct; + pStruct = pStruct->pNext; + } + + // Could not find it, return FALSE + RemoveWorldFlagsFromNewNode( iMapIndex, usIndex ); + + return(FALSE); +} + BOOLEAN RemoveStructFromLevelNode( INT32 iMapIndex, LEVELNODE *pNode ) { @@ -1555,10 +1603,8 @@ BOOLEAN RemoveAllStructsOfTypeRange( INT32 iMapIndex, UINT32 fStartType, UINT32 while( pStruct != NULL ) { - if ( pStruct->usIndex != NO_TILE ) { - GetTileType( pStruct->usIndex, &fTileType ); // Advance to next @@ -1580,10 +1626,49 @@ BOOLEAN RemoveAllStructsOfTypeRange( INT32 iMapIndex, UINT32 fStartType, UINT32 } } } - } - } + + return fRetVal; +} + +// Flugente: permanently remove roof tiles +BOOLEAN RemoveAllRoofsOfTypeRangeAdjustSaveFile( INT32 iMapIndex, UINT32 fStartType, UINT32 fEndType ) +{ + LEVELNODE *pStruct = NULL; + LEVELNODE *pOldStruct = NULL; + UINT32 fTileType; + BOOLEAN fRetVal = FALSE; + + LEVELNODE *pRoof = NULL; + LEVELNODE *pOldRoof = NULL; + + pRoof = gpWorldLevelData[iMapIndex].pRoofHead; + + // Look through all Roofs and Search for type + + if ( pRoof != NULL ) + { + if ( pRoof->usIndex != NO_TILE ) + { + GetTileType( pRoof->usIndex, &fTileType ); + + // Advance to next + pOldRoof = pRoof; + pRoof = pRoof->pNext; + + if ( fTileType >= fStartType && fTileType <= fEndType ) + { + // Remove Item + if ( pOldRoof->usIndex < giNumberOfTiles ) + { + RemoveRoofAdjustSavefile( iMapIndex, pOldRoof->usIndex ); + fRetVal = TRUE; + } + } + } + } + return fRetVal; } diff --git a/TileEngine/worldman.h b/TileEngine/worldman.h index fd313ce4..e7e1c3d6 100644 --- a/TileEngine/worldman.h +++ b/TileEngine/worldman.h @@ -69,6 +69,10 @@ BOOLEAN WaterTooDeepForAttacks( INT32 sGridNo ); // Structure manipulation routines BOOLEAN RemoveStruct( INT32 iMapIndex, UINT16 usIndex ); + +// Flugente: permanently remove roof tiles +BOOLEAN RemoveRoofAdjustSavefile( INT32 iMapIndex, UINT16 usIndex ); + LEVELNODE *AddStructToTail( INT32 iMapIndex, UINT16 usIndex ); LEVELNODE *AddStructToTailCommon( INT32 iMapIndex, UINT16 usIndex, BOOLEAN fAddStructDBInfo ); LEVELNODE *ForceStructToTail( INT32 iMapIndex, UINT16 usIndex ); @@ -76,6 +80,10 @@ LEVELNODE *ForceStructToTail( INT32 iMapIndex, UINT16 usIndex ); BOOLEAN AddStructToHead( INT32 iMapIndex, UINT16 usIndex ); BOOLEAN TypeExistsInStructLayer( INT32 iMapIndex, UINT32 fType, UINT16 *pusStructIndex ); BOOLEAN RemoveAllStructsOfTypeRange( INT32 iMapIndex, UINT32 fStartType, UINT32 fEndType ); + +// Flugente: permanently remove roof tiles +BOOLEAN RemoveAllRoofsOfTypeRangeAdjustSaveFile( INT32 iMapIndex, UINT32 fStartType, UINT32 fEndType ); + BOOLEAN AddWallToStructLayer( INT32 iMapIndex, UINT16 usIndex, BOOLEAN fReplace ); BOOLEAN ReplaceStructIndex( INT32 iMapIndex, UINT16 usOldIndex, UINT16 usNewIndex ); BOOLEAN HideStructOfGivenType(INT32 iMapIndex, UINT32 fType, BOOLEAN fHide);//dnl ch80 011213