From 0768027db92131d8e38480fef0431ca1af9ae87d Mon Sep 17 00:00:00 2001
From: Marko Lindqvist <cazfi74@gmail.com>
Date: Thu, 27 Aug 2026 05:08:46 +0300
Subject: [PATCH 56/56] Tiledef: Add "known" property

See RM #2170

Signed-off-by: Marko Lindqvist <cazfi74@gmail.com>
---
 client/packhand.c                |  2 ++
 common/networking/packets.def    |  1 +
 common/requirements.c            | 25 +++++++++++++++++--------
 common/tiledef.c                 | 16 +++++++++++-----
 common/tiledef.h                 |  8 +++++---
 data/alien/terrain.ruleset       |  1 +
 data/civ1/terrain.ruleset        |  1 +
 data/civ2/terrain.ruleset        |  1 +
 data/civ2civ3/terrain.ruleset    |  1 +
 data/classic/terrain.ruleset     |  1 +
 data/goldkeep/terrain.ruleset    |  1 +
 data/granularity/terrain.ruleset |  1 +
 data/multiplayer/terrain.ruleset |  1 +
 data/ruledit/comments-3.4.txt    |  1 +
 data/sandbox/terrain.ruleset     |  1 +
 data/stub/terrain.ruleset        |  1 +
 server/aahand.c                  |  2 +-
 server/ruleset/ruleload.c        |  7 +++++++
 tools/ruleutil/rulesave.c        |  4 ++++
 19 files changed, 59 insertions(+), 17 deletions(-)

diff --git a/client/packhand.c b/client/packhand.c
index e10fcd89a8..84f653460c 100644
--- a/client/packhand.c
+++ b/client/packhand.c
@@ -4546,6 +4546,8 @@ void handle_ruleset_tiledef(const struct packet_ruleset_tiledef *p)
       extra_type_list_append(td->extras, pextra);
     }
   } extra_type_iterate_end;
+
+  td->known = p->known;
 }
 
 /************************************************************************//**
diff --git a/common/networking/packets.def b/common/networking/packets.def
index 59ccfef855..9dbd9cb2cb 100644
--- a/common/networking/packets.def
+++ b/common/networking/packets.def
@@ -1961,6 +1961,7 @@ PACKET_RULESET_TILEDEF = 520; sc, lsend
   STRING rule_name[MAX_LEN_NAME];
 
   BV_EXTRAS extras;
+  BOOL known;
 end
 
 /**************************************************************************
diff --git a/common/requirements.c b/common/requirements.c
index 7d9d3f076b..c27faca103 100644
--- a/common/requirements.c
+++ b/common/requirements.c
@@ -3294,32 +3294,41 @@ is_tiledef_req_active(const struct civ_map *nmap,
 
   ptdef = req->source.value.tiledef;
 
+  if (ptdef->known && context->player == nullptr) {
+    return TRI_MAYBE;
+  }
+
   switch (req->range) {
   case REQ_RANGE_TILE:
     /* The requirement is filled if the tile has tiledef of requested type. */
     if (!context->tile) {
       return TRI_MAYBE;
     }
-    return BOOL_TO_TRISTATE(tile_matches_tiledef(ptdef, context->tile));
+    return BOOL_TO_TRISTATE(tile_matches_tiledef(ptdef, context->tile,
+                                                 context->player));
   case REQ_RANGE_CADJACENT:
     if (!context->tile) {
       return TRI_MAYBE;
     }
-    return BOOL_TO_TRISTATE(tile_matches_tiledef(ptdef, context->tile)
-                            || is_tiledef_card_near(nmap, context->tile, ptdef));
+    return BOOL_TO_TRISTATE(tile_matches_tiledef(ptdef, context->tile,
+                                                 context->player)
+                            || is_tiledef_card_near(nmap, context->tile, ptdef,
+                                                    context->player));
   case REQ_RANGE_ADJACENT:
     if (!context->tile) {
       return TRI_MAYBE;
     }
