From 17b18a77c9aadf4c99be0264e882f72f1325826f Mon Sep 17 00:00:00 2001
From: Marko Lindqvist <cazfi74@gmail.com>
Date: Fri, 22 May 2026 06:39:26 +0300
Subject: [PATCH 44/44] Turn Tech_Cost_Factor effect to a percentage effect
 Tech_Cost_Pct

See RM #2005

Signed-off-by: Marko Lindqvist <cazfi74@gmail.com>
---
 ai/default/daieffects.c             | 4 ++--
 common/research.c                   | 4 ++--
 data/alien/effects.ruleset          | 4 ++--
 data/civ1/effects.ruleset           | 8 ++++----
 data/civ2/effects.ruleset           | 8 ++++----
 data/civ2civ3/effects.ruleset       | 4 ++--
 data/classic/effects.ruleset        | 4 ++--
 data/goldkeep/effects.ruleset       | 4 ++--
 data/granularity/effects.ruleset    | 4 ++--
 data/multiplayer/effects.ruleset    | 4 ++--
 data/sandbox/effects.ruleset        | 4 ++--
 data/stub/effects.ruleset           | 4 ++--
 doc/README.effects                  | 4 ++--
 gen_headers/enums/effects_enums.def | 2 +-
 server/ruleset/rscompat.c           | 6 ++++++
 server/ruleset/rssanity.c           | 2 +-
 16 files changed, 38 insertions(+), 32 deletions(-)

