Help shows terrain types in the section for their default_base

Instead of the aliasing hack that the oasis terrain used to use, have
the help topic generator reuse the editor_default_base as a hint that it
should be included in that section.

The terrain_type class gets a new boolean method, and more documentation.
There's a couple of refactors to use the new method in old code too.
This commit is contained in:
Steve Cotton 2024-04-22 22:05:22 +02:00 committed by Steve Cotton
parent ebf93c02bb
commit 3fba8897d8
7 changed files with 65 additions and 25 deletions

View file

@ -353,10 +353,7 @@ Most units receive 20 to 40% defense in sand."
name= _ "Oasis"
default_base=Dd
string=^Do
# Note: we must do the aliasing this way to allow the terrain to show up in the "Sand" terrain help group
aliasof=Dt, Wst
def_alias=_bas, Wst
mvt_alias=_bas, Wst
aliasof=_bas, Wst
submerge=0.3
heals=8
editor_group=desert, water

View file

@ -170,11 +170,11 @@ void terrain_palette::setup_item(
texture& overlay_image,
std::stringstream& tooltip_text)
{
const t_translation::terrain_code base_terrain = map().get_terrain_info(terrain).default_base();
const auto& info = map().get_terrain_info(terrain);
//Draw default base for overlay terrains
if(base_terrain != t_translation::NONE_TERRAIN) {
const std::string base_filename = map().get_terrain_info(base_terrain).editor_image();
if(info.has_default_base()) {
const std::string base_filename = info.editor_image();
base_image = image::get_texture(base_filename);
if(!base_image) {
@ -188,7 +188,7 @@ void terrain_palette::setup_item(
}
}
const std::string filename = map().get_terrain_info(terrain).editor_image();
const std::string filename = info.editor_image();
overlay_image = image::get_texture(filename);
if(!overlay_image) {
tooltip_text << "IMAGE NOT FOUND\n";

View file

@ -43,6 +43,7 @@
#include "tod_manager.hpp" // for tod_manager
#include "tstring.hpp" // for t_string, operator<<
#include "units/types.hpp" // for unit_type, unit_type_data, etc
#include "utils/general.hpp" // for contains
#include "serialization/unicode.hpp" // for iterator
#include "color.hpp"
@ -990,6 +991,13 @@ void generate_terrain_sections(section& sec, int /*level*/)
terrain_topic.text = std::make_shared<terrain_topic_generator>(info);
t_translation::ter_list base_terrains = tdata->underlying_union_terrain(t);
if (info.has_default_base()) {
for (const auto base : tdata->underlying_union_terrain(info.default_base())) {
if (!utils::contains(base_terrains, base)) {
base_terrains.emplace_back(base);
}
}
}
for (const t_translation::terrain_code& base : base_terrains) {
const terrain_type& base_info = tdata->get_terrain_info(base);

View file

@ -89,9 +89,14 @@ static std::string print_behavior_description(ter_iter start, ter_iter end, cons
if (!last_change_pos) {
std::vector<std::string> names;
for (ter_iter i = start; i != end; ++i) {
const terrain_type tt = tdata->get_terrain_info(*i);
if (!tt.editor_name().empty())
names.push_back(tt.editor_name());
if (*i == t_translation::BASE) {
// TRANSLATORS: in a description of an overlay terrain, the terrain that it's placed on
names.push_back(_("base terrain"));
} else {
const terrain_type tt = tdata->get_terrain_info(*i);
if (!tt.editor_name().empty())
names.push_back(tt.editor_name());
}
}
if (names.empty()) return "";
@ -189,21 +194,31 @@ std::string terrain_topic_generator::operator()() const {
// Almost all terrains will show the data in this conditional block. The ones that don't are the
// archetypes used in [movetype]'s subtags such as [movement_costs].
if (!type_.is_indivisible()) {
ss << "\n" << _("Base Terrain: ");
bool first = true;
std::vector<t_string> underlying;
for (const auto& underlying_terrain : type_.union_type()) {
const terrain_type& base = tdata->get_terrain_info(underlying_terrain);
if (base.editor_name().empty()) continue;
if (!first) {
ss << ", ";
} else {
first = false;
if (!base.editor_name().empty()) {
underlying.push_back(make_link(base.editor_name(), ".." + terrain_prefix + base.id()));
}
}
utils::string_map symbols;
symbols["types"] = utils::format_conjunct_list("", underlying);
// TRANSLATORS: $types is a conjunct list, typical values will be "Castle" or "Flat and Shallow Water".
// The terrain names will be hypertext links to the help page of the corresponding terrain type.
// There will always be at least 1 item in the list, but unlikely to be more than 3.
ss << "\n" << VNGETTEXT("Basic terrain type: $types", "Basic terrain types: $types", underlying.size(), symbols);
ss << make_link(base.editor_name(), ".." + terrain_prefix + base.id());
if (type_.has_default_base()) {
const terrain_type& base = tdata->get_terrain_info(type_.default_base());
symbols.clear();
if (base.is_indivisible()) {
symbols["type"] = make_link(base.editor_name(), ".." + terrain_prefix + base.id());
} else {
symbols["type"] = make_link(base.editor_name(), terrain_prefix + base.id());
}
// TRANSLATORS: In the help for a terrain type, for example Dwarven Village is often placed on Cave Floor
ss << "\n" << VGETTEXT("Typical base terrain: $type", symbols);
}
ss << "\n";

View file

@ -293,7 +293,7 @@ terrain_type::terrain_type(const terrain_type& base, const terrain_type& overlay
}
t_translation::terrain_code terrain_type::terrain_with_default_base() const {
if(overlay_ && editor_default_base_ != t_translation::NONE_TERRAIN) {
if(overlay_ && has_default_base()) {
return t_translation::terrain_code(editor_default_base_.base, number_.overlay);
}
return number_;

View file

@ -166,11 +166,31 @@ public:
*/
bool is_combined() const { return combined_; }
/**
* Overlay terrains defined by a [terrain_type] can declare a fallback base
* terrain, for use when the overlay is selected in the editor, or when the
* overlay is placed on the map using [terrain]replace_if_failed=true.
*
* If there's no default, returns a sentinel value; see has_default_base().
*/
t_translation::terrain_code default_base() const { return editor_default_base_; }
bool has_default_base() const { return editor_default_base_ != t_translation::NONE_TERRAIN; }
/**
* Return the overlay part of this terrain, on the default_base(). Might
* return an unknown terrain, if there's a typo in the default base.
*
* If this terrain has no overlay, it returns the terrain itself, ignoring
* the default_base() even if the terrain has a default_base().
*
* This is intended for the editor's single-layer placement, or for
* replacing terrains via ActionWML, where the user or WML author intends
* to only use one layer of the current terrain.
*/
t_translation::terrain_code terrain_with_default_base() const;
/**
* Returns true if all most of the data matches. The ones that don't need to match:
* Returns true if most of the data matches. The ones that don't need to match:
* - editor_group_
* - icon_image_
* - description_

View file

@ -267,7 +267,7 @@ t_translation::terrain_code terrain_type_data::merge_terrains(const t_translatio
if(new_t.base != t_translation::NO_LAYER) {
result = new_t;
}
else if (get_terrain_info(new_t).default_base() != t_translation::NONE_TERRAIN) {
else if (get_terrain_info(new_t).has_default_base()) {
result = get_terrain_info(new_t).terrain_with_default_base();
}
}