Example #1
0
static sint32 map_get_corner_height(sint32 x, sint32 y, sint32 corner)
{
    rct_map_element *mapElement = map_get_surface_element_at(x, y);
    sint32 baseHeight = mapElement->base_height;
    sint32 slope = mapElement->properties.surface.slope;
    sint32 doubleCorner = slope & 16;
    if (doubleCorner) {
        if (!(slope & 1)) doubleCorner = 4;
        else if (!(slope & 2)) doubleCorner = 8;
        else if (!(slope & 4)) doubleCorner = 1;
        else if (!(slope & 8)) doubleCorner = 2;
    }

    switch (corner) {
    case 0:
        return baseHeight + ((slope & 1) ? (doubleCorner == 1 ? 4 : 2) : 0);
    case 1:
        return baseHeight + ((slope & 8) ? (doubleCorner == 8 ? 4 : 2) : 0);
    case 2:
        return baseHeight + ((slope & 2) ? (doubleCorner == 2 ? 4 : 2) : 0);
    case 3:
        return baseHeight + ((slope & 4) ? (doubleCorner == 4 ? 4 : 2) : 0);
    default:
        return baseHeight;
    }
}
Example #2
0
int map_get_corner_height(int x, int y, int corner)
{
	rct_map_element *mapElement = map_get_surface_element_at(x, y);
	int baseHeight = mapElement->base_height;
	int slope = mapElement->properties.surface.slope;
	int doubleCorner = slope & 16;
	if (doubleCorner) {
		if (!(slope & 1)) doubleCorner = 4;
		else if (!(slope & 2)) doubleCorner = 8;
		else if (!(slope & 4)) doubleCorner = 1;
		else if (!(slope & 8)) doubleCorner = 2;
	}

	switch (corner) {
	case 0:
		return baseHeight + (slope & 1 ? (doubleCorner == 1 ? 4 : 2) : 0);
	case 1:
		return baseHeight + (slope & 8 ? (doubleCorner == 8 ? 4 : 2) : 0);
	case 2:
		return baseHeight + (slope & 2 ? (doubleCorner == 2 ? 4 : 2) : 0);
	case 3:
		return baseHeight + (slope & 4 ? (doubleCorner == 4 ? 4 : 2) : 0);
	default:
		return baseHeight;
	}
}
Example #3
0
/**
 * Sets the height of the actual game map tiles to the height map.
 */
static void mapgen_set_height()
{
	sint32 x, y, heightX, heightY, mapSize;
	rct_map_element *mapElement;

	mapSize = _heightSize / 2;
	for (y = 1; y < mapSize - 1; y++) {
		for (x = 1; x < mapSize - 1; x++) {
			heightX = x * 2;
			heightY = y * 2;

			uint8 q00 = get_height(heightX + 0, heightY + 0);
			uint8 q01 = get_height(heightX + 0, heightY + 1);
			uint8 q10 = get_height(heightX + 1, heightY + 0);
			uint8 q11 = get_height(heightX + 1, heightY + 1);

			uint8 baseHeight = (q00 + q01 + q10 + q11) / 4;

			mapElement = map_get_surface_element_at(x, y);
			mapElement->base_height = max(2, baseHeight * 2);
			mapElement->clearance_height = mapElement->base_height;

			if (q00 > baseHeight)
				mapElement->properties.surface.slope |= 4;
			if (q01 > baseHeight)
				mapElement->properties.surface.slope |= 8;
			if (q10 > baseHeight)
				mapElement->properties.surface.slope |= 2;
			if (q11 > baseHeight)
				mapElement->properties.surface.slope |= 1;
		}
	}
}
Example #4
0
/**
 * Sets each tile's water level to the specified water level if underneath that water level.
 */
static void mapgen_set_water_level(int waterLevel)
{
	int x, y, mapSize;
	rct_map_element *mapElement;

	mapSize = RCT2_GLOBAL(RCT2_ADDRESS_MAP_SIZE, sint16);

	for (y = 1; y < mapSize - 1; y++) {
		for (x = 1; x < mapSize - 1; x++) {
			mapElement = map_get_surface_element_at(x, y);
			if (mapElement->base_height < waterLevel)
				mapElement->properties.surface.terrain |= (waterLevel  / 2);
		}
	}
}
Example #5
0
/**
 * Sets each tile's water level to the specified water level if underneath that water level.
 */
