Now realy introduced the new map format.

This commit is contained in:
Fabian Müller 2012-02-23 02:39:58 +00:00
parent 1a27a8cb08
commit a8aa81f338
15 changed files with 215 additions and 101 deletions

View File

@ -90,7 +90,7 @@ size_t cave_map_generator::translate_y(size_t y) const
std::string cave_map_generator::create_map(const std::vector<std::string>& args)
{
const config res = create_scenario(args);
return res["map_data"];
return res["data"];
}
config cave_map_generator::create_scenario(const std::vector<std::string>& /*args*/)
@ -121,8 +121,10 @@ config cave_map_generator::create_scenario(const std::vector<std::string>& /*arg
LOG_NG << "outputting map....\n";
res_["map_data"] = gamemap::default_map_header +
t_translation::write_game_map(map_, starting_positions_);
config& map = res_.add_child("map");
map["data"] = t_translation::write_game_map(map_, starting_positions_);
map["usage"] = "map";
map["border_size"] = 1;
LOG_NG << "returning result...\n";

View File

@ -504,6 +504,8 @@ void editor_controller::generate_map_dialog()
if (map_string.empty()) {
gui2::show_transient_message(gui().video(), "", _("Map creation failed."));
} else {
// config map;
// map["data"] = map_string;
editor_map new_map(game_config_, map_string);
editor_action_whole_map a(new_map);
perform_refresh(a);

View File

@ -42,11 +42,18 @@ editor_map_load_exception wrap_exc(const char* type, const std::string& e_msg, c
}
editor_map::editor_map(const config& terrain_cfg)
: gamemap(terrain_cfg, gamemap::default_map_header)
: gamemap(terrain_cfg, "")
, selection_()
{
}
editor_map::editor_map(const config& terrain_cfg, const config& level)
: gamemap(terrain_cfg, level)
, selection_()
{
sanity_check();
}
editor_map::editor_map(const config& terrain_cfg, const std::string& data)
: gamemap(terrain_cfg, data)
, selection_()
@ -68,8 +75,7 @@ editor_map editor_map::from_string(const config& terrain_cfg, const std::string&
}
editor_map::editor_map(const config& terrain_cfg, size_t width, size_t height, t_translation::t_terrain filler)
: gamemap(terrain_cfg, gamemap::default_map_header + t_translation::write_game_map(
t_translation::t_map(width + 2, t_translation::t_list(height + 2, filler))))
: gamemap(terrain_cfg, t_translation::write_game_map(t_translation::t_map(width + 2, t_translation::t_list(height + 2, filler))))
, selection_()
{
sanity_check();

View File

@ -80,6 +80,8 @@ public:
/**
* Create an editor map from a map data string
*/
editor_map(const config& terrain_cfg, const config& level);
editor_map(const config& terrain_cfg, const std::string& data);
/**

View File

@ -23,6 +23,7 @@
#include "../gettext.hpp"
#include "../map_exception.hpp"
#include "../map_label.hpp"
#include "../serialization/parser.hpp"
#include "../wml_exception.hpp"
#include "formula_string_utils.hpp"
@ -70,7 +71,24 @@ map_context::map_context(const config& game_config, const std::string& filename)
if (!file_exists(filename) || is_directory(filename)) {
throw editor_map_load_exception(filename, _("File not found"));
}
std::string map_string = read_file(filename);
if (map_string.empty()) {
std::string message = _("Empty file");
throw editor_map_load_exception(filename, message);
}
boost::regex reg("[map]");
boost::smatch mym;
if (boost::regex_search(map_string, mym, reg, boost::regex_constants::match_not_dot_null)) {
config file;
::read(file, map_string);
map_ = editor_map(game_config, file);
return;
}
boost::regex re("map_data\\s*=\\s*\"(.+?)\"");
boost::smatch m;
if (boost::regex_search(map_string, m, re, boost::regex_constants::match_not_dot_null)) {
@ -100,6 +118,7 @@ map_context::map_context(const config& game_config, const std::string& filename)
std::string message = _("Empty map file");
throw editor_map_load_exception(filename, message);
}
map_ = editor_map::from_string(game_config, map_string); //throws on error
}
@ -202,10 +221,15 @@ void map_context::reset_starting_position_labels(display& disp)
bool map_context::save()
{
std::string data = map_.write();
config data;
config& map = data.add_child("map");
map_.write(map);
try {
if (!is_embedded()) {
write_file(get_filename(), data);
std::stringstream ss;
ss << data;
write_file(get_filename(), ss.str());
} else {
std::string map_string = read_file(get_filename());
boost::regex re("(.*map_data\\s*=\\s*\")(.+?)(\".*)");

View File

@ -1717,10 +1717,21 @@ WML_HANDLER_FUNCTION(terrain_mask, /*event_info*/, cfg)
{
map_location loc = cfg_to_loc(cfg, 1, 1);
gamemap mask(*resources::game_map);
gamemap mask_map(*resources::game_map);
//config level;
std::string mask = cfg["mask"];
std::string usage = "mask";
int border_size = 0;
if (mask.empty()) {
usage = cfg["usage"].str();
border_size = cfg["border_size"];
mask = cfg["data"].str();
}
try {
mask.read(cfg["mask"], false);
mask_map.read(mask, false, border_size, usage);
} catch(incorrect_map_format_error&) {
ERR_NG << "terrain mask is in the incorrect format, and couldn't be applied\n";
return;
@ -1729,7 +1740,7 @@ WML_HANDLER_FUNCTION(terrain_mask, /*event_info*/, cfg)
return;
}
bool border = cfg["border"].to_bool();
resources::game_map->overlay(mask, cfg.get_parsed_config(), loc.x, loc.y, border);
resources::game_map->overlay(mask_map, cfg.get_parsed_config(), loc.x, loc.y, border);
screen_needs_rebuild = true;
}

