From 86457be90ef14f4f8750305372d0478af6c432e7 Mon Sep 17 00:00:00 2001
From: Marko Lindqvist <cazfi74@gmail.com>
Date: Thu, 3 Jul 2025 19:36:02 +0300
Subject: [PATCH 12/65] Add internal city action type "Finish Building"

See RM #1470

Signed-off-by: Marko Lindqvist <cazfi74@gmail.com>
---
 common/actions.c                    | 13 ++++++++++++-
 data/alien/actions.ruleset          |  6 ++++++
 data/civ1/actions.ruleset           |  6 ++++++
 data/civ2/actions.ruleset           |  6 ++++++
 data/civ2civ3/actions.ruleset       |  6 ++++++
 data/classic/actions.ruleset        |  6 ++++++
 data/goldkeep/actions.ruleset       |  6 ++++++
 data/granularity/actions.ruleset    |  6 ++++++
 data/multiplayer/actions.ruleset    |  6 ++++++
 data/sandbox/actions.ruleset        |  6 ++++++
 data/webperimental/actions.ruleset  |  6 ++++++
 doc/README.actions                  |  2 ++
 gen_headers/enums/actions_enums.def |  1 +
 server/cityturn.c                   |  4 +++-
 server/ruleset/rscompat.c           |  4 ++++
 15 files changed, 82 insertions(+), 2 deletions(-)

diff --git a/common/actions.c b/common/actions.c
index 5259a1e083..15791ecfea 100644
--- a/common/actions.c
+++ b/common/actions.c
@@ -829,6 +829,9 @@ static void hard_code_actions(void)
   actions[ACTION_FINISH_UNIT] =
       city_action_new(ACTION_FINISH_UNIT, ACTRES_ENABLER_CHECK);
 
+  actions[ACTION_FINISH_BUILDING] =
+      city_action_new(ACTION_FINISH_BUILDING, ACTRES_ENABLER_CHECK);
+
   /* The structure even for these need to be created, for
    * the action_id_rule_name() to work on iterations. */
 
@@ -6198,7 +6201,9 @@ const char *action_ui_name_default(int act)
   case ACTION_CIVIL_WAR:
     return N_("%sCivil War%s");
   case ACTION_FINISH_UNIT:
-    return N_("%sFinish Unit%s");
+    return N_("Finish %sUnit%s");
+  case ACTION_FINISH_BUILDING:
+    return N_("Finish %sBuilding%s");
   case ACTION_COUNT:
     fc_assert(act != ACTION_COUNT);
     break;
@@ -6343,6 +6348,7 @@ const char *action_min_range_ruleset_var_name(int act)
   case ACTION_ESCAPE:
   case ACTION_CIVIL_WAR:
   case ACTION_FINISH_UNIT:
+  case ACTION_FINISH_BUILDING:
     /* Min range is not ruleset changeable */
     return NULL;
   case ACTION_NUKE:
@@ -6513,6 +6519,7 @@ const char *action_max_range_ruleset_var_name(int act)
   case ACTION_ESCAPE:
   case ACTION_CIVIL_WAR:
   case ACTION_FINISH_UNIT:
+  case ACTION_FINISH_BUILDING:
     /* Max range is not ruleset changeable */
     return NULL;
   case ACTION_HELP_WONDER:
@@ -6717,6 +6724,7 @@ const char *action_target_kind_ruleset_var_name(int act)
   case ACTION_ESCAPE:
   case ACTION_CIVIL_WAR:
   case ACTION_FINISH_UNIT:
+  case ACTION_FINISH_BUILDING:
     /* Target kind is not ruleset changeable */
     return NULL;
   case ACTION_NUKE:
@@ -6889,6 +6897,7 @@ const char *action_actor_consuming_always_ruleset_var_name(action_id act)
   case ACTION_ESCAPE:
   case ACTION_CIVIL_WAR:
   case ACTION_FINISH_UNIT:
+  case ACTION_FINISH_BUILDING:
     /* Actor consuming always is not ruleset changeable */
     return NULL;
   case ACTION_FOUND_CITY:
@@ -7100,6 +7109,7 @@ const char *action_blocked_by_ruleset_var_name(const struct action *act)
   case ACTION_GAIN_VETERANCY:
   case ACTION_CIVIL_WAR:
   case ACTION_FINISH_UNIT:
