Files
source/Editor/Smoothing Utils.cpp
T

470 lines
13 KiB
C++

// WANNE: EDITOR: no changes
#ifdef PRECOMPILEDHEADERS
#include "Editor All.h"
#else
#include "builddefines.h"
#endif
#ifdef JA2EDITOR
#ifndef PRECOMPILEDHEADERS
#include <stdlib.h>
#include "worlddef.h" //for LEVELNODE def
#include "worldman.h" //for RemoveXXXX()
#include "Isometric Utils.h" //for GridNoOnVisibleWorldTile()
#include "SmartMethod.h"
#include "Smoothing Utils.h"
#include "Editor Undo.h"
#include "EditorDefines.h"
#include "edit_sys.h"
#include "environment.h"
#endif
extern UINT16 PickAWallPiece( UINT16 usWallPieceType );
//This method isn't foolproof, but because erasing large areas of buildings could result in
//multiple wall types for each building. When processing the region, it is necessary to
//calculate the roof type by searching for the nearest roof tile.
UINT16 SearchForWallType( UINT32 iMapIndex )
{
UINT32 uiTileType;
LEVELNODE *pWall;
INT16 sOffset;
INT16 x, y, sRadius = 0;
if( gfBasement )
{
UINT16 usWallType;
usWallType = GetRandomIndexByRange( FIRSTWALL, LASTWALL );
if( usWallType == 0xffff )
usWallType = FIRSTWALL;
return usWallType;
}
while( sRadius < 32 )
{
//NOTE: start at the higher y value and go negative because it is possible to have another
// structure type one tile north, but not one tile south -- so it'll find the correct wall first.
for( y = sRadius; y >= -sRadius; y-- ) for( x = -sRadius; x <= sRadius; x++ )
{
if( abs(x) == abs(sRadius) || abs(y) == abs(sRadius) )
{
sOffset = y * WORLD_COLS + x;
if( !GridNoOnVisibleWorldTile( (INT16)(iMapIndex + sOffset) ) )
{
continue;
}
pWall = gpWorldLevelData[ iMapIndex + sOffset ].pStructHead;
while( pWall )
{
GetTileType( pWall->usIndex, &uiTileType );
if( uiTileType >= FIRSTWALL && uiTileType <= LASTWALL )
{ //found a roof, so return its type.
return (UINT16)uiTileType;
}
//if( uiTileType >= FIRSTWINDOW && uiTileType <= LASTWINDOW )
//{ //Window types can be converted to a wall type.
// return (UINT16)(FIRSTWALL + uiTileType - FIRSTWINDOW );
//}
pWall = pWall->pNext;
}
}
}
sRadius++;
}
return 0xffff;
}
//This method isn't foolproof, but because erasing large areas of buildings could result in
//multiple roof types for each building. When processing the region, it is necessary to
//calculate the roof type by searching for the nearest roof tile.
UINT16 SearchForRoofType( UINT32 iMapIndex )
{
UINT32 uiTileType;
LEVELNODE *pRoof;
INT16 x, y, sRadius = 0;
INT16 sOffset;
while( sRadius < 32 )
{
for( y = -sRadius; y <= sRadius; y++ ) for( x = -sRadius; x <= sRadius; x++ )
{
if( abs(x) == abs(sRadius) || abs(y) == abs(sRadius) )
{
sOffset = y * WORLD_COLS + x;
if( !GridNoOnVisibleWorldTile( (INT16)(iMapIndex + sOffset) ) )
{
continue;
}
pRoof = gpWorldLevelData[ iMapIndex + sOffset ].pRoofHead;
while( pRoof )
{
GetTileType( pRoof->usIndex, &uiTileType );
if( uiTileType >= FIRSTROOF && uiTileType <= LASTROOF )
{ //found a roof, so return its type.
return (UINT16)uiTileType;
}
pRoof = pRoof->pNext;
}
}
}
sRadius++;
}
return 0xffff;
}
BOOLEAN RoofAtGridNo( UINT32 iMapIndex )
{
LEVELNODE *pRoof;
UINT32 uiTileType;
pRoof = gpWorldLevelData[ iMapIndex ].pRoofHead;
// Look through all objects and Search for type
while( pRoof )
{
if ( pRoof->usIndex != NO_TILE )
{
GetTileType( pRoof->usIndex, &uiTileType );
if ( uiTileType >= FIRSTROOF && uiTileType <= SECONDSLANTROOF )
return TRUE;
pRoof = pRoof->pNext;
}
}
return FALSE;
}
BOOLEAN BuildingAtGridNo( UINT32 iMapIndex )
{
if( RoofAtGridNo( iMapIndex ) )
return TRUE;
if( FloorAtGridNo( iMapIndex ) )
return TRUE;
return FALSE;
}
BOOLEAN ValidDecalPlacement( UINT32 iMapIndex )
{
if( GetVerticalWall( iMapIndex ) || GetHorizontalWall( iMapIndex )
|| GetVerticalFence( iMapIndex ) || GetHorizontalFence( iMapIndex ) )
return TRUE;
return FALSE;
}
LEVELNODE* GetVerticalWall( UINT32 iMapIndex )
{
LEVELNODE *pStruct;
UINT32 uiTileType;
UINT16 usWallOrientation;
pStruct = gpWorldLevelData[ iMapIndex ].pStructHead;
while( pStruct )
{
if ( pStruct->usIndex != NO_TILE )
{
GetTileType( pStruct->usIndex, &uiTileType );
if ( uiTileType >= FIRSTWALL && uiTileType <= LASTWALL ||
uiTileType >= FIRSTDOOR && uiTileType <= LASTDOOR )
{
GetWallOrientation( pStruct->usIndex, &usWallOrientation );
if( usWallOrientation == INSIDE_TOP_RIGHT || usWallOrientation == OUTSIDE_TOP_RIGHT )
{
return pStruct;
}
}
}
pStruct = pStruct->pNext;
}
return NULL;
}
LEVELNODE* GetHorizontalWall( UINT32 iMapIndex )
{
LEVELNODE *pStruct;
UINT32 uiTileType;
UINT16 usWallOrientation;
pStruct = gpWorldLevelData[ iMapIndex ].pStructHead;
while( pStruct )
{
if ( pStruct->usIndex != NO_TILE )
{
GetTileType( pStruct->usIndex, &uiTileType );
if ( uiTileType >= FIRSTWALL && uiTileType <= LASTWALL ||
uiTileType >= FIRSTDOOR && uiTileType <= LASTDOOR )
{
GetWallOrientation( pStruct->usIndex, &usWallOrientation );
if( usWallOrientation == INSIDE_TOP_LEFT || usWallOrientation == OUTSIDE_TOP_LEFT )
{
return pStruct;
}
}
}
pStruct = pStruct->pNext;
}
return NULL;
}
UINT16 GetVerticalWallType( UINT32 iMapIndex )
{
LEVELNODE *pWall;
UINT32 uiTileType;
pWall = GetVerticalWall( iMapIndex );
if( pWall )
{
GetTileType( pWall->usIndex, &uiTileType );
if( uiTileType >= FIRSTDOOR && uiTileType <= LASTDOOR )
uiTileType = SearchForWallType( iMapIndex );
return (UINT16)uiTileType;
}
return 0;
}
UINT16 GetHorizontalWallType( UINT32 iMapIndex )
{
LEVELNODE *pWall;
UINT32 uiTileType;
pWall = GetHorizontalWall( iMapIndex );
if( pWall )
{
GetTileType( pWall->usIndex, &uiTileType );
if( uiTileType >= FIRSTDOOR && uiTileType <= LASTDOOR )
uiTileType = SearchForWallType( iMapIndex );
return (UINT16)uiTileType;
}
return 0;
}
LEVELNODE* GetVerticalFence( UINT32 iMapIndex )
{
LEVELNODE *pStruct;
UINT32 uiTileType;
UINT16 usWallOrientation;
pStruct = gpWorldLevelData[ iMapIndex ].pStructHead;
while( pStruct )
{
if ( pStruct->usIndex != NO_TILE )
{
GetTileType( pStruct->usIndex, &uiTileType );
if ( uiTileType == FENCESTRUCT )
{
GetWallOrientation( pStruct->usIndex, &usWallOrientation );
if( usWallOrientation == INSIDE_TOP_RIGHT || usWallOrientation == OUTSIDE_TOP_RIGHT )
{
return pStruct;
}
}
}
pStruct = pStruct->pNext;
}
return NULL;
}
LEVELNODE* GetHorizontalFence( UINT32 iMapIndex )
{
LEVELNODE *pStruct;
UINT32 uiTileType;
UINT16 usWallOrientation;
pStruct = gpWorldLevelData[ iMapIndex ].pStructHead;
while( pStruct )
{
if ( pStruct->usIndex != NO_TILE )
{
GetTileType( pStruct->usIndex, &uiTileType );
if ( uiTileType == FENCESTRUCT )
{
GetWallOrientation( pStruct->usIndex, &usWallOrientation );
if( usWallOrientation == INSIDE_TOP_LEFT || usWallOrientation == OUTSIDE_TOP_LEFT )
{
return pStruct;
}
}
}
pStruct = pStruct->pNext;
}
return NULL;
}
void EraseHorizontalWall( UINT32 iMapIndex )
{
LEVELNODE *pWall;
pWall = GetHorizontalWall( iMapIndex );
if( pWall )
{
AddToUndoList( iMapIndex );
RemoveStruct( iMapIndex, pWall->usIndex );
RemoveAllShadowsOfTypeRange( iMapIndex, FIRSTWALL, LASTWALL );
}
}
void EraseVerticalWall( UINT32 iMapIndex )
{
LEVELNODE *pWall;
pWall = GetVerticalWall( iMapIndex );
if( pWall )
{
AddToUndoList( iMapIndex );
RemoveStruct( iMapIndex, pWall->usIndex );
RemoveAllShadowsOfTypeRange( iMapIndex, FIRSTWALL, LASTWALL );
}
}
void ChangeHorizontalWall( UINT32 iMapIndex, UINT16 usNewPiece )
{
LEVELNODE *pWall;
UINT32 uiTileType;
UINT16 usTileIndex;
INT16 sIndex;
pWall = GetHorizontalWall( iMapIndex );
if( pWall )
{
GetTileType( pWall->usIndex, &uiTileType );
if( uiTileType >= FIRSTWALL && uiTileType <= LASTWALL )
{ //Okay, we have the wall, now change it's type.
sIndex = PickAWallPiece( usNewPiece );
AddToUndoList( iMapIndex );
GetTileIndexFromTypeSubIndex( uiTileType, sIndex, &usTileIndex );
ReplaceStructIndex( iMapIndex, pWall->usIndex, usTileIndex );
}
}
}
void ChangeVerticalWall( UINT32 iMapIndex, UINT16 usNewPiece )
{
LEVELNODE *pWall;
UINT32 uiTileType;
UINT16 usTileIndex;
INT16 sIndex;
pWall = GetVerticalWall( iMapIndex );
if( pWall )
{
GetTileType( pWall->usIndex, &uiTileType );
if( uiTileType >= FIRSTWALL && uiTileType <= LASTWALL )
{ //Okay, we have the wall, now change it's type.
sIndex = PickAWallPiece( usNewPiece );
AddToUndoList( iMapIndex );
GetTileIndexFromTypeSubIndex( uiTileType, sIndex, &usTileIndex );
ReplaceStructIndex( iMapIndex, pWall->usIndex, usTileIndex );
}
}
}
void RestoreWalls( UINT32 iMapIndex )
{
LEVELNODE *pWall = NULL;
UINT32 uiTileType;
UINT16 usWallType;
UINT16 usWallOrientation;
UINT8 ubSaveWallUIValue;
BOOLEAN fDone = FALSE;
pWall = GetHorizontalWall( iMapIndex );
if( pWall )
{
GetTileType( pWall->usIndex, &uiTileType );
usWallType = (UINT16)uiTileType;
if( uiTileType >= FIRSTDOOR && uiTileType <= LASTDOOR )
usWallType = SearchForWallType( iMapIndex );
GetWallOrientation( pWall->usIndex, &usWallOrientation );
AddToUndoList( iMapIndex );
RemoveStruct( iMapIndex, pWall->usIndex );
RemoveAllShadowsOfTypeRange( iMapIndex, FIRSTWALL, LASTWALL );
switch( usWallOrientation )
{
case OUTSIDE_TOP_LEFT:
BuildWallPiece( iMapIndex, INTERIOR_BOTTOM, usWallType );
break;
case INSIDE_TOP_LEFT:
BuildWallPiece( iMapIndex, EXTERIOR_BOTTOM, usWallType );
break;
}
fDone = TRUE;
}
pWall = GetVerticalWall( iMapIndex );
if( pWall )
{
GetTileType( pWall->usIndex, &uiTileType );
usWallType = (UINT16)uiTileType;
if( uiTileType >= FIRSTDOOR && uiTileType <= LASTDOOR )
usWallType = SearchForWallType( iMapIndex );
GetWallOrientation( pWall->usIndex, &usWallOrientation );
AddToUndoList( iMapIndex );
RemoveStruct( iMapIndex, pWall->usIndex );
RemoveAllShadowsOfTypeRange( iMapIndex, FIRSTWALL, LASTWALL );
switch( usWallOrientation )
{
case OUTSIDE_TOP_RIGHT:
BuildWallPiece( iMapIndex, INTERIOR_RIGHT, usWallType );
break;
case INSIDE_TOP_RIGHT:
BuildWallPiece( iMapIndex, EXTERIOR_RIGHT, usWallType );
break;
}
fDone = TRUE;
}
if( fDone )
{
return;
}
//we are in a special case here. The user is attempting to restore a wall, though nothing
//is here. We will hook into the smart wall method by tricking it into using the local wall
//type, but only if we have adjacent walls.
fDone = FALSE;
if( pWall = GetHorizontalWall( iMapIndex - 1 ) )
fDone = TRUE;
if( !fDone && (pWall = GetHorizontalWall( iMapIndex + 1 )) )
fDone = TRUE;
if( !fDone && (pWall = GetVerticalWall( iMapIndex - WORLD_COLS )) )
fDone = TRUE;
if( !fDone && (pWall = GetVerticalWall( iMapIndex + WORLD_COLS )) )
fDone = TRUE;
if( !fDone )
return;
//found a wall. Let's back up the current wall value, and restore it after pasting a smart wall.
if( pWall )
{
GetTileType( pWall->usIndex, &uiTileType );
usWallType = (UINT16)uiTileType;
if( uiTileType >= FIRSTDOOR && uiTileType <= LASTDOOR )
usWallType = SearchForWallType( iMapIndex );
if( usWallType != 0xffff )
{
ubSaveWallUIValue = gubWallUIValue; //save the wall UI value.
gubWallUIValue = (UINT8)usWallType; //trick the UI value
PasteSmartWall( iMapIndex ); //paste smart wall with fake UI value
gubWallUIValue = ubSaveWallUIValue; //restore the real UI value.
}
}
}
UINT16 GetWallClass( LEVELNODE *pWall )
{
UINT16 row, col, rowVariants;
UINT16 usWallIndex;
if( !pWall )
return 0xffff;
GetSubIndexFromTileIndex( pWall->usIndex, &usWallIndex );
for ( row = 0; row < NUM_WALL_TYPES; row++ )
{
rowVariants = gbWallTileLUT[row][0];
for( col = 1; col <= rowVariants; col++ )
{
if( usWallIndex == gbWallTileLUT[row][col] )
{
return row; //row is the wall class
}
}
}
return 0xffff;
}
UINT16 GetVerticalWallClass( UINT16 iMapIndex )
{
LEVELNODE *pWall;
if( pWall = GetVerticalWall( iMapIndex ) )
return GetWallClass( pWall );
return 0xffff;
}
UINT16 GetHorizontalWallClass( UINT16 iMapIndex )
{
LEVELNODE *pWall;
if( pWall = GetVerticalWall( iMapIndex ) )
return GetWallClass( pWall );
return 0xffff;
}
#endif