View File

@ -449,16 +449,15 @@ void extract_summary_from_config(config& cfg_save, config& cfg_summary)
cfg_summary["leader"] = leader;
cfg_summary["leader_image"] = leader_image;
cfg_summary["map_data"] = "";
if(!shrouded) {
if(has_snapshot) {
if (!cfg_snapshot.find_child("side", "shroud", "yes")) {
cfg_summary["map_data"] = cfg_snapshot["map_data"];
cfg_summary.add_child("map", cfg_snapshot.child_or_empty("map"));
}
} else if(has_replay) {
if (!cfg_replay_start.find_child("side","shroud","yes")) {
cfg_summary["map_data"] = cfg_replay_start["map_data"];
cfg_summary.add_child("map", cfg_replay_start.child_or_empty("map"));
}
}
}

View File

@ -37,7 +37,6 @@ static lg::log_domain log_config("config");
#define LOG_G LOG_STREAM(info, lg::general)
#define DBG_G LOG_STREAM(debug, lg::general)
const std::string gamemap::default_map_header = "usage=map\nborder_size=1\n\n";
const gamemap::tborder gamemap::default_border = gamemap::SINGLE_TILE_BORDER;
const t_translation::t_list& gamemap::underlying_mvt_terrain(t_translation::t_terrain terrain) const
@ -147,7 +146,7 @@ gamemap::gamemap(const config& cfg, const std::string& data):
h_(-1),
total_width_(0),
total_height_(0),
border_size_(NO_BORDER),
border_size_(gamemap::SINGLE_TILE_BORDER),
usage_(IS_MAP)
{
DBG_G << "loading map: '" << data << "'\n";
@ -157,13 +156,50 @@ gamemap::gamemap(const config& cfg, const std::string& data):
read(data);
}
gamemap::gamemap(const config& cfg, const config& level):
tiles_(1),
terrainList_(),
tcodeToTerrain_(),
villages_(),
borderCache_(),
terrainFrequencyCache_(),
w_(-1),
h_(-1),
total_width_(0),
total_height_(0),
border_size_(gamemap::SINGLE_TILE_BORDER),
usage_(IS_MAP)
{
DBG_G << "loading map: '" << level.debug() << "'\n";
const config::const_child_itors &terrains = cfg.child_range("terrain_type");
create_terrain_maps(terrains, terrainList_, tcodeToTerrain_);
const config& map_child = level.child_or_empty("map");
if (map_child.empty()) {
const std::string& map_data = level["map_data"];
if (!map_data.empty()) {
read(map_data);
} else {
w_ = 0;
h_ = 0;
total_width_ = 0;
total_height_ = 0;
}
} else {
read(map_child["data"], true, map_child["border_size"], map_child["usage"]);
}
}
gamemap::~gamemap()
{
}
void gamemap::read(const std::string& data, const bool allow_invalid)
{
void gamemap::read(const std::string& data, const bool allow_invalid, int border_size, std::string usage) {
// Initial stuff
border_size_ = border_size;
set_usage(usage);
tiles_.clear();
villages_.clear();
std::fill(startingPositions_, startingPositions_ +
@ -178,56 +214,12 @@ void gamemap::read(const std::string& data, const bool allow_invalid)
if(allow_invalid) return;
}
// Test whether there is a header section
size_t header_offset = data.find("\n\n");
if(header_offset == std::string::npos) {
// For some reason Windows will fail to load a file with \r\n
// lineending properly no problems on Linux with those files.
// This workaround fixes the problem the copy later will copy
// the second \r\n to the map, but that's no problem.
header_offset = data.find("\r\n\r\n");
}
const size_t comma_offset = data.find(",");
// The header shouldn't contain commas, so if the comma is found
// before the header, we hit a \n\n inside or after a map.
// This is no header, so don't parse it as it would be.
VALIDATE(
!(header_offset == std::string::npos || comma_offset < header_offset),
_("A map without a header is not supported"));
int offset = read_header(data);
std::string header_str(std::string(data, 0, header_offset + 1));
config header;
::read(header, header_str);
border_size_ = header["border_size"];
const std::string usage = header["usage"];
utils::string_map symbols;
symbols["border_size_key"] = "border_size";
symbols["usage_key"] = "usage";
symbols["usage_val"] = usage;
const std::string msg = "'$border_size_key|' should be "
"'$border_size_val|' when '$usage_key| = $usage_val|'";
if(usage == "map") {
usage_ = IS_MAP;
symbols["border_size_val"] = "1";
VALIDATE(border_size_ == 1, vgettext(msg.c_str(), symbols));
} else if(usage == "mask") {
usage_ = IS_MASK;
symbols["border_size_val"] = "0";
VALIDATE(border_size_ == 0, vgettext(msg.c_str(), symbols));
} else if(usage == "") {
throw incorrect_map_format_error("Map has a header but no usage");
} else {
std::string msg = "Map has a header but an unknown usage:" + usage;
throw incorrect_map_format_error(msg.c_str());
}
const std::string& map = std::string(data, header_offset + 2);
const std::string& data_only = std::string(data, offset);
try {
tiles_ = t_translation::read_game_map(map, starting_positions);
tiles_ = t_translation::read_game_map(data_only, starting_positions);
} catch(t_translation::error& e) {
// We re-throw the error but as map error.
@ -291,7 +283,62 @@ void gamemap::read(const std::string& data, const bool allow_invalid)
}
}
std::string gamemap::write() const
void gamemap::set_usage(const std::string& usage)
{
utils::string_map symbols;
symbols["border_size_key"] = "border_size";
symbols["usage_key"] = "usage";
symbols["usage_val"] = usage;
const std::string msg = "'$border_size_key|' should be "
"'$border_size_val|' when '$usage_key| = $usage_val|'";
if(usage == "map") {
usage_ = IS_MAP;
symbols["border_size_val"] = "1";
VALIDATE(border_size_ == 1, vgettext(msg.c_str(), symbols));
} else if(usage == "mask") {
usage_ = IS_MASK;
symbols["border_size_val"] = "0";
VALIDATE(border_size_ == 0, vgettext(msg.c_str(), symbols));
} else if(usage == "") {
throw incorrect_map_format_error("Map has a header but no usage");
} else {
std::string msg = "Map has a header but an unknown usage:" + usage;
throw incorrect_map_format_error(msg.c_str());
}
}
int gamemap::read_header(const std::string& data)
{
// Test whether there is a header section
size_t header_offset = data.find("\n\n");
if(header_offset == std::string::npos) {
// For some reason Windows will fail to load a file with \r\n
// lineending properly no problems on Linux with those files.
// This workaround fixes the problem the copy later will copy
// the second \r\n to the map, but that's no problem.
header_offset = data.find("\r\n\r\n");
}
const size_t comma_offset = data.find(",");
// The header shouldn't contain commas, so if the comma is found
// before the header, we hit a \n\n inside or after a map.
// This is no header, so don't parse it as it would be.
if (!(!(header_offset == std::string::npos || comma_offset < header_offset)))
return 0;
std::string header_str(std::string(data, 0, header_offset + 1));
config header;
::read(header, header_str);
border_size_ = header["border_size"];
set_usage(header["usage"]);
return header_offset + 2;
}
void gamemap::write(config& cfg) const
{
// Convert the starting positions to a map
std::map<int, t_translation::coordinate> starting_positions;
@ -304,12 +351,14 @@ std::string gamemap::write() const
starting_positions[i] = position;
}
// Let the low level convertor do the conversion
cfg["border_size"] = border_size_;
cfg["usage"] = (usage_ == IS_MAP ? "map" : "mask");
std::ostringstream s;
s << "border_size=" << border_size_ << "\nusage="
<< (usage_ == IS_MAP ? "map" : "mask") << "\n\n"
<< t_translation::write_game_map(tiles_, starting_positions);
return s.str();
s << "\"" << t_translation::write_game_map(tiles_, starting_positions) << "\"" ;
cfg["data"] = s.str();
}
void gamemap::overlay(const gamemap& m, const config& rules_cfg, int xpos, int ypos, bool border)

