Added support for automatically generated topics and sections...
...in the help browser. About added as the first generated topic, takes information from about.cpp.
This commit is contained in:
parent
20c627432f
commit
5b8bfeedce
5 changed files with 175 additions and 42 deletions
|
@ -1,7 +1,7 @@
|
||||||
[help]
|
[help]
|
||||||
[toplevel]
|
[toplevel]
|
||||||
sections=introduction,gameplay
|
sections=introduction,gameplay
|
||||||
#topics=about
|
topics=about
|
||||||
[/toplevel]
|
[/toplevel]
|
||||||
|
|
||||||
[section]
|
[section]
|
||||||
|
@ -14,6 +14,7 @@ topics=introduction
|
||||||
id=gameplay
|
id=gameplay
|
||||||
title="Gameplay"
|
title="Gameplay"
|
||||||
topics=fundamentals,recruit_and_recall,movement,combat,damage_types_and_resistance,time_of_day,experience_and_advancement,healing,income_and_upkeep,wrap_up
|
topics=fundamentals,recruit_and_recall,movement,combat,damage_types_and_resistance,time_of_day,experience_and_advancement,healing,income_and_upkeep,wrap_up
|
||||||
|
generator=foo
|
||||||
[/section]
|
[/section]
|
||||||
|
|
||||||
[topic]
|
[topic]
|
||||||
|
@ -77,7 +78,7 @@ There are two exceptions to this rule: <ref>dst=magical_attacks text='Magical at
|
||||||
|
|
||||||
Each blow which hits causes a base amount of damage depending on the attack type. For instance, an Elvish Fighter with a 5-4 sword attack causes a base of 5 damage. This is usually modified by two things: <ref>dst=damage_type_and_resistance text=Resistance</ref> and <ref>dst=time_of_day text='Time of Day'</ref>, both of which are explained below.
|
Each blow which hits causes a base amount of damage depending on the attack type. For instance, an Elvish Fighter with a 5-4 sword attack causes a base of 5 damage. This is usually modified by two things: <ref>dst=damage_type_and_resistance text=Resistance</ref> and <ref>dst=time_of_day text='Time of Day'</ref>, both of which are explained below.
|
||||||
|
|
||||||
A few units have special abilities which affect damage dealt in combat. The most common of these is <ref>dst=ability_charg text=Charge</ref>, which doubles the damage dealt by both attacker and defender when the unit with Charge attacks."
|
A few units have special abilities which affect damage dealt in combat. The most common of these is <ref>dst=ability_charge text=Charge</ref>, which doubles the damage dealt by both attacker and defender when the unit with Charge attacks."
|
||||||
[/topic]
|
[/topic]
|
||||||
|
|
||||||
[topic]
|
[topic]
|
||||||
|
@ -165,8 +166,21 @@ text="This concludes the fundamentals of Wesnoth. You might want to read up on B
|
||||||
id=about
|
id=about
|
||||||
title="About"
|
title="About"
|
||||||
# Does not work yet, but soon! :)
|
# Does not work yet, but soon! :)
|
||||||
generator="generate_about"
|
generator=about
|
||||||
[/topic]
|
[/topic]
|
||||||
|
|
||||||
|
#[section]
|
||||||
|
#id=traits
|
||||||
|
#title="Traits"
|
||||||
|
#generator=traits
|
||||||
|
#topics=trait_introduction
|
||||||
|
#[/section]
|
||||||
|
|
||||||
|
#[topic]
|
||||||
|
#id=trait_introduction
|
||||||
|
#title=Introduction
|
||||||
|
#text="Must units have two traits. Traits are attributes of the unit that modify its attributes slightly. The available traits are listed in this section."
|
||||||
|
#[/topic]
|
||||||
|
|
||||||
[/help]
|
[/help]
|
||||||
|
|
||||||
|
|
|
@ -23,33 +23,11 @@
|
||||||
#include "widgets/button.hpp"
|
#include "widgets/button.hpp"
|
||||||
|
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <string>
|
|
||||||
|
|
||||||
namespace about
|
namespace about
|
||||||
{
|
{
|
||||||
|
|
||||||
void show_about(display& disp)
|
std::vector<std::string> get_text() {
|
||||||
{
|
|
||||||
SDL_Rect rect = {0, 0, disp.x(), disp.y()};
|
|
||||||
|
|
||||||
const surface_restorer restorer(&disp.video(), rect);
|
|
||||||
|
|
||||||
// Clear the screen
|
|
||||||
gui::draw_solid_tinted_rectangle(0,0,disp.x()-1,disp.y()-1,
|
|
||||||
0,0,0,1.0,disp.video().getSurface());
|
|
||||||
update_whole_screen();
|
|
||||||
|
|
||||||
const scoped_sdl_surface map_image(image::get_image(game_config::map_image,image::UNSCALED));
|
|
||||||
SDL_Rect map_rect;
|
|
||||||
map_rect.x = disp.x()/2 - map_image->w/2;
|
|
||||||
map_rect.y = disp.y()/2 - map_image->h/2;
|
|
||||||
map_rect.w = map_image->w;
|
|
||||||
map_rect.h = map_image->h;
|
|
||||||
|
|
||||||
gui::button close(disp,string_table["close_button"]);
|
|
||||||
close.set_location((disp.x()/2)-(close.width()/2), map_rect.y+map_rect.h+15);
|
|
||||||
|
|
||||||
|
|
||||||
std::vector<std::string> text;
|
std::vector<std::string> text;
|
||||||
text.push_back(" ");
|
text.push_back(" ");
|
||||||
text.push_back("- ");
|
text.push_back("- ");
|
||||||
|
@ -225,6 +203,31 @@ void show_about(display& disp)
|
||||||
text.push_back("- Frédéric Wagner");
|
text.push_back("- Frédéric Wagner");
|
||||||
text.push_back("- Jan Zvánovec (jaz)");
|
text.push_back("- Jan Zvánovec (jaz)");
|
||||||
text.push_back("+ ");
|
text.push_back("+ ");
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
|
||||||
|
void show_about(display& disp)
|
||||||
|
{
|
||||||
|
std::vector<std::string> text = get_text();
|
||||||
|
SDL_Rect rect = {0, 0, disp.x(), disp.y()};
|
||||||
|
|
||||||
|
const surface_restorer restorer(&disp.video(), rect);
|
||||||
|
|
||||||
|
// Clear the screen
|
||||||
|
gui::draw_solid_tinted_rectangle(0,0,disp.x()-1,disp.y()-1,
|
||||||
|
0,0,0,1.0,disp.video().getSurface());
|
||||||
|
update_whole_screen();
|
||||||
|
|
||||||
|
const scoped_sdl_surface map_image(image::get_image(game_config::map_image,image::UNSCALED));
|
||||||
|
SDL_Rect map_rect;
|
||||||
|
map_rect.x = disp.x()/2 - map_image->w/2;
|
||||||
|
map_rect.y = disp.y()/2 - map_image->h/2;
|
||||||
|
map_rect.w = map_image->w;
|
||||||
|
map_rect.h = map_image->h;
|
||||||
|
|
||||||
|
gui::button close(disp,string_table["close_button"]);
|
||||||
|
close.set_location((disp.x()/2)-(close.width()/2), map_rect.y+map_rect.h+15);
|
||||||
|
|
||||||
|
|
||||||
//substitute in the correct control characters for '+' and '-'
|
//substitute in the correct control characters for '+' and '-'
|
||||||
for(std::vector<std::string>::iterator itor = text.begin(); itor != text.end(); ++itor) {
|
for(std::vector<std::string>::iterator itor = text.begin(); itor != text.end(); ++itor) {
|
||||||
|
|
|
@ -15,11 +15,14 @@
|
||||||
#define ABOUT_H_INCLUDED
|
#define ABOUT_H_INCLUDED
|
||||||
|
|
||||||
#include "display.hpp"
|
#include "display.hpp"
|
||||||
|
#include <vector>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
namespace about
|
namespace about
|
||||||
{
|
{
|
||||||
|
|
||||||
void show_about(display& disp);
|
void show_about(display& disp);
|
||||||
|
std::vector<std::string> get_text();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
135
src/help.cpp
135
src/help.cpp
|
@ -12,12 +12,14 @@
|
||||||
|
|
||||||
#include "help.hpp"
|
#include "help.hpp"
|
||||||
|
|
||||||
|
#include "about.hpp"
|
||||||
#include "cursor.hpp"
|
#include "cursor.hpp"
|
||||||
#include "events.hpp"
|
#include "events.hpp"
|
||||||
#include "font.hpp"
|
#include "font.hpp"
|
||||||
#include "language.hpp"
|
#include "language.hpp"
|
||||||
#include "preferences.hpp"
|
#include "preferences.hpp"
|
||||||
#include "show_dialog.hpp"
|
#include "show_dialog.hpp"
|
||||||
|
#include "unit.hpp"
|
||||||
#include "util.hpp"
|
#include "util.hpp"
|
||||||
|
|
||||||
#include "SDL_ttf.h"
|
#include "SDL_ttf.h"
|
||||||
|
@ -56,8 +58,53 @@ namespace {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Class to be used as a function object when generating the about
|
||||||
|
/// text. Translate the about dialog formatting to format suitable
|
||||||
|
/// for the help dialog.
|
||||||
|
class about_text_formatter {
|
||||||
|
public:
|
||||||
|
about_text_formatter() : text_started_(false) {}
|
||||||
|
std::string operator()(const std::string &s) {
|
||||||
|
std::string res = s;
|
||||||
|
if (res.size() > 0) {
|
||||||
|
bool header = false;
|
||||||
|
// Format + as headers, and the rest as normal text.
|
||||||
|
if (res[0] == '+') {
|
||||||
|
header = true;
|
||||||
|
res.erase(res.begin());
|
||||||
|
}
|
||||||
|
else if (res[0] == '-') {
|
||||||
|
res.erase(res.begin());
|
||||||
|
}
|
||||||
|
// There is a bunch of empty rows in the start in about.cpp,
|
||||||
|
// we do not want to show these here. Thus, if we still
|
||||||
|
// encounter one of those, return an empty string that will
|
||||||
|
// be removed totally at a later stage.
|
||||||
|
if (!text_started_ && res.find_first_not_of(' ') != std::string::npos) {
|
||||||
|
text_started_ = true;
|
||||||
|
}
|
||||||
|
if (text_started_) {
|
||||||
|
std::stringstream ss;
|
||||||
|
if (header) {
|
||||||
|
ss << "<header>text='" << res << "'</header>";
|
||||||
|
res = ss.str();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
res = "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
bool text_started_;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace help {
|
||||||
|
|
||||||
void parse_config_internal(const config *help_cfg, const config *section_cfg,
|
void parse_config_internal(const config *help_cfg, const config *section_cfg,
|
||||||
help::section &sec, int level=0) {
|
section &sec, int level) {
|
||||||
if (level > max_section_level) {
|
if (level > max_section_level) {
|
||||||
std::cerr << "Maximum section depth has been reached. Maybe circular dependency?"
|
std::cerr << "Maximum section depth has been reached. Maybe circular dependency?"
|
||||||
<< std::endl;
|
<< std::endl;
|
||||||
|
@ -69,7 +116,7 @@ namespace {
|
||||||
if (!is_valid_id(id)) {
|
if (!is_valid_id(id)) {
|
||||||
std::stringstream ss;
|
std::stringstream ss;
|
||||||
ss << "Invalid ID, used for internal purpose: '" << id << "'";
|
ss << "Invalid ID, used for internal purpose: '" << id << "'";
|
||||||
throw help::parse_error(ss.str());
|
throw parse_error(ss.str());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const std::string title = level == 0 ? "" : (*section_cfg)["title"];
|
const std::string title = level == 0 ? "" : (*section_cfg)["title"];
|
||||||
|
@ -80,7 +127,7 @@ namespace {
|
||||||
for (it = sections.begin(); it != sections.end(); it++) {
|
for (it = sections.begin(); it != sections.end(); it++) {
|
||||||
config const *child_cfg = help_cfg->find_child("section", "id", *it);
|
config const *child_cfg = help_cfg->find_child("section", "id", *it);
|
||||||
if (child_cfg != NULL) {
|
if (child_cfg != NULL) {
|
||||||
help::section child_section;
|
section child_section;
|
||||||
parse_config_internal(help_cfg, child_cfg, child_section, level + 1);
|
parse_config_internal(help_cfg, child_cfg, child_section, level + 1);
|
||||||
sec.add_section(child_section);
|
sec.add_section(child_section);
|
||||||
}
|
}
|
||||||
|
@ -88,20 +135,25 @@ namespace {
|
||||||
std::stringstream ss;
|
std::stringstream ss;
|
||||||
ss << "Help-section '" << *it << "' referenced from '"
|
ss << "Help-section '" << *it << "' referenced from '"
|
||||||
<< id << "' but could not be found.";
|
<< id << "' but could not be found.";
|
||||||
throw help::parse_error(ss.str());
|
throw parse_error(ss.str());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
const std::vector<section> generated_sections =
|
||||||
|
generate_sections((*section_cfg)["generator"]);
|
||||||
|
std::transform(generated_sections.begin(), generated_sections.end(),
|
||||||
|
std::back_inserter(sec.sections), create_section());
|
||||||
const std::vector<std::string> topics = config::quoted_split((*section_cfg)["topics"]);
|
const std::vector<std::string> topics = config::quoted_split((*section_cfg)["topics"]);
|
||||||
// Find all topics in this section.
|
// Find all topics in this section.
|
||||||
for (it = topics.begin(); it != topics.end(); it++) {
|
for (it = topics.begin(); it != topics.end(); it++) {
|
||||||
config const *topic_cfg = help_cfg->find_child("topic", "id", *it);
|
config const *topic_cfg = help_cfg->find_child("topic", "id", *it);
|
||||||
if (topic_cfg != NULL) {
|
if (topic_cfg != NULL) {
|
||||||
help::topic child_topic((*topic_cfg)["title"], (*topic_cfg)["id"],
|
std::string text = (*topic_cfg)["text"];
|
||||||
(*topic_cfg)["text"]);
|
text += generate_topic_text((*topic_cfg)["generator"]);
|
||||||
|
topic child_topic((*topic_cfg)["title"], (*topic_cfg)["id"], text);
|
||||||
if (!is_valid_id(child_topic.id)) {
|
if (!is_valid_id(child_topic.id)) {
|
||||||
std::stringstream ss;
|
std::stringstream ss;
|
||||||
ss << "Invalid ID, used for internal purpose: '" << id << "'";
|
ss << "Invalid ID, used for internal purpose: '" << id << "'";
|
||||||
throw help::parse_error(ss.str());
|
throw parse_error(ss.str());
|
||||||
}
|
}
|
||||||
sec.topics.push_back(child_topic);
|
sec.topics.push_back(child_topic);
|
||||||
}
|
}
|
||||||
|
@ -109,14 +161,16 @@ namespace {
|
||||||
std::stringstream ss;
|
std::stringstream ss;
|
||||||
ss << "Help-topic '" << *it << "' referenced from '" << id
|
ss << "Help-topic '" << *it << "' referenced from '" << id
|
||||||
<< "' but could not be found." << std::endl;
|
<< "' but could not be found." << std::endl;
|
||||||
throw help::parse_error(ss.str());
|
throw parse_error(ss.str());
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
const std::vector<topic> generated_topics =
|
||||||
|
generate_topics((*section_cfg)["generator"]);
|
||||||
|
std::copy(generated_topics.begin(), generated_topics.end(),
|
||||||
|
std::back_inserter(sec.topics));
|
||||||
|
|
||||||
namespace help {
|
}
|
||||||
|
}
|
||||||
|
|
||||||
section parse_config(const config *cfg) {
|
section parse_config(const config *cfg) {
|
||||||
section sec;
|
section sec;
|
||||||
|
@ -127,6 +181,57 @@ section parse_config(const config *cfg) {
|
||||||
return sec;
|
return sec;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
std::vector<section> generate_sections(const std::string &generator) {
|
||||||
|
std::vector<section> empty_vec;
|
||||||
|
if (generator == "") {
|
||||||
|
return empty_vec;
|
||||||
|
}
|
||||||
|
|
||||||
|
return empty_vec;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<topic> generate_topics(const std::string &generator) {
|
||||||
|
std::vector<topic> empty_vec;
|
||||||
|
if (generator == "") {
|
||||||
|
return empty_vec;
|
||||||
|
}
|
||||||
|
if (generator == "traits") {
|
||||||
|
return generate_trait_topics();
|
||||||
|
}
|
||||||
|
return empty_vec;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string generate_topic_text(const std::string &generator) {
|
||||||
|
std::string empty_string = "";
|
||||||
|
if (generator == "") {
|
||||||
|
return empty_string;
|
||||||
|
}
|
||||||
|
if (generator == "about") {
|
||||||
|
return generate_about();
|
||||||
|
}
|
||||||
|
return empty_string;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<topic> generate_trait_topics() {
|
||||||
|
return std::vector<topic>();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
std::string generate_about() {
|
||||||
|
std::vector<std::string> about_lines = about::get_text();
|
||||||
|
std::vector<std::string> res_lines;
|
||||||
|
std::transform(about_lines.begin(), about_lines.end(), std::back_inserter(res_lines),
|
||||||
|
about_text_formatter());
|
||||||
|
std::vector<std::string>::iterator it =
|
||||||
|
std::remove(res_lines.begin(), res_lines.end(), "");
|
||||||
|
std::vector<std::string> res_lines_rem(res_lines.begin(), it);
|
||||||
|
std::string text = config::join(res_lines_rem, '\n');
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
help_manager::help_manager(const config *hlp_config) {
|
help_manager::help_manager(const config *hlp_config) {
|
||||||
help_config = hlp_config == NULL ? &dummy_cfg : hlp_config;
|
help_config = hlp_config == NULL ? &dummy_cfg : hlp_config;
|
||||||
}
|
}
|
||||||
|
@ -944,8 +1049,7 @@ void help_text_area::handle_event(const SDL_Event &event) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (focus()) {
|
if (focus()) {
|
||||||
switch (event.type) {
|
if (event.type == SDL_MOUSEBUTTONDOWN) {
|
||||||
case SDL_MOUSEBUTTONDOWN:
|
|
||||||
// Scroll up/down when the mouse wheel is used.
|
// Scroll up/down when the mouse wheel is used.
|
||||||
SDL_MouseButtonEvent mouse_event = event.button;
|
SDL_MouseButtonEvent mouse_event = event.button;
|
||||||
if (mouse_event.button == SDL_BUTTON_WHEELUP) {
|
if (mouse_event.button == SDL_BUTTON_WHEELUP) {
|
||||||
|
@ -954,9 +1058,6 @@ void help_text_area::handle_event(const SDL_Event &event) {
|
||||||
if (mouse_event.button == SDL_BUTTON_WHEELDOWN) {
|
if (mouse_event.button == SDL_BUTTON_WHEELDOWN) {
|
||||||
scroll_down();
|
scroll_down();
|
||||||
}
|
}
|
||||||
break;
|
|
||||||
default:
|
|
||||||
;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
12
src/help.hpp
12
src/help.hpp
|
@ -64,6 +64,7 @@ struct section {
|
||||||
/// Comparison on the ID.
|
/// Comparison on the ID.
|
||||||
bool operator<(const section &) const;
|
bool operator<(const section &) const;
|
||||||
|
|
||||||
|
/// Allocate memory for and add the section.
|
||||||
void add_section(const section &s);
|
void add_section(const section &s);
|
||||||
|
|
||||||
void clear();
|
void clear();
|
||||||
|
@ -381,6 +382,17 @@ private:
|
||||||
/// Parse a help config, return the top level section. Return an empty
|
/// Parse a help config, return the top level section. Return an empty
|
||||||
/// section if cfg is NULL.
|
/// section if cfg is NULL.
|
||||||
section parse_config(const config *cfg);
|
section parse_config(const config *cfg);
|
||||||
|
/// Recursive function used by parse_config.
|
||||||
|
void parse_config_internal(const config *help_cfg, const config *section_cfg,
|
||||||
|
section &sec, int level=0);
|
||||||
|
|
||||||
|
/// Dispatch generators to their appropriate functions.
|
||||||
|
std::vector<section> generate_sections(const std::string &generator);
|
||||||
|
std::vector<topic> generate_topics(const std::string &generator);
|
||||||
|
std::string generate_topic_text(const std::string &generator);
|
||||||
|
|
||||||
|
std::string generate_about();
|
||||||
|
std::vector<topic> generate_trait_topics();
|
||||||
|
|
||||||
/// Search for the topic with the specified identifier in the section
|
/// Search for the topic with the specified identifier in the section
|
||||||
/// and it's subsections. Return the found topic, or NULL if none could
|
/// and it's subsections. Return the found topic, or NULL if none could
|
||||||
|
|
Loading…
Add table
Reference in a new issue