From e5e541d21120a4d0eff8d42d9ccb35e27fd1574c Mon Sep 17 00:00:00 2001
From: Marko Lindqvist <cazfi74@gmail.com>
Date: Wed, 22 Jul 2026 04:23:14 +0300
Subject: [PATCH 25/25] Fix over-the-max veteran level at
 process_attacker_want()

Value of EFT_VETERAN_BUILD was being assigned as
veterancy level directly, without checking unit's
max veteran level.

See RM #2081

Signed-off-by: Marko Lindqvist <cazfi74@gmail.com>
---
 ai/default/daimilitary.c | 10 +++++++---
 1 file changed, 7 insertions(+), 3 deletions(-)

diff --git a/ai/default/daimilitary.c b/ai/default/daimilitary.c
index 3edf6fd2cc..ffff324afc 100644
--- a/ai/default/daimilitary.c
+++ b/ai/default/daimilitary.c
@@ -1311,6 +1311,8 @@ static void process_attacker_want(struct ai_type *ait,
                                    },
                                    NULL,
                                    EFT_VETERAN_BUILD);
+      /* Levels start from zero, so max level is 'number of levels - 1' */
+      int max_level = utype_veteran_levels(punittype) - 1;
       /* Cost (shield equivalent) of gaining these techs. */
       /* FIXME? Katvrr advises that this should be weighted more heavily in big
        * danger. */
@@ -1318,11 +1320,13 @@ static void process_attacker_want(struct ai_type *ait,
       int bcost_balanced = build_cost_balanced(punittype);
       /* See description of kill_desire() for info about this variables. */
       int bcost = utype_build_shield_cost(pcity, NULL, punittype);
-      int attack = adv_unittype_att_rating(punittype, veteran_level,
-                                           SINGLE_MOVE,
-                                           punittype->hp);
+      int attack;
       int tech_dist = 0;
 
+      veteran_level = CLIP(0, veteran_level, max_level);
+      attack = adv_unittype_att_rating(punittype, veteran_level,
+                                       SINGLE_MOVE, punittype->hp);
+
       unit_tech_reqs_iterate(punittype, padv) {
         Tech_type_id tech_req = advance_number(padv);
 
-- 
2.53.0