View File

@ -98,24 +98,30 @@ public:
* Loads a map, with the given terrain configuration.
*
* Data should be a series of lines, with each character representing one
* hex on the map. Starting locations are represented by numbers, and will
* be of type keep.
* hex on the map. Starting locations are represented by numbers.
*
* @param cfg the game config.
* @param data the map data to load.
*/
gamemap(const config &cfg, const std::string &data); //throw(incorrect_map_format_error)
virtual ~gamemap();
/**
* Reads a map
* Loads a map, from the [map] wml config in @level.
*
* @param data The mapdata to load.
* Data should be a series of lines, with each character representing one
* hex on the map. Starting locations are represented by numbers
*
* @param cfg the game confg.
* @param level the scenario config to load from.
*/
void read(const std::string& data, const bool allow_invalid = true);
gamemap(const config &cfg, const config &level); //throw(incorrect_map_format_error)
std::string write() const;
virtual ~gamemap();
void read(const std::string& data, const bool allow_invalid = true, const int border_size = 1, const std::string usage = "map");
void write(config&) const;
/** Overlays another map onto this one at the given position. */
void overlay(const gamemap& m, const config& rules, int x=0, int y=0, bool border=false);
@ -221,12 +227,6 @@ public:
/** Returns the usage of the map. */
tusage get_usage() const { return usage_; }
/**
* The default map header, needed for maps created with
* terrain_translation::write_game_map().
*/
static const std::string default_map_header;
/** The default border style for a map. */
static const tborder default_border;
@ -255,6 +255,16 @@ protected:
void clear_border_cache() { borderCache_.clear(); }
private:
void set_usage(const std::string& usage);
/**
* Reads the header of a map which is saved in the deprecated map_data format.
*
* @param data The mapdata to load.
*/
int read_header(const std::string& data);
int num_starting_positions() const
{ return sizeof(startingPositions_)/sizeof(*startingPositions_); }

