Home » MODDING HQ 1.13 » v1.13 Coding Talk » Roof climbing problem in 1.13
Roof climbing problem in 1.13[message #344502]
|
Sun, 13 March 2016 10:44
|
|
Deleted. |
![](/images/ranks/liutenant.png) |
Messages:2656
Registered:December 2012 Location: Russian Federation |
|
|
1. In 1.13, roof climbing used ASTAR.
2. At some moment, ASTAR was disabled.
3. As a result, roof climbing is partially broken now.
4. As solution, all functions that use ASTAR should be reverted to vanilla state.
For example, FindClosestClimbPoint in FindLocations.cpp:
// 0verhaul: This function is optimized to take advantage of the new climb point info.
if (gpWorldLevelData[ sGridNo].ubExtFlags[0] & MAPELEMENT_EXT_CLIMBPOINT)
MAPELEMENT_EXT_CLIMBPOINT is set in void AStarPathfinder::ExecuteAStarLogic(), which is disabled.
So, all functions that use MAPELEMENT_EXT_CLIMBPOINT should be reverted to vanilla state:
FindClosestClimbPoint, FindDirectionForClimbing
Another problem:
in GenerateBuilding (Buildings.cpp), the following code is used:
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
What is interesting is
gubBuildingInfo[ sCurrGridNo ] = ubBuildingID;
which sets building information for the tile that is outside of the building. As a result, all tiles adjacent to building are set as they belong to that building, as a result, SameBuilding returns TRUE when first gridno is inside building and second gridno is outside of the building.
There's no such code in vanilla source, this looks like some old 1.13 bug.
Another problem:
when AI tries to find a path to a disturbance on different level, it first searches for a closest climb point.
Then, if the climbing point is found, it tries to estimate path cost to climb point, at which moment it fails if the soldier is already standing at the climb point, because EstimatePathCostToLocation returns 0.
This means that if AI was moving to climb and reached the climb point, next turn instead of climbing he moves to another climb point or simply decides taking cover or something else, because his seacrh for closest disturbance call fails.
--------------------------------------------------------------------------------------------------------------
My solution to some probems, implemented and tested in ja2+ai:
in Buildings.cpp/FindClosestClimbPoint:
allow climbing from the spot where soldier stands:
Toggle Spoiler for ( ubLoop = 0; ubLoop < ubNumClimbSpots; ubLoop++ )
{
if( (WhoIsThere2( pBuilding->sUpClimbSpots[ ubLoop ], 0 ) == NOBODY ||
WhoIsThere2( pBuilding->sUpClimbSpots[ ubLoop ], 0 ) == pSoldier->ubID ) &&
(WhoIsThere2( pBuilding->sDownClimbSpots[ ubLoop ], 1 ) == NOBODY ||
WhoIsThere2( pBuilding->sDownClimbSpots[ ubLoop ], 1 ) == pSoldier->ubID) &&
!InGas( pSoldier, psClimbSpots[ ubLoop] ) &&
!Water( psClimbSpots[ ubLoop], pSoldier->pathing.bLevel) )
{
sDistance = PythSpacesAway( sStartGridNo, psClimbSpots[ ubLoop ] );
if (sDistance < sClosestDistance )
{
sClosestDistance = sDistance;
sClosestSpot = psClimbSpots[ ubLoop ];
}
}
}
In AIUtils.cpp/EstimatePathCostToLocation:
don't return 0 if soldier is standing at climb point:
Toggle SpoilersPathCost = PlotPath( pSoldier, sClimbGridNo, FALSE, FALSE, FALSE, WALKING, FALSE, FALSE, 0 );
// sevenfm: check if we are already standing at climb gridno
if (sPathCost != 0 || pSoldier->sGridNo == sClimbGridNo)
{
// add in cost of climbing down
if (fAddCostAfterClimbingUp)
{
// add in cost of later climbing up, too
sPathCost += APBPConstants[AP_CLIMBOFFROOF] + APBPConstants[AP_CLIMBROOF];
// add in an estimate of getting there after climbing down
sPathCost += (APBPConstants[AP_MOVEMENT_FLAT] + APBPConstants[AP_MODIFIER_WALK]) * PythSpacesAway( sClimbGridNo, sDestGridNo );
}
else
{
sPathCost += APBPConstants[AP_CLIMBOFFROOF];
// add in an estimate of getting there after climbing down, *but not on top of roof*
sPathCost += (APBPConstants[AP_MOVEMENT_FLAT] + APBPConstants[AP_MODIFIER_WALK]) * PythSpacesAway( sClimbGridNo, sDestGridNo ) / 2;
}
*pfClimbingNecessary = TRUE;
*psClimbGridNo = sClimbGridNo;
}
sPathCost = PlotPath( pSoldier, sClimbGridNo, FALSE, FALSE, FALSE, WALKING, FALSE, FALSE, 0);
// sevenfm: check if we are already standing at climb gridno
if (sPathCost != 0 || pSoldier->sGridNo == sClimbGridNo)
{
// add in the cost of climbing up or down
if (pSoldier->pathing.bLevel == 0)
{
// must climb up
sPathCost += APBPConstants[AP_CLIMBROOF];
if (fAddCostAfterClimbingUp)
{
// add to path a rough estimate of how far to go from the climb gridno to the friend
// estimate walk cost
sPathCost += (APBPConstants[AP_MOVEMENT_FLAT] + APBPConstants[AP_MODIFIER_WALK]) * PythSpacesAway( sClimbGridNo, sDestGridNo );
}
}
else
{
// must climb down
sPathCost += APBPConstants[AP_CLIMBOFFROOF];
// add to path a rough estimate of how far to go from the climb gridno to the friend
// estimate walk cost
sPathCost += (APBPConstants[AP_MOVEMENT_FLAT] + APBPConstants[AP_MODIFIER_WALK]) * PythSpacesAway( sClimbGridNo, sDestGridNo );
}
*pfClimbingNecessary = TRUE;
*psClimbGridNo = sClimbGridNo;
}
FindLocations.cpp/FindClosestClimbPoint: new calculation using FindHeigherLevel
Toggle SpoilerINT32 FindClosestClimbPoint (SOLDIERTYPE *pSoldier, BOOLEAN fClimbUp )
{
INT32 sBestSpot = NOWHERE;
// sevenfm: safety check
if(!pSoldier)
{
return NOWHERE;
}
//DebugMsg( TOPIC_JA2AI , DBG_LEVEL_3 , "FindClosestClimbPoint");
if (fClimbUp)
{
// For the climb up case, search the nearby limits for a climb up point and take the closest.
INT32 sGridNo;
static const INT32 iSearchRange = 20;
INT16 sMaxLeft, sMaxRight, sMaxUp, sMaxDown, sXOffset, sYOffset;
//UINT8 ubTestDir;
INT8 ubClimbDir;
INT32 sClimbSpot;
// determine maximum horizontal limits
sMaxLeft = min( iSearchRange, (pSoldier->sGridNo % MAXCOL));
//NumMessage("sMaxLeft = ",sMaxLeft);
sMaxRight = min( iSearchRange, MAXCOL - ((pSoldier->sGridNo % MAXCOL) + 1));
//NumMessage("sMaxRight = ",sMaxRight);
// determine maximum vertical limits
sMaxUp = min( iSearchRange, (pSoldier->sGridNo / MAXROW));
//NumMessage("sMaxUp = ",sMaxUp);
sMaxDown = min( iSearchRange, MAXROW - ((pSoldier->sGridNo / MAXROW) + 1));
//NumMessage("sMaxDown = ",sMaxDown);
//DebugMsg( TOPIC_JA2AI , DBG_LEVEL_3 , String("Max: Left %d Right %d Up %d Down %d", sMaxLeft, sMaxRight, sMaxUp, sMaxDown ) );
for (sYOffset = -sMaxUp; sYOffset <= sMaxDown; sYOffset++)
{
for (sXOffset = -sMaxLeft; sXOffset <= sMaxRight; sXOffset++)
{
// calculate the next potential gridno
sGridNo = pSoldier->sGridNo + sXOffset + (MAXCOL * sYOffset);
//DebugMsg( TOPIC_JA2AI , DBG_LEVEL_3 , String("Checking grid %d" , sGridNo ));
//NumMessage("Testing gridno #",gridno);
if ( !(sGridNo >=0 && sGridNo < WORLD_MAX) )
{
continue;
}
if ( sGridNo == pSoldier->pathing.sBlackList )
{
continue;
}
// exclude locations with tear/mustard gas (at this point, smoke is cool!)
if ( InGas( pSoldier, sGridNo ) )
{
continue;
}
// exclude water tiles
if ( Water( sGridNo, pSoldier->pathing.bLevel ) )
{
continue;
}
// check that there's noone at sGridNo at level 0 (this soldier is allowed)
if( WhoIsThere2( sGridNo, 0 ) != NOBODY &&
WhoIsThere2( sGridNo, 0 ) != pSoldier->ubID )
{
continue;
}
if( FindHeigherLevel( pSoldier, sGridNo, pSoldier->ubDirection, &ubClimbDir ) )
{
// ubClimbDir is new direction
// check that there's noone there
sClimbSpot = NewGridNo( sGridNo, DirectionInc( ubClimbDir));
if( WhoIsThere2( sClimbSpot, 1 ) == NOBODY )
{
// Good climb point. Is it closer than the previous point?
if( TileIsOutOfBounds(sBestSpot) ||
GetRangeInCellCoordsFromGridNoDiff( pSoldier->sGridNo , sGridNo ) < GetRangeInCellCoordsFromGridNoDiff( pSoldier->sGridNo , sBestSpot ))
{
// If not, we have a new winnar!
sBestSpot = sGridNo;
}
}
}
}
}
}
else
{
// Climbing down is easier. Just find the nearest climb point ;)
sBestSpot = FindClosestClimbPoint( pSoldier, pSoldier->sGridNo, pSoldier->sGridNo, fClimbUp);
}
// DebugMsg( TOPIC_JA2AI , DBG_LEVEL_3 , String("FindClosestClimbPoint Returning %d", sBestSpot ));
return( sBestSpot );
}
FindLocations.cpp/FindDirectionForClimbing: new calculation using FindHeigherLevel/FindLowerLevel
Toggle SpoilerINT8 FindDirectionForClimbing( SOLDIERTYPE *pSoldier, INT32 sGridNo )
{
INT8 ubClimbDir;
INT32 sClimbSpot;
if(!pSoldier)
{
return DIRECTION_IRRELEVANT;
}
if (pSoldier->pathing.bLevel == 0)
{
// check climb up
if( FindHeigherLevel( pSoldier, sGridNo, pSoldier->ubDirection, &ubClimbDir ) )
{
// ubClimbDir is new direction
// check that there's noone there
sClimbSpot = NewGridNo( sGridNo, DirectionInc( ubClimbDir));
if( WhoIsThere2( sClimbSpot, 1 ) == NOBODY &&
!Water(sClimbSpot, 1) )
{
return ubClimbDir;
}
}
}
else
{
// check climb down
if( FindLowerLevel( pSoldier, pSoldier->sGridNo, pSoldier->ubDirection, &ubClimbDir ) )
{
// ubClimbDir is new direction
sClimbSpot = NewGridNo( sGridNo, DirectionInc( ubClimbDir));
if( WhoIsThere2( sClimbSpot, 0 ) == NOBODY &&
!Water(sClimbSpot, 0) )
{
return ubClimbDir;
}
}
}
return DIRECTION_IRRELEVANT;
}
Hope that helps.
[Updated on: Sun, 13 March 2016 10:47]
Left this community.Report message to a moderator
|
|
|
|
|
Re: Roof climbing problem in 1.13[message #344529 is a reply to message #344527]
|
Mon, 14 March 2016 10:43 ![Go to previous message Go to previous message](/theme/Bear_Classic_Brown/images/up.png)
|
|
Deleted. |
![](/images/ranks/liutenant.png) |
Messages:2656
Registered:December 2012 Location: Russian Federation |
|
|
Sorry I posted the code from my ja2+ai project, where I also changed Water, DeepWater and WaterTooDeepForAttack functions to accepts second parameter (level), as it allows AI to make correct decisions for locations on bridges (roof above the water).
For the simple roof climbing fix it should be enough to use default 1.13 water checks, so you can simply remove second (level) parameter, or you can use updated functions and change all instances in the code to use correct second parameter (usually pSoldier->pathing.bLevel)
The checks in Water functions are simple - return FALSE if level > 0, because we cannot have water on roofs anyway
here's the code:
Toggle SpoilerBOOLEAN Water( INT32 sGridNo, BOOLEAN bLevel )
{
MAP_ELEMENT * pMapElement;
if( TileIsOutOfBounds( sGridNo ) )
{
return( FALSE );
}
// sevenfm: check bridge
if( bLevel )
{
return FALSE;
}
pMapElement = &(gpWorldLevelData[sGridNo]);
if ( TERRAIN_IS_WATER( pMapElement->ubTerrainID) )
{
// check for a bridge! otherwise...
return( TRUE );
}
else
{
return( FALSE );
}
}
BOOLEAN DeepWater( INT32 sGridNo, BOOLEAN bLevel )
{
MAP_ELEMENT * pMapElement;
if( TileIsOutOfBounds( sGridNo ) )
{
return( FALSE );
}
// sevenfm: check bridge
if( bLevel )
{
return FALSE;
}
pMapElement = &(gpWorldLevelData[sGridNo]);
if (TERRAIN_IS_DEEP_WATER( pMapElement->ubTerrainID) )
{
// check for a bridge! otherwise...
return( TRUE );
}
else
{
return( FALSE );
}
}
BOOLEAN WaterTooDeepForAttacks( INT32 sGridNo, BOOLEAN bLevel )
{
return( DeepWater( sGridNo, bLevel ) );
}
There are also some other roof climbing decision problems in DecideAction, for example I think there's a path in RED seek code when AI will not climb when it should, but fixing it requires much more changes than simple patch. I think the original RED seek logic was partially broken at the time when flanking was added originally.
There's also a problem with the vanilla code that creates climbing spots for building, especially on some maps where building info is not set up correctly, but I never looked too deep to fix it. The result is that AI cannot climb on some buildings even when the climbing code works correcly (for example, some Wildfire maps), but this probably should be fixed with map editor.
Left this community.Report message to a moderator
|
|
|
|
Re: Roof climbing problem in 1.13[message #344541 is a reply to message #344529]
|
Mon, 14 March 2016 19:05
|
|
wanne (aka RoWa21) |
![](/images/ranks/sergeant_major.png) |
Messages:1961
Registered:October 2005 Location: Austria |
|
|
Thanks, I will commit the patch tomorrow.
EDIT: Bugfixes are committed. I also updated the "Water", ... methods with the additional parameter
[Updated on: Tue, 15 March 2016 14:47] Report message to a moderator
|
|
|
|
Goto Forum:
Current Time: Tue Feb 18 22:05:29 GMT+2 2025
Total time taken to generate the page: 0.01259 seconds
|