gamestatus.cpp

Go to the documentation of this file.
00001 //* $Id: gamestatus.cpp 53932 2012-04-14 23:43:43Z shadowmaster $ */
00002 /*
00003    Copyright (C) 2003 - 2012 by David White <dave@whitevine.net>
00004    Part of the Battle for Wesnoth Project http://www.wesnoth.org/
00005 
00006    This program is free software; you can redistribute it and/or modify
00007    it under the terms of the GNU General Public License as published by
00008    the Free Software Foundation; either version 2 of the License, or
00009    (at your option) any later version.
00010    This program is distributed in the hope that it will be useful,
00011    but WITHOUT ANY WARRANTY.
00012 
00013    See the COPYING file for more details.
00014 */
00015 
00016 /**
00017  * @file
00018  * Maintain status of a game, load&save games.
00019  */
00020 
00021 #include "global.hpp"
00022 #include "config.hpp"
00023 
00024 #include "gamestatus.hpp"
00025 
00026 #include "actions.hpp"
00027 #include "filesystem.hpp"
00028 #include "foreach.hpp"
00029 #include "gettext.hpp"
00030 #include "log.hpp"
00031 #include "game_preferences.hpp"
00032 #include "replay.hpp"
00033 #include "resources.hpp"
00034 #include "statistics.hpp"
00035 #include "team.hpp"
00036 #include "unit.hpp"
00037 #include "unit_id.hpp"
00038 #include "wesconfig.h"
00039 #include "wml_exception.hpp"
00040 #include "formula_string_utils.hpp"
00041 #include "map.hpp"
00042 #include "pathfind/pathfind.hpp"
00043 #include "whiteboard/side_actions.hpp"
00044 
00045 #include <boost/bind.hpp>
00046 
00047 #ifndef _MSC_VER
00048 #include <sys/time.h>
00049 #endif
00050 
00051 static lg::log_domain log_engine("engine");
00052 #define ERR_NG LOG_STREAM(err, log_engine)
00053 #define WRN_NG LOG_STREAM(warn, log_engine)
00054 #define LOG_NG LOG_STREAM(info, log_engine)
00055 #define DBG_NG LOG_STREAM(debug, log_engine)
00056 
00057 static lg::log_domain log_engine_tc("engine/team_construction");
00058 #define ERR_NG_TC LOG_STREAM(err, log_engine_tc)
00059 #define WRN_NG_TC LOG_STREAM(warn, log_engine_tc)
00060 #define LOG_NG_TC LOG_STREAM(info, log_engine_tc)
00061 #define DBG_NG_TC LOG_STREAM(debug, log_engine_tc)
00062 
00063 game_classification::game_classification():
00064     savegame_config(),
00065     label(),
00066     parent(),
00067     version(),
00068     campaign_type(),
00069     campaign_define(),
00070     campaign_xtra_defines(),
00071     campaign(),
00072     history(),
00073     abbrev(),
00074     scenario(),
00075     next_scenario(),
00076     completion(),
00077     end_credits(true),
00078     end_text(),
00079     end_text_duration(),
00080     difficulty("NORMAL")
00081     {}
00082 
00083 game_classification::game_classification(const config& cfg):
00084     savegame_config(),
00085     label(cfg["label"]),
00086     parent(cfg["parent"]),
00087     version(cfg["version"]),
00088     campaign_type(cfg["campaign_type"].empty() ? "scenario" : cfg["campaign_type"].str()),
00089     campaign_define(cfg["campaign_define"]),
00090     campaign_xtra_defines(utils::split(cfg["campaign_extra_defines"])),
00091     campaign(cfg["campaign"]),
00092     history(cfg["history"]),
00093     abbrev(cfg["abbrev"]),
00094     scenario(cfg["scenario"]),
00095     next_scenario(cfg["next_scenario"]),
00096     completion(cfg["completion"]),
00097     end_credits(cfg["end_credits"].to_bool(true)),
00098     end_text(cfg["end_text"]),
00099     end_text_duration(cfg["end_text_duration"]),
00100     difficulty(cfg["difficulty"].empty() ? "NORMAL" : cfg["difficulty"].str())
00101     {}
00102 
00103 game_classification::game_classification(const game_classification& gc):
00104     savegame_config(),
00105     label(gc.label),
00106     parent(gc.parent),
00107     version(gc.version),
00108     campaign_type(gc.campaign_type),
00109     campaign_define(gc.campaign_define),
00110     campaign_xtra_defines(gc.campaign_xtra_defines),
00111     campaign(gc.campaign),
00112     history(gc.history),
00113     abbrev(gc.abbrev),
00114     scenario(gc.scenario),
00115     next_scenario(gc.next_scenario),
00116     completion(gc.completion),
00117     end_credits(gc.end_credits),
00118     end_text(gc.end_text),
00119     end_text_duration(gc.end_text_duration),
00120     difficulty(gc.difficulty)
00121 {
00122 }
00123 
00124 config game_classification::to_config() const
00125 {
00126     config cfg;
00127 
00128     cfg["label"] = label;
00129     cfg["parent"] = parent;
00130     cfg["version"] = game_config::version;
00131     cfg["campaign_type"] = campaign_type;
00132     cfg["campaign_define"] = campaign_define;
00133     cfg["campaign_extra_defines"] = utils::join(campaign_xtra_defines);
00134     cfg["campaign"] = campaign;
00135     cfg["history"] = history;
00136     cfg["abbrev"] = abbrev;
00137     cfg["scenario"] = scenario;
00138     cfg["next_scenario"] = next_scenario;
00139     cfg["completion"] = completion;
00140     cfg["end_credits"] = end_credits;
00141     cfg["end_text"] = end_text;
00142     cfg["end_text_duration"] = str_cast<unsigned int>(end_text_duration);
00143     cfg["difficulty"] = difficulty;
00144 
00145     return cfg;
00146 }
00147 
00148 game_state::game_state()  :
00149         scoped_variables(),
00150         wml_menu_items(),
00151         replay_data(),
00152         starting_pos(),
00153         snapshot(),
00154         last_selected(map_location::null_location),
00155         rng_(),
00156         variables_(),
00157         temporaries_(),
00158         generator_setter_(&recorder),
00159         classification_(),
00160         mp_settings_(),
00161         phase_(INITIAL),
00162         can_end_turn_(true)
00163         {}
00164 
00165 void write_players(game_state& gamestate, config& cfg, const bool use_snapshot, const bool merge_side)
00166 {
00167     // If there is already a player config available it means we are loading
00168     // from a savegame. Don't do anything then, the information is already there
00169     config::child_itors player_cfg = cfg.child_range("player");
00170     if (player_cfg.first != player_cfg.second)
00171         return;
00172 
00173     config *source = NULL;
00174     if (use_snapshot) {
00175         source = &gamestate.snapshot;
00176     } else {
00177         source = &gamestate.starting_pos;
00178     }
00179 
00180     if (merge_side) {
00181         //merge sides/players from starting pos with the scenario cfg
00182         std::vector<std::string> tags;
00183         tags.push_back("side");
00184         tags.push_back("player"); //merge [player] tags for backwards compatibility of saves
00185 
00186         foreach (const std::string& side_tag, tags)
00187         {
00188             foreach (config &carryover_side, source->child_range(side_tag))
00189             {
00190                 config *scenario_side = NULL;
00191 
00192                 //TODO: use the player_id instead of the save_id for that
00193                 if (config& c = cfg.find_child("side", "save_id", carryover_side["save_id"])) {
00194                     scenario_side = &c;
00195                 } else if (config& c = cfg.find_child("side", "id", carryover_side["save_id"])) {
00196                     scenario_side = &c;
00197                 }
00198 
00199                 if (scenario_side == NULL) {
00200                     //no matching side in the current scenario, we add the persistent information in a [player] tag
00201                     cfg.add_child("player", carryover_side);
00202                     continue;
00203                 }
00204 
00205                 //we have a matching side in the current scenario
00206 
00207                 //sort carryover gold
00208                 int ngold = (*scenario_side)["gold"].to_int(100);
00209                 int player_gold = carryover_side["gold"];
00210                 if (carryover_side["gold_add"].to_bool()) {
00211                     ngold += player_gold;
00212                 } else if (player_gold >= ngold) {
00213                     ngold = player_gold;
00214                 }
00215                 carryover_side["gold"] = str_cast(ngold);
00216                 if (const config::attribute_value *v = scenario_side->get("gold_add")) {
00217                     carryover_side["gold_add"] = *v;
00218                 }
00219                 //merge player information into the scenario cfg
00220                 (*scenario_side)["save_id"] = carryover_side["save_id"];
00221                 (*scenario_side)["gold"] = ngold;
00222                 (*scenario_side)["gold_add"] = carryover_side["gold_add"];
00223                 if (const config::attribute_value *v = carryover_side.get("previous_recruits")) {
00224                     (*scenario_side)["previous_recruits"] = *v;
00225                 } else {
00226                     (*scenario_side)["previous_recruits"] = carryover_side["can_recruit"];
00227                 }
00228                 (*scenario_side)["name"] = carryover_side["name"];
00229                 (*scenario_side)["current_player"] = carryover_side["current_player"];
00230 
00231                 (*scenario_side)["color"] = carryover_side["color"];
00232 
00233                 //add recallable units
00234                 foreach (const config &u, carryover_side.child_range("unit")) {
00235                     scenario_side->add_child("unit", u);
00236                 }
00237             }
00238         }
00239     } else {
00240         foreach(const config &snapshot_side, source->child_range("side")) {
00241             //take all side tags and add them as players (assuming they only contain carryover information)
00242             cfg.add_child("player", snapshot_side);
00243         }
00244     }
00245 }
00246 
00247 game_state::game_state(const config& cfg, bool show_replay) :
00248         scoped_variables(),
00249         wml_menu_items(),
00250         replay_data(),
00251         starting_pos(),
00252         snapshot(),
00253         last_selected(map_location::null_location),
00254         rng_(cfg),
00255         variables_(),
00256         temporaries_(),
00257         generator_setter_(&recorder),
00258         classification_(cfg),
00259         mp_settings_(cfg),
00260         phase_(INITIAL),
00261         can_end_turn_(true)
00262 {
00263     n_unit::id_manager::instance().set_save_id(cfg["next_underlying_unit_id"]);
00264     log_scope("read_game");
00265 
00266     const config &snapshot = cfg.child("snapshot");
00267     const config &replay_start = cfg.child("replay_start");
00268     // We're loading a snapshot if we have it and the user didn't request a replay.
00269     bool load_snapshot = !show_replay && snapshot && !snapshot.empty();
00270 
00271     if (load_snapshot) {
00272         this->snapshot = snapshot;
00273 
00274         rng_.seed_random(snapshot["random_calls"]);
00275     } else {
00276         assert(replay_start);
00277     }
00278 
00279     can_end_turn_ = cfg["can_end_turn"].to_bool(true);
00280 
00281     LOG_NG << "scenario: '" << classification_.scenario << "'\n";
00282     LOG_NG << "next_scenario: '" << classification_.next_scenario << "'\n";
00283 
00284     //priority of populating wml variables:
00285     //snapshot -> replay_start -> root
00286     if (load_snapshot) {
00287         if (const config &vars = snapshot.child("variables")) {
00288             set_variables(vars);
00289         } else if (const config &vars = cfg.child("variables")) {
00290             set_variables(vars);
00291         }
00292     }
00293     else if (const config &vars = replay_start.child("variables")) {
00294         set_variables(vars);
00295     }
00296     else if (const config &vars = cfg.child("variables")) {
00297         set_variables(vars);
00298     }
00299     set_menu_items(cfg.child_range("menu_item"));
00300 
00301     if (const config &replay = cfg.child("replay")) {
00302         replay_data = replay;
00303     }
00304 
00305     if (replay_start) {
00306         starting_pos = replay_start;
00307         //This is a quick hack to make replays for campaigns work again:
00308         //The [player] information needs to be stored somewhere within the gamestate,
00309         //because we need it later on when creating the replay savegame.
00310         //We therefore put it inside the starting_pos, so it doesn't get lost.
00311         //See also playcampaign::play_game, where after finishing the scenario the replay
00312         //will be saved.
00313         if(!starting_pos.empty()) {
00314             foreach (const config &p, cfg.child_range("player")) {
00315                 config& cfg_player = starting_pos.add_child("player");
00316                 cfg_player.merge_with(p);
00317             }
00318         }
00319     }
00320 
00321     if (const config &stats = cfg.child("statistics")) {
00322         statistics::fresh_stats();
00323         statistics::read_stats(stats);
00324     }
00325 }
00326 
00327 void game_state::write_snapshot(config& cfg) const
00328 {
00329     log_scope("write_game");
00330     cfg["label"] = classification_.label;
00331     cfg["history"] = classification_.history;
00332     cfg["abbrev"] = classification_.abbrev;
00333     cfg["version"] = game_config::version;
00334 
00335     cfg["scenario"] = classification_.scenario;
00336     cfg["next_scenario"] = classification_.next_scenario;
00337 
00338     cfg["completion"] = classification_.completion;
00339 
00340     cfg["campaign"] = classification_.campaign;
00341     cfg["campaign_type"] = classification_.campaign_type;
00342     cfg["difficulty"] = classification_.difficulty;
00343 
00344     cfg["campaign_define"] = classification_.campaign_define;
00345     cfg["campaign_extra_defines"] = utils::join(classification_.campaign_xtra_defines);
00346     cfg["next_underlying_unit_id"] = str_cast(n_unit::id_manager::instance().get_save_id());
00347     cfg["can_end_turn"] = can_end_turn_;
00348 
00349     cfg["random_seed"] = rng_.get_random_seed();
00350     cfg["random_calls"] = rng_.get_random_calls();
00351 
00352     cfg["end_credits"] = classification_.end_credits;
00353     cfg["end_text"] = classification_.end_text;
00354     cfg["end_text_duration"] = str_cast<unsigned int>(classification_.end_text_duration);
00355 
00356     cfg.add_child("variables", variables_);
00357 
00358     for(std::map<std::string, wml_menu_item *>::const_iterator j=wml_menu_items.begin();
00359         j!=wml_menu_items.end(); ++j) {
00360         config new_cfg;
00361         new_cfg["id"]=j->first;
00362         new_cfg["image"]=j->second->image;
00363         new_cfg["description"]=j->second->description;
00364         new_cfg["needs_select"] = j->second->needs_select;
00365         if(!j->second->show_if.empty())
00366             new_cfg.add_child("show_if", j->second->show_if);
00367         if(!j->second->filter_location.empty())
00368             new_cfg.add_child("filter_location", j->second->filter_location);
00369         if(!j->second->command.empty())
00370             new_cfg.add_child("command", j->second->command);
00371         cfg.add_child("menu_item", new_cfg);
00372     }
00373 }
00374 
00375 void extract_summary_from_config(config& cfg_save, config& cfg_summary)
00376 {
00377     const config &cfg_snapshot = cfg_save.child("snapshot");
00378     const config &cfg_replay_start = cfg_save.child("replay_start");
00379 
00380     const config &cfg_replay = cfg_save.child("replay");
00381     const bool has_replay = cfg_replay && !cfg_replay.empty();
00382     const bool has_snapshot = cfg_snapshot && cfg_snapshot.child("side");
00383 
00384     cfg_summary["replay"] = has_replay;
00385     cfg_summary["snapshot"] = has_snapshot;
00386 
00387     cfg_summary["label"] = cfg_save["label"];
00388     cfg_summary["parent"] = cfg_save["parent"];
00389     cfg_summary["campaign_type"] = cfg_save["campaign_type"];
00390     cfg_summary["scenario"] = cfg_save["scenario"];
00391     cfg_summary["campaign"] = cfg_save["campaign"];
00392     cfg_summary["difficulty"] = cfg_save["difficulty"];
00393     cfg_summary["version"] = cfg_save["version"];
00394     cfg_summary["corrupt"] = "";
00395 
00396     if(has_snapshot) {
00397         cfg_summary["turn"] = cfg_snapshot["turn_at"];
00398         if (cfg_snapshot["turns"] != "-1") {
00399             cfg_summary["turn"] = cfg_summary["turn"].str() + "/" + cfg_snapshot["turns"].str();
00400         }
00401     }
00402 
00403     // Find the first human leader so we can display their icon in the load menu.
00404 
00405     /** @todo Ideally we should grab all leaders if there's more than 1 human player? */
00406     std::string leader;
00407     std::string leader_image;
00408 
00409     //foreach (const config &p, cfg_save.child_range("player"))
00410     //{
00411     //  if (p["canrecruit"].to_bool(false))) {
00412     //      leader = p["save_id"];
00413     //  }
00414     //}
00415 
00416     bool shrouded = false;
00417 
00418     //if (!leader.empty())
00419     //{
00420         if (const config &snapshot = *(has_snapshot ? &cfg_snapshot : &cfg_replay_start))
00421         {
00422             foreach (const config &side, snapshot.child_range("side"))
00423             {
00424                 if (side["controller"] != "human") {
00425                     continue;
00426                 }
00427 
00428                 if (side["shroud"].to_bool()) {
00429                     shrouded = true;
00430                 }
00431 
00432                 if (side["canrecruit"].to_bool())
00433                 {
00434                         leader = side["id"].str();
00435                         leader_image = side["image"].str();
00436                         break;
00437                 }
00438 
00439                 foreach (const config &u, side.child_range("unit"))
00440                 {
00441                     if (u["canrecruit"].to_bool()) {
00442                         leader = u["id"].str();
00443                         leader_image = u["image"].str();
00444                         break;
00445                     }
00446                 }
00447             }
00448         }
00449     //}
00450 
00451     cfg_summary["leader"] = leader;
00452     // We need a binary path-independent path to the leader image here
00453     // so it can be displayed for campaign-specific units in the dialog
00454     // even when the campaign isn't loaded yet.
00455     cfg_summary["leader_image"] = get_independent_image_path(leader_image);
00456 
00457     if(!shrouded) {
00458         if(has_snapshot) {
00459             if (!cfg_snapshot.find_child("side", "shroud", "yes")) {
00460                 cfg_summary.add_child("map", cfg_snapshot.child_or_empty("map"));
00461             }
00462         } else if(has_replay) {
00463             if (!cfg_replay_start.find_child("side","shroud","yes")) {
00464                 cfg_summary.add_child("map", cfg_replay_start.child_or_empty("map"));
00465             }
00466         }
00467     }
00468 }
00469 
00470 config::attribute_value &game_state::get_variable(const std::string& key)
00471 {
00472     return variable_info(key, true, variable_info::TYPE_SCALAR).as_scalar();
00473 }
00474 
00475 config::attribute_value game_state::get_variable_const(const std::string &key) const
00476 {
00477     variable_info to_get(key, false, variable_info::TYPE_SCALAR);
00478     if (!to_get.is_valid)
00479     {
00480         config::attribute_value &to_return = temporaries_[key];
00481         if (key.size() > 7 && key.substr(key.size() - 7) == ".length") {
00482             // length is a special attribute, so guarantee its correctness
00483             to_return = 0;
00484         }
00485         return to_return;
00486     }
00487     return to_get.as_scalar();
00488 }
00489 
00490 config& game_state::get_variable_cfg(const std::string& key)
00491 {
00492     return variable_info(key, true, variable_info::TYPE_CONTAINER).as_container();
00493 }
00494 
00495 void game_state::set_variable(const std::string& key, const t_string& value)
00496 {
00497     get_variable(key) = value;
00498 }
00499 
00500 config& game_state::add_variable_cfg(const std::string& key, const config& value)
00501 {
00502     variable_info to_add(key, true, variable_info::TYPE_ARRAY);
00503     return to_add.vars->add_child(to_add.key, value);
00504 }
00505 
00506 void game_state::clear_variable_cfg(const std::string& varname)
00507 {
00508     variable_info to_clear(varname, false, variable_info::TYPE_CONTAINER);
00509     if(!to_clear.is_valid) return;
00510     if(to_clear.explicit_index) {
00511         to_clear.vars->remove_child(to_clear.key, to_clear.index);
00512     } else {
00513         to_clear.vars->clear_children(to_clear.key);
00514     }
00515 }
00516 
00517 void game_state::clear_variable(const std::string& varname)
00518 {
00519     variable_info to_clear(varname, false);
00520     if(!to_clear.is_valid) return;
00521     if(to_clear.explicit_index) {
00522         to_clear.vars->remove_child(to_clear.key, to_clear.index);
00523     } else {
00524         to_clear.vars->clear_children(to_clear.key);
00525         to_clear.vars->remove_attribute(to_clear.key);
00526     }
00527 }
00528 
00529 static void clear_wmi(std::map<std::string, wml_menu_item *> &gs_wmi)
00530 {
00531     for (std::map<std::string, wml_menu_item *>::iterator i = gs_wmi.begin(),
00532          i_end = gs_wmi.end(); i != i_end; ++i)
00533     {
00534         delete i->second;
00535     }
00536     gs_wmi.clear();
00537 }
00538 
00539 game_state::game_state(const game_state& state) :
00540     variable_set(), // Not sure why empty, copied from old code
00541     scoped_variables(state.scoped_variables),
00542     wml_menu_items(),
00543     replay_data(state.replay_data),
00544     starting_pos(state.starting_pos),
00545     snapshot(state.snapshot),
00546     last_selected(state.last_selected),
00547     rng_(state.rng_),
00548     variables_(state.variables_),
00549     temporaries_(), // Not sure why empty, copied from old code
00550     generator_setter_(state.generator_setter_),
00551     classification_(state.classification_),
00552     mp_settings_(state.mp_settings_),
00553     phase_(state.phase_),
00554     can_end_turn_(state.can_end_turn_)
00555 {
00556     clear_wmi(wml_menu_items);
00557     std::map<std::string, wml_menu_item*>::const_iterator itor;
00558     for (itor = state.wml_menu_items.begin(); itor != state.wml_menu_items.end(); ++itor) {
00559         wml_menu_item*& mref = wml_menu_items[itor->first];
00560         mref = new wml_menu_item(*(itor->second));
00561     }
00562 }
00563 
00564 game_state& game_state::operator=(const game_state& state)
00565 {
00566     // Use copy constructor to make sure we are coherent
00567     if (this != &state) {
00568         this->~game_state();
00569         new (this) game_state(state) ;
00570     }
00571     return *this ;
00572 }
00573 
00574 game_state::~game_state() {
00575     clear_wmi(wml_menu_items);
00576 }
00577 
00578 void game_state::set_variables(const config& vars) {
00579     variables_ = vars;
00580 }
00581 
00582 
00583 class team_builder {
00584 public:
00585     team_builder(const config& side_cfg,
00586              const std::string &save_id, std::vector<team>& teams,
00587              const config& level, gamemap& map, unit_map& units,
00588              bool snapshot, const config &starting_pos)
00589         : gold_info_ngold_(0)
00590         , gold_info_add_(false)
00591         , leader_configs_()
00592         , level_(level)
00593         , map_(map)
00594         , player_cfg_(NULL)
00595         , player_exists_(false)
00596         , save_id_(save_id)
00597         , seen_ids_()
00598         , side_(0)
00599         , side_cfg_(side_cfg)
00600         , snapshot_(snapshot)
00601         , starting_pos_(starting_pos)
00602         , t_(NULL)
00603         , teams_(teams)
00604         , unit_configs_()
00605         , units_(units)
00606     {
00607     }
00608 
00609     void build_team_stage_one()
00610     {
00611         //initialize the context variables and flags, find relevant tags, set up everything
00612         init();
00613 
00614         //find out the correct qty of gold and handle gold carryover.
00615         gold();
00616 
00617         //create a new instance of team and push it to back of resources::teams vector
00618         new_team();
00619 
00620         assert(t_!=NULL);
00621 
00622         //set team objectives if necessary
00623         objectives();
00624 
00625         // If the game state specifies additional units that can be recruited by the player, add them.
00626         previous_recruits();
00627 
00628         //place leader
00629         leader();
00630 
00631         //prepare units, populate obvious recall lists elements
00632         prepare_units();
00633 
00634     }
00635 
00636 
00637     void build_team_stage_two()
00638     {
00639         //place units
00640         //this is separate stage because we need to place units only after every other team is constructed
00641         place_units();
00642 
00643     }
00644 
00645 protected:
00646 
00647     static const std::string default_gold_qty_;
00648 
00649     int gold_info_ngold_;
00650     bool gold_info_add_;
00651     std::deque<config> leader_configs_;
00652     const config &level_;
00653     gamemap &map_;
00654     const config *player_cfg_;
00655     bool player_exists_;
00656     const std::string save_id_;
00657     std::set<std::string> seen_ids_;
00658     int side_;
00659     const config &side_cfg_;
00660     bool snapshot_;
00661     const config &starting_pos_;
00662     team *t_;
00663     std::vector<team> &teams_;
00664     std::vector<const config*> unit_configs_;
00665     unit_map &units_;
00666 
00667 
00668     void log_step(const char *s) const
00669     {
00670         LOG_NG_TC << "team "<<side_<<" construction: "<< s << std::endl;
00671     }
00672 
00673 
00674     void init()
00675     {
00676         side_ = side_cfg_["side"].to_int(1);
00677         if (unsigned(side_ - 1) >= teams_.size() || teams_[side_ - 1].side() != 0)
00678             throw config::error("Invalid side number.");
00679         t_ = &teams_[side_ - 1];
00680 
00681         log_step("init");
00682 
00683         player_cfg_ = NULL;
00684         //track whether a [player] tag with persistence information exists (in addition to the [side] tag)
00685         player_exists_ = false;
00686 
00687         if(map_.empty()) {
00688             throw game::load_game_failed("Map not found");
00689         }
00690 
00691         if(side_cfg_["controller"] == "human" ||
00692            side_cfg_["controller"] == "network" ||
00693            side_cfg_["controller"] == "network_ai" ||
00694            side_cfg_["controller"] == "human_ai" ||
00695             side_cfg_["persistent"].to_bool())
00696         {
00697             player_exists_ = true;
00698 
00699             //if we have a snapshot, level contains team information
00700             //else, we look for [side] or [player] (deprecated) tags in starting_pos
00701             ///@deprecated r37519 [player] instead of [side] in starting_pos
00702             if (snapshot_) {
00703                 if (const config &c = level_.find_child("player","save_id",save_id_))  {
00704                     player_cfg_ = &c;
00705                 }
00706             } else {
00707                 //at the start of scenario, get the persistence information from starting_pos
00708                 if (const config &c =  starting_pos_.find_child("player","save_id",save_id_))  {
00709                     player_cfg_ = &c;
00710                 } else if (const config &c =  starting_pos_.find_child("side","save_id",save_id_))  {
00711                     player_cfg_ = &c;
00712                     player_exists_ = false; //there is only a [side] tag for this save_id in starting_pos
00713                 } else {
00714                     player_cfg_ = NULL;
00715                     player_exists_ = false;
00716                 }
00717             }
00718         }
00719 
00720         DBG_NG_TC << "save id: "<< save_id_ <<std::endl;
00721         DBG_NG_TC << "snapshot: "<< (player_exists_ ? "true" : "false") <<std::endl;
00722         DBG_NG_TC << "player_cfg: "<< (player_cfg_==NULL ? "is null" : "is not null") <<std::endl;
00723         DBG_NG_TC << "player_exists: "<< (player_exists_ ? "true" : "false") <<std::endl;
00724 
00725         unit_configs_.clear();
00726         seen_ids_.clear();
00727 
00728     }
00729 
00730     bool use_player_cfg() const
00731     {
00732         return (player_cfg_ != NULL) && (!snapshot_);
00733     }
00734 
00735     void gold()
00736     {
00737         log_step("gold");
00738 
00739         std::string gold = side_cfg_["gold"];
00740         if(gold.empty()) {
00741             gold = default_gold_qty_;
00742         }
00743 
00744         DBG_NG_TC << "found gold: '" << gold << "'\n";
00745 
00746         gold_info_ngold_ = lexical_cast_default<int>(gold);
00747 
00748         /* This is the gold carry-over mechanism for subsequent campaign
00749            scenarios. Snapshots and replays are loaded from savegames and
00750            got their own gold information, which must not be altered here
00751         */
00752 
00753         //true  - carryover gold is added to the start_gold.
00754         //false - the max of the two is taken as start_gold.
00755         gold_info_add_ = side_cfg_["gold_add"].to_bool();
00756 
00757         if (use_player_cfg()) {
00758             try {
00759                 int player_gold = (*player_cfg_)["gold"];
00760                 if (!player_exists_) {
00761                     //if we get the persistence information from [side], carryover gold is already sorted
00762                     gold_info_ngold_ = player_gold;
00763                     gold_info_add_ = (*player_cfg_)["gold_add"].to_bool();
00764                 } else if ((*player_cfg_)["gold_add"].to_bool()) {
00765                     gold_info_ngold_ +=  player_gold;
00766                     gold_info_add_ = true;
00767                 } else if(player_gold >= gold_info_ngold_) {
00768                     gold_info_ngold_ = player_gold;
00769                 }
00770             } catch (config::error&) {
00771                 ERR_NG_TC << "player tag for " << save_id_ << " does not have gold information\n";
00772             }
00773         }
00774 
00775         DBG_NG_TC << "set gold to '" << gold_info_ngold_ << "'\n";
00776         DBG_NG_TC << "set gold add flag to '" << gold_info_add_ << "'\n";
00777     }
00778 
00779 
00780     void new_team()
00781     {
00782         log_step("new team");
00783         t_->build(side_cfg_, map_, gold_info_ngold_);
00784         t_->set_gold_add(gold_info_add_);
00785     }
00786 
00787 
00788     void objectives()
00789     {
00790         log_step("objectives");
00791         // If this team has no objectives, set its objectives
00792         // to the level-global "objectives"
00793         if (t_->objectives().empty())
00794             t_->set_objectives(level_["objectives"], false);
00795     }
00796 
00797 
00798     void previous_recruits()
00799     {
00800         log_step("previous recruits");
00801         // If the game state specifies units that
00802         // can be recruited for the player, add them.
00803         if (!player_cfg_) return;
00804         if (const config::attribute_value *v = player_cfg_->get("previous_recruits")) {
00805             foreach (const std::string &rec, utils::split(*v)) {
00806                 DBG_NG_TC << "adding previous recruit: " << rec << '\n';
00807                 t_->add_recruit(rec);
00808             }
00809         }
00810     }
00811 
00812 
00813 
00814 
00815     void handle_unit(const config &u, const char *origin)
00816     {
00817         DBG_NG_TC
00818             << "unit from "<<origin
00819             << ": type=["<<u["type"]
00820             << "] id=["<<u["id"]
00821             << "] placement=["<<u["placement"]
00822             << "] x=["<<u["x"]
00823             << "] y=["<<u["y"]
00824             <<"]"<< std::endl;
00825         const std::string &id = u["id"];
00826         if (!id.empty()) {
00827             if ( seen_ids_.find(id)!=seen_ids_.end() ) {
00828                 //seen before
00829                 config u_tmp = u;
00830                 u_tmp["side"] = str_cast(side_);
00831                 unit new_unit(u_tmp, true);
00832                 t_->recall_list().push_back(new_unit);
00833             } else {
00834                 //not seen before
00835                 unit_configs_.push_back(&u);
00836                 seen_ids_.insert(id);
00837             }
00838 
00839         } else {
00840             unit_configs_.push_back(&u);
00841         }
00842     }
00843 
00844     void handle_leader(const config &leader)
00845     {
00846         leader_configs_.push_back(leader);
00847 
00848         config::attribute_value &a1 = leader_configs_.back()["canrecruit"];
00849         if (a1.blank()) a1 = true;
00850         config::attribute_value &a2 = leader_configs_.back()["placement"];
00851         if (a2.blank()) a2 = "map,leader";
00852 
00853         handle_unit(leader_configs_.back(), "leader_cfg");
00854     }
00855 
00856     void leader()
00857     {
00858         log_step("leader");
00859         // If this side tag describes the leader of the side, we can simply add it to front of unit queue
00860         // there was a hack: if this side tag describes the leader of the side,
00861         // we may replace the leader with someone from recall list who can recruit, but take positioning from [side]
00862         // this hack shall be removed, since it messes up with 'multiple leaders'
00863 
00864         // If this side tag describes the leader of the side
00865         if (!side_cfg_["no_leader"].to_bool() && side_cfg_["controller"] != "null") {
00866             if (side_cfg_["type"] == "random") {
00867                 std::vector<std::string> types = utils::split(side_cfg_["random_leader"]);
00868                 if (types.empty())
00869                     types = utils::split(side_cfg_["leader"]);
00870                 if (types.empty()) {
00871                     utils::string_map i18n_symbols;
00872                     i18n_symbols["faction"] = side_cfg_["name"];
00873                     throw config::error(vgettext("Unable to find a leader type for faction $faction", i18n_symbols));
00874                 }
00875                 const int choice = rand() % types.size();
00876                 config leader = side_cfg_;
00877                 leader["type"] = types[choice];
00878                 handle_leader(leader);
00879             } else
00880                 handle_leader(side_cfg_);
00881         }
00882         foreach (const config &l, side_cfg_.child_range("leader")) {
00883             handle_leader(l);
00884         }
00885     }
00886 
00887 
00888     void prepare_units()
00889     {
00890         log_step("prepare units");
00891         if (use_player_cfg()) {
00892             //units in [replay_start][side] merged with [side]
00893             //only relevant in start-of-scenario saves, that's why !shapshot
00894             //units that are in '[scenario][side]' are 'first'
00895             //for create-or-recall semantics to work: for each unit with non-empty id, unconditionally put OTHER, later, units with same id directly to recall list, not including them in unit_configs_
00896             foreach(const config &u, (*player_cfg_).child_range("unit")) {
00897                 handle_unit(u,"player_cfg");
00898             }
00899 
00900         } else {
00901             //units in [side]
00902             foreach (const config &su, side_cfg_.child_range("unit")) {
00903                 handle_unit(su, "side_cfg");
00904             }
00905         }
00906     }
00907 
00908 
00909     void place_units()
00910     {
00911         static char const *side_attrs[] = {
00912             "income", "team_name", "user_team_name", "save_id",
00913             "current_player", "countdown_time", "action_bonus_count",
00914             "flag", "flag_icon", "objectives", "objectives_changed",
00915             "disallow_observers", "allow_player", "no_leader",
00916             "hidden", "music", "color", "ai_config", "gold",
00917             "start_gold", "team_rgb", "village_gold", "recall_cost",
00918             "controller", "persistent", "share_view",
00919             "share_maps", "recruit", "fog", "shroud", "shroud_data",
00920             "scroll_to_leader",
00921             // Multiplayer attributes.
00922             "income_lock", "gold_lock", "color_lock", "team_lock", "leader",
00923             "random_leader", "terrain_liked",
00924             "allow_changes", "faction_name", "user_description", "faction" };
00925 
00926         log_step("place units");
00927         foreach (const config *u, unit_configs_) {
00928             unit_creator uc(*t_,map_.starting_position(side_));
00929             uc
00930                 .allow_add_to_recall(true)
00931                 .allow_discover(true)
00932                 .allow_get_village(true)
00933                 .allow_invalidate(false)
00934                 .allow_rename_side(true)
00935                 .allow_show(false);
00936 
00937             config cfg = *u;
00938             foreach (const char *attr, side_attrs) {
00939                 cfg.remove_attribute(attr);
00940             }
00941             uc.add_unit(cfg);
00942 
00943         }
00944 
00945         // Find the first leader and use its name as the player name.
00946         unit_map::iterator u = resources::units->find_first_leader(t_->side());
00947         if ((u != resources::units->end()) && t_->current_player().empty())
00948             t_->set_current_player(u->name());
00949 
00950     }
00951 
00952 };
00953 
00954 const std::string team_builder::default_gold_qty_ = "100";
00955 
00956 
00957 team_builder_ptr game_state::create_team_builder(const config& side_cfg,
00958                      std::string save_id, std::vector<team>& teams,
00959                      const config& level, gamemap& map, unit_map& units,
00960                      bool snapshot)
00961 {
00962     return team_builder_ptr(new team_builder(side_cfg,save_id,teams,level,map,units,snapshot,starting_pos));
00963 }
00964 
00965 void game_state::build_team_stage_one(team_builder_ptr tb_ptr)
00966 {
00967     tb_ptr->build_team_stage_one();
00968 }
00969 
00970 void game_state::build_team_stage_two(team_builder_ptr tb_ptr)
00971 {
00972     tb_ptr->build_team_stage_two();
00973 }
00974 
00975 void game_state::set_menu_items(const config::const_child_itors &menu_items)
00976 {
00977     clear_wmi(wml_menu_items);
00978     foreach (const config &item, menu_items)
00979     {
00980         std::string id = item["id"];
00981         wml_menu_item*& mref = wml_menu_items[id];
00982         if(mref == NULL) {
00983             mref = new wml_menu_item(id, &item);
00984         } else {
00985             WRN_NG << "duplicate menu item (" << id << ") while loading gamestate\n";
00986         }
00987     }
00988 }
00989 
00990 void game_state::write_config(config_writer& out, bool write_variables) const
00991 {
00992     out.write(classification_.to_config());
00993     if (classification_.campaign_type == "multiplayer")
00994         out.write_child("multiplayer", mp_settings_.to_config());
00995     out.write_key_val("random_seed", lexical_cast<std::string>(rng_.get_random_seed()));
00996     out.write_key_val("random_calls", lexical_cast<std::string>(rng_.get_random_calls()));
00997     if (write_variables) {
00998         out.write_child("variables", variables_);
00999     }
01000 
01001     for(std::map<std::string, wml_menu_item *>::const_iterator j = wml_menu_items.begin();
01002         j != wml_menu_items.end(); ++j) {
01003         out.open_child("menu_item");
01004         out.write_key_val("id", j->first);
01005         out.write_key_val("image", j->second->image);
01006         out.write_key_val("description", j->second->description);
01007         out.write_key_val("needs_select", (j->second->needs_select) ? "yes" : "no");
01008         if(!j->second->show_if.empty())
01009             out.write_child("show_if", j->second->show_if);
01010         if(!j->second->filter_location.empty())
01011             out.write_child("filter_location", j->second->filter_location);
01012         if(!j->second->command.empty())
01013             out.write_child("command", j->second->command);
01014         out.close_child("menu_item");
01015     }
01016 
01017     if (!replay_data.child("replay")) {
01018         out.write_child("replay", replay_data);
01019     }
01020 
01021     out.write_child("replay_start",starting_pos);
01022 }
01023 
01024 wml_menu_item::wml_menu_item(const std::string& id, const config* cfg) :
01025         name(),
01026         event_id(id),
01027         image(),
01028         description(),
01029         needs_select(false),
01030         show_if(),
01031         filter_location(),
01032         command()
01033 
01034 {
01035     std::stringstream temp;
01036     temp << "menu item";
01037     if(!id.empty()) {
01038         temp << ' ' << id;
01039     }
01040     name = temp.str();
01041     if(cfg != NULL) {
01042         image = (*cfg)["image"].str();
01043         description = (*cfg)["description"];
01044         needs_select = (*cfg)["needs_select"].to_bool();
01045         if (const config &c = cfg->child("show_if")) show_if = c;
01046         if (const config &c = cfg->child("filter_location")) filter_location = c;
01047         if (const config &c = cfg->child("command")) command = c;
01048     }
01049 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines

Generated by doxygen 1.7.1 on Fri May 25 2012 01:02:53 for The Battle for Wesnoth
Gna! | Forum | Wiki | CIA | devdocs