added basic statistics module

This commit is contained in:
Dave White 2004-03-22 21:44:38 +00:00
parent e96b98ed3a
commit 0bc0032b08
13 changed files with 337 additions and 8 deletions

View file

@ -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"

View file

@ -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));

View file

@ -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

View file

@ -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)
{}

View file

@ -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) {}

View file

@ -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;

View file

@ -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.

View file

@ -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);

View file

@ -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()

View file

@ -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_;

View file

@ -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);

View file

@ -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;
}
}

View file

@ -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);