diff --git a/data/json/vehicleparts/vehicle_parts.json b/data/json/vehicleparts/vehicle_parts.json index c2702dd13be56..0a09fd450b6ad 100644 --- a/data/json/vehicleparts/vehicle_parts.json +++ b/data/json/vehicleparts/vehicle_parts.json @@ -1891,7 +1891,7 @@ "removal": { "skills": [ [ "mechanics", 1 ] ], "time": "5 m", "using": [ [ "vehicle_screw", 1 ] ] }, "repair": { "skills": [ [ "mechanics", 1 ] ], "time": "20 s", "using": [ [ "adhesive", 1 ] ] } }, - "flags": [ "OPENABLE", "OPENCLOSE_INSIDE", "OPAQUE", "CURTAIN", "WINDOW_CURTAIN", "MULTISQUARE" ], + "flags": [ "OPENABLE", "OPENCLOSE_INSIDE", "OPAQUE", "CURTAIN", "MULTISQUARE", "NEEDS_WINDOW" ], "breaks_into": [ ] }, { diff --git a/data/json/vehicleparts/vp_flags.json b/data/json/vehicleparts/vp_flags.json index cedf4c825f1fc..50299b7a1a5d6 100644 --- a/data/json/vehicleparts/vp_flags.json +++ b/data/json/vehicleparts/vp_flags.json @@ -3,7 +3,8 @@ "id": "ALTERNATOR", "type": "json_flag", "context": [ "vehicle_part" ], - "info": "An alternator. When mounted on a gasoline or diesel engine and the engine is on, will produce electrical power that can be stored in a battery." + "info": "An alternator. When mounted on a gasoline or diesel engine and the engine is on, will produce electrical power that can be stored in a battery.", + "requires_flag": "E_ALTERNATOR" }, { "id": "AISLE", @@ -65,6 +66,12 @@ "context": [ "vehicle_part" ], "info": "Closed, it prevents people from seeing through the curtain. A door motor allows you to remotely open or close it from the vehicle controls." }, + { + "id": "NEEDS_WINDOW", + "type": "json_flag", + "context": [ "vehicle_part" ], + "requires_flag": "WINDOW" + }, { "id": "DOOR", "type": "json_flag", @@ -123,7 +130,8 @@ "id": "SEATBELT", "type": "json_flag", "context": [ "vehicle_part" ], - "info": "This part will help prevent you from being thrown from the vehicle in a collision. You will automatically enable this part when you move into a tile with it." + "info": "This part will help prevent you from being thrown from the vehicle in a collision. You will automatically enable this part when you move into a tile with it.", + "requires_flag": "BELTABLE" }, { "id": "STABLE", @@ -141,7 +149,14 @@ "id": "TURRET", "type": "json_flag", "context": [ "vehicle_part" ], - "info": "A heavy weapon mounted in a turret. It can be fired from the vehicle controls." + "info": "A heavy weapon mounted in a turret. It can be fired from the vehicle controls.", + "requires_flag": "TURRET_MOUNT" + }, + { + "id": "TURRET_CONTROLS", + "type": "json_flag", + "context": [ "vehicle_part" ], + "requires_flag": "TURRET" }, { "id": "WHEEL", @@ -160,5 +175,59 @@ "type": "json_flag", "context": [ "vehicle_part" ], "info": "You can craft here." + }, + { + "id": "NEEDS_WHEEL_MOUNT_LIGHT", + "type": "json_flag", + "context": [ "vehicle_part" ], + "requires_flag": "WHEEL_MOUNT_LIGHT" + }, + { + "id": "NEEDS_WHEEL_MOUNT_MEDIUM", + "type": "json_flag", + "context": [ "vehicle_part" ], + "requires_flag": "WHEEL_MOUNT_MEDIUM" + }, + { + "id": "NEEDS_WHEEL_MOUNT_HEAVY", + "type": "json_flag", + "context": [ "vehicle_part" ], + "requires_flag": "WHEEL_MOUNT_HEAVY" + }, + { + "id": "NEEDS_BATTERY_MOUNT", + "type": "json_flag", + "context": [ "vehicle_part" ], + "requires_flag": "BATTERY_MOUNT" + }, + { + "id": "ON_CONTROLS", + "type": "json_flag", + "context": [ "vehicle_part" ], + "requires_flag": "CONTROLS" + }, + { + "id": "INTERNAL", + "type": "json_flag", + "context": [ "vehicle_part" ], + "requires_flag": "CARGO" + }, + { + "id": "CARGO_LOCKING", + "type": "json_flag", + "context": [ "vehicle_part" ], + "requires_flag": "LOCKABLE_CARGO" + }, + { + "id": "DOOR_MOTOR", + "type": "json_flag", + "context": [ "vehicle_part" ], + "requires_flag": "OPENABLE" + }, + { + "id": "ON_ROOF", + "type": "json_flag", + "context": [ "vehicle_part" ], + "requires_flag": "ROOF" } ] diff --git a/doc/JSON_FLAGS.md b/doc/JSON_FLAGS.md index 85f597177209d..0415bc0b2b65e 100644 --- a/doc/JSON_FLAGS.md +++ b/doc/JSON_FLAGS.md @@ -1312,7 +1312,7 @@ Those flags are added by the game code to specific items (that specific welder, - ```AUTOPILOT``` This part will enable a vehicle to have a simple autopilot. - ```AISLE_LIGHT``` - ```AISLE``` Player can move over this part with less speed penalty than normal. -- ```ALTERNATOR``` Recharges batteries installed on the vehicle. +- ```ALTERNATOR``` Recharges batteries installed on the vehicle. Can only be installed on a part with ```E_ALTERNATOR``` flag. - ```ANCHOR_POINT``` Allows secure seatbelt attachment. - ```ANIMAL_CTRL``` Can harness an animal, need HARNESS_bodytype flag to specify bodytype of animal. - ```ARMOR``` Protects the other vehicle parts it's installed over during collisions. @@ -1326,7 +1326,7 @@ Those flags are added by the game code to specific items (that specific welder, - ```CAMERA_CONTROL``` - ```CAMERA``` - ```CAPTURE_MOSNTER_VEH``` Can be used to capture monsters when mounted on a vehicle. -- ```CARGO_LOCKING``` This cargo area is inaccessible to NPCs. +- ```CARGO_LOCKING``` This cargo area is inaccessible to NPCs. Can only be installed on a part with ```LOCKABLE_CARGO``` flag. - ```CARGO``` Cargo holding area. - ```CHEMLAB``` Acts as a chemistry set for crafting. - ```CHIMES``` Generates continuous noise when used. @@ -1341,7 +1341,7 @@ Those flags are added by the game code to specific items (that specific welder, - ```CURTAIN``` Can be installed over a part flagged with ```WINDOW```, and functions the same as blinds found on windows in buildings. - ```DIFFICULTY_REMOVE``` - ```DOME_LIGHT``` -- ```DOOR_MOTOR``` +- ```DOOR_MOTOR``` Can only be installed on a part with ```OPENABLE``` flag. - ```ENGINE``` Is an engine and contributes towards vehicle mechanical power. - ```EVENTURN``` Only on during even turns. - ```EXTRA_DRAG``` tells the vehicle that the part exerts engine power reduction. @@ -1363,7 +1363,7 @@ Those flags are added by the game code to specific items (that specific welder, - ```HARNESS_bodytype``` Replace bodytype with `any` to accept any type, or with the targeted type. - ```HORN``` Generates noise when used. - ```INITIAL_PART``` When starting a new vehicle via the construction menu, this vehicle part will be the initial part of the vehicle (if the used item matches the item required for this part). The items of parts with this flag are automatically added as component to the vehicle start construction. -- ```INTERNAL``` Must be mounted inside a cargo area. +- ```INTERNAL``` Can only be installed on a part with ```CARGO``` flag. - ```KITCHEN``` Acts as a kitchen unit and heat source for crafting. - ```LOCKABLE_CARGO``` Cargo containers that are able to have a lock installed. - ```MUFFLER``` Muffles the noise a vehicle makes while running. @@ -1376,7 +1376,7 @@ Those flags are added by the game code to specific items (that specific welder, - ```NO_JACK``` - ```OBSTACLE``` Cannot walk through part, unless the part is also ```OPENABLE```. - ```ODDTURN``` Only on during odd turns. -- ```ON_CONTROLS``` +- ```ON_CONTROLS``` Can only be installed on a part with ```CONTROLS``` flag. - ```ON_ROOF``` - Parts with this flag could only be installed on a roof (parts with ```ROOF``` flag). - ```OPAQUE``` Cannot be seen through. - ```OPENABLE``` Can be opened or closed. @@ -1394,7 +1394,7 @@ Those flags are added by the game code to specific items (that specific welder, - ```REVERSIBLE``` Removal has identical requirements to installation but is twice as quick - ```ROOF``` Covers a section of the vehicle. Areas of the vehicle that have a roof and roofs on surrounding sections, are considered inside. Otherwise they're outside. - ```SCOOP``` Pulls items from underneath the vehicle to the cargo space of the part. Also mops up liquids. -- ```SEATBELT``` Helps prevent the player from being ejected from the vehicle during an accident. +- ```SEATBELT``` Helps prevent the player from being ejected from the vehicle during an accident. Can only be installed on a part with ```BELTABLE``` flag. - ```SEAT``` A seat where the player can sit or sleep. - ```SECURITY``` - ```SHARP``` Striking a monster with this part does cutting damage instead of bashing damage, and prevents stunning the monster. @@ -1410,7 +1410,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_CONTROLS``` If part with this flag is installed over the turret, it allows to set said turret's targeting mode to full auto. Can only be installed on a part with ```TURRET``` flag. - ```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. @@ -1428,6 +1428,15 @@ Those flags are added by the game code to specific items (that specific welder, - ```WIND_POWERED``` This engine is powered by wind ( sails etc ). - ```WIND_TURBINE``` Recharges vehicle batteries when exposed to wind. - ```WORKBENCH``` Can craft at this part, must be paired with a workbench json entry. +- ```NEEDS_WINDOW``` Can only be installed on a part with ```WINDOW``` flag. +- ```NEEDS_WHEEL_MOUNT_LIGHT``` Can only be installed on a part with ```WHEEL_MOUNT_LIGHT``` flag. +- ```NEEDS_WHEEL_MOUNT_MEDIUM``` Can only be installed on a part with ```WHEEL_MOUNT_MEDIUM``` flag. +- ```NEEDS_WHEEL_MOUNT_HEAVY``` Can only be installed on a part with ```WHEEL_MOUNT_HEAVY``` flag. + +### Vehicle parts requiring other vehicle parts + +The requirement for other vehicle parts is defined for a json flag by setting ```requires_flag``` for the flag. ```requires_flag``` is the other flag that a part with this flag requires. + ### Fuel types diff --git a/src/flag.cpp b/src/flag.cpp index 611ccb45aac55..e025c3df82c91 100644 --- a/src/flag.cpp +++ b/src/flag.cpp @@ -24,6 +24,7 @@ void json_flag::load( const JsonObject &jo ) jo.read( "conflicts", f.conflicts_ ); jo.read( "inherit", f.inherit_ ); jo.read( "craft_inherit", f.craft_inherit_ ); + jo.read( "requires_flag", f.requires_flag_ ); jo.read( "taste_mod", f.taste_mod_ ); // FIXME: most flags have a "context" field that isn't used for anything diff --git a/src/flag.h b/src/flag.h index 67f35224abae6..19b6fc2c03442 100644 --- a/src/flag.h +++ b/src/flag.h @@ -35,6 +35,11 @@ class json_flag return craft_inherit_; } + /** Requires this flag to be installed on vehicle */ + std::string requires_flag() const { + return requires_flag_; + } + /** The flag's modifier on the fun value of comestibles */ int taste_mod() const { return taste_mod_; @@ -51,6 +56,7 @@ class json_flag std::set conflicts_; bool inherit_ = true; bool craft_inherit_ = false; + std::string requires_flag_; int taste_mod_ = 0; json_flag( const std::string &id = std::string() ) : id_( id ) {} diff --git a/src/veh_type.h b/src/veh_type.h index 510b60605a163..2339063f4bdf1 100644 --- a/src/veh_type.h +++ b/src/veh_type.h @@ -331,6 +331,9 @@ class vpart_info // Display order in vehicle interact display int list_order = 0; + const std::set &get_flags() const { + return flags; + } bool has_flag( const std::string &flag ) const { return flags.count( flag ) != 0; } diff --git a/src/vehicle.cpp b/src/vehicle.cpp index adc777ffb8f30..68e4c6fa845cb 100644 --- a/src/vehicle.cpp +++ b/src/vehicle.cpp @@ -20,6 +20,7 @@ #include "bionics.h" #include "cata_utility.h" #include "clzones.h" +#include "flag.h" #include "colony.h" #include "coordinate_conversions.h" #include "debug.h" @@ -1350,111 +1351,23 @@ bool vehicle::can_mount( const point &dp, const vpart_id &id ) const return false; } - // Alternators must be installed on a gas engine - if( part.has_flag( VPFLAG_ALTERNATOR ) ) { - bool anchor_found = false; - for( const auto &elem : parts_in_square ) { - if( part_info( elem ).has_flag( "E_ALTERNATOR" ) ) { - anchor_found = true; - } - } - if( !anchor_found ) { - return false; - } - } - - //Seatbelts must be installed on a seat - if( part.has_flag( "SEATBELT" ) ) { - bool anchor_found = false; - for( const auto &elem : parts_in_square ) { - if( part_info( elem ).has_flag( "BELTABLE" ) ) { - anchor_found = true; - } - } - if( !anchor_found ) { - return false; - } - } - - //Internal must be installed into a cargo area. - if( part.has_flag( "INTERNAL" ) ) { - bool anchor_found = false; - for( const auto &elem : parts_in_square ) { - if( part_info( elem ).has_flag( "CARGO" ) ) { - anchor_found = true; - } - } - if( !anchor_found ) { - return false; - } - } - - // curtains must be installed on (reinforced)windshields - // TODO: do this automatically using "location":"on_mountpoint" - if( part.has_flag( "WINDOW_CURTAIN" ) ) { - bool anchor_found = false; - for( const auto &elem : parts_in_square ) { - if( part_info( elem ).has_flag( "WINDOW" ) ) { - anchor_found = true; - } - } - if( !anchor_found ) { - return false; - } - } - - // Security system must be installed on controls - if( part.has_flag( "ON_CONTROLS" ) ) { - bool anchor_found = false; - for( auto it : parts_in_square ) { - if( part_info( it ).has_flag( "CONTROLS" ) ) { - anchor_found = true; + // Check all the flags of the part to see if they require other flags + // If other flags are required check if those flags are present + for( const std::string flag : part.get_flags() ) { + if( !json_flag::get( flag ).requires_flag().empty() ) { + bool anchor_found = false; + for( const auto &elem : parts_in_square ) { + if( part_info( elem ).has_flag( json_flag::get( flag ).requires_flag() ) ) { + anchor_found = true; + } } - } - if( !anchor_found ) { - return false; - } - } - - // Cargo locks must go on lockable cargo containers - // TODO: do this automatically using "location":"on_mountpoint" - if( part.has_flag( "CARGO_LOCKING" ) ) { - bool anchor_found = false; - for( auto it : parts_in_square ) { - if( part_info( it ).has_flag( "LOCKABLE_CARGO" ) ) { - anchor_found = true; + if( !anchor_found ) { + return false; } } - if( !anchor_found ) { - return false; - } } - //Swappable storage battery must be installed on a BATTERY_MOUNT - if( part.has_flag( "NEEDS_BATTERY_MOUNT" ) ) { - bool anchor_found = false; - for( const auto &elem : parts_in_square ) { - if( part_info( elem ).has_flag( "BATTERY_MOUNT" ) ) { - anchor_found = true; - } - } - if( !anchor_found ) { - return false; - } - } - //Door motors need OPENABLE - if( part.has_flag( "DOOR_MOTOR" ) ) { - bool anchor_found = false; - for( const auto &elem : parts_in_square ) { - if( part_info( elem ).has_flag( "OPENABLE" ) ) { - anchor_found = true; - } - } - if( !anchor_found ) { - return false; - } - } //Mirrors cannot be mounted on OPAQUE parts if( part.has_flag( "VISION" ) && !part.has_flag( "CAMERA" ) ) { @@ -1464,7 +1377,7 @@ bool vehicle::can_mount( const point &dp, const vpart_id &id ) const } } } - //and vice versa + //Opaque parts cannot be mounted on mirrors parts if( part.has_flag( "OPAQUE" ) ) { for( const auto &elem : parts_in_square ) { if( part_info( elem ).has_flag( "VISION" ) && @@ -1474,20 +1387,6 @@ bool vehicle::can_mount( const point &dp, const vpart_id &id ) const } } - //Turrets must be installed on a turret mount - if( part.has_flag( "TURRET" ) ) { - bool anchor_found = false; - for( const auto &elem : parts_in_square ) { - if( part_info( elem ).has_flag( "TURRET_MOUNT" ) ) { - anchor_found = true; - break; - } - } - if( !anchor_found ) { - return false; - } - } - //Turret mounts must NOT be installed on other (modded) turret mounts if( part.has_flag( "TURRET_MOUNT" ) ) { for( const auto &elem : parts_in_square ) { @@ -1497,72 +1396,6 @@ bool vehicle::can_mount( const point &dp, const vpart_id &id ) const } } - //Roof-mounted parts must be installed on a roofs - if( part.has_flag( "ON_ROOF" ) ) { - bool anchor_found = false; - for( const auto &elem : parts_in_square ) { - if( part_info( elem ).has_flag( "ROOF" ) ) { - anchor_found = true; - break; - } - } - if( !anchor_found ) { - return false; - } - } - - // Wheels that need axles must be installed on a wheel mount - if( part.has_flag( "NEEDS_WHEEL_MOUNT_LIGHT" ) ) { - bool anchor_found = false; - for( const auto &elem : parts_in_square ) { - if( part_info( elem ).has_flag( "WHEEL_MOUNT_LIGHT" ) ) { - anchor_found = true; - break; - } - } - if( !anchor_found ) { - return false; - } - } - if( part.has_flag( "NEEDS_WHEEL_MOUNT_MEDIUM" ) ) { - bool anchor_found = false; - for( const auto &elem : parts_in_square ) { - if( part_info( elem ).has_flag( "WHEEL_MOUNT_MEDIUM" ) ) { - anchor_found = true; - break; - } - } - if( !anchor_found ) { - return false; - } - } - if( part.has_flag( "NEEDS_WHEEL_MOUNT_HEAVY" ) ) { - bool anchor_found = false; - for( const auto &elem : parts_in_square ) { - if( part_info( elem ).has_flag( "WHEEL_MOUNT_HEAVY" ) ) { - anchor_found = true; - break; - } - } - if( !anchor_found ) { - return false; - } - } - - //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; } @@ -1579,25 +1412,16 @@ bool vehicle::can_unmount( const int p, std::string &reason ) const return false; } - // Check if the part is required by another part. Do not allow removing those. - // { "FLAG THAT IS REQUIRED", "FLAG THAT REQUIRES", "Reason why can't remove." } - static const std::array, 9> blocking_flags = {{ - std::make_tuple( "ENGINE", "ALTERNATOR", translate_marker( "Remove attached alternator first." ) ), - std::make_tuple( "BELTABLE", "SEATBELT", translate_marker( "Remove attached seatbelt first." ) ), - std::make_tuple( "WINDOW", "CURTAIN", translate_marker( "Remove attached curtains first." ) ), - std::make_tuple( "CONTROLS", "ON_CONTROLS", translate_marker( "Remove the attached %s first." ) ), - std::make_tuple( "BATTERY_MOUNT", "NEEDS_BATTERY_MOUNT", translate_marker( "Remove battery from mount first." ) ), - std::make_tuple( "TURRET_MOUNT", "TURRET", translate_marker( "Remove attached mounted weapon first." ) ), - std::make_tuple( "WHEEL_MOUNT_LIGHT", "NEEDS_WHEEL_MOUNT_LIGHT", translate_marker( "Remove attached wheel first." ) ), - std::make_tuple( "WHEEL_MOUNT_MEDIUM", "NEEDS_WHEEL_MOUNT_MEDIUM", translate_marker( "Remove attached wheel first." ) ), - std::make_tuple( "WHEEL_MOUNT_HEAVY", "NEEDS_WHEEL_MOUNT_HEAVY", translate_marker( "Remove attached wheel first." ) ) - } - }; - for( auto &flag_check : blocking_flags ) { - const int part_idx_requires = part_with_feature( p, std::get<1>( flag_check ), false ); - if( part_flag( p, std::get<0>( flag_check ) ) && part_idx_requires >= 0 ) { - reason = string_format( _( std::get<2>( flag_check ) ), parts[part_idx_requires].info().name() ); - return false; + // Find all the flags on parts in this tile that require other flags + const point pt = parts[p].mount; + std::vector parts_here = parts_at_relative( pt, false ); + + for( auto &elem : parts_here ) { + for( const std::string &flag : part_info( elem ).get_flags() ) { + if( part_info( p ).has_flag( json_flag::get( flag ).requires_flag() ) ) { + reason = string_format( _( "Remove the attached %s first." ), part_info( elem ).name() ); + return false; + } } }