Exemple #1
0
void gamemap::set_terrain(const map_location& loc, const t_translation::t_terrain terrain, const tmerge_mode mode, bool replace_if_failed) {
	if(!on_board_with_border(loc)) {
		// off the map: ignore request
		return;
	}

	t_translation::t_terrain new_terrain = merge_terrains(get_terrain(loc), terrain, mode, replace_if_failed);

	if(new_terrain == t_translation::NONE_TERRAIN) {
		return;
	}

	if(on_board(loc)) {
		const bool old_village = is_village(loc);
		const bool new_village = is_village(new_terrain);

		if(old_village && !new_village) {
			villages_.erase(std::remove(villages_.begin(),villages_.end(),loc),villages_.end());
		} else if(!old_village && new_village) {
			villages_.push_back(loc);
		}
	}

	tiles_[loc.x + border_size_][loc.y + border_size_] = new_terrain;

	// Update the off-map autogenerated tiles
	map_location adj[6];
	get_adjacent_tiles(loc,adj);

	for(int n = 0; n < 6; ++n) {
		remove_from_border_cache(adj[n]);
	}
}
Exemple #2
0
void gamemap::set_terrain(const map_location& loc, const t_translation::terrain_code & terrain, const terrain_type_data::merge_mode mode, bool replace_if_failed) {
	if(!on_board_with_border(loc)) {
		DBG_G << "set_terrain: " << loc << " is not on the map.\n";
		// off the map: ignore request
		return;
	}

	t_translation::terrain_code new_terrain = tdata_->merge_terrains(get_terrain(loc), terrain, mode, replace_if_failed);

	if(new_terrain == t_translation::NONE_TERRAIN) {
		return;
	}

	if(on_board(loc)) {
		const bool old_village = is_village(loc);
		const bool new_village = tdata_->is_village(new_terrain);

		if(old_village && !new_village) {
			villages_.erase(std::remove(villages_.begin(),villages_.end(),loc),villages_.end());
		} else if(!old_village && new_village) {
			villages_.push_back(loc);
		}
	}

	(*this)[loc] = new_terrain;
}
Exemple #3
0
	bool is_village(const map_location& loc) const
		{ return on_board(loc) && is_village(get_terrain(loc)); }