static void mapgen_set_water_level(sint32 waterLevel)
{
	sint32 x, y, mapSize;
	rct_map_element *mapElement;

	mapSize = gMapSize;

	for (y = 1; y < mapSize - 1; y++) {
		for (x = 1; x < mapSize - 1; x++) {
			mapElement = map_get_surface_element_at(x, y);
			if (mapElement->base_height < waterLevel)
				mapElement->properties.surface.terrain |= (waterLevel  / 2);
		}
	}
}
Example #6
0
void mapgen_generate_blank(mapgen_settings *settings)
{
	sint32 x, y;
	rct_map_element *mapElement;

	map_clear_all_elements();

	map_init(settings->mapSize);
	for (y = 1; y < settings->mapSize - 1; y++) {
		for (x = 1; x < settings->mapSize - 1; x++) {
			mapElement = map_get_surface_element_at(x, y);
			map_element_set_terrain(mapElement, settings->floor);
			map_element_set_terrain_edge(mapElement, settings->wall);
			mapElement->base_height = settings->height;
			mapElement->clearance_height = settings->height;
		}
	}

	mapgen_set_water_level(settings->waterLevel);
}
Example #7
0
static void cheat_set_grass_length(int length)
{
	int x, y;
	rct_map_element *mapElement;

	for (y = 0; y < 256; y++) {
		for (x = 0; x < 256; x++) {
			mapElement = map_get_surface_element_at(x, y);
			if (!(mapElement->properties.surface.ownership & OWNERSHIP_OWNED))
				continue;

			if (map_element_get_terrain(mapElement) != TERRAIN_GRASS)
				continue;

			if ((mapElement->properties.surface.terrain & 0x1F) > 0)
				continue;

			mapElement->properties.surface.grass_length = length;
		}
	}

	gfx_invalidate_screen();
}
Example #8
0
static void cheat_set_grass_length(sint32 length)
{
    sint32 x, y;
    rct_tile_element *tileElement;

    for (y = 0; y < MAXIMUM_MAP_SIZE_TECHNICAL; y++) {
        for (x = 0; x < MAXIMUM_MAP_SIZE_TECHNICAL; x++) {
            tileElement = map_get_surface_element_at(x, y);
            if (!(tileElement->properties.surface.ownership & OWNERSHIP_OWNED))
                continue;

            if (tile_element_get_terrain(tileElement) != TERRAIN_GRASS)
                continue;

            if (map_get_water_height(tileElement) > 0)
                continue;

            tileElement->properties.surface.grass_length = length;
        }
    }

    gfx_invalidate_screen();
}
Example #9
0
/**
* Returns the audio parameters to use when playing the specified sound at a virtual location.
* @param soundId The sound effect to be played.
* @param location The location at which the sound effect is to be played.
* @return The audio parameters to be used when playing this sound effect.
*/
rct_audio_params audio_get_params_from_location(int soundId, const rct_xyz16 *location)
{
    int volumeDown = 0;
    rct_audio_params params;
    params.in_range = true;
    params.volume = 0;
    params.pan = 0;

    rct_map_element *element = map_get_surface_element_at(location->x / 32, location->y / 32);
    if (element && (element->base_height * 8) - 5 > location->z)
        volumeDown = 10;

    uint8 rotation = get_current_rotation();
    rct_xy16 pos2 = coordinate_3d_to_2d(location, rotation);
    rct_window *window = RCT2_GLOBAL(RCT2_ADDRESS_NEW_WINDOW_PTR, rct_window*);
    while (true) {
        window--;
        if (window < RCT2_ADDRESS(RCT2_ADDRESS_WINDOW_LIST, rct_window))
            break;

        rct_viewport *viewport = window->viewport;
        if (!viewport || !(viewport->flags & VIEWPORT_FLAG_SOUND_ON))
            continue;

        sint16 vy = pos2.y - viewport->view_y;
        sint16 vx = pos2.x - viewport->view_x;
        params.pan = viewport->x + (vx >> viewport->zoom);
        params.volume = RCT2_ADDRESS(0x0099282C, int)[soundId] + ((-1024 * viewport->zoom - 1) << volumeDown) + 1;

        if (vy < 0 || vy >= viewport->view_height || vx < 0 || vx >= viewport->view_width || params.volume < -10000) {
            params.in_range = false;
            return params;
        }
    }

    return params;
}
Example #10
0
/**
 * Randomly places a selection of preset trees on the map. Picks the right tree for the terrain it is placing it on.
 */
