16 #define GETTEXT_DOMAIN "wesnoth-editor"
36 #include <boost/regex.hpp>
43 , name(
t.user_team_name())
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");
195 std::string message =
_(
"File does not have .map or .cfg extension");
201 LOG_ED <<
"Loading map or mask file";
208 if(file_string.find(
"[multiplayer]") == std::string::npos &&
209 file_string.find(
"[scenario]") == std::string::npos &&
210 file_string.find(
"[test]") == std::string::npos) {
211 LOG_ED <<
"Loading generated scenario file";
214 }
catch(
const std::exception&
e) {
222 if(!map_data_loc.empty()) {
223 if(map_data_loc.find(
"\"{") == std::string::npos) {
225 LOG_ED <<
"Loading embedded map file";
228 std::size_t
start = file_string.find(map_data_loc)+1;
229 std::size_t length = file_string.find(
"\"",
start)-
start;
230 std::string map_data = file_string.substr(
start, length);
235 const std::string& macro_argument = map_data_loc.substr(2, map_data_loc.size()-4);
236 LOG_ED <<
"Map looks like a scenario, trying {" << macro_argument <<
"}";
240 if(new_filename.empty()) {
241 std::string message =
_(
"The map file looks like a scenario, but the map_data value does not point to an existing file")
242 + std::string(
"\n") + macro_argument;
246 LOG_ED <<
"New filename is: " << new_filename;
255 }
else if(!map_file_loc.empty()) {
259 if(file_string.find(
"<<") != std::string::npos) {
263 }
catch(
const std::exception&) {
272 std::string message =
_(
"The map file looks like a scenario, but the map_file value does not point to an existing file")
273 + std::string(
"\n") + new_filename;
277 LOG_ED <<
"New filename is: " << new_filename;
304 cfg[
"side"] =
teams_.size();
305 cfg[
"hidden"] =
false;
314 assert(
teams_.size() >=
static_cast<unsigned int>(
info.side));
319 t.have_leader(!
info.no_leader);
320 t.change_controller(
info.controller);
321 t.set_gold(
info.gold);
322 t.set_base_income(
info.income);
323 t.set_hidden(
info.hidden);
325 t.set_shroud(
info.shroud);
326 t.set_share_vision(
info.share_vision);
327 t.set_village_gold(
info.village_income);
328 t.set_village_support(
info.village_support);
334 const std::string& name,
335 const std::string& description,
338 bool victory_defeated,
387 std::string map_data = multiplayer[
"map_data"];
391 if(!map_data.empty()) {
402 ERR_ED <<
"Cannot convert " <<
filename_ <<
" due to missing map_data attribute.";
407 event[
"name"] =
"start";
408 event[
"id"] =
"editor_event-start";
419 if(child.key !=
"side" && child.key !=
"time") {
420 config&
c =
event.add_child(child.key);
421 c.append_attributes(child.cfg);
422 c.append_children(child.cfg);
423 }
else if(child.key ==
"side") {
425 c.append_attributes(child.cfg);
427 if(side_child.key ==
"village") {
428 config& c1 =
c.add_child(
"village");
433 if(side_child.key ==
"unit") {
434 c1[
"side"] = child.cfg[
"side"];
438 }
else if(child.key ==
"time") {
440 c.append_attributes(child.cfg);
455 }
else if(scen.
has_child(
"multiplayer")) {
460 ERR_ED <<
"Found no [scenario], [multiplayer], or [test] tag in " <<
filename_ <<
", assuming old-style editor scenario and defaulting to [multiplayer]";
470 xp_mod_ = experience_modifier->to_int();
473 random_time_ = scenario[
"random_start_time"].to_bool(
false);
475 if(!scenario[
"map_data"].str().empty()) {
477 }
else if(!scenario[
"map_file"].str().empty()) {
486 if(!side[
"recruit"].str().empty()) {
493 auto event = scenario.
find_child(
"event",
"id",
"editor_event-start");
495 config& evt =
event.value();
540 LOG_ED <<
"Attempted to draw terrain off the map (" << loc <<
")";
546 if(terrain != old_terrain) {
549 }
else if(one_layer_only) {
650 scenario[
"experience_modifier"] = *
xp_mod_;
663 event[
"name"] =
"start";
664 event[
"id"] =
"editor_event-start";
673 if(scenario[
"turns"].to_int() == -1) {
676 scenario[
"turns"] = times[
"turns"];
684 config&
t =
event.add_child(
"time_area");
692 for(
const auto& overlay_pair :
overlays_) {
693 for(
const overlay& o : overlay_pair.second) {
697 overlay_pair.first.write(
item);
704 item[
"id"].write_if_not_empty(o.
id);
705 item[
"name"].write_if_not_empty(o.
name);
707 item[
"halo"].write_if_not_empty(o.
halo);
716 track.second.write(event,
true);
727 u[
"name"].write_if_not_empty(
unit.
name());
730 if(!boost::regex_match(
unit.
id(), boost::regex(
".*-[0-9]+"))) {
757 side[
"faction"] =
"Custom";
768 village.write(side.
add_child(
"village"));
789 std::stringstream wml_stream;
791 <<
"# This file was generated using the scenario editor.\n"
793 <<
"# If you edit this file by hand, then do not use macros.\n"
794 <<
"# The editor doesn't support macros, and so using them will result in only being able to edit the map.\n"
795 <<
"# 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"
796 <<
"# Any manual changes made to those will be lost.\n"
803 if(!wml_stream.str().empty()) {
810 symbols[
"msg"] =
e.what();
811 const std::string
msg =
VGETTEXT(
"Could not save the scenario: $msg", symbols);
834 boost::regex rexpression_map_data(R
"""((.*map_data\s*=\s*")(.+?)(".*))""");
835 boost::smatch matched_map_data;
837 if(boost::regex_search(map_string, matched_map_data, rexpression_map_data,
838 boost::regex_constants::match_not_dot_null)) {
839 std::stringstream ss;
840 ss << matched_map_data[1];
842 ss << matched_map_data[3];
855 symbols[
"msg"] =
e.what();
856 const std::string
msg =
VGETTEXT(
"Could not save the map: $msg", symbols);
879 LOG_ED <<
"Performing action " << action.
get_id() <<
": " << action.
get_name() <<
", actions count is "
899 LOG_ED <<
"Performing (partial) action " << action.
get_id() <<
": " << action.
get_name() <<
", actions count is "
906 if(undo_chain ==
nullptr) {
971 WRN_ED <<
"undo() called with an empty undo stack";
985 WRN_ED <<
"redo() called with an empty redo stack";
999 if(undo_chain ==
nullptr) {
1006 if(undo_chain->
empty()) {
1011 redo_stack_.emplace_back(first_action_in_chain->perform(*
this));
1030 assert(!from.empty());
1032 std::unique_ptr<editor_action> action;
1033 action.swap(from.back());
1037 auto reverse_action = action->perform(*
this);
1038 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.
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
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.
const_all_children_itors all_children_range() const
In-order iteration over all children.
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)
void remove_children(config_key_type key, std::function< bool(const config &)> p=[](config){return true;})
Removes all children with tag key for which p returns true.
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.
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...
std::optional< int > xp_mod_
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()
std::optional< config > previous_cfg_
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_
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)
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
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.
bool save_scenario()
Saves the scenario 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)
bool save_map()
Saves the map under the current filename.
std::optional< bool > victory_defeated_
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)
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)
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
umap_retval_pair_t insert(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.
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.
std::string id
Text to match against addon_info.tags()
#define log_scope2(domain, description)
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.
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.
std::string get_wml_location(const std::string &filename, const std::string ¤t_dir)
Returns a complete path to the actual WML file or directory or an empty string if the file isn't pres...
std::string read_file(const std::string &fname)
Basic disk I/O - read file.
bool ends_with(const std::string &str, const std::string &suffix)
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.
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_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.
std::pair< std::string, unsigned > item
void add_recent_files_entry(const std::string &path)
Adds an entry to the recent files list.
::tod_manager * tod_manager
std::size_t index(const std::string &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 > set_split(const std::string &val, const char c=',', const int flags=REMOVE_EMPTY|STRIP_SPACES)
Splits a (comma-)separated string into a set of pieces.
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="")
filesystem::scoped_istream preprocess_file(const std::string &fname, preproc_map *defines)
Function to use the WML preprocessor on a file.
void read(config &cfg, std::istream &in, abstract_validator *validator)
editor_team_info(const team &t)
An exception object used when an IO error occurs.
Encapsulates the map of the game.
void write(config &cfg) const
static std::string write_direction(DIRECTION dir)
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...