Heavy vehiclular weapons implementation (with C++ code)

I’ve made and decided to share on here a simple-ish C++ implementation of proper support for heavy vehicular weapons that prevent firing said weapons from bicycles, shopping carts or similar light vehicles, as was the issue with how Tankmod had tank guns implemented.

The way it is currently done is as such:

Any vehicle-only guns get the JSON flag called “VEHICLE_GUN_ONLY”.

If a gun with this flag is mounted on a vehicle which has total mass lower than the mass defined in “min_veh_mass” JSON field of a gun item, then said gun can’t be fired.

Furthermore, if a gun with this flag is installed on a vehicle tile with has no parts with a new “MOUNTABLE_HEAVY” JSON flag (which usually is a heavy-duty frame), then the gun can’t be fired either.

The code additions are below. Anyone feel free to make a pull request with it, but note that code may or may not need minor updates for the most recent experimental version of DDA. Suggestions are welcome.

  • File avatar_action.cpp:
    Function avatar_action::fire_turret_manual: this code defines the logic behind forbidding firing vehicular weapons from an unsuitable vehicle.
if( turret.base()->has_flag( flag_VEHICLE_GUN_ONLY ) ) {
        const optional_vpart_position vp = m.veh_at( you.pos() );
        vehicle *veh = veh_pointer_or_null( vp );
        if( !veh ) {
            debugmsg( "Vehicle not found on fired vehicular weapon tile." );
            return false;
        }
        if( !static_cast<bool>( vp.part_with_feature( "MOUNTABLE_HEAVY", true ) ) ) {
            add_msg( m_bad, _( "This vehicle's frame is not durable enough to handle the recoil of the %s." ),
                     turret.name() );
            return false;
        }
        if( veh->total_mass() < turret.base()->get_min_veh_mass() ) {
            add_msg( m_bad, _( "This vehicle is too light to safely fire the %s." ), turret.name() );
            return false;
        }
    }

Function avatar_action::fire_wielded_weapon: show failure text if player tries to fire a vehicle-only weapon while wielding it.

if( weapon->has_flag( flag_VEHICLE_GUN_ONLY ) ) {
        add_msg( m_info,
                 _( "Your %s must be mounted on a vehicle with a sturdy frame and enough mass to fire." ),
                 weapon->tname() );
        return;
    }
  • File flag.cpp: define new flag.
const flag_id flag_VEHICLE_GUN_ONLY( "VEHICLE_GUN_ONLY" );
  • File flag.h: define new flag.
extern const flag_id flag_VEHICLE_GUN_ONLY;
  • File item.cpp:
    Function item::gun_info: code to display minimum vehicle mass in gun info; insert after the code to display barrel length.
    if( mod->get_min_veh_mass().value() > 0 ) {
        if( parts->test( iteminfo_parts::GUN_MIN_VEH_MASS ) ) {
            info.emplace_back( weight_to_info( "GUN", _( "Minimum Vehicle Mass: " ), get_min_veh_mass() ) );
        }
    }
    info.back().bNewLine = true;

New function to get minimum vehicle mass from item data:

units::mass item::get_min_veh_mass() const
{
    return type->min_veh_mass;
}
  • File item.h: defining the new function above.
        units::mass get_min_veh_mass() const;
  • File item_factory.cpp, function Item_factory::load_basic_info: load minimum vehicle mass field from JSON.
    assign( jo, "min_veh_mass", def.min_veh_mass, false, 0_gram );
  • File iteminfo_query.h, enum class iteminfo_parts : add GUN_MIN_VEH_MASS after GUN_BARRELLENGTH to get minimum vehicle mass data to display in item description.

  • File turret.cpp: replace vehicle::find_all_ready_turrets function with this one:

