diff --git a/data/json/items/vehicle/controls.json b/data/json/items/vehicle/controls.json index 3dda828dde67f..63ec8a5773d16 100644 --- a/data/json/items/vehicle/controls.json +++ b/data/json/items/vehicle/controls.json @@ -68,5 +68,17 @@ "bashing": 1, "price": 20000, "copy-from": "vehicle_controls" + }, + { + "type": "GENERIC", + "id": "turret_controls", + "name": "turret control unit", + "description": "A set of motor, camera, and various electronic modules banded together to allow for tracking targets, friend-or-foe identification, and firing the connected turret in full automatic mode.", + "weight": "4000 g", + "color": "white", + "symbol": "&", + "volume": "9 L", + "price": 500000, + "copy-from": "vehicle_controls" } ] diff --git a/data/json/recipes/recipe_vehicle.json b/data/json/recipes/recipe_vehicle.json index 6a3f55cf95874..a90b5e5e95bb1 100644 --- a/data/json/recipes/recipe_vehicle.json +++ b/data/json/recipes/recipe_vehicle.json @@ -392,5 +392,27 @@ "using": [ [ "welding_standard", 1 ] ], "qualities": [ { "id": "DRILL", "level": 3 } ], "components": [ [ [ "scrap", 2 ] ] ] + }, + { + "type": "recipe", + "result": "turret_controls", + "category": "CC_OTHER", + "subcategory": "CSC_OTHER_VEHICLE", + "skill_used": "electronics", + "skills_required": [ [ "computer", 5 ], [ "mechanics", 3 ] ], + "difficulty": 5, + "time": "2 h", + "autolearn": true, + "using": [ [ "soldering_standard", 10 ] ], + "qualities": [ { "id": "SCREW", "level": 1 } ], + "tools": [ [ [ "laptop", -1 ] ] ], + "components": [ + [ [ "targeting_module", 1 ] ], + [ [ "ai_module", 1 ] ], + [ [ "gun_module", 1 ] ], + [ [ "cable", 10 ] ], + [ [ "omnicamera", 1 ] ], + [ [ "robot_controls", 1 ] ] + ] } ] diff --git a/data/json/vehicleparts/vehicle_parts.json b/data/json/vehicleparts/vehicle_parts.json index a97fe8adb7a9c..c8602c7f7edcb 100644 --- a/data/json/vehicleparts/vehicle_parts.json +++ b/data/json/vehicleparts/vehicle_parts.json @@ -3100,5 +3100,31 @@ "flags": [ "TURRET_MOUNT" ], "breaks_into": [ { "item": "scrap", "count": [ 1, 4 ] } ], "damage_reduction": { "all": 54 } + }, + { + "type": "vehicle_part", + "id": "controls_turret", + "name": "turret control unit", + "symbol": "$", + "color": "yellow", + "broken_symbol": "$", + "broken_color": "red", + "damage_modifier": 10, + "durability": 50, + "epower": -250, + "item": "turret_controls", + "description": "A set of motor, camera, and an AI unit which allows for tracking targets, friend-or-foe identification, and firing the connected turret in full automatic mode. When installed over the turret, it will enable auto targeting mode for said turret.", + "folded_volume": 3, + "flags": [ "ENABLED_DRAINS_EPOWER", "TURRET_CONTROLS", "UNMOUNT_ON_DAMAGE" ], + "requirements": { + "install": { + "time": 120000, + "skills": [ [ "mechanics", 3 ], [ "electronics", 3 ] ], + "qualities": [ { "id": "SCREW", "level": 1 } ] + }, + "repair": { "skills": [ [ "mechanics", 2 ] ], "time": 8000, "using": [ [ "adhesive", 1 ] ] }, + "removal": { "skills": [ [ "mechanics", 3 ] ], "qualities": [ { "id": "SCREW", "level": 1 } ] } + }, + "breaks_into": "ig_vp_device" } ] diff --git a/doc/JSON_FLAGS.md b/doc/JSON_FLAGS.md index 4ea9d37de5214..3726bbbcb6ca5 100644 --- a/doc/JSON_FLAGS.md +++ b/doc/JSON_FLAGS.md @@ -1371,6 +1371,7 @@ Those flags are added by the game code to specific items (that specific welder, - ```TOWEL``` Can be used to dry yourself up. - ```TRACKED``` Contributes to steering effectiveness but doesn't count as a steering axle for install difficulty and still contributes to drag for the center of steering calculation. - ```TRACK``` Allows the vehicle installed on, to be marked and tracked on map. +- ```TURRET_CONTROLS``` If part with this flag is installed over the turret, it allows to set said turret's targeting mode to full auto. - ```TURRET_MOUNT``` Parts with this flag are suitable for installing turrets. - ```TURRET``` Is a weapon turret. Can only be installed on a part with ```TURRET_MOUNT``` flag. - ```UNMOUNT_ON_DAMAGE``` Part breaks off the vehicle when destroyed by damage. diff --git a/src/savegame_json.cpp b/src/savegame_json.cpp index 3ec7879bf3f0f..e5b9d0687a866 100644 --- a/src/savegame_json.cpp +++ b/src/savegame_json.cpp @@ -2514,6 +2514,15 @@ void vehicle::deserialize( JsonIn &jsin ) for( const vpart_reference &vp : get_any_parts( "TURRET" ) ) { install_part( vp.mount(), vpart_id( "turret_mount" ), false ); + + //Forcibly set turrets' targeting mode to manual if no turret control unit is present on turret's tile on loading save + if( !has_part( global_part_pos3( vp.part() ), "TURRET_CONTROLS" ) ) { + vp.part().enabled = false; + } + //Set turret control unit's state equal to turret's targeting mode on loading save + for( const vpart_reference &turret_part : get_any_parts( "TURRET_CONTROLS" ) ) { + turret_part.part().enabled = vp.part().enabled; + } } // Add vehicle mounts to cars that are missing them. diff --git a/src/turret.cpp b/src/turret.cpp index 874e8b367eab1..c25574c272647 100644 --- a/src/turret.cpp +++ b/src/turret.cpp @@ -292,12 +292,16 @@ void vehicle::turrets_set_targeting() { std::vector turrets; std::vector locations; + std::vector turret_controls; for( auto &p : parts ) { - if( p.base.is_gun() && !p.info().has_flag( "MANUAL" ) ) { + if( p.base.is_gun() ) { turrets.push_back( &p ); locations.push_back( global_part_pos3( p ) ); } + if( part_flag( index_of_part( &p ), "TURRET_CONTROLS" ) ) { + turret_controls.push_back( &p ); + } } pointmenu_cb callback( locations ); @@ -312,8 +316,11 @@ void vehicle::turrets_set_targeting() menu.w_y = 2; for( auto &p : turrets ) { - menu.addentry( -1, true, MENU_AUTOASSIGN, "%s [%s]", p->name(), - p->enabled ? _( "auto" ) : _( "manual" ) ); + menu.addentry( -1, has_part( global_part_pos3( *p ), "TURRET_CONTROLS" ), MENU_AUTOASSIGN, + "%s [%s]", p->name(), p->enabled ? + _( "auto -> manual" ) : has_part( global_part_pos3( *p ), "TURRET_CONTROLS" ) ? + _( "manual -> auto" ) : + _( "manual (turret control unit required for auto mode)" ) ); } menu.query(); @@ -322,7 +329,12 @@ void vehicle::turrets_set_targeting() } sel = menu.ret; - turrets[ sel ]->enabled = !turrets[ sel ]->enabled; + if( has_part( locations[ sel ], "TURRET_CONTROLS" ) ) { + turrets[sel]->enabled = !turrets[sel]->enabled; + turret_controls[sel]->enabled = turrets[sel]->enabled; + } else { + turrets[sel]->enabled = false; + } // clear the turret's current targets to prevent unwanted auto-firing tripoint pos = locations[ sel ]; @@ -381,7 +393,8 @@ bool vehicle::turrets_aim( bool manual, bool automatic, vehicle_part *tur_part ) } ), last ); if( opts.empty() ) { - add_msg( m_warning, _( "Can't aim turrets: all turrets are offline" ) ); + add_msg( m_warning, + _( "Can't aim turrets: all turrets are offline or set to manual targeting mode." ) ); return false; } diff --git a/src/veh_type.cpp b/src/veh_type.cpp index dd665e618ed36..b65f3c85c9d0e 100644 --- a/src/veh_type.cpp +++ b/src/veh_type.cpp @@ -103,6 +103,7 @@ static const std::unordered_map vpart_bitflag_map = { "FLUIDTANK", VPFLAG_FLUIDTANK }, { "REACTOR", VPFLAG_REACTOR }, { "RAIL", VPFLAG_RAIL }, + { "TURRET_CONTROLS", VPFLAG_TURRET_CONTROLS }, }; static const std::vector> standard_terrain_mod = {{ diff --git a/src/veh_type.h b/src/veh_type.h index 297ac9e27e640..b872fd460719e 100644 --- a/src/veh_type.h +++ b/src/veh_type.h @@ -75,6 +75,7 @@ enum vpart_bitflags : int { VPFLAG_FLUIDTANK, VPFLAG_REACTOR, VPFLAG_RAIL, + VPFLAG_TURRET_CONTROLS, NUM_VPFLAGS }; diff --git a/src/vehicle.cpp b/src/vehicle.cpp index b5275e7827b82..e0d522bbc10c2 100644 --- a/src/vehicle.cpp +++ b/src/vehicle.cpp @@ -1381,6 +1381,20 @@ bool vehicle::can_mount( const point &dp, const vpart_id &id ) const } } + //Turret controls must be installed on a turret + if( part.has_flag( "TURRET_CONTROLS" ) ) { + bool anchor_found = false; + for( const auto &elem : parts_in_square ) { + if( part_info( elem ).has_flag( "TURRET" ) ) { + anchor_found = true; + break; + } + } + if( !anchor_found ) { + return false; + } + } + //Anything not explicitly denied is permitted return true; } @@ -5289,6 +5303,9 @@ void vehicle::refresh() } else if( !camera_on && vpi.has_flag( "CAMERA" ) ) { vp.part().enabled = false; } + if( vpi.has_flag( "TURRET" ) && !has_part( global_part_pos3( vp.part() ), "TURRET_CONTROLS" ) ) { + vp.part().enabled = false; + } } rail_wheel_bounding_box.p1 = point( railwheel_xmin, railwheel_ymin );