Exemple #4
0
t_translation::t_terrain gamemap::get_terrain(const map_location& loc) const
{

	if(on_board_with_border(loc)) {
		return tiles_[loc.x + border_size_][loc.y + border_size_];
	}

	const std::map<map_location, t_translation::t_terrain>::const_iterator itor = borderCache_.find(loc);
	if(itor != borderCache_.end())
		return itor->second;

	// If not on the board, decide based on what surrounding terrain is
	t_translation::t_terrain items[6];
	int nitems = 0;

	map_location adj[6];
	get_adjacent_tiles(loc,adj);
	for(int n = 0; n != 6; ++n) {
		if(on_board(adj[n])) {
			items[nitems] = tiles_[adj[n].x][adj[n].y];
			++nitems;
		} else {
			// If the terrain is off map but already in the border cache,
			// this will be used to determine the terrain.
			// This avoids glitches
			// * on map with an even width in the top right corner
			// * on map with an odd height in the bottom left corner.
			// It might also change the result on other map and become random,
			// but the border tiles will be determined in the future, so then
			// this will no longer be used in the game
			// (The editor will use this feature to expand maps in a better way).
			std::map<map_location, t_translation::t_terrain>::const_iterator itor =
				borderCache_.find(adj[n]);

			// Only add if it is in the cache and a valid terrain
			if(itor != borderCache_.end() &&
					itor->second != t_translation::NONE_TERRAIN)  {

				items[nitems] = itor->second;
				++nitems;
			}
		}

	}

	// Count all the terrain types found,
	// and see which one is the most common, and use it.
	t_translation::t_terrain used_terrain;
	int terrain_count = 0;
	for(int i = 0; i != nitems; ++i) {
		if(items[i] != used_terrain && !is_village(items[i]) && !is_keep(items[i])) {
			const int c = std::count(items+i+1,items+nitems,items[i]) + 1;
			if(c > terrain_count) {
				used_terrain = items[i];
				terrain_count = c;
			}
		}
	}

	borderCache_.insert(std::pair<map_location, t_translation::t_terrain>(loc,used_terrain));
	return used_terrain;

}
Exemple #5
0
void gamemap::read(const std::string& data)
{
	// Initial stuff
	tiles_.clear();
	villages_.clear();
	std::fill(startingPositions_, startingPositions_ +
		sizeof(startingPositions_) / sizeof(*startingPositions_), map_location());
	std::map<int, t_translation::coordinate> starting_positions;

	if(data.empty()) {
		w_ = 0;
		h_ = 0;
		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"));

	std::string header_str(std::string(data, 0, header_offset + 1));
	config header;
	::read(header, header_str);

	border_size_ = lexical_cast_default<int>(header["border_size"], 0);
	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_exception("Map has a header but no usage");
	} else {
		std::string msg = "Map has a header but an unknown usage:" + usage;
		throw incorrect_map_format_exception(msg.c_str());
	}

	/* The third parameter is required for MSVC++ 6.0 */
	const std::string& map = std::string(data, header_offset + 2, std::string::npos);

	try {
		tiles_ = t_translation::read_game_map(map, starting_positions);

	} catch(t_translation::error& e) {
		// We re-throw the error but as map error.
		// Since all codepaths test for this, it's the least work.
		throw incorrect_map_format_exception(e.message);
	}

	// Convert the starting positions to the array
	std::map<int, t_translation::coordinate>::const_iterator itor =
		starting_positions.begin();

	for(; itor != starting_positions.end(); ++itor) {

		// Check for valid position,
		// the first valid position is 1,
		// so the offset 0 in the array is never used.
		if(itor->first < 1 || itor->first >= MAX_PLAYERS+1) {
			std::stringstream ss;
			ss << "Starting position " << itor->first << " out of range\n";
			ERR_CF << ss.str();
			ss << "The map cannot be loaded.";
			throw incorrect_map_format_exception(ss.str().c_str());
		}

		// Add to the starting position array
		startingPositions_[itor->first] = map_location(itor->second.x - 1, itor->second.y - 1);
	}

	// Post processing on the map
	total_width_ = tiles_.size();
	total_height_ = total_width_ > 0 ? tiles_[0].size() : 0;
	w_ = total_width_ - 2 * border_size_;
	h_ = total_height_ - 2 * border_size_;

	for(int x = 0; x < total_width_; ++x) {
		for(int y = 0; y < total_height_; ++y) {

			// Is the terrain valid?
			if(tcodeToTerrain_.count(tiles_[x][y]) == 0) {
				if(!try_merge_terrains(tiles_[x][y])) {
					std::stringstream ss;
					ss << "Illegal character in map: (" << t_translation::write_terrain_code(tiles_[x][y])
						   << ") '" << tiles_[x][y] << "'\n";
					ERR_CF << ss.str();
					ss << "The map cannot be loaded.";
					throw incorrect_map_format_exception(ss.str().c_str());
				}
			}

			// Is it a village?
			if(x >= border_size_ && y >= border_size_
					&& x < total_width_-border_size_  && y < total_height_-border_size_
					&& is_village(tiles_[x][y])) {
				villages_.push_back(map_location(x-border_size_, y-border_size_));
			}
		}
	}
}
Exemple #6
0
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_ +
		sizeof(startingPositions_) / sizeof(*startingPositions_), map_location());
	std::map<int, t_translation::coordinate> starting_positions;

	if(data.empty()) {
		w_ = 0;
		h_ = 0;
		total_width_ = 0;
		total_height_ = 0;
		if(allow_invalid) return;
	}

	int offset = read_header(data);

	const std::string& data_only = std::string(data, offset);

	try {
		tiles_ = t_translation::read_game_map(data_only, starting_positions);

	} catch(t_translation::error& e) {
		// We re-throw the error but as map error.
		// Since all codepaths test for this, it's the least work.
		throw incorrect_map_format_error(e.message);
	}

	// Convert the starting positions to the array
	std::map<int, t_translation::coordinate>::const_iterator itor =
		starting_positions.begin();

	for(; itor != starting_positions.end(); ++itor) {

		// Check for valid position,
		// the first valid position is 1,
		// so the offset 0 in the array is never used.
		if(itor->first < 1 || itor->first >= MAX_PLAYERS+1) {
			std::stringstream ss;
			ss << "Starting position " << itor->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
		startingPositions_[itor->first] = map_location(itor->second.x - 1, itor->second.y - 1);
	}

	// Post processing on the map
	total_width_ = tiles_.size();
	total_height_ = total_width_ > 0 ? tiles_[0].size() : 0;
	w_ = total_width_ - 2 * border_size_;
	h_ = total_height_ - 2 * border_size_;
	//Disabled since there are callcases which pass along a valid map header but empty
	//map data. Still, loading (and actually applying) an empty map causes problems later on.
	//Other callcases which need to load a dummy map use completely empty data :(.
	//VALIDATE((w_ >= 1 && h_ >= 1), "A map needs at least 1 tile, the map cannot be loaded.");

	for(int x = 0; x < total_width_; ++x) {
		for(int y = 0; y < total_height_; ++y) {

			// Is the terrain valid?
			if(tcodeToTerrain_.count(tiles_[x][y]) == 0) {
				if(!try_merge_terrains(tiles_[x][y])) {
					std::stringstream ss;
					ss << "Illegal tile in map: (" << t_translation::write_terrain_code(tiles_[x][y])
						   << ") '" << tiles_[x][y] << "'\n";
					ERR_CF << ss.str();
					ss << "The map cannot be loaded.";
					throw incorrect_map_format_error(ss.str().c_str());
				}
			}

			// Is it a village?
			if(x >= border_size_ && y >= border_size_
					&& x < total_width_-border_size_  && y < total_height_-border_size_
					&& is_village(tiles_[x][y])) {
				villages_.push_back(map_location(x-border_size_, y-border_size_));
			}
		}
	}
}