From 8cf191d11512b64080567a94943fd3438e41d754 Mon Sep 17 00:00:00 2001
From: Marko Lindqvist <cazfi74@gmail.com>
Date: Thu, 9 May 2024 03:50:24 +0300
Subject: [PATCH 48/48] Manual: Add Unit Classes page

See RM #572

Signed-off-by: Marko Lindqvist <cazfi74@gmail.com>
---
 client/gui-gtk-3.22/helpdlg.c |  3 +-
 client/gui-gtk-4.0/helpdlg.c  |  3 +-
 client/gui-qt/citydlg.cpp     |  4 +-
 client/gui-qt/helpdlg.cpp     |  2 +-
 client/gui-qt/repodlgs.cpp    |  2 +-
 client/gui-sdl2/helpdlg.c     |  3 +-
 client/gui-sdl3/helpdlg.c     |  3 +-
 client/helpdata.c             | 12 ++++--
 client/helpdata.h             |  4 +-
 meson.build                   |  1 +
 tools/manual/Makefile.am      |  1 +
 tools/manual/fc_manual.c      |  7 ++--
 tools/manual/fc_manual.h      |  7 +++-
 tools/manual/manual_uclass.c  | 70 +++++++++++++++++++++++++++++++++++
 tools/manual/manual_units.c   |  2 +-
 15 files changed, 105 insertions(+), 19 deletions(-)
 create mode 100644 tools/manual/manual_uclass.c

diff --git a/client/gui-gtk-3.22/helpdlg.c b/client/gui-gtk-3.22/helpdlg.c
index ca0deb946c..f9445e9a0a 100644
--- a/client/gui-gtk-3.22/helpdlg.c
+++ b/client/gui-gtk-3.22/helpdlg.c
@@ -1020,7 +1020,8 @@ static void help_update_unit_type(const struct help_item *pitem,
 			 utype_name_translation(utype->obsoleted_by));
     }
 
-    helptext_unit(buf, sizeof(buf), client.conn.playing, pitem->text, utype);
+    helptext_unit(buf, sizeof(buf), client.conn.playing, pitem->text, utype,
+                  TRUE);
 
     gtk_text_buffer_set_text(help_text, buf, -1);
     gtk_widget_show(help_text_sw);
diff --git a/client/gui-gtk-4.0/helpdlg.c b/client/gui-gtk-4.0/helpdlg.c
index 0491fffd9e..ac5e18d4aa 100644
--- a/client/gui-gtk-4.0/helpdlg.c
+++ b/client/gui-gtk-4.0/helpdlg.c
@@ -1054,7 +1054,8 @@ static void help_update_unit_type(const struct help_item *pitem,
                          utype_name_translation(utype->obsoleted_by));
     }
 
-    helptext_unit(buf, sizeof(buf), client_player(), pitem->text, utype);
+    helptext_unit(buf, sizeof(buf), client_player(), pitem->text, utype,
+                  TRUE);
 
     gtk_text_buffer_set_text(help_text, buf, -1);
     gtk_widget_set_visible(help_text_sw, TRUE);
diff --git a/client/gui-qt/citydlg.cpp b/client/gui-qt/citydlg.cpp
index 1bb1437364..9de27e9e96 100644
--- a/client/gui-qt/citydlg.cpp
+++ b/client/gui-qt/citydlg.cpp
@@ -3990,7 +3990,7 @@ QString get_tooltip_unit(const struct unit_type *utype, bool ext)
 
     buf2[0] = '\0';
     str = helptext_unit(buffer, sizeof(buffer), client.conn.playing,
-                        buf2, utype);
+                        buf2, utype, TRUE);
     str = cut_helptext(str);
     str = split_text(str, true);
     str = str.trimmed().toHtmlEscaped();