static void mapgen_place_trees()
{
	sint32 numGrassTreeIds = 0, numDesertTreeIds = 0, numSnowTreeIds = 0;
	sint32 *grassTreeIds = (sint32*)malloc(countof(GrassTrees) * sizeof(sint32));
	sint32 *desertTreeIds = (sint32*)malloc(countof(DesertTrees) * sizeof(sint32));
	sint32 *snowTreeIds = (sint32*)malloc(countof(SnowTrees) * sizeof(sint32));

	for (sint32 i = 0; i < object_entry_group_counts[OBJECT_TYPE_SMALL_SCENERY]; i++) {
		rct_scenery_entry *sceneryEntry = get_small_scenery_entry(i);
		rct_object_entry_extended *entry = &object_entry_groups[OBJECT_TYPE_SMALL_SCENERY].entries[i];

		if (sceneryEntry == (rct_scenery_entry*)-1 || sceneryEntry == NULL)
			continue;

		sint32 j;
		for (j = 0; j < countof(GrassTrees); j++)
			if (strncmp(GrassTrees[j], entry->name, 8) == 0)
				break;
		if (j != countof(GrassTrees)) {
			grassTreeIds[numGrassTreeIds++] = i;
			continue;
		}

		for (j = 0; j < countof(DesertTrees); j++)
			if (strncmp(DesertTrees[j], entry->name, 8) == 0)
				break;
		if (j != countof(DesertTrees)) {
			desertTreeIds[numDesertTreeIds++] = i;
			continue;
		}

		for (j = 0; j < countof(SnowTrees); j++)
			if (strncmp(SnowTrees[j], entry->name, 8) == 0)
				break;
		if (j != countof(SnowTrees)) {
			snowTreeIds[numSnowTreeIds++] = i;
			continue;
		}
	}

	sint32 availablePositionsCount = 0;
	struct { sint32 x; sint32 y; } tmp, *pos, *availablePositions;
	availablePositions = malloc(256 * 256 * sizeof(tmp));

	// Create list of available tiles
	for (sint32 y = 1; y < gMapSize - 1; y++) {
		for (sint32 x = 1; x < gMapSize - 1; x++) {
			rct_map_element *mapElement = map_get_surface_element_at(x, y);

			// Exclude water tiles
			if ((mapElement->properties.surface.terrain & 0x1F) != 0)
				continue;

			pos = &availablePositions[availablePositionsCount++];
			pos->x = x;
			pos->y = y;
		}
	}

	// Shuffle list
	for (sint32 i = 0; i < availablePositionsCount; i++) {
		sint32 rindex = util_rand() % availablePositionsCount;
		if (rindex == i)
			continue;

		tmp = availablePositions[i];
		availablePositions[i] = availablePositions[rindex];
		availablePositions[rindex] = tmp;
	}

	// Place trees
	float treeToLandRatio = (10 + (util_rand() % 30)) / 100.0f;
	sint32 numTrees = max(4, (sint32)(availablePositionsCount * treeToLandRatio));

	for (sint32 i = 0; i < numTrees; i++) {
		pos = &availablePositions[i];

		sint32 type = -1;
		rct_map_element *mapElement = map_get_surface_element_at(pos->x, pos->y);
		switch (map_element_get_terrain(mapElement)) {
		case TERRAIN_GRASS:
		case TERRAIN_DIRT:
		case TERRAIN_GRASS_CLUMPS:
			if (numGrassTreeIds == 0)
				break;

			type = grassTreeIds[util_rand() % numGrassTreeIds];
			break;

		case TERRAIN_SAND:
		case TERRAIN_SAND_DARK:
		case TERRAIN_SAND_LIGHT:
			if (numDesertTreeIds == 0)
				break;

			if (util_rand() % 4 == 0)
				type = desertTreeIds[util_rand() % numDesertTreeIds];
			break;

		case TERRAIN_ICE:
			if (numSnowTreeIds == 0)
				break;

			type = snowTreeIds[util_rand() % numSnowTreeIds];
			break;
		}

		if (type != -1)
			mapgen_place_tree(type, pos->x, pos->y);
	}

	free(availablePositions);
	free(grassTreeIds);
	free(desertTreeIds);
	free(snowTreeIds);
}
Example #11
0
void mapgen_generate(mapgen_settings *settings)
{
	sint32 x, y, mapSize, floorTexture, wallTexture, waterLevel;
	rct_map_element *mapElement;

	util_srand((sint32)SDL_GetTicks());

	mapSize = settings->mapSize;
	floorTexture = settings->floor;
	wallTexture = settings->wall;
	waterLevel = settings->waterLevel;

	if (floorTexture == -1)
		floorTexture = BaseTerrain[util_rand() % countof(BaseTerrain)];

	if (wallTexture == -1) {
		// Base edge type on surface type
		switch (floorTexture) {
		case TERRAIN_DIRT:
			wallTexture = TERRAIN_EDGE_WOOD_RED;
			break;
		case TERRAIN_ICE:
			wallTexture = TERRAIN_EDGE_ICE;
			break;
		default:
			wallTexture = TERRAIN_EDGE_ROCK;
			break;
		}
	}

	map_clear_all_elements();

	// Initialise the base map
	map_init(mapSize);
	for (y = 1; y < mapSize - 1; y++) {
		for (x = 1; x < mapSize - 1; x++) {
			mapElement = map_get_surface_element_at(x, y);
			map_element_set_terrain(mapElement, floorTexture);
			map_element_set_terrain_edge(mapElement, wallTexture);
			mapElement->base_height = settings->height;
			mapElement->clearance_height = settings->height;
		}
	}

	// Create the temporary height map and initialise
	_heightSize = mapSize * 2;
	_height = (uint8*)malloc(_heightSize * _heightSize * sizeof(uint8));
	memset(_height, 0, _heightSize * _heightSize * sizeof(uint8));

	if (1) {
		mapgen_simplex(settings);
		mapgen_smooth_height(2 + (util_rand() % 6));
	} else {
		// Keep overwriting the map with rough cicular blobs of different sizes and heights.
		// This procedural method can produce intersecting contour like land and lakes.
		// Large blobs, general shape of map
		mapgen_blobs(6, _heightSize / 2, _heightSize * 4, 4, 16);
		// Medium blobs
		mapgen_blobs(12, _heightSize / 16, _heightSize / 8, 4, 18);
		// Small blobs, small hills and lakes
		mapgen_blobs(32, _heightSize / 32, _heightSize / 16, 4, 18);

		// Smooth the land so that their aren't cliffs round every blob.
		mapgen_smooth_height(2);
	}

	// Set the game map to the height map
	mapgen_set_height();
	free(_height);

	// Set the tile slopes so that there are no cliffs
	while (map_smooth(1, 1, mapSize - 1, mapSize - 1)) { }

	// Add the water
	mapgen_set_water_level(waterLevel);

	// Add sandy beaches
	sint32 beachTexture = floorTexture;
	if (settings->floor == -1 && floorTexture == TERRAIN_GRASS) {
		switch (util_rand() % 4) {
		case 0:
			beachTexture = TERRAIN_SAND;
			break;
		case 1:
			beachTexture = TERRAIN_SAND_LIGHT;
			break;
		}
	}
	for (y = 1; y < mapSize - 1; y++) {
		for (x = 1; x < mapSize - 1; x++) {
			mapElement = map_get_surface_element_at(x, y);

			if (mapElement->base_height < waterLevel + 6)
				map_element_set_terrain(mapElement, beachTexture);
		}
	}

	// Place the trees
	if (settings->trees != 0)
		mapgen_place_trees();

	map_reorganise_elements();
}
Example #12
0
/**
 * Return the absolute height of an element, given its (x,y) coordinates
 *
 *  rct2: 0x00662783
 */
