Allow me to elucidate this matter for you. Evidently, we have two parties involved here. On one side, we have the victim of the attack - in this instance, the ship.
Victims are required to implement the "Prey" Interface, which stipulates that they have a "HitbyMask()" method. This method returns an integer value indicating the types of weapons that can be used against the victim. The returned integer is a binary mask value from the "WeaponTypes" class:
public static final int BULLET_LIGHT = 1;
public static final int BULLET_HEAVY = 2;
public static final int SHELL = 4;
public static final int ROCKET = 8;
public static final int BOMB = 16;
public static final int NONE = 0;
public static final int ALL = -1;
public static final int ALL_EXCEPT_LIGHT = -2;
Ships typically return -2 (ALL_EXCEPT_LIGHT) in "HitbyMask()" (I am not aware of any exceptions to this rule). Tanks, Cars, Artillery, Searchlights, Beacons or any "generic" stationary object with a "Panzer" value set lower than 0.015 (in technics.ini) return "-1" (ALL), otherwise they return -2 (ALL_EXCEPT_LIGHT) as well.
This makes sense when you consider it. Armoured entities require "heavy" bullets, also known as Cannons, while others can be attacked with any gun.
So, what makes ships so special?
This is where the other party comes into play - the attacker. Attacking AI planes use a simplified flight model which inherits the "Pilot" class. This class dictates what an AI pilot does (and what it does not do). Herein, when the waypoint type is "GATTACK" (ground attack) and the target where the GATTACK waypoint is set to is still alive, we have a code block starting with...
if ((this.target_ground instanceof Prey) && (((Prey)this.target_ground).HitbyMask() & WeaponTypes.BULLET_LIGHT) == 0)
This can be interpreted as: If the target is of "Prey" type (i.e., it defines what you can hit it with) but you
cannot hit it with "light" bullets (aka guns), then...
What follows next is a block of code which iterates through all remaining weapons on the attacking aircraft, calculating the heaviest "bullet" remaining. "Bullet" in this context can also be a rocket or bomb. The simple logic here is: If the heaviest "bullet" weight is less than 0.08kg, then don't perform the ground attack at all. The reasoning behind this is probably "don't strafe ground targets with peashooters" - sort of.
The next logic step is crucial: If the target is a ship, then raise the minimum "bullet" weight for performing an attack to 0.55kg. That's quite a lot. For comparison, the weight of an MK 108 bullet is 0.33kg, the one of an MK 101 / 103 is 0.355kg. Those guns, therefore, would not qualify for attacking a ship. So, which guns do? Well, for instance, German "BK" guns such as the BK 37 (Ju-87, Bf 110, Hs 129), the BK 5 (Me-410, Me-262) or the allied "M4" (P-39) do.
That's the crux of the matter. Further on, the code will check the type of weapon available for attacking the ship, naturally preferring Torpedos over Bombs over Rockets over Guns, and set the "Maneuver" task accordingly. If no Torpedo, Bomb or Rocket is available, but the gun is heavy enough to qualify for attacking the ship, AI will strafe it.
Mike