Added automatically generated help for units, abilities and weapon specials.

The units and terrains the user has encountered are saved in the preferences
file so that no spoilers will be shown.
This commit is contained in:
Kristoffer Erlandsson 2004-08-13 21:24:33 +00:00
parent 8173bbf891
commit 05552caacb
9 changed files with 172 additions and 34 deletions

View file

@ -1,7 +1,7 @@
[help]
[toplevel]
#sections=introduction,gameplay,units,abilities,traits,weapon_specials
sections=introduction,gameplay,traits
sections=introduction,gameplay,units,abilities,traits,weapon_specials
#sections=introduction,gameplay,traits
topics=about
[/toplevel]

View file

@ -1278,7 +1278,9 @@ void advance_unit(const game_data& info,
const unit& new_unit = get_advanced_unit(info,units,loc,advance_to);
statistics::advance_unit(new_unit);
preferences::encountered_units().insert(new_unit.type().name());
std::cout << "Added '" << new_unit.type().name() << "' to encountered units" << std::endl;
units.erase(loc);
units.insert(std::pair<gamemap::location,unit>(loc,new_unit));
}

View file

@ -68,9 +68,6 @@ public:
/// changed.
void terrain_palette::adjust_size();
//void bg_backup();
//void bg_restore();
private:
void draw_old(bool);
/// To be called when a mouse click occurs. Check if the coordinates
@ -99,7 +96,6 @@ private:
gui::button top_button_, bot_button_;
size_t button_x_, top_button_y_, bot_button_y_;
size_t nterrains_, terrain_start_;
//surface_restorer restorer_;
};
/// A bar where the brush is drawin

View file

