From abe74db7cb8d1787a1ef8ef3ebb5443708e005b7 Mon Sep 17 00:00:00 2001 From: Kevin Granade Date: Tue, 1 Dec 2020 21:26:22 -0800 Subject: [PATCH 01/43] Proportional part salvaging --- src/iuse_actor.cpp | 82 ++++++++++++++++++++++++++++++++++------------ 1 file changed, 61 insertions(+), 21 deletions(-) diff --git a/src/iuse_actor.cpp b/src/iuse_actor.cpp index a4e4ccc02b617..c372c842dad69 100644 --- a/src/iuse_actor.cpp +++ b/src/iuse_actor.cpp @@ -1359,11 +1359,40 @@ int salvage_actor::use( player &p, item &it, bool t, const tripoint & ) const return cut_up( p, it, item_loc ); } -static const units::volume minimal_volume_to_cut = 250_ml; +// Helper to visit instances of all the sub-materials of an item. +static void visit_salvage_products( const item &it, std::function func ) +{ + for( const material_id &material : it.made_of() ) { + if( const cata::optional id = material->salvaged_into() ) { + item exemplar( *id ); + func( exemplar ); + } + } +} + +// Helper to find smallest sub-component of an item. +static units::mass minimal_weight_to_cut( const item &it ) +{ + units::mass min_weight = units::mass_max; + visit_salvage_products( it, [&min_weight]( const item & exemplar ) { + min_weight = std::min( min_weight, exemplar.weight() ); + } ); + return min_weight; +} int salvage_actor::time_to_cut_up( const item &it ) const { - int count = it.volume() / minimal_volume_to_cut; + units::mass total_material_weight; + int num_materials = 0; + visit_salvage_products( it, [&total_material_weight, &num_materials]( const item & exemplar ) { + total_material_weight += exemplar.weight(); + num_materials += 1; + } ); + if( num_materials == 0 ) { + return 0; + } + units::mass average_material_weight = total_material_weight / num_materials; + int count = it.weight() / average_material_weight; return moves_per_part * count; } @@ -1382,7 +1411,7 @@ bool salvage_actor::valid_to_cut_up( const item &it ) const if( !it.contents.empty() ) { return false; } - if( it.volume() < minimal_volume_to_cut ) { + if( it.weight() < minimal_weight_to_cut( it ) ) { return false; } @@ -1416,7 +1445,7 @@ bool salvage_actor::try_to_cut_up( player &p, item &it ) const add_msg( m_info, _( "Please empty the %s before cutting it up." ), it.tname() ); return false; } - if( it.volume() < minimal_volume_to_cut ) { + if( it.weight() < minimal_weight_to_cut( it ) ) { add_msg( m_info, _( "The %s is too small to salvage material from." ), it.tname() ); return false; } @@ -1440,9 +1469,8 @@ bool salvage_actor::try_to_cut_up( player &p, item &it ) const int salvage_actor::cut_up( player &p, item &it, item_location &cut ) const { const bool filthy = cut.get_item()->is_filthy(); - // total number of raw components == total volume of item. - // This can go awry if there is a volume / recipe mismatch. - int count = cut.get_item()->volume() / minimal_volume_to_cut; + // This is the value that tracks progress, as we cut pieces off, we reduce this number. + units::mass remaining_weight = cut.get_item()->weight(); // Chance of us losing a material component to entropy. /** @EFFECT_FABRICATION reduces chance of losing components when cutting items up */ int entropy_threshold = std::max( 5, 10 - p.get_skill_level( skill_fabrication ) ); @@ -1462,39 +1490,49 @@ int salvage_actor::cut_up( player &p, item &it, item_location &cut ) const return 0; } - // Time based on number of components. - p.moves -= moves_per_part * count; // Not much practice, and you won't get very far ripping things up. p.practice( skill_fabrication, rng( 0, 5 ), 1 ); // Higher fabrication, less chance of entropy, but still a chance. if( rng( 1, 10 ) <= entropy_threshold ) { - count -= 1; + remaining_weight *= 0.99; } // Fail dex roll, potentially lose more parts. /** @EFFECT_DEX randomly reduces component loss when cutting items up */ if( dice( 3, 4 ) > p.dex_cur ) { - count -= rng( 0, 2 ); + remaining_weight *= 0.95; } // If more than 1 material component can still be salvaged, // chance of losing more components if the item is damaged. // If the item being cut is not damaged, no additional losses will be incurred. - if( count > 0 && cut.get_item()->damage() > 0 ) { + if( cut.get_item()->damage() > 0 ) { float component_success_chance = std::min( std::pow( 0.8, cut.get_item()->damage_level() ), 1.0 ); - for( int i = count; i > 0; i-- ) { - if( component_success_chance < rng_float( 0, 1 ) ) { - count--; - } - } + remaining_weight *= component_success_chance; } - // Decided to split components evenly. Since salvage will likely change - // soon after I write this, I'll go with the one that is cleaner. + // Essentially we round-robbin through the components subtracting mass as we go. + std::map weight_to_item_map; for( const material_id &material : cut_material_components ) { - if( const auto id = material->salvaged_into() ) { - materials_salvaged[*id] = std::max( 0, count / static_cast( cut_material_components.size() ) ); + if( const cata::optional id = material->salvaged_into() ) { + materials_salvaged[*id] = 0; + weight_to_item_map[ item( *id, calendar::turn_zero, item::solitary_tag{} ).weight() ] = *id; + } + } + while( remaining_weight > 0_gram && !weight_to_item_map.empty() ) { + units::mass components_weight = std::accumulate( weight_to_item_map.begin(), + weight_to_item_map.end(), 0_gram, []( const units::mass & a, + const std::pair &b ) { + return a + b.first; + } ); + if( components_weight > 0_gram && components_weight <= remaining_weight ) { + int count = remaining_weight / components_weight; + for( std::pair mat_pair : weight_to_item_map ) { + materials_salvaged[mat_pair.second] += count; + } + remaining_weight -= components_weight * count; } + weight_to_item_map.erase( std::prev( weight_to_item_map.end() ) ); } add_msg( m_info, _( "You try to salvage materials from the %s." ), @@ -1516,6 +1554,8 @@ int salvage_actor::cut_up( player &p, item &it, item_location &cut ) const int amount = salvaged.second; item result( mat_name, calendar::turn ); if( amount > 0 ) { + // Time based on number of components. + p.moves -= moves_per_part; if( result.count_by_charges() ) { result.charges = amount; amount = 1; From 438bb9073a011a25b4e61d9bed2ba26f47e0df17 Mon Sep 17 00:00:00 2001 From: Kevin Granade Date: Sat, 5 Dec 2020 00:45:48 -0800 Subject: [PATCH 02/43] Add test for cut_up yields --- tests/iuse_actor_test.cpp | 77 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 77 insertions(+) diff --git a/tests/iuse_actor_test.cpp b/tests/iuse_actor_test.cpp index 2cdbd655b755b..33fe9083606e5 100644 --- a/tests/iuse_actor_test.cpp +++ b/tests/iuse_actor_test.cpp @@ -99,3 +99,80 @@ TEST_CASE( "tool transform when activated", "[iuse][tool][transform]" ) } } } + +static void cut_up_yields( const std::string &target ) +{ + map &here = get_map(); + player &guy = get_avatar(); + clear_avatar(); + // Nominal dex to avoid yield penalty. + guy.dex_cur = 12; + //guy.set_skill_level( skill_id( "fabrication" ), 10 ); + here.i_at( guy.pos() ).clear(); + + CAPTURE( target ); + salvage_actor test_actor; + item cut_up_target{ target }; + item tool{ "knife_butcher" }; + const std::vector &target_materials = cut_up_target.made_of(); + units::mass smallest_yield_mass = units::mass_max; + for( const material_id &mater : target_materials ) { + if( const cata::optional item_id = mater->salvaged_into() ) { + smallest_yield_mass = std::min( smallest_yield_mass, item_id->obj().weight ); + } + } + REQUIRE( smallest_yield_mass != units::mass_max ); + + units::mass cut_up_target_mass = cut_up_target.weight(); + item &spawned_item = here.add_item_or_charges( guy.pos(), cut_up_target ); + item_location item_loc( map_cursor( guy.pos() ), &spawned_item ); + + REQUIRE( smallest_yield_mass <= cut_up_target_mass ); + + test_actor.cut_up( guy, tool, item_loc ); + + map_stack salvaged_items = here.i_at( guy.pos() ); + units::mass salvaged_mass = 0_gram; + for( const item &salvage : salvaged_items ) { + salvaged_mass += salvage.weight(); + } + CHECK( salvaged_mass <= cut_up_target_mass ); + CHECK( salvaged_mass >= ( cut_up_target_mass * 0.99 ) - smallest_yield_mass ); +} + +TEST_CASE( "cut_up_yields" ) +{ + cut_up_yields( "blanket" ); + cut_up_yields( "down_mattress" ); + cut_up_yields( "plastic_boat_hull" ); + cut_up_yields( "bunker_coat" ); + cut_up_yields( "bunker_pants" ); + cut_up_yields( "kevlar_harness" ); + cut_up_yields( "touring_suit" ); + cut_up_yields( "dress_wedding" ); + cut_up_yields( "wetsuit" ); + cut_up_yields( "wetsuit_booties" ); + cut_up_yields( "wetsuit_hood" ); + cut_up_yields( "wetsuit_spring" ); + cut_up_yields( "wetsuit_gloves" ); + cut_up_yields( "peacoat" ); + cut_up_yields( "log" ); + cut_up_yields( "stick" ); + cut_up_yields( "stick_long" ); + cut_up_yields( "tazer" ); + cut_up_yields( "control_laptop" ); + cut_up_yields( "voltmeter" ); + cut_up_yields( "burette" ); + cut_up_yields( "eink_tablet_pc" ); + cut_up_yields( "camera" ); + cut_up_yields( "cell_phone" ); + cut_up_yields( "laptop" ); + cut_up_yields( "radio" ); + cut_up_yields( "under_armor" ); + cut_up_yields( "acidchitin_harness_dog" ); + cut_up_yields( "chitin_harness_dog" ); + cut_up_yields( "leather_harness_dog" ); + cut_up_yields( "leatherbone_harness_dog" ); + cut_up_yields( "kevlar_harness" ); + cut_up_yields( "rubber_harness_dog" ); +} From 4aa80cb3e9fe62c10f75cf349cf9eba979a1bcea Mon Sep 17 00:00:00 2001 From: Kevin Granade Date: Sun, 6 Dec 2020 02:07:33 -0800 Subject: [PATCH 03/43] Remove some uncraft recipes that should no longer be necessary --- data/json/uncraft/armor.json | 16 -------- data/json/uncraft/armor/pets_dog.json | 58 --------------------------- data/json/uncraft/generic.json | 7 ---- 3 files changed, 81 deletions(-) diff --git a/data/json/uncraft/armor.json b/data/json/uncraft/armor.json index 182489cfbd190..65640686ba23e 100644 --- a/data/json/uncraft/armor.json +++ b/data/json/uncraft/armor.json @@ -15,22 +15,6 @@ "qualities": [ { "id": "CUT", "level": 1 } ], "components": [ [ [ "rag", 35 ] ], [ [ "down_feather", 160 ] ] ] }, - { - "result": "blanket", - "type": "uncraft", - "activity_level": "LIGHT_EXERCISE", - "time": "20 m", - "qualities": [ { "id": "CUT", "level": 1 } ], - "components": [ [ [ "rag", 35 ] ] ] - }, - { - "result": "under_armor", - "type": "uncraft", - "activity_level": "LIGHT_EXERCISE", - "time": "2 m", - "qualities": [ { "id": "CUT", "level": 1 } ], - "components": [ [ [ "lycra_patch", 1 ] ] ] - }, { "result": "under_armor_shorts", "type": "uncraft", diff --git a/data/json/uncraft/armor/pets_dog.json b/data/json/uncraft/armor/pets_dog.json index 5868b88223e79..59558d0d09d7c 100644 --- a/data/json/uncraft/armor/pets_dog.json +++ b/data/json/uncraft/armor/pets_dog.json @@ -1,24 +1,4 @@ [ - { - "result": "acidchitin_harness_dog", - "type": "uncraft", - "activity_level": "MODERATE_EXERCISE", - "skill_used": "fabrication", - "difficulty": 2, - "time": "30 s", - "qualities": [ { "id": "CUT", "level": 1 } ], - "components": [ [ [ "acidchitin_piece", 6 ] ] ] - }, - { - "result": "chitin_harness_dog", - "type": "uncraft", - "activity_level": "MODERATE_EXERCISE", - "skill_used": "fabrication", - "difficulty": 2, - "time": "30 s", - "qualities": [ { "id": "CUT", "level": 1 } ], - "components": [ [ [ "chitin_piece", 6 ] ] ] - }, { "result": "chainmail_harness_dog", "type": "uncraft", @@ -29,44 +9,6 @@ "qualities": [ { "id": "SAW_M_FINE", "level": 1 } ], "components": [ [ [ "link_sheet", 3 ] ], [ [ "leather", 3 ] ], [ [ "chain_link", 70 ] ] ] }, - { - "result": "leather_harness_dog", - "type": "uncraft", - "activity_level": "LIGHT_EXERCISE", - "skill_used": "tailor", - "difficulty": 1, - "time": "30 s", - "qualities": [ { "id": "CUT", "level": 1 } ], - "components": [ [ [ "leather", 4 ] ] ] - }, - { - "result": "leatherbone_harness_dog", - "type": "uncraft", - "activity_level": "LIGHT_EXERCISE", - "skill_used": "tailor", - "difficulty": 1, - "time": "30 s", - "qualities": [ { "id": "CUT", "level": 1 } ], - "components": [ [ [ "leather", 4 ] ], [ [ "bone", 7 ] ] ] - }, - { - "result": "kevlar_harness", - "type": "uncraft", - "activity_level": "MODERATE_EXERCISE", - "skill_used": "tailor", - "difficulty": 1, - "time": "5 m", - "qualities": [ { "id": "CUT", "level": 1 } ], - "components": [ [ [ "sheet_kevlar_layered", 6 ] ] ] - }, - { - "result": "rubber_harness_dog", - "type": "uncraft", - "time": "30 s", - "activity_level": "LIGHT_EXERCISE", - "qualities": [ { "id": "CUT", "level": 1 } ], - "components": [ [ [ "rag", 5 ] ], [ [ "plastic_chunk", 5 ] ] ] - }, { "result": "superalloy_harness_dog", "type": "uncraft", diff --git a/data/json/uncraft/generic.json b/data/json/uncraft/generic.json index 950150e068cb2..52ef4bf2d3deb 100644 --- a/data/json/uncraft/generic.json +++ b/data/json/uncraft/generic.json @@ -1,11 +1,4 @@ [ - { - "result": "basket_laundry", - "type": "uncraft", - "activity_level": "MODERATE_EXERCISE", - "time": "30 s", - "components": [ [ [ "plastic_chunk", 10 ] ] ] - }, { "result": "blade_scythe", "type": "uncraft", From 5274bd4664b3ad510620e7c76d5a3e2312aeb58f Mon Sep 17 00:00:00 2001 From: Kevin Granade Date: Sat, 28 Nov 2020 01:54:07 -0800 Subject: [PATCH 04/43] Use gun ammo compat instead of magazine --- src/item.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/item.cpp b/src/item.cpp index d0243e42adad0..86e7fe0c50eea 100644 --- a/src/item.cpp +++ b/src/item.cpp @@ -7681,6 +7681,9 @@ std::set item::ammo_types( bool conversion ) const } } + if( is_gun() ) { + return type->gun->ammo; + } return contents.ammo_types(); } From cc61f927418ff50620c94e3fe4854f2f0bfc85f8 Mon Sep 17 00:00:00 2001 From: Kevin Granade Date: Sat, 28 Nov 2020 02:13:25 -0800 Subject: [PATCH 05/43] Fix test data --- data/mods/TEST_DATA/items.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/mods/TEST_DATA/items.json b/data/mods/TEST_DATA/items.json index ae3f05661d7d8..e7e7d9574025f 100644 --- a/data/mods/TEST_DATA/items.json +++ b/data/mods/TEST_DATA/items.json @@ -1057,7 +1057,7 @@ "material": [ "plastic", "steel" ], "symbol": "(", "color": "dark_gray", - "ammo": [ "9mm" ], + "ammo": [ "test_9mm" ], "ranged_damage": { "damage_type": "bullet", "amount": -1 }, "dispersion": 480, "durability": 6, From 51bcf73317134d4c411965742179bf099344260f Mon Sep 17 00:00:00 2001 From: Kevin Granade Date: Sat, 28 Nov 2020 12:01:51 -0800 Subject: [PATCH 06/43] Reverse some assertions about gun internal state --- tests/ammo_set_test.cpp | 2 +- tests/ammo_test.cpp | 16 +++++++--------- tests/iteminfo_test.cpp | 4 +--- 3 files changed, 9 insertions(+), 13 deletions(-) diff --git a/tests/ammo_set_test.cpp b/tests/ammo_set_test.cpp index 2e8e1848afe2b..3995db4668414 100644 --- a/tests/ammo_set_test.cpp +++ b/tests/ammo_set_test.cpp @@ -293,7 +293,7 @@ TEST_CASE( "ammo_set", "[ammo_set][magazine][ammo]" ) REQUIRE( cz75.magazine_default().str() == cz75mag_12rd_id.str() ); const ammotype &amtype = ammo9mm_id->ammo->type; REQUIRE( cz75.ammo_capacity( amtype ) == 0 ); - REQUIRE( cz75.ammo_default().is_null() ); + REQUIRE( !cz75.ammo_default().is_null() ); REQUIRE( cz75.magazine_default()->magazine->default_ammo.str() == ammo9mm_id.str() ); WHEN( "set 9mm ammo in the gun w/o magazine w/o quantity" ) { cz75.ammo_set( ammo9mm_id ); diff --git a/tests/ammo_test.cpp b/tests/ammo_test.cpp index b858fc5c7ba85..d33472650edce 100644 --- a/tests/ammo_test.cpp +++ b/tests/ammo_test.cpp @@ -102,14 +102,14 @@ TEST_CASE( "ammo types", "[ammo][ammo_types]" ) // These items have NO ammo_types: - SECTION( "GUN items with MAGAZINE_WELL pockets do NOT have ammo_types" ) { + SECTION( "GUN items with MAGAZINE_WELL pockets also have ammo_types" ) { REQUIRE_FALSE( item( "m1911" ).magazine_integral() ); - CHECK_FALSE( has_ammo_types( item( "m1911" ) ) ); - CHECK_FALSE( has_ammo_types( item( "usp_9mm" ) ) ); - CHECK_FALSE( has_ammo_types( item( "tommygun" ) ) ); - CHECK_FALSE( has_ammo_types( item( "ak74" ) ) ); - CHECK_FALSE( has_ammo_types( item( "ak47" ) ) ); + CHECK( has_ammo_types( item( "m1911" ) ) ); + CHECK( has_ammo_types( item( "usp_9mm" ) ) ); + CHECK( has_ammo_types( item( "tommygun" ) ) ); + CHECK( has_ammo_types( item( "ak74" ) ) ); + CHECK( has_ammo_types( item( "ak47" ) ) ); } SECTION( "TOOL items with MAGAZINE_WELL pockets do NOT have ammo_types" ) { @@ -149,15 +149,13 @@ TEST_CASE( "ammo types", "[ammo][ammo_types]" ) // The same items with no ammo_types, also have no ammo_default. TEST_CASE( "ammo default", "[ammo][ammo_default]" ) { - // TOOLMOD type, and TOOL/GUN type items with MAGAZINE_WELL pockets have no ammo_default + // TOOLMOD type, and TOOL type items with MAGAZINE_WELL pockets have no ammo_default SECTION( "items without ammo_default" ) { item flashlight( "flashlight" ); item med_mod( "magazine_battery_medium_mod" ); - item tommygun( "tommygun" ); CHECK( flashlight.ammo_default().is_null() ); CHECK( med_mod.ammo_default().is_null() ); - CHECK( tommygun.ammo_default().is_null() ); } // MAGAZINE type, and TOOL/GUN items with integral MAGAZINE pockets do have ammo_default diff --git a/tests/iteminfo_test.cpp b/tests/iteminfo_test.cpp index a641d2a733bc6..0cfaae37a5282 100644 --- a/tests/iteminfo_test.cpp +++ b/tests/iteminfo_test.cpp @@ -1376,11 +1376,9 @@ TEST_CASE( "gun or other ranged weapon attributes", "[iteminfo][weapon][gun]" ) } SECTION( "gun type and current magazine" ) { - std::vector gun_type = { iteminfo_parts::GUN_TYPE }; std::vector magazine = { iteminfo_parts::GUN_MAGAZINE }; - // When unloaded, the "Type:" and "Magazine:" sections should not be shown - CHECK( item_info_str( glock, gun_type ).empty() ); + // When unloaded, the "Magazine:" section should not be shown CHECK( item_info_str( glock, magazine ).empty() ); // TODO: Test guns having "Type:" (not many exist; ex. ar15_retool_300blk) From 6dffea67e4710ace3e65b73f1cb6d619d1546d17 Mon Sep 17 00:00:00 2001 From: Kevin Granade Date: Mon, 30 Nov 2020 00:25:20 -0800 Subject: [PATCH 07/43] Address data issues uncovered by change to ammo-magazine agreement change --- data/json/items/migration.json | 15 ++ data/json/obsolete.json | 149 ------------------ data/mods/Aftershock/items/gun/advanced.json | 1 + .../vehicleparts/blaze_turrets_vanilla.json | 8 +- .../firearms/gg_firearms_migration.json | 2 +- .../Generic_Guns/firearms/pistol_magnum.json | 7 +- data/mods/Generic_Guns/obsolete.json | 36 ----- src/item_factory.cpp | 4 +- 8 files changed, 27 insertions(+), 195 deletions(-) diff --git a/data/json/items/migration.json b/data/json/items/migration.json index 649a373de0dfd..cfd9716e67a5f 100644 --- a/data/json/items/migration.json +++ b/data/json/items/migration.json @@ -1298,5 +1298,20 @@ "id": "jug_plastic_sealed", "type": "MIGRATION", "replace": "jug_plastic" + }, + { + "id": "surv_carbine_223", + "type": "MIGRATION", + "replace": "ar15" + }, + { + "id": "rifle_308", + "type": "MIGRATION", + "replace": "m1a" + }, + { + "id": "rifle_3006", + "type": "MIGRATION", + "replace": "remington_700" } ] diff --git a/data/json/obsolete.json b/data/json/obsolete.json index 550583133a47a..19a9a18eeee43 100644 --- a/data/json/obsolete.json +++ b/data/json/obsolete.json @@ -156,155 +156,6 @@ "flags": [ "IRREMOVABLE", "RELOAD_ONE", "NEVER_JAMS", "RELOAD_EJECT" ], "pocket_data": [ { "pocket_type": "MAGAZINE", "ammo_restriction": { "shot": 2 } } ] }, - { - "id": "rifle_3006", - "looks_like": "ar15", - "type": "GUN", - "reload_noise_volume": 10, - "name": { "str": "pipe rifle: .30-06", "str_pl": "pipe rifles: .30-06" }, - "//": "It's the same size as the others, but it's still a scrap weapon.", - "description": "A homemade rifle. It is simply a pipe attached to a stock, with a hammer to strike the single round it holds.", - "weight": "4080 g", - "volume": "3 L", - "price": 110000, - "price_postapoc": 500, - "to_hit": -1, - "bashing": 12, - "material": [ "steel", "wood" ], - "symbol": "(", - "color": "brown", - "ammo": [ "3006" ], - "skill": "rifle", - "ranged_damage": { "damage_type": "bullet", "amount": -2 }, - "dispersion": 550, - "durability": 6, - "blackpowder_tolerance": 60, - "loudness": 25, - "clip_size": 1, - "reload": 200, - "barrel_volume": "1000 ml", - "valid_mod_locations": [ - [ "accessories", 2 ], - [ "muzzle", 1 ], - [ "sling", 1 ], - [ "stock", 1 ], - [ "grip mount", 1 ], - [ "rail mount", 1 ], - [ "sights mount", 1 ], - [ "underbarrel mount", 1 ] - ], - "faults": [ "fault_gun_blackpowder", "fault_gun_dirt" ], - "flags": [ "RELOAD_EJECT" ], - "pocket_data": [ { "pocket_type": "MAGAZINE", "ammo_restriction": { "3006": 1 } } ] - }, - { - "id": "rifle_308", - "copy-from": "rifle_manual", - "looks_like": "ar15", - "type": "GUN", - "name": { "str": "handmade heavy carbine" }, - "//": "It's amongst the smallest of the .308 firearms, and a scrap weapon as well. This means a short handmade barrel, and considerable loss of energy.", - "description": "A homemade lever-action magazine-fed smoothbore rifle. While still a primitive pipe and 2x4 design, some minor improvements have been made, such as being able to accept G3 compatible magazines, and chambering the more powerful .308 rounds.", - "weight": "2311 g", - "volume": "2 L", - "price": 10000, - "price_postapoc": 2250, - "to_hit": -1, - "bashing": 10, - "material": [ "steel", "wood" ], - "symbol": "(", - "color": "brown", - "ammo": [ "308" ], - "ranged_damage": { "damage_type": "bullet", "amount": -5 }, - "dispersion": 550, - "durability": 6, - "blackpowder_tolerance": 60, - "reload": 200, - "barrel_volume": "500 ml", - "valid_mod_locations": [ - [ "accessories", 4 ], - [ "barrel", 1 ], - [ "bore", 1 ], - [ "brass catcher", 1 ], - [ "muzzle", 1 ], - [ "stock", 1 ], - [ "mechanism", 4 ], - [ "sights", 1 ], - [ "sling", 1 ], - [ "grip mount", 1 ], - [ "rail mount", 1 ], - [ "underbarrel mount", 1 ] - ], - "pocket_data": [ - { - "pocket_type": "MAGAZINE_WELL", - "holster": true, - "max_contains_volume": "20 L", - "max_contains_weight": "20 kg", - "item_restriction": [ "g3_makeshiftmag", "g3mag", "g3bigmag" ] - } - ] - }, - { - "id": "surv_carbine_223", - "copy-from": "rifle_manual", - "looks_like": "ar15", - "type": "GUN", - "name": { "str": "handmade carbine" }, - "//": "It's smaller than an M4A1, plus it's a homemade firearm.", - "description": "A well-designed improvised lever-action carbine with a shortened barrel. Accepting crude detachable magazines or STANAG magazines, this is one of the better homemade weapons.", - "weight": "1950 g", - "volume": "1500 ml", - "price": 10000, - "price_postapoc": 2750, - "to_hit": -1, - "bashing": 6, - "material": [ "steel", "wood" ], - "symbol": "(", - "color": "brown", - "ammo": [ "223" ], - "ranged_damage": { "damage_type": "bullet", "amount": -4 }, - "dispersion": 550, - "durability": 6, - "blackpowder_tolerance": 32, - "loudness": 25, - "reload": 200, - "valid_mod_locations": [ - [ "accessories", 2 ], - [ "barrel", 1 ], - [ "brass catcher", 1 ], - [ "muzzle", 1 ], - [ "sling", 1 ], - [ "stock", 1 ], - [ "underbarrel", 1 ], - [ "grip mount", 1 ], - [ "rail mount", 1 ], - [ "sights mount", 1 ] - ], - "pocket_data": [ - { - "pocket_type": "MAGAZINE_WELL", - "holster": true, - "max_contains_volume": "20 L", - "max_contains_weight": "20 kg", - "item_restriction": [ - "survivor223mag", - "stanag30", - "stanag5", - "stanag10", - "stanag20", - "stanag40", - "stanag50", - "stanag60", - "stanag60drum", - "stanag90", - "stanag100", - "stanag100drum", - "stanag150" - ] - } - ] - }, { "id": "rifle_223", "copy-from": "gun_base", diff --git a/data/mods/Aftershock/items/gun/advanced.json b/data/mods/Aftershock/items/gun/advanced.json index de43a41620117..85d6bf0ebe260 100644 --- a/data/mods/Aftershock/items/gun/advanced.json +++ b/data/mods/Aftershock/items/gun/advanced.json @@ -32,6 +32,7 @@ "ammo": "metal_rail", "skill": "rifle", "modes": [ [ "DEFAULT", "semi-auto", 1 ], [ "BURST", "2 rd.", 2 ] ], + "pocket_data": [ { "pocket_type": "MAGAZINE", "ammo_restriction": { "metal_rail": 100 } } ], "ammo_effects": [ "TRAIL" ] } ] diff --git a/data/mods/BlazeIndustries/vehicleparts/blaze_turrets_vanilla.json b/data/mods/BlazeIndustries/vehicleparts/blaze_turrets_vanilla.json index 7b874685363ad..7142e33d81d77 100644 --- a/data/mods/BlazeIndustries/vehicleparts/blaze_turrets_vanilla.json +++ b/data/mods/BlazeIndustries/vehicleparts/blaze_turrets_vanilla.json @@ -449,8 +449,8 @@ "copy-from": "turret", "type": "vehicle_part", "name": { "str": "mounted .308 pipe rifle" }, - "item": "rifle_308", - "breaks_into": [ { "item": "rifle_308", "prob": 50 } ], + "item": "m1a", + "breaks_into": [ { "item": "m1a", "prob": 50 } ], "requirements": { "install": { "skills": [ [ "mechanics", 4 ] ] }, "removal": { "skills": [ [ "mechanics", 2 ] ] } } }, { @@ -623,8 +623,8 @@ "copy-from": "turret", "type": "vehicle_part", "name": { "str": "mounted 223 carbine" }, - "item": "surv_carbine_223", - "breaks_into": [ { "item": "surv_carbine_223", "prob": 50 } ], + "item": "ar15", + "breaks_into": [ { "item": "ar15", "prob": 50 } ], "requirements": { "install": { "skills": [ [ "mechanics", 4 ] ] }, "removal": { "skills": [ [ "mechanics", 2 ] ] } } }, { diff --git a/data/mods/Generic_Guns/firearms/gg_firearms_migration.json b/data/mods/Generic_Guns/firearms/gg_firearms_migration.json index c3a4efb0539c2..76fa0bad19c1a 100644 --- a/data/mods/Generic_Guns/firearms/gg_firearms_migration.json +++ b/data/mods/Generic_Guns/firearms/gg_firearms_migration.json @@ -320,7 +320,7 @@ "replace": "rocket_disposable" }, { - "id": [ "m202_flash", "m3_carlgustav", "RPG", "surv_rocket_launcher" ], + "id": [ "m202_flash", "m3_carlgustav", "m4_carlgustav", "RPG", "surv_rocket_launcher" ], "type": "MIGRATION", "replace": "rocket_recoilless" }, diff --git a/data/mods/Generic_Guns/firearms/pistol_magnum.json b/data/mods/Generic_Guns/firearms/pistol_magnum.json index 2d823611512c5..58b5fb14f5f09 100644 --- a/data/mods/Generic_Guns/firearms/pistol_magnum.json +++ b/data/mods/Generic_Guns/firearms/pistol_magnum.json @@ -4,7 +4,7 @@ "copy-from": "deagle_44", "type": "GUN", "name": { "str": "hand cannon" }, - "ammo": [ "ammo_pistol_magnum", "ammo_pistol" ], + "ammo": [ "ammo_pistol", "ammo_pistol_magnum" ], "//": "We're just going to prtend that .357 and .44 magnum deagles will run .38's and .44 special just fine", "description": "This large pistol is almost as heavy as a small carbine, and just about as powerful too. Chambered in hard hitting magnum calibers, it is suitable for hunting medium game, humans, or offsetting any of one's perceived deficiencies. Though tradtionally such magnums are revolvers, this one is a magazine fed semi-automatic.", "pocket_data": [ @@ -13,7 +13,7 @@ "holster": true, "max_contains_volume": "20 L", "max_contains_weight": "20 kg", - "item_restriction": [ "pistol_magnum_mag", "pistol_magnum_mag" ], + "item_restriction": [ "pistol_mag", "pistol_magnum_mag" ], "rigid": true } ] @@ -62,7 +62,7 @@ "holster": true, "max_contains_volume": "20 L", "max_contains_weight": "20 kg", - "item_restriction": [ "pistol_magnum_mag", "pistol_magnum_mag" ], + "item_restriction": [ "pistol_mag", "pistol_magnum_mag" ], "rigid": true } ] @@ -75,6 +75,7 @@ "ammo": [ "ammo_pistol_magnum", "ammo_pistol" ], "ranged_damage": { "damage_type": "bullet", "amount": -2 }, "description": "A firearm made from a stout pipe, reinforced at the chamber. It holds a single a round of standard or magnum pistol ammunition, and has a crude assembly to fire it. There's no extractor, so it might be slow to reload, and its construction makes for poor reliability and longevity.", + "pocket_data": [ { "pocket_type": "MAGAZINE", "rigid": true, "ammo_restriction": { "ammo_pistol": 1, "ammo_pistol_magnum": 1 } } ], "dispersion": 440, "reload": 110 }, diff --git a/data/mods/Generic_Guns/obsolete.json b/data/mods/Generic_Guns/obsolete.json index 7891570283959..4369edf896e82 100644 --- a/data/mods/Generic_Guns/obsolete.json +++ b/data/mods/Generic_Guns/obsolete.json @@ -1,40 +1,4 @@ [ - { - "id": "rifle_pipe_rifle", - "copy-from": "rifle_308", - "type": "GUN", - "name": { "str": "pipe rifle" }, - "ammo": [ "ammo_rifle" ], - "description": "A crude longarm chambered in standard rifle ammunition, reinforced near the chamber. It holds a single a round and has a crude assembly to fire it. There's no extractor, so it might be slow to reload, and its construction makes for poor reliability and longevity.", - "clip_size": 1, - "pocket_data": [ - { - "pocket_type": "MAGAZINE_WELL", - "holster": true, - "max_contains_volume": "20 L", - "max_contains_weight": "20 kg", - "rigid": true - } - ] - }, - { - "id": "rifle_pipe_carbine", - "copy-from": "surv_carbine_223", - "type": "GUN", - "name": "survivor carbine", - "ammo": [ "ammo_rifle" ], - "description": "A crudely constructed carbine chambered for standard rifle ammo, fed from service rifle magazines. It locks with a rudimentary lever action system. The high pressures involved and questionable construction make for less than ideal durability and reliability, but this should still be a serviceable weapon, provided you can stay accurate with it.", - "pocket_data": [ - { - "pocket_type": "MAGAZINE_WELL", - "holster": true, - "max_contains_volume": "20 L", - "max_contains_weight": "20 kg", - "item_restriction": [ "rifle_mag", "rifle_sniper_mag" ], - "rigid": true - } - ] - }, { "type": "recipe", "result": "rifle_pipe_carbine", diff --git a/src/item_factory.cpp b/src/item_factory.cpp index 5b25b0bf437ac..8cccd3c2c728d 100644 --- a/src/item_factory.cpp +++ b/src/item_factory.cpp @@ -1384,7 +1384,7 @@ void Item_factory::check_definitions() const const itype_id &default_ammo = type->magazine->default_ammo; const itype *da = find_template( default_ammo ); if( da->ammo && type->magazine->type.count( da->ammo->type ) ) { - if( !migrations.count( type->id ) ) { + if( !migrations.count( type->id ) && !item_is_blacklisted( type->id ) ) { // Verify that the default amnmo can actually be put in this // item item( type ).ammo_set( default_ammo, 1 ); @@ -1461,7 +1461,7 @@ void Item_factory::check_definitions() const } } - if( !migrations.count( type->id ) ) { + if( !migrations.count( type->id ) && !item_is_blacklisted( type->id ) ) { // If type has a default ammo then check it can fit within item tmp_item( type ); if( tmp_item.is_gun() || tmp_item.is_magazine() ) { From 4f249fe0517ea5a59950ebcc160b0136d26a933d Mon Sep 17 00:00:00 2001 From: Kevin Granade Date: Mon, 30 Nov 2020 13:17:10 -0800 Subject: [PATCH 08/43] Migrate ammo restrictions in magazines --- src/item_factory.cpp | 35 ++++++++++++++++++++++++++++++++--- 1 file changed, 32 insertions(+), 3 deletions(-) diff --git a/src/item_factory.cpp b/src/item_factory.cpp index 8cccd3c2c728d..d6de6a2621d4d 100644 --- a/src/item_factory.cpp +++ b/src/item_factory.cpp @@ -253,12 +253,27 @@ void Item_factory::finalize_pre( itype &obj ) } // Helper for ammo migration in following sections - auto migrate_ammo_set = [&]( std::set &ammoset ) { + auto migrate_ammo_set = [this]( std::set &ammoset ) { for( auto ammo_type_it = ammoset.begin(); ammo_type_it != ammoset.end(); ) { - auto maybe_migrated = migrated_ammo.find( ammo_type_it->obj().default_ammotype() ); + const itype_id default_ammo_type = ammo_type_it->obj().default_ammotype(); + auto maybe_migrated = migrated_ammo.find( default_ammo_type ); if( maybe_migrated != migrated_ammo.end() ) { ammo_type_it = ammoset.erase( ammo_type_it ); - ammoset.insert( ammoset.begin(), maybe_migrated->second ); + ammoset.insert( maybe_migrated->second ); + } else { + ++ammo_type_it; + } + } + }; + + auto migrate_ammo_map = [this]( std::map &ammomap ) { + for( auto ammo_type_it = ammomap.begin(); ammo_type_it != ammomap.end(); ) { + const itype_id default_ammo_type = ammo_type_it->first.obj().default_ammotype(); + auto maybe_migrated = migrated_ammo.find( default_ammo_type ); + if( maybe_migrated != migrated_ammo.end() ) { + int capacity = ammo_type_it->second; + ammo_type_it = ammomap.erase( ammo_type_it ); + ammomap.emplace( maybe_migrated->second, capacity ); } else { ++ammo_type_it; } @@ -280,6 +295,13 @@ void Item_factory::finalize_pre( itype &obj ) if( maybe_migrated != migrated_ammo.end() ) { obj.magazine->default_ammo = maybe_migrated->second.obj().default_ammotype(); } + + for( pocket_data &magazine : obj.pockets ) { + if( magazine.type != item_pocket::pocket_type::MAGAZINE ) { + continue; + } + migrate_ammo_map( magazine.ammo_restriction ); + } } // Migrate compataible magazines @@ -352,6 +374,13 @@ void Item_factory::finalize_pre( itype &obj ) } } + for( pocket_data &magazine : obj.pockets ) { + if( magazine.type != item_pocket::pocket_type::MAGAZINE ) { + continue; + } + migrate_ammo_map( magazine.ammo_restriction ); + } + // TODO: add explicit action field to gun definitions const auto defmode_name = [&]() { if( obj.gun->clip == 1 ) { From 9353bf8f9cbd063e0a20a859cb92e47f64116017 Mon Sep 17 00:00:00 2001 From: Kevin Granade Date: Wed, 23 Dec 2020 13:48:02 -0800 Subject: [PATCH 09/43] Add some more items to validate cut-up yields Co-authored-by: actual-nh <74678550+actual-nh@users.noreply.github.com> --- tests/iuse_actor_test.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/iuse_actor_test.cpp b/tests/iuse_actor_test.cpp index 33fe9083606e5..1acab38dec97f 100644 --- a/tests/iuse_actor_test.cpp +++ b/tests/iuse_actor_test.cpp @@ -143,6 +143,9 @@ static void cut_up_yields( const std::string &target ) TEST_CASE( "cut_up_yields" ) { cut_up_yields( "blanket" ); + cut_up_yields( "backpack_hiking" ); + cut_up_yields( "boxpack" ); + cut_up_yields( "case_violin" ); cut_up_yields( "down_mattress" ); cut_up_yields( "plastic_boat_hull" ); cut_up_yields( "bunker_coat" ); From 809090501ca21decd6c895ae75afeae8d5477c28 Mon Sep 17 00:00:00 2001 From: Kevin Granade Date: Wed, 23 Dec 2020 14:19:21 -0800 Subject: [PATCH 10/43] Whoops add an include --- tests/iuse_actor_test.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/iuse_actor_test.cpp b/tests/iuse_actor_test.cpp index 1acab38dec97f..e28c8493792ee 100644 --- a/tests/iuse_actor_test.cpp +++ b/tests/iuse_actor_test.cpp @@ -9,6 +9,7 @@ #include "item.h" #include "itype.h" #include "iuse_actor.h" +#include "map.h" #include "monster.h" #include "mtype.h" #include "pimpl.h" From 81e7298bfed445c2a4d55fed42a17b6c803e54a1 Mon Sep 17 00:00:00 2001 From: Kevin Granade Date: Sun, 27 Dec 2020 23:31:44 -0800 Subject: [PATCH 11/43] Seperate the concept of transparent floor and no floor at all, mark stairs as having transparent floors --- src/map.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/map.cpp b/src/map.cpp index 00b0278aec7bf..64459dc38ff67 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -2037,7 +2037,7 @@ bool map::has_floor( const tripoint &p ) const return true; } - return get_cache_ref( p.z ).floor_cache[p.x][p.y]; + return ter( p )->has_flag( TFLAG_NO_FLOOR ); } bool map::supports_above( const tripoint &p ) const @@ -8007,7 +8007,7 @@ bool map::build_floor_cache( const int zlev ) for( int sy = 0; sy < SEEY; ++sy ) { point sp( sx, sy ); const ter_t &terrain = cur_submap->get_ter( sp ).obj(); - if( terrain.has_flag( TFLAG_NO_FLOOR ) ) { + if( terrain.has_flag( TFLAG_NO_FLOOR ) || terrain.has_flag( TFLAG_GOES_DOWN ) ) { if( below_submap && ( below_submap->get_furn( sp ).obj().has_flag( TFLAG_SUN_ROOF_ABOVE ) ) ) { continue; } From d2bbf482a3779ff0f8b53f24f8adfa39d572eece Mon Sep 17 00:00:00 2001 From: Kevin Granade Date: Sun, 27 Dec 2020 23:32:38 -0800 Subject: [PATCH 12/43] Remove arbitrary blockers to vertical adjacency. This allows monsters to attack up and down stairs in particular. --- src/creature.cpp | 8 +++++--- src/map.cpp | 2 +- src/monmove.cpp | 4 ---- 3 files changed, 6 insertions(+), 8 deletions(-) diff --git a/src/creature.cpp b/src/creature.cpp index 476e0aaf15317..3ace374530005 100644 --- a/src/creature.cpp +++ b/src/creature.cpp @@ -530,12 +530,14 @@ bool Creature::is_adjacent( Creature *target, const bool allow_z_levels ) const return false; } - // The square above must have no floor (currently only open air). - // The square below must have no ceiling (i.e. be outside). + // The square above must have no floor. + // The square below must have no ceiling (i.e. no floor on the tile above it). const bool target_above = target->posz() > posz(); const tripoint &up = target_above ? target->pos() : pos(); const tripoint &down = target_above ? pos() : target->pos(); - return here.ter( up ) == t_open_air && here.is_outside( down ); + const tripoint above{ down.xy(), up.z }; + return ( !here.has_floor( up ) || here.ter( up )->has_flag( TFLAG_GOES_DOWN ) ) && + ( !here.has_floor( above ) || here.ter( above )->has_flag( TFLAG_GOES_DOWN ) ); } int Creature::deal_melee_attack( Creature *source, int hitroll ) diff --git a/src/map.cpp b/src/map.cpp index 64459dc38ff67..a6ae889136188 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -2037,7 +2037,7 @@ bool map::has_floor( const tripoint &p ) const return true; } - return ter( p )->has_flag( TFLAG_NO_FLOOR ); + return !ter( p )->has_flag( TFLAG_NO_FLOOR ); } bool map::supports_above( const tripoint &p ) const diff --git a/src/monmove.cpp b/src/monmove.cpp index c2a007ff893ff..158bd208fd731 100644 --- a/src/monmove.cpp +++ b/src/monmove.cpp @@ -1479,10 +1479,6 @@ bool monster::attack_at( const tripoint &p ) if( has_flag( MF_PACIFIST ) ) { return false; } - if( p.z != posz() ) { - // TODO: Remove this - return false; - } Character &player_character = get_player_character(); if( p == player_character.pos() ) { From 751bdcccc351a59aebf0547d169ea29ab581a7b8 Mon Sep 17 00:00:00 2001 From: Kevin Granade Date: Mon, 28 Dec 2020 19:32:57 -0800 Subject: [PATCH 13/43] prevent player melee atacks across z-levels unless it's a reach attack. --- src/avatar_action.cpp | 6 +++++- src/character.h | 6 ++++-- src/creature.cpp | 2 +- src/creature.h | 2 +- src/melee.cpp | 26 +++++++++++++++++++------- src/mission_companion.cpp | 2 +- src/monmove.cpp | 9 +++------ src/monster.cpp | 17 +++++++++-------- src/monster.h | 4 ++-- tests/effective_dps_test.cpp | 2 +- 10 files changed, 46 insertions(+), 30 deletions(-) diff --git a/src/avatar_action.cpp b/src/avatar_action.cpp index f49ca084607cb..6cfec4ae56f84 100644 --- a/src/avatar_action.cpp +++ b/src/avatar_action.cpp @@ -591,7 +591,11 @@ void avatar_action::autoattack( avatar &you, map &m ) { int reach = you.weapon.reach_range( you ); std::vector critters = you.get_targetable_creatures( reach, true ); - critters.erase( std::remove_if( critters.begin(), critters.end(), []( const Creature * c ) { + critters.erase( std::remove_if( critters.begin(), critters.end(), [&you, + reach]( const Creature * c ) { + if( reach == 1 && !you.is_adjacent( c, true ) ) { + return true; + } if( !c->is_npc() ) { return false; } diff --git a/src/character.h b/src/character.h index a845aa9e5a0dd..fea319ddb7970 100644 --- a/src/character.h +++ b/src/character.h @@ -791,8 +791,10 @@ class Character : public Creature, public visitable * @param force_technique special technique to use in attack. * @param allow_unarmed always uses the wielded weapon regardless of martialarts style */ - void melee_attack( Creature &t, bool allow_special, const matec_id &force_technique, + bool melee_attack( Creature &t, bool allow_special, const matec_id &force_technique, bool allow_unarmed = true ); + bool melee_attack_abstract( Creature &t, bool allow_special, const matec_id &force_technique, + bool allow_unarmed = true ); /** Handles reach melee attacks */ void reach_attack( const tripoint &p ); @@ -801,7 +803,7 @@ class Character : public Creature, public visitable * Calls the to other melee_attack function with an empty technique id (meaning no specific * technique should be used). */ - void melee_attack( Creature &t, bool allow_special ); + bool melee_attack( Creature &t, bool allow_special ); /** Handles combat effects, returns a string of any valid combat effect messages */ std::string melee_special_effects( Creature &t, damage_instance &d, item &weap ); /** Performs special attacks and their effects (poisonous, stinger, etc.) */ diff --git a/src/creature.cpp b/src/creature.cpp index 3ace374530005..b4f5ce650b2c8 100644 --- a/src/creature.cpp +++ b/src/creature.cpp @@ -508,7 +508,7 @@ int Creature::size_melee_penalty() const return 0; } -bool Creature::is_adjacent( Creature *target, const bool allow_z_levels ) const +bool Creature::is_adjacent( const Creature *target, const bool allow_z_levels ) const { if( target == nullptr ) { return false; diff --git a/src/creature.h b/src/creature.h index 962c61715a3ef..fd58726321a1f 100644 --- a/src/creature.h +++ b/src/creature.h @@ -397,7 +397,7 @@ class Creature : public location, public viewer // If allow_zlev is true, also allow distance == 1 and on different z-level // as long as floor/ceiling doesn't exist. // Also check other factors, like vehicle separating deep water/air - bool is_adjacent( Creature *target, bool allow_z_levels ) const; + bool is_adjacent( const Creature *target, bool allow_z_levels ) const; // modifies the damage dealt based on the creature's enchantments // since creatures currently don't have enchantments, this is just virtual diff --git a/src/melee.cpp b/src/melee.cpp index 30f5c55bbf6ee..81a4401a7581b 100644 --- a/src/melee.cpp +++ b/src/melee.cpp @@ -381,10 +381,10 @@ static void melee_train( Character &p, int lo, int hi, const item &weap ) } } -void Character::melee_attack( Creature &t, bool allow_special ) +bool Character::melee_attack( Creature &t, bool allow_special ) { static const matec_id no_technique_id( "" ); - melee_attack( t, allow_special, no_technique_id ); + return melee_attack( t, allow_special, no_technique_id ); } damage_instance Creature::modify_damage_dealt_with_enchantments( const damage_instance &dam ) const @@ -454,12 +454,23 @@ damage_instance Character::modify_damage_dealt_with_enchantments( const damage_i // Melee calculation is in parts. This sets up the attack, then in deal_melee_attack, // we calculate if we would hit. In Creature::deal_melee_hit, we calculate if the target dodges. -void Character::melee_attack( Creature &t, bool allow_special, const matec_id &force_technique, +bool Character::melee_attack( Creature &t, bool allow_special, const matec_id &force_technique, bool allow_unarmed ) { if( has_effect( effect_incorporeal ) ) { add_msg_if_player( m_info, _( "You lack the substance to affect anything." ) ); + return false; + } + if( !is_adjacent( &t, true ) ) { + return false; } + return melee_attack_abstract( t, allow_special, force_technique, allow_unarmed ); +} + +bool Character::melee_attack_abstract( Creature &t, bool allow_special, + const matec_id &force_technique, + bool allow_unarmed ) +{ melee::melee_stats.attack_count += 1; int hit_spread = t.deal_melee_attack( this, hit_roll() ); if( !t.is_player() ) { @@ -472,7 +483,7 @@ void Character::melee_attack( Creature &t, bool allow_special, const matec_id &f if( !mons->check_mech_powered() ) { add_msg( m_bad, _( "The %s has dead batteries and will not move its arms." ), mons->get_name() ); - return; + return false; } if( mons->type->has_special_attack( "SMASH" ) && one_in( 3 ) ) { add_msg( m_info, _( "The %s hisses as its hydraulic arm pumps forward!" ), @@ -483,7 +494,7 @@ void Character::melee_attack( Creature &t, bool allow_special, const matec_id &f mons->melee_attack( t ); } mod_moves( -mons->type->attack_cost ); - return; + return true; } } @@ -511,7 +522,7 @@ void Character::melee_attack( Creature &t, bool allow_special, const matec_id &f if( cur_weapon->attack_time() > attack_speed( *cur_weapon ) * 20 ) { add_msg( m_bad, _( "This weapon is too unwieldy to attack with!" ) ); - return; + return false; } int move_cost = attack_speed( *cur_weapon ); @@ -728,6 +739,7 @@ void Character::melee_attack( Creature &t, bool allow_special, const matec_id &f dealt_projectile_attack dp = dealt_projectile_attack(); t.as_character()->on_hit( this, bodypart_id( "bp_null" ), 0.0f, &dp ); } + return true; } void Character::reach_attack( const tripoint &p ) @@ -793,7 +805,7 @@ void Character::reach_attack( const tripoint &p ) } reach_attacking = true; - melee_attack( *critter, false, force_technique, false ); + melee_attack_abstract( *critter, false, force_technique, false ); reach_attacking = false; } diff --git a/src/mission_companion.cpp b/src/mission_companion.cpp index b916af473e8d6..7fb345c929aa2 100644 --- a/src/mission_companion.cpp +++ b/src/mission_companion.cpp @@ -863,7 +863,7 @@ void talk_function::attack_random( const std::vector &attacker, } const auto att = random_entry( attacker ); monster *def = random_entry( group ); - att->melee_attack( *def, false ); + att->melee_attack_abstract( *def, false, matec_id( "" ) ); if( def->get_hp() <= 0 ) { popup( _( "%s is wasted by %s!" ), def->type->nname(), att->name ); } diff --git a/src/monmove.cpp b/src/monmove.cpp index 158bd208fd731..ec03d1c18ee58 100644 --- a/src/monmove.cpp +++ b/src/monmove.cpp @@ -1482,8 +1482,7 @@ bool monster::attack_at( const tripoint &p ) Character &player_character = get_player_character(); if( p == player_character.pos() ) { - melee_attack( player_character ); - return true; + return melee_attack( player_character ); } if( monster *mon_ = g->critter_at( p, is_hallucination() ) ) { @@ -1503,8 +1502,7 @@ bool monster::attack_at( const tripoint &p ) Creature::Attitude attitude = attitude_to( mon ); // MF_ATTACKMON == hulk behavior, whack everything in your way if( attitude == Attitude::HOSTILE || has_flag( MF_ATTACKMON ) ) { - melee_attack( mon ); - return true; + return melee_attack( mon ); } return false; @@ -1516,8 +1514,7 @@ bool monster::attack_at( const tripoint &p ) // way. This is consistent with how it worked previously, but // later on not hitting allied NPCs would be cool. guy->on_attacked( *this ); // allow NPC hallucination to be one shot by monsters - melee_attack( *guy ); - return true; + return melee_attack( *guy ); } // Nothing to attack. diff --git a/src/monster.cpp b/src/monster.cpp index 7a64cc64779df..47736ac07b3ef 100644 --- a/src/monster.cpp +++ b/src/monster.cpp @@ -1360,24 +1360,24 @@ void monster::absorb_hit( const bodypart_id &, damage_instance &dam ) } } -void monster::melee_attack( Creature &target ) +bool monster::melee_attack( Creature &target ) { - melee_attack( target, get_hit() ); + return melee_attack( target, get_hit() ); } -void monster::melee_attack( Creature &target, float accuracy ) +bool monster::melee_attack( Creature &target, float accuracy ) { // Note: currently this method must consume move even if attack hasn't actually happen // otherwise infinite loop will happen mod_moves( -type->attack_cost ); if( /*This happens sometimes*/ this == &target || !is_adjacent( &target, true ) ) { - return; + return false; } int hitspread = target.deal_melee_attack( this, melee::melee_hit_range( accuracy ) ); if( type->melee_dice == 0 ) { - // We don't attack, so just return - return; + // We don't hit, so just return + return true; } Character &player_character = get_player_character(); @@ -1500,11 +1500,11 @@ void monster::melee_attack( Creature &target, float accuracy ) if( one_in( 7 ) ) { die( nullptr ); } - return; + return true; } if( total_dealt <= 0 ) { - return; + return true; } // Add any on damage effects @@ -1533,6 +1533,7 @@ void monster::melee_attack( Creature &target, float accuracy ) target.add_msg_if_player( m_bad, _( "You feel venom enter your body!" ) ); target.add_effect( effect_paralyzepoison, 10_minutes ); } + return true; } void monster::deal_projectile_attack( Creature *source, dealt_projectile_attack &attack, diff --git a/src/monster.h b/src/monster.h index f964438b0af2e..c2a5d59ce1e22 100644 --- a/src/monster.h +++ b/src/monster.h @@ -313,8 +313,8 @@ class monster : public Creature void absorb_hit( const bodypart_id &bp, damage_instance &dam ) override; bool block_hit( Creature *source, bodypart_id &bp_hit, damage_instance &d ) override; - void melee_attack( Creature &target ); - void melee_attack( Creature &target, float accuracy ); + bool melee_attack( Creature &target ); + bool melee_attack( Creature &target, float accuracy ); void melee_attack( Creature &p, bool ) = delete; void deal_projectile_attack( Creature *source, dealt_projectile_attack &attack, bool print_messages = true ) override; diff --git a/tests/effective_dps_test.cpp b/tests/effective_dps_test.cpp index 2db197458963d..72d1b25cdc658 100644 --- a/tests/effective_dps_test.cpp +++ b/tests/effective_dps_test.cpp @@ -46,7 +46,7 @@ static double weapon_dps_trials( avatar &attacker, monster &defender, item &weap defender.set_hp( starting_hp ); // Attack once - attacker.melee_attack( defender, false ); + attacker.melee_attack_abstract( defender, false, matec_id( "" ) ); // Tally total damage and moves total_damage += std::max( 0, starting_hp - defender.get_hp() ); From 08cb63b0b0d241e4e53e862f62a58c8070cf3840 Mon Sep 17 00:00:00 2001 From: Kevin Granade Date: Mon, 28 Dec 2020 19:33:32 -0800 Subject: [PATCH 14/43] Add a test verifying ability of melee attacks to cross z-levels --- tests/monster_attack.cpp | 82 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 82 insertions(+) create mode 100644 tests/monster_attack.cpp diff --git a/tests/monster_attack.cpp b/tests/monster_attack.cpp new file mode 100644 index 0000000000000..64fa499a4ea04 --- /dev/null +++ b/tests/monster_attack.cpp @@ -0,0 +1,82 @@ +#include "catch/catch.hpp" + +#include "character.h" +#include "monster.h" + +#include "map.h" + +#include "map_helpers.h" + +static constexpr tripoint attacker_location{ 65, 65, 0 }; + +static void test_monster_attack( const tripoint &target_offset, bool expected ) +{ + clear_creatures(); + // Monster adjacent to target. + const std::string monster_type = "mon_zombie"; + const tripoint target_location = attacker_location + target_offset; + Character &you = get_player_character(); + you.setpos( target_location ); + monster &test_monster = spawn_test_monster( monster_type, attacker_location ); + // Trigger basic attack. + CAPTURE( attacker_location ); + CAPTURE( target_location ); + CHECK( test_monster.attack_at( target_location ) == expected ); + // Then test the reverse. + clear_creatures(); + you.setpos( attacker_location ); + monster &target_monster = spawn_test_monster( monster_type, target_location ); + CHECK( you.melee_attack( target_monster, false ) == expected ); +} + +static void monster_attack_zlevel( const std::string &title, const tripoint &offset, + const std::string &monster_ter, const std::string &target_ter, + bool expected ) +{ + map &here = get_map(); + SECTION( title ) { + here.ter_set( attacker_location, ter_id( monster_ter ) ); + here.ter_set( attacker_location + offset, ter_id( target_ter ) ); + test_monster_attack( offset, expected ); + for( const tripoint &more_offset : eight_horizontal_neighbors ) { + here.ter_set( attacker_location + offset + more_offset, ter_id( "t_floor" ) ); + test_monster_attack( offset + more_offset, false ); + } + } +} + +TEST_CASE( "monster_attack" ) +{ + clear_map(); + SECTION( "attacking on open ground" ) { + // Adjacent can attack of course. + for( const tripoint &offset : eight_horizontal_neighbors ) { + test_monster_attack( offset, true ); + } + // Too far away cannot. + test_monster_attack( { 2, 2, 0 }, false ); + test_monster_attack( { 2, 1, 0 }, false ); + test_monster_attack( { 2, 0, 0 }, false ); + test_monster_attack( { 2, -1, 0 }, false ); + test_monster_attack( { 2, -2, 0 }, false ); + test_monster_attack( { 1, 2, 0 }, false ); + test_monster_attack( { 1, -2, 0 }, false ); + test_monster_attack( { 0, 2, 0 }, false ); + test_monster_attack( { 0, -2, 0 }, false ); + test_monster_attack( { -1, 2, 0 }, false ); + test_monster_attack( { -1, -2, 0 }, false ); + test_monster_attack( { -2, 2, 0 }, false ); + test_monster_attack( { -2, 1, 0 }, false ); + test_monster_attack( { -2, 0, 0 }, false ); + test_monster_attack( { -2, -1, 0 }, false ); + test_monster_attack( { -2, -2, 0 }, false ); + } + + monster_attack_zlevel( "attack_up_stairs", tripoint_above, "t_stairs_up", "t_stairs_down", true ); + monster_attack_zlevel( "attack_down_stairs", tripoint_below, "t_stairs_down", "t_stairs_up", true ); + monster_attack_zlevel( "attack through ceiling", tripoint_above, "t_floor", "t_floor", false ); + monster_attack_zlevel( "attack through floor", tripoint_below, "t_floor", "t_floor", false ); + + monster_attack_zlevel( "attack up legde", tripoint_above, "t_floor", "t_floor", false ); + monster_attack_zlevel( "attack down legde", tripoint_below, "t_floor", "t_floor", false ); +} From 1f0d68748659011a77394a86880a554dafca952f Mon Sep 17 00:00:00 2001 From: John Candlebury Date: Sun, 3 Jan 2021 18:47:25 -0600 Subject: [PATCH 15/43] Unhardcode nanofabricator template requirments. Let nanofabricators specify what templates they can print --- src/iexamine.cpp | 28 ++++++++++++++++++++-------- src/item_factory.cpp | 4 ++++ src/itype.h | 2 ++ src/mapdata.cpp | 2 ++ src/mapdata.h | 1 + 5 files changed, 29 insertions(+), 8 deletions(-) diff --git a/src/iexamine.cpp b/src/iexamine.cpp index f17edec03714b..3ca5c7157274c 100644 --- a/src/iexamine.cpp +++ b/src/iexamine.cpp @@ -218,6 +218,7 @@ static const std::string flag_CLIMB_SIMPLE( "CLIMB_SIMPLE" ); static const std::string flag_GROWTH_HARVEST( "GROWTH_HARVEST" ); static const std::string flag_OPENCLOSE_INSIDE( "OPENCLOSE_INSIDE" ); static const std::string flag_PICKABLE( "PICKABLE" ); +static const std::string flag_NANOFAB_TABLE( "NANOFAB_TABLE" ); static const std::string flag_WALL( "WALL" ); // @TODO maybe make this a property of the item (depend on volume/type) @@ -273,15 +274,17 @@ void iexamine::cvdmachine( player &p, const tripoint & ) } /** - * UI FOR LAB_FINALE NANO FABRICATOR. + * TEMPLATE FABRICATORS + * Generate items from found blueprints. */ void iexamine::nanofab( player &p, const tripoint &examp ) { bool table_exists = false; tripoint spawn_point; map &here = get_map(); + std::set allowed_template = here.ter( examp )->allowed_template_id; for( const auto &valid_location : here.points_in_radius( examp, 1 ) ) { - if( here.ter( valid_location ) == ter_str_id( "t_nanofab_body" ) ) { + if( here.has_flag( flag_NANOFAB_TABLE, valid_location ) ) { spawn_point = valid_location; table_exists = true; break; @@ -290,11 +293,21 @@ void iexamine::nanofab( player &p, const tripoint &examp ) if( !table_exists ) { return; } + //Create a list of the names of all acceptable templates. + std::set templatenames; + for( const itype_id &id : allowed_template ) { + templatenames.insert( id->nname( 1 ) ); + } + std::string name_list = enumerate_as_string( templatenames ); - item_location nanofab_template = g->inv_map_splice( []( const item & e ) { - return e.has_var( "NANOFAB_ITEM_ID" ); - }, _( "Introduce Nanofabricator template" ), PICKUP_RANGE, - _( "You don't have any usable templates." ) ); + //Template selection + item_location nanofab_template = g->inv_map_splice( [&]( const item & e ) { + return std::any_of( allowed_template.begin(), allowed_template.end(), + [&e]( const itype_id itid ) { + return e.typeId() == itid; + } ); + }, _( "Introduce a compatible template." ), PICKUP_RANGE, + _( "You don't have any usable templates.\n\nCompatible templates are: " + name_list ) ); if( !nanofab_template ) { return; @@ -303,8 +316,7 @@ void iexamine::nanofab( player &p, const tripoint &examp ) item new_item( nanofab_template->get_var( "NANOFAB_ITEM_ID" ), calendar::turn ); int qty = std::max( 1, new_item.volume() / 250_ml ); - requirement_data reqs = *requirement_id( "nanofabricator" ) * qty; - + requirement_data reqs = *nanofab_template->type->template_requirements * qty; if( !reqs.can_make_with_inventory( p.crafting_inventory(), is_crafting_component ) ) { popup( "%s", reqs.list_missing() ); return; diff --git a/src/item_factory.cpp b/src/item_factory.cpp index cae3388e03e66..f1811e3470b8c 100644 --- a/src/item_factory.cpp +++ b/src/item_factory.cpp @@ -2937,6 +2937,10 @@ void Item_factory::load_basic_info( const JsonObject &jo, itype &def, const std: def.nanofab_template_group = item_group_id( jo.get_string( "nanofab_template_group" ) ); } + if( jo.has_string( "template_requirements" ) ) { + def.template_requirements = requirement_id( jo.get_string( "template_requirements" ) ); + } + JsonArray jarr = jo.get_array( "min_skills" ); if( !jarr.empty() ) { def.min_skills.clear(); diff --git a/src/itype.h b/src/itype.h index cca71c9846180..c466411862dd8 100644 --- a/src/itype.h +++ b/src/itype.h @@ -1000,6 +1000,8 @@ struct itype { // itemgroup used to generate the recipes within nanofabricator templates. item_group_id nanofab_template_group; + requirement_id template_requirements; + private: /** Can item be combined with other identical items? */ bool stackable_ = false; diff --git a/src/mapdata.cpp b/src/mapdata.cpp index 75575e899c913..0c2555a2b8f6d 100644 --- a/src/mapdata.cpp +++ b/src/mapdata.cpp @@ -1223,6 +1223,8 @@ void ter_t::load( const JsonObject &jo, const std::string &src ) set_connects( jo.get_string( "connects_to" ) ); } + optional( jo, was_loaded, "allowed_template_ids", allowed_template_id ); + optional( jo, was_loaded, "open", open, ter_str_id::NULL_ID() ); optional( jo, was_loaded, "close", close, ter_str_id::NULL_ID() ); optional( jo, was_loaded, "transforms_into", transforms_into, ter_str_id::NULL_ID() ); diff --git a/src/mapdata.h b/src/mapdata.h index 217e34e40aa85..ef683292b8db3 100644 --- a/src/mapdata.h +++ b/src/mapdata.h @@ -373,6 +373,7 @@ struct ter_t : map_data_common_t { trap_id trap; // The id of the trap located at this terrain. Limit one trap per tile currently. std::set emissions; + std::set allowed_template_id; ter_t(); From 3ef3d25b5e9e11dc96147acadc4f79cf71d3bd22 Mon Sep 17 00:00:00 2001 From: John Candlebury Date: Sun, 3 Jan 2021 18:48:26 -0600 Subject: [PATCH 16/43] Update nanofabricator/template json. Add a debug template --- .../furniture_and_terrain/terrain-manufactured.json | 3 ++- data/json/itemgroups/main.json | 6 ++++++ data/json/items/generic.json | 11 +++++++++++ 3 files changed, 19 insertions(+), 1 deletion(-) diff --git a/data/json/furniture_and_terrain/terrain-manufactured.json b/data/json/furniture_and_terrain/terrain-manufactured.json index b9cad4719f910..b418c6274f253 100644 --- a/data/json/furniture_and_terrain/terrain-manufactured.json +++ b/data/json/furniture_and_terrain/terrain-manufactured.json @@ -453,7 +453,7 @@ "color": "dark_gray", "move_cost": 0, "coverage": 65, - "flags": [ "PLACE_ITEM" ], + "flags": [ "PLACE_ITEM", "NANOFAB_TABLE" ], "bash": { "str_min": 120, "str_max": 150, @@ -481,6 +481,7 @@ "name": "nanofabricator control panel", "symbol": "&", "description": "A small computer panel attached to a nanofabricator. It has a single slot for reading templates.", + "allowed_template_ids": [ "standard_template_construct", "debug_template" ], "color": "red", "looks_like": "f_console", "move_cost": 0, diff --git a/data/json/itemgroups/main.json b/data/json/itemgroups/main.json index 84c75a26e9ee5..54d6695ad0398 100644 --- a/data/json/itemgroups/main.json +++ b/data/json/itemgroups/main.json @@ -78,6 +78,12 @@ { "item": "v29", "prob": 10 } ] }, + { + "type": "item_group", + "id": "debug_template_item", + "subtype": "distribution", + "entries": [ { "item": "standard_template_construct", "prob": 10 } ] + }, { "type": "item_group", "id": "nanofab_recipes", diff --git a/data/json/items/generic.json b/data/json/items/generic.json index dc627c9f0e2dc..4151454d7cbdf 100644 --- a/data/json/items/generic.json +++ b/data/json/items/generic.json @@ -1605,11 +1605,22 @@ "price_postapoc": 1000, "material": [ "steel", "plastic" ], "nanofab_template_group": "nanofab_recipes", + "template_requirements": "nanofabricator", "weight": "317 g", "volume": "250 ml", "to_hit": -1, "flags": [ "NANOFAB_TEMPLATE", "TRADER_AVOID" ] }, + { + "type": "GENERIC", + "id": "debug_template", + "copy-from": "standard_template_construct", + "color": "yellow", + "name": { "str": "nanofabricator template" }, + "description": "A state-of-the-art optical storage system, containing the instruction set to replicate itself.", + "nanofab_template_group": "debug_template_item", + "template_requirements": "copper_scrap_equivalent" + }, { "type": "GENERIC", "id": "template_photonics", From a78ca33e64a6117c6056c73190a59f3e43a35524 Mon Sep 17 00:00:00 2001 From: Binrui Dong Date: Sun, 3 Jan 2021 23:45:57 -0500 Subject: [PATCH 17/43] Fix text overflow in NPC dialogue window --- src/npctalk.cpp | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/npctalk.cpp b/src/npctalk.cpp index 043cce282790e..67771d605ec96 100644 --- a/src/npctalk.cpp +++ b/src/npctalk.cpp @@ -1451,6 +1451,13 @@ talk_topic dialogue::opt( dialogue_window &d_win, const std::string &npc_name, d_win.add_history_separator(); + ui_adaptor ui; + const auto resize_cb = [&]( ui_adaptor & ui ) { + d_win.resize_dialogue( ui ); + }; + ui.on_screen_resize( resize_cb ); + resize_cb( ui ); + // Number of lines to highlight const size_t hilight_lines = d_win.add_to_history( challenge ); @@ -1493,12 +1500,6 @@ talk_topic dialogue::opt( dialogue_window &d_win, const std::string &npc_name, }; generate_response_lines(); - ui_adaptor ui; - ui.on_screen_resize( [&]( ui_adaptor & ui ) { - d_win.resize_dialogue( ui ); - } ); - ui.mark_resize(); - ui.on_redraw( [&]( const ui_adaptor & ) { d_win.print_header( npc_name ); d_win.display_responses( hilight_lines, response_lines ); From ff9ca50b40cce840aa73ea736455c38abeaf8247 Mon Sep 17 00:00:00 2001 From: John Candlebury Date: Sun, 3 Jan 2021 23:42:12 -0600 Subject: [PATCH 18/43] Correct string translation Co-authored-by: Binrui Dong --- src/iexamine.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/iexamine.cpp b/src/iexamine.cpp index 3ca5c7157274c..ddd535ae37559 100644 --- a/src/iexamine.cpp +++ b/src/iexamine.cpp @@ -307,7 +307,7 @@ void iexamine::nanofab( player &p, const tripoint &examp ) return e.typeId() == itid; } ); }, _( "Introduce a compatible template." ), PICKUP_RANGE, - _( "You don't have any usable templates.\n\nCompatible templates are: " + name_list ) ); + _( "You don't have any usable templates.\n\nCompatible templates are: " ) + name_list ); if( !nanofab_template ) { return; From 6988d40b18b63f39e18c588a3d16e5e5241cbea3 Mon Sep 17 00:00:00 2001 From: Binrui Dong Date: Mon, 4 Jan 2021 02:10:40 -0500 Subject: [PATCH 19/43] Invalidate tow data on both vehicles on car crash --- src/vehicle.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vehicle.cpp b/src/vehicle.cpp index 03cd917d084fa..7302774f9ec23 100644 --- a/src/vehicle.cpp +++ b/src/vehicle.cpp @@ -6695,7 +6695,7 @@ int vehicle::damage_direct( int p, int dmg, damage_type type ) mon->remove_effect( effect_harnessed ); } if( part_flag( p, "TOW_CABLE" ) ) { - invalidate_towing(); + invalidate_towing( true ); } else { remove_part( p ); } From 7c78dc9bbcb2a7b5b527ca269020506e78c89069 Mon Sep 17 00:00:00 2001 From: LyleSY Date: Mon, 4 Jan 2021 11:20:28 -0500 Subject: [PATCH 20/43] [DinoMod] Dinos bleed --- data/mods/DinoMod/monsters/dinosaur.json | 1 + 1 file changed, 1 insertion(+) diff --git a/data/mods/DinoMod/monsters/dinosaur.json b/data/mods/DinoMod/monsters/dinosaur.json index 4aa22ae67e0e9..7a8ed5c84eb15 100644 --- a/data/mods/DinoMod/monsters/dinosaur.json +++ b/data/mods/DinoMod/monsters/dinosaur.json @@ -4,6 +4,7 @@ "id": "DINOSAUR", "//": "Capping these at 1000 L and kg to prevent bodies and meat disappearing and vision at 50 for less aggro", "fear_triggers": [ "FIRE" ], + "bleeds": "fd_blood", "description": "a dinosaur" }, { From 9d93865f37fef8597ead97b171f3dfe2dff6487f Mon Sep 17 00:00:00 2001 From: casswedson Date: Mon, 4 Jan 2021 17:41:08 -0500 Subject: [PATCH 21/43] obsolete this --- data/json/recipes/recipe_deconstruction.json | 55 ------------------- .../recipe_deconstruction_package.json | 51 ----------------- data/json/recipes/recipe_obsolete.json | 30 ++++++++++ 3 files changed, 30 insertions(+), 106 deletions(-) diff --git a/data/json/recipes/recipe_deconstruction.json b/data/json/recipes/recipe_deconstruction.json index b900a39a932a2..403bfffda476a 100644 --- a/data/json/recipes/recipe_deconstruction.json +++ b/data/json/recipes/recipe_deconstruction.json @@ -3148,26 +3148,6 @@ "qualities": [ { "id": "SCREW", "level": 1 } ], "components": [ [ [ "e_scrap", 3 ] ], [ [ "plastic_chunk", 1 ] ] ] }, - { - "result": "radio_car", - "type": "uncraft", - "activity_level": "LIGHT_EXERCISE", - "skill_used": "electronics", - "time": "20 m", - "qualities": [ { "id": "SCREW", "level": 1 } ], - "components": [ - [ [ "antenna", 1 ] ], - [ [ "cable", 8 ] ], - [ [ "motor_tiny", 1 ] ], - [ [ "motor_micro", 2 ] ], - [ [ "amplifier", 2 ] ], - [ [ "plastic_chunk", 8 ] ], - [ [ "receiver", 1 ] ], - [ [ "e_scrap", 2 ] ], - [ [ "scrap", 3 ] ], - [ [ "radio_car_wheel", 1 ] ] - ] - }, { "result": "radio_car_box", "type": "uncraft", @@ -4204,41 +4184,6 @@ "components": [ [ [ "felt_patch", 10 ] ] ], "flags": [ "BLIND_EASY" ] }, - { - "result": "1st_aid", - "type": "uncraft", - "activity_level": "NO_EXERCISE", - "time": "12 s", - "components": [ - [ [ "medical_tape", 20 ] ], - [ [ "aspirin", 10 ] ], - [ [ "disinfectant", 10 ] ], - [ [ "saline", 5 ] ], - [ [ "adhesive_bandages", 6 ] ], - [ [ "bandages", 3 ] ], - [ [ "medical_gauze", 6 ] ], - [ [ "booklet_firstaid", 1 ] ] - ], - "flags": [ "BLIND_EASY", "UNCRAFT_LIQUIDS_CONTAINED" ] - }, - { - "result": "ifak", - "type": "uncraft", - "activity_level": "NO_EXERCISE", - "time": "12 s", - "components": [ - [ [ "medical_tape", 20 ] ], - [ [ "tourniquet_upper", 1 ] ], - [ [ "quikclot", 5 ] ], - [ [ "bandages", 9 ] ], - [ [ "medical_gauze", 3 ] ], - [ [ "adhesive_bandages", 3 ] ], - [ [ "pur_tablets", 3 ] ], - [ [ "gloves_medical", 1 ] ], - [ [ "scissors", 1 ] ] - ], - "flags": [ "BLIND_EASY" ] - }, { "result": "garlic", "type": "uncraft", diff --git a/data/json/recipes/recipe_deconstruction_package.json b/data/json/recipes/recipe_deconstruction_package.json index 676ded13d91ed..36fb768f05108 100644 --- a/data/json/recipes/recipe_deconstruction_package.json +++ b/data/json/recipes/recipe_deconstruction_package.json @@ -456,56 +456,5 @@ [ [ "bag_plastic", 1 ] ] ], "flags": [ "BLIND_EASY" ] - }, - { - "//": "Until 0.F, allow disassembling legacy toolboxes", - "result": "toolbox", - "type": "uncraft", - "activity_level": "NO_EXERCISE", - "time": "30 s", - "components": [ - [ [ "toolbox_empty", 1 ] ], - [ [ "pockknife", 1 ] ], - [ [ "screwdriver_set", 1 ] ], - [ [ "hacksaw", 1 ] ], - [ [ "wrench", 1 ] ], - [ [ "saw", 1 ] ], - [ [ "hammer", 1 ] ] - ], - "flags": [ "BLIND_EASY" ] - }, - { - "//": "Until 0.F, allow disassembling legacy survivor utility belt", - "result": "survivor_belt", - "type": "uncraft", - "activity_level": "NO_EXERCISE", - "time": "30 s", - "components": [ - [ [ "survivor_belt_notools", 1 ] ], - [ [ "screwdriver_set", 1 ] ], - [ [ "wrench", 1 ] ], - [ [ "saw", 1 ] ], - [ [ "hacksaw", 1 ] ], - [ [ "hammer", 1 ] ] - ] - }, - { - "//": "Until 0.F, allow disassembling legacy workshop toolboxes", - "result": "survivor_belt", - "type": "uncraft", - "activity_level": "NO_EXERCISE", - "time": "30 s", - "components": [ - [ [ "toolbox_workshop_empty", 1 ] ], - [ [ "pockknife", 1 ] ], - [ [ "screwdriver_set", 1 ] ], - [ [ "hacksaw", 1 ] ], - [ [ "wrench", 1 ] ], - [ [ "saw", 1 ] ], - [ [ "hammer", 1 ] ], - [ [ "metal_file", 1 ] ], - [ [ "pin_reamer", 1 ] ], - [ [ "clamp", 1 ] ] - ] } ] diff --git a/data/json/recipes/recipe_obsolete.json b/data/json/recipes/recipe_obsolete.json index 7fd0322f2e72e..0128073322eb3 100644 --- a/data/json/recipes/recipe_obsolete.json +++ b/data/json/recipes/recipe_obsolete.json @@ -14,6 +14,36 @@ "result": "arrow_metal_bodkin", "obsolete": true }, + { + "type": "recipe", + "result": "radio_car", + "obsolete": true + }, + { + "type": "recipe", + "result": "1st_aid", + "obsolete": true + }, + { + "type": "recipe", + "result": "ifak", + "obsolete": true + }, + { + "type": "recipe", + "result": "toolbox", + "obsolete": true + }, + { + "type": "recipe", + "result": "survivor_belt", + "obsolete": true + }, + { + "type": "recipe", + "result": "survivor_belt", + "obsolete": true + }, { "type": "recipe", "result": "survival_kit", From dc3dcd47f3c9e372100b35d77801393fe81eae85 Mon Sep 17 00:00:00 2001 From: Jamuro-g Date: Mon, 4 Jan 2021 15:57:12 +0100 Subject: [PATCH 22/43] Added batch time savings for scrambled eggs Added a 80% time reduction for batches > 2 to the scrambled egg recipes to put them on par with the egg recipes that already had a batch modifier of 80%, 2 --- data/json/recipes/recipe_food.json | 2 ++ 1 file changed, 2 insertions(+) diff --git a/data/json/recipes/recipe_food.json b/data/json/recipes/recipe_food.json index 7fb5922987fec..26ee966ca04bf 100644 --- a/data/json/recipes/recipe_food.json +++ b/data/json/recipes/recipe_food.json @@ -3518,6 +3518,7 @@ "difficulty": 1, "time": "6 m", "autolearn": true, + "batch_time_factors": [ 80, 2 ], "qualities": [ { "id": "COOK", "level": 2 } ], "tools": [ [ [ "surface_heat", 2, "LIST" ] ] ], "components": [ [ [ "eggs_bird", 2, "LIST" ], [ "egg_reptile", 2 ] ] ] @@ -3533,6 +3534,7 @@ "difficulty": 1, "time": "6 m", "autolearn": true, + "batch_time_factors": [ 80, 2 ], "qualities": [ { "id": "COOK", "level": 2 } ], "tools": [ [ [ "surface_heat", 2, "LIST" ] ] ], "components": [ [ [ "powder_eggs", 2 ] ], [ [ "water", 2 ], [ "water_clean", 2 ] ] ] From 87b6e4f6027ce20460807c1a4e8e0ff3af6f8581 Mon Sep 17 00:00:00 2001 From: ToxiClay Date: Mon, 4 Jan 2021 20:08:29 -0500 Subject: [PATCH 23/43] Update folding bicycle material/volumes (#46542) * Modified material used for item folding-bicycle * Updated weight and volume to match a real-world folding bike --- data/json/items/tool/deployable.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/data/json/items/tool/deployable.json b/data/json/items/tool/deployable.json index 7ce4e1331e763..328e2595e0631 100644 --- a/data/json/items/tool/deployable.json +++ b/data/json/items/tool/deployable.json @@ -121,14 +121,14 @@ "type": "TOOL", "name": { "str": "folding bicycle" }, "description": "This is a bicycle folded into a relatively portable package.", - "weight": "9071 g", - "volume": "21500 ml", - "//": "needs huge realism balance, current values are wildly inaccurate", + "weight": "10160 g", + "volume": "76651 ml", + "//": "Data taken from given values/dimensions for the Brompton S1E folding bicycle", "price": 35000, "price_postapoc": 2000, "to_hit": -5, "bashing": 10, - "material": [ "aluminum" ], + "material": [ "steel" ], "symbol": "0", "color": "light_gray", "use_action": { From 40b68af9668ac3d5cc7ec717e9ef8dfeaa53be6d Mon Sep 17 00:00:00 2001 From: Eric <52087122+Ramza13@users.noreply.github.com> Date: Mon, 4 Jan 2021 20:11:54 -0500 Subject: [PATCH 24/43] Fix multi tile door closing (#46536) * Add logic to handle closing other tiles --- src/gates.cpp | 11 +++++++---- src/vehicle.h | 2 ++ src/vehicle_use.cpp | 21 +++++++++++++++++++++ 3 files changed, 30 insertions(+), 4 deletions(-) diff --git a/src/gates.cpp b/src/gates.cpp index 7439a482f6287..bd8c1b9b35ccb 100644 --- a/src/gates.cpp +++ b/src/gates.cpp @@ -283,10 +283,13 @@ void doors::close_door( map &m, Creature &who, const tripoint &closep ) if( !veh->handle_potential_theft( get_avatar() ) ) { return; } - veh->close( closable ); - //~ %1$s - vehicle name, %2$s - part name - who.add_msg_if_player( _( "You close the %1$s's %2$s." ), veh->name, veh->part( closable ).name() ); - didit = true; + Character *ch = who.as_character(); + if( ch && veh->can_close( closable, *ch ) ) { + veh->close( closable ); + //~ %1$s - vehicle name, %2$s - part name + who.add_msg_if_player( _( "You close the %1$s's %2$s." ), veh->name, veh->part( closable ).name() ); + didit = true; + } } else if( inside_closable >= 0 ) { who.add_msg_if_player( m_info, _( "That %s can only be closed from the inside." ), veh->part( inside_closable ).name() ); diff --git a/src/vehicle.h b/src/vehicle.h index 6be91d7c7da2a..fd58b21bedb08 100644 --- a/src/vehicle.h +++ b/src/vehicle.h @@ -1646,6 +1646,8 @@ class vehicle // returns whether the door is open or not bool is_open( int part_index ) const; + bool can_close( int part_index, Character &who ); + // Consists only of parts with the FOLDABLE tag. bool is_foldable() const; // Restore parts of a folded vehicle. diff --git a/src/vehicle_use.cpp b/src/vehicle_use.cpp index a1e3a09c042b1..038e240893429 100644 --- a/src/vehicle_use.cpp +++ b/src/vehicle_use.cpp @@ -1542,6 +1542,27 @@ bool vehicle::is_open( int part_index ) const return parts[part_index].open; } +bool vehicle::can_close( int part_index, Character &who ) +{ + for( auto const &vec : find_lines_of_parts( part_index, "OPENABLE" ) ) { + for( auto const &partID : vec ) { + const Creature *const mon = g->critter_at( global_part_pos3( parts[partID] ) ); + if( mon ) { + if( mon->is_player() ) { + who.add_msg_if_player( m_info, _( "There's some buffoon in the way!" ) ); + } else if( mon->is_monster() ) { + // TODO: Houseflies, mosquitoes, etc shouldn't count + who.add_msg_if_player( m_info, _( "The %s is in the way!" ), mon->get_name() ); + } else { + who.add_msg_if_player( m_info, _( "%s is in the way!" ), mon->disp_name() ); + } + return false; + } + } + } + return true; +} + void vehicle::open_all_at( int p ) { std::vector parts_here = parts_at_relative( parts[p].mount, true ); From 8ca4bccbeed354370c5b65834f6e26a829329738 Mon Sep 17 00:00:00 2001 From: casswedson Date: Mon, 4 Jan 2021 20:31:40 -0500 Subject: [PATCH 25/43] fix tiny error (introduced by me) heh, derped really hard, not gonna lie. --- data/json/recipes/recipe_deconstruction.json | 20 ++++++++++++++------ data/json/recipes/recipe_obsolete.json | 2 +- 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/data/json/recipes/recipe_deconstruction.json b/data/json/recipes/recipe_deconstruction.json index 403bfffda476a..acf590609d326 100644 --- a/data/json/recipes/recipe_deconstruction.json +++ b/data/json/recipes/recipe_deconstruction.json @@ -3149,15 +3149,23 @@ "components": [ [ [ "e_scrap", 3 ] ], [ [ "plastic_chunk", 1 ] ] ] }, { - "result": "radio_car_box", + "result": "radio_car", "type": "uncraft", "activity_level": "LIGHT_EXERCISE", - "time": "2 m", + "skill_used": "electronics", + "time": "20 m", + "qualities": [ { "id": "SCREW", "level": 1 } ], "components": [ - [ [ "radio_car", 1 ] ], - [ [ "radiocontrol", 1 ] ], - [ [ "light_minus_battery_cell", 1 ] ], - [ [ "light_battery_cell", 1 ] ] + [ [ "antenna", 1 ] ], + [ [ "cable", 8 ] ], + [ [ "motor_tiny", 1 ] ], + [ [ "motor_micro", 2 ] ], + [ [ "amplifier", 2 ] ], + [ [ "plastic_chunk", 8 ] ], + [ [ "receiver", 1 ] ], + [ [ "e_scrap", 2 ] ], + [ [ "scrap", 3 ] ], + [ [ "radio_car_wheel", 1 ] ] ] }, { diff --git a/data/json/recipes/recipe_obsolete.json b/data/json/recipes/recipe_obsolete.json index 0128073322eb3..fa2d49c5927a1 100644 --- a/data/json/recipes/recipe_obsolete.json +++ b/data/json/recipes/recipe_obsolete.json @@ -16,7 +16,7 @@ }, { "type": "recipe", - "result": "radio_car", + "result": "radio_car_box", "obsolete": true }, { From 98f5dacd9f6c1e689aa1dcc9cddf47c0646d5ea3 Mon Sep 17 00:00:00 2001 From: Perry Fraser Date: Mon, 4 Jan 2021 20:58:48 -0500 Subject: [PATCH 26/43] Correct form of "costs" on scenario selection screen --- src/newcharacter.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/newcharacter.cpp b/src/newcharacter.cpp index 52fc28ca213ff..6ea407567f4bb 100644 --- a/src/newcharacter.cpp +++ b/src/newcharacter.cpp @@ -2198,7 +2198,7 @@ tab_direction set_scenario( avatar &u, points_left &points, } else { //~ 1s - scenario name, 2d - current character points. scen_msg_temp = ngettext( "Scenario %1$s costs %2$d point", - "Scenario %1$s cost %2$d points", + "Scenario %1$s costs %2$d points", pointsForScen ); } } else { @@ -2207,7 +2207,7 @@ tab_direction set_scenario( avatar &u, points_left &points, "Scenario earns %2$d points", pointsForScen ); } else { scen_msg_temp = ngettext( "Scenario costs %2$d point", - "Scenario cost %2$d points", pointsForScen ); + "Scenario costs %2$d points", pointsForScen ); } } From aef74c40117c8d66355aec86964b67b308a9d88b Mon Sep 17 00:00:00 2001 From: curstwist <39442864+curstwist@users.noreply.github.com> Date: Mon, 4 Jan 2021 21:24:19 -0500 Subject: [PATCH 27/43] adjust floor terrains in lab nested chunks --- .../lab/lab_surface/lab_surface_nested.json | 71 ++++++++++++++----- 1 file changed, 53 insertions(+), 18 deletions(-) diff --git a/data/json/mapgen/lab/lab_surface/lab_surface_nested.json b/data/json/mapgen/lab/lab_surface/lab_surface_nested.json index da5be15a3e112..08d2e9f76b56a 100644 --- a/data/json/mapgen/lab/lab_surface/lab_surface_nested.json +++ b/data/json/mapgen/lab/lab_surface/lab_surface_nested.json @@ -13,6 +13,7 @@ " d", " BBB" ], + "terrain": { " ": "t_null" }, "items": { "d": [ { "item": "office", "chance": 25, "repeat": [ 1, 3 ] }, { "item": "electronics", "chance": 15 } ], "H": { "item": "bed", "chance": 15 }, @@ -38,6 +39,7 @@ " cd", " ix" ], + "terrain": { " ": "t_null" }, "items": { "d": [ { "item": "office", "chance": 25, "repeat": [ 1, 3 ] }, { "item": "electronics", "chance": 15 } ], "H": { "item": "bed", "chance": 15 }, @@ -65,6 +67,7 @@ " cd", " dd" ], + "terrain": { " ": "t_null" }, "items": { "d": [ { "item": "office", "chance": 25, "repeat": [ 1, 3 ] }, { "item": "electronics", "chance": 15 } ], "H": { "item": "bed", "chance": 15 }, @@ -117,6 +120,7 @@ "dc ", "xi " ], + "terrain": { " ": "t_null" }, "items": { "d": [ { "item": "office", "chance": 25, "repeat": [ 1, 3 ] }, { "item": "electronics", "chance": 15 } ], "H": { "item": "bed", "chance": 15 }, @@ -144,6 +148,7 @@ "dc ", "dd " ], + "terrain": { " ": "t_null" }, "items": { "d": [ { "item": "office", "chance": 25, "repeat": [ 1, 3 ] }, { "item": "electronics", "chance": 15 } ], "H": { "item": "bed", "chance": 15 }, @@ -171,6 +176,7 @@ " ", " HHH" ], + "terrain": { " ": "t_null" }, "items": { "d": [ { "item": "office", "chance": 25, "repeat": [ 1, 3 ] }, { "item": "electronics", "chance": 15 } ], "H": { "item": "bed", "chance": 15 }, @@ -198,6 +204,7 @@ "i d", " cd" ], + "terrain": { " ": "t_null" }, "items": { "d": [ { "item": "office", "chance": 25, "repeat": [ 1, 3 ] }, { "item": "electronics", "chance": 15 } ], "H": { "item": "bed", "chance": 15 }, @@ -225,6 +232,7 @@ " cd", " dd" ], + "terrain": { " ": "t_null" }, "items": { "d": [ { "item": "office", "chance": 25, "repeat": [ 1, 3 ] }, { "item": "electronics", "chance": 15 } ], "H": { "item": "bed", "chance": 15 }, @@ -252,6 +260,7 @@ " cd", " xdd" ], + "terrain": { " ": "t_null" }, "items": { "d": [ { "item": "office", "chance": 25, "repeat": [ 1, 3 ] }, { "item": "electronics", "chance": 15 } ], "H": { "item": "bed", "chance": 15 }, @@ -279,6 +288,7 @@ " ", " iBB" ], + "terrain": { " ": "t_null" }, "items": { "d": [ { "item": "office", "chance": 25, "repeat": [ 1, 3 ] }, { "item": "electronics", "chance": 15 } ], "H": { "item": "bed", "chance": 15 }, @@ -306,6 +316,7 @@ "B ", "BBB " ], + "terrain": { " ": "t_null" }, "items": { "d": [ { "item": "office", "chance": 25, "repeat": [ 1, 3 ] }, { "item": "electronics", "chance": 15 } ], "H": { "item": "bed", "chance": 15 }, @@ -333,6 +344,7 @@ "d i", "dc " ], + "terrain": { " ": "t_null" }, "items": { "d": [ { "item": "office", "chance": 25, "repeat": [ 1, 3 ] }, { "item": "electronics", "chance": 15 } ], "H": { "item": "bed", "chance": 15 }, @@ -360,6 +372,7 @@ "dc ", "dd " ], + "terrain": { " ": "t_null" }, "items": { "d": [ { "item": "office", "chance": 25, "repeat": [ 1, 3 ] }, { "item": "electronics", "chance": 15 } ], "H": { "item": "bed", "chance": 15 }, @@ -387,6 +400,7 @@ "dc ", "ddx " ], + "terrain": { " ": "t_null" }, "items": { "d": [ { "item": "office", "chance": 25, "repeat": [ 1, 3 ] }, { "item": "electronics", "chance": 15 } ], "H": { "item": "bed", "chance": 15 }, @@ -414,6 +428,7 @@ " ", "BBi " ], + "terrain": { " ": "t_null" }, "items": { "d": [ { "item": "office", "chance": 25, "repeat": [ 1, 3 ] }, { "item": "electronics", "chance": 15 } ], "H": { "item": "bed", "chance": 15 }, @@ -695,6 +710,7 @@ "os ", "bbbbS" ], + "terrain": { " ": "t_null" }, "items": { "b": { "item": "tools_science", "chance": 30, "repeat": [ 1, 2 ] }, "f": { "item": "supplies_reagents_lab", "chance": 60, "repeat": [ 1, 5 ] }, @@ -722,6 +738,7 @@ " ", "f8SCh" ], + "terrain": { " ": "t_null" }, "items": { "b": { "item": "tools_science", "chance": 30, "repeat": [ 1, 2 ] }, "f": { "item": "supplies_reagents_lab", "chance": 60, "repeat": [ 1, 5 ] }, @@ -749,6 +766,7 @@ "bs f", "bbSDC" ], + "terrain": { " ": "t_null" }, "items": { "b": { "item": "tools_science", "chance": 30, "repeat": [ 1, 2 ] }, "f": { "item": "supplies_reagents_lab", "chance": 60, "repeat": [ 1, 5 ] }, @@ -776,6 +794,7 @@ " sbs ", "fbbbf" ], + "terrain": { " ": "t_null" }, "items": { "b": { "item": "tools_science", "chance": 30, "repeat": [ 1, 2 ] }, "f": { "item": "supplies_reagents_lab", "chance": 60, "repeat": [ 1, 5 ] }, @@ -804,6 +823,7 @@ " s ", "Fbhf8" ], + "terrain": { " ": "t_null" }, "items": { "b": { "item": "tools_science", "chance": 30, "repeat": [ 1, 2 ] }, "f": { "item": "supplies_reagents_lab", "chance": 60, "repeat": [ 1, 5 ] }, @@ -832,6 +852,7 @@ " sHs ", "bbMbb" ], + "terrain": { " ": "t_null" }, "items": { "b": { "item": "tools_science", "chance": 30, "repeat": [ 1, 2 ] }, "f": { "item": "supplies_reagents_lab", "chance": 60, "repeat": [ 1, 5 ] }, @@ -860,6 +881,7 @@ "#=#sb", "N #bb" ], + "terrain": { " ": "t_null" }, "items": { "b": { "item": "tools_science", "chance": 30, "repeat": [ 1, 2 ] }, "f": { "item": "supplies_reagents_lab", "chance": 60, "repeat": [ 1, 5 ] }, @@ -888,6 +910,7 @@ "|-|-|", "dc|cd" ], + "terrain": { " ": "t_null" }, "items": { "d": [ { "item": "office", "chance": 50, "repeat": [ 2, 5 ] }, @@ -912,6 +935,7 @@ "fsHs8", "bbMbb" ], + "terrain": { " ": "t_null" }, "items": { "b": { "item": "tools_science", "chance": 30, "repeat": [ 1, 2 ] }, "f": { "item": "supplies_reagents_lab", "chance": 60, "repeat": [ 1, 5 ] }, @@ -943,7 +967,7 @@ " s s ", " sts " ], - "terrain": { "r": "t_linoleum_white", "f": "t_linoleum_white" }, + "terrain": { "r": "t_linoleum_white", "f": "t_linoleum_white", " ": "t_null" }, "furniture": { "v": "f_glass_fridge" }, "items": { "v": { "item": "coffee_shop", "chance": 90, "repeat": [ 2, 10 ] }, @@ -971,6 +995,7 @@ "Httc ", "HHc " ], + "terrain": { " ": "t_null" }, "furniture": { "c": [ "f_armchair", "f_armchair", "f_null", "f_null", "f_null", "f_null" ] }, "items": { "t": { "item": "magazines", "chance": 20 } }, "palettes": [ "lab_surface_palette" ] @@ -994,6 +1019,7 @@ "ctc ctc", " c " ], + "terrain": { " ": "t_null" }, "furniture": { "c": "f_armchair" }, "items": { "t": { "item": "magazines", "chance": 20 } }, "palettes": [ "lab_surface_palette" ] @@ -1017,6 +1043,7 @@ " x%%% ", " Cffr " ], + "terrain": { " ": "t_null" }, "furniture": { "f": "f_glass_fridge" }, "items": { "f": { "item": "fridgesnacks", "chance": 90, "repeat": [ 2, 15 ] }, @@ -1047,6 +1074,7 @@ " ccccc ", " " ], + "terrain": { " ": "t_null" }, "items": { "c": { "item": "office", "chance": 8 }, "t": { "item": "electronics", "chance": 15 } }, "palettes": [ "lab_surface_palette" ] } @@ -1072,7 +1100,7 @@ " dc dc dc ", " " ], - "terrain": { "%": "t_carpet_concrete_yellow" }, + "terrain": { " ": "t_null", "%": "t_carpet_concrete_yellow" }, "items": { "d": { "item": "office", "chance": 8 } }, "palettes": [ "lab_surface_palette" ] } @@ -1098,7 +1126,7 @@ " cxxc cxxc ", " " ], - "terrain": { "%": "t_carpet_concrete_yellow" }, + "terrain": { " ": "t_null" }, "palettes": [ "lab_surface_palette" ] } }, @@ -1367,6 +1395,7 @@ "Ull### llll", " =H= &" ], + "terrain": { " ": "t_null" }, "furniture": { "H": "f_shower" }, "items": { "b": [ @@ -1408,6 +1437,7 @@ "f & - sb", " #f &S" ], + "terrain": { " ": "t_null" }, "items": { "b": { "item": "tools_science", "chance": 30, "repeat": [ 1, 2 ] }, "f": { "item": "supplies_reagents_lab", "chance": 70, "repeat": [ 2, 5 ] }, @@ -1445,6 +1475,7 @@ "S e#Ull", "CC& = " ], + "terrain": { " ": "t_null" }, "items": { "b": { "item": "tools_science", "chance": 30, "repeat": [ 1, 2 ] }, "f": { "item": "supplies_reagents_lab", "chance": 70, "repeat": [ 2, 5 ] }, @@ -1481,6 +1512,7 @@ "llU#& - sb", " - #S Fb" ], + "terrain": { " ": "t_null" }, "items": { "b": { "item": "tools_science", "chance": 30, "repeat": [ 1, 2 ] }, "f": { "item": "supplies_reagents_lab", "chance": 70, "repeat": [ 2, 5 ] }, @@ -1518,6 +1550,7 @@ "bs - &#Ull", "bF S# - " ], + "terrain": { " ": "t_null" }, "items": { "b": { "item": "tools_science", "chance": 30, "repeat": [ 1, 2 ] }, "f": { "item": "supplies_reagents_lab", "chance": 70, "repeat": [ 2, 5 ] }, @@ -1555,6 +1588,7 @@ "bs &#Ull", "bbG - " ], + "terrain": { " ": "t_null" }, "items": { "b": { "item": "tools_science", "chance": 30, "repeat": [ 1, 2 ] }, "f": { "item": "supplies_reagents_lab", "chance": 70, "repeat": [ 2, 5 ] }, @@ -1591,6 +1625,7 @@ "bs ", "bbb& CSC& " ], + "terrain": { " ": "t_null" }, "items": { "b": [ { "item": "tools_science", "chance": 30, "repeat": [ 1, 2 ] }, @@ -1736,7 +1771,7 @@ "mapgensize": [ 1, 1 ], "rows": [ "X" ], "//": "Places a crate, 25% chance of being pre-opened.", - "terrain": { "X": "t_strconc_floor" }, + "terrain": { "X": "t_null" }, "furniture": { "X": [ [ "f_cardboard_box", 2 ], [ "f_crate_c", 3 ], "f_crate_o" ] }, "place_items": [ { "item": "supplies_reagents_lab", "x": 0, "y": 0, "chance": 90, "repeat": [ 5, 20 ] } ] } @@ -1749,7 +1784,7 @@ "mapgensize": [ 1, 1 ], "rows": [ "X" ], "//": "Places a crate, 25% chance of being pre-opened.", - "terrain": { "X": "t_strconc_floor" }, + "terrain": { "X": "t_null" }, "furniture": { "X": [ [ "f_cardboard_box", 2 ], [ "f_crate_c", 3 ], "f_crate_o" ] }, "place_items": [ { "item": "tools_science", "x": 0, "y": 0, "chance": 90, "repeat": [ 5, 10 ] } ] } @@ -1762,7 +1797,7 @@ "mapgensize": [ 1, 1 ], "rows": [ "X" ], "//": "Places a crate, 25% chance of being pre-opened.", - "terrain": { "X": "t_strconc_floor" }, + "terrain": { "X": "t_null" }, "furniture": { "X": [ [ "f_cardboard_box", 2 ], [ "f_crate_c", 3 ], "f_crate_o" ] }, "place_items": [ { "item": "electronics", "x": 0, "y": 0, "chance": 90, "repeat": [ 1, 10 ] } ] } @@ -1775,7 +1810,7 @@ "mapgensize": [ 1, 1 ], "rows": [ "X" ], "//": "Places a crate, 25% chance of being pre-opened.", - "terrain": { "X": "t_strconc_floor" }, + "terrain": { "X": "t_null" }, "furniture": { "X": [ [ "f_cardboard_box", 2 ], [ "f_crate_c", 3 ], "f_crate_o" ] }, "place_items": [ { "item": "hardware_bulk", "x": 0, "y": 0, "chance": 90, "repeat": [ 1, 5 ] } ] } @@ -1788,7 +1823,7 @@ "mapgensize": [ 1, 1 ], "rows": [ "X" ], "//": "Places a crate, 25% chance of being pre-opened.", - "terrain": { "X": "t_strconc_floor" }, + "terrain": { "X": "t_null" }, "furniture": { "X": [ [ "f_cardboard_box", 2 ], [ "f_crate_c", 3 ], "f_crate_o" ] }, "place_items": [ { "item": "produce", "x": 0, "y": 0, "chance": 90, "repeat": [ 5, 10 ] } ] } @@ -1801,7 +1836,7 @@ "mapgensize": [ 1, 1 ], "rows": [ "X" ], "//": "Places a crate, 25% chance of being pre-opened.", - "terrain": { "X": "t_strconc_floor" }, + "terrain": { "X": "t_null" }, "furniture": { "X": [ [ "f_cardboard_box", 2 ], [ "f_crate_c", 3 ], "f_crate_o" ] }, "place_items": [ { "item": "supplies_samples_lab", "x": 0, "y": 0, "chance": 90, "repeat": [ 10, 20 ] } ] } @@ -1814,7 +1849,7 @@ "mapgensize": [ 1, 1 ], "rows": [ "X" ], "//": "Places a crate, 25% chance of being pre-opened.", - "terrain": { "X": "t_strconc_floor" }, + "terrain": { "X": "t_null" }, "furniture": { "X": [ [ "f_cardboard_box", 2 ], [ "f_crate_c", 3 ], "f_crate_o" ] }, "place_items": [ { "item": "snacks", "x": 0, "y": 0, "chance": 50, "repeat": [ 5, 10 ] } ] } @@ -1827,7 +1862,7 @@ "mapgensize": [ 1, 1 ], "rows": [ "X" ], "//": "Places a crate, 25% chance of being pre-opened.", - "terrain": { "X": "t_strconc_floor" }, + "terrain": { "X": "t_null" }, "furniture": { "X": [ [ "f_cardboard_box", 2 ], [ "f_crate_c", 3 ], "f_crate_o" ] }, "place_items": [ { "item": "hospital_lab", "x": 0, "y": 0, "chance": 90, "repeat": [ 5, 10 ] } ] } @@ -1839,7 +1874,7 @@ "object": { "mapgensize": [ 1, 1 ], "rows": [ "X" ], - "terrain": { "X": "t_strconc_floor" }, + "terrain": { "X": "t_null" }, "furniture": { "X": "f_sample_freezer" }, "place_items": [ { "item": "supplies_reagents_lab", "x": 0, "y": 0, "chance": 70, "repeat": [ 1, 5 ] } ] } @@ -1851,7 +1886,7 @@ "object": { "mapgensize": [ 1, 1 ], "rows": [ "X" ], - "terrain": { "X": "t_strconc_floor" }, + "terrain": { "X": "t_null" }, "furniture": { "X": "f_counter" }, "place_items": [ { "item": "snacks", "x": 0, "y": 0, "chance": 60, "repeat": [ 1, 5 ] } ] } @@ -1863,7 +1898,7 @@ "object": { "mapgensize": [ 1, 1 ], "rows": [ "X" ], - "terrain": { "X": "t_strconc_floor" }, + "terrain": { "X": "t_null" }, "furniture": { "X": "f_fridge" }, "place_items": [ { "item": "fridge", "x": 0, "y": 0, "chance": 60, "repeat": [ 1, 5 ] }, @@ -1878,7 +1913,7 @@ "object": { "mapgensize": [ 1, 1 ], "rows": [ "X" ], - "terrain": { "X": "t_strconc_floor" }, + "terrain": { "X": "t_null" }, "furniture": { "X": [ "f_IV_pole", "f_desk", "f_counter" ] } } }, @@ -1889,7 +1924,7 @@ "object": { "mapgensize": [ 1, 1 ], "rows": [ "X" ], - "terrain": { "X": "t_strconc_floor" }, + "terrain": { "X": "t_null" }, "furniture": { "X": [ "f_autoclave", "f_console_broken", "f_centrifuge" ] } } }, @@ -1900,7 +1935,7 @@ "object": { "mapgensize": [ 1, 1 ], "rows": [ "X" ], - "terrain": { "X": "t_strconc_floor" }, + "terrain": { "X": "t_null" }, "furniture": { "X": [ "f_machinery_old", "f_machinery_light", "f_machinery_electronic" ] } } }, @@ -1911,7 +1946,7 @@ "object": { "mapgensize": [ 1, 1 ], "rows": [ "X" ], - "terrain": { "X": "t_strconc_floor" }, + "terrain": { "X": "t_null" }, "furniture": { "X": "f_glass_cabinet" }, "place_items": [ { "item": "lab_bookshelves", "x": 0, "y": 0, "chance": 70, "repeat": [ 1, 5 ] } ] } From 618a3be79ef7de19e1e339ef0714250143cb12f5 Mon Sep 17 00:00:00 2001 From: Hirmuolio <22011552+Hirmuolio@users.noreply.github.com> Date: Tue, 5 Jan 2021 04:29:15 +0200 Subject: [PATCH 28/43] Display power unit for bionic power and batteries (#46514) --- data/json/items/ammo_types.json | 2 +- src/player_display.cpp | 18 +++++++++++++++--- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/data/json/items/ammo_types.json b/data/json/items/ammo_types.json index 62d1b662f1b08..eb3620ad13cc1 100644 --- a/data/json/items/ammo_types.json +++ b/data/json/items/ammo_types.json @@ -374,7 +374,7 @@ { "type": "ammunition_type", "id": "battery", - "name": "batteries", + "name": "kJ", "default": "battery" }, { diff --git a/src/player_display.cpp b/src/player_display.cpp index 3937a716b442d..4e38f3d86935f 100644 --- a/src/player_display.cpp +++ b/src/player_display.cpp @@ -632,11 +632,23 @@ static void draw_bionics_tab( const catacurses::window &w_bionics, const bool is_current_tab = curtab == player_display_tab::bionics; const nc_color title_col = is_current_tab ? h_light_gray : c_light_gray; center_print( w_bionics, 0, title_col, _( title_BIONICS ) ); + int power_amount; + std::string power_unit; + if( you.get_power_level() < 1_J ) { + power_amount = units::to_millijoule( you.get_power_level() ); + power_unit = "mJ"; + } else if( you.get_power_level() < 1_kJ ) { + power_amount = units::to_joule( you.get_power_level() ); + power_unit = "J"; + } else { + power_amount = units::to_kilojoule( you.get_power_level() ); + power_unit = "kJ"; + } // NOLINTNEXTLINE(cata-use-named-point-constants) trim_and_print( w_bionics, point( 1, 1 ), getmaxx( w_bionics ) - 1, c_white, - string_format( _( "Bionic Power: %1$d" - " / %2$d" ), - units::to_kilojoule( you.get_power_level() ), units::to_kilojoule( you.get_max_power_level() ) ) ); + string_format( _( "Power: %1$d %2$s" + " / %3$d kJ" ), + power_amount, power_unit, units::to_kilojoule( you.get_max_power_level() ) ) ); const size_t useful_y = bionics_win_size_y - 2; const size_t half_y = useful_y / 2; From ed2e44e1404b68e5a5fb88ba752aab089f996ef0 Mon Sep 17 00:00:00 2001 From: John Candlebury Date: Tue, 5 Jan 2021 00:43:48 -0600 Subject: [PATCH 29/43] Mission leading to Hub 01 from the Evac Center (#46503) --- data/json/items/generic.json | 14 ++++ .../NPC_free_merchant_shopkeep.json | 73 +++++++++++++++++++ .../npcs/robofac/NPC_ROBOFAC_INTERCOM.json | 66 ++++++++++++++++- .../overmap/overmap_special/specials.json | 2 +- 4 files changed, 153 insertions(+), 2 deletions(-) diff --git a/data/json/items/generic.json b/data/json/items/generic.json index dc627c9f0e2dc..f30ddab69e566 100644 --- a/data/json/items/generic.json +++ b/data/json/items/generic.json @@ -592,6 +592,20 @@ "pocket_data": [ { "pocket_type": "SOFTWARE", "max_contains_volume": "1 L", "max_contains_weight": "1 kg" } ], "to_hit": -3 }, + { + "type": "GENERIC", + "id": "fema_data", + "symbol": ",", + "color": "white", + "name": { "str_sp": "FEMA data" }, + "description": "Evacuation plans, disaster risk projections, personnel lists and last minute communications from just before the end, all scrubbed from FEMA servers and stored in an external hdd.", + "price": 10000, + "price_postapoc": 50, + "material": [ "plastic" ], + "weight": "30 g", + "volume": "250 ml", + "to_hit": -3 + }, { "type": "GENERIC", "id": "candlestick", diff --git a/data/json/npcs/refugee_center/surface_staff/NPC_free_merchant_shopkeep.json b/data/json/npcs/refugee_center/surface_staff/NPC_free_merchant_shopkeep.json index 6ea22e615a7ba..3d866d7007c03 100644 --- a/data/json/npcs/refugee_center/surface_staff/NPC_free_merchant_shopkeep.json +++ b/data/json/npcs/refugee_center/surface_staff/NPC_free_merchant_shopkeep.json @@ -321,6 +321,46 @@ "dynamic_line": "Well, there is a party of about a dozen 'scavengers' that found some sort of government facility. They bring us a literal truck load of jumpsuits, m4's, and canned food every week or so. Since some of those guys got family here, we've been doing alright. As to where it is, I don't have the foggiest of ideas.", "responses": [ { "text": "Thanks, I'll keep an eye out.", "topic": "TALK_EVAC_MERCHANT" } ] }, + { + "id": "TALK_EVAC_MERCHANT_HUB01", + "type": "talk_topic", + "dynamic_line": "We had an odd group came by two days after the power grid went off, two armed guards wearing brown uniforms, escorting what seemed to be a gov' doctor of some sort, had a hazmat suit and mask they never took off. They looked rather organized, probably military remnant from somewhere close by.", + "responses": [ + { "text": "I was hoping for something more substantial than that.", "topic": "TALK_EVAC_MERCHANT" }, + { "text": "Just that?", "topic": "TALK_EVAC_MERCHANT_HUB01_ACTIVITY" } + ] + }, + { + "id": "TALK_EVAC_MERCHANT_HUB01_ACTIVITY", + "type": "talk_topic", + "dynamic_line": "They surely didn't seem the nice talkative type. Doctor paid us in advance to get some old FEMA info from our computers here, told us to deliver the data to some remote locale afterwards. Still haven't done that however, we're half thinking it must have been some sort of strange trap.", + "responses": [ + { "text": "Think you could share that meeting location?", "topic": "TALK_EVAC_MERCHANT_HUB01_DELIVERY_MISSION_OFFER" }, + { "text": "Seems like you are right to be suspicious.", "topic": "TALK_EVAC_MERCHANT" } + ] + }, + { + "id": "TALK_EVAC_MERCHANT_HUB01_DELIVERY_MISSION_OFFER", + "type": "talk_topic", + "dynamic_line": "I could share the drop off location if you agree to deliver the data for us. But I'm certain that you won't find whatever you seek in there.", + "responses": [ + { + "text": "Seems like a deal to me.", + "topic": "TALK_EVAC_MERCHANT_HUB_DELIVERY_ACCEPTED", + "effect": { "add_mission": "MISSION_FREE_MERCHANTS_HUB_DELIVERY_1" } + }, + { "text": "That seems rather dangerous, I think I'll pass.", "topic": "TALK_EVAC_MERCHANT" } + ] + }, + { + "id": "TALK_EVAC_MERCHANT_HUB_DELIVERY_ACCEPTED", + "type": "talk_topic", + "dynamic_line": "Location should be an old isolated building not far from here, you should probably just leave the hdd to the persons there and be off your way. And as I said, be careful approaching the place, I wouldn't like leading you straight into your death.", + "responses": [ + { "text": "Don't worry, I'll be careful.", "topic": "TALK_DONE" }, + { "text": "Pretty sure I'll manage.", "topic": "TALK_DONE" } + ] + }, { "id": "TALK_EVAC_MERCHANT_NO", "type": "talk_topic", @@ -360,6 +400,7 @@ "responses": [ { "text": "Hordes?", "topic": "TALK_EVAC_MERCHANT_HORDES" }, { "text": "Heard of anything better than the odd gun cache?", "topic": "TALK_EVAC_MERCHANT_PRIME_LOOT" }, + { "text": "Any other settlements nearby?", "topic": "TALK_EVAC_MERCHANT_HUB01" }, { "text": "Was hoping for something more…", "topic": "TALK_EVAC_MERCHANT" } ] }, @@ -684,5 +725,37 @@ "success_lie": "What good does this do us?", "failure": "It was a lost cause anyways…" } + }, + { + "id": "MISSION_FREE_MERCHANTS_HUB_DELIVERY_1", + "type": "mission_definition", + "name": { "str": "Unknown Recipient" }, + "description": "Deliver the hdd to the military remnant. Return to the Evacuation Center when the trade is complete.", + "goal": "MGOAL_CONDITION", + "difficulty": 5, + "start": { + "effect": [ { "u_buy_item": "fema_data", "count": 1 } ], + "assign_mission_target": { + "om_terrain": "robofachq_surface_entrance", + "om_special": "hub_01", + "reveal_radius": 1, + "random": true, + "search_range": 280 + } + }, + "goal_condition": { "u_has_var": "completed_robofac_intercom_1", "type": "dialogue", "context": "intercom", "value": "yes" }, + "value": 5000, + "origins": [ "ORIGIN_SECONDARY" ], + "dialogue": { + "describe": "…", + "offer": "…", + "accepted": "…", + "rejected": "…", + "advice": "…", + "inquire": "Have you finished the delivery already?", + "success": "Good, that's one less thing to worry about.", + "success_lie": "What good does this do us?", + "failure": "At least you came back with your life…" + } } ] diff --git a/data/json/npcs/robofac/NPC_ROBOFAC_INTERCOM.json b/data/json/npcs/robofac/NPC_ROBOFAC_INTERCOM.json index bed20e0419179..102ea5095169b 100644 --- a/data/json/npcs/robofac/NPC_ROBOFAC_INTERCOM.json +++ b/data/json/npcs/robofac/NPC_ROBOFAC_INTERCOM.json @@ -286,11 +286,45 @@ "sentinel": "robofac_intercom_firstmeet" }, "responses": [ + { + "text": "I was sent here by the traders at the refugee center. They told me to deliver this hdd drive to you.", + "condition": { + "and": [ + { "not": { "u_has_var": "completed_robofac_intercom_1", "type": "dialogue", "context": "intercom", "value": "yes" } }, + { + "not": { "u_has_var": "completed_free_merchants_hub_delivery_1", "type": "dialogue", "context": "intercom", "value": "yes" } + }, + { "u_has_mission": "MISSION_FREE_MERCHANTS_HUB_DELIVERY_1" } + ] + }, + "topic": "TALK_ROBOFAC_INTERCOM_FREE_MERCHANT_DELIVERY_1" + }, + { + "text": "The traders also mentioned you were looking for help.", + "condition": { + "and": [ + { "not": { "u_has_var": "completed_robofac_intercom_1", "type": "dialogue", "context": "intercom", "value": "yes" } }, + { + "u_has_var": "completed_free_merchants_hub_delivery_1", + "type": "dialogue", + "context": "intercom", + "value": "yes" + } + ] + }, + "trial": { "type": "LIE", "difficulty": 1 }, + "success": { "topic": "MISSION_ROBOFAC_INTERCOM_1_INTRODUCTION" }, + "failure": { "topic": "MISSION_ROBOFAC_INTERCOM_1_INTRODUCTION" } + }, { "text": "Wait! What??", "condition": { "and": [ { "not": { "u_has_var": "completed_robofac_intercom_1", "type": "dialogue", "context": "intercom", "value": "yes" } }, + { + "not": { "u_has_var": "completed_free_merchants_hub_delivery_1", "type": "dialogue", "context": "intercom", "value": "yes" } + }, + { "not": { "u_has_mission": "MISSION_FREE_MERCHANTS_HUB_DELIVERY_1" } }, { "not": { "u_has_mission": "MISSION_ROBOFAC_INTERCOM_1" } } ] }, @@ -301,6 +335,10 @@ "condition": { "and": [ { "not": { "u_has_var": "completed_robofac_intercom_1", "type": "dialogue", "context": "intercom", "value": "yes" } }, + { + "not": { "u_has_var": "completed_free_merchants_hub_delivery_1", "type": "dialogue", "context": "intercom", "value": "yes" } + }, + { "not": { "u_has_mission": "MISSION_FREE_MERCHANTS_HUB_DELIVERY_1" } }, { "not": { "u_has_mission": "MISSION_ROBOFAC_INTERCOM_1" } } ] }, @@ -311,6 +349,10 @@ "condition": { "and": [ { "not": { "u_has_var": "completed_robofac_intercom_1", "type": "dialogue", "context": "intercom", "value": "yes" } }, + { + "not": { "u_has_var": "completed_free_merchants_hub_delivery_1", "type": "dialogue", "context": "intercom", "value": "yes" } + }, + { "not": { "u_has_mission": "MISSION_FREE_MERCHANTS_HUB_DELIVERY_1" } }, { "not": { "u_has_mission": "MISSION_ROBOFAC_INTERCOM_1" } } ] }, @@ -334,7 +376,7 @@ "truefalsetext": { "condition": { "npc_has_effect": "npc_said" }, "true": "There are other things I need to bring up…", - "false": "We have nothing else to offer currently." + "false": "[Identify yourself before the Intercom]" }, "condition": { "and": [ @@ -530,6 +572,28 @@ { "text": "I have to give it a thought.", "topic": "TALK_ROBOFAC_INTERCOM_SERVICES" } ] }, + { + "id": "TALK_ROBOFAC_INTERCOM_FREE_MERCHANT_DELIVERY_1", + "type": "talk_topic", + "dynamic_line": "Understood. Please drop the drive on the box embedded beneath the intercom. You are welcome to leave afterwards.", + "responses": [ + { + "text": "[Do as the Intercom Says]", + "condition": { "u_has_items": { "item": "fema_data", "count": 1 } }, + "effect": [ + { "u_sell_item": "fema_data", "count": 1 }, + { + "u_add_var": "completed_free_merchants_hub_delivery_1", + "type": "dialogue", + "context": "intercom", + "value": "yes" + } + ], + "topic": "TALK_DONE" + }, + { "text": "Didn't bring the hdd now, let me return with it.", "topic": "TALK_DONE" } + ] + }, { "id": "TALK_ROBOFAC_INTERCOM_PLEAD", "type": "talk_topic", diff --git a/data/json/overmap/overmap_special/specials.json b/data/json/overmap/overmap_special/specials.json index 9cd9748adb16a..f07d44f2d757f 100644 --- a/data/json/overmap/overmap_special/specials.json +++ b/data/json/overmap/overmap_special/specials.json @@ -4362,7 +4362,7 @@ "locations": [ "wilderness" ], "city_distance": [ 3, -1 ], "city_sizes": [ 1, 16 ], - "occurrences": [ 1, 1 ], + "occurrences": [ 33, 100 ], "rotate": false, "flags": [ "UNIQUE" ] }, From 33568641546ddc071d7034071a7115f743f77749 Mon Sep 17 00:00:00 2001 From: Zhilkin Serg Date: Tue, 5 Jan 2021 10:06:56 +0300 Subject: [PATCH 30/43] Rename monster_attack.cpp to monster_attack_test.cpp --- tests/{monster_attack.cpp => monster_attack_test.cpp} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename tests/{monster_attack.cpp => monster_attack_test.cpp} (100%) diff --git a/tests/monster_attack.cpp b/tests/monster_attack_test.cpp similarity index 100% rename from tests/monster_attack.cpp rename to tests/monster_attack_test.cpp From 9f7d804dbf46a2b1770cc715545a369f9ab9d665 Mon Sep 17 00:00:00 2001 From: Beneficial-Insurance <57273656+Beneficial-Insurance@users.noreply.github.com> Date: Tue, 5 Jan 2021 20:20:58 +1300 Subject: [PATCH 31/43] Add more Military Professions, add realistic skills to Military Professions (#45359) --- data/json/professions.json | 254 ++++++++++++++++++++++++++++++++++--- data/json/scenarios.json | 32 ++++- 2 files changed, 266 insertions(+), 20 deletions(-) diff --git a/data/json/professions.json b/data/json/professions.json index 5b9484cd01e3f..50eb85103ec80 100644 --- a/data/json/professions.json +++ b/data/json/professions.json @@ -733,7 +733,34 @@ }, { "type": "profession", - "id": "soldier", + "id": "major-general", + "name": "Major General", + "description": "You worked your way up through the ranks, from a no-name private, to a big shot Major General. Now however, it is years since you last fired a weapon in anger, and you barely escaped your aides.", + "points": 4, + "proficiencies": [ "prof_gunsmithing_basic" ], + "skills": [ + { "level": 1, "name": "gun" }, + { "level": 1, "name": "rifle" }, + { "level": 1, "name": "pistol" }, + { "level": 1, "name": "melee" }, + { "level": 6, "name": "speech" } + ], + "items": { + "both": { + "items": [ "pants_army", "undershirt", "jacket_army", "gloves_liner", "socks", "boots", "beret", "wristwatch", "binoculars" ], + "entries": [ + { "item": "m17", "ammo-item": "9mm", "container-item": "holster", "charges": 17 }, + { "item": "p320mag_17rd_9x19mm", "ammo-item": "9mm", "charges": 17, "count": 2 }, + { "item": "militarymap" } + ] + }, + "male": [ "boxer_shorts" ], + "female": [ "sports_bra", "boxer_shorts" ] + } + }, + { + "type": "profession", + "id": "recruit", "name": "Military Recruit", "description": "Joining the military has been your dream for years. You finally got in, just in time for your training to get interrupted by some sort of national emergency. As far as you can tell, military command abandoned you in this hellhole when you missed the emergency evac.", "points": 4, @@ -743,7 +770,194 @@ { "level": 1, "name": "rifle" }, { "level": 2, "name": "melee" }, { "level": 1, "name": "stabbing" }, - { "level": 1, "name": "dodge" } + { "level": 1, "name": "dodge" }, + { "level": 1, "name": "firstaid" }, + { "level": 1, "name": "survival" } + ], + "items": { + "both": { + "items": [ + "pants_army", + "undershirt", + "jacket_army", + "ballistic_vest_esapi", + "helmet_army", + "mask_ski", + "gloves_liner", + "gloves_tactical", + "socks", + "boots_combat", + "wristwatch", + "molle_pack" + ], + "entries": [ + { "group": "charged_two_way_radio" }, + { "item": "legpouch_large", "contents-group": "army_mags_m4" }, + { "item": "ear_plugs", "custom-flags": [ "no_auto_equip" ] }, + { "item": "knife_combat", "container-item": "sheath" }, + { "item": "m4a1", "ammo-item": "556", "charges": 30, "contents-item": [ "shoulder_strap", "holo_sight" ] } + ] + }, + "male": [ "boxer_shorts" ], + "female": [ "sports_bra", "boxer_shorts" ] + } + }, + { + "type": "profession", + "id": "combat-mechanic", + "name": "Combat Mechanic", + "description": "You failed out of high school, and joined the army. You were soon hand picked for extra training in the mechanics trade, keeping the armour running. It's been years since you last touched a rifle, and the dead men are marching again…", + "points": 4, + "proficiencies": [ "prof_gunsmithing_basic" ], + "skills": [ + { "level": 2, "name": "gun" }, + { "level": 1, "name": "rifle" }, + { "level": 2, "name": "melee" }, + { "level": 1, "name": "stabbing" }, + { "level": 1, "name": "dodge" }, + { "level": 1, "name": "firstaid" }, + { "level": 1, "name": "survival" }, + { "level": 6, "name": "mechanics" } + ], + "items": { + "both": { + "items": [ + "pants_army", + "undershirt", + "jacket_army", + "ballistic_vest_esapi", + "helmet_army", + "mask_ski", + "gloves_liner", + "gloves_tactical", + "socks", + "boots_combat", + "wristwatch", + "molle_pack" + ], + "entries": [ + { "item": "wrench" }, + { "item": "hacksaw" }, + { "item": "pliers" }, + { "item": "goggles_welding", "custom-flags": [ "no_auto_equip" ] }, + { "item": "jack" } + ] + }, + "male": [ "boxer_shorts" ], + "female": [ "sports_bra", "boxer_shorts" ] + } + }, + { + "type": "profession", + "id": "combat-engineer", + "name": "Combat Engineer", + "description": "Your job was simple. Keep the army moving. You built bridges, you built roads, you destroyed fortifications, and you cleared mines. It has been years since you last handled a rifle in basic training, now might be the time to dust off those skills.", + "points": 6, + "proficiencies": [ "prof_gunsmithing_basic" ], + "skills": [ + { "level": 2, "name": "gun" }, + { "level": 1, "name": "rifle" }, + { "level": 2, "name": "melee" }, + { "level": 1, "name": "stabbing" }, + { "level": 1, "name": "dodge" }, + { "level": 1, "name": "firstaid" }, + { "level": 1, "name": "survival" }, + { "level": 6, "name": "fabrication" }, + { "level": 6, "name": "mechanics" }, + { "level": 6, "name": "traps" } + ], + "items": { + "both": { + "items": [ + "pants_army", + "undershirt", + "jacket_army", + "ballistic_vest_esapi", + "helmet_army", + "mask_ski", + "gloves_liner", + "gloves_tactical", + "socks", + "boots_combat", + "wristwatch", + "molle_pack" + ], + "entries": [ + { "group": "charged_two_way_radio" }, + { "item": "ear_plugs", "custom-flags": [ "no_auto_equip" ] }, + { "item": "c4", "count": 3 } + ] + }, + "male": [ "boxer_shorts" ], + "female": [ "sports_bra", "boxer_shorts" ] + } + }, + { + "type": "profession", + "id": "nco", + "name": "Non Comissioned Officer", + "description": "You're a veteran of several peace keeping missions. You lead your squad as a sort of parental figure, offering helpful advice on how not to die. Now your squad are shambling and want to eat your brains.", + "points": 7, + "proficiencies": [ "prof_gunsmithing_basic" ], + "skills": [ + { "level": 3, "name": "gun" }, + { "level": 2, "name": "speech" }, + { "level": 3, "name": "shotgun" }, + { "level": 3, "name": "rifle" }, + { "level": 2, "name": "melee" }, + { "level": 2, "name": "stabbing" }, + { "level": 2, "name": "dodge" }, + { "level": 3, "name": "firstaid" }, + { "level": 3, "name": "survival" } + ], + "items": { + "both": { + "items": [ + "pants_army", + "undershirt", + "jacket_army", + "ballistic_vest_esapi", + "helmet_army", + "mask_ski", + "gloves_liner", + "gloves_tactical", + "socks", + "boots_combat", + "wristwatch", + "molle_pack" + ], + "entries": [ + { "group": "charged_two_way_radio" }, + { "item": "ear_plugs", "custom-flags": [ "no_auto_equip" ] }, + { "item": "knife_combat", "container-item": "sheath" }, + { "item": "shot_00", "count": 2 }, + { + "item": "m1014", + "ammo-item": "shot_00", + "charges": 8, + "contents-item": [ "shoulder_strap", "holo_sight" ] + } + ] + }, + "male": [ "boxer_shorts" ], + "female": [ "sports_bra", "boxer_shorts" ] + } + }, + { + "type": "profession", + "id": "rifleman", + "name": "Rifleman", + "description": "When you were young, you dreamed of being a soldier. War is hell and hell is war, but you never thought that it would happen like this.", + "points": 6, + "proficiencies": [ "prof_gunsmithing_basic" ], + "skills": [ + { "level": 3, "name": "gun" }, + { "level": 3, "name": "rifle" }, + { "level": 2, "name": "melee" }, + { "level": 2, "name": "stabbing" }, + { "level": 2, "name": "dodge" }, + { "level": 2, "name": "survival" }, + { "level": 2, "name": "firstaid" } ], "items": { "both": { @@ -778,18 +992,19 @@ "id": "specops", "name": "Special Operator", "description": "You were the best of the best, the military's finest. That's why you're still alive, even after all your comrades fell to the undead. As far as you can tell, military command abandoned you in this hellhole when you missed the emergency evac.", - "points": 5, + "points": 8, "proficiencies": [ "prof_gunsmithing_basic", "prof_traps", "prof_disarming" ], "skills": [ - { "level": 3, "name": "gun" }, - { "level": 3, "name": "smg" }, - { "level": 2, "name": "cutting" }, - { "level": 2, "name": "melee" }, - { "level": 2, "name": "dodge" }, - { "level": 2, "name": "traps" }, - { "level": 1, "name": "firstaid" }, - { "level": 1, "name": "swimming" }, - { "level": 1, "name": "survival" } + { "level": 6, "name": "gun" }, + { "level": 6, "name": "smg" }, + { "level": 6, "name": "rifle" }, + { "level": 4, "name": "cutting" }, + { "level": 4, "name": "melee" }, + { "level": 3, "name": "dodge" }, + { "level": 3, "name": "traps" }, + { "level": 3, "name": "firstaid" }, + { "level": 3, "name": "swimming" }, + { "level": 4, "name": "survival" } ], "items": { "both": { @@ -2475,7 +2690,7 @@ "name": "National Guard", "description": "The government activated your National Guard unit to deal with the growing epidemics. Despite your best efforts, you were unable to form up before all communications ceased and you found yourself alone amongst the dead.", "points": 3, - "skills": [ { "level": 1, "name": "gun" }, { "level": 1, "name": "firstaid" } ], + "skills": [ { "level": 2, "name": "gun" }, { "level": 2, "name": "firstaid" } ], "items": { "both": { "items": [ "shorts", "tshirt", "socks", "lowtops", "wristwatch" ], @@ -3651,11 +3866,14 @@ "description": "In response to the outbreak, you were dispatched to contain the infection by burning the corpses. With command consumed by the undead, your priorities have shifted to basic survival.", "points": 6, "skills": [ - { "level": 2, "name": "gun" }, - { "level": 1, "name": "launcher" }, - { "level": 1, "name": "melee" }, - { "level": 1, "name": "pistol" }, - { "level": 1, "name": "stabbing" } + { "level": 3, "name": "gun" }, + { "level": 3, "name": "rifle" }, + { "level": 3, "name": "launcher" }, + { "level": 2, "name": "melee" }, + { "level": 2, "name": "pistol" }, + { "level": 2, "name": "survival" }, + { "level": 2, "name": "firstaid" }, + { "level": 2, "name": "stabbing" } ], "items": { "both": { diff --git a/data/json/scenarios.json b/data/json/scenarios.json index 8a2ae6fb088b7..f9ff7f41b3e9b 100644 --- a/data/json/scenarios.json +++ b/data/json/scenarios.json @@ -510,7 +510,20 @@ "description": "While being transported to a different military base, the pilot lost control of the helicopter and crashed in the middle of nowhere. Hopefully some of the soldiers that were with you also survived the accident.", "start_name": "Crash site", "allowed_locs": [ "sloc_field", "sloc_forest" ], - "professions": [ "soldier", "specops", "national_guard", "politician", "mili_pilot", "mili_medic", "mili_burner" ], + "professions": [ + "rifleman", + "recruit", + "specops", + "national_guard", + "politician", + "mili_pilot", + "mili_medic", + "mili_burner", + "major-general", + "nco", + "combat-mechanic", + "combat-engineer" + ], "map_extra": "mx_helicopter", "flags": [ "HELI_CRASH", "LONE_START" ] }, @@ -533,7 +546,22 @@ "description": "Despite all the soldiers, guns and minefields, the base you were on got overrun by the dead. Everyone was ordered to fall back to the armory, but during all the chaos you got lost and you are now stuck in the warehouse all alone. You are not sure if anyone made it to the armory, or if you are the last man alive.", "start_name": "Military Base Warehouse", "allowed_locs": [ "sloc_military_base_warehouse" ], - "professions": [ "unemployed", "soldier", "specops", "labtech", "medic", "mili_pilot", "mili_medic", "mili_burner", "hazmat_unit" ], + "professions": [ + "unemployed", + "recruit", + "rifleman", + "specops", + "labtech", + "medic", + "mili_pilot", + "mili_medic", + "mili_burner", + "hazmat_unit", + "major-general", + "nco", + "combat-mechanic", + "combat-engineer" + ], "flags": [ "CHALLENGE", "LONE_START" ] }, { From 85b5249d291ee2e6503ca3fb8f6f7226b7b3ce0a Mon Sep 17 00:00:00 2001 From: Anton Burmistrov Date: Tue, 5 Jan 2021 11:26:50 +0400 Subject: [PATCH 32/43] Warn player about "X is dangerously close" only if X is able to reach to the player or if X has some sort of ranged attack (#45752) --- data/json/monsters/insect_spider.json | 2 +- data/json/monsters/zed_acid.json | 12 +++++++---- data/json/monsters/zed_electric.json | 6 ++++-- data/json/monsters/zed_fusion.json | 2 +- data/json/monsters/zed_misc.json | 26 +++++++++++++++++++----- doc/JSON_FLAGS.md | 1 + src/creature.cpp | 16 +++++++++++++++ src/creature.h | 5 +++++ src/game.cpp | 29 ++++++++++++++++++++------- src/game.h | 10 +++++++-- src/monstergenerator.cpp | 1 + src/mtype.h | 9 +++++---- src/pathfinding.h | 2 ++ 13 files changed, 95 insertions(+), 26 deletions(-) diff --git a/data/json/monsters/insect_spider.json b/data/json/monsters/insect_spider.json index cae9a557f7f7f..1cef343228bb8 100644 --- a/data/json/monsters/insect_spider.json +++ b/data/json/monsters/insect_spider.json @@ -1698,7 +1698,7 @@ "weight": "815 kg", "melee_dice": 2, "special_attacks": [ [ "SMASH", 30 ], [ "BIO_OP_TAKEDOWN", 20 ], [ "RANGED_PULL", 20 ], [ "GRAB", 5 ] ], - "extend": { "flags": [ "DESTROYS", "PUSH_MON", "PUSH_VEH" ] } + "extend": { "flags": [ "DESTROYS", "PUSH_MON", "PUSH_VEH", "RANGED_ATTACKER" ] } }, { "id": "mon_locust_small", diff --git a/data/json/monsters/zed_acid.json b/data/json/monsters/zed_acid.json index 573166df01112..a7c46a911faf7 100644 --- a/data/json/monsters/zed_acid.json +++ b/data/json/monsters/zed_acid.json @@ -43,7 +43,8 @@ "NO_BREATHE", "REVIVES", "PUSH_MON", - "FILTHY" + "FILTHY", + "RANGED_ATTACKER" ] }, { @@ -155,7 +156,8 @@ "NO_BREATHE", "REVIVES", "PUSH_MON", - "FILTHY" + "FILTHY", + "RANGED_ATTACKER" ] }, { @@ -203,7 +205,8 @@ "NO_BREATHE", "REVIVES", "PUSH_MON", - "FILTHY" + "FILTHY", + "RANGED_ATTACKER" ] }, { @@ -248,7 +251,8 @@ "REVIVES", "FILTHY", "ACIDPROOF", - "ACID_BLOOD" + "ACID_BLOOD", + "RANGED_ATTACKER" ] }, { diff --git a/data/json/monsters/zed_electric.json b/data/json/monsters/zed_electric.json index bad92c7ab432d..cd0af42c05415 100644 --- a/data/json/monsters/zed_electric.json +++ b/data/json/monsters/zed_electric.json @@ -44,7 +44,8 @@ "ELECTRIC", "NO_BREATHE", "REVIVES", - "FILTHY" + "FILTHY", + "RANGED_ATTACKER" ] }, { @@ -91,7 +92,8 @@ "NO_BREATHE", "REVIVES", "PUSH_MON", - "FILTHY" + "FILTHY", + "RANGED_ATTACKER" ] }, { diff --git a/data/json/monsters/zed_fusion.json b/data/json/monsters/zed_fusion.json index 61bb333f05c85..65ce920c03cea 100644 --- a/data/json/monsters/zed_fusion.json +++ b/data/json/monsters/zed_fusion.json @@ -270,7 +270,7 @@ "harvest": "exempt", "special_attacks": [ [ "RANGED_PULL", 20 ], [ "SMASH", 20 ] ], "death_function": [ "GAS" ], - "flags": [ "SEES", "HEARS", "SMELLS", "WARM", "POISON", "CLIMBS", "NO_BREATHE", "IMMOBILE" ] + "flags": [ "SEES", "HEARS", "SMELLS", "WARM", "POISON", "CLIMBS", "NO_BREATHE", "IMMOBILE", "RANGED_ATTACKER" ] }, { "id": "mon_zombie_giant_heart", diff --git a/data/json/monsters/zed_misc.json b/data/json/monsters/zed_misc.json index cc7eb362c957c..09f060579673d 100644 --- a/data/json/monsters/zed_misc.json +++ b/data/json/monsters/zed_misc.json @@ -53,7 +53,8 @@ "NO_BREATHE", "REVIVES", "PUSH_MON", - "FILTHY" + "FILTHY", + "RANGED_ATTACKER" ] }, { @@ -283,7 +284,8 @@ "REVIVES", "PUSH_MON", "PUSH_VEH", - "FILTHY" + "FILTHY", + "RANGED_ATTACKER" ] }, { @@ -470,7 +472,8 @@ "NO_BREATHE", "REVIVES", "PUSH_MON", - "FILTHY" + "FILTHY", + "RANGED_ATTACKER" ] }, { @@ -1170,7 +1173,8 @@ "REVIVES", "PUSH_MON", "PUSH_VEH", - "FILTHY" + "FILTHY", + "RANGED_ATTACKER" ] }, { @@ -1250,7 +1254,19 @@ "special_attacks": [ [ "PULL_METAL_WEAPON", 25 ], { "type": "bite", "cooldown": 20 } ], "death_drops": "mon_zombie_technician_death_drops", "death_function": [ "NORMAL" ], - "flags": [ "SEES", "HEARS", "SMELLS", "WARM", "BASHES", "GROUP_BASH", "POISON", "NO_BREATHE", "REVIVES", "FILTHY" ] + "flags": [ + "SEES", + "HEARS", + "SMELLS", + "WARM", + "BASHES", + "GROUP_BASH", + "POISON", + "NO_BREATHE", + "REVIVES", + "FILTHY", + "RANGED_ATTACKER" + ] }, { "id": "mon_zombie_thorny", diff --git a/doc/JSON_FLAGS.md b/doc/JSON_FLAGS.md index 23cceef737724..a1fc3aa9b05f1 100644 --- a/doc/JSON_FLAGS.md +++ b/doc/JSON_FLAGS.md @@ -982,6 +982,7 @@ Other monster flags. - ```POISON``` Poisonous to eat. - ```PUSH_MON``` Can push creatures out of its way. - ```QUEEN``` When it dies, local populations start to die off too. +- ```RANGED_ATTACKER``` Monster has any sort of ranged attack. - ```REVIVES``` Monster corpse will revive after a short period of time. - ```RIDEABLE_MECH``` This monster is a mech suit that can be piloted. - ```SEES``` It can see you (and will run/follow). diff --git a/src/creature.cpp b/src/creature.cpp index 476b58a907661..f396be79842c8 100644 --- a/src/creature.cpp +++ b/src/creature.cpp @@ -181,6 +181,22 @@ bool Creature::is_underwater() const return underwater; } +bool Creature::is_ranged_attacker() const +{ + if( has_flag( MF_RANGED_ATTACKER ) ) { + return true; + } + + for( const std::pair &attack : + as_monster()->type->special_attacks ) { + if( attack.second->id == "gun" ) { + return true; + } + } + + return false; +} + bool Creature::digging() const { return false; diff --git a/src/creature.h b/src/creature.h index 4887bd92a90b2..8fd24d755dc89 100644 --- a/src/creature.h +++ b/src/creature.h @@ -449,6 +449,11 @@ class Creature : public location, public viewer virtual void on_hit( Creature *source, bodypart_id bp_hit, float difficulty = INT_MIN, dealt_projectile_attack const *proj = nullptr ) = 0; + /** Returns true if this monster has any sort of ranged attack. This doesn't necessarily mean direct damage ranged attack, + * but also includes any sort of potentially dangerous ranged interaction, e.g. monster with RANGED_PULL special attack will fit here too. + */ + virtual bool is_ranged_attacker() const; + virtual bool digging() const; virtual bool is_on_ground() const = 0; virtual bool is_underwater() const; diff --git a/src/game.cpp b/src/game.cpp index 45f288abdfe9d..2ab5dc7396a22 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -1533,12 +1533,14 @@ bool game::do_turn() mon_info_update(); - // If player is performing a task and a monster is dangerously close, warn them - // regardless of previous safemode warnings + // If player is performing a task, a monster is dangerously close, + // and monster can reach to the player or it has some sort of a ranged attack, + // warn them regardless of previous safemode warnings if( u.activity && !u.has_activity( activity_id( "ACT_AIM" ) ) && u.activity.moves_left > 0 && !u.activity.is_distraction_ignored( distraction_type::hostile_spotted_near ) ) { - Creature *hostile_critter = is_hostile_very_close(); + Creature *hostile_critter = is_hostile_very_close( true ); + if( hostile_critter != nullptr ) { cancel_activity_or_ignore_query( distraction_type::hostile_spotted_near, string_format( _( "The %s is dangerously close!" ), @@ -4061,15 +4063,28 @@ Creature *game::is_hostile_nearby() return is_hostile_within( distance ); } -Creature *game::is_hostile_very_close() +Creature *game::is_hostile_very_close( bool dangerous ) { - return is_hostile_within( DANGEROUS_PROXIMITY ); + return is_hostile_within( DANGEROUS_PROXIMITY, dangerous ); } -Creature *game::is_hostile_within( int distance ) +Creature *game::is_hostile_within( int distance, bool dangerous ) { for( auto &critter : u.get_visible_creatures( distance ) ) { if( u.attitude_to( *critter ) == Creature::Attitude::HOSTILE ) { + if( dangerous ) { + if( critter->is_ranged_attacker() ) { + return critter; + } + + const pathfinding_settings pf_settings = pathfinding_settings { 8, distance, distance * 2, 4, true, false, true, false, false }; + static const std::set path_avoid = {}; + + if( !get_map().route( u.pos(), critter->pos(), pf_settings, path_avoid ).empty() ) { + return critter; + } + continue; + } return critter; } } @@ -8799,7 +8814,7 @@ void game::butcher() return; } - Creature *hostile_critter = is_hostile_very_close(); + Creature *hostile_critter = is_hostile_very_close( true ); if( hostile_critter != nullptr ) { if( !query_yn( _( "You see %s nearby! Start butchering anyway?" ), hostile_critter->disp_name() ) ) { diff --git a/src/game.h b/src/game.h index 92ad7b1f9a94f..f352d83502107 100644 --- a/src/game.h +++ b/src/game.h @@ -552,7 +552,7 @@ class game void reset_light_level(); character_id assign_npc_id(); Creature *is_hostile_nearby(); - Creature *is_hostile_very_close(); + Creature *is_hostile_very_close( bool dangerous = false ); // Handles shifting coordinates transparently when moving between submaps. // Helper to make calling with a player pointer less verbose. point update_map( Character &p ); @@ -940,7 +940,13 @@ class game cata::optional start_time = cata::nullopt; } debug_hour_timer; - Creature *is_hostile_within( int distance ); + /** + * Checks if there's a hostile creature within given distance. + * @param dangerous If true, makes additional checks for monsters with ranged attack capabilities within distance OR + * if there's a route from monster to player, and returns this particular monster. + * @return Hostile creature within given distance. + */ + Creature *is_hostile_within( int distance, bool dangerous = false ); void move_save_to_graveyard(); bool save_player_data(); diff --git a/src/monstergenerator.cpp b/src/monstergenerator.cpp index b602473f972bb..98d20f0067879 100644 --- a/src/monstergenerator.cpp +++ b/src/monstergenerator.cpp @@ -182,6 +182,7 @@ std::string enum_to_string( m_flag data ) case MF_LOUDMOVES: return "LOUDMOVES"; case MF_DROPS_AMMO: return "DROPS_AMMO"; case MF_INSECTICIDEPROOF: return "INSECTICIDEPROOF"; + case MF_RANGED_ATTACKER: return "RANGED_ATTACKER"; // *INDENT-ON* case m_flag::MF_MAX: break; diff --git a/src/mtype.h b/src/mtype.h index 0752407d46e37..7d422f15cb2df 100644 --- a/src/mtype.h +++ b/src/mtype.h @@ -69,7 +69,7 @@ enum m_flag : int { MF_HEARS, // It can hear you MF_GOODHEARING, // Pursues sounds more than most monsters MF_SMELLS, // It can smell you - MF_KEENNOSE, //Keen sense of smell + MF_KEENNOSE, // Keen sense of smell MF_STUMBLES, // Stumbles in its movement MF_WARM, // Warm blooded MF_NOHEAD, // Headshots not allowed! @@ -95,7 +95,7 @@ enum m_flag : int { MF_ELECTRIC, // Shocks unarmed attackers MF_ACIDPROOF, // Immune to acid MF_ACIDTRAIL, // Leaves a trail of acid - MF_SHORTACIDTRAIL, // Leaves an intermittent trail of acid + MF_SHORTACIDTRAIL, // Leaves an intermittent trail of acid MF_FIREPROOF, // Immune to fire MF_SLUDGEPROOF, // Ignores the effect of sludge trails MF_SLUDGETRAIL, // Causes monster to leave a sludge trap trail when moving @@ -110,9 +110,9 @@ enum m_flag : int { MF_FAT, // May produce fat when butchered; if combined with POISON flag, tainted fat MF_CONSOLE_DESPAWN, // Despawns when a nearby console is properly hacked MF_IMMOBILE, // Doesn't move (e.g. turrets) - MF_ID_CARD_DESPAWN, // Despawns when a science ID card is used on a nearby console + MF_ID_CARD_DESPAWN, // Despawns when a science ID card is used on a nearby console MF_RIDEABLE_MECH, // A rideable mech that is immobile until ridden. - MF_MILITARY_MECH, // A rideable mech that was designed for military work. + MF_MILITARY_MECH, // A rideable mech that was designed for military work. MF_MECH_RECON_VISION, // This mech gives you IR night-vision. MF_MECH_DEFENSIVE, // This mech gives you thorough protection. MF_HIT_AND_RUN, // Flee for several turns after a melee attack @@ -175,6 +175,7 @@ enum m_flag : int { MF_STUN_IMMUNE, // This monster is immune to the stun effect MF_DROPS_AMMO, // This monster drops ammo. Should not be set for monsters that use pseudo ammo. MF_INSECTICIDEPROOF, // This monster is immune to insecticide, even though it's made of bug flesh + MF_RANGED_ATTACKER, // This monster has any sort of ranged attack MF_MAX // Sets the length of the flags - obviously must be LAST }; diff --git a/src/pathfinding.h b/src/pathfinding.h index c868fa44dabad..467dad0253dc5 100644 --- a/src/pathfinding.h +++ b/src/pathfinding.h @@ -70,6 +70,8 @@ struct pathfinding_settings { : bash_strength( bs ), max_dist( md ), max_length( ml ), climb_cost( cc ), allow_open_doors( aod ), avoid_traps( at ), allow_climb_stairs( acs ), avoid_rough_terrain( art ), avoid_sharp( as ) {} + + pathfinding_settings &operator=( const pathfinding_settings & ) = default; }; #endif // CATA_SRC_PATHFINDING_H From 1010032b7b24f1ece9567fd53c50550faf1011eb Mon Sep 17 00:00:00 2001 From: Eric <52087122+Ramza13@users.noreply.github.com> Date: Tue, 5 Jan 2021 05:57:42 -0500 Subject: [PATCH 33/43] NPC's are friends with their factions monster faction (#46554) --- src/monster.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/monster.cpp b/src/monster.cpp index 7189d7b6ad547..be272f1caa909 100644 --- a/src/monster.cpp +++ b/src/monster.cpp @@ -1110,6 +1110,10 @@ monster_attitude monster::attitude( const Character *u ) const effective_anger -= 10; } } + auto *u_fac = u->get_faction(); + if( u_fac && faction == u_fac->mon_faction ) { + return MATT_FRIEND; + } if( type->in_species( species_FUNGUS ) && ( u->has_trait( trait_THRESH_MYCUS ) || u->has_trait( trait_MYCUS_FRIEND ) ) ) { From fca526d9ce6cd77af71355900d8f501b0cf4ba35 Mon Sep 17 00:00:00 2001 From: Binrui Dong Date: Tue, 5 Jan 2021 06:43:37 -0500 Subject: [PATCH 34/43] Ignore book templates in translation --- lang/extract_json_strings.py | 1 + 1 file changed, 1 insertion(+) diff --git a/lang/extract_json_strings.py b/lang/extract_json_strings.py index 62c56584e2fc1..c84947de2964a 100755 --- a/lang/extract_json_strings.py +++ b/lang/extract_json_strings.py @@ -65,6 +65,7 @@ def warning_supressed(filename): # these files will not be parsed. Full related path. ignore_files = {os.path.normpath(i) for i in { "data/json/anatomy.json", + "data/json/items/book/abstract.json", "data/mods/replacements.json", "data/raw/color_templates/no_bright_background.json" }} From 117997174bdf354f43f0c55ecd75352068c58d11 Mon Sep 17 00:00:00 2001 From: Binrui Dong Date: Tue, 5 Jan 2021 06:43:58 -0500 Subject: [PATCH 35/43] Fix plural names of various 40x46mm ammo --- data/json/items/ammo/40x46mm.json | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/data/json/items/ammo/40x46mm.json b/data/json/items/ammo/40x46mm.json index c7cb7860ab5b1..ee9222bab7524 100644 --- a/data/json/items/ammo/40x46mm.json +++ b/data/json/items/ammo/40x46mm.json @@ -58,7 +58,7 @@ "id": "40x46mm_m651", "copy-from": "40x46mm_grenade", "type": "AMMO", - "name": { "str": "40x46mm M651 tear gas" }, + "name": { "str": "40x46mm M651 tear gas", "str_pl": "40x46mm M651 tear gases" }, "description": "A low velocity 40mm tear gas canister. It is effective for riot control and driving infantry from entrenched positions, and is able to penetrate thin wood.", "weight": "290 g", "damage": { "damage_type": "bullet", "amount": 24 }, @@ -70,7 +70,7 @@ "id": "40x46mm_buckshot_m118", "copy-from": "40x46mm_buckshot_m199", "type": "AMMO", - "name": { "str": "40x46mm-M118 buckshot, reloaded" }, + "name": { "str": "40x46mm-M118 buckshot, reloaded", "str_pl": "40x46mm-M118 buckshots, reloaded" }, "description": "An improvised 40x46mm buckshot load somewhat resembling M576, loaded into a M118 canister. Due to the limitations of weapons built to fire 40x46mm grenades, it's much less powerful than most people would expect.", "casing": "40x46mm_m118_casing" }, @@ -78,7 +78,7 @@ "id": "40x46mm_buckshot_m199", "copy-from": "40x46mm_m576", "type": "AMMO", - "name": { "str": "40x46mm-M199 buckshot, reloaded" }, + "name": { "str": "40x46mm-M199 buckshot, reloaded", "str_pl": "40x46mm-M199 buckshots, reloaded" }, "description": "An improvised 40x46mm buckshot load somewhat resembling M576, loaded into a M199 canister. Due to the limitations of weapons built to fire 40x46mm grenades, it's much less powerful than most people would expect.", "proportional": { "price": 0.7, "damage": { "damage_type": "bullet", "amount": 0.9 }, "dispersion": 1.1 }, "delete": { "flags": [ "IRREPLACEABLE_CONSUMABLE" ] } @@ -87,7 +87,7 @@ "id": "40x46mm_slug_m118", "copy-from": "40x46mm_grenade", "type": "AMMO", - "name": { "str": "40x46mm-M118 slug, reloaded" }, + "name": { "str": "40x46mm-M118 slug, reloaded", "str_pl": "40x46mm-M118 slugs, reloaded" }, "description": "An improvised 40x46mm load resembling a shotgun slug, loaded into a M118 canister. Due to the limitations of weapons built to fire 40x46mm grenades, it's much less powerful than most people would expect.", "weight": "120 g", "damage": { "damage_type": "bullet", "amount": 25, "armor_penetration": 3 }, @@ -98,7 +98,7 @@ "id": "40x46mm_slug_m199", "copy-from": "40x46mm_slug_m118", "type": "AMMO", - "name": { "str": "40x46mm-M199 slug, reloaded" }, + "name": { "str": "40x46mm-M199 slug, reloaded", "str_pl": "40x46mm-M199 slugs, reloaded" }, "description": "An improvised 40x46mm load resembling a shotgun slug, loaded into a M199 canister. Due to the limitations of weapons built to fire 40x46mm grenades, it's much less powerful than most people would expect.", "casing": "40x46mm_m199_casing" }, @@ -106,7 +106,7 @@ "id": "40x46mm_flechette_m118", "copy-from": "40x46mm_buckshot_m118", "type": "AMMO", - "name": { "str": "40x46mm-M118 flechette, reloaded" }, + "name": { "str": "40x46mm-M118 flechette, reloaded", "str_pl": "40x46mm-M118 flechettes, reloaded" }, "description": "An improvised 40x46mm flechette load containing 10 steel darts, loaded into a M118 canister. Due to the limitations of weapons built to fire 40x46mm grenades, it's much less powerful than most people would expect.", "damage": { "damage_type": "bullet", "amount": 22, "armor_penetration": 6 }, "delete": { "effects": [ "NOGIB" ] } @@ -115,7 +115,7 @@ "id": "40x46mm_flechette_m199", "copy-from": "40x46mm_flechette_m118", "type": "AMMO", - "name": { "str": "40x46mm-M199 flechette, reloaded" }, + "name": { "str": "40x46mm-M199 flechette, reloaded", "str_pl": "40x46mm-M199 flechettes, reloaded" }, "description": "An improvised 40x46mm flechette load containing 10 steel darts, loaded into a M199 canister. Due to the limitations of weapons built to fire 40x46mm grenades, it's much less powerful than most people would expect.", "casing": "40x46mm_m199_casing" }, @@ -123,7 +123,7 @@ "id": "bp_40x46mm_buckshot_m118", "copy-from": "bp_40x46mm_buckshot_m199", "type": "AMMO", - "name": { "str": "40x46mm-M118 buckshot, black powder" }, + "name": { "str": "40x46mm-M118 buckshot, black powder", "str_pl": "40x46mm-M118 buckshots, black powder" }, "description": "An improvised 40x46mm buckshot load somewhat resembling M576, loaded into a M118 canister. Due to the limitations of weapons built to fire 40x46mm grenades, it's much less powerful than most people would expect.", "casing": "40x46mm_m118_casing" }, @@ -131,7 +131,7 @@ "id": "bp_40x46mm_buckshot_m199", "copy-from": "40x46mm_m576", "type": "AMMO", - "name": { "str": "40x46mm-M199 buckshot, black powder" }, + "name": { "str": "40x46mm-M199 buckshot, black powder", "str_pl": "40x46mm-M199 buckshots, black powder" }, "description": "An improvised 40x46mm buckshot load somewhat resembling M576, loaded into a M199 canister. Due to the limitations of weapons built to fire 40x46mm grenades, it's much less powerful than most people would expect.", "proportional": { "price": 0.3, @@ -146,7 +146,7 @@ "id": "bp_40x46mm_slug_m118", "copy-from": "40x46mm_grenade", "type": "AMMO", - "name": { "str": "40x46mm-M118 slug, black powder" }, + "name": { "str": "40x46mm-M118 slug, black powder", "str_pl": "40x46mm-M118 slugs, black powder" }, "description": "An improvised 40x46mm load resembling a shotgun slug, loaded into a M118 canister. Due to the limitations of weapons built to fire 40x46mm grenades, it's much less powerful than most people would expect.", "damage": { "damage_type": "bullet", "amount": 21, "armor_penetration": 1 }, "weight": "120 g", @@ -159,7 +159,7 @@ "id": "bp_40x46mm_slug_m199", "copy-from": "bp_40x46mm_slug_m118", "type": "AMMO", - "name": { "str": "40x46mm-M199 slug, black powder" }, + "name": { "str": "40x46mm-M199 slug, black powder", "str_pl": "40x46mm-M199 slugs, black powder" }, "description": "An improvised 40x46mm load resembling an oversized shotgun slug, loaded into a M199 canister. Due to the limitations of weapons built to fire 40x46mm grenades, it's much less powerful than most people would expect.", "casing": "40x46mm_m199_casing" }, @@ -167,7 +167,7 @@ "id": "bp_40x46mm_flechette_m118", "copy-from": "bp_40x46mm_buckshot_m118", "type": "AMMO", - "name": { "str": "40x46mm-M118 flechette, black powder" }, + "name": { "str": "40x46mm-M118 flechette, black powder", "str_pl": "40x46mm-M118 flechettes, black powder" }, "description": "An improvised 40x46mm flechette load containing 10 steel darts, loaded into a M118 canister. Due to the limitations of weapons built to fire 40x46mm grenades, it's much less powerful than most people would expect.", "damage": { "damage_type": "bullet", "amount": 19, "armor_penetration": 3 }, "delete": { "effects": [ "NOGIB" ] } @@ -176,7 +176,7 @@ "id": "bp_40x46mm_flechette_m199", "copy-from": "bp_40x46mm_flechette_m118", "type": "AMMO", - "name": { "str": "40x46mm-M199 flechette, black powder" }, + "name": { "str": "40x46mm-M199 flechette, black powder", "str_pl": "40x46mm-M199 flechettes, black powder" }, "description": "An improvised 40x46mm flechette load containing 10 steel darts, loaded into a M199 canister. Due to the limitations of weapons built to fire 40x46mm grenades, it's much less powerful than most people would expect.", "casing": "40x46mm_m199_casing" } From 979370f69d84b51bcb874da2ae8c1eded1cbcf5f Mon Sep 17 00:00:00 2001 From: Binrui Dong Date: Tue, 5 Jan 2021 06:44:24 -0500 Subject: [PATCH 36/43] Fix plural names in CRT_EXPANSION mod --- data/mods/CRT_EXPANSION/items/crt_ammo.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/data/mods/CRT_EXPANSION/items/crt_ammo.json b/data/mods/CRT_EXPANSION/items/crt_ammo.json index 3da6421e8f960..b207d694a30e0 100644 --- a/data/mods/CRT_EXPANSION/items/crt_ammo.json +++ b/data/mods/CRT_EXPANSION/items/crt_ammo.json @@ -3,7 +3,7 @@ "type": "AMMO", "id": "pellet", "price": 1000, - "name": "lead pellets", + "name": { "str_sp": "lead pellets" }, "symbol": "=", "color": "light_gray", "description": "A round tin of small light grain .177 lead pellets. These are common, tipped field pellets that can deal some light damage but are generally used for plinking.", @@ -23,7 +23,7 @@ "id": "dhp_pellet", "copy-from": "pellet", "type": "AMMO", - "name": "domed HP pellets", + "name": { "str_sp": "domed HP pellets" }, "description": "A stable, heavier grain lead pellet with the purpose of expanding upon hitting a target for maximized damage, the dome shape allows it to pack quite a punch for something so small", "material": [ "steel" ], "symbol": "=", @@ -34,7 +34,7 @@ "id": "hp_pellet", "copy-from": "pellet", "type": "AMMO", - "name": "tipped HP pellets", + "name": { "str_sp": "tipped HP pellets" }, "//": "Based off of the Gamo Redfire pellet, the plastic tip pushes back into the lead pleet to cause a greater deformation and ballooning effect; makes birds explode!", "description": "A medium grain lead pellet tipped with a pointed bit of hard plastic with the purpose of maximum expansion upon hitting a target.", "material": [ "steel" ], @@ -46,7 +46,7 @@ "id": "alloy_pellet", "copy-from": "pellet", "type": "AMMO", - "name": "alloy pellets", + "name": { "str_sp": "alloy pellets" }, "description": "An gimmicky alloy pellet with the purpose of reaching a higher velocity than a normal lead pellet for breaking the sound barrier resulting in an extremely loud crack, not so useful for stealth.", "material": [ "steel" ], "symbol": "=", From 2c8fa94c1c062e9604f7221e1ba8cda67eaf05e7 Mon Sep 17 00:00:00 2001 From: Binrui Dong Date: Tue, 5 Jan 2021 06:44:44 -0500 Subject: [PATCH 37/43] Fix plural names in Aftershock mod --- data/mods/Aftershock/items/tools.json | 4 ++-- data/mods/Aftershock/items/weapons.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/data/mods/Aftershock/items/tools.json b/data/mods/Aftershock/items/tools.json index cddac717caa09..6808f1f2f1fed 100644 --- a/data/mods/Aftershock/items/tools.json +++ b/data/mods/Aftershock/items/tools.json @@ -415,7 +415,7 @@ "id": "diamond_press", "type": "GENERIC", "category": "tools", - "name": { "str": "folded diamond press" }, + "name": { "str": "folded diamond press", "str_pl": "folded diamond presses" }, "description": "Matrioshka Fabritechnics this device that can create pressures beyond those that create diamonds. Once deployed it can be used to craft ultratech marvels that require substances of singular qualities.", "weight": "8464 g", "volume": "11356 ml", @@ -433,7 +433,7 @@ { "id": "pseudo_diamond_press", "type": "TOOL", - "name": { "str": "pseudo diamond press" }, + "name": { "str": "pseudo diamond press", "str_pl": "pseudo diamond presses" }, "description": "This is a crafting_pseudo_item if you have it something is wrong.", "weight": "6464 g", "volume": "11356 ml", diff --git a/data/mods/Aftershock/items/weapons.json b/data/mods/Aftershock/items/weapons.json index 21b807ee613e9..67dc37eb7c182 100644 --- a/data/mods/Aftershock/items/weapons.json +++ b/data/mods/Aftershock/items/weapons.json @@ -139,7 +139,7 @@ { "id": "afs_freeze_gauntlet", "type": "GENERIC", - "name": { "str": "psychrophile handling gloves" }, + "name": { "str_sp": "psychrophile handling gloves" }, "description": "These gloves are designed for handling organisms that live in extremely cold temperatures. They can also be used to freeze your foes to death.", "weight": "3778 g", "volume": "2 L", From 4ad863aad79495816d597a59392ef29063fc7d9f Mon Sep 17 00:00:00 2001 From: Binrui Dong Date: Tue, 5 Jan 2021 07:58:28 -0500 Subject: [PATCH 38/43] Updated 'soldier' profession reference in Dark-Skies-Above mod --- .../Dark-Skies-Above/overrides/scenarios.json | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/data/mods/Dark-Skies-Above/overrides/scenarios.json b/data/mods/Dark-Skies-Above/overrides/scenarios.json index 88fd43dc74690..94f4f9d87c676 100644 --- a/data/mods/Dark-Skies-Above/overrides/scenarios.json +++ b/data/mods/Dark-Skies-Above/overrides/scenarios.json @@ -293,6 +293,19 @@ "copy-from": "heli_crash", "name": "Helicopter Crash", "description": "While being evacuated from a hot zone, the pilot lost control of the helicopter and crashed in the middle of nowhere. Hopefully some of the soldiers that were with you also survived the accident.", - "professions": [ "soldier", "specops", "national_guard", "politician", "mili_pilot" ] + "professions": [ + "rifleman", + "recruit", + "specops", + "national_guard", + "politician", + "mili_pilot", + "mili_medic", + "mili_burner", + "major-general", + "nco", + "combat-mechanic", + "combat-engineer" + ] } ] From 1e50fede5903f8fc01106052d5b23200972a55d5 Mon Sep 17 00:00:00 2001 From: Jamuro-g Date: Tue, 5 Jan 2021 15:05:28 +0100 Subject: [PATCH 39/43] rebar_plate: Removed redundant description Vehicle Components with the Armor tag already have a description. --- data/json/vehicleparts/armor.json | 1 - 1 file changed, 1 deletion(-) diff --git a/data/json/vehicleparts/armor.json b/data/json/vehicleparts/armor.json index f7302ab633c69..f18dda1df30b5 100644 --- a/data/json/vehicleparts/armor.json +++ b/data/json/vehicleparts/armor.json @@ -12,7 +12,6 @@ "broken_color": "light_gray", "durability": 150, "folded_volume": "1 L", - "description": "Improvised armor plate. Will partially protect other components on the same frame from damage.", "breaks_into": [ { "item": "rebar", "count": [ 1, 2 ] }, { "item": "scrap", "count": [ 4, 8 ] } ], "requirements": { "install": { "skills": [ [ "mechanics", 2 ] ], "time": "60 m", "using": [ [ "welding_standard", 5 ] ] }, From 01d3c34bb363a0f7817effccc0b36280adee98a4 Mon Sep 17 00:00:00 2001 From: John Candlebury Date: Tue, 5 Jan 2021 08:39:09 -0600 Subject: [PATCH 40/43] Fix invalid faction in mon_advbot_base --- data/mods/Aftershock/mobs/robots.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/mods/Aftershock/mobs/robots.json b/data/mods/Aftershock/mobs/robots.json index b4be4fdbab42e..6ba47c69775dc 100644 --- a/data/mods/Aftershock/mobs/robots.json +++ b/data/mods/Aftershock/mobs/robots.json @@ -691,7 +691,7 @@ "looks_like": "mon_copbot", "name": "advanced robot", "description": "An advanced robot still functioning due to its internal fusion core. This model is armed with a powerful laser-emitter.", - "default_faction": "robot", + "default_faction": "defense_bot", "species": [ "ROBOT" ], "hp": 100, "armor_bash": 8, From 1546a9b04ab3fa4fc56235b2b5bb9f4dbabb9dd4 Mon Sep 17 00:00:00 2001 From: Jamuro-g <76928284+Jamuro-g@users.noreply.github.com> Date: Tue, 5 Jan 2021 15:52:24 +0100 Subject: [PATCH 41/43] color-code Proficiencies that cannot be learned in the recipe screen (#46538) --- src/character.cpp | 5 +++++ src/character.h | 1 + src/recipe.cpp | 17 +++++++++-------- 3 files changed, 15 insertions(+), 8 deletions(-) diff --git a/src/character.cpp b/src/character.cpp index cdb9a3764be78..55da1213e0b14 100644 --- a/src/character.cpp +++ b/src/character.cpp @@ -12591,6 +12591,11 @@ bool Character::has_proficiency( const proficiency_id &prof ) const return _proficiencies->has_learned( prof ); } +bool Character::has_prof_prereqs( const proficiency_id &prof ) const +{ + return _proficiencies->has_prereqs( prof ); +} + void Character::add_proficiency( const proficiency_id &prof, bool ignore_requirements ) { if( ignore_requirements ) { diff --git a/src/character.h b/src/character.h index a94ab9d61967d..c2d8c84944699 100644 --- a/src/character.h +++ b/src/character.h @@ -1807,6 +1807,7 @@ class Character : public Creature, public visitable // --------------- Proficiency Stuff ---------------- bool has_proficiency( const proficiency_id &prof ) const; + bool has_prof_prereqs( const proficiency_id &prof ) const; void add_proficiency( const proficiency_id &prof, bool ignore_requirements = false ); void lose_proficiency( const proficiency_id &prof, bool ignore_requirements = false ); void practice_proficiency( const proficiency_id &prof, const time_duration &amount, diff --git a/src/recipe.cpp b/src/recipe.cpp index 88fb591c3f535..efe48ab133aaa 100644 --- a/src/recipe.cpp +++ b/src/recipe.cpp @@ -609,7 +609,8 @@ struct prof_penalty { }; static std::string profstring( const prof_penalty &prof, - std::string &color ) + std::string &color, + const std::string &name_color = "cyan" ) { std::string mitigated_str; if( prof.mitigated ) { @@ -617,16 +618,16 @@ static std::string profstring( const prof_penalty &prof, } if( prof.time_mult == 1.0f ) { - return string_format( _( "%s (%gx\u00a0failure%s)" ), - prof.id->name(), color, prof.failure_mult, mitigated_str ); + return string_format( _( "%s (%gx\u00a0failure%s)" ), + name_color, prof.id->name(), color, prof.failure_mult, mitigated_str ); } else if( prof.failure_mult == 1.0f ) { - return string_format( _( "%s (%gx\u00a0time%s)" ), - prof.id->name(), color, prof.time_mult, mitigated_str ); + return string_format( _( "%s (%gx\u00a0time%s)" ), + name_color, prof.id->name(), color, prof.time_mult, mitigated_str ); } return string_format( - _( "%s (%gx\u00a0time, %gx\u00a0failure%s)" ), - prof.id->name(), color, prof.time_mult, prof.failure_mult, mitigated_str ); + _( "%s (%gx\u00a0time, %gx\u00a0failure%s)" ), + name_color, prof.id->name(), color, prof.time_mult, prof.failure_mult, mitigated_str ); } std::string recipe::used_proficiencies_string( const Character *c ) const @@ -682,7 +683,7 @@ std::string recipe::missing_proficiencies_string( Character *c ) const std::string color = "yellow"; std::string missing = enumerate_as_string( missing_profs.begin(), missing_profs.end(), [&]( const prof_penalty & prof ) { - return profstring( prof, color ); + return profstring( prof, color, c->has_prof_prereqs( prof.id ) ? "cyan" : "red" ); } ); return missing; From 326e83c72317ab4a81019d3e3bdd34e83ea516dc Mon Sep 17 00:00:00 2001 From: akirashirosawa <38557723+akirashirosawa@users.noreply.github.com> Date: Tue, 5 Jan 2021 18:06:21 +0300 Subject: [PATCH 42/43] power unit pgettext --- src/player_display.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/player_display.cpp b/src/player_display.cpp index 4e38f3d86935f..875adcc9a6bde 100644 --- a/src/player_display.cpp +++ b/src/player_display.cpp @@ -636,13 +636,13 @@ static void draw_bionics_tab( const catacurses::window &w_bionics, std::string power_unit; if( you.get_power_level() < 1_J ) { power_amount = units::to_millijoule( you.get_power_level() ); - power_unit = "mJ"; + power_unit = pgettext( "energy unit: millijoule", "mJ" ); } else if( you.get_power_level() < 1_kJ ) { power_amount = units::to_joule( you.get_power_level() ); - power_unit = "J"; + power_unit = pgettext( "energy unit: joule", "J" ); } else { power_amount = units::to_kilojoule( you.get_power_level() ); - power_unit = "kJ"; + power_unit = pgettext( "energy unit: kilojoule", "kJ" ); } // NOLINTNEXTLINE(cata-use-named-point-constants) trim_and_print( w_bionics, point( 1, 1 ), getmaxx( w_bionics ) - 1, c_white, From 4fc94be0fc52a18d02525c733fe4ab6a2ec09412 Mon Sep 17 00:00:00 2001 From: Binrui Dong Date: Tue, 5 Jan 2021 11:01:02 -0500 Subject: [PATCH 43/43] Fix avatar name overflow in look around menu (#46566) * Fix avatar name overflow in look around menu for real * Corrected calculation of max width in NPC info in look around menu --- src/avatar.cpp | 2 +- src/npc.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/avatar.cpp b/src/avatar.cpp index a058ed617c869..64acda77047f4 100644 --- a/src/avatar.cpp +++ b/src/avatar.cpp @@ -1036,7 +1036,7 @@ nc_color avatar::basic_symbol_color() const int avatar::print_info( const catacurses::window &w, int vStart, int, int column ) const { - return vStart + fold_and_print( w, point( column, vStart ), getmaxx( w ), c_dark_gray, + return vStart + fold_and_print( w, point( column, vStart ), getmaxx( w ) - column - 1, c_dark_gray, _( "You (%s)" ), name ) - 1; } diff --git a/src/npc.cpp b/src/npc.cpp index 0ff293bb877c3..2ebd0cbf9ee0e 100644 --- a/src/npc.cpp +++ b/src/npc.cpp @@ -2297,7 +2297,7 @@ nc_color npc::basic_symbol_color() const int npc::print_info( const catacurses::window &w, int line, int vLines, int column ) const { const int last_line = line + vLines; - const int iWidth = getmaxx( w ) - 2; + const int iWidth = getmaxx( w ) - 1 - column; // First line of w is the border; the next 4 are terrain info, and after that // is a blank line. w is 13 characters tall, and we can't use the last one // because it's a border as well; so we have lines 6 through 11.