From 6f5f4207fee9f36c3fde8c3a837da2a21c8de9ea Mon Sep 17 00:00:00 2001
From: Marko Lindqvist <cazfi74@gmail.com>
Date: Sat, 12 Jul 2025 14:06:32 +0300
Subject: [PATCH 84/84] Add government flag requirement type

See RM #1501

Signed-off-by: Marko Lindqvist <cazfi74@gmail.com>
---
 ai/default/daieffects.c              |  3 ++
 ai/default/daimilitary.c             |  3 +-
 common/fc_types.h                    |  3 +-
 common/reqtext.c                     | 14 ++++++++
 common/requirements.c                | 51 ++++++++++++++++++++++++++++
 doc/README.effects                   |  1 +
 gen_headers/enums/fc_types_enums.def |  1 +
 server/cityturn.c                    | 23 +++++++++++++
 server/ruleset/rssanity.c            |  1 +
 tools/ruledit/univ_value.c           |  8 +++++
 10 files changed, 106 insertions(+), 2 deletions(-)

diff --git a/ai/default/daieffects.c b/ai/default/daieffects.c
index 37cc165dd9..b6b55af8d1 100644
--- a/ai/default/daieffects.c
+++ b/ai/default/daieffects.c
@@ -827,6 +827,9 @@ bool dai_can_requirement_be_met_in_city(const struct requirement *preq,
     /* We can't meet a government requirement if we have a better one. */
     return !have_better_government(pplayer, preq->source.value.govern);
 
+  case VUT_GOVFLAG:
+    break;
+
   case VUT_IMPROVEMENT:
   case VUT_SITE:
   {
diff --git a/ai/default/daimilitary.c b/ai/default/daimilitary.c
index f18956432f..630c89b869 100644
--- a/ai/default/daimilitary.c
+++ b/ai/default/daimilitary.c
@@ -431,7 +431,7 @@ tactical_req_cb(const struct req_context *context,
   case VUT_UTFLAG:
   case VUT_UCLASS:
   case VUT_UCFLAG:
-    /* FIXME: support converting siege machines (needs hard reqs checked) */
+    /* FIXME: Support converting siege machines (needs hard reqs checked) */
   case VUT_ACTION:
   case VUT_OTYPE:
   case VUT_SPECIALIST:
@@ -449,6 +449,7 @@ tactical_req_cb(const struct req_context *context,
   case VUT_ADVANCE:
   case VUT_TECHFLAG:
   case VUT_GOVERNMENT:
+  case VUT_GOVFLAG:
   case VUT_ACHIEVEMENT:
   case VUT_IMPR_GENUS:
   case VUT_IMPR_FLAG:
diff --git a/common/fc_types.h b/common/fc_types.h
index 268dccb787..d9f20cab9b 100644
--- a/common/fc_types.h
+++ b/common/fc_types.h
@@ -521,7 +521,7 @@ enum req_problem_type {
   (x == RPT_CERTAIN ? RPT_POSSIBLE : RPT_CERTAIN)
 
 /* Originally in requirements.h, bumped up and revised to unify with
- * city_production and worklists.  Functions remain in requirements.c
+ * city_production and worklists. Functions remain in requirements.c
  * Used in the network protocol. */
 typedef union {
   struct advance *advance;
@@ -555,6 +555,7 @@ typedef union {
   Output_type_id outputtype;
   int terrainclass;			/* enum terrain_class */
   int terrainalter;                     /* enum terrain_alteration */
+  int govflag;                          /* enum gov_flag_id */
   int unitclassflag;			/* enum unit_class_flag_id */
   int unitflag;				/* enum unit_flag_id */
   int terrainflag;                      /* enum terrain_flag_id */
diff --git a/common/reqtext.c b/common/reqtext.c
index 312013de48..92821f6d69 100644
--- a/common/reqtext.c
+++ b/common/reqtext.c
@@ -250,6 +250,20 @@ bool req_text_insert(char *buf, size_t bufsz, struct player *pplayer,
     }
     return TRUE;
 
+  case VUT_GOVFLAG:
+    if (preq->range != REQ_RANGE_PLAYER) {
+      break;
+    }
+    fc_strlcat(buf, prefix, bufsz);
+    if (preq->present) {
+      cat_snprintf(buf, bufsz, _("Requires a %s government."),
+                   gov_flag_id_translated_name(preq->source.value.govflag));
+    } else {
+      cat_snprintf(buf, bufsz, _("Not available under a %s government."),
+                   gov_flag_id_translated_name(preq->source.value.govflag));
+    }
+    return TRUE;
+
   case VUT_ACHIEVEMENT:
     switch (preq->range) {
     case REQ_RANGE_PLAYER:
diff --git a/common/requirements.c b/common/requirements.c
index 681c435abf..c067b529ec 100644
--- a/common/requirements.c
+++ b/common/requirements.c
@@ -304,6 +304,12 @@ void universal_value_from_str(struct universal *source, const char *value)
       return;
     }
     break;
+  case VUT_GOVFLAG:
+    source->value.govflag = gov_flag_id_by_name(value, fc_strcasecmp);
+    if (gov_flag_id_is_valid(source->value.govflag)) {
+      return;
+    }
+    break;
   case VUT_ACHIEVEMENT:
     source->value.achievement = achievement_by_rule_name(value);
     if (source->value.achievement != nullptr) {
@@ -685,6 +691,9 @@ struct universal universal_by_number(const enum universals_n kind,
       return source;
     }
     break;
+  case VUT_GOVFLAG:
+    source.value.govflag = value;
+    return source;
   case VUT_ACHIEVEMENT:
     source.value.achievement = achievement_by_number(value);
     if (source.value.achievement != nullptr) {
@@ -938,6 +947,8 @@ int universal_number(const struct universal *source)
     return source->value.techflag;
   case VUT_GOVERNMENT:
     return government_number(source->value.govern);
+  case VUT_GOVFLAG:
+    return source->value.govflag;
   case VUT_ACHIEVEMENT:
     return achievement_number(source->value.achievement);
   case VUT_STYLE:
@@ -1182,6 +1193,7 @@ struct requirement req_from_str(const char *type, const char *range,
         req.range = REQ_RANGE_CITY;
         break;
       case VUT_GOVERNMENT:
+      case VUT_GOVFLAG:
       case VUT_ACHIEVEMENT:
       case VUT_STYLE:
       case VUT_ADVANCE:
@@ -1253,6 +1265,7 @@ struct requirement req_from_str(const char *type, const char *range,
                  && req.range != REQ_RANGE_LOCAL);
       break;
     case VUT_GOVERNMENT:
+    case VUT_GOVFLAG:
     case VUT_AI_LEVEL:
     case VUT_STYLE:
     case VUT_MINCITIES:
@@ -1439,6 +1452,7 @@ struct requirement req_from_str(const char *type, const char *range,
     case VUT_PLAYER_FLAG:
     case VUT_PLAYER_STATE:
     case VUT_GOVERNMENT:
+    case VUT_GOVFLAG:
     case VUT_TERRAIN:
     case VUT_UTYPE:
     case VUT_UTFLAG:
@@ -3882,6 +3896,30 @@ is_gov_req_active(const struct civ_map *nmap,
   }
 }
 
+/**********************************************************************//**
+  Determine whether a government flag requirement is satisfied in a given
+  context, ignoring parts of the requirement that can be handled uniformly
+  for all requirement types.
+
+  context, other_context and req must not be null,
+  and req must be a gov flag requirement
+**************************************************************************/
+static enum fc_tristate
+is_govflag_req_active(const struct civ_map *nmap,
+                      const struct req_context *context,
+                      const struct req_context *other_context,
+                      const struct requirement *req)
+{
+  IS_REQ_ACTIVE_VARIANT_ASSERT(VUT_GOVFLAG);
+
+  if (context->player == nullptr) {
+    return TRI_MAYBE;
+  } else {
+    return BOOL_TO_TRISTATE(BV_ISSET(government_of_player(context->player)->flags,
+                                     req->source.value.govflag));
+  }
+}
+
 /**********************************************************************//**
   Determine whether a style requirement is satisfied in a given context,
   ignoring parts of the requirement that can be handled uniformly for all
@@ -6433,6 +6471,7 @@ static struct req_def req_definitions[VUT_COUNT] = {
   [VUT_FUTURETECHS] = {is_futuretechs_req_active, REQUCH_ACT, REQUC_WORLD},
   [VUT_GOOD] = {is_good_req_active, REQUCH_NO},
   [VUT_GOVERNMENT] = {is_gov_req_active, REQUCH_NO},
+  [VUT_GOVFLAG] = {is_govflag_req_active, REQUCH_NO},
   [VUT_IMPROVEMENT] = {is_building_req_active, REQUCH_NO, REQUC_IMPR},
   [VUT_SITE] = {is_building_req_active, REQUCH_NO, REQUC_IMPR},
   [VUT_IMPR_GENUS] = {is_buildinggenus_req_active, REQUCH_YES},
@@ -6977,6 +7016,7 @@ bool universal_never_there(const struct universal *source)
   case VUT_ADVANCE:
   case VUT_TECHFLAG:
   case VUT_GOVERNMENT:
+  case VUT_GOVFLAG:
   case VUT_ACHIEVEMENT:
   case VUT_IMPROVEMENT:
   case VUT_SITE:
@@ -7590,6 +7630,8 @@ bool are_universals_equal(const struct universal *psource1,
     return psource1->value.techflag == psource2->value.techflag;
   case VUT_GOVERNMENT:
     return psource1->value.govern == psource2->value.govern;
+  case VUT_GOVFLAG:
+    return psource1->value.govflag == psource2->value.govflag;
   case VUT_ACHIEVEMENT:
     return psource1->value.achievement == psource2->value.achievement;
   case VUT_STYLE:
@@ -7753,6 +7795,8 @@ const char *universal_rule_name(const struct universal *psource)
     return tech_flag_id_name(psource->value.techflag);
   case VUT_GOVERNMENT:
     return government_rule_name(psource->value.govern);
+  case VUT_GOVFLAG:
+    return gov_flag_id_name(psource->value.govflag);
   case VUT_ACHIEVEMENT:
     return achievement_rule_name(psource->value.achievement);
   case VUT_STYLE:
@@ -7921,6 +7965,10 @@ const char *universal_name_translation(const struct universal *psource,
     fc_strlcat(buf, government_name_translation(psource->value.govern),
                bufsz);
     return buf;
+  case VUT_GOVFLAG:
+    cat_snprintf(buf, bufsz, _("\"%s\" gov"),
+                 gov_flag_id_translated_name(psource->value.govflag));
+    return buf;
   case VUT_ACHIEVEMENT:
     fc_strlcat(buf, achievement_name_translation(psource->value.achievement),
                bufsz);
@@ -8551,6 +8599,9 @@ static enum req_item_found government_found(const struct requirement *preq,
   if (preq->source.kind == VUT_GOVERNMENT) {
     return preq->source.value.govern == source->value.govern ? ITF_YES
                                                              : ITF_NO;
+  } else if (preq->source.kind == VUT_GOVFLAG) {
+    return BV_ISSET(source->value.govern->flags, preq->source.value.govflag)
+      ? ITF_YES : ITF_NO;
   }
 
   return ITF_NOT_APPLICABLE;
diff --git a/doc/README.effects b/doc/README.effects
index 2f7d9a9e6a..d4ff226abb 100644
--- a/doc/README.effects
+++ b/doc/README.effects
@@ -65,6 +65,7 @@ MinCities:         Player
 Achievement:       World, Alliance, Team, Player
 Counter:           City
 Gov:               Player
+GovFlag:           Player
 Building:          World, Alliance, Team, Player, Continent, Traderoute,
                    City, Tile, Local
 BuildingFlag:      Local, Tile, City
diff --git a/gen_headers/enums/fc_types_enums.def b/gen_headers/enums/fc_types_enums.def
index 0ac84c3c2f..74b483126e 100644
--- a/gen_headers/enums/fc_types_enums.def
+++ b/gen_headers/enums/fc_types_enums.def
@@ -61,6 +61,7 @@ values
   FUTURETECHS        "FutureTechs"
   GOOD               "Good"
   GOVERNMENT         "Gov"
+  GOVFLAG            "GovFlag"
   IMPROVEMENT        "Building"
   IMPR_FLAG          "BuildingFlag"
   IMPR_GENUS         "BuildingGenus"
diff --git a/server/cityturn.c b/server/cityturn.c
index ffe84238ef..0dc5599517 100644
--- a/server/cityturn.c
+++ b/server/cityturn.c
@@ -1425,6 +1425,29 @@ static bool worklist_item_postpone_req_vec(struct universal *target,
                                     pcity, "have_government");
         }
         break;
+      case VUT_GOVFLAG:
+        if (preq->present) {
+          notify_player(pplayer, city_tile(pcity),
+                        E_CITY_CANTBUILD, ftc_server,
+                        _("%s can't build %s from the worklist; "
+                          "it needs %s government. Postponing..."),
+                        city_link(pcity),
+                        tgt_name,
+                        gov_flag_id_translated_name(preq->source.value.govflag));
+          script_server_signal_emit(signal_name, ptarget,
+                                    pcity, "need_govflag");
+        } else {
+          notify_player(pplayer, city_tile(pcity),
+                        E_CITY_CANTBUILD, ftc_server,
+                        _("%s can't build %s from the worklist; "
+                          "it cannot have %s government. Postponing..."),
+                        city_link(pcity),
+                        tgt_name,
+                        gov_flag_id_translated_name(preq->source.value.govflag));
+          script_server_signal_emit(signal_name, ptarget,
+                                    pcity, "have_govflag");
+        }
+        break;
       case VUT_ACHIEVEMENT:
         if (preq->present) {
           notify_player(pplayer, city_tile(pcity),
diff --git a/server/ruleset/rssanity.c b/server/ruleset/rssanity.c
index 9da47b0f3b..0c3d5490f6 100644
--- a/server/ruleset/rssanity.c
+++ b/server/ruleset/rssanity.c
@@ -442,6 +442,7 @@ static bool sanity_check_req_set(rs_conversion_logger logger,
     case VUT_UCLASS:
     case VUT_TILE_REL:
       /* Can check different properties. */
+    case VUT_GOVFLAG:
     case VUT_UTFLAG:
     case VUT_UCFLAG:
     case VUT_TERRFLAG:
diff --git a/tools/ruledit/univ_value.c b/tools/ruledit/univ_value.c
index d94afab0dd..2e30ed5b97 100644
--- a/tools/ruledit/univ_value.c
+++ b/tools/ruledit/univ_value.c
@@ -60,6 +60,9 @@ bool universal_value_initial(struct universal *src)
   case VUT_GOVERNMENT:
     src->value.govern = game.government_during_revolution;
     return TRUE;
+  case VUT_GOVFLAG:
+    src->value.govflag = 0;
+    return TRUE;
   case VUT_IMPROVEMENT:
   case VUT_SITE:
     if (game.control.num_impr_types <= 0) {
@@ -302,6 +305,11 @@ void universal_kind_values(struct universal *univ,
       cb(government_rule_name(pgov), univ->value.govern == pgov, data);
     } governments_re_active_iterate_end;
     break;
+  case VUT_GOVFLAG:
+    for (i = 0; i < GOVF_LAST_USER_FLAG; i++) {
+      cb(gov_flag_id_name(i), univ->value.govflag == i, data);
+    }
+    break;
   case VUT_IMPROVEMENT:
   case VUT_SITE:
     improvement_re_active_iterate(pimpr) {
-- 
2.47.2

