From 498ef2b51618143a368210b542a2ca67212a93c2 Mon Sep 17 00:00:00 2001
From: Marko Lindqvist <cazfi74@gmail.com>
Date: Mon, 8 Jan 2024 23:29:04 +0200
Subject: [PATCH 41/41] Add "either_reqs" support for clauses

See RM #152

Signed-off-by: Marko Lindqvist <cazfi74@gmail.com>
---
 client/packhand.c               |  5 +++++
 common/diptreaty.c              | 10 ++++++++++
 common/diptreaty.h              |  1 +
 common/networking/packets.def   |  2 ++
 data/alien/game.ruleset         |  5 +++--
 data/civ1/game.ruleset          |  5 +++--
 data/civ2/game.ruleset          |  5 +++--
 data/civ2civ3/game.ruleset      |  5 +++--
 data/classic/game.ruleset       |  5 +++--
 data/goldkeep/game.ruleset      |  5 +++--
 data/granularity/game.ruleset   |  5 +++--
 data/multiplayer/game.ruleset   |  5 +++--
 data/ruledit/comments-3.3.txt   |  5 +++--
 data/sandbox/game.ruleset       |  5 +++--
 data/stub/game.ruleset          |  5 +++--
 data/webperimental/game.ruleset |  5 +++--
 server/ruleset/ruleload.c       | 13 +++++++++++++
 tools/ruleutil/rulesave.c       |  1 +
 18 files changed, 68 insertions(+), 24 deletions(-)

diff --git a/client/packhand.c b/client/packhand.c
index 0065fd10a4..dca1fd7af0 100644
--- a/client/packhand.c
+++ b/client/packhand.c
@@ -4733,6 +4733,11 @@ void handle_ruleset_clause(const struct packet_ruleset_clause *p)
     requirement_vector_append(&info->receiver_reqs, p->receiver_reqs[i]);
   }
   fc_assert(info->receiver_reqs.size == p->receiver_reqs_count);
+
+  for (i = 0; i < p->either_reqs_count; i++) {
+    requirement_vector_append(&info->either_reqs, p->either_reqs[i]);
+  }
+  fc_assert(info->either_reqs.size == p->either_reqs_count);
 }
 
 /************************************************************************//**
diff --git a/common/diptreaty.c b/common/diptreaty.c
index 6ad57fb6b0..2d724a6979 100644
--- a/common/diptreaty.c
+++ b/common/diptreaty.c
@@ -202,6 +202,16 @@ bool add_clause(struct Treaty *ptreaty, struct player *pfrom,
       return FALSE;
     }
   }
+  if (client_player == NULL) {
+    if (!are_reqs_active(&(const struct req_context) { .player = pfrom },
+                         pfrom, &clause_infos[type].either_reqs,
+                         RPT_POSSIBLE)
+        && !are_reqs_active(&(const struct req_context) { .player = pto },
+                         pfrom, &clause_infos[type].either_reqs,
+                            RPT_POSSIBLE)) {
+      return FALSE;
+    }
+  }
 
   clause_list_iterate(ptreaty->clauses, old_clause) {
     if (old_clause->type == type
diff --git a/common/diptreaty.h b/common/diptreaty.h
index 161f28778e..024d66b5df 100644
--- a/common/diptreaty.h
+++ b/common/diptreaty.h
@@ -57,6 +57,7 @@ struct clause_info
   bool enabled;
   struct requirement_vector giver_reqs;
   struct requirement_vector receiver_reqs;
+  struct requirement_vector either_reqs;
 };
 
 /* For when we need to iterate over treaties */
diff --git a/common/networking/packets.def b/common/networking/packets.def
index 25e57f26b4..ab55d78dce 100644
--- a/common/networking/packets.def
+++ b/common/networking/packets.def
@@ -1952,6 +1952,8 @@ PACKET_RULESET_CLAUSE = 512; sc, lsend
   REQUIREMENT giver_reqs[MAX_NUM_REQS:giver_reqs_count];
   UINT8  receiver_reqs_count;
   REQUIREMENT receiver_reqs[MAX_NUM_REQS:receiver_reqs_count];
