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:
Kristoffer Erlandsson 2004-06-18 16:59:51 +00:00
parent 20c627432f
commit 5b8bfeedce
5 changed files with 175 additions and 42 deletions

View file

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

View file

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

View file

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

View file

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

View file

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