+  case ACTION_FINISH_BUILDING:
   case ACTION_ESCAPE:
   case ACTION_USER_ACTION1:
   case ACTION_USER_ACTION2:
@@ -7280,6 +7290,7 @@ action_post_success_forced_ruleset_var_name(const struct action *act)
   case ACTION_ESCAPE:
   case ACTION_CIVIL_WAR:
   case ACTION_FINISH_UNIT:
+  case ACTION_FINISH_BUILDING:
   case ACTION_USER_ACTION1:
   case ACTION_USER_ACTION2:
   case ACTION_USER_ACTION3:
diff --git a/data/alien/actions.ruleset b/data/alien/actions.ruleset
index 467290f846..b6b03571e4 100644
--- a/data/alien/actions.ruleset
+++ b/data/alien/actions.ruleset
@@ -1001,3 +1001,9 @@ action        = "Finish Unit"
 actor_reqs    =
     { "type", "name", "range", "present"
     }
+
+[enabler_finish_building]
+action        = "Finish Building"
+actor_reqs    =
+    { "type", "name", "range", "present"
+    }
diff --git a/data/civ1/actions.ruleset b/data/civ1/actions.ruleset
index 6e65fa69b8..54c5384586 100644
--- a/data/civ1/actions.ruleset
+++ b/data/civ1/actions.ruleset
@@ -804,3 +804,9 @@ action        = "Finish Unit"
 actor_reqs    =
     { "type", "name", "range", "present"
     }
+
+[enabler_finish_building]
+action        = "Finish Building"
+actor_reqs    =
+    { "type", "name", "range", "present"
+    }
diff --git a/data/civ2/actions.ruleset b/data/civ2/actions.ruleset
index b33470acce..3de1c112a7 100644
--- a/data/civ2/actions.ruleset
+++ b/data/civ2/actions.ruleset
@@ -1120,3 +1120,9 @@ action        = "Finish Unit"
 actor_reqs    =
     { "type", "name", "range", "present"
     }
+
+[enabler_finish_building]
+action        = "Finish Building"
+actor_reqs    =
+    { "type", "name", "range", "present"
+    }
diff --git a/data/civ2civ3/actions.ruleset b/data/civ2civ3/actions.ruleset
index adb9013cfb..9e27fbac5e 100644
--- a/data/civ2civ3/actions.ruleset
+++ b/data/civ2civ3/actions.ruleset
@@ -1445,3 +1445,9 @@ action        = "Finish Unit"
 actor_reqs    =
     { "type", "name", "range", "present"
     }
+
+[enabler_finish_building]
+action        = "Finish Building"
+actor_reqs    =
+    { "type", "name", "range", "present"
+    }
diff --git a/data/classic/actions.ruleset b/data/classic/actions.ruleset
index 5139eada76..c2eb3426ca 100644
--- a/data/classic/actions.ruleset
+++ b/data/classic/actions.ruleset
@@ -1324,3 +1324,9 @@ action        = "Finish Unit"
 actor_reqs    =
     { "type", "name", "range", "present"
     }
+
+[enabler_finish_building]
+action        = "Finish Building"
+actor_reqs    =
+    { "type", "name", "range", "present"
+    }
diff --git a/data/goldkeep/actions.ruleset b/data/goldkeep/actions.ruleset
index 0dd8876269..23bec5b5e4 100644
--- a/data/goldkeep/actions.ruleset
+++ b/data/goldkeep/actions.ruleset
@@ -1393,3 +1393,9 @@ action        = "Finish Unit"
 actor_reqs    =
     { "type", "name", "range", "present"
     }
+
+[enabler_finish_building]
+action        = "Finish Building"
+actor_reqs    =
+    { "type", "name", "range", "present"
+    }
diff --git a/data/granularity/actions.ruleset b/data/granularity/actions.ruleset
index 5905e51f94..75634c8403 100644
--- a/data/granularity/actions.ruleset
+++ b/data/granularity/actions.ruleset
@@ -561,3 +561,9 @@ action        = "Finish Unit"
 actor_reqs    =
     { "type", "name", "range", "present"
     }
+
+[enabler_finish_building]
+action        = "Finish Building"
+actor_reqs    =
+    { "type", "name", "range", "present"
+    }
diff --git a/data/multiplayer/actions.ruleset b/data/multiplayer/actions.ruleset
index dd57ce6d7a..361bdb83c6 100644
--- a/data/multiplayer/actions.ruleset
+++ b/data/multiplayer/actions.ruleset
@@ -1306,3 +1306,9 @@ action        = "Finish Unit"
 actor_reqs    =
     { "type", "name", "range", "present"
     }
