Attack Predictions: initial implementation of HP probability graphs

This commit is contained in:
Charles Dang 2017-03-21 15:18:29 +11:00
parent 55b90a128b
commit 32118d59c4
3 changed files with 243 additions and 6 deletions

View file

@ -115,6 +115,47 @@
[/grid]
#enddef
#define _GUI_HP_GRAPH _ID_PREFIX
[drawing]
id = {_ID_PREFIX} + "_hp_graph"
definition = "default"
width = 300
height = 200
[draw]
[rectangle]
x = 0
y = 0
w = "(width)"
h = "(height)"
fill_color = "25, 25, 25, 255"
border_thickness = 2
border_color = "183, 193, 183, 255"
[/rectangle]
[rectangle]
x = "(hp_column_width)"
y = 0
w = 2
h = "(height - 2)"
fill_color = "183, 193, 183, 255"
[/rectangle]
[rectangle]
x = "(width - chance_column_width - 2)"
y = 0
w = 2
h = "(height - 2)"
fill_color = "183, 193, 183, 255"
[/rectangle]
[/draw]
[/drawing]
#enddef
[window]
id = "attack_predictions"
description = "Statistics dialog."
@ -126,7 +167,7 @@
vertical_placement = "center"
horizontal_placement = "center"
maximum_height = 400
#maximum_height = 400
[linked_group]
id = "header"
@ -311,6 +352,44 @@
[/row]
[row]
grow_factor = 1
[column]
horizontal_grow = true
[grid]
[row]
[column]
grow_factor = 1
border = "all"
border_size = 5
horizontal_grow = true
vertical_grow = true
{_GUI_HP_GRAPH ("attacker")}
[/column]
[column]
grow_factor = 1
border = "all"
border_size = 5
horizontal_grow = true
vertical_grow = true
{_GUI_HP_GRAPH ("defender")}
[/column]
[/row]
[/grid]
[/column]
[/row]
[row]
grow_factor = 0
@ -334,5 +413,6 @@
[/window]
#undef _GUI_HP_GRAPH
#undef _GUI_DATA_PANEL
#undef _GUI_SPACER_ROW

View file

