Files
source/Editor/newsmooth.cpp
T
00fb898600 Delete builddefines.h
Nothing was left in it but the include of profiler.h, and 132 translation units
were including it for that alone. Fifteen files were leaning on profiler.h to
drag in <set>, <vector> and <ostream> for them; those now include what they use.

This is the commit that moves line numbers. Removing an include line shifts
__LINE__ by one for everything below it, and __LINE__ is an immediate operand in
every Assert() and DebugMsg() call, so the four game executables differ from
their predecessors by roughly a thousand 32-bit constants each. Every one of
those is accounted for: each is a single immediate that moved by -1 where the
builddefines.h include went away, or +1 where a <set>/<vector> include was
added. Nothing else in .text, .rdata or .data moves, no object file's section
sizes change, and symbolize_crash and Ja2Export stay bit-identical.

The one non-immediate difference is that the 24 Editor translation units of the
non-editor apps stop emitting __Avx2WmemEnabledWeakValue, a 4-byte weak COMDAT
they only ever instantiated through profiler.h's <vector>. It is a UCRT weak
default that other translation units still provide.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-22 01:07:26 -03:00

1197 lines
42 KiB
C++

#ifdef JA2EDITOR
#include <stdlib.h>
#include "tiledef.h"
#include "worlddef.h"
#include "worldman.h"
#include "newsmooth.h"
#include "Isometric Utils.h"
#include "EditorBuildings.h"
#include "edit_sys.h"
#include "Smoothing Utils.h"
#include "Editor Undo.h"
#include "Structure Internals.h"
#include "environment.h"
#include "random.h"
#include "Render Fun.h"
BOOLEAN CaveAtGridNo( INT32 iMapIndex );
UINT16 GetCaveTileIndexFromPerimeterValue( UINT8 ubTotal );
UINT8 CalcNewCavePerimeterValue( INT32 iMapIndex );
BOOLEAN CaveAtGridNo( INT32 iMapIndex )
{
STRUCTURE *pStruct;
LEVELNODE* pLevel;
if( TileIsOutOfBounds(iMapIndex))
return FALSE;
pStruct = gpWorldLevelData[ iMapIndex ].pStructureHead;
while( pStruct )
{
if( pStruct->fFlags & STRUCTURE_CAVEWALL )
{
return TRUE;
}
pStruct = pStruct->pNext;
}
//may not have structure information, so check if there is a levelnode flag.
pLevel = gpWorldLevelData[ iMapIndex ].pStructHead;
while( pLevel )
{
if( pLevel->uiFlags & LEVELNODE_CAVE )
{
return TRUE;
}
pLevel = pLevel->pNext;
}
return FALSE;
}
UINT16 GetCaveTileIndexFromPerimeterValue( UINT8 ubTotal )
{
UINT16 usType = FIRSTWALL;
UINT16 usIndex;
UINT16 usTileIndex;
switch( ubTotal )
{
case 0x00: case 0x10: case 0x20: case 0x30: case 0x40: case 0x50: case 0x60: case 0x70:
case 0x80: case 0x90: case 0xa0: case 0xb0: case 0xc0: case 0xd0: case 0xe0: case 0xf0:
//usIndex = 0;
//break;
return 0xffff;
case 0x01: case 0x11: case 0x21: case 0x31: case 0x41: case 0x51: case 0x61: case 0x71:
case 0x81: case 0x91: case 0xa1: case 0xb1: case 0xc1: case 0xd1: case 0xe1: case 0xf1:
usType = SECONDWALL;
usIndex = 1 + (UINT16)Random( 4 );
break;
case 0x02: case 0x12: case 0x22: case 0x32: case 0x42: case 0x52: case 0x62: case 0x72:
case 0x82: case 0x92: case 0xa2: case 0xb2: case 0xc2: case 0xd2: case 0xe2: case 0xf2:
usType = SECONDWALL;
usIndex = 5 + (UINT16)Random( 4 );
break;
case 0x03: case 0x13: case 0x43: case 0x53: case 0x83: case 0x93: case 0xc3: case 0xd3:
usIndex = 1;
break;
case 0x04: case 0x14: case 0x24: case 0x34: case 0x44: case 0x54: case 0x64: case 0x74:
case 0x84: case 0x94: case 0xa4: case 0xb4: case 0xc4: case 0xd4: case 0xe4: case 0xf4:
usType = SECONDWALL;
usIndex = 9 + (UINT16)Random( 4 );
break;
case 0x05: case 0x15: case 0x25: case 0x35: case 0x45: case 0x55: case 0x65: case 0x75:
case 0x85: case 0x95: case 0xa5: case 0xb5: case 0xc5: case 0xd5: case 0xe5: case 0xf5:
usIndex = 2 + (UINT16)Random( 2 );
break;
case 0x06: case 0x16: case 0x26: case 0x36: case 0x86: case 0x96: case 0xa6: case 0xb6:
usIndex = 4;
break;
case 0x07: case 0x17: case 0x87: case 0x97:
usIndex = 5;
break;
case 0x08: case 0x18: case 0x28: case 0x38: case 0x48: case 0x58: case 0x68: case 0x78:
case 0x88: case 0x98: case 0xa8: case 0xb8: case 0xc8: case 0xd8: case 0xe8: case 0xf8:
usType = SECONDWALL;
usIndex = 13 + (UINT16)Random( 4 );
break;
case 0x09: case 0x29: case 0x49: case 0x69: case 0x89: case 0xa9: case 0xc9: case 0xe9:
usIndex = 6;
break;
case 0x0a: case 0x1a: case 0x2a: case 0x3a: case 0x4a: case 0x5a: case 0x6a: case 0x7a:
case 0x8a: case 0x9a: case 0xaa: case 0xba: case 0xca: case 0xda: case 0xea: case 0xfa:
usIndex = 7 + (UINT16)Random( 2 );
break;
case 0x0b: case 0x4b: case 0x8b: case 0xcb:
usIndex = 9;
break;
case 0x0c: case 0x1c: case 0x2c: case 0x3c: case 0x4c: case 0x5c: case 0x6c: case 0x7c:
usIndex = 10;
break;
case 0x0d: case 0x2d: case 0x4d: case 0x6d:
usIndex = 11;
break;
case 0x0e: case 0x1e: case 0x2e: case 0x3e:
usIndex = 12;
break;
case 0x0f:
usIndex = 13;
break;
case 0x19: case 0x39: case 0x59: case 0x79: case 0x99: case 0xb9: case 0xd9: case 0xf9:
usIndex = 14 + (UINT16)Random( 2 );
break;
case 0x1b: case 0x5b: case 0x9b: case 0xdb:
usIndex = 16;
break;
case 0x1d: case 0x3d: case 0x5d: case 0x7d:
usIndex = 17;
break;
case 0x1f:
usIndex = 18;
break;
case 0x23: case 0x33: case 0x63: case 0x73: case 0xa3: case 0xb3: case 0xe3: case 0xf3:
usIndex = 19 + (UINT16)Random( 2 );
break;
case 0x27: case 0x37: case 0xa7: case 0xb7:
usIndex = 21;
break;
case 0x2b: case 0x6b: case 0xab: case 0xeb:
usIndex = 22;
break;
case 0x2f:
usIndex = 23;
break;
case 0x3b: case 0x7b: case 0xbb: case 0xfb:
usIndex = 24 + (UINT16)Random( 3 );
break;
case 0x3f:
usIndex = 27;
break;
case 0x46: case 0x56: case 0x66: case 0x76: case 0xc6: case 0xd6: case 0xe6: case 0xf6:
usIndex = 28 + (UINT16)Random( 2 );
break;
case 0x47: case 0x57: case 0xc7: case 0xd7:
usIndex = 30;
break;
case 0x4e: case 0x5e: case 0x6e: case 0x7e:
usIndex = 31;
break;
case 0x4f:
usIndex = 32;
break;
case 0x5f:
usIndex = 33;
break;
case 0x67: case 0x77: case 0xe7: case 0xf7:
usIndex = 34 + (UINT16)Random( 3 );
break;
case 0x6f:
usIndex = 37;
break;
case 0x7f:
usIndex = 38 + (UINT16)Random( 2 );
break;
case 0x8c: case 0x9c: case 0xac: case 0xbc: case 0xcc: case 0xdc: case 0xec: case 0xfc:
usIndex = 40 + (UINT16)Random( 2 );
break;
case 0x8d: case 0xad: case 0xcd: case 0xed:
usIndex = 42;
break;
case 0x8e: case 0x9e: case 0xae: case 0xbe:
usIndex = 43;
break;
case 0x8f:
usIndex = 44;
break;
case 0x9d: case 0xbd: case 0xdd: case 0xfd:
usIndex = 45 + (UINT16)Random( 3 );
break;
case 0x9f:
usIndex = 48;
break;
case 0xaf:
usIndex = 49;
break;
case 0xbf:
usIndex = 50 + (UINT16)Random( 2 );
break;
case 0xce: case 0xde: case 0xee: case 0xfe:
usIndex = 52 + (UINT16)Random( 3 );
break;
case 0xcf:
usIndex = 55;
break;
case 0xdf:
usIndex = 56 + (UINT16)Random( 2 );
break;
case 0xef:
usIndex = 58 + (UINT16)Random( 2 );
break;
case 0xff:
usIndex = 60 + (UINT16)Random( 6 );
break;
default:
AssertMsg(0, "Invalid total given");
return 0xffff;
}
GetTileIndexFromTypeSubIndex( usType, usIndex, &usTileIndex );
return usTileIndex;
}
// 16 | 1 | 32
// ---+---+---
// 8 | | 2
// ---+---+---
// 128| 4 | 64
//These values are combined in any possible order ranging in
//values from 0 - 255. If there is a cave existing in any of
//these bordering gridnos, then the corresponding number is added
//to this total. The lookup table has been precalculated to know
//which piece to use for all of these combinations. In many cases,
//up to 16 combinations can share the same graphic image, as corners
//may not effect the look of the piece.
UINT8 CalcNewCavePerimeterValue( INT32 iMapIndex )
{
UINT8 ubTotal = 0;
if( CaveAtGridNo( iMapIndex - WORLD_COLS ) )
ubTotal += 0x01; //north
if( CaveAtGridNo( iMapIndex + 1 ) )
ubTotal += 0x02; //east
if( CaveAtGridNo( iMapIndex + WORLD_COLS ) )
ubTotal += 0x04; //south
if( CaveAtGridNo( iMapIndex - 1 ) )
ubTotal += 0x08; //west
if( CaveAtGridNo( iMapIndex - WORLD_COLS - 1 ) )
ubTotal += 0x10; //north west
if( CaveAtGridNo( iMapIndex - WORLD_COLS + 1 ) )
ubTotal += 0x20; //north east
if( CaveAtGridNo( iMapIndex + WORLD_COLS + 1 ) )
ubTotal += 0x40; //south east
if( CaveAtGridNo( iMapIndex + WORLD_COLS - 1 ) )
ubTotal += 0x80; //south west
return ubTotal;
}
void AddCave( INT32 iMapIndex, UINT16 usIndex )
{
LEVELNODE *pStruct;
if(TileIsOutOfBounds(iMapIndex))
return;
//First toast any existing wall (caves)
RemoveAllStructsOfTypeRange( iMapIndex, FIRSTWALL, LASTWALL );
//Now, add this piece
if( !AddWallToStructLayer( iMapIndex, usIndex, TRUE ) )
return;
//Set the cave flag
pStruct = gpWorldLevelData[ iMapIndex ].pStructHead;
Assert( pStruct );
pStruct->uiFlags |= LEVELNODE_CAVE;
}
//These walls have shadows associated with them, and are draw when the wall is drawn.
#define EXTERIOR_L_SHADOW_INDEX 30
#define INTERIOR_BOTTOMEND_SHADOW_INDEX 31
//Wall Look Up Table containing variants and indices with each row being a different walltype.
INT8 gbWallTileLUT[NUM_WALL_TYPES][7] =
{
// The number of variants of this tile type.
// | The first relative index of the wall type (FIRSTWALL, SECONDWALL, etc. ) walltype + 10
// | | The 2nd relative index ( walltype + 11 )
// | | | 3rd 4th 5th 6th
// | | | | | | |
6, 10, 11, 12, 27, 28, 29, //INTERIOR_L
6, 7, 8, 9, 24, 25, 26, //INTERIOR_R
6, 4, 5, 6, 21, 22, 23, //EXTERIOR_L
6, 1, 2, 3, 18, 19, 20, //EXTERIOR_R
1, 14, 0, 0, 0, 0, 0, //INTERIOR_CORNER
1, 15, 0, 0, 0, 0, 0, //INTERIOR_BOTTOMEND
1, 13, 0, 0, 0, 0, 0, //EXTERIOR_BOTTOMEND
1, 16, 0, 0, 0, 0, 0, //INTERIOR_EXTENDED
1, 57, 0, 0, 0, 0, 0, //EXTERIOR_EXTENDED
1, 56, 0, 0, 0, 0, 0, //INTERIOR_EXTENDED_BOTTOMEND
1, 17, 0, 0, 0, 0, 0, //EXTERIOR_EXTENDED_BOTTOMEND
};
//Roof table -- such a small table, using definitions instead.
#define TOP_ROOF_INDEX 2
#define BOTTOM_ROOF_INDEX 4
#define LEFT_ROOF_INDEX 1
#define RIGHT_ROOF_INDEX 5
#define TOPLEFT_ROOF_INDEX 3
#define TOPRIGHT_ROOF_INDEX 7
#define BOTTOMLEFT_ROOF_INDEX 8
#define BOTTOMRIGHT_ROOF_INDEX 6
#define CENTER_ROOF_BASE_INDEX 9
#define CENTER_ROOF_VARIANTS 3
//slant roof table
#define THIN_BOTTOM 1
#define THIN_TOP 2
#define THIN_LEFT 5
#define THIN_RIGHT 6
#define THICK_BOTTOM 3
#define THICK_TOP 4
#define THICK_LEFT 7
#define THICK_RIGHT 8
#define VWALL_LEFT 32
#define VWALL_RIGHT 33
#define HWALL_LEFT 35
#define HWALL_RIGHT 34
#define FLOOR_VARIANTS 8
//PRIVATELY "ENCAPSULATED" FUNCTIONS
//These construction functions do all the smoothing.
//NOTE: passing null for wall/roof type will force the function to search for the nearest
// existing respective type.
void BuildSlantRoof( INT32 iLeft, INT32 iTop, INT32 iRight, INT32 iBottom, UINT16 usWallType, UINT16 usRoofType, BOOLEAN fVertical );
void BulldozeNature( INT32 iMapIndex );
void EraseRoof( INT32 iMapIndex );
void EraseFloor( INT32 iMapIndex );
void EraseBuilding( INT32 iMapIndex );
void EraseFloorOwnedBuildingPieces( INT32 iMapIndex );
void ConsiderEffectsOfNewWallPiece( INT32 iMapIndex, UINT8 usWallOrientation );
//----------------------------------------------------------------------------------------------------
//BEGIN IMPLEMENTATION OF PRIVATE FUNCTIONS
//----------------------------------------------------------------------------------------------------
void BuildSlantRoof( INT32 iLeft, INT32 iTop, INT32 iRight, INT32 iBottom, UINT16 usWallType, UINT16 usRoofType, BOOLEAN fVertical )
{
INT32 i;
UINT16 usTileIndex;
INT32 iMapIndex;
if( fVertical )
{
iMapIndex = iBottom * WORLD_COLS + iLeft;
//This happens to be the only mapindex that needs to be backed up. The rest have already been
//done because of the building code before this.
AddToUndoList( iMapIndex + 8 );
//Add the closest viewable pieces. There are two aframe walls pieces, and extended aframe roof pieces.
GetTileIndexFromTypeSubIndex( usWallType, VWALL_LEFT, &usTileIndex );
AddRoofToHead( iMapIndex + 4, usTileIndex );
GetTileIndexFromTypeSubIndex( usWallType, VWALL_RIGHT, &usTileIndex );
AddRoofToHead( iMapIndex + 8, usTileIndex );
GetTileIndexFromTypeSubIndex( usRoofType, THICK_LEFT, &usTileIndex );
AddRoofToHead( iMapIndex + 3, usTileIndex );
GetTileIndexFromTypeSubIndex( usRoofType, THICK_RIGHT, &usTileIndex );
AddRoofToHead( iMapIndex + 7, usTileIndex );
for( i = iBottom - 1; i > iTop; i-- )
{
iMapIndex -= WORLD_COLS;
GetTileIndexFromTypeSubIndex( usRoofType, THIN_LEFT, &usTileIndex );
AddRoofToHead( iMapIndex + 3, usTileIndex );
GetTileIndexFromTypeSubIndex( usRoofType, THIN_RIGHT, &usTileIndex );
AddRoofToHead( iMapIndex + 7, usTileIndex );
}
iMapIndex -= WORLD_COLS;
GetTileIndexFromTypeSubIndex( usRoofType, THICK_LEFT, &usTileIndex );
AddRoofToHead( iMapIndex + 3, usTileIndex );
GetTileIndexFromTypeSubIndex( usRoofType, THICK_RIGHT, &usTileIndex );
AddRoofToHead( iMapIndex + 7, usTileIndex );
}
else
{
iMapIndex = iTop * WORLD_COLS + iRight;
//This happens to be the only mapindex that needs to be backed up. The rest have already been
//done because of the building code before this.
AddToUndoList( iMapIndex + 8*WORLD_COLS );
//Add the closest viewable pieces. There are two aframe walls pieces, and extended aframe roof pieces.
GetTileIndexFromTypeSubIndex( usWallType, HWALL_LEFT, &usTileIndex );
AddRoofToHead( iMapIndex + 4*WORLD_COLS, usTileIndex );
GetTileIndexFromTypeSubIndex( usWallType, HWALL_RIGHT, &usTileIndex );
AddRoofToHead( iMapIndex + 8*WORLD_COLS, usTileIndex );
GetTileIndexFromTypeSubIndex( usRoofType, THICK_TOP, &usTileIndex );
AddRoofToHead( iMapIndex + 3*WORLD_COLS, usTileIndex );
GetTileIndexFromTypeSubIndex( usRoofType, THICK_BOTTOM, &usTileIndex );
AddRoofToHead( iMapIndex + 7*WORLD_COLS, usTileIndex );
for( i = iRight - 1; i > iLeft; i-- )
{
iMapIndex--;
GetTileIndexFromTypeSubIndex( usRoofType, THIN_TOP, &usTileIndex );
AddRoofToHead( iMapIndex + 3*WORLD_COLS, usTileIndex );
GetTileIndexFromTypeSubIndex( usRoofType, THIN_BOTTOM, &usTileIndex );
AddRoofToHead( iMapIndex + 7*WORLD_COLS, usTileIndex );
}
iMapIndex--;
GetTileIndexFromTypeSubIndex( usRoofType, THICK_TOP, &usTileIndex );
AddRoofToHead( iMapIndex + 3*WORLD_COLS, usTileIndex );
GetTileIndexFromTypeSubIndex( usRoofType, THICK_BOTTOM, &usTileIndex );
AddRoofToHead( iMapIndex + 7*WORLD_COLS, usTileIndex );
}
}
UINT16 PickAWallPiece( UINT16 usWallPieceType )
{
UINT16 usVariants;
UINT16 usVariantChosen;
UINT16 usWallPieceChosen = 0;
if( usWallPieceType >= 0 || usWallPieceType < NUM_WALL_TYPES )
{
usVariants = gbWallTileLUT[ usWallPieceType ][ 0 ];
usVariantChosen = ( rand() % usVariants ) + 1;
usWallPieceChosen = gbWallTileLUT[ usWallPieceType ][ usVariantChosen ];
}
return usWallPieceChosen;
}
//From a given gridNo and perspective (wallpiece), it will calculate the new piece, and
//where to place it as well as handle the special cases.
//NOTE: Placing top and left pieces are placed relative to the gridno, and the gridNo will
// shift accordingly to place the piece. Pretend you are the floor, and you want to place a piece to
// the left. You pass your position, and INTERIOR_LEFT, with interior meaning from the inside of a
// building. If you were outside the building, you would call EXTERIOR_LEFT. The left tile will be
// placed on gridNo - 1! Up tiles will be placed on gridNo - 160.
//NOTE: Passing NULL for usWallType will force it to calculate the closest existing wall type, and
// use that for building this new wall. It is necessary for restructuring a building, but not for
// adding on to an existing building, where the type is already known.
void BuildWallPiece( INT32 iMapIndex, UINT8 ubWallPiece, UINT16 usWallType )
{
INT16 sIndex;
UINT16 usTileIndex;
UINT16 ubWallClass;
LEVELNODE *pStruct;
if( !usWallType )
{
usWallType = SearchForWallType( iMapIndex );
}
switch( ubWallPiece )
{
case EXTERIOR_TOP:
ubWallClass = EXTERIOR_L;
iMapIndex -= WORLD_COLS;
//exterior bottom left corner generated
if ( !gfBasement && GetVerticalWall( iMapIndex - 1) && !GetVerticalWall( iMapIndex + WORLD_COLS - 1) )
{ //Special case where a shadow has to be created as it now is a bottom corner and
//must contribute to the bottom shadow.
AddToUndoList( iMapIndex - 1 );
GetTileIndexFromTypeSubIndex( usWallType, INTERIOR_BOTTOMEND_SHADOW_INDEX, &usTileIndex );
AddExclusiveShadow( iMapIndex - 1, usTileIndex );
}
if ( pStruct = GetVerticalWall( iMapIndex ) ) //right corner
{ //Special case where placing the new wall will generate a corner to the right, so replace
//the vertical piece with a bottomend.
sIndex = PickAWallPiece( EXTERIOR_BOTTOMEND );
AddToUndoList( iMapIndex );
GetTileIndexFromTypeSubIndex( usWallType, sIndex, &usTileIndex );
ReplaceStructIndex( iMapIndex, pStruct->usIndex, usTileIndex );
}
if( !gfBasement )
{
//All exterior_l walls have shadows.
GetTileIndexFromTypeSubIndex( usWallType, EXTERIOR_L_SHADOW_INDEX, &usTileIndex );
AddExclusiveShadow( iMapIndex, usTileIndex );
}
break;
case EXTERIOR_BOTTOM:
ubWallClass = INTERIOR_L;
if( (pStruct = GetVerticalWall( iMapIndex + WORLD_COLS - 1 )) && !GetVerticalWall( iMapIndex - 1) )
{
sIndex = PickAWallPiece( INTERIOR_EXTENDED );
AddToUndoList( iMapIndex + WORLD_COLS - 1 );
GetTileIndexFromTypeSubIndex( usWallType, sIndex, &usTileIndex );
ReplaceStructIndex( iMapIndex + WORLD_COLS - 1, pStruct->usIndex, usTileIndex );
}
break;
case EXTERIOR_LEFT:
ubWallClass = EXTERIOR_R;
iMapIndex--;
if( GetHorizontalWall( iMapIndex ) )
{ //Special case where placing the new wall will generate a corner. This piece
//becomes an exterior bottomend, but nothing else is effected.
ubWallClass = EXTERIOR_BOTTOMEND;
}
if( GetHorizontalWall( iMapIndex - WORLD_COLS + 1 ) )
{
if( ubWallClass == EXTERIOR_BOTTOMEND )
ubWallClass = EXTERIOR_EXTENDED_BOTTOMEND;
else
ubWallClass = EXTERIOR_EXTENDED;
}
break;
case EXTERIOR_RIGHT:
ubWallClass = INTERIOR_R;
if( GetHorizontalWall( iMapIndex - WORLD_COLS + 1 ) && !GetHorizontalWall( iMapIndex - WORLD_COLS ) )
{
ubWallClass = INTERIOR_EXTENDED;
}
else if( GetHorizontalWall( iMapIndex ) && !GetVerticalWall( iMapIndex + WORLD_COLS ) )
{
ubWallClass = INTERIOR_BOTTOMEND;
}
break;
case INTERIOR_TOP:
ubWallClass = INTERIOR_L;
iMapIndex -= WORLD_COLS;
//check for a lower left corner.
if( pStruct = GetVerticalWall( iMapIndex + WORLD_COLS - 1 ) )
{ //Replace the piece with an extended piece.
sIndex = PickAWallPiece( INTERIOR_EXTENDED );
AddToUndoList( iMapIndex + WORLD_COLS - 1 );
GetTileIndexFromTypeSubIndex( usWallType, sIndex, &usTileIndex );
ReplaceStructIndex( iMapIndex + WORLD_COLS - 1, pStruct->usIndex, usTileIndex );
//NOTE: Not yet checking for interior extended bottomend!
}
if( pStruct = GetVerticalWall( iMapIndex ) )
{
sIndex = PickAWallPiece( INTERIOR_BOTTOMEND );
AddToUndoList( iMapIndex );
GetTileIndexFromTypeSubIndex( usWallType, sIndex, &usTileIndex );
ReplaceStructIndex( iMapIndex, pStruct->usIndex, usTileIndex );
}
break;
case INTERIOR_BOTTOM:
ubWallClass = EXTERIOR_L;
if( pStruct = GetVerticalWall( iMapIndex ) ) //right corner
{ //Special case where placing the new wall will generate a corner to the right, so replace
//the vertical piece with a bottomend.
sIndex = PickAWallPiece( EXTERIOR_BOTTOMEND );
AddToUndoList( iMapIndex );
GetTileIndexFromTypeSubIndex( usWallType, sIndex, &usTileIndex );
ReplaceStructIndex( iMapIndex, pStruct->usIndex, usTileIndex );
}
if( (pStruct = GetVerticalWall( iMapIndex + WORLD_COLS - 1 )) && !GetVerticalWall( iMapIndex - 1) )
{
sIndex = PickAWallPiece( EXTERIOR_EXTENDED );
AddToUndoList( iMapIndex + WORLD_COLS - 1 );
GetTileIndexFromTypeSubIndex( usWallType, sIndex, &usTileIndex );
ReplaceStructIndex( iMapIndex + WORLD_COLS - 1, pStruct->usIndex, usTileIndex );
}
if( !gfBasement )
{
//All exterior_l walls have shadows.
GetTileIndexFromTypeSubIndex( usWallType, EXTERIOR_L_SHADOW_INDEX, &usTileIndex );
AddExclusiveShadow( iMapIndex, usTileIndex );
}
break;
case INTERIOR_LEFT:
ubWallClass = INTERIOR_R;
iMapIndex--;
if( GetHorizontalWall( iMapIndex ) )
{
ubWallClass = INTERIOR_BOTTOMEND;
}
if( !gfBasement && GetHorizontalWall( iMapIndex + 1 ) )
{
AddToUndoList( iMapIndex );
GetTileIndexFromTypeSubIndex( usWallType, INTERIOR_BOTTOMEND_SHADOW_INDEX, &usTileIndex );
AddExclusiveShadow( iMapIndex, usTileIndex );
}
if( GetHorizontalWall( iMapIndex - WORLD_COLS + 1 ) )
{
if( ubWallClass == INTERIOR_BOTTOMEND )
ubWallClass = INTERIOR_EXTENDED_BOTTOMEND;
else
ubWallClass = INTERIOR_EXTENDED;
}
break;
case INTERIOR_RIGHT:
ubWallClass = EXTERIOR_R;
if( GetHorizontalWall( iMapIndex ) )
{ //Special case where placing the new wall will generate a corner. This piece
//becomes an exterior bottomend, but nothing else is effected.
ubWallClass = EXTERIOR_BOTTOMEND;
}
if( GetHorizontalWall( iMapIndex - WORLD_COLS + 1 ) )
{
if( ubWallClass == EXTERIOR_BOTTOMEND )
ubWallClass = EXTERIOR_EXTENDED_BOTTOMEND;
else
ubWallClass = EXTERIOR_EXTENDED;
}
if( !gfBasement && GetHorizontalWall( iMapIndex + 1 ) && !GetHorizontalWall( iMapIndex )
&& !FloorAtGridNo( iMapIndex + WORLD_COLS ) )
{
GetTileIndexFromTypeSubIndex( usWallType, INTERIOR_BOTTOMEND_SHADOW_INDEX, &usTileIndex );
AddExclusiveShadow( iMapIndex, usTileIndex );
}
break;
default:
AssertMsg( 0, "Invalid wall piece");
return;
}
sIndex = PickAWallPiece( ubWallClass );
GetTileIndexFromTypeSubIndex( usWallType, sIndex, &usTileIndex );
AddToUndoList( iMapIndex );
AddWallToStructLayer( iMapIndex, usTileIndex, FALSE );
}
void RebuildRoofUsingFloorInfo( INT32 iMapIndex, UINT16 usRoofType )
{
UINT16 usRoofIndex, usTileIndex;
BOOLEAN fTop = FALSE, fBottom = FALSE, fLeft = FALSE, fRight = FALSE;
if( !usRoofType )
{
usRoofType = SearchForRoofType( iMapIndex );
}
if( usRoofType == 0xffff )
return; //no roof type around, so don't draw one.
//Analyse the mapindex for walls and set the flags.
//NOTE: There is no support for more than 2 side on a roof, so if there is, draw TOPLEFT
AddToUndoList( iMapIndex );
EraseRoof( iMapIndex );
fTop = FloorAtGridNo( iMapIndex - WORLD_COLS ) ? FALSE : TRUE;
fLeft = FloorAtGridNo( iMapIndex - 1 ) ? FALSE : TRUE;
fBottom = FloorAtGridNo( iMapIndex + WORLD_COLS ) ? FALSE : TRUE;
fRight = FloorAtGridNo( iMapIndex + 1 ) ? FALSE : TRUE;
if( fTop && fLeft ) usRoofIndex = TOPLEFT_ROOF_INDEX;
else if( fTop && fRight) usRoofIndex = TOPRIGHT_ROOF_INDEX;
else if( fBottom && fLeft ) usRoofIndex = BOTTOMLEFT_ROOF_INDEX;
else if( fBottom && fRight ) usRoofIndex = BOTTOMRIGHT_ROOF_INDEX;
else if( fTop ) usRoofIndex = TOP_ROOF_INDEX;
else if( fBottom ) usRoofIndex = BOTTOM_ROOF_INDEX;
else if( fLeft ) usRoofIndex = LEFT_ROOF_INDEX;
else if( fRight ) usRoofIndex = RIGHT_ROOF_INDEX;
else usRoofIndex = CENTER_ROOF_BASE_INDEX + ( rand() % CENTER_ROOF_VARIANTS );
GetTileIndexFromTypeSubIndex( usRoofType, usRoofIndex, &usTileIndex );
AddRoofToHead( iMapIndex, usTileIndex );
//if the editor view roofs is off, then the new roofs need to be hidden.
if( !fBuildingShowRoofs )
{
HideStructOfGivenType( iMapIndex, usRoofType, TRUE );
}
}
//Given a gridno, it will erase the current roof, and calculate the new roof piece based on the
//wall orientions giving priority to the top and left walls before anything else.
//NOTE: passing NULL for usRoofType will force the function to calculate the nearest roof type,
// and use that for the new roof. This is needed when erasing parts of multiple buildings simultaneously.
void RebuildRoof( INT32 iMapIndex, UINT16 usRoofType )
{
UINT16 usRoofIndex, usTileIndex;
BOOLEAN fTop, fBottom, fLeft, fRight;
if( !usRoofType )
{
usRoofType = SearchForRoofType( iMapIndex );
}
if( usRoofType == 0xffff )
return; //no roof type around, so don't draw one.
//Analyse the mapindex for walls and set the flags.
//NOTE: There is no support for more than 2 side on a roof, so if there is, draw TOPLEFT
AddToUndoList( iMapIndex );
EraseRoof( iMapIndex );
fTop = GetHorizontalWall( iMapIndex - WORLD_COLS ) ? TRUE : FALSE;
fLeft = GetVerticalWall( iMapIndex - 1 ) ? TRUE : FALSE;
fBottom = GetHorizontalWall( iMapIndex ) ? TRUE : FALSE;
fRight = GetVerticalWall( iMapIndex ) ? TRUE : FALSE;
if( fTop && fLeft ) usRoofIndex = TOPLEFT_ROOF_INDEX;
else if( fTop && fRight) usRoofIndex = TOPRIGHT_ROOF_INDEX;
else if( fBottom && fLeft ) usRoofIndex = BOTTOMLEFT_ROOF_INDEX;
else if( fBottom && fRight ) usRoofIndex = BOTTOMRIGHT_ROOF_INDEX;
else if( fTop ) usRoofIndex = TOP_ROOF_INDEX;
else if( fBottom ) usRoofIndex = BOTTOM_ROOF_INDEX;
else if( fLeft ) usRoofIndex = LEFT_ROOF_INDEX;
else if( fRight ) usRoofIndex = RIGHT_ROOF_INDEX;
else usRoofIndex = CENTER_ROOF_BASE_INDEX + ( rand() % CENTER_ROOF_VARIANTS );
GetTileIndexFromTypeSubIndex( usRoofType, usRoofIndex, &usTileIndex );
AddRoofToHead( iMapIndex, usTileIndex );
//if the editor view roofs is off, then the new roofs need to be hidden.
if( !fBuildingShowRoofs )
{
HideStructOfGivenType( iMapIndex, usRoofType, TRUE );
}
}
void BulldozeNature( INT32 iMapIndex )
{
AddToUndoList( iMapIndex );
RemoveAllStructsOfTypeRange( iMapIndex, FIRSTISTRUCT,LASTISTRUCT );
RemoveAllShadowsOfTypeRange( iMapIndex, FIRSTCLIFFSHADOW, LASTCLIFFSHADOW );
RemoveAllStructsOfTypeRange( iMapIndex, FIRSTOSTRUCT,LASTOSTRUCT ); //outside objects.
RemoveAllShadowsOfTypeRange( iMapIndex, FIRSTSHADOW,LASTSHADOW );
RemoveAllStructsOfTypeRange( iMapIndex, FIRSTROAD, LASTROAD );
RemoveAllObjectsOfTypeRange( iMapIndex, DEBRISROCKS, LASTDEBRIS );
RemoveAllObjectsOfTypeRange( iMapIndex, ANOTHERDEBRIS, ANOTHERDEBRIS );
}
void EraseRoof( INT32 iMapIndex )
{
AddToUndoList( iMapIndex );
RemoveAllRoofsOfTypeRange( iMapIndex, FIRSTTEXTURE, LASTITEM );
RemoveAllOnRoofsOfTypeRange( iMapIndex, FIRSTTEXTURE, LASTITEM );
RemoveAllShadowsOfTypeRange( iMapIndex, FIRSTROOF, LASTSLANTROOF );
}
void EraseFloor( INT32 iMapIndex )
{
AddToUndoList( iMapIndex );
RemoveAllLandsOfTypeRange( iMapIndex, FIRSTFLOOR, LASTFLOOR );
}
void EraseWalls( INT32 iMapIndex )
{
AddToUndoList( iMapIndex );
RemoveAllStructsOfTypeRange( iMapIndex, FIRSTTEXTURE, LASTITEM );
RemoveAllShadowsOfTypeRange( iMapIndex, FIRSTWALL, LASTWALL );
RemoveAllShadowsOfTypeRange( iMapIndex, FIRSTDOORSHADOW, LASTDOORSHADOW );
RemoveAllObjectsOfTypeRange( iMapIndex, DEBRISROCKS, LASTDEBRIS );
RemoveAllTopmostsOfTypeRange( iMapIndex, WIREFRAMES, WIREFRAMES );
RemoveAllObjectsOfTypeRange( iMapIndex, DEBRIS2MISC, DEBRIS2MISC );
RemoveAllObjectsOfTypeRange( iMapIndex, ANOTHERDEBRIS, ANOTHERDEBRIS );
}
void EraseBuilding( INT32 iMapIndex )
{
EraseRoof( iMapIndex );
EraseFloor( iMapIndex );
EraseWalls( iMapIndex );
gusWorldRoomInfo[ iMapIndex ] = 0;
}
//Specialized function that will delete only the TOP_RIGHT oriented wall in the gridno to the left
//and the TOP_LEFT oriented wall in the gridno up one as well as the other building information at this
//gridno.
void EraseFloorOwnedBuildingPieces( INT32 iMapIndex )
{
LEVELNODE *pStruct = NULL;
UINT32 uiTileType;
UINT16 usWallOrientation;
if( !gfBasement && !FloorAtGridNo( iMapIndex ) )
{ //We don't have ownership issues if there isn't a floor here.
return;
}
EraseBuilding( iMapIndex );
//FIRST, SEARCH AND DESTROY FOR A LEFT NEIGHBORING TILE WITH A TOP_RIGHT ORIENTED WALL
pStruct = gpWorldLevelData[ iMapIndex - 1 ].pStructHead;
while( pStruct != NULL )
{
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 )
{
AddToUndoList( iMapIndex - 1 );
RemoveStruct( iMapIndex - 1, pStruct->usIndex );
RemoveAllShadowsOfTypeRange( iMapIndex - 1, FIRSTWALL, LASTWALL );
break; //otherwise, it'll crash because pStruct is toast.
}
}
}
pStruct = pStruct->pNext;
}
//FINALLY, SEARCH AND DESTROY FOR A TOP NEIGHBORING TILE WITH A TOP_LEFT ORIENTED WALL
pStruct = gpWorldLevelData[ iMapIndex - WORLD_COLS ].pStructHead;
while( pStruct != NULL )
{
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 )
{
AddToUndoList( iMapIndex - WORLD_COLS );
RemoveStruct( iMapIndex - WORLD_COLS , pStruct->usIndex );
RemoveAllShadowsOfTypeRange( iMapIndex - WORLD_COLS, FIRSTWALL, LASTWALL );
break; //otherwise, it'll crash because pStruct is toast.
}
}
}
pStruct = pStruct->pNext;
}
}
/*
BOOLEAN CaveAtGridNo( INT32 iMapIndex );
UINT16 GetCaveTileIndexFromPerimeterValue( UINT8 ubTotal );
UINT8 CalcNewCavePerimeterValue( INT32 iMapIndex );
void AddCave( INT32 iMapIndex, UINT16 usIndex );
*/
void RemoveCaveSectionFromWorld( SGPRect *pSelectRegion )
{
UINT32 top, left, right, bottom, x, y;
INT32 iMapIndex;
UINT16 usIndex;
UINT8 ubPerimeterValue;
top = pSelectRegion->iTop;
left = pSelectRegion->iLeft;
right = pSelectRegion->iRight;
bottom = pSelectRegion->iBottom;
//Pass 1: Remove all pieces in area
for( y = top; y <= bottom; y++ ) for( x = left; x <= right; x++ )
{
iMapIndex = y * WORLD_COLS + x;
AddToUndoList( iMapIndex );
RemoveAllStructsOfTypeRange( iMapIndex, FIRSTWALL, LASTWALL );
}
//Past 2: Go around outside perimeter and smooth each piece
for( y = top - 1; y <= bottom + 1; y++ ) for( x = left - 1; x <= right + 1; x++ )
{
iMapIndex = y * WORLD_COLS + x;
if( CaveAtGridNo( iMapIndex ) )
{
ubPerimeterValue = CalcNewCavePerimeterValue( iMapIndex );
usIndex = GetCaveTileIndexFromPerimeterValue( ubPerimeterValue );
AddToUndoList( iMapIndex );
if( usIndex != 0xffff )
AddCave( iMapIndex, usIndex );
else
{ //change piece to stalagmite...
RemoveAllStructsOfTypeRange( iMapIndex, FIRSTWALL, LASTWALL );
}
}
}
}
void AddCaveSectionToWorld( SGPRect *pSelectRegion )
{
INT32 top, left, right, bottom, x, y;
INT32 uiMapIndex;
UINT16 usIndex;
UINT8 ubPerimeterValue;
top = pSelectRegion->iTop;
left = pSelectRegion->iLeft;
right = pSelectRegion->iRight;
bottom = pSelectRegion->iBottom;
//Pass 1: Add bogus piece to each gridno in region
for( y = top; y <= bottom; y++ ) for( x = left; x <= right; x++ )
{
uiMapIndex = y * WORLD_COLS + x;
if (!TileIsOutOfBounds(uiMapIndex))
{
usIndex = GetCaveTileIndexFromPerimeterValue( 0xff );
AddToUndoList( uiMapIndex );
AddCave( uiMapIndex, usIndex );
}
}
//Past 2: Go around outside perimeter and smooth each piece
for( y = top - 1; y <= bottom + 1; y++ ) for( x = left - 1; x <= right + 1; x++ )
{
uiMapIndex = y * WORLD_COLS + x;
if(!TileIsOutOfBounds(uiMapIndex))
{
if( CaveAtGridNo( uiMapIndex ) )
{
ubPerimeterValue = CalcNewCavePerimeterValue( uiMapIndex );
usIndex = GetCaveTileIndexFromPerimeterValue( ubPerimeterValue );
AddToUndoList( uiMapIndex );
if( usIndex != 0xffff )
AddCave( uiMapIndex, usIndex );
else
{ //change piece to stalagmite...
RemoveAllStructsOfTypeRange( uiMapIndex, FIRSTWALL, LASTWALL );
}
}
}
}
}
//--------------------------------------------------------------------------------------------------
// END OF PRIVATE FUNCTION IMPLEMENTATION
//--------------------------------------------------------------------------------------------------
//--------------------------------------------------------------------------------------------------
// BEGIN PUBLIC FUNCTION IMPLEMENTATION
//--------------------------------------------------------------------------------------------------
//When the user removes a section from a building, it will not only erase the
//entire highlighted area, it'll repair the building itself so there are no
//outside walls missing from the new building.
void RemoveBuildingSectionFromWorld( SGPRect *pSelectRegion )
{
UINT32 top, left, right, bottom, x, y;
INT32 iMapIndex;
UINT16 usTileIndex;
UINT16 usFloorType;
BOOLEAN fFloor;
top = pSelectRegion->iTop;
left = pSelectRegion->iLeft;
right = pSelectRegion->iRight;
bottom = pSelectRegion->iBottom;
//1ST PASS: Erase all building owned by the floor tile if there is one.
for( y = top; y <= bottom; y++ ) for( x = left; x <= right; x++ )
{
iMapIndex = y * WORLD_COLS + x;
EraseFloorOwnedBuildingPieces( iMapIndex ); //Erase possible top and left walls in bordering tiles.
}
//2ND PASS: Build new walls whereever there are neighboring floor tiles.
for( y = top; y <= bottom; y++ ) for( x = left; x <= right; x++ )
{
iMapIndex = y * WORLD_COLS + x;
//NOTE: Top and bottom walls MUST be placed first -- it minimizes the number of special cases.
if( y == top )
{
fFloor = FloorAtGridNo( iMapIndex - WORLD_COLS );
if( gfBasement && !fFloor || !gfBasement && fFloor && !GetHorizontalWall( iMapIndex - WORLD_COLS ) )
BuildWallPiece( iMapIndex, EXTERIOR_TOP, 0 );
}
if( y == bottom )
{
fFloor = FloorAtGridNo( iMapIndex + WORLD_COLS );
if( gfBasement && !fFloor || !gfBasement && fFloor && !GetHorizontalWall( iMapIndex ) )
BuildWallPiece( iMapIndex, EXTERIOR_BOTTOM, 0 );
}
if( x == left )
{
fFloor = FloorAtGridNo( iMapIndex - 1 );
if( gfBasement && !fFloor || !gfBasement && fFloor && !GetVerticalWall( iMapIndex - 1 ) )
BuildWallPiece( iMapIndex, EXTERIOR_LEFT, 0 );
}
if( x == right )
{
fFloor = FloorAtGridNo( iMapIndex + 1);
if( gfBasement && !fFloor || !gfBasement && fFloor && !GetVerticalWall( iMapIndex ) )
BuildWallPiece( iMapIndex, EXTERIOR_RIGHT, 0 );
}
}
//3RD PASS: Go around the outside of the region, and rebuild the roof.
if( gfBasement )
{
usFloorType = GetRandomIndexByRange( FIRSTFLOOR, LASTFLOOR );
if( usFloorType == 0xffff )
usFloorType = FIRSTFLOOR;
for( y = top; y <= bottom; y++ ) for( x = left; x <= right; x++ )
{
iMapIndex = y * WORLD_COLS + x;
GetTileIndexFromTypeSubIndex( usFloorType, 1, &usTileIndex );
AddLandToHead( iMapIndex, (UINT16)( usTileIndex + Random( FLOOR_VARIANTS ) ) );
}
}
for( y = top - 1; y <= bottom + 1; y++ ) for( x = left - 1; x <= right + 1; x++ )
{
iMapIndex = y * WORLD_COLS + x;
if( y == top - 1 || y == bottom + 1 || x == left - 1 || x == right + 1 )
{
if( !gfBasement && FloorAtGridNo( iMapIndex ) || gfBasement && !FloorAtGridNo( iMapIndex ) )
{
RebuildRoof( iMapIndex, 0 );
}
}
}
}
void AddBuildingSectionToWorld( SGPRect *pSelectRegion )
{
INT32 top, left, right, bottom, x, y;
INT32 iMapIndex;
UINT16 usFloorType, usWallType, usRoofType;
UINT16 usTileIndex;
BOOLEAN fNewBuilding;
BOOLEAN fSlantRoof = FALSE;
BOOLEAN fVertical;
BOOLEAN fFloor;
top = pSelectRegion->iTop;
left = pSelectRegion->iLeft;
right = pSelectRegion->iRight;
bottom = pSelectRegion->iBottom;
//Special case scenario:
//If the user selects a floor without walls, then it is implied that the user wishes to
//change the floor for say a kitchen which might have a different floor type.
usWallType = GetRandomIndexByRange( FIRSTWALL, LASTWALL );
usFloorType = GetRandomIndexByRange( FIRSTFLOOR, LASTFLOOR );
usRoofType = 0xffff;
if( usWallType == 0xffff && usFloorType != 0xffff )
{ //allow user to place floors
for( y = top; y <= bottom; y++ ) for( x = left; x <= right; x++ )
{
iMapIndex = y * WORLD_COLS + x;
EraseFloor( iMapIndex );
GetTileIndexFromTypeSubIndex( usFloorType, 1, &usTileIndex );
AddLandToHead( iMapIndex, (UINT16)( usTileIndex + Random( FLOOR_VARIANTS ) ) );
}
//we are done!
return;
}
//1ST PASS: Determine if there are any floor tiles in this region. If there are, then
// that signifies that we are concantenating this building to an existing one. Otherwise,
// we are just drawing an individual building. If we find a floor, extract the type so
// we know how to draw it later.
fNewBuilding = TRUE;
for( y = top; y <= bottom; y++ ) for( x = left; x <= right; x++ )
{
iMapIndex = y * WORLD_COLS + x;
if( FloorAtGridNo( iMapIndex ) )
{
LEVELNODE *pFloor;
UINT32 uiTileType;
//If a floor is found, then we are adding to an existing structure.
fNewBuilding = FALSE;
//Extract the floor type. We already checked if there was a floor here, so it is assumed.
pFloor = gpWorldLevelData[ iMapIndex ].pLandHead;
while( pFloor )
{
GetTileType( pFloor->usIndex, &uiTileType );
if( uiTileType >= FIRSTFLOOR && uiTileType <= LASTFLOOR )
{
usFloorType = (UINT16)uiTileType;
break;
}
}
usWallType = SearchForWallType( iMapIndex );
usRoofType = SearchForRoofType( iMapIndex );
if( usWallType != 0xffff && usRoofType != 0xffff && usFloorType !=0xffff )
{ //we have extracted all of the information we need, so we can break out.
y = bottom;
break;
}
}
}
if( fNewBuilding )
{
//if( gfBasement )
// return;
//Get materials via selection window method.
usWallType = GetRandomIndexByRange( FIRSTWALL, LASTWALL );
usFloorType = GetRandomIndexByRange( FIRSTFLOOR, LASTFLOOR );
usRoofType = GetRandomIndexByRange( FIRSTROOF, LASTROOF );
if( usRoofType == 0xffff )
{
usRoofType = GetRandomIndexByRange( FIRSTSLANTROOF, LASTSLANTROOF );
if( usRoofType != 0xffff )
{
if( !gfBasement )
fSlantRoof = TRUE;
else
usRoofType = FIRSTROOF;
}
}
if( usWallType == 0xffff )
return;
}
//2ND PASS: Remove all walls in the region that border no floor tile, or simply walls
// that are considered exterior walls. That way, it won't wreck the inside of a building
// if you select too much interior. Also, gridnos that delete walls will also delete the
// floor and roof tiles there. That signifies that the floorless parts will be resmoothed,
// and rebuilt in the third pass.
for( y = top; y <= bottom; y++ ) for( x = left; x <= right; x++ )
{
iMapIndex = y * WORLD_COLS + x;
if( gfBasement )
{
EraseBuilding( iMapIndex );
}
else if( FloorAtGridNo( iMapIndex ) && !fNewBuilding)
{
if( y >= top && !FloorAtGridNo( iMapIndex - WORLD_COLS ) )
{
EraseHorizontalWall( iMapIndex - WORLD_COLS );
EraseFloor( iMapIndex );
EraseRoof( iMapIndex );
}
if( y <= bottom && !FloorAtGridNo( iMapIndex + WORLD_COLS ) )
{
EraseHorizontalWall( iMapIndex );
EraseFloor( iMapIndex );
EraseRoof( iMapIndex );
}
if( x >= left && !FloorAtGridNo( iMapIndex - 1 ) )
{
EraseVerticalWall( iMapIndex - 1 );
EraseFloor( iMapIndex );
EraseRoof( iMapIndex );
}
if( x <= right && !FloorAtGridNo( iMapIndex + 1 ) )
{
EraseVerticalWall( iMapIndex );
EraseFloor( iMapIndex );
EraseRoof( iMapIndex );
}
}
else //we will be building outside of this structure, so bulldoze the nature -- trees, rocks, etc.
{
BulldozeNature( iMapIndex );
}
}
//3RD PASS: Process the region, and all walls of floorless tiles are rebuilt from interior perspective.
for( y = top; y <= bottom; y++ ) for( x = left; x <= right; x++ )
{
iMapIndex = y * WORLD_COLS + x;
if( !FloorAtGridNo( iMapIndex ) )
{
if( y == top && !GetHorizontalWall( iMapIndex - WORLD_COLS ) )
{
fFloor = FloorAtGridNo( iMapIndex - WORLD_COLS );
if( gfBasement == fFloor )
BuildWallPiece( iMapIndex, INTERIOR_TOP, usWallType);
}
if( y == bottom && !GetHorizontalWall( iMapIndex ) )
{
fFloor = FloorAtGridNo( iMapIndex + WORLD_COLS );
if( gfBasement == fFloor )
BuildWallPiece( iMapIndex, INTERIOR_BOTTOM, usWallType );
}
if( x == left && !GetVerticalWall( iMapIndex - 1 ) )
{
fFloor = FloorAtGridNo( iMapIndex - 1 );
if( gfBasement == fFloor )
BuildWallPiece( iMapIndex, INTERIOR_LEFT, usWallType );
}
if( x == right && !GetVerticalWall( iMapIndex ) )
{
fFloor = FloorAtGridNo( iMapIndex + 1 );
if( gfBasement == fFloor )
BuildWallPiece( iMapIndex, INTERIOR_RIGHT, usWallType );
}
}
}
//If we are dealing with slant roofs then build the whole thing now.
//Slant roofs always have a width or height of 8 tiles.
if( fSlantRoof )
{
fVertical = (bottom - top == 7) ? FALSE : TRUE;
BuildSlantRoof( left, top, right, bottom, usWallType, usRoofType, fVertical );
}
//4TH PASS: Process the region, and all floorless tiles get new roofs and floors.
for( y = top; y <= bottom; y++ ) for( x = left; x <= right; x++ )
{
iMapIndex = y * WORLD_COLS + x;
if( !FloorAtGridNo( iMapIndex ) )
{
if( !fSlantRoof )
RebuildRoof( iMapIndex, usRoofType );
if( usFloorType != 0xffff && !gfBasement )
{
GetTileIndexFromTypeSubIndex( usFloorType, 1, &usTileIndex );
AddLandToHead( iMapIndex, (UINT16)( usTileIndex + Random( FLOOR_VARIANTS ) ) );
}
}
}
}
void AnalyseCaveMapForStructureInfo()
{
LEVELNODE *pStruct;
UINT32 uiTileType;
INT32 iMapIndex;
for( iMapIndex = 0; iMapIndex < WORLD_MAX; iMapIndex++ )
{
pStruct = gpWorldLevelData[ iMapIndex ].pStructHead;
while( pStruct )
{
if ( pStruct->usIndex != NO_TILE )
{
GetTileType( pStruct->usIndex, &uiTileType );
if( uiTileType == FIRSTWALL )
{
UINT16 usSubIndex;
GetSubIndexFromTileIndex( pStruct->usIndex, &usSubIndex );
if( usSubIndex >= 60 && usSubIndex <= 65 )
{
pStruct->uiFlags |= LEVELNODE_CAVE;
}
}
}
pStruct = pStruct->pNext;
}
}
}
#endif