int map_element_height(int x, int y)
{
	rct_map_element *mapElement;

	// Off the map
	if (x >= 8192 || y >= 8192)
		return 16;

	// Truncate subtile coordinates
	int x_tile = x & 0xFFFFFFE0;
	int y_tile = y & 0xFFFFFFE0;

	// Get the surface element for the tile
	mapElement = map_get_surface_element_at(x_tile / 32, y_tile / 32);

	uint32 height =
		((mapElement->properties.surface.terrain & MAP_ELEMENT_WATER_HEIGHT_MASK) << 20) |
		(mapElement->base_height << 3);

	uint32 slope = (mapElement->properties.surface.slope & MAP_ELEMENT_SLOPE_MASK);
	uint8 extra_height = (slope & 0x10) >> 4; // 0x10 is the 5th bit - sets slope to double height
	// Remove the extra height bit
	slope &= 0xF;

	uint8 quad, quad_extra; // which quadrant the element is in?
	                        // quad_extra is for extra height tiles

	uint8 xl, yl;	    // coordinates across this tile

	uint8 TILE_SIZE = 31;

	xl = x & 0x1f;
	yl = y & 0x1f;

	// Slope logic:
	// Each of the four bits in slope represents that corner being raised
	// slope == 15 (all four bits) is not used and slope == 0 is flat
	// If the extra_height bit is set, then the slope goes up two z-levels

	// We arbitrarily take the SW corner to be closest to the viewer

	// One corner up
	if ((slope == 1) || (slope == 2) || (slope == 4) || (slope == 8)) {
		switch (slope) {
		case 1:    // NE corner up
			quad = xl + yl - TILE_SIZE;
			break;
		case 2:    // SE corner up
			quad = xl - yl;
			break;
		case 4:   // SW corner up
			quad = TILE_SIZE - yl - xl;
			break;
		case 8:   // NW corner up
			quad = xl - yl;
			break;
		}
		// If the element is in the quadrant with the slope, raise its height
		if (quad > 0) {
			height += quad / 2;
		}
	}

	// One side up
	switch (slope) {
	case 3:   // E side up
		height += xl / 2;
		break;
	case 6:   // S side up
		height += (TILE_SIZE - yl) / 2;
		break;
	case 9:    // N side up
		height += yl / 2;
		height++;
		break;
	case 12:  // W side up
		height += (TILE_SIZE - xl) / 2;
		break;
	}

	// One corner down
	if ((slope == 7) || (slope == 11) || (slope == 13) || (slope == 14)) {
		switch (slope) {
		case 7:   // NW corner down
			quad_extra = xl + TILE_SIZE - yl;
			quad = xl - yl;
			break;
		case 11:  // SW corner down
			quad_extra = xl + yl;
			quad = xl + yl - TILE_SIZE;
			break;
		case 13:  // SE corner down
			quad_extra = TILE_SIZE - xl + yl;
			quad = xl - yl;
			break;
		case 14:  // NE corner down
			quad_extra = (TILE_SIZE - xl) + (TILE_SIZE - yl);
			quad = TILE_SIZE - yl - xl;
			break;
		}

		if (extra_height) {
			height += quad_extra / 2;
			height++;
			return height;
		}
		// This tile is essentially at the next height level
		height += 0x10;
		// so we move *down* the slope
		if (quad < 0) {
			height += quad / 2;
			height += 0xFF00;
		}
	}

	// Valleys
	if ((slope == 5) || (slope == 10)) {
		switch (slope) {
		case 5:  // NW-SE valley
			if (xl + yl <= TILE_SIZE + 1) {
				return height;
			}
			quad = TILE_SIZE - xl - yl;
			break;
		case 10: // NE-SW valley
			quad = xl - yl;
			break;
		}
		if (quad > 0) {
			height += quad / 2;
		}
	}

	return height;
}
Example #13
0
/**
 * Not perfect, this still leaves some particular tiles unsmoothed.
 */