@ -18,12 +18,14 @@
#include "attack_prediction.hpp"
#include "color.hpp"
#include "config.hpp"
#include "font/text_formatting.hpp"
#include "formatter.hpp"
#include "formula/variant.hpp"
#include "game_config.hpp"
#include "gui/auxiliary/find_widget.hpp"
#include "gui/widgets/drawing.hpp"
#include "gui/widgets/label.hpp"
#include "gui/widgets/image.hpp"
#include "gui/widgets/settings.hpp"
#include "gui/widgets/window.hpp"
#include "gettext.hpp"
@ -40,6 +42,9 @@ namespace dialogs
REGISTER_DIALOG(attack_predictions)
const unsigned int attack_predictions::graph_width = 300;
const unsigned int attack_predictions::graph_height = 200;
attack_predictions::attack_predictions(battle_context& bc, const unit& attacker, const unit& defender)
: attacker_data_(attacker, bc.get_attacker_combatant(), bc.get_attacker_stats())
, defender_data_(defender, bc.get_defender_combatant(), bc.get_defender_stats())
@ -196,8 +201,148 @@ void attack_predictions::set_data(window& window, const combatant_data& attacker
const color_t ndc_color = game_config::red_to_green(attacker.combatant_.untouched * 10);
// Unscathed probability.
set_label_helper("no_damage_chance", (formatter() << font::span_color(ndc_color) << get_probability_string(attacker.combatant_.untouched) << "</span>").str());
set_label_helper("no_damage_chance",
(formatter() << font::span_color(ndc_color) << get_probability_string(attacker.combatant_.untouched) << "</span>").str());
drawing& graph_widget = find_widget<drawing>(&window, widget_id_prefix + "hp_graph", false);
draw_hp_graph(graph_widget, attacker, defender);
}
void attack_predictions::draw_hp_graph(drawing& hp_graph, const combatant_data& attacker, const combatant_data& defender)
{
// Font size. If you change this, you must update the separator space.
const int fs = font::SIZE_SMALL;
// Space before HP separator.
const int hp_sep = 24 + 6;
// Space after percentage separator.
const int percent_sep = 43 + 6;
// Bar space between both separators.
const int bar_space = graph_width - hp_sep - percent_sep - 4;
// Set some variables for the WML portion of the graph to use.
canvas& hp_graph_canvas = hp_graph.get_drawing_canvas();
hp_graph_canvas.set_variable("hp_column_width", variant(hp_sep));
hp_graph_canvas.set_variable("chance_column_width", variant(percent_sep));
config cfg, shape;
int i = 0;
// Draw the rows (lower HP values are at the bottom).
for(const auto& probability : get_hitpoint_probabilities(attacker.combatant_.hp_dist)) {
// Get the HP and probability.
int hp; double prob;
std::tie(hp, prob) = probability;
color_t row_color;
// Death line is red.
if(hp == 0) {
row_color = {229, 0, 0};
}
// Below current hitpoints value is orange.
else if(hp < static_cast<int>(attacker.stats_.hp)) {
// Stone is grey.
if(defender.stats_.petrifies) {
row_color = {154, 154, 154};
} else {
row_color = {244, 201, 0};
}
}
// Current hitpoints value and above is green.
else {
row_color = {8, 202, 0};
}
shape["text"] = hp;
shape["x"] = 2;
shape["y"] = 2 + (fs + 2) * i;
shape["w"] = "(text_width)";
shape["h"] = "(text_height)";
shape["font_size"] = 12;
shape["color"] = "255, 255, 255, 255";
shape["text_alignment"] = "(text_alignment)";
cfg.add_child("text", shape);
shape.clear();
shape["text"] = get_probability_string(prob);
shape["x"] = graph_width - percent_sep + 2;
shape["y"] = 2 + (fs + 2) * i;
shape["w"] = "(text_width)";
shape["h"] = "(text_height)";
shape["font_size"] = 12;
shape["color"] = "255, 255, 255, 255";
shape["text_alignment"] = "(text_alignment)";
cfg.add_child("text", shape);
const int bar_len = std::max(static_cast<int>((prob * (bar_space - 4)) + 0.5), 2);
const SDL_Rect bar_rect_1 {
hp_sep + 4,
6 + (fs + 2) * i,
bar_len,
8
};
shape.clear();
shape["x"] = bar_rect_1.x;
shape["y"] = bar_rect_1.y;
shape["w"] = bar_rect_1.w;
shape["h"] = bar_rect_1.h;
shape["fill_color"] = row_color.to_rgba_string();
cfg.add_child("rectangle", shape);
++i;
}
hp_graph.append_drawing_data(cfg);
}
hp_probability_vector attack_predictions::get_hitpoint_probabilities(const std::vector<double>& hp_dist)
{
hp_probability_vector res;
// First, we sort the probabilities in ascending order.
std::vector<std::pair<double, int>> prob_hp_vector;
int i;
for(i = 0; i < static_cast<int>(hp_dist.size()); i++) {
double prob = hp_dist[i];
// We keep only values above 0.1%.
if(prob > 0.001) {
prob_hp_vector.push_back(std::pair<double, int>(prob, i));
}
}
std::sort(prob_hp_vector.begin(), prob_hp_vector.end());
// We store a few of the highest probability hitpoint values.
int nb_elem = std::min<int>(10, prob_hp_vector.size());
//int nb_elem = prob_hp_vector.size();
for(i = prob_hp_vector.size() - nb_elem; i < static_cast<int>(prob_hp_vector.size()); i++) {
res.push_back(std::pair<int, double>
(prob_hp_vector[i].second, prob_hp_vector[i].first));
}
// Then, we sort the hitpoint values in ascending order.
std::sort(res.begin(), res.end());
return res;
}
} // namespace dialogs
} // namespace gui2

View file

@ -19,17 +19,22 @@
#include "gui/dialogs/modal_dialog.hpp"
#include "units/map.hpp"
class battle_context;
class battle_context;
class CVideo;
struct battle_context_unit_stats;
class CVideo;
struct map_location;
struct combatant;
struct map_location;
namespace gui2
{
class drawing;
namespace dialogs
{
using hp_probability_vector = std::vector<std::pair<int, double>>;
class attack_predictions : public modal_dialog
{
public:
@ -62,6 +67,13 @@ private:
void set_data(window& window, const combatant_data& attacker, const combatant_data& defender);
hp_probability_vector get_hitpoint_probabilities(const std::vector<double>& hp_dist);
static const unsigned int graph_width;
static const unsigned int graph_height;
void draw_hp_graph(drawing& hp_graph, const combatant_data& attacker, const combatant_data& defender);
combatant_data attacker_data_;
combatant_data defender_data_;
};