added basic statistics module
This commit is contained in:
parent
e96b98ed3a
commit
0bc0032b08
13 changed files with 337 additions and 8 deletions
|
@ -193,6 +193,7 @@ your_turn="It is now your turn"
|
|||
leader="Leader"
|
||||
|
||||
#fighting statistics
|
||||
action_statistics="Statistics"
|
||||
base_damage="base damage"
|
||||
minimum_damage="minimum damage"
|
||||
damage_calculations="Damage Calculations"
|
||||
|
@ -200,6 +201,11 @@ attacker="Attacker"
|
|||
defender="Defender"
|
||||
attacker_resistance="attacker resistance vs"
|
||||
defender_resistance="defender resistance vs"
|
||||
total_recruits="Recruits"
|
||||
total_recalls="Recalls"
|
||||
total_advances="Advancements"
|
||||
total_deaths="Losses"
|
||||
total_kills="Kills"
|
||||
|
||||
#buttons
|
||||
main_menu="Menu"
|
||||
|
|
|
@ -393,6 +393,15 @@ battle_stats evaluate_battle_stats(
|
|||
}
|
||||
}
|
||||
|
||||
if(res.damage_defender_takes < 1) {
|
||||
res.damage_defender_takes = 1;
|
||||
if(include_strings) {
|
||||
std::stringstream str;
|
||||
str << translate_string("minimum_damage") << ", ,1";
|
||||
res.attack_calculations.push_back(str.str());
|
||||
}
|
||||
}
|
||||
|
||||
//if the attacker drains, and the defender is a living creature, then
|
||||
//the attacker will drain for half the damage it does
|
||||
if(attack.special() == drain_string && !d->second.type().not_living()) {
|
||||
|
@ -1034,6 +1043,8 @@ void advance_unit(const game_data& info,
|
|||
gamemap::location loc, const std::string& advance_to)
|
||||
{
|
||||
const unit& new_unit = get_advanced_unit(info,units,loc,advance_to);
|
||||
|
||||
statistics::advance_unit(new_unit);
|
||||
|
||||
units.erase(loc);
|
||||
units.insert(std::pair<gamemap::location,unit>(loc,new_unit));
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
#include "playturn.hpp"
|
||||
#include "replay.hpp"
|
||||
#include "show_dialog.hpp"
|
||||
#include "statistics.hpp"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
|
@ -102,6 +103,7 @@ bool ai_interface::recruit(const std::string& unit_name, location loc)
|
|||
|
||||
//see if we can actually recruit (i.e. have enough room etc)
|
||||
if(recruit_unit(info_.map,info_.team_num,info_.units,new_unit,loc,&info_.disp).empty()) {
|
||||
statistics::recruit_unit(new_unit);
|
||||
current_team().spend_gold(u->second.cost());
|
||||
|
||||
//confirm the transaction - i.e. don't undo recruitment
|
||||
|
|
|
@ -1130,6 +1130,11 @@ void config::clear()
|
|||
ordered_children.clear();
|
||||
}
|
||||
|
||||
bool config::empty() const
|
||||
{
|
||||
return children.empty() && values.empty();
|
||||
}
|
||||
|
||||
config::all_children_iterator::all_children_iterator(config::all_children_iterator::Itor i) : i_(i)
|
||||
{}
|
||||
|
||||
|
|
|
@ -159,6 +159,7 @@ struct config
|
|||
static bool has_value(const std::string& values, const std::string& val);
|
||||
|
||||
void clear();
|
||||
bool empty() const;
|
||||
|
||||
struct error {
|
||||
error(const std::string& msg) : message(msg) {}
|
||||
|
|
|
@ -38,6 +38,7 @@
|
|||
#include "replay.hpp"
|
||||
#include "show_dialog.hpp"
|
||||
#include "sound.hpp"
|
||||
#include "statistics.hpp"
|
||||
#include "team.hpp"
|
||||
#include "unit_types.hpp"
|
||||
#include "unit.hpp"
|
||||
|
@ -362,6 +363,8 @@ int play_game(int argc, char** argv)
|
|||
}
|
||||
|
||||
for(;;) {
|
||||
statistics::fresh_stats();
|
||||
|
||||
sound::play_music(game_config::title_music);
|
||||
|
||||
game_state state;
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#include "gamestatus.hpp"
|
||||
#include "language.hpp"
|
||||
#include "log.hpp"
|
||||
#include "statistics.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdio>
|
||||
|
@ -168,6 +169,11 @@ game_state read_game(game_data& data, const config* cfg)
|
|||
std::copy(can_recruit.begin(),can_recruit.end(),std::inserter(res.can_recruit,res.can_recruit.end()));
|
||||
}
|
||||
|
||||
statistics::fresh_stats();
|
||||
if(cfg->child("statistics")) {
|
||||
statistics::read_stats(*cfg->child("statistics"));
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
@ -211,6 +217,8 @@ void write_game(const game_state& game, config& cfg)
|
|||
can_recruit_str.resize(can_recruit_str.size()-1);
|
||||
|
||||
cfg["can_recruit"] = can_recruit_str;
|
||||
|
||||
cfg.add_child("statistics",statistics::write_stats());
|
||||
}
|
||||
|
||||
//a structure for comparing to save_info objects based on their modified time.
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
#include "replay.hpp"
|
||||
#include "scoped_resource.hpp"
|
||||
#include "sound.hpp"
|
||||
#include "statistics.hpp"
|
||||
#include "tooltips.hpp"
|
||||
|
||||
#include <iostream>
|
||||
|
@ -98,6 +99,8 @@ LEVEL_RESULT play_level(game_data& gameinfo, config& game_config,
|
|||
game_state& state_of_game,
|
||||
const std::vector<config*>& story)
|
||||
{
|
||||
const statistics::scenario_context statistics_context(translate_string_default((*level)["id"],(*level)["name"]));
|
||||
|
||||
const int num_turns = atoi(level->values["turns"].c_str());
|
||||
gamestatus status(*level,num_turns);
|
||||
|
||||
|
|
|
@ -1425,6 +1425,7 @@ void turn_info::do_recruit(const std::string& name)
|
|||
recruit_unit(map_,team_num_,units_,new_unit,last_hex_,&gui_);
|
||||
if(msg.empty()) {
|
||||
current_team.spend_gold(u_type->second.cost());
|
||||
statistics::recruit_unit(new_unit);
|
||||
} else {
|
||||
recorder.undo();
|
||||
gui::show_dialog(gui_,NULL,"",msg,gui::OK_ONLY);
|
||||
|
@ -1503,6 +1504,8 @@ void turn_info::recall()
|
|||
if(!err.empty()) {
|
||||
gui::show_dialog(gui_,NULL,"",err,gui::OK_ONLY);
|
||||
} else {
|
||||
statistics::recall_unit(state_of_game_.available_units[res]);
|
||||
|
||||
current_team.spend_gold(game_config::recall_cost);
|
||||
state_of_game_.available_units.erase(
|
||||
state_of_game_.available_units.begin()+res);
|
||||
|
@ -1632,12 +1635,90 @@ void turn_info::unit_list()
|
|||
}
|
||||
}
|
||||
|
||||
std::vector<std::string> turn_info::create_unit_table(const statistics::stats::str_int_map& m)
|
||||
{
|
||||
std::vector<std::string> table;
|
||||
for(statistics::stats::str_int_map::const_iterator i = m.begin(); i != m.end(); ++i) {
|
||||
const game_data::unit_type_map::const_iterator type = gameinfo_.unit_types.find(i->first);
|
||||
if(type == gameinfo_.unit_types.end()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
std::stringstream str;
|
||||
str << "&" << type->second.image() << "," << type->second.language_name() << "," << i->second << "\n";
|
||||
table.push_back(str.str());
|
||||
}
|
||||
|
||||
return table;
|
||||
}
|
||||
|
||||
void turn_info::show_statistics()
|
||||
{
|
||||
std::stringstream str;
|
||||
str << "Kills: " << statistics::calculate_stats(0,team_num_).killed.size() << "\n"
|
||||
<< "Deaths: " << statistics::calculate_stats(0,team_num_).deaths.size() << "\n";
|
||||
gui::show_dialog(gui_,NULL,"",str.str(),gui::OK_ONLY);
|
||||
const statistics::stats& stats = statistics::calculate_stats(0,team_num_);
|
||||
std::vector<std::string> items;
|
||||
|
||||
{
|
||||
std::stringstream str;
|
||||
str << string_table["total_recruits"] << "," << statistics::sum_str_int_map(stats.recruits);
|
||||
items.push_back(str.str());
|
||||
}
|
||||
|
||||
{
|
||||
std::stringstream str;
|
||||
str << string_table["total_recalls"] << "," << statistics::sum_str_int_map(stats.recalls);
|
||||
items.push_back(str.str());
|
||||
}
|
||||
|
||||
{
|
||||
std::stringstream str;
|
||||
str << string_table["total_advances"] << "," << statistics::sum_str_int_map(stats.advanced_to);
|
||||
items.push_back(str.str());
|
||||
}
|
||||
|
||||
{
|
||||
std::stringstream str;
|
||||
str << string_table["total_deaths"] << "," << statistics::sum_str_int_map(stats.deaths);
|
||||
items.push_back(str.str());
|
||||
}
|
||||
|
||||
{
|
||||
std::stringstream str;
|
||||
str << string_table["total_kills"] << "," << statistics::sum_str_int_map(stats.killed);
|
||||
items.push_back(str.str());
|
||||
}
|
||||
|
||||
const int res = gui::show_dialog(gui_,NULL,"",string_table["action_statistics"],gui::MESSAGE,&items);
|
||||
std::string title;
|
||||
items.clear();
|
||||
switch(res) {
|
||||
case 0:
|
||||
items = create_unit_table(stats.recruits);
|
||||
title = string_table["total_recruits"];
|
||||
break;
|
||||
case 1:
|
||||
items = create_unit_table(stats.recalls);
|
||||
title = string_table["total_recalls"];
|
||||
break;
|
||||
case 2:
|
||||
items = create_unit_table(stats.advanced_to);
|
||||
title = string_table["total_advances"];
|
||||
break;
|
||||
case 3:
|
||||
items = create_unit_table(stats.deaths);
|
||||
title = string_table["total_deaths"];
|
||||
break;
|
||||
case 4:
|
||||
items = create_unit_table(stats.killed);
|
||||
title = string_table["total_kills"];
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
if(items.empty() == false) {
|
||||
gui::show_dialog(gui_,NULL,"",title,gui::OK_ONLY,&items);
|
||||
show_statistics();
|
||||
}
|
||||
}
|
||||
|
||||
unit_map::iterator turn_info::current_unit()
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
#include "key.hpp"
|
||||
#include "pathfind.hpp"
|
||||
#include "show_dialog.hpp"
|
||||
#include "statistics.hpp"
|
||||
#include "team.hpp"
|
||||
#include "unit_types.hpp"
|
||||
#include "unit.hpp"
|
||||
|
@ -114,6 +115,8 @@ private:
|
|||
unit_map::iterator current_unit();
|
||||
unit_map::const_iterator current_unit() const;
|
||||
|
||||
std::vector<std::string> create_unit_table(const statistics::stats::str_int_map& m);
|
||||
|
||||
game_data& gameinfo_;
|
||||
game_state& state_of_game_;
|
||||
gamestatus& status_;
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
#include "playturn.hpp"
|
||||
#include "replay.hpp"
|
||||
#include "show_dialog.hpp"
|
||||
#include "statistics.hpp"
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
|
@ -451,6 +452,8 @@ bool do_replay(display& disp, const gamemap& map, const game_data& gameinfo,
|
|||
throw replay::error();
|
||||
}
|
||||
|
||||
statistics::recruit_unit(new_unit);
|
||||
|
||||
current_team.spend_gold(u_type->second.cost());
|
||||
}
|
||||
|
||||
|
@ -465,6 +468,7 @@ bool do_replay(display& disp, const gamemap& map, const game_data& gameinfo,
|
|||
const gamemap::location loc(*child);
|
||||
|
||||
if(val >= 0 && val < int(state_of_game.available_units.size())) {
|
||||
statistics::recall_unit(state_of_game.available_units[val]);
|
||||
recruit_unit(map,team_num,units,state_of_game.available_units[val],loc);
|
||||
state_of_game.available_units.erase(state_of_game.available_units.begin()+val);
|
||||
current_team.spend_gold(game_config::recall_cost);
|
||||
|
|
|
@ -2,17 +2,45 @@
|
|||
|
||||
namespace {
|
||||
|
||||
//this variable is true whenever the statistics are mid-scenario. This means
|
||||
//a new scenario shouldn't be added to the master stats record.
|
||||
bool mid_scenario = false;
|
||||
|
||||
typedef statistics::stats stats;
|
||||
|
||||
struct scenario_stats
|
||||
{
|
||||
scenario_stats(const std::string& name) : scenario_name(name)
|
||||
explicit scenario_stats(const std::string& name) : scenario_name(name)
|
||||
{}
|
||||
|
||||
explicit scenario_stats(const config& cfg);
|
||||
|
||||
config write() const;
|
||||
|
||||
std::vector<stats> team_stats;
|
||||
std::string scenario_name;
|
||||
};
|
||||
|
||||
scenario_stats::scenario_stats(const config& cfg)
|
||||
{
|
||||
scenario_name = cfg["scenario"];
|
||||
const config::child_list& teams = cfg.get_children("team");
|
||||
for(config::child_list::const_iterator i = teams.begin(); i != teams.end(); ++i) {
|
||||
team_stats.push_back(stats(**i));
|
||||
}
|
||||
}
|
||||
|
||||
config scenario_stats::write() const
|
||||
{
|
||||
config res;
|
||||
res["scenario"] = scenario_name;
|
||||
for(std::vector<stats>::const_iterator i = team_stats.begin(); i != team_stats.end(); ++i) {
|
||||
res.add_child("team",i->write());
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
std::vector<scenario_stats> master_stats;
|
||||
|
||||
stats& get_stats(int team)
|
||||
|
@ -30,6 +58,57 @@ stats& get_stats(int team)
|
|||
return team_stats[index];
|
||||
}
|
||||
|
||||
config write_str_int_map(const stats::str_int_map& m)
|
||||
{
|
||||
config res;
|
||||
for(stats::str_int_map::const_iterator i = m.begin(); i != m.end(); ++i) {
|
||||
char buf[50];
|
||||
sprintf(buf,"%d",i->second);
|
||||
res[i->first] = buf;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
stats::str_int_map read_str_int_map(const config& cfg)
|
||||
{
|
||||
stats::str_int_map m;
|
||||
for(string_map::const_iterator i = cfg.values.begin(); i != cfg.values.end(); ++i) {
|
||||
m[i->first] = atoi(i->second.c_str());
|
||||
}
|
||||
|
||||
return m;
|
||||
}
|
||||
|
||||
config write_battle_result_map(const stats::battle_result_map& m)
|
||||
{
|
||||
config res;
|
||||
for(stats::battle_result_map::const_iterator i = m.begin(); i != m.end(); ++i) {
|
||||
config& new_cfg = res.add_child("sequence");
|
||||
new_cfg = write_str_int_map(i->second);
|
||||
|
||||
char buf[50];
|
||||
sprintf(buf,"%d",i->first);
|
||||
new_cfg["_num"] = buf;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
stats::battle_result_map read_battle_result_map(const config& cfg)
|
||||
{
|
||||
stats::battle_result_map m;
|
||||
const config::child_list c = cfg.get_children("sequence");
|
||||
for(config::child_list::const_iterator i = c.begin(); i != c.end(); ++i) {
|
||||
config item = **i;
|
||||
const int key = atoi(item["_num"].c_str());
|
||||
item.values.erase("_num");
|
||||
m[key] = read_str_int_map(item);
|
||||
}
|
||||
|
||||
return m;
|
||||
}
|
||||
|
||||
void merge_str_int_map(stats::str_int_map& a, const stats::str_int_map& b)
|
||||
{
|
||||
for(stats::str_int_map::const_iterator i = b.begin(); i != b.end(); ++i) {
|
||||
|
@ -69,13 +148,90 @@ namespace statistics
|
|||
stats::stats() : recruit_cost(0), recall_cost(0), damage_inflicted(0), damage_taken(0)
|
||||
{}
|
||||
|
||||
stats::stats(const config& cfg)
|
||||
{
|
||||
read(cfg);
|
||||
}
|
||||
|
||||
config stats::write() const
|
||||
{
|
||||
config res;
|
||||
res.add_child("recruits",write_str_int_map(recruits));
|
||||
res.add_child("recalls",write_str_int_map(recalls));
|
||||
res.add_child("advances",write_str_int_map(advanced_to));
|
||||
res.add_child("deaths",write_str_int_map(deaths));
|
||||
res.add_child("killed",write_str_int_map(killed));
|
||||
res.add_child("attacks",write_battle_result_map(attacks));
|
||||
res.add_child("defends",write_battle_result_map(defends));
|
||||
|
||||
char buf[50];
|
||||
sprintf(buf,"%d",recruit_cost);
|
||||
res["recruit_cost"] = buf;
|
||||
|
||||
sprintf(buf,"%d",recall_cost);
|
||||
res["recall_cost"] = buf;
|
||||
|
||||
sprintf(buf,"%d",damage_inflicted);
|
||||
res["damage_inflicted"] = buf;
|
||||
|
||||
sprintf(buf,"%d",damage_taken);
|
||||
res["damage_taken"] = buf;
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
void stats::read(const config& cfg)
|
||||
{
|
||||
if(cfg.child("recruits")) {
|
||||
recruits = read_str_int_map(*cfg.child("recruits"));
|
||||
}
|
||||
|
||||
if(cfg.child("recalls")) {
|
||||
recalls = read_str_int_map(*cfg.child("recalls"));
|
||||
}
|
||||
|
||||
if(cfg.child("advances")) {
|
||||
advanced_to = read_str_int_map(*cfg.child("advances"));
|
||||
}
|
||||
|
||||
if(cfg.child("deaths")) {
|
||||
deaths = read_str_int_map(*cfg.child("deaths"));
|
||||
}
|
||||
|
||||
if(cfg.child("killed")) {
|
||||
killed = read_str_int_map(*cfg.child("killed"));
|
||||
}
|
||||
|
||||
if(cfg.child("recalls")) {
|
||||
recalls = read_str_int_map(*cfg.child("recalls"));
|
||||
}
|
||||
|
||||
if(cfg.child("attacks")) {
|
||||
attacks = read_battle_result_map(*cfg.child("attacks"));
|
||||
}
|
||||
|
||||
if(cfg.child("defends")) {
|
||||
attacks = read_battle_result_map(*cfg.child("attacks"));
|
||||
}
|
||||
|
||||
recruit_cost = atoi(cfg["recruit_cost"].c_str());
|
||||
recall_cost = atoi(cfg["recall_cost"].c_str());
|
||||
damage_inflicted = atoi(cfg["damage_inflicted"].c_str());
|
||||
damage_taken = atoi(cfg["damage_taken"].c_str());
|
||||
}
|
||||
|
||||
scenario_context::scenario_context(const std::string& name)
|
||||
{
|
||||
master_stats.push_back(scenario_stats(name));
|
||||
if(!mid_scenario || master_stats.empty()) {
|
||||
master_stats.push_back(scenario_stats(name));
|
||||
}
|
||||
|
||||
mid_scenario = true;
|
||||
}
|
||||
|
||||
scenario_context::~scenario_context()
|
||||
{
|
||||
mid_scenario = false;
|
||||
}
|
||||
|
||||
attack_context::attack_context(const unit& a, const unit& d, const battle_stats& stats)
|
||||
|
@ -179,4 +335,43 @@ stats calculate_stats(int category, int side)
|
|||
}
|
||||
}
|
||||
|
||||
config write_stats()
|
||||
{
|
||||
config res;
|
||||
res["mid_scenario"] = (mid_scenario ? "true" : "false");
|
||||
|
||||
for(std::vector<scenario_stats>::const_iterator i = master_stats.begin(); i != master_stats.end(); ++i) {
|
||||
res.add_child("scenario",i->write());
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
void read_stats(const config& cfg)
|
||||
{
|
||||
fresh_stats();
|
||||
mid_scenario = (cfg["mid_scenario"] == "true");
|
||||
|
||||
const config::child_list& scenarios = cfg.get_children("scenario");
|
||||
for(config::child_list::const_iterator i = scenarios.begin(); i != scenarios.end(); ++i) {
|
||||
master_stats.push_back(scenario_stats(**i));
|
||||
}
|
||||
}
|
||||
|
||||
void fresh_stats()
|
||||
{
|
||||
master_stats.clear();
|
||||
mid_scenario = false;
|
||||
}
|
||||
|
||||
int sum_str_int_map(const stats::str_int_map& m)
|
||||
{
|
||||
int res = 0;
|
||||
for(stats::str_int_map::const_iterator i = m.begin(); i != m.end(); ++i) {
|
||||
res += i->second;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
}
|
|
@ -9,6 +9,10 @@ namespace statistics
|
|||
struct stats
|
||||
{
|
||||
stats();
|
||||
explicit stats(const config& cfg);
|
||||
|
||||
config write() const;
|
||||
void read(const config& cfg);
|
||||
|
||||
typedef std::map<std::string,int> str_int_map;
|
||||
str_int_map recruits, recalls, advanced_to, deaths, killed;
|
||||
|
@ -25,6 +29,8 @@ namespace statistics
|
|||
|
||||
int damage_inflicted, damage_taken;
|
||||
};
|
||||
|
||||
int sum_str_int_map(const stats::str_int_map& m);
|
||||
|
||||
struct scenario_context
|
||||
{
|
||||
|
@ -58,8 +64,9 @@ namespace statistics
|
|||
|
||||
void advance_unit(const unit& u);
|
||||
|
||||
config write_stats(int team);
|
||||
void read_stats(int team, const config& cfg);
|
||||
config write_stats();
|
||||
void read_stats(const config& cfg);
|
||||
void fresh_stats();
|
||||
|
||||
std::vector<std::string> get_categories();
|
||||
stats calculate_stats(int category, int side);
|
||||
|
|
Loading…
Add table
Reference in a new issue