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

Charge bionics from UPS via Cable Charger System CBM + bugfixes for solar backpacks #33546

Merged
merged 11 commits into from
Sep 4, 2019
2 changes: 1 addition & 1 deletion data/json/bionics.json
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@
"type": "bionic",
"name": "Cable Charger System",
"capacity": 10,
"description": "You have a complex port surgically mounted above your hip. While active, it will recharge bionic power when connected to a battery via jumper cable.",
"description": "You have a complex port surgically mounted above your hip. While active, it will recharge bionic power when connected to a power source via jumper cable.",
"occupied_bodyparts": [ [ "TORSO", 10 ] ],
"flags": [ "BIONIC_POWER_SOURCE", "BIONIC_SHOCKPROOF", "BIONIC_TOGGLED" ]
},
Expand Down
8 changes: 5 additions & 3 deletions data/json/items/tools.json
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@
"type": "TOOL",
"name": "UPS",
"name_plural": "UPS's",
"description": "This is a unified power supply, or UPS. It is a device developed jointly by military and scientific interests for use in combat and the field. The UPS is designed to power armor and some guns, but drains batteries quickly.",
"description": "This is a unified power supply, or UPS. It is a device developed jointly by military and scientific interests for use in combat and the field. The UPS is designed to power bionics, armor and some guns, but drains batteries quickly.",
"weight": 680,
"volume": "2500 ml",
"price": 280000,
Expand All @@ -121,7 +121,8 @@
"magazines": [
[ "battery", [ "heavy_plus_battery_cell", "heavy_battery_cell", "heavy_atomic_battery_cell", "heavy_disposable_cell" ] ]
],
"magazine_well": 4
"magazine_well": 4,
"flags": [ "IS_UPS" ]
},
{
"id": "acidbomb",
Expand Down Expand Up @@ -155,7 +156,8 @@
"symbol": ";",
"color": "light_green",
"ammo": "plutonium",
"max_charges": 2500
"max_charges": 2500,
"flags": [ "IS_UPS" ]
},
{
"id": "advanced_ecig",
Expand Down
4 changes: 2 additions & 2 deletions data/json/items/vehicle/cables.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"type": "TOOL",
"id": "jumper_cable",
"name": "jumper cable",
"description": "A jumper cable, like you've seen many times before: it's a short multi-stranded copper cable with power leads on either end, whose purpose is to share power between vehicles.",
"description": "A jumper cable, like you've seen many times before: it's a short multi-stranded copper cable with power leads on either end, whose main purpose is to share power between vehicles, but can also link other electrical systems.",
"to_hit": 1,
"color": "light_blue",
"symbol": "&",
Expand All @@ -22,7 +22,7 @@
"type": "TOOL",
"id": "jumper_cable_heavy",
"name": "heavy-duty cable",
"description": "A long, thick, heavy-duty cable with power leads on either end. It looks like you could use it to hook up two vehicles to each other, though you expect the power loss would be noticeable.",
"description": "A long, thick, heavy-duty cable with power leads on either end. It looks like you could use it to hook up two vehicles to each other, though you expect the power loss would be noticeable. Can also link other electrical systems.",
"volume": "1500 ml",
"weight": 750,
"max_charges": 20,
Expand Down
1 change: 1 addition & 0 deletions doc/JSON_FLAGS.md
Original file line number Diff line number Diff line change
Expand Up @@ -1191,6 +1191,7 @@ Melee flags are fully compatible with tool flags, and vice versa.
- ```FISH_GOOD``` When used for fishing, it's a good tool (requires that the matching use_action has been set).
- ```FISH_POOR``` When used for fishing, it's a poor tool (requires that the matching use_action has been set).
- ```HAS_RECIPE``` Used by the E-Ink tablet to indicates it's currently showing a recipe.
- ```IS_UPS``` Item is Unified Power Supply. Used in active item processing
- ```LIGHT_[X]``` Illuminates the area with light intensity `[X]` where `[X]` is an intensity value. (e.x. `LIGHT_4` or `LIGHT_100`).
- ```MC_MOBILE```, ```MC_RANDOM_STUFF```, ```MC_SCIENCE_STUFF```, ```MC_USED```, ```MC_HAS_DATA``` Memory card related flags, see `iuse.cpp`
- ```NO_DROP``` Item should never exist on map tile as a discrete item (must be contained by another item)
Expand Down
81 changes: 63 additions & 18 deletions src/bionics.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -670,28 +670,49 @@ bool player::activate_bionic( int b, bool eff_only )
reactor_plut = 0;
}
} else if( bio.id == "bio_cable" ) {
bool has_cable = has_item_with( []( const item & it ) {
return it.active && it.has_flag( "CABLE_SPOOL" );
} );
bool has_connected_cable = has_item_with( []( const item & it ) {
return it.active && it.has_flag( "CABLE_SPOOL" ) && it.get_var( "state" ) == "solar_pack_link";
std::vector<item *> cables = items_with( []( const item & it ) {
return it.has_flag( "CABLE_SPOOL" );
} );

bool has_cable = cables.size() > 0;
bool free_cable = false;
if( !has_cable ) {
add_msg_if_player( m_info,
_( "You need a jumper cable connected to a vehicle to drain power from it." ) );
}
if( is_wearing( "solarpack_on" ) || is_wearing( "q_solarpack_on" ) ) {
if( has_connected_cable ) {
add_msg_if_player( m_info, _( "Your plugged-in solar pack is now able to charge"
" your system." ) );
} else {
add_msg_if_player( m_info, _( "You need to connect the cable to yourself and the solar pack"
" before your solar pack can charge your system." ) );
_( "You need a jumper cable connected to a power source to drain power from it." ) );
} else {
for( item *cable : cables ) {
const std::string state = cable->get_var( "state" );
if( state == "cable_charger" ) {
add_msg_if_player( m_info,
_( "Cable is plugged-in to the CBM but it has to be also connected to the power source." ) );
}
if( state == "cable_charger_link" ) {
add_msg_if_player( m_info,
_( "You are plugged to the vehicle. It will charge you if it has some juice in it." ) );
}
if( state == "solar_pack_link" ) {
add_msg_if_player( m_info,
_( "You are plugged to a solar pack. It will charge you if it's unfolded and in sunlight." ) );
}
if( state == "UPS_link" ) {
add_msg_if_player( m_info,
_( "You are plugged to a UPS. It will charge you if it has some juice in it." ) );
}
if( state == "solar_pack" || state == "UPS" ) {
add_msg_if_player( m_info,
_( "You have a cable plugged to a portable power source, but you need to plug it in to the CBM." ) );
}
if( state == "pay_oyt_cable" ) {
add_msg_if_player( m_info,
_( "You have a cable plugged to a vehicle, but you need to plug it in to the CBM." ) );
}
if( state == "attach_first" ) {
free_cable = true;
}
}
} else if( is_wearing( "solarpack" ) || is_wearing( "q_solarpack" ) ) {
add_msg_if_player( m_info, _( "You might plug in your solar pack to the cable charging"
" system, if you unfold it." ) );
}
if( free_cable ) {
add_msg_if_player( m_info,
_( "You have at least one free cable in your inventory that you could use to plug yourself in." ) );
}
}

Expand Down Expand Up @@ -973,6 +994,30 @@ void player::process_bionic( int b )
for( const item *cable : cables ) {
const cata::optional<tripoint> target = cable->get_cable_target( this, pos() );
if( !target ) {
if( g->m.is_outside( pos() ) && !is_night( calendar::turn ) &&
cable->get_var( "state" ) == "solar_pack_link" ) {
double modifier = g->natural_light_level( pos().z ) / default_daylight_level();
// basic solar panel produces 50W = 1 charge/20_seconds = 180 charges/hour(3600)
if( is_wearing( "solarpack_on" ) && x_in_y( 180 * modifier, 3600 ) ) {
charge_power( 1 );
}
// quantum solar backpack = solar panel x6
if( is_wearing( "q_solarpack_on" ) && x_in_y( 6 * 180 * modifier, 3600 ) ) {
charge_power( 1 );
}
}
if( cable->get_var( "state" ) == "UPS_link" ) {
static const item_filter used_ups = [&]( const item & itm ) {
return itm.get_var( "cable" ) == "plugged_in";
};
if( has_charges( "UPS_off", 1, used_ups ) ) {
use_charges( "UPS_off", 1, used_ups );
charge_power( 1 );
} else if( has_charges( "adv_UPS_off", 1, used_ups ) ) {
use_charges( "adv_UPS_off", roll_remainder( 0.6 ), used_ups );
charge_power( 1 );
}
}
continue;
}
const optional_vpart_position vp = g->m.veh_at( *target );
Expand Down
52 changes: 52 additions & 0 deletions src/item.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3520,6 +3520,8 @@ std::string item::tname( unsigned int quantity, bool with_prefix, unsigned int t
}
if( active && ( has_flag( "WATER_EXTINGUISH" ) || has_flag( "LITCIG" ) ) ) {
ret << _( " (lit)" );
} else if( has_flag( "IS_UPS" ) && get_var( "cable" ) == "plugged_in" ) {
ret << _( " (plugged in)" );
} else if( active && !is_food() && !is_corpse() && ( typeId().length() < 3 ||
typeId().compare( typeId().length() - 3, 3, "_on" ) != 0 ) ) {
// Usually the items whose ids end in "_on" have the "active" or "on" string already contained
Expand Down Expand Up @@ -8203,6 +8205,34 @@ cata::optional<tripoint> item::get_cable_target( player *p, const tripoint &pos

bool item::process_cable( player *p, const tripoint &pos )
{
if( p == nullptr ) {
reset_cable( p );
return false;
}
std::string state = get_var( "state" );
if( state == "solar_pack_link" || state == "solar_pack" ) {
if( !p->has_item( *this ) || ( !p->is_wearing( "solarpack_on" ) ||
nexusmrsep marked this conversation as resolved.
Show resolved Hide resolved
!p->is_wearing( "solarpack_on" ) ) ) {
p->add_msg_if_player( m_bad, _( "You notice the cable has come loose!" ) );
reset_cable( p );
return false;
}
}

static const item_filter used_ups = [&]( const item & itm ) {
return itm.get_var( "cable" ) == "plugged_in";
};

if( state == "UPS" ) {
if( !p->has_item( *this ) || ( !p->has_item_with( used_ups ) ) ) {
nexusmrsep marked this conversation as resolved.
Show resolved Hide resolved
p->add_msg_if_player( m_bad, _( "You notice the cable has come loose!" ) );
for( item *used : p->items_with( used_ups ) ) {
used->erase_var( "cable" );
}
reset_cable( p );
return false;
}
}
const cata::optional<tripoint> source = get_cable_target( p, pos );
if( !source ) {
return false;
Expand Down Expand Up @@ -8247,6 +8277,24 @@ void item::reset_cable( player *p )
}
}

bool item::process_UPS( player *p, const tripoint & /*pos*/ )
{
if( p == nullptr ) {
erase_var( "cable" );
active = false;
return false;
}
bool has_connected_cable = p->has_item_with( []( const item & it ) {
return it.active && it.has_flag( "CABLE_SPOOL" ) && ( it.get_var( "state" ) == "UPS_link" ||
it.get_var( "state" ) == " UPS " );
nexusmrsep marked this conversation as resolved.
Show resolved Hide resolved
} );
if( !has_connected_cable ) {
erase_var( "cable" );
active = false;
}
return false;
}

bool item::process_wet( player * /*carrier*/, const tripoint & /*pos*/ )
{
if( item_counter == 0 ) {
Expand Down Expand Up @@ -8415,6 +8463,10 @@ bool item::process( player *carrier, const tripoint &pos, bool activate,
// DO NOT process this as a tool! It really isn't!
return process_cable( carrier, pos );
}
if( has_flag( "IS_UPS" ) ) {
// DO NOT process this as a tool! It really isn't!
return process_UPS( carrier, pos );
}
if( is_tool() ) {
return process_tool( carrier, pos );
}
Expand Down
3 changes: 2 additions & 1 deletion src/item.h
Original file line number Diff line number Diff line change
Expand Up @@ -2020,7 +2020,8 @@ class item : public visitable<item>
// Place conditions that should remove fake smoke item in this sub-function
bool process_fake_smoke( player *carrier, const tripoint &pos );
bool process_fake_mill( player *carrier, const tripoint &pos );
bool process_cable( player *p, const tripoint &pos );
bool process_cable( player *carrier, const tripoint &pos );
nexusmrsep marked this conversation as resolved.
Show resolved Hide resolved
bool process_UPS( player *carrier, const tripoint &pos );
bool process_blackpowder_fouling( player *carrier );
bool process_tool( player *carrier, const tripoint &pos );

Expand Down
75 changes: 63 additions & 12 deletions src/iuse.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8448,6 +8448,7 @@ int iuse::cable_attach( player *p, item *it, bool, const tripoint & )
const bool has_solar_pack = p->is_wearing( "solarpack" ) || p->is_wearing( "q_solarpack" );
const bool has_solar_pack_on = p->is_wearing( "solarpack_on" ) || p->is_wearing( "q_solarpack_on" );
const bool wearing_solar_pack = has_solar_pack || has_solar_pack_on;
const bool has_ups = p->has_charges( "UPS_off", 1 ) || p->has_charges( "adv_UPS_off", 1 );

const auto set_cable_active = []( player * p, item * it, const std::string & state ) {
it->set_var( "state", state );
Expand All @@ -8464,16 +8465,35 @@ int iuse::cable_attach( player *p, item *it, bool, const tripoint & )
if( wearing_solar_pack ) {
kmenu.addentry( 2, has_solar_pack_on, -1, _( "Attach cable to solar pack" ) );
}
if( has_ups ) {
kmenu.addentry( 3, true, -1, _( "Attach cable to UPS" ) );
}
kmenu.query();
int choice = kmenu.ret;

if( choice < 0 ) {
return 0; // we did nothing.
} else if( choice == 1 ) {
set_cable_active( p, it, "cable_charger" );
p->add_msg_if_player( m_info, _( "You attach the cable to your Cable Charger System." ) );
return 0;
} else if( choice == 2 ) {
set_cable_active( p, it, "solar_pack" );
p->add_msg_if_player( m_info, _( "You attach the cable to the solar pack." ) );
return 0;
} else if( choice == 3 ) {
int pos = g->inv_for_filter( _( "Choose UPS:" ), [&]( const item & itm ) {
return itm.has_flag( "IS_UPS" );
}, _( "You don't have any UPS." ) );
if( pos == INT_MIN ) {
add_msg( _( "Never mind" ) );
return 0;
}
item &chosen = p->i_at( pos );
chosen.set_var( "cable", "plugged_in" );
chosen.activate();
set_cable_active( p, it, "UPS" );
p->add_msg_if_player( m_info, _( "You attach the cable to the UPS." ) );
return 0;
}
// fall through for attaching to a vehicle
Expand Down Expand Up @@ -8515,43 +8535,73 @@ int iuse::cable_attach( player *p, item *it, bool, const tripoint & )
const bool paying_out = initial_state == "pay_out_cable";
const bool cable_cbm = initial_state == "cable_charger";
const bool solar_pack = initial_state == "solar_pack";
bool loose_ends = paying_out || cable_cbm || solar_pack;
const bool UPS = initial_state == "UPS";
bool loose_ends = paying_out || cable_cbm || solar_pack || UPS;
uilist kmenu;
kmenu.text = _( "Using cable:" );
kmenu.addentry( 0, paying_out || cable_cbm, -1, _( "Attach loose end of the cable" ) );
kmenu.addentry( 1, true, -1, _( "Detach and re-spool the cable" ) );
kmenu.addentry( 0, true, -1, _( "Detach and re-spool the cable" ) );
if( has_bio_cable && loose_ends ) {
kmenu.addentry( 2, !cable_cbm, -1, _( "Attach cable to self" ) );
// can't attach solar backpacks to cars
if( wearing_solar_pack && cable_cbm ) {
kmenu.addentry( 3, has_solar_pack_on, -1, _( "Attach cable to solar pack" ) );
kmenu.addentry( 1, ( paying_out || cable_cbm ) && !solar_pack &&
!UPS, -1, _( "Attach loose end to vehicle" ) );
kmenu.addentry( 2, !cable_cbm, -1, _( "Attach loose end to self" ) );
if( wearing_solar_pack ) {
kmenu.addentry( 3, !solar_pack && !paying_out && !UPS, -1, _( "Attach loose end to solar pack" ) );
}
if( has_ups ) {
kmenu.addentry( 4, !UPS && !solar_pack && !paying_out, -1, _( "Attach loose end to UPS" ) );
}
}
kmenu.query();
int choice = kmenu.ret;

if( choice < 0 ) {
return 0; // we did nothing.
} else if( choice == 1 ) {
} else if( choice == 0 ) { // unconnect & respool
it->reset_cable( p );
return 0;
} else if( choice == 2 ) {
// connecting self to backpack or car
} else if( choice == 2 ) { // connect self while other end already connected
p->add_msg_if_player( m_info, _( "You attach the cable to the Cable Charger System." ) );
// connecting self, solar backpack connected
if( solar_pack ) {
set_cable_active( p, it, "solar_pack_link" );
p->add_msg_if_player( m_good, _( "You are now plugged to the solar backpack." ) );
return 0;
}
// connecting self, UPS connected
if( UPS ) {
set_cable_active( p, it, "UPS_link" );
p->add_msg_if_player( m_good, _( "You are now plugged to the UPS." ) );
return 0;
}
// connecting self, vehicle connected
const optional_vpart_position source_vp = confirm_source_vehicle( p, it, true );
if( veh_pointer_or_null( source_vp ) != nullptr ) {
set_cable_active( p, it, "cable_charger_link" );
p->add_msg_if_player( m_good, _( "You are now plugged to the vehicle." ) );
}
return 0;
} else if( choice == 3 ) {
// connecting self to backpack
// connecting self to solar backpack
set_cable_active( p, it, "solar_pack_link" );
p->add_msg_if_player( m_good, _( "You are now plugged to the solar backpack." ) );
return 0;
} else if( choice == 4 ) {
// connecting self to UPS
int pos = g->inv_for_filter( _( "Choose UPS:" ), [&]( const item & itm ) {
return itm.has_flag( "IS_UPS" );
}, _( "You don't have any UPS." ) );
if( pos == INT_MIN ) {
add_msg( _( "Never mind" ) );
return 0;
}
item &chosen = p->i_at( pos );
chosen.set_var( "cable", "plugged_in" );
chosen.activate();
set_cable_active( p, it, "UPS_link" );
p->add_msg_if_player( m_good, _( "You are now plugged to the UPS." ) );
return 0;
}

// connecting self to vehicle
const optional_vpart_position source_vp = confirm_source_vehicle( p, it, paying_out );
vehicle *const source_veh = veh_pointer_or_null( source_vp );
if( source_veh == nullptr && paying_out ) {
Expand All @@ -8574,6 +8624,7 @@ int iuse::cable_attach( player *p, item *it, bool, const tripoint & )
it->set_var( "source_y", abspos.y );
it->set_var( "source_z", g->get_levz() );
set_cable_active( p, it, "cable_charger_link" );
p->add_msg_if_player( m_good, _( "You are now plugged to the vehicle." ) );
return 0;
} else {
vehicle *const target_veh = &target_vp->vehicle();
Expand Down
Loading