Freeciv21
Develop your civilization from humble roots to a global empire
layer_units.cpp
Go to the documentation of this file.
1 /*
2  * SPDX-FileCopyrightText: 2022-2023 Louis Moureaux <m_louis30@yahoo.com>
3  *
4  * SPDX-License-Identifier: GPLv3-or-later
5  */
6 
7 #include "layer_units.h"
8 
9 #include "control.h"
10 #include "movement.h"
11 #include "options.h"
12 #include "rgbcolor.h"
13 #include "tilespec.h"
14 
20 namespace freeciv {
21 
25 layer_units::layer_units(struct tileset *ts, mapview_layer layer,
26  const QPoint &activity_offset,
27  const QPoint &select_offset,
28  const QPoint &unit_offset,
29  const QPoint &unit_flag_offset)
31  m_activity_offset(activity_offset), m_select_offset(select_offset),
32  m_unit_offset(unit_offset), m_unit_flag_offset(unit_flag_offset)
33 {
34 }
35 
40 {
42 
43  m_auto_attack = load_sprite({"unit.auto_attack"}, true);
44  m_auto_explore = load_sprite({"unit.auto_explore"}, true);
45  m_auto_settler = load_sprite({"unit.auto_settler"}, true);
46  m_connect = load_sprite({"unit.connect"}, true);
47  m_loaded = load_sprite({"unit.loaded"}, true);
48  m_lowfuel = load_sprite({"unit.lowfuel"}, true);
49  m_patrol = load_sprite({"unit.patrol"}, true);
50  m_stack = load_sprite({"unit.stack"}, true);
51  m_tired = load_sprite({"unit.tired"}, true);
52  m_action_decision_want = load_sprite({"unit.action_decision_want"}, false);
53 
54  static_assert(MAX_NUM_BATTLEGROUPS < NUM_TILES_DIGITS);
55  for (int i = 0; i < MAX_NUM_BATTLEGROUPS; i++) {
56  QStringList buffer = {QStringLiteral("unit.battlegroup_%1").arg(i),
57  QStringLiteral("city.size_%1").arg(i + 1)};
58  m_battlegroup[i] = load_sprite({buffer}, true);
59  }
60 
61  for (int i = 0; i <= 100; i++) {
62  auto name = QStringLiteral("unit.hp_%1").arg(i);
63  if (auto sprite = load_sprite({name}, true, false)) {
64  m_hp_bar.push_back(sprite);
65  }
66  }
67  if (m_hp_bar.empty()) {
68  tileset_error(tileset(), QtFatalMsg,
69  "No unit.hp_* sprite in the tileset.");
70  } else {
71  tileset_error(tileset(), QtInfoMsg, "Loaded %d unit.hp* sprites.",
72  m_hp_bar.size());
73  }
74 
75  for (int i = 0; i < MAX_VET_LEVELS; i++) {
76  /* Veteran level sprites are optional. For instance "green" units
77  * usually have no special graphic. */
78  auto name = QStringLiteral("unit.vet_%1").arg(i);
80  }
81 
82  for (int i = 0;; i++) {
83  auto buffer = QStringLiteral("unit.select%1").arg(QString::number(i));
84  auto sprite = load_sprite({buffer});
85  if (!sprite) {
86  break;
87  }
88  m_select.push_back(sprite);
89  }
90 }
91 
92 std::vector<drawn_sprite>
93 layer_units::fill_sprite_array(const tile *ptile, const tile_edge *pedge,
94  const tile_corner *pcorner,
95  const unit *punit) const
96 {
97  Q_UNUSED(pedge);
98  Q_UNUSED(pcorner);
99 
100  // Should we draw anything in the first place?
101  if (!do_draw_unit(ptile, punit)) {
102  return {};
103  }
104 
105  // Only draw the focused unit on LAYER_FOCUS_UNIT
106  if ((type() == LAYER_FOCUS_UNIT) != unit_is_in_focus(punit)) {
107  return {};
108  }
109 
110  // Now we draw
111  std::vector<drawn_sprite> sprs;
112  const auto full_offset = tileset_full_tile_offset(tileset());
113 
114  const auto type = unit_type_get(punit);
115 
116  // Selection animation. The blinking unit is handled separately, inside
117  // get_drawable_unit().
118  if (ptile && unit_is_in_focus(punit) && !m_select.empty()) {
119  sprs.emplace_back(tileset(), m_select[focus_unit_state()], true,
121  }
122 
123  // Flag
124  if (!ptile || !tile_city(ptile)) {
126  sprs.emplace_back(tileset(),
127  get_unit_nation_flag_sprite(tileset(), punit), true,
128  full_offset + m_unit_flag_offset);
129  } else {
130  // Taken care of in layer_background.
131  }
132  }
133 
134  // Add the sprite for the unit type.
135  {
136  const auto rgb = punit->owner ? punit->owner->rgb : nullptr;
137  const auto color = rgb ? QColor(rgb->r, rgb->g, rgb->b) : QColor();
138  const auto uspr =
139  get_unittype_sprite(tileset(), type, punit->facing, color);
140  sprs.emplace_back(tileset(), uspr, true, full_offset + m_unit_offset);
141  }
142 
143  // Loaded
144  if (unit_transported(punit)) {
145  sprs.emplace_back(tileset(), m_loaded, true, full_offset);
146  }
147 
148  // Activity
149  if (auto sprite =
150  activity_sprite(punit->activity, punit->activity_target)) {
151  sprs.emplace_back(tileset(), sprite, true,
152  full_offset + m_activity_offset);
153  }
154 
155  // Automated units
156  add_automated_sprite(sprs, punit, full_offset);
157 
158  // Goto/patrol/connect
159  add_orders_sprite(sprs, punit, full_offset);
160 
161  // Wants a decision?
163  sprs.emplace_back(tileset(), m_action_decision_want, true,
164  full_offset + m_activity_offset);
165  }
166 
167  // Battlegroup
168  if (punit->battlegroup != BATTLEGROUP_NONE) {
169  sprs.emplace_back(tileset(), m_battlegroup[punit->battlegroup], true,
170  full_offset);
171  }
172 
173  // Show a low-fuel graphic if the unit has 2 or fewer moves left.
174  if (m_lowfuel && utype_fuel(type) && punit->fuel == 1
175  && punit->moves_left <= 2 * SINGLE_MOVE) {
176  sprs.emplace_back(tileset(), m_lowfuel, true, full_offset);
177  }
178 
179  // Out of moves
180  if (m_tired && punit->moves_left < SINGLE_MOVE && type->move_rate > 0) {
181  // Show a "tired" graphic if the unit has fewer than one move remaining,
182  // except for units for which it's full movement.
183  sprs.emplace_back(tileset(), m_tired, true, full_offset);
184  }
185 
186  // Stacks
187  if ((ptile && unit_list_size(ptile->units) > 1)
188  || punit->client.occupied) {
189  sprs.emplace_back(tileset(), m_stack, true, full_offset);
190  }
191 
192  // Veteran level
193  if (m_veteran_level[punit->veteran]) {
194  sprs.emplace_back(tileset(), m_veteran_level[punit->veteran], true,
195  full_offset);
196  }
197 
198  // HP
199  {
200  auto ihp = ((m_hp_bar.size() - 1) * punit->hp) / type->hp;
201  ihp = CLIP(0, ihp, m_hp_bar.size() - 1); // Safety
202  sprs.emplace_back(tileset(), m_hp_bar[ihp], true, full_offset);
203  }
204 
205  return sprs;
206 }
207 
211 void layer_units::add_automated_sprite(std::vector<drawn_sprite> &sprs,
212  const unit *punit,
213  const QPoint &full_offset) const
214 {
215  QPixmap *sprite = nullptr;
216  QPoint offset;
217 
218  switch (punit->ssa_controller) {
219  case SSA_NONE:
220  break;
221  case SSA_AUTOSETTLER:
222  sprite = m_auto_settler;
223  break;
224  case SSA_AUTOEXPLORE:
225  sprite = m_auto_explore;
226  // Specified as an activity in the tileset.
227  offset = m_activity_offset;
228  break;
229  default:
230  sprite = m_auto_attack; // FIXME What's this hack?
231  break;
232  }
233 
234  if (sprite) {
235  sprs.emplace_back(tileset(), sprite, true, full_offset + offset);
236  }
237 }
238 
242 void layer_units::add_orders_sprite(std::vector<drawn_sprite> &sprs,
243  const unit *punit,
244  const QPoint &full_offset) const
245 {
246  if (unit_has_orders(punit)) {
247  if (punit->orders.repeat) {
248  sprs.emplace_back(tileset(), m_patrol, true, full_offset);
249  } else if (punit->activity != ACTIVITY_IDLE) {
250  sprs.emplace_back(tileset(), m_connect);
251  } else {
252  sprs.emplace_back(tileset(), activity_sprite(ACTIVITY_GOTO, nullptr),
253  true, full_offset + m_activity_offset);
254  }
255  }
256 }
257 
258 } // namespace freeciv
An abstract class for layers that need sprites for unit activities.
void load_sprites() override
Loads the sprites in memory.
QPixmap * activity_sprite(unit_activity id, const extra_type *extra) const
Returns the sprite used to represent a given activity on the map.
QPixmap * m_action_decision_want
Definition: layer_units.h:62
std::array< QPixmap *, MAX_VET_LEVELS > m_veteran_level
Definition: layer_units.h:65
std::vector< QPixmap * > m_select
Definition: layer_units.h:63
QPixmap * m_auto_explore
Definition: layer_units.h:60
QPixmap * m_auto_settler
Definition: layer_units.h:60
layer_units(struct tileset *ts, mapview_layer layer, const QPoint &activity_offset, const QPoint &select_offset, const QPoint &unit_offset, const QPoint &unit_flag_offset)
Constructor.
Definition: layer_units.cpp:25
QPixmap * m_auto_attack
Definition: layer_units.h:60
int focus_unit_state() const
Returns the current state of the focused unit animation.
Definition: layer_units.h:44
std::vector< drawn_sprite > fill_sprite_array(const tile *ptile, const tile_edge *pedge, const tile_corner *pcorner, const unit *punit) const override
Returns the list of sprites drawn by this layer somewhere on the map.
Definition: layer_units.cpp:93
void add_automated_sprite(std::vector< drawn_sprite > &sprs, const unit *punit, const QPoint &full_offset) const
Adds the sprite used to represent an automated unit on the map to sprs.
std::array< QPixmap *, MAX_NUM_BATTLEGROUPS > m_battlegroup
Definition: layer_units.h:64
void load_sprites() override
Loads all static sprites needed by this layer (activities etc).
Definition: layer_units.cpp:39
std::vector< QPixmap * > m_hp_bar
Definition: layer_units.h:63
void add_orders_sprite(std::vector< drawn_sprite > &sprs, const unit *punit, const QPoint &full_offset) const
Adds the sprite used to represent unit orders to sprs.
A layer when drawing the map.
Definition: layer.h:153
mapview_layer type() const
Definition: layer.h:238
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
bool do_draw_unit(const tile *ptile, const unit *punit) const
Whether a unit should be drawn.
Definition: layer.cpp:27
struct tileset * tileset() const
Definition: layer.h:241
bool unit_is_in_focus(const struct unit *punit)
Return TRUE iff this unit is in focus.
Definition: control.cpp:364
bool should_ask_server_for_actions(const struct unit *punit)
Returns TRUE iff the client should ask the server about what actions a unit can perform.
Definition: control.cpp:318
#define MAX_VET_LEVELS
Definition: fc_types.h:41
const char * name
Definition: inputfile.cpp:118
#define NUM_TILES_DIGITS
Definition: layer.h:78
#define SINGLE_MOVE
Definition: movement.h:17
Definition: path.cpp:10
client_options * gui_options
Definition: options.cpp:74
#define CLIP(lower, current, upper)
Definition: shared.h:51
bool solid_color_behind_units
Local Options:
Definition: options.h:79
struct rgbcolor * rgb
Definition: player.h:293
Definition: tile.h:42
struct unit_list * units
Definition: tile.h:50
Definition: unit.h:134
int battlegroup
Definition: unit.h:188
enum unit_activity activity
Definition: unit.h:154
int moves_left
Definition: unit.h:147
struct unit::@76::@78 client
int hp
Definition: unit.h:148
int fuel
Definition: unit.h:150
enum direction8 facing
Definition: unit.h:138
struct extra_type * activity_target
Definition: unit.h:161
bool repeat
Definition: unit.h:193
struct unit::@75 orders
int veteran
Definition: unit.h:149
struct player * owner
Definition: unit.h:139
enum server_side_agent ssa_controller
Definition: unit.h:169
struct city * tile_city(const struct tile *ptile)
Return the city on this tile (or nullptr), checking for city center.
Definition: tile.cpp:72
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
const QPixmap * get_unittype_sprite(const struct tileset *t, const struct unit_type *punittype, enum direction8 facing, const QColor &replace)
Return the sprite for the unit type (the base "unit" sprite).
Definition: tilespec.cpp:3416
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
QPixmap * get_unit_nation_flag_sprite(const struct tileset *t, const struct unit *punit)
Return a sprite for the national flag for this unit.
Definition: tilespec.cpp:3061
bool unit_transported(const struct unit *pcargo)
Returns TRUE iff the unit is transported.
Definition: unit.cpp:2176
bool unit_has_orders(const struct unit *punit)
Return TRUE iff the unit is following client-side orders.
Definition: unit.cpp:195
#define BATTLEGROUP_NONE
Definition: unit.h:187
#define MAX_NUM_BATTLEGROUPS
Definition: unit.h:186
const struct unit_type * unit_type_get(const struct unit *punit)
Return the unit type for this unit.
Definition: unittype.cpp:114
#define utype_fuel(ptype)
Definition: unittype.h:772