Skip to content

Commit

Permalink
Adds a deconstruct query with item list (#74440)
Browse files Browse the repository at this point in the history
* Add deconstruction query

Can be toggled with an option

* Ugly temporary count handling

* Embarrassing typo

* Add every_item_min_max() 🎉 and remove ugly temp handling

* Add periods so Sadenar doesn't get angey

* Remember charges.first and charges.second can be individually set

* Appease our clang overlords
  • Loading branch information
Procyonae authored Jun 13, 2024
1 parent 2a081fb commit cb44970
Show file tree
Hide file tree
Showing 7 changed files with 137 additions and 10 deletions.
6 changes: 4 additions & 2 deletions data/json/construction/misc.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,25 @@
"type": "construction",
"id": "constr_deconstruct",
"group": "deconstruct_furniture",
"category": "FURN",
"category": "OTHER",
"required_skills": [ [ "fabrication", 0 ] ],
"time": "20 m",
"qualities": [ [ { "id": "PRY", "level": 1 } ], [ { "id": "SCREW", "level": 1 } ] ],
"pre_special": "check_deconstruct",
"do_turn_special": "do_turn_deconstruct",
"post_flags": [ "keep_items" ],
"post_special": "done_deconstruct"
},
{
"type": "construction",
"id": "constr_deconstruct_simple",
"group": "deconstruct_simple_furniture",
"category": "FURN",
"category": "OTHER",
"required_skills": [ [ "fabrication", 0 ] ],
"time": "10 s",
"pre_note": "Certain terrain and furniture can be deconstructed without any tools.",
"pre_flags": "EASY_DECONSTRUCT",
"do_turn_special": "do_turn_deconstruct",
"post_flags": [ "keep_items" ],
"post_special": "done_deconstruct"
},
Expand Down
4 changes: 2 additions & 2 deletions data/json/construction_group.json
Original file line number Diff line number Diff line change
Expand Up @@ -957,12 +957,12 @@
{
"type": "construction_group",
"id": "deconstruct_furniture",
"name": "Deconstruct Furniture"
"name": "Deconstruct Terrain/Furniture"
},
{
"type": "construction_group",
"id": "deconstruct_simple_furniture",
"name": "Deconstruct Simple Furniture"
"name": "Deconstruct Simple Terrain/Furniture"
},
{
"type": "construction_group",
Expand Down
6 changes: 2 additions & 4 deletions src/activity_handlers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3413,12 +3413,10 @@ void activity_handlers::build_do_turn( player_activity *act, Character *you )
// Current progress in moves
const double current_progress = old_counter * base_total_moves / 10000000.0 +
delta_progress;
// Current progress as a percent of base_total_moves to 2 decimal places
pc->counter = std::round( current_progress / base_total_moves * 10000000.0 );

you->set_moves( 0 );

pc->id->do_turn_special( here.bub_from_abs( act->placement ), *you );
// Current progress as a percent of base_total_moves to 2 decimal places
pc->counter = std::round( current_progress / base_total_moves * 10000000.0 );
pc->counter = std::min( pc->counter, 10000000 );
// If construction_progress has reached 100% or more
if( pc->counter >= 10000000 ) {
Expand Down
62 changes: 62 additions & 0 deletions src/construction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,7 @@ static void add_matching_down_above( const tripoint_bub_ms &p, Character & );
static void remove_above( const tripoint_bub_ms &p, Character & );
static void add_roof( const tripoint_bub_ms &p, Character & );

static void do_turn_deconstruct( const tripoint_bub_ms &, Character & );
static void do_turn_shovel( const tripoint_bub_ms &, Character & );
static void do_turn_exhume( const tripoint_bub_ms &, Character & );

Expand Down Expand Up @@ -2056,6 +2057,66 @@ void construct::add_roof( const tripoint_bub_ms &p, Character &/*who*/ )
here.ter_set( p + tripoint_above, roof );
}

void construct::do_turn_deconstruct( const tripoint_bub_ms &p, Character &who )
{
map &here = get_map();
// Only run once at the start of construction
if( here.partial_con_at( p )->counter == 0 && who.is_avatar() &&
get_option<bool>( "QUERY_DECONSTRUCT" ) ) {
bool cancel_construction = false;

auto deconstruct_items = []( const item_group_id & drop_group ) {
std::string ret;
const std::map<const itype *, std::pair<int, int>> deconstruct_items =
item_group::spawn_data_from_group( drop_group )->every_item_min_max();
for( const auto &deconstruct_item : deconstruct_items ) {
const int &min = deconstruct_item.second.first;
const int &max = deconstruct_item.second.second;
if( min != max ) {
ret += string_format( "- %d-%d %s\n", min, max, deconstruct_item.first->nname( max ) );
} else {
ret += string_format( "- %d %s\n", max, deconstruct_item.first->nname( max ) );
}
}
return ret;
};
auto deconstruction_will_practice_skill = [ &who ]( auto & skill ) {
return who.get_skill_level( skill.id ) >= skill.min &&
who.get_skill_level( skill.id ) < skill.max;
};

if( here.has_furn( p ) ) {
const furn_t &f = here.furn( p ).obj();
if( !!f.deconstruct.skill &&
deconstruction_will_practice_skill( *f.deconstruct.skill ) ) {
cancel_construction = !who.query_yn(
_( "Deconstructing the %s will yield:\n%s\nYou feel you might also learn something about %s.\nReally deconstruct?" ),
f.name(), deconstruct_items( f.deconstruct.drop_group ), f.deconstruct.skill->id.obj().name() );
} else {
cancel_construction = !who.query_yn(
_( "Deconstructing the %s will yield:\n%s\nReally deconstruct?" ),
f.name(), deconstruct_items( f.deconstruct.drop_group ) );
}
} else {
const ter_t &t = here.ter( p ).obj();
if( !!t.deconstruct.skill &&
deconstruction_will_practice_skill( *t.deconstruct.skill ) ) {
cancel_construction = !who.query_yn(
_( "Deconstructing the %s will yield:\n%s\nYou feel you might also learn something about %s.\nReally deconstruct?" ),
t.name(), deconstruct_items( t.deconstruct.drop_group ), t.deconstruct.skill->id.obj().name() );
} else {
cancel_construction = !who.query_yn(
_( "Deconstructing the %s will yield:\n%s\nReally deconstruct?" ),
t.name(), deconstruct_items( t.deconstruct.drop_group ) );
}
}
if( cancel_construction ) {
here.partial_con_remove( p );
who.cancel_activity();
}
}
}

void construct::do_turn_shovel( const tripoint_bub_ms &p, Character &who )
{
// TODO: fix point types
Expand Down Expand Up @@ -2272,6 +2333,7 @@ void load_construction( const JsonObject &jo )
static const std::map<std::string, void( * )( const tripoint_bub_ms &, Character & )>
do_turn_special_map = {{
{ "", construct::done_nothing },
{ "do_turn_deconstruct", construct::do_turn_deconstruct },
{ "do_turn_shovel", construct::do_turn_shovel },
{ "do_turn_exhume", construct::do_turn_exhume },
}
Expand Down
53 changes: 53 additions & 0 deletions src/item_group.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -488,6 +488,36 @@ std::set<const itype *> Single_item_creator::every_item() const
return {};
}

std::map<const itype *, std::pair<int, int>> Single_item_creator::every_item_min_max() const
{
switch( type ) {
case S_ITEM: {
const itype *i = item::find_type( itype_id( id ) );
if( i->count_by_charges() ) {
// TODO: Not technically perfect for only charge_min/charge max and for ammo/liquids but Item_modifier::modify()'s logic is gross and I'd rather not try to replicate it perfectly as is
const int min_charges = modifier->charges.first == -1 ?
i->charges_default() : modifier->charges.first;
const int max_charges = modifier->charges.second == -1 ?
i->charges_default() : modifier->charges.second;
return { std::make_pair( i, std::make_pair( modifier->count.first * min_charges, modifier->count.second * max_charges ) ) };
}
return { std::make_pair( i, modifier->count ) };
}
case S_ITEM_GROUP: {
Item_spawn_data *isd = item_controller->get_group( item_group_id( id ) );
if( isd != nullptr ) {
return isd->every_item_min_max();
}
return {};
}
case S_NONE:
return {};
}
// NOLINTNEXTLINE(misc-static-assert,cert-dcl03-c)
cata_fatal( "Unexpected type" );
return {};
}

void Single_item_creator::inherit_ammo_mag_chances( const int ammo, const int mag )
{
if( ammo != 0 || mag != 0 ) {
Expand Down Expand Up @@ -976,6 +1006,24 @@ std::set<const itype *> Item_group::every_item() const
return result;
}

std::map<const itype *, std::pair<int, int>> Item_group::every_item_min_max() const
{
std::map<const itype *, std::pair<int, int>> result;
for( const auto &spawn_data : items ) {
std::map<const itype *, std::pair<int, int>> item_entries = spawn_data->every_item_min_max();
for( const auto &item_entry : item_entries ) {
const auto existing_item_entry = result.find( item_entry.first );
if( existing_item_entry != result.end() ) {
existing_item_entry->second.first += item_entry.second.first;
existing_item_entry->second.second += item_entry.second.second;
} else {
result.insert( item_entry );
}
}
}
return result;
}

item_group::ItemList item_group::items_from( const item_group_id &group_id,
const time_point &birthday, spawn_flags flags )
{
Expand Down Expand Up @@ -1013,6 +1061,11 @@ bool item_group::group_is_defined( const item_group_id &group_id )
return item_controller->get_group( group_id ) != nullptr;
}

Item_spawn_data *item_group::spawn_data_from_group( const item_group_id &group_id )
{
return item_controller->get_group( group_id );
}

bool item_group::group_contains_item( const item_group_id &group_id, const itype_id &type_id )
{
const Item_spawn_data *group = item_controller->get_group( group_id );
Expand Down
7 changes: 7 additions & 0 deletions src/item_group.h
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,10 @@ std::set<const itype *> every_possible_item_from( const item_group_id &group_id
* refers to a non-existing group is broken), or just alert the player.
*/
bool group_is_defined( const item_group_id &group_id );
/**
* Return the corresponding Item_spawn_data for an item_group_id as .obj() is undefined
*/
Item_spawn_data *spawn_data_from_group( const item_group_id &group_id );
/**
* Shows an menu to debug the item groups.
*/
Expand Down Expand Up @@ -176,6 +180,7 @@ class Item_spawn_data
virtual bool has_item( const itype_id &itemid ) const = 0;

virtual std::set<const itype *> every_item() const = 0;
virtual std::map<const itype *, std::pair<int, int>> every_item_min_max() const = 0;

const std::string &context() const {
return context_;
Expand Down Expand Up @@ -343,6 +348,7 @@ class Single_item_creator : public Item_spawn_data

bool has_item( const itype_id &itemid ) const override;
std::set<const itype *> every_item() const override;
std::map<const itype *, std::pair<int, int>> every_item_min_max() const override;
};

/**
Expand Down Expand Up @@ -391,6 +397,7 @@ class Item_group : public Item_spawn_data
void replace_items( const std::unordered_map<itype_id, itype_id> &replacements ) override;
bool has_item( const itype_id &itemid ) const override;
std::set<const itype *> every_item() const override;
std::map<const itype *, std::pair<int, int>> every_item_min_max() const override;

/**
* These aren't directly used. Instead, the values (both with a default value of 0) "trickle down"
Expand Down
9 changes: 7 additions & 2 deletions src/options.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1907,8 +1907,13 @@ void options_manager::add_options_interface()
true
);

add( "QUERY_DISASSEMBLE", page_id, to_translation( "Query on disassembly while butchering" ),
to_translation( "If true, will query before disassembling items while butchering." ),
add( "QUERY_DISASSEMBLE", page_id, to_translation( "Query on item disassembly" ),
to_translation( "If true, will query before disassembling items." ),
true
);

add( "QUERY_DECONSTRUCT", page_id, to_translation( "Query on terrain/furniture deconstruction" ),
to_translation( "If true, will query before deconstructing terrain/furniture." ),
true
);

Expand Down

0 comments on commit cb44970

Please sign in to comment.