Feature improvement:

- via StructureConstruct.xml and StructureDeconstruct.xml, one can now define which structures can be removed and created via items and the fortifications feature.

For more information, see http://www.ja-galaxy-forum.com/ubbthreads.php/topics/335557#Post335557

does not break savegame compatibility. Requires GameDir >= r2140.

git-svn-id: https://ja2svn.mooo.com/source/ja2/trunk/GameSource/ja2_v1.13/Build@7491 3b4a5df2-a311-0410-b5c6-a8a6f20db521
This commit is contained in:
Flugente
2014-09-07 00:11:16 +00:00
parent 3f4973b54c
commit 760a1b16f1
18 changed files with 1021 additions and 344 deletions
+8
View File
@@ -373,6 +373,14 @@ BOOLEAN LoadExternalGameplayData(STR directoryName)
strcat( fileName, DISEASEFILENAME );
SGP_THROW_IFFALSE( ReadInDiseaseStats( fileName ), DISEASEFILENAME );
strcpy(fileName, directoryName);
strcat(fileName, STRUCTUREDECONSTRUCTFILENAME);
SGP_THROW_IFFALSE(ReadInStructureDeconstructStats(fileName),STRUCTUREDECONSTRUCTFILENAME);
strcpy(fileName, directoryName);
strcat(fileName, STRUCTURECONSTRUCTFILENAME);
SGP_THROW_IFFALSE(ReadInStructureConstructStats(fileName),STRUCTURECONSTRUCTFILENAME);
strcpy(fileName, directoryName);
strcat(fileName, MERCHANTSFILENAME);
SGP_THROW_IFFALSE(ReadInMerchantStats(fileName),MERCHANTSFILENAME);
+279 -158
View File
@@ -86,6 +86,9 @@ UINT8 gubLastTripwire = 0;
// set to TRUE if shift+click was pressed for planting bomb - will use for Improved Bomb Planting feature
BOOLEAN gfShiftBombPlant = FALSE;
// Flugente: (de-)construct structures
STRUCTURE_DECONSTRUCT gStructureDeconstruct[STRUCTURE_DECONSTRUCT_MAX];
STRUCTURE_CONSTRUCT gStructureConstruct[STRUCTURE_CONSTRUCT_MAX];
LEVELNODE *AddItemGraphicToWorld( INVTYPE *pItem, INT32 sGridNo, UINT8 ubLevel );
INT8 GetListMouseHotSpot( INT16 sLargestLineWidth, INT8 bNumItemsListed, INT16 sFontX, INT16 sFontY, INT8 bCurStart );
@@ -1148,14 +1151,21 @@ INT32 HandleItem( SOLDIERTYPE *pSoldier, INT32 sGridNo, INT8 bLevel, UINT16 usHa
}
}
// Flugente: sandbag stuff
if ( HasItemFlag(usHandItem, (EMPTY_SANDBAG|FULL_SANDBAG|SHOVEL|CONCERTINA)) )
// Flugente: (de-)construct structures
if ( HasItemFlag( usHandItem, EMPTY_SANDBAG ) || IsStructureDeconstructItem( usHandItem, sGridNo, pSoldier ) || IsStructureConstructItem( usHandItem, sGridNo, pSoldier ) )
{
// 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)) )
if ( HasItemFlag(usHandItem, EMPTY_SANDBAG) )
{
sAPCost = GetAPsForMultiTurnAction( pSoldier, MTA_FILL_SANDBAG );
// we can only do this on certain terraintypes
INT8 bOverTerrainType = GetTerrainType( sGridNo );
if ( bOverTerrainType != FLAT_GROUND && bOverTerrainType != DIRT_ROAD && bOverTerrainType != LOW_GRASS )
{
return(ITEM_HANDLE_REFUSAL);
}
// check if we have a shovel in our second hand
OBJECTTYPE* pShovelObj = &(pSoldier->inv[SECONDHANDPOS]);
@@ -1163,39 +1173,40 @@ INT32 HandleItem( SOLDIERTYPE *pSoldier, INT32 sGridNo, INT8 bLevel, UINT16 usHa
{
return( ITEM_HANDLE_REFUSAL );
}
// we can only do this on certain terraintypes
INT8 bOverTerrainType = GetTerrainType( sGridNo );
if( bOverTerrainType != FLAT_GROUND && bOverTerrainType != DIRT_ROAD && bOverTerrainType != LOW_GRASS )
{
return( ITEM_HANDLE_REFUSAL );
}
}
// if we have a shovel in our hands, the targeted gridno must be a fortification (debris will do for this check)
else if ( HasItemFlag(usHandItem, (SHOVEL)) )
{
sAPCost = GetAPsForMultiTurnAction( pSoldier, MTA_REMOVE_FORTIFY );
STRUCTURE* pStruct = FindStructure(sGridNo, STRUCTURE_GENERIC);
if ( !pStruct )
{
return( ITEM_HANDLE_REFUSAL );
}
}
}
else
{
sAPCost = GetAPsForMultiTurnAction( pSoldier, MTA_FORTIFY );
// if there is a structure, see if we can deconstruct it, if not, see if we can construct something
STRUCTURE* pStruct = FindStructure( sGridNo, (STRUCTURE_GENERIC | STRUCTURE_WIREFENCE) );
if ( pStruct )
{
if ( !IsStructureDeconstructItem( usHandItem, sGridNo, pSoldier ) )
return(ITEM_HANDLE_REFUSAL);
sAPCost = GetAPsForMultiTurnAction( pSoldier, MTA_REMOVE_FORTIFY );
}
else
{
if ( !IsStructureConstructItem( usHandItem, sGridNo, pSoldier ) )
return(ITEM_HANDLE_REFUSAL);
sAPCost = GetAPsForMultiTurnAction( pSoldier, MTA_FORTIFY );
}
}
sActionGridNo = FindAdjacentGridEx( pSoldier, sGridNo, &ubDirection, &sAdjustedGridNo, TRUE, FALSE );
// we are at pSoldier->sGridNo and the action takes place at sGridNo
// we need to find out in which direction we need to look
// sActionGridNo is the gridno we are supposed to sit
ubDirection = GetDirectionToGridNoFromGridNo( sGridNo, pSoldier->sGridNo );
sActionGridNo = FindAdjacentGridEx( pSoldier, sGridNo, &ubDirection, &sAdjustedGridNo, FALSE, FALSE );
if ( sActionGridNo != -1 )
if ( !TileIsOutOfBounds( sActionGridNo ) )
{
// Calculate AP costs...
sAPCost += PlotPath( pSoldier, sActionGridNo, NO_COPYROUTE, FALSE, TEMPORARY, (UINT16)pSoldier->usUIMovementMode, NOT_STEALTH, FORWARD, pSoldier->bActionPoints);
sAPCost += PlotPath( pSoldier, sActionGridNo, NO_COPYROUTE, FALSE, TEMPORARY, (UINT16)pSoldier->usUIMovementMode, NOT_STEALTH, FORWARD, pSoldier->bActionPoints );
//if ( EnoughPoints( pSoldier, sAPCost, 0, fFromUI ) )
if ( pSoldier->bActionPoints > 0 )
{
// CHECK IF WE ARE AT THIS GRIDNO NOW
@@ -1203,7 +1214,7 @@ INT32 HandleItem( SOLDIERTYPE *pSoldier, INT32 sGridNo, INT8 bLevel, UINT16 usHa
{
// SEND PENDING ACTION
pSoldier->aiData.ubPendingAction = MERC_BUILD_FORTIFICATION;
pSoldier->aiData.sPendingActionData2 = sAdjustedGridNo;
pSoldier->aiData.sPendingActionData2 = sGridNo;
pSoldier->aiData.bPendingActionData3 = ubDirection;
pSoldier->aiData.ubPendingActionAnimCount = 0;
@@ -1212,7 +1223,7 @@ INT32 HandleItem( SOLDIERTYPE *pSoldier, INT32 sGridNo, INT8 bLevel, UINT16 usHa
}
else
{
pSoldier->EVENT_SoldierBuildStructure( sAdjustedGridNo, ubDirection );
pSoldier->EVENT_SoldierBuildStructure( sGridNo, ubDirection );
}
// OK, set UI
@@ -6693,7 +6704,37 @@ void SoldierStealItemFromSoldier( SOLDIERTYPE *pSoldier, SOLDIERTYPE *pOpponent,
SetCustomizableTimerCallbackAndDelay( 1000, CheckForPickedOwnership, TRUE );
}
BOOLEAN BuildFortification( INT32 sGridNo, UINT32 flag )
// can we construct a structure with this item?
BOOLEAN IsStructureConstructItem(UINT16 usItem, INT32 sGridNo, SOLDIERTYPE* pSoldier)
{
if ( !usItem || TileIsOutOfBounds( sGridNo ) )
return FALSE;
for ( UINT16 i = 0; i < STRUCTURE_CONSTRUCT_MAX; ++i )
{
if ( usItem == gStructureConstruct[i].usCreationItem )
return TRUE;
}
return FALSE;
}
// can we remove a structure with this item?
BOOLEAN IsStructureDeconstructItem( UINT16 usItem, INT32 sGridNo, SOLDIERTYPE* pSoldier )
{
if ( !usItem || TileIsOutOfBounds( sGridNo ) )
return FALSE;
for ( UINT16 i = 0; i < STRUCTURE_DECONSTRUCT_MAX; ++i )
{
if ( usItem == gStructureDeconstruct[i].usDeconstructItem )
return TRUE;
}
return FALSE;
}
BOOLEAN BuildFortification( INT32 sGridNo, SOLDIERTYPE *pSoldier, OBJECTTYPE *pObj )
{
UINT32 fHeadType;
UINT16 usUseIndex;
@@ -6702,146 +6743,202 @@ BOOLEAN BuildFortification( INT32 sGridNo, UINT32 flag )
BOOLEAN fOkayToAdd;
UINT8 ubDirection;
if ( gusSelectedSoldier == NOBODY )
// we need a valid soldier and a valid object
if ( !pSoldier || !pObj )
return FALSE;
if( gbWorldSectorZ > 0 || gsInterfaceLevel > 0)
{
// no building on roofs
if( gsInterfaceLevel > 0 )
return FALSE;
// needs to be a valid location
if ( TileIsOutOfBounds( sGridNo ) )
return FALSE;
}
if( InARoom( sGridNo, NULL ) )
return FALSE;
//if( InARoom( sGridNo, NULL ) )
//return FALSE;
// don't build in water
INT8 bOverTerrainType = GetTerrainType( sGridNo );
if( bOverTerrainType == MED_WATER || bOverTerrainType == DEEP_WATER || bOverTerrainType == LOW_WATER )
return FALSE;
UINT16 CurrentStruct = NO_TILE;
if ( sGridNo < 0x80000000 )
ubDirection = pSoldier->ubDirection;
// we'll check wether this item is in our index of known creation items
BOOLEAN indexfound = FALSE;
INT16 structureconstructindex = -1;
for (UINT16 i = 0; i < STRUCTURE_DECONSTRUCT_MAX; ++i)
{
ubDirection = MercPtrs[ gusSelectedSoldier ]->ubDirection;
if ( indexfound )
break;
if ( (flag & CONCERTINA) != 0 )
if ( pObj->usItem == gStructureConstruct[i].usCreationItem )
{
// 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 ( !gStructureConstruct[i].northtilevector.empty( ) )
{
if ( (_strnicmp(gTilesets[ giCurrentTilesetID ].TileSurfaceFilenames[ uiType ], "spot_1.sti", 10) == 0) )
{
usUseObjIndex = uiType;
break;
}
usUseIndex = gStructureConstruct[i].northtilevector[Random( gStructureConstruct[i].northtilevector.size( ) )];
structureconstructindex = i;
indexfound = TRUE;
}
else if( (_strnicmp(gTilesets[ giCurrentTilesetID ].TileSurfaceFilenames[ uiType ], "sandbag.sti", 11) == 0) )
break;
case SOUTH:
if ( !gStructureConstruct[i].southtilevector.empty( ) )
{
usUseIndex = gStructureConstruct[i].southtilevector[Random( gStructureConstruct[i].southtilevector.size( ) )];
structureconstructindex = i;
indexfound = TRUE;
}
break;
case WEST:
if ( !gStructureConstruct[i].westtilevector.empty( ) )
{
usUseIndex = gStructureConstruct[i].westtilevector[Random( gStructureConstruct[i].westtilevector.size( ) )];
structureconstructindex = i;
indexfound = TRUE;
}
break;
case EAST:
if ( !gStructureConstruct[i].easttilevector.empty( ) )
{
usUseIndex = gStructureConstruct[i].easttilevector[Random( gStructureConstruct[i].easttilevector.size( ) )];
structureconstructindex = i;
indexfound = TRUE;
}
break;
default:
// for all other directions, just pick one tile, it doesn not matter from which direction
if ( !gStructureConstruct[i].northtilevector.empty( ) )
{
usUseIndex = gStructureConstruct[i].northtilevector[Random( gStructureConstruct[i].northtilevector.size( ) )];
structureconstructindex = i;
indexfound = TRUE;
}
if ( !indexfound && !gStructureConstruct[i].southtilevector.empty( ) )
{
usUseIndex = gStructureConstruct[i].southtilevector[Random( gStructureConstruct[i].southtilevector.size( ) )];
structureconstructindex = i;
indexfound = TRUE;
}
if ( !indexfound && !gStructureConstruct[i].westtilevector.empty( ) )
{
usUseIndex = gStructureConstruct[i].westtilevector[Random( gStructureConstruct[i].westtilevector.size( ) )];
structureconstructindex = i;
indexfound = TRUE;
}
if ( !indexfound && !gStructureConstruct[i].easttilevector.empty( ) )
{
usUseIndex = gStructureConstruct[i].easttilevector[Random( gStructureConstruct[i].easttilevector.size( ) )];
structureconstructindex = i;
indexfound = TRUE;
}
break;
}
}
}
if ( structureconstructindex < 0 )
{
ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, szMTATextStr[STR_MTA_CANNOT_BUILD] );
return FALSE;
}
// search wether structure exists in the current tilesets. If not, well, too bad
for (UINT32 uiType = 0; uiType < giNumberOfTileTypes; ++uiType)
{
if( gTilesets[ giCurrentTilesetID ].TileSurfaceFilenames[ uiType ][0] )
{
if ( structureconstructindex >= 0 )
{
if ( !_strnicmp(gTilesets[ giCurrentTilesetID ].TileSurfaceFilenames[ uiType ], gStructureConstruct[structureconstructindex].szTileSetName, 10) )
{
usUseObjIndex = uiType;
break;
}
}
}
}
if ( !usUseObjIndex )
if ( !usUseObjIndex )
{
ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, szMTATextStr[ STR_MTA_CANNOT_BUILD ] );
return FALSE;
}
// consumables are removed on creation
if ( structureconstructindex >= 0 && gStructureConstruct[structureconstructindex].usItemStatusLoss )
{
if ( (*pObj)[0]->data.objectStatus <= gStructureConstruct[structureconstructindex].usItemStatusLoss )
{
ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, szMTATextStr[ STR_MTA_CANNOT_BUILD ] );
return FALSE;
UINT16 usItem = pObj->usItem;
// Erase 'material' item from our hand - we 'use' it to build the structure
DeleteObj( &(pSoldier->inv[HANDPOS]) );
// sevenfm: auto-taking of items
if ( !(gTacticalStatus.uiFlags & TURNBASED && gTacticalStatus.uiFlags & INCOMBAT) && gfShiftBombPlant )
{
pSoldier->TakeNewItemFromInventory( pObj->usItem );
}
}
// 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) )
else
{
// 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;
(*pObj)[0]->data.objectStatus -= gStructureConstruct[structureconstructindex].usItemStatusLoss;
}
}
else if ( CurrentStruct == ERASE_TILE && sGridNo < 0x80000000 )
// TODO: if we create an earth pile, create a decal (signifiying the 'hole' we created at our position)
// 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) )
{
RemoveAllStructsOfTypeRange( sGridNo, FIRSTOSTRUCT, LASTOSTRUCT );
RemoveAllShadowsOfTypeRange( sGridNo, FIRSTSHADOW, LASTSHADOW );
// 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;
}
return FALSE;
}
BOOLEAN RemoveFortification( INT32 sGridNo )
BOOLEAN RemoveFortification( INT32 sGridNo, SOLDIERTYPE *pSoldier, OBJECTTYPE *pObj )
{
STRUCTURE* pStruct = FindStructure(sGridNo, STRUCTURE_GENERIC);
// we need a valid soldier and a valid object
if ( !pSoldier || !pObj )
return FALSE;
// not on roofs
if ( gsInterfaceLevel > 0 )
return FALSE;
// needs to be a valid location
if ( TileIsOutOfBounds( sGridNo ) )
return FALSE;
STRUCTURE* pStruct = FindStructure( sGridNo, (STRUCTURE_GENERIC | STRUCTURE_WIREFENCE) );
if ( pStruct != NULL )
{
@@ -6850,34 +6947,58 @@ BOOLEAN RemoveFortification( INT32 sGridNo )
if ( pNode )
{
UINT16 usIndex = pNode->usIndex;
UINT32 uiTileType = 0;
if ( GetTileType( pNode->usIndex, &uiTileType ) )
if ( GetTileType( usIndex, &uiTileType ) )
{
UINT16 usIndex = pNode->usIndex;
// Check if we are a sandbag
if ( _strnicmp( gTilesets[ giCurrentTilesetID ].TileSurfaceFilenames[ uiTileType ], "sandbag.sti", 11) == 0 )
for (UINT16 i = 0; i < STRUCTURE_DECONSTRUCT_MAX; ++i)
{
// Remove old graphic
ApplyMapChangesToMapTempFile( TRUE );
RemoveStruct( sGridNo, pNode->usIndex );
if ( !GridNoIndoors( sGridNo ) && gTileDatabase[ usIndex ].uiFlags & HAS_SHADOW_BUDDY && gTileDatabase[ usIndex ].sBuddyNum != -1 )
// item we have in our hand must be one with which we can deconstruct this
if ( pObj->usItem == gStructureDeconstruct[i].usDeconstructItem )
{
RemoveShadow( sGridNo, gTileDatabase[ usIndex ].sBuddyNum );
// Check if we are a sandbag or a earth pile
if ( !_strnicmp( gTilesets[ giCurrentTilesetID ].TileSurfaceFilenames[ uiTileType ], gStructureDeconstruct[i].szTileSetName, 11) )
{
// we have to check wether this specific structure can be removed. Just checking the tileset name won't be enough.
// For example, we could have a set consisting of crates and piles of earth, which we want to remove via applying a shovel on it.
// We only want to do this on the earth piles, of course - flattening crates via a shovel would be odd :-)
UINT8 numbertofind = (UINT8)(pStruct->pDBStructureRef->pDBStructure->usStructureNumber);
UINT8 size = gStructureDeconstruct[i].tilevector.size();
for ( UINT8 j = 0; j < size; ++j)
{
if ( gStructureDeconstruct[i].tilevector[j] == numbertofind )
{
// 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 );
}
RecompileLocalMovementCosts(sGridNo);
// Turn off permanent changes....
ApplyMapChangesToMapTempFile( FALSE );
SetRenderFlags( RENDER_FLAG_FULL );
// eventually create item
if ( gStructureDeconstruct[i].usItemToCreate && gStructureDeconstruct[i].usCreatedItemStatus )
{
// when creating the object, make it have at least 1% status
CreateItem( gStructureDeconstruct[i].usItemToCreate, max(1, gStructureDeconstruct[i].usCreatedItemStatus), &gTempObject );
AddItemToPool( sGridNo, &gTempObject, 1, 0, 0, -1 );
}
return TRUE;
}
}
}
}
// 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;
}
}
}
+32 -2
View File
@@ -79,6 +79,33 @@ typedef struct
} ITEM_POOL_LOCATOR;
// Flugente: we can construct and deconstruct map structures via items.
typedef struct {
UINT16 usDeconstructItem; // the item that has to be used to deconstruct the structure
UINT16 usItemToCreate; // the item that will be created when we deconstruct a structure
UINT8 usCreatedItemStatus; // status of the item to create
char szTileSetName[20]; // name of the tileset
std::vector<UINT8> tilevector; // structures in the tileset that we can deconstruct
} STRUCTURE_DECONSTRUCT;
#define STRUCTURE_DECONSTRUCT_MAX 200
extern STRUCTURE_DECONSTRUCT gStructureDeconstruct[STRUCTURE_DECONSTRUCT_MAX];
typedef struct {
UINT16 usCreationItem; // the item that will be consumed when creating the structure
UINT8 usItemStatusLoss; // item will lose this number of status points
char szTileSetName[20]; // name of the tileset
std::vector<UINT8> northtilevector; // structures in the tileset we can create while facing north
std::vector<UINT8> southtilevector; // structures in the tileset we can create while facing south
std::vector<UINT8> easttilevector; // structures in the tileset we can create while facing east
std::vector<UINT8> westtilevector; // structures in the tileset we can create while facing west
} STRUCTURE_CONSTRUCT;
#define STRUCTURE_CONSTRUCT_MAX 200
extern STRUCTURE_CONSTRUCT gStructureConstruct[STRUCTURE_CONSTRUCT_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 );
@@ -180,8 +207,11 @@ UINT8 StealItems(SOLDIERTYPE* pSoldier,SOLDIERTYPE* pOpponent, UINT8* ubIndexRet
BOOLEAN MarblesExistAtLocation( INT32 sGridNo, UINT8 ubLevel, INT32 * piItemIndex );
BOOLEAN BuildFortification( INT32 sGridNo , UINT32 flag = FULL_SANDBAG ); // Flugente: build a structure, return true if sucessful
BOOLEAN RemoveFortification( INT32 sGridNo );
// Flugente: build an deconstruct structures
BOOLEAN IsStructureConstructItem( UINT16 usItem, INT32 sGridNo, SOLDIERTYPE* pSoldier ); // can we construct a structure with this item?
BOOLEAN IsStructureDeconstructItem( UINT16 usItem, INT32 sGridNo, SOLDIERTYPE* pSoldier ); // can we remove a structure with this item?
BOOLEAN BuildFortification( INT32 sGridNo, SOLDIERTYPE *pSoldier, OBJECTTYPE *pObj ); // build a structure, return true if sucessful
BOOLEAN RemoveFortification( INT32 sGridNo, SOLDIERTYPE *pSoldier, OBJECTTYPE *pObj ); // remove a structure, return true if sucessful
extern ITEM_POOL *gpItemPool;//dnl ch26 210909
+9 -8
View File
@@ -4512,7 +4512,7 @@ BOOLEAN UIMouseOnValidAttackLocation( SOLDIERTYPE *pSoldier )
// 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)) )
if ( pShovelObj && pShovelObj->exists() && HasItemFlag(pSoldier->inv[ SECONDHANDPOS ].usItem, (SHOVEL)) )
{
INT8 bOverTerrainType = GetTerrainType( usMapPos );
if( bOverTerrainType == FLAT_GROUND || bOverTerrainType == DIRT_ROAD || bOverTerrainType == LOW_GRASS )
@@ -4521,19 +4521,20 @@ BOOLEAN UIMouseOnValidAttackLocation( SOLDIERTYPE *pSoldier )
}
}
}
else if ( HasItemFlag( (&(pSoldier->inv[HANDPOS]))->usItem, (SHOVEL)) )
if ( IsFortificationPossibleAtGridNo( usMapPos ) && IsStructureConstructItem( pSoldier->inv[HANDPOS].usItem, usMapPos, pSoldier ) )
{
STRUCTURE* pStruct = FindStructure(usMapPos, STRUCTURE_GENERIC);
return(TRUE);
}
else if ( IsStructureDeconstructItem( pSoldier->inv[HANDPOS].usItem, usMapPos, pSoldier ) )
{
STRUCTURE* pStruct = FindStructure( usMapPos, (STRUCTURE_GENERIC | STRUCTURE_WIREFENCE) );
if ( pStruct )
{
return( TRUE );
return(TRUE);
}
}
else if ( IsFortificationPossibleAtGridNo( usMapPos ) )
{
return( TRUE );
}
}
return( FALSE );
+3 -3
View File
@@ -731,9 +731,9 @@ extern OBJECTTYPE gTempObject;
// 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 FULL_SANDBAG 0x00000002 //2
#define SHOVEL 0x00000004 //4 // a shovel is used for filling sandbags and other building-related tasks
#define CONCERTINA 0x00000008 //8
//#define CONCERTINA 0x00000008 //8
#define WATER_DRUM 0x00000010 //16 // water drums allow to refill canteens in the sector they are in
#define MEAT_BLOODCAT 0x00000020 //32 // retrieve this by gutting a bloodcat
@@ -765,7 +765,7 @@ extern OBJECTTYPE gTempObject;
#define SIGNAL_SHELL 0x04000000 //67108864 // this is a signal shell that precedes artillery barrages
#define POWER_PACK 0x08000000 //134217728 // item continously powers an item it is attached to
#define SPOTTERITEM 0x10000000 //268435456 // binocular
//#define WOODENPLANK 0x10000000 //268435456 // wooden planks
#define DISEASEPROTECTION_FACE 0x20000000 //536870912 // this item protects us from getting diseases by human contact if worn in a face slot
#define DISEASEPROTECTION_HAND 0x40000000 //1073741824 // this item protects us from getting diseases by human contact if kept in inventory
/*#define PLAYER_NET_4_LVL_4 0x80000000 //2147483648*/
+6 -2
View File
@@ -3162,8 +3162,12 @@ BOOLEAN AdjustToNextAnimationFrame( SOLDIERTYPE *pSoldier )
case 766:
// Say COOL quote
pSoldier->DoMercBattleSound( BATTLE_SOUND_COOL1 );
// Flugente: if doing this action a lot, this gets annoying - only play sound sometimes
if ( Chance(gGameExternalOptions.iChanceSayAnnoyingPhrase) )
{
// Say COOL quote
pSoldier->DoMercBattleSound( BATTLE_SOUND_COOL1 );
}
break;
case 767:
+149 -151
View File
@@ -16188,10 +16188,10 @@ UINT8 SOLDIERTYPE::GetMultiTurnAction( )
return usMultiTurnAction;
}
void SOLDIERTYPE::StartMultiTurnAction( UINT8 usActionType )
void SOLDIERTYPE::StartMultiTurnAction( UINT8 usActionType, INT32 asGridNo )
{
// check wether we can perform any action at all
if ( !this->bActive || !this->bInSector || this->stats.bLife < OKLIFE || TileIsOutOfBounds( this->sGridNo ) || this->bCollapsed )
if ( !this->bActive || !this->bInSector || this->stats.bLife < OKLIFE || TileIsOutOfBounds( asGridNo ) || this->bCollapsed )
return;
// wether an action is possible or not depends on action itself (there are actions without a gridno)
@@ -16200,7 +16200,7 @@ void SOLDIERTYPE::StartMultiTurnAction( UINT8 usActionType )
case MTA_FORTIFY:
case MTA_REMOVE_FORTIFY:
case MTA_FILL_SANDBAG:
if ( this->pathing.bLevel != 0 || gAnimControl[this->usAnimState].ubEndHeight == ANIM_PRONE || gAnimControl[this->usAnimState].ubEndHeight == ANIM_STAND )
if ( this->pathing.bLevel != 0 )
return;
break;
}
@@ -16213,8 +16213,8 @@ void SOLDIERTYPE::StartMultiTurnAction( UINT8 usActionType )
usMultiTurnAction = usActionType;
// the action shall be performed on the gridno directly in front of us
sMTActionGridNo = NewGridNo( this->sGridNo, DirectionInc( this->ubDirection ) );
sMTActionGridNo = asGridNo;
// for now, adding and removing a structure has the same AP cost - that might change in the future
bOverTurnAPS = GetAPsForMultiTurnAction( this, usMultiTurnAction );
@@ -16248,24 +16248,31 @@ BOOLEAN SOLDIERTYPE::UpdateMultiTurnAction( )
CancelMultiTurnAction( FALSE );
return FALSE;
}
// wether an action is possible or not depends on action itself (there are actions without a gridno)
switch ( usMultiTurnAction )
{
case MTA_FORTIFY:
case MTA_REMOVE_FORTIFY:
case MTA_FILL_SANDBAG:
if ( this->pathing.bLevel != 0 || gAnimControl[this->usAnimState].ubEndHeight == ANIM_PRONE || gAnimControl[this->usAnimState].ubEndHeight == ANIM_STAND )
// building on a roof is forbidden! stopt his!
if ( this->pathing.bLevel != 0 )
{
CancelMultiTurnAction( FALSE );
return FALSE;
}
// we have to be crouched, get out of here, set us to be crouched first
if ( gAnimControl[this->usAnimState].ubEndHeight != ANIM_CROUCH )
{
return TRUE;
}
break;
default: // default: exit
{
CancelMultiTurnAction( FALSE );
return FALSE;
}
{
CancelMultiTurnAction( FALSE );
return FALSE;
}
break;
}
@@ -16289,56 +16296,54 @@ BOOLEAN SOLDIERTYPE::UpdateMultiTurnAction( )
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 = GetAPsForMultiTurnAction( this, MTA_FORTIFY );
entirebpcost = APBPConstants[BP_FORTIFICATION];
case MTA_FORTIFY:
{
entireapcost = GetAPsForMultiTurnAction( this, MTA_FORTIFY );
entirebpcost = APBPConstants[BP_FORTIFICATION];
if ( !IsFortificationPossibleAtGridNo( this->sMTActionGridNo ) )
fActionStillValid = FALSE;
else if ( !HasItemFlag( this->inv[HANDPOS].usItem, (FULL_SANDBAG | CONCERTINA) ) )
fActionStillValid = FALSE;
}
break;
case MTA_REMOVE_FORTIFY:
{
entireapcost = GetAPsForMultiTurnAction( this, MTA_REMOVE_FORTIFY );
entirebpcost = APBPConstants[BP_REMOVE_FORTIFICATION];
if ( !HasItemFlag( this->inv[HANDPOS].usItem, (SHOVEL) ) )
fActionStillValid = FALSE;
}
if ( !IsFortificationPossibleAtGridNo( this->sMTActionGridNo ) )
fActionStillValid = FALSE;
else if ( !IsStructureConstructItem( this->inv[HANDPOS].usItem, this->sMTActionGridNo, this ) )
fActionStillValid = FALSE;
}
break;
case MTA_FILL_SANDBAG:
{
entireapcost = GetAPsForMultiTurnAction( this, MTA_FILL_SANDBAG );
entirebpcost = APBPConstants[BP_FILL_SANDBAG];
case MTA_REMOVE_FORTIFY:
{
entireapcost = GetAPsForMultiTurnAction( this, MTA_REMOVE_FORTIFY );
entirebpcost = APBPConstants[BP_REMOVE_FORTIFICATION];
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;
}
}
}
if ( !IsStructureDeconstructItem( this->inv[HANDPOS].usItem, this->sMTActionGridNo, this ) )
fActionStillValid = FALSE;
}
break;
case MTA_FILL_SANDBAG:
{
entireapcost = GetAPsForMultiTurnAction( this, MTA_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;
}
@@ -16351,25 +16356,25 @@ BOOLEAN SOLDIERTYPE::UpdateMultiTurnAction( )
// refresh animations
switch ( usMultiTurnAction )
{
case MTA_FORTIFY:
case MTA_REMOVE_FORTIFY:
case MTA_FILL_SANDBAG:
{
// 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
{
if ( !is_networked )
this->EVENT_InitNewSoldierAnim( CUTTING_FENCE, 0, FALSE );
else
this->ChangeSoldierState( CUTTING_FENCE, 0, 0 );
case MTA_FORTIFY:
case MTA_REMOVE_FORTIFY:
case MTA_FILL_SANDBAG:
{
// 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
{
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];
}
}
// as setting the new animation costs APBPConstants[AP_USEWIRECUTTERS] APs every time, account for that
this->bActionPoints += APBPConstants[AP_USEWIRECUTTERS];
}
}
break;
}
@@ -16378,73 +16383,59 @@ BOOLEAN SOLDIERTYPE::UpdateMultiTurnAction( )
{
switch ( usMultiTurnAction )
{
case MTA_FORTIFY:
{
// Build the thing
if ( BuildFortification( this->sMTActionGridNo, Item[pObj->usItem].usItemFlag ) )
{
UINT16 usItem = pObj->usItem;
// Erase 'material' item from our hand - we 'use' it to build the structure
DeleteObj( &(this->inv[HANDPOS]) );
// sevenfm: auto-taking of items
if ( !(gTacticalStatus.uiFlags & TURNBASED && gTacticalStatus.uiFlags & INCOMBAT) && gfShiftBombPlant )
{
TakeNewItemFromInventory( usItem );
}
case MTA_FORTIFY:
{
// Build the thing
if ( BuildFortification( this->sMTActionGridNo, this, pObj ) )
{
// we gain a bit of experience...
StatChange( this, STRAMT, 4, TRUE );
StatChange( this, HEALTHAMT, 2, TRUE );
}
}
break;
// 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, this, pObj ) )
{
// we gain a bit of experience...
StatChange( this, STRAMT, 3, 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 );
case MTA_FILL_SANDBAG:
{
// eventually search for the number of a sandbag item
UINT16 fullsandbagnr = Item[this->inv[HANDPOS].usItem].usBuddyItem;
if ( fullsandbagnr )
{
INT8 bObjSlot = HANDPOS;
UINT16 usItem = pObj->usItem;
AddItemToPool( this->sMTActionGridNo, &gTempObject, 1, 0, 0, -1 );
CreateItem( fullsandbagnr, 100, &gTempObject );
if ( !(gTacticalStatus.uiFlags & TURNBASED && gTacticalStatus.uiFlags & INCOMBAT) && gfShiftBombPlant )
{
// sevenfm: drop filled sandbag to the ground and take new empty from inventory
AddItemToPool( this->sGridNo, &gTempObject, 1, 0, 0, -1 );
DeleteObj( &(this->inv[HANDPOS]) );
TakeNewItemFromInventory( usItem );
}
else
{
SwapObjs( this, bObjSlot, &gTempObject, TRUE );
}
// we gain a bit of experience...
StatChange( this, STRAMT, 3, TRUE );
StatChange( this, HEALTHAMT, 2, TRUE );
}
}
}
break;
// sevenfm: added this to correctly update interface
DirtyMercPanelInterface( this, DIRTYLEVEL2 );
case MTA_FILL_SANDBAG:
{
// eventually search for the number of a sandbag item
if ( HasItemFlag( fullsandbagnr, FULL_SANDBAG ) || GetFirstItemWithFlag( &fullsandbagnr, FULL_SANDBAG ) )
{
INT8 bObjSlot = HANDPOS;
UINT16 usItem = pObj->usItem;
CreateItem( fullsandbagnr, 100, &gTempObject );
if ( !(gTacticalStatus.uiFlags & TURNBASED && gTacticalStatus.uiFlags & INCOMBAT) && gfShiftBombPlant )
{ // sevenfm: drop filled sandbag to the ground and take new empty from inventory
AddItemToPool( this->sGridNo, &gTempObject, 1, 0, 0, -1 );
DeleteObj( &(this->inv[HANDPOS]) );
TakeNewItemFromInventory( usItem );
}
else
{
SwapObjs( this, bObjSlot, &gTempObject, TRUE );
}
// sevenfm: added this to correctly update interface
DirtyMercPanelInterface( this, DIRTYLEVEL2 );
// we gain a bit of experience...
StatChange( this, STRAMT, 1, TRUE );
StatChange( this, HEALTHAMT, 1, TRUE );
}
}
// we gain a bit of experience...
StatChange( this, STRAMT, 1, TRUE );
StatChange( this, HEALTHAMT, 1, TRUE );
}
}
break;
}
@@ -19742,19 +19733,16 @@ void SOLDIERTYPE::EVENT_SoldierBuildStructure( INT32 sGridNo, UINT8 ubDirection
}
}
// CHANGE DIRECTION AND GOTO ANIMATION NOW
this->EVENT_SetSoldierDesiredDirection( ubDirection );
this->EVENT_SetSoldierDirection( ubDirection );
// do checks here...
OBJECTTYPE* pObj = &(this->inv[HANDPOS]);
if ( pObj && pObj->exists( ) && HasItemFlag( this->inv[HANDPOS].usItem, (EMPTY_SANDBAG | FULL_SANDBAG | SHOVEL | CONCERTINA) ) )
if ( pObj && pObj->exists( ) && (HasItemFlag( this->inv[HANDPOS].usItem, EMPTY_SANDBAG ) || IsStructureConstructItem( this->inv[HANDPOS].usItem, sGridNo, this ) || IsStructureDeconstructItem( this->inv[HANDPOS].usItem, sGridNo, this ) ) )
{
if ( HasItemFlag( this->inv[HANDPOS].usItem, (FULL_SANDBAG | CONCERTINA) ) )
{
// Build the thing
this->StartMultiTurnAction( MTA_FORTIFY );
fSuccess = TRUE;
}
else if ( HasItemFlag( this->inv[HANDPOS].usItem, (EMPTY_SANDBAG) ) )
if ( HasItemFlag( this->inv[HANDPOS].usItem, EMPTY_SANDBAG ) )
{
INT8 bOverTerrainType = GetTerrainType( sGridNo );
if ( bOverTerrainType == FLAT_GROUND || bOverTerrainType == DIRT_ROAD || bOverTerrainType == LOW_GRASS )
@@ -19762,19 +19750,33 @@ void SOLDIERTYPE::EVENT_SoldierBuildStructure( INT32 sGridNo, UINT8 ubDirection
// 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) ) )
if ( pShovelObj && pShovelObj->exists( ) && HasItemFlag( this->inv[SECONDHANDPOS].usItem, SHOVEL ) )
{
this->StartMultiTurnAction( MTA_FILL_SANDBAG );
this->StartMultiTurnAction( MTA_FILL_SANDBAG, sGridNo );
fSuccess = TRUE;
}
}
}
else if ( HasItemFlag( this->inv[HANDPOS].usItem, (SHOVEL) ) )
else
{
this->StartMultiTurnAction( MTA_REMOVE_FORTIFY );
// it is possible that an item is used in both constructing and deconstructing. We thus check wether a structure already exists at sGridNo to dedcut the players intention
// if there is a structure, we will remove this fortification. If there isn't, then we will dig and create a earth pile
STRUCTURE* pStruct = FindStructure( sGridNo, STRUCTURE_GENERIC );
fSuccess = TRUE;
if ( !pStruct && IsStructureConstructItem( this->inv[HANDPOS].usItem, sGridNo, this ) )
{
// Build the thing
this->StartMultiTurnAction( MTA_FORTIFY, sGridNo );
fSuccess = TRUE;
}
else if ( IsStructureDeconstructItem( this->inv[HANDPOS].usItem, sGridNo, this ) )
{
this->StartMultiTurnAction( MTA_REMOVE_FORTIFY, sGridNo );
fSuccess = TRUE;
}
}
}
@@ -19785,10 +19787,6 @@ void SOLDIERTYPE::EVENT_SoldierBuildStructure( INT32 sGridNo, UINT8 ubDirection
}
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
+1 -1
View File
@@ -1775,7 +1775,7 @@ public:
// Flugente: multi-turn actions
UINT8 GetMultiTurnAction();
void StartMultiTurnAction(UINT8 usActionType);
void StartMultiTurnAction( UINT8 usActionType, INT32 asGridNo );
void CancelMultiTurnAction(BOOLEAN fFinished);
BOOLEAN UpdateMultiTurnAction();
+8
View File
@@ -1240,6 +1240,14 @@
RelativePath=".\XML_SpreadPatterns.cpp"
>
</File>
<File
RelativePath=".\XML_StructureConstruct.cpp"
>
</File>
<File
RelativePath=".\XML_StructureDeconstruct.cpp"
>
</File>
<File
RelativePath=".\XML_Taunts.cpp"
>
+8
View File
@@ -1242,6 +1242,14 @@
RelativePath=".\XML_SpreadPatterns.cpp"
>
</File>
<File
RelativePath=".\XML_StructureConstruct.cpp"
>
</File>
<File
RelativePath=".\XML_StructureDeconstruct.cpp"
>
</File>
<File
RelativePath=".\XML_Taunts.cpp"
>
+2
View File
@@ -247,6 +247,8 @@
<ClCompile Include="XML_SoundProfile.cpp" />
<ClCompile Include="XML_Sounds.cpp" />
<ClCompile Include="XML_SpreadPatterns.cpp" />
<ClCompile Include="XML_StructureConstruct.cpp" />
<ClCompile Include="XML_StructureDeconstruct.cpp" />
<ClCompile Include="XML_Taunts.cpp" />
<ClCompile Include="XML_AdditionalTileProperties.cpp" />
<ClCompile Include="XML_TonyInventory.cpp" />
+6
View File
@@ -689,5 +689,11 @@
<ClCompile Include="XML_FoodOpinions.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="XML_StructureDeconstruct.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="XML_StructureConstruct.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
</Project>
+2
View File
@@ -248,6 +248,8 @@
<ClCompile Include="XML_SoundProfile.cpp" />
<ClCompile Include="XML_Sounds.cpp" />
<ClCompile Include="XML_SpreadPatterns.cpp" />
<ClCompile Include="XML_StructureConstruct.cpp" />
<ClCompile Include="XML_StructureDeconstruct.cpp" />
<ClCompile Include="XML_Taunts.cpp" />
<ClCompile Include="XML_AdditionalTileProperties.cpp" />
<ClCompile Include="XML_TonyInventory.cpp" />
+6
View File
@@ -601,6 +601,12 @@
</ClCompile>
<ClCompile Include="XML_SpreadPatterns.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="XML_StructureConstruct.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="XML_StructureDeconstruct.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="XML_TonyInventory.cpp">
<Filter>Source Files</Filter>
+16 -19
View File
@@ -2209,12 +2209,12 @@ UINT8 HandleFortificationCursor( SOLDIERTYPE *pSoldier, INT32 sGridNo, UINT32 ui
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)) )
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)) )
if ( pShovelObj && (pShovelObj->exists()) && HasItemFlag(pSoldier->inv[ SECONDHANDPOS ].usItem, SHOVEL) )
{
INT8 bOverTerrainType = GetTerrainType( sGridNo );
if( bOverTerrainType == FLAT_GROUND || bOverTerrainType == DIRT_ROAD || bOverTerrainType == LOW_GRASS )
@@ -2225,23 +2225,19 @@ UINT8 HandleFortificationCursor( SOLDIERTYPE *pSoldier, INT32 sGridNo, UINT32 ui
return( FORTIFICATION_RED_UICURSOR );
}
if ( HasItemFlag( (&(pSoldier->inv[HANDPOS]))->usItem, (SHOVEL)) )
if ( IsFortificationPossibleAtGridNo( sGridNo ) && IsStructureConstructItem( pSoldier->inv[HANDPOS].usItem, sGridNo, pSoldier ) )
{
STRUCTURE* pStruct = FindStructure(sGridNo, STRUCTURE_GENERIC);
return( FORTIFICATION_GREY_UICURSOR );
}
else if ( IsStructureDeconstructItem( pSoldier->inv[HANDPOS].usItem, sGridNo, pSoldier ) )
{
STRUCTURE* pStruct = FindStructure( sGridNo, (STRUCTURE_GENERIC | STRUCTURE_WIREFENCE));
if ( pStruct )
{
return( FORTIFICATION_GREY_UICURSOR );
return(FORTIFICATION_GREY_UICURSOR);
}
else
return( FORTIFICATION_RED_UICURSOR );
}
// can we build something here?
if ( IsFortificationPossibleAtGridNo( sGridNo ) )
{
return( FORTIFICATION_GREY_UICURSOR );
}
return( FORTIFICATION_RED_UICURSOR );
@@ -2741,7 +2737,7 @@ void HandleRightClickAdjustCursor( SOLDIERTYPE *pSoldier, INT32 usMapPos )
UINT8 GetActionModeCursor( SOLDIERTYPE *pSoldier )
{
UINT8 ubCursor;
UINT8 ubCursor;
UINT16 usInHand;
// If we are an EPC, do nothing....
@@ -2772,7 +2768,6 @@ UINT8 GetActionModeCursor( SOLDIERTYPE *pSoldier )
}
}
if ( pSoldier->bWeaponMode == WM_ATTACHED_GL )
{
// Flugente: if using a rifle grenade, only allow firing if there is a bullet in the gun's magazine (required for firing)
@@ -2787,6 +2782,7 @@ UINT8 GetActionModeCursor( SOLDIERTYPE *pSoldier )
return( TRAJECTORYCURS );
}
if ( pSoldier->bWeaponMode == WM_ATTACHED_GL_BURST || ( pSoldier->bWeaponMode == WM_BURST && Item[pSoldier->inv[HANDPOS].usItem].grenadelauncher ) )
{
if ( gGameSettings.fOptions [ TOPTION_GL_BURST_CURSOR ] )
@@ -2824,8 +2820,9 @@ UINT8 GetActionModeCursor( SOLDIERTYPE *pSoldier )
}
}
// Flugente: cursor for building fortifications
if ( HasItemFlag(usInHand, (EMPTY_SANDBAG|FULL_SANDBAG|SHOVEL|CONCERTINA)) )
// Flugente: cursor for constructing/deconstructing
// at the moment the gridno is not required in these functions, thus 1 suffices
if ( HasItemFlag( usInHand, EMPTY_SANDBAG ) || IsStructureConstructItem( usInHand, 1, pSoldier ) || IsStructureDeconstructItem( usInHand, 1, pSoldier ) )
ubCursor = FORTICURS;
// Flugente: cursor for handcuffs
@@ -2836,7 +2833,7 @@ UINT8 GetActionModeCursor( SOLDIERTYPE *pSoldier )
if ( ItemCanBeAppliedToOthers( usInHand ) )
{
// if item is a bomb, only allow apply if hovering over a soldier (otherwise we cannot mine anymore)
if ( Item[ usInHand ].usItemClass == IC_BOMB )
if ( Item[ usInHand ].usItemClass & IC_BOMB )
{
INT32 usMapPos = NOWHERE;
if ( GetMouseMapPos( &usMapPos ) )
+9
View File
@@ -66,6 +66,8 @@ typedef PARSE_STAGE;
#define DRUGSFILENAME "Items\\Drugs.xml"
#define FOODFILENAME "Items\\Food.xml"
#define FOODOPINIONFILENAME "Items\\FoodOpinion.xml"
#define STRUCTUREDECONSTRUCTFILENAME "Items\\StructureDeconstruct.xml"
#define STRUCTURECONSTRUCTFILENAME "Items\\StructureConstruct.xml"
#define CLOTHESFILENAME "Items\\Clothes.xml"
#define RANDOMITEMFILENAME "Items\\RandomItem.xml"
#define AMMOFILENAME "Items\\AmmoStrings.xml"
@@ -322,6 +324,13 @@ extern BOOLEAN WriteFoodOpinionStats();
extern BOOLEAN ReadInDiseaseStats( STR fileName );
extern BOOLEAN WriteDiseaseStats( );
// Flugente: structure (de-)construct
extern BOOLEAN ReadInStructureDeconstructStats(STR fileName);
extern BOOLEAN WriteStructureDeconstructStats();
extern BOOLEAN ReadInStructureConstructStats(STR fileName);
extern BOOLEAN WriteStructureConstructStats();
// Flugente: merchants
extern BOOLEAN ReadInMerchantStats(STR fileName);
extern BOOLEAN WriteMerchantStats();
+242
View File
@@ -0,0 +1,242 @@
#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
struct
{
PARSE_STAGE curElement;
CHAR8 szCharData[MAX_CHAR_DATA_LENGTH+1];
STRUCTURE_CONSTRUCT curFood;
STRUCTURE_CONSTRUCT * curArray;
UINT32 maxArraySize;
UINT32 currentDepth;
UINT32 maxReadDepth;
}
typedef structureconstructParseData;
UINT16 structureconstructcnt = 0;
static void XMLCALL
structureconstructStartElementHandle(void *userData, const XML_Char *name, const XML_Char **atts)
{
structureconstructParseData * pData = (structureconstructParseData *)userData;
if(pData->currentDepth <= pData->maxReadDepth) //are we reading this element?
{
if(strcmp(name, "STRUCTURESLIST") == 0 && pData->curElement == ELEMENT_NONE)
{
pData->curElement = ELEMENT_LIST;
memset(pData->curArray,0,sizeof(STRUCTURE_CONSTRUCT)*pData->maxArraySize);
pData->maxReadDepth++; //we are not skipping this element
}
else if(strcmp(name, "STRUCTURE") == 0 && pData->curElement == ELEMENT_LIST)
{
pData->curElement = ELEMENT;
memset(&pData->curFood,0,sizeof(STRUCTURE_CONSTRUCT));
pData->maxReadDepth++; //we are not skipping this element
}
else if(pData->curElement == ELEMENT &&
(strcmp(name, "usCreationItem") == 0 ||
strcmp(name, "usItemStatusLoss") == 0 ||
strcmp(name, "szTileSetName") == 0 ||
strcmp(name, "northfacingtile") == 0 ||
strcmp(name, "southfacingtile" ) == 0 ||
strcmp(name, "eastfacingtile" ) == 0 ||
strcmp(name, "westfacingtile") == 0 ))
{
pData->curElement = ELEMENT_PROPERTY;
pData->maxReadDepth++; //we are not skipping this element
}
pData->szCharData[0] = '\0';
}
pData->currentDepth++;
}
static void XMLCALL
structureconstructCharacterDataHandle(void *userData, const XML_Char *str, int len)
{
structureconstructParseData * pData = (structureconstructParseData *)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
structureconstructEndElementHandle(void *userData, const XML_Char *name)
{
structureconstructParseData * pData = (structureconstructParseData *)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;
// we do NOT want to read the first entry -> move stuff by 1
if(structureconstructcnt < pData->maxArraySize)
{
pData->curArray[structureconstructcnt] = pData->curFood; //write the data into the table
}
++structureconstructcnt;
}
else if(strcmp(name, "usCreationItem") == 0)
{
pData->curElement = ELEMENT;
pData->curFood.usCreationItem = (UINT16) atol(pData->szCharData);
}
else if(strcmp(name, "usItemStatusLoss") == 0)
{
pData->curElement = ELEMENT;
pData->curFood.usItemStatusLoss = (UINT8) atol(pData->szCharData);
}
else if(strcmp(name, "szTileSetName") == 0)
{
pData->curElement = ELEMENT;
strncpy(pData->curFood.szTileSetName, pData->szCharData, 20);
}
else if(strcmp(name, "northfacingtile") == 0)
{
pData->curElement = ELEMENT;
pData->curFood.northtilevector.push_back( (UINT8) atol(pData->szCharData) );
}
else if(strcmp(name, "southfacingtile") == 0)
{
pData->curElement = ELEMENT;
pData->curFood.southtilevector.push_back( (UINT8) atol(pData->szCharData) );
}
else if ( strcmp( name, "eastfacingtile" ) == 0 )
{
pData->curElement = ELEMENT;
pData->curFood.easttilevector.push_back( (UINT8)atol( pData->szCharData ) );
}
else if ( strcmp( name, "westfacingtile" ) == 0 )
{
pData->curElement = ELEMENT;
pData->curFood.westtilevector.push_back( (UINT8)atol( pData->szCharData ) );
}
pData->maxReadDepth--;
}
pData->currentDepth--;
}
BOOLEAN ReadInStructureConstructStats(STR fileName)
{
HWFILE hFile;
UINT32 uiBytesRead;
UINT32 uiFSize;
CHAR8 * lpcBuffer;
XML_Parser parser = XML_ParserCreate(NULL);
structureconstructParseData pData;
DebugMsg(TOPIC_JA2, DBG_LEVEL_3, "Loading StructureConstruct.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, structureconstructStartElementHandle, structureconstructEndElementHandle);
XML_SetCharacterDataHandler(parser, structureconstructCharacterDataHandle);
memset(&pData,0,sizeof(pData));
pData.curArray = gStructureConstruct;
pData.maxArraySize = STRUCTURE_CONSTRUCT_MAX;
XML_SetUserData(parser, &pData);
if(!XML_Parse(parser, lpcBuffer, uiFSize, TRUE))
{
CHAR8 errorBuf[511];
sprintf(errorBuf, "XML Parser Error in StructureConstruct.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 WriteStructureConstructStats()
{
//DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"writefoodsstats");
HWFILE hFile;
//Debug code; make sure that what we got from the file is the same as what's there
// Open a new file
hFile = FileOpen( "TABLEDATA\\StructureConstruct out.xml", FILE_ACCESS_WRITE | FILE_CREATE_ALWAYS, FALSE );
if ( !hFile )
return( FALSE );
{
UINT32 cnt;
FilePrintf(hFile,"<STRUCTURESLIST>\r\n");
for(cnt = 0; cnt < STRUCTURE_CONSTRUCT_MAX; ++cnt)
{
FilePrintf(hFile,"\t<STRUCTURE>\r\n");
FilePrintf(hFile,"\t\t<usCreationItem>%d</usCreationItem>\r\n", gStructureConstruct[cnt].usCreationItem );
FilePrintf(hFile,"\t\t<usItemStatusLoss>%d</usItemStatusLoss>\r\n", gStructureConstruct[cnt].usItemStatusLoss );
FilePrintf(hFile,"\t\t<szTileSetName>%s</szTileSetName>\r\n", gStructureConstruct[cnt].szTileSetName );
FilePrintf(hFile,"\t</STRUCTURE>\r\n");
}
FilePrintf(hFile,"</STRUCTURESLIST>\r\n");
}
FileClose( hFile );
return( TRUE );
}
+235
View File
@@ -0,0 +1,235 @@
#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
struct
{
PARSE_STAGE curElement;
CHAR8 szCharData[MAX_CHAR_DATA_LENGTH+1];
STRUCTURE_DECONSTRUCT curFood;
STRUCTURE_DECONSTRUCT * curArray;
UINT32 maxArraySize;
UINT32 currentDepth;
UINT32 maxReadDepth;
}
typedef structuredeconstructParseData;
UINT16 structuredeconstructcnt = 0;
static void XMLCALL
structuredeconstructStartElementHandle(void *userData, const XML_Char *name, const XML_Char **atts)
{
structuredeconstructParseData * pData = (structuredeconstructParseData *)userData;
if(pData->currentDepth <= pData->maxReadDepth) //are we reading this element?
{
if(strcmp(name, "STRUCTURESLIST") == 0 && pData->curElement == ELEMENT_NONE)
{
pData->curElement = ELEMENT_LIST;
memset(pData->curArray,0,sizeof(STRUCTURE_DECONSTRUCT)*pData->maxArraySize);
pData->maxReadDepth++; //we are not skipping this element
}
else if(strcmp(name, "STRUCTURE") == 0 && pData->curElement == ELEMENT_LIST)
{
pData->curElement = ELEMENT;
memset(&pData->curFood,0,sizeof(STRUCTURE_DECONSTRUCT));
pData->maxReadDepth++; //we are not skipping this element
}
else if(pData->curElement == ELEMENT &&
(strcmp(name, "usDeconstructItem") == 0 ||
strcmp( name, "usItemToCreate" ) == 0 ||
strcmp( name, "usCreatedItemStatus" ) == 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
structuredeconstructCharacterDataHandle(void *userData, const XML_Char *str, int len)
{
structuredeconstructParseData * pData = (structuredeconstructParseData *)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
structuredeconstructEndElementHandle(void *userData, const XML_Char *name)
{
structuredeconstructParseData * pData = (structuredeconstructParseData *)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(structuredeconstructcnt < pData->maxArraySize)
{
pData->curArray[structuredeconstructcnt] = pData->curFood; //write the data into the table
}
++structuredeconstructcnt;
}
else if ( strcmp( name, "usDeconstructItem" ) == 0 )
{
pData->curElement = ELEMENT;
pData->curFood.usDeconstructItem = (UINT16)atol( pData->szCharData );
}
else if(strcmp(name, "usItemToCreate") == 0)
{
pData->curElement = ELEMENT;
pData->curFood.usItemToCreate = (UINT16) atol(pData->szCharData);
}
else if ( strcmp( name, "usCreatedItemStatus" ) == 0 )
{
pData->curElement = ELEMENT;
pData->curFood.usCreatedItemStatus = (UINT8)atol( pData->szCharData );
}
else if(strcmp(name, "szTileSetName") == 0)
{
pData->curElement = ELEMENT;
strncpy(pData->curFood.szTileSetName, pData->szCharData, 20);
}
else if(strcmp(name, "allowedtile") == 0)
{
pData->curElement = ELEMENT;
pData->curFood.tilevector.push_back( (UINT8) atol(pData->szCharData) );
}
pData->maxReadDepth--;
}
pData->currentDepth--;
}
BOOLEAN ReadInStructureDeconstructStats(STR fileName)
{
HWFILE hFile;
UINT32 uiBytesRead;
UINT32 uiFSize;
CHAR8 * lpcBuffer;
XML_Parser parser = XML_ParserCreate(NULL);
structuredeconstructParseData pData;
DebugMsg(TOPIC_JA2, DBG_LEVEL_3, "Loading StructureDeconstruct.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, structuredeconstructStartElementHandle, structuredeconstructEndElementHandle);
XML_SetCharacterDataHandler(parser, structuredeconstructCharacterDataHandle);
memset(&pData,0,sizeof(pData));
pData.curArray = gStructureDeconstruct;
pData.maxArraySize = STRUCTURE_DECONSTRUCT_MAX;
XML_SetUserData(parser, &pData);
if(!XML_Parse(parser, lpcBuffer, uiFSize, TRUE))
{
CHAR8 errorBuf[511];
sprintf(errorBuf, "XML Parser Error in StructureDeconstruct.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 WriteStructureDeconstructStats()
{
//DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"writefoodsstats");
HWFILE hFile;
//Debug code; make sure that what we got from the file is the same as what's there
// Open a new file
hFile = FileOpen( "TABLEDATA\\StructureDeconstruct out.xml", FILE_ACCESS_WRITE | FILE_CREATE_ALWAYS, FALSE );
if ( !hFile )
return( FALSE );
{
UINT32 cnt;
FilePrintf(hFile,"<STRUCTURESLIST>\r\n");
for(cnt = 0; cnt < STRUCTURE_DECONSTRUCT_MAX; ++cnt)
{
FilePrintf(hFile,"\t<STRUCTURE>\r\n");
FilePrintf(hFile,"\t\t<usDeconstructItem>%d</usDeconstructItem>\r\n", gStructureDeconstruct[cnt].usDeconstructItem );
FilePrintf(hFile,"\t\t<usItemToCreate>%d</usItemToCreate>\r\n", gStructureDeconstruct[cnt].usItemToCreate );
FilePrintf(hFile,"\t\t<usCreatedItemStatus>%d</usCreatedItemStatus>\r\n", gStructureDeconstruct[cnt].usCreatedItemStatus );
FilePrintf(hFile,"\t\t<szTileSetName>%s</szTileSetName>\r\n", gStructureDeconstruct[cnt].szTileSetName );
FilePrintf(hFile,"\t</STRUCTURE>\r\n");
}
FilePrintf(hFile,"</STRUCTURESLIST>\r\n");
}
FileClose( hFile );
return( TRUE );
}