Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- /**
- * Helper for CircularTileSearch to found a town on or near a given tile.
- * @param tile The tile to try founding the town upon.
- * @param user_data The ExternalTownData to attempt to found.
- * @return True if the town was founded successfully.
- */
- static bool TryFoundTownNearby(TileIndex tile, void *user_data)
- {
- ExternalTownData &town = *static_cast<ExternalTownData *>(user_data);
- std::tuple<CommandCost, Money, TownID> result = Command<CMD_FOUND_TOWN>::Do(DC_EXEC, tile, TSZ_SMALL, town.is_city, _settings_game.economy.town_layout, false, 0, town.name);
- TownID id = std::get<TownID>(result);
- /* Check if the command failed. */
- if (id == INVALID_TOWN) return false;
- /* The command succeeded, send the ID back through user_data. */
- town.town_id = id;
- return true;
- }
- /**
- * Load town data from _file_to_saveload and place its contents into the world.
- * @return True if the JSON file is valid and has north and south extents, otherwise false.
- */
- void LoadTownData()
- {
- /* Load the JSON file as a string initially. We'll parse it soon. */
- size_t filesize;
- FILE *f = FioFOpenFile(_file_to_saveload.name, "rb", HEIGHTMAP_DIR, &filesize);
- if (f == nullptr) {
- ShowErrorMessage(STR_TOWN_DATA_ERROR_LOAD_FAILED, STR_TOWN_DATA_ERROR_JSON_FORMATTED_INCORRECTLY, WL_ERROR);
- return;
- }
- std::string text(filesize, '\0');
- size_t len = fread(text.data(), filesize, 1, f);
- FioFCloseFile(f);
- if (len != 1) {
- ShowErrorMessage(STR_TOWN_DATA_ERROR_LOAD_FAILED, STR_TOWN_DATA_ERROR_JSON_FORMATTED_INCORRECTLY, WL_ERROR);
- return;
- }
- /* Now parse the JSON. */
- nlohmann::json town_data;
- try {
- town_data = nlohmann::json::parse(text);
- } catch (nlohmann::json::exception &) {
- ShowErrorMessage(STR_TOWN_DATA_ERROR_LOAD_FAILED, STR_TOWN_DATA_ERROR_JSON_FORMATTED_INCORRECTLY, WL_ERROR);
- return;
- }
- /* Check for JSON formatting errors with the array of towns. */
- if (!town_data.is_array()) {
- ShowErrorMessage(STR_TOWN_DATA_ERROR_LOAD_FAILED, STR_TOWN_DATA_ERROR_JSON_FORMATTED_INCORRECTLY, WL_ERROR);
- return;
- }
- std::vector<std::pair<Town *, uint> > towns;
- uint failed_towns = 0;
- /* Iterate through towns and attempt to found them. */
- for (auto &feature : town_data) {
- ExternalTownData town;
- /* Ensure JSON is formatted properly. */
- if (!feature.is_object()) {
- ShowErrorMessage(STR_TOWN_DATA_ERROR_LOAD_FAILED, STR_TOWN_DATA_ERROR_JSON_FORMATTED_INCORRECTLY, WL_ERROR);
- return;
- }
- /* Check to ensure all fields exist and are of the correct type.
- * If the town name is formatted wrong, all we can do is give a general warning. */
- if (!feature.contains("name") || !feature.at("name").is_string()) {
- ShowErrorMessage(STR_TOWN_DATA_ERROR_LOAD_FAILED, STR_TOWN_DATA_ERROR_JSON_FORMATTED_INCORRECTLY, WL_ERROR);
- return;
- }
- /* If other fields are formatted wrong, we can actually inform the player which town is the problem. */
- if (!feature.contains("population") || !feature.at("population").is_number() ||
- !feature.contains("city") || !feature.at("city").is_boolean() ||
- !feature.contains("x") || !feature.at("x").is_number() ||
- !feature.contains("y") || !feature.at("y").is_number()) {
- feature.at("name").get_to(town.name);
- SetDParamStr(0, town.name);
- ShowErrorMessage(STR_TOWN_DATA_ERROR_LOAD_FAILED, STR_TOWN_DATA_ERROR_TOWN_FORMATTED_INCORRECTLY, WL_ERROR);
- return;
- }
- /* Set town properties. */
- feature.at("name").get_to(town.name);
- feature.at("population").get_to(town.population);
- feature.at("city").get_to(town.is_city);
- /* Set town coordinates. */
- feature.at("x").get_to(town.x_proportion);
- feature.at("y").get_to(town.y_proportion);
- /* Check for improper coordinates and warn the player. */
- if (town.x_proportion <= 0 || town.y_proportion <= 0 || town.x_proportion >= 1 || town.y_proportion >= 1) {
- SetDParamStr(0, town.name);
- ShowErrorMessage(STR_TOWN_DATA_ERROR_LOAD_FAILED, STR_TOWN_DATA_ERROR_BAD_COORDINATE, WL_ERROR);
- return;
- }
- /* Find the target tile for the town. */
- TileIndex tile = TileXY(town.x_proportion * Map::MaxX(), town.y_proportion * Map::MaxY());
- /* Try founding on the target tile, and if that doesn't work, find the nearest suitable tile up to 16 tiles away.
- * The target might be on water, blocked somehow, or on a steep slope that can't be terraformed by the founding command. */
- TileIndex search_tile = tile;
- bool success = CircularTileSearch(&search_tile, 16, 0, 0, TryFoundTownNearby, &town);
- /* If we still fail to found the town, we'll create a sign at the intended location and tell the player how many towns we failed to create in an error message.
- * This allows the player to diagnose a heightmap misalignment, if towns end up in the sea, or place towns manually, if in rough terrain. */
- if (!success) {
- Command<CMD_PLACE_SIGN>::Post(tile, town.name);
- failed_towns++;
- continue;
- }
- towns.emplace_back(std::make_pair(Town::Get(town.town_id), town.population));
- }
- /* If we couldn't found a town (or multiple), display a message to the player with the number of failed towns. */
- if (failed_towns > 0) {
- SetDParam(0, failed_towns);
- ShowErrorMessage(STR_TOWN_DATA_ERROR_FAILED_TO_FOUND_TOWN, INVALID_STRING_ID, WL_WARNING);
- }
- /* Now that we've created the towns, let's grow them to their target populations. */
- for (auto &item : towns) {
- Town *t = item.first;
- uint population = item.second;
- uint tries = 500;
- do {
- Command<CMD_EXPAND_TOWN>::Post(t->index, 0);
- if (tries-- == 0) break;
- } while (t->cache.population < population);
- }
- }
Advertisement
Add Comment
Please, Sign In to add comment