Skip to content

Commit

Permalink
Merge pull request #385 from cbeck88/random_faction_mode
Browse files Browse the repository at this point in the history
Random faction mode
  • Loading branch information
cbeck88 committed Mar 13, 2015
2 parents 738070a + 8fffc85 commit 4f70998
Show file tree
Hide file tree
Showing 13 changed files with 146 additions and 24 deletions.
8 changes: 8 additions & 0 deletions src/game_initialization/configure_engine.cpp
@@ -1,6 +1,7 @@
#include "configure_engine.hpp"
#include "formula_string_utils.hpp"
#include "game_config_manager.hpp"
#include "mp_game_settings.hpp"
#include "settings.hpp"

#include <boost/foreach.hpp>
Expand Down Expand Up @@ -54,6 +55,7 @@ void configure_engine::set_default_values() {
set_random_start_time(random_start_time_default());
set_fog_game(fog_game_default());
set_shroud_game(shroud_game_default());
set_random_faction_mode(random_faction_mode_default());
}

bool configure_engine::force_lock_settings() const {
Expand All @@ -77,6 +79,7 @@ bool configure_engine::fog_game() const { return parameters_.fog_game; }
bool configure_engine::shroud_game() const { return parameters_.shroud_game; }
bool configure_engine::allow_observers() const { return parameters_.allow_observers; }
bool configure_engine::shuffle_sides() const { return parameters_.shuffle_sides; }
mp_game_settings::RANDOM_FACTION_MODE configure_engine::random_faction_mode() const { return parameters_.random_faction_mode; }
const config& configure_engine::options() const { return parameters_.options; }

void configure_engine::set_game_name(std::string val) { parameters_.name = val; }
Expand All @@ -96,6 +99,7 @@ void configure_engine::set_shroud_game(bool val) { parameters_.shroud_game = val
void configure_engine::set_allow_observers(bool val) { parameters_.allow_observers = val; }
void configure_engine::set_oos_debug(bool val) { state_.classification().oos_debug = val; }
void configure_engine::set_shuffle_sides(bool val) { parameters_.shuffle_sides = val; }
void configure_engine::set_random_faction_mode(mp_game_settings::RANDOM_FACTION_MODE val) { parameters_.random_faction_mode = val;}
void configure_engine::set_options(const config& cfg) { parameters_.options = cfg; }

void configure_engine::set_scenario(size_t scenario_num) {
Expand Down Expand Up @@ -180,6 +184,10 @@ bool configure_engine::allow_observers_default() const {
bool configure_engine::shuffle_sides_default() const {
return preferences::shuffle_sides();
}
mp_game_settings::RANDOM_FACTION_MODE configure_engine::random_faction_mode_default() const {
return mp_game_settings::string_to_RANDOM_FACTION_MODE_default(preferences::random_faction_mode(), mp_game_settings::DEFAULT);
}

const config& configure_engine::options_default() const {
return preferences::options();
}
Expand Down
4 changes: 4 additions & 0 deletions src/game_initialization/configure_engine.hpp
Expand Up @@ -17,6 +17,7 @@

#include "gettext.hpp"
#include "game_preferences.hpp"
#include "mp_game_settings.hpp"
#include "saved_game.hpp"

namespace ng {
Expand Down Expand Up @@ -55,6 +56,7 @@ class configure_engine
bool shroud_game() const;
bool allow_observers() const;
bool shuffle_sides() const;
mp_game_settings::RANDOM_FACTION_MODE random_faction_mode() const;
const config& options() const;

// setter methods
Expand All @@ -75,6 +77,7 @@ class configure_engine
void set_allow_observers(bool val);
void set_oos_debug(bool val);
void set_shuffle_sides(bool val);
void set_random_faction_mode(mp_game_settings::RANDOM_FACTION_MODE val);
void set_options(const config& cfg);

void set_scenario(size_t scenario_num);
Expand All @@ -97,6 +100,7 @@ class configure_engine
bool shroud_game_default() const;
bool allow_observers_default() const;
bool shuffle_sides_default() const;
mp_game_settings::RANDOM_FACTION_MODE random_faction_mode_default() const;
const config& options_default() const;

// parameters_ accessor
Expand Down
39 changes: 32 additions & 7 deletions src/game_initialization/connect_engine.cpp
Expand Up @@ -21,6 +21,7 @@
#include "map.hpp"
#include "multiplayer_ui.hpp"
#include "mp_game_utils.hpp"
#include "mt_rng.hpp"
#include "tod_manager.hpp"

#include <boost/foreach.hpp>
Expand Down Expand Up @@ -391,10 +392,32 @@ void connect_engine::start_game(LOAD_USERS load_users)
{
DBG_MP << "starting a new game" << std::endl;

// Resolves the "random faction", "random gender" and "random message"
// Must be done before shuffle sides, or some cases will cause errors
// Resolves the "random faction", "random gender" and "random message"
// Must be done before shuffle sides, or some cases will cause errors
rand_rng::mt_rng rng; // Make an RNG for all the shuffling and random faction operations
BOOST_FOREACH(side_engine_ptr side, side_engines_) {
side->resolve_random();
std::vector<std::string> avoid_faction_ids;

// If we aren't resolving random factions independently at random, calculate which factions should not appear for this side.
if (params_.random_faction_mode != mp_game_settings::DEFAULT) {
BOOST_FOREACH(side_engine_ptr side2, side_engines_) {
if (!side2->flg().is_random_faction()) {
switch(params_.random_faction_mode) {
case mp_game_settings::NO_MIRROR:
avoid_faction_ids.push_back(side2->flg().current_faction()["id"].str());
break;
case mp_game_settings::NO_ALLY_MIRROR:
if (side2->team() == side->team()) {// TODO: When the connect engines are fixed to allow multiple teams, this should be changed to "if side1 and side2 are allied, i.e. their list of teams has nonempty intersection"
avoid_faction_ids.push_back(side2->flg().current_faction()["id"].str());
}
break;
default:
break; // assert(false);
}
}
}
}
side->resolve_random(rng, avoid_faction_ids);
}

// Shuffle sides (check settings and if it is a re-loaded game).
Expand All @@ -413,7 +436,7 @@ void connect_engine::start_game(LOAD_USERS load_users)
// Fisher-Yates shuffle.
for (int i = playable_sides.size(); i > 1; i--)
{
int j_side = playable_sides[rand() % i];
int j_side = playable_sides[rng.get_next_random() % i];
int i_side = playable_sides[i - 1];

if (i_side == j_side) continue; //nothing to swap
Expand Down Expand Up @@ -464,6 +487,8 @@ void connect_engine::start_game_commandline(

typedef boost::tuple<unsigned int, std::string> mp_option;

rand_rng::mt_rng rng;

unsigned num = 0;
BOOST_FOREACH(side_engine_ptr side, side_engines_) {
num++;
Expand Down Expand Up @@ -514,7 +539,7 @@ void connect_engine::start_game_commandline(

// Finally, resolve "random faction",
// "random gender" and "random message", if any remains unresolved.
side->resolve_random();
side->resolve_random(rng);
} // end top-level loop

update_and_send_diff(true);
Expand Down Expand Up @@ -1168,15 +1193,15 @@ bool side_engine::swap_sides_on_drop_target(const unsigned drop_target) {
return true;
}

void side_engine::resolve_random()
void side_engine::resolve_random(rand_rng::mt_rng & rng, const std::vector<std::string> & avoid_faction_ids)
{
if (parent_.params_.saved_game) {
return;
}

chose_random_ = flg_.is_random_faction();

flg_.resolve_random();
flg_.resolve_random(rng, avoid_faction_ids);

LOG_MP << "side " << (index_ + 1) << ": faction=" <<
(flg_.current_faction())["name"] << ", leader=" <<
Expand Down
4 changes: 3 additions & 1 deletion src/game_initialization/connect_engine.hpp
Expand Up @@ -22,6 +22,8 @@
#include "saved_game.hpp"
#include <boost/scoped_ptr.hpp>

namespace rand_rng { class mt_rng; }

namespace ng {

enum controller {
Expand Down Expand Up @@ -157,7 +159,7 @@ class side_engine

bool swap_sides_on_drop_target(const unsigned drop_target);

void resolve_random();
void resolve_random( rand_rng::mt_rng & rng, const std::vector<std::string> & avoid_faction_ids = std::vector<std::string>());

// Resets this side to its default state.
void reset();
Expand Down
33 changes: 28 additions & 5 deletions src/game_initialization/flg_manager.cpp
Expand Up @@ -16,6 +16,7 @@
#include "config.hpp"
#include "formula_string_utils.hpp"
#include "gettext.hpp"
#include "mt_rng.hpp"
#include "unit_types.hpp"
#include "wml_separators.hpp"

Expand Down Expand Up @@ -206,7 +207,16 @@ bool flg_manager::is_random_faction()
return (*current_faction_)["random_faction"].to_bool();
}

void flg_manager::resolve_random() {
// When we use a random mode like "no mirror", "no ally mirror", the list of
// faction ids to avoid is past as an argument.
// It may be that for some scenario configuration, a strict no mirror
// assignment is not possible, because there are too many sides, or some users
// have forced their faction choices to be matching, etc.
// In that case we gracefully continue by ignoring the no mirror rule and
// assigning as we would have if it were off.
// If there is still no options we throw a config error because it means the
// era is misconfigured.
void flg_manager::resolve_random(rand_rng::mt_rng & rng, const std::vector<std::string> & avoid) {
if (is_random_faction()) {
std::vector<std::string> faction_choices, faction_excepts;

Expand All @@ -223,6 +233,7 @@ void flg_manager::resolve_random() {
// Builds the list of factions eligible for choice
// (non-random factions).
std::vector<int> nonrandom_sides;
std::vector<int> fallback_nonrandom_sides;
int num = -1;
BOOST_FOREACH(const config* i, available_factions_) {
++num;
Expand All @@ -241,16 +252,28 @@ void flg_manager::resolve_random() {
continue;
}

nonrandom_sides.push_back(num);
fallback_nonrandom_sides.push_back(num); //This side is consistent with this random faction, remember as a fallback.

if (!avoid.empty() &&
std::find(avoid.begin(), avoid.end(),
faction_id) != avoid.end()) {
continue;
}

nonrandom_sides.push_back(num); //This side is consistent with this random faction, and the avoid factions argument.
}
}

if (nonrandom_sides.empty()) {
nonrandom_sides = fallback_nonrandom_sides; // There was no way to succeed consistently with the avoid factions argument, so ignore it as a fallback.
}

if (nonrandom_sides.empty()) {
throw config::error(_("Only random sides in the current era."));
}

const int faction_index =
nonrandom_sides[rand() % nonrandom_sides.size()];
nonrandom_sides[rng.get_next_random() % nonrandom_sides.size()];
current_faction_ = available_factions_[faction_index];

update_available_leaders();
Expand All @@ -275,7 +298,7 @@ void flg_manager::resolve_random() {
"Unable to find a leader type for faction $faction",
i18n_symbols));
} else {
const int lchoice = rand() % nonrandom_leaders.size();
const int lchoice = rng.get_next_random() % nonrandom_leaders.size();
current_leader_ = nonrandom_leaders[lchoice];

update_available_genders();
Expand All @@ -294,7 +317,7 @@ void flg_manager::resolve_random() {
}
}

const int gchoice = rand() % nonrandom_genders.size();
const int gchoice = rng.get_next_random() % nonrandom_genders.size();
current_gender_ = nonrandom_genders[gchoice];
} else {
utils::string_map i18n_symbols;
Expand Down
3 changes: 2 additions & 1 deletion src/game_initialization/flg_manager.hpp
Expand Up @@ -20,6 +20,7 @@
#include <vector>

class config;
namespace rand_rng { class mt_rng; }

namespace ng {

Expand Down Expand Up @@ -55,7 +56,7 @@ class flg_manager
void reset_gender_combo(gui::combo& combo_gender) const;

bool is_random_faction();
void resolve_random();
void resolve_random(rand_rng::mt_rng & rng, const std::vector<std::string> & avoid); //Second Argument is a list of faction ids we don't want to match, used to implement random faction modes. If it is not possible to resolve then we just proceed anyways rather than give an error.

// Picks the first faction with the greater amount of data
// matching the criteria.
Expand Down
38 changes: 32 additions & 6 deletions src/game_initialization/multiplayer_configure.cpp
Expand Up @@ -26,6 +26,7 @@
#include "gui/dialogs/mp_create_game_set_password.hpp"
#include "gui/dialogs/transient_message.hpp"
#include "minimap.hpp"
#include "mp_game_settings.hpp"
#include "multiplayer_configure.hpp"
#include "filesystem.hpp"
#include "log.hpp"
Expand All @@ -35,6 +36,7 @@
#include "wml_separators.hpp"
#include "formula_string_utils.hpp"

#include <boost/assign/list_of.hpp>
#include <boost/bind.hpp>
#include <boost/foreach.hpp>

Expand Down Expand Up @@ -82,6 +84,8 @@ configure::configure(game_display& disp, const config &cfg, chat& c, config& gam
observers_game_(disp.video(), _("Observers"), gui::button::TYPE_CHECK),
oos_debug_(disp.video(), _("Debug OOS"), gui::button::TYPE_CHECK),
shuffle_sides_(disp.video(), _("Shuffle sides"), gui::button::TYPE_CHECK),
random_faction_mode_label_(disp.video(), _("Random Factions:"), font::SIZE_SMALL, font::LOBBY_COLOR),
random_faction_mode_(disp, std::vector<std::string>()),
cancel_game_(disp.video(), _("Back")),
launch_game_(disp.video(), _("OK")),
password_button_(disp.video(), _("Set Password...")),
Expand Down Expand Up @@ -183,6 +187,17 @@ configure::configure(game_display& disp, const config &cfg, chat& c, config& gam
shuffle_sides_.set_check(engine_.shuffle_sides_default());
shuffle_sides_.set_help_string(_("Assign sides to players at random"));

random_faction_mode_label_.set_help_string(_("Allow for mirror matchups when random factions are chosen"));

std::vector<std::string> translated_modes;
for(size_t i = 0; i < mp_game_settings::RANDOM_FACTION_MODE_COUNT; ++i) {
std::string mode_str = mp_game_settings::RANDOM_FACTION_MODE_to_string(static_cast<mp_game_settings::RANDOM_FACTION_MODE> (i));
translated_modes.push_back(translation::gettext(mode_str.c_str()));
}
random_faction_mode_.set_items(translated_modes);
random_faction_mode_.set_selected(engine_.random_faction_mode());
random_faction_mode_.set_help_string(_("Independent: Random factions assigned independently\nNo Mirror: No two players will get the same faction\nNo Ally Mirror: No two allied players will get the same faction"));

#if 0
// The possible vision settings
std::vector<std::string> vision_types;
Expand Down Expand Up @@ -229,6 +244,7 @@ configure::~configure()
// Save values for next game
DBG_MP << "storing parameter values in preferences" << std::endl;
preferences::set_shuffle_sides(engine_.shuffle_sides());
preferences::set_random_faction_mode(mp_game_settings::RANDOM_FACTION_MODE_to_string(engine_.random_faction_mode()));
preferences::set_use_map_settings(engine_.use_map_settings());
preferences::set_countdown(engine_.mp_countdown());
preferences::set_countdown_init_time(engine_.mp_countdown_init_time());
Expand Down Expand Up @@ -292,6 +308,7 @@ const mp_game_settings& configure::get_parameters()
engine_.set_allow_observers(observers_game_.checked());
engine_.set_oos_debug(oos_debug_.checked());
engine_.set_shuffle_sides(shuffle_sides_.checked());
engine_.set_random_faction_mode(static_cast<mp_game_settings::RANDOM_FACTION_MODE>(random_faction_mode_.selected()));

engine_.set_options(options_manager_.get_values());

Expand Down Expand Up @@ -483,6 +500,8 @@ void configure::hide_children(bool hide)
observers_game_.hide(hide);
oos_debug_.hide(hide);
shuffle_sides_.hide(hide);
random_faction_mode_label_.hide(hide);
random_faction_mode_.hide(hide);
cancel_game_.hide(hide);
launch_game_.hide(hide);

Expand Down Expand Up @@ -534,24 +553,31 @@ void configure::layout_children(const SDL_Rect& rect)

int slider_width = options_pane_left_.width() - 40;

int xpos_left = 0;
int ypos_left = 0;
unsigned int xpos_left = 0;
unsigned int ypos_left = 0;

ypos_left += 2 * border_size;
options_pane_left_.add_widget(&observers_game_, xpos_left, ypos_left);
options_pane_left_.add_widget(&shuffle_sides_,
options_pane_left_.add_widget(&shuffle_sides_, xpos_left, ypos_left);
options_pane_left_.add_widget(&observers_game_,
xpos_left + (options_pane_left_.width() - xpos_left) / 2 + border_size, ypos_left);
ypos_left += shuffle_sides_.height() + border_size;

options_pane_left_.add_widget(&countdown_game_, xpos_left, ypos_left);
options_pane_left_.add_widget(&random_faction_mode_label_, xpos_left, ypos_left);
xpos_left += random_faction_mode_label_.width() + border_size;

options_pane_left_.add_widget(&random_faction_mode_, xpos_left, ypos_left);
xpos_left += random_faction_mode_.width() + border_size;

if(!local_players_only_) {
options_pane_left_.add_widget(&password_button_,
(ca.x + first_column_width / 2) - 40, ypos_left);
std::max(xpos_left + 2* border_size, (options_pane_left_.width() / 2) + border_size), ypos_left);
} else {
password_button_.hide(true);
}
xpos_left = 0;
ypos_left += random_faction_mode_.height() + border_size;

options_pane_left_.add_widget(&countdown_game_, xpos_left, ypos_left);
ypos_left += countdown_game_.height() + border_size;

options_pane_left_.add_widget(&countdown_init_time_label_, xpos_left, ypos_left );
Expand Down
3 changes: 3 additions & 0 deletions src/game_initialization/multiplayer_configure.hpp
Expand Up @@ -79,6 +79,9 @@ class configure : public mp::ui
gui::button observers_game_;
gui::button oos_debug_;
gui::button shuffle_sides_;
gui::label random_faction_mode_label_;
gui::combo random_faction_mode_;

gui::button cancel_game_;
gui::button launch_game_;
gui::button password_button_;
Expand Down

0 comments on commit 4f70998

Please sign in to comment.