GUI2: initial basic implementation of a GUI2 Attack Predictions dialog (WIP)

This commit is contained in:
Charles Dang 2016-12-18 11:10:24 +11:00
parent 76cfdd589d
commit fc91f5df10
7 changed files with 626 additions and 2 deletions

View file

@ -0,0 +1,338 @@
#textdomain wesnoth-lib
###
### Definition of the window to display various possible attack results
###
#define _GUI_SPACER_ROW
[row]
grow_factor = 0
[column]
#border = "all"
#border_size = 5
[spacer]
height = 15
[/spacer]
[/column]
[/row]
#enddef
#define _GUI_DATA_PANEL _HEADER _ID_PREFIX
[grid]
[row]
[column]
border = "all"
border_size = 5
horizontal_alignment = "left"
[label]
label = {_HEADER}
definition = "default_bold"
linked_group = "header"
[/label]
[/column]
[/row]
[row]
[column]
border = "all"
border_size = 5
horizontal_alignment = "left"
[label]
id = {_ID_PREFIX} + "_attack"
definition = "default"
linked_group = "header"
use_markup = "true"
[/label]
[/column]
[/row]
[row]
[column]
border = "all"
border_size = 5
horizontal_alignment = "left"
[label]
id = {_ID_PREFIX} + "_resis"
definition = "default"
linked_group = "header"
use_markup = "true"
[/label]
[/column]
[/row]
{_GUI_SPACER_ROW}
[row]
[column]
border = "all"
border_size = 5
horizontal_alignment = "left"
[label]
id = {_ID_PREFIX} + "_damage"
definition = "default"
linked_group = "header"
use_markup = "true"
[/label]
[/column]
[/row]
[row]
[column]
border = "all"
border_size = 5
horizontal_alignment = "left"
[label]
id = {_ID_PREFIX} + "_no_damage_chance"
definition = "default"
linked_group = "header"
use_markup = "true"
[/label]
[/column]
[/row]
[/grid]
#enddef
[window]
id = "attack_predictions"
description = "Statistics dialog."
[resolution]
definition = "default"
automatic_placement = "true"
vertical_placement = "center"
horizontal_placement = "center"
maximum_height = 400
[linked_group]
id = "header"
fixed_height = "true"
[/linked_group]
[linked_group]
id = "attack"
fixed_height = "true"
[/linked_group]
[linked_group]
id = "resis"
fixed_height = "true"
[/linked_group]
[linked_group]
id = "damage"
fixed_height = "true"
[/linked_group]
[linked_group]
id = "chance_unscathed"
fixed_height = "true"
[/linked_group]
[linked_group]
id = "bonuses"
fixed_height = "true"
[/linked_group]
[tooltip]
id = "tooltip"
[/tooltip]
[helptip]
id = "tooltip"
[/helptip]
[grid]
[row]
[column]
grow_factor = 1
border = "all"
border_size = 5
horizontal_alignment = "left"
[label]
id = "title"
definition = "title"
label = _ "Damage Calculations"
[/label]
[/column]
[/row]
[row]
grow_factor = 0
[column]
horizontal_grow = "true"
vertical_grow = "true"
[grid]
[row]
[column]
grow_factor = 0
horizontal_alignment = "left"
[grid]
[row]
[column]
border = "all"
border_size = 5
[spacer]
linked_group = "header"
[/spacer]
[/column]
[/row]
[row]
[column]
border = "all"
border_size = 5
horizontal_alignment = "left"
[label]
label = _ "Base damage"
definition = "default"
linked_group = "header"
[/label]
[/column]
[/row]
[row]
[column]
border = "all"
border_size = 5
horizontal_alignment = "left"
[label]
#label = _ "Opponent resistance against"
label = _ "Modifiers"
definition = "default"
linked_group = "header"
[/label]
[/column]
[/row]
{_GUI_SPACER_ROW}
[row]
[column]
border = "all"
border_size = 5
horizontal_alignment = "left"
[label]
label = _ "Total damage"
definition = "default"
linked_group = "header"
[/label]
[/column]
[/row]
[row]
[column]
border = "all"
border_size = 5
horizontal_alignment = "left"
[label]
label = _ "Chance of being unscathed"
definition = "default"
linked_group = "header"
[/label]
[/column]
[/row]
[/grid]
[/column]
[column]
grow_factor = 1
horizontal_grow = "true"
{_GUI_DATA_PANEL ( _ "Attacker") ("attacker")}
[/column]
[column]
grow_factor = 1
horizontal_grow = "true"
{_GUI_DATA_PANEL ( _ "Defender") ("defender")}
[/column]
[/row]
[/grid]
[/column]
[/row]
[row]
grow_factor = 0
[column]
border = "all"
border_size = 5
horizontal_alignment = "right"
[button]
id = "cancel"
label = _ "Close"
[/button]
[/column]
[/row]
[/grid]
[/resolution]
[/window]
#undef _GUI_DATA_PANEL
#undef _GUI_SPACER_ROW

