Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement requirement for manual install of CBMs #40360

Merged
merged 7 commits into from
May 12, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 0 additions & 7 deletions data/json/game_balance.json
Original file line number Diff line number Diff line change
Expand Up @@ -76,13 +76,6 @@
"stype": "bool",
"value": false
},
{
"type": "EXTERNAL_OPTION",
"name": "MANUAL_BIONIC_INSTALLATION",
"info": "Permits manual self-installation of bionics.",
"stype": "bool",
"value": false
},
{
"type": "EXTERNAL_OPTION",
"name": "NO_NPC_FOOD",
Expand Down
8 changes: 0 additions & 8 deletions data/mods/ManualBionicInstall/game_balance.json

This file was deleted.

13 changes: 0 additions & 13 deletions data/mods/ManualBionicInstall/modinfo.json

This file was deleted.

2 changes: 2 additions & 0 deletions doc/JSON_INFO.md
Original file line number Diff line number Diff line change
Expand Up @@ -499,6 +499,7 @@ This section describes each json file and their contents. Each json has their ow
| stat_bonus | (_optional_) List of passive stat bonus. Stat are designated as follow: "DEX", "INT", "STR", "PER".
| enchantments | (_optional_) List of enchantments applied by this CBM (see MAGIC.md for instructions on enchantment. NB: enchantments are not necessarily magic.)
| learned_spells | (_optional_) Map of {spell:level} you gain when installing this CBM, and lose when you uninstall this CBM. Spell classes are automatically gained.
| installation_requirement | (_optional_) Requirment id pointing to a requirment defining the tools and componentsnt necessary to install this CBM.

```C++
{
Expand All @@ -514,6 +515,7 @@ This section describes each json file and their contents. Each json has their ow
"encumbrance" : [ [ "TORSO", 10 ], [ "ARM_L", 10 ], [ "ARM_R", 10 ], [ "LEG_L", 10 ], [ "LEG_R", 10 ], [ "FOOT_L", 10 ], [ "FOOT_R", 10 ] ],
"description" : "You have a battery draining attachment, and thus can make use of the energy contained in normal, everyday batteries. Use 'E' to consume batteries.",
"canceled_mutations": ["HYPEROPIC"],
"installation_requirement": "sewing_standard",
"included_bionics": ["bio_blindfold"]
},
{
Expand Down
87 changes: 81 additions & 6 deletions src/bionics.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,8 @@ void bionic_data::load( const JsonObject &jsobj, const std::string )

optional( jsobj, was_loaded, "available_upgrades", available_upgrades );

optional( jsobj, was_loaded, "installation_requirement", installation_requirement );

if( jsobj.has_array( "stat_bonus" ) ) {
// clear data first so that copy-from can override it
stat_bonus.clear();
Expand Down Expand Up @@ -359,6 +361,10 @@ void bionic_data::check_bionic_consistency()
{
for( const bionic_data &bio : get_all() ) {

if( !bio.installation_requirement.is_empty() && !bio.installation_requirement.is_valid() ) {
debugmsg( "Bionic %s uses undefined requirement_id %s", bio.id.c_str(),
bio.installation_requirement.c_str() );
}
if( bio.has_flag( flag_BIO_GUN ) && bio.has_flag( flag_BIO_WEAPON ) ) {
debugmsg( "Bionic %s specified as both gun and weapon bionic", bio.id.c_str() );
}
Expand Down Expand Up @@ -1868,25 +1874,88 @@ void Character::bionics_uninstall_failure( monster &installer, player &patient,
}
}

bool Character::has_enough_anesth( const itype *cbm, player &patient )
bool Character::has_enough_anesth( const itype &cbm, player &patient )
{
if( !cbm->bionic ) {
debugmsg( "has_enough_anesth( const itype *cbm ): %s is not a bionic", cbm->get_id() );
if( !cbm.bionic ) {
debugmsg( "has_enough_anesth( const itype *cbm ): %s is not a bionic", cbm.get_id() );
return false;
}

if( has_bionic( bio_painkiller ) || has_trait( trait_NOPAIN ) ||
if( patient.has_bionic( bio_painkiller ) || patient.has_trait( trait_NOPAIN ) ||
has_trait( trait_DEBUG_BIONICS ) ) {
return true;
}

const int weight = units::to_kilogram( patient.bodyweight() ) / 10;
const requirement_data req_anesth = *requirement_id( "anesthetic" ) *
cbm->bionic->difficulty * 2 * weight;
cbm.bionic->difficulty * 2 * weight;

return req_anesth.can_make_with_inventory( crafting_inventory(), is_crafting_component );
}

bool Character::has_enough_anesth( const itype &cbm )
{
if( has_bionic( bio_painkiller ) || has_trait( trait_NOPAIN ) ||
has_trait( trait_DEBUG_BIONICS ) ) {
return true;
}
const int weight = units::to_kilogram( bodyweight() ) / 10;
const requirement_data req_anesth = *requirement_id( "anesthetic" ) *
cbm.bionic->difficulty * 2 * weight;
if( !req_anesth.can_make_with_inventory( crafting_inventory(),
is_crafting_component ) ) {
std::string buffer = _( "You don't have enough anesthetic to perform the installation." );
buffer += "\n";
buffer += req_anesth.list_missing();
popup( buffer, PF_NONE );
return false;
}
return true;
}

void Character::consume_anesth_requirment( const itype &cbm, player &patient )
{
const int weight = units::to_kilogram( patient.bodyweight() ) / 10;
const requirement_data req_anesth = *requirement_id( "anesthetic" ) *
cbm.bionic->difficulty * 2 * weight;
for( const auto &e : req_anesth.get_components() ) {
as_player()->consume_items( e, 1, is_crafting_component );
}
for( const auto &e : req_anesth.get_tools() ) {
as_player()->consume_tools( e );
}
invalidate_crafting_inventory();
}

bool Character::has_installation_requirment( bionic_id bid )
{
if( bid->installation_requirement.is_empty() ) {
return false;
}

if( !bid->installation_requirement->can_make_with_inventory( crafting_inventory(),
is_crafting_component ) ) {
std::string buffer = _( "You don't have the required components to perform the installation." );
buffer += "\n";
buffer += bid->installation_requirement->list_missing();
popup( buffer, PF_NONE );
return false;
}

return true;
}

void Character::consume_installation_requirment( bionic_id bid )
{
for( const auto &e : bid->installation_requirement->get_components() ) {
as_player()->consume_items( e, 1, is_crafting_component );
}
for( const auto &e : bid->installation_requirement->get_tools() ) {
as_player()->consume_tools( e );
}
invalidate_crafting_inventory();
}

// bionic manipulation adjusted skill
float Character::bionics_adjusted_skill( const skill_id &most_important_skill,
const skill_id &important_skill,
Expand Down Expand Up @@ -2216,7 +2285,7 @@ bool Character::uninstall_bionic( const bionic &target_cbm, monster &installer,
return false;
}

bool Character::can_install_bionics( const itype &type, player &installer, bool autodoc,
bool Character::can_install_bionics( const itype &type, Character &installer, bool autodoc,
int skill_level )
{
if( !type.bionic ) {
Expand All @@ -2231,6 +2300,12 @@ bool Character::can_install_bionics( const itype &type, player &installer, bool
const int difficult = type.bionic->difficulty;
float adjusted_skill;

// if we're doing self install
if( !autodoc && installer.is_avatar() ) {
return installer.has_enough_anesth( type ) &&
installer.has_installation_requirment( bioid );
}

if( autodoc ) {
adjusted_skill = installer.bionics_adjusted_skill( skill_firstaid,
skill_computer,
Expand Down
3 changes: 3 additions & 0 deletions src/bionics.h
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,9 @@ struct bionic_data {
*/
std::set<bionic_id> available_upgrades;

/**Requirement to bionic installation*/
requirement_id installation_requirement;

cata::flat_set<std::string> flags;
bool has_flag( const std::string &flag ) const;

Expand Down
9 changes: 7 additions & 2 deletions src/character.h
Original file line number Diff line number Diff line change
Expand Up @@ -1047,7 +1047,12 @@ class Character : public Creature, public visitable<Character>
int get_free_bionics_slots( body_part bp ) const;

/**Has enough anesthetic for surgery*/
bool has_enough_anesth( const itype *cbm, player &patient );
bool has_enough_anesth( const itype &cbm, player &patient );
bool has_enough_anesth( const itype &cbm );
void consume_anesth_requirment( const itype &cbm, player &patient );
/**Has the required equipement for manual installation*/
bool has_installation_requirment( bionic_id bid );
void consume_installation_requirment( bionic_id bid );
/** Handles process of introducing patient into anesthesia during Autodoc operations. Requires anesthesia kits or NOPAIN mutation */
void introduce_into_anesthesia( const time_duration &duration, player &installer,
bool needs_anesthesia );
Expand All @@ -1068,7 +1073,7 @@ class Character : public Creature, public visitable<Character>
const skill_id &least_important_skill,
int skill_level = -1 );
/**Is the installation possible*/
bool can_install_bionics( const itype &type, player &installer, bool autodoc = false,
bool can_install_bionics( const itype &type, Character &installer, bool autodoc = false,
int skill_level = -1 );
std::map<body_part, int> bionic_installation_issues( const bionic_id &bioid );
/** Initialize all the values needed to start the operation player_activity */
Expand Down
4 changes: 2 additions & 2 deletions src/game_inventory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1683,7 +1683,7 @@ class bionic_install_preset: public inventory_selector_preset
return _( "CBM not compatible with patient" );
} else if( units::energy_max - pa.get_max_power_level() < bid->capacity ) {
return _( "Max power capacity already reached" );
} else if( !p.has_enough_anesth( itemtype, pa ) ) {
} else if( !p.has_enough_anesth( *itemtype, pa ) ) {
const int weight = units::to_kilogram( pa.bodyweight() ) / 10;
const int duration = loc.get_item()->type->bionic->difficulty * 2;
const requirement_data req_anesth = *requirement_id( "anesthetic" ) *
Expand Down Expand Up @@ -1864,7 +1864,7 @@ class bionic_uninstall_preset : public inventory_selector_preset
std::string get_denial( const item_location &loc ) const override {
const itype *itemtype = loc.get_item()->type;

if( !p.has_enough_anesth( itemtype, pa ) ) {
if( !p.has_enough_anesth( *itemtype, pa ) ) {
const int weight = units::to_kilogram( pa.bodyweight() ) / 10;
const int duration = loc.get_item()->type->bionic->difficulty * 2;
const requirement_data req_anesth = *requirement_id( "anesthetic" ) *
Expand Down
11 changes: 6 additions & 5 deletions src/iuse_actor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3967,6 +3967,8 @@ std::unique_ptr<iuse_actor> saw_barrel_actor::clone() const
int install_bionic_actor::use( player &p, item &it, bool, const tripoint & ) const
{
if( p.can_install_bionics( *it.type, p, false ) ) {
p.consume_installation_requirment( it.type->bionic->id );
p.consume_anesth_requirment( *it.type, p );
return p.install_bionics( *it.type, p, false ) ? it.type->charges_to_use() : 0;
} else {
return 0;
Expand All @@ -3983,11 +3985,10 @@ ret_val<bool> install_bionic_actor::can_use( const Character &p, const item &it,
if( p.is_mounted() ) {
return ret_val<bool>::make_failure( _( "You can't install bionics while mounted." ) );
}
if( !get_option<bool>( "MANUAL_BIONIC_INSTALLATION" ) &&
!p.has_trait( trait_DEBUG_BIONICS ) ) {
return ret_val<bool>::make_failure( _( "You can't self-install bionics." ) );
} else if( !p.has_trait( trait_DEBUG_BIONICS ) ) {
if( it.has_flag( "FILTHY" ) ) {
if( !p.has_trait( trait_DEBUG_BIONICS ) ) {
if( bid->installation_requirement.is_empty() ) {
return ret_val<bool>::make_failure( _( "You can't self-install this CBM." ) );
} else if( it.has_flag( "FILTHY" ) ) {
return ret_val<bool>::make_failure( _( "You can't install a filthy CBM!" ) );
} else if( it.has_flag( "NO_STERILE" ) ) {
return ret_val<bool>::make_failure( _( "This CBM is not sterile, you can't install it." ) );
Expand Down