Ejemplo n.º 1
0
void ScriptRunner::timerFired(Timer<ScriptRunner>* timer)
{
	ActionLogScope log_scope("script runner timer");
    ASSERT_UNUSED(timer, timer == &m_timer);

    RefPtr<Document> protect(m_document);

    Vector<PendingScript> scripts;
    scripts.swap(m_scriptsToExecuteSoon);

    size_t numInOrderScriptsToExecute = 0;
    for (; numInOrderScriptsToExecute < m_scriptsToExecuteInOrder.size() && m_scriptsToExecuteInOrder[numInOrderScriptsToExecute].cachedScript()->isLoaded(); ++numInOrderScriptsToExecute)
        scripts.append(m_scriptsToExecuteInOrder[numInOrderScriptsToExecute]);
    if (numInOrderScriptsToExecute)
        m_scriptsToExecuteInOrder.remove(0, numInOrderScriptsToExecute);

    size_t size = scripts.size();
    for (size_t i = 0; i < size; ++i) {
        CachedScript* cachedScript = scripts[i].cachedScript();
        RefPtr<Element> element = scripts[i].releaseElementAndClear();
        ScriptElement* script = toScriptElement(element.get());
        ActionLogFormat(ActionLog::READ_MEMORY, "ScriptRunner-%p-%p",
        		static_cast<void*>(this), static_cast<void*>(script));
        script->execute(cachedScript);
        m_document->decrementLoadEventDelayCount();
    }
}
Ejemplo n.º 2
0
void loadgame::load_multiplayer_game()
{
	show_dialog(false, false);

	if (filename_.empty())
		throw load_game_cancelled_exception();

	std::string error_log;
	{
		cursor::setter cur(cursor::WAIT);
		log_scope("load_game");

		manager::read_save_file(filename_, load_config_, &error_log);
		copy_era(load_config_);

		gamestate_ = game_state(load_config_);
	}

	if(!error_log.empty()) {
		gui2::show_error_message(gui_.video(),
				_("The file you have tried to load is corrupt: '") +
				error_log);
		throw load_game_cancelled_exception();
	}

	if(gamestate_.classification().campaign_type != "multiplayer") {
		gui2::show_message(gui_.video(), "", _("This is not a multiplayer save"));
		throw load_game_cancelled_exception();
	}

	check_version_compatibility();
}
Ejemplo n.º 3
0
bool loadgame::load_multiplayer_game()
{
	show_dialog(false, false);

	if (filename_.empty())
		return false;

	std::string error_log;
	{
		cursor::setter cur(cursor::WAIT);
		log_scope("load_game");

		read_save_file(filename_, load_config_, &error_log);
		copy_era(load_config_);

		gamestate_.set_data(load_config_);
	}

	if(!error_log.empty()) {
		gui2::show_error_message(gui_.video(),
				_("The file you have tried to load is corrupt: '") +
				error_log);
		return false;
	}

	if(gamestate_.classification().campaign_type != game_classification::CAMPAIGN_TYPE::MULTIPLAYER) {
		gui2::show_transient_error_message(gui_.video(), _("This is not a multiplayer save."));
		return false;
	}

	return check_version_compatibility();
}
Ejemplo n.º 4
0
void manager::load_summary(const std::string& name, config& cfg_summary, std::string* error_log){
	log_scope("load_game_summary");

	config cfg;
	read_save_file(name,cfg,error_log);

	::extract_summary_from_config(cfg, cfg_summary);
}
Ejemplo n.º 5
0
void save_index::write_save_index()
{
	log_scope("write_save_index()");
	try {
		scoped_ostream stream = ostream_file(get_save_index_file());
		write(*stream, load());
	} catch(io_exception& e) {
		ERR_SAVE << "error writing to save index file: '" << e.what() << "'\n";
	}
}
Ejemplo n.º 6
0
void ingame_savegame::write_game(config_writer &out) {
	log_scope("write_game");

	savegame::write_game(out);
	
	gamestate().write_carryover(out);
	out.write_child("snapshot",snapshot());
	out.write_child("replay_start", gamestate().replay_start());
	out.write_child("replay", gamestate().replay_data);
}
Ejemplo n.º 7
0
void ResourceLoader::cancel(const ResourceError& error)
{
    // If the load has already completed - succeeded, failed, or previously cancelled - do nothing.
    if (m_reachedTerminalState)
        return;
       
    ResourceError nonNullError = error.isNull() ? cancelledError() : error;
    
    // willCancel() and didFailToLoad() both call out to clients that might do 
    // something causing the last reference to this object to go away.
    RefPtr<ResourceLoader> protector(this);
    
    // If we re-enter cancel() from inside willCancel(), we want to pick up from where we left 
    // off without re-running willCancel()
    if (!m_calledWillCancel) {
        m_calledWillCancel = true;
        
        willCancel(nonNullError);
    }

    // If we re-enter cancel() from inside didFailToLoad(), we want to pick up from where we 
    // left off without redoing any of this work.
    if (!m_cancelled) {
        m_cancelled = true;
        
        if (FormData* data = m_request.httpBody())
            data->removeGeneratedFilesIfNeeded();

        if (m_handle)
            m_handle->clearAuthentication();

        m_documentLoader->cancelPendingSubstituteLoad(this);
        if (m_handle) {
            m_handle->cancel();
            m_handle = 0;
        }

        if (m_options.sendLoadCallbacks == SendCallbacks && m_identifier && !m_notifiedLoadComplete) {
        	ActionLogScope log_scope(
            		String::format("cancel_fail:%s",
            				m_request.url().lastPathComponent().ascii().data()).ascii().data());
            frameLoader()->notifier()->didFailToLoad(this, nonNullError);
        }
    }

    // If cancel() completed from within the call to willCancel() or didFailToLoad(),
    // we don't want to redo didCancel() or releasesResources().
    if (m_reachedTerminalState)
        return;

    didCancel(nonNullError);
            
    releaseResources();
}
Ejemplo n.º 8
0
void savegame::write_game(config_writer &out)
{
	log_scope("write_game");

	out.write_key_val("version", game_config::version);
	
	gamestate_.write_general_info(out);
	out.open_child("statistics");
	statistics::write_stats(out);
	out.close_child("statistics");
}
Ejemplo n.º 9
0
void savegame::write_game(config_writer &out)
{
	log_scope("write_game");

	out.write_key_val("version", game_config::version);
	out.write_key_val("next_underlying_unit_id", lexical_cast<std::string>(n_unit::id_manager::instance().get_save_id()));

	gamestate_.write_general_info(out);
	out.open_child("statistics");
	statistics::write_stats(out);
	out.close_child("statistics");
}
Ejemplo n.º 10
0
void ingame_savegame::write_game(config_writer &out) {
	log_scope("write_game");

	savegame::write_game(out);

	gamestate().write_carryover(out);
	out.write_child("snapshot",gamestate().get_starting_pos());
	out.write_child("replay_start", gamestate().replay_start());
	out.open_child("replay");
	gamestate().get_replay().write(out);
	out.close_child("replay");
}
Ejemplo n.º 11
0
void ResourceLoader::didFinishLoadingOnePart(double finishTime)
{
    if (m_cancelled)
        return;
    ASSERT(!m_reachedTerminalState);

    if (m_notifiedLoadComplete)
        return;
    m_notifiedLoadComplete = true;
    if (m_options.sendLoadCallbacks == SendCallbacks) {
    	// SRL: Log end of network response.
    	ActionLogScope log_scope(
        		String::format("finish_load:%s",
        				m_request.url().lastPathComponent().ascii().data()).ascii().data());
        frameLoader()->notifier()->didFinishLoad(this, finishTime);
    }
}
Ejemplo n.º 12
0
void playsingle_controller::before_human_turn(bool save)
{
	log_scope("player turn");
	browse_ = false;
	linger_ = false;

	ai::manager::raise_turn_started();

	if(save && level_result_ == NONE) {
		savegame::autosave_savegame save(gamestate_, *gui_, to_config(), preferences::save_compression_format());
		save.autosave(game_config::disable_autosave, preferences::autosavemax(), preferences::INFINITE_AUTO_SAVES);
	}

	if(preferences::turn_bell() && level_result_ == NONE) {
		sound::play_bell(game_config::sounds::turn_bell);
	}
}
Ejemplo n.º 13
0
possible_end_play_signal playsingle_controller::before_human_turn()
{
    log_scope("player turn");
    browse_ = false;
    linger_ = false;

    HANDLE_END_PLAY_SIGNAL( ai::manager::raise_turn_started() ); //This line throws exception from here: https://github.com/wesnoth/wesnoth/blob/ac96a2b91b3276e20b682210617cf87d1e0d366a/src/playsingle_controller.cpp#L954

    if(do_autosaves_ && level_result_ == NONE) {
        savegame::autosave_savegame save(saved_game_, *gui_, to_config(), preferences::save_compression_format());
        save.autosave(game_config::disable_autosave, preferences::autosavemax(), preferences::INFINITE_AUTO_SAVES);
    }

    if(preferences::turn_bell() && level_result_ == NONE) {
        sound::play_bell(game_config::sounds::turn_bell);
    }
    return boost::none;
}
Ejemplo n.º 14
0
void playsingle_controller::before_human_turn()
{
	log_scope("player turn");
	assert(!linger_);
	if(end_turn_ != END_TURN_NONE || is_regular_game_end()) {
		return;
	}

	if(init_side_done_now_) {
		scoped_savegame_snapshot snapshot(*this);
		savegame::autosave_savegame save(saved_game_, *gui_, preferences::save_compression_format());
		save.autosave(game_config::disable_autosave, preferences::autosavemax(), preferences::INFINITE_AUTO_SAVES);
	}

	if(preferences::turn_bell()) {
		sound::play_bell(game_config::sounds::turn_bell);
	}
}
Ejemplo n.º 15
0
void help_browser::show_topic(const topic &t, bool save_in_history)
{
	log_scope("show_topic");

	if (save_in_history) {
		forward_topics_.clear();
		if (shown_topic_ != NULL) {
			if (back_topics_.size() > max_history) {
				back_topics_.pop_front();
			}
			back_topics_.push_back(shown_topic_);
		}
	}

	shown_topic_ = &t;
	text_area_.show_topic(t);
	menu_.select_topic(t);
	update_cursor();
}
Ejemplo n.º 16
0
void playsingle_controller::before_human_turn()
{
	log_scope("player turn");
	assert(!linger_);
	if(end_turn_ != END_TURN_NONE) {
		return;
	}
	//TODO: why do we need the next line?
	ai::manager::raise_turn_started();

	if(init_side_done_now_ && !is_regular_game_end()) {
		update_savegame_snapshot();
		savegame::autosave_savegame save(saved_game_, *gui_, preferences::save_compression_format());
		save.autosave(game_config::disable_autosave, preferences::autosavemax(), preferences::INFINITE_AUTO_SAVES);
	}

	if(preferences::turn_bell() && !is_regular_game_end()) {
		sound::play_bell(game_config::sounds::turn_bell);
	}
}
Ejemplo n.º 17
0
void ResourceLoader::didReceiveData(const char* data, int length, long long encodedDataLength, bool allAtOnce)
{
    // The following assertions are not quite valid here, since a subclass
    // might override didReceiveData in a way that invalidates them. This
    // happens with the steps listed in 3266216
    // ASSERT(con == connection);
    // ASSERT(!m_reachedTerminalState);

    // Protect this in this delegate method since the additional processing can do
    // anything including possibly derefing this; one example of this is Radar 3266216.
    RefPtr<ResourceLoader> protector(this);

    addData(data, length, allAtOnce);
    // FIXME: If we get a resource with more than 2B bytes, this code won't do the right thing.
    // However, with today's computers and networking speeds, this won't happen in practice.
    // Could be an issue with a giant local file.
    if (m_options.sendLoadCallbacks == SendCallbacks && m_frame) {
    	ActionLogScope log_scope(
        		String::format("recv_data:%s",
        				m_request.url().lastPathComponent().ascii().data()).ascii().data());
        frameLoader()->notifier()->didReceiveData(this, data, length, static_cast<int>(encodedDataLength));
    }
}
Ejemplo n.º 18
0
void terrain_builder::build_terrains()
{
	log_scope("terrain_builder::build_terrains");

	// Builds the terrain_by_type_ cache
	for(int x = -2; x <= map().w(); ++x) {
		for(int y = -2; y <= map().h(); ++y) {
			const map_location loc(x,y);
			const t_translation::t_terrain t = map().get_terrain(loc);

			terrain_by_type_[t].push_back(loc);
		}
	}

	int rule_index = 0;
	building_ruleset::const_iterator r;

	for(r = building_rules_.begin(); r != building_rules_.end(); ++r) {

		const building_rule& rule = r->second;

		// Find the constraint that contains the less terrain of all terrain rules.
		// We will keep a track of the matching terrains of this constraint
		// and later try to apply the rule only on them
		size_t min_size = INT_MAX;
		t_translation::t_list min_types;
		constraint_set::const_iterator min_constraint = rule.constraints.end();

		for(constraint_set::const_iterator constraint = rule.constraints.begin();
		    	constraint != rule.constraints.end(); ++constraint) {

		    const t_translation::t_match& match = constraint->second.terrain_types_match;
			t_translation::t_list matching_types;
			size_t constraint_size = 0;

			for (terrain_by_type_map::iterator type_it = terrain_by_type_.begin();
					 type_it != terrain_by_type_.end(); ++type_it) {

				const t_translation::t_terrain t = type_it->first;
				if (terrain_matches(t, match)) {
					const size_t match_size = type_it->second.size();
					constraint_size += match_size;
					if (constraint_size >= min_size) {
						break; // not a minimum, bail out
					}
					matching_types.push_back(t);
				}
			}

			if (constraint_size < min_size) {
				min_size = constraint_size;
				min_types = matching_types;
				min_constraint = constraint;
				if (min_size == 0) {
				 	// a constraint is never matched on this map
				 	// we break with a empty type list
					break;
				}
			}
		}

		//NOTE: if min_types is not empty, we have found a valid min_constraint;
		for(t_translation::t_list::const_iterator t = min_types.begin();
				t != min_types.end(); ++t) {

			const std::vector<map_location>* locations = &terrain_by_type_[*t];

			for(std::vector<map_location>::const_iterator itor = locations->begin();
					itor != locations->end(); ++itor) {
				const map_location loc = itor->legacy_difference(min_constraint->second.loc);

				if(rule_matches(rule, loc, rule_index, min_constraint)) {
					apply_rule(rule, loc);
				}
			}
		}

		++rule_index;
	}
}
Ejemplo n.º 19
0
void show_hotkeys_dialog (display & disp, config *save_config)
{
	log_scope ("show_hotkeys_dialog");

	const events::event_context dialog_events_context;

	const int centerx = disp.w()/2;
	const int centery = disp.h()/2;
	const int width  = 700;
	const int height = disp.video().gety() < 600 ? 380 : 500;
	const int xpos = centerx  - width/2;
	const int ypos = centery  - height/2;

	gui::button close_button (disp.video(), _("Close"));
	std::vector<gui::button*> buttons;
	buttons.push_back(&close_button);

	gui::dialog_frame f(disp.video(),_("Hotkey Settings"),gui::dialog_frame::default_style,true,&buttons);
	f.layout(xpos,ypos,width,height);
	f.draw();

	SDL_Rect clip_rect = create_rect(0, 0, disp.w (), disp.h ());
	SDL_Rect text_size = font::draw_text(NULL, clip_rect, font::SIZE_LARGE,
					     font::NORMAL_COLOR,_("Press desired hotkey (Esc cancels)"),
					     0, 0);

	std::vector<std::string> menu_items;

	std::vector<hotkey::hotkey_item>& hotkeys = hotkey::get_hotkeys();
	for(std::vector<hotkey::hotkey_item>::iterator i = hotkeys.begin(); i != hotkeys.end(); ++i) {
		if(i->hidden() || !i->is_in_active_scope())
			continue;
		std::stringstream str,name;
		name << i->get_description();
		str << name.str();
		str << COLUMN_SEPARATOR;
		// This trick allows to display chars identical to markup characters
		str << font::NULL_MARKUP << i->get_name();
		menu_items.push_back(str.str());
	}

	std::ostringstream heading;
	heading << HEADING_PREFIX << _("Action") << COLUMN_SEPARATOR << _("Binding");
	menu_items.push_back(heading.str());

	gui::menu::basic_sorter sorter;
	sorter.set_alpha_sort(0).set_alpha_sort(1);

	gui::menu menu_(disp.video(), menu_items, false, height - font::relative_size(10), -1, &sorter, &gui::menu::bluebg_style);
	menu_.sort_by(0);
	menu_.reset_selection();
	menu_.set_width(font::relative_size(500));
	menu_.set_location(xpos + font::relative_size(10), ypos + font::relative_size(10));

	gui::button change_button (disp.video(), _("Change Hotkey"));
	change_button.set_location(xpos + width - change_button.width () - font::relative_size(30),ypos + font::relative_size(30));

	gui::button clear_button (disp.video(), _("Clear Hotkey"));
	clear_button.set_location(xpos + width - clear_button.width () - font::relative_size(30),ypos + font::relative_size(80));
//	gui::button save_button (disp.video(), _("Save Hotkeys"));
//	save_button.set_location(xpos + width - save_button.width () - font::relative_size(30),ypos + font::relative_size(130));

	escape_handler esc_hand;

	for(;;) {

		if (close_button.pressed() || esc_hand.escape_pressed())
		{
			if (save_config == NULL) {
				save_hotkeys();
			} else {
				hotkey::save_hotkeys(*save_config);
			}
			break;
		}

		if (change_button.pressed () || menu_.double_clicked()) {
			// Lets change this hotkey......
			SDL_Rect dlgr = create_rect(centerx - text_size.w / 2 - 30
					, centery - text_size.h / 2 - 16
					, text_size.w + 60
					, text_size.h + 32);

			surface_restorer restorer(&disp.video(),dlgr);
			gui::dialog_frame mini_frame(disp.video());
			mini_frame.layout(centerx-text_size.w/2 - 20,
									centery-text_size.h/2 - 6,
									text_size.w+40,
									text_size.h+12);
			mini_frame.draw_background();
			mini_frame.draw_border();
			font::draw_text (&disp.video(), clip_rect, font::SIZE_LARGE,font::NORMAL_COLOR,
				 _("Press desired hotkey (Esc cancels)"),centerx-text_size.w/2,
				 centery-text_size.h/2);
			disp.update_display();
			SDL_Event event;
			event.type = 0;
			int character = 0, keycode = 0, mod = 0; // Just to avoid warning
			int joystick = 0, button = 0, hat = 0, value = 0;
			const int any_mod = KMOD_CTRL | KMOD_ALT | KMOD_LMETA;

			while (event.type!=SDL_KEYDOWN && event.type!=SDL_JOYBUTTONDOWN && event.type!= SDL_JOYHATMOTION) SDL_PollEvent(&event);
			do {
				if (event.type==SDL_KEYDOWN)
				{
					keycode=event.key.keysym.sym;
					character=event.key.keysym.unicode;
					mod=event.key.keysym.mod;
				};
				if (event.type==SDL_JOYBUTTONDOWN) {
					joystick = event.jbutton.which;
					button = event.jbutton.button;
				}
				if (event.type==SDL_JOYHATMOTION) {
					joystick = event.jhat.which;
					hat = event.jhat.hat;
					value = event.jhat.value;
				}
				SDL_PollEvent(&event);
				disp.flip();
				disp.delay(10);
			} while (event.type!=SDL_KEYUP && event.type!=SDL_JOYBUTTONUP && event.type!=SDL_JOYHATMOTION);
			restorer.restore();
			disp.update_display();
			if (keycode == SDLK_ESCAPE && (mod & any_mod) == 0) {
				//cancel -- no action
			} else {
				const hotkey::hotkey_item& oldhk = hotkey::get_hotkey(character, keycode, (mod & KMOD_SHIFT) != 0,
						(mod & KMOD_CTRL) != 0, (mod & KMOD_ALT) != 0, (mod & KMOD_LMETA) != 0);

				hotkey::hotkey_item& newhk = hotkey::get_visible_hotkey(menu_.selection());

				if(oldhk.get_id() != newhk.get_id() && !oldhk.null()) {
					std::stringstream msg;
					msg << "   " << oldhk.get_description() << " : " << oldhk.get_name();
					gui2::show_transient_message(disp.video(),_("This hotkey is already in use."),msg.str());
				} else {
					if (event.type == SDL_JOYHATMOTION) {
						const hotkey::hotkey_item& oldhkhat = hotkey::get_hotkey(joystick, hat, value);

						if(oldhkhat.get_id() != newhk.get_id() && !oldhkhat.null()) {
							std::stringstream msg;
							msg << "   " << oldhkhat.get_description() << " : " << oldhkhat.get_name();
							gui2::show_transient_message(disp.video(),_("This hotkey is already in use."),msg.str());
						} else {
							newhk.set_hat(joystick, hat, value);
							menu_.change_item(menu_.selection(), 1, font::NULL_MARKUP + newhk.get_name());
						}
					} else
					if (event.type == SDL_JOYBUTTONUP) {
						const hotkey::hotkey_item& oldhkbtn = hotkey::get_hotkey(button, joystick);

						if(oldhkbtn.get_id() != newhk.get_id() && !oldhkbtn.null()) {
							std::stringstream msg;
							msg << "   " << oldhkbtn.get_description() << " : " << oldhkbtn.get_name();
							gui2::show_transient_message(disp.video(),_("This hotkey is already in use."),msg.str());
						} else {
							newhk.set_button(button, joystick);
							menu_.change_item(menu_.selection(), 1, font::NULL_MARKUP + newhk.get_name());
						}
					} else {

						newhk.set_key(character, keycode, (mod & KMOD_SHIFT) != 0,
								(mod & KMOD_CTRL) != 0, (mod & KMOD_ALT) != 0, (mod & KMOD_LMETA) != 0);

						menu_.change_item(menu_.selection(), 1, font::NULL_MARKUP + newhk.get_name());

						if ((newhk.get_id() == hotkey::HOTKEY_SCREENSHOT
								|| newhk.get_id() == hotkey::HOTKEY_MAP_SCREENSHOT)
								&& (mod & any_mod) == 0) {
							gui2::show_transient_message(disp.video(), _("Warning"), _("Screenshot hotkeys should be combined with the Control, Alt or Meta modifiers to avoid problems."));
						}
					}
				}
			}
		}
//		if (save_button.pressed()) {
//			if (save_config == NULL) {
//				save_hotkeys();
//			} else {
//				hotkey::save_hotkeys(*save_config);
//			}
//		}

		if (clear_button.pressed()) {
			// clear hotkey
			hotkey::hotkey_item& newhk = hotkey::get_visible_hotkey(menu_.selection());
			newhk.clear_hotkey();
			menu_.change_item(menu_.selection(), 1, font::NULL_MARKUP + newhk.get_name());
		}

		menu_.process();

		events::pump();
		events::raise_process_event();
		events::raise_draw_event();

		disp.update_display();

		disp.delay(10);
	}
}
Ejemplo n.º 20
0
void play_controller::do_init_side()
{
    set_scontext_synced sync;
    log_scope("player turn");
    // In case we might end up calling sync:network during the side turn events,
    // and we don't want do_init_side to be called when a player drops.
    gamestate_->init_side_done() = true;
    init_side_done_now_ = true;

    const std::string turn_num = std::to_string(turn());
    const std::string side_num = std::to_string(current_side());

    gamestate().gamedata_.get_variable("side_number") = current_side();

    // We might have skipped some sides because they were empty so it is not enough to check for side_num==1
    if(!gamestate().tod_manager_.has_turn_event_fired())
    {
        pump().fire("turn_" + turn_num);
        pump().fire("new_turn");
        gamestate().tod_manager_.turn_event_fired();
    }

    pump().fire("side_turn");
    pump().fire("side_" + side_num + "_turn");
    pump().fire("side_turn_" + turn_num);
    pump().fire("side_" + side_num + "_turn_" + turn_num);

    // We want to work out if units for this player should get healed,
    // and the player should get income now.
    // Healing/income happen if it's not the first turn of processing,
    // or if we are loading a game.
    if (turn() > 1) {
        gamestate().board_.new_turn(current_side());
        current_team().new_turn();

        // If the expense is less than the number of villages owned
        // times the village support capacity,
        // then we don't have to pay anything at all
        int expense = gamestate().board_.side_upkeep(current_side()) -
                      current_team().support();
        if(expense > 0) {
            current_team().spend_gold(expense);
        }

        calculate_healing(current_side(), !is_skipping_replay());
    }

    // Prepare the undo stack.
    undo_stack().new_side_turn(current_side());

    pump().fire("turn_refresh");
    pump().fire("side_" + side_num + "_turn_refresh");
    pump().fire("turn_" + turn_num + "_refresh");
    pump().fire("side_" + side_num + "_turn_" + turn_num + "_refresh");

    // Make sure vision is accurate.
    actions::clear_shroud(current_side(), true);
    init_side_end();
    check_victory();
    sync.do_final_checkup();
}
Ejemplo n.º 21
0
void unit_attack(
                 const map_location& a, const map_location& b, int damage,
                 const attack_type& attack, const attack_type* secondary_attack,
		  int swing,std::string hit_text,bool drain,std::string att_text)
{
	game_display* disp = game_display::get_singleton();
	if(!disp ||disp->video().update_locked() || disp->video().faked() ||
			(disp->fogged(a) && disp->fogged(b)) || preferences::show_combat() == false) {
		return;
	}
	unit_map& units = disp->get_units();
	disp->select_hex(map_location::null_location);

	// scroll such that there is at least half a hex spacing around fighters
	disp->scroll_to_tiles(a,b,game_display::ONSCREEN,true,0.5,false);

	log_scope("unit_attack");

	const unit_map::iterator att = units.find(a);
	assert(att != units.end());
	unit& attacker = *att;

	const unit_map::iterator def = units.find(b);
	assert(def != units.end());
	unit &defender = *def;
	int def_hitpoints = defender.hitpoints();

	att->set_facing(a.get_relative_dir(b));
	def->set_facing(b.get_relative_dir(a));
	defender.set_facing(b.get_relative_dir(a));


	unit_animator animator;
	unit_ability_list leaders = attacker.get_abilities("leadership");
	unit_ability_list helpers = defender.get_abilities("resistance");

	std::string text ;
	if(damage) text = lexical_cast<std::string>(damage);
	if(!hit_text.empty()) {
		text.insert(text.begin(),hit_text.size()/2,' ');
		text = text + "\n" + hit_text;
	}

	std::string text_2 ;
	if(drain && damage) text_2 = lexical_cast<std::string>(std::min<int>(damage,defender.hitpoints())/2);
	if(!att_text.empty()) {
		text_2.insert(text_2.begin(),att_text.size()/2,' ');
		text_2 = text_2 + "\n" + att_text;
	}

	unit_animation::hit_type hit_type;
	if(damage >= defender.hitpoints()) {
		hit_type = unit_animation::KILL;
	} else if(damage > 0) {
		hit_type = unit_animation::HIT;
	}else {
		hit_type = unit_animation::MISS;
	}
	animator.add_animation(&attacker, "attack", att->get_location(),
		def->get_location(), damage, true,  text_2,
		display::rgb(0, 255, 0), hit_type, &attack, secondary_attack,
		swing);

	// note that we take an anim from the real unit, we'll use it later
	const unit_animation *defender_anim = def->choose_animation(*disp,
		def->get_location(), "defend", att->get_location(), damage,
		hit_type, &attack, secondary_attack, swing);
	animator.add_animation(&defender, defender_anim, def->get_location(),
		true,  text , display::rgb(255, 0, 0));

	for (std::vector<std::pair<const config *, map_location> >::iterator itor = leaders.cfgs.begin(); itor != leaders.cfgs.end(); ++itor) {
		if(itor->second == a) continue;
		if(itor->second == b) continue;
		unit_map::iterator leader = units.find(itor->second);
		assert(leader != units.end());
		leader->set_facing(itor->second.get_relative_dir(a));
		animator.add_animation(&*leader, "leading", itor->second,
			att->get_location(), damage, true,  "", 0,
			hit_type, &attack, secondary_attack, swing);
	}
	for (std::vector<std::pair<const config *, map_location> >::iterator itor = helpers.cfgs.begin(); itor != helpers.cfgs.end(); ++itor) {
		if(itor->second == a) continue;
		if(itor->second == b) continue;
		unit_map::iterator helper = units.find(itor->second);
		assert(helper != units.end());
		helper->set_facing(itor->second.get_relative_dir(b));
		animator.add_animation(&*helper, "resistance", itor->second,
			def->get_location(), damage, true,  "", 0,
			hit_type, &attack, secondary_attack, swing);
	}


	animator.start_animations();
	animator.wait_until(0);
	int damage_left = damage;
	while(damage_left > 0 && !animator.would_end()) {
		int step_left = (animator.get_end_time() - animator.get_animation_time() )/50;
		if(step_left < 1) step_left = 1;
		int removed_hp =  damage_left/step_left ;
		if(removed_hp < 1) removed_hp = 1;
		defender.take_hit(removed_hp);
		damage_left -= removed_hp;
		animator.wait_until(animator.get_animation_time_potential() +50);
	}
	animator.wait_for_end();
	// pass the animation back to the real unit
	def->start_animation(animator.get_end_time(), defender_anim, true);
	reset_helpers(&*att, &*def);
	def->set_hitpoints(def_hitpoints);
}
Ejemplo n.º 22
0
void config_cache::read_cache(const std::string& file_path, config& cfg)
{
	static const std::string extension = ".gz";

	std::stringstream defines_string;
	defines_string << file_path;

	bool is_valid = true;

	for(const preproc_map::value_type& d : defines_map_) {
		//
		// Only WESNOTH_VERSION is allowed to be non-empty.
		//
		if((!d.second.value.empty() || !d.second.arguments.empty()) &&
		   d.first != "WESNOTH_VERSION")
		{
			is_valid = false;
			ERR_CACHE << "Invalid preprocessor define: " << d.first << '\n';
			break;
		}

		defines_string << " " << d.first;
	}

	// Do cache check only if define map is valid and
	// caching is allowed.
	const std::string& cache_path = filesystem::get_cache_dir();

	if(is_valid && !cache_path.empty()) {
		// Use a hash for a shorter display of the defines.
		const std::string fname = cache_path + "/" +
								  cache_file_prefix_ +
								  sha1_hash(defines_string.str()).display();
		const std::string fname_checksum = fname + ".checksum" + extension;

		filesystem::file_tree_checksum dir_checksum;

		if(!force_valid_cache_ && !fake_invalid_cache_) {
			try {
				if(filesystem::file_exists(fname_checksum)) {
					config checksum_cfg;

					DBG_CACHE << "Reading checksum: " << fname_checksum << "\n";
					read_file(fname_checksum, checksum_cfg);

					dir_checksum = filesystem::file_tree_checksum(checksum_cfg);
				}
			} catch(config::error&) {
				ERR_CACHE << "cache checksum is corrupt" << std::endl;
			} catch(filesystem::io_exception&) {
				ERR_CACHE << "error reading cache checksum" << std::endl;
			}
		}

		if(force_valid_cache_) {
			LOG_CACHE << "skipping cache validation (forced)\n";
		}

		if(filesystem::file_exists(fname + extension) && (force_valid_cache_ || (dir_checksum == filesystem::data_tree_checksum()))) {
			LOG_CACHE << "found valid cache at '" << fname << extension << "' with defines_map " << defines_string.str() << "\n";
			log_scope("read cache");

			try {
				read_file(fname + extension,cfg);
				const std::string define_file = fname + ".define" + extension;

				if(filesystem::file_exists(define_file)) {
					config_cache_transaction::instance().add_define_file(define_file);
				}

				return;
			} catch(config::error& e) {
				ERR_CACHE << "cache " << fname << extension << " is corrupt. Loading from files: "<< e.message << std::endl;
			} catch(filesystem::io_exception&) {
				ERR_CACHE << "error reading cache " << fname << extension << ". Loading from files" << std::endl;
			} catch (boost::iostreams::gzip_error& e) {
				//read_file -> ... -> read_gz can throw this exception.
				ERR_CACHE << "cache " << fname << extension << " is corrupt. Error code: " << e.error() << std::endl;
			}
		}

		LOG_CACHE << "no valid cache found. Writing cache to '" << fname << extension << " with defines_map "<< defines_string.str() << "'\n";

		// Now we need queued defines so read them to memory
		read_defines_queue();

		preproc_map copy_map(make_copy_map());

		read_configs(file_path, cfg, copy_map);
		add_defines_map_diff(copy_map);

		try {
			write_file(fname + extension, cfg);
			write_file(fname + ".define" + extension, copy_map);

			config checksum_cfg;

			filesystem::data_tree_checksum().write(checksum_cfg);
			write_file(fname_checksum, checksum_cfg);
		} catch(filesystem::io_exception&) {
			ERR_CACHE << "could not write to cache '" << fname << "'" << std::endl;
		}

		return;
	}

	LOG_CACHE << "Loading plain config instead of cache\n";

	preproc_map copy_map(make_copy_map());
	read_configs(file_path, cfg, copy_map);
	add_defines_map_diff(copy_map);
}
Ejemplo n.º 23
0
std::string default_generate_map(size_t width, size_t height, size_t island_size, size_t island_off_center,
                                 size_t iterations, size_t hill_size,
						         size_t max_lakes, size_t nvillages, size_t castle_size, size_t nplayers, bool roads_between_castles,
								 std::map<map_location,std::string>* labels, const config& cfg)
{
	log_scope("map generation");

	// Odd widths are nasty
	VALIDATE(is_even(width), _("Random maps with an odd width aren't supported."));

	int ticks = SDL_GetTicks();

	// Find out what the 'flatland' on this map is, i.e. grassland.
	std::string flatland = cfg["default_flatland"];
	if(flatland == "") {
		flatland = t_translation::write_terrain_code(t_translation::GRASS_LAND);
	}

	const t_translation::t_terrain grassland = t_translation::read_terrain_code(flatland);

	// We want to generate a map that is 9 times bigger
	// than the actual size desired.
	// Only the middle part of the map will be used,
	// but the rest is so that the map we end up using
	// can have a context (e.g. rivers flowing from
	// out of the map into the map, same for roads, etc.)
	width *= 3;
	height *= 3;

	LOG_NG << "generating height map...\n";
	// Generate the height of everything.
	const height_map heights = generate_height_map(width,height,iterations,hill_size,island_size,island_off_center);
	LOG_NG << "done generating height map...\n";
	LOG_NG << (SDL_GetTicks() - ticks) << "\n"; ticks = SDL_GetTicks();

	config naming = cfg.child_or_empty("naming");
	// HACK: dummy names to satisfy unit_race requirements
	naming["id"] = "village_naming";
	naming["plural_name"] = "villages";

	// Make a dummy race for generating names
	const unit_race name_generator(naming);

	std::vector<terrain_height_mapper> height_conversion;

	BOOST_FOREACH(const config &h, cfg.child_range("height")) {
		height_conversion.push_back(terrain_height_mapper(h));
	}

	terrain_map terrain(width, t_translation::t_list(height, grassland));
	size_t x, y;
	for(x = 0; x != heights.size(); ++x) {
		for(y = 0; y != heights[x].size(); ++y) {
			for(std::vector<terrain_height_mapper>::const_iterator i = height_conversion.begin();
			    i != height_conversion.end(); ++i) {
				if(i->convert_terrain(heights[x][y])) {
					terrain[x][y] = i->convert_to();
					break;
				}
			}
		}
	}

	std::map<int, t_translation::coordinate> starting_positions;
	LOG_NG << output_map(terrain, starting_positions);
	LOG_NG << "placed land forms\n";
	LOG_NG << (SDL_GetTicks() - ticks) << "\n"; ticks = SDL_GetTicks();

	// Now that we have our basic set of flatland/hills/mountains/water,
	// we can place lakes and rivers on the map.
	// All rivers are sourced at a lake.
	// Lakes must be in high land - at least 'min_lake_height'.
	// (Note that terrain below a certain altitude may be made
	// into bodies of water in the code above - i.e. 'sea',
	// but these are not considered 'lakes', because
	// they are not sources of rivers).
	//
	// We attempt to place 'max_lakes' lakes.
	// Each lake will be placed at a random location,
	// if that random location meets the minimum terrain requirements for a lake.
	// We will also attempt to source a river from each lake.
	std::set<location> lake_locs;

	std::map<location, std::string> river_names, lake_names, road_names, bridge_names, mountain_names, forest_names, swamp_names;

	const size_t nlakes = max_lakes > 0 ? (rand()%max_lakes) : 0;
	for(size_t lake = 0; lake != nlakes; ++lake) {
		for(int tries = 0; tries != 100; ++tries) {
			const int x = rand()%width;
			const int y = rand()%height;
			if (heights[x][y] > cfg["min_lake_height"].to_int()) {
				std::vector<location> river = generate_river(heights,
					terrain, x, y, cfg["river_frequency"]);

				if(river.empty() == false && labels != NULL) {
					std::string base_name;
					LOG_NG << "generating name for river...\n";
					const std::string& name = generate_name(name_generator,"river_name",&base_name);
					LOG_NG << "named river '" << name << "'\n";
					size_t name_frequency = 20;
					for(std::vector<location>::const_iterator r = river.begin(); r != river.end(); ++r) {

						const map_location loc(r->x-width/3,r->y-height/3);

						if(((r - river.begin())%name_frequency) == name_frequency/2) {
							labels->insert(std::pair<map_location,std::string>(loc,name));
						}

						river_names.insert(std::pair<location,std::string>(loc,base_name));
					}

					LOG_NG << "put down river name...\n";
				}

				LOG_NG << "generating lake...\n";
				std::set<location> locs;
				bool res = generate_lake(terrain, x, y, cfg["lake_size"], locs);
				if(res && labels != NULL) {
					bool touches_other_lake = false;

					std::string base_name;
					const std::string& name = generate_name(name_generator,"lake_name",&base_name);

					std::set<location>::const_iterator i;

					// Only generate a name if the lake hasn't touched any other lakes,
					// so that we don't end up with one big lake with multiple names.
					for(i = locs.begin(); i != locs.end(); ++i) {
						if(lake_locs.count(*i) != 0) {
							touches_other_lake = true;

							// Reassign the name of this lake to be the same as the other lake
							const location loc(i->x-width/3,i->y-height/3);
							const std::map<location,std::string>::const_iterator other_name = lake_names.find(loc);
							if(other_name != lake_names.end()) {
								base_name = other_name->second;
							}
						}

						lake_locs.insert(*i);
					}

					if(!touches_other_lake) {
						const map_location loc(x-width/3,y-height/3);
						labels->erase(loc);
						labels->insert(std::pair<map_location,std::string>(loc,name));
					}

					for(i = locs.begin(); i != locs.end(); ++i) {
						const location loc(i->x-width/3,i->y-height/3);
						lake_names.insert(std::pair<location, std::string>(loc, base_name));
					}
				}

				break;
			}
		}
	}

	LOG_NG << "done generating rivers...\n";
	LOG_NG << (SDL_GetTicks() - ticks) << "\n"; ticks = SDL_GetTicks();

	const size_t default_dimensions = 40*40*9;

	/*
	 * Convert grassland terrain to other types of flat terrain.
	 *
	 * We generate a 'temperature map' which uses the height generation
	 * algorithm to generate the temperature levels all over the map.  Then we
	 * can use a combination of height and terrain to divide terrain up into
	 * more interesting types than the default.
	 */
	const height_map temperature_map = generate_height_map(width,height,
		cfg["temperature_iterations"].to_int() * width * height / default_dimensions,
		cfg["temperature_size"], 0, 0);

	LOG_NG << "generated temperature map...\n";
	LOG_NG << (SDL_GetTicks() - ticks) << "\n"; ticks = SDL_GetTicks();

	std::vector<terrain_converter> converters;
	BOOST_FOREACH(const config &cv, cfg.child_range("convert")) {
		converters.push_back(terrain_converter(cv));
	}

	LOG_NG << "created terrain converters\n";
	LOG_NG << (SDL_GetTicks() - ticks) << "\n"; ticks = SDL_GetTicks();


	// Iterate over every flatland tile, and determine
	// what type of flatland it is, based on our [convert] tags.
	for(x = 0; x != width; ++x) {
		for(y = 0; y != height; ++y) {
			for(std::vector<terrain_converter>::const_iterator i = converters.begin(); i != converters.end(); ++i) {
				if(i->convert_terrain(terrain[x][y],heights[x][y],temperature_map[x][y])) {
					terrain[x][y] = i->convert_to();
					break;
				}
			}
		}
	}

	LOG_NG << "placing villages...\n";
	LOG_NG << (SDL_GetTicks() - ticks) << "\n"; ticks = SDL_GetTicks();

	/*
	 * Place villages in a 'grid', to make placing fair, but with villages
	 * displaced from their position according to terrain and randomness, to
	 * add some variety.
	 */
	std::set<location> villages;

	LOG_NG << "placing castles...\n";

	/** Try to find configuration for castles. */
	const config &castle_config = cfg.child("castle");
	if (!castle_config) {
		LOG_NG << "Could not find castle configuration\n";
		return std::string();
	}

	/*
	 * Castle configuration tag contains a 'valid_terrain' attribute which is a
	 * list of terrains that the castle may appear on.
	 */
	const t_translation::t_list list =
		t_translation::read_list(castle_config["valid_terrain"]);

	const is_valid_terrain terrain_tester(terrain, list);

	/*
	 * Attempt to place castles at random.
	 *
	 * Once we have placed castles, we run a sanity check to make sure that the
	 * castles are well-placed.  If the castles are not well-placed, we try
	 * again.  Definition of 'well-placed' is if no two castles are closer than
	 * 'min_distance' hexes from each other, and the castles appear on a
	 * terrain listed in 'valid_terrain'.
	 */
	std::vector<location> castles;
	std::set<location> failed_locs;

	for(size_t player = 0; player != nplayers; ++player) {
		LOG_NG << "placing castle for " << player << "\n";
		log_scope("placing castle");
		const int min_x = width/3 + 3;
		const int min_y = height/3 + 3;
		const int max_x = (width/3)*2 - 4;
		const int max_y = (height/3)*2 - 4;
		int min_distance = castle_config["min_distance"];

		location best_loc;
		int best_ranking = 0;
		for(int x = min_x; x != max_x; ++x) {
			for(int y = min_y; y != max_y; ++y) {
				const location loc(x,y);
				if(failed_locs.count(loc)) {
					continue;
				}

				const int ranking = rank_castle_location(x,y,terrain_tester,min_x,max_x,min_y,max_y,min_distance,castles,best_ranking);
				if(ranking <= 0) {
					failed_locs.insert(loc);
				}

				if(ranking > best_ranking) {
					best_ranking = ranking;
					best_loc = loc;
				}
			}
		}
		if(best_ranking == 0) {
			ERR_NG << "No castle location found, aborting.\n";
			std::string error = _("No valid castle location found. Too many or too few mountain hexes? (please check the 'max hill size' parameter)");
			throw mapgen_exception(error);
		}
		assert(std::find(castles.begin(), castles.end(), best_loc) == castles.end());
		castles.push_back(best_loc);
		// Make sure the location can't get a second castle.
		failed_locs.insert(best_loc);
	}

	LOG_NG << "placing roads...\n";
	LOG_NG << (SDL_GetTicks() - ticks) << "\n"; ticks = SDL_GetTicks();

	// Place roads.
	// We select two tiles at random locations on the borders
	// of the map, and try to build roads between them.
	int nroads = cfg["roads"];
	if(roads_between_castles) {
		nroads += castles.size()*castles.size();
	}

	std::set<location> bridges;

	road_path_calculator calc(terrain,cfg);
	for (int road = 0; road != nroads; ++road) {
		log_scope("creating road");

		/*
		 * We want the locations to be on the portion of the map we're actually
		 * going to use, since roads on other parts of the map won't have any
		 * influence, and doing it like this will be quicker.
		 */
		location src = random_point_at_side(width/3 + 2,height/3 + 2);
		location dst = random_point_at_side(width/3 + 2,height/3 + 2);

		src.x += width/3 - 1;
		src.y += height/3 - 1;
		dst.x += width/3 - 1;
		dst.y += height/3 - 1;

		if (roads_between_castles && road < int(castles.size() * castles.size())) {
			const size_t src_castle = road/castles.size();
			const size_t dst_castle = road%castles.size();
			if(src_castle >= dst_castle) {
				continue;
			}

			src = castles[src_castle];
			dst = castles[dst_castle];
		}

		// If the road isn't very interesting (on the same border), don't draw it.
		else if(src.x == dst.x || src.y == dst.y) {
			continue;
		}

		if (calc.cost(src, 0.0) >= 1000.0 || calc.cost(dst, 0.0) >= 1000.0) {
			continue;
		}

		// Search a path out for the road
		pathfind::plain_route rt = pathfind::a_star_search(src, dst, 10000.0, &calc, width, height);

		std::string road_base_name;
		const std::string& name = generate_name(name_generator, "road_name", &road_base_name);
		const int name_frequency = 20;
		int name_count = 0;

		bool on_bridge = false;

		// Draw the road.
		// If the search failed, rt.steps will simply be empty.
		for(std::vector<location>::const_iterator step = rt.steps.begin();
				step != rt.steps.end(); ++step) {

			const int x = step->x;
			const int y = step->y;

			if(x < 0 || y < 0 || x >= static_cast<long>(width) ||
					y >= static_cast<long>(height)) {

				continue;
			}

			// Find the configuration which tells us
			// what to convert this tile to, to make it into a road.
			if (const config &child = cfg.find_child("road_cost", "terrain",
					t_translation::write_terrain_code(terrain[x][y])))
			{
				// Convert to bridge means that we want to convert
				// depending upon the direction the road is going.
				// Typically it will be in a format like,
				// convert_to_bridge=\,|,/
				// '|' will be used if the road is going north-south
				// '/' will be used if the road is going south west-north east
				// '\' will be used if the road is going south east-north west
				// The terrain will be left unchanged otherwise
				// (if there is no clear direction).
				const std::string &convert_to_bridge = child["convert_to_bridge"];
				if(convert_to_bridge.empty() == false) {
					if(step == rt.steps.begin() || step+1 == rt.steps.end())
						continue;

					const location& last = *(step-1);
					const location& next = *(step+1);

					location adj[6];
					get_adjacent_tiles(*step,adj);

					int direction = -1;

					// If we are going north-south
					if((last == adj[0] && next == adj[3]) || (last == adj[3] && next == adj[0])) {
						direction = 0;
					}

					// If we are going south west-north east
					else if((last == adj[1] && next == adj[4]) || (last == adj[4] && next == adj[1])) {
						direction = 1;
					}

					// If we are going south east-north west
					else if((last == adj[2] && next == adj[5]) || (last == adj[5] && next == adj[2])) {
						direction = 2;
					}

					if(labels != NULL && on_bridge == false) {
						on_bridge = true;
						std::string bridge_base_name;
						const std::string& name = generate_name(name_generator, "bridge_name", &bridge_base_name);
						const location loc(x - width / 3, y-height/3);
						labels->insert(std::pair<map_location,std::string>(loc,name));
						bridge_names.insert(std::pair<location,std::string>(loc, bridge_base_name)); //add to use for village naming
						bridges.insert(loc);
					}

					if(direction != -1) {
						const std::vector<std::string> items = utils::split(convert_to_bridge);
						if(size_t(direction) < items.size() && items[direction].empty() == false) {
							terrain[x][y] = t_translation::read_terrain_code(items[direction]);
						}

						continue;
					}
				} else {
					on_bridge = false;
				}

				// Just a plain terrain substitution for a road
				const std::string &convert_to = child["convert_to"];
				if(convert_to.empty() == false) {
					const t_translation::t_terrain letter =
						t_translation::read_terrain_code(convert_to);
					if(labels != NULL && terrain[x][y] != letter && name_count++ == name_frequency && on_bridge == false) {
						labels->insert(std::pair<map_location,std::string>(map_location(x-width/3,y-height/3),name));
						name_count = 0;
					}

					terrain[x][y] = letter;
					const location loc(x - width / 3, y - height / 3); //add to use for village naming
					road_names.insert(std::pair<location,std::string>(loc, road_base_name));
				}
			}
		}

		LOG_NG << "looked at " << calc.calls << " locations\n";
	}


	// Now that road drawing is done, we can plonk down the castles.
	for(std::vector<location>::const_iterator c = castles.begin(); c != castles.end(); ++c) {
		if(c->valid() == false) {
			continue;
		}

		const int x = c->x;
		const int y = c->y;
		const int player = c - castles.begin() + 1;
		const struct t_translation::coordinate coord(x, y);
		starting_positions.insert(std::pair<int, t_translation::coordinate>(player, coord));
		terrain[x][y] = t_translation::HUMAN_KEEP;

		const int castles[13][2] = {
		  {-1, 0}, {-1, -1}, {0, -1}, {1, -1}, {1, 0}, {0, 1}, {-1, 1},
		  {-2, 1}, {-2, 0}, {-2, -1}, {-1, -2}, {0, -2}, {1, -2}
		};

		for (size_t i = 0; i < castle_size - 1; i++) {
		  terrain[x+castles[i][0]][y+castles[i][1]] = t_translation::HUMAN_CASTLE;
		}

		// Remove all labels under the castle tiles
		if(labels != NULL) {
		  labels->erase(location(x-width/3,y-height/3));
		  for (size_t i = 0; i < castle_size - 1; i++) {
		    labels->erase(location(x+castles[i][0]-width/3,
					   y+castles[i][1]-height/3));
		  }

		}

	}

	LOG_NG << "placed castles\n";
	LOG_NG << (SDL_GetTicks() - ticks) << "\n"; ticks = SDL_GetTicks();

	/*Random naming for landforms: mountains, forests, swamps, hills
	 *we name these now that everything else is placed (as e.g., placing
	 * roads could split a forest)
	 */
	for (x = width / 3; x < (width / 3)*2; x++) {
		for (y = height / 3; y < (height / 3) * 2;y++) {
		//check the terrain of the tile
		const location loc(x - width / 3, y - height / 3);
		const t_translation::t_terrain terr = terrain[x][y];
		std::string name, base_name;
		std::set<std::string> used_names;
		if (t_translation::terrain_matches(terr, t_translation::ALL_MOUNTAINS)) {
			//name every 15th mountain
			if ((rand()%15) == 0) {
				for(size_t ntry = 0; ntry != 30 && (ntry == 0 || used_names.count(name) > 0); ++ntry) {
					name = generate_name(name_generator, "mountain_name", &base_name);
				}
				labels->insert(std::pair<map_location, std::string>(loc, name));
				mountain_names.insert(std::pair<location, std::string>(loc, base_name));
			}
		}
		else if (t_translation::terrain_matches(terr, t_translation::ALL_FORESTS)) {
			//if the forest tile is not named yet, name it
			const std::map<location, std::string>::const_iterator forest_name = forest_names.find(loc);
			if(forest_name == forest_names.end()) {
				for(size_t ntry = 0; ntry != 30 && (ntry == 0 || used_names.count(name) > 0); ++ntry) {
					name = generate_name(name_generator, "forest_name", &base_name);
				}
				forest_names.insert(std::pair<location, std::string>(loc, base_name));
				// name all connected forest tiles accordingly
				flood_name(loc, base_name, forest_names, t_translation::ALL_FORESTS, terrain, width, height, 0, labels, name);
			}
		}
		else if (t_translation::terrain_matches(terr, t_translation::ALL_SWAMPS)) {
			//if the swamp tile is not named yet, name it
			const std::map<location, std::string>::const_iterator swamp_name = swamp_names.find(loc);
			if(swamp_name == swamp_names.end()) {
				for(size_t ntry = 0; ntry != 30 && (ntry == 0 || used_names.count(name) > 0); ++ntry) {
					name = generate_name(name_generator, "swamp_name", &base_name);
				}
				swamp_names.insert(std::pair<location, std::string>(loc, base_name));
				// name all connected swamp tiles accordingly
				flood_name(loc, base_name, swamp_names, t_translation::ALL_SWAMPS, terrain, width, height, 0, labels, name);
			}
		}

		}
	}

	if (nvillages > 0)
	{
		config naming_cfg = cfg.child_or_empty("village_naming");
		// HACK: dummy names to satisfy unit_race requirements
		naming_cfg["id"] = "village_naming";
		naming_cfg["plural_name"] = "villages";

		const unit_race village_names_generator(naming_cfg);

		// First we work out the size of the x and y distance between villages
		const size_t tiles_per_village = ((width*height)/9)/nvillages;
		size_t village_x = 1, village_y = 1;

		// Alternate between incrementing the x and y value.
		// When they are high enough to equal or exceed the tiles_per_village,
		// then we have them to the value we want them at.
		while(village_x*village_y < tiles_per_village) {
			if(village_x < village_y) {
				++village_x;
			} else {
				++village_y;
			}
		}

		std::set<std::string> used_names;
		tcode_list_cache adj_liked_cache;

		for(size_t vx = 0; vx < width; vx += village_x) {
			LOG_NG << "village at " << vx << "\n";
			for(size_t vy = rand()%village_y; vy < height; vy += village_y) {

				const size_t add_x = rand()%3;
				const size_t add_y = rand()%3;
				const size_t x = (vx + add_x) - 1;
				const size_t y = (vy + add_y) - 1;

				const map_location res = place_village(terrain,x,y,2,cfg,adj_liked_cache);

				if(res.x >= static_cast<long>(width) / 3 &&
						res.x  < static_cast<long>(width * 2) / 3 &&
						res.y >= static_cast<long>(height) / 3 &&
						res.y  < static_cast<long>(height * 2) / 3) {

					const std::string str =
						t_translation::write_terrain_code(terrain[res.x][res.y]);
					if (const config &child = cfg.find_child("village", "terrain", str))
					{
						const std::string &convert_to = child["convert_to"];
						if(convert_to != "") {
							terrain[res.x][res.y] =
								t_translation::read_terrain_code(convert_to);

							villages.insert(res);

							if(labels != NULL && naming_cfg.empty() == false) {
								const map_location loc(res.x-width/3,res.y-height/3);

								map_location adj[6];
								get_adjacent_tiles(loc,adj);

								std::string name_type = "village_name";
								const t_translation::t_list
									field    = t_translation::t_list(1, t_translation::GRASS_LAND),
									forest   = t_translation::t_list(1, t_translation::FOREST),
									mountain = t_translation::t_list(1, t_translation::MOUNTAIN),
									hill     = t_translation::t_list(1, t_translation::HILL);

								size_t field_count = 0, forest_count = 0, mountain_count = 0, hill_count = 0;

								utils::string_map symbols;

								size_t n;
								for(n = 0; n != 6; ++n) {
									const std::map<location,std::string>::const_iterator road_name = road_names.find(adj[n]);
									if(road_name != road_names.end()) {
										symbols["road"] = road_name->second;
										name_type = "village_name_road";
										break;
									}

									const std::map<location,std::string>::const_iterator river_name = river_names.find(adj[n]);
									if(river_name != river_names.end()) {
										symbols["river"] = river_name->second;
										name_type = "village_name_river";

										const std::map<location,std::string>::const_iterator bridge_name = bridge_names.find(adj[n]);
										if(bridge_name != bridge_names.end()) {
										//we should always end up here, since if there is an adjacent bridge, there has to be an adjacent river too
										symbols["bridge"] = bridge_name->second;
										name_type = "village_name_river_bridge";
										}

										break;
									}

									const std::map<location,std::string>::const_iterator forest_name = forest_names.find(adj[n]);
									if(forest_name != forest_names.end()) {
										symbols["forest"] = forest_name->second;
										name_type = "village_name_forest";
										break;
									}

									const std::map<location,std::string>::const_iterator lake_name = lake_names.find(adj[n]);
									if(lake_name != lake_names.end()) {
										symbols["lake"] = lake_name->second;
										name_type = "village_name_lake";
										break;
									}

									const std::map<location,std::string>::const_iterator mountain_name = mountain_names.find(adj[n]);
									if(mountain_name != mountain_names.end()) {
										symbols["mountain"] = mountain_name->second;
										name_type = "village_name_mountain";
										break;
									}

									const std::map<location,std::string>::const_iterator swamp_name = swamp_names.find(adj[n]);
									if(swamp_name != swamp_names.end()) {
										symbols["swamp"] = swamp_name->second;
										name_type = "village_name_swamp";
										break;
									}

									const t_translation::t_terrain terr =
										terrain[adj[n].x+width/3][adj[n].y+height/3];

									if(std::count(field.begin(),field.end(),terr) > 0) {
										++field_count;
									} else if(std::count(forest.begin(),forest.end(),terr) > 0) {
										++forest_count;
									} else if(std::count(hill.begin(),hill.end(),terr) > 0) {
										++hill_count;
									} else if(std::count(mountain.begin(),mountain.end(),terr) > 0) {
										++mountain_count;
									}
								}

								if(n == 6) {
									if(field_count == 6) {
										name_type = "village_name_grassland";
									} else if(forest_count >= 2) {
										name_type = "village_name_forest";
									} else if(mountain_count >= 1) {
										name_type = "village_name_mountain_anonymous";
									} else if(hill_count >= 2) {
										name_type = "village_name_hill";
									}
								}

								std::string name;
								for(size_t ntry = 0; ntry != 30 && (ntry == 0 || used_names.count(name) > 0); ++ntry) {
									name = generate_name(village_names_generator,name_type,NULL,&symbols);
								}

								used_names.insert(name);
								labels->insert(std::pair<map_location,std::string>(loc,name));
							}
						}
					}
				}
			}
		}
	}

	LOG_NG << "placed villages\n";
	LOG_NG << (SDL_GetTicks() - ticks) << "\n"; ticks = SDL_GetTicks();

	return output_map(terrain, starting_positions);
}
Ejemplo n.º 24
0
void terrain_builder::parse_config(const config &cfg, bool local)
{
	log_scope("terrain_builder::parse_config");

	// Parses the list of building rules (BRs)
	foreach (const config &br, cfg.child_range("terrain_graphics"))
	{
		building_rule pbr; // Parsed Building rule
		pbr.local = local;

		// add_images_from_config(pbr.images, **br);

		if(!br["x"].empty() && !br["y"].empty())
			pbr.location_constraints =
				map_location(atoi(br["x"].c_str()) - 1, atoi(br["y"].c_str()) - 1);

		pbr.probability = br["probability"].empty() ? -1 : atoi(br["probability"].c_str());

		// Mapping anchor indices to anchor locations.
		anchormap anchors;

		// Parse the map= , if there is one (and fill the anchors list)
		parse_mapstring(br["map"], pbr, anchors, br);

		// Parses the terrain constraints (TCs)
		foreach (const config &tc, br.child_range("tile"))
		{
			// Adds the terrain constraint to the current built terrain's list
			// of terrain constraints, if it does not exist.
			map_location loc;
			if (!tc["x"].empty()) {
				loc.x = atoi(tc["x"].c_str());
			}
			if (!tc["y"].empty()) {
				loc.y = atoi(tc["y"].c_str());
			}
			if (!tc["loc"].empty()) {
				std::vector<std::string> sloc = utils::split(tc["loc"]);
				if(sloc.size() == 2) {
					loc.x = atoi(sloc[0].c_str());
					loc.y = atoi(sloc[1].c_str());
				}
			}
			if(loc.valid()) {
				add_constraints(pbr.constraints, loc, tc, br);
			}
			if (!tc["pos"].empty()) {
				int pos = atoi(tc["pos"].c_str());
				if(anchors.find(pos) == anchors.end()) {
					WRN_NG << "Invalid anchor!\n";
					continue;
				}

				std::pair<anchormap::const_iterator, anchormap::const_iterator> range =
					anchors.equal_range(pos);

				for(; range.first != range.second; ++range.first) {
					loc = range.first->second;
					add_constraints(pbr.constraints, loc, tc, br);
				}
			}
		}

		const std::vector<std::string> global_set_flag = utils::split(br["set_flag"]);
		const std::vector<std::string> global_no_flag = utils::split(br["no_flag"]);
		const std::vector<std::string> global_has_flag = utils::split(br["has_flag"]);

		for(constraint_set::iterator constraint = pbr.constraints.begin(); constraint != pbr.constraints.end();
		    ++constraint) {

			if(global_set_flag.size())
				constraint->second.set_flag.insert(constraint->second.set_flag.end(),
						global_set_flag.begin(), global_set_flag.end());

			if(global_no_flag.size())
				constraint->second.no_flag.insert(constraint->second.no_flag.end(),
						global_no_flag.begin(), global_no_flag.end());

			if(global_has_flag.size())
				constraint->second.has_flag.insert(constraint->second.has_flag.end(),
						global_has_flag.begin(), global_has_flag.end());

		}

		// Handles rotations
		const std::string &rotations = br["rotations"];

		int precedence = lexical_cast_default<int>(br["precedence"],0);

		add_rotated_rules(building_rules_, pbr, precedence, rotations);

	}

// Debug output for the terrain rules
#if 0
	std::cerr << "Built terrain rules: \n";

	building_ruleset::const_iterator rule;
	for(rule = building_rules_.begin(); rule != building_rules_.end(); ++rule) {
		std::cerr << ">> New rule: image_background = "
			<< "\n>> Location " << rule->second.location_constraints
			<< "\n>> Probability " << rule->second.probability

		for(constraint_set::const_iterator constraint = rule->second.constraints.begin();
		    constraint != rule->second.constraints.end(); ++constraint) {

			std::cerr << ">>>> New constraint: location = (" << constraint->second.loc
			          << "), terrain types = '" << t_translation::write_list(constraint->second.terrain_types_match.terrain) << "'\n";

			std::vector<std::string>::const_iterator flag;

			for(flag  = constraint->second.set_flag.begin(); flag != constraint->second.set_flag.end(); ++flag) {
				std::cerr << ">>>>>> Set_flag: " << *flag << "\n";
			}

			for(flag = constraint->second.no_flag.begin(); flag != constraint->second.no_flag.end(); ++flag) {
				std::cerr << ">>>>>> No_flag: " << *flag << "\n";
			}
		}

	}
#endif

}
Ejemplo n.º 25
0
void unit_attack(display * disp, game_board & board,
                 const map_location& a, const map_location& b, int damage,
                 const attack_type& attack, const attack_type* secondary_attack,
                 int swing,std::string hit_text,int drain_amount,std::string att_text, const std::vector<std::string>* extra_hit_sounds)
{
	if(!disp ||disp->video().update_locked() || disp->video().faked() ||
			(disp->fogged(a) && disp->fogged(b)) || preferences::show_combat() == false) {
		return;
	}
	//const unit_map& units = disp->get_units();
	disp->select_hex(map_location::null_location());

	// scroll such that there is at least half a hex spacing around fighters
	disp->scroll_to_tiles(a,b,game_display::ONSCREEN,true,0.5,false);

	log_scope("unit_attack");

	const unit_map::const_iterator att = board.units().find(a);
	assert(att.valid());
	const unit& attacker = *att;

	const unit_map::iterator def = board.find_unit(b);
	assert(def.valid());
	unit &defender = *def;
	int def_hitpoints = defender.hitpoints();

	att->set_facing(a.get_relative_dir(b));
	def->set_facing(b.get_relative_dir(a));
	defender.set_facing(b.get_relative_dir(a));


	unit_animator animator;
	unit_ability_list leaders = attacker.get_abilities("leadership");
	unit_ability_list helpers = defender.get_abilities("resistance");

	std::string text   = number_and_text(damage, hit_text);
	std::string text_2 = number_and_text(abs(drain_amount), att_text);

	unit_animation::hit_type hit_type;
	if(damage >= defender.hitpoints()) {
		hit_type = unit_animation::hit_type::KILL;
	} else if(damage > 0) {
		hit_type = unit_animation::hit_type::HIT;
	}else {
		hit_type = unit_animation::hit_type::MISS;
	}
	animator.add_animation(&attacker, "attack", att->get_location(),
		def->get_location(), damage, true,  text_2,
		(drain_amount >= 0) ? display::rgb(0, 255, 0) : display::rgb(255, 0, 0),
		hit_type, &attack, secondary_attack, swing);

	// note that we take an anim from the real unit, we'll use it later
	const unit_animation *defender_anim = def->anim_comp().choose_animation(*disp,
		def->get_location(), "defend", att->get_location(), damage,
		hit_type, &attack, secondary_attack, swing);
	animator.add_animation(&defender, defender_anim, def->get_location(),
		true,  text , display::rgb(255, 0, 0));

	for (const unit_ability & ability : leaders) {
		if(ability.second == a) continue;
		if(ability.second == b) continue;
		unit_map::const_iterator leader = board.units().find(ability.second);
		assert(leader.valid());
		leader->set_facing(ability.second.get_relative_dir(a));
		animator.add_animation(&*leader, "leading", ability.second,
			att->get_location(), damage, true,  "", 0,
			hit_type, &attack, secondary_attack, swing);
	}
	for (const unit_ability & ability : helpers) {
		if(ability.second == a) continue;
		if(ability.second == b) continue;
		unit_map::const_iterator helper = board.units().find(ability.second);
		assert(helper.valid());
		helper->set_facing(ability.second.get_relative_dir(b));
		animator.add_animation(&*helper, "resistance", ability.second,
			def->get_location(), damage, true,  "", 0,
			hit_type, &attack, secondary_attack, swing);
	}


	animator.start_animations();
	animator.wait_until(0);
	int damage_left = damage;
	bool extra_hit_sounds_played = false;
	while(damage_left > 0 && !animator.would_end()) {
		if(!extra_hit_sounds_played && extra_hit_sounds != nullptr) {
			for (std::string hit_sound : *extra_hit_sounds) {
				sound::play_sound(hit_sound);
			}
			extra_hit_sounds_played = true;
		}

		int step_left = (animator.get_end_time() - animator.get_animation_time() )/50;
		if(step_left < 1) step_left = 1;
		int removed_hp =  damage_left/step_left ;
		if(removed_hp < 1) removed_hp = 1;
		defender.take_hit(removed_hp);
		damage_left -= removed_hp;
		animator.wait_until(animator.get_animation_time_potential() +50);
	}
	animator.wait_for_end();
	// pass the animation back to the real unit
	def->anim_comp().start_animation(animator.get_end_time(), defender_anim, true);
	reset_helpers(&*att, &*def);
	def->set_hitpoints(def_hitpoints);
}
std::string default_map_generator_job::default_generate_map(generator_data data, std::map<map_location,std::string>* labels, const config& cfg)
{
	log_scope("map generation");

	// Odd widths are nasty
	VALIDATE(is_even(data.width), _("Random maps with an odd width aren't supported."));

	// Try to find configuration for castles
	const config& castle_config = cfg.child("castle");

	int ticks = SDL_GetTicks();

	// We want to generate a map that is 9 times bigger than the actual size desired.
	// Only the middle part of the map will be used, but the rest is so that the map we
	// end up using can have a context (e.g. rivers flowing from out of the map into the map,
	// same for roads, etc.)
	data.width  *= 3;
	data.height *= 3;

	config naming;

	if(cfg.has_child("naming")) {
		naming = game_config_.child("naming");
		naming.append_attributes(cfg.child("naming"));
	}

	// If the [naming] child is empty, we cannot provide good names.
	std::map<map_location,std::string>* misc_labels = naming.empty() ? nullptr : labels;

	std::shared_ptr<name_generator>
		base_name_generator, river_name_generator, lake_name_generator,
		road_name_generator, bridge_name_generator, mountain_name_generator,
		forest_name_generator, swamp_name_generator;

	if(misc_labels != nullptr) {
		name_generator_factory base_generator_factory{ naming, {"male", "base", "bridge", "road", "river", "forest", "lake", "mountain", "swamp"} };

		naming.get_old_attribute("base_names", "male_names", "[naming]male_names= is deprecated, use base_names= instead");
		//Due to the attribute detection feature of the factory we also support male_name_generator= but keep it undocumented.

		base_name_generator = base_generator_factory.get_name_generator( (naming.has_attribute("base_names") || naming.has_attribute("base_name_generator")) ? "base" : "male" );
		river_name_generator    = base_generator_factory.get_name_generator("river");
		lake_name_generator     = base_generator_factory.get_name_generator("lake");
		road_name_generator     = base_generator_factory.get_name_generator("road");
		bridge_name_generator   = base_generator_factory.get_name_generator("bridge");
		mountain_name_generator = base_generator_factory.get_name_generator("mountain");
		forest_name_generator   = base_generator_factory.get_name_generator("forest");
		swamp_name_generator    = base_generator_factory.get_name_generator("swamp");
	}

	// Generate the height of everything.
	const height_map heights = generate_height_map(data.width, data.height, data.iterations, data.hill_size, data.island_size, data.island_off_center);

	LOG_NG << "Done generating height map. " << (SDL_GetTicks() - ticks) << " ticks elapsed" << "\n";
	ticks = SDL_GetTicks();

	// Find out what the 'flatland' on this map is, i.e. grassland.
	std::string flatland = cfg["default_flatland"];
	if(flatland.empty()) {
		flatland = t_translation::write_terrain_code(t_translation::GRASS_LAND);
	}

	const t_translation::terrain_code grassland = t_translation::read_terrain_code(flatland);

	std::vector<terrain_height_mapper> height_conversion;
	for(const config& h : cfg.child_range("height")) {
		height_conversion.emplace_back(h);
	}

	terrain_map terrain(data.width, data.height, grassland);
	for(size_t x = 0; x != heights.size(); ++x) {
		for(size_t y = 0; y != heights[x].size(); ++y) {
			for(auto i : height_conversion) {
				if(i.convert_terrain(heights[x][y])) {
					terrain[x][y] = i.convert_to();
					break;
				}
			}
		}
	}

	t_translation::starting_positions starting_positions;
	LOG_NG << output_map(terrain, starting_positions);
	LOG_NG << "Placed landforms. " << (SDL_GetTicks() - ticks) << " ticks elapsed" << "\n";
	ticks = SDL_GetTicks();

	/* Now that we have our basic set of flatland/hills/mountains/water,
	 * we can place lakes and rivers on the map.
	 * All rivers are sourced at a lake.
	 * Lakes must be in high land - at least 'min_lake_height'.
	 * (Note that terrain below a certain altitude may be made into bodies of water
	 *  in the code above - i.e. 'sea', but these are not considered 'lakes',
	 *  because they are not sources of rivers).
	 *
	 * We attempt to place 'max_lakes' lakes.
	 * Each lake will be placed at a random location, if that random location meets theminimum
	 * terrain requirements for a lake. We will also attempt to source a river from each lake.
	 */
	std::set<map_location> lake_locs;

	std::map<map_location, std::string> river_names, lake_names, road_names, bridge_names, mountain_names, forest_names, swamp_names;

	const size_t nlakes = data.max_lakes > 0 ? (rng_()%data.max_lakes) : 0;
	for(size_t lake = 0; lake != nlakes; ++lake) {
		for(int tries = 0; tries != 100; ++tries) {
			const int x = rng_()%data.width;
			const int y = rng_()%data.height;

			if(heights[x][y] <= cfg["min_lake_height"].to_int()) {
				continue;
			}

			std::vector<map_location> river = generate_river(heights, terrain, x, y, cfg["river_frequency"]);

			if(!river.empty() && misc_labels != nullptr) {
				const std::string base_name = base_name_generator->generate();
				const std::string& name = river_name_generator->generate({{"base",  base_name}});
				LOG_NG << "Named river '" << name << "'\n";

				size_t name_frequency = 20;
				for(std::vector<map_location>::const_iterator r = river.begin(); r != river.end(); ++r) {
					const map_location loc(r->x-data.width/3,r->y-data.height/3);

					if(((r - river.begin())%name_frequency) == name_frequency/2) {
						misc_labels->emplace(loc, name);
					}

					river_names.emplace(loc, base_name);
				}
			}

			LOG_NG << "Generating lake...\n";

			std::set<map_location> locs;
			if(generate_lake(terrain, x, y, cfg["lake_size"], locs) && misc_labels != nullptr) {
				bool touches_other_lake = false;

				std::string base_name = base_name_generator->generate();
				const std::string& name = lake_name_generator->generate({{"base",  base_name}});

				// Only generate a name if the lake hasn't touched any other lakes,
				// so that we don't end up with one big lake with multiple names.
				for(auto i : locs) {
					if(lake_locs.count(i) != 0) {
						touches_other_lake = true;

						// Reassign the name of this lake to be the same as the other lake
						const map_location loc(i.x-data.width/3,i.y-data.height/3);
						const std::map<map_location,std::string>::const_iterator other_name = lake_names.find(loc);
						if(other_name != lake_names.end()) {
							base_name = other_name->second;
						}
					}

					lake_locs.insert(i);
				}

				if(!touches_other_lake) {
					const map_location loc(x-data.width/3,y-data.height/3);
					misc_labels->erase(loc);
					misc_labels->emplace(loc, name);
				}

				for(auto i : locs) {
					const map_location loc(i.x-data.width/3,i.y-data.height/3);
					lake_names.emplace(loc, base_name);
				}
			}

			break;
		}
	}

	LOG_NG << "Generated rivers. " << (SDL_GetTicks() - ticks) << " ticks elapsed" << "\n";
	ticks = SDL_GetTicks();

	const size_t default_dimensions = 40*40*9;

	/*
	 * Convert grassland terrain to other types of flat terrain.
	 *
	 * We generate a 'temperature map' which uses the height generation
	 * algorithm to generate the temperature levels all over the map.  Then we
	 * can use a combination of height and terrain to divide terrain up into
	 * more interesting types than the default.
	 */
	const height_map temperature_map = generate_height_map(data.width,data.height,
		cfg["temperature_iterations"].to_int() * data.width * data.height / default_dimensions,
		cfg["temperature_size"], 0, 0);

	LOG_NG << "Generated temperature map. " << (SDL_GetTicks() - ticks) << " ticks elapsed" << "\n";
	ticks = SDL_GetTicks();

	std::vector<terrain_converter> converters;
	for(const config& cv : cfg.child_range("convert")) {
		converters.emplace_back(cv);
	}

	LOG_NG << "Created terrain converters. " << (SDL_GetTicks() - ticks) << " ticks elapsed" << "\n";
	ticks = SDL_GetTicks();

	// Iterate over every flatland tile, and determine what type of flatland it is, based on our [convert] tags.
	for(int x = 0; x != data.width; ++x) {
		for(int y = 0; y != data.height; ++y) {
			for(auto i : converters) {
				if(i.convert_terrain(terrain[x][y],heights[x][y],temperature_map[x][y])) {
					terrain[x][y] = i.convert_to();
					break;
				}
			}
		}
	}

	LOG_NG << "Placing castles...\n";

	/*
	 * Attempt to place castles at random.
	 *
	 * After they are placed, we run a sanity check to make sure no two castles
	 * are closer than 'min_distance' hexes apart, and that they appear on a
	 * terrain listed in 'valid_terrain'.
	 *
	 * If not, we attempt to place them again.
	 */
	std::vector<map_location> castles;
	std::set<map_location> failed_locs;

	if(castle_config) {
		/*
		 * Castle configuration tag contains a 'valid_terrain' attribute which is a
		 * list of terrains that the castle may appear on.
		 */
		const t_translation::ter_list list = t_translation::read_list(castle_config["valid_terrain"]);

		const is_valid_terrain terrain_tester(terrain, list);

		for(int player = 0; player != data.nplayers; ++player) {
			LOG_NG << "placing castle for " << player << "\n";
			lg::scope_logger inner_scope_logging_object__(lg::general(), "placing castle");
			const int min_x = data.width/3 + 3;
			const int min_y = data.height/3 + 3;
			const int max_x = (data.width/3)*2 - 4;
			const int max_y = (data.height/3)*2 - 4;
			int min_distance = castle_config["min_distance"];

			map_location best_loc;
			int best_ranking = 0;
			for(int x = min_x; x != max_x; ++x) {
				for(int y = min_y; y != max_y; ++y) {
					const map_location loc(x,y);
					if(failed_locs.count(loc)) {
						continue;
					}

					const int ranking = rank_castle_location(x, y, terrain_tester, min_x, max_x, min_y, max_y, min_distance, castles, best_ranking);
					if(ranking <= 0) {
						failed_locs.insert(loc);
					}

					if(ranking > best_ranking) {
						best_ranking = ranking;
						best_loc = loc;
					}
				}
			}

			if(best_ranking == 0) {
				ERR_NG << "No castle location found, aborting." << std::endl;
				const std::string error = _("No valid castle location found. Too many or too few mountain hexes? (please check the 'max hill size' parameter)");
				throw mapgen_exception(error);
			}

			assert(std::find(castles.begin(), castles.end(), best_loc) == castles.end());
			castles.push_back(best_loc);

			// Make sure the location can't get a second castle.
			failed_locs.insert(best_loc);
		}

		LOG_NG << "Placed castles. " << (SDL_GetTicks() - ticks) << " ticks elapsed" << "\n";
	}
	LOG_NG << "Placing roads...\n";
	ticks = SDL_GetTicks();

	// Place roads.
	// We select two tiles at random locations on the borders of the map
	// and try to build roads between them.
	int nroads = cfg["roads"];
	if(data.link_castles) {
		nroads += castles.size()*castles.size();
	}

	std::set<map_location> bridges;

	road_path_calculator calc(terrain, cfg, rng_());
	for(int road = 0; road != nroads; ++road) {
		lg::scope_logger another_inner_scope_logging_object__(lg::general(), "creating road");

		/*
		 * We want the locations to be on the portion of the map we're actually
		 * going to use, since roads on other parts of the map won't have any
		 * influence, and doing it like this will be quicker.
		 */
		map_location src = random_point_at_side(data.width/3 + 2,data.height/3 + 2);
		map_location dst = random_point_at_side(data.width/3 + 2,data.height/3 + 2);

		src.x += data.width/3 - 1;
		src.y += data.height/3 - 1;
		dst.x += data.width/3 - 1;
		dst.y += data.height/3 - 1;

		if(data.link_castles && road < int(castles.size() * castles.size())) {
			const size_t src_castle = road/castles.size();
			const size_t dst_castle = road%castles.size();
			if(src_castle >= dst_castle) {
				continue;
			}

			src = castles[src_castle];
			dst = castles[dst_castle];
		} else if(src.x == dst.x || src.y == dst.y) {
			// If the road isn't very interesting (on the same border), don't draw it.
			continue;
		}

		if(calc.cost(src, 0.0) >= 1000.0 || calc.cost(dst, 0.0) >= 1000.0) {
			continue;
		}

		// Search a path out for the road
		pathfind::plain_route rt = pathfind::a_star_search(src, dst, 10000.0, calc, data.width, data.height);

		const std::string& road_base_name = misc_labels != nullptr
			? base_name_generator->generate()
			: "";
		const std::string& road_name = misc_labels != nullptr
			? road_name_generator->generate({{"base", road_base_name}})
			: "";
		const int name_frequency = 20;
		int name_count = 0;

		bool on_bridge = false;

		// Draw the road.
		// If the search failed, rt.steps will simply be empty.
		for(std::vector<map_location>::const_iterator step = rt.steps.begin();
				step != rt.steps.end(); ++step) {

			const int x = step->x;
			const int y = step->y;

			if(x < 0 || y < 0 || x >= static_cast<long>(data.width) || y >= static_cast<long>(data.height)) {
				continue;
			}

			// Find the configuration which tells us what to convert this tile to, to make it into a road.
			const config& child = cfg.find_child("road_cost", "terrain", t_translation::write_terrain_code(terrain[x][y]));
			if(child.empty()){
				continue;
			}

			/* Convert to bridge means that we want to convert depending on the direction of the road.
			 * Typically it will be in a format like convert_to_bridge = \,|,/
			 * '|' will be used if the road is going north-south
			 * '/' will be used if the road is going south west-north east
			 * '\' will be used if the road is going south east-north west
			 * The terrain will be left unchanged otherwise (if there is no clear direction).
			 */
			const std::string& convert_to_bridge = child["convert_to_bridge"];
			if(!convert_to_bridge.empty()) {
				if(step == rt.steps.begin() || step+1 == rt.steps.end()) {
					continue;
				}

				const map_location& last = *(step-1);
				const map_location& next = *(step+1);

				map_location adj[6];
				get_adjacent_tiles(*step,adj);

				int direction = -1;

				// If we are going north-south
				if((last == adj[0] && next == adj[3]) || (last == adj[3] && next == adj[0])) {
					direction = 0;
				}

				// If we are going south west-north east
				else if((last == adj[1] && next == adj[4]) || (last == adj[4] && next == adj[1])) {
					direction = 1;
				}

				// If we are going south east-north west
				else if((last == adj[2] && next == adj[5]) || (last == adj[5] && next == adj[2])) {
					direction = 2;
				}

				if(misc_labels != nullptr && !on_bridge) {
					on_bridge = true;
					std::string bridge_base_name = base_name_generator->generate();
					const std::string& name = bridge_name_generator->generate({{"base",  bridge_base_name}});
					const map_location loc(x - data.width / 3, y-data.height/3);
					misc_labels->emplace(loc, name);
					bridge_names.emplace(loc, bridge_base_name); //add to use for village naming
					bridges.insert(loc);
				}

				if(direction != -1) {
					const std::vector<std::string> items = utils::split(convert_to_bridge);
					if(size_t(direction) < items.size() && !items[direction].empty()) {
						terrain[x][y] = t_translation::read_terrain_code(items[direction]);
					}

					continue;
				}
			} else {
				on_bridge = false;
			}

			// Just a plain terrain substitution for a road
			const std::string& convert_to = child["convert_to"];
			if(!convert_to.empty()) {
				const t_translation::terrain_code letter = t_translation::read_terrain_code(convert_to);
				if(misc_labels != nullptr && terrain[x][y] != letter && name_count++ == name_frequency && !on_bridge) {
					misc_labels->emplace(map_location(x - data.width / 3, y - data.height / 3), road_name);
					name_count = 0;
				}

				terrain[x][y] = letter;
				if(misc_labels != nullptr) {
					const map_location loc(x - data.width / 3, y - data.height / 3); //add to use for village naming
					if(!road_base_name.empty())
						road_names.emplace(loc, road_base_name);
				}
			}
		}
	}

	// Now that road drawing is done, we can plonk down the castles.
	for(std::vector<map_location>::const_iterator c = castles.begin(); c != castles.end(); ++c) {
		if(!c->valid()) {
			continue;
		}

		const int x = c->x;
		const int y = c->y;
		const int player = c - castles.begin() + 1;
		const t_translation::coordinate coord(x, y);
		starting_positions.insert(t_translation::starting_positions::value_type(std::to_string(player), coord));
		terrain[x][y] = t_translation::HUMAN_KEEP;

		const int castle_array[13][2] {
			{-1, 0}, {-1, -1}, {0, -1}, {1, -1}, {1, 0}, {0, 1}, {-1, 1},
			{-2, 1}, {-2, 0}, {-2, -1}, {-1, -2}, {0, -2}, {1, -2}
		};

		for(int i = 0; i < data.castle_size - 1; i++) {
			terrain[x+ castle_array[i][0]][y+ castle_array[i][1]] = t_translation::HUMAN_CASTLE;
		}

		// Remove all labels under the castle tiles
		if(labels != nullptr) {
			labels->erase(map_location(x-data.width/3,y-data.height/3));
			for(int i = 0; i < data.castle_size - 1; i++) {
				labels->erase(map_location(x+ castle_array[i][0]-data.width/3, y+ castle_array[i][1]-data.height/3));
			}
		}
	}

	LOG_NG << "Placed roads. " << (SDL_GetTicks() - ticks) << " ticks elapsed" << "\n";
	ticks = SDL_GetTicks();

	/* Random naming for landforms: mountains, forests, swamps, hills
	 * we name these now that everything else is placed (as e.g., placing
	 * roads could split a forest)
	 */
	if(misc_labels != nullptr) {
		for(int x = data.width / 3; x < (data.width / 3)*2; x++) {
			for(int y = data.height / 3; y < (data.height / 3) * 2;y++) {
				//check the terrain of the tile
				const map_location loc(x - data.width / 3, y - data.height / 3);
				const t_translation::terrain_code terr = terrain[x][y];
				std::string name, base_name;
				std::set<std::string> used_names;

				if(t_translation::terrain_matches(terr, t_translation::ALL_MOUNTAINS)) {
					//name every 15th mountain
					if((rng_() % 15) == 0) {
						for(size_t ntry = 0; ntry != 30 && (ntry == 0 || used_names.count(name) > 0); ++ntry) {
							base_name = base_name_generator->generate();
							name = mountain_name_generator->generate({{"base",  base_name}});
						}
						misc_labels->emplace(loc, name);
						mountain_names.emplace(loc, base_name);
					}
				} else if(t_translation::terrain_matches(terr, t_translation::ALL_FORESTS)) {
					// If the forest tile is not named yet, name it
					const std::map<map_location, std::string>::const_iterator forest_name = forest_names.find(loc);
					if(forest_name == forest_names.end()) {
						for(size_t ntry = 0; ntry != 30 && (ntry == 0 || used_names.count(name) > 0); ++ntry) {
							base_name = base_name_generator->generate();
							name = forest_name_generator->generate({{"base",  base_name}});
						}
						forest_names.emplace(loc, base_name);
						// name all connected forest tiles accordingly
						flood_name(loc, base_name, forest_names, t_translation::ALL_FORESTS, terrain, data.width, data.height, 0, misc_labels, name);
					}
				} else if(t_translation::terrain_matches(terr, t_translation::ALL_SWAMPS)) {
					// If the swamp tile is not named yet, name it
					const std::map<map_location, std::string>::const_iterator swamp_name = swamp_names.find(loc);
					if(swamp_name == swamp_names.end()) {
						for(size_t ntry = 0; ntry != 30 && (ntry == 0 || used_names.count(name) > 0); ++ntry) {
							base_name = base_name_generator->generate();
							name = swamp_name_generator->generate({{"base",  base_name}});
						}
						swamp_names.emplace(loc, base_name);
						// name all connected swamp tiles accordingly
						flood_name(loc, base_name, swamp_names, t_translation::ALL_SWAMPS, terrain, data.width, data.height, 0, misc_labels, name);
					}
				}
			}
		}
	}

	LOG_NG << "Named landforms. " << (SDL_GetTicks() - ticks) << " ticks elapsed" << "\n";
	LOG_NG << "Placing villages...\n";
	ticks = SDL_GetTicks();

	/*
	 * Place villages in a 'grid', to make placing fair, but with villages
	 * displaced from their position according to terrain and randomness, to
	 * add some variety.
	 */
	std::set<map_location> villages;

	if(data.nvillages > 0) {

		// First we work out the size of the x and y distance between villages
		const size_t tiles_per_village = ((data.width*data.height)/9)/data.nvillages;
		size_t village_x = 1, village_y = 1;

		// Alternate between incrementing the x and y value.
		// When they are high enough to equal or exceed the tiles_per_village,
		// then we have them to the value we want them at.
		while(village_x*village_y < tiles_per_village) {
			if(village_x < village_y) {
				++village_x;
			} else {
				++village_y;
			}
		}

		std::set<std::string> used_names;
		tcode_list_cache adj_liked_cache;

		config village_naming = game_config_.child("village_naming");

		if(cfg.has_child("village_naming")) {
			village_naming.append_attributes(cfg.child("village_naming"));
		}

		// If the [village_naming] child is empty, we cannot provide good names.
		std::map<map_location,std::string>* village_labels = village_naming.empty() ? nullptr : labels;

		for(int vx = 0; vx < data.width; vx += village_x) {
			LOG_NG << "village at " << vx << "\n";

			for(int vy = rng_()%village_y; vy < data.height; vy += village_y) {
				const size_t add = rng_()%3;
				const size_t x = (vx + add) - 1;
				const size_t y = (vy + add) - 1;

				const map_location res = place_village(terrain, x, y, 2, cfg, adj_liked_cache);

				if(res.x  < static_cast<long>(data.width     ) / 3 ||
				   res.x >= static_cast<long>(data.width  * 2) / 3 ||
				   res.y  < static_cast<long>(data.height    ) / 3 ||
				   res.y >= static_cast<long>(data.height * 2) / 3) {
					continue;
				}

				const std::string str = t_translation::write_terrain_code(terrain[res.x][res.y]);

				const std::string& convert_to = cfg.find_child("village", "terrain", str)["convert_to"].str();
				if(convert_to.empty()) {
					continue;
				}

				terrain[res.x][res.y] = t_translation::read_terrain_code(convert_to);

				villages.insert(res);

				if(village_labels == nullptr) {
					continue;
				}

				name_generator_factory village_name_generator_factory{ village_naming,
					{"base", "male", "village", "lake", "river", "bridge", "grassland", "forest", "hill", "mountain", "mountain_anon", "road", "swamp"} };

				village_naming.get_old_attribute("base_names", "male_names", "[village_naming]male_names= is deprecated, use base_names= instead");
				//Due to the attribute detection feature of the factory we also support male_name_generator= but keep it undocumented.

				base_name_generator = village_name_generator_factory.get_name_generator(
					(village_naming.has_attribute("base_names") || village_naming.has_attribute("base_name_generator")) ? "base" : "male" );

				const map_location loc(res.x-data.width/3,res.y-data.height/3);

				map_location adj[6];
				get_adjacent_tiles(loc,adj);

				std::string name_type = "village";
				const t_translation::ter_list
					field	 = t_translation::ter_list(1, t_translation::GRASS_LAND),
					forest   = t_translation::ter_list(1, t_translation::FOREST),
					mountain = t_translation::ter_list(1, t_translation::MOUNTAIN),
					hill	 = t_translation::ter_list(1, t_translation::HILL);

				size_t field_count = 0, forest_count = 0, mountain_count = 0, hill_count = 0;

				std::map<std::string,std::string> symbols;

				size_t n;
				for(n = 0; n != 6; ++n) {
					const std::map<map_location,std::string>::const_iterator road_name = road_names.find(adj[n]);
					if(road_name != road_names.end()) {
						symbols["road"] = road_name->second;
						name_type = "road";
						break;
					}

					const std::map<map_location,std::string>::const_iterator river_name = river_names.find(adj[n]);
					if(river_name != river_names.end()) {
						symbols["river"] = river_name->second;
						name_type = "river";

						const std::map<map_location,std::string>::const_iterator bridge_name = bridge_names.find(adj[n]);
						if(bridge_name != bridge_names.end()) {
							//we should always end up here, since if there is an adjacent bridge, there has to be an adjacent river too
							symbols["bridge"] = bridge_name->second;
							name_type = "river_bridge";
						}

						break;
					}

					const std::map<map_location,std::string>::const_iterator forest_name = forest_names.find(adj[n]);
					if(forest_name != forest_names.end()) {
						symbols["forest"] = forest_name->second;
						name_type = "forest";
						break;
					}

					const std::map<map_location,std::string>::const_iterator lake_name = lake_names.find(adj[n]);
					if(lake_name != lake_names.end()) {
						symbols["lake"] = lake_name->second;
						name_type = "lake";
						break;
					}

					const std::map<map_location,std::string>::const_iterator mountain_name = mountain_names.find(adj[n]);
					if(mountain_name != mountain_names.end()) {
						symbols["mountain"] = mountain_name->second;
						name_type = "mountain";
						break;
					}

					const std::map<map_location,std::string>::const_iterator swamp_name = swamp_names.find(adj[n]);
					if(swamp_name != swamp_names.end()) {
						symbols["swamp"] = swamp_name->second;
						name_type = "swamp";
						break;
					}

					const t_translation::terrain_code terr = terrain[adj[n].x+data.width/3][adj[n].y+data.height/3];

					if(std::count(field.begin(),field.end(),terr) > 0) {
						++field_count;
					} else if(std::count(forest.begin(),forest.end(),terr) > 0) {
						++forest_count;
					} else if(std::count(hill.begin(),hill.end(),terr) > 0) {
						++hill_count;
					} else if(std::count(mountain.begin(),mountain.end(),terr) > 0) {
						++mountain_count;
					}
				}

				if(n == 6) {
					if(field_count == 6) {
						name_type = "grassland";
					} else if(forest_count >= 2) {
						name_type = "forest";
					} else if(mountain_count >= 1) {
						name_type = "mountain_anon";
					} else if(hill_count >= 2) {
						name_type = "hill";
					}
				}

				std::string name;

				symbols["base"] = base_name_generator->generate();
				std::shared_ptr<name_generator> village_name_generator = village_name_generator_factory.get_name_generator(name_type);

				for(size_t ntry = 0; ntry != 30 && (ntry == 0 || used_names.count(name) > 0); ++ntry) {
					name = village_name_generator->generate(symbols);
				}

				used_names.insert(name);
				village_labels->emplace(loc, name);
			}
		}
	}

	LOG_NG << "Placed villages. " << (SDL_GetTicks() - ticks) << " ticks elapsed" << "\n";

	return output_map(terrain, starting_positions);
}