Slopes and bits

Discuss the new AI features ("NoAI") introduced into OpenTTD 0.7, allowing you to implement custom AIs, and the new Game Scripts available in OpenTTD 1.2 and higher.

Moderator: OpenTTD Developers

Post Reply
R2dical
Traffic Manager
Traffic Manager
Posts: 163
Joined: 18 Mar 2013 22:22

Slopes and bits

Post by R2dical »

So my bit arithmetic/logic manipulation knowledge is not very comprehensive and it seems you need that mindset when working with slopes in the API. As a result I am struggling to write a function I need to landscape a tile to the desired slope, and am hoping there is someone able to help.

My current code is plain wrong so is omitted, but here is the function skeleton:

Code: Select all

/**
 * Perform landscapeing on a tile to get a specific slope.
 * @param tile The tile to landscape.
 * @param slope The slope to try get.
 * @return True if completed, false if fail.
 */
function MapBuilder::LandscapeSlope(tile, desired_slope) {

}
My current approach is like so:

// Validate input.

// Raise.
if (!AITile.RaiseTile(tile, desired_slope)) return false;
// Lower.
if (!AITile.LowerTile(tile, desired_slope)) return false;

return true;

But clearly wrong, I think I may have to go the way of analysing current tile slope, raise/lower the difference, etc.
Any help will be appreciated!
User avatar
planetmaker
OpenTTD Developer
OpenTTD Developer
Posts: 9432
Joined: 07 Nov 2007 22:44
Location: Sol d

Re: Slopes and bits

Post by planetmaker »

You might be interested in the value listed in the column 'numerical value': http://newgrf-specs.tt-wiki.net/wiki/NM ... ile_slopes
R2dical
Traffic Manager
Traffic Manager
Posts: 163
Joined: 18 Mar 2013 22:22

Re: Slopes and bits

Post by R2dical »

Thanks planetmaker, though I can't figure out a way to "deconstruct" the bitmask result (or related numerical value), or find an API approach to use them directly.

I settled on a large switch, for each slope type and some arrays linking the bits, but its just seems like there is a better way somewhere...

Code: Select all

/**
 * Perform landscapeing on a single tile to get a specific slope.
 * @param tile The tile to landscape.
 * @param desired_slope The slope to try get.
 * @param desired_height The desired AITile.GetMinHeight of the resulting tile.
 * @return True if completed, false if fail.
 */
