Skip to content

Commit

Permalink
Merge pull request #78904 from moxian/craft-variant-name
Browse files Browse the repository at this point in the history
Properly show variant's prefix/suffix in the crafting menu
  • Loading branch information
Maleclypse authored Jan 4, 2025
2 parents c36d390 + 046dca5 commit 005a9fe
Show file tree
Hide file tree
Showing 6 changed files with 82 additions and 25 deletions.
25 changes: 25 additions & 0 deletions data/mods/TEST_DATA/items.json
Original file line number Diff line number Diff line change
Expand Up @@ -1589,6 +1589,31 @@
}
]
},
{
"id": "test_xl_waist_apron_long",
"type": "GENERIC",
"name": { "str": "long waist apron" },
"copy-from": "test_waist_apron_long",
"extend": { "flags": [ "OVERSIZE", "PREFIX_XL" ] },
"//": "the variants are not copy-from'd at the time of writing so let's make some",
"variants": [
{
"id": "generic_apron_cotton",
"name": { "str": "long waist apron" },
"description": "It's colored white, like the ones commonly used by chefs, professional or otherwise.",
"weight": 50,
"append": true
},
{
"id": "pink_apron_cotton",
"name": { "str": "pink long waist apron" },
"description": "It's colored neon pink, commonly used by women or men who like pink.",
"color": "pink",
"weight": 3,
"append": true
}
]
},
{
"id": "test_umbrella",
"type": "GENERIC",
Expand Down
6 changes: 6 additions & 0 deletions data/mods/TEST_DATA/recipes.json
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,12 @@
"autolearn": true,
"using": [ [ "tailoring_cotton_patchwork", 6 ] ]
},
{
"//": "Variant version with XL prefix",
"result": "test_xl_waist_apron_long",
"type": "recipe",
"copy-from": "test_waist_apron_long_pink_apron_cotton"
},
{
"result": "test_200_kcal",
"type": "recipe",
Expand Down
14 changes: 12 additions & 2 deletions src/item_tname.h
Original file line number Diff line number Diff line change
Expand Up @@ -111,16 +111,26 @@ constexpr uint64_t tname_conditional_bits = // TODO: fine grain?
1ULL << static_cast<size_t>( tname::segments::COMPONENTS ) |
1ULL << static_cast<size_t>( tname::segments::TAGS ) |
1ULL << static_cast<size_t>( tname::segments::VARS );
constexpr uint64_t item_name_bits = // item prefix + item name + item suffix
constexpr uint64_t base_item_name_bits =
1ULL << static_cast<size_t>( tname::segments::CUSTOM_ITEM_PREFIX ) |
1ULL << static_cast<size_t>( tname::segments::TYPE ) |
1ULL << static_cast<size_t>( tname::segments::CUSTOM_ITEM_SUFFIX );
constexpr uint64_t variant_bits =
1ULL << static_cast<size_t>( tname::segments::VARIANT );
constexpr segment_bitset default_tname( default_tname_bits );
constexpr segment_bitset unprefixed_tname( default_tname_bits & ~tname_prefix_bits );
constexpr segment_bitset tname_sort_key( default_tname_bits & ~tname_unsortable_bits );
constexpr segment_bitset tname_contents( tname_contents_bits );
constexpr segment_bitset tname_conditional( tname_conditional_bits );
constexpr segment_bitset item_name( item_name_bits );
// Name for an abstract base item of a given class, not any specific one.
// Often used in crafting UI and similar.
// For example in the sentence "To make some 'XL socks' I will need to cut up a 'blanket'",
// when we don't care which color (read: variant) 'XL socks' we want or 'blanket' we have.
constexpr segment_bitset base_item_name( base_item_name_bits );
// Name of a specific item in the game world, carries the item identity, and will not
// change in a normal playthrough except through exordinary means (e.g. via `iuse_transform`).
// E.g. "XL green socks" (notably, not "|. XL green socks (filthy)")
constexpr segment_bitset item_identity_name( base_item_name_bits | variant_bits );

} // namespace tname

