Files
source/TileEngine/Buildings.cpp
T
Wanne ba6cb14f9b - Bugfix: Fixed bug, where enemies do not climb on rooftops anymore, when ASTAR is disabled
.) The bug was introduced in revision 1534 (TileEngine/Buildings.cpp)
.) Overhaul reworked the code for climbing on buildings and it seems the bug was introduced there
.) I created a new define "VANILLA_BUILDING_CLIMBING" (defined in Building.h). If defined the "old" building climbing code will be used. If not defined Overhauls code will be used (and so enemies do not climb on buildings). Overhauls code should be fixed and then used instead of the "old" vanilla building climbing code.
.) I also disabled ASTAR pathing, because it is a HUGH PERFORMANCE killer

git-svn-id: https://ja2svn.mooo.com/source/ja2/trunk/GameSource/ja2_v1.13/Build@3228 3b4a5df2-a311-0410-b5c6-a8a6f20db521
2009-10-05 15:28:59 +00:00

581 lines
16 KiB
C++

#ifdef PRECOMPILEDHEADERS
#include "TileEngine All.h"
#else
#include "Types.h"
#include "Buildings.h"
#include "Isometric Utils.h"
#include "Pathai.h"
#include "Structure Wrap.h"
#include "Random.h"
#include "Overhead.h"
#include "Render Fun.h"
#include "Strategicmap.h"
#include "Sys Globals.h"
#include "worldman.h"
#endif
#include "AIInternals.h"
#define ROOF_LOCATION_CHANCE 8
UINT8 gubBuildingInfo[ WORLD_MAX ];
BUILDING gBuildings[ MAX_BUILDINGS ];
UINT8 gubNumberOfBuildings;
#ifdef ROOF_DEBUG
extern INT16 gsCoverValue[WORLD_MAX];
#include "video.h"
#include "renderworld.h"
#endif
BUILDING * CreateNewBuilding( UINT8 * pubBuilding )
{
if (gubNumberOfBuildings + 1 >= MAX_BUILDINGS)
{
return( NULL );
}
// increment # of buildings
gubNumberOfBuildings++;
// clear entry
gBuildings[ gubNumberOfBuildings ].ubNumClimbSpots = 0;
*pubBuilding = gubNumberOfBuildings;
// return pointer (have to subtract 1 since we just added 1
return( &(gBuildings[ gubNumberOfBuildings ]) );
}
BUILDING * GenerateBuilding( INT16 sDesiredSpot )
{
#ifdef VANILLA_BUILDING_CLIMBING
UINT32 uiLoop;
UINT32 uiLoop2;
INT16 sTempGridNo, sNextTempGridNo, sVeryTemporaryGridNo;
INT16 sStartGridNo, sCurrGridNo, sPrevGridNo = NOWHERE, sRightGridNo;
UINT8 ubDirection, ubTempDirection;
BOOLEAN fFoundDir, fFoundWall;
UINT32 uiChanceIn = ROOF_LOCATION_CHANCE; // chance of a location being considered
INT16 sWallGridNo;
INT8 bDesiredOrientation;
INT8 bSkipSpots = 0;
SOLDIERTYPE FakeSoldier;
INT32 iLoopCount = 0;
#endif
BUILDING * pBuilding;
UINT8 ubBuildingID = 0;
//INT32 iLoopCount = 0;
pBuilding = CreateNewBuilding( &ubBuildingID );
if (!pBuilding)
{
return( NULL );
}
// WDS - Clean up inventory handling
// set up fake soldier for location testing
// memset( &FakeSoldier, 0, SIZEOF_SOLDIERTYPE );
#ifdef VANILLA_BUILDING_CLIMBING
FakeSoldier.initialize();
FakeSoldier.sGridNo = sDesiredSpot;
FakeSoldier.pathing.bLevel = 1;
FakeSoldier.bTeam = 1;
#endif
// Set reachable
RoofReachableTest( sDesiredSpot, ubBuildingID );
// 0verhaul: The RoofReachableTest now finds ALL of the climb points for each climbable building, instead of a max of
// 21 climb points (and a min of 0) for each building. It claims an extended map flag to mark a tile as a climb point.
// So the array of up-climbs and down-climbs is now obsolete. FindClosestClimbPoint is now updated to search the map
// for these flags.
#ifdef VANILLA_BUILDING_CLIMBING
// From sGridNo, search until we find a spot that isn't part of the building
ubDirection = NORTHWEST;
sTempGridNo = sDesiredSpot;
// using diagonal directions to hopefully prevent picking a
// spot that
while( (gpWorldLevelData[ sTempGridNo ].uiFlags & MAPELEMENT_REACHABLE ) )
{
sNextTempGridNo = NewGridNo( sTempGridNo, DirectionInc( ubDirection ) );
if ( sTempGridNo == sNextTempGridNo )
{
// hit edge of map!??!
return( NULL );
}
else
{
sTempGridNo = sNextTempGridNo;
}
}
// we've got our spot
sStartGridNo = sTempGridNo;
sCurrGridNo = sStartGridNo;
sVeryTemporaryGridNo = NewGridNo( sCurrGridNo, DirectionInc( EAST ) );
if ( gpWorldLevelData[ sVeryTemporaryGridNo ].uiFlags & MAPELEMENT_REACHABLE )
{
// go north first
ubDirection = NORTH;
}
else
{
// go that way (east)
ubDirection = EAST;
}
gpWorldLevelData[ sStartGridNo ].ubExtFlags[0] |= MAPELEMENT_EXT_ROOFCODE_VISITED;
uiLoop2 = 0;
while(uiLoop2 < WORLD_MAX)
{
// if point to (2 clockwise) is not part of building and is not visited,
// or is starting point, turn!
sRightGridNo = NewGridNo( sCurrGridNo, DirectionInc( gTwoCDirection[ ubDirection ] ) );
sTempGridNo = sRightGridNo;
if ( ( ( !(gpWorldLevelData[ sTempGridNo ].uiFlags & MAPELEMENT_REACHABLE) && !(gpWorldLevelData[ sTempGridNo ].ubExtFlags[0] & MAPELEMENT_EXT_ROOFCODE_VISITED) ) || (sTempGridNo == sStartGridNo) ) && (sCurrGridNo != sStartGridNo) )
{
iLoopCount++;
if ( iLoopCount >= 10 )
{
return( NULL );
}
ubDirection = gTwoCDirection[ ubDirection ];
// try in that direction
continue;
}
iLoopCount = 0;
// if spot ahead is part of building, turn
sTempGridNo = NewGridNo( sCurrGridNo, DirectionInc( ubDirection ) );
if ( gpWorldLevelData[ sTempGridNo ].uiFlags & MAPELEMENT_REACHABLE )
{
// first search for a spot that is neither part of the building or visited
// we KNOW that the spot in the original direction is blocked, so only loop 3 times
ubTempDirection = gTwoCDirection[ ubDirection ];
fFoundDir = FALSE;
for ( uiLoop = 0; uiLoop < 3; uiLoop++ )
{
sTempGridNo = NewGridNo( sCurrGridNo, DirectionInc( ubTempDirection ) );
if ( !(gpWorldLevelData[ sTempGridNo ].uiFlags & MAPELEMENT_REACHABLE) && !(gpWorldLevelData[ sTempGridNo ].ubExtFlags[0] & MAPELEMENT_EXT_ROOFCODE_VISITED) )
{
// this is the way to go!
fFoundDir = TRUE;
break;
}
ubTempDirection = gTwoCDirection[ ubTempDirection ];
}
if (!fFoundDir)
{
// now search for a spot that is just not part of the building
ubTempDirection = gTwoCDirection[ ubDirection ];
fFoundDir = FALSE;
for ( uiLoop = 0; uiLoop < 3; uiLoop++ )
{
sTempGridNo = NewGridNo( sCurrGridNo, DirectionInc( ubTempDirection ) );
if ( !(gpWorldLevelData[ sTempGridNo ].uiFlags & MAPELEMENT_REACHABLE) )
{
// this is the way to go!
fFoundDir = TRUE;
break;
}
ubTempDirection = gTwoCDirection[ ubTempDirection ];
}
if (!fFoundDir)
{
// WTF is going on?
return( NULL );
}
}
ubDirection = ubTempDirection;
// try in that direction
continue;
}
// move ahead
sPrevGridNo = sCurrGridNo;
sCurrGridNo = sTempGridNo;
sRightGridNo = NewGridNo( sCurrGridNo, DirectionInc( gTwoCDirection[ ubDirection ] ) );
#ifdef ROOF_DEBUG
if (gsCoverValue[sCurrGridNo] == 0x7F7F)
{
gsCoverValue[sCurrGridNo] = 1;
}
else if (gsCoverValue[sCurrGridNo] >= 0)
{
gsCoverValue[sCurrGridNo]++;
}
//DebugAI( String( "Roof code visits %d", sCurrGridNo ) );
#endif
if (sCurrGridNo == sStartGridNo)
{
// done
break;
}
if ( !(gpWorldLevelData[ sCurrGridNo ].ubExtFlags[0] & MAPELEMENT_EXT_ROOFCODE_VISITED) )
{
gpWorldLevelData[ sCurrGridNo ].ubExtFlags[0] |= MAPELEMENT_EXT_ROOFCODE_VISITED;
gubBuildingInfo[ sCurrGridNo ] = ubBuildingID;
// consider this location as possible climb gridno
// there must be a regular wall adjacent to this for us to consider it a
// climb gridno
// if the direction is east or north, the wall would be in our gridno;
// if south or west, the wall would be in the gridno two clockwise
fFoundWall = FALSE;
#ifndef VANILLA_BUILDING_CLIMBING
// There must not be roof here either. There are places where a pitched roof butts up against a flat roof.
// Don't mark such a border as a climb point. Otherwise AI units will get stuck.
if (FindStructure( sCurrGridNo, STRUCTURE_ROOF ) == NULL &&
NewOKDestination( &FakeSoldier, sCurrGridNo, FALSE, 0 ) )
{
#endif
switch( ubDirection )
{
case NORTH:
sWallGridNo = sCurrGridNo;
bDesiredOrientation = OUTSIDE_TOP_RIGHT;
break;
case EAST:
sWallGridNo = sCurrGridNo;
bDesiredOrientation = OUTSIDE_TOP_LEFT;
break;
case SOUTH:
sWallGridNo = (INT16) ( sCurrGridNo + DirectionInc( gTwoCDirection[ ubDirection ] ) );
bDesiredOrientation = OUTSIDE_TOP_RIGHT;
break;
case WEST:
sWallGridNo = (INT16) ( sCurrGridNo + DirectionInc( gTwoCDirection[ ubDirection ] ) );
bDesiredOrientation = OUTSIDE_TOP_LEFT;
break;
default:
// what the heck?
return( NULL );
}
if (bDesiredOrientation == OUTSIDE_TOP_LEFT)
{
if (WallExistsOfTopLeftOrientation( sWallGridNo ))
{
fFoundWall = TRUE;
}
}
else
{
if (WallExistsOfTopRightOrientation( sWallGridNo ))
{
fFoundWall = TRUE;
}
}
#ifndef VANILLA_BUILDING_CLIMBING
}
#endif
if (fFoundWall)
{
#ifdef ROOF_DEBUG
gsCoverValue[sCurrGridNo] = 50;
#endif
if (bSkipSpots > 0)
{
bSkipSpots--;
}
else if ( Random( uiChanceIn ) == 0 )
{
pBuilding->sUpClimbSpots[ pBuilding->ubNumClimbSpots ] = sCurrGridNo;
pBuilding->sDownClimbSpots[ pBuilding->ubNumClimbSpots ] = sRightGridNo;
pBuilding->ubNumClimbSpots++;
if ( pBuilding->ubNumClimbSpots == MAX_CLIMBSPOTS_PER_BUILDING)
{
// gotta stop!
return( pBuilding );
}
// if location is added as a spot, reset uiChanceIn
uiChanceIn = ROOF_LOCATION_CHANCE;
#ifdef ROOF_DEBUG
gsCoverValue[sCurrGridNo] = 99;
#endif
// skip the next spot
bSkipSpots = 1;
}
else
{
// didn't pick this location, so increase chance that next location
// will be considered
if (uiChanceIn > 2)
{
uiChanceIn--;
}
}
}
else
{
// can't select this spot
if ( (sPrevGridNo != NOWHERE) && (pBuilding->ubNumClimbSpots > 0) )
{
if ( pBuilding->sDownClimbSpots[ pBuilding->ubNumClimbSpots - 1 ] == sCurrGridNo )
{
// unselect previous spot
pBuilding->ubNumClimbSpots--;
// overwrote a selected spot so go into automatic selection for later
uiChanceIn = 1;
#ifdef ROOF_DEBUG
// reset marker
gsCoverValue[sPrevGridNo] = 1;
#endif
}
}
// skip the next gridno
bSkipSpots = 1;
}
}
uiLoop2++;
}
// If we've run out of loop before we've run out of building, then there is
// something that has gone pear shaped
if(uiLoop2 >= WORLD_MAX)
{
UINT8 x = 0;
UINT8 y = 0;
while((sDesiredSpot - ((y + 1) * 160)) >= 0)
{
y++;
}
x = sDesiredSpot - (y * 160);
DebugMsg (TOPIC_JA2,DBG_LEVEL_2,String( "113/UC Warning! Building Walk Algorithm has covered the entire map! Building %d located at [%d,%d] must be bogus.", ubBuildingID, x, y ));
}
// at end could prune # of locations if there are too many
/*
#ifdef ROOF_DEBUG
SetRenderFlags( RENDER_FLAG_FULL );
RenderWorld();
//RenderCoverDebug( );
InvalidateScreen( );
EndFrameBufferRender();
RefreshScreen( NULL );
#endif
*/
#endif
return( pBuilding );
}
BUILDING * FindBuilding( INT16 sGridNo )
{
UINT8 ubBuildingID;
//UINT8 ubRoomNo;
if (sGridNo <= 0 || sGridNo > WORLD_MAX)
{
return( NULL );
}
// id 0 indicates no building
ubBuildingID = gubBuildingInfo[ sGridNo ];
if ( ubBuildingID == NO_BUILDING )
{
return( NULL );
/*
// need extra checks to see if is valid spot...
// must have valid room information and be a flat-roofed
// building
if ( InARoom( sGridNo, &ubRoomNo ) && (FindStructure( sGridNo, STRUCTURE_NORMAL_ROOF ) != NULL) )
{
return( GenerateBuilding( sGridNo ) );
}
else
{
return( NULL );
}
*/
}
else if ( ubBuildingID > gubNumberOfBuildings ) // huh?
{
return( NULL );
}
return( &(gBuildings[ ubBuildingID ]) );
}
BOOLEAN InBuilding( INT16 sGridNo )
{
if ( FindBuilding( sGridNo ) == NULL )
{
return( FALSE );
}
return( TRUE );
}
void GenerateBuildings( void )
{
UINT32 uiLoop;
// init building structures and variables
memset( gubBuildingInfo, 0, WORLD_MAX * sizeof( UINT8 ) );
memset( &gBuildings, 0, MAX_BUILDINGS * sizeof( BUILDING ) );
gubNumberOfBuildings = 0;
if ( (gbWorldSectorZ > 0) || gfEditMode)
{
return;
}
#ifdef ROOF_DEBUG
memset( gsCoverValue, 0x7F, sizeof( INT16 ) * WORLD_MAX );
#endif
// reset ALL reachable flags
// do once before we start building generation for
// whole map
for ( uiLoop = 0; uiLoop < WORLD_MAX; uiLoop++ )
{
gpWorldLevelData[ uiLoop ].uiFlags &= ~(MAPELEMENT_REACHABLE);
gpWorldLevelData[ uiLoop ].ubExtFlags[0] &= ~(MAPELEMENT_EXT_ROOFCODE_VISITED);
}
// search through world
// for each location in a room try to find building info
for ( uiLoop = 0; uiLoop < WORLD_MAX; uiLoop++ )
{
if ( (gubWorldRoomInfo[ uiLoop ] != NO_ROOM) && (gubBuildingInfo[ uiLoop ] == NO_BUILDING) && (FindStructure( (INT16) uiLoop, STRUCTURE_NORMAL_ROOF ) != NULL) )
{
GenerateBuilding( (INT16) uiLoop );
}
}
}
INT16 FindClosestClimbPoint( SOLDIERTYPE *pSoldier, INT16 sStartGridNo, INT16 sDesiredGridNo, BOOLEAN fClimbUp )
{
BUILDING * pBuilding;
#ifndef VANILLA_BUILDING_CLIMBING
INT16 sGridNo;
INT16 sTestGridNo;
UINT8 ubTestDir;
#else
UINT8 ubNumClimbSpots;
INT16 * psClimbSpots;
UINT8 ubLoop;
#endif
INT16 sDistance, sClosestDistance = 1000, sClosestSpot= NOWHERE;
pBuilding = FindBuilding( sDesiredGridNo );
if (!pBuilding)
{
return( NOWHERE );
}
#ifndef VANILLA_BUILDING_CLIMBING
for (sGridNo = 0; sGridNo < WORLD_MAX; sGridNo++)
{
if (gubBuildingInfo[ sGridNo ] == gubBuildingInfo[ sDesiredGridNo ] &&
gpWorldLevelData[ sGridNo ].ubExtFlags[1] & MAPELEMENT_EXT_CLIMBPOINT)
{
// Found a climb point for this building
if (fClimbUp)
{
for (ubTestDir = 0; ubTestDir < 8; ubTestDir += 2)
{
sTestGridNo = NewGridNo( sGridNo, DirectionInc( ubTestDir));
if (gpWorldLevelData[ sTestGridNo ].ubExtFlags[0] & MAPELEMENT_EXT_CLIMBPOINT)
{
// Found a matching climb point
if ( (WhoIsThere2( sTestGridNo, 0 ) == NOBODY || sTestGridNo == pSoldier->sGridNo)
&& (WhoIsThere2( sGridNo, 1 ) == NOBODY) &&
(!pSoldier || !InGas( pSoldier, sTestGridNo ) ) )
{
// And it's open
sDistance = PythSpacesAway( sStartGridNo, sTestGridNo );
if (sDistance < sClosestDistance )
{
sClosestDistance = sDistance;
sClosestSpot = sTestGridNo;
}
}
}
}
}
else
{
for (ubTestDir = 0; ubTestDir < 8; ubTestDir += 2)
{
sTestGridNo = NewGridNo( sGridNo, DirectionInc( ubTestDir));
if (gpWorldLevelData[ sTestGridNo ].ubExtFlags[0] & MAPELEMENT_EXT_CLIMBPOINT)
{
// Found a matching climb point
if ( (WhoIsThere2( sTestGridNo, 0 ) == NOBODY) &&
(WhoIsThere2( sGridNo, 1 ) == NOBODY || sGridNo == pSoldier->sGridNo) &&
(!pSoldier || !InGas( pSoldier, sTestGridNo ) ) )
{
// And it's open
sDistance = PythSpacesAway( sStartGridNo, sGridNo );
if (sDistance < sClosestDistance )
{
sClosestDistance = sDistance;
sClosestSpot = sGridNo;
}
}
}
}
}
}
}
#else
ubNumClimbSpots = pBuilding->ubNumClimbSpots;
if (fClimbUp)
{
psClimbSpots = pBuilding->sUpClimbSpots;
}
else
{
psClimbSpots = pBuilding->sDownClimbSpots;
}
for ( ubLoop = 0; ubLoop < ubNumClimbSpots; ubLoop++ )
{
if ( (WhoIsThere2( pBuilding->sUpClimbSpots[ ubLoop ], 0 ) == NOBODY)
&& (WhoIsThere2( pBuilding->sDownClimbSpots[ ubLoop ], 1 ) == NOBODY) &&
(!pSoldier || !InGas( pSoldier, psClimbSpots[ ubLoop] ) ) )
{
sDistance = PythSpacesAway( sStartGridNo, psClimbSpots[ ubLoop ] );
if (sDistance < sClosestDistance )
{
sClosestDistance = sDistance;
sClosestSpot = psClimbSpots[ ubLoop ];
}
}
}
#endif
return( sClosestSpot );
}
BOOLEAN SameBuilding( INT16 sGridNo1, INT16 sGridNo2 )
{
if ( gubBuildingInfo[ sGridNo1 ] == NO_BUILDING )
{
return( FALSE );
}
if ( gubBuildingInfo[ sGridNo2 ] == NO_BUILDING )
{
return( FALSE );
}
return( (BOOLEAN) (gubBuildingInfo[ sGridNo1] == gubBuildingInfo[ sGridNo2 ]) );
}