void* Intent::GetPointerExtra(uint32_t key) const { if (_Data.count(key) == 0) { return nullptr; } auto data = _Data.at(key); openrct2_assert(data.type == IntentData::DT_POINTER, "Actual type doesn't match requested type"); return (void*)data.pointerVal; }
uint32_t Intent::GetUIntExtra(uint32_t key) const { if (_Data.count(key) == 0) { return 0; } auto data = _Data.at(key); openrct2_assert(data.type == IntentData::DT_INT, "Actual type doesn't match requested type"); return data.intVal.unsignedInt; }
close_callback Intent::GetCloseCallbackExtra(uint32_t key) const { if (_Data.count(key) == 0) { return nullptr; } auto data = _Data.at(key); openrct2_assert(data.type == IntentData::DT_CLOSE_CALLBACK, "Actual type doesn't match requested type"); return data.closeCallbackVal; }
std::string Intent::GetStringExtra(uint32_t key) const { if (_Data.count(key) == 0) { return std::string{}; } auto data = _Data.at(key); openrct2_assert(data.type == IntentData::DT_STRING, "Actual type doesn't match requested type"); return data.stringVal; }
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); }
bool mapgen_load_heightmap(const utf8 *path) { const char* extension = path_get_extension(path); uint8 *pixels; size_t pitch; uint32 numChannels; uint32 width, height; if (strcicmp(extension, ".png") == 0) { if (!image_io_png_read(&pixels, &width, &height, path)) { log_warning("Error reading PNG"); window_error_open(STR_HEIGHT_MAP_ERROR, STR_ERROR_READING_PNG); return false; } numChannels = 4; pitch = width * numChannels; } else if (strcicmp(extension, ".bmp") == 0) { if (!context_read_bmp((void *)&pixels, &width, &height, path)) { // ReadBMP contains window_error_open calls return false; } numChannels = 4; pitch = width * numChannels; } else { openrct2_assert(false, "A file with an invalid file extension was selected."); return false; } if (width != height) { window_error_open(STR_HEIGHT_MAP_ERROR, STR_ERROR_WIDTH_AND_HEIGHT_DO_NOT_MATCH); free(pixels); return false; } if (width > MAXIMUM_MAP_SIZE_PRACTICAL) { window_error_open(STR_HEIGHT_MAP_ERROR, STR_ERROR_HEIHGT_MAP_TOO_BIG); width = height = min(height, MAXIMUM_MAP_SIZE_PRACTICAL); } // Allocate memory for the height map values, one byte pixel free(_heightMapData.mono_bitmap); _heightMapData.mono_bitmap = (uint8*)malloc(width * height); _heightMapData.width = width; _heightMapData.height = height; // Copy average RGB value to mono bitmap for (uint32 x = 0; x < _heightMapData.width; x++) { for (uint32 y = 0; y < _heightMapData.height; y++) { const uint8 red = pixels[x * numChannels + y * pitch]; const uint8 green = pixels[x * numChannels + y * pitch + 1]; const uint8 blue = pixels[x * numChannels + y * pitch + 2]; _heightMapData.mono_bitmap[x + y * _heightMapData.width] = (red + green + blue) / 3; } } free(pixels); return true; }
/** * * rct2: 0x006848D4 */ void research_finish_item(rct_research_item* researchItem) { gResearchLastItem = *researchItem; research_invalidate_related_windows(); if (researchItem->type == RESEARCH_ENTRY_TYPE_RIDE) { // Ride uint32_t base_ride_type = researchItem->baseRideType; int32_t rideEntryIndex = researchItem->entryIndex; rct_ride_entry* rideEntry = get_ride_entry(rideEntryIndex); if (rideEntry != nullptr && base_ride_type != RIDE_TYPE_NULL) { bool ride_group_was_invented_before = false; bool ride_type_was_invented_before = ride_type_is_invented(base_ride_type); rct_string_id availabilityString; // Determine if the ride group this entry belongs to was invented before. if (RideGroupManager::RideTypeHasRideGroups(base_ride_type)) { const RideGroup* rideGroup = RideGroupManager::GetRideGroup(base_ride_type, rideEntry); if (rideGroup->IsInvented()) { ride_group_was_invented_before = true; } } ride_type_set_invented(base_ride_type); openrct2_assert( base_ride_type < std::size(RideTypePossibleTrackConfigurations), "Invalid base_ride_type = %d", base_ride_type); ride_entry_set_invented(rideEntryIndex); bool seenRideEntry[MAX_RIDE_OBJECTS]{}; rct_research_item* researchItem2 = gResearchItems; for (; researchItem2->rawValue != RESEARCHED_ITEMS_END; researchItem2++) { if (researchItem2->rawValue != RESEARCHED_ITEMS_SEPARATOR && researchItem2->type == RESEARCH_ENTRY_TYPE_RIDE) { uint8_t index = researchItem2->entryIndex; seenRideEntry[index] = true; } } // RCT2 made non-separated vehicles available at once, by removing all but one from research. // To ensure old files keep working, look for ride entries not in research, and make them available as well. for (int32_t i = 0; i < MAX_RIDE_OBJECTS; i++) { if (!seenRideEntry[i]) { rct_ride_entry* rideEntry2 = get_ride_entry(i); if (rideEntry2 != nullptr) { for (uint8_t j = 0; j < MAX_RIDE_TYPES_PER_RIDE_ENTRY; j++) { if (rideEntry2->ride_type[j] == base_ride_type) { ride_entry_set_invented(i); break; } } } } } // If a vehicle should be listed separately (maze, mini golf, flat rides, shops) if (RideGroupManager::RideTypeIsIndependent(base_ride_type)) { availabilityString = STR_NEWS_ITEM_RESEARCH_NEW_RIDE_AVAILABLE; set_format_arg(0, rct_string_id, rideEntry->naming.name); } // If a vehicle is the first to be invented for its ride group, show the ride group name. else if ( !ride_type_was_invented_before || (RideGroupManager::RideTypeHasRideGroups(base_ride_type) && !ride_group_was_invented_before)) { rct_ride_name naming = get_ride_naming(base_ride_type, rideEntry); availabilityString = STR_NEWS_ITEM_RESEARCH_NEW_RIDE_AVAILABLE; set_format_arg(0, rct_string_id, naming.name); } // If the vehicle should not be listed separately and it isn't the first to be invented for its ride group, // report it as a new vehicle for the existing ride group. else { availabilityString = STR_NEWS_ITEM_RESEARCH_NEW_VEHICLE_AVAILABLE; rct_ride_name baseRideNaming = get_ride_naming(base_ride_type, rideEntry); set_format_arg(0, rct_string_id, baseRideNaming.name); set_format_arg(2, rct_string_id, rideEntry->naming.name); } if (!gSilentResearch) { if (gConfigNotifications.ride_researched) { news_item_add_to_queue(NEWS_ITEM_RESEARCH, availabilityString, researchItem->rawValue); } } research_invalidate_related_windows(); } } else { // Scenery rct_scenery_group_entry* sceneryGroupEntry = get_scenery_group_entry(researchItem->entryIndex); if (sceneryGroupEntry != nullptr) { scenery_group_set_invented(researchItem->entryIndex); set_format_arg(0, rct_string_id, sceneryGroupEntry->name); if (!gSilentResearch) { if (gConfigNotifications.ride_researched) { news_item_add_to_queue( NEWS_ITEM_RESEARCH, STR_NEWS_ITEM_RESEARCH_NEW_SCENERY_SET_AVAILABLE, researchItem->rawValue); } } research_invalidate_related_windows(); init_scenery(); } } }