Expand Down
22 changes: 13 additions & 9 deletions src/recipe.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1218,17 +1218,21 @@ std::string recipe::result_name( const bool decorated ) const
{
std::string name;
if( !name_.empty() ) {
// if the recipe has an explicit name (such as for proficiency training) - use that
name = name_.translated();
} else if( !variant().empty() ) {
auto iter_var = std::find_if( result_->variants.begin(), result_->variants.end(),
[this]( const itype_variant_data & itvar ) {
return itvar.id == variant();
} );
if( iter_var != result_->variants.end() ) {
name = iter_var->alt_name.translated();
}
} else {
name = item::tname( result_, 1, tname::item_name );
// Names are tricky, so we have to create a temporary fake result item to get one.
// As of 2025-01-01 there's no better way around this.
item temp_item( result_ );
// Use generic item name by default.
tname::segment_bitset segs = tname::base_item_name;
if( !variant().empty() ) {
// ..but if the recipe calls for a specific varaint - then use that variant.
// Note that `temp_item` is likely to already have a random variant set at the time of creation.
temp_item.set_itype_variant( variant() );
segs = tname::item_identity_name;
}
name = temp_item.tname( 1, segs );
}
if( decorated &&
uistate.favorite_recipes.find( this->ident() ) != uistate.favorite_recipes.end() ) {
Expand Down
16 changes: 8 additions & 8 deletions src/requirements.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -158,9 +158,9 @@ std::string tool_comp::to_string( const int batch, const int ) const
//~ %1$s: tool name, %2$d: charge requirement
return string_format( npgettext( "requirement", "%1$s (%2$d charge)", "%1$s (%2$d charges)",
charge_total ),
item::tname( type, 1, tname::item_name ), charge_total );
item::tname( type, 1, tname::base_item_name ), charge_total );
} else {
return item::tname( type, std::abs( count ), tname::item_name );
return item::tname( type, std::abs( count ), tname::base_item_name );
}
}

Expand All @@ -176,33 +176,33 @@ std::string item_comp::to_string( const int batch, const int avail ) const
return string_format( npgettext( "requirement", "%2$d %1$s (have infinite)",
"%2$d %1$s (have infinite)",
c ),
item_temp.tname( 1, tname::item_name ), c );
item_temp.tname( 1, tname::base_item_name ), c );
} else if( avail > 0 ) {
//~ %1$s: item name, %2$d: charge requirement, %3%d: available charges
return string_format( npgettext( "requirement", "%2$d %1$s (have %3$d)",
"%2$d %1$s (have %3$d)", c ),
item_temp.tname( 1, tname::item_name ), c, avail );
item_temp.tname( 1, tname::base_item_name ), c, avail );
} else {
//~ %1$s: item name, %2$d: charge requirement
return string_format( npgettext( "requirement", "%2$d %1$s", "%2$d %1$s", c ),
item_temp.tname( 1, tname::item_name ), c );
item_temp.tname( 1, tname::base_item_name ), c );
}
} else {
if( avail == item::INFINITE_CHARGES ) {
//~ %1$s: item name, %2$d: required count
return string_format( npgettext( "requirement", "%2$d %1$s (have infinite)",
"%2$d %1$s (have infinite)",
c ),
item_temp.tname( c, tname::item_name ), c );
item_temp.tname( c, tname::base_item_name ), c );
} else if( avail > 0 ) {
//~ %1$s: item name, %2$d: required count, %3%d: available count
return string_format( npgettext( "requirement", "%2$d %1$s (have %3$d)",
"%2$d %1$s (have %3$d)", c ),
item_temp.tname( c, tname::item_name ), c, avail );
item_temp.tname( c, tname::base_item_name ), c, avail );
} else {
//~ %1$s: item name, %2$d: required count
return string_format( npgettext( "requirement", "%2$d %1$s", "%2$d %1$s", c ),
item_temp.tname( c, tname::item_name ), c );
item_temp.tname( c, tname::base_item_name ), c );
}
}
}
Expand Down
24 changes: 18 additions & 6 deletions tests/crafting_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,8 @@ static const recipe_id recipe_test_tallow2( "test_tallow2" );
static const recipe_id recipe_test_waist_apron_long( "test_waist_apron_long" );
static const recipe_id
recipe_test_waist_apron_long_pink_apron_cotton( "test_waist_apron_long_pink_apron_cotton" );
static const recipe_id
recipe_test_xl_waist_apron_long_pink_apron_cotton( "test_xl_waist_apron_long_pink_apron_cotton" );
static const recipe_id recipe_vambrace_larmor( "vambrace_larmor" );
static const recipe_id recipe_water_clean( "water_clean" );

