diff --git a/GameVersion.cpp b/GameVersion.cpp index ca36219e..e93f06b8 100644 --- a/GameVersion.cpp +++ b/GameVersion.cpp @@ -55,8 +55,8 @@ #endif -CHAR8 czVersionNumber[16] = { "Build 20.07.17" }; //YY.MM.DD +CHAR8 czVersionNumber[16] = { "Build 20.07.24" }; //YY.MM.DD CHAR16 zTrackingNumber[16] = { L"Z" }; -CHAR16 zRevisionNumber[16] = { L"Revision 8868" }; +CHAR16 zRevisionNumber[16] = { L"Revision 8870" }; // SAVE_GAME_VERSION is defined in header, change it there diff --git a/GameVersion.h b/GameVersion.h index e2d02fb7..4fa11cd9 100644 --- a/GameVersion.h +++ b/GameVersion.h @@ -22,6 +22,7 @@ extern CHAR16 zRevisionNumber[16]; // Keeps track of the saved game version. Increment the saved game version whenever // you will invalidate the saved game file +#define DRAGSTRUCTURE 182 // Flugente: we can drag structures behind us #define DISABILITYFLAGMASK 181 // Flugente: disabilities get a flagmask #define PROFILETYPE_STORED 180 // Flugente: the type of each profile is stored in the savegame #define CORPSE_DISPOSAL 179 // Flugente: corpses can be removed by assignment @@ -101,7 +102,7 @@ extern CHAR16 zRevisionNumber[16]; #define AP100_SAVEGAME_DATATYPE_CHANGE 105 // Before this, we didn't have the 100AP structure changes #define NIV_SAVEGAME_DATATYPE_CHANGE 102 // Before this, we used the old structure system -#define SAVE_GAME_VERSION DISABILITYFLAGMASK +#define SAVE_GAME_VERSION DRAGSTRUCTURE //#define RUSSIANGOLD #ifdef __cplusplus diff --git a/Init.cpp b/Init.cpp index 31632b94..c72f5c28 100644 --- a/Init.cpp +++ b/Init.cpp @@ -395,6 +395,10 @@ BOOLEAN LoadExternalGameplayData(STR directoryName, BOOLEAN isMultiplayer) strcat( fileName, INTERACTIVEACTIONSFILENAME ); SGP_THROW_IFFALSE( ReadInInteractiveActionsStats( fileName ), INTERACTIVEACTIONSFILENAME ); + strcpy( fileName, directoryName ); + strcat( fileName, STRUCTUREMOVEFILENAME ); + SGP_THROW_IFFALSE( ReadInStructureMoveStats( fileName ), STRUCTUREMOVEFILENAME ); + strcpy(fileName, directoryName); strcat(fileName, MERCHANTSFILENAME); SGP_THROW_IFFALSE(ReadInMerchantStats(fileName),MERCHANTSFILENAME); diff --git a/SaveLoadGame.cpp b/SaveLoadGame.cpp index 8b4b593e..38b49724 100644 --- a/SaveLoadGame.cpp +++ b/SaveLoadGame.cpp @@ -2436,18 +2436,6 @@ BOOLEAN SOLDIERTYPE::Load(HWFILE hFile) if ( guiCurrentSaveGameVersion >= SNITCH_TRAIT_EXTENDED ) { numBytesRead = ReadFieldByField(hFile, &this->usSoldierFlagMask2, sizeof(usSoldierFlagMask2), sizeof(UINT32), numBytesRead); - - if ( guiCurrentSaveGameVersion >= INDIVIDUAL_MILITIA ) - { - numBytesRead = ReadFieldByField( hFile, &this->usIndividualMilitiaID, sizeof(usIndividualMilitiaID), sizeof(UINT32), numBytesRead ); - } - else - { - this->usIndividualMilitiaID = 0; - - for ( int i = 0; i < sizeof(usIndividualMilitiaID); ++i ) - buffer++; - } } else { @@ -2457,6 +2445,18 @@ BOOLEAN SOLDIERTYPE::Load(HWFILE hFile) buffer++; } + if ( guiCurrentSaveGameVersion >= INDIVIDUAL_MILITIA ) + { + numBytesRead = ReadFieldByField( hFile, &this->usIndividualMilitiaID, sizeof( usIndividualMilitiaID ), sizeof( UINT32 ), numBytesRead ); + } + else + { + this->usIndividualMilitiaID = 0; + + for ( int i = 0; i < sizeof( usIndividualMilitiaID ); ++i ) + buffer++; + } + if ( guiCurrentSaveGameVersion >= DISABILITYFLAGMASK ) { numBytesRead = ReadFieldByField( hFile, &this->usDisabilityFlagMask, sizeof( usDisabilityFlagMask ), sizeof( UINT32 ), numBytesRead ); @@ -2469,6 +2469,18 @@ BOOLEAN SOLDIERTYPE::Load(HWFILE hFile) buffer++; } + if ( guiCurrentSaveGameVersion >= DRAGSTRUCTURE ) + { + numBytesRead = ReadFieldByField( hFile, &this->sDragGridNo, sizeof( sDragGridNo ), sizeof( INT32 ), numBytesRead ); + } + else + { + this->sDragGridNo = NOWHERE; + + for ( int i = 0; i < sizeof( sDragGridNo ); ++i ) + buffer++; + } + /*if ( guiCurrentSaveGameVersion >= FOOD_CHANGES ) { numBytesRead = ReadFieldByField(hFile, &this->bFoodLevel, sizeof(bFoodLevel), sizeof(INT32), numBytesRead); diff --git a/Tactical/Handle Items.cpp b/Tactical/Handle Items.cpp index bc095973..10e8cb39 100644 --- a/Tactical/Handle Items.cpp +++ b/Tactical/Handle Items.cpp @@ -3997,6 +3997,46 @@ BOOLEAN MoveItemPools( INT32 sStartPos, INT32 sEndPos, INT8 bStartLevel, INT8 bE return( TRUE ); } +BOOLEAN MoveItemPools_ForDragging( INT32 sStartPos, INT32 sEndPos, INT8 bStartLevel, INT8 bEndLevel ) +{ + // note, only works between locations on the ground + ITEM_POOL* pItemPool; + WORLDITEM TempWorldItem; + + // loop over all items and note the ones we have to move. Move them afterwards, if we do it during the operation we risk pointer issues + std::vector itemindexestomove_vector; + + if ( bStartLevel ) + GetItemPoolFromRoof( sStartPos, &pItemPool ); + else + GetItemPoolFromGround( sStartPos, &pItemPool ); + + while ( pItemPool ) + { + TempWorldItem = gWorldItems[pItemPool->iItemIndex]; + + // don't move an item if it's an armed bomb, an action item or if it isn't a fresh drop + if ( TempWorldItem.object.usItem != ACTION_ITEM + && !( TempWorldItem.usFlags & WORLD_ITEM_ARMED_BOMB ) ) + { + itemindexestomove_vector.push_back( pItemPool->iItemIndex ); + } + + pItemPool = pItemPool->pNext; + } + + // now move the stuff + for ( std::vector::iterator it = itemindexestomove_vector.begin(), itend = itemindexestomove_vector.end(); it != itend; ++it ) + { + TempWorldItem = gWorldItems[(*it)]; + + RemoveItemFromPool( sStartPos, ( *it ), bStartLevel ); + AddItemToPool( sEndPos, &( TempWorldItem.object ), 1, bEndLevel, TempWorldItem.usFlags, TempWorldItem.bRenderZHeightAboveLevel ); + } + + return( TRUE ); +} + BOOLEAN GetItemPool( INT32 usMapPos, ITEM_POOL **ppItemPool, UINT8 ubLevel ) { LEVELNODE *pObject; @@ -4065,6 +4105,33 @@ BOOLEAN GetItemPoolFromGround( INT32 sMapPos, ITEM_POOL **ppItemPool ) return( FALSE ); } +BOOLEAN GetItemPoolFromRoof( INT32 sMapPos, ITEM_POOL **ppItemPool ) +{ + // Flugente: apparently the ...Ground version is called a lot, otherwise it would be smarter to just add an extra argument for level + LEVELNODE *pObject = gpWorldLevelData[sMapPos].pOnRoofHead; + + ( *ppItemPool ) = NULL; + + // LOOP THORUGH OBJECT LAYER + while ( pObject != NULL ) + { + if ( pObject->uiFlags & LEVELNODE_ITEM ) + { + ( *ppItemPool ) = pObject->pItemPool; + + //DEF added the check because pObject->pItemPool was NULL which was causing problems + if ( *ppItemPool ) + return( TRUE ); + else + return( FALSE ); + } + + pObject = pObject->pNext; + } + + return( FALSE ); +} + void NotifySoldiersToLookforItems( ) { UINT32 cnt; @@ -7731,6 +7798,72 @@ UINT8 CheckBuildFortification( INT32 sGridNo, INT8 sLevel, UINT8 usIndex, UINT32 return 1; } +UINT16 gusTempDragBuildSoldierID = NOBODY; + +BOOLEAN BuildStructDrag( INT32 sGridNo, INT8 sLevel, UINT32 uiTileType, UINT8 usIndex, UINT16 usSoldierID ) +{ + // needs to be a valid location + if ( TileIsOutOfBounds( sGridNo ) ) + return FALSE; + + // if we want to build on a roof, a roof is required + if ( sLevel && !FlatRoofAboveGridNo( sGridNo ) ) + return FALSE; + + // don't build in water + if ( TERRAIN_IS_WATER( GetTerrainType( sGridNo ) ) ) + return FALSE; + + // do not build into people + if ( NOBODY != WhoIsThere2( sGridNo, sLevel ) ) + return FALSE; + + if ( uiTileType < 0 ) + { + ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, szMTATextStr[STR_MTA_CANNOT_BUILD] ); + return FALSE; + } + + gusTempDragBuildSoldierID = usSoldierID; + + // Check with Structure Database (aka ODB) if we can put the object here! + BOOLEAN fOkayToAdd = OkayToAddStructureToWorld( sGridNo, sLevel, gTileDatabase[( gTileTypeStartIndex[uiTileType] + usIndex )].pDBStructureRef, INVALID_STRUCTURE_ID ); + if ( fOkayToAdd || ( gTileDatabase[( gTileTypeStartIndex[uiTileType] + usIndex )].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 + // Actual structure info is added by the functions below + if ( sLevel ) + { + RemoveOnRoofStruct( sGridNo, (UINT16)( gTileTypeStartIndex[uiTileType] + usIndex ) ); + + AddOnRoofToTail( sGridNo, (UINT16)( gTileTypeStartIndex[uiTileType] + usIndex ) ); + } + else + { + RemoveStruct( sGridNo, (UINT16)( gTileTypeStartIndex[uiTileType] + usIndex ) ); + + AddStructToHead( sGridNo, (UINT16)( gTileTypeStartIndex[uiTileType] + usIndex ) ); + } + + RecompileLocalMovementCosts( sGridNo ); + + // Turn off permanent changes.... + ApplyMapChangesToMapTempFile( FALSE ); + SetRenderFlags( RENDER_FLAG_FULL ); + + gusTempDragBuildSoldierID = NOBODY; + + return TRUE; + } + + gusTempDragBuildSoldierID = NOBODY; + + return FALSE; +} + BOOLEAN BuildFortification( INT32 sGridNo, INT8 sLevel, UINT8 usIndex, UINT32 usStructureconstructindex ) { INT16 sUseObjIndex = -1; @@ -7871,6 +8004,149 @@ BOOLEAN CanRemoveFortification( INT32 sGridNo, INT8 sLevel, UINT32 usStructureco return FALSE; } +BOOLEAN IsDragStructurePresent( INT32 sGridNo, INT8 sLevel, UINT32& arusTileType, UINT16& arusStructureNumber ) +{ + // needs to be a valid location + if ( TileIsOutOfBounds( sGridNo ) ) + return FALSE; + + STRUCTURE* pStruct = GetTallestStructureOnGridnoDrag( sGridNo, sLevel ); + + if ( pStruct != NULL ) + { + // Get LEVELNODE for struct and remove! + LEVELNODE* pNode = FindLevelNodeBasedOnStructure( pStruct->sGridNo, pStruct ); + + if ( pNode ) + { + if ( GetTileType( pNode->usIndex, &arusTileType ) ) + { + arusStructureNumber = pStruct->pDBStructureRef->pDBStructure->usStructureNumber; + + // if tileset is from the current tileset, check that + for ( int i = 0; i < STRUCTURE_MOVEPOSSIBLE_MAX; ++i ) + { + // if tileset is from the current tileset, check that + bool found = FALSE; + if ( gTilesets[giCurrentTilesetID].TileSurfaceFilenames[arusTileType][0] ) + { + if ( !_strnicmp( gTilesets[giCurrentTilesetID].TileSurfaceFilenames[arusTileType], gStructureMovePossible[i].szTileSetName, 11 ) ) + found = true; + } + // otherwise, check first tileset (GENERIC 1) + else if ( gTilesets[0].TileSurfaceFilenames[arusTileType][0] ) + { + if ( !_strnicmp( gTilesets[0].TileSurfaceFilenames[arusTileType], gStructureMovePossible[i].szTileSetName, 11 ) ) + found = true; + } + + if ( found ) + { + for ( size_t j = 0, size = gStructureMovePossible[i].tilevector.size(); j < size; ++j ) + { + if ( gStructureMovePossible[i].tilevector[j] == arusStructureNumber ) + { + return TRUE; + } + } + } + } + } + } + } + + return FALSE; +} + +void GetDragStructureXmlEntry( UINT32 ausTileType, UINT16 ausStructureNumber, int& arXmlVectorEntry ) +{ + arXmlVectorEntry = -1; + + // if tileset is from the current tileset, check that + for ( int i = 0; i < STRUCTURE_MOVEPOSSIBLE_MAX; ++i ) + { + // if tileset is from the current tileset, check that + bool found = FALSE; + if ( gTilesets[giCurrentTilesetID].TileSurfaceFilenames[ausTileType][0] ) + { + if ( !_strnicmp( gTilesets[giCurrentTilesetID].TileSurfaceFilenames[ausTileType], gStructureMovePossible[i].szTileSetName, 11 ) ) + found = true; + } + // otherwise, check first tileset (GENERIC 1) + else if ( gTilesets[0].TileSurfaceFilenames[ausTileType][0] ) + { + if ( !_strnicmp( gTilesets[0].TileSurfaceFilenames[ausTileType], gStructureMovePossible[i].szTileSetName, 11 ) ) + found = true; + } + + if ( found ) + { + for ( size_t j = 0, size = gStructureMovePossible[i].tilevector.size(); j < size; ++j ) + { + if ( gStructureMovePossible[i].tilevector[j] == ausStructureNumber ) + { + arXmlVectorEntry = i; + + return; + } + } + } + } +} + +BOOLEAN RemoveStructDrag( INT32 sGridNo, INT8 sLevel, UINT32 uiTileType ) +{ + // needs to be a valid location + if ( TileIsOutOfBounds( sGridNo ) ) + return FALSE; + + STRUCTURE* pStruct = GetTallestStructureOnGridnoDrag( sGridNo, sLevel ); + + if ( pStruct != NULL ) + { + // Get LEVELNODE for struct and remove! + LEVELNODE* pNode = FindLevelNodeBasedOnStructure( pStruct->sGridNo, pStruct ); + + if ( pNode ) + { + UINT16 usIndex = pNode->usIndex; + + // Remove old graphic + ApplyMapChangesToMapTempFile( TRUE ); + + if ( sLevel ) + { + RemoveOnRoofStruct( sGridNo, usIndex ); + } + + // if this is a wall, check wether the roof will collapse. + // Yes, the player can damage himself by collapsing the roof of the house he is currently in. Such stupidity has to be punished. + if ( pStruct->fFlags & STRUCTURE_WALL ) + { + // this isn't an explosion, so the structural damage is moderate + HandleRoofDestruction( sGridNo, 50 ); + } + + RemoveStruct( sGridNo, pNode->usIndex ); + + if ( !GridNoIndoors( sGridNo ) && gTileDatabase[usIndex].uiFlags & HAS_SHADOW_BUDDY && gTileDatabase[usIndex].sBuddyNum != -1 ) + { + RemoveShadow( sGridNo, gTileDatabase[usIndex].sBuddyNum ); + } + + RecompileLocalMovementCosts( sGridNo ); + + // Turn off permanent changes.... + ApplyMapChangesToMapTempFile( FALSE ); + SetRenderFlags( RENDER_FLAG_FULL ); + + return TRUE; + } + } + + return FALSE; +} + BOOLEAN RemoveFortification( INT32 sGridNo, INT8 sLevel, UINT32 usStructureconstructindex ) { // needs to be a valid location diff --git a/Tactical/Handle Items.h b/Tactical/Handle Items.h index 6648f413..4c94df53 100644 --- a/Tactical/Handle Items.h +++ b/Tactical/Handle Items.h @@ -159,6 +159,20 @@ typedef struct INTERACTIVE_STRUCTURE { extern INTERACTIVE_STRUCTURE gInteractiveStructure[INTERACTIVE_STRUCTURE_MAX]; extern UINT32 gMaxInteractiveStructureRead; +// Flugente: we need to define what structures can be moved. Otherwise the player will drag around parts of the scenery. +typedef struct STRUCTURE_MOVEPOSSIBLE { + STRUCTURE_MOVEPOSSIBLE() + : szTileSetDisplayName(), szTileSetName() {} + + char szTileSetDisplayName[20]; // name of this structure to the player (tileset names are obscure) + char szTileSetName[20]; // name of the tileset + std::vector tilevector; // structures in the tileset that we can deconstruct +} STRUCTURE_MOVEPOSSIBLE; + +#define STRUCTURE_MOVEPOSSIBLE_MAX 200 + +extern STRUCTURE_MOVEPOSSIBLE gStructureMovePossible[STRUCTURE_MOVEPOSSIBLE_MAX]; + class SOLDIERTYPE; INT32 HandleItem( SOLDIERTYPE *pSoldier, INT32 sGridNo, INT8 bLevel, UINT16 usHandItem, BOOLEAN fFromUI ); void SoldierPickupItem( SOLDIERTYPE *pSoldier, INT32 iItemIndex, INT32 sGridNo, INT8 bZLevel ); @@ -186,10 +200,12 @@ OBJECTTYPE* InternalAddItemToPool( INT32 *psGridNo, OBJECTTYPE *pObject, INT8 bV INT32 AdjustGridNoForItemPlacement( SOLDIERTYPE *pSoldier, INT32 sGridNo ); BOOLEAN GetItemPool( INT32 usMapPos, ITEM_POOL **ppItemPool, UINT8 ubLevel ); BOOLEAN GetItemPoolFromGround( INT32 sMapPos, ITEM_POOL **ppItemPool ); +BOOLEAN GetItemPoolFromRoof( INT32 sMapPos, ITEM_POOL **ppItemPool ); BOOLEAN DrawItemPoolList( ITEM_POOL *pItemPool, INT32 sGridNo, UINT8 bCommand, INT8 bZLevel, INT16 sXPos, INT16 sYPos ); BOOLEAN RemoveItemFromPool( INT32 sGridNo, INT32 iItemIndex, UINT8 ubLevel ); BOOLEAN ItemExistsAtLocation( INT32 sGridNo, INT32 iItemIndex, UINT8 ubLevel ); BOOLEAN MoveItemPools( INT32 sStartPos, INT32 sEndPos, INT8 bStartLevel, INT8 bEndLevel ); +BOOLEAN MoveItemPools_ForDragging( INT32 sStartPos, INT32 sEndPos, INT8 bStartLevel, INT8 bEndLevel ); void SetItemPoolLocator( ITEM_POOL *pItemPool ); void SetItemPoolLocatorWithCallback( ITEM_POOL *pItemPool, ITEM_POOL_LOCATOR_HOOK Callback ); @@ -274,6 +290,11 @@ BOOLEAN BuildFortification( INT32 sGridNo, INT8 sLevel, UINT8 usIndex, UINT32 us BOOLEAN CanRemoveFortification( INT32 sGridNo, INT8 sLevel, UINT32 usStructureconstructindex ); BOOLEAN RemoveFortification( INT32 sGridNo, INT8 sLevel, UINT32 usStructureconstructindex ); +BOOLEAN IsDragStructurePresent( INT32 sGridNo, INT8 sLevel, UINT32& arusTileType, UINT16& arusStructureNumber ); +void GetDragStructureXmlEntry( UINT32 ausTileType, UINT16 ausStructureNumber, int& arXmlVectorEntry ); +BOOLEAN RemoveStructDrag( INT32 sGridNo, INT8 sLevel, UINT32 uiTileType ); +BOOLEAN BuildStructDrag( INT32 sGridNo, INT8 sLevel, UINT32 uiTileType, UINT8 usIndex, UINT16 usSoldierID ); + void UpdateFortificationPossibleAmount(); void HandleFortificationUpdate(); diff --git a/Tactical/SkillMenu.cpp b/Tactical/SkillMenu.cpp index 9deb64f3..596cdfb6 100644 --- a/Tactical/SkillMenu.cpp +++ b/Tactical/SkillMenu.cpp @@ -15,6 +15,7 @@ #include "Map Screen Interface.h" #include "Rotting Corpses.h" #include "Dialogue Control.h" +#include "Handle Items.h" extern void GetEquipmentTemplates( ); @@ -115,6 +116,7 @@ void Wrapper_Cancel_SoldierSelection( UINT32 aVal ) { gSoldierSelection.Cancel( DragSelection gDragSelection; void Wrapper_Function_DragSelection( UINT32 aVal) { gDragSelection.Functions(aVal); } +void Wrapper_Function_DragSelection_GridNo( INT32 aVal ){ gDragSelection.FunctionsGridNo( aVal ); } void Wrapper_Setup_DragSelection( UINT32 aVal ) { gDragSelection.Setup( aVal ); } void Wrapper_Cancel_DragSelection( UINT32 aVal ) { gDragSelection.Cancel( ); } /////////////////////////////// Drag Selection //////////////////////////////////////////// @@ -844,9 +846,34 @@ DragSelection::Setup( UINT32 aVal ) GetPopup( )->addOption( *pOption ); } + // gridno + for ( int ubDirection = 0; ubDirection < NUM_WORLD_DIRECTIONS; ++ubDirection ) + { + INT32 sTempGridNo = NewGridNo( pSoldier->sGridNo, DirectionInc( ubDirection ) ); + + UINT32 tiletype; + UINT16 structurenumber; + if ( pSoldier->CanDragStructure( sTempGridNo ) + && IsDragStructurePresent( sTempGridNo, pSoldier->pathing.bLevel, tiletype, structurenumber ) ) + { + int xmlentry; + GetDragStructureXmlEntry( tiletype, structurenumber, xmlentry ); + + if ( xmlentry >= 0 ) + { + swprintf( pStr, L"%hs %s", gStructureMovePossible[xmlentry].szTileSetDisplayName, FaceDirs[gOneCDirection[ubDirection]] ); + + // we have to use an offset of NOBODY in order to differentiate between person and corpse + pOption = new POPUP_OPTION( &std::wstring( pStr ), new popupCallbackFunction( &Wrapper_Function_DragSelection_GridNo, sTempGridNo ) ); + + GetPopup()->addOption( *pOption ); + } + } + } + // cancel option swprintf( pStr, pSkillMenuStrings[SKILLMENU_CANCEL] ); - pOption = new POPUP_OPTION( &std::wstring( pStr ), new popupCallbackFunction( &Wrapper_Cancel_SoldierSelection, 0 ) ); + pOption = new POPUP_OPTION( &std::wstring( pStr ), new popupCallbackFunction( &Wrapper_Cancel_DragSelection, 0 ) ); GetPopup( )->addOption( *pOption ); } @@ -863,7 +890,7 @@ DragSelection::Functions( UINT32 aVal ) if ( pSoldier ) { - BOOLEAN result = pSoldier->UseSkill( usSkill, sTraitsMenuTargetGridNo, aVal ); + BOOLEAN result = pSoldier->UseSkill( usSkill, NOWHERE, aVal ); // additional dialogue AdditionalTacticalCharacterDialogue_CallsLua( pSoldier, ADE_SKILL_RESULT, usSkill, result ); @@ -873,6 +900,26 @@ DragSelection::Functions( UINT32 aVal ) gSkillSelection.Cancel(); gTraitSelection.Cancel( ); } + +void +DragSelection::FunctionsGridNo( INT32 aVal ) +{ + SOLDIERTYPE * pSoldier = NULL; + + GetSoldier( &pSoldier, gusSelectedSoldier ); + + if ( pSoldier ) + { + BOOLEAN result = pSoldier->UseSkill( usSkill, aVal, 0 ); + + // additional dialogue + AdditionalTacticalCharacterDialogue_CallsLua( pSoldier, ADE_SKILL_RESULT, usSkill, result ); + } + + Cancel(); + gSkillSelection.Cancel(); + gTraitSelection.Cancel(); +} /////////////////////////////// Drag Selection //////////////////////////////////////////// /** diff --git a/Tactical/SkillMenu.h b/Tactical/SkillMenu.h index e06252d5..826cbc2e 100644 --- a/Tactical/SkillMenu.h +++ b/Tactical/SkillMenu.h @@ -162,6 +162,7 @@ public: void Setup( UINT32 aVal ); void Functions( UINT32 aVal ); + void FunctionsGridNo( INT32 aVal ); private: UINT32 usSkill; diff --git a/Tactical/Soldier Control.cpp b/Tactical/Soldier Control.cpp index 1b0a20dc..9cfc5ac1 100644 --- a/Tactical/Soldier Control.cpp +++ b/Tactical/Soldier Control.cpp @@ -1144,6 +1144,7 @@ void SOLDIERTYPE::initialize( ) this->usQuickItemId = 0; this->ubQuickItemSlot = 0; this->usDisabilityFlagMask = 0; + this->sDragGridNo = NOWHERE; } bool SOLDIERTYPE::exists( ) @@ -11705,34 +11706,7 @@ void SOLDIERTYPE::MoveMerc( FLOAT dMovementChange, FLOAT dAngle, BOOLEAN fCheckR // Flugente: drag people if ( currentlydragging ) { - // sevenfm: play sound while dragging - if (!(this->usSoldierFlagMask2 & SOLDIER_DRAG_SOUND)) - { - SGPFILENAME zFilename_Used; - CHAR8 zFilename[512]; - // prepare drag sound - if (gsDragSoundNum < 0) - { - gsDragSoundNum = 0; - do - { - gsDragSoundNum++; - sprintf(zFilename, "Sounds\\Misc\\DragBody%d", gsDragSoundNum); - } while ( SoundFileExists( zFilename, zFilename_Used ) ); - gsDragSoundNum--; - } - - if (gsDragSoundNum > 0) - { - sprintf(zFilename, "Sounds\\Misc\\DragBody%d", Random(gsDragSoundNum) + 1); - if ( SoundFileExists( zFilename, zFilename_Used ) ) - { - PlayJA2SampleFromFile( zFilename_Used, RATE_11025, SoundVolume(MIDVOLUME, this->sGridNo), 1, SoundDir(this->sGridNo)); - } - - this->usSoldierFlagMask2 |= SOLDIER_DRAG_SOUND; - } - } + bool dragaborted = false; if ( this->usDragPersonID != NOBODY ) { @@ -11742,7 +11716,6 @@ void SOLDIERTYPE::MoveMerc( FLOAT dMovementChange, FLOAT dAngle, BOOLEAN fCheckR { // while it would be neat to take the opposite direction (which would make it look like we drag the other person by the legs), // this causes problems, as a prone person needs additional space for the legs. So just take the same direction - //pSoldier->ubDirection = (this->ubDirection + 4 ) % NUM_WORLD_DIRECTIONS; pSoldier->ubDirection = this->ubDirection; FLOAT dx = 0; @@ -11769,6 +11742,10 @@ void SOLDIERTYPE::MoveMerc( FLOAT dMovementChange, FLOAT dAngle, BOOLEAN fCheckR pSoldier->EVENT_InternalSetSoldierPosition( base_x + dx, base_y + dy, FALSE, FALSE, FALSE ); } + else + { + dragaborted = true; + } } else if ( this->sDragCorpseID >= 0 ) { @@ -11776,6 +11753,10 @@ void SOLDIERTYPE::MoveMerc( FLOAT dMovementChange, FLOAT dAngle, BOOLEAN fCheckR if ( pCorpse ) { + // move all enemy-dropped items along with the corpse, to make it look as if the items are still 'on' the body + if ( sOldGridNo != this->sGridNo ) + MoveItemPools_ForDragging( pCorpse->def.sGridNo, sOldGridNo, this->pathing.bLevel, this->pathing.bLevel ); + // move corpse to new location. We have to actually delete and recreate the corpse, otherwise direction changes will only be visible after saving the game ROTTING_CORPSE_DEFINITION CorpseDef; @@ -11820,6 +11801,82 @@ void SOLDIERTYPE::MoveMerc( FLOAT dMovementChange, FLOAT dAngle, BOOLEAN fCheckR this->sDragCorpseID = AddRottingCorpse(&CorpseDef); } + else + { + dragaborted = true; + } + } + else if ( sOldGridNo != this->sGridNo && this->sDragGridNo != NOWHERE ) + { + bool success = false; + UINT32 arusTileType; + UINT16 arusStructureNumber; + if ( IsDragStructurePresent( this->sDragGridNo, this->pathing.bLevel, arusTileType, arusStructureNumber ) ) + { + // add + if ( BuildStructDrag( sOldGridNo, gsInterfaceLevel, arusTileType, arusStructureNumber, this->ubID ) ) + { + // remove + RemoveStructDrag( this->sDragGridNo, gsInterfaceLevel, arusTileType ); + + // also move doors, this includes moving locks and traps + DOOR* pDoor = FindDoorInfoAtGridNo( this->sDragGridNo ); + if ( pDoor ) + pDoor->sGridNo = sOldGridNo; + + success = true; + } + } + + if ( success ) + { + // move all items in/on the structure along + MoveItemPools_ForDragging( this->sDragGridNo, sOldGridNo, this->pathing.bLevel, this->pathing.bLevel ); + + this->sDragGridNo = sOldGridNo; + } + else + { + this->CancelDrag(); + + dragaborted = true; + } + } + + if ( !dragaborted ) + { + // sevenfm: play sound while dragging + if ( !( this->usSoldierFlagMask2 & SOLDIER_DRAG_SOUND ) ) + { + SGPFILENAME zFilename_Used; + CHAR8 zFilename[512]; + // prepare drag sound + if ( gsDragSoundNum < 0 ) + { + gsDragSoundNum = 0; + do + { + gsDragSoundNum++; + sprintf( zFilename, "Sounds\\Misc\\DragBody%d", gsDragSoundNum ); + } while ( SoundFileExists( zFilename, zFilename_Used ) ); + gsDragSoundNum--; + } + + if ( gsDragSoundNum > 0 ) + { + sprintf( zFilename, "Sounds\\Misc\\DragBody%d", Random( gsDragSoundNum ) + 1 ); + if ( SoundFileExists( zFilename, zFilename_Used ) ) + { + PlayJA2SampleFromFile( zFilename_Used, RATE_11025, SoundVolume( MIDVOLUME, this->sGridNo ), 1, SoundDir( this->sGridNo ) ); + } + + this->usSoldierFlagMask2 |= SOLDIER_DRAG_SOUND; + } + } + } + else + { + this->CancelDrag(); } } @@ -18144,14 +18201,16 @@ BOOLEAN SOLDIERTYPE::UseSkill( UINT8 iSkill, INT32 usMapPos, UINT32 ID ) case SKILLS_CREATE_TURNCOAT: AttemptToCreateTurncoat ( ID ); + return TRUE; break; case SKILLS_ACTIVATE_TURNCOATS: - OrderTurnCoatToSwitchSides( ID ); + return OrderTurnCoatToSwitchSides( ID ); break; case SKILLS_ACTIVATE_TURNCOATS_ALL: OrderAllTurnCoatToSwitchSides(); + return TRUE; break; case SKILLS_SPOTTER: @@ -18164,19 +18223,27 @@ BOOLEAN SOLDIERTYPE::UseSkill( UINT8 iSkill, INT32 usMapPos, UINT32 ID ) { usSoldierFlagMask2 &= ~SOLDIER_TRAIT_FOCUS; sFocusGridNo = NOWHERE; + + return FALSE; } else { usSoldierFlagMask2 |= SOLDIER_TRAIT_FOCUS; sFocusGridNo = usMapPos; + + return TRUE; } break; case SKILLS_DRAG: - if ( ID < NOBODY ) + if ( usMapPos != NOWHERE ) + SetDragOrderStructure( usMapPos ); + else if ( ID < NOBODY ) SetDragOrderPerson( ID ); else SetDragOrderCorpse( ID - NOBODY ); + + return TRUE; break; default: @@ -20466,6 +20533,191 @@ BOOLEAN SOLDIERTYPE::CanDragCorpse( UINT16 usCorpseNum ) return FALSE; } +BOOLEAN SOLDIERTYPE::CanDragStructure( INT32 sGridNo ) +{ + if ( !CanDragInPrinciple() ) + return FALSE; + + if ( sGridNo == NOWHERE ) + return FALSE; + + // not on the same tile + if ( sGridNo == this->sGridNo ) + return FALSE; + + // not in water + if ( TERRAIN_IS_HIGH_WATER( sGridNo ) ) + return FALSE; + + // must be near us + if ( PythSpacesAway( sGridNo, this->sGridNo ) > 1 ) + return FALSE; + + UINT32 tiletype; + UINT16 structurenumber; + if ( !IsDragStructurePresent( sGridNo, this->pathing.bLevel, tiletype, structurenumber ) ) + return FALSE; + + // Now we need to check if there is not a wall between the two middle tiles + UINT8 ubDragDirection = GetDirectionToGridNoFromGridNo( this->sGridNo, sGridNo ); + + { + switch ( ubDragDirection ) + { + case NORTH: + if ( WallOrClosedDoorExistsOfTopLeftOrientation( sGridNo ) ) + return FALSE; + break; + case EAST: + if ( WallOrClosedDoorExistsOfTopRightOrientation( this->sGridNo ) ) + return FALSE; + break; + case SOUTH: + if ( WallOrClosedDoorExistsOfTopLeftOrientation( this->sGridNo ) ) + return FALSE; + break; + case WEST: + if ( WallOrClosedDoorExistsOfTopRightOrientation( sGridNo ) ) + return FALSE; + break; + + case NORTHEAST: + { + bool successA = true; + bool successB = true; + + // two possibilities: + // A) check whether there is no wall to our north, and no wall from there to the east + { + INT32 midpointgridno = NewGridNo( this->sGridNo, DirectionInc( NORTH ) ); + + if ( WallOrClosedDoorExistsOfTopLeftOrientation( midpointgridno ) + || WallOrClosedDoorExistsOfTopRightOrientation( midpointgridno ) ) + { + successA = false; + } + } + + // B) check whether there is no wall to our east, and no wall from there to the north + { + INT32 midpointgridno = NewGridNo( this->sGridNo, DirectionInc( EAST ) ); + + if ( WallOrClosedDoorExistsOfTopRightOrientation( this->sGridNo ) + || WallOrClosedDoorExistsOfTopLeftOrientation( sGridNo ) ) + { + successB = false; + } + } + + if ( !successA && !successB ) + return FALSE; + } + break; + case SOUTHEAST: + { + bool successA = true; + bool successB = true; + + // two possibilities: + // A) check whether there is no wall to our south, and no wall from there to the east + { + INT32 midpointgridno = NewGridNo( this->sGridNo, DirectionInc( SOUTH ) ); + + if ( WallOrClosedDoorExistsOfTopLeftOrientation( this->sGridNo ) + || WallOrClosedDoorExistsOfTopRightOrientation( midpointgridno ) ) + { + successA = false; + } + } + + // B) check whether there is no wall to our east, and no wall from there to the south + { + INT32 midpointgridno = NewGridNo( this->sGridNo, DirectionInc( EAST ) ); + + if ( WallOrClosedDoorExistsOfTopRightOrientation( this->sGridNo ) + || WallOrClosedDoorExistsOfTopLeftOrientation( midpointgridno ) ) + { + successB = false; + } + } + + if ( !successA && !successB ) + return FALSE; + } + break; + case SOUTHWEST: + { + bool successA = true; + bool successB = true; + + // two possibilities: + // A) check whether there is no wall to our south, and no wall from there to the west + { + INT32 midpointgridno = NewGridNo( this->sGridNo, DirectionInc( SOUTH ) ); + + if ( WallOrClosedDoorExistsOfTopLeftOrientation( this->sGridNo ) + || WallOrClosedDoorExistsOfTopRightOrientation( sGridNo ) ) + { + successA = false; + } + } + + // B) check whether there is no wall to our west, and no wall from there to the south + { + INT32 midpointgridno = NewGridNo( this->sGridNo, DirectionInc( WEST ) ); + + if ( WallOrClosedDoorExistsOfTopRightOrientation( midpointgridno ) + || WallOrClosedDoorExistsOfTopLeftOrientation( midpointgridno ) ) + { + successB = false; + } + } + + if ( !successA && !successB ) + return FALSE; + } + break; + case NORTHWEST: + { + bool successA = true; + bool successB = true; + + // two possibilities: + // A) check whether there is no wall to our north, and no wall from there to the west + { + INT32 midpointgridno = NewGridNo( this->sGridNo, DirectionInc( NORTH ) ); + + if ( WallOrClosedDoorExistsOfTopLeftOrientation( midpointgridno ) + || WallOrClosedDoorExistsOfTopRightOrientation( sGridNo ) ) + { + successA = false; + } + } + + // B) check whether there is no wall to our west, and no wall from there to the north + { + INT32 midpointgridno = NewGridNo( this->sGridNo, DirectionInc( WEST ) ); + + if ( WallOrClosedDoorExistsOfTopRightOrientation( midpointgridno ) + || WallOrClosedDoorExistsOfTopLeftOrientation( sGridNo ) ) + { + successB = false; + } + } + + if ( !successA && !successB ) + return FALSE; + } + break; + default: + return FALSE; + break; + } + } + + return TRUE; +} + BOOLEAN SOLDIERTYPE::IsDraggingSomeone( bool aStopIfConditionNotSatisfied ) { if ( this->sDragCorpseID >= 0 ) @@ -20482,6 +20734,13 @@ BOOLEAN SOLDIERTYPE::IsDraggingSomeone( bool aStopIfConditionNotSatisfied ) else if ( aStopIfConditionNotSatisfied ) CancelDrag(); } + else if ( this->sDragGridNo != NOWHERE ) + { + if ( this->CanDragStructure( this->sDragGridNo ) ) + return TRUE; + else if ( aStopIfConditionNotSatisfied ) + CancelDrag(); + } return FALSE; } @@ -20528,6 +20787,27 @@ void SOLDIERTYPE::SetDragOrderCorpse( UINT32 usID ) } } +void SOLDIERTYPE::SetDragOrderStructure( INT32 sGridNo ) +{ + if ( CanDragStructure( sGridNo ) ) + { + // sevenfm: if someone is dragging this corpse, cancel drag + SOLDIERTYPE *pSoldier; + for ( UINT32 uiLoop = 0; uiLoop < guiNumMercSlots; ++uiLoop ) + { + pSoldier = MercPtrs[uiLoop]; + if ( pSoldier && pSoldier->sDragGridNo == sGridNo ) + { + pSoldier->CancelDrag(); + } + } + + CancelDrag(); + + this->sDragGridNo = sGridNo; + } +} + void SOLDIERTYPE::CancelDrag() { // if we are dragging a person, set them to the center of their gridno, otherwise their position might be off @@ -20547,6 +20827,7 @@ void SOLDIERTYPE::CancelDrag() this->usDragPersonID = NOBODY; this->sDragCorpseID = -1; + this->sDragGridNo = NOWHERE; } // Flugente: spy assignments diff --git a/Tactical/Soldier Control.h b/Tactical/Soldier Control.h index 30422acb..f656d3ea 100644 --- a/Tactical/Soldier Control.h +++ b/Tactical/Soldier Control.h @@ -1585,6 +1585,9 @@ public: // Flugente: store disabilities as a flagmask, so we can have multiple ones UINT32 usDisabilityFlagMask; + + // Flugente: drag structures + INT32 sDragGridNo; #ifdef JA2UB //ja25 @@ -2015,9 +2018,11 @@ public: BOOLEAN CanDragInPrinciple(); BOOLEAN CanDragPerson( UINT16 usID ); BOOLEAN CanDragCorpse( UINT16 usCorpseNum ); + BOOLEAN CanDragStructure( INT32 sGridNo ); BOOLEAN IsDraggingSomeone(bool aStopIfConditionNotSatisfied = true); void SetDragOrderPerson( UINT16 usID ); void SetDragOrderCorpse( UINT32 usID ); + void SetDragOrderStructure( INT32 sGridNo ); void CancelDrag(); // Flugente: spy assignments diff --git a/Tactical/Tactical_VS2005.vcproj b/Tactical/Tactical_VS2005.vcproj index b74e3b27..f80ffd15 100644 --- a/Tactical/Tactical_VS2005.vcproj +++ b/Tactical/Tactical_VS2005.vcproj @@ -1260,6 +1260,10 @@ RelativePath=".\XML_StructureDeconstruct.cpp" > + + diff --git a/Tactical/Tactical_VS2008.vcproj b/Tactical/Tactical_VS2008.vcproj index 65dc7bb5..9e5bcdac 100644 --- a/Tactical/Tactical_VS2008.vcproj +++ b/Tactical/Tactical_VS2008.vcproj @@ -1262,6 +1262,10 @@ RelativePath=".\XML_StructureDeconstruct.cpp" > + + diff --git a/Tactical/Tactical_VS2010.vcxproj b/Tactical/Tactical_VS2010.vcxproj index 3c1d2629..39d827ac 100644 --- a/Tactical/Tactical_VS2010.vcxproj +++ b/Tactical/Tactical_VS2010.vcxproj @@ -277,6 +277,7 @@ + diff --git a/Tactical/Tactical_VS2010.vcxproj.filters b/Tactical/Tactical_VS2010.vcxproj.filters index bdee8df2..58cbd2da 100644 --- a/Tactical/Tactical_VS2010.vcxproj.filters +++ b/Tactical/Tactical_VS2010.vcxproj.filters @@ -742,6 +742,9 @@ Source Files + + + Source Files Source Files diff --git a/Tactical/Tactical_VS2013.vcxproj b/Tactical/Tactical_VS2013.vcxproj index 656ccdf0..0740bc05 100644 --- a/Tactical/Tactical_VS2013.vcxproj +++ b/Tactical/Tactical_VS2013.vcxproj @@ -278,6 +278,7 @@ + diff --git a/Tactical/Tactical_VS2013.vcxproj.filters b/Tactical/Tactical_VS2013.vcxproj.filters index 8db6d9d0..649d9c80 100644 --- a/Tactical/Tactical_VS2013.vcxproj.filters +++ b/Tactical/Tactical_VS2013.vcxproj.filters @@ -652,6 +652,9 @@ Source Files + + + Source Files Source Files diff --git a/Tactical/Tactical_VS2017.vcxproj b/Tactical/Tactical_VS2017.vcxproj index 85fde975..43193273 100644 --- a/Tactical/Tactical_VS2017.vcxproj +++ b/Tactical/Tactical_VS2017.vcxproj @@ -278,6 +278,7 @@ + diff --git a/Tactical/Tactical_VS2017.vcxproj.filters b/Tactical/Tactical_VS2017.vcxproj.filters index 8db6d9d0..50989116 100644 --- a/Tactical/Tactical_VS2017.vcxproj.filters +++ b/Tactical/Tactical_VS2017.vcxproj.filters @@ -653,6 +653,9 @@ Source Files + + Source Files + Source Files diff --git a/Tactical/Tactical_VS2019.vcxproj b/Tactical/Tactical_VS2019.vcxproj index 010bc5eb..9b6f2874 100644 --- a/Tactical/Tactical_VS2019.vcxproj +++ b/Tactical/Tactical_VS2019.vcxproj @@ -278,6 +278,7 @@ + diff --git a/Tactical/Tactical_VS2019.vcxproj.filters b/Tactical/Tactical_VS2019.vcxproj.filters index 8db6d9d0..649d9c80 100644 --- a/Tactical/Tactical_VS2019.vcxproj.filters +++ b/Tactical/Tactical_VS2019.vcxproj.filters @@ -652,6 +652,9 @@ Source Files + + + Source Files Source Files diff --git a/Tactical/XML.h b/Tactical/XML.h index f2521e1d..7fb6b056 100644 --- a/Tactical/XML.h +++ b/Tactical/XML.h @@ -78,6 +78,7 @@ typedef PARSE_STAGE; #define FOODOPINIONFILENAME "Items\\FoodOpinion.xml" #define STRUCTUREDECONSTRUCTFILENAME "Items\\StructureDeconstruct.xml" #define STRUCTURECONSTRUCTFILENAME "Items\\StructureConstruct.xml" +#define STRUCTUREMOVEFILENAME "Items\\StructureMove.xml" #define CLOTHESFILENAME "Items\\Clothes.xml" #define RANDOMITEMFILENAME "Items\\RandomItem.xml" #define AMMOFILENAME "Items\\AmmoStrings.xml" @@ -335,6 +336,11 @@ extern BOOLEAN WriteStructureConstructStats(); extern BOOLEAN ReadInInteractiveActionsStats( STR fileName ); extern BOOLEAN WriteInteractiveActionsStats( ); +// Flugente: structure move +extern BOOLEAN ReadInStructureMoveStats( STR fileName ); +extern BOOLEAN WriteStructureMoveStats(); + + // Flugente: merchants extern BOOLEAN ReadInMerchantStats(STR fileName); extern BOOLEAN WriteMerchantStats(); diff --git a/Tactical/XML_Structure_Move.cpp b/Tactical/XML_Structure_Move.cpp new file mode 100644 index 00000000..dd2d2d4d --- /dev/null +++ b/Tactical/XML_Structure_Move.cpp @@ -0,0 +1,216 @@ +#ifdef PRECOMPILEDHEADERS +#include "Tactical All.h" +#else +#include "sgp.h" +#include "overhead.h" +#include "Handle Items.h" +#include "Debug Control.h" +#include "expat.h" +#include "XML.h" +#endif + +STRUCTURE_MOVEPOSSIBLE gStructureMovePossible[STRUCTURE_MOVEPOSSIBLE_MAX]; + +struct +{ + PARSE_STAGE curElement; + + CHAR8 szCharData[MAX_CHAR_DATA_LENGTH + 1]; + STRUCTURE_MOVEPOSSIBLE* curArray; + UINT32 maxArraySize; + UINT32 curIndex; + UINT32 currentDepth; + UINT32 maxReadDepth; +} +typedef structuremoveParseData; + +static void XMLCALL +structuredemoveStartElementHandle( void *userData, const XML_Char *name, const XML_Char **atts ) +{ + structuremoveParseData * pData = (structuremoveParseData *)userData; + + if ( pData->currentDepth <= pData->maxReadDepth ) //are we reading this element? + { + if ( strcmp( name, "STRUCTURESLIST" ) == 0 && pData->curElement == ELEMENT_NONE ) + { + pData->curElement = ELEMENT_LIST; + + pData->maxReadDepth++; //we are not skipping this element + } + else if ( strcmp( name, "STRUCTURE" ) == 0 && pData->curElement == ELEMENT_LIST ) + { + pData->curElement = ELEMENT; + + pData->maxReadDepth++; //we are not skipping this element + } + else if ( pData->curElement == ELEMENT && + ( strcmp( name, "szTileSetDisplayName" ) == 0 || + strcmp( name, "szTileSetName" ) == 0 || + strcmp( name, "allowedtile" ) == 0 ) ) + { + pData->curElement = ELEMENT_PROPERTY; + + pData->maxReadDepth++; //we are not skipping this element + } + + pData->szCharData[0] = '\0'; + } + + pData->currentDepth++; + +} + +static void XMLCALL +structuredemoveCharacterDataHandle( void *userData, const XML_Char *str, int len ) +{ + structuremoveParseData * pData = (structuremoveParseData *)userData; + + if ( ( pData->currentDepth <= pData->maxReadDepth ) && + ( strlen( pData->szCharData ) < MAX_CHAR_DATA_LENGTH ) + ) { + strncat( pData->szCharData, str, __min( (unsigned int)len, MAX_CHAR_DATA_LENGTH - strlen( pData->szCharData ) ) ); + } +} + + +static void XMLCALL +structuredemoveEndElementHandle( void *userData, const XML_Char *name ) +{ + static std::vector statictilevector; + + structuremoveParseData * pData = (structuremoveParseData *)userData; + + if ( pData->currentDepth <= pData->maxReadDepth ) //we're at the end of an element that we've been reading + { + if ( strcmp( name, "STRUCTURESLIST" ) == 0 ) + { + pData->curElement = ELEMENT_NONE; + } + else if ( strcmp( name, "STRUCTURE" ) == 0 ) + { + pData->curElement = ELEMENT_LIST; + + if ( pData->curIndex < pData->maxArraySize ) + { + // for whatever reasons the game crashes in VS2008 Release builds when copying over the tilevector + // this seems odd, as this works just fine in VS2010 and VS2013, and also works in VS205 debug builds + // for now, copy over the content by hand + // check if the vector is empty because assigning an empty vector will crash VS2010 debug builds! + if ( !statictilevector.empty() ) + { + pData->curArray[pData->curIndex].tilevector = statictilevector; + statictilevector.clear(); + } + } + + pData->curIndex++; + } + else if ( strcmp( name, "szTileSetDisplayName" ) == 0 ) + { + pData->curElement = ELEMENT; + + strncpy( pData->curArray[pData->curIndex].szTileSetDisplayName, pData->szCharData, 20 ); + } + else if ( strcmp( name, "szTileSetName" ) == 0 ) + { + pData->curElement = ELEMENT; + + strncpy( pData->curArray[pData->curIndex].szTileSetName, pData->szCharData, 20 ); + } + else if ( strcmp( name, "allowedtile" ) == 0 ) + { + pData->curElement = ELEMENT; + statictilevector.push_back( (UINT8)atol( pData->szCharData ) ); + } + + pData->maxReadDepth--; + } + + pData->currentDepth--; +} + + +BOOLEAN ReadInStructureMoveStats( STR fileName ) +{ + HWFILE hFile; + UINT32 uiBytesRead; + UINT32 uiFSize; + CHAR8 * lpcBuffer; + XML_Parser parser = XML_ParserCreate( NULL ); + + structuremoveParseData pData; + + DebugMsg( TOPIC_JA2, DBG_LEVEL_3, "Loading StructureMove.xml" ); + + // Open foods file + hFile = FileOpen( fileName, FILE_ACCESS_READ, FALSE ); + if ( !hFile ) + return( FALSE ); + + uiFSize = FileGetSize( hFile ); + lpcBuffer = (CHAR8 *)MemAlloc( uiFSize + 1 ); + + //Read in block + if ( !FileRead( hFile, lpcBuffer, uiFSize, &uiBytesRead ) ) + { + MemFree( lpcBuffer ); + return( FALSE ); + } + + lpcBuffer[uiFSize] = 0; //add a null terminator + + FileClose( hFile ); + + XML_SetElementHandler( parser, structuredemoveStartElementHandle, structuredemoveEndElementHandle ); + XML_SetCharacterDataHandler( parser, structuredemoveCharacterDataHandle ); + + memset( &pData, 0, sizeof( pData ) ); + pData.curArray = gStructureMovePossible; + pData.curIndex = 0; + pData.maxArraySize = STRUCTURE_MOVEPOSSIBLE_MAX; + + XML_SetUserData( parser, &pData ); + + if ( !XML_Parse( parser, lpcBuffer, uiFSize, TRUE ) ) + { + CHAR8 errorBuf[511]; + + sprintf( errorBuf, "XML Parser Error in StructureMove.xml: %s at line %d", XML_ErrorString( XML_GetErrorCode( parser ) ), XML_GetCurrentLineNumber( parser ) ); + LiveMessage( errorBuf ); + + MemFree( lpcBuffer ); + return FALSE; + } + + MemFree( lpcBuffer ); + + XML_ParserFree( parser ); + + return( TRUE ); +} + +BOOLEAN WriteStructureMoveStats() +{ + //Debug code; make sure that what we got from the file is the same as what's there + // Open a new file + HWFILE hFile = FileOpen( "TABLEDATA\\StructureMove out.xml", FILE_ACCESS_WRITE | FILE_CREATE_ALWAYS, FALSE ); + if ( !hFile ) + return( FALSE ); + + { + FilePrintf( hFile, "\r\n" ); + for ( UINT32 cnt = 0; cnt < STRUCTURE_MOVEPOSSIBLE_MAX; ++cnt ) + { + FilePrintf( hFile, "\t\r\n" ); + + FilePrintf( hFile, "\t\t%s\r\n", gStructureMovePossible[cnt].szTileSetDisplayName ); + FilePrintf( hFile, "\t\t%s\r\n", gStructureMovePossible[cnt].szTileSetName ); + + FilePrintf( hFile, "\t\r\n" ); + } + FilePrintf( hFile, "\r\n" ); + } + FileClose( hFile ); + + return( TRUE ); +} diff --git a/TileEngine/Structure Internals.h b/TileEngine/Structure Internals.h index 8584676a..9ad1f50d 100644 --- a/TileEngine/Structure Internals.h +++ b/TileEngine/Structure Internals.h @@ -65,60 +65,60 @@ extern UINT8 AtHeight[PROFILE_Z_SIZE]; // how to handle explodable structures // NOT used in DB structures! -#define STRUCTURE_BASE_TILE 0x00000001 +#define STRUCTURE_BASE_TILE 0x00000001 #define STRUCTURE_OPEN 0x00000002 -#define STRUCTURE_OPENABLE 0x00000004 +#define STRUCTURE_OPENABLE 0x00000004 // synonyms for STRUCTURE_OPENABLE -#define STRUCTURE_CLOSEABLE 0x00000004 -#define STRUCTURE_SEARCHABLE 0x00000004 +#define STRUCTURE_CLOSEABLE 0x00000004 +#define STRUCTURE_SEARCHABLE 0x00000004 #define STRUCTURE_HIDDEN 0x00000008 #define STRUCTURE_MOBILE 0x00000010 // STRUCTURE_PASSABLE is set for each structure instance where // the tile flag TILE_PASSABLE is set -#define STRUCTURE_PASSABLE 0x00000020 -#define STRUCTURE_EXPLOSIVE 0x00000040 -#define STRUCTURE_TRANSPARENT 0x00000080 +#define STRUCTURE_PASSABLE 0x00000020 +#define STRUCTURE_EXPLOSIVE 0x00000040 +#define STRUCTURE_TRANSPARENT 0x00000080 #define STRUCTURE_GENERIC 0x00000100 #define STRUCTURE_TREE 0x00000200 #define STRUCTURE_FENCE 0x00000400 -#define STRUCTURE_WIREFENCE 0x00000800 +#define STRUCTURE_WIREFENCE 0x00000800 -#define STRUCTURE_HASITEMONTOP 0x00001000 // ATE: HASITEM: struct has item on top of it +#define STRUCTURE_HASITEMONTOP 0x00001000 // ATE: HASITEM: struct has item on top of it #define STRUCTURE_SPECIAL 0x00002000 -#define STRUCTURE_LIGHTSOURCE 0x00004000 +#define STRUCTURE_LIGHTSOURCE 0x00004000 #define STRUCTURE_VEHICLE 0x00008000 #define STRUCTURE_WALL 0x00010000 -#define STRUCTURE_WALLNWINDOW 0x00020000 -#define STRUCTURE_SLIDINGDOOR 0x00040000 +#define STRUCTURE_WALLNWINDOW 0x00020000 +#define STRUCTURE_SLIDINGDOOR 0x00040000 #define STRUCTURE_DOOR 0x00080000 // a "multi" structure (as opposed to multitiled) is composed of multiple graphics & structures #define STRUCTURE_MULTI 0x00100000 -#define STRUCTURE_CAVEWALL 0x00200000 -#define STRUCTURE_DDOOR_LEFT 0x00400000 -#define STRUCTURE_DDOOR_RIGHT 0x00800000 +#define STRUCTURE_CAVEWALL 0x00200000 +#define STRUCTURE_DDOOR_LEFT 0x00400000 +#define STRUCTURE_DDOOR_RIGHT 0x00800000 -#define STRUCTURE_NORMAL_ROOF 0x01000000 -#define STRUCTURE_SLANTED_ROOF 0x02000000 -#define STRUCTURE_TALL_ROOF 0x04000000 +#define STRUCTURE_NORMAL_ROOF 0x01000000 +#define STRUCTURE_SLANTED_ROOF 0x02000000 +#define STRUCTURE_TALL_ROOF 0x04000000 #define STRUCTURE_SWITCH 0x08000000 -#define STRUCTURE_ON_LEFT_WALL 0x10000000 -#define STRUCTURE_ON_RIGHT_WALL 0x20000000 +#define STRUCTURE_ON_LEFT_WALL 0x10000000 +#define STRUCTURE_ON_RIGHT_WALL 0x20000000 #define STRUCTURE_CORPSE 0x40000000 #define STRUCTURE_PERSON 0x80000000 // COMBINATION FLAGS -#define STRUCTURE_ANYFENCE 0x00000C00 +#define STRUCTURE_ANYFENCE 0x00000C00 #define STRUCTURE_ANYDOOR 0x00CC0000 -#define STRUCTURE_OBSTACLE 0x00008F00 -#define STRUCTURE_WALLSTUFF 0x00CF0000 -#define STRUCTURE_BLOCKSMOVES 0x00208F00 -#define STRUCTURE_TYPE_DEFINED 0x8FEF8F00 -#define STRUCTURE_ROOF 0x07000000 +#define STRUCTURE_OBSTACLE 0x00008F00 +#define STRUCTURE_WALLSTUFF 0x00CF0000 +#define STRUCTURE_BLOCKSMOVES 0x00208F00 +#define STRUCTURE_TYPE_DEFINED 0x8FEF8F00 +#define STRUCTURE_ROOF 0x07000000 #define TILE_ON_ROOF 0x01 #define TILE_PASSABLE 0x02 diff --git a/TileEngine/structure.cpp b/TileEngine/structure.cpp index 711a5498..d9219e49 100644 --- a/TileEngine/structure.cpp +++ b/TileEngine/structure.cpp @@ -633,6 +633,8 @@ STRUCTURE * CreateStructureFromDB( DB_STRUCTURE_REF * pDBStructureRef, UINT8 ubT return( pStructure ); } +extern UINT16 gusTempDragBuildSoldierID; + BOOLEAN OkayToAddStructureToTile( INT32 sBaseGridNo, INT16 sCubeOffset, DB_STRUCTURE_REF * pDBStructureRef, UINT8 ubTileIndex, INT16 sExclusionID, BOOLEAN fAddingForReal = FALSE, INT16 sSoldierID = NOBODY ) { // Verifies whether a structure is blocked from being added to the map at a particular point @@ -953,7 +955,9 @@ BOOLEAN OkayToAddStructureToTile( INT32 sBaseGridNo, INT16 sCubeOffset, DB_STRUC if ((pDBStructure->fFlags & STRUCTURE_OPENABLE)) { - if (pExistingStructure->fFlags & STRUCTURE_OPENABLE) + // Flugente: we allow this if this structure is being dragged by a soldier, otherwise we can't move containers our of rooms (and are likely to unintenionally bar rooms) + if (pExistingStructure->fFlags & STRUCTURE_OPENABLE + && !( gusTempDragBuildSoldierID != NOBODY && MercPtrs[gusTempDragBuildSoldierID]->sDragGridNo ) ) { // don't allow two openable structures in the same tile or things will screw // up on an interface level @@ -2502,7 +2506,6 @@ BOOLEAN FiniStructureDB( void ) STRUCTURE* GetTallestStructureOnGridno( INT32 sGridNo, INT8 bLevel ) { - STRUCTURE* pCurrent = NULL; STRUCTURE* pStruct = NULL; INT8 bestheight = -1; INT16 sDesiredLevel = STRUCTURE_ON_GROUND; @@ -2510,7 +2513,7 @@ STRUCTURE* GetTallestStructureOnGridno( INT32 sGridNo, INT8 bLevel ) if ( bLevel ) sDesiredLevel = STRUCTURE_ON_ROOF; - pCurrent = gpWorldLevelData[sGridNo].pStructureHead; + STRUCTURE* pCurrent = gpWorldLevelData[sGridNo].pStructureHead; while ( pCurrent != NULL ) { @@ -2538,6 +2541,42 @@ STRUCTURE* GetTallestStructureOnGridno( INT32 sGridNo, INT8 bLevel ) return pStruct; } +STRUCTURE* GetTallestStructureOnGridnoDrag( INT32 sGridNo, INT8 bLevel ) +{ + STRUCTURE* pStruct = NULL; + INT8 bestheight = -1; + INT16 sDesiredLevel = STRUCTURE_ON_GROUND; + + if ( bLevel ) + sDesiredLevel = STRUCTURE_ON_ROOF; + + STRUCTURE* pCurrent = gpWorldLevelData[sGridNo].pStructureHead; + + while ( pCurrent != NULL ) + { + // Check level! + if ( pCurrent->sCubeOffset == sDesiredLevel ) + { + if ( pCurrent->fFlags & ( STRUCTURE_CORPSE | STRUCTURE_PERSON | STRUCTURE_ROOF | STRUCTURE_SWITCH | STRUCTURE_WALLSTUFF | STRUCTURE_MULTI | STRUCTURE_CAVEWALL | STRUCTURE_LIGHTSOURCE | STRUCTURE_VEHICLE | STRUCTURE_ANYFENCE | STRUCTURE_TREE ) ) + { + pCurrent = pCurrent->pNext; + continue; + } + + INT8 height = StructureHeight( pCurrent ); + if ( height > bestheight ) + { + bestheight = height; + pStruct = pCurrent; + } + } + + pCurrent = pCurrent->pNext; + } + + return pStruct; +} + INT8 GetBlockingStructureInfo( INT32 sGridNo, INT8 bDir, INT8 bNextDir, INT8 bLevel, INT8 *pStructHeight, STRUCTURE ** ppTallestStructure, BOOLEAN fWallsBlock ) { diff --git a/TileEngine/structure.h b/TileEngine/structure.h index dd3a9f14..083dbd19 100644 --- a/TileEngine/structure.h +++ b/TileEngine/structure.h @@ -85,6 +85,9 @@ BOOLEAN AddZStripInfoToVObject( HVOBJECT hVObject, STRUCTURE_FILE_REF * pStructu // Flugente: return the highest structure (no person, roof or corpse) on a gridno and level STRUCTURE* GetTallestStructureOnGridno( INT32 sGridNo, INT8 bLevel ); +// Flugente: return the highest structure (no person, roof or corpse) on a gridno and level that we could possibly drag +STRUCTURE* GetTallestStructureOnGridnoDrag( INT32 sGridNo, INT8 bLevel ); + // FUNCTIONS FOR DETERMINING STUFF THAT BLOCKS VIEW FOR TILE_bASED LOS INT8 GetBlockingStructureInfo( INT32 sGridNo, INT8 bDir, INT8 bNextDir, INT8 bLevel, INT8 *pStructHeight, STRUCTURE ** ppTallestStructure, BOOLEAN fWallsBlock ); diff --git a/Utils/_ChineseText.cpp b/Utils/_ChineseText.cpp index 75226f2f..62d9f0e4 100644 --- a/Utils/_ChineseText.cpp +++ b/Utils/_ChineseText.cpp @@ -2865,7 +2865,7 @@ STR16 pTraitSkillsDenialStrings[] = L" - 恶魔的财产\n", //L" - posession by a demon" L" - 与枪有关的技能(如自动武器)\n", //L" - a gun-related trait\n", L" - 举起枪(瞄准状态)\n", //L" - aimed gun\n", - L" - 在佣兵旁边放下人或尸体\n", //L" - prone person or corpse next to merc\n", + L" - prone person, corpse or structure next to merc\n", // TODO.Translate L" - 下蹲姿势\n", //L" - crouched position\n", L" - 清空主手装备\n", //L" - free main hand\n", L" - 潜伏技能\n", //L" - covert trait\n", diff --git a/Utils/_DutchText.cpp b/Utils/_DutchText.cpp index 289a5016..a241be5b 100644 --- a/Utils/_DutchText.cpp +++ b/Utils/_DutchText.cpp @@ -2864,7 +2864,7 @@ STR16 pTraitSkillsDenialStrings[] = L" - possession by a demon", L" - a gun-related trait\n", // TODO.Translate L" - aimed gun\n", - L" - prone person or corpse next to merc\n", // TODO.Translate + L" - prone person, corpse or structure next to merc\n", L" - crouched position\n", L" - free main hand\n", L" - covert trait\n", diff --git a/Utils/_EnglishText.cpp b/Utils/_EnglishText.cpp index 99774530..b60056ae 100644 --- a/Utils/_EnglishText.cpp +++ b/Utils/_EnglishText.cpp @@ -2873,7 +2873,7 @@ STR16 pTraitSkillsDenialStrings[] = L" - possession by a demon\n", L" - a gun-related trait\n", L" - aimed gun\n", - L" - prone person or corpse next to merc\n", + L" - prone person, corpse or structure next to merc\n", L" - crouched position\n", L" - free main hand\n", L" - covert trait\n", diff --git a/Utils/_FrenchText.cpp b/Utils/_FrenchText.cpp index 8bd71b76..ff89279d 100644 --- a/Utils/_FrenchText.cpp +++ b/Utils/_FrenchText.cpp @@ -2873,7 +2873,7 @@ STR16 pTraitSkillsDenialStrings[] = L" - possession par un démon", L" - a gun-related trait\n", // TODO.Translate L" - aimed gun\n", - L" - prone person or corpse next to merc\n", // TODO.Translate + L" - prone person, corpse or structure next to merc\n", L" - crouched position\n", L" - free main hand\n", L" - covert trait\n", diff --git a/Utils/_GermanText.cpp b/Utils/_GermanText.cpp index ca19f5e9..6dbeafee 100644 --- a/Utils/_GermanText.cpp +++ b/Utils/_GermanText.cpp @@ -2894,7 +2894,7 @@ STR16 pTraitSkillsDenialStrings[] = L" - besessen von einem Dämon", L" - a gun-related trait\n", // TODO.Translate L" - aimed gun\n", - L" - prone person or corpse next to merc\n", // TODO.Translate + L" - prone person, corpse or structure next to merc\n", L" - crouched position\n", L" - free main hand\n", L" - covert trait\n", diff --git a/Utils/_ItalianText.cpp b/Utils/_ItalianText.cpp index 5cdb97a8..cc4c9c4d 100644 --- a/Utils/_ItalianText.cpp +++ b/Utils/_ItalianText.cpp @@ -2859,7 +2859,7 @@ STR16 pTraitSkillsDenialStrings[] = L" - possession by a demon", L" - a gun-related trait\n", // TODO.Translate L" - aimed gun\n", - L" - prone person or corpse next to merc\n", // TODO.Translate + L" - prone person, corpse or structure next to merc\n", L" - crouched position\n", L" - free main hand\n", L" - covert trait\n", diff --git a/Utils/_PolishText.cpp b/Utils/_PolishText.cpp index 4ac16e75..13e60c58 100644 --- a/Utils/_PolishText.cpp +++ b/Utils/_PolishText.cpp @@ -2871,7 +2871,7 @@ STR16 pTraitSkillsDenialStrings[] = L" - possession by a demon", L" - a gun-related trait\n", // TODO.Translate L" - aimed gun\n", - L" - prone person or corpse next to merc\n", // TODO.Translate + L" - prone person, corpse or structure next to merc\n", L" - crouched position\n", L" - free main hand\n", L" - covert trait\n", diff --git a/Utils/_RussianText.cpp b/Utils/_RussianText.cpp index 7d1f15a2..219d9e21 100644 --- a/Utils/_RussianText.cpp +++ b/Utils/_RussianText.cpp @@ -2865,7 +2865,7 @@ STR16 pTraitSkillsDenialStrings[] = L" - одержим бесами", L" - a gun-related trait\n", // TODO.Translate L" - aimed gun\n", - L" - prone person or corpse next to merc\n", // TODO.Translate + L" - prone person, corpse or structure next to merc\n", L" - crouched position\n", L" - free main hand\n", L" - covert trait\n",