18 #define GETTEXT_DOMAIN "wesnoth-lib"
49 #include <boost/algorithm/string/replace.hpp>
50 #include <boost/filesystem.hpp>
75 if (x.first.find(
"INTERNAL") == std::string::npos) {
80 connect_signal<event::SDL_KEY_DOWN>(std::bind(
88 button& quit = find_widget<button>(
"exit");
97 menu_button& alignments = find_widget<menu_button>(
"alignment_list");
98 for (
auto& align : unit_alignments::values) {
101 const std::string& icon_path =
"icons/alignments/alignment_" + std::string(align) +
"_30.png";
102 align_list_.emplace_back(
"label",
t_string(
static_cast<std::string
>(align),
"wesnoth"),
"icon", icon_path);
106 menu_button& races = find_widget<menu_button>(
"race_list");
108 const std::string& race_name =
i.second.id();
109 race_list_.emplace_back(
"label", race_name,
"icon",
i.second.get_icon_path_stem() +
"_30.png");
116 button&
load = find_widget<button>(
"load_unit_type");
128 find_widget<button>(
"browse_unit_image"),
131 find_widget<button>(
"preview_unit_image"),
134 find_widget<button>(
"browse_portrait_image"),
137 find_widget<button>(
"preview_portrait_image"),
141 find_widget<text_box>(
"name_box"),
144 find_widget<text_box>(
"id_box"),
152 menu_button& movetypes = find_widget<menu_button>(
"movetype_list");
161 menu_button& defenses = find_widget<menu_button>(
"defense_list");
170 menu_button& movement_costs = find_widget<menu_button>(
"movement_costs_list");
178 menu_button& resistances = find_widget<menu_button>(
"resistances_list");
185 resistances_list_.emplace_back(
"label", key,
"icon",
"icons/profiles/" + key +
".png");
193 menu_button& usage_types = find_widget<menu_button>(
"usage_list");
201 multimenu_button& abilities = find_widget<multimenu_button>(
"abilities_list");
205 find_widget<button>(
"browse_small_profile_image"),
208 find_widget<button>(
"preview_small_profile_image"),
211 find_widget<button>(
"load_movetype"),
214 find_widget<slider>(
"resistances_slider"),
217 find_widget<menu_button>(
"resistances_list"),
220 find_widget<toggle_button>(
"resistances_checkbox"),
224 find_widget<slider>(
"defense_slider"),
227 find_widget<menu_button>(
"defense_list"),
230 find_widget<toggle_button>(
"defense_checkbox"),
234 find_widget<slider>(
"movement_costs_slider"),
237 find_widget<menu_button>(
"movement_costs_list"),
240 find_widget<toggle_button>(
"movement_costs_checkbox"),
259 multimenu_button& specials = find_widget<multimenu_button>(
"weapon_specials_list");
262 combobox& attack_types = find_widget<combobox>(
"attack_type_list");
269 find_widget<button>(
"browse_attack_image"),
272 find_widget<button>(
"preview_attack_image"),
275 find_widget<menu_button>(
"atk_list"),
278 find_widget<button>(
"atk_new"),
281 find_widget<button>(
"atk_delete"),
284 find_widget<button>(
"atk_next"),
287 find_widget<button>(
"atk_prev"),
318 std::string dn = dlg.
path();
320 =
_(
"This file is outside Wesnoth’s data dirs. Do you wish to copy it into your add-on?");
322 if(id_stem ==
"unit_image") {
330 }
else if((id_stem ==
"portrait_image")||(id_stem ==
"small_profile_image")) {
338 }
else if(id_stem ==
"attack_image") {
348 find_widget<text_box>(
"path_"+id_stem).set_value(dn);
361 find_widget<text_box>(
"id_box").set_value(
type->id());
362 find_widget<text_box>(
"name_box").set_value(
type->type_name().base_str());
363 find_widget<spinner>(
"level_box").set_value(
type->level());
364 find_widget<slider>(
"cost_slider").set_value(
type->cost());
365 find_widget<text_box>(
"adv_box").set_value(
utils::join(
type->advances_to()));
366 find_widget<slider>(
"hp_slider").set_value(
type->hitpoints());
367 find_widget<slider>(
"xp_slider").set_value(
type->experience_needed());
368 find_widget<slider>(
"move_slider").set_value(
type->movement());
369 find_widget<scroll_text>(
"desc_box").set_value(
type->unit_description().base_str());
370 find_widget<text_box>(
"path_unit_image").set_value(
type->image());
371 find_widget<text_box>(
"path_portrait_image").set_value(
type->big_profile());
373 for (
const auto& gender :
type->genders())
375 if (gender == unit_race::GENDER::MALE) {
376 find_widget<toggle_button>(
"gender_male").set_value(
true);
379 if (gender == unit_race::GENDER::FEMALE) {
380 find_widget<toggle_button>(
"gender_female").set_value(
true);
385 find_widget<menu_button>(
"race_list"),
390 find_widget<menu_button>(
"alignment_list"),
397 find_widget<text_box>(
"path_small_profile_image").set_value(
type->small_profile());
400 find_widget<menu_button>(
"movetype_list"),
402 type->movement_type_id());
405 type->movement_type().write(cfg,
false);
412 if (!
type->get_cfg().has_child(
"resistance")) {
416 for (
const auto& [key,
_] :
type->get_cfg().mandatory_child(
"resistance").attribute_range()) {
424 if (
type->get_cfg().has_child(
"defense")) {
425 for (
const auto& [key,
_] :
type->get_cfg().mandatory_child(
"defense").attribute_range()) {
432 if (
type->get_cfg().has_child(
"movement_costs")) {
433 for (
const auto& [key,
_] :
type->get_cfg().mandatory_child(
"movement_costs").attribute_range()) {
446 find_widget<menu_button>(
"usage_list"),
454 for(
const auto& atk :
type->attacks())
458 attack[
"name"] = atk.id();
459 attack[
"description"] = atk.name().base_str();
460 attack[
"icon"] = atk.icon();
461 attack[
"range"] = atk.range();
462 attack[
"damage"] = atk.damage();
463 attack[
"number"] = atk.num_attacks();
464 attack[
"type"] = atk.type();
465 attacks_.push_back(std::make_pair(enabled, attack));
468 if (!
type->attacks().empty()) {
488 std::string current_textdomain =
"wesnoth-"+
addon_id_;
513 utype[
"gender"] =
"male,female";
515 utype[
"gender"] =
"male";
519 utype[
"gender"] =
"female";
564 if (abilities_states.any()) {
568 if (
i >= abilities_states.size()) {
572 if (abilities_states[
i] ==
true) {
584 find_widget<slider>(
"resistances_slider")
586 100 -
resistances_[find_widget<menu_button>(
"resistances_list").get_value_string()].to_int());
588 find_widget<slider>(
"resistances_slider")
589 .set_active(
res_toggles_[find_widget<menu_button>(
"resistances_list").get_value()]);
591 find_widget<toggle_button>(
"resistances_checkbox")
592 .set_value(
res_toggles_[find_widget<menu_button>(
"resistances_list").get_value()]);
596 resistances_[find_widget<menu_button>(
"resistances_list").get_value_string()]
597 = 100 - find_widget<slider>(
"resistances_slider").get_value();
601 bool toggle = find_widget<toggle_button>(
"resistances_checkbox").get_value();
602 res_toggles_[find_widget<menu_button>(
"resistances_list").get_value()] = toggle;
603 find_widget<slider>(
"resistances_slider").set_active(toggle);
607 find_widget<slider>(
"defense_slider")
609 100 -
defenses_[find_widget<menu_button>(
"defense_list").get_value_string()].to_int());
611 find_widget<slider>(
"defense_slider")
612 .set_active(
def_toggles_[find_widget<menu_button>(
"defense_list").get_value()]);
614 find_widget<toggle_button>(
"defense_checkbox")
615 .set_value(
def_toggles_[find_widget<menu_button>(
"defense_list").get_value()]);
619 defenses_[find_widget<menu_button>(
"defense_list").get_value_string()]
620 = 100 - find_widget<slider>(
"defense_slider").get_value();
624 bool toggle = find_widget<toggle_button>(
"defense_checkbox").get_value();
625 def_toggles_[find_widget<menu_button>(
"defense_list").get_value()] = toggle;
626 find_widget<slider>(
"defense_slider").set_active(toggle);
630 find_widget<slider>(
"movement_costs_slider")
632 movement_[find_widget<menu_button>(
"movement_costs_list").get_value_string()].to_int());
634 find_widget<slider>(
"movement_costs_slider")
635 .set_active(
move_toggles_[find_widget<menu_button>(
"movement_costs_list").get_value()]);
637 find_widget<toggle_button>(
"movement_costs_checkbox")
638 .set_value(
move_toggles_[find_widget<menu_button>(
"movement_costs_list").get_value()]);
642 movement_[find_widget<menu_button>(
"movement_costs_list").get_value_string()]
643 = find_widget<slider>(
"movement_costs_slider").get_value();
647 bool toggle = find_widget<toggle_button>(
"movement_costs_checkbox").get_value();
648 move_toggles_[find_widget<menu_button>(
"movement_costs_list").get_value()] = toggle;
649 find_widget<slider>(
"movement_costs_slider").set_active(toggle);
654 std::string current_textdomain =
"wesnoth-"+
addon_id_;
667 attack[
"name"] = find_widget<text_box>(
"atk_id_box").get_value();
668 attack[
"description"] =
t_string(find_widget<text_box>(
"atk_name_box").get_value(), current_textdomain);
669 attack[
"icon"] = find_widget<text_box>(
"path_attack_image").get_value();
670 attack[
"type"] = find_widget<combobox>(
"attack_type_list").get_value();
671 attack[
"damage"] = find_widget<slider>(
"dmg_box").get_value();
672 attack[
"number"] = find_widget<slider>(
"dmg_num_box").get_value();
673 attack[
"range"] = find_widget<combobox>(
"range_list").get_value();
692 find_widget<text_box>(
"atk_id_box").set_value(attack[
"name"]);
693 find_widget<text_box>(
"atk_name_box").set_value(attack[
"description"]);
694 find_widget<text_box>(
"path_attack_image").set_value(attack[
"icon"]);
696 find_widget<slider>(
"dmg_box").set_value(attack[
"damage"].to_int());
697 find_widget<slider>(
"dmg_num_box").set_value(attack[
"number"].to_int());
698 find_widget<combobox>(
"range_list").set_value(attack[
"range"]);
703 find_widget<multimenu_button>(
"weapon_specials_list")
719 std::vector<config> atk_name_list;
720 for(
const auto& atk_data :
attacks_) {
721 atk_name_list.emplace_back(
"label", atk_data.second[
"name"]);
723 menu_button& atk_list = find_widget<menu_button>(
"atk_list");
730 find_widget<label>(
"atk_number").set_label(new_index_str);
737 std::string current_textdomain =
"wesnoth-"+
addon_id_;
745 attack[
"name"] = find_widget<text_box>(
"atk_id_box").get_value();
746 attack[
"description"] =
t_string(find_widget<text_box>(
"atk_name_box").get_value(), current_textdomain);
747 attack[
"icon"] = find_widget<text_box>(
"path_attack_image").get_value();
748 attack[
"type"] = find_widget<combobox>(
"attack_type_list").get_value();
749 attack[
"damage"] = find_widget<slider>(
"dmg_box").get_value();
750 attack[
"number"] = find_widget<slider>(
"dmg_num_box").get_value();
751 attack[
"range"] = find_widget<combobox>(
"range_list").get_value();
757 , std::make_pair(find_widget<multimenu_button>(
"weapon_specials_list").get_toggle_states(), attack));
777 find_widget<button>(
"atk_delete").set_active(
false);
829 .mandatory_child(
"units")
830 .child_range(
"movetype")) {
831 if (
movetype[
"name"] == find_widget<menu_button>(
"movetype_list").get_value_string()) {
853 out <<
"{" << macro_name <<
"}\n";
863 std::stringstream wml_stream;
866 std::string current_textdomain =
"wesnoth-"+
addon_id_;
869 <<
"#textdomain " << current_textdomain <<
"\n"
871 <<
"# This file was generated using the scenario editor.\n"
901 for (
const auto& [key, value] : atk.second.attribute_range()) {
902 if (!value.empty()) {
907 if(atk.first.any()) {
976 find_widget<scroll_text>(
"wml_view").set_label(
generated_wml);
980 std::string rel_path = find_widget<text_box>(
"path_"+id_stem).get_value();
983 if (rel_path.find(
"~") != std::string::npos) {
984 rel_path = rel_path.substr(0, rel_path.find(
"~"));
987 int scale_size = 200;
988 if (rel_path.size() > 0) {
990 float aspect_ratio =
static_cast<float>(img_size.x)/img_size.y;
991 if(img_size.x > scale_size) {
992 rel_path.append(
"~SCALE(" + std::to_string(scale_size) +
"," + std::to_string(scale_size*aspect_ratio) +
")");
993 }
else if (img_size.y > scale_size) {
994 rel_path.append(
"~SCALE(" + std::to_string(scale_size/aspect_ratio) +
"," + std::to_string(scale_size) +
")");
998 if (id_stem ==
"portrait_image") {
1000 find_widget<image>(
"unit_image").set_label(rel_path);
1002 find_widget<image>(id_stem).set_label(rel_path);
1011 if (!(std::isalnum(
c) ||
c ==
'_' ||
c ==
' ')) {
1020 grid*
grid = find_widget<tab_container>(
"tabs").get_tab_grid(0);
1025 find_widget<button>(
"ok").set_active(!
id.empty() && !name.empty() &&
check_id(
id));
1032 =
_(
"Unsaved changes will be lost. Do you want to leave?");
1043 boost::algorithm::replace_all(
unit_name,
" ",
"_");
1059 const SDL_Keycode key,
1060 SDL_Keymod modifier)
1064 const SDL_Keycode modifier_key = KMOD_GUI;
1068 const SDL_Keycode modifier_key = KMOD_CTRL;
1074 if (modifier & modifier_key) {
Class for writing a config out to a file in pieces.
void close_child(const std::string &key)
void open_child(const std::string &key)
A config object defines a single node in a WML file, with access to child nodes.
std::size_t attribute_count() const
Count the number of non-blank attributes.
config & mandatory_child(config_key_type key, int n=0)
Returns the nth child with the given key, or throws an error if there is none.
const_attr_itors attribute_range() const
config & add_child(config_key_type key)
A class grating read only view to a vector of config objects, viewed as one config with all children ...
const config & mandatory_child(config_key_type key) const
void set_values(const std::vector<::config > &values, unsigned selected=0)
Dialog that allows user to create custom unit types.
unsigned int selected_attack_
0 means there are no attacks.
void write()
Write the cfg file.
void quit_confirmation()
Quit confirmation.
bool check_id(std::string id)
Utility method to check if ID contains any invalid characters.
std::vector< config > defense_list_
boost::dynamic_bitset res_toggles_
Used to control checkboxes for various resistances, defences, etc.
const game_config_view & game_config_
std::vector< std::string > sel_abilities_
Need this because can't store macros in config.
std::vector< std::pair< boost::dynamic_bitset<>, config > > attacks_
std::vector< config > abilities_list_
void store_attack()
Callbacks for attack page.
void set_selected_from_string(menu_button &list, std::vector< config > values, std::string item)
std::vector< config > movetype_list_
std::vector< config > race_list_
void update_defenses()
Callbacks for defense list.
std::string generated_wml
Generated WML.
void save_unit_type()
Save Unit Type data to cfg.
std::vector< config > align_list_
void button_state_change()
Callback to enable/disable OK button if ID/Name is invalid.
void signal_handler_sdl_key_down(const event::ui_event, bool &handled, const SDL_Keycode key, SDL_Keymod modifier)
editor_edit_unit(const game_config_view &game_config, const std::string &addon_id)
void select_file(const std::string &default_dir, const std::string &id_stem)
Callback for file select button.
preproc_map specials_map_
boost::dynamic_bitset move_toggles_
void update_movement_costs()
Callbacks for movement list.
std::vector< config > resistances_list_
preproc_map abilities_map_
void enable_defense_slider()
void on_page_select()
Callback when an tab item in the "page" listbox is selected.
void update_resistances()
Callback for resistance list.
const std::string & addon_id_
void store_movement_costs()
void enable_movement_slider()
std::vector< config > specials_list_
void enable_resistances_slider()
void write_macro(std::ostream &out, unsigned level, const std::string macro_name)
Write macro to a stream at specified tab level.
boost::dynamic_bitset def_toggles_
void load_unit_type()
Load Unit Type data from cfg.
virtual void pre_show() override
Actions to be taken before showing the window.
void update_wml_view()
Update wml preview.
std::vector< config > usage_type_list_
void load_movetype()
Callback for loading movetype data in UI.
void update_image(const std::string &id_stem)
Callback for image update.
file_dialog & set_ok_label(const std::string &value)
Sets the OK button label.
file_dialog & set_path(const std::string &value)
Sets the initial file selection.
file_dialog & set_title(const std::string &value)
Sets the current dialog title text.
file_dialog & set_read_only(bool value)
Whether to provide user interface elements for manipulating existing objects.
std::string path() const
Gets the current file selection.
Main class to show messages to the user.
@ yes_no_buttons
Shows a yes and no button.
Abstract base class for all modal dialogs.
bool show(const unsigned auto_close_time=0)
Shows the window.
const std::string & choice() const
Unit type choice from the user.
A container widget that shows one of its pages of widgets depending on which tab the user clicked.
grid * get_tab_grid(unsigned i)
void select_tab(unsigned index)
unsigned get_active_tab_index()
std::string get_value() const
A widget that allows the user to input text in single line.
void set_retval(const int retval, const bool close_window=true)
Sets there return value of the window.
void invalidate_layout()
Updates the size of the window.
static const std::string & type()
Static type getter that does not rely on the widget being constructed.
Generic locator abstracting the location of an image.
The basic "size" of the unit - flying, small land, large land, etc.
const movement_type_map & movement_types() const
const unit_type * find(const std::string &key, unit_type::BUILD_STATUS status=unit_type::FULL) const
Finds a unit_type by its id() and makes sure it is built to the specified level.
const race_map & races() const
A single unit type that the player may recruit.
Declarations for File-IO.
static std::string _(const char *str)
const std::string wml_extension
void copy_file(const std::string &src, const std::string &dest)
Read a file and then writes it back out.
void write_file(const std::string &fname, const std::string &data, std::ios_base::openmode mode)
Throws io_exception if an error occurs.
bool to_asset_path(std::string &path, std::string addon_id, std::string asset_type)
Helper function to convert absolute path to wesnoth relative path.
std::string get_current_editor_dir(const std::string &addon_id)
Game configuration data as global variables.
REGISTER_DIALOG(editor_edit_unit)
ui_event
The event sent to the dispatcher.
void connect_signal_notify_modified(dispatcher &dispatcher, const signal_notification &signal)
Connects a signal handler for getting a notification upon modification.
void connect_signal_mouse_left_click(dispatcher &dispatcher, const signal &signal)
Connects a signal handler for a left mouse button click.
std::vector< game_tip > load(const config &cfg)
Loads the tips from a config.
void show_transient_message(const std::string &title, const std::string &message, const std::string &image, const bool message_use_markup, const bool title_use_markup)
Shows a transient message to the user.
void show_message(const std::string &title, const std::string &msg, const std::string &button_caption, const bool auto_close, const bool message_use_markup, const bool title_use_markup)
Shows a message to the user.
@ OK
Dialog was closed with the OK button.
@ CANCEL
Dialog was closed with the CANCEL button.
point get_size(const locator &i_locator, bool skip_cache)
Returns the width and height of an image.
std::string join(const T &v, const std::string &s=",")
Generates a new string joining container items in a list.
filesystem::scoped_istream preprocess_file(const std::string &fname, preproc_map *defines)
Function to use the WML preprocessor on a file.
static config unit_name(const unit *u)
void read(config &cfg, std::istream &in, abstract_validator *validator)
void write_key_val(std::ostream &out, const std::string &key, const config::attribute_value &value, unsigned level, std::string &textdomain)
An exception object used when an IO error occurs.
static std::string get_string(enum_type key)
Converts a enum to its string equivalent.
unit_type_data unit_types