diff --git a/data/json/furniture_and_terrain/furniture-storage.json b/data/json/furniture_and_terrain/furniture-storage.json index 8841b06631984..4bb233b63f02f 100644 --- a/data/json/furniture_and_terrain/furniture-storage.json +++ b/data/json/furniture_and_terrain/furniture-storage.json @@ -294,7 +294,7 @@ "required_str": 14, "max_volume": "250 L", "flags": [ "TRANSPARENT", "CONTAINER", "SEALED", "PLACE_ITEM", "MOUNTABLE", "MINEABLE" ], - "examine_action": "gunsafe_ml", + "examine_action": "locked_object_pickable", "bash": { "str_min": 40, "str_max": 200, diff --git a/data/json/itemgroups/books.json b/data/json/itemgroups/books.json index c94c383fcc18e..9cb45466e6591 100644 --- a/data/json/itemgroups/books.json +++ b/data/json/itemgroups/books.json @@ -410,7 +410,8 @@ { "item": "textbook_weapeast", "prob": 3 }, { "item": "textbook_armwest", "prob": 3 }, { "item": "textbook_armeast", "prob": 3 }, - { "item": "survnote", "prob": 2 } + { "item": "survnote", "prob": 2 }, + { "item": "book_lockpick", "prob": 2 } ] }, { @@ -463,6 +464,7 @@ { "item": "collector_book", "prob": 30 }, { "item": "carpentry_book", "prob": 5 }, { "item": "jewelry_book", "prob": 5 }, + { "item": "book_lockpick", "prob": 2 }, { "group": "religious_books", "prob": 40 } ] }, @@ -510,6 +512,7 @@ { "item": "manual_wingchun", "prob": 3 }, { "item": "manual_medievalpole", "prob": 3 }, { "item": "manual_swordsmanship", "prob": 3 }, + { "item": "book_lockpick", "prob": 1 }, { "group": "rare_martial_arts_books", "prob": 6 } ] }, diff --git a/data/json/items/armor/jewelry.json b/data/json/items/armor/jewelry.json index ad00f9a433336..844cfe0d507d6 100644 --- a/data/json/items/armor/jewelry.json +++ b/data/json/items/armor/jewelry.json @@ -295,7 +295,9 @@ "material": [ "copper" ], "symbol": "[", "looks_like": "tieclip", - "color": "brown" + "color": "brown", + "use_action": { "type": "picklock", "pick_quality": 3 }, + "qualities": [ [ "LOCKPICK", 3 ] ] }, { "id": "copper_locket", @@ -793,7 +795,9 @@ "material": [ "gold" ], "symbol": "[", "color": "yellow", - "flags": [ "FANCY" ] + "flags": [ "FANCY" ], + "use_action": { "type": "picklock", "pick_quality": 3 }, + "qualities": [ [ "LOCKPICK", 3 ] ] }, { "id": "gold_locket", @@ -1090,7 +1094,9 @@ "material": [ "platinum" ], "symbol": "[", "color": "white", - "flags": [ "FANCY" ] + "flags": [ "FANCY" ], + "use_action": { "type": "picklock", "pick_quality": 3 }, + "qualities": [ [ "LOCKPICK", 3 ] ] }, { "id": "platinum_locket", @@ -1216,7 +1222,9 @@ "material": [ "silver" ], "symbol": "[", "color": "light_gray", - "flags": [ "FANCY" ] + "flags": [ "FANCY" ], + "use_action": { "type": "picklock", "pick_quality": 3 }, + "qualities": [ [ "LOCKPICK", 3 ] ] }, { "id": "silver_locket", diff --git a/data/json/items/book/lockpick.json b/data/json/items/book/lockpick.json new file mode 100644 index 0000000000000..f4a2e1a953113 --- /dev/null +++ b/data/json/items/book/lockpick.json @@ -0,0 +1,19 @@ +[ + { + "id": "book_lockpick", + "type": "BOOK", + "name": { "str": "MIT Guide to Lock Picking", "str_pl": "copies of MIT Guide to Lock Picking" }, + "description": "A home-made booklet with large black-and-white pictures of several types of modern locks and general information on how to crack them open.", + "weight": "400 g", + "volume": "500 ml", + "price": 100, + "price_postapoc": 5000, + "material": [ "paper" ], + "symbol": "?", + "color": "green", + "skill": "lockpick", + "max_level": 2, + "intelligence": 8, + "time": "20 m" + } +] diff --git a/data/json/items/tool/entry_tools.json b/data/json/items/tool/entry_tools.json index 4478b5e423409..e17d7d56d0993 100644 --- a/data/json/items/tool/entry_tools.json +++ b/data/json/items/tool/entry_tools.json @@ -29,6 +29,7 @@ "material": "steel", "symbol": ";", "color": "light_gray", + "qualities": [ [ "LOCKPICK", 3 ] ], "use_action": { "type": "picklock", "pick_quality": 3 } }, { @@ -80,6 +81,7 @@ "material": "steel", "symbol": ";", "color": "light_gray", + "qualities": [ [ "LOCKPICK", 5 ] ], "use_action": { "type": "picklock", "pick_quality": 5 } }, { @@ -94,6 +96,7 @@ "initial_charges": 1, "max_charges": 1, "charges_per_use": 1, + "qualities": [ [ "LOCKPICK", 40 ] ], "use_action": { "type": "picklock", "pick_quality": 40 } } ] diff --git a/data/json/items/tool_armor.json b/data/json/items/tool_armor.json index 5c3f9083a6a6e..eb779f3309274 100644 --- a/data/json/items/tool_armor.json +++ b/data/json/items/tool_armor.json @@ -1767,7 +1767,8 @@ "price": 50, "material": [ "iron" ], "weight": "2 g", - "use_action": { "type": "picklock", "pick_quality": 3 } + "use_action": { "type": "picklock", "pick_quality": 3 }, + "qualities": [ [ "LOCKPICK", 3 ] ] }, { "id": "fc_hairpin", @@ -1781,6 +1782,7 @@ "material": [ "plastic" ], "weight": "4 g", "use_action": { "type": "picklock", "pick_quality": 1 }, + "qualities": [ [ "LOCKPICK", 1 ] ], "flags": [ "FANCY" ] }, { diff --git a/data/json/player_activities.json b/data/json/player_activities.json index 4227d096d000c..3be9f008c4200 100644 --- a/data/json/player_activities.json +++ b/data/json/player_activities.json @@ -540,6 +540,14 @@ "rooted": true, "based_on": "speed" }, + { + "id": "ACT_LOCKPICK", + "type": "activity_type", + "activity_level": "NO_EXERCISE", + "verb": "picking lock", + "rooted": true, + "based_on": "speed" + }, { "id": "ACT_REPAIR_ITEM", "type": "activity_type", diff --git a/data/json/professions.json b/data/json/professions.json index 4b8506e5f7442..b454fa38088f1 100644 --- a/data/json/professions.json +++ b/data/json/professions.json @@ -384,6 +384,7 @@ { "level": 2, "name": "gun" }, { "level": 2, "name": "rifle" }, { "level": 2, "name": "melee" }, + { "level": 2, "name": "lockpick" }, { "level": 2, "name": "cutting" }, { "level": 1, "name": "bashing" } ], @@ -607,7 +608,8 @@ { "level": 1, "name": "stabbing" }, { "level": 1, "name": "unarmed" }, { "level": 1, "name": "dodge" }, - { "level": 1, "name": "barter" } + { "level": 1, "name": "barter" }, + { "level": 1, "name": "lockpick" } ], "items": { "both": { @@ -783,6 +785,7 @@ { "level": 2, "name": "cutting" }, { "level": 2, "name": "melee" }, { "level": 2, "name": "dodge" }, + { "level": 2, "name": "lockpick" }, { "level": 1, "name": "firstaid" }, { "level": 1, "name": "swimming" }, { "level": 1, "name": "survival" } @@ -1005,7 +1008,8 @@ { "level": 2, "name": "fabrication" }, { "level": 2, "name": "cooking" }, { "level": 2, "name": "firstaid" }, - { "level": 2, "name": "swimming" } + { "level": 2, "name": "swimming" }, + { "level": 1, "name": "lockpick" } ], "items": { "both": { @@ -1483,7 +1487,7 @@ "name": { "male": "Handy Man", "female": "Handy Woman" }, "description": "You used to work at a local hardware store, and you did a lot of home renovations yourself. Now you look out at the horizon of a ruined world, and wonder - are your meager skills, and the few supplies you grabbed on the way out, sufficient to help it rebuild?", "points": 1, - "skills": [ { "level": 2, "name": "fabrication" } ], + "skills": [ { "level": 2, "name": "fabrication" }, { "level": 1, "name": "lockpick" } ], "items": { "both": { "items": [ "tank_top", "socks", "boots_steel", "pants", "multitool", "wristwatch" ], @@ -1661,7 +1665,7 @@ "description": "You have done many high profile heists, but your gains mean nothing in this world. All you have left are the tools of your trade and your impeccable style.", "points": 4, "CBMs": [ "bio_batteries", "bio_lockpick", "bio_fingerhack", "bio_power_storage_mkII" ], - "skills": [ { "level": 1, "name": "gun" }, { "level": 1, "name": "smg" } ], + "skills": [ { "level": 1, "name": "gun" }, { "level": 1, "name": "smg" }, { "level": 3, "name": "lockpick" } ], "items": { "both": { "items": [ "waistcoat", "pants", "dress_shirt", "socks", "dress_shoes", "knit_scarf", "gold_watch", "smart_phone" ], @@ -2417,7 +2421,7 @@ "name": "Convict", "description": "The Cataclysm gave you a chance to escape, but freedom comes with a steep price.", "points": 0, - "skills": [ { "level": 1, "name": "melee" } ], + "skills": [ { "level": 1, "name": "melee" }, { "level": 1, "name": "lockpick" } ], "items": { "both": [ "striped_shirt", "striped_pants", "sneakers", "socks", "glass_shiv" ], "male": [ "briefs" ], @@ -2508,6 +2512,7 @@ "name": "Burglar", "description": "You thought this would be your lucky break. Does it count as breaking and entering if everyone in town is undead?", "points": 3, + "skills": [ { "level": 4, "name": "lockpick" } ], "//": "A ski mask would fit the stereotype better, but wool allergy breaks this.", "items": { "both": [ @@ -3390,7 +3395,12 @@ "name": "Survivalist Jr.", "description": "Your parents were crazy preppers who thought some \"Cataclysm\" was coming, and insisted on preparing you for it. Turns out they were right. You didn't get a chance to thank them. The only thing you can do for them now is what they always hoped you would do in the dark days ahead: survive.", "points": 3, - "skills": [ { "level": 1, "name": "survival" }, { "level": 1, "name": "fabrication" }, { "level": 1, "name": "firstaid" } ], + "skills": [ + { "level": 1, "name": "survival" }, + { "level": 1, "name": "fabrication" }, + { "level": 1, "name": "firstaid" }, + { "level": 1, "name": "lockpick" } + ], "items": { "both": { "items": [ diff --git a/data/json/skills.json b/data/json/skills.json index b42605cb8b96b..97e0476ee1b1b 100644 --- a/data/json/skills.json +++ b/data/json/skills.json @@ -276,6 +276,14 @@ "display_category": "display_melee", "companion_skill_practice": [ { "skill": "hunting", "weight": 10 }, { "skill": "combat", "weight": 10 } ] }, + { + "type": "skill", + "ident": "lockpick", + "name": { "str": "lock picking" }, + "description": "Your skill in picking all sorts of locks which require physical keys - from simple pin tumbler locks to intricate disc tumbler locks. Skill increases chance and decreases time required to successfully pick the lock.", + "companion_survival_rank_factor": 1, + "display_category": "display_interaction" + }, { "type": "skill", "ident": "weapon", diff --git a/data/json/tool_qualities.json b/data/json/tool_qualities.json index 1e85781869bd7..ed354a4dec26b 100644 --- a/data/json/tool_qualities.json +++ b/data/json/tool_qualities.json @@ -261,5 +261,10 @@ "type": "tool_quality", "id": "PRESSURIZATION", "name": { "str": "pressurizing" } + }, + { + "type": "tool_quality", + "id": "LOCKPICK", + "name": { "str": "lockpicking" } } ] diff --git a/doxygen_doc/doxygen_conf.txt b/doxygen_doc/doxygen_conf.txt index 3c76e6da0b6dd..e08a825be1217 100644 --- a/doxygen_doc/doxygen_conf.txt +++ b/doxygen_doc/doxygen_conf.txt @@ -242,6 +242,7 @@ ALIASES += "EFFECT_PISTOL=\xrefitem Effects_Skill_Pistol \"\" \"\" Pistol" ALIASES += "EFFECT_RIFLE=\xrefitem Effects_Skill_Rifle \"\" \"\" Rifle" ALIASES += "EFFECT_SHOTGUN=\xrefitem Effects_Skill_Shotgun \"\" \"\" Shotgun" ALIASES += "EFFECT_SMG=\xrefitem Effects_Skill_Smg \"\" \"\" Smg" +ALIASES += "EFFECT_LOCKPICK=\xrefitem Effects_Skill_Lockpick \"\" \"\" Lock Picking" ALIASES += "EFFECT_BARTER_NPC=\xrefitem Effects_Skill_Barter \"\" \"\" NPC Barter" ALIASES += "EFFECT_COMPUTER_NPC=\xrefitem Effects_Skill_Computer \"\" \"\" NPC Computer" @@ -271,6 +272,7 @@ ALIASES += "EFFECT_PISTOL_NPC=\xrefitem Effects_Skill_Pistol \"\" \"\" NPC Pisto ALIASES += "EFFECT_RIFLE_NPC=\xrefitem Effects_Skill_Rifle \"\" \"\" NPC Rifle" ALIASES += "EFFECT_SHOTGUN_NPC=\xrefitem Effects_Skill_Shotgun \"\" \"\" NPC Shotgun" ALIASES += "EFFECT_SMG_NPC=\xrefitem Effects_Skill_Smg \"\" \"\" NPC Smg" +ALIASES += "EFFECT_LOCKPICK_NPC=\xrefitem Effects_Skill_Lockpick \"\" \"\" NPC Lock Picking" # This tag can be used to specify a number of word-keyword mappings (TCL only). # A mapping has the form "name=value". For example adding diff --git a/doxygen_doc/pages.h b/doxygen_doc/pages.h index 9f83933b3b9dd..a61cec74b49ca 100644 --- a/doxygen_doc/pages.h +++ b/doxygen_doc/pages.h @@ -152,3 +152,7 @@ * @brief Cross referenced effects of the Smg skill. * @par */ + *! @page Effects_Skill_Lockpick + * @brief Cross referenced effects of the Lock picking skill. + * @par + */ diff --git a/src/activity_handlers.cpp b/src/activity_handlers.cpp index 05cbe71813368..f9905d45b167c 100644 --- a/src/activity_handlers.cpp +++ b/src/activity_handlers.cpp @@ -147,6 +147,7 @@ static const activity_id ACT_HAND_CRANK( "ACT_HAND_CRANK" ); static const activity_id ACT_HEATING( "ACT_HEATING" ); static const activity_id ACT_HOTWIRE_CAR( "ACT_HOTWIRE_CAR" ); static const activity_id ACT_JACKHAMMER( "ACT_JACKHAMMER" ); +static const activity_id ACT_LOCKPICK( "ACT_LOCKPICK" ); static const activity_id ACT_LONGSALVAGE( "ACT_LONGSALVAGE" ); static const activity_id ACT_MAKE_ZLAVE( "ACT_MAKE_ZLAVE" ); static const activity_id ACT_MEDITATE( "ACT_MEDITATE" ); @@ -214,10 +215,13 @@ static const skill_id skill_computer( "computer" ); static const skill_id skill_electronics( "electronics" ); static const skill_id skill_fabrication( "fabrication" ); static const skill_id skill_firstaid( "firstaid" ); +static const skill_id skill_lockpick( "lockpick" ); +static const skill_id skill_mechanics( "mechanics" ); static const skill_id skill_survival( "survival" ); static const quality_id qual_BUTCHER( "BUTCHER" ); static const quality_id qual_CUT_FINE( "CUT_FINE" ); +static const quality_id qual_LOCKPICK( "LOCKPICK" ); static const quality_id qual_SAW_M( "SAW_M" ); static const quality_id qual_SAW_W( "SAW_W" ); @@ -236,6 +240,7 @@ static const mtype_id mon_zombie_crawler( "mon_zombie_crawler" ); static const bionic_id bio_ears( "bio_ears" ); static const bionic_id bio_fingerhack( "bio_fingerhack" ); +static const bionic_id bio_lockpick( "bio_lockpick" ); static const bionic_id bio_painkiller( "bio_painkiller" ); static const trait_id trait_DEBUG_HS( "DEBUG_HS" ); @@ -371,6 +376,7 @@ activity_handlers::finish_functions = { { ACT_OXYTORCH, oxytorch_finish }, { ACT_PULP, pulp_finish }, { ACT_CRACKING, cracking_finish }, + { ACT_LOCKPICK, lockpicking_finish }, { ACT_OPEN_GATE, open_gate_finish }, { ACT_REPAIR_ITEM, repair_item_finish }, { ACT_HEATING, heat_item_finish }, @@ -2527,6 +2533,90 @@ void activity_handlers::cracking_finish( player_activity *act, player *p ) act->set_to_null(); } +void activity_handlers::lockpicking_finish( player_activity *act, player *p ) +{ + item_location &loc = act->targets[ 0 ]; + item *it = loc.get_item(); + if( it == nullptr ) { + debugmsg( "lockpick item location lost" ); + return; + } + + const ter_id ter_type = g->m.ter( act->placement ); + const furn_id furn_type = g->m.furn( act->placement ); + ter_id new_ter_type; + furn_id new_furn_type; + std::string open_message; + if( ter_type == t_chaingate_l ) { + new_ter_type = t_chaingate_c; + open_message = _( "With a satisfying click, the chain-link gate opens." ); + } else if( ter_type == t_door_locked || ter_type == t_door_locked_alarm || + ter_type == t_door_locked_interior ) { + new_ter_type = t_door_c; + open_message = _( "With a satisfying click, the lock on the door opens." ); + } else if( ter_type == t_door_locked_peep ) { + new_ter_type = t_door_c_peep; + open_message = _( "With a satisfying click, the lock on the door opens." ); + } else if( ter_type == t_door_metal_pickable ) { + new_ter_type = t_door_metal_c; + open_message = _( "With a satisfying click, the lock on the door opens." ); + } else if( ter_type == t_door_bar_locked ) { + new_ter_type = t_door_bar_o; + //Bar doors auto-open (and lock if closed again) so show a different message) + open_message = _( "The door swings open…" ); + } else if( furn_type == f_gunsafe_ml ) { + new_furn_type = f_safe_o; + open_message = _( "With a satisfying click, the lock on the door opens." ); + } else { + act->set_to_null(); + } + + bool destroy = false; + + /** @EFFECT_DEX improves chances of successfully picking door lock, reduces chances of bad outcomes */ + /** @EFFECT_MECHANICS improves chances of successfully picking door lock, reduces chances of bad outcomes */ + /** @EFFECT_LOCKPICK greatly improves chances of successfully picking door lock, reduces chances of bad outcomes */ + int pick_roll = std::pow( 1.5, p->get_skill_level( skill_lockpick ) ) * + ( std::pow( 1.3, p->get_skill_level( skill_mechanics ) ) + + it->get_quality( qual_LOCKPICK ) - it->damage() / 2000 ) + + p->dex_cur / 4; + int lock_roll = rng( 1, 120 ); + if( pick_roll >= lock_roll ) { + p->practice( skill_lockpick, lock_roll ); + g->m.has_furn( act->placement ) ? + g->m.furn_set( act->placement, new_furn_type ) : + static_cast( g->m.ter_set( act->placement, new_ter_type ) ); + p->add_msg_if_player( m_good, open_message ); + } else if( furn_type == f_gunsafe_ml && lock_roll > ( 3 * pick_roll ) ) { + p->add_msg_if_player( m_bad, _( "Your clumsy attempt jams the lock!" ) ); + g->m.furn_set( act->placement, furn_str_id( "f_gunsafe_mj" ) ); + } else if( lock_roll > ( 1.5 * pick_roll ) ) { + if( it->inc_damage() ) { + p->add_msg_if_player( m_bad, + _( "The lock stumps your efforts to pick it, and you destroy your tool." ) ); + destroy = true; + } else { + p->add_msg_if_player( m_bad, + _( "The lock stumps your efforts to pick it, and you damage your tool." ) ); + } + } else { + p->add_msg_if_player( m_bad, _( "The lock stumps your efforts to pick it." ) ); + } + if( ter_type == t_door_locked_alarm && ( lock_roll + dice( 1, 30 ) ) > pick_roll ) { + sounds::sound( p->pos(), 40, sounds::sound_t::alarm, _( "an alarm sound!" ), true, "environment", + "alarm" ); + if( !g->timed_events.queued( TIMED_EVENT_WANTED ) ) { + g->timed_events.add( TIMED_EVENT_WANTED, calendar::turn + 30_minutes, 0, + p->global_sm_location() ); + } + } + if( destroy ) { + p->i_rem( it ); + } + + act->set_to_null(); +} + void activity_handlers::open_gate_finish( player_activity *act, player * ) { // Don't use reference and don't inline, because act can change diff --git a/src/activity_handlers.h b/src/activity_handlers.h index 3faf67d96b31e..e6ce8e82b1594 100644 --- a/src/activity_handlers.h +++ b/src/activity_handlers.h @@ -224,6 +224,7 @@ void churn_finish( player_activity *act, player *p ); void plant_seed_finish( player_activity *act, player *p ); void oxytorch_finish( player_activity *act, player *p ); void cracking_finish( player_activity *act, player *p ); +void lockpicking_finish( player_activity *act, player *p ); void open_gate_finish( player_activity *act, player * ); void repair_item_finish( player_activity *act, player *p ); void mend_item_finish( player_activity *act, player *p ); diff --git a/src/iexamine.cpp b/src/iexamine.cpp index 15eba0cd09a4a..3737f8a9805ae 100644 --- a/src/iexamine.cpp +++ b/src/iexamine.cpp @@ -110,6 +110,7 @@ static const skill_id skill_cooking( "cooking" ); static const skill_id skill_electronics( "electronics" ); static const skill_id skill_fabrication( "fabrication" ); static const skill_id skill_firstaid( "firstaid" ); +static const skill_id skill_lockpick( "lockpick" ); static const skill_id skill_mechanics( "mechanics" ); static const skill_id skill_survival( "survival" ); @@ -1288,52 +1289,6 @@ void iexamine::safe( player &p, const tripoint &examp ) } } -/** - * Attempt to pick the gunsafe's lock and open it. - */ -void iexamine::gunsafe_ml( player &p, const tripoint &examp ) -{ - int pick_quality = 0; - - if( p.has_amount( "picklocks", 1 ) || p.has_bionic( bio_lockpick ) ) { - pick_quality = 5; - } else if( p.has_amount( "crude_picklock", 1 ) || p.has_amount( "hairpin", 1 ) ) { - pick_quality = 3; - } else if( p.has_amount( "fc_hairpin", 1 ) ) { - pick_quality = 1; - } - - if( pick_quality == 0 ) { - add_msg( m_info, _( "You need a lockpick to open this gun safe." ) ); - return; - } else if( !query_yn( _( "Pick the gun safe?" ) ) ) { - return; - } - - p.practice( skill_mechanics, 1 ); - - ///\EFFECT_DEX speeds up lock picking gun safe - ///\EFFECT_MECHANICS speeds up lock picking gun safe - p.moves -= std::max( 0, to_turns( 10_minutes - time_duration::from_minutes( pick_quality ) ) - - ( p.dex_cur + p.get_skill_level( skill_mechanics ) ) * 5 ); - - ///\EFFECT_DEX increases chance of lock picking gun safe - ///\EFFECT_MECHANICS increases chance of lock picking gun safe - int pick_roll = ( dice( 2, p.get_skill_level( skill_mechanics ) ) + dice( 2, - p.dex_cur ) ) * pick_quality; - int door_roll = dice( 4, 30 ); - if( pick_roll >= door_roll ) { - p.practice( skill_mechanics, 1 ); - add_msg( _( "You successfully unlock the gun safe." ) ); - g->m.furn_set( examp, furn_str_id( "f_safe_o" ) ); - } else if( door_roll > ( 3 * pick_roll ) ) { - add_msg( _( "Your clumsy attempt jams the lock!" ) ); - g->m.furn_set( examp, furn_str_id( "f_gunsafe_mj" ) ); - } else { - add_msg( _( "The gun safe stumps your efforts to pick it." ) ); - } -} - /** * Attempt to "hack" the gunsafe's electronic lock and open it. */ @@ -1392,7 +1347,7 @@ void iexamine::locked_object_pickable( player &p, const tripoint &examp ) if( picklocks.empty() ) { add_msg( m_info, _( "The %s is locked. If only you had something to pick its lock with…" ), - g->m.tername( examp ) ); + g->m.has_furn( examp ) ? g->m.furnname( examp ) : g->m.tername( examp ) ); return; } @@ -1409,7 +1364,7 @@ void iexamine::locked_object_pickable( player &p, const tripoint &examp ) const auto actor = dynamic_cast ( it->type->get_use( "picklock" )->get_actor_ptr() ); p.add_msg_if_player( _( "You attempt to pick lock of %1$s using your %2$s…" ), - g->m.tername( examp ), it->tname() ); + g->m.has_furn( examp ) ? g->m.furnname( examp ) : g->m.tername( examp ), it->tname() ); const ret_val can_use = actor->can_use( p, *it, false, examp ); if( can_use.success() ) { actor->use( p, *it, false, examp ); @@ -5802,7 +5757,6 @@ iexamine_function iexamine_function_from_string( const std::string &function_nam { "curtains", &iexamine::curtains }, { "sign", &iexamine::sign }, { "pay_gas", &iexamine::pay_gas }, - { "gunsafe_ml", &iexamine::gunsafe_ml }, { "gunsafe_el", &iexamine::gunsafe_el }, { "locked_object", &iexamine::locked_object }, { "locked_object_pickable", &iexamine::locked_object_pickable }, diff --git a/src/iexamine.h b/src/iexamine.h index 54a723da5e499..a5823b2186364 100644 --- a/src/iexamine.h +++ b/src/iexamine.h @@ -48,7 +48,6 @@ void pit( player &p, const tripoint &examp ); void pit_covered( player &p, const tripoint &examp ); void slot_machine( player &p, const tripoint &examp ); void safe( player &p, const tripoint &examp ); -void gunsafe_ml( player &p, const tripoint &examp ); void gunsafe_el( player &p, const tripoint &examp ); void harvest_furn_nectar( player &p, const tripoint &examp ); void harvest_furn( player &p, const tripoint &examp ); diff --git a/src/iuse_actor.cpp b/src/iuse_actor.cpp index 4259b73e3bd77..4244592467e57 100644 --- a/src/iuse_actor.cpp +++ b/src/iuse_actor.cpp @@ -103,6 +103,7 @@ static const bionic_id bio_syringe( "bio_syringe" ); static const skill_id skill_fabrication( "fabrication" ); static const skill_id skill_firstaid( "firstaid" ); +static const skill_id skill_lockpick( "lockpick" ); static const skill_id skill_mechanics( "mechanics" ); static const skill_id skill_survival( "survival" ); @@ -125,6 +126,8 @@ static const trait_id trait_SELFAWARE( "SELFAWARE" ); static const trait_id trait_SMALL_OK( "SMALL_OK" ); static const trait_id trait_SMALL2( "SMALL2" ); +static const quality_id qual_LOCKPICK( "LOCKPICK" ); + static const std::string flag_FIT( "FIT" ); static const std::string flag_OVERSIZE( "OVERSIZE" ); static const std::string flag_UNDERSIZE( "UNDERSIZE" ); @@ -1053,13 +1056,20 @@ int pick_lock_actor::use( player &p, item &it, bool, const tripoint & ) const t_door_metal_pickable, t_door_bar_locked }; - const std::function f = [&allowed_ter_id]( const tripoint & pnt ) { + const std::set allowed_furn_id { + f_gunsafe_ml + }; + + const std::function f = [&allowed_ter_id, + &allowed_furn_id]( const tripoint & pnt ) { if( pnt == g->u.pos() ) { return false; } - const ter_id type = g->m.ter( pnt ); - const bool is_allowed_terrain = allowed_ter_id.find( type ) != allowed_ter_id.end(); - return is_allowed_terrain; + const ter_id ter = g->m.ter( pnt ); + const furn_id furn = g->m.furn( pnt ); + const bool is_allowed = allowed_ter_id.find( ter ) != allowed_ter_id.end() || + allowed_furn_id.find( furn ) != allowed_furn_id.end(); + return is_allowed; }; const cata::optional pnt_ = choose_adjacent_highlight( @@ -1074,7 +1084,7 @@ int pick_lock_actor::use( player &p, item &it, bool, const tripoint & ) const p.add_msg_if_player( m_info, _( "You pick your nose and your sinuses swing open." ) ); } else if( g->critter_at( pnt ) ) { p.add_msg_if_player( m_info, - _( "You can pick your friends, and you can\npick your nose, but you can't pick\nyour friend's nose" ) ); + _( "You can pick your friends, and you can\npick your nose, but you can't pick\nyour friend's nose." ) ); } else if( type == t_door_c ) { p.add_msg_if_player( m_info, _( "That door isn't locked." ) ); } else { @@ -1083,70 +1093,17 @@ int pick_lock_actor::use( player &p, item &it, bool, const tripoint & ) const return 0; } - ter_id new_type; - std::string open_message; - if( type == t_chaingate_l ) { - new_type = t_chaingate_c; - open_message = _( "With a satisfying click, the chain-link gate opens." ); - } else if( type == t_door_locked || type == t_door_locked_alarm || - type == t_door_locked_interior ) { - new_type = t_door_c; - open_message = _( "With a satisfying click, the lock on the door opens." ); - } else if( type == t_door_locked_peep ) { - new_type = t_door_c_peep; - open_message = _( "With a satisfying click, the lock on the door opens." ); - } else if( type == t_door_metal_pickable ) { - new_type = t_door_metal_c; - open_message = _( "With a satisfying click, the lock on the door opens." ); - } else if( type == t_door_bar_locked ) { - new_type = t_door_bar_o; - //Bar doors auto-open (and lock if closed again) so show a different message) - open_message = _( "The door swings open…" ); - } else { - return 0; - } - p.practice( skill_mechanics, 1 ); - /** @EFFECT_DEX speeds up door lock picking */ - /** @EFFECT_MECHANICS speeds up door lock picking */ - p.moves -= std::max( 0, to_turns( 10_minutes - time_duration::from_minutes( pick_quality ) ) - - ( p.dex_cur + p.get_skill_level( skill_mechanics ) ) * 5 ); - - bool destroy = false; - - /** @EFFECT_DEX improves chances of successfully picking door lock, reduces chances of bad outcomes */ - /** @EFFECT_MECHANICS improves chances of successfully picking door lock, reduces chances of bad outcomes */ - int pick_roll = ( dice( 2, p.get_skill_level( skill_mechanics ) ) + dice( 2, - p.dex_cur ) - it.damage_level( 4 ) / 2 ) * pick_quality; - int door_roll = dice( 4, 30 ); - if( pick_roll >= door_roll ) { - p.practice( skill_mechanics, 1 ); - p.add_msg_if_player( m_good, open_message ); - g->m.ter_set( pnt, new_type ); - } else if( door_roll > ( 1.5 * pick_roll ) ) { - if( it.inc_damage() ) { - p.add_msg_if_player( m_bad, - _( "The lock stumps your efforts to pick it, and you destroy your tool." ) ); - destroy = true; - } else { - p.add_msg_if_player( m_bad, - _( "The lock stumps your efforts to pick it, and you damage your tool." ) ); - } - } else { - p.add_msg_if_player( m_bad, _( "The lock stumps your efforts to pick it." ) ); - } - if( type == t_door_locked_alarm && ( door_roll + dice( 1, 30 ) ) > pick_roll ) { - sounds::sound( p.pos(), 40, sounds::sound_t::alarm, _( "an alarm sound!" ), true, "environment", - "alarm" ); - if( !g->timed_events.queued( TIMED_EVENT_WANTED ) ) { - g->timed_events.add( TIMED_EVENT_WANTED, calendar::turn + 30_minutes, 0, - p.global_sm_location() ); - } - } - if( destroy ) { - p.i_rem( &it ); - return 0; - } + /** @EFFECT_LOCKPICK speeds up door lock picking */ + const int duration = std::max( 0, + to_moves( 10_minutes - time_duration::from_minutes( it.get_quality( qual_LOCKPICK ) ) ) - + ( p.dex_cur + p.get_skill_level( skill_lockpick ) ) * 2300 ); + p.practice( skill_lockpick, std::pow( 2, p.get_skill_level( skill_lockpick ) ) + 1 ); + + p.assign_activity( activity_id( "ACT_LOCKPICK" ), duration, -1, p.get_item_position( &it ) ); + p.activity.targets.push_back( item_location( p, &it ) ); + p.activity.placement = pnt; + return it.type->charges_to_use(); } diff --git a/src/mapdata.cpp b/src/mapdata.cpp index 8ad81f2459add..15010d88d49d0 100644 --- a/src/mapdata.cpp +++ b/src/mapdata.cpp @@ -941,6 +941,7 @@ furn_id f_null, f_washer, f_dryer, f_vending_c, f_vending_o, f_dumpster, f_dive_block, f_crate_c, f_crate_o, f_coffin_c, f_coffin_o, + f_gunsafe_ml, f_large_canvas_wall, f_canvas_wall, f_canvas_door, f_canvas_door_o, f_groundsheet, f_fema_groundsheet, f_large_groundsheet, f_large_canvas_door, f_large_canvas_door_o, f_center_groundsheet, f_skin_wall, f_skin_door, @@ -1082,6 +1083,7 @@ void set_furn_ids() f_tourist_table = furn_id( "f_tourist_table" ); f_camp_chair = furn_id( "f_camp_chair" ); f_sign = furn_id( "f_sign" ); + f_gunsafe_ml = furn_id( "f_gunsafe_ml" ); } size_t ter_t::count() diff --git a/src/mapdata.h b/src/mapdata.h index 0d250f8376923..65e7ad8c1dd42 100644 --- a/src/mapdata.h +++ b/src/mapdata.h @@ -565,7 +565,8 @@ extern furn_id f_null, f_firering, f_tourist_table, f_camp_chair, - f_sign; + f_sign, + f_gunsafe_ml; /////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //// These are on their way OUT and only used in certain switch statements until they are rewritten.