sint32 map_smooth(sint32 l, sint32 t, sint32 r, sint32 b)
{
    sint32 i, x, y, highest, count, cornerHeights[4], doubleCorner, raisedLand = 0;
    rct_map_element *mapElement, *mapElement2;
    for (y = t; y < b; y++) {
        for (x = l; x < r; x++) {
            mapElement = map_get_surface_element_at(x, y);
            mapElement->properties.surface.slope &= ~0x1F;

            // Raise to edge height - 2
            highest = mapElement->base_height;
            highest = max(highest, map_get_surface_element_at(x - 1, y + 0)->base_height);
            highest = max(highest, map_get_surface_element_at(x + 1, y + 0)->base_height);
            highest = max(highest, map_get_surface_element_at(x + 0, y - 1)->base_height);
            highest = max(highest, map_get_surface_element_at(x + 0, y + 1)->base_height);
            if (mapElement->base_height < highest - 2) {
                raisedLand = 1;
                mapElement->base_height = mapElement->clearance_height = highest - 2;
            }

            // Check corners
            doubleCorner = -1;
            cornerHeights[0] = map_get_surface_element_at(x - 1, y - 1)->base_height;
            cornerHeights[1] = map_get_surface_element_at(x + 1, y - 1)->base_height;
            cornerHeights[2] = map_get_surface_element_at(x + 1, y + 1)->base_height;
            cornerHeights[3] = map_get_surface_element_at(x - 1, y + 1)->base_height;
            highest = mapElement->base_height;
            for (i = 0; i < 4; i++)
                highest = max(highest, cornerHeights[i]);

            if (highest >= mapElement->base_height + 4) {
                count = 0;
                sint32 canCompensate = 1;
                for (i = 0; i < 4; i++)
                    if (cornerHeights[i] == highest){
                        count++;

                        // Check if surrounding corners aren't too high. The current tile
                        // can't compensate for all the height differences anymore if it has
                        // the extra height slope.
                        sint32 highestOnLowestSide;
                        switch (i){
                        default:
                        case 0:
                            highestOnLowestSide = max(
                                map_get_surface_element_at(x + 1, y)->base_height,
                                map_get_surface_element_at(x, y + 1)->base_height);
                            break;
                        case 1:
                            highestOnLowestSide = max(
                                map_get_surface_element_at(x - 1, y)->base_height,
                                map_get_surface_element_at(x, y + 1)->base_height);
                            break;
                        case 2:
                            highestOnLowestSide = max(
                                map_get_surface_element_at(x - 1, y)->base_height,
                                map_get_surface_element_at(x, y - 1)->base_height);
                            break;
                        case 3:
                            highestOnLowestSide = max(
                                map_get_surface_element_at(x + 1, y)->base_height,
                                map_get_surface_element_at(x, y - 1)->base_height);
                            break;
                        }

                        if (highestOnLowestSide > mapElement->base_height){
                            mapElement->base_height = mapElement->clearance_height = highestOnLowestSide;
                            raisedLand = 1;
                            canCompensate = 0;
                        }
                    }

                if (count == 1 && canCompensate) {
                    if (mapElement->base_height < highest - 4) {
                        mapElement->base_height = mapElement->clearance_height = highest - 4;
                        raisedLand = 1;
                    }
                    if (cornerHeights[0] == highest && cornerHeights[2] <= cornerHeights[0] - 4)
                        doubleCorner = 0;
                    else if (cornerHeights[1] == highest && cornerHeights[3] <= cornerHeights[1] - 4)
                        doubleCorner = 1;
                    else if (cornerHeights[2] == highest && cornerHeights[0] <= cornerHeights[2] - 4)
                        doubleCorner = 2;
                    else if (cornerHeights[3] == highest && cornerHeights[1] <= cornerHeights[3] - 4)
                        doubleCorner = 3;
                } else {
                    if (mapElement->base_height < highest - 2) {
                        mapElement->base_height = mapElement->clearance_height = highest - 2;
                        raisedLand = 1;
                    }
                }
            }

            if (doubleCorner != -1) {
                mapElement->properties.surface.slope |= 16;
                switch (doubleCorner) {
                case 0:
                    mapElement->properties.surface.slope |= 2 | 4 | 8;
                    break;
                case 1:
                    mapElement->properties.surface.slope |= 1 | 2 | 4;
                    break;
                case 2:
                    mapElement->properties.surface.slope |= 1 | 2 | 8;
                    break;
                case 3:
                    mapElement->properties.surface.slope |= 1 | 4 | 8;
                    break;
                }
            } else {
                // Corners
                mapElement2 = map_get_surface_element_at(x + 1, y + 1);
                if (mapElement2->base_height > mapElement->base_height)
                    mapElement->properties.surface.slope |= 1;

                mapElement2 = map_get_surface_element_at(x - 1, y + 1);
                if (mapElement2->base_height > mapElement->base_height)
                    mapElement->properties.surface.slope |= 8;

                mapElement2 = map_get_surface_element_at(x + 1, y - 1);
                if (mapElement2->base_height > mapElement->base_height)
                    mapElement->properties.surface.slope |= 2;

                mapElement2 = map_get_surface_element_at(x - 1, y - 1);
                if (mapElement2->base_height > mapElement->base_height)
                    mapElement->properties.surface.slope |= 4;

                // Sides
                mapElement2 = map_get_surface_element_at(x + 1, y + 0);
                if (mapElement2->base_height > mapElement->base_height)
                    mapElement->properties.surface.slope |= 1 | 2;

                mapElement2 = map_get_surface_element_at(x - 1, y + 0);
                if (mapElement2->base_height > mapElement->base_height)
                    mapElement->properties.surface.slope |= 4 | 8;

                mapElement2 = map_get_surface_element_at(x + 0, y - 1);
                if (mapElement2->base_height > mapElement->base_height)
                    mapElement->properties.surface.slope |= 2 | 4;

                mapElement2 = map_get_surface_element_at(x + 0, y + 1);
                if (mapElement2->base_height > mapElement->base_height)
                    mapElement->properties.surface.slope |= 1 | 8;

                // Raise
                if (mapElement->properties.surface.slope == (1 | 2 | 4 | 8)) {
                    mapElement->properties.surface.slope &= ~0x1F;
                    mapElement->base_height = mapElement->clearance_height += 2;
                }
            }
        }
    }

    return raisedLand;
}
Example #14
0
/**
 * There are non-smoothed tiles with this version, but diagonal land blocks end up being wavy.
 */