function MapBuilder::LandscapeSlope(tile, desired_slope, desired_height) {

	// Validate.
	if (!AIMap.IsValidTile(tile))														throw("Must be valid tile!");
	if (typeof(desired_slope) != "integer" || desired_slope == AITile.SLOPE_INVALID)	throw("Must be valid slope!");
	if (typeof(desired_height) != "integer")											throw("Argument is invalid height!");
	
	// Check if not required.
	if (AITile.GetSlope(tile) == desired_slope && AITile.GetMinHeight(tile) == desired_height) return true;
	
	// Get desired corner height.
	local corners_to_set = null;		// Delta from tile min height for each corner [N, E, W, S];
	switch (desired_slope) {
		case AITile.SLOPE_FLAT:			corners_to_set = [0, 0, 0, 0]; break;
		case AITile.SLOPE_W:			corners_to_set = [0, 0, 1, 0]; break;
		case AITile.SLOPE_S:			corners_to_set = [0, 0, 0, 1]; break;
		case AITile.SLOPE_E:			corners_to_set = [0, 1, 0, 0]; break;
		case AITile.SLOPE_N:			corners_to_set = [1, 0, 0, 0]; break;
		case AITile.SLOPE_STEEP:		throw("Steep slope not valid!"); break;
		case AITile.SLOPE_NW:			corners_to_set = [1, 0, 1, 0]; break;
		case AITile.SLOPE_SW:			corners_to_set = [0, 0, 1, 1]; break;
		case AITile.SLOPE_SE:			corners_to_set = [0, 1, 0, 1]; break;
		case AITile.SLOPE_NE:			corners_to_set = [1, 1, 0, 0]; break;
		case AITile.SLOPE_EW:			corners_to_set = [0, 1, 1, 0]; break;
		case AITile.SLOPE_NS:			corners_to_set = [1, 0, 0, 1]; break;
		case AITile.SLOPE_ELEVATED:		throw("Elevated slope not valid!"); break;
		case AITile.SLOPE_NWS:			corners_to_set = [1, 0, 1, 1]; break;
		case AITile.SLOPE_WSE:			corners_to_set = [0, 1, 1, 1]; break;
		case AITile.SLOPE_SEN:			corners_to_set = [1, 1, 0, 1]; break;
		case AITile.SLOPE_ENW:			corners_to_set = [1, 1, 1, 0]; break;
		case AITile.SLOPE_STEEP_W:		corners_to_set = [0, 0, 2, 0]; break;
		case AITile.SLOPE_STEEP_S:		corners_to_set = [0, 0, 0, 2]; break;
		case AITile.SLOPE_STEEP_E:		corners_to_set = [0, 2, 0, 0]; break;
		case AITile.SLOPE_STEEP_N:		corners_to_set = [2, 0, 0, 0]; break;
		case AITile.SLOPE_INVALID:		throw("Invalid slope!"); break;
		default: 						throw("Unhandled slope!"); break;
	}
	
	// Create mirror corner and slope arrays.
	// Has to match above SWITCH array.
	local corner_array =	[AITile.CORNER_N,	AITile.CORNER_E,	AITile.CORNER_W,	AITile.CORNER_S];
	local slope_array =		[AITile.SLOPE_N,	AITile.SLOPE_E,		AITile.SLOPE_W,		AITile.SLOPE_S];
	local build_mode = AIExecMode();
	
	// Loop for each corner.
	foreach (corner_index, corner in corner_array) {
		
		// Loop landscape each corner till desired height or fail.
		local corner_height = null;
		local desired_corner_height = desired_height + corners_to_set[corner_index];
		while((corner_height = AITile.GetCornerHeight(tile, corner)) != desired_corner_height) {
			
			// Raise if lower.
			if (corner_height < desired_corner_height)
				if(!AITile.RaiseTile(tile, slope_array[corner_index])) return false;
			
			// Lower if higher.
			else if (corner_height > desired_corner_height)
				if(!AITile.LowerTile(tile, slope_array[corner_index])) return false;
		}
	}
	
	return true;
}
krinn
Transport Coordinator
Transport Coordinator
Posts: 342
Joined: 29 Dec 2010 19:36

Re: Slopes and bits

Post by krinn »

I think your adding complexity for no result.
There's in "real" no point (ok for me) for a function to transform a tile to the wanted slope.

Only human may do that, as it will only gave a visual result.
An AI have no value of "it's nice like that", so an AI would only get stuck to the primary usage of altering any tile corner -> force that tile to be flat so we could build on it.

You'll see that no AI will ever query to get SLOPE_W or SLOPE_STEEP_W. For construction, no human will ask that too, but for some aesthetic usage, a human may do that.

That's why your aim get complex, you don't make a function to alter a tile to be at FLAT ONLY at the desired height, but build a function to alter a tile to be like the desired slope.

If you still go like that, and we assume that really someone may use the function to create a steep slope, make sure height > 1 if anyone ask a steep slope. It's easy to see you cannot get any steep slope without at least a height of 2.
But i think you should just get back to make a function that just limit "desired_sloop == SLOPE_FLAT"
R2dical
Traffic Manager
Traffic Manager
Posts: 163
Joined: 18 Mar 2013 22:22

Re: Slopes and bits

Post by R2dical »

I need this function for my canal/lock building, locks need a 2 corner slope tile (SLOPE_NW for example). Any other slope type returns an ERR_SLOPE_WRONG error. Also this way I can try get my desired slope rather than try evaluate current, build the lock, then retry pathfinding if the lock entry and exit tiles are not on the expected axis.

Additionally I plan to integrate this into pathfinding directly, calling in AITestMode will allow the pathfinder to do some basic landscaping planning while planning the path: "the current slope is X", "well if it is Y would the slope cost be different?", "if a different slope allows a better path then mark it for landscaping"...

Finally it will be used in rail and road building, as currently I get a fail when building a segment with "allow building on slopes" changes the next path tiles expected slope and therefor prohibits building the planned part. Especially for Rail which uses a 2-lane pathfinder and gets this event quite often.

In all those cases it is much simpler to try get the desired slope+height than evaluate and plan around the current slope.
Post Reply

Return to “OpenTTD AIs and Game Scripts”

Who is online

Users browsing this forum: Ahrefs [Bot], Google [Bot], SilverHawk and 17 guests