From 9ea7ebf393c63e3fc664c38d06d3439ae55230bf Mon Sep 17 00:00:00 2001 From: Flugente Date: Sun, 6 Jan 2013 03:51:09 +0000 Subject: [PATCH] New Feature: Multi-turn actions allow mercs to perform actions intended on a specific gridno over multiple turns. Currently demonstrated on the adding/removing/filling of sandbags. git-svn-id: https://ja2svn.mooo.com/source/ja2/trunk/GameSource/ja2_v1.13/Build@5777 3b4a5df2-a311-0410-b5c6-a8a6f20db521 --- GameSettings.cpp | 29 ++-- GameVersion.h | 3 +- Init.h | 22 ++- SaveLoadGame.cpp | 25 ++- Tactical/Faces.cpp | 7 + Tactical/Handle Items.cpp | 26 +-- Tactical/Handle Items.h | 2 +- Tactical/Handle UI.cpp | 3 +- Tactical/Points.cpp | 11 -- Tactical/Points.h | 1 - Tactical/Soldier Control.cpp | 322 +++++++++++++++++++++++++++-------- Tactical/Soldier Control.h | 23 ++- Tactical/Structure Wrap.cpp | 2 +- Tactical/Tactical Turns.cpp | 3 + Tactical/TeamTurns.cpp | 13 +- Utils/Text.h | 13 ++ Utils/_ChineseText.cpp | 9 + Utils/_DutchText.cpp | 9 + Utils/_EnglishText.cpp | 9 + Utils/_FrenchText.cpp | 9 + Utils/_GermanText.cpp | 9 + Utils/_ItalianText.cpp | 9 + Utils/_PolishText.cpp | 9 + Utils/_RussianText.cpp | 9 + Utils/_TaiwaneseText.cpp | 9 + 25 files changed, 462 insertions(+), 124 deletions(-) diff --git a/GameSettings.cpp b/GameSettings.cpp index 038ae806..0554ba09 100644 --- a/GameSettings.cpp +++ b/GameSettings.cpp @@ -2415,6 +2415,16 @@ void LoadGameAPBPConstants() APBPConstants[AP_INV_TO_BIG_POCKET] = DynamicAdjustAPConstants(iniReader.ReadInteger("APConstants","AP_INV_TO_BIG_POCKET",4),4); APBPConstants[AP_INV_TO_SMALL_POCKET] = DynamicAdjustAPConstants(iniReader.ReadInteger("APConstants","AP_INV_TO_SMALL_POCKET",5),5); APBPConstants[AP_INV_MAX_COST] = DynamicAdjustAPConstants(iniReader.ReadInteger("APConstants","AP_INV_MAX_COST",25),25); + 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", 250), 250); + APBPConstants[AP_REMOVE_FORTIFICATION] = DynamicAdjustAPConstants(iniReader.ReadInteger("APConstants","AP_REMOVE_FORTIFICATION", 150), 150); + APBPConstants[AP_FILL_SANDBAG] = DynamicAdjustAPConstants(iniReader.ReadInteger("APConstants","AP_FILL_SANDBAG", 200), 200); + APBPConstants[AP_EAT] = DynamicAdjustAPConstants(iniReader.ReadInteger("APConstants","AP_EAT",80),80); + APBPConstants[AP_CLEANINGKIT] = DynamicAdjustAPConstants(iniReader.ReadInteger("APConstants","AP_CLEANINGKIT",80),80); + APBPConstants[AP_INVENTORY_EXPLOSIVE_ACTIVATE] = DynamicAdjustAPConstants(iniReader.ReadInteger("APConstants","AP_INVENTORY_EXPLOSIVE_ACTIVATE",20),20); + APBPConstants[AP_DISGUISE] = DynamicAdjustAPConstants(iniReader.ReadInteger("APConstants","AP_DISGUISE",80),80); + APBPConstants[AP_HANDCUFF] = DynamicAdjustAPConstants(iniReader.ReadInteger("APConstants","AP_HANDCUFF",50),50); APBPConstants[DEFAULT_APS] = DynamicAdjustAPConstants(iniReader.ReadInteger("APConstants","DEFAULT_APS",80),80); APBPConstants[DEFAULT_AIMSKILL] = iniReader.ReadInteger("APConstants","DEFAULT_AIMSKILL",80); @@ -2465,21 +2475,14 @@ void LoadGameAPBPConstants() APBPConstants[BP_JUMP_OVER] = iniReader.ReadInteger("BPConstants","BP_JUMP_OVER",250); APBPConstants[BP_BACK_PACK] = iniReader.ReadInteger("APConstants","BP_JUMP_OVER",50); APBPConstants[BP_WORK_ZIPPER] = iniReader.ReadInteger("APConstants","BP_WORK_ZIPPER",250); - APBPConstants[BP_UNJAM] = iniReader.ReadInteger("APConstants","BP_WORK_ZIPPER",0); - + APBPConstants[BP_UNJAM] = iniReader.ReadInteger("APConstants","BP_WORK_ZIPPER",0); APBPConstants[BP_JUMPOFFWALL] = iniReader.ReadInteger("BPConstants","BP_JUMPOFFWALL",250); APBPConstants[BP_JUMPWALL] = iniReader.ReadInteger("BPConstants","BP_JUMPWALL",500); - - 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); - APBPConstants[AP_EAT] = DynamicAdjustAPConstants(iniReader.ReadInteger("APConstants","AP_EAT",80),80); - APBPConstants[AP_CLEANINGKIT] = DynamicAdjustAPConstants(iniReader.ReadInteger("APConstants","AP_CLEANINGKIT",80),80); - APBPConstants[AP_INVENTORY_EXPLOSIVE_ACTIVATE] = DynamicAdjustAPConstants(iniReader.ReadInteger("APConstants","AP_INVENTORY_EXPLOSIVE_ACTIVATE",20),20); - APBPConstants[AP_DISGUISE] = DynamicAdjustAPConstants(iniReader.ReadInteger("APConstants","AP_DISGUISE",80),80); - APBPConstants[AP_HANDCUFF] = DynamicAdjustAPConstants(iniReader.ReadInteger("APConstants","AP_HANDCUFF",50),50); - + APBPConstants[BP_FORTIFICATION] = iniReader.ReadInteger("BPConstants","BP_FORTIFICATION", 700); + APBPConstants[BP_REMOVE_FORTIFICATION] = iniReader.ReadInteger("BPConstants","BP_REMOVE_FORTIFICATION", 400); + APBPConstants[BP_FILL_SANDBAG] = iniReader.ReadInteger("BPConstants","BP_FILL_SANDBAG", 500); + APBPConstants[BP_HANDCUFF] = iniReader.ReadInteger("BPConstants","BP_HANDCUFF", 100); + SetupMaxActionPointsAnimation(); #undef ReadInteger } diff --git a/GameVersion.h b/GameVersion.h index 85497657..ff323764 100644 --- a/GameVersion.h +++ b/GameVersion.h @@ -20,8 +20,9 @@ extern CHAR16 zTrackingNumber[16]; // // Keeps track of the saved game version. Increment the saved game version whenever // you will invalidate the saved game file -#define SAVE_GAME_VERSION 137 // 134 // 133 // 132 //131 //125 //124 //114 //113 //112 //111 //110 //109 //108 //107 //106 //105 //104 //103 //102 //101 //100 // 99 +#define SAVE_GAME_VERSION 138 +#define MULTITURN_ACTIONS 138 // Flugente: multiturn actions #define EXTERNALISED_MERCHANTS 137 // Flugente: increased the number of merchants and externalised their data #define PRISON_SYSTEM 136 // Flugente: added var to the sector structure. Shouldn't break compatibility, but let's be safe #define DUAL_BURST_ADDED 135 diff --git a/Init.h b/Init.h index 92cdc58e..20f4025f 100644 --- a/Init.h +++ b/Init.h @@ -143,6 +143,16 @@ AP_INV_TO_BIG_POCKET, AP_INV_TO_SMALL_POCKET, //Max inventory cost AP_INV_MAX_COST, +AP_JUMPWALL, +AP_JUMPOFFWALL, +AP_FORTIFICATION, +AP_REMOVE_FORTIFICATION, +AP_FILL_SANDBAG, +AP_EAT, +AP_CLEANINGKIT, +AP_INVENTORY_EXPLOSIVE_ACTIVATE, +AP_DISGUISE, +AP_HANDCUFF, DEFAULT_APS, DEFAULT_AIMSKILL, @@ -195,14 +205,10 @@ BP_WORK_ZIPPER, BP_UNJAM, BP_JUMPOFFWALL, BP_JUMPWALL, -AP_JUMPWALL, -AP_JUMPOFFWALL, -AP_FORTIFICATION, -AP_EAT, -AP_CLEANINGKIT, -AP_INVENTORY_EXPLOSIVE_ACTIVATE, -AP_DISGUISE, -AP_HANDCUFF, +BP_FORTIFICATION, +BP_REMOVE_FORTIFICATION, +BP_FILL_SANDBAG, +BP_HANDCUFF, TOTAL_APBP_VALUES }; #endif diff --git a/SaveLoadGame.cpp b/SaveLoadGame.cpp index 1fb2af2e..3cca87a9 100644 --- a/SaveLoadGame.cpp +++ b/SaveLoadGame.cpp @@ -2024,12 +2024,31 @@ BOOLEAN SOLDIERTYPE::Load(HWFILE hFile) numBytesRead = ReadFieldByField(hFile, &this->usStarveDamageHealth, sizeof(usStarveDamageHealth), sizeof(UINT8), numBytesRead); numBytesRead = ReadFieldByField(hFile, &this->usStarveDamageStrength, sizeof(usStarveDamageStrength), sizeof(UINT8), numBytesRead); - numBytesRead = ReadFieldByField(hFile, &this->ubFiller, sizeof(ubFiller), sizeof(UINT8), numBytesRead); + if ( guiCurrentSaveGameVersion >= MULTITURN_ACTIONS ) + { + numBytesRead = ReadFieldByField(hFile, &this->bOverTurnAPS, sizeof(bOverTurnAPS), sizeof(INT16), numBytesRead); + numBytesRead = ReadFieldByField(hFile, &this->sMTActionGridNo, sizeof(sMTActionGridNo), sizeof(INT32), numBytesRead); + numBytesRead = ReadFieldByField(hFile, &this->usMultiTurnAction, sizeof(usMultiTurnAction), sizeof(UINT8), numBytesRead); + + numBytesRead = ReadFieldByField(hFile, &this->ubFiller, sizeof(ubFiller), sizeof(UINT8), numBytesRead); + } + else + { + this->bOverTurnAPS = 0; + this->sMTActionGridNo = NOWHERE; + this->usMultiTurnAction = 0; + + // as we added new variables, fillersize was reduced, here we account for that. We have to also read the existing fillers that now do not exist anymore + const UINT8 tmp = sizeof(bOverTurnAPS) + sizeof(this->sMTActionGridNo) + sizeof(usMultiTurnAction); + UINT8 blarg[tmp]; + numBytesRead = ReadFieldByField(hFile, &blarg, tmp, sizeof(UINT8), numBytesRead); + + numBytesRead = ReadFieldByField(hFile, &this->ubFiller, sizeof(ubFiller), sizeof(UINT8), numBytesRead); + } } else { //CHRISL: We have to make sure we add a buffer to account for the lack of ubInterruptCounter and that buffer needs to be a full DWORD in size - buffer = 0; for(int i = 0; i < sizeof(bFoodLevel); ++i) buffer++; while((buffer%4) > 0) @@ -2054,8 +2073,6 @@ BOOLEAN SOLDIERTYPE::Load(HWFILE hFile) buffer++; while((buffer%4) > 0) buffer++; - - numBytesRead += buffer; } #ifdef JA2UB diff --git a/Tactical/Faces.cpp b/Tactical/Faces.cpp index c3890fe8..2e65b50a 100644 --- a/Tactical/Faces.cpp +++ b/Tactical/Faces.cpp @@ -2327,6 +2327,13 @@ void HandleRenderFaceAdjustments( FACETYPE *pFace, BOOLEAN fDisplayBuffer, BOOLE DoRightIcon( uiRenderBuffer, pFace, sFaceX, sFaceY, bNumRightIcons, 22 ); bNumRightIcons++; } + + // Flugente: add an icon if we are performing a multi-turn action + if ( MercPtrs[ pFace->ubSoldierID ]->GetMultiTurnAction() > MTA_NONE ) + { + DoRightIcon( uiRenderBuffer, pFace, sFaceX, sFaceY, bNumRightIcons, 24 ); + bNumRightIcons++; + } } switch( pSoldier->bAssignment ) diff --git a/Tactical/Handle Items.cpp b/Tactical/Handle Items.cpp index 203eba14..f8890a46 100644 --- a/Tactical/Handle Items.cpp +++ b/Tactical/Handle Items.cpp @@ -1140,6 +1140,8 @@ INT32 HandleItem( SOLDIERTYPE *pSoldier, INT32 sGridNo, INT8 bLevel, UINT16 usHa // 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)) ) { + sAPCost = APBPConstants[AP_FILL_SANDBAG]; + // check if we have a shovel in our second hand OBJECTTYPE* pShovelObj = &(pSoldier->inv[SECONDHANDPOS]); @@ -1155,10 +1157,11 @@ INT32 HandleItem( SOLDIERTYPE *pSoldier, INT32 sGridNo, INT8 bLevel, UINT16 usHa 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)) ) + // if we have a shovel in our hands, the targeted gridno must be a fortification (debris will do for this check) + else if ( HasItemFlag(usHandItem, (SHOVEL)) ) { + sAPCost = APBPConstants[AP_REMOVE_FORTIFICATION]; + STRUCTURE* pStruct = FindStructure(sGridNo, STRUCTURE_GENERIC); if ( !pStruct ) @@ -1166,16 +1169,20 @@ INT32 HandleItem( SOLDIERTYPE *pSoldier, INT32 sGridNo, INT8 bLevel, UINT16 usHa return( ITEM_HANDLE_REFUSAL ); } } + else + { + sAPCost = APBPConstants[AP_FORTIFICATION]; + } 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 ) ) + //if ( EnoughPoints( pSoldier, sAPCost, 0, fFromUI ) ) + if ( pSoldier->bActionPoints > 0 ) { // CHECK IF WE ARE AT THIS GRIDNO NOW if ( pSoldier->sGridNo != sActionGridNo ) @@ -6251,9 +6258,8 @@ void SoldierStealItemFromSoldier( SOLDIERTYPE *pSoldier, SOLDIERTYPE *pOpponent, SetCustomizableTimerCallbackAndDelay( 1000, CheckForPickedOwnership, TRUE ); } -BOOLEAN BuildFortification( UINT32 flag ) -{ - INT32 sGridNo; +BOOLEAN BuildFortification( INT32 sGridNo, UINT32 flag ) +{ UINT32 fHeadType; UINT16 usUseIndex; UINT16 usUseObjIndex = 0; @@ -6268,9 +6274,7 @@ BOOLEAN BuildFortification( UINT32 flag ) { return FALSE; } - - GetMouseMapPos( &sGridNo ); - + if( InARoom( sGridNo, NULL ) ) return FALSE; diff --git a/Tactical/Handle Items.h b/Tactical/Handle Items.h index 5db2dc66..90d60259 100644 --- a/Tactical/Handle Items.h +++ b/Tactical/Handle Items.h @@ -180,7 +180,7 @@ 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 BuildFortification( INT32 sGridNo , UINT32 flag = FULL_SANDBAG ); // Flugente: build a structure, return true if sucessful BOOLEAN RemoveFortification( INT32 sGridNo ); extern ITEM_POOL *gpItemPool;//dnl ch26 210909 diff --git a/Tactical/Handle UI.cpp b/Tactical/Handle UI.cpp index e362e37a..9249e747 100644 --- a/Tactical/Handle UI.cpp +++ b/Tactical/Handle UI.cpp @@ -4132,8 +4132,7 @@ INT8 DrawUIMovementPath( SOLDIERTYPE *pSoldier, INT32 usMapPos, UINT32 uiFlags ) sActionGridNo = usMapPos; } - sAPCost = GetAPsToBuildFortification( pSoldier, sActionGridNo ); - + sAPCost = APBPConstants[AP_FORTIFICATION]; sAPCost += UIPlotPath( pSoldier, sActionGridNo, NO_COPYROUTE, fPlot, TEMPORARY, (UINT16)pSoldier->usUIMovementMode, NOT_STEALTH, FORWARD, pSoldier->bActionPoints); if ( sActionGridNo != pSoldier->sGridNo ) diff --git a/Tactical/Points.cpp b/Tactical/Points.cpp index f7a453db..6678426e 100644 --- a/Tactical/Points.cpp +++ b/Tactical/Points.cpp @@ -3681,17 +3681,6 @@ 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 ); - - sAPCost += APBPConstants[AP_FORTIFICATION]; - - return sAPCost; -} - INT16 GetAPsToHandcuff( SOLDIERTYPE *pSoldier, INT32 usMapPos ) { INT16 sAPCost = 0; diff --git a/Tactical/Points.h b/Tactical/Points.h index 93955f57..6e18cd37 100644 --- a/Tactical/Points.h +++ b/Tactical/Points.h @@ -356,7 +356,6 @@ 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 GetAPsToHandcuff( SOLDIERTYPE *pSoldier, INT32 usMapPos ); // added by Flugente INT16 GetAPsToJumpOver( SOLDIERTYPE *pSoldier ); diff --git a/Tactical/Soldier Control.cpp b/Tactical/Soldier Control.cpp index 0f4715ff..b0bf2161 100644 --- a/Tactical/Soldier Control.cpp +++ b/Tactical/Soldier Control.cpp @@ -15474,6 +15474,240 @@ BOOLEAN SOLDIERTYPE::IsAssassin() return FALSE; } +UINT8 SOLDIERTYPE::GetMultiTurnAction() +{ + return usMultiTurnAction; +} + +void SOLDIERTYPE::StartMultiTurnAction(UINT8 usActionType) +{ + // check wether we can perform any action at all + if ( !this->bActive || !this->bInSector || this->stats.bLife < OKLIFE || TileIsOutOfBounds(this->sGridNo) || this->pathing.bLevel != 0 || gAnimControl[ this->usAnimState ].ubEndHeight == ANIM_PRONE || gAnimControl[ this->usAnimState ].ubEndHeight == ANIM_STAND || this->bCollapsed ) + return; + + // if we already perform a multi-turn action, overwrite it + if ( GetMultiTurnAction() > MTA_NONE ) + CancelMultiTurnAction(FALSE); + + // set up the action and costs + usMultiTurnAction = usActionType; + + // the action shall be performed on the gridno directly in front of us + sMTActionGridNo = NewGridNo( this->sGridNo, DirectionInc( this->ubDirection ) ); + + // for now, adding and removing a structure has the same AP cost - that might change in the future + switch( usMultiTurnAction ) + { + case MTA_FORTIFY: + bOverTurnAPS = APBPConstants[AP_FORTIFICATION]; + break; + case MTA_REMOVE_FORTIFY: + bOverTurnAPS = APBPConstants[AP_REMOVE_FORTIFICATION]; + break; + case MTA_FILL_SANDBAG: + bOverTurnAPS = APBPConstants[AP_FILL_SANDBAG]; + break; + } + + // immediately starting the action would leave us without APs, thus removing the benefit of multi-turn actions (ability to do something else while performing a longer action) + // for this reason, we only do this when we are not in combat + if ( !( gTacticalStatus.uiFlags & TURNBASED && gTacticalStatus.uiFlags & INCOMBAT ) ) + UpdateMultiTurnAction(); +} + +void SOLDIERTYPE::CancelMultiTurnAction(BOOLEAN fFinished) +{ + // stop action + if ( !fFinished ) + ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, szMTATextStr[STR_MTA_CANCEL], this->name, szMTATextStr[this->usMultiTurnAction] ); + + bOverTurnAPS = 0; + sMTActionGridNo = NOWHERE; + usMultiTurnAction = MTA_NONE; +} + +// if we are doing any multiturn-action, remove our current APs from it, and if possible, perform the action and finish the process +BOOLEAN SOLDIERTYPE::UpdateMultiTurnAction() +{ + // nothing to do here + if ( this->usMultiTurnAction == MTA_NONE ) + return FALSE; + + // check wether we can perform any action at all + if ( !this->bActive || !this->bInSector || this->stats.bLife < OKLIFE || TileIsOutOfBounds(this->sGridNo) || TileIsOutOfBounds(this->sMTActionGridNo) || this->pathing.bLevel != 0 || gAnimControl[ this->usAnimState ].ubEndHeight == ANIM_PRONE || gAnimControl[ this->usAnimState ].ubEndHeight == ANIM_STAND || this->bCollapsed ) + { + CancelMultiTurnAction(FALSE); + return FALSE; + } + + // check wether our selected action can still be performed + BOOLEAN fActionStillValid = TRUE; + + // determine the gridno before us and the item we have in our main hand, this is enough for the current actions + OBJECTTYPE* pObj = &(this->inv[HANDPOS]); + if ( !pObj || !(pObj->exists()) ) + fActionStillValid = FALSE; + + // error if the gridno we started working on is not the gridno we are currently looking at + if ( this->sMTActionGridNo != NewGridNo( this->sGridNo, DirectionInc( this->ubDirection ) ) ) + fActionStillValid = FALSE; + + if ( !fActionStillValid ) + { + CancelMultiTurnAction(FALSE); + return FALSE; + } + + // a static variable that is the item number of sandbags. Hardcoding item numbers is bad, that's why we will search for it. + static UINT16 fullsandbagnr = 1541; + + INT16 entireapcost = 0; + INT16 entirebpcost = 0; + switch( usMultiTurnAction ) + { + case MTA_FORTIFY: + { + entireapcost = APBPConstants[AP_FORTIFICATION]; + entirebpcost = APBPConstants[BP_FORTIFICATION]; + + if ( !IsFortificationPossibleAtGridNo( this->sMTActionGridNo ) ) + fActionStillValid = FALSE; + } + break; + case MTA_REMOVE_FORTIFY: + { + entireapcost = APBPConstants[AP_REMOVE_FORTIFICATION]; + entirebpcost = APBPConstants[BP_REMOVE_FORTIFICATION]; + + if ( !HasItemFlag(this->inv[ HANDPOS ].usItem, (SHOVEL)) ) + fActionStillValid = FALSE; + } + break; + + case MTA_FILL_SANDBAG: + { + entireapcost = APBPConstants[AP_FILL_SANDBAG]; + entirebpcost = APBPConstants[BP_FILL_SANDBAG]; + + if ( !HasItemFlag(this->inv[ HANDPOS ].usItem, EMPTY_SANDBAG) ) + fActionStillValid = FALSE; + else + { + INT8 bOverTerrainType = GetTerrainType( sGridNo ); + if( bOverTerrainType != FLAT_GROUND && bOverTerrainType != DIRT_ROAD && bOverTerrainType != LOW_GRASS ) + fActionStillValid = FALSE; + else + { + // 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) ) + fActionStillValid = FALSE; + } + } + } + break; + } + + if ( !fActionStillValid ) + { + CancelMultiTurnAction(FALSE); + return FALSE; + } + + // if we are not in turnbased and no enemies are around, we reduce the number of necessary action points to 0. No need to keep waiting if there's nobody around anyway + if ( !( gTacticalStatus.uiFlags & TURNBASED && gTacticalStatus.uiFlags & INCOMBAT ) ) + bOverTurnAPS = 0; + // otherwise this might take longer, so we refresh our animation + else + { + // refresh animations + if (!is_networked) + this->EVENT_InitNewSoldierAnim( CUTTING_FENCE, 0 , FALSE ); + else + this->ChangeSoldierState( CUTTING_FENCE, 0, 0 ); + + // as setting the new animation costs APBPConstants[AP_USEWIRECUTTERS] APs every time, account for that + this->bActionPoints += APBPConstants[AP_USEWIRECUTTERS]; + } + + // if we can afford it, do it now + if ( bOverTurnAPS <= this->bActionPoints ) + { + switch( usMultiTurnAction ) + { + case MTA_FORTIFY: + { + // Build the thing + if ( BuildFortification( this->sMTActionGridNo, Item[ pObj->usItem ].usItemFlag ) ) + { + // Erase 'material' item from our hand - we 'use' 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 ); + } + } + break; + + case MTA_REMOVE_FORTIFY: + { + if ( RemoveFortification( this->sMTActionGridNo ) ) + { + // eventually search for the number of a sandbag item + if ( HasItemFlag(fullsandbagnr, FULL_SANDBAG) || GetFirstItemWithFlag(&fullsandbagnr, FULL_SANDBAG) ) + { + CreateItem( fullsandbagnr, 100, &gTempObject ); + + AddItemToPool( this->sMTActionGridNo, &gTempObject, 1, 0, 0, -1 ); + + // we gain a bit of experience... + StatChange( this, STRAMT, 3, TRUE ); + StatChange( this, HEALTHAMT, 2, TRUE ); + } + } + } + break; + + case MTA_FILL_SANDBAG: + { + // eventually search for the number of a sandbag item + if ( HasItemFlag(fullsandbagnr, FULL_SANDBAG) || GetFirstItemWithFlag(&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 ); + } + } + break; + } + + if ( entireapcost > 0 ) + DeductPoints( this, bOverTurnAPS, (INT32)(entirebpcost * this->bOverTurnAPS / entireapcost), 0); + + // we're done here! + CancelMultiTurnAction(TRUE); + } + // remove the costs as much as we can + else if ( this->bActionPoints > 0 ) + { + INT16 oldAPs = this->bActionPoints; + if ( bOverTurnAPS > 0 ) + DeductPoints( this, this->bActionPoints, (INT32)(entirebpcost * this->bActionPoints / entireapcost), 0); + + bOverTurnAPS -= oldAPs; + } + + return TRUE; +} + INT32 CheckBleeding( SOLDIERTYPE *pSoldier ) { INT8 bBandaged; //,savedOurTurn; @@ -16255,8 +16489,6 @@ extern UINT8 NumEnemiesInAnySector( INT16 sSectorX, INT16 sSectorY, INT16 sSecto void SOLDIERTYPE::EVENT_SoldierBuildStructure( INT32 sGridNo, UINT8 ubDirection ) { - // a static variable that is the item number of sandbags. Hardcoding item numbers is bad, that's why we will search for it. - static UINT16 fullsandbagnr = 1541; BOOLEAN fSuccess = FALSE; // if specified by the ini, building/disassembling stuff is disabled while enemies are around @@ -16275,36 +16507,12 @@ void SOLDIERTYPE::EVENT_SoldierBuildStructure( INT32 sGridNo, UINT8 ubDirection 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 ); - - // ugly, ugly hack: as e do not have a special animation for this action, we use the 'cut a fence' animations - // however, APs are directly deducted because of this. So we can't use the AP_FORTIFICATION costs - // for this reason, we deduct the APS still missing here - INT16 restAPs = max(0, APBPConstants[AP_FORTIFICATION] - APBPConstants[AP_USEWIRECUTTERS]); - 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]) ); + this->StartMultiTurnAction( MTA_FORTIFY ); - // we gain a bit of experience... - StatChange( this, STRAMT, 4, TRUE ); - StatChange( this, HEALTHAMT, 2, TRUE ); - - DeductPoints( this, restAPs, 0, AFTERACTION_INTERRUPT ); - - fSuccess = TRUE; - } + fSuccess = TRUE; } else if ( HasItemFlag(this->inv[ HANDPOS ].usItem, (EMPTY_SANDBAG)) ) { @@ -16316,52 +16524,17 @@ void SOLDIERTYPE::EVENT_SoldierBuildStructure( INT32 sGridNo, UINT8 ubDirection if ( pShovelObj && pShovelObj->exists() && HasItemFlag(this->inv[ SECONDHANDPOS ].usItem, (SHOVEL)) ) { - // eventually search for the number of a sandbag item - if ( HasItemFlag(fullsandbagnr, FULL_SANDBAG) || GetFirstItemWithFlag(&fullsandbagnr, FULL_SANDBAG) ) - { - // Erase 'material' item from our hand - we 'used' it to build the structure - INT8 bObjSlot = HANDPOS; + this->StartMultiTurnAction( MTA_FILL_SANDBAG ); - 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 ); - - DeductPoints( this, restAPs, 0, AFTERACTION_INTERRUPT ); - - fSuccess = TRUE; - } + fSuccess = TRUE; } } } else if ( HasItemFlag(this->inv[ HANDPOS ].usItem, (SHOVEL)) ) { - // Build the thing - if ( RemoveFortification( sGridNo ) ) - { - // eventually search for the number of a sandbag item - if ( HasItemFlag(fullsandbagnr, FULL_SANDBAG) || GetFirstItemWithFlag(&fullsandbagnr, FULL_SANDBAG) ) - { - // Erase 'material' item from our hand - we 'used' it to build the structure - INT8 bObjSlot = HANDPOS; + this->StartMultiTurnAction( MTA_REMOVE_FORTIFY ); - 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 ); - - DeductPoints( this, restAPs, 0, AFTERACTION_INTERRUPT ); - - fSuccess = TRUE; - } - } + fSuccess = TRUE; } } @@ -16370,6 +16543,17 @@ void SOLDIERTYPE::EVENT_SoldierBuildStructure( INT32 sGridNo, UINT8 ubDirection // Say NOTHING quote... this->DoMercBattleSound( BATTLE_SOUND_NOTHING ); } + else + { + // 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 ); + } } void SOLDIERTYPE::EVENT_SoldierHandcuffPerson( INT32 sGridNo, UINT8 ubDirection ) @@ -16474,7 +16658,7 @@ void SOLDIERTYPE::EVENT_SoldierHandcuffPerson( INT32 sGridNo, UINT8 ubDirection StatChange( this, DEXTAMT, 3, TRUE ); StatChange( this, EXPERAMT, 2, TRUE ); - DeductPoints( this, GetAPsToHandcuff( this, sGridNo ), 0, AFTERACTION_INTERRUPT ); + DeductPoints( this, GetAPsToHandcuff( this, sGridNo ), APBPConstants[BP_HANDCUFF], AFTERACTION_INTERRUPT ); // this might have been the last guy... CheckForEndOfBattle( FALSE ); @@ -16484,7 +16668,7 @@ void SOLDIERTYPE::EVENT_SoldierHandcuffPerson( INT32 sGridNo, UINT8 ubDirection // we gain a bit of experience... StatChange( this, DEXTAMT, 2, TRUE ); - DeductPoints( this, GetAPsToHandcuff( this, sGridNo ), 0, AFTERACTION_INTERRUPT ); + DeductPoints( this, GetAPsToHandcuff( this, sGridNo ), APBPConstants[BP_HANDCUFF], AFTERACTION_INTERRUPT ); // curses! this->DoMercBattleSound( BATTLE_SOUND_CURSE1 ); diff --git a/Tactical/Soldier Control.h b/Tactical/Soldier Control.h index ea907842..18a08899 100644 --- a/Tactical/Soldier Control.h +++ b/Tactical/Soldier Control.h @@ -389,6 +389,16 @@ enum #define WH40K_SOLDIER_KILLTHISTURN 0x80000000 //2147483648 // Soldier is on a kill streak*/ // ---------------------------------------------------------------- +// Flugente: types of multi-turn actions +enum +{ + MTA_NONE = 0, + MTA_FORTIFY, + MTA_REMOVE_FORTIFY, + MTA_FILL_SANDBAG, + NUM_MTA, +}; + // enum of uniform pieces typedef struct { @@ -1249,8 +1259,13 @@ public: UINT8 usStarveDamageHealth; // damage to health due to starvation. Can be cured by surgery, but only if nutrition level is high enough again UINT8 usStarveDamageStrength; // damage to strength due to starvation. Can be cured by surgery, but only if nutrition level is high enough again + // Flugente: multi-turn actions + INT16 bOverTurnAPS; // remaining AP cost for the next turns (allows actions to be performed for more than one turn) + INT32 sMTActionGridNo; // gridno on which we perfrom our multi-turn action + UINT8 usMultiTurnAction; // specifies which multi-turn action we are currently performing, 0: none + // Flugente: Decrease this filler by 1 for each new UINT8 / BOOLEAN variable, so we can maintain savegame compatibility!! - UINT8 ubFiller[20]; + UINT8 ubFiller[13]; #ifdef JA2UB //ja25 @@ -1512,6 +1527,12 @@ public: // Flugente: are we an assassin? BOOLEAN IsAssassin(); + + // Flugente: multi-turn actions + UINT8 GetMultiTurnAction(); + void StartMultiTurnAction(UINT8 usActionType); + void CancelMultiTurnAction(BOOLEAN fFinished); + BOOLEAN UpdateMultiTurnAction(); ////////////////////////////////////////////////////////////////////////////// }; // SOLDIERTYPE; diff --git a/Tactical/Structure Wrap.cpp b/Tactical/Structure Wrap.cpp index 261d065e..120651f3 100644 --- a/Tactical/Structure Wrap.cpp +++ b/Tactical/Structure Wrap.cpp @@ -574,7 +574,7 @@ BOOLEAN IsFortificationPossibleAtGridNo( INT32 sGridNo ) STRUCTURE * pStructure = NULL; - pStructure = FindStructure( sGridNo, (STRUCTURE_OBSTACLE) ); + pStructure = FindStructure( sGridNo, (STRUCTURE_OBSTACLE|STRUCTURE_PERSON) ); return( pStructure == NULL ); } diff --git a/Tactical/Tactical Turns.cpp b/Tactical/Tactical Turns.cpp index 74554cb5..c5750b90 100644 --- a/Tactical/Tactical Turns.cpp +++ b/Tactical/Tactical Turns.cpp @@ -240,6 +240,9 @@ void HandleTacticalEndTurn( ) //{ //// UpdateStats( pSoldier ); //} + + // Flugente: update multi-turn actions + pSoldier->UpdateMultiTurnAction(); } } diff --git a/Tactical/TeamTurns.cpp b/Tactical/TeamTurns.cpp index c0a39307..aefc784c 100644 --- a/Tactical/TeamTurns.cpp +++ b/Tactical/TeamTurns.cpp @@ -464,8 +464,19 @@ void EndTurnEvents( void ) DecaySmokeEffects( GetWorldTotalSeconds( ) ); DecayLightEffects( GetWorldTotalSeconds( ) ); + SOLDIERTYPE* pSoldier = NULL; + UINT32 cnt = gTacticalStatus.Team[ gbPlayerNum ].bFirstID; + for ( pSoldier = MercPtrs[ cnt ]; cnt <= gTacticalStatus.Team[ gbPlayerNum ].bLastID; ++cnt, ++pSoldier) + { + if ( pSoldier->bActive && pSoldier->stats.bLife > 0 && !( pSoldier->flags.uiStatusFlags & SOLDIER_VEHICLE ) && !( AM_A_ROBOT( pSoldier ) ) ) + { + // Flugente: update multi-turn actions + pSoldier->UpdateMultiTurnAction(); + } + } + // Flugente: Cool down/decay all items not in a soldier's inventory - CoolDownWorldItems(); + CoolDownWorldItems( ); #ifdef ENABLE_ZOMBIES // Flugente: raise zombies if in gamescreen and option set diff --git a/Utils/Text.h b/Utils/Text.h index ee2530ed..15e27abe 100644 --- a/Utils/Text.h +++ b/Utils/Text.h @@ -748,6 +748,19 @@ enum extern STR16 szPrisonerTextStr[]; +enum +{ + STR_MTA_NONE, + STR_MTA_FORTIFY, + STR_MTA_REMOVE_FORTIFY, + STR_TA_FILL_SANDBAG, + STR_MTA_CANCEL, + + TEXT_NUM_MTA_STR +}; + +extern STR16 szMTATextStr[]; + enum { AIR_RAID_TURN_STR, diff --git a/Utils/_ChineseText.cpp b/Utils/_ChineseText.cpp index 41c35b60..efea0c23 100644 --- a/Utils/_ChineseText.cpp +++ b/Utils/_ChineseText.cpp @@ -7499,4 +7499,13 @@ STR16 szPrisonerTextStr[]= L"这敌人丫宁死不从!",//L"The enemy refuses to surrender!", }; +STR16 szMTATextStr[]= // TODO.Translate +{ + L"nothing", + L"building a fortification", + L"removing a fortification", + L"filling sandbags", + L"%s had to stop %s.", +}; + #endif //CHINESE diff --git a/Utils/_DutchText.cpp b/Utils/_DutchText.cpp index 4fc82618..7d7f05a3 100644 --- a/Utils/_DutchText.cpp +++ b/Utils/_DutchText.cpp @@ -7497,4 +7497,13 @@ STR16 szPrisonerTextStr[]= L"The enemy refuses to surrender!", }; +STR16 szMTATextStr[]= // TODO.Translate +{ + L"nothing", + L"building a fortification", + L"removing a fortification", + L"filling sandbags", + L"%s had to stop %s.", +}; + #endif //DUTCH diff --git a/Utils/_EnglishText.cpp b/Utils/_EnglishText.cpp index f53d8e1d..4c13a77c 100644 --- a/Utils/_EnglishText.cpp +++ b/Utils/_EnglishText.cpp @@ -7484,4 +7484,13 @@ STR16 szPrisonerTextStr[]= L"The enemy refuses to surrender!", }; +STR16 szMTATextStr[]= +{ + L"nothing", + L"building a fortification", + L"removing a fortification", + L"filling sandbags", + L"%s had to stop %s.", +}; + #endif //ENGLISH diff --git a/Utils/_FrenchText.cpp b/Utils/_FrenchText.cpp index 9075343e..7fa3828b 100644 --- a/Utils/_FrenchText.cpp +++ b/Utils/_FrenchText.cpp @@ -7471,4 +7471,13 @@ STR16 szPrisonerTextStr[]= L"The enemy refuses to surrender!", }; +STR16 szMTATextStr[]= // TODO.Translate +{ + L"nothing", + L"building a fortification", + L"removing a fortification", + L"filling sandbags", + L"%s had to stop %s.", +}; + #endif //FRENCH diff --git a/Utils/_GermanText.cpp b/Utils/_GermanText.cpp index 53d1eedd..c9fe40e4 100644 --- a/Utils/_GermanText.cpp +++ b/Utils/_GermanText.cpp @@ -7311,4 +7311,13 @@ STR16 szPrisonerTextStr[]= L"Der Gegner weigert sich aufzugeben!", }; +STR16 szMTATextStr[]= // TODO.Translate +{ + L"nothing", + L"building a fortification", + L"removing a fortification", + L"filling sandbags", + L"%s had to stop %s.", +}; + #endif //GERMAN diff --git a/Utils/_ItalianText.cpp b/Utils/_ItalianText.cpp index c726dc56..934589d3 100644 --- a/Utils/_ItalianText.cpp +++ b/Utils/_ItalianText.cpp @@ -7484,4 +7484,13 @@ STR16 szPrisonerTextStr[]= L"The enemy refuses to surrender!", }; +STR16 szMTATextStr[]= // TODO.Translate +{ + L"nothing", + L"building a fortification", + L"removing a fortification", + L"filling sandbags", + L"%s had to stop %s.", +}; + #endif //ITALIAN diff --git a/Utils/_PolishText.cpp b/Utils/_PolishText.cpp index 1678059a..42771a89 100644 --- a/Utils/_PolishText.cpp +++ b/Utils/_PolishText.cpp @@ -7498,4 +7498,13 @@ STR16 szPrisonerTextStr[]= L"The enemy refuses to surrender!", }; +STR16 szMTATextStr[]= // TODO.Translate +{ + L"nothing", + L"building a fortification", + L"removing a fortification", + L"filling sandbags", + L"%s had to stop %s.", +}; + #endif //POLISH diff --git a/Utils/_RussianText.cpp b/Utils/_RussianText.cpp index acfc2218..ae79d9b8 100644 --- a/Utils/_RussianText.cpp +++ b/Utils/_RussianText.cpp @@ -7450,4 +7450,13 @@ STR16 szPrisonerTextStr[]= L"The enemy refuses to surrender!", }; +STR16 szMTATextStr[]= // TODO.Translate +{ + L"nothing", + L"building a fortification", + L"removing a fortification", + L"filling sandbags", + L"%s had to stop %s.", +}; + #endif //RUSSIAN diff --git a/Utils/_TaiwaneseText.cpp b/Utils/_TaiwaneseText.cpp index 40ccc0fe..d207878d 100644 --- a/Utils/_TaiwaneseText.cpp +++ b/Utils/_TaiwaneseText.cpp @@ -7501,4 +7501,13 @@ STR16 szPrisonerTextStr[]= L"The enemy refuses to surrender!", }; +STR16 szMTATextStr[]= // TODO.Translate +{ + L"nothing", + L"building a fortification", + L"removing a fortification", + L"filling sandbags", + L"%s had to stop %s.", +}; + #endif //TAIWANESE