static sint32 map_smooth_wavy(sint32 l, sint32 t, sint32 r, sint32 b)
{
    sint32 i, x, y, highest, count, cornerHeights[4], doubleCorner, raisedLand = 0;
    rct_map_element *mapElement;
    for (y = t; y < b; y++) {
        for (x = l; x < r; x++) {
            mapElement = map_get_surface_element_at(x, y);
            mapElement->properties.surface.slope &= ~0x1F;

            // Raise to edge height - 2
            highest = mapElement->base_height;
            highest = max(highest, map_get_surface_element_at(x - 1, y + 0)->base_height);
            highest = max(highest, map_get_surface_element_at(x + 1, y + 0)->base_height);
            highest = max(highest, map_get_surface_element_at(x + 0, y - 1)->base_height);
            highest = max(highest, map_get_surface_element_at(x + 0, y + 1)->base_height);
            if (mapElement->base_height < highest - 2) {
                raisedLand = 1;
                mapElement->base_height = mapElement->clearance_height = highest - 2;
            }

            // Check corners
            doubleCorner = -1;
            cornerHeights[0] = max(map_get_corner_height(x - 1, y - 1, 0), max(map_get_corner_height(x + 1, y + 0, 1), map_get_corner_height(x + 0, y + 1, 2)));
            cornerHeights[1] = max(map_get_corner_height(x + 1, y - 1, 1), max(map_get_corner_height(x - 1, y + 0, 0), map_get_corner_height(x + 0, y + 1, 3)));
            cornerHeights[2] = max(map_get_corner_height(x + 1, y + 1, 3), max(map_get_corner_height(x + 1, y + 0, 3), map_get_corner_height(x + 0, y - 1, 0)));
            cornerHeights[3] = max(map_get_corner_height(x - 1, y + 1, 2), max(map_get_corner_height(x - 1, y + 0, 2), map_get_corner_height(x + 0, y - 1, 1)));
            highest = mapElement->base_height;
            for (i = 0; i < 4; i++)
                highest = max(highest, cornerHeights[i]);

            if (highest >= mapElement->base_height + 4) {
                count = 0;
                for (i = 0; i < 4; i++)
                    if (cornerHeights[i] == highest)
                        count++;

                if (count == 1) {
                    if (mapElement->base_height < highest - 4) {
                        mapElement->base_height = mapElement->clearance_height = highest - 4;
                        raisedLand = 1;
                    }
                    if (cornerHeights[0] == highest && cornerHeights[2] <= cornerHeights[0] - 4)
                        doubleCorner = 0;
                    else if (cornerHeights[1] == highest && cornerHeights[3] <= cornerHeights[1] - 4)
                        doubleCorner = 1;
                    else if (cornerHeights[2] == highest && cornerHeights[0] <= cornerHeights[2] - 4)
                        doubleCorner = 2;
                    else if (cornerHeights[3] == highest && cornerHeights[1] <= cornerHeights[3] - 4)
                        doubleCorner = 3;
                } else {
                    if (mapElement->base_height < highest - 2) {
                        mapElement->base_height = mapElement->clearance_height = highest - 2;
                        raisedLand = 1;
                    }
                }
            }

            if (doubleCorner != -1) {
                mapElement->properties.surface.slope |= 16;
                switch (doubleCorner) {
                case 0:
                    mapElement->properties.surface.slope |= 2 | 4 | 8;
                    break;
                case 1:
                    mapElement->properties.surface.slope |= 1 | 2 | 4;
                    break;
                case 2:
                    mapElement->properties.surface.slope |= 1 | 2 | 8;
                    break;
                case 3:
                    mapElement->properties.surface.slope |= 1 | 4 | 8;
                    break;
                }
            } else {
                // Corners
                if (
                    map_get_corner_height(x + 1, y + 1, 3) > mapElement->base_height ||
                    map_get_corner_height(x + 1, y + 0, 1) > mapElement->base_height ||
                    map_get_corner_height(x + 0, y + 1, 2) > mapElement->base_height
                )
                    mapElement->properties.surface.slope |= 1;

                if (
                    map_get_corner_height(x - 1, y + 1, 2) > mapElement->base_height ||
                    map_get_corner_height(x - 1, y + 0, 0) > mapElement->base_height ||
                    map_get_corner_height(x + 0, y + 1, 3) > mapElement->base_height
                )
                    mapElement->properties.surface.slope |= 8;

                if (
                    map_get_corner_height(x + 1, y - 1, 1) > mapElement->base_height ||
                    map_get_corner_height(x + 1, y + 0, 3) > mapElement->base_height ||
                    map_get_corner_height(x + 0, y - 1, 0) > mapElement->base_height
                )
                    mapElement->properties.surface.slope |= 2;

                if (
                    map_get_corner_height(x - 1, y - 1, 0) > mapElement->base_height ||
                    map_get_corner_height(x - 1, y + 0, 2) > mapElement->base_height ||
                    map_get_corner_height(x + 0, y - 1, 1) > mapElement->base_height
                )
                    mapElement->properties.surface.slope |= 4;

                // Raise
                if (mapElement->properties.surface.slope == (1 | 2 | 4 | 8)) {
                    mapElement->properties.surface.slope &= ~0x1F;
                    mapElement->base_height = mapElement->clearance_height += 2;
                }
            }
        }
    }

    return raisedLand;
}
Example #15
0
void mapgen_generate_from_heightmap(mapgen_settings *settings)
{
    openrct2_assert(_heightMapData.width == _heightMapData.height, "Invalid height map size");
    openrct2_assert(_heightMapData.mono_bitmap != NULL, "No height map loaded");
    openrct2_assert(settings->simplex_high != settings->simplex_low, "Low and high setting cannot be the same");

    // Make a copy of the original height map that we can edit
    uint8 *dest = (uint8*)malloc(_heightMapData.width * _heightMapData.height);
    memcpy(dest, _heightMapData.mono_bitmap, _heightMapData.width * _heightMapData.width);

    map_init(_heightMapData.width + 2); // + 2 for the black tiles around the map

    if (settings->smooth_height_map)
    {
        mapgen_smooth_heightmap(dest, settings->smooth_strength);
    }

    uint8 maxValue = 255;
    uint8 minValue = 0;

    if (settings->normalize_height)
    {
        // Get highest and lowest pixel value
        maxValue = 0;
        minValue = 0xff;
        for (uint32 y = 0; y < _heightMapData.height; y++)
        {
            for (uint32 x = 0; x < _heightMapData.width; x++)
            {
                uint8 value = dest[x + y * _heightMapData.width];
                maxValue = max(maxValue, value);
                minValue = min(minValue, value);
            }
        }

        if (minValue == maxValue)
        {
            window_error_open(STR_HEIGHT_MAP_ERROR, STR_ERROR_CANNOT_NORMALIZE);
            free(dest);
            return;
        }
    }

    openrct2_assert(maxValue > minValue, "Input range is invalid");
    openrct2_assert(settings->simplex_high > settings->simplex_low, "Output range is invalid");

    const uint8 rangeIn = maxValue - minValue;
    const uint8 rangeOut = settings->simplex_high - settings->simplex_low;

    for (uint32 y = 0; y < _heightMapData.height; y++)
    {
        for (uint32 x = 0; x < _heightMapData.width; x++)
        {
            // The x and y axis are flipped in the world, so this uses y for x and x for y.
            rct_map_element *const surfaceElement = map_get_surface_element_at(y + 1, x + 1);

            // Read value from bitmap, and convert its range
            uint8 value = dest[x + y * _heightMapData.width];
            value = (uint8)((float)(value - minValue) / rangeIn * rangeOut) + settings->simplex_low;
            surfaceElement->base_height = value;

            // Floor to even number
            surfaceElement->base_height /= 2;
            surfaceElement->base_height *= 2;
            surfaceElement->clearance_height = surfaceElement->base_height;

            // Set water level
            if (surfaceElement->base_height < settings->water_level)
            {
                surfaceElement->properties.surface.terrain |= settings->water_level / 2;
            }
        }
    }

    // Smooth map
    if (settings->smooth)
    {
        // Keep smoothing the entire map until no tiles are changed anymore
        while (true)
        {
            uint32 numTilesChanged = 0;
            for (uint32 y = 1; y <= _heightMapData.height; y++)
            {
                for (uint32 x = 1; x <= _heightMapData.width; x++)
                {
                    numTilesChanged += tile_smooth(x, y);
                }
            }

            if (numTilesChanged == 0)
                break;
        }
    }

    // Clean up
    free(dest);
}
Example #16
0
/**
 * Raises the corners based on the height offset of neighbour tiles.
 * This does not change the base height, unless all corners have been raised.
 * @returns 0 if no edits were made, 1 otherwise
 */