+  UINT8  either_reqs_count;
+  REQUIREMENT either_reqs[MAX_NUM_REQS:either_reqs_count];
 end
 
 /**************************************************************************
diff --git a/data/alien/game.ruleset b/data/alien/game.ruleset
index dd8aa3ca86..d6ede54b4a 100644
--- a/data/alien/game.ruleset
+++ b/data/alien/game.ruleset
@@ -576,9 +576,10 @@ priority       = 1
 ; type                   = Type of the clause, one of "Advance", "Gold", "Map", "Seamap",
 ;                          "City", "Ceasefire", "Peace", "Alliance", "Vision", "Embassy",
 ;                          "SharedTiles"
-; giver_reqs             = requirements that the giving side of the clause needs to fulfill
+; giver_reqs             = requirements that the giving side of the clause needs to meet
 ;                          (see effects.ruleset and README.effects for help on requirements)
-; receiver_reqs          = requirements that the receiving side of the clause needs to fulfill
+; receiver_reqs          = requirements that the receiving side of the clause needs to meet
+; either_reqs            = requirements that either side of the clause transaction would need to meet
 ;
 ; */ <-- avoid gettext warnings
 
diff --git a/data/civ1/game.ruleset b/data/civ1/game.ruleset
index 7d4f94c509..5dd7a4c2d3 100644
--- a/data/civ1/game.ruleset
+++ b/data/civ1/game.ruleset
@@ -609,9 +609,10 @@ flags          = "Bidirectional"
 ; type                   = Type of the clause, one of "Advance", "Gold", "Map", "Seamap",
 ;                          "City", "Ceasefire", "Peace", "Alliance", "Vision", "Embassy",
 ;                          "SharedTiles"
-; giver_reqs             = requirements that the giving side of the clause needs to fulfill
+; giver_reqs             = requirements that the giving side of the clause needs to meet
 ;                          (see effects.ruleset and README.effects for help on requirements)
-; receiver_reqs          = requirements that the receiving side of the clause needs to fulfill
+; receiver_reqs          = requirements that the receiving side of the clause needs to meet
+; either_reqs            = requirements that either side of the clause transaction would need to meet
 ;
 ; */ <-- avoid gettext warnings
 
diff --git a/data/civ2/game.ruleset b/data/civ2/game.ruleset
index 103c4abce5..be93b7f281 100644
--- a/data/civ2/game.ruleset
+++ b/data/civ2/game.ruleset
@@ -540,9 +540,10 @@ flags          = "Bidirectional"
 ; type                   = Type of the clause, one of "Advance", "Gold", "Map", "Seamap",
 ;                          "City", "Ceasefire", "Peace", "Alliance", "Vision", "Embassy",
 ;                          "SharedTiles"
-; giver_reqs             = requirements that the giving side of the clause needs to fulfill
+; giver_reqs             = requirements that the giving side of the clause needs to meet
 ;                          (see effects.ruleset and README.effects for help on requirements)
-; receiver_reqs          = requirements that the receiving side of the clause needs to fulfill
+; receiver_reqs          = requirements that the receiving side of the clause needs to meet
+; either_reqs            = requirements that either side of the clause transaction would need to meet
 ;
 ; */ <-- avoid gettext warnings
 
diff --git a/data/civ2civ3/game.ruleset b/data/civ2civ3/game.ruleset
index 3ae749d3b6..7958951e02 100644
--- a/data/civ2civ3/game.ruleset
+++ b/data/civ2civ3/game.ruleset
@@ -673,9 +673,10 @@ priority       = 1
 ; type                   = Type of the clause, one of "Advance", "Gold", "Map", "Seamap",
 ;                          "City", "Ceasefire", "Peace", "Alliance", "Vision", "Embassy",
 ;                          "SharedTiles"
-; giver_reqs             = requirements that the giving side of the clause needs to fulfill
+; giver_reqs             = requirements that the giving side of the clause needs to meet
 ;                          (see effects.ruleset and README.effects for help on requirements)
-; receiver_reqs          = requirements that the receiving side of the clause needs to fulfill
+; receiver_reqs          = requirements that the receiving side of the clause needs to meet
+; either_reqs            = requirements that either side of the clause transaction would need to meet
 ;
 ; */ <-- avoid gettext warnings
 
diff --git a/data/classic/game.ruleset b/data/classic/game.ruleset
index fede7d36e7..433e372915 100644
--- a/data/classic/game.ruleset
+++ b/data/classic/game.ruleset
@@ -591,9 +591,10 @@ priority       = 1
 ; type                   = Type of the clause, one of "Advance", "Gold", "Map", "Seamap",
 ;                          "City", "Ceasefire", "Peace", "Alliance", "Vision", "Embassy",
 ;                          "SharedTiles"
-; giver_reqs             = requirements that the giving side of the clause needs to fulfill
+; giver_reqs             = requirements that the giving side of the clause needs to meet
 ;                          (see effects.ruleset and README.effects for help on requirements)
-; receiver_reqs          = requirements that the receiving side of the clause needs to fulfill
+; receiver_reqs          = requirements that the receiving side of the clause needs to meet
+; either_reqs            = requirements that either side of the clause transaction would need to meet
 ;
 ; */ <-- avoid gettext warnings
 