View File

@ -42,7 +42,7 @@ static lg::log_domain log_engine("engine");
config map_generator::create_scenario(const std::vector<std::string>& args)
{
config res;
res["map_data"] = create_map(args);
res["data"] = create_map(args);
return res;
}
@ -353,7 +353,7 @@ static std::string output_map(const terrain_map& terrain,
itor->second.y -= begin_y;
}
return gamemap::default_map_header + t_translation::write_game_map(map, starting_positions);
return t_translation::write_game_map(map, starting_positions);
}
namespace {

View File

@ -411,11 +411,12 @@ config default_map_generator::create_scenario(const std::vector<std::string>& ar
std::map<map_location,std::string> labels;
DBG_NG << "generating map...\n";
config& map = res.add_child("map");
try{
res["map_data"] = generate_map(args,&labels);
map["data"] = generate_map(args,&labels);
}
catch (mapgen_exception& exc){
res["map_data"] = "";
map["data"] = "";
res["error_message"] = exc.message;
}
DBG_NG << "done generating map..\n";

View File

@ -614,7 +614,12 @@ void menu_handler::save_map()
// Try to save the map, if it fails we reset the filename.
if (res == 0) {
try {
write_file(input_name, map_.write());
config file;
config& map = file.add_child("map");
map_.write(map);
std::stringstream str;
str << file;
write_file(input_name, str.str());
gui2::show_transient_message(gui_->video(), "", _("Map saved."));
} catch (io_exception& e) {
utils::string_map symbols;

View File

@ -91,7 +91,7 @@ play_controller::play_controller(const config& level, game_state& state_of_game,
level_(level),
teams_(),
gamestate_(state_of_game),
map_(game_config, level["map_data"]),
map_(game_config, level),
units_(),
undo_stack_(),
redo_stack_(),
@ -708,7 +708,10 @@ config play_controller::to_config() const
}
//write out the current state of the map
cfg["map_data"] = map_.write();
config& map = cfg.add_child("map");
map_.write(map);
cfg.merge_with(pathfind_manager_->to_config());
config display;

View File

@ -322,17 +322,16 @@ LEVEL_RESULT play_game(display& disp, game_state& gamestate, const config& game_
//level_ = scenario;
//merge carryover information into the newly generated scenario
config temp(scenario2);
ERR_GUI_D << scenario2.debug();
write_players(gamestate, temp, false, true);
gamestate.starting_pos = temp;
scenario = &scenario2;
}
std::string map_data = (*scenario)["map_data"];
if(map_data.empty() && (*scenario)["map"] != "") {
map_data = read_map((*scenario)["map"]);
}
// If the map should be randomly generated
if(map_data.empty() && (*scenario)["map_generation"] != "") {
if ( (*scenario).child_or_empty("map").empty() && map_data.empty() && (*scenario)["map_generation"] != "") {
const cursor::setter cursor_setter(cursor::WAIT);
map_data = random_generate_map((*scenario)["map_generation"],scenario->child("generator"));
@ -341,7 +340,8 @@ LEVEL_RESULT play_game(display& disp, game_state& gamestate, const config& game_
// it will not ask for the map to be generated again on reload
static config new_level;
new_level = *scenario;
new_level["map_data"] = map_data;
config& map = new_level.add_child("map");
map["data"] = map_data;
scenario = &new_level;
//merge carryover information into the scenario
@ -504,14 +504,12 @@ LEVEL_RESULT play_game(display& disp, game_state& gamestate, const config& game_
static config scenario2;
scenario2 = random_generate_scenario((*scenario)["scenario_generation"], scenario->child("generator"));
//TODO comment or remove
//level_ = scenario;
gamestate.starting_pos = scenario2;
scenario = &scenario2;
}
std::string map_data = (*scenario)["map_data"];
if(map_data.empty() && (*scenario)["map"] != "") {
map_data = read_map((*scenario)["map"]);
}
// If the map should be randomly generated
if(map_data.empty() && (*scenario)["map_generation"] != "") {
@ -523,7 +521,8 @@ LEVEL_RESULT play_game(display& disp, game_state& gamestate, const config& game_
// it will not ask for the map to be generated again on reload
static config new_level;
new_level = *scenario;
new_level["map_data"] = map_data;
config& map = new_level.add_child("map");
map["data"] = map_data;
scenario = &new_level;
gamestate.starting_pos = new_level;

View File

@ -717,7 +717,8 @@ void loadgame::fill_mplevel_config(config& level){
// If we have a start of scenario MP campaign scenario the snapshot
// is empty the starting position contains the wanted info.
const config& start_data = !gamestate_.snapshot.empty() ? gamestate_.snapshot : gamestate_.starting_pos;
level["map_data"] = start_data["map_data"];
level.add_child("map", start_data.child_or_empty("map"));
level["id"] = start_data["id"];
level["name"] = start_data["name"];
level["completion"] = start_data["completion"];