diff --git a/GameSettings.cpp b/GameSettings.cpp index 1d7d4d0e..d14b26e2 100644 --- a/GameSettings.cpp +++ b/GameSettings.cpp @@ -1356,6 +1356,9 @@ void LoadGameExternalOptions() gGameExternalOptions.sZombiePoisonDamagePercentage = iniReader.ReadInteger("Tactical Poison Settings", "ZOMBIE_POISON_DAMAGE_PERCENTAGE", 50, 0, 100); gGameExternalOptions.sPoisonInfectionDamageMultiplier = iniReader.ReadFloat("Tactical Poison Settings", "POISON_INFECTION_DAMAGE_MULTIPLIER", 4.0, 1.0, 10.0); + //################# Tactical Fortification Settings ################## + gGameExternalOptions.fFortificationAllowInHostileSector = iniReader.ReadBoolean("Tactical Fortification Settings", "FORTIFICATION_ALLOW_IN_HOSTILE_SECTOR", FALSE); + //################# Strategic Gamestart Settings ################## //Lalien: Game starting time diff --git a/GameSettings.h b/GameSettings.h index 6086fc5b..2baba6fb 100644 --- a/GameSettings.h +++ b/GameSettings.h @@ -382,6 +382,9 @@ typedef struct INT16 sZombiePoisonDamagePercentage; FLOAT sPoisonInfectionDamageMultiplier; + // Flugente: fortification settings + BOOLEAN fFortificationAllowInHostileSector; + //Animation settings FLOAT giPlayerTurnSpeedUpFactor; FLOAT giEnemyTurnSpeedUpFactor; diff --git a/Tactical/Handle Items.cpp b/Tactical/Handle Items.cpp index 413e9b58..6e978120 100644 --- a/Tactical/Handle Items.cpp +++ b/Tactical/Handle Items.cpp @@ -1133,6 +1133,13 @@ INT32 HandleItem( SOLDIERTYPE *pSoldier, INT32 sGridNo, INT8 bLevel, UINT16 usHa { return( ITEM_HANDLE_REFUSAL ); } + + // we can only do this on certain terraintypes + INT8 bOverTerrainType = GetTerrainType( sGridNo ); + if( bOverTerrainType != FLAT_GROUND && bOverTerrainType != DIRT_ROAD && bOverTerrainType != LOW_GRASS ) + { + return( ITEM_HANDLE_REFUSAL ); + } } // if we have a shovel in our hands, the targetted gridno must be a fortification (debris will do for this check) @@ -5982,8 +5989,6 @@ void SoldierStealItemFromSoldier( SOLDIERTYPE *pSoldier, SOLDIERTYPE *pOpponent, SetCustomizableTimerCallbackAndDelay( 1000, CheckForPickedOwnership, TRUE ); } -extern UINT8 NumEnemiesInAnySector( INT16 sSectorX, INT16 sSectorY, INT16 sSectorZ ); - BOOLEAN BuildFortification( UINT32 flag ) { INT32 sGridNo; @@ -5997,19 +6002,6 @@ BOOLEAN BuildFortification( UINT32 flag ) if ( gusSelectedSoldier == NOBODY ) return FALSE; - // comment this in to forbid building while enemies are around - /*if( (gTacticalStatus.uiFlags & INCOMBAT) && (gTacticalStatus.uiFlags & TURNBASED) ) - { - return FALSE; - } - - if( gWorldSectorX != -1 && gWorldSectorY != -1 && gWorldSectorX != 0 && gWorldSectorY != 0 && - NumEnemiesInAnySector( gWorldSectorX, gWorldSectorY, gbWorldSectorZ ) > 0 ) - { - ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"Cannot build while enemies are in this sector" ); - return FALSE; - }*/ - if( gbWorldSectorZ > 0 || gsInterfaceLevel > 0) { return FALSE; diff --git a/Tactical/LOS.cpp b/Tactical/LOS.cpp index e72eca71..a06d1ad6 100644 --- a/Tactical/LOS.cpp +++ b/Tactical/LOS.cpp @@ -5693,132 +5693,131 @@ INT8 FireBulletGivenTargetTrapOnly( SOLDIERTYPE* pThrower, OBJECTTYPE* pObj, INT pBullet->fTracer = FALSE; } }*/ - - ///////////////////////// SOUND //////////////////////////// - //PLAY SOUND - // ( For throwing knife.. it's earlier in the animation - if ( Weapon[ pObj->usItem ].sSound != 0 && Item[ pObj->usItem ].usItemClass != IC_THROWING_KNIFE ) - { - // Switch on silencer... - UINT16 noisefactor = GetPercentNoiseVolume( pObj ); - if( noisefactor < MAX_PERCENT_NOISE_VOLUME_FOR_SILENCED_SOUND || Weapon[ pObj->usItem ].ubAttackVolume <= 10 ) - { - INT32 uiSound; - - uiSound = Weapon [ pObj->usItem ].silencedSound; - - PlayJA2Sample( uiSound, RATE_11025, SoundVolume( HIGHVOLUME, gridno ), 1, SoundDir( gridno ) ); - } - else - { - INT8 volume = HIGHVOLUME; - if ( noisefactor < 100 ) volume = (volume * noisefactor) / 100; - PlayJA2Sample( Weapon[ pObj->usItem ].sSound, RATE_11025, SoundVolume( volume, gridno ), 1, SoundDir( gridno ) ); - } - } - ///////////////////////// SOUND //////////////////////////// - - // deduct ammo - (*pObj)[0]->data.gun.ubGunShotsLeft = max(0, (*pObj)[0]->data.gun.ubGunShotsLeft - 1); - - //gTacticalStatus.ubAttackBusyCount++; - + if(is_client)send_bullet( pBullet, pObj->usItem ); FireBullet( pThrower, pBullet, FALSE ); - - ///////////////////////// SOUND //////////////////////////// - UINT8 ubVolume = Weapon[ pObj->usItem ].ubAttackVolume; - - // Snap: get cumulative noise reduction from the weapon and its attachments - UINT16 noisefactor = GetPercentNoiseVolume( pObj ); - if ( ubVolume * noisefactor > 25000 ) - { // Snap: hack this to prevent overflow (damn miserly programmers!) - ubVolume = 250; - } - else - { - ubVolume = __max( 1, ( ubVolume * GetPercentNoiseVolume( pObj ) ) / 100 ); - } - - MakeNoise( NOBODY, gridno, 0, pThrower->bOverTerrainType, ubVolume, NOISE_GUNFIRE ); - ///////////////////////// SOUND //////////////////////////// - - ///////////////////////// OVERHEATING AND STATUS REDUCTION //////////////////////////// - INT16 iOverheatReliabilityMalus = 0; - // Flugente: Increase Weapon Temperature - if ( gGameOptions.fWeaponOverheating ) - { - FLOAT overheatjampercentage = GetGunOverheatDamagePercentage( pObj ); // ... how much above the gun's usOverheatingDamageThreshold are we? ... - - if ( overheatjampercentage > 1.0 ) - iOverheatReliabilityMalus = (INT16)floor(overheatjampercentage*overheatjampercentage); - - GunIncreaseHeat( pObj ); - } - - // Flugente : Added a malus to reliability for overheated guns - // HEADROCK HAM 5: Variable NCTH base change - UINT32 uiDepreciateTest = 0; - if ( UsingNewCTHSystem() == true) - { - UINT16 usBaseChance = gGameCTHConstants.BASIC_RELIABILITY_ODDS; - FLOAT dReliabilityRatio = 3.0f * ((FLOAT)usBaseChance / (FLOAT)BASIC_DEPRECIATE_CHANCE); // Compare original odds to new odds. - uiDepreciateTest = usBaseChance + (INT16)( dReliabilityRatio * GetReliability( pObj ) - iOverheatReliabilityMalus); - uiDepreciateTest = max(0, uiDepreciateTest); - } - else - { - uiDepreciateTest = max( BASIC_DEPRECIATE_CHANCE + 3 * GetReliability( pObj ) - iOverheatReliabilityMalus, 0); - } - if ( !PreRandom( uiDepreciateTest ) && ( (*pObj)[0]->data.objectStatus > 1) ) - { - (*pObj)[0]->data.objectStatus--; - } - ///////////////////////// OVERHEATING AND STATUS REDUCTION //////////////////////////// - - ///////////////////////// JAMMING //////////////////////////// - // Algorithm for jamming - int maxJamChance = 50; // Externalize this? - int reliability = GetReliability( pObj ); - int condition = (*pObj)[0]->data.gun.bGunStatus; - int invertedBaseJamChance = condition + (reliability * 2) - gGameExternalOptions.ubWeaponReliabilityReductionPerRainIntensity * 1; - - // Flugente FTW 1: If overheating is allowed, a gun will be prone to more overheating if its temperature is high - if ( gGameOptions.fWeaponOverheating ) - { - FLOAT overheatjampercentage = GetGunOverheatJamPercentage( pObj ); // how much above the gun's usOverheatingJamThreshold are we? ... - - int overheatjamfactor = (int)(100* overheatjampercentage); // We need an integer value and rough percentages - - overheatjamfactor = max(0, overheatjamfactor - 100); // If we haven't reached the OverheatJamThreshold, no increased chance of jamming because of overheating - - invertedBaseJamChance -= overheatjamfactor; // lower invertedBaseJamChance (thereby increasing jamChance later on) - } - - if (invertedBaseJamChance < 0) - invertedBaseJamChance = 0; - else if (invertedBaseJamChance > 100) - invertedBaseJamChance = 100; - int jamChance = 100 - (int)sqrt((double)invertedBaseJamChance * (75.0 + (double)invertedBaseJamChance / 2.0)); - if (jamChance < 0) - jamChance = 0; - else if (jamChance > maxJamChance - reliability) - jamChance = maxJamChance - reliability; - - - if ((INT32) PreRandom( 100 ) < jamChance ) - { - // jam! negate the gun ammo status. - (*pObj)[0]->data.gun.bGunAmmoStatus *= -1; - } - ///////////////////////// JAMMING //////////////////////////// - - // manual recharge - if (Weapon[Item[pObj->usItem].ubClassIndex].APsToReloadManually > 0) - (*pObj)[0]->data.gun.ubGunState &= ~GS_CARTRIDGE_IN_CHAMBER; - // } + ///////////////////////// SOUND //////////////////////////// + //PLAY SOUND + // ( For throwing knife.. it's earlier in the animation + if ( Weapon[ pObj->usItem ].sSound != 0 && Item[ pObj->usItem ].usItemClass != IC_THROWING_KNIFE ) + { + // Switch on silencer... + UINT16 noisefactor = GetPercentNoiseVolume( pObj ); + if( noisefactor < MAX_PERCENT_NOISE_VOLUME_FOR_SILENCED_SOUND || Weapon[ pObj->usItem ].ubAttackVolume <= 10 ) + { + INT32 uiSound; + + uiSound = Weapon [ pObj->usItem ].silencedSound; + + PlayJA2Sample( uiSound, RATE_11025, SoundVolume( HIGHVOLUME, gridno ), 1, SoundDir( gridno ) ); + } + else + { + INT8 volume = HIGHVOLUME; + if ( noisefactor < 100 ) volume = (volume * noisefactor) / 100; + PlayJA2Sample( Weapon[ pObj->usItem ].sSound, RATE_11025, SoundVolume( volume, gridno ), 1, SoundDir( gridno ) ); + } + } + ///////////////////////// SOUND //////////////////////////// + + // deduct ammo + (*pObj)[0]->data.gun.ubGunShotsLeft = max(0, (*pObj)[0]->data.gun.ubGunShotsLeft - 1); + + //gTacticalStatus.ubAttackBusyCount++; + + ///////////////////////// SOUND //////////////////////////// + UINT8 ubVolume = Weapon[ pObj->usItem ].ubAttackVolume; + + // Snap: get cumulative noise reduction from the weapon and its attachments + UINT16 noisefactor = GetPercentNoiseVolume( pObj ); + if ( ubVolume * noisefactor > 25000 ) + { // Snap: hack this to prevent overflow (damn miserly programmers!) + ubVolume = 250; + } + else + { + ubVolume = __max( 1, ( ubVolume * GetPercentNoiseVolume( pObj ) ) / 100 ); + } + + MakeNoise( NOBODY, gridno, 0, pThrower->bOverTerrainType, ubVolume, NOISE_GUNFIRE ); + ///////////////////////// SOUND //////////////////////////// + + ///////////////////////// OVERHEATING AND STATUS REDUCTION //////////////////////////// + INT16 iOverheatReliabilityMalus = 0; + // Flugente: Increase Weapon Temperature + if ( gGameOptions.fWeaponOverheating ) + { + FLOAT overheatjampercentage = GetGunOverheatDamagePercentage( pObj ); // ... how much above the gun's usOverheatingDamageThreshold are we? ... + + if ( overheatjampercentage > 1.0 ) + iOverheatReliabilityMalus = (INT16)floor(overheatjampercentage*overheatjampercentage); + + GunIncreaseHeat( pObj ); + } + + // Flugente : Added a malus to reliability for overheated guns + // HEADROCK HAM 5: Variable NCTH base change + UINT32 uiDepreciateTest = 0; + if ( UsingNewCTHSystem() == true) + { + UINT16 usBaseChance = gGameCTHConstants.BASIC_RELIABILITY_ODDS; + FLOAT dReliabilityRatio = 3.0f * ((FLOAT)usBaseChance / (FLOAT)BASIC_DEPRECIATE_CHANCE); // Compare original odds to new odds. + uiDepreciateTest = usBaseChance + (INT16)( dReliabilityRatio * GetReliability( pObj ) - iOverheatReliabilityMalus); + uiDepreciateTest = max(0, uiDepreciateTest); + } + else + { + uiDepreciateTest = max( BASIC_DEPRECIATE_CHANCE + 3 * GetReliability( pObj ) - iOverheatReliabilityMalus, 0); + } + if ( !PreRandom( uiDepreciateTest ) && ( (*pObj)[0]->data.objectStatus > 1) ) + { + (*pObj)[0]->data.objectStatus--; + } + ///////////////////////// OVERHEATING AND STATUS REDUCTION //////////////////////////// + + ///////////////////////// JAMMING //////////////////////////// + // Algorithm for jamming + int maxJamChance = 50; // Externalize this? + int reliability = GetReliability( pObj ); + int condition = (*pObj)[0]->data.gun.bGunStatus; + int invertedBaseJamChance = condition + (reliability * 2) - gGameExternalOptions.ubWeaponReliabilityReductionPerRainIntensity * 1; + + // Flugente FTW 1: If overheating is allowed, a gun will be prone to more overheating if its temperature is high + if ( gGameOptions.fWeaponOverheating ) + { + FLOAT overheatjampercentage = GetGunOverheatJamPercentage( pObj ); // how much above the gun's usOverheatingJamThreshold are we? ... + + int overheatjamfactor = (int)(100* overheatjampercentage); // We need an integer value and rough percentages + + overheatjamfactor = max(0, overheatjamfactor - 100); // If we haven't reached the OverheatJamThreshold, no increased chance of jamming because of overheating + + invertedBaseJamChance -= overheatjamfactor; // lower invertedBaseJamChance (thereby increasing jamChance later on) + } + + if (invertedBaseJamChance < 0) + invertedBaseJamChance = 0; + else if (invertedBaseJamChance > 100) + invertedBaseJamChance = 100; + int jamChance = 100 - (int)sqrt((double)invertedBaseJamChance * (75.0 + (double)invertedBaseJamChance / 2.0)); + if (jamChance < 0) + jamChance = 0; + else if (jamChance > maxJamChance - reliability) + jamChance = maxJamChance - reliability; + + + if ((INT32) PreRandom( 100 ) < jamChance ) + { + // jam! negate the gun ammo status. + (*pObj)[0]->data.gun.bGunAmmoStatus *= -1; + } + ///////////////////////// JAMMING //////////////////////////// + + // manual recharge + if (Weapon[Item[pObj->usItem].ubClassIndex].APsToReloadManually > 0) + (*pObj)[0]->data.gun.ubGunState &= ~GS_CARTRIDGE_IN_CHAMBER; + // return( TRUE ); } diff --git a/Tactical/Rotting Corpses.cpp b/Tactical/Rotting Corpses.cpp index ef4c965f..4ebc3f98 100644 --- a/Tactical/Rotting Corpses.cpp +++ b/Tactical/Rotting Corpses.cpp @@ -2232,13 +2232,10 @@ void CreateZombiefromCorpse( ROTTING_CORPSE * pCorpse, UINT16 usAnimState ) MercCreateStruct.fVisible = TRUE; - // do not generate a new palette, thereby using the old palette of the corpse - MercCreateStruct.fNoGenNewPalette = TRUE; - INT8 iNewIndex; if ( TacticalCreateSoldier( &MercCreateStruct, (UINT8 *)&iNewIndex ) ) { - /* certain values have to be set afterwards - the alternative to edit each and every function that gets called from TacticalCreateSoldier() subsequently and + /* certain values have to be set afterwards - the alternative would be to edit each and every function that gets called from TacticalCreateSoldier() subsequently and * make an exception for zombies every time... */ SOLDIERTYPE* pNewSoldier = MercPtrs[ (UINT8)iNewIndex ]; diff --git a/Tactical/Soldier Control.cpp b/Tactical/Soldier Control.cpp index 84fadf9f..a281dd5c 100644 --- a/Tactical/Soldier Control.cpp +++ b/Tactical/Soldier Control.cpp @@ -14374,10 +14374,23 @@ void SOLDIERTYPE::EVENT_SoldierBeginAttachCan( INT32 sGridNo, UINT8 ubDirection } +extern UINT8 NumEnemiesInAnySector( INT16 sSectorX, INT16 sSectorY, INT16 sSectorZ ); void SOLDIERTYPE::EVENT_SoldierBuildStructure( INT32 sGridNo, UINT8 ubDirection ) { BOOLEAN fSuccess = FALSE; + + // if specified by the ini, building/disassembling stuff is disabled while enemies are around + if ( !gGameExternalOptions.fFortificationAllowInHostileSector ) + { + if( gWorldSectorX != -1 && gWorldSectorY != -1 && gWorldSectorX != 0 && gWorldSectorY != 0 && + NumEnemiesInAnySector( gWorldSectorX, gWorldSectorY, gbWorldSectorZ ) > 0 ) + { + ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"Cannot build while enemies are in this sector" ); + return; + } + } + // do checks here... OBJECTTYPE* pObj = &(this->inv[HANDPOS]); diff --git a/Tactical/Soldier Create.cpp b/Tactical/Soldier Create.cpp index 4d1c0f64..70342564 100644 --- a/Tactical/Soldier Create.cpp +++ b/Tactical/Soldier Create.cpp @@ -341,7 +341,6 @@ SOLDIERCREATE_STRUCT::SOLDIERCREATE_STRUCT() { SOLDIERCREATE_STRUCT::SOLDIERCREATE_STRUCT(const SOLDIERCREATE_STRUCT& src) { memcpy(this, &src, SIZEOF_SOLDIERCREATE_STRUCT_POD); this->Inv = src.Inv; - this->fNoGenNewPalette = src.fNoGenNewPalette; } // Assignment operator @@ -350,7 +349,6 @@ SOLDIERCREATE_STRUCT& SOLDIERCREATE_STRUCT::operator=(const SOLDIERCREATE_STRUCT if (this != &src) { memcpy(this, &src, SIZEOF_SOLDIERCREATE_STRUCT_POD); this->Inv = src.Inv; - this->fNoGenNewPalette = src.fNoGenNewPalette; } return *this; } @@ -1360,6 +1358,10 @@ void GeneratePaletteForSoldier( SOLDIERTYPE *pSoldier, UINT8 ubSoldierClass ) BOOLEAN fMercClothingScheme; hair = -1; + // Flugente: if all palettes are already set, no need to rebuild them. This allows zombies to use a corpses palettes + if( pSoldier->PantsPal[0] && pSoldier->VestPal[0] && pSoldier->SkinPal[0] && pSoldier->HeadPal[0] ) + return; + //choose random skin tone which will limit the choice of hair colors. skin = (INT8)Random( NUMSKINS ); switch( skin ) @@ -1739,8 +1741,7 @@ BOOLEAN TacticalCopySoldierFromCreateStruct( SOLDIERTYPE *pSoldier, SOLDIERCREAT } //Generate colors for soldier based on the body type. - if ( !pCreateStruct->fNoGenNewPalette ) - GeneratePaletteForSoldier( pSoldier, pCreateStruct->ubSoldierClass ); + GeneratePaletteForSoldier( pSoldier, pCreateStruct->ubSoldierClass ); // Copy item info over pSoldier->inv = pCreateStruct->Inv; diff --git a/Tactical/Soldier Create.h b/Tactical/Soldier Create.h index 13332131..d2c7efba 100644 --- a/Tactical/Soldier Create.h +++ b/Tactical/Soldier Create.h @@ -305,9 +305,6 @@ public: INT8 bUseGivenVehicleID; BOOLEAN fHasKeys; - // Flugente: do not generate new palette for soldier (for soldiers that are created from existing soldiers/corpses, like doppelgangers and zombies) - BOOLEAN fNoGenNewPalette; - // // New and OO stuff goes after here. Above this point any changes will goof up reading from files. // diff --git a/Tactical/Tactical Turns.cpp b/Tactical/Tactical Turns.cpp index ea06a7f8..555ece2e 100644 --- a/Tactical/Tactical Turns.cpp +++ b/Tactical/Tactical Turns.cpp @@ -271,7 +271,7 @@ void HandleTacticalEndTurn( ) HandleRPCDescription( ); #endif - // Flugente FTW 1: Cool down all items not in a soldier's inventory + // Flugente: Cool down all items not in a soldier's inventory CoolDownWorldItems( FALSE ); // Flugente: raise zombies if in gamescreen and option set if ( guiCurrentScreen == GAME_SCREEN ) diff --git a/Tactical/TeamTurns.cpp b/Tactical/TeamTurns.cpp index c1554f63..c74cdd1d 100644 --- a/Tactical/TeamTurns.cpp +++ b/Tactical/TeamTurns.cpp @@ -459,6 +459,14 @@ void EndTurnEvents( void ) DecaySmokeEffects( GetWorldTotalSeconds( ) ); DecayLightEffects( GetWorldTotalSeconds( ) ); + // Flugente: Cool down all items not in a soldier's inventory + CoolDownWorldItems( FALSE ); + // Flugente: raise zombies if in gamescreen and option set + if ( guiCurrentScreen == GAME_SCREEN ) + { + RaiseZombies(); + } + // decay AI warning values from corpses DecayRottingCorpseAIWarnings(); diff --git a/TileEngine/Explosion Control.cpp b/TileEngine/Explosion Control.cpp index c80a631d..ff45c19b 100644 --- a/TileEngine/Explosion Control.cpp +++ b/TileEngine/Explosion Control.cpp @@ -3607,62 +3607,66 @@ BOOLEAN ActivateSurroundingTripwire( UINT8 ubID, INT32 sGridNo, INT8 bLevel, UIN if ( Item[pObj->usItem].tripwireactivation == 1 ) { // tripwire just gets activated - // this is important - we have to check for the tripwire's temperature. - // The temperature serves as a temporary flag replacement, marking that a tripwire has already been activated, adn shouldn't be activated again - if ( Item[pObj->usItem].tripwire == 1 && (*pObj)[0]->data.bTemperature < 1 ) + if ( Item[pObj->usItem].tripwire == 1 ) { - // determine this tripwire's flag - UINT32 ubWireNetworkFlag = (*pObj)[0]->data.ubWireNetworkFlag; - - // check if a) tripwire belongs to the same tripwire network and b) its of the same or lower hierarchy level - BOOLEAN samenetwork = FALSE; - BOOLEAN sameorlowerhierarchy = FALSE; - - if ( ubWireNetworkFlag <= ubFlag ) // hierarchy of a group is sorted, so this suffices - sameorlowerhierarchy = TRUE; - - if ( !sameorlowerhierarchy ) - continue; - - for (INT8 i = 0; i < 2; ++i) // 2 runs: first for enemy networks, second for player networks + // this is important - we have to check for the tripwire's temperature. + // The temperature serves as a temporary flag replacement, marking that a tripwire has already been activated, and shouldn't be activated again + if ( (*pObj)[0]->data.bTemperature < 1 ) { - for (INT8 j = 0; j < 4; ++j) - { - UINT32 samenetworkflag = ( ENEMY_NET_1_LVL_1 | ENEMY_NET_1_LVL_2 | ENEMY_NET_1_LVL_3 | ENEMY_NET_1_LVL_4 ) << (16*i + j); // comparing with this flag will determine the network + // determine this tripwire's flag + UINT32 ubWireNetworkFlag = (*pObj)[0]->data.ubWireNetworkFlag; - if ( ( (ubFlag & samenetworkflag) != 0 ) && ( (ubWireNetworkFlag & samenetworkflag) != 0 ) ) - samenetwork = TRUE; + // check if a) tripwire belongs to the same tripwire network and b) its of the same or lower hierarchy level + BOOLEAN samenetwork = FALSE; + BOOLEAN sameorlowerhierarchy = FALSE; - if ( samenetwork ) - break; - } - } - - if ( samenetwork && sameorlowerhierarchy ) - { - gubPersonToSetOffExplosions = ubID; - - // put this bomb on the queue - AddBombToQueue( uiWorldBombIndex, uiTimeStamp ); - if (pObj->usItem != ACTION_ITEM || (*pObj)[0]->data.misc.bActionValue == ACTION_ITEM_BLOW_UP) - { - uiTimeStamp += BOMB_QUEUE_DELAY; - } + if ( ubWireNetworkFlag <= ubFlag ) // hierarchy of a group is sorted, so this suffices + sameorlowerhierarchy = TRUE; - if ( (*pObj)[0]->data.misc.usBombItem != NOTHING && Item[ (*pObj)[0]->data.misc.usBombItem ].usItemClass & IC_EXPLOSV ) + if ( !sameorlowerhierarchy ) + continue; + + for (INT8 i = 0; i < 2; ++i) // 2 runs: first for enemy networks, second for player networks { - fFoundMine = TRUE; + for (INT8 j = 0; j < 4; ++j) + { + UINT32 samenetworkflag = ( ENEMY_NET_1_LVL_1 | ENEMY_NET_1_LVL_2 | ENEMY_NET_1_LVL_3 | ENEMY_NET_1_LVL_4 ) << (16*i + j); // comparing with this flag will determine the network + + if ( ( (ubFlag & samenetworkflag) != 0 ) && ( (ubWireNetworkFlag & samenetworkflag) != 0 ) ) + samenetwork = TRUE; + + if ( samenetwork ) + break; + } } + + if ( samenetwork && sameorlowerhierarchy ) + { + gubPersonToSetOffExplosions = ubID; + + /*if (pObj->usItem != ACTION_ITEM || (*pObj)[0]->data.misc.bActionValue == ACTION_ITEM_BLOW_UP) + { + uiTimeStamp += BOMB_QUEUE_DELAY; + }*/ + + // put this bomb on the queue + AddBombToQueue( uiWorldBombIndex, uiTimeStamp ); + + if ( (*pObj)[0]->data.misc.usBombItem != NOTHING && Item[ (*pObj)[0]->data.misc.usBombItem ].usItemClass & IC_EXPLOSV ) + { + fFoundMine = TRUE; + } - // Flugente hack: the tripwire will get removed in HandleExplosionQueue (it is still needed in there until the bomb gets called). - // Because of this, ActivateSurroundingTripwire wouldm normally find this wire again, a loop would occur. - // As i do not want to create an itm flag for this purpose right now (but its on my TODO-list), we set the temperature of the wire to a higher value - // (temperature for tripwire isn't needed anywhere else, so its ok) - (*pObj)[0]->data.bTemperature = 1000.0; + // Flugente hack: the tripwire will get removed in HandleExplosionQueue (it is still needed in there until the bomb gets called). + // Because of this, ActivateSurroundingTripwire wouldm normally find this wire again, a loop would occur. + // As i do not want to create an itm flag for this purpose right now (but its on my TODO-list), we set the temperature of the wire to a higher value + // (temperature for tripwire isn't needed anywhere else, so its ok) + (*pObj)[0]->data.bTemperature = 1000.0; - // activate surrounding tripwires, unless tripwire is too much damaged and we are unlucky.. - if ( (*pObj)[0]->data.objectStatus > (INT16)Random(50) ) - fFoundMine = ActivateSurroundingTripwire(ubID, adjgrid, bLevel, ubWireNetworkFlag); + // activate surrounding tripwires, unless tripwire is too much damaged and we are unlucky.. + if ( (*pObj)[0]->data.objectStatus > (INT16)Random(50) ) + fFoundMine = ActivateSurroundingTripwire(ubID, adjgrid, bLevel, ubWireNetworkFlag); + } } } // bombs go off @@ -3678,12 +3682,13 @@ BOOLEAN ActivateSurroundingTripwire( UINT8 ubID, INT32 sGridNo, INT8 bLevel, UIN gMercProfiles[ MercPtrs[ ((*pObj)[0]->data.misc.ubBombOwner - 2) ]->ubProfile ].records.usExpDetonated++; } - // put this bomb on the queue - AddBombToQueue( uiWorldBombIndex, uiTimeStamp ); - if (pObj->usItem != ACTION_ITEM || (*pObj)[0]->data.misc.bActionValue == ACTION_ITEM_BLOW_UP) + /*if (pObj->usItem != ACTION_ITEM || (*pObj)[0]->data.misc.bActionValue == ACTION_ITEM_BLOW_UP) { uiTimeStamp += BOMB_QUEUE_DELAY; - } + }*/ + + // put this bomb on the queue + AddBombToQueue( uiWorldBombIndex, uiTimeStamp ); if ( (*pObj)[0]->data.misc.usBombItem != NOTHING && Item[ (*pObj)[0]->data.misc.usBombItem ].usItemClass & IC_EXPLOSV ) { @@ -3706,45 +3711,27 @@ void CheckAndFireTripwireGun( OBJECTTYPE* pObj, INT32 sGridNo, INT8 bLevel, UINT if ( !pObj ) return; - // search for attached guns - BOOLEAN fgunfound = FALSE; - OBJECTTYPE* pAttGun = NULL; - // check all attachments - attachmentList::iterator iterend = (*pObj)[0]->attachments.end(); - for (attachmentList::iterator iter = (*pObj)[0]->attachments.begin(); iter != iterend; ++iter) + // we actually found a gun. + // if the gun has ammo and is not jammed, fire it + if ( (*pObj)[0]->data.gun.ubGunShotsLeft > 0 && (*pObj)[0]->data.gun.bGunAmmoStatus > 0 ) { - if ( iter->exists() && Item[iter->usItem].usItemClass == IC_GUN ) + // Increment attack counter... + if (gubElementsOnExplosionQueue == 0) { - fgunfound = TRUE; - pAttGun = &(*iter); - break; - } - } + // single explosion, disable sight until the end, and set flag + // to check sight at end of attack - if ( fgunfound && pAttGun ) - { - // we actually found a gun. - // if the gun has ammo and is not jammed, fire it - if ( (*pAttGun)[0]->data.gun.ubGunShotsLeft > 0 && (*pAttGun)[0]->data.gun.bGunAmmoStatus > 0 ) - { - // Increment attack counter... - if (gubElementsOnExplosionQueue == 0) - { - // single explosion, disable sight until the end, and set flag - // to check sight at end of attack - - gTacticalStatus.uiFlags |= (DISALLOW_SIGHT | CHECK_SIGHT_AT_END_OF_ATTACK); - } - - FireFragmentsTrapGun( MercPtrs[ubId], sGridNo, 0, pAttGun, ubDirection ); - - // this is important... if not set, the game will remain in a loop - gTacticalStatus.ubAttackBusyCount = 0; + gTacticalStatus.uiFlags |= (DISALLOW_SIGHT | CHECK_SIGHT_AT_END_OF_ATTACK); } - // add this gun to the floor - AddItemToPool( sGridNo, pAttGun, 1, bLevel, 0, -1 ); + FireFragmentsTrapGun( MercPtrs[ubId], sGridNo, 0, pObj, ubDirection ); + + // this is important... if not set, the game will remain in a loop + gTacticalStatus.ubAttackBusyCount = 0; } + + // add this gun to the floor + AddItemToPool( sGridNo, pObj, 1, bLevel, 0, -1 ); } void HandleExplosionQueue( void ) @@ -3795,15 +3782,40 @@ void HandleExplosionQueue( void ) // tripwire gets called and activated in ActivateSurroundingTripwire else if ( Item[pObj->usItem].tripwire == 1 ) { - // check if there is a gun attached to this piece of wire, and eventually fire it - CheckAndFireTripwireGun( pObj, gWorldItems[ gWorldBombs[uiWorldBombIndex].iItemIndex ].sGridNo, ubLevel, (*pObj)[0]->data.misc.ubBombOwner, (*pObj)[0]->data.ubDirection ); - OBJECTTYPE newtripwireObject; CreateItem( pObj->usItem, (*pObj)[0]->data.objectStatus, &newtripwireObject ); // determine this tripwire's flag UINT32 ubWireNetworkFlag = (*pObj)[0]->data.ubWireNetworkFlag; + // search for attached guns + BOOLEAN fgunfound = FALSE; + OBJECTTYPE* pAttGun = NULL; + // check all attachments + attachmentList::iterator iterend = (*pObj)[0]->attachments.end(); + for (attachmentList::iterator iter = (*pObj)[0]->attachments.begin(); iter != iterend; ++iter) + { + if ( iter->exists() && Item[iter->usItem].usItemClass == IC_GUN ) + { + fgunfound = TRUE; + pAttGun = &(*iter); + break; + } + } + + if ( fgunfound && pAttGun && pAttGun->exists() ) + { + // Flugente 2012-06-16: I ran into a weird bug. If activating multiple guntraps, an errro would occur when placing a gun on the floor. + // The problem is the function GetFreeWorldItemIndex( ) in AddItemToWorld() in World Items.cpp. + // It gets a free index for the item that should be placed. If there is no free index, it creates a bigger array, copies the current array into it, and then deletes the old index. + // For some reason still unknown to me, this corrupts the pObj-pointer. It also corrupts the pAttGun-pointer, this is the reason I create a new object. + // I had to move some functions here to take care of that. It would be good if spmeone with more knowledge could take a look at this. + + // fire with this gun, if possible. Afterwards palce it on the floor + OBJECTTYPE object(*pAttGun); + CheckAndFireTripwireGun( &object, sGridNo, ubLevel, (*pObj)[0]->data.misc.ubBombOwner, (*pObj)[0]->data.ubDirection ); + } + // this is important: delete the tripwire, otherwise we get into an infinite loop if there are two piecs of tripwire.... RemoveItemFromPool( sGridNo, gWorldBombs[ uiWorldBombIndex ].iItemIndex, ubLevel ); @@ -4287,14 +4299,14 @@ BOOLEAN SetOffBombsInGridNo( UINT8 ubID, INT32 sGridNo, BOOLEAN fAllBombs, INT8 { gubPersonToSetOffExplosions = ubID; - // put this bomb on the queue - AddBombToQueue( uiWorldBombIndex, uiTimeStamp ); - if (pObj->usItem != ACTION_ITEM || (*pObj)[0]->data.misc.bActionValue == ACTION_ITEM_BLOW_UP) + /*if (pObj->usItem != ACTION_ITEM || (*pObj)[0]->data.misc.bActionValue == ACTION_ITEM_BLOW_UP) { uiTimeStamp += BOMB_QUEUE_DELAY; - //uiTimeStamp += BOMB_QUEUE_DELAY; - } + }*/ + // put this bomb on the queue + AddBombToQueue( uiWorldBombIndex, uiTimeStamp ); + if ( (*pObj)[0]->data.misc.usBombItem != NOTHING && Item[ (*pObj)[0]->data.misc.usBombItem ].usItemClass & IC_EXPLOSV ) { fFoundMine = TRUE; @@ -4302,7 +4314,7 @@ BOOLEAN SetOffBombsInGridNo( UINT8 ubID, INT32 sGridNo, BOOLEAN fAllBombs, INT8 // Flugente hack: the tripwire will get removed in HandleExplosionQueue (it is still needed in there until the bomb gets called). // Because of this, ActivateSurroundingTripwire wouldm normally find this wire again, a loop would occur. - // As i do not want to create an itm flag for this purpose right now (but its on my TODO-list), we set the temperature of the wire to a higher value + // As i do not want to create an item flag for this purpose right now (but its on my TODO-list), we set the temperature of the wire to a higher value // (temperature for tripwire isn't needed anywhere else, so its ok) (*pObj)[0]->data.bTemperature = 1000.0; @@ -4322,12 +4334,13 @@ BOOLEAN SetOffBombsInGridNo( UINT8 ubID, INT32 sGridNo, BOOLEAN fAllBombs, INT8 gMercProfiles[ MercPtrs[ ((*pObj)[0]->data.misc.ubBombOwner - 2) ]->ubProfile ].records.usExpDetonated++; } - // put this bomb on the queue - AddBombToQueue( uiWorldBombIndex, uiTimeStamp ); - if (pObj->usItem != ACTION_ITEM || (*pObj)[0]->data.misc.bActionValue == ACTION_ITEM_BLOW_UP) + /*if (pObj->usItem != ACTION_ITEM || (*pObj)[0]->data.misc.bActionValue == ACTION_ITEM_BLOW_UP) { uiTimeStamp += BOMB_QUEUE_DELAY; - } + }*/ + + // put this bomb on the queue + AddBombToQueue( uiWorldBombIndex, uiTimeStamp ); if ( (*pObj)[0]->data.misc.usBombItem != NOTHING && Item[ (*pObj)[0]->data.misc.usBombItem ].usItemClass & IC_EXPLOSV ) {