diff --git a/data/goldkeep/game.ruleset b/data/goldkeep/game.ruleset
index 6af8c94226..c03232015f 100644
--- a/data/goldkeep/game.ruleset
+++ b/data/goldkeep/game.ruleset
@@ -609,9 +609,10 @@ priority       = 1
 ; type                   = Type of the clause, one of "Advance", "Gold", "Map", "Seamap",
 ;                          "City", "Ceasefire", "Peace", "Alliance", "Vision", "Embassy",
 ;                          "SharedTiles"
-; giver_reqs             = requirements that the giving side of the clause needs to fulfill
+; giver_reqs             = requirements that the giving side of the clause needs to meet
 ;                          (see effects.ruleset and README.effects for help on requirements)
-; receiver_reqs          = requirements that the receiving side of the clause needs to fulfill
+; receiver_reqs          = requirements that the receiving side of the clause needs to meet
+; either_reqs            = requirements that either side of the clause transaction would need to meet
 ;
 ; */ <-- avoid gettext warnings
 
diff --git a/data/granularity/game.ruleset b/data/granularity/game.ruleset
index 5d1045ec73..a86dd582a3 100644
--- a/data/granularity/game.ruleset
+++ b/data/granularity/game.ruleset
@@ -560,9 +560,10 @@ priority       = 1
 ; type                   = Type of the clause, one of "Advance", "Gold", "Map", "Seamap",
 ;                          "City", "Ceasefire", "Peace", "Alliance", "Vision", "Embassy",
 ;                          "SharedTiles"
-; giver_reqs             = requirements that the giving side of the clause needs to fulfill
+; giver_reqs             = requirements that the giving side of the clause needs to meet
 ;                          (see effects.ruleset and README.effects for help on requirements)
-; receiver_reqs          = requirements that the receiving side of the clause needs to fulfill
+; receiver_reqs          = requirements that the receiving side of the clause needs to meet
+; either_reqs            = requirements that either side of the clause transaction would need to meet
 ;
 ; */ <-- avoid gettext warnings
 
diff --git a/data/multiplayer/game.ruleset b/data/multiplayer/game.ruleset
index 4beb69869f..0b2883dae8 100644
--- a/data/multiplayer/game.ruleset
+++ b/data/multiplayer/game.ruleset
@@ -557,9 +557,10 @@ priority       = 1
 ; type                   = Type of the clause, one of "Advance", "Gold", "Map", "Seamap",
 ;                          "City", "Ceasefire", "Peace", "Alliance", "Vision", "Embassy",
 ;                          "SharedTiles"
-; giver_reqs             = requirements that the giving side of the clause needs to fulfill
+; giver_reqs             = requirements that the giving side of the clause needs to meet
 ;                          (see effects.ruleset and README.effects for help on requirements)
-; receiver_reqs          = requirements that the receiving side of the clause needs to fulfill
+; receiver_reqs          = requirements that the receiving side of the clause needs to meet
+; either_reqs            = requirements that either side of the clause transaction would need to meet
 ;
 ; */ <-- avoid gettext warnings
 
diff --git a/data/ruledit/comments-3.3.txt b/data/ruledit/comments-3.3.txt
index de9b05e631..4d16968e15 100644
--- a/data/ruledit/comments-3.3.txt
+++ b/data/ruledit/comments-3.3.txt
@@ -1254,9 +1254,10 @@ clauses = "\
 ; type                   = Type of the clause, one of \"Advance\", \"Gold\", \"Map\", \"Seamap\",\n\
 ;                          \"City\", \"Ceasefire\", \"Peace\", \"Alliance\", \"Vision\", \"Embassy\",\n\
 ;                          \"SharedTiles\"\n\
-; giver_reqs             = requirements that the giving side of the clause needs to fulfill\n\
+; giver_reqs             = requirements that the giving side of the clause needs to meet\n\
 ;                          (see effects.ruleset and README.effects for help on requirements)\n\
-; receiver_reqs          = requirements that the receiving side of the clause needs to fulfill\n\
+; receiver_reqs          = requirements that the receiving side of the clause needs to meet\n\
+; either_reqs            = requirements that either side of the clause transaction would need to meet\n\
 ;\n\
 ; */ <-- avoid gettext warnings\n\
 "
diff --git a/data/sandbox/game.ruleset b/data/sandbox/game.ruleset
index c7a9fc8272..2e72d9cbb5 100644
--- a/data/sandbox/game.ruleset
+++ b/data/sandbox/game.ruleset
@@ -754,9 +754,10 @@ flags          = "Depletes"
 ; type                   = Type of the clause, one of "Advance", "Gold", "Map", "Seamap",
 ;                          "City", "Ceasefire", "Peace", "Alliance", "Vision", "Embassy",
 ;                          "SharedTiles"
