The Battle for Wesnoth  1.19.5+dev
unit_list.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2016 - 2024
3  Part of the Battle for Wesnoth Project https://www.wesnoth.org/
4 
5  This program is free software; you can redistribute it and/or modify
6  it under the terms of the GNU General Public License as published by
7  the Free Software Foundation; either version 2 of the License, or
8  (at your option) any later version.
9  This program is distributed in the hope that it will be useful,
10  but WITHOUT ANY WARRANTY.
11 
12  See the COPYING file for more details.
13 */
14 
15 #define GETTEXT_DOMAIN "wesnoth-lib"
16 
18 
19 #include "gui/widgets/listbox.hpp"
20 #include "gui/widgets/image.hpp"
22 #include "gui/widgets/window.hpp"
23 #include "display.hpp"
24 #include "formatter.hpp"
25 #include "units/map.hpp"
26 #include "units/ptr.hpp"
27 #include "units/unit.hpp"
28 #include "serialization/markup.hpp"
29 
30 #include <functional>
31 
32 static lg::log_domain log_display("display");
33 #define LOG_DP LOG_STREAM(info, log_display)
34 
35 namespace gui2::dialogs
36 {
37 
38 REGISTER_DIALOG(unit_list)
39 
41  : modal_dialog(window_id())
42  , unit_list_(unit_list)
43  , scroll_to_(scroll_to)
44 {
45 }
46 
47 static std::string format_level_string(const int level)
48 {
49  if(level < 1) {
50  return markup::span_color("#969696", level);
51  } else if(level == 1) {
52  return std::to_string(level);
53  } else if(level == 2) {
54  return markup::bold(level);
55  } else { // level must be > 2
56  return markup::span_color("#ffffff", markup::bold(level));
57  }
58 
59 }
60 
61 static std::string format_if_leader(unit_const_ptr u, const std::string& str)
62 {
63  return u->can_recruit() ? markup::span_color("#cdad00", str) : str;
64 }
65 
67 {
68  const int moves_left = u->movement_left();
69  const int moves_max = u->total_movement();
70 
71  std::string color = "#00ff00";
72  if(moves_left == 0) {
73  color = "#ff0000";
74  } else if(moves_left < moves_max) {
75  color = "#ffff00";
76  }
77 
78  return markup::span_color(color, moves_left, "/", moves_max);
79 }
80 
82 {
83  listbox& list = find_widget<listbox>("units_list");
84 
86 
87  list.clear();
88 
89  keyboard_capture(&list);
90 
91  for(const unit_const_ptr& unit : unit_list_) {
92  widget_data row_data;
93  widget_item column;
94 
95  column["use_markup"] = "true";
96 
97  column["label"] = format_if_leader(unit, unit->type_name());
98  row_data.emplace("unit_type", column);
99 
100  const std::string& name = !unit->name().empty() ? format_if_leader(unit, unit->name().str()) : font::unicode_en_dash;
101  column["label"] = name;
102  row_data.emplace("unit_name", column);
103 
104  column["label"] = format_movement_string(unit);
105  row_data.emplace("unit_moves", column);
106 
107  std::stringstream hp_str;
108  hp_str << markup::span_color(unit->hp_color(), unit->hitpoints(), "/", unit->max_hitpoints());
109 
110  column["label"] = hp_str.str();
111  row_data.emplace("unit_hp", column);
112 
113  column["label"] = format_level_string(unit->level());
114  row_data.emplace("unit_level", column);
115 
116  if(unit->can_advance()) {
117  column["label"] = markup::span_color(unit->xp_color(), unit->experience(), "/", unit->max_experience());
118  } else {
119  column["label"] = markup::span_color(unit->xp_color(), font::unicode_en_dash);
120  }
121  row_data.emplace("unit_experience", column);
122 
123  column["label"] = utils::join(unit->trait_names(), ", ");
124  row_data.emplace("unit_traits", column);
125 
126  grid& row_grid = list.add_row(row_data);
127 
128  // NOTE: this needs to be done *after* the row is added
129  // TODO: show custom statuses
131  row_grid.find_widget<image>("unit_status_petrified").set_visible(widget::visibility::invisible);
132  }
133 
135  row_grid.find_widget<image>("unit_status_poisoned").set_visible(widget::visibility::invisible);
136  }
137 
139  row_grid.find_widget<image>("unit_status_slowed").set_visible(widget::visibility::invisible);
140  }
141 
142  if(!unit->invisible(unit->get_location(), false)) {
143  row_grid.find_widget<image>("unit_status_invisible").set_visible(widget::visibility::invisible);
144  }
145  }
146 
147  list.register_translatable_sorting_option(0, [this](const int i) { return unit_list_[i]->type_name().str(); });
148  list.register_translatable_sorting_option(1, [this](const int i) { return unit_list_[i]->name().str(); });
149  list.register_sorting_option(2, [this](const int i) { return unit_list_[i]->movement_left(); });
150  list.register_sorting_option(3, [this](const int i) { return unit_list_[i]->hitpoints(); });
151  list.register_sorting_option(4, [this](const int i) {
152  const unit& u = *unit_list_[i];
153  return std::tuple(u.level(), -static_cast<int>(u.experience_to_advance()));
154  });
155  list.register_sorting_option(5, [this](const int i) { return unit_list_[i]->experience(); });
156  list.register_translatable_sorting_option(6, [this](const int i) {
157  return !unit_list_[i]->trait_names().empty() ? unit_list_[i]->trait_names().front().str() : ""; });
158 
160 }
161 
163 {
164  const int selected_row
165  = find_widget<listbox>("units_list").get_selected_row();
166 
167  if(selected_row == -1) {
168  return;
169  }
170 
171  find_widget<unit_preview_pane>("unit_details")
172  .set_displayed_unit(*unit_list_[selected_row].get());
173 }
174 
176 {
177  if(get_retval() == retval::OK) {
178  const int selected_row = find_widget<listbox>("units_list").get_selected_row();
179 
180  scroll_to_ = unit_list_[selected_row]->get_location();
181  }
182 }
183 
185 {
186  std::vector<unit_const_ptr> unit_list;
187  map_location scroll_to;
188 
189  const unit_map& units = gui.context().units();
190  for(unit_map::const_iterator i = units.begin(); i != units.end(); ++i) {
191  if(i->side() != gui.viewing_team().side()) {
192  continue;
193  }
194 
195  unit_list.push_back(i.get_shared_ptr());
196  }
197 
198  if(unit_list::execute(unit_list, scroll_to)) {
199  gui.scroll_to_tile(scroll_to, display::WARP);
200  gui.select_hex(scroll_to);
201  }
202 }
203 
204 } // namespace dialogs
Sort-of-Singleton that many classes, both GUI and non-GUI, use to access the game data.
Definition: display.hpp:97
Abstract base class for all modal dialogs.
int get_retval() const
Returns the cached window exit code.
virtual void post_show() override
Actions to be taken after the window has been shown.
Definition: unit_list.cpp:175
virtual void pre_show() override
Actions to be taken before showing the window.
Definition: unit_list.cpp:81
map_location & scroll_to_
Definition: unit_list.hpp:50
std::vector< unit_const_ptr > & unit_list_
Definition: unit_list.hpp:48
void list_item_clicked()
Callbacks.
Definition: unit_list.cpp:162
static bool execute(std::vector< unit_const_ptr > &units, map_location &scroll_to)
Definition: unit_list.hpp:37
Base container class.
Definition: grid.hpp:32
The listbox class.
Definition: listbox.hpp:43
grid & add_row(const widget_item &item, const int index=-1)
When an item in the list is selected by the user we need to update the state.
Definition: listbox.cpp:58
void register_translatable_sorting_option(const int col, translatable_sorter_func_t f)
Registers a special sorting function specifically for translatable values.
Definition: listbox.cpp:612
void register_sorting_option(const int col, const Func &f)
Definition: listbox.hpp:260
void clear()
Removes all the rows in the listbox, clearing it.
Definition: listbox.cpp:117
NOT_DANGLING T * find_widget(const std::string &id, const bool must_be_active, const bool must_exist)
Gets a widget with the wanted id.
Definition: widget.hpp:742
void set_visible(const visibility visible)
Definition: widget.cpp:479
@ invisible
The user set the widget invisible, that means:
void keyboard_capture(widget *widget)
Definition: window.cpp:1207
bool empty() const
Definition: tstring.hpp:194
const std::string & str() const
Definition: tstring.hpp:198
Container associating units to locations.
Definition: map.hpp:98
unit_iterator end()
Definition: map.hpp:428
unit_iterator begin()
Definition: map.hpp:418
This class represents a single unit of a specific type.
Definition: unit.hpp:133
map_display and display: classes which take care of displaying the map and game-data on the screen.
std::size_t i
Definition: function.cpp:1028
bool invisible(const map_location &loc, bool see_all=true) const
Definition: unit.cpp:2579
int max_hitpoints() const
The max number of hitpoints this unit can have.
Definition: unit.hpp:505
int level() const
The current level of this unit.
Definition: unit.hpp:559
const t_string & type_name() const
Gets the translatable name of this unit's type.
Definition: unit.hpp:369
int hitpoints() const
The current number of hitpoints this unit has.
Definition: unit.hpp:499
bool get_state(const std::string &state) const
Check if the unit is affected by a status effect.
Definition: unit.cpp:1386
int experience() const
The current number of experience points this unit has.
Definition: unit.hpp:523
unsigned int experience_to_advance() const
The number of experience points this unit needs to level up, or 0 if current XP > max XP.
Definition: unit.hpp:541
int max_experience() const
The max number of experience points this unit can have.
Definition: unit.hpp:529
const t_string & name() const
Gets this unit's translatable display name.
Definition: unit.hpp:403
@ STATE_SLOWED
Definition: unit.hpp:860
@ STATE_PETRIFIED
The unit is poisoned - it loses health each turn.
Definition: unit.hpp:862
@ STATE_POISONED
The unit is slowed - it moves slower and does less damage.
Definition: unit.hpp:861
bool can_advance() const
Checks whether this unit has any options to advance to.
Definition: unit.hpp:272
color_t xp_color() const
Color for this unit's XP.
Definition: unit.cpp:1215
color_t hp_color() const
Color for this unit's current hitpoints.
Definition: unit.cpp:1171
const map_location & get_location() const
The current map location this unit is at.
Definition: unit.hpp:1404
const std::vector< t_string > & trait_names() const
Gets the names of the currently registered traits.
Definition: unit.hpp:1098
This file contains the window object, this object is a top level container which has the event manage...
CURSOR_TYPE get()
Definition: cursor.cpp:216
const std::string unicode_en_dash
Definition: constants.cpp:43
static std::string format_level_string(const int level)
Definition: unit_list.cpp:47
static std::string format_if_leader(unit_const_ptr u, const std::string &str)
Definition: unit_list.cpp:61
static std::string format_movement_string(unit_const_ptr u)
Definition: unit_list.cpp:66
REGISTER_DIALOG(editor_edit_unit)
void show_unit_list(display &gui)
Definition: unit_list.cpp:184
void connect_signal_notify_modified(dispatcher &dispatcher, const signal_notification &signal)
Connects a signal handler for getting a notification upon modification.
Definition: dispatcher.cpp:203
std::map< std::string, widget_item > widget_data
Definition: widget.hpp:36
std::map< std::string, t_string > widget_item
Definition: widget.hpp:33
@ OK
Dialog was closed with the OK button.
Definition: retval.hpp:35
General purpose widgets.
Functions to load and save images from/to disk.
std::string bold(Args &&... data)
Definition: markup.hpp:128
std::string span_color(const color_t &color, Args &&... data)
Definition: markup.hpp:68
std::string join(const T &v, const std::string &s=",")
Generates a new string joining container items in a list.
int moves_left
Definition: pathfind.cpp:156
std::shared_ptr< const unit > unit_const_ptr
Definition: ptr.hpp:27
Encapsulates the map of the game.
Definition: location.hpp:45
static lg::log_domain log_display("display")