diff --git a/data/json/flags.json b/data/json/flags.json index be2479e6f193c..e3283e374cf2f 100644 --- a/data/json/flags.json +++ b/data/json/flags.json @@ -1419,7 +1419,8 @@ { "id": "IS_UPS", "type": "json_flag", - "context": [ ] + "context": [ ], + "info": "This item provides power to UPS compatible items." }, { "id": "LEAK_DAM", diff --git a/data/json/itemgroups/Clothing_Gear/clothing.json b/data/json/itemgroups/Clothing_Gear/clothing.json index 467f3896bc397..7e654c3d2630b 100644 --- a/data/json/itemgroups/Clothing_Gear/clothing.json +++ b/data/json/itemgroups/Clothing_Gear/clothing.json @@ -1948,8 +1948,7 @@ [ "optical_cloak", 1 ], [ "holo_cloak", 1 ], [ "backpack", 38 ], - { "item": "UPS_off", "prob": 5, "charges": [ 0, 1000 ] }, - [ "adv_UPS_off", 3 ], + { "item": "UPS_off", "prob": 8, "charges": [ 0, 1000 ] }, [ "tacvest", 10 ], [ "molle_pack", 8 ], [ "duffelbag", 15 ], @@ -1989,8 +1988,7 @@ { "item": "mil_mess_kit", "prob": 1, "charges": [ 0, 500 ] }, [ "backpack", 38 ], [ "briefcase", 10 ], - { "item": "UPS_off", "prob": 5, "charges": [ 0, 1000 ] }, - [ "adv_UPS_off", 3 ], + { "item": "UPS_off", "prob": 8, "charges": [ 0, 1000 ] }, [ "chestguard_hard", 20 ], [ "armguard_hard", 20 ], [ "legguard_hard", 15 ], @@ -2111,8 +2109,7 @@ [ "legpouch_large", 6 ], [ "chestpouch", 9 ], [ "ammo_satchel", 4 ], - { "item": "UPS_off", "prob": 5, "charges": [ 0, 1000 ] }, - [ "adv_UPS_off", 3 ], + { "item": "UPS_off", "prob": 8, "charges": [ 0, 1000 ] }, [ "tacvest", 10 ], [ "molle_pack", 8 ], [ "legrig", 10 ], diff --git a/data/json/itemgroups/Clothing_Gear/gear.json b/data/json/itemgroups/Clothing_Gear/gear.json index b0c222207bca6..f80f72587f7b0 100644 --- a/data/json/itemgroups/Clothing_Gear/gear.json +++ b/data/json/itemgroups/Clothing_Gear/gear.json @@ -126,8 +126,7 @@ [ "knee_pads", 5 ], [ "elbow_pads", 5 ], [ "legrig", 5 ], - { "item": "UPS_off", "prob": 5, "charges": [ 0, 1000 ] }, - [ "adv_UPS_off", 2 ], + { "item": "UPS_off", "prob": 7, "charges": [ 0, 1000 ] }, [ "emer_blanket", 5 ], { "group": "ammo_pocket_batteries_full", "prob": 50 }, [ "tool_belt", 5 ], @@ -154,7 +153,7 @@ { "item": "rad_monitor", "prob": 10, "charges": [ 0, 100 ] }, { "item": "oxygen_tank", "prob": 10, "charges-min": 0 }, { "item": "smoxygen_tank", "prob": 15, "charges-min": 0 }, - [ "adv_UPS_off", 2 ], + [ "UPS_off", 2 ], { "item": "prussian_blue", "prob": 25, "charges": [ 1, 10 ] }, { "item": "iodine", "prob": 50, "charges": [ 1, 10 ] } ] diff --git a/data/json/itemgroups/Locations_MapExtras/locations.json b/data/json/itemgroups/Locations_MapExtras/locations.json index 9fea53bc64c65..bc8fa360a62a8 100644 --- a/data/json/itemgroups/Locations_MapExtras/locations.json +++ b/data/json/itemgroups/Locations_MapExtras/locations.json @@ -85,7 +85,7 @@ { "group": "gunmod_energy", "prob": 40 }, { "item": "plut_cell", "prob": 30, "charges": [ 1, 5 ] }, [ "plasma", 30 ], - [ "adv_UPS_off", 20 ] + [ "UPS_off", 20 ] ] }, { diff --git a/data/json/itemgroups/Monsters_Animals_Lairs/monster_drops_lairs.json b/data/json/itemgroups/Monsters_Animals_Lairs/monster_drops_lairs.json index c16c6e60b6232..ac013f2c49083 100644 --- a/data/json/itemgroups/Monsters_Animals_Lairs/monster_drops_lairs.json +++ b/data/json/itemgroups/Monsters_Animals_Lairs/monster_drops_lairs.json @@ -237,8 +237,7 @@ { "item": "bot_grenade_hack", "prob": 1 }, { "item": "bot_flashbang_hack", "prob": 1 }, { "item": "bot_gasbomb_hack", "prob": 1 }, - { "item": "UPS_off", "prob": 5 }, - { "item": "adv_UPS_off", "prob": 3 }, + { "item": "UPS_off", "prob": 8 }, { "item": "bio_power_storage", "prob": 10 }, { "item": "bio_flashlight", "prob": 10 }, { "item": "bio_lighter", "prob": 10 }, diff --git a/data/json/itemgroups/activities_hobbies.json b/data/json/itemgroups/activities_hobbies.json index 5b005686179cc..5a0e221d1b405 100644 --- a/data/json/itemgroups/activities_hobbies.json +++ b/data/json/itemgroups/activities_hobbies.json @@ -249,8 +249,7 @@ { "item": "water_purifier", "prob": 5, "charges": [ 0, 100 ] }, { "item": "radio", "prob": 20, "charges": [ 0, 100 ] }, [ "beartrap", 5 ], - { "item": "UPS_off", "prob": 5, "charges": [ 0, 1000 ] }, - [ "adv_UPS_off", 3 ], + { "item": "UPS_off", "prob": 8, "charges": [ 0, 1000 ] }, [ "string_36", 40 ], [ "longbow", 5 ], [ "compbow", 1 ], diff --git a/data/json/itemgroups/main.json b/data/json/itemgroups/main.json index 46ba7335f28cf..845774908c34b 100644 --- a/data/json/itemgroups/main.json +++ b/data/json/itemgroups/main.json @@ -103,7 +103,7 @@ { "item": "knife_rm42", "prob": 10 }, { "item": "laser_rifle", "prob": 10 }, { "item": "mininuke", "prob": 10 }, - { "item": "adv_UPS_off", "prob": 10 }, + { "item": "UPS_off", "prob": 10 }, { "item": "optical_cloak", "prob": 10 }, { "item": "superalloy_harness_dog", "prob": 10 }, { "item": "holo_cloak", "prob": 10 }, diff --git a/data/json/itemgroups/military.json b/data/json/itemgroups/military.json index e1efa1a9a32c4..4cc677140925a 100644 --- a/data/json/itemgroups/military.json +++ b/data/json/itemgroups/military.json @@ -552,8 +552,7 @@ { "item": "v8_combustion", "prob": 10 }, { "item": "extinguisher", "prob": 20, "charges": 100 }, { "item": "radio", "prob": 20, "charges": [ 0, 100 ] }, - { "item": "UPS_off", "prob": 5, "charges": [ 0, 1000 ] }, - { "item": "adv_UPS_off", "prob": 3 }, + { "item": "UPS_off", "prob": 8, "charges": [ 0, 1000 ] }, { "item": "tacvest", "prob": 10 }, { "item": "molle_pack", "prob": 8 }, { "item": "legrig", "prob": 10 }, diff --git a/data/json/itemgroups/science_and_tech.json b/data/json/itemgroups/science_and_tech.json index 2561b7e6b007a..0962ba2743c4f 100644 --- a/data/json/itemgroups/science_and_tech.json +++ b/data/json/itemgroups/science_and_tech.json @@ -78,8 +78,7 @@ [ "flux_comp_gen", 3 ], [ "portal", 2 ], [ "bot_manhack", 1 ], - { "item": "UPS_off", "prob": 5, "charges": [ 0, 1000 ] }, - [ "adv_UPS_off", 3 ], + { "item": "UPS_off", "prob": 8, "charges": [ 0, 1000 ] }, { "item": "tazer", "prob": 3, "charges": [ 0, 500 ] }, { "item": "plasma", "prob": 8, "charges": [ 1, 25 ] }, [ "usb_drive", 5 ], diff --git a/data/json/items/ammo_types.json b/data/json/items/ammo_types.json index eb3620ad13cc1..997bcf05db342 100644 --- a/data/json/items/ammo_types.json +++ b/data/json/items/ammo_types.json @@ -467,12 +467,6 @@ "name": "ferrous rail projectile", "default": "rebar_rail" }, - { - "type": "ammunition_type", - "id": "UPS", - "name": "UPS", - "default": "UPS" - }, { "type": "ammunition_type", "id": "thrown", diff --git a/data/json/items/migration.json b/data/json/items/migration.json index 2c89d4764328c..252efdfcd13fc 100644 --- a/data/json/items/migration.json +++ b/data/json/items/migration.json @@ -77,7 +77,7 @@ { "id": "adv_UPS_on", "type": "MIGRATION", - "replace": "adv_UPS_off" + "replace": "UPS_off" }, { "id": "battery_truck", @@ -1420,5 +1420,10 @@ "id": "gloves_xlsurvivor", "type": "MIGRATION", "replace": "xl_gloves_survivor" + }, + { + "id": "adv_UPS_off", + "type": "MIGRATION", + "replace": "UPS_off" } ] diff --git a/data/json/items/tool/electronics.json b/data/json/items/tool/electronics.json index e9533b04e7c47..209ae95d724a9 100644 --- a/data/json/items/tool/electronics.json +++ b/data/json/items/tool/electronics.json @@ -1,30 +1,4 @@ [ - { - "id": "adv_UPS_off", - "type": "TOOL", - "name": { "str": "advanced UPS", "str_pl": "advanced UPS's" }, - "description": "This is an advanced version of the unified power supply, or UPS. This device has been significantly redesigned to provide better efficiency as well as to consume plutonium fuel batteries rather than regular batteries. Sadly, its plutonium reactor can't be charged in UPS charging station.", - "weight": "453 g", - "volume": "2 L", - "price": 560000, - "price_postapoc": 3000, - "to_hit": -1, - "bashing": 8, - "material": [ "aluminum", "plastic" ], - "symbol": ";", - "color": "light_green", - "ammo": [ "battery" ], - "pocket_data": [ - { - "pocket_type": "MAGAZINE_WELL", - "holster": true, - "max_contains_volume": "20 L", - "max_contains_weight": "20 kg", - "item_restriction": [ "heavy_atomic_battery_cell" ] - } - ], - "flags": [ "IS_UPS" ] - }, { "id": "camera", "type": "TOOL", diff --git a/data/json/npcs/NC_SOLDIER.json b/data/json/npcs/NC_SOLDIER.json index 32254860913cf..26579f0da6481 100644 --- a/data/json/npcs/NC_SOLDIER.json +++ b/data/json/npcs/NC_SOLDIER.json @@ -136,8 +136,7 @@ { "item": "goggles_nv", "prob": 1 }, { "item": "goggles_ir", "prob": 1 }, { "item": "optical_cloak", "prob": 1 }, - { "item": "UPS_off", "prob": 5 }, - { "item": "adv_UPS_off", "prob": 3 }, + { "item": "UPS_off", "prob": 8 }, { "item": "tacvest", "prob": 10 }, { "item": "backpack", "prob": 8 }, { "item": "dump_pouch", "prob": 20 }, diff --git a/data/json/npcs/items_generic.json b/data/json/npcs/items_generic.json index 64a3fe75c951b..a12addde7ba34 100644 --- a/data/json/npcs/items_generic.json +++ b/data/json/npcs/items_generic.json @@ -339,7 +339,6 @@ [ "adderall", 2 ], [ "adjustable_stock", 1 ], [ "adv_chemistry", 1 ], - [ "adv_UPS_off", 1 ], [ "advanced_ecig", 5 ], [ "advanced_electronics", 1 ], [ "airhorn", 3 ], @@ -1082,7 +1081,7 @@ [ "trappers_companion", 2 ], [ "triple_sec", 2 ], [ "two_way_radio", 10 ], - [ "UPS_off", 3 ], + [ "UPS_off", 4 ], [ "umbrella", 3 ], [ "usb_drive", 3 ], [ "V8", 3 ], diff --git a/data/json/obsolete.json b/data/json/obsolete.json index 82c8646ae6964..5c8036d93da7f 100644 --- a/data/json/obsolete.json +++ b/data/json/obsolete.json @@ -1547,5 +1547,31 @@ ], "tools": [ [ [ "tongs", -1 ] ], [ [ "swage", -1 ] ], [ [ "surface_heat", 1, "LIST" ], [ "forge", 1 ], [ "oxy_torch", 1 ] ] ], "components": [ [ [ "copper", 1 ] ], [ [ "duct_tape", 1 ] ] ] + }, + { + "id": "adv_UPS_off", + "type": "TOOL", + "name": { "str": "advanced UPS", "str_pl": "advanced UPS's" }, + "description": "This is an advanced version of the unified power supply, or UPS. This device has been significantly redesigned to provide better efficiency as well as to consume plutonium fuel batteries rather than regular batteries. Sadly, its plutonium reactor can't be charged in UPS charging station.", + "weight": "453 g", + "volume": "2 L", + "price": 560000, + "price_postapoc": 3000, + "to_hit": -1, + "bashing": 8, + "material": [ "aluminum", "plastic" ], + "symbol": ";", + "color": "light_green", + "ammo": [ "battery" ], + "pocket_data": [ + { + "pocket_type": "MAGAZINE_WELL", + "holster": true, + "max_contains_volume": "20 L", + "max_contains_weight": "20 kg", + "item_restriction": [ "heavy_atomic_battery_cell" ] + } + ], + "flags": [ "IS_UPS" ] } ] diff --git a/data/json/recipes/other/power_supplies.json b/data/json/recipes/other/power_supplies.json index 03ada284bede5..f0d57a80e8dee 100644 --- a/data/json/recipes/other/power_supplies.json +++ b/data/json/recipes/other/power_supplies.json @@ -76,23 +76,6 @@ "qualities": [ { "id": "SCREW", "level": 1 } ], "components": [ [ [ "power_supply", 4 ] ], [ [ "amplifier", 3 ] ], [ [ "scrap", 4 ] ], [ [ "cable", 10 ] ] ] }, - { - "type": "recipe", - "activity_level": "LIGHT_EXERCISE", - "result": "adv_UPS_off", - "category": "CC_ELECTRONIC", - "subcategory": "CSC_ELECTRONIC_COMPONENTS", - "skill_used": "electronics", - "skills_required": [ "mechanics", 1 ], - "difficulty": 9, - "time": "1 h 25 m", - "reversible": true, - "decomp_learn": 4, - "book_learn": [ [ "recipe_lab_elec", 7 ] ], - "using": [ [ "soldering_standard", 24 ] ], - "qualities": [ { "id": "SCREW", "level": 1 } ], - "components": [ [ [ "power_supply", 6 ] ], [ [ "amplifier", 5 ] ], [ [ "scrap", 4 ] ], [ [ "cable", 14 ] ], [ [ "plut_cell", 2 ] ] ] - }, { "type": "recipe", "activity_level": "LIGHT_EXERCISE", diff --git a/data/mods/Aftershock/items/migration.json b/data/mods/Aftershock/items/migration.json new file mode 100644 index 0000000000000..7d29383915d07 --- /dev/null +++ b/data/mods/Aftershock/items/migration.json @@ -0,0 +1,8 @@ +[ + { + "id": "adv_UPS_off", + "type": "MIGRATION", + "replace": "adv_UPS_off", + "//": "Overrides migration from CDDA." + } +] diff --git a/data/mods/Aftershock/items/tools.json b/data/mods/Aftershock/items/tools.json index 5d0c6819e3493..8725aed196e06 100644 --- a/data/mods/Aftershock/items/tools.json +++ b/data/mods/Aftershock/items/tools.json @@ -195,7 +195,7 @@ "type": "TOOL_ARMOR", "copy-from": "adv_UPS_off", "name": { "str": "advanced UPS", "str_pl": "advanced UPS's" }, - "description": "This is an advanced version of the unified power supply, or UPS. This device has been significantly redesigned to provide better efficiency as well as to consume plutonium fuel cells rather than batteries, and is both slimmer and lighter to wear. Sadly, its plutonium reactor can't be charged in UPS charging station.", + "description": "This is an advanced version of the unified power supply, or UPS. This device has been significantly redesigned to consume plutonium fuel cells rather than batteries, and is both slimmer and lighter to wear. Sadly, its plutonium reactor can't be charged in UPS charging station.", "sided": true, "flags": [ "WAIST", "FRAGILE", "OVERSIZE", "IS_UPS" ], "armor": [ { "encumbrance": 1, "coverage": 5, "covers": [ "leg_l", "leg_r" ] } ] diff --git a/src/activity_handlers.cpp b/src/activity_handlers.cpp index 424c315f04fd6..7402363c6fe9e 100644 --- a/src/activity_handlers.cpp +++ b/src/activity_handlers.cpp @@ -204,7 +204,6 @@ static const itype_id itype_splinter( "splinter" ); static const itype_id itype_stick_long( "stick_long" ); static const itype_id itype_steel_chunk( "steel_chunk" ); static const itype_id itype_steel_plate( "steel_plate" ); -static const itype_id itype_UPS( "UPS" ); static const itype_id itype_wire( "wire" ); static const itype_id itype_chain( "chain" ); @@ -1696,13 +1695,7 @@ void activity_handlers::generic_game_turn_handler( player_activity *act, player if( calendar::once_every( 1_minutes ) ) { if( !act->targets.empty() ) { item &game_item = *act->targets.front(); - const int ammo_required = game_item.ammo_required(); - bool fail = false; - if( game_item.has_flag( flag_USE_UPS ) ) { - fail = !p->use_charges_if_avail( itype_UPS, ammo_required ); - } else { - fail = game_item.ammo_consume( ammo_required, p->pos() ) == 0; - } + bool fail = game_item.ammo_consume( game_item.ammo_required(), tripoint_zero, p ) == 0; if( fail ) { act->moves_left = 0; add_msg( m_info, _( "The %s runs out of batteries." ), game_item.tname() ); @@ -1921,9 +1914,8 @@ void activity_handlers::start_fire_finish( player_activity *act, player *p ) return; } - if( it.type->can_have_charges() ) { - p->consume_charges( it, it.type->charges_to_use() ); - } + it.ammo_consume( it.type->charges_to_use(), tripoint_zero, p ); + p->practice( skill_survival, act->index, 5 ); firestarter_actor::resolve_firestarter_use( *p, act->placement ); @@ -2131,10 +2123,10 @@ void activity_handlers::vibe_do_turn( player_activity *act, player *p ) } if( calendar::once_every( 1_minutes ) ) { - if( vibrator_item.ammo_remaining() > 0 ) { - vibrator_item.ammo_consume( 1, p->pos() ); + if( vibrator_item.ammo_remaining( p ) > 0 ) { + vibrator_item.ammo_consume( 1, p->pos(), p ); p->add_morale( MORALE_FEELING_GOOD, 3, 40 ); - if( vibrator_item.ammo_remaining() == 0 ) { + if( vibrator_item.ammo_remaining( p ) == 0 ) { add_msg( m_info, _( "The %s runs out of batteries." ), vibrator_item.tname() ); } } else { @@ -2235,7 +2227,7 @@ void activity_handlers::oxytorch_do_turn( player_activity *act, player *p ) // act->values[0] is the number of charges yet to be consumed const int charges_used = std::min( act->values[0], it.ammo_required() ); - it.ammo_consume( charges_used, p->pos() ); + it.ammo_consume( charges_used, p->pos(), p ); act->values[0] -= static_cast( charges_used ); sfx::play_activity_sound( "tool", "oxytorch", sfx::get_heard_volume( act->placement ) ); @@ -2252,7 +2244,7 @@ void activity_handlers::oxytorch_finish( player_activity *act, player *p ) const ter_id ter = here.ter( pos ); const furn_id furn = here.furn( pos ); // fast players might still have some charges left to be consumed - act->targets.front()->ammo_consume( act->values[0], p->pos() ); + act->targets.front()->ammo_consume( act->values[0], p->pos(), p ); if( furn == f_rack ) { here.furn_set( pos, f_null ); @@ -2479,7 +2471,7 @@ void activity_handlers::repair_item_finish( player_activity *act, player *p ) const repair_item_actor::attempt_hint attempt = actor->repair( *p, *used_tool, fix_location ); if( attempt != repair_item_actor::AS_CANT ) { if( ploc && ploc->where() == item_location::type::map ) { - used_tool->ammo_consume( used_tool->ammo_required(), ploc->position() ); + used_tool->ammo_consume( used_tool->ammo_required(), ploc->position(), p ); } else { p->consume_charges( *used_tool, used_tool->ammo_required() ); } @@ -2568,10 +2560,7 @@ void activity_handlers::repair_item_finish( player_activity *act, player *p ) ammo_name = item::nname( used_tool->ammo_current() ); } - int ammo_remaining = used_tool->ammo_remaining(); - if( used_tool->has_flag( flag_USE_UPS ) ) { - ammo_remaining = p->charges_of( itype_UPS ); - } + int ammo_remaining = used_tool->ammo_remaining( p ); std::set valid_entries = actor->get_valid_repair_materials( fix ); const inventory &crafting_inv = p->crafting_inventory(); @@ -3772,7 +3761,7 @@ void activity_handlers::jackhammer_finish( player_activity *act, player *p ) act->set_to_null(); if( !act->targets.empty() ) { item &it = *act->targets.front(); - p->consume_charges( it, it.ammo_required() ); + it.ammo_consume( it.ammo_required(), tripoint_zero, p ); } else { debugmsg( "jackhammer activity targets empty" ); } diff --git a/src/activity_item_handling.cpp b/src/activity_item_handling.cpp index cbb4a39f40d9b..f520f93c8651a 100644 --- a/src/activity_item_handling.cpp +++ b/src/activity_item_handling.cpp @@ -1134,9 +1134,9 @@ static activity_reason_info can_do_activity_there( const activity_id &act, playe if( !here.has_flag( "MINEABLE", src_loc ) ) { return activity_reason_info::fail( do_activity_reason::NO_ZONE ); } - std::vector mining_inv = p.items_with( []( const item & itm ) { + std::vector mining_inv = p.items_with( [&p]( const item & itm ) { return ( itm.has_flag( flag_DIG_TOOL ) && !itm.type->can_use( "JACKHAMMER" ) ) || - ( itm.type->can_use( "JACKHAMMER" ) && itm.ammo_sufficient() ); + ( itm.type->can_use( "JACKHAMMER" ) && itm.ammo_sufficient( &p ) ); } ); if( mining_inv.empty() ) { return activity_reason_info::fail( do_activity_reason::NEEDS_MINING ); @@ -1433,8 +1433,8 @@ static std::vector> requirements_map( player if( comp_elem.by_charges() ) { // we don't care if there are 10 welders with 5 charges each // we only want the one welder that has the required charge. - if( stack_elem.ammo_remaining() >= comp_elem.count ) { - temp_map[stack_elem.typeId()] += stack_elem.ammo_remaining(); + if( stack_elem.ammo_remaining( &p ) >= comp_elem.count ) { + temp_map[stack_elem.typeId()] += stack_elem.ammo_remaining( &p ); } } else { temp_map[stack_elem.typeId()] += stack_elem.count(); @@ -2119,9 +2119,9 @@ static int chop_moves( player &p, item *it ) static bool mine_activity( player &p, const tripoint &src_loc ) { - std::vector mining_inv = p.items_with( []( const item & itm ) { + std::vector mining_inv = p.items_with( [&p]( const item & itm ) { return ( itm.has_flag( flag_DIG_TOOL ) && !itm.type->can_use( "JACKHAMMER" ) ) || - ( itm.type->can_use( "JACKHAMMER" ) && itm.ammo_sufficient() ); + ( itm.type->can_use( "JACKHAMMER" ) && itm.ammo_sufficient( &p ) ); } ); map &here = get_map(); if( mining_inv.empty() || p.is_mounted() || p.is_underwater() || here.veh_at( src_loc ) || diff --git a/src/ammo.cpp b/src/ammo.cpp index b1148125a5848..6d369cd128e22 100644 --- a/src/ammo.cpp +++ b/src/ammo.cpp @@ -10,8 +10,6 @@ #include "translations.h" #include "type_id.h" -static const itype_id itype_UPS( "UPS" ); - namespace { using ammo_map_t = std::unordered_map; @@ -69,7 +67,7 @@ void ammunition_type::check_consistency() const auto &at = ammo.second.default_ammotype_; // TODO: these ammo types should probably not have default ammo at all. - if( at == itype_UPS || at.str() == "components" || at.str() == "thrown" ) { + if( at.str() == "components" || at.str() == "thrown" ) { continue; } diff --git a/src/avatar_action.cpp b/src/avatar_action.cpp index f056e7cbb6a2a..5bdc43fb33c53 100644 --- a/src/avatar_action.cpp +++ b/src/avatar_action.cpp @@ -175,7 +175,8 @@ bool avatar_action::move( avatar &you, map &m, const tripoint &d ) !m.veh_at( dest_loc ) && !you.is_underwater() && !you.has_effect( effect_stunned ) && !is_riding && !you.has_effect( effect_incorporeal ) ) { if( you.weapon.has_flag( flag_DIG_TOOL ) ) { - if( you.weapon.type->can_use( "JACKHAMMER" ) && you.weapon.ammo_sufficient() ) { + if( you.weapon.type->can_use( "JACKHAMMER" ) && + you.weapon.ammo_sufficient( &you ) ) { you.invoke_item( &you.weapon, "JACKHAMMER", dest_loc ); // don't move into the tile until done mining you.defer_move( dest_loc ); diff --git a/src/bionics.cpp b/src/bionics.cpp index 066838618a2d3..bbd55bce4a4bf 100644 --- a/src/bionics.cpp +++ b/src/bionics.cpp @@ -129,12 +129,9 @@ static const material_id fuel_type_muscle( "muscle" ); static const material_id fuel_type_sun_light( "sunlight" ); static const material_id fuel_type_wind( "wind" ); -static const itype_id itype_adv_UPS_off( "adv_UPS_off" ); static const itype_id itype_anesthetic( "anesthetic" ); static const itype_id itype_radiocontrol( "radiocontrol" ); static const itype_id itype_remotevehcontrol( "remotevehcontrol" ); -static const itype_id itype_UPS( "UPS" ); -static const itype_id itype_UPS_off( "UPS_off" ); static const itype_id itype_water_clean( "water_clean" ); static const fault_id fault_bionic_salvaged( "fault_bionic_salvaged" ); @@ -536,11 +533,10 @@ void npc::check_or_use_weapon_cbm( const bionic_id &cbm_id ) return; } - const int ups_charges = charges_of( itype_UPS ); - int ammo_count = weapon.ammo_remaining(); + int ammo_count = weapon.ammo_remaining( this ); const int ups_drain = weapon.get_gun_ups_drain(); if( ups_drain > 0 ) { - ammo_count = std::min( ammo_count, ups_charges / ups_drain ); + ammo_count = ammo_count / ups_drain; } const int cbm_ammo = free_power / bio.info().power_activate; @@ -1507,19 +1503,15 @@ material_id Character::find_remote_fuel( bool look_only ) } if( cable->get_var( "state" ) == "UPS_link" ) { - static const item_filter used_ups = [&]( const item & itm ) { - return itm.get_var( "cable" ) == "plugged_in"; - }; if( !look_only ) { - if( has_charges( itype_UPS_off, 1, used_ups ) ) { - set_value( "rem_battery", std::to_string( charges_of( itype_UPS_off, - units::to_kilojoule( max_power_level ), used_ups ) ) ); - } else if( has_charges( itype_adv_UPS_off, 1, used_ups ) ) { - set_value( "rem_battery", std::to_string( charges_of( itype_adv_UPS_off, - units::to_kilojoule( max_power_level ), used_ups ) ) ); - } else { - set_value( "rem_battery", std::to_string( 0 ) ); + int remote_battery = 0; + for( const item *i : all_items_with_flag( flag_IS_UPS ) ) { + if( i->get_var( "cable" ) == "plugged_in" ) { + remote_battery = i->ammo_remaining(); + } } + remote_battery = std::min( remote_battery, units::to_kilojoule( max_power_level ) ); + set_value( "rem_battery", std::to_string( remote_battery ) ); } remote_fuel = fuel_type_battery; } @@ -1559,15 +1551,12 @@ int Character::consume_remote_fuel( int amount ) } if( unconsumed_amount > 0 ) { - static const item_filter used_ups = [&]( const item & itm ) { - return itm.get_var( "cable" ) == "plugged_in"; - }; - if( has_charges( itype_UPS_off, unconsumed_amount, used_ups ) ) { - use_charges( itype_UPS_off, unconsumed_amount, used_ups ); - unconsumed_amount -= 1; - } else if( has_charges( itype_adv_UPS_off, unconsumed_amount, used_ups ) ) { - use_charges( itype_adv_UPS_off, roll_remainder( unconsumed_amount * 0.6 ), used_ups ); - unconsumed_amount -= 1; + for( const item *i : all_items_with_flag( flag_IS_UPS ) ) { + if( i->get_var( "cable" ) == "plugged_in" ) { + unconsumed_amount -= const_cast( i )->ammo_consume( unconsumed_amount, tripoint_zero, + nullptr ); + } + break; } } diff --git a/src/character.cpp b/src/character.cpp index 037f8dfdbb91b..5b97cc9f8f8e9 100644 --- a/src/character.cpp +++ b/src/character.cpp @@ -216,7 +216,6 @@ static const efftype_id effect_winded( "winded" ); static const field_type_str_id field_fd_clairvoyant( "fd_clairvoyant" ); -static const itype_id itype_adv_UPS_off( "adv_UPS_off" ); static const itype_id itype_apparatus( "apparatus" ); static const itype_id itype_beartrap( "beartrap" ); static const itype_id itype_e_handcuffs( "e_handcuffs" ); @@ -226,7 +225,6 @@ static const itype_id itype_rope_6( "rope_6" ); static const itype_id itype_snare_trigger( "snare_trigger" ); static const itype_id itype_string_36( "string_36" ); static const itype_id itype_UPS( "UPS" ); -static const itype_id itype_UPS_off( "UPS_off" ); static const skill_id skill_archery( "archery" ); static const skill_id skill_dodge( "dodge" ); @@ -7898,7 +7896,7 @@ int Character::ammo_count_for( const item &gun ) int ups_drain = gun.get_gun_ups_drain(); if( ups_drain > 0 ) { - ret = std::min( ret, charges_of( itype_UPS ) / ups_drain ); + ret = std::min( ret, available_ups() / ups_drain ); } return ret; @@ -8789,7 +8787,23 @@ bool Character::invoke_item( item *used, const std::string &method, const tripoi add_msg_if_player( m_bad, _( "Your %s was broken and won't turn on." ), used->tname() ); return false; } - if( !has_enough_charges( *used, true ) ) { + if( !used->ammo_sufficient( this ) ) { + int ammo_req = used->ammo_required(); + std::string it_name = used->tname(); + if( used->has_flag( flag_USE_UPS ) ) { + add_msg_if_player( m_info, + ngettext( "Your %s needs %d charge from some UPS.", + "Your %s needs %d charges from some UPS.", + ammo_req ), + it_name, ammo_req ); + } else { + int ammo_rem = used->ammo_remaining(); + add_msg_if_player( m_info, + ngettext( "Your %s has %d charge, but needs %d.", + "Your %s has %d charges, but needs %d.", + ammo_rem ), + it_name, ammo_rem, ammo_req ); + } moves = pre_obtain_moves; return false; } @@ -8921,36 +8935,6 @@ bool Character::dispose_item( item_location &&obj, const std::string &prompt ) return false; } -bool Character::has_enough_charges( const item &it, bool show_msg ) const -{ - if( !it.is_tool() || !it.ammo_required() ) { - return true; - } - if( it.has_flag( flag_USE_UPS ) ) { - if( has_charges( itype_UPS, it.ammo_required() ) || it.ammo_sufficient() ) { - return true; - } - if( show_msg ) { - add_msg_if_player( m_info, - ngettext( "Your %s needs %d charge from some UPS.", - "Your %s needs %d charges from some UPS.", - it.ammo_required() ), - it.tname(), it.ammo_required() ); - } - return false; - } else if( !it.ammo_sufficient() ) { - if( show_msg ) { - add_msg_if_player( m_info, - ngettext( "Your %s has %d charge, but needs %d.", - "Your %s has %d charges, but needs %d.", - it.ammo_remaining() ), - it.tname(), it.ammo_remaining(), it.ammo_required() ); - } - return false; - } - return true; -} - bool Character::consume_charges( item &used, int qty ) { if( qty < 0 ) { @@ -8993,19 +8977,7 @@ bool Character::consume_charges( item &used, int qty ) return true; } - // USE_UPS never occurs on base items but is instead added by the UPS tool mod - if( used.has_flag( flag_USE_UPS ) ) { - // With the new UPS system, we'll want to use any charges built up in the tool before pulling from the UPS - // The usage of the item was already approved, so drain item if possible, otherwise use UPS - if( used.charges >= qty || ( used.magazine_integral() && - !used.has_flag( flag_id( "USES_BIONIC_POWER" ) ) && used.ammo_remaining() >= qty ) ) { - used.ammo_consume( qty, pos() ); - } else { - use_charges( itype_UPS, qty ); - } - } else { - used.ammo_consume( std::min( qty, used.ammo_remaining() ), pos() ); - } + used.ammo_consume( qty, pos(), this ); return false; } @@ -11301,11 +11273,6 @@ bool Character::has_charges( const itype_id &it, int quantity, if( it == itype_fire || it == itype_apparatus ) { return has_fire( quantity ); } - if( it == itype_UPS && is_mounted() && - mounted_creature.get()->has_flag( MF_RIDEABLE_MECH ) ) { - auto *mons = mounted_creature.get(); - return quantity <= mons->battery_item->ammo_remaining(); - } return charges_of( it, quantity, filter ) == quantity; } @@ -11341,6 +11308,65 @@ bool Character::use_charges_if_avail( const itype_id &it, int quantity ) return false; } +int Character::available_ups() const +{ + int available_charges = 0; + if( is_mounted() && mounted_creature.get()->has_flag( MF_RIDEABLE_MECH ) ) { + auto *mons = mounted_creature.get(); + available_charges += mons->battery_item->ammo_remaining(); + } + if( has_active_bionic( bio_ups ) ) { + available_charges += units::to_kilojoule( get_power_level() ); + } + + for( const item *i : all_items_with_flag( flag_IS_UPS ) ) { + available_charges += i->ammo_remaining(); + } + + return available_charges; +} + +int Character::consume_ups( int qty, const int radius ) +{ + const int wanted_qty = qty; + + // UPS from mounted mech + if( qty != 0 && is_mounted() && mounted_creature.get()->has_flag( MF_RIDEABLE_MECH ) && + mounted_creature.get()->battery_item ) { + auto *mons = mounted_creature.get(); + int power_drain = std::min( mons->battery_item->ammo_remaining(), qty ); + mons->use_mech_power( -power_drain ); + qty -= std::min( qty, power_drain ); + } + + // UPS from bionic + if( qty != 0 && has_power() && has_active_bionic( bio_ups ) ) { + int bio = std::min( units::to_kilojoule( get_power_level() ), qty ); + mod_power_level( units::from_kilojoule( -bio ) ); + qty -= std::min( qty, bio ); + } + + // UPS from inventory + if( qty != 0 ) { + std::vector ups_items = all_items_with_flag( flag_IS_UPS ); + for( const item *i : ups_items ) { + qty -= const_cast( i )->ammo_consume( qty, tripoint_zero, nullptr ); + } + } + + // UPS from nearby map + if( qty != 0 && radius > 0 ) { + inventory inv = crafting_inventory( pos(), radius, true ); + + int ups = inv.charges_of( itype_UPS, qty ); + if( qty != 0 && ups > 0 ) { + qty -= get_map().consume_ups( pos(), radius, ups ); + } + } + + return wanted_qty - qty; +} + std::list Character::use_charges( const itype_id &what, int qty, const int radius, const std::function &filter ) { @@ -11363,33 +11389,9 @@ std::list Character::use_charges( const itype_id &what, int qty, const int return res; } else if( what == itype_UPS ) { - if( is_mounted() && mounted_creature.get()->has_flag( MF_RIDEABLE_MECH ) && - mounted_creature.get()->battery_item ) { - auto *mons = mounted_creature.get(); - int power_drain = std::min( mons->battery_item->ammo_remaining(), qty ); - mons->use_mech_power( -power_drain ); - qty -= std::min( qty, power_drain ); - return res; - } - if( has_power() && has_active_bionic( bio_ups ) ) { - int bio = std::min( units::to_kilojoule( get_power_level() ), qty ); - mod_power_level( units::from_kilojoule( -bio ) ); - qty -= std::min( qty, bio ); - } - - int adv = inv.charges_of( itype_adv_UPS_off, static_cast( std::ceil( qty * 0.6 ) ) ); - if( adv > 0 ) { - std::list found = use_charges( itype_adv_UPS_off, adv, radius ); - res.splice( res.end(), found ); - qty -= std::min( qty, static_cast( adv / 0.6 ) ); - } - - int ups = inv.charges_of( itype_UPS_off, qty ); - if( ups > 0 ) { - std::list found = use_charges( itype_UPS_off, ups, radius ); - res.splice( res.end(), found ); - qty -= std::min( qty, ups ); - } + // Fairly sure that nothing comes here. But handle it anyways. + debugmsg( _( "This UPS use needs updating. Create issue on github." ) ); + consume_ups( qty, radius ); return res; } @@ -11422,7 +11424,7 @@ std::list Character::use_charges( const itype_id &what, int qty, const int } if( has_tool_with_UPS ) { - use_charges( itype_UPS, qty, radius ); + consume_ups( qty, radius ); } return res; diff --git a/src/character.h b/src/character.h index cf8f38aa646e2..69aeae553cf7f 100644 --- a/src/character.h +++ b/src/character.h @@ -1415,12 +1415,6 @@ class Character : public Creature, public visitable */ virtual bool dispose_item( item_location &&obj, const std::string &prompt = std::string() ); - /** - * Has the item enough charges to invoke its use function? - * Also checks if UPS from this player is used instead of item charges. - */ - bool has_enough_charges( const item &it, bool show_msg ) const; - /** Consume charges of a tool or comestible item, potentially destroying it in the process * @param used item consuming the charges * @param qty number of charges to consume which must be non-zero @@ -2054,6 +2048,21 @@ class Character : public Creature, public visitable // Uses up charges bool use_charges_if_avail( const itype_id &it, int quantity ); + /** + * Available ups from all sources + * Sum of mech, bionic UPS and UPS + * @return amount of UPS available + */ + int available_ups() const; + + /** + * Consume UPS charges. + * Consume order: mech, Bionic UPS, UPS. + * @param qty Number of charges (kJ) + * @return amount of UPS consumed which will be between 0 and qty + */ + int consume_ups( int qty, int radius = -1 ); + /** * Use charges in character inventory. * @param what itype_id of item using charges diff --git a/src/game.cpp b/src/game.cpp index 472440d5390e0..9d15e8c1104bc 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -2116,7 +2116,7 @@ static hint_rating rate_action_use( const avatar &you, const item &it ) if( it.is_broken() ) { return hint_rating::iffy; } else if( it.is_tool() ) { - return it.ammo_sufficient() ? hint_rating::good : hint_rating::iffy; + return it.ammo_sufficient( &you ) ? hint_rating::good : hint_rating::iffy; } else if( it.is_gunmod() ) { /** @EFFECT_GUN >0 allows rating estimates for gun modifications */ if( you.get_skill_level( skill_gun ) == 0 ) { diff --git a/src/game_inventory.cpp b/src/game_inventory.cpp index 8586f1babc95b..6f161b615c73a 100644 --- a/src/game_inventory.cpp +++ b/src/game_inventory.cpp @@ -1143,7 +1143,7 @@ class activatable_inventory_preset : public pickup_inventory_preset return string_format( _( "Your %s was broken and won't turn on." ), it.tname() ); } - if( !p.has_enough_charges( it, false ) ) { + if( !it.ammo_sufficient( &p ) ) { return string_format( ngettext( "Needs at least %d charge", "Needs at least %d charges", loc->ammo_required() ), diff --git a/src/iexamine.cpp b/src/iexamine.cpp index 186e411456e84..3c2a9240f89b9 100644 --- a/src/iexamine.cpp +++ b/src/iexamine.cpp @@ -162,7 +162,6 @@ static const itype_id itype_string_36( "string_36" ); static const itype_id itype_tree_spile( "tree_spile" ); static const itype_id itype_unfinished_cac2( "unfinished_cac2" ); static const itype_id itype_unfinished_charcoal( "unfinished_charcoal" ); -static const itype_id itype_UPS( "UPS" ); static const itype_id itype_water( "water" ); static const skill_id skill_cooking( "cooking" ); @@ -2693,7 +2692,7 @@ void iexamine::arcfurnace_empty( player &p, const tripoint &examp ) return; } //arc furnaces require a huge amount of current, so 1 full storage battery would work as a stand in - if( !p.has_charges( itype_UPS, 1250 ) ) { + if( p.available_ups() < 1250 ) { add_msg( _( "This furnace is ready to be turned on, but you lack a UPS with sufficient power." ) ); return; } else { @@ -2704,7 +2703,7 @@ void iexamine::arcfurnace_empty( player &p, const tripoint &examp ) } } - p.use_charges( itype_UPS, 1250 ); + p.consume_ups( 1250 ); here.i_clear( examp ); here.furn_set( examp, next_arcfurnace_type ); item result( "unfinished_cac2", calendar::turn ); diff --git a/src/item.cpp b/src/item.cpp index fe0a3fa87c1dd..de4d11c6de067 100644 --- a/src/item.cpp +++ b/src/item.cpp @@ -141,7 +141,6 @@ static const itype_id itype_hand_crossbow( "hand_crossbow" ); static const itype_id itype_joint_roach( "joint_roach" ); static const itype_id itype_rad_badge( "rad_badge" ); static const itype_id itype_tuned_mechanism( "tuned_mechanism" ); -static const itype_id itype_UPS( "UPS" ); static const itype_id itype_waterproof_gunmod( "waterproof_gunmod" ); static const skill_id skill_cooking( "cooking" ); @@ -7950,52 +7949,45 @@ units::energy item::energy_remaining() const return 0_J; } -int item::ammo_remaining() const +int item::ammo_remaining( const Character *carrier ) const { + int ret = 0; + + // Magagzine in the item const item *mag = magazine_current(); if( mag ) { - return mag->ammo_remaining(); + ret += mag->ammo_remaining(); } - if( is_tool() ) { + // Power from bionic + if( carrier != nullptr && has_flag( flag_USES_BIONIC_POWER ) ) { + ret += units::to_kilojoule( carrier->get_power_level() ); + } - if( ammo_types().empty() || - !contents.has_pocket_type( item_pocket::pocket_type::MAGAZINE ) ) { - // includes auxiliary gunmods - if( has_flag( flag_USES_BIONIC_POWER ) ) { - int power = units::to_kilojoule( get_player_character().get_power_level() ); - return power; - } - return charges; - } else { - int res = 0; - for( const item *e : contents.all_items_top( item_pocket::pocket_type::MAGAZINE ) ) { - res += e->charges; - } - return res; - } - } else if( is_gun() && magazine_integral() && !contents.empty() ) { - return contents.first_ammo().charges; + // Weird non-item charges. Not sure if used by anything in this context + if( is_tool() && ammo_types().empty() ) { + ret += charges; } + // Extra power from UPS + if( carrier != nullptr && ( has_flag( flag_USE_UPS ) || get_gun_ups_drain() ) ) { + ret += carrier->available_ups(); + } + + // Magazines and integral magazines on their own if( is_magazine() ) { - int res = 0; for( const item *e : contents.all_items_top( item_pocket::pocket_type::MAGAZINE ) ) { - res += e->charges; + ret += e->charges; } - return res; } // Handle non-magazines with ammo_restriction in a CONTAINER type pocket (like quivers) if( !ammo_types().empty() ) { - int res = 0; for( const item *e : contents.all_items_top( item_pocket::pocket_type::CONTAINER ) ) { - res += e->charges; + ret += e->charges; } - return res; } - - return 0; + return ret; } int item::remaining_ammo_capacity() const @@ -8053,39 +8045,49 @@ int item::ammo_required() const return 0; } -bool item::ammo_sufficient( int qty ) const +bool item::ammo_sufficient( const Character *carrier, int qty ) const { - return ammo_remaining() >= ammo_required() * qty; + if( ammo_required() ) { + return ammo_remaining( carrier ) >= ammo_required() * qty; + } else if( get_gun_ups_drain() ) { + return ammo_remaining( carrier ) >= get_gun_ups_drain() * qty; + } + return true; } -int item::ammo_consume( int qty, const tripoint &pos ) +int item::ammo_consume( int qty, const tripoint &pos, Character *carrier ) { if( qty < 0 ) { debugmsg( "Cannot consume negative quantity of ammo for %s", tname() ); return 0; } + const int wanted_qty = qty; + // Consume charges loaded in the item or its magazines if( is_magazine() || contents.has_pocket_type( item_pocket::pocket_type::MAGAZINE_WELL ) ) { - return contents.ammo_consume( qty, pos ); - - } else if( is_tool() || is_gun() ) { - if( !contents.has_pocket_type( item_pocket::pocket_type::MAGAZINE ) || - ( is_tool() && type->tool->ammo_id.empty() ) ) { - qty = std::min( qty, charges ); - Character &player_character = get_player_character(); - if( has_flag( flag_USES_BIONIC_POWER ) ) { - charges = units::to_kilojoule( player_character.get_power_level() ); - player_character.mod_power_level( units::from_kilojoule( -qty ) ); - } - charges -= qty; - if( charges == 0 ) { - curammo = nullptr; - } - return qty; - } + qty -= contents.ammo_consume( qty, pos ); } - return 0; + // Some weird internal non-item charges (used by grenades) + if( is_tool() && type->tool->ammo_id.empty() ) { + int charg_used = std::min( charges, qty ); + charges -= charg_used; + qty -= charg_used; + } + + // Consume UPS power from various sources + if( carrier != nullptr && has_flag( flag_USE_UPS ) ) { + qty -= carrier->consume_ups( qty ); + } + + // Consume bio pwr directly + if( carrier != nullptr && has_flag( flag_USES_BIONIC_POWER ) ) { + int bio_used = std::min( units::to_kilojoule( carrier->get_power_level() ), qty ); + carrier->mod_power_level( -units::from_kilojoule( bio_used ) ); + qty -= bio_used; + } + + return wanted_qty - qty; } const itype *item::ammo_data() const @@ -8533,12 +8535,7 @@ int item::units_remaining( const Character &ch, int limit ) const return std::min( static_cast( charges ), limit ); } - int res = ammo_remaining(); - if( res < limit && has_flag( flag_USE_UPS ) ) { - res += ch.charges_of( itype_UPS, limit - res ); - } - - return std::min( static_cast( res ), limit ); + return std::min( ammo_remaining( &ch ), limit ); } bool item::units_sufficient( const Character &ch, int qty ) const @@ -8700,7 +8697,7 @@ bool item::reload( Character &u, item_location ammo, int qty ) item ammo_copy( ammo->contents.first_ammo() ); ammo_copy.charges = qty; put_in( ammo_copy, item_pocket::pocket_type::MAGAZINE ); - ammo->ammo_consume( qty, tripoint_zero ); + ammo->ammo_consume( qty, tripoint_zero, &u ); } else if( ammo->ammo_type() == ammo_plutonium ) { curammo = ammo->type; ammo->charges -= qty; @@ -9316,7 +9313,7 @@ bool item::use_charges( const itype_id &what, int &qty, std::list &used, item temp( *e ); temp.ammo_set( e->ammo_current(), n ); used.push_back( temp ); - e->ammo_consume( n, pos ); + e->ammo_consume( n, pos, nullptr ); } } return VisitResponse::SKIP; @@ -10417,21 +10414,14 @@ bool item::process_tool( player *carrier, const tripoint &pos ) if( type->tool->turns_per_charge > 0 && to_turn( calendar::turn ) % type->tool->turns_per_charge == 0 ) { energy = std::max( ammo_required(), 1 ); - } else if( type->tool->power_draw > 0 ) { // power_draw in mW / 1000000 to give kJ (battery unit) per second energy = type->tool->power_draw / 1000000; // energy_bat remainder results in chance at additional charge/discharge energy += x_in_y( type->tool->power_draw % 1000000, 1000000 ) ? 1 : 0; } - energy -= ammo_consume( energy, pos ); - // for items in player possession if insufficient charges within tool try UPS - if( carrier && has_flag( flag_USE_UPS ) ) { - if( carrier->use_charges_if_avail( itype_UPS, energy ) ) { - energy = 0; - } - } + energy -= ammo_consume( energy, pos, carrier ); avatar &player_character = get_avatar(); // if insufficient available charges shutdown the tool diff --git a/src/item.h b/src/item.h index 79249c5847908..5bbc1774bb38c 100644 --- a/src/item.h +++ b/src/item.h @@ -1861,41 +1861,52 @@ class item : public visitable /** Quantity of energy currently loaded in tool or battery */ units::energy energy_remaining() const; - /** Quantity of ammunition currently loaded in tool, gun or auxiliary gunmod */ - int ammo_remaining() const; + /** + * Quantity of ammunition currently loaded in tool, gun or auxiliary gunmod. Can include UPS and bionic + * If UPS/bionic power does not matter then the carrier can be nullptr + * @param carrier is used for UPS and bionic power + */ + int ammo_remaining( const Character *carrier = nullptr ) const; + /** * ammo capacity for a specific ammo */ int ammo_capacity( const ammotype &ammo ) const; + /** * how much more ammo can fit into this item * if this item is not loaded, gives remaining capacity of its default ammo */ int remaining_ammo_capacity() const; + /** Quantity of ammunition consumed per usage of tool or with each shot of gun */ int ammo_required() const; /** * Check if sufficient ammo is loaded for given number of uses. - * * Check if there is enough ammo loaded in a tool for the given number of uses - * or given number of gun shots. Using this function for this check is preferred + * or given number of gun shots. + * If carrier is provides then UPS and bionic may be also used as ammo + * Using this function for this check is preferred * because we expect to add support for items consuming multiple ammo types in * the future. Users of this function will not need to be refactored when this * happens. * - * @param[in] qty Number of uses + * @param carrier who holds the item. Needed for UPS/bionic + * @param qty Number of uses * @returns true if ammo sufficient for number of uses is loaded, false otherwise */ - bool ammo_sufficient( int qty = 1 ) const; + bool ammo_sufficient( const Character *carrier, int qty = 1 ) const; /** * Consume ammo (if available) and return the amount of ammo that was consumed + * Consume order: loaded items, UPS, bionic * @param qty maximum amount of ammo that should be consumed * @param pos current location of item, used for ejecting magazines and similar effects + * @param carrier holder of the item, used for getting UPS and bionic power * @return amount of ammo consumed which will be between 0 and qty */ - int ammo_consume( int qty, const tripoint &pos ); + int ammo_consume( int qty, const tripoint &pos, Character *carrier ); /** Specific ammo data, returns nullptr if item is neither ammo nor loaded with any */ const itype *ammo_data() const; diff --git a/src/item_action.cpp b/src/item_action.cpp index 1519c67686f14..498ac3d1f2ef4 100644 --- a/src/item_action.cpp +++ b/src/item_action.cpp @@ -40,8 +40,6 @@ class Character; static const std::string errstring( "ERROR" ); -static const itype_id itype_UPS( "UPS" ); - struct tripoint; static item_action nullaction; @@ -153,9 +151,7 @@ item_action_map item_action_generator::map_actions_to_items( player &p, func->get_actor_ptr()->can_use( p, *actual_item, false, p.pos() ).success() ) ) { continue; } - if( !actual_item->ammo_sufficient() && - ( !actual_item->has_flag( STATIC( flag_id( "USE_UPS" ) ) ) || - p.charges_of( itype_UPS ) < actual_item->ammo_required() ) ) { + if( !actual_item->ammo_sufficient( &p ) ) { continue; } diff --git a/src/item_contents.cpp b/src/item_contents.cpp index 2a561d93bc248..feffb5146b64e 100644 --- a/src/item_contents.cpp +++ b/src/item_contents.cpp @@ -725,7 +725,7 @@ int item_contents::ammo_consume( int qty, const tripoint &pos ) } // assuming only one mag item &mag = pocket.front(); - const int res = mag.ammo_consume( qty, pos ); + const int res = mag.ammo_consume( qty, pos, nullptr ); if( res && mag.ammo_remaining() == 0 ) { if( mag.has_flag( STATIC( flag_id( "MAG_DESTROY" ) ) ) ) { pocket.remove_item( mag ); diff --git a/src/iuse.cpp b/src/iuse.cpp index 95d31de9795da..7a6560b9850f1 100644 --- a/src/iuse.cpp +++ b/src/iuse.cpp @@ -225,8 +225,6 @@ static const efftype_id effect_weak_antibiotic_visible( "weak_antibiotic_visible static const efftype_id effect_webbed( "webbed" ); static const efftype_id effect_weed_high( "weed_high" ); -static const itype_id itype_adv_UPS_off( "adv_UPS_off" ); -static const itype_id itype_UPS( "UPS" ); static const itype_id itype_advanced_ecig( "advanced_ecig" ); static const itype_id itype_afs_atomic_smartphone( "afs_atomic_smartphone" ); static const itype_id itype_afs_atomic_smartphone_music( "afs_atomic_smartphone_music" ); @@ -292,7 +290,6 @@ static const itype_id itype_string_36( "string_36" ); static const itype_id itype_thermometer( "thermometer" ); static const itype_id itype_towel( "towel" ); static const itype_id itype_towel_wet( "towel_wet" ); -static const itype_id itype_UPS_off( "UPS_off" ); static const itype_id itype_water( "water" ); static const itype_id itype_water_clean( "water_clean" ); static const itype_id itype_wax( "wax" ); @@ -1940,7 +1937,7 @@ cata::optional iuse::fish_trap( player *p, item *it, bool t, const tripoint } if( fishes == 0 ) { - it->ammo_consume( it->ammo_remaining(), pos ); + it->ammo_consume( it->ammo_remaining(), pos, p ); p->practice( skill_survival, rng( 5, 15 ) ); return 0; @@ -1981,7 +1978,7 @@ cata::optional iuse::fish_trap( player *p, item *it, bool t, const tripoint } } } - it->ammo_consume( bait_consumed, pos ); + it->ammo_consume( bait_consumed, pos, p ); } return 0; } @@ -1989,7 +1986,7 @@ cata::optional iuse::fish_trap( player *p, item *it, bool t, const tripoint cata::optional iuse::extinguisher( player *p, item *it, bool, const tripoint & ) { - if( !it->ammo_sufficient() ) { + if( !it->ammo_sufficient( p ) ) { return cata::nullopt; } // If anyone other than the player wants to use one of these, @@ -2045,7 +2042,7 @@ cata::optional iuse::extinguisher( player *p, item *it, bool, const tripoin cata::optional iuse::rm13armor_off( player *p, item *it, bool, const tripoint & ) { // This allows it to turn on for a turn, because ammo_sufficient assumes non-tool non-weapons need zero ammo, for some reason. - if( !it->ammo_sufficient() ) { + if( !it->ammo_sufficient( p ) ) { p->add_msg_if_player( m_info, _( "The RM13 combat armor's fuel cells are dead." ) ); return cata::nullopt; } else { @@ -2302,8 +2299,7 @@ cata::optional iuse::radio_on( player *p, item *it, bool t, const tripoint } } else { // Activated int ch = 1; - if( it->ammo_remaining() > 0 || ( it->has_flag( flag_USE_UPS ) && - p->has_enough_charges( *it, false ) ) ) { + if( it->ammo_sufficient( p ) ) { ch = uilist( _( "Radio:" ), { _( "Scan" ), _( "Turn off" ) } ); @@ -3235,7 +3231,7 @@ static int toolweapon_on( player &p, item &it, const bool t, "_off"; if( t ) { // Effects while simply on if( double_charge_cost && it.units_sufficient( p ) ) { - it.ammo_consume( 1, p.pos() ); + it.ammo_consume( 1, p.pos(), &p ); } if( !works_underwater && p.is_underwater() ) { p.add_msg_if_player( _( "Your %s gurgles in the water and stops." ), tname ); @@ -3329,7 +3325,7 @@ cata::optional iuse::jackhammer( player *p, item *it, bool, const tripoint { // use has_enough_charges to check for UPS availability // p is assumed to exist for iuse cases - if( !p->has_enough_charges( *it, false ) ) { + if( !it->ammo_sufficient( p ) ) { return cata::nullopt; } if( p->is_mounted() ) { @@ -3596,7 +3592,7 @@ cata::optional iuse::teleport( player *p, item *it, bool, const tripoint & p->add_msg_if_player( m_info, _( "You can't do that while mounted." ) ); return cata::nullopt; } - if( !it->ammo_sufficient() ) { + if( !it->ammo_sufficient( p ) ) { return cata::nullopt; } p->moves -= to_moves( 1_seconds ); @@ -4012,7 +4008,7 @@ cata::optional iuse::mininuke( player *p, item *it, bool, const tripoint & cata::optional iuse::portal( player *p, item *it, bool, const tripoint & ) { - if( !it->ammo_sufficient() ) { + if( !it->ammo_sufficient( p ) ) { return cata::nullopt; } if( p->is_mounted() ) { @@ -4096,8 +4092,7 @@ cata::optional iuse::tazer( player *p, item *it, bool, const tripoint &pos cata::optional iuse::tazer2( player *p, item *it, bool b, const tripoint &pos ) { - if( it->ammo_remaining() >= 100 || ( it->has_flag( flag_USE_UPS ) && - p->charges_of( itype_UPS ) >= 100 ) ) { + if( it->ammo_remaining( p ) >= 100 ) { // Instead of having a ctrl+c+v of the function above, spawn a fake tazer and use it // Ugly, but less so than copied blocks item fake( "tazer", calendar::turn_zero ); @@ -4121,8 +4116,7 @@ cata::optional iuse::shocktonfa_off( player *p, item *it, bool t, const tri return iuse::tazer2( p, it, t, pos ); } case 1: { - if( !it->units_sufficient( *p ) && !( it->has_flag( flag_USE_UPS ) && - p->has_enough_charges( *it, false ) ) ) { + if( !it->units_sufficient( *p ) ) { p->add_msg_if_player( m_info, _( "The batteries are dead." ) ); return cata::nullopt; } else { @@ -4140,8 +4134,7 @@ cata::optional iuse::shocktonfa_on( player *p, item *it, bool t, const trip if( t ) { // Effects while simply on } else { - if( !it->units_sufficient( *p ) && !( it->has_flag( flag_USE_UPS ) && - p->has_enough_charges( *it, false ) ) ) { + if( !it->units_sufficient( *p ) ) { p->add_msg_if_player( m_info, _( "Your tactical tonfa is out of power." ) ); it->convert( itype_shocktonfa_off ).active = false; } else { @@ -4166,8 +4159,7 @@ cata::optional iuse::shocktonfa_on( player *p, item *it, bool t, const trip cata::optional iuse::mp3( player *p, item *it, bool, const tripoint & ) { // TODO: avoid item id hardcoding to make this function usable for pure json-defined devices. - if( !it->units_sufficient( *p ) && !( it->has_flag( flag_USE_UPS ) && - p->has_enough_charges( *it, false ) ) ) { + if( !it->units_sufficient( *p ) ) { p->add_msg_if_player( m_info, _( "The device's batteries are dead." ) ); } else if( p->has_active_item( itype_mp3_on ) || p->has_active_item( itype_smartphone_music ) || p->has_active_item( itype_afs_atomic_smartphone_music ) || @@ -4400,7 +4392,7 @@ cata::optional iuse::gasmask( player *p, item *it, bool t, const tripoint & } } if( it->get_var( "gas_absorbed", 0 ) >= 100 ) { - it->ammo_consume( 1, p->pos() ); + it->ammo_consume( 1, p->pos(), p ); it->set_var( "gas_absorbed", 0 ); } if( it->ammo_remaining() == 0 ) { @@ -5594,7 +5586,7 @@ cata::optional iuse::adrenaline_injector( player *p, item *it, bool, const cata::optional iuse::jet_injector( player *p, item *it, bool, const tripoint & ) { - if( !it->ammo_sufficient() ) { + if( !it->ammo_sufficient( p ) ) { p->add_msg_if_player( m_info, _( "The jet injector is empty." ) ); return cata::nullopt; } else { @@ -5622,7 +5614,7 @@ cata::optional iuse::stimpack( player *p, item *it, bool, const tripoint & return cata::nullopt; } - if( !it->ammo_sufficient() ) { + if( !it->ammo_sufficient( p ) ) { p->add_msg_if_player( m_info, _( "The stimulant delivery system is empty." ) ); return cata::nullopt; } else { @@ -5842,8 +5834,7 @@ cata::optional iuse::toolmod_attach( player *p, item *it, bool, const tripo auto filter = [&it]( const item & e ) { // don't allow ups battery mods on a UPS or UPS-powered tools if( it->has_flag( flag_USE_UPS ) && - ( e.typeId() == itype_UPS_off || e.typeId() == itype_adv_UPS_off || - e.has_flag( flag_USE_UPS ) ) ) { + ( e.has_flag( flag_IS_UPS ) || e.has_flag( flag_USE_UPS ) ) ) { return false; } @@ -6288,11 +6279,9 @@ cata::optional iuse::einktabletpc( player *p, item *it, bool t, const tripo { if( t ) { if( !it->get_var( "EIPC_MUSIC_ON" ).empty() && - ( it->ammo_remaining() > 0 || ( it->has_flag( flag_USE_UPS ) && - p->has_enough_charges( *it, false ) ) ) ) { + it->ammo_sufficient( p ) ) { if( calendar::once_every( 5_minutes ) ) { - //it->ammo_consume( 1, p->pos() ); - p->consume_charges( *it, 1 ); + it->ammo_consume( 1, p->pos(), p ); } //the more varied music, the better max mood. @@ -7862,7 +7851,7 @@ cata::optional iuse::radiocar( player *p, item *it, bool, const tripoint & } if( choice == 0 ) { //Turn car ON - if( !it->ammo_sufficient() ) { + if( !it->ammo_sufficient( p ) ) { p->add_msg_if_player( _( "The RC car's batteries seem to be dead." ) ); return cata::nullopt; } @@ -7926,7 +7915,7 @@ cata::optional iuse::radiocaron( player *p, item *it, bool t, const tripoin sounds::sound( pos, 6, sounds::sound_t::movement, _( "buzzz…" ), true, "misc", "rc_car_drives" ); return it->type->charges_to_use(); - } else if( !it->ammo_sufficient() ) { + } else if( !it->ammo_sufficient( p ) ) { // Deactivate since other mode has an iuse too. it->active = false; return 0; @@ -8354,8 +8343,7 @@ cata::optional iuse::autoclave( player *p, item *it, bool t, const tripoint //Using power_draw seem to consume random amount of battery so +100 to be safe static const int power_need = ( ( it->type->tool->power_draw / 1000 ) * to_seconds ( 90_minutes ) ) / 1000 + 100; - if( power_need > it->ammo_remaining() && !( it->has_flag( flag_USE_UPS ) && - p->charges_of( itype_UPS ) >= power_need ) ) { + if( power_need > it->ammo_remaining( p ) ) { popup( _( "The autoclave doesn't have enough battery for one cycle. You need at least %s charges." ), power_need ); return cata::nullopt; @@ -8407,13 +8395,12 @@ cata::optional iuse::multicooker( player *p, item *it, bool t, const tripoi if( t ) { //stop action before power runs out and iuse deletes the cooker - if( !( it->ammo_remaining() >= charge_buffer ) && !( it->has_flag( flag_USE_UPS ) && - p->charges_of( itype_UPS ) >= charge_buffer ) ) { + if( it->ammo_remaining( p ) < charge_buffer ) { it->active = false; it->erase_var( "RECIPE" ); it->convert( itype_multi_cooker ); //drain the buffer amount given at activation - it->ammo_consume( charge_buffer, pos ); + it->ammo_consume( charge_buffer, pos, p ); p->add_msg_if_player( m_info, _( "Batteries low, entering standby mode. With a low buzzing sound the multi-cooker shuts down." ) ); return 0; @@ -8502,8 +8489,7 @@ cata::optional iuse::multicooker( player *p, item *it, bool t, const tripoi menu.addentry( mc_stop, true, 's', _( "Stop cooking" ) ); } else { if( dish_it == nullptr ) { - if( it->ammo_remaining() < charges_to_start && !( it->has_flag( flag_USE_UPS ) && - p->charges_of( itype_UPS ) >= charges_to_start ) ) { + if( it->ammo_remaining( p ) < charges_to_start ) { p->add_msg_if_player( _( "Batteries are low." ) ); return 0; } @@ -8639,8 +8625,7 @@ cata::optional iuse::multicooker( player *p, item *it, bool t, const tripoi const int all_charges = charges_to_start + ( ( mealtime / 100 ) * ( it->type->tool->power_draw / 1000 ) ) / 1000; - if( it->ammo_remaining() < all_charges && !( it->has_flag( flag_USE_UPS ) && - p->charges_of( itype_UPS ) >= all_charges ) ) { + if( it->ammo_remaining( p ) < all_charges ) { p->add_msg_if_player( m_warning, _( "The multi-cooker needs %d charges to cook this dish." ), @@ -8668,7 +8653,7 @@ cata::optional iuse::multicooker( player *p, item *it, bool t, const tripoi _( "The screen flashes blue symbols and scales as the multi-cooker begins to shake." ) ); it->convert( itype_multi_cooker_filled ).active = true; - it->ammo_consume( charges_to_start - charge_buffer, pos ); + it->ammo_consume( charges_to_start - charge_buffer, pos, p ); p->practice( skill_cooking, meal->difficulty * 3 ); //little bonus @@ -8884,7 +8869,7 @@ cata::optional iuse::cable_attach( player *p, item *it, bool, const tripoin const bool has_solar_pack = p->worn_with_flag( flag_SOLARPACK ); const bool has_solar_pack_on = p->worn_with_flag( flag_SOLARPACK_ON ); const bool wearing_solar_pack = has_solar_pack || has_solar_pack_on; - const bool has_ups = p->has_charges( itype_UPS_off, 1 ) || p->has_charges( itype_adv_UPS_off, 1 ); + const bool has_ups = !( p->all_items_with_flag( flag_IS_UPS ) ).empty(); item_location loc; avatar *you = p->as_avatar(); @@ -9127,7 +9112,7 @@ cata::optional iuse::shavekit( player *p, item *it, bool, const tripoint & p->add_msg_if_player( m_info, _( "You can't do that while mounted." ) ); return cata::nullopt; } - if( !it->ammo_sufficient() ) { + if( !it->ammo_sufficient( p ) ) { p->add_msg_if_player( _( "You need soap to use this." ) ); } else { p->assign_activity( player_activity( shave_activity_actor() ) ); diff --git a/src/iuse_actor.cpp b/src/iuse_actor.cpp index 612655403f362..a511e0fec3e9e 100644 --- a/src/iuse_actor.cpp +++ b/src/iuse_actor.cpp @@ -1241,7 +1241,7 @@ ret_val firestarter_actor::can_use( const Character &p, const item &it, bo return ret_val::make_failure( _( "You can't do that while underwater." ) ); } - if( it.ammo_remaining() < it.ammo_required() ) { + if( !it.ammo_sufficient( &p ) ) { return ret_val::make_failure( _( "This tool doesn't have enough charges." ) ); } @@ -4367,7 +4367,7 @@ cata::optional sew_advanced_actor::use( player &p, item &it, bool, const tr return t; }; // Mod not already present, check if modification is possible - if( !it.ammo_sufficient( thread_needed ) ) { + if( !it.ammo_sufficient( &p, thread_needed ) ) { //~ %1$s: modification desc, %2$d: number of thread needed prompt = string_format( _( "Can't %1$s (need %2$d thread loaded)" ), tolower( obj.implement_prompt.translated() ), thread_needed ); diff --git a/src/map.cpp b/src/map.cpp index 0bfe60628765d..31bbc8e411b32 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -5008,6 +5008,30 @@ std::list map::use_charges( const tripoint &origin, const int range, return ret; } +int map::consume_ups( const tripoint &origin, const int range, int qty ) +{ + // populate a grid of spots that can be reached + std::vector reachable_pts; + reachable_flood_steps( reachable_pts, origin, range, 1, 100 ); + + for( const tripoint &p : reachable_pts ) { + if( accessible_items( p ) ) { + + map_stack items = i_at( p ); + for( auto &elem : items ) { + if( elem.has_flag( flag_IS_UPS ) ) { + qty -= elem.ammo_consume( qty, p, nullptr ); + if( qty == 0 ) { + break; + } + } + } + } + } + + return qty; +} + std::list > map::get_rc_items( const tripoint &p ) { std::list > rc_pairs; diff --git a/src/map.h b/src/map.h index 2b65f3f6b5d72..841b4d195ea5b 100644 --- a/src/map.h +++ b/src/map.h @@ -1283,6 +1283,15 @@ class map std::list use_charges( const tripoint &origin, int range, const itype_id &type, int &quantity, const std::function &filter = return_true, basecamp *bcp = nullptr ); + + /** + * Consume UPS from UPS sources from area centered at origin. + * @param origin the position of player + * @param range how far the UPS can be used from + * @return Amount of UPS used which will be between 0 and qty + */ + int consume_ups( const tripoint &origin, int range, int qty ); + /*@}*/ std::list > get_rc_items( const tripoint &p = { -1, -1, -1 } ); diff --git a/src/mattack_actors.cpp b/src/mattack_actors.cpp index 40fb40fd7b845..33bb13206c1a4 100644 --- a/src/mattack_actors.cpp +++ b/src/mattack_actors.cpp @@ -593,7 +593,7 @@ void gun_actor::shoot( monster &z, Creature &target, const gun_mode_id &mode ) c return; } - if( !gun.ammo_sufficient() ) { + if( !gun.ammo_sufficient( nullptr ) ) { if( !no_ammo_sound.empty() ) { sounds::sound( z.pos(), 10, sounds::sound_t::combat, no_ammo_sound ); } diff --git a/src/monster.cpp b/src/monster.cpp index 625c9090d0324..37f4e7a84650c 100644 --- a/src/monster.cpp +++ b/src/monster.cpp @@ -206,7 +206,7 @@ monster::monster( const mtype_id &id ) : monster() const itype &type = *item::find_type( mech_bat ); int max_charge = type.magazine->capacity; item mech_bat_item = item( mech_bat, calendar::turn_zero ); - mech_bat_item.ammo_consume( rng( 0, max_charge ), tripoint_zero ); + mech_bat_item.ammo_consume( rng( 0, max_charge ), tripoint_zero, nullptr ); battery_item = cata::make_value( mech_bat_item ); } } @@ -2505,7 +2505,7 @@ bool monster::use_mech_power( int amt ) return false; } amt = -amt; - battery_item->ammo_consume( amt, pos() ); + battery_item->ammo_consume( amt, pos(), nullptr ); return battery_item->ammo_remaining() > 0; } diff --git a/src/npc.cpp b/src/npc.cpp index 41541a33c9f47..a2850f8ac3f60 100644 --- a/src/npc.cpp +++ b/src/npc.cpp @@ -97,8 +97,6 @@ static const efftype_id effect_pkill3( "pkill3" ); static const efftype_id effect_ridden( "ridden" ); static const efftype_id effect_riding( "riding" ); -static const itype_id itype_UPS_off( "UPS_off" ); - static const skill_id skill_archery( "archery" ); static const skill_id skill_bashing( "bashing" ); static const skill_id skill_cutting( "cutting" ); @@ -1434,8 +1432,7 @@ void npc::decide_needs() if( weapon.is_gun() ) { int ups_drain = weapon.get_gun_ups_drain(); if( ups_drain > 0 ) { - int ups_charges = charges_of( itype_UPS_off, ups_drain ) + - charges_of( itype_UPS_off, ups_drain ); + int ups_charges = available_ups(); needrank[need_ammo] = static_cast( ups_charges ) / ups_drain; } else { const ammotype ammo_type = weapon.ammo_type(); diff --git a/src/npc_attack.cpp b/src/npc_attack.cpp index 3534a29ea271e..b0f74194165ba 100644 --- a/src/npc_attack.cpp +++ b/src/npc_attack.cpp @@ -189,7 +189,7 @@ npc_attack_rating npc_attack_melee::evaluate_critter( const npc &source, void npc_attack_gun::use( npc &source, const tripoint &location ) const { const item &weapon = *gunmode; - if( !weapon.ammo_sufficient() ) { + if( !weapon.ammo_sufficient( &source ) ) { source.do_reload( weapon ); add_msg_debug( debugmode::debug_filter::DF_NPC, "%s is reloading %s", source.disp_name(), weapon.display_name() ); @@ -223,7 +223,7 @@ int npc_attack_gun::base_time_penalty( const npc &source ) const time_penalty += npc_attack_constants::base_time_penalty; } // we want the need to reload a gun cumulative with needing to wield the gun - if( !weapon.ammo_sufficient() ) { + if( !weapon.ammo_sufficient( &source ) ) { time_penalty += npc_attack_constants::base_time_penalty; } int recoil_penalty = 0; diff --git a/src/npcmove.cpp b/src/npcmove.cpp index 1315c106e1510..0d9a03a0095ba 100644 --- a/src/npcmove.cpp +++ b/src/npcmove.cpp @@ -125,7 +125,6 @@ static const itype_id itype_lsd( "lsd" ); static const itype_id itype_smoxygen_tank( "smoxygen_tank" ); static const itype_id itype_thorazine( "thorazine" ); static const itype_id itype_oxygen_tank( "oxygen_tank" ); -static const itype_id itype_UPS( "UPS" ); static const material_id material_battery( "battery" ); static const material_id material_chem_ethanol( "chem_ethanol" ); @@ -1430,7 +1429,7 @@ void npc::evaluate_best_weapon( const Creature *target ) // punching things is always available compare( std::make_shared( null_item_reference() ) ); - const int ups_charges = charges_of( itype_UPS ); + const int ups_charges = available_ups(); visit_items( [&compare, &ups_charges, this]( item * it, item * ) { // you can theoretically melee with anything. compare( std::make_shared( *it ) ); @@ -3331,7 +3330,7 @@ bool npc::wield_better_weapon() item *best = &weapon; double best_value = -100.0; - const int ups_charges = charges_of( itype_UPS ); + const int ups_charges = available_ups(); const auto compare_weapon = [this, &best, &best_value, ups_charges, can_use_gun, use_silent]( const item & it ) { @@ -3623,7 +3622,7 @@ void npc::heal_self() const auto filter_use = [this]( const std::string & filter ) -> std::vector { const auto inv_filtered = items_with( [&filter]( const item & itm ) { - return ( itm.type->get_use( filter ) != nullptr ) && ( itm.ammo_sufficient() ); + return ( itm.type->get_use( filter ) != nullptr ) && ( itm.ammo_sufficient( nullptr ) ); } ); return inv_filtered; }; @@ -3640,7 +3639,7 @@ void npc::heal_self() } if( treatment != nullptr ) { treatment->get_use( iusage )->call( *this, *treatment, treatment->active, pos() ); - treatment->ammo_consume( treatment->ammo_required(), pos() ); + treatment->ammo_consume( treatment->ammo_required(), pos(), this ); return; } } diff --git a/src/player.cpp b/src/player.cpp index 1397a89e938c7..6923c7943ea7b 100644 --- a/src/player.cpp +++ b/src/player.cpp @@ -93,12 +93,9 @@ static const efftype_id effect_onfire( "onfire" ); static const efftype_id effect_sleep( "sleep" ); static const efftype_id effect_stunned( "stunned" ); -static const itype_id itype_adv_UPS_off( "adv_UPS_off" ); static const itype_id itype_battery( "battery" ); static const itype_id itype_large_repairkit( "large_repairkit" ); static const itype_id itype_small_repairkit( "small_repairkit" ); -static const itype_id itype_UPS( "UPS" ); -static const itype_id itype_UPS_off( "UPS_off" ); static const trait_id trait_DEBUG_NODMG( "DEBUG_NODMG" ); static const trait_id trait_CENOBITE( "CENOBITE" ); @@ -1152,18 +1149,7 @@ void player::process_items() } // Active item processing done, now we're recharging. - int ch_UPS = 0; - const auto inv_is_ups = items_with( []( const item & itm ) { - return itm.has_flag( flag_IS_UPS ); - } ); - for( const auto &it : inv_is_ups ) { - itype_id identifier = it->type->get_id(); - if( identifier == itype_UPS_off ) { - ch_UPS += it->ammo_remaining(); - } else if( identifier == itype_adv_UPS_off ) { - ch_UPS += it->ammo_remaining() / 0.6; - } - } + bool update_required = get_check_encumbrance(); for( item &w : worn ) { if( !update_required && w.encumbrance_update_ ) { @@ -1175,34 +1161,32 @@ void player::process_items() calc_encumbrance(); set_check_encumbrance( false ); } - if( has_active_bionic( bionic_id( "bio_ups" ) ) ) { - ch_UPS += units::to_kilojoule( get_power_level() ); - } - int ch_UPS_used = 0; // Load all items that use the UPS to their minimal functional charge, // The tool is not really useful if its charges are below charges_to_use const auto inv_use_ups = items_with( []( const item & itm ) { return itm.has_flag( flag_USE_UPS ); } ); - for( const auto &it : inv_use_ups ) { - // For powered armor, an armor-powering bionic should always be preferred over UPS usage. - if( it->is_power_armor() && can_interface_armor() && has_power() ) { - // Bionic power costs are handled elsewhere - continue; - //this is for UPS-modded items with no battery well - } else if( it->active && !it->ammo_sufficient() && - ( ch_UPS_used >= ch_UPS || - it->ammo_required() > ch_UPS - ch_UPS_used ) ) { - it->deactivate(); - } else if( ch_UPS_used < ch_UPS && - it->ammo_remaining() < it->ammo_capacity( ammotype( "battery" ) ) ) { - ch_UPS_used++; - it->ammo_set( itype_battery, it->ammo_remaining() + 1 ); - } - } - if( ch_UPS_used > 0 ) { - use_charges( itype_UPS, ch_UPS_used ); + if( !inv_use_ups.empty() ) { + const int available_charges = available_ups(); + int ups_used = 0; + for( const auto &it : inv_use_ups ) { + // For powered armor, an armor-powering bionic should always be preferred over UPS usage. + if( it->is_power_armor() && can_interface_armor() && has_power() ) { + // Bionic power costs are handled elsewhere + continue; + } else if( it->active && !it->ammo_sufficient( this ) ) { + it->deactivate(); + } else if( ups_used < available_charges && + it->ammo_remaining() < it->ammo_capacity( ammotype( "battery" ) ) ) { + // Charge the battery in the UPS modded tool + ups_used++; + it->ammo_set( itype_battery, it->ammo_remaining() + 1 ); + } + } + if( ups_used > 0 ) { + consume_ups( ups_used ); + } } } diff --git a/src/ranged.cpp b/src/ranged.cpp index ce40ce2c39740..b0965052efcd8 100644 --- a/src/ranged.cpp +++ b/src/ranged.cpp @@ -85,14 +85,11 @@ static const itype_id itype_40x46mm( "40x46mm" ); static const itype_id itype_40x53mm( "40x53mm" ); static const itype_id itype_66mm( "66mm" ); static const itype_id itype_84x246mm( "84x246mm" ); -static const itype_id itype_adv_UPS_off( "adv_UPS_off" ); static const itype_id itype_arrow( "arrow" ); static const itype_id itype_bolt( "bolt" ); static const itype_id itype_flammable( "flammable" ); static const itype_id itype_m235( "m235" ); static const itype_id itype_metal_rail( "metal_rail" ); -static const itype_id itype_UPS( "UPS" ); -static const itype_id itype_UPS_off( "UPS_off" ); static const trap_str_id tr_practice_target( "tr_practice_target" ); @@ -107,7 +104,6 @@ static const skill_id skill_launcher( "launcher" ); static const skill_id skill_throw( "throw" ); static const bionic_id bio_railgun( "bio_railgun" ); -static const bionic_id bio_ups( "bio_ups" ); static const std::string flag_MOUNTABLE( "MOUNTABLE" ); @@ -703,7 +699,7 @@ void npc::pretend_fire( npc *source, int shots, item &gun ) } while( curshot != shots ) { const int required = gun.ammo_required(); - if( gun.ammo_consume( required, pos() ) != required ) { + if( gun.ammo_consume( required, pos(), this ) != required ) { debugmsg( "Unexpected shortage of ammo whilst firing %s", gun.tname().c_str() ); break; } @@ -740,7 +736,7 @@ int player::fire_gun( const tripoint &target, int shots, item &gun ) // cap our maximum burst size by the amount of UPS power left if( !gun.has_flag( flag_VEHICLE ) && gun.get_gun_ups_drain() > 0 ) { - shots = std::min( shots, static_cast( charges_of( itype_UPS ) / gun.get_gun_ups_drain() ) ); + shots = std::min( shots, static_cast( available_ups() / gun.get_gun_ups_drain() ) ); } if( shots <= 0 ) { @@ -810,7 +806,7 @@ int player::fire_gun( const tripoint &target, int shots, item &gun ) } const int required = gun.ammo_required(); - if( gun.ammo_consume( required, pos() ) != required ) { + if( gun.ammo_consume( required, pos(), this ) != required ) { debugmsg( "Unexpected shortage of ammo whilst firing %s", gun.tname() ); break; } @@ -820,7 +816,7 @@ int player::fire_gun( const tripoint &target, int shots, item &gun ) } if( !gun.has_flag( flag_VEHICLE ) ) { - use_charges( itype_UPS, gun.get_gun_ups_drain() ); + consume_ups( gun.get_gun_ups_drain() ); } if( shot.missed_by <= .1 ) { @@ -3495,8 +3491,8 @@ bool gunmode_checks_weapon( avatar &you, const map &m, std::vector const gun_mode &gmode ) { bool result = true; - - if( !gmode->ammo_sufficient() && !gmode->has_flag( flag_RELOAD_AND_SHOOT ) ) { + if( !gmode->ammo_sufficient( &you ) && + !gmode->has_flag( flag_RELOAD_AND_SHOOT ) ) { if( !gmode->ammo_remaining() ) { messages.push_back( string_format( _( "Your %s is empty!" ), gmode->tname() ) ); } else { @@ -3517,17 +3513,14 @@ bool gunmode_checks_weapon( avatar &you, const map &m, std::vector } } if( !is_mech_weapon ) { - if( !( you.has_charges( itype_UPS_off, ups_drain ) || - you.has_charges( itype_adv_UPS_off, adv_ups_drain ) || - ( you.has_active_bionic( bio_ups ) && - you.get_power_level() >= units::from_kilojoule( ups_drain ) ) ) ) { + if( you.available_ups() < ups_drain ) { messages.push_back( string_format( - _( "You need a UPS with at least %2$d charges or an advanced UPS with at least %3$d charges to fire the %1$s!" ), + _( "You need a UPS with at least %2$d charges to fire the %1$s!" ), gmode->tname(), ups_drain, adv_ups_drain ) ); result = false; } } else { - if( !you.has_charges( itype_UPS, ups_drain ) ) { + if( you.available_ups() < ups_drain ) { messages.push_back( string_format( _( "Your mech has an empty battery, its %s will not fire." ), gmode->tname() ) ); result = false; diff --git a/src/turret.cpp b/src/turret.cpp index 7e623560be269..85e9e042c2fce 100644 --- a/src/turret.cpp +++ b/src/turret.cpp @@ -245,7 +245,7 @@ turret_data::status turret_data::query() const } } else { - if( !part->base.ammo_sufficient() ) { + if( !part->base.ammo_sufficient( nullptr ) ) { return status::no_ammo; } } diff --git a/src/vehicle_part.cpp b/src/vehicle_part.cpp index 07c8c54cc7962..2c316a1ef22fa 100644 --- a/src/vehicle_part.cpp +++ b/src/vehicle_part.cpp @@ -312,7 +312,7 @@ int vehicle_part::ammo_consume( int qty, const tripoint &pos ) } return res; } - return base.ammo_consume( qty, pos ); + return base.ammo_consume( qty, pos, nullptr ); } double vehicle_part::consume_energy( const itype_id &ftype, double energy_j ) diff --git a/src/visitable.cpp b/src/visitable.cpp index cd8d4e61e0f9a..af0a0f8a54957 100644 --- a/src/visitable.cpp +++ b/src/visitable.cpp @@ -38,7 +38,6 @@ #include "vehicle_selector.h" static const itype_id itype_apparatus( "apparatus" ); -static const itype_id itype_adv_UPS_off( "adv_UPS_off" ); static const itype_id itype_UPS( "UPS" ); static const itype_id itype_UPS_off( "UPS_off" ); @@ -753,35 +752,39 @@ static int charges_of_internal( const T &self, const M &main, const itype_id &id bool found_tool_with_UPS = false; self.visit_items( [&]( const item * e, item * ) { - if( !e->is_broken() && filter( *e ) ) { - if( e->is_tool() ) { - if( e->typeId() == id ) { - // includes charges from any included magazine. + if( filter( *e ) && id == e->typeId() && !e->is_broken() ) { + if( id != itype_UPS_off ) { + if( e->count_by_charges() ) { + qty = sum_no_wrap( qty, e->charges ); + } else { qty = sum_no_wrap( qty, e->ammo_remaining() ); - if( e->has_flag( STATIC( flag_id( "USE_UPS" ) ) ) ) { - found_tool_with_UPS = true; - } } - if( !e->is_container() ) { - return qty < limit ? VisitResponse::SKIP : VisitResponse::ABORT; + if( e->has_flag( STATIC( flag_id( "USE_UPS" ) ) ) ) { + found_tool_with_UPS = true; } - - } else if( e->count_by_charges() ) { - if( e->typeId() == id ) { - qty = sum_no_wrap( qty, e->charges ); + if( e->has_flag( STATIC( flag_id( "USES_BIONIC_POWER" ) ) ) ) { + qty = sum_no_wrap( qty, units::to_kilojoule( get_player_character().get_power_level() ) ); } - // items counted by charges are not themselves expected to be containers - return qty < limit ? VisitResponse::SKIP : VisitResponse::ABORT; + } else if( id == itype_UPS_off && e->has_flag( STATIC( flag_id( "IS_UPS" ) ) ) ) { + qty = sum_no_wrap( qty, e->ammo_remaining() ); } } - // recurse through any nested containers - return qty < limit ? VisitResponse::NEXT : VisitResponse::ABORT; + if( qty >= limit ) { + return VisitResponse::ABORT; + } + // recurse through nested containers if any + return e->is_container() ? VisitResponse::NEXT : VisitResponse::SKIP; } ); + if( found_tool_with_UPS && qty < limit && get_player_character().has_active_bionic( bio_ups ) ) { + qty = sum_no_wrap( qty, units::to_kilojoule( get_player_character().get_power_level() ) ); + } + if( qty < limit && found_tool_with_UPS ) { - qty += main.charges_of( itype_UPS, limit - qty ); + int used_ups = main.charges_of( itype_UPS, limit - qty ); + qty += used_ups; if( visitor ) { - visitor( qty ); + visitor( used_ups ); } } @@ -796,7 +799,6 @@ int read_only_visitable::charges_of( const itype_id &what, int limit, if( what == itype_UPS ) { int qty = 0; qty = sum_no_wrap( qty, charges_of( itype_UPS_off ) ); - qty = sum_no_wrap( qty, static_cast( charges_of( itype_adv_UPS_off ) / 0.6 ) ); return std::min( qty, limit ); } @@ -811,10 +813,10 @@ int inventory::charges_of( const itype_id &what, int limit, if( what == itype_UPS ) { int qty = 0; qty = sum_no_wrap( qty, charges_of( itype_UPS_off ) ); - qty = sum_no_wrap( qty, static_cast( charges_of( itype_adv_UPS_off ) / 0.6 ) ); return std::min( qty, limit ); } - const auto &binned = get_binned_items(); + + const itype_bin &binned = get_binned_items(); const auto iter = binned.find( what ); if( iter == binned.end() ) { return 0; @@ -840,26 +842,17 @@ int Character::charges_of( const itype_id &what, int limit, for( const auto &bio : *this->my_bionics ) { const bionic_data &bid = bio.info(); if( bid.fake_item == what && ( !bid.activated || bio.powered ) ) { - return std::min( units::to_kilojoule( p->get_power_level() ), limit ); + return std::min( item( bid.fake_item ).ammo_remaining( p ), limit ); } } if( what == itype_UPS ) { - int qty = 0; - qty = sum_no_wrap( qty, charges_of( itype_UPS_off ) ); - qty = sum_no_wrap( qty, static_cast( charges_of( itype_adv_UPS_off ) / 0.6 ) ); - if( p && p->has_active_bionic( bio_ups ) ) { - qty = sum_no_wrap( qty, units::to_kilojoule( p->get_power_level() ) ); - } - if( p && p->is_mounted() ) { - const auto *mons = p->mounted_creature.get(); - if( mons->has_flag( MF_RIDEABLE_MECH ) && mons->battery_item ) { - qty = sum_no_wrap( qty, mons->battery_item->ammo_remaining() ); - } + int ups_power = available_ups(); + if( has_active_bionic( bio_ups ) ) { + ups_power -= units::to_kilojoule( get_power_level() ); } - return std::min( qty, limit ); + return std::min( ups_power, limit ); } - return charges_of_internal( *this, *this, what, limit, filter, visitor ); } diff --git a/tests/crafting_test.cpp b/tests/crafting_test.cpp index 682ab774e58a8..513fc5c45a271 100644 --- a/tests/crafting_test.cpp +++ b/tests/crafting_test.cpp @@ -407,8 +407,7 @@ TEST_CASE( "UPS shows as a crafting component", "[crafting][ups]" ) item &ups = dummy.i_add( item( "UPS_off", calendar::turn_zero, 500 ) ); REQUIRE( dummy.has_item( ups ) ); REQUIRE( ups.charges == 500 ); - REQUIRE( dummy.charges_of( itype_id( "UPS_off" ) ) == 500 ); - REQUIRE( dummy.charges_of( itype_id( "UPS" ) ) == 500 ); + REQUIRE( dummy.available_ups() == 500 ); } TEST_CASE( "tools use charge to craft", "[crafting][charge]" )