View file

@ -525,6 +525,8 @@
<Unit filename="../../src/gui/dialogs/addon/uninstall_list.hpp" />
<Unit filename="../../src/gui/dialogs/advanced_graphics_options.cpp" />
<Unit filename="../../src/gui/dialogs/advanced_graphics_options.hpp" />
<Unit filename="../../src/gui/dialogs/attack_predictions.cpp" />
<Unit filename="../../src/gui/dialogs/attack_predictions.hpp" />
<Unit filename="../../src/gui/dialogs/campaign_difficulty.cpp" />
<Unit filename="../../src/gui/dialogs/campaign_difficulty.hpp" />
<Unit filename="../../src/gui/dialogs/campaign_selection.cpp" />

View file

@ -167,6 +167,7 @@ gui/dialogs/addon/filter_options.cpp
gui/dialogs/addon/manager.cpp
gui/dialogs/addon/uninstall_list.cpp
gui/dialogs/advanced_graphics_options.cpp
gui/dialogs/attack_predictions.cpp
gui/dialogs/campaign_difficulty.cpp
gui/dialogs/campaign_selection.cpp
gui/dialogs/campaign_settings.cpp

View file

@ -0,0 +1,205 @@
/*
Copyright (C) 2010 - 2016 by Mark de Wever <koraq@xs4all.nl>
Part of the Battle for Wesnoth Project http://www.wesnoth.org/
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY.
See the COPYING file for more details.
*/
#define GETTEXT_DOMAIN "wesnoth-lib"
#include "gui/dialogs/attack_predictions.hpp"
#include "attack_prediction.hpp"
#include "color.hpp"
#include "font/standard_colors.hpp"
#include "font/text_formatting.hpp"
#include "formatter.hpp"
#include "game_config.hpp"
#include "gui/auxiliary/find_widget.hpp"
#include "gui/widgets/label.hpp"
#include "gui/widgets/image.hpp"
#include "gui/widgets/settings.hpp"
#include "gui/widgets/window.hpp"
#include "gettext.hpp"
#include "language.hpp"
#include "units/abilities.hpp"
#include "units/unit.hpp"
#include <iomanip>
namespace gui2
{
namespace dialogs
{
REGISTER_DIALOG(attack_predictions)
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())
{
}
void attack_predictions::pre_show(window& window)
{
set_data(window, attacker_data_, defender_data_);
set_data(window, defender_data_, attacker_data_);
}
static std::string get_probability_string(const double prob)
{
std::ostringstream ss;
if(prob > 0.9995) {
ss << "100%";
} else {
ss << std::fixed << std::setprecision(1) << 100.0 * prob << '%';
}
return ss.str();
}
void attack_predictions::set_data(window& window, const combatant_data& attacker, const combatant_data& defender)
{
const std::string widget_id_prefix = attacker.stats.is_attacker ? "attacker_" : "defender_";
const auto set_label_helper = [&](const std::string& id, const std::string& value) {
find_widget<label>(&window, widget_id_prefix + id, false).set_label(value);
};
std::stringstream ss;
// With a weapon.
if(attacker.stats.weapon) {
// Set specials context (for safety, it should not have changed normally).
const attack_type* weapon = attacker.stats.weapon;
weapon->set_specials_context(attacker.unit.get_location(), defender.unit.get_location(), attacker.stats.is_attacker, defender.stats.weapon);
// Get damage modifiers.
unit_ability_list dmg_specials = weapon->get_specials("damage");
unit_abilities::effect dmg_effect(dmg_specials, weapon->damage(), attacker.stats.backstab_pos);
// Get the SET damage modifier, if any.
const unit_abilities::individual_effect* set_dmg_effect = &*(
std::find_if(dmg_effect.begin(), dmg_effect.end(), [](const unit_abilities::individual_effect& e) { return e.type == unit_abilities::SET; })
);
// Either user the SET modifier or the base weapon damage.
if(!set_dmg_effect) {
// TODO: formatting
ss << weapon->name() << ": " << weapon->damage();
} else {
assert(set_dmg_effect->ability);
ss << (*set_dmg_effect->ability)["name"] << ": " << set_dmg_effect->value;
}
// Process the ADD damage modifiers.
for(const auto& e : dmg_effect) {
if(e.type == unit_abilities::ADD) {
ss << "\n";
ss << (*e.ability)["name"] << ": ";
if(e.value >= 0) {
ss << '+';
}
ss << e.value;
}
}
// Process the MUL damage modifiers.
for(const auto& e : dmg_effect) {
if(e.type == unit_abilities::MUL) {
ss << "\n";
ss << (*e.ability)["name"] << ": " << font::unicode_multiplication_sign << (e.value / 100);
if(e.value % 100) {
ss << "." << ((e.value % 100) / 10);
if(e.value % 10) {
ss << (e.value % 10);
}
}
}
}
set_label_helper("attack", ss.str());
#if 0
// Time of day modifier.
int tod_modifier = combat_modifier(resources::gameboard->units(), resources::gameboard->map(), attacker.unit.get_location(), u.alignment(), u.is_fearless());
if(tod_modifier != 0) {
left_strings.push_back(_("Time of day"));
str.str("");
ss << utils::signed_percent(tod_modifier);
right_strings.push_back(str.str());
}
// Leadership bonus.
int leadership_bonus = 0;
under_leadership(resources::gameboard->units(), attacker.unit.get_location(), &leadership_bonus);
if(leadership_bonus != 0) {
left_strings.push_back(_("Leadership"));
str.str("");
ss << utils::signed_percent(leadership_bonus);
right_strings.push_back(str.str());
}
#endif
ss.str("");
// Resistance modifier.
const int resistance_modifier = defender.unit.damage_from(*weapon, !attacker.stats.is_attacker, defender.unit.get_location());
if(resistance_modifier != 100) {
ss << string_table["type_" + weapon->type()] << ": ";
ss << font::unicode_multiplication_sign << (resistance_modifier / 100) << "." << ((resistance_modifier % 100) / 10);
set_label_helper("resis", ss.str());
}
// Slowed penalty.
#if 0
if(attacker.stats.is_slowed) {
left_strings.push_back(_("Slowed"));
right_strings.push_back("/ 2");
}
#endif
ss.str("");
// Total damage.
const int base_damage = weapon->damage();
color_t dmg_color = font::weapon_color;
if(attacker.stats.damage > base_damage) {
dmg_color = font::good_dmg_color;
} else if(attacker.stats.damage < base_damage) {
dmg_color = font::bad_dmg_color;
}
const color_t cth_color = game_config::red_to_green(attacker.stats.chance_to_hit);
ss << font::span_color(dmg_color) << attacker.stats.damage << "</span>"
<< font::weapon_numbers_sep << attacker.stats.num_blows << " ("
<< font::span_color(cth_color) << attacker.stats.chance_to_hit << "%</span>)";
set_label_helper("damage", ss.str());
} else {
// Without a weapon.
set_label_helper("attack", _("No usable weapon"));
}
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());
}
}
}

