Guest User

Untitled

a guest
May 28th, 2024
68
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 5.56 KB | None | 0 0
  1. /**
  2. * Helper for CircularTileSearch to found a town on or near a given tile.
  3. * @param tile The tile to try founding the town upon.
  4. * @param user_data The ExternalTownData to attempt to found.
  5. * @return True if the town was founded successfully.
  6. */
  7. static bool TryFoundTownNearby(TileIndex tile, void *user_data)
  8. {
  9. ExternalTownData &town = *static_cast<ExternalTownData *>(user_data);
  10. 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);
  11.  
  12. TownID id = std::get<TownID>(result);
  13.  
  14. /* Check if the command failed. */
  15. if (id == INVALID_TOWN) return false;
  16.  
  17. /* The command succeeded, send the ID back through user_data. */
  18. town.town_id = id;
  19. return true;
  20. }
  21.  
  22. /**
  23. * Load town data from _file_to_saveload and place its contents into the world.
  24. * @return True if the JSON file is valid and has north and south extents, otherwise false.
  25. */
  26. void LoadTownData()
  27. {
  28. /* Load the JSON file as a string initially. We'll parse it soon. */
  29. size_t filesize;
  30. FILE *f = FioFOpenFile(_file_to_saveload.name, "rb", HEIGHTMAP_DIR, &filesize);
  31.  
  32. if (f == nullptr) {
  33. ShowErrorMessage(STR_TOWN_DATA_ERROR_LOAD_FAILED, STR_TOWN_DATA_ERROR_JSON_FORMATTED_INCORRECTLY, WL_ERROR);
  34. return;
  35. }
  36.  
  37. std::string text(filesize, '\0');
  38. size_t len = fread(text.data(), filesize, 1, f);
  39. FioFCloseFile(f);
  40. if (len != 1) {
  41. ShowErrorMessage(STR_TOWN_DATA_ERROR_LOAD_FAILED, STR_TOWN_DATA_ERROR_JSON_FORMATTED_INCORRECTLY, WL_ERROR);
  42. return;
  43. }
  44.  
  45. /* Now parse the JSON. */
  46. nlohmann::json town_data;
  47. try {
  48. town_data = nlohmann::json::parse(text);
  49. } catch (nlohmann::json::exception &) {
  50. ShowErrorMessage(STR_TOWN_DATA_ERROR_LOAD_FAILED, STR_TOWN_DATA_ERROR_JSON_FORMATTED_INCORRECTLY, WL_ERROR);
  51. return;
  52. }
  53.  
  54. /* Check for JSON formatting errors with the array of towns. */
  55. if (!town_data.is_array()) {
  56. ShowErrorMessage(STR_TOWN_DATA_ERROR_LOAD_FAILED, STR_TOWN_DATA_ERROR_JSON_FORMATTED_INCORRECTLY, WL_ERROR);
  57. return;
  58. }
  59.  
  60. std::vector<std::pair<Town *, uint> > towns;
  61. uint failed_towns = 0;
  62.  
  63. /* Iterate through towns and attempt to found them. */
  64. for (auto &feature : town_data) {
  65. ExternalTownData town;
  66.  
  67. /* Ensure JSON is formatted properly. */
  68. if (!feature.is_object()) {
  69. ShowErrorMessage(STR_TOWN_DATA_ERROR_LOAD_FAILED, STR_TOWN_DATA_ERROR_JSON_FORMATTED_INCORRECTLY, WL_ERROR);
  70. return;
  71. }
  72.  
  73. /* Check to ensure all fields exist and are of the correct type.
  74. * If the town name is formatted wrong, all we can do is give a general warning. */
  75. if (!feature.contains("name") || !feature.at("name").is_string()) {
  76. ShowErrorMessage(STR_TOWN_DATA_ERROR_LOAD_FAILED, STR_TOWN_DATA_ERROR_JSON_FORMATTED_INCORRECTLY, WL_ERROR);
  77. return;
  78. }
  79.  
  80. /* If other fields are formatted wrong, we can actually inform the player which town is the problem. */
  81. if (!feature.contains("population") || !feature.at("population").is_number() ||
  82. !feature.contains("city") || !feature.at("city").is_boolean() ||
  83. !feature.contains("x") || !feature.at("x").is_number() ||
  84. !feature.contains("y") || !feature.at("y").is_number()) {
  85. feature.at("name").get_to(town.name);
  86. SetDParamStr(0, town.name);
  87. ShowErrorMessage(STR_TOWN_DATA_ERROR_LOAD_FAILED, STR_TOWN_DATA_ERROR_TOWN_FORMATTED_INCORRECTLY, WL_ERROR);
  88. return;
  89. }
  90.  
  91. /* Set town properties. */
  92. feature.at("name").get_to(town.name);
  93. feature.at("population").get_to(town.population);
  94. feature.at("city").get_to(town.is_city);
  95.  
  96. /* Set town coordinates. */
  97. feature.at("x").get_to(town.x_proportion);
  98. feature.at("y").get_to(town.y_proportion);
  99.  
  100. /* Check for improper coordinates and warn the player. */
  101. if (town.x_proportion <= 0 || town.y_proportion <= 0 || town.x_proportion >= 1 || town.y_proportion >= 1) {
  102. SetDParamStr(0, town.name);
  103. ShowErrorMessage(STR_TOWN_DATA_ERROR_LOAD_FAILED, STR_TOWN_DATA_ERROR_BAD_COORDINATE, WL_ERROR);
  104. return;
  105. }
  106.  
  107. /* Find the target tile for the town. */
  108. TileIndex tile = TileXY(town.x_proportion * Map::MaxX(), town.y_proportion * Map::MaxY());
  109.  
  110. /* Try founding on the target tile, and if that doesn't work, find the nearest suitable tile up to 16 tiles away.
  111. * The target might be on water, blocked somehow, or on a steep slope that can't be terraformed by the founding command. */
  112. TileIndex search_tile = tile;
  113. bool success = CircularTileSearch(&search_tile, 16, 0, 0, TryFoundTownNearby, &town);
  114.  
  115. /* 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.
  116. * This allows the player to diagnose a heightmap misalignment, if towns end up in the sea, or place towns manually, if in rough terrain. */
  117. if (!success) {
  118. Command<CMD_PLACE_SIGN>::Post(tile, town.name);
  119. failed_towns++;
  120. continue;
  121. }
  122.  
  123. towns.emplace_back(std::make_pair(Town::Get(town.town_id), town.population));
  124. }
  125.  
  126. /* If we couldn't found a town (or multiple), display a message to the player with the number of failed towns. */
  127. if (failed_towns > 0) {
  128. SetDParam(0, failed_towns);
  129. ShowErrorMessage(STR_TOWN_DATA_ERROR_FAILED_TO_FOUND_TOWN, INVALID_STRING_ID, WL_WARNING);
  130. }
  131.  
  132. /* Now that we've created the towns, let's grow them to their target populations. */
  133. for (auto &item : towns) {
  134. Town *t = item.first;
  135. uint population = item.second;
  136.  
  137. uint tries = 500;
  138. do {
  139. Command<CMD_EXPAND_TOWN>::Post(t->index, 0);
  140. if (tries-- == 0) break;
  141. } while (t->cache.population < population);
  142. }
  143. }
Advertisement
Add Comment
Please, Sign In to add comment