diff --git a/GameSettings.cpp b/GameSettings.cpp index c2a835eb6..1d7d4d0e7 100644 --- a/GameSettings.cpp +++ b/GameSettings.cpp @@ -1346,6 +1346,7 @@ void LoadGameExternalOptions() gGameExternalOptions.fZombieCanClimb = iniReader.ReadBoolean("Tactical Zombie Settings", "ZOMBIE_CAN_CLIMB", TRUE); gGameExternalOptions.fZombieExplodingCivs = iniReader.ReadBoolean("Tactical Zombie Settings", "ZOMBIE_EXPLODING_CIVS", FALSE); gGameExternalOptions.sEnemyZombieDamageResistance = iniReader.ReadInteger("Tactical Zombie Settings", "ZOMBIE_DAMAGE_RESISTANCE", 0, -50, 95); + gGameExternalOptions.sEnemyZombieBreathDamageResistance = iniReader.ReadInteger("Tactical Zombie Settings", "ZOMBIE_BREATH_DAMAGE_RESISTANCE", 0, -50, 95); gGameExternalOptions.fZombieOnlyHeadshotsWork = iniReader.ReadBoolean("Tactical Zombie Settings", "ZOMBIE_ONLY_HEADSHOTS_WORK", FALSE); gGameExternalOptions.sZombieDifficultyLevel = iniReader.ReadInteger("Tactical Zombie Settings", "ZOMBIE_DIFFICULTY_LEVEL", 2, 1, 4); gGameExternalOptions.fZombieRiseWithArmour = iniReader.ReadBoolean("Tactical Zombie Settings", "ZOMBIE_RISE_WITH_ARMOUR", TRUE); @@ -2283,6 +2284,8 @@ void LoadGameAPBPConstants() APBPConstants[AP_JUMPWALL] = DynamicAdjustAPConstants(iniReader.ReadInteger("APConstants","AP_JUMPOFFWALL",40),40); APBPConstants[AP_JUMPOFFWALL] = DynamicAdjustAPConstants(iniReader.ReadInteger("APConstants","AP_JUMPWALL",24),24); + APBPConstants[AP_FORTIFICATION] = DynamicAdjustAPConstants(iniReader.ReadInteger("APConstants","AP_FORTIFICATION",80),80); + SetupMaxActionPointsAnimation(); #undef ReadInteger } diff --git a/GameSettings.h b/GameSettings.h index 8d02dd859..6086fc5bb 100644 --- a/GameSettings.h +++ b/GameSettings.h @@ -372,6 +372,7 @@ typedef struct BOOLEAN fZombieCanClimb; BOOLEAN fZombieExplodingCivs; INT8 sEnemyZombieDamageResistance; + INT8 sEnemyZombieBreathDamageResistance; BOOLEAN fZombieOnlyHeadshotsWork; INT8 sZombieDifficultyLevel; BOOLEAN fZombieRiseWithArmour; diff --git a/Init.h b/Init.h index 4b8af54c3..bb88e4be5 100644 --- a/Init.h +++ b/Init.h @@ -197,6 +197,7 @@ BP_JUMPOFFWALL, BP_JUMPWALL, AP_JUMPWALL, AP_JUMPOFFWALL, +AP_FORTIFICATION, TOTAL_APBP_VALUES }; #endif diff --git a/Tactical/Handle Items.cpp b/Tactical/Handle Items.cpp index 0a2cb129e..4045f0759 100644 --- a/Tactical/Handle Items.cpp +++ b/Tactical/Handle Items.cpp @@ -1120,6 +1120,80 @@ INT32 HandleItem( SOLDIERTYPE *pSoldier, INT32 sGridNo, INT8 bLevel, UINT16 usHa } } + // Flugente: sandbag stuff + if ( HasItemFlag(usHandItem, (EMPTY_SANDBAG|FULL_SANDBAG|SHOVEL|CONCERTINA)) ) + { + // if we have an empty sandbag in our hands, we also need to have a shovel in our second hand, otherwise we can't fill it + if ( HasItemFlag(usHandItem, (EMPTY_SANDBAG)) ) + { + // check if we have a shovel in our second hand + OBJECTTYPE* pShovelObj = &(pSoldier->inv[SECONDHANDPOS]); + + if ( !pShovelObj || !(pShovelObj->exists()) || !HasItemFlag(pSoldier->inv[ SECONDHANDPOS ].usItem, (SHOVEL)) ) + { + 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) + if ( HasItemFlag(usHandItem, (SHOVEL)) ) + { + STRUCTURE* pStruct = FindStructure(sGridNo, STRUCTURE_GENERIC); + + if ( !pStruct ) + { + return( ITEM_HANDLE_REFUSAL ); + } + } + + sActionGridNo = FindAdjacentGridEx( pSoldier, sGridNo, &ubDirection, &sAdjustedGridNo, TRUE, FALSE ); + + if ( sActionGridNo != -1 ) + { + // Calculate AP costs... + sAPCost = GetAPsToBuildFortification( pSoldier, sActionGridNo ); + sAPCost += PlotPath( pSoldier, sActionGridNo, NO_COPYROUTE, FALSE, TEMPORARY, (UINT16)pSoldier->usUIMovementMode, NOT_STEALTH, FORWARD, pSoldier->bActionPoints); + + if ( EnoughPoints( pSoldier, sAPCost, 0, fFromUI ) ) + { + // CHECK IF WE ARE AT THIS GRIDNO NOW + if ( pSoldier->sGridNo != sActionGridNo ) + { + // SEND PENDING ACTION + pSoldier->aiData.ubPendingAction = MERC_BUILD_FORTIFICATION; + pSoldier->aiData.sPendingActionData2 = sAdjustedGridNo; + pSoldier->aiData.bPendingActionData3 = ubDirection; + pSoldier->aiData.ubPendingActionAnimCount = 0; + + // WALK UP TO DEST FIRST + pSoldier->EVENT_InternalGetNewSoldierPath( sActionGridNo, pSoldier->usUIMovementMode, FALSE, TRUE ); + } + else + { + pSoldier->EVENT_SoldierBuildStructure( sAdjustedGridNo, ubDirection ); + } + + // OK, set UI + SetUIBusy( pSoldier->ubID ); + + if ( fFromUI ) + { + guiPendingOverrideEvent = A_CHANGE_TO_MOVE; + } + + return( ITEM_HANDLE_OK ); + } + else + { + return( ITEM_HANDLE_NOAPS ); + } + } + else + { + return( ITEM_HANDLE_CANNOT_GETTO_LOCATION ); + } + } + if ( Item[usHandItem].canandstring ) { STRUCTURE *pStructure; @@ -5907,3 +5981,214 @@ void SoldierStealItemFromSoldier( SOLDIERTYPE *pSoldier, SOLDIERTYPE *pOpponent, gsTempGridNo = sGridNo; SetCustomizableTimerCallbackAndDelay( 1000, CheckForPickedOwnership, TRUE ); } + +extern UINT8 NumEnemiesInAnySector( INT16 sSectorX, INT16 sSectorY, INT16 sSectorZ ); + +BOOLEAN BuildFortification( UINT32 flag ) +{ + INT32 sGridNo; + UINT32 fHeadType; + UINT16 usUseIndex; + UINT16 usUseObjIndex = 0; + INT32 iRandSelIndex = 1; + BOOLEAN fOkayToAdd; + UINT8 ubDirection; + + if ( gusSelectedSoldier == NOBODY ) + return FALSE; + + if( (gTacticalStatus.uiFlags & INCOMBAT) && (gTacticalStatus.uiFlags & TURNBASED) ) + { + return FALSE; + } + + /* // comment this in to forbid building while enemies are around + 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; + } + + GetMouseMapPos( &sGridNo ); + + if( InARoom( sGridNo, NULL ) ) + return FALSE; + + INT8 bOverTerrainType = GetTerrainType( sGridNo ); + if( bOverTerrainType == MED_WATER || bOverTerrainType == DEEP_WATER || bOverTerrainType == LOW_WATER ) + return FALSE; + + UINT16 CurrentStruct = NO_TILE; + + if ( sGridNo < 0x80000000 ) + { + ubDirection = MercPtrs[ gusSelectedSoldier ]->ubDirection; + + if ( (flag & CONCERTINA) != 0 ) + { + // concertina wire + switch ( ubDirection ) + { + case NORTH: + case SOUTH: + usUseIndex = 2 + 2*Random(2); + break; + case WEST: + case EAST: + usUseIndex = 3 + 2*Random(2); + break; + case NORTHEAST: + usUseIndex = 9; + break; + case SOUTHWEST: + usUseIndex = 8; + break; + case NORTHWEST: + usUseIndex = 7; + break; + case SOUTHEAST: + default: + usUseIndex = 6; + break; + } + } + else + { + // sandbags + switch ( ubDirection ) + { + case NORTH: + case SOUTH: + usUseIndex = 3 + Random(2); + break; + case WEST: + case EAST: + usUseIndex = 8 + Random(2); + break; + case NORTHEAST: + case SOUTHWEST: + case NORTHWEST: + case SOUTHEAST: + default: + usUseIndex = 5; + break; + } + + //usUseObjIndex = (UINT16)THIRDOSTRUCT;//SelOStructs2[ iRandSelIndex ].uiObject; + } + + // search wether structure exists in the current tilesets. If not, well, too bad + UINT32 uiType = 0; + + for(uiType = 0; uiType < giNumberOfTileTypes; ++uiType) + { + if( gTilesets[ giCurrentTilesetID ].TileSurfaceFilenames[ uiType ][0] ) + { + if( (flag & CONCERTINA) != 0 ) + { + if ( (_strnicmp(gTilesets[ giCurrentTilesetID ].TileSurfaceFilenames[ uiType ], "spot_1.sti", 10) == 0) ) + { + usUseObjIndex = uiType; + break; + } + } + else if( (_strnicmp(gTilesets[ giCurrentTilesetID ].TileSurfaceFilenames[ uiType ], "sandbag.sti", 11) == 0) ) + { + usUseObjIndex = uiType; + break; + } + } + } + + if ( !usUseObjIndex ) + { + ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"The selected barricade cannot be built in this sector" ); + return FALSE; + } + + // Check with Structure Database (aka ODB) if we can put the object here! + fOkayToAdd = OkayToAddStructureToWorld( sGridNo, 0, gTileDatabase[ (gTileTypeStartIndex[ usUseObjIndex ] + usUseIndex) ].pDBStructureRef, INVALID_STRUCTURE_ID ); + if ( fOkayToAdd || (gTileDatabase[ (gTileTypeStartIndex[ usUseObjIndex ] + usUseIndex) ].pDBStructureRef == NULL) ) + { + // Remove old graphic + ApplyMapChangesToMapTempFile( TRUE ); + + //dnl Remove existing structure before adding the same, seems to solve problem with stacking but still need test to be sure that is not removed something what should stay + RemoveStruct( sGridNo, (UINT16)(gTileTypeStartIndex[ usUseObjIndex ] + usUseIndex) );//dnl + // Actual structure info is added by the functions below + AddStructToHead( sGridNo, (UINT16)(gTileTypeStartIndex[ usUseObjIndex ] + usUseIndex) ); + // For now, adjust to shadows by a hard-coded amount, + + // Add mask if in long grass + GetLandHeadType( sGridNo, &fHeadType ); + + RecompileLocalMovementCosts(sGridNo); + + // Turn off permanent changes.... + ApplyMapChangesToMapTempFile( FALSE ); + SetRenderFlags( RENDER_FLAG_FULL ); + + return TRUE; + } + } + else if ( CurrentStruct == ERASE_TILE && sGridNo < 0x80000000 ) + { + RemoveAllStructsOfTypeRange( sGridNo, FIRSTOSTRUCT, LASTOSTRUCT ); + RemoveAllShadowsOfTypeRange( sGridNo, FIRSTSHADOW, LASTSHADOW ); + } + + return FALSE; +} + +BOOLEAN RemoveFortification( INT32 sGridNo ) +{ + STRUCTURE* pStruct = FindStructure(sGridNo, STRUCTURE_GENERIC); + + if ( pStruct != NULL ) + { + // Get LEVELNODE for struct and remove! + LEVELNODE* pNode = FindLevelNodeBasedOnStructure( pStruct->sGridNo, pStruct ); + + if ( pNode ) + { + UINT32 uiTileType = 0; + if ( GetTileType( pNode->usIndex, &uiTileType ) ) + { + UINT16 usIndex = pNode->usIndex; + + // Check if we are a sandbag + if ( _strnicmp( gTilesets[ giCurrentTilesetID ].TileSurfaceFilenames[ uiTileType ], "sandbag.sti", 11) == 0 ) + { + // Remove old graphic + ApplyMapChangesToMapTempFile( TRUE ); + + RemoveStruct( sGridNo, pNode->usIndex ); + if ( !GridNoIndoors( sGridNo ) && gTileDatabase[ usIndex ].uiFlags & HAS_SHADOW_BUDDY && gTileDatabase[ usIndex ].sBuddyNum != -1 ) + { + RemoveShadow( sGridNo, gTileDatabase[ usIndex ].sBuddyNum ); + } + + // Add mask if in long grass + UINT32 fHeadType = 0; + GetLandHeadType( sGridNo, &fHeadType ); + + RecompileLocalMovementCosts(sGridNo); + + // Turn off permanent changes.... + ApplyMapChangesToMapTempFile( FALSE ); + SetRenderFlags( RENDER_FLAG_FULL ); + + return TRUE; + } + } + } + } + + return FALSE; +} diff --git a/Tactical/Handle Items.h b/Tactical/Handle Items.h index f6d6f25f3..f5960f62f 100644 --- a/Tactical/Handle Items.h +++ b/Tactical/Handle Items.h @@ -175,6 +175,9 @@ UINT8 StealItems(SOLDIERTYPE* pSoldier,SOLDIERTYPE* pOpponent, UINT8* ubIndexRet BOOLEAN MarblesExistAtLocation( INT32 sGridNo, UINT8 ubLevel, INT32 * piItemIndex ); +BOOLEAN BuildFortification( UINT32 flag = FULL_SANDBAG ); // Flugente: build a structure, return true if sucessful +BOOLEAN RemoveFortification( INT32 sGridNo ); + extern ITEM_POOL *gpItemPool;//dnl ch26 210909 #endif \ No newline at end of file diff --git a/Tactical/Handle UI.cpp b/Tactical/Handle UI.cpp index ff6cc2ce7..17895b325 100644 --- a/Tactical/Handle UI.cpp +++ b/Tactical/Handle UI.cpp @@ -4117,6 +4117,24 @@ INT8 DrawUIMovementPath( SOLDIERTYPE *pSoldier, INT32 usMapPos, UINT32 uiFlags ) gsUIHandleShowMoveGridLocation = sActionGridNo; } } + else if ( uiFlags == MOVEUI_TARGET_FORTIFICATION ) + { + sActionGridNo = FindAdjacentGridEx( pSoldier, usMapPos, &ubDirection, NULL, FALSE, TRUE ); + if ( sActionGridNo == -1 ) + { + sActionGridNo = usMapPos; + } + + sAPCost = GetAPsToBuildFortification( pSoldier, sActionGridNo ); + + sAPCost += UIPlotPath( pSoldier, sActionGridNo, NO_COPYROUTE, fPlot, TEMPORARY, (UINT16)pSoldier->usUIMovementMode, NOT_STEALTH, FORWARD, pSoldier->bActionPoints); + + if ( sActionGridNo != pSoldier->sGridNo ) + { + gfUIHandleShowMoveGrid = TRUE; + gsUIHandleShowMoveGridLocation = sActionGridNo; + } + } else if ( uiFlags == MOVEUI_TARGET_MERCS ) { INT32 sGotLocation = NOWHERE; @@ -4423,6 +4441,45 @@ BOOLEAN UIMouseOnValidAttackLocation( SOLDIERTYPE *pSoldier ) } } + if ( ubItemCursor == FORTICURS ) + { + if ( gfUIFullTargetFound ) + { + usMapPos = MercPtrs[ gusUIFullTargetID ]->sGridNo; + } + + if ( pSoldier->pathing.bLevel == 0 ) + { + if ( IsFortificationPossibleAtGridNo( usMapPos, NULL ) ) + { + return( TRUE ); + } + + if ( HasItemFlag( (&(pSoldier->inv[HANDPOS]))->usItem, (EMPTY_SANDBAG)) ) + { + // check if we have a shovel in our second hand + OBJECTTYPE* pShovelObj = &(pSoldier->inv[SECONDHANDPOS]); + + if ( !pShovelObj || !(pShovelObj->exists()) || !HasItemFlag(pSoldier->inv[ SECONDHANDPOS ].usItem, (SHOVEL)) ) + { + return( TRUE ); + } + } + + if ( HasItemFlag( (&(pSoldier->inv[HANDPOS]))->usItem, (SHOVEL)) ) + { + STRUCTURE* pStruct = FindStructure(usMapPos, STRUCTURE_GENERIC); + + if ( pStruct ) + { + return( TRUE ); + } + } + } + + return( FALSE ); + } + if ( ubItemCursor == BOMBCURS ) { if ( usMapPos == pSoldier->sGridNo ) diff --git a/Tactical/Handle UI.h b/Tactical/Handle UI.h index f71f5c46e..3580f9281 100644 --- a/Tactical/Handle UI.h +++ b/Tactical/Handle UI.h @@ -29,6 +29,7 @@ #define MOVEUI_TARGET_JAR 10 #define MOVEUI_TARGET_CAN 11 #define MOVEUI_TARGET_REFUEL 12 +#define MOVEUI_TARGET_FORTIFICATION 13 #define MOVEUI_RETURN_ON_TARGET_MERC 1 diff --git a/Tactical/Interface Cursors.cpp b/Tactical/Interface Cursors.cpp index 0f1de7f47..e1c92a753 100644 --- a/Tactical/Interface Cursors.cpp +++ b/Tactical/Interface Cursors.cpp @@ -212,6 +212,9 @@ UICursor gUICursors[ NUM_UI_CURSORS ] = REFUEL_GREY_UICURSOR, UICURSOR_FREEFLOWING, CURSOR_FUEL, 0, REFUEL_RED_UICURSOR, UICURSOR_FREEFLOWING, CURSOR_FUEL_RED, 0, + FORTIFICATION_GREY_UICURSOR, UICURSOR_FREEFLOWING, CURSOR_FORTIFICATION, 0, + FORTIFICATION_RED_UICURSOR, UICURSOR_FREEFLOWING, CURSOR_FORTIFICATION_RED, 0, + }; diff --git a/Tactical/Interface Cursors.h b/Tactical/Interface Cursors.h index 599ea2c38..f15243617 100644 --- a/Tactical/Interface Cursors.h +++ b/Tactical/Interface Cursors.h @@ -186,6 +186,9 @@ typedef enum REFUEL_GREY_UICURSOR, REFUEL_RED_UICURSOR, + FORTIFICATION_GREY_UICURSOR, + FORTIFICATION_RED_UICURSOR, + NUM_UI_CURSORS } UICursorDefines; diff --git a/Tactical/Item Types.h b/Tactical/Item Types.h index cb72bb062..39b29d65b 100644 --- a/Tactical/Item Types.h +++ b/Tactical/Item Types.h @@ -173,6 +173,7 @@ typedef enum ATTACHMENT_SLOT{ #define JARCURS 21 #define TINCANCURS 22 #define REFUELCURS 23 +#define FORTICURS 24 #define CAMERARANGE 10 @@ -708,6 +709,50 @@ extern OBJECTTYPE gTempObject; #define PLAYER_NET_4_LVL_4 0x80000000 //2147483648 // ---------------------------------------------------------------- +// -------- added by Flugente: various item flags -------- +// flags used for various item properties (easier than adding 32 differently named variables). DO NOT CHANGE THEM, UNLESS YOU KNOW WHAT YOU ARE DOING!!! +// note that these should not be used to determine what kind of an attachment an item is, that is determined by attachmentclass and the AC_xxx flags above +#define EMPTY_SANDBAG 0x00000001 //1 +#define FULL_SANDBAG 0x00000002 //2 +#define SHOVEL 0x00000004 //4 +#define CONCERTINA 0x00000008 //8 + +/*#define WH40K_POWER_ARMOR 0x00000010 //16 +#define WH40K_POWER_PACK 0x00000020 //32 +#define WH40K_JUMPPACK 0x00000040 //64 +#define WH40K_DISPLACER 0x00000080 //128 + +#define WH40K_ROSARIUS 0x00000100 //256 +#define WH40K_SEAL 0x00000200 //512 +#define WH40K_POWER_WEAPON 0x00000400 //1024 +#define ENEMY_NET_4_LVL_3 0x00000800 //2048 + +#define ENEMY_NET_1_LVL_4 0x00001000 //4096 +#define ENEMY_NET_2_LVL_4 0x00002000 //8192 +#define ENEMY_NET_3_LVL_4 0x00004000 //16384 +#define ENEMY_NET_4_LVL_4 0x00008000 //32768 + +#define PLAYER_NET_1_LVL_1 0x00010000 //65536 +#define PLAYER_NET_2_LVL_1 0x00020000 //131072 +#define PLAYER_NET_3_LVL_1 0x00040000 //262144 +#define PLAYER_NET_4_LVL_1 0x00080000 //524288 + +#define PLAYER_NET_1_LVL_2 0x00100000 //1048576 +#define PLAYER_NET_2_LVL_2 0x00200000 //2097152 +#define PLAYER_NET_3_LVL_2 0x00400000 //4194304 +#define PLAYER_NET_4_LVL_2 0x00800000 //8388608 + +#define PLAYER_NET_1_LVL_3 0x01000000 //16777216 +#define PLAYER_NET_2_LVL_3 0x02000000 //33554432 +#define PLAYER_NET_3_LVL_3 0x04000000 //67108864 +#define PLAYER_NET_4_LVL_3 0x08000000 //134217728 + +#define PLAYER_NET_1_LVL_4 0x10000000 //268435456 +#define PLAYER_NET_2_LVL_4 0x20000000 //536870912 +#define PLAYER_NET_3_LVL_4 0x40000000 //1073741824 +#define PLAYER_NET_4_LVL_4 0x80000000 //2147483648*/ +// ---------------------------------------------------------------- + // replaces candamage //#define ITEM_DAMAGEABLE 0x0001 //// replaces canrepair @@ -962,6 +1007,8 @@ typedef struct // Flugente poison system INT16 bPoisonPercentage; + UINT32 usItemFlag; // bitflags to store various item properties (better than introducing 32 BOOLEAN values). If I only had thought of this earlier.... + } INVTYPE; // CHRISL: Added new structures to handle LBE gear and the two new XML files that will be needed to deal diff --git a/Tactical/Items.cpp b/Tactical/Items.cpp index 471fe1e20..34d5b5c7a 100644 --- a/Tactical/Items.cpp +++ b/Tactical/Items.cpp @@ -13938,3 +13938,9 @@ void CheckBombSpecifics( OBJECTTYPE * pObj, INT8* detonatortype, INT8* setting, *defusefrequency = (*pObj)[0]->data.bDefuseFrequency; } } + +// Flugente: check for specific flags +BOOLEAN HasItemFlag( UINT16 usItem, UINT32 aFlag ) +{ + return( (Item[usItem].usItemFlag & aFlag) != 0 ); +} diff --git a/Tactical/Items.h b/Tactical/Items.h index ca2c700e6..e4f07df2d 100644 --- a/Tactical/Items.h +++ b/Tactical/Items.h @@ -462,4 +462,7 @@ UINT64 GetAvailableAttachmentPoint ( OBJECTTYPE * pObject, UINT8 subObject ); // Flugente: check if and how a bomb has been set up void CheckBombSpecifics( OBJECTTYPE * pObj, INT8* detonatortype, INT8* setting, INT8* defusefrequency ); +// Flugente: check for specific flags +BOOLEAN HasItemFlag( UINT16 usItem, UINT32 aFlag ); + #endif diff --git a/Tactical/Overhead.cpp b/Tactical/Overhead.cpp index f1df4ae11..69f4857d7 100644 --- a/Tactical/Overhead.cpp +++ b/Tactical/Overhead.cpp @@ -1649,6 +1649,11 @@ BOOLEAN ExecuteOverhead( ) pSoldier->EVENT_SoldierBeginCutFence( pSoldier->aiData.sPendingActionData2, pSoldier->aiData.bPendingActionData3 ); pSoldier->aiData.ubPendingAction = NO_PENDING_ACTION; } + else if ( pSoldier->aiData.ubPendingAction == MERC_BUILD_FORTIFICATION ) + { + pSoldier->EVENT_SoldierBuildStructure( pSoldier->aiData.sPendingActionData2, pSoldier->aiData.bPendingActionData3 ); + pSoldier->aiData.ubPendingAction = NO_PENDING_ACTION; + } else if ( pSoldier->aiData.ubPendingAction == MERC_GIVEITEM ) { pSoldier->EVENT_SoldierBeginGiveItem( ); diff --git a/Tactical/Points.cpp b/Tactical/Points.cpp index 730b2ab15..b86353c57 100644 --- a/Tactical/Points.cpp +++ b/Tactical/Points.cpp @@ -3476,6 +3476,23 @@ INT16 GetAPsToUseCan( SOLDIERTYPE *pSoldier, INT32 usMapPos ) } +INT16 GetAPsToBuildFortification( SOLDIERTYPE *pSoldier, INT32 usMapPos ) +{ + INT16 sAPCost = 0; + + sAPCost = PlotPath( pSoldier, usMapPos, NO_COPYROUTE, NO_PLOT, TEMPORARY, (UINT16)pSoldier->usUIMovementMode, NOT_STEALTH, FORWARD, pSoldier->bActionPoints ); + + // If point cost is zero, return 0 + if ( sAPCost != 0 ) + { + // ADD APS TO PICKUP + sAPCost += APBPConstants[AP_FORTIFICATION]; + } + + return sAPCost; + +} + INT16 GetAPsToJumpOver( SOLDIERTYPE *pSoldier ) { diff --git a/Tactical/Points.h b/Tactical/Points.h index b210be340..a9be7f803 100644 --- a/Tactical/Points.h +++ b/Tactical/Points.h @@ -356,6 +356,8 @@ INT16 GetAPsToUseJar( SOLDIERTYPE *pSoldier, INT32 usMapPos ); INT16 GetAPsToUseCan( SOLDIERTYPE *pSoldier, INT32 usMapPos ); INT16 GetBPsTouseJar( SOLDIERTYPE *pSoldier ); +INT16 GetAPsToBuildFortification( SOLDIERTYPE *pSoldier, INT32 usMapPos ); // added by Flugente + INT16 GetAPsToJumpOver( SOLDIERTYPE *pSoldier ); void GetAPChargeForShootOrStabWRTGunRaises( SOLDIERTYPE *pSoldier, INT32 sGridNo, UINT8 ubAddTurningCost, BOOLEAN *pfChargeTurning, BOOLEAN *pfChargeRaise ); diff --git a/Tactical/Rotting Corpses.cpp b/Tactical/Rotting Corpses.cpp index 1b18818e2..ef4c965fa 100644 --- a/Tactical/Rotting Corpses.cpp +++ b/Tactical/Rotting Corpses.cpp @@ -2143,6 +2143,8 @@ void RaiseZombies( void ) if ( zombieshaverisen ) { + SetRenderFlags( RENDER_FLAG_FULL ); + #ifdef JA2TESTVERSION ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_BETAVERSION, L"A wave of zombies is created"); #endif diff --git a/Tactical/Soldier Control.cpp b/Tactical/Soldier Control.cpp index a84efacd9..4cf684e36 100644 --- a/Tactical/Soldier Control.cpp +++ b/Tactical/Soldier Control.cpp @@ -13403,7 +13403,12 @@ INT32 SOLDIERTYPE::GetDamageResistance(BOOLEAN fAutoResolve, BOOLEAN fCalcBreath else if (this->ubSoldierClass == SOLDIER_CLASS_ELITE && gGameExternalOptions.sEnemyEliteDamageResistance != 0) resistance += gGameExternalOptions.sEnemyEliteDamageResistance; else if (IsZombie()) - resistance += gGameExternalOptions.sEnemyZombieDamageResistance; + { + if ( fCalcBreathLoss ) + resistance += gGameExternalOptions.sEnemyZombieBreathDamageResistance; + else + resistance += gGameExternalOptions.sEnemyZombieDamageResistance; + } } ////////////////////////////////////////////////////////////////////////////////////// @@ -14370,6 +14375,103 @@ void SOLDIERTYPE::EVENT_SoldierBeginAttachCan( INT32 sGridNo, UINT8 ubDirection } +void SOLDIERTYPE::EVENT_SoldierBuildStructure( INT32 sGridNo, UINT8 ubDirection ) +{ + BOOLEAN fSuccess = FALSE; + // do checks here... + OBJECTTYPE* pObj = &(this->inv[HANDPOS]); + + if ( pObj && pObj->exists() && HasItemFlag(this->inv[ HANDPOS ].usItem, (EMPTY_SANDBAG|FULL_SANDBAG|SHOVEL|CONCERTINA)) ) + { + // CHANGE DIRECTION AND GOTO ANIMATION NOW + this->EVENT_SetSoldierDesiredDirection( ubDirection ); + this->EVENT_SetSoldierDirection( ubDirection ); + + if (!is_networked) + this->EVENT_InitNewSoldierAnim( CUTTING_FENCE, 0 , FALSE ); + else + this->ChangeSoldierState( CUTTING_FENCE, 0, 0 ); + + if ( HasItemFlag(this->inv[ HANDPOS ].usItem, (FULL_SANDBAG|CONCERTINA)) ) + { + // Build the thing + if ( BuildFortification( Item[ pObj->usItem ].usItemFlag ) ) + { + // Erase 'material' item from our hand - we 'used' it to build the structure + DeleteObj( &(this->inv[HANDPOS]) ); + + // we gain a bit of experience... + StatChange( this, STRAMT, 4, TRUE ); + StatChange( this, HEALTHAMT, 2, TRUE ); + + fSuccess = TRUE; + } + } + else if ( HasItemFlag(this->inv[ HANDPOS ].usItem, (EMPTY_SANDBAG)) ) + { + INT8 bOverTerrainType = GetTerrainType( sGridNo ); + if( bOverTerrainType == FLAT_GROUND || bOverTerrainType == DIRT_ROAD || bOverTerrainType == LOW_GRASS ) + { + // check if we have a shovel in our second hand + OBJECTTYPE* pShovelObj = &(this->inv[SECONDHANDPOS]); + + if ( pShovelObj && pShovelObj->exists() && HasItemFlag(this->inv[ SECONDHANDPOS ].usItem, (SHOVEL)) ) + { + // test: does there an item that is a filled sandbag exist? (xmls might have changed) + UINT16 fullsandbagnr = 1541; + if ( HasItemFlag(fullsandbagnr, FULL_SANDBAG) ) + { + // Erase 'material' item from our hand - we 'used' it to build the structure + INT8 bObjSlot = HANDPOS; + + CreateItem( fullsandbagnr, 100, &gTempObject ); + + SwapObjs( this, bObjSlot, &gTempObject, TRUE ); + + // we gain a bit of experience... + StatChange( this, STRAMT, 1, TRUE ); + StatChange( this, HEALTHAMT, 1, TRUE ); + + fSuccess = TRUE; + } + } + } + } + else if ( HasItemFlag(this->inv[ HANDPOS ].usItem, (SHOVEL)) ) + { + // Build the thing + if ( RemoveFortification( sGridNo ) ) + { + // test: does there an item that is a filled sandbag exist? (xmls might have changed) + UINT16 fullsandbagnr = 1541; + if ( HasItemFlag(fullsandbagnr, FULL_SANDBAG) ) + { + // Erase 'material' item from our hand - we 'used' it to build the structure + INT8 bObjSlot = HANDPOS; + + CreateItem( fullsandbagnr, 100, &gTempObject ); + + // now add a tripwire item to the floor, simulating that activating tripwire deactivates it + AddItemToPool( sGridNo, &gTempObject, 1, 0, 0, -1 ); + + // we gain a bit of experience... + StatChange( this, STRAMT, 3, TRUE ); + StatChange( this, HEALTHAMT, 2, TRUE ); + + fSuccess = TRUE; + } + } + } + } + + if ( !fSuccess ) + { + // Say NOTHING quote... + this->DoMercBattleSound( BATTLE_SOUND_NOTHING ); + } +} + + void SOLDIERTYPE::EVENT_SoldierBeginReloadRobot( INT32 sGridNo, UINT8 ubDirection, UINT8 ubMercSlot ) { UINT8 ubPerson; diff --git a/Tactical/Soldier Control.h b/Tactical/Soldier Control.h index 8ceb42fb2..9be10be74 100644 --- a/Tactical/Soldier Control.h +++ b/Tactical/Soldier Control.h @@ -220,6 +220,7 @@ enum MERC_TAKEBLOOD, MERC_ATTACH_CAN, MERC_FUEL_VEHICLE, + MERC_BUILD_FORTIFICATION, }; // ENUMERATIONS FOR THROW ACTIONS @@ -1240,6 +1241,8 @@ public: void EVENT_SoldierBeginAttachCan( INT32 sGridNo, UINT8 ubDirection ); void EVENT_BeginMercTurn( BOOLEAN fFromRealTime, INT32 iRealTimeCounter ); + void EVENT_SoldierBuildStructure( INT32 sGridNo, UINT8 ubDirection ); // added by Flugente + BOOLEAN EVENT_InternalGetNewSoldierPath( INT32 sDestGridNo, UINT16 usMovementAnim, BOOLEAN fFromUI, BOOLEAN fForceRestart ); void EVENT_InternalSetSoldierDestination( UINT16 usNewDirection, BOOLEAN fFromMove, UINT16 usAnimState ); void EVENT_InternalSetSoldierPosition( FLOAT dNewXPos, FLOAT dNewYPos ,BOOLEAN fUpdateDest, BOOLEAN fUpdateFinalDest, BOOLEAN fForceDelete ); diff --git a/Tactical/Structure Wrap.cpp b/Tactical/Structure Wrap.cpp index db384fd75..51f1f06b3 100644 --- a/Tactical/Structure Wrap.cpp +++ b/Tactical/Structure Wrap.cpp @@ -564,6 +564,22 @@ BOOLEAN IsRefuelableStructAtGridNo( INT32 sGridNo, UINT8 *pubID ) } +// Flugente: determine wether a fortification can be built on this position +BOOLEAN IsFortificationPossibleAtGridNo( INT32 sGridNo, UINT8 *pubID ) +{ + GetMouseMapPos( &sGridNo ); + + INT8 bOverTerrainType = GetTerrainType( sGridNo ); + if( bOverTerrainType == MED_WATER || bOverTerrainType == DEEP_WATER || bOverTerrainType == LOW_WATER ) + return FALSE; + + STRUCTURE * pStructure = NULL; + + pStructure = FindStructure( sGridNo, (STRUCTURE_OBSTACLE) ); + + return( pStructure == NULL ); +} + BOOLEAN IsCutWireFenceAtGridNo( INT32 sGridNo ) { diff --git a/Tactical/Structure Wrap.h b/Tactical/Structure Wrap.h index d46b1c971..ca2c28b3c 100644 --- a/Tactical/Structure Wrap.h +++ b/Tactical/Structure Wrap.h @@ -38,6 +38,7 @@ BOOLEAN IsCutWireFenceAtGridNo( INT32 sGridNo ); BOOLEAN IsRepairableStructAtGridNo( INT32 sGridNo, UINT8 *pubID ); BOOLEAN IsRefuelableStructAtGridNo( INT32 sGridNo, UINT8 *pubID ); +BOOLEAN IsFortificationPossibleAtGridNo( INT32 sGridNo, UINT8 *pubID ); // added by Flugente BOOLEAN IsRoofPresentAtGridNo( INT32 sGridNo ); diff --git a/Tactical/Turn Based Input.cpp b/Tactical/Turn Based Input.cpp index 410a697af..f784ffaa0 100644 --- a/Tactical/Turn Based Input.cpp +++ b/Tactical/Turn Based Input.cpp @@ -4440,7 +4440,10 @@ void GetKeyboardInput( UINT32 *puiNewEvent ) } else { - SetScopeMode(); + INT32 sGridNo = 0; + GetMouseMapPos( &sGridNo ); + RemoveFortification( sGridNo ); + //SetScopeMode(); } break; diff --git a/Tactical/UI Cursors.cpp b/Tactical/UI Cursors.cpp index 7def00e3a..f5a328781 100644 --- a/Tactical/UI Cursors.cpp +++ b/Tactical/UI Cursors.cpp @@ -53,6 +53,7 @@ UINT8 HandleRemoteCursor( SOLDIERTYPE *pSoldier, INT32 sGridNo, BOOLEAN fActivat UINT8 HandleBombCursor( SOLDIERTYPE *pSoldier, INT32 sGridNo, BOOLEAN fActivated, UINT32 uiCursorFlags ); UINT8 HandleJarCursor( SOLDIERTYPE *pSoldier, INT32 sGridNo, UINT32 uiCursorFlags ); UINT8 HandleTinCanCursor( SOLDIERTYPE *pSoldier, INT32 sGridNo, UINT32 uiCursorFlags ); +UINT8 HandleFortificationCursor( SOLDIERTYPE *pSoldier, INT32 sGridNo, UINT32 uiCursorFlags ); //added by Flugente extern BOOLEAN HandleCheckForBadChangeToGetThrough( SOLDIERTYPE *pSoldier, SOLDIERTYPE *pTargetSoldier, INT32 sTargetGridNo , INT8 bLevel ); @@ -298,6 +299,11 @@ UINT8 GetProperItemCursor( UINT8 ubSoldierID, UINT16 ubItemIndex, INT32 usMapPos ubCursorID = HandleRefuelCursor( pSoldier, sTargetGridNo, uiCursorFlags ); break; + case FORTICURS: + + ubCursorID = HandleFortificationCursor( pSoldier, sTargetGridNo, uiCursorFlags ); + break; + case INVALIDCURS: ubCursorID = INVALID_ACTION_UICURSOR; @@ -2138,6 +2144,45 @@ UINT8 HandleBombCursor( SOLDIERTYPE *pSoldier, INT32 sGridNo, BOOLEAN fActivated } } +UINT8 HandleFortificationCursor( SOLDIERTYPE *pSoldier, INT32 sGridNo, UINT32 uiCursorFlags ) +{ + // DRAW PATH TO GUY + HandleUIMovementCursor( pSoldier, uiCursorFlags, sGridNo, MOVEUI_TARGET_FORTIFICATION ); + + if ( pSoldier->pathing.bLevel != 0 ) + return( FORTIFICATION_RED_UICURSOR ); + + // if we have an empty sandbag in our hands, we also need to have a shovel in our second hand, otherwise we can't fill it + if ( HasItemFlag( (&(pSoldier->inv[HANDPOS]))->usItem, (EMPTY_SANDBAG)) ) + { + // check if we have a shovel in our second hand + OBJECTTYPE* pShovelObj = &(pSoldier->inv[SECONDHANDPOS]); + + if ( !pShovelObj || !(pShovelObj->exists()) || !HasItemFlag(pSoldier->inv[ SECONDHANDPOS ].usItem, (SHOVEL)) ) + { + return( FORTIFICATION_RED_UICURSOR ); + } + } + + if ( HasItemFlag( (&(pSoldier->inv[HANDPOS]))->usItem, (SHOVEL)) ) + { + STRUCTURE* pStruct = FindStructure(sGridNo, STRUCTURE_GENERIC); + + if ( pStruct ) + { + return( FORTIFICATION_GREY_UICURSOR ); + } + } + + // can we build something here? + if ( IsFortificationPossibleAtGridNo( sGridNo, NULL ) && pSoldier->pathing.bLevel == 0 ) + { + return( FORTIFICATION_GREY_UICURSOR ); + } + + return( FORTIFICATION_RED_UICURSOR ); +} + void HandleEndConfirmCursor( SOLDIERTYPE *pSoldier ) @@ -2596,6 +2641,10 @@ UINT8 GetActionModeCursor( SOLDIERTYPE *pSoldier ) } } + // Flugente: cursor for building fortifications + if ( HasItemFlag(usInHand, (EMPTY_SANDBAG|FULL_SANDBAG|SHOVEL|CONCERTINA)) ) + ubCursor = FORTICURS; + // Now check our terrain to see if we cannot do the action now... if ( WaterTooDeepForAttacks( pSoldier->sGridNo) ) { diff --git a/Utils/Cursors.cpp b/Utils/Cursors.cpp index 76acd5756..5f224fb1c 100644 --- a/Utils/Cursors.cpp +++ b/Utils/Cursors.cpp @@ -118,6 +118,10 @@ CursorFileData CursorFileDatabase[] = { "CURSORS\\cur_swit.sti" , FALSE, 0, 0, 0, NULL }, { "CURSORS\\bullseye.sti" , FALSE, 0, 0, 0, NULL }, { "CURSORS\\deadleap.sti" , FALSE, 0, 0, 0, NULL }, + + { "CURSORS\\hammer.sti" , FALSE, 0, ANIMATED_CURSOR, 3, NULL }, // Flugente: fortification stuff + { "CURSORS\\hammer_r.sti" , FALSE, 0, ANIMATED_CURSOR, 3, NULL }, + { "CURSORS\\can_01.sti" , FALSE, 0, 0, 0, NULL }, { "CURSORS\\can_02.sti" , FALSE, 0, 0, 0, NULL }, { "CURSORS\\cur_tagr_ncth.sti" , FALSE, 0, ANIMATED_CURSOR, 7, NULL }, @@ -1178,6 +1182,27 @@ CursorData CursorDatabase[] = 0, 0, 0, 0, 0, 2, CENTER_CURSOR, CENTER_CURSOR , 0, 0 , 0, 0 }, + /*{ C_TRINGS, 6, 0, HIDE_SUBCURSOR, HIDE_SUBCURSOR, + C_FORTIFICATION , 0, 0, CENTER_SUBCURSOR, CENTER_SUBCURSOR, + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + 2, CENTER_CURSOR, CENTER_CURSOR, 0, 0 , 0, 0 },*/ + + { C_TRINGS, 6, 0, HIDE_SUBCURSOR, HIDE_SUBCURSOR, + C_FORTIFICATION , 0, 0, CENTER_SUBCURSOR, CENTER_SUBCURSOR, + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + 2, CENTER_CURSOR, CENTER_CURSOR, 0, 0 , 0, 0 }, + + { C_TRINGS, 6, 0, HIDE_SUBCURSOR, HIDE_SUBCURSOR, + C_FORTIFICATION_RED , 0, 0, CENTER_SUBCURSOR, CENTER_SUBCURSOR, + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + 2, CENTER_CURSOR, CENTER_CURSOR, 0, 0 , 0, 0 }, + { C_TRINGS, 6, 0, HIDE_SUBCURSOR, HIDE_SUBCURSOR, C_FUEL , 0, 0, CENTER_SUBCURSOR, CENTER_SUBCURSOR, 0, 0, 0, 0, 0, diff --git a/Utils/Cursors.h b/Utils/Cursors.h index 2dcacae9c..660a44718 100644 --- a/Utils/Cursors.h +++ b/Utils/Cursors.h @@ -152,6 +152,10 @@ typedef enum CURSOR_STRATEGIC_BULLSEYE, CURSOR_JUMP_OVER, + + CURSOR_FORTIFICATION, + CURSOR_FORTIFICATION_RED, + CURSOR_FUEL, CURSOR_FUEL_RED, @@ -225,6 +229,8 @@ typedef enum C_EXCHANGE, C_BULLSEYE, C_JUMPOVER, + C_FORTIFICATION, + C_FORTIFICATION_RED, C_FUEL, C_FUEL_RED, C_ACTIONMODERED_NCTH, diff --git a/Utils/XML_Items.cpp b/Utils/XML_Items.cpp index 54df5473e..a7f0ffaa9 100644 --- a/Utils/XML_Items.cpp +++ b/Utils/XML_Items.cpp @@ -265,8 +265,8 @@ itemStartElementHandle(void *userData, const XML_Char *name, const XML_Char **at strcmp(name, "Directional") == 0 || strcmp(name, "DrugType") == 0 || strcmp(name, "BlockIronSight") == 0 || - // Flugente: poison system strcmp(name, "PoisonPercentage") == 0 || + strcmp(name, "ItemFlag") == 0 || strcmp(name, "fFlags") == 0 )) { @@ -1333,12 +1333,16 @@ itemEndElementHandle(void *userData, const XML_Char *name) pData->curElement = ELEMENT; pData->curItem.blockironsight = (BOOLEAN) atol(pData->szCharData); } - // Flugente poison system else if(strcmp(name, "PoisonPercentage") == 0) { pData->curElement = ELEMENT; pData->curItem.bPoisonPercentage = (INT16) atol(pData->szCharData); } + else if(strcmp(name, "ItemFlag") == 0) + { + pData->curElement = ELEMENT; + pData->curItem.usItemFlag = (UINT32) atol(pData->szCharData); + } pData->maxReadDepth--; @@ -1954,6 +1958,8 @@ BOOLEAN WriteItemStats() FilePrintf(hFile,"\t\t%d\r\n", Item[cnt].drugtype ); FilePrintf(hFile,"\t\t%d\r\n", Item[cnt].blockironsight ); + + FilePrintf(hFile,"\t\t%d\r\n", Item[cnt].usItemFlag ); // Flugente poison system FilePrintf(hFile,"\t\t%d\r\n", Item[cnt].bPoisonPercentage );