Improved feature: structures can be dragged. Which ones is defined in a new xml.

For more info, see http://thepit.ja-galaxy-forum.com/index.php?t=msg&goto=360672&#msg_360672

Requires GameDir >= r2558

git-svn-id: https://ja2svn.mooo.com/source/ja2/trunk/GameSource/ja2_v1.13/Build@8870 3b4a5df2-a311-0410-b5c6-a8a6f20db521
This commit is contained in:
Flugente
2020-07-23 22:11:23 +00:00
parent c8737017ff
commit 0c4997f6ec
33 changed files with 1021 additions and 85 deletions
+276
View File
@@ -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<INT32> 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<INT32>::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
+21
View File
@@ -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<UINT8> 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();
+49 -2
View File
@@ -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<void, UINT32>( &Wrapper_Function_DragSelection_GridNo, sTempGridNo ) );
GetPopup()->addOption( *pOption );
}
}
}
// cancel option
swprintf( pStr, pSkillMenuStrings[SKILLMENU_CANCEL] );
pOption = new POPUP_OPTION( &std::wstring( pStr ), new popupCallbackFunction<void, UINT32>( &Wrapper_Cancel_SoldierSelection, 0 ) );
pOption = new POPUP_OPTION( &std::wstring( pStr ), new popupCallbackFunction<void, UINT32>( &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 ////////////////////////////////////////////
/**
+1
View File
@@ -162,6 +162,7 @@ public:
void Setup( UINT32 aVal );
void Functions( UINT32 aVal );
void FunctionsGridNo( INT32 aVal );
private:
UINT32 usSkill;
+312 -31
View File
@@ -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
+5
View File
@@ -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
+4
View File
@@ -1260,6 +1260,10 @@
RelativePath=".\XML_StructureDeconstruct.cpp"
>
</File>
<File
RelativePath=".\XML_Structure_Move.cpp"
>
</File>
<File
RelativePath=".\XML_Taunts.cpp"
>
+4
View File
@@ -1262,6 +1262,10 @@
RelativePath=".\XML_StructureDeconstruct.cpp"
>
</File>
<File
RelativePath=".\XML_Structure_Move.cpp"
>
</File>
<File
RelativePath=".\XML_Taunts.cpp"
>
+1
View File
@@ -277,6 +277,7 @@
<ClCompile Include="XML_SpreadPatterns.cpp" />
<ClCompile Include="XML_StructureConstruct.cpp" />
<ClCompile Include="XML_StructureDeconstruct.cpp" />
<ClCompile Include="XML_Structure_Move.cpp" />
<ClCompile Include="XML_Taunts.cpp" />
<ClCompile Include="XML_AdditionalTileProperties.cpp" />
<ClCompile Include="XML_TonyInventory.cpp" />
+3
View File
@@ -742,6 +742,9 @@
</ClCompile>
<ClCompile Include="XML_StructureDeconstruct.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="XML_Structure_Move.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="XML_StructureConstruct.cpp">
<Filter>Source Files</Filter>
+1
View File
@@ -278,6 +278,7 @@
<ClCompile Include="XML_SpreadPatterns.cpp" />
<ClCompile Include="XML_StructureConstruct.cpp" />
<ClCompile Include="XML_StructureDeconstruct.cpp" />
<ClCompile Include="XML_Structure_Move.cpp" />
<ClCompile Include="XML_Taunts.cpp" />
<ClCompile Include="XML_AdditionalTileProperties.cpp" />
<ClCompile Include="XML_TonyInventory.cpp" />
+3
View File
@@ -652,6 +652,9 @@
</ClCompile>
<ClCompile Include="XML_StructureDeconstruct.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="XML_Structure_Move.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="XML_TonyInventory.cpp">
<Filter>Source Files</Filter>
+1
View File
@@ -278,6 +278,7 @@
<ClCompile Include="XML_SpreadPatterns.cpp" />
<ClCompile Include="XML_StructureConstruct.cpp" />
<ClCompile Include="XML_StructureDeconstruct.cpp" />
<ClCompile Include="XML_Structure_Move.cpp" />
<ClCompile Include="XML_Taunts.cpp" />
<ClCompile Include="XML_AdditionalTileProperties.cpp" />
<ClCompile Include="XML_TonyInventory.cpp" />
+3
View File
@@ -653,6 +653,9 @@
<ClCompile Include="XML_StructureDeconstruct.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="XML_Structure_Move.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="XML_TonyInventory.cpp">
<Filter>Source Files</Filter>
</ClCompile>
+1
View File
@@ -278,6 +278,7 @@
<ClCompile Include="XML_SpreadPatterns.cpp" />
<ClCompile Include="XML_StructureConstruct.cpp" />
<ClCompile Include="XML_StructureDeconstruct.cpp" />
<ClCompile Include="XML_Structure_Move.cpp" />
<ClCompile Include="XML_Taunts.cpp" />
<ClCompile Include="XML_AdditionalTileProperties.cpp" />
<ClCompile Include="XML_TonyInventory.cpp" />
+3
View File
@@ -652,6 +652,9 @@
</ClCompile>
<ClCompile Include="XML_StructureDeconstruct.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="XML_Structure_Move.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="XML_TonyInventory.cpp">
<Filter>Source Files</Filter>
+6
View File
@@ -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();
+216
View File
@@ -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<UINT8> 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, "<STRUCTURESLIST>\r\n" );
for ( UINT32 cnt = 0; cnt < STRUCTURE_MOVEPOSSIBLE_MAX; ++cnt )
{
FilePrintf( hFile, "\t<STRUCTURE>\r\n" );
FilePrintf( hFile, "\t\t<szTileSetDisplayName>%s</szTileSetDisplayName>\r\n", gStructureMovePossible[cnt].szTileSetDisplayName );
FilePrintf( hFile, "\t\t<szTileSetName>%s</szTileSetName>\r\n", gStructureMovePossible[cnt].szTileSetName );
FilePrintf( hFile, "\t</STRUCTURE>\r\n" );
}
FilePrintf( hFile, "</STRUCTURESLIST>\r\n" );
}
FileClose( hFile );
return( TRUE );
}