16 #define GETTEXT_DOMAIN "wesnoth-editor"
36 #include <boost/regex.hpp>
43 , name(
t.user_team_name())
50 , shroud(
t.uses_shroud())
51 , share_vision(
t.share_vision())
53 , no_leader(
t.no_leader())
68 , actions_since_save_(0)
69 , starting_position_label_locs_()
70 , needs_reload_(false)
71 , needs_terrain_rebuild_(false)
72 , needs_labels_reset_(false)
73 , changed_locations_()
74 , everything_changed_(false)
79 , scenario_description_()
81 , victory_defeated_(true)
89 , game_classification_()
94 static std::string
get_map_location(
const std::string& file_contents,
const std::string& attr)
96 std::size_t attr_name_start = file_contents.find(attr);
97 if(attr_name_start == std::string::npos)
return "";
99 std::size_t attr_value_start = file_contents.find(
"=", attr_name_start);
100 std::size_t line_end = file_contents.find(
"\n", attr_name_start);
101 if(line_end < attr_value_start)
return "";
104 std::string attr_value = file_contents.substr(attr_value_start, line_end - attr_value_start);
105 std::string_view v2 = attr_value;
108 return std::string(v2);
119 , actions_since_save_(0)
120 , starting_position_label_locs_()
121 , needs_reload_(false)
122 , needs_terrain_rebuild_(false)
123 , needs_labels_reset_(false)
124 , changed_locations_()
125 , everything_changed_(false)
126 , addon_id_(addon_id)
130 , scenario_description_()
132 , victory_defeated_(true)
133 , random_time_(false)
140 , game_classification_()
188 if(file_string.empty()) {
189 std::string message =
_(
"Empty file");
198 std::string message =
_(
"File does not have .map, .cfg, or .mask extension");
205 LOG_ED <<
"Loading map or mask file";
212 if(file_string.find(
"[multiplayer]") == std::string::npos &&
213 file_string.find(
"[scenario]") == std::string::npos &&
214 file_string.find(
"[test]") == std::string::npos) {
215 LOG_ED <<
"Loading generated scenario file";
218 }
catch(
const std::exception&
e) {
226 if(!map_data_loc.empty()) {
227 if(map_data_loc.find(
"\"{") == std::string::npos) {
229 LOG_ED <<
"Loading embedded map file";
232 std::size_t
start = file_string.find(map_data_loc)+1;
233 std::size_t length = file_string.find(
"\"",
start)-
start;
234 std::string map_data = file_string.substr(
start, length);
239 const std::string& macro_argument = map_data_loc.substr(2, map_data_loc.size()-4);
240 LOG_ED <<
"Map looks like a scenario, trying {" << macro_argument <<
"}";
245 std::string message =
_(
"The map file looks like a scenario, but the map_data value does not point to an existing file")
246 + std::string(
"\n") + macro_argument;
250 LOG_ED <<
"New filename is: " << new_filename.value();
259 }
else if(!map_file_loc.empty()) {
263 if(file_string.find(
"<<") != std::string::npos) {
267 }
catch(
const std::exception&) {
276 std::string message =
_(
"The map file looks like a scenario, but the map_file value does not point to an existing file")
277 + std::string(
"\n") + new_filename;
281 LOG_ED <<
"New filename is: " << new_filename;
308 cfg[
"side"] =
teams_.size();
309 cfg[
"hidden"] =
false;
318 assert(
teams_.size() >=
static_cast<unsigned int>(
info.side));
323 t.have_leader(!
info.no_leader);
324 t.change_controller(
info.controller);
325 t.set_gold(
info.gold);
326 t.set_base_income(
info.income);
327 t.set_hidden(
info.hidden);
329 t.set_shroud(
info.shroud);
330 t.set_share_vision(
info.share_vision);
331 t.set_village_gold(
info.village_income);
332 t.set_village_support(
info.village_support);
338 const std::string& name,
339 const std::string& description,
342 bool victory_defeated,
391 std::string map_data = multiplayer[
"map_data"];
395 if(!map_data.empty()) {
406 ERR_ED <<
"Cannot convert " <<
filename_ <<
" due to missing map_data attribute.";
411 event[
"name"] =
"prestart";
412 event[
"id"] =
"editor_event-prestart";
423 if(child_key !=
"side" && child_key !=
"time") {
424 config&
c =
event.add_child(child_key);
425 c.append_attributes(child_cfg);
426 c.append_children(child_cfg);
427 }
else if(child_key ==
"side") {
429 c.append_attributes(child_cfg);
430 for(
const auto [side_key, side_cfg] : child_cfg.all_children_view()) {
431 if(side_key ==
"village") {
432 config& c1 =
c.add_child(
"village");
437 if(side_key ==
"unit") {
438 c1[
"side"] = child_cfg[
"side"];
442 }
else if(child_key ==
"time") {
444 c.append_attributes(child_cfg);
459 }
else if(scen.
has_child(
"multiplayer")) {
464 ERR_ED <<
"Found no [scenario], [multiplayer], or [test] tag in " <<
filename_ <<
", assuming old-style editor scenario and defaulting to [multiplayer]";
474 xp_mod_ = experience_modifier->to_int();
477 random_time_ = scenario[
"random_start_time"].to_bool(
false);
479 if(!scenario[
"map_data"].str().empty()) {
481 }
else if(!scenario[
"map_file"].str().empty()) {
490 if(!side[
"recruit"].str().empty()) {
497 auto event = scenario.
find_child(
"event",
"id",
"editor_event-start");
499 event = scenario.
find_child(
"event",
"id",
"editor_event-prestart");
502 config& evt =
event.value();
547 LOG_ED <<
"Attempted to draw terrain off the map (" <<
loc <<
")";
553 if(terrain != old_terrain) {
556 }
else if(one_layer_only) {
632 std::string current_textdomain =
"wesnoth-"+
addon_id_;
654 return cfg[
"id"].str() ==
"editor_event-start" || cfg[
"id"].str() ==
"editor_event-prestart";
662 scenario[
"experience_modifier"] = *
xp_mod_;
675 event[
"name"] =
"prestart";
676 event[
"id"] =
"editor_event-prestart";
677 event[
"priority"] = 1000;
686 if(scenario[
"turns"].to_int() == -1) {
689 scenario[
"turns"] = times[
"turns"];
697 config&
t =
event.add_child(
"time_area");
705 for(
const auto& overlay_pair :
overlays_) {
706 for(
const overlay& o : overlay_pair.second) {
710 overlay_pair.first.write(item);
713 item[
"image"] = o.
image;
717 item[
"id"].write_if_not_empty(o.
id);
718 item[
"name"].write_if_not_empty(
t_string(o.
name, current_textdomain));
719 item[
"team_name"].write_if_not_empty(o.
team_name);
720 item[
"halo"].write_if_not_empty(o.
halo);
729 track.second.write(event,
true);
747 if(!boost::regex_match(
unit.
id(), boost::regex(
".*-[0-9]+"))) {
784 side[
"faction"] =
"Custom";
795 village.write(side.
add_child(
"village"));
806 std::string current_textdomain =
"wesnoth-"+
addon_id_;
823 symbols[
"msg"] =
e.what();
824 const std::string
msg =
VGETTEXT(
"Could not save time schedule: $msg", symbols);
830 editor_times[
"id"] = schedule_id;
831 editor_times[
"name"] =
t_string(schedule_name, current_textdomain);
840 std::stringstream wml_stream;
843 <<
"#textdomain " << current_textdomain <<
"\n"
845 <<
"# This file was generated using the scenario editor.\n"
847 <<
"#ifdef EDITOR\n";
854 wml_stream <<
"#endif";
856 if(!wml_stream.str().empty()) {
863 symbols[
"msg"] =
e.what();
864 const std::string
msg =
VGETTEXT(
"Could not save time schedule: $msg", symbols);
882 std::stringstream wml_stream;
884 <<
"# This file was generated using the scenario editor.\n"
886 <<
"# If you edit this file by hand, then do not use macros.\n"
887 <<
"# The editor doesn't support macros, and so using them will result in only being able to edit the map.\n"
888 <<
"# Additionally, the contents of all [side] and [time] tags as well as any events that have an id starting with 'editor_event-' are replaced entirely.\n"
889 <<
"# Any manual changes made to those will be lost.\n"
896 if(!wml_stream.str().empty()) {
903 symbols[
"msg"] =
e.what();
904 const std::string
msg =
VGETTEXT(
"Could not save the scenario: $msg", symbols);
923 boost::regex rexpression_map_data(R
"((.*map_data\s*=\s*")(.+?)(".*))");
924 boost::smatch matched_map_data;
926 if(boost::regex_search(map_string, matched_map_data, rexpression_map_data,
927 boost::regex_constants::match_not_dot_null)) {
928 std::stringstream ss;
929 ss << matched_map_data[1];
931 ss << matched_map_data[3];
944 symbols[
"msg"] =
e.what();
945 const std::string
msg =
VGETTEXT(
"Could not save the map: $msg", symbols);
964 LOG_ED <<
"Performing action " << action.
get_id() <<
": " << action.
get_name() <<
", actions count is "
984 LOG_ED <<
"Performing (partial) action " << action.
get_id() <<
": " << action.
get_name() <<
", actions count is "
991 if(undo_chain ==
nullptr) {
1056 WRN_ED <<
"undo() called with an empty undo stack";
1070 WRN_ED <<
"redo() called with an empty redo stack";
1084 if(undo_chain ==
nullptr) {
1091 if(undo_chain->
empty()) {
1096 redo_stack_.emplace_back(first_action_in_chain->perform(*
this));
1115 assert(!from.empty());
1117 std::unique_ptr<editor_action> action;
1118 action.swap(from.back());
1122 auto reverse_action = action->perform(*
this);
1123 to.emplace_back(std::move(reverse_action));
Variant for storing WML attributes.
Class for writing a config out to a file in pieces.
void write(const config &cfg)
A config object defines a single node in a WML file, with access to child nodes.
void append(const config &cfg)
Append data from another config object to this one.
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.
std::size_t child_count(config_key_type key) const
auto all_children_view() const
In-order iteration over all children.
optional_config_impl< config > find_child(config_key_type key, const std::string &name, const std::string &value)
Returns the first child of tag key with a name attribute containing value.
bool has_child(config_key_type key) const
Determine whether a config has a child or not.
void remove_children(config_key_type key, const std::function< bool(const config &)> &p={})
Removes all children with tag key for which p returns true.
child_itors child_range(config_key_type key)
void append_attributes(const config &cfg)
Adds attributes from cfg.
void remove_attribute(config_key_type key)
const attribute_value * get(config_key_type key) const
Returns a pointer to the attribute with the given key or nullptr if it does not exist.
config & add_child(config_key_type key)
Sort-of-Singleton that many classes, both GUI and non-GUI, use to access the game data.
Container action wrapping several actions into one.
std::unique_ptr< editor_action > pop_first_action()
Remove the first added action and return it, transferring ownership to the caller.
void prepend_action(std::unique_ptr< editor_action > a)
Add an action at the beginning of the chain.
Base class for all editor actions.
int get_id() const
Debugging aid.
virtual const std::string & get_name() const
virtual std::unique_ptr< editor_action > perform(map_context &) const
Perform the action, returning an undo action that, when performed, shall reverse any effects of this ...
static int get_instance_count()
Debugging aid.
This class adds extra editor-specific functionality to a normal gamemap.
bool set_selection(const std::set< map_location > &area)
Select the given area.
std::set< map_location > set_starting_position_labels(display &disp)
Set labels for staring positions in the given display object.
static editor_map from_string(const std::string &data)
Wrapper around editor_map(cfg, data) that catches possible exceptions and wraps them in a editor_map_...
std::set< map_location > starting_position_label_locs_
Cache of set starting position labels.
std::string filename_
The actual filename of this map.
utils::optional< config > previous_cfg_
void perform_partial_action(const editor_action &action)
Performs a partial action, assumes that the top undo action has been modified to maintain coherent st...
bool pure_map_
Whether the map context refers to a file containing only the pure map data.
action_stack redo_stack_
The redo stack.
std::unique_ptr< tod_manager > tod_manager_
bool embedded_
Whether the map context refers to a map embedded in a scenario file.
void set_needs_labels_reset(bool value=true)
Setter for the labels reset flag.
void set_needs_reload(bool value=true)
Setter for the reload flag.
std::string scenario_name_
void clear_starting_position_labels(display &disp)
editor_map map_
The map object of this map_context.
void new_side()
Adds a new side to the map.
static const std::size_t max_action_stack_size_
Action stack (i.e.
action_stack undo_stack_
The undo stack.
editor_action * last_undo_action()
void redo()
Re-does a previously undid action, and puts it back in the undo stack.
std::set< map_location > changed_locations_
void set_starting_time(int time)
void draw_terrain(const t_translation::terrain_code &terrain, const map_location &loc, bool one_layer_only=false)
Draw a terrain on a single location on the map.
void set_map(const editor_map &map)
bool select_area(int index)
Select the nth tod area.
void set_side_setup(editor_team_info &info)
void trim_stack(action_stack &stack)
Checks if an action stack reached its capacity and removes the front element if so.
int actions_since_save_
Number of actions performed since the map was saved.
std::string scenario_description_
void save_scenario()
Saves the scenario under the current filename.
std::vector< team > teams_
void perform_action_between_stacks(action_stack &from, action_stack &to)
Perform an action at the back of one stack, and then move it to the back of the other stack.
void perform_action(const editor_action &action)
Performs an action (thus modifying the map).
void undo()
Un-does the last action, and puts it in the redo stack for a possible redo.
void replace_schedule(const std::vector< time_of_day > &schedule)
utils::optional< bool > victory_defeated_
void remove_area(int index)
void clear_undo_redo()
Clear the undo and redo stacks.
virtual ~map_context()
Map context destructor.
void draw_terrain_actual(const t_translation::terrain_code &terrain, const map_location &loc, bool one_layer_only=false)
Actual drawing function used by both overloaded variants of draw_terrain.
void clear_changed_locations()
map_context(const map_context &)=delete
utils::optional< int > xp_mod_
void add_to_recent_files()
Adds the map to the editor's recent files list.
void replace_local_schedule(const std::vector< time_of_day > &schedule)
Replace the [time]s of the currently active area.
void save_map()
Saves the map under the current filename.
void reset_starting_position_labels(display &disp)
editor_action * last_redo_action()
void partial_undo()
Un-does a single step from a undo action chain.
void set_everything_changed()
virtual const editor_map & map() const override
Const map accessor.
void set_needs_terrain_rebuild(bool value=true)
Setter for the terrain rebuild flag.
config convert_scenario(const config &old_scenario)
Convert an old-style editor scenario config to a config with a top level [multiplayer] tag.
bool everything_changed() const
const t_string get_default_context_name() const
const std::string & get_filename() const
void set_starting_position_labels(display &disp)
void clear_modified()
Clear the modified state.
bool victory_defeated() const
void set_scenario_setup(const std::string &id, const std::string &name, const std::string &description, int turns, int xp_mod, bool victory_defeated, bool random_time)
void add_changed_location(const map_location &loc)
void save_schedule(const std::string &schedule_id, const std::string &schedule_name)
Save custom time of day schedule in the utils directory.
A class grating read only view to a vector of config objects, viewed as one config with all children ...
terrain_code get_terrain(const map_location &loc) const
Looks up terrain at a particular location.
int w() const
Effective map width.
int h() const
Effective map height.
bool on_board_with_border(const map_location &loc) const
std::string write() const
const terrain_type & get_terrain_info(const t_translation::terrain_code &terrain) const
void set_terrain(const map_location &loc, const terrain_code &terrain, const terrain_type_data::merge_mode mode=terrain_type_data::BOTH, bool replace_if_failed=false) override
Clobbers over the terrain at location 'loc', with the given terrain.
@ auto_close
Enables auto close.
void write(config &res) const
void read(const config &cfg)
void add_recent_files_entry(const std::string &path)
Adds an entry to the recent files list.
Internal representation of music tracks.
This class stores all the data for a single 'side' (in game nomenclature).
const std::string & team_name() const
team_shared_vision::type share_vision() const
const std::set< map_location > & villages() const
side_controller::type controller() const
const std::set< std::string > & recruits() const
const t_string & user_team_name() const
t_translation::terrain_code terrain_with_default_base() const
Return the overlay part of this terrain, on the default_base().
umap_retval_pair_t insert(const unit_ptr &p)
Inserts the unit pointed to by p into the map.
This class represents a single unit of a specific type.
static unit_ptr create(const config &cfg, bool use_traits=false, const vconfig *vcfg=nullptr)
Initializes a unit from a config.
map_display and display: classes which take care of displaying the map and game-data on the screen.
lg::log_domain log_editor
Declarations for File-IO.
static std::string _(const char *str)
bool unrenamable() const
Whether this unit can be renamed.
const std::string & type_id() const
The id of this unit's type.
bool can_recruit() const
Whether this unit can recruit other units - ie, are they a leader unit.
const std::string & id() const
Gets this unit's id.
int side() const
The side this unit belongs to.
const t_string & name() const
Gets this unit's translatable display name.
const map_location & get_location() const
The current map location this unit is at.
map_location::direction facing() const
The current direction this unit is facing within its hex.
bool loyal() const
Gets whether this unit is loyal - ie, it costs no upkeep.
std::string id
Text to match against addon_info.tags()
#define log_scope2(domain, description)
std::map< std::string, config > traits_map
Manage the empty-palette in the editor.
static std::string get_map_location(const std::string &file_contents, const std::string &attr)
std::deque< std::unique_ptr< editor_action > > action_stack
Action stack typedef.
EXIT_STATUS start(bool clear_id, const std::string &filename, bool take_screenshot, const std::string &screenshot_filename)
Main interface for launching the editor from the title screen.
bool is_cfg(const std::string &filename)
Returns true if the file ends with the wmlfile extension.
std::string base_name(const std::string &file, const bool remove_extension)
Returns the base filename of a file, with directory name stripped.
static bool file_exists(const bfs::path &fpath)
bool is_directory(const std::string &fname)
Returns true if the given file is a directory.
utils::optional< std::string > get_wml_location(const std::string &path, const utils::optional< std::string > ¤t_dir)
Returns a translated path to the actual file or directory, if it exists.
std::string read_file(const std::string &fname)
Basic disk I/O - read file.
const std::string map_extension
bool is_mask(const std::string &filename)
Returns true if the file ends with the maskfile extension.
void write_file(const std::string &fname, const std::string &data, std::ios_base::openmode mode)
Throws io_exception if an error occurs.
std::string get_short_wml_path(const std::string &filename)
Returns a short path to filename, skipping the (user) data directory.
std::string directory_name(const std::string &file)
Returns the directory name of a file, with filename stripped.
bool is_map(const std::string &filename)
Returns true if the file ends with the mapfile extension.
std::string get_next_filename(const std::string &name, const std::string &extension)
Get the next free filename using "name + number (3 digits) + extension" maximum 1000 files then start...
std::string get_current_editor_dir(const std::string &addon_id)
Game configuration data as global variables.
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.
::tod_manager * tod_manager
std::size_t index(std::string_view str, const std::size_t index)
Codepoint index corresponding to the nth character in a UTF-8 string.
void trim(std::string_view &s)
std::set< std::string > split_set(std::string_view s, char sep, const int flags)
std::string join(const T &v, const std::string &s=",")
Generates a new string joining container items in a list.
std::map< std::string, t_string > string_map
static void msg(const char *act, debug_info &i, const char *to="", const char *result="")
std::string preprocess_string(const std::string &contents, preproc_map *defines, const std::string &textdomain)
Function to use the WML preprocessor on a string.
filesystem::scoped_istream preprocess_file(const std::string &fname, preproc_map *defines)
Function to use the WML preprocessor on a file.
std::map< std::string, struct preproc_define > preproc_map
void read(config &cfg, std::istream &in, abstract_validator *validator)
std::string filename
Filename.
editor_team_info(const team &t)
An exception object used when an IO error occurs.
Encapsulates the map of the game.
static std::string write_direction(direction dir)
void write(config &cfg) const
static std::string get_string(enum_type key)
Converts a enum to its string equivalent.
A terrain string which is converted to a terrain is a string with 1 or 2 layers the layers are separa...