diff --git a/src/actions/unit_creator.cpp b/src/actions/unit_creator.cpp index fe9b57efb892..b73bf211b438 100644 --- a/src/actions/unit_creator.cpp +++ b/src/actions/unit_creator.cpp @@ -134,6 +134,9 @@ map_location unit_creator::find_location(const config &cfg, const unit* pass_che else if ( place == "map" || place.compare(0, 4, "map_") == 0 ) { loc = map_location(cfg, resources::gamedata); } + else { + loc = board_->map().special_location(place); + } if(loc.valid() && board_->map().on_board(loc)) { const bool pass((place == "leader_passable") || (place == "map_passable")); diff --git a/src/editor/action/action.cpp b/src/editor/action/action.cpp index 2eb3333a544d..73f75fa0944f 100644 --- a/src/editor/action/action.cpp +++ b/src/editor/action/action.cpp @@ -221,32 +221,31 @@ editor_action_starting_position* editor_action_starting_position::clone() const editor_action* editor_action_starting_position::perform(map_context& mc) const { util::unique_ptr undo; - int old_player = mc.get_map().is_starting_position(loc_); - map_location old_loc = mc.get_map().starting_position(player_); - LOG_ED << "ssp perform, player_" << player_ << ", loc_ " << loc_ << ", old_player " << old_player << ", old_loc " << old_loc << "\n"; - if (old_player != -1) { + const std::string* old_loc_id = mc.get_map().is_starting_position(loc_); + map_location old_loc = mc.get_map().special_location(loc_id_); + if (old_loc_id != nullptr) { // If another player was starting at the location, we actually perform two actions, so the undo is an action_chain. editor_action_chain* undo_chain = new editor_action_chain(); - undo_chain->append_action(new editor_action_starting_position(loc_, old_player)); - undo_chain->append_action(new editor_action_starting_position(old_loc, player_)); + undo_chain->append_action(new editor_action_starting_position(loc_, *old_loc_id)); + undo_chain->append_action(new editor_action_starting_position(old_loc, loc_id_)); undo.reset(undo_chain); - LOG_ED << "ssp actual: " << old_player << " to " << map_location() << "\n"; - mc.get_map().set_starting_position(old_player, map_location()); + LOG_ED << "ssp actual: " << *old_loc_id << " to " << map_location() << "\n"; + mc.get_map().set_special_location(*old_loc_id, map_location()); } else { - undo.reset(new editor_action_starting_position(old_loc, player_)); + undo.reset(new editor_action_starting_position(old_loc, loc_id_)); } - LOG_ED << "ssp actual: " << player_ << " to " << loc_ << "\n"; - mc.get_map().set_starting_position(player_, loc_); + LOG_ED << "ssp actual: " << loc_id_ << " to " << loc_ << "\n"; + mc.get_map().set_special_location(loc_id_, loc_); mc.set_needs_labels_reset(); return undo.release(); } void editor_action_starting_position::perform_without_undo(map_context& mc) const { - int old_player = mc.get_map().is_starting_position(loc_) - 1; - if (old_player != -1) { - mc.get_map().set_starting_position(old_player - 1, map_location()); + const std::string* old_id = mc.get_map().is_starting_position(loc_); + if (old_id != nullptr) { + mc.get_map().set_special_location(*old_id, map_location()); } - mc.get_map().set_starting_position(player_, loc_); + mc.get_map().set_special_location(loc_id_, loc_); mc.set_needs_labels_reset(); } diff --git a/src/editor/action/action.hpp b/src/editor/action/action.hpp index 606c59a85ca7..1efa864f7676 100644 --- a/src/editor/action/action.hpp +++ b/src/editor/action/action.hpp @@ -283,8 +283,8 @@ class editor_action_fill : public editor_action_location_terrain class editor_action_starting_position : public editor_action_location { public: - editor_action_starting_position(map_location loc, int player) - : editor_action_location(loc), player_(player) + editor_action_starting_position(map_location loc, std::string loc_id) + : editor_action_location(loc), loc_id_(loc_id) { } editor_action_starting_position* clone() const; @@ -292,7 +292,7 @@ class editor_action_starting_position : public editor_action_location void perform_without_undo(map_context& mc) const; const char* get_name() const { return "starting_pos"; } protected: - int player_; + std::string loc_id_; }; diff --git a/src/editor/action/mouse/mouse_action.cpp b/src/editor/action/mouse/mouse_action.cpp index ab9a2d30448d..50da4b1a2743 100644 --- a/src/editor/action/mouse/mouse_action.cpp +++ b/src/editor/action/mouse/mouse_action.cpp @@ -107,11 +107,11 @@ editor_action* mouse_action::key_event( || event.key.keysym.sym == SDLK_DELETE) { int res = event.key.keysym.sym - '0'; if (res > gamemap::MAX_PLAYERS || event.key.keysym.sym == SDLK_DELETE) res = 0; - int player_starting_at_hex = disp.map().is_starting_position(previous_move_hex_); - if (res == 0 && player_starting_at_hex != -1) { - a = new editor_action_starting_position(map_location(), player_starting_at_hex); - } else if (res > 0 && res != player_starting_at_hex) { - a = new editor_action_starting_position(previous_move_hex_, res); + const std::string* old_id = disp.map().is_starting_position(previous_move_hex_); + if (res == 0 && old_id != nullptr) { + a = new editor_action_starting_position(map_location(), *old_id); + } else if (res > 0 && (old_id == nullptr || *old_id == std::to_string(res))) { + a = new editor_action_starting_position(previous_move_hex_, std::to_string(res)); } } return a; @@ -399,20 +399,20 @@ editor_action* mouse_action_starting_position::up_left(editor_display& disp, int return nullptr; } - const unsigned player_starting_at_hex = disp.map().is_starting_position(hex); + auto player_starting_at_hex = disp.map().is_starting_position(hex); std::vector starting_positions; - unsigned new_player_at_hex = std::stoi(location_palette_.selected_item()); + std::string new_player_at_hex = location_palette_.selected_item(); editor_action* a = nullptr; - if(new_player_at_hex != player_starting_at_hex) { + if(!player_starting_at_hex || new_player_at_hex != *player_starting_at_hex) { // Set a starting position a = new editor_action_starting_position(hex, new_player_at_hex); } else { // Erase current starting position - a = new editor_action_starting_position(map_location(), player_starting_at_hex); + a = new editor_action_starting_position(map_location(), *player_starting_at_hex); } update_brush_highlights(disp, hex); @@ -429,9 +429,9 @@ editor_action* mouse_action_starting_position::click_left(editor_display& /*disp editor_action* mouse_action_starting_position::up_right(editor_display& disp, int x, int y) { map_location hex = disp.hex_clicked_on(x, y); - int player_starting_at_hex = disp.map().is_starting_position(hex); - if (player_starting_at_hex != -1) { - return new editor_action_starting_position(map_location(), player_starting_at_hex); + auto player_starting_at_hex = disp.map().is_starting_position(hex); + if (player_starting_at_hex != nullptr) { + return new editor_action_starting_position(map_location(), *player_starting_at_hex); } else { return nullptr; } diff --git a/src/editor/controller/editor_controller.cpp b/src/editor/controller/editor_controller.cpp index 26b1c32446ff..a8c88aad8028 100644 --- a/src/editor/controller/editor_controller.cpp +++ b/src/editor/controller/editor_controller.cpp @@ -984,7 +984,7 @@ bool editor_controller::execute_command(const hotkey::hotkey_command& cmd, int i case HOTKEY_EDITOR_REMOVE_LOCATION: { location_palette* lp = dynamic_cast(&toolkit_->get_palette_manager()->active_palette()); if (lp) { - perform_delete(new editor_action_starting_position(map_location(), std::stoi(lp->selected_item()))); + perform_delete(new editor_action_starting_position(map_location(), lp->selected_item())); } return true; } diff --git a/src/editor/map/editor_map.cpp b/src/editor/map/editor_map.cpp index b417d4f45992..8858b9383fc8 100644 --- a/src/editor/map/editor_map.cpp +++ b/src/editor/map/editor_map.cpp @@ -18,6 +18,7 @@ #include "formula/string_utils.hpp" #include "display.hpp" +#include "formula/string_utils.hpp" #include "gettext.hpp" #include "map/exception.hpp" #include "map/label.hpp" @@ -148,10 +149,20 @@ std::set editor_map::get_contiguous_terrain_tiles(const map_locati std::set editor_map::set_starting_position_labels(display& disp) { std::set label_locs; - std::string label = _("Player"); - label += " "; + std::string label; + + for (const auto& pair : starting_positions_.left) { - disp.labels().set_label(map_location(pair.second.x, pair.second.y), label + std::to_string(pair.first)); + + bool is_number = std::find_if(pair.first.begin(), pair.first.end(), [](char c) { return !std::isdigit(c); }) == pair.first.end(); + if (is_number) { + label = vgettext("Player $side_num", utils::string_map{ { "side_num", pair.first } }); + } + else { + label = pair.first; + } + + disp.labels().set_label(map_location(pair.second.x, pair.second.y), label); label_locs.insert(map_location(pair.second.x, pair.second.y)); } return label_locs; diff --git a/src/editor/palette/location_palette.cpp b/src/editor/palette/location_palette.cpp index d25f5e1e1930..68af09603554 100644 --- a/src/editor/palette/location_palette.cpp +++ b/src/editor/palette/location_palette.cpp @@ -21,6 +21,7 @@ #include "tooltips.hpp" #include "editor/action/mouse/mouse_action.hpp" +#include "gui/dialogs/edit_text.hpp" #include "wml_separators.hpp" #include "formula/string_utils.hpp" @@ -71,7 +72,10 @@ class location_palette_item : public gui::widget parent_.select_item(id_); } if (e.button == SDL_BUTTON_RIGHT) { - //TODO: implement 'jump to item' or 'delete item' here. + //TODO: add a context menu with the follwing options: + // 1) 'copy it to clipboard' + // 2) 'jump to item' + // 3) 'delete item'. } } @@ -223,23 +227,32 @@ void location_palette::adjust_size(const SDL_Rect& target) palette_x_ = target.x; palette_y_ = target.y; const int button_height = 30; - int bottom = target.y + target.h - button_height; + int bottom = target.y + target.h; button_add_.reset(); button_delete_.reset(); button_goto_.reset(); - button_goto_.reset(new location_palette_button(video(), SDL_Rect{ target.x , bottom, target.w - 10, button_height }, _("Go To"), [this]() { + button_goto_.reset(new location_palette_button(video(), SDL_Rect{ target.x , bottom -= button_height, target.w - 10, button_height }, _("Go To"), [this]() { //static_cast(**active_mouse_action_). map_location pos = disp_.get_map().starting_position(std::stoi(selected_item_)); if (pos.valid()) { disp_.scroll_to_tile(pos, display::WARP); } })); + button_add_.reset(new location_palette_button(video(), SDL_Rect{ target.x , bottom -= button_height, target.w - 10, button_height }, _("Add"), [this]() { + std::string newid; + if (gui2::tedit_text::execute(_("New Location Identifer"), "", newid, video())) { + items_.push_back(newid); + adjust_size(location()); + } + })); button_delete_.reset(new location_palette_button(video(), SDL_Rect{ target.x , bottom -= button_height, target.w - 10, button_height }, _("Delete"), nullptr)); - const size_t space_for_items = bottom - target.x; - const unsigned items_fitting = static_cast (space_for_items / item_space_); + + + const size_t space_for_items = bottom - target.y; + const int items_fitting = static_cast (space_for_items / item_space_); nitems_ = std::min(items_fitting, items_.size()); if (buttons_.size() != nitems_) { buttons_.resize(nitems_, &location_palette_item(gui_.video(), *this)); diff --git a/src/generators/cave_map_generator.cpp b/src/generators/cave_map_generator.cpp index 966d306a1c9e..e65f1088c0ce 100644 --- a/src/generators/cave_map_generator.cpp +++ b/src/generators/cave_map_generator.cpp @@ -345,7 +345,7 @@ void cave_map_generator::cave_map_generator_job::place_castle(int starting_posit t_translation::coordinate coord( loc.x + gamemap::default_border , loc.y + gamemap::default_border); - starting_positions_.insert(t_translation::tstarting_positions::value_type(starting_position, coord)); + starting_positions_.insert(t_translation::tstarting_positions::value_type(std::to_string(starting_position), coord)); } map_location adj[6]; diff --git a/src/generators/default_map_generator_job.cpp b/src/generators/default_map_generator_job.cpp index 92044fa31eaf..f6996bf1a8b5 100644 --- a/src/generators/default_map_generator_job.cpp +++ b/src/generators/default_map_generator_job.cpp @@ -1202,7 +1202,7 @@ std::string default_map_generator_job::default_generate_map(size_t width, size_t const int y = c->y; const int player = c - castles.begin() + 1; const struct t_translation::coordinate coord(x, y); - starting_positions.insert(t_translation::tstarting_positions::value_type(player, coord)); + starting_positions.insert(t_translation::tstarting_positions::value_type(std::to_string(player), coord)); terrain[x][y] = t_translation::HUMAN_KEEP; const int castles[13][2] = { diff --git a/src/gui/dialogs/edit_text.cpp b/src/gui/dialogs/edit_text.cpp index a4897f90bc68..6880b66c1668 100644 --- a/src/gui/dialogs/edit_text.cpp +++ b/src/gui/dialogs/edit_text.cpp @@ -39,6 +39,7 @@ namespace gui2 REGISTER_DIALOG(edit_text) +//TODO: add a way to disallow certain chracters (like spaces or ") tedit_text::tedit_text(const std::string& title, const std::string& label, std::string& text) diff --git a/src/map/map.cpp b/src/map/map.cpp index 448cf0171939..372b4dbea5a6 100644 --- a/src/map/map.cpp +++ b/src/map/map.cpp @@ -158,7 +158,6 @@ void gamemap::read(const std::string& data, const bool allow_invalid, int border tiles_.clear(); villages_.clear(); starting_positions_.clear(); - std::map starting_positions; if(data.empty()) { w_ = 0; @@ -181,23 +180,6 @@ void gamemap::read(const std::string& data, const bool allow_invalid, int border throw incorrect_map_format_error(e.message); } - // Convert the starting positions to the array - int max_stating_pos = 0; - for(const auto& pair : starting_positions) { - // Check for valid position, - // the first valid position is 1, - // so the offset 0 in the array is never used. - if(pair.first < 1 || pair.first >= MAX_PLAYERS+1) { - std::stringstream ss; - ss << "Starting position " << pair.first << " out of range\n"; - ERR_CF << ss.str(); - ss << "The map cannot be loaded."; - throw incorrect_map_format_error(ss.str().c_str()); - } - - // Add to the starting position array - max_stating_pos = std::max(max_stating_pos, pair.first); - } // Post processing on the map total_width_ = tiles_.size(); total_height_ = total_width_ > 0 ? tiles_[0].size() : 0; @@ -424,9 +406,9 @@ t_translation::t_terrain gamemap::get_terrain(const map_location& loc) const } -map_location gamemap::starting_position(int n) const +map_location gamemap::special_location(const std::string& id) const { - auto it = starting_positions_.left.find(n); + auto it = starting_positions_.left.find(id); if (it != starting_positions_.left.end()) { auto& coordinate = it->second; return map_location(coordinate.x, coordinate.y); @@ -436,32 +418,52 @@ map_location gamemap::starting_position(int n) const } } +map_location gamemap::starting_position(int n) const +{ + return special_location(std::to_string(n)); +} + int gamemap::num_valid_starting_positions() const { - const int res = is_starting_position(map_location()); - if(res == 0) - return num_starting_positions(); - else - return res; + int res = 0; + for (auto pair : starting_positions_) { + const std::string& id = pair.left; + bool is_number = std::find_if(id.begin(), id.end(), [](char c) { return !std::isdigit(c); }) == id.end(); + if (is_number) { + res = std::max(res, std::stoi(id)); + } + } + return res; } -int gamemap::is_starting_position(const map_location& loc) const +const std::string* gamemap::is_starting_position(const map_location& loc) const { auto it = starting_positions_.right.find(t_translation::coordinate(loc.x, loc.y)); - return it == starting_positions_.right.end() ? 0 : it->second; + return it == starting_positions_.right.end() ? nullptr : &it->second; } -void gamemap::set_starting_position(int side, const map_location& loc) +void gamemap::set_special_location(const std::string& id, const map_location& loc) { - auto it_left = starting_positions_.left.find(side); + bool valid = loc.valid(); + auto it_left = starting_positions_.left.find(id); if (it_left != starting_positions_.left.end()) { - starting_positions_.left.replace_data(it_left, t_translation::coordinate(loc.x, loc.y)); + if (valid) { + starting_positions_.left.replace_data(it_left, t_translation::coordinate(loc.x, loc.y)); + } + else { + starting_positions_.left.erase(it_left); + } } else { - starting_positions_.left.insert(it_left, std::make_pair(side, t_translation::coordinate(loc.x, loc.y))); + starting_positions_.left.insert(it_left, std::make_pair(id, t_translation::coordinate(loc.x, loc.y))); } } +void gamemap::set_starting_position(int side, const map_location& loc) +{ + set_special_location(std::to_string(side), loc); +} + bool gamemap::on_board(const map_location& loc) const { return loc.valid() && loc.x < w_ && loc.y < h_; diff --git a/src/map/map.hpp b/src/map/map.hpp index d0078bd81f84..67159aa66da2 100644 --- a/src/map/map.hpp +++ b/src/map/map.hpp @@ -132,12 +132,17 @@ class gamemap /** Manipulate starting positions of the different sides. */ + void set_starting_position(int side, const map_location& loc); map_location starting_position(int side) const; + + void set_special_location(const std::string& id, const map_location& loc); + map_location special_location(const std::string& id) const; + + /// returns the side number of the side starting at position loc, 0 if no such side exists. - int is_starting_position(const map_location& loc) const; + const std::string* is_starting_position(const map_location& loc) const; int num_valid_starting_positions() const; - void set_starting_position(int side, const map_location& loc); /** * Tell if a location is on the map. @@ -224,9 +229,6 @@ class gamemap */ int read_header(const std::string& data); - int num_starting_positions() const - { return starting_positions_.size(); } - /** Allows lookup of terrain at a particular location. */ //const t_translation::t_list operator[](int index) const // { return tiles_[index + border_size_]; } diff --git a/src/terrain/filter.cpp b/src/terrain/filter.cpp index 44d718d8bd95..014f351098bd 100644 --- a/src/terrain/filter.cpp +++ b/src/terrain/filter.cpp @@ -134,12 +134,12 @@ bool terrain_filter::match_internal(const map_location& loc, const bool ignore_x } //Allow filtering on location ranges - if(!ignore_xy) { - if(!loc.matches_range(cfg_["x"], cfg_["y"])) { + if (!ignore_xy) { + if (!loc.matches_range(cfg_["x"], cfg_["y"])) { return false; } //allow filtering by searching a stored variable of locations - if(cfg_.has_attribute("find_in")) { + if (cfg_.has_attribute("find_in")) { if (const game_data * gd = fc_->get_game_data()) { try { @@ -154,14 +154,19 @@ bool terrain_filter::match_internal(const map_location& loc, const bool ignore_x } if (!found) return false; } - catch(const invalid_variablename_exception&) + catch (const invalid_variablename_exception&) { return false; } } } } - + //TODO: move this inside if(!ignore_xy) and ad d check for this in terrain_filter::get_locations + if (cfg_.has_attribute("location_id")) { + if (loc != fc_->get_disp_context().map().special_location(cfg_["location_id"])) { + return false; + } + } //Allow filtering on unit if(cfg_.has_child("filter")) { const unit_map::const_iterator u = fc_->get_disp_context().units().find(loc); diff --git a/src/terrain/translation.cpp b/src/terrain/translation.cpp index ed5e232f46fe..d9d0bdc1d59f 100644 --- a/src/terrain/translation.cpp +++ b/src/terrain/translation.cpp @@ -88,7 +88,7 @@ namespace t_translation { * @return The terrain code found in the string if no * valid terrain is found VOID will be returned. */ - static t_terrain string_to_number_(std::string str, int& start_position, const t_layer filler); + static t_terrain string_to_number_(std::string str, std::string& start_position, const t_layer filler); static t_terrain string_to_number_(const std::string& str, const t_layer filler = NO_LAYER); /** @@ -102,7 +102,7 @@ namespace t_translation { * position given it's padded to 4 chars else * padded to 7 chars. */ - static std::string number_to_string_(t_terrain terrain, const int start_position = -1); + static std::string number_to_string_(t_terrain terrain, const std::string& start_position = ""); /** * Converts a terrain string to a number for the builder. @@ -295,12 +295,12 @@ t_map read_game_map(const std::string& str, tstarting_positions& starting_positi const std::string terrain = str.substr(offset, pos_separator - offset); // Process the chunk - int starting_position = -1; + std::string starting_position; // The gamemap never has a wildcard const t_terrain tile = string_to_number_(terrain, starting_position, NO_LAYER); // Add to the resulting starting position - if(starting_position != -1) { + if(!starting_position.empty()) { if (starting_positions.left.find(starting_position) != starting_positions.left.end()) { WRN_G << "Starting position " << starting_position << " is redefined." << std::endl; } @@ -387,7 +387,7 @@ std::string write_game_map(const t_map& map, const tstarting_positions& starting // After it's found it can't be found again, // so the location is removed from the map. auto itor = starting_positions.right.find(coordinate(x, y)); - int starting_position = 0; + std::string starting_position; if (itor != starting_positions.right.end()) { starting_position = itor->second; } @@ -725,11 +725,11 @@ static t_layer string_to_layer_(const char* begin, const char* end) } static t_terrain string_to_number_(const std::string& str, const t_layer filler) { - int dummy = -1; + std::string dummy; return string_to_number_(str, dummy, filler); } -static t_terrain string_to_number_(std::string str, int& start_position, const t_layer filler) +static t_terrain string_to_number_(std::string str, std::string& start_position, const t_layer filler) { const char* c_str = str.c_str(); size_t begin = 0; @@ -748,7 +748,7 @@ static t_terrain string_to_number_(std::string str, int& start_position, const t size_t offset = str.find(' ', begin); if(offset < end) { try { - start_position = lexical_cast(str.substr(begin, offset)); + start_position = str.substr(begin, offset - begin); } catch(bad_lexical_cast&) { return VOID_TERRAIN; } @@ -774,13 +774,13 @@ static t_terrain string_to_number_(std::string str, int& start_position, const t return result; } -static std::string number_to_string_(t_terrain terrain, const int start_position) +static std::string number_to_string_(t_terrain terrain, const std::string& start_position) { std::string result = ""; // Insert the start position - if(start_position > 0) { - result = std::to_string(start_position) + " "; + if(!start_position.empty()) { + result = start_position + " "; } /* diff --git a/src/terrain/translation.hpp b/src/terrain/translation.hpp index f03e64f68009..8f3461015b01 100644 --- a/src/terrain/translation.hpp +++ b/src/terrain/translation.hpp @@ -202,7 +202,7 @@ namespace t_translation { */ std::string write_list(const t_list& list); - using tstarting_positions = boost::bimaps::bimap, boost::bimaps::multiset_of>; + using tstarting_positions = boost::bimaps::bimap, boost::bimaps::multiset_of>; /** * Reads a gamemap string into a 2D vector *