diff --git a/ai/default/daieffects.c b/ai/default/daieffects.c
index 1db44ecc5f..2a1238c1d1 100644
--- a/ai/default/daieffects.c
+++ b/ai/default/daieffects.c
@@ -642,8 +642,8 @@ adv_want dai_effect_value(struct player *pplayer,
     /* Assume that this multiplies accumulation of 5 history points / turn */
     v += amount * 5 * 5 / 100;
     break;
-  case EFT_TECH_COST_FACTOR:
-    v -= amount * 50;
+  case EFT_TECH_COST_PCT:
+    v -= amount / 2;
     break;
   case EFT_TECH_LEAKAGE:
     {
diff --git a/common/research.c b/common/research.c
index 9cbb4f872c..df9f526000 100644
--- a/common/research.c
+++ b/common/research.c
@@ -928,7 +928,7 @@ int research_total_bulbs_required(const struct research *presearch,
   research_players_iterate(presearch, pplayer) {
     members++;
     total_cost += (base_cost
-                   * get_player_bonus(pplayer, EFT_TECH_COST_FACTOR));
+                   * get_player_bonus(pplayer, EFT_TECH_COST_PCT) / 100);
     if (!leakage && get_player_bonus(pplayer, EFT_TECH_LEAKAGE)) {
       leakage = TRUE;
     }
@@ -1074,7 +1074,7 @@ int player_tech_upkeep(const struct player *pplayer)
   total_research_factor = 0.0;
   members = 0;
   research_players_iterate(presearch, contributor) {
-    total_research_factor += (get_player_bonus(contributor, EFT_TECH_COST_FACTOR)
+    total_research_factor += (get_player_bonus(contributor, EFT_TECH_COST_PCT) / 100.0
                               + (is_ai(contributor)
                                  ? contributor->ai_common.science_cost / 100.0
                                  : 1));
diff --git a/data/alien/effects.ruleset b/data/alien/effects.ruleset
index d9f02bad73..fbc618856b 100644
--- a/data/alien/effects.ruleset
+++ b/data/alien/effects.ruleset
@@ -731,8 +731,8 @@ type    = "Turn_Fragments"
 value   = 1
 
 [effect_tech_cost_base]
-type    = "Tech_Cost_Factor"
-value   = 1
+type    = "Tech_Cost_Pct"
+value   = 100
 
 [effect_tech_leakage]
 type    = "Tech_Leakage"
diff --git a/data/civ1/effects.ruleset b/data/civ1/effects.ruleset
index 575cc18808..a02a54f617 100644
--- a/data/civ1/effects.ruleset
+++ b/data/civ1/effects.ruleset
@@ -1260,12 +1260,12 @@ reqs    =
     }
 
 [effect_tech_cost_base]
-type    = "Tech_Cost_Factor"
-value   = 1
+type    = "Tech_Cost_Pct"
+value   = 100
 
 [effect_tech_cost_double]
-type    = "Tech_Cost_Factor"
-value   = 1
+type    = "Tech_Cost_Pct"
+value   = 100
 reqs    =
     { "type", "name", "range"
       "MinYear", "1", "World"
diff --git a/data/civ2/effects.ruleset b/data/civ2/effects.ruleset
index 556268347a..7b45640cf5 100644
--- a/data/civ2/effects.ruleset
+++ b/data/civ2/effects.ruleset
@@ -2264,12 +2264,12 @@ reqs    =
     }
 
 [effect_tech_cost_base]
-type    = "Tech_Cost_Factor"
-value   = 1
+type    = "Tech_Cost_Pct"
+value   = 100
 
 [effect_tech_cost_double]
-type    = "Tech_Cost_Factor"
-value   = 1
+type    = "Tech_Cost_Pct"
+value   = 100
 reqs    =
     { "type", "name", "range"
       "MinYear", "1", "World"
diff --git a/data/civ2civ3/effects.ruleset b/data/civ2civ3/effects.ruleset
index 0eb5666488..a95efeed1d 100644
--- a/data/civ2civ3/effects.ruleset
+++ b/data/civ2civ3/effects.ruleset
@@ -3801,8 +3801,8 @@ type    = "Tech_Upkeep_Free"
 value   = 3
 
 [effect_tech_cost_base]
-type    = "Tech_Cost_Factor"
-value   = 3
+type    = "Tech_Cost_Pct"
+value   = 300
 
 [effect_tech_leakage]
 type    = "Tech_Leakage"
diff --git a/data/classic/effects.ruleset b/data/classic/effects.ruleset
index 1acc611b3e..36bcd27370 100644
--- a/data/classic/effects.ruleset
+++ b/data/classic/effects.ruleset
@@ -2305,8 +2305,8 @@ reqs    =
     }
 
 [effect_tech_cost_base]
-type    = "Tech_Cost_Factor"
-value   = 1
+type    = "Tech_Cost_Pct"
+value   = 100
 
 [effect_tech_leakage]
 type    = "Tech_Leakage"
diff --git a/data/goldkeep/effects.ruleset b/data/goldkeep/effects.ruleset
index d4d06677ea..d991078750 100644
--- a/data/goldkeep/effects.ruleset
+++ b/data/goldkeep/effects.ruleset
@@ -2517,8 +2517,8 @@ reqs    =
     }
 
 [effect_tech_cost_base]
-type    = "Tech_Cost_Factor"
-value   = 1
+type    = "Tech_Cost_Pct"
+value   = 100
 
 [effect_tech_leakage]
 type    = "Tech_Leakage"
diff --git a/data/granularity/effects.ruleset b/data/granularity/effects.ruleset
index e5ff690f4e..a6d950c92d 100644
--- a/data/granularity/effects.ruleset
+++ b/data/granularity/effects.ruleset
@@ -111,8 +111,8 @@ type    = "Max_Rates"
 value   = 50
 
 [effect_tech_cost_base]
-type    = "Tech_Cost_Factor"
-value   = 1
+type    = "Tech_Cost_Pct"
+value   = 100
 
 [effect_tech_leakage]
 type    = "Tech_Leakage"
diff --git a/data/multiplayer/effects.ruleset b/data/multiplayer/effects.ruleset
index 0b082eeb3b..b7501ad5bb 100644
--- a/data/multiplayer/effects.ruleset
+++ b/data/multiplayer/effects.ruleset
@@ -2334,8 +2334,8 @@ reqs    =
     }
 
 [effect_tech_cost_base]
-type    = "Tech_Cost_Factor"
-value   = 1
+type    = "Tech_Cost_Pct"
+value   = 100
 
 [effect_tech_leakage]
 type    = "Tech_Leakage"
diff --git a/data/sandbox/effects.ruleset b/data/sandbox/effects.ruleset
index 48669008f4..e555be0c8c 100644
--- a/data/sandbox/effects.ruleset
+++ b/data/sandbox/effects.ruleset
@@ -4022,8 +4022,8 @@ type    = "Tech_Upkeep_Free"
 value   = 3
 
 [effect_tech_cost_base]
-type    = "Tech_Cost_Factor"
-value   = 3
+type    = "Tech_Cost_Pct"
+value   = 300
 
 [effect_tech_leakage]
 type    = "Tech_Leakage"
diff --git a/data/stub/effects.ruleset b/data/stub/effects.ruleset
index 54dfeaa47a..1f5936f67f 100644
--- a/data/stub/effects.ruleset
+++ b/data/stub/effects.ruleset
@@ -65,8 +65,8 @@ type    = "Size_Adj"
 value   = 1
 
 [effect_tech_cost_base]
-type    = "Tech_Cost_Factor"
-value   = 1
+type    = "Tech_Cost_Pct"
+value   = 100
 
 ; Cities can always work tiles
 [effect_tile_workable]
diff --git a/doc/README.effects b/doc/README.effects
index f61de5572b..aa32e16ad8 100644
--- a/doc/README.effects
+++ b/doc/README.effects
@@ -707,8 +707,8 @@ Surplus_Waste_Pct_By_Rel_Distance
     to world size. This is a percentage calculated from what otherwise would
     be the surplus. Percentage is (distance * amount / 100 / max_distance)
 
-Tech_Cost_Factor
-    Factor for research costs.
+Tech_Cost_Pct
+    Percentage factor for research costs.
 
 Tech_Leakage
     If value is positive, tech leakage towards the player is enabled.
diff --git a/gen_headers/enums/effects_enums.def b/gen_headers/enums/effects_enums.def
index 24792824c0..927bda1956 100644
--- a/gen_headers/enums/effects_enums.def
+++ b/gen_headers/enums/effects_enums.def
@@ -100,7 +100,7 @@ values
   UPGRADE_PRICE_PCT                 "Upgrade_Price_Pct"
   /* City should use walls gfx */
   VISIBLE_WALLS                     "Visible_Walls"
-  TECH_COST_FACTOR                  "Tech_Cost_Factor"
+  TECH_COST_PCT                     "Tech_Cost_Pct"
   /* [x%] gold upkeep instead of [1] shield upkeep for units */
   SHIELD2GOLD_PCT                   "Shield2Gold_Pct"
   TILE_WORKABLE                     "Tile_Workable"
diff --git a/server/ruleset/rscompat.c b/server/ruleset/rscompat.c
index d9e0356f90..36b478e3ec 100644
--- a/server/ruleset/rscompat.c
+++ b/server/ruleset/rscompat.c
@@ -480,6 +480,9 @@ static bool effect_list_compat_cb(struct effect *peffect, void *data)
                                                  "HasHomeCity"));
         }
       }
+    } else if (peffect->type == EFT_TECH_COST_PCT) {
+      /* From old Tech_Cost_Factor to new Tech_Cost_Pct */
+      peffect->value *= 100;
     }
   }
 
@@ -571,6 +574,9 @@ const char *rscompat_effect_name_3_4(const char *old_name)
   if (!fc_strcasecmp("Upkeep_Factor", old_name)) {
     return "Upkeep_Pct";
   }
+  if (!fc_strcasecmp("Tech_Cost_Factor", old_name)) {
+    return "Tech_Cost_Pct";
+  }
 
   return old_name;
 }
diff --git a/server/ruleset/rssanity.c b/server/ruleset/rssanity.c
index c31a4800be..eb5262ae31 100644
--- a/server/ruleset/rssanity.c
+++ b/server/ruleset/rssanity.c
@@ -45,7 +45,7 @@ enum effect_type req_base_effects[] =
     EFT_CITY_VISION_RADIUS_SQ,
     EFT_MAX_RATES,
     EFT_UPKEEP_PCT,
-    EFT_TECH_COST_FACTOR,
+    EFT_TECH_COST_PCT,
     EFT_COUNT
   };
 
-- 
2.53.0