View file

@ -0,0 +1,72 @@
/*
Copyright (C) 2010 - 2016 by Mark de Wever <koraq@xs4all.nl>
Part of the Battle for Wesnoth Project http://www.wesnoth.org/
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY.
See the COPYING file for more details.
*/
#ifndef GUI_DIALOGS_ATTACK_PREDICTIONS_HPP_INCLUDED
#define GUI_DIALOGS_ATTACK_PREDICTIONS_HPP_INCLUDED
#include "actions/attack.hpp"
#include "gui/dialogs/modal_dialog.hpp"
#include "units/map.hpp"
class battle_context;
class battle_context_unit_stats;
class CVideo;
class map_location;
struct combatant;
namespace gui2
{
namespace dialogs
{
class attack_predictions : public modal_dialog
{
public:
attack_predictions(battle_context& bc, const unit& attacker, const unit& defender);
static void display(battle_context& bc, const unit& attacker, const unit& defender, CVideo& video)
{
attack_predictions(bc, attacker, defender).show(video);
}
private:
/** Inherited from modal_dialog, implemented by REGISTER_DIALOG. */
virtual const std::string& window_id() const;
/** Inherited from modal_dialog. */
void pre_show(window& window);
struct combatant_data
{
combatant_data(const unit& unit, const combatant& combatant, const battle_context_unit_stats& stats)
: stats(stats)
, combatant(combatant)
, unit(unit)
{}
const battle_context_unit_stats& stats;
const combatant& combatant;
const unit& unit;
};
void set_data(window& window, const combatant_data& attacker, const combatant_data& defender);
combatant_data attacker_data_;
combatant_data defender_data_;
};
} // namespace dialogs
} // namespace gui2
#endif

