18 #define GETTEXT_DOMAIN "wesnoth-lib"
49 #include <boost/algorithm/string/replace.hpp>
50 #include <boost/filesystem.hpp>
73 if (x.first.find(
"INTERNAL") == std::string::npos) {
78 connect_signal<event::SDL_KEY_DOWN>(std::bind(
83 tab_container& tabs = find_widget<tab_container>(&win,
"tabs",
false);
92 menu_button& alignments = find_widget<menu_button>(&win,
"alignment_list",
false);
100 menu_button& races = find_widget<menu_button>(&win,
"race_list",
false);
102 const std::string& race_name =
i.second.id();
110 button&
load = find_widget<button>(&win,
"load_unit_type",
false);
122 find_widget<button>(&win,
"browse_unit_image",
false),
125 find_widget<button>(&win,
"preview_unit_image",
false),
128 find_widget<button>(&win,
"browse_portrait_image",
false),
131 find_widget<button>(&win,
"preview_portrait_image",
false),
135 find_widget<text_box>(&win,
"name_box",
false),
138 find_widget<text_box>(&win,
"id_box",
false),
146 menu_button& movetypes = find_widget<menu_button>(&win,
"movetype_list",
false);
155 menu_button& defenses = find_widget<menu_button>(&win,
"defense_list",
false);
169 menu_button& movement_costs = find_widget<menu_button>(&win,
"movement_costs_list",
false);
175 menu_button& resistances = find_widget<menu_button>(&win,
"resistances_list",
false);
190 menu_button& usage_types = find_widget<menu_button>(&win,
"usage_list",
false);
198 multimenu_button& abilities = find_widget<multimenu_button>(&win,
"abilities_list",
false);
202 find_widget<button>(&win,
"browse_small_profile_image",
false),
205 find_widget<button>(&win,
"preview_small_profile_image",
false),
208 find_widget<button>(&win,
"load_movetype",
false),
211 find_widget<slider>(&win,
"resistances_slider",
false),
214 find_widget<menu_button>(&win,
"resistances_list",
false),
217 find_widget<toggle_button>(&win,
"resistances_checkbox",
false),
221 find_widget<slider>(&win,
"defense_slider",
false),
224 find_widget<menu_button>(&win,
"defense_list",
false),
227 find_widget<toggle_button>(&win,
"defense_checkbox",
false),
231 find_widget<slider>(&win,
"movement_costs_slider",
false),
234 find_widget<menu_button>(&win,
"movement_costs_list",
false),
237 find_widget<toggle_button>(&win,
"movement_costs_checkbox",
false),
256 multimenu_button& specials = find_widget<multimenu_button>(&win,
"weapon_specials_list",
false);
259 range_group.
add_member(find_widget<toggle_button>(&win,
"range_melee",
false,
true),
"melee");
260 range_group.
add_member(find_widget<toggle_button>(&win,
"range_ranged",
false,
true),
"ranged");
263 menu_button& attack_types = find_widget<menu_button>(&win,
"attack_type_list",
false);
270 find_widget<button>(&win,
"browse_attack_image",
false),
273 find_widget<button>(&win,
"preview_attack_image",
false),
276 find_widget<menu_button>(&win,
"atk_list",
false),
279 find_widget<button>(&win,
"atk_new",
false),
282 find_widget<button>(&win,
"atk_delete",
false),
285 find_widget<button>(&win,
"atk_next",
false),
288 find_widget<button>(&win,
"atk_prev",
false),
319 std::string dn = dlg.
path();
321 =
_(
"This file is outside Wesnoth's data dirs. Do you wish to copy it into your add-on?");
323 if(id_stem ==
"unit_image") {
331 }
else if((id_stem ==
"portrait_image")||(id_stem ==
"small_profile_image")) {
339 }
else if(id_stem ==
"attack_image") {
349 find_widget<text_box>(
get_window(),
"path_"+id_stem,
false).set_value(dn);
362 find_widget<text_box>(
get_window(),
"id_box",
false).set_value(
type->id());
363 find_widget<text_box>(
get_window(),
"name_box",
false).set_value(
type->type_name().base_str());
364 find_widget<spinner>(
get_window(),
"level_box",
false).set_value(
type->level());
365 find_widget<slider>(
get_window(),
"cost_slider",
false).set_value(
type->cost());
367 find_widget<slider>(
get_window(),
"hp_slider",
false).set_value(
type->hitpoints());
368 find_widget<slider>(
get_window(),
"xp_slider",
false).set_value(
type->experience_needed());
369 find_widget<slider>(
get_window(),
"move_slider",
false).set_value(
type->movement());
370 find_widget<scroll_text>(
get_window(),
"desc_box",
false).set_value(
type->unit_description().base_str());
372 find_widget<text_box>(
get_window(),
"path_unit_image",
false).set_value(
type->image());
373 find_widget<text_box>(
get_window(),
"path_portrait_image",
false).set_value(
type->big_profile());
375 for (
const auto& gender :
type->genders())
377 if (gender == unit_race::GENDER::MALE) {
378 find_widget<toggle_button>(
get_window(),
"gender_male",
false).set_value(
true);
381 if (gender == unit_race::GENDER::FEMALE) {
382 find_widget<toggle_button>(
get_window(),
"gender_female",
false).set_value(
true);
387 find_widget<menu_button>(
get_window(),
"race_list",
false),
392 find_widget<menu_button>(
get_window(),
"alignment_list",
false),
399 find_widget<text_box>(
get_window(),
"path_small_profile_image",
false).set_value(
type->small_profile());
402 find_widget<menu_button>(
get_window(),
"movetype_list",
false),
404 type->movement_type_id());
407 type->movement_type().write(cfg,
false);
414 if (!
type->get_cfg().has_child(
"resistance")) {
418 for (
const auto& attr :
type->get_cfg().mandatory_child(
"resistance").attribute_range()) {
426 if (
type->get_cfg().has_child(
"defense")) {
427 for (
const auto& attr :
type->get_cfg().mandatory_child(
"defense").attribute_range()) {
434 if (
type->get_cfg().has_child(
"movement_costs")) {
435 for (
const auto& attr :
type->get_cfg().mandatory_child(
"movement_costs").attribute_range()) {
448 find_widget<menu_button>(
get_window(),
"usage_list",
false),
456 for(
const auto& atk :
type->attacks())
460 attack[
"name"] = atk.id();
461 attack[
"description"] = atk.name().base_str();
462 attack[
"icon"] = atk.icon();
463 attack[
"range"] = atk.range();
464 attack[
"damage"] = atk.damage();
465 attack[
"number"] = atk.num_attacks();
466 attack[
"type"] = atk.type();
467 attacks_.push_back(std::make_pair(enabled, attack));
487 std::string current_textdomain =
"wesnoth-"+
addon_id_;
496 utype[
"id"] = find_widget<text_box>(
grid,
"id_box",
false).get_value();
497 utype[
"name"] =
t_string(find_widget<text_box>(
grid,
"name_box",
false).get_value(), current_textdomain);
498 utype[
"image"] = find_widget<text_box>(
grid,
"path_unit_image",
false).get_value();
499 utype[
"profile"] = find_widget<text_box>(
grid,
"path_portrait_image",
false).get_value();
500 utype[
"level"] = find_widget<spinner>(
grid,
"level_box",
false).get_value();
501 utype[
"advances_to"] = find_widget<text_box>(
grid,
"adv_box",
false).get_value();
502 utype[
"hitpoints"] = find_widget<slider>(
grid,
"hp_slider",
false).get_value();
503 utype[
"experience"] = find_widget<slider>(
grid,
"xp_slider",
false).get_value();
504 utype[
"cost"] = find_widget<slider>(
grid,
"cost_slider",
false).get_value();
505 utype[
"movement"] = find_widget<slider>(
grid,
"move_slider",
false).get_value();
506 utype[
"description"] =
t_string(find_widget<scroll_text>(
grid,
"desc_box",
false).get_value(), current_textdomain);
507 utype[
"race"] = find_widget<menu_button>(
grid,
"race_list",
false).get_value_string();
508 utype[
"alignment"] = find_widget<menu_button>(
grid,
"alignment_list",
false).get_value_string();
511 if (find_widget<toggle_button>(
grid,
"gender_male",
false).get_value()) {
512 if (find_widget<toggle_button>(
grid,
"gender_female",
false).get_value()) {
513 utype[
"gender"] =
"male,female";
515 utype[
"gender"] =
"male";
518 if (find_widget<toggle_button>(
grid,
"gender_female",
false).get_value()) {
519 utype[
"gender"] =
"female";
526 utype[
"small_profile"] = find_widget<text_box>(
grid,
"path_small_profile_image",
false).get_value();
527 utype[
"movement_type"] = find_widget<menu_button>(
grid,
"movetype_list",
false).get_value_string();
528 utype[
"usage"] = find_widget<menu_button>(
grid,
"usage_list",
false).get_value_string();
546 defenses[attr.first] =
defenses_[attr.first];
557 movement_costs[attr.first] =
movement_[attr.first];
563 const auto& abilities_states = find_widget<multimenu_button>(
grid,
"abilities_list",
false).get_toggle_states();
564 if (abilities_states.any()) {
568 if (
i >= abilities_states.size()) {
572 if (abilities_states[
i] ==
true) {
584 find_widget<slider>(
get_window(),
"resistances_slider",
false)
586 100 -
resistances_[find_widget<menu_button>(
get_window(),
"resistances_list",
false).get_value_string()]);
588 find_widget<slider>(
get_window(),
"resistances_slider",
false)
591 find_widget<toggle_button>(
get_window(),
"resistances_checkbox",
false)
597 = 100 - find_widget<slider>(
get_window(),
"resistances_slider",
false).get_value();
601 bool toggle = find_widget<toggle_button>(
get_window(),
"resistances_checkbox",
false).get_value();
603 find_widget<slider>(
get_window(),
"resistances_slider",
false).set_active(toggle);
607 find_widget<slider>(
get_window(),
"defense_slider",
false)
609 100 -
defenses_[find_widget<menu_button>(
get_window(),
"defense_list",
false).get_value_string()]);
611 find_widget<slider>(
get_window(),
"defense_slider",
false)
614 find_widget<toggle_button>(
get_window(),
"defense_checkbox",
false)
619 defenses_[find_widget<menu_button>(
get_window(),
"defense_list",
false).get_value_string()]
620 = 100 - find_widget<slider>(
get_window(),
"defense_slider",
false).get_value();
624 bool toggle = find_widget<toggle_button>(
get_window(),
"defense_checkbox",
false).get_value();
626 find_widget<slider>(
get_window(),
"defense_slider",
false).set_active(toggle);
630 find_widget<slider>(
get_window(),
"movement_costs_slider",
false)
632 movement_[find_widget<menu_button>(
get_window(),
"movement_costs_list",
false).get_value_string()]);
634 find_widget<slider>(
get_window(),
"movement_costs_slider",
false)
637 find_widget<toggle_button>(
get_window(),
"movement_costs_checkbox",
false)
642 movement_[find_widget<menu_button>(
get_window(),
"movement_costs_list",
false).get_value_string()]
643 = find_widget<slider>(
get_window(),
"movement_costs_slider",
false).get_value();
647 bool toggle = find_widget<toggle_button>(
get_window(),
"movement_costs_checkbox",
false).get_value();
649 find_widget<slider>(
get_window(),
"movement_costs_slider",
false).set_active(toggle);
654 std::string current_textdomain =
"wesnoth-"+
addon_id_;
667 attack[
"name"] = find_widget<text_box>(
get_window(),
"atk_id_box",
false).get_value();
668 attack[
"description"] =
t_string(find_widget<text_box>(
get_window(),
"atk_name_box",
false).get_value(), current_textdomain);
669 attack[
"icon"] = find_widget<text_box>(
get_window(),
"path_attack_image",
false).get_value();
670 attack[
"type"] = find_widget<menu_button>(
get_window(),
"attack_type_list",
false).get_value_string();
671 attack[
"damage"] = find_widget<slider>(
get_window(),
"dmg_box",
false).get_value();
672 attack[
"number"] = find_widget<slider>(
get_window(),
"dmg_num_box",
false).get_value();
673 if (find_widget<toggle_button>(
get_window(),
"range_melee",
false).get_value()) {
674 attack[
"range"] =
"melee";
676 if (find_widget<toggle_button>(
get_window(),
"range_ranged",
false).get_value()) {
677 attack[
"range"] =
"ranged";
693 find_widget<text_box>(
get_window(),
"atk_id_box",
false).set_value(attack[
"name"]);
694 find_widget<text_box>(
get_window(),
"atk_name_box",
false).set_value(attack[
"description"]);
695 find_widget<text_box>(
get_window(),
"path_attack_image",
false).set_value(attack[
"icon"]);
697 find_widget<slider>(
get_window(),
"dmg_box",
false).set_value(attack[
"damage"]);
698 find_widget<slider>(
get_window(),
"dmg_num_box",
false).set_value(attack[
"number"]);
700 if (attack[
"range"] ==
"melee") {
701 find_widget<toggle_button>(
get_window(),
"range_melee",
false).set_value(
true);
702 find_widget<toggle_button>(
get_window(),
"range_ranged",
false).set_value(
false);
704 if (attack[
"range"] ==
"ranged") {
705 find_widget<toggle_button>(
get_window(),
"range_melee",
false).set_value(
false);
706 find_widget<toggle_button>(
get_window(),
"range_ranged",
false).set_value(
true);
710 find_widget<menu_button>(
get_window(),
"attack_type_list",
false),
714 find_widget<multimenu_button>(
get_window(),
"weapon_specials_list",
false)
726 find_widget<button>(
get_window(),
"atk_prev",
false).set_active(
false);
728 find_widget<button>(
get_window(),
"atk_prev",
false).set_active(
true);
732 find_widget<button>(
get_window(),
"atk_delete",
false).set_active(
true);
734 find_widget<button>(
get_window(),
"atk_delete",
false).set_active(
false);
738 find_widget<button>(
get_window(),
"atk_next",
false).set_active(
false);
740 find_widget<button>(
get_window(),
"atk_next",
false).set_active(
true);
744 std::vector<config> atk_name_list;
745 for(
const auto& atk_data :
attacks_) {
746 atk_name_list.emplace_back(
"label", atk_data.second[
"name"]);
755 find_widget<label>(
get_window(),
"atk_number",
false).set_label(new_index_str);
762 std::string current_textdomain =
"wesnoth-"+
addon_id_;
770 attack[
"name"] = find_widget<text_box>(
get_window(),
"atk_id_box",
false).get_value();
771 attack[
"description"] =
t_string(find_widget<text_box>(
get_window(),
"atk_name_box",
false).get_value(), current_textdomain);
772 attack[
"icon"] = find_widget<text_box>(
get_window(),
"path_attack_image",
false).get_value();
773 attack[
"type"] = find_widget<menu_button>(
get_window(),
"attack_type_list",
false).get_value_string();
774 attack[
"damage"] = find_widget<slider>(
get_window(),
"dmg_box",
false).get_value();
775 attack[
"number"] = find_widget<slider>(
get_window(),
"dmg_num_box",
false).get_value();
776 if (find_widget<toggle_button>(
get_window(),
"range_melee",
false).get_value()) {
777 attack[
"range"] =
"melee";
779 if (find_widget<toggle_button>(
get_window(),
"range_ranged",
false).get_value()) {
780 attack[
"range"] =
"ranged";
787 find_widget<multimenu_button>(
get_window(),
"weapon_specials_list",
false).get_toggle_states()
808 find_widget<button>(
get_window(),
"atk_delete",
false).set_active(
false);
863 .mandatory_child(
"units")
864 .child_range(
"movetype")) {
865 if (
movetype[
"name"] == find_widget<menu_button>(
get_window(),
"movetype_list",
false).get_value_string()) {
887 out <<
"{" << macro_name <<
"}\n";
897 std::stringstream wml_stream;
900 std::string current_textdomain =
"wesnoth-"+
addon_id_;
903 <<
"#textdomain " << current_textdomain <<
"\n"
905 <<
"# This file was generated using the scenario editor.\n"
935 for (
const auto& attr : atk.second.attribute_range()) {
936 if (!attr.second.empty()) {
941 if(atk.first.any()) {
1014 std::string rel_path = find_widget<text_box>(
get_window(),
"path_"+id_stem,
false).get_value();
1017 if (rel_path.find(
"~") != std::string::npos) {
1018 rel_path = rel_path.substr(0, rel_path.find(
"~"));
1021 int scale_size = 200;
1022 if (rel_path.size() > 0) {
1024 float aspect_ratio =
static_cast<float>(img_size.x)/img_size.y;
1025 if(img_size.x > scale_size) {
1026 rel_path.append(
"~SCALE(" + std::to_string(scale_size) +
"," + std::to_string(scale_size*aspect_ratio) +
")");
1027 }
else if (img_size.y > scale_size) {
1028 rel_path.append(
"~SCALE(" + std::to_string(scale_size/aspect_ratio) +
"," + std::to_string(scale_size) +
")");
1032 if (id_stem ==
"portrait_image") {
1034 find_widget<image>(
get_window(),
"unit_image",
false).set_label(rel_path);
1036 find_widget<image>(
get_window(), id_stem,
false).set_label(rel_path);
1045 if (!(std::isalnum(
c) ||
c ==
'_' ||
c ==
' ')) {
1056 std::string
id = find_widget<text_box>(tabs.
get_tab_grid(0),
"id_box",
false).get_value();
1057 std::string name = find_widget<text_box>(tabs.
get_tab_grid(0),
"name_box",
false).get_value();
1064 find_widget<button>(
get_window(),
"ok",
false).set_active(
false);
1066 find_widget<button>(
get_window(),
"ok",
false).set_active(
true);
1077 boost::algorithm::replace_all(
unit_name,
" ",
"_");
1093 const SDL_Keycode key,
1094 SDL_Keymod modifier)
1098 const SDL_Keycode modifier_key = KMOD_GUI;
1102 const SDL_Keycode modifier_key = KMOD_CTRL;
1108 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
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.
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)
Utility method to set state of menu_button from a string.
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.
void update_wml_view()
Update wml preview.
std::vector< config > usage_type_list_
virtual void pre_show(window &window) override
Actions to be taken before showing the window.
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.
window * get_window()
Returns a pointer to the dialog's window.
const std::string & choice() const
Unit type choice from the user.
void add_member(selectable_item *w, const T &value)
Adds a widget/value pair to the group map.
void set_member_states(const T &value)
Sets the toggle values for all widgets besides the one associated with the specified value to false.
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()
base class of top level items, the only item which needs to store the final canvases to draw on.
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.
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