sint32 tile_smooth(sint32 x, sint32 y)
{
    rct_map_element *const surfaceElement = map_get_surface_element_at(x, y);

    // +-----+-----+-----+
    // | NW  |  N  | NE  |
    // |  2  |  1  |  0  |
    // +-----+-----+-----+
    // |  W  |  _  |  E  |
    // |  4  |     |  3  |
    // +-----+-----+-----+
    // | SW  |  S  | SE  |
    // |  7  |  6  |  5  |
    // +-----+-----+-----+
    union
    {
        sint32 baseheight[8];
        struct { sint32 NE, N, NW, E, W, SE, S, SW; };
    } neighbourHeightOffset = { 0 };

    // Find the neighbour base heights
    for (sint32 index = 0, y_offset = -1; y_offset <= 1; y_offset++)
    {
        for (sint32 x_offset = -1; x_offset <= 1; x_offset++)
        {
            // Skip self
            if (y_offset == 0 && x_offset == 0)
                continue;

            // Get neighbour height. If the element is not valid (outside of map) assume the same height
            rct_map_element *neighbour_element = map_get_surface_element_at(x + x_offset, y + y_offset);
            neighbourHeightOffset.baseheight[index] = neighbour_element ? neighbour_element->base_height : surfaceElement->base_height;

            // Make the height relative to the current surface element
            neighbourHeightOffset.baseheight[index] -= surfaceElement->base_height;

            index++;
        }
    }

    // Count number from the three tiles that is currently higher
    sint8 thresholdNW = clamp(neighbourHeightOffset.W, 0, 1) + clamp(neighbourHeightOffset.NW, 0, 1) + clamp(neighbourHeightOffset.N, 0, 1);
    sint8 thresholdNE = clamp(neighbourHeightOffset.N, 0, 1) + clamp(neighbourHeightOffset.NE, 0, 1) + clamp(neighbourHeightOffset.E, 0, 1);
    sint8 thresholdSE = clamp(neighbourHeightOffset.E, 0, 1) + clamp(neighbourHeightOffset.SE, 0, 1) + clamp(neighbourHeightOffset.S, 0, 1);
    sint8 thresholdSW = clamp(neighbourHeightOffset.S, 0, 1) + clamp(neighbourHeightOffset.SW, 0, 1) + clamp(neighbourHeightOffset.W, 0, 1);

    uint8 slope = 0;
    slope |= (thresholdNW >= 1) ? (1 << 1) : 0;
    slope |= (thresholdNE >= 1) ? (1 << 2) : 0;
    slope |= (thresholdSE >= 1) ? (1 << 3) : 0;
    slope |= (thresholdSW >= 1) ? (1 << 0) : 0;

    // Set diagonal when three corners have been raised, and the middle one can be raised one more
    if ((slope == 0b0111 && neighbourHeightOffset.NW >= 4) || (slope == 0b1011 && neighbourHeightOffset.SW >= 4) ||
        (slope == 0b1101 && neighbourHeightOffset.SE >= 4) || (slope == 0b1110 && neighbourHeightOffset.NE >= 4))
    {
        slope |= 1 << 4;
    }

    // Check if the calculated slope is the same already
    uint8 currentSlope = surfaceElement->properties.surface.slope & 0x1F;
    if (currentSlope == slope)
    {
        return 0;
    }

    // Remove old slope value
    surfaceElement->properties.surface.slope &= ~0x1F;
    if ((slope & 0xF) == 0xF)
    {
        // All corners are raised, raise the entire tile instead.
        surfaceElement->base_height = (surfaceElement->clearance_height += 2);
        uint8 waterHeight = map_get_water_height(surfaceElement) * 2;
        if (waterHeight <= surfaceElement->base_height)
        {
            surfaceElement->properties.surface.terrain &= ~MAP_ELEMENT_WATER_HEIGHT_MASK;
        }
    }
    else
    {
        // Apply the slope to this tile
        surfaceElement->properties.surface.slope |= slope;

        // Set correct clearance height
        if (slope & 0x10)
            surfaceElement->clearance_height = surfaceElement->base_height + 4;
        else if (slope & 0x0F)
            surfaceElement->clearance_height = surfaceElement->base_height + 2;
    }

    return 1;
}
Example #17
0
static money32 ParkEntrancePlace(sint32 flags, sint16 x, sint16 y, uint8 z, uint8 direction) 
{
    if (!(gScreenFlags & SCREEN_FLAGS_EDITOR) && !gCheatsSandboxMode) 
    {
        return MONEY32_UNDEFINED;
    }

    gCommandExpenditureType = RCT_EXPENDITURE_TYPE_LAND_PURCHASE;

    gCommandPosition.x = x;
    gCommandPosition.y = y;
    gCommandPosition.z = z * 16;

    if (!map_check_free_elements_and_reorganise(3)) 
    {
        return MONEY32_UNDEFINED;
    }

    if (x <= 32 || y <= 32 || x >= (gMapSizeUnits - 32) || y >= (gMapSizeUnits - 32)) 
    {
        gGameCommandErrorText = STR_TOO_CLOSE_TO_EDGE_OF_MAP;
        return MONEY32_UNDEFINED;
    }

    sint8 entranceNum = -1;
    for (uint8 i = 0; i < MAX_PARK_ENTRANCES; ++i) 
    {
        if (gParkEntrances[i].x == MAP_LOCATION_NULL) 
        {
            entranceNum = i;
            break;
        }
    }

    if (entranceNum == -1) 
    {
        gGameCommandErrorText = STR_ERR_TOO_MANY_PARK_ENTRANCES;
        return MONEY32_UNDEFINED;
    }

    if (flags & GAME_COMMAND_FLAG_APPLY) 
    {
        gParkEntrances[entranceNum].x = x;
        gParkEntrances[entranceNum].y = y;
        gParkEntrances[entranceNum].z = z  * 16;
        gParkEntrances[entranceNum].direction = direction;
    }

    sint8 zLow = z * 2;
    sint8 zHigh = zLow + 12;

    for (uint8 index = 0; index < 3; index++) 
    {
        if (index == 1)
        {
            x += TileDirectionDelta[(direction - 1) & 0x3].x;
            y += TileDirectionDelta[(direction - 1) & 0x3].y;
        }
        else if (index == 2) 
        {
            x += TileDirectionDelta[(direction + 1) & 0x3].x * 2;
            y += TileDirectionDelta[(direction + 1) & 0x3].y * 2;
        }

        if (!gCheatsDisableClearanceChecks) 
        {
            if (!map_can_construct_at(x, y, zLow, zHigh, 0xF)) 
            {
                return MONEY32_UNDEFINED;
            }
        }

        // Check that entrance element does not already exist at this location
        rct_map_element* entranceElement = map_get_park_entrance_element_at(x, y, zLow, false);
        if (entranceElement != NULL)
        {
            return MONEY32_UNDEFINED;
        }

        if (!(flags & GAME_COMMAND_FLAG_APPLY)) 
        {
            continue;
        }

        if (!(flags & GAME_COMMAND_FLAG_GHOST)) 
        {
            rct_map_element* surfaceElement = map_get_surface_element_at(x / 32, y / 32);
            surfaceElement->properties.surface.ownership = 0;
        }

        rct_map_element* newElement = map_element_insert(x / 32, y / 32, zLow, 0xF);
        assert(newElement != NULL);
        newElement->clearance_height = zHigh;

        if (flags & GAME_COMMAND_FLAG_GHOST) 
        {
            newElement->flags |= MAP_ELEMENT_FLAG_GHOST;
        }

        newElement->type = MAP_ELEMENT_TYPE_ENTRANCE;
        newElement->type |= direction;
        newElement->properties.entrance.index = index;
        newElement->properties.entrance.type = ENTRANCE_TYPE_PARK_ENTRANCE;
        newElement->properties.entrance.path_type = gFootpathSelectedId;

        if (!(flags & GAME_COMMAND_FLAG_GHOST)) 
        {
            footpath_connect_edges(x, y, newElement, 1);
        }

        update_park_fences(x, y);
        update_park_fences(x - 32, y);
        update_park_fences(x + 32, y);
        update_park_fences(x, y - 32);
        update_park_fences(x, y + 32);

        map_invalidate_tile(x, y, newElement->base_height * 8, newElement->clearance_height * 8);

        if (index == 0) 
        {
            map_animation_create(MAP_ANIMATION_TYPE_PARK_ENTRANCE, x, y, zLow);
        }
    }
    return 0;
}