//move all sides till stop/end
possible_end_play_signal replay_controller::play_replay(){

	if (recorder.at_end()){
		//shouldn't actually happen
		return boost::none;
	}

	is_playing_ = true;
	replay_ui_playback_should_start();

	possible_end_play_signal signal = play_replay_main_loop();

	if(signal) {
		switch ( boost::apply_visitor(get_signal_type(), *signal)) {
			case END_TURN:
				return signal;
			case END_LEVEL:
				if ( boost::apply_visitor(get_result(), *signal) == QUIT) {
					return signal;
				}
		}

	}

	if (!is_playing_) {
		gui_->scroll_to_leader(player_number_,game_display::ONSCREEN,false);
	}

	replay_ui_playback_should_stop();

	return boost::none;
}
LEVEL_RESULT play_replay_level(const config& game_config,
		CVideo& video, saved_game& state_of_game, bool is_unit_test)
{
	const int ticks = SDL_GetTicks();

	DBG_NG << "creating objects... " << (SDL_GetTicks() - ticks) << std::endl;

	boost::scoped_ptr<replay_controller> rc;

	try {
		rc.reset(new replay_controller(state_of_game.get_replay_starting_pos(), state_of_game, ticks, game_config, video));
	} catch (end_level_exception & e){
		return e.result;
	} catch (end_turn_exception &) {
		throw; //this should never happen? It would likely have crashed the program before, so in refactor I won't change but we should fix it later.
	}
	DBG_NG << "created objects... " << (SDL_GetTicks() - rc->get_ticks()) << std::endl;

	const events::command_disabler disable_commands;

	//replay event-loop
	possible_end_play_signal signal = play_replay_level_main_loop(*rc, is_unit_test);

	if (signal) {
		switch( boost::apply_visitor( get_signal_type(), *signal ) ) {
			case END_LEVEL:
				DBG_NG << "play_replay_level: end_level_exception" << std::endl;
				break;
			case END_TURN:
				DBG_NG << "Unchecked end_turn_exception signal propogated to replay controller play_replay_level! Terminating." << std::endl;
				assert(false && "unchecked end turn exception in replay controller");
				throw 42;
		}
	}

	return VICTORY;
}
Example #3
0
possible_end_play_signal playsingle_controller::play_side()
{
    //check for team-specific items in the scenario
    gui_->parse_team_overlays();

    HANDLE_END_PLAY_SIGNAL( maybe_do_init_side(false) );

    //flag used when we fallback from ai and give temporarily control to human
    bool temporary_human = false;
    do {
        // This flag can be set by derived classes (in overridden functions).
        player_type_changed_ = false;
        if (!skip_next_turn_)
            end_turn_ = false;

        statistics::reset_turn_stats(gamestate_.board_.teams()[player_number_ - 1].save_id());

        if(current_team().is_human() || temporary_human) {
            LOG_NG << "is human...\n";
            temporary_human = false;
            // If a side is dead end the turn, but play at least side=1's
            // turn in case all sides are dead
            if (gamestate_.board_.side_units(player_number_) != 0
                    || (resources::units->size() == 0 && player_number_ == 1))
            {
                possible_end_play_signal signal = before_human_turn();

                if (!signal) {
                    signal = play_human_turn();
                }

                if (signal) {
                    switch (boost::apply_visitor(get_signal_type(), *signal)) {
                    case END_LEVEL:
                        return signal;
                    case END_TURN:
                        if (int(boost::apply_visitor(get_redo(),*signal)) == player_number_) {
                            player_type_changed_ = true;
                            // If new controller is not human,
                            // reset gui to prev human one
                            if (!gamestate_.board_.teams()[player_number_-1].is_human()) {
                                browse_ = true;
                                int s = find_human_team_before_current_player();
                                if (s <= 0)
                                    s = gui_->playing_side();
                                update_gui_to_player(s-1);
                            }
                        }

                    }
                }
            }

            // Ending the turn commits all moves.
            undo_stack_->clear();
            if ( !player_type_changed_ )
                after_human_turn();
            LOG_NG << "human finished turn...\n";

        } else if(current_team().is_ai()) {
            try {
                play_ai_turn();
            } catch(fallback_ai_to_human_exception&) {
                // Give control to a human for this turn.
                player_type_changed_ = true;
                temporary_human = true;
            } catch (end_level_exception & e) { //Don't know at the moment if these two are possible but can't hurt to add
                return possible_end_play_signal(e.to_struct());
            } catch (end_turn_exception & e) {
                return possible_end_play_signal(e.to_struct());
            }
            if(!player_type_changed_)
            {
                recorder.end_turn();
            }

        } else if(current_team().is_network()) {
            PROPOGATE_END_PLAY_SIGNAL( play_network_turn() );
        } else if(current_team().is_idle()) {
            end_turn_enable(false);
            do_idle_notification();

            possible_end_play_signal signal = before_human_turn();

            if (!signal) {
                signal = play_idle_loop();
            }

            if (signal) {
                switch (boost::apply_visitor(get_signal_type(), *signal)) {
                case END_LEVEL:
                    return signal;
                case END_TURN:
                    LOG_NG << "Escaped from idle state with exception!" << std::endl;
                    if (int(boost::apply_visitor(get_redo(), *signal)) == player_number_) {
                        player_type_changed_ = true;
                        // If new controller is not human,
                        // reset gui to prev human one
                        if (!gamestate_.board_.teams()[player_number_-1].is_human()) {
                            browse_ = true;
                            int s = find_human_team_before_current_player();
                            if (s <= 0)
                                s = gui_->playing_side();
                            update_gui_to_player(s-1);
                        }
                    }
                }
            }
        }

        // Else current_team().is_empty(), so do nothing.

    } while (player_type_changed_);
    // Keep looping if the type of a team (human/ai/networked)
    // has changed mid-turn
    skip_next_turn_ = false;
    return boost::none;
}
Example #4
0
possible_end_play_signal playsingle_controller::play_turn()
{
    whiteboard_manager_->on_gamestate_change();
    gui_->new_turn();
    gui_->invalidate_game_status();
    events::raise_draw_event();

    LOG_NG << "turn: " << turn() << "\n";

    if(non_interactive()) {
        LOG_AIT << "Turn " << turn() << ":" << std::endl;
    }

    for (player_number_ = first_player_; player_number_ <= int(gamestate_.board_.teams().size()); ++player_number_)
    {
        // If a side is empty skip over it.
        if (current_team().is_empty()) continue;

        possible_end_play_signal signal;
        {
            save_blocker blocker;
            signal = init_side();
        }

        if (signal) {
            switch (boost::apply_visitor(get_signal_type(), *signal)) {
            case END_TURN:
                if (current_team().is_network() == false) {
                    turn_data_.send_data();
                    recorder.end_turn();
                    turn_data_.sync_network();
                }
                continue;
            case END_LEVEL:
                return signal;
            }
        }

        if (replaying_) {
            LOG_NG << "doing replay " << player_number_ << "\n";
            HANDLE_END_PLAY_SIGNAL ( replaying_ = ::do_replay() == REPLAY_FOUND_END_TURN );
            LOG_NG << "result of replay: " << (replaying_?"true":"false") << "\n";
        } else {
            ai_testing::log_turn_start(player_number_);
            PROPOGATE_END_PLAY_SIGNAL ( play_side() );
        }

        finish_side_turn();

        if(non_interactive()) {
            LOG_AIT << " Player " << player_number_ << ": " <<
                    current_team().villages().size() << " Villages" <<
                    std::endl;
            ai_testing::log_turn_end(player_number_);
        }

        HANDLE_END_PLAY_SIGNAL ( check_victory() );

        //if loading a savegame, network turns might not have reset this yet
        loading_game_ = false;
    }
    //If the loop exits due to the last team having been processed,
    //player_number_ will be 1 too high
    if(player_number_ > static_cast<int>(gamestate_.board_.teams().size()))
        player_number_ = gamestate_.board_.teams().size();

    finish_turn();

    // Time has run out
    PROPOGATE_END_PLAY_SIGNAL ( check_time_over() );
    return boost::none;
}
Example #5
0
LEVEL_RESULT playsingle_controller::play_scenario(
    const config::const_child_itors &story,
    bool skip_replay)
{
    LOG_NG << "in playsingle_controller::play_scenario()...\n";

    // Start music.
    BOOST_FOREACH(const config &m, level_.child_range("music")) {
        sound::play_music_config(m);
    }
    sound::commit_music_changes();

    if(!skip_replay) {
        show_story(*gui_, level_["name"], story);
    }
    gui_->labels().read(level_);

    // Read sound sources
    assert(soundsources_manager_ != NULL);
    BOOST_FOREACH(const config &s, level_.child_range("sound_source")) {
        try {
            soundsource::sourcespec spec(s);
            soundsources_manager_->add(spec);
        } catch (bad_lexical_cast &) {
            ERR_NG << "Error when parsing sound_source config: bad lexical cast." << std::endl;
            ERR_NG << "sound_source config was: " << s.debug() << std::endl;
            ERR_NG << "Skipping this sound source..." << std::endl;
        }
    }

    set_victory_when_enemies_defeated(level_["victory_when_enemies_defeated"].to_bool(true));
    set_remove_from_carryover_on_defeat(level_["remove_from_carryover_on_defeat"].to_bool(true));
    end_level_data &end_level = get_end_level_data();
    end_level.carryover_percentage = level_["carryover_percentage"].to_int(game_config::gold_carryover_percentage);
    end_level.carryover_add = level_["carryover_add"].to_bool();

    bool past_prestart = false;

    LOG_NG << "entering try... " << (SDL_GetTicks() - ticks_) << "\n";
    try {
        possible_end_play_signal signal = play_scenario_init(end_level, past_prestart);

        if (!signal) {

            signal = play_scenario_main_loop(end_level, past_prestart);
        }

        if (signal) {
            switch (boost::apply_visitor( get_signal_type(), *signal )) {
            //BEGIN CASES
            case END_TURN:
                assert(false && "end turn signal propogated to playsingle_controller::play_scenario. This results in terminate!");
                throw 42;
            case END_LEVEL:
                if(!past_prestart) {
                    sdl::draw_solid_tinted_rectangle(
                        0, 0, gui_->video().getx(), gui_->video().gety(), 0, 0, 0, 1.0,
                        gui_->video().getSurface()
                    );
                    update_rect(0, 0, gui_->video().getx(), gui_->video().gety());
                }

                ai_testing::log_game_end();
                LEVEL_RESULT end_level_result = boost::apply_visitor( get_result(), *signal );
                if (!end_level.transient.custom_endlevel_music.empty()) {
                    if (end_level_result == DEFEAT) {
                        set_defeat_music_list(end_level.transient.custom_endlevel_music);
                    } else {
                        set_victory_music_list(end_level.transient.custom_endlevel_music);
                    }
                }

                if (gamestate_.board_.teams().empty())
                {
                    //store persistent teams
                    saved_game_.set_snapshot(config());

                    return VICTORY; // this is probably only a story scenario, i.e. has its endlevel in the prestart event
                }
                const bool obs = is_observer();
                if (game_config::exit_at_end) {
                    exit(0);
                }
                if (end_level_result == DEFEAT || end_level_result == VICTORY)
                {
                    saved_game_.classification().completion = (end_level_result == VICTORY) ? "victory" : "defeat";
                    // If we're a player, and the result is victory/defeat, then send
                    // a message to notify the server of the reason for the game ending.
                    if (!obs) {
                        config cfg;
                        config& info = cfg.add_child("info");
                        info["type"] = "termination";
                        info["condition"] = "game over";
                        info["result"] = saved_game_.classification().completion;
                        network::send_data(cfg, 0);
                    } else {
                        gui2::show_transient_message(gui_->video(),_("Game Over"),
                                                     _("The game is over."));
                        return OBSERVER_END;
                    }
                }

                if (end_level_result == QUIT) {
                    return QUIT;
                }
                else if (end_level_result == DEFEAT)
                {
                    saved_game_.classification().completion = "defeat";
                    game_events::fire("defeat");

                    if (!obs) {
                        const std::string& defeat_music = select_defeat_music();
                        if(defeat_music.empty() != true)
                            sound::play_music_once(defeat_music);

                        persist_.end_transaction();
                        return DEFEAT;
                    } else {
                        return QUIT;
                    }
                }
                else if (end_level_result == VICTORY)
                {
                    saved_game_.classification().completion =
                        !end_level.transient.linger_mode ? "running" : "victory";
                    game_events::fire("victory");

                    //
                    // Play victory music once all victory events
                    // are finished, if we aren't observers.
                    //
                    // Some scenario authors may use 'continue'
                    // result for something that is not story-wise
                    // a victory, so let them use [music] tags
                    // instead should they want special music.
                    //
                    if (!obs && end_level.transient.linger_mode) {
                        const std::string& victory_music = select_victory_music();
                        if(victory_music.empty() != true)
                            sound::play_music_once(victory_music);
                    }

                    // Add all the units that survived the scenario.
                    // this function doesn't move unit to the recalllist anymore i just keep this name to prevent merging conflicts.
                    LOG_NG << "Add units that survived the scenario to the recall list.\n";
                    gamestate_.board_.all_survivors_to_recall();

                    saved_game_.remove_snapshot();
                    if(!is_observer()) {
                        persist_.end_transaction();
                    }

                    return VICTORY;
                }
                else if (end_level_result == SKIP_TO_LINGER)
                {
                    LOG_NG << "resuming from loaded linger state...\n";
                    //as carryover information is stored in the snapshot, we have to re-store it after loading a linger state
                    saved_game_.set_snapshot(config());
                    if(!is_observer()) {
                        persist_.end_transaction();
                    }
                    return VICTORY;
                }

                break;
                //END CASES
            } // END SWITCH
        } //end if
    } catch(const game::load_game_exception &) {
        // Loading a new game is effectively a quit.
        //
        if ( game::load_game_exception::game != "" ) {
            saved_game_ = saved_game();
        }
        throw;
    } catch(network::error& e) {
        bool disconnect = false;
        if(e.socket) {
            e.disconnect();
            disconnect = true;
        }

        savegame::ingame_savegame save(saved_game_, *gui_, to_config(), preferences::save_compression_format());
        save.save_game_interactive(gui_->video(), _("A network disconnection has occurred, and the game cannot continue. Do you want to save the game?"), gui::YES_NO);
        if(disconnect) {
            throw network::error();
        } else {
            return QUIT;
        }
    }

    return QUIT;
}
//make only one side move
possible_end_play_signal replay_controller::play_side() {

	DBG_REPLAY << "Status turn number: " << turn() << "\n";
	DBG_REPLAY << "Replay_Controller turn number: " << current_turn_ << "\n";
	DBG_REPLAY << "Player number: " << player_number_ << "\n";

	// If a side is empty skip over it.
	if (!current_team().is_empty()) {
		statistics::reset_turn_stats(current_team().save_id());

		possible_end_play_signal signal = play_controller::init_side(true);

		if (signal) {
			switch (boost::apply_visitor(get_signal_type(), *signal) ) {
				case END_TURN:
					return signal;
				case END_LEVEL:
					//VICTORY/DEFEAT end_level_exception shall not return to title screen
					LEVEL_RESULT res = boost::apply_visitor(get_result(), *signal);
					if ( res != VICTORY && res != DEFEAT ) return signal;
			}
		}

		DBG_REPLAY << "doing replay " << player_number_ << "\n";
		// if have reached the end we don't want to execute finish_side_turn and finish_turn
		// becasue we might not have enough data to execute them (like advancements during turn_end for example)

		try {
			if(do_replay() != REPLAY_FOUND_END_TURN) {
				// We reached the end of teh replay without finding and end turn tag.
				return boost::none;
			}
		} catch(end_level_exception& e){
			//VICTORY/DEFEAT end_level_exception shall not return to title screen
			if (e.result != VICTORY && e.result != DEFEAT) {
				return possible_end_play_signal(e.to_struct());
			}
		} catch (end_turn_exception & e) {
			return possible_end_play_signal(e.to_struct());
		}

		finish_side_turn();
	}

	player_number_++;

	if (static_cast<size_t>(player_number_) > gamestate_.board_.teams().size()) {
		//during the orginal game player_number_ would also be gamestate_.board_.teams().size(),
		player_number_ = gamestate_.board_.teams().size();
		finish_turn();
		gamestate_.tod_manager_.next_turn(*resources::gamedata);
		it_is_a_new_turn_ = true;
		player_number_ = 1;
		current_turn_++;
		gui_->new_turn();
	}

	// This is necessary for replays in order to show possible movements.
	gamestate_.board_.new_turn(player_number_);

	update_teams();
	update_gui();

	return boost::none;
}