mirror of
https://github.com/1dot13/source.git
synced 2026-07-29 13:52:17 +02:00
Original Source for 1.13 Mod High Resolution version from 12/06/05
git-svn-id: https://ja2svn.mooo.com/source/ja2/trunk/GameSource/ja2_v1.13/Build@21 3b4a5df2-a311-0410-b5c6-a8a6f20db521
This commit is contained in:
@@ -0,0 +1,999 @@
|
||||
#ifdef PRECOMPILEDHEADERS
|
||||
#include "Editor All.h"
|
||||
#else
|
||||
#include "builddefines.h"
|
||||
#endif
|
||||
|
||||
#ifdef JA2EDITOR
|
||||
|
||||
#ifndef PRECOMPILEDHEADERS
|
||||
#include "worlddef.h"
|
||||
#include "worldman.h"
|
||||
#include "smooth.h"
|
||||
#include "input.h"
|
||||
#include "debug.h"
|
||||
#include "Isometric Utils.h"
|
||||
|
||||
#include "editscreen.h"
|
||||
#include "Editor Undo.h"
|
||||
#include "Render Fun.h" //for access to gubWorldRoomInfo;
|
||||
#include "Cursor Modes.h"
|
||||
#include "Exit Grids.h"
|
||||
#endif
|
||||
|
||||
/*
|
||||
Kris -- Notes on how the undo code works:
|
||||
|
||||
At the bottom of the hierarchy, we need to determine the state of the undo command. The idea
|
||||
is that we want to separate undo commands by separating them by new mouse clicks. By holding a mouse
|
||||
down and painting various objects in the world would all constitute a single undo command. As soon as
|
||||
the mouse is release, then a new undo command is setup. So, to automate this, there is a call every
|
||||
frame to DetermineUndoState().
|
||||
|
||||
At the next level, there is a binary tree that keeps track of what map indices have been backup up in
|
||||
the current undo command. The whole reason to maintain this list, is to avoid multiple map elements of
|
||||
the same map index from being saved. In the outer code, everytime something is changed, a call to
|
||||
AddToUndoList() is called, so there are many cases (especially with building/terrain smoothing) that the
|
||||
same mapindex is added to the undo list. This is also completely transparent, and doesn't need to be
|
||||
maintained.
|
||||
|
||||
In the outer code, there are several calls to AddToUndoList( iMapIndex ). This function basically looks
|
||||
in the binary tree for an existing entry, and if there isn't, then the entire mapelement is saved (with
|
||||
the exception of the merc level ). Lights are also supported, but there is a totally different methodology
|
||||
for accomplishing this. The equivalent function is AddLightToUndoList( iMapIndex ). In this case, only the
|
||||
light is saved, along with internal maintanance of several flags.
|
||||
|
||||
The actual mapelement copy code, is very complex. The mapelement is copied in parallel with a new one which
|
||||
has to allocate several nodes of several types because a mapelement contains over a dozen separate lists, and
|
||||
all of them need to be saved. The structure information of certain mapelements may be multitiled and must
|
||||
also save the affected gridno's as well. This is also done internally. Basically, your call to
|
||||
AddToUndoList() for any particular gridno, may actually save several entries (like for a car which could be 6+
|
||||
tiles)
|
||||
|
||||
MERCS
|
||||
Mercs are not supported in the undo code. Because they are so dynamic, and stats could change, they could
|
||||
move, etc., it doesn't need to be in the undo code. The editor has its own way of dealing with mercs which
|
||||
doesn't use the undo methodology.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
BOOLEAN gfUndoEnabled = FALSE;
|
||||
|
||||
void EnableUndo()
|
||||
{
|
||||
gfUndoEnabled = TRUE;
|
||||
}
|
||||
|
||||
void DisableUndo()
|
||||
{
|
||||
gfUndoEnabled = FALSE;
|
||||
}
|
||||
|
||||
// undo node data element
|
||||
typedef struct
|
||||
{
|
||||
INT32 iMapIndex;
|
||||
MAP_ELEMENT *pMapTile;
|
||||
BOOLEAN fLightSaved; //determines that a light has been saved
|
||||
UINT8 ubLightRadius; //the radius of the light to build if undo is called
|
||||
UINT8 ubLightID; //only applies if a light was saved.
|
||||
UINT8 ubRoomNum;
|
||||
} undo_struct;
|
||||
|
||||
// Undo stack node
|
||||
typedef struct TAG_undo_stack
|
||||
{
|
||||
INT32 iCmdCount;
|
||||
undo_struct *pData;
|
||||
struct TAG_undo_stack *pNext;
|
||||
INT32 iUndoType;
|
||||
} undo_stack;
|
||||
undo_stack *gpTileUndoStack = NULL;
|
||||
|
||||
// Map tile element manipulation functions
|
||||
BOOLEAN CopyMapElementFromWorld( MAP_ELEMENT *pMapTile, INT32 iMapIndex );
|
||||
BOOLEAN SwapMapElementWithWorld( INT32 iDestMapTileIndex, MAP_ELEMENT *pMapTile );
|
||||
|
||||
// Undo structure functions
|
||||
BOOLEAN DeleteTopStackNode( void );
|
||||
undo_stack *DeleteThisStackNode( undo_stack *pThisNode );
|
||||
BOOLEAN DeleteStackNodeContents( undo_stack *pCurrent );
|
||||
BOOLEAN AddToUndoListCmd( INT32 iMapIndex, INT32 iCmdCount );
|
||||
void CropStackToMaxLength( INT32 iMaxCmds );
|
||||
void SmoothUndoMapTileTerrain( INT32 iWorldTile, MAP_ELEMENT *pUndoTile );
|
||||
|
||||
|
||||
BOOLEAN fNewUndoCmd = TRUE;
|
||||
BOOLEAN gfIgnoreUndoCmdsForLights = FALSE;
|
||||
|
||||
//New pre-undo binary tree stuff
|
||||
//With this, new undo commands will not duplicate saves in the same command. This will
|
||||
//increase speed, and save memory.
|
||||
typedef struct MapIndexBinaryTree
|
||||
{
|
||||
struct MapIndexBinaryTree *left, *right;
|
||||
UINT16 usMapIndex;
|
||||
}MapIndexBinaryTree;
|
||||
|
||||
MapIndexBinaryTree *top = NULL;
|
||||
void ClearUndoMapIndexTree();
|
||||
BOOLEAN AddMapIndexToTree( UINT16 usMapIndex );
|
||||
void CheckMapIndexForMultiTileStructures( UINT16 usMapIndex );
|
||||
void CheckForMultiTilesInTreeAndAddToUndoList( MapIndexBinaryTree *node );
|
||||
|
||||
|
||||
//Recursively deletes all nodes below the node passed including itself.
|
||||
void DeleteTreeNode( MapIndexBinaryTree **node )
|
||||
{
|
||||
if( (*node)->left )
|
||||
DeleteTreeNode( &((*node)->left) );
|
||||
if( (*node)->right )
|
||||
DeleteTreeNode( &((*node)->right) );
|
||||
MemFree( *node );
|
||||
*node = NULL;
|
||||
}
|
||||
|
||||
//Recursively delete all nodes (from the top down).
|
||||
void ClearUndoMapIndexTree()
|
||||
{
|
||||
if( top )
|
||||
DeleteTreeNode( &top );
|
||||
}
|
||||
|
||||
BOOLEAN AddMapIndexToTree( UINT16 usMapIndex )
|
||||
{
|
||||
MapIndexBinaryTree *curr, *parent;
|
||||
if( !top )
|
||||
{
|
||||
top = (MapIndexBinaryTree*)MemAlloc( sizeof( MapIndexBinaryTree ) );
|
||||
Assert( top );
|
||||
top->usMapIndex = usMapIndex;
|
||||
top->left = NULL;
|
||||
top->right = NULL;
|
||||
return TRUE;
|
||||
}
|
||||
curr = top;
|
||||
parent = NULL;
|
||||
//Traverse down the tree and attempt to find a matching mapindex.
|
||||
//If one is encountered, then we fail, and don't add the mapindex to the
|
||||
//tree.
|
||||
while( curr )
|
||||
{
|
||||
parent = curr;
|
||||
if( curr->usMapIndex == usMapIndex ) //found a match, so stop
|
||||
return FALSE;
|
||||
//if the mapIndex is < node's mapIndex, then go left, else right
|
||||
curr = ( usMapIndex < curr->usMapIndex ) ? curr->left : curr->right;
|
||||
}
|
||||
//if we made it this far, then curr is null and parent is pointing
|
||||
//directly above.
|
||||
//Create the new node and fill in the information.
|
||||
curr = (MapIndexBinaryTree*)MemAlloc( sizeof( MapIndexBinaryTree ) );
|
||||
Assert( curr );
|
||||
curr->usMapIndex = usMapIndex;
|
||||
curr->left = NULL;
|
||||
curr->right = NULL;
|
||||
//Now link the new node to the parent.
|
||||
if( curr->usMapIndex < parent->usMapIndex )
|
||||
parent->left = curr;
|
||||
else
|
||||
parent->right = curr;
|
||||
return TRUE;
|
||||
}
|
||||
//*************************************************************************
|
||||
//
|
||||
// Start of Undo code
|
||||
//
|
||||
//*************************************************************************
|
||||
|
||||
BOOLEAN DeleteTopStackNode( void )
|
||||
{
|
||||
undo_stack *pCurrent;
|
||||
|
||||
pCurrent = gpTileUndoStack;
|
||||
|
||||
DeleteStackNodeContents( pCurrent );
|
||||
|
||||
// Remove node from stack, and free it's memory
|
||||
gpTileUndoStack = gpTileUndoStack->pNext;
|
||||
MemFree( pCurrent );
|
||||
|
||||
return( TRUE );
|
||||
}
|
||||
|
||||
|
||||
undo_stack *DeleteThisStackNode( undo_stack *pThisNode )
|
||||
{
|
||||
undo_stack *pCurrent;
|
||||
undo_stack *pNextNode;
|
||||
|
||||
pCurrent = pThisNode;
|
||||
pNextNode = pThisNode->pNext;
|
||||
|
||||
// Remove node from stack, and free it's memory
|
||||
DeleteStackNodeContents( pCurrent );
|
||||
MemFree( pCurrent );
|
||||
|
||||
return( pNextNode );
|
||||
}
|
||||
|
||||
|
||||
BOOLEAN DeleteStackNodeContents( undo_stack *pCurrent )
|
||||
{
|
||||
undo_struct *pData;
|
||||
MAP_ELEMENT *pMapTile;
|
||||
LEVELNODE *pLandNode;
|
||||
LEVELNODE *pObjectNode;
|
||||
LEVELNODE *pStructNode;
|
||||
LEVELNODE *pShadowNode;
|
||||
LEVELNODE *pMercNode;
|
||||
LEVELNODE *pTopmostNode;
|
||||
LEVELNODE *pRoofNode;
|
||||
LEVELNODE *pOnRoofNode;
|
||||
STRUCTURE *pStructureNode;
|
||||
|
||||
pData = pCurrent->pData;
|
||||
pMapTile = pData->pMapTile;
|
||||
|
||||
if( !pMapTile )
|
||||
return TRUE; //light was saved -- mapelement wasn't saved.
|
||||
|
||||
// Free the memory associated with the map tile liked lists
|
||||
pLandNode = pMapTile->pLandHead;
|
||||
while ( pLandNode != NULL )
|
||||
{
|
||||
pMapTile->pLandHead = pLandNode->pNext;
|
||||
MemFree( pLandNode );
|
||||
pLandNode = pMapTile->pLandHead;
|
||||
}
|
||||
|
||||
pObjectNode = pMapTile->pObjectHead;
|
||||
while ( pObjectNode != NULL )
|
||||
{
|
||||
pMapTile->pObjectHead = pObjectNode->pNext;
|
||||
MemFree( pObjectNode );
|
||||
pObjectNode = pMapTile->pObjectHead;
|
||||
}
|
||||
|
||||
pStructNode = pMapTile->pStructHead;
|
||||
while ( pStructNode != NULL )
|
||||
{
|
||||
pMapTile->pStructHead = pStructNode->pNext;
|
||||
MemFree( pStructNode );
|
||||
pStructNode = pMapTile->pStructHead;
|
||||
}
|
||||
|
||||
pShadowNode = pMapTile->pShadowHead;
|
||||
while ( pShadowNode != NULL )
|
||||
{
|
||||
pMapTile->pShadowHead = pShadowNode->pNext;
|
||||
MemFree( pShadowNode );
|
||||
pShadowNode = pMapTile->pShadowHead;
|
||||
}
|
||||
|
||||
pMercNode = pMapTile->pMercHead;
|
||||
while ( pMercNode != NULL )
|
||||
{
|
||||
pMapTile->pMercHead = pMercNode->pNext;
|
||||
MemFree( pMercNode );
|
||||
pMercNode = pMapTile->pMercHead;
|
||||
}
|
||||
|
||||
pRoofNode = pMapTile->pRoofHead;
|
||||
while ( pRoofNode != NULL )
|
||||
{
|
||||
pMapTile->pRoofHead = pRoofNode->pNext;
|
||||
MemFree( pRoofNode );
|
||||
pRoofNode = pMapTile->pRoofHead;
|
||||
}
|
||||
|
||||
pOnRoofNode = pMapTile->pOnRoofHead;
|
||||
while ( pOnRoofNode != NULL )
|
||||
{
|
||||
pMapTile->pOnRoofHead = pOnRoofNode->pNext;
|
||||
MemFree( pOnRoofNode );
|
||||
pOnRoofNode = pMapTile->pOnRoofHead;
|
||||
}
|
||||
|
||||
pTopmostNode = pMapTile->pTopmostHead;
|
||||
while ( pTopmostNode != NULL )
|
||||
{
|
||||
pMapTile->pTopmostHead = pTopmostNode->pNext;
|
||||
MemFree( pTopmostNode );
|
||||
pTopmostNode = pMapTile->pTopmostHead;
|
||||
}
|
||||
|
||||
pStructureNode = pMapTile->pStructureHead;
|
||||
while( pStructureNode )
|
||||
{
|
||||
pMapTile->pStructureHead = pStructureNode->pNext;
|
||||
if( pStructureNode->usStructureID > INVALID_STRUCTURE_ID )
|
||||
{ //Okay to delete the structure data -- otherwise, this would be
|
||||
//merc structure data that we DON'T want to delete, because the merc node
|
||||
//that hasn't been modified will still use this structure data!
|
||||
MemFree( pStructureNode );
|
||||
}
|
||||
pStructureNode = pMapTile->pStructureHead;
|
||||
}
|
||||
|
||||
// Free the map tile structure itself
|
||||
MemFree( pMapTile );
|
||||
|
||||
// Free the undo struct
|
||||
MemFree( pData );
|
||||
|
||||
return( TRUE );
|
||||
}
|
||||
|
||||
|
||||
void CropStackToMaxLength( INT32 iMaxCmds )
|
||||
{
|
||||
INT32 iCmdCount;
|
||||
undo_stack *pCurrent;
|
||||
|
||||
iCmdCount = 0;
|
||||
pCurrent = gpTileUndoStack;
|
||||
|
||||
// If stack is empty, leave
|
||||
if ( pCurrent == NULL )
|
||||
return;
|
||||
|
||||
while ( (iCmdCount <= (iMaxCmds - 1)) && (pCurrent != NULL) )
|
||||
{
|
||||
if ( pCurrent->iCmdCount == 1 )
|
||||
iCmdCount++;
|
||||
pCurrent = pCurrent->pNext;
|
||||
}
|
||||
|
||||
// If the max number of commands was reached, and there is something
|
||||
// to crop, from the rest of the stack, remove it.
|
||||
if ( (iCmdCount >= iMaxCmds) && pCurrent != NULL )
|
||||
{
|
||||
while ( pCurrent->pNext != NULL )
|
||||
pCurrent->pNext = DeleteThisStackNode( pCurrent->pNext );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//We are adding a light to the undo list. We won't save the mapelement, nor will
|
||||
//we validate the gridno in the binary tree. This works differently than a mapelement,
|
||||
//because lights work on a different system. By setting the fLightSaved flag to TRUE,
|
||||
//this will handle the way the undo command is handled. If there is no lightradius in
|
||||
//our saved light, then we intend on erasing the light upon undo execution, otherwise, we
|
||||
//save the light radius and light ID, so that we place it during undo execution.
|
||||
void AddLightToUndoList( INT32 iMapIndex, INT32 iLightRadius, UINT8 ubLightID )
|
||||
{
|
||||
undo_stack *pNode;
|
||||
undo_struct *pUndoInfo;
|
||||
|
||||
if( !gfUndoEnabled )
|
||||
return;
|
||||
//When executing an undo command (by adding a light or removing one), that command
|
||||
//actually tries to add it to the undo list. So we wrap the execution of the undo
|
||||
//command by temporarily setting this flag, so it'll ignore, and not place a new undo
|
||||
//command. When finished, the flag is cleared, and lights are again allowed to be saved
|
||||
//in the undo list.
|
||||
if( gfIgnoreUndoCmdsForLights )
|
||||
return;
|
||||
|
||||
pNode = (undo_stack*)MemAlloc( sizeof( undo_stack ) );
|
||||
if( !pNode )
|
||||
return;
|
||||
|
||||
pUndoInfo = (undo_struct *)MemAlloc( sizeof( undo_struct ) );
|
||||
if( !pUndoInfo )
|
||||
{
|
||||
MemFree( pNode );
|
||||
return;
|
||||
}
|
||||
|
||||
pUndoInfo->fLightSaved = TRUE;
|
||||
//if ubLightRadius is 0, then we don't need to save the light information because we
|
||||
//will erase it when it comes time to execute the undo command.
|
||||
pUndoInfo->ubLightRadius = (UINT8)iLightRadius;
|
||||
pUndoInfo->ubLightID = ubLightID;
|
||||
pUndoInfo->iMapIndex = iMapIndex;
|
||||
pUndoInfo->pMapTile = NULL;
|
||||
|
||||
//Add to undo stack
|
||||
pNode->iCmdCount = 1;
|
||||
pNode->pData = pUndoInfo;
|
||||
pNode->pNext = gpTileUndoStack;
|
||||
gpTileUndoStack = pNode;
|
||||
|
||||
CropStackToMaxLength( MAX_UNDO_COMMAND_LENGTH );
|
||||
}
|
||||
|
||||
BOOLEAN AddToUndoList( INT32 iMapIndex )
|
||||
{
|
||||
static INT32 iCount = 1;
|
||||
|
||||
if( !gfUndoEnabled )
|
||||
return FALSE;
|
||||
if ( fNewUndoCmd )
|
||||
{
|
||||
iCount = 0;
|
||||
fNewUndoCmd = FALSE;
|
||||
}
|
||||
|
||||
//Check to see if the tile in question is even on the visible map, then
|
||||
//if that is true, then check to make sure we don't already have the mapindex
|
||||
//saved in the new binary tree (which only holds unique mapindex values).
|
||||
if( GridNoOnVisibleWorldTile( (INT16)iMapIndex ) && AddMapIndexToTree( (UINT16)iMapIndex ) )
|
||||
|
||||
{
|
||||
if( AddToUndoListCmd( iMapIndex, ++iCount ) )
|
||||
return TRUE;
|
||||
iCount--;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
||||
BOOLEAN AddToUndoListCmd( INT32 iMapIndex, INT32 iCmdCount )
|
||||
{
|
||||
undo_stack *pNode;
|
||||
undo_struct *pUndoInfo;
|
||||
MAP_ELEMENT *pData;
|
||||
STRUCTURE *pStructure;
|
||||
INT32 iCoveredMapIndex;
|
||||
UINT8 ubLoop;
|
||||
|
||||
if ( (pNode = (undo_stack *)MemAlloc(sizeof( undo_stack )) ) == NULL )
|
||||
{
|
||||
return( FALSE );
|
||||
}
|
||||
|
||||
if ( (pUndoInfo = (undo_struct *)MemAlloc(sizeof( undo_struct )) ) == NULL )
|
||||
{
|
||||
MemFree( pNode );
|
||||
return( FALSE );
|
||||
}
|
||||
|
||||
if ( (pData = (MAP_ELEMENT *)MemAlloc(sizeof( MAP_ELEMENT )) ) == NULL )
|
||||
{
|
||||
MemFree( pNode );
|
||||
MemFree( pUndoInfo );
|
||||
return( FALSE );
|
||||
}
|
||||
|
||||
// Init map element struct
|
||||
pData->pLandHead = pData->pLandStart = NULL;
|
||||
pData->pObjectHead = NULL;
|
||||
pData->pStructHead = NULL;
|
||||
pData->pShadowHead = NULL;
|
||||
pData->pMercHead = NULL;
|
||||
pData->pRoofHead = NULL;
|
||||
pData->pOnRoofHead = NULL;
|
||||
pData->pTopmostHead = NULL;
|
||||
pData->pStructureHead = pData->pStructureTail = NULL;
|
||||
pData->sHeight = 0;
|
||||
|
||||
|
||||
// Copy the world map's tile
|
||||
if ( CopyMapElementFromWorld( pData, iMapIndex ) == FALSE )
|
||||
{
|
||||
MemFree( pNode );
|
||||
MemFree( pUndoInfo );
|
||||
MemFree( pData );
|
||||
return( FALSE );
|
||||
}
|
||||
|
||||
// copy the room number information (it's not in the mapelement structure)
|
||||
pUndoInfo->ubRoomNum = gubWorldRoomInfo[ iMapIndex ];
|
||||
|
||||
pUndoInfo->fLightSaved = FALSE;
|
||||
pUndoInfo->ubLightRadius = 0;
|
||||
pUndoInfo->ubLightID = 0;
|
||||
pUndoInfo->pMapTile = pData;
|
||||
pUndoInfo->iMapIndex = iMapIndex;
|
||||
|
||||
pNode->pData = pUndoInfo;
|
||||
pNode->iCmdCount = iCmdCount;
|
||||
pNode->pNext = gpTileUndoStack;
|
||||
gpTileUndoStack = pNode;
|
||||
|
||||
// loop through pData->pStructureHead list
|
||||
// for each structure
|
||||
// find the base tile
|
||||
// reference the db structure
|
||||
// if number of tiles > 1
|
||||
// add all covered tiles to undo list
|
||||
pStructure = pData->pStructureHead;
|
||||
while (pStructure)
|
||||
{
|
||||
for (ubLoop = 1; ubLoop < pStructure->pDBStructureRef->pDBStructure->ubNumberOfTiles; ubLoop++)
|
||||
{
|
||||
// this loop won't execute for single-tile structures; for multi-tile structures, we have to
|
||||
// add to the undo list all the other tiles covered by the structure
|
||||
iCoveredMapIndex = pStructure->sBaseGridNo + pStructure->pDBStructureRef->ppTile[ubLoop]->sPosRelToBase;
|
||||
AddToUndoList( iCoveredMapIndex );
|
||||
}
|
||||
pStructure = pStructure->pNext;
|
||||
}
|
||||
|
||||
CropStackToMaxLength( MAX_UNDO_COMMAND_LENGTH );
|
||||
|
||||
return( TRUE );
|
||||
}
|
||||
|
||||
|
||||
void CheckMapIndexForMultiTileStructures( UINT16 usMapIndex )
|
||||
{
|
||||
STRUCTURE * pStructure;
|
||||
UINT8 ubLoop;
|
||||
INT32 iCoveredMapIndex;
|
||||
|
||||
pStructure = gpWorldLevelData[usMapIndex].pStructureHead;
|
||||
while (pStructure)
|
||||
{
|
||||
if (pStructure->pDBStructureRef->pDBStructure->ubNumberOfTiles > 1)
|
||||
{
|
||||
for (ubLoop = 0; ubLoop < pStructure->pDBStructureRef->pDBStructure->ubNumberOfTiles; ubLoop++)
|
||||
{
|
||||
// for multi-tile structures we have to add, to the undo list, all the other tiles covered by the structure
|
||||
if (pStructure->fFlags & STRUCTURE_BASE_TILE)
|
||||
{
|
||||
iCoveredMapIndex = usMapIndex + pStructure->pDBStructureRef->ppTile[ubLoop]->sPosRelToBase;
|
||||
}
|
||||
else
|
||||
{
|
||||
iCoveredMapIndex = pStructure->sBaseGridNo + pStructure->pDBStructureRef->ppTile[ubLoop]->sPosRelToBase;
|
||||
}
|
||||
AddToUndoList( iCoveredMapIndex );
|
||||
}
|
||||
}
|
||||
pStructure = pStructure->pNext;
|
||||
}
|
||||
}
|
||||
|
||||
void CheckForMultiTilesInTreeAndAddToUndoList( MapIndexBinaryTree *node )
|
||||
{
|
||||
CheckMapIndexForMultiTileStructures( node->usMapIndex );
|
||||
if( node->left )
|
||||
CheckForMultiTilesInTreeAndAddToUndoList( node->left );
|
||||
if( node->right )
|
||||
CheckForMultiTilesInTreeAndAddToUndoList( node->right );
|
||||
}
|
||||
|
||||
BOOLEAN RemoveAllFromUndoList( void )
|
||||
{
|
||||
ClearUndoMapIndexTree();
|
||||
|
||||
while ( gpTileUndoStack != NULL )
|
||||
DeleteTopStackNode();
|
||||
|
||||
return( TRUE );
|
||||
}
|
||||
|
||||
|
||||
BOOLEAN ExecuteUndoList( void )
|
||||
{
|
||||
INT32 iCmdCount, iCurCount;
|
||||
INT32 iUndoMapIndex;
|
||||
BOOLEAN fExitGrid;
|
||||
|
||||
if( !gfUndoEnabled )
|
||||
return FALSE;
|
||||
|
||||
// Is there something on the undo stack?
|
||||
if ( gpTileUndoStack == NULL )
|
||||
return( TRUE );
|
||||
|
||||
// Get number of stack entries for this command (top node will tell this)
|
||||
iCmdCount = gpTileUndoStack->iCmdCount;
|
||||
|
||||
// Execute each stack node in command, and remove each from stack.
|
||||
iCurCount = 0;
|
||||
while ( (iCurCount < iCmdCount) && (gpTileUndoStack != NULL) )
|
||||
{
|
||||
iUndoMapIndex = gpTileUndoStack->pData->iMapIndex;
|
||||
|
||||
// Find which map tile we are to "undo"
|
||||
if( gpTileUndoStack->pData->fLightSaved )
|
||||
{ //We saved a light, so delete that light
|
||||
INT16 sX, sY;
|
||||
//Turn on this flag so that the following code, when executed, doesn't attempt to
|
||||
//add lights to the undo list. That would cause problems...
|
||||
gfIgnoreUndoCmdsForLights = TRUE;
|
||||
ConvertGridNoToXY( (INT16)iUndoMapIndex, &sX, &sY );
|
||||
if( !gpTileUndoStack->pData->ubLightRadius )
|
||||
RemoveLight( sX, sY );
|
||||
else
|
||||
PlaceLight( gpTileUndoStack->pData->ubLightRadius, sX, sY, gpTileUndoStack->pData->ubLightID );
|
||||
//Turn off the flag so lights can again be added to the undo list.
|
||||
gfIgnoreUndoCmdsForLights = FALSE;
|
||||
}
|
||||
else
|
||||
{ // We execute the undo command node by simply swapping the contents
|
||||
// of the undo's MAP_ELEMENT with the world's element.
|
||||
fExitGrid = ExitGridAtGridNo( (UINT16)iUndoMapIndex );
|
||||
SwapMapElementWithWorld( iUndoMapIndex, gpTileUndoStack->pData->pMapTile );
|
||||
|
||||
// copy the room number information back
|
||||
gubWorldRoomInfo[ iUndoMapIndex ] = gpTileUndoStack->pData->ubRoomNum;
|
||||
|
||||
// Now we smooth out the changes...
|
||||
//SmoothUndoMapTileTerrain( iUndoMapIndex, gpTileUndoStack->pData->pMapTile );
|
||||
SmoothAllTerrainTypeRadius( iUndoMapIndex, 1, TRUE );
|
||||
}
|
||||
|
||||
// ...trash the top element of the stack...
|
||||
DeleteTopStackNode( );
|
||||
|
||||
// ...and bump the command counter up by 1
|
||||
iCurCount++;
|
||||
|
||||
//Kris:
|
||||
//The new cursor system is somehow interfering with the undo stuff. When
|
||||
//an undo is called, the item is erased, but a cursor is added! I'm quickly
|
||||
//hacking around this by erasing all cursors here.
|
||||
RemoveAllTopmostsOfTypeRange( iUndoMapIndex, FIRSTPOINTERS, FIRSTPOINTERS );
|
||||
|
||||
if( fExitGrid && !ExitGridAtGridNo( (UINT16)iUndoMapIndex ) )
|
||||
{ //An exitgrid has been removed, so get rid of the associated indicator.
|
||||
RemoveTopmost( (UINT16)iUndoMapIndex, FIRSTPOINTERS8 );
|
||||
}
|
||||
else if( !fExitGrid && ExitGridAtGridNo( (UINT16)iUndoMapIndex ) )
|
||||
{ //An exitgrid has been added, so add the associated indicator
|
||||
AddTopmostToTail( (UINT16)iUndoMapIndex, FIRSTPOINTERS8 );
|
||||
}
|
||||
}
|
||||
|
||||
return( TRUE );
|
||||
}
|
||||
|
||||
|
||||
void SmoothUndoMapTileTerrain( INT32 iWorldTile, MAP_ELEMENT *pUndoTile )
|
||||
{
|
||||
LEVELNODE *pWorldLand;
|
||||
LEVELNODE *pUndoLand;
|
||||
LEVELNODE *pLand;
|
||||
LEVELNODE *pWLand;
|
||||
UINT32 uiCheckType, uiWCheckType;
|
||||
BOOLEAN fFound;
|
||||
|
||||
pUndoLand = pUndoTile->pLandHead;
|
||||
pWorldLand = gpWorldLevelData[ iWorldTile ].pLandHead;
|
||||
|
||||
if ( pUndoLand == NULL )
|
||||
{
|
||||
// nothing in the old tile, so smooth the entire land in world's tile
|
||||
pLand = gpWorldLevelData[ iWorldTile ].pLandHead;
|
||||
while( pLand != NULL )
|
||||
{
|
||||
GetTileType( pLand->usIndex, &uiCheckType );
|
||||
SmoothTerrainRadius( iWorldTile, uiCheckType, 1, TRUE );
|
||||
pLand = pLand->pNext;
|
||||
}
|
||||
}
|
||||
else if ( gpWorldLevelData[ iWorldTile ].pLandHead == NULL )
|
||||
{
|
||||
// Nothing in world's tile, so smooth out the land in the old tile.
|
||||
pLand = pUndoLand;
|
||||
while( pLand != NULL )
|
||||
{
|
||||
GetTileType( pLand->usIndex, &uiCheckType );
|
||||
SmoothTerrainRadius( iWorldTile, uiCheckType, 1, TRUE );
|
||||
pLand = pLand->pNext;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
pLand = pUndoLand;
|
||||
while( pLand != NULL )
|
||||
{
|
||||
GetTileType( pLand->usIndex, &uiCheckType);
|
||||
|
||||
fFound = FALSE;
|
||||
pWLand = pWorldLand;
|
||||
while( pWLand != NULL && !fFound )
|
||||
{
|
||||
GetTileType( pWLand->usIndex, &uiWCheckType);
|
||||
|
||||
if ( uiCheckType == uiWCheckType )
|
||||
fFound = TRUE;
|
||||
|
||||
pWLand = pWLand->pNext;
|
||||
}
|
||||
|
||||
if ( !fFound )
|
||||
SmoothTerrainRadius( iWorldTile, uiCheckType, 1, TRUE );
|
||||
|
||||
pLand = pLand->pNext;
|
||||
}
|
||||
|
||||
pWLand = pWorldLand;
|
||||
while( pWLand != NULL )
|
||||
{
|
||||
GetTileType( pWLand->usIndex, &uiWCheckType);
|
||||
|
||||
fFound = FALSE;
|
||||
pLand = pUndoLand;
|
||||
while( pLand != NULL && !fFound )
|
||||
{
|
||||
GetTileType( pLand->usIndex, &uiCheckType);
|
||||
|
||||
if ( uiCheckType == uiWCheckType )
|
||||
fFound = TRUE;
|
||||
|
||||
pLand = pLand->pNext;
|
||||
}
|
||||
|
||||
if ( !fFound )
|
||||
SmoothTerrainRadius( iWorldTile, uiWCheckType, 1, TRUE );
|
||||
|
||||
pWLand = pWLand->pNext;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Because of the potentially huge amounts of memory that can be allocated due to the inefficient
|
||||
//undo methods coded by Bret, it is feasible that it could fail. Instead of using assertions to
|
||||
//terminate the program, destroy the memory allocated thusfar.
|
||||
void DeleteMapElementContentsAfterCreationFail( MAP_ELEMENT *pNewMapElement )
|
||||
{
|
||||
LEVELNODE *pLevelNode;
|
||||
STRUCTURE *pStructure;
|
||||
INT32 x;
|
||||
for( x = 0; x < 9; x++ )
|
||||
{
|
||||
if( x == 1 )
|
||||
continue;
|
||||
pLevelNode = pNewMapElement->pLevelNodes[ x ];
|
||||
while( pLevelNode )
|
||||
{
|
||||
LEVELNODE *temp;
|
||||
temp = pLevelNode;
|
||||
pLevelNode = pLevelNode->pNext;
|
||||
MemFree( temp );
|
||||
}
|
||||
}
|
||||
pStructure = pNewMapElement->pStructureHead;
|
||||
while( pStructure )
|
||||
{
|
||||
STRUCTURE *temp;
|
||||
temp = pStructure;
|
||||
pStructure = pStructure->pNext;
|
||||
MemFree( temp );
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
union
|
||||
{
|
||||
struct TAG_level_node *pPrevNode; // FOR LAND, GOING BACKWARDS POINTER
|
||||
ITEM_POOL *pItemPool; // ITEM POOLS
|
||||
STRUCTURE *pStructureData; // STRUCTURE DATA
|
||||
INT32 iPhysicsObjectID; // ID FOR PHYSICS ITEM
|
||||
INT32 uiAPCost; // FOR AP DISPLAY
|
||||
}; // ( 4 byte union )
|
||||
union
|
||||
{
|
||||
struct
|
||||
{
|
||||
UINT16 usIndex; // TILE DATABASE INDEX
|
||||
INT16 sCurrentFrame; // Stuff for animated tiles for a given tile location ( doors, etc )
|
||||
};
|
||||
struct
|
||||
{
|
||||
SOLDIERTYPE *pSoldier; // POINTER TO SOLDIER
|
||||
};
|
||||
}; // ( 4 byte union )
|
||||
*/
|
||||
BOOLEAN CopyMapElementFromWorld( MAP_ELEMENT *pNewMapElement, INT32 iMapIndex )
|
||||
{
|
||||
MAP_ELEMENT *pOldMapElement;
|
||||
LEVELNODE *pOldLevelNode;
|
||||
LEVELNODE *pLevelNode;
|
||||
LEVELNODE *pNewLevelNode;
|
||||
LEVELNODE *tail;
|
||||
INT32 x;
|
||||
|
||||
STRUCTURE *pOldStructure;
|
||||
|
||||
//Get a pointer to the current map index
|
||||
pOldMapElement = &gpWorldLevelData[ iMapIndex ];
|
||||
|
||||
//Save the structure information from the mapelement
|
||||
pOldStructure = pOldMapElement->pStructureHead;
|
||||
if( pOldStructure )
|
||||
{
|
||||
STRUCTURE *pNewStructure;
|
||||
STRUCTURE *pStructure;
|
||||
STRUCTURE *tail;
|
||||
tail = NULL;
|
||||
pNewStructure = NULL;
|
||||
while( pOldStructure )
|
||||
{
|
||||
pStructure = (STRUCTURE*)MemAlloc( sizeof( STRUCTURE ) );
|
||||
if( !pStructure )
|
||||
{
|
||||
DeleteMapElementContentsAfterCreationFail( pNewMapElement );
|
||||
return FALSE;
|
||||
}
|
||||
if( !tail )
|
||||
{ //first node in structure list
|
||||
tail = pStructure;
|
||||
*tail = *pOldStructure;
|
||||
tail->pPrev = NULL;
|
||||
tail->pNext = NULL;
|
||||
}
|
||||
else
|
||||
{ //add to the end of the levelnode list
|
||||
tail->pNext = pStructure;
|
||||
*pStructure = *pOldStructure;
|
||||
pStructure->pPrev = tail;
|
||||
pStructure->pNext = NULL;
|
||||
tail = tail->pNext;
|
||||
}
|
||||
//place the new node inside of the new map element
|
||||
if( !pNewStructure )
|
||||
{
|
||||
pNewMapElement->pStructureHead = pStructure;
|
||||
pNewStructure = pStructure;
|
||||
}
|
||||
else
|
||||
{
|
||||
pNewStructure->pNext = pStructure;
|
||||
pNewStructure = pNewStructure->pNext;
|
||||
}
|
||||
pOldStructure = pOldStructure->pNext;
|
||||
}
|
||||
if( tail )
|
||||
{
|
||||
pNewMapElement->pStructureTail = tail;
|
||||
}
|
||||
}
|
||||
|
||||
//For each of the 9 levelnodes, save each one
|
||||
//except for levelnode[1] which is a pointer to the first land to render.
|
||||
for( x = 0; x < 9; x++ )
|
||||
{
|
||||
if( x == 1 || x == 5 ) //skip the pLandStart and pMercLevel LEVELNODES
|
||||
continue;
|
||||
tail = NULL;
|
||||
pOldLevelNode = pOldMapElement->pLevelNodes[ x ];
|
||||
pNewLevelNode = NULL;
|
||||
while( pOldLevelNode )
|
||||
{
|
||||
//copy the level node
|
||||
pLevelNode = ( LEVELNODE* )MemAlloc( sizeof( LEVELNODE ) );
|
||||
if( !pLevelNode )
|
||||
{
|
||||
DeleteMapElementContentsAfterCreationFail( pNewMapElement );
|
||||
return FALSE;
|
||||
}
|
||||
if( !tail )
|
||||
{ //first node in levelnode list
|
||||
tail = pLevelNode;
|
||||
*tail = *pOldLevelNode;
|
||||
if( !x ) //land layer only
|
||||
tail->pPrevNode = NULL;
|
||||
tail->pNext = NULL;
|
||||
}
|
||||
else
|
||||
{ //add to the end of the levelnode list
|
||||
tail->pNext = pLevelNode;
|
||||
*pLevelNode = *pOldLevelNode;
|
||||
if( !x ) //land layer only
|
||||
pLevelNode->pPrevNode = tail;
|
||||
pLevelNode->pNext = NULL;
|
||||
tail = tail->pNext;
|
||||
}
|
||||
//place the new node inside of the new map element
|
||||
if( !pNewLevelNode )
|
||||
{
|
||||
pNewMapElement->pLevelNodes[ x ] = pLevelNode;
|
||||
pNewLevelNode = pLevelNode;
|
||||
}
|
||||
else
|
||||
{
|
||||
pNewLevelNode->pNext = pLevelNode;
|
||||
pNewLevelNode = pNewLevelNode->pNext;
|
||||
}
|
||||
//Handle levelnode layer specific stuff
|
||||
switch( x )
|
||||
{
|
||||
case 0: //LAND LAYER
|
||||
if( pOldLevelNode == pOldMapElement->pLandStart )
|
||||
{ //set the new landstart to point to the new levelnode.
|
||||
pNewMapElement->pLandStart = pNewLevelNode;
|
||||
}
|
||||
break;
|
||||
case 2: //OBJECT LAYER
|
||||
if( pOldLevelNode->pItemPool )
|
||||
{ //save the item pool?
|
||||
//pNewLevelNode->pItemPool = (ITEM_POOL*)MemAlloc( sizeof( ITEM_POOL ) );
|
||||
}
|
||||
break;
|
||||
case 3: //STRUCT LAYER
|
||||
case 6: //ROOF LAYER
|
||||
case 7: //ON ROOF LAYER
|
||||
if( pOldLevelNode->pStructureData )
|
||||
{ //make sure the structuredata pointer points to the parallel structure
|
||||
STRUCTURE *pOld, *pNew;
|
||||
//both lists are exactly the same size and contain the same information,
|
||||
//but the addresses are different. We will traverse the old list until
|
||||
//we find the match, then
|
||||
pOld = pOldMapElement->pStructureHead;
|
||||
pNew = pNewMapElement->pStructureHead;
|
||||
while( pOld )
|
||||
{
|
||||
Assert( pNew );
|
||||
if( pOld == pOldLevelNode->pStructureData )
|
||||
{
|
||||
pNewLevelNode->pStructureData = pNew;
|
||||
break;
|
||||
}
|
||||
pOld = pOld->pNext;
|
||||
pNew = pNew->pNext;
|
||||
}
|
||||
//Kris:
|
||||
//If this assert should fail, that means there is something wrong with
|
||||
//the preservation of the structure data within the mapelement.
|
||||
if( pOld != pOldLevelNode->pStructureData )
|
||||
{
|
||||
//OUCH!!! THIS IS HAPPENING. DISABLED IT FOR LINDA'S SAKE
|
||||
Assert( 1 );
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
//Done, go to next node in this level
|
||||
pOldLevelNode = pOldLevelNode->pNext;
|
||||
}
|
||||
//Done, go to next level
|
||||
}
|
||||
|
||||
//Save the rest of the information in the mapelement.
|
||||
pNewMapElement->uiFlags = pOldMapElement->uiFlags;
|
||||
pNewMapElement->sSumRealLights[0] = pOldMapElement->sSumRealLights[0];
|
||||
pNewMapElement->sSumRealLights[1] = pOldMapElement->sSumRealLights[1];
|
||||
pNewMapElement->sHeight = pOldMapElement->sHeight;
|
||||
pNewMapElement->ubTerrainID = pOldMapElement->ubTerrainID;
|
||||
pNewMapElement->ubReservedSoldierID = pOldMapElement->ubReservedSoldierID;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
BOOLEAN SwapMapElementWithWorld( INT32 iMapIndex, MAP_ELEMENT *pUndoMapElement )
|
||||
{
|
||||
MAP_ELEMENT *pCurrentMapElement;
|
||||
MAP_ELEMENT TempMapElement;
|
||||
|
||||
pCurrentMapElement = &gpWorldLevelData[ iMapIndex ];
|
||||
|
||||
//Transfer the merc level node from the current world to the undo mapelement
|
||||
//that will replace it. We do this, because mercs aren't associated with
|
||||
//undo commands.
|
||||
pUndoMapElement->pMercHead = gpWorldLevelData[ iMapIndex ].pMercHead;
|
||||
gpWorldLevelData[ iMapIndex ].pMercHead = NULL;
|
||||
|
||||
//Swap the mapelements
|
||||
TempMapElement = *pCurrentMapElement;
|
||||
*pCurrentMapElement = *pUndoMapElement;
|
||||
*pUndoMapElement = TempMapElement;
|
||||
|
||||
return ( TRUE );
|
||||
}
|
||||
|
||||
void DetermineUndoState()
|
||||
{
|
||||
// Reset the undo command mode if we released the left button.
|
||||
if( !fNewUndoCmd )
|
||||
{
|
||||
if( !gfLeftButtonState && !gfCurrentSelectionWithRightButton ||
|
||||
!gfRightButtonState && gfCurrentSelectionWithRightButton )
|
||||
{
|
||||
//Clear the mapindex binary tree list, and set up flag for new undo command.
|
||||
fNewUndoCmd = TRUE;
|
||||
ClearUndoMapIndexTree();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user