@ -16,6 +16,7 @@
#include "language.hpp"
#include "log.hpp"
#include "playlevel.hpp"
#include "preferences.hpp"
#include "replay.hpp"
#include "show_dialog.hpp"
#include "sound.hpp"
@ -294,6 +295,7 @@ bool event_handler::handle_event_command(const queued_event& event_info, const s
const std::vector<std::string>& types = config::split(type);
for(std::vector<std::string>::const_iterator i = types.begin(); i != types.end(); ++i) {
(*teams)[index].recruits().insert(*i);
preferences::encountered_units().insert(*i);
if(index == 0) {
state_of_game->can_recruit.insert(*i);
}
@ -679,6 +681,7 @@ bool event_handler::handle_event_command(const queued_event& event_info, const s
for(std::vector<gamemap::location>::const_iterator loc = locs.begin(); loc != locs.end(); ++loc) {
const std::string& terrain_type = cfg["letter"];
preferences::encountered_terrains().insert(terrain_type);
if(terrain_type.size() > 0) {
game_map->set_terrain(*loc,terrain_type[0]);
}
@ -691,6 +694,7 @@ bool event_handler::handle_event_command(const queued_event& event_info, const s
//if we should spawn a new unit on the map somewhere
else if(cmd == "unit") {
unit new_unit(*game_data_ptr,cfg);
preferences::encountered_units().insert(new_unit.type().name());
gamemap::location loc(cfg);
if(game_map->on_board(loc)) {

View file

@ -36,6 +36,7 @@
namespace {
const config *game_config = NULL;
game_data *game_info = NULL;
gamemap *map = NULL;
// The default toplevel.
help::section toplevel;
// All sections and topics not referenced from the default toplevel.
@ -137,9 +138,15 @@ namespace {
namespace help {
help_manager::help_manager(const config *cfg, game_data *gameinfo) {
help_manager::help_manager(const config *cfg, game_data *gameinfo, gamemap *_map) {
game_config = cfg == NULL ? &dummy_cfg : cfg;
game_info = gameinfo;
map = _map;
}
void generate_contents() {
toplevel.clear();
hidden_sections.clear();
if (game_config != NULL) {
const config *help_config = game_config->child("help");
if (help_config == NULL) {
@ -203,6 +210,7 @@ help_manager::help_manager(const config *cfg, game_data *gameinfo) {
help_manager::~help_manager() {
game_config = NULL;
game_info = NULL;
map = NULL;
toplevel.clear();
hidden_sections.clear();
}
@ -385,22 +393,24 @@ std::vector<topic> generate_weapon_special_topics() {
for (std::vector<attack_type>::const_iterator it = attacks.begin();
it != attacks.end(); it++) {
const std::string special = (*it).special();
if (checked_specials.find(special) == checked_specials.end()) {
std::string lang_special = string_table["weapon_special_" + special];
if (lang_special == "") {
lang_special = special;
if (special != "") {
if (checked_specials.find(special) == checked_specials.end()) {
std::string lang_special = string_table["weapon_special_" + special];
if (lang_special == "") {
lang_special = special;
}
lang_special = cap(lang_special);
std::string description
= string_table["weapon_special_" + special + "_description"];
const size_t colon_pos = description.find(':');
if (colon_pos != std::string::npos) {
// Remove the first colon and the following newline.
description.erase(0, colon_pos + 2);
}
topic t(lang_special, "weaponspecial_" + special, description);
topics.push_back(t);
checked_specials.insert(special);
}
lang_special = cap(lang_special);
std::string description
= string_table["weapon_special_" + special + "_description"];
const size_t colon_pos = description.find(':');
if (colon_pos != std::string::npos) {
// Remove the first colon and the following newline.
description.erase(0, colon_pos + 2);
}
topic t(lang_special, "weaponspecial_" + special, description);
topics.push_back(t);
checked_specials.insert(special);
}
}
}
@ -470,12 +480,18 @@ std::vector<topic> generate_unit_topics() {
}
else if (desc_type == FULL_DESCRIPTION) {
const std::string detailed_description = type.unit_description();
const std::string normal_image = type.image();
const unit_type *female_type = type.get_gender_unit_type(unit_race::FEMALE);
const unit_type *male_type = type.get_gender_unit_type(unit_race::MALE);
// Show the unit's image and it's level.
ss << "<img>src='" << normal_image << "' align=left float=no</img>"
<< "<format>font_size=11 text='" << translate_string("level")
<< " " << type.level() << "'</format>\n";
if (male_type != NULL) {
ss << "<img>src='" << male_type->image() << "'</img> ";
}
if (female_type != NULL && female_type != male_type) {
ss << "<img>src='" << female_type->image() << "'</img> ";
}
ss << "<format>font_size=11 text=' " << translate_string("level")
<< " " << type.level() << "'</format>\n";
// Print the units this unit can advance to. Cross reference
// to the topics containing information about those units.
@ -530,7 +546,8 @@ std::vector<topic> generate_unit_topics() {
ss << translate_string("hp") << ": " << type.hitpoints() << jump_to(100)
<< translate_string("moves") << ": " << type.movement() << jump_to(200)
<< translate_string("alignment") << ": "
<< type.alignment_description(type.alignment()) << jump_to(350);
<< translate_string(type.alignment_description(type.alignment()))
<< jump_to(350);
if (type.experience_needed() != 500) {
// 500 is apparently used when the units cannot advance.
ss << translate_string("required_xp") << ": " << type.experience_needed();
@ -608,6 +625,39 @@ std::vector<topic> generate_unit_topics() {
<< " text='"<< resistance << "%'</format>\n";
}
if (map != NULL) {
// Print the terrain modifier table of the unit.
ss << "\n<header>text='" << cap(translate_string("terrain_info"))
<< "'</header>\n\n"
<< bold(cap(translate_string("terrain"))) << jump_to(140)
<< bold(cap(translate_string("movement"))) << jump_to(280)
<< bold(cap(translate_string("defense"))) << "\n";
for (std::set<std::string>::const_iterator terrain_it =
preferences::encountered_terrains().begin();
terrain_it != preferences::encountered_terrains().end();
terrain_it++) {
assert(terrain_it->size() > 0);
const gamemap::TERRAIN terrain = (*terrain_it)[0];
if (terrain == gamemap::FOGGED || terrain == gamemap::VOID_TERRAIN) {
continue;
}
const terrain_type& info = map->get_terrain_info(terrain);
if (!info.is_alias()) {
const std::string& name = map->terrain_name(terrain);
const std::string& lang_name = string_table[name];
const int moves = movement_type.movement_cost(*map,terrain);
std::stringstream str;
ss << lang_name << jump_to(140);
if(moves < 100)
ss << moves;
else
ss << "--";
const int defense =
100 - movement_type.defense_modifier(*map,terrain);
ss << jump_to(280) << defense << "%\n";
}
}
}
}
else {
assert(false);
@ -619,8 +669,12 @@ std::vector<topic> generate_unit_topics() {
}
UNIT_DESCRIPTION_TYPE description_type(const unit_type &type) {
// For now, until decision is made, show the full description for everything.
return FULL_DESCRIPTION;
const std::string id = type.name();
const std::set<std::string> &encountered_units = preferences::encountered_units();
if (encountered_units.find(id) != encountered_units.end()) {
return FULL_DESCRIPTION;
}
return NO_DESCRIPTION;
}
std::string generate_traits_text() {
@ -2061,6 +2115,7 @@ void show_help(display &disp, const section &toplevel_sec, const std::string sho
gui::draw_dialog(xloc, yloc, width, height, disp, translate_string("help_title"),
NULL, &buttons_ptr, &restorer);
generate_contents();
try {
help_browser hb(disp, toplevel_sec);
hb.set_location(xloc + left_padding, yloc + top_padding);

View file

@ -15,6 +15,7 @@
#include "config.hpp"
#include "display.hpp"
#include "font.hpp"
#include "map.hpp"
#include "sdl_utils.hpp"
#include "unit_types.hpp"
#include "widgets/button.hpp"
@ -31,10 +32,14 @@
namespace help {
struct help_manager {
help_manager(const config *game_config, game_data *game_info);
help_manager(const config *game_config, game_data *game_info, gamemap *map);
~help_manager();
};
/// Generate the help contents from the configurations given to the
/// manager.
void generate_contents();
struct section;
typedef std::vector<section *> section_list;

View file

@ -261,7 +261,7 @@ LEVEL_RESULT play_level(game_data& gameinfo, const config& game_config,
if(teams.size() == 1) {
state_of_game.can_recruit = teams.back().recruits();
}
//if there are additional starting units on this side
const config::child_list& starting_units = (*ui)->get_children("unit");
for(config::child_list::const_iterator su = starting_units.begin();
@ -281,8 +281,51 @@ LEVEL_RESULT play_level(game_data& gameinfo, const config& game_config,
std::cerr << "inserting unit for side " << new_unit.side() << "\n";
}
}
}
// Add all recruitable units as encountered so that information
// about them are displayed to the user in the help system.
for (std::vector<team>::const_iterator help_team_it = teams.begin();
help_team_it != teams.end(); help_team_it++) {
std::cout << "Adding help units for team '" << help_team_it->name()
<< "'" << std::endl;
const std::set<std::string> &recruitable = help_team_it->recruits();
std::set<std::string> &enc_units = preferences::encountered_units();
std::cout << "Adding recruitable units: " << std::endl;
for (std::set<std::string>::iterator it = recruitable.begin();
it != recruitable.end(); it++) {
std::cout << *it << std::endl;
}
std::cout << "Added all recruitable units" << std::endl;
std::copy(recruitable.begin(), recruitable.end(),
std::inserter(enc_units, enc_units.begin()));
}
// Add all units that exist at the start to the encountered units so
// that information about them are displayed to the user in the help
// system.
for (unit_map::const_iterator help_unit_it = units.begin();
help_unit_it != units.end(); help_unit_it++) {
const std::string name = help_unit_it->second.type().name();
preferences::encountered_units().insert(name);
}
// Add all units that are recallable as encountred units.
for(std::vector<unit>::iterator help_recall_it = state_of_game.available_units.begin();
help_recall_it != state_of_game.available_units.end(); help_recall_it++) {
preferences::encountered_units().insert(help_recall_it->type().name());
}
// Add all terrains on the map as encountered terrains.
for (int map_x = 0; map_x < map.x(); map_x++) {
for (int map_y = 0; map_y < map.y(); map_y++) {
const gamemap::TERRAIN t = map.get_terrain(gamemap::location(map_x, map_y));
std::string s;
s += t;
preferences::encountered_terrains().insert(s);
}
}
std::cerr << "initialized teams... " << (SDL_GetTicks() - ticks) << "\n";
const config* theme_cfg = NULL;
@ -344,7 +387,7 @@ LEVEL_RESULT play_level(game_data& gameinfo, const config& game_config,
std::cerr << "initializing events manager... " << (SDL_GetTicks() - ticks) << "\n";
help::help_manager help_manager(&game_config, &gameinfo);
help::help_manager help_manager(&game_config, &gameinfo, &map);
//find a list of 'items' (i.e. overlays) on the level, and add them
const config::child_list& overlays = level->get_children("item");

View file

@ -30,6 +30,7 @@
#include <cstdlib>
#include <iostream>
#include <sstream>
#include <iterator>
namespace {
@ -44,6 +45,9 @@ bool message_private_on = true;
bool haloes = true;
std::set<std::string> encountered_units_set;
std::set<std::string> encountered_terrains_set;
}
namespace preferences {
@ -51,16 +55,33 @@ namespace preferences {
manager::manager()
{
prefs.read(read_file(get_prefs_file()));
set_music_volume(music_volume());
set_sound_volume(sound_volume());
set_colour_cursors(prefs["colour_cursors"] == "yes");
set_show_haloes(prefs["show_haloes"] != "no");
std::vector<std::string> v;
v = config::split(prefs["encountered_units"]);
std::copy(v.begin(), v.end(),
std::inserter(encountered_units_set, encountered_units_set.begin()));
v = config::split(prefs["encountered_terrains"]);
std::copy(v.begin(), v.end(),
std::inserter(encountered_terrains_set, encountered_terrains_set.begin()));
}
manager::~manager()
{
std::vector<std::string> v;
std::copy(encountered_units_set.begin(), encountered_units_set.end(), std::back_inserter(v));
prefs["encountered_units"] = config::join(v);
v.clear();
std::copy(encountered_terrains_set.begin(), encountered_terrains_set.end(),
std::back_inserter(v));
prefs["encountered_terrains"] = config::join(v);
encountered_units_set.clear();
encountered_terrains_set.clear();
try {
write_file(get_prefs_file(),prefs.write());
} catch(io_exception&) {
@ -527,6 +548,14 @@ void set_show_haloes(bool value)
prefs["show_haloes"] = value ? "yes" : "no";
}
std::set<std::string> &encountered_units() {
return encountered_units_set;
}
std::set<std::string> &encountered_terrains() {
return encountered_terrains_set;
}
CACHE_SAVES_METHOD cache_saves()
{
if(prefs["cache_saves"] == "always") {

View file

@ -18,6 +18,7 @@
#include <string>
#include <utility>
#include <set>
namespace preferences {
@ -109,6 +110,9 @@ namespace preferences {
bool show_haloes();
void set_show_haloes(bool value);
std::set<std::string> &encountered_units();
std::set<std::string> &encountered_terrains();
enum CACHE_SAVES_METHOD { CACHE_SAVES_ASK, CACHE_SAVES_NEVER, CACHE_SAVES_ALWAYS };
CACHE_SAVES_METHOD cache_saves();
void set_cache_saves(CACHE_SAVES_METHOD method);