Found the problem: it was a subtle type-casting error introduced when I was doing some AI updates.
The code before:
firing_range = MIN((swip->max_speed * swip->lifetime * (Game_skill_level + 1 + aip->ai_class/2)/NUM_SKILL_LEVELS), swip->weapon_range);
The (broken) code after:
float secondary_range_mult = (aip->ai_secondary_range_mult == FLT_MIN)
? (Game_skill_level + 1 + (3 * aip->ai_class/(Num_ai_classes - 1)))/NUM_SKILL_LEVELS
: aip->ai_secondary_range_mult;
firing_range = MIN((swip->max_speed * swip->lifetime * secondary_range_mult), swip->weapon_range);
The fixed code:
float secondary_range_mult = (aip->ai_secondary_range_mult == FLT_MIN)
? (float)(Game_skill_level + 1 + (3 * aip->ai_class/(Num_ai_classes - 1)))/NUM_SKILL_LEVELS
: aip->ai_secondary_range_mult;
firing_range = MIN((swip->max_speed * swip->lifetime * secondary_range_mult), swip->weapon_range);
Basically, the problem is that the expression involving game_skill_level (and other integer variables) was previously being implicitly cast to float before division because it was multiplied with another float (swip->lifetime). When I moved that expression, that implicit cast from int to float was lost.
This is fixed in trunk rev. 6077, along with a couple of other AI bugs.