-    return BOOL_TO_TRISTATE(tile_matches_tiledef(ptdef, context->tile)
-                            || is_tiledef_near_tile(nmap, context->tile, ptdef));
+    return BOOL_TO_TRISTATE(tile_matches_tiledef(ptdef, context->tile,
+                                                 context->player)
+                            || is_tiledef_near_tile(nmap, context->tile, ptdef,
+                                                    context->player));
   case REQ_RANGE_CITY:
     if (!context->city) {
       return TRI_MAYBE;
     }
     city_tile_iterate(nmap, city_map_radius_sq_get(context->city),
                       city_tile(context->city), ptile) {
-      if (tile_matches_tiledef(ptdef, ptile)) {
+      if (tile_matches_tiledef(ptdef, ptile, context->player)) {
         return TRI_YES;
       }
     } city_tile_iterate_end;
@@ -3332,7 +3341,7 @@ is_tiledef_req_active(const struct civ_map *nmap,
     }
     city_tile_iterate(nmap, city_map_radius_sq_get(context->city),
                       city_tile(context->city), ptile) {
-      if (tile_matches_tiledef(ptdef, ptile)) {
+      if (tile_matches_tiledef(ptdef, ptile, context->player)) {
         return TRI_YES;
       }
     } city_tile_iterate_end;
@@ -3344,7 +3353,7 @@ is_tiledef_req_active(const struct civ_map *nmap,
       } else {
         city_tile_iterate(nmap, city_map_radius_sq_get(trade_partner),
                           city_tile(trade_partner), ptile) {
-          if (tile_matches_tiledef(ptdef, ptile)) {
+          if (tile_matches_tiledef(ptdef, ptile, context->player)) {
             return TRI_YES;
           }
         } city_tile_iterate_end;
diff --git a/common/tiledef.c b/common/tiledef.c
index f972f3d93b..550ef5baa0 100644
--- a/common/tiledef.c
+++ b/common/tiledef.c
@@ -147,7 +147,8 @@ struct tiledef *tiledef_by_translated_name(const char *name)
 /************************************************************************//**
   Check if tile matches tiledef
 ****************************************************************************/
-bool tile_matches_tiledef(const struct tiledef *td, const struct tile *ptile)
+bool tile_matches_tiledef(const struct tiledef *td, const struct tile *ptile,
+                          const struct player *pplayer)
 {
   if (terrain_flag_id_is_valid(td->terr_flag)) {
     if (!terrain_has_flag(tile_terrain(ptile), td->terr_flag)) {
@@ -161,6 +162,11 @@ bool tile_matches_tiledef(const struct tiledef *td, const struct tile *ptile)
     }
   } extra_type_list_iterate_end;
 
+  if (td->known && pplayer != nullptr
+      && tile_get_known(ptile, pplayer) == TILE_UNKNOWN) {
+    return FALSE;
+  }
+
   return TRUE;
 }
 
@@ -169,10 +175,10 @@ bool tile_matches_tiledef(const struct tiledef *td, const struct tile *ptile)
   (Does not check ptile itself.)
 ****************************************************************************/
 bool is_tiledef_card_near(const struct civ_map *nmap, const struct tile *ptile,
-                          const struct tiledef *ptd)
+                          const struct tiledef *ptd, const struct player *pplayer)
 {
   cardinal_adjc_iterate(nmap, ptile, adjc_tile) {
-    if (tile_matches_tiledef(ptd, adjc_tile)) {
+    if (tile_matches_tiledef(ptd, adjc_tile, pplayer)) {
       return TRUE;
     }
   } cardinal_adjc_iterate_end;
@@ -185,10 +191,10 @@ bool is_tiledef_card_near(const struct civ_map *nmap, const struct tile *ptile,
   (Does not check ptile itself.)
 ****************************************************************************/
 bool is_tiledef_near_tile(const struct civ_map *nmap, const struct tile *ptile,
-                          const struct tiledef *ptd)
+                          const struct tiledef *ptd, const struct player *pplayer)
 {
   adjc_iterate(nmap, ptile, adjc_tile) {
-    if (tile_matches_tiledef(ptd, adjc_tile)) {
+    if (tile_matches_tiledef(ptd, adjc_tile, pplayer)) {
       return TRUE;
     }
   } adjc_iterate_end;
diff --git a/common/tiledef.h b/common/tiledef.h
index 0dc3e3802d..463d99b57d 100644
--- a/common/tiledef.h
+++ b/common/tiledef.h
@@ -28,6 +28,7 @@ struct tiledef
 
   enum terrain_flag_id terr_flag;
   struct extra_type_list *extras;
+  bool known;
 };
 
 void tiledefs_init(void);
@@ -57,13 +58,14 @@ struct tiledef *tiledef_by_translated_name(const char *name);
   }                                               \
 }
 
-bool tile_matches_tiledef(const struct tiledef *td, const struct tile *ptile)
+bool tile_matches_tiledef(const struct tiledef *td, const struct tile *ptile,
+                          const struct player *pplayer)
   fc__attribute((nonnull (1, 2)));
 
 bool is_tiledef_card_near(const struct civ_map *nmap, const struct tile *ptile,
-                          const struct tiledef *ptd);
+                          const struct tiledef *ptd, const struct player *pplayer);
 bool is_tiledef_near_tile(const struct civ_map *nmap, const struct tile *ptile,
-                          const struct tiledef *ptd);
+                          const struct tiledef *ptd, const struct player *pplayer);
 
 #ifdef __cplusplus
 }
diff --git a/data/alien/terrain.ruleset b/data/alien/terrain.ruleset
index 2d401237b4..3e75085e6f 100644
--- a/data/alien/terrain.ruleset
+++ b/data/alien/terrain.ruleset
@@ -1564,5 +1564,6 @@ flags          = "River", "UnrestrictedInfra"
 ;                        to satisfy tiledef requirements.
 ; extras               = List of extras that the tile must have
 ;                        to satisfy tiledef requirements.
+; known                = Whether the target player must know the tile or not
 ;
 ; */ <-- avoid gettext warnings
diff --git a/data/civ1/terrain.ruleset b/data/civ1/terrain.ruleset
index e3c587124a..7d01b977c8 100644
--- a/data/civ1/terrain.ruleset
+++ b/data/civ1/terrain.ruleset
@@ -1521,5 +1521,6 @@ flags          = "River", "UnrestrictedInfra"
 ;                        to satisfy tiledef requirements.
 ; extras               = List of extras that the tile must have
 ;                        to satisfy tiledef requirements.
+; known                = Whether the target player must know the tile or not
 ;
 ; */ <-- avoid gettext warnings
diff --git a/data/civ2/terrain.ruleset b/data/civ2/terrain.ruleset
index d9ec189580..f96475824b 100644
--- a/data/civ2/terrain.ruleset
+++ b/data/civ2/terrain.ruleset
@@ -1823,5 +1823,6 @@ flags          = "River", "UnrestrictedInfra"
 ;                        to satisfy tiledef requirements.
 ; extras               = List of extras that the tile must have
 ;                        to satisfy tiledef requirements.
+; known                = Whether the target player must know the tile or not
 ;
 ; */ <-- avoid gettext warnings
diff --git a/data/civ2civ3/terrain.ruleset b/data/civ2civ3/terrain.ruleset
index bb62fda91c..3f5cc75bba 100644
--- a/data/civ2civ3/terrain.ruleset
+++ b/data/civ2civ3/terrain.ruleset
@@ -2354,5 +2354,6 @@ gui_type       = "Other"
 ;                        to satisfy tiledef requirements.
 ; extras               = List of extras that the tile must have
 ;                        to satisfy tiledef requirements.
+; known                = Whether the target player must know the tile or not
 ;
 ; */ <-- avoid gettext warnings
diff --git a/data/classic/terrain.ruleset b/data/classic/terrain.ruleset
index 98550d4a41..6e04348314 100644
--- a/data/classic/terrain.ruleset
+++ b/data/classic/terrain.ruleset
@@ -2122,5 +2122,6 @@ gui_type          = "Other"
 ;                        to satisfy tiledef requirements.
 ; extras               = List of extras that the tile must have
 ;                        to satisfy tiledef requirements.
+; known                = Whether the target player must know the tile or not
 ;
 ; */ <-- avoid gettext warnings
diff --git a/data/goldkeep/terrain.ruleset b/data/goldkeep/terrain.ruleset
index 963b53a33d..dd8a6beda4 100644
--- a/data/goldkeep/terrain.ruleset
+++ b/data/goldkeep/terrain.ruleset
@@ -2146,5 +2146,6 @@ gui_type       = "Other"
 ;                        to satisfy tiledef requirements.
 ; extras               = List of extras that the tile must have
 ;                        to satisfy tiledef requirements.
+; known                = Whether the target player must know the tile or not
 ;
 ; */ <-- avoid gettext warnings
diff --git a/data/granularity/terrain.ruleset b/data/granularity/terrain.ruleset
index afbedc957e..5bae2cb5e6 100644
--- a/data/granularity/terrain.ruleset
+++ b/data/granularity/terrain.ruleset
@@ -1040,5 +1040,6 @@ flags          = "River", "UnrestrictedInfra"
 ;                        to satisfy tiledef requirements.
 ; extras               = List of extras that the tile must have
 ;                        to satisfy tiledef requirements.
+; known                = Whether the target player must know the tile or not
 ;
 ; */ <-- avoid gettext warnings
diff --git a/data/multiplayer/terrain.ruleset b/data/multiplayer/terrain.ruleset
index c461174e12..c8af7afca6 100644
--- a/data/multiplayer/terrain.ruleset
+++ b/data/multiplayer/terrain.ruleset
@@ -2086,5 +2086,6 @@ gui_type       = "Other"
 ;                        to satisfy tiledef requirements.
 ; extras               = List of extras that the tile must have
 ;                        to satisfy tiledef requirements.
+; known                = Whether the target player must know the tile or not
 ;
 ; */ <-- avoid gettext warnings
diff --git a/data/ruledit/comments-3.4.txt b/data/ruledit/comments-3.4.txt
index 1b1ae22ff2..9f02e31b50 100644
--- a/data/ruledit/comments-3.4.txt
+++ b/data/ruledit/comments-3.4.txt
@@ -753,6 +753,7 @@ tiledefs = "\
 ;                        to satisfy tiledef requirements.\n\
 ; extras               = List of extras that the tile must have\n\
 ;                        to satisfy tiledef requirements.\n\
+; known                = Whether the target player must know the tile or not\n\
 ;\n\
 ; */ <-- avoid gettext warnings\n\
 "
diff --git a/data/sandbox/terrain.ruleset b/data/sandbox/terrain.ruleset
index 31182de657..04547db11f 100644
--- a/data/sandbox/terrain.ruleset
+++ b/data/sandbox/terrain.ruleset
@@ -2471,6 +2471,7 @@ flags          = "River", "JumpFrom", "JumpTo"
 ;                        to satisfy tiledef requirements.
 ; extras               = List of extras that the tile must have
 ;                        to satisfy tiledef requirements.
+; known                = Whether the target player must know the tile or not
 ;
 ; */ <-- avoid gettext warnings
 
diff --git a/data/stub/terrain.ruleset b/data/stub/terrain.ruleset
index 00a5c837ed..a9e8f01cbf 100644
--- a/data/stub/terrain.ruleset
+++ b/data/stub/terrain.ruleset
@@ -656,5 +656,6 @@ rmact_gfx_alt2 = "-"
 ;                        to satisfy tiledef requirements.
 ; extras               = List of extras that the tile must have
 ;                        to satisfy tiledef requirements.
+; known                = Whether the target player must know the tile or not
 ;
 ; */ <-- avoid gettext warnings
diff --git a/server/aahand.c b/server/aahand.c
index a032c7fc76..77ba892223 100644
--- a/server/aahand.c
+++ b/server/aahand.c
@@ -98,7 +98,7 @@ void access_areas_refresh(struct civ_map *nmap, struct player *plr)
         pf_map_tiles_iterate(pfm, ptile, TRUE) {
           if (ptile != nullptr) {
             tiledef_iterate(td) {
-              if (tile_matches_tiledef(td, ptile)) {
+              if (tile_matches_tiledef(td, ptile, plr)) {
                 int tn = tiledef_number(td);
 
                 BV_SET(aarea->tiledefs, tn);
diff --git a/server/ruleset/ruleload.c b/server/ruleset/ruleload.c
index 65e2ebcfe2..f79ca7c9f4 100644
--- a/server/ruleset/ruleload.c
+++ b/server/ruleset/ruleload.c
@@ -4334,6 +4334,11 @@ static bool load_ruleset_terrain(struct section_file *file,
           ok = FALSE;
         }
 
+        if (ok) {
+          td->known = secfile_lookup_bool_default(file, FALSE,
+                                                  "%s.known", section);
+        }
+
         if (!ok) {
           break;
         }
@@ -8884,6 +8889,8 @@ static void send_ruleset_tiledefs(struct conn_list *dest)
 
     packet.extras = extras;
 
+    packet.known = td->known;
+
     lsend_packet_ruleset_tiledef(dest, &packet);
   } tiledef_iterate_end;
 }
diff --git a/tools/ruleutil/rulesave.c b/tools/ruleutil/rulesave.c
index f479b7554c..2405012203 100644
--- a/tools/ruleutil/rulesave.c
+++ b/tools/ruleutil/rulesave.c
@@ -3129,6 +3129,10 @@ static bool save_terrain_ruleset(const char *filename, const char *name)
       secfile_insert_str_vec(sfile, extra_names, set_count,
                              "%s.extras", path);
     }
+
+    if (td->known) {
+      secfile_insert_bool(sfile, td->known, "%s.known", path);
+    }
   } tiledef_iterate_end;
 
   return save_ruleset_file(sfile, filename);
-- 
2.53.0

