Esempio n. 1
0
LEVEL_RESULT campaign_controller::play_game()
{
	if(is_replay_) {
		state_.get_replay().set_pos(0);
	}
	else {
		state_.get_replay().set_to_end();
	}

	state_.expand_scenario();

	game_classification::CAMPAIGN_TYPE game_type = state_.classification().campaign_type;

	while(state_.valid())
	{
		LEVEL_RESULT res = LEVEL_RESULT::VICTORY;
		end_level_data end_level;
		try {

			state_.expand_random_scenario();
			//In case this an mp scenario reloaded by sp this was not already done yet.
			state_.expand_mp_events();

			sound::empty_playlist();

			state_.expand_carryover();
			//expand_mp_options must be called after expand_carryover because expand_carryover will to set previous variables if there are already variables in the [scenario]
			state_.expand_mp_options();

#if !defined(ALWAYS_USE_MP_CONTROLLER)
			if (game_type != game_classification::CAMPAIGN_TYPE::MULTIPLAYER || is_replay_) {
				res = playsingle_scenario(end_level);
				if(is_replay_) {
					return res;
				}
			} else
#endif
			{
				res = playmp_scenario(end_level);
			}
		} catch(game::load_game_failed& e) {
			gui2::show_error_message(video_, _("The game could not be loaded: ") + e.message);
			return LEVEL_RESULT::QUIT;
		} catch(quit_game_exception&) {
			LOG_NG << "The game was aborted\n";
			return LEVEL_RESULT::QUIT;
		} catch(game::game_error& e) {
			gui2::show_error_message(video_, _("Error while playing the game: ") + e.message);
			return LEVEL_RESULT::QUIT;
		} catch(incorrect_map_format_error& e) {
			gui2::show_error_message(video_, std::string(_("The game map could not be loaded: ")) + e.message);
			return LEVEL_RESULT::QUIT;
		} catch (mapgen_exception& e) {
			gui2::show_error_message(video_, std::string(_("Map generator error: ") + e.message));
		} catch(config::error& e) {
			gui2::show_error_message(video_, _("Error while reading the WML: ") + e.message);
			return LEVEL_RESULT::QUIT;
		} catch(twml_exception& e) {
			e.show(video_);
			return LEVEL_RESULT::QUIT;
		}

		if (is_unit_test_) {
			return res;
		}
		if(res == LEVEL_RESULT::QUIT) {
			return res;
		}
		// proceed_to_next_level <=> 'any human side recieved victory'
		// If 'any human side recieved victory' we do the Save-management options
		// Otherwise we are done now
		if(!end_level.proceed_to_next_level) {
			return res;
		}

		if (preferences::delete_saves()) {
			savegame::clean_saves(state_.classification().label);
		}
		if (preferences::save_replays() && end_level.replay_save) {
			savegame::replay_savegame save(state_, preferences::save_compression_format());
			save.save_game_automatic(video_, true);
		}

		state_.convert_to_start_save();

		//If there is no next scenario we're done now.
		if(state_.get_scenario_id().empty())
		{
			return res;
		}
		else if(res == LEVEL_RESULT::OBSERVER_END && mp_info_ && !mp_info_->is_host)
		{
			const int dlg_res = gui2::show_message(video_, _("Game Over"),
				_("This scenario has ended. Do you want to continue the campaign?"),
				gui2::tmessage::yes_no_buttons);

			if(dlg_res == gui2::twindow::CANCEL) {
				return res;
			}
		}

		if (mp_info_ && !mp_info_->is_host) {
			// Opens mp::connect dialog to get a new gamestate.
			mp::ui::result wait_res = mp::goto_mp_wait(video_, state_,
				game_config_, &mp_info_->wesnothd_connection, res == LEVEL_RESULT::OBSERVER_END);
			if (wait_res == mp::ui::QUIT) {
				return LEVEL_RESULT::QUIT;
			}

			//The host should send the complete savegame now that also contains the carryvoer sides start.
		} else {
			// Retrieve next scenario data.
			state_.expand_scenario();

			if (state_.valid()) {
				//note that although starting_pos is const it might be changed by gamestate.some_non_const_operation()  .
				const config& starting_pos = state_.get_starting_pos();

				const bool is_mp = state_.classification().is_normal_mp_game();
				state_.mp_settings().num_turns = starting_pos["turns"].to_int(-1);
				state_.mp_settings().saved_game = false;
				state_.mp_settings().use_map_settings = starting_pos["force_lock_settings"].to_bool(!is_mp);

				ng::connect_engine_ptr connect_engine(new ng::connect_engine(state_, false, mp_info_));

				if (!connect_engine->can_start_game() || (game_config::debug && game_type == game_classification::CAMPAIGN_TYPE::MULTIPLAYER)) {
					// Opens mp::connect dialog to allow users to make an adjustments for scenario.
					mp::ui::result connect_res = mp::goto_mp_connect(video_,
						*connect_engine, game_config_, mp_info_ ? &mp_info_->wesnothd_connection : nullptr, state_.mp_settings().name);
					if (connect_res == mp::ui::QUIT) {
						return LEVEL_RESULT::QUIT;
					}
				} else {
					// Start the next scenario immediately.
					connect_engine->start_game();
				}
			}
		}

		if(state_.valid()) {
			// Update the label
			state_.update_label();

			// If this isn't the last scenario, then save the game
			if(end_level.prescenario_save) {

				// For multiplayer, we want the save
				// to contain the starting position.
				// For campaigns however, this is the
				// start-of-scenario save and the
				// starting position needs to be empty,
				// to force a reload of the scenario config.

				savegame::scenariostart_savegame save(state_, preferences::save_compression_format());

				save.save_game_automatic(video_);
			}

		}
	}

	if (!state_.get_scenario_id().empty()) {
		std::string message = _("Unknown scenario: '$scenario|'");
		utils::string_map symbols;
		symbols["scenario"] = state_.get_scenario_id();
		message = utils::interpolate_variables_into_string(message, &symbols);
		gui2::show_error_message(video_, message);
		return LEVEL_RESULT::QUIT;
	}

	if (game_type == game_classification::CAMPAIGN_TYPE::SCENARIO){
		if (preferences::delete_saves()) {
			savegame::clean_saves(state_.classification().label);
		}
	}
	return LEVEL_RESULT::VICTORY;
}
Esempio n. 2
0
LEVEL_RESULT play_game(display& disp, game_state& gamestate, const config& game_config, hero_map& heros, hero_map& heros_start,
		card_map& cards,
		io_type_t io_type, bool skip_replay)
{
	tent::io_type = io_type;
	std::string type = gamestate.classification().campaign_type;
	if (type.empty()) {
		type = "scenario";
	}

	config const* scenario = NULL;

	// 'starting_pos' will contain the position we start the game from.
	config starting_pos;

	// Do we have any snapshot data?
	// yes => this must be a savegame
	// no  => we are starting a fresh scenario
	if (!gamestate.snapshot["runtime"].to_bool() || !recorder.at_end()) {
		gamestate.classification().completion = "running";
		// Campaign or Multiplayer?
		// If the gamestate already contains a starting_pos,
		// then we are starting a fresh multiplayer game.
		// Otherwise this is the start of a campaign scenario.
		if (gamestate.starting_pos["id"].empty() == false) {
			starting_pos = gamestate.starting_pos;
			scenario = &starting_pos;
		} else {
			// reload of the scenario, as starting_pos contains carryover information only
			scenario = &load_campagin_scenario(gamestate.classification().campaign, gamestate.classification().scenario, type);
			VALIDATE(!scenario->empty(), std::string("play_game, cannot load scenario id = ") + gamestate.classification().scenario + " in game_config!");
			gamestate.starting_pos = *scenario;
			starting_pos = gamestate.starting_pos;
			scenario = &starting_pos;
		}
	} else {
		// This game was started from a savegame
		starting_pos = gamestate.starting_pos;
		scenario = &gamestate.snapshot;
		// When starting wesnoth --multiplayer there might be
		// no variables which leads to a segfault
		if (const config &vars = gamestate.snapshot.child("variables")) {
			gamestate.set_variables(vars);
		}
		gamestate.set_menu_items(gamestate.snapshot.child_range("menu_item"));
		// Replace game label with that from snapshot
		if (!gamestate.snapshot["label"].empty()){
			gamestate.classification().label = gamestate.snapshot["label"].str();
		}
	}

	bool fog = scenario->get("fog")->to_bool();
	bool shroud = scenario->get("shroud")->to_bool();

	while (scenario != NULL) {
		// If we are a multiplayer client, tweak the controllers
		if (io_type == IO_CLIENT) {
			if (scenario != &starting_pos) {
				starting_pos = *scenario;
				scenario = &starting_pos;
			}
		}

		config::const_child_itors story = scenario->child_range("story");
		gamestate.classification().next_scenario = (*scenario)["next_scenario"].str();

		bool save_game_after_scenario = true;

		LEVEL_RESULT res = VICTORY;
		end_level_data end_level;

		try {
			// Preserve old label eg. replay
			if (gamestate.classification().label.empty()) {
				t_string tstr = (*scenario)["name"];
				if (gamestate.classification().abbrev.empty()) {
					gamestate.classification().label = tstr;
					gamestate.classification().original_label = tstr.base_str();
				} else {
					gamestate.classification().label = std::string(gamestate.classification().abbrev);
					gamestate.classification().label.append("-");
					gamestate.classification().original_label = gamestate.classification().label;
					gamestate.classification().label.append(tstr);
					gamestate.classification().original_label.append(tstr.base_str());
				}
			}

			// If the entire scenario should be randomly generated
			if ((*scenario)["scenario_generation"] != "") {
				const cursor::setter cursor_setter(cursor::WAIT);

				static config scenario2;
				scenario2 = random_generate_scenario((*scenario)["scenario_generation"], scenario->child("generator"));
				//level_ = scenario;
				//merge carryover information into the newly generated scenario
				config temp(scenario2);
				gamestate.starting_pos = temp;
				scenario = &scenario2;
			}
			std::string map_data = (*scenario)["map_data"];
			if(map_data.empty() && (*scenario)["map"] != "") {
				map_data = read_map((*scenario)["map"]);
			}

			// If the map should be randomly generated
			if(map_data.empty() && (*scenario)["map_generation"] != "") {
				const cursor::setter cursor_setter(cursor::WAIT);
				map_data = random_generate_map((*scenario)["map_generation"],scenario->child("generator"));

				// Since we've had to generate the map,
				// make sure that when we save the game,
				// it will not ask for the map to be generated again on reload
				static config new_level;
				new_level = *scenario;
				new_level["map_data"] = map_data;
				scenario = &new_level;

				//merge carryover information into the scenario
				config temp(new_level);
				gamestate.starting_pos = temp;
			}

			sound::empty_playlist();

			//add the variables to the starting_pos unless they are already there
			const config &wmlvars = gamestate.starting_pos.child("variables");
			if (!wmlvars || wmlvars.empty()){
				gamestate.starting_pos.clear_children("variables");
				gamestate.starting_pos.add_child("variables", gamestate.get_variables());
			}

			switch (io_type){
			case IO_NONE:
				res = playsingle_scenario(game_config, scenario, disp, gamestate, heros, heros_start, cards, story, skip_replay, end_level);
				break;
			case IO_SERVER:
			case IO_CLIENT:
				res = playmp_scenario(game_config, scenario, disp, gamestate, heros, heros_start, cards, story, skip_replay, io_type, end_level);
				break;
			}
		} catch(game::load_game_failed& e) {
			gui2::show_error_message(disp.video(), _("The game could not be loaded: ") + e.message);
			return QUIT;
		} catch(game::game_error& e) {
			gui2::show_error_message(disp.video(), _("Error while playing the game: ") + e.message);
			return QUIT;
		} catch(incorrect_map_format_error& e) {
			gui2::show_error_message(disp.video(), std::string(_("The game map could not be loaded: ")) + e.message);
			return QUIT;
		} catch(config::error& e) {
			std::cerr << "caught config::error...\n";
			gui2::show_error_message(disp.video(), _("Error while reading the WML: ") + e.message);
			return QUIT;
		} catch(twml_exception& e) {
			e.show(disp);
			return QUIT;
		}


		// Save-management options fire on game end.
		// This means: (a) we have a victory, or
		// or (b) we're multiplayer live, in which
		// case defeat is also game end.  Someday,
		// if MP campaigns ever work again, we might
		// need to change this test.
		if (res == VICTORY || (io_type != IO_NONE && res == DEFEAT)) {
			if (preferences::delete_saves()) {
				savegame::manager::clean_saves(gamestate.classification().label);
			}
		}

		recorder.clear();
		gamestate.replay_data.clear();
		gamestate.start_scenario_ss.str("");
		gamestate.clear_start_hero_data();

		// On DEFEAT, QUIT, or OBSERVER_END, we're done now
		if (res != VICTORY) {
			if (res != OBSERVER_END || gamestate.classification().next_scenario.empty()) {
				gamestate.snapshot = config();
				return res;
			}

			const int dlg_res = gui2::show_message(disp.video(), _("Game Over"),
				_("This scenario has ended. Do you want to continue the campaign?"),
				gui2::tmessage::yes_no_buttons);

			if (dlg_res == gui2::twindow::CANCEL) {
				gamestate.snapshot = config();
				return res;
			}
		}

		// Continue without saving is like a victory,
		// but the save game dialog isn't displayed
		if (!end_level.prescenario_save) {
			save_game_after_scenario = false;
		}

		// Switch to the next scenario.
		gamestate.classification().scenario = gamestate.classification().next_scenario;
		gamestate.rng().rotate_random();
		// gamestate.rng().seed_random(rand(), 0);

		scenario = NULL;
		if (io_type == IO_NONE) {
			scenario = &load_campagin_scenario(gamestate.classification().campaign, gamestate.classification().scenario, type);
			if (scenario->empty()) {
				scenario = NULL;
			} else{
				starting_pos = *scenario;
				starting_pos["fog"] = fog;
				starting_pos["shroud"] = shroud;
				scenario = &starting_pos;
			}
		}

		if (scenario != NULL) {
			// to single-player campaign, there are multi-secenario, 
			// set this result to next start hero data
			heros.reset_to_unstage();
			heros_start = heros;

			loadscreen::global_loadscreen_manager* loadscreen_manager = loadscreen::global_loadscreen_manager::get();
			if (loadscreen_manager) {
				loadscreen_manager->reset();
			}
			// Update the label
			// std::string oldlabel = gamestate.classification().label;
			t_string scenario_name = (*scenario)["name"];
			if (gamestate.classification().abbrev.empty()) {
				gamestate.classification().label = scenario_name;
				gamestate.classification().original_label = scenario_name.base_str();
			} else {
				gamestate.classification().label = std::string(gamestate.classification().abbrev);
				gamestate.classification().label.append("-");
				gamestate.classification().original_label = gamestate.classification().label;
				gamestate.classification().label.append(scenario_name);
				gamestate.classification().original_label.append(scenario_name.base_str());
			}

			// If this isn't the last scenario, then save the game
			if (save_game_after_scenario) {

				// For multiplayer, we want the save
				// to contain the starting position.
				// For campaigns however, this is the
				// start-of-scenario save and the
				// starting position needs to be empty,
				// to force a reload of the scenario config.
				if (gamestate.classification().campaign_type != "multiplayer"){
					gamestate.starting_pos = config();
				}

				//add the variables to the starting position
				gamestate.starting_pos.add_child("variables", gamestate.get_variables());

				savegame::scenariostart_savegame save(heros, heros_start, gamestate);

				save.save_game_automatic(disp.video());
			}

			if (gamestate.classification().campaign_type != "multiplayer"){
				gamestate.starting_pos = *scenario;
				//add the variables to the starting position
				gamestate.starting_pos.add_child("variables", gamestate.get_variables());
			}
			gamestate.start_scenario_ss.str("");
		}
		gamestate.snapshot = config();
	}

	if (!gamestate.classification().scenario.empty() && gamestate.classification().scenario != "null") {
		std::string message = _("Unknown scenario: '$scenario|'");
		utils::string_map symbols;
		symbols["scenario"] = gamestate.classification().scenario;
		message = utils::interpolate_variables_into_string(message, &symbols);
		gui2::show_error_message(disp.video(), message);
		return QUIT;
	}

	if (gamestate.classification().campaign_type == "scenario"){
		if (preferences::delete_saves()) {
			savegame::manager::clean_saves(gamestate.classification().label);
		}
	}
	return VICTORY;
}