+
+[enabler_finish_building]
+action        = "Finish Building"
+actor_reqs    =
+    { "type", "name", "range", "present"
+    }
diff --git a/data/sandbox/actions.ruleset b/data/sandbox/actions.ruleset
index e9ccdc858b..8080484a23 100644
--- a/data/sandbox/actions.ruleset
+++ b/data/sandbox/actions.ruleset
@@ -2853,3 +2853,9 @@ action        = "Finish Unit"
 actor_reqs    =
     { "type", "name", "range", "present"
     }
+
+[enabler_finish_building]
+action        = "Finish Building"
+actor_reqs    =
+    { "type", "name", "range", "present"
+    }
diff --git a/data/webperimental/actions.ruleset b/data/webperimental/actions.ruleset
index 63f2bf67e7..230132d0bf 100644
--- a/data/webperimental/actions.ruleset
+++ b/data/webperimental/actions.ruleset
@@ -1572,3 +1572,9 @@ action        = "Finish Unit"
 actor_reqs    =
     { "type", "name", "range", "present"
     }
+
+[enabler_finish_building]
+action        = "Finish Building"
+actor_reqs    =
+    { "type", "name", "range", "present"
+    }
diff --git a/doc/README.actions b/doc/README.actions
index bc4acd5159..e6e38c7fd5 100644
--- a/doc/README.actions
+++ b/doc/README.actions
@@ -1576,6 +1576,8 @@ Internal actions
 
 "Finish Unit" - city can finish building a unit
 
+"Finish Building" - city can finish building a building
+
 Ruleset defined actions
 =======================
 User actions are "blank". The ruleset does everything they do. The following
diff --git a/gen_headers/enums/actions_enums.def b/gen_headers/enums/actions_enums.def
index c9f1766a6d..5ebf4d733b 100644
--- a/gen_headers/enums/actions_enums.def
+++ b/gen_headers/enums/actions_enums.def
@@ -146,6 +146,7 @@ values
   ACTION_ESCAPE                           "Escape"
   ACTION_CIVIL_WAR                        "Civil War"
   ACTION_FINISH_UNIT                      "Finish Unit"
+  ACTION_FINISH_BUILDING                  "Finish Building"
 
   # User actions
   ACTION_USER_ACTION1                     "User Action 1"
diff --git a/server/cityturn.c b/server/cityturn.c
index 88fe60b1c4..abe91588da 100644
--- a/server/cityturn.c
+++ b/server/cityturn.c
@@ -2663,6 +2663,7 @@ static bool city_build_building(struct player *pplayer, struct city *pcity)
   int mod;
   const struct impr_type *pimprove;
   int saved_id = pcity->id;
+  const struct civ_map *nmap = &(wld.map);
 
   if (is_convert_improvement(pcity->production.value.building)) {
     /* Coinage-like improvements that convert production */
@@ -2697,7 +2698,8 @@ static bool city_build_building(struct player *pplayer, struct city *pcity)
                               "unavailable");
     return TRUE;
   }
-  if (pcity->shield_stock >= impr_build_shield_cost(pcity, pimprove)) {
+  if (pcity->shield_stock >= impr_build_shield_cost(pcity, pimprove)
+      && is_action_enabled_city(nmap, ACTION_FINISH_BUILDING, pcity)) {
     int cost;
 
     if (is_small_wonder(pimprove)) {
diff --git a/server/ruleset/rscompat.c b/server/ruleset/rscompat.c
index 84e3347bfa..d9e0356f90 100644
--- a/server/ruleset/rscompat.c
+++ b/server/ruleset/rscompat.c
@@ -504,6 +504,10 @@ void rscompat_postprocess(struct rscompat_info *info)
   enabler->action = ACTION_FINISH_UNIT;
   action_enabler_add(enabler);
 
+  enabler = action_enabler_new();
+  enabler->action = ACTION_FINISH_BUILDING;
+  action_enabler_add(enabler);
+
   /* Upgrade existing effects. Done before new effects are added to prevent
    * the new effects from being upgraded by accident. */
   iterate_effect_cache(effect_list_compat_cb, info);
-- 
2.47.2