-; giver_reqs             = requirements that the giving side of the clause needs to fulfill
+; giver_reqs             = requirements that the giving side of the clause needs to meet
 ;                          (see effects.ruleset and README.effects for help on requirements)
-; receiver_reqs          = requirements that the receiving side of the clause needs to fulfill
+; receiver_reqs          = requirements that the receiving side of the clause needs to meet
+; either_reqs            = requirements that either side of the clause transaction would need to meet
 ;
 ; */ <-- avoid gettext warnings
 
diff --git a/data/stub/game.ruleset b/data/stub/game.ruleset
index bb49f6c25b..5e58df33e0 100644
--- a/data/stub/game.ruleset
+++ b/data/stub/game.ruleset
@@ -551,9 +551,10 @@ priority       = 1
 ; type                   = Type of the clause, one of "Advance", "Gold", "Map", "Seamap",
 ;                          "City", "Ceasefire", "Peace", "Alliance", "Vision", "Embassy",
 ;                          "SharedTiles"
-; giver_reqs             = requirements that the giving side of the clause needs to fulfill
+; giver_reqs             = requirements that the giving side of the clause needs to meet
 ;                          (see effects.ruleset and README.effects for help on requirements)
-; receiver_reqs          = requirements that the receiving side of the clause needs to fulfill
+; receiver_reqs          = requirements that the receiving side of the clause needs to meet
+; either_reqs            = requirements that either side of the clause transaction would need to meet
 ;
 ; */ <-- avoid gettext warnings
 
diff --git a/data/webperimental/game.ruleset b/data/webperimental/game.ruleset
index db787def7e..eff3268841 100644
--- a/data/webperimental/game.ruleset
+++ b/data/webperimental/game.ruleset
@@ -595,9 +595,10 @@ priority       = 1
 ; type                   = Type of the clause, one of "Advance", "Gold", "Map", "Seamap",
 ;                          "City", "Ceasefire", "Peace", "Alliance", "Vision", "Embassy",
 ;                          "SharedTiles"
-; giver_reqs             = requirements that the giving side of the clause needs to fulfill
+; giver_reqs             = requirements that the giving side of the clause needs to meet
 ;                          (see effects.ruleset and README.effects for help on requirements)
-; receiver_reqs          = requirements that the receiving side of the clause needs to fulfill
+; receiver_reqs          = requirements that the receiving side of the clause needs to meet
+; either_reqs            = requirements that either side of the clause transaction would need to meet
 ;
 ; */ <-- avoid gettext warnings
 
diff --git a/server/ruleset/ruleload.c b/server/ruleset/ruleload.c
index 01c99760d9..a6674e959b 100644
--- a/server/ruleset/ruleload.c
+++ b/server/ruleset/ruleload.c
@@ -7477,6 +7477,13 @@ static bool load_ruleset_game(struct section_file *file, bool act,
         }
         requirement_vector_copy(&info->receiver_reqs, reqs);
 
+        reqs = lookup_req_list(file, compat, sec_name, "either_reqs", clause_name);
+        if (reqs == NULL) {
+          ok = FALSE;
+          break;
+        }
+        requirement_vector_copy(&info->either_reqs, reqs);
+
         info->enabled = TRUE;
       }
     }
@@ -8969,6 +8976,12 @@ static void send_ruleset_clauses(struct conn_list *dest)
     } requirement_vector_iterate_end;
     packet.receiver_reqs_count = j;
 
+    j = 0;
+    requirement_vector_iterate(&info->either_reqs, preq) {
+      packet.either_reqs[j++] = *preq;
+    } requirement_vector_iterate_end;
+    packet.either_reqs_count = j;
+
     lsend_packet_ruleset_clause(dest, &packet);
   }
 }
diff --git a/tools/ruleutil/rulesave.c b/tools/ruleutil/rulesave.c
index c2e382ac1d..c030168b4b 100644
--- a/tools/ruleutil/rulesave.c
+++ b/tools/ruleutil/rulesave.c
@@ -1803,6 +1803,7 @@ static bool save_game_ruleset(const char *filename, const char *name)
                          "%s.type", path);
       save_reqs_vector(sfile, &(info->giver_reqs), path, "giver_reqs");
       save_reqs_vector(sfile, &(info->receiver_reqs), path, "receiver_reqs");
+      save_reqs_vector(sfile, &(info->either_reqs), path, "either_reqs");
     }
   }
 
-- 
2.43.0

