From 3bb07fd3c25588b846e478e4ae3a1f99c680f577 Mon Sep 17 00:00:00 2001 From: Davi Date: Sun, 13 Oct 2019 06:44:52 -0400 Subject: [PATCH 1/3] Add infrastructure for restricting clothing mods --- doc/JSON_INFO.md | 2 ++ src/clothing_mod.cpp | 1 + src/clothing_mod.h | 1 + src/item_factory.cpp | 1 + src/itype.h | 5 +++++ src/iuse_actor.cpp | 42 ++++++++++++++++++++++++++++-------------- 6 files changed, 38 insertions(+), 14 deletions(-) diff --git a/doc/JSON_INFO.md b/doc/JSON_INFO.md index 51e419500e34f..cb7d9da7659b3 100644 --- a/doc/JSON_INFO.md +++ b/doc/JSON_INFO.md @@ -1216,6 +1216,7 @@ Armor can be defined like this: "coverage" : 80, // What percentage of body part "material_thickness" : 1, // Thickness of material, in millimeter units (approximately). Generally ranges between 1 - 5, more unusual armor types go up to 10 or more "power_armor" : false, // If this is a power armor item (those are special). +"valid_mods" : ["steel_padded"] // List of valid clothing mods. Note that if the clothing mod doesn't have "restricted" listed, this isn't needed. ``` Alternately, every item (book, tool, gun, even food) can be used as armor if it has armor_data: ```C++ @@ -2380,6 +2381,7 @@ A flat multiplier on the harvest count of the plant. For numbers greater than on "item": "leather", // item to consume. "implement_prompt": "Pad with leather", // prompt to show when implement mod. "destroy_prompt": "Destroy leather padding", // prompt to show when destroy mod. +"restricted": true, // (optional) If true, clothing must list this mod's flag in "valid_mods" list to use it. Defaults to false. "mod_value": [ // List of mod effect. { "type": "bash", // "bash", "cut", "fire", "acid", "warmth", "storage", and "encumbrance" is available. diff --git a/src/clothing_mod.cpp b/src/clothing_mod.cpp index 274e7a2cc1252..169310877822a 100644 --- a/src/clothing_mod.cpp +++ b/src/clothing_mod.cpp @@ -62,6 +62,7 @@ void clothing_mod::load( JsonObject &jo, const std::string & ) mandatory( jo, was_loaded, "item", item_string ); mandatory( jo, was_loaded, "implement_prompt", implement_prompt ); mandatory( jo, was_loaded, "destroy_prompt", destroy_prompt ); + optional( jo, was_loaded, "restricted", restricted, false ); JsonArray jarr = jo.get_array( "mod_value" ); while( jarr.has_more() ) { diff --git a/src/clothing_mod.h b/src/clothing_mod.h index b53e121156f16..759670034afcc 100644 --- a/src/clothing_mod.h +++ b/src/clothing_mod.h @@ -53,6 +53,7 @@ struct clothing_mod { std::string implement_prompt; std::string destroy_prompt; std::vector< mod_value > mod_values; + bool restricted; static size_t count(); }; diff --git a/src/item_factory.cpp b/src/item_factory.cpp index 627104d799be7..5536968dbe7b2 100644 --- a/src/item_factory.cpp +++ b/src/item_factory.cpp @@ -1512,6 +1512,7 @@ void Item_factory::load( islot_armor &slot, JsonObject &jo, const std::string &s assign( jo, "weight_capacity_modifier", slot.weight_capacity_modifier ); assign( jo, "weight_capacity_bonus", slot.weight_capacity_bonus, strict, 0_gram ); assign( jo, "power_armor", slot.power_armor, strict ); + assign( jo, "valid_mods", slot.valid_mods, strict ); assign_coverage_from_json( jo, "covers", slot.covers, slot.sided ); } diff --git a/src/itype.h b/src/itype.h index 5abf692e497d3..628e2d3f575e9 100644 --- a/src/itype.h +++ b/src/itype.h @@ -264,6 +264,11 @@ struct islot_armor { * Whether this is a power armor item. */ bool power_armor = false; + /** + * Whitelisted clothing mods. + * Restricted clothing mods must be listed here by id to be compatible. + */ + std::vector valid_mods; }; struct islot_pet_armor { diff --git a/src/iuse_actor.cpp b/src/iuse_actor.cpp index 30d0708273584..0b68762d18af4 100644 --- a/src/iuse_actor.cpp +++ b/src/iuse_actor.cpp @@ -4525,11 +4525,8 @@ int sew_advanced_actor::use( player &p, item &it, bool, const tripoint & ) const // We need extra thread to lose it on bad rolls const int thread_needed = mod.volume() / 125_ml + 10; - // Returns true if the item already has the mod or if we have enough materials and thread to add it - const auto can_add_mod = [&]( const std::string & new_mod, const itype_id & mat_item ) { - return mod.item_tags.count( new_mod ) > 0 || - ( it.charges >= thread_needed && has_enough[mat_item] ); - }; + + const auto valid_mods = mod.find_armor_data()->valid_mods; const auto get_compare_color = [&]( const int before, const int after, const bool higher_is_better ) { @@ -4548,24 +4545,41 @@ int sew_advanced_actor::use( player &p, item &it, bool, const tripoint & ) const }; uilist tmenu; - // TODO: Tell how much thread will we use - if( it.charges >= thread_needed ) { - tmenu.text = _( "How do you want to modify it?" ); - } else { - tmenu.text = _( "Not enough thread to modify. Which modification do you want to remove?" ); - } + tmenu.text = _( "How do you want to modify it?" ); int index = 0; for( auto cm : clothing_mods ) { auto obj = cm.obj(); item temp_item = modded_copy( mod, obj.flag ); temp_item.update_clothing_mod_val(); - bool enab = can_add_mod( obj.flag, obj.item_string ); + + bool enab = false; std::string prompt; if( mod.item_tags.count( obj.flag ) == 0 ) { - prompt = string_format( "%s (%d %s)", obj.implement_prompt, items_needed, - item::nname( obj.item_string, items_needed ) ); + // Mod not already present, check if modification is possible + if( it.charges < thread_needed ) { + prompt = string_format( "Can't %s (need %d thread loaded)", obj.implement_prompt, thread_needed ); + prompt[6] = std::tolower( prompt[6] ); + } else if( !has_enough[obj.item_string] ) { + prompt = string_format( "Can't %s (need %d %s)", obj.implement_prompt, items_needed, + item::nname( obj.item_string, items_needed ) ); + prompt[6] = std::tolower( prompt[6] ); + } else if( obj.restricted && + std::find( valid_mods.begin(), valid_mods.end(), obj.flag ) == valid_mods.end() ) { + prompt = string_format( "Can't %s (incompatible with %s)", + obj.implement_prompt, mod.tname( 1, false ) ); + prompt[6] = std::tolower( prompt[6] ); + } else { + // Modification is possible + enab = true; + prompt = string_format( "%s (%d %s and %d thread)", obj.implement_prompt, items_needed, + item::nname( obj.item_string, items_needed ), thread_needed ); + prompt[0] = std::toupper( prompt[0] ); + } + } else { + // Mod already present, give option to destroy + enab = true; prompt = obj.destroy_prompt; } std::ostringstream desc; From c0e841b78e8509490b49912e98e980bac80b3e5b Mon Sep 17 00:00:00 2001 From: Davi Date: Mon, 14 Oct 2019 07:00:41 -0400 Subject: [PATCH 2/3] Apply suggestions from Qrox's review --- src/iuse_actor.cpp | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/src/iuse_actor.cpp b/src/iuse_actor.cpp index 58ad399cf0bf0..51def0eff7e90 100644 --- a/src/iuse_actor.cpp +++ b/src/iuse_actor.cpp @@ -4558,21 +4558,26 @@ int sew_advanced_actor::use( player &p, item &it, bool, const tripoint & ) const if( mod.item_tags.count( obj.flag ) == 0 ) { // Mod not already present, check if modification is possible if( it.charges < thread_needed ) { - prompt = string_format( "Can't %s (need %d thread loaded)", obj.implement_prompt, thread_needed ); + //~ %1$s: modification desc, %2$d: number of thread needed + prompt = string_format( _( "Can't %1$s (need %2$d thread loaded)" ), + obj.implement_prompt, thread_needed ); prompt[6] = std::tolower( prompt[6] ); } else if( !has_enough[obj.item_string] ) { - prompt = string_format( "Can't %s (need %d %s)", obj.implement_prompt, items_needed, + //~ %1$s: modification desc, %2$d: number of items needed, %3$s: items needed + prompt = string_format( _( "Can't %1$s (need %2$d %3$s)" ), obj.implement_prompt, items_needed, item::nname( obj.item_string, items_needed ) ); prompt[6] = std::tolower( prompt[6] ); } else if( obj.restricted && std::find( valid_mods.begin(), valid_mods.end(), obj.flag ) == valid_mods.end() ) { - prompt = string_format( "Can't %s (incompatible with %s)", + //~ %1$s: modification desc, %2$s: mod name + prompt = string_format( _( "Can't %1$s (incompatible with %2$s)" ), obj.implement_prompt, mod.tname( 1, false ) ); prompt[6] = std::tolower( prompt[6] ); } else { // Modification is possible enab = true; - prompt = string_format( "%s (%d %s and %d thread)", obj.implement_prompt, items_needed, + //~ %1$s: modification desc, %2$d: number of items needed, %3$s: items needed, %4$s: number of thread needed + prompt = string_format( _( "%1$s (%2$d %3$s and %4$d thread)" ), obj.implement_prompt, items_needed, item::nname( obj.item_string, items_needed ), thread_needed ); prompt[0] = std::toupper( prompt[0] ); } @@ -4580,7 +4585,7 @@ int sew_advanced_actor::use( player &p, item &it, bool, const tripoint & ) const } else { // Mod already present, give option to destroy enab = true; - prompt = obj.destroy_prompt; + prompt = _( obj.destroy_prompt ); } std::ostringstream desc; desc << format_desc_string( _( "Bash" ), mod.bash_resist(), temp_item.bash_resist(), true ); @@ -4596,7 +4601,7 @@ int sew_advanced_actor::use( player &p, item &it, bool, const tripoint & ) const format_volume( before ), volume_units_abbr(), format_volume( after ), volume_units_abbr() ), get_volume_compare_color( before, after, true ) ); - tmenu.addentry_desc( index++, enab, MENU_AUTOASSIGN, _( prompt.c_str() ), desc.str() ); + tmenu.addentry_desc( index++, enab, MENU_AUTOASSIGN, prompt.c_str(), desc.str() ); } tmenu.textwidth = 80; tmenu.desc_enabled = true; From 2139353303b540028d31c4735120e0bd9d89f25c Mon Sep 17 00:00:00 2001 From: DaviBones Date: Mon, 14 Oct 2019 09:09:17 -0400 Subject: [PATCH 3/3] remove c_str() call MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Jianxiang Wang (王健翔) --- src/iuse_actor.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/iuse_actor.cpp b/src/iuse_actor.cpp index 51def0eff7e90..cc39ab0e4362c 100644 --- a/src/iuse_actor.cpp +++ b/src/iuse_actor.cpp @@ -4601,7 +4601,7 @@ int sew_advanced_actor::use( player &p, item &it, bool, const tripoint & ) const format_volume( before ), volume_units_abbr(), format_volume( after ), volume_units_abbr() ), get_volume_compare_color( before, after, true ) ); - tmenu.addentry_desc( index++, enab, MENU_AUTOASSIGN, prompt.c_str(), desc.str() ); + tmenu.addentry_desc( index++, enab, MENU_AUTOASSIGN, prompt, desc.str() ); } tmenu.textwidth = 80; tmenu.desc_enabled = true;