std::vector<vehicle_part *> vehicle::find_all_ready_turrets( bool manual, bool automatic )
{
    std::vector<vehicle_part *> res;
    for( vehicle_part *t : turrets() ) {
        if( ( t->enabled && automatic ) || ( !t->enabled && manual ) ) {
            if( turret_query( *t ).query() == turret_data::status::ready ) {
                turret_data turret_here = turret_query( global_part_pos3( *t ) );
                if( turret_here.base()->has_flag( flag_VEHICLE_GUN_ONLY ) ) {
                    if( !has_part( global_part_pos3( *t ), "MOUNTABLE_HEAVY", false ) ||
                        total_mass() < turret_here.base()->get_min_veh_mass() ) {
                        continue;
                    }
                }
                res.push_back( t );
            }
        }
    }
    return res;
}

Also add #include "flag.h" to the file.

Sample gun JSON data to test this mechanic with:

{
    "id": "gau19",
    "copy-from": "rifle_auto",
    "looks_like": "m2browning",
    "type": "GUN",
    "name": { "str_sp": "GAU-19/B" },
    "description": "An electrically driven, three-barrel rotary heavy machine gun firing a .50 BMG cartridge.  If you could find enough ammo for it, it would become a devastating weapon.  It must be mounted on a heavy vehicle frame before use.",
    "weight": "48 kg",
    "volume": "12 L",
    "longest_side": "1369 mm",
    "barrel_length": "914 mm",
    "price": 1800000,
    "price_postapoc": 10000,
    "to_hit": -4,
    "material": [ "qt_steel" ],
    "ammo": [ "50" ],
    "range": 110,
    "dispersion": 250,
    "durability": 9,
    "blackpowder_tolerance": 96,
    "energy_drain": "1 kJ",
    "reload": 400,
    "modes": [ [ "DEFAULT", "low auto", 6 ], [ "AUTO", "high auto", 50 ] ],
    "valid_mod_locations": [ [ "sling", 1 ] ],
    "min_veh_mass": "250 kg",
    "flags": [ "NEVER_JAMS", "VEHICLE_GUN_ONLY", "USE_UPS" ],
    "pocket_data": [ { "pocket_type": "MAGAZINE_WELL", "item_restriction": [ "belt50" ] } ],
    "melee_damage": { "bash": 18 }
  }

Could you make it so it only prevents firing while driving? If you dismount the vehicle and grab it then it should be possible to fire basically anything. Most towed artillery guns are like that, their chassis is just 1 frame +1 foldable frame in CDDA terms, with the gun often outweighting the chassis. Also consider these:


This implementation is meant for weapons heavier than those, lighter weapons up to and including M2HB can already be mounted on anything. Trying to do that with an autocannon or a tank cannon would knock over the user.

Now that I think about it, you could actually provide the mechanic that would do everything you want without having to manually set those values. Considering that gun ammo and rate of fire are known, you can calculate momentum each shot/burst provides and apply it to the vehicle. You could still mount tank gun on a single frame but firing it would accelerate it to 50mph and smear you over asphalt. Perhaps PC could absorb some momentum depending on the strength value to allow mutants more leeway.
It just doesn’t make sense to not allow doing stupid things which are possible in reality just because they’re stupid.

Well, first of all, at a glance implementing fired turrets affecting the vehicle’s movement seems extremely complicated to code. Given that it’s not even an expected use case of such weapons, I don’t feel like coding something like that if it would be used extremely rarely.

Second, this needs a clear way to communicate to the player that firing a tank gun mounted on a shopping cart is obviously a bad idea. I don’t know how this could be communcated clear enough, personally.

Third, cyclic rate of fire for guns isn’t really defined anywhere in a way that is actually consistent; last time I calculated it, current burst sizes for at least some full-auto guns, combined with amount of moves spent on firing the gun without aiming, allow you to mag-dump faster than the gun can actually fire. This can be fixed but it would be tedious to edit everything.

For as long as those are an issue, I think it’d be much easier to just have such a weight limit (which would assume that the vehicle wouldn’t move around noticeably due to recoil) and durable frame requirement (which would assume that the vehicle itself isn’t damaged by recoil) in place.

However, such a mechanic would also be useful in other places. For example, aircraft. And you dont need to know the rate of fire beforehand, you can just apply momentum of a round after every single shot.