@@ -4018,7 +4018,7 @@ QString get_tooltip(QVariant qvar)
   } else if (VUT_UTYPE == target->kind) {
     def_str = get_tooltip_unit(target->value.utype);
     str = helptext_unit(buffer, sizeof(buffer), client.conn.playing,
-                        buf2, target->value.utype);
+                        buf2, target->value.utype, TRUE);
   } else {
     if (!is_convert_improvement(target->value.building)) {
       def_str = get_tooltip_improvement(target->value.building);
diff --git a/client/gui-qt/helpdlg.cpp b/client/gui-qt/helpdlg.cpp
index 7815c0bc14..c87401297f 100644
--- a/client/gui-qt/helpdlg.cpp
+++ b/client/gui-qt/helpdlg.cpp
@@ -888,7 +888,7 @@ void help_widget::set_topic_unit(const help_item *topic,
   utype = unit_type_by_translated_name(title);
   if (utype) {
     helptext_unit(buffer, sizeof(buffer), client.conn.playing,
-                  topic->text, utype);
+                  topic->text, utype, TRUE);
     text_browser->setPlainText(buffer);
 
     // Create information panel
diff --git a/client/gui-qt/repodlgs.cpp b/client/gui-qt/repodlgs.cpp
index 9002034f1b..e715050896 100644
--- a/client/gui-qt/repodlgs.cpp
+++ b/client/gui-qt/repodlgs.cpp
@@ -793,7 +793,7 @@ void research_diagram::mouseMoveEvent(QMouseEvent *event)
       } else if (rttp->tunit != nullptr) {
         def_str = get_tooltip_unit(rttp->tunit);
         tt_text += helptext_unit(buffer, sizeof(buffer), client.conn.playing,
-                                buf2, rttp->tunit);
+                                 buf2, rttp->tunit, TRUE);
         tt_text = cut_helptext(tt_text);
       } else if (rttp->tgov != nullptr) {
         helptext_government(buffer, sizeof(buffer), client.conn.playing,
diff --git a/client/gui-sdl2/helpdlg.c b/client/gui-sdl2/helpdlg.c
index 1f7d0c00ef..39d6298bed 100644
--- a/client/gui-sdl2/helpdlg.c
+++ b/client/gui-sdl2/helpdlg.c
@@ -930,7 +930,8 @@ void popup_unit_info(Unit_type_id type_id)
   start_x = (area.x + 1 + scrollbar_width + help_dlg->active_widget_list->size.w + adj_size(20));
 
   buffer[0] = '\0';
-  helptext_unit(buffer, sizeof(buffer), client.conn.playing, "", utype_by_number(type_id));
+  helptext_unit(buffer, sizeof(buffer), client.conn.playing, "", utype_by_number(type_id),
+                TRUE);
   if (buffer[0] != '\0') {
     utf8_str *ustr = create_utf8_from_char_fonto(buffer, FONTO_ATTENTION);
 
diff --git a/client/gui-sdl3/helpdlg.c b/client/gui-sdl3/helpdlg.c
index 20ac10599b..882283dd46 100644
--- a/client/gui-sdl3/helpdlg.c
+++ b/client/gui-sdl3/helpdlg.c
@@ -928,7 +928,8 @@ void popup_unit_info(Unit_type_id type_id)
   start_x = (area.x + 1 + scrollbar_width + help_dlg->active_widget_list->size.w + adj_size(20));
 
   buffer[0] = '\0';
-  helptext_unit(buffer, sizeof(buffer), client.conn.playing, "", utype_by_number(type_id));
+  helptext_unit(buffer, sizeof(buffer), client.conn.playing, "", utype_by_number(type_id),
+                TRUE);
   if (buffer[0] != '\0') {
     utf8_str *ustr = create_utf8_from_char_fonto(buffer, FONTO_ATTENTION);
 
diff --git a/client/helpdata.c b/client/helpdata.c
index 2e120af8ba..0d055afa14 100644
--- a/client/helpdata.c
+++ b/client/helpdata.c
@@ -1863,8 +1863,7 @@ static bool utype_may_do_escape_action(const struct unit_type *utype)
   @param buf    Buffer to append help text to
   @param bufsz  Size of the buffer
 ****************************************************************************/
-static void helptext_unitclass(struct unit_class *pclass,
-                               char *buf, size_t bufsz)
+void helptext_unitclass(struct unit_class *pclass, char *buf, size_t bufsz)
 {
   int flagid;
 
@@ -1931,7 +1930,8 @@ static void helptext_unitclass(struct unit_class *pclass,
   pplayer may be NULL.
 ****************************************************************************/
 char *helptext_unit(char *buf, size_t bufsz, struct player *pplayer,
-                    const char *user_text, const struct unit_type *utype)
+                    const char *user_text, const struct unit_type *utype,
+                    bool class_help)
 {
   bool has_vet_levels;
   int flagid;
@@ -1956,7 +1956,11 @@ char *helptext_unit(char *buf, size_t bufsz, struct player *pplayer,
                BULLET,
                uclass_name_translation(pclass));
 
-  helptext_unitclass(pclass, buf, bufsz);
+  if (class_help) {
+    helptext_unitclass(pclass, buf, bufsz);
+  } else {
+    cat_snprintf(buf, bufsz, "\n");
+  }
 
   if (uclass_has_flag(pclass, UCF_ZOC)
       && !utype_has_flag(utype, UTYF_IGZOC)) {
diff --git a/client/helpdata.h b/client/helpdata.h
index e492aa974b..3113624116 100644
--- a/client/helpdata.h
+++ b/client/helpdata.h
@@ -44,7 +44,9 @@ const struct help_item *help_iter_next(void);
 char *helptext_building(char *buf, size_t bufsz, struct player *pplayer,
                         const char *user_text, const struct impr_type *pimprove);
 char *helptext_unit(char *buf, size_t bufsz, struct player *pplayer,
-                    const char *user_text, const struct unit_type *utype);
+                    const char *user_text, const struct unit_type *utype,
+                    bool class_help);
+void helptext_unitclass(struct unit_class *pclass, char *buf, size_t bufsz);
 void helptext_advance(char *buf, size_t bufsz, struct player *pplayer,
                       const char *user_text, int i);
 void helptext_terrain(char *buf, size_t bufsz, struct player *pplayer,
diff --git a/meson.build b/meson.build
index a902442e04..2fef91ef7c 100644
--- a/meson.build
+++ b/meson.build
@@ -4223,6 +4223,7 @@ executable('freeciv-manual',
   'tools/manual/manual_settings.c',
   'tools/manual/manual_techs.c',
   'tools/manual/manual_terrain.c',
+  'tools/manual/manual_uclass.c',
   'tools/manual/manual_units.c',
   'client/helpdata.c',
   link_with: [common_lib, server_lib, tool_lib, ais],
diff --git a/tools/manual/Makefile.am b/tools/manual/Makefile.am
index 71d8d25c6c..37ee2f97e2 100644
--- a/tools/manual/Makefile.am
+++ b/tools/manual/Makefile.am
@@ -26,6 +26,7 @@ freeciv_manual_SOURCES =		\
 		manual_settings.c	\
 		manual_techs.c		\
 		manual_terrain.c	\
+		manual_uclass.c		\
 		manual_units.c
 
 # This is a bit of a hack. The program links in with the server lib
diff --git a/tools/manual/fc_manual.c b/tools/manual/fc_manual.c
index 37f0cc7713..caf92f5e84 100644
--- a/tools/manual/fc_manual.c
+++ b/tools/manual/fc_manual.c
@@ -342,6 +342,7 @@ static bool manual_command(struct tag_types *tag_info)
       || !manual_buildings(tag_info)
       || !manual_governments(tag_info)
       || !manual_units(tag_info)
+      || !manual_uclasses(tag_info)
       || !manual_techs(tag_info)) {
     return FALSE;
   }
@@ -374,7 +375,7 @@ int main(int argc, char **argv)
   /* Set the default log level. */
   srvarg.loglevel = LOG_NORMAL;
 
-  /* parse command-line arguments... */
+  /* Parse command-line arguments... */
   inx = 1;
   while (inx < argc) {
     if ((option = get_option_malloc("--ruleset", argv, &inx, argc, TRUE))) {
@@ -421,11 +422,11 @@ int main(int argc, char **argv)
 
   init_our_capability();
 
-  /* must be before con_log_init() */
+  /* Must be before con_log_init() */
   init_connections();
   con_log_init(srvarg.log_filename, srvarg.loglevel,
                srvarg.fatal_assertions);
-  /* logging available after this point */
+  /* Logging available after this point */
 
   /* Get common code to treat us as a tool. */
   i_am_tool();
diff --git a/tools/manual/fc_manual.h b/tools/manual/fc_manual.h
index 245d8178e2..a2d1ad1b6f 100644
--- a/tools/manual/fc_manual.h
+++ b/tools/manual/fc_manual.h
@@ -39,8 +39,10 @@ extern "C" {
 #define SPECENUM_VALUE6NAME N_("Governments")
 #define SPECENUM_VALUE7 MANUAL_UNITS
 #define SPECENUM_VALUE7NAME N_("Units")
-#define SPECENUM_VALUE8 MANUAL_TECHS
-#define SPECENUM_VALUE8NAME N_("Techs")
+#define SPECENUM_VALUE8 MANUAL_UCLASS
+#define SPECENUM_VALUE8NAME N_("Unit Classes")
+#define SPECENUM_VALUE9 MANUAL_TECHS
+#define SPECENUM_VALUE9NAME N_("Techs")
 #define SPECENUM_COUNT MANUAL_COUNT
 #include "specenum_gen.h"
 
@@ -75,6 +77,7 @@ bool manual_extras(struct tag_types *tag_info);
 bool manual_buildings(struct tag_types *tag_info);
 bool manual_governments(struct tag_types *tag_info);
 bool manual_units(struct tag_types *tag_info);
+bool manual_uclasses(struct tag_types *tag_info);
 bool manual_techs(struct tag_types *tag_info);
 
 #ifdef __cplusplus
diff --git a/tools/manual/manual_uclass.c b/tools/manual/manual_uclass.c
new file mode 100644
index 0000000000..fc24679e98
--- /dev/null
+++ b/tools/manual/manual_uclass.c
@@ -0,0 +1,70 @@
+/***********************************************************************
+ Freeciv - Copyright (C) 1996 - A Kjeldberg, L Gregersen, P Unold
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2, or (at your option)
+   any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+***********************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include <fc_config.h>
+#endif
+
+/* utility */
+#include "fcintl.h"
+
+/* common */
+#include "game.h"
+#include "tech.h"
+
+/* client */
+#include "helpdata.h"
+
+/* tools/manual */
+#include "fc_manual.h"
+
+/**********************************************************************//**
+  Write unit classes manual page
+
+  @param  tag_info  Tag set to use
+  @return           Success
+**************************************************************************/
+bool manual_uclasses(struct tag_types *tag_info)
+{
+  FILE *doc;
+
+  doc = manual_start(tag_info, MANUAL_UCLASS);
+
+  if (doc == NULL) {
+    return FALSE;
+  }
+
+  /* TRANS: markup ... Freeciv version ... ruleset name ... markup */
+  fprintf(doc, _("%sFreeciv %s unit classes help (%s)%s\n\n"),
+          tag_info->title_begin, VERSION_STRING, game.control.name,
+          tag_info->title_end);
+  unit_class_iterate(uclass) {
+    char buf[64000];
+
+    fprintf(doc, tag_info->item_begin, "uclass", uclass_index(uclass));
+    fprintf(doc, "%s%s%s\n\n", tag_info->sect_title_begin,
+            uclass_name_translation(uclass), tag_info->sect_title_end);
+
+    fprintf(doc, tag_info->subitem_begin, "helptext");
+    buf[0] = '\0';
+    helptext_unitclass(uclass, buf, sizeof(buf));
+    fprintf(doc, "%s", buf);
+    fprintf(doc, "%s", tag_info->subitem_end);
+
+    fprintf(doc, "%s", tag_info->item_end);
+  } unit_class_iterate_end;
+
+  manual_finalize(tag_info, doc, MANUAL_UCLASS);
+
+  return TRUE;
+}
diff --git a/tools/manual/manual_units.c b/tools/manual/manual_units.c
index aaef2dfe5f..350deb1182 100644
--- a/tools/manual/manual_units.c
+++ b/tools/manual/manual_units.c
@@ -99,7 +99,7 @@ bool manual_units(struct tag_types *tag_info)
             utype_name_translation(putype->obsoleted_by));
     fprintf(doc, "%s", tag_info->subitem_end);
     fprintf(doc, tag_info->subitem_begin, "helptext");
-    helptext_unit(buf, sizeof(buf), NULL, "", putype);
+    helptext_unit(buf, sizeof(buf), NULL, "", putype, FALSE);
     fprintf(doc, "%s", buf);
     fprintf(doc, "%s", tag_info->subitem_end);
     fprintf(doc, "%s", tag_info->item_end);
-- 
2.43.0

