diff --git a/data/json/items/vehicle/controls.json b/data/json/items/vehicle/controls.json index 63ec8a5773d16..59a7001d1688b 100644 --- a/data/json/items/vehicle/controls.json +++ b/data/json/items/vehicle/controls.json @@ -15,6 +15,22 @@ "category": "veh_parts", "price": 40000 }, + { + "id": "reins_tackle", + "type": "GENERIC", + "name": { "str": "rein and tackle", "str_pl": "reins and tackles" }, + "category": "veh_parts", + "description": "A set of leather bindings to control a mountable creature.", + "weight": "500 g", + "volume": "1200 ml", + "price": 30000, + "bashing": 1, + "to_hit": -2, + "material": [ "leather" ], + "symbol": "W", + "color": "light_gray", + "looks_like": "rope_6" + }, { "type": "GENERIC", "id": "vehicle_dashboard", diff --git a/data/json/recipes/recipe_vehicle.json b/data/json/recipes/recipe_vehicle.json index 058c3269f3e27..f2f5fccef33cc 100644 --- a/data/json/recipes/recipe_vehicle.json +++ b/data/json/recipes/recipe_vehicle.json @@ -39,6 +39,20 @@ "qualities": [ { "id": "HAMMER", "level": 2 } ], "components": [ [ [ "sheet_metal", 3 ] ] ] }, + { + "result": "reins_tackle", + "type": "recipe", + "category": "CC_OTHER", + "subcategory": "CSC_OTHER_VEHICLE", + "skill_used": "tailor", + "difficulty": 3, + "time": "120 m", + "using": [ [ "sewing_standard", 100 ] ], + "components": [ + [ [ "leather", 12 ], [ "tanned_hide", 2 ], [ "fur", 12 ], [ "tanned_pelt", 2 ] ], + [ [ "cordage_superior", 2, "LIST" ] ] + ] + }, { "result": "yoke_harness", "type": "recipe", diff --git a/data/json/vehicleparts/vehicle_parts.json b/data/json/vehicleparts/vehicle_parts.json index 68237b33ff771..1ff22f871634e 100644 --- a/data/json/vehicleparts/vehicle_parts.json +++ b/data/json/vehicleparts/vehicle_parts.json @@ -1627,6 +1627,28 @@ "breaks_into": [ { "item": "splinter", "count": [ 2, 4 ] } ], "damage_reduction": { "all": 5 } }, + { + "type": "vehicle_part", + "id": "reins_tackle", + "name": "reins and tackle", + "symbol": "W", + "color": "light_gray", + "broken_symbol": "X", + "broken_color": "red", + "durability": 80, + "description": "A set of leather bindings to control a creature pulling a vehicle, such as a cart.", + "bonus": 10, + "damage_modifier": 5, + "item": "reins_tackle", + "folded_volume": 1, + "requirements": { + "install": { "skills": [ [ "mechanics", 1 ] ], "time": "6 m", "using": [ [ "vehicle_nail_install", 1 ] ] }, + "removal": { "skills": [ [ "mechanics", 1 ] ], "time": "3 m", "using": [ [ "vehicle_nail_removal", 1 ] ] }, + "repair": { "skills": [ [ "mechanics", 1 ] ], "time": "6 m", "using": [ [ "adhesive", 2 ] ] } + }, + "flags": [ "CONTROL_ANIMAL", "FOLDABLE" ], + "breaks_into": [ { "item": "leather", "count": [ 1, 2 ] } ] + }, { "type": "vehicle_part", "id": "controls", diff --git a/doc/JSON_FLAGS.md b/doc/JSON_FLAGS.md index e6455a41978b6..e7291158b9925 100644 --- a/doc/JSON_FLAGS.md +++ b/doc/JSON_FLAGS.md @@ -1330,6 +1330,7 @@ Those flags are added by the game code to specific items (that specific welder, - ```CIRCLE_LIGHT``` Projects a circular radius of light when turned on. - ```CONE_LIGHT``` Projects a cone of light when turned on. - ```CONTROLS``` Can be used to control the vehicle. +- ```CONTROL_ANIMAL``` These controls can only be used to control a vehicle pulled by an animal (such as reins etc). - ```COOLER``` There is separate command to toggle this part. - ```COVERED``` Prevents items in cargo parts from emitting any light. - ```CRAFTRIG``` Acts as a dehydrator, vacuum sealer and reloading press for crafting purposes. Potentially to include additional tools in the future. diff --git a/src/game.cpp b/src/game.cpp index 0a2b1a471cdc0..e7982fcf01a5c 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -5103,6 +5103,7 @@ void game::moving_vehicle_dismount( const tripoint &dest_loc ) void game::control_vehicle() { + static const itype_id fuel_type_animal( "animal" ); int veh_part = -1; vehicle *veh = remoteveh(); if( veh == nullptr ) { @@ -5111,9 +5112,16 @@ void game::control_vehicle() veh_part = vp->part_index(); } } - if( veh != nullptr && veh->player_in_control( u ) ) { + if( veh != nullptr && veh->player_in_control( u ) && + veh->avail_part_with_feature( veh_part, "CONTROLS", true ) >= 0 ) { veh->use_controls( u.pos() ); - } else if( veh && veh->avail_part_with_feature( veh_part, "CONTROLS", true ) >= 0 && + } else if( veh && veh->player_in_control( u ) && + veh->avail_part_with_feature( veh_part, "CONTROL_ANIMAL", true ) >= 0 ) { + u.controlling_vehicle = false; + add_msg( m_info, _( "You let go of the reins." ) ); + } else if( veh && ( veh->avail_part_with_feature( veh_part, "CONTROLS", true ) >= 0 || + ( veh->avail_part_with_feature( veh_part, "CONTROL_ANIMAL", true ) >= 0 && + veh->has_engine_type( fuel_type_animal, false ) && veh->has_harnessed_animal() ) ) && u.in_vehicle ) { if( !veh->interact_vehicle_locked() ) { veh->handle_potential_theft( dynamic_cast( u ) ); @@ -9383,7 +9391,8 @@ point game::place_player( const tripoint &dest_loc ) } } - if( vp1.part_with_feature( "CONTROLS", true ) && u.in_vehicle && !u.is_mounted() ) { + if( ( vp1.part_with_feature( "CONTROL_ANIMAL", true ) || + vp1.part_with_feature( "CONTROLS", true ) ) && u.in_vehicle && !u.is_mounted() ) { add_msg( _( "There are vehicle controls here." ) ); if( !u.has_trait( trait_id( "WAYFARER" ) ) ) { add_msg( m_info, _( "%s to drive." ), press_x( ACTION_CONTROL_VEHICLE ) ); diff --git a/src/handle_action.cpp b/src/handle_action.cpp index 050ac4bfb4929..fe8677a52799e 100644 --- a/src/handle_action.cpp +++ b/src/handle_action.cpp @@ -418,11 +418,19 @@ static void pldrive( int x, int y ) return; } if( !remote ) { - int pctr = veh->part_with_feature( part, "CONTROLS", true ); - if( pctr < 0 ) { + static const itype_id fuel_type_animal( "animal" ); + const bool has_animal_controls = veh->part_with_feature( part, "CONTROL_ANIMAL", true ) >= 0; + const bool has_controls = veh->part_with_feature( part, "CONTROLS", true ) >= 0; + const bool has_animal = veh->has_engine_type( fuel_type_animal, false ) && + veh->has_harnessed_animal(); + if( !has_controls && !has_animal_controls ) { add_msg( m_info, _( "You can't drive the vehicle from here. You need controls!" ) ); u.controlling_vehicle = false; return; + } else if( !has_controls && has_animal_controls && !has_animal ) { + add_msg( m_info, _( "You can't drive this vehicle without an animal to pull it." ) ); + u.controlling_vehicle = false; + return; } } else { if( empty( veh->get_avail_parts( "REMOTE_CONTROLS" ) ) ) { diff --git a/src/vehicle.cpp b/src/vehicle.cpp index 9b78f98a7e402..b8f11daeafa7c 100644 --- a/src/vehicle.cpp +++ b/src/vehicle.cpp @@ -244,7 +244,10 @@ bool vehicle::player_in_control( const player &p ) const const optional_vpart_position vp = g->m.veh_at( p.pos() ); if( vp && &vp->vehicle() == this && - part_with_feature( vp->part_index(), VPFLAG_CONTROLS, false ) >= 0 && p.controlling_vehicle ) { + ( ( part_with_feature( vp->part_index(), "CONTROL_ANIMAL", true ) >= 0 && + has_engine_type( fuel_type_animal, false ) && has_harnessed_animal() ) || + ( part_with_feature( vp->part_index(), VPFLAG_CONTROLS, false ) >= 0 ) ) && + p.controlling_vehicle ) { return true; } diff --git a/src/vehicle.h b/src/vehicle.h index 14a4f377cce2d..660c0414d6156 100644 --- a/src/vehicle.h +++ b/src/vehicle.h @@ -1534,6 +1534,7 @@ class vehicle //true if an engine exists with specified type //If enabled true, this engine must be enabled to return true bool has_engine_type( const itype_id &ft, bool enabled ) const; + bool has_harnessed_animal() const; //true if an engine exists without the specified type //If enabled true, this engine must be enabled to return true bool has_engine_type_not( const itype_id &ft, bool enabled ) const; diff --git a/src/vehicle_move.cpp b/src/vehicle_move.cpp index 5b2e91937b23e..5f22d25f51891 100644 --- a/src/vehicle_move.cpp +++ b/src/vehicle_move.cpp @@ -854,6 +854,20 @@ void vehicle::handle_trap( const tripoint &p, int part ) } } +bool vehicle::has_harnessed_animal() const +{ + for( size_t e = 0; e < parts.size(); e++ ) { + const vehicle_part &vp = parts[ e ]; + if( vp.info().fuel_type == fuel_type_animal ) { + monster *mon = get_pet( e ); + if( mon && mon->has_effect( effect_harnessed ) && mon->has_effect( effect_pet ) ) { + return true; + } + } + } + return false; +} + void vehicle::autodrive( int x, int y ) { // for now, autodriving is only possible when pulled by an animal