View file

@ -20,6 +20,7 @@
#include "font/text_formatting.hpp"
#include "gui/auxiliary/find_widget.hpp"
#include "gui/dialogs/attack_predictions.hpp"
#include "gui/widgets/button.hpp"
#include "gui/widgets/label.hpp"
#include "gui/widgets/image.hpp"
@ -90,9 +91,11 @@ unit_attack::unit_attack(const unit_map::iterator& attacker_itor,
void unit_attack::damage_calc_callback(window& window)
{
const size_t index
= find_widget<listbox>(&window, "weapon_list", false).get_selected_row();
const size_t index = find_widget<listbox>(&window, "weapon_list", false).get_selected_row();
attack_predictions::display(weapons_[index], *attacker_itor_, *defender_itor_, window.video());
// TODO: remove when the GUI2 dialog is complete
battle_prediction_pane battle_pane(weapons_[index], (*attacker_itor_).get_location(), (*defender_itor_).get_location());
std::vector<gui::preview_pane*> preview_panes = {&battle_pane};

View file

@ -33,6 +33,7 @@
#include "gui/dialogs/addon/connect.hpp"
#include "gui/dialogs/addon/manager.hpp"
#include "gui/dialogs/advanced_graphics_options.hpp"
#include "gui/dialogs/attack_predictions.hpp"
#include "gui/dialogs/campaign_difficulty.hpp"
#include "gui/dialogs/campaign_selection.hpp"
#include "gui/dialogs/campaign_settings.hpp"
@ -388,6 +389,7 @@ BOOST_AUTO_TEST_CASE(test_gui2)
test<addon_connect>();
//test<addon_manager>();
test<advanced_graphics_options>();
//test<attack_predictions>();
test<campaign_difficulty>();
test<campaign_selection>();
test<campaign_settings>();
@ -502,6 +504,7 @@ BOOST_AUTO_TEST_CASE(test_gui2)
"mp_staging",
"mp_join_game",
"terrain_layers",
"attack_predictions",
};
std::sort(list.begin(), list.end());
std::sort(omitted.begin(), omitted.end());