map_preview_generator.cpp 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157
  1. #include "map_preview_generator.h"
  2. #include "../../units/spawn_type.h"
  3. #include "../map_loader.h"
  4. #include "minimap_generator.h"
  5. #include "minimap_utils.h"
  6. #include <QColor>
  7. #include <QFile>
  8. #include <QJsonDocument>
  9. #include <QJsonObject>
  10. #include <QPainter>
  11. #include <QPen>
  12. #include <QVariantMap>
  13. #include <cmath>
  14. namespace Game::Map::Minimap {
  15. namespace {
  16. constexpr float BASE_SIZE = 16.0F;
  17. constexpr float INNER_SIZE_RATIO = 0.35F;
  18. constexpr float INNER_OFFSET_RATIO = 0.3F;
  19. } // namespace
  20. MapPreviewGenerator::MapPreviewGenerator()
  21. : m_minimap_generator(std::make_unique<MinimapGenerator>()) {}
  22. MapPreviewGenerator::~MapPreviewGenerator() = default;
  23. auto MapPreviewGenerator::generate_preview(
  24. const QString &map_path, const QVariantList &player_configs) -> QImage {
  25. MapDefinition map_def;
  26. QString error;
  27. if (!MapLoader::loadFromJsonFile(map_path, map_def, &error)) {
  28. QImage error_image(200, 200, QImage::Format_RGBA8888);
  29. error_image.fill(QColor(40, 40, 40));
  30. return error_image;
  31. }
  32. QImage preview = m_minimap_generator->generate(map_def);
  33. std::vector<PlayerConfig> parsed_configs =
  34. parse_player_configs(player_configs);
  35. draw_player_bases(preview, map_def, parsed_configs);
  36. return preview;
  37. }
  38. auto MapPreviewGenerator::parse_player_configs(
  39. const QVariantList &configs) const -> std::vector<PlayerConfig> {
  40. std::vector<PlayerConfig> result;
  41. for (const QVariant &var : configs) {
  42. if (!var.canConvert<QVariantMap>()) {
  43. continue;
  44. }
  45. QVariantMap config = var.toMap();
  46. PlayerConfig player_config;
  47. if (config.contains("player_id")) {
  48. player_config.player_id = config["player_id"].toInt();
  49. }
  50. if (config.contains("colorHex")) {
  51. QString color_hex = config["colorHex"].toString();
  52. player_config.color = QColor(color_hex);
  53. }
  54. if (player_config.player_id > 0 && player_config.color.isValid()) {
  55. result.push_back(player_config);
  56. }
  57. }
  58. return result;
  59. }
  60. void MapPreviewGenerator::draw_player_bases(
  61. QImage &image, const MapDefinition &map_def,
  62. const std::vector<PlayerConfig> &player_configs) {
  63. if (player_configs.empty()) {
  64. return;
  65. }
  66. QPainter painter(&image);
  67. painter.setRenderHint(QPainter::Antialiasing, true);
  68. constexpr float pixels_per_tile = 2.0F;
  69. for (const auto &spawn : map_def.spawns) {
  70. if (!Game::Units::is_building_spawn(spawn.type)) {
  71. continue;
  72. }
  73. if (spawn.player_id <= 0) {
  74. continue;
  75. }
  76. QColor player_color;
  77. for (const auto &config : player_configs) {
  78. if (config.player_id == spawn.player_id) {
  79. player_color = config.color;
  80. break;
  81. }
  82. }
  83. if (!player_color.isValid()) {
  84. continue;
  85. }
  86. const auto [world_x, world_z] =
  87. grid_to_world_coords(spawn.x, spawn.z, map_def);
  88. const auto [px, py] =
  89. world_to_pixel(world_x, world_z, map_def.grid, pixels_per_tile);
  90. constexpr float HALF = BASE_SIZE * 0.5F;
  91. QColor border_color = player_color.darker(150);
  92. painter.setBrush(player_color);
  93. painter.setPen(QPen(border_color, 2.5));
  94. painter.drawEllipse(QPointF(px, py), HALF, HALF);
  95. painter.setBrush(player_color.lighter(130));
  96. painter.setPen(Qt::NoPen);
  97. constexpr float INNER_SIZE = BASE_SIZE * INNER_SIZE_RATIO;
  98. painter.drawEllipse(
  99. QPointF(px - HALF * INNER_OFFSET_RATIO, py - HALF * INNER_OFFSET_RATIO),
  100. INNER_SIZE * 0.5F, INNER_SIZE * 0.5F);
  101. }
  102. }
  103. auto MapPreviewGenerator::world_to_pixel(
  104. float world_x, float world_z, const GridDefinition &grid,
  105. float pixels_per_tile) const -> std::pair<float, float> {
  106. const auto &orient = MinimapOrientation::instance();
  107. const float rotated_x =
  108. world_x * orient.cos_yaw() - world_z * orient.sin_yaw();
  109. const float rotated_z =
  110. world_x * orient.sin_yaw() + world_z * orient.cos_yaw();
  111. const float world_width = grid.width * grid.tile_size;
  112. const float world_height = grid.height * grid.tile_size;
  113. const float img_width = grid.width * pixels_per_tile;
  114. const float img_height = grid.height * pixels_per_tile;
  115. const float px = (rotated_x + world_width * 0.5F) * (img_width / world_width);
  116. const float py =
  117. (rotated_z + world_height * 0.5F) * (img_height / world_height);
  118. return {px, py};
  119. }
  120. } // namespace Game::Map::Minimap