Expand Down Expand Up @@ -2247,11 +2249,12 @@ TEST_CASE( "variant_crafting_recipes", "[crafting][slow]" )
tools.emplace_back( "scissors" );
tools.insert( tools.end(), 10, item( "sheet_cotton" ) );
tools.insert( tools.end(), 10, item( "thread" ) );
prep_craft( recipe_test_waist_apron_long, tools, true );
actually_test_craft( recipe_test_waist_apron_long, INT_MAX, 10 );
const recipe_id apron_recipe = recipe_test_waist_apron_long;
prep_craft( apron_recipe, tools, true );
actually_test_craft( apron_recipe, INT_MAX, 10 );
item_location apron = player_character.get_wielded_item();

REQUIRE( apron->type->get_id() == recipe_test_waist_apron_long->result() );
REQUIRE( apron->type->get_id() == apron_recipe->result() );
REQUIRE( apron->has_itype_variant() );

if( variant_counts.count( apron->itype_variant().id ) == 0 ) {
Expand All @@ -2275,11 +2278,12 @@ TEST_CASE( "variant_crafting_recipes", "[crafting][slow]" )
tools.emplace_back( "scissors" );
tools.insert( tools.end(), 10, item( "sheet_cotton" ) );
tools.insert( tools.end(), 10, item( "thread" ) );
prep_craft( recipe_test_waist_apron_long_pink_apron_cotton, tools, true );
actually_test_craft( recipe_test_waist_apron_long_pink_apron_cotton, INT_MAX, 10 );
const recipe_id apron_recipe = recipe_test_xl_waist_apron_long_pink_apron_cotton;
prep_craft( apron_recipe, tools, true );
actually_test_craft( apron_recipe, INT_MAX, 10 );
item_location apron = player_character.get_wielded_item();

REQUIRE( apron->type->get_id() == recipe_test_waist_apron_long_pink_apron_cotton->result() );
REQUIRE( apron->type->get_id() == apron_recipe->result() );
REQUIRE( apron->has_itype_variant() );

if( apron->itype_variant().id == "pink_apron_cotton" ) {
Expand All @@ -2288,6 +2292,14 @@ TEST_CASE( "variant_crafting_recipes", "[crafting][slow]" )
}
CHECK( specific_variant_count == max_iters );
}
SECTION( "recipe names" ) {
const recipe_id basic_recipe = recipe_test_waist_apron_long;
CHECK( basic_recipe.obj().result_name() == "long waist apron" );
const recipe_id variant_recipe = recipe_test_waist_apron_long_pink_apron_cotton;
CHECK( variant_recipe.obj().result_name() == "pink long waist apron" );
const recipe_id variant_prefix_recipe = recipe_test_xl_waist_apron_long_pink_apron_cotton;
CHECK( variant_prefix_recipe.obj().result_name() == "XL pink long waist apron" );
}
}

TEST_CASE( "pseudo_tools_in_crafting_inventory", "[crafting][tools]" )
Expand Down

0 comments on commit 005a9fe

Please sign in to comment.