/** * Constructor from a config. */ clearer_info::clearer_info(const config & cfg) : underlying_id(cfg["underlying_id"].to_size_t()), sight_range(cfg["vision"].to_int()), slowed(cfg.child_or_empty("status")["slowed"].to_bool()), costs(cfg.child_or_empty("vision_costs")) { }
/** * Constructor from a config */ movetype::movetype(const config & cfg) : movement_(cfg.child_or_empty("movement_costs"), nullptr, &vision_), // This is not access before initialization; the address is merely stored at this point. vision_(cfg.child_or_empty("vision_costs"), &movement_, &jamming_), // This is not access before initialization; the address is merely stored at this point. jamming_(cfg.child_or_empty("jamming_costs"), &vision_, nullptr), defense_(cfg.child_or_empty("defense")), resist_(cfg.child_or_empty("resistance")), flying_(cfg["flies"].to_bool(false)) { }
/** * Constructor for when read from a saved config. * This is the reverse of to_config() and corresponds to reading [menu_item]. * Handlers are not initialized. */ wml_menu_item::wml_menu_item(const std::string& id, const config & cfg) : item_id_(id), event_name_(make_item_name(id)), hotkey_id_(make_item_hotkey(id)), image_(cfg["image"].str()), description_(cfg["description"].t_str()), needs_select_(cfg["needs_select"].to_bool(false)), show_if_(cfg.child_or_empty("show_if"), true), filter_location_(cfg.child_or_empty("filter_location"), true), command_(cfg.child_or_empty("command")), default_hotkey_(cfg.child_or_empty("default_hotkey")), use_hotkey_(cfg["use_hotkey"].to_bool(true)), use_wml_menu_(cfg["use_hotkey"].str() != "only") { }
carryover_info::carryover_info(const config& cfg, bool from_snpashot) : carryover_sides_() , variables_(cfg.child_or_empty("variables")) , rng_(cfg) , wml_menu_items_() , next_scenario_(cfg["next_scenario"]) , next_underlying_unit_id_(cfg["next_underlying_unit_id"].to_int(0)) { for(const config& side : cfg.child_range("side")) { if(side["lost"].to_bool(false) || !side["persistent"].to_bool(true) || side["save_id"].empty()) { //this shouldn't happen outside a snpshot. if(!from_snpashot) { ERR_NG << "found invalid carryover data in saved game, lost='" << side["lost"] << "' persistent='" << side["persistent"] << "' save_id='" << side["save_id"] << "'\n"; } continue; } this->carryover_sides_.emplace_back(side); } for(const config& item : cfg.child_range("menu_item")) { wml_menu_items_.push_back(new config(item)); } }
gamemap::gamemap(const tdata_cache& tdata, const config& level): tiles_(1), tdata_(tdata), villages_(), borderCache_(), terrainFrequencyCache_(), w_(-1), h_(-1), total_width_(0), total_height_(0), border_size_(gamemap::default_border) { DBG_G << "loading map: '" << level.debug() << "'\n"; const config& map_child = level.child_or_empty("map"); if (map_child.empty()) { const std::string& map_data = level["map_data"]; if (!map_data.empty()) { read(map_data); } else { w_ = 0; h_ = 0; total_width_ = 0; total_height_ = 0; } } else { read(map_child["data"], true); } }
attack_type::attack_type(const config& cfg) : self_loc_(), other_loc_(), is_attacker_(false), other_attack_(nullptr), description_(cfg["description"].t_str()), id_(cfg["name"]), type_(cfg["type"]), icon_(cfg["icon"]), range_(cfg["range"]), min_range_(cfg["min_range"].to_int(1)), max_range_(cfg["max_range"].to_int(1)), damage_(cfg["damage"]), num_attacks_(cfg["number"]), attack_weight_(cfg["attack_weight"].to_double(1.0)), defense_weight_(cfg["defense_weight"].to_double(1.0)), accuracy_(cfg["accuracy"]), movement_used_(cfg["movement_used"].to_int(100000)), parry_(cfg["parry"]), specials_(cfg.child_or_empty("specials")) { if (description_.empty()) description_ = translation::egettext(id_.c_str()); if(icon_.empty()){ if (id_ != "") icon_ = "attacks/" + id_ + ".png"; else icon_ = "attacks/blank-attack.png"; } }
gamemap::gamemap(const config& cfg, const config& level): tiles_(1), terrainList_(), tcodeToTerrain_(), villages_(), borderCache_(), terrainFrequencyCache_(), w_(-1), h_(-1), total_width_(0), total_height_(0), border_size_(gamemap::SINGLE_TILE_BORDER), usage_(IS_MAP) { DBG_G << "loading map: '" << level.debug() << "'\n"; const config::const_child_itors &terrains = cfg.child_range("terrain_type"); create_terrain_maps(terrains, terrainList_, tcodeToTerrain_); const config& map_child = level.child_or_empty("map"); if (map_child.empty()) { const std::string& map_data = level["map_data"]; if (!map_data.empty()) { read(map_data); } else { w_ = 0; h_ = 0; total_width_ = 0; total_height_ = 0; } } else { read(map_child["data"], true, map_child["border_size"], map_child["usage"]); } }
shroud_clearing_action(const config& cfg) : route() , view_info(cfg.child_or_empty("unit")) , original_village_owner(cfg["village_owner"].to_int()) , take_village_timebonus(cfg["village_timebonus"].to_bool()) { read_locations(cfg, route); }
game_data::game_data(const config& level) : variable_set() , scoped_variables() , last_selected(map_location::null_location()) , rng_(level) , variables_(level.child_or_empty("variables")) , phase_(INITIAL) , can_end_turn_(level["can_end_turn"].to_bool(true)) , next_scenario_(level["next_scenario"]) { }
/** * Note that initially we get access only to readonly context (engine is created rather early, when there's no way to move/attack. * We inject full ai_context later. */ engine_lua::engine_lua( readonly_context &context, const config &cfg ) : engine(context,cfg) , code_(cfg["code"]) , lua_ai_context_(resources::lua_kernel->create_lua_ai_context( cfg["code"].str().c_str(), this)) { name_ = "lua"; config data(cfg.child_or_empty("data")); lua_ai_context_->set_persistent_data(data); }
/** * Note that initially we get access only to readonly context (engine is created rather early, when there's no way to move/attack. * We inject full ai_context later. */ engine_lua::engine_lua( readonly_context &context, const config &cfg ) : engine(context,cfg) , code_(cfg["code"]) , lua_ai_context_(resources::lua_kernel->create_lua_ai_context( get_engine_code(cfg).c_str(), this)) { name_ = "lua"; config data(cfg.child_or_empty("data")); if (lua_ai_context_) { // The context might be NULL if the config contains errors lua_ai_context_->set_persistent_data(data); } }
turn_info::PROCESS_DATA_RESULT turn_info::process_network_data(const config& cfg, network::connection from, std::deque<config>& backlog, bool skip_replay) { if (const config &rnd_seed = cfg.child("random_seed")) { rand_rng::set_seed(rnd_seed["seed"]); //may call a callback function, see rand_rng::set_seed_callback } if (const config &msg = cfg.child("message")) { resources::screen->add_chat_message(time(NULL), msg["sender"], msg["side"], msg["message"], events::chat_handler::MESSAGE_PUBLIC, preferences::message_bell()); } if (const config &msg = cfg.child("whisper") /*&& is_observer()*/) { resources::screen->add_chat_message(time(NULL), "whisper: " + msg["sender"].str(), 0, msg["message"], events::chat_handler::MESSAGE_PRIVATE, preferences::message_bell()); } BOOST_FOREACH(const config &ob, cfg.child_range("observer")) { resources::screen->add_observer(ob["name"]); } BOOST_FOREACH(const config &ob, cfg.child_range("observer_quit")) { resources::screen->remove_observer(ob["name"]); } if (cfg.child("leave_game")) { throw network::error(""); } bool turn_end = false; config::const_child_itors turns = cfg.child_range("turn"); if (turns.first != turns.second && from != network::null_connection) { //forward the data to other peers network::send_data_all_except(cfg, from); } const config& change = cfg.child_or_empty("change_controller"); const std::string& side_drop = cfg["side_drop"].str(); BOOST_FOREACH(const config &t, turns) { handle_turn(turn_end, t, skip_replay, backlog); }
carryover::carryover(const config& side) : add_(!side["carryover_add"].empty() ? side["carryover_add"].to_bool() : side["add"].to_bool()) , current_player_(side["current_player"]) , gold_(!side["carryover_gold"].empty() ? side["carryover_gold"].to_int() : side["gold"].to_int()) // if we load it from a snapshot we need to read the recruits from "recruits" and not from "previous_recruits". , previous_recruits_(side.has_attribute("recruit") ? utils::set_split(side["recruit"]) :utils::set_split(side["previous_recruits"])) , recall_list_() , save_id_(side["save_id"]) , variables_(side.child_or_empty("variables")) { for(const config& u : side.child_range("unit")) { recall_list_.push_back(u); config& u_back = recall_list_.back(); u_back.remove_attributes("side", "goto_x", "goto_y", "x", "y", "hidden"); } }
/** * Note that initially we get access only to readonly context (engine is created rather early, when there's no way to move/attack. * We inject full ai_context later. */ engine_lua::engine_lua( readonly_context &context, const config &cfg ) : engine(context,cfg) , code_(get_engine_code(cfg)) , lua_ai_context_(resources::lua_kernel->create_lua_ai_context( get_engine_code(cfg).c_str(), this)) { name_ = "lua"; config data(cfg.child_or_empty("data")); config args(cfg.child_or_empty("args")); if (lua_ai_context_) { // The context might be nullptr if the config contains errors lua_ai_context_->set_persistent_data(data); lua_ai_context_->set_arguments(args); lua_ai_context_->update_state(); } }
mp_game_settings::mp_game_settings(const config& cfg) : name(cfg["scenario"].str()) , password() , hash(cfg["hash"].str()) , mp_era(cfg["mp_era"].str()) , mp_era_addon_id(cfg["mp_era_addon_id"].str()) , mp_scenario(cfg["mp_scenario"].str()) , mp_scenario_name(cfg["mp_scenario_name"].str()) , mp_campaign(cfg["mp_campaign"].str()) , active_mods(utils::split(cfg["active_mods"], ',')) , side_users(utils::map_split(cfg["side_users"])) , show_connect(cfg["show_connect"].to_bool(true)) , num_turns(cfg["mp_num_turns"]) , village_gold(cfg["mp_village_gold"]) , village_support(cfg["mp_village_support"]) , xp_modifier(cfg["experience_modifier"].to_int(100)) , mp_countdown_init_time(cfg["mp_countdown_init_time"]) , mp_countdown_reservoir_time(cfg["mp_countdown_reservoir_time"]) , mp_countdown_turn_bonus(cfg["mp_countdown_turn_bonus"]) , mp_countdown_action_bonus(cfg["mp_countdown_action_bonus"]) , mp_countdown(cfg["mp_countdown"].to_bool()) , use_map_settings(cfg["mp_use_map_settings"].to_bool()) , random_start_time(cfg["mp_random_start_time"].to_bool()) , fog_game(cfg["mp_fog"].to_bool()) , shroud_game(cfg["mp_shroud"].to_bool()) , allow_observers(cfg["observer"].to_bool()) , registered_users_only(cfg["registered_users_only"].to_bool()) , shuffle_sides(cfg["shuffle_sides"].to_bool()) , saved_game(cfg["savegame"].to_bool()) , random_faction_mode(cfg["random_faction_mode"].to_enum<RANDOM_FACTION_MODE>(RANDOM_FACTION_MODE::DEFAULT)) , options(cfg.child_or_empty("options")) , addons() { for (const config & a : cfg.child_range("addon")) { if (!a["id"].empty()) { addons.insert(std::make_pair(a["id"].str(), addon_version_info(a))); } } }
carryover_info::carryover_info(const config& cfg, bool from_snpashot) : carryover_sides_() , variables_(cfg.child_or_empty("variables")) , rng_(cfg) , wml_menu_items_() , next_scenario_(cfg["next_scenario"]) , next_underlying_unit_id_(cfg["next_underlying_unit_id"].to_int(0)) { for(const config& side : cfg.child_range("side")) { if(side["lost"].to_bool(false) || !side["persistent"].to_bool(true)) { //this shouldnt happen outside a snpshot. assert(from_snpashot); continue; } this->carryover_sides_.push_back(carryover(side)); } for(const config& item : cfg.child_range("menu_item")) { wml_menu_items_.push_back(new config(item)); } }
game_info::game_info(const config& game, const config& game_config) : mini_map() , id(game["id"]) , map_data(game["map_data"]) , name(game["name"]) , scenario() , remote_scenario(false) , map_info() , map_size_info() , era() , era_short() , gold(game["mp_village_gold"]) , support(game["mp_village_support"]) , xp(game["experience_modifier"].str() + "%") , vision() , status() , time_limit() , vacant_slots(lexical_cast_default<int>(game["slots"], 0)) // Can't use to_int() here. , current_turn(0) , reloaded(game["savegame"].to_bool()) , started(false) , fog(game["mp_fog"].to_bool()) , shroud(game["mp_shroud"].to_bool()) , observers(game["observer"].to_bool(true)) , shuffle_sides(game["shuffle_sides"].to_bool(true)) , use_map_settings(game["mp_use_map_settings"].to_bool()) , verified(true) , password_required(game["password"].to_bool()) , have_era(true) , have_all_mods(true) , has_friends(false) , has_ignored(false) , display_status(NEW) { std::string turn = game["turn"]; if(!game["mp_era"].empty()) { const config& era_cfg = game_config.find_child("era", "id", game["mp_era"]); utils::string_map symbols; symbols["era_id"] = game["mp_era"]; if(era_cfg) { era = era_cfg["name"].str(); era_short = era_cfg["short_name"].str(); if(era_short.empty()) { era_short = make_short_name(era); } } else { have_era = !game["require_era"].to_bool(true); era = vgettext("Unknown era: $era_id", symbols); era_short = "?" + make_short_name(era); verified = false; } } else { era = _("Unknown era"); era_short = "??"; verified = false; } map_info = era; if(!game.child_or_empty("modification").empty()) { BOOST_FOREACH(const config &cfg, game.child_range("modification")) { if (cfg["require_modification"].to_bool(false)) { const config &mod = game_config.find_child("modification", "id", cfg["id"]); if (!mod) { have_all_mods = false; break; } } } }
shroud_clearing_action(const config& cfg) : route() , view_info(cfg.child_or_empty("unit")) { read_locations(cfg, route); }
game_info::game_info(const config& game, const config& game_config, const std::vector<std::string>& installed_addons) : mini_map() , id(game["id"]) , map_data(game["map_data"]) , name(game["name"]) , scenario() , remote_scenario(false) , map_info() , map_size_info() , era() , era_short() , gold(game["mp_village_gold"]) , support(game["mp_village_support"]) , xp(game["experience_modifier"].str() + "%") , vision() , status() , time_limit() , vacant_slots(lexical_cast_default<int>(game["slots"], 0)) // Can't use to_int() here. , current_turn(0) , reloaded(game["savegame"].to_bool()) , started(false) , fog(game["mp_fog"].to_bool()) , shroud(game["mp_shroud"].to_bool()) , observers(game["observer"].to_bool(true)) , shuffle_sides(game["shuffle_sides"].to_bool(true)) , use_map_settings(game["mp_use_map_settings"].to_bool()) , registered_users_only(game["registered_users_only"].to_bool()) , verified(true) , password_required(game["password"].to_bool()) , have_era(true) , have_all_mods(true) , has_friends(false) , has_ignored(false) , display_status(NEW) , required_addons() , addons_outcome(SATISFIED) { const auto parse_requirements = [&](const config& c, const std::string& id_key) { if(c.has_attribute(id_key)) { if(std::find(installed_addons.begin(), installed_addons.end(), c[id_key].str()) == installed_addons.end()) { required_addon r; r.addon_id = c[id_key].str(); r.outcome = NEED_DOWNLOAD; r.message = vgettext("Missing addon: $id", {{"id", c[id_key].str()}}); required_addons.push_back(r); if(addons_outcome == SATISFIED) { addons_outcome = NEED_DOWNLOAD; } } } }; for(const config& addon : game.child_range("addon")) { parse_requirements(addon, "id"); } /* * Modifications have a different format than addons. The id and addon_id are keys sent by the * server, so we have to parse them separately here and add them to the required_addons vector. */ for(const config& mod : game.child_range("modification")) { parse_requirements(mod, "addon_id"); } std::string turn = game["turn"]; if(!game["mp_era"].empty()) { const config& era_cfg = game_config.find_child("era", "id", game["mp_era"]); if(era_cfg) { era = era_cfg["name"].str(); era_short = era_cfg["short_name"].str(); if(era_short.empty()) { era_short = make_short_name(era); } ADDON_REQ result = check_addon_version_compatibility(era_cfg, game); addons_outcome = std::max(addons_outcome, result); // Elevate to most severe error level encountered so far } else { have_era = !game["require_era"].to_bool(true); era = vgettext("Unknown era: $era_id", {{"era_id", game["mp_era_addon_id"].str()}}); era_short = make_short_name(era); verified = false; addons_outcome = NEED_DOWNLOAD; } } else { era = _("Unknown era"); era_short = "??"; verified = false; } std::stringstream info_stream; info_stream << era; if(!game.child_or_empty("modification").empty()) { for(const config& cfg : game.child_range("modification")) { if(const config& mod = game_config.find_child("modification", "id", cfg["id"])) { mod_info += (mod_info.empty() ? "" : ", ") + mod["name"].str(); if(cfg["require_modification"].to_bool(false)) { ADDON_REQ result = check_addon_version_compatibility(mod, game); addons_outcome = std::max(addons_outcome, result); // Elevate to most severe error level encountered so far } } else { mod_info += (mod_info.empty() ? "" : ", ") + cfg["addon_id"].str(); if(cfg["require_modification"].to_bool(false)) { have_all_mods = false; mod_info += " " + _("(missing)"); addons_outcome = NEED_DOWNLOAD; } } } } if(map_data.empty()) { map_data = filesystem::read_map(game["mp_scenario"]); } if(map_data.empty()) { info_stream << " — ??×??"; } else { try { gamemap map(std::make_shared<terrain_type_data>(game_config), map_data); // mini_map = image::getMinimap(minimap_size_, minimap_size_, map, // 0); std::ostringstream msi; msi << map.w() << font::unicode_multiplication_sign << map.h(); map_size_info = msi.str(); info_stream << " — " + map_size_info; } catch(incorrect_map_format_error& e) { ERR_CF << "illegal map: " << e.message << std::endl; verified = false; } catch(wml_exception& e) { ERR_CF << "map could not be loaded: " << e.dev_message << '\n'; verified = false; } } info_stream << " "; // // Check scenarios and campaigns // if(!game["mp_scenario"].empty() && game["mp_campaign"].empty()) { // Check if it's a multiplayer scenario const config* level_cfg = &game_config.find_child("multiplayer", "id", game["mp_scenario"]); // Check if it's a user map if(!*level_cfg) { level_cfg = &game_config.find_child("generic_multiplayer", "id", game["mp_scenario"]); } if(*level_cfg) { scenario = formatter() << "<b>" << _("(S)") << "</b>" << " " << (*level_cfg)["name"].str(); info_stream << scenario; // Reloaded games do not match the original scenario hash, so it makes no sense // to test them, since they always would appear as remote scenarios if(!reloaded) { if(const config& hashes = game_config.child("multiplayer_hashes")) { std::string hash = game["hash"]; bool hash_found = false; for(const auto & i : hashes.attribute_range()) { if(i.first == game["mp_scenario"] && i.second == hash) { hash_found = true; break; } } if(!hash_found) { remote_scenario = true; info_stream << " — "; info_stream << _("Remote scenario"); verified = false; } } } if((*level_cfg)["require_scenario"].to_bool(false)) { ADDON_REQ result = check_addon_version_compatibility((*level_cfg), game); addons_outcome = std::max(addons_outcome, result); // Elevate to most severe error level encountered so far } } else { scenario = vgettext("Unknown scenario: $scenario_id", {{"scenario_id", game["mp_scenario_name"].str()}}); info_stream << scenario; verified = false; } } else if(!game["mp_campaign"].empty()) { if(const config& level_cfg = game_config.find_child("campaign", "id", game["mp_campaign"])) { std::stringstream campaign_text; campaign_text << "<b>" << _("(C)") << "</b>" << " " << level_cfg["name"] << " — " << game["mp_scenario_name"]; // Difficulty config difficulties = gui2::dialogs::generate_difficulty_config(level_cfg); for(const config& difficulty : difficulties.child_range("difficulty")) { if(difficulty["define"] == game["difficulty_define"]) { campaign_text << " — " << difficulty["description"]; break; } } scenario = campaign_text.str(); info_stream << campaign_text.rdbuf(); // TODO: should we have this? //if(game["require_scenario"].to_bool(false)) { ADDON_REQ result = check_addon_version_compatibility(level_cfg, game); addons_outcome = std::max(addons_outcome, result); // Elevate to most severe error level encountered so far //} } else { scenario = vgettext("Unknown campaign: $campaign_id", {{"campaign_id", game["mp_campaign"].str()}}); info_stream << scenario; verified = false; } } else { scenario = _("Unknown scenario"); info_stream << scenario; verified = false; } // Remove any newlines that might have been in game titles boost::replace_all(scenario, "\n", " " + font::unicode_em_dash + " "); if(reloaded) { info_stream << " — "; info_stream << _("Reloaded game"); verified = false; } if(!turn.empty()) { started = true; int index = turn.find_first_of('/'); if(index > -1) { const std::string current_turn_string = turn.substr(0, index); current_turn = lexical_cast<unsigned int>(current_turn_string); } status = _("Turn") + " " + turn; } else { started = false; if(vacant_slots > 0) { status = _n("Vacant Slot:", "Vacant Slots:", vacant_slots) + " " + game["slots"]; } } if(fog) { vision = _("Fog"); if(shroud) { vision += "/"; vision += _("Shroud"); } } else if(shroud) { vision = _("Shroud"); } else { vision = _("none"); } if(game["mp_countdown"].to_bool()) { time_limit = formatter() << game["mp_countdown_init_time"].str() << "+" << game["mp_countdown_turn_bonus"].str() << "/" << game["mp_countdown_action_bonus"].str(); } map_info = info_stream.str(); }
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); }
/*WIKI * @page = GUIToolkitWML * @order = 1_window * @begin{parent}{name=gui/window/} * == Resolution == * @begin{tag}{name="resolution"}{min="0"}{max="-1"} * @begin{table}{config} * window_width & unsigned & 0 & Width of the application window. $ * window_height & unsigned & 0 & Height of the application window. $ * * * automatic_placement & bool & true & * Automatically calculate the best size for the window and place it. If * automatically placed ''vertical_placement'' and ''horizontal_placement'' * can be used to modify the final placement. If not automatically placed * the ''width'' and ''height'' are mandatory. $ * * * x & f_unsigned & 0 & X coordinate of the window to show. $ * y & f_unsigned & 0 & Y coordinate of the window to show. $ * width & f_unsigned & 0 & Width of the window to show. $ * height & f_unsigned & 0 & Height of the window to show. $ * * reevaluate_best_size & f_bool & false & * The foo $ * * functions & function & "" & * The function definitions s available for the formula fields in window. $ * * vertical_placement & v_align & "" & * The vertical placement of the window. $ * * horizontal_placement & h_align & "" & * The horizontal placement of the window. $ * * * maximum_width & unsigned & 0 & * The maximum width of the window (only used for automatic placement). $ * * maximum_height & unsigned & 0 & * The maximum height of the window (only used for automatic placement). $ * * * click_dismiss & bool & false & * Does the window need click dismiss behavior? Click dismiss behavior * means that any mouse click will close the dialog. Note certain widgets * will automatically disable this behavior since they need to process the * clicks as well, for example buttons do need a click and a misclick on * button shouldn't close the dialog. NOTE with some widgets this behavior * depends on their contents (like scrolling labels) so the behavior might * get changed depending on the data in the dialog. NOTE the default * behavior might be changed since it will be disabled when can't be used * due to widgets which use the mouse, including buttons, so it might be * wise to set the behavior explicitly when not wanted and no mouse using * widgets are available. This means enter, escape or an external source * needs to be used to close the dialog (which is valid). $ * * * definition & string & "default" & * Definition of the window which we want to show. $ * * * linked_group & sections & [] & A group of linked widget sections. $ * * * tooltip & section & & * Information regarding the tooltip for this window. $ * * helptip & section & & * Information regarding the helptip for this window. $ * * * grid & grid & & The grid with the widgets to show. $ * @end{table} * @begin{tag}{name="linked_group"}{min=0}{max=-1} * A linked_group section has the following fields: * @begin{table}{config} * id & string & & The unique id of the group (unique in this * window). $ * fixed_width & bool & false & Should widget in this group have the same * width. $ * fixed_height & bool & false & Should widget in this group have the same * height. $ * @end{table} * @end{tag}{name="linked_group"} * A linked group needs to have at least one size fixed. * @begin{tag}{name="tooltip"}{min=0}{max=1} * A tooltip and helptip section have the following field: * @begin{table}{config} * id & string & & The id of the tip to show. * Note more fields will probably be added later on. * @end{table}{config} * @end{tag}{name=tooltip} * @begin{tag}{name="foreground"}{min=0}{max=1} * @end{tag}{name="foreground"} * @begin{tag}{name="background"}{min=0}{max=1} * @end{tag}{name="background"} * @end{tag}{name="resolution"} * @end{parent}{name=gui/window/} * @begin{parent}{name=gui/window/resolution/} * @begin{tag}{name="helptip"}{min=0}{max=1}{super="gui/window/resolution/tooltip"} * @end{tag}{name="helptip"} * @end{parent}{name=gui/window/resolution/} */ twindow_builder::tresolution::tresolution(const config& cfg) : window_width(cfg["window_width"]) , window_height(cfg["window_height"]) , automatic_placement(cfg["automatic_placement"].to_bool(true)) , x(cfg["x"]) , y(cfg["y"]) , width(cfg["width"]) , height(cfg["height"]) , reevaluate_best_size(cfg["reevaluate_best_size"]) , functions() , vertical_placement(implementation::get_v_align(cfg["vertical_placement"])) , horizontal_placement( implementation::get_h_align(cfg["horizontal_placement"])) , maximum_width(cfg["maximum_width"]) , maximum_height(cfg["maximum_height"]) , click_dismiss(cfg["click_dismiss"].to_bool()) , definition(cfg["definition"]) , linked_groups() , tooltip(cfg.child_or_empty("tooltip"), "tooltip") , helptip(cfg.child_or_empty("helptip"), "helptip") , grid(0) { if(!cfg["functions"].empty()) { game_logic::formula(cfg["functions"], &functions).evaluate(); } const config& c = cfg.child("grid"); VALIDATE(c, _("No grid defined.")); grid = new tbuilder_grid(c); if(!automatic_placement) { VALIDATE(width.has_formula() || width(), missing_mandatory_wml_key("resolution", "width")); VALIDATE(height.has_formula() || height(), missing_mandatory_wml_key("resolution", "height")); } DBG_GUI_P << "Window builder: parsing resolution " << window_width << ',' << window_height << '\n'; if(definition.empty()) { definition = "default"; } for(const auto & lg : cfg.child_range("linked_group")) { tlinked_group linked_group; linked_group.id = lg["id"].str(); linked_group.fixed_width = lg["fixed_width"].to_bool(); linked_group.fixed_height = lg["fixed_height"].to_bool(); VALIDATE(!linked_group.id.empty(), missing_mandatory_wml_key("linked_group", "id")); if(!(linked_group.fixed_width || linked_group.fixed_height)) { utils::string_map symbols; symbols["id"] = linked_group.id; t_string msg = vgettext("Linked '$id' group needs a 'fixed_width' or " "'fixed_height' key.", symbols); VALIDATE(false, msg); } linked_groups.push_back(linked_group); } }
/** * @todo This might not be 100% correct, but at the moment * it is not possible to show the fog and shroud per player. * This might change in the future. * NOTE when 'load game' is selected there are no sides. */ const config& side_cfg() const { return initial_->child_or_empty("side"); }
lua_stage_wrapper( ai_context &context, const config &cfg, lua_ai_context &lua_ai_ctx ) : stage(context,cfg),action_handler_(),code_(cfg["code"]),serialized_evaluation_state_(cfg.child_or_empty("args")) { action_handler_ = std::shared_ptr<lua_ai_action_handler>(resources::lua_kernel->create_lua_ai_action_handler(code_.c_str(),lua_ai_ctx)); }
void team::team_info::read(const config& cfg) { gold = cfg["gold"]; income = cfg["income"]; team_name = cfg["team_name"].str(); user_team_name = cfg["user_team_name"]; side_name = cfg["side_name"]; faction = cfg["faction"].str(); faction_name = cfg["faction_name"]; save_id = cfg["save_id"].str(); current_player = cfg["current_player"].str(); countdown_time = cfg["countdown_time"].str(); action_bonus_count = cfg["action_bonus_count"]; flag = cfg["flag"].str(); flag_icon = cfg["flag_icon"].str(); id = cfg["id"].str(); scroll_to_leader = cfg["scroll_to_leader"].to_bool(true); objectives = cfg["objectives"]; objectives_changed = cfg["objectives_changed"].to_bool(); disallow_observers = cfg["disallow_observers"].to_bool(); allow_player = cfg["allow_player"].to_bool(true); chose_random = cfg["chose_random"].to_bool(false); no_leader = cfg["no_leader"].to_bool(); defeat_condition = cfg["defeat_condition"].to_enum<team::DEFEAT_CONDITION>(team::DEFEAT_CONDITION::NO_LEADER); lost = cfg["lost"].to_bool(false); hidden = cfg["hidden"].to_bool(); no_turn_confirmation = cfg["suppress_end_turn_confirmation"].to_bool(); side = cfg["side"].to_int(1); carryover_percentage = cfg["carryover_percentage"].to_int(game_config::gold_carryover_percentage); carryover_add = cfg["carryover_add"].to_bool(false); carryover_bonus = cfg["carryover_bonus"].to_double(1); carryover_gold = cfg["carryover_gold"].to_int(0); variables = cfg.child_or_empty("variables"); is_local = cfg["is_local"].to_bool(true); color = get_side_color_id_from_config(cfg); // If starting new scenario override settings from [ai] tags if(!user_team_name.translatable()) user_team_name = t_string::from_serialized(user_team_name); display* disp = display::get_singleton(); // // FIXME: the display isn't constructed at this point when launching a game, // but we still want to add the ai config. On the other hand, when creating // a scenario in the editor, the display IS constructed, but there's no AI // manager. All of which leads to this awkward conditional. We really should // figure out a better one... // // --vultraz, 2018-01-26 // if(!disp || !disp->in_editor()) { if(cfg.has_attribute("ai_config")) { ai::manager::get_singleton().add_ai_for_side_from_file(side, cfg["ai_config"], true); } else { ai::manager::get_singleton().add_ai_for_side_from_config(side, cfg, true); } } std::vector<std::string> recruits = utils::split(cfg["recruit"]); can_recruit.insert(recruits.begin(), recruits.end()); // at the start of a scenario "start_gold" is not set, we need to take the // value from the gold setting (or fall back to the gold default) if(!cfg["start_gold"].empty()) { start_gold = cfg["start_gold"]; } else if(!cfg["gold"].empty()) { start_gold = gold; } else { start_gold = default_team_gold_; } if(team_name.empty()) { team_name = cfg["side"].str(); } if(save_id.empty()) { save_id = id; } income_per_village = cfg["village_gold"].to_int(game_config::village_income); recall_cost = cfg["recall_cost"].to_int(game_config::recall_cost); const std::string& village_support = cfg["village_support"]; if(village_support.empty()) { support_per_village = game_config::village_support; } else { support_per_village = lexical_cast_default<int>(village_support, game_config::village_support); } controller = team::CONTROLLER::AI; controller.parse(cfg["controller"].str()); // TODO: Why do we read disallow observers differently when controller is empty? if(controller == CONTROLLER::EMPTY) { disallow_observers = cfg["disallow_observers"].to_bool(true); } // override persistence flag if it is explicitly defined in the config // by default, persistence of a team is set depending on the controller persistent = cfg["persistent"].to_bool(this->controller == CONTROLLER::HUMAN); //======================================================== // END OF MESSY CODE // Share_view and share_maps can't both be enabled, // so share_view overrides share_maps. share_vision = cfg["share_vision"].to_enum<team::SHARE_VISION>(team::SHARE_VISION::ALL); handle_legacy_share_vision(cfg); LOG_NG << "team_info::team_info(...): team_name: " << team_name << ", share_vision: " << share_vision << ".\n"; }
static ministage cfg_to_value(const config &cfg) { return ministage(cfg.child_or_empty("value")); }
static config cfg_to_value(const config &cfg) { return cfg.child_or_empty("value"); }
game_info::game_info(const config& game, const config& game_config) : mini_map() , id(game["id"]) , map_data(game["map_data"]) , name(game["name"]) , scenario() , remote_scenario(false) , map_info() , map_size_info() , era() , era_short() , gold(game["mp_village_gold"]) , support(game["mp_village_support"]) , xp(game["experience_modifier"].str() + "%") , vision() , status() , time_limit() , vacant_slots(lexical_cast_default<int>(game["slots"], 0)) // Can't use to_int() here. , current_turn(0) , reloaded(game["savegame"].to_bool()) , started(false) , fog(game["mp_fog"].to_bool()) , shroud(game["mp_shroud"].to_bool()) , observers(game["observer"].to_bool(true)) , shuffle_sides(game["shuffle_sides"].to_bool(true)) , use_map_settings(game["mp_use_map_settings"].to_bool()) , verified(true) , password_required(game["password"].to_bool()) , have_era(true) , have_all_mods(true) , has_friends(false) , has_ignored(false) , display_status(NEW) { std::string turn = game["turn"]; if(!game["mp_era"].empty()) { const config& era_cfg = game_config.find_child("era", "id", game["mp_era"]); utils::string_map symbols; symbols["era_id"] = game["mp_era"]; if(era_cfg) { era = era_cfg["name"].str(); era_short = era_cfg["short_name"].str(); if(era_short.empty()) { era_short = make_short_name(era); } } else { have_era = !game["require_era"].to_bool(true); era = vgettext("Unknown era: $era_id", symbols); era_short = "?" + make_short_name(era); verified = false; } } else { era = _("Unknown era"); era_short = "??"; verified = false; } map_info = era; if(!game.child_or_empty("modification").empty()) { for(const config &cfg : game.child_range("modification")) { if (cfg["require_modification"].to_bool(false)) { const config &mod = game_config.find_child("modification", "id", cfg["id"]); if (!mod) { have_all_mods = false; break; } } } } if(map_data.empty()) { map_data = filesystem::read_map(game["mp_scenario"]); } if(map_data.empty()) { map_info += " — ??×??"; } else { try { gamemap map(boost::make_shared<terrain_type_data>(game_config), map_data); // mini_map = image::getMinimap(minimap_size_, minimap_size_, map, // 0); std::ostringstream msi; msi << map.w() << utils::unicode_multiplication_sign << map.h(); map_size_info = msi.str(); map_info += " — " + map_size_info; } catch(incorrect_map_format_error& e) { ERR_CF << "illegal map: " << e.message << std::endl; verified = false; } catch(twml_exception& e) { ERR_CF << "map could not be loaded: " << e.dev_message << '\n'; verified = false; } } map_info += " "; if(!game["mp_scenario"].empty()) { // check if it's a multiplayer scenario const config* level_cfg = &game_config.find_child("multiplayer", "id", game["mp_scenario"]); if(!*level_cfg) { // check if it's a user map level_cfg = &game_config.find_child("generic_multiplayer", "id", game["mp_scenario"]); } if(*level_cfg) { scenario = (*level_cfg)["name"].str(); map_info += scenario; // reloaded games do not match the original scenario hash, // so it makes no sense to test them, they always would appear // as remote scenarios if(!reloaded) { if(const config& hashes = game_config.child("multiplayer_hashes")) { std::string hash = game["hash"]; bool hash_found = false; for(const auto & i : hashes.attribute_range()) { if(i.first == game["mp_scenario"] && i.second == hash) { hash_found = true; break; } } if(!hash_found) { remote_scenario = true; map_info += " — "; map_info += _("Remote scenario"); verified = false; } } } } else { utils::string_map symbols; symbols["scenario_id"] = game["mp_scenario"]; scenario = vgettext("Unknown scenario: $scenario_id", symbols); map_info += scenario; verified = false; } } else { scenario = _("Unknown scenario"); map_info += scenario; verified = false; } if(reloaded) { map_info += " — "; map_info += _("Reloaded game"); verified = false; } if(!turn.empty()) { started = true; int index = turn.find_first_of('/'); if(index > -1) { const std::string current_turn_string = turn.substr(0, index); current_turn = lexical_cast<unsigned int>(current_turn_string); } status = _("Turn ") + turn; } else { started = false; if(vacant_slots > 0) { status = std::string( _n("Vacant Slot:", "Vacant Slots:", vacant_slots)) + " " + game["slots"]; } } if(fog) { vision = _("Fog"); if(shroud) { vision += "/"; vision += _("Shroud"); } } else if(shroud) { vision = _("Shroud"); } else { vision = _("none"); } if(game["mp_countdown"].to_bool()) { time_limit = game["mp_countdown_init_time"].str() + "+" + game["mp_countdown_turn_bonus"].str() + "/" + game["mp_countdown_action_bonus"].str(); } else { time_limit = ""; } }
engine_fai::engine_fai( readonly_context &context, const config &cfg ) : engine(context,cfg), formula_ai_(context,cfg.child_or_empty("formula_ai")) { name_ = "fai"; formula_ai_.on_create(); }
lua_candidate_action_wrapper_base( rca_context &context, const config &cfg) : candidate_action(context, cfg),evaluation_action_handler_(),execution_action_handler_(),serialized_evaluation_state_(cfg.child_or_empty("args")) { // do nothing }