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]
|
||||
[toplevel]
|
||||
sections=introduction,gameplay
|
||||
#topics=about
|
||||
topics=about
|
||||
[/toplevel]
|
||||
|
||||
[section]
|
||||
|
@ -14,6 +14,7 @@ topics=introduction
|
|||
id=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
|
||||
generator=foo
|
||||
[/section]
|
||||
|
||||
[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.
|
||||
|
||||
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]
|
||||
|
@ -165,8 +166,21 @@ text="This concludes the fundamentals of Wesnoth. You might want to read up on B
|
|||
id=about
|
||||
title="About"
|
||||
# Does not work yet, but soon! :)
|
||||
generator="generate_about"
|
||||
generator=about
|
||||
[/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]
|
||||
|
||||
|
|
|
@ -23,33 +23,11 @@
|
|||
#include "widgets/button.hpp"
|
||||
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
namespace about
|
||||
{
|
||||
|
||||
void show_about(display& disp)
|
||||
{
|
||||
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> get_text() {
|
||||
std::vector<std::string> text;
|
||||
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("- Jan Zvánovec (jaz)");
|
||||
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 '-'
|
||||
for(std::vector<std::string>::iterator itor = text.begin(); itor != text.end(); ++itor) {
|
||||
|
|
|
@ -15,11 +15,14 @@
|
|||
#define ABOUT_H_INCLUDED
|
||||
|
||||
#include "display.hpp"
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
namespace about
|
||||
{
|
||||
|
||||
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 "about.hpp"
|
||||
#include "cursor.hpp"
|
||||
#include "events.hpp"
|
||||
#include "font.hpp"
|
||||
#include "language.hpp"
|
||||
#include "preferences.hpp"
|
||||
#include "show_dialog.hpp"
|
||||
#include "unit.hpp"
|
||||
#include "util.hpp"
|
||||
|
||||
#include "SDL_ttf.h"
|
||||
|
@ -56,8 +58,53 @@ namespace {
|
|||
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,
|
||||
help::section &sec, int level=0) {
|
||||
section &sec, int level) {
|
||||
if (level > max_section_level) {
|
||||
std::cerr << "Maximum section depth has been reached. Maybe circular dependency?"
|
||||
<< std::endl;
|
||||
|
@ -69,7 +116,7 @@ namespace {
|
|||
if (!is_valid_id(id)) {
|
||||
std::stringstream ss;
|
||||
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"];
|
||||
|
@ -80,7 +127,7 @@ namespace {
|
|||
for (it = sections.begin(); it != sections.end(); it++) {
|
||||
config const *child_cfg = help_cfg->find_child("section", "id", *it);
|
||||
if (child_cfg != NULL) {
|
||||
help::section child_section;
|
||||
section child_section;
|
||||
parse_config_internal(help_cfg, child_cfg, child_section, level + 1);
|
||||
sec.add_section(child_section);
|
||||
}
|
||||
|
@ -88,20 +135,25 @@ namespace {
|
|||
std::stringstream ss;
|
||||
ss << "Help-section '" << *it << "' referenced from '"
|
||||
<< 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"]);
|
||||
// Find all topics in this section.
|
||||
for (it = topics.begin(); it != topics.end(); it++) {
|
||||
config const *topic_cfg = help_cfg->find_child("topic", "id", *it);
|
||||
if (topic_cfg != NULL) {
|
||||
help::topic child_topic((*topic_cfg)["title"], (*topic_cfg)["id"],
|
||||
(*topic_cfg)["text"]);
|
||||
std::string 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)) {
|
||||
std::stringstream ss;
|
||||
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);
|
||||
}
|
||||
|
@ -109,14 +161,16 @@ namespace {
|
|||
std::stringstream ss;
|
||||
ss << "Help-topic '" << *it << "' referenced from '" << id
|
||||
<< "' 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 sec;
|
||||
|
@ -127,6 +181,57 @@ section parse_config(const config *cfg) {
|
|||
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_config = hlp_config == NULL ? &dummy_cfg : hlp_config;
|
||||
}
|
||||
|
@ -944,8 +1049,7 @@ void help_text_area::handle_event(const SDL_Event &event) {
|
|||
return;
|
||||
}
|
||||
if (focus()) {
|
||||
switch (event.type) {
|
||||
case SDL_MOUSEBUTTONDOWN:
|
||||
if (event.type == SDL_MOUSEBUTTONDOWN) {
|
||||
// Scroll up/down when the mouse wheel is used.
|
||||
SDL_MouseButtonEvent mouse_event = event.button;
|
||||
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) {
|
||||
scroll_down();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
12
src/help.hpp
12
src/help.hpp
|
@ -64,6 +64,7 @@ struct section {
|
|||
/// Comparison on the ID.
|
||||
bool operator<(const section &) const;
|
||||
|
||||
/// Allocate memory for and add the section.
|
||||
void add_section(const section &s);
|
||||
|
||||
void clear();
|
||||
|
@ -381,6 +382,17 @@ private:
|
|||
/// Parse a help config, return the top level section. Return an empty
|
||||
/// section if cfg is NULL.
|
||||
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
|
||||
/// and it's subsections. Return the found topic, or NULL if none could
|
||||
|
|
Loading…
Add table
Reference in a new issue