Freeciv21
Develop your civilization from humble roots to a global empire
layer_city.cpp
Go to the documentation of this file.
1 // SPDX-License-Identifier: GPLv3-or-later
2 // SPDX-FileCopyrightText: Freeciv authors
3 // SPDX-FileCopyrightText: Freeciv21 authors
4 // SPDX-FileCopyrightText: Louis Moureaux <m_louis30@yahoo.com>
5 
6 #include "layer_city.h"
7 
8 #include "city.h"
9 #include "citybar.h"
10 #include "rgbcolor.h"
11 #include "style.h"
12 #include "tilespec.h"
13 #include "utils/colorizer.h"
14 
15 namespace freeciv {
16 
17 layer_city::layer_city(struct tileset *ts, const QPoint &city_offset,
18  const QPoint &city_flag_offset,
19  const QPoint &occupied_offset)
20  : freeciv::layer(ts, LAYER_CITY1), m_city_offset(city_offset),
21  m_city_flag_offset(city_flag_offset),
22  m_occupied_offset(occupied_offset)
23 {
24 }
25 
30 {
31  m_disorder = load_sprite({QStringLiteral("city.disorder")}, true);
32  m_happy = load_sprite({QStringLiteral("city.happy")}, false);
33 }
34 
40  const citystyle &style)
41 {
42  auto gfx_in_use = style.graphic;
43  auto sprites = layer_city::styles();
44 
45  for (int size = 0; size < MAX_CITY_SIZE; size++) {
46  const auto buffer = QStringLiteral("%1_%2_%3")
47  .arg(gfx_in_use, tag, QString::number(size));
48  if (const auto sprite = load_sprite({buffer}, true, false)) {
49  sprites.push_back(std::make_unique<freeciv::colorizer>(
50  *sprite, tileset_replaced_hue(tileset())));
51  } else if (size == 0) {
52  if (gfx_in_use == style.graphic) {
53  // Try again with graphic_alt.
54  size--;
55  gfx_in_use = style.graphic_alt;
56  } else {
57  // Don't load any others if the 0 element isn't there.
58  break;
59  }
60  } else {
61  // Stop loading as soon as we don't find the next one.
62  break;
63  }
64  }
65  if (sprites.empty() && strlen(style.graphic_alt)
66  && style.graphic_alt != QStringLiteral("-")) {
67  tileset_error(tileset(), QtInfoMsg,
68  "Could not find any sprite matching %s_%s_0 or %s_%s_0",
69  style.graphic, qUtf8Printable(tag), style.graphic_alt,
70  qUtf8Printable(tag));
71  } else if (sprites.empty()) {
72  // No graphic_alt
73  tileset_error(tileset(), QtInfoMsg,
74  "Could not find any sprite matching %s_%s_0",
75  style.graphic, qUtf8Printable(tag));
76  } else {
77  tileset_error(tileset(), QtInfoMsg, "Loaded %d %s_%s* sprites",
78  sprites.size(), gfx_in_use, qUtf8Printable(tag));
79  }
80 
81  return sprites;
82 }
83 
87 void layer_city::initialize_city_style(const citystyle &style, int index)
88 {
89  fc_assert_ret(index == m_tile.size());
90 
91  m_tile.push_back(load_city_size_sprites(QStringLiteral("city"), style));
92  m_single_wall.push_back(
93  load_city_size_sprites(QStringLiteral("wall"), style));
94  m_occupied.push_back(
95  load_city_size_sprites(QStringLiteral("occupied"), style));
96 
97  for (int i = 0; i < m_walls.size(); i++) {
98  m_walls[i].push_back(
99  load_city_size_sprites(QStringLiteral("bldg_%1").arg(i), style));
100  }
101 
102  if (m_tile.back().empty()) {
104  _("City style \"%s\": no city graphics."),
105  city_style_rule_name(index));
106  }
107  if (m_occupied.back().empty()) {
109  _("City style \"%s\": no occupied graphics."),
110  city_style_rule_name(index));
111  }
112 }
113 
114 namespace /* anonymous */ {
121 static const QPixmap *
122 get_city_sprite(const layer_city::city_sprite &city_sprite,
123  const struct city *pcity)
124 {
125  // get style and match the best tile based on city size
126  int style = style_of_city(pcity);
127  int img_index;
128 
129  fc_assert_ret_val(style < city_sprite.size(), nullptr);
130 
131  const auto num_thresholds = city_sprite[style].size();
132  const auto &thresholds = city_sprite[style];
133 
134  if (num_thresholds == 0) {
135  return nullptr;
136  }
137 
138  // Get the sprite with the index defined by the effects.
139  img_index = pcity->client.city_image;
140  if (img_index == -100) {
141  /* The server doesn't know the right value as this is from and old
142  * savegame. Guess here based on *client* side information as was done in
143  * versions where information was not saved to savegame - this should
144  * give us right answer of what city looked like by the time it was put
145  * under FoW. */
146  img_index = get_city_bonus(pcity, EFT_CITY_IMAGE);
147  }
148  img_index = CLIP(0, img_index, num_thresholds - 1);
149 
150  const auto owner =
151  pcity->owner; // city_owner asserts when there is no owner
152  auto color = QColor();
153  if (owner && owner->rgb) {
154  color.setRgb(owner->rgb->r, owner->rgb->g, owner->rgb->b);
155  }
156  return thresholds[img_index]->pixmap(color);
157 }
158 } // anonymous namespace
159 
164 std::vector<drawn_sprite>
165 layer_city::fill_sprite_array(const tile *ptile, const tile_edge *pedge,
166  const tile_corner *pcorner,
167  const unit *punit) const
168 {
169  if (!gui_options->draw_cities) {
170  return {};
171  }
172 
173  auto pcity = tile_city(ptile);
174  if (!pcity) {
175  return {};
176  }
177 
178  std::vector<drawn_sprite> sprs;
179 
180  // The flag, if needed
181  if (!citybar_painter::current()->has_flag()) {
182  sprs.emplace_back(
183  tileset(), get_city_flag_sprite(tileset(), pcity), true,
185  }
186 
187  auto more = fill_sprite_array_no_flag(
188  pcity, !citybar_painter::current()->has_units());
189  sprs.insert(sprs.end(), more.begin(), more.end());
190 
191  return sprs;
192 }
193 
199 std::vector<drawn_sprite>
201  bool show_occupied) const
202 {
203  auto walls = pcity->client.walls;
204  if (walls >= NUM_WALL_TYPES) { // Failsafe
205  walls = 1;
206  }
207 
208  std::vector<drawn_sprite> sprs;
209 
210  /* For iso-view the city.wall graphics include the full city, whereas for
211  * non-iso view they are an overlay on top of the base city graphic. This
212  * leads to the mess below. */
213 
214  // Non-isometric: city & Isometric: city without walls
215  if (!tileset_is_isometric(tileset()) || walls <= 0) {
216  sprs.emplace_back(tileset(), get_city_sprite(m_tile, pcity), true,
218  }
219  // Isometric: walls and city together
220  if (tileset_is_isometric(tileset()) && walls > 0) {
221  auto sprite = get_city_sprite(m_walls[walls - 1], pcity);
222  if (!sprite) {
223  sprite = get_city_sprite(m_single_wall, pcity);
224  }
225 
226  if (sprite) {
227  sprs.emplace_back(tileset(), sprite, true,
229  }
230  }
231 
232  // Occupied flag
233  if (show_occupied && pcity->client.occupied) {
234  sprs.emplace_back(tileset(), get_city_sprite(m_occupied, pcity), true,
237  }
238 
239  // Non-isometric: add walls on top
240  if (!tileset_is_isometric(tileset()) && walls > 0) {
241  auto sprite = get_city_sprite(m_walls[walls - 1], pcity);
242  if (!sprite) {
243  sprite = get_city_sprite(m_single_wall, pcity);
244  }
245 
246  if (sprite) {
247  sprs.emplace_back(tileset(), sprite, true,
249  }
250  }
251 
252  // Happiness
253  if (pcity->client.unhappy) {
254  sprs.emplace_back(tileset(), m_disorder, true,
256  } else if (m_happy && pcity->client.happy) {
257  sprs.emplace_back(tileset(), m_happy, true,
259  }
260 
261  return sprs;
262 }
263 
269 {
270  m_tile.clear();
271 
272  for (auto &wall : m_walls) {
273  wall.clear();
274  }
275  m_single_wall.clear();
276  m_occupied.clear();
277 }
278 
279 } // namespace freeciv
const char * city_style_rule_name(const int style)
Return the (untranslated) rule name of the city style.
Definition: city.cpp:1651
#define MAX_CITY_SIZE
Definition: city.h:79
static citybar_painter * current()
Returns the current painter (never null).
Definition: citybar.cpp:295
layer_city::styles load_city_size_sprites(const QString &tag, const citystyle &style)
Loads a set of size-dependent city sprites, with tags of the form graphic_tag_size.
Definition: layer_city.cpp:39
constexpr static int NUM_WALL_TYPES
Definition: layer_city.h:24
std::vector< std::unique_ptr< freeciv::colorizer > > styles
Definition: layer_city.h:22
city_sprite m_occupied
Definition: layer_city.h:61
void initialize_city_style(const citystyle &style, int index) override
Loads all sprites for a city style.
Definition: layer_city.cpp:87
QPixmap * m_disorder
Definition: layer_city.h:59
city_sprite m_tile
Definition: layer_city.h:61
std::array< city_sprite, NUM_WALL_TYPES > m_walls
Definition: layer_city.h:62
QPixmap * m_happy
Definition: layer_city.h:59
QPoint m_occupied_offset
Definition: layer_city.h:58
std::vector< styles > city_sprite
Definition: layer_city.h:23
QPoint m_city_flag_offset
Definition: layer_city.h:58
layer_city(struct tileset *ts, const QPoint &city_offset, const QPoint &city_flag_offset, const QPoint &occupied_offset)
Definition: layer_city.cpp:17
void reset_ruleset() override
All sprites depend on the ruleset-defined city styles and need to be reset.
Definition: layer_city.cpp:268
city_sprite m_single_wall
Definition: layer_city.h:61
void load_sprites() override
Loads the disorder and happy sprites.
Definition: layer_city.cpp:29
std::vector< drawn_sprite > fill_sprite_array_no_flag(const city *pcity, bool show_occupied) const
Fill in the given sprite array with any city sprites.
Definition: layer_city.cpp:200
std::vector< drawn_sprite > fill_sprite_array(const tile *ptile, const tile_edge *pedge, const tile_corner *pcorner, const unit *punit) const override
Fill in the given sprite array with any needed city sprites.
Definition: layer_city.cpp:165
A layer when drawing the map.
Definition: layer.h:153
QPixmap * load_sprite(const QStringList &possible_names, bool required=false, bool verbose=true) const
Shortcut to load a sprite from the tileset.
Definition: layer.cpp:79
struct tileset * tileset() const
Definition: layer.h:241
int get_city_bonus(const struct city *pcity, enum effect_type effect_type, enum vision_layer vlayer)
Returns the effect bonus at a city.
Definition: effects.cpp:688
#define _(String)
Definition: fcintl.h:50
#define fc_assert_ret(condition)
Definition: log.h:112
#define fc_assert_ret_val(condition, val)
Definition: log.h:114
constexpr auto LOG_FATAL
Definition: log.h:22
static int img_index(const int x, const int y, const struct img *pimg)
Get the index for an (x,y) image coordinate.
Definition: mapimg.cpp:1665
Definition: path.cpp:10
client_options * gui_options
Definition: options.cpp:74
#define CLIP(lower, current, upper)
Definition: shared.h:51
size_t size
Definition: specvec.h:64
static bool wall(char *str, bool check)
Send a message to all players.
Definition: stdinhand.cpp:1972
Definition: city.h:291
struct city::@15::@18 client
struct player * owner
Definition: city.h:294
char graphic_alt[MAX_LEN_NAME]
Definition: city.h:467
char graphic[MAX_LEN_NAME]
Definition: city.h:466
bool draw_cities
Definition: options.h:136
Definition: tile.h:42
Definition: unit.h:134
int style_of_city(const struct city *pcity)
Evaluate which style should be used to draw a city.
Definition: style.cpp:192
struct city * tile_city(const struct tile *ptile)
Return the city on this tile (or nullptr), checking for city center.
Definition: tile.cpp:72
const QPixmap * get_city_flag_sprite(const struct tileset *t, const struct city *pcity)
Return the flag graphic to be used by the city.
Definition: tilespec.cpp:3052
bool tileset_is_isometric(const struct tileset *t)
Return whether the current tileset is isometric.
Definition: tilespec.cpp:336
QPoint tileset_full_tile_offset(const struct tileset *t)
Return the x and y offsets of full tiles in the tileset.
Definition: tilespec.cpp:416
int tileset_replaced_hue(const struct tileset *t)
Returns the hue (color) that should be replaced with the player color in player-dependent sprites.
Definition: tilespec.cpp:548
void tileset_error(struct tileset *t, QtMsgType level, const char *format,...)
Called when ever there's problem in ruleset/tileset compatibility.
Definition: tilespec.cpp:291