From 6ccd790fd25b1c0a78e7d826b0dca84adb7d4514 Mon Sep 17 00:00:00 2001 From: Hirmuolio Date: Sat, 27 Jun 2020 14:02:43 +0300 Subject: [PATCH 001/420] molotov --- data/json/items/tool/explosives.json | 2 +- .../Cataclysm-lib-vcpkg-static.vcxproj | 822 +++++++++++++++--- .../Cataclysm-test-vcpkg-static.vcxproj | 126 ++- msvc-full-features/Cataclysm-vcpkg-static.sln | 2 - .../Cataclysm-vcpkg-static.vcxproj | 3 + msvc-full-features/JsonFormatter.vcxproj | 9 +- src/iuse.cpp | 20 +- 7 files changed, 847 insertions(+), 137 deletions(-) diff --git a/data/json/items/tool/explosives.json b/data/json/items/tool/explosives.json index 476a2192988f7..d2db890e53673 100644 --- a/data/json/items/tool/explosives.json +++ b/data/json/items/tool/explosives.json @@ -1104,7 +1104,7 @@ "max_charges": 1, "turns_per_charge": 1, "use_action": [ "MOLOTOV_LIT" ], - "flags": [ "LIGHT_15", "TRADER_AVOID", "NPC_THROW_NOW", "NO_REPAIR", "WATER_EXTINGUISH" ] + "flags": [ "LIGHT_15", "TRADER_AVOID", "NPC_THROW_NOW", "NO_REPAIR", "WATER_EXTINGUISH", "ACT_ON_RANGED_HIT" ] }, { "id": "bootleg_pipebomb", diff --git a/msvc-full-features/Cataclysm-lib-vcpkg-static.vcxproj b/msvc-full-features/Cataclysm-lib-vcpkg-static.vcxproj index 66cf377f1163a..d452936caa658 100644 --- a/msvc-full-features/Cataclysm-lib-vcpkg-static.vcxproj +++ b/msvc-full-features/Cataclysm-lib-vcpkg-static.vcxproj @@ -1,137 +1,717 @@ - - - Debug - x64 - - - Debug - Win32 - - - Release - x64 - - - Release - Win32 - - + + + Debug + x64 + + + Debug + Win32 + + + Release + x64 + + + Release + Win32 + + 16.0 {0009BB11-11AD-4C14-A5FC-D882A942C00B} Win32Proj CataclysmLib 10.0 - x86-windows-static - x64-windows-static + x86-windows-static + x64-windows-static - - StaticLibrary - v142 - MultiByte + StaticLibrary + v142 + MultiByte - true + true - false - false + false + false - - - - - - - - - - $(ProjectName)-$(Configuration)-$(Platform) - .lib - $(SolutionDir)..\ - $(SolutionDir)$(ProjectName)\$(Configuration)\$(Platform)\ - - - true - - - false - - - - Level1 - Use - true - false - true - false - ProgramDatabase - 4819;4146;26495;26444;26451;4068;6319;6237 - stdafx.h - /bigobj /utf-8 %(AdditionalOptions) - _SCL_SECURE_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS;WIN32_LEAN_AND_MEAN;_CONSOLE;SDL_SOUND;TILES;SDL_BUILDING_LIBRARY;LOCALIZE;USE_VCPKG;%(PreprocessorDefinitions) - stdcpp14 - ..\src;%(AdditionalIncludeDirectories) - - - Windows - true - Default - true - /LTCG:OFF %(AdditionalOptions) - winmm.lib;imm32.lib;version.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;setupapi.lib;%(AdditionalDependencies) - - - prebuild.cmd - Get version string - - - true - - - - - Disabled - false - false - _DEBUG;%(PreprocessorDefinitions) - MultiThreadedDebug - - - - - MaxSpeed - true - true - NDEBUG;%(PreprocessorDefinitions) - DebugFastLink - MultiThreaded - - - true - true - - - - - WIN32;%(PreprocessorDefinitions) - - - - - - - - - - Create - - - - - - + + + + + + + + + $(ProjectName)-$(Configuration)-$(Platform) + .lib + $(SolutionDir)..\ + $(SolutionDir)$(ProjectName)\$(Configuration)\$(Platform)\ + + + true + + + false + + + true + + + + Level1 + Use + true + false + true + false + ProgramDatabase + 4819;4146;26495;26444;26451;4068;6319;6237 + stdafx.h + /bigobj /utf-8 %(AdditionalOptions) + _SCL_SECURE_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS;WIN32_LEAN_AND_MEAN;_CONSOLE;SDL_SOUND;TILES;SDL_BUILDING_LIBRARY;LOCALIZE;USE_VCPKG;%(PreprocessorDefinitions) + stdcpp14 + ..\src;%(AdditionalIncludeDirectories) + + + Windows + true + Default + true + /LTCG:OFF %(AdditionalOptions) + winmm.lib;imm32.lib;version.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;setupapi.lib;%(AdditionalDependencies) + + + prebuild.cmd + Get version string + + + true + + + + + Disabled + false + false + _DEBUG;%(PreprocessorDefinitions) + MultiThreadedDebug + + + + + MaxSpeed + true + true + NDEBUG;%(PreprocessorDefinitions) + DebugFastLink + MultiThreaded + + + true + true + + + + + WIN32;%(PreprocessorDefinitions) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Create + + + + + + \ No newline at end of file diff --git a/msvc-full-features/Cataclysm-test-vcpkg-static.vcxproj b/msvc-full-features/Cataclysm-test-vcpkg-static.vcxproj index e1bfccb6bdd0c..801762197fce9 100644 --- a/msvc-full-features/Cataclysm-test-vcpkg-static.vcxproj +++ b/msvc-full-features/Cataclysm-test-vcpkg-static.vcxproj @@ -59,6 +59,9 @@ false + + true + Level1 @@ -120,12 +123,131 @@ - + + + + + + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Create diff --git a/msvc-full-features/Cataclysm-vcpkg-static.sln b/msvc-full-features/Cataclysm-vcpkg-static.sln index ce23375f05220..0449aebd41c46 100644 --- a/msvc-full-features/Cataclysm-vcpkg-static.sln +++ b/msvc-full-features/Cataclysm-vcpkg-static.sln @@ -38,7 +38,6 @@ Global {2C1ECEE1-9686-4C3C-8DE1-88996EE43378}.Debug|x86.ActiveCfg = Debug|Win32 {2C1ECEE1-9686-4C3C-8DE1-88996EE43378}.Debug|x86.Build.0 = Debug|Win32 {2C1ECEE1-9686-4C3C-8DE1-88996EE43378}.Release|x64.ActiveCfg = Release|x64 - {2C1ECEE1-9686-4C3C-8DE1-88996EE43378}.Release|x64.Build.0 = Release|x64 {2C1ECEE1-9686-4C3C-8DE1-88996EE43378}.Release|x86.ActiveCfg = Release|Win32 {2C1ECEE1-9686-4C3C-8DE1-88996EE43378}.Release|x86.Build.0 = Release|Win32 {0009BB11-11AD-4C14-A5FC-D882A942C00B}.Debug|x64.ActiveCfg = Debug|x64 @@ -54,7 +53,6 @@ Global {35D74C75-FC4A-442F-AF44-43BC9D845BAF}.Debug|x86.ActiveCfg = Debug|Win32 {35D74C75-FC4A-442F-AF44-43BC9D845BAF}.Debug|x86.Build.0 = Debug|Win32 {35D74C75-FC4A-442F-AF44-43BC9D845BAF}.Release|x64.ActiveCfg = Release|x64 - {35D74C75-FC4A-442F-AF44-43BC9D845BAF}.Release|x64.Build.0 = Release|x64 {35D74C75-FC4A-442F-AF44-43BC9D845BAF}.Release|x86.ActiveCfg = Release|Win32 {35D74C75-FC4A-442F-AF44-43BC9D845BAF}.Release|x86.Build.0 = Release|Win32 EndGlobalSection diff --git a/msvc-full-features/Cataclysm-vcpkg-static.vcxproj b/msvc-full-features/Cataclysm-vcpkg-static.vcxproj index b8021c0e3d405..51ebb1a536e64 100644 --- a/msvc-full-features/Cataclysm-vcpkg-static.vcxproj +++ b/msvc-full-features/Cataclysm-vcpkg-static.vcxproj @@ -59,6 +59,9 @@ false + + true + Level1 diff --git a/msvc-full-features/JsonFormatter.vcxproj b/msvc-full-features/JsonFormatter.vcxproj index bf31ec6133545..7a24cc17584c6 100644 --- a/msvc-full-features/JsonFormatter.vcxproj +++ b/msvc-full-features/JsonFormatter.vcxproj @@ -59,6 +59,9 @@ false + + true + Level1 @@ -121,12 +124,12 @@ - + - + Create @@ -139,4 +142,4 @@ - + \ No newline at end of file diff --git a/src/iuse.cpp b/src/iuse.cpp index 900a37a409afd..d48f00321e221 100644 --- a/src/iuse.cpp +++ b/src/iuse.cpp @@ -3923,25 +3923,29 @@ int iuse::arrow_flammable( player *p, item *it, bool, const tripoint & ) int iuse::molotov_lit( player *p, item *it, bool t, const tripoint &pos ) { if( pos.x == -999 || pos.y == -999 ) { + debugmsg(_("pos")); return 0; + } else if( !t ) { + debugmsg(_("bool t")); + for( auto &pt : g->m.points_in_radius( pos, 1, 0 ) ) { + const int intensity = 1 + one_in( 3 ) + one_in( 5 ); + g->m.add_field( pt, fd_fire, intensity ); + } + return 1; } else if( it->charges > 0 ) { p->add_msg_if_player( m_info, _( "You've already lit the %s, try throwing it instead." ), it->tname() ); + debugmsg(_("charges")); return 0; } else if( p->has_item( *it ) && it->charges == 0 ) { + debugmsg(_("go out")); it->charges += 1; if( one_in( 5 ) ) { p->add_msg_if_player( _( "Your lit Molotov goes out." ) ); it->convert( itype_molotov ).active = false; } - } else { - if( !t ) { - for( auto &pt : g->m.points_in_radius( pos, 1, 0 ) ) { - const int intensity = 1 + one_in( 3 ) + one_in( 5 ); - g->m.add_field( pt, fd_fire, intensity ); - } - } - } + } + debugmsg(_("nothing happen")); return 0; } From 1309f9f2332c396b8513717fe2e35cf0a2caf1de Mon Sep 17 00:00:00 2001 From: Hirmuolio Date: Sat, 27 Jun 2020 14:51:18 +0300 Subject: [PATCH 002/420] molotov --- src/iuse.cpp | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/iuse.cpp b/src/iuse.cpp index 3528dd428cf5a..b228d27d639ab 100644 --- a/src/iuse.cpp +++ b/src/iuse.cpp @@ -3925,10 +3925,8 @@ int iuse::arrow_flammable( player *p, item *it, bool, const tripoint & ) int iuse::molotov_lit( player *p, item *it, bool t, const tripoint &pos ) { if( pos.x == -999 || pos.y == -999 ) { - debugmsg(_("pos")); return 0; } else if( !t ) { - debugmsg(_("bool t")); for( auto &pt : g->m.points_in_radius( pos, 1, 0 ) ) { const int intensity = 1 + one_in( 3 ) + one_in( 5 ); g->m.add_field( pt, fd_fire, intensity ); @@ -3937,17 +3935,15 @@ int iuse::molotov_lit( player *p, item *it, bool t, const tripoint &pos ) } else if( it->charges > 0 ) { p->add_msg_if_player( m_info, _( "You've already lit the %s, try throwing it instead." ), it->tname() ); - debugmsg(_("charges")); return 0; } else if( p->has_item( *it ) && it->charges == 0 ) { - debugmsg(_("go out")); it->charges += 1; if( one_in( 5 ) ) { p->add_msg_if_player( _( "Your lit Molotov goes out." ) ); it->convert( itype_molotov ).active = false; } + return 0; } - debugmsg(_("nothing happen")); return 0; } From 14a41edb000f0364e0637fd931da0f1ba8eec248 Mon Sep 17 00:00:00 2001 From: Hirmuolio Date: Sat, 27 Jun 2020 14:52:37 +0300 Subject: [PATCH 003/420] astyle --- src/iuse.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/iuse.cpp b/src/iuse.cpp index b228d27d639ab..b13ded24123a5 100644 --- a/src/iuse.cpp +++ b/src/iuse.cpp @@ -3926,12 +3926,12 @@ int iuse::molotov_lit( player *p, item *it, bool t, const tripoint &pos ) { if( pos.x == -999 || pos.y == -999 ) { return 0; - } else if( !t ) { + } else if( !t ) { for( auto &pt : g->m.points_in_radius( pos, 1, 0 ) ) { const int intensity = 1 + one_in( 3 ) + one_in( 5 ); g->m.add_field( pt, fd_fire, intensity ); } - return 1; + return 1; } else if( it->charges > 0 ) { p->add_msg_if_player( m_info, _( "You've already lit the %s, try throwing it instead." ), it->tname() ); @@ -3943,7 +3943,7 @@ int iuse::molotov_lit( player *p, item *it, bool t, const tripoint &pos ) it->convert( itype_molotov ).active = false; } return 0; - } + } return 0; } From d368a9e68ea12457ab5ebb016b30eb59d3f59ac0 Mon Sep 17 00:00:00 2001 From: Hirmuolio Date: Sat, 27 Jun 2020 14:54:04 +0300 Subject: [PATCH 004/420] VS mess --- .../Cataclysm-lib-vcpkg-static.vcxproj | 822 +++--------------- .../Cataclysm-test-vcpkg-static.vcxproj | 126 +-- msvc-full-features/Cataclysm-vcpkg-static.sln | 2 + .../Cataclysm-vcpkg-static.vcxproj | 3 - msvc-full-features/JsonFormatter.vcxproj | 9 +- 5 files changed, 128 insertions(+), 834 deletions(-) diff --git a/msvc-full-features/Cataclysm-lib-vcpkg-static.vcxproj b/msvc-full-features/Cataclysm-lib-vcpkg-static.vcxproj index d452936caa658..66cf377f1163a 100644 --- a/msvc-full-features/Cataclysm-lib-vcpkg-static.vcxproj +++ b/msvc-full-features/Cataclysm-lib-vcpkg-static.vcxproj @@ -1,717 +1,137 @@ - - - Debug - x64 - - - Debug - Win32 - - - Release - x64 - - - Release - Win32 - - + + + Debug + x64 + + + Debug + Win32 + + + Release + x64 + + + Release + Win32 + + 16.0 {0009BB11-11AD-4C14-A5FC-D882A942C00B} Win32Proj CataclysmLib 10.0 - x86-windows-static - x64-windows-static + x86-windows-static + x64-windows-static + - StaticLibrary - v142 - MultiByte + StaticLibrary + v142 + MultiByte - true + true - false - false + false + false - - - - - - - - - $(ProjectName)-$(Configuration)-$(Platform) - .lib - $(SolutionDir)..\ - $(SolutionDir)$(ProjectName)\$(Configuration)\$(Platform)\ - - - true - - - false - - - true - - - - Level1 - Use - true - false - true - false - ProgramDatabase - 4819;4146;26495;26444;26451;4068;6319;6237 - stdafx.h - /bigobj /utf-8 %(AdditionalOptions) - _SCL_SECURE_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS;WIN32_LEAN_AND_MEAN;_CONSOLE;SDL_SOUND;TILES;SDL_BUILDING_LIBRARY;LOCALIZE;USE_VCPKG;%(PreprocessorDefinitions) - stdcpp14 - ..\src;%(AdditionalIncludeDirectories) - - - Windows - true - Default - true - /LTCG:OFF %(AdditionalOptions) - winmm.lib;imm32.lib;version.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;setupapi.lib;%(AdditionalDependencies) - - - prebuild.cmd - Get version string - - - true - - - - - Disabled - false - false - _DEBUG;%(PreprocessorDefinitions) - MultiThreadedDebug - - - - - MaxSpeed - true - true - NDEBUG;%(PreprocessorDefinitions) - DebugFastLink - MultiThreaded - - - true - true - - - - - WIN32;%(PreprocessorDefinitions) - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Create - - - - - - \ No newline at end of file + + + + + + + + + + $(ProjectName)-$(Configuration)-$(Platform) + .lib + $(SolutionDir)..\ + $(SolutionDir)$(ProjectName)\$(Configuration)\$(Platform)\ + + + true + + + false + + + + Level1 + Use + true + false + true + false + ProgramDatabase + 4819;4146;26495;26444;26451;4068;6319;6237 + stdafx.h + /bigobj /utf-8 %(AdditionalOptions) + _SCL_SECURE_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS;WIN32_LEAN_AND_MEAN;_CONSOLE;SDL_SOUND;TILES;SDL_BUILDING_LIBRARY;LOCALIZE;USE_VCPKG;%(PreprocessorDefinitions) + stdcpp14 + ..\src;%(AdditionalIncludeDirectories) + + + Windows + true + Default + true + /LTCG:OFF %(AdditionalOptions) + winmm.lib;imm32.lib;version.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;setupapi.lib;%(AdditionalDependencies) + + + prebuild.cmd + Get version string + + + true + + + + + Disabled + false + false + _DEBUG;%(PreprocessorDefinitions) + MultiThreadedDebug + + + + + MaxSpeed + true + true + NDEBUG;%(PreprocessorDefinitions) + DebugFastLink + MultiThreaded + + + true + true + + + + + WIN32;%(PreprocessorDefinitions) + + + + + + + + + + Create + + + + + + diff --git a/msvc-full-features/Cataclysm-test-vcpkg-static.vcxproj b/msvc-full-features/Cataclysm-test-vcpkg-static.vcxproj index 801762197fce9..e1bfccb6bdd0c 100644 --- a/msvc-full-features/Cataclysm-test-vcpkg-static.vcxproj +++ b/msvc-full-features/Cataclysm-test-vcpkg-static.vcxproj @@ -59,9 +59,6 @@ false - - true - Level1 @@ -123,131 +120,12 @@ + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + Create diff --git a/msvc-full-features/Cataclysm-vcpkg-static.sln b/msvc-full-features/Cataclysm-vcpkg-static.sln index 0449aebd41c46..ce23375f05220 100644 --- a/msvc-full-features/Cataclysm-vcpkg-static.sln +++ b/msvc-full-features/Cataclysm-vcpkg-static.sln @@ -38,6 +38,7 @@ Global {2C1ECEE1-9686-4C3C-8DE1-88996EE43378}.Debug|x86.ActiveCfg = Debug|Win32 {2C1ECEE1-9686-4C3C-8DE1-88996EE43378}.Debug|x86.Build.0 = Debug|Win32 {2C1ECEE1-9686-4C3C-8DE1-88996EE43378}.Release|x64.ActiveCfg = Release|x64 + {2C1ECEE1-9686-4C3C-8DE1-88996EE43378}.Release|x64.Build.0 = Release|x64 {2C1ECEE1-9686-4C3C-8DE1-88996EE43378}.Release|x86.ActiveCfg = Release|Win32 {2C1ECEE1-9686-4C3C-8DE1-88996EE43378}.Release|x86.Build.0 = Release|Win32 {0009BB11-11AD-4C14-A5FC-D882A942C00B}.Debug|x64.ActiveCfg = Debug|x64 @@ -53,6 +54,7 @@ Global {35D74C75-FC4A-442F-AF44-43BC9D845BAF}.Debug|x86.ActiveCfg = Debug|Win32 {35D74C75-FC4A-442F-AF44-43BC9D845BAF}.Debug|x86.Build.0 = Debug|Win32 {35D74C75-FC4A-442F-AF44-43BC9D845BAF}.Release|x64.ActiveCfg = Release|x64 + {35D74C75-FC4A-442F-AF44-43BC9D845BAF}.Release|x64.Build.0 = Release|x64 {35D74C75-FC4A-442F-AF44-43BC9D845BAF}.Release|x86.ActiveCfg = Release|Win32 {35D74C75-FC4A-442F-AF44-43BC9D845BAF}.Release|x86.Build.0 = Release|Win32 EndGlobalSection diff --git a/msvc-full-features/Cataclysm-vcpkg-static.vcxproj b/msvc-full-features/Cataclysm-vcpkg-static.vcxproj index 51ebb1a536e64..b8021c0e3d405 100644 --- a/msvc-full-features/Cataclysm-vcpkg-static.vcxproj +++ b/msvc-full-features/Cataclysm-vcpkg-static.vcxproj @@ -59,9 +59,6 @@ false - - true - Level1 diff --git a/msvc-full-features/JsonFormatter.vcxproj b/msvc-full-features/JsonFormatter.vcxproj index 7a24cc17584c6..bf31ec6133545 100644 --- a/msvc-full-features/JsonFormatter.vcxproj +++ b/msvc-full-features/JsonFormatter.vcxproj @@ -59,9 +59,6 @@ false - - true - Level1 @@ -124,12 +121,12 @@ - + + - Create @@ -142,4 +139,4 @@ - \ No newline at end of file + From b35831a1f0314783fb7d4a274bbd9c66bb8b9996 Mon Sep 17 00:00:00 2001 From: Hirmuolio Date: Sat, 27 Jun 2020 15:08:10 +0300 Subject: [PATCH 005/420] do not process weapon twice --- src/character.cpp | 5 ----- src/character.h | 2 +- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/src/character.cpp b/src/character.cpp index 80955b7c4fb0c..0adc59c35cfd4 100644 --- a/src/character.cpp +++ b/src/character.cpp @@ -2431,11 +2431,6 @@ std::vector Character::all_items_loc() std::vector Character::top_items_loc() { - std::vector ret; - if( has_weapon() ) { - item_location weap_loc( *this, &weapon ); - ret.push_back( weap_loc ); - } for( item &worn_it : worn ) { item_location worn_loc( *this, &worn_it ); ret.push_back( worn_loc ); diff --git a/src/character.h b/src/character.h index d58d31a355c25..468d1bdf32ced 100644 --- a/src/character.h +++ b/src/character.h @@ -1318,7 +1318,7 @@ class Character : public Creature, public visitable // returns a list of all item_location the character has, including items contained in other items. // only for CONTAINER pocket type; does not look for magazines std::vector all_items_loc(); - // Returns list of all the top level item_lodation the character has. Includes worn and held items. + // Returns list of all the top level item_lodation the character has. Includes worn items but excludes items held on hand. std::vector top_items_loc(); /** Return the item pointer of the item with given invlet, return nullptr if * the player does not have such an item with that invlet. Don't use this on npcs. From 098acc9059093ddd8d63bf2acfa904b29ad62c7e Mon Sep 17 00:00:00 2001 From: Hirmuolio Date: Sat, 27 Jun 2020 15:36:22 +0300 Subject: [PATCH 006/420] missing vector --- src/character.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/character.cpp b/src/character.cpp index 0adc59c35cfd4..5b4c2f2d635fe 100644 --- a/src/character.cpp +++ b/src/character.cpp @@ -2431,6 +2431,7 @@ std::vector Character::all_items_loc() std::vector Character::top_items_loc() { + std::vector ret; for( item &worn_it : worn ) { item_location worn_loc( *this, &worn_it ); ret.push_back( worn_loc ); From 014192bc8ddfc64e4881dd4459f8d29da2de1082 Mon Sep 17 00:00:00 2001 From: Fris0uman Date: Mon, 29 Jun 2020 20:02:54 +0200 Subject: [PATCH 007/420] Move bandage and disinfectant to body --- src/bodypart.h | 1 - src/character.cpp | 28 ++++++++++++---------------- src/character.h | 1 - src/creature.cpp | 30 ++++++++++++++++++++++++++++++ src/creature.h | 6 ++++++ src/iexamine.cpp | 5 ++--- src/iuse_actor.cpp | 6 ++++-- src/savegame_json.cpp | 30 ++++++++++++++++++++++++------ 8 files changed, 78 insertions(+), 29 deletions(-) diff --git a/src/bodypart.h b/src/bodypart.h index 500d733bc8e5d..78c4eb7ec32ac 100644 --- a/src/bodypart.h +++ b/src/bodypart.h @@ -158,7 +158,6 @@ class bodypart int hp_max; int healed_total = 0; - /** Not used yet*/ int damage_bandaged = 0; int damage_disinfected = 0; diff --git a/src/character.cpp b/src/character.cpp index 3f4a2c628cfa6..72f8d0102edb6 100644 --- a/src/character.cpp +++ b/src/character.cpp @@ -400,8 +400,6 @@ Character &get_player_character() Character::Character() : visitable(), - damage_bandaged( {{ 0 }} ), - damage_disinfected( {{ 0 }} ), cached_time( calendar::before_time_starts ), id( -1 ), next_climate_control_check( calendar::before_time_starts ), @@ -4565,25 +4563,23 @@ void Character::regen( int rate_multiplier ) } // include healing effects - for( int i = 0; i < num_hp_parts; i++ ) { - const bodypart_id &bp = convert_bp( hp_to_bp( static_cast( i ) ) ).id(); + for( const bodypart_id &bp : get_all_body_parts( true ) ) { float healing = healing_rate_medicine( rest, bp ) * to_turns( 5_minutes ); - int healing_apply = roll_remainder( healing ); - healed_bp( i, healing_apply ); + mod_part_healed_total( bp, healing_apply ); heal( bp, healing_apply ); - if( damage_bandaged[i] > 0 ) { - damage_bandaged[i] -= healing_apply; - if( damage_bandaged[i] <= 0 ) { - damage_bandaged[i] = 0; + if( get_part_damage_bandaged( bp ) > 0 ) { + mod_part_damage_bandaged( bp, -healing_apply ); + if( get_part_damage_bandaged( bp ) <= 0 ) { + set_part_damage_bandaged( bp, 0 ); remove_effect( effect_bandaged, bp->token ); add_msg_if_player( _( "Bandaged wounds on your %s healed." ), body_part_name( bp ) ); } } - if( damage_disinfected[i] > 0 ) { - damage_disinfected[i] -= healing_apply; - if( damage_disinfected[i] <= 0 ) { - damage_disinfected[i] = 0; + if( get_part_damage_disinfected( bp ) > 0 ) { + mod_part_damage_disinfected( bp, -healing_apply ); + if( get_part_damage_disinfected( bp ) <= 0 ) { + set_part_damage_disinfected( bp, 0 ); remove_effect( effect_disinfected, bp->token ); add_msg_if_player( _( "Disinfected wounds on your %s healed." ), body_part_name( bp ) ); } @@ -4591,12 +4587,12 @@ void Character::regen( int rate_multiplier ) // remove effects if the limb was healed by other way if( has_effect( effect_bandaged, bp->token ) && ( get_part( bp )->is_at_max_hp() ) ) { - damage_bandaged[i] = 0; + set_part_damage_bandaged( bp, 0 ); remove_effect( effect_bandaged, bp->token ); add_msg_if_player( _( "Bandaged wounds on your %s healed." ), body_part_name( bp ) ); } if( has_effect( effect_disinfected, bp->token ) && ( get_part( bp )->is_at_max_hp() ) ) { - damage_disinfected[i] = 0; + set_part_damage_disinfected( bp, 0 ); remove_effect( effect_disinfected, bp->token ); add_msg_if_player( _( "Disinfected wounds on your %s healed." ), body_part_name( bp ) ); } diff --git a/src/character.h b/src/character.h index d58d31a355c25..5773a32adc225 100644 --- a/src/character.h +++ b/src/character.h @@ -1707,7 +1707,6 @@ class Character : public Creature, public visitable bool male = false; std::list worn; - std::array damage_bandaged, damage_disinfected; bool nv_cached = false; // Means player sit inside vehicle on the tile he is now bool in_vehicle = false; diff --git a/src/creature.cpp b/src/creature.cpp index 25c59fc1655d9..f7d9f229e6e27 100644 --- a/src/creature.cpp +++ b/src/creature.cpp @@ -1506,6 +1506,16 @@ int Creature::get_part_healed_total( const bodypart_id &id ) const return get_part( id ).get_healed_total(); } +int Creature::get_part_damage_disinfected( const bodypart_id &id ) const +{ + return get_part( id ).get_damage_disinfected(); +} + +int Creature::get_part_damage_bandaged( const bodypart_id &id ) const +{ + return get_part( id ).get_damage_bandaged(); +} + void Creature::set_part_hp_cur( const bodypart_id &id, int set ) { get_part( id )->set_hp_cur( set ); @@ -1521,6 +1531,16 @@ void Creature::set_part_healed_total( const bodypart_id &id, int set ) get_part( id )->set_healed_total( set ); } +void Creature::set_part_damage_disinfected( const bodypart_id &id, int set ) +{ + get_part( id )->set_damage_disinfected( set ); +} + +void Creature::set_part_damage_bandaged( const bodypart_id &id, int set ) +{ + get_part( id )->set_damage_bandaged( set ); +} + void Creature::mod_part_hp_cur( const bodypart_id &id, int mod ) { get_part( id )->mod_hp_cur( mod ); @@ -1536,6 +1556,16 @@ void Creature::mod_part_healed_total( const bodypart_id &id, int mod ) get_part( id )->mod_healed_total( mod ); } +void Creature::mod_part_damage_disinfected( const bodypart_id &id, int mod ) +{ + get_part( id )->mod_damage_disinfected( mod ); +} + +void Creature::mod_part_damage_bandaged( const bodypart_id &id, int mod ) +{ + get_part( id )->mod_damage_bandaged( mod ); +} + void Creature::set_all_parts_hp_cur( const int set ) { for( std::pair &elem : body ) { diff --git a/src/creature.h b/src/creature.h index f17b3a727eafd..51e9b99d83ab8 100644 --- a/src/creature.h +++ b/src/creature.h @@ -617,13 +617,19 @@ class Creature int get_part_hp_max( const bodypart_id &id ) const; int get_part_healed_total( const bodypart_id &id ) const; + int get_part_damage_disinfected( const bodypart_id &id ) const; + int get_part_damage_bandaged( const bodypart_id &id ) const; void set_part_hp_cur( const bodypart_id &id, int set ); void set_part_hp_max( const bodypart_id &id, int set ); void set_part_healed_total( const bodypart_id &id, int set ); + void set_part_damage_disinfected( const bodypart_id &id, int set ); + void set_part_damage_bandaged( const bodypart_id &id, int set ); void mod_part_hp_cur( const bodypart_id &id, int mod ); void mod_part_hp_max( const bodypart_id &id, int mod ); void mod_part_healed_total( const bodypart_id &id, int mod ); + void mod_part_damage_disinfected( const bodypart_id &id, int mod ); + void mod_part_damage_bandaged( const bodypart_id &id, int mod ); void set_all_parts_hp_cur( int set ); diff --git a/src/iexamine.cpp b/src/iexamine.cpp index 7476f9e8eae9f..45bf621677cd1 100644 --- a/src/iexamine.cpp +++ b/src/iexamine.cpp @@ -4897,9 +4897,8 @@ void iexamine::autodoc( player &p, const tripoint &examp ) patient.add_effect( effect_disinfected, 1_turns, bp_healed->token ); effect &e = patient.get_effect( effect_disinfected, bp_healed->token ); e.set_duration( e.get_int_dur_factor() * disinfectant_intensity ); - hp_part target_part = player::bp_to_hp( bp_healed->token ); - patient.damage_disinfected[target_part] = patient.get_part_hp_max( bp_healed ) - - patient.get_part_hp_cur( bp_healed ); + patient.set_part_damage_disinfected( bp_healed, + patient.get_part_hp_max( bp_healed ) - patient.get_part_hp_cur( bp_healed ) ); } } patient.moves -= 500; diff --git a/src/iuse_actor.cpp b/src/iuse_actor.cpp index 73069a4020575..5b8dbc93da0b5 100644 --- a/src/iuse_actor.cpp +++ b/src/iuse_actor.cpp @@ -3299,7 +3299,8 @@ int heal_actor::finish_using( player &healer, player &patient, item &it, hp_part patient.add_effect( effect_bandaged, 1_turns, bp_healed ); effect &e = patient.get_effect( effect_bandaged, bp_healed ); e.set_duration( e.get_int_dur_factor() * bandages_intensity ); - patient.damage_bandaged[healed] = patient.get_part_hp_max( bp ) - patient.get_part_hp_cur( bp ); + patient.set_part_damage_bandaged( bp, + patient.get_part_hp_max( bp ) - patient.get_part_hp_cur( bp ) ); practice_amount += 2 * bandages_intensity; } if( disinfectant_power > 0 ) { @@ -3307,7 +3308,8 @@ int heal_actor::finish_using( player &healer, player &patient, item &it, hp_part patient.add_effect( effect_disinfected, 1_turns, bp_healed ); effect &e = patient.get_effect( effect_disinfected, bp_healed ); e.set_duration( e.get_int_dur_factor() * disinfectant_intensity ); - patient.damage_disinfected[healed] = patient.get_part_hp_max( bp ) - patient.get_part_hp_cur( bp ); + patient.set_part_damage_disinfected( bp, + patient.get_part_hp_max( bp ) - patient.get_part_hp_cur( bp ) ); practice_amount += 2 * disinfectant_intensity; } practice_amount = std::max( 9.0f, practice_amount ); diff --git a/src/savegame_json.cpp b/src/savegame_json.cpp index 67c4aa268a39d..349bd27b0a9c8 100644 --- a/src/savegame_json.cpp +++ b/src/savegame_json.cpp @@ -563,10 +563,7 @@ void Character::load( const JsonObject &data ) data.read( "stim", stim ); data.read( "stamina", stamina ); - data.read( "damage_bandaged", damage_bandaged ); - data.read( "damage_disinfected", damage_disinfected ); data.read( "magic", magic ); - JsonArray parray; data.read( "underwater", underwater ); @@ -645,6 +642,30 @@ void Character::load( const JsonObject &data ) set_part_hp_cur( bodypart_id( "leg_r" ), hp_cur[5] ); set_part_hp_max( bodypart_id( "leg_r" ), hp_max[5] ); } + if( data.has_array( "damage_bandaged" ) ) { + set_anatomy( anatomy_id( "human_anatomy" ) ); + set_body(); + std::array damage_bandaged; + data.read( "damage_bandaged", damage_bandaged ); + set_part_damage_bandaged( bodypart_id( "head" ), damage_bandaged[0] ); + set_part_damage_bandaged( bodypart_id( "torso" ), damage_bandaged[1] ); + set_part_damage_bandaged( bodypart_id( "arm_l" ), damage_bandaged[2] ); + set_part_damage_bandaged( bodypart_id( "arm_r" ), damage_bandaged[3] ); + set_part_damage_bandaged( bodypart_id( "leg_l" ), damage_bandaged[4] ); + set_part_damage_bandaged( bodypart_id( "leg_r" ), damage_bandaged[5] ); + } + if( data.has_array( "damage_disinfected" ) ) { + set_anatomy( anatomy_id( "human_anatomy" ) ); + set_body(); + std::array damage_disinfected; + data.read( "damage_disinfected", damage_disinfected ); + set_part_damage_disinfected( bodypart_id( "head" ), damage_disinfected[0] ); + set_part_damage_disinfected( bodypart_id( "torso" ), damage_disinfected[1] ); + set_part_damage_disinfected( bodypart_id( "arm_l" ), damage_disinfected[2] ); + set_part_damage_disinfected( bodypart_id( "arm_r" ), damage_disinfected[3] ); + set_part_damage_disinfected( bodypart_id( "leg_l" ), damage_disinfected[4] ); + set_part_damage_disinfected( bodypart_id( "leg_r" ), damage_disinfected[5] ); + } inv.clear(); @@ -901,9 +922,6 @@ void player::store( JsonOut &json ) const json.member( "in_vehicle", in_vehicle ); json.member( "id", getID() ); - // potential incompatibility with future expansion - json.member( "damage_bandaged", damage_bandaged ); - json.member( "damage_disinfected", damage_disinfected ); // "Looks like I picked the wrong week to quit smoking." - Steve McCroskey json.member( "addictions", addictions ); json.member( "followers", follower_ids ); From 6fb0e08ce3c1a9030aefbd4c469fc7820e3c4461 Mon Sep 17 00:00:00 2001 From: Fris0uman Date: Mon, 29 Jun 2020 20:11:24 +0200 Subject: [PATCH 008/420] move healed_total --- src/character.cpp | 6 ------ src/character.h | 5 ----- src/savegame_json.cpp | 15 ++++++++++++--- 3 files changed, 12 insertions(+), 14 deletions(-) diff --git a/src/character.cpp b/src/character.cpp index 72f8d0102edb6..0bf0e7bbcb4e4 100644 --- a/src/character.cpp +++ b/src/character.cpp @@ -437,7 +437,6 @@ Character::Character() : healthy_calories = 55000; stored_calories = healthy_calories; initialize_stomach_contents(); - healed_total = { { 0, 0, 0, 0, 0, 0 } }; name.clear(); custom_profession.clear(); @@ -8162,11 +8161,6 @@ tripoint Character::adjacent_tile() const return random_entry( ret, pos() ); // player position if no valid adjacent tiles } -void Character::healed_bp( int bp, int amount ) -{ - healed_total[bp] += amount; -} - void Character::set_fac_id( const std::string &my_fac_id ) { fac_id = faction_id( my_fac_id ); diff --git a/src/character.h b/src/character.h index 5773a32adc225..30c9c18db0bd8 100644 --- a/src/character.h +++ b/src/character.h @@ -1928,11 +1928,6 @@ class Character : public Creature, public visitable void shout( std::string msg = "", bool order = false ); /** Handles Character vomiting effects */ void vomit(); - // adds total healing to the bodypart. this is only a counter. - void healed_bp( int bp, int amount ); - - // the amount healed per bodypart per day - std::array healed_total; std::map mutation_category_level; diff --git a/src/savegame_json.cpp b/src/savegame_json.cpp index 349bd27b0a9c8..a7204704b4bf7 100644 --- a/src/savegame_json.cpp +++ b/src/savegame_json.cpp @@ -544,7 +544,6 @@ void Character::load( const JsonObject &data ) // health data.read( "healthy", healthy ); data.read( "healthy_mod", healthy_mod ); - data.read( "healed_24h", healed_total ); // status temp_cur.fill( 5000 ); @@ -666,7 +665,18 @@ void Character::load( const JsonObject &data ) set_part_damage_disinfected( bodypart_id( "leg_l" ), damage_disinfected[4] ); set_part_damage_disinfected( bodypart_id( "leg_r" ), damage_disinfected[5] ); } - + if( data.has_array( "healed_24h" ) ) { + set_anatomy( anatomy_id( "human_anatomy" ) ); + set_body(); + std::array healed_total; + data.read( "healed_24h", healed_total ); + set_part_healed_total( bodypart_id( "head" ), healed_total[0] ); + set_part_healed_total( bodypart_id( "torso" ), healed_total[1] ); + set_part_healed_total( bodypart_id( "arm_l" ), healed_total[2] ); + set_part_healed_total( bodypart_id( "arm_r" ), healed_total[3] ); + set_part_healed_total( bodypart_id( "leg_l" ), healed_total[4] ); + set_part_healed_total( bodypart_id( "leg_r" ), healed_total[5] ); + } inv.clear(); if( data.has_member( "inv" ) ) { @@ -792,7 +802,6 @@ void Character::store( JsonOut &json ) const // health json.member( "healthy", healthy ); json.member( "healthy_mod", healthy_mod ); - json.member( "healed_24h", healed_total ); // status json.member( "temp_cur", temp_cur ); From 11557e9c38ee5675023f9249fc56911bde6a4726 Mon Sep 17 00:00:00 2001 From: Fris0uman Date: Mon, 29 Jun 2020 20:29:58 +0200 Subject: [PATCH 009/420] some hp_part cleaning --- src/character.cpp | 3 +-- src/iexamine.cpp | 7 +++---- src/map_field.cpp | 3 +-- src/npc.cpp | 8 +++----- src/npctalk_funcs.cpp | 34 ++++++++++++++++------------------ src/panels.cpp | 3 +-- src/player.cpp | 3 +-- 7 files changed, 26 insertions(+), 35 deletions(-) diff --git a/src/character.cpp b/src/character.cpp index 0bf0e7bbcb4e4..7d6687197e6fa 100644 --- a/src/character.cpp +++ b/src/character.cpp @@ -8921,8 +8921,7 @@ void Character::hurtall( int dam, Creature *source, bool disturb /*= true*/ ) int Character::hitall( int dam, int vary, Creature *source ) { int damage_taken = 0; - for( int i = 0; i < num_hp_parts; i++ ) { - const bodypart_id bp = convert_bp( hp_to_bp( static_cast( i ) ) ).id(); + for( const bodypart_id &bp : get_all_body_parts( true ) ) { int ddam = vary ? dam * rng( 100 - vary, 100 ) / 100 : dam; int cut = 0; auto damage = damage_instance::physical( ddam, cut, 0 ); diff --git a/src/iexamine.cpp b/src/iexamine.cpp index 45bf621677cd1..6b77ea88b19ac 100644 --- a/src/iexamine.cpp +++ b/src/iexamine.cpp @@ -4804,8 +4804,7 @@ void iexamine::autodoc( player &p, const tripoint &examp ) case BONESETTING: { int broken_limbs_count = 0; - for( int i = 0; i < num_hp_parts; i++ ) { - const bodypart_id &part = convert_bp( player::hp_to_bp( static_cast( i ) ) ).id(); + for( const bodypart_id &part : patient.get_all_body_parts( true ) ) { const bool broken = patient.is_limb_broken( part ); effect &existing_effect = patient.get_effect( effect_mending, part->token ); // Skip part if not broken or already healed 50% @@ -4822,9 +4821,9 @@ void iexamine::autodoc( player &p, const tripoint &examp ) // TODO: fail here if unable to perform the action, i.e. can't wear more, trait mismatch. if( !patient.worn_with_flag( flag_SPLINT, part ) ) { item splint; - if( i == hp_arm_l || i == hp_arm_r ) { + if( part == bodypart_id( "arm_l" ) || part == bodypart_id( "arm_r" ) ) { splint = item( "arm_splint", 0 ); - } else if( i == hp_leg_l || i == hp_leg_r ) { + } else if( part == bodypart_id( "leg_l" ) || part == bodypart_id( "leg_r" ) ) { splint = item( "leg_splint", 0 ); } item &equipped_splint = patient.i_add( splint ); diff --git a/src/map_field.cpp b/src/map_field.cpp index c0c7b22bc8085..2897aed7badb9 100644 --- a/src/map_field.cpp +++ b/src/map_field.cpp @@ -1585,8 +1585,7 @@ void map::player_in_field( player &u ) // Small universal damage based on intensity, only if not electroproofed. if( !u.is_elec_immune() ) { int total_damage = 0; - for( size_t i = 0; i < num_hp_parts; i++ ) { - const bodypart_id bp = convert_bp( player::hp_to_bp( static_cast( i ) ) ).id(); + for( const bodypart_id &bp : u.get_all_body_parts( true ) ) { const int dmg = rng( 1, cur.get_field_intensity() ); total_damage += u.deal_damage( nullptr, bp, damage_instance( DT_ELECTRIC, dmg ) ).total_damage(); } diff --git a/src/npc.cpp b/src/npc.cpp index 27758bf59d125..573f8291453f6 100644 --- a/src/npc.cpp +++ b/src/npc.cpp @@ -1054,11 +1054,9 @@ bool npc::wear_if_wanted( const item &it, std::string &reason ) // Splints ignore limits, but only when being equipped on a broken part // TODO: Drop splints when healed if( it.has_flag( "SPLINT" ) ) { - for( int i = 0; i < num_hp_parts; i++ ) { - hp_part hpp = static_cast( i ); - body_part bp = player::hp_to_bp( hpp ); - if( is_limb_broken( convert_bp( bp ) ) && !has_effect( effect_mending, bp ) && - it.covers( convert_bp( bp ).id() ) ) { + for( const bodypart_id &bp : get_all_body_parts( true ) ) { + if( is_limb_broken( bp ) && !has_effect( effect_mending, bp->token ) && + it.covers( bp ) ) { reason = _( "Thanks, I'll wear that now." ); return !!wear_item( it, false ); } diff --git a/src/npctalk_funcs.cpp b/src/npctalk_funcs.cpp index 4b4a773920587..26433c91dfe81 100644 --- a/src/npctalk_funcs.cpp +++ b/src/npctalk_funcs.cpp @@ -554,17 +554,16 @@ void talk_function::give_equipment( npc &p ) void talk_function::give_aid( npc &p ) { p.add_effect( effect_currently_busy, 30_minutes ); - for( int i = 0; i < num_hp_parts; i++ ) { - const body_part bp_healed = player::hp_to_bp( static_cast( i ) ); - g->u.heal( convert_bp( bp_healed ), 5 * rng( 2, 5 ) ); - if( g->u.has_effect( effect_bite, bp_healed ) ) { - g->u.remove_effect( effect_bite, bp_healed ); + for( const bodypart_id &bp : g->u.get_all_body_parts( true ) ) { + g->u.heal( bp, 5 * rng( 2, 5 ) ); + if( g->u.has_effect( effect_bite, bp->token ) ) { + g->u.remove_effect( effect_bite, bp->token ); } - if( g->u.has_effect( effect_bleed, bp_healed ) ) { - g->u.remove_effect( effect_bleed, bp_healed ); + if( g->u.has_effect( effect_bleed, bp->token ) ) { + g->u.remove_effect( effect_bleed, bp->token ); } - if( g->u.has_effect( effect_infected, bp_healed ) ) { - g->u.remove_effect( effect_infected, bp_healed ); + if( g->u.has_effect( effect_infected, bp->token ) ) { + g->u.remove_effect( effect_infected, bp->token ); } } const int moves = to_moves( 100_minutes ); @@ -578,17 +577,16 @@ void talk_function::give_all_aid( npc &p ) give_aid( p ); for( npc &guy : g->all_npcs() ) { if( guy.is_walking_with() && rl_dist( guy.pos(), g->u.pos() ) < PICKUP_RANGE ) { - for( int i = 0; i < num_hp_parts; i++ ) { - const body_part bp_healed = player::hp_to_bp( static_cast( i ) ); - guy.heal( convert_bp( bp_healed ), 5 * rng( 2, 5 ) ); - if( guy.has_effect( effect_bite, bp_healed ) ) { - guy.remove_effect( effect_bite, bp_healed ); + for( const bodypart_id &bp : guy.get_all_body_parts( true ) ) { + guy.heal( bp, 5 * rng( 2, 5 ) ); + if( guy.has_effect( effect_bite, bp->token ) ) { + guy.remove_effect( effect_bite, bp->token ); } - if( guy.has_effect( effect_bleed, bp_healed ) ) { - guy.remove_effect( effect_bleed, bp_healed ); + if( guy.has_effect( effect_bleed, bp->token ) ) { + guy.remove_effect( effect_bleed, bp->token ); } - if( guy.has_effect( effect_infected, bp_healed ) ) { - guy.remove_effect( effect_infected, bp_healed ); + if( guy.has_effect( effect_infected, bp->token ) ) { + guy.remove_effect( effect_infected, bp->token ); } } } diff --git a/src/panels.cpp b/src/panels.cpp index 459f366f047ec..2764f9cd11e73 100644 --- a/src/panels.cpp +++ b/src/panels.cpp @@ -874,8 +874,7 @@ static void draw_limb_health( avatar &u, const catacurses::window &w, int limb_i } }; const bodypart_id bp = convert_bp( avatar::hp_to_bp( static_cast( limb_index ) ) ).id(); - if( u.is_limb_broken( bp.id() ) && ( limb_index >= hp_arm_l && - limb_index <= hp_leg_r ) ) { + if( u.is_limb_broken( bp ) && ( limb_index >= hp_arm_l && limb_index <= hp_leg_r ) ) { //Limb is broken std::string limb = "~~%~~"; nc_color color = c_light_red; diff --git a/src/player.cpp b/src/player.cpp index 0cd7bc9e53cfb..6695fb38616d5 100644 --- a/src/player.cpp +++ b/src/player.cpp @@ -1238,8 +1238,7 @@ int player::impact( const int force, const tripoint &p ) int total_dealt = 0; if( mod * effective_force >= 5 ) { - for( int i = 0; i < num_hp_parts; i++ ) { - const bodypart_id bp = convert_bp( hp_to_bp( static_cast( i ) ) ).id(); + for( const bodypart_id &bp : get_all_body_parts( true ) ) { const int bash = effective_force * rng( 60, 100 ) / 100; damage_instance di; di.add_damage( DT_BASH, bash, 0, armor_eff, mod ); From 9d5ccabeb17830632259930229bdff761f2012e3 Mon Sep 17 00:00:00 2001 From: Ununsept117 <63505222+Ununsept117@users.noreply.github.com> Date: Mon, 29 Jun 2020 21:31:27 +0200 Subject: [PATCH 010/420] Make clean water batch time saving more realistic Change batch time savings when crafting clean water from 80% at >4 units to 20% at >1 unit --- data/json/recipes/recipe_food.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/json/recipes/recipe_food.json b/data/json/recipes/recipe_food.json index 5ca27e103297e..d565e5812bad4 100644 --- a/data/json/recipes/recipe_food.json +++ b/data/json/recipes/recipe_food.json @@ -48,7 +48,7 @@ "skill_used": "cooking", "time": "5 m", "autolearn": true, - "batch_time_factors": [ 80, 4 ], + "batch_time_factors": [ 20, 1 ], "qualities": [ { "id": "BOIL", "level": 1 } ], "tools": [ [ [ "water_boiling_heat", 3, "LIST" ] ] ], "components": [ [ [ "water", 1 ] ] ] From 487d44d2a36af66ba295bef5d00a2e89df52c963 Mon Sep 17 00:00:00 2001 From: Fris0uman Date: Mon, 29 Jun 2020 22:32:15 +0200 Subject: [PATCH 011/420] test fix --- tests/creature_effect_test.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/creature_effect_test.cpp b/tests/creature_effect_test.cpp index 02b2e893f5fc4..714f405f7efb4 100644 --- a/tests/creature_effect_test.cpp +++ b/tests/creature_effect_test.cpp @@ -33,6 +33,7 @@ TEST_CASE( "character add_effect", "[creature][character][effect][add]" ) { avatar dummy; + dummy.set_body(); const efftype_id effect_bleed( "bleed" ); const efftype_id effect_grabbed( "grabbed" ); const body_part left_arm = bodypart_id( "arm_l" )->token; From e91c3c8a32de38025c386bbf5f013a2bfe7c5f00 Mon Sep 17 00:00:00 2001 From: faefux <49350286+faefux@users.noreply.github.com> Date: Tue, 30 Jun 2020 16:52:04 -0400 Subject: [PATCH 012/420] Update music.json (#41713) --- data/json/items/generic/music.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/json/items/generic/music.json b/data/json/items/generic/music.json index b77aeabc02625..4c9b508d056b5 100644 --- a/data/json/items/generic/music.json +++ b/data/json/items/generic/music.json @@ -39,7 +39,7 @@ "type": "GENERIC", "id": "microphone_xlr_generic", "name": { "str": "XLR microphone" }, - "description": "A typical microphone used to record or amplify voice. Comes with a clip. Has a 3-pin XLR connector on the bottom in order to connect to am amp.", + "description": "A typical microphone used to record or amplify voice. Comes with a clip. Has a 3-pin XLR connector on the bottom in order to connect to an amp.", "symbol": "i", "color": "dark_gray", "volume": "550 ml", From 67d661f11854666651c4cfdf5e26eb97b1534a46 Mon Sep 17 00:00:00 2001 From: Matias Fito Date: Tue, 30 Jun 2020 18:06:37 -0300 Subject: [PATCH 013/420] Fix MISSION_RANCH_SCAVENGER_1 required item Actual requested item is knife_spear_superior not knife_spear. fixes #41560 --- data/json/npcs/tacoma_ranch/NPC_ranch_scavenger.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/json/npcs/tacoma_ranch/NPC_ranch_scavenger.json b/data/json/npcs/tacoma_ranch/NPC_ranch_scavenger.json index d55784f1dbd87..d3ea627c516e5 100644 --- a/data/json/npcs/tacoma_ranch/NPC_ranch_scavenger.json +++ b/data/json/npcs/tacoma_ranch/NPC_ranch_scavenger.json @@ -49,7 +49,7 @@ "goal": "MGOAL_FIND_ITEM", "difficulty": 5, "value": 50000, - "item": "spear_knife", + "item": "spear_knife_superior", "count": 12, "origins": [ "ORIGIN_SECONDARY" ], "followup": "MISSION_RANCH_SCAVENGER_2", From 377bbbd0a12d38807042469c2803771dc1a2570d Mon Sep 17 00:00:00 2001 From: olanti-p Date: Wed, 1 Jul 2020 01:25:06 +0300 Subject: [PATCH 014/420] Fix copying in loops --- src/map.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/map.cpp b/src/map.cpp index 53e08c0711b5a..cb4ffcf43187a 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -3347,7 +3347,7 @@ void map::bash_vehicle( const tripoint &p, bash_params ¶ms ) void map::bash_field( const tripoint &p, bash_params ¶ms ) { - for( const std::pair &fd : field_at( p ) ) { + for( const std::pair &fd : field_at( p ) ) { if( fd.first->bash_info.str_min > -1 ) { params.did_bash = true; params.bashed_solid = true; // To prevent bashing furniture/vehicles @@ -3652,7 +3652,7 @@ void map::shoot( const tripoint &p, projectile &proj, const bool hit_items ) dam = std::max( 0.0f, dam ); // Check fields? - for( const std::pair &fd : field_at( p ) ) { + for( const std::pair &fd : field_at( p ) ) { if( fd.first->bash_info.str_min > 0 ) { if( inc ) { add_field( p, fd_fire, fd.second.get_field_intensity() - 1 ); From e3f2df2c142c4c17c91c8f753aa92ea2be1e02c2 Mon Sep 17 00:00:00 2001 From: nexusmrsep <39925111+nexusmrsep@users.noreply.github.com> Date: Wed, 1 Jul 2020 04:02:15 +0200 Subject: [PATCH 015/420] Workout and exercise (#41409) * new workout equpment mapgen distribution * serialize & deserialize variables --- .../furniture-recreation.json | 84 +++++- .../json/mapgen/basement/basement_bionic.json | 8 +- .../urban_14_dense_house_mart_food.json | 7 +- data/json/mapgen/fire_station.json | 2 +- data/json/mapgen/gym.json | 8 +- data/json/mapgen/house/house15.json | 2 +- data/json/mapgen/lab/lab_rooms.json | 6 +- data/json/mapgen/mall/mall_second_floor.json | 13 +- data/json/mapgen/nested/basement_nested.json | 32 ++- data/json/mapgen/police_station.json | 4 +- data/json/mapgen/robofachq_static.json | 8 +- data/json/mapgen/sports_store.json | 4 +- data/json/mapgen/stadium_football.json | 4 +- .../mapgen_palettes/hotel_tower_palette.json | 2 +- .../house_general_palette.json | 2 +- data/json/mapgen_palettes/lmoe.json | 2 +- data/json/mapgen_palettes/mansion.json | 8 +- .../necropolis/necropolis_b2.json | 4 +- .../necropolis/necropolis_b3.json | 4 +- data/json/player_activities.json | 28 ++ .../worldgen/gas/s_gas_b21.json | 2 +- .../Magiclysm/worldgen/magic_academy.json | 8 +- .../bandit_tower/bandit_tower_2trc_04.json | 2 +- data/mods/More_Locations/tpalettes.json | 4 +- .../palettes/national_guard_camp.json | 2 +- .../palettes/national_guard_camp_b.json | 2 +- .../building_jsons/urban_20_duplex.json | 2 +- .../building_jsons/urban_33_hotel.json | 2 +- data/raw/keybindings.json | 6 + doc/JSON_FLAGS.md | 2 + src/action.cpp | 3 + src/action.h | 2 + src/activity_actor.cpp | 241 ++++++++++++++++++ src/activity_actor.h | 41 +++ src/game.cpp | 1 + src/handle_action.cpp | 6 + src/iexamine.cpp | 12 +- src/iexamine.h | 1 + src/player_activity.cpp | 23 +- src/player_activity.h | 5 + 40 files changed, 544 insertions(+), 55 deletions(-) diff --git a/data/json/furniture_and_terrain/furniture-recreation.json b/data/json/furniture_and_terrain/furniture-recreation.json index 87ae45ae4cd6b..58ae31a1257f8 100644 --- a/data/json/furniture_and_terrain/furniture-recreation.json +++ b/data/json/furniture_and_terrain/furniture-recreation.json @@ -4,12 +4,13 @@ "id": "f_exercise", "name": "exercise machine", "symbol": "T", - "description": "A heavy set of weightlifting equipment for strength training, with a pair of heavy weights affixed to opposite ends of a sturdy pipe. The weights are huge, and using them without a spotter would be a good way to seriously injure yourself.", + "description": "A heavy set of weightlifting equipment for strength training, with a pair of heavy weights affixed to opposite ends of a sturdy pipe. You can adjust the set by hand-picking the weights you wish to use.", "color": "dark_gray", "move_cost_mod": 1, "coverage": 35, "required_str": 8, - "flags": [ "TRANSPARENT", "MINEABLE" ], + "flags": [ "TRANSPARENT", "MINEABLE", "WORKOUT_ARMS" ], + "examine_action": "workout", "deconstruct": { "items": [ { "item": "pipe", "count": 1 }, @@ -250,6 +251,47 @@ ] } }, + { + "type": "furniture", + "id": "f_ergometer_mechanical", + "name": "mechanical ergometer", + "description": "An exercise machine with a set of handles and plates meant to emulate rowing a boat. This an older model with mechanical resistance adjustments, but it works without power.", + "symbol": "5", + "color": "dark_gray", + "move_cost_mod": 2, + "required_str": 8, + "looks_like": "f_ergometer", + "flags": [ "BLOCKSDOOR", "TRANSPARENT", "MOUNTABLE", "WORKOUT_ARMS", "WORKOUT_LEGS" ], + "deconstruct": { + "items": [ + { "item": "foot_crank", "count": [ 1, 1 ] }, + { "item": "plastic_chunk", "count": [ 8, 10 ] }, + { "item": "scrap", "count": [ 2, 4 ] }, + { "item": "chain", "count": 1 }, + { "item": "pipe", "count": [ 4, 5 ] }, + { "item": "saddle", "count": [ 1, 1 ] }, + { "item": "wheel_small", "count": [ 1, 1 ] }, + { "item": "nail", "charges": [ 6, 8 ] } + ] + }, + "examine_action": "workout", + "bash": { + "str_min": 6, + "str_max": 25, + "sound": "smash!", + "sound_fail": "thump!", + "items": [ + { "item": "foot_crank", "prob": 50 }, + { "item": "plastic_chunk", "count": [ 4, 6 ] }, + { "item": "scrap", "count": [ 0, 2 ] }, + { "item": "chain", "prob": 50 }, + { "item": "pipe", "count": [ 0, 4 ] }, + { "item": "saddle", "prob": 50 }, + { "item": "wheel_small", "prob": 50 }, + { "item": "nail", "charges": [ 2, 6 ] } + ] + } + }, { "type": "furniture", "id": "f_treadmill", @@ -267,6 +309,7 @@ { "item": "pipe", "count": [ 4, 3 ] }, { "item": "small_lcd_screen", "count": 1 }, { "item": "RAM", "count": 1 }, + { "item": "motor_tiny", "count": 1 }, { "item": "nail", "charges": [ 6, 8 ] } ] }, @@ -281,6 +324,40 @@ { "item": "pipe", "count": [ 0, 4 ] }, { "item": "small_lcd_screen", "prob": 50 }, { "item": "RAM", "count": [ 0, 1 ] }, + { "item": "motor_tiny", "count": [ 0, 1 ] }, + { "item": "nail", "charges": [ 2, 6 ] } + ] + } + }, + { + "type": "furniture", + "id": "f_treadmill_mechanical", + "name": "gravity treadmill", + "description": "A gravity driven conveyor belt with a mechanical control panel for running in place. Conveyor belt is positioned in a steep, but adjustable incline, so it slides back under your weight.", + "symbol": "L", + "color": "dark_gray", + "move_cost_mod": 1, + "required_str": 12, + "looks_like": "f_treadmill", + "flags": [ "BLOCKSDOOR", "TRANSPARENT", "MOUNTABLE", "WORKOUT_LEGS" ], + "deconstruct": { + "items": [ + { "item": "plastic_chunk", "count": [ 10, 14 ] }, + { "item": "scrap", "count": [ 2, 10 ] }, + { "item": "pipe", "count": [ 4, 6 ] }, + { "item": "nail", "charges": [ 6, 8 ] } + ] + }, + "examine_action": "workout", + "bash": { + "str_min": 12, + "str_max": 40, + "sound": "smash!", + "sound_fail": "thump!", + "items": [ + { "item": "plastic_chunk", "count": [ 4, 10 ] }, + { "item": "scrap", "count": [ 0, 5 ] }, + { "item": "pipe", "count": [ 0, 4 ] }, { "item": "nail", "charges": [ 2, 6 ] } ] } @@ -297,7 +374,8 @@ "move_cost_mod": -1, "coverage": 65, "required_str": 10, - "flags": [ "BASHABLE", "BLOCKSDOOR", "PLACE_ITEM", "ORGANIC" ], + "flags": [ "BASHABLE", "BLOCKSDOOR", "PLACE_ITEM", "ORGANIC", "WORKOUT_ARMS" ], + "examine_action": "workout", "bash": { "str_min": 15, "str_max": 20, diff --git a/data/json/mapgen/basement/basement_bionic.json b/data/json/mapgen/basement/basement_bionic.json index 729e4d713465f..c89d9178d2f22 100644 --- a/data/json/mapgen/basement/basement_bionic.json +++ b/data/json/mapgen/basement/basement_bionic.json @@ -37,8 +37,8 @@ "furniture": { "}": "f_pinball_machine", "*": "f_shower", - "!": "f_ergometer", - "@": "f_treadmill", + "!": [ "f_ergometer", "f_ergometer_mechanical" ], + "@": [ "f_treadmill", "f_treadmill_mechanical" ], "^": "f_exercise", "%": "f_floor_canvas", "C": "f_cupboard", @@ -113,8 +113,8 @@ "furniture": { "}": "f_pinball_machine", "*": "f_shower", - "!": "f_ergometer", - "@": "f_treadmill", + "!": [ "f_ergometer", "f_ergometer_mechanical" ], + "@": [ "f_treadmill", "f_treadmill_mechanical" ], "^": "f_exercise", "%": "f_floor_canvas", "C": "f_cupboard", diff --git a/data/json/mapgen/city_blocks/urban_14_dense_house_mart_food.json b/data/json/mapgen/city_blocks/urban_14_dense_house_mart_food.json index 236f7a1fcc508..9fa7b68530de2 100644 --- a/data/json/mapgen/city_blocks/urban_14_dense_house_mart_food.json +++ b/data/json/mapgen/city_blocks/urban_14_dense_house_mart_food.json @@ -53,7 +53,12 @@ ">": "t_stairs_down", "-": "t_thconc_floor" }, - "furniture": { "!": "f_counter", "]": "f_treadmill", ")": "f_exercise", "}": "f_ergometer" }, + "furniture": { + "!": "f_counter", + "]": [ "f_treadmill", "f_treadmill_mechanical" ], + ")": "f_exercise", + "}": [ "f_ergometer", "f_ergometer_mechanical" ] + }, "vendingmachines": { "[": { "item_group": "vending_drink" }, "(": { "item_group": "vending_food" } }, "set": [ { "point": "trap", "id": "tr_rollmat", "x": [ 9, 11 ], "y": 11 }, diff --git a/data/json/mapgen/fire_station.json b/data/json/mapgen/fire_station.json index cc655746c8104..67f08c0d32318 100644 --- a/data/json/mapgen/fire_station.json +++ b/data/json/mapgen/fire_station.json @@ -195,7 +195,7 @@ "furniture": { "#": "f_table", "&": "f_sink", - "5": "f_ergometer", + "5": [ "f_ergometer", "f_ergometer_mechanical" ], "B": "f_bookcase", "E": "f_exercise", "F": "f_fridge", diff --git a/data/json/mapgen/gym.json b/data/json/mapgen/gym.json index 0bf565cf27fb2..47336aeb21b99 100644 --- a/data/json/mapgen/gym.json +++ b/data/json/mapgen/gym.json @@ -47,10 +47,10 @@ "4": "t_gutter_downspout" }, "furniture": { - "!": "f_ergometer", + "!": [ "f_ergometer", "f_ergometer_mechanical" ], "#": "f_counter", "*": "f_shower", - "@": "f_treadmill", + "@": [ "f_treadmill", "f_treadmill_mechanical" ], "O": "f_locker", "V": "f_exercise", "a": "f_stool", @@ -176,13 +176,13 @@ "<": "t_stairs_up" }, "furniture": { - "!": "f_ergometer", + "!": [ "f_ergometer", "f_ergometer_mechanical" ], "{": "f_bigmirror", "#": "f_counter", "H": "f_vending_c", "&": "f_counter", "*": "f_shower", - "@": "f_treadmill", + "@": [ "f_treadmill", "f_treadmill_mechanical" ], "O": "f_locker", "V": "f_exercise", "C": "f_bench", diff --git a/data/json/mapgen/house/house15.json b/data/json/mapgen/house/house15.json index ee74d8d46bb55..952d3f72e4d93 100644 --- a/data/json/mapgen/house/house15.json +++ b/data/json/mapgen/house/house15.json @@ -34,7 +34,7 @@ ], "palettes": [ "standard_domestic_palette" ], "terrain": { "%": "t_region_shrub_fruit", "!": "t_region_groundcover_urban", "'": "t_concrete" }, - "furniture": { "!": "f_bluebell", "$": "f_treadmill" }, + "furniture": { "!": "f_bluebell", "$": [ "f_treadmill", "f_treadmill_mechanical" ] }, "place_loot": [ { "group": "livingroom", "x": [ 7, 16 ], "y": [ 7, 9 ], "chance": 90, "repeat": [ 1, 6 ] } ], "place_monsters": [ { "monster": "GROUP_ZOMBIE", "x": [ 1, 22 ], "y": [ 2, 21 ], "chance": 5 } ] } diff --git a/data/json/mapgen/lab/lab_rooms.json b/data/json/mapgen/lab/lab_rooms.json index 99b271c604cec..eb218c8ba97b0 100644 --- a/data/json/mapgen/lab/lab_rooms.json +++ b/data/json/mapgen/lab/lab_rooms.json @@ -986,7 +986,11 @@ ], "palettes": [ "lab_palette" ], "terrain": { "%": "t_thconc_floor" }, - "furniture": { "%": "f_treadmill", "5": [ "f_ergometer", "f_exercise" ], "`": "f_canvas_floor" }, + "furniture": { + "%": [ "f_treadmill", "f_treadmill_mechanical" ], + "5": [ "f_ergometer", "f_ergometer_mechanical", "f_exercise" ], + "`": "f_canvas_floor" + }, "items": { ".": { "item": "clutter_gym", "chance": 3 }, "l": { "item": "locker_gym", "chance": 30 }, diff --git a/data/json/mapgen/mall/mall_second_floor.json b/data/json/mapgen/mall/mall_second_floor.json index 6681f69e49b1a..66da84c6b67a1 100644 --- a/data/json/mapgen/mall/mall_second_floor.json +++ b/data/json/mapgen/mall/mall_second_floor.json @@ -1144,7 +1144,14 @@ "S": "t_thconc_floor", "I": "t_thconc_floor" }, - "furniture": { "%": "f_counter", "*": "f_crate_c", "!": "f_beaded_door", "p": "f_desk", "]": "f_ergometer", "0": "f_exercise" }, + "furniture": { + "%": "f_counter", + "*": "f_crate_c", + "!": "f_beaded_door", + "p": "f_desk", + "]": [ "f_ergometer", "f_ergometer_mechanical" ], + "0": "f_exercise" + }, "items": { "K": { "item": "pottery", "chance": 30, "repeat": [ 2, 4 ] }, "J": { "item": "office", "chance": 20 }, @@ -1220,8 +1227,8 @@ "I": "t_thconc_floor" }, "furniture": { - "*": "f_treadmill", - "]": "f_ergometer", + "*": [ "f_treadmill", "f_treadmill_mechanical" ], + "]": [ "f_ergometer", "f_ergometer_mechanical" ], "!": "f_exercise", "&": "f_counter", "%": "f_shower", diff --git a/data/json/mapgen/nested/basement_nested.json b/data/json/mapgen/nested/basement_nested.json index eed3e4d43c684..f00bf35ad6a8b 100644 --- a/data/json/mapgen/nested/basement_nested.json +++ b/data/json/mapgen/nested/basement_nested.json @@ -2760,7 +2760,13 @@ "O": "t_carpet_purple", "=": "t_carpet_purple" }, - "furniture": { "!": "f_ergometer", "@": "f_treadmill", "V": "f_exercise", "c": "f_bench", "B": "f_bigmirror" }, + "furniture": { + "!": [ "f_ergometer", "f_ergometer_mechanical" ], + "@": [ "f_treadmill", "f_treadmill_mechanical" ], + "V": "f_exercise", + "c": "f_bench", + "B": "f_bigmirror" + }, "traps": { "=": "tr_rollmat" }, "nested": { "1": { "chunks": [ [ "5x5_sauna_N", 30 ], [ "5x5_pool", 10 ], [ "5x5_gym_N", 60 ] ] } } } @@ -2791,7 +2797,13 @@ "O": "t_carpet_green", "=": "t_carpet_green" }, - "furniture": { "!": "f_ergometer", "@": "f_treadmill", "V": "f_exercise", "c": "f_bench", "B": "f_bigmirror" }, + "furniture": { + "!": [ "f_ergometer", "f_ergometer_mechanical" ], + "@": [ "f_treadmill", "f_treadmill_mechanical" ], + "V": "f_exercise", + "c": "f_bench", + "B": "f_bigmirror" + }, "traps": { "=": "tr_rollmat" }, "nested": { "1": { "chunks": [ [ "5x5_sauna_S", 30 ], [ "5x5_pool", 10 ], [ "5x5_gym_S", 60 ] ] } } } @@ -2822,7 +2834,13 @@ "O": "t_carpet_yellow", "=": "t_carpet_yellow" }, - "furniture": { "!": "f_ergometer", "@": "f_treadmill", "V": "f_exercise", "c": "f_bench", "B": "f_bigmirror" }, + "furniture": { + "!": [ "f_ergometer", "f_ergometer_mechanical" ], + "@": [ "f_treadmill", "f_treadmill_mechanical" ], + "V": "f_exercise", + "c": "f_bench", + "B": "f_bigmirror" + }, "traps": { "=": "tr_rollmat" }, "nested": { "1": { "chunks": [ [ "5x5_sauna_E", 30 ], [ "5x5_pool", 10 ], [ "5x5_gym_E", 60 ] ] } } } @@ -2853,7 +2871,13 @@ "O": "t_carpet_red", "=": "t_carpet_red" }, - "furniture": { "!": "f_ergometer", "@": "f_treadmill", "V": "f_exercise", "c": "f_bench", "B": "f_bigmirror" }, + "furniture": { + "!": [ "f_ergometer", "f_ergometer_mechanical" ], + "@": [ "f_treadmill", "f_treadmill_mechanical" ], + "V": "f_exercise", + "c": "f_bench", + "B": "f_bigmirror" + }, "traps": { "=": "tr_rollmat" }, "nested": { "1": { "chunks": [ [ "5x5_sauna_W", 30 ], [ "5x5_pool", 10 ], [ "5x5_gym_W", 60 ] ] } } } diff --git a/data/json/mapgen/police_station.json b/data/json/mapgen/police_station.json index 06dbdae88ad93..c5084d364a98a 100644 --- a/data/json/mapgen/police_station.json +++ b/data/json/mapgen/police_station.json @@ -573,10 +573,10 @@ "5": "t_gutter_drop" }, "furniture": { - "&": "f_ergometer", + "&": [ "f_ergometer", "f_ergometer_mechanical" ], ":": "f_exercise", "b": "f_bench", - "=": "f_treadmill", + "=": [ "f_treadmill", "f_treadmill_mechanical" ], "A": "f_bigmirror", "s": "f_shower", "S": "f_sink", diff --git a/data/json/mapgen/robofachq_static.json b/data/json/mapgen/robofachq_static.json index 3c7ca357af165..662312a1303ad 100644 --- a/data/json/mapgen/robofachq_static.json +++ b/data/json/mapgen/robofachq_static.json @@ -294,8 +294,8 @@ "S": "f_table", "A": "f_canvas_wall", "%": "f_canvas_door", - "E": "f_ergometer", - "T": "f_treadmill", + "E": [ "f_ergometer", "f_ergometer_mechanical" ], + "T": [ "f_treadmill", "f_treadmill_mechanical" ], "X": "f_exercise" }, "sealed_item": { @@ -415,8 +415,8 @@ "%": "f_canvas_door", "H": "f_armchair", "M": "f_server", - "E": "f_ergometer", - "T": "f_treadmill", + "E": [ "f_ergometer", "f_ergometer_mechanical" ], + "T": [ "f_treadmill", "f_treadmill_mechanical" ], "X": "f_exercise" }, "items": { diff --git a/data/json/mapgen/sports_store.json b/data/json/mapgen/sports_store.json index 35be3e5075581..ffaa665dc4d73 100644 --- a/data/json/mapgen/sports_store.json +++ b/data/json/mapgen/sports_store.json @@ -48,8 +48,8 @@ "furniture": { "#": "f_counter", "h": "f_stool", - "t": "f_treadmill", - "e": "f_ergometer", + "t": [ "f_treadmill", "f_treadmill_mechanical" ], + "e": [ "f_ergometer", "f_ergometer_mechanical" ], "p": "f_floor_canvas", "m": "f_exercise", "%": "f_sink", diff --git a/data/json/mapgen/stadium_football.json b/data/json/mapgen/stadium_football.json index 9d3c32abad4ee..357b6b079dbce 100644 --- a/data/json/mapgen/stadium_football.json +++ b/data/json/mapgen/stadium_football.json @@ -1011,10 +1011,10 @@ "+": "f_null", "-": "f_null", ".": "f_null", - "5": "f_ergometer", + "5": [ "f_ergometer", "f_ergometer_mechanical" ], "C": "f_counter", "D": "f_null", - "L": "f_treadmill", + "L": [ "f_treadmill", "f_treadmill_mechanical" ], "S": "f_sink", "T": "f_trashcan", "_": "f_null", diff --git a/data/json/mapgen_palettes/hotel_tower_palette.json b/data/json/mapgen_palettes/hotel_tower_palette.json index 6a0b5d0f93b39..2a55e64166211 100644 --- a/data/json/mapgen_palettes/hotel_tower_palette.json +++ b/data/json/mapgen_palettes/hotel_tower_palette.json @@ -18,7 +18,7 @@ "S": "f_sink", "&": "f_toilet", "b": "f_bathtub", - "E": [ "f_exercise", "f_ergometer", "f_treadmill" ], + "E": [ "f_exercise", "f_ergometer", "f_ergometer_mechanical", "f_treadmill", "f_treadmill_mechanical" ], "e": "f_fridge", "l": "f_locker", "W": "f_washer", diff --git a/data/json/mapgen_palettes/house_general_palette.json b/data/json/mapgen_palettes/house_general_palette.json index d5906dfd9d8e3..0078a6b98f6a0 100644 --- a/data/json/mapgen_palettes/house_general_palette.json +++ b/data/json/mapgen_palettes/house_general_palette.json @@ -212,7 +212,7 @@ "u": "t_thconc_floor", "X": "t_thconc_floor" }, - "furniture": { "c": "f_exercise", "u": "f_ergometer", "X": "f_punching_bag" }, + "furniture": { "c": "f_exercise", "u": [ "f_ergometer", "f_ergometer_mechanical" ], "X": "f_punching_bag" }, "monsters": { "!": [ { "monster": "GROUP_ROACH", "chance": 70 }, { "monster": "GROUP_ZOMBIE", "chance": 100 } ], ".": [ { "monster": "GROUP_ROACH", "chance": 80 }, { "monster": "GROUP_ZOMBIE", "chance": 60 } ] diff --git a/data/json/mapgen_palettes/lmoe.json b/data/json/mapgen_palettes/lmoe.json index 28bc0f7a493ad..90c3cd16938ce 100644 --- a/data/json/mapgen_palettes/lmoe.json +++ b/data/json/mapgen_palettes/lmoe.json @@ -28,7 +28,7 @@ "C": "f_cupboard", "d": "f_dresser", "D": "f_desk", - "e": "f_ergometer", + "e": "f_ergometer_mechanical", "E": "f_exercise", "F": "f_fridge", "f": "f_sofa", diff --git a/data/json/mapgen_palettes/mansion.json b/data/json/mapgen_palettes/mansion.json index 38d71fed78598..f81b9ffd22891 100644 --- a/data/json/mapgen_palettes/mansion.json +++ b/data/json/mapgen_palettes/mansion.json @@ -24,8 +24,8 @@ "D": "f_desk", "F": "f_fridge", "G": "f_dryer", - "H": "f_treadmill", - "I": "f_ergometer", + "H": [ "f_treadmill", "f_treadmill_mechanical" ], + "I": [ "f_ergometer", "f_ergometer_mechanical" ], "J": "f_bathtub", "K": [ [ "f_cardboard_box", 5 ], "f_crate_c", "f_crate_o" ], "L": [ [ "f_cardboard_box", 5 ], "f_crate_c", "f_crate_o" ], @@ -46,8 +46,8 @@ "e": "f_pool_table", "f": "f_fridge", "g": "f_dryer", - "h": "f_treadmill", - "i": "f_ergometer", + "h": [ "f_treadmill", "f_treadmill_mechanical" ], + "i": [ "f_ergometer", "f_ergometer_mechanical" ], "j": "f_shower", "l": "f_bookcase", "m": "f_locker", diff --git a/data/json/mapgen_palettes/necropolis/necropolis_b2.json b/data/json/mapgen_palettes/necropolis/necropolis_b2.json index 623867b26765b..35a0329c92f4b 100644 --- a/data/json/mapgen_palettes/necropolis/necropolis_b2.json +++ b/data/json/mapgen_palettes/necropolis/necropolis_b2.json @@ -6,9 +6,9 @@ "?": "f_sofa", "@": "f_bed", "B": "f_bathtub", - "C": "f_treadmill", + "C": [ "f_treadmill", "f_treadmill_mechanical" ], "D": "f_trashcan", - "J": "f_ergometer", + "J": [ "f_ergometer", "f_ergometer_mechanical" ], "L": "f_locker", "N": "f_robotic_arm", "O": "f_oven", diff --git a/data/json/mapgen_palettes/necropolis/necropolis_b3.json b/data/json/mapgen_palettes/necropolis/necropolis_b3.json index 1a28f41637278..4184ef4853819 100644 --- a/data/json/mapgen_palettes/necropolis/necropolis_b3.json +++ b/data/json/mapgen_palettes/necropolis/necropolis_b3.json @@ -6,9 +6,9 @@ "?": "f_sofa", "@": "f_bed", "B": "f_bathtub", - "C": "f_treadmill", + "C": [ "f_treadmill", "f_treadmill_mechanical" ], "D": "f_trashcan", - "J": "f_ergometer", + "J": [ "f_ergometer", "f_ergometer_mechanical" ], "L": "f_locker", "N": "f_robotic_arm", "O": "f_oven", diff --git a/data/json/player_activities.json b/data/json/player_activities.json index 9db49f5ac2afa..f66090b8e2350 100644 --- a/data/json/player_activities.json +++ b/data/json/player_activities.json @@ -866,5 +866,33 @@ "activity_level": "NO_EXERCISE", "verb": "consuming", "based_on": "time" + }, + { + "id": "ACT_WORKOUT_HARD", + "type": "activity_type", + "activity_level": "EXTRA_EXERCISE", + "verb": { "ctxt": "training", "str": "working out" }, + "based_on": "time" + }, + { + "id": "ACT_WORKOUT_ACTIVE", + "type": "activity_type", + "activity_level": "ACTIVE_EXERCISE", + "verb": { "ctxt": "training", "str": "working out" }, + "based_on": "time" + }, + { + "id": "ACT_WORKOUT_MODERATE", + "type": "activity_type", + "activity_level": "MODERATE_EXERCISE", + "verb": { "ctxt": "training", "str": "working out" }, + "based_on": "time" + }, + { + "id": "ACT_WORKOUT_LIGHT", + "type": "activity_type", + "activity_level": "LIGHT_EXERCISE", + "verb": { "ctxt": "training", "str": "working out" }, + "based_on": "time" } ] diff --git a/data/mods/Fuji_Structures/worldgen/gas/s_gas_b21.json b/data/mods/Fuji_Structures/worldgen/gas/s_gas_b21.json index 7e3544246d05f..d73f21557f905 100644 --- a/data/mods/Fuji_Structures/worldgen/gas/s_gas_b21.json +++ b/data/mods/Fuji_Structures/worldgen/gas/s_gas_b21.json @@ -67,7 +67,7 @@ "i": "f_locker", "j": "f_bench", "k": "f_locker", - "l": "f_treadmill", + "l": [ "f_treadmill", "f_treadmill_mechanical" ], "m": "f_shower", "n": "f_toilet", "p": "f_sink", diff --git a/data/mods/Magiclysm/worldgen/magic_academy.json b/data/mods/Magiclysm/worldgen/magic_academy.json index 5fe37bae294f2..124cc479cd113 100644 --- a/data/mods/Magiclysm/worldgen/magic_academy.json +++ b/data/mods/Magiclysm/worldgen/magic_academy.json @@ -115,7 +115,13 @@ "palettes": [ "standard_domestic_palette" ], "traps": { "=": "tr_rollmat" }, "terrain": { " ": "t_thconc_floor", "`": "t_rock", "]": "t_door_glass_green_c", "#": "t_rock_blue", "~": "t_water_pool" }, - "furniture": { ")": "f_beaded_door", "}": "f_huge_mana_crystal", "!": "f_ergometer", "$": "f_treadmill", "%": "f_exercise" }, + "furniture": { + ")": "f_beaded_door", + "}": "f_huge_mana_crystal", + "!": [ "f_ergometer", "f_ergometer_mechanical" ], + "$": [ "f_treadmill", "f_treadmill_mechanical" ], + "%": "f_exercise" + }, "place_loot": [ { "item": "television", "x": 8, "y": 1, "chance": 100 }, { "item": "stereo", "x": 7, "y": 1, "chance": 100 } ], "items": { "q": [ diff --git a/data/mods/More_Locations/bandit_tower/bandit_tower_2trc_04.json b/data/mods/More_Locations/bandit_tower/bandit_tower_2trc_04.json index e3b27dfb5eadd..4dcb0b10af242 100644 --- a/data/mods/More_Locations/bandit_tower/bandit_tower_2trc_04.json +++ b/data/mods/More_Locations/bandit_tower/bandit_tower_2trc_04.json @@ -36,7 +36,7 @@ "rotation": 1, "palettes": [ "tri_tower", "upper_level", "bandit_tower_stuff" ], "terrain": { "5": "t_pavement", "8": "t_bridge", "y": "t_wall_wood" }, - "furniture": { "Y": "f_exercise", "J": "f_treadmill" }, + "furniture": { "Y": "f_exercise", "J": [ "f_treadmill", "f_treadmill_mechanical" ] }, "items": { "l": { "item": "sports", "chance": 20 }, "x": { "item": "allsporting", "chance": 20 } }, "npcs": { "@": { "class": "thug" } } } diff --git a/data/mods/More_Locations/tpalettes.json b/data/mods/More_Locations/tpalettes.json index 064bb40945c8f..d1257d5566d95 100644 --- a/data/mods/More_Locations/tpalettes.json +++ b/data/mods/More_Locations/tpalettes.json @@ -120,7 +120,7 @@ "P": "f_piano", "W": "f_wardrobe", "Y": "f_statue", - "Z": [ "f_treadmill", "f_exercise" ] + "Z": [ "f_treadmill", "f_treadmill_mechanical", "f_exercise" ] }, "items": { "$": { "item": "art", "chance": 75 }, @@ -642,7 +642,7 @@ "3": "f_dryer", "5": "f_pool_table", "7": "f_exercise", - "8": "f_treadmill", + "8": [ "f_treadmill", "f_treadmill_mechanical" ], "9": "f_pinball_machine", "0": "f_arcade_machine" } diff --git a/data/mods/National_Guard_Camp/palettes/national_guard_camp.json b/data/mods/National_Guard_Camp/palettes/national_guard_camp.json index cc2263d43e805..eb4d7b3a4d59e 100644 --- a/data/mods/National_Guard_Camp/palettes/national_guard_camp.json +++ b/data/mods/National_Guard_Camp/palettes/national_guard_camp.json @@ -32,7 +32,7 @@ "t": "f_toilet", "u": "f_shower", "v": "f_oven", - "y": "f_treadmill" + "y": [ "f_treadmill", "f_treadmill_mechanical" ] }, "terrain": { " ": "t_thconc_floor", diff --git a/data/mods/National_Guard_Camp/palettes/national_guard_camp_b.json b/data/mods/National_Guard_Camp/palettes/national_guard_camp_b.json index e9e81b21b3ff2..83d7b77b4e9a8 100644 --- a/data/mods/National_Guard_Camp/palettes/national_guard_camp_b.json +++ b/data/mods/National_Guard_Camp/palettes/national_guard_camp_b.json @@ -31,7 +31,7 @@ "t": "f_toilet", "u": "f_shower", "v": "f_oven", - "y": "f_treadmill", + "y": [ "f_treadmill", "f_treadmill_mechanical" ], "z": "f_trashcan" }, "terrain": { diff --git a/data/mods/Urban_Development/building_jsons/urban_20_duplex.json b/data/mods/Urban_Development/building_jsons/urban_20_duplex.json index ab03813b0dcf8..95e6333f27ef2 100644 --- a/data/mods/Urban_Development/building_jsons/urban_20_duplex.json +++ b/data/mods/Urban_Development/building_jsons/urban_20_duplex.json @@ -78,7 +78,7 @@ ], "palettes": [ "acidia_residential_commercial_palette" ], "terrain": { "7": "t_strconc_floor" }, - "furniture": { "7": "f_treadmill" }, + "furniture": { "7": [ "f_treadmill", "f_treadmill_mechanical" ] }, "items": { "'": { "item": "cleaning", "chance": 2 }, "D": { "item": "allclothes", "chance": 60 }, diff --git a/data/mods/Urban_Development/building_jsons/urban_33_hotel.json b/data/mods/Urban_Development/building_jsons/urban_33_hotel.json index e42a6a3a02bfa..9173d3810cf80 100644 --- a/data/mods/Urban_Development/building_jsons/urban_33_hotel.json +++ b/data/mods/Urban_Development/building_jsons/urban_33_hotel.json @@ -515,7 +515,7 @@ ], "palettes": [ "acidia_commercial_palette" ], "terrain": { " ": "t_linoleum_white", "!": "t_linoleum_white", "2": "t_linoleum_white" }, - "furniture": { "!": "f_ergometer", "2": "f_treadmill" }, + "furniture": { "!": [ "f_ergometer", "f_ergometer_mechanical" ], "2": [ "f_treadmill", "f_treadmill_mechanical" ] }, "items": { " ": { "item": "traveler", "chance": 1 }, "3": { "item": "traveler", "chance": 1 } }, "place_monsters": [ { "monster": "GROUP_ZOMBIE", "x": [ 2, 11 ], "y": [ 1, 18 ], "density": 0.4 } ] } diff --git a/data/raw/keybindings.json b/data/raw/keybindings.json index 5a0d798c2df3e..5c022b87163ae 100644 --- a/data/raw/keybindings.json +++ b/data/raw/keybindings.json @@ -1855,6 +1855,12 @@ "id": "sleep", "bindings": [ { "input_method": "keyboard", "key": "$" } ] }, + { + "type": "keybinding", + "name": "Workout", + "category": "DEFAULTMODE", + "id": "workout" + }, { "type": "keybinding", "name": "Control Vehicle", diff --git a/doc/JSON_FLAGS.md b/doc/JSON_FLAGS.md index c12ac74a150b0..9de5fccbca008 100644 --- a/doc/JSON_FLAGS.md +++ b/doc/JSON_FLAGS.md @@ -596,6 +596,8 @@ List of known flags, used in both `terrain.json` and `furniture.json`. - ```UNSTABLE``` Walking here cause the bouldering effect on the character. - ```USABLE_FIRE``` This terrain or furniture counts as a nearby fire for crafting. - ```WALL``` This terrain is an upright obstacle. Used for fungal conversion, and also implies `CONNECT_TO_WALL`. +- ```WORKOUT_LEGS``` This furniture is for training your legs. Needed for checks like `is_limb_broken()`. +- ```WORKOUT_ARMS``` This furniture is for training your arms. Needed for checks like `is_limb_broken()`. ### Examine Actions diff --git a/src/action.cpp b/src/action.cpp index 4c0eec381e9d2..55089b097c265 100644 --- a/src/action.cpp +++ b/src/action.cpp @@ -296,6 +296,8 @@ std::string action_ident( action_id act ) return "ignore_enemy"; case ACTION_WHITELIST_ENEMY: return "whitelist_enemy"; + case ACTION_WORKOUT: + return "workout"; case ACTION_SAVE: return "save"; case ACTION_QUICKSAVE: @@ -928,6 +930,7 @@ action_id handle_action_menu() } else if( category == _( "Misc" ) ) { REGISTER_ACTION( ACTION_WAIT ); REGISTER_ACTION( ACTION_SLEEP ); + REGISTER_ACTION( ACTION_WORKOUT ); REGISTER_ACTION( ACTION_BIONICS ); REGISTER_ACTION( ACTION_MUTATIONS ); REGISTER_ACTION( ACTION_CONTROL_VEHICLE ); diff --git a/src/action.h b/src/action.h index ad6b004fe8452..db4a3e0bcd4ec 100644 --- a/src/action.h +++ b/src/action.h @@ -219,6 +219,8 @@ enum action_id : int { ACTION_IGNORE_ENEMY, /** Whitelist the enemy that triggered safemode */ ACTION_WHITELIST_ENEMY, + /** Open workout menu */ + ACTION_WORKOUT, /** Save the game and quit */ ACTION_SAVE, /** Quicksave the game */ diff --git a/src/activity_actor.cpp b/src/activity_actor.cpp index 3cfe572ba1f88..a005dfa2e9c85 100644 --- a/src/activity_actor.cpp +++ b/src/activity_actor.cpp @@ -21,6 +21,7 @@ #include "line.h" #include "map.h" #include "map_iterator.h" +#include "morale_types.h" #include "npc.h" #include "options.h" #include "output.h" @@ -1293,6 +1294,242 @@ std::unique_ptr try_sleep_activity_actor::deserialize( JsonIn &j return actor.clone(); } +void workout_activity_actor::start( player_activity &act, Character &who ) +{ + if( who.get_fatigue() > fatigue_levels::DEAD_TIRED ) { + who.add_msg_if_player( _( "You are too tired to exercise." ) ); + act_id = activity_id::NULL_ID(); + act.set_to_null(); + return; + } + if( who.get_thirst() > 240 ) { + who.add_msg_if_player( _( "You are too dehydrated to exercise." ) ); + act_id = activity_id::NULL_ID(); + act.set_to_null(); + return; + } + if( who.is_armed() ) { + who.add_msg_if_player( _( "Empty your hands first." ) ); + act_id = activity_id::NULL_ID(); + act.set_to_null(); + return; + } + // free training requires all limbs intact, but specialized workout machines + // train upper or lower parts of body only and may permit workout with + // broken limbs as long as they are not involved by the machine + bool hand_equipment = g->m.has_flag_furn( "WORKOUT_ARMS", location ); + bool leg_equipment = g->m.has_flag_furn( "WORKOUT_LEGS", location ); + static const bodypart_id arm_l = bodypart_id( "arm_l" ); + static const bodypart_id arm_r = bodypart_id( "arm_r" ); + static const bodypart_id leg_l = bodypart_id( "leg_l" ); + static const bodypart_id leg_r = bodypart_id( "leg_r" ); + if( hand_equipment && ( ( who.is_limb_broken( arm_l ) ) || + who.is_limb_broken( arm_r ) ) ) { + who.add_msg_if_player( _( "You cannot train here with a broken arm." ) ); + act_id = activity_id::NULL_ID(); + act.set_to_null(); + return; + } + if( leg_equipment && ( ( who.is_limb_broken( leg_l ) ) || + who.is_limb_broken( leg_r ) ) ) { + who.add_msg_if_player( _( "You cannot train here with a broken leg." ) ); + act_id = activity_id::NULL_ID(); + act.set_to_null(); + return; + } + if( !hand_equipment && !leg_equipment && + ( who.is_limb_broken( arm_l ) || + who.is_limb_broken( arm_r ) || + who.is_limb_broken( leg_l ) || + who.is_limb_broken( leg_r ) ) ) { + who.add_msg_if_player( _( "You cannot train freely with a broken limb." ) ); + act_id = activity_id::NULL_ID(); + act.set_to_null(); + return; + } + uilist workout_query; + workout_query.desc_enabled = true; + workout_query.text = + _( "Physical effort determines workout efficiency, but also rate of exhaustion." ); + workout_query.title = _( "Choose training intensity:" ); + workout_query.addentry_desc( 1, true, 'l', _( "Light" ), + _( "Light excercise comparable in intensity to walking, but more focused and methodical." ) ); + workout_query.addentry_desc( 2, true, 'm', _( "Moderate" ), + _( "Moderate excercise without excessive exertion, but with enough effort to break a sweat." ) ); + workout_query.addentry_desc( 3, true, 'a', _( "Active" ), + _( "Active excercise with full involvement. Strenuous, but in a controlled manner." ) ); + workout_query.addentry_desc( 4, true, 'h', _( "High" ), + _( "High intensity excercise with maximum effort and full power. Exhausting in the long run." ) ); + workout_query.query(); + switch( workout_query.ret ) { + case UILIST_CANCEL: + act.set_to_null(); + act_id = activity_id::NULL_ID(); + return; + case 4: + act_id = activity_id( "ACT_WORKOUT_HARD" ); + intensity_modifier = 4; + break; + case 3: + act_id = activity_id( "ACT_WORKOUT_ACTIVE" ); + intensity_modifier = 3; + break; + case 2: + act_id = activity_id( "ACT_WORKOUT_MODERATE" ); + intensity_modifier = 2; + break; + case 1: + default: + act_id = activity_id( "ACT_WORKOUT_LIGHT" ); + intensity_modifier = 1; + break; + } + int length; + query_int( length, _( "Train for how long (minutes): " ) ); + if( length > 0 ) { + duration = length * 1_minutes; + } else { + act_id = activity_id::NULL_ID(); + act.set_to_null(); + return; + } + act.moves_total = to_moves( duration ); + act.moves_left = act.moves_total; + if( who.male ) { + sfx::play_activity_sound( "plmove", "fatigue_m_med", sfx::get_heard_volume( location ) ); + } else { + sfx::play_activity_sound( "plmove", "fatigue_f_med", sfx::get_heard_volume( location ) ); + } + who.add_msg_if_player( _( "You start your workout session." ) ); +} + +void workout_activity_actor::do_turn( player_activity &act, Character &who ) +{ + if( who.get_fatigue() > fatigue_levels::DEAD_TIRED ) { + who.add_msg_if_player( _( "You are exhausted so you finish your workout early." ) ); + act.set_to_null(); + return; + } + if( who.get_thirst() > 240 ) { + who.add_msg_if_player( _( "You are dehydrated so you finish your workout early." ) ); + act.set_to_null(); + return; + } + if( !rest_mode && who.get_stamina() > who.get_stamina_max() / 3 ) { + who.mod_stamina( -25 - intensity_modifier ); + if( one_in( 180 / intensity_modifier ) ) { + who.mod_fatigue( 1 ); + who.mod_thirst( 1 ); + } + if( calendar::once_every( 16_minutes / intensity_modifier ) ) { + //~ heavy breathing when excercising + std::string huff = _( "yourself huffing and puffing!" ); + sounds::sound( location + tripoint_east, 2 * intensity_modifier, sounds::sound_t::speech, huff, + true ); + } + // morale bonus kicks in gradually after 5 minutes of exercise + if( calendar::once_every( 2_minutes ) && + ( ( elapsed + act.moves_total - act.moves_left ) / 100 * 1_turns ) > 5_minutes ) { + who.add_morale( MORALE_FEELING_GOOD, intensity_modifier, 20, 6_hours, 30_minutes ); + } + if( calendar::once_every( 2_minutes ) ) { + who.add_msg_if_player( m_debug, who.activity_level_str() ); + who.add_msg_if_player( m_debug, act.id().c_str() ); + } + } else if( !rest_mode ) { + rest_mode = true; + who.add_msg_if_player( _( "You catch your breath for few moments." ) ); + } else if( who.get_stamina() >= who.get_stamina_max() ) { + rest_mode = false; + who.add_msg_if_player( _( "You get back to your training." ) ); + } +} + +void workout_activity_actor::finish( player_activity &act, Character &who ) +{ + if( !query_keep_training( act, who ) ) { + act.set_to_null(); + who.add_msg_if_player( _( "You finish your workout session." ) ); + } +} + +void workout_activity_actor::canceled( player_activity &/*act*/, Character &/*who*/ ) +{ + stop_time = calendar::turn; +} + +bool workout_activity_actor::query_keep_training( player_activity &act, Character &who ) +{ + if( disable_query || !who.is_avatar() ) { + elapsed += act.moves_total - act.moves_left; + act.moves_total = to_moves( 60_minutes ); + act.moves_left = act.moves_total; + return true; + } + int length; + uilist workout_query; + workout_query.text = _( "You have finished your training cycle, keep training?" ); + workout_query.addentry( 1, true, 'S', _( "Stop training." ) ); + workout_query.addentry( 2, true, 'c', _( "Continue training." ) ); + workout_query.addentry( 3, true, 'C', _( "Continue training and don't ask again." ) ); + workout_query.query(); + switch( workout_query.ret ) { + case UILIST_CANCEL: + case 1: + act_id = activity_id::NULL_ID(); + act.set_to_null(); + return false; + case 3: + disable_query = true; + elapsed += act.moves_total - act.moves_left; + act.moves_total = to_moves( 60_minutes ); + act.moves_left = act.moves_total; + return true; + case 2: + default: + query_int( length, _( "Train for how long (minutes): " ) ); + elapsed += act.moves_total - act.moves_left; + act.moves_total = to_moves( length * 1_minutes ); + act.moves_left = act.moves_total; + return true; + break; + } +} + +void workout_activity_actor::serialize( JsonOut &jsout ) const +{ + jsout.start_object(); + + jsout.member( "disable_query", disable_query ); + jsout.member( "act_id", act_id ); + jsout.member( "duration", duration ); + jsout.member( "location", location ); + jsout.member( "stop_time", stop_time ); + jsout.member( "elapsed", elapsed ); + jsout.member( "intensity_modifier", intensity_modifier ); + jsout.member( "rest_mode", rest_mode ); + + jsout.end_object(); +} + +std::unique_ptr workout_activity_actor::deserialize( JsonIn &jsin ) +{ + workout_activity_actor actor = workout_activity_actor( tripoint_zero ); + + JsonObject data = jsin.get_object(); + + data.read( "disable_query", actor.disable_query ); + data.read( "act_id", actor.act_id ); + data.read( "duration", actor.duration ); + data.read( "location", actor.location ); + data.read( "stop_time", actor.stop_time ); + data.read( "elapsed", actor.elapsed ); + data.read( "intensity_modifier", actor.intensity_modifier ); + data.read( "rest_mode", actor.rest_mode ); + + return actor.clone(); +} + namespace activity_actors { @@ -1311,6 +1548,10 @@ deserialize_functions = { { activity_id( "ACT_OPEN_GATE" ), &open_gate_activity_actor::deserialize }, { activity_id( "ACT_PICKUP" ), &pickup_activity_actor::deserialize }, { activity_id( "ACT_TRY_SLEEP" ), &try_sleep_activity_actor::deserialize }, + { activity_id( "ACT_WORKOUT_HARD" ), &workout_activity_actor::deserialize }, + { activity_id( "ACT_WORKOUT_ACTIVE" ), &workout_activity_actor::deserialize }, + { activity_id( "ACT_WORKOUT_MODERATE" ), &workout_activity_actor::deserialize }, + { activity_id( "ACT_WORKOUT_LIGHT" ), &workout_activity_actor::deserialize }, }; } // namespace activity_actors diff --git a/src/activity_actor.h b/src/activity_actor.h index e8eeff6d42169..f0f654929c3d1 100644 --- a/src/activity_actor.h +++ b/src/activity_actor.h @@ -7,6 +7,7 @@ #include #include +#include "activity_type.h" #include "clone_ptr.h" #include "item_location.h" #include "item.h" @@ -581,6 +582,46 @@ class try_sleep_activity_actor : public activity_actor static std::unique_ptr deserialize( JsonIn &jsin ); }; +class workout_activity_actor : public activity_actor +{ + private: + bool disable_query = false; // disables query, continue as long as possible + bool rest_mode = false; // work or rest during training session + time_duration duration; + tripoint location; + time_point stop_time; // can resume if time apart is not above + activity_id act_id = activity_id( "ACT_WORKOUT_LIGHT" ); // variable activities + int intensity_modifier = 1; + int elapsed = 0; + + public: + workout_activity_actor( const tripoint &loc ) : location( loc ) {} + + // can assume different sub-activities + activity_id get_type() const override { + return act_id; + } + + bool can_resume_with_internal( const activity_actor &other, const Character & ) const override { + const workout_activity_actor &w_actor = static_cast( other ); + return ( location == w_actor.location && calendar::turn - stop_time <= 10_minutes ); + } + + void start( player_activity &act, Character &who ) override; + void do_turn( player_activity &act, Character &who ) override; + void finish( player_activity &act, Character &who ) override; + void canceled( player_activity &/*act*/, Character &/*who*/ ) override; + + bool query_keep_training( player_activity &act, Character &who ); + + std::unique_ptr clone() const override { + return std::make_unique( *this ); + } + + void serialize( JsonOut &jsout ) const override; + static std::unique_ptr deserialize( JsonIn &jsin ); +}; + namespace activity_actors { diff --git a/src/game.cpp b/src/game.cpp index 16ddae9681fd8..31fe9ede25a93 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -2580,6 +2580,7 @@ input_context get_default_mode_input_context() ctxt.register_action( "autoattack" ); ctxt.register_action( "ignore_enemy" ); ctxt.register_action( "whitelist_enemy" ); + ctxt.register_action( "workout" ); ctxt.register_action( "save" ); ctxt.register_action( "quicksave" ); #if !defined(RELEASE) diff --git a/src/handle_action.cpp b/src/handle_action.cpp index 14bf87009ea8d..c0a516b4db933 100644 --- a/src/handle_action.cpp +++ b/src/handle_action.cpp @@ -2251,6 +2251,12 @@ bool game::handle_action() } break; + case ACTION_WORKOUT: + if( query_yn( _( "Start workout?" ) ) ) { + u.assign_activity( player_activity( workout_activity_actor( u.pos() ) ) ); + } + break; + case ACTION_SUICIDE: if( query_yn( _( "Commit suicide?" ) ) ) { if( query_yn( _( "REALLY commit suicide?" ) ) ) { diff --git a/src/iexamine.cpp b/src/iexamine.cpp index 7476f9e8eae9f..3da765f262377 100644 --- a/src/iexamine.cpp +++ b/src/iexamine.cpp @@ -5953,6 +5953,15 @@ void iexamine::workbench_internal( player &p, const tripoint &examp, } } +void iexamine::workout( player &p, const tripoint &examp ) +{ + if( !query_yn( _( "Use the %s to exercise?" ), get_map().furnname( examp ) ) ) { + none( p, examp ); + return; + } + p.assign_activity( player_activity( workout_activity_actor( examp ) ) ); +} + /** * Given then name of one of the above functions, returns the matching function * pointer. If no match is found, defaults to iexamine::none but prints out a @@ -6039,7 +6048,8 @@ iexamine_function iexamine_function_from_string( const std::string &function_nam { "quern_examine", &iexamine::quern_examine }, { "smoker_options", &iexamine::smoker_options }, { "open_safe", &iexamine::open_safe }, - { "workbench", &iexamine::workbench } + { "workbench", &iexamine::workbench }, + { "workout", &iexamine::workout } } }; diff --git a/src/iexamine.h b/src/iexamine.h index 9e4b2e2ca4c33..abb9665b081a7 100644 --- a/src/iexamine.h +++ b/src/iexamine.h @@ -111,6 +111,7 @@ void open_safe( player &p, const tripoint &examp ); void workbench( player &p, const tripoint &examp ); void workbench_internal( player &p, const tripoint &examp, const cata::optional &part ); +void workout( player &p, const tripoint &examp ); bool pour_into_keg( const tripoint &pos, item &liquid ); cata::optional getGasPumpByNumber( const tripoint &p, int number ); diff --git a/src/player_activity.cpp b/src/player_activity.cpp index d23501835c0fd..913aef93cde31 100644 --- a/src/player_activity.cpp +++ b/src/player_activity.cpp @@ -76,6 +76,13 @@ void player_activity::set_to_null() sfx::end_activity_sounds(); // kill activity sounds when activity is nullified } +void player_activity::sychronize_type_with_actor() +{ + if( actor && type != activity_id::NULL_ID() ) { + type = actor->get_type(); + } +} + bool player_activity::rooted() const { return type->rooted(); @@ -177,13 +184,17 @@ void player_activity::start_or_resume( Character &who, bool resuming ) if( actor && !resuming ) { actor->start( *this, who ); } - if( rooted() ) { + if( !type.is_null() && rooted() ) { who.rooted_message(); } + // last, as start function may have changed the type + sychronize_type_with_actor(); } void player_activity::do_turn( player &p ) { + // first to ensure sync with actor + sychronize_type_with_actor(); // Should happen before activity or it may fail du to 0 moves if( *this && type->will_refuel_fires() ) { try_fuel_fire( *this, p ); @@ -241,6 +252,7 @@ void player_activity::do_turn( player &p ) const bool travel_activity = id() == ACT_TRAVELLING; // This might finish the activity (set it to null) if( actor ) { + p.increase_activity_level( actor->get_type()->exertion_level() ); actor->do_turn( *this, p ); } else { // Use the legacy turn function @@ -254,7 +266,13 @@ void player_activity::do_turn( player &p ) // to simulate that the next step will surely use up some stamina anyway // this is to ensure that resting will occur when traveling overburdened const int adjusted_stamina = travel_activity ? p.get_stamina() - 1 : p.get_stamina(); - if( adjusted_stamina < previous_stamina && p.get_stamina() < p.get_stamina_max() / 3 ) { + activity_id act_id = actor ? actor->get_type() : type; + bool excluded = act_id == activity_id( "ACT_WORKOUT_HARD" ) || + act_id == activity_id( "ACT_WORKOUT_ACTIVE" ) || + act_id == activity_id( "ACT_WORKOUT_MODERATE" ) || + act_id == activity_id( "ACT_WORKOUT_LIGHT" ); + if( !excluded && adjusted_stamina < previous_stamina && + p.get_stamina() < p.get_stamina_max() / 3 ) { if( one_in( 50 ) ) { p.add_msg_if_player( _( "You pause for a moment to catch your breath." ) ); } @@ -280,6 +298,7 @@ void player_activity::do_turn( player &p ) set_to_null(); } } + p.reset_activity_level(); } if( !*this ) { // Make sure data of previous activity is cleared diff --git a/src/player_activity.h b/src/player_activity.h index 419d8c66e4b88..39b14c00ed80e 100644 --- a/src/player_activity.h +++ b/src/player_activity.h @@ -93,6 +93,11 @@ class player_activity /** This replaces the former usage `act.type = ACT_NULL` */ void set_to_null(); + // This makes player_activity's activity type inherit activity_actor's activity type, + // in order to synchronize both, due to possible variablility of actor's activity type + // allowed via override of activity_actor::get_type() + void sychronize_type_with_actor(); + const activity_id &id() const { return type; } From bacfe163dc288a09cdf1032cdd66c29744d4d028 Mon Sep 17 00:00:00 2001 From: Xenomorph-III Date: Wed, 1 Jul 2020 14:08:46 +1200 Subject: [PATCH 016/420] Adds a no-welding version of the four-winds shotgun. (#41626) * add alternate recipe for slam_fire shotgun. No welder this time, and it's given right off the bat! Say hello to your new early early-game shotgun! --- data/json/items/gun/shot.json | 4 +- data/json/items/migration.json | 5 ++ data/json/recipes/recipe_obsolete.json | 5 ++ data/json/recipes/weapon/ranged.json | 47 ++++++++++++++++++- .../firearms/gg_firearms_migration.json | 5 ++ data/mods/Generic_Guns/firearms/shot.json | 8 ++-- data/mods/Generic_Guns/obsolete.json | 5 ++ .../recipes/recipes_firearms_single.json | 44 ++++++++++++++++- 8 files changed, 113 insertions(+), 10 deletions(-) diff --git a/data/json/items/gun/shot.json b/data/json/items/gun/shot.json index 82de9e4b1edb0..bb29392ff70a8 100644 --- a/data/json/items/gun/shot.json +++ b/data/json/items/gun/shot.json @@ -827,10 +827,10 @@ "pocket_data": [ { "pocket_type": "MAGAZINE", "rigid": true, "ammo_restriction": { "shot": 6 } } ] }, { - "id": "slam_shotgun", + "id": "four_winds_shotgun", "copy-from": "shotgun_base", "type": "GUN", - "name": { "str": "makeshift shotgun" }, + "name": { "str": "four winds shotgun" }, "description": "A crude shotgun, composed of two thick steel pipes, an end cap and a nail. The lack of sights make this weapon only useful at point-blank range.", "weight": "3629 g", "volume": "2264 ml", diff --git a/data/json/items/migration.json b/data/json/items/migration.json index 5733fd4dd7373..a8c09f9122da4 100644 --- a/data/json/items/migration.json +++ b/data/json/items/migration.json @@ -1233,5 +1233,10 @@ "id": "carton_unsealed", "type": "MIGRATION", "replace": "carton_sealed" + }, + { + "id": "slam_shotgun", + "type": "MIGRATION", + "replace": "four_winds_shotgun" } ] diff --git a/data/json/recipes/recipe_obsolete.json b/data/json/recipes/recipe_obsolete.json index 1cb21d004dbb5..2d9f696a4e43d 100644 --- a/data/json/recipes/recipe_obsolete.json +++ b/data/json/recipes/recipe_obsolete.json @@ -2499,5 +2499,10 @@ "type": "recipe", "result": "bone_plate", "obsolete": true + }, + { + "type": "recipe", + "result": "slam_shotgun", + "obsolete": true } ] diff --git a/data/json/recipes/weapon/ranged.json b/data/json/recipes/weapon/ranged.json index cdf1df3b28f6e..1941edb1980c9 100644 --- a/data/json/recipes/weapon/ranged.json +++ b/data/json/recipes/weapon/ranged.json @@ -260,13 +260,13 @@ }, { "type": "recipe", - "result": "slam_shotgun", + "result": "four_winds_shotgun", "category": "CC_WEAPON", "subcategory": "CSC_WEAPON_RANGED", "skill_used": "mechanics", "skills_required": [ [ "gun", 1 ] ], "difficulty": 1, - "time": "2 h", + "time": "4 h", "autolearn": true, "book_learn": [ [ "manual_shotgun", 1 ] ], "qualities": [ @@ -301,5 +301,48 @@ ] ], "components": [ [ [ "pipe", 2 ] ], [ [ "nail", 1 ] ], [ [ "scrap", 3 ] ] ] + }, + { + "type": "recipe", + "result": "four_winds_shotgun", + "id_suffix": "without_welding", + "category": "CC_WEAPON", + "subcategory": "CSC_WEAPON_RANGED", + "skill_used": "mechanics", + "skills_required": [ [ "gun", 1 ] ], + "time": "2 h", + "autolearn": true, + "qualities": [ + { "id": "SAW_M", "level": 1 }, + { "id": "HAMMER", "level": 2 }, + { "id": "FILE", "level": 1 }, + { "id": "WRENCH", "level": 1 }, + { "id": "DRILL", "level": 2 } + ], + "tools": [ + [ + [ "shot_00", -1 ], + [ "shot_bird", -1 ], + [ "shot_dragon", -1 ], + [ "shot_flechette", -1 ], + [ "shot_slug", -1 ], + [ "shot_scrap", -1 ], + [ "shot_he", -1 ], + [ "shot_beanbag", -1 ], + [ "reloaded_shot_00", -1 ], + [ "reloaded_shot_bird", -1 ], + [ "reloaded_shot_dragon", -1 ], + [ "reloaded_shot_flechette", -1 ], + [ "reloaded_shot_slug", -1 ], + [ "bp_shot_00", -1 ], + [ "bp_shot_bird", -1 ], + [ "bp_shot_dragon", -1 ], + [ "bp_shot_flechette", -1 ], + [ "bp_shot_slug", -1 ], + [ "bp_shot_scrap", -1 ], + [ "shot_hull", -1 ] + ] + ], + "components": [ [ [ "pipe", 2 ] ], [ [ "nail", 1 ] ], [ [ "pipe_fittings", 1 ] ] ] } ] diff --git a/data/mods/Generic_Guns/firearms/gg_firearms_migration.json b/data/mods/Generic_Guns/firearms/gg_firearms_migration.json index a3bb83542b657..09a76cd919cd4 100644 --- a/data/mods/Generic_Guns/firearms/gg_firearms_migration.json +++ b/data/mods/Generic_Guns/firearms/gg_firearms_migration.json @@ -306,5 +306,10 @@ ], "type": "MIGRATION", "replace": "shot_pump" + }, + { + "id": "slam_shotgun", + "type": "MIGRATION", + "replace": "four_winds_shotgun" } ] diff --git a/data/mods/Generic_Guns/firearms/shot.json b/data/mods/Generic_Guns/firearms/shot.json index 85f7ddeea8119..4986437868f5d 100644 --- a/data/mods/Generic_Guns/firearms/shot.json +++ b/data/mods/Generic_Guns/firearms/shot.json @@ -50,10 +50,10 @@ "ammo": [ "ammo_shot" ] }, { - "id": "slam_shotgun", - "copy-from": "slam_shotgun", + "id": "four_winds_shotgun", + "copy-from": "four_winds_shotgun", "type": "GUN", - "name": { "str": "slam-fire shotgun" }, + "name": { "str": "four winds shotgun" }, "description": "A crude shotgun, composed of two thick steel pipes, an end cap and a nail. The lack of sights make this weapon only useful at point-blank range.", "weight": "3629 g", "volume": "2264 ml", @@ -62,7 +62,7 @@ "price_postapoc": 1000, "bashing": 8, "material": [ "steel" ], - "ranged_damage": { "damage_type": "bullet", "amount": -2 }, + "ranged_damage": { "damage_type": "bullet", "amount": -6 }, "dispersion": 960, "durability": 4, "clip_size": 1, diff --git a/data/mods/Generic_Guns/obsolete.json b/data/mods/Generic_Guns/obsolete.json index 452cccd75d252..69beceb33f2fb 100644 --- a/data/mods/Generic_Guns/obsolete.json +++ b/data/mods/Generic_Guns/obsolete.json @@ -43,5 +43,10 @@ "type": "recipe", "result": "rifle_pipe_rifle", "obsolete": true + }, + { + "type": "recipe", + "result": "slam_shotgun", + "obsolete": true } ] diff --git a/data/mods/Generic_Guns/recipes/recipes_firearms_single.json b/data/mods/Generic_Guns/recipes/recipes_firearms_single.json index 1c2998dfd75bc..d3c1c77753998 100644 --- a/data/mods/Generic_Guns/recipes/recipes_firearms_single.json +++ b/data/mods/Generic_Guns/recipes/recipes_firearms_single.json @@ -125,7 +125,7 @@ }, { "type": "recipe", - "result": "slam_shotgun", + "result": "four_winds_shotgun", "category": "CC_WEAPON", "subcategory": "CSC_WEAPON_RANGED", "skill_used": "mechanics", @@ -146,7 +146,6 @@ [ "shot_buck", -1 ], [ "shot_fowl", -1 ], [ "shot_pyro", -1 ], - [ "shot_dart", -1 ], [ "shot_foster", -1 ], [ "shot_bean", -1 ], [ "reloaded_shot_buck", -1 ], @@ -165,5 +164,46 @@ ] ], "components": [ [ [ "pipe", 2 ] ], [ [ "nail", 1 ] ], [ [ "scrap", 3 ] ] ] + }, + { + "type": "recipe", + "result": "four_winds_shotgun", + "id_suffix": "without_welding", + "category": "CC_WEAPON", + "subcategory": "CSC_WEAPON_RANGED", + "skill_used": "mechanics", + "skills_required": [ [ "gun", 1 ] ], + "time": "1 h", + "autolearn": true, + "qualities": [ + { "id": "SAW_M", "level": 1 }, + { "id": "DRILL", "level": 2 }, + { "id": "HAMMER", "level": 2 }, + { "id": "FILE", "level": 1 }, + { "id": "WRENCH", "level": 1 } + ], + "tools": [ + [ + [ "shot_buck", -1 ], + [ "shot_fowl", -1 ], + [ "shot_pyro", -1 ], + [ "shot_foster", -1 ], + [ "shot_bean", -1 ], + [ "reloaded_shot_buck", -1 ], + [ "reloaded_shot_fowl", -1 ], + [ "reloaded_shot_pyro", -1 ], + [ "reloaded_shot_dart", -1 ], + [ "reloaded_shot_foster", -1 ], + [ "reloaded_shot_junk", -1 ], + [ "bp_shot_buck", -1 ], + [ "bp_shot_fowl", -1 ], + [ "bp_shot_pyro", -1 ], + [ "bp_shot_dart", -1 ], + [ "bp_shot_foster", -1 ], + [ "bp_shot_junk", -1 ], + [ "shot_hull", -1 ] + ] + ], + "components": [ [ [ "pipe", 2 ] ], [ [ "nail", 1 ] ], [ [ "pipe_fittings", 1 ] ] ] } ] From 975c9420a15dc46c867d80677a33bf62b47b1ef1 Mon Sep 17 00:00:00 2001 From: Kevin Granade Date: Tue, 30 Jun 2020 19:13:52 -0700 Subject: [PATCH 017/420] Remove auto --- src/iuse.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/iuse.cpp b/src/iuse.cpp index b13ded24123a5..e8d585fa0aa08 100644 --- a/src/iuse.cpp +++ b/src/iuse.cpp @@ -3927,7 +3927,7 @@ int iuse::molotov_lit( player *p, item *it, bool t, const tripoint &pos ) if( pos.x == -999 || pos.y == -999 ) { return 0; } else if( !t ) { - for( auto &pt : g->m.points_in_radius( pos, 1, 0 ) ) { + for( const tripoint &pt : g->m.points_in_radius( pos, 1, 0 ) ) { const int intensity = 1 + one_in( 3 ) + one_in( 5 ); g->m.add_field( pt, fd_fire, intensity ); } From 0df224f2eb7511d9e503d3f1c99fb3154f4ebde8 Mon Sep 17 00:00:00 2001 From: Menschheit Date: Wed, 1 Jul 2020 04:47:04 +0200 Subject: [PATCH 018/420] Fix loading problem for guns with internal magazine (still need json change for specific guns) (#41667) * fix speed loader(still need some more json changes) * Added magazine pocket for marlin_9a --- data/json/items/gun/22.json | 1 + data/json/items/gun/3006.json | 1 + data/json/items/gun/762.json | 1 + data/json/items/gun/762R.json | 1 + src/item.cpp | 7 +++++++ 5 files changed, 11 insertions(+) diff --git a/data/json/items/gun/22.json b/data/json/items/gun/22.json index 57f61e61c8a72..9e5d8d21e460b 100644 --- a/data/json/items/gun/22.json +++ b/data/json/items/gun/22.json @@ -88,6 +88,7 @@ "faults": [ "fault_gun_blackpowder", "fault_gun_dirt" ], "flags": [ "RELOAD_ONE" ], "pocket_data": [ + { "pocket_type": "MAGAZINE", "ammo_restriction": { "22": 19 } }, { "pocket_type": "MAGAZINE_WELL", "holster": true, diff --git a/data/json/items/gun/3006.json b/data/json/items/gun/3006.json index 5ed0ebb3026d8..30c23b48aaf5a 100644 --- a/data/json/items/gun/3006.json +++ b/data/json/items/gun/3006.json @@ -130,6 +130,7 @@ ], "flags": [ "RELOAD_ONE" ], "pocket_data": [ + { "pocket_type": "MAGAZINE", "ammo_restriction": { "3006": 5 } }, { "pocket_type": "MAGAZINE_WELL", "holster": true, diff --git a/data/json/items/gun/762.json b/data/json/items/gun/762.json index 8ee6fec7d9b75..599efb0022157 100644 --- a/data/json/items/gun/762.json +++ b/data/json/items/gun/762.json @@ -135,6 +135,7 @@ ], "flags": [ "RELOAD_ONE", "NEVER_JAMS" ], "pocket_data": [ + { "pocket_type": "MAGAZINE", "ammo_restriction": { "762": 10 } }, { "pocket_type": "MAGAZINE_WELL", "holster": true, diff --git a/data/json/items/gun/762R.json b/data/json/items/gun/762R.json index 96e20fedc44e7..b33dab7af9c43 100644 --- a/data/json/items/gun/762R.json +++ b/data/json/items/gun/762R.json @@ -73,6 +73,7 @@ ], "flags": [ "RELOAD_ONE" ], "pocket_data": [ + { "pocket_type": "MAGAZINE", "ammo_restriction": { "762R": 5 } }, { "pocket_type": "MAGAZINE_WELL", "holster": true, diff --git a/src/item.cpp b/src/item.cpp index ea1169e61133b..68330de06a237 100644 --- a/src/item.cpp +++ b/src/item.cpp @@ -6587,6 +6587,13 @@ bool item::is_reloadable_helper( const itype_id &ammo, bool now ) const return false; } } + //Now single shoted gun also has magazine_well slot for speedloader + //If ammo is not an ammo it may be dangerous to use parameters like ammo->ammo->type + //It is complicated: normal magazine in addition to speedloader? Magazines of mods? + if( now && !ammo->ammo ) { + return magazine_compatible().count( ammo ); + } + return now ? ammo_remaining() < ammo_capacity( ammo->ammo->type ) : true; } return magazine_compatible().count( ammo ); From 672e309c7bb298324b70341de5106c9ddfac1be5 Mon Sep 17 00:00:00 2001 From: Menschheit Date: Wed, 1 Jul 2020 05:04:54 +0200 Subject: [PATCH 019/420] Fix multi select for washing (#41633) * Fix Selector UI for washing * Fix UI display for detergent consumption * Modify existing class instead of adding new one * Always use item_location to avoid type conversion --- src/inventory_ui.cpp | 25 ++++++++++++++++--------- src/inventory_ui.h | 6 ++++-- src/iuse.cpp | 10 ++++------ 3 files changed, 24 insertions(+), 17 deletions(-) diff --git a/src/inventory_ui.cpp b/src/inventory_ui.cpp index eb98670ff40df..6864339383c0a 100644 --- a/src/inventory_ui.cpp +++ b/src/inventory_ui.cpp @@ -2282,12 +2282,10 @@ drop_locations inventory_iuse_selector::execute() count = 0; } } - drop_locations dropped_pos_and_qty; - for( const std::pair &use_pair : to_use ) { - item_location loc( u, const_cast( use_pair.first ) ); - dropped_pos_and_qty.push_back( std::make_pair( loc, use_pair.second ) ); + for( const std::pair &use_pair : to_use ) { + dropped_pos_and_qty.push_back( std::make_pair( *use_pair.first, use_pair.second ) ); } return dropped_pos_and_qty; @@ -2295,17 +2293,26 @@ drop_locations inventory_iuse_selector::execute() void inventory_iuse_selector::set_chosen_count( inventory_entry &entry, size_t count ) { - const item *it = &*entry.any_item(); + const item_location &it = entry.any_item(); if( count == 0 ) { entry.chosen_count = 0; - const auto iter = to_use.find( it ); - if( iter != to_use.end() ) { - to_use.erase( iter ); + for( const item_location &loc : entry.locations ) { + to_use.erase( &loc ); } } else { entry.chosen_count = std::min( std::min( count, max_chosen_count ), entry.get_available_count() ); - to_use[it] = entry.chosen_count; + if( it->count_by_charges() ) { + to_use[&it] = entry.chosen_count; + } else { + for( const item_location &loc : entry.locations ) { + if( count == 0 ) { + break; + } + to_use[&loc] = 1; + count--; + } + } } on_change( entry ); diff --git a/src/inventory_ui.h b/src/inventory_ui.h index 70c2808689d1d..e013973cf12e0 100644 --- a/src/inventory_ui.h +++ b/src/inventory_ui.h @@ -734,7 +734,7 @@ class inventory_compare_selector : public inventory_multiselector class inventory_iuse_selector : public inventory_multiselector { public: - using GetStats = std::function & )>; + using GetStats = std::function & )>; inventory_iuse_selector( player &p, const std::string &selector_title, const inventory_selector_preset &preset = default_preset, @@ -747,7 +747,7 @@ class inventory_iuse_selector : public inventory_multiselector private: GetStats get_stats; - std::map to_use; + std::map to_use; size_t max_chosen_count; }; @@ -769,4 +769,6 @@ class inventory_drop_selector : public inventory_multiselector std::vector> dropping; size_t max_chosen_count; }; + + #endif // CATA_SRC_INVENTORY_UI_H diff --git a/src/iuse.cpp b/src/iuse.cpp index c82ba03e28839..b02f7f613781d 100644 --- a/src/iuse.cpp +++ b/src/iuse.cpp @@ -9755,12 +9755,12 @@ int iuse::wash_items( player *p, bool soft_items, bool hard_items ) ( hard_items && !location->is_soft() ) ); } ); auto make_raw_stats = [available_water, available_cleanser]( - const std::map &items + const std::map &locs ) { units::volume total_volume = 0_ml; - for( const auto &p : items ) { - total_volume += p.first->volume() * p.second / - ( p.first->count_by_charges() ? p.first->charges : 1 ); + for( const auto &p : locs ) { + total_volume += ( *p.first )->volume() * p.second / + ( ( *p.first )->count_by_charges() ? ( *p.first )->charges : 1 ); } washing_requirements required = washing_requirements_for_volume( total_volume ); auto to_string = []( int val ) -> std::string { @@ -9789,7 +9789,6 @@ int iuse::wash_items( player *p, bool soft_items, bool hard_items ) if( to_clean.empty() ) { return 0; } - // Determine if we have enough water and cleanser for all the items. units::volume total_volume = 0_ml; for( drop_location pair : to_clean ) { @@ -9823,7 +9822,6 @@ int iuse::wash_items( player *p, bool soft_items, bool hard_items ) } // Assign the activity values. p->assign_activity( ACT_WASH, required.time ); - for( const drop_location &pair : to_clean ) { p->activity.targets.push_back( pair.first ); p->activity.values.push_back( pair.second ); From e42715fcff76e5d10c271a5158545855ae2d7ec7 Mon Sep 17 00:00:00 2001 From: Qrox Date: Mon, 29 Jun 2020 18:51:48 +0800 Subject: [PATCH 020/420] Fix crash when trying to reloading item inside advanced inventory --- src/advanced_inv.cpp | 8 ++++++++ src/advanced_inv.h | 1 + 2 files changed, 9 insertions(+) diff --git a/src/advanced_inv.cpp b/src/advanced_inv.cpp index eb177cf3e2a92..602d5e17e5951 100644 --- a/src/advanced_inv.cpp +++ b/src/advanced_inv.cpp @@ -1348,8 +1348,12 @@ void advanced_inventory::action_examine( advanced_inv_listitem *sitem, // "return to AIM". do_return_entry(); assert( g->u.has_activity( ACT_ADV_INVENTORY ) ); + // `inventory_item_menu` may call functions that move items, so we should + // always recalculate during this period to ensure all item references are valid + always_recalc = true; ret = g->inventory_item_menu( loc, info_startx, info_width, src == advanced_inventory::side::left ? game::LEFT_OF_INFO : game::RIGHT_OF_INFO ); + always_recalc = false; if( !g->u.has_activity( ACT_ADV_INVENTORY ) ) { exit = true; } else { @@ -1432,6 +1436,10 @@ void advanced_inventory::display() ui->mark_resize(); ui->on_redraw( [&]( const ui_adaptor & ) { + if( always_recalc ) { + recalc = true; + } + redraw_pane( advanced_inventory::side::left ); redraw_pane( advanced_inventory::side::right ); redraw_sidebar(); diff --git a/src/advanced_inv.h b/src/advanced_inv.h index 29baf1e1da147..28267a33c9774 100644 --- a/src/advanced_inv.h +++ b/src/advanced_inv.h @@ -81,6 +81,7 @@ class advanced_inventory int colstart = 0; bool recalc = false; + bool always_recalc = false; /** * Which panels is active (item moved from there). */ From 0c9fb0f37cb97ae7b3ee54f1b281f10d5dd178fd Mon Sep 17 00:00:00 2001 From: Menschheit Date: Wed, 1 Jul 2020 05:41:23 +0200 Subject: [PATCH 021/420] Allow unloading mods instead of the gun itself (#41685) --- src/player.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/player.cpp b/src/player.cpp index d0debcb939207..643ff5d2c31a3 100644 --- a/src/player.cpp +++ b/src/player.cpp @@ -2918,7 +2918,7 @@ bool player::unload( item_location &loc ) if( target->is_magazine() ) { player_activity unload_mag_act( activity_id( "ACT_UNLOAD_MAG" ) ); assign_activity( unload_mag_act ); - activity.targets.emplace_back( loc ); + activity.targets.emplace_back( item_location( loc, target ) ); // Calculate the time to remove the contained ammo (consuming half as much time as required to load the magazine) int mv = 0; From 07111b0049fdc33114a81abc6c015b1a87e32bb2 Mon Sep 17 00:00:00 2001 From: Broken27 <67008253+Broken27@users.noreply.github.com> Date: Wed, 1 Jul 2020 05:42:50 +0200 Subject: [PATCH 022/420] Added recipe for separation funnel (#41689) --- data/json/recipes/recipe_others.json | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/data/json/recipes/recipe_others.json b/data/json/recipes/recipe_others.json index a91bade313322..4e39d6a1186a0 100644 --- a/data/json/recipes/recipe_others.json +++ b/data/json/recipes/recipe_others.json @@ -4609,5 +4609,33 @@ "autolearn": true, "tools": [ [ [ "tongs", -1 ] ], [ [ "crucible", -1 ], [ "crucible_clay", -1 ] ], [ [ "sheet_metal", -1 ] ], [ [ "forge", 75 ] ] ], "components": [ [ [ "plastic_chunk", 200 ] ] ] + }, + { + "type": "recipe", + "result": "funnel_separation", + "category": "CC_OTHER", + "subcategory": "CSC_OTHER_TOOLS", + "skill_used": "fabrication", + "difficulty": 7, + "time": "1 h", + "book_learn": [ [ "glassblowing_book", 5 ] ], + "qualities": [ { "id": "CHISEL", "level": 3 } ], + "tools": [ [ [ "tongs", -1 ] ], [ [ "pipe", -1 ] ], [ [ "crucible", -1 ], [ "crucible_clay", -1 ] ], [ [ "forge", 75 ] ] ], + "components": [ + [ [ "glass_shard", 3 ], [ "pipe_glass", 1 ], [ "flask_glass", 3 ], [ "test_tube", 6 ], [ "marble", 75 ] ], + [ [ "stopcock", 1 ] ] + ] + }, + { + "type": "recipe", + "result": "stopcock", + "category": "CC_OTHER", + "subcategory": "CSC_OTHER_OTHER", + "skill_used": "fabrication", + "difficulty": 4, + "time": "30 m", + "autolearn": true, + "tools": [ [ [ "mold_plastic", -1 ] ], [ [ "surface_heat", 5, "LIST" ] ] ], + "components": [ [ [ "plastic_chunk", 1 ] ] ] } ] From a1e694840af497e4b9fb0f985b61a463a156298c Mon Sep 17 00:00:00 2001 From: Menschheit Date: Wed, 1 Jul 2020 05:45:20 +0200 Subject: [PATCH 023/420] update item_contents::only_item (#41690) --- src/item_contents.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/item_contents.cpp b/src/item_contents.cpp index cea650f10942b..37bf67cabea6b 100644 --- a/src/item_contents.cpp +++ b/src/item_contents.cpp @@ -372,8 +372,8 @@ ret_val item_contents::insert_item( const item &it, item_pocket::pocket_ty // LAST is invalid, so we assume it will be a regular container pk_type = item_pocket::pocket_type::CONTAINER; } - ret_val pocket = find_pocket_for( it, pk_type ); + ret_val pocket = find_pocket_for( it, pk_type ); if( pocket.value() == nullptr ) { return ret_val::make_failure( "No success" ); } @@ -827,12 +827,14 @@ const item &item_contents::only_item() const return null_item_reference(); } for( const item_pocket &pocket : contents ) { - if( pocket.empty() || !pocket.is_type( item_pocket::pocket_type::CONTAINER ) ) { + if( pocket.empty() || !( pocket.is_type( item_pocket::pocket_type::CONTAINER ) || + pocket.is_type( item_pocket::pocket_type::SOFTWARE ) ) ) { continue; } // the first item we come to is the only one. return pocket.front(); } + return null_item_reference(); } From 80bf30d6f706d2de190b1e609ac4fed740141fd0 Mon Sep 17 00:00:00 2001 From: Andrey Bienkowski Date: Tue, 30 Jun 2020 18:52:35 +0300 Subject: [PATCH 024/420] Modernize more volume and weight --- data/core/basic.json | 4 +- data/legacy/1/obsolete.json | 116 +++++++++--------- data/legacy/3/crazy.json | 8 +- data/legacy/4/boots.json | 92 +++++++------- data/legacy/5/vehicle_parts.json | 4 +- data/mods/Aftershock/items/comestibles.json | 2 +- .../mods/CRT_EXPANSION/items/crt_gunmods.json | 2 +- .../items/crt_makeshift_survival.json | 8 +- data/mods/DinoMod/egg.json | 4 +- data/mods/DinoMod/monsters/dinosaur.json | 2 +- data/mods/Generic_Guns/magazines/grenade.json | 2 +- data/mods/Magiclysm/items/alchemy_items.json | 6 +- data/mods/Magiclysm/items/books_lore.json | 2 +- data/mods/Magiclysm/items/enchanted_misc.json | 2 +- .../mods/Magiclysm/items/enchanted_rings.json | 8 +- data/mods/Magiclysm/items/ethereal_items.json | 8 +- 16 files changed, 135 insertions(+), 135 deletions(-) diff --git a/data/core/basic.json b/data/core/basic.json index 8ea36066cbbf7..b825d6e455bfe 100644 --- a/data/core/basic.json +++ b/data/core/basic.json @@ -12,8 +12,8 @@ "category": "food", "name": { "str_sp": "water" }, "description": "Water, the stuff of life, the best thirst-quencher available. It would be safer to drink once purified.", - "weight": 250, - "volume": 1, + "weight": "250 g", + "volume": "250 ml", "price": 50, "symbol": "~", "color": "light_blue", diff --git a/data/legacy/1/obsolete.json b/data/legacy/1/obsolete.json index ae5e8e6bda3fd..fa27ed1399e39 100644 --- a/data/legacy/1/obsolete.json +++ b/data/legacy/1/obsolete.json @@ -8,8 +8,8 @@ "color": "yellow", "description": "Conical Ball .22 is a variety of .22 ammunition with a very small propellant charge and generally lacks gunpowder. The end result is a subsonic round. It is nearly silent, but is so weak as to be nearly useless. This one has been handmade using an intact casing.", "material": [ "steel", "powder" ], - "volume": 1, - "weight": 3, + "volume": "250 ml", + "weight": "3 g", "bashing": 1, "ammo_type": "22", "casing": "22_casing", @@ -30,8 +30,8 @@ "color": "yellow", "description": "Rat-shot is extremely weak ammunition, designed for killing rats, snakes, or other small vermin while being unable to damage walls. It has an extremely short range and is unable to injure all but the smallest creatures. This one has been handmade using an intact casing.", "material": [ "steel", "powder" ], - "volume": 1, - "weight": 3, + "volume": "250 ml", + "weight": "3 g", "bashing": 1, "ammo_type": "22", "casing": "22_casing", @@ -52,8 +52,8 @@ "color": "red", "description": "A hand-reloaded beanbag round for shotguns, not deadly but designed to disable.", "material": [ "plastic", "powder" ], - "volume": 1, - "weight": 46, + "volume": "250 ml", + "weight": "46 g", "ammo_type": "shot", "casing": "shot_hull", "damage": 8, @@ -72,8 +72,8 @@ "description": "A homemade, rechargeable power cell built from salvaged electronics. With enough electronics skill, you could attach it to an electric-powered device to provide it with energy. The power cell is not compatible with standard batteries; it must be re-energized via a special recharging station.", "price": 2000, "material": [ "plastic", "aluminum" ], - "weight": 142, - "volume": 1, + "weight": "142 g", + "volume": "250 ml", "to_hit": -2, "category": "spare_parts" }, @@ -82,7 +82,7 @@ "id": "brew_bum_wine", "name": "cheap wine must", "description": "Unfermented grape wine. A hearty mix of pressed grapes, with some added brandy to fortify it.", - "weight": 46, + "weight": "46 g", "color": "red", "container": "jug_plastic", "flags": [ "TRADER_AVOID" ], @@ -92,7 +92,7 @@ "fun": -15, "price": 0, "material": [ "fruit", "water" ], - "volume": 1, + "volume": "250 ml", "charges": 7, "phase": "liquid", "comestible_type": "DRINK", @@ -110,8 +110,8 @@ "flags": [ "RELOAD_ONE", "NEVER_JAMS", "RELOAD_EJECT" ], "skill": "shotgun", "ammo": [ "shot" ], - "weight": 1814, - "volume": 4, + "weight": "1814 g", + "volume": "1 L", "bashing": 12, "to_hit": -1, "ranged_damage": 4, @@ -146,8 +146,8 @@ "flags": [ "RELOAD_ONE", "NEVER_JAMS", "RELOAD_EJECT" ], "skill": "shotgun", "ammo": [ "shot" ], - "weight": 1133, - "volume": 6, + "weight": "1133 g", + "volume": "1500 ml", "bashing": 12, "to_hit": -1, "ranged_damage": 1, @@ -166,12 +166,12 @@ "name": "spare magazine", "description": "A spare magazine you can keep on hand to make reloads faster, but must itself be reloaded before it can be used again.", "location": "accessories", - "weight": 330, + "weight": "330 g", "color": "light_gray", "symbol": ":", "material": [ "steel", "plastic" ], "mod_targets": [ "pistol", "shotgun", "smg", "rifle" ], - "volume": 1, + "volume": "250 ml", "price": 5000 }, { @@ -181,13 +181,13 @@ "clip_size_modifier": 50, "description": "Increases the ammunition capacity of your firearm by 50%.", "location": "magazine", - "weight": 495, + "weight": "495 g", "to_hit": -1, "color": "light_gray", "symbol": ":", "material": [ "steel", "plastic" ], "mod_targets": [ "pistol", "shotgun", "smg", "rifle" ], - "volume": 1, + "volume": "250 ml", "price": 5600 }, { @@ -197,12 +197,12 @@ "clip_size_modifier": 100, "description": "Completely doubles the ammunition capacity of your firearm.", "location": "magazine", - "weight": 660, + "weight": "660 g", "color": "light_gray", "symbol": ":", "material": [ "steel", "plastic" ], "mod_targets": [ "shotgun", "smg", "rifle", "pistol" ], - "volume": 2, + "volume": "500 ml", "price": 7200 }, { @@ -215,8 +215,8 @@ "description": "10 plastic bags, folded smooth and wrapped tightly together with a string.", "price": 500, "material": [ "plastic" ], - "weight": 10, - "volume": 1, + "weight": "10 g", + "volume": "250 ml", "to_hit": 1 }, { @@ -227,12 +227,12 @@ "dispersion_modifier": 90, "damage_modifier": 6, "description": "This is a complete conversion kit, designed to turn a rifle into a powerful battle rifle. It reduces accuracy, and increases noise and recoil, but also increases damage and fire rate.", - "weight": 589, + "weight": "589 g", "color": "magenta", "symbol": ":", "material": [ "steel" ], "mod_targets": [ "rifle" ], - "volume": 2, + "volume": "500 ml", "integral_volume": 0, "burst_modifier": 4, "recoil_modifier": 60, @@ -248,12 +248,12 @@ "dispersion_modifier": -150, "damage_modifier": 8, "description": "This is a complete conversion kit, designed to turn a rifle into a deadly sniper rifle. It removes any automatic fire capabilities but also increases accuracy and damage.", - "weight": 589, + "weight": "589 g", "color": "green", "symbol": ":", "material": [ "steel" ], "mod_targets": [ "rifle" ], - "volume": 2, + "volume": "500 ml", "integral_volume": 0, "burst_modifier": -99, "price": 66000, @@ -270,8 +270,8 @@ "description": "A large block of semi-processed coal.", "price": 60000, "material": [ "stone" ], - "weight": 13840, - "volume": 72, + "weight": "13840 g", + "volume": "18 L", "bashing": 8, "to_hit": -5 }, @@ -285,8 +285,8 @@ "color": "dark_gray", "description": "These are smaller pieces of charcoal used in smaller heating devices.", "material": [ "wood" ], - "volume": 1, - "weight": 22, + "volume": "250 ml", + "weight": "22 g", "phase": "solid", "ammo_type": "charcoal", "count": 10, @@ -302,8 +302,8 @@ "color": "dark_gray", "description": "These are smaller pieces of coal used in smaller heating devices.", "material": [ "wood" ], - "volume": 1, - "weight": 22, + "volume": "250 ml", + "weight": "22 g", "ammo_type": "charcoal", "count": 10, "stack_size": 20 @@ -317,8 +317,8 @@ "color": "white", "description": "Also known as a flight, this item provides aerodynamic stabilization of arrows. You'll need to put these on your arrows to make them functional.", "material": [ "paper" ], - "volume": 1, - "weight": 1, + "volume": "250 ml", + "weight": "1 g", "bashing": 1, "ammo_type": "components", "count": 10, @@ -328,7 +328,7 @@ "type": "ARMOR", "id": "helmet_netting", "name": "helmet netting", - "weight": 25, + "weight": "25 g", "color": "green", "covers": [ "HEAD" ], "to_hit": -1, @@ -337,7 +337,7 @@ "flags": [ "OVERSIZE", "BELTED", "WATER_FRIENDLY" ], "price": 2000, "material": [ "cotton" ], - "volume": 0, + "volume": "0 L", "coverage": 12, "material_thickness": 1 }, @@ -350,8 +350,8 @@ "category": "other", "description": "A small metal tank for holding gas or liquids. Useful for crafting.", "price": 6000, - "weight": 3200, - "volume": 45, + "weight": "3200 g", + "volume": "11250 ml", "bashing": 5, "to_hit": -3, "material": [ "steel" ] @@ -367,8 +367,8 @@ "material": [ "steel", "plastic" ], "flags": [ "CHARGE", "NO_UNLOAD", "NEVER_JAMS", "NO_UNLOAD" ], "skill": "rifle", - "weight": 1814, - "volume": 13, + "weight": "1814 g", + "volume": "3250 ml", "bashing": 8, "to_hit": -1, "dispersion": 10, @@ -396,8 +396,8 @@ "color": "yellow", "description": "A single capacitor charged with current to be used by a laser weapon or similar armament.", "material": [ "steel" ], - "volume": 1, - "weight": 5 + "volume": "250 ml", + "weight": "5 g" }, { "id": "inhaler_stimgas", @@ -405,8 +405,8 @@ "comestible_type": "MED", "name": "stimgas inhaler", "description": "A powerful stimulant with no risk of addiction.", - "weight": 10, - "volume": 1, + "weight": "10 g", + "volume": "250 ml", "price": 750, "charges": 25, "material": [ "plastic" ], @@ -422,8 +422,8 @@ "comestible_type": "MED", "name": "sewagas inhaler", "description": "A powerful hallucinogen with low risk of addiction.", - "weight": 10, - "volume": 1, + "weight": "10 g", + "volume": "250 ml", "price": 500, "charges": 25, "material": [ "plastic" ], @@ -451,8 +451,8 @@ "flags": [ "RELOAD_AND_SHOOT", "NEVER_JAMS", "PRIMITIVE_RANGED_WEAPON" ], "skill": "archery", "ammo": [ "dart" ], - "weight": 440, - "volume": 4, + "weight": "440 g", + "volume": "1 L", "bashing": 8, "to_hit": -2, "dispersion": 75, @@ -470,8 +470,8 @@ "color": "yellow", "description": "A handful of darts, useful as ammunition for blowguns.", "material": [ "wood" ], - "volume": 1, - "weight": 1, + "volume": "250 ml", + "weight": "1 g", "ammo_type": "dart", "damage": 1, "range": 30, @@ -490,8 +490,8 @@ "description": "An electrode plate from a lead-acid battery.", "price": 10, "material": [ "steel" ], - "weight": 400, - "volume": 1, + "weight": "400 g", + "volume": "250 ml", "bashing": 4, "to_hit": -2 }, @@ -501,8 +501,8 @@ "name": "active nail bomb", "description": "This is an active nail bomb, likely to explode any second now. Better throw it!", "category": "weapons", - "weight": 290, - "volume": 2, + "weight": "290 g", + "volume": "500 ml", "price": 0, "material": [ "steel", "plastic" ], "symbol": "*", @@ -528,8 +528,8 @@ "name": "forged sword", "description": "A common short sword, forged from several pieces of steel. The pointy end is the dangerous one.", "material": [ "steel" ], - "volume": 8, - "weight": 700, + "volume": "2 L", + "weight": "700 g", "bashing": 4, "cutting": 14, "to_hit": 2, @@ -546,8 +546,8 @@ "category": "other", "material": [ "wood" ], "flags": [ "NO_SALVAGE", "TRADER_AVOID" ], - "weight": 6, - "volume": 0, + "weight": "6 g", + "volume": "0 L", "to_hit": -5, "qualities": [ [ "COOK", 1 ] ] }, diff --git a/data/legacy/3/crazy.json b/data/legacy/3/crazy.json index c1b2cef436118..4ddad28328f6f 100644 --- a/data/legacy/3/crazy.json +++ b/data/legacy/3/crazy.json @@ -9,8 +9,8 @@ "category": "weapons", "name": "Granade", "description": "Attached to this grenade is a name tag with the name Kevin written on it. Does not seem to work like a grenade, handle with care.", - "weight": 180, - "volume": 1, + "weight": "180 g", + "volume": "250 ml", "price": 40000, "to_hit": -1, "bashing": 6, @@ -25,8 +25,8 @@ "category": "weapons", "name": "active Granade", "description": "Attached to this grenade is a name tag with the name Kevin written on it. Does not seem to work like a grenade, handle with care. Better throw it!", - "weight": 180, - "volume": 1, + "weight": "180 g", + "volume": "250 ml", "price": 0, "to_hit": -1, "bashing": 6, diff --git a/data/legacy/4/boots.json b/data/legacy/4/boots.json index 368d17a085ae2..36d9718eb42b8 100644 --- a/data/legacy/4/boots.json +++ b/data/legacy/4/boots.json @@ -4,8 +4,8 @@ "type": "ARMOR", "name": { "str": "pair of boots", "str_pl": "pairs of boots" }, "description": "Tough leather boots. Very durable.", - "weight": 1060, - "volume": 10, + "weight": "1060 g", + "volume": "2500 ml", "price": 10000, "to_hit": -1, "bashing": 1, @@ -43,8 +43,8 @@ "category": "armor", "name": { "str": "pair of bone armor boots", "str_pl": "pairs of bone armor boots" }, "description": "Leather boots armored with reinforcements made from bone. Light and strong.", - "weight": 1824, - "volume": 17, + "weight": "1824 g", + "volume": "4250 ml", "price": 13500, "to_hit": -1, "bashing": 4, @@ -82,8 +82,8 @@ "category": "armor", "name": { "str": "pair of turnout boots", "str_pl": "pairs of turnout boots" }, "description": "A pair of steel-toed rubber boots, the sort worn by firefighters. Highly resistant to heat and flame, they provide excellent protection from injury.", - "weight": 1930, - "volume": 14, + "weight": "1930 g", + "volume": "3500 ml", "price": 13000, "to_hit": 2, "material": [ "plastic", "nomex" ], @@ -120,8 +120,8 @@ "category": "armor", "name": { "str": "pair of chitinous boots", "str_pl": "pairs of chitinous boots" }, "description": "Boots made from the exoskeletons of insects. Light and durable.", - "weight": 1620, - "volume": 17, + "weight": "1620 g", + "volume": "4250 ml", "price": 13500, "to_hit": -1, "bashing": 4, @@ -159,8 +159,8 @@ "category": "armor", "name": { "str": "pair of combat boots", "str_pl": "pairs of combat boots" }, "description": "Modern reinforced tactical combat boots. Very durable.", - "weight": 1060, - "volume": 8, + "weight": "1060 g", + "volume": "2 L", "price": 7000, "to_hit": -1, "bashing": 1, @@ -198,8 +198,8 @@ "category": "armor", "name": { "str": "pair of survivor fireboots", "str_pl": "pairs of survivor fireboots" }, "description": "A pair of customized, Kevlar armored Nomex boots, modified to provide maximum protection from harm and the elements, even when knee-deep in the dead.", - "weight": 1980, - "volume": 12, + "weight": "1980 g", + "volume": "3 L", "price": 24000, "to_hit": -1, "bashing": 1, @@ -236,8 +236,8 @@ "type": "ARMOR", "name": { "str": "pair of fur boots", "str_pl": "pairs of fur boots" }, "description": "Boots lined with fur for warmth.", - "weight": 1890, - "volume": 18, + "weight": "1890 g", + "volume": "4500 ml", "price": 14000, "to_hit": -1, "bashing": 1, @@ -275,8 +275,8 @@ "category": "armor", "name": { "str": "pair of survivor wetsuit boots", "str_pl": "pairs of survivor wetsuit boots" }, "description": "A pair of customized, Kevlar armored neoprene boots, modified to provide maximum protection from harm and the elements, even when knee-deep in the dead.", - "weight": 1180, - "volume": 6, + "weight": "1180 g", + "volume": "1500 ml", "price": 24000, "to_hit": -1, "bashing": 1, @@ -313,8 +313,8 @@ "type": "ARMOR", "name": { "str": "pair of hiking boots", "str_pl": "pairs of hiking boots" }, "description": "Tough yet light leather boots. Durable and comfortable.", - "weight": 960, - "volume": 8, + "weight": "960 g", + "volume": "2 L", "price": 14000, "to_hit": -1, "bashing": 1, @@ -352,8 +352,8 @@ "category": "armor", "name": { "str": "pair of heavy survivor boots", "str_pl": "pairs of heavy survivor boots" }, "description": "A pair of customized Kevlar boots, heavily armored with steel and modified to provide maximum protection from harm, even when knee-deep in the dead.", - "weight": 1610, - "volume": 12, + "weight": "1610 g", + "volume": "3 L", "price": 24000, "to_hit": -1, "bashing": 1, @@ -391,8 +391,8 @@ "category": "armor", "name": { "str": "pair of leather armor boots", "str_pl": "pairs of leather armor boots" }, "description": "Thick leather boots made specifically to protect the feet. Light and tough.", - "weight": 902, - "volume": 8, + "weight": "902 g", + "volume": "2 L", "price": 12500, "to_hit": -1, "bashing": 4, @@ -430,8 +430,8 @@ "category": "armor", "name": { "str": "pair of light survivor boots", "str_pl": "pairs of light survivor boots" }, "description": "A pair of customized, Kevlar armored cloth boots, modified to provide maximum protection from harm, even when knee-deep in the dead.", - "weight": 1120, - "volume": 8, + "weight": "1120 g", + "volume": "2 L", "price": 24000, "to_hit": -1, "bashing": 1, @@ -469,8 +469,8 @@ "category": "armor", "name": { "str": "pair of armored boots", "str_pl": "pairs of armored boots" }, "description": "An extremely heavy set of armor plated boots.", - "weight": 1890, - "volume": 13, + "weight": "1890 g", + "volume": "3250 ml", "price": 50000, "to_hit": -2, "bashing": 7, @@ -507,8 +507,8 @@ "type": "ARMOR", "name": { "str": "pair of rubber boots", "str_pl": "pairs of rubber boots" }, "description": "A pair of rubber boots, often used while cleaning with caustic materials.", - "weight": 980, - "volume": 14, + "weight": "980 g", + "volume": "3500 ml", "price": 8000, "to_hit": 2, "material": [ "plastic" ], @@ -544,8 +544,8 @@ "type": "ARMOR", "name": { "str": "pair of steeltoed boots", "str_pl": "pairs of steeltoed boots" }, "description": "Leather boots with a steel toe. Extremely durable.", - "weight": 1320, - "volume": 12, + "weight": "1320 g", + "volume": "3 L", "price": 12000, "to_hit": -1, "bashing": 4, @@ -583,8 +583,8 @@ "category": "armor", "name": { "str": "pair of survivor boots", "str_pl": "pairs of survivor boots" }, "description": "A pair of customized leather boots, armored with Kevlar and modified to provide maximum protection from harm, even when knee-deep in the dead.", - "weight": 1330, - "volume": 10, + "weight": "1330 g", + "volume": "2500 ml", "price": 24000, "to_hit": -1, "bashing": 1, @@ -621,8 +621,8 @@ "type": "ARMOR", "name": { "str": "pair of western boots", "str_pl": "pairs of western boots" }, "description": "Stiff leather boots with intricate embroidery and one-inch heels. They look good, but aren't made for running.", - "weight": 1060, - "volume": 10, + "weight": "1060 g", + "volume": "2500 ml", "price": 15000, "to_hit": -1, "bashing": 1, @@ -659,8 +659,8 @@ "type": "ARMOR", "name": { "str": "pair of winter boots", "str_pl": "pairs of winter boots" }, "description": "Cumbersome boots designed for warmth.", - "weight": 1640, - "volume": 14, + "weight": "1640 g", + "volume": "3500 ml", "price": 7000, "to_hit": -1, "material": [ "wool", "plastic" ], @@ -697,8 +697,8 @@ "category": "armor", "name": { "str": "pair of winter survivor boots", "str_pl": "pairs of winter survivor boots" }, "description": "A pair of customized, Kevlar armored fur boots, modified to provide maximum protection from harm and the elements, even when knee-deep in the dead.", - "weight": 1760, - "volume": 14, + "weight": "1760 g", + "volume": "3500 ml", "price": 24000, "to_hit": -1, "bashing": 1, @@ -736,8 +736,8 @@ "category": "armor", "name": { "str": "pair of XL survivor boots", "str_pl": "pairs of XL survivor boots" }, "description": "A massive pair of customized leather boots, armored with Kevlar and modified to provide maximum protection from harm and the elements, even when knee-deep in the dead.", - "weight": 2460, - "volume": 20, + "weight": "2460 g", + "volume": "5 L", "price": 24000, "to_hit": -1, "bashing": 1, @@ -774,8 +774,8 @@ "type": "ARMOR", "name": { "str": "pair of knee-high boots", "str_pl": "pairs of knee-high boots" }, "description": "Very long leather boots that cover the lower legs. Difficult to wear but extremely durable.", - "weight": 1520, - "volume": 15, + "weight": "1520 g", + "volume": "3750 ml", "price": 8000, "to_hit": -1, "bashing": 1, @@ -812,8 +812,8 @@ "type": "ARMOR", "name": { "str": "pair of rollerblades", "str_pl": "pairs of rollerblades" }, "description": "A pair of inline skates. Very fast on flat floors, but they make it hard to move on rough terrain, or to dodge effectively.", - "weight": 1640, - "volume": 15, + "weight": "1640 g", + "volume": "3750 ml", "price": 8500, "to_hit": -2, "material": [ "plastic", "cotton" ], @@ -849,8 +849,8 @@ "type": "ARMOR", "name": { "str": "pair of rollerskates", "str_pl": "pairs of rollerskates" }, "description": "An old-fashioned pair of leather rollerskates with steel frames. While quite fast on flat floors, they make it difficult to move on rough terrain.", - "weight": 2720, - "volume": 12, + "weight": "2720 g", + "volume": "3 L", "price": 8500, "to_hit": -2, "bashing": 6, diff --git a/data/legacy/5/vehicle_parts.json b/data/legacy/5/vehicle_parts.json index 924fe9d2faf1c..63ba8bcd2170f 100644 --- a/data/legacy/5/vehicle_parts.json +++ b/data/legacy/5/vehicle_parts.json @@ -4,12 +4,12 @@ "id": "v_curtain_item", "name": { "str": "vehicle curtain" }, "description": "A rod, a few metal rings, and a large piece of cloth with some strings attached for securely fastening the edges.", - "weight": 2200, + "weight": "2200 g", "to_hit": -4, "color": "white", "symbol": "\"", "material": [ "cotton", "steel" ], - "volume": 20, + "volume": "5 L", "bashing": 1, "category": "veh_parts", "price": 3500 diff --git a/data/mods/Aftershock/items/comestibles.json b/data/mods/Aftershock/items/comestibles.json index 127bf14320281..635e8bb42b274 100644 --- a/data/mods/Aftershock/items/comestibles.json +++ b/data/mods/Aftershock/items/comestibles.json @@ -12,7 +12,7 @@ "quench": -5, "description": "A crumbly white pill half the size of your thumb, packed with vitamins and calories. Provides a whole day's nutrition in portable form, but it's a little hard to keep down, thanks to the rancid taste and chalky texture.", "price": 10000, - "volume": 0, + "volume": "0 L", "charges": 5, "looks_like": "vitamins", "fun": -8, diff --git a/data/mods/CRT_EXPANSION/items/crt_gunmods.json b/data/mods/CRT_EXPANSION/items/crt_gunmods.json index 5934e0ffedf7c..d766cef8f9de8 100644 --- a/data/mods/CRT_EXPANSION/items/crt_gunmods.json +++ b/data/mods/CRT_EXPANSION/items/crt_gunmods.json @@ -23,7 +23,7 @@ "name": "CQB SI shotgun", "description": "The integrated underbarrel shotgun of this gun which holds a single shot. It's irremovable.", "weight": "2600 g", - "volume": 0, + "volume": "0 L", "price": 95000, "to_hit": -1, "material": [ "steel" ], diff --git a/data/mods/CRT_EXPANSION/items/crt_makeshift_survival.json b/data/mods/CRT_EXPANSION/items/crt_makeshift_survival.json index 1c5364d238df6..7e1a56be307c2 100644 --- a/data/mods/CRT_EXPANSION/items/crt_makeshift_survival.json +++ b/data/mods/CRT_EXPANSION/items/crt_makeshift_survival.json @@ -5,8 +5,8 @@ "category": "armor", "name": "plant fiber tunic", "description": "A loose garment cobbled together from a collection of plant bundles and wound together by makeshift cordage", - "weight": 1520, - "volume": 23, + "weight": "1520 g", + "volume": "5750 ml", "price": 0, "to_hit": -5, "material": [ "dry_plant" ], @@ -25,8 +25,8 @@ "category": "armor", "name": "plant fiber bracelet", "description": "A bracelet wound together by makeshift cordage. Has some cool looking pebbles. ", - "weight": 1520, - "volume": 23, + "weight": "1520 g", + "volume": "5750 ml", "price": 0, "to_hit": -5, "material": [ "dry_plant" ], diff --git a/data/mods/DinoMod/egg.json b/data/mods/DinoMod/egg.json index 615beeec8dfc8..851bf76b47ecc 100644 --- a/data/mods/DinoMod/egg.json +++ b/data/mods/DinoMod/egg.json @@ -3,7 +3,7 @@ "type": "COMESTIBLE", "id": "egg_dino", "name": "dinosaur egg", - "weight": 75, + "weight": "75 g", "color": "green", "spoils_in": "4 days", "comestible_type": "FOOD", @@ -14,7 +14,7 @@ "description": "Pale, football-shaped egg laid by a dinosaur.", "price": 500, "material": [ "egg" ], - "volume": 1, + "volume": "250 ml", "stack_size": 4, "fun": -6, "flags": [ "FREEZERBURN" ], diff --git a/data/mods/DinoMod/monsters/dinosaur.json b/data/mods/DinoMod/monsters/dinosaur.json index 31c3d55a1f949..fe0304a8d9fce 100644 --- a/data/mods/DinoMod/monsters/dinosaur.json +++ b/data/mods/DinoMod/monsters/dinosaur.json @@ -868,7 +868,7 @@ "symbol": "D", "color": "magenta_green", "volume": "400000 ml", - "weight": 400000, + "weight": "400 kg", "material": [ "flesh" ], "aggression": 10, "morale": 30, diff --git a/data/mods/Generic_Guns/magazines/grenade.json b/data/mods/Generic_Guns/magazines/grenade.json index 13d8f2c655acd..d5fc0146d0af8 100644 --- a/data/mods/Generic_Guns/magazines/grenade.json +++ b/data/mods/Generic_Guns/magazines/grenade.json @@ -4,7 +4,7 @@ "type": "MAGAZINE", "name": { "str": "grenade machine gun belt" }, "description": "An ammo belt consisting of metal linkages which separate from the belt upon firing. This one holds grenade cartridges and is too bulky to be worn like other ammo belts.", - "volume": 0, + "volume": "0 L", "price": 0, "material": [ "steel" ], "symbol": "#", diff --git a/data/mods/Magiclysm/items/alchemy_items.json b/data/mods/Magiclysm/items/alchemy_items.json index 1ea8c222106b0..9345100f06711 100644 --- a/data/mods/Magiclysm/items/alchemy_items.json +++ b/data/mods/Magiclysm/items/alchemy_items.json @@ -119,7 +119,7 @@ "description": "The powdered remains of a will-o-wisps's physical form. It seems to still possess an otherworldly glow.", "material": [ "powder" ], "weight": "5 g", - "volume": 1 + "volume": "250 ml" }, { "id": "glow_light", @@ -165,7 +165,7 @@ "description": "The great plates from behind a bulette's head have always been prized for use in shield and armor making.", "material": [ "arcane_skin" ], "weight": "30 kg", - "volume": 12 + "volume": "3 L" }, { "id": "bulette_pearl", @@ -176,7 +176,7 @@ "description": "As a bulette burrows through the earth its gills collect minute amounts of precious metals and gems which slowly aggregate into lustrous gemstones prized for their beauty and power.", "material": [ "stone" ], "weight": "120 g", - "volume": 1 + "volume": "250 ml" }, { "id": "dragon_essence", diff --git a/data/mods/Magiclysm/items/books_lore.json b/data/mods/Magiclysm/items/books_lore.json index d7920c471160f..33b24e508593e 100644 --- a/data/mods/Magiclysm/items/books_lore.json +++ b/data/mods/Magiclysm/items/books_lore.json @@ -43,7 +43,7 @@ "name": "old photo", "description": "A photo of a jovial, old wizard, he seems to be dancing with a coat rack in this basement. There is a stack of suitcases in the background.", "weight": "1 g", - "volume": 0, + "volume": "0 L", "price": 800, "material": [ "paper" ], "symbol": "*", diff --git a/data/mods/Magiclysm/items/enchanted_misc.json b/data/mods/Magiclysm/items/enchanted_misc.json index a31fd6860ca07..39b097220230a 100644 --- a/data/mods/Magiclysm/items/enchanted_misc.json +++ b/data/mods/Magiclysm/items/enchanted_misc.json @@ -53,7 +53,7 @@ "name": { "str": "skeleton key of opening", "str_pl": "skeleton keys of opening" }, "description": "A small gold skeleton key. You can activate it to unlock locked things.", "weight": "20 g", - "volume": 0, + "volume": "0 L", "price": 500000, "material": [ "gold" ], "symbol": "[", diff --git a/data/mods/Magiclysm/items/enchanted_rings.json b/data/mods/Magiclysm/items/enchanted_rings.json index 09ec19b12640e..0cdac26a70129 100644 --- a/data/mods/Magiclysm/items/enchanted_rings.json +++ b/data/mods/Magiclysm/items/enchanted_rings.json @@ -5,7 +5,7 @@ "name": "copper magic ring", "description": "A generic copper magic ring.", "weight": "4 g", - "volume": 0, + "volume": "0 L", "price": 5000, "material": [ "copper" ], "symbol": "[", @@ -21,7 +21,7 @@ "name": "magic ring", "description": "A generic silver magic ring.", "weight": "5 g", - "volume": 0, + "volume": "0 L", "price": 5000, "material": [ "silver" ], "symbol": "[", @@ -37,7 +37,7 @@ "name": "magic ring", "description": "A generic gold magic ring.", "weight": "9 g", - "volume": 0, + "volume": "0 L", "price": 5000, "material": [ "gold" ], "symbol": "[", @@ -53,7 +53,7 @@ "name": "magic ring", "description": "A generic platinum magic ring.", "weight": "11 g", - "volume": 0, + "volume": "0 L", "price": 5000, "material": [ "platinum" ], "symbol": "[", diff --git a/data/mods/Magiclysm/items/ethereal_items.json b/data/mods/Magiclysm/items/ethereal_items.json index 6d3c6a06a4757..2cc6e97e9f495 100644 --- a/data/mods/Magiclysm/items/ethereal_items.json +++ b/data/mods/Magiclysm/items/ethereal_items.json @@ -83,8 +83,8 @@ "category": "tools", "name": "magic light", "description": "A small magical light that you can read by.", - "weight": 0, - "volume": 0, + "weight": "0 kg", + "volume": "0 L", "price": 0, "symbol": ",", "color": "light_green", @@ -234,8 +234,8 @@ "type": "ARMOR", "name": { "str": "flesh pouch", "str_pl": "flesh pouches" }, "description": "A large pouch of tough flesh on your back, filled with tiny tentacles that grasp and hold anything you place inside. It shifts and adjusts itself to minimize encumbrance.", - "weight": 0, - "volume": 0, + "weight": "0 kg", + "volume": "0 L", "price": 0, "material": [ "flesh" ], "symbol": "[", From c3cba8e66e3e22438dfb87933a5d7b03eec95ff6 Mon Sep 17 00:00:00 2001 From: Mark Langsdorf Date: Mon, 29 Jun 2020 18:24:16 -0500 Subject: [PATCH 025/420] options: remove the option to not use z-levels Ground vehicle z-level transitions requires that z-levels be enabled, and getting rid of this option will also allow much of the map code to be dramatically simplified. --- src/game.cpp | 2 +- src/map.h | 2 +- src/options.cpp | 7 ------- tests/ground_destroy_test.cpp | 14 -------------- tests/monster_vision_test.cpp | 3 +-- tests/test_main.cpp | 2 +- 6 files changed, 4 insertions(+), 26 deletions(-) diff --git a/src/game.cpp b/src/game.cpp index 31fe9ede25a93..2581eb5a28f50 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -574,7 +574,7 @@ void game::setup() load_world_modfiles( ui ); - m = map( get_option( "ZLEVELS" ) ); + m = map( true ); next_npc_id = character_id( 1 ); next_mission_id = 1; diff --git a/src/map.h b/src/map.h index 2a209ceaea716..f559c940aebc7 100644 --- a/src/map.h +++ b/src/map.h @@ -212,7 +212,7 @@ class map public: // Constructors & Initialization - map( int mapsize = MAPSIZE, bool zlev = false ); + map( int mapsize = MAPSIZE, bool zlev = true ); map( bool zlev ) : map( MAPSIZE, zlev ) { } virtual ~map(); diff --git a/src/options.cpp b/src/options.cpp index f7c9cc54535f7..096a75d14e8bb 100644 --- a/src/options.cpp +++ b/src/options.cpp @@ -2145,13 +2145,6 @@ void options_manager::add_options_world_default() add_empty_line(); - add( "ZLEVELS", "world_default", translate_marker( "Z-levels" ), - translate_marker( "If true, enables several features related to vertical movement, such as hauling items up stairs, climbing downspouts, and flying aircraft. May cause problems if toggled mid-game." ), - true - ); - - add_empty_line(); - add( "CHARACTER_POINT_POOLS", "world_default", translate_marker( "Character point pools" ), translate_marker( "Allowed point pools for character generation." ), { { "any", translate_marker( "Any" ) }, { "multi_pool", translate_marker( "Multi-pool only" ) }, { "no_freeform", translate_marker( "No freeform" ) } }, diff --git a/tests/ground_destroy_test.cpp b/tests/ground_destroy_test.cpp index 85cbda89f8c5b..2a76bd17fb684 100644 --- a/tests/ground_destroy_test.cpp +++ b/tests/ground_destroy_test.cpp @@ -19,14 +19,11 @@ // Destroying pavement with a pickaxe should not leave t_flat_roof. // See issue #24707: // https://github.com/CleverRaven/Cataclysm-DDA/issues/24707 -// Behavior may depend on ZLEVELS being set. TEST_CASE( "pavement_destroy", "[.]" ) { const ter_id flat_roof_id = ter_id( "t_flat_roof" ); REQUIRE( flat_roof_id != t_null ); - const bool zlevels_set = get_option( "ZLEVELS" ); - INFO( "ZLEVELS is " << zlevels_set ); clear_map_and_put_player_underground(); // Populate the map with pavement. g->m.ter_set( tripoint_zero, ter_id( "t_pavement" ) ); @@ -44,15 +41,11 @@ TEST_CASE( "pavement_destroy", "[.]" ) // Ground-destroying explosions on dirt or grass shouldn't leave t_flat_roof. // See issue #23250: // https://github.com/CleverRaven/Cataclysm-DDA/issues/23250 -// Behavior may depend on ZLEVELS being set. TEST_CASE( "explosion_on_ground", "[.]" ) { ter_id flat_roof_id = ter_id( "t_flat_roof" ); REQUIRE( flat_roof_id != t_null ); - const bool zlevels_set = get_option( "ZLEVELS" ); - INFO( "ZLEVELS is " << zlevels_set ); - clear_map_and_put_player_underground(); std::vector test_terrain_id = { ter_id( "t_dirt" ), ter_id( "t_grass" ) @@ -90,7 +83,6 @@ TEST_CASE( "explosion_on_ground", "[.]" ) // Ground-destroying explosions on t_floor with a t_rock_floor basement // below should create some t_open_air, not just t_flat_roof (which is // the defined roof of a t_rock-floor). -// Behavior depends on ZLEVELS being set. TEST_CASE( "explosion_on_floor_with_rock_floor_basement", "[.]" ) { ter_id flat_roof_id = ter_id( "t_flat_roof" ); @@ -103,9 +95,6 @@ TEST_CASE( "explosion_on_floor_with_rock_floor_basement", "[.]" ) REQUIRE( rock_floor_id != t_null ); REQUIRE( open_air_id != t_null ); - const bool zlevels_set = get_option( "ZLEVELS" ); - INFO( "ZLEVELS is " << zlevels_set ); - clear_map_and_put_player_underground(); const int area_dim = 24; @@ -148,7 +137,6 @@ TEST_CASE( "explosion_on_floor_with_rock_floor_basement", "[.]" ) // Destroying interior floors shouldn't cause the roofs above to collapse. // Destroying supporting walls should cause the roofs above to collapse. -// Behavior may depend on ZLEVELS being set. TEST_CASE( "collapse_checks", "[.]" ) { constexpr int wall_size = 5; @@ -163,8 +151,6 @@ TEST_CASE( "collapse_checks", "[.]" ) REQUIRE( wall_id != t_null ); REQUIRE( open_air_id != t_null ); - const bool zlevels_set = get_option( "ZLEVELS" ); - INFO( "ZLEVELS is " << zlevels_set ); clear_map_and_put_player_underground(); // build a structure diff --git a/tests/monster_vision_test.cpp b/tests/monster_vision_test.cpp index cef8bd52fa32d..b35d863942ea2 100644 --- a/tests/monster_vision_test.cpp +++ b/tests/monster_vision_test.cpp @@ -23,8 +23,7 @@ static const time_point midday = calendar::turn_zero + 12_hours; TEST_CASE( "monsters shouldn't see through floors", "[vision]" ) { - override_option opt( "ZLEVELS", "true" ); - override_option opt2( "FOV_3D", "true" ); + override_option opt( "FOV_3D", "true" ); bool old_fov_3d = fov_3d; fov_3d = true; calendar::turn = midday; diff --git a/tests/test_main.cpp b/tests/test_main.cpp index f0ff30227e652..316df98c4e7e7 100644 --- a/tests/test_main.cpp +++ b/tests/test_main.cpp @@ -144,7 +144,7 @@ static void init_global_game_state( const std::vector &mods, g->u = avatar(); g->u.create( character_type::NOW ); - g->m = map( get_option( "ZLEVELS" ) ); + g->m = map(); overmap_special_batch empty_specials( point_zero ); overmap_buffer.create_custom_overmap( point_zero, empty_specials ); From d7c8c55285cf4257d220f9a0da1c0ccb8be5e402 Mon Sep 17 00:00:00 2001 From: Stephen Pittman Date: Wed, 1 Jul 2020 15:54:41 +1000 Subject: [PATCH 026/420] Use correct typing in inventory_ui.cpp --- src/inventory_ui.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/inventory_ui.cpp b/src/inventory_ui.cpp index 6864339383c0a..87cc0697172ec 100644 --- a/src/inventory_ui.cpp +++ b/src/inventory_ui.cpp @@ -2284,7 +2284,7 @@ drop_locations inventory_iuse_selector::execute() } drop_locations dropped_pos_and_qty; - for( const std::pair &use_pair : to_use ) { + for( const std::pair &use_pair : to_use ) { dropped_pos_and_qty.push_back( std::make_pair( *use_pair.first, use_pair.second ) ); } From add04520dc7cf7ab1e0e8c96d5cb6c57323b2955 Mon Sep 17 00:00:00 2001 From: Kevin Granade Date: Wed, 1 Jul 2020 01:08:36 -0700 Subject: [PATCH 027/420] Test that items in various locations are updated once per tick (#41710) --- tests/active_item_test.cpp | 43 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 tests/active_item_test.cpp diff --git a/tests/active_item_test.cpp b/tests/active_item_test.cpp new file mode 100644 index 0000000000000..18fa63411c168 --- /dev/null +++ b/tests/active_item_test.cpp @@ -0,0 +1,43 @@ +#include "avatar.h" +#include "catch/catch.hpp" +#include "item.h" +#include "map.h" +#include "map_helpers.h" +#include "player_helpers.h" +#include "point.h" + +TEST_CASE( "active_items_processed_regularly", "[item]" ) +{ + clear_avatar(); + clear_map(); + avatar &player_character = get_avatar(); + map &here = get_map(); + // An arbitrary active item that ticks every turn. + item active_item( "firecracker_act", 0, 5 ); + active_item.activate(); + const int active_item_ticks = active_item.charges; + item storage( "backpack", 0 ); + cata::optional::iterator> wear_success = player_character.wear_item( storage ); + REQUIRE( wear_success ); + + item *inventory_item = player_character.try_add( active_item ); + REQUIRE( inventory_item != nullptr ); + REQUIRE( inventory_item->charges == active_item_ticks ); + + bool wield_success = player_character.wield( active_item ); + REQUIRE( wield_success ); + REQUIRE( player_character.weapon.charges == active_item_ticks ); + + here.add_item( player_character.pos(), active_item ); + REQUIRE( here.i_at( player_character.pos() ).only_item().charges == active_item_ticks ); + // TODO: spawn a vehicle and stash a firecracker in there too. + + // Call item processing entry points. + here.process_items(); + player_character.process_items(); + + const int expected_ticks = active_item_ticks - 1; + CHECK( inventory_item->charges == expected_ticks ); + CHECK( player_character.weapon.charges == expected_ticks ); + CHECK( here.i_at( player_character.pos() ).only_item().charges == expected_ticks ); +} From 4fbcd8a7d6c47b0d7bf153dd6a9cf0a3a6d11c9a Mon Sep 17 00:00:00 2001 From: Andrey Bienkowski Date: Wed, 1 Jul 2020 12:57:34 +0300 Subject: [PATCH 028/420] Modernize more volume and weight --- data/legacy/1/obsolete.json | 4 ++-- data/mods/CRT_EXPANSION/items/crt_gunmods.json | 6 +++--- data/mods/Generic_Guns/firearms/pistol_magnum.json | 2 +- data/mods/Magiclysm/items/enchanted_gunmods.json | 6 +++--- data/mods/Magiclysm/items/enchanted_ranged.json | 2 +- data/mods/Magiclysm/items/enchanted_unarmed.json | 2 +- 6 files changed, 11 insertions(+), 11 deletions(-) diff --git a/data/legacy/1/obsolete.json b/data/legacy/1/obsolete.json index fa27ed1399e39..d42cc839b842f 100644 --- a/data/legacy/1/obsolete.json +++ b/data/legacy/1/obsolete.json @@ -233,7 +233,7 @@ "material": [ "steel" ], "mod_targets": [ "rifle" ], "volume": "500 ml", - "integral_volume": 0, + "integral_volume": "0 L", "burst_modifier": 4, "recoil_modifier": 60, "price": 68000, @@ -254,7 +254,7 @@ "material": [ "steel" ], "mod_targets": [ "rifle" ], "volume": "500 ml", - "integral_volume": 0, + "integral_volume": "0 L", "burst_modifier": -99, "price": 66000, "min_skills": [ [ "gun", 8 ] ], diff --git a/data/mods/CRT_EXPANSION/items/crt_gunmods.json b/data/mods/CRT_EXPANSION/items/crt_gunmods.json index d766cef8f9de8..605911a93c984 100644 --- a/data/mods/CRT_EXPANSION/items/crt_gunmods.json +++ b/data/mods/CRT_EXPANSION/items/crt_gunmods.json @@ -43,7 +43,7 @@ "description": ", A military-grade stock which folds reducing the guns volume. The weight and the pivoting hook which latches onto your forearm allows for greater stability. ", "weight": "500 g", "volume": "125 ml", - "integral_volume": 0, + "integral_volume": "0 L", "price": 42000, "material": [ "plastic", "steel" ], "symbol": ":", @@ -74,7 +74,7 @@ "description": "A compact flashlight which is mounted to the side of your weapon, not powerful, but good enough for tight hallways.", "weight": "250 g", "volume": "125 ml", - "integral_volume": 0, + "integral_volume": "0 L", "price": 42000, "material": [ "plastic", "steel" ], "symbol": ":", @@ -102,7 +102,7 @@ "description": "A compact flashlight which is attached to the side of your weapon, not powerful, but good enough for tight hallways.", "weight": "250 g", "volume": "125 ml", - "integral_volume": 0, + "integral_volume": "0 L", "price": 42000, "material": [ "plastic", "steel" ], "symbol": ":", diff --git a/data/mods/Generic_Guns/firearms/pistol_magnum.json b/data/mods/Generic_Guns/firearms/pistol_magnum.json index f1079f276cb95..3c63d886dd77f 100644 --- a/data/mods/Generic_Guns/firearms/pistol_magnum.json +++ b/data/mods/Generic_Guns/firearms/pistol_magnum.json @@ -44,7 +44,7 @@ "durability": 6, "blackpowder_tolerance": 60, "loudness": 25, - "barrel_length": 1, + "barrel_length": "250 ml", "valid_mod_locations": [ [ "accessories", 2 ], [ "muzzle", 1 ], diff --git a/data/mods/Magiclysm/items/enchanted_gunmods.json b/data/mods/Magiclysm/items/enchanted_gunmods.json index 3ac8615b79ec5..073a736aab194 100644 --- a/data/mods/Magiclysm/items/enchanted_gunmods.json +++ b/data/mods/Magiclysm/items/enchanted_gunmods.json @@ -29,7 +29,7 @@ "description": "A small visible-light laser using light shown through a mana crystal that mounts on a firearm's accessory rail to enhance ease and speed of target acquisition. Aside from increased weight, there are no drawbacks. You can rotate the attachment rail to fit under the barrel.", "weight": "108 g", "volume": "250 ml", - "integral_volume": 0, + "integral_volume": "0 L", "price": 36000, "material": [ "plastic", "steel" ], "symbol": ":", @@ -48,7 +48,7 @@ "description": "A small visible-light laser using light shown through a mana crystal that mounts on a firearm's accessory rail to enhance ease and speed of target acquisition. Aside from increased weight, there are no drawbacks. You can rotate the attachment rail to fit on the rail.", "weight": "108 g", "volume": "250 ml", - "integral_volume": 0, + "integral_volume": "0 L", "price": 36000, "material": [ "plastic", "steel" ], "symbol": ":", @@ -68,7 +68,7 @@ "description": "Adds a blue dot optic made from crystallized mana to the top of your gun, replacing the iron sights. Increases accuracy and weight.", "weight": "270 g", "volume": "250 ml", - "integral_volume": 0, + "integral_volume": "0 L", "price": 68000, "material": [ "plastic", "steel" ], "symbol": ":", diff --git a/data/mods/Magiclysm/items/enchanted_ranged.json b/data/mods/Magiclysm/items/enchanted_ranged.json index 9669ff05c6055..f3b5d1af3e5f6 100644 --- a/data/mods/Magiclysm/items/enchanted_ranged.json +++ b/data/mods/Magiclysm/items/enchanted_ranged.json @@ -85,7 +85,7 @@ "modes": [ [ "DEFAULT", "single", 1 ] ], "ammo": [ "shot" ], "reload": 200, - "barrel_length": 2, + "barrel_length": "500 ml", "valid_mod_locations": [ [ "mechanism", 1 ] ], "reload_noise": "chuk chuk.", "flags": [ "RELOAD_ONE", "PUMP_ACTION", "DURABLE_MELEE", "SHEATH_SWORD" ], diff --git a/data/mods/Magiclysm/items/enchanted_unarmed.json b/data/mods/Magiclysm/items/enchanted_unarmed.json index 6daa637744fae..1081895966618 100644 --- a/data/mods/Magiclysm/items/enchanted_unarmed.json +++ b/data/mods/Magiclysm/items/enchanted_unarmed.json @@ -88,7 +88,7 @@ "range": -8, "modes": [ [ "DEFAULT", "single", 1 ], [ "DOUBLE", "double", 2 ] ], "reload": 200, - "barrel_length": 2, + "barrel_length": "500 ml", "valid_mod_locations": [ [ "mechanism", 1 ] ], "flags": [ "UNARMED_WEAPON", "DURABLE_MELEE", "NEVER_JAMS", "RELOAD_EJECT", "RELOAD_ONE" ], "faults": [ "fault_gun_blackpowder", "fault_gun_dirt" ], From 501f14189b541d03cdd0390c97b07f54eb952758 Mon Sep 17 00:00:00 2001 From: Kevin Granade Date: Wed, 1 Jul 2020 04:14:36 -0700 Subject: [PATCH 029/420] Map reference migration part nine (#41717) * Map reference migration part nine * Downgrade avatar references to Character Reducing inclusions of avatar.h and player.h --- src/avatar_action.cpp | 2 +- src/avatar_action.h | 5 +- src/descriptions.cpp | 2 +- src/inventory_ui.cpp | 56 ++++++------ src/inventory_ui.h | 17 ++-- src/kill_tracker.cpp | 4 +- src/magic.cpp | 25 +++--- src/map.cpp | 6 +- src/map.h | 2 +- src/mondefense.cpp | 9 +- src/monexamine.cpp | 150 ++++++++++++++++++-------------- src/mutation.cpp | 13 +-- src/npctalk.cpp | 19 ++-- src/overmapbuffer.cpp | 27 +++--- src/pickup.cpp | 27 +++--- src/pixel_minimap.cpp | 7 +- src/sounds.cpp | 24 ++--- src/turret.cpp | 12 ++- src/vehicle.h | 2 +- src/weather.cpp | 18 ++-- tests/encumbrance_test.cpp | 17 ++-- tests/item_location_test.cpp | 6 +- tests/monster_test.cpp | 18 ++-- tests/npc_talk_test.cpp | 30 +++---- tests/player_test.cpp | 20 ++--- tests/projectile_test.cpp | 7 +- tests/vehicle_drag_test.cpp | 4 +- tests/vehicle_power_test.cpp | 5 +- tests/vehicle_test.cpp | 2 +- tests/vehicle_turrets_test.cpp | 4 +- tests/visitable_remove_test.cpp | 33 ++++--- 31 files changed, 306 insertions(+), 267 deletions(-) diff --git a/src/avatar_action.cpp b/src/avatar_action.cpp index 20ee21ace8e63..bd71cca6d6d68 100644 --- a/src/avatar_action.cpp +++ b/src/avatar_action.cpp @@ -838,7 +838,7 @@ void avatar_action::fire_wielded_weapon( avatar &you ) you.assign_activity( aim_activity_actor::use_wielded(), false ); } -void avatar_action::fire_ranged_mutation( avatar &you, const item &fake_gun ) +void avatar_action::fire_ranged_mutation( Character &you, const item &fake_gun ) { you.assign_activity( aim_activity_actor::use_mutation( fake_gun ), false ); } diff --git a/src/avatar_action.h b/src/avatar_action.h index fbed8231365f9..c2715f1cd6f7d 100644 --- a/src/avatar_action.h +++ b/src/avatar_action.h @@ -6,12 +6,13 @@ #include "point.h" #include "units.h" +class aim_activity_actor; class avatar; +class Character; class item; class item_location; class map; class turret_data; -class aim_activity_actor; namespace avatar_action { @@ -53,7 +54,7 @@ bool can_fire_weapon( avatar &you, const map &m, const item &weapon ); void fire_wielded_weapon( avatar &you ); /** Stores fake gun specified by the mutation and starts interactive aiming */ -void fire_ranged_mutation( avatar &you, const item &fake_gun ); +void fire_ranged_mutation( Character &you, const item &fake_gun ); /** Stores fake gun specified by the bionic and starts interactive aiming */ void fire_ranged_bionic( avatar &you, const item &fake_gun, const units::energy &cost_per_shot ); diff --git a/src/descriptions.cpp b/src/descriptions.cpp index 01210d004a48a..d8b7e9ffbecb6 100644 --- a/src/descriptions.cpp +++ b/src/descriptions.cpp @@ -155,7 +155,7 @@ std::string map_data_common_t::extended_description() const if( has_any_harvest ) { ss << "--" << std::endl; - int player_skill = get_avatar().get_skill_level( skill_survival ); + int player_skill = get_player_character().get_skill_level( skill_survival ); ss << _( "You could harvest the following things from it:" ) << std::endl; // Group them by identical ids to avoid repeating same blocks of data // First, invert the mapping: season->id to id->seasons diff --git a/src/inventory_ui.cpp b/src/inventory_ui.cpp index 87cc0697172ec..1ea1e2590f965 100644 --- a/src/inventory_ui.cpp +++ b/src/inventory_ui.cpp @@ -1,12 +1,10 @@ #include "inventory_ui.h" -#include "avatar.h" #include "cata_utility.h" #include "catacharset.h" #include "character.h" #include "colony.h" #include "debug.h" -#include "game.h" #include "ime.h" #include "inventory.h" #include "item.h" @@ -18,7 +16,6 @@ #include "optional.h" #include "options.h" #include "output.h" -#include "player.h" #include "point.h" #include "string_formatter.h" #include "string_id.h" @@ -114,10 +111,11 @@ class selection_column_preset : public inventory_selector_preset } nc_color get_color( const inventory_entry &entry ) const override { + Character &player_character = get_player_character(); if( entry.is_item() ) { - if( &*entry.any_item() == &g->u.weapon ) { + if( &*entry.any_item() == &player_character.weapon ) { return c_light_blue; - } else if( g->u.is_worn( *entry.any_item() ) ) { + } else if( player_character.is_worn( *entry.any_item() ) ) { return c_cyan; } } @@ -171,7 +169,7 @@ nc_color inventory_entry::get_invlet_color() const { if( !is_selectable() ) { return c_dark_gray; - } else if( g->u.inv.assigned_invlet.count( get_invlet() ) ) { + } else if( get_player_character().inv.assigned_invlet.count( get_invlet() ) ) { return c_yellow; } else { return c_white; @@ -232,9 +230,10 @@ inventory_selector_preset::inventory_selector_preset() bool inventory_selector_preset::sort_compare( const inventory_entry &lhs, const inventory_entry &rhs ) const { + Character &player_character = get_player_character(); // Place items with an assigned inventory letter first, since the player cared enough to assign them - const bool left_fav = g->u.inv.assigned_invlet.count( lhs.any_item()->invlet ); - const bool right_fav = g->u.inv.assigned_invlet.count( rhs.any_item()->invlet ); + const bool left_fav = player_character.inv.assigned_invlet.count( lhs.any_item()->invlet ); + const bool right_fav = player_character.inv.assigned_invlet.count( rhs.any_item()->invlet ); if( left_fav == right_fav ) { return lhs.cached_name.compare( rhs.cached_name ) < 0; // Simple alphabetic order } else if( left_fav ) { @@ -637,17 +636,21 @@ void inventory_column::set_stack_favorite( const item_location &location, bool f { const item *selected_item = location.get_item(); std::list to_favorite; + map &here = get_map(); + Character &player_character = get_player_character(); if( location.where() == item_location::type::character ) { - int position = g->u.get_item_position( selected_item ); + int position = player_character.get_item_position( selected_item ); if( position < 0 ) { - g->u.i_at( position ).set_favorite( !selected_item->is_favorite ); // worn/wielded + // worn/wielded + player_character.i_at( position ).set_favorite( !selected_item->is_favorite ); } else { - g->u.inv.set_stack_favorite( position, !selected_item->is_favorite ); // in inventory + // in inventory + player_character.inv.set_stack_favorite( position, !selected_item->is_favorite ); } } else if( location.where() == item_location::type::map ) { - map_stack items = g->m.i_at( location.position() ); + map_stack items = here.i_at( location.position() ); for( auto &item : items ) { if( item.stacks_with( *selected_item ) ) { @@ -658,7 +661,7 @@ void inventory_column::set_stack_favorite( const item_location &location, bool f item->set_favorite( favorite ); } } else if( location.where() == item_location::type::vehicle ) { - const cata::optional vp = g->m.veh_at( + const cata::optional vp = here.veh_at( location.position() ).part_with_feature( "CARGO", true ); assert( vp ); @@ -916,7 +919,7 @@ size_t inventory_column::get_entry_indent( const inventory_entry &entry ) const return res; } -int inventory_column::reassign_custom_invlets( const player &p, int min_invlet, int max_invlet ) +int inventory_column::reassign_custom_invlets( const Character &p, int min_invlet, int max_invlet ) { int cur_invlet = min_invlet; for( auto &elem : entries ) { @@ -1324,9 +1327,10 @@ void inventory_selector::add_character_items( Character &character ) void inventory_selector::add_map_items( const tripoint &target ) { - if( g->m.accessible_items( target ) ) { - map_stack items = g->m.i_at( target ); - const std::string name = to_upper_case( g->m.name( target ) ); + map &here = get_map(); + if( here.accessible_items( target ) ) { + map_stack items = here.i_at( target ); + const std::string name = to_upper_case( here.name( target ) ); const item_category map_cat( name, no_translation( name ), 100 ); add_items( map_column, [ &target ]( item * it ) { @@ -1342,7 +1346,8 @@ void inventory_selector::add_map_items( const tripoint &target ) void inventory_selector::add_vehicle_items( const tripoint &target ) { - const cata::optional vp = g->m.veh_at( target ).part_with_feature( "CARGO", true ); + const cata::optional vp = + get_map().veh_at( target ).part_with_feature( "CARGO", true ); if( !vp ) { return; } @@ -1367,9 +1372,10 @@ void inventory_selector::add_vehicle_items( const tripoint &target ) void inventory_selector::add_nearby_items( int radius ) { if( radius >= 0 ) { + map &here = get_map(); for( const tripoint &pos : closest_tripoints_first( u.pos(), radius ) ) { // can not reach this -> can not access its contents - if( u.pos() != pos && !g->m.clear_path( u.pos(), pos, rl_dist( u.pos(), pos ), 1, 100 ) ) { + if( u.pos() != pos && !here.clear_path( u.pos(), pos, rl_dist( u.pos(), pos ), 1, 100 ) ) { continue; } add_map_items( pos ); @@ -1828,7 +1834,7 @@ void inventory_selector::draw_footer( const catacurses::window &w ) const } } -inventory_selector::inventory_selector( player &u, const inventory_selector_preset &preset ) +inventory_selector::inventory_selector( Character &u, const inventory_selector_preset &preset ) : u( u ) , preset( preset ) , ctxt( "INVENTORY" ) @@ -2125,7 +2131,7 @@ item_location inventory_pick_selector::execute() } } -inventory_multiselector::inventory_multiselector( player &p, +inventory_multiselector::inventory_multiselector( Character &p, const inventory_selector_preset &preset, const std::string &selection_column_title ) : inventory_selector( p, preset ), @@ -2147,7 +2153,7 @@ void inventory_multiselector::rearrange_columns( size_t client_width ) selection_col->set_visibility( !is_overflown( client_width ) ); } -inventory_compare_selector::inventory_compare_selector( player &p ) : +inventory_compare_selector::inventory_compare_selector( Character &p ) : inventory_multiselector( p, default_preset, _( "ITEMS TO COMPARE" ) ) {} std::pair inventory_compare_selector::execute() @@ -2218,7 +2224,7 @@ void inventory_compare_selector::toggle_entry( inventory_entry *entry ) } inventory_iuse_selector::inventory_iuse_selector( - player &p, + Character &p, const std::string &selector_title, const inventory_selector_preset &preset, const GetStats &get_st @@ -2326,7 +2332,7 @@ inventory_selector::stats inventory_iuse_selector::get_raw_stats() const return stats{{ stat{{ "", "", "", "" }}, stat{{ "", "", "", "" }} }}; } -inventory_drop_selector::inventory_drop_selector( player &p, +inventory_drop_selector::inventory_drop_selector( Character &p, const inventory_selector_preset &preset, const std::string &selection_column_title ) : inventory_multiselector( p, preset, selection_column_title ), max_chosen_count( std::numeric_limits::max() ) @@ -2423,7 +2429,7 @@ drop_locations inventory_drop_selector::execute() const auto filter_to_nonfavorite_and_nonworn = []( const inventory_entry & entry ) { return entry.is_item() && !entry.any_item()->is_favorite && - !g->u.is_worn( *entry.any_item() ); + !get_player_character().is_worn( *entry.any_item() ); }; const auto selected( get_active_column().get_entries( filter_to_nonfavorite_and_nonworn ) ); diff --git a/src/inventory_ui.h b/src/inventory_ui.h index e013973cf12e0..d253f7d479949 100644 --- a/src/inventory_ui.h +++ b/src/inventory_ui.h @@ -27,7 +27,6 @@ class Character; class item; -class player; class string_input_popup; struct tripoint; class ui_adaptor; @@ -336,7 +335,7 @@ class inventory_column /** Resets width to original (unchanged). */ virtual void reset_width( const std::vector &all_columns ); /** Returns next custom inventory letter. */ - int reassign_custom_invlets( const player &p, int min_invlet, int max_invlet ); + int reassign_custom_invlets( const Character &p, int min_invlet, int max_invlet ); /** Reorder entries, repopulate titles, adjust to the new height. */ virtual void prepare_paging( const std::string &filter = "" ); /** @@ -479,7 +478,7 @@ class selection_column : public inventory_column class inventory_selector { public: - inventory_selector( player &u, const inventory_selector_preset &preset = default_preset ); + inventory_selector( Character &u, const inventory_selector_preset &preset = default_preset ); virtual ~inventory_selector(); /** These functions add items from map / vehicles. */ void add_contained_items( item_location &container ); @@ -519,7 +518,7 @@ class inventory_selector bool keep_open = false; protected: - player &u; + Character &u; const inventory_selector_preset &preset; /** @@ -698,7 +697,7 @@ inventory_selector::stat display_stat( const std::string &caption, int cur_value class inventory_pick_selector : public inventory_selector { public: - inventory_pick_selector( player &p, + inventory_pick_selector( Character &p, const inventory_selector_preset &preset = default_preset ) : inventory_selector( p, preset ) {} @@ -708,7 +707,7 @@ class inventory_pick_selector : public inventory_selector class inventory_multiselector : public inventory_selector { public: - inventory_multiselector( player &p, const inventory_selector_preset &preset = default_preset, + inventory_multiselector( Character &p, const inventory_selector_preset &preset = default_preset, const std::string &selection_column_title = "" ); protected: void rearrange_columns( size_t client_width ) override; @@ -720,7 +719,7 @@ class inventory_multiselector : public inventory_selector class inventory_compare_selector : public inventory_multiselector { public: - inventory_compare_selector( player &p ); + inventory_compare_selector( Character &p ); std::pair execute(); protected: @@ -735,7 +734,7 @@ class inventory_iuse_selector : public inventory_multiselector { public: using GetStats = std::function & )>; - inventory_iuse_selector( player &p, + inventory_iuse_selector( Character &p, const std::string &selector_title, const inventory_selector_preset &preset = default_preset, const GetStats & = {} ); @@ -754,7 +753,7 @@ class inventory_iuse_selector : public inventory_multiselector class inventory_drop_selector : public inventory_multiselector { public: - inventory_drop_selector( player &p, + inventory_drop_selector( Character &p, const inventory_selector_preset &preset = default_preset, const std::string &selection_column_title = _( "ITEMS TO DROP" ) ); drop_locations execute(); diff --git a/src/kill_tracker.cpp b/src/kill_tracker.cpp index 5cee5f4cccd37..63748db738ee8 100644 --- a/src/kill_tracker.cpp +++ b/src/kill_tracker.cpp @@ -121,7 +121,7 @@ void kill_tracker::notify( const cata::event &e ) switch( e.type() ) { case event_type::character_kills_monster: { character_id killer = e.get( "killer" ); - if( killer != get_avatar().getID() ) { + if( killer != get_player_character().getID() ) { // TODO: add a kill counter for npcs? break; } @@ -131,7 +131,7 @@ void kill_tracker::notify( const cata::event &e ) } case event_type::character_kills_character: { character_id killer = e.get( "killer" ); - if( killer != get_avatar().getID() ) { + if( killer != get_player_character().getID() ) { break; } std::string victim_name = e.get( "victim_name" ); diff --git a/src/magic.cpp b/src/magic.cpp index 3147ea802946d..c87849df39e93 100644 --- a/src/magic.cpp +++ b/src/magic.cpp @@ -9,7 +9,6 @@ #include #include -#include "avatar.h" #include "calendar.h" #include "cata_utility.h" #include "catacharset.h" @@ -1419,7 +1418,7 @@ void known_magic::forget_spell( const spell_id &sp ) } add_msg( m_bad, _( "All knowledge of %s leaves you." ), sp->name ); // TODO: add parameter for owner of known_magic for this function - g->events().send( get_avatar().getID(), sp->id ); + g->events().send( get_player_character().getID(), sp->id ); spellbook.erase( sp ); } @@ -1563,8 +1562,8 @@ class spellcasting_callback : public uilist_callback int invlet = 0; invlet = popup_getkey( _( "Choose a new hotkey for this spell." ) ); if( inv_chars.valid( invlet ) ) { - const bool invlet_set = g->u.magic.set_invlet( known_spells[entnum]->id(), invlet, - reserved_invlets ); + const bool invlet_set = + get_player_character().magic.set_invlet( known_spells[entnum]->id(), invlet, reserved_invlets ); if( !invlet_set ) { popup( _( "Hotkey already used." ) ); } else { @@ -1573,7 +1572,7 @@ class spellcasting_callback : public uilist_callback } } else { popup( _( "Hotkey removed." ) ); - g->u.magic.rem_invlet( known_spells[entnum]->id() ); + get_player_character().magic.rem_invlet( known_spells[entnum]->id() ); } return true; } @@ -1687,7 +1686,7 @@ void spellcasting_callback::draw_spell_info( const spell &sp, const uilist *menu string_format( "%s: %d", _( "Max Level" ), sp.get_max_level() ) ); print_colored_text( w_menu, point( h_col1, line ), gray, gray, - sp.colorized_fail_percent( g->u ) ); + sp.colorized_fail_percent( get_player_character() ) ); print_colored_text( w_menu, point( h_col2, line++ ), gray, gray, string_format( "%s: %d", _( "Difficulty" ), sp.get_difficulty() ) ); @@ -1701,21 +1700,21 @@ void spellcasting_callback::draw_spell_info( const spell &sp, const uilist *menu line++; } - const bool cost_encumb = energy_cost_encumbered( sp, g->u ); + const bool cost_encumb = energy_cost_encumbered( sp, get_player_character() ); std::string cost_string = cost_encumb ? _( "Casting Cost (impeded)" ) : _( "Casting Cost" ); std::string energy_cur = sp.energy_source() == magic_energy_type::hp ? "" : - string_format( _( " (%s current)" ), sp.energy_cur_string( g->u ) ); - if( !sp.can_cast( g->u ) ) { + string_format( _( " (%s current)" ), sp.energy_cur_string( get_player_character() ) ); + if( !sp.can_cast( get_player_character() ) ) { cost_string = colorize( _( "Not Enough Energy" ), c_red ); energy_cur = ""; } print_colored_text( w_menu, point( h_col1, line++ ), gray, gray, string_format( "%s: %s %s%s", cost_string, - sp.energy_cost_string( g->u ), sp.energy_string(), energy_cur ) ); - const bool c_t_encumb = casting_time_encumbered( sp, g->u ); + sp.energy_cost_string( get_player_character() ), sp.energy_string(), energy_cur ) ); + const bool c_t_encumb = casting_time_encumbered( sp, get_player_character() ); print_colored_text( w_menu, point( h_col1, line++ ), gray, gray, colorize( string_format( "%s: %s", c_t_encumb ? _( "Casting Time (impeded)" ) : _( "Casting Time" ), - moves_to_string( sp.casting_time( g->u ) ) ), + moves_to_string( sp.casting_time( get_player_character() ) ) ), c_t_encumb ? c_red : c_light_gray ) ); if( line <= win_height * 3 / 4 ) { @@ -2159,7 +2158,7 @@ void spell_events::notify( const cata::event &e ) int learn_at_level = it->second; if( learn_at_level == slvl ) { std::string learn_spell_id = it->first; - g->u.magic.learn_spell( learn_spell_id, g->u ); + get_player_character().magic.learn_spell( learn_spell_id, get_player_character() ); spell_type spell_learned = spell_factory.obj( spell_id( learn_spell_id ) ); add_msg( _( "Your experience and knowledge in creating and manipulating magical energies to cast %s have opened your eyes to new possibilities, you can now cast %s." ), diff --git a/src/map.cpp b/src/map.cpp index cb4ffcf43187a..16b9b5cff676e 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -978,7 +978,7 @@ vehicle *map::veh_at_internal( const tripoint &p, int &part_num ) return const_cast( const_cast( this )->veh_at_internal( p, part_num ) ); } -void map::board_vehicle( const tripoint &pos, player *p ) +void map::board_vehicle( const tripoint &pos, Character *p ) { if( p == nullptr ) { debugmsg( "map::board_vehicle: null player" ); @@ -988,7 +988,9 @@ void map::board_vehicle( const tripoint &pos, player *p ) const cata::optional vp = veh_at( pos ).part_with_feature( VPFLAG_BOARDABLE, true ); if( !vp ) { - if( p->grab_point.x == 0 && p->grab_point.y == 0 ) { + avatar *player_character = p->as_avatar(); + if( player_character != nullptr && + player_character->grab_point.x == 0 && player_character->grab_point.y == 0 ) { debugmsg( "map::board_vehicle: vehicle not found" ); } return; diff --git a/src/map.h b/src/map.h index f559c940aebc7..ccb53bfa0fd6f 100644 --- a/src/map.h +++ b/src/map.h @@ -573,7 +573,7 @@ class map vehicle *veh_at_internal( const tripoint &p, int &part_num ); const vehicle *veh_at_internal( const tripoint &p, int &part_num ) const; // Put player on vehicle at x,y - void board_vehicle( const tripoint &p, player *pl ); + void board_vehicle( const tripoint &p, Character *pl ); // Remove given passenger from given vehicle part. // If dead_passenger, then null passenger is acceptable. void unboard_vehicle( const vpart_reference &, player *passenger, diff --git a/src/mondefense.cpp b/src/mondefense.cpp index b38dad3d0be35..cdaaafa3d690f 100644 --- a/src/mondefense.cpp +++ b/src/mondefense.cpp @@ -10,9 +10,9 @@ #include #include -#include "avatar.h" #include "ballistics.h" #include "bodypart.h" +#include "character.h" #include "creature.h" #include "damage.h" #include "dispersion.h" @@ -76,8 +76,9 @@ void mdefense::zapback( monster &m, Creature *const source, return; } - if( get_avatar().sees( source->pos() ) ) { - const auto msg_type = source == &get_avatar() ? m_bad : m_info; + + if( get_player_character().sees( source->pos() ) ) { + const auto msg_type = source->is_avatar() ? m_bad : m_info; add_msg( msg_type, _( "Striking the %1$s shocks %2$s!" ), m.name(), source->disp_name() ); } @@ -139,7 +140,7 @@ void mdefense::acidsplash( monster &m, Creature *const source, projectile_attack( prj, m.pos(), target, dispersion_sources{ 1200 }, &m ); } - if( get_avatar().sees( m.pos() ) ) { + if( get_player_character().sees( m.pos() ) ) { add_msg( m_warning, _( "Acid sprays out of %s as it is hit!" ), m.disp_name() ); } } diff --git a/src/monexamine.cpp b/src/monexamine.cpp index 81ebdd3976f62..9fd185b08dc67 100644 --- a/src/monexamine.cpp +++ b/src/monexamine.cpp @@ -99,6 +99,7 @@ bool monexamine::pet_menu( monster &z ) amenu.addentry( swap_pos, true, 's', _( "Swap positions" ) ); amenu.addentry( rename, true, 'e', _( "Rename" ) ); + Character &player_character = get_player_character(); if( z.has_effect( effect_has_bag ) ) { amenu.addentry( give_items, true, 'g', _( "Place items into bag" ) ); amenu.addentry( remove_bag, true, 'b', _( "Remove bag from %s" ), pet_name ); @@ -123,7 +124,7 @@ bool monexamine::pet_menu( monster &z ) if( z.has_effect( effect_tied ) ) { amenu.addentry( rope, true, 't', _( "Untie" ) ); } else if( !z.has_flag( MF_RIDEABLE_MECH ) ) { - std::vector rope_inv = g->u.items_with( []( const item & itm ) { + std::vector rope_inv = player_character.items_with( []( const item & itm ) { return itm.has_flag( "TIE_UP" ); } ); if( !rope_inv.empty() ) { @@ -151,7 +152,7 @@ bool monexamine::pet_menu( monster &z ) available = false; } if( available ) { - if( g->u.has_quality( qual_SHEAR, 1 ) ) { + if( player_character.has_quality( qual_SHEAR, 1 ) ) { amenu.addentry( shear, true, 'S', _( "Shear %s." ), pet_name ); } else { amenu.addentry( shear, false, 'S', _( "You cannot shear this animal without shears." ) ); @@ -159,29 +160,32 @@ bool monexamine::pet_menu( monster &z ) } } if( z.has_flag( MF_PET_MOUNTABLE ) && !z.has_effect( effect_monster_saddled ) && - g->u.has_item_with_flag( "TACK" ) && g->u.get_skill_level( skill_survival ) >= 1 ) { + player_character.has_item_with_flag( "TACK" ) && + player_character.get_skill_level( skill_survival ) >= 1 ) { amenu.addentry( attach_saddle, true, 'h', _( "Tack up %s" ), pet_name ); } else if( z.has_flag( MF_PET_MOUNTABLE ) && z.has_effect( effect_monster_saddled ) ) { amenu.addentry( remove_saddle, true, 'h', _( "Remove tack from %s" ), pet_name ); } else if( z.has_flag( MF_PET_MOUNTABLE ) && !z.has_effect( effect_monster_saddled ) && - g->u.has_item_with_flag( "TACK" ) && g->u.get_skill_level( skill_survival ) < 1 ) { + player_character.has_item_with_flag( "TACK" ) && + player_character.get_skill_level( skill_survival ) < 1 ) { amenu.addentry( remove_saddle, false, 'h', _( "You don't know how to saddle %s" ), pet_name ); } if( z.has_flag( MF_PAY_BOT ) ) { amenu.addentry( pay, true, 'f', _( "Manage your friendship with %s" ), pet_name ); } if( !z.has_flag( MF_RIDEABLE_MECH ) ) { - if( z.has_flag( MF_PET_MOUNTABLE ) && g->u.can_mount( z ) ) { + if( z.has_flag( MF_PET_MOUNTABLE ) && player_character.can_mount( z ) ) { amenu.addentry( mount, true, 'r', _( "Mount %s" ), pet_name ); } else if( !z.has_flag( MF_PET_MOUNTABLE ) ) { amenu.addentry( mount, false, 'r', _( "%s cannot be mounted" ), pet_name ); - } else if( z.get_size() <= g->u.get_size() ) { + } else if( z.get_size() <= player_character.get_size() ) { amenu.addentry( mount, false, 'r', _( "%s is too small to carry your weight" ), pet_name ); - } else if( g->u.get_skill_level( skill_survival ) < 1 ) { + } else if( player_character.get_skill_level( skill_survival ) < 1 ) { amenu.addentry( mount, false, 'r', _( "You have no knowledge of riding at all" ) ); - } else if( g->u.get_weight() >= z.get_weight() * z.get_mountable_weight_ratio() ) { + } else if( player_character.get_weight() >= z.get_weight() * z.get_mountable_weight_ratio() ) { amenu.addentry( mount, false, 'r', _( "You are too heavy to mount %s" ), pet_name ); - } else if( !z.has_effect( effect_monster_saddled ) && g->u.get_skill_level( skill_survival ) < 4 ) { + } else if( !z.has_effect( effect_monster_saddled ) && + player_character.get_skill_level( skill_survival ) < 4 ) { amenu.addentry( mount, false, 'r', _( "You are not skilled enough to ride without a saddle" ) ); } } else { @@ -195,16 +199,16 @@ bool monexamine::pet_menu( monster &z ) } amenu.addentry( check_bat, false, 'c', _( "%s battery level is %d%%" ), z.get_name(), static_cast( charge_percent ) ); - if( g->u.weapon.is_null() && z.battery_item ) { + if( player_character.weapon.is_null() && z.battery_item ) { amenu.addentry( mount, true, 'r', _( "Climb into the mech and take control" ) ); - } else if( !g->u.weapon.is_null() ) { + } else if( !player_character.weapon.is_null() ) { amenu.addentry( mount, false, 'r', _( "You cannot pilot the mech whilst wielding something" ) ); } else if( !z.battery_item ) { amenu.addentry( mount, false, 'r', _( "This mech has a dead battery and won't turn on" ) ); } if( z.battery_item ) { amenu.addentry( remove_bat, true, 'x', _( "Remove the mech's battery pack" ) ); - } else if( g->u.has_amount( z.type->mech_battery, 1 ) ) { + } else if( player_character.has_amount( z.type->mech_battery, 1 ) ) { amenu.addentry( insert_bat, true, 'x', _( "Insert a new battery pack" ) ); } else { amenu.addentry( insert_bat, false, 'x', _( "You need a %s to power this mech" ), type.nname( 1 ) ); @@ -283,17 +287,19 @@ bool monexamine::pet_menu( monster &z ) void monexamine::shear_animal( monster &z ) { - const int moves = to_moves( time_duration::from_minutes( 30 / g->u.max_quality( + Character &player_character = get_player_character(); + const int moves = to_moves( time_duration::from_minutes( 30 / player_character.max_quality( qual_SHEAR ) ) ); - g->u.assign_activity( activity_id( "ACT_SHEAR" ), moves, -1 ); - g->u.activity.coords.push_back( g->m.getabs( z.pos() ) ); + player_character.assign_activity( activity_id( "ACT_SHEAR" ), moves, -1 ); + player_character.activity.coords.push_back( get_map().getabs( z.pos() ) ); // pin the sheep in place if it isn't already if( !z.has_effect( effect_tied ) ) { z.add_effect( effect_tied, 1_turns, num_bp, true ); - g->u.activity.str_values.push_back( "temp_tie" ); + player_character.activity.str_values.push_back( "temp_tie" ); } - g->u.activity.targets.push_back( item_location( g->u, g->u.best_quality_item( qual_SHEAR ) ) ); + player_character.activity.targets.push_back( item_location( player_character, + player_character.best_quality_item( qual_SHEAR ) ) ); add_msg( _( "You start shearing the %s." ), z.get_name() ); } @@ -305,7 +311,7 @@ static item_location pet_armor_loc( monster &z ) z.get_volume() <= it.get_pet_armor_max_vol(); }; - return game_menus::inv::titled_filter_menu( filter, g->u, _( "Pet armor" ) ); + return game_menus::inv::titled_filter_menu( filter, get_avatar(), _( "Pet armor" ) ); } static item_location tack_loc() @@ -314,12 +320,12 @@ static item_location tack_loc() return it.has_flag( "TACK" ); }; - return game_menus::inv::titled_filter_menu( filter, g->u, _( "Tack" ) ); + return game_menus::inv::titled_filter_menu( filter, get_avatar(), _( "Tack" ) ); } void monexamine::remove_battery( monster &z ) { - g->m.add_item_or_charges( g->u.pos(), *z.battery_item ); + get_map().add_item_or_charges( get_player_character().pos(), *z.battery_item ); z.battery_item.reset(); } @@ -329,7 +335,8 @@ void monexamine::insert_battery( monster &z ) // already has a battery, shouldn't be called with one, but just incase. return; } - std::vector bat_inv = g->u.items_with( []( const item & itm ) { + Character &player_character = get_player_character(); + std::vector bat_inv = player_character.items_with( []( const item & itm ) { return itm.has_flag( "MECH_BAT" ); } ); if( bat_inv.empty() ) { @@ -351,24 +358,25 @@ void monexamine::insert_battery( monster &z ) return; } item *bat_item = bat_inv[index - 1]; - int item_pos = g->u.get_item_position( bat_item ); + int item_pos = player_character.get_item_position( bat_item ); if( item_pos != INT_MIN ) { z.battery_item = cata::make_value( *bat_item ); - g->u.i_rem( item_pos ); + player_character.i_rem( item_pos ); } } bool monexamine::mech_hack( monster &z ) { + Character &player_character = get_player_character(); itype_id card_type = itype_id_military; - if( g->u.has_amount( card_type, 1 ) ) { + if( player_character.has_amount( card_type, 1 ) ) { if( query_yn( _( "Swipe your ID card into the mech's security port?" ) ) ) { - g->u.mod_moves( -100 ); + player_character.mod_moves( -100 ); z.add_effect( effect_pet, 1_turns, num_bp, true ); z.friendly = -1; add_msg( m_good, _( "The %s whirs into life and opens its restraints to accept a pilot." ), z.get_name() ); - g->u.use_amount( card_type, 1 ); + player_character.use_amount( card_type, 1 ); return true; } } else { @@ -392,8 +400,9 @@ static int prompt_for_amount( const char *const msg, const int max ) bool monexamine::pay_bot( monster &z ) { + Character &player_character = get_player_character(); time_duration friend_time = z.get_effect_dur( effect_pet ); - const int charge_count = g->u.charges_of( itype_cash_card ); + const int charge_count = player_character.charges_of( itype_cash_card ); int amount = 0; uilist bot_menu; @@ -414,7 +423,7 @@ bool monexamine::pay_bot( monster &z ) "How much friendship do you get? Max: %d minutes.", charge_count / 10 ), charge_count / 10 ); if( amount > 0 ) { time_duration time_bought = time_duration::from_minutes( amount ); - g->u.use_charges( itype_cash_card, amount * 10 ); + player_character.use_charges( itype_cash_card, amount * 10 ); z.add_effect( effect_pet, time_bought ); z.add_effect( effect_paid, time_bought, num_bp, true ); z.friendly = -1; @@ -434,7 +443,7 @@ void monexamine::attach_or_remove_saddle( monster &z ) { if( z.has_effect( effect_monster_saddled ) ) { z.remove_effect( effect_monster_saddled ); - g->u.i_add( *z.tack_item ); + get_player_character().i_add( *z.tack_item ); z.tack_item.reset(); } else { item_location loc = tack_loc(); @@ -452,7 +461,7 @@ void monexamine::attach_or_remove_saddle( monster &z ) bool Character::can_mount( const monster &critter ) const { const auto &avoid = get_path_avoid(); - auto route = g->m.route( pos(), critter.pos(), get_pathfinding_settings(), avoid ); + auto route = get_map().route( pos(), critter.pos(), get_pathfinding_settings(), avoid ); if( route.empty() ) { return false; @@ -466,23 +475,24 @@ bool Character::can_mount( const monster &critter ) const void monexamine::mount_pet( monster &z ) { - g->u.mount_creature( z ); + get_player_character().mount_creature( z ); } void monexamine::swap( monster &z ) { std::string pet_name = z.get_name(); - g->u.moves -= 150; + Character &player_character = get_player_character(); + player_character.moves -= 150; ///\EFFECT_STR increases chance to successfully swap positions with your pet ///\EFFECT_DEX increases chance to successfully swap positions with your pet - if( !one_in( ( g->u.str_cur + g->u.dex_cur ) / 6 ) ) { + if( !one_in( ( player_character.str_cur + player_character.dex_cur ) / 6 ) ) { bool t = z.has_effect( effect_tied ); if( t ) { z.remove_effect( effect_tied ); } - g->swap_critters( g->u, z ); + g->swap_critters( player_character, z ); if( t ) { z.add_effect( effect_tied, 1_turns, num_bp, true ); @@ -496,17 +506,18 @@ void monexamine::swap( monster &z ) void monexamine::push( monster &z ) { std::string pet_name = z.get_name(); - g->u.moves -= 30; + Character &player_character = get_player_character(); + player_character.moves -= 30; ///\EFFECT_STR increases chance to successfully push your pet - if( !one_in( g->u.str_cur ) ) { + if( !one_in( player_character.str_cur ) ) { add_msg( _( "You pushed the %s." ), pet_name ); } else { add_msg( _( "You pushed the %s, but it resisted." ), pet_name ); return; } - point delta( z.posx() - g->u.posx(), z.posy() - g->u.posy() ); + point delta( z.posx() - player_character.posx(), z.posy() - player_character.posy() ); z.move_to( tripoint( z.posx() + delta.x, z.posy() + delta.y, z.posz() ) ); } @@ -529,7 +540,9 @@ void monexamine::attach_bag_to( monster &z ) return it.is_armor() && it.get_total_capacity() > 0_ml; }; - item_location loc = game_menus::inv::titled_filter_menu( filter, g->u, _( "Bag item" ) ); + avatar &player_character = get_avatar(); + item_location loc = game_menus::inv::titled_filter_menu( filter, player_character, + _( "Bag item" ) ); if( !loc ) { add_msg( _( "Never mind." ) ); @@ -539,11 +552,11 @@ void monexamine::attach_bag_to( monster &z ) item &it = *loc; z.storage_item = cata::make_value( it ); add_msg( _( "You mount the %1$s on your %2$s." ), it.display_name(), pet_name ); - g->u.i_rem( &it ); + player_character.i_rem( &it ); z.add_effect( effect_has_bag, 1_turns, num_bp, true ); // Update encumbrance in case we were wearing it - g->u.flag_encumbrance(); - g->u.moves -= 200; + player_character.flag_encumbrance(); + player_character.moves -= 200; } void monexamine::remove_bag_from( monster &z ) @@ -553,10 +566,11 @@ void monexamine::remove_bag_from( monster &z ) if( !z.inv.empty() ) { dump_items( z ); } - g->m.add_item_or_charges( g->u.pos(), *z.storage_item ); + Character &player_character = get_player_character(); + get_map().add_item_or_charges( player_character.pos(), *z.storage_item ); add_msg( _( "You remove the %1$s from %2$s." ), z.storage_item->display_name(), pet_name ); z.storage_item.reset(); - g->u.moves -= 200; + player_character.moves -= 200; } else { add_msg( m_bad, _( "Your %1$s doesn't have a bag!" ), pet_name ); } @@ -566,12 +580,14 @@ void monexamine::remove_bag_from( monster &z ) void monexamine::dump_items( monster &z ) { std::string pet_name = z.get_name(); + Character &player_character = get_player_character(); + map &here = get_map(); for( auto &it : z.inv ) { - g->m.add_item_or_charges( g->u.pos(), it ); + here.add_item_or_charges( player_character.pos(), it ); } z.inv.clear(); add_msg( _( "You dump the contents of the %s's bag on the ground." ), pet_name ); - g->u.moves -= 200; + player_character.moves -= 200; } bool monexamine::give_items_to( monster &z ) @@ -586,7 +602,8 @@ bool monexamine::give_items_to( monster &z ) units::mass max_weight = z.weight_capacity() - z.get_carried_weight(); units::volume max_volume = storage.get_total_capacity() - z.get_carried_volume(); - drop_locations items = game_menus::inv::multidrop( g->u ); + avatar &player_character = get_avatar(); + drop_locations items = game_menus::inv::multidrop( player_character ); drop_locations to_move; for( const drop_location &itq : items ) { const item &it = *itq.first; @@ -605,7 +622,7 @@ bool monexamine::give_items_to( monster &z ) } } z.add_effect( effect_controlled, 5_turns ); - g->u.drop( to_move, z.pos(), true ); + player_character.drop( to_move, z.pos(), true ); return false; } @@ -635,7 +652,7 @@ bool monexamine::add_armor( monster &z ) loc.remove_item(); z.add_effect( effect_monster_armor, 1_turns, num_bp, true ); // TODO: armoring a horse takes a lot longer than 2 seconds. This should be a long action. - g->u.moves -= 200; + get_player_character().moves -= 200; return true; } @@ -650,12 +667,12 @@ void monexamine::remove_armor( monster &z ) std::string pet_name = z.get_name(); if( z.armor_item ) { z.armor_item->erase_var( "pet_armor" ); - g->m.add_item_or_charges( z.pos(), *z.armor_item ); + get_map().add_item_or_charges( z.pos(), *z.armor_item ); add_msg( pgettext( "pet armor", "You remove the %1$s from %2$s." ), z.armor_item->display_name(), pet_name ); z.armor_item.reset(); // TODO: removing armor from a horse takes a lot longer than 2 seconds. This should be a long action. - g->u.moves -= 200; + get_player_character().moves -= 200; } else { add_msg( m_bad, _( "Your %1$s isn't wearing armor!" ), pet_name ); } @@ -665,34 +682,38 @@ void monexamine::remove_armor( monster &z ) void monexamine::play_with( monster &z ) { std::string pet_name = z.get_name(); - g->u.assign_activity( ACT_PLAY_WITH_PET, rng( 50, 125 ) * 100 ); - g->u.activity.str_values.push_back( pet_name ); + Character &player_character = get_player_character(); + player_character.assign_activity( ACT_PLAY_WITH_PET, rng( 50, 125 ) * 100 ); + player_character.activity.str_values.push_back( pet_name ); } void monexamine::kill_zslave( monster &z ) { - z.apply_damage( &g->u, bodypart_id( "torso" ), 100 ); // damage the monster (and its corpse) - z.die( &g->u ); // and make sure it's really dead + avatar &player_character = get_avatar(); + z.apply_damage( &player_character, bodypart_id( "torso" ), + 100 ); // damage the monster (and its corpse) + z.die( &player_character ); // and make sure it's really dead - g->u.moves -= 150; + player_character.moves -= 150; if( !one_in( 3 ) ) { - g->u.add_msg_if_player( _( "You tear out the pheromone ball from the zombie slave." ) ); + player_character.add_msg_if_player( _( "You tear out the pheromone ball from the zombie slave." ) ); item ball( "pheromone", 0 ); - iuse::pheromone( &g->u, &ball, true, g->u.pos() ); + iuse::pheromone( &player_character, &ball, true, player_character.pos() ); } } void monexamine::tie_or_untie( monster &z ) { + Character &player_character = get_player_character(); if( z.has_effect( effect_tied ) ) { z.remove_effect( effect_tied ); if( z.tied_item ) { - g->u.i_add( *z.tied_item ); + player_character.i_add( *z.tied_item ); z.tied_item.reset(); } } else { - std::vector rope_inv = g->u.items_with( []( const item & itm ) { + std::vector rope_inv = player_character.items_with( []( const item & itm ) { return itm.has_flag( "TIE_UP" ); } ); if( rope_inv.empty() ) { @@ -713,10 +734,10 @@ void monexamine::tie_or_untie( monster &z ) return; } item *rope_item = rope_inv[index - 1]; - int item_pos = g->u.get_item_position( rope_item ); + int item_pos = player_character.get_item_position( rope_item ); if( item_pos != INT_MIN ) { z.tied_item = cata::make_value( *rope_item ); - g->u.i_rem( item_pos ); + player_character.i_rem( item_pos ); z.add_effect( effect_tied, 1_turns, num_bp, true ); } } @@ -732,13 +753,14 @@ void monexamine::milk_source( monster &source_mon ) } if( milkable_ammo->second > 0 ) { const int moves = to_moves( time_duration::from_minutes( milkable_ammo->second / 2 ) ); - g->u.assign_activity( ACT_MILK, moves, -1 ); - g->u.activity.coords.push_back( g->m.getabs( source_mon.pos() ) ); + Character &player_character = get_player_character(); + player_character.assign_activity( ACT_MILK, moves, -1 ); + player_character.activity.coords.push_back( get_map().getabs( source_mon.pos() ) ); // pin the cow in place if it isn't already bool temp_tie = !source_mon.has_effect( effect_tied ); if( temp_tie ) { source_mon.add_effect( effect_tied, 1_turns, num_bp, true ); - g->u.activity.str_values.push_back( "temp_tie" ); + player_character.activity.str_values.push_back( "temp_tie" ); } add_msg( _( "You milk the %s." ), source_mon.get_name() ); } else { diff --git a/src/mutation.cpp b/src/mutation.cpp index 137b6b0158bb4..d2b134efc4cb2 100644 --- a/src/mutation.cpp +++ b/src/mutation.cpp @@ -402,7 +402,7 @@ void Character::mutation_effect( const trait_id &mut ) _( "Your %s is pushed off!" ), _( "'s %s is pushed off!" ), armor.tname() ); - g->m.add_item_or_charges( pos(), armor ); + get_map().add_item_or_charges( pos(), armor ); } return true; } ); @@ -608,7 +608,7 @@ void Character::activate_mutation( const trait_id &mut ) } if( mut == trait_WEB_WEAVER ) { - g->m.add_field( pos(), fd_web, 1 ); + get_map().add_field( pos(), fd_web, 1 ); add_msg_if_player( _( "You start spinning web with your spinnerets!" ) ); } else if( mut == trait_BURROW ) { tdata.powered = false; @@ -668,8 +668,9 @@ void Character::activate_mutation( const trait_id &mut ) } // Check for adjacent trees. bool adjacent_tree = false; - for( const tripoint &p2 : g->m.points_in_radius( pos(), 1 ) ) { - if( g->m.has_flag( "TREE", p2 ) ) { + map &here = get_map(); + for( const tripoint &p2 : here.points_in_radius( pos(), 1 ) ) { + if( here.has_flag( "TREE", p2 ) ) { adjacent_tree = true; } } @@ -718,7 +719,7 @@ void Character::activate_mutation( const trait_id &mut ) return; } else if( !mdata.ranged_mutation.is_empty() ) { add_msg_if_player( mdata.ranged_mutation_message() ); - avatar_action::fire_ranged_mutation( g->u, item( mdata.ranged_mutation ) ); + avatar_action::fire_ranged_mutation( *this, item( mdata.ranged_mutation ) ); tdata.powered = false; return; } @@ -1431,7 +1432,7 @@ static mutagen_rejection try_reject_mutagen( Character &guy, const item &it, boo if( guy.has_trait( trait_M_SPORES ) || guy.has_trait( trait_M_FERTILE ) || guy.has_trait( trait_M_BLOSSOMS ) || guy.has_trait( trait_M_BLOOM ) ) { guy.add_msg_if_player( m_good, _( "We decontaminate it with spores." ) ); - g->m.ter_set( guy.pos(), t_fungus ); + get_map().ter_set( guy.pos(), t_fungus ); if( guy.is_avatar() ) { g->memorial().add( pgettext( "memorial_male", "Destroyed a harmful invader." ), diff --git a/src/npctalk.cpp b/src/npctalk.cpp index dc02ddef94c2e..ec0a479a66436 100644 --- a/src/npctalk.cpp +++ b/src/npctalk.cpp @@ -138,7 +138,7 @@ std::string talk_trial::name() const /** Time (in turns) and cost (in cent) for training: */ time_duration calc_skill_training_time( const npc &p, const skill_id &skill ) { - return 1_minutes + 30_seconds * g->u.get_skill_level( skill ) - + return 1_minutes + 30_seconds * get_player_character().get_skill_level( skill ) - 1_seconds * p.get_skill_level( skill ); } @@ -340,7 +340,7 @@ static void npc_temp_orders_menu( const std::vector &npc_list ) static void tell_veh_stop_following() { - for( wrapped_vehicle &veh : g->m.get_vehicles() ) { + for( wrapped_vehicle &veh : get_map().get_vehicles() ) { vehicle *v = veh.v; if( v->has_engine_type( fuel_type_animal, false ) && v->is_owned_by( g->u ) ) { v->is_following = false; @@ -351,7 +351,7 @@ static void tell_veh_stop_following() static void assign_veh_to_follow() { - for( wrapped_vehicle &veh : g->m.get_vehicles() ) { + for( wrapped_vehicle &veh : get_map().get_vehicles() ) { vehicle *v = veh.v; if( v->has_engine_type( fuel_type_animal, false ) && v->is_owned_by( g->u ) ) { v->activate_animal_follow(); @@ -361,7 +361,7 @@ static void assign_veh_to_follow() static void tell_magic_veh_to_follow() { - for( wrapped_vehicle &veh : g->m.get_vehicles() ) { + for( wrapped_vehicle &veh : get_map().get_vehicles() ) { vehicle *v = veh.v; if( v->magic ) { for( const vpart_reference &vp : v->get_all_parts() ) { @@ -377,7 +377,7 @@ static void tell_magic_veh_to_follow() static void tell_magic_veh_stop_following() { - for( wrapped_vehicle &veh : g->m.get_vehicles() ) { + for( wrapped_vehicle &veh : get_map().get_vehicles() ) { vehicle *v = veh.v; if( v->magic ) { for( const vpart_reference &vp : v->get_all_parts() ) { @@ -422,7 +422,7 @@ void game::chat() std::vector following_vehicles; std::vector magic_vehicles; std::vector magic_following_vehicles; - for( auto &veh : g->m.get_vehicles() ) { + for( auto &veh : get_map().get_vehicles() ) { auto &v = veh.v; if( v->has_engine_type( fuel_type_animal, false ) && v->is_owned_by( g->u ) ) { animal_vehicles.push_back( v ); @@ -627,8 +627,9 @@ void game::chat() void npc::handle_sound( const sounds::sound_t spriority, const std::string &description, int heard_volume, const tripoint &spos ) { - const tripoint s_abs_pos = g->m.getabs( spos ); - const tripoint my_abs_pos = g->m.getabs( pos() ); + const map &here = get_map(); + const tripoint s_abs_pos = here.getabs( spos ); + const tripoint my_abs_pos = here.getabs( pos() ); add_msg( m_debug, "%s heard '%s', priority %d at volume %d from %d:%d, my pos %d:%d", disp_name(), description, static_cast( spriority ), heard_volume, @@ -2363,7 +2364,7 @@ void talk_effect_fun_t::set_add_mission( const std::string &mission_id ) function = [mission_id]( const dialogue & d ) { npc &p = *d.beta; mission *miss = mission::reserve_new( mission_type_id( mission_id ), p.getID() ); - miss->assign( g->u ); + miss->assign( get_avatar() ); p.chatbin.missions_assigned.push_back( miss ); }; } diff --git a/src/overmapbuffer.cpp b/src/overmapbuffer.cpp index 209ecdd9b7778..d48aa6cfb6225 100644 --- a/src/overmapbuffer.cpp +++ b/src/overmapbuffer.cpp @@ -7,10 +7,10 @@ #include #include -#include "avatar.h" #include "basecamp.h" #include "calendar.h" #include "cata_utility.h" +#include "character.h" #include "character_id.h" #include "color.h" #include "common_types.h" @@ -482,7 +482,7 @@ void overmapbuffer::process_mongroups() { // arbitrary radius to include nearby overmaps (aside from the current one) const auto radius = MAPSIZE * 2; - const auto center = g->u.global_sm_location(); + const auto center = get_player_character().global_sm_location(); for( auto &om : get_overmaps_near( center, radius ) ) { om->process_mongroups(); } @@ -492,7 +492,7 @@ void overmapbuffer::move_hordes() { // arbitrary radius to include nearby overmaps (aside from the current one) const auto radius = MAPSIZE * 2; - const auto center = g->u.global_sm_location(); + const auto center = get_player_character().global_sm_location(); for( auto &om : get_overmaps_near( center, radius ) ) { om->move_hordes(); } @@ -564,7 +564,7 @@ void overmapbuffer::set_scent( const tripoint &loc, int strength ) void overmapbuffer::move_vehicle( vehicle *veh, const point &old_msp ) { - const point new_msp = g->m.getabs( veh->global_pos3().xy() ); + const point new_msp = get_map().getabs( veh->global_pos3().xy() ); const point old_omt = ms_to_omt_copy( old_msp ); const point new_omt = ms_to_omt_copy( new_msp ); const overmap_with_local_coords old_om_loc = get_om_global( old_omt ); @@ -592,14 +592,14 @@ void overmapbuffer::remove_camp( const basecamp &camp ) void overmapbuffer::remove_vehicle( const vehicle *veh ) { - const point omt = ms_to_omt_copy( g->m.getabs( veh->global_pos3().xy() ) ); + const point omt = ms_to_omt_copy( get_map().getabs( veh->global_pos3().xy() ) ); const overmap_with_local_coords om_loc = get_om_global( omt ); om_loc.om->vehicles.erase( veh->om_id ); } void overmapbuffer::add_vehicle( vehicle *veh ) { - const point abs_pos = g->m.getabs( veh->global_pos3().xy() ); + const point abs_pos = get_map().getabs( veh->global_pos3().xy() ); const point omt = ms_to_omt_copy( abs_pos ); const overmap_with_local_coords om_loc = get_om_global( omt ); int id = om_loc.om->vehicles.size() + 1; @@ -1073,11 +1073,11 @@ shared_ptr_fast overmapbuffer::remove_npc( const character_id &id ) std::vector> overmapbuffer::get_npcs_near_player( int radius ) { - tripoint plpos = g->u.global_omt_location(); + tripoint plpos = get_player_character().global_omt_location(); // get_npcs_near needs submap coordinates omt_to_sm( plpos.x, plpos.y ); // INT_MIN is a (a bit ugly) way to inform get_npcs_near not to filter by z-level - const int zpos = g->m.has_zlevels() ? INT_MIN : plpos.z; + const int zpos = get_map().has_zlevels() ? INT_MIN : plpos.z; return get_npcs_near( tripoint( plpos.xy(), zpos ), radius ); } @@ -1179,7 +1179,7 @@ static radio_tower_reference create_radio_tower_reference( const overmap &om, ra radio_tower_reference overmapbuffer::find_radio_station( const int frequency ) { - const auto center = g->u.global_sm_location(); + const auto center = get_player_character().global_sm_location(); for( auto &om : get_overmaps_near( center, RADIO_MAX_STRENGTH ) ) { for( auto &tower : om->radios ) { const auto rref = create_radio_tower_reference( *om, tower, center ); @@ -1194,7 +1194,7 @@ radio_tower_reference overmapbuffer::find_radio_station( const int frequency ) std::vector overmapbuffer::find_all_radio_stations() { std::vector result; - const auto center = g->u.global_sm_location(); + const auto center = get_player_character().global_sm_location(); // perceived signal strength is distance (in submaps) - signal strength, so towers // further than RADIO_MAX_STRENGTH submaps away can never be received at all. const int radius = RADIO_MAX_STRENGTH; @@ -1370,9 +1370,10 @@ void overmapbuffer::spawn_monster( const tripoint &p ) assert( ms.x >= 0 && ms.x < SEEX ); assert( ms.y >= 0 && ms.y < SEEX ); ms += sm_to_ms_copy( p.xy() ); + const map &here = get_map(); // The monster position must be local to the main map when added to the game - const tripoint local = tripoint( g->m.getlocal( ms ), p.z ); - assert( g->m.inbounds( local ) ); + const tripoint local = tripoint( here.getlocal( ms ), p.z ); + assert( here.inbounds( local ) ); monster *const placed = g->place_critter_at( make_shared_fast( this_monster ), local ); if( placed ) { @@ -1385,7 +1386,7 @@ void overmapbuffer::spawn_monster( const tripoint &p ) void overmapbuffer::despawn_monster( const monster &critter ) { // Get absolute coordinates of the monster in map squares, translate to submap position - tripoint sm = ms_to_sm_copy( g->m.getabs( critter.pos() ) ); + tripoint sm = ms_to_sm_copy( get_map().getabs( critter.pos() ) ); // Get the overmap coordinates and get the overmap, sm is now local to that overmap const point omp = sm_to_om_remain( sm.x, sm.y ); overmap &om = get( omp ); diff --git a/src/pickup.cpp b/src/pickup.cpp index c143686030031..417b46006f33f 100644 --- a/src/pickup.cpp +++ b/src/pickup.cpp @@ -140,7 +140,7 @@ static pickup_answer handle_problematic_pickup( const item &it, bool &offered_sw return CANCEL; } - player &u = g->u; + Character &u = get_player_character(); uilist amenu; @@ -175,7 +175,7 @@ static pickup_answer handle_problematic_pickup( const item &it, bool &offered_sw bool Pickup::query_thief() { - player &u = g->u; + Character &u = get_player_character(); const bool force_uc = get_option( "FORCE_CAPITAL_YN" ); const auto &allow_key = force_uc ? input_context::disallow_lower_case : input_context::allow_all_keys; @@ -219,7 +219,7 @@ bool Pickup::query_thief() bool pick_one_up( item_location &loc, int quantity, bool &got_water, bool &offered_swap, PickupMap &mapPickup, bool autopickup ) { - player &u = g->u; + player &u = get_avatar(); int moves_taken = 100; bool picked_up = false; pickup_answer option = CANCEL; @@ -235,7 +235,7 @@ bool pick_one_up( item_location &loc, int quantity, bool &got_water, bool &offer const auto wield_check = u.can_wield( newit ); - if( !newit.is_owned_by( g->u, true ) ) { + if( !newit.is_owned_by( u, true ) ) { // Has the player given input on if stealing is ok? if( u.get_value( "THIEF_MODE" ) == "THIEF_ASK" ) { Pickup::query_thief(); @@ -363,7 +363,7 @@ bool pick_one_up( item_location &loc, int quantity, bool &got_water, bool &offer } else { loc.remove_item(); } - g->u.moves -= moves_taken; + u.moves -= moves_taken; } return picked_up || !did_prompt; @@ -416,7 +416,8 @@ void Pickup::pick_up( const tripoint &p, int min, from_where get_items_from ) { int cargo_part = -1; - const optional_vpart_position vp = g->m.veh_at( p ); + map &local = get_map(); + const optional_vpart_position vp = local.veh_at( p ); vehicle *const veh = veh_pointer_or_null( vp ); bool from_vehicle = false; @@ -424,7 +425,7 @@ void Pickup::pick_up( const tripoint &p, int min, from_where get_items_from ) if( veh != nullptr && get_items_from == prompt ) { const cata::optional carg = vp.part_with_feature( "CARGO", false ); const bool veh_has_items = carg && !veh->get_items( carg->part_index() ).empty(); - const bool map_has_items = g->m.has_items( p ); + const bool map_has_items = local.has_items( p ); if( veh_has_items && map_has_items ) { uilist amenu( _( "Get items from where?" ), { _( "Get items from vehicle cargo" ), _( "Get items on the ground" ) } ); if( amenu.ret == UILIST_CANCEL ) { @@ -441,20 +442,20 @@ void Pickup::pick_up( const tripoint &p, int min, from_where get_items_from ) from_vehicle = cargo_part >= 0; } else { // Nothing to change, default is to pick from ground anyway. - if( g->m.has_flag( "SEALED", p ) ) { + if( local.has_flag( "SEALED", p ) ) { return; } } } if( !from_vehicle ) { - bool isEmpty = ( g->m.i_at( p ).empty() ); + bool isEmpty = ( local.i_at( p ).empty() ); // Hide the pickup window if this is a toilet and there's nothing here // but non-frozen water. - if( ( !isEmpty ) && g->m.furn( p ) == f_toilet ) { + if( ( !isEmpty ) && local.furn( p ) == f_toilet ) { isEmpty = true; - for( const item &maybe_water : g->m.i_at( p ) ) { + for( const item &maybe_water : local.i_at( p ) ) { if( maybe_water.typeId() != itype_water || maybe_water.is_frozen_liquid() ) { isEmpty = false; break; @@ -475,7 +476,7 @@ void Pickup::pick_up( const tripoint &p, int min, from_where get_items_from ) here.push_back( it ); } } else { - map_stack mapitems = g->m.i_at( p ); + map_stack mapitems = local.i_at( p ); for( item_stack::iterator it = mapitems.begin(); it != mapitems.end(); ++it ) { here.push_back( it ); } @@ -498,7 +499,7 @@ void Pickup::pick_up( const tripoint &p, int min, from_where get_items_from ) // Bail out if this square cannot be auto-picked-up if( g->check_zone( zone_type_id( "NO_AUTO_PICKUP" ), p ) ) { return; - } else if( g->m.has_flag( "SEALED", p ) ) { + } else if( local.has_flag( "SEALED", p ) ) { return; } } diff --git a/src/pixel_minimap.cpp b/src/pixel_minimap.cpp index e48725537c7d4..6fde954b06de6 100644 --- a/src/pixel_minimap.cpp +++ b/src/pixel_minimap.cpp @@ -14,7 +14,6 @@ #include #include -#include "avatar.h" #include "cata_utility.h" #include "character.h" #include "color.h" @@ -101,7 +100,7 @@ SDL_Color get_critter_color( Creature *critter, int flicker, int mixture ) if( const monster *m = dynamic_cast( critter ) ) { //faction status (attacking or tracking) determines if red highlights get applied to creature - const monster_attitude matt = m->attitude( &get_avatar() ); + const monster_attitude matt = m->attitude( &get_player_character() ); if( MATT_ATTACK == matt || MATT_FOLLOW == matt ) { const SDL_Color red_pixel = SDL_Color{ 0xFF, 0x0, 0x0, 0xFF }; @@ -296,7 +295,7 @@ void pixel_minimap::update_cache_at( const tripoint &sm_pos ) { const map &here = get_map(); const level_cache &access_cache = here.access_cache( sm_pos.z ); - const bool nv_goggle = get_avatar().get_vision_modes()[NV_GOGGLES]; + const bool nv_goggle = get_player_character().get_vision_modes()[NV_GOGGLES]; submap_cache &cache_item = get_cache_at( here.get_abs_sub() + sm_pos ); const tripoint ms_pos = sm_to_ms_copy( sm_pos ); @@ -514,7 +513,7 @@ void pixel_minimap::render_critters( const tripoint ¢er ) const auto critter = g->critter_at( p, true ); - if( critter == nullptr || !get_avatar().sees( *critter ) ) { + if( critter == nullptr || !get_player_character().sees( *critter ) ) { continue; } diff --git a/src/sounds.cpp b/src/sounds.cpp index 0d48fc898703e..41af771d22db3 100644 --- a/src/sounds.cpp +++ b/src/sounds.cpp @@ -11,9 +11,9 @@ #include #include -#include "avatar.h" #include "bodypart.h" #include "calendar.h" +#include "character.h" #include "coordinate_conversions.h" #include "creature.h" #include "debug.h" @@ -647,7 +647,7 @@ int sfx::set_channel_volume( channel channel, int volume ) void sfx::do_vehicle_engine_sfx() { static const channel ch = channel::interior_engine_sound; - const avatar &player_character = get_avatar(); + const Character &player_character = get_player_character(); if( !player_character.in_vehicle ) { fade_audio_channel( ch, 300 ); add_msg( m_debug, "STOP interior_engine_sound, OUT OF CAR" ); @@ -768,7 +768,7 @@ void sfx::do_vehicle_exterior_engine_sfx() { static const channel ch = channel::exterior_engine_sound; static const int ch_int = static_cast( ch ); - const avatar &player_character = get_avatar(); + const Character &player_character = get_player_character(); // early bail-outs for efficiency if( player_character.in_vehicle ) { fade_audio_channel( ch, 300 ); @@ -855,7 +855,7 @@ void sfx::do_vehicle_exterior_engine_sfx() void sfx::do_ambient() { - const avatar &player_character = get_avatar(); + const Character &player_character = get_player_character(); if( player_character.in_sleep_state() && !audio_muted ) { fade_audio_channel( channel::any, 300 ); audio_muted = true; @@ -986,7 +986,7 @@ void sfx::generate_gun_sound( const player &source_arg, const item &firing ) int angle = 0; int distance = 0; std::string selected_sound; - const avatar &player_character = get_avatar(); + const Character &player_character = get_player_character(); // this does not mean p == avatar (it could be a vehicle turret) if( player_character.pos() == source ) { selected_sound = "fire_gun"; @@ -1184,7 +1184,7 @@ void sfx::do_player_death_hurt( const player &target, bool death ) void sfx::do_danger_music() { - avatar &player_character = get_avatar(); + Character &player_character = get_player_character(); if( player_character.in_sleep_state() && !audio_muted ) { fade_audio_channel( channel::any, 100 ); audio_muted = true; @@ -1235,7 +1235,7 @@ void sfx::do_danger_music() void sfx::do_fatigue() { - avatar &player_character = get_avatar(); + Character &player_character = get_player_character(); /*15: Stamina 75% 16: Stamina 50% 17: Stamina 25%*/ @@ -1315,7 +1315,7 @@ void sfx::do_footstep() end_sfx_timestamp = std::chrono::high_resolution_clock::now(); sfx_time = end_sfx_timestamp - start_sfx_timestamp; if( std::chrono::duration_cast ( sfx_time ).count() > 400 ) { - const avatar &player_character = get_avatar(); + const Character &player_character = get_player_character(); int heard_volume = sfx::get_heard_volume( player_character.pos() ); const auto terrain = get_map().ter( player_character.pos() ).id(); static const std::set grass = { @@ -1446,7 +1446,7 @@ void sfx::do_footstep() void sfx::do_obstacle( const std::string &obst ) { - int heard_volume = sfx::get_heard_volume( get_avatar().pos() ); + int heard_volume = sfx::get_heard_volume( get_player_character().pos() ); if( sfx::has_variant_sound( "plmove", obst ) ) { play_variant_sound( "plmove", obst, heard_volume, 0, 0.8, 1.2 ); } else if( ter_str_id( obst ).is_valid() && @@ -1460,7 +1460,7 @@ void sfx::do_obstacle( const std::string &obst ) void sfx::play_activity_sound( const std::string &id, const std::string &variant, int volume ) { - avatar &player_character = get_avatar(); + Character &player_character = get_player_character(); if( act != player_character.activity.id() ) { act = player_character.activity.id(); play_ambient_variant_sound( id, variant, volume, channel::player_activities, 0 ); @@ -1525,7 +1525,7 @@ void sfx::do_obstacle( const std::string & ) { } /*@{*/ int sfx::get_heard_volume( const tripoint &source ) { - int distance = sound_distance( get_avatar().pos(), source ); + int distance = sound_distance( get_player_character().pos(), source ); // fract = -100 / 24 const float fract = -4.166666; int heard_volume = fract * distance - 1 + 100; @@ -1538,7 +1538,7 @@ int sfx::get_heard_volume( const tripoint &source ) int sfx::get_heard_angle( const tripoint &source ) { - int angle = coord_to_angle( get_avatar().pos(), source ) + 90; + int angle = coord_to_angle( get_player_character().pos(), source ) + 90; //add_msg(m_warning, "angle: %i", angle); return ( angle ); } diff --git a/src/turret.cpp b/src/turret.cpp index 8ecd3c4463af7..dfe6451c58f28 100644 --- a/src/turret.cpp +++ b/src/turret.cpp @@ -291,17 +291,21 @@ void turret_data::post_fire( player &p, int shots ) veh->drain( fuel_type_battery, mode->get_gun_ups_drain() * shots ); } -int turret_data::fire( player &p, const tripoint &target ) +int turret_data::fire( Character &c, const tripoint &target ) { if( !veh || !part ) { return 0; } int shots = 0; auto mode = base()->gun_current_mode(); + player *player_character = c.as_player(); + if( player_character == nullptr ) { + return 0; + } - prepare_fire( p ); - shots = p.fire_gun( target, mode.qty, *mode ); - post_fire( p, shots ); + prepare_fire( *player_character ); + shots = player_character->fire_gun( target, mode.qty, *mode ); + post_fire( *player_character, shots ); return shots; } diff --git a/src/vehicle.h b/src/vehicle.h index 8b7f5e828454c..52f4dbdd9a512 100644 --- a/src/vehicle.h +++ b/src/vehicle.h @@ -537,7 +537,7 @@ class turret_data * @param target coordinates that will be fired on. * @return the number of shots actually fired (may be zero). */ - int fire( player &p, const tripoint &target ); + int fire( Character &c, const tripoint &target ); bool can_reload() const; bool can_unload() const; diff --git a/src/weather.cpp b/src/weather.cpp index ac1cea97a9864..882859210654e 100644 --- a/src/weather.cpp +++ b/src/weather.cpp @@ -63,7 +63,7 @@ weather_manager &get_weather() static bool is_player_outside() { - return g->m.is_outside( point( g->u.posx(), g->u.posy() ) ) && g->get_levz() >= 0; + return get_map().is_outside( point( g->u.posx(), g->u.posy() ) ) && g->get_levz() >= 0; } static constexpr int THUNDER_CHANCE = 50; @@ -331,8 +331,9 @@ double trap::funnel_turns_per_charge( double rain_depth_mm_per_hour ) const static void fill_funnels( int rain_depth_mm_per_hour, bool acid, const trap &tr ) { const double turns_per_charge = tr.funnel_turns_per_charge( rain_depth_mm_per_hour ); + map &here = get_map(); // Give each funnel on the map a chance to collect the rain. - const std::vector &funnel_locs = g->m.trap_locations( tr.loadid ); + const std::vector &funnel_locs = here.trap_locations( tr.loadid ); for( const tripoint &loc : funnel_locs ) { units::volume maxcontains = 0_ml; if( one_in( turns_per_charge ) ) { @@ -341,7 +342,7 @@ static void fill_funnels( int rain_depth_mm_per_hour, bool acid, const trap &tr // This funnel has collected some rain! Put the rain in the largest // container here which is either empty or contains some mixture of // impure water and acid. - map_stack items = g->m.i_at( loc ); + map_stack items = here.i_at( loc ); auto container = items.end(); for( auto candidate_container = items.begin(); candidate_container != items.end(); ++candidate_container ) { @@ -440,7 +441,7 @@ void do_rain( weather_type const w ) decay_time = 45_turns; wetness = 60; } - g->m.decay_fields_and_scent( decay_time ); + get_map().decay_fields_and_scent( decay_time ); wet_player( wetness ); } @@ -905,7 +906,7 @@ double get_local_windpower( double windpower, const oter_id &omter, const tripoi bool is_wind_blocker( const tripoint &location ) { - return g->m.has_flag( "BLOCK_WIND", location ); + return get_map().has_flag( "BLOCK_WIND", location ); } // Description of Wind Speed - https://en.wikipedia.org/wiki/Beaufort_scale @@ -1011,9 +1012,10 @@ void weather_manager::update_weather() // Check weather every few turns, instead of every turn. // TODO: predict when the weather changes and use that time. nextweather = calendar::turn + 5_minutes; + map &here = get_map(); const weather_datum wdata = weather_data( weather ); if( weather != old_weather && wdata.dangerous && - g->get_levz() >= 0 && g->m.is_outside( g->u.pos() ) + g->get_levz() >= 0 && here.is_outside( g->u.pos() ) && !g->u.has_activity( ACT_WAIT_WEATHER ) ) { g->cancel_activity_or_ignore_query( distraction_type::weather_change, string_format( _( "The weather changed to %s!" ), wdata.name ) ); @@ -1026,7 +1028,7 @@ void weather_manager::update_weather() if( wdata.sight_penalty != weather::sight_penalty( old_weather ) ) { for( int i = -OVERMAP_DEPTH; i <= OVERMAP_HEIGHT; i++ ) { - g->m.set_transparency_cache_dirty( i ); + here.set_transparency_cache_dirty( i ); } } } @@ -1054,7 +1056,7 @@ int weather_manager::get_temperature( const tripoint &location ) } //underground temperature = average New England temperature = 43F/6C rounded to int const int temp = ( location.z < 0 ? AVERAGE_ANNUAL_TEMPERATURE : temperature ) + - ( g->new_game ? 0 : g->m.get_temperature( location ) + temp_mod ); + ( g->new_game ? 0 : get_map().get_temperature( location ) + temp_mod ); temperature_cache.emplace( std::make_pair( location, temp ) ); return temp; diff --git a/tests/encumbrance_test.cpp b/tests/encumbrance_test.cpp index 27ae8fe12a3ff..d548f51e987c1 100644 --- a/tests/encumbrance_test.cpp +++ b/tests/encumbrance_test.cpp @@ -5,23 +5,21 @@ #include #include -#include "avatar.h" #include "catch/catch.hpp" -#include "npc.h" -#include "player.h" #include "bodypart.h" #include "character.h" +#include "debug.h" #include "item.h" #include "material.h" +#include "npc.h" #include "type_id.h" -#include "debug.h" static void test_encumbrance_on( - player &p, + Character &p, const std::vector &clothing, const std::string &body_part, int expected_encumbrance, - const std::function &tweak_player = {} + const std::function &tweak_player = {} ) { CAPTURE( body_part ); @@ -43,7 +41,7 @@ static void test_encumbrance_items( const std::vector &clothing, const std::string &body_part, const int expected_encumbrance, - const std::function &tweak_player = {} + const std::function &tweak_player = {} ) { // Test NPC first because NPC code can accidentally end up using properties @@ -53,7 +51,8 @@ static void test_encumbrance_items( test_encumbrance_on( example_npc, clothing, body_part, expected_encumbrance, tweak_player ); } SECTION( "testing on player" ) { - test_encumbrance_on( get_avatar(), clothing, body_part, expected_encumbrance, tweak_player ); + test_encumbrance_on( get_player_character(), clothing, body_part, expected_encumbrance, + tweak_player ); } } @@ -75,7 +74,7 @@ struct add_trait { add_trait( const std::string &t ) : trait( t ) {} add_trait( const trait_id &t ) : trait( t ) {} - void operator()( player &p ) { + void operator()( Character &p ) { p.toggle_trait( trait ); } diff --git a/tests/item_location_test.cpp b/tests/item_location_test.cpp index 03a94bbab44c9..ea95b1e1990ce 100644 --- a/tests/item_location_test.cpp +++ b/tests/item_location_test.cpp @@ -2,8 +2,8 @@ #include #include -#include "avatar.h" #include "catch/catch.hpp" +#include "character.h" #include "item.h" #include "map_helpers.h" #include "rng.h" @@ -68,7 +68,7 @@ TEST_CASE( "item_location_doesnt_return_stale_map_item", "[item][item_location]" TEST_CASE( "item_in_container", "[item][item_location]" ) { - avatar &dummy = get_avatar(); + Character &dummy = get_player_character(); clear_avatar(); item &backpack = dummy.i_add( item( "backpack" ) ); item jeans( "jeans" ); @@ -77,7 +77,7 @@ TEST_CASE( "item_in_container", "[item][item_location]" ) backpack.put_in( jeans, item_pocket::pocket_type::CONTAINER ); - item_location backpack_loc( dummy, & **dummy.wear( backpack ) ); + item_location backpack_loc( dummy, & **dummy.wear_item( backpack ) ); REQUIRE( dummy.has_item( *backpack_loc ) ); diff --git a/tests/monster_test.cpp b/tests/monster_test.cpp index dddceab3914d1..1343278ff84b7 100644 --- a/tests/monster_test.cpp +++ b/tests/monster_test.cpp @@ -8,20 +8,19 @@ #include #include -#include "avatar.h" #include "catch/catch.hpp" +#include "character.h" #include "game.h" +#include "game_constants.h" +#include "item.h" +#include "line.h" #include "map.h" #include "map_helpers.h" #include "monster.h" #include "options_helpers.h" #include "options.h" -#include "player.h" -#include "test_statistics.h" -#include "game_constants.h" -#include "item.h" -#include "line.h" #include "point.h" +#include "test_statistics.h" using move_statistics = statistics; @@ -86,10 +85,11 @@ static int can_catch_player( const std::string &monster_type, const tripoint &di { clear_map(); REQUIRE( g->num_creatures() == 1 ); // the player - player &test_player = get_avatar(); + Character &test_player = get_player_character(); // Strip off any potentially encumbering clothing. - std::list temp; - while( test_player.takeoff( test_player.i_at( -2 ), &temp ) ) {} + test_player.remove_worn_items_with( []( item & ) { + return true; + } ); const tripoint center{ 65, 65, 0 }; test_player.setpos( center ); diff --git a/tests/npc_talk_test.cpp b/tests/npc_talk_test.cpp index f05b2e4f1f9e9..e1b5a1b5c9ddf 100644 --- a/tests/npc_talk_test.cpp +++ b/tests/npc_talk_test.cpp @@ -85,7 +85,7 @@ static std::string gen_dynamic_line( dialogue &d ) static void change_om_type( const std::string &new_type ) { - const tripoint omt_pos = ms_to_omt_copy( get_map().getabs( get_avatar().pos() ) ); + const tripoint omt_pos = ms_to_omt_copy( get_map().getabs( get_player_character().pos() ) ); overmap_buffer.ter_set( omt_pos, oter_id( new_type ) ); } @@ -93,7 +93,7 @@ static npc &prep_test( dialogue &d ) { clear_avatar(); clear_vehicles(); - avatar &player_character = get_avatar(); + player &player_character = get_avatar(); REQUIRE_FALSE( player_character.in_vehicle ); const tripoint test_origin( 15, 15, 0 ); @@ -134,7 +134,7 @@ TEST_CASE( "npc_talk_stats", "[npc_talk]" ) dialogue d; prep_test( d ); - avatar &player_character = get_avatar(); + player &player_character = get_avatar(); player_character.str_cur = 8; player_character.dex_cur = 8; player_character.int_cur = 8; @@ -170,7 +170,7 @@ TEST_CASE( "npc_talk_skills", "[npc_talk]" ) const skill_id skill( "driving" ); - avatar &player_character = get_avatar(); + player &player_character = get_avatar(); player_character.set_skill_level( skill, 8 ); d.add_topic( "TALK_TEST_SIMPLE_SKILLS" ); @@ -193,7 +193,7 @@ TEST_CASE( "npc_talk_wearing_and_trait", "[npc_talk]" ) dialogue d; npc &talker_npc = prep_test( d ); - avatar &player_character = get_avatar(); + player &player_character = get_avatar(); for( const trait_id &tr : player_character.get_mutations() ) { player_character.unset_mutation( tr ); } @@ -238,7 +238,7 @@ TEST_CASE( "npc_talk_effect", "[npc_talk]" ) { dialogue d; npc &talker_npc = prep_test( d ); - avatar &player_character = get_avatar(); + player &player_character = get_avatar(); d.add_topic( "TALK_TEST_EFFECT" ); gen_response_lines( d, 1 ); @@ -259,7 +259,7 @@ TEST_CASE( "npc_talk_service", "[npc_talk]" ) { dialogue d; npc &talker_npc = prep_test( d ); - avatar &player_character = get_avatar(); + player &player_character = get_avatar(); d.add_topic( "TALK_TEST_SERVICE" ); player_character.cash = 0; @@ -466,7 +466,7 @@ TEST_CASE( "npc_talk_switch", "[npc_talk]" ) { dialogue d; prep_test( d ); - avatar &player_character = get_avatar(); + player &player_character = get_avatar(); d.add_topic( "TALK_TEST_SWITCH" ); player_character.cash = 1000; @@ -496,7 +496,7 @@ TEST_CASE( "npc_talk_or", "[npc_talk]" ) { dialogue d; npc &talker_npc = prep_test( d ); - avatar &player_character = get_avatar(); + player &player_character = get_avatar(); d.add_topic( "TALK_TEST_OR" ); player_character.cash = 0; @@ -513,7 +513,7 @@ TEST_CASE( "npc_talk_and", "[npc_talk]" ) { dialogue d; npc &talker_npc = prep_test( d ); - avatar &player_character = get_avatar(); + player &player_character = get_avatar(); player_character.toggle_trait( trait_id( "ELFA_EARS" ) ); d.add_topic( "TALK_TEST_AND" ); @@ -530,7 +530,7 @@ TEST_CASE( "npc_talk_nested", "[npc_talk]" ) { dialogue d; npc &talker_npc = prep_test( d ); - avatar &player_character = get_avatar(); + player &player_character = get_avatar(); d.add_topic( "TALK_TEST_NESTED" ); talker_npc.add_effect( effect_currently_busy, 9999_turns ); @@ -547,7 +547,7 @@ TEST_CASE( "npc_talk_nested", "[npc_talk]" ) TEST_CASE( "npc_talk_conditionals", "[npc_talk]" ) { dialogue d; - avatar &player_character = get_avatar(); + player &player_character = get_avatar(); prep_test( d ); player_character.cash = 800; @@ -577,7 +577,7 @@ TEST_CASE( "npc_talk_items", "[npc_talk]" ) { dialogue d; npc &talker_npc = prep_test( d ); - avatar &player_character = get_avatar(); + player &player_character = get_avatar(); player_character.remove_items_with( []( const item & it ) { return it.get_category().get_id() == item_category_id( "books" ) || @@ -860,7 +860,7 @@ TEST_CASE( "npc_talk_bionics", "[npc_talk]" ) { dialogue d; npc &talker_npc = prep_test( d ); - avatar &player_character = get_avatar(); + player &player_character = get_avatar(); player_character.clear_bionics(); talker_npc.clear_bionics(); @@ -879,7 +879,7 @@ TEST_CASE( "npc_talk_effects", "[npc_talk]" ) { dialogue d; npc &talker_npc = prep_test( d ); - avatar &player_character = get_avatar(); + player &player_character = get_avatar(); // speaker effects just use are owed because I don't want to do anything complicated player_character.cash = 1000; diff --git a/tests/player_test.cpp b/tests/player_test.cpp index d3ffb68878053..15e64c69d73a2 100644 --- a/tests/player_test.cpp +++ b/tests/player_test.cpp @@ -3,9 +3,8 @@ #include #include -#include "avatar.h" #include "catch/catch.hpp" -#include "player.h" +#include "character.h" #include "weather.h" #include "bodypart.h" #include "calendar.h" @@ -13,7 +12,7 @@ // Set the stage for a particular ambient and target temperature and run update_bodytemp() until // core body temperature settles. -static void temperature_check( player *p, const int ambient_temp, const int target_temp ) +static void temperature_check( Character *p, const int ambient_temp, const int target_temp ) { get_weather().temperature = ambient_temp; for( int i = 0 ; i < num_bp; i++ ) { @@ -40,7 +39,7 @@ static void temperature_check( player *p, const int ambient_temp, const int targ CHECK( high > p->temp_cur[0] ); } -static void equip_clothing( player *p, const std::string &clothing ) +static void equip_clothing( Character *p, const std::string &clothing ) { const item article( clothing, 0 ); p->wear_item( article ); @@ -48,7 +47,7 @@ static void equip_clothing( player *p, const std::string &clothing ) // Run the tests for each of the temperature setpoints. // ambient_temps MUST have 7 values or we'll segfault. -static void test_temperature_spread( player *p, const std::array &ambient_temps ) +static void test_temperature_spread( Character *p, const std::array &ambient_temps ) { temperature_check( p, ambient_temps[0], BODYTEMP_FREEZING ); temperature_check( p, ambient_temps[1], BODYTEMP_VERY_COLD ); @@ -59,14 +58,15 @@ static void test_temperature_spread( player *p, const std::array &ambien temperature_check( p, ambient_temps[6], BODYTEMP_SCORCHING ); } -TEST_CASE( "Player body temperatures converge on expected values.", "[.bodytemp]" ) +TEST_CASE( "player body temperatures converge on expected values.", "[.bodytemp]" ) { - player &dummy = get_avatar(); + Character &dummy = get_player_character(); - // Remove first worn item until there are none left. - std::list temp; - while( dummy.takeoff( dummy.i_at( -2 ), &temp ) ) {} + // Strip off any potentially encumbering clothing. + dummy.remove_worn_items_with( []( item & ) { + return true; + } ); // See http://personal.cityu.edu.hk/~bsapplec/heat.htm for temperature basis. // As we aren't modeling metabolic rate, assume 2 METS when not sleeping. diff --git a/tests/projectile_test.cpp b/tests/projectile_test.cpp index c1bfb3aff4303..2c4f4dc318ade 100644 --- a/tests/projectile_test.cpp +++ b/tests/projectile_test.cpp @@ -1,7 +1,7 @@ #include "catch/catch.hpp" -#include "avatar.h" #include "ballistics.h" +#include "character.h" #include "dispersion.h" #include "game.h" #include "itype.h" @@ -21,7 +21,8 @@ static tripoint projectile_end_point( const std::vector &range, const dealt_projectile_attack attack; - attack = projectile_attack( test_proj, range[0], range[2], dispersion_sources(), &get_avatar(), + attack = projectile_attack( test_proj, range[0], range[2], dispersion_sources(), + &get_player_character(), nullptr ); return attack.end_point; @@ -33,7 +34,7 @@ TEST_CASE( "projectiles_through_obstacles", "[projectile]" ) map &here = get_map(); // Move the player out of the way of the test area - get_avatar().setpos( { 2, 2, 0 } ); + get_player_character().setpos( { 2, 2, 0 } ); // Ensure that a projectile fired from a gun can pass through a chain link fence // First, set up a test area - three tiles in a row diff --git a/tests/vehicle_drag_test.cpp b/tests/vehicle_drag_test.cpp index 499b2eafc1ddb..a7268479d814e 100644 --- a/tests/vehicle_drag_test.cpp +++ b/tests/vehicle_drag_test.cpp @@ -3,10 +3,10 @@ #include #include -#include "avatar.h" #include "bodypart.h" #include "calendar.h" #include "catch/catch.hpp" +#include "character.h" #include "map.h" #include "map_helpers.h" #include "point.h" @@ -27,7 +27,7 @@ static void clear_game_drag( const ter_id &terrain ) clear_creatures(); clear_npcs(); - avatar &player_character = get_avatar(); + Character &player_character = get_player_character(); // Move player somewhere safe CHECK( !player_character.in_vehicle ); player_character.setpos( tripoint_zero ); diff --git a/tests/vehicle_power_test.cpp b/tests/vehicle_power_test.cpp index 74b0fbb5fb372..6d016d910b3bc 100644 --- a/tests/vehicle_power_test.cpp +++ b/tests/vehicle_power_test.cpp @@ -2,10 +2,10 @@ #include #include -#include "avatar.h" #include "bodypart.h" #include "calendar.h" #include "catch/catch.hpp" +#include "character.h" #include "map.h" #include "map_helpers.h" #include "point.h" @@ -17,9 +17,10 @@ static const itype_id fuel_type_battery( "battery" ); static const itype_id fuel_type_plut_cell( "plut_cell" ); static const efftype_id effect_blind( "blind" ); +// TODO: Move this into player_helpers to avoid character include. static void reset_player() { - avatar &player_character = get_avatar(); + Character &player_character = get_player_character(); // Move player somewhere safe REQUIRE( !player_character.in_vehicle ); player_character.setpos( tripoint_zero ); diff --git a/tests/vehicle_test.cpp b/tests/vehicle_test.cpp index 82cbe00928294..c6c5b63e0d31f 100644 --- a/tests/vehicle_test.cpp +++ b/tests/vehicle_test.cpp @@ -19,7 +19,7 @@ TEST_CASE( "detaching_vehicle_unboards_passengers" ) const tripoint test_origin( 60, 60, 0 ); const tripoint vehicle_origin = test_origin; map &here = get_map(); - avatar &player_character = get_avatar(); + Character &player_character = get_player_character(); vehicle *veh_ptr = here.add_vehicle( vproto_id( "bicycle" ), vehicle_origin, -90, 0, 0 ); here.board_vehicle( test_origin, &player_character ); REQUIRE( player_character.in_vehicle ); diff --git a/tests/vehicle_turrets_test.cpp b/tests/vehicle_turrets_test.cpp index 6c319c9adf96d..a1416f8983660 100644 --- a/tests/vehicle_turrets_test.cpp +++ b/tests/vehicle_turrets_test.cpp @@ -5,8 +5,8 @@ #include #include "ammo.h" -#include "avatar.h" #include "catch/catch.hpp" +#include "character.h" #include "item.h" #include "item_location.h" #include "itype.h" @@ -61,7 +61,7 @@ static const vpart_info *biggest_tank( const ammotype &ammo ) TEST_CASE( "vehicle_turret", "[vehicle] [gun] [magazine] [.]" ) { map &here = get_map(); - avatar &player_character = get_avatar(); + Character &player_character = get_player_character(); for( auto e : turret_types() ) { SECTION( e->name() ) { vehicle *veh = here.add_vehicle( vproto_id( "none" ), point( 65, 65 ), 270, 0, 0 ); diff --git a/tests/visitable_remove_test.cpp b/tests/visitable_remove_test.cpp index 696208e169996..fc6cd722d4894 100644 --- a/tests/visitable_remove_test.cpp +++ b/tests/visitable_remove_test.cpp @@ -4,11 +4,9 @@ #include #include -#include "avatar.h" #include "calendar.h" #include "cata_utility.h" #include "catch/catch.hpp" -#include "game.h" #include "inventory.h" #include "item.h" #include "item_contents.h" @@ -16,7 +14,7 @@ #include "map.h" #include "map_selector.h" #include "optional.h" -#include "player.h" +#include "character.h" #include "point.h" #include "rng.h" #include "type_id.h" @@ -46,25 +44,26 @@ TEST_CASE( "visitable_remove", "[visitable]" ) REQUIRE( item( container_id ).is_container() ); REQUIRE( item( worn_id ).is_container() ); - player &p = g->u; + Character &p = get_player_character(); p.worn.clear(); p.worn.push_back( item( "backpack" ) ); p.inv.clear(); p.remove_weapon(); p.wear_item( item( "backpack" ) ); // so we don't drop anything + map &here = get_map(); // check if all tiles within radius are loaded within current submap and passable - const auto suitable = []( const tripoint & pos, const int radius ) { + const auto suitable = [&here]( const tripoint & pos, const int radius ) { std::vector tiles = closest_tripoints_first( pos, radius ); - return std::all_of( tiles.begin(), tiles.end(), []( const tripoint & e ) { - if( !g->m.inbounds( e ) ) { + return std::all_of( tiles.begin(), tiles.end(), [&here]( const tripoint & e ) { + if( !here.inbounds( e ) ) { return false; } - if( const optional_vpart_position vp = g->m.veh_at( e ) ) { - g->m.destroy_vehicle( &vp->vehicle() ); + if( const optional_vpart_position vp = here.veh_at( e ) ) { + here.destroy_vehicle( &vp->vehicle() ); } - g->m.i_clear( e ); - return g->m.passable( e ); + here.i_clear( e ); + return here.passable( e ); } ); }; @@ -306,11 +305,11 @@ TEST_CASE( "visitable_remove", "[visitable]" ) if( i == 0 || tiles.empty() ) { // always place at least one bottle on player tile our++; - g->m.add_item( p.pos(), obj ); + here.add_item( p.pos(), obj ); } else { // randomly place bottles on adjacent tiles adj++; - g->m.add_item( random_entry( tiles ), obj ); + here.add_item( random_entry( tiles ), obj ); } } REQUIRE( our + adj == count ); @@ -416,13 +415,13 @@ TEST_CASE( "visitable_remove", "[visitable]" ) std::vector tiles = closest_tripoints_first( p.pos(), 1 ); tiles.erase( tiles.begin() ); // player tile tripoint veh = random_entry( tiles ); - REQUIRE( g->m.add_vehicle( vproto_id( "shopping_cart" ), veh, 0, 0, 0 ) ); + REQUIRE( here.add_vehicle( vproto_id( "shopping_cart" ), veh, 0, 0, 0 ) ); - REQUIRE( std::count_if( tiles.begin(), tiles.end(), []( const tripoint & e ) { - return static_cast( g->m.veh_at( e ) ); + REQUIRE( std::count_if( tiles.begin(), tiles.end(), [&here]( const tripoint & e ) { + return static_cast( here.veh_at( e ) ); } ) == 1 ); - const cata::optional vp = g->m.veh_at( veh ).part_with_feature( "CARGO", true ); + const cata::optional vp = here.veh_at( veh ).part_with_feature( "CARGO", true ); REQUIRE( vp ); vehicle *const v = &vp->vehicle(); const int part = vp->part_index(); From 632f66f2c1656f722248090a21b680b5c9d5e3d5 Mon Sep 17 00:00:00 2001 From: Rail-Runner <8814734+Rail-Runner@users.noreply.github.com> Date: Wed, 1 Jul 2020 18:01:21 +0300 Subject: [PATCH 030/420] Add recipe for molasses --- data/json/recipes/food/other.json | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 data/json/recipes/food/other.json diff --git a/data/json/recipes/food/other.json b/data/json/recipes/food/other.json new file mode 100644 index 0000000000000..669fae5381065 --- /dev/null +++ b/data/json/recipes/food/other.json @@ -0,0 +1,17 @@ +[ + { + "type": "recipe", + "result": "molasses", + "category": "CC_FOOD", + "subcategory": "CSC_FOOD_OTHER", + "skill_used": "cooking", + "difficulty": 3, + "time": "50 m", + "batch_time_factors": [ 20, 1 ], + "charges": 1, + "autolearn": true, + "qualities": [ { "id": "BOIL", "level": 2 } ], + "tools": [ [ [ "surface_heat", 16, "LIST" ] ] ], + "components": [ [ [ "beet_syrup", 6 ] ] ] + } +] From 0dfd12a93cbbe571b102b54697ebf0d78210bbae Mon Sep 17 00:00:00 2001 From: Sage Barton Date: Wed, 1 Jul 2020 13:13:57 -0500 Subject: [PATCH 031/420] copied json for sheath from survivor belt to survivor utility belt --- data/json/items/tool_armor.json | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/data/json/items/tool_armor.json b/data/json/items/tool_armor.json index a0a718705bb39..942a88def7416 100644 --- a/data/json/items/tool_armor.json +++ b/data/json/items/tool_armor.json @@ -1878,10 +1878,11 @@ "material_thickness": 2, "pocket_data": [ { - "pocket_type": "CONTAINER", + "holster": true, "min_item_volume": "250 ml", "max_contains_volume": "1 L", - "max_contains_weight": "1 kg", + "max_contains_weight": "2 kg", + "max_item_length": "70 cm", "moves": 3, "flag_restriction": [ "SHEATH_KNIFE", "SHEATH_SWORD" ] } From 9419cac8103fa9842235b2c81f2d9b50c1056f89 Mon Sep 17 00:00:00 2001 From: ZhilkinSerg Date: Wed, 1 Jul 2020 22:04:55 +0300 Subject: [PATCH 032/420] Fix compiling when using SDL and SOUND --- src/sounds.cpp | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/sounds.cpp b/src/sounds.cpp index 41af771d22db3..dc9f0d3e62129 100644 --- a/src/sounds.cpp +++ b/src/sounds.cpp @@ -1066,9 +1066,10 @@ sfx::sound_thread::sound_thread( const tripoint &source, const tripoint &target, { // This is function is run in the main thread. const int heard_volume = get_heard_volume( source ); - const player *p = g->critter_at( source ); - if( !p ) { - p = &g->u; + npc *np = g->critter_at( source ); + const player &p = np ? static_cast( *np ) : + dynamic_cast( get_player_character() ); + if( !p.is_npc() ) { // sound comes from the same place as the player is, calculation of angle wouldn't work ang_src = 0; vol_src = heard_volume; @@ -1079,8 +1080,8 @@ sfx::sound_thread::sound_thread( const tripoint &source, const tripoint &target, vol_targ = std::max( heard_volume - 20, 0 ); } ang_targ = get_heard_angle( target ); - weapon_skill = p->weapon.melee_skill(); - weapon_volume = p->weapon.volume() / units::legacy_volume_factor; + weapon_skill = p.weapon.melee_skill(); + weapon_volume = p.weapon.volume() / units::legacy_volume_factor; } // Operator overload required for thread API. From 8368932b644cb9a879d78d464afd385d808c089d Mon Sep 17 00:00:00 2001 From: Sergey Alirzaev Date: Thu, 2 Jul 2020 00:46:08 +0300 Subject: [PATCH 033/420] Don't damage legs wearing rubber boots in acid puddle and tone down the immediate effect a little while we're at it fixes https://github.com/CleverRaven/Cataclysm-DDA/issues/38416 --- src/map_field.cpp | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/map_field.cpp b/src/map_field.cpp index 293f011238b20..bce1580c891ec 100644 --- a/src/map_field.cpp +++ b/src/map_field.cpp @@ -124,7 +124,7 @@ int map::burn_body_part( player &u, field_entry &cur, body_part bp, const int sc { int total_damage = 0; const int intensity = cur.get_field_intensity(); - const int damage = rng( 1, scale + intensity ); + const int damage = rng( 1, ( scale + intensity ) / 2 ); // A bit ugly, but better than being annoyed by acid when in hazmat if( u.get_armor_type( DT_ACID, convert_bp( bp ) ) < damage ) { const dealt_damage_instance ddi = u.deal_damage( nullptr, convert_bp( bp ).id(), @@ -1387,16 +1387,13 @@ void map::player_in_field( player &u ) // you're certainly not standing in it. if( !u.in_vehicle && !u.has_trait( trait_ACIDPROOF ) ) { int total_damage = 0; - const int intensity = cur.get_field_intensity(); - // 1-3 at intensity, 1-4 at 2, 1-5 at 3 total_damage += burn_body_part( u, cur, bp_foot_l, 2 ); total_damage += burn_body_part( u, cur, bp_foot_r, 2 ); - // 1 dmg at 1 intensity, 1-3 at 2, 1-5 at 3 - total_damage += burn_body_part( u, cur, bp_leg_l, intensity - 1 ); - total_damage += burn_body_part( u, cur, bp_leg_r, intensity - 1 ); const bool on_ground = u.is_on_ground(); if( on_ground ) { - // Before, it would just break the legs and leave the survivor alone + // Apply the effect to the remaining body parts + total_damage += burn_body_part( u, cur, bp_leg_l, 2 ); + total_damage += burn_body_part( u, cur, bp_leg_r, 2 ); total_damage += burn_body_part( u, cur, bp_hand_l, 2 ); total_damage += burn_body_part( u, cur, bp_hand_r, 2 ); total_damage += burn_body_part( u, cur, bp_torso, 2 ); From 99a47b91b1c3963e16238b2757e86aa8b9c71038 Mon Sep 17 00:00:00 2001 From: "U-N\\N" Date: Thu, 2 Jul 2020 01:12:48 +0200 Subject: [PATCH 034/420] fix transparency --- src/map.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/map.cpp b/src/map.cpp index cb4ffcf43187a..8a9dc81e0f24d 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -5342,7 +5342,7 @@ void map::remove_field( const tripoint &p, const field_type_id &field_to_remove p.y / SEEX ) * MAPSIZE ) ) ); } const auto &fdata = field_to_remove.obj(); - if( fdata.is_transparent() ) { + if( !fdata.is_transparent() ) { set_transparency_cache_dirty( p.z ); } if( fdata.is_dangerous() ) { From 04971e057147374c9d065a5de96446ff34f7e5ae Mon Sep 17 00:00:00 2001 From: Kevin Granade Date: Wed, 1 Jul 2020 23:44:43 +0000 Subject: [PATCH 035/420] Migrate map references part ten --- src/inventory.cpp | 20 ++-- src/melee.cpp | 47 +++++---- src/player_hardcoded_effects.cpp | 55 ++++++----- src/sounds.cpp | 4 +- src/sounds.h | 3 +- src/suffer.cpp | 33 ++++--- src/veh_interact.cpp | 165 +++++++++++++++++-------------- tests/map_helpers.cpp | 47 +++++---- 8 files changed, 206 insertions(+), 168 deletions(-) diff --git a/src/inventory.cpp b/src/inventory.cpp index 8baac97a3e809..04a048727ddb9 100644 --- a/src/inventory.cpp +++ b/src/inventory.cpp @@ -261,6 +261,7 @@ void inventory::update_cache_with_item( item &newit ) char inventory::find_usable_cached_invlet( const itype_id &item_type ) { + Character &player_character = get_player_character(); // Some of our preferred letters might already be used. for( auto invlet : invlet_cache.invlets_for( item_type ) ) { // Don't overwrite user assignments. @@ -268,7 +269,7 @@ char inventory::find_usable_cached_invlet( const itype_id &item_type ) continue; } // Check if anything is using this invlet. - if( g->u.invlet_to_item( invlet ) != nullptr ) { + if( player_character.invlet_to_item( invlet ) != nullptr ) { continue; } return invlet; @@ -281,6 +282,7 @@ item &inventory::add_item( item newit, bool keep_invlet, bool assign_invlet, boo { binned = false; + Character &player_character = get_player_character(); if( should_stack ) { // See if we can't stack this item. for( auto &elem : items ) { @@ -302,7 +304,7 @@ item &inventory::add_item( item newit, bool keep_invlet, bool assign_invlet, boo return elem.back(); } else if( keep_invlet && assign_invlet && it_ref->invlet == newit.invlet ) { // If keep_invlet is true, we'll be forcing other items out of their current invlet. - assign_empty_invlet( *it_ref, g->u ); + assign_empty_invlet( *it_ref, player_character ); } } } @@ -410,7 +412,7 @@ void inventory::form_from_map( const tripoint &origin, int range, const Characte bool assign_invlet, bool clear_path ) { - form_from_map( g->m, origin, range, pl, assign_invlet, clear_path ); + form_from_map( get_map(), origin, range, pl, assign_invlet, clear_path ); } void inventory::form_from_zone( map &m, std::unordered_set &zone_pts, const Character *pl, @@ -877,6 +879,8 @@ item *inventory::most_appropriate_painkiller( int pain ) void inventory::rust_iron_items() { + Character &player_character = get_player_character(); + map &here = get_map(); for( auto &elem : items ) { for( auto &elem_stack_iter : elem ) { if( elem_stack_iter.made_of( material_id( "iron" ) ) && @@ -894,7 +898,7 @@ void inventory::rust_iron_items() elem_stack_iter.base_volume().value() ) / 250 ) ) ) ) && // ^season length ^14/5*0.75/pi (from volume of sphere) //Freshwater without oxygen rusts slower than air - g->m.water_from( g->u.pos() ).typeId() == itype_salt_water ) { + here.water_from( player_character.pos() ).typeId() == itype_salt_water ) { elem_stack_iter.inc_damage( DT_ACID ); // rusting never completely destroys an item add_msg( m_bad, _( "Your %s is damaged by rust." ), elem_stack_iter.tname() ); } @@ -1040,8 +1044,7 @@ void inventory::assign_empty_invlet( item &it, const Character &p, const bool fo if( cur_inv.count() < inv_chars.size() ) { // XXX YUCK I don't know how else to get the keybindings // FIXME: Find a better way to get bound keys - avatar &u = g->u; - inventory_selector selector( u ); + inventory_selector selector( get_avatar() ); for( const auto &inv_char : inv_chars ) { if( assigned_invlet.count( inv_char ) ) { @@ -1101,11 +1104,12 @@ void inventory::update_invlet( item &newit, bool assign_invlet ) } } + Character &player_character = get_player_character(); // Remove letters that have been assigned to other items in the inventory if( newit.invlet ) { char tmp_invlet = newit.invlet; newit.invlet = '\0'; - if( g->u.invlet_to_item( tmp_invlet ) == nullptr ) { + if( player_character.invlet_to_item( tmp_invlet ) == nullptr ) { newit.invlet = tmp_invlet; } } @@ -1118,7 +1122,7 @@ void inventory::update_invlet( item &newit, bool assign_invlet ) // Give the item an invlet if it has none if( !newit.invlet ) { - assign_empty_invlet( newit, g->u ); + assign_empty_invlet( newit, player_character ); } } } diff --git a/src/melee.cpp b/src/melee.cpp index 63ac92469fb80..bfa32267045db 100644 --- a/src/melee.cpp +++ b/src/melee.cpp @@ -265,7 +265,7 @@ bool Character::handle_melee_wear( item &shield, float wear_multiplier ) if( comp.typeId() == big_comp && !is_armed() ) { wield( comp ); } else { - g->m.add_item_or_charges( pos(), comp ); + get_map().add_item_or_charges( pos(), comp ); } } } else { @@ -454,6 +454,7 @@ void Character::melee_attack( Creature &t, bool allow_special, const matec_id &f int move_cost = attack_speed( *cur_weapon ); + Character &player_character = get_player_character(); if( hit_spread < 0 ) { int stumble_pen = stumble( *this, *cur_weapon ); sfx::generate_melee_sound( pos(), t.pos(), false, false ); @@ -476,7 +477,7 @@ void Character::melee_attack( Creature &t, bool allow_special, const matec_id &f } else { add_msg( _( "You miss." ) ); } - } else if( g->u.sees( *this ) ) { + } else if( player_character.sees( *this ) ) { if( stumble_pen >= 60 ) { add_msg( _( "%s misses and stumbles with the momentum." ), name ); } else if( stumble_pen >= 10 ) { @@ -502,7 +503,7 @@ void Character::melee_attack( Creature &t, bool allow_special, const matec_id &f } else { melee::melee_stats.hit_count += 1; // Remember if we see the monster at start - it may change - const bool seen = g->u.sees( t ); + const bool seen = player_character.sees( t ); // Start of attacks. const bool critical_hit = scored_crit( t.dodge_roll(), *cur_weapon ); if( critical_hit ) { @@ -594,7 +595,7 @@ void Character::melee_attack( Creature &t, bool allow_special, const matec_id &f } // Treat monster as seen if we see it before or after the attack - if( seen || g->u.sees( t ) ) { + if( seen || player_character.sees( t ) ) { std::string message = melee_message( technique, *this, dealt_dam ); player_hit_message( this, message, t, dam, critical_hit ); } else { @@ -664,6 +665,7 @@ void player::reach_attack( const tripoint &p ) int move_cost = attack_speed( weapon ); int skill = std::min( 10, get_skill_level( skill_stabbing ) ); int t = 0; + map &here = get_map(); std::vector path = line_to( pos(), p, t, 0 ); path.pop_back(); // Last point is our critter for( const tripoint &path_point : path ) { @@ -677,13 +679,13 @@ void player::reach_attack( const tripoint &p ) critter = inter; break; /** @EFFECT_STABBING increases ability to reach attack through fences */ - } else if( g->m.impassable( path_point ) && + } else if( here.impassable( path_point ) && // Fences etc. Spears can stab through those !( weapon.has_flag( "SPEAR" ) && - g->m.has_flag( "THIN_OBSTACLE", path_point ) && + here.has_flag( "THIN_OBSTACLE", path_point ) && x_in_y( skill, 10 ) ) ) { /** @EFFECT_STR increases bash effects when reach attacking past something */ - g->m.bash( path_point, str_cur + weapon.damage_melee( DT_BASH ) ); + here.bash( path_point, str_cur + weapon.damage_melee( DT_BASH ) ); handle_melee_wear( weapon ); mod_moves( -move_cost ); return; @@ -830,7 +832,7 @@ float player::get_dodge() const if( has_effect( effect_grabbed ) ) { int zed_number = 0; - for( auto &dest : g->m.points_in_radius( pos(), 1, 0 ) ) { + for( auto &dest : get_map().points_in_radius( pos(), 1, 0 ) ) { const monster *const mon = g->critter_at( dest ); if( mon && mon->has_effect( effect_grabbing ) ) { zed_number++; @@ -1170,7 +1172,7 @@ matec_id Character::pick_technique( Creature &t, const item &weap, bool downed = t.has_effect( effect_downed ); bool stunned = t.has_effect( effect_stunned ); - bool wall_adjacent = g->m.is_wall_adjacent( pos() ); + bool wall_adjacent = get_map().is_wall_adjacent( pos() ); // first add non-aoe tecs for( const matec_id &tec_id : all ) { @@ -1353,7 +1355,7 @@ bool Character::valid_aoe_technique( Creature &t, const ma_technique &technique, } if( targets.empty() && technique.aoe == "spin" ) { - for( const tripoint &tmp : g->m.points_in_radius( pos(), 1 ) ) { + for( const tripoint &tmp : get_map().points_in_radius( pos(), 1 ) ) { if( tmp == t.pos() ) { continue; } @@ -1476,6 +1478,7 @@ void Character::perform_technique( const ma_technique &technique, Creature &t, d t.add_effect( effect_stunned, rng( 1_turns, time_duration::from_turns( technique.stun_dur ) ) ); } + map &here = get_map(); if( technique.knockback_dist ) { const tripoint prev_pos = t.pos(); // track target startpoint for knockback_follow const int kb_offset_x = rng( -technique.knockback_spread, technique.knockback_spread ); @@ -1486,10 +1489,10 @@ void Character::perform_technique( const ma_technique &technique, Creature &t, d } // This technique makes the player follow into the tile the target was knocked from if( technique.knockback_follow ) { - const optional_vpart_position vp0 = g->m.veh_at( pos() ); + const optional_vpart_position vp0 = here.veh_at( pos() ); vehicle *const veh0 = veh_pointer_or_null( vp0 ); - bool to_swimmable = g->m.has_flag( "SWIMMABLE", prev_pos ); - bool to_deepwater = g->m.has_flag( TFLAG_DEEP_WATER, prev_pos ); + bool to_swimmable = here.has_flag( "SWIMMABLE", prev_pos ); + bool to_deepwater = here.has_flag( TFLAG_DEEP_WATER, prev_pos ); // Check if it's possible to move to the new tile bool move_issue = @@ -1497,7 +1500,7 @@ void Character::perform_technique( const ma_technique &technique, Creature &t, d ( to_swimmable && to_deepwater ) || // Dive into deep water is_mounted() || ( veh0 != nullptr && std::abs( veh0->velocity ) > 100 ) || // Diving from moving vehicle - ( veh0 != nullptr && veh0->player_in_control( g->u ) ) || // Player is driving + ( veh0 != nullptr && veh0->player_in_control( get_avatar() ) ) || // Player is driving has_effect( effect_amigara ); if( !move_issue ) { @@ -1524,7 +1527,7 @@ void Character::perform_technique( const ma_technique &technique, Creature &t, d } if( technique.disarms && p != nullptr && p->is_armed() ) { - g->m.add_item_or_charges( p->pos(), p->remove_weapon() ); + here.add_item_or_charges( p->pos(), p->remove_weapon() ); if( p->is_player() ) { add_msg_if_npc( _( " disarms you!" ) ); } else { @@ -2193,6 +2196,7 @@ void player_hit_message( Character *attacker, const std::string &message, std::string sSCTmod; game_message_type gmtSCTcolor = m_good; + Character &player_character = get_player_character(); if( dam <= 0 ) { if( attacker->is_npc() ) { //~ NPC hits something but does no damage @@ -2202,9 +2206,9 @@ void player_hit_message( Character *attacker, const std::string &message, msg = string_format( _( "%s but do no damage." ), message ); } msgtype = m_neutral; - } else if( - crit ) { //Player won't see exact numbers of damage dealt by NPC unless player has DEBUG_NIGHTVISION trait - if( attacker->is_npc() && !g->u.has_trait( trait_DEBUG_NIGHTVISION ) ) { + } else if( crit ) { + //Player won't see exact numbers of damage dealt by NPC unless player has DEBUG_NIGHTVISION trait + if( attacker->is_npc() && !player_character.has_trait( trait_DEBUG_NIGHTVISION ) ) { //~ NPC hits something (critical) msg = string_format( _( "%s. Critical!" ), message ); } else { @@ -2214,7 +2218,7 @@ void player_hit_message( Character *attacker, const std::string &message, sSCTmod = _( "Critical!" ); gmtSCTcolor = m_critical; } else { - if( attacker->is_npc() && !g->u.has_trait( trait_DEBUG_NIGHTVISION ) ) { + if( attacker->is_npc() && !player_character.has_trait( trait_DEBUG_NIGHTVISION ) ) { //~ NPC hits something msg = string_format( _( "%s." ), message ); } else { @@ -2372,6 +2376,7 @@ void player::disarm( npc &target ) return; } + map &here = get_map(); // hitspread >= 0, which means we are going to disarm by grabbing target by their weapon if( !is_armed() ) { /** @EFFECT_UNARMED increases chance to disarm, bonus when nothing wielded */ @@ -2389,7 +2394,7 @@ void player::disarm( npc &target ) add_msg( _( "You grab at %s and pull with all your force, but it drops nearby!" ), it.tname() ); const tripoint tp = target.pos() + tripoint( rng( -1, 1 ), rng( -1, 1 ), 0 ); - g->m.add_item_or_charges( tp, target.i_rem( &it ) ); + here.add_item_or_charges( tp, target.i_rem( &it ) ); mod_moves( -100 ); } else { add_msg( _( "You grab at %s and pull with all your force, but in vain!" ), it.tname() ); @@ -2406,7 +2411,7 @@ void player::disarm( npc &target ) add_msg( _( "You smash %s with all your might forcing their %s to drop down nearby!" ), target.name, it.tname() ); const tripoint tp = target.pos() + tripoint( rng( -1, 1 ), rng( -1, 1 ), 0 ); - g->m.add_item_or_charges( tp, target.i_rem( &it ) ); + here.add_item_or_charges( tp, target.i_rem( &it ) ); } else { add_msg( _( "You smash %s with all your might but %s remains in their hands!" ), target.name, it.tname() ); diff --git a/src/player_hardcoded_effects.cpp b/src/player_hardcoded_effects.cpp index f3bb49fccfbb7..652c70b49b81e 100644 --- a/src/player_hardcoded_effects.cpp +++ b/src/player_hardcoded_effects.cpp @@ -6,7 +6,7 @@ #include #include "activity_handlers.h" -#include "avatar.h" +#include "character.h" #include "effect.h" #include "enums.h" #include "event.h" @@ -182,8 +182,9 @@ static void eff_fun_fungus( player &u, effect &it ) _( " vomits thousands of live spores!" ) ); u.moves = -500; - fungal_effects fe( *g, g->m ); - for( const tripoint &sporep : g->m.points_in_radius( u.pos(), 1 ) ) { + map &here = get_map(); + fungal_effects fe( *g, here ); + for( const tripoint &sporep : here.points_in_radius( u.pos(), 1 ) ) { if( sporep == u.pos() ) { continue; } @@ -489,6 +490,8 @@ void player::hardcoded_effects( effect &it ) int intense = it.get_intensity(); const bodypart_id &bp = convert_bp( it.get_bp() ).id(); bool sleeping = has_effect( effect_sleep ); + map &here = get_map(); + Character &player_character = get_player_character(); if( id == effect_dermatik ) { bool triggered = false; int formication_chance = 3600; @@ -537,8 +540,8 @@ void player::hardcoded_effects( effect &it ) //~ %s is bodypart in accusative. add_msg( m_warning, _( "You start scratching your %s!" ), body_part_name_accusative( bp ) ); - g->u.cancel_activity(); - } else if( g->u.sees( pos() ) ) { + player_character.cancel_activity(); + } else if( player_character.sees( pos() ) ) { //~ 1$s is NPC name, 2$s is bodypart in accusative. add_msg( _( "%1$s starts scratching their %2$s!" ), name, body_part_name_accusative( bp ) ); @@ -588,13 +591,13 @@ void player::hardcoded_effects( effect &it ) tries++; } while( g->critter_at( dest ) && tries < 10 ); if( tries < 10 ) { - if( g->m.impassable( dest ) ) { - g->m.make_rubble( dest, f_rubble_rock, true ); + if( here.impassable( dest ) ) { + here.make_rubble( dest, f_rubble_rock, true ); } MonsterGroupResult spawn_details = MonsterGroupManager::GetResultFromGroup( GROUP_NETHER ); g->place_critter_at( spawn_details.name, dest ); - if( g->u.sees( dest ) ) { + if( player_character.sees( dest ) ) { g->cancel_activity_or_ignore_query( distraction_type::hostile_spotted_far, _( "A monster appears nearby!" ) ); add_msg_if_player( m_warning, _( "A portal opens nearby, and a monster crawls through!" ) ); @@ -627,9 +630,9 @@ void player::hardcoded_effects( effect &it ) } } else if( id == effect_tindrift ) { add_msg_if_player( m_bad, _( "You are beset with a vision of a prowling beast." ) ); - for( const tripoint &dest : g->m.points_in_radius( pos(), 6 ) ) { - if( g->m.is_cornerfloor( dest ) ) { - g->m.add_field( dest, fd_tindalos_rift, 3 ); + for( const tripoint &dest : here.points_in_radius( pos(), 6 ) ) { + if( here.is_cornerfloor( dest ) ) { + here.add_field( dest, fd_tindalos_rift, 3 ); add_msg_if_player( m_info, _( "Your surroundings are permeated with a foul scent." ) ); //Remove the effect, since it's done all it needs to do to the target. remove_effect( effect_tindrift ); @@ -691,13 +694,13 @@ void player::hardcoded_effects( effect &it ) } } while( g->critter_at( dest ) ); if( tries < 10 ) { - if( g->m.impassable( dest ) ) { - g->m.make_rubble( dest, f_rubble_rock, true ); + if( here.impassable( dest ) ) { + here.make_rubble( dest, f_rubble_rock, true ); } MonsterGroupResult spawn_details = MonsterGroupManager::GetResultFromGroup( GROUP_NETHER ); g->place_critter_at( spawn_details.name, dest ); - if( g->u.sees( dest ) ) { + if( player_character.sees( dest ) ) { g->cancel_activity_or_ignore_query( distraction_type::hostile_spotted_far, _( "A monster appears nearby!" ) ); add_msg( m_warning, _( "A portal opens nearby, and a monster crawls through!" ) ); @@ -888,7 +891,7 @@ void player::hardcoded_effects( effect &it ) } else if( id == effect_grabbed ) { set_num_blocks_bonus( get_num_blocks_bonus() - 1 ); int zed_number = 0; - for( auto &dest : g->m.points_in_radius( pos(), 1, 0 ) ) { + for( auto &dest : here.points_in_radius( pos(), 1, 0 ) ) { const monster *const mon = g->critter_at( dest ); if( mon && mon->has_effect( effect_grabbing ) ) { zed_number += mon->get_grab_strength(); @@ -1076,7 +1079,7 @@ void player::hardcoded_effects( effect &it ) if( calendar::once_every( 10_minutes ) && ( has_trait( trait_CHLOROMORPH ) || has_trait( trait_M_SKIN3 ) || has_trait( trait_WATERSLEEP ) ) && - g->m.is_outside( pos() ) ) { + here.is_outside( pos() ) ) { if( has_trait( trait_CHLOROMORPH ) ) { // Hunger and thirst fall before your Chloromorphic physiology! if( g->natural_light_level( posz() ) >= 12 && compatible_weather_types ) { @@ -1092,7 +1095,7 @@ void player::hardcoded_effects( effect &it ) } if( has_trait( trait_M_SKIN3 ) ) { // Spores happen! - if( g->m.has_flag_ter_or_furn( "FUNGUS", pos() ) ) { + if( here.has_flag_ter_or_furn( "FUNGUS", pos() ) ) { if( get_fatigue() >= 0 ) { mod_fatigue( -5 ); // Local guides need less sleep on fungal soil } @@ -1150,7 +1153,7 @@ void player::hardcoded_effects( effect &it ) trait_SEESLEEP ) ) { // People who can see while sleeping are acclimated to the light. if( has_trait( trait_HEAVYSLEEPER2 ) && !has_trait( trait_HIBERNATE ) ) { // So you can too sleep through noon - if( ( tirednessVal * 1.25 ) < g->m.ambient_light_at( pos() ) && ( get_fatigue() < 10 || + if( ( tirednessVal * 1.25 ) < here.ambient_light_at( pos() ) && ( get_fatigue() < 10 || one_in( get_fatigue() / 2 ) ) ) { add_msg_if_player( _( "It's too bright to sleep." ) ); // Set ourselves up for removal @@ -1159,14 +1162,14 @@ void player::hardcoded_effects( effect &it ) } // Ursine hibernators would likely do so indoors. Plants, though, might be in the sun. } else if( has_trait( trait_HIBERNATE ) ) { - if( ( tirednessVal * 5 ) < g->m.ambient_light_at( pos() ) && ( get_fatigue() < 10 || + if( ( tirednessVal * 5 ) < here.ambient_light_at( pos() ) && ( get_fatigue() < 10 || one_in( get_fatigue() / 2 ) ) ) { add_msg_if_player( _( "It's too bright to sleep." ) ); // Set ourselves up for removal it.set_duration( 0_turns ); woke_up = true; } - } else if( tirednessVal < g->m.ambient_light_at( pos() ) && ( get_fatigue() < 10 || + } else if( tirednessVal < here.ambient_light_at( pos() ) && ( get_fatigue() < 10 || one_in( get_fatigue() / 2 ) ) ) { add_msg_if_player( _( "It's too bright to sleep." ) ); // Set ourselves up for removal @@ -1222,12 +1225,12 @@ void player::hardcoded_effects( effect &it ) } else { int max_count = rng( 1, 3 ); int count = 0; - for( const tripoint &mp : g->m.points_in_radius( pos(), 1 ) ) { + for( const tripoint &mp : here.points_in_radius( pos(), 1 ) ) { if( mp == pos() ) { continue; } - if( g->m.has_flag( "FLAT", mp ) && - g->m.pl_sees( mp, 2 ) ) { + if( here.has_flag( "FLAT", mp ) && + here.pl_sees( mp, 2 ) ) { g->spawn_hallucination( mp ); if( ++count > max_count ) { break; @@ -1295,9 +1298,9 @@ void player::hardcoded_effects( effect &it ) } } else { if( dur == 1_turns ) { - if( g->u.has_alarm_clock() ) { - sounds::sound( g->u.pos(), 16, sounds::sound_t::alarm, _( "beep-beep-beep!" ), false, "tool", - "alarm_clock" ); + if( player_character.has_alarm_clock() ) { + sounds::sound( player_character.pos(), 16, sounds::sound_t::alarm, + _( "beep-beep-beep!" ), false, "tool", "alarm_clock" ); const std::string alarm = _( "Your alarm is going off." ); g->cancel_activity_or_ignore_query( distraction_type::noise, alarm ); add_msg( _( "Your alarm went off." ) ); diff --git a/src/sounds.cpp b/src/sounds.cpp index dc9f0d3e62129..6e93b38288376 100644 --- a/src/sounds.cpp +++ b/src/sounds.cpp @@ -1168,7 +1168,7 @@ void sfx::do_projectile_hit( const Creature &target ) play_variant_sound( "bullet_hit", "hit_flesh", heard_volume, angle, 0.8, 1.2 ); } -void sfx::do_player_death_hurt( const player &target, bool death ) +void sfx::do_player_death_hurt( const Character &target, bool death ) { int heard_volume = get_heard_volume( target.pos() ); const bool male = target.male; @@ -1514,7 +1514,7 @@ bool sfx::has_variant_sound( const std::string &, const std::string & ) } void sfx::stop_sound_effect_fade( channel, int ) { } void sfx::stop_sound_effect_timed( channel, int ) {} -void sfx::do_player_death_hurt( const player &, bool ) { } +void sfx::do_player_death_hurt( const Character &, bool ) { } void sfx::do_fatigue() { } void sfx::do_obstacle( const std::string & ) { } /*@}*/ diff --git a/src/sounds.h b/src/sounds.h index 1420f41702410..e33d1ab20fd1c 100644 --- a/src/sounds.h +++ b/src/sounds.h @@ -6,6 +6,7 @@ #include #include +class Character; class Creature; class JsonObject; class item; @@ -157,7 +158,7 @@ bool has_variant_sound( const std::string &id, const std::string &variant ); void stop_sound_effect_fade( channel channel, int duration ); void stop_sound_effect_timed( channel channel, int time ); int set_channel_volume( channel channel, int volume ); -void do_player_death_hurt( const player &target, bool death ); +void do_player_death_hurt( const Character &target, bool death ); void do_fatigue(); // @param obst should be string id of obstacle terrain or vehicle part void do_obstacle( const std::string &obst = "" ); diff --git a/src/suffer.cpp b/src/suffer.cpp index 481490761e772..f6de9b63afed1 100644 --- a/src/suffer.cpp +++ b/src/suffer.cpp @@ -13,7 +13,6 @@ #include #include "addiction.h" -#include "avatar.h" #include "bodypart.h" #include "calendar.h" #include "cata_utility.h" @@ -262,7 +261,7 @@ void Character::suffer_while_underwater() apply_damage( nullptr, bodypart_id( "torso" ), rng( 1, 4 ) ); } } - if( has_trait( trait_FRESHWATEROSMOSIS ) && !g->m.has_flag_ter( "SALT_WATER", pos() ) && + if( has_trait( trait_FRESHWATEROSMOSIS ) && !get_map().has_flag_ter( "SALT_WATER", pos() ) && get_thirst() > -60 ) { mod_thirst( -1 ); } @@ -630,9 +629,11 @@ void Character::suffer_from_asthma( const int current_stim ) add_msg_player_or_npc( m_bad, _( "You have an asthma attack!" ), " starts wheezing and coughing." ); + map &here = get_map(); if( in_sleep_state() && !has_effect( effect_narcosis ) ) { inventory map_inv; - map_inv.form_from_map( g->u.pos(), 2, &g->u ); + Character &player_character = get_player_character(); + map_inv.form_from_map( player_character.pos(), 2, &player_character ); // check if an inhaler is somewhere near bool nearby_use = auto_use || oxygenator || map_inv.has_charges( itype_inhaler, 1 ) || map_inv.has_charges( itype_oxygen_tank, 1 ) || @@ -653,10 +654,10 @@ void Character::suffer_from_asthma( const int current_stim ) } else if( nearby_use ) { // create new variable to resolve a reference issue int amount = 1; - if( !g->m.use_charges( g->u.pos(), 2, itype_inhaler, amount ).empty() ) { + if( !here.use_charges( player_character.pos(), 2, itype_inhaler, amount ).empty() ) { add_msg_if_player( m_info, _( "You use your inhaler and go back to sleep." ) ); - } else if( !g->m.use_charges( g->u.pos(), 2, itype_oxygen_tank, amount ).empty() || - !g->m.use_charges( g->u.pos(), 2, itype_smoxygen_tank, amount ).empty() ) { + } else if( !here.use_charges( player_character.pos(), 2, itype_oxygen_tank, amount ).empty() || + !here.use_charges( player_character.pos(), 2, itype_smoxygen_tank, amount ).empty() ) { add_msg_if_player( m_info, _( "You take a deep breath from your oxygen tank " "and go back to sleep." ) ); } @@ -718,7 +719,7 @@ void Character::suffer_in_sunlight() const bool leafier = has_trait( trait_LEAVES2 ) || has_trait( trait_LEAVES3 ); const bool leafiest = has_trait( trait_LEAVES3 ); int sunlight_nutrition = 0; - if( leafy && g->m.is_outside( pos() ) && ( g->light_level( pos().z ) >= 40 ) ) { + if( leafy && get_map().is_outside( pos() ) && ( g->light_level( pos().z ) >= 40 ) ) { const float weather_factor = ( g->weather.weather == WEATHER_CLEAR || g->weather.weather == WEATHER_SUNNY ) ? 1.0 : 0.5; const int player_local_temp = g->weather.get_temperature( pos() ); @@ -882,9 +883,10 @@ void Character::suffer_from_albinism() void Character::suffer_from_other_mutations() { + map &here = get_map(); if( has_trait( trait_SHARKTEETH ) && one_turn_in( 24_hours ) ) { add_msg_if_player( m_neutral, _( "You shed a tooth!" ) ); - g->m.spawn_item( pos(), "bone", 1 ); + here.spawn_item( pos(), "bone", 1 ); } if( has_active_mutation( trait_WINGS_INSECT ) ) { @@ -896,7 +898,7 @@ void Character::suffer_from_other_mutations() bool wearing_shoes = is_wearing_shoes( side::LEFT ) || is_wearing_shoes( side::RIGHT ); int root_vitamins = 0; int root_water = 0; - if( has_trait( trait_ROOTS3 ) && g->m.has_flag( flag_PLOWABLE, pos() ) && !wearing_shoes ) { + if( has_trait( trait_ROOTS3 ) && here.has_flag( flag_PLOWABLE, pos() ) && !wearing_shoes ) { root_vitamins += 1; if( get_thirst() <= -2000 ) { root_water += 51; @@ -930,7 +932,7 @@ void Character::suffer_from_other_mutations() //Web Weavers...weave web if( has_active_mutation( trait_WEB_WEAVER ) && !in_vehicle ) { // this adds intensity to if its not already there. - g->m.add_field( pos(), fd_web, 1 ); + here.add_field( pos(), fd_web, 1 ); } @@ -955,7 +957,7 @@ void Character::suffer_from_other_mutations() if( has_trait( trait_WEB_SPINNER ) && !in_vehicle && one_in( 3 ) ) { // this adds intensity to if its not already there. - g->m.add_field( pos(), fd_web, 1 ); + here.add_field( pos(), fd_web, 1 ); } bool should_mutate = has_trait( trait_UNSTABLE ) && !has_trait( trait_CHAOTIC_BAD ) && @@ -990,9 +992,10 @@ void Character::suffer_from_other_mutations() void Character::suffer_from_radiation() { + map &here = get_map(); // checking for radioactive items in inventory const int item_radiation = leak_level( "RADIOACTIVE" ); - const int map_radiation = g->m.get_radiation( pos() ); + const int map_radiation = here.get_radiation( pos() ); float rads = map_radiation / 100.0f + item_radiation / 10.0f; int rad_mut = 0; @@ -1025,8 +1028,8 @@ void Character::suffer_from_radiation() // If you can't, irradiate the player instead tripoint rad_point = pos() + point( rng( -3, 3 ), rng( -3, 3 ) ); // TODO: Radioactive vehicles? - if( g->m.get_radiation( rad_point ) < rad_mut ) { - g->m.adjust_radiation( rad_point, 1 ); + if( here.get_radiation( rad_point ) < rad_mut ) { + here.adjust_radiation( rad_point, 1 ); } else { rads += rad_mut; } @@ -1186,7 +1189,7 @@ void Character::suffer_from_bad_bionics() add_msg_if_player( m_bad, _( "You suffer a burning acidic discharge!" ) ); hurtall( 1, nullptr ); sfx::play_variant_sound( "bionics", "acid_discharge", 100 ); - sfx::do_player_death_hurt( g->u, false ); + sfx::do_player_death_hurt( get_player_character(), false ); } if( has_bionic( bio_drain ) && get_power_level() > 24_kJ && one_turn_in( 1_hours ) ) { add_msg_if_player( m_bad, _( "Your batteries discharge slightly." ) ); diff --git a/src/veh_interact.cpp b/src/veh_interact.cpp index 051bb37277794..2eb424620e8b9 100644 --- a/src/veh_interact.cpp +++ b/src/veh_interact.cpp @@ -118,27 +118,28 @@ player_activity veh_interact::serialize_activity() return player_activity(); } + avatar &player_character = get_avatar(); int time = 1000; switch( sel_cmd ) { case 'i': - time = vp->install_time( g->u ); + time = vp->install_time( player_character ); break; case 'r': if( pt != nullptr ) { if( pt->is_broken() ) { - time = vp->install_time( g->u ); + time = vp->install_time( player_character ); } else if( pt->base.max_damage() > 0 ) { - time = vp->repair_time( g->u ) * pt->base.damage() / pt->base.max_damage(); + time = vp->repair_time( player_character ) * pt->base.damage() / pt->base.max_damage(); } } break; case 'o': - time = vp->removal_time( g->u ); + time = vp->removal_time( player_character ); break; default: break; } - if( g->u.has_trait( trait_DEBUG_HS ) ) { + if( player_character.has_trait( trait_DEBUG_HS ) ) { time = 1; } player_activity res( ACT_VEHICLE, time, static_cast( sel_cmd ) ); @@ -147,11 +148,12 @@ player_activity veh_interact::serialize_activity() // otherwise (e.g. installing a new frame), just use part 0 const point q = veh->coord_translate( pt ? pt->mount : veh->part( 0 ).mount ); const vehicle_part *vpt = pt ? pt : &veh->part( 0 ); + map &here = get_map(); for( const tripoint &p : veh->get_points( true ) ) { - res.coord_set.insert( g->m.getabs( p ) ); + res.coord_set.insert( here.getabs( p ) ); } - res.values.push_back( g->m.getabs( veh->global_pos3() ).x + q.x ); // values[0] - res.values.push_back( g->m.getabs( veh->global_pos3() ).y + q.y ); // values[1] + res.values.push_back( here.getabs( veh->global_pos3() ).x + q.x ); // values[0] + res.values.push_back( here.getabs( veh->global_pos3() ).y + q.y ); // values[1] res.values.push_back( dd.x ); // values[2] res.values.push_back( dd.y ); // values[3] res.values.push_back( -dd.x ); // values[4] @@ -295,7 +297,8 @@ void veh_interact::allocate_windows() bool veh_interact::format_reqs( std::string &msg, const requirement_data &reqs, const std::map &skills, int moves ) const { - const inventory &inv = g->u.crafting_inventory(); + Character &player_character = get_player_character(); + const inventory &inv = player_character.crafting_inventory(); bool ok = reqs.can_make_with_inventory( inv, is_crafting_component ); msg += _( "Time required:\n" ); @@ -304,7 +307,7 @@ bool veh_interact::format_reqs( std::string &msg, const requirement_data &reqs, msg += _( "Skills required:\n" ); for( const auto &e : skills ) { - bool hasSkill = g->u.get_skill_level( e.first ) >= e.second; + bool hasSkill = player_character.get_skill_level( e.first ) >= e.second; if( !hasSkill ) { ok = false; } @@ -383,7 +386,9 @@ shared_ptr_fast veh_interact::create_or_get_ui_adaptor() void veh_interact::do_main_loop() { bool finish = false; - const bool owned_by_player = veh->handle_potential_theft( dynamic_cast( g->u ), true ); + Character &player_character = get_player_character(); + const bool owned_by_player = veh->handle_potential_theft( dynamic_cast + ( player_character ), true ); faction *owner_fac; if( veh->has_owner() ) { owner_fac = g->faction_manager_ptr->get( veh->get_owner() ); @@ -403,23 +408,23 @@ void veh_interact::do_main_loop() } else if( action == "QUIT" ) { finish = true; } else if( action == "INSTALL" ) { - if( veh->handle_potential_theft( dynamic_cast( g->u ) ) ) { + if( veh->handle_potential_theft( dynamic_cast( player_character ) ) ) { do_install(); } } else if( action == "REPAIR" ) { - if( veh->handle_potential_theft( dynamic_cast( g->u ) ) ) { + if( veh->handle_potential_theft( dynamic_cast( player_character ) ) ) { do_repair(); } } else if( action == "MEND" ) { - if( veh->handle_potential_theft( dynamic_cast( g->u ) ) ) { + if( veh->handle_potential_theft( dynamic_cast( player_character ) ) ) { do_mend(); } } else if( action == "REFILL" ) { - if( veh->handle_potential_theft( dynamic_cast( g->u ) ) ) { + if( veh->handle_potential_theft( dynamic_cast( player_character ) ) ) { do_refill(); } } else if( action == "REMOVE" ) { - if( veh->handle_potential_theft( dynamic_cast( g->u ) ) ) { + if( veh->handle_potential_theft( dynamic_cast( player_character ) ) ) { do_remove(); } } else if( action == "RENAME" ) { @@ -431,18 +436,18 @@ void veh_interact::do_main_loop() } } } else if( action == "SIPHON" ) { - if( veh->handle_potential_theft( dynamic_cast( g->u ) ) ) { + if( veh->handle_potential_theft( dynamic_cast( player_character ) ) ) { do_siphon(); // Siphoning may have started a player activity. If so, we should close the // vehicle dialog and continue with the activity. - finish = !g->u.activity.is_null(); + finish = !player_character.activity.is_null(); if( !finish ) { // it's possible we just invalidated our crafting inventory cache_tool_availability(); } } } else if( action == "UNLOAD" ) { - if( veh->handle_potential_theft( dynamic_cast( g->u ) ) ) { + if( veh->handle_potential_theft( dynamic_cast( player_character ) ) ) { finish = do_unload(); } } else if( action == "ASSIGN_CREW" ) { @@ -483,23 +488,24 @@ void veh_interact::do_main_loop() void veh_interact::cache_tool_availability() { - crafting_inv = g->u.crafting_inventory(); + Character &player_character = get_player_character(); + crafting_inv = player_character.crafting_inventory(); - cache_tool_availability_update_lifting( g->u.pos() ); + cache_tool_availability_update_lifting( player_character.pos() ); int mech_jack = 0; - if( g->u.is_mounted() ) { - mech_jack = g->u.mounted_creature->mech_str_addition() + 10; + if( player_character.is_mounted() ) { + mech_jack = player_character.mounted_creature->mech_str_addition() + 10; } - int max_quality = std::max( { g->u.max_quality( qual_JACK ), mech_jack, - map_selector( g->u.pos(), PICKUP_RANGE ).max_quality( qual_JACK ), - vehicle_selector( g->u.pos(), 2, true, *veh ).max_quality( qual_JACK ) + int max_quality = std::max( { player_character.max_quality( qual_JACK ), mech_jack, + map_selector( player_character.pos(), PICKUP_RANGE ).max_quality( qual_JACK ), + vehicle_selector( player_character.pos(), 2, true, *veh ).max_quality( qual_JACK ) } ); max_jack = lifting_quality_to_mass( max_quality ); } void veh_interact::cache_tool_availability_update_lifting( const tripoint &world_cursor_pos ) { - max_lift = g->u.best_nearby_lifting_assist( world_cursor_pos ); + max_lift = get_player_character().best_nearby_lifting_assist( world_cursor_pos ); } /** @@ -525,29 +531,30 @@ task_reason veh_interact::cant_do( char mode ) bool has_skill = true; bool enough_light = true; const vehicle_part_range vpr = veh->get_all_parts(); + avatar &player_character = get_avatar(); switch( mode ) { case 'i': // install mode - enough_morale = g->u.has_morale_to_craft(); + enough_morale = player_character.has_morale_to_craft(); valid_target = !can_mount.empty() && 0 == veh->tags.count( "convertible" ); //tool checks processed later - enough_light = g->u.fine_detail_vision_mod() <= 4; + enough_light = player_character.fine_detail_vision_mod() <= 4; has_tools = true; break; case 'r': // repair mode - enough_morale = g->u.has_morale_to_craft(); + enough_morale = player_character.has_morale_to_craft(); valid_target = !need_repair.empty() && cpart >= 0; // checked later has_tools = true; - enough_light = g->u.fine_detail_vision_mod() <= 4; + enough_light = player_character.fine_detail_vision_mod() <= 4; break; case 'm': { // mend mode - enough_morale = g->u.has_morale_to_craft(); - const bool toggling = g->u.has_trait( trait_DEBUG_HS ); + enough_morale = player_character.has_morale_to_craft(); + const bool toggling = player_character.has_trait( trait_DEBUG_HS ); valid_target = std::any_of( vpr.begin(), vpr.end(), [toggling]( const vpart_reference & pt ) { if( toggling ) { return pt.part().is_available() && !pt.part().faults_potential().empty(); @@ -555,7 +562,7 @@ task_reason veh_interact::cant_do( char mode ) return pt.part().is_available() && !pt.part().faults().empty(); } } ); - enough_light = g->u.fine_detail_vision_mod() <= 4; + enough_light = player_character.fine_detail_vision_mod() <= 4; // checked later has_tools = true; } @@ -570,13 +577,13 @@ task_reason veh_interact::cant_do( char mode ) case 'o': // remove mode - enough_morale = g->u.has_morale_to_craft(); + enough_morale = player_character.has_morale_to_craft(); valid_target = cpart >= 0 && 0 == veh->tags.count( "convertible" ); part_free = parts_here.size() > 1 || ( cpart >= 0 && veh->can_unmount( cpart ) ); //tool and skill checks processed later has_tools = true; has_skill = true; - enough_light = g->u.fine_detail_vision_mod() <= 4; + enough_light = player_character.fine_detail_vision_mod() <= 4; break; case 's': @@ -590,7 +597,7 @@ task_reason veh_interact::cant_do( char mode ) break; } } - has_tools = g->u.has_quality( qual_HOSE ); + has_tools = player_character.has_quality( qual_HOSE ); break; case 'd': @@ -623,7 +630,7 @@ task_reason veh_interact::cant_do( char mode ) return task_reason::UNKNOWN_TASK; } - if( std::abs( veh->velocity ) > 100 || g->u.controlling_vehicle ) { + if( std::abs( veh->velocity ) > 100 || player_character.controlling_vehicle ) { return task_reason::MOVING_VEHICLE; } if( !valid_target ) { @@ -740,29 +747,30 @@ bool veh_interact::can_install_part() const auto reqs = sel_vpart_info->install_requirements(); + avatar &player_character = get_avatar(); std::string nmsg; bool ok = format_reqs( nmsg, reqs, sel_vpart_info->install_skills, - sel_vpart_info->install_time( g->u ) ); + sel_vpart_info->install_time( player_character ) ); nmsg += _( "Additional requirements:\n" ); if( dif_eng > 0 ) { - if( g->u.get_skill_level( skill_mechanics ) < dif_eng ) { + if( player_character.get_skill_level( skill_mechanics ) < dif_eng ) { ok = false; } //~ %1$s represents the internal color name which shouldn't be translated, %2$s is skill name, and %3$i is skill level nmsg += string_format( _( "> %1$s%2$s %3$i for extra engines." ), - status_color( g->u.get_skill_level( skill_mechanics ) >= dif_eng ), + status_color( player_character.get_skill_level( skill_mechanics ) >= dif_eng ), skill_mechanics.obj().name(), dif_eng ) + "\n"; } if( dif_steering > 0 ) { - if( g->u.get_skill_level( skill_mechanics ) < dif_steering ) { + if( player_character.get_skill_level( skill_mechanics ) < dif_steering ) { ok = false; } //~ %1$s represents the internal color name which shouldn't be translated, %2$s is skill name, and %3$i is skill level nmsg += string_format( _( "> %1$s%2$s %3$i for extra steering axles." ), - status_color( g->u.get_skill_level( skill_mechanics ) >= dif_steering ), + status_color( player_character.get_skill_level( skill_mechanics ) >= dif_steering ), skill_mechanics.obj().name(), dif_steering ) + "\n"; } @@ -776,7 +784,7 @@ bool veh_interact::can_install_part() lvl = jack_quality( *veh ); str = veh->lift_strength(); use_aid = ( max_jack >= lifting_quality_to_mass( lvl ) ) || can_self_jack(); - use_str = g->u.can_lift( *veh ); + use_str = player_character.can_lift( *veh ); } else { item base( sel_vpart_info->item ); qual = qual_LIFT; @@ -784,7 +792,7 @@ bool veh_interact::can_install_part() lifting_quality_to_mass( 1 ) ); str = base.lift_strength(); use_aid = max_lift >= base.weight(); - use_str = g->u.can_lift( base ); + use_str = player_character.can_lift( base ); } if( !( use_aid || use_str ) ) { @@ -794,7 +802,7 @@ bool veh_interact::can_install_part() nc_color aid_color = use_aid ? c_green : ( use_str ? c_dark_gray : c_red ); nc_color str_color = use_str ? c_green : ( use_aid ? c_dark_gray : c_red ); - const auto helpers = g->u.get_crafting_helpers(); + const auto helpers = player_character.get_crafting_helpers(); std::string str_string; if( !helpers.empty() ) { str_string = string_format( _( "strength ( assisted ) %d" ), str ); @@ -811,7 +819,7 @@ bool veh_interact::can_install_part() sel_vpart_info->format_description( nmsg, c_light_gray, getmaxx( w_msg ) - 4 ); msg = colorize( nmsg, c_light_gray ); - return ok || g->u.has_trait( trait_DEBUG_HS ); + return ok || player_character.has_trait( trait_DEBUG_HS ); } /** @@ -1173,6 +1181,7 @@ void veh_interact::do_repair() restore_on_out_of_scope prev_hilight_part( highlight_part ); + avatar &player_character = get_avatar(); while( true ) { vehicle_part &pt = veh->part( parts_here[need_repair[pos]] ); const vpart_info &vp = pt.info(); @@ -1182,7 +1191,8 @@ void veh_interact::do_repair() // this will always be set, but the gcc thinks that sometimes it won't be bool ok = true; if( pt.is_broken() ) { - ok = format_reqs( nmsg, vp.install_requirements(), vp.install_skills, vp.install_time( g->u ) ); + ok = format_reqs( nmsg, vp.install_requirements(), vp.install_skills, + vp.install_time( player_character ) ); } else { if( vp.has_flag( "NO_REPAIR" ) || vp.repair_requirements().is_empty() || pt.base.max_damage() <= 0 ) { @@ -1204,7 +1214,7 @@ void veh_interact::do_repair() } } else { ok = format_reqs( nmsg, vp.repair_requirements() * pt.base.damage_level( 4 ), vp.repair_skills, - vp.repair_time( g->u ) * pt.base.damage() / pt.base.max_damage() ); + vp.repair_time( player_character ) * pt.base.damage() / pt.base.max_damage() ); } } @@ -1226,7 +1236,7 @@ void veh_interact::do_repair() } sel_vehicle_part = &pt; sel_vpart_info = &vp; - const std::vector helpers = g->u.get_crafting_helpers(); + const std::vector helpers = player_character.get_crafting_helpers(); for( const npc *np : helpers ) { add_msg( m_info, _( "%s helps with this task…" ), np->name ); } @@ -1264,7 +1274,8 @@ void veh_interact::do_mend() restore_on_out_of_scope> prev_title( title ); title = _( "Choose a part here to mend:" ); - const bool toggling = g->u.has_trait( trait_DEBUG_HS ); + avatar &player_character = get_avatar(); + const bool toggling = player_character.has_trait( trait_DEBUG_HS ); auto sel = [toggling]( const vehicle_part & pt ) { if( toggling ) { return !pt.faults_potential().empty(); @@ -1274,7 +1285,7 @@ void veh_interact::do_mend() }; auto act = [&]( const vehicle_part & pt ) { - g->u.mend_item( veh->part_base( veh->index_of_part( &pt ) ) ); + player_character.mend_item( veh->part_base( veh->index_of_part( &pt ) ) ); sel_cmd = 'q'; }; @@ -1726,7 +1737,7 @@ vehicle_part *veh_interact::get_most_damaged_part() const vehicle_part *veh_interact::get_most_repariable_part() const { - auto &part = veh_utils::most_repairable_part( *veh, g->u ); + auto &part = veh_utils::most_repairable_part( *veh, get_player_character() ); return part ? &part : nullptr; } @@ -1775,12 +1786,13 @@ bool veh_interact::can_remove_part( int idx, const player &p ) quality_id qual; bool use_aid = false; bool use_str = false; + avatar &player_character = get_avatar(); if( sel_vpart_info->has_flag( "NEEDS_JACKING" ) ) { qual = qual_JACK; lvl = jack_quality( *veh ); str = veh->lift_strength(); use_aid = ( max_jack >= lifting_quality_to_mass( lvl ) ) || can_self_jack(); - use_str = g->u.can_lift( *veh ); + use_str = player_character.can_lift( *veh ); } else { item base( sel_vpart_info->item ); qual = qual_LIFT; @@ -1788,7 +1800,7 @@ bool veh_interact::can_remove_part( int idx, const player &p ) lifting_quality_to_mass( 1 ) ); str = base.lift_strength(); use_aid = max_lift >= base.weight(); - use_str = g->u.can_lift( base ); + use_str = player_character.can_lift( base ); } if( !( use_aid || use_str ) ) { @@ -1796,7 +1808,7 @@ bool veh_interact::can_remove_part( int idx, const player &p ) } nc_color aid_color = use_aid ? c_green : ( use_str ? c_dark_gray : c_red ); nc_color str_color = use_str ? c_green : ( use_aid ? c_dark_gray : c_red ); - const auto helpers = g->u.get_crafting_helpers(); + const auto helpers = player_character.get_crafting_helpers(); //~ %1$s is quality name, %2$d is quality level std::string aid_string = string_format( _( "1 tool with %1$s %2$d" ), qual.obj().name, lvl ); @@ -1821,7 +1833,7 @@ bool veh_interact::can_remove_part( int idx, const player &p ) sel_vehicle_part->info().format_description( nmsg, desc_color, getmaxx( w_msg ) - 4 ); msg = colorize( nmsg, c_light_gray ); - return ok || g->u.has_trait( trait_DEBUG_HS ); + return ok || player_character.has_trait( trait_DEBUG_HS ); } void veh_interact::do_remove() @@ -1836,9 +1848,10 @@ void veh_interact::do_remove() restore_on_out_of_scope> prev_title( title ); title = _( "Choose a part here to remove:" ); + avatar &player_character = get_avatar(); int pos = 0; for( size_t i = 0; i < parts_here.size(); i++ ) { - if( can_remove_part( parts_here[ i ], g->u ) ) { + if( can_remove_part( parts_here[ i ], player_character ) ) { pos = i; break; } @@ -1854,7 +1867,7 @@ void veh_interact::do_remove() while( true ) { int part = parts_here[ pos ]; - bool can_remove = can_remove_part( part, g->u ); + bool can_remove = can_remove_part( part, player_character ); overview_enable = [this, part]( const vehicle_part & pt ) { return &pt == &veh->part( part ); @@ -1896,7 +1909,7 @@ void veh_interact::do_remove() return; } } - const std::vector helpers = g->u.get_crafting_helpers(); + const std::vector helpers = player_character.get_crafting_helpers(); for( const npc *np : helpers ) { add_msg( m_info, _( "%s helps with this task…" ), np->name ); } @@ -2058,7 +2071,7 @@ int veh_interact::part_at( const point &d ) */ bool veh_interact::can_potentially_install( const vpart_info &vpart ) { - return g->u.has_trait( trait_DEBUG_HS ) || + return get_player_character().has_trait( trait_DEBUG_HS ) || vpart.install_requirements().can_make_with_inventory( crafting_inv, is_crafting_component ); } @@ -2082,8 +2095,9 @@ void veh_interact::move_cursor( const point &d, int dstart_at ) const point q = veh->coord_translate( vd ); const tripoint vehp = veh->global_pos3() + q; const bool has_critter = g->critter_at( vehp ); - bool obstruct = g->m.impassable_ter_furn( vehp ); - const optional_vpart_position ovp = g->m.veh_at( vehp ); + map &here = get_map(); + bool obstruct = here.impassable_ter_furn( vehp ); + const optional_vpart_position ovp = here.veh_at( vehp ); if( ovp && &ovp->vehicle() != veh ) { obstruct = true; } @@ -2253,8 +2267,9 @@ void veh_interact::display_veh() const point vd = -dd; const point q = veh->coord_translate( vd ); const tripoint vehp = veh->global_pos3() + q; - bool obstruct = g->m.impassable_ter_furn( vehp ); - const optional_vpart_position ovp = g->m.veh_at( vehp ); + map &here = get_map(); + bool obstruct = here.impassable_ter_furn( vehp ); + const optional_vpart_position ovp = here.veh_at( vehp ); if( ovp && &ovp->vehicle() != veh ) { obstruct = true; } @@ -2530,7 +2545,7 @@ void veh_interact::display_name() mvwprintz( w_name, point( 1, 0 ), c_light_gray, _( "Name: " ) ); mvwprintz( w_name, point( 1 + utf8_width( _( "Name: " ) ), 0 ), - !veh->is_owned_by( g->u, true ) ? c_light_red : c_light_green, + !veh->is_owned_by( get_player_character(), true ) ? c_light_red : c_light_green, string_format( _( "%s (%s)" ), veh->name, veh->get_owner_name() ) ); wnoutrefresh( w_name ); } @@ -2899,6 +2914,7 @@ void act_vehicle_unload_fuel( vehicle *veh ) fuel = fuels.front(); } + Character &player_character = get_player_character(); int qty = veh->fuel_left( fuel ); if( fuel == itype_plut_cell ) { if( qty / PLUTONIUM_CHARGES == 0 ) { @@ -2906,11 +2922,11 @@ void act_vehicle_unload_fuel( vehicle *veh ) return; } item plutonium( fuel, calendar::turn, qty / PLUTONIUM_CHARGES ); - g->u.i_add( plutonium ); + player_character.i_add( plutonium ); veh->drain( fuel, qty - ( qty % PLUTONIUM_CHARGES ) ); } else { item solid_fuel( fuel, calendar::turn, qty ); - g->u.i_add( solid_fuel ); + player_character.i_add( solid_fuel ); veh->drain( fuel, qty ); } @@ -2926,14 +2942,15 @@ void veh_interact::complete_vehicle( player &p ) debugmsg( "Invalid activity ACT_VEHICLE values:%d", p.activity.values.size() ); return; } - optional_vpart_position vp = g->m.veh_at( g->m.getlocal( tripoint( p.activity.values[0], + map &here = get_map(); + optional_vpart_position vp = here.veh_at( here.getlocal( tripoint( p.activity.values[0], p.activity.values[1], p.posz() ) ) ); if( !vp ) { // so the vehicle could have lost some of its parts from other NPCS works during this player/NPCs activity. // check the vehicle points that were stored at beginning of activity. if( !p.activity.coord_set.empty() ) { for( const auto pt : p.activity.coord_set ) { - vp = g->m.veh_at( g->m.getlocal( pt ) ); + vp = here.veh_at( here.getlocal( pt ) ); if( vp ) { break; } @@ -3039,7 +3056,7 @@ void veh_interact::complete_vehicle( player &p ) // TODO: allow boarding for non-players as well. player *const pl = g->critter_at( vehp ); if( vpinfo.has_flag( VPFLAG_BOARDABLE ) && pl ) { - g->m.board_vehicle( vehp, pl ); + here.board_vehicle( vehp, pl ); } p.add_msg_if_player( m_good, _( "You install a %1$s into the %2$s." ), veh->part( partnum ).name(), @@ -3048,7 +3065,7 @@ void veh_interact::complete_vehicle( player &p ) for( const auto &sk : vpinfo.install_skills ) { p.practice( sk.first, veh_utils::calc_xp_gain( vpinfo, sk.first, p ) ); } - g->m.update_vehicle_cache( veh, veh->sm_pos.z ); + here.update_vehicle_cache( veh, veh->sm_pos.z ); break; } @@ -3181,11 +3198,11 @@ void veh_interact::complete_vehicle( player &p ) if( veh->part_count() < 2 ) { p.add_msg_if_player( _( "You completely dismantle the %s." ), veh->name ); p.activity.set_to_null(); - g->m.destroy_vehicle( veh ); + here.destroy_vehicle( veh ); } else { veh->remove_part( vehicle_part ); veh->part_removal_cleanup(); - g->m.update_vehicle_cache( veh, veh->sm_pos.z ); + here.update_vehicle_cache( veh, veh->sm_pos.z ); } // This will be part of an NPC "job" where they need to clean up the acitivty items afterwards if( p.is_npc() ) { diff --git a/tests/map_helpers.cpp b/tests/map_helpers.cpp index 70c5741ab0952..2e5eae6a79cf6 100644 --- a/tests/map_helpers.cpp +++ b/tests/map_helpers.cpp @@ -6,7 +6,7 @@ #include #include -#include "avatar.h" +#include "character.h" #include "field.h" #include "game.h" #include "game_constants.h" @@ -22,25 +22,27 @@ class vehicle; // Remove all vehicles from the map void clear_vehicles() { - for( wrapped_vehicle &veh : g->m.get_vehicles() ) { - g->m.destroy_vehicle( veh.v ); + map &here = get_map(); + for( wrapped_vehicle &veh : here.get_vehicles() ) { + here.destroy_vehicle( veh.v ); } } void wipe_map_terrain() { - const int mapsize = g->m.getmapsize() * SEEX; + map &here = get_map(); + const int mapsize = here.getmapsize() * SEEX; for( int z = 0; z <= OVERMAP_HEIGHT; ++z ) { ter_id terrain = z == 0 ? t_grass : t_open_air; for( int x = 0; x < mapsize; ++x ) { for( int y = 0; y < mapsize; ++y ) { - g->m.set( { x, y, z}, terrain, f_null ); + here.set( { x, y, z}, terrain, f_null ); } } } clear_vehicles(); - g->m.invalidate_map_cache( 0 ); - g->m.build_map_cache( 0, true ); + here.invalidate_map_cache( 0 ); + here.build_map_cache( 0, true ); } void clear_creatures() @@ -61,16 +63,17 @@ void clear_npcs() void clear_fields( const int zlevel ) { - const int mapsize = g->m.getmapsize() * SEEX; + map &here = get_map(); + const int mapsize = here.getmapsize() * SEEX; for( int x = 0; x < mapsize; ++x ) { for( int y = 0; y < mapsize; ++y ) { const tripoint p( x, y, zlevel ); std::vector fields; - for( auto &pr : g->m.field_at( p ) ) { + for( auto &pr : here.field_at( p ) ) { fields.push_back( pr.second.get_field_type() ); } for( field_type_id f : fields ) { - g->m.remove_field( p, f ); + here.remove_field( p, f ); } } } @@ -78,10 +81,11 @@ void clear_fields( const int zlevel ) void clear_items( const int zlevel ) { - const int mapsize = g->m.getmapsize() * SEEX; + map &here = get_map(); + const int mapsize = here.getmapsize() * SEEX; for( int x = 0; x < mapsize; ++x ) { for( int y = 0; y < mapsize; ++y ) { - g->m.i_clear( { x, y, zlevel } ); + here.i_clear( { x, y, zlevel } ); } } } @@ -96,7 +100,7 @@ void clear_map() wipe_map_terrain(); clear_npcs(); clear_creatures(); - g->m.clear_traps(); + get_map().clear_traps(); for( int z = -2; z <= 0; ++z ) { clear_items( z ); } @@ -106,7 +110,7 @@ void clear_map_and_put_player_underground() { clear_map(); // Make sure the player doesn't block the path of the monster being tested. - g->u.setpos( { 0, 0, -2 } ); + get_player_character().setpos( { 0, 0, -2 } ); } monster &spawn_test_monster( const std::string &monster_type, const tripoint &start ) @@ -120,14 +124,15 @@ monster &spawn_test_monster( const std::string &monster_type, const tripoint &st // terrain, and no furniture, traps, or items. void build_test_map( const ter_id &terrain ) { - for( const tripoint &p : g->m.points_in_rectangle( tripoint_zero, + map &here = get_map(); + for( const tripoint &p : here.points_in_rectangle( tripoint_zero, tripoint( MAPSIZE * SEEX, MAPSIZE * SEEY, 0 ) ) ) { - g->m.furn_set( p, furn_id( "f_null" ) ); - g->m.ter_set( p, terrain ); - g->m.trap_set( p, trap_id( "tr_null" ) ); - g->m.i_clear( p ); + here.furn_set( p, furn_id( "f_null" ) ); + here.ter_set( p, terrain ); + here.trap_set( p, trap_id( "tr_null" ) ); + here.i_clear( p ); } - g->m.invalidate_map_cache( 0 ); - g->m.build_map_cache( 0, true ); + here.invalidate_map_cache( 0 ); + here.build_map_cache( 0, true ); } From 1263c7ce3326947e06b21d15df7f50ddc86881d0 Mon Sep 17 00:00:00 2001 From: Kevin Granade Date: Thu, 2 Jul 2020 02:55:01 +0000 Subject: [PATCH 036/420] map reference migration part elleven --- src/iuse.cpp | 524 ++++++++++++++++++++++++++++----------------------- 1 file changed, 285 insertions(+), 239 deletions(-) diff --git a/src/iuse.cpp b/src/iuse.cpp index b02f7f613781d..e45b76115da3a 100644 --- a/src/iuse.cpp +++ b/src/iuse.cpp @@ -480,7 +480,7 @@ int iuse::sewage( player *p, item *it, bool, const tripoint & ) int iuse::honeycomb( player *p, item *it, bool, const tripoint & ) { - g->m.spawn_item( p->pos(), itype_wax, 2 ); + get_map().spawn_item( p->pos(), itype_wax, 2 ); return it->type->charges_to_use(); } @@ -705,14 +705,15 @@ int iuse::fungicide( player *p, item *it, bool, const tripoint & ) } p->remove_effect( effect_spores ); int spore_count = rng( 1, 6 ); - for( const tripoint &dest : g->m.points_in_radius( p->pos(), 1 ) ) { + map &here = get_map(); + for( const tripoint &dest : here.points_in_radius( p->pos(), 1 ) ) { if( spore_count == 0 ) { break; } if( dest == p->pos() ) { continue; } - if( g->m.passable( dest ) && x_in_y( spore_count, 8 ) ) { + if( here.passable( dest ) && x_in_y( spore_count, 8 ) ) { if( monster *const mon_ptr = g->critter_at( dest ) ) { monster &critter = *mon_ptr; if( g->u.sees( dest ) && @@ -873,9 +874,10 @@ int iuse::meth( player *p, item *it, bool, const tripoint & ) } else { duration *= ( p->has_trait( trait_LIGHTWEIGHT ) ? 1.8 : 1.5 ); } + map &here = get_map(); // breathe out some smoke for( int i = 0; i < 3; i++ ) { - g->m.add_field( {p->posx() + static_cast( rng( -2, 2 ) ), p->posy() + static_cast( rng( -2, 2 ) ), p->posz()}, + here.add_field( {p->posx() + static_cast( rng( -2, 2 ) ), p->posy() + static_cast( rng( -2, 2 ) ), p->posz()}, fd_methsmoke, 2 ); } } else { @@ -1302,9 +1304,10 @@ int iuse::purify_smart( player *p, item *it, bool, const tripoint & ) static void spawn_spores( const player &p ) { int spores_spawned = 0; - fungal_effects fe( *g, g->m ); + map &here = get_map(); + fungal_effects fe( *g, here ); for( const tripoint &dest : closest_tripoints_first( p.pos(), 4 ) ) { - if( g->m.impassable( dest ) ) { + if( here.impassable( dest ) ) { continue; } float dist = rl_dist( dest, p.pos() ); @@ -1426,7 +1429,7 @@ static void marloss_common( player &p, item &it, const trait_id ¤t_color ) } p.set_mutation( trait_THRESH_MARLOSS ); - g->m.ter_set( p.pos(), t_marloss ); + get_map().ter_set( p.pos(), t_marloss ); g->events().send( p.getID() ); p.add_msg_if_player( m_good, _( "You wake up in a marloss bush. Almost *cradled* in it, actually, as though it grew there for you." ) ); @@ -1566,8 +1569,9 @@ int iuse::mycus( player *p, item *it, bool t, const tripoint &pos ) p->add_msg_if_player( m_good, _( "Even now, our fruits adapt to better serve local physiology." ) ); p->add_msg_if_player( m_good, _( "As, in time, shall we adapt to better welcome those who have not received us." ) );*/ - fungal_effects fe( *g, g->m ); - for( const tripoint &nearby_pos : g->m.points_in_radius( p->pos(), 3 ) ) { + map &here = get_map(); + fungal_effects fe( *g, here ); + for( const tripoint &nearby_pos : here.points_in_radius( p->pos(), 3 ) ) { fe.marlossify( nearby_pos ); } p->rem_addiction( add_type::MARLOSS_R ); @@ -1819,16 +1823,17 @@ int iuse::remove_all_mods( player *p, item *, bool, const tripoint & ) return 0; } -static bool good_fishing_spot( tripoint pos ) +static bool good_fishing_spot( tripoint pos, player *p ) { std::unordered_set fishable_locations = g->get_fishable_locations( 60, pos ); std::vector fishables = g->get_fishable_monsters( fishable_locations ); + map &here = get_map(); // isolated little body of water with no definite fish population - const oter_id &cur_omt = overmap_buffer.ter( ms_to_omt_copy( g->m.getabs( pos ) ) ); + const oter_id &cur_omt = overmap_buffer.ter( ms_to_omt_copy( here.getabs( pos ) ) ); std::string om_id = cur_omt.id().c_str(); - if( fishables.empty() && !g->m.has_flag( "CURRENT", pos ) && + if( fishables.empty() && !here.has_flag( "CURRENT", pos ) && om_id.find( "river_" ) == std::string::npos && !cur_omt->is_lake() && !cur_omt->is_lake_shore() ) { - g->u.add_msg_if_player( m_info, _( "You doubt you will have much luck catching fish here" ) ); + p->add_msg_if_player( m_info, _( "You doubt you will have much luck catching fish here" ) ); return false; } return true; @@ -1844,9 +1849,10 @@ int iuse::fishing_rod( player *p, item *it, bool, const tripoint & ) p->add_msg_if_player( m_info, _( "You cannot do that while mounted." ) ); return 0; } + map &here = get_map(); cata::optional found; - for( const tripoint &pnt : g->m.points_in_radius( p->pos(), 1 ) ) { - if( g->m.has_flag( flag_FISHABLE, pnt ) && good_fishing_spot( pnt ) ) { + for( const tripoint &pnt : here.points_in_radius( p->pos(), 1 ) ) { + if( here.has_flag( flag_FISHABLE, pnt ) && good_fishing_spot( pnt, p ) ) { found = pnt; break; } @@ -1864,6 +1870,7 @@ int iuse::fishing_rod( player *p, item *it, bool, const tripoint & ) int iuse::fish_trap( player *p, item *it, bool t, const tripoint &pos ) { + map &here = get_map(); if( !t ) { // Handle deploying fish trap. if( it->active ) { @@ -1895,16 +1902,16 @@ int iuse::fish_trap( player *p, item *it, bool t, const tripoint &pos ) } const tripoint pnt = *pnt_; - if( !g->m.has_flag( "FISHABLE", pnt ) ) { + if( !here.has_flag( "FISHABLE", pnt ) ) { p->add_msg_if_player( m_info, _( "You can't fish there!" ) ); return 0; } - if( !good_fishing_spot( pnt ) ) { + if( !good_fishing_spot( pnt, p ) ) { return 0; } it->active = true; it->set_age( 0_turns ); - g->m.add_item_or_charges( pnt, *it ); + here.add_item_or_charges( pnt, *it ); p->i_rem( it ); p->add_msg_if_player( m_info, _( "You place the fish trap, in three hours or so you may catch some fish." ) ); @@ -1920,7 +1927,7 @@ int iuse::fish_trap( player *p, item *it, bool t, const tripoint &pos ) if( it->age() > 3_hours ) { it->active = false; - if( !g->m.has_flag( "FISHABLE", pos ) ) { + if( !here.has_flag( "FISHABLE", pos ) ) { return 0; } @@ -1968,7 +1975,7 @@ int iuse::fish_trap( player *p, item *it, bool t, const tripoint &pos ) if( chosen_fish->fish_population <= 0 ) { g->catch_a_monster( chosen_fish, pos, p, 300_hours ); //catch the fish! } else { - g->m.add_item_or_charges( pos, item::make_corpse( chosen_fish->type->id, + here.add_item_or_charges( pos, item::make_corpse( chosen_fish->type->id, calendar::turn + rng( 0_turns, 3_hours ) ) ); } @@ -1985,7 +1992,7 @@ int iuse::fish_trap( player *p, item *it, bool t, const tripoint &pos ) //but it's not as comfortable as if you just put fishes in the same tile with the trap. //Also: corpses and comestibles do not rot in containers like this, but on the ground they will rot. //we don't know when it was caught so use a random turn - g->m.add_item_or_charges( pos, item::make_corpse( fish_mon, it->birthday() + rng( 0_turns, + here.add_item_or_charges( pos, item::make_corpse( fish_mon, it->birthday() + rng( 0_turns, 3_hours ) ) ); break; //this can happen only once } @@ -2011,8 +2018,9 @@ int iuse::extinguisher( player *p, item *it, bool, const tripoint & ) p->moves -= to_moves( 2_seconds ); + map &here = get_map(); // Reduce the strength of fire (if any) in the target tile. - g->m.add_field( dest, fd_extinguisher, 3, 10_turns ); + here.add_field( dest, fd_extinguisher, 3, 10_turns ); // Also spray monsters in that tile. if( monster *const mon_ptr = g->critter_at( dest, true ) ) { @@ -2039,11 +2047,11 @@ int iuse::extinguisher( player *p, item *it, bool, const tripoint & ) } // Slightly reduce the strength of fire immediately behind the target tile. - if( g->m.passable( dest ) ) { + if( here.passable( dest ) ) { dest.x += ( dest.x - p->posx() ); dest.y += ( dest.y - p->posy() ); - g->m.mod_field_intensity( dest, fd_fire, std::min( 0 - rng( 0, 1 ) + rng( 0, 1 ), 0 ) ); + here.mod_field_intensity( dest, fd_fire, std::min( 0 - rng( 0, 1 ) + rng( 0, 1 ), 0 ) ); } return it->type->charges_to_use(); @@ -2401,11 +2409,12 @@ int iuse::hammer( player *p, item *it, bool, const tripoint & ) t_rdoor_boarded_damaged }; - const std::function f = [&allowed_ter_id]( const tripoint & pnt ) { + map &here = get_map(); + const std::function f = [&allowed_ter_id, &here]( const tripoint & pnt ) { if( pnt == g->u.pos() ) { return false; } - const ter_id ter = g->m.ter( pnt ); + const ter_id ter = here.ter( pnt ); const bool is_allowed = allowed_ter_id.find( ter ) != allowed_ter_id.end(); return is_allowed; @@ -2417,7 +2426,7 @@ int iuse::hammer( player *p, item *it, bool, const tripoint & ) return 0; } const tripoint &pnt = *pnt_; - const ter_id type = g->m.ter( pnt ); + const ter_id type = here.ter( pnt ); if( !f( pnt ) ) { if( pnt == p->pos() ) { p->add_msg_if_player( _( "You try to hit yourself with the hammer." ) ); @@ -2465,13 +2474,14 @@ int iuse::crowbar( player *p, item *it, bool, const tripoint &pos ) f_coffin_c }; + map &here = get_map(); const std::function f = [&allowed_ter_id, - &allowed_furn_id]( const tripoint & pnt ) { + &allowed_furn_id, &here]( const tripoint & pnt ) { if( pnt == g->u.pos() ) { return false; } - const ter_id ter = g->m.ter( pnt ); - const auto furn = g->m.furn( pnt ); + const ter_id ter = here.ter( pnt ); + const auto furn = here.furn( pnt ); const bool is_allowed = allowed_ter_id.find( ter ) != allowed_ter_id.end() || allowed_furn_id.find( furn ) != allowed_furn_id.end(); @@ -2484,8 +2494,8 @@ int iuse::crowbar( player *p, item *it, bool, const tripoint &pos ) return 0; } const tripoint &pnt = *pnt_; - const ter_id type = g->m.ter( pnt ); - const furn_id furn = g->m.furn( pnt ); + const ter_id type = here.ter( pnt ); + const furn_id furn = here.furn( pnt ); if( !f( pnt ) ) { if( pnt == p->pos() ) { p->add_msg_if_player( m_info, _( "You attempt to pry open your wallet " @@ -2518,12 +2528,12 @@ int iuse::crowbar( player *p, item *it, bool, const tripoint &pos ) difficulty = 6; } else if( type == t_door_c ) { p->add_msg_if_player( m_info, _( "You notice the door is unlocked, so you simply open it." ) ); - g->m.ter_set( pnt, t_door_o ); + here.ter_set( pnt, t_door_o ); p->mod_moves( -100 ); return 0; } else if( type == t_door_c_peep ) { p->add_msg_if_player( m_info, _( "You notice the door is unlocked, so you simply open it." ) ); - g->m.ter_set( pnt, t_door_o_peep ); + here.ter_set( pnt, t_door_o_peep ); p->mod_moves( -100 ); return 0; } else if( type == t_manhole_cover ) { @@ -2580,19 +2590,19 @@ int iuse::crowbar( player *p, item *it, bool, const tripoint &pos ) if( dice( 4, difficulty ) < dice( 4, p->str_cur ) ) { p->add_msg_if_player( m_good, succ_action ); - if( g->m.furn( pnt ) == f_crate_c ) { - g->m.furn_set( pnt, f_crate_o ); - } else if( g->m.furn( pnt ) == f_coffin_c ) { - g->m.furn_set( pnt, f_coffin_o ); + if( here.furn( pnt ) == f_crate_c ) { + here.furn_set( pnt, f_crate_o ); + } else if( here.furn( pnt ) == f_coffin_c ) { + here.furn_set( pnt, f_coffin_o ); } else { - g->m.ter_set( pnt, new_type ); + here.ter_set( pnt, new_type ); } if( noisy ) { sounds::sound( pnt, 12, sounds::sound_t::combat, _( "crunch!" ), true, "tool", "crowbar" ); } if( type == t_manhole_cover ) { - g->m.spawn_item( pnt, itype_manhole_cover ); + here.spawn_item( pnt, itype_manhole_cover ); } if( type == t_door_locked_alarm ) { g->events().send( p->getID() ); @@ -2613,10 +2623,10 @@ int iuse::crowbar( player *p, item *it, bool, const tripoint &pos ) p->str_cur ) ) { p->add_msg_if_player( m_mixed, _( "You break the glass." ) ); sounds::sound( pnt, 24, sounds::sound_t::combat, _( "glass breaking!" ), true, "smash", "glass" ); - g->m.ter_set( pnt, t_window_frame ); - g->m.spawn_item( pnt, itype_sheet, 2 ); - g->m.spawn_item( pnt, itype_stick ); - g->m.spawn_item( pnt, itype_string_36 ); + here.ter_set( pnt, t_window_frame ); + here.spawn_item( pnt, itype_sheet, 2 ); + here.spawn_item( pnt, itype_stick ); + here.spawn_item( pnt, itype_string_36 ); return it->type->charges_to_use(); } } @@ -2646,10 +2656,11 @@ int iuse::makemound( player *p, item *it, bool t, const tripoint & ) return 0; } - if( g->m.has_flag( flag_PLOWABLE, pnt ) && !g->m.has_flag( flag_PLANT, pnt ) ) { + map &here = get_map(); + if( here.has_flag( flag_PLOWABLE, pnt ) && !here.has_flag( flag_PLANT, pnt ) ) { p->add_msg_if_player( _( "You start churning up the earth here." ) ); p->assign_activity( ACT_CHURN, 18000, -1, p->get_item_position( it ) ); - p->activity.placement = g->m.getabs( pnt ); + p->activity.placement = here.getabs( pnt ); return it->type->charges_to_use(); } else { p->add_msg_if_player( _( "You can't churn up this ground." ) ); @@ -2775,7 +2786,7 @@ static digging_moves_and_byproducts dig_pit_moves_and_byproducts( player *p, ite // Modify the number of moves based on the help. // TODO: this block of code is all over the place and could probably be consolidated. - const int helpersize = g->u.get_num_crafting_helpers( 3 ); + const int helpersize = p->get_num_crafting_helpers( 3 ); moves = moves * ( 1 - ( helpersize / 10 ) ); ter_id result_terrain; @@ -2799,19 +2810,20 @@ int iuse::dig( player *p, item *it, bool t, const tripoint & ) } const tripoint dig_point = p->pos(); - const bool can_dig_here = g->m.has_flag( "DIGGABLE", dig_point ) && - !g->m.has_furn( dig_point ) && - g->m.tr_at( dig_point ).is_null() && - ( g->m.ter( dig_point ) == t_grave_new || g->m.i_at( dig_point ).empty() ) && - !g->m.veh_at( dig_point ); + map &here = get_map(); + const bool can_dig_here = here.has_flag( "DIGGABLE", dig_point ) && + !here.has_furn( dig_point ) && + here.tr_at( dig_point ).is_null() && + ( here.ter( dig_point ) == t_grave_new || here.i_at( dig_point ).empty() ) && + !here.veh_at( dig_point ); if( !can_dig_here ) { p->add_msg_if_player( _( "You can't dig a pit in this location. Ensure it is clear diggable ground with no items or obstacles." ) ); return 0; } - const bool can_deepen = g->m.has_flag( "DIGGABLE_CAN_DEEPEN", dig_point ); - const bool grave = g->m.ter( dig_point ) == t_grave; + const bool can_deepen = here.has_flag( "DIGGABLE_CAN_DEEPEN", dig_point ); + const bool grave = here.ter( dig_point ) == t_grave; if( !p->crafting_inventory().has_quality( qual_DIG, 2 ) ) { if( can_deepen ) { @@ -2823,8 +2835,8 @@ int iuse::dig( player *p, item *it, bool t, const tripoint & ) } } - const std::function f = []( const tripoint & pnt ) { - return g->m.passable( pnt ); + const std::function f = [&here]( const tripoint & pnt ) { + return here.passable( pnt ); }; const cata::optional pnt_ = choose_adjacent_highlight( @@ -2900,11 +2912,12 @@ int iuse::dig_channel( player *p, item *it, bool t, const tripoint & ) tripoint west = dig_point + point_west; tripoint east = dig_point + point_east; - const bool can_dig_here = g->m.has_flag( flag_DIGGABLE, dig_point ) && - !g->m.has_furn( dig_point ) && - g->m.tr_at( dig_point ).is_null() && g->m.i_at( dig_point ).empty() && !g->m.veh_at( dig_point ) && - ( g->m.has_flag( flag_CURRENT, north ) || g->m.has_flag( flag_CURRENT, south ) || - g->m.has_flag( flag_CURRENT, east ) || g->m.has_flag( flag_CURRENT, west ) ); + map &here = get_map(); + const bool can_dig_here = here.has_flag( flag_DIGGABLE, dig_point ) && + !here.has_furn( dig_point ) && + here.tr_at( dig_point ).is_null() && here.i_at( dig_point ).empty() && !here.veh_at( dig_point ) && + ( here.has_flag( flag_CURRENT, north ) || here.has_flag( flag_CURRENT, south ) || + here.has_flag( flag_CURRENT, east ) || here.has_flag( flag_CURRENT, west ) ); if( !can_dig_here ) { p->add_msg_if_player( @@ -2912,8 +2925,8 @@ int iuse::dig_channel( player *p, item *it, bool t, const tripoint & ) return 0; } - const std::function f = []( const tripoint & pnt ) { - return g->m.passable( pnt ); + const std::function f = [&here]( const tripoint & pnt ) { + return here.passable( pnt ); }; const cata::optional pnt_ = choose_adjacent_highlight( @@ -2930,7 +2943,7 @@ int iuse::dig_channel( player *p, item *it, bool t, const tripoint & ) return 0; } - const std::vector helpers = g->u.get_crafting_helpers(); + const std::vector helpers = p->get_crafting_helpers(); for( const npc *np : helpers ) { add_msg( m_info, _( "%s helps with this task…" ), np->name ); break; @@ -2968,11 +2981,12 @@ int iuse::fill_pit( player *p, item *it, bool t, const tripoint & ) t_dirtmound }; - const std::function f = [&allowed_ter_id]( const tripoint & pnt ) { + map &here = get_map(); + const std::function f = [&allowed_ter_id, &here]( const tripoint & pnt ) { if( pnt == g->u.pos() ) { return false; } - const ter_id type = g->m.ter( pnt ); + const ter_id type = here.ter( pnt ); return ( allowed_ter_id.find( type ) != allowed_ter_id.end() ); }; @@ -2982,7 +2996,7 @@ int iuse::fill_pit( player *p, item *it, bool t, const tripoint & ) return 0; } const tripoint &pnt = *pnt_; - const ter_id ter = g->m.ter( pnt ); + const ter_id ter = here.ter( pnt ); if( !f( pnt ) ) { if( pnt == p->pos() ) { p->add_msg_if_player( m_info, _( "You decide not to bury yourself that early." ) ); @@ -3004,7 +3018,7 @@ int iuse::fill_pit( player *p, item *it, bool t, const tripoint & ) return 0; } const std::vector helpers = g->u.get_crafting_helpers(); - const int helpersize = g->u.get_num_crafting_helpers( 3 ); + const int helpersize = p->get_num_crafting_helpers( 3 ); moves = moves * ( 1 - ( helpersize / 10 ) ); for( const npc *np : helpers ) { add_msg( m_info, _( "%s helps with this task…" ), np->name ); @@ -3030,7 +3044,7 @@ int iuse::clear_rubble( player *p, item *it, bool, const tripoint & ) return 0; } const std::function f = []( const tripoint & pnt ) { - return g->m.has_flag( "RUBBLE", pnt ); + return get_map().has_flag( "RUBBLE", pnt ); }; const cata::optional pnt_ = choose_adjacent_highlight( @@ -3050,7 +3064,7 @@ int iuse::clear_rubble( player *p, item *it, bool, const tripoint & ) add_msg( m_info, _( "%s helps with this task…" ), np->name ); break; } - const int helpersize = g->u.get_num_crafting_helpers( 3 ); + const int helpersize = p->get_num_crafting_helpers( 3 ); const int moves = to_moves( 30_seconds ) * ( 1 - ( helpersize / 10 ) ); player_activity act( ACT_CLEAR_RUBBLE, moves / bonus, bonus ); p->assign_activity( act ); @@ -3066,15 +3080,16 @@ int iuse::siphon( player *p, item *it, bool, const tripoint & ) p->add_msg_if_player( m_info, _( "You cannot do that while mounted." ) ); return 0; } - const std::function f = []( const tripoint & pnt ) { - const optional_vpart_position vp = g->m.veh_at( pnt ); + map &here = get_map(); + const std::function f = [&here]( const tripoint & pnt ) { + const optional_vpart_position vp = here.veh_at( pnt ); return !!vp; }; vehicle *v = nullptr; bool found_more_than_one = false; - for( const tripoint &pos : g->m.points_in_radius( g->u.pos(), 1 ) ) { - const optional_vpart_position vp = g->m.veh_at( pos ); + for( const tripoint &pos : here.points_in_radius( p->pos(), 1 ) ) { + const optional_vpart_position vp = here.veh_at( pos ); if( !vp ) { continue; } @@ -3096,7 +3111,7 @@ int iuse::siphon( player *p, item *it, bool, const tripoint & ) if( !pnt_ ) { return 0; } - const optional_vpart_position vp = g->m.veh_at( *pnt_ ); + const optional_vpart_position vp = here.veh_at( *pnt_ ); if( vp ) { v = &vp->vehicle(); } @@ -3337,23 +3352,24 @@ int iuse::jackhammer( player *p, item *it, bool, const tripoint &pos ) pnt = *pnt_; } - if( !g->m.has_flag( "MINEABLE", pnt ) ) { + map &here = get_map(); + if( !here.has_flag( "MINEABLE", pnt ) ) { p->add_msg_if_player( m_info, _( "You can't drill there." ) ); return 0; } - if( g->m.veh_at( pnt ) ) { + if( here.veh_at( pnt ) ) { p->add_msg_if_player( _( "There's a vehicle in the way!" ) ); return 0; } int moves = to_moves( 30_minutes ); - if( g->m.move_cost( pnt ) == 2 ) { + if( here.move_cost( pnt ) == 2 ) { // We're breaking up some flat surface like pavement, which is much easier moves /= 2; } const std::vector helpers = g->u.get_crafting_helpers(); - const int helpersize = g->u.get_num_crafting_helpers( 3 ); + const int helpersize = p->get_num_crafting_helpers( 3 ); moves *= ( 1 - ( helpersize / 10 ) ); for( const npc *np : helpers ) { add_msg( m_info, _( "%s helps with this task…" ), np->name ); @@ -3361,9 +3377,9 @@ int iuse::jackhammer( player *p, item *it, bool, const tripoint &pos ) } p->assign_activity( ACT_JACKHAMMER, moves, -1, p->get_item_position( it ) ); - p->activity.placement = g->m.getabs( pnt ); + p->activity.placement = here.getabs( pnt ); p->add_msg_if_player( _( "You start drilling into the %1$s with your %2$s." ), - g->m.tername( pnt ), it->tname() ); + here.tername( pnt ), it->tname() ); return it->type->charges_to_use(); } @@ -3405,7 +3421,7 @@ int iuse::pick_lock( player *p, item *it, bool, const tripoint &pos ) } you.assign_activity( lockpick_activity_actor( duration, item_location( you, it ), cata::nullopt, - g->m.getabs( *target ) ) ); + get_map().getabs( *target ) ) ); return it->type->charges_to_use(); } @@ -3433,24 +3449,25 @@ int iuse::pickaxe( player *p, item *it, bool, const tripoint &pos ) pnt = *pnt_; } - if( !g->m.has_flag( "MINEABLE", pnt ) ) { + map &here = get_map(); + if( !here.has_flag( "MINEABLE", pnt ) ) { p->add_msg_if_player( m_info, _( "You can't mine there." ) ); return 0; } - if( g->m.veh_at( pnt ) ) { + if( here.veh_at( pnt ) ) { p->add_msg_if_player( _( "There's a vehicle in the way!" ) ); return 0; } int moves = to_moves( 20_minutes ); moves += ( ( MAX_STAT + 4 ) - std::min( p->str_cur, MAX_STAT ) ) * to_moves( 5_minutes ); - if( g->m.move_cost( pnt ) == 2 ) { + if( here.move_cost( pnt ) == 2 ) { // We're breaking up some flat surface like pavement, which is much easier moves /= 2; } const std::vector helpers = g->u.get_crafting_helpers(); - const int helpersize = g->u.get_num_crafting_helpers( 3 ); + const int helpersize = p->get_num_crafting_helpers( 3 ); moves *= ( 1 - ( helpersize / 10 ) ); for( const npc *np : helpers ) { add_msg( m_info, _( "%s helps with this task…" ), np->name ); @@ -3459,9 +3476,9 @@ int iuse::pickaxe( player *p, item *it, bool, const tripoint &pos ) p->assign_activity( ACT_PICKAXE, moves, -1 ); p->activity.targets.push_back( item_location( *p, it ) ); - p->activity.placement = g->m.getabs( pnt ); + p->activity.placement = here.getabs( pnt ); p->add_msg_if_player( _( "You strike the %1$s with your %2$s." ), - g->m.tername( pnt ), it->tname() ); + here.tername( pnt ), it->tname() ); return 0; // handled when the activity finishes } @@ -3489,32 +3506,34 @@ int iuse::burrow( player *p, item *it, bool, const tripoint &pos ) pnt = *pnt_; } - if( !g->m.has_flag( "MINEABLE", pnt ) ) { + map &here = get_map(); + if( !here.has_flag( "MINEABLE", pnt ) ) { p->add_msg_if_player( m_info, _( "You can't burrow there." ) ); return 0; } - if( g->m.veh_at( pnt ) ) { + if( here.veh_at( pnt ) ) { p->add_msg_if_player( _( "There's a vehicle in the way!" ) ); return 0; } int moves = to_moves( 5_minutes ); moves += ( ( MAX_STAT + 3 ) - std::min( p->str_cur, MAX_STAT ) ) * to_moves( 2_minutes ); - if( g->m.move_cost( pnt ) == 2 ) { + if( here.move_cost( pnt ) == 2 ) { // We're breaking up some flat surface like pavement, which is much easier moves /= 2; } p->assign_activity( ACT_BURROW, moves, -1, 0 ); p->activity.placement = pnt; p->add_msg_if_player( _( "You start tearing into the %1$s with your %2$s." ), - g->m.tername( pnt ), it->tname() ); + here.tername( pnt ), it->tname() ); return 0; // handled when the activity finishes } int iuse::geiger( player *p, item *it, bool t, const tripoint &pos ) { + map &here = get_map(); if( t ) { // Every-turn use when it's on - const int rads = g->m.get_radiation( pos ); + const int rads = here.get_radiation( pos ); if( rads == 0 ) { return it->type->charges_to_use(); } @@ -3582,7 +3601,7 @@ int iuse::geiger( player *p, item *it, bool t, const tripoint &pos ) } case 1: p->add_msg_if_player( m_info, _( "The ground's radiation level: %d mSv/h" ), - g->m.get_radiation( p->pos() ) ); + here.get_radiation( p->pos() ) ); break; case 2: p->add_msg_if_player( _( "The geiger counter's scan LED turns on." ) ); @@ -3620,11 +3639,12 @@ int iuse::can_goo( player *p, item *it, bool, const tripoint & ) int tries = 0; tripoint goop; goop.z = p->posz(); + map &here = get_map(); do { goop.x = p->posx() + rng( -2, 2 ); goop.y = p->posy() + rng( -2, 2 ); tries++; - } while( g->m.impassable( goop ) && tries < 10 ); + } while( here.impassable( goop ) && tries < 10 ); if( tries == 10 ) { return 0; } @@ -3653,13 +3673,13 @@ int iuse::can_goo( player *p, item *it, bool, const tripoint & ) goop.x = p->posx() + rng( -2, 2 ); goop.y = p->posy() + rng( -2, 2 ); tries++; - found = g->m.passable( goop ) && g->m.tr_at( goop ).is_null(); + found = here.passable( goop ) && here.tr_at( goop ).is_null(); } while( !found && tries < 10 ); if( found ) { if( g->u.sees( goop ) ) { add_msg( m_warning, _( "A nearby splatter of goo forms into a goo pit." ) ); } - g->m.trap_set( goop, tr_goo ); + here.trap_set( goop, tr_goo ); } else { return 0; } @@ -3681,6 +3701,7 @@ int iuse::granade_act( player *p, item *it, bool t, const tripoint &pos ) if( pos.x == -999 || pos.y == -999 ) { return 0; } + map &here = get_map(); if( t ) { // Simple timer effects // Vol 0 = only heard if you hold it sounds::sound( pos, 0, sounds::sound_t::electronic_speech, _( "Merged!" ), @@ -3701,7 +3722,7 @@ int iuse::granade_act( player *p, item *it, bool t, const tripoint &pos ) sounds::sound( pos, 100, sounds::sound_t::electronic_speech, _( "BUGFIXES!" ), true, "speech", it->typeId().str() ); explosion_handler::draw_explosion( pos, explosion_radius, c_light_cyan ); - for( const tripoint &dest : g->m.points_in_radius( pos, explosion_radius ) ) { + for( const tripoint &dest : here.points_in_radius( pos, explosion_radius ) ) { monster *const mon = g->critter_at( dest, true ); if( mon && ( mon->type->in_species( species_INSECT ) || mon->is_hallucination() ) ) { mon->die_in_explosion( nullptr ); @@ -3713,7 +3734,7 @@ int iuse::granade_act( player *p, item *it, bool t, const tripoint &pos ) sounds::sound( pos, 100, sounds::sound_t::electronic_speech, _( "BUFFS!" ), true, "speech", it->typeId().str() ); explosion_handler::draw_explosion( pos, explosion_radius, c_green ); - for( const tripoint &dest : g->m.points_in_radius( pos, explosion_radius ) ) { + for( const tripoint &dest : here.points_in_radius( pos, explosion_radius ) ) { if( monster *const mon_ptr = g->critter_at( dest ) ) { monster &critter = *mon_ptr; critter.set_speed_base( @@ -3753,7 +3774,7 @@ int iuse::granade_act( player *p, item *it, bool t, const tripoint &pos ) sounds::sound( pos, 100, sounds::sound_t::electronic_speech, _( "NERFS!" ), true, "speech", it->typeId().str() ); explosion_handler::draw_explosion( pos, explosion_radius, c_red ); - for( const tripoint &dest : g->m.points_in_radius( pos, explosion_radius ) ) { + for( const tripoint &dest : here.points_in_radius( pos, explosion_radius ) ) { if( monster *const mon_ptr = g->critter_at( dest ) ) { monster &critter = *mon_ptr; critter.set_speed_base( @@ -3792,7 +3813,7 @@ int iuse::granade_act( player *p, item *it, bool t, const tripoint &pos ) sounds::sound( pos, 100, sounds::sound_t::electronic_speech, _( "REVERTS!" ), true, "speech", it->typeId().str() ); explosion_handler::draw_explosion( pos, explosion_radius, c_pink ); - for( const tripoint &dest : g->m.points_in_radius( pos, explosion_radius ) ) { + for( const tripoint &dest : here.points_in_radius( pos, explosion_radius ) ) { if( monster *const mon_ptr = g->critter_at( dest ) ) { monster &critter = *mon_ptr; critter.set_speed_base( critter.type->speed ); @@ -3810,9 +3831,9 @@ int iuse::granade_act( player *p, item *it, bool t, const tripoint &pos ) sounds::sound( pos, 100, sounds::sound_t::electronic_speech, _( "BEES!" ), true, "speech", it->typeId().str() ); explosion_handler::draw_explosion( pos, explosion_radius, c_yellow ); - for( const tripoint &dest : g->m.points_in_radius( pos, explosion_radius ) ) { + for( const tripoint &dest : here.points_in_radius( pos, explosion_radius ) ) { if( one_in( 5 ) && !g->critter_at( dest ) ) { - g->m.add_field( dest, fd_bees, rng( 1, 3 ) ); + here.add_field( dest, fd_bees, rng( 1, 3 ) ); } } break; @@ -3840,8 +3861,9 @@ int iuse::acidbomb_act( player *p, item *it, bool, const tripoint &pos ) { if( !p->has_item( *it ) ) { it->charges = -1; - for( const tripoint &tmp : g->m.points_in_radius( pos.x == -999 ? p->pos() : pos, 1 ) ) { - g->m.add_field( tmp, fd_acid, 3 ); + map &here = get_map(); + for( const tripoint &tmp : here.points_in_radius( pos.x == -999 ? p->pos() : pos, 1 ) ) { + here.add_field( tmp, fd_acid, 3 ); } return 1; } @@ -3861,17 +3883,18 @@ int iuse::grenade_inc_act( player *p, item *it, bool t, const tripoint &pos ) p->add_msg_if_player( m_info, _( "You've already released the handle, try throwing it instead." ) ); return 0; } else { // blow up + map &here = get_map(); int num_flames = rng( 3, 5 ); for( int current_flame = 0; current_flame < num_flames; current_flame++ ) { tripoint dest( pos + point( rng( -5, 5 ), rng( -5, 5 ) ) ); std::vector flames = line_to( pos, dest, 0, 0 ); for( auto &flame : flames ) { - g->m.add_field( flame, fd_fire, rng( 0, 2 ) ); + here.add_field( flame, fd_fire, rng( 0, 2 ) ); } } explosion_handler::explosion( pos, 8, 0.8, true ); - for( const tripoint &dest : g->m.points_in_radius( pos, 2 ) ) { - g->m.add_field( dest, fd_incendiary, 3 ); + for( const tripoint &dest : here.points_in_radius( pos, 2 ) ) { + here.add_field( dest, fd_incendiary, 3 ); } } @@ -3905,9 +3928,10 @@ int iuse::molotov_lit( player *p, item *it, bool t, const tripoint &pos ) if( pos.x == -999 || pos.y == -999 ) { return 0; } else if( !t ) { - for( const tripoint &pt : g->m.points_in_radius( pos, 1, 0 ) ) { + map &here = get_map(); + for( const tripoint &pt : here.points_in_radius( pos, 1, 0 ) ) { const int intensity = 1 + one_in( 3 ) + one_in( 5 ); - g->m.add_field( pt, fd_fire, intensity ); + here.add_field( pt, fd_fire, intensity ); } return 1; } else if( it->charges > 0 ) { @@ -4037,7 +4061,7 @@ int iuse::pheromone( player *p, item *it, bool, const tripoint &pos ) p->moves -= 15; int converts = 0; - for( const tripoint &dest : g->m.points_in_radius( pos, 4 ) ) { + for( const tripoint &dest : get_map().points_in_radius( pos, 4 ) ) { monster *const mon_ptr = g->critter_at( dest, true ); if( !mon_ptr ) { continue; @@ -4072,7 +4096,7 @@ int iuse::portal( player *p, item *it, bool, const tripoint & ) return 0; } tripoint t( p->posx() + rng( -2, 2 ), p->posy() + rng( -2, 2 ), p->posz() ); - g->m.trap_set( t, tr_portal ); + get_map().trap_set( t, tr_portal ); return it->type->charges_to_use(); } @@ -4442,7 +4466,7 @@ int iuse::gasmask( player *p, item *it, bool t, const tripoint &pos ) if( t ) { // Normal use if( p->is_worn( *it ) ) { // calculate amount of absorbed gas per filter charge - const field &gasfield = g->m.field_at( pos ); + const field &gasfield = get_map().field_at( pos ); for( auto &dfield : gasfield ) { const field_entry &entry = dfield.second; if( entry.get_gas_absorption_factor() > 0 ) { @@ -4744,9 +4768,10 @@ int iuse::dog_whistle( player *p, item *it, bool, const tripoint & ) int iuse::call_of_tindalos( player *p, item *it, bool, const tripoint & ) { - for( const tripoint &dest : g->m.points_in_radius( p->pos(), 12 ) ) { - if( g->m.is_cornerfloor( dest ) ) { - g->m.add_field( dest, fd_tindalos_rift, 3 ); + map &here = get_map(); + for( const tripoint &dest : here.points_in_radius( p->pos(), 12 ) ) { + if( here.is_cornerfloor( dest ) ) { + here.add_field( dest, fd_tindalos_rift, 3 ); add_msg( m_info, _( "You hear a low-pitched echoing howl." ) ); } } @@ -4770,7 +4795,7 @@ int iuse::blood_draw( player *p, item *it, bool, const tripoint & ) item blood( "blood", calendar::turn ); bool drew_blood = false; bool acid_blood = false; - for( item &map_it : g->m.i_at( point( p->posx(), p->posy() ) ) ) { + for( item &map_it : get_map().i_at( point( p->posx(), p->posy() ) ) ) { if( map_it.is_corpse() && query_yn( _( "Draw blood from %s?" ), colorize( map_it.tname(), map_it.color_in_inventory() ) ) ) { @@ -4826,7 +4851,7 @@ int iuse::mind_splicer( player *p, item *it, bool, const tripoint & ) p->add_msg_if_player( m_info, _( "You cannot do that while mounted." ) ); return 0; } - for( item &map_it : g->m.i_at( point( p->posx(), p->posy() ) ) ) { + for( item &map_it : get_map().i_at( point( p->posx(), p->posy() ) ) ) { if( map_it.typeId() == itype_rmi2_corpse && query_yn( _( "Use the mind splicer kit on the %s?" ), colorize( map_it.tname(), map_it.color_in_inventory() ) ) ) { @@ -4869,7 +4894,7 @@ void iuse::cut_log_into_planks( player &p ) p.add_msg_if_player( _( "You cut the log into planks." ) ); p.assign_activity( ACT_CHOP_PLANKS, moves, -1 ); - p.activity.placement = g->m.getabs( p.pos() ); + p.activity.placement = get_map().getabs( p.pos() ); } int iuse::lumber( player *p, item *it, bool t, const tripoint & ) @@ -4881,10 +4906,11 @@ int iuse::lumber( player *p, item *it, bool t, const tripoint & ) p->add_msg_if_player( m_info, _( "You cannot do that while mounted." ) ); return 0; } + map &here = get_map(); // Check if player is standing on any lumber - for( item &i : g->m.i_at( p->pos() ) ) { + for( item &i : here.i_at( p->pos() ) ) { if( i.typeId() == itype_log ) { - g->m.i_rem( p->pos(), &i ); + here.i_rem( p->pos(), &i ); cut_log_into_planks( *p ); return it->type->charges_to_use(); } @@ -4918,7 +4944,7 @@ static int chop_moves( player *p, item *it ) const int attr = it->has_flag( "POWERED" ) ? p->dex_cur : p->str_cur; int moves = to_moves( time_duration::from_minutes( 60 - attr ) / std::pow( 2, quality - 1 ) ); - const int helpersize = g->u.get_num_crafting_helpers( 3 ); + const int helpersize = p->get_num_crafting_helpers( 3 ); moves = moves * ( 1 - ( helpersize / 10 ) ); return moves; } @@ -4932,11 +4958,12 @@ int iuse::chop_tree( player *p, item *it, bool t, const tripoint & ) p->add_msg_if_player( m_info, _( "You cannot do that while mounted." ) ); return 0; } - const std::function f = []( const tripoint & pnt ) { + map &here = get_map(); + const std::function f = [&here]( const tripoint & pnt ) { if( pnt == g->u.pos() ) { return false; } - return g->m.has_flag( "TREE", pnt ); + return here.has_flag( "TREE", pnt ); }; const cata::optional pnt_ = choose_adjacent_highlight( @@ -4960,7 +4987,7 @@ int iuse::chop_tree( player *p, item *it, bool t, const tripoint & ) break; } p->assign_activity( ACT_CHOP_TREE, moves, -1, p->get_item_position( it ) ); - p->activity.placement = g->m.getabs( pnt ); + p->activity.placement = here.getabs( pnt ); return it->type->charges_to_use(); } @@ -4979,8 +5006,9 @@ int iuse::chop_logs( player *p, item *it, bool t, const tripoint & ) t_trunk, t_stump }; - const std::function f = [&allowed_ter_id]( const tripoint & pnt ) { - const ter_id type = g->m.ter( pnt ); + map &here = get_map(); + const std::function f = [&allowed_ter_id, &here]( const tripoint & pnt ) { + const ter_id type = here.ter( pnt ); const bool is_allowed_terrain = allowed_ter_id.find( type ) != allowed_ter_id.end(); return is_allowed_terrain; }; @@ -5003,7 +5031,7 @@ int iuse::chop_logs( player *p, item *it, bool t, const tripoint & ) break; } p->assign_activity( ACT_CHOP_LOGS, moves, -1, p->get_item_position( it ) ); - p->activity.placement = g->m.getabs( pnt ); + p->activity.placement = here.getabs( pnt ); return it->type->charges_to_use(); } @@ -5045,13 +5073,14 @@ int iuse::oxytorch( player *p, item *it, bool, const tripoint & ) f_rack }; + map &here = get_map(); const std::function f = [&allowed_ter_id, - &allowed_furn_id]( const tripoint & pnt ) { + &allowed_furn_id, &here]( const tripoint & pnt ) { if( pnt == g->u.pos() ) { return false; } - const ter_id ter = g->m.ter( pnt ); - const auto furn = g->m.furn( pnt ); + const ter_id ter = here.ter( pnt ); + const auto furn = here.furn( pnt ); const bool is_allowed = ( allowed_ter_id.find( ter ) != allowed_ter_id.end() ) || ( allowed_furn_id.find( furn ) != allowed_furn_id.end() ); @@ -5064,8 +5093,8 @@ int iuse::oxytorch( player *p, item *it, bool, const tripoint & ) return 0; } const tripoint &pnt = *pnt_; - const ter_id ter = g->m.ter( pnt ); - const furn_id furn = g->m.furn( pnt ); + const ter_id ter = here.ter( pnt ); + const furn_id furn = here.furn( pnt ); if( !f( pnt ) ) { if( pnt == p->pos() ) { p->add_msg_if_player( m_info, _( "Yuck. Acetylene gas smells weird." ) ); @@ -5135,13 +5164,14 @@ int iuse::hacksaw( player *p, item *it, bool t, const tripoint & ) const std::set allowed_furn_id { f_rack }; + map &here = get_map(); const std::function f = [&allowed_ter_id, - &allowed_furn_id]( const tripoint & pnt ) { + &allowed_furn_id, &here]( const tripoint & pnt ) { if( pnt == g->u.pos() ) { return false; } - const ter_id ter = g->m.ter( pnt ); - const auto furn = g->m.furn( pnt ); + const ter_id ter = here.ter( pnt ); + const auto furn = here.furn( pnt ); const bool is_allowed = ( allowed_ter_id.find( ter ) != allowed_ter_id.end() ) || ( allowed_furn_id.find( furn ) != allowed_furn_id.end() ); @@ -5154,7 +5184,7 @@ int iuse::hacksaw( player *p, item *it, bool t, const tripoint & ) return 0; } const tripoint &pnt = *pnt_; - const ter_id ter = g->m.ter( pnt ); + const ter_id ter = here.ter( pnt ); if( !f( pnt ) ) { if( pnt == p->pos() ) { p->add_msg_if_player( m_info, _( "Why would you do that?" ) ); @@ -5166,7 +5196,7 @@ int iuse::hacksaw( player *p, item *it, bool t, const tripoint & ) } int moves; - if( ter == t_chainfence_posts || g->m.furn( pnt ) == f_rack ) { + if( ter == t_chainfence_posts || here.furn( pnt ) == f_rack ) { moves = to_moves( 2_minutes ); } else if( ter == t_window_enhanced || ter == t_window_enhanced_noglass ) { moves = to_moves( 5_minutes ); @@ -5196,11 +5226,12 @@ int iuse::boltcutters( player *p, item *it, bool, const tripoint & ) t_chaingate_l, t_chainfence }; - const std::function f = [&allowed_ter_id]( const tripoint & pnt ) { + map &here = get_map(); + const std::function f = [&allowed_ter_id, &here]( const tripoint & pnt ) { if( pnt == g->u.pos() ) { return false; } - const ter_id ter = g->m.ter( pnt ); + const ter_id ter = here.ter( pnt ); const bool is_allowed = allowed_ter_id.find( ter ) != allowed_ter_id.end(); return is_allowed; }; @@ -5211,7 +5242,7 @@ int iuse::boltcutters( player *p, item *it, bool, const tripoint & ) return 0; } const tripoint &pnt = *pnt_; - const ter_id type = g->m.ter( pnt ); + const ter_id type = here.ter( pnt ); if( !f( pnt ) ) { if( pnt == p->pos() ) { p->add_msg_if_player( m_info, @@ -5224,15 +5255,15 @@ int iuse::boltcutters( player *p, item *it, bool, const tripoint & ) if( type == t_chaingate_l ) { p->moves -= to_moves( 1_seconds ); - g->m.ter_set( pnt, t_chaingate_c ); + here.ter_set( pnt, t_chaingate_c ); sounds::sound( pnt, 5, sounds::sound_t::combat, _( "Gachunk!" ), true, "tool", "boltcutters" ); - g->m.spawn_item( point( p->posx(), p->posy() ), "scrap", 3 ); + here.spawn_item( point( p->posx(), p->posy() ), "scrap", 3 ); } else if( type == t_chainfence ) { p->moves -= to_moves( 5_seconds ); - g->m.ter_set( pnt, t_chainfence_posts ); + here.ter_set( pnt, t_chainfence_posts ); sounds::sound( pnt, 5, sounds::sound_t::combat, _( "Snick, snick, gachunk!" ), true, "tool", "boltcutters" ); - g->m.spawn_item( pnt, "wire", 20 ); + here.spawn_item( pnt, "wire", 20 ); } else { return 0; } @@ -5259,9 +5290,10 @@ int iuse::mop( player *p, item *it, bool, const tripoint & ) fd_slime, fd_sludge }; - const std::function f = [&to_check]( const tripoint & pnt ) { - if( !g->m.has_flag( "LIQUIDCONT", pnt ) ) { - map_stack items = g->m.i_at( pnt ); + map &here = get_map(); + const std::function f = [&to_check, &here]( const tripoint & pnt ) { + if( !here.has_flag( "LIQUIDCONT", pnt ) ) { + map_stack items = here.i_at( pnt ); auto found = std::find_if( items.begin(), items.end(), []( const item & it ) { return it.made_of( phase_id::LIQUID ); } ); @@ -5269,13 +5301,13 @@ int iuse::mop( player *p, item *it, bool, const tripoint & ) return true; } } - field &fld = g->m.field_at( pnt ); + field &fld = here.field_at( pnt ); for( field_type_id fid : to_check ) { if( fld.find_field_c( fid ) ) { return true; } } - if( const optional_vpart_position vp = g->m.veh_at( pnt ) ) { + if( const optional_vpart_position vp = here.veh_at( pnt ) ) { vehicle *const veh = &vp->vehicle(); std::vector parts_here = veh->parts_at_relative( vp->mount(), true ); for( int elem : parts_here ) { @@ -5313,9 +5345,9 @@ int iuse::mop( player *p, item *it, bool, const tripoint & ) p->add_msg_if_player( m_info, _( "You move the mop around, unsure whether it's doing any good." ) ); p->moves -= 15; if( one_in( 3 ) ) { - g->m.mop_spills( pnt ); + here.mop_spills( pnt ); } - } else if( g->m.mop_spills( pnt ) ) { + } else if( here.mop_spills( pnt ) ) { p->add_msg_if_player( m_info, _( "You mop up the spill." ) ); p->moves -= 15; } else { @@ -5348,6 +5380,7 @@ int iuse::artifact( player *p, item *it, bool, const tripoint & ) num_used += rng( 1, art->effects_activated.size() - num_used ); } + map &here = get_map(); std::vector effects = art->effects_activated; for( size_t i = 0; i < num_used && !effects.empty(); i++ ) { const art_effect_active used = random_entry_removed( effects ); @@ -5368,7 +5401,7 @@ int iuse::artifact( player *p, item *it, bool, const tripoint & ) for( int n = 0; n < dist; n++ ) { bolt.x += dir.x; bolt.y += dir.y; - g->m.add_field( {bolt, p->posz()}, fd_electricity, rng( 2, 3 ) ); + here.add_field( {bolt, p->posz()}, fd_electricity, rng( 2, 3 ) ); if( one_in( 4 ) ) { if( dir.x == 0 ) { dir.x = rng( 0, 1 ) * 2 - 1; @@ -5413,8 +5446,8 @@ int iuse::artifact( player *p, item *it, bool, const tripoint & ) case AEA_BLOOD: { bool blood = false; - for( const tripoint &tmp : g->m.points_in_radius( p->pos(), 4 ) ) { - if( !one_in( 4 ) && g->m.add_field( tmp, fd_blood, 3 ) && + for( const tripoint &tmp : here.points_in_radius( p->pos(), 4 ) ) { + if( !one_in( 4 ) && here.add_field( tmp, fd_blood, 3 ) && ( blood || g->u.sees( tmp ) ) ) { blood = true; } @@ -5428,14 +5461,14 @@ int iuse::artifact( player *p, item *it, bool, const tripoint & ) case AEA_FATIGUE: { p->add_msg_if_player( m_warning, _( "The fabric of space seems to decay." ) ); point p2( rng( p->posx() - 3, p->posx() + 3 ), rng( p->posy() - 3, p->posy() + 3 ) ); - g->m.add_field( {p2, p->posz()}, fd_fatigue, rng( 1, 2 ) ); + here.add_field( {p2, p->posz()}, fd_fatigue, rng( 1, 2 ) ); } break; case AEA_ACIDBALL: { if( const cata::optional acidball = g->look_around() ) { - for( const tripoint &tmp : g->m.points_in_radius( *acidball, 1 ) ) { - g->m.add_field( tmp, fd_acid, rng( 2, 3 ) ); + for( const tripoint &tmp : here.points_in_radius( *acidball, 1 ) ) { + here.add_field( tmp, fd_acid, rng( 2, 3 ) ); } } } @@ -5444,12 +5477,12 @@ int iuse::artifact( player *p, item *it, bool, const tripoint & ) case AEA_PULSE: sounds::sound( p->pos(), 30, sounds::sound_t::combat, _( "The earth shakes!" ), true, "misc", "earthquake" ); - for( const tripoint &pt : g->m.points_in_radius( p->pos(), 2 ) ) { - g->m.bash( pt, 40 ); - g->m.bash( pt, 40 ); // Multibash effect, so that doors &c will fall - g->m.bash( pt, 40 ); - if( g->m.is_bashable( pt ) && rng( 1, 10 ) >= 3 ) { - g->m.bash( pt, 999, false, true ); + for( const tripoint &pt : here.points_in_radius( p->pos(), 2 ) ) { + here.bash( pt, 40 ); + here.bash( pt, 40 ); // Multibash effect, so that doors &c will fall + here.bash( pt, 40 ); + if( here.is_bashable( pt ) && rng( 1, 10 ) >= 3 ) { + here.bash( pt, 999, false, true ); } } break; @@ -5460,7 +5493,7 @@ int iuse::artifact( player *p, item *it, bool, const tripoint & ) break; case AEA_CONFUSED: - for( const tripoint &dest : g->m.points_in_radius( p->pos(), 8 ) ) { + for( const tripoint &dest : here.points_in_radius( p->pos(), 8 ) ) { if( monster *const mon = g->critter_at( dest, true ) ) { mon->add_effect( effect_stunned, rng( 5_turns, 15_turns ) ); } @@ -5468,7 +5501,7 @@ int iuse::artifact( player *p, item *it, bool, const tripoint & ) break; case AEA_ENTRANCE: - for( const tripoint &dest : g->m.points_in_radius( p->pos(), 8 ) ) { + for( const tripoint &dest : here.points_in_radius( p->pos(), 8 ) ) { monster *const mon = g->critter_at( dest, true ); if( mon && mon->friendly == 0 && rng( 0, 600 ) > mon->get_hp() ) { mon->make_friendly(); @@ -5529,8 +5562,8 @@ int iuse::artifact( player *p, item *it, bool, const tripoint & ) case AEA_RADIATION: add_msg( m_warning, _( "Horrible gases are emitted!" ) ); - for( const tripoint &dest : g->m.points_in_radius( p->pos(), 1 ) ) { - g->m.add_field( dest, fd_nuke_gas, rng( 2, 3 ) ); + for( const tripoint &dest : here.points_in_radius( p->pos(), 1 ) ) { + here.add_field( dest, fd_nuke_gas, rng( 2, 3 ) ); } break; @@ -5558,7 +5591,7 @@ int iuse::artifact( player *p, item *it, bool, const tripoint & ) std::vector ps = closest_tripoints_first( p->pos(), 3 ); for( auto p_it : ps ) { if( !one_in( 3 ) ) { - g->m.add_field( p_it, fd_fire, 1 + rng( 0, 1 ) * rng( 0, 1 ), 3_minutes ); + here.add_field( p_it, fd_fire, 1 + rng( 0, 1 ) * rng( 0, 1 ), 3_minutes ); } } break; @@ -5617,7 +5650,7 @@ int iuse::artifact( player *p, item *it, bool, const tripoint & ) monp.x = ( one_in( 2 ) ? p->posx() - 5 : p->posx() + 5 ); monp.y = rng( p->posy() - 5, p->posy() + 5 ); } - if( !g->m.sees( monp, p->pos(), 10 ) ) { + if( !here.sees( monp, p->pos(), 10 ) ) { continue; } if( monster *const spawned = g->place_critter_at( mon_shadow, monp ) ) { @@ -5672,22 +5705,23 @@ int iuse::spray_can( player *p, item *it, bool, const tripoint & ) int iuse::handle_ground_graffiti( player &p, item *it, const std::string &prefix, const tripoint &where ) { + map &here = get_map(); string_input_popup popup; std::string message = popup .title( prefix + " " + _( "(To delete, clear the text and confirm)" ) ) - .text( g->m.has_graffiti_at( where ) ? g->m.graffiti_at( where ) : std::string() ) + .text( here.has_graffiti_at( where ) ? here.graffiti_at( where ) : std::string() ) .identifier( "graffiti" ) .query_string(); if( popup.canceled() ) { return 0; } - bool grave = g->m.ter( where ) == t_grave_new; + bool grave = here.ter( where ) == t_grave_new; int move_cost; if( message.empty() ) { - if( g->m.has_graffiti_at( where ) ) { - move_cost = 3 * g->m.graffiti_at( where ).length(); - g->m.delete_graffiti( where ); + if( here.has_graffiti_at( where ) ) { + move_cost = 3 * here.graffiti_at( where ).length(); + here.delete_graffiti( where ); if( grave ) { p.add_msg_if_player( m_info, _( "You blur the inscription on the grave." ) ); } else { @@ -5697,7 +5731,7 @@ int iuse::handle_ground_graffiti( player &p, item *it, const std::string &prefix return 0; } } else { - g->m.set_graffiti( where, message ); + here.set_graffiti( where, message ); if( grave ) { p.add_msg_if_player( m_info, _( "You carve an inscription on the grave." ) ); } else { @@ -5753,7 +5787,7 @@ int iuse::heatpack( player *p, item *it, bool, const tripoint & ) int iuse::heat_food( player *p, item *it, bool, const tripoint & ) { - if( g->m.has_nearby_fire( p->pos() ) ) { + if( get_map().has_nearby_fire( p->pos() ) ) { heat_item( *p ); } else if( p->has_active_bionic( bio_tools ) && p->get_power_level() > 10_kJ && query_yn( _( "There is no fire around, use your integrated toolset instead?" ) ) ) { @@ -5881,21 +5915,22 @@ int iuse::unfold_generic( player *p, item *it, bool, const tripoint & ) p->add_msg_if_player( m_info, _( "You cannot do that while mounted." ) ); return 0; } - vehicle *veh = g->m.add_vehicle( vproto_id( "none" ), p->pos(), 0, 0, 0, false ); + map &here = get_map(); + vehicle *veh = here.add_vehicle( vproto_id( "none" ), p->pos(), 0, 0, 0, false ); if( veh == nullptr ) { p->add_msg_if_player( m_info, _( "There's no room to unfold the %s." ), it->tname() ); return 0; } veh->name = it->get_var( "vehicle_name" ); if( !veh->restore( it->get_var( "folding_bicycle_parts" ) ) ) { - g->m.destroy_vehicle( veh ); + here.destroy_vehicle( veh ); return 0; } const bool can_float = size( veh->get_avail_parts( "FLOATS" ) ) > 2; - const auto invalid_pos = []( const tripoint & pp, bool can_float ) { - return ( g->m.has_flag_ter( TFLAG_DEEP_WATER, pp ) && !can_float ) || - g->m.veh_at( pp ) || g->m.impassable( pp ); + const auto invalid_pos = [&here]( const tripoint & pp, bool can_float ) { + return ( here.has_flag_ter( TFLAG_DEEP_WATER, pp ) && !can_float ) || + here.veh_at( pp ) || here.impassable( pp ); }; for( const vpart_reference &vp : veh->get_all_parts() ) { if( vp.info().location != "structure" ) { @@ -5904,12 +5939,12 @@ int iuse::unfold_generic( player *p, item *it, bool, const tripoint & ) const tripoint pp = vp.pos(); if( invalid_pos( pp, can_float ) ) { p->add_msg_if_player( m_info, _( "There's no room to unfold the %s." ), it->tname() ); - g->m.destroy_vehicle( veh ); + here.destroy_vehicle( veh ); return 0; } } - g->m.add_vehicle_to_cache( veh ); + here.add_vehicle_to_cache( veh ); std::string unfold_msg = it->get_var( "unfold_msg" ); if( unfold_msg.empty() ) { @@ -6958,7 +6993,7 @@ struct extended_photo_def : public JsonDeserializer, public JsonSerializer { static std::string colorized_trap_name_at( const tripoint &point ) { - const trap &trap = g->m.tr_at( point ); + const trap &trap = get_map().tr_at( point ); std::string name; if( !trap.is_null() && trap.get_visibility() <= 1 ) { name = colorize( trap.name(), trap.color ) + _( " on " ); @@ -6969,7 +7004,7 @@ static std::string colorized_trap_name_at( const tripoint &point ) static std::string colorized_field_description_at( const tripoint &point ) { std::string field_text; - const field &field = g->m.field_at( point ); + const field &field = get_map().field_at( point ); const field_entry *entry = field.find_field( field.displayed_field_type() ); if( entry ) { field_text = string_format( _( description_affixes.at( field.displayed_description_affix() ) ), @@ -7000,7 +7035,7 @@ static std::string colorized_item_description( const item &item ) static item get_top_item_at_point( const tripoint &point, const units::volume &min_visible_volume ) { - map_stack items = g->m.i_at( point ); + map_stack items = get_map().i_at( point ); // iterate from topmost item down to ground for( const item &it : items ) { if( it.volume() > min_visible_volume ) { @@ -7014,9 +7049,10 @@ static item get_top_item_at_point( const tripoint &point, static std::string colorized_ter_name_flags_at( const tripoint &point, const std::vector &flags, const std::vector &ter_whitelist ) { - const ter_id ter = g->m.ter( point ); + map &here = get_map(); + const ter_id ter = here.ter( point ); std::string name = colorize( ter->name(), ter->color() ); - const std::string &graffiti_message = g->m.graffiti_at( point ); + const std::string &graffiti_message = here.graffiti_at( point ); if( !graffiti_message.empty() ) { name += string_format( _( " with graffiti \"%s\"" ), graffiti_message ); @@ -7049,10 +7085,11 @@ static std::string colorized_feature_description_at( const tripoint ¢er_poin const units::volume &min_visible_volume ) { item_found = false; - const furn_id furn = g->m.furn( center_point ); + map &here = get_map(); + const furn_id furn = here.furn( center_point ); if( furn != f_null && furn.is_valid() ) { std::string furn_str = colorize( furn->name(), c_yellow ); - std::string sign_message = g->m.get_signage( center_point ); + std::string sign_message = here.get_signage( center_point ); if( !sign_message.empty() ) { furn_str += string_format( _( " with message \"%s\"" ), sign_message ); } @@ -7191,8 +7228,9 @@ static object_names_collection enumerate_objects_around_point( const tripoint &p std::unordered_set &ignored_points, std::unordered_set &vehicles_recorded ) { - const tripoint_range bounds = g->m.points_in_radius( bounds_center_point, bounds_radius ); - const tripoint_range points_in_radius = g->m.points_in_radius( point, radius ); + map &here = get_map(); + const tripoint_range bounds = here.points_in_radius( bounds_center_point, bounds_radius ); + const tripoint_range points_in_radius = here.points_in_radius( point, radius ); int dist = rl_dist( camera_pos, point ); bool item_found = false; @@ -7206,7 +7244,7 @@ static object_names_collection enumerate_objects_around_point( const tripoint &p // store objects in radius for( const tripoint &point_around_figure : points_in_radius ) { if( !bounds.is_point_inside( point_around_figure ) || - !g->m.sees( camera_pos, point_around_figure, dist + radius ) || + !here.sees( camera_pos, point_around_figure, dist + radius ) || ( ignored_points.find( point_around_figure ) != ignored_points.end() && !( point_around_figure == point && create_figure_desc ) ) ) { continue; // disallow photos with not visible objects @@ -7219,7 +7257,7 @@ static object_names_collection enumerate_objects_around_point( const tripoint &p const item item = get_top_item_at_point( point_around_figure, volume_to_search ); - const optional_vpart_position veh_part_pos = g->m.veh_at( point_around_figure ); + const optional_vpart_position veh_part_pos = here.veh_at( point_around_figure ); std::string unusual_ter_desc = colorized_ter_name_flags_at( point_around_figure, camera_ter_whitelist_flags, camera_ter_whitelist_types ); @@ -7343,7 +7381,8 @@ static extended_photo_def photo_def_for_camera_point( const tripoint &aim_point, std::string timestamp = to_string( time_point( calendar::turn ) ); int dist = rl_dist( camera_pos, aim_point ); - const tripoint_range bounds = g->m.points_in_radius( aim_point, 2 ); + map &here = get_map(); + const tripoint_range bounds = here.points_in_radius( aim_point, 2 ); extended_photo_def photo; bool need_store_weather = false; int outside_tiles_num = 0; @@ -7361,14 +7400,14 @@ static extended_photo_def photo_def_for_camera_point( const tripoint &aim_point, // first scan for critters and mark nearby furniture, vehicles and items for( const tripoint ¤t : bounds ) { - if( !g->m.sees( camera_pos, current, dist + 3 ) ) { + if( !here.sees( camera_pos, current, dist + 3 ) ) { continue; // disallow photos with non-visible objects } monster *const mon = g->critter_at( current, false ); avatar *guy = g->critter_at( current ); total_tiles_num++; - if( g->m.is_outside( current ) ) { + if( here.is_outside( current ) ) { need_store_weather = true; outside_tiles_num++; } @@ -7447,15 +7486,15 @@ static extended_photo_def photo_def_for_camera_point( const tripoint &aim_point, std::string ter_name = colorized_ter_name_flags_at( aim_point, {}, {} ); const std::string field_desc = colorized_field_description_at( aim_point ); - bool found_vehicle_aim_point = g->m.veh_at( aim_point ).has_value(), + bool found_vehicle_aim_point = here.veh_at( aim_point ).has_value(), found_furniture_aim_point = !furn_desc.empty(); // colorized_feature_description_at do not update flag if no furniture found, so need to check again if( !found_furniture_aim_point ) { found_item_aim_point = !item.is_null(); } - const ter_id ter_aim = g->m.ter( aim_point ); - const furn_id furn_aim = g->m.furn( aim_point ); + const ter_id ter_aim = here.ter( aim_point ); + const furn_id furn_aim = here.furn( aim_point ); if( !description_figures_status.empty() ) { std::string names = enumerate_as_string( description_figures_status.begin(), @@ -7472,7 +7511,7 @@ static extended_photo_def photo_def_for_camera_point( const tripoint &aim_point, + " " + figure_status.second; } } else if( found_vehicle_aim_point ) { - const optional_vpart_position veh_part_pos = g->m.veh_at( aim_point ); + const optional_vpart_position veh_part_pos = here.veh_at( aim_point ); const std::string veh_name = colorize( veh_part_pos->vehicle().disp_name(), c_light_blue ); photo.name = veh_name; photo_text += veh_name + "."; @@ -7547,7 +7586,7 @@ static extended_photo_def photo_def_for_camera_point( const tripoint &aim_point, obj_list ); } - const oter_id &cur_ter = overmap_buffer.ter( ms_to_omt_copy( g->m.getabs( aim_point ) ) ); + const oter_id &cur_ter = overmap_buffer.ter( ms_to_omt_copy( here.getabs( aim_point ) ) ); std::string overmap_desc = string_format( _( "In the background you can see a %s" ), colorize( cur_ter->get_name(), cur_ter->get_color() ) ); if( outside_tiles_num == total_tiles_num ) { @@ -7752,6 +7791,7 @@ int iuse::camera( player *p, item *it, bool, const tripoint & ) return 0; } + map &here = get_map(); if( c_shot == choice ) { const cata::optional aim_point_ = g->look_around(); @@ -7761,7 +7801,7 @@ int iuse::camera( player *p, item *it, bool, const tripoint & ) } tripoint aim_point = *aim_point_; bool incorrect_focus = false; - tripoint_range aim_bounds = g->m.points_in_radius( aim_point, 2 ); + tripoint_range aim_bounds = here.points_in_radius( aim_point, 2 ); std::vector trajectory = line_to( p->pos(), aim_point, 0, 0 ); trajectory.push_back( aim_point ); @@ -7776,12 +7816,12 @@ int iuse::camera( player *p, item *it, bool, const tripoint & ) const tripoint trajectory_point = *point_it; if( point_it != trajectory.end() ) { const tripoint next_point = *( point_it + 1 ); // Trajectory ends on last visible tile - if( !g->m.sees( p->pos(), next_point, rl_dist( p->pos(), next_point ) + 3 ) ) { + if( !here.sees( p->pos(), next_point, rl_dist( p->pos(), next_point ) + 3 ) ) { p->add_msg_if_player( _( "You have the wrong camera focus." ) ); incorrect_focus = true; // recalculate target point aim_point = trajectory_point; - aim_bounds = g->m.points_in_radius( trajectory_point, 2 ); + aim_bounds = here.points_in_radius( trajectory_point, 2 ); } } @@ -8014,7 +8054,7 @@ int iuse::ehandcuffs( player *p, item *it, bool t, const tripoint &pos ) if( t ) { - if( g->m.has_flag( "SWIMMABLE", pos.xy() ) ) { + if( get_map().has_flag( "SWIMMABLE", pos.xy() ) ) { it->item_tags.erase( "NO_UNWIELD" ); it->ammo_unset(); it->active = false; @@ -8234,8 +8274,9 @@ int iuse::radiocaron( player *p, item *it, bool t, const tripoint &pos ) static void sendRadioSignal( player &p, const std::string &signal ) { - for( const tripoint &loc : g->m.points_in_radius( p.pos(), 30 ) ) { - for( item &it : g->m.i_at( loc ) ) { + map &here = get_map(); + for( const tripoint &loc : here.points_in_radius( p.pos(), 30 ) ) { + for( item &it : here.i_at( loc ) ) { if( it.has_flag( "RADIO_ACTIVATION" ) && it.has_flag( signal ) ) { sounds::sound( p.pos(), 6, sounds::sound_t::alarm, _( "beep" ), true, "misc", "beep" ); if( it.has_flag( "RADIO_INVOKE_PROC" ) ) { @@ -8292,6 +8333,7 @@ int iuse::radiocontrol( player *p, item *it, bool t, const tripoint & ) _( "Press red button" ), _( "Press blue button" ), _( "Press green button" ) } ); + map &here = get_map(); if( choice < 0 ) { return 0; } else if( choice == 0 ) { @@ -8299,7 +8341,7 @@ int iuse::radiocontrol( player *p, item *it, bool t, const tripoint & ) it->active = false; p->remove_value( "remote_controlling" ); } else { - std::list> rc_pairs = g->m.get_rc_items(); + std::list> rc_pairs = here.get_rc_items(); tripoint rc_item_location = {999, 999, 999}; // TODO: grab the closest car or similar? for( auto &rc_pairs_rc_pair : rc_pairs ) { @@ -8426,7 +8468,7 @@ static vehicle *pickveh( const tripoint ¢er, bool advanced ) pmenu.title = _( "Select vehicle to access" ); std::vector< vehicle * > vehs; - for( auto &veh : g->m.get_vehicles() ) { + for( auto &veh : get_map().get_vehicles() ) { auto &v = veh.v; if( rl_dist( center, v->global_pos3() ) < 40 && v->fuel_left( itype_battery, true ) > 0 && @@ -8575,7 +8617,7 @@ static bool multicooker_hallu( player &p ) } else { p.add_msg_if_player( m_info, _( "You're surrounded by aggressive multi-cookers!" ) ); - for( const tripoint &pn : g->m.points_in_radius( p.pos(), 1 ) ) { + for( const tripoint &pn : get_map().points_in_radius( p.pos(), 1 ) ) { if( monster *const m = g->place_critter_at( mon_hallu_multicooker, pn ) ) { m->hallucination = true; } @@ -8628,7 +8670,7 @@ int iuse::autoclave( player *p, item *it, bool t, const tripoint &pos ) if( clean_cbm ) { empty = false; if( query_yn( _( "Autoclave already contains a CBM. Do you want to remove it?" ) ) ) { - g->m.add_item( pos, *clean_cbm ); + get_map().add_item( pos, *clean_cbm ); it->remove_item( *clean_cbm ); if( !query_yn( _( "Do you want to use the autoclave?" ) ) ) { return 0; @@ -9010,6 +9052,7 @@ int iuse::tow_attach( player *p, item *it, bool, const tripoint & ) it->process( p, p->pos(), false ); p->moves -= 15; }; + map &here = get_map(); if( initial_state == "attach_first" ) { const cata::optional posp_ = choose_adjacent( _( "Attach cable to the vehicle that will do the towing." ) ); @@ -9017,7 +9060,7 @@ int iuse::tow_attach( player *p, item *it, bool, const tripoint & ) return 0; } const tripoint posp = *posp_; - const optional_vpart_position vp = g->m.veh_at( posp ); + const optional_vpart_position vp = here.veh_at( posp ); if( !vp ) { p->add_msg_if_player( _( "There's no vehicle there." ) ); return 0; @@ -9034,19 +9077,19 @@ int iuse::tow_attach( player *p, item *it, bool, const tripoint & ) return 0; } } - const tripoint &abspos = g->m.getabs( posp ); + const tripoint &abspos = here.getabs( posp ); it->set_var( "source_x", abspos.x ); it->set_var( "source_y", abspos.y ); it->set_var( "source_z", g->get_levz() ); set_cable_active( p, it, "pay_out_cable" ); } } else { - const auto confirm_source_vehicle = []( player * p, item * it, const bool detach_if_missing ) { + const auto confirm_source_vehicle = [&here]( player * p, item * it, const bool detach_if_missing ) { tripoint source_global( it->get_var( "source_x", 0 ), it->get_var( "source_y", 0 ), it->get_var( "source_z", 0 ) ); - tripoint source_local = g->m.getlocal( source_global ); - const optional_vpart_position source_vp = g->m.veh_at( source_local ); + tripoint source_local = here.getlocal( source_global ); + const optional_vpart_position source_vp = here.veh_at( source_local ); vehicle *const source_veh = veh_pointer_or_null( source_vp ); if( detach_if_missing && source_veh == nullptr ) { if( p->has_item( *it ) ) { @@ -9084,7 +9127,7 @@ int iuse::tow_attach( player *p, item *it, bool, const tripoint & ) } const tripoint vpos = *vpos_; - const optional_vpart_position target_vp = g->m.veh_at( vpos ); + const optional_vpart_position target_vp = here.veh_at( vpos ); if( !target_vp ) { p->add_msg_if_player( _( "There's no vehicle there." ) ); return 0; @@ -9156,6 +9199,7 @@ int iuse::cable_attach( player *p, item *it, bool, const tripoint & ) p->find_remote_fuel(); } }; + map &here = get_map(); if( initial_state == "attach_first" ) { if( has_bio_cable ) { uilist kmenu; @@ -9203,25 +9247,25 @@ int iuse::cable_attach( player *p, item *it, bool, const tripoint & ) return 0; } const tripoint posp = *posp_; - const optional_vpart_position vp = g->m.veh_at( posp ); - auto ter = g->m.ter( posp ); + const optional_vpart_position vp = here.veh_at( posp ); + auto ter = here.ter( posp ); if( !vp && ter != t_chainfence ) { p->add_msg_if_player( _( "There's no vehicle there." ) ); return 0; } else { - const auto abspos = g->m.getabs( posp ); + const auto abspos = here.getabs( posp ); it->set_var( "source_x", abspos.x ); it->set_var( "source_y", abspos.y ); it->set_var( "source_z", g->get_levz() ); set_cable_active( p, it, "pay_out_cable" ); } } else { - const auto confirm_source_vehicle = []( player * p, item * it, const bool detach_if_missing ) { + const auto confirm_source_vehicle = [&here]( player * p, item * it, const bool detach_if_missing ) { tripoint source_global( it->get_var( "source_x", 0 ), it->get_var( "source_y", 0 ), it->get_var( "source_z", 0 ) ); - tripoint source_local = g->m.getlocal( source_global ); - const optional_vpart_position source_vp = g->m.veh_at( source_local ); + tripoint source_local = here.getlocal( source_global ); + const optional_vpart_position source_vp = here.veh_at( source_local ); vehicle *const source_veh = veh_pointer_or_null( source_vp ); if( detach_if_missing && source_veh == nullptr ) { if( p != nullptr && p->has_item( *it ) ) { @@ -9314,12 +9358,12 @@ int iuse::cable_attach( player *p, item *it, bool, const tripoint & ) } const tripoint vpos = *vpos_; - const optional_vpart_position target_vp = g->m.veh_at( vpos ); + const optional_vpart_position target_vp = here.veh_at( vpos ); if( !target_vp ) { p->add_msg_if_player( _( "There's no vehicle there." ) ); return 0; } else if( cable_cbm ) { - const auto abspos = g->m.getabs( vpos ); + const auto abspos = here.getabs( vpos ); it->set_var( "source_x", abspos.x ); it->set_var( "source_y", abspos.y ); it->set_var( "source_z", g->get_levz() ); @@ -9336,7 +9380,7 @@ int iuse::cable_attach( player *p, item *it, bool, const tripoint & ) return 0; } - tripoint target_global = g->m.getabs( vpos ); + tripoint target_global = here.getabs( vpos ); // TODO: make sure there is always a matching vpart id here. Maybe transform this into // a iuse_actor class, or add a check in item_factory. const vpart_id vpid( it->typeId().str() ); @@ -9344,7 +9388,7 @@ int iuse::cable_attach( player *p, item *it, bool, const tripoint & ) point vcoords = source_vp->mount(); vehicle_part source_part( vpid, vcoords, item( *it ) ); source_part.target.first = target_global; - source_part.target.second = g->m.getabs( target_veh->global_pos3() ); + source_part.target.second = here.getabs( target_veh->global_pos3() ); source_veh->install_part( vcoords, source_part ); vcoords = target_vp->mount(); @@ -9353,7 +9397,7 @@ int iuse::cable_attach( player *p, item *it, bool, const tripoint & ) it->get_var( "source_y", 0 ), it->get_var( "source_z", 0 ) ); target_part.target.first = source_global; - target_part.target.second = g->m.getabs( source_veh->global_pos3() ); + target_part.target.second = here.getabs( source_veh->global_pos3() ); target_veh->install_part( vcoords, target_part ); if( p != nullptr && p->has_item( *it ) ) { @@ -9440,7 +9484,7 @@ int iuse::weather_tool( player *p, item *it, bool, const tripoint & ) if( it->typeId() == itype_weather_reader ) { int vehwindspeed = 0; - if( optional_vpart_position vp = g->m.veh_at( p->pos() ) ) { + if( optional_vpart_position vp = get_map().veh_at( p->pos() ) ) { vehwindspeed = std::abs( vp->vehicle().velocity / 100 ); // For mph } const oter_id &cur_om_ter = overmap_buffer.ter( p->global_omt_location() ); @@ -9638,7 +9682,8 @@ int iuse::capture_monster_act( player *p, item *it, bool, const tripoint &pos ) int iuse::ladder( player *p, item *, bool, const tripoint & ) { - if( !g->m.has_zlevels() ) { + map &here = get_map(); + if( !here.has_zlevels() ) { debugmsg( "Ladder can't be used in non-z-level mode" ); return 0; } @@ -9652,14 +9697,14 @@ int iuse::ladder( player *p, item *, bool, const tripoint & ) } const tripoint pnt = *pnt_; - if( !g->is_empty( pnt ) || g->m.has_furn( pnt ) ) { + if( !g->is_empty( pnt ) || here.has_furn( pnt ) ) { p->add_msg_if_player( m_bad, _( "Can't place it there." ) ); return 0; } p->add_msg_if_player( _( "You set down the ladder." ) ); p->moves -= to_moves( 5_seconds ); - g->m.furn_set( pnt, furn_str_id( "f_ladder" ) ); + here.furn_set( pnt, furn_str_id( "f_ladder" ) ); return 1; } @@ -9814,7 +9859,7 @@ int iuse::wash_items( player *p, bool soft_items, bool hard_items ) return 0; } const std::vector helpers = g->u.get_crafting_helpers(); - const int helpersize = g->u.get_num_crafting_helpers( 3 ); + const int helpersize = p->get_num_crafting_helpers( 3 ); required.time = required.time * ( 1 - ( helpersize / 10 ) ); for( const npc *np : helpers ) { add_msg( m_info, _( "%s helps with this task…" ), np->name ); @@ -9847,18 +9892,19 @@ int iuse::break_stick( player *p, item *it, bool, const tripoint & ) comps.push_back( item_comp( it->typeId(), 1 ) ); p->consume_items( comps, 1, is_crafting_component ); int chance = rng( 0, 100 ); + map &here = get_map(); if( chance <= 20 ) { p->add_msg_if_player( _( "You try to break the stick in two, but it shatters into splinters." ) ); - g->m.spawn_item( p->pos(), "splinter", 2 ); + here.spawn_item( p->pos(), "splinter", 2 ); return 1; } else if( chance <= 40 ) { p->add_msg_if_player( _( "The stick breaks clean into two parts." ) ); - g->m.spawn_item( p->pos(), "stick", 2 ); + here.spawn_item( p->pos(), "stick", 2 ); return 1; } else if( chance <= 100 ) { p->add_msg_if_player( _( "You break the stick, but one half shatters into splinters." ) ); - g->m.spawn_item( p->pos(), "stick", 1 ); - g->m.spawn_item( p->pos(), "splinter", 1 ); + here.spawn_item( p->pos(), "stick", 1 ); + here.spawn_item( p->pos(), "splinter", 1 ); return 1; } return 0; From ad2d3ea0d58251805397db119cd1b099e01c44f7 Mon Sep 17 00:00:00 2001 From: olanti-p Date: Thu, 2 Jul 2020 02:45:43 +0300 Subject: [PATCH 037/420] Catch disk write errors for small files --- src/mapsharing.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/mapsharing.cpp b/src/mapsharing.cpp index da5d18872e296..f8715c8bfa96e 100644 --- a/src/mapsharing.cpp +++ b/src/mapsharing.cpp @@ -134,13 +134,15 @@ void ofstream_wrapper::close() return; } - if( file_stream.fail() ) { + file_stream.flush(); + bool failed = file_stream.fail(); + file_stream.close(); + if( failed ) { // Remove the incomplete or otherwise faulty file (if possible). // Failures from it are ignored as we can't really do anything about them. remove_file( temp_path ); throw std::runtime_error( "writing to file failed" ); } - file_stream.close(); if( !rename_file( temp_path, path ) ) { // Leave the temp path, so the user can move it if possible. throw std::runtime_error( "moving temporary file \"" + temp_path + "\" failed" ); From 65c9bdaeee9265a04484181887f98d497629c863 Mon Sep 17 00:00:00 2001 From: Menschheit Date: Thu, 2 Jul 2020 08:22:17 +0200 Subject: [PATCH 038/420] Make it possible to directly use vehicle mounted electric tools (#41700) --- src/character.cpp | 1 - src/item.cpp | 4 +++- src/vehicle_part.cpp | 1 - src/vehicle_use.cpp | 11 ++++++++--- 4 files changed, 11 insertions(+), 6 deletions(-) diff --git a/src/character.cpp b/src/character.cpp index a46065613732e..c6bb41b07a840 100644 --- a/src/character.cpp +++ b/src/character.cpp @@ -7698,7 +7698,6 @@ bool Character::invoke_item( item *used, const std::string &method, const tripoi return false; } // Prevent accessing the item as it may have been deleted by the invoked iuse function. - if( used->is_tool() || actually_used->is_medication() ) { return consume_charges( *actually_used, charges_used ); } else if( used->is_bionic() || used->is_deployable() || method == "place_trap" ) { diff --git a/src/item.cpp b/src/item.cpp index 68330de06a237..5ef85882ae8b5 100644 --- a/src/item.cpp +++ b/src/item.cpp @@ -592,6 +592,7 @@ item &item::ammo_set( const itype_id &ammo, int qty ) const ammotype &ammo_type = ammo->ammo->type; if( qty < 0 ) { // completely fill an integral or existing magazine + //if( magazine_current() ) then we need capacity of the magazine instead of the item maybe? if( magazine_integral() || magazine_current() ) { qty = ammo_capacity( ammo_type ); @@ -628,7 +629,8 @@ item &item::ammo_set( const itype_id &ammo, int qty ) // check ammo is valid for the item const itype *atype = item_controller->find_template( ammo ); if( atype->ammo && ammo_types().count( atype->ammo->type ) == 0 && - !magazine_compatible().count( atype->get_id() ) ) { + !magazine_compatible().count( atype->get_id() ) && !( magazine_current() && + magazine_current()->ammo_types().count( atype->ammo->type ) ) ) { debugmsg( "Tried to set invalid ammo of %s for %s", atype->nname( qty ), tname() ); return *this; } diff --git a/src/vehicle_part.cpp b/src/vehicle_part.cpp index 3cf10e0e41350..810a98ee32060 100644 --- a/src/vehicle_part.cpp +++ b/src/vehicle_part.cpp @@ -233,7 +233,6 @@ int vehicle_part::ammo_remaining() const if( is_tank() ) { return base.contents.empty() ? 0 : base.contents.legacy_front().charges; } - if( is_fuel_store( false ) || is_turret() ) { return base.ammo_remaining(); } diff --git a/src/vehicle_use.cpp b/src/vehicle_use.cpp index 01ac30d7147c0..e18433533905c 100644 --- a/src/vehicle_use.cpp +++ b/src/vehicle_use.cpp @@ -2015,9 +2015,14 @@ void vehicle::interact_with( const tripoint &pos, int interact_part ) } auto veh_tool = [&]( const itype_id & obj ) { item pseudo( obj ); - if( fuel_left( itype_battery, true ) < pseudo.ammo_required() ) { - return false; - } + //Pseudo items don't have a magazine in it, and they don't need it anymore. + //Pseudo magazine in Pseudo item sounds good. + //Somehow the set of available ammos in pocket_data loaded from json is alphabetic, + //so the default ammo is always atomic, haven't checked the relevant codes yet. + item pseudo_magazine( pseudo.magazine_default() ); + //no initial ammo + pseudo_magazine.contents.clear_items(); + pseudo.put_in( pseudo_magazine, item_pocket::pocket_type::MAGAZINE_WELL ); int capacity = pseudo.ammo_capacity( ammotype( "battery" ) ); int qty = capacity - discharge_battery( capacity ); pseudo.ammo_set( itype_battery, qty ); From 33ea3e059b0c4b13cce64ce89d3b6fe9ca84a042 Mon Sep 17 00:00:00 2001 From: Kevin Granade Date: Wed, 1 Jul 2020 23:38:39 -0700 Subject: [PATCH 039/420] Iuse audit of g->u use (#41749) --- src/iuse.cpp | 167 +++++++++++++++++++++++++++------------------------ 1 file changed, 88 insertions(+), 79 deletions(-) diff --git a/src/iuse.cpp b/src/iuse.cpp index e45b76115da3a..e35c769cedf11 100644 --- a/src/iuse.cpp +++ b/src/iuse.cpp @@ -716,7 +716,7 @@ int iuse::fungicide( player *p, item *it, bool, const tripoint & ) if( here.passable( dest ) && x_in_y( spore_count, 8 ) ) { if( monster *const mon_ptr = g->critter_at( dest ) ) { monster &critter = *mon_ptr; - if( g->u.sees( dest ) && + if( get_player_character().sees( dest ) && !critter.type->in_species( species_FUNGUS ) ) { add_msg( m_warning, _( "The %s is covered in tiny spores!" ), critter.name() ); @@ -2031,14 +2031,15 @@ int iuse::extinguisher( player *p, item *it, bool, const tripoint & ) blind = true; critter.add_effect( effect_blind, rng( 1_minutes, 2_minutes ) ); } - if( g->u.sees( critter ) ) { + Character &player_character = get_player_character(); + if( player_character.sees( critter ) ) { p->add_msg_if_player( _( "The %s is sprayed!" ), critter.name() ); if( blind ) { p->add_msg_if_player( _( "The %s looks blinded." ), critter.name() ); } } if( critter.made_of( phase_id::LIQUID ) ) { - if( g->u.sees( critter ) ) { + if( player_character.sees( critter ) ) { p->add_msg_if_player( _( "The %s is frozen!" ), critter.name() ); } critter.apply_damage( p, bodypart_id( "torso" ), rng( 20, 60 ) ); @@ -2410,8 +2411,9 @@ int iuse::hammer( player *p, item *it, bool, const tripoint & ) }; map &here = get_map(); - const std::function f = [&allowed_ter_id, &here]( const tripoint & pnt ) { - if( pnt == g->u.pos() ) { + const std::function f = + [&allowed_ter_id, &here, p]( const tripoint & pnt ) { + if( pnt == p->pos() ) { return false; } const ter_id ter = here.ter( pnt ); @@ -2475,9 +2477,9 @@ int iuse::crowbar( player *p, item *it, bool, const tripoint &pos ) }; map &here = get_map(); - const std::function f = [&allowed_ter_id, - &allowed_furn_id, &here]( const tripoint & pnt ) { - if( pnt == g->u.pos() ) { + const std::function f = + [&allowed_ter_id, &allowed_furn_id, &here, p]( const tripoint & pnt ) { + if( pnt == p->pos() ) { return false; } const ter_id ter = here.ter( pnt ); @@ -2854,28 +2856,28 @@ int iuse::dig( player *p, item *it, bool t, const tripoint & ) } if( grave ) { - if( g->u.has_trait( trait_SPIRITUAL ) && !g->u.has_trait( trait_PSYCHOPATH ) && - g->u.query_yn( _( "Would you really touch the sacred resting place of the dead?" ) ) ) { + if( p->has_trait( trait_SPIRITUAL ) && !p->has_trait( trait_PSYCHOPATH ) && + p->query_yn( _( "Would you really touch the sacred resting place of the dead?" ) ) ) { add_msg( m_info, _( "Exhuming a grave is really against your beliefs." ) ); - g->u.add_morale( MORALE_GRAVEDIGGER, -50, -100, 48_hours, 12_hours ); + p->add_morale( MORALE_GRAVEDIGGER, -50, -100, 48_hours, 12_hours ); if( one_in( 3 ) ) { - g->u.vomit(); + p->vomit(); } - } else if( g->u.has_trait( trait_PSYCHOPATH ) ) { + } else if( p->has_trait( trait_PSYCHOPATH ) ) { p->add_msg_if_player( m_good, _( "Exhuming a grave is fun now, where there is no one to object." ) ); - g->u.add_morale( MORALE_GRAVEDIGGER, 25, 50, 2_hours, 1_hours ); - } else if( !g->u.has_trait( trait_EATDEAD ) && - !g->u.has_trait( trait_SAPROVORE ) ) { + p->add_morale( MORALE_GRAVEDIGGER, 25, 50, 2_hours, 1_hours ); + } else if( !p->has_trait( trait_EATDEAD ) && + !p->has_trait( trait_SAPROVORE ) ) { p->add_msg_if_player( m_bad, _( "Exhuming this grave is utterly disgusting!" ) ); - g->u.add_morale( MORALE_GRAVEDIGGER, -25, -50, 2_hours, 1_hours ); + p->add_morale( MORALE_GRAVEDIGGER, -25, -50, 2_hours, 1_hours ); if( one_in( 5 ) ) { p->vomit(); } } } - const std::vector helpers = g->u.get_crafting_helpers(); + const std::vector helpers = p->get_crafting_helpers(); for( const npc *np : helpers ) { add_msg( m_info, _( "%s helps with this task…" ), np->name ); break; @@ -2982,8 +2984,9 @@ int iuse::fill_pit( player *p, item *it, bool t, const tripoint & ) }; map &here = get_map(); - const std::function f = [&allowed_ter_id, &here]( const tripoint & pnt ) { - if( pnt == g->u.pos() ) { + const std::function f = + [&allowed_ter_id, &here, p]( const tripoint & pnt ) { + if( pnt == p->pos() ) { return false; } const ter_id type = here.ter( pnt ); @@ -3017,7 +3020,7 @@ int iuse::fill_pit( player *p, item *it, bool t, const tripoint & ) } else { return 0; } - const std::vector helpers = g->u.get_crafting_helpers(); + const std::vector helpers = p->get_crafting_helpers(); const int helpersize = p->get_num_crafting_helpers( 3 ); moves = moves * ( 1 - ( helpersize / 10 ) ); for( const npc *np : helpers ) { @@ -3059,7 +3062,7 @@ int iuse::clear_rubble( player *p, item *it, bool, const tripoint & ) } int bonus = std::max( it->get_quality( quality_id( "DIG" ) ) - 1, 1 ); - const std::vector helpers = g->u.get_crafting_helpers(); + const std::vector helpers = p->get_crafting_helpers(); for( const npc *np : helpers ) { add_msg( m_info, _( "%s helps with this task…" ), np->name ); break; @@ -3368,7 +3371,7 @@ int iuse::jackhammer( player *p, item *it, bool, const tripoint &pos ) moves /= 2; } - const std::vector helpers = g->u.get_crafting_helpers(); + const std::vector helpers = p->get_crafting_helpers(); const int helpersize = p->get_num_crafting_helpers( 3 ); moves *= ( 1 - ( helpersize / 10 ) ); for( const npc *np : helpers ) { @@ -3466,7 +3469,7 @@ int iuse::pickaxe( player *p, item *it, bool, const tripoint &pos ) moves /= 2; } - const std::vector helpers = g->u.get_crafting_helpers(); + const std::vector helpers = p->get_crafting_helpers(); const int helpersize = p->get_num_crafting_helpers( 3 ); moves *= ( 1 - ( helpersize / 10 ) ); for( const npc *np : helpers ) { @@ -3586,7 +3589,7 @@ int iuse::geiger( player *p, item *it, bool t, const tripoint &pos ) return 0; } const tripoint &pnt = *pnt_; - if( pnt == g->u.pos() ) { + if( pnt == p->pos() ) { p->add_msg_if_player( m_info, _( "Your radiation level: %d mSv (%d mSv from items)" ), p->get_rad(), p->leak_level( "RADIOACTIVE" ) ); break; @@ -3648,9 +3651,10 @@ int iuse::can_goo( player *p, item *it, bool, const tripoint & ) if( tries == 10 ) { return 0; } + Character &player_character = get_player_character(); if( monster *const mon_ptr = g->critter_at( goop ) ) { monster &critter = *mon_ptr; - if( g->u.sees( goop ) ) { + if( player_character.sees( goop ) ) { add_msg( _( "Black goo emerges from the canister and envelopes a %s!" ), critter.name() ); } @@ -3659,7 +3663,7 @@ int iuse::can_goo( player *p, item *it, bool, const tripoint & ) critter.set_speed_base( critter.get_speed_base() - rng( 5, 25 ) ); critter.set_hp( critter.get_speed() ); } else { - if( g->u.sees( goop ) ) { + if( player_character.sees( goop ) ) { add_msg( _( "Living black goo emerges from the canister!" ) ); } if( monster *const goo = g->place_critter_at( mon_blob, goop ) ) { @@ -3676,7 +3680,7 @@ int iuse::can_goo( player *p, item *it, bool, const tripoint & ) found = here.passable( goop ) && here.tr_at( goop ).is_null(); } while( !found && tries < 10 ); if( found ) { - if( g->u.sees( goop ) ) { + if( player_character.sees( goop ) ) { add_msg( m_warning, _( "A nearby splatter of goo forms into a goo pit." ) ); } here.trap_set( goop, tr_goo ); @@ -3717,6 +3721,7 @@ int iuse::granade_act( player *p, item *it, bool t, const tripoint &pos ) auto modified_stat = current_stat + modify_by; current_stat = std::max( current_stat, std::min( 15, modified_stat ) ); }; + avatar &player_character = get_avatar(); switch( effect_roll ) { case 1: sounds::sound( pos, 100, sounds::sound_t::electronic_speech, _( "BUGFIXES!" ), @@ -3749,21 +3754,22 @@ int iuse::granade_act( player *p, item *it, bool t, const tripoint &pos ) buff_stat( person->int_max, rng( 0, person->int_max / 2 ) ); /** @EFFECT_PER_MAX increases possible granade per buff for NPCs */ buff_stat( person->per_max, rng( 0, person->per_max / 2 ) ); - } else if( g->u.pos() == dest ) { + } else if( player_character.pos() == dest ) { /** @EFFECT_STR_MAX increases possible granade str buff */ - buff_stat( g->u.str_max, rng( 0, g->u.str_max / 2 ) ); + buff_stat( player_character.str_max, rng( 0, player_character.str_max / 2 ) ); /** @EFFECT_DEX_MAX increases possible granade dex buff */ - buff_stat( g->u.dex_max, rng( 0, g->u.dex_max / 2 ) ); + buff_stat( player_character.dex_max, rng( 0, player_character.dex_max / 2 ) ); /** @EFFECT_INT_MAX increases possible granade int buff */ - buff_stat( g->u.int_max, rng( 0, g->u.int_max / 2 ) ); + buff_stat( player_character.int_max, rng( 0, player_character.int_max / 2 ) ); /** @EFFECT_PER_MAX increases possible granade per buff */ - buff_stat( g->u.per_max, rng( 0, g->u.per_max / 2 ) ); - g->u.recalc_hp(); - for( const bodypart_id &bp : g->u.get_all_body_parts() ) { - g->u.set_part_hp_cur( bp, g->u.get_part_hp_cur( bp ) * rng_float( 1, 1.2 ) ); - const int hp_max = g->u.get_part_hp_max( bp ); - if( g->u.get_part_hp_cur( bp ) > hp_max ) { - g->u.set_part_hp_cur( bp, hp_max ); + buff_stat( player_character.per_max, rng( 0, player_character.per_max / 2 ) ); + player_character.recalc_hp(); + for( const bodypart_id &bp : player_character.get_all_body_parts() ) { + player_character.set_part_hp_cur( bp, player_character.get_part_hp_cur( bp ) * rng_float( 1, + 1.2 ) ); + const int hp_max = player_character.get_part_hp_max( bp ); + if( player_character.get_part_hp_cur( bp ) > hp_max ) { + player_character.set_part_hp_cur( bp, hp_max ); } } } @@ -3789,20 +3795,20 @@ int iuse::granade_act( player *p, item *it, bool t, const tripoint &pos ) person->int_max -= rng( 0, person->int_max / 2 ); /** @EFFECT_PER_MAX increases possible granade per debuff for NPCs (NEGATIVE) */ person->per_max -= rng( 0, person->per_max / 2 ); - } else if( g->u.pos() == dest ) { + } else if( player_character.pos() == dest ) { /** @EFFECT_STR_MAX increases possible granade str debuff (NEGATIVE) */ - g->u.str_max -= rng( 0, g->u.str_max / 2 ); + player_character.str_max -= rng( 0, player_character.str_max / 2 ); /** @EFFECT_DEX_MAX increases possible granade dex debuff (NEGATIVE) */ - g->u.dex_max -= rng( 0, g->u.dex_max / 2 ); + player_character.dex_max -= rng( 0, player_character.dex_max / 2 ); /** @EFFECT_INT_MAX increases possible granade int debuff (NEGATIVE) */ - g->u.int_max -= rng( 0, g->u.int_max / 2 ); + player_character.int_max -= rng( 0, player_character.int_max / 2 ); /** @EFFECT_PER_MAX increases possible granade per debuff (NEGATIVE) */ - g->u.per_max -= rng( 0, g->u.per_max / 2 ); - g->u.recalc_hp(); - for( const bodypart_id &bp : g->u.get_all_body_parts() ) { - const int hp_cur = g->u.get_part_hp_cur( bp ); + player_character.per_max -= rng( 0, player_character.per_max / 2 ); + player_character.recalc_hp(); + for( const bodypart_id &bp : player_character.get_all_body_parts() ) { + const int hp_cur = player_character.get_part_hp_cur( bp ); if( hp_cur > 0 ) { - g->u.set_part_hp_cur( bp, rng( 1, hp_cur ) ); + player_character.set_part_hp_cur( bp, rng( 1, hp_cur ) ); } } } @@ -3821,9 +3827,9 @@ int iuse::granade_act( player *p, item *it, bool t, const tripoint &pos ) critter.clear_effects(); } else if( npc *const person = g->critter_at( dest ) ) { person->environmental_revert_effect(); - } else if( g->u.pos() == dest ) { - g->u.environmental_revert_effect(); - do_purify( g->u ); + } else if( player_character.pos() == dest ) { + player_character.environmental_revert_effect(); + do_purify( player_character ); } } break; @@ -4074,7 +4080,7 @@ int iuse::pheromone( player *p, item *it, bool, const tripoint &pos ) } } - if( g->u.sees( *p ) ) { + if( get_player_character().sees( *p ) ) { if( converts == 0 ) { add_msg( _( "…but nothing happens." ) ); } else if( converts == 1 ) { @@ -4749,7 +4755,7 @@ int iuse::dog_whistle( player *p, item *it, bool, const tripoint & ) p->add_msg_if_player( _( "You blow your dog whistle." ) ); for( monster &critter : g->all_monsters() ) { if( critter.friendly != 0 && critter.has_flag( MF_DOGFOOD ) ) { - bool u_see = g->u.sees( critter ); + bool u_see = get_player_character().sees( critter ); if( critter.has_effect( effect_docile ) ) { if( u_see ) { p->add_msg_if_player( _( "Your %s looks ready to attack." ), critter.name() ); @@ -4959,8 +4965,8 @@ int iuse::chop_tree( player *p, item *it, bool t, const tripoint & ) return 0; } map &here = get_map(); - const std::function f = [&here]( const tripoint & pnt ) { - if( pnt == g->u.pos() ) { + const std::function f = [&here, p]( const tripoint & pnt ) { + if( pnt == p->pos() ) { return false; } return here.has_flag( "TREE", pnt ); @@ -4981,7 +4987,7 @@ int iuse::chop_tree( player *p, item *it, bool t, const tripoint & ) return 0; } int moves = chop_moves( p, it ); - const std::vector helpers = g->u.get_crafting_helpers(); + const std::vector helpers = p->get_crafting_helpers(); for( const npc *np : helpers ) { add_msg( m_info, _( "%s helps with this task…" ), np->name ); break; @@ -5025,7 +5031,7 @@ int iuse::chop_logs( player *p, item *it, bool t, const tripoint & ) } int moves = chop_moves( p, it ); - const std::vector helpers = g->u.get_crafting_helpers(); + const std::vector helpers = p->get_crafting_helpers(); for( const npc *np : helpers ) { add_msg( m_info, _( "%s helps with this task…" ), np->name ); break; @@ -5074,9 +5080,9 @@ int iuse::oxytorch( player *p, item *it, bool, const tripoint & ) }; map &here = get_map(); - const std::function f = [&allowed_ter_id, - &allowed_furn_id, &here]( const tripoint & pnt ) { - if( pnt == g->u.pos() ) { + const std::function f = + [&allowed_ter_id, &allowed_furn_id, &here, p]( const tripoint & pnt ) { + if( pnt == p->pos() ) { return false; } const ter_id ter = here.ter( pnt ); @@ -5165,9 +5171,9 @@ int iuse::hacksaw( player *p, item *it, bool t, const tripoint & ) f_rack }; map &here = get_map(); - const std::function f = [&allowed_ter_id, - &allowed_furn_id, &here]( const tripoint & pnt ) { - if( pnt == g->u.pos() ) { + const std::function f = + [&allowed_ter_id, &allowed_furn_id, &here, p]( const tripoint & pnt ) { + if( pnt == p->pos() ) { return false; } const ter_id ter = here.ter( pnt ); @@ -5227,8 +5233,9 @@ int iuse::boltcutters( player *p, item *it, bool, const tripoint & ) t_chainfence }; map &here = get_map(); - const std::function f = [&allowed_ter_id, &here]( const tripoint & pnt ) { - if( pnt == g->u.pos() ) { + const std::function f = + [&allowed_ter_id, &here, p]( const tripoint & pnt ) { + if( pnt == p->pos() ) { return false; } const ter_id ter = here.ter( pnt ); @@ -5448,7 +5455,7 @@ int iuse::artifact( player *p, item *it, bool, const tripoint & ) bool blood = false; for( const tripoint &tmp : here.points_in_radius( p->pos(), 4 ) ) { if( !one_in( 4 ) && here.add_field( tmp, fd_blood, 3 ) && - ( blood || g->u.sees( tmp ) ) ) { + ( blood || get_player_character().sees( tmp ) ) ) { blood = true; } } @@ -6123,7 +6130,8 @@ int iuse::gun_repair( player *p, item *it, bool, const tripoint & ) p->add_msg_if_player( m_info, _( "You need a mechanics skill of 2 to use this repair kit." ) ); return 0; } - item_location loc = game_menus::inv::titled_menu( g->u, ( "Select the firearm to repair" ) ); + item_location loc = game_menus::inv::titled_menu( get_avatar(), + ( "Select the firearm to repair" ) ); if( !loc ) { p->add_msg_if_player( m_info, _( "You do not have that item!" ) ); return 0; @@ -7417,7 +7425,7 @@ static extended_photo_def photo_def_for_camera_point( const tripoint &aim_point, Creature *creature; if( mon && mon->has_effect( effect_ridden ) ) { // only player can ride, see monexamine::mount_pet - guy = &g->u; + guy = &get_avatar(); description_figures_appearance[ mon->name() ] = "\"" + mon->type->get_description() + "\""; } @@ -8540,7 +8548,8 @@ int iuse::remoteveh( player *p, item *it, bool t, const tripoint &pos ) return 0; } - point p2( g->u.view_offset.xy() ); + avatar &player_character = get_avatar(); + point p2( player_character.view_offset.xy() ); vehicle *veh = pickveh( pos, choice == 0 ); @@ -8553,7 +8562,7 @@ int iuse::remoteveh( player *p, item *it, bool t, const tripoint &pos ) } if( choice == 0 ) { - if( g->u.has_trait( trait_WAYFARER ) ) { + if( p->has_trait( trait_WAYFARER ) ) { add_msg( m_info, _( "Despite using a controller, you still refuse to take control of this vehicle." ) ); } else { @@ -8574,8 +8583,8 @@ int iuse::remoteveh( player *p, item *it, bool t, const tripoint &pos ) } } - g->u.view_offset.x = p2.x; - g->u.view_offset.y = p2.y; + player_character.view_offset.x = p2.x; + player_character.view_offset.y = p2.y; return it->type->charges_to_use(); } @@ -8895,7 +8904,7 @@ int iuse::multicooker( player *p, item *it, bool t, const tripoint &pos ) std::vector dishes; - inventory crafting_inv = g->u.crafting_inventory(); + inventory crafting_inv = p->crafting_inventory(); //add some tools and qualities. we can't add this qualities to json, because multicook must be used only by activating, not as component other crafts. crafting_inv.push_back( item( "hotplate", 0 ) ); //hotplate inside crafting_inv.push_back( item( "tongs", 0 ) ); //some recipes requires tongs @@ -8904,7 +8913,7 @@ int iuse::multicooker( player *p, item *it, bool t, const tripoint &pos ) int counter = 0; - for( const auto &r : g->u.get_learned_recipes().in_category( "CC_FOOD" ) ) { + for( const auto &r : get_avatar().get_learned_recipes().in_category( "CC_FOOD" ) ) { if( multicooked_subcats.count( r->subcategory ) > 0 ) { dishes.push_back( r ); const bool can_make = r->deduped_requirements().can_make_with_inventory( @@ -8976,7 +8985,7 @@ int iuse::multicooker( player *p, item *it, bool t, const tripoint &pos ) bool has_tools = true; - const inventory &cinv = g->u.crafting_inventory(); + const inventory &cinv = p->crafting_inventory(); if( !cinv.has_amount( itype_soldering_iron, 1 ) ) { p->add_msg_if_player( m_warning, _( "You need a %s." ), @@ -9443,7 +9452,7 @@ int iuse::weather_tool( player *p, item *it, bool, const tripoint & ) const w_point weatherPoint = *g->weather.weather_precise; /* Possibly used twice. Worth spending the time to precalculate. */ - const auto player_local_temp = g->weather.get_temperature( g->u.pos() ); + const auto player_local_temp = g->weather.get_temperature( p->pos() ); if( it->typeId() == itype_weather_reader ) { p->add_msg_if_player( m_neutral, _( "The %s's monitor slowly outputs the data…" ), @@ -9463,12 +9472,12 @@ int iuse::weather_tool( player *p, item *it, bool, const tripoint & ) p->add_msg_if_player( m_neutral, _( "The %1$s reads %2$s." ), it->tname(), print_humidity( get_local_humidity( weatherPoint.humidity, g->weather.weather, - g->is_sheltered( g->u.pos() ) ) ) ); + g->is_sheltered( p->pos() ) ) ) ); } else { p->add_msg_if_player( m_neutral, _( "Relative Humidity: %s." ), print_humidity( get_local_humidity( weatherPoint.humidity, g->weather.weather, - g->is_sheltered( g->u.pos() ) ) ) ); + g->is_sheltered( p->pos() ) ) ) ); } } if( it->has_flag( "BAROMETER" ) ) { @@ -9858,7 +9867,7 @@ int iuse::wash_items( player *p, bool soft_items, bool hard_items ) required.cleanser ); return 0; } - const std::vector helpers = g->u.get_crafting_helpers(); + const std::vector helpers = p->get_crafting_helpers(); const int helpersize = p->get_num_crafting_helpers( 3 ); required.time = required.time * ( 1 - ( helpersize / 10 ) ); for( const npc *np : helpers ) { From ffad17b229b99b6832de69464f5761973d836741 Mon Sep 17 00:00:00 2001 From: "U-N\\N" Date: Thu, 2 Jul 2020 09:58:07 +0200 Subject: [PATCH 040/420] restore codes --- src/vehicle_use.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/vehicle_use.cpp b/src/vehicle_use.cpp index e18433533905c..c544a874ea96d 100644 --- a/src/vehicle_use.cpp +++ b/src/vehicle_use.cpp @@ -2015,6 +2015,9 @@ void vehicle::interact_with( const tripoint &pos, int interact_part ) } auto veh_tool = [&]( const itype_id & obj ) { item pseudo( obj ); + if( fuel_left( itype_battery, true ) < pseudo.ammo_required() ) { + return false; + } //Pseudo items don't have a magazine in it, and they don't need it anymore. //Pseudo magazine in Pseudo item sounds good. //Somehow the set of available ammos in pocket_data loaded from json is alphabetic, From 1c69950540d5f33cd904a998c6e257b0409cde27 Mon Sep 17 00:00:00 2001 From: johnrdconnolly <59102789+johnrdconnolly@users.noreply.github.com> Date: Thu, 2 Jul 2020 08:11:23 -0700 Subject: [PATCH 041/420] beans and rice fix (#41662) * deluxe beand and rice fix * beans and rice fix updated beans and rice weights and calorie values for items that contain beans and/or rice for veggy_dishes.json * Update veggy_dishes.json mulptiple comments fix --- data/json/items/comestibles/veggy_dishes.json | 31 ++++++++++--------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/data/json/items/comestibles/veggy_dishes.json b/data/json/items/comestibles/veggy_dishes.json index a21d87f3eaecb..8dc38afd3aca8 100644 --- a/data/json/items/comestibles/veggy_dishes.json +++ b/data/json/items/comestibles/veggy_dishes.json @@ -224,7 +224,8 @@ "type": "COMESTIBLE", "id": "deluxe_veggy_beans", "name": { "str_sp": "vegetarian baked beans" }, - "weight": "270 g", + "weight": "350 g", + "//": "real world measurements: 250ml of cooked beans is 240g with 316 calores, and 250ml of veggies is ~100g with ~100 calories", "color": "brown", "spoils_in": "15 days", "comestible_type": "FOOD", @@ -237,7 +238,6 @@ "price_postapoc": 100, "material": [ "veggy", "bean" ], "volume": "500 ml", - "//": "one beans + one veggy", "flags": [ "EATEN_HOT" ], "fun": 4 }, @@ -245,14 +245,15 @@ "type": "COMESTIBLE", "id": "dry_rice", "name": { "str_sp": "dried rice" }, - "weight": "40 g", + "weight": "200 g", + "//": "real world measurements: 250ml of dry rice weighs 200g and has 720 calories", "color": "white", "spoils_in": "360 days", "container": "bag_plastic", "comestible_type": "FOOD", "symbol": "%", "quench": -6, - "calories": 222, + "calories": 720, "description": "Dehydrated long-grain rice. Tasty and nutritious when cooked, virtually inedible when dry.", "price": 500, "price_postapoc": 300, @@ -270,17 +271,17 @@ "id": "rice_cooked", "name": { "str_sp": "cooked rice" }, "copy-from": "dry_rice", - "weight": "62 g", + "//": "real world measurements: 250ml of cooked rice weighs 200g and has 260 calores, rice roughly triples when cooked", "color": "light_gray", "spoils_in": "10 days", "quench": 0, + "calories": 260, "healthy": 1, "description": "A hearty serving of cooked long-grain white rice.", "price": 100, "price_postapoc": 50, "flags": [ "EATEN_HOT", "FREEZERBURN" ], "fun": 2, - "//": "rice triples in size when cooked", "charges": 1, "vitamins": [ [ "vitA", 1 ], [ "iron", 11 ] ] }, @@ -288,7 +289,8 @@ "type": "COMESTIBLE", "id": "deluxe_veggy_rice", "name": { "str_sp": "fried rice" }, - "weight": "135 g", + "weight": "300 g", + "//": "real world measurements: 250ml cooked rice is 200g with 260 calores, 250mL of veggies is ~100g with ~100 calories", "color": "yellow", "spoils_in": "15 days", "comestible_type": "FOOD", @@ -299,7 +301,7 @@ "price": 700, "price_postapoc": 150, "material": [ "veggy" ], - "volume": "250 ml", + "volume": "500 ml", "flags": [ "EATEN_HOT", "FREEZERBURN" ], "fun": 5, "vitamins": [ [ "vitA", 2 ], [ "vitC", 4 ], [ "calcium", 3 ], [ "iron", 3 ] ] @@ -308,18 +310,19 @@ "type": "COMESTIBLE", "id": "beansnrice", "name": { "str_sp": "beans and rice" }, - "weight": "70 g", + "weight": "440 g", + "//": "real world measurements: 250ml of cooked beans weighs 240g wiht 316 calories, 250ml of cooked rice weighs 200g with 260 calories", "color": "light_gray", "spoils_in": "10 days", "comestible_type": "FOOD", "symbol": "%", "healthy": 1, - "calories": 425, + "calories": 566, "description": "A serving of beans and rice that has been cooked together. Delicious and healthy!", "price": 500, "price_postapoc": 150, "material": [ "veggy", "bean" ], - "volume": "250 ml", + "volume": "500 ml", "flags": [ "EATEN_HOT", "FREEZERBURN" ], "fun": 2, "vitamins": [ [ "vitC", 14 ], [ "calcium", 4 ], [ "iron", 16 ] ] @@ -369,19 +372,19 @@ "type": "COMESTIBLE", "id": "deluxe_veggy_beansnrice", "name": { "str_sp": "deluxe vegetarian beans and rice" }, - "weight": "140 g", + "weight": "540 g", + "//": "real world measurements: 250 mL of cooked beans is 240g w 316 calories, 250ml cooked rice is 200g w 260 calories, and 250mL of veggies is ~100g w ~100 calories", "color": "brown", "spoils_in": "15 days", "comestible_type": "FOOD", "symbol": "%", "healthy": 1, - "calories": 459, + "calories": 660, "description": "Slow-cooked beans and rice with vegetables and seasonings. Tasty and very filling.", "price": 750, "price_postapoc": 200, "material": [ "veggy", "bean" ], "volume": "750 ml", - "//": "one bean + one rice + one veggy", "flags": [ "EATEN_HOT", "FREEZERBURN" ], "fun": 4, "vitamins": [ [ "vitC", 28 ], [ "calcium", 8 ], [ "iron", 32 ] ] From 4cddc88fcfa5477cb1ebc1ca5f9de7d594d4e4a7 Mon Sep 17 00:00:00 2001 From: Matias Fito Date: Wed, 1 Jul 2020 00:10:02 -0300 Subject: [PATCH 042/420] Fix player can wield butane Add missing phase: liquid to butane ammo. Add watertight to lighter magazine to hold butane. Fixes #41682 --- data/json/items/ammo.json | 1 + data/json/items/tool/fire.json | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/data/json/items/ammo.json b/data/json/items/ammo.json index a4f7924cc0974..c26e2276baed8 100644 --- a/data/json/items/ammo.json +++ b/data/json/items/ammo.json @@ -29,6 +29,7 @@ "weight": "1 mg", "color": "white", "flags": [ "TRADER_AVOID" ], + "phase": "liquid", "ammo_type": "butane" }, { diff --git a/data/json/items/tool/fire.json b/data/json/items/tool/fire.json index 142937578c0f3..3953de2e7f6fe 100644 --- a/data/json/items/tool/fire.json +++ b/data/json/items/tool/fire.json @@ -104,7 +104,7 @@ "rand_charges": [ 1, 10, 12, 15, 16, 22, 44, 50, 67, 75, 82, 100 ], "max_charges": 100, "charges_per_use": 1, - "pocket_data": [ { "pocket_type": "MAGAZINE", "ammo_restriction": { "butane": 100 } } ], + "pocket_data": [ { "pocket_type": "MAGAZINE", "ammo_restriction": { "butane": 100 }, "watertight": true } ], "use_action": { "type": "firestarter", "moves": 50 }, "flags": [ "FIRESTARTER", "NO_UNLOAD" ] }, From 3003f1755d42b93da8bf23e79f2f1d877665015f Mon Sep 17 00:00:00 2001 From: Sergey Alirzaev Date: Thu, 2 Jul 2020 19:06:48 +0300 Subject: [PATCH 043/420] Make water purification tablets be made of powder fixes https://github.com/CleverRaven/Cataclysm-DDA/issues/41725 see https://github.com/CleverRaven/Cataclysm-DDA/pull/41608 --- data/json/items/chemicals_and_resources.json | 1 + 1 file changed, 1 insertion(+) diff --git a/data/json/items/chemicals_and_resources.json b/data/json/items/chemicals_and_resources.json index adfcf09242462..3b83ab8d64e7a 100644 --- a/data/json/items/chemicals_and_resources.json +++ b/data/json/items/chemicals_and_resources.json @@ -1020,6 +1020,7 @@ "id": "pur_tablets", "name": "water purification tablet", "category": "chems", + "material": [ "powder" ], "weight": "1 g", "color": "white", "use_action": [ "WATER_PURIFIER" ], From 43e80978a468798ed3a692dbec5b2053074b9e47 Mon Sep 17 00:00:00 2001 From: ZhilkinSerg Date: Thu, 2 Jul 2020 23:56:55 +0300 Subject: [PATCH 044/420] Move fire field processing to a separate function Will need refactoring later --- src/map.h | 2 + src/map_field.cpp | 973 +++++++++++++++++++++++----------------------- 2 files changed, 496 insertions(+), 479 deletions(-) diff --git a/src/map.h b/src/map.h index ccb53bfa0fd6f..cd0a9502e8ede 100644 --- a/src/map.h +++ b/src/map.h @@ -1161,6 +1161,8 @@ class map // See fields.cpp bool process_fields(); bool process_fields_in_submap( submap *current_submap, const tripoint &submap_pos ); + bool process_fire_field_in_submap( submap *current_submap, field_entry &cur, const tripoint &p, + bool &dirty_transparency_cache ); /** * Apply field effects to the creature when it's on a square with fields. */ diff --git a/src/map_field.cpp b/src/map_field.cpp index bce1580c891ec..79e3c017fbc2d 100644 --- a/src/map_field.cpp +++ b/src/map_field.cpp @@ -454,7 +454,6 @@ bool map::process_fields_in_submap( submap *const current_submap, cur.set_field_intensity( cur.get_field_intensity() + 1 ); } - int part; const ter_t &ter = map_tile.get_ter_t(); // Dissipate faster in water if( ter.has_flag( TFLAG_SWIMMABLE ) ) { @@ -501,484 +500,8 @@ bool map::process_fields_in_submap( submap *const current_submap, sblk.apply_slime( p, cur.get_field_intensity() * curtype.obj().apply_slime_factor ); } if( curtype == fd_fire ) { - cur.set_field_age( std::max( -24_hours, cur.get_field_age() ) ); - // Entire objects for ter/frn for flags - const oter_id &cur_om_ter = overmap_buffer.ter( ms_to_omt_copy( here.getabs( p ) ) ); - bool sheltered = g->is_sheltered( p ); - int winddirection = g->weather.winddirection; - int windpower = get_local_windpower( g->weather.windspeed, cur_om_ter, p, winddirection, - sheltered ); - const ter_t &ter = map_tile.get_ter_t(); - const furn_t &frn = map_tile.get_furn_t(); - - // We've got ter/furn cached, so let's use that - const bool is_sealed = ter_furn_has_flag( ter, frn, TFLAG_SEALED ) && - !ter_furn_has_flag( ter, frn, TFLAG_ALLOW_FIELD_EFFECT ); - // Smoke generation probability, consumed items count - int smoke = 0; - int consumed = 0; - // How much time to add to the fire's life due to burned items/terrain/furniture - time_duration time_added = 0_turns; - // Checks if the fire can spread - const bool can_spread = !ter_furn_has_flag( ter, frn, TFLAG_FIRE_CONTAINER ); - // If the flames are in furniture with fire_container flag like brazier or oven, - // they're fully contained, so skip consuming terrain - const bool can_burn = ( ter.is_flammable() || frn.is_flammable() ) && - !ter_furn_has_flag( ter, frn, TFLAG_FIRE_CONTAINER ); - // The huge indent below should probably be somehow moved away from here - // without forcing the function to use i_at( p ) for fires without items - if( !is_sealed && map_tile.get_item_count() > 0 ) { - map_stack items_here = i_at( p ); - std::vector new_content; - for( auto it = items_here.begin(); it != items_here.end(); ) { - if( it->will_explode_in_fire() ) { - // We need to make a copy because the iterator validity is not predictable - item copy = *it; - it = items_here.erase( it ); - if( copy.detonate( p, new_content ) ) { - // Need to restart, iterators may not be valid - it = items_here.begin(); - } - } else { - ++it; - } - } - - fire_data frd( cur.get_field_intensity(), !can_spread ); - // The highest # of items this fire can remove in one turn - int max_consume = cur.get_field_intensity() * 2; - - for( auto fuel = items_here.begin(); fuel != items_here.end() && consumed < max_consume; ) { - // `item::burn` modifies the charges in order to simulate some of them getting - // destroyed by the fire, this changes the item weight, but may not actually - // destroy it. We need to spawn products anyway. - const units::mass old_weight = fuel->weight( false ); - bool destroyed = fuel->burn( frd ); - // If the item is considered destroyed, it may have negative charge count, - // see `item::burn?. This in turn means `item::weight` returns a negative value, - // which we can not use, so only call `weight` when it's still an existing item. - const units::mass new_weight = destroyed ? 0_gram : fuel->weight( false ); - if( old_weight != new_weight ) { - create_burnproducts( p, *fuel, old_weight - new_weight ); - } - - if( destroyed ) { - // If we decided the item was destroyed by fire, remove it. - // But remember its contents, except for irremovable mods, if any - const std::list content_list = fuel->contents.all_items_top(); - for( item *it : content_list ) { - if( !it->is_irremovable() ) { - new_content.push_back( item( *it ) ); - } - } - fuel = items_here.erase( fuel ); - consumed++; - } else { - ++fuel; - } - } - - spawn_items( p, new_content ); - smoke = roll_remainder( frd.smoke_produced ); - time_added = 1_turns * roll_remainder( frd.fuel_produced ); - } - - // Get the part of the vehicle in the fire (_internal skips the boundary check) - vehicle *veh = veh_at_internal( p, part ); - if( veh != nullptr ) { - veh->damage( part, cur.get_field_intensity() * 10, DT_HEAT, true ); - // Damage the vehicle in the fire. - } - if( can_burn ) { - if( ter.has_flag( TFLAG_SWIMMABLE ) ) { - // Flames die quickly on water - cur.set_field_age( cur.get_field_age() + 4_minutes ); - } - - // Consume the terrain we're on - if( ter_furn_has_flag( ter, frn, TFLAG_FLAMMABLE ) ) { - // The fire feeds on the ground itself until max intensity. - time_added += 1_turns * ( 5 - cur.get_field_intensity() ); - smoke += 2; - smoke += static_cast( windpower / 5 ); - if( cur.get_field_intensity() > 1 && - one_in( 200 - cur.get_field_intensity() * 50 ) ) { - destroy( p, false ); - } - - } else if( ter_furn_has_flag( ter, frn, TFLAG_FLAMMABLE_HARD ) && - one_in( 3 ) ) { - // The fire feeds on the ground itself until max intensity. - time_added += 1_turns * ( 4 - cur.get_field_intensity() ); - smoke += 2; - smoke += static_cast( windpower / 5 ); - if( cur.get_field_intensity() > 1 && - one_in( 200 - cur.get_field_intensity() * 50 ) ) { - destroy( p, false ); - } - - } else if( ter.has_flag( TFLAG_FLAMMABLE_ASH ) ) { - // The fire feeds on the ground itself until max intensity. - time_added += 1_turns * ( 5 - cur.get_field_intensity() ); - smoke += 2; - smoke += static_cast( windpower / 5 ); - if( cur.get_field_intensity() > 1 && - one_in( 200 - cur.get_field_intensity() * 50 ) ) { - if( p.z > 0 ) { - // We're in the air - ter_set( p, t_open_air ); - } else { - ter_set( p, t_dirt ); - } - } - - } else if( frn.has_flag( TFLAG_FLAMMABLE_ASH ) ) { - // The fire feeds on the ground itself until max intensity. - time_added += 1_turns * ( 5 - cur.get_field_intensity() ); - smoke += 2; - smoke += static_cast( windpower / 5 ); - if( cur.get_field_intensity() > 1 && - one_in( 200 - cur.get_field_intensity() * 50 ) ) { - furn_set( p, f_ash ); - add_item_or_charges( p, item( "ash" ) ); - } - - } else if( ter.has_flag( TFLAG_NO_FLOOR ) && zlevels && p.z > -OVERMAP_DEPTH ) { - // We're hanging in the air - let's fall down - tripoint dst{ p.xy(), p.z - 1 }; - if( valid_move( p, dst, true, true ) ) { - maptile dst_tile = maptile_at_internal( dst ); - field_entry *fire_there = dst_tile.find_field( fd_fire ); - if( fire_there == nullptr ) { - dst_tile.add_field( fd_fire, 1, 0_turns ); - cur.set_field_intensity( cur.get_field_intensity() - 1 ); - } else { - // Don't fuel raging fires or they'll burn forever - // as they can produce small fires above themselves - int new_intensity = std::max( cur.get_field_intensity(), - fire_there->get_field_intensity() ); - // Allow smaller fires to combine - if( new_intensity < 3 && - cur.get_field_intensity() == fire_there->get_field_intensity() ) { - new_intensity++; - } - // A raging fire below us can support us for a while - // Otherwise decay and decay fast - if( fire_there->get_field_intensity() < 3 || one_in( 10 ) ) { - cur.set_field_intensity( cur.get_field_intensity() - 1 ); - } - fire_there->set_field_intensity( new_intensity ); - } - break; - } - } - } - // Lower age is a longer lasting fire - if( time_added != 0_turns ) { - cur.set_field_age( cur.get_field_age() - time_added ); - } else if( can_burn ) { - // Nothing to burn = fire should be dying out faster - // Drain more power from big fires, so that they stop raging over nothing - // Except for fires on stoves and fireplaces, those are made to keep the fire alive - cur.mod_field_age( 10_seconds * cur.get_field_intensity() ); - } - - // Below we will access our nearest 8 neighbors, so let's cache them now - // This should probably be done more globally, because large fires will re-do it a lot - auto neighs = get_neighbors( p ); - // Get the neighbours that are allowed due to wind direction - auto maptiles = get_wind_blockers( winddirection, p ); - maptile remove_tile = std::get<0>( maptiles ); - maptile remove_tile2 = std::get<1>( maptiles ); - maptile remove_tile3 = std::get<2>( maptiles ); - std::vector neighbour_vec; - size_t end_it = static_cast( rng( 0, neighs.size() - 1 ) ); - // Start at end_it + 1, then wrap around until all elements have been processed - for( size_t i = ( end_it + 1 ) % neighs.size(), count = 0; - count != neighs.size(); - i = ( i + 1 ) % neighs.size(), count++ ) { - const auto &neigh = neighs[i]; - if( ( neigh.pos_.x != remove_tile.pos_.x && neigh.pos_.y != remove_tile.pos_.y ) || - ( neigh.pos_.x != remove_tile2.pos_.x && neigh.pos_.y != remove_tile2.pos_.y ) || - ( neigh.pos_.x != remove_tile3.pos_.x && neigh.pos_.y != remove_tile3.pos_.y ) ) { - neighbour_vec.push_back( neigh ); - } else if( x_in_y( 1, std::max( 2, windpower ) ) ) { - neighbour_vec.push_back( neigh ); - } - } - // If the flames are in a pit, it can't spread to non-pit - const bool in_pit = ter.id.id() == t_pit; - - // Count adjacent fires, to optimize out needless smoke and hot air - int adjacent_fires = 0; - - // If the flames are big, they contribute to adjacent flames - if( can_spread ) { - if( cur.get_field_intensity() > 1 && one_in( 3 ) ) { - // Basically: Scan around for a spot, - // if there is more fire there, make it bigger and give it some fuel. - // This is how big fires spend their excess age: - // making other fires bigger. Flashpoint. - if( sheltered || windpower < 5 ) { - end_it = static_cast( rng( 0, neighs.size() - 1 ) ); - for( size_t i = ( end_it + 1 ) % neighs.size(), count = 0; - count != neighs.size() && cur.get_field_age() < 0_turns; - i = ( i + 1 ) % neighs.size(), count++ ) { - maptile &dst = neighs[i]; - auto dstfld = dst.find_field( fd_fire ); - // If the fire exists and is weaker than ours, boost it - if( dstfld != nullptr && - ( dstfld->get_field_intensity() <= cur.get_field_intensity() || - dstfld->get_field_age() > cur.get_field_age() ) && - ( in_pit == ( dst.get_ter() == t_pit ) ) ) { - if( dstfld->get_field_intensity() < 2 ) { - dstfld->set_field_intensity( dstfld->get_field_intensity() + 1 ); - } - - dstfld->set_field_age( dstfld->get_field_age() - 5_minutes ); - cur.set_field_age( cur.get_field_age() + 5_minutes ); - } - if( dstfld != nullptr ) { - adjacent_fires++; - } - } - } else { - end_it = static_cast( rng( 0, neighbour_vec.size() - 1 ) ); - for( size_t i = ( end_it + 1 ) % neighbour_vec.size(), count = 0; - count != neighbour_vec.size() && cur.get_field_age() < 0_turns; - i = ( i + 1 ) % neighbour_vec.size(), count++ ) { - maptile &dst = neighbour_vec[i]; - field_entry *dstfld = dst.find_field( fd_fire ); - // If the fire exists and is weaker than ours, boost it - if( dstfld != nullptr && - ( dstfld->get_field_intensity() <= cur.get_field_intensity() || - dstfld->get_field_age() > cur.get_field_age() ) && - ( in_pit == ( dst.get_ter() == t_pit ) ) ) { - if( dstfld->get_field_intensity() < 2 ) { - dstfld->set_field_intensity( dstfld->get_field_intensity() + 1 ); - } - - dstfld->set_field_age( dstfld->get_field_age() - 5_minutes ); - cur.set_field_age( cur.get_field_age() + 5_minutes ); - } - - if( dstfld != nullptr ) { - adjacent_fires++; - } - } - } - } else if( cur.get_field_age() < 0_turns && cur.get_field_intensity() < 3 ) { - // See if we can grow into a stage 2/3 fire, for this - // burning neighbors are necessary in addition to - // field age < 0, or alternatively, a LOT of fuel. - - // The maximum fire intensity is 1 for a lone fire, 2 for at least 1 neighbor, - // 3 for at least 2 neighbors. - int maximum_intensity = 1; - - // The following logic looks a bit complex due to optimization concerns, so here are the semantics: - // 1. Calculate maximum field intensity based on fuel, -50 minutes is 2(medium), -500 minutes is 3(raging) - // 2. Calculate maximum field intensity based on neighbors, 3 neighbors is 2(medium), 7 or more neighbors is 3(raging) - // 3. Pick the higher maximum between 1. and 2. - if( cur.get_field_age() < -500_minutes ) { - maximum_intensity = 3; - } else { - for( auto &neigh : neighs ) { - if( neigh.get_field().find_field( fd_fire ) != nullptr ) { - adjacent_fires++; - } - } - maximum_intensity = 1 + ( adjacent_fires >= 3 ) + ( adjacent_fires >= 7 ); - - if( maximum_intensity < 2 && cur.get_field_age() < -50_minutes ) { - maximum_intensity = 2; - } - } - - // If we consumed a lot, the flames grow higher - if( cur.get_field_intensity() < maximum_intensity && cur.get_field_age() < 0_turns ) { - // Fires under 0 age grow in size. Level 3 fires under 0 spread later on. - // Weaken the newly-grown fire - cur.set_field_intensity( cur.get_field_intensity() + 1 ); - cur.set_field_age( cur.get_field_age() + 10_minutes * cur.get_field_intensity() ); - } - } - } - // Consume adjacent fuel / terrain / webs to spread. - // Allow raging fires (and only raging fires) to spread up - // Spreading down is achieved by wrecking the walls/floor and then falling - if( zlevels && cur.get_field_intensity() == 3 && p.z < OVERMAP_HEIGHT ) { - // Let it burn through the floor - maptile dst = maptile_at_internal( {p.xy(), p.z + 1} ); - const auto &dst_ter = dst.get_ter_t(); - if( dst_ter.has_flag( TFLAG_NO_FLOOR ) || - dst_ter.has_flag( TFLAG_FLAMMABLE ) || - dst_ter.has_flag( TFLAG_FLAMMABLE_ASH ) || - dst_ter.has_flag( TFLAG_FLAMMABLE_HARD ) ) { - field_entry *nearfire = dst.find_field( fd_fire ); - if( nearfire != nullptr ) { - nearfire->mod_field_age( -2_turns ); - } else { - dst.add_field( fd_fire, 1, 0_turns ); - } - // Fueling fires above doesn't cost fuel - } - } - // Our iterator will start at end_i + 1 and increment from there and then wrap around. - // This guarantees it will check all neighbors, starting from a random one - if( sheltered || windpower < 5 ) { - const size_t end_i = static_cast( rng( 0, neighs.size() - 1 ) ); - for( size_t i = ( end_i + 1 ) % neighs.size(), count = 0; - count != neighs.size(); - i = ( i + 1 ) % neighs.size(), count++ ) { - if( one_in( cur.get_field_intensity() * 2 ) ) { - // Skip some processing to save on CPU - continue; - } - - maptile &dst = neighs[i]; - // No bounds checking here: we'll treat the invalid neighbors as valid. - // We're using the map tile wrapper, so we can treat invalid tiles as sentinels. - // This will create small oddities on map edges, but nothing more noticeable than - // "cut-off" that happens with bounds checks. - - field_entry *nearfire = dst.find_field( fd_fire ); - if( nearfire != nullptr ) { - // We handled supporting fires in the section above, no need to do it here - continue; - } - - field_entry *nearwebfld = dst.find_field( fd_web ); - int spread_chance = 25 * ( cur.get_field_intensity() - 1 ); - if( nearwebfld != nullptr ) { - spread_chance = 50 + spread_chance / 2; - } - - const ter_t &dster = dst.get_ter_t(); - const furn_t &dsfrn = dst.get_furn_t(); - // Allow weaker fires to spread occasionally - const int power = cur.get_field_intensity() + one_in( 5 ); - if( can_spread && rng( 1, 100 ) < spread_chance && - ( dster.is_flammable() || dsfrn.is_flammable() ) && - ( in_pit == ( dster.id.id() == t_pit ) ) && - ( - ( power >= 3 && cur.get_field_age() < 0_turns && one_in( 20 ) ) || - ( power >= 2 && ( ter_furn_has_flag( dster, dsfrn, TFLAG_FLAMMABLE ) && one_in( 2 ) ) ) || - ( power >= 2 && ( ter_furn_has_flag( dster, dsfrn, TFLAG_FLAMMABLE_ASH ) && one_in( 2 ) ) ) || - ( power >= 3 && ( ter_furn_has_flag( dster, dsfrn, TFLAG_FLAMMABLE_HARD ) && one_in( 5 ) ) ) || - nearwebfld || ( dst.get_item_count() > 0 && - flammable_items_at( p + eight_horizontal_neighbors[i] ) && - one_in( 5 ) ) - ) ) { - // Nearby open flammable ground? Set it on fire. - dst.add_field( fd_fire, 1, 0_turns ); - tmpfld = dst.find_field( fd_fire ); - if( tmpfld != nullptr ) { - // Make the new fire quite weak, so that it doesn't start jumping around instantly - tmpfld->set_field_age( 2_minutes ); - // Consume a bit of our fuel - cur.set_field_age( cur.get_field_age() + 1_minutes ); - } - if( nearwebfld ) { - nearwebfld->set_field_intensity( 0 ); - } - } - } - } else { - const size_t end_i = static_cast( rng( 0, neighbour_vec.size() - 1 ) ); - for( size_t i = ( end_i + 1 ) % neighbour_vec.size(), count = 0; - count != neighbour_vec.size(); - i = ( i + 1 ) % neighbour_vec.size(), count++ ) { - if( one_in( cur.get_field_intensity() * 2 ) ) { - // Skip some processing to save on CPU - continue; - } - - if( neighbour_vec.empty() ) { - continue; - } - - maptile &dst = neighbour_vec[i]; - // No bounds checking here: we'll treat the invalid neighbors as valid. - // We're using the map tile wrapper, so we can treat invalid tiles as sentinels. - // This will create small oddities on map edges, but nothing more noticeable than - // "cut-off" that happens with bounds checks. - - field_entry *nearfire = dst.find_field( fd_fire ); - if( nearfire != nullptr ) { - // We handled supporting fires in the section above, no need to do it here - continue; - } - - field_entry *nearwebfld = dst.find_field( fd_web ); - int spread_chance = 25 * ( cur.get_field_intensity() - 1 ); - if( nearwebfld != nullptr ) { - spread_chance = 50 + spread_chance / 2; - } - - const ter_t &dster = dst.get_ter_t(); - const furn_t &dsfrn = dst.get_furn_t(); - // Allow weaker fires to spread occasionally - const int power = cur.get_field_intensity() + one_in( 5 ); - if( can_spread && rng( 1, 100 - windpower ) < spread_chance && - ( dster.is_flammable() || dsfrn.is_flammable() ) && - ( in_pit == ( dster.id.id() == t_pit ) ) && - ( - ( power >= 3 && cur.get_field_age() < 0_turns && one_in( 20 ) ) || - ( power >= 2 && ( ter_furn_has_flag( dster, dsfrn, TFLAG_FLAMMABLE ) && one_in( 2 ) ) ) || - ( power >= 2 && ( ter_furn_has_flag( dster, dsfrn, TFLAG_FLAMMABLE_ASH ) && one_in( 2 ) ) ) || - ( power >= 3 && ( ter_furn_has_flag( dster, dsfrn, TFLAG_FLAMMABLE_HARD ) && one_in( 5 ) ) ) || - nearwebfld || ( dst.get_item_count() > 0 && - flammable_items_at( p + eight_horizontal_neighbors[i] ) && - one_in( 5 ) ) - ) ) { - // Nearby open flammable ground? Set it on fire. - dst.add_field( fd_fire, 1, 0_turns ); - tmpfld = dst.find_field( fd_fire ); - if( tmpfld != nullptr ) { - // Make the new fire quite weak, so that it doesn't start jumping around instantly - tmpfld->set_field_age( 2_minutes ); - // Consume a bit of our fuel - cur.set_field_age( cur.get_field_age() + 1_minutes ); - } - if( nearwebfld ) { - nearwebfld->set_field_intensity( 0 ); - } - } - } - } - // Create smoke once - above us if possible, at us otherwise - if( !ter_furn_has_flag( ter, frn, TFLAG_SUPPRESS_SMOKE ) && - rng( 0, 100 - windpower ) <= smoke && - rng( 3, 35 ) < cur.get_field_intensity() * 10 ) { - bool smoke_up = zlevels && p.z < OVERMAP_HEIGHT; - if( smoke_up ) { - tripoint up{p.xy(), p.z + 1}; - maptile dst = maptile_at_internal( up ); - const ter_t &dst_ter = dst.get_ter_t(); - if( dst_ter.has_flag( TFLAG_NO_FLOOR ) ) { - dst.add_field( fd_smoke, rng( 1, cur.get_field_intensity() ), 0_turns ); - } else { - // Can't create smoke above - smoke_up = false; - } - } - - if( !smoke_up ) { - maptile dst = maptile_at_internal( p ); - // Create thicker smoke - dst.add_field( fd_smoke, cur.get_field_intensity(), 0_turns ); - } - - // Smoke affects transparency - dirty_transparency_cache = true; - } - - // Hot air is a load on the CPU - // Don't produce too much of it if we have a lot fires nearby, they produce - // radiant heat which does what hot air would do anyway - if( adjacent_fires < 5 && rng( 0, 4 - adjacent_fires ) ) { - create_hot_air( p, cur.get_field_intensity() ); + if( process_fire_field_in_submap( current_submap, cur, p, dirty_transparency_cache ) ) { + break; } } @@ -1349,6 +872,498 @@ bool map::process_fields_in_submap( submap *const current_submap, return dirty_transparency_cache; } +bool map::process_fire_field_in_submap( submap *current_submap, field_entry &cur, + const tripoint &p, bool &dirty_transparency_cache ) +{ + bool breaks_loop = false; + map &here = get_map(); + maptile map_tile( current_submap, point_zero ); + field_entry *tmpfld = nullptr; + cur.set_field_age( std::max( -24_hours, cur.get_field_age() ) ); + // Entire objects for ter/frn for flags + const oter_id &cur_om_ter = overmap_buffer.ter( ms_to_omt_copy( here.getabs( p ) ) ); + bool sheltered = g->is_sheltered( p ); + int winddirection = g->weather.winddirection; + int windpower = get_local_windpower( g->weather.windspeed, cur_om_ter, p, winddirection, + sheltered ); + const ter_t &ter = map_tile.get_ter_t(); + const furn_t &frn = map_tile.get_furn_t(); + + // We've got ter/furn cached, so let's use that + const bool is_sealed = ter_furn_has_flag( ter, frn, TFLAG_SEALED ) && + !ter_furn_has_flag( ter, frn, TFLAG_ALLOW_FIELD_EFFECT ); + // Smoke generation probability, consumed items count + int smoke = 0; + int consumed = 0; + // How much time to add to the fire's life due to burned items/terrain/furniture + time_duration time_added = 0_turns; + // Checks if the fire can spread + const bool can_spread = !ter_furn_has_flag( ter, frn, TFLAG_FIRE_CONTAINER ); + // If the flames are in furniture with fire_container flag like brazier or oven, + // they're fully contained, so skip consuming terrain + const bool can_burn = ( ter.is_flammable() || frn.is_flammable() ) && + !ter_furn_has_flag( ter, frn, TFLAG_FIRE_CONTAINER ); + // The huge indent below should probably be somehow moved away from here + // without forcing the function to use i_at( p ) for fires without items + if( !is_sealed && map_tile.get_item_count() > 0 ) { + map_stack items_here = i_at( p ); + std::vector new_content; + for( auto it = items_here.begin(); it != items_here.end(); ) { + if( it->will_explode_in_fire() ) { + // We need to make a copy because the iterator validity is not predictable + item copy = *it; + it = items_here.erase( it ); + if( copy.detonate( p, new_content ) ) { + // Need to restart, iterators may not be valid + it = items_here.begin(); + } + } else { + ++it; + } + } + + fire_data frd( cur.get_field_intensity(), !can_spread ); + // The highest # of items this fire can remove in one turn + int max_consume = cur.get_field_intensity() * 2; + + for( auto fuel = items_here.begin(); fuel != items_here.end() && consumed < max_consume; ) { + // `item::burn` modifies the charges in order to simulate some of them getting + // destroyed by the fire, this changes the item weight, but may not actually + // destroy it. We need to spawn products anyway. + const units::mass old_weight = fuel->weight( false ); + bool destroyed = fuel->burn( frd ); + // If the item is considered destroyed, it may have negative charge count, + // see `item::burn?. This in turn means `item::weight` returns a negative value, + // which we can not use, so only call `weight` when it's still an existing item. + const units::mass new_weight = destroyed ? 0_gram : fuel->weight( false ); + if( old_weight != new_weight ) { + create_burnproducts( p, *fuel, old_weight - new_weight ); + } + + if( destroyed ) { + // If we decided the item was destroyed by fire, remove it. + // But remember its contents, except for irremovable mods, if any + const std::list content_list = fuel->contents.all_items_top(); + for( item *it : content_list ) { + if( !it->is_irremovable() ) { + new_content.push_back( item( *it ) ); + } + } + fuel = items_here.erase( fuel ); + consumed++; + } else { + ++fuel; + } + } + + spawn_items( p, new_content ); + smoke = roll_remainder( frd.smoke_produced ); + time_added = 1_turns * roll_remainder( frd.fuel_produced ); + } + + int part; + // Get the part of the vehicle in the fire (_internal skips the boundary check) + vehicle *veh = veh_at_internal( p, part ); + if( veh != nullptr ) { + veh->damage( part, cur.get_field_intensity() * 10, DT_HEAT, true ); + // Damage the vehicle in the fire. + } + if( can_burn ) { + if( ter.has_flag( TFLAG_SWIMMABLE ) ) { + // Flames die quickly on water + cur.set_field_age( cur.get_field_age() + 4_minutes ); + } + + // Consume the terrain we're on + if( ter_furn_has_flag( ter, frn, TFLAG_FLAMMABLE ) ) { + // The fire feeds on the ground itself until max intensity. + time_added += 1_turns * ( 5 - cur.get_field_intensity() ); + smoke += 2; + smoke += static_cast( windpower / 5 ); + if( cur.get_field_intensity() > 1 && + one_in( 200 - cur.get_field_intensity() * 50 ) ) { + destroy( p, false ); + } + + } else if( ter_furn_has_flag( ter, frn, TFLAG_FLAMMABLE_HARD ) && + one_in( 3 ) ) { + // The fire feeds on the ground itself until max intensity. + time_added += 1_turns * ( 4 - cur.get_field_intensity() ); + smoke += 2; + smoke += static_cast( windpower / 5 ); + if( cur.get_field_intensity() > 1 && + one_in( 200 - cur.get_field_intensity() * 50 ) ) { + destroy( p, false ); + } + + } else if( ter.has_flag( TFLAG_FLAMMABLE_ASH ) ) { + // The fire feeds on the ground itself until max intensity. + time_added += 1_turns * ( 5 - cur.get_field_intensity() ); + smoke += 2; + smoke += static_cast( windpower / 5 ); + if( cur.get_field_intensity() > 1 && + one_in( 200 - cur.get_field_intensity() * 50 ) ) { + if( p.z > 0 ) { + // We're in the air + ter_set( p, t_open_air ); + } else { + ter_set( p, t_dirt ); + } + } + + } else if( frn.has_flag( TFLAG_FLAMMABLE_ASH ) ) { + // The fire feeds on the ground itself until max intensity. + time_added += 1_turns * ( 5 - cur.get_field_intensity() ); + smoke += 2; + smoke += static_cast( windpower / 5 ); + if( cur.get_field_intensity() > 1 && + one_in( 200 - cur.get_field_intensity() * 50 ) ) { + furn_set( p, f_ash ); + add_item_or_charges( p, item( "ash" ) ); + } + + } else if( ter.has_flag( TFLAG_NO_FLOOR ) && zlevels && p.z > -OVERMAP_DEPTH ) { + // We're hanging in the air - let's fall down + tripoint dst{ p.xy(), p.z - 1 }; + if( valid_move( p, dst, true, true ) ) { + maptile dst_tile = maptile_at_internal( dst ); + field_entry *fire_there = dst_tile.find_field( fd_fire ); + if( fire_there == nullptr ) { + dst_tile.add_field( fd_fire, 1, 0_turns ); + cur.set_field_intensity( cur.get_field_intensity() - 1 ); + } else { + // Don't fuel raging fires or they'll burn forever + // as they can produce small fires above themselves + int new_intensity = std::max( cur.get_field_intensity(), + fire_there->get_field_intensity() ); + // Allow smaller fires to combine + if( new_intensity < 3 && + cur.get_field_intensity() == fire_there->get_field_intensity() ) { + new_intensity++; + } + // A raging fire below us can support us for a while + // Otherwise decay and decay fast + if( fire_there->get_field_intensity() < 3 || one_in( 10 ) ) { + cur.set_field_intensity( cur.get_field_intensity() - 1 ); + } + fire_there->set_field_intensity( new_intensity ); + } + dirty_transparency_cache = true; + return breaks_loop; + } + } + } + // Lower age is a longer lasting fire + if( time_added != 0_turns ) { + cur.set_field_age( cur.get_field_age() - time_added ); + } else if( can_burn ) { + // Nothing to burn = fire should be dying out faster + // Drain more power from big fires, so that they stop raging over nothing + // Except for fires on stoves and fireplaces, those are made to keep the fire alive + cur.mod_field_age( 10_seconds * cur.get_field_intensity() ); + } + + // Below we will access our nearest 8 neighbors, so let's cache them now + // This should probably be done more globally, because large fires will re-do it a lot + auto neighs = get_neighbors( p ); + // Get the neighbours that are allowed due to wind direction + auto maptiles = get_wind_blockers( winddirection, p ); + maptile remove_tile = std::get<0>( maptiles ); + maptile remove_tile2 = std::get<1>( maptiles ); + maptile remove_tile3 = std::get<2>( maptiles ); + std::vector neighbour_vec; + size_t end_it = static_cast( rng( 0, neighs.size() - 1 ) ); + // Start at end_it + 1, then wrap around until all elements have been processed + for( size_t i = ( end_it + 1 ) % neighs.size(), count = 0; + count != neighs.size(); + i = ( i + 1 ) % neighs.size(), count++ ) { + const auto &neigh = neighs[i]; + if( ( neigh.pos_.x != remove_tile.pos_.x && neigh.pos_.y != remove_tile.pos_.y ) || + ( neigh.pos_.x != remove_tile2.pos_.x && neigh.pos_.y != remove_tile2.pos_.y ) || + ( neigh.pos_.x != remove_tile3.pos_.x && neigh.pos_.y != remove_tile3.pos_.y ) ) { + neighbour_vec.push_back( neigh ); + } else if( x_in_y( 1, std::max( 2, windpower ) ) ) { + neighbour_vec.push_back( neigh ); + } + } + // If the flames are in a pit, it can't spread to non-pit + const bool in_pit = ter.id.id() == t_pit; + + // Count adjacent fires, to optimize out needless smoke and hot air + int adjacent_fires = 0; + + // If the flames are big, they contribute to adjacent flames + if( can_spread ) { + if( cur.get_field_intensity() > 1 && one_in( 3 ) ) { + // Basically: Scan around for a spot, + // if there is more fire there, make it bigger and give it some fuel. + // This is how big fires spend their excess age: + // making other fires bigger. Flashpoint. + if( sheltered || windpower < 5 ) { + end_it = static_cast( rng( 0, neighs.size() - 1 ) ); + for( size_t i = ( end_it + 1 ) % neighs.size(), count = 0; + count != neighs.size() && cur.get_field_age() < 0_turns; + i = ( i + 1 ) % neighs.size(), count++ ) { + maptile &dst = neighs[i]; + auto dstfld = dst.find_field( fd_fire ); + // If the fire exists and is weaker than ours, boost it + if( dstfld != nullptr && + ( dstfld->get_field_intensity() <= cur.get_field_intensity() || + dstfld->get_field_age() > cur.get_field_age() ) && + ( in_pit == ( dst.get_ter() == t_pit ) ) ) { + if( dstfld->get_field_intensity() < 2 ) { + dstfld->set_field_intensity( dstfld->get_field_intensity() + 1 ); + } + + dstfld->set_field_age( dstfld->get_field_age() - 5_minutes ); + cur.set_field_age( cur.get_field_age() + 5_minutes ); + } + if( dstfld != nullptr ) { + adjacent_fires++; + } + } + } else { + end_it = static_cast( rng( 0, neighbour_vec.size() - 1 ) ); + for( size_t i = ( end_it + 1 ) % neighbour_vec.size(), count = 0; + count != neighbour_vec.size() && cur.get_field_age() < 0_turns; + i = ( i + 1 ) % neighbour_vec.size(), count++ ) { + maptile &dst = neighbour_vec[i]; + field_entry *dstfld = dst.find_field( fd_fire ); + // If the fire exists and is weaker than ours, boost it + if( dstfld != nullptr && + ( dstfld->get_field_intensity() <= cur.get_field_intensity() || + dstfld->get_field_age() > cur.get_field_age() ) && + ( in_pit == ( dst.get_ter() == t_pit ) ) ) { + if( dstfld->get_field_intensity() < 2 ) { + dstfld->set_field_intensity( dstfld->get_field_intensity() + 1 ); + } + + dstfld->set_field_age( dstfld->get_field_age() - 5_minutes ); + cur.set_field_age( cur.get_field_age() + 5_minutes ); + } + + if( dstfld != nullptr ) { + adjacent_fires++; + } + } + } + } else if( cur.get_field_age() < 0_turns && cur.get_field_intensity() < 3 ) { + // See if we can grow into a stage 2/3 fire, for this + // burning neighbors are necessary in addition to + // field age < 0, or alternatively, a LOT of fuel. + + // The maximum fire intensity is 1 for a lone fire, 2 for at least 1 neighbor, + // 3 for at least 2 neighbors. + int maximum_intensity = 1; + + // The following logic looks a bit complex due to optimization concerns, so here are the semantics: + // 1. Calculate maximum field intensity based on fuel, -50 minutes is 2(medium), -500 minutes is 3(raging) + // 2. Calculate maximum field intensity based on neighbors, 3 neighbors is 2(medium), 7 or more neighbors is 3(raging) + // 3. Pick the higher maximum between 1. and 2. + if( cur.get_field_age() < -500_minutes ) { + maximum_intensity = 3; + } else { + for( auto &neigh : neighs ) { + if( neigh.get_field().find_field( fd_fire ) != nullptr ) { + adjacent_fires++; + } + } + maximum_intensity = 1 + ( adjacent_fires >= 3 ) + ( adjacent_fires >= 7 ); + + if( maximum_intensity < 2 && cur.get_field_age() < -50_minutes ) { + maximum_intensity = 2; + } + } + + // If we consumed a lot, the flames grow higher + if( cur.get_field_intensity() < maximum_intensity && cur.get_field_age() < 0_turns ) { + // Fires under 0 age grow in size. Level 3 fires under 0 spread later on. + // Weaken the newly-grown fire + cur.set_field_intensity( cur.get_field_intensity() + 1 ); + cur.set_field_age( cur.get_field_age() + 10_minutes * cur.get_field_intensity() ); + } + } + } + // Consume adjacent fuel / terrain / webs to spread. + // Allow raging fires (and only raging fires) to spread up + // Spreading down is achieved by wrecking the walls/floor and then falling + if( zlevels && cur.get_field_intensity() == 3 && p.z < OVERMAP_HEIGHT ) { + // Let it burn through the floor + maptile dst = maptile_at_internal( {p.xy(), p.z + 1} ); + const auto &dst_ter = dst.get_ter_t(); + if( dst_ter.has_flag( TFLAG_NO_FLOOR ) || + dst_ter.has_flag( TFLAG_FLAMMABLE ) || + dst_ter.has_flag( TFLAG_FLAMMABLE_ASH ) || + dst_ter.has_flag( TFLAG_FLAMMABLE_HARD ) ) { + field_entry *nearfire = dst.find_field( fd_fire ); + if( nearfire != nullptr ) { + nearfire->mod_field_age( -2_turns ); + } else { + dst.add_field( fd_fire, 1, 0_turns ); + } + // Fueling fires above doesn't cost fuel + } + } + // Our iterator will start at end_i + 1 and increment from there and then wrap around. + // This guarantees it will check all neighbors, starting from a random one + if( sheltered || windpower < 5 ) { + const size_t end_i = static_cast( rng( 0, neighs.size() - 1 ) ); + for( size_t i = ( end_i + 1 ) % neighs.size(), count = 0; + count != neighs.size(); + i = ( i + 1 ) % neighs.size(), count++ ) { + if( one_in( cur.get_field_intensity() * 2 ) ) { + // Skip some processing to save on CPU + continue; + } + + maptile &dst = neighs[i]; + // No bounds checking here: we'll treat the invalid neighbors as valid. + // We're using the map tile wrapper, so we can treat invalid tiles as sentinels. + // This will create small oddities on map edges, but nothing more noticeable than + // "cut-off" that happens with bounds checks. + + field_entry *nearfire = dst.find_field( fd_fire ); + if( nearfire != nullptr ) { + // We handled supporting fires in the section above, no need to do it here + continue; + } + + field_entry *nearwebfld = dst.find_field( fd_web ); + int spread_chance = 25 * ( cur.get_field_intensity() - 1 ); + if( nearwebfld != nullptr ) { + spread_chance = 50 + spread_chance / 2; + } + + const ter_t &dster = dst.get_ter_t(); + const furn_t &dsfrn = dst.get_furn_t(); + // Allow weaker fires to spread occasionally + const int power = cur.get_field_intensity() + one_in( 5 ); + if( can_spread && rng( 1, 100 ) < spread_chance && + ( dster.is_flammable() || dsfrn.is_flammable() ) && + ( in_pit == ( dster.id.id() == t_pit ) ) && + ( + ( power >= 3 && cur.get_field_age() < 0_turns && one_in( 20 ) ) || + ( power >= 2 && ( ter_furn_has_flag( dster, dsfrn, TFLAG_FLAMMABLE ) && one_in( 2 ) ) ) || + ( power >= 2 && ( ter_furn_has_flag( dster, dsfrn, TFLAG_FLAMMABLE_ASH ) && one_in( 2 ) ) ) || + ( power >= 3 && ( ter_furn_has_flag( dster, dsfrn, TFLAG_FLAMMABLE_HARD ) && one_in( 5 ) ) ) || + nearwebfld || ( dst.get_item_count() > 0 && + flammable_items_at( p + eight_horizontal_neighbors[i] ) && + one_in( 5 ) ) + ) ) { + // Nearby open flammable ground? Set it on fire. + dst.add_field( fd_fire, 1, 0_turns ); + tmpfld = dst.find_field( fd_fire ); + if( tmpfld != nullptr ) { + // Make the new fire quite weak, so that it doesn't start jumping around instantly + tmpfld->set_field_age( 2_minutes ); + // Consume a bit of our fuel + cur.set_field_age( cur.get_field_age() + 1_minutes ); + } + if( nearwebfld ) { + nearwebfld->set_field_intensity( 0 ); + } + } + } + } else { + const size_t end_i = static_cast( rng( 0, neighbour_vec.size() - 1 ) ); + for( size_t i = ( end_i + 1 ) % neighbour_vec.size(), count = 0; + count != neighbour_vec.size(); + i = ( i + 1 ) % neighbour_vec.size(), count++ ) { + if( one_in( cur.get_field_intensity() * 2 ) ) { + // Skip some processing to save on CPU + continue; + } + + if( neighbour_vec.empty() ) { + continue; + } + + maptile &dst = neighbour_vec[i]; + // No bounds checking here: we'll treat the invalid neighbors as valid. + // We're using the map tile wrapper, so we can treat invalid tiles as sentinels. + // This will create small oddities on map edges, but nothing more noticeable than + // "cut-off" that happens with bounds checks. + + field_entry *nearfire = dst.find_field( fd_fire ); + if( nearfire != nullptr ) { + // We handled supporting fires in the section above, no need to do it here + continue; + } + + field_entry *nearwebfld = dst.find_field( fd_web ); + int spread_chance = 25 * ( cur.get_field_intensity() - 1 ); + if( nearwebfld != nullptr ) { + spread_chance = 50 + spread_chance / 2; + } + + const ter_t &dster = dst.get_ter_t(); + const furn_t &dsfrn = dst.get_furn_t(); + // Allow weaker fires to spread occasionally + const int power = cur.get_field_intensity() + one_in( 5 ); + if( can_spread && rng( 1, 100 - windpower ) < spread_chance && + ( dster.is_flammable() || dsfrn.is_flammable() ) && + ( in_pit == ( dster.id.id() == t_pit ) ) && + ( + ( power >= 3 && cur.get_field_age() < 0_turns && one_in( 20 ) ) || + ( power >= 2 && ( ter_furn_has_flag( dster, dsfrn, TFLAG_FLAMMABLE ) && one_in( 2 ) ) ) || + ( power >= 2 && ( ter_furn_has_flag( dster, dsfrn, TFLAG_FLAMMABLE_ASH ) && one_in( 2 ) ) ) || + ( power >= 3 && ( ter_furn_has_flag( dster, dsfrn, TFLAG_FLAMMABLE_HARD ) && one_in( 5 ) ) ) || + nearwebfld || ( dst.get_item_count() > 0 && + flammable_items_at( p + eight_horizontal_neighbors[i] ) && + one_in( 5 ) ) + ) ) { + // Nearby open flammable ground? Set it on fire. + dst.add_field( fd_fire, 1, 0_turns ); + tmpfld = dst.find_field( fd_fire ); + if( tmpfld != nullptr ) { + // Make the new fire quite weak, so that it doesn't start jumping around instantly + tmpfld->set_field_age( 2_minutes ); + // Consume a bit of our fuel + cur.set_field_age( cur.get_field_age() + 1_minutes ); + } + if( nearwebfld ) { + nearwebfld->set_field_intensity( 0 ); + } + } + } + } + // Create smoke once - above us if possible, at us otherwise + if( !ter_furn_has_flag( ter, frn, TFLAG_SUPPRESS_SMOKE ) && + rng( 0, 100 - windpower ) <= smoke && + rng( 3, 35 ) < cur.get_field_intensity() * 10 ) { + bool smoke_up = zlevels && p.z < OVERMAP_HEIGHT; + if( smoke_up ) { + tripoint up{p.xy(), p.z + 1}; + maptile dst = maptile_at_internal( up ); + const ter_t &dst_ter = dst.get_ter_t(); + if( dst_ter.has_flag( TFLAG_NO_FLOOR ) ) { + dst.add_field( fd_smoke, rng( 1, cur.get_field_intensity() ), 0_turns ); + } else { + // Can't create smoke above + smoke_up = false; + } + } + + if( !smoke_up ) { + maptile dst = maptile_at_internal( p ); + // Create thicker smoke + dst.add_field( fd_smoke, cur.get_field_intensity(), 0_turns ); + } + + // Smoke affects transparency + dirty_transparency_cache = true; + } + + // Hot air is a load on the CPU + // Don't produce too much of it if we have a lot fires nearby, they produce + // radiant heat which does what hot air would do anyway + if( adjacent_fires < 5 && rng( 0, 4 - adjacent_fires ) ) { + create_hot_air( p, cur.get_field_intensity() ); + } + + return breaks_loop; +} + // This entire function makes very little sense. Why are the rules the way they are? Why does walking into some things destroy them but not others? /* From 7bed8e43cf402a4f2480ab5c4573b9abb9d93728 Mon Sep 17 00:00:00 2001 From: Kevin Granade Date: Thu, 2 Jul 2020 14:28:37 -0700 Subject: [PATCH 045/420] Global references migration part twelve (#41764) * Replace g->u with get_player_character() * Migrate map references to getters part 12 --- src/monattack.cpp | 808 +++++++++++++++++++++++++--------------------- 1 file changed, 440 insertions(+), 368 deletions(-) diff --git a/src/monattack.cpp b/src/monattack.cpp index 648b4cdaa2790..51578519a3086 100644 --- a/src/monattack.cpp +++ b/src/monattack.cpp @@ -15,7 +15,6 @@ #include #include -#include "avatar.h" #include "ballistics.h" #include "bodypart.h" #include "calendar.h" @@ -209,7 +208,9 @@ static const bionic_id bio_uncanny_dodge( "bio_uncanny_dodge" ); // shared utility functions static bool within_visual_range( monster *z, int max_range ) { - return !( rl_dist( z->pos(), g->u.pos() ) > max_range || !z->sees( g->u ) ); + Character &player_character = get_player_character(); + return !( rl_dist( z->pos(), player_character.pos() ) > max_range || + !z->sees( player_character ) ); } static bool within_target_range( const monster *const z, const Creature *const target, int range ) @@ -229,7 +230,7 @@ static Creature *sting_get_target( monster *z, float range = 5.0f ) // Can't see/reach target, no attack if( !z->sees( *target ) || - !g->m.clear_path( z->pos(), target->pos(), range, 1, 100 ) ) { + !get_map().clear_path( z->pos(), target->pos(), range, 1, 100 ) ) { return nullptr; } @@ -296,7 +297,8 @@ static bool is_adjacent( const monster *z, const Creature *target, const bool al const bool target_above = target->posz() > z->posz(); const tripoint &up = target_above ? target->pos() : z->pos(); const tripoint &down = target_above ? z->pos() : target->pos(); - return g->m.ter( up ) == t_open_air && g->m.is_outside( down ); + map &here = get_map(); + return here.ter( up ) == t_open_air && here.is_outside( down ); } static npc make_fake_npc( monster *z, int str, int dex, int inte, int per ) @@ -327,15 +329,16 @@ bool mattack::eat_crop( monster *z ) { cata::optional target; int num_targets = 1; - for( const auto &p : g->m.points_in_radius( z->pos(), 1 ) ) { - if( g->m.has_flag( "PLANT", p ) && one_in( num_targets ) ) { + map &here = get_map(); + for( const auto &p : here.points_in_radius( z->pos(), 1 ) ) { + if( here.has_flag( "PLANT", p ) && one_in( num_targets ) ) { num_targets++; target = p; } } if( target ) { - g->m.furn_set( *target, furn_str_id( g->m.furn( *target )->plant->base ) ); - g->m.i_clear( *target ); + here.furn_set( *target, furn_str_id( here.furn( *target )->plant->base ) ); + here.i_clear( *target ); return true; } return true; @@ -343,12 +346,13 @@ bool mattack::eat_crop( monster *z ) bool mattack::eat_food( monster *z ) { - for( const auto &p : g->m.points_in_radius( z->pos(), 1 ) ) { + map &here = get_map(); + for( const auto &p : here.points_in_radius( z->pos(), 1 ) ) { //Protect crop seeds from carnivores, give omnivores eat_crop special also - if( g->m.has_flag( "PLANT", p ) ) { + if( here.has_flag( "PLANT", p ) ) { continue; } - map_stack items = g->m.i_at( p ); + map_stack items = here.i_at( p ); for( auto &item : items ) { //Fun limit prevents scavengers from eating feces if( !item.is_food() || item.get_comestible_fun() < -20 ) { @@ -358,9 +362,9 @@ bool mattack::eat_food( monster *z ) if( z->type->baby_egg != item.type->get_id() ) { int consumed = 1; if( item.count_by_charges() ) { - g->m.use_charges( p, 0, item.type->get_id(), consumed ); + here.use_charges( p, 0, item.type->get_id(), consumed ); } else { - g->m.use_amount( p, 0, item.type->get_id(), consumed ); + here.use_amount( p, 0, item.type->get_id(), consumed ); } return true; } @@ -373,9 +377,10 @@ bool mattack::antqueen( monster *z ) { std::vector egg_points; std::vector ants; + map &here = get_map(); // Count up all adjacent tiles the contain at least one egg. - for( const auto &dest : g->m.points_in_radius( z->pos(), 2 ) ) { - if( g->m.impassable( dest ) ) { + for( const auto &dest : here.points_in_radius( z->pos(), 2 ) ) { + if( here.impassable( dest ) ) { continue; } @@ -387,8 +392,8 @@ bool mattack::antqueen( monster *z ) continue; } - if( g->is_empty( dest ) && g->m.has_items( dest ) ) { - for( item &i : g->m.i_at( dest ) ) { + if( g->is_empty( dest ) && here.has_items( dest ) ) { + for( item &i : here.i_at( dest ) ) { if( i.typeId() == itype_ant_egg ) { egg_points.push_back( dest ); // Done looking at this tile @@ -398,30 +403,31 @@ bool mattack::antqueen( monster *z ) } } + Character &player_character = get_player_character(); if( !ants.empty() ) { // It takes a while z->moves -= 100; monster *ant = random_entry( ants ); - if( g->u.sees( *z ) && g->u.sees( *ant ) ) { + if( player_character.sees( *z ) && player_character.sees( *ant ) ) { add_msg( m_warning, _( "The %1$s feeds an %2$s and it grows!" ), z->name(), ant->name() ); } ant->poly( ant->type->upgrade_into ); } else if( egg_points.empty() ) { // There's no eggs nearby--lay one. - if( g->u.sees( *z ) ) { + if( player_character.sees( *z ) ) { add_msg( _( "The %s lays an egg!" ), z->name() ); } - g->m.spawn_item( z->pos(), "ant_egg", 1, 0, calendar::turn ); + here.spawn_item( z->pos(), "ant_egg", 1, 0, calendar::turn ); } else { // There are eggs nearby. Let's hatch some. // It takes a while z->moves -= 20 * egg_points.size(); - if( g->u.sees( *z ) ) { + if( player_character.sees( *z ) ) { add_msg( m_warning, _( "The %s tends nearby eggs, and they hatch!" ), z->name() ); } for( const tripoint &egg_pos : egg_points ) { - map_stack items = g->m.i_at( egg_pos ); + map_stack items = here.i_at( egg_pos ); for( map_stack::iterator it = items.begin(); it != items.end(); ) { if( it->typeId() != itype_ant_egg ) { ++it; @@ -470,7 +476,7 @@ bool mattack::shriek_alert( monster *z ) return false; } - if( g->u.sees( *z ) ) { + if( get_player_character().sees( *z ) ) { add_msg( _( "The %s begins shrieking!" ), z->name() ); } @@ -503,7 +509,8 @@ bool mattack::shriek_stun( monster *z ) int target_angle = coord_to_angle( z->pos(), target->pos() ); int cone_angle = 20; - for( const tripoint &cone : g->m.points_in_radius( z->pos(), 4 ) ) { + map &here = get_map(); + for( const tripoint &cone : here.points_in_radius( z->pos(), 4 ) ) { int tile_angle = coord_to_angle( z->pos(), cone ); int diff = std::abs( target_angle - tile_angle ); // Skip the target, because it's outside cone or it's the source @@ -512,7 +519,7 @@ bool mattack::shriek_stun( monster *z ) } // Affect the target // Small bash to every square, silent to not flood message box - g->m.bash( cone, 4, true ); + here.bash( cone, 4, true ); // If a monster is there, chance for stun Creature *target = g->critter_at( cone ); @@ -565,7 +572,7 @@ bool mattack::rattle( monster *z ) { // TODO: Let it rattle at non-player friendlies const int min_dist = z->friendly != 0 ? 1 : 4; - Creature *target = &g->u; + Creature *target = &get_player_character(); // Can't use attack_target - the snake has no target if( rl_dist( z->pos(), target->pos() ) > min_dist || !z->sees( *target ) ) { @@ -591,9 +598,10 @@ bool mattack::acid( monster *z ) return false; } + map &here = get_map(); // Can't see/reach target, no attack if( !z->sees( *target ) || - !g->m.clear_path( z->pos(), target->pos(), 10, 1, 100 ) ) { + !here.clear_path( z->pos(), target->pos(), 10, 1, 100 ) ) { return false; } // It takes a while @@ -610,10 +618,10 @@ bool mattack::acid( monster *z ) auto dealt = projectile_attack( proj, z->pos(), target->pos(), dispersion_sources{ 5400 }, z ); const tripoint &hitp = dealt.end_point; const Creature *hit_critter = dealt.hit_critter; - if( hit_critter == nullptr && g->m.hit_with_acid( hitp ) && g->u.sees( hitp ) ) { + if( hit_critter == nullptr && here.hit_with_acid( hitp ) && get_player_character().sees( hitp ) ) { add_msg( _( "A glob of acid hits the %s!" ), - g->m.tername( hitp ) ); - if( g->m.impassable( hitp ) ) { + here.tername( hitp ) ); + if( here.impassable( hitp ) ) { // TODO: Allow it to spill on the side it hit from return true; } @@ -622,10 +630,10 @@ bool mattack::acid( monster *z ) for( int i = -3; i <= 3; i++ ) { for( int j = -3; j <= 3; j++ ) { tripoint dest = hitp + tripoint( i, j, 0 ); - if( g->m.passable( dest ) && - g->m.clear_path( dest, hitp, 6, 1, 100 ) && + if( here.passable( dest ) && + here.clear_path( dest, hitp, 6, 1, 100 ) && ( ( one_in( std::abs( j ) ) && one_in( std::abs( i ) ) ) || ( i == 0 && j == 0 ) ) ) { - g->m.add_field( dest, fd_acid, 2 ); + here.add_field( dest, fd_acid, 2 ); } } } @@ -647,11 +655,11 @@ bool mattack::acid_barf( monster *z ) z->moves -= 80; // Make sure it happens before uncanny dodge - g->m.add_field( target->pos(), fd_acid, 1 ); + get_map().add_field( target->pos(), fd_acid, 1 ); bool uncanny = target->uncanny_dodge(); // Can we dodge the attack? Uses player dodge function % chance (melee.cpp) if( uncanny || dodge_check( z, target ) ) { - auto msg_type = target == &g->u ? m_warning : m_info; + auto msg_type = target->is_avatar() ? m_warning : m_info; target->add_msg_player_or_npc( msg_type, _( "The %s barfs acid at you, but you dodge!" ), _( "The %s barfs acid at , but they dodge!" ), @@ -671,7 +679,7 @@ bool mattack::acid_barf( monster *z ) hit->token ); if( dam > 0 ) { - auto msg_type = target == &g->u ? m_bad : m_info; + auto msg_type = target->is_avatar() ? m_bad : m_info; target->add_msg_player_or_npc( msg_type, //~ 1$s is monster name, 2$s bodypart in accusative _( "The %1$s barfs acid on your %2$s for %3$d damage!" ), @@ -738,10 +746,12 @@ bool mattack::shockstorm( monster *z ) return false; } - bool seen = g->u.sees( *z ); + Character &player_character = get_player_character(); + bool seen = player_character.sees( *z ); + map &here = get_map(); // Can't see/reach target, no attack if( !z->sees( *target ) || - !g->m.clear_path( z->pos(), target->pos(), 12, 1, 100 ) ) { + !here.clear_path( z->pos(), target->pos(), 12, 1, 100 ) ) { return false; } @@ -749,10 +759,10 @@ bool mattack::shockstorm( monster *z ) z->moves -= 50; if( seen ) { - auto msg_type = target == &g->u ? m_bad : m_neutral; + auto msg_type = target->is_avatar() ? m_bad : m_neutral; add_msg( msg_type, _( "A bolt of electricity arcs towards %s!" ), target->disp_name() ); } - if( !g->u.is_deaf() ) { + if( !player_character.is_deaf() ) { sfx::play_variant_sound( "fire_gun", "bio_lightning", sfx::get_heard_volume( z->pos() ) ); } tripoint tarp( target->posx() + rng( -1, 1 ) + rng( -1, 1 ), @@ -762,13 +772,13 @@ bool mattack::shockstorm( monster *z ) // Fill the LOS with electricity for( auto &i : bolt ) { if( !one_in( 4 ) ) { - g->m.add_field( i, fd_electricity, rng( 1, 3 ) ); + here.add_field( i, fd_electricity, rng( 1, 3 ) ); } } // 5x5 cloud of electricity at the square hit - for( const auto &dest : g->m.points_in_radius( tarp, 2 ) ) { + for( const auto &dest : here.points_in_radius( tarp, 2 ) ) { if( !one_in( 4 ) ) { - g->m.add_field( dest, fd_electricity, rng( 1, 3 ) ); + here.add_field( dest, fd_electricity, rng( 1, 3 ) ); } } @@ -806,11 +816,11 @@ bool mattack::pull_metal_weapon( monster *z ) } // Can't see/reach target, no attack - if( !z->sees( *target ) || !g->m.clear_path( z->pos(), target->pos(), + if( !z->sees( *target ) || !get_map().clear_path( z->pos(), target->pos(), max_distance, 1, 100 ) ) { return false; } - player *foe = dynamic_cast< player * >( target ); + Character *foe = dynamic_cast< Character * >( target ); if( foe != nullptr ) { // Wielded steel or iron items except for built-in things like bionic claws or monomolecular blade if( !foe->weapon.has_flag( "NO_UNWIELD" ) && @@ -827,7 +837,7 @@ bool mattack::pull_metal_weapon( monster *z ) ///\EFFECT_MELEE increases resistance to pull_metal_weapon special attack success = std::max( 100 - ( 6 * ( foe->str_cur - 6 ) ) - ( 6 * wp_skill ), 0 ); } - auto m_type = foe == &g->u ? m_bad : m_neutral; + auto m_type = foe->is_avatar() ? m_bad : m_neutral; if( rng( 1, 100 ) <= success ) { target->add_msg_player_or_npc( m_type, _( "%s is pulled away from your hands!" ), _( "%s is pulled away from 's hands!" ), foe->weapon.tname() ); @@ -857,21 +867,23 @@ bool mattack::boomer( monster *z ) return false; } - std::vector line = g->m.find_clear_path( z->pos(), target->pos() ); + map &here = get_map(); + std::vector line = here.find_clear_path( z->pos(), target->pos() ); // It takes a while z->moves -= 250; - bool u_see = g->u.sees( *z ); + Character &player_character = get_player_character(); + bool u_see = player_character.sees( *z ); if( u_see ) { add_msg( m_warning, _( "The %s spews bile!" ), z->name() ); } for( auto &i : line ) { - g->m.add_field( i, fd_bile, 1 ); + here.add_field( i, fd_bile, 1 ); // If bile hit a solid tile, return. - if( g->m.impassable( i ) ) { - g->m.add_field( i, fd_bile, 3 ); - if( g->u.sees( i ) ) { + if( here.impassable( i ) ) { + here.add_field( i, fd_bile, 3 ); + if( player_character.sees( i ) ) { add_msg( _( "Bile splatters on the %s!" ), - g->m.tername( i ) ); + here.tername( i ) ); } return true; } @@ -901,19 +913,21 @@ bool mattack::boomer_glow( monster *z ) return false; } - std::vector line = g->m.find_clear_path( z->pos(), target->pos() ); + map &here = get_map(); + std::vector line = here.find_clear_path( z->pos(), target->pos() ); // It takes a while z->moves -= 250; - bool u_see = g->u.sees( *z ); + Character &player_character = get_player_character(); + bool u_see = player_character.sees( *z ); if( u_see ) { add_msg( m_warning, _( "The %s spews bile!" ), z->name() ); } for( auto &i : line ) { - g->m.add_field( i, fd_bile, 1 ); - if( g->m.impassable( i ) ) { - g->m.add_field( i, fd_bile, 3 ); - if( g->u.sees( i ) ) { - add_msg( _( "Bile splatters on the %s!" ), g->m.tername( i ) ); + here.add_field( i, fd_bile, 1 ); + if( here.impassable( i ) ) { + here.add_field( i, fd_bile, 3 ); + if( player_character.sees( i ) ) { + add_msg( _( "Bile splatters on the %s!" ), here.tername( i ) ); } return true; } @@ -958,19 +972,21 @@ bool mattack::resurrect( monster *z ) raising_level = z->get_effect_int( effect_raising ) * 40; } - bool sees_necromancer = g->u.sees( *z ); + Character &player_character = get_player_character(); + bool sees_necromancer = player_character.sees( *z ); std::vector> corpses; // Find all corpses that we can see within 10 tiles. int range = 10; bool found_eligible_corpse = false; int lowest_raise_score = INT_MAX; - for( const tripoint &p : g->m.points_in_radius( z->pos(), range ) ) { - if( !g->is_empty( p ) || g->m.get_field_intensity( p, fd_fire ) > 1 || - !g->m.sees( z->pos(), p, -1 ) ) { + map &here = get_map(); + for( const tripoint &p : here.points_in_radius( z->pos(), range ) ) { + if( !g->is_empty( p ) || here.get_field_intensity( p, fd_fire ) > 1 || + !here.sees( z->pos(), p, -1 ) ) { continue; } - for( item &i : g->m.i_at( p ) ) { + for( item &i : here.i_at( p ) ) { const mtype *mt = i.get_mtype(); if( !( i.is_corpse() && i.can_revive() && i.active && mt->has_flag( MF_REVIVES ) && mt->in_species( species_ZOMBIE ) && !mt->has_flag( MF_NO_NECRO ) ) ) { @@ -1046,7 +1062,7 @@ bool mattack::resurrect( monster *z ) float corpse_damage = raised.second->damage_level( 4 ); // Did we successfully raise something? if( g->revive_corpse( raised.first, *raised.second ) ) { - g->m.i_rem( raised.first, raised.second ); + here.i_rem( raised.first, raised.second ); if( sees_necromancer ) { add_msg( m_info, _( "The %s gestures at a nearby corpse." ), z->name() ); } @@ -1063,7 +1079,7 @@ bool mattack::resurrect( monster *z ) } zed->make_ally( *z ); - if( g->u.sees( *zed ) ) { + if( player_character.sees( *zed ) ) { add_msg( m_warning, _( "A nearby %s rises from the dead!" ), zed->name() ); } else if( sees_necromancer ) { // We saw the necromancer but not the revival @@ -1147,7 +1163,7 @@ find_empty_neighbors( const tripoint &origin ) std::pair < std::array < tripoint, ( 2 * N + 1 )*( 2 * N + 1 ) >, size_t > result; - for( const tripoint &tmp : g->m.points_in_radius( origin, r ) ) { + for( const tripoint &tmp : get_map().points_in_radius( origin, r ) ) { if( g->is_empty( tmp ) ) { result.first[result.second++] = tmp; } @@ -1258,10 +1274,8 @@ bool mattack::science( monster *const z ) // I said SCIENCE again! valid_attacks[valid_attack_count++] = att_shock; } - // TODO: mutate() doesn't like non-players right now - // It will mutate NPCs, but it will say it mutated the player - player *const foe = dynamic_cast( target ); - if( ( foe == &g->u ) && dist <= 2 ) { + Character *const foe = dynamic_cast( target ); + if( ( foe->is_avatar() ) && dist <= 2 ) { valid_attacks[valid_attack_count++] = att_radiation; } @@ -1279,6 +1293,7 @@ bool mattack::science( monster *const z ) // I said SCIENCE again! // flavor is always okay valid_attacks[valid_attack_count++] = att_flavor; + Character &player_character = get_player_character(); //////////////////////////////////////////////////////////////////////////////////////////////// // choose and do a valid attack const int attack_index = get_random_index( valid_attack_count ); @@ -1297,8 +1312,7 @@ bool mattack::science( monster *const z ) // I said SCIENCE again! z->moves -= att_cost_rad; // if the player can see it - if( g->u.sees( *z ) ) { - // TODO: mutate() doesn't like non-players right now + if( player_character.sees( *z ) ) { add_msg( m_bad, _( "The %1$s fires a shimmering beam towards %2$s!" ), z->name(), target->disp_name() ); } @@ -1336,7 +1350,7 @@ bool mattack::science( monster *const z ) // I said SCIENCE again! z->ammo[itype_bot_manhack]--; // if the player can see it - if( g->u.sees( *z ) ) { + if( player_character.sees( *z ) ) { add_msg( m_warning, _( "A manhack flies out of one of the holes on the %s!" ), z->name() ); } @@ -1347,23 +1361,24 @@ bool mattack::science( monster *const z ) // I said SCIENCE again! } } break; - case att_acid_pool : + case att_acid_pool : { z->moves -= att_cost_acid; // if the player can see it - if( g->u.sees( *z ) ) { + if( player_character.sees( *z ) ) { add_msg( m_warning, _( "The %s shudders, and some sort of caustic fluid leaks from a its damaged shell!" ), z->name() ); } + map &here = get_map(); // fill empty tiles with acid for( size_t i = 0; i < empty_neighbor_count; ++i ) { const tripoint &p = empty_neighbors.first[i]; - g->m.add_field( p, fd_acid, att_acid_intensity ); + here.add_field( p, fd_acid, att_acid_intensity ); } - - break; + } + break; case att_flavor : { // flavor messages static const std::array m_flavor = {{ @@ -1383,7 +1398,7 @@ bool mattack::science( monster *const z ) // I said SCIENCE again! } // if the player can see it, else forget about it - if( g->u.sees( *z ) ) { + if( player_character.sees( *z ) ) { add_msg( m_warning, _( m_flavor[i] ), z->name() ); } } @@ -1415,20 +1430,21 @@ static body_part body_part_hit_by_plant() bool mattack::growplants( monster *z ) { - for( const auto &p : g->m.points_in_radius( z->pos(), 3 ) ) { + map &here = get_map(); + for( const auto &p : here.points_in_radius( z->pos(), 3 ) ) { // Only affect natural, dirtlike terrain or trees. - if( !( g->m.has_flag_ter( "DIGGABLE", p ) || - g->m.has_flag_ter( "TREE", p ) || - g->m.ter( p ) == t_tree_young ) ) { + if( !( here.has_flag_ter( "DIGGABLE", p ) || + here.has_flag_ter( "TREE", p ) || + here.ter( p ) == t_tree_young ) ) { continue; } - if( g->m.is_bashable( p ) && one_in( 3 ) ) { + if( here.is_bashable( p ) && one_in( 3 ) ) { // Destroy everything - g->m.destroy( p ); + here.destroy( p ); // And then make the ground fertile - g->m.ter_set( p, t_dirtmound ); + here.ter_set( p, t_dirtmound ); continue; } @@ -1436,7 +1452,7 @@ bool mattack::growplants( monster *z ) if( !one_in( 4 ) ) { if( one_in( 3 ) ) { // If no tree, perhaps underbrush - g->m.ter_set( p, t_underbrush ); + here.ter_set( p, t_underbrush ); } continue; @@ -1450,7 +1466,7 @@ bool mattack::growplants( monster *z ) continue; } - g->m.ter_set( p, t_tree_young ); + here.ter_set( p, t_tree_young ); if( critter == nullptr || critter->uncanny_dodge() ) { continue; } @@ -1469,8 +1485,8 @@ bool mattack::growplants( monster *z ) if( !one_in( 5 ) ) { return true; } - for( const tripoint &p : g->m.points_in_radius( z->pos(), 5 ) ) { - const auto ter = g->m.ter( p ); + for( const tripoint &p : here.points_in_radius( z->pos(), 5 ) ) { + const auto ter = here.ter( p ); if( ter != t_tree_young && ter != t_underbrush ) { // Skip as soon as possible to avoid all the checks continue; @@ -1485,10 +1501,10 @@ bool mattack::growplants( monster *z ) if( ter == t_tree_young ) { // Young tree => tree // TODO: Make this deal damage too - young tree can be walked on, tree can't - g->m.ter_set( p, t_tree ); + here.ter_set( p, t_tree ); } else if( ter == t_underbrush ) { // Underbrush => young tree - g->m.ter_set( p, t_tree_young ); + here.ter_set( p, t_tree_young ); if( critter != nullptr && !critter->uncanny_dodge() ) { const body_part hit = body_part_hit_by_plant(); critter->add_msg_player_or_npc( m_bad, @@ -1509,7 +1525,7 @@ bool mattack::growplants( monster *z ) bool mattack::grow_vine( monster *z ) { if( z->friendly ) { - if( rl_dist( g->u.pos(), z->pos() ) <= 3 ) { + if( rl_dist( get_player_character().pos(), z->pos() ) <= 3 ) { // Friendly vines keep the area around you free, so you can move. return false; } @@ -1530,14 +1546,15 @@ bool mattack::grow_vine( monster *z ) bool mattack::vine( monster *z ) { int vine_neighbors = 0; - bool parent_out_of_range = !g->m.inbounds( z->move_target() ); + map &here = get_map(); + bool parent_out_of_range = !here.inbounds( z->move_target() ); monster *parent = g->critter_at( z->move_target() ); if( !parent_out_of_range && ( parent == nullptr || parent->type->id != mon_creeper_hub ) ) { // TODO: Should probably die instead. return true; } z->moves -= 100; - for( const tripoint &dest : g->m.points_in_radius( z->pos(), 1 ) ) { + for( const tripoint &dest : here.points_in_radius( z->pos(), 1 ) ) { Creature *critter = g->critter_at( dest ); if( critter != nullptr && z->attitude_to( *critter ) == Creature::Attitude::HOSTILE ) { if( critter->uncanny_dodge() ) { @@ -1616,31 +1633,34 @@ bool mattack::triffid_heartbeat( monster *z ) return true; // TODO: when friendly: open a way to the stairs, don't spawn monsters } - if( g->u.posz() != z->posz() ) { + Character &player_character = get_player_character(); + if( player_character.posz() != z->posz() ) { // Maybe remove this and allow spawning monsters above? return true; } + map &here = get_map(); static pathfinding_settings root_pathfind( 10, 20, 50, 0, false, false, false, false, false ); - if( rl_dist( z->pos(), g->u.pos() ) > 5 && - !g->m.route( g->u.pos(), z->pos(), root_pathfind ).empty() ) { + if( rl_dist( z->pos(), player_character.pos() ) > 5 && + !here.route( player_character.pos(), z->pos(), root_pathfind ).empty() ) { add_msg( m_warning, _( "The root walls creak around you." ) ); - for( const tripoint &dest : g->m.points_in_radius( z->pos(), 3 ) ) { + for( const tripoint &dest : here.points_in_radius( z->pos(), 3 ) ) { if( g->is_empty( dest ) && one_in( 4 ) ) { - g->m.ter_set( dest, t_root_wall ); - } else if( g->m.ter( dest ) == t_root_wall && one_in( 10 ) ) { - g->m.ter_set( dest, t_dirt ); + here.ter_set( dest, t_root_wall ); + } else if( here.ter( dest ) == t_root_wall && one_in( 10 ) ) { + here.ter_set( dest, t_dirt ); } } // Open blank tiles as long as there's no possible route int tries = 0; - while( g->m.route( g->u.pos(), z->pos(), root_pathfind ).empty() && + while( here.route( player_character.pos(), z->pos(), root_pathfind ).empty() && tries < 20 ) { - point p( rng( g->u.posx(), z->posx() - 3 ), rng( g->u.posy(), z->posy() - 3 ) ); + point p( rng( player_character.posx(), z->posx() - 3 ), + rng( player_character.posy(), z->posy() - 3 ) ); tripoint dest( p, z->posz() ); tries++; - g->m.ter_set( dest, t_dirt ); - if( rl_dist( dest, g->u.pos() ) > 3 && g->num_creatures() < 30 && + here.ter_set( dest, t_dirt ); + if( rl_dist( dest, player_character.pos() ) > 3 && g->num_creatures() < 30 && !g->critter_at( dest ) && one_in( 20 ) ) { // Spawn an extra monster mtype_id montype = mon_triffid; if( one_in( 4 ) ) { @@ -1672,12 +1692,13 @@ bool mattack::fungus( monster *z ) // TODO: Infect NPCs? // It takes a while z->moves -= 200; - if( g->u.has_trait( trait_THRESH_MYCUS ) ) { + Character &player_character = get_player_character(); + if( player_character.has_trait( trait_THRESH_MYCUS ) ) { z->friendly = 100; } //~ the sound of a fungus releasing spores sounds::sound( z->pos(), 10, sounds::sound_t::combat, _( "Pouf!" ), false, "misc", "puff" ); - if( g->u.sees( *z ) ) { + if( player_character.sees( *z ) ) { add_msg( m_warning, _( "Spores are released from the %s!" ), z->name() ); } @@ -1705,15 +1726,16 @@ bool mattack::fungus( monster *z ) } } - fungal_effects fe( *g, g->m ); - for( const tripoint &sporep : g->m.points_in_radius( z->pos(), radius ) ) { + map &here = get_map(); + fungal_effects fe( *g, here ); + for( const tripoint &sporep : here.points_in_radius( z->pos(), radius ) ) { if( sporep == z->pos() ) { continue; } const int dist = rl_dist( z->pos(), sporep ); if( !one_in( dist ) || - g->m.impassable( sporep ) || - ( dist > 1 && !g->m.clear_path( z->pos(), sporep, 2, 1, 10 ) ) ) { + here.impassable( sporep ) || + ( dist > 1 && !here.clear_path( z->pos(), sporep, 2, 1, 10 ) ) ) { continue; } @@ -1727,9 +1749,9 @@ bool mattack::fungus_corporate( monster *z ) { if( x_in_y( 1, 20 ) ) { sounds::sound( z->pos(), 10, sounds::sound_t::speech, _( "\"Buy SpOreos(tm) now!\"" ) ); - if( g->u.sees( *z ) ) { + if( get_player_character().sees( *z ) ) { add_msg( m_warning, _( "Delicious snacks are released from the %s!" ), z->name() ); - g->m.add_item( z->pos(), item( "sporeos" ) ); + get_map().add_item( z->pos(), item( "sporeos" ) ); } // only spawns SpOreos if the player is near; can't have the COMMONERS stealing our product from good customers return true; } else { @@ -1741,12 +1763,13 @@ bool mattack::fungus_haze( monster *z ) { //~ That spore sound again sounds::sound( z->pos(), 10, sounds::sound_t::combat, _( "Pouf!" ), true, "misc", "puff" ); - if( g->u.sees( *z ) ) { + if( get_player_character().sees( *z ) ) { add_msg( m_info, _( "The %s pulses, and fresh fungal material bursts forth." ), z->name() ); } z->moves -= 150; - for( const tripoint &dest : g->m.points_in_radius( z->pos(), 3 ) ) { - g->m.add_field( dest, fd_fungal_haze, rng( 1, 2 ) ); + map &here = get_map(); + for( const tripoint &dest : here.points_in_radius( z->pos(), 3 ) ) { + here.add_field( dest, fd_fungal_haze, rng( 1, 2 ) ); } return true; @@ -1755,16 +1778,17 @@ bool mattack::fungus_haze( monster *z ) bool mattack::fungus_big_blossom( monster *z ) { bool firealarm = false; - const auto u_see = g->u.sees( *z ); + const auto u_see = get_player_character().sees( *z ); + map &here = get_map(); // Fungal fire-suppressor! >:D - for( const tripoint &dest : g->m.points_in_radius( z->pos(), 6 ) ) { - if( g->m.get_field_intensity( dest, fd_fire ) != 0 ) { + for( const tripoint &dest : here.points_in_radius( z->pos(), 6 ) ) { + if( here.get_field_intensity( dest, fd_fire ) != 0 ) { firealarm = true; } if( firealarm ) { - g->m.remove_field( dest, fd_fire ); - g->m.remove_field( dest, fd_smoke ); - g->m.add_field( dest, fd_fungal_haze, 3 ); + here.remove_field( dest, fd_fire ); + here.remove_field( dest, fd_smoke ); + here.add_field( dest, fd_fungal_haze, 3 ); } } // Special effects handled outside the loop @@ -1790,8 +1814,8 @@ bool mattack::fungus_big_blossom( monster *z ) add_msg( m_info, _( "The %s pulses, and fresh fungal material bursts forth!" ), z->name() ); } z->moves -= 150; - for( const tripoint &dest : g->m.points_in_radius( z->pos(), 12 ) ) { - g->m.add_field( dest, fd_fungal_haze, rng( 1, 2 ) ); + for( const tripoint &dest : here.points_in_radius( z->pos(), 12 ) ) { + here.add_field( dest, fd_fungal_haze, rng( 1, 2 ) ); } } @@ -1801,30 +1825,33 @@ bool mattack::fungus_big_blossom( monster *z ) bool mattack::fungus_inject( monster *z ) { // For faster copy+paste - Creature *target = &g->u; - if( rl_dist( z->pos(), g->u.pos() ) > 1 ) { + Creature *target = &get_player_character(); + Character &player_character = get_player_character(); + if( rl_dist( z->pos(), player_character.pos() ) > 1 ) { return false; } - if( g->u.has_trait( trait_THRESH_MARLOSS ) || g->u.has_trait( trait_THRESH_MYCUS ) ) { + if( player_character.has_trait( trait_THRESH_MARLOSS ) || + player_character.has_trait( trait_THRESH_MYCUS ) ) { z->friendly = 1; return true; } - if( ( g->u.has_trait( trait_MARLOSS ) ) && ( g->u.has_trait( trait_MARLOSS_BLUE ) ) && - !g->u.crossed_threshold() ) { + if( ( player_character.has_trait( trait_MARLOSS ) ) && + ( player_character.has_trait( trait_MARLOSS_BLUE ) ) && + !player_character.crossed_threshold() ) { add_msg( m_info, _( "The %s seems to wave you toward the tower…" ), z->name() ); z->anger = 0; return true; } if( z->friendly ) { - // TODO: attack other creatures, not just g->u, for now just skip the code below as it - // only attacks g->u but the monster is friendly. + // TODO: attack other creatures, not just player_character, for now just skip the code below as it + // only attacks player_character but the monster is friendly. return true; } add_msg( m_warning, _( "The %s jabs at you with a needlelike point!" ), z->name() ); z->moves -= 150; - if( g->u.uncanny_dodge() ) { + if( player_character.uncanny_dodge() ) { return true; } @@ -1838,7 +1865,7 @@ bool mattack::fungus_inject( monster *z ) const bodypart_id hit = target->get_random_body_part(); int dam = rng( 5, 11 ); - dam = g->u.deal_damage( z, hit, damage_instance( DT_CUT, dam ) ).total_damage(); + dam = player_character.deal_damage( z, hit, damage_instance( DT_CUT, dam ) ).total_damage(); if( dam > 0 ) { //~ 1$s is monster name, 2$s bodypart in accusative @@ -1846,7 +1873,7 @@ bool mattack::fungus_inject( monster *z ) body_part_name_accusative( hit ) ); if( one_in( 10 - dam ) ) { - g->u.add_effect( effect_fungus, 10_minutes, num_bp, true ); + player_character.add_effect( effect_fungus, 10_minutes, num_bp, true ); add_msg( m_warning, _( "You feel thousands of live spores pumping into you…" ) ); } } else { @@ -1856,14 +1883,16 @@ bool mattack::fungus_inject( monster *z ) } target->on_hit( z, hit, z->type->melee_skill ); - g->u.check_dead_state(); + player_character.check_dead_state(); return true; } bool mattack::fungus_bristle( monster *z ) { - if( g->u.has_trait( trait_THRESH_MARLOSS ) || g->u.has_trait( trait_THRESH_MYCUS ) ) { + Character &player_character = get_player_character(); + if( player_character.has_trait( trait_THRESH_MARLOSS ) || + player_character.has_trait( trait_THRESH_MYCUS ) ) { z->friendly = 1; } Creature *target = z->attack_target(); @@ -1873,7 +1902,7 @@ bool mattack::fungus_bristle( monster *z ) return false; } - auto msg_type = target == &g->u ? m_warning : m_neutral; + auto msg_type = target->is_avatar() ? m_warning : m_neutral; add_msg( msg_type, _( "The %1$s swipes at %2$s with a barbed tendril!" ), z->name(), target->disp_name() ); @@ -1921,7 +1950,7 @@ bool mattack::fungus_bristle( monster *z ) bool mattack::fungus_growth( monster *z ) { // Young fungaloid growing into an adult - if( g->u.sees( *z ) ) { + if( get_player_character().sees( *z ) ) { add_msg( m_warning, _( "The %s grows into an adult!" ), z->name() ); } @@ -1935,8 +1964,9 @@ bool mattack::fungus_sprout( monster *z ) { // To avoid map shift weirdness bool push_player = false; - for( const tripoint &dest : g->m.points_in_radius( z->pos(), 1 ) ) { - if( g->u.pos() == dest ) { + Character &player_character = get_player_character(); + for( const tripoint &dest : get_map().points_in_radius( z->pos(), 1 ) ) { + if( player_character.pos() == dest ) { push_player = true; } if( monster *const wall = g->place_critter_at( mon_fungal_wall, dest ) ) { @@ -1945,9 +1975,9 @@ bool mattack::fungus_sprout( monster *z ) } if( push_player ) { - const int angle = coord_to_angle( z->pos(), g->u.pos() ); + const int angle = coord_to_angle( z->pos(), player_character.pos() ); add_msg( m_bad, _( "You're shoved away as a fungal wall grows!" ) ); - g->fling_creature( &g->u, angle, rng( 10, 50 ) ); + g->fling_creature( &player_character, angle, rng( 10, 50 ) ); } return true; @@ -1959,39 +1989,43 @@ bool mattack::fungus_fortify( monster *z ) // TODO: handle friendly monsters return false; } - Creature *target = &g->u; + Creature *target = &get_player_character(); + Character &player_character = get_player_character(); bool mycus = false; bool peaceful = true; //No nifty support effects. Yet. This lets it rebuild hedges. - if( g->u.has_trait( trait_THRESH_MARLOSS ) || g->u.has_trait( trait_THRESH_MYCUS ) ) { + if( player_character.has_trait( trait_THRESH_MARLOSS ) || + player_character.has_trait( trait_THRESH_MYCUS ) ) { mycus = true; } - if( ( g->u.has_trait( trait_MARLOSS ) ) && ( g->u.has_trait( trait_MARLOSS_BLUE ) ) && - !g->u.crossed_threshold() && !mycus ) { + map &here = get_map(); + if( ( player_character.has_trait( trait_MARLOSS ) ) && + ( player_character.has_trait( trait_MARLOSS_BLUE ) ) && + !player_character.crossed_threshold() && !mycus ) { // You have the other two. Is it really necessary for us to fight? add_msg( m_info, _( "The %s spreads its tendrils. It seems as though it's expecting you…" ), z->name() ); - if( rl_dist( z->pos(), g->u.pos() ) < 3 ) { + if( rl_dist( z->pos(), player_character.pos() ) < 3 ) { if( query_yn( _( "The tower extends and aims several tendrils from its depths. Hold still?" ) ) ) { add_msg( m_warning, _( "The %s works several tendrils into your arms, legs, torso, and even neck…" ), z->name() ); - g->u.hurtall( 1, z ); + player_character.hurtall( 1, z ); add_msg( m_warning, _( "You see a clear golden liquid pump through the tendrils--and then lose consciousness." ) ); - g->u.unset_mutation( trait_MARLOSS ); - g->u.unset_mutation( trait_MARLOSS_BLUE ); - g->u.set_mutation( trait_THRESH_MARLOSS ); - g->m.ter_set( g->u.pos(), + player_character.unset_mutation( trait_MARLOSS ); + player_character.unset_mutation( trait_MARLOSS_BLUE ); + player_character.set_mutation( trait_THRESH_MARLOSS ); + here.ter_set( player_character.pos(), t_marloss ); // We only show you the door. You walk through it on your own. g->memorial().add( pgettext( "memorial_male", "Was shown to the Marloss Gateway." ), pgettext( "memorial_female", "Was shown to the Marloss Gateway." ) ); - g->u.add_msg_if_player( m_good, - _( "You wake up in a marloss bush. Almost *cradled* in it, actually, as though it grew there for you." ) ); - g->u.add_msg_if_player( m_good, - //~ Beginning to hear the Mycus while conscious: this is it speaking - _( "assistance, on an arduous quest. unity. together we have reached the door. now to pass through…" ) ); + add_msg( m_good, + _( "You wake up in a marloss bush. Almost *cradled* in it, actually, as though it grew there for you." ) ); + add_msg( m_good, + //~ Beginning to hear the Mycus while conscious: this is it speaking + _( "assistance, on an arduous quest. unity. together we have reached the door. now to pass through…" ) ); return true; } else { peaceful = false; // You declined the offer. Fight! @@ -2003,8 +2037,8 @@ bool mattack::fungus_fortify( monster *z ) bool fortified = false; bool push_player = false; // To avoid map shift weirdness - for( const tripoint &dest : g->m.points_in_radius( z->pos(), 1 ) ) { - if( g->u.pos() == dest ) { + for( const tripoint &dest : here.points_in_radius( z->pos(), 1 ) ) { + if( player_character.pos() == dest ) { push_player = true; } if( monster *const wall = g->place_critter_at( mon_fungal_hedgerow, dest ) ) { @@ -2014,14 +2048,15 @@ bool mattack::fungus_fortify( monster *z ) } if( push_player ) { add_msg( m_bad, _( "You're shoved away as a fungal hedgerow grows!" ) ); - g->fling_creature( &g->u, coord_to_angle( z->pos(), g->u.pos() ), rng( 10, 50 ) ); + g->fling_creature( &player_character, coord_to_angle( z->pos(), player_character.pos() ), rng( 10, + 50 ) ); } if( fortified || mycus || peaceful ) { return true; } // TODO: De-playerize the whole block - const int dist = rl_dist( z->pos(), g->u.pos() ); + const int dist = rl_dist( z->pos(), player_character.pos() ); if( dist >= 12 ) { return false; } @@ -2036,8 +2071,8 @@ bool mattack::fungus_fortify( monster *z ) //~ %s is bodypart name in accusative. add_msg( m_bad, _( "A fungal tendril bursts forth from the earth and pierces your %s!" ), body_part_name_accusative( convert_bp( hit ).id() ) ); - g->u.deal_damage( z, convert_bp( hit ).id(), damage_instance( DT_CUT, rng( 5, 11 ) ) ); - g->u.check_dead_state(); + player_character.deal_damage( z, convert_bp( hit ).id(), damage_instance( DT_CUT, rng( 5, 11 ) ) ); + player_character.check_dead_state(); // Probably doesn't have spores available *just* yet. Let's be nice. } else if( monster *const tendril = g->place_critter_at( mon_fungal_tendril, hit_pos ) ) { add_msg( m_bad, _( "A fungal tendril bursts forth from the earth!" ) ); @@ -2050,7 +2085,7 @@ bool mattack::fungus_fortify( monster *z ) z->name() ); z->moves -= 150; - if( g->u.uncanny_dodge() ) { + if( player_character.uncanny_dodge() ) { return true; } // Can we dodge the attack? Uses player dodge function % chance (melee.cpp) @@ -2064,13 +2099,13 @@ bool mattack::fungus_fortify( monster *z ) // TODO: 21 damage with no chance to critical isn't scary const bodypart_id hit = target->get_random_body_part(); int dam = rng( 15, 21 ); - dam = g->u.deal_damage( z, hit, damage_instance( DT_STAB, dam ) ).total_damage(); + dam = player_character.deal_damage( z, hit, damage_instance( DT_STAB, dam ) ).total_damage(); if( dam > 0 ) { //~ 1$s is monster name, 2$s bodypart in accusative add_msg( m_bad, _( "The %1$s sinks its point into your %2$s!" ), z->name(), body_part_name_accusative( hit ) ); - g->u.add_effect( effect_fungus, 40_minutes, num_bp, true ); + player_character.add_effect( effect_fungus, 40_minutes, num_bp, true ); add_msg( m_warning, _( "You feel millions of live spores pumping into you…" ) ); } else { //~ 1$s is monster name, 2$s bodypart in accusative @@ -2079,7 +2114,7 @@ bool mattack::fungus_fortify( monster *z ) } target->on_hit( z, hit, z->type->melee_skill ); - g->u.check_dead_state(); + player_character.check_dead_state(); return true; } @@ -2096,7 +2131,7 @@ bool mattack::impale( monster *z ) z->moves -= 80; bool uncanny = target->uncanny_dodge(); if( uncanny || dodge_check( z, target ) ) { - auto msg_type = target == &g->u ? m_warning : m_info; + auto msg_type = target->is_avatar() ? m_warning : m_info; target->add_msg_player_or_npc( msg_type, _( "The %s lunges at you, but you dodge!" ), _( "The %s lunges at , but they dodge!" ), z->name() ); @@ -2111,7 +2146,7 @@ bool mattack::impale( monster *z ) rng( 5, 15 ), .5 ) ).total_damage(); if( dam > 0 ) { - auto msg_type = target == &g->u ? m_bad : m_info; + auto msg_type = target->is_avatar() ? m_bad : m_info; target->add_msg_player_or_npc( msg_type, //~ 1$s is monster name, 2$s bodypart in accusative _( "The %1$s impales your torso!" ), @@ -2167,7 +2202,7 @@ bool mattack::dermatik( monster *z ) } // Can we dodge the attack? Uses player dodge function % chance (melee.cpp) if( dodge_check( z, target ) ) { - if( target == &g->u ) { + if( target->is_avatar() ) { add_msg( _( "The %s tries to land on you, but you dodge." ), z->name() ); } z->stumble(); @@ -2190,10 +2225,11 @@ bool mattack::dermatik( monster *z ) ///\EFFECT_UNARMED increases chance of deflecting dermatik attack with TAIL_CATTLE player_swat += ( ( foe->dex_cur + foe->get_skill_level( skill_unarmed ) ) / 2 ); } + Character &player_character = get_player_character(); if( player_swat > dodge_roll ) { target->add_msg_if_player( _( "The %s lands on you, but you swat it off." ), z->name() ); if( z->get_hp() >= z->get_hp_max() / 2 ) { - z->apply_damage( &g->u, bodypart_id( "torso" ), 1 ); + z->apply_damage( &player_character, bodypart_id( "torso" ), 1 ); z->check_dead_state(); } if( player_swat > dodge_roll * 1.5 ) { @@ -2204,7 +2240,7 @@ bool mattack::dermatik( monster *z ) // Can the bug penetrate our armor? const bodypart_id targeted = target->get_random_body_part(); - if( 4 < g->u.get_armor_cut( targeted ) / 3 ) { + if( 4 < player_character.get_armor_cut( targeted ) / 3 ) { //~ 1$s monster name(dermatik), 2$s bodypart name in accusative. target->add_msg_if_player( _( "The %1$s lands on your %2$s, but can't penetrate your armor." ), z->name(), body_part_name_accusative( targeted ) ); @@ -2229,7 +2265,7 @@ bool mattack::dermatik( monster *z ) bool mattack::dermatik_growth( monster *z ) { // Dermatik larva growing into an adult - if( g->u.sees( *z ) ) { + if( get_player_character().sees( *z ) ) { add_msg( m_warning, _( "The %s dermatik larva grows into an adult!" ), z->name() ); } @@ -2240,20 +2276,22 @@ bool mattack::dermatik_growth( monster *z ) bool mattack::fungal_trail( monster *z ) { - fungal_effects fe( *g, g->m ); + fungal_effects fe( *g, get_map() ); fe.spread_fungus( z->pos() ); return false; } bool mattack::plant( monster *z ) { - fungal_effects fe( *g, g->m ); + map &here = get_map(); + fungal_effects fe( *g, here ); const tripoint monster_position = z->pos(); - const bool is_fungi = g->m.has_flag_ter( "FUNGUS", monster_position ); + const bool is_fungi = here.has_flag_ter( "FUNGUS", monster_position ); // Spores taking seed and growing into a fungaloid fe.spread_fungus( monster_position ); + Character &player_character = get_player_character(); if( is_fungi && one_in( 10 + g->num_creatures() / 5 ) ) { - if( g->u.sees( *z ) ) { + if( player_character.sees( *z ) ) { add_msg( m_warning, _( "The %s takes seed and becomes a young fungaloid!" ), z->name() ); } @@ -2262,7 +2300,7 @@ bool mattack::plant( monster *z ) z->moves -= 1000; // It takes a while return false; } else { - if( g->u.sees( *z ) ) { + if( player_character.sees( *z ) ) { add_msg( _( "The %s falls to the ground and bursts!" ), z->name() ); } @@ -2291,7 +2329,7 @@ static void poly_keep_speed( monster &mon, const mtype_id &id ) static bool blobify( monster &blob, monster &target ) { - if( g->u.sees( target ) ) { + if( get_player_character().sees( target ) ) { add_msg( m_warning, _( "%s is engulfed by %s!" ), target.disp_name(), blob.disp_name() ); } @@ -2417,7 +2455,7 @@ bool mattack::callblobs( monster *z ) // if we want to deal with NPCS and friendly monsters as well. // The strategy is to send about 1/3 of the available blobs after the player, // and keep the rest near the brain blob for protection. - tripoint enemy = g->u.pos(); + tripoint enemy = get_player_character().pos(); std::list allies; std::vector nearby_points = closest_tripoints_first( z->pos(), 3 ); for( monster &candidate : g->all_monsters() ) { @@ -2481,7 +2519,7 @@ bool mattack::jackson( monster *z ) } // Did we convert anybody? if( converted ) { - if( g->u.sees( *z ) ) { + if( get_player_character().sees( *z ) ) { add_msg( m_warning, _( "The %s lets out a high-pitched cry!" ), z->name() ); } } @@ -2491,7 +2529,7 @@ bool mattack::jackson( monster *z ) bool mattack::dance( monster *z ) { - if( g->u.sees( *z ) ) { + if( get_player_character().sees( *z ) ) { switch( rng( 1, 10 ) ) { case 1: add_msg( m_neutral, _( "The %s swings its arms from side to side!" ), z->name() ); @@ -2536,14 +2574,14 @@ bool mattack::dogthing( monster *z ) return false; } - if( !one_in( 3 ) || !g->u.sees( *z ) ) { + if( !one_in( 3 ) || !get_player_character().sees( *z ) ) { return false; } add_msg( _( "The %s's head explodes in a mass of roiling tentacles!" ), z->name() ); - g->m.add_splash( z->bloodType(), z->pos(), 2, 3 ); + get_map().add_splash( z->bloodType(), z->pos(), 2, 3 ); z->friendly = 0; z->poly( mon_headless_dog_thing ); @@ -2561,7 +2599,7 @@ bool mattack::tentacle( monster *z ) if( target == nullptr || rl_dist( z->pos(), target->pos() ) > 3 || !z->sees( *target ) ) { return false; } - game_message_type msg_type = target == &g->u ? m_bad : m_info; + game_message_type msg_type = target->is_avatar() ? m_bad : m_info; target->add_msg_player_or_npc( msg_type, _( "The %s lashes its tentacle at you!" ), _( "The %s lashes its tentacle at !" ), @@ -2615,8 +2653,9 @@ bool mattack::ranged_pull( monster *z ) } player *foe = dynamic_cast< player * >( target ); - std::vector line = g->m.find_clear_path( z->pos(), target->pos() ); - bool seen = g->u.sees( *z ); + map &here = get_map(); + std::vector line = here.find_clear_path( z->pos(), target->pos() ); + bool seen = get_player_character().sees( *z ); for( auto &i : line ) { // Player can't be pulled though bars, furniture, cars or creatures @@ -2631,7 +2670,7 @@ bool mattack::ranged_pull( monster *z ) const bool uncanny = target->uncanny_dodge(); if( uncanny || dodge_check( z, target ) ) { z->moves -= 200; - auto msg_type = foe == &g->u ? m_warning : m_info; + auto msg_type = foe->is_avatar() ? m_warning : m_info; target->add_msg_player_or_npc( msg_type, _( "The %s's arms fly out at you, but you dodge!" ), _( "The %s's arms fly out at , but they dodge!" ), z->name() ); @@ -2662,7 +2701,7 @@ bool mattack::ranged_pull( monster *z ) if( foe != nullptr ) { if( foe->in_vehicle ) { - g->m.unboard_vehicle( foe->pos() ); + here.unboard_vehicle( foe->pos() ); } if( target->is_player() && ( pt.x < HALF_MAPSIZE_X || pt.y < HALF_MAPSIZE_Y || @@ -2681,7 +2720,7 @@ bool mattack::ranged_pull( monster *z ) } // The monster might drag a target that's not on it's z level // So if they leave them on open air, make them fall - g->m.creature_on_trap( *target ); + here.creature_on_trap( *target ); if( seen ) { if( z->type->bodytype == "human" || z->type->bodytype == "angel" ) { add_msg( _( "The %1$s's arms fly out and pull and grab %2$s!" ), z->name(), @@ -2711,7 +2750,7 @@ bool mattack::grab( monster *z ) z->moves -= 80; const bool uncanny = target->uncanny_dodge(); - const auto msg_type = target == &g->u ? m_warning : m_info; + const auto msg_type = target->is_avatar() ? m_warning : m_info; if( uncanny || dodge_check( z, target ) ) { target->add_msg_player_or_npc( msg_type, _( "The %s gropes at you, but you dodge!" ), _( "The %s gropes at , but they dodge!" ), @@ -2805,7 +2844,7 @@ bool mattack::grab_drag( monster *z ) } if( foe != nullptr ) { if( foe->in_vehicle ) { - g->m.unboard_vehicle( foe->pos() ); + get_map().unboard_vehicle( foe->pos() ); } foe->setpos( zpt ); } else { @@ -2870,7 +2909,7 @@ bool mattack::para_sting( monster *z ) bool mattack::triffid_growth( monster *z ) { // Young triffid growing into an adult - if( g->u.sees( *z ) ) { + if( get_player_character().sees( *z ) ) { add_msg( m_warning, _( "The %s young triffid grows into an adult!" ), z->name() ); } @@ -2886,33 +2925,34 @@ bool mattack::stare( monster *z ) return false; } z->moves -= 200; - if( z->sees( g->u ) ) { + Character &player_character = get_player_character(); + if( z->sees( player_character ) ) { //dimensional effects don't take against dimensionally anchored foes. - if( g->u.worn_with_flag( "DIMENSIONAL_ANCHOR" ) || - g->u.has_effect_with_flag( "DIMENSIONAL_ANCHOR" ) ) { + if( player_character.worn_with_flag( "DIMENSIONAL_ANCHOR" ) || + player_character.has_effect_with_flag( "DIMENSIONAL_ANCHOR" ) ) { add_msg( m_warning, _( "You feel a strange reverberation across your body." ) ); return true; } - if( g->u.sees( *z ) ) { + if( player_character.sees( *z ) ) { add_msg( m_bad, _( "The %s stares at you, and you shudder." ), z->name() ); } else { add_msg( m_bad, _( "You feel like you're being watched, it makes you sick." ) ); } - g->u.add_effect( effect_taint, rng( 2_minutes, 5_minutes ) ); + player_character.add_effect( effect_taint, rng( 2_minutes, 5_minutes ) ); //Check severity before adding more debuffs - if( g->u.get_effect_int( effect_taint ) > 2 ) { - g->u.add_effect( effect_hallu, 30_minutes ); + if( player_character.get_effect_int( effect_taint ) > 2 ) { + player_character.add_effect( effect_hallu, 30_minutes ); //Check if target is a player before spawning hallucinations - if( g->u.is_player() && one_in( 2 ) ) { - g->spawn_hallucination( g->u.pos() + tripoint( rng( -10, 10 ), rng( -10, 10 ), 0 ) ); + if( player_character.is_player() && one_in( 2 ) ) { + g->spawn_hallucination( player_character.pos() + tripoint( rng( -10, 10 ), rng( -10, 10 ), 0 ) ); } if( one_in( 12 ) ) { - g->u.add_effect( effect_blind, 5_minutes ); + player_character.add_effect( effect_blind, 5_minutes ); add_msg( m_bad, _( "Your sight darkens as the visions overtake you!" ) ); } } - if( g->u.get_effect_int( effect_taint ) >= 3 && one_in( 12 ) ) { - g->u.add_effect( effect_tindrift, 1_turns ); + if( player_character.get_effect_int( effect_taint ) >= 3 && one_in( 12 ) ) { + player_character.add_effect( effect_tindrift, 1_turns ); } } return true; @@ -2924,15 +2964,17 @@ bool mattack::fear_paralyze( monster *z ) // TODO: handle friendly monsters return false; } - if( g->u.sees( *z ) && !g->u.has_effect( effect_fearparalyze ) ) { - if( g->u.has_artifact_with( AEP_PSYSHIELD ) || ( g->u.worn_with_flag( "PSYSHIELD_PARTIAL" ) && - one_in( 4 ) ) ) { + Character &player_character = get_player_character(); + if( player_character.sees( *z ) && !player_character.has_effect( effect_fearparalyze ) ) { + if( player_character.has_artifact_with( AEP_PSYSHIELD ) || + ( player_character.worn_with_flag( "PSYSHIELD_PARTIAL" ) && + one_in( 4 ) ) ) { add_msg( _( "The %s probes your mind, but is rebuffed!" ), z->name() ); ///\EFFECT_INT decreases chance of being paralyzed by fear attack - } else if( rng( 0, 20 ) > g->u.get_int() ) { + } else if( rng( 0, 20 ) > player_character.get_int() ) { add_msg( m_bad, _( "The terrifying visage of the %s paralyzes you." ), z->name() ); - g->u.add_effect( effect_fearparalyze, 5_turns ); - g->u.moves -= 4 * g->u.get_speed(); + player_character.add_effect( effect_fearparalyze, 5_turns ); + player_character.moves -= 4 * player_character.get_speed(); } else { add_msg( _( "You manage to avoid staring at the horrendous %s." ), z->name() ); } @@ -2945,10 +2987,11 @@ bool mattack::nurse_check_up( monster *z ) bool found_target = false; player *target = nullptr; tripoint tmp_pos( z->pos() + point( 12, 12 ) ); - for( auto critter : g->m.get_creatures_in_radius( z->pos(), 6 ) ) { + map &here = get_map(); + for( auto critter : here.get_creatures_in_radius( z->pos(), 6 ) ) { player *tmp_player = dynamic_cast( critter ); if( tmp_player != nullptr && z->sees( *tmp_player ) && - g->m.clear_path( z->pos(), tmp_player->pos(), 10, 0, + here.clear_path( z->pos(), tmp_player->pos(), 10, 0, 100 ) ) { // no need to scan players we can't reach if( rl_dist( z->pos(), tmp_player->pos() ) < rl_dist( z->pos(), tmp_pos ) ) { tmp_pos = tmp_player->pos(); @@ -2974,7 +3017,7 @@ bool mattack::nurse_check_up( monster *z ) sounds::sound( z->pos(), 8, sounds::sound_t::electronic_speech, string_format( _( "a soft robotic voice say, \"Here we go. Just hold still.\"" ) ) ); - if( target == &g->u ) { + if( target->is_avatar() ) { add_msg( m_good, _( "You get a medical check-up." ) ); } target->add_effect( effect_got_checked, 10_turns ); @@ -2987,7 +3030,7 @@ bool mattack::nurse_check_up( monster *z ) bool mattack::nurse_assist( monster *z ) { - const bool u_see = g->u.sees( *z ); + const bool u_see = get_player_character().sees( *z ); if( u_see && one_in( 100 ) ) { add_msg( m_info, _( "The %s is scanning its surroundings." ), z->name() ); @@ -2995,12 +3038,13 @@ bool mattack::nurse_assist( monster *z ) bool found_target = false; player *target = nullptr; + map &here = get_map(); tripoint tmp_pos( z->pos() + point( 12, 12 ) ); - for( auto critter : g->m.get_creatures_in_radius( z->pos(), 6 ) ) { + for( auto critter : here.get_creatures_in_radius( z->pos(), 6 ) ) { player *tmp_player = dynamic_cast( critter ); // No need to scan players we can't reach if( tmp_player != nullptr && z->sees( *tmp_player ) && - g->m.clear_path( z->pos(), tmp_player->pos(), 10, 0, 100 ) ) { + here.clear_path( z->pos(), tmp_player->pos(), 10, 0, 100 ) ) { if( rl_dist( z->pos(), tmp_player->pos() ) < rl_dist( z->pos(), tmp_pos ) ) { tmp_pos = tmp_player->pos(); target = tmp_player; @@ -3029,14 +3073,15 @@ bool mattack::nurse_operate( monster *z ) if( z->has_effect( effect_dragging ) || z->has_effect( effect_operating ) ) { return false; } - const bool u_see = g->u.sees( *z ); + Character &player_character = get_player_character(); + const bool u_see = player_character.sees( *z ); if( u_see && one_in( 100 ) ) { add_msg( m_info, _( "The %s is scanning its surroundings." ), z->name() ); } - if( ( ( g->u.is_wearing( itype_badge_doctor ) || - z->attitude_to( g->u ) == Creature::Attitude::FRIENDLY ) && u_see ) && one_in( 100 ) ) { + if( ( ( player_character.is_wearing( itype_badge_doctor ) || + z->attitude_to( player_character ) == Creature::Attitude::FRIENDLY ) && u_see ) && one_in( 100 ) ) { add_msg( m_info, _( "The %s doesn't seem to register you as a doctor." ), z->name() ); } @@ -3050,12 +3095,13 @@ bool mattack::nurse_operate( monster *z ) bool found_target = false; player *target = nullptr; + map &here = get_map(); tripoint tmp_pos( z->pos() + point( 12, 12 ) ); - for( auto critter : g->m.get_creatures_in_radius( z->pos(), 6 ) ) { + for( auto critter : here.get_creatures_in_radius( z->pos(), 6 ) ) { player *tmp_player = dynamic_cast< player *>( critter ); // No need to scan players we can't reach if( tmp_player != nullptr && z->sees( *tmp_player ) && - g->m.clear_path( z->pos(), tmp_player->pos(), 10, 0, 100 ) ) { + here.clear_path( z->pos(), tmp_player->pos(), 10, 0, 100 ) ) { if( tmp_player->has_any_bionic() ) { if( rl_dist( z->pos(), tmp_player->pos() ) < rl_dist( z->pos(), tmp_pos ) ) { tmp_pos = tmp_player->pos(); @@ -3065,7 +3111,7 @@ bool mattack::nurse_operate( monster *z ) } } } - if( found_target && z->attitude_to( g->u ) == Creature::Attitude::FRIENDLY ) { + if( found_target && z->attitude_to( player_character ) == Creature::Attitude::FRIENDLY ) { // 50% chance to not turn hostile again if( one_in( 2 ) ) { return false; @@ -3080,7 +3126,7 @@ bool mattack::nurse_operate( monster *z ) z->friendly = 0; z->anger = 100; - std::list couch_pos = g->m.find_furnitures_with_flag_in_radius( z->pos(), 10, + std::list couch_pos = here.find_furnitures_with_flag_in_radius( z->pos(), 10, flag_AUTODOC_COUCH ); if( couch_pos.empty() ) { @@ -3093,7 +3139,7 @@ bool mattack::nurse_operate( monster *z ) // Check if target is already grabbed by something else if( target->has_effect( effect_grabbed ) ) { - for( auto critter : g->m.get_creatures_in_radius( target->pos(), 1 ) ) { + for( auto critter : here.get_creatures_in_radius( target->pos(), 1 ) ) { monster *mon = dynamic_cast( critter ); if( mon != nullptr && mon != z ) { if( mon->type->id != mon_nursebot_defective ) { @@ -3135,7 +3181,7 @@ bool mattack::check_money_left( monster *z ) if( !z->inv.empty() ) { for( const item &it : z->inv ) { - g->m.add_item_or_charges( z->pos(), it ); + get_map().add_item_or_charges( z->pos(), it ); } z->inv.clear(); z->remove_effect( effect_has_bag ); @@ -3175,12 +3221,13 @@ bool mattack::photograph( monster *z ) return false; } + Character &player_character = get_player_character(); // Badges should NOT be swappable between roles. // Hence separate checking. // If you are in fact listed as a police officer - if( g->u.has_trait( trait_PROF_POLICE ) ) { + if( player_character.has_trait( trait_PROF_POLICE ) ) { // And you're wearing your badge - if( g->u.is_wearing( itype_badge_deputy ) ) { + if( player_character.is_wearing( itype_badge_deputy ) ) { if( one_in( 3 ) ) { add_msg( m_info, _( "The %s flashes a LED and departs. Human officer on scene." ), z->name() ); @@ -3198,9 +3245,9 @@ bool mattack::photograph( monster *z ) } } - if( g->u.has_trait( trait_PROF_PD_DET ) ) { + if( player_character.has_trait( trait_PROF_PD_DET ) ) { // And you have your shield on - if( g->u.is_wearing( itype_badge_detective ) ) { + if( player_character.is_wearing( itype_badge_detective ) ) { if( one_in( 4 ) ) { add_msg( m_info, _( "The %s flashes a LED and departs. Human officer on scene." ), z->name() ); @@ -3216,9 +3263,9 @@ bool mattack::photograph( monster *z ) return true; } } - } else if( g->u.has_trait( trait_PROF_SWAT ) ) { + } else if( player_character.has_trait( trait_PROF_SWAT ) ) { // And you're wearing your badge - if( g->u.is_wearing( itype_badge_swat ) ) { + if( player_character.is_wearing( itype_badge_swat ) ) { if( one_in( 3 ) ) { add_msg( m_info, _( "The %s flashes a LED and departs. SWAT's working the area." ), z->name() ); @@ -3233,9 +3280,9 @@ bool mattack::photograph( monster *z ) return true; } } - } else if( g->u.has_trait( trait_PROF_CYBERCO ) ) { + } else if( player_character.has_trait( trait_PROF_CYBERCO ) ) { // And you're wearing your badge - if( g->u.is_wearing( itype_badge_cybercop ) ) { + if( player_character.is_wearing( itype_badge_cybercop ) ) { if( one_in( 3 ) ) { add_msg( m_info, _( "The %s winks a LED and departs. One machine to another?" ), z->name() ); @@ -3253,9 +3300,9 @@ bool mattack::photograph( monster *z ) } } - if( g->u.has_trait( trait_PROF_FED ) ) { + if( player_character.has_trait( trait_PROF_FED ) ) { // And you're wearing your badge - if( g->u.is_wearing( itype_badge_marshal ) ) { + if( player_character.is_wearing( itype_badge_marshal ) ) { add_msg( m_info, _( "The %s flashes a LED and departs. The Feds got this." ), z->name() ); z->no_corpse_quiet = true; z->no_extra_death_drops = true; @@ -3264,7 +3311,7 @@ bool mattack::photograph( monster *z ) } } - if( z->friendly || g->u.weapon.typeId() == itype_e_handcuffs ) { + if( z->friendly || player_character.weapon.typeId() == itype_e_handcuffs ) { // Friendly (hacked?) bot ignore the player. Arrested suspect ignored too. // TODO: might need to be revisited when it can target npcs. return false; @@ -3274,23 +3321,23 @@ bool mattack::photograph( monster *z ) // TODO: Make the player known to the faction std::string cname = _( "…database connection lost!" ); if( one_in( 6 ) ) { - cname = Name::generate( g->u.male ); + cname = Name::generate( player_character.male ); } else if( one_in( 3 ) ) { - cname = g->u.name; + cname = player_character.name; } sounds::sound( z->pos(), 15, sounds::sound_t::alert, string_format( _( "a robotic voice boom, \"Citizen %s!\"" ), cname ), false, "speech", z->type->id.str() ); - if( g->u.weapon.is_gun() ) { + if( player_character.weapon.is_gun() ) { sounds::sound( z->pos(), 15, sounds::sound_t::alert, _( "\"Drop your gun! Now!\"" ) ); - } else if( g->u.is_armed() ) { + } else if( player_character.is_armed() ) { sounds::sound( z->pos(), 15, sounds::sound_t::alert, _( "\"Drop your weapon! Now!\"" ) ); } const SpeechBubble &speech = get_speech( z->type->id.str() ); sounds::sound( z->pos(), speech.volume, sounds::sound_t::alert, speech.text.translated() ); g->timed_events.add( timed_event_type::ROBOT_ATTACK, calendar::turn + rng( 15_turns, 30_turns ), 0, - g->u.global_sm_location() ); + player_character.global_sm_location() ); return true; } @@ -3323,7 +3370,8 @@ void mattack::taze( monster *z, Creature *target ) return; } - auto m_type = target->attitude_to( g->u ) == Creature::Attitude::FRIENDLY ? m_bad : m_neutral; + auto m_type = target->attitude_to( get_player_character() ) == Creature::Attitude::FRIENDLY ? + m_bad : m_neutral; target->add_msg_player_or_npc( m_type, _( "The %s shocks you!" ), _( "The %s shocks !" ), @@ -3347,7 +3395,7 @@ void mattack::rifle( monster *z, Creature *target ) // No need to aim tmp.recoil = 0; - if( target == &g->u ) { + if( target->is_avatar() ) { if( !z->has_effect( effect_targeted ) ) { sounds::sound( z->pos(), 8, sounds::sound_t::alarm, _( "beep-beep." ), false, "misc", "beep" ); z->add_effect( effect_targeted, 8_turns ); @@ -3366,7 +3414,7 @@ void mattack::rifle( monster *z, Creature *target ) } return; } - if( g->u.sees( *z ) ) { + if( get_player_character().sees( *z ) ) { add_msg( m_warning, _( "The %s opens up with its rifle!" ), z->name() ); } @@ -3375,7 +3423,7 @@ void mattack::rifle( monster *z, Creature *target ) z->ammo[ ammo_type ] -= tmp.fire_gun( target->pos(), burst ) * tmp.weapon.ammo_required(); - if( target == &g->u ) { + if( target->is_avatar() ) { z->add_effect( effect_targeted, 3_turns ); } } @@ -3390,9 +3438,10 @@ void mattack::frag( monster *z, Creature *target ) // This is for the bots, not z->ammo[ammo_type] = 200; } - if( target == &g->u ) { + Character &player_character = get_player_character(); + if( target->is_avatar() ) { if( !z->has_effect( effect_targeted ) ) { - if( g->u.has_trait( trait_PROF_CHURL ) ) { + if( player_character.has_trait( trait_PROF_CHURL ) ) { //~ Potential grenading detected. add_msg( m_warning, _( "Thee eye o dat divil be upon me!" ) ); } else { @@ -3400,7 +3449,7 @@ void mattack::frag( monster *z, Creature *target ) // This is for the bots, not add_msg( m_warning, _( "Those laser dots don't seem very friendly…" ) ); } // Effect removed in game.cpp, duration doesn't much matter - g->u.add_effect( effect_laserlocked, 3_turns ); + player_character.add_effect( effect_laserlocked, 3_turns ); sounds::sound( z->pos(), 10, sounds::sound_t::electronic_speech, _( "Targeting." ), false, "speech", z->type->id.str() ); z->add_effect( effect_targeted, 5_turns ); @@ -3426,7 +3475,7 @@ void mattack::frag( monster *z, Creature *target ) // This is for the bots, not } return; } - if( g->u.sees( *z ) ) { + if( player_character.sees( *z ) ) { add_msg( m_warning, _( "The %s's grenade launcher fires!" ), z->name() ); } @@ -3435,7 +3484,7 @@ void mattack::frag( monster *z, Creature *target ) // This is for the bots, not z->ammo[ ammo_type ] -= tmp.fire_gun( target->pos(), burst ) * tmp.weapon.ammo_required(); - if( target == &g->u ) { + if( target->is_avatar() ) { z->add_effect( effect_targeted, 3_turns ); } } @@ -3486,7 +3535,7 @@ void mattack::tankgun( monster *z, Creature *target ) } return; } - if( g->u.sees( *z ) ) { + if( get_player_character().sees( *z ) ) { add_msg( m_warning, _( "The %s's 120mm cannon fires!" ), z->name() ); } tmp.weapon = item( "TANK" ).ammo_set( ammo_type, z->ammo[ ammo_type ] ); @@ -3509,6 +3558,7 @@ bool mattack::searchlight( monster *z ) const int zposx = z->posx(); const int zposy = z->posy(); + map &here = get_map(); //this searchlight is not initialized if( z->inv.empty() ) { @@ -3521,7 +3571,7 @@ bool mattack::searchlight( monster *z ) settings.set_var( "SL_PREFER_RIGHT", "TRUE" ); settings.set_var( "SL_PREFER_LEFT", "TRUE" ); - for( const tripoint &dest : g->m.points_in_radius( z->pos(), 24 ) ) { + for( const tripoint &dest : here.points_in_radius( z->pos(), 24 ) ) { const monster *const mon = g->critter_at( dest ); if( mon && mon->type->id == mon_turret_searchlight ) { if( dest.x < zposx ) { @@ -3554,7 +3604,7 @@ bool mattack::searchlight( monster *z ) for( int x = zposx - 24; x < zposx + 24; x++ ) { for( int y = zposy - 24; y < zposy + 24; y++ ) { tripoint dest( x, y, z->posz() ); - if( g->m.ter( dest ) == ter_str_id( "t_plut_generator" ) ) { + if( here.ter( dest ) == ter_str_id( "t_plut_generator" ) ) { generator_ok = true; } } @@ -3569,6 +3619,7 @@ bool mattack::searchlight( monster *z ) } } + Character &player_character = get_player_character(); for( int i = 0; i < max_lamp_count; i++ ) { item &settings = z->inv[i]; @@ -3603,7 +3654,7 @@ bool mattack::searchlight( monster *z ) for( int i = 0; i < rng( 1, 2 ); i++ ) { - if( !z->sees( g->u ) ) { + if( !z->sees( player_character ) ) { shift = settings.get_var( "SL_DIR", shift ); switch( shift ) { @@ -3641,16 +3692,16 @@ bool mattack::searchlight( monster *z ) } } else { - if( x < g->u.posx() ) { + if( x < player_character.posx() ) { x++; } - if( x > g->u.posx() ) { + if( x > player_character.posx() ) { x--; } - if( y < g->u.posy() ) { + if( y < player_character.posy() ) { y++; } - if( y > g->u.posy() ) { + if( y > player_character.posy() ) { y--; } } @@ -3674,7 +3725,7 @@ bool mattack::searchlight( monster *z ) settings.set_var( "SL_SPOT_X", x - zposx ); settings.set_var( "SL_SPOT_Y", y - zposy ); - g->m.add_field( tripoint( x, y, z->posz() ), fd_spotlight, 1 ); + here.add_field( tripoint( x, y, z->posz() ), fd_spotlight, 1 ); } @@ -3687,6 +3738,7 @@ bool mattack::flamethrower( monster *z ) // TODO: handle friendly monsters return false; } + Character &player_character = get_player_character(); // TODO: that is always false! if( z->friendly != 0 ) { // Attacking monsters, not the player! @@ -3695,7 +3747,7 @@ bool mattack::flamethrower( monster *z ) // Couldn't find any targets! if( target == nullptr ) { // Because that stupid oaf was in the way! - if( boo_hoo > 0 && g->u.sees( *z ) ) { + if( boo_hoo > 0 && player_character.sees( *z ) ) { add_msg( m_warning, ngettext( "Pointed in your direction, the %s emits an IFF warning beep.", "Pointed in your direction, the %s emits %d annoyed sounding beeps.", boo_hoo ), @@ -3712,7 +3764,7 @@ bool mattack::flamethrower( monster *z ) return false; } - flame( z, &g->u ); + flame( z, &player_character ); return true; } @@ -3720,27 +3772,29 @@ bool mattack::flamethrower( monster *z ) void mattack::flame( monster *z, Creature *target ) { int dist = rl_dist( z->pos(), target->pos() ); - if( target != &g->u ) { + Character &player_character = get_player_character(); + map &here = get_map(); + if( target != &player_character ) { // friendly // It takes a while z->moves -= 500; - if( !g->m.sees( z->pos(), target->pos(), dist ) ) { + if( !here.sees( z->pos(), target->pos(), dist ) ) { // shouldn't happen debugmsg( "mattack::flame invoked on invisible target" ); } - std::vector traj = g->m.find_clear_path( z->pos(), target->pos() ); + std::vector traj = here.find_clear_path( z->pos(), target->pos() ); for( auto &i : traj ) { // break out of attack if flame hits a wall // TODO: Z - if( g->m.hit_with_fire( tripoint( i.xy(), z->posz() ) ) ) { - if( g->u.sees( i ) ) { + if( here.hit_with_fire( tripoint( i.xy(), z->posz() ) ) ) { + if( player_character.sees( i ) ) { add_msg( _( "The tongue of flame hits the %s!" ), - g->m.tername( i.xy() ) ); + here.tername( i.xy() ) ); } return; } - g->m.add_field( i, fd_fire, 1 ); + here.add_field( i, fd_fire, 1 ); } target->add_effect( effect_onfire, 8_turns, bp_torso ); @@ -3749,22 +3803,22 @@ void mattack::flame( monster *z, Creature *target ) // It takes a while z->moves -= 500; - if( !g->m.sees( z->pos(), target->pos(), dist + 1 ) ) { + if( !here.sees( z->pos(), target->pos(), dist + 1 ) ) { // shouldn't happen debugmsg( "mattack::flame invoked on invisible target" ); } - std::vector traj = g->m.find_clear_path( z->pos(), target->pos() ); + std::vector traj = here.find_clear_path( z->pos(), target->pos() ); for( auto &i : traj ) { // break out of attack if flame hits a wall - if( g->m.hit_with_fire( tripoint( i.xy(), z->posz() ) ) ) { - if( g->u.sees( i ) ) { + if( here.hit_with_fire( tripoint( i.xy(), z->posz() ) ) ) { + if( player_character.sees( i ) ) { add_msg( _( "The tongue of flame hits the %s!" ), - g->m.tername( i.xy() ) ); + here.tername( i.xy() ) ); } return; } - g->m.add_field( i, fd_fire, 1 ); + here.add_field( i, fd_fire, 1 ); } if( !target->uncanny_dodge() ) { target->add_effect( effect_onfire, 8_turns, bp_torso ); @@ -3825,6 +3879,7 @@ bool mattack::chickenbot( monster *z ) int mode = 0; int boo_hoo = 0; Creature *target; + Character &player_character = get_player_character(); if( z->friendly == 0 ) { target = z->attack_target(); if( target == nullptr ) { @@ -3833,7 +3888,7 @@ bool mattack::chickenbot( monster *z ) } else { target = z->auto_find_hostile_target( 38, boo_hoo ); if( target == nullptr ) { - if( boo_hoo > 0 && g->u.sees( *z ) ) { // because that stupid oaf was in the way! + if( boo_hoo > 0 && player_character.sees( *z ) ) { // because that stupid oaf was in the way! add_msg( m_warning, ngettext( "Pointed in your direction, the %s emits an IFF warning beep.", "Pointed in your direction, the %s emits %d annoyed sounding beeps.", boo_hoo ), @@ -3854,13 +3909,13 @@ bool mattack::chickenbot( monster *z ) } int dist = rl_dist( z->pos(), target->pos() ); - int player_dist = rl_dist( target->pos(), g->u.pos() ); + int player_dist = rl_dist( target->pos(), player_character.pos() ); if( dist == 1 && one_in( 2 ) ) { // Use tazer at point-blank range, and even then, not continuously. mode = 1; } else if( ( z->friendly == 0 || player_dist >= 6 ) && // Avoid shooting near player if we're friendly. - ( dist >= 12 || ( g->u.in_vehicle && dist >= 6 ) ) ) { + ( dist >= 12 || ( player_character.in_vehicle && dist >= 6 ) ) ) { // Only use at long range, unless player is in a vehicle, then tolerate closer targeting. mode = 3; } else if( dist >= 4 ) { @@ -3907,6 +3962,7 @@ bool mattack::multi_robot( monster *z ) int mode = 0; int boo_hoo = 0; Creature *target; + Character &player_character = get_player_character(); if( z->friendly == 0 ) { target = z->attack_target(); if( target == nullptr ) { @@ -3915,7 +3971,7 @@ bool mattack::multi_robot( monster *z ) } else { target = z->auto_find_hostile_target( 48, boo_hoo ); if( target == nullptr ) { - if( boo_hoo > 0 && g->u.sees( *z ) ) { // because that stupid oaf was in the way! + if( boo_hoo > 0 && player_character.sees( *z ) ) { // because that stupid oaf was in the way! add_msg( m_warning, ngettext( "Pointed in your direction, the %s emits an IFF warning beep.", "Pointed in your direction, the %s emits %d annoyed sounding beeps.", boo_hoo ), @@ -3940,7 +3996,7 @@ bool mattack::multi_robot( monster *z ) mode = 1; } else if( dist <= 30 ) { mode = 2; - } else if( ( target == &g->u && g->u.in_vehicle ) || + } else if( ( target->is_avatar() && player_character.in_vehicle ) || z->friendly != 0 || cap > 4 ) { // Primary only kicks in if you're in a vehicle or are big enough to be mistaken for one. @@ -3985,8 +4041,10 @@ bool mattack::ratking( monster *z ) // TODO: handle friendly monsters return false; } + Character &player_character = get_player_character(); // Disable z-level ratting or it can get silly - if( rl_dist( z->pos(), g->u.pos() ) > 50 || z->posz() != g->u.posz() ) { + if( rl_dist( z->pos(), player_character.pos() ) > 50 || + z->posz() != player_character.posz() ) { return false; } @@ -4007,8 +4065,8 @@ bool mattack::ratking( monster *z ) add_msg( m_warning, _( "\"FOUL INTERLOPER…\"" ) ); break; } - if( rl_dist( z->pos(), g->u.pos() ) <= 10 ) { - g->u.add_effect( effect_rat, 3_minutes ); + if( rl_dist( z->pos(), player_character.pos() ) <= 10 ) { + player_character.add_effect( effect_rat, 3_minutes ); } return true; @@ -4052,11 +4110,12 @@ bool mattack::upgrade( monster *z ) monster *target = random_entry( targets ); std::string old_name = target->name(); - const auto could_see = g->u.sees( *target ); + Character &player_character = get_player_character(); + const bool could_see = player_character.sees( *target ); target->hasten_upgrade(); target->try_upgrade( false ); - const auto can_see = g->u.sees( *target ); - if( g->u.sees( *z ) ) { + const bool can_see = player_character.sees( *target ); + if( player_character.sees( *z ) ) { if( could_see ) { //~ %1$s is the name of the zombie upgrading the other, %2$s is the zombie being upgraded. add_msg( m_warning, _( "A black mist floats from the %1$s around the %2$s." ), @@ -4087,7 +4146,7 @@ bool mattack::breathe( monster *z ) bool able = ( z->type->id == mon_breather_hub ); if( !able ) { - for( const tripoint &dest : g->m.points_in_radius( z->pos(), 3 ) ) { + for( const tripoint &dest : get_map().points_in_radius( z->pos(), 3 ) ) { monster *const mon = g->critter_at( dest ); if( mon && mon->type->id == mon_breather_hub ) { able = true; @@ -4122,12 +4181,13 @@ bool mattack::stretch_bite( monster *z ) z->moves -= 150; - for( auto &pnt : g->m.find_clear_path( z->pos(), target->pos() ) ) { - if( g->m.impassable( pnt ) ) { + map &here = get_map(); + for( auto &pnt : here.find_clear_path( z->pos(), target->pos() ) ) { + if( here.impassable( pnt ) ) { z->add_effect( effect_stunned, 6_turns ); target->add_msg_player_or_npc( _( "The %1$s stretches its head at you, but bounces off the %2$s" ), _( "The %1$s stretches its head at , but bounces off the %2$s" ), - z->name(), g->m.obstacle_name( pnt ) ); + z->name(), here.obstacle_name( pnt ) ); return true; } } @@ -4136,7 +4196,7 @@ bool mattack::stretch_bite( monster *z ) if( uncanny || dodge_check( z, target ) ) { z->moves -= 150; z->add_effect( effect_stunned, 3_turns ); - auto msg_type = target == &g->u ? m_warning : m_info; + auto msg_type = target->is_avatar() ? m_warning : m_info; target->add_msg_player_or_npc( msg_type, _( "The %s's head extends to bite you, but you dodge and the head sails past!" ), _( "The %s's head extends to bite , but they dodge and the head sails past!" ), @@ -4154,7 +4214,7 @@ bool mattack::stretch_bite( monster *z ) dam = target->deal_damage( z, hit, damage_instance( DT_STAB, dam ) ).total_damage(); if( dam > 0 ) { - auto msg_type = target == &g->u ? m_bad : m_info; + auto msg_type = target->is_avatar() ? m_bad : m_info; target->add_msg_player_or_npc( msg_type, //~ 1$s is monster name, 2$s bodypart in accusative _( "The %1$s's teeth sink into your %2$s!" ), @@ -4191,7 +4251,7 @@ bool mattack::brandish( monster *z ) return false; } // Only brandish if we can see you! - if( !z->sees( g->u ) ) { + if( !z->sees( get_player_character() ) ) { return false; } add_msg( m_warning, _( "He's brandishing a knife!" ) ); @@ -4231,7 +4291,7 @@ bool mattack::flesh_golem( monster *z ) // No attacking through floor, even if we can see the target somehow return false; } - if( g->u.sees( *z ) ) { + if( get_player_character().sees( *z ) ) { add_msg( _( "The %1$s swings a massive claw at %2$s!" ), z->name(), target->disp_name() ); } @@ -4273,9 +4333,11 @@ bool mattack::absorb_meat( monster *z ) const int max_meat_absorbed = monster_volume / 10.0 * average_meat_chunk_volume; //For every milliliter of meat absorbed, heal this many HP const float meat_absorption_factor = 0.01; + Character &player_character = get_player_character(); + map &here = get_map(); //Search surrounding tiles for meat - for( const auto &p : g->m.points_in_radius( z->pos(), 1 ) ) { - map_stack items = g->m.i_at( p ); + for( const auto &p : here.points_in_radius( z->pos(), 1 ) ) { + map_stack items = here.i_at( p ); for( auto ¤t_item : items ) { const material_id current_item_material = current_item.get_base_material().ident(); if( current_item_material == material_id( "flesh" ) || @@ -4295,15 +4357,15 @@ bool mattack::absorb_meat( monster *z ) int meat_absorbed = std::min( max_meat_absorbed, rng( 1, total_charges ) ); const int hp_to_heal = meat_absorbed * ml_per_charge * meat_absorption_factor; z->heal( hp_to_heal, true ); - g->m.use_charges( p, 0, current_item.type->get_id(), meat_absorbed ); + here.use_charges( p, 0, current_item.type->get_id(), meat_absorbed ); } else { //Only absorb one meaty item int meat_absorbed = 1; const int hp_to_heal = meat_absorbed * ml_per_charge * meat_absorption_factor; z->heal( hp_to_heal, true ); - g->m.use_amount( p, 0, current_item.type->get_id(), meat_absorbed ); + here.use_amount( p, 0, current_item.type->get_id(), meat_absorbed ); } - if( g->u.sees( *z ) ) { + if( player_character.sees( *z ) ) { add_msg( m_warning, _( "The %1$s absorbs the %2$s, growing larger." ), z->name(), current_item.tname() ); add_msg( m_debug, "The %1$s now has %2$s out of %3$s hp", z->name(), z->get_hp(), @@ -4333,7 +4395,7 @@ bool mattack::lunge( monster *z ) return false; } - bool seen = g->u.sees( *z ); + bool seen = get_player_character().sees( *z ); if( dist > 1 ) { if( one_in( 5 ) ) { // Out of range @@ -4372,7 +4434,7 @@ bool mattack::lunge( monster *z ) int dam = rng( 3, 7 ); dam = target->deal_damage( z, hit, damage_instance( DT_BASH, dam ) ).total_damage(); if( dam > 0 ) { - auto msg_type = target == &g->u ? m_bad : m_warning; + auto msg_type = target->is_avatar() ? m_bad : m_warning; target->add_msg_player_or_npc( msg_type, _( "The %1$s lunges at your %2$s, battering it for %3$d damage!" ), _( "The %1$s lunges at 's %2$s, battering it for %3$d damage!" ), @@ -4405,16 +4467,17 @@ bool mattack::longswipe( monster *z ) if( rl_dist( z->pos(), target->pos() ) > 3 || !z->sees( *target ) ) { return false; } + map &here = get_map(); //Is there something impassable blocking the claw? - for( const auto &pnt : g->m.find_clear_path( z->pos(), target->pos() ) ) { - if( g->m.impassable( pnt ) ) { + for( const auto &pnt : here.find_clear_path( z->pos(), target->pos() ) ) { + if( here.impassable( pnt ) ) { //If we're here, it's an nonadjacent attack, which is only attempted 1/5 of the time. if( !one_in( 5 ) ) { return false; } target->add_msg_player_or_npc( _( "The %1$s thrusts a claw at you, but it bounces off the %2$s!" ), _( "The %1$s thrusts a claw at , but it bounces off the %2$s!" ), - z->name(), g->m.obstacle_name( pnt ) ); + z->name(), here.obstacle_name( pnt ) ); z->mod_moves( -150 ); return true; } @@ -4440,7 +4503,7 @@ bool mattack::longswipe( monster *z ) int dam = rng( 3, 7 ); dam = target->deal_damage( z, hit, damage_instance( DT_CUT, dam ) ).total_damage(); if( dam > 0 ) { - auto msg_type = target == &g->u ? m_bad : m_warning; + auto msg_type = target->is_avatar() ? m_bad : m_warning; target->add_msg_player_or_npc( msg_type, //~ 1$s is bodypart name, 2$d is damage value. _( "The %1$s thrusts a claw at your %2$s, slashing it for %3$d damage!" ), @@ -4477,7 +4540,7 @@ bool mattack::longswipe( monster *z ) dam = target->deal_damage( z, bodypart_id( "head" ), damage_instance( DT_CUT, dam ) ).total_damage(); if( dam > 0 ) { - auto msg_type = target == &g->u ? m_bad : m_warning; + auto msg_type = target->is_avatar() ? m_bad : m_warning; target->add_msg_player_or_npc( msg_type, _( "The %1$s slashes at your neck, cutting your throat for %2$d damage!" ), _( "The %1$s slashes at 's neck, cutting their throat for %2$d damage!" ), @@ -4545,19 +4608,20 @@ bool mattack::darkman( monster *z ) // TODO: handle friendly monsters return false; } - if( rl_dist( z->pos(), g->u.pos() ) > 40 ) { + Character &player_character = get_player_character(); + if( rl_dist( z->pos(), player_character.pos() ) > 40 ) { return false; } if( monster *const shadow = g->place_critter_around( mon_shadow, z->pos(), 1 ) ) { z->moves -= 10; shadow->make_ally( *z ); - if( g->u.sees( *z ) ) { + if( player_character.sees( *z ) ) { add_msg( m_warning, _( "A shadow splits from the %s!" ), z->name() ); } } // Wont do the combat stuff unless it can see you - if( !z->sees( g->u ) ) { + if( !z->sees( player_character ) ) { return true; } // What do we say? @@ -4584,56 +4648,58 @@ bool mattack::darkman( monster *z ) add_msg( _( "\"Please dont\"" ) ); break; } - g->u.add_effect( effect_darkness, 1_turns, num_bp, true ); + player_character.add_effect( effect_darkness, 1_turns, num_bp, true ); return true; } bool mattack::slimespring( monster *z ) { - if( rl_dist( z->pos(), g->u.pos() ) > 30 ) { + Character &player_character = get_player_character(); + if( rl_dist( z->pos(), player_character.pos() ) > 30 ) { return false; } // This morale buff effect could get spammy - if( g->u.get_morale_level() <= 1 ) { + if( player_character.get_morale_level() <= 1 ) { switch( rng( 1, 3 ) ) { case 1: //~ Your slimes try to cheer you up! //~ Lowercase is intended: they're small voices. add_msg( m_good, _( "\"hey, it's gonna be all right!\"" ) ); - g->u.add_morale( MORALE_SUPPORT, 10, 50 ); + player_character.add_morale( MORALE_SUPPORT, 10, 50 ); break; case 2: //~ Your slimes try to cheer you up! //~ Lowercase is intended: they're small voices. add_msg( m_good, _( "\"we'll get through this!\"" ) ); - g->u.add_morale( MORALE_SUPPORT, 10, 50 ); + player_character.add_morale( MORALE_SUPPORT, 10, 50 ); break; case 3: //~ Your slimes try to cheer you up! //~ Lowercase is intended: they're small voices. add_msg( m_good, _( "\"i'm here for you!\"" ) ); - g->u.add_morale( MORALE_SUPPORT, 10, 50 ); + player_character.add_morale( MORALE_SUPPORT, 10, 50 ); break; } } - if( rl_dist( z->pos(), g->u.pos() ) <= 3 && z->sees( g->u ) ) { - if( ( g->u.has_effect( effect_bleed ) ) || ( g->u.has_effect( effect_bite ) ) ) { + if( rl_dist( z->pos(), player_character.pos() ) <= 3 && z->sees( player_character ) ) { + if( ( player_character.has_effect( effect_bleed ) ) || + ( player_character.has_effect( effect_bite ) ) ) { //~ Lowercase is intended: they're small voices. add_msg( _( "\"let me help!\"" ) ); // Yes, your slimespring(s) handle/don't all Bad Damage at the same time. - if( g->u.has_effect( effect_bite ) ) { + if( player_character.has_effect( effect_bite ) ) { if( one_in( 3 ) ) { - g->u.remove_effect( effect_bite ); + player_character.remove_effect( effect_bite ); add_msg( m_good, _( "The slime cleans you out!" ) ); } else { add_msg( _( "The slime flows over you, but your gouges still ache." ) ); } } - if( g->u.has_effect( effect_bleed ) ) { + if( player_character.has_effect( effect_bleed ) ) { if( one_in( 2 ) ) { - g->u.remove_effect( effect_bleed ); + player_character.remove_effect( effect_bleed ); add_msg( m_good, _( "The slime seals up your leaks!" ) ); } else { add_msg( _( "The slime flows over you, but your fluids are still leaking." ) ); @@ -4707,11 +4773,12 @@ bool mattack::riotbot( monster *z ) player *foe = dynamic_cast( target ); + map &here = get_map(); if( calendar::once_every( 1_minutes ) ) { - for( const tripoint &dest : g->m.points_in_radius( z->pos(), 4 ) ) { - if( g->m.passable( dest ) && - g->m.clear_path( z->pos(), dest, 3, 1, 100 ) ) { - g->m.add_field( dest, fd_relax_gas, rng( 1, 3 ) ); + for( const tripoint &dest : here.points_in_radius( z->pos(), 4 ) ) { + if( here.passable( dest ) && + here.clear_path( z->pos(), dest, 3, 1, 100 ) ) { + here.add_field( dest, fd_relax_gas, rng( 1, 3 ) ); } } } @@ -4739,7 +4806,7 @@ bool mattack::riotbot( monster *z ) const int dist = rl_dist( z->pos(), target->pos() ); //we need empty hands to arrest - if( foe == &g->u && !foe->is_armed() ) { + if( foe->is_avatar() && !foe->is_armed() ) { sounds::sound( z->pos(), 15, sounds::sound_t::electronic_speech, _( "Please stay in place, citizen, do not make any movements!" ), false, "speech", @@ -4845,10 +4912,10 @@ bool mattack::riotbot( monster *z ) add_msg( m_bad, _( "The robot sprays tear gas!" ) ); z->moves -= 200; - for( const tripoint &dest : g->m.points_in_radius( z->pos(), 2 ) ) { - if( g->m.passable( dest ) && - g->m.clear_path( z->pos(), dest, 3, 1, 100 ) ) { - g->m.add_field( dest, fd_tear_gas, rng( 1, 3 ) ); + for( const tripoint &dest : here.points_in_radius( z->pos(), 2 ) ) { + if( here.passable( dest ) && + here.clear_path( z->pos(), dest, 3, 1, 100 ) ) { + here.add_field( dest, fd_tear_gas, rng( 1, 3 ) ); } } @@ -4883,10 +4950,10 @@ bool mattack::riotbot( monster *z ) std::vector traj = line_to( z->pos(), dest, 0, 0 ); for( auto &elem : traj ) { - if( !g->m.is_transparent( elem ) ) { + if( !here.is_transparent( elem ) ) { break; } - g->m.add_field( elem, fd_dazzling, 1 ); + here.add_field( elem, fd_dazzling, 1 ); } return true; @@ -4910,7 +4977,7 @@ bool mattack::evolve_kill_strike( monster *z ) z->moves -= 100; const bool uncanny = target->uncanny_dodge(); if( uncanny || dodge_check( z, target ) ) { - auto msg_type = target == &g->u ? m_warning : m_info; + auto msg_type = target->is_avatar() ? m_warning : m_info; target->add_msg_player_or_npc( msg_type, _( "The %s lunges at you, but you dodge!" ), _( "The %s lunges at , but they dodge!" ), z->name() ); @@ -4930,7 +4997,7 @@ bool mattack::evolve_kill_strike( monster *z ) 15 ), 1.0, 0.5 ) ); int damage_dealt = target->deal_damage( z, bodypart_id( "torso" ), damage ).total_damage(); if( damage_dealt > 0 ) { - auto msg_type = target == &g->u ? m_bad : m_warning; + auto msg_type = target->is_avatar() ? m_bad : m_warning; target->add_msg_player_or_npc( msg_type, _( "The %1$s impales yor chest for %2$d damage!" ), _( "The %1$s impales 's chest for %2$d damage!" ), @@ -4942,15 +5009,16 @@ bool mattack::evolve_kill_strike( monster *z ) z->name() ); return true; } + Character &player_character = get_player_character(); if( target->is_dead_state() && g->is_empty( target_pos ) && target->made_of_any( Creature::cmat_flesh ) ) { const std::string old_name = z->name(); - const bool could_see_z = g->u.sees( *z ); + const bool could_see_z = player_character.sees( *z ); z->allow_upgrade(); z->try_upgrade( false ); z->setpos( target_pos ); const std::string upgrade_name = z->name(); - const bool can_see_z_upgrade = g->u.sees( *z ); + const bool can_see_z_upgrade = player_character.sees( *z ); if( could_see_z && can_see_z_upgrade ) { add_msg( m_warning, _( "The %1$s burrows within %2$s corpse and a %3$s emerges from the remains!" ), old_name, @@ -4966,7 +5034,7 @@ bool mattack::evolve_kill_strike( monster *z ) bool mattack::leech_spawner( monster *z ) { - const bool u_see = g->u.sees( *z ); + const bool u_see = get_player_character().sees( *z ); std::list allies; for( monster &candidate : g->all_monsters() ) { if( candidate.in_species( species_LEECH_PLANT ) && !candidate.has_flag( MF_IMMOBILE ) ) { @@ -4998,7 +5066,7 @@ bool mattack::leech_spawner( monster *z ) bool mattack::mon_leech_evolution( monster *z ) { - const bool u_see = g->u.sees( *z ); + const bool u_see = get_player_character().sees( *z ); const bool is_queen = z->has_flag( MF_QUEEN ); std::list queens; for( monster &candidate : g->all_monsters() ) { @@ -5026,29 +5094,31 @@ bool mattack::tindalos_teleport( monster *z ) if( target == nullptr ) { return false; } + Character &player_character = get_player_character(); if( one_in( 7 ) ) { if( monster *const afterimage = g->place_critter_around( mon_hound_tindalos_afterimage, z->pos(), 1 ) ) { z->moves -= 140; afterimage->make_ally( *z ); - if( g->u.sees( *z ) ) { + if( player_character.sees( *z ) ) { add_msg( m_warning, _( "The hound's movements chaotically rewind as a living afterimage splits from it!" ) ); } } } const int distance_to_target = rl_dist( z->pos(), target->pos() ); + map &here = get_map(); if( distance_to_target > 5 ) { const tripoint oldpos = z->pos(); - for( const tripoint &dest : g->m.points_in_radius( target->pos(), 4 ) ) { - if( g->m.is_cornerfloor( dest ) ) { + for( const tripoint &dest : here.points_in_radius( target->pos(), 4 ) ) { + if( here.is_cornerfloor( dest ) ) { if( g->is_empty( dest ) ) { z->setpos( dest ); // Not teleporting if it means losing sight of our current target if( z->sees( *target ) ) { - g->m.add_field( oldpos, fd_tindalos_rift, 2 ); - g->m.add_field( dest, fd_tindalos_rift, 2 ); - if( g->u.sees( *z ) ) { + here.add_field( oldpos, fd_tindalos_rift, 2 ); + here.add_field( dest, fd_tindalos_rift, 2 ); + if( player_character.sees( *z ) ) { add_msg( m_bad, _( "The %s dissipates and reforms close by." ), z->name() ); } return true; @@ -5088,8 +5158,8 @@ bool mattack::flesh_tendril( monster *z ) if( monster *const summoned = g->place_critter_around( spawned, z->pos(), 1 ) ) { z->moves -= 100; summoned->make_ally( *z ); - g->m.propagate_field( z->pos(), fd_gibs_flesh, 75, 1 ); - if( g->u.sees( *z ) ) { + get_map().propagate_field( z->pos(), fd_gibs_flesh, 75, 1 ); + if( get_player_character().sees( *z ) ) { add_msg( m_warning, _( "A %s struggles to pull itself free from the %s!" ), summoned->name(), z->name() ); } @@ -5177,7 +5247,7 @@ bool mattack::bio_op_takedown( monster *z ) return false; } - bool seen = g->u.sees( *z ); + bool seen = get_player_character().sees( *z ); player *foe = dynamic_cast< player * >( target ); if( seen ) { add_msg( _( "The %1$s mechanically grabs at %2$s!" ), z->name(), @@ -5271,7 +5341,7 @@ bool mattack::bio_op_impale( monster *z ) return false; } - const bool seen = g->u.sees( *z ); + const bool seen = get_player_character().sees( *z ); player *foe = dynamic_cast< player * >( target ); if( seen ) { add_msg( _( "The %1$s mechanically lunges at %2$s!" ), z->name(), @@ -5352,7 +5422,7 @@ bool mattack::bio_op_disarm( monster *z ) return false; } - const bool seen = g->u.sees( *z ); + const bool seen = get_player_character().sees( *z ); player *foe = dynamic_cast< player * >( target ); // disarm doesn't work on creatures or unarmed targets @@ -5397,7 +5467,7 @@ bool mattack::bio_op_disarm( monster *z ) if( my_roll >= their_roll && !it.has_flag( "NO_UNWIELD" ) ) { target->add_msg_if_player( m_bad, _( "and throws it to the ground!" ) ); const tripoint tp = foe->pos() + tripoint( rng( -1, 1 ), rng( -1, 1 ), 0 ); - g->m.add_item_or_charges( tp, foe->i_rem( &it ) ); + get_map().add_item_or_charges( tp, foe->i_rem( &it ) ); } else { target->add_msg_if_player( m_good, _( "but you break its grip!" ) ); } @@ -5546,7 +5616,7 @@ bool mattack::kamikaze( monster *z ) */ // END HORRIBLE HACK - if( g->u.sees( z->pos() ) ) { + if( get_player_character().sees( z->pos() ) ) { add_msg( m_bad, _( "The %s lights up menacingly." ), z->name() ); } @@ -5601,7 +5671,7 @@ static int grenade_helper( monster *const z, Creature *const target, const int d z->ammo[att]--; // if the player can see it - if( g->u.sees( *z ) ) { + if( get_player_character().sees( *z ) ) { if( data[att].message.empty() ) { add_msg( m_debug, "Invalid ammo message in grenadier special." ); } else { @@ -5652,7 +5722,7 @@ bool mattack::grenadier( monster *const z ) // Only can actively target the player right now. Once we have the ability to grab targets that we aren't // actively attacking change this to use that instead. - Creature *const target = static_cast( &g->u ); + Creature *const target = static_cast( &get_player_character() ); if( z->attitude_to( *target ) == Creature::Attitude::FRIENDLY ) { return false; } @@ -5685,7 +5755,7 @@ bool mattack::grenadier_elite( monster *const z ) // Only can actively target the player right now. Once we have the ability to grab targets that we aren't // actively attacking change this to use that instead. - Creature *const target = static_cast( &g->u ); + Creature *const target = static_cast( &get_player_character() ); if( z->attitude_to( *target ) == Creature::Attitude::FRIENDLY ) { return false; } @@ -5716,21 +5786,22 @@ bool mattack::stretch_attack( monster *z ) int dam = rng( 5, 10 ); z->moves -= 100; - for( auto &pnt : g->m.find_clear_path( z->pos(), target->pos() ) ) { - if( g->m.impassable( pnt ) ) { + map &here = get_map(); + for( auto &pnt : here.find_clear_path( z->pos(), target->pos() ) ) { + if( here.impassable( pnt ) ) { target->add_msg_player_or_npc( _( "The %1$s thrusts its arm at you, but bounces off the %2$s." ), _( "The %1$s thrusts its arm at , but bounces off the %2$s." ), - z->name(), g->m.obstacle_name( pnt ) ); + z->name(), here.obstacle_name( pnt ) ); return true; } } - auto msg_type = target == &g->u ? m_warning : m_info; + auto msg_type = target->is_avatar() ? m_warning : m_info; target->add_msg_player_or_npc( msg_type, _( "The %s thrusts its arm at you, stretching to reach you from afar." ), _( "The %s thrusts its arm at ." ), z->name() ); - if( dodge_check( z, target ) || g->u.uncanny_dodge() ) { + if( dodge_check( z, target ) || get_player_character().uncanny_dodge() ) { target->add_msg_player_or_npc( msg_type, _( "You evade the stretched arm and it sails past you!" ), _( " evades the stretched arm!" ) ); target->on_dodge( z, z->type->melee_skill * 2 ); @@ -5743,7 +5814,7 @@ bool mattack::stretch_attack( monster *z ) dam = target->deal_damage( z, hit, damage_instance( DT_STAB, dam ) ).total_damage(); if( dam > 0 ) { - auto msg_type = target == &g->u ? m_bad : m_info; + auto msg_type = target->is_avatar() ? m_bad : m_info; target->add_msg_player_or_npc( msg_type, //~ 1$s is monster name, 2$s bodypart in accusative _( "The %1$s's arm pierces your %2$s!" ), @@ -5768,7 +5839,7 @@ bool mattack::stretch_attack( monster *z ) bool mattack::zombie_fuse( monster *z ) { monster *critter = nullptr; - for( const tripoint &p : g->m.points_in_radius( z->pos(), 1 ) ) { + for( const tripoint &p : get_map().points_in_radius( z->pos(), 1 ) ) { critter = g->critter_at( p ); if( critter != nullptr && critter->faction == z->faction && critter != z && critter->get_size() <= z->get_size() ) { @@ -5781,7 +5852,7 @@ bool mattack::zombie_fuse( monster *z ) effect_grown_of_fuse ).get_max_intensity() ) ) ) { return false; } - if( g->u.sees( *z ) ) { + if( get_player_character().sees( *z ) ) { add_msg( _( "The %1$s fuses with the %2$s." ), critter->name(), z->name() ); @@ -5798,17 +5869,18 @@ bool mattack::zombie_fuse( monster *z ) bool mattack::doot( monster *z ) { z->moves -= 300; - if( g->u.sees( *z ) ) { + Character &player_character = get_player_character(); + if( player_character.sees( *z ) ) { add_msg( _( "The %s doots its trumpet!" ), z->name() ); } int spooks = 0; - for( const tripoint &spookyscary : g->m.points_in_radius( z->pos(), 2 ) ) { + for( const tripoint &spookyscary : get_map().points_in_radius( z->pos(), 2 ) ) { if( !g->is_empty( spookyscary ) ) { continue; } const int dist = rl_dist( z->pos(), spookyscary ); if( ( one_in( dist + 3 ) || spooks == 0 ) && spooks < 5 ) { - if( g->u.sees( *z ) ) { + if( player_character.sees( *z ) ) { add_msg( _( "A spooky skeleton rises from the ground!" ) ); } g->place_critter_at( mon_zombie_skeltal_minion, spookyscary ); From 3294d800bf38d0e75b06f49958a58a2935c62809 Mon Sep 17 00:00:00 2001 From: Kevin Granade Date: Thu, 2 Jul 2020 14:29:32 -0700 Subject: [PATCH 046/420] Map reference migration part thirteen (#41774) * Add get_attitude() to Character * Map reference migration part 13 --- src/character.cpp | 5 + src/character.h | 2 + src/monmove.cpp | 305 ++++++++++++++++++++++++---------------------- src/monster.cpp | 144 ++++++++++++---------- src/monster.h | 2 +- src/npc.h | 2 +- 6 files changed, 249 insertions(+), 211 deletions(-) diff --git a/src/character.cpp b/src/character.cpp index c6bb41b07a840..603c2e4a5bd36 100644 --- a/src/character.cpp +++ b/src/character.cpp @@ -10781,6 +10781,11 @@ Creature::Attitude Character::attitude_to( const Creature &other ) const return Attitude::NEUTRAL; } +npc_attitude Character::get_attitude() const +{ + return NPCATT_NULL; +} + bool Character::sees( const tripoint &t, bool, int ) const { const int wanted_range = rl_dist( pos(), t ); diff --git a/src/character.h b/src/character.h index 5d12d84b3ced6..e6177d039a240 100644 --- a/src/character.h +++ b/src/character.h @@ -70,6 +70,7 @@ struct needs_rates; struct pathfinding_settings; struct points_left; template struct enum_traits; +enum npc_attitude : int; using drop_location = std::pair; using drop_locations = std::list; @@ -2213,6 +2214,7 @@ class Character : public Creature, public visitable // see Creature::sees bool sees( const Creature &critter ) const override; Attitude attitude_to( const Creature &other ) const override; + virtual npc_attitude get_attitude() const; // used in debugging all health int get_lowest_hp() const; diff --git a/src/monmove.cpp b/src/monmove.cpp index 3bf63ea95b2ef..c7068f117565a 100644 --- a/src/monmove.cpp +++ b/src/monmove.cpp @@ -12,7 +12,6 @@ #include #include -#include "avatar.h" #include "behavior.h" #include "bionics.h" #include "cata_utility.h" @@ -36,7 +35,6 @@ #include "npc.h" #include "pathfinding.h" #include "pimpl.h" -#include "player.h" #include "rng.h" #include "scent_map.h" #include "sounds.h" @@ -116,25 +114,26 @@ static bool z_is_valid( int z ) bool monster::will_move_to( const tripoint &p ) const { - if( g->m.impassable( p ) ) { + map &here = get_map(); + if( here.impassable( p ) ) { if( digging() ) { - if( !g->m.has_flag( "BURROWABLE", p ) ) { + if( !here.has_flag( "BURROWABLE", p ) ) { return false; } - } else if( !( can_climb() && g->m.has_flag( "CLIMBABLE", p ) ) ) { + } else if( !( can_climb() && here.has_flag( "CLIMBABLE", p ) ) ) { return false; } } - if( ( !can_submerge() && !flies() ) && g->m.has_flag( TFLAG_DEEP_WATER, p ) ) { + if( ( !can_submerge() && !flies() ) && here.has_flag( TFLAG_DEEP_WATER, p ) ) { return false; } - if( digs() && !g->m.has_flag( "DIGGABLE", p ) && !g->m.has_flag( "BURROWABLE", p ) ) { + if( digs() && !here.has_flag( "DIGGABLE", p ) && !here.has_flag( "BURROWABLE", p ) ) { return false; } - if( has_flag( MF_AQUATIC ) && !g->m.has_flag( "SWIMMABLE", p ) ) { + if( has_flag( MF_AQUATIC ) && !here.has_flag( "SWIMMABLE", p ) ) { return false; } @@ -142,7 +141,7 @@ bool monster::will_move_to( const tripoint &p ) const return false; } - if( get_size() > creature_size::medium && g->m.has_flag_ter( TFLAG_SMALL_PASSAGE, p ) ) { + if( get_size() > creature_size::medium && here.has_flag_ter( TFLAG_SMALL_PASSAGE, p ) ) { return false; // if a large critter, can't move through tight passages } @@ -169,7 +168,7 @@ bool monster::will_move_to( const tripoint &p ) const // technically this will shortcut in evaluation from fire or fall // before hitting simple or complex but this is more explicit if( avoid_fire || avoid_fall || avoid_simple || avoid_complex ) { - const ter_id target = g->m.ter( p ); + const ter_id target = here.ter( p ); // Don't enter lava if we have any concept of heat being bad if( avoid_fire && target == t_lava ) { @@ -178,7 +177,7 @@ bool monster::will_move_to( const tripoint &p ) const if( avoid_fall ) { // Don't throw ourselves off cliffs if we have a concept of falling - if( !g->m.has_floor( p ) && !flies() ) { + if( !here.has_floor( p ) && !flies() ) { return false; } @@ -190,15 +189,15 @@ bool monster::will_move_to( const tripoint &p ) const } // Some things are only avoided if we're not attacking - if( attitude( &g->u ) != MATT_ATTACK ) { + if( attitude( &get_player_character() ) != MATT_ATTACK ) { // Sharp terrain is ignored while attacking - if( avoid_simple && g->m.has_flag( "SHARP", p ) && + if( avoid_simple && here.has_flag( "SHARP", p ) && !( type->size == creature_size::tiny || flies() ) ) { return false; } } - const field &target_field = g->m.field_at( p ); + const field &target_field = here.field_at( p ); // Higher awareness is needed for identifying these as threats. if( avoid_complex ) { @@ -207,8 +206,8 @@ bool monster::will_move_to( const tripoint &p ) const return false; } // Don't step on any traps (if we can see) - const trap &target_trap = g->m.tr_at( p ); - if( has_flag( MF_SEES ) && !target_trap.is_benign() && g->m.has_floor( p ) ) { + const trap &target_trap = here.tr_at( p ); + if( has_flag( MF_SEES ) && !target_trap.is_benign() && here.has_floor( p ) ) { return false; } } @@ -227,13 +226,14 @@ bool monster::will_move_to( const tripoint &p ) const bool monster::can_reach_to( const tripoint &p ) const { + map &here = get_map(); if( p.z > pos().z && z_is_valid( pos().z ) ) { - if( !g->m.has_flag( TFLAG_GOES_UP, pos() ) && !g->m.has_flag( TFLAG_NO_FLOOR, p ) ) { + if( !here.has_flag( TFLAG_GOES_UP, pos() ) && !here.has_flag( TFLAG_NO_FLOOR, p ) ) { // can't go through the roof return false; } } else if( p.z < pos().z && z_is_valid( pos().z ) ) { - if( !g->m.has_flag( TFLAG_GOES_DOWN, pos() ) ) { + if( !here.has_flag( TFLAG_GOES_DOWN, pos() ) ) { // can't go through the floor // you would fall anyway if there was no floor, so no need to check for that here return false; @@ -322,12 +322,12 @@ void monster::plan() bool group_morale = has_flag( MF_GROUP_MORALE ) && morale < type->morale; bool swarms = has_flag( MF_SWARMS ); auto mood = attitude(); - + Character &player_character = get_player_character(); // If we can see the player, move toward them or flee, simpleminded animals are too dumb to follow the player. - if( friendly == 0 && sees( g->u ) && !has_flag( MF_PET_WONT_FOLLOW ) ) { - dist = rate_target( g->u, dist, smart_planning ); - fleeing = fleeing || is_fleeing( g->u ); - target = &g->u; + if( friendly == 0 && sees( player_character ) && !has_flag( MF_PET_WONT_FOLLOW ) ) { + dist = rate_target( player_character, dist, smart_planning ); + fleeing = fleeing || is_fleeing( player_character ); + target = &player_character; if( dist <= 5 ) { anger += angers_hostile_near; morale -= fears_hostile_near; @@ -352,7 +352,7 @@ void monster::plan() for( monster &tmp : g->all_monsters() ) { if( type->baby_monster == tmp.type->id ) { // baby nearby; is the player too close? - dist = tmp.rate_target( g->u, dist, smart_planning ); + dist = tmp.rate_target( player_character, dist, smart_planning ); if( dist <= 3 ) { //proximity to baby; monster gets furious and less likely to flee anger += angers_cub_threatened; @@ -500,11 +500,12 @@ void monster::plan() } } + map &here = get_map(); // Operating monster keep you safe while they operate, how nice.... if( type->has_special_attack( "OPERATE" ) ) { if( has_effect( effect_operating ) ) { friendly = 100; - for( auto critter : g->m.get_creatures_in_radius( pos(), 6 ) ) { + for( auto critter : here.get_creatures_in_radius( pos(), 6 ) ) { monster *mon = dynamic_cast( critter ); if( mon != nullptr && mon->type->in_species( species_ZOMBIE ) ) { anger = 100; @@ -522,9 +523,9 @@ void monster::plan() bool found_path_to_couch = false; tripoint tmp( pos() + point( 12, 12 ) ); tripoint couch_loc; - for( const auto &couch_pos : g->m.find_furnitures_with_flag_in_radius( pos(), 10, + for( const auto &couch_pos : here.find_furnitures_with_flag_in_radius( pos(), 10, flag_AUTODOC_COUCH ) ) { - if( g->m.clear_path( pos(), couch_pos, 10, 0, 100 ) ) { + if( here.clear_path( pos(), couch_pos, 10, 0, 100 ) ) { if( rl_dist( pos(), couch_pos ) < rl_dist( pos(), tmp ) ) { tmp = couch_pos; found_path_to_couch = true; @@ -559,9 +560,9 @@ void monster::plan() } else if( friendly > 0 && one_in( 3 ) ) { // Grow restless with no targets friendly--; - } else if( friendly < 0 && sees( g->u ) ) { - if( rl_dist( pos(), g->u.pos() ) > 2 ) { - set_dest( g->u.pos() ); + } else if( friendly < 0 && sees( player_character ) ) { + if( rl_dist( pos(), player_character.pos() ) > 2 ) { + set_dest( player_character.pos() ); } else { unset_dest(); } @@ -594,15 +595,16 @@ static float get_stagger_adjust( const tripoint &source, const tripoint &destina */ bool monster::is_aquatic_danger( const tripoint &at_pos ) { - return g->m.has_flag_ter( TFLAG_DEEP_WATER, at_pos ) && g->m.has_flag( flag_LIQUID, at_pos ) && - can_drown() && !g->m.veh_at( at_pos ).part_with_feature( "BOARDABLE", false ); + map &here = get_map(); + return here.has_flag_ter( TFLAG_DEEP_WATER, at_pos ) && here.has_flag( flag_LIQUID, at_pos ) && + can_drown() && !here.veh_at( at_pos ).part_with_feature( "BOARDABLE", false ); } bool monster::die_if_drowning( const tripoint &at_pos, const int chance ) { if( is_aquatic_danger( at_pos ) && one_in( chance ) ) { die( nullptr ); - if( g->u.sees( at_pos ) ) { + if( get_player_character().sees( at_pos ) ) { add_msg( _( "The %s drowns!" ), name() ); } return true; @@ -629,6 +631,8 @@ void monster::move() die( nullptr ); return; } + map &here = get_map(); + Character &player_character = get_player_character(); behavior::monster_oracle_t oracle( this ); behavior::tree goals; @@ -638,12 +642,12 @@ void monster::move() //If there are. Consume them. // TODO: Stick this in a map and dispatch to it via the action string. if( action == "consume_items" ) { - if( g->u.sees( *this ) ) { + if( player_character.sees( *this ) ) { add_msg( _( "The %s flows around the objects on the floor and they are quickly dissolved!" ), name() ); } static const auto volume_per_hp = 250_ml; - for( item &elem : g->m.i_at( pos() ) ) { + for( item &elem : here.i_at( pos() ) ) { hp += elem.volume() / volume_per_hp; // Yeah this means it can get more HP than normal. if( has_flag( MF_ABSORBS_SPLITS ) ) { while( hp / 2 > type->hp ) { @@ -654,13 +658,13 @@ void monster::move() hp -= type->hp; //this is a new copy of the monster. Ideally we should copy the stats/effects that affect the parent spawn->make_ally( *this ); - if( g->u.sees( *this ) ) { + if( player_character.sees( *this ) ) { add_msg( _( "The %s splits in two!" ), name() ); } } } } - g->m.i_clear( pos() ); + here.i_clear( pos() ); } else if( action == "eat_crop" ) { // TODO: Create a special attacks whitelist unordered map instead of an if chain. std::map::const_iterator attack = @@ -672,7 +676,7 @@ void monster::move() } } // record position before moving to put the player there if we're dragging - tripoint drag_to = g->m.getabs( pos() ); + tripoint drag_to = here.getabs( pos() ); const bool pacified = has_effect( effect_pacified ); @@ -715,8 +719,8 @@ void monster::move() nursebot_operate( dragged_foe ); // The monster can sometimes hang in air due to last fall being blocked - if( !flies() && g->m.has_flag( TFLAG_NO_FLOOR, pos() ) ) { - g->m.creature_on_trap( *this, false ); + if( !flies() && here.has_flag( TFLAG_NO_FLOOR, pos() ) ) { + here.creature_on_trap( *this, false ); if( is_dead() ) { return; } @@ -751,8 +755,8 @@ void monster::move() } // don't move if a passenger in a moving vehicle - auto vp = g->m.veh_at( pos() ); - bool harness_part = static_cast( g->m.veh_at( pos() ).part_with_feature( "ANIMAL_CTRL", + auto vp = here.veh_at( pos() ); + bool harness_part = static_cast( here.veh_at( pos() ).part_with_feature( "ANIMAL_CTRL", true ) ); if( vp && vp->vehicle().is_moving() && vp->vehicle().get_pet( vp->part_index() ) ) { moves = 0; @@ -768,8 +772,8 @@ void monster::move() // Set attitude to attitude to our current target monster_attitude current_attitude = attitude( nullptr ); if( !wander() ) { - if( goal == g->u.pos() ) { - current_attitude = attitude( &g->u ); + if( goal == player_character.pos() ) { + current_attitude = attitude( &player_character ); } else { for( const npc &guy : g->all_npcs() ) { if( goal == guy.pos() ) { @@ -800,7 +804,7 @@ void monster::move() if( pf_settings.max_dist >= rl_dist( pos(), goal ) && ( path.empty() || rl_dist( pos(), path.front() ) >= 2 || path.back() != goal ) ) { // We need a new path - path = g->m.route( pos(), goal, pf_settings, get_path_avoid() ); + path = here.route( pos(), goal, pf_settings, get_path_avoid() ); } // Try to respect old paths, even if we can't pathfind at the moment @@ -832,7 +836,7 @@ void monster::move() } } - if( !g->m.has_zlevels() ) { + if( !here.has_zlevels() ) { // Otherwise weird things happen destination.z = posz(); } @@ -866,10 +870,10 @@ void monster::move() // in both circular and roguelike distance modes. const float distance_to_target = trig_dist( pos(), destination ); for( const tripoint &candidate : squares_closer_to( pos(), destination ) ) { - tripoint candidate_abs = g->m.getabs( candidate ); + tripoint candidate_abs = here.getabs( candidate ); if( candidate.z != posz() ) { bool can_z_move = true; - if( !g->m.valid_move( pos(), candidate, false, true ) ) { + if( !here.valid_move( pos(), candidate, false, true ) ) { // Can't phase through floor can_z_move = false; } @@ -877,7 +881,7 @@ void monster::move() // If we're trying to go up but can't fly, check if we can climb. If we can't, then don't // This prevents non-climb/fly enemies running up walls if( candidate.z > posz() && !flies() ) { - if( !can_climb() || !g->m.has_floor_or_support( candidate ) ) { + if( !can_climb() || !here.has_floor_or_support( candidate ) ) { // Can't "jump" up a whole z-level can_z_move = false; } @@ -890,7 +894,7 @@ void monster::move() posy() / ( SEEY * 2 ) == candidate.y / ( SEEY * 2 ) ) { const tripoint &upper = candidate.z > posz() ? candidate : pos(); const tripoint &lower = candidate.z > posz() ? pos() : candidate; - if( g->m.has_flag( TFLAG_GOES_DOWN, upper ) && g->m.has_flag( TFLAG_GOES_UP, lower ) ) { + if( here.has_flag( TFLAG_GOES_DOWN, upper ) && here.has_flag( TFLAG_GOES_UP, lower ) ) { can_z_move = true; } } @@ -931,7 +935,7 @@ void monster::move() if( !can_bash ) { continue; } - const int estimate = g->m.bash_rating( bash_estimate(), candidate ); + const int estimate = here.bash_rating( bash_estimate(), candidate ); if( estimate <= 0 ) { continue; } @@ -963,10 +967,10 @@ void monster::move() // Finished logic section. By this point, we should have chosen a square to // move to (moved = true). if( moved ) { // Actual effects of moving to the square we've chosen - const tripoint local_next_step = g->m.getlocal( next_step ); + const tripoint local_next_step = here.getlocal( next_step ); const bool did_something = ( !pacified && attack_at( local_next_step ) ) || - ( !pacified && can_open_doors && g->m.open_door( local_next_step, !g->m.is_outside( pos() ) ) ) || + ( !pacified && can_open_doors && here.open_door( local_next_step, !here.is_outside( pos() ) ) ) || ( !pacified && bash_at( local_next_step ) ) || ( !pacified && push_to( local_next_step, 0, 0 ) ) || move_to( local_next_step, false, false, get_stagger_adjust( pos(), destination, local_next_step ) ); @@ -979,9 +983,9 @@ void monster::move() if( !dragged_foe->has_effect( effect_grabbed ) ) { dragged_foe = nullptr; remove_effect( effect_dragging ); - } else if( g->m.getlocal( drag_to ) != pos() && - g->critter_at( g->m.getlocal( drag_to ) ) == nullptr ) { - dragged_foe->setpos( g->m.getlocal( drag_to ) ); + } else if( here.getlocal( drag_to ) != pos() && + g->critter_at( here.getlocal( drag_to ) ) == nullptr ) { + dragged_foe->setpos( here.getlocal( drag_to ) ); } } } else { @@ -1026,7 +1030,7 @@ void monster::nursebot_operate( player *dragged_foe ) return; } - if( rl_dist( pos(), goal ) == 1 && !g->m.has_flag_furn( flag_AUTODOC_COUCH, goal ) && + if( rl_dist( pos(), goal ) == 1 && !get_map().has_flag_furn( flag_AUTODOC_COUCH, goal ) && !has_effect( effect_operating ) ) { if( dragged_foe->has_effect( effect_grabbed ) && !has_effect( effect_countdown ) && ( g->critter_at( goal ) == nullptr || g->critter_at( goal ) == dragged_foe ) ) { @@ -1057,7 +1061,7 @@ void monster::nursebot_operate( player *dragged_foe ) const float adjusted_skill = static_cast( 77 ) - std::min( static_cast( 40 ), static_cast( 77 ) - static_cast( 77 ) / static_cast( 10.0 ) ); - g->u.uninstall_bionic( target_cbm, *this, *dragged_foe, adjusted_skill ); + get_player_character().uninstall_bionic( target_cbm, *this, *dragged_foe, adjusted_skill ); dragged_foe->remove_effect( effect_grabbed ); remove_effect( effect_dragging ); @@ -1106,7 +1110,7 @@ void monster::footsteps( const tripoint &p ) if( volume == 0 ) { return; } - int dist = rl_dist( p, g->u.pos() ); + int dist = rl_dist( p, get_player_character().pos() ); sounds::add_footstep( p, volume, dist, this, type->get_footsteps() ); } @@ -1129,7 +1133,8 @@ tripoint monster::scent_move() smell_threshold = 400; } - const bool fleeing = is_fleeing( g->u ); + Character &player_character = get_player_character(); + const bool fleeing = is_fleeing( player_character ); if( fleeing ) { bestsmell = g->scent.get( pos() ); } @@ -1140,7 +1145,8 @@ tripoint monster::scent_move() return next; } const bool can_bash = bash_skill() > 0; - for( const auto &dest : g->m.points_in_radius( pos(), 1, SCENT_MAP_Z_REACH ) ) { + map &here = get_map(); + for( const auto &dest : here.points_in_radius( pos(), 1, SCENT_MAP_Z_REACH ) ) { int smell = g->scent.get( dest ); const scenttype_id &type_scent = g->scent.get_type( dest ); @@ -1168,9 +1174,9 @@ tripoint monster::scent_move() if( ( !fleeing && smell < bestsmell ) || ( fleeing && smell > bestsmell ) || !right_scent ) { continue; } - if( g->m.valid_move( pos(), dest, can_bash, true ) && - ( can_move_to( dest ) || ( dest == g->u.pos() ) || - ( can_bash && g->m.bash_rating( bash_estimate(), dest ) > 0 ) ) ) { + if( here.valid_move( pos(), dest, can_bash, true ) && + ( can_move_to( dest ) || ( dest == player_character.pos() ) || + ( can_bash && here.bash_rating( bash_estimate(), dest ) > 0 ) ) ) { if( ( !fleeing && smell > bestsmell ) || ( fleeing && smell < bestsmell ) ) { smoves.clear(); smoves.push_back( dest ); @@ -1188,46 +1194,47 @@ int monster::calc_movecost( const tripoint &f, const tripoint &t ) const { int movecost = 0; - const int source_cost = g->m.move_cost( f ); - const int dest_cost = g->m.move_cost( t ); + map &here = get_map(); + const int source_cost = here.move_cost( f ); + const int dest_cost = here.move_cost( t ); // Digging and flying monsters ignore terrain cost - if( flies() || ( digging() && g->m.has_flag( "DIGGABLE", t ) ) ) { + if( flies() || ( digging() && here.has_flag( "DIGGABLE", t ) ) ) { movecost = 100; // Swimming monsters move super fast in water } else if( swims() ) { - if( g->m.has_flag( "SWIMMABLE", f ) ) { + if( here.has_flag( "SWIMMABLE", f ) ) { movecost += 25; } else { - movecost += 50 * g->m.move_cost( f ); + movecost += 50 * here.move_cost( f ); } - if( g->m.has_flag( "SWIMMABLE", t ) ) { + if( here.has_flag( "SWIMMABLE", t ) ) { movecost += 25; } else { - movecost += 50 * g->m.move_cost( t ); + movecost += 50 * here.move_cost( t ); } } else if( can_submerge() ) { // No-breathe monsters have to walk underwater slowly - if( g->m.has_flag( "SWIMMABLE", f ) ) { + if( here.has_flag( "SWIMMABLE", f ) ) { movecost += 250; } else { - movecost += 50 * g->m.move_cost( f ); + movecost += 50 * here.move_cost( f ); } - if( g->m.has_flag( "SWIMMABLE", t ) ) { + if( here.has_flag( "SWIMMABLE", t ) ) { movecost += 250; } else { - movecost += 50 * g->m.move_cost( t ); + movecost += 50 * here.move_cost( t ); } movecost /= 2; } else if( climbs() ) { - if( g->m.has_flag( "CLIMBABLE", f ) ) { + if( here.has_flag( "CLIMBABLE", f ) ) { movecost += 150; } else { - movecost += 50 * g->m.move_cost( f ); + movecost += 50 * here.move_cost( f ); } - if( g->m.has_flag( "CLIMBABLE", t ) ) { + if( here.has_flag( "CLIMBABLE", t ) ) { movecost += 150; } else { - movecost += 50 * g->m.move_cost( t ); + movecost += 50 * here.move_cost( t ); } movecost /= 2; } else { @@ -1243,8 +1250,9 @@ int monster::calc_climb_cost( const tripoint &f, const tripoint &t ) const return 100; } - if( climbs() && !g->m.has_flag( TFLAG_NO_FLOOR, t ) ) { - const int diff = g->m.climb_difficulty( f ); + map &here = get_map(); + if( climbs() && !here.has_flag( TFLAG_NO_FLOOR, t ) ) { + const int diff = here.climb_difficulty( f ); if( diff <= 10 ) { return 150; } @@ -1306,14 +1314,15 @@ bool monster::bash_at( const tripoint &p ) return false; } - bool can_bash = g->m.is_bashable( p ) && bash_skill() > 0; + map &here = get_map(); + bool can_bash = here.is_bashable( p ) && bash_skill() > 0; if( !can_bash ) { return false; } - bool flat_ground = g->m.has_flag( "ROAD", p ) || g->m.has_flag( "FLAT", p ); + bool flat_ground = here.has_flag( "ROAD", p ) || here.has_flag( "FLAT", p ); if( flat_ground ) { - bool can_bash_ter = g->m.is_bashable_ter( p ); + bool can_bash_ter = here.is_bashable_ter( p ); bool try_bash_ter = one_in( 50 ); if( !( can_bash_ter && try_bash_ter ) ) { return false; @@ -1321,7 +1330,7 @@ bool monster::bash_at( const tripoint &p ) } int bashskill = group_bash_skill( p ); - g->m.bash( p, bashskill ); + here.bash( p, bashskill ); moves -= 100; return true; } @@ -1394,8 +1403,9 @@ bool monster::attack_at( const tripoint &p ) return false; } - if( p == g->u.pos() ) { - melee_attack( g->u ); + Character &player_character = get_player_character(); + if( p == player_character.pos() ) { + melee_attack( player_character ); return true; } @@ -1439,8 +1449,9 @@ bool monster::attack_at( const tripoint &p ) static tripoint find_closest_stair( const tripoint &near_this, const ter_bitflags stair_type ) { + map &here = get_map(); for( const tripoint &candidate : closest_tripoints_first( near_this, 10 ) ) { - if( g->m.has_flag( stair_type, candidate ) ) { + if( here.has_flag( stair_type, candidate ) ) { return candidate; } } @@ -1457,38 +1468,40 @@ bool monster::move_to( const tripoint &p, bool force, bool step_on_critter, const bool going_up = p.z > pos().z; tripoint destination = p; + map &here = get_map(); // This is stair teleportation hackery. // TODO: Remove this in favor of stair alignment if( going_up ) { - if( g->m.has_flag( TFLAG_GOES_UP, pos() ) ) { + if( here.has_flag( TFLAG_GOES_UP, pos() ) ) { destination = find_closest_stair( p, TFLAG_GOES_DOWN ); } } else if( z_move ) { - if( g->m.has_flag( TFLAG_GOES_DOWN, pos() ) ) { + if( here.has_flag( TFLAG_GOES_DOWN, pos() ) ) { destination = find_closest_stair( p, TFLAG_GOES_UP ); } } + Character &player_character = get_player_character(); // Allows climbing monsters to move on terrain with movecost <= 0 Creature *critter = g->critter_at( destination, is_hallucination() ); - if( g->m.has_flag( "CLIMBABLE", destination ) ) { - if( g->m.impassable( destination ) && critter == nullptr ) { + if( here.has_flag( "CLIMBABLE", destination ) ) { + if( here.impassable( destination ) && critter == nullptr ) { if( flies() ) { moves -= 100; force = true; - if( g->u.sees( *this ) ) { + if( player_character.sees( *this ) ) { add_msg( _( "The %1$s flies over the %2$s." ), name(), - g->m.has_flag_furn( "CLIMBABLE", p ) ? g->m.furnname( p ) : - g->m.tername( p ) ); + here.has_flag_furn( "CLIMBABLE", p ) ? here.furnname( p ) : + here.tername( p ) ); } } else if( climbs() ) { moves -= 150; force = true; - if( g->u.sees( *this ) ) { + if( player_character.sees( *this ) ) { add_msg( _( "The %1$s climbs over the %2$s." ), name(), - g->m.has_flag_furn( "CLIMBABLE", p ) ? g->m.furnname( p ) : - g->m.tername( p ) ); + here.has_flag_furn( "CLIMBABLE", p ) ? here.furnname( p ) : + here.tername( p ) ); } } } @@ -1510,7 +1523,7 @@ bool monster::move_to( const tripoint &p, bool force, bool step_on_critter, // Note: Keep this as float here or else it will cancel valid moves const float cost = stagger_adjustment * static_cast( climbs() && - g->m.has_flag( TFLAG_NO_FLOOR, p ) ? calc_climb_cost( pos(), destination ) : calc_movecost( pos(), + here.has_flag( TFLAG_NO_FLOOR, p ) ? calc_climb_cost( pos(), destination ) : calc_movecost( pos(), destination ) ); if( cost > 0.0f ) { moves -= static_cast( std::ceil( cost ) ); @@ -1520,28 +1533,28 @@ bool monster::move_to( const tripoint &p, bool force, bool step_on_critter, } //Check for moving into/out of water - bool was_water = g->m.is_divable( pos() ); - bool will_be_water = on_ground && can_submerge() && g->m.is_divable( destination ); + bool was_water = here.is_divable( pos() ); + bool will_be_water = on_ground && can_submerge() && here.is_divable( destination ); //Birds and other flying creatures flying over the deep water terrain - if( was_water && flies() && g->u.sees( *this ) ) { + if( was_water && flies() && player_character.sees( *this ) ) { if( one_in( 4 ) ) { add_msg( m_warning, _( "A %1$s flies over the %2$s!" ), name(), - g->m.tername( pos() ) ); + here.tername( pos() ) ); } - } else if( was_water && !will_be_water && g->u.sees( *this ) ) { + } else if( was_water && !will_be_water && player_character.sees( *this ) ) { // Use more dramatic messages for swimming monsters //~ Message when a monster emerges from water //~ %1$s: monster name, %2$s: leaps/emerges, %3$s: terrain name add_msg( m_warning, pgettext( "monster movement", "A %1$s %2$s from the %3$s!" ), name(), swims() || has_flag( MF_AQUATIC ) ? _( "leaps" ) : _( "emerges" ), - g->m.tername( pos() ) ); - } else if( !was_water && will_be_water && g->u.sees( *this ) ) { + here.tername( pos() ) ); + } else if( !was_water && will_be_water && player_character.sees( *this ) ) { //~ Message when a monster enters water //~ %1$s: monster name, %2$s: dives/sinks, %3$s: terrain name add_msg( m_warning, pgettext( "monster movement", "A %1$s %2$s into the %3$s!" ), name(), swims() || has_flag( MF_AQUATIC ) ? _( "dives" ) : _( "sinks" ), - g->m.tername( destination ) ); + here.tername( destination ) ); } setpos( destination ); @@ -1555,37 +1568,37 @@ bool monster::move_to( const tripoint &p, bool force, bool step_on_critter, if( type->size != creature_size::tiny && on_ground ) { const int sharp_damage = rng( 1, 10 ); const int rough_damage = rng( 1, 2 ); - if( g->m.has_flag( "SHARP", pos() ) && !one_in( 4 ) && + if( here.has_flag( "SHARP", pos() ) && !one_in( 4 ) && get_armor_cut( bodypart_id( "torso" ) ) < sharp_damage ) { apply_damage( nullptr, bodypart_id( "torso" ), sharp_damage ); } - if( g->m.has_flag( "ROUGH", pos() ) && one_in( 6 ) && + if( here.has_flag( "ROUGH", pos() ) && one_in( 6 ) && get_armor_cut( bodypart_id( "torso" ) ) < rough_damage ) { apply_damage( nullptr, bodypart_id( "torso" ), rough_damage ); } } - if( g->m.has_flag( "UNSTABLE", destination ) && on_ground ) { + if( here.has_flag( "UNSTABLE", destination ) && on_ground ) { add_effect( effect_bouldering, 1_turns, num_bp, true ); } else if( has_effect( effect_bouldering ) ) { remove_effect( effect_bouldering ); } - if( g->m.has_flag_ter_or_furn( TFLAG_NO_SIGHT, destination ) && on_ground ) { + if( here.has_flag_ter_or_furn( TFLAG_NO_SIGHT, destination ) && on_ground ) { add_effect( effect_no_sight, 1_turns, num_bp, true ); } else if( has_effect( effect_no_sight ) ) { remove_effect( effect_no_sight ); } - g->m.creature_on_trap( *this ); + here.creature_on_trap( *this ); if( is_dead() ) { return true; } if( !will_be_water && ( digs() || can_dig() ) ) { - underwater = g->m.has_flag( "DIGGABLE", pos() ); + underwater = here.has_flag( "DIGGABLE", pos() ); } // Diggers turn the dirt into dirtmound - if( digging() && g->m.has_flag( "DIGGABLE", pos() ) ) { + if( digging() && here.has_flag( "DIGGABLE", pos() ) ) { int factor = 0; switch( type->size ) { case creature_size::tiny: @@ -1606,26 +1619,26 @@ bool monster::move_to( const tripoint &p, bool force, bool step_on_critter, } // TODO: make this take terrain type into account so diggers traveling under sand will create mounds of sand etc. if( one_in( factor ) ) { - g->m.ter_set( pos(), t_dirtmound ); + here.ter_set( pos(), t_dirtmound ); } } // Acid trail monsters leave... a trail of acid if( has_flag( MF_ACIDTRAIL ) ) { - g->m.add_field( pos(), fd_acid, 3 ); + here.add_field( pos(), fd_acid, 3 ); } // Not all acid trail monsters leave as much acid. Every time this monster takes a step, there is a 1/5 chance it will drop a puddle. if( has_flag( MF_SHORTACIDTRAIL ) ) { if( one_in( 5 ) ) { - g->m.add_field( pos(), fd_acid, 3 ); + here.add_field( pos(), fd_acid, 3 ); } } if( has_flag( MF_SLUDGETRAIL ) ) { - for( const tripoint &sludge_p : g->m.points_in_radius( pos(), 1 ) ) { + for( const tripoint &sludge_p : here.points_in_radius( pos(), 1 ) ) { const int fstr = 3 - ( std::abs( sludge_p.x - posx() ) + std::abs( sludge_p.y - posy() ) ); if( fstr >= 2 ) { - g->m.add_field( sludge_p, fd_sludge, fstr ); + here.add_field( sludge_p, fd_sludge, fstr ); } } } @@ -1634,7 +1647,7 @@ bool monster::move_to( const tripoint &p, bool force, bool step_on_critter, if( one_in( 10 ) ) { // if it has more napalm, drop some and reduce ammo in tank if( ammo[itype_pressurized_tank] > 0 ) { - g->m.add_item_or_charges( pos(), item( "napalm", calendar::turn, 50 ) ); + here.add_item_or_charges( pos(), item( "napalm", calendar::turn, 50 ) ); ammo[itype_pressurized_tank] -= 50; } else { // TODO: remove MF_DRIPS_NAPALM flag since no more napalm in tank @@ -1645,7 +1658,7 @@ bool monster::move_to( const tripoint &p, bool force, bool step_on_critter, if( has_flag( MF_DRIPS_GASOLINE ) ) { if( one_in( 5 ) ) { // TODO: use same idea that limits napalm dripping - g->m.add_item_or_charges( pos(), item( "gasoline" ) ); + here.add_item_or_charges( pos(), item( "gasoline" ) ); } } return true; @@ -1687,7 +1700,8 @@ bool monster::push_to( const tripoint &p, const int boost, const size_t depth ) return false; } - const int movecost_from = 50 * g->m.move_cost( p ); + map &here = get_map(); + const int movecost_from = 50 * here.move_cost( p ); const int movecost_attacker = std::max( movecost_from, 200 - 10 * ( attack - defend ) ); const tripoint dir = p - pos(); @@ -1707,10 +1721,10 @@ bool monster::push_to( const tripoint &p, const int boost, const size_t depth ) } tripoint dest( p + d ); - const int dest_movecost_from = 50 * g->m.move_cost( dest ); + const int dest_movecost_from = 50 * here.move_cost( dest ); // Pushing into cars/windows etc. is harder - const int movecost_penalty = g->m.move_cost( dest ) - 2; + const int movecost_penalty = here.move_cost( dest ) - 2; if( movecost_penalty <= -2 ) { // Can't push into unpassable terrain continue; @@ -1768,8 +1782,9 @@ bool monster::push_to( const tripoint &p, const int boost, const size_t depth ) g->swap_critters( *critter, *this ); critter->add_effect( effect_stunned, rng( 0_turns, 2_turns ) ); + Character &player_character = get_player_character(); // Only print the message when near player or it can get spammy - if( rl_dist( g->u.pos(), pos() ) < 4 && g->u.sees( *critter ) ) { + if( rl_dist( player_character.pos(), pos() ) < 4 && player_character.sees( *critter ) ) { add_msg( m_warning, _( "The %1$s tramples %2$s" ), name(), critter->disp_name() ); } @@ -1794,18 +1809,19 @@ void monster::stumble() return; } + map &here = get_map(); std::vector valid_stumbles; valid_stumbles.reserve( 11 ); const bool avoid_water = has_flag( MF_NO_BREATHE ) && !swims() && !has_flag( MF_AQUATIC ); - for( const tripoint &dest : g->m.points_in_radius( pos(), 1 ) ) { + for( const tripoint &dest : here.points_in_radius( pos(), 1 ) ) { if( dest != pos() ) { valid_stumbles.push_back( dest ); } } - if( g->m.has_zlevels() ) { + if( here.has_zlevels() ) { tripoint below( posx(), posy(), posz() - 1 ); - if( g->m.valid_move( pos(), below, false, true ) ) { + if( here.valid_move( pos(), below, false, true ) ) { valid_stumbles.push_back( below ); } } @@ -1816,8 +1832,8 @@ void monster::stumble() //(Unless they can swim/are aquatic) //But let them wander OUT of water if they are there. !( avoid_water && - g->m.has_flag( TFLAG_SWIMMABLE, dest ) && - !g->m.has_flag( TFLAG_SWIMMABLE, pos() ) ) && + here.has_flag( TFLAG_SWIMMABLE, dest ) && + !here.has_flag( TFLAG_SWIMMABLE, pos() ) ) && ( g->critter_at( dest, is_hallucination() ) == nullptr ) ) { if( move_to( dest, true, false ) ) { break; @@ -1837,7 +1853,7 @@ void monster::knock_back_to( const tripoint &to ) return; } - bool u_see = g->u.sees( to ); + bool u_see = get_player_character().sees( to ); // First, see if we hit another monster if( monster *const z = g->critter_at( to ) ) { @@ -1883,14 +1899,15 @@ void monster::knock_back_to( const tripoint &to ) } } - if( g->m.impassable( to ) ) { + map &here = get_map(); + if( here.impassable( to ) ) { // It's some kind of wall. apply_damage( nullptr, bodypart_id( "torso" ), static_cast( type->size ) ); add_effect( effect_stunned, 2_turns ); if( u_see ) { add_msg( _( "The %1$s bounces off a %2$s." ), name(), - g->m.obstacle_name( to ) ); + here.obstacle_name( to ) ); } } else { // It's no wall @@ -1907,7 +1924,7 @@ void monster::knock_back_to( const tripoint &to ) */ bool monster::will_reach( const point &p ) { - monster_attitude att = attitude( &g->u ); + monster_attitude att = attitude( &get_player_character() ); if( att != MATT_FOLLOW && att != MATT_ATTACK && att != MATT_FRIEND ) { return false; } @@ -1920,7 +1937,7 @@ bool monster::will_reach( const point &p ) return false; } - auto path = g->m.route( pos(), tripoint( p, posz() ), get_pathfinding_settings() ); + auto path = get_map().route( pos(), tripoint( p, posz() ), get_pathfinding_settings() ); if( path.empty() ) { return false; } @@ -1944,8 +1961,9 @@ bool monster::will_reach( const point &p ) int monster::turns_to_reach( const point &p ) { + map &here = get_map(); // HACK: This function is a(n old) temporary hack that should soon be removed - auto path = g->m.route( pos(), tripoint( p, posz() ), get_pathfinding_settings() ); + auto path = here.route( pos(), tripoint( p, posz() ), get_pathfinding_settings() ); if( path.empty() ) { return 999; } @@ -1953,7 +1971,7 @@ int monster::turns_to_reach( const point &p ) double turns = 0.; for( size_t i = 0; i < path.size(); i++ ) { const tripoint &next = path[i]; - if( g->m.impassable( next ) ) { + if( here.impassable( next ) ) { // No bashing through, it looks stupid when you go back and find // the doors intact. return 999; @@ -1970,8 +1988,9 @@ int monster::turns_to_reach( const point &p ) void monster::shove_vehicle( const tripoint &remote_destination, const tripoint &nearby_destination ) { + map &here = get_map(); if( this->has_flag( MF_PUSH_VEH ) ) { - auto vp = g->m.veh_at( nearby_destination ); + auto vp = here.veh_at( nearby_destination ); if( vp ) { vehicle &veh = vp->vehicle(); const units::mass veh_mass = veh.total_mass(); @@ -2015,10 +2034,10 @@ void monster::shove_vehicle( const tripoint &remote_destination, break; } if( shove_velocity > 0 ) { - if( g->u.sees( this->pos() ) ) { + if( get_player_character().sees( this->pos() ) ) { //~ %1$s - monster name, %2$s - vehicle name - g->u.add_msg_if_player( m_bad, _( "%1$s shoves %2$s out of their way!" ), this->disp_name(), - veh.disp_name() ); + add_msg( m_bad, _( "%1$s shoves %2$s out of their way!" ), this->disp_name(), + veh.disp_name() ); } int shove_moves = shove_veh_mass_moves_factor * veh_mass / 10_kilogram; shove_moves = std::max( shove_moves, shove_moves_minimal ); @@ -2033,10 +2052,10 @@ void monster::shove_vehicle( const tripoint &remote_destination, if( shove_destination.z != 0 ) { veh.vertical_velocity = shove_destination.z < 0 ? -shove_velocity : +shove_velocity; } - g->m.move_vehicle( veh, shove_destination, veh.face ); + here.move_vehicle( veh, shove_destination, veh.face ); } veh.move = tileray( destination_delta.xy() ); - veh.smash( g->m, shove_damage_min, shove_damage_max, 0.10F ); + veh.smash( here, shove_damage_min, shove_damage_max, 0.10F ); } } } diff --git a/src/monster.cpp b/src/monster.cpp index c1bdc725fb615..f525d2d5a4442 100644 --- a/src/monster.cpp +++ b/src/monster.cpp @@ -6,7 +6,6 @@ #include #include -#include "avatar.h" #include "character.h" #include "compatibility.h" #include "coordinate_conversions.h" @@ -45,7 +44,6 @@ #include "output.h" #include "overmapbuffer.h" #include "pimpl.h" -#include "player.h" #include "projectile.h" #include "rng.h" #include "sounds.h" @@ -437,6 +435,7 @@ void monster::try_reproduce() } } + map &here = get_map(); // add a decreasing chance of additional spawns when "catching up" an existing animal int chance = -1; while( true ) { @@ -460,9 +459,9 @@ void monster::try_reproduce() if( season_match && female && one_in( chance ) ) { int spawn_cnt = rng( 1, type->baby_count ); if( type->baby_monster ) { - g->m.add_spawn( type->baby_monster, spawn_cnt, pos() ); + here.add_spawn( type->baby_monster, spawn_cnt, pos() ); } else { - g->m.add_item_or_charges( pos(), item( type->baby_egg, *baby_timer, spawn_cnt ), true ); + here.add_item_or_charges( pos(), item( type->baby_egg, *baby_timer, spawn_cnt ), true ); } } @@ -514,6 +513,7 @@ void monster::try_biosignature() if( !biosig_timer ) { biosig_timer.emplace( calendar::turn + *type->biosig_timer ); } + map &here = get_map(); int counter = 0; while( true ) { // don't catch up too much, otherwise on some scenarios, @@ -521,7 +521,7 @@ void monster::try_biosignature() if( *biosig_timer > calendar::turn || counter > 50 ) { return; } - g->m.add_item_or_charges( pos(), item( type->biosig_item, *biosig_timer, 1 ), true ); + here.add_item_or_charges( pos(), item( type->biosig_item, *biosig_timer, 1 ), true ); *biosig_timer += *type->biosig_timer; counter += 1; } @@ -603,7 +603,7 @@ void monster::get_HP_Bar( nc_color &color, std::string &text ) const std::pair monster::get_attitude() const { - const auto att = attitude_names.at( attitude( &g->u ) ); + const auto att = attitude_names.at( attitude( &get_player_character() ) ); return { _( att.first ), all_colors.get( att.second ) @@ -661,9 +661,10 @@ int monster::print_info( const catacurses::window &w, int vStart, int vLines, in mvwprintz( w, point( column, ++vStart ), att.second, att.first ); // Awareness indicator in the third line. - std::string senses_str = sees( g->u ) ? _( "Can see to your current location" ) : + bool sees_player = sees( get_player_character() ); + std::string senses_str = sees_player ? _( "Can see to your current location" ) : _( "Can't see to your current location" ); - mvwprintz( w, point( column, ++vStart ), sees( g->u ) ? c_red : c_green, senses_str ); + mvwprintz( w, point( column, ++vStart ), sees_player ? c_red : c_green, senses_str ); // Monster description on following lines. std::vector lines = foldstring( type->get_description(), max_width ); @@ -982,7 +983,7 @@ Creature *monster::attack_target() return target; } -bool monster::is_fleeing( player &u ) const +bool monster::is_fleeing( Character &u ) const { if( effect_cache[FLEEING] ) { return true; @@ -997,7 +998,7 @@ bool monster::is_fleeing( player &u ) const Creature::Attitude monster::attitude_to( const Creature &other ) const { const monster *m = other.is_monster() ? static_cast< const monster *>( &other ) : nullptr; - const player *p = other.as_player(); + const Character *p = other.as_character(); if( m != nullptr ) { if( m == this ) { return Attitude::FRIENDLY; @@ -1020,7 +1021,7 @@ Creature::Attitude monster::attitude_to( const Creature &other ) const return Attitude::HOSTILE; } } else if( p != nullptr ) { - switch( attitude( const_cast( p ) ) ) { + switch( attitude( p ) ) { case MATT_FRIEND: return Attitude::FRIENDLY; case MATT_FPASSIVE: @@ -1045,16 +1046,17 @@ monster_attitude monster::attitude( const Character *u ) const if( has_effect( effect_docile ) ) { return MATT_FPASSIVE; } - if( u == &g->u ) { + if( u->is_avatar() ) { return MATT_FRIEND; } - // Zombies don't understand not attacking NPCs, but dogs and bots should. - const npc *np = dynamic_cast< const npc * >( u ); - if( np != nullptr && np->get_attitude() != NPCATT_KILL && !type->in_species( species_ZOMBIE ) ) { - return MATT_FRIEND; - } - if( np != nullptr && np->is_hallucination() ) { - return MATT_IGNORE; + if( u->is_npc() ) { + // Zombies don't understand not attacking NPCs, but dogs and bots should. + if( u->get_attitude() != NPCATT_KILL && !type->in_species( species_ZOMBIE ) ) { + return MATT_FRIEND; + } + if( u->is_hallucination() ) { + return MATT_IGNORE; + } } } if( effect_cache[FLEEING] ) { @@ -1170,8 +1172,9 @@ void monster::process_triggers() process_trigger( mon_trigger::FIRE, [this]() { int ret = 0; - for( const auto &p : g->m.points_in_radius( pos(), 3 ) ) { - ret += 5 * g->m.get_field_intensity( p, fd_fire ); + map &here = get_map(); + for( const auto &p : here.points_in_radius( pos(), 3 ) ) { + ret += 5 * here.get_field_intensity( p, fd_fire ); } return ret; } ); @@ -1359,8 +1362,9 @@ void monster::melee_attack( Creature &target, float accuracy ) return; } + Character &player_character = get_player_character(); if( target.is_player() || - ( target.is_npc() && g->u.attitude_to( target ) == Attitude::FRIENDLY ) ) { + ( target.is_npc() && player_character.attitude_to( target ) == Attitude::FRIENDLY ) ) { // Make us a valid target for a few turns add_effect( effect_hit_by_player, 3_turns ); } @@ -1369,7 +1373,7 @@ void monster::melee_attack( Creature &target, float accuracy ) add_effect( effect_run, 4_turns ); } - const bool u_see_me = g->u.sees( *this ); + const bool u_see_me = player_character.sees( *this ); damage_instance damage = !is_hallucination() ? type->melee_damage : damage_instance(); if( !is_hallucination() && type->melee_dice > 0 ) { @@ -1409,7 +1413,8 @@ void monster::melee_attack( Creature &target, float accuracy ) add_msg( m_bad, _( "The %1$s hits your %2$s." ), name(), body_part_name_accusative( dealt_dam.bp_hit ) ); } else if( target.is_npc() ) { - if( has_effect( effect_ridden ) && has_flag( MF_RIDEABLE_MECH ) && pos() == g->u.pos() ) { + if( has_effect( effect_ridden ) && has_flag( MF_RIDEABLE_MECH ) && + pos() == player_character.pos() ) { //~ %1$s: name of your mount, %2$s: target NPC name, %3$d: damage value add_msg( m_good, _( "Your %1$s hits %2$s for %3$d damage!" ), name(), target.disp_name(), total_dealt ); @@ -1420,7 +1425,8 @@ void monster::melee_attack( Creature &target, float accuracy ) body_part_name_accusative( dealt_dam.bp_hit ) ); } } else { - if( has_effect( effect_ridden ) && has_flag( MF_RIDEABLE_MECH ) && pos() == g->u.pos() ) { + if( has_effect( effect_ridden ) && has_flag( MF_RIDEABLE_MECH ) && + pos() == player_character.pos() ) { //~ %1$s: name of your mount, %2$s: target creature name, %3$d: damage value add_msg( m_good, _( "Your %1$s hits %2$s for %3$d damage!" ), get_name(), target.disp_name(), total_dealt ); @@ -1648,7 +1654,8 @@ bool monster::move_effects( bool ) return true; } - bool u_see_me = g->u.sees( *this ); + map &here = get_map(); + bool u_see_me = get_player_character().sees( *this ); if( has_effect( effect_tied ) ) { // friendly pet, will stay tied down and obey. if( friendly == -1 ) { @@ -1668,7 +1675,7 @@ bool monster::move_effects( bool ) if( u_see_me ) { add_msg( _( "The %s easily slips out of its bonds." ), name() ); } - g->m.add_item_or_charges( pos(), *tied_item ); + here.add_item_or_charges( pos(), *tied_item ); tied_item.reset(); } } else { @@ -1676,7 +1683,7 @@ bool monster::move_effects( bool ) const bool broken = rng( type->melee_dice * type->melee_sides, std::min( 10000, type->melee_dice * type->melee_sides * 250 ) ) > 800; if( !broken ) { - g->m.add_item_or_charges( pos(), *tied_item ); + here.add_item_or_charges( pos(), *tied_item ); } tied_item.reset(); if( u_see_me ) { @@ -1716,8 +1723,8 @@ bool monster::move_effects( bool ) if( has_effect( effect_lightsnare ) ) { if( x_in_y( type->melee_dice * type->melee_sides, 12 ) ) { remove_effect( effect_lightsnare ); - g->m.spawn_item( pos(), "string_36" ); - g->m.spawn_item( pos(), "snare_trigger" ); + here.spawn_item( pos(), "string_36" ); + here.spawn_item( pos(), "snare_trigger" ); if( u_see_me ) { add_msg( _( "The %s escapes the light snare!" ), name() ); } @@ -1728,8 +1735,8 @@ bool monster::move_effects( bool ) if( type->melee_dice * type->melee_sides >= 7 ) { if( x_in_y( type->melee_dice * type->melee_sides, 32 ) ) { remove_effect( effect_heavysnare ); - g->m.spawn_item( pos(), "rope_6" ); - g->m.spawn_item( pos(), "snare_trigger" ); + here.spawn_item( pos(), "rope_6" ); + here.spawn_item( pos(), "snare_trigger" ); if( u_see_me ) { add_msg( _( "The %s escapes the heavy snare!" ), name() ); } @@ -1741,7 +1748,7 @@ bool monster::move_effects( bool ) if( type->melee_dice * type->melee_sides >= 18 ) { if( x_in_y( type->melee_dice * type->melee_sides, 200 ) ) { remove_effect( effect_beartrap ); - g->m.spawn_item( pos(), "beartrap" ); + here.spawn_item( pos(), "beartrap" ); if( u_see_me ) { add_msg( _( "The %s escapes the bear trap!" ), name() ); } @@ -1986,7 +1993,7 @@ int monster::impact( const int force, const tripoint &p ) const float mod = fall_damage_mod(); int total_dealt = 0; - if( g->m.has_flag( TFLAG_SHARP, p ) ) { + if( get_map().has_flag( TFLAG_SHARP, p ) ) { const int cut_damage = std::max( 0.0f, 10 * mod - get_armor_cut( bodypart_id( "torso" ) ) ); apply_damage( nullptr, bodypart_id( "torso" ), cut_damage ); total_dealt += 10 * mod; @@ -2066,6 +2073,7 @@ void monster::decrement_summon_timer() void monster::process_turn() { + map &here = get_map(); decrement_summon_timer(); if( !is_hallucination() ) { for( const std::pair &e : type->emit_fields ) { @@ -2077,11 +2085,11 @@ void monster::process_turn() if( has_effect( effect_emp ) ) { continue; // don't emit electricity while EMPed } else if( has_effect( effect_supercharged ) ) { - g->m.emit_field( pos(), emit_id( "emit_shock_cloud_big" ) ); + here.emit_field( pos(), emit_id( "emit_shock_cloud_big" ) ); continue; } } - g->m.emit_field( pos(), emid ); + here.emit_field( pos(), emid ); } } @@ -2104,7 +2112,7 @@ void monster::process_turn() } // Persist grabs as long as there's an adjacent target. if( has_effect( effect_grabbing ) ) { - for( auto &dest : g->m.points_in_radius( pos(), 1, 0 ) ) { + for( auto &dest : here.points_in_radius( pos(), 1, 0 ) ) { const player *const p = g->critter_at( dest ); if( p && p->has_effect( effect_grabbed ) ) { add_effect( effect_grabbing, 2_turns ); @@ -2118,12 +2126,13 @@ void monster::process_turn() sounds::sound( pos(), 5, sounds::sound_t::combat, _( "hummmmm." ), false, "humming", "electric" ); } } else { - for( const tripoint &zap : g->m.points_in_radius( pos(), 1 ) ) { - const bool player_sees = g->u.sees( zap ); - const map_stack items = g->m.i_at( zap ); + Character &player_character = get_player_character(); + for( const tripoint &zap : here.points_in_radius( pos(), 1 ) ) { + const bool player_sees = player_character.sees( zap ); + const map_stack items = here.i_at( zap ); for( const auto &item : items ) { if( item.made_of( phase_id::LIQUID ) && item.flammable() ) { // start a fire! - g->m.add_field( zap, fd_fire, 2, 1_minutes ); + here.add_field( zap, fd_fire, 2, 1_minutes ); sounds::sound( pos(), 30, sounds::sound_t::combat, _( "fwoosh!" ), false, "fire", "ignition" ); break; } @@ -2131,33 +2140,33 @@ void monster::process_turn() if( zap != pos() ) { explosion_handler::emp_blast( zap ); // Fries electronics due to the intensity of the field } - const auto t = g->m.ter( zap ); + const auto t = here.ter( zap ); if( t == ter_str_id( "t_gas_pump" ) || t == ter_str_id( "t_gas_pump_a" ) ) { if( one_in( 4 ) ) { explosion_handler::explosion( pos(), 40, 0.8, true ); if( player_sees ) { - add_msg( m_warning, _( "The %s explodes in a fiery inferno!" ), g->m.tername( zap ) ); + add_msg( m_warning, _( "The %s explodes in a fiery inferno!" ), here.tername( zap ) ); } } else { if( player_sees ) { add_msg( m_warning, _( "Lightning from %1$s engulfs the %2$s!" ), name(), - g->m.tername( zap ) ); + here.tername( zap ) ); } - g->m.add_field( zap, fd_fire, 1, 2_turns ); + here.add_field( zap, fd_fire, 1, 2_turns ); } } } if( g->weather.lightning_active && !has_effect( effect_supercharged ) && - g->m.is_outside( pos() ) ) { + here.is_outside( pos() ) ) { g->weather.lightning_active = false; // only one supercharge per strike sounds::sound( pos(), 300, sounds::sound_t::combat, _( "BOOOOOOOM!!!" ), false, "environment", "thunder_near" ); sounds::sound( pos(), 20, sounds::sound_t::combat, _( "vrrrRRRUUMMMMMMMM!" ), false, "explosion", "default" ); - if( g->u.sees( pos() ) ) { + if( player_character.sees( pos() ) ) { add_msg( m_bad, _( "Lightning strikes the %s!" ), name() ); add_msg( m_bad, _( "Your vision goes white!" ) ); - g->u.add_effect( effect_blind, rng( 1_minutes, 2_minutes ) ); + player_character.add_effect( effect_blind, rng( 1_minutes, 2_minutes ) ); } add_effect( effect_supercharged, 12_hours ); } else if( has_effect( effect_supercharged ) && calendar::once_every( 5_turns ) ) { @@ -2225,15 +2234,16 @@ void monster::die( Creature *nkiller ) if( has_effect( effect_beartrap ) ) { add_item( item( "beartrap", 0 ) ); } + map &here = get_map(); if( has_effect( effect_grabbing ) ) { remove_effect( effect_grabbing ); - for( auto &player_pos : g->m.points_in_radius( pos(), 1, 0 ) ) { + for( auto &player_pos : here.points_in_radius( pos(), 1, 0 ) ) { player *p = g->critter_at( player_pos ); if( !p || !p->has_effect( effect_grabbed ) ) { continue; } bool grabbed = false; - for( auto &mon_pos : g->m.points_in_radius( player_pos, 1, 0 ) ) { + for( auto &mon_pos : here.points_in_radius( player_pos, 1, 0 ) ) { const monster *const mon = g->critter_at( mon_pos ); if( mon && mon->has_effect( effect_grabbing ) ) { grabbed = true; @@ -2249,7 +2259,7 @@ void monster::die( Creature *nkiller ) } if( !is_hallucination() ) { for( const auto &it : inv ) { - g->m.add_item_or_charges( pos(), it ); + here.add_item_or_charges( pos(), it ); } } @@ -2257,7 +2267,7 @@ void monster::die( Creature *nkiller ) if( !is_hallucination() && has_flag( MF_QUEEN ) ) { // The submap coordinates of this monster, monster groups coordinates are // submap coordinates. - const tripoint abssub = ms_to_sm_copy( g->m.getabs( pos() ) ); + const tripoint abssub = ms_to_sm_copy( here.getabs( pos() ) ); // Do it for overmap above/below too for( const tripoint &p : points_in_radius( abssub, HALF_MAPSIZE, 1 ) ) { for( auto &mgp : overmap_buffer.groups_at( p ) ) { @@ -2300,7 +2310,7 @@ void monster::die( Creature *nkiller ) continue; } - if( g->m.sees( critter.pos(), pos(), light ) ) { + if( here.sees( critter.pos(), pos(), light ) ) { critter.morale += morale_adjust; critter.anger += anger_adjust; } @@ -2368,7 +2378,7 @@ void monster::drop_items_on_death() items = remaining; } - const auto dropped = g->m.spawn_items( pos(), items ); + const auto dropped = get_map().spawn_items( pos(), items ); if( has_flag( MF_FILTHY ) ) { for( const auto &it : dropped ) { @@ -2445,9 +2455,10 @@ void monster::process_effects() set_speed_bonus( min_speed_bonus ); } + Character &player_character = get_player_character(); //If this monster has the ability to heal in combat, do it now. const int healed_amount = heal( type->regenerates ); - if( healed_amount > 0 && one_in( 2 ) && g->u.sees( *this ) ) { + if( healed_amount > 0 && one_in( 2 ) && player_character.sees( *this ) ) { std::string healing_format_string; if( healed_amount >= 50 ) { healing_format_string = _( "The %s is visibly regenerating!" ); @@ -2460,10 +2471,10 @@ void monster::process_effects() } if( type->regenerates_in_dark ) { - const float light = g->m.ambient_light_at( pos() ); + const float light = get_map().ambient_light_at( pos() ); // Magic number 10000 was chosen so that a floodlight prevents regeneration in a range of 20 tiles if( heal( static_cast( 50.0 * std::exp( - light * light / 10000 ) ) > 0 && one_in( 2 ) && - g->u.sees( *this ) ) ) { + player_character.sees( *this ) ) ) { add_msg( m_warning, _( "The %s uses the darkness to regenerate." ), name() ); } } @@ -2471,7 +2482,7 @@ void monster::process_effects() //Monster will regen morale and aggression if it is on max HP //It regens more morale and aggression if is currently fleeing. if( type->regen_morale && hp >= type->hp ) { - if( is_fleeing( g->u ) ) { + if( is_fleeing( player_character ) ) { morale = type->morale; anger = type->agro; } @@ -2491,7 +2502,7 @@ void monster::process_effects() // If this critter dies in sunlight, check & assess damage. if( has_flag( MF_SUNDEATH ) && g->is_in_sunlight( pos() ) ) { - if( g->u.sees( *this ) ) { + if( player_character.sees( *this ) ) { add_msg( m_good, _( "The %s burns horribly in the sunlight!" ), name() ); } apply_damage( nullptr, bodypart_id( "torso" ), 100 ); @@ -2587,7 +2598,7 @@ bool monster::make_fungus() return false; } - if( g->u.sees( pos() ) ) { + if( get_player_character().sees( pos() ) ) { add_msg( m_info, _( "The spores transform %1$s into a %2$s!" ), old_name, name() ); } @@ -2654,7 +2665,7 @@ units::volume monster::get_volume() const void monster::add_msg_if_npc( const std::string &msg ) const { - if( g->u.sees( *this ) ) { + if( get_player_character().sees( *this ) ) { add_msg( replace_with_npc_name( msg ) ); } } @@ -2662,14 +2673,14 @@ void monster::add_msg_if_npc( const std::string &msg ) const void monster::add_msg_player_or_npc( const std::string &/*player_msg*/, const std::string &npc_msg ) const { - if( g->u.sees( *this ) ) { + if( get_player_character().sees( *this ) ) { add_msg( replace_with_npc_name( npc_msg ) ); } } void monster::add_msg_if_npc( const game_message_params ¶ms, const std::string &msg ) const { - if( g->u.sees( *this ) ) { + if( get_player_character().sees( *this ) ) { add_msg( params, replace_with_npc_name( msg ) ); } } @@ -2677,7 +2688,7 @@ void monster::add_msg_if_npc( const game_message_params ¶ms, const std::stri void monster::add_msg_player_or_npc( const game_message_params ¶ms, const std::string &/*player_msg*/, const std::string &npc_msg ) const { - if( g->u.sees( *this ) ) { + if( get_player_character().sees( *this ) ) { add_msg( params, replace_with_npc_name( npc_msg ) ); } } @@ -2816,12 +2827,13 @@ void monster::on_hit( Creature *source, bodypart_id, if( anger_adjust != 0 || morale_adjust != 0 ) { int light = g->light_level( posz() ); + map &here = get_map(); for( monster &critter : g->all_monsters() ) { if( !critter.type->same_species( *type ) ) { continue; } - if( g->m.sees( critter.pos(), pos(), light ) ) { + if( here.sees( critter.pos(), pos(), light ) ) { critter.morale += morale_adjust; critter.anger += anger_adjust; } @@ -2918,7 +2930,7 @@ bool monster::will_join_horde( int size ) return false; } else if( mha == MHA_ALWAYS ) { return true; - } else if( g->m.has_flag( TFLAG_INDOORS, pos() ) && ( mha == MHA_OUTDOORS || + } else if( get_map().has_flag( TFLAG_INDOORS, pos() ) && ( mha == MHA_OUTDOORS || mha == MHA_OUTDOORS_AND_LARGE ) ) { return false; } else if( size < 3 && ( mha == MHA_LARGE || mha == MHA_OUTDOORS_AND_LARGE ) ) { diff --git a/src/monster.h b/src/monster.h index d4e71015b717f..418bddec9373c 100644 --- a/src/monster.h +++ b/src/monster.h @@ -295,7 +295,7 @@ class monster : public Creature void knock_back_to( const tripoint &to ) override; // Combat - bool is_fleeing( player &u ) const; // True if we're fleeing + bool is_fleeing( Character &u ) const; monster_attitude attitude( const Character *u = nullptr ) const; // See the enum above Attitude attitude_to( const Creature &other ) const override; void process_triggers(); // Process things that anger/scare us diff --git a/src/npc.h b/src/npc.h index 48136e010532d..599c54544de2f 100644 --- a/src/npc.h +++ b/src/npc.h @@ -1243,7 +1243,7 @@ class npc : public player */ void setpos( const tripoint &pos ) override; void travel_overmap( const tripoint &pos ); - npc_attitude get_attitude() const; + npc_attitude get_attitude() const override; void set_attitude( npc_attitude new_attitude ); void set_mission( npc_mission new_mission ); bool has_activity() const; From 61764cb4efbcffd8a7b19ef5bfa4927282001862 Mon Sep 17 00:00:00 2001 From: Mark Langsdorf Date: Thu, 2 Jul 2020 17:18:00 -0500 Subject: [PATCH 047/420] items: fix the effective DPS test Halligan bars got heavier, so their moves per attack increased, and their DPS dropped, breaking the effective DPS test. Slightly increase the damage of Halligan bars and then adjust the expected damage in the test. Inferior katanas should have less balance than real katanas, so reduce the to_hit and adjust the effective DPS test. Tighten the margin for the effective DPS test, and slightly adjust some expected values to keep everything in range. --- data/json/items/melee/swords_and_blades.json | 2 +- data/json/items/tool/firefighting.json | 2 +- tests/effective_dps_test.cpp | 50 ++++++++++---------- 3 files changed, 27 insertions(+), 27 deletions(-) diff --git a/data/json/items/melee/swords_and_blades.json b/data/json/items/melee/swords_and_blades.json index b522cba17549d..605412f37da43 100644 --- a/data/json/items/melee/swords_and_blades.json +++ b/data/json/items/melee/swords_and_blades.json @@ -1411,7 +1411,7 @@ "longest_side": "90 cm", "bashing": 17, "cutting": 8, - "to_hit": 2, + "to_hit": 1, "category": "weapons", "qualities": [ [ "CUT", 1 ], [ "BUTCHER", 9 ] ] }, diff --git a/data/json/items/tool/firefighting.json b/data/json/items/tool/firefighting.json index 36e62708d7c11..5525c81a809ad 100644 --- a/data/json/items/tool/firefighting.json +++ b/data/json/items/tool/firefighting.json @@ -81,7 +81,7 @@ "longest_side": "60 cm", "price": 7500, "price_postapoc": 1500, - "bashing": 40, + "bashing": 45, "cutting": 5, "material": [ "steel" ], "symbol": ";", diff --git a/tests/effective_dps_test.cpp b/tests/effective_dps_test.cpp index 00761c3e18c1f..88bd803bbfd1d 100644 --- a/tests/effective_dps_test.cpp +++ b/tests/effective_dps_test.cpp @@ -250,8 +250,8 @@ static void make_experienced_tester( avatar &test_guy ) static void calc_expected_dps( avatar &test_guy, const std::string &weapon_id, double target ) { item weapon( weapon_id ); - CHECK( test_guy.melee_value( weapon ) == Approx( target ).margin( 0.75 ) ); - if( test_guy.melee_value( weapon ) != Approx( target ).margin( 0.75 ) ) { + CHECK( test_guy.melee_value( weapon ) == Approx( target ).margin( 0.5 ) ); + if( test_guy.melee_value( weapon ) != Approx( target ).margin( 0.5 ) ) { std::cout << weapon_id << " out of range, expected: " << target; std::cout << " got " << test_guy.melee_value( weapon ) << std::endl; } @@ -269,15 +269,15 @@ TEST_CASE( "expected weapon dps", "[expected][dps]" ) SECTION( "staves" ) { // typical value around 18 calc_expected_dps( test_guy, "i_staff", 18.0 ); calc_expected_dps( test_guy, "q_staff", 17.0 ); - calc_expected_dps( test_guy, "l-stick_on", 18.0 ); - calc_expected_dps( test_guy, "l-stick", 18.0 ); + calc_expected_dps( test_guy, "l-stick_on", 17.5 ); + calc_expected_dps( test_guy, "l-stick", 17.5 ); calc_expected_dps( test_guy, "shock_staff", 17.0 ); - calc_expected_dps( test_guy, "hockey_stick", 13.0 ); + calc_expected_dps( test_guy, "hockey_stick", 13.75 ); calc_expected_dps( test_guy, "pool_cue", 10.0 ); - calc_expected_dps( test_guy, "broom", 4.0 ); + calc_expected_dps( test_guy, "broom", 3.25 ); } SECTION( "spear" ) { // typical value around 24 - calc_expected_dps( test_guy, "spear_steel", 24.0 ); + calc_expected_dps( test_guy, "spear_steel", 24.5 ); calc_expected_dps( test_guy, "pike", 23.0 ); calc_expected_dps( test_guy, "qiang", 23.0 ); calc_expected_dps( test_guy, "spear_dory", 23 ); @@ -297,9 +297,9 @@ TEST_CASE( "expected weapon dps", "[expected][dps]" ) calc_expected_dps( test_guy, "halberd", 36.0 ); calc_expected_dps( test_guy, "halberd_fake", 15.0 ); calc_expected_dps( test_guy, "ji", 35.0 ); - calc_expected_dps( test_guy, "glaive", 35.0 ); + calc_expected_dps( test_guy, "glaive", 34.5 ); calc_expected_dps( test_guy, "naginata", 35.0 ); - calc_expected_dps( test_guy, "naginata_inferior", 21.0 ); + calc_expected_dps( test_guy, "naginata_inferior", 21.5 ); calc_expected_dps( test_guy, "naginata_fake", 10.0 ); calc_expected_dps( test_guy, "lucern_hammer", 36.0 ); calc_expected_dps( test_guy, "lucern_hammerfake", 14.0 ); @@ -309,10 +309,10 @@ TEST_CASE( "expected weapon dps", "[expected][dps]" ) SECTION( "two-handed axe" ) { // typical value around 29 calc_expected_dps( test_guy, "battleaxe", 29.0 ); calc_expected_dps( test_guy, "battleaxe_fake", 11.0 ); - calc_expected_dps( test_guy, "battleaxe_inferior", 20.0 ); + calc_expected_dps( test_guy, "battleaxe_inferior", 19.25 ); calc_expected_dps( test_guy, "fire_ax", 25.0 ); calc_expected_dps( test_guy, "lobotomizer", 24.0 ); - calc_expected_dps( test_guy, "ax", 21.0 ); + calc_expected_dps( test_guy, "ax", 20.25 ); calc_expected_dps( test_guy, "copper_ax", 12.0 ); calc_expected_dps( test_guy, "e_combatsaw_on", 28.0 ); calc_expected_dps( test_guy, "combatsaw_on", 28.0 ); @@ -325,12 +325,12 @@ TEST_CASE( "expected weapon dps", "[expected][dps]" ) calc_expected_dps( test_guy, "combatsaw_off", 3.0 ); calc_expected_dps( test_guy, "chainsaw_off", 2.0 ); calc_expected_dps( test_guy, "cs_lajatang_off", 2.5 ); - calc_expected_dps( test_guy, "circsaw_off", 2.0 ); + calc_expected_dps( test_guy, "circsaw_off", 1.25 ); } SECTION( "two-handed club/hammer" ) { // expected value ideally around 28 calc_expected_dps( test_guy, "warhammer", 28.0 ); calc_expected_dps( test_guy, "hammer_sledge", 20.0 ); - calc_expected_dps( test_guy, "halligan", 17.0 ); + calc_expected_dps( test_guy, "halligan", 15.25 ); calc_expected_dps( test_guy, "stick_long", 6.0 ); } SECTION( "two-handed flails" ) { // expected value ideally around 28 @@ -339,7 +339,7 @@ TEST_CASE( "expected weapon dps", "[expected][dps]" ) calc_expected_dps( test_guy, "homewrecker", 13.0 ); } SECTION( "fist weapons" ) { // expected value around 10 but wide variation - calc_expected_dps( test_guy, "bio_claws_weapon", 18.0 ); // basically a knife + calc_expected_dps( test_guy, "bio_claws_weapon", 17.25 ); // basically a knife calc_expected_dps( test_guy, "bagh_nakha", 14.5 ); calc_expected_dps( test_guy, "punch_dagger", 11.0 ); calc_expected_dps( test_guy, "knuckle_katar", 10.5 ); @@ -383,7 +383,7 @@ TEST_CASE( "expected weapon dps", "[expected][dps]" ) calc_expected_dps( test_guy, "golf_club", 14.0 ); calc_expected_dps( test_guy, "mace_fake", 13.0 ); calc_expected_dps( test_guy, "claw_bar", 11.0 ); - calc_expected_dps( test_guy, "shovel", 11.0 ); + calc_expected_dps( test_guy, "shovel", 10.25 ); calc_expected_dps( test_guy, "e_tool", 11.0 ); calc_expected_dps( test_guy, "sword_nail", 11.0 ); calc_expected_dps( test_guy, "sword_wood", 10.5 ); @@ -401,15 +401,15 @@ TEST_CASE( "expected weapon dps", "[expected][dps]" ) calc_expected_dps( test_guy, "rock", 6.0 ); } SECTION( "two-handed sword" ) { // expected value around 27, 25 for long swords - calc_expected_dps( test_guy, "nodachi", 27.0 ); + calc_expected_dps( test_guy, "nodachi", 26.5 ); calc_expected_dps( test_guy, "zweihander", 27.0 ); calc_expected_dps( test_guy, "estoc", 27.0 ); - calc_expected_dps( test_guy, "longsword", 25.0 ); + calc_expected_dps( test_guy, "longsword", 24.25 ); calc_expected_dps( test_guy, "katana", 25.0 ); calc_expected_dps( test_guy, "longsword_inferior", 18.5 ); calc_expected_dps( test_guy, "zweihander_inferior", 16.5 ); - calc_expected_dps( test_guy, "katana_inferior", 16.0 ); - calc_expected_dps( test_guy, "nodachi_inferior", 17.0 ); + calc_expected_dps( test_guy, "katana_inferior", 14.0 ); + calc_expected_dps( test_guy, "nodachi_inferior", 16.25 ); calc_expected_dps( test_guy, "estoc_inferior", 16.0 ); calc_expected_dps( test_guy, "estoc_fake", 11.0 ); calc_expected_dps( test_guy, "zweihander_fake", 10.0 ); @@ -428,16 +428,16 @@ TEST_CASE( "expected weapon dps", "[expected][dps]" ) calc_expected_dps( test_guy, "broadsword_fake", 10.0 ); calc_expected_dps( test_guy, "rapier_fake", 8.0 ); calc_expected_dps( test_guy, "arming_sword_fake", 13.0 ); - calc_expected_dps( test_guy, "jian_fake", 8.0 ); + calc_expected_dps( test_guy, "jian_fake", 8.25 ); calc_expected_dps( test_guy, "glass_macuahuitl", 11.0 ); - calc_expected_dps( test_guy, "blade_scythe", 6.0 ); + calc_expected_dps( test_guy, "blade_scythe", 5.25 ); } SECTION( "shortsword" ) { // expected value 22 calc_expected_dps( test_guy, "scimitar", 22.0 ); calc_expected_dps( test_guy, "butterfly_swords", 22.0 ); calc_expected_dps( test_guy, "cutlass", 22.0 ); - calc_expected_dps( test_guy, "sword_bayonet", 22.0 ); - calc_expected_dps( test_guy, "kukri", 22.0 ); + calc_expected_dps( test_guy, "sword_bayonet", 22.75 ); + calc_expected_dps( test_guy, "kukri", 22.75 ); calc_expected_dps( test_guy, "wakizashi", 22.0 ); calc_expected_dps( test_guy, "sword_xiphos", 22.0 ); calc_expected_dps( test_guy, "khopesh", 21.0 ); @@ -452,7 +452,7 @@ TEST_CASE( "expected weapon dps", "[expected][dps]" ) calc_expected_dps( test_guy, "wakizashi_inferior", 13.0 ); calc_expected_dps( test_guy, "makeshift_machete", 11.0 ); calc_expected_dps( test_guy, "cavalry_sabre_fake", 8.0 ); - calc_expected_dps( test_guy, "cutlass_fake", 7.0 ); + calc_expected_dps( test_guy, "cutlass_fake", 7.75 ); calc_expected_dps( test_guy, "scimitar_fake", 7.0 ); calc_expected_dps( test_guy, "wakizashi_fake", 7.0 ); calc_expected_dps( test_guy, "blade", 7.0 ); @@ -477,7 +477,7 @@ TEST_CASE( "expected weapon dps", "[expected][dps]" ) calc_expected_dps( test_guy, "copper_knife", 8.0 ); calc_expected_dps( test_guy, "knife_butcher", 7.5 ); calc_expected_dps( test_guy, "throwing_knife", 7.0 ); - calc_expected_dps( test_guy, "tanto_fake", 7.0 ); + calc_expected_dps( test_guy, "tanto_fake", 7.75 ); calc_expected_dps( test_guy, "pockknife", 4.5 ); calc_expected_dps( test_guy, "spike", 4.0 ); calc_expected_dps( test_guy, "kris_fake", 2.5 ); From 7194cdc5c17f84498b4aecdc44bce07e4d79a420 Mon Sep 17 00:00:00 2001 From: Curtis Merrill Date: Thu, 2 Jul 2020 19:16:43 -0400 Subject: [PATCH 048/420] cans spawn sealed (#41745) --- src/item_group.cpp | 2 ++ src/item_pocket.cpp | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/item_group.cpp b/src/item_group.cpp index ddfaa377a1ae1..10bb167b12919 100644 --- a/src/item_group.cpp +++ b/src/item_group.cpp @@ -378,6 +378,7 @@ void Item_modifier::modify( item &new_item ) const if( !cont.is_null() ) { cont.put_in( new_item, item_pocket::pocket_type::CONTAINER ); + cont.seal(); new_item = cont; } @@ -386,6 +387,7 @@ void Item_modifier::modify( item &new_item ) const for( const item &it : contentitems ) { new_item.put_in( it, item_pocket::pocket_type::CONTAINER ); } + new_item.seal(); } for( auto &flag : custom_flags ) { diff --git a/src/item_pocket.cpp b/src/item_pocket.cpp index af3f0d59c0f7a..f248ff08fb57a 100644 --- a/src/item_pocket.cpp +++ b/src/item_pocket.cpp @@ -1129,7 +1129,7 @@ void item_pocket::on_pickup( Character &guy ) void item_pocket::on_contents_changed() { - _sealed = false; + unseal(); restack(); } From 4f526274177dd3b42c211554660f79af93c7cdf4 Mon Sep 17 00:00:00 2001 From: Mark Langsdorf Date: Mon, 6 Apr 2020 11:24:36 -0500 Subject: [PATCH 049/420] vehicles: check that the return of part_displayed_at exists part_id_string() calls part_displayed_at() but doesn't check that the return value is valid, even though part_displayed_at can return negative values. Check for an invalid return and handle it. --- src/vehicle_display.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/vehicle_display.cpp b/src/vehicle_display.cpp index 3895e85d6f551..6445b7cc234e7 100644 --- a/src/vehicle_display.cpp +++ b/src/vehicle_display.cpp @@ -47,8 +47,9 @@ char vehicle::part_sym( const int p, const bool exact ) const } } -// similar to part_sym(int p) but for use when drawing SDL tiles. Called only by cata_tiles during draw_vpart -// vector returns at least 1 element, max of 2 elements. If 2 elements the second denotes if it is open or damaged +// similar to part_sym(int p) but for use when drawing SDL tiles. Called only by cata_tiles +// during draw_vpart vector returns at least 1 element, max of 2 elements. If 2 elements the +// second denotes if it is open or damaged vpart_id vehicle::part_id_string( const int p, char &part_mod ) const { part_mod = 0; @@ -57,6 +58,11 @@ vpart_id vehicle::part_id_string( const int p, char &part_mod ) const } int displayed_part = part_displayed_at( parts[p].mount ); + if( displayed_part < 0 || displayed_part >= static_cast( parts.size() ) || + parts[ displayed_part ].removed ) { + return vpart_id::NULL_ID(); + } + const vpart_id idinfo = parts[displayed_part].id; if( part_flag( displayed_part, VPFLAG_OPENABLE ) && parts[displayed_part].open ) { From ef5f89a2da2e2882888cc9b084716d4fd0e8ed51 Mon Sep 17 00:00:00 2001 From: Mark Langsdorf Date: Thu, 2 Apr 2020 10:16:19 -0500 Subject: [PATCH 050/420] vehicles: make precalc a tripoint mostly working --- src/vehicle.cpp | 38 ++++++++++++++++++++------------------ src/vehicle.h | 6 +++--- src/vehicle_move.cpp | 2 +- 3 files changed, 24 insertions(+), 22 deletions(-) diff --git a/src/vehicle.cpp b/src/vehicle.cpp index f4954245dc703..7266bad5efe55 100644 --- a/src/vehicle.cpp +++ b/src/vehicle.cpp @@ -1023,11 +1023,13 @@ void vehicle::smash( map &m, float hp_percent_loss_min, float hp_percent_loss_ma int roll = dice( 1, 1000 ); int pct_af = ( percent_of_parts_to_affect * 1000.0f ); if( roll < pct_af ) { - double dist = damage_size == 0.0 ? 1.0 : - clamp( 1.0 - trig_dist( damage_origin, part.precalc[0] ) / damage_size, 0.0, 1.0 ); + double dist = damage_size == 0.0f ? 1.0f : + clamp( 1.0f - trig_dist( damage_origin, part.precalc[0].xy() ) / + damage_size, 0.0, 1.0 ); //Everywhere else, drop by 10-120% of max HP (anything over 100 = broken) if( mod_hp( part, 0 - ( rng_float( hp_percent_loss_min * dist, - hp_percent_loss_max * dist ) * part.info().durability ), DT_BASH ) ) { + hp_percent_loss_max * dist ) * + part.info().durability ), DT_BASH ) ) { part.ammo_unset(); } } @@ -2610,7 +2612,7 @@ bool vehicle::has_part( const tripoint &pos, const std::string &flag, bool enabl const tripoint relative_pos = pos - global_pos3(); for( const auto &e : parts ) { - if( e.precalc[0].x != relative_pos.x || e.precalc[0].y != relative_pos.y ) { + if( e.precalc[0] != relative_pos ) { continue; } if( !e.removed && ( !enabled || e.enabled ) && !e.is_broken() && e.info().has_flag( flag ) ) { @@ -2626,7 +2628,7 @@ std::vector vehicle::get_parts_at( const tripoint &pos, const st const tripoint relative_pos = pos - global_pos3(); std::vector res; for( auto &e : parts ) { - if( e.precalc[ 0 ].x != relative_pos.x || e.precalc[ 0 ].y != relative_pos.y ) { + if( e.precalc[ 0 ] != relative_pos ) { continue; } if( !e.removed && @@ -2646,7 +2648,7 @@ std::vector vehicle::get_parts_at( const tripoint &pos, const tripoint relative_pos = pos - global_pos3(); std::vector res; for( const auto &e : parts ) { - if( e.precalc[ 0 ].x != relative_pos.x || e.precalc[ 0 ].y != relative_pos.y ) { + if( e.precalc[ 0 ] != relative_pos ) { continue; } if( !e.removed && @@ -2936,7 +2938,7 @@ bool vehicle::part_flag( int part, const vpart_bitflags flag ) const int vehicle::part_at( const point &dp ) const { for( const vpart_reference &vp : get_all_parts() ) { - if( vp.part().precalc[0] == dp && !vp.part().removed ) { + if( vp.part().precalc[0].xy() == dp && !vp.part().removed ) { return static_cast( vp.part_index() ); } } @@ -3030,12 +3032,12 @@ int vehicle::roof_at_part( const int part ) const point vehicle::coord_translate( const point &p ) const { - point q; + tripoint q; coord_translate( pivot_rotation[0], pivot_anchor[0], p, q ); - return q; + return q.xy(); } -void vehicle::coord_translate( int dir, const point &pivot, const point &p, point &q ) const +void vehicle::coord_translate( int dir, const point &pivot, const point &p, tripoint &q ) const { tileray tdir( dir ); tdir.advance( p.x - pivot.x ); @@ -3043,7 +3045,7 @@ void vehicle::coord_translate( int dir, const point &pivot, const point &p, poin q.y = tdir.dy() + tdir.ortho_dy( p.y - pivot.y ); } -void vehicle::coord_translate( tileray tdir, const point &pivot, const point &p, point &q ) const +void vehicle::coord_translate( tileray tdir, const point &pivot, const point &p, tripoint &q ) const { tdir.clear_advance(); tdir.advance( p.x - pivot.x ); @@ -3053,9 +3055,9 @@ void vehicle::coord_translate( tileray tdir, const point &pivot, const point &p, point vehicle::rotate_mount( int old_dir, int new_dir, const point &pivot, const point &p ) const { - point q; + tripoint q; coord_translate( new_dir - old_dir, pivot, p, q ); - return q; + return q.xy(); } tripoint vehicle::mount_to_tripoint( const point &mount ) const @@ -3065,7 +3067,7 @@ tripoint vehicle::mount_to_tripoint( const point &mount ) const tripoint vehicle::mount_to_tripoint( const point &mount, const point &offset ) const { - point mnt_translated; + tripoint mnt_translated; coord_translate( pivot_rotation[0], pivot_anchor[ 0 ], mount + offset, mnt_translated ); return global_pos3() + mnt_translated; } @@ -3076,7 +3078,7 @@ void vehicle::precalc_mounts( int idir, int dir, const point &pivot ) idir = 0; } tileray tdir( dir ); - std::unordered_map mount_to_precalc; + std::unordered_map mount_to_precalc; for( auto &p : parts ) { if( p.removed ) { continue; @@ -3211,9 +3213,9 @@ point vehicle::pivot_displacement() const // the vehicle. // rotate the old pivot point around the new pivot point with the old rotation angle - point dp; + tripoint dp; coord_translate( pivot_rotation[0], pivot_anchor[1], pivot_anchor[0], dp ); - return dp; + return dp.xy(); } int vehicle::fuel_left( const itype_id &ftype, bool recurse ) const @@ -6830,7 +6832,7 @@ bounding_box vehicle::get_bounding_box() int i_use = 0; for( const tripoint &p : get_points( true ) ) { - const point pt = parts[part_at( p.xy() )].precalc[i_use]; + const point pt = parts[part_at( p.xy() )].precalc[i_use].xy(); if( pt.x < min_x ) { min_x = pt.x; } diff --git a/src/vehicle.h b/src/vehicle.h index 52f4dbdd9a512..614ac248be52f 100644 --- a/src/vehicle.h +++ b/src/vehicle.h @@ -365,7 +365,7 @@ struct vehicle_part { /** mount translated to face.dir [0] and turn_dir [1] */ // NOLINTNEXTLINE(cata-use-named-point-constants) - std::array precalc = { { point( -1, -1 ), point( -1, -1 ) } }; + std::array precalc = { { tripoint( -1, -1, 0 ), tripoint( -1, -1, 0 ) } }; /** current part health with range [0,durability] */ int hp() const; @@ -1042,10 +1042,10 @@ class vehicle point coord_translate( const point &p ) const; // Translate mount coordinates "p" into tile coordinates "q" using given pivot direction and anchor - void coord_translate( int dir, const point &pivot, const point &p, point &q ) const; + void coord_translate( int dir, const point &pivot, const point &p, tripoint &q ) const; // Translate mount coordinates "p" into tile coordinates "q" using given tileray and anchor // should be faster than previous call for repeated translations - void coord_translate( tileray tdir, const point &pivot, const point &p, point &q ) const; + void coord_translate( tileray tdir, const point &pivot, const point &p, tripoint &q ) const; // Rotates mount coordinates "p" from old_dir to new_dir along pivot point rotate_mount( int old_dir, int new_dir, const point &pivot, const point &p ) const; diff --git a/src/vehicle_move.cpp b/src/vehicle_move.cpp index 282963a234ddd..708346c537da0 100644 --- a/src/vehicle_move.cpp +++ b/src/vehicle_move.cpp @@ -1277,7 +1277,7 @@ void vehicle::precalculate_vehicle_turning( int new_turn_dir, bool check_rail_di for( int part_index : wheelcache ) { const auto &wheel = parts[ part_index ]; bool rails_ahead = true; - point wheel_point; + tripoint wheel_point; coord_translate( mdir.dir(), this->pivot_point(), wheel.mount, wheel_point ); From d44d1e8ce96d725f3f3fe998e26279c1a77b79d9 Mon Sep 17 00:00:00 2001 From: Mark Langsdorf Date: Tue, 16 Jun 2020 10:31:06 -0500 Subject: [PATCH 051/420] mapdata: add Z_TRANSPARENT flag Add a new flag that indicates a tile in transparent through the z-level while still blocking moving through the z-level. --- src/cata_tiles.cpp | 6 +++--- src/map.cpp | 38 ++++++++++++++++++++++---------------- src/map.h | 4 ++-- src/mapdata.cpp | 1 + src/mapdata.h | 1 + 5 files changed, 29 insertions(+), 21 deletions(-) diff --git a/src/cata_tiles.cpp b/src/cata_tiles.cpp index d058232b725ff..bde784aee3783 100644 --- a/src/cata_tiles.cpp +++ b/src/cata_tiles.cpp @@ -2112,7 +2112,7 @@ bool cata_tiles::draw_terrain_below( const tripoint &p, const lit_level, int &, const auto low_override = draw_below_override.find( p ); const bool low_overridden = low_override != draw_below_override.end(); if( low_overridden ? !low_override->second : ( invisible[0] || - !here.need_draw_lower_floor( p ) ) ) { + here.dont_draw_lower_floor( p ) ) ) { return false; } @@ -2584,7 +2584,7 @@ bool cata_tiles::draw_vpart_below( const tripoint &p, const lit_level /*ll*/, in const auto low_override = draw_below_override.find( p ); const bool low_overridden = low_override != draw_below_override.end(); if( low_overridden ? !low_override->second : ( invisible[0] || - !get_map().need_draw_lower_floor( p ) ) ) { + get_map().dont_draw_lower_floor( p ) ) ) { return false; } tripoint pbelow( p.xy(), p.z - 1 ); @@ -2661,7 +2661,7 @@ bool cata_tiles::draw_critter_at_below( const tripoint &p, const lit_level, int const auto low_override = draw_below_override.find( p ); const bool low_overridden = low_override != draw_below_override.end(); if( low_overridden ? !low_override->second : ( invisible[0] || - !get_map().need_draw_lower_floor( p ) ) ) { + get_map().dont_draw_lower_floor( p ) ) ) { return false; } diff --git a/src/map.cpp b/src/map.cpp index 1ece0df9221c3..7b2d105ad2a2a 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -5568,7 +5568,7 @@ bool map::draw_maptile_from_memory( const catacurses::window &w, const tripoint { int sym = g->u.get_memorized_symbol( getabs( p ) ); if( sym == 0 ) { - return false; + return true; } if( move_cursor ) { const int k = p.x + getmaxx( w ) / 2 - view_center.x; @@ -5578,7 +5578,7 @@ bool map::draw_maptile_from_memory( const catacurses::window &w, const tripoint } else { wputch( w, c_brown, sym ); } - return true; + return false; } void map::draw( const catacurses::window &w, const tripoint ¢er ) @@ -5613,7 +5613,7 @@ void map::draw( const catacurses::window &w, const tripoint ¢er ) x = center.x - getmaxx( w ) / 2; if( y < 0 || y >= MAPSIZE_Y ) { for( ; x < maxxrender; x++ ) { - if( !do_map_memory || !draw_maptile_from_memory( w, p, center, false ) ) { + if( !do_map_memory || draw_maptile_from_memory( w, p, center, false ) ) { wputch( w, c_black, ' ' ); } } @@ -5621,7 +5621,7 @@ void map::draw( const catacurses::window &w, const tripoint ¢er ) } while( x < 0 ) { - if( !do_map_memory || !draw_maptile_from_memory( w, p, center, false ) ) { + if( !do_map_memory || draw_maptile_from_memory( w, p, center, false ) ) { wputch( w, c_black, ' ' ); } x++; @@ -5638,15 +5638,17 @@ void map::draw( const catacurses::window &w, const tripoint ¢er ) const visibility_type vis = get_visibility( lighting, cache ); if( !apply_vision_effects( w, vis ) ) { const maptile curr_maptile = maptile( cur_submap, l ); - const bool just_this_zlevel = + const bool draw_lower_zlevel = draw_maptile( w, g->u, p, curr_maptile, false, true, center, - lighting == lit_level::LOW, lighting == lit_level::BRIGHT, true ); - if( !just_this_zlevel ) { + lighting == lit_level::LOW, + lighting == lit_level::BRIGHT, true ); + if( draw_lower_zlevel ) { p.z--; const maptile tile_below = maptile( sm_below, l ); draw_from_above( w, g->u, p, tile_below, false, center, - lighting == lit_level::LOW, lighting == lit_level::BRIGHT, false ); + lighting == lit_level::LOW, + lighting == lit_level::BRIGHT, false ); p.z++; } } else if( do_map_memory && ( vis == visibility_type::HIDDEN || vis == visibility_type::DARK ) ) { @@ -5659,7 +5661,7 @@ void map::draw( const catacurses::window &w, const tripoint ¢er ) } while( x < maxxrender ) { - if( !do_map_memory || !draw_maptile_from_memory( w, p, center, false ) ) { + if( !do_map_memory || draw_maptile_from_memory( w, p, center, false ) ) { wputch( w, c_black, ' ' ); } x++; @@ -5690,9 +5692,9 @@ void map::drawsq( const catacurses::window &w, player &u, const tripoint &p, con } const maptile tile = maptile_at( p ); - const bool done = draw_maptile( w, u, p, tile, invert_arg, show_items_arg, + const bool more = draw_maptile( w, u, p, tile, invert_arg, show_items_arg, view_center, low_light, bright_light, inorder ); - if( !done ) { + if( more ) { tripoint below( p.xy(), p.z - 1 ); const maptile tile_below = maptile_at( below ); draw_from_above( w, u, below, tile_below, @@ -5702,11 +5704,13 @@ void map::drawsq( const catacurses::window &w, player &u, const tripoint &p, con } // a check to see if the lower floor needs to be rendered in tiles -bool map::need_draw_lower_floor( const tripoint &p ) +bool map::dont_draw_lower_floor( const tripoint &p ) { - return !( !zlevels || p.z <= -OVERMAP_DEPTH || !ter( p ).obj().has_flag( TFLAG_NO_FLOOR ) ); + return !zlevels || p.z <= -OVERMAP_DEPTH || + !( has_flag( TFLAG_NO_FLOOR, p ) || has_flag( TFLAG_Z_TRANSPARENT, p ) ); } +// returns true if lower z-level needs to be drawn, false otherwise bool map::draw_maptile( const catacurses::window &w, const player &u, const tripoint &p, const maptile &curr_maptile, bool invert, bool show_items, @@ -5897,8 +5901,9 @@ bool map::draw_maptile( const catacurses::window &w, const player &u, const trip } } - return !zlevels || sym != ' ' || !item_sym.empty() || p.z <= -OVERMAP_DEPTH || - !curr_ter.has_flag( TFLAG_NO_FLOOR ); + return zlevels && item_sym.empty() && p.z > -OVERMAP_DEPTH && + ( curr_ter.has_flag( TFLAG_Z_TRANSPARENT ) || + ( sym == ' ' && curr_ter.has_flag( TFLAG_NO_FLOOR ) ) ); } void map::draw_from_above( const catacurses::window &w, const player &u, const tripoint &p, @@ -8353,7 +8358,8 @@ void map::update_pathfinding_cache( int zlev ) const } if( terrain.has_flag( TFLAG_GOES_DOWN ) || terrain.has_flag( TFLAG_GOES_UP ) || - terrain.has_flag( TFLAG_RAMP ) ) { + terrain.has_flag( TFLAG_RAMP ) || terrain.has_flag( TFLAG_RAMP_UP ) || + terrain.has_flag( TFLAG_RAMP_DOWN ) ) { cur_value |= PF_UPDOWN; } diff --git a/src/map.h b/src/map.h index cd0a9502e8ede..d16a053d9d9bb 100644 --- a/src/map.h +++ b/src/map.h @@ -1650,7 +1650,7 @@ class map /** * Internal version of the drawsq. Keeps a cached maptile for less re-getting. - * Returns true if it has drawn all it should, false if `draw_from_above` should be called after. + * Returns false if it has drawn all it should, true if `draw_from_above` should be called after. */ bool draw_maptile( const catacurses::window &w, const player &u, const tripoint &p, const maptile &tile, @@ -1810,7 +1810,7 @@ class map level_cache &access_cache( int zlev ); const level_cache &access_cache( int zlev ) const; - bool need_draw_lower_floor( const tripoint &p ); + bool dont_draw_lower_floor( const tripoint &p ); }; map &get_map(); diff --git a/src/mapdata.cpp b/src/mapdata.cpp index 7fedd71ff51c4..1da6c53ce0041 100644 --- a/src/mapdata.cpp +++ b/src/mapdata.cpp @@ -177,6 +177,7 @@ static const std::unordered_map ter_bitflags_map = { { "RAMP", TFLAG_RAMP }, // Can be used to move up a z-level { "RAIL", TFLAG_RAIL }, // Rail tile (used heavily) { "THIN_OBSTACLE", TFLAG_THIN_OBSTACLE }, // Passable by players and monsters. Vehicles destroy it. + { "Z_TRANSPARENT", TFLAG_Z_TRANSPARENT }, // Doesn't block vision passing through the z-level { "SMALL_PASSAGE", TFLAG_SMALL_PASSAGE } // A small passage, that large or huge things cannot pass through } }; diff --git a/src/mapdata.h b/src/mapdata.h index 15bfdee87a0d6..8bb0679405c13 100644 --- a/src/mapdata.h +++ b/src/mapdata.h @@ -202,6 +202,7 @@ enum ter_bitflags : int { TFLAG_RAIL, TFLAG_THIN_OBSTACLE, TFLAG_SMALL_PASSAGE, + TFLAG_Z_TRANSPARENT, NUM_TERFLAGS }; From 5b7c8ca4ac09db9fab1d7e8bb5fd98aa361fafa7 Mon Sep 17 00:00:00 2001 From: Duck13Tales <67176313+Duck13Tales@users.noreply.github.com> Date: Thu, 2 Jul 2020 20:59:47 -0300 Subject: [PATCH 052/420] Changes to most tea recipes to allow inherit the nutrients from its components --- data/json/items/comestibles/drink.json | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/data/json/items/comestibles/drink.json b/data/json/items/comestibles/drink.json index 39ca8c3237680..4673b37a808fe 100644 --- a/data/json/items/comestibles/drink.json +++ b/data/json/items/comestibles/drink.json @@ -146,7 +146,7 @@ "material": [ "milk", "water" ], "volume": "250 ml", "phase": "liquid", - "flags": [ "EATEN_HOT", "NUTRIENT_OVERRIDE" ], + "flags": [ "EATEN_HOT" ], "fun": 14 }, { @@ -391,7 +391,7 @@ "volume": "250 ml", "material": [ "water" ], "phase": "liquid", - "flags": [ "EATEN_HOT", "NUTRIENT_OVERRIDE" ], + "flags": [ "EATEN_HOT" ], "fun": 2 }, { @@ -411,7 +411,7 @@ "volume": "250 ml", "material": [ "water" ], "phase": "liquid", - "flags": [ "EATEN_HOT", "NUTRIENT_OVERRIDE" ], + "flags": [ "EATEN_HOT" ], "fun": 3 }, { @@ -499,9 +499,8 @@ "volume": "250 ml", "material": [ "water" ], "phase": "liquid", - "flags": [ "EATEN_HOT", "NUTRIENT_OVERRIDE" ], - "fun": 4, - "vitamins": [ [ "iron", 1 ] ] + "flags": [ "EATEN_HOT" ], + "fun": 4 }, { "id": "hot_chocolate", @@ -975,7 +974,7 @@ "volume": "250 ml", "material": [ "water" ], "phase": "liquid", - "flags": [ "EATEN_HOT", "NUTRIENT_OVERRIDE" ], + "flags": [ "EATEN_HOT" ], "fun": 6 }, { @@ -995,7 +994,7 @@ "volume": "500 ml", "material": [ "water" ], "phase": "liquid", - "flags": [ "EATEN_HOT", "NUTRIENT_OVERRIDE" ], + "flags": [ "EATEN_HOT" ], "fun": -5 }, { From 2f41986e39db80e5f6a3814eacf45540bfdd4908 Mon Sep 17 00:00:00 2001 From: John Bytheway Date: Thu, 2 Jul 2020 20:17:22 -0400 Subject: [PATCH 053/420] item::put_in now returns ret_val Instead of void, so that failure can be more usefully reported. --- src/item.cpp | 5 +++-- src/item.h | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/item.cpp b/src/item.cpp index 5ef85882ae8b5..188a27fb3d39b 100644 --- a/src/item.cpp +++ b/src/item.cpp @@ -1028,10 +1028,11 @@ bool item::merge_charges( const item &rhs ) return true; } -void item::put_in( const item &payload, item_pocket::pocket_type pk_type ) +ret_val item::put_in( const item &payload, item_pocket::pocket_type pk_type ) { - contents.insert_item( payload, pk_type ); + ret_val result = contents.insert_item( payload, pk_type ); on_contents_changed(); + return result; } void item::set_var( const std::string &name, const int value ) diff --git a/src/item.h b/src/item.h index 4d25475c675c9..50aa19fb5867f 100644 --- a/src/item.h +++ b/src/item.h @@ -740,7 +740,7 @@ class item : public visitable /** * Puts the given item into this one, no checks are performed. */ - void put_in( const item &payload, item_pocket::pocket_type pk_type ); + ret_val put_in( const item &payload, item_pocket::pocket_type pk_type ); /** * Returns this item into its default container. If it does not have a default container, From 8dd48417e61e77ba8942e1fa516a4b0f35819a2c Mon Sep 17 00:00:00 2001 From: John Bytheway Date: Thu, 2 Jul 2020 20:18:14 -0400 Subject: [PATCH 054/420] item_contents_test: Explain insertion failure When this test fails to insert an item, report why (via the ret_val error message). --- tests/item_contents_test.cpp | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/tests/item_contents_test.cpp b/tests/item_contents_test.cpp index 6c74b339cfb14..ce13bfd485f82 100644 --- a/tests/item_contents_test.cpp +++ b/tests/item_contents_test.cpp @@ -17,10 +17,27 @@ TEST_CASE( "item_contents" ) item wrench( "wrench" ); item crowbar( "crowbar" ); - tool_belt.put_in( hammer, item_pocket::pocket_type::CONTAINER ); - tool_belt.put_in( tongs, item_pocket::pocket_type::CONTAINER ); - tool_belt.put_in( wrench, item_pocket::pocket_type::CONTAINER ); - tool_belt.put_in( crowbar, item_pocket::pocket_type::CONTAINER ); + ret_val i1 = tool_belt.put_in( hammer, item_pocket::pocket_type::CONTAINER ); + ret_val i2 = tool_belt.put_in( tongs, item_pocket::pocket_type::CONTAINER ); + ret_val i3 = tool_belt.put_in( wrench, item_pocket::pocket_type::CONTAINER ); + ret_val i4 = tool_belt.put_in( crowbar, item_pocket::pocket_type::CONTAINER ); + + { + CAPTURE( i1.str() ); + CHECK( i1.success() ); + } + { + CAPTURE( i2.str() ); + CHECK( i2.success() ); + } + { + CAPTURE( i3.str() ); + CHECK( i3.success() ); + } + { + CAPTURE( i4.str() ); + CHECK( i4.success() ); + } // check the items actually got added to the tool belt REQUIRE( tool_belt.contents.num_item_stacks() == 4 ); From 4ebcb415c83e1f2e50bfda98587c6aa6d3b87e8d Mon Sep 17 00:00:00 2001 From: John Bytheway Date: Thu, 2 Jul 2020 20:19:36 -0400 Subject: [PATCH 055/420] Refactor item_contents::find_pocket_for Previously there were two implementations of this function, for different constnesses, which has already become out of sync. Consolidate them into a single static helper function. Also, enhance the error reporting so that when a suitable pocket cannot be found a useful message results. --- src/item_contents.cpp | 89 +++++++++++++++++++++++++++---------------- src/item_contents.h | 3 ++ 2 files changed, 60 insertions(+), 32 deletions(-) diff --git a/src/item_contents.cpp b/src/item_contents.cpp index 37bf67cabea6b..cf344c33f410b 100644 --- a/src/item_contents.cpp +++ b/src/item_contents.cpp @@ -2,6 +2,7 @@ #include #include +#include #include "avatar.h" #include "character.h" @@ -303,46 +304,70 @@ void item_contents::combine( const item_contents &read_input ) } } -ret_val item_contents::find_pocket_for( const item &it, - item_pocket::pocket_type pk_type ) -{ - static item_pocket *null_pocket = nullptr; - ret_val ret = ret_val::make_failure( null_pocket, - _( "is not a container" ) ); - for( item_pocket &pocket : contents ) { - if( !pocket.is_type( pk_type ) ) { - continue; - } - if( pk_type != item_pocket::pocket_type::CONTAINER && - pk_type != item_pocket::pocket_type::MAGAZINE && - pk_type != item_pocket::pocket_type::MAGAZINE_WELL && - pocket.is_type( pk_type ) ) { - return ret_val::make_success( &pocket, "special pocket type override" ); +struct item_contents::item_contents_helper { + // Static helper function to implement the const and non-const versions of + // find_pocket_for with less code duplication + template + using pocket_type = std::conditional_t < + std::is_const::value, + const item_pocket, + item_pocket + >; + + template + static ret_val*> find_pocket_for( + ItemContents &contents, const item &it, item_pocket::pocket_type pk_type ) { + using my_pocket_type = pocket_type; + static constexpr item_pocket *null_pocket = nullptr; + + std::vector failure_messages; + int num_pockets_of_type = 0; + + for( my_pocket_type &pocket : contents.contents ) { + if( !pocket.is_type( pk_type ) ) { + continue; + } + if( pk_type != item_pocket::pocket_type::CONTAINER && + pk_type != item_pocket::pocket_type::MAGAZINE && + pk_type != item_pocket::pocket_type::MAGAZINE_WELL && + pocket.is_type( pk_type ) ) { + return ret_val::make_success( + &pocket, "special pocket type override" ); + } + ++num_pockets_of_type; + ret_val ret_contain = pocket.can_contain( it ); + if( ret_contain.success() ) { + return ret_val::make_success( &pocket, ret_contain.str() ); + } else { + failure_messages.push_back( ret_contain.str() ); + } } - ret_val ret_contain = pocket.can_contain( it ); - if( ret_contain.success() ) { - return ret_val::make_success( &pocket, ret_contain.str() ); + + if( failure_messages.empty() ) { + return ret_val::make_failure( null_pocket, _( "is not a container" ) ); } + std::sort( failure_messages.begin(), failure_messages.end(), localized_compare ); + failure_messages.erase( + std::unique( failure_messages.begin(), failure_messages.end() ), + failure_messages.end() ); + return ret_val::make_failure( + null_pocket, string_format( + ngettext( "pocket unacceptable because %s", "pockets unacceptable because %s", + num_pockets_of_type ), + enumerate_as_string( failure_messages, enumeration_conjunction::or_ ) ) ); } - return ret; +}; + +ret_val item_contents::find_pocket_for( const item &it, + item_pocket::pocket_type pk_type ) +{ + return item_contents_helper::find_pocket_for( *this, it, pk_type ); } ret_val item_contents::find_pocket_for( const item &it, item_pocket::pocket_type pk_type ) const { - static item_pocket *null_pocket = nullptr; - ret_val ret = ret_val::make_failure( null_pocket, - _( "is not a container" ) ); - for( const item_pocket &pocket : contents ) { - if( !pocket.is_type( pk_type ) ) { - continue; - } - ret_val ret_contain = pocket.can_contain( it ); - if( ret_contain.success() ) { - return ret_val::make_success( &pocket, ret_contain.str() ); - } - } - return ret; + return item_contents_helper::find_pocket_for( *this, it, pk_type ); } int item_contents::obtain_cost( const item &it ) const diff --git a/src/item_contents.h b/src/item_contents.h index cc5f3024fe67b..b90387f09cc7d 100644 --- a/src/item_contents.h +++ b/src/item_contents.h @@ -243,6 +243,9 @@ class item_contents item_pocket::pocket_type pk_type = item_pocket::pocket_type::CONTAINER ) const; std::list contents; + + struct item_contents_helper; + friend struct item_contents_helper; }; #endif // CATA_SRC_ITEM_CONTENTS_H From 914f3404569a9b4823d742edb0de52f77e895a53 Mon Sep 17 00:00:00 2001 From: John Bytheway Date: Thu, 2 Jul 2020 20:21:28 -0400 Subject: [PATCH 056/420] Better error message in item_contents::insert_item When find_pocket_for fails, include its error message in the error output from insert_item. --- src/item_contents.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/item_contents.cpp b/src/item_contents.cpp index cf344c33f410b..4f52782bdc3e3 100644 --- a/src/item_contents.cpp +++ b/src/item_contents.cpp @@ -400,7 +400,7 @@ ret_val item_contents::insert_item( const item &it, item_pocket::pocket_ty ret_val pocket = find_pocket_for( it, pk_type ); if( pocket.value() == nullptr ) { - return ret_val::make_failure( "No success" ); + return ret_val::make_failure( "No pocket found: " + pocket.str() ); } ret_val pocket_contain_code = pocket.value()->insert_item( it ); From 9ee9777db91f0d0d0a929855c695bbf685e11c5f Mon Sep 17 00:00:00 2001 From: Mark Langsdorf Date: Mon, 6 Apr 2020 15:42:29 -0500 Subject: [PATCH 057/420] vehicles: ground vehicle ramp transitions Add vehicle ramps and the complicated logic in map::displace_vehicle() to make ground vehicles change z-levels by tile when each tile enters the high end of an up ramp or the low end of a down ramp, transition to the high end of a down ramp or the low end of an up ramp, respectively. --- .../terrain-zlevel-transitions.json | 80 ++++++ src/avatar_action.cpp | 15 +- src/game.cpp | 9 +- src/game.h | 4 +- src/map.cpp | 213 +++++++++------- src/map.h | 5 +- src/mapdata.cpp | 2 + src/mapdata.h | 2 + src/monmove.cpp | 28 +- src/pathfinding.cpp | 21 ++ src/savegame_json.cpp | 11 + src/veh_interact.cpp | 13 +- src/vehicle.cpp | 56 +++- src/vehicle.h | 5 +- tests/vehicle_ramp.cpp | 241 ++++++++++++++++++ 15 files changed, 582 insertions(+), 123 deletions(-) create mode 100644 tests/vehicle_ramp.cpp diff --git a/data/json/furniture_and_terrain/terrain-zlevel-transitions.json b/data/json/furniture_and_terrain/terrain-zlevel-transitions.json index eee796b62309c..e5898ac8f49eb 100644 --- a/data/json/furniture_and_terrain/terrain-zlevel-transitions.json +++ b/data/json/furniture_and_terrain/terrain-zlevel-transitions.json @@ -165,6 +165,86 @@ "move_cost": 2, "flags": [ "TRANSPARENT", "GOES_DOWN", "PLACE_ITEM", "DIFFICULT_Z" ] }, + { + "type": "terrain", + "id": "t_ramp_down_high", + "name": "road ramp down (high end)", + "description": "The upper end of an asphalt ramp leading down.", + "symbol": ">", + "color": "dark_gray", + "move_cost": 2, + "flags": [ "TRANSPARENT", "ROAD", "Z_TRANSPARENT" ] + }, + { + "type": "terrain", + "id": "t_ramp_down_low", + "name": "road ramp down (low end)", + "description": "The lower end of an asphalt ramp leading down.", + "symbol": ">", + "color": "dark_gray", + "move_cost": 2, + "flags": [ "TRANSPARENT", "ROAD", "RAMP_DOWN", "Z_TRANSPARENT" ] + }, + { + "type": "terrain", + "id": "t_ramp_up_high", + "name": "road ramp up (high end)", + "description": "The upper end of an asphalt ramp leading up.", + "symbol": "<", + "color": "dark_gray", + "move_cost": 2, + "flags": [ "ROAD", "RAMP_UP" ] + }, + { + "type": "terrain", + "id": "t_ramp_up_low", + "name": "road ramp up (low end)", + "description": "The lower end of an asphalt ramp leading up.", + "symbol": "<", + "color": "dark_gray", + "move_cost": 2, + "flags": [ "TRANSPARENT", "ROAD" ] + }, + { + "type": "terrain", + "id": "t_sidewalk_ramp_down_high", + "name": "sidewalk ramp down (high end)", + "description": "The upper end of a sidewalk ramp leading down.", + "symbol": ">", + "color": "light_gray", + "move_cost": 2, + "flags": [ "TRANSPARENT", "ROAD", "Z_TRANSPARENT" ] + }, + { + "type": "terrain", + "id": "t_sidewalk_ramp_down_low", + "name": "sidewalk ramp down (low end)", + "description": "The lower end of a sidewalk ramp leading down.", + "symbol": ">", + "color": "light_gray", + "move_cost": 2, + "flags": [ "TRANSPARENT", "ROAD", "RAMP_DOWN", "Z_TRANSPARENT" ] + }, + { + "type": "terrain", + "id": "t_sidewalk_ramp_up_high", + "name": "sidewalk ramp up (high end)", + "description": "The upper end of a sidewalk ramp leading up.", + "symbol": "<", + "color": "light_gray", + "move_cost": 2, + "flags": [ "ROAD", "RAMP_UP" ] + }, + { + "type": "terrain", + "id": "t_sidewalk_ramp_up_low", + "name": "sidewalk ramp up (low end)", + "description": "The lower end of a sidewalk ramp leading up.", + "symbol": "<", + "color": "light_gray", + "move_cost": 2, + "flags": [ "TRANSPARENT", "ROAD" ] + }, { "type": "terrain", "id": "t_slope_down", diff --git a/src/avatar_action.cpp b/src/avatar_action.cpp index bd71cca6d6d68..d59d6e1fb132f 100644 --- a/src/avatar_action.cpp +++ b/src/avatar_action.cpp @@ -118,6 +118,14 @@ bool avatar_action::move( avatar &you, map &m, const tripoint &d ) // Well that sure was easy return true; } + bool via_ramp = false; + if( m.has_flag( TFLAG_RAMP_UP, dest_loc ) ) { + dest_loc.z += 1; + via_ramp = true; + } else if( m.has_flag( TFLAG_RAMP_DOWN, dest_loc ) ) { + dest_loc.z -= 1; + via_ramp = true; + } if( m.has_flag( TFLAG_MINEABLE, dest_loc ) && g->mostseen == 0 && get_option( "AUTO_FEATURES" ) && get_option( "AUTO_MINING" ) && @@ -212,11 +220,6 @@ bool avatar_action::move( avatar &you, map &m, const tripoint &d ) } } - if( d.z == 0 && ramp_move( you, m, dest_loc ) ) { - // TODO: Make it work nice with automove (if it doesn't do so already?) - return false; - } - if( you.has_effect( effect_amigara ) ) { int curdist = INT_MAX; int newdist = INT_MAX; @@ -397,7 +400,7 @@ bool avatar_action::move( avatar &you, map &m, const tripoint &d ) } return true; } - if( g->walk_move( dest_loc ) ) { + if( g->walk_move( dest_loc, via_ramp ) ) { return true; } if( g->phasing_move( dest_loc ) ) { diff --git a/src/game.cpp b/src/game.cpp index 2581eb5a28f50..13fa89d556037 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -9277,7 +9277,7 @@ std::vector game::get_dangerous_tile( const tripoint &dest_loc ) co return harmful_stuff; } -bool game::walk_move( const tripoint &dest_loc ) +bool game::walk_move( const tripoint &dest_loc, const bool via_ramp ) { if( m.has_flag_ter( TFLAG_SMALL_PASSAGE, dest_loc ) ) { if( u.get_size() > creature_size::medium ) { @@ -9420,7 +9420,8 @@ bool game::walk_move( const tripoint &dest_loc ) multiplier *= 3; } - const int mcost = m.combined_movecost( u.pos(), dest_loc, grabbed_vehicle, modifier ) * multiplier; + const int mcost = m.combined_movecost( u.pos(), dest_loc, grabbed_vehicle, modifier, + via_ramp ) * multiplier; if( grabbed_move( dest_loc - u.pos() ) ) { return true; } else if( mcost == 0 ) { @@ -9968,14 +9969,14 @@ void game::place_player_overmap( const tripoint &om_dest ) place_player( player_pos ); } -bool game::phasing_move( const tripoint &dest_loc ) +bool game::phasing_move( const tripoint &dest_loc, const bool via_ramp ) { if( !u.has_active_bionic( bionic_id( "bio_probability_travel" ) ) || u.get_power_level() < 250_kJ ) { return false; } - if( dest_loc.z != u.posz() ) { + if( dest_loc.z != u.posz() && !via_ramp ) { // No vertical phasing yet return false; } diff --git a/src/game.h b/src/game.h index 509b4218ef7dc..d3b60f04c8daa 100644 --- a/src/game.h +++ b/src/game.h @@ -741,9 +741,9 @@ class game bool npc_menu( npc &who ); // Handle phasing through walls, returns true if it handled the move - bool phasing_move( const tripoint &dest ); + bool phasing_move( const tripoint &dest, bool via_ramp = false ); // Regular movement. Returns false if it failed for any reason - bool walk_move( const tripoint &dest ); + bool walk_move( const tripoint &dest, bool via_ramp = false ); void on_move_effects(); private: // Game-start procedures diff --git a/src/map.cpp b/src/map.cpp index 7b2d105ad2a2a..ed64c2bbcfa46 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -243,53 +243,42 @@ void map::add_vehicle_to_cache( vehicle *veh ) return; } - auto &ch = get_cache( veh->sm_pos.z ); - ch.veh_in_active_range = true; // Get parts for( const vpart_reference &vpr : veh->get_all_parts() ) { if( vpr.part().removed ) { continue; } const tripoint p = veh->global_part_pos3( vpr.part() ); - ch.veh_cached_parts.insert( std::make_pair( p, - std::make_pair( veh, static_cast( vpr.part_index() ) ) ) ); + level_cache &ch = get_cache( p.z ); + ch.veh_in_active_range = true; + ch.veh_cached_parts[p] = std::make_pair( veh, static_cast( vpr.part_index() ) ); if( inbounds( p ) ) { ch.veh_exists_at[p.x][p.y] = true; } } } -void map::update_vehicle_cache( vehicle *veh, const int old_zlevel ) +void map::clear_vehicle_point_from_cache( vehicle *veh, const tripoint &pt ) { if( veh == nullptr ) { debugmsg( "Tried to add null vehicle to cache" ); return; } - // Existing must be cleared - auto &ch = get_cache( old_zlevel ); - auto it = ch.veh_cached_parts.begin(); - const auto end = ch.veh_cached_parts.end(); - while( it != end ) { - if( it->second.first == veh ) { - const tripoint p = it->first; - if( inbounds( p ) ) { - ch.veh_exists_at[p.x][p.y] = false; - } - ch.veh_cached_parts.erase( it++ ); - // If something was resting on vehicle, drop it - support_dirty( tripoint( p.xy(), old_zlevel + 1 ) ); - } else { - ++it; - } + level_cache &ch = get_cache( pt.z ); + if( inbounds( pt ) ) { + ch.veh_exists_at[pt.x][pt.y] = false; + } + auto it = ch.veh_cached_parts.find( pt ); + if( it != ch.veh_cached_parts.end() && it->second.first == veh ) { + ch.veh_cached_parts.erase( it ); } - add_vehicle_to_cache( veh ); } void map::clear_vehicle_cache( const int zlev ) { - auto &ch = get_cache( zlev ); + level_cache &ch = get_cache( zlev ); while( !ch.veh_cached_parts.empty() ) { const auto part = ch.veh_cached_parts.begin(); const auto &p = part->first; @@ -298,6 +287,7 @@ void map::clear_vehicle_cache( const int zlev ) } ch.veh_cached_parts.erase( part ); } + ch.veh_in_active_range = false; } void map::clear_vehicle_list( const int zlev ) @@ -310,7 +300,7 @@ void map::clear_vehicle_list( const int zlev ) void map::update_vehicle_list( const submap *const to, const int zlev ) { // Update vehicle data - auto &ch = get_cache( zlev ); + level_cache &ch = get_cache( zlev ); for( const auto &elem : to->vehicles ) { ch.vehicle_list.insert( elem.get() ); if( !elem->loot_zones.empty() ) { @@ -343,7 +333,7 @@ std::unique_ptr map::detach_vehicle( vehicle *veh ) } veh->invalidate_towing( true ); submap *const current_submap = get_submap_at_grid( veh->sm_pos ); - auto &ch = get_cache( z ); + level_cache &ch = get_cache( z ); for( size_t i = 0; i < current_submap->vehicles.size(); i++ ) { if( current_submap->vehicles[i].get() == veh ) { ch.vehicle_list.erase( veh ); @@ -454,6 +444,25 @@ bool map::vehproceed( VehicleList &vehicle_list ) if( cur_veh->v == nullptr ) { vehicle_list = get_vehicles(); } + + // confirm that veh_in_active_range is still correct for each z-level + int minz = zlevels ? -OVERMAP_DEPTH : abs_sub.z; + int maxz = zlevels ? OVERMAP_HEIGHT : abs_sub.z; + for( int zlev = minz; zlev <= maxz; ++zlev ) { + level_cache &cache = get_cache( zlev ); + bool still_active = false; + if( cache.veh_in_active_range ) { + for( int vehx = 0; vehx < MAPSIZE_X; vehx++ ) { + for( int vehy = 0; vehy < MAPSIZE_Y; vehy++ ) { + if( cache.veh_exists_at[vehx][vehy] ) { + still_active = true; + break; + } + } + } + } + cache.veh_in_active_range = still_active; + } return true; } @@ -956,7 +965,7 @@ optional_vpart_position map::veh_at( const tripoint &p ) const const vehicle *map::veh_at_internal( const tripoint &p, int &part_num ) const { // This function is called A LOT. Move as much out of here as possible. - const auto &ch = get_cache_ref( p.z ); + const level_cache &ch = get_cache( p.z ); if( !ch.veh_in_active_range || !ch.veh_exists_at[p.x][p.y] ) { part_num = -1; return nullptr; // Clear cache indicates no vehicle. This should optimize a great deal. @@ -1052,10 +1061,16 @@ void map::unboard_vehicle( const tripoint &p, bool dead_passenger ) bool map::displace_vehicle( vehicle &veh, const tripoint &dp ) { - const tripoint p = veh.global_pos3(); - const tripoint p2 = p + dp; - const tripoint src = p; - const tripoint dst = p2; + const tripoint src = veh.global_pos3(); + // handle vehicle ramps + int ramp_offset = 0; + if( has_flag( TFLAG_RAMP_UP, src + dp ) ) { + ramp_offset += 1; + } else if( has_flag( TFLAG_RAMP_DOWN, src + dp ) ) { + ramp_offset -= 1; + } + + const tripoint dst = src + dp + tripoint( 0, 0, ramp_offset ); if( !inbounds( src ) ) { add_msg( m_debug, "map::displace_vehicle: coordinates out of bounds %d,%d,%d->%d,%d,%d", @@ -1067,6 +1082,7 @@ bool map::displace_vehicle( vehicle &veh, const tripoint &dp ) point dst_offset; submap *src_submap = get_submap_at( src, src_offset ); submap *const dst_submap = get_submap_at( dst, dst_offset ); + std::set smzs; // first, let's find our position in current vehicles vector size_t our_i = 0; @@ -1092,7 +1108,7 @@ bool map::displace_vehicle( vehicle &veh, const tripoint &dp ) // move the vehicle // don't let it go off grid - if( !inbounds( p2 ) ) { + if( !inbounds( dst ) ) { veh.stop(); // Silent debug dbg( D_ERROR ) << "map:displace_vehicle: Stopping vehicle, displaced dp=(" @@ -1134,11 +1150,18 @@ bool map::displace_vehicle( vehicle &veh, const tripoint &dp ) "passenger at %d,%d,%d", prt, part_pos.x, part_pos.y, part_pos.z, psg->posx(), psg->posy(), psg->posz() ); } + const vehicle_part &veh_part = veh.part( prt ); + + // ramps make everything super tricky + int psg_offset_z = -ramp_offset; + if( has_flag( TFLAG_RAMP_UP, src + dp + veh_part.precalc[1] ) ) { + psg_offset_z += 1; + } else if( has_flag( TFLAG_RAMP_DOWN, src + dp + veh_part.precalc[1] ) ) { + psg_offset_z -= 1; + } // Place passenger on the new part location - const vehicle_part &veh_part = veh.part( prt ); - tripoint psgp( dp + part_pos.xy() - veh_part.precalc[0] + veh_part.precalc[1] + tripoint( 0, 0, - psg->posz() ) ); + tripoint psgp( dst + veh_part.precalc[1] + tripoint( 0, 0, psg_offset_z ) ); // someone is in the way so try again if( g->critter_at( psgp ) ) { complete = false; @@ -1147,39 +1170,45 @@ bool map::displace_vehicle( vehicle &veh, const tripoint &dp ) if( psg == &g->u ) { // If passenger is you, we need to update the map need_update = true; - z_change = dp.z; + z_change = psgp.z - part_pos.z; } + psg->setpos( psgp ); r.moved = true; } } veh.shed_loose_parts(); - veh.advance_precalc_mounts( dst_offset, p2.z ); - + smzs = veh.advance_precalc_mounts( dst_offset, src, dp, ramp_offset ); if( src_submap != dst_submap ) { - veh.set_submap_moved( point( p2.x / SEEX, p2.y / SEEY ) ); + veh.set_submap_moved( tripoint( dst.x / SEEX, dst.y / SEEY, dst.z ) ); auto src_submap_veh_it = src_submap->vehicles.begin() + our_i; dst_submap->vehicles.push_back( std::move( *src_submap_veh_it ) ); src_submap->vehicles.erase( src_submap_veh_it ); dst_submap->is_uniform = false; } - update_vehicle_cache( &veh, src.z ); if( need_update ) { g->update_map( g->u ); } + add_vehicle_to_cache( &veh ); - if( z_change != 0 ) { - g->vertical_move( z_change, true ); - // I don't know why all this is needed, but the cache does not update properly without. + if( z_change || src.z != dst.z ) { + if( z_change ) { + g->vertical_move( z_change, true ); + // vertical moves can flush the caches, so make sure we're still in the cache + add_vehicle_to_cache( &veh ); + } update_vehicle_list( dst_submap, dst.z ); - update_vehicle_cache( &veh, src.z ); - level_cache &ch2 = get_cache( src.z ); - for( const vehicle *elem : ch2.vehicle_list ) { - if( elem == &veh ) { - ch2.vehicle_list.erase( &veh ); - ch2.zone_vehicles.erase( &veh ); - break; + // delete the vehicle from the source z-level vehicle cache set if it is no longer on + // that z-level + if( src.z != dst.z ) { + level_cache &ch2 = get_cache( src.z ); + for( const vehicle *elem : ch2.vehicle_list ) { + if( elem == &veh ) { + ch2.vehicle_list.erase( &veh ); + ch2.zone_vehicles.erase( &veh ); + break; + } } } veh.check_is_heli_landed(); @@ -1191,11 +1220,13 @@ bool map::displace_vehicle( vehicle &veh, const tripoint &dp ) } veh.check_falling_or_floating(); - + // //global positions of vehicle loot zones have changed. veh.zones_dirty = true; - on_vehicle_moved( veh.sm_pos.z ); + for( int vsmz : smzs ) { + on_vehicle_moved( vsmz ); + } return true; } @@ -1715,7 +1746,7 @@ bool map::passable_ter_furn( const tripoint &p ) const int map::combined_movecost( const tripoint &from, const tripoint &to, const vehicle *ignored_vehicle, - const int modifier, const bool flying ) const + const int modifier, const bool flying, const bool via_ramp ) const { const int mults[4] = { 0, 50, 71, 100 }; const int cost1 = move_cost( from, ignored_vehicle ); @@ -1728,7 +1759,7 @@ int map::combined_movecost( const tripoint &from, const tripoint &to, } // Inter-z-level movement by foot (not flying) - if( !valid_move( from, to, false ) ) { + if( !valid_move( from, to, false, via_ramp ) ) { return 0; } @@ -1737,7 +1768,7 @@ int map::combined_movecost( const tripoint &from, const tripoint &to, } bool map::valid_move( const tripoint &from, const tripoint &to, - const bool bash, const bool flying ) const + const bool bash, const bool flying, const bool via_ramp ) const { // Used to account for the fact that older versions of GCC can trip on the if statement here. assert( to.z > std::numeric_limits::min() ); @@ -1787,19 +1818,20 @@ bool map::valid_move( const tripoint &from, const tripoint &to, return false; } - if( !up_ter.has_flag( TFLAG_NO_FLOOR ) && !up_ter.has_flag( TFLAG_GOES_DOWN ) && !up_is_ledge ) { + if( !up_ter.has_flag( TFLAG_NO_FLOOR ) && !up_ter.has_flag( TFLAG_GOES_DOWN ) && !up_is_ledge && + !via_ramp ) { // Can't move from up to down if( std::abs( from.x - to.x ) == 1 || std::abs( from.y - to.y ) == 1 ) { // Break the move into two - vertical then horizontal tripoint midpoint( down_p.xy(), up_p.z ); - return valid_move( down_p, midpoint, bash, flying ) && - valid_move( midpoint, up_p, bash, flying ); + return valid_move( down_p, midpoint, bash, flying, via_ramp ) && + valid_move( midpoint, up_p, bash, flying, via_ramp ); } return false; } if( !flying && !down_ter.has_flag( TFLAG_GOES_UP ) && !down_ter.has_flag( TFLAG_RAMP ) && - !up_is_ledge ) { + !up_is_ledge && !via_ramp ) { // Can't safely reach the lower tile return false; } @@ -1855,7 +1887,8 @@ int map::climb_difficulty( const tripoint &p ) const if( has_flag( "LADDER", p ) ) { // Really easy, but you have to stand on the tile return 1; - } else if( has_flag( TFLAG_RAMP, p ) ) { + } else if( has_flag( TFLAG_RAMP, p ) || has_flag( TFLAG_RAMP_UP, p ) || + has_flag( TFLAG_RAMP_DOWN, p ) ) { // We're on something stair-like, so halfway there already best_difficulty = 7; } @@ -6493,6 +6526,8 @@ void map::shift( const point &sp ) // absx and absy are our position in the world, for saving/loading purposes. for( int gridz = zmin; gridz <= zmax; gridz++ ) { // Clear vehicle list and rebuild after shift + // mlangsdorf 2020 - this is kind of insane, building the cache is not free, why are + // we doing this? clear_vehicle_cache( gridz ); clear_vehicle_list( gridz ); shift_bitset_cache( get_cache( gridz ).map_memory_seen_cache, sp ); @@ -7705,40 +7740,44 @@ void map::build_floor_caches() } } -void map::do_vehicle_caching( int z ) +static void vehicle_caching_internal( level_cache &zch, const vpart_reference &vp, vehicle *v ) { - auto &ch = get_cache( z ); - auto &outside_cache = ch.outside_cache; - auto &transparency_cache = ch.transparency_cache; - auto &floor_cache = ch.floor_cache; - for( vehicle *v : ch.vehicle_list ) { - for( const vpart_reference &vp : v->get_all_parts() ) { - const size_t part = vp.part_index(); - point p2( v->global_pos3().xy() + vp.part().precalc[0] ); - const point p( p2 ); - if( !inbounds( p ) ) { - continue; - } + auto &outside_cache = zch.outside_cache; + auto &transparency_cache = zch.transparency_cache; + auto &floor_cache = zch.floor_cache; - bool vehicle_is_opaque = - vp.has_feature( VPFLAG_OPAQUE ) && !vp.part().is_broken(); + const size_t part = vp.part_index(); + const tripoint &part_pos = v->global_part_pos3( vp.part() ); - if( vehicle_is_opaque ) { - int dpart = v->part_with_feature( part, VPFLAG_OPENABLE, true ); - if( dpart < 0 || !v->part( dpart ).open ) { - transparency_cache[p2.x][p2.y] = LIGHT_TRANSPARENCY_SOLID; - } else { - vehicle_is_opaque = false; - } - } + bool vehicle_is_opaque = vp.has_feature( VPFLAG_OPAQUE ) && !vp.part().is_broken(); - if( vehicle_is_opaque || vp.is_inside() ) { - outside_cache[p2.x][p2.y] = false; - } + if( vehicle_is_opaque ) { + int dpart = v->part_with_feature( part, VPFLAG_OPENABLE, true ); + if( dpart < 0 || !v->part( dpart ).open ) { + transparency_cache[part_pos.x][part_pos.y] = LIGHT_TRANSPARENCY_SOLID; + } else { + vehicle_is_opaque = false; + } + } - if( vp.has_feature( VPFLAG_BOARDABLE ) && !vp.part().is_broken() ) { - floor_cache[p2.x][p2.y] = true; + if( vehicle_is_opaque || vp.is_inside() ) { + outside_cache[part_pos.x][part_pos.y] = false; + } + + if( vp.has_feature( VPFLAG_BOARDABLE ) && !vp.part().is_broken() ) { + floor_cache[part_pos.x][part_pos.y] = true; + } +} +void map::do_vehicle_caching( int z ) +{ + level_cache &ch = get_cache( z ); + for( vehicle *v : ch.vehicle_list ) { + for( const vpart_reference &vp : v->get_all_parts() ) { + const tripoint &part_pos = v->global_part_pos3( vp.part() ); + if( !inbounds( part_pos.xy() ) ) { + continue; } + vehicle_caching_internal( get_cache( part_pos.z ), vp, v ); } } } diff --git a/src/map.h b/src/map.h index d16a053d9d9bb..681b4b2502380 100644 --- a/src/map.h +++ b/src/map.h @@ -439,7 +439,7 @@ class map */ int combined_movecost( const tripoint &from, const tripoint &to, const vehicle *ignored_vehicle = nullptr, - int modifier = 0, bool flying = false ) const; + int modifier = 0, bool flying = false, bool via_ramp = false ) const; /** * Returns true if a creature could walk from `from` to `to` in one step. @@ -447,7 +447,7 @@ class map * by stairs or (in case of flying monsters) open air with no floors. */ bool valid_move( const tripoint &from, const tripoint &to, - bool bash = false, bool flying = false ) const; + bool bash = false, bool flying = false, bool via_ramp = false ) const; /** * Size of map objects at `p` for purposes of ranged combat. @@ -543,6 +543,7 @@ class map // Vehicles: Common to 2D and 3D VehicleList get_vehicles(); void add_vehicle_to_cache( vehicle * ); + void clear_vehicle_point_from_cache( vehicle *veh, const tripoint &pt ); void update_vehicle_cache( vehicle *, int old_zlevel ); void reset_vehicle_cache( int zlev ); void clear_vehicle_cache( int zlev ); diff --git a/src/mapdata.cpp b/src/mapdata.cpp index 1da6c53ce0041..ed44da5fdb1c8 100644 --- a/src/mapdata.cpp +++ b/src/mapdata.cpp @@ -175,6 +175,8 @@ static const std::unordered_map ter_bitflags_map = { { "BLOCK_WIND", TFLAG_BLOCK_WIND }, // This tile will partially block the wind. { "FLAT", TFLAG_FLAT }, // This tile is flat. { "RAMP", TFLAG_RAMP }, // Can be used to move up a z-level + { "RAMP_DOWN", TFLAG_RAMP_DOWN }, // Anything entering this tile moves down a z-level + { "RAMP_UP", TFLAG_RAMP_UP }, // Anything entering this tile moves up a z-level { "RAIL", TFLAG_RAIL }, // Rail tile (used heavily) { "THIN_OBSTACLE", TFLAG_THIN_OBSTACLE }, // Passable by players and monsters. Vehicles destroy it. { "Z_TRANSPARENT", TFLAG_Z_TRANSPARENT }, // Doesn't block vision passing through the z-level diff --git a/src/mapdata.h b/src/mapdata.h index 8bb0679405c13..3efd0fa2090e2 100644 --- a/src/mapdata.h +++ b/src/mapdata.h @@ -195,6 +195,8 @@ enum ter_bitflags : int { TFLAG_GOES_UP, TFLAG_NO_FLOOR, TFLAG_SEEN_FROM_ABOVE, + TFLAG_RAMP_DOWN, + TFLAG_RAMP_UP, TFLAG_RAMP, TFLAG_HIDE_PLACE, TFLAG_BLOCK_WIND, diff --git a/src/monmove.cpp b/src/monmove.cpp index c7068f117565a..d7fe26550d2d1 100644 --- a/src/monmove.cpp +++ b/src/monmove.cpp @@ -228,6 +228,9 @@ bool monster::can_reach_to( const tripoint &p ) const { map &here = get_map(); if( p.z > pos().z && z_is_valid( pos().z ) ) { + if( here.has_flag( TFLAG_RAMP_UP, tripoint( p.xy(), p.z - 1 ) ) ) { + return true; + } if( !here.has_flag( TFLAG_GOES_UP, pos() ) && !here.has_flag( TFLAG_NO_FLOOR, p ) ) { // can't go through the roof return false; @@ -869,18 +872,27 @@ void monster::move() // This is a float and using trig_dist() because that Does the Right Thing(tm) // in both circular and roguelike distance modes. const float distance_to_target = trig_dist( pos(), destination ); - for( const tripoint &candidate : squares_closer_to( pos(), destination ) ) { - tripoint candidate_abs = here.getabs( candidate ); + for( tripoint &candidate : squares_closer_to( pos(), destination ) ) { + bool via_ramp = false; + if( here.has_flag( TFLAG_RAMP_UP, candidate ) ) { + via_ramp = true; + candidate.z += 1; + } else if( here.has_flag( TFLAG_RAMP_DOWN, candidate ) ) { + via_ramp = true; + candidate.z -= 1; + } + tripoint candidate_abs = g->m.getabs( candidate ); + if( candidate.z != posz() ) { bool can_z_move = true; - if( !here.valid_move( pos(), candidate, false, true ) ) { + if( !here.valid_move( pos(), candidate, false, true, via_ramp ) ) { // Can't phase through floor can_z_move = false; } // If we're trying to go up but can't fly, check if we can climb. If we can't, then don't // This prevents non-climb/fly enemies running up walls - if( candidate.z > posz() && !flies() ) { + if( candidate.z > posz() && !( via_ramp || flies() ) ) { if( !can_climb() || !here.has_floor_or_support( candidate ) ) { // Can't "jump" up a whole z-level can_z_move = false; @@ -1815,7 +1827,13 @@ void monster::stumble() const bool avoid_water = has_flag( MF_NO_BREATHE ) && !swims() && !has_flag( MF_AQUATIC ); for( const tripoint &dest : here.points_in_radius( pos(), 1 ) ) { if( dest != pos() ) { - valid_stumbles.push_back( dest ); + if( g->m.has_flag( TFLAG_RAMP_DOWN, dest ) ) { + valid_stumbles.push_back( tripoint( dest.xy(), dest.z - 1 ) ); + } else if( g->m.has_flag( TFLAG_RAMP_UP, dest ) ) { + valid_stumbles.push_back( tripoint( dest.xy(), dest.z + 1 ) ); + } else { + valid_stumbles.push_back( dest ); + } } } diff --git a/src/pathfinding.cpp b/src/pathfinding.cpp index 333fe4aa06328..8e0ac223a2144 100644 --- a/src/pathfinding.cpp +++ b/src/pathfinding.cpp @@ -465,6 +465,27 @@ std::vector map::route( const tripoint &f, const tripoint &t, cur, above ); } } + if( cur.z < maxz && parent_terrain.has_flag( TFLAG_RAMP_UP ) && + valid_move( cur, tripoint( cur.xy(), cur.z + 1 ), false, true, true ) ) { + auto &layer = pf.get_layer( cur.z + 1 ); + for( size_t it = 0; it < 8; it++ ) { + const tripoint above( cur.x + x_offset[it], cur.y + y_offset[it], cur.z + 1 ); + pf.add_point( layer.gscore[parent_index] + 4, + layer.score[parent_index] + 4 + 2 * rl_dist( above, t ), + cur, above ); + } + } + if( cur.z > minz && parent_terrain.has_flag( TFLAG_RAMP_DOWN ) && + valid_move( cur, tripoint( cur.xy(), cur.z - 1 ), false, true, true ) ) { + auto &layer = pf.get_layer( cur.z - 1 ); + for( size_t it = 0; it < 8; it++ ) { + const tripoint below( cur.x + x_offset[it], cur.y + y_offset[it], cur.z - 1 ); + pf.add_point( layer.gscore[parent_index] + 4, + layer.score[parent_index] + 4 + 2 * rl_dist( below, t ), + cur, below ); + } + } + } while( !done && !pf.empty() ); if( done ) { diff --git a/src/savegame_json.cpp b/src/savegame_json.cpp index a7204704b4bf7..741e503d3a91d 100644 --- a/src/savegame_json.cpp +++ b/src/savegame_json.cpp @@ -2542,6 +2542,14 @@ void vehicle_part::deserialize( JsonIn &jsin ) data.read( "enabled", enabled ); data.read( "flags", flags ); data.read( "passenger_id", passenger_id ); + if( data.has_int( "z_offset" ) ) { + int z_offset = data.get_int( "z_offset" ); + if( std::abs( z_offset ) > 10 ) { + data.throw_error( "z_offset out of range", "z_offset" ); + } + precalc[0].z = z_offset; + precalc[1].z = z_offset; + } JsonArray ja = data.get_array( "carry" ); // count down from size - 1, then stop after unsigned long 0 - 1 becomes MAX_INT for( size_t index = ja.size() - 1; index < ja.size(); index-- ) { @@ -2615,6 +2623,9 @@ void vehicle_part::serialize( JsonOut &json ) const } json.member( "passenger_id", passenger_id ); json.member( "crew_id", crew_id ); + if( precalc[0].z ) { + json.member( "z_offset", precalc[0].z ); + } json.member( "items", items ); if( target.first != tripoint_min ) { json.member( "target_first_x", target.first.x ); diff --git a/src/veh_interact.cpp b/src/veh_interact.cpp index 2eb424620e8b9..a809c8e81742f 100644 --- a/src/veh_interact.cpp +++ b/src/veh_interact.cpp @@ -3065,7 +3065,7 @@ void veh_interact::complete_vehicle( player &p ) for( const auto &sk : vpinfo.install_skills ) { p.practice( sk.first, veh_utils::calc_xp_gain( vpinfo, sk.first, p ) ); } - here.update_vehicle_cache( veh, veh->sm_pos.z ); + here.add_vehicle_to_cache( veh ); break; } @@ -3132,7 +3132,6 @@ void veh_interact::complete_vehicle( player &p ) add_msg( m_info, _( "You don't meet the requirements to remove the %s." ), vpinfo.name() ); break; } - for( const auto &e : reqs.get_components() ) { p.consume_items( e, 1, is_crafting_component ); } @@ -3198,11 +3197,17 @@ void veh_interact::complete_vehicle( player &p ) if( veh->part_count() < 2 ) { p.add_msg_if_player( _( "You completely dismantle the %s." ), veh->name ); p.activity.set_to_null(); + // destroy vehicle clears the cache here.destroy_vehicle( veh ); } else { + point mount = veh->part( vehicle_part ).mount; + const tripoint &part_pos = veh->global_part_pos3( vehicle_part ); veh->remove_part( vehicle_part ); + // part_removal_cleanup calls refresh, so parts_at_relative is valid veh->part_removal_cleanup(); - here.update_vehicle_cache( veh, veh->sm_pos.z ); + if( veh->parts_at_relative( mount, true ).empty() ) { + g->m.clear_vehicle_point_from_cache( veh, part_pos ); + } } // This will be part of an NPC "job" where they need to clean up the acitivty items afterwards if( p.is_npc() ) { @@ -3210,7 +3215,7 @@ void veh_interact::complete_vehicle( player &p ) it.set_var( "activity_var", p.name ); } } - // Finally, put all the reults somewhere (we wanted to wait until this + // Finally, put all the results somewhere (we wanted to wait until this // point because we don't want to put them back into the vehicle part // that just got removed). put_into_vehicle_or_drop( p, item_drop_reason::deliberate, resulting_items ); diff --git a/src/vehicle.cpp b/src/vehicle.cpp index 7266bad5efe55..a1be5526c49c1 100644 --- a/src/vehicle.cpp +++ b/src/vehicle.cpp @@ -1025,7 +1025,7 @@ void vehicle::smash( map &m, float hp_percent_loss_min, float hp_percent_loss_ma if( roll < pct_af ) { double dist = damage_size == 0.0f ? 1.0f : clamp( 1.0f - trig_dist( damage_origin, part.precalc[0].xy() ) / - damage_size, 0.0, 1.0 ); + damage_size, 0.0f, 1.0f ); //Everywhere else, drop by 10-120% of max HP (anything over 100 = broken) if( mod_hp( part, 0 - ( rng_float( hp_percent_loss_min * dist, hp_percent_loss_max * dist ) * @@ -1992,12 +1992,15 @@ bool vehicle::remove_part( const int p, RemovePartHandler &handler ) void vehicle::part_removal_cleanup() { bool changed = false; + map &here = get_map(); for( std::vector::iterator it = parts.begin(); it != parts.end(); /* noop */ ) { if( it->removed ) { auto items = get_items( std::distance( parts.begin(), it ) ); while( !items.empty() ) { items.erase( items.begin() ); } + const tripoint &pt = global_part_pos3( *it ); + here.clear_vehicle_point_from_cache( this, pt ); it = parts.erase( it ); changed = true; } else { @@ -2008,10 +2011,10 @@ void vehicle::part_removal_cleanup() if( changed || parts.empty() ) { refresh(); if( parts.empty() ) { - g->m.destroy_vehicle( this ); + here.destroy_vehicle( this ); return; } else { - g->m.update_vehicle_cache( this, sm_pos.z ); + here.add_vehicle_to_cache( this ); } } shift_if_needed(); @@ -3154,11 +3157,10 @@ tripoint vehicle::global_part_pos3( const vehicle_part &pt ) const return global_pos3() + pt.precalc[ 0 ]; } -void vehicle::set_submap_moved( const point &p ) +void vehicle::set_submap_moved( const tripoint &p ) { const point old_msp = g->m.getabs( global_pos3().xy() ); - sm_pos.x = p.x; - sm_pos.y = p.y; + sm_pos = p; if( !tracking_on ) { return; } @@ -6882,18 +6884,50 @@ void vehicle::force_erase_part( int part_num ) parts.erase( parts.begin() + part_num ); } -void vehicle::advance_precalc_mounts( const point &new_pos, int submap_z ) -{ - for( vehicle_part &pt : parts ) { - pt.precalc[0] = pt.precalc[1]; +std::set vehicle::advance_precalc_mounts( const point &new_pos, const tripoint &src, + const tripoint &dp, int ramp_offset ) +{ + map &here = get_map(); + std::set smzs; + // when a vehicle part enters the low end of a down ramp, or the high end of an up ramp, + // it immediately translates down or up a z-level, respectively, ending up on the low + // end of an up ramp or high end of a down ramp, respectively. The two ends are set + // past each other, like so: + // (side view) z+1 Rdh RDl + // z+0 RUh Rul + // A vehicle moving left to right on z+1 drives down to z+0 by entering the ramp down low end. + // A vehicle moving right to left on z+0 drives up to z+1 by entering the ramp up high end. + // A vehicle moving left to right on z+0 should ideally collide into a wall before entering + // the ramp up high end, but even if it does, it briefly transitions to z+1 before returning + // to z0 by entering the ramp down low end. + // A vehicle moving right to left on z+1 drives down to z+0 by entering the ramp down low end, + // then immediately returns to z+1 by entering the ramp up high end. + // When a vehicle's pivot point transitions a z-level via a ramp, all other pre-calc points + // make the opposite transition, so that points that were above an ascending pivot point are + // now level with it, and parts that were level with an ascending pivot point are now below + // it. + // parts that enter the translation portion of a ramp on the same displacement as the + // pivot point stay at the same relative z to the pivot point, as the ramp_offset adjustments + // cancel out. + for( vehicle_part &prt : parts ) { + here.clear_vehicle_point_from_cache( this, src + prt.precalc[0] ); + prt.precalc[0] = prt.precalc[1]; + if( here.has_flag( TFLAG_RAMP_UP, src + dp + prt.precalc[0] ) ) { + prt.precalc[0].z += 1; + } else if( here.has_flag( TFLAG_RAMP_DOWN, src + dp + prt.precalc[0] ) ) { + prt.precalc[0].z -= 1; + } + prt.precalc[0].z -= ramp_offset; + prt.precalc[1].z = prt.precalc[0].z; + smzs.insert( prt.precalc[0].z ); } pivot_anchor[0] = pivot_anchor[1]; pivot_rotation[0] = pivot_rotation[1]; pos = new_pos; - sm_pos.z = submap_z; // Invalidate vehicle's point cache occupied_cache_time = calendar::before_time_starts; + return smzs; } bool vehicle::refresh_zones() diff --git a/src/vehicle.h b/src/vehicle.h index 614ac248be52f..872295ddc66b1 100644 --- a/src/vehicle.h +++ b/src/vehicle.h @@ -1692,7 +1692,7 @@ class vehicle * This should be called only when the vehicle has actually been moved, not when * the map is just shifted (in the later case simply set smx/smy directly). */ - void set_submap_moved( const point &p ); + void set_submap_moved( const tripoint &p ); void use_autoclave( int p ); void use_washing_machine( int p ); void use_dishwasher( int p ); @@ -1744,7 +1744,8 @@ class vehicle void force_erase_part( int part_num ); // Updates the internal precalculated mount offsets after the vehicle has been displaced // used in map::displace_vehicle() - void advance_precalc_mounts( const point &new_pos, int submap_z ); + std::set advance_precalc_mounts( const point &new_pos, const tripoint &src, + const tripoint &dp, int ramp_offset ); std::vector omt_path; // route for overmap-scale auto-driving std::vector alternators; // List of alternator indices diff --git a/tests/vehicle_ramp.cpp b/tests/vehicle_ramp.cpp new file mode 100644 index 0000000000000..57cbb2c3f1887 --- /dev/null +++ b/tests/vehicle_ramp.cpp @@ -0,0 +1,241 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "avatar.h" +#include "catch/catch.hpp" +#include "game.h" +#include "itype.h" +#include "map.h" +#include "map_helpers.h" +#include "map_iterator.h" +#include "veh_type.h" +#include "vehicle.h" +#include "vpart_range.h" +#include "bodypart.h" +#include "calendar.h" +#include "enums.h" +#include "game_constants.h" +#include "item.h" +#include "line.h" +#include "mapdata.h" +#include "map_helpers.h" +#include "monster.h" +#include "mtype.h" +#include "units.h" +#include "type_id.h" +#include "point.h" +#include "vpart_position.h" +#include "player_helpers.h" +#include "map_helpers.h" + +const efftype_id effect_blind( "blind" ); + +static void clear_game_and_set_map( const int transit_x, bool use_ramp, bool up ) +{ + // Set to turn 0 to prevent solars from producing power + calendar::turn = 0; + clear_creatures(); + clear_npcs(); + clear_vehicles(); + + // Move player somewhere safe + REQUIRE_FALSE( g->u.in_vehicle ); + g->u.setpos( tripoint_zero ); + // Blind the player to avoid needless drawing-related overhead + //g->u.add_effect( effect_blind, 1_turns, num_bp, true ); + + map &here = get_map(); + wipe_map_terrain(); + build_test_map( ter_id( "t_pavement" ) ); + if( use_ramp ) { + const int upper_zlevel = up ? 1 : 0; + const int lower_zlevel = up - 1; + const int highx = transit_x + ( up ? 0 : 1 ); + const int lowx = transit_x + ( up ? 1 : 0 ); + + // up z1 ...... rdh rDl + // z0 rUh rul ................. + // down z0 rDl rdh ................. + // z-1 ...... rdl rUh + // 60 61 + for( int y = 0; y < SEEY * MAPSIZE; y++ ) { + for( int x = 0; x < transit_x; x++ ) { + const int mid = up ? upper_zlevel : lower_zlevel; + here.ter_set( tripoint( x, y, mid - 2 ), ter_id( "t_rock" ) ); + here.ter_set( tripoint( x, y, mid - 1 ), ter_id( "t_rock" ) ); + here.ter_set( tripoint( x, y, mid ), ter_id( "t_pavement" ) ); + here.ter_set( tripoint( x, y, mid + 1 ), ter_id( "t_open_air" ) ); + here.ter_set( tripoint( x, y, mid + 2 ), ter_id( "t_open_air" ) ); + } + const tripoint ramp_up_low = tripoint( lowx, y, lower_zlevel ); + const tripoint ramp_up_high = tripoint( highx, y, lower_zlevel ); + const tripoint ramp_down_low = tripoint( lowx, y, upper_zlevel ); + const tripoint ramp_down_high = tripoint( highx, y, upper_zlevel ); + here.ter_set( ramp_up_low, ter_id( "t_ramp_up_low" ) ); + here.ter_set( ramp_up_high, ter_id( "t_ramp_up_high" ) ); + here.ter_set( ramp_down_low, ter_id( "t_ramp_down_low" ) ); + here.ter_set( ramp_down_high, ter_id( "t_ramp_down_high" ) ); + for( int x = transit_x + 2; x < SEEX * MAPSIZE; x++ ) { + here.ter_set( tripoint( x, y, 1 ), ter_id( "t_open_air" ) ); + here.ter_set( tripoint( x, y, 0 ), ter_id( "t_pavement" ) ); + here.ter_set( tripoint( x, y, -1 ), ter_id( "t_rock" ) ); + } + } + } + g->m.invalidate_map_cache( 0 ); + g->m.build_map_cache( 0, true ); +} + +// Algorithm goes as follows: +// Clear map +// Spawn a vehicle +// Drive it over the ramp, and confirm that the vehicle changes z-levels +static void ramp_transition_angled( const vproto_id &veh_id, const int angle, + const int transition_x, bool use_ramp, bool up ) +{ + map &here = get_map(); + clear_game_and_set_ramp( transition_x, use_ramp, up ); + + const tripoint map_starting_point( transition_x + 4, 60, 0 ); + REQUIRE( here.ter( map_starting_point ) == ter_id( "t_pavement" ) ); + if( here.ter( map_starting_point ) != ter_id( "t_pavement" ) ) { + return; + } + vehicle *veh_ptr = here.add_vehicle( veh_id, map_starting_point, angle, 1, 0 ); + + REQUIRE( veh_ptr != nullptr ); + if( veh_ptr == nullptr ) { + return 0; + } + + vehicle &veh = *veh_ptr; + veh.check_falling_or_floating(); + + REQUIRE( !veh.is_in_water() ); + + veh.tags.insert( "IN_CONTROL_OVERRIDE" ); + veh.engine_on = true; + g->u.setpos( map_starting_point ); + + REQUIRE( g->u.pos() == map_starting_point ); + if( g->u.pos() != map_starting_point ) { + return; + } + g->m.board_vehicle( map_starting_point, &g->u ); + REQUIRE( g->u.pos() == map_starting_point ); + if( g->u.pos() != map_starting_point ) { + return; + } + const int transition_cycle = 3; + veh.cruise_velocity = 0; + veh.velocity = 0; + here.vehmove(); + + const int target_velocity = 400; + veh.cruise_velocity = target_velocity; + veh.velocity = target_velocity; + CHECK( veh.safe_velocity() > 0 ); + int cycles = 0; + const int target_z = use_ramp ? ( up ? 1 : -1 ) : 0; + + std::set vpts = veh.get_points(); + while( veh.engine_on && veh.safe_velocity() > 0 && cycles < 10 ) { + for( const tripoint &checkpt : vpts ) { + int partnum = 0; + vehicle *check_veh = here.veh_at_internal( checkpt, partnum ); + CHECK( check_veh == veh_ptr ); + } + vpts.clear(); + here.vehmove(); + // If the vehicle starts skidding, the effects become random and test is RUINED + REQUIRE( !veh.skidding ); + for( const tripoint &pos : veh.get_points() ) { + REQUIRE( here.ter( pos ) ); + } + for( const vpart_reference &vp : veh.get_all_parts() ) { + if( vp.info().location != "structure" ) { + continue; + } + const point &pmount = vp.mount(); + const tripoint &ppos = vp.pos(); + if( cycles > ( transition_cycle - pmount.x ) ) { + CHECK( ppos.z == target_z ); + } else { + CHECK( ppos.z == 0 ); + } + if( pmount.x == 0 && pmount.y == 0 ) { + CHECK( g->u.pos() == ppos ); + } + } + vpts = veh.get_points(); + cycles++; + } + const cata::optional vp = here.veh_at( g->u.pos() ).part_with_feature( + VPFLAG_BOARDABLE, true ); + REQUIRE( vp ); + if( vp ) { + const int z_change = map_starting_point.z - g->u.pos().z; + here.unboard_vehicle( *vp, &g->u, false ); + here.ter_set( map_starting_point, ter_id( "t_pavement" ) ); + g->u.setpos( map_starting_point ); + if( z_change ) { + g->vertical_move( z_change, true ); + } + } +} + +static void test_ramp( std::string type, const int transition_x ) +{ + SECTION( type + " no ramp" ) { + ramp_transition_angled( vproto_id( type ), 180, transition_x, false, false ); + } + SECTION( type + " ramp up" ) { + ramp_transition_angled( vproto_id( type ), 180, transition_x, true, true ); + } + SECTION( type + " ramp down" ) { + ramp_transition_angled( vproto_id( type ), 180, transition_x, true, false ); + } + SECTION( type + " angled no ramp" ) { + ramp_transition_angled( vproto_id( type ), 225, transition_x, false, false ); + } + SECTION( type + " angled ramp down" ) { + ramp_transition_angled( vproto_id( type ), 225, transition_x, true, false ); + } + SECTION( type + " angled ramp up" ) { + ramp_transition_angled( vproto_id( type ), 225, transition_x, true, true ); + } +} + +static std::vector ramp_vehs_to_test = {{ + "motorcycle", + } +}; + +// I'd like to do this in a single loop, but that doesn't work for some reason +TEST_CASE( "vehicle_ramp_test_59", "[vehicle][ramp]" ) +{ + for( const std::string &veh : ramp_vehs_to_test ) { + test_ramp( veh, 59 ); + } +} +TEST_CASE( "vehicle_ramp_test_60", "[vehicle][ramp]" ) +{ + for( const std::string &veh : ramp_vehs_to_test ) { + test_ramp( veh, 60 ); + } +} +TEST_CASE( "vehicle_ramp_test_61", "[vehicle][ramp]" ) +{ + for( const std::string &veh : ramp_vehs_to_test ) { + test_ramp( veh, 61 ); + } +} From a84902e1bef3f247dbbacaf933ecb9e97b4c4fe0 Mon Sep 17 00:00:00 2001 From: Mark Langsdorf Date: Wed, 17 Jun 2020 16:53:04 -0500 Subject: [PATCH 058/420] vehicles: even out partially unsupported vehicles detect vehicles that are both split across z-levels and have z-levels that aren't supported. Then re-level the vehicle, updating the cache as necessary. This code repurposes map::displace_vehicle() and does a lot of damage to the clean, simple logic but it works. --- src/grab.cpp | 5 +- src/map.cpp | 89 +++++++++++------ src/map.h | 7 +- src/vehicle.cpp | 32 ++++-- src/vehicle.h | 5 +- src/vehicle_move.cpp | 53 +++++++++- ...vehicle_ramp.cpp => vehicle_ramp_test.cpp} | 99 +++++++++++++++++-- 7 files changed, 242 insertions(+), 48 deletions(-) rename tests/{vehicle_ramp.cpp => vehicle_ramp_test.cpp} (74%) diff --git a/src/grab.cpp b/src/grab.cpp index ffa5a942f3d76..e901b2cd49c06 100644 --- a/src/grab.cpp +++ b/src/grab.cpp @@ -185,7 +185,10 @@ bool game::grabbed_veh_move( const tripoint &dp ) m.displace_vehicle( *grabbed_vehicle, final_dp_veh ); - if( !grabbed_vehicle ) { + if( grabbed_vehicle ) { + m.level_vehicle( *grabbed_vehicle ); + grabbed_vehicle->check_falling_or_floating(); + } else { debugmsg( "Grabbed vehicle disappeared" ); return false; } diff --git a/src/map.cpp b/src/map.cpp index ed64c2bbcfa46..17a4cca14ff34 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -452,12 +452,10 @@ bool map::vehproceed( VehicleList &vehicle_list ) level_cache &cache = get_cache( zlev ); bool still_active = false; if( cache.veh_in_active_range ) { - for( int vehx = 0; vehx < MAPSIZE_X; vehx++ ) { - for( int vehy = 0; vehy < MAPSIZE_Y; vehy++ ) { - if( cache.veh_exists_at[vehx][vehy] ) { - still_active = true; - break; - } + for( const bool &has_vehicle : cache.veh_exists_at ) { + if( has_vehicle ) { + still_active = true; + break; } } } @@ -638,9 +636,11 @@ vehicle *map::move_vehicle( vehicle &veh, const tripoint &dp, const tileray &fac veh.on_move(); // Actually change position displace_vehicle( *new_vehicle, dp1 ); + level_vehicle( *new_vehicle ); } else if( !vertical ) { veh.stop(); } + veh.check_falling_or_floating(); // If the PC is in the currently moved vehicle, adjust the // view offset. if( g->u.controlling_vehicle && veh_pointer_or_null( veh_at( g->u.pos() ) ) == &veh ) { @@ -1059,18 +1059,22 @@ void map::unboard_vehicle( const tripoint &p, bool dead_passenger ) unboard_vehicle( *vp, passenger, dead_passenger ); } -bool map::displace_vehicle( vehicle &veh, const tripoint &dp ) +bool map::displace_vehicle( vehicle &veh, const tripoint &dp, const bool adjust_pos, + const std::set &parts_to_move ) { const tripoint src = veh.global_pos3(); // handle vehicle ramps int ramp_offset = 0; - if( has_flag( TFLAG_RAMP_UP, src + dp ) ) { - ramp_offset += 1; - } else if( has_flag( TFLAG_RAMP_DOWN, src + dp ) ) { - ramp_offset -= 1; + if( adjust_pos ) { + if( has_flag( TFLAG_RAMP_UP, src + dp ) ) { + ramp_offset += 1; + } else if( has_flag( TFLAG_RAMP_DOWN, src + dp ) ) { + ramp_offset -= 1; + } } - const tripoint dst = src + dp + tripoint( 0, 0, ramp_offset ); + const tripoint dst = src + ( adjust_pos ? + ( dp + tripoint( 0, 0, ramp_offset ) ) : tripoint_zero ); if( !inbounds( src ) ) { add_msg( m_debug, "map::displace_vehicle: coordinates out of bounds %d,%d,%d->%d,%d,%d", @@ -1134,6 +1138,10 @@ bool map::displace_vehicle( vehicle &veh, const tripoint &dp ) continue; } const int prt = r.prt; + if( !parts_to_move.empty() && parts_to_move.find( prt ) == parts_to_move.end() ) { + r.moved = true; + continue; + } Creature *psg = r.psg; const tripoint part_pos = veh.global_part_pos3( prt ); if( psg == nullptr ) { @@ -1154,14 +1162,18 @@ bool map::displace_vehicle( vehicle &veh, const tripoint &dp ) // ramps make everything super tricky int psg_offset_z = -ramp_offset; - if( has_flag( TFLAG_RAMP_UP, src + dp + veh_part.precalc[1] ) ) { + tripoint next_pos; // defaults to 0,0,0 + if( parts_to_move.empty() ) { + next_pos = veh_part.precalc[1]; + } + if( has_flag( TFLAG_RAMP_UP, src + dp + next_pos ) ) { psg_offset_z += 1; - } else if( has_flag( TFLAG_RAMP_DOWN, src + dp + veh_part.precalc[1] ) ) { + } else if( has_flag( TFLAG_RAMP_DOWN, src + dp + next_pos ) ) { psg_offset_z -= 1; } // Place passenger on the new part location - tripoint psgp( dst + veh_part.precalc[1] + tripoint( 0, 0, psg_offset_z ) ); + tripoint psgp( dst + next_pos + tripoint( 0, 0, psg_offset_z ) ); // someone is in the way so try again if( g->critter_at( psgp ) ) { complete = false; @@ -1179,7 +1191,7 @@ bool map::displace_vehicle( vehicle &veh, const tripoint &dp ) } veh.shed_loose_parts(); - smzs = veh.advance_precalc_mounts( dst_offset, src, dp, ramp_offset ); + smzs = veh.advance_precalc_mounts( dst_offset, src, dp, ramp_offset, adjust_pos, parts_to_move ); if( src_submap != dst_submap ) { veh.set_submap_moved( tripoint( dst.x / SEEX, dst.y / SEEY, dst.z ) ); auto src_submap_veh_it = src_submap->vehicles.begin() + our_i; @@ -1219,7 +1231,6 @@ bool map::displace_vehicle( vehicle &veh, const tripoint &dp ) g->setremoteveh( &veh ); } - veh.check_falling_or_floating(); // //global positions of vehicle loot zones have changed. veh.zones_dirty = true; @@ -1230,6 +1241,14 @@ bool map::displace_vehicle( vehicle &veh, const tripoint &dp ) return true; } +void map::level_vehicle( vehicle &veh ) +{ + int cnt = 0; + while( !veh.level_vehicle() && cnt < ( 2 * OVERMAP_DEPTH ) ) { + cnt++; + } +} + bool map::displace_water( const tripoint &p ) { // Check for shallow water @@ -4764,7 +4783,8 @@ std::list use_charges_from_stack( Stack stack, const itype_id &type, int & } static void use_charges_from_furn( const furn_t &f, const itype_id &type, int &quantity, - map *m, const tripoint &p, std::list &ret, const std::function &filter ) + map *m, const tripoint &p, std::list &ret, + const std::function &filter ) { if( m->has_flag( "LIQUIDCONT", p ) ) { map_stack item_list = m->i_at( p ); @@ -5255,7 +5275,8 @@ time_duration map::set_field_age( const tripoint &p, const field_type_id &type, * set intensity of field type at point, creating if not present, removing if intensity is 0 * returns resulting intensity, or 0 for not present */ -int map::set_field_intensity( const tripoint &p, const field_type_id &type, const int new_intensity, +int map::set_field_intensity( const tripoint &p, const field_type_id &type, + const int new_intensity, bool isoffset ) { field_entry *field_ptr = get_field( p, type ); @@ -5405,7 +5426,8 @@ void map::add_splatter( const field_type_id &type, const tripoint &where, int in mod_field_intensity( where, type, intensity ); } -void map::add_splatter_trail( const field_type_id &type, const tripoint &from, const tripoint &to ) +void map::add_splatter_trail( const field_type_id &type, const tripoint &from, + const tripoint &to ) { if( !type.id() ) { return; @@ -5422,7 +5444,8 @@ void map::add_splatter_trail( const field_type_id &type, const tripoint &from, c } } -void map::add_splash( const field_type_id &type, const tripoint ¢er, int radius, int intensity ) +void map::add_splash( const field_type_id &type, const tripoint ¢er, int radius, + int intensity ) { if( !type.id() ) { return; @@ -5533,7 +5556,8 @@ const visibility_variables &map::get_visibility_variables_cache() const return visibility_variables_cache; } -visibility_type map::get_visibility( const lit_level ll, const visibility_variables &cache ) const +visibility_type map::get_visibility( const lit_level ll, + const visibility_variables &cache ) const { switch( ll ) { case lit_level::DARK: @@ -5708,7 +5732,8 @@ void map::drawsq( const catacurses::window &w, player &u, const tripoint &p, drawsq( w, u, p, invert, show_items, u.pos() + u.view_offset, false, false, false ); } -void map::drawsq( const catacurses::window &w, player &u, const tripoint &p, const bool invert_arg, +void map::drawsq( const catacurses::window &w, player &u, const tripoint &p, + const bool invert_arg, const bool show_items_arg, const tripoint &view_center, const bool low_light, const bool bright_light, const bool inorder ) const { @@ -6029,7 +6054,8 @@ bool map::sees( const tripoint &F, const tripoint &T, const int range ) const /** * This one is internal-only, we don't want to expose the slope tweaking ickiness outside the map class. **/ -bool map::sees( const tripoint &F, const tripoint &T, const int range, int &bresenham_slope ) const +bool map::sees( const tripoint &F, const tripoint &T, const int range, + int &bresenham_slope ) const { if( ( range >= 0 && range < rl_dist( F, T ) ) || !inbounds( T ) ) { @@ -7064,7 +7090,8 @@ void map::rad_scorch( const tripoint &p, const time_duration &time_since_last_ac } } -void map::decay_cosmetic_fields( const tripoint &p, const time_duration &time_since_last_actualize ) +void map::decay_cosmetic_fields( const tripoint &p, + const time_duration &time_since_last_actualize ) { for( auto &pr : field_at( p ) ) { auto &fd = pr.second; @@ -7958,7 +7985,8 @@ void map::draw_square_ter( ter_id( *f )(), const point &p1, const point &p2 ) }, p1, p2 ); } -void map::draw_square_ter( const weighted_int_list &f, const point &p1, const point &p2 ) +void map::draw_square_ter( const weighted_int_list &f, const point &p1, + const point &p2 ) { draw_square( [this, f]( const point & p ) { const ter_id *tid = f.pick(); @@ -8164,7 +8192,8 @@ tripoint_range map::points_in_rectangle( const tripoint &from, const tripoint &t return tripoint_range( min, max ); } -tripoint_range map::points_in_radius( const tripoint ¢er, size_t radius, size_t radiusz ) const +tripoint_range map::points_in_radius( const tripoint ¢er, size_t radius, + size_t radiusz ) const { const tripoint min( std::max( 0, center.x - radius ), std::max( 0, center.y - radius ), clamp( center.z - radiusz, -OVERMAP_DEPTH, OVERMAP_HEIGHT ) ); @@ -8189,7 +8218,8 @@ tripoint_range map::points_on_zlevel() const return points_on_zlevel( abs_sub.z ); } -std::list map::get_active_items_in_radius( const tripoint ¢er, int radius ) const +std::list map::get_active_items_in_radius( const tripoint ¢er, + int radius ) const { return get_active_items_in_radius( center, radius, special_item_type::none ); } @@ -8234,7 +8264,8 @@ std::list map::get_active_items_in_radius( const tripoint ¢er return result; } -std::list map::find_furnitures_with_flag_in_radius( const tripoint ¢er, size_t radius, +std::list map::find_furnitures_with_flag_in_radius( const tripoint ¢er, + size_t radius, const std::string &flag, size_t radiusz ) { diff --git a/src/map.h b/src/map.h index 681b4b2502380..e1d0a1ca0dcba 100644 --- a/src/map.h +++ b/src/map.h @@ -583,7 +583,12 @@ class map void unboard_vehicle( const tripoint &p, bool dead_passenger = false ); // Change vehicle coordinates and move vehicle's driver along. // WARNING: not checking collisions! - bool displace_vehicle( vehicle &veh, const tripoint &dp ); + // optionally: include a list of parts to displace instead of the entire vehicle + bool displace_vehicle( vehicle &veh, const tripoint &dp, bool adjust_pos = true, + const std::set &parts_to_move = {} ); + // make sure a vehicle that is split across z-levels is properly supported + // calls displace_vehicle() and shouldn't be called from displace_vehicle + void level_vehicle( vehicle &veh ); // move water under wheels. true if moved bool displace_water( const tripoint &dp ); diff --git a/src/vehicle.cpp b/src/vehicle.cpp index a1be5526c49c1..3b9f20be1bb85 100644 --- a/src/vehicle.cpp +++ b/src/vehicle.cpp @@ -3159,7 +3159,7 @@ tripoint vehicle::global_part_pos3( const vehicle_part &pt ) const void vehicle::set_submap_moved( const tripoint &p ) { - const point old_msp = g->m.getabs( global_pos3().xy() ); + const point old_msp = get_map().getabs( global_pos3().xy() ); sm_pos = p; if( !tracking_on ) { return; @@ -6885,7 +6885,8 @@ void vehicle::force_erase_part( int part_num ) } std::set vehicle::advance_precalc_mounts( const point &new_pos, const tripoint &src, - const tripoint &dp, int ramp_offset ) + const tripoint &dp, int ramp_offset, const bool adjust_pos, + std::set parts_to_move ) { map &here = get_map(); std::set smzs; @@ -6909,9 +6910,23 @@ std::set vehicle::advance_precalc_mounts( const point &new_pos, const tripo // parts that enter the translation portion of a ramp on the same displacement as the // pivot point stay at the same relative z to the pivot point, as the ramp_offset adjustments // cancel out. + // if a vehicle manages move partially up or down a ramp and then veers off course, it + // can get split across the z-levels and continue moving, enough though large parts of the + // vehicle are unsupported. In that case, move the unsupported parts down until they are + // supported. + int index = -1; for( vehicle_part &prt : parts ) { + index += 1; here.clear_vehicle_point_from_cache( this, src + prt.precalc[0] ); - prt.precalc[0] = prt.precalc[1]; + // no parts means this is a normal horizontal or vertical move + if( parts_to_move.empty() ) { + prt.precalc[0] = prt.precalc[1]; + // partial part movement means we're zero-ing out after missing a ramp + } else if( adjust_pos && parts_to_move.find( index ) == parts_to_move.end() ) { + prt.precalc[0].z -= dp.z; + } else if( !adjust_pos && parts_to_move.find( index ) != parts_to_move.end() ) { + prt.precalc[0].z += dp.z; + } if( here.has_flag( TFLAG_RAMP_UP, src + dp + prt.precalc[0] ) ) { prt.precalc[0].z += 1; } else if( here.has_flag( TFLAG_RAMP_DOWN, src + dp + prt.precalc[0] ) ) { @@ -6921,10 +6936,13 @@ std::set vehicle::advance_precalc_mounts( const point &new_pos, const tripo prt.precalc[1].z = prt.precalc[0].z; smzs.insert( prt.precalc[0].z ); } - pivot_anchor[0] = pivot_anchor[1]; - pivot_rotation[0] = pivot_rotation[1]; - - pos = new_pos; + if( adjust_pos ) { + if( parts_to_move.empty() ) { + pivot_anchor[0] = pivot_anchor[1]; + pivot_rotation[0] = pivot_rotation[1]; + } + pos = new_pos; + } // Invalidate vehicle's point cache occupied_cache_time = calendar::before_time_starts; return smzs; diff --git a/src/vehicle.h b/src/vehicle.h index 872295ddc66b1..8a1edc1872625 100644 --- a/src/vehicle.h +++ b/src/vehicle.h @@ -1745,7 +1745,10 @@ class vehicle // Updates the internal precalculated mount offsets after the vehicle has been displaced // used in map::displace_vehicle() std::set advance_precalc_mounts( const point &new_pos, const tripoint &src, - const tripoint &dp, int ramp_offset ); + const tripoint &dp, int ramp_offset, + bool adjust_pos, std::set parts_to_move ); + // make sure the vehicle is supported across z-levels or on the same z-level + bool level_vehicle(); std::vector omt_path; // route for overmap-scale auto-driving std::vector alternators; // List of alternator indices diff --git a/src/vehicle_move.cpp b/src/vehicle_move.cpp index 708346c537da0..78bccb6028481 100644 --- a/src/vehicle_move.cpp +++ b/src/vehicle_move.cpp @@ -1595,6 +1595,54 @@ vehicle *vehicle::act_on_map() return g->m.move_vehicle( *this, dp, mdir ); } +bool vehicle::level_vehicle() +{ + map &here = get_map(); + if( !here.has_zlevels() || ( is_flying && is_rotorcraft() ) ) { + return true; + } + // make sure that all parts are either supported across levels or on the same level + std::map no_support; + for( vehicle_part &prt : parts ) { + if( prt.info().location != part_location_structure ) { + continue; + } + const tripoint part_pos = global_part_pos3( prt ); + if( no_support.find( part_pos.z ) == no_support.end() ) { + no_support[part_pos.z] = part_pos.z > -OVERMAP_DEPTH; + } + if( no_support[part_pos.z] ) { + no_support[part_pos.z] = here.has_flag_ter_or_furn( TFLAG_NO_FLOOR, part_pos ) && + !here.supports_above( part_pos + tripoint_below ); + } + } + std::set dropped_parts; + // if it's unsupported but on the same level, just let it fall + bool center_drop = false; + bool adjust_level = false; + if( no_support.size() > 1 ) { + for( int zlevel = -OVERMAP_DEPTH; zlevel <= OVERMAP_DEPTH; zlevel++ ) { + if( no_support.find( zlevel ) == no_support.end() || !no_support[zlevel] ) { + continue; + } + center_drop |= global_pos3().z == zlevel; + adjust_level = true; + // drop unsupported parts 1 zlevel + for( size_t prt = 0; prt < parts.size(); prt++ ) { + if( global_part_pos3( prt ).z == zlevel ) { + dropped_parts.insert( static_cast( prt ) ); + } + } + } + } + if( adjust_level ) { + here.displace_vehicle( *this, tripoint_below, center_drop, dropped_parts ); + return false; + } else { + return true; + } +} + void vehicle::check_falling_or_floating() { // TODO: Make the vehicle "slide" towards its center of weight @@ -1616,13 +1664,14 @@ void vehicle::check_falling_or_floating() } else { is_flying = false; } + size_t deep_water_tiles = 0; size_t water_tiles = 0; for( const tripoint &p : pts ) { if( is_falling ) { tripoint below( p.xy(), p.z - 1 ); - is_falling &= g->m.has_flag_ter_or_furn( TFLAG_NO_FLOOR, p ) && ( p.z > -OVERMAP_DEPTH ) && - !g->m.supports_above( below ); + is_falling &= g->m.has_flag_ter_or_furn( TFLAG_NO_FLOOR, p ) && + ( p.z > -OVERMAP_DEPTH ) && !g->m.supports_above( below ); } deep_water_tiles += g->m.has_flag( TFLAG_DEEP_WATER, p ) ? 1 : 0; water_tiles += g->m.has_flag( TFLAG_SWIMMABLE, p ) ? 1 : 0; diff --git a/tests/vehicle_ramp.cpp b/tests/vehicle_ramp_test.cpp similarity index 74% rename from tests/vehicle_ramp.cpp rename to tests/vehicle_ramp_test.cpp index 57cbb2c3f1887..39ca3ca6a6088 100644 --- a/tests/vehicle_ramp.cpp +++ b/tests/vehicle_ramp_test.cpp @@ -12,7 +12,6 @@ #include "avatar.h" #include "catch/catch.hpp" -#include "game.h" #include "itype.h" #include "map.h" #include "map_helpers.h" @@ -23,6 +22,7 @@ #include "bodypart.h" #include "calendar.h" #include "enums.h" +#include "game.h" #include "game_constants.h" #include "item.h" #include "line.h" @@ -37,9 +37,9 @@ #include "player_helpers.h" #include "map_helpers.h" -const efftype_id effect_blind( "blind" ); +static const efftype_id effect_blind( "blind" ); -static void clear_game_and_set_map( const int transit_x, bool use_ramp, bool up ) +static void clear_game_and_set_ramp( const int transit_x, bool use_ramp, bool up ) { // Set to turn 0 to prevent solars from producing power calendar::turn = 0; @@ -91,12 +91,12 @@ static void clear_game_and_set_map( const int transit_x, bool use_ramp, bool up } } } - g->m.invalidate_map_cache( 0 ); - g->m.build_map_cache( 0, true ); + here.invalidate_map_cache( 0 ); + here.build_map_cache( 0, true ); } // Algorithm goes as follows: -// Clear map +// Clear map and create a ramp // Spawn a vehicle // Drive it over the ramp, and confirm that the vehicle changes z-levels static void ramp_transition_angled( const vproto_id &veh_id, const int angle, @@ -114,7 +114,7 @@ static void ramp_transition_angled( const vproto_id &veh_id, const int angle, REQUIRE( veh_ptr != nullptr ); if( veh_ptr == nullptr ) { - return 0; + return; } vehicle &veh = *veh_ptr; @@ -239,3 +239,88 @@ TEST_CASE( "vehicle_ramp_test_61", "[vehicle][ramp]" ) test_ramp( veh, 61 ); } } + +static void level_out( const vproto_id &veh_id, const bool drop_pos ) +{ + map &here = get_map(); + clear_game_and_set_ramp( 75, drop_pos, false ); + const int start_z = drop_pos ? 1 : 0; + + const tripoint map_starting_point( 60, 60, start_z ); + vehicle *veh_ptr = here.add_vehicle( veh_id, map_starting_point, 180, 1, 0 ); + + REQUIRE( veh_ptr != nullptr ); + if( veh_ptr == nullptr ) { + return; + } + vehicle &veh = *veh_ptr; + veh.check_falling_or_floating(); + + REQUIRE( !veh.is_in_water() ); + + veh.tags.insert( "IN_CONTROL_OVERRIDE" ); + veh.engine_on = true; + + const int target_velocity = 800; + veh.cruise_velocity = target_velocity; + veh.velocity = target_velocity; + CHECK( veh.safe_velocity() > 0 ); + + std::vector all_parts; + for( const tripoint &pos : veh.get_points() ) { + for( vehicle_part *prt : veh.get_parts_at( pos, "", part_status_flag::any ) ) { + all_parts.push_back( prt ); + if( drop_pos && prt->mount.x < 0 ) { + prt->precalc[0].z = -1; + prt->precalc[1].z = -1; + } else if( !drop_pos && prt->mount.x > 1 ) { + prt->precalc[0].z = 1; + prt->precalc[1].z = 1; + } + } + } + std::set z_span; + for( vehicle_part *prt : all_parts ) { + z_span.insert( veh.global_part_pos3( *prt ).z ); + } + REQUIRE( z_span.size() > 1 ); + + monster *dmon_p = g->place_critter_at( mtype_id( "debug_mon" ), map_starting_point ); + monster &dmon = *dmon_p; + + for( int y = 0; y < SEEY * MAPSIZE; y++ ) { + for( int x = 0; x < SEEX * MAPSIZE; x++ ) { + here.ter_set( tripoint( x, y, 1 ), ter_id( "t_open_air" ) ); + here.ter_set( tripoint( x, y, 0 ), ter_id( "t_pavement" ) ); + } + } + + here.vehmove(); + for( vehicle_part *prt : all_parts ) { + CHECK( veh.global_part_pos3( *prt ).z == 0 ); + } + CHECK( dmon.posz() == 0 ); + CHECK( veh.global_pos3().z == 0 ); +} + +static void test_leveling( std::string type ) +{ + SECTION( type + " body drop" ) { + level_out( vproto_id( type ), true ); + } + SECTION( type + " edge drop" ) { + level_out( vproto_id( type ), false ); + } +} + +static std::vector level_vehs_to_test = {{ + "beetle", + } +}; + +TEST_CASE( "vehicle_level_test", "[vehicle][ramp]" ) +{ + for( const std::string &veh : level_vehs_to_test ) { + test_leveling( veh ); + } +} From cbeea94189ac07bea182b4c03919b6e66f24c797 Mon Sep 17 00:00:00 2001 From: ZhilkinSerg Date: Wed, 10 Jun 2020 23:57:20 +0300 Subject: [PATCH 059/420] mapgen: add bridge heads with ramps and move bridge roads to z+1 From: ZhilkinSerg Change bridges to be a multi-zlevel structure. At z0 and over the water, a bridge is a series of rock pylons. Above the pylons on z+1 is the standard bridge road. At each end of the bridge is a line of up ramps on z0 and a line of down ramps on z+1. Bridge generation is now handled explictly in overmap.cpp. --- .../terrain-fences-gates.json | 19 ++ .../terrain-highways.json | 59 ++++++ data/json/mapgen/bridges.json | 182 ++++++++++++++++++ .../overmap_terrain_transportation.json | 33 +++- data/json/regional_map_settings.json | 2 +- src/map_extras.cpp | 22 ++- src/mapgen_functions.cpp | 40 ---- src/mapgen_functions.h | 2 +- src/overmap.cpp | 76 ++++++++ src/overmap.h | 1 + tests/map_extra_test.cpp | 9 +- 11 files changed, 389 insertions(+), 56 deletions(-) create mode 100644 data/json/furniture_and_terrain/terrain-highways.json create mode 100644 data/json/mapgen/bridges.json diff --git a/data/json/furniture_and_terrain/terrain-fences-gates.json b/data/json/furniture_and_terrain/terrain-fences-gates.json index e38358b49e8fc..873df1c6b81d0 100644 --- a/data/json/furniture_and_terrain/terrain-fences-gates.json +++ b/data/json/furniture_and_terrain/terrain-fences-gates.json @@ -947,6 +947,25 @@ ] } }, + { + "type": "terrain", + "id": "t_guardrail_hw_air", + "name": "guard rail", + "description": "A section of metal railing, put in place to prevent people from falling or taking the easy way out.", + "symbol": "#", + "color": "light_gray", + "looks_like": "t_guardrail_bg_dp", + "move_cost": 3, + "flags": [ "TRANSPARENT", "NOITEM", "REDUCE_SCENT", "MOUNTABLE", "SHORT", "THIN_OBSTACLE", "ROAD", "BURROWABLE" ], + "bash": { + "str_min": 8, + "str_max": 150, + "sound": "crunch!", + "sound_fail": "clang!", + "ter_set": "t_pavement_hw_air", + "items": [ { "item": "pipe", "count": [ 1, 2 ] }, { "item": "scrap", "count": [ 3, 6 ] } ] + } + }, { "type": "terrain", "id": "t_guardrail_bg_dp", diff --git a/data/json/furniture_and_terrain/terrain-highways.json b/data/json/furniture_and_terrain/terrain-highways.json new file mode 100644 index 0000000000000..7eeaa08f1d3eb --- /dev/null +++ b/data/json/furniture_and_terrain/terrain-highways.json @@ -0,0 +1,59 @@ +[ + { + "type": "terrain", + "id": "t_pavement_hw_air", + "name": "bridge pavement", + "description": "A bridge section made out of metal and concrete.", + "looks_like": "t_pavement", + "symbol": ".", + "color": "dark_gray", + "move_cost": 2, + "flags": [ "TRANSPARENT", "FLAT", "ROAD", "MINEABLE" ], + "bash": { + "str_min": 70, + "str_max": 300, + "sound": "concrete cracking and metal screeching!", + "sound_fail": "whump!", + "ter_set": "t_open_air", + "items": [ { "item": "rock", "count": [ 4, 20 ] }, { "item": "rebar", "count": [ 10, 20 ] } ] + } + }, + { + "type": "terrain", + "id": "t_pavement_y_hw_air", + "name": "bridge yellow pavement", + "description": "A bridge section made out of metal and concrete. It's painted yellow.", + "looks_like": "t_pavement_y", + "symbol": ".", + "color": "yellow", + "move_cost": 2, + "flags": [ "TRANSPARENT", "FLAT", "ROAD", "MINEABLE" ], + "bash": { + "str_min": 70, + "str_max": 300, + "sound": "concrete cracking and metal screeching!", + "sound_fail": "whump!", + "ter_set": "t_open_air", + "items": [ { "item": "rock", "count": [ 4, 20 ] }, { "item": "rebar", "count": [ 10, 20 ] } ] + } + }, + { + "type": "terrain", + "id": "t_sidewalk_hw_air", + "name": "bridge sidewalk", + "description": "The sidewalk section of a concrete bridge.", + "looks_like": "t_sidewalk", + "symbol": ".", + "color": "light_gray", + "move_cost": 2, + "flags": [ "TRANSPARENT", "FLAT", "ROAD", "MINEABLE" ], + "bash": { + "str_min": 70, + "str_max": 300, + "sound": "concrete cracking and metal screeching!", + "sound_fail": "whump!", + "ter_set": "t_open_air", + "items": [ { "item": "rock", "count": [ 4, 20 ] }, { "item": "rebar", "count": [ 10, 20 ] } ] + } + } +] diff --git a/data/json/mapgen/bridges.json b/data/json/mapgen/bridges.json new file mode 100644 index 0000000000000..726f5d1c3fbb2 --- /dev/null +++ b/data/json/mapgen/bridges.json @@ -0,0 +1,182 @@ +[ + { + "type": "palette", + "id": "bridge_ground_palette", + "terrain": { + "s": "t_water_moving_sh", + "~": "t_water_moving_dp", + ".": "t_pavement_bg_dp", + ":": "t_pavement_y_bg_dp", + "_": "t_sidewalk_bg_dp", + "=": "t_guardrail_bg_dp", + "u": "t_ramp_up_low", + "U": "t_ramp_up_high", + "d": "t_ramp_down_high", + "D": "t_ramp_down_low", + "W": "t_sidewalk_ramp_down_high", + ">": "t_sidewalk_ramp_down_low", + "<": "t_sidewalk_ramp_up_high", + "w": "t_sidewalk_ramp_up_low", + "#": "t_concrete_wall" + } + }, + { + "type": "palette", + "id": "bridge_road_palette", + "terrain": { + " ": "t_open_air", + ".": "t_pavement_hw_air", + ":": "t_pavement_y_hw_air", + "_": "t_sidewalk_hw_air", + "=": "t_guardrail_hw_air", + "U": "t_ramp_up_high", + "d": "t_ramp_down_high", + "D": "t_ramp_down_low", + "W": "t_sidewalk_ramp_down_high", + ">": "t_sidewalk_ramp_down_low", + "<": "t_sidewalk_ramp_up_high", + "w": "t_sidewalk_ramp_up_low", + "#": "t_concrete_wall" + } + }, + { + "type": "mapgen", + "method": "json", + "om_terrain": [ "bridge" ], + "object": { + "fill_ter": "t_water_moving_dp", + "rows": [ + "~~~~~~~~s######s~~~~~~~~", + "~~~~~~~~ss####ss~~~~~~~~", + "~~~~~~~~~ss##ss~~~~~~~~~", + "~~~~~~~~~~ssss~~~~~~~~~~", + "~~~~~~~~~~~~~~~~~~~~~~~~", + "~~~~~~~~~~~~~~~~~~~~~~~~", + "~~~~~~~~~~~~~~~~~~~~~~~~", + "~~~~~~~~~~~~~~~~~~~~~~~~", + "~~~~~~~~~~~~~~~~~~~~~~~~", + "~~~~~~~~~~~~~~~~~~~~~~~~", + "~~~~~~~~~~~~~~~~~~~~~~~~", + "~~~~~~~~~~~~~~~~~~~~~~~~", + "~~~~~~~~~~~~~~~~~~~~~~~~", + "~~~~~~~~~~~~~~~~~~~~~~~~", + "~~~~~~~~~~~~~~~~~~~~~~~~", + "~~~~~~~~~~~~~~~~~~~~~~~~", + "~~~~~~~~~~~~~~~~~~~~~~~~", + "~~~~~~~~~~~~~~~~~~~~~~~~", + "~~~~~~~~~~~~~~~~~~~~~~~~", + "~~~~~~~~~~~~~~~~~~~~~~~~", + "~~~~~~~~~~ssss~~~~~~~~~~", + "~~~~~~~~~ss##ss~~~~~~~~~", + "~~~~~~~~ss####ss~~~~~~~~", + "~~~~~~~~s######s~~~~~~~~" + ], + "palettes": [ "bridge_ground_palette" ] + } + }, + { + "type": "mapgen", + "method": "json", + "om_terrain": [ "bridge_road" ], + "object": { + "fill_ter": "t_open_air", + "rows": [ + " =_................_= ", + " =_.......::......._= ", + " =_.......::......._= ", + " =_.......::......._= ", + " =_................_= ", + " =_.......::......._= ", + " =_.......::......._= ", + " =_.......::......._= ", + " =_................_= ", + " =_.......::......._= ", + " =_.......::......._= ", + " =_.......::......._= ", + " =_................_= ", + " =_.......::......._= ", + " =_.......::......._= ", + " =_.......::......._= ", + " =_................_= ", + " =_.......::......._= ", + " =_.......::......._= ", + " =_.......::......._= ", + " =_................_= ", + " =_.......::......._= ", + " =_.......::......._= ", + " =_.......::......._= " + ], + "palettes": [ "bridge_road_palette" ] + } + }, + { + "type": "mapgen", + "method": "json", + "om_terrain": [ "bridgehead_ground" ], + "object": { + "fill_ter": "t_water_moving_dp", + "rows": [ + "ss=_................_=ss", + "ss=_.......::......._=ss", + "ss=_.......::......._=ss", + "ss=_.......::......._=ss", + "ss=_................_=ss", + "ss=wuuuuuuuuuuuuuuuuw=ss", + "ss=DDDDDDDDDDDDDDDD>= ", + " =WddddddddddddddddW= ", + " =_.......::......._= ", + " =_................_= ", + " =_.......::......._= ", + " =_.......::......._= ", + " =_.......::......._= ", + " =_................_= ", + " =_.......::......._= ", + " =_.......::......._= ", + " =_.......::......._= ", + " =_................_= ", + " =_.......::......._= ", + " =_.......::......._= ", + " =_.......::......._= ", + " =_................_= ", + " =_.......::......._= ", + " =_.......::......._= ", + " =_.......::......._= " + ], + "palettes": [ "bridge_road_palette" ] + } + } +] diff --git a/data/json/overmap/overmap_terrain/overmap_terrain_transportation.json b/data/json/overmap/overmap_terrain/overmap_terrain_transportation.json index eb7a6f94d496e..ad2842be6c13a 100644 --- a/data/json/overmap/overmap_terrain/overmap_terrain_transportation.json +++ b/data/json/overmap/overmap_terrain/overmap_terrain_transportation.json @@ -62,16 +62,41 @@ }, { "type": "overmap_terrain", - "id": "bridge", + "abstract": "generic_bridge", "copy-from": "generic_transportation", "name": "bridge", "sym": "│", "color": "white", - "see_cost": 2, - "extras": "bridge", - "mapgen": [ { "method": "builtin", "name": "bridge" } ], + "see_cost": 2 + }, + { + "type": "overmap_terrain", + "id": "bridge", + "copy-from": "generic_bridge", + "color": "blue", "flags": [ "RIVER" ] }, + { + "type": "overmap_terrain", + "id": "bridge_road", + "copy-from": "generic_bridge", + "name": "bridge (overpass)" + }, + { + "type": "overmap_terrain", + "id": "bridgehead_ground", + "copy-from": "generic_bridge", + "name": "bridgehead (ground)", + "sym": "v", + "extras": "bridgehead_ground" + }, + { + "type": "overmap_terrain", + "id": "bridgehead_ramp", + "copy-from": "generic_bridge", + "name": "bridgehead (ramp)", + "sym": "^" + }, { "type": "overmap_terrain", "id": [ "roadstop", "roadstop_roof" ], diff --git a/data/json/regional_map_settings.json b/data/json/regional_map_settings.json index 4d45cca7a5304..873a2dbbe1596 100644 --- a/data/json/regional_map_settings.json +++ b/data/json/regional_map_settings.json @@ -499,8 +499,8 @@ "mx_prison_bus": 15 } }, + "bridgehead_ground": { "chance": 5, "extras": { "mx_minefield": 100 } }, "road_nesw_manhole": { "chance": 20, "extras": { "mx_city_trap": 100 } }, - "bridge": { "chance": 5, "extras": { "mx_minefield": 100 } }, "build": { "chance": 90, "extras": { diff --git a/src/map_extras.cpp b/src/map_extras.cpp index 55ffc8b999d9c..185a6a6b75d03 100644 --- a/src/map_extras.cpp +++ b/src/map_extras.cpp @@ -1098,7 +1098,7 @@ static bool mx_portal( map &m, const tripoint &abs_sub ) return true; } -static bool mx_minefield( map &m, const tripoint &abs_sub ) +static bool mx_minefield( map &, const tripoint &abs_sub ) { const tripoint abs_omt = sm_to_omt_copy( abs_sub ); const oter_id ¢er = overmap_buffer.ter( abs_omt ); @@ -1107,7 +1107,7 @@ static bool mx_minefield( map &m, const tripoint &abs_sub ) const oter_id &west = overmap_buffer.ter( abs_omt + point_west ); const oter_id &east = overmap_buffer.ter( abs_omt + point_east ); - const bool bridge_at_center = is_ot_match( "bridge", center, ot_match_type::type ); + const bool bridgehead_at_center = is_ot_match( "bridgehead_ground", center, ot_match_type::type ); const bool bridge_at_north = is_ot_match( "bridge", north, ot_match_type::type ); const bool bridge_at_south = is_ot_match( "bridge", south, ot_match_type::type ); const bool bridge_at_west = is_ot_match( "bridge", west, ot_match_type::type ); @@ -1124,7 +1124,14 @@ static bool mx_minefield( map &m, const tripoint &abs_sub ) bool did_something = false; - if( bridge_at_north && bridge_at_center && road_at_south ) { + if( !bridgehead_at_center ) { + return false; + } + + tinymap m; + if( bridge_at_north && bridgehead_at_center && road_at_south ) { + m.load( omt_to_sm_copy( abs_omt + point_south ), false ); + //Sandbag block at the left edge line_furn( &m, f_sandbag_half, point( 3, 4 ), point( 3, 7 ) ); line_furn( &m, f_sandbag_half, point( 3, 7 ), point( 9, 7 ) ); @@ -1222,7 +1229,8 @@ static bool mx_minefield( map &m, const tripoint &abs_sub ) did_something = true; } - if( bridge_at_south && bridge_at_center && road_at_north ) { + if( bridge_at_south && bridgehead_at_center && road_at_north ) { + m.load( omt_to_sm_copy( abs_omt + point_north ), false ); //Two horizontal lines of sandbags line_furn( &m, f_sandbag_half, point( 5, 15 ), point( 10, 15 ) ); line_furn( &m, f_sandbag_half, point( 13, 15 ), point( 18, 15 ) ); @@ -1323,7 +1331,8 @@ static bool mx_minefield( map &m, const tripoint &abs_sub ) did_something = true; } - if( bridge_at_west && bridge_at_center && road_at_east ) { + if( bridge_at_west && bridgehead_at_center && road_at_east ) { + m.load( omt_to_sm_copy( abs_omt + point_east ), false ); //Draw walls of first tent square_furn( &m, f_canvas_wall, point( 0, 3 ), point( 4, 13 ) ); @@ -1469,7 +1478,8 @@ static bool mx_minefield( map &m, const tripoint &abs_sub ) did_something = true; } - if( bridge_at_east && bridge_at_center && road_at_west ) { + if( bridge_at_east && bridgehead_at_center && road_at_west ) { + m.load( omt_to_sm_copy( abs_omt + point_west ), false ); //Spawn military cargo truck blocking the entry m.add_vehicle( vproto_id( "military_cargo_truck" ), point( 15, 11 ), 270, 70, 1 ); diff --git a/src/mapgen_functions.cpp b/src/mapgen_functions.cpp index 9077f9ba3a7b3..db75db95b46b7 100644 --- a/src/mapgen_functions.cpp +++ b/src/mapgen_functions.cpp @@ -103,7 +103,6 @@ building_gen_pointer get_mapgen_cfunction( const std::string &ident ) { "road_tee", &mapgen_road }, { "road_four_way", &mapgen_road }, { "field", &mapgen_field }, - { "bridge", &mapgen_bridge }, { "highway", &mapgen_highway }, { "railroad_straight", &mapgen_railroad }, { "railroad_curved", &mapgen_railroad }, @@ -1356,45 +1355,6 @@ void mapgen_sewer_four_way( mapgendata &dat ) m->place_items( "sewer", 28, point_zero, point( SEEX * 2 - 1, SEEY * 2 - 1 ), true, dat.when() ); } -/////////////////// -void mapgen_bridge( mapgendata &dat ) -{ - map *const m = &dat.m; - const auto is_river = [&]( const om_direction::type dir ) { - return dat.t_nesw[static_cast( om_direction::add( dir, - dat.terrain_type()->get_dir() ) )]->is_river(); - }; - - const bool river_west = is_river( om_direction::type::west ); - const bool river_east = is_river( om_direction::type::east ); - - for( int i = 0; i < SEEX * 2; i++ ) { - for( int j = 0; j < SEEY * 2; j++ ) { - if( i < 2 ) { - m->ter_set( point( i, j ), river_west ? t_water_moving_dp : grass_or_dirt() ); - } else if( i >= SEEX * 2 - 2 ) { - m->ter_set( point( i, j ), river_east ? t_water_moving_dp : grass_or_dirt() ); - } else if( i == 2 || i == SEEX * 2 - 3 ) { - m->ter_set( point( i, j ), t_guardrail_bg_dp ); - } else if( i == 3 || i == SEEX * 2 - 4 ) { - m->ter_set( point( i, j ), t_sidewalk_bg_dp ); - } else { - if( ( i == SEEX - 1 || i == SEEX ) && j % 4 != 0 ) { - m->ter_set( point( i, j ), t_pavement_y_bg_dp ); - } else { - m->ter_set( point( i, j ), t_pavement_bg_dp ); - } - } - } - } - - // spawn regular road out of fuel vehicles - VehicleSpawn::apply( vspawn_id( "default_bridge" ), *m, "bridge" ); - - m->rotate( static_cast( dat.terrain_type()->get_dir() ) ); - m->place_items( "road", 5, point_zero, point( SEEX * 2 - 1, SEEX * 2 - 1 ), false, dat.when() ); -} - void mapgen_highway( mapgendata &dat ) { map *const m = &dat.m; diff --git a/src/mapgen_functions.h b/src/mapgen_functions.h index 004d7919bdb4a..cc719b05c4f1f 100644 --- a/src/mapgen_functions.h +++ b/src/mapgen_functions.h @@ -44,7 +44,7 @@ void mapgen_hive( mapgendata &dat ); void mapgen_spider_pit( mapgendata &dat ); void mapgen_river_center( mapgendata &dat ); void mapgen_road( mapgendata &dat ); -void mapgen_bridge( mapgendata &dat ); +//void mapgen_bridge( mapgendata &dat ); void mapgen_railroad( mapgendata &dat ); void mapgen_railroad_bridge( mapgendata &dat ); void mapgen_highway( mapgendata &dat ); diff --git a/src/overmap.cpp b/src/overmap.cpp index d64ef6cddd869..9284303d12ab4 100644 --- a/src/overmap.cpp +++ b/src/overmap.cpp @@ -1487,6 +1487,14 @@ void overmap::generate( const overmap *north, const overmap *east, requires_sub = generate_sub( z ); } while( requires_sub && ( --z >= -OVERMAP_DEPTH ) ); + // Always need at least one overlevel, but how many more + z = 1; + bool requires_over = false; + do { + requires_over = generate_over( z ); + } while( requires_over && ( ++z <= OVERMAP_HEIGHT ) ); + + // Place the monsters, now that the terrain is laid out place_mongroups(); place_radios(); @@ -1762,6 +1770,74 @@ bool overmap::generate_sub( const int z ) return requires_sub; } +bool overmap::generate_over( const int z ) +{ + bool requires_over = false; + std::vector bridge_points; + + // These are so common that it's worth checking first as int. + const std::set skip_below = { + oter_id( "empty_rock" ), oter_id( "forest" ), oter_id( "field" ), + oter_id( "forest_thick" ), oter_id( "forest_water" ) + }; + + if( z == 1 ) { + for( int i = 0; i < OMAPX; i++ ) { + for( int j = 0; j < OMAPY; j++ ) { + tripoint p( i, j, z ); + const oter_id oter_below = ter( p + tripoint_below ); + const oter_id oter_ground = ter( tripoint( p.xy(), 0 ) ); + + // implicitly skip skip_below oter_ids + if( skip_below.find( oter_below ) != skip_below.end() ) { + continue; + } + + if( is_ot_match( "bridge", oter_ground, ot_match_type::type ) ) { + ter_set( p, oter_id( "bridge_road" + oter_get_rotation_string( oter_ground ) ) ); + bridge_points.emplace_back( i, j ); + } + } + } + } + + // Check and put bridgeheads + std::vector> bridgehead_points; + for( const point &bp : bridge_points ) { + //const oter_id oter_ground = ter( tripoint( bp, 0 ) ); + const oter_id oter_ground_north = ter( tripoint( bp, 0 ) + tripoint_north ); + const oter_id oter_ground_south = ter( tripoint( bp, 0 ) + tripoint_south ); + const oter_id oter_ground_east = ter( tripoint( bp, 0 ) + tripoint_east ); + const oter_id oter_ground_west = ter( tripoint( bp, 0 ) + tripoint_west ); + const bool is_bridge_north = is_ot_match( "bridge", oter_ground_north, ot_match_type::type ); + const bool is_bridge_south = is_ot_match( "bridge", oter_ground_south, ot_match_type::type ); + const bool is_bridge_east = is_ot_match( "bridge", oter_ground_east, ot_match_type::type ); + const bool is_bridge_west = is_ot_match( "bridge", oter_ground_west, ot_match_type::type ); + + if( is_bridge_north ^ is_bridge_south || is_bridge_east ^ is_bridge_west ) { + std::string ramp_facing; + if( is_bridge_north ) { + ramp_facing = "_south"; + } else if( is_bridge_south ) { + ramp_facing = "_north"; + } else if( is_bridge_east ) { + ramp_facing = "_west"; + } else { + ramp_facing = "_east"; + } + bridgehead_points.emplace_back( bp, ramp_facing ); + } + } + for( const std::pair &bhp : bridgehead_points ) { + tripoint p( bhp.first, 0 ); + ter_set( p, oter_id( "bridgehead_ground" + bhp.second ) ); + ter_set( p + tripoint_above, oter_id( "bridgehead_ramp" + bhp.second ) ); + } + + return requires_over; +} + + std::vector overmap::find_terrain( const std::string &term, int zlevel ) { std::vector found; diff --git a/src/overmap.h b/src/overmap.h index c77e5e716d540..d511563ae933d 100644 --- a/src/overmap.h +++ b/src/overmap.h @@ -387,6 +387,7 @@ class overmap const overmap *south, const overmap *west, overmap_special_batch &enabled_specials ); bool generate_sub( int z ); + bool generate_over( int z ); const city &get_nearest_city( const tripoint &p ) const; diff --git a/tests/map_extra_test.cpp b/tests/map_extra_test.cpp index 01f690463e9a8..283d94e9a8d25 100644 --- a/tests/map_extra_test.cpp +++ b/tests/map_extra_test.cpp @@ -11,9 +11,9 @@ TEST_CASE( "mx_minefield real spawn", "[map_extra][overmap]" ) // many overmaps when searching. const tripoint origin = tripoint( 90, 90, 0 ); - // Find all of the bridges within a 180 OMT radius of this location. + // Find all of the bridgeheads within a 180 OMT radius of this location. omt_find_params find_params; - find_params.types = {{"bridge", ot_match_type::type}}; + find_params.types = {{"bridgehead_ground", ot_match_type::type}}; find_params.search_range = 180; const std::vector bridges = overmap_buffer.find_all( origin, find_params ); @@ -46,15 +46,16 @@ TEST_CASE( "mx_minefield theoretical spawn", "[map_extra][overmap]" ) overmap &om = overmap_buffer.get( point_zero ); const oter_id road( "road_ns" ); + const oter_id bridgehead( "bridgehead_ground_north" ); const oter_id bridge( "bridge_north" ); // The mx_minefield map extra expects to have a particular configuration with - // three OMTs--a road, then a bridge, then a bridge once again. + // three OMTs--a bridgehead, then a road, then a road once again. // It does this for four rotations, with the road on the north, south, east, // and west of the target point. const auto setup_terrain_and_generate = [&]( const tripoint & center, om_direction::type bridge_direction ) { - om.ter_set( center, bridge ); + om.ter_set( center, bridgehead ); om.ter_set( center + om_direction::displace( bridge_direction, 1 ), bridge ); om.ter_set( center + om_direction::displace( om_direction::opposite( bridge_direction ), 1 ), road ); From 0ea8a27db35e7f4acfc48338167a74ed1f2c56c1 Mon Sep 17 00:00:00 2001 From: Mark Langsdorf Date: Tue, 16 Jun 2020 06:46:36 -0500 Subject: [PATCH 060/420] construction: add construction for ramps add construction entries for concrete ramps. Ramp construction has to begin with the low end up the up ramp, which automatically builds the low end of the down ramp above it. The high end of the up ramp has to be built next to an existing low end of an up ramp, and also automtically builds the high end of the up ramp above it. --- data/json/construction.json | 34 ++++++++++++++++++++++++++++++ src/construction.cpp | 42 +++++++++++++++++++++++++++++++++++-- 2 files changed, 74 insertions(+), 2 deletions(-) diff --git a/data/json/construction.json b/data/json/construction.json index 87b51e6bfc103..bb0e9e596c20f 100644 --- a/data/json/construction.json +++ b/data/json/construction.json @@ -2615,6 +2615,40 @@ "pre_terrain": "t_rock", "post_special": "done_mine_upstair" }, + { + "type": "construction", + "id": "constr_concrete_ramp_low", + "description": "Build Low End of a Concrete Ramp", + "//": "Builds a low end of a concrete ramp going up on this level and down on the level above.", + "pre_note": "Build a concrete ramp leading to the next z-level above, and the corresponding ramp down leading from the z-level above to this level. The high end of a ramp must be built adjacent to allow moving between z-levels in both directions.", + "category": "DIG", + "required_skills": [ [ "fabrication", 3 ] ], + "time": "150 m", + "tools": [ [ [ "con_mix", 125 ] ] ], + "qualities": [ [ { "id": "SMOOTH", "level": 1 } ] ], + "components": [ [ [ "concrete", 5 ] ], [ [ "water", 5 ] ] ], + "pre_terrain": "t_pit_shallow", + "pre_special": "check_ramp_low", + "post_terrain": "t_ramp_up_low", + "post_special": "done_ramp_low" + }, + { + "type": "construction", + "id": "constr_concrete_ramp_high", + "description": "Build High End of a Concrete Ramp", + "//": "Builds a high end of a concrete ramp going up on this level and down on the level above.", + "pre_note": "Build a concrete ramp leading to the next z-level above, and the corresponding ramp down leading from the z-level above to this level. It must be built next to a low end of a ramp to allow moving between z-levels in both directions.", + "category": "DIG", + "required_skills": [ [ "fabrication", 3 ] ], + "time": "150 m", + "tools": [ [ [ "con_mix", 125 ] ] ], + "qualities": [ [ { "id": "SMOOTH", "level": 1 } ] ], + "components": [ [ [ "concrete", 5 ] ], [ [ "water", 5 ] ] ], + "pre_terrain": "t_pit_shallow", + "pre_special": "check_ramp_high", + "post_terrain": "t_ramp_up_high", + "post_special": "done_ramp_high" + }, { "type": "construction", "id": "constr_veh", diff --git a/src/construction.cpp b/src/construction.cpp index 6c2cfe62de99c..1b5765c4367e2 100644 --- a/src/construction.cpp +++ b/src/construction.cpp @@ -107,6 +107,8 @@ bool check_empty_up_OK( const tripoint & ); // tile is empty and below OVERMAP_H bool check_up_OK( const tripoint & ); // tile is below OVERMAP_HEIGHT bool check_down_OK( const tripoint & ); // tile is above OVERMAP_DEPTH bool check_no_trap( const tripoint & ); +bool check_ramp_low( const tripoint & ); +bool check_ramp_high( const tripoint & ); // Special actions to be run post-terrain-mod static void done_nothing( const tripoint & ) {} @@ -123,6 +125,8 @@ void done_window_curtains( const tripoint & ); void done_extract_maybe_revert_to_dirt( const tripoint & ); void done_mark_firewood( const tripoint & ); void done_mark_practice_target( const tripoint & ); +void done_ramp_low( const tripoint & ); +void done_ramp_high( const tripoint & ); void failure_standard( const tripoint & ); void failure_deconstruct( const tripoint & ); @@ -1118,6 +1122,24 @@ bool construct::check_no_trap( const tripoint &p ) return get_map().tr_at( p ).is_null(); } +bool construct::check_ramp_high( const tripoint &p ) +{ + if( check_up_OK( p ) && check_up_OK( p + tripoint_above ) ) { + for( const point &car_d : four_cardinal_directions ) { + // check adjacent points on the z-level above for a completed down ramp + if( get_map().has_flag( TFLAG_RAMP_DOWN, p + car_d + tripoint_above ) ) { + return true; + } + } + } + return false; +} + +bool construct::check_ramp_low( const tripoint &p ) +{ + return check_up_OK( p ) && check_up_OK( p + tripoint_above ); +} + void construct::done_trunk_plank( const tripoint &/*p*/ ) { int num_logs = rng( 2, 3 ); @@ -1416,6 +1438,18 @@ void construct::done_mark_practice_target( const tripoint &p ) get_map().trap_set( p, tr_practice_target ); } +void construct::done_ramp_low( const tripoint &p ) +{ + const tripoint top = p + tripoint_above; + get_map().ter_set( top, ter_id( "t_ramp_down_low" ) ); +} + +void construct::done_ramp_high( const tripoint &p ) +{ + const tripoint top = p + tripoint_above; + get_map().ter_set( top, ter_id( "t_ramp_down_high" ) ); +} + void construct::failure_standard( const tripoint & ) { add_msg( m_info, _( "You cannot build there!" ) ); @@ -1519,7 +1553,9 @@ void load_construction( const JsonObject &jo ) { "check_empty_up_OK", construct::check_empty_up_OK }, { "check_up_OK", construct::check_up_OK }, { "check_down_OK", construct::check_down_OK }, - { "check_no_trap", construct::check_no_trap } + { "check_no_trap", construct::check_no_trap }, + { "check_ramp_low", construct::check_ramp_low }, + { "check_ramp_high", construct::check_ramp_high } } }; static const std::map> post_special_map = {{ @@ -1535,7 +1571,9 @@ void load_construction( const JsonObject &jo ) { "done_window_curtains", construct::done_window_curtains }, { "done_extract_maybe_revert_to_dirt", construct::done_extract_maybe_revert_to_dirt }, { "done_mark_firewood", construct::done_mark_firewood }, - { "done_mark_practice_target", construct::done_mark_practice_target } + { "done_mark_practice_target", construct::done_mark_practice_target }, + { "done_ramp_low", construct::done_ramp_low }, + { "done_ramp_high", construct::done_ramp_high } } }; std::map> explain_fail_map; From b0a9fa4487f432b747cca76d2e757f171d2ddb98 Mon Sep 17 00:00:00 2001 From: John Bytheway Date: Thu, 2 Jul 2020 20:22:27 -0400 Subject: [PATCH 061/420] Expand pockets in test_tool_belt The test was failing because test_tool_belt's pockets were too small to contain a crowbar. Increase the max_item_length of all its pockets accordingly. Fix iteminfo test accordingly. --- data/mods/TEST_DATA/items.json | 4 ++++ tests/iteminfo_test.cpp | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/data/mods/TEST_DATA/items.json b/data/mods/TEST_DATA/items.json index 0da4fd0a1ea26..bc594099da600 100644 --- a/data/mods/TEST_DATA/items.json +++ b/data/mods/TEST_DATA/items.json @@ -96,6 +96,7 @@ { "holster": true, "min_item_volume": "50 ml", + "max_item_length": "60 cm", "max_contains_volume": "1500 ml", "max_contains_weight": "1000 g", "moves": 50, @@ -104,6 +105,7 @@ { "holster": true, "min_item_volume": "50 ml", + "max_item_length": "60 cm", "max_contains_volume": "1500 ml", "max_contains_weight": "1000 g", "moves": 50, @@ -112,6 +114,7 @@ { "holster": true, "min_item_volume": "50 ml", + "max_item_length": "60 cm", "max_contains_volume": "1500 ml", "max_contains_weight": "1000 g", "moves": 50, @@ -120,6 +123,7 @@ { "holster": true, "min_item_volume": "50 ml", + "max_item_length": "60 cm", "max_contains_volume": "1500 ml", "max_contains_weight": "1000 g", "moves": 50, diff --git a/tests/iteminfo_test.cpp b/tests/iteminfo_test.cpp index 0d2ea0397f26f..bff1c6ffc7610 100644 --- a/tests/iteminfo_test.cpp +++ b/tests/iteminfo_test.cpp @@ -2117,7 +2117,7 @@ TEST_CASE( "pocket info for a multi-pocket item", "[iteminfo][pocket][multiple]" "--\n" "4 Pockets with capacity:\n" "Volume: 1.50 L Weight: 1.00 kg\n" - "Maximum item length: 155 mm\n" + "Maximum item length: 60 cm\n" "Minimum item volume: 0.050 L\n" "Base moves to remove item: 50\n" ); } From 8f27fb597d4e75f09e24658c4b5395d9ca49ccf5 Mon Sep 17 00:00:00 2001 From: Kevin Granade Date: Fri, 3 Jul 2020 01:40:40 +0000 Subject: [PATCH 062/420] Global reference migration part 15 --- src/activity_actor.cpp | 48 ++++++++++++++++++-------------- src/sdltiles.cpp | 43 +++++++++++++++------------- src/start_location.cpp | 16 ++++++----- src/wish.cpp | 2 +- tests/active_item_cache_test.cpp | 28 +++++++++---------- tests/memorial_test.cpp | 11 ++++---- tests/player_helpers.cpp | 8 +++--- tests/vehicle_interact_test.cpp | 21 +++++++------- 8 files changed, 96 insertions(+), 81 deletions(-) diff --git a/src/activity_actor.cpp b/src/activity_actor.cpp index a005dfa2e9c85..b48bfff63e87e 100644 --- a/src/activity_actor.cpp +++ b/src/activity_actor.cpp @@ -90,7 +90,7 @@ std::string enum_to_string( WS data ) aim_activity_actor::aim_activity_actor() { - initial_view_offset = g->u.view_offset; + initial_view_offset = get_avatar().view_offset; } aim_activity_actor aim_activity_actor::use_wielded() @@ -136,7 +136,7 @@ void aim_activity_actor::do_turn( player_activity &act, Character &who ) aborted = true; return; } - avatar &you = g->u; + avatar &you = get_avatar(); item *weapon = get_weapon(); if( !weapon || !avatar_action::can_fire_weapon( you, get_map(), *weapon ) ) { @@ -241,10 +241,12 @@ std::unique_ptr aim_activity_actor::deserialize( JsonIn &jsin ) item *aim_activity_actor::get_weapon() { switch( weapon_source ) { - case WeaponSource::Wielded: + case WeaponSource::Wielded: { + Character &player_character = get_player_character(); // Check for lost gun (e.g. yanked by zombie technician) // TODO: check that this is the same gun that was used to start aiming - return g->u.weapon.is_null() ? nullptr : &g->u.weapon; + return player_character.weapon.is_null() ? nullptr : &player_character.weapon; + } case WeaponSource::Bionic: case WeaponSource::Mutation: // TODO: check if the player lost relevant bionic/mutation @@ -257,10 +259,11 @@ item *aim_activity_actor::get_weapon() void aim_activity_actor::restore_view() { - bool changed_z = g->u.view_offset.z != initial_view_offset.z; - g->u.view_offset = initial_view_offset; + avatar &player_character = get_avatar(); + bool changed_z = player_character.view_offset.z != initial_view_offset.z; + player_character.view_offset = initial_view_offset; if( changed_z ) { - get_map().invalidate_map_cache( g->u.view_offset.z ); + get_map().invalidate_map_cache( player_character.view_offset.z ); g->invalidate_main_ui_adaptor(); } } @@ -268,7 +271,7 @@ void aim_activity_actor::restore_view() bool aim_activity_actor::load_RAS_weapon() { // TODO: use activity for fetching ammo and loading weapon - player &you = g->u; + player &you = get_avatar(); item *weapon = get_weapon(); gun_mode gun = weapon->gun_current_mode(); const auto ammo_location_is_valid = [&]() -> bool { @@ -313,7 +316,7 @@ bool aim_activity_actor::load_RAS_weapon() void aim_activity_actor::unload_RAS_weapon() { // Unload reload-and-shoot weapons to avoid leaving bows pre-loaded with arrows - avatar &you = g->u; + avatar &you = get_avatar(); item *weapon = get_weapon(); if( !weapon ) { return; @@ -390,7 +393,7 @@ void dig_activity_actor::finish( player_activity &act, Character &who ) calendar::turn ) ); } - const int helpersize = g->u.get_num_crafting_helpers( 3 ); + const int helpersize = get_avatar().get_num_crafting_helpers( 3 ); who.mod_stored_nutr( 5 - helpersize ); who.mod_thirst( 5 - helpersize ); who.mod_fatigue( 10 - ( helpersize * 2 ) ); @@ -460,7 +463,7 @@ void dig_channel_activity_actor::finish( player_activity &act, Character &who ) calendar::turn ) ); } - const int helpersize = g->u.get_num_crafting_helpers( 3 ); + const int helpersize = get_avatar().get_num_crafting_helpers( 3 ); who.mod_stored_nutr( 5 - helpersize ); who.mod_thirst( 5 - helpersize ); who.mod_fatigue( 10 - ( helpersize * 2 ) ); @@ -1129,8 +1132,9 @@ std::unique_ptr open_gate_activity_actor::deserialize( JsonIn &j void consume_activity_actor::start( player_activity &act, Character &guy ) { int moves; + Character &player_character = get_player_character(); if( consume_location ) { - const auto ret = g->u.will_eat( *consume_location, true ); + const auto ret = player_character.will_eat( *consume_location, true ); if( !ret.success() ) { consume_menu_selections = std::vector(); return; @@ -1139,7 +1143,7 @@ void consume_activity_actor::start( player_activity &act, Character &guy ) } moves = to_moves( guy.get_consume_time( *consume_location ) ); } else if( !consume_item.is_null() ) { - const auto ret = g->u.will_eat( consume_item, true ); + const auto ret = player_character.will_eat( consume_item, true ); if( !ret.success() ) { consume_menu_selections = std::vector(); return; @@ -1163,22 +1167,23 @@ void consume_activity_actor::finish( player_activity &act, Character & ) // too late; we've already consumed). act.interruptable = false; + avatar &player_character = get_avatar(); if( consume_location ) { - g->u.consume( consume_location, force ); + player_character.consume( consume_location, force ); } else if( !consume_item.is_null() ) { - g->u.consume( consume_item, force ); + player_character.consume( consume_item, force ); } else { debugmsg( "Item location/name to be consumed should not be null." ); } - if( g->u.get_value( "THIEF_MODE_KEEP" ) != "YES" ) { - g->u.set_value( "THIEF_MODE", "THIEF_ASK" ); + if( player_character.get_value( "THIEF_MODE_KEEP" ) != "YES" ) { + player_character.set_value( "THIEF_MODE", "THIEF_ASK" ); } //setting act to null clears these so back them up std::vector temp_selections = consume_menu_selections; act.set_to_null(); if( !temp_selections.empty() ) { - g->u.assign_activity( ACT_EAT_MENU ); - g->u.activity.values = temp_selections; + player_character.assign_activity( ACT_EAT_MENU ); + player_character.activity.values = temp_selections; } } @@ -1314,11 +1319,12 @@ void workout_activity_actor::start( player_activity &act, Character &who ) act.set_to_null(); return; } + map &here = get_map(); // free training requires all limbs intact, but specialized workout machines // train upper or lower parts of body only and may permit workout with // broken limbs as long as they are not involved by the machine - bool hand_equipment = g->m.has_flag_furn( "WORKOUT_ARMS", location ); - bool leg_equipment = g->m.has_flag_furn( "WORKOUT_LEGS", location ); + bool hand_equipment = here.has_flag_furn( "WORKOUT_ARMS", location ); + bool leg_equipment = here.has_flag_furn( "WORKOUT_LEGS", location ); static const bodypart_id arm_l = bodypart_id( "arm_l" ); static const bodypart_id arm_r = bodypart_id( "arm_r" ); static const bodypart_id leg_l = bodypart_id( "leg_l" ); diff --git a/src/sdltiles.cpp b/src/sdltiles.cpp index 033ac1fe16bcb..646187eaa54e5 100644 --- a/src/sdltiles.cpp +++ b/src/sdltiles.cpp @@ -1250,7 +1250,7 @@ void cata_cursesport::curses_drawwindow( const catacurses::window &w ) clear_window_area( w ); tilecontext->draw_minimap( point( win->pos.x * fontwidth, win->pos.y * fontheight ), - tripoint( g->u.pos().xy(), g->ter_view_p.z ), + tripoint( get_player_character().pos().xy(), g->ter_view_p.z ), win->width * font->fontwidth, win->height * font->fontheight ); update = true; @@ -1854,7 +1854,8 @@ input_context touch_input_context; std::string get_quick_shortcut_name( const std::string &category ) { - if( category == "DEFAULTMODE" && g->check_zone( zone_type_id( "NO_AUTO_PICKUP" ), g->u.pos() ) && + if( category == "DEFAULTMODE" && + g->check_zone( zone_type_id( "NO_AUTO_PICKUP" ), get_player_character().pos() ) && get_option( "ANDROID_SHORTCUT_ZONE" ) ) { return "DEFAULTMODE____SHORTCUTS"; } @@ -2112,10 +2113,11 @@ void remove_stale_inventory_quick_shortcuts() valid = inv_chars.valid( key ); in_inventory = false; if( valid ) { - in_inventory = g->u.inv.invlet_to_position( key ) != INT_MIN; + Character &player_character = get_player_character(); + in_inventory = player_character.inv.invlet_to_position( key ) != INT_MIN; if( !in_inventory ) { // We couldn't find this item in the inventory, let's check worn items - for( const auto &item : g->u.worn ) { + for( const auto &item : player_character.worn ) { if( item.invlet == key ) { in_inventory = true; break; @@ -2124,7 +2126,7 @@ void remove_stale_inventory_quick_shortcuts() } if( !in_inventory ) { // We couldn't find it in worn items either, check weapon held - if( g->u.weapon.invlet == key ) { + if( player_character.weapon.invlet == key ) { in_inventory = true; } } @@ -2244,11 +2246,13 @@ void draw_quick_shortcuts() std::string hint_text; if( show_hint ) { if( touch_input_context.get_category() == "INVENTORY" && inv_chars.valid( key ) ) { + Character &player_character = get_player_character(); // Special case for inventory items - show the inventory item name as help text - hint_text = g->u.inv.find_item( g->u.inv.invlet_to_position( key ) ).display_name(); + hint_text = player_character.inv.find_item( player_character.inv.invlet_to_position( + key ) ).display_name(); if( hint_text == "none" ) { // We couldn't find this item in the inventory, let's check worn items - for( const auto &item : g->u.worn ) { + for( const auto &item : player_character.worn ) { if( item.invlet == key ) { hint_text = item.display_name(); break; @@ -2257,8 +2261,8 @@ void draw_quick_shortcuts() } if( hint_text == "none" ) { // We couldn't find it in worn items either, must be weapon held - if( g->u.weapon.invlet == key ) { - hint_text = g->u.weapon.display_name(); + if( player_character.weapon.invlet == key ) { + hint_text = player_character.weapon.display_name(); } } } else { @@ -2627,24 +2631,25 @@ static void CheckMessages() // Actions to remove - we only want to remove things that we're 100% sure won't be useful to players otherwise std::set actions_remove; + Character &player_character = get_player_character(); // Check if we're in a potential combat situation, if so, sort a few actions to the top. - if( !g->u.get_hostile_creatures( 60 ).empty() ) { + if( !player_character.get_hostile_creatures( 60 ).empty() ) { // Only prioritize movement options if we're not driving. - if( !g->u.controlling_vehicle ) { + if( !player_character.controlling_vehicle ) { actions.insert( ACTION_CYCLE_MOVE ); } // Only prioritize fire weapon options if we're wielding a ranged weapon. - if( g->u.weapon.is_gun() || g->u.weapon.has_flag( "REACH_ATTACK" ) ) { + if( player_character.weapon.is_gun() || player_character.weapon.has_flag( "REACH_ATTACK" ) ) { actions.insert( ACTION_FIRE ); } } // If we're already running, make it simple to toggle running to off. - if( g->u.is_running() ) { + if( player_character.is_running() ) { actions.insert( ACTION_TOGGLE_RUN ); } // If we're already crouching, make it simple to toggle crouching to off. - if( g->u.is_crouching() ) { + if( player_character.is_crouching() ) { actions.insert( ACTION_TOGGLE_CROUCH ); } @@ -2658,9 +2663,9 @@ static void CheckMessages() // display that action at the top of the list. for( int dx = -1; dx <= 1; dx++ ) { for( int dy = -1; dy <= 1; dy++ ) { - int x = g->u.posx() + dx; - int y = g->u.posy() + dy; - int z = g->u.posz(); + int x = player_character.posx() + dx; + int y = player_character.posy() + dy; + int z = player_character.posz(); const tripoint pos( x, y, z ); // Check if we're near a vehicle, if so, vehicle controls should be top. @@ -2757,12 +2762,12 @@ static void CheckMessages() } // Check if we're significantly hungry or thirsty - if so, add eat - if( g->u.get_hunger() > 100 || g->u.get_thirst() > 40 ) { + if( player_character.get_hunger() > 100 || player_character.get_thirst() > 40 ) { actions.insert( ACTION_EAT ); } // Check if we're dead tired - if so, add sleep - if( g->u.get_fatigue() > fatigue_levels::DEAD_TIRED ) { + if( player_character.get_fatigue() > fatigue_levels::DEAD_TIRED ) { actions.insert( ACTION_SLEEP ); } diff --git a/src/start_location.cpp b/src/start_location.cpp index f4d7060f2c1be..aecd2da4ceccc 100644 --- a/src/start_location.cpp +++ b/src/start_location.cpp @@ -159,9 +159,10 @@ static void board_up( map &m, const tripoint_range &range ) continue; } // If the furniture is movable and the character can move it, use it to barricade - // g->u is workable here as NPCs by definition are not starting the game. (Let's hope.) + // is workable here as NPCs by definition are not starting the game. (Let's hope.) ///\EFFECT_STR determines what furniture might be used as a starting area barricade - if( m.furn( p ).obj().is_movable() && m.furn( p ).obj().move_str_req < g->u.get_str() ) { + if( m.furn( p ).obj().is_movable() && + m.furn( p ).obj().move_str_req < get_player_character().get_str() ) { if( m.furn( p ).obj().movecost == 0 ) { // Obstacles are better, prefer them furnitures1.push_back( p ); @@ -288,13 +289,13 @@ static int rate_location( map &m, const tripoint &p, const bool must_be_inside, void start_location::place_player( player &u ) const { // Need the "real" map with it's inside/outside cache and the like. - map &m = g->m; + map &here = get_map(); // Start us off somewhere in the center of the map u.setx( HALF_MAPSIZE_X ); u.sety( HALF_MAPSIZE_Y ); u.setz( g->get_levz() ); - m.invalidate_map_cache( m.get_abs_sub().z ); - m.build_map_cache( m.get_abs_sub().z ); + here.invalidate_map_cache( here.get_abs_sub().z ); + here.build_map_cache( here.get_abs_sub().z ); const bool must_be_inside = flags().count( "ALLOW_OUTSIDE" ) == 0; ///\EFFECT_STR allows player to start behind less-bashable furniture and terrain // TODO: Allow using items here @@ -315,7 +316,7 @@ void start_location::place_player( player &u ) const int tries = 0; const auto check_spot = [&]( const tripoint & pt ) { tries++; - const int rate = rate_location( m, pt, must_be_inside, bash, tries, checked ); + const int rate = rate_location( here, pt, must_be_inside, bash, tries, checked ); if( best_rate < rate ) { best_rate = rate; u.setpos( pt ); @@ -355,7 +356,8 @@ void start_location::burn( const tripoint &omtstart, const size_t count, const i tinymap m; m.load( player_location, false ); m.build_outside_cache( m.get_abs_sub().z ); - const point u( g->u.posx() % HALF_MAPSIZE_X, g->u.posy() % HALF_MAPSIZE_Y ); + point player_pos = get_player_character().pos().xy(); + const point u( player_pos.x % HALF_MAPSIZE_X, player_pos.y % HALF_MAPSIZE_Y ); std::vector valid; for( const tripoint &p : m.points_on_zlevel() ) { if( !( m.has_flag_ter( "DOOR", p ) || diff --git a/src/wish.cpp b/src/wish.cpp index 1c8691355e6ad..6ffd27f56f673 100644 --- a/src/wish.cpp +++ b/src/wish.cpp @@ -603,7 +603,7 @@ void debug_menu::wishitem( player *p, const tripoint &pos ) } p->invalidate_crafting_inventory(); } else if( pos.x >= 0 && pos.y >= 0 ) { - g->m.add_item_or_charges( pos, granted ); + get_map().add_item_or_charges( pos, granted ); wmenu.ret = -1; } if( amount > 0 ) { diff --git a/tests/active_item_cache_test.cpp b/tests/active_item_cache_test.cpp index 7b518b842b571..dcea4669b4912 100644 --- a/tests/active_item_cache_test.cpp +++ b/tests/active_item_cache_test.cpp @@ -3,7 +3,6 @@ #include "calendar.h" #include "catch/catch.hpp" -#include "game.h" #include "game_constants.h" #include "item.h" #include "map.h" @@ -13,14 +12,15 @@ TEST_CASE( "place_active_item_at_various_coordinates", "[item]" ) { clear_map(); + map &here = get_map(); for( int z = -OVERMAP_DEPTH; z < OVERMAP_HEIGHT; ++z ) { for( int x = 0; x < MAPSIZE_X; ++x ) { for( int y = 0; y < MAPSIZE_Y; ++y ) { - g->m.i_clear( { x, y, z } ); + here.i_clear( { x, y, z } ); } } } - REQUIRE( g->m.get_submaps_with_active_items().empty() ); + REQUIRE( here.get_submaps_with_active_items().empty() ); // An arbitrary active item. item active( "firecracker_act", 0, item::default_charges_tag() ); active.activate(); @@ -29,20 +29,20 @@ TEST_CASE( "place_active_item_at_various_coordinates", "[item]" ) int z = 0; for( int x = 0; x < MAPSIZE_X; ++x ) { for( int y = 0; y < MAPSIZE_Y; ++y ) { - REQUIRE( g->m.i_at( { x, y, z } ).empty() ); + REQUIRE( here.i_at( { x, y, z } ).empty() ); CAPTURE( x, y, z ); - tripoint abs_loc = g->m.get_abs_sub() + tripoint( x / SEEX, y / SEEY, z ); + tripoint abs_loc = here.get_abs_sub() + tripoint( x / SEEX, y / SEEY, z ); CAPTURE( abs_loc.x, abs_loc.y, abs_loc.z ); - REQUIRE( g->m.get_submaps_with_active_items().empty() ); - REQUIRE( g->m.get_submaps_with_active_items().find( abs_loc ) == - g->m.get_submaps_with_active_items().end() ); - item &item_ref = g->m.add_item( { x, y, z }, active ); + REQUIRE( here.get_submaps_with_active_items().empty() ); + REQUIRE( here.get_submaps_with_active_items().find( abs_loc ) == + here.get_submaps_with_active_items().end() ); + item &item_ref = here.add_item( { x, y, z }, active ); REQUIRE( item_ref.active ); - REQUIRE_FALSE( g->m.get_submaps_with_active_items().empty() ); - REQUIRE( g->m.get_submaps_with_active_items().find( abs_loc ) != - g->m.get_submaps_with_active_items().end() ); - REQUIRE_FALSE( g->m.i_at( { x, y, z } ).empty() ); - g->m.i_clear( { x, y, z } ); + REQUIRE_FALSE( here.get_submaps_with_active_items().empty() ); + REQUIRE( here.get_submaps_with_active_items().find( abs_loc ) != + here.get_submaps_with_active_items().end() ); + REQUIRE_FALSE( here.i_at( { x, y, z } ).empty() ); + here.i_clear( { x, y, z } ); } } } diff --git a/tests/memorial_test.cpp b/tests/memorial_test.cpp index 105451d3eb82d..0ce5628ed00e9 100644 --- a/tests/memorial_test.cpp +++ b/tests/memorial_test.cpp @@ -58,9 +58,10 @@ TEST_CASE( "memorials" ) event_bus &b = g->events(); - g->u.male = false; - character_id ch = g->u.getID(); - std::string u_name = g->u.name; + avatar &player_character = get_avatar(); + player_character.male = false; + character_id ch = player_character.getID(); + std::string u_name = player_character.name; character_id ch2 = character_id( ch.get_value() + 1 ); mutagen_technique mutagen = mutagen_technique::injected_purifier; mtype_id mon( "mon_zombie_kevlar_2" ); @@ -193,8 +194,8 @@ TEST_CASE( "memorials" ) std::chrono::seconds( 100 ) ); check_memorial( - m, b, u_name + " began their journey into the Cataclysm.", ch, u_name, g->u.male, - g->u.prof->ident(), g->u.custom_profession, "VERSION_STRING" ); + m, b, u_name + " began their journey into the Cataclysm.", ch, u_name, player_character.male, + player_character.prof->ident(), player_character.custom_profession, "VERSION_STRING" ); // Invokes achievement, so send another to clear the log for the test b.send( ch, cbm ); diff --git a/tests/player_helpers.cpp b/tests/player_helpers.cpp index df42e38bedb94..cba5ba4e15b89 100644 --- a/tests/player_helpers.cpp +++ b/tests/player_helpers.cpp @@ -26,7 +26,7 @@ int get_remaining_charges( const std::string &tool_id ) { - const inventory crafting_inv = g->u.crafting_inventory(); + const inventory crafting_inv = get_player_character().crafting_inventory(); std::vector items = crafting_inv.items_with( [tool_id]( const item & i ) { return i.typeId() == itype_id( tool_id ); @@ -40,7 +40,7 @@ int get_remaining_charges( const std::string &tool_id ) bool player_has_item_of_type( const std::string &type ) { - std::vector matching_items = g->u.inv.items_with( + std::vector matching_items = get_player_character().inv.items_with( [&]( const item & i ) { return i.type->get_id() == itype_id( type ); } ); @@ -113,7 +113,7 @@ void clear_character( player &dummy, bool debug_storage ) void clear_avatar() { - clear_character( g->u ); + clear_character( get_avatar() ); } void process_activity( player &dummy ) @@ -129,7 +129,7 @@ void process_activity( player &dummy ) npc &spawn_npc( const point &p, const std::string &npc_class ) { const string_id test_guy( npc_class ); - const character_id model_id = g->m.place_npc( p, test_guy, true ); + const character_id model_id = get_map().place_npc( p, test_guy, true ); g->load_npcs(); npc *guy = g->find_npc( model_id ); diff --git a/tests/vehicle_interact_test.cpp b/tests/vehicle_interact_test.cpp index 4caaaeb294134..93add711f3688 100644 --- a/tests/vehicle_interact_test.cpp +++ b/tests/vehicle_interact_test.cpp @@ -2,10 +2,9 @@ #include #include -#include "avatar.h" #include "calendar.h" #include "catch/catch.hpp" -#include "game.h" +#include "character.h" #include "inventory.h" #include "item.h" #include "map.h" @@ -23,15 +22,16 @@ static void test_repair( const std::vector &tools, bool expect_craftable ) clear_map(); const tripoint test_origin( 60, 60, 0 ); - g->u.setpos( test_origin ); + Character &player_character = get_player_character(); + player_character.setpos( test_origin ); const item backpack( "backpack" ); - g->u.wear( g->u.i_add( backpack ), false ); + player_character.wear_item( backpack ); for( const item &gear : tools ) { - g->u.i_add( gear ); + player_character.i_add( gear ); } const tripoint vehicle_origin = test_origin + tripoint_south_east; - vehicle *veh_ptr = g->m.add_vehicle( vproto_id( "bicycle" ), vehicle_origin, -90, 0, 0 ); + vehicle *veh_ptr = get_map().add_vehicle( vproto_id( "bicycle" ), vehicle_origin, -90, 0, 0 ); REQUIRE( veh_ptr != nullptr ); // Find the frame at the origin. vehicle_part *origin_frame = nullptr; @@ -51,10 +51,11 @@ static void test_repair( const std::vector &tools, bool expect_craftable ) requirement_data reqs = vp.repair_requirements(); // Bust cache on crafting_inventory() - g->u.mod_moves( 1 ); - inventory crafting_inv = g->u.crafting_inventory(); - bool can_repair = vp.repair_requirements().can_make_with_inventory( g->u.crafting_inventory(), - is_crafting_component ); + player_character.mod_moves( 1 ); + inventory crafting_inv = player_character.crafting_inventory(); + bool can_repair = vp.repair_requirements().can_make_with_inventory( + player_character.crafting_inventory(), + is_crafting_component ); CHECK( can_repair == expect_craftable ); } From 4db33b10f9951a0b7bb5386adbc9e0d4bd009fbb Mon Sep 17 00:00:00 2001 From: ZhilkinSerg Date: Fri, 3 Jul 2020 09:13:17 +0300 Subject: [PATCH 063/420] Add missing nullptr check --- src/monster.cpp | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/monster.cpp b/src/monster.cpp index f525d2d5a4442..0e2ea45eaf6b2 100644 --- a/src/monster.cpp +++ b/src/monster.cpp @@ -1046,16 +1046,18 @@ monster_attitude monster::attitude( const Character *u ) const if( has_effect( effect_docile ) ) { return MATT_FPASSIVE; } - if( u->is_avatar() ) { - return MATT_FRIEND; - } - if( u->is_npc() ) { - // Zombies don't understand not attacking NPCs, but dogs and bots should. - if( u->get_attitude() != NPCATT_KILL && !type->in_species( species_ZOMBIE ) ) { + if( u != nullptr ) { + if( u->is_avatar() ) { return MATT_FRIEND; } - if( u->is_hallucination() ) { - return MATT_IGNORE; + if( u->is_npc() ) { + // Zombies don't understand not attacking NPCs, but dogs and bots should. + if( u->get_attitude() != NPCATT_KILL && !type->in_species( species_ZOMBIE ) ) { + return MATT_FRIEND; + } + if( u->is_hallucination() ) { + return MATT_IGNORE; + } } } } From 04415841eb9e39289be0beea7c179bf3f33756f9 Mon Sep 17 00:00:00 2001 From: Brett Dong Date: Fri, 3 Jul 2020 14:47:15 +0800 Subject: [PATCH 064/420] Routine i18n updates on 3 Jul 2020 --- lang/po/cataclysm-dda.pot | 3570 ++++++++++++++++++++++++++++++------- lang/po/de.po | 54 +- lang/po/es_AR.po | 2293 +++++++++++++++++------- lang/po/es_ES.po | 54 +- lang/po/ja.po | 741 +++++--- lang/po/pl.po | 60 +- lang/po/ru.po | 74 +- lang/po/zh_CN.po | 900 ++++++---- lang/po/zh_TW.po | 61 +- 9 files changed, 5856 insertions(+), 1951 deletions(-) diff --git a/lang/po/cataclysm-dda.pot b/lang/po/cataclysm-dda.pot index 3863d4ff443f4..4fedf171aed03 100644 --- a/lang/po/cataclysm-dda.pot +++ b/lang/po/cataclysm-dda.pot @@ -2,7 +2,7 @@ msgid "" msgstr "" "Project-Id-Version: cataclysm-dda 0.E\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-06-26 12:50+0800\n" +"POT-Creation-Date: 2020-07-03 14:25+0800\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -827,6 +827,17 @@ msgstr[1] "" msgid "Mixture of oxygen and nitrogen in proportions suitable for diving." msgstr "" +#: lang/json/AMMO_from_json.py lang/json/ammunition_type_from_json.py +msgid "extinguishing agent" +msgid_plural "extinguishing agent" +msgstr[0] "" +msgstr[1] "" + +#. ~ Description for {'str_sp': 'extinguishing agent'} +#: lang/json/AMMO_from_json.py +msgid "Dry chemical solution effective in extinguishing fires." +msgstr "" + #: lang/json/AMMO_from_json.py lang/json/ammunition_type_from_json.py msgid "tinder" msgid_plural "tinder" @@ -4195,6 +4206,24 @@ msgid "" "with some kind of mask or mouth protection." msgstr "" +#: lang/json/AMMO_from_json.py +msgid "12.3ln round" +msgid_plural "12.3ln rounds" +msgstr[0] "" +msgstr[1] "" + +#. ~ Description for {'str': '12.3ln round'} +#: lang/json/AMMO_from_json.py +msgid "" +"The 12.3ln cartridge was introduced in Romania in the wake of the second " +"Carpathian conflict. The PA md. 71 rifle using this ammunition rapidly " +"gained popularity in the Eastern Union, and from there, the world. Due to " +"this, the 12.3ln rapidly became the standard combat round in the Eurasian " +"sphere. It was easily scavenged and stockpiled by the Exodii. To you, it " +"looks and feels quite similar to a .30-06 Springfield cartridge, but with a " +"slightly sharper taper." +msgstr "" + #: lang/json/AMMO_from_json.py #: lang/json/ammunition_type_from_json.py msgid "paper cartridge" @@ -5240,7 +5269,7 @@ msgstr[1] "" msgid "A can of yellow paint." msgstr "" -#: lang/json/AMMO_from_json.py lang/json/terrain_from_json.py +#: lang/json/AMMO_from_json.py msgid "red carpet" msgid_plural "red carpets" msgstr[0] "" @@ -7234,7 +7263,7 @@ msgstr[1] "" #: lang/json/ARMOR_from_json.py msgid "" "A small pouch that can be used to store most types of small ammunition, " -"rockets will not fit. Activate to store ammunition." +"rockets will not fit. Use insert to store ammunition." msgstr "" #: lang/json/ARMOR_from_json.py @@ -7330,8 +7359,8 @@ msgstr[1] "" #. ~ Description for {'str': 'quiver'} #: lang/json/ARMOR_from_json.py msgid "" -"A leather quiver worn at the waist that can hold 20 arrows. Activate to " -"store arrows." +"A leather quiver worn at the waist that can hold 20 arrows or bolts. Use " +"insert to store arrows or bolts." msgstr "" #: lang/json/ARMOR_from_json.py @@ -7344,7 +7373,7 @@ msgstr[1] "" #: lang/json/ARMOR_from_json.py msgid "" "A quiver woven from strips of birch bark, worn at the waist, that can hold " -"20 arrows. Activate to store arrows." +"20 arrows or bolts. Use insert to store arrows or bolts." msgstr "" #: lang/json/ARMOR_from_json.py @@ -7357,8 +7386,9 @@ msgstr[1] "" #: lang/json/ARMOR_from_json.py msgid "" "A large leather quiver trimmed with metal, worn on the back, that can hold " -"60 arrows. Historically used by horse archers, rather than foot archers, " -"but neither of THEM had to fight zombies. Activate to store arrows." +"60 arrows or bolts. Historically used by horse archers, rather than foot " +"archers, but neither of THEM had to fight zombies. Use insert to store " +"arrows or bolts." msgstr "" #: lang/json/ARMOR_from_json.py @@ -7371,7 +7401,7 @@ msgstr[1] "" #: lang/json/ARMOR_from_json.py msgid "" "A large quiver woven from strips of birchbark, worn on the back, that can " -"hold 60 arrows. Activate to store arrows." +"hold 60 arrows or bolts. Use insert to store arrows or bolts." msgstr "" #: lang/json/ARMOR_from_json.py @@ -17274,7 +17304,7 @@ msgstr "" #. ~ Description for {'str': 'suitcase'} #: lang/json/ARMOR_from_json.py msgid "" -"A mid-sized suitcase used mainly for transporting clothes and other " +"A mid-sized wheeled suitcase used mainly for transporting clothes and other " "possessions during trips, provides a decent amount of storage but hauling it " "around is not exactly comfortable." msgstr "" @@ -29389,6 +29419,18 @@ msgid "" "your pain at bay." msgstr "" +#: lang/json/BOOK_from_json.py +msgid "Scroll of Baleful Polymorph" +msgid_plural "Scrolls of Baleful Polymorph" +msgstr[0] "" +msgstr[1] "" + +#. ~ Description for {'str': 'Scroll of Baleful Polymorph', 'str_pl': 'Scrolls of Baleful Polymorph'} +#. ~ Description for Baleful Polymorph +#: lang/json/BOOK_from_json.py lang/json/SPELL_from_json.py +msgid "Transform your enemies into frogs." +msgstr "" + #: lang/json/BOOK_from_json.py msgid "Scroll of Summon Zombie" msgid_plural "Scrolls of Summon Zombie" @@ -30893,6 +30935,20 @@ msgid "" "on combining magic with EM radiation." msgstr "" +#: lang/json/BOOK_from_json.py +msgid "Runic Tablet shard" +msgid_plural "Runic Tablet shards" +msgstr[0] "" +msgstr[1] "" + +#. ~ Description for {'str': 'Runic Tablet shard'} +#: lang/json/BOOK_from_json.py +msgid "" +"A small tablet of blackened stone, apparently cut from a much larger slab. " +"Golden runes glow over its surface, and slowly shift into intelligible " +"sentences when you stare at them." +msgstr "" + #: lang/json/BOOK_from_json.py msgid "Geospatial Systems: The Lie Of Linearity" msgid_plural "copies of Geospatial Systems: The Lie Of Linearity" @@ -36188,6 +36244,32 @@ msgid "" "cheese. Delicious." msgstr "" +#: lang/json/COMESTIBLE_from_json.py +msgid "vegetarian nachos" +msgid_plural "vegetarian nachoss" +msgstr[0] "" +msgstr[1] "" + +#. ~ Description for vegetarian nachos +#: lang/json/COMESTIBLE_from_json.py +msgid "" +"Salted chips made from corn tortillas, now with beans. Could probably use " +"some cheese, though." +msgstr "" + +#: lang/json/COMESTIBLE_from_json.py +msgid "vegetarian nachos with cheese" +msgid_plural "vegetarian nachos with cheeses" +msgstr[0] "" +msgstr[1] "" + +#. ~ Description for vegetarian nachos with cheese +#: lang/json/COMESTIBLE_from_json.py +msgid "" +"Salted chips made from corn tortillas with beans and smothered in cheese. " +"Delicious, even if you're not a vegetarian." +msgstr "" + #: lang/json/COMESTIBLE_from_json.py msgid "pork stick" msgid_plural "pork sticks" @@ -40955,25 +41037,55 @@ msgid "" msgstr "" #: lang/json/COMESTIBLE_from_json.py -msgid "dog food" -msgid_plural "dog food" +msgid "wet dog food" +msgid_plural "wet dog food" msgstr[0] "" msgstr[1] "" -#. ~ Description for {'str_sp': 'dog food'} +#. ~ Description for {'str_sp': 'wet dog food'} #: lang/json/COMESTIBLE_from_json.py -msgid "This is food for dogs. It smells strange, but dogs seem to love it." +msgid "" +"This is wet food for dogs, made from canned fresh meats. It smells strange, " +"but dogs seem to love it." +msgstr "" + +#: lang/json/COMESTIBLE_from_json.py +msgid "dry dog food" +msgid_plural "dry dog food" +msgstr[0] "" +msgstr[1] "" + +#. ~ Description for {'str_sp': 'dry dog food'} +#: lang/json/COMESTIBLE_from_json.py +msgid "" +"Dry morsels of dog food with a long shelf life. Made from dried processed " +"meats and grains, and enriched with vitamins and minerals." msgstr "" #: lang/json/COMESTIBLE_from_json.py -msgid "cat food" -msgid_plural "cat food" +msgid "wet cat food" +msgid_plural "wet cat food" msgstr[0] "" msgstr[1] "" -#. ~ Description for {'str_sp': 'cat food'} +#. ~ Description for {'str_sp': 'wet cat food'} #: lang/json/COMESTIBLE_from_json.py -msgid "This is food for cats. It smells strange, but cats seem to love it." +msgid "" +"This is wet food for cats, made from canned fresh meats. It has a pungent " +"aroma that cats seem to love." +msgstr "" + +#: lang/json/COMESTIBLE_from_json.py +msgid "dry cat food" +msgid_plural "dry cat food" +msgstr[0] "" +msgstr[1] "" + +#. ~ Description for {'str_sp': 'dry cat food'} +#: lang/json/COMESTIBLE_from_json.py +msgid "" +"Dry kibbles of cat food with a long shelf life. Made from dried processed " +"meats and grains, and enriched with vitamins and minerals." msgstr "" #: lang/json/COMESTIBLE_from_json.py lang/json/terrain_from_json.py @@ -51842,6 +51954,58 @@ msgid "" "wound." msgstr "" +#: lang/json/GENERIC_from_json.py +msgid "broken exodii worker" +msgid_plural "broken exodii workers" +msgstr[0] "" +msgstr[1] "" + +#. ~ Description for broken exodii worker +#: lang/json/GENERIC_from_json.py +msgid "A broken exodii worker. It's possible it could be gutted for parts." +msgstr "" + +#: lang/json/GENERIC_from_json.py +msgid "broken exodii quadruped" +msgid_plural "broken exodii quadrupeds" +msgstr[0] "" +msgstr[1] "" + +#. ~ Description for broken exodii quadruped +#: lang/json/GENERIC_from_json.py +msgid "" +"A broken exodii walker. Still looks intimidating despite being permanently " +"inoperative, possibly due to the sheer size and mass. Could be gutted for " +"parts." +msgstr "" + +#: lang/json/GENERIC_from_json.py +msgid "broken exodii turret" +msgid_plural "broken exodii turrets" +msgstr[0] "" +msgstr[1] "" + +#. ~ Description for broken exodii turret +#: lang/json/GENERIC_from_json.py +msgid "" +"A broken exodii turret. Still looks intimidating despite being permanently " +"inoperative, possibly due to the sheer size and mass. Could be gutted for " +"parts." +msgstr "" + +#: lang/json/GENERIC_from_json.py +msgid "broken exodii balloon-drone" +msgid_plural "broken exodii balloon-drones" +msgstr[0] "" +msgstr[1] "" + +#. ~ Description for broken exodii balloon-drone +#: lang/json/GENERIC_from_json.py +msgid "" +"A broken balloon drone. The balloon has been shredded, but most of the " +"chassis is still intact. Could be gutted for parts." +msgstr "" + #: lang/json/GENERIC_from_json.py msgid "ammo belt linkage" msgid_plural "ammo belt linkages" @@ -53629,7 +53793,7 @@ msgstr[1] "" #: lang/json/GENERIC_from_json.py msgid "" "A typical microphone used to record or amplify voice. Comes with a clip. " -"Has a 3-pin XLR connector on the bottom in order to connect to am amp." +"Has a 3-pin XLR connector on the bottom in order to connect to an amp." msgstr "" #: lang/json/GENERIC_from_json.py @@ -53813,6 +53977,19 @@ msgid "" "anything on its own." msgstr "" +#: lang/json/GENERIC_from_json.py +msgid "set of pipe fittings" +msgid_plural "sets of pipe fittings" +msgstr[0] "" +msgstr[1] "" + +#. ~ Description for {'str': 'set of pipe fittings', 'str_pl': 'sets of pipe fittings'} +#: lang/json/GENERIC_from_json.py +msgid "" +"A loose assortment of metal pipe fittings - end caps, pipe junctions, and " +"similar items. They can be used in a variety of projects." +msgstr "" + #: lang/json/GENERIC_from_json.py msgid "short cordage piece" msgid_plural "short cordage pieces" @@ -55673,6 +55850,109 @@ msgid "" "somewhat warm to the touch." msgstr "" +#: lang/json/GENERIC_from_json.py +msgid "Exodii chassis" +msgid_plural "Exodii chassis" +msgstr[0] "" +msgstr[1] "" + +#. ~ Description for {'str_sp': 'Exodii chassis'} +#: lang/json/GENERIC_from_json.py +msgid "" +"This roughly hexagonal frame and associated bodywork looks like it was " +"constructed as a single monolithic piece. The fitting holes and attachments " +"are extremely durable, despite showing signs of heavy wear and repair. The " +"structure is versatile, and could probably be engineered to serve a number " +"of different heavy combat roles." +msgstr "" + +#: lang/json/GENERIC_from_json.py +msgid "Exodii drone chassis" +msgid_plural "Exodii drone chassis" +msgstr[0] "" +msgstr[1] "" + +#. ~ Description for {'str_sp': 'Exodii drone chassis'} +#: lang/json/GENERIC_from_json.py +msgid "" +"This small, roughly hexagonal frame and associated bodywork looks like it " +"was constructed as a single monolithic piece. The fitting holes and " +"attachments are extremely durable, despite showing signs of heavy wear and " +"repair. The structure is versatile, and could probably be engineered to " +"serve a number of different heavy combat roles." +msgstr "" + +#: lang/json/GENERIC_from_json.py +msgid "cybernetic neural matrix" +msgid_plural "cybernetic neural matrices" +msgstr[0] "" +msgstr[1] "" + +#. ~ Description for {'str': 'cybernetic neural matrix', 'str_pl': 'cybernetic neural matrices'} +#: lang/json/GENERIC_from_json.py +msgid "" +"A series of tanks and tubes with ports for fluids, electricity, and input " +"and output, this complex arrangement is made to house a brain and spine and " +"the most difficult to replace organs for keeping them alive." +msgstr "" + +#: lang/json/GENERIC_from_json.py +msgid "unfamiliar electronic thingy" +msgid_plural "unfamiliar electronic thingys" +msgstr[0] "" +msgstr[1] "" + +#. ~ Description for {'str': 'unfamiliar electronic thingy'} +#: lang/json/GENERIC_from_json.py +msgid "" +"The wiring and general shape suggest to you that this is a computer, or at " +"least some sort of electronic device, but what it is and what role it serves " +"is lost on you. It's heavy and sturdy in construction." +msgstr "" + +#: lang/json/GENERIC_from_json.py +msgid "inscribed metal plates" +msgid_plural "inscribed metal platess" +msgstr[0] "" +msgstr[1] "" + +#. ~ Description for {'str': 'inscribed metal plates'} +#: lang/json/GENERIC_from_json.py +msgid "" +"This device looks electronic, but is unfamiliar. It is a series of tightly " +"fitted coppery-looking rings, set concentrically. Wires run from each ring " +"to an axis in the middle." +msgstr "" + +#: lang/json/GENERIC_from_json.py +msgid "cybernetic sensor" +msgid_plural "cybernetic sensors" +msgstr[0] "" +msgstr[1] "" + +#. ~ Description for {'str': 'cybernetic sensor'} +#: lang/json/GENERIC_from_json.py +msgid "" +"From the large glassy eye - the size of a small dinner plate - in the front, " +"you deduce this is some sort of camera. None of the inner workings make any " +"sense to you nor resemble any camera you've seen before." +msgstr "" + +#: lang/json/GENERIC_from_json.py +msgid "rotary device" +msgid_plural "rotary devices" +msgstr[0] "" +msgstr[1] "" + +#. ~ Description for {'str': 'rotary device'} +#: lang/json/GENERIC_from_json.py +msgid "" +"You assume from the coils of coppery wire and the protruding piston that " +"this is some sort of motor or generator, but the design doesn't look similar " +"to anything you've seen before, and you can't figure out how to get it to " +"work." +msgstr "" + #: lang/json/GENERIC_from_json.py msgid "sheet of glass" msgid_plural "sheets of glass" @@ -64608,6 +64888,34 @@ msgid "" "thrower." msgstr "" +#: lang/json/MAGAZINE_from_json.py +msgid "PA Md. 71 Exodii 12.3ln magazine" +msgid_plural "PA Md. 71 Exodii 12.3ln magazines" +msgstr[0] "" +msgstr[1] "" + +#. ~ Description for {'str': 'PA Md. 71 Exodii 12.3ln magazine'} +#: lang/json/MAGAZINE_from_json.py +msgid "" +"A small, sleek magazine based on a classic PA Md. 71 clip, with some " +"redesigns by the Exodii for better use in lighter-than-air drones. Its " +"small size is less appropriate for ground-fighting of hordes of zombies, as " +"it is a bit inconvenient to reload." +msgstr "" + +#: lang/json/MAGAZINE_from_json.py +msgid "PA Md. 68 Exodii 12.3ln magazine" +msgid_plural "PA Md. 68 Exodii 12.3ln magazines" +msgstr[0] "" +msgstr[1] "" + +#. ~ Description for {'str': 'PA Md. 68 Exodii 12.3ln magazine'} +#: lang/json/MAGAZINE_from_json.py +msgid "" +"An unreasonably large magazine for the already heavy PA Md. 68 battle rifle, " +"custom designed and manufactured by the Exodii." +msgstr "" + #: lang/json/MAGAZINE_from_json.py msgid "pressurized fuel tank" msgid_plural "pressurized fuel tanks" @@ -65622,6 +65930,49 @@ msgid "" "able to give them back some humanity. If only they cared…" msgstr "" +#: lang/json/MONSTER_from_json.py +msgid "Exodii worker" +msgid_plural "Exodii workers" +msgstr[0] "" +msgstr[1] "" + +#. ~ Description for Exodii worker +#: lang/json/MONSTER_from_json.py +msgid "" +"This is a mostly humanoid robot equipped with various construction tools." +msgstr "" + +#: lang/json/MONSTER_from_json.py +msgid "Exodii quadruped" +msgid_plural "Exodii quadrupeds" +msgstr[0] "" +msgstr[1] "" + +#. ~ Description for Exodii quadruped +#: lang/json/MONSTER_from_json.py +msgid "" +"This enormous quadrupedal robot seems to be cobbled together from parts, " +"most of them unfamiliar to you. It moves with a heavy, oddly graceful gait, " +"its footsteps leaving shallow craters behind. It bristles with an arsenal " +"of weaponry, but doesn't seem in a particular rush to target you." +msgstr "" + +#: lang/json/MONSTER_from_json.py +msgid "zomborg" +msgid_plural "zomborgs" +msgstr[0] "" +msgstr[1] "" + +#. ~ Description for zomborg +#: lang/json/MONSTER_from_json.py +msgid "" +"A mix of dead human and even deader technology, this twisted mess of steel " +"and flesh moves like a puppet in the hands of an angry toddler. Its robotic " +"components seem to have shut down, and new bands of flesh have wrapped " +"around them, tugging and pulling them in awkward directions. Bits of " +"metallic skeleton and armor plating jut from its decaying flesh." +msgstr "" + #: lang/json/MONSTER_from_json.py msgid "police bot" msgid_plural "police bots" @@ -65849,6 +66200,23 @@ msgid "" "appears to have a mininuke inside. If this is targeting you… Run." msgstr "" +#: lang/json/MONSTER_from_json.py +msgid "balloon sniper-drone" +msgid_plural "balloon sniper-drones" +msgstr[0] "" +msgstr[1] "" + +#. ~ Description for {'str': 'balloon sniper-drone'} +#: lang/json/MONSTER_from_json.py +msgid "" +"This unusual contraption looks like a combination of a weather balloon and a " +"quadcopter. Beneath the crude box containing its components hangs a small " +"articulated rig wielding an integrated rifle. Its propellers flicker to " +"life briefly, then shut down again, keeping it mostly stationary despite the " +"air currents. It looks capable of hanging in the air for quite a long time " +"before running out of power." +msgstr "" + #: lang/json/MONSTER_from_json.py msgid "alpha razorclaw" msgid_plural "alpha razorclaws" @@ -67684,6 +68052,21 @@ msgid "" "delivering the finishing blow with its enormous fangs." msgstr "" +#: lang/json/MONSTER_from_json.py +msgid "tiger" +msgid_plural "tigers" +msgstr[0] "" +msgstr[1] "" + +#. ~ Description for {'str': 'tiger'} +#: lang/json/MONSTER_from_json.py +msgid "" +"The majestic tiger, a large feline predator. Native to Asia, they are now " +"most populous in private reserves in the United States. Fast and powerful, " +"this predator is one of the most recognizable and beloved animals in the " +"world. Also one of the deadliest." +msgstr "" + #: lang/json/MONSTER_from_json.py msgid "calf" msgid_plural "calves" @@ -69829,6 +70212,20 @@ msgstr[1] "" msgid "High-powered loudspeaker, repeating loud messages over and over again." msgstr "" +#: lang/json/MONSTER_from_json.py +msgid "upcycled turret" +msgid_plural "upcycled turrets" +msgstr[0] "" +msgstr[1] "" + +#. ~ Description for upcycled turret +#: lang/json/MONSTER_from_json.py +msgid "" +"This hefty turret appears to be bolted together out of various scraps of " +"technology, many of them extremely foreign looking. It is equipped with a " +"hefty looking machine gun." +msgstr "" + #: lang/json/MONSTER_from_json.py msgid "eyebot" msgid_plural "eyebots" @@ -70153,6 +70550,32 @@ msgid "" "and its eyes bulge with black goo." msgstr "" +#: lang/json/MONSTER_from_json.py +msgid "Tiger wight" +msgid_plural "Tiger wights" +msgstr[0] "" +msgstr[1] "" + +#. ~ Description for {'str': 'Tiger wight'} +#: lang/json/MONSTER_from_json.py +msgid "" +"This otherwise normal looking tiger stumbles and sways, its jaws slack, its " +"eyes wide open and shining black." +msgstr "" + +#: lang/json/MONSTER_from_json.py +msgid "mass of zombie spiders" +msgid_plural "mass of zombie spiderss" +msgstr[0] "" +msgstr[1] "" + +#. ~ Description for {'str': 'mass of zombie spiders'} +#: lang/json/MONSTER_from_json.py +msgid "" +"Thousands, maybe millions of spiders piling up high, each slowly oozing " +"sticky green pus, struggling to keep the fetid mass together and moving." +msgstr "" + #: lang/json/MONSTER_from_json.py msgid "scarred zombie" msgid_plural "scarred zombies" @@ -73650,6 +74073,19 @@ msgid "" "off creatures that disturb it." msgstr "" +#: lang/json/MONSTER_from_json.py +msgid "frog" +msgid_plural "frogs" +msgstr[0] "" +msgstr[1] "" + +#. ~ Description for {'str': 'frog'} +#: lang/json/MONSTER_from_json.py +msgid "" +"A conspicuously average american bullfrog. You better keep prince charming " +"away from it." +msgstr "" + #: lang/json/MONSTER_from_json.py msgid "lemure" msgid_plural "lemures" @@ -74299,6 +74735,14 @@ msgstr "" msgid "a bird" msgstr "" +#: lang/json/SPECIES_from_json.py +msgid "an alien cyborg" +msgstr "" + +#: lang/json/SPECIES_from_json.py +msgid "heavy thuds." +msgstr "" + #: lang/json/SPECIES_from_json.py msgid "a reptile" msgstr "" @@ -75173,6 +75617,17 @@ msgid "" "rune as a catalyst for recipes." msgstr "" +#: lang/json/SPELL_from_json.py +msgid "Soulrend" +msgstr "" + +#. ~ Description for Soulrend +#: lang/json/SPELL_from_json.py +msgid "" +"Violently tears the spirit from the body, and bounds the resulting shade to " +"your will." +msgstr "" + #: lang/json/SPELL_from_json.py msgid "Ignus Fatuus" msgstr "" @@ -75395,6 +75850,15 @@ msgstr "" msgid "Uses a little fatigue" msgstr "" +#: lang/json/SPELL_from_json.py +msgid "Debug polymorph" +msgstr "" + +#. ~ Description for Debug polymorph +#: lang/json/SPELL_from_json.py +msgid "Well you wanted to lose weight, right?" +msgstr "" + #: lang/json/SPELL_from_json.py msgid "Debug HP Spell" msgstr "" @@ -75821,6 +76285,10 @@ msgstr "" msgid "Haste" msgstr "" +#: lang/json/SPELL_from_json.py +msgid "Baleful Polymorph" +msgstr "" + #: lang/json/SPELL_from_json.py msgid "Mana Beam" msgstr "" @@ -79478,6 +79946,19 @@ msgid_plural "bionic firestarters" msgstr[0] "" msgstr[1] "" +#: lang/json/TOOL_from_json.py lang/json/furniture_from_json.py +msgid "smoking rack" +msgid_plural "smoking racks" +msgstr[0] "" +msgstr[1] "" + +#. ~ Description for {'str': 'smoking rack'} +#. ~ Description for {'str': 'pseudo butter churn'} +#. ~ Description for {'str': 'pseudo atomic butter churn'} +#: lang/json/TOOL_from_json.py +msgid "This is a crafting_pseudo_item if you have it something is wrong." +msgstr "" + #: lang/json/TOOL_from_json.py msgid "cash card" msgid_plural "cash cards" @@ -79549,7 +80030,7 @@ msgstr[1] "" #. ~ Use action menu_text for {'str': 'candle'}. #. ~ Use action menu_text for {'str': 'Louisville Slaughterer'}. #: lang/json/TOOL_from_json.py -#: src/veh_interact.cpp +#: src/activity_actor.cpp src/veh_interact.cpp msgid "Light" msgstr "" @@ -82066,12 +82547,6 @@ msgid_plural "pseudo butter churns" msgstr[0] "" msgstr[1] "" -#. ~ Description for {'str': 'pseudo butter churn'} -#. ~ Description for {'str': 'pseudo atomic butter churn'} -#: lang/json/TOOL_from_json.py -msgid "This is a crafting_pseudo_item if you have it something is wrong." -msgstr "" - #: lang/json/TOOL_from_json.py msgid "electric carver (off)" msgid_plural "electric carvers (off)" @@ -84299,12 +84774,36 @@ msgid_plural "throwable fire extinguishers" msgstr[0] "" msgstr[1] "" +#. ~ Use action menu_text for {'str': 'throwable fire extinguisher'}. +#: lang/json/TOOL_from_json.py +msgid "Pull plug" +msgstr "" + +#. ~ Use action msg for {'str': 'throwable fire extinguisher'}. +#: lang/json/TOOL_from_json.py +msgid "You pull the plug on the extinguisher grenade." +msgstr "" + #. ~ Description for {'str': 'throwable fire extinguisher'} #: lang/json/TOOL_from_json.py msgid "" "This is a fire extinguisher in grenade form. While not as effective as a " -"regular fire extinguisher, you can use it from a distance. It is activated " -"by heat, so just throw it into the flames." +"regular fire extinguisher, you can use it from a distance. It has a plastic " +"plug that can be pulled, but is primarely activated by heat, so just throw " +"it into the flames." +msgstr "" + +#: lang/json/TOOL_from_json.py +msgid "active throwable fire extinguisher" +msgid_plural "active throwable fire extinguishers" +msgstr[0] "" +msgstr[1] "" + +#. ~ Description for {'str': 'active throwable fire extinguisher'} +#: lang/json/TOOL_from_json.py +msgid "" +"This is an active extinguisher grenade, likely to burst any second now. " +"Better throw it!" msgstr "" #: lang/json/TOOL_from_json.py @@ -90592,7 +91091,7 @@ msgid "Pheidippides was a hack" msgstr "" #: lang/json/achievement_from_json.py -msgid "Run a marathon…plus a little bit more." +msgid "Run a marathon… plus a little bit more." msgstr "" #: lang/json/achievement_from_json.py @@ -90659,6 +91158,592 @@ msgstr "" msgid "Return to the location you started the game" msgstr "" +#: lang/json/achievement_from_json.py +msgid "Timber" +msgstr "" + +#: lang/json/achievement_from_json.py +msgid "" +"If a tree falls in a forest and no one is around to hear it, does it make a " +"sound?" +msgstr "" + +#: lang/json/achievement_from_json.py lang/json/npc_from_json.py +msgid "Lumberjack" +msgstr "" + +#: lang/json/achievement_from_json.py +msgid "What is a forest for a man with an axe?" +msgstr "" + +#: lang/json/achievement_from_json.py +msgid "Deforestation" +msgstr "" + +#: lang/json/achievement_from_json.py +msgid "If you cut down the trees you will find the wolf." +msgstr "" + +#: lang/json/achievement_from_json.py +msgid "Grave Digger" +msgstr "" + +#: lang/json/achievement_from_json.py +msgid "That's exactly what we need: more dead bodies." +msgstr "" + +#: lang/json/achievement_from_json.py +msgid "Grave Robber" +msgstr "" + +#: lang/json/achievement_from_json.py +msgid "Hey, what if they turned down there? You've gotta check." +msgstr "" + +#: lang/json/achievement_from_json.py +msgid "Funeral" +msgstr "" + +#: lang/json/achievement_from_json.py +msgid "It's a privilege to be buried when billions will not be." +msgstr "" + +#: lang/json/achievement_from_json.py +msgid "Undertaker" +msgstr "" + +#: lang/json/achievement_from_json.py +msgid "Leave no one to rot among the living dead." +msgstr "" + +#: lang/json/achievement_from_json.py +msgid "Funeral House" +msgstr "" + +#: lang/json/achievement_from_json.py +msgid "You cannot bury the whole world, can you?" +msgstr "" + +#: lang/json/achievement_from_json.py +msgid "Cyberpunk" +msgstr "" + +#: lang/json/achievement_from_json.py +msgid "Spiritus quidem promptus; caro vero infirma." +msgstr "" + +#: lang/json/achievement_from_json.py +msgid "Clockwork Man" +msgstr "" + +#: lang/json/achievement_from_json.py +msgid "" +"By most mechanical and dirty hand. I shall have such revenges on you… " +"both. The things I will do, what they are, yet I know not. But they will " +"be the terrors of the earth." +msgstr "" + +#: lang/json/achievement_from_json.py +msgid "Homo Evolutis" +msgstr "" + +#: lang/json/achievement_from_json.py +msgid "World of man has ended. Long live the world of transhumanism." +msgstr "" + +#: lang/json/achievement_from_json.py +msgid "Broken But Not Defeated" +msgstr "" + +#: lang/json/achievement_from_json.py +msgid "Does your medical insurance cover that?" +msgstr "" + +#: lang/json/achievement_from_json.py +msgid "Free Trader" +msgstr "" + +#: lang/json/achievement_from_json.py +msgid "Extraordinary gizmos for obscenely low prices!" +msgstr "" + +#: lang/json/achievement_from_json.py +msgid "Cut-Me-Own-Throat Dibbler" +msgstr "" + +#: lang/json/achievement_from_json.py +msgid "" +"My Innuit friend, I'm selling you this ice for such a low price, that it's " +"cutting me own throat." +msgstr "" + +#: lang/json/achievement_from_json.py +msgid "Eloquent" +msgstr "" + +#: lang/json/achievement_from_json.py +msgid "We're frends, aren't we?" +msgstr "" + +#: lang/json/achievement_from_json.py +msgid "Silver Tongue" +msgstr "" + +#: lang/json/achievement_from_json.py +msgid "Legend has it that you convinced a zombie hulk to go away." +msgstr "" + +#: lang/json/achievement_from_json.py +msgid "HackerMan" +msgstr "" + +#: lang/json/achievement_from_json.py +msgid "This OS has a back door. There is always a back door." +msgstr "" + +#: lang/json/achievement_from_json.py +msgid "Still not quite like Kevin" +msgstr "" + +#: lang/json/achievement_from_json.py +msgid "It's not cheating. It's debugging." +msgstr "" + +#: lang/json/achievement_from_json.py lang/json/mutation_from_json.py +msgid "MD" +msgstr "" + +#: lang/json/achievement_from_json.py +msgid "Is there a doctor in the house?" +msgstr "" + +#: lang/json/achievement_from_json.py +msgid "Dr House" +msgstr "" + +#: lang/json/achievement_from_json.py +msgid "It's lupus." +msgstr "" + +#: lang/json/achievement_from_json.py +msgid "Engineer" +msgstr "" + +#: lang/json/achievement_from_json.py +msgid "Just give me my wrench." +msgstr "" + +#: lang/json/achievement_from_json.py +msgid "MacGyver" +msgstr "" + +#: lang/json/achievement_from_json.py +msgid "This whole deal is holding on faith, spit and duct tape." +msgstr "" + +#: lang/json/achievement_from_json.py +msgid "Trapper" +msgstr "" + +#: lang/json/achievement_from_json.py +msgid "A good trap doesn't discriminate between beavers and zombeavers." +msgstr "" + +#: lang/json/achievement_from_json.py src/iuse.cpp +#: src/iuse_software_minesweeper.cpp +msgid "Minesweeper" +msgstr "" + +#: lang/json/achievement_from_json.py +msgid "All it takes is one mistake." +msgstr "" + +#: lang/json/achievement_from_json.py +msgid "Ace Driver" +msgstr "" + +#: lang/json/achievement_from_json.py +msgid "No turn is too sharp." +msgstr "" + +#: lang/json/achievement_from_json.py +msgid "The Stig" +msgstr "" + +#: lang/json/achievement_from_json.py +msgid "Formula One is for Sunday drivers." +msgstr "" + +#: lang/json/achievement_from_json.py +msgid "Swimmer" +msgstr "" + +#: lang/json/achievement_from_json.py +msgid "Like a fish to water." +msgstr "" + +#: lang/json/achievement_from_json.py +msgid "Michael Phelps" +msgstr "" + +#: lang/json/achievement_from_json.py +msgid "Faster then Jaws." +msgstr "" + +#: lang/json/achievement_from_json.py +msgid "Do-It-Yourselfer" +msgstr "" + +#: lang/json/achievement_from_json.py +msgid "Take this thing, put it in that thing, and voila." +msgstr "" + +#: lang/json/achievement_from_json.py +msgid "Jack of All Trades" +msgstr "" + +#: lang/json/achievement_from_json.py +msgid "With a right ammount of glue, there is nothing I can't do." +msgstr "" + +#: lang/json/achievement_from_json.py +msgid "Master Chef" +msgstr "" + +#: lang/json/achievement_from_json.py +msgid "Glazed tenderloin is a cakewalk." +msgstr "" + +#: lang/json/achievement_from_json.py +msgid "Hell's Kitchen" +msgstr "" + +#: lang/json/achievement_from_json.py +msgid "Today's menu: Soupe a l'oignon, Boeuf Bourguignon and Creme brulee." +msgstr "" + +#: lang/json/achievement_from_json.py +msgid "Tailor" +msgstr "" + +#: lang/json/achievement_from_json.py +msgid "A needle, a thread and a dream." +msgstr "" + +#: lang/json/achievement_from_json.py +msgid "Fashion Designer" +msgstr "" + +#: lang/json/achievement_from_json.py +msgid "Male, feamale and mutant fashion alike." +msgstr "" + +#: lang/json/achievement_from_json.py +msgid "Survivalist" +msgstr "" + +#: lang/json/achievement_from_json.py +msgid "Survival is my game." +msgstr "" + +#: lang/json/achievement_from_json.py +msgid "Bear Grylls" +msgstr "" + +#: lang/json/achievement_from_json.py +msgid "So you say you can survive on your own urine?" +msgstr "" + +#: lang/json/achievement_from_json.py +msgid "Ohm's Law" +msgstr "" + +#: lang/json/achievement_from_json.py +msgid "Thunder Ohm. Two volts enter, one volt leaves. Resistance is futile." +msgstr "" + +#: lang/json/achievement_from_json.py +msgid "Nicola Tesla" +msgstr "" + +#: lang/json/achievement_from_json.py +msgid "One does not simply taste a 9V battery." +msgstr "" + +#: lang/json/achievement_from_json.py +msgid "Bull's Eye" +msgstr "" + +#: lang/json/achievement_from_json.py +msgid "Better then Legolas." +msgstr "" + +#: lang/json/achievement_from_json.py +msgid "Robin Hood" +msgstr "" + +#: lang/json/achievement_from_json.py +msgid "Wilhelm Tell? Never heard of." +msgstr "" + +#: lang/json/achievement_from_json.py +msgid "Eagle Eye" +msgstr "" + +#: lang/json/achievement_from_json.py +msgid "Only me and my target." +msgstr "" + +#: lang/json/achievement_from_json.py +msgid "Deadshot" +msgstr "" + +#: lang/json/achievement_from_json.py +msgid "Don't run. You'll die tired." +msgstr "" + +#: lang/json/achievement_from_json.py +msgid "Gunner" +msgstr "" + +#: lang/json/achievement_from_json.py +msgid "Caliber makes the difference." +msgstr "" + +#: lang/json/achievement_from_json.py +msgid "Rocket Man" +msgstr "" + +#: lang/json/achievement_from_json.py +msgid "I'm sending you to the moon. In pieces." +msgstr "" + +#: lang/json/achievement_from_json.py +msgid "Small But Deadly" +msgstr "" + +#: lang/json/achievement_from_json.py +msgid "Caliber doesn't count when you're on the recieving side of the barrel." +msgstr "" + +#: lang/json/achievement_from_json.py +msgid "Dirty Harry" +msgstr "" + +#: lang/json/achievement_from_json.py +msgid "" +"But being this is a .44 Magnum, the most powerful handgun in the world and " +"would blow your head clean off, you've gotta ask yourself one question: Do " +"I feel lucky? Well, do ya, punk?" +msgstr "" + +#: lang/json/achievement_from_json.py +msgid "Rifleman" +msgstr "" + +#: lang/json/achievement_from_json.py +msgid "" +"This is my rifle. There are many like it, but this one is mine. My rifle " +"is my best friend. It is my life. I must master it as I must master my " +"life." +msgstr "" + +#: lang/json/achievement_from_json.py lang/json/npc_class_from_json.py +#: lang/json/npc_class_from_json.py lang/json/npc_from_json.py +msgid "Soldier" +msgstr "" + +#: lang/json/achievement_from_json.py +msgid "" +"Without me, my rifle is useless. Without my rifle, I am useless. I will " +"keep my rifle clean and ready, even as I am clean and ready. We will become " +"part of each other." +msgstr "" + +#: lang/json/achievement_from_json.py +msgid "Double Barrel, Double Fun" +msgstr "" + +#: lang/json/achievement_from_json.py +msgid "When you want to hit your target nine times with one shot." +msgstr "" + +#: lang/json/achievement_from_json.py +msgid "Elmer Fudd" +msgstr "" + +#: lang/json/achievement_from_json.py +msgid "What's up doc? Hunting wabbits?" +msgstr "" + +#: lang/json/achievement_from_json.py +msgid "Spray'n'Pray" +msgstr "" + +#: lang/json/achievement_from_json.py +msgid "One will hit. It's a matter of statistics." +msgstr "" + +#: lang/json/achievement_from_json.py +msgid "SMG Goes BRRRT!" +msgstr "" + +#: lang/json/achievement_from_json.py +msgid "We definitely need more ammo." +msgstr "" + +#: lang/json/achievement_from_json.py +msgid "Yeet!" +msgstr "" + +#: lang/json/achievement_from_json.py +msgid "And never come back." +msgstr "" + +#: lang/json/achievement_from_json.py +msgid "Kobe Bryant" +msgstr "" + +#: lang/json/achievement_from_json.py +msgid "Frag out!" +msgstr "" + +#: lang/json/achievement_from_json.py +msgid "Brawler" +msgstr "" + +#: lang/json/achievement_from_json.py +msgid "Bottle in left hand, chair leg in right hand." +msgstr "" + +#: lang/json/achievement_from_json.py +msgid "Street Fighter" +msgstr "" + +#: lang/json/achievement_from_json.py +msgid "It's winning that matters, not the style." +msgstr "" + +#: lang/json/achievement_from_json.py +msgid "Batter" +msgstr "" + +#: lang/json/achievement_from_json.py +msgid "Every strike brings me closer to a home run." +msgstr "" + +#: lang/json/achievement_from_json.py +msgid "Stone Age" +msgstr "" + +#: lang/json/achievement_from_json.py +msgid "" +"Cudgel was humanity's first tool. And it may be it's last, so why not " +"master it?" +msgstr "" + +#: lang/json/achievement_from_json.py +msgid "Way of the Sword" +msgstr "" + +#: lang/json/achievement_from_json.py +msgid "" +"When the sword is once drawn, the passions of men observe no bounds of " +"moderation." +msgstr "" + +#: lang/json/achievement_from_json.py +msgid "Miyamoto Musashi" +msgstr "" + +#: lang/json/achievement_from_json.py +msgid "" +"The sword has to be more than a simple weapon; it has to be an answer to " +"life's questions." +msgstr "" + +#: lang/json/achievement_from_json.py +msgid "Elusive" +msgstr "" + +#: lang/json/achievement_from_json.py +msgid "A strongest of blows is nothing if it doesn't land." +msgstr "" + +#: lang/json/achievement_from_json.py +msgid "Neo" +msgstr "" + +#: lang/json/achievement_from_json.py +msgid "But can you dodge a bullet?" +msgstr "" + +#: lang/json/achievement_from_json.py +msgid "Cold Steel" +msgstr "" + +#: lang/json/achievement_from_json.py +msgid "While you were partying, I studied the blade." +msgstr "" + +#: lang/json/achievement_from_json.py +msgid "Jack the Ripper" +msgstr "" + +#: lang/json/achievement_from_json.py +msgid "" +"Is this a dagger which I see before me, the handle toward my hand? Come, " +"let me clutch thee." +msgstr "" + +#: lang/json/achievement_from_json.py +msgid "Road to Shaolin" +msgstr "" + +#: lang/json/achievement_from_json.py +msgid "I feel an army in my fist." +msgstr "" + +#: lang/json/achievement_from_json.py +msgid "Mr Miyagi" +msgstr "" + +#: lang/json/achievement_from_json.py +msgid "To be your own weapon." +msgstr "" + +#: lang/json/achievement_from_json.py +msgid "Burglar" +msgstr "" + +#: lang/json/achievement_from_json.py +msgid "Crowbar? Such a barbarity." +msgstr "" + +#: lang/json/achievement_from_json.py +msgid "Locksmith" +msgstr "" + +#: lang/json/achievement_from_json.py +msgid "If there is a lock, there is a key." +msgstr "" + +#: lang/json/achievement_from_json.py +msgid "Periodic Table" +msgstr "" + +#: lang/json/achievement_from_json.py +msgid "It's somewhat like cooking. Just don't lick the spoon." +msgstr "" + +#: lang/json/achievement_from_json.py +msgid "Heisenberg" +msgstr "" + +#: lang/json/achievement_from_json.py +msgid "You all know who I am. I'm the cook. Say my name." +msgstr "" + #: lang/json/achievement_from_json.py msgid "Would-be Wizard" msgstr "" @@ -91116,6 +92201,11 @@ msgstr "" msgid "canceling activity serialized with legacy code" msgstr "" +#: lang/json/activity_type_from_json.py +msgctxt "training" +msgid "working out" +msgstr "" + #: lang/json/ammunition_type_from_json.py msgid "fusion cell" msgstr "" @@ -91418,6 +92508,10 @@ msgstr "" msgid "compressed air" msgstr "" +#: lang/json/ammunition_type_from_json.py +msgid "12.3ln cartridge" +msgstr "" + #: lang/json/ammunition_type_from_json.py msgid "shotcanisters" msgstr "" @@ -93270,6 +94364,62 @@ msgstr "" msgid "Merciful" msgstr "" +#: lang/json/conduct_from_json.py +msgid "The Elven Path" +msgstr "" + +#: lang/json/conduct_from_json.py +msgid "Cut no trees" +msgstr "" + +#: lang/json/conduct_from_json.py +msgid "Homo Sapiens" +msgstr "" + +#: lang/json/conduct_from_json.py +msgid "Install no bionic implants" +msgstr "" + +#: lang/json/conduct_from_json.py +msgid "Install no faulty bionic implants" +msgstr "" + +#: lang/json/conduct_from_json.py +msgid "No mutations" +msgstr "" + +#: lang/json/conduct_from_json.py +msgid "Clean on X-ray" +msgstr "" + +#: lang/json/conduct_from_json.py +msgid "Pure Blood" +msgstr "" + +#: lang/json/conduct_from_json.py +msgid "Structural Integrity" +msgstr "" + +#: lang/json/conduct_from_json.py +msgid "Break no bones" +msgstr "" + +#: lang/json/conduct_from_json.py +msgid "Teacher, Leave Them Kids Alone" +msgstr "" + +#: lang/json/conduct_from_json.py +msgid "Gain no skill levels" +msgstr "" + +#: lang/json/conduct_from_json.py +msgid "Self-Imposed Illiteracy" +msgstr "" + +#: lang/json/conduct_from_json.py +msgid "Read no books" +msgstr "" + #: lang/json/construction_category_from_json.py src/advanced_inv.cpp #: src/armor_layers.cpp src/debug_menu.cpp src/options.cpp #: src/scenario.cpp @@ -93896,10 +95046,6 @@ msgstr "" msgid "Take Paint Off Wall" msgstr "" -#: lang/json/construction_from_json.py -msgid "Remove Carpet" -msgstr "" - #: lang/json/construction_from_json.py msgid "Carpet Floor Red" msgstr "" @@ -96902,7 +98048,7 @@ msgstr "" msgid "You smoked too much." msgstr "" -#: lang/json/effects_from_json.py +#: lang/json/effects_from_json.py src/activity_actor.cpp msgid "High" msgstr "" @@ -98880,6 +100026,18 @@ msgstr "" msgid "raging fire" msgstr "" +#: lang/json/field_type_from_json.py +msgid "extinguisher mist" +msgstr "" + +#: lang/json/field_type_from_json.py +msgid "extinguisher cloud" +msgstr "" + +#: lang/json/field_type_from_json.py +msgid "thick extinguisher cloud" +msgstr "" + #: lang/json/field_type_from_json.py msgid "legacy rubble" msgstr "" @@ -100723,9 +101881,8 @@ msgstr "" #: lang/json/furniture_from_json.py msgid "" "A heavy set of weightlifting equipment for strength training, with a pair of " -"heavy weights affixed to opposite ends of a sturdy pipe. The weights are " -"huge, and using them without a spotter would be a good way to seriously " -"injure yourself." +"heavy weights affixed to opposite ends of a sturdy pipe. You can adjust the " +"set by hand-picking the weights you wish to use." msgstr "" #: lang/json/furniture_from_json.py @@ -100814,6 +101971,18 @@ msgid "" "to be scavanged." msgstr "" +#: lang/json/furniture_from_json.py +msgid "mechanical ergometer" +msgstr "" + +#. ~ Description for mechanical ergometer +#: lang/json/furniture_from_json.py +msgid "" +"An exercise machine with a set of handles and plates meant to emulate rowing " +"a boat. This an older model with mechanical resistance adjustments, but it " +"works without power." +msgstr "" + #: lang/json/furniture_from_json.py msgid "treadmill" msgstr "" @@ -100826,6 +101995,18 @@ msgid "" "you're probably getting enough cardio on your own." msgstr "" +#: lang/json/furniture_from_json.py +msgid "gravity treadmill" +msgstr "" + +#. ~ Description for gravity treadmill +#: lang/json/furniture_from_json.py +msgid "" +"A gravity driven conveyor belt with a mechanical control panel for running " +"in place. Conveyor belt is positioned in a steep, but adjustable incline, " +"so it slides back under your weight." +msgstr "" + #: lang/json/furniture_from_json.py msgid "heavy punching bag" msgstr "" @@ -102092,10 +103273,6 @@ msgstr "" msgid "filled arc furnace" msgstr "" -#: lang/json/furniture_from_json.py -msgid "smoking rack" -msgstr "" - #. ~ Description for smoking rack #. ~ Description for metal smoking rack #: lang/json/furniture_from_json.py @@ -102838,6 +104015,7 @@ msgid "auto" msgstr "" #: lang/json/gun_from_json.py +#: lang/json/gun_from_json.py lang/json/gunmod_from_json.py #: lang/json/gunmod_from_json.py msgctxt "gun_type_type" msgid "rifle" @@ -103020,9 +104198,8 @@ msgid "" msgstr "" #: lang/json/gun_from_json.py -#: lang/json/gun_from_json.py lang/json/gunmod_from_json.py #: lang/json/gunmod_from_json.py -#: src/item.cpp +#: lang/json/gunmod_from_json.py src/item.cpp msgctxt "gun_type_type" msgid "pistol" msgstr "" @@ -103086,7 +104263,7 @@ msgid "" msgstr "" #: lang/json/gun_from_json.py -#: src/item_factory.cpp +#: lang/json/gun_from_json.py src/item_factory.cpp msgid "semi-auto" msgstr "" @@ -105832,15 +107009,15 @@ msgid "" msgstr "" #: lang/json/gun_from_json.py -msgid "CZ-75" -msgid_plural "CZ-75s" +msgid "CZ 75 B" +msgid_plural "CZ 75 Bs" msgstr[0] "" msgstr[1] "" #: lang/json/gun_from_json.py msgid "" -"The CZ-75 is a semi-automatic pistol developed in Czechoslovakia, and is one " -"of the original wonder nines. Though designed for export to western " +"The CZ 75 B is a semi-automatic pistol developed in Czechoslovakia, and is " +"one of the original wonder nines. Though designed for export to western " "countries, it was declared a state secret; lack of international patent " "protection meant that many clones and variants were produced and distributed " "around the world, with Česká zbrojovka only joining in the 90's. This " @@ -105953,6 +107130,39 @@ msgid "" "their egomaniac descendants in New England." msgstr "" +#: lang/json/gun_from_json.py +msgid "PA md. 68 Battle Rifle" +msgid_plural "PA md. 68 Battle Rifles" +msgstr[0] "" +msgstr[1] "" + +#: lang/json/gun_from_json.py +msgid "" +"The most popular gun to use the 12.3ln cartridge was, of course, the PA md. " +"71. Its predecessor, the md. 68, was viewed by many as a sort of failure: " +"although it was reliable and powerful, it was too heavy to be used as a good " +"infantry weapon, and not really heavy enough to be a good support gun. " +"Enough were made, though, that during the zombie apocalypse, it gained a " +"great deal of resurgent popularity as a light emplacement gun that used " +"readily available ammunition. It perfectly served the purposes of the " +"Exodii, who had far less concern about its unwieldiness." +msgstr "" + +#: lang/json/gun_from_json.py +msgid "PA md. 71 zombie hunting rifle" +msgid_plural "PA md. 71 zombie hunting rifles" +msgstr[0] "" +msgstr[1] "" + +#: lang/json/gun_from_json.py +msgid "" +"This extremely popular Romanian assault rifle, made famous in the third " +"Carpachian War, has been redesigned slightly by the Exodii to serve as a " +"sniper weapon. It is well suited to precision shots against high-priority " +"targets. This modified design fires an extremely rapid 5-shot burst, with " +"the goal of shredding the target and preventing revivification." +msgstr "" + #: lang/json/gun_from_json.py msgid "flamethrower" msgid_plural "flamethrowers" @@ -106460,8 +107670,8 @@ msgid "" msgstr "" #: lang/json/gun_from_json.py -msgid "makeshift shotgun" -msgid_plural "makeshift shotguns" +msgid "four winds shotgun" +msgid_plural "four winds shotguns" msgstr[0] "" msgstr[1] "" @@ -107937,12 +109147,6 @@ msgid "" "reliability." msgstr "" -#: lang/json/gun_from_json.py -msgid "slam-fire shotgun" -msgid_plural "slam-fire shotguns" -msgstr[0] "" -msgstr[1] "" - #: lang/json/gun_from_json.py msgid "Ichaival" msgid_plural "Ichaivals" @@ -109913,6 +111117,12 @@ msgid "" "experiment" msgstr "" +#: lang/json/harvest_from_json.py +msgid "" +"You search for any salvageable hardware in what's left of this flesh and " +"metal monster" +msgstr "" + #: lang/json/harvest_from_json.py msgid "" "You messily hack apart the hulking mass of fused, rancid flesh, taking note " @@ -111968,10 +113178,6 @@ msgstr "" msgid "Teleport yourself" msgstr "" -#: lang/json/item_action_from_json.py -msgid "Extinguish a fire" -msgstr "" - #: lang/json/item_action_from_json.py msgid "Dry/clean yourself" msgstr "" @@ -113208,6 +114414,14 @@ msgstr "" msgid "Toggle sorting order" msgstr "" +#: lang/json/keybinding_from_json.py +msgid "Randomize profession" +msgstr "" + +#: lang/json/keybinding_from_json.py +msgid "Randomize scenario" +msgstr "" + #: lang/json/keybinding_from_json.py msgid "Scroll description up" msgstr "" @@ -113608,6 +114822,10 @@ msgstr "" msgid "Sleep" msgstr "" +#: lang/json/keybinding_from_json.py +msgid "Workout" +msgstr "" + #: lang/json/keybinding_from_json.py msgid "Control Vehicle" msgstr "" @@ -119450,7 +120668,7 @@ msgstr "" msgid "There is always work to be done, song to be woven." msgstr "" -#: lang/json/mission_def_from_json.py +#: lang/json/mission_def_from_json.py lang/json/talk_topic_from_json.py msgid "" "If you wish to be set on the path to enlightenment, first you must learn to " "listen and hear the song. Go out, butcher a creature and feel the power " @@ -119458,16 +120676,16 @@ msgid "" "you." msgstr "" -#: lang/json/mission_def_from_json.py +#: lang/json/mission_def_from_json.py lang/json/talk_topic_from_json.py msgid "Excellent. Now be on your way." msgstr "" -#: lang/json/mission_def_from_json.py +#: lang/json/mission_def_from_json.py lang/json/talk_topic_from_json.py msgid "" "I understand your reluctancy. Feel free to return when you see the way." msgstr "" -#: lang/json/mission_def_from_json.py +#: lang/json/mission_def_from_json.py lang/json/talk_topic_from_json.py msgid "" "The shambling corpses we see all around move in discord. Their song can be " "used, but for an Acolyte, this would be needlessly hard. Be sure to carve " @@ -120661,7 +121879,7 @@ msgstr "" #: lang/json/mission_def_from_json.py msgid "" -"I'll see you then…or I won't, and then I'll know I made the right decision." +"I'll see you then… or I won't, and then I'll know I made the right decision." msgstr "" #: lang/json/mission_def_from_json.py @@ -120670,7 +121888,7 @@ msgid "" msgstr "" #: lang/json/mission_def_from_json.py -msgid "Well, you're not dead…yet." +msgid "Well, you're not dead… yet." msgstr "" #: lang/json/mission_def_from_json.py @@ -131752,7 +132970,7 @@ msgid "Well, maybe you'll just have to make your own world wide web." msgstr "" #: lang/json/mutation_from_json.py -#: lang/json/mutation_from_json.py lang/json/npc_from_json.py +#: lang/json/npc_from_json.py msgid "Survivor" msgstr "" @@ -132057,10 +133275,6 @@ msgid "" "are real and the only thing standing between this world and oblivion is you." msgstr "" -#: lang/json/mutation_from_json.py -msgid "MD" -msgstr "" - #. ~ Description for {'str': 'MD'} #: lang/json/mutation_from_json.py msgid "" @@ -132437,11 +133651,29 @@ msgid "" msgstr "" #: lang/json/mutation_from_json.py -msgid "Survivor Story" -msgstr "" - -#. ~ Description for {'str': 'Survivor Story'} -#. ~ Description for {'str': 'Survivor'} +msgid "Survivor: Confused 1" +msgstr "" + +#. ~ Description for {'str': 'Survivor: Confused 1'} +#. ~ Description for {'str': 'Survivor: No Past 1'} +#. ~ Description for {'str': 'Survivor: No Past 2'} +#. ~ Description for {'str': 'Survivor: No Past 3'} +#. ~ Description for {'str': 'Survivor: No Past 4'} +#. ~ Description for {'str': 'Survivor: No Past 5'} +#. ~ Description for {'str': 'Survivor: Religious 1'} +#. ~ Description for {'str': 'Survivor: Religious 2'} +#. ~ Description for {'str': 'Survivor: Dreamer 1'} +#. ~ Description for {'str': 'Survivor: Wedding 1'} +#. ~ Description for {'str': 'Survivor: Evacuee 1'} +#. ~ Description for {'str': 'Survivor: Evacuee 2'} +#. ~ Description for {'str': 'Survivor: Evacuee 3'} +#. ~ Description for {'str': 'Survivor: Evacuee 4'} +#. ~ Description for {'str': 'Survivor: Evacuee 5'} +#. ~ Description for {'str': 'Survivor: Evacuee 6'} +#. ~ Description for {'str': 'Survivor: FEMA Evacuee 1'} +#. ~ Description for {'str': 'Survivor: Left for Dead 1'} +#. ~ Description for {'str': 'Survivor: Left for Dead 2'} +#. ~ Description for {'str': 'Survivor: Left for Dead 3'} #. ~ Description for {'str': 'Survivor Story'} #. ~ Description for {'str': 'Survivor'} #. ~ Description for {'str': 'Survivor Story'} @@ -132449,6 +133681,86 @@ msgstr "" msgid "This NPC could tell you about how they survived the Cataclysm" msgstr "" +#: lang/json/mutation_from_json.py +msgid "Survivor: No Past 1" +msgstr "" + +#: lang/json/mutation_from_json.py +msgid "Survivor: No Past 2" +msgstr "" + +#: lang/json/mutation_from_json.py +msgid "Survivor: No Past 3" +msgstr "" + +#: lang/json/mutation_from_json.py +msgid "Survivor: No Past 4" +msgstr "" + +#: lang/json/mutation_from_json.py +msgid "Survivor: No Past 5" +msgstr "" + +#: lang/json/mutation_from_json.py +msgid "Survivor: Religious 1" +msgstr "" + +#: lang/json/mutation_from_json.py +msgid "Survivor: Religious 2" +msgstr "" + +#: lang/json/mutation_from_json.py +msgid "Survivor: Dreamer 1" +msgstr "" + +#: lang/json/mutation_from_json.py +msgid "Survivor: Wedding 1" +msgstr "" + +#: lang/json/mutation_from_json.py +msgid "Survivor: Evacuee 1" +msgstr "" + +#: lang/json/mutation_from_json.py +msgid "Survivor: Evacuee 2" +msgstr "" + +#: lang/json/mutation_from_json.py +msgid "Survivor: Evacuee 3" +msgstr "" + +#: lang/json/mutation_from_json.py +msgid "Survivor: Evacuee 4" +msgstr "" + +#: lang/json/mutation_from_json.py +msgid "Survivor: Evacuee 5" +msgstr "" + +#: lang/json/mutation_from_json.py +msgid "Survivor: Evacuee 6" +msgstr "" + +#: lang/json/mutation_from_json.py +msgid "Survivor: FEMA Evacuee 1" +msgstr "" + +#: lang/json/mutation_from_json.py +msgid "Survivor: Left for Dead 1" +msgstr "" + +#: lang/json/mutation_from_json.py +msgid "Survivor: Left for Dead 2" +msgstr "" + +#: lang/json/mutation_from_json.py +msgid "Survivor: Left for Dead 3" +msgstr "" + +#: lang/json/mutation_from_json.py +msgid "Survivor Story" +msgstr "" + #: lang/json/mutation_from_json.py msgid "Mark of the Seer" msgstr "" @@ -134190,11 +135502,6 @@ msgstr "" msgid "I'm tracking game." msgstr "" -#: lang/json/npc_class_from_json.py -#: lang/json/npc_from_json.py -msgid "Soldier" -msgstr "" - #: lang/json/npc_class_from_json.py lang/json/npc_from_json.py msgid "Bartender" msgstr "" @@ -134946,10 +136253,6 @@ msgstr "" msgid "Laborer" msgstr "" -#: lang/json/npc_from_json.py -msgid "Lumberjack" -msgstr "" - #: lang/json/npc_from_json.py msgid "Woodworker" msgstr "" @@ -152360,96 +153663,6 @@ msgstr "" msgid "I'd kill for a sip of water right now." msgstr "" -#: lang/json/snippet_from_json.py -msgid "" -"Yeah sure, can't help but notice you got beer with you! Let's crack a cold " -"one and chat, , how goes it?" -msgstr "" - -#: lang/json/snippet_from_json.py -msgid "" -"Oh definitely, how about one of those beers I see on you? What's up anyway?" -msgstr "" - -#: lang/json/snippet_from_json.py -msgid "" -"Yeah you share those beers I see you hoarding and then we chat all you " -"like! Only joking, what's up ?" -msgstr "" - -#: lang/json/snippet_from_json.py -msgid "" -"Hey , I bet a chat would be all the sweeter with a nice, cold beer " -"in hand. How's it going?" -msgstr "" - -#: lang/json/snippet_from_json.py -msgid "" -"While we chat, what say you we open a beer and just… pretend the world isn't " -"ending, just for a while?" -msgstr "" - -#: lang/json/snippet_from_json.py -msgid "Pass me one and let's talk about the good ol' days, ." -msgstr "" - -#: lang/json/snippet_from_json.py -msgid "Hey, sure thing, , I need a break anyway, how are you?" -msgstr "" - -#: lang/json/snippet_from_json.py -msgid "Yeah OK, , how's it going?" -msgstr "" - -#: lang/json/snippet_from_json.py -msgid "Sure, let's shoot the shit! You OK?" -msgstr "" - -#: lang/json/snippet_from_json.py -msgid "Why not? How you doing?" -msgstr "" - -#: lang/json/snippet_from_json.py -msgid "I'm OK with that, what's up?" -msgstr "" - -#: lang/json/snippet_from_json.py -msgid "I can spare a few minutes, how's things?" -msgstr "" - -#: lang/json/snippet_from_json.py -msgid "Sure thing , you good?" -msgstr "" - -#: lang/json/snippet_from_json.py -msgid "Alright, you got something to get off your chest?" -msgstr "" - -#: lang/json/snippet_from_json.py -msgid "Always ready for a good chat! But why, you OK?" -msgstr "" - -#: lang/json/snippet_from_json.py -msgid "OK , we should get to know each other, how are you coping?" -msgstr "" - -#: lang/json/snippet_from_json.py -msgid "Definitely, I'm game. How you holding up?" -msgstr "" - -#: lang/json/snippet_from_json.py -msgid "" -"Good idea . Let's forget the world for a while. How you doin'?" -msgstr "" - -#: lang/json/snippet_from_json.py -msgid "Ah, what the heck. How's life been treating you?" -msgstr "" - -#: lang/json/snippet_from_json.py -msgid "Sure. So, how about that weather ey?" -msgstr "" - #: lang/json/snippet_from_json.py msgid "darn" msgstr "" @@ -154912,6 +156125,18 @@ msgstr "" msgid "Was it rough surviving thus far?" msgstr "" +#: lang/json/snippet_from_json.py +msgid "How do you think we ended up here? What even happened?" +msgstr "" + +#: lang/json/snippet_from_json.py +msgid "What's going on? Like, big picture, what the hell happened?" +msgstr "" + +#: lang/json/snippet_from_json.py +msgid "Have you heard anything about how the apocalypse came about?" +msgstr "" + #: lang/json/snippet_from_json.py lang/json/talk_topic_from_json.py #: lang/json/talk_topic_from_json.py msgid "Let's talk about something else." @@ -155627,6 +156852,302 @@ msgstr "" msgid " will follow normal engagement rules." msgstr "" +#: lang/json/snippet_from_json.py +msgid "Yeah sure, want to crack open one of them beers?" +msgstr "" + +#: lang/json/snippet_from_json.py +msgid "Oh definitely, how about one of those beers you got?" +msgstr "" + +#: lang/json/snippet_from_json.py +msgid "" +"Yeah you share those beers I see you hoarding and then we chat all you " +"like! Only joking, heh." +msgstr "" + +#: lang/json/snippet_from_json.py +msgid "" +"Hey , I bet a chat would be all the sweeter with a nice, cold beer " +"in hand." +msgstr "" + +#: lang/json/snippet_from_json.py +msgid "" +"While we chat, what say you we open a beer and just… pretend the world isn't " +"ending." +msgstr "" + +#: lang/json/snippet_from_json.py +msgid "Pass me one and let's talk, ." +msgstr "" + +#: lang/json/snippet_from_json.py +msgid "Yeah, this summer heat is hitting me hard, you know?" +msgstr "" + +#: lang/json/snippet_from_json.py +msgid "Enjoying the summer." +msgstr "" + +#: lang/json/snippet_from_json.py +msgid "Kinda wishing it would cool off a bit, to be honest." +msgstr "" + +#: lang/json/snippet_from_json.py +msgid "OK, maybe it'll stop me from freezing in this weather." +msgstr "" + +#: lang/json/snippet_from_json.py +msgid "Gotta say, I'm not minding the snow." +msgstr "" + +#: lang/json/snippet_from_json.py +msgid "It's weird the zombies don't freeze." +msgstr "" + +#: lang/json/snippet_from_json.py +msgid "Well, I'm feeling pretty sick… but sure." +msgstr "" + +#: lang/json/snippet_from_json.py +#: lang/json/talk_topic_from_json.py +msgid "" +msgstr "" + +#: lang/json/snippet_from_json.py +msgid "I need a break anyway, how are you?" +msgstr "" + +#: lang/json/snippet_from_json.py +msgid "So, how's it going?" +msgstr "" + +#: lang/json/snippet_from_json.py +msgid "Let's shoot the shit! You OK, ?" +msgstr "" + +#: lang/json/snippet_from_json.py +msgid "I'm OK with that, what's up?" +msgstr "" + +#: lang/json/snippet_from_json.py +msgid "I guess I can spare a few minutes, how's things?" +msgstr "" + +#: lang/json/snippet_from_json.py +msgid "Sure thing , you good?" +msgstr "" + +#: lang/json/snippet_from_json.py +msgid "Alright, you got something to get off your chest?" +msgstr "" + +#: lang/json/snippet_from_json.py +msgid "Always ready for a good chat! But why, you OK?" +msgstr "" + +#: lang/json/snippet_from_json.py +msgid "OK , how are you coping?" +msgstr "" + +#: lang/json/snippet_from_json.py +msgid "I'm game. How you holding up?" +msgstr "" + +#: lang/json/snippet_from_json.py +msgid "Let's forget the world for a while. How you doin'?" +msgstr "" + +#: lang/json/snippet_from_json.py +msgid "What the heck. How's life been treating you?" +msgstr "" + +#: lang/json/snippet_from_json.py +msgid "So, how about that weather, eh?" +msgstr "" + +#: lang/json/snippet_from_json.py +msgid "Nice of you to make time. How's it been for you lately?" +msgstr "" + +#: lang/json/snippet_from_json.py +msgid "My dogs’ve been barkin’ lately, you know?" +msgstr "" + +#: lang/json/snippet_from_json.py +msgid "I feel great today. Not sure what it is, just one of those days." +msgstr "" + +#: lang/json/snippet_from_json.py +msgid "" +"I just can't believe it's over. I keep running my head back to the days it " +"all fell apart. The riots. The lies. The psychos. It never really felt " +"like it was going to go like this." +msgstr "" + +#: lang/json/snippet_from_json.py +msgid "" +"You ever think there's any truth to the crap they were spouting before the " +"world ended? Mind control drugs in the water, bio-terrorism? Some of it " +"would make sense, but it seems so far-fetched. Then again, we're dealing " +"with actual zombies." +msgstr "" + +#: lang/json/snippet_from_json.py +msgid "" +"I wonder if I should be getting more religious now, or less. You know what " +"I'm sayin', ?" +msgstr "" + +#: lang/json/snippet_from_json.py +msgid "" +"I been thinkin’ about rearranging my gear. It’s a real mess. Don’t wanna " +"go for my weapon and accidentally pull out a granola bar, right?" +msgstr "" + +#: lang/json/snippet_from_json.py +msgid "" +"You ever wonder why we even bother? We’re all just gonna be zombies " +"eventually anyway. I mean, not that I’m gonna stop fighting, but what’s the " +"damn point?" +msgstr "" + +#: lang/json/snippet_from_json.py +msgid "" +"I wish I could go bust a cap in one of those zombies right now, without all " +"the fuss about being scared for my life." +msgstr "" + +#: lang/json/snippet_from_json.py +msgid "" +"Every time I close my eyes, I can still see the riots. Do you get that, or " +"is it just me?" +msgstr "" + +#: lang/json/snippet_from_json.py +msgid "" +"You ever feel like the whole time before the apocalypse was just a dream " +"you’re waking up from?" +msgstr "" + +#: lang/json/snippet_from_json.py +msgid "" +"When do you think you realized the world was ending? For me, it was that " +"damned YouTube video with the lady killing the baby. Holy shit, you know?" +msgstr "" + +#: lang/json/snippet_from_json.py +msgid "I wonder if the government's still out there, holed up in some bunker." +msgstr "" + +#: lang/json/snippet_from_json.py +msgid "" +"Remember some of the crazy news from the end of the world? The stuff that " +"got drowned out by the riot coverage I mean. Like, didn't the governor of " +"Rhode Island secede from the Union or something?" +msgstr "" + +#: lang/json/snippet_from_json.py +msgid "" +"I keep having dreams that zombies still remember who they were as people, " +"and are just trapped inside the bodies watching everything happen. Haven't " +"been sleeping well." +msgstr "" + +#: lang/json/snippet_from_json.py +msgid "" +"Those mind-control drugs they put in the water to make people riot… you " +"think they're still in there?" +msgstr "" + +#: lang/json/snippet_from_json.py +msgid "" +"I can't stop wondering who fucked up to make all this happen. Obviously we " +"can't trust the news, they claimed the zombies were \"rioters\" for weeks. " +"Why? Where did this come from?" +msgstr "" + +#: lang/json/snippet_from_json.py +msgid "" +"If what they told us about the Chinese was even partly true, do you think " +"it's like this in China? Or maybe the US is some kind of quarantine zone, " +"and at least some of the world is still out there." +msgstr "" + +#: lang/json/snippet_from_json.py +msgid "" +"Have you noticed injuries aren’t healing the same as usual? I started " +"spotting it before the world ended, but it’s become more pronounced. " +"There’s hardly even a granulation step after a cut closes." +msgstr "" + +#: lang/json/snippet_from_json.py +msgid "" +"I still don’t understand how these zombies are powered. They’re like " +"perpetual motion machines." +msgstr "" + +#: lang/json/snippet_from_json.py +msgid "" +"So many parts of this still don't fit together. Who created the zombies? " +"What powers them? Maybe the rumours of mind control drugs were true all " +"along, and someone found a way to bioengineer living dead." +msgstr "" + +#: lang/json/snippet_from_json.py lang/json/talk_topic_from_json.py +#: lang/json/talk_topic_from_json.py +msgid "" +msgstr "" + +#: lang/json/snippet_from_json.py +msgid "" +"How do these zombies even keep going? What are they eating? Do you think " +"they'll ever rot away?" +msgstr "" + +#: lang/json/snippet_from_json.py +msgid "" +"I been thinkin', one of these days, we're gonna run out of toilet paper. " +"What then?" +msgstr "" + +#: lang/json/snippet_from_json.py +msgid "" +"Do you think it’s weird how we’ll, like, open a locked building and find a " +"lone zombie inside? How’d it even get there?" +msgstr "" + +#: lang/json/snippet_from_json.py +msgid "" +"Sometimes I wonder if we're all psychos, not just the ones that went crazy " +"and rioted. I never would have thought I could do the things I've done " +"since the world ended." +msgstr "" + +#: lang/json/snippet_from_json.py +msgid "You read any good books lately? I'm glad we still got books." +msgstr "" + +#: lang/json/snippet_from_json.py +msgid "" +"You know what I miss? Movie theaters. You think Hollywood survived this? " +"Maybe there's a bunch of zombie actors out there, filmin' shit out of " +"reflex. Hah, I'd watch that shit." +msgstr "" + +#: lang/json/snippet_from_json.py +msgid "I hear you, …" +msgstr "" + +#: lang/json/snippet_from_json.py +msgid "That reminds me of something…" +msgstr "" + +#: lang/json/snippet_from_json.py +msgid "Right, right. Say, you ever thought about…" +msgstr "" + #: lang/json/snippet_from_json.py msgid "" "\n" @@ -168039,11 +169560,11 @@ msgid "Acolyte." msgstr "" #: lang/json/talk_topic_from_json.py -msgid "You're back. Have you come to listen to the song?" +msgid "You're back. Have you come to listen to the song?" msgstr "" #: lang/json/talk_topic_from_json.py -msgid "You there. Quiet down. Can you hear it? The song?" +msgid "You there. Quiet down. Can you hear it? The song?" msgstr "" #: lang/json/talk_topic_from_json.py @@ -168078,8 +169599,8 @@ msgstr "" #: lang/json/talk_topic_from_json.py msgid "" -"Listen carefully. The bones… they sing. Can you hear it? The song they " -"weave? The stories they hold?" +"Listen carefully. The bones… they sing. Can you hear it? The song they " +"weave? The stories they hold?" msgstr "" #: lang/json/talk_topic_from_json.py @@ -168092,11 +169613,11 @@ msgstr "" #: lang/json/talk_topic_from_json.py msgid "" -"When it all happened, the Cataclysm, something… changed. You can see it in " -"all creatures, but most of all their bones. They break, morph, rise again, " -"in an infinite cycle. Living dead walk. Monsters rip and tear each other " -"apart. You can see the resonance, the quiet hum of raw strength, and only by " -"taking the bones does the cycle end - their story, their song, their " +"When it all happened, the Cataclysm, something… changed. You can see it in " +"all creatures, but most of all their bones. They break, morph, rise again, " +"in an infinite cycle. Living dead walk. Monsters rip and tear each other " +"apart. You can see the resonance, the quiet hum of raw strength, and only " +"by taking the bones does the cycle end - their story, their song, their " "strength, become yours to use." msgstr "" @@ -168114,11 +169635,11 @@ msgstr "" #: lang/json/talk_topic_from_json.py msgid "" -"Only when you crush the bones of a body does it cease to rise. Only if you " -"examine the bones can you see what was. Thus is the story. Whatever causes " +"Only when you crush the bones of a body does it cease to rise. Only if you " +"examine the bones can you see what was. Thus is the story. Whatever causes " "this change is alive, moving within us all, an inevitable part of this new " -"world. It holds the power of change. When we hold the bones, we hold the " -"power. Thus the strength. Together… they form a beautiful song." +"world. It holds the power of change. When we hold the bones, we hold the " +"power. Thus the strength. Together… they form a beautiful song." msgstr "" #: lang/json/talk_topic_from_json.py @@ -168127,7 +169648,7 @@ msgstr "" #: lang/json/talk_topic_from_json.py msgid "" -"There are others who follow this cause. You'd do well to aid them, for " +"There are others who follow this cause. You'd do well to aid them, for " "though we may not be numerous, we are emboldened by the songs we carry." msgstr "" @@ -168141,10 +169662,10 @@ msgstr "" #: lang/json/talk_topic_from_json.py msgid "" -"The song can be weaved in many forms. Carved bone charms, weapons and armor " +"The song can be weaved in many forms. Carved bone charms, weapons and armor " "all hold immense power, and when the time comes, me and my kindred shall " -"gather a great amount of song and sing it to restore this world. Restore it, " -"or end it. Makes no difference." +"gather a great amount of song and sing it to restore this world. Restore " +"it, or end it. Makes no difference." msgstr "" #: lang/json/talk_topic_from_json.py @@ -168154,7 +169675,7 @@ msgstr "" #: lang/json/talk_topic_from_json.py msgid "" "We believe that enough power in one song could revert the Cataclysm - or " -"accelerate it to a time beyond all, ending it all the same. But with the " +"accelerate it to a time beyond all, ending it all the same. But with the " "world looking as is, both options are preferable." msgstr "" @@ -168170,8 +169691,8 @@ msgstr "" #: lang/json/talk_topic_from_json.py msgid "" -"Your mind is open. More than most. Perhaps one day, you too will feel the " -"power of the song and become Kindred. For now, Acolyte, listen, listen and " +"Your mind is open. More than most. Perhaps one day, you too will feel the " +"power of the song and become Kindred. For now, Acolyte, listen, listen and " "feel the song." msgstr "" @@ -168181,9 +169702,9 @@ msgstr "" #: lang/json/talk_topic_from_json.py msgid "" -"Your skepticism does not surprise me. Perhaps one day, you too will hear the " -"inevitability of the song, feel its power. But until then, you will remain " -"an Acolyte, path to the Kindred closed." +"Your skepticism does not surprise me. Perhaps one day, you too will hear " +"the inevitability of the song, feel its power. But until then, you will " +"remain an Acolyte, path to the Kindred closed." msgstr "" #: lang/json/talk_topic_from_json.py @@ -168241,14 +169762,6 @@ msgstr "" msgid "Perhaps another time, Seer." msgstr "" -#: lang/json/talk_topic_from_json.py -msgid "" -"If you wish to be set on the path to enlightenment, first you must learn to " -"listen and hear the song. Go out, butcher a creature and feel the power " -"between your fingertips. Then bring me the bones and I shall carve them for " -"you. " -msgstr "" - #: lang/json/talk_topic_from_json.py msgid "Well, I guess I oughta see where this goes. I'm in." msgstr "" @@ -168257,10 +169770,6 @@ msgstr "" msgid "Not interested." msgstr "" -#: lang/json/talk_topic_from_json.py -msgid "Excellent. Now be on your way." -msgstr "" - #: lang/json/talk_topic_from_json.py msgid "Consider it done. But I also wanted to ask…" msgstr "" @@ -168277,20 +169786,13 @@ msgstr "" msgid "I'm off then." msgstr "" -#: lang/json/talk_topic_from_json.py -msgid "" -"The shambling corpses we see all around move in discord. Their song can be " -"used, but for an Acolyte, this would be needlessly hard. Be sure to carve an " -"unspoiled living creature." -msgstr "" - #: lang/json/talk_topic_from_json.py msgid "So, a creature that isn't a zombie, or a monster. Got it." msgstr "" #: lang/json/talk_topic_from_json.py msgid "" -"The path to enlightenment is for you to walk. For me to aid you would " +"The path to enlightenment is for you to walk. For me to aid you would " "ultimately impede your progress and muddle your song." msgstr "" @@ -168301,7 +169803,7 @@ msgstr "" #: lang/json/talk_topic_from_json.py msgid "" "You bear my mark, meaning I believe you have potential to learn to truly " -"listen to the Song. Yes, I will lend my skills to you, for now." +"listen to the Song. Yes, I will lend my skills to you, for now." msgstr "" #: lang/json/talk_topic_from_json.py @@ -168316,10 +169818,6 @@ msgstr "" msgid "That's good, but I need to go at it alone right now. Maybe later." msgstr "" -#: lang/json/talk_topic_from_json.py -msgid "I understand your reluctancy. Feel free to return when you see the way." -msgstr "" - #: lang/json/talk_topic_from_json.py msgid "Maybe some other time. Changing the topic…" msgstr "" @@ -168331,14 +169829,14 @@ msgstr "" #: lang/json/talk_topic_from_json.py msgid "" "It's not just walking horrors and monsters that have changed with the " -"Cataclysm. It started a… cycle, of sorts. Everything repeats. We can only " -"see it in others, but it happens to us, even you and I. How many times have " -"you fallen? Your flesh rent from your body, devoured. Or perhaps it was the " -"quiet whimper of death to exposure. But your bones rose again. Different " -"flesh, different name, sometimes even different knowledge, but the bones, " -"the same. We are all trapped in the same cycle. We just keep forgetting. " -"That's why we need to amass the Song. That's why it has to end, even if it " -"means the destruction, not restoration." +"Cataclysm. It started a… cycle, of sorts. Everything repeats. We can only " +"see it in others, but it happens to us, even you and I. How many times have " +"you fallen? Your flesh rent from your body, devoured. Or perhaps it was " +"the quiet whimper of death to exposure. But your bones rose again. " +"Different flesh, different name, sometimes even different knowledge, but the " +"bones, the same. We are all trapped in the same cycle. We just keep " +"forgetting. That's why we need to amass the Song. That's why it has to " +"end, even if it means the destruction, not restoration." msgstr "" #: lang/json/talk_topic_from_json.py @@ -168371,6 +169869,14 @@ msgstr "" msgid "Skip it, let's get going." msgstr "" +#: lang/json/talk_topic_from_json.py +msgid "Any hints about the world we now live in?" +msgstr "" + +#: lang/json/talk_topic_from_json.py +msgid "Let's talk about faction camps." +msgstr "" + #: lang/json/talk_topic_from_json.py msgid "What do you mean, \"mostly\" willing to follow my lead?" msgstr "" @@ -168608,11 +170114,11 @@ msgstr "" msgid "" "I can help with some tasks if you show me where to work.\n" " Use the zone manager (keybind 'Y') to set up sorting zones for your loot, " -"or to draw blueprints for a building, or to define where you want to plant " +"or to draw blueprints for a building, or to define where you want to plant " "some crops, or where you'd like some trees cut down, or where you want a " "vehicle dismantled or repaired, or a good fishing spot. Then talk to me " "about my current activity and tell me to sort stuff, or build stuff, or cut " -"down trees, or repair or dismantle a vehicle, or do farmwork, or catch some " +"down trees, or repair or dismantle a vehicle, or do farmwork, or catch some " "fish, and I'll go off and do my best to get what you want done.\n" " If I need tools, you should leave them in a loot zone near where you want " "me to work - axes for logging, shovels and seeds and fertilizer for farming, " @@ -168869,8 +170375,8 @@ msgstr "" #: lang/json/talk_topic_from_json.py msgid "" -"STOP, Put your hands in the air! Ha, startled you didn't I…there is no law " -"anymore..." +"STOP, Put your hands in the air! Ha, startled you didn't I… there is no law " +"anymore…" msgstr "" #: lang/json/talk_topic_from_json.py @@ -168892,7 +170398,7 @@ msgstr "" #: lang/json/talk_topic_from_json.py msgid "" "I was watching the station when things went sideways. None of the other " -"officers returned from the last call, well not as humans anyway..." +"officers returned from the last call, well not as humans anyway…" msgstr "" #: lang/json/talk_topic_from_json.py @@ -168943,7 +170449,7 @@ msgid "" msgstr "" #: lang/json/talk_topic_from_json.py -msgid "No, just no..." +msgid "No, just no…" msgstr "" #: lang/json/talk_topic_from_json.py @@ -168955,7 +170461,7 @@ msgid "Make it quick, I want to go back to sleep." msgstr "" #: lang/json/talk_topic_from_json.py -msgid "Just few minutes more..." +msgid "Just few minutes more…" msgstr "" #: lang/json/talk_topic_from_json.py @@ -169002,75 +170508,117 @@ msgid "I want to set some miscellaneous rules." msgstr "" #: lang/json/talk_topic_from_json.py -msgid "Can you teach me anything?" +msgid "I'd like to know a bit more about your abilities." msgstr "" #: lang/json/talk_topic_from_json.py -msgid "Let's trade items" +msgid "There's something I want you to do." msgstr "" #: lang/json/talk_topic_from_json.py -msgid "I want you to use this item." +msgid "I just wanted to talk for a bit." msgstr "" #: lang/json/talk_topic_from_json.py -msgid "Hold on to this item." +msgid "Can you help me understand something? (HELP/TUTORIAL)" msgstr "" #: lang/json/talk_topic_from_json.py -msgid "Guard this position." +msgid "I'm going to go my own way for a while." msgstr "" #: lang/json/talk_topic_from_json.py -msgid "I want to assign you to work at this camp." +msgid "Let's go." msgstr "" #: lang/json/talk_topic_from_json.py -msgid "Let's talk about your current activity." +msgid "" +" *tshk* Are you serious? This isn't a cell phone. Can it wait until we're " +"in the same place?" msgstr "" #: lang/json/talk_topic_from_json.py -msgid "Let's talk about faction camps." +msgid "Sure, what did you want to say?" msgstr "" #: lang/json/talk_topic_from_json.py -msgid "Find a horse and mount up!" +msgid "Mind if we just chat for a bit about your history?" msgstr "" #: lang/json/talk_topic_from_json.py -msgid "Get off your mount, please." +msgid "Let's just chitchat for a while, I could use some relaxation." msgstr "" #: lang/json/talk_topic_from_json.py -msgid "Please go to this location." +msgid "I changed my mind, wanted to ask you something else." msgstr "" #: lang/json/talk_topic_from_json.py -msgid "I'd like to know a bit more about your abilities." +msgid "I'm all ears, my friend." msgstr "" #: lang/json/talk_topic_from_json.py -msgid "Any hints about the world we now live in?" +msgid "You gonna give me orders?" msgstr "" #: lang/json/talk_topic_from_json.py -msgid "Mind if we just chat for a bit about your history?" +msgid "What would you like?" msgstr "" #: lang/json/talk_topic_from_json.py -msgid "Let's just chitchat for a while, I could use some relaxation." +msgid "Just say the word." msgstr "" #: lang/json/talk_topic_from_json.py -msgid "Tell me about giving you orders (NPC TUTORIAL)." +msgid "Can you teach me anything?" msgstr "" #: lang/json/talk_topic_from_json.py -msgid "I'm going to go my own way for a while." +msgid "Let's trade items" msgstr "" #: lang/json/talk_topic_from_json.py -msgid "Let's go." +msgid "I want you to use this item." +msgstr "" + +#: lang/json/talk_topic_from_json.py +msgid "Hold on to this item." +msgstr "" + +#: lang/json/talk_topic_from_json.py +msgid "Guard this position." +msgstr "" + +#: lang/json/talk_topic_from_json.py +msgid "I want to assign you to work at this camp." +msgstr "" + +#: lang/json/talk_topic_from_json.py +msgid "Find a horse and mount up!" +msgstr "" + +#: lang/json/talk_topic_from_json.py +msgid "Get off your mount, please." +msgstr "" + +#: lang/json/talk_topic_from_json.py +msgid "Please go to this location." +msgstr "" + +#: lang/json/talk_topic_from_json.py +msgid "I want you to build a camp here." +msgstr "" + +#: lang/json/talk_topic_from_json.py +msgid "We need to abandon this camp." +msgstr "" + +#: lang/json/talk_topic_from_json.py +msgid "Show me what needs to be done at the camp." +msgstr "" + +#: lang/json/talk_topic_from_json.py +msgid "Let's talk about your current activity." msgstr "" #: lang/json/talk_topic_from_json.py @@ -169622,10 +171170,6 @@ msgstr "" msgid "Stay at your current position." msgstr "" -#: lang/json/talk_topic_from_json.py -msgid "Show me what needs to be done at the camp." -msgstr "" - #: lang/json/talk_topic_from_json.py msgid "I'm currently ." msgstr "" @@ -169698,59 +171242,6 @@ msgstr "" msgid "Sure thing, I'll make my way there." msgstr "" -#: lang/json/talk_topic_from_json.py -msgid "" -msgstr "" - -#: lang/json/talk_topic_from_json.py -msgid "" -msgstr "" - -#: lang/json/talk_topic_from_json.py -msgid "" -"Yeah, this summer heat is hitting me hard, let's take a quick break, how " -"goes it ?" -msgstr "" - -#: lang/json/talk_topic_from_json.py -msgid "OK, maybe it'll stop me from freezing in this weather, what's up?" -msgstr "" - -#: lang/json/talk_topic_from_json.py -msgid "" -"Well, it's the time of day for a quick break surely! How are you holding up?" -msgstr "" - -#: lang/json/talk_topic_from_json.py -msgid "Man it's dark out isn't it? what's up?" -msgstr "" - -#: lang/json/talk_topic_from_json.py -msgid "Well, I'm feeling pretty sick… are you doing OK though?" -msgstr "" - -#: lang/json/talk_topic_from_json.py -msgid "" -"Definitely, by the way, thanks for helping me so much with my tasks! " -"Anyway, you coping OK, ? " -msgstr "" - -#: lang/json/talk_topic_from_json.py -msgid "" -"OK, let's take a moment, oh, and thanks for helping me with that thing, so… " -"what's up?" -msgstr "" - -#: lang/json/talk_topic_from_json.py -msgid "" -"Now, we've got a moment, I was just thinking it's been a month or so since… " -"since all this, how are you coping with it all?" -msgstr "" - -#: lang/json/talk_topic_from_json.py -msgid "Oh you know, not bad, not bad…" -msgstr "" - #: lang/json/talk_topic_from_json.py msgid "" msgstr "" @@ -169806,7 +171297,7 @@ msgid "Hello there." msgstr "" #: lang/json/talk_topic_from_json.py -msgid "Okay, no sudden movements..." +msgid "Okay, no sudden movements…" msgstr "" #: lang/json/talk_topic_from_json.py @@ -170159,7 +171650,7 @@ msgid "Ah, okay." msgstr "" #: lang/json/talk_topic_from_json.py -msgid "Not until I get some antibiotics..." +msgid "Not until I get some antibiotics…" msgstr "" #: lang/json/talk_topic_from_json.py @@ -170267,7 +171758,7 @@ msgid "I can't train you properly while I'm operating a vehicle!" msgstr "" #: lang/json/talk_topic_from_json.py -msgid "Give it some time, I'll show you something new later..." +msgid "Give it some time, I'll show you something new later…" msgstr "" #: lang/json/talk_topic_from_json.py @@ -170295,7 +171786,7 @@ msgid "See you around." msgstr "" #: lang/json/talk_topic_from_json.py -msgid "I really don't feel comfortable doing so..." +msgid "I really don't feel comfortable doing so…" msgstr "" #: lang/json/talk_topic_from_json.py @@ -170367,7 +171858,7 @@ msgid "Thanks, see you later!" msgstr "" #: lang/json/talk_topic_from_json.py -msgid "You picked up something that does not belong to you..." +msgid "You picked up something that does not belong to you…" msgstr "" #: lang/json/talk_topic_from_json.py @@ -170461,13 +171952,13 @@ msgid "You might be seeing more of me…" msgstr "" #: lang/json/talk_topic_from_json.py -msgid "Hey again. *kzzz*" +msgid "Hey again. *kzzz*" msgstr "" #: lang/json/talk_topic_from_json.py msgid "" -"I… I'm free. *Zzzt* I'm actually free! *bzzz* Look, you're the first person " -"I've seen in a long time." +"I… I'm free. *Zzzt* I'm actually free! *bzzz* Look, you're the first " +"person I've seen in a long time." msgstr "" #: lang/json/talk_topic_from_json.py @@ -170489,7 +171980,7 @@ msgid "Sorry, I'm nobody. Enjoy your freedom, I guess." msgstr "" #: lang/json/talk_topic_from_json.py -msgid "*buzz* Great! So what happens now?" +msgid "*buzz* Great! So what happens now?" msgstr "" #: lang/json/talk_topic_from_json.py @@ -170502,7 +171993,7 @@ msgstr "" #: lang/json/talk_topic_from_json.py msgid "" -"...Wait. *BEEP* Why do I believe you? *ZZZT* You could be just as bad as " +"…Wait. *BEEP* Why do I believe you? *ZZZT* You could be just as bad as " "them!" msgstr "" @@ -170524,7 +172015,7 @@ msgstr "" #: lang/json/talk_topic_from_json.py msgid "" -"Okay, okay, *BUZZ* I'm sorry! Don't hurt me again! Anything but the chip!" +"Okay, okay, *BUZZ* I'm sorry! Don't hurt me again! Anything but the chip!" msgstr "" #: lang/json/talk_topic_from_json.py @@ -170540,7 +172031,7 @@ msgid "No, *I'm* sorry, I didn't mean that. Go do what you want." msgstr "" #: lang/json/talk_topic_from_json.py -msgid "...kill… *ZTZTZT* …you!" +msgid "…kill… *ZTZTZT* …you!" msgstr "" #: lang/json/talk_topic_from_json.py @@ -170575,14 +172066,6 @@ msgstr "" msgid "Tell me how faction camps have changed." msgstr "" -#: lang/json/talk_topic_from_json.py -msgid "I want you to build a camp here." -msgstr "" - -#: lang/json/talk_topic_from_json.py -msgid "We need to abandon this camp." -msgstr "" - #: lang/json/talk_topic_from_json.py msgid "Nothing. Let's talk about something else." msgstr "" @@ -170929,7 +172412,7 @@ msgstr "" #: lang/json/talk_topic_from_json.py msgid "" "Are you sure? This doesn't seem like a particularly safe place for small " -"talk..." +"talk…" msgstr "" #: lang/json/talk_topic_from_json.py @@ -170952,6 +172435,50 @@ msgstr "" msgid "Actually, never mind." msgstr "" +#: lang/json/talk_topic_from_json.py +msgid " " +msgstr "" + +#: lang/json/talk_topic_from_json.py +msgid "" +msgstr "" + +#: lang/json/talk_topic_from_json.py +msgid " " +msgstr "" + +#: lang/json/talk_topic_from_json.py +msgid " " +msgstr "" + +#: lang/json/talk_topic_from_json.py +msgid " " +msgstr "" + +#: lang/json/talk_topic_from_json.py +msgid "" +msgstr "" + +#: lang/json/talk_topic_from_json.py +msgid " " +msgstr "" + +#: lang/json/talk_topic_from_json.py +msgid " " +msgstr "" + +#: lang/json/talk_topic_from_json.py +msgid " " +msgstr "" + +#: lang/json/talk_topic_from_json.py +msgid " " +msgstr "" + +#: lang/json/talk_topic_from_json.py +msgid "" +msgstr "" + #: lang/json/talk_topic_from_json.py msgid "Yes, friend?" msgstr "" @@ -170973,7 +172500,7 @@ msgid "May the earth flourish beneath our paths." msgstr "" #: lang/json/talk_topic_from_json.py -msgid "Unity of spirit, of mind, and body..." +msgid "Unity of spirit, of mind, and body…" msgstr "" #: lang/json/talk_topic_from_json.py @@ -171157,7 +172684,7 @@ msgstr "" #: lang/json/talk_topic_from_json.py msgid "" -"I grew up on the farm, I don't know much about ghosts and goblins, but I've " +"I grew up on the farm, I don't know much about ghosts and goblins, but I've " "spent a lot of time growing food and I work hard. It's better in the " "country, cleaner. Not as dangerous. I hope." msgstr "" @@ -171732,7 +173259,7 @@ msgid "Nevermind me, I'm just going to leave." msgstr "" #: lang/json/talk_topic_from_json.py -msgid "Indeed it is I! The one and only FOODPERSON!" +msgid "Indeed it is I! The one and only FOODPERSON!" msgstr "" #: lang/json/talk_topic_from_json.py @@ -172177,6 +173704,38 @@ msgstr "" msgid "Huh." msgstr "" +#: lang/json/talk_topic_from_json.py +msgid "" +"I barely understand what's going on *now*. Why do you think I'd know how " +"the world ended?" +msgstr "" + +#: lang/json/talk_topic_from_json.py +msgid "" +"OK, fine. Can you at least tell me what you remember about the events " +"leading up to now?" +msgstr "" + +#: lang/json/talk_topic_from_json.py +msgid "" +"What, don't you remember? No, sorry, that's not fair, it was a weird time. " +"OK, well, I guess this all started with the riots, didn't it? We were just " +"leading our lives, doing our jobs, and then people started rioting. Not the " +"usual protests that turned violent or anything, either, people just left " +"their houses and started breaking shit. The news tried to downplay it but " +"they couldn't keep it off the internet. I don't know what caused it, they " +"said it was some kind of drug or toxin in the water? Still, I didn't really " +"realize how bad it was getting at first. Somewhere along the way the " +"\"rioters\" started getting up and walking around with holes in their " +"chests, and that's when the real panic took over. The next few days - or " +"weeks, not really sure - are a blur to me. You'd have to ask someone else " +"how we got from there to total collapse." +msgstr "" + +#: lang/json/talk_topic_from_json.py +msgid "Thanks for filling me in." +msgstr "" + #: lang/json/talk_topic_from_json.py msgid "" "I was a cop. Small town sheriff. We got orders without even really knowing " @@ -172229,8 +173788,8 @@ msgid "" "tented around me. I wasn't even too badly hurt. I grabbed as much gear as " "I could, and I slipped out. It was night. I could hear fighting farther " "away in the city, so I went the other way. I made it a few blocks before I " -"ran into any ... I ran from them. I ran, and I ran, and I ran " -"some more. And here I am." +"ran into any … I ran from them. I ran, and I ran, and I ran some " +"more. And here I am." msgstr "" #: lang/json/talk_topic_from_json.py @@ -172496,6 +174055,30 @@ msgstr "" msgid "Thanks for telling me that stuff. " msgstr "" +#: lang/json/talk_topic_from_json.py +msgid "" +"So, like, there were some really bad riots, okay? Everyone got realy " +"violent and nasty, and to be honest, I was on a bit of a spirit journey for " +"a lot of it and I don't really remember too well. But the weirdest part is, " +"nobody even *cared* about each other. It's like all our caring got sucked " +"away. I think it was some kind of negative energy thing. And also that " +"made the dead, like, come back to life and stuff. Plus, like, there were " +"some monsters? I'm not really sure how they fit in." +msgstr "" + +#: lang/json/talk_topic_from_json.py +msgid "You seem to know a lot, what do you think caused it all?" +msgstr "" + +#: lang/json/talk_topic_from_json.py +msgid "" +"That's a tough one, but I keep thinking back to this dream I had like, a " +"year before it all started. I dreamed there was this big ball of evil " +"energy that was just waiting to suck up all the good thoughts on the earth " +"and turn us into monsters and things? So I guess that's what I think " +"happened. Everything else just seems too far-fetched, you know?" +msgstr "" + #: lang/json/talk_topic_from_json.py msgid "" "I made it to one of those evac shelters, but it was almost worse " @@ -172545,6 +174128,24 @@ msgid "" "in the crash, but I am not going back to find out." msgstr "" +#: lang/json/talk_topic_from_json.py +msgid "" +"What happened? I'm not really sure. You must know about the riots and all " +"that, that the government and the police totally failed to contain. I don't " +"have a good guess what caused that. I thought it was the usual stuff at " +"first, and I gotta admit, I was sort of excited and scared it was the start " +"of a revolution. Not excited enough to join in though, and I guess anyone " +"who was is probably dead now. I tried to wait it out at home, packed a " +"little bug-out bag, but then the internet started showing videos of rioters " +"getting back up and fighting with crazy injuries. I don't know how many " +"people really believed it at first, but I took that as my sign and ditched " +"town for the evac shelter. I don't know exactly what happened after that. " +"The center I was in was heavily vandalized and empty, and I never saw anyone " +"else. The cell phone grid was locked up, except for one emergency message " +"that came through around a day later saying the government had fallen. " +"Power went out a few days later." +msgstr "" + #: lang/json/talk_topic_from_json.py msgid "" "Same as most people who didn't get killed straight up during the riots. I " @@ -172578,6 +174179,21 @@ msgstr "" msgid "What do you think happened? You see them around anywhere?" msgstr "" +#: lang/json/talk_topic_from_json.py +msgid "" +"Well, I assume you know about the riots and the military and police and the " +"freakin' nightmare monsters walking the earth beside zombies, right? If " +"you're asking what I think caused it all, well, I dunno. My best guess it " +"was some huge government overreach, maybe some kind of experimental " +"bioweapon that got away. They tried to lie so much at the start about " +"everything that was going on, I don't think the whole 'Chinese attack' shit " +"measures up. They were trying to cover something up. As for the real end " +"times, maybe the rest of the world tried to contain it. I heard there were " +"honest-to-god nukes going off here on American soil. To me that seems like " +"somewhere else, maybe Europe, trying to get whatever is going on here " +"contained. Maybe it even worked. It's bad now but it's not like it was." +msgstr "" + #: lang/json/talk_topic_from_json.py msgid "" "There's nothing too special about me, I'm not sure why I survived. I got " @@ -172634,6 +174250,40 @@ msgid "" "pretty good life compared to those first few months." msgstr "" +#: lang/json/talk_topic_from_json.py +msgid "" +"Woah, , I don't even know where to start. The riots? I think it " +"was going on sooner than that. There were bad murmurs going on a few weeks " +"before that happened. Lots of really scary crimes, not your usual stuff but " +"like cannibalism and some real unspeakable shit, you know? When the riots " +"started, I think I was already primed to think of it as something different " +"from a normal equality riot or anything like that. I think that's part of " +"how I got out safer, I had had some time to get some stuff and get going, " +"and didn't try to make shopping trips. People were abso-fuckin-lutely crazy " +"in those days. It was a lot like the pandemic a few years back, except the " +"police were out in the streets in force, gunning people down like it was " +"going out of style." +msgstr "" + +#: lang/json/talk_topic_from_json.py +msgid "Do you have any idea what the actual cause was?" +msgstr "" + +#: lang/json/talk_topic_from_json.py +msgid "" +"Not really. Government fed us all kinds of conflicting stories, and there " +"was some absolutely heinous stuff going on. I mean, you can't have missed " +"that video of the woman killing her own baby, right? God, that still gives " +"me nightmares. I don't know what it was about it, something about the look " +"on her face. Worse stuff came out of course, and now we've both seen worse " +"things with our own eyes, but that one still comes back to haunt me. " +"Anyway, they never could control the riots, and by the time the rioters " +"started turning into undead it was way too late. I don't know if morale " +"just broke or what but I heard rumours the military and police started " +"turning on each other as much as the crowds. What actually made the dead " +"come back to life though? I haven't got a clue." +msgstr "" + #: lang/json/talk_topic_from_json.py msgid "" "They were shipping me with a bunch of evacuees over to a refugee center, " @@ -172642,6 +174292,50 @@ msgid "" "out of there." msgstr "" +#: lang/json/talk_topic_from_json.py +msgid "Don't leave me hanging, what happened next?" +msgstr "" + +#: lang/json/talk_topic_from_json.py +msgid "" +"I ran until I felt like my lungs were going to jump right out of my mouth. " +"I holed up in the forest for the night, under a fir tree. In the morning I " +"heard someone talking, so I went to see. I was playing it pretty careful " +"though, there were still a lot of psychos and rioters around. I snuck up on " +"some kind of thing, some monster worse than any zombie. Some huge bug " +"thing, saying random phrases like some kind of broken tape recorder. It was " +"dragging a few human bodies behind it, I couldn't tell if they were dead or " +"unconscious. Honestly I didn't wait to find out, I ducked into the bushes " +"and tried not to breath until I couldn't hear it anymore." +msgstr "" + +#: lang/json/talk_topic_from_json.py +msgid "Where did you go from there?" +msgstr "" + +#: lang/json/talk_topic_from_json.py +msgid "" +"Once I was okay leaving the bushes, I made my way to an old shed I could see " +"a ways off. It was falling in but it kept the rain and wind off and gave me " +"a place out of sight. I stayed there until I ran out of those ass-tasting " +"ration bars I'd filled my backpack with. Then I took on the wanderin' life " +"until we met." +msgstr "" + +#: lang/json/talk_topic_from_json.py +msgid "" +"What's this, some kinda Back to the Future thing? How could you not know " +"what happened? The world damn well ended, that's what. And it didn't start " +"with an earthquake, birds, snakes, or aeroplanes. It started with riots, " +"and they had to dispatch cops and then the military to take care of the " +" criminals. The government tried to pad it claiming it was some kind " +"of mind control, but I didn't see too much different from the usual " +"bullshit: entitled babies looking for an excuse to break the law. It just " +"got way worse, this time, until it was time to get out of dodge. I heard " +"rumours they were even bombing some of the urban centers to try to control " +"it - which, I have to admit, is maybe a bit too hard-core." +msgstr "" + #: lang/json/talk_topic_from_json.py msgid "" "My Evac shelter got swarmed by some of those bees, the ones the size of " @@ -172683,6 +174377,27 @@ msgstr "" msgid "Right. Sorry." msgstr "" +#: lang/json/talk_topic_from_json.py +msgid "" +"Okay listen. Don't believe that government stuff. There's a common thread " +"to all of it: the riots, the military failing to contain it, even the giant " +"monsters they said were appearing in the last few days and had to be wiped " +"out with nukes." +msgstr "" + +#: lang/json/talk_topic_from_json.py +msgid "You've got my attention." +msgstr "" + +#: lang/json/talk_topic_from_json.py +msgid "" +"You ever see the Matrix? This is it. In real life. To keep us locked in " +"here, the creators of the simulation have to make sure we're just the right " +"level of miserable. I think their algorithms got messed up though, and went " +"into overdrive, because all this is a little implausible. Still, I guess " +"we're still jacked in, so maybe it's working." +msgstr "" + #: lang/json/talk_topic_from_json.py msgid "" "Well, I was at home when the cell phone alert went off and told me to get to " @@ -172695,6 +174410,19 @@ msgid "" "what happened to all those people." msgstr "" +#: lang/json/talk_topic_from_json.py +msgid "" +"I gotta be honest with you, I heard a lot of stories back at the shelter, " +"and only one of them really stuck. This is some kind of science experiment " +"gone wrong. I don't know what caused the riots and the undead in the first " +"place, but there's no way it's this out of control everywhere. Yeah, I got " +"the same 'the government has fallen' text as everyone, but it doesn't make " +"*sense* that it could be so widespread so fast. I think we're in some sort " +"of exclusion zone, where they're letting whatever is going on run its course " +"to see how it works so they can fight it better next time. Somewhere out " +"there, outside the zone, it's more or less business as usual." +msgstr "" + #: lang/json/talk_topic_from_json.py msgid "" "That's a tall order. I guess the short version is that I got evacuated to a " @@ -172766,6 +174494,42 @@ msgstr "" msgid "Sorry for asking. " msgstr "" +#: lang/json/talk_topic_from_json.py +msgid "" +"Woof, you ready for a real hot take? The government did this to " +"us. Intentionally, or at least sort-of intentionally." +msgstr "" + +#: lang/json/talk_topic_from_json.py +msgid "Oh?" +msgstr "" + +#: lang/json/talk_topic_from_json.py +msgid "" +"Damn right. They lied to us for god knows how long about what was going on " +"because they didn't want us to figure it out. It probably started as a way " +"to keep the people in line, but it backfired somehow. My guess is they " +"tried to de-educate us, tried to mislead us, and when that wasn't working " +"they tried actual drugs in the water to make us stupid or something. " +"Instead of just stupid, some people got violent. Then they tried to " +"leverage that to put in martial law, but that didn't work and they wound up " +"fighting hordes of people. Only they didn't realize their brain " +"drugs were some kind of mutagen that turn people into zombies." +msgstr "" + +#: lang/json/talk_topic_from_json.py +msgid "What about all the other stuff?" +msgstr "" + +#: lang/json/talk_topic_from_json.py +msgid "" +"I think it's mostly mutation. I don't know what they used, but it's some " +"real mad science shit, I didn't think most of this was even possible. I " +"also wonder if whatever they put in the water has made us a bit crazy. " +"Maybe some of this is just a hallucination? That one blows my mind a bit, " +"I'm not sure I believe it but I got nothin' else." +msgstr "" + #: lang/json/talk_topic_from_json.py msgid "" "I'm not from around here… You can probably tell from the accent, I'm from " @@ -173354,6 +175118,48 @@ msgid "" "woods. I wasn't doing a great job of it, so I'm kinda glad you showed up." msgstr "" +#: lang/json/talk_topic_from_json.py +msgid "" +"Okay, so, hear me out. This might sound crazy, but we're dealing with the " +" walking dead, so I think I get a pass on that. You know your Greek " +"mythology at all?" +msgstr "" + +#: lang/json/talk_topic_from_json.py +msgid "Not really. How is that relevant?" +msgstr "" + +#: lang/json/talk_topic_from_json.py +msgid "Sure, why?" +msgstr "" + +#: lang/json/talk_topic_from_json.py +msgid "" +"Okay, well, I know this sounds like an Indiana Jones B-plot, but I think " +"someone found Pandora's Box, the actual thing or close to it. I think they " +"tried to somehow harness it, to use the power in it for something. Maybe " +"even something good, who knows, the power of the gods seems like it would be " +"a green energy source to me. Whatever it was, they screwed it up, and " +"released it for real. Not just a metaphorical thing like in the stories, " +"but actually set the forces of Hades loose on Earth. Yeah, I know it's " +"farfetched, but like I said: I think I get a pass on that." +msgstr "" + +#: lang/json/talk_topic_from_json.py +msgid "What? Pandora's box?" +msgstr "" + +#: lang/json/talk_topic_from_json.py +msgid "" +"According to legend, Pandora was in the house of the gods and found an " +"unopened box. She decided to investigate, and when she opened it and " +"unthinkable horrors and diseases spilled out. Sound familiar?" +msgstr "" + +#: lang/json/talk_topic_from_json.py +msgid "Sure, what's that go to do with anything?" +msgstr "" + #: lang/json/talk_topic_from_json.py msgid "" "I was home with the flu when the world went to shit, and when I recovered " @@ -173399,6 +175205,128 @@ msgstr "" msgid "Thanks for telling me all that. " msgstr "" +#: lang/json/talk_topic_from_json.py +msgid "" +"You mean what caused the riots and all that? Well, they told us it was a " +"Chinese bioweapon but I have troubles believing anyone could engineer a " +"bioweapon that could do all this. The only answer I can come up with, silly " +"though it sounds, is aliens." +msgstr "" + +#: lang/json/talk_topic_from_json.py +msgid "You think this is an invasion?" +msgstr "" + +#: lang/json/talk_topic_from_json.py +msgid "" +"Well, maybe, but I'd guess it's too disorganized to be a proper invasion. " +"If I had to guess, I'd say there was something locked in the polar ice. " +"Like, remember a few years back there was that big thing online about an " +"alien body found in the ice? I don't know if you remember but it was all " +"over the news for a while until it turned out to be a hoax. Only, since " +"then, I've seen some aliens walking around that look a *lot* like that ice " +"body. Maybe it wasn't a hoax, maybe that was a cover-up. So, maybe those " +"aliens had some kind of virus. It went around the world and infected " +"everything silently until, somehow, it activated and caused the violence and " +"monsters and mutations." +msgstr "" + +#: lang/json/talk_topic_from_json.py +msgid "" +"Let's not dance around it: I joined the riots, at first. I don't really " +"remember what I was thinking. I'd protested stuff like police brutality " +"before, but this was different. I didn't make a sign and go down there " +"expecting to chant and march, I grabbed a bat and went outside planning to " +"fuck shit up. I've never felt so angry before. The riots had already been " +"going on a while at that point, and to me, it just looked like the " +"government trying to squash the people again." +msgstr "" + +#: lang/json/talk_topic_from_json.py +msgid "Those rioters had a reputation for being absolutely psycho." +msgstr "" + +#: lang/json/talk_topic_from_json.py +msgid "Not many people made it out of the riots alive." +msgstr "" + +#: lang/json/talk_topic_from_json.py +msgid "" +"Yeah, you're telling me. The rioters… they weren't even like humans, let " +"alone protestors. Nothing like any protest I'd ever been in before. That " +"didn't stop me. I joined them, and I was as bad as a bunch of them. " +"Smashed windows, beat up bystanders, burnt cars. I remember ripping riot " +"gear off a cop and… nevermind. I don't want to remember that." +msgstr "" + +#: lang/json/talk_topic_from_json.py +msgid "How did you survive?" +msgstr "" + +#: lang/json/talk_topic_from_json.py +msgid "What made you come back?" +msgstr "" + +#: lang/json/talk_topic_from_json.py +msgid "" +"At some point, I felt like I was waking up. It was around the time they " +"were busting out those humvees with riot control turrets on top. Says " +"something about my frame of mind that I don't even remember if I'd seen them " +"before that point. Anyway I heard the gunfire going off and just kinda " +"realized I was on the edges of a mob charging a heavily armed military " +"emplacement. That's when I started seeing the mob for what it was, seeing " +"the wild-eyed animals, even the zombies. I turned and ran." +msgstr "" + +#: lang/json/talk_topic_from_json.py +msgid "" +"I honestly don't know. I wonder if some of the others would have come back " +"to their senses, given time. I don't think I'm anything special. Maybe a " +"lot of them did, and just weren't on the edge of the crowd at the time. " +"I'll tell you, almost as soon as I came back to myself, I could see some of " +"the rioters looking at me like I was prey. I didn't stick around to see " +"what would happen." +msgstr "" + +#: lang/json/talk_topic_from_json.py +msgid "" +"I knew the city pretty well. I went for an abandoned building I knew about, " +"headed through a broken window, and holed up in there for a few days. I had " +"a fair bit of stolen food and I just kept to myself. When things started to " +"quiet down, I headed out, and here I am." +msgstr "" + +#: lang/json/talk_topic_from_json.py +msgid "" +"So, I remember the time leading up to the riots, same as anyone. Things " +"were bad, there were some really awful crimes being reported in the news, " +"and there was a lot of racial tension as usual from the way the cops were " +"handling it. Then people started rioting, which isn't unusual, but it was " +"weird the kind of places that the riots were starting in. Like, upper " +"middle class neighbourhoods, midwestern small towns, things like that. " +"Anyway, I joined the riots and I don't remember a lot of clear stuff after " +"that." +msgstr "" + +#: lang/json/talk_topic_from_json.py +msgid "You joined the riots?" +msgstr "" + +#: lang/json/talk_topic_from_json.py +msgid "You must have some insights into what caused all this, then." +msgstr "" + +#: lang/json/talk_topic_from_json.py +msgid "" +"Kinda, I guess. I heard people blaming the riots on some kind of mind " +"control drug, and frankly I'm not sure that's far off base. That's kinda " +"what it felt like, although the whole time I really felt like myself. It " +"wasn't until I snapped out of it that I realized how weird it was. That " +"doesn't explain anything else though: the zombies, the monsters, all this " +"stuff is way off base. Anything I've tried to guess just sounds like bad " +"science fiction, like demonic curses or alien weapons kinda stuff." +msgstr "" + #: lang/json/talk_topic_from_json.py msgid "" "My husband made it out with me, but got eaten by one of those plant " @@ -173664,13 +175592,48 @@ msgstr "" msgid "I can respect that." msgstr "" +#: lang/json/talk_topic_from_json.py +msgid "I don't really want to talk about the time before, you know?" +msgstr "" + +#: lang/json/talk_topic_from_json.py +msgid "Keep it vague if you want, but please, can you fill me in a little?" +msgstr "" + +#: lang/json/talk_topic_from_json.py +msgid "" +"I - fine. Drugs in the water, some kind of bioweapon I guess. You know how " +"things were with China, they blamed it on them mostly. Made people violent " +"and ugly. There were riots. People I cared about joined them, and I guess " +"I'll never know why. Riots led to military and police action, which made " +"the riots worse. People acted like animals, not just the rioters but " +"everyone. Then came the monsters and nightmares walking the world like real " +"Armageddon, and everyone died, and started coming back as monsters " +"themselves. There's your story. If you want more, talk to someone else." +msgstr "" + +#: lang/json/talk_topic_from_json.py +msgid "Thanks for that." +msgstr "" + +#: lang/json/talk_topic_from_json.py +msgid "" +"To be honest… I don't really remember. I remember vague details of my life " +"before the world was like this, but itself? It's all a " +"blur. I think something pretty bad must have happened to me. I remember a " +"few things: snatches of violence, something about a woman killing her baby. " +"I feel like I'd rather not remember." +msgstr "" + #: lang/json/talk_topic_from_json.py msgid "" "To be honest… I don't really remember. I remember vague details of my life " "before the world was like this, but itself? It's all a " "blur. I don't know how I got where I am now, or how any of this happened. " "I think something pretty bad must have happened to me. Or maybe I was just " -"hit in the head really hard. Or both. Both seems likely." +"hit in the head really hard. Or both. Both seems likely. First thing I " +"remember is seeing an already-read text on my phone from the emergency " +"government broadcast system, saying the United States had fallen." msgstr "" #: lang/json/talk_topic_from_json.py @@ -173756,6 +175719,43 @@ msgid "" "don't ask again." msgstr "" +#: lang/json/talk_topic_from_json.py +msgid "" +"You're asking me what I think caused all this? It was all over the news. " +"Some kind of Chinese bio-weapon. It's no different from the pandemic a few " +"years back, but this time they got the formula right. Maybe too right. " +"Doesn't matter anyway, I hear it got out on them and wiped them out too. " +"Serves em right." +msgstr "" + +#: lang/json/talk_topic_from_json.py +msgid "Can you tell me more about what actually went down, though?" +msgstr "" + +#: lang/json/talk_topic_from_json.py +msgid "How does that explain all the other crazy stuff?" +msgstr "" + +#: lang/json/talk_topic_from_json.py +msgid "" +"Well, you know. First there were the riots from the mind-control drugs in " +"the water. Except I think we can all see now it was actually a virus " +"again. The military and the cops did their damndest to put it down but it " +"got out of hand. Then the virus mutated and started bringing the dead back " +"to life like in some kinda B-movie, and shit got really real. They let the " +"big things loose, or they set them on us, I dunno. Huge unspeakable " +"monsters… still makes me shudder to think of them. They obviously weren't " +"built for combat though, and the military took 'em down fast." +msgstr "" + +#: lang/json/talk_topic_from_json.py +msgid "" +"What? Of course it does. They started with a bioweapon and then it went " +"full nuclear. Only the weapons we had now were a lot worse than H-bombs. " +"Uncle Sam managed to beat back the really nasty stuff, but I guess it was " +"with his dying breath." +msgstr "" + #: lang/json/talk_topic_from_json.py msgid "" "Let's not talk about it, ok? It just hurts to think about. I've lost so " @@ -174048,7 +176048,7 @@ msgstr "" #: lang/json/talk_topic_from_json.py msgid "" "Tax evasion. I was an accountant, and I helped my boss move a hell of a lot " -"of money in some very clever ways. Not clever enough, it turns out..." +"of money in some very clever ways. Not clever enough, it turns out…" msgstr "" #: lang/json/talk_topic_from_json.py @@ -174221,6 +176221,32 @@ msgid "" "we're the meek that shall inherit the Earth. Although I don't love our odds." msgstr "" +#: lang/json/talk_topic_from_json.py +msgid "" +"It's clear enough, isn't it? That… that end, was the Rapture. I'm still " +"here, and I still don't understand why, but I will keep Jesus in my heart " +"through the Tribulations to come. When they're past, I'm sure He will " +"welcome me into the Kingdom of Heaven. Or… or something along those lines." +msgstr "" + +#: lang/json/talk_topic_from_json.py +msgid "I meant more the actual events. What happened?" +msgstr "" + +#: lang/json/talk_topic_from_json.py +msgid "" +"Oh. Well, I think it follows the good word in Revelations, if I remember " +"right. I haven't talked to a preacher in a bit, you know. There were the " +"plagues… the first one was the one a couple years ago, that big pandemic, " +"that was when people started talking about the end being near. Then there " +"was a plague of blood, or was it violence? That was the riots. Then the " +"seas turned red with blood, that was from all the people being shot. Then a " +"plague of fire, I remember that one for sure, that was when there were bombs " +"and things going off everywhere to try to contain the riots. And then " +"demons and monsters walked the Earth, and the dead rose from their graves, " +"and finally the meek inherited. Clear as day." +msgstr "" + #: lang/json/talk_topic_from_json.py msgid "" "Same as anyone. I turned away from God, and now I'm paying the price. The " @@ -174228,6 +176254,23 @@ msgid "" "Hell on Earth. I wish I'd paid more attention in Sunday School." msgstr "" +#: lang/json/talk_topic_from_json.py +msgid "" +"Well, I guess that was the Rapture. It didn't play out how I thought it " +"would. They made me think it was gonna be a flash of light and then *poof*, " +"everyone's gone. Instead it was messy and dirty. Riots in the streets, the " +"military and police serving the Antichrist to gun down the people like - " +"what was it my dad used to say - like wheat before the chaff? Then when " +"we'd really showed our Sin, God came in with the real plagues. The dead " +"started walking, getting up with machine gun holes in them to fight the " +"military, and the military started turning on each other too. After that, " +"the legions of Hell itself came out. Huge monsters, worse than anything I'd " +"ever imagined, just tore through the cities ripping everything to shreds. " +"Then they started dying off as quick as they'd arrived, and we were left " +"trying to run and hide from the undead. A day or two later the power " +"started going out." +msgstr "" + #: lang/json/talk_topic_from_json.py msgid "" "I lived alone, on the old family property way out of town. My husband " @@ -174441,10 +176484,6 @@ msgstr "" msgid "What was working for the Old Guard like?" msgstr "" -#: lang/json/talk_topic_from_json.py -msgid "Thanks for that." -msgstr "" - #: lang/json/talk_topic_from_json.py msgid "Thanks for that. Let's get going." msgstr "" @@ -174656,6 +176695,22 @@ msgstr "" msgid "What were you saying before that?" msgstr "" +#: lang/json/talk_topic_from_json.py +msgid "" +"I'll be honest with you, I was paying more attention to wedding planning " +"than current events leading up to things. I knew there were riots going on, " +"but they were out of town. Even when they got closer to home, I tried to " +"ignore them so we could have our big day. After the zombies started coming, " +"though, well that's when stuff got really weird. When I was running from " +"the wedding I swear I saw the sky rip open and monsters fly out of the hole, " +"like something out of Independence Day. I don't know what it all was, it " +"looked like black magic or something." +msgstr "" + +#: lang/json/talk_topic_from_json.py +msgid "" +msgstr "" + #: lang/json/talk_topic_from_json.py msgid "Hey there." msgstr "" @@ -175177,7 +177232,7 @@ msgid "You should get off my farm, I won't deal with a government stooge." msgstr "" #: lang/json/talk_topic_from_json.py -msgid "Go on..." +msgid "Go on…" msgstr "" #: lang/json/talk_topic_from_json.py @@ -175569,10 +177624,6 @@ msgid "" "catastrophe." msgstr "" -#: lang/json/talk_topic_from_json.py -msgid "Go on ..." -msgstr "" - #: lang/json/talk_topic_from_json.py msgid "Tell me about your wife, is she around?" msgstr "" @@ -176992,7 +179043,7 @@ msgstr "" #: lang/json/talk_topic_from_json.py msgid "" -"Gross, isn't it? Feels like pubes. I just started growing it everywhere a " +"Gross, isn't it? Feels like pubes. I just started growing it everywhere a " "little while after the Cataclysm. No idea what caused it. I can't blame " "them for hating it, I hate it." msgstr "" @@ -177716,7 +179767,7 @@ msgid "" "Guitar's my baby. You like folk and the blues, friend? Well, that was my " "bag and I sure could please my own ear with em, anyway. Folks who's bein' " "generous might also say it pleased theirs. Problem is, I seem to be between " -"guitars right now, you know? Temporarily guitar-light, if you get my " +"guitars right now, you know? Temporarily guitar-light, if you get my " "saying. Problem is, in the run for my life, I had to use old Jasmine as a " "bit of a billy club. Had to curb some rowdy dudes on my way here. It was " "her or me, you understand? You wouldn't begrudge a man breakin' his " @@ -178809,12 +180860,12 @@ msgstr "" #: lang/json/talk_topic_from_json.py msgid "" -"Dana and I were evacuated early, because of her pregnancy. They took us to a " -"concentration center, and then we got on a bus to come here. The bus though, " -"it was rolled over by a giant monster, and many died. We made it out along " -"with a few others, and we kept going until we made it here. It wasn't much " -"farther, and for some reason the monster didn't chase us, just kept tearing " -"at the bus." +"Dana and I were evacuated early, because of her pregnancy. They took us to " +"a concentration center, and then we got on a bus to come here. The bus " +"though, it was rolled over by a giant monster, and many died. We made it " +"out along with a few others, and we kept going until we made it here. It " +"wasn't much farther, and for some reason the monster didn't chase us, just " +"kept tearing at the bus." msgstr "" #: lang/json/talk_topic_from_json.py @@ -178942,7 +180993,7 @@ msgstr "" #: lang/json/talk_topic_from_json.py msgid "" "It's a long, long story. I'm not from around here, I'm actually from way " -"out in Western Canada. I'd always wanted to see New England, and I was down " +"out in Western Canada. I'd always wanted to see New England, and I was down " "here on vacation when, well, you know. I got evacuated, but because I'm not " "a US citizen they weren't willing to take me downstairs. I can understand " "that, even if I don't like it much. To tell you the truth I'm still coming " @@ -179011,7 +181062,7 @@ msgid "Hm? Oh, hi." msgstr "" #: lang/json/talk_topic_from_json.py -msgid "...Hi." +msgid "…Hi." msgstr "" #: lang/json/talk_topic_from_json.py @@ -179375,8 +181426,8 @@ msgstr "" #: lang/json/talk_topic_from_json.py msgid "" "Even once we got things sorted out, there weren't enough beds for everyone, " -"and definitely not enough supplies. These are harsh times. We're doing what " -"we can for those folks… at least they've got shelter." +"and definitely not enough supplies. These are harsh times. We're doing " +"what we can for those folks… at least they've got shelter." msgstr "" #: lang/json/talk_topic_from_json.py @@ -179393,9 +181444,9 @@ msgstr "" #: lang/json/talk_topic_from_json.py msgid "" -"We didn't have great organization when we first arrived. A few of the " +"We didn't have great organization when we first arrived. A few of the " "earliest arrivals set up a triage and sorting system, with the sick and " -"infirm getting set aside to wait. It's cruel, but we could see there was " +"infirm getting set aside to wait. It's cruel, but we could see there was " "only space for so many, and we didn't know what was causing people to turn " "into zombies at the time, so we were trying to quarantine out infection. A " "couple folks died in there, and it escalated. One of the first people here, " @@ -179719,7 +181770,7 @@ msgid "" msgstr "" #: lang/json/talk_topic_from_json.py -msgid "I'm not in charge here, you're looking for someone else..." +msgid "I'm not in charge here, you're looking for someone else…" msgstr "" #: lang/json/talk_topic_from_json.py @@ -179767,11 +181818,11 @@ msgid "Well, I'd better be going. Bye." msgstr "" #: lang/json/talk_topic_from_json.py -msgid "Welcome marshal..." +msgid "Welcome marshal…" msgstr "" #: lang/json/talk_topic_from_json.py -msgid "Welcome..." +msgid "Welcome…" msgstr "" #: lang/json/talk_topic_from_json.py @@ -179999,11 +182050,11 @@ msgid "" msgstr "" #: lang/json/talk_topic_from_json.py -msgid "Marshal..." +msgid "Marshal…" msgstr "" #: lang/json/talk_topic_from_json.py -msgid "Citizen..." +msgid "Citizen…" msgstr "" #: lang/json/talk_topic_from_json.py @@ -180601,7 +182652,7 @@ msgid "" "protective gear: gas mask, suit and gear, at a considerable discount. We " "will sell it for two of our coins.\n" "\n" -"the intercom: Hmm wait, we might not have your size..." +"the intercom: Hmm wait, we might not have your size…" msgstr "" #: lang/json/talk_topic_from_json.py @@ -180789,15 +182840,15 @@ msgid "Now that you mention it, it does seem rather strange." msgstr "" #: lang/json/talk_topic_from_json.py -msgid "Thinking I should go hunt something soon..." +msgid "Thinking I should go hunt something soon…" msgstr "" #: lang/json/talk_topic_from_json.py -msgid "Wondering if things will get better someday..." +msgid "Wondering if things will get better someday…" msgstr "" #: lang/json/talk_topic_from_json.py -msgid "Hmm? Nothing, I guess I just like resting in this place." +msgid "Hmm? Nothing, I guess I just like resting in this place." msgstr "" #: lang/json/talk_topic_from_json.py @@ -180837,7 +182888,7 @@ msgstr "" #: lang/json/talk_topic_from_json.py msgid "" "Still plenty of outlaws in the roads, perhaps you should tend to your job, " -"marshal..." +"marshal…" msgstr "" #: lang/json/talk_topic_from_json.py @@ -180906,7 +182957,7 @@ msgid "I can't imagine what I'd need your assistance with." msgstr "" #: lang/json/talk_topic_from_json.py -msgid "Stand still while I get my clippers..." +msgid "Stand still while I get my clippers…" msgstr "" #: lang/json/talk_topic_from_json.py @@ -181277,7 +183328,7 @@ msgid "" msgstr "" #: lang/json/talk_topic_from_json.py -msgid "Please leave me alone..." +msgid "Please leave me alone…" msgstr "" #: lang/json/talk_topic_from_json.py @@ -181296,13 +183347,13 @@ msgstr "" #: lang/json/talk_topic_from_json.py msgid "" -"I don't know what you could do. I've tried everything. Just give me time..." +"I don't know what you could do. I've tried everything. Just give me time…" msgstr "" #: lang/json/talk_topic_from_json.py msgid "" "I keep getting sick! At first I thought it was something I ate but now it " -"seems like I can't keep anything down..." +"seems like I can't keep anything down…" msgstr "" #: lang/json/talk_topic_from_json.py @@ -181424,8 +183475,8 @@ msgstr "" #: lang/json/talk_topic_from_json.py msgid "" -"Here? Fruits and berries. Maybe the occasional piece of farm equipment, but " -"you need crypto coins" +"Here? Fruits and berries. Maybe the occasional piece of farm equipment, " +"but you need crypto coins" msgstr "" #: lang/json/talk_topic_from_json.py @@ -181769,7 +183820,7 @@ msgstr "" #: lang/json/talk_topic_from_json.py msgid "" -"You look hungry friend. So much hunger in this world. This is the time of " +"You look hungry friend. So much hunger in this world. This is the time of " "the eaters." msgstr "" @@ -181937,7 +183988,7 @@ msgid "Happy to be of service to the great eaters. I'm in." msgstr "" #: lang/json/talk_topic_from_json.py -msgid "Excellent. Make it happen." +msgid "Excellent. Make it happen." msgstr "" #: lang/json/talk_topic_from_json.py @@ -182097,7 +184148,7 @@ msgid "Oh, you again." msgstr "" #: lang/json/talk_topic_from_json.py -msgid "Huh? *mumble mumble* … Who are you?" +msgid "Huh? *mumble mumble* … Who are you?" msgstr "" #: lang/json/talk_topic_from_json.py @@ -182105,7 +184156,7 @@ msgid "I'm busy, what is it?" msgstr "" #: lang/json/talk_topic_from_json.py -msgid "And leave my tower and all my research? I think not." +msgid "And leave my tower and all my research? I think not." msgstr "" #: lang/json/talk_topic_from_json.py @@ -186320,114 +188371,88 @@ msgid "" msgstr "" #: lang/json/terrain_from_json.py -msgid "dirt" +msgid "concrete floor" msgstr "" -#. ~ Description for dirt +#. ~ Description for concrete floor #: lang/json/terrain_from_json.py msgid "" -"It's dirt. Looks like some fine soil for tillage. Could also be dug out " -"for construction projects." +"A bare and cold concrete floor with matching roof, could still insulate from " +"the outdoors but roof collapse is possible if supporting walls are broken " +"down." msgstr "" #: lang/json/terrain_from_json.py -msgid "thump" +msgid "SMASH!" msgstr "" -#. ~ Description for sand +#. ~ Description for concrete floor #: lang/json/terrain_from_json.py msgid "" -"A large area of fine sand that could be useful in a number of ways, if it " -"was extracted properly." -msgstr "" - -#: lang/json/terrain_from_json.py -msgid "mud" -msgstr "" - -#. ~ Description for mud -#: lang/json/terrain_from_json.py -msgid "An area of wet, slick mud." +"A bare and cold concrete floor with a streak of yellow paint, could still " +"insulate from the outdoors but roof collapse is possible if supporting walls " +"are broken down." msgstr "" #: lang/json/terrain_from_json.py -msgid "clay" +msgid "concrete floor, overhead light" msgstr "" -#. ~ Description for clay +#. ~ Description for concrete floor, overhead light #: lang/json/terrain_from_json.py msgid "" -"A field full of malleable clay, suitable for kiln firing if it was extracted " -"properly." -msgstr "" - -#: lang/json/terrain_from_json.py -msgid "mound of clay" -msgstr "" - -#. ~ Description for mound of clay -#: lang/json/terrain_from_json.py -msgid "A mound of clay soil." -msgstr "" - -#: lang/json/terrain_from_json.py -msgid "splosh!" +"A bare and cold concrete floor with a still-functioning light attached to " +"the ceiling above." msgstr "" #: lang/json/terrain_from_json.py -msgid "mound of sand" +msgid "small rebar roof cage" msgstr "" -#. ~ Description for mound of sand +#. ~ Description for small rebar roof cage #: lang/json/terrain_from_json.py -msgid "A mound of sand." +msgid "" +"A series of structural support crafted from rebar in order to allow the " +"pouring of concrete for a more stable floor and roof." msgstr "" #: lang/json/terrain_from_json.py -msgid "mound of dirt" +msgid "SCRRRASH!" msgstr "" -#. ~ Description for mound of dirt #: lang/json/terrain_from_json.py -msgid "" -"An area of heaped dirt, not easily traversable. If examined more closely, " -"it's quite favorable for planting seeds and the like." +msgid "reinforced concrete floor" msgstr "" -#. ~ Description for mound of dirt +#. ~ Description for reinforced concrete floor #: lang/json/terrain_from_json.py msgid "" -"A giant hill of dirt that looks like you could crawl inside for shelter." +"Extremely resilient floor made from carefully placed rebar and poured " +"concrete, capable of providing protection from the elements. As for the " +"matching roof, it still requires supporting walls, otherwise it may very " +"well cave in." msgstr "" #: lang/json/terrain_from_json.py -msgid "odd fault" +msgid "rebar roof cage" msgstr "" -#. ~ Description for odd fault +#. ~ Description for rebar roof cage #: lang/json/terrain_from_json.py msgid "" -"An unnaturally humanoid-shaped hole, it seems oddly familiar. There's a " -"strange sensation to examine it closer, as if it belongs to you somehow." -msgstr "" - -#: lang/json/terrain_from_json.py -msgid "grave" +"A network of architecturally sound rebar in order to support a floor and " +"roof, looks like it's missing the poured concrete." msgstr "" -#. ~ Description for grave #: lang/json/terrain_from_json.py -msgid "" -"A dirt grave, with some grass growing on it. At least some of the dead do " -"actually rest in peace." +msgid "half-built reinforced concrete floor" msgstr "" -#. ~ Description for grave +#. ~ Description for half-built reinforced concrete floor #: lang/json/terrain_from_json.py msgid "" -"A fresh grave, covered with stones, either to keep something from digging it " -"out or to keep one inside from digging out of it. Two planks mark this " -"place of someone's eternal rest." +"Unfinished series of rebar and poured concrete; the floor hasn't been " +"smoothed and the roof isn't quite filled in yet." msgstr "" #: lang/json/terrain_from_json.py @@ -186442,91 +188467,60 @@ msgid "" msgstr "" #: lang/json/terrain_from_json.py -msgid "pavement" -msgstr "" - -#. ~ Description for pavement -#: lang/json/terrain_from_json.py -msgid "" -"A segment of asphalt, slowly degrading from cracks, frost heaves and lack of " -"maintenance." -msgstr "" - -#: lang/json/terrain_from_json.py -msgid "yellow pavement" +msgid "metal floor" msgstr "" -#. ~ Description for yellow pavement +#. ~ Description for metal floor #: lang/json/terrain_from_json.py msgid "" -"Streaks of carefully aligned yellow paint mark the road to inform drivers " -"not to cross. No one is enforcing these rules anymore." -msgstr "" - -#: lang/json/terrain_from_json.py -msgid "sidewalk" +"High-quality and tough checkered flooring to reduce risk of slips and falls, " +"with a matching roof." msgstr "" -#. ~ Description for sidewalk #: lang/json/terrain_from_json.py -msgid "" -"An area of common poured concrete, damaged by frost heaves and large cracks " -"due to lack of maintenance." +msgid "thump" msgstr "" -#. ~ Description for concrete #: lang/json/terrain_from_json.py -msgid "" -"A newer segment of poured concrete with surface finishes for aesthetics and " -"resistance to freeze-thaw cycles." +msgid "floor" msgstr "" -#. ~ Description for concrete +#. ~ Description for floor #: lang/json/terrain_from_json.py msgid "" -"A newer segment of poured concrete with surface finishes for aesthetics and " -"resistance to freeze-thaw cycles. Covered with a streak of yellow paint." -msgstr "" - -#: lang/json/terrain_from_json.py -msgid "wooden floor" +"Interlocking wooden tiles that are more than likely treated against fire, " +"with wooden posts and beams supporting a roof." msgstr "" -#. ~ Description for wooden floor #: lang/json/terrain_from_json.py -msgid "" -"Wooden floor created from boards, packed tightly together and nailed down. " -"Common in patios." +msgid "primitive floor" msgstr "" +#. ~ Description for primitive floor #: lang/json/terrain_from_json.py -msgid "SMASH!" +msgid "Timber floor and supports, holding up a sod roof." msgstr "" #: lang/json/terrain_from_json.py -msgid "metal floor" +msgid "simple metal floor" msgstr "" -#. ~ Description for metal floor +#. ~ Description for simple metal floor #: lang/json/terrain_from_json.py msgid "" -"High-quality and tough checkered flooring to reduce risk of slips and falls." +"A crudely welded together floor of metal with steel trusses and supporting " +"girders." msgstr "" #: lang/json/terrain_from_json.py -msgid "linoleum tile" +msgid "waxed floor" msgstr "" -#. ~ Description for linoleum tile +#. ~ Description for waxed floor #: lang/json/terrain_from_json.py msgid "" -"A section of flooring made out of a tough, rubbery material. Colored a " -"simple white." -msgstr "" - -#. ~ Description for linoleum tile -#: lang/json/terrain_from_json.py -msgid "A section of flooring made out of a tough, gray, rubbery material." +"Hardwood flooring that has been treated with chemicals to improve slip " +"resistance and sliding, commonly for recreational sports." msgstr "" #: lang/json/terrain_from_json.py @@ -186535,260 +188529,230 @@ msgstr "" #. ~ Description for dirt floor #: lang/json/terrain_from_json.py -msgid "Floor consisting of finely mixed earth that has been tamped down." -msgstr "" - -#: lang/json/terrain_from_json.py -msgid "concrete floor" -msgstr "" - -#. ~ Description for concrete floor -#: lang/json/terrain_from_json.py msgid "" -"A bare and cold concrete floor with matching roof, could still insulate from " -"the outdoors but roof collapse is possible if supporting walls are broken " -"down." +"Floor consisting of finely mixed earth that has been tamped down, with a " +"wooden ceiling above it." msgstr "" -#. ~ Description for concrete floor +#. ~ Description for dirt floor #: lang/json/terrain_from_json.py msgid "" -"A bare and cold concrete floor with a streak of yellow paint, could still " -"insulate from the outdoors but roof collapse is possible if supporting walls " -"are broken down." +"Floor consisting of finely mixed earth that has been tamped down, with " +"thatched roof above it." msgstr "" #: lang/json/terrain_from_json.py -msgid "concrete floor, overhead light" +msgid "metal grate" msgstr "" -#. ~ Description for concrete floor, overhead light +#. ~ Description for metal grate #: lang/json/terrain_from_json.py msgid "" -"A bare and cold concrete floor with a still-functioning light attached to " -"the ceiling above." +"A type of walkway that can be used as protective covering over drains or " +"even used as a filter." msgstr "" #: lang/json/terrain_from_json.py -msgid "small rebar roof cage" +msgid "utility light" msgstr "" -#. ~ Description for small rebar roof cage +#. ~ Description for utility light #: lang/json/terrain_from_json.py msgid "" -"A series of structural support crafted from rebar in order to allow the " -"pouring of concrete for a more stable floor and roof." +"An industrial flood light set up to illuminate the surroundings. Smashing " +"it doesn't seem like it'd produce any worthwhile salvage." msgstr "" #: lang/json/terrain_from_json.py -msgid "SCRRRASH!" +msgid "wax floor" msgstr "" +#. ~ Description for wax floor #: lang/json/terrain_from_json.py -msgid "reinforced concrete floor" +msgid "A floor section made entirely out of wax." msgstr "" -#. ~ Description for reinforced concrete floor #: lang/json/terrain_from_json.py -msgid "" -"Extremely resilient floor made from carefully placed rebar and poured " -"concrete, capable of providing protection from the elements. As for the " -"matching roof, it still requires supporting walls, otherwise it may very " -"well cave in." +msgid "elevator" msgstr "" +#. ~ Description for elevator #: lang/json/terrain_from_json.py -msgid "rebar roof cage" +msgid "The interior section of an elevator." msgstr "" -#. ~ Description for rebar roof cage #: lang/json/terrain_from_json.py -msgid "" -"A network of architecturally sound rebar in order to support a floor and " -"roof, looks like it's missing the poured concrete." +msgid "red floor" msgstr "" +#. ~ Description for red floor #: lang/json/terrain_from_json.py -msgid "half-built reinforced concrete floor" +msgid "A red section of flooring." msgstr "" -#. ~ Description for half-built reinforced concrete floor #: lang/json/terrain_from_json.py -msgid "" -"Unfinished series of rebar and poured concrete; the floor hasn't been " -"smoothed and the roof isn't quite filled in yet." +msgid "green floor" msgstr "" -#. ~ Description for metal floor +#. ~ Description for green floor #: lang/json/terrain_from_json.py -msgid "" -"High-quality and tough checkered flooring to reduce risk of slips and falls, " -"with a matching roof." +msgid "A green section of flooring." msgstr "" #: lang/json/terrain_from_json.py -msgid "floor" +msgid "blue floor" msgstr "" -#. ~ Description for floor +#. ~ Description for blue floor #: lang/json/terrain_from_json.py -msgid "" -"Interlocking wooden tiles that are more than likely treated against fire, " -"with wooden posts and beams supporting a roof." +msgid "A blue section of flooring." msgstr "" #: lang/json/terrain_from_json.py -msgid "primitive floor" +msgid "carpet" msgstr "" -#. ~ Description for primitive floor +#. ~ Description for carpet #: lang/json/terrain_from_json.py -msgid "Timber floor and supports, holding up a sod roof." +msgid "Base carpet!" msgstr "" +#. ~ Description for carpet #: lang/json/terrain_from_json.py -msgid "simple metal floor" +msgid "Soft red carpet." msgstr "" -#. ~ Description for simple metal floor +#. ~ Description for yellow carpet #: lang/json/terrain_from_json.py -msgid "" -"A crudely welded together floor of metal with steel trusses and supporting " -"girders." +msgid "Soft yellow carpet." msgstr "" +#. ~ Description for green carpet #: lang/json/terrain_from_json.py -msgid "waxed floor" +msgid "Soft green carpet." msgstr "" -#. ~ Description for waxed floor +#. ~ Description for purple carpet #: lang/json/terrain_from_json.py -msgid "" -"Hardwood flooring that has been treated with chemicals to improve slip " -"resistance and sliding, commonly for recreational sports." +msgid "Soft purple carpet." msgstr "" -#. ~ Description for dirt floor #: lang/json/terrain_from_json.py -msgid "" -"Floor consisting of finely mixed earth that has been tamped down, with a " -"wooden ceiling above it." +msgid "Carpet" msgstr "" -#. ~ Description for dirt floor +#. ~ Description for Carpet #: lang/json/terrain_from_json.py -msgid "" -"Floor consisting of finely mixed earth that has been tamped down, with " -"thatched roof above it." +msgid "Base concrete carpet!" msgstr "" #: lang/json/terrain_from_json.py -msgid "metal grate" +msgid "industrial red carpet" msgstr "" -#. ~ Description for metal grate +#. ~ Description for industrial red carpet #: lang/json/terrain_from_json.py msgid "" -"A type of walkway that can be used as protective covering over drains or " -"even used as a filter." +"Firm, low-pile, high-durability carpet in a red color, for laying down on " +"bare concrete." msgstr "" #: lang/json/terrain_from_json.py -msgid "utility light" +msgid "industrial yellow carpet" msgstr "" -#. ~ Description for utility light +#. ~ Description for industrial yellow carpet #: lang/json/terrain_from_json.py msgid "" -"An industrial flood light set up to illuminate the surroundings. Smashing " -"it doesn't seem like it'd produce any worthwhile salvage." +"Firm, low-pile, high-durability carpet in a yellow color, for laying down on " +"bare concrete." msgstr "" #: lang/json/terrain_from_json.py -msgid "wax floor" -msgstr "" - -#. ~ Description for wax floor -#: lang/json/terrain_from_json.py -msgid "A floor section made entirely out of wax." +msgid "industrial green carpet" msgstr "" +#. ~ Description for industrial green carpet #: lang/json/terrain_from_json.py -msgid "elevator" +msgid "" +"Firm, low-pile, high-durability carpet in a green color, for laying down on " +"bare concrete." msgstr "" -#. ~ Description for elevator #: lang/json/terrain_from_json.py -msgid "The interior section of an elevator." +msgid "industrial purple carpet" msgstr "" +#. ~ Description for industrial purple carpet #: lang/json/terrain_from_json.py -msgid "red floor" +msgid "" +"Firm, low-pile, high-durability carpet in a purple color, for laying down on " +"bare concrete." msgstr "" -#. ~ Description for red floor +#. ~ Description for carpet #: lang/json/terrain_from_json.py -msgid "A red section of flooring." +msgid "Base metal carpet!" msgstr "" #: lang/json/terrain_from_json.py -msgid "green floor" +msgid "bunker red carpet" msgstr "" -#. ~ Description for green floor +#. ~ Description for bunker red carpet #: lang/json/terrain_from_json.py -msgid "A green section of flooring." +msgid "" +"Firm, low-pile, totally non-flammable carpet in a red color, with an " +"insulation layer beneath." msgstr "" #: lang/json/terrain_from_json.py -msgid "blue floor" +msgid "bunker yellow carpet" msgstr "" -#. ~ Description for blue floor +#. ~ Description for bunker yellow carpet #: lang/json/terrain_from_json.py -msgid "A blue section of flooring." +msgid "" +"Firm, low-pile, totally non-flammable carpet in a yellow color, with an " +"insulation layer beneath." msgstr "" #: lang/json/terrain_from_json.py -msgid "industrial carpet" +msgid "bunker green carpet" msgstr "" -#. ~ Description for industrial carpet +#. ~ Description for bunker green carpet #: lang/json/terrain_from_json.py msgid "" -"Firm, low-pile, high-durability carpet in a neutral gray color, for laying " -"down on bare concrete." +"Firm, low-pile, totally non-flammable carpet in a green color, with an " +"insulation layer beneath." msgstr "" #: lang/json/terrain_from_json.py -msgid "bunker carpet" +msgid "bunker carpet purple" msgstr "" -#. ~ Description for bunker carpet +#. ~ Description for bunker carpet purple #: lang/json/terrain_from_json.py msgid "" -"Firm, low-pile, totally non-flammable carpet in a neutral cream color, with " -"an insulation layer beneath." -msgstr "" - -#. ~ Description for red carpet -#: lang/json/terrain_from_json.py -msgid "Soft red carpet." +"Firm, low-pile, totally non-flammable carpet in a purple color, with an " +"insulation layer beneath." msgstr "" -#. ~ Description for yellow carpet #: lang/json/terrain_from_json.py -msgid "Soft yellow carpet." +msgid "linoleum tile" msgstr "" -#. ~ Description for green carpet +#. ~ Description for linoleum tile #: lang/json/terrain_from_json.py -msgid "Soft green carpet." +msgid "" +"A section of flooring made out of a tough, rubbery material. Colored a " +"simple white." msgstr "" -#. ~ Description for purple carpet +#. ~ Description for linoleum tile #: lang/json/terrain_from_json.py -msgid "Soft purple carpet." +msgid "A section of flooring made out of a tough, gray, rubbery material." msgstr "" #: lang/json/terrain_from_json.py @@ -186825,6 +188789,33 @@ msgid "" "you like the sound of rain on corrugated metal." msgstr "" +#: lang/json/terrain_from_json.py +msgid "dirt" +msgstr "" + +#. ~ Description for dirt +#: lang/json/terrain_from_json.py +msgid "" +"It's dirt. Looks like some fine soil for tillage. Could also be dug out " +"for construction projects." +msgstr "" + +#. ~ Description for sand +#: lang/json/terrain_from_json.py +msgid "" +"A large area of fine sand that could be useful in a number of ways, if it " +"was extracted properly." +msgstr "" + +#: lang/json/terrain_from_json.py +msgid "mud" +msgstr "" + +#. ~ Description for mud +#: lang/json/terrain_from_json.py +msgid "An area of wet, slick mud." +msgstr "" + #: lang/json/terrain_from_json.py msgid "moss" msgstr "" @@ -186834,6 +188825,30 @@ msgstr "" msgid "Moist spongy moss." msgstr "" +#: lang/json/terrain_from_json.py +msgid "clay" +msgstr "" + +#. ~ Description for clay +#: lang/json/terrain_from_json.py +msgid "" +"A field full of malleable clay, suitable for kiln firing if it was extracted " +"properly." +msgstr "" + +#: lang/json/terrain_from_json.py +msgid "mound of clay" +msgstr "" + +#. ~ Description for mound of clay +#: lang/json/terrain_from_json.py +msgid "A mound of clay soil." +msgstr "" + +#: lang/json/terrain_from_json.py +msgid "splosh!" +msgstr "" + #: lang/json/terrain_from_json.py msgid "paper floor" msgstr "" @@ -186843,6 +188858,131 @@ msgstr "" msgid "Floor made of pulpy mass, covered in sticky wasp saliva." msgstr "" +#: lang/json/terrain_from_json.py +msgid "mound of sand" +msgstr "" + +#. ~ Description for mound of sand +#: lang/json/terrain_from_json.py +msgid "A mound of sand." +msgstr "" + +#: lang/json/terrain_from_json.py +msgid "mound of dirt" +msgstr "" + +#. ~ Description for mound of dirt +#: lang/json/terrain_from_json.py +msgid "" +"An area of heaped dirt, not easily traversable. If examined more closely, " +"it's quite favorable for planting seeds and the like." +msgstr "" + +#. ~ Description for mound of dirt +#: lang/json/terrain_from_json.py +msgid "" +"A giant hill of dirt that looks like you could crawl inside for shelter." +msgstr "" + +#: lang/json/terrain_from_json.py +msgid "odd fault" +msgstr "" + +#. ~ Description for odd fault +#: lang/json/terrain_from_json.py +msgid "" +"An unnaturally humanoid-shaped hole, it seems oddly familiar. There's a " +"strange sensation to examine it closer, as if it belongs to you somehow." +msgstr "" + +#: lang/json/terrain_from_json.py +msgid "grave" +msgstr "" + +#. ~ Description for grave +#: lang/json/terrain_from_json.py +msgid "" +"A dirt grave, with some grass growing on it. At least some of the dead do " +"actually rest in peace." +msgstr "" + +#. ~ Description for grave +#: lang/json/terrain_from_json.py +msgid "" +"A fresh grave, covered with stones, either to keep something from digging it " +"out or to keep one inside from digging out of it. Two planks mark this " +"place of someone's eternal rest." +msgstr "" + +#: lang/json/terrain_from_json.py +msgid "pavement" +msgstr "" + +#. ~ Description for pavement +#: lang/json/terrain_from_json.py +msgid "" +"A segment of asphalt, slowly degrading from cracks, frost heaves and lack of " +"maintenance." +msgstr "" + +#: lang/json/terrain_from_json.py +msgid "yellow pavement" +msgstr "" + +#. ~ Description for yellow pavement +#: lang/json/terrain_from_json.py +msgid "" +"Streaks of carefully aligned yellow paint mark the road to inform drivers " +"not to cross. No one is enforcing these rules anymore." +msgstr "" + +#: lang/json/terrain_from_json.py +msgid "sidewalk" +msgstr "" + +#. ~ Description for sidewalk +#: lang/json/terrain_from_json.py +msgid "" +"An area of common poured concrete, damaged by frost heaves and large cracks " +"due to lack of maintenance." +msgstr "" + +#. ~ Description for concrete +#: lang/json/terrain_from_json.py +msgid "" +"A newer segment of poured concrete with surface finishes for aesthetics and " +"resistance to freeze-thaw cycles." +msgstr "" + +#. ~ Description for concrete +#: lang/json/terrain_from_json.py +msgid "" +"A newer segment of poured concrete with surface finishes for aesthetics and " +"resistance to freeze-thaw cycles. Covered with a streak of yellow paint." +msgstr "" + +#: lang/json/terrain_from_json.py +msgid "wooden floor" +msgstr "" + +#. ~ Description for wooden floor +#: lang/json/terrain_from_json.py +msgid "" +"Wooden floor created from boards, packed tightly together and nailed down. " +"Common in patios." +msgstr "" + +#. ~ Description for metal floor +#: lang/json/terrain_from_json.py +msgid "" +"High-quality and tough checkered flooring to reduce risk of slips and falls." +msgstr "" + +#. ~ Description for dirt floor +#: lang/json/terrain_from_json.py +msgid "Floor consisting of finely mixed earth that has been tamped down." +msgstr "" + #: lang/json/terrain_from_json.py msgid "walnut tree" msgstr "" @@ -191078,7 +193218,7 @@ msgstr "" #. ~ Trap-vehicle collision message for trap 'shotgun trap' #: lang/json/trap_from_json.py #: lang/json/trap_from_json.py src/iuse.cpp -#: src/iuse.cpp src/ranged.cpp +#: src/ranged.cpp msgid "Bang!" msgstr "" @@ -194427,6 +196567,120 @@ msgstr "" msgid "Continue trying to fall asleep and don't ask again." msgstr "" +#: src/activity_actor.cpp +msgid "You are too tired to exercise." +msgstr "" + +#: src/activity_actor.cpp +msgid "You are too dehydrated to exercise." +msgstr "" + +#: src/activity_actor.cpp +msgid "Empty your hands first." +msgstr "" + +#: src/activity_actor.cpp +msgid "You cannot train here with a broken arm." +msgstr "" + +#: src/activity_actor.cpp +msgid "You cannot train here with a broken leg." +msgstr "" + +#: src/activity_actor.cpp +msgid "You cannot train freely with a broken limb." +msgstr "" + +#: src/activity_actor.cpp +msgid "" +"Physical effort determines workout efficiency, but also rate of exhaustion." +msgstr "" + +#: src/activity_actor.cpp +msgid "Choose training intensity:" +msgstr "" + +#: src/activity_actor.cpp +msgid "" +"Light excercise comparable in intensity to walking, but more focused and " +"methodical." +msgstr "" + +#: src/activity_actor.cpp +msgid "Moderate" +msgstr "" + +#: src/activity_actor.cpp +msgid "" +"Moderate excercise without excessive exertion, but with enough effort to " +"break a sweat." +msgstr "" + +#: src/activity_actor.cpp +msgid "Active" +msgstr "" + +#: src/activity_actor.cpp +msgid "" +"Active excercise with full involvement. Strenuous, but in a controlled " +"manner." +msgstr "" + +#: src/activity_actor.cpp +msgid "" +"High intensity excercise with maximum effort and full power. Exhausting in " +"the long run." +msgstr "" + +#: src/activity_actor.cpp +msgid "Train for how long (minutes): " +msgstr "" + +#: src/activity_actor.cpp +msgid "You start your workout session." +msgstr "" + +#: src/activity_actor.cpp +msgid "You are exhausted so you finish your workout early." +msgstr "" + +#: src/activity_actor.cpp +msgid "You are dehydrated so you finish your workout early." +msgstr "" + +#. ~ heavy breathing when excercising +#: src/activity_actor.cpp +msgid "yourself huffing and puffing!" +msgstr "" + +#: src/activity_actor.cpp +msgid "You catch your breath for few moments." +msgstr "" + +#: src/activity_actor.cpp +msgid "You get back to your training." +msgstr "" + +#: src/activity_actor.cpp +msgid "You finish your workout session." +msgstr "" + +#: src/activity_actor.cpp +msgid "You have finished your training cycle, keep training?" +msgstr "" + +#: src/activity_actor.cpp +msgid "Stop training." +msgstr "" + +#: src/activity_actor.cpp +msgid "Continue training." +msgstr "" + +#: src/activity_actor.cpp +msgid "Continue training and don't ask again." +msgstr "" + #. ~ Sound of a Rat mutant burrowing! #: src/activity_handlers.cpp msgid "ScratchCrunchScrabbleScurry." @@ -199804,6 +202058,23 @@ msgstr "" msgid "You try to free yourself from the webs, but can't get loose!" msgstr "" +#: src/character.cpp +#, c-format +msgid "The %s breaks free!" +msgstr "" + +#: src/character.cpp +msgid "You free yourself!" +msgstr "" + +#: src/character.cpp +msgid " frees themselves!" +msgstr "" + +#: src/character.cpp +msgid "You try to free yourself, but can't!" +msgstr "" + #: src/character.cpp msgid "You try to escape the pit, but slip back in." msgstr "" @@ -200655,6 +202926,10 @@ msgstr "" msgid "Your body strains under the weight!" msgstr "" +#: src/character.cpp src/player.cpp +msgid "Your biology is not compatible with that healing item." +msgstr "" + #: src/character.cpp #, c-format msgid "Dispose of %s" @@ -208650,6 +210925,10 @@ msgstr "" msgid "You aren't holding something you can reload." msgstr "" +#: src/game.cpp +msgid "You need to put the bag away before trying to wield something from it." +msgstr "" + #: src/game.cpp #, c-format msgid "There's an angry red dot on your body, %s to brush it off." @@ -211198,6 +213477,10 @@ msgstr "" msgid "Creature whitelisted: %s" msgstr "" +#: src/handle_action.cpp +msgid "Start workout?" +msgstr "" + #: src/handle_action.cpp msgid "Commit suicide?" msgstr "" @@ -213417,6 +215700,11 @@ msgid "" " doesn't know the recipe for the %s and can't continue crafting." msgstr "" +#: src/iexamine.cpp +#, c-format +msgid "Use the %s to exercise?" +msgstr "" + #: src/init.cpp msgid "Finalizing" msgstr "" @@ -214415,7 +216703,7 @@ msgid "" "very bad idea." msgstr "" -#: src/item.cpp +#: src/item.cpp src/item_pocket.cpp #, c-format msgid " round of %s" msgid_plural " rounds of %s" @@ -216138,6 +218426,10 @@ msgstr "" msgid "Pocket %d:" msgstr "" +#: src/item_pocket.cpp +msgid "Holds: " +msgstr "" + #: src/item_pocket.cpp msgid "Maximum item length: " msgstr "" @@ -217908,10 +220200,6 @@ msgstr "" msgid "Sokoban" msgstr "" -#: src/iuse.cpp src/iuse_software_minesweeper.cpp -msgid "Minesweeper" -msgstr "" - #: src/iuse.cpp src/iuse_software_lightson.cpp msgid "Lights on!" msgstr "" @@ -222527,6 +224815,15 @@ msgstr "" msgid "Summon" msgstr "" +#: src/magic.cpp +msgid "random creature" +msgstr "" + +#: src/magic.cpp +#, c-format +msgid "Targets under: %dhp become a %s" +msgstr "" + #: src/magic.cpp src/veh_interact.cpp msgid "Range" msgstr "" @@ -222547,6 +224844,10 @@ msgstr "" msgid "Spawned" msgstr "" +#: src/magic.cpp +msgid "Threshold" +msgstr "" + #: src/magic.cpp msgid "Recover" msgstr "" @@ -222599,6 +224900,15 @@ msgstr "" msgid "%s wounds are closing up!" msgstr "" +#: src/magic_spell_effect.cpp +#, c-format +msgid "The %s transforms into a %s." +msgstr "" + +#: src/magic_spell_effect.cpp +msgid "Your target resists transformation." +msgstr "" + #: src/magic_spell_effect.cpp msgid "There is already a vehicle there." msgstr "" @@ -224624,6 +226934,19 @@ msgctxt "memorial_female" msgid "Became wanted by the police!" msgstr "" +#. ~ %s is bodypart +#: src/memorial_logger.cpp +#, c-format +msgctxt "memorial_male" +msgid "Broke his %s." +msgstr "" + +#: src/memorial_logger.cpp +#, c-format +msgctxt "memorial_female" +msgid "Broke her %s." +msgstr "" + #. ~ %s is bodypart #: src/memorial_logger.cpp #, c-format @@ -233554,17 +235877,6 @@ msgstr "" msgid "If true, radiation causes the player to mutate." msgstr "" -#: src/options.cpp -msgid "Z-levels" -msgstr "" - -#: src/options.cpp -msgid "" -"If true, enables several features related to vertical movement, such as " -"hauling items up stairs, climbing downspouts, and flying aircraft. May " -"cause problems if toggled mid-game." -msgstr "" - #: src/options.cpp msgid "Character point pools" msgstr "" diff --git a/lang/po/de.po b/lang/po/de.po index 08c69d286bab3..99717d2d563c1 100644 --- a/lang/po/de.po +++ b/lang/po/de.po @@ -30,7 +30,7 @@ msgid "" msgstr "" "Project-Id-Version: cataclysm-dda 0.E\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-06-23 10:06+0800\n" +"POT-Creation-Date: 2020-06-26 12:50+0800\n" "PO-Revision-Date: 2018-04-26 14:47+0000\n" "Last-Translator: Wuzzy , 2020\n" "Language-Team: German (https://www.transifex.com/cataclysm-dda-translators/teams/2217/de/)\n" @@ -52527,6 +52527,34 @@ msgstr "" "Filzfetzen, die für die Lagerung eng zusammengebunden wurden. Demontiere " "dies, um es auszupacken." +#: lang/json/GENERIC_from_json.py +msgid "bundle of planks" +msgid_plural "bundles of planks" +msgstr[0] "" +msgstr[1] "" + +#. ~ Description for {'str': 'bundle of planks', 'str_pl': 'bundles of +#. planks'} +#: lang/json/GENERIC_from_json.py +msgid "" +"Ten construction planks securely lashed together with a rope. Disassemble " +"to unpack." +msgstr "" + +#: lang/json/GENERIC_from_json.py +msgid "bundle of stout branches" +msgid_plural "bundles of stout branches" +msgstr[0] "" +msgstr[1] "" + +#. ~ Description for {'str': 'bundle of stout branches', 'str_pl': 'bundles of +#. stout branches'} +#: lang/json/GENERIC_from_json.py +msgid "" +"Ten stout branches securely lashed together with a rope. Disassemble to " +"untie them." +msgstr "" + #: lang/json/GENERIC_from_json.py msgid "t-substrate sample" msgid_plural "t-substrate samples" @@ -64792,6 +64820,17 @@ msgstr "" "Ein Metallwasserhahn, der an einem Wassertank für die einfache Entnahme " "angebracht werden kann." +#: lang/json/GENERIC_from_json.py lang/json/vehicle_part_from_json.py +msgid "wooden wheel mount" +msgid_plural "wooden wheel mounts" +msgstr[0] "" +msgstr[1] "" + +#. ~ Description for {'str': 'wooden wheel mount'} +#: lang/json/GENERIC_from_json.py +msgid "A piece of wood with holes suitable for a bike wheel." +msgstr "" + #: lang/json/GENERIC_from_json.py lang/json/vehicle_part_from_json.py msgid "light wheel mount" msgid_plural "light wheel mounts" @@ -113208,8 +113247,8 @@ msgid "" msgstr "" #: lang/json/gun_from_json.py -msgid "MAS 223" -msgid_plural "MAS 223" +msgid "MAS .223" +msgid_plural "MAS .223" msgstr[0] "" msgstr[1] "" @@ -209305,6 +209344,15 @@ msgid "" "extending the time until the food spoils." msgstr "" +#. ~ Description for {'str': 'wooden wheel mount'} +#: lang/json/vehicle_part_from_json.py +msgid "A piece of wood with holes suitable for a bike or motorbike wheel." +msgstr "" + +#: lang/json/vehicle_part_from_json.py +msgid "wooden wheel mount (steerable)" +msgstr "" + #: lang/json/vehicle_part_from_json.py msgid "light wheel mount (steerable)" msgstr "" diff --git a/lang/po/es_AR.po b/lang/po/es_AR.po index 301b8ddb86cab..f51a9ccd08124 100644 --- a/lang/po/es_AR.po +++ b/lang/po/es_AR.po @@ -9,7 +9,7 @@ msgid "" msgstr "" "Project-Id-Version: cataclysm-dda 0.E\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-06-23 10:06+0800\n" +"POT-Creation-Date: 2020-06-26 12:50+0800\n" "PO-Revision-Date: 2018-04-26 14:47+0000\n" "Last-Translator: Noctivagante , 2020\n" "Language-Team: Spanish (Argentina) (https://www.transifex.com/cataclysm-dda-translators/teams/2217/es_AR/)\n" @@ -5258,7 +5258,7 @@ msgid "" "A heavy metal slug used with shotguns to give them the range capabilities of" " a rifle. Extremely damaging but rather inaccurate." msgstr "" -"Una posta pesada utilizada en escopetas para que tengan el alcance de un " +"Es una posta pesada utilizada en escopetas para que tengan el alcance de un " "rifle. Causan muchísimo daño pero son poco precisas." #: lang/json/AMMO_from_json.py @@ -6957,7 +6957,7 @@ msgstr "" #: lang/json/AMMO_from_json.py lang/json/ammunition_type_from_json.py msgid "lead pellets" msgid_plural "lead pelletss" -msgstr[0] "perdigón de plomo" +msgstr[0] "perdigones de plomo" msgstr[1] "perdigones de plomo" #. ~ Description for lead pellets @@ -9125,9 +9125,9 @@ msgid "" " doubles as an equipment belt with a reinforced loop for holding a large " "tool." msgstr "" -"Un cinturón resistente de bombero. Además de mantener tu ropa en su lugar, " -"también sirve llevar herramientas con una presilla reforzada para poner una " -"herramienta grande." +"Es un cinturón resistente de bombero. Además de mantener tu ropa en su " +"lugar, también sirve llevar herramientas con una presilla reforzada para " +"poner una herramienta grande." #: lang/json/ARMOR_from_json.py msgid "judo belt template" @@ -20159,7 +20159,7 @@ msgid "" "possessions during trips, provides a decent amount of storage but hauling it" " around is not exactly comfortable." msgstr "" -"Una valija mediana usada generalmente para transportar ropa y otras " +"Es una valija mediana usada generalmente para transportar ropa y otras " "posesiones durante los viajes. Tiene buena capacidad de almacenamiento pero " "ir arrastrándola por ahí no es ni rápido ni cómodo." @@ -22676,8 +22676,8 @@ msgstr "" #: lang/json/ARMOR_from_json.py msgid "cestus" msgid_plural "cestuses" -msgstr[0] "caestus" -msgstr[1] "caestus" +msgstr[0] "cestus" +msgstr[1] "cesti" #. ~ Description for {'str': 'cestus', 'str_pl': 'cestuses'} #: lang/json/ARMOR_from_json.py @@ -22804,8 +22804,8 @@ msgstr "" #: lang/json/ARMOR_from_json.py msgid "sentinel-lx cloak" msgid_plural "sentinel-lx cloaks" -msgstr[0] "capa sentinel-lx" -msgstr[1] "capas sentinel-lx" +msgstr[0] "capa centinela-lx" +msgstr[1] "capas centinela-lx" #. ~ Description for {'str': 'sentinel-lx cloak'} #: lang/json/ARMOR_from_json.py @@ -22814,8 +22814,8 @@ msgid "" "unnatural shadow. Made from woven graphene, its lightweight and resistant, " "but cannot be repaired" msgstr "" -"Esta capa sentinel-lx hecha de vantablack, cuelga sobre los hombros como una" -" sombra sólida y antinatural. Hecha de grafeno tejido, es liviano y " +"Esta capa centinela-lx hecha de vantablack, cuelga sobre los hombros como " +"una sombra sólida y antinatural. Hecha de grafeno tejido, es liviano y " "resistente, pero no puede ser reparado." #: lang/json/ARMOR_from_json.py @@ -55672,6 +55672,38 @@ msgstr "" "Son pedazos de fieltro, atados para que ocupen menos espacio. Lo tenés que " "desarmar para abrirlo." +#: lang/json/GENERIC_from_json.py +msgid "bundle of planks" +msgid_plural "bundles of planks" +msgstr[0] "paquete de tablas" +msgstr[1] "paquetes de tablas" + +#. ~ Description for {'str': 'bundle of planks', 'str_pl': 'bundles of +#. planks'} +#: lang/json/GENERIC_from_json.py +msgid "" +"Ten construction planks securely lashed together with a rope. Disassemble " +"to unpack." +msgstr "" +"Son diez tablas atadas juntas con una soga. Hay que desarmarlo para " +"desatarlas." + +#: lang/json/GENERIC_from_json.py +msgid "bundle of stout branches" +msgid_plural "bundles of stout branches" +msgstr[0] "paquete de ramas fuertes" +msgstr[1] "paquetes de ramas fuertes" + +#. ~ Description for {'str': 'bundle of stout branches', 'str_pl': 'bundles of +#. stout branches'} +#: lang/json/GENERIC_from_json.py +msgid "" +"Ten stout branches securely lashed together with a rope. Disassemble to " +"untie them." +msgstr "" +"Son diez ramas fuertes atadas juntas con una soga. Hay que desarmarlo para " +"desatarlas." + #: lang/json/GENERIC_from_json.py msgid "t-substrate sample" msgid_plural "t-substrate samples" @@ -68394,8 +68426,8 @@ msgstr "Es un lavarropas chico, diseñado para ser usado en vehículos." #: lang/json/GENERIC_from_json.py lang/json/vehicle_part_from_json.py msgid "programmable autopilot" msgid_plural "programmable autopilots" -msgstr[0] "autopiloto programable" -msgstr[1] "autopilotos programables" +msgstr[0] "piloto automático programable" +msgstr[1] "pilotos automáticos programables" #. ~ Description for {'str': 'programmable autopilot'} #: lang/json/GENERIC_from_json.py @@ -68489,6 +68521,18 @@ msgstr "" "Una canilla de metal que puede ser puesta en un tanque de agua para " "facilitar el uso." +#: lang/json/GENERIC_from_json.py lang/json/vehicle_part_from_json.py +msgid "wooden wheel mount" +msgid_plural "wooden wheel mounts" +msgstr[0] "soporte de madera para ruedas" +msgstr[1] "soportes de madera para ruedas" + +#. ~ Description for {'str': 'wooden wheel mount'} +#: lang/json/GENERIC_from_json.py +msgid "A piece of wood with holes suitable for a bike wheel." +msgstr "" +"Es un pedazo de madera con agujeros adecuados para una rueda de bicicleta." + #: lang/json/GENERIC_from_json.py lang/json/vehicle_part_from_json.py msgid "light wheel mount" msgid_plural "light wheel mounts" @@ -68738,8 +68782,8 @@ msgstr "" #: lang/json/GENERIC_from_json.py msgid "broken sentinel-lx" msgid_plural "broken sentinels-lx" -msgstr[0] "sentinel-lx roto" -msgstr[1] "sentineles-lx rotos" +msgstr[0] "centinela-lx roto" +msgstr[1] "centinelas-lx rotos" #. ~ Description for {'str': 'broken sentinel-lx', 'str_pl': 'broken #. sentinels-lx'} @@ -68748,7 +68792,7 @@ msgid "" "The irreparably broken remains of a Sentinel-lx. Could be gutted for " "valuable parts." msgstr "" -"Son los restos rotos e irreparables de un Sentinel-lx. Puede ser desarmada " +"Son los restos rotos e irreparables de un Centinela-lx. Puede ser desarmada " "para recuperar partes." #: lang/json/GENERIC_from_json.py @@ -80956,8 +81000,8 @@ msgid "" "black body glistens as it oozes its way along the ground. Eye stalks " "occasionally push their way out of the oily mass and look around." msgstr "" -"Una criatura similar a una babosa, de dos metros y medio de largo y ancha " -"como una heladera. Su cuerpo negro brilla mientras va rezumando por el " +"Es una criatura similar a una babosa, de dos metros y medio de largo y ancha" +" como una heladera. Su cuerpo negro brilla mientras va rezumando por el " "suelo. Sus ojos pedunculados salen ocasionalmente de la masa aceitosa y " "miran a su alrededor." @@ -80973,7 +81017,7 @@ msgid "" "A mutated leopard slug, as wide as a golf cart. Venom dripping from its " "fanged maw, it slithers ahead slowly, leaving a trail of glistening slime." msgstr "" -"Una babosa leopardo mutada, tan ancha como un carrito de golf. Le gotea " +"Es una babosa leopardo mutada, tan ancha como un carrito de golf. Le gotea " "veneno de sus fauces con colmillos, repta lentamente hacia adelante, dejando" " un rastro de baba brillante." @@ -83328,8 +83372,8 @@ msgstr "" #: lang/json/MONSTER_from_json.py msgid "Wraitheon Sentinel-lx" msgid_plural "Wraitheon Sentinel-lxs" -msgstr[0] "sentinel-lxs Wraitheon" -msgstr[1] "sentineles-lxs Wraitheon" +msgstr[0] "Centinela-lx Wraitheon" +msgstr[1] "Centinelas-lxs Wraitheon" #. ~ Description for {'str': 'Wraitheon Sentinel-lx'} #: lang/json/MONSTER_from_json.py @@ -91321,8 +91365,8 @@ msgstr "" #: lang/json/TOOL_ARMOR_from_json.py msgid "CRIT S-I G.E.A.R" msgid_plural "CRIT S-I G.E.A.Rs" -msgstr[0] "CRIT S-I G.E.A.R" -msgstr[1] "CRIT S-I G.E.A.R" +msgstr[0] "S-I G.E.A.R CRIT" +msgstr[1] "S-I G.E.A.R CRIT" #. ~ Description for CRIT S-I G.E.A.R #: lang/json/TOOL_ARMOR_from_json.py @@ -103932,8 +103976,8 @@ msgstr "" #: lang/json/TOOL_from_json.py msgid "inactive sentinel-lx" msgid_plural "inactive sentinel-lxs" -msgstr[0] "sentinel-lx inactivo" -msgstr[1] "sentineles-lx inactivos" +msgstr[0] "centinela-lx inactivo" +msgstr[1] "centinelas-lx inactivos" #. ~ Description for {'str': 'inactive sentinel-lx'} #: lang/json/TOOL_from_json.py @@ -104372,8 +104416,8 @@ msgstr "" #: lang/json/TOOL_from_json.py msgid "CRIT Reso-blade" msgid_plural "CRIT Reso-blades" -msgstr[0] "CRIT Reso-cuchilla" -msgstr[1] "CRIT Reso-cuchillas" +msgstr[0] "Reso-cuchilla CRIT" +msgstr[1] "Reso-cuchillas CRIT" #. ~ Description for CRIT Reso-blade #: lang/json/TOOL_from_json.py @@ -120769,10 +120813,10 @@ msgstr "" "AR-15, comercialmente dirigida como arma de defensa hogareña." #: lang/json/gun_from_json.py -msgid "MAS 223" -msgid_plural "MAS 223" -msgstr[0] "MAS 223" -msgstr[1] "MAS 223" +msgid "MAS .223" +msgid_plural "MAS .223" +msgstr[0] "MAS .223" +msgstr[1] "MAS .223" #: lang/json/gun_from_json.py msgid "" @@ -122090,7 +122134,7 @@ msgid "" "lower-recoil alternative to 12 gauge shotguns, it is light and easy to " "manufacture." msgstr "" -"Es una escopeta de cañón de quiebre de un disparo con calibre .410. Diseñada" +"Es una escopeta de cañón basculante de un disparo con calibre .410. Diseñada" " como una alternativa de bajo retroceso a las escopetas 12 gauge, es liviana" " y simple de fabricar." @@ -122833,8 +122877,8 @@ msgstr "todos los cañones" #: lang/json/gun_from_json.py msgid "Elephant gun" msgid_plural "Elephant guns" -msgstr[0] "rifle de caza mayor" -msgstr[1] "rifles de caza mayor" +msgstr[0] "arma de caza mayor" +msgstr[1] "armas de caza mayor" #: lang/json/gun_from_json.py msgid "" @@ -122908,12 +122952,16 @@ msgid "" "function, their solid firepower and reliability in harsh conditions make " "them valuable weapons in this new world." msgstr "" +"Las copias civiles de la icónica Kalashnikov, como esta, fueron fabricadas e" +" importadas por muchas compañías diferentes. Incluso sin la función " +"automática, su sólida potencia de disparo y fiabilidad en condiciones " +"difíciles la hacen armas valoradas en este mundo nuevo." #: lang/json/gun_from_json.py msgid "Mini Draco" msgid_plural "Mini Dracos" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "Mini Draco" +msgstr[1] "Mini Dracos" #: lang/json/gun_from_json.py msgid "" @@ -122921,6 +122969,9 @@ msgid "" "\"pistol\" has seen some popularity with civilian shooters and gangbangers " "alike." msgstr "" +"Es, esencialmente, una AK semiautomática con el cañón corto y sin culata. " +"Esta incómoda \"pistola\" ha sido popular entre los tiradores civiles y " +"bandas por igual" #: lang/json/gun_from_json.py msgid "Mosin-Nagant M44" @@ -123034,14 +123085,17 @@ msgstr "" #: lang/json/gun_from_json.py msgid "AT4" msgid_plural "AT4s" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "AT4" +msgstr[1] "AT4" #: lang/json/gun_from_json.py msgid "" "Mil-Spec rocket launcher. An 84-mm unguided, portable, single-shot " "recoilless smoothbore weapon used primarily by the US military." msgstr "" +"Es un lanzacohetes Mil-Spec. Un arma de 84mm sin guía, portátil, de un " +"disparo solo, sin retroceso y de ánima lisa, usada primariamente por el " +"ejército de los Estados Unidos." #: lang/json/gun_from_json.py msgid "RM103A automagnum" @@ -123175,8 +123229,8 @@ msgstr "" #: lang/json/gun_from_json.py msgid "Beretta 90-two" msgid_plural "Beretta 90-twos" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "Beretta 90-two" +msgstr[1] "Beretta 90-two" #: lang/json/gun_from_json.py msgid "" @@ -123185,6 +123239,9 @@ msgid "" "futuristic-looking design owed in large parts to the polymer underbarrel " "rail cover." msgstr "" +"Es una versión más moderna de la popular serie 92 de Beretta, la 90-two " +"tiene un desempeña casi igual. La mayor diferencia es su diseño más elegante" +" y casi futurista debido a su cobertura de polímero bajo el cañón." #: lang/json/gun_from_json.py msgid "Calico M960" @@ -123233,8 +123290,8 @@ msgstr "" #: lang/json/gun_from_json.py msgid "H&K MP5A2" msgid_plural "H&K MP5A2s" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "H&K MP5A2" +msgstr[1] "H&K MP5A2" #: lang/json/gun_from_json.py msgid "" @@ -123262,12 +123319,19 @@ msgid "" "waned in popularity among special forces due to its decreased terminal " "performance." msgstr "" +"El H&K MP5SD ofrece desempeño subsónico con casi el mismo impacto que el H&K" +" MP5, manteniendo su estabilidad. El silenciador grande reduce mucho el " +"destello de la bocacha, un beneficio para operaciones nocturnas. Ha tenido " +"popularidad con los equipos SWAT, ya que disminuye el riesgo de fuegos y " +"explosiones en laboratorios ilegales. Con el uso más común de armaduras " +"blindadas, el MP5SD ha perdido popularidad entre las fuerzas especiales " +"debido a su potencia terminal menor." #: lang/json/gun_from_json.py msgid "H&K MP5K-PDW" msgid_plural "H&K MP5K-PDWs" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "H&K MP5K-PDW" +msgstr[1] "H&K MP5K-PDW" #: lang/json/gun_from_json.py msgid "" @@ -123277,12 +123341,17 @@ msgid "" "MP5K-PDW model features a shorter barrel, a built-in foregrip, and a side " "folding stock for vehicle or aircraft crew use." msgstr "" +"El Heckler & Koch MP5 es uno de los subfusiles más utilizados en el mundo, y" +" ha sido adoptado por las fuerzas policiales y militares especiales. Su alto" +" grado de precisión y su poco retroceso son elogiados universalmente. El " +"modelo MP5K-PDW tiene un cañón más corto, empuñadura delantera incorporada y" +" culata que se pliega hacia el costado para uso en vehículo." #: lang/json/gun_from_json.py msgid "PTR 603" msgid_plural "PTR 603s" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "PTR 603" +msgstr[1] "PTR 603" #: lang/json/gun_from_json.py msgid "" @@ -123295,12 +123364,20 @@ msgid "" "offers convenient mounting for aftermarket optics, but precludes the use of " "claw mounts." msgstr "" +"El PTR 603 es una copia semiautomática de la famosa serie MP5, producida " +"para el mercado de Estados Unidos por PTR Industries con instrumental " +"Heckler and Koch. Como el MP5, su acción de rodillo de retraso de retroceso " +"ofrece una precisión y disparo más suaves, diferenciándolo de otros " +"subfusiles y pistolas con calibre de carabina. Este modelo está configurado " +"como pistola para evitar las regulaciones costosas e inconvenientes de NFA. " +"Una sección de riel M1913 soldado ofrece una montura conveniente para " +"ópticas de mercados secundarios, pero impide el uso de monturas de garra." #: lang/json/gun_from_json.py msgid "H&K operational briefcase" msgid_plural "H&K operational briefcases" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "maletín operativo H&K" +msgstr[1] "maletines operativos H&K" #: lang/json/gun_from_json.py msgid "" @@ -123312,6 +123389,13 @@ msgid "" "while bodyguarding, this is your gun. When the briefcase is open the MP5 " "may be reloaded or dismounted." msgstr "" +"Es un maletín rígido con un gatillo en la manija y un agujero oculto en el " +"costado. Para disparar la MP5K-PDW interna, hay que apoyar el maletín a la " +"cintura, asegurarse de estar apuntándolo hacia el enemigo y apretar el " +"gatillo. La precisión y la maniobrabilidad están severamente dificultadas, " +"pero si necesitás 'entregar la mercancía' o tener un perfil bajo mientras " +"estás cuidando a alguien, esta es tu arma. Cuando el maletín está abierto, " +"el MP5 puede ser recargado o quitado." #: lang/json/gun_from_json.py msgid "Kel-Tec SUB-2000" @@ -123330,8 +123414,8 @@ msgstr "" #: lang/json/gun_from_json.py msgid "Beretta M9A1" msgid_plural "Beretta M9A1" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "Beretta M9A1" +msgstr[1] "Beretta M9A1" #: lang/json/gun_from_json.py msgid "" @@ -123340,12 +123424,17 @@ msgid "" "enforcement before the advent of commercially available polymer-framed " "handguns and the FBI's adoption of .40S&W." msgstr "" +"Es la icónica pistola 9x19mm. La M9 fue el arma de apoyo estándar de las " +"fuerzas armadas de Estados Unidos desde 1985 hasta recientemente. Fue " +"popular entre las fuerzas de la ley antes de la llegada de las armas de mano" +" de polímero y disponibles comercialmente, y la adopción del .40 S&W por " +"parte del FBI." #: lang/json/gun_from_json.py msgid "Beretta Px4 Storm" msgid_plural "Beretta Px4 Storms" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "Beretta Px4 Storm" +msgstr[1] "Beretta Px4 Storm" #: lang/json/gun_from_json.py msgid "" @@ -123354,6 +123443,10 @@ msgid "" "was also optimized for concealed carry, while maintaining the reliability " "the 92 series is known for." msgstr "" +"Más liviana que la icónica serie 92 de Beretta, la Px4 Storm fue construida " +"utilizando polímero liviano con inserciones de acero. Su forma fue " +"optimizada para poder llevar oculta pero manteniendo la fiabilidad por la " +"cual es famosa la serie 92." #: lang/json/gun_from_json.py msgid "pipe rifle: 9x19mm" @@ -123364,8 +123457,8 @@ msgstr[1] "rifles de caño: 9x19mm" #: lang/json/gun_from_json.py msgid "Luty SMG: 9x19mm" msgid_plural "Luty SMGs: 9x19mm" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "Luty SMG: 9x19mm" +msgstr[1] "Luty SMG: 9x19mm" #: lang/json/gun_from_json.py msgid "" @@ -123375,6 +123468,11 @@ msgid "" "shop, but still very unreliable. This one is chambered for 9x19mm " "cartridges and accepts STEN compatible magazines." msgstr "" +"Es un subfusil de patrón Luty de ánima lisa, construida simplemente de " +"varias partes de acero, usando algunas de las armas de mano más avanzadas. " +"Probablemente sea una de las armas más complejas que se pueden hacer fuera " +"de una fábrica, pero igual es poco fiable. Esta tiene calibre 9x19mm y " +"acepta cargadores compatibles STEN." #: lang/json/gun_from_json.py msgid "STEN" @@ -123459,8 +123557,8 @@ msgstr "" #: lang/json/gun_from_json.py msgid "Glock 18C" msgid_plural "Glock 18Cs" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "Glock 18C" +msgstr[1] "Glock 18C" #: lang/json/gun_from_json.py msgid "" @@ -123468,12 +123566,15 @@ msgid "" "Austria's EKO Cobra unit. It has compensator cuts along its barrel to make " "recoil more manageable." msgstr "" +"Es una variante de disparo selectivo de la Glock 17, originalmente diseñada " +"para la unidad EKO Cobra de Austria. Tiene cortes de compensación en el " +"cañón para hacer más manejable el retroceso." #: lang/json/gun_from_json.py msgid "Kel-Tec PF-9" msgid_plural "Kel-Tec PF-9s" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "Kel-Tec PF-9" +msgstr[1] "Kel-Tec PF-9" #: lang/json/gun_from_json.py msgid "" @@ -123482,24 +123583,30 @@ msgid "" "9x19mm, recoil is best described as unpleasant, and follow-up shots are " "difficult to place quickly." msgstr "" +"La Kel-Tec PF-9 sigue siendo una de las pistolas de apoyo más populares " +"debido a su histórica fiabilidad, bajo costo y ocultabilidad. Con calibre " +"9x19mm, su retroceso se puede describir como desagradable y los disparos " +"posteriores son difíciles de ubicar rápidamente." #: lang/json/gun_from_json.py msgid "M17" msgid_plural "M17s" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "M17" +msgstr[1] "M17" #: lang/json/gun_from_json.py msgid "" "The M17 is a semi-automatic, short recoil operated pistol derived from the " "SIG Sauer P320." msgstr "" +"La M17 es una pistola semiautomática de retroceso corto derivada de la SIG " +"Sauer P320." #: lang/json/gun_from_json.py msgid "Browning Hi-Power 9x19mm" msgid_plural "Browning Hi-Power 9x19mms" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "Browning Hi-Power 9x19mm" +msgstr[1] "Browning Hi-Power 9x19mm" #: lang/json/gun_from_json.py msgid "" @@ -123508,12 +123615,16 @@ msgid "" " Canada and Australia. This is a commercial variant produced by Browning " "Arms in 9x19mm Parabellum." msgstr "" +"La Browning Hi-Power es una arma de mano semiautomática desarrollada poco " +"antes de la segunda guerra mundial. Muy usada desde entonces, sigue en uso " +"en India, Canadá y Australia. Esta es la versión comercial producida por " +"Browning Arms con calibre 9x19mm Parabellum." #: lang/json/gun_from_json.py msgid "Walther P38" msgid_plural "Walther P38s" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "Walther P38" +msgstr[1] "Walther P38" #: lang/json/gun_from_json.py msgid "" @@ -123524,12 +123635,18 @@ msgid "" " more modern firearms such as the Beretta 92 series, and served Germany " "until 2004." msgstr "" +"La Walther P38 es un arma de mano semiautomática adoptada poco antes de la " +"segunda guerra mundial. Desarrollada debido al alto costo de producción de " +"su predecesora, la Luger P08, la P38 también tiene calibre 9mm Parabellum. " +"Este temprano diseño DA/SA de cierre bloqueado introduce características " +"vistas luego en armas más modernas como la serie Beretta 92, y fue usada por" +" Alemania hasta 2004." #: lang/json/gun_from_json.py msgid "Walther PPQ 9mm" msgid_plural "Walther PPQ 9mms" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "Walther PPQ 9mm" +msgstr[1] "Walther PPQ 9mm" #: lang/json/gun_from_json.py msgid "" @@ -123537,12 +123654,15 @@ msgid "" "P99QA, and maintains compatibility with some of its predecessor's " "accessories. This model is chambered in 9x19mm Parabellum." msgstr "" +"La Walther PPQ es una pistola semiautomática desarrollada a partir de la " +"Walther P99QA y que mantiene compatibilidad con algunos accesorios de sus " +"predecesoras. Este modelo tiene calibre 9x19mm Parabellum." #: lang/json/gun_from_json.py msgid "Hi-Point C-9" msgid_plural "Hi-Point C-9s" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "Hi-Point C-9" +msgstr[1] "Hi-Point C-9" #: lang/json/gun_from_json.py msgid "" @@ -123551,12 +123671,16 @@ msgid "" "making said firearms bulky and uncomfortable. Hi-Points have slides made " "with a zinc pot-metal which is relatively fragile compared to steel slides." msgstr "" +"La Hi-Point C-9 es una pistola semiautomática con retroceso simple diseñada " +"por Hi-Point Firearms, que es conocida por hacer armas de fuego baratas y " +"por hacerlas incómodas y voluminosas. Las Hi-Point tiene corredora de zinc " +"que es relativamente frágil comparada con las corredoras de acero." #: lang/json/gun_from_json.py msgid "CZ-75" msgid_plural "CZ-75s" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "CZ-75" +msgstr[1] "CZ-75" #: lang/json/gun_from_json.py msgid "" @@ -123567,12 +123691,19 @@ msgid "" " around the world, with Česká zbrojovka only joining in the 90's. This " "pistol remains wildly popular among competition shooters." msgstr "" +"La CZ-75 es una pistola semiautomática desarrollada en Checoslovaquia y es " +"una de las nueve maravillas originales. Aunque fue diseñada para exportar a " +"países del occidente, fue declarada como secreto de estado. La falta de " +"protección internacional de su patente hizo que muchas copias y variantes " +"fueran producidas y distribuidas por el mundo, con la Česká zbrojovka " +"llegando en la década del 90. Esta pistola sigue siendo popular entre los " +"tiradores de competición." #: lang/json/gun_from_json.py msgid "Walther CCP" msgid_plural "Walther CCPs" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "Walther CCP" +msgstr[1] "Walther CCP" #: lang/json/gun_from_json.py msgid "" @@ -123582,6 +123713,11 @@ msgid "" " accurate than many other pistols, though this may difficult to realize with" " its average trigger and short sight radius." msgstr "" +"La Walther CCP es una pistola semiautomática con retroceso simple de retardo" +" por gas, pensada para llevar oculta. Internamente, es casi idéntica a la " +"clásica de culto H&K P7. Su diseño de cañón fijo la hace potencialmente más " +"precisa que muchas otras pistolas, aunque sea difícil darse cuenta con su " +"gatillo común y su corto radio de mira." #: lang/json/gun_from_json.py msgid "Makarov PM" @@ -123616,8 +123752,8 @@ msgstr "" #: lang/json/gun_from_json.py msgid "BGM-71F TOW" msgid_plural "BGM-71F TOW" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "BGM-71F TOW" +msgstr[1] "BGM-71F TOW" #: lang/json/gun_from_json.py msgid "" @@ -123632,12 +123768,13 @@ msgstr "" #: lang/json/gun_from_json.py msgid "bionic shotgun" msgid_plural "bionic shotguns" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "escopeta biónica" +msgstr[1] "escopetas biónicas" #: lang/json/gun_from_json.py msgid "Bionic one-shot retracting shotgun integrated with your arm." msgstr "" +"Es una escopeta biónica retráctil de un disparo integrada en tu brazo." #: lang/json/gun_from_json.py msgid "laser finger" @@ -123648,8 +123785,8 @@ msgstr[1] "dedos láser" #: lang/json/gun_from_json.py lang/json/mutation_from_json.py msgid "Assault barbs" msgid_plural "Assault barbs" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "Púas de asalto" +msgstr[1] "Púas de asalto" #: lang/json/gun_from_json.py msgid "" @@ -123663,8 +123800,8 @@ msgstr "" #: lang/json/gun_from_json.py msgid "makeshift chemical thrower" msgid_plural "makeshift chemical throwers" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "lanzador químico improvisado" +msgstr[1] "lanzadores químicos improvisados" #: lang/json/gun_from_json.py msgid "" @@ -123686,7 +123823,7 @@ msgid "" " bore barrels. Historically used by egomaniac hunters in Africa, now used " "by their egomaniac descendants in New England." msgstr "" -"Es un arma de cañón de quiebre compuesta por un cañón .30-06 sobre dos " +"Es un arma de cañón basculante compuesta por un cañón .30-06 sobre dos " "cañones calibre 12 gauge suave. Históricamente usada por los cazadores " "egocéntricos en África, ahora es usado por los descendientes egocéntricos en" " New England." @@ -123769,12 +123906,14 @@ msgid "" "A beautifully decorated flintlock pistol. If using this doesn't make you " "feel a pirate, nothing will." msgstr "" +"Es una pistola de chispa hermosamente decorada. Si cuando usás esto no te " +"sentís como un pirata, ninguna otra cosa te lo hará sentir." #: lang/json/gun_from_json.py msgid "flintlock musket" msgid_plural "flintlock muskets" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "mosquete de chispa" +msgstr[1] "mosquetes de chispa" #: lang/json/gun_from_json.py msgid "" @@ -123799,26 +123938,30 @@ msgid "" "has signficantly increased accuracy at the cost of taking even longer to " "reload." msgstr "" +"También conocido como rifle Pennsylvania, esta arma de chispa de cañón largo" +" tiene una precisión muy mejorada aunque tarda bastante en ser recargada." #: lang/json/gun_from_json.py msgid "barb launching organ" msgid_plural "barb launching organs" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "órgano lanzapúas" +msgstr[1] "órganos lanzapúas" #: lang/json/gun_from_json.py msgid "A mutated organ capable of launching bony barbs at great speed." -msgstr "" +msgstr "Es un órgano mutado capaz de lanzar púas óseas a gran velocidad." #: lang/json/gun_from_json.py msgid "electric alien frond" msgid_plural "electric alien fronds" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "fronda alienígena eléctrica" +msgstr[1] "frondas alienígenas eléctricas" #: lang/json/gun_from_json.py msgid "Electricity unnaturally arcs from the tips of this alien frond." msgstr "" +"La electricidad salta sobrenaturalmente entre las puntas de esta fronda " +"alienígena." #: lang/json/gun_from_json.py msgid "nail gun" @@ -123831,6 +123974,8 @@ msgid "" "A tool used to drive nails into wood or other material. It could also be " "used as an ad-hoc weapon." msgstr "" +"Es una herramienta usada para meter clavos en la madera u otros materiales. " +"También puede usarse como arma." #: lang/json/gun_from_json.py msgid "Paintball gun" @@ -123860,8 +124005,8 @@ msgstr "" #: lang/json/gun_from_json.py msgid "12-gauge gatling gun" msgid_plural "12-gauge gatling guns" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "ametralladora gatling 12-gauge" +msgstr[1] "ametralladoras gatling 12-gauge" #: lang/json/gun_from_json.py msgid "" @@ -123870,12 +124015,16 @@ msgid "" "six separate barrels make for difficult zeroing. The externally driven " "action means this is much less likely to jam." msgstr "" +"Es una escopeta eléctrica gatling de seis cañones, alimentada con cintas " +"hechas de tela. Incluso estando montada apropiadamente, es una bestia " +"incómoda y los seis cañones separados dificultan la puesta a cero. La acción" +" externa hace que sea mucho menos probable que se trabe." #: lang/json/gun_from_json.py msgid "handmade lever shotgun" msgid_plural "handmade lever shotguns" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "escopeta de palanca casera" +msgstr[1] "escopetas de palanca caseras" #: lang/json/gun_from_json.py msgid "" @@ -123883,12 +124032,15 @@ msgid "" "While still a primitive pipe and 2x4 design, it is a formiddable shotgun in " "it's own right with room for improvement." msgstr "" +"Es una escopeta de acción de palanca corta, hecha en casa, con un pequeño " +"tubo cargador interno. Aunque sigue siendo un primitivo diseño de caño y " +"tabla, es una formidable escopeta con algunos puntos para mejorar." #: lang/json/gun_from_json.py msgid "Browning Auto 5" msgid_plural "Browning Auto 5s" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "Browning Auto 5" +msgstr[1] "Browning Auto 5" #: lang/json/gun_from_json.py msgid "" @@ -123897,6 +124049,11 @@ msgid "" "million made between 1902 and 1998. The recoil tuning mechanism under the " "handguard and long dwell time of the action make for pleasant shooting." msgstr "" +"Esta escopeta de aspecto humilde fue la primera escopeta semiautomática " +"exitosa, y la segunda más exitosa en la historia de Estados Unidos, con más " +"de 2.7 millones fabricados entre 1902 y 1998. El mecanismo de ajuste de " +"retroceso bajo la empuñadura y el tiempo largo de la acción la hacen " +"placentera de disparar." #: lang/json/gun_from_json.py msgid "Kel-Tec KSG" @@ -123918,8 +124075,8 @@ msgstr "" #: lang/json/gun_from_json.py msgid "Kel-Tec KSG-25" msgid_plural "Kel-Tec KSG-25" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "Kel-Tec KSG-25" +msgstr[1] "Kel-Tec KSG-25" #: lang/json/gun_from_json.py msgid "" @@ -123929,6 +124086,11 @@ msgid "" "situations. The big brother of the KSG, it has a longer barrel and longer " "magazine tubes." msgstr "" +"Es una escopeta bullpup de acción de bombeo, la Kel-Tec KSG utiliza un par " +"de tubos cargadores para aumentar su capacidad. Cada tubo debe ser cargado " +"por separado, pero esto brinda la posibilidad de cargar diferente munición " +"para diferentes situaciones. La hermana mayor de la KSG, tiene un cañón más " +"largo y tubos cargadores más grandes." #: lang/json/gun_from_json.py msgid "M1014 shotgun" @@ -123944,12 +124106,16 @@ msgid "" "Combat Shotgun, the Benelli M4 is one of the finest combat shotguns " "available." msgstr "" +"Es la primera escopeta operada por gas de Benelli, con dos pistones para " +"mejorar la fiabilidad con varias cargas y culata colapsable que reduce el " +"largo en casi 20 centímetros. Adoptada en 1999 como la Escopeta de Combate " +"M1014, la Benelli M4 es una de las mejores escopetas de combate disponibles." #: lang/json/gun_from_json.py msgid "Mossberg 500 Field" msgid_plural "Mossberg 500 Field" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "Mossberg 500 Field" +msgstr[1] "Mossberg 500 Field" #: lang/json/gun_from_json.py msgid "" @@ -123957,12 +124123,15 @@ msgid "" " for military use. It is noted for its high durability and low recoil. " "This one is fitted with a 28 inch barrel with sight rib." msgstr "" +"La Mossberg 500 es una serie popular de escopetas de acción de bombeo, " +"comúnmente utilizada por los militares. Es notable por su gran durabilidad y" +" su poco retroceso. Esta tiene un cañón de 70 centímetros con sight rib." #: lang/json/gun_from_json.py msgid "Mossberg 500 Security" msgid_plural "Mossberg 500 Security" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "Mossberg 500 Security" +msgstr[1] "Mossberg 500 Security" #: lang/json/gun_from_json.py msgid "" @@ -123970,12 +124139,15 @@ msgid "" " for military use. It is noted for its high durability and low recoil. " "This one is fitted with an 18.5 inch barrel." msgstr "" +"La Mossberg 500 es una serie popular de escopetas de acción de bombeo, " +"comúnmente utilizada por los militares. Es notable por su gran durabilidad y" +" su poco retroceso. Esta tiene un cañón de 46 centímetros." #: lang/json/gun_from_json.py msgid "Mossberg 590A1" msgid_plural "Mossberg 590A1" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "Mossberg 590A1" +msgstr[1] "Mossberg 590A1" #: lang/json/gun_from_json.py msgid "" @@ -123983,12 +124155,15 @@ msgid "" " 500. It features a heavier barrel, a bayonet lug, and a different magazine" " tube for easier cleaning and maintenance." msgstr "" +"La Mossberg 590A1 es una versión de la Mossberg 500 orientada para militares" +" y policías. Tiene un cañón más pesado, agarradera para bayoneta y un " +"cargador diferente para facilitar la limpieza y mantenimiento." #: lang/json/gun_from_json.py msgid "Mossberg 930 SPX" msgid_plural "Mossberg 930 SPXs" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "Mossberg 930 SPX" +msgstr[1] "Mossberg 930 SPX" #: lang/json/gun_from_json.py msgid "" @@ -123997,6 +124172,10 @@ msgid "" "Affordable pricing and decent ergonomics make this a popular entry-level " "3-gun shotgun." msgstr "" +"Esta versión semiautomática de Mossberg posee un sistema de gas de reducción" +" de retroceso, miras de rifle y un riel picatinny incorporado de fábrica. Su" +" precio accesible y ergonomía decente la hace una escopeta popular de nivel " +"3." #: lang/json/gun_from_json.py msgid "double-barrel pipe shotgun" @@ -124029,8 +124208,8 @@ msgstr "" #: lang/json/gun_from_json.py msgid "Remington 870 Wingmaster" msgid_plural "Remington 870 Wingmaster" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "Remington 870 Wingmaster" +msgstr[1] "Remington 870 Wingmaster" #: lang/json/gun_from_json.py msgid "" @@ -124039,12 +124218,16 @@ msgid "" "agencies alike thanks to its high accuracy and muzzle velocity. This one is" " a 28 inch barreled model for hunting fowl and game." msgstr "" +"Con más de 10 millones fabricadas, la Remington 870 es una de las escopetas " +"más populares del mercado, usada tanto por cazadores como por agencias de " +"fuerzas de la ley gracias a su alta precisión y velocidad de bocacha. Esta " +"tiene un cañón de 70 centímetros para caza mayor y de aves." #: lang/json/gun_from_json.py msgid "Remington 870 MCS" msgid_plural "Remington 870 MCSs" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "Remington 870 MCS" +msgstr[1] "Remington 870 MCS" #: lang/json/gun_from_json.py msgid "" @@ -124054,12 +124237,18 @@ msgid "" "doors you might come across. The grip's design makes for controllable yet " "unpleasant recoil, and the barrel lacks any sights." msgstr "" +"Esta escopeta Remigton 870 con sistema de combate modular es actualmente " +"configurada para operaciones de rompimiento, con un cañón de 25 centímetros " +"y sin culata. Es lo suficientemente chica como para llevar como arma " +"secundaria, específicamente para abrir puertas molestas que te puedas " +"cruzar. El diseño de la empuñadura le da un retroceso desagradable pero " +"controlable, y el cañón no posee miras." #: lang/json/gun_from_json.py msgid "Remington 870 express" msgid_plural "Remington 870 expresses" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "Remington 870 express" +msgstr[1] "Remington 870 express" #: lang/json/gun_from_json.py msgid "" @@ -124068,12 +124257,16 @@ msgid "" "agencies alike thanks to its high accuracy and muzzle velocity. This one is" " an 18.5 inch barreled defensive model." msgstr "" +"Con más de 10 millones fabricadas, la Remington 870 es una de las escopetas " +"más populares del mercado, usada tanto por cazadores como por agencias de " +"fuerzas de la ley gracias a su alta precisión y velocidad de bocacha. Este " +"modelo defensivo tiene un cañón de 46 centímetros." #: lang/json/gun_from_json.py msgid "Remington 1100 competiton" msgid_plural "Remington 1100 competitons" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "Remington 1100 competiton" +msgstr[1] "Remington 1100 competiton" #: lang/json/gun_from_json.py msgid "" @@ -124084,6 +124277,13 @@ msgid "" "the nickel finished, teflon coated competition model, with a full length " "magazine tube and 30 inch barrel." msgstr "" +"Esta escopeta semiautomática posee un sistema de gas compensatorio que puede" +" alimentar una gran variedad de cartuchos y disminuir su retroceso. " +"Introducida en 1963, es la favorita de las fuerzas de la ley, cazadores y " +"tiradores de competencia, y ha sido la escopeta más vendida de autocarga en " +"la historia de Estados Unidos. Este modelo de competición tiene terminación " +"en níquel y cobertura de teflón, con tubo cargador de tamaño completo y " +"cañón de 76 centímetros." #: lang/json/gun_from_json.py msgid "shotgun revolver" @@ -124113,6 +124313,11 @@ msgid "" "Mikhail Kalashnikov, and was popular in open division shotgun competitions " "prior to its ban from import via executive order." msgstr "" +"La Saiga-12 es una escopeta semiautomática diseñada con el mismo modelo de " +"Kalashnikov de los rifles AK47. Utiliza un cargador, en lugar de tener que " +"poner cada cartucho de a uno como en la mayoría de las escopetas. Es uno de " +"los últimos diseños de Mikhail Kalashnikov y fue popular en competiciones de" +" escopetas antes de prohibirse su importación por una orden ejecutiva." #: lang/json/gun_from_json.py msgid "double barrel shotgun" @@ -124147,8 +124352,8 @@ msgstr "" #: lang/json/gun_from_json.py msgid "Cobray Streetsweeper" msgid_plural "Cobray Streetsweepers" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "Cobray Streetsweeper" +msgstr[1] "Cobray Streetsweeper" #: lang/json/gun_from_json.py msgid "" @@ -124158,12 +124363,18 @@ msgid "" " an ejector rod. Its unique design allows for all 12 shells to be fired in " "under 3 seconds, as demonstrated by the ATF technical branch." msgstr "" +"Es menos una escopeta que un revólver graciosamente grande, el Cobray " +"Streetsweeper se vendió poco antes de ser considerada un dispositivo de " +"destrucción. El cilindro está guiado por un contractor giratorio, no puede " +"ser preparado a mano y debe ser eyectado con un palo eyector. Su diseño " +"único le permite disparar los 12 cartuchos en menos de 3 segundos, como fue " +"demostrado por los técnicos de ATF." #: lang/json/gun_from_json.py msgid "Franchi SPAS-12" msgid_plural "Franchi SPAS-12s" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "Franchi SPAS-12" +msgstr[1] "Franchi SPAS-12" #: lang/json/gun_from_json.py msgid "" @@ -124173,12 +124384,16 @@ msgid "" "semi-automatic firearm, with an arm stabilizing hook for one handed " "shooting." msgstr "" +"De apariencia intimidatoria, la SPAS 12 tiene el dudoso honor de ser " +"declarada dispositivo de destrucción y ser prohibida su importación, además " +"de su considerable atractivo. Es una combinación de acción de bombeo y " +"semiautomática, con un brazo de estabilización para disparar con una mano." #: lang/json/gun_from_json.py msgid "Tavor TS12" msgid_plural "Tavor TS12s" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "Tavor TS12" +msgstr[1] "Tavor TS12" #: lang/json/gun_from_json.py msgid "" @@ -124188,12 +124403,17 @@ msgid "" "more like a sci-fi prop gun than a firearm. An integral top rail is " "provided for mounting sights." msgstr "" +"Es una escopeta bullpup de triple cargador y operada por gas fabricada por " +"Israeli Weapon Industries. Es capaz de cargar 15 cartuchos todo en un " +"paquete relativamente pequeño. Como muchos otros diseños de IWI, este parece" +" más un arma de ciencia ficción que un arma de fuego. Un riel integrado " +"superior permite montarle miras." #: lang/json/gun_from_json.py msgid "USAS 12" msgid_plural "USAS 12s" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "USAS 12" +msgstr[1] "USAS 12" #: lang/json/gun_from_json.py msgid "" @@ -124202,12 +124422,16 @@ msgid "" "or drum magazines. The in-line recoil system and sheer weight make for " "pleasant shooting." msgstr "" +"La USAS 12 parece un caricatura medio cuadrada de la M16A2. Al igual que su " +"predecesor Auto Assault-12, es una escopeta de disparo selectivo alimentada " +"con cargadores de tambor o de caja. El sistema de retroceso interno y el " +"peso disminuido la hacen agradable de disparar." #: lang/json/gun_from_json.py msgid "1887 bootleg shotgun" msgid_plural "1887 bootleg shotguns" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "escopeta trucha 1887" +msgstr[1] "escopetas truchas 1887" #: lang/json/gun_from_json.py msgid "" @@ -124217,12 +124441,18 @@ msgid "" "today. This one has a very short barrel, no stock, and would pair nicely " "with a motorcycle jacket and a Harley Davidson." msgstr "" +"Una de las primeras escopetas de repetición comercialmente exitosas, la " +"Winchester 1887 fue realizada específicamente con acción de palanca por " +"pedido de Winchester. Aunque luego fue tapada por el éxito de mejores " +"diseños, la 1887 sigue siendo popular hoy. Esta tiene un cañón muy corto, no" +" tiene culata y hace buena pareja con una campera de motoquero y una Harley " +"Davidson." #: lang/json/gun_from_json.py msgid "M1897 Trench Gun" msgid_plural "M1897 Trench Guns" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "M1897 de Trinchera" +msgstr[1] "M1897 de Trinchera" #: lang/json/gun_from_json.py msgid "" @@ -124233,18 +124463,27 @@ msgid "" " There aren't any more trenches to clear, so the next zombie infested town " "will have to suffice." msgstr "" +"La Winchester 1897 fue una de las primeras escopetas de acción de bombeo " +"exitosas. En esta configuración 'de trinchera' ha sido romantizada como un " +"ícono estadounidense de la Primera Guerra Mundial. Con su cañón tapado, " +"agarradera para bayoneta y bayoneta de 43 centímetros, esta escopeta es " +"innegablemente temida por su apariencia. Ya no hay tantas trincheras para " +"despejar, así que la próxima ciudad infectada de zombis va a tener que ser " +"el objetivo." #: lang/json/gun_from_json.py msgid "makeshift shotgun" msgid_plural "makeshift shotguns" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "escopeta improvisada" +msgstr[1] "escopetas improvisadas" #: lang/json/gun_from_json.py msgid "" "A crude shotgun, composed of two thick steel pipes, an end cap and a nail. " "The lack of sights make this weapon only useful at point-blank range." msgstr "" +"Es una escopeta simple compuesta de dos caños gruesos de acero, una tapa en " +"la punta y un clavo. La ausencia de miras la hacen solamente útil de cerca." #: lang/json/gun_from_json.py msgid "flaregun" @@ -124275,8 +124514,8 @@ msgstr "" #: lang/json/gun_from_json.py msgid "CMES laser cannon" msgid_plural "CMES laser cannons" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "cañón láser CMES" +msgstr[1] "cañones láser CMES" #: lang/json/gun_from_json.py msgid "" @@ -124284,18 +124523,23 @@ msgid "" "rotating-barrel active-cooled rapid-fire laser system, can spray death " "downrange with ease." msgstr "" +"Es un sistema integrado de armas para el el mecatraje exoesqueleto CMES. Un " +"sistema de cañón rotativo de disparo rápido de láser que puede esparcir la " +"muerte con facilidad." #: lang/json/gun_from_json.py msgid "RMES marksman system" msgid_plural "RMES marksman systems" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "sistema francotirador RMES" +msgstr[1] "sistemas francotirador RMES" #: lang/json/gun_from_json.py msgid "" "This is the integral weapon system for the RMES exoskeleton mech-suit, a " "quiet and accurate marksman laser rifle." msgstr "" +"Es un sistema integrado de armas para el el mecatraje exoesqueleto RMES. Un " +"rifle de francotirador silencioso y preciso." #: lang/json/gun_from_json.py msgid "A7 laser rifle" @@ -124310,6 +124554,11 @@ msgid "" "corporate skulduggery. Though the Cataclysm put that on the ash heap of " "history, this weapon can still do the same to your foes." msgstr "" +"Es un rifle láser de última generación, desarrollado por el equipo de " +"investigación y desarrollo \"Aerial Lbas\". Compitió con lo mejor de " +"Rivtech, y hubo rumores sobre trampas empresariales. Aunque el Cataclismo " +"puso eso en la pila de cenizas de la historia, esta arma puede hacer lo " +"mismo con tus enemigos." #: lang/json/gun_from_json.py msgid "V29 laser pistol" @@ -124322,6 +124571,8 @@ msgid "" "This V29 laser pistol was one of the first handheld laser weapons. It is " "larger than most traditional handguns, but displays no recoil whatsoever." msgstr "" +"Esta pistola láser V29 fue una de las primeras armas láser de mano. Es más " +"grande que la mayoría de las pistolas, pero no tiene retroceso." #: lang/json/gun_from_json.py msgid "self bow" @@ -124335,6 +124586,9 @@ msgid "" " the person using it. Weak and wildly inaccurate, it doesn't work that " "well, unfortunately…" msgstr "" +"Es un arco primitivo de una sola pieza de madera, hecho específicamente para" +" la persona que lo está usando. No es potente y poco preciso… " +"lamentablemente, no es muy bueno." #: lang/json/gun_from_json.py msgid "short bow" @@ -124549,6 +124803,9 @@ msgid "" "projectiles instead of the traditional quarrel. Primarily intended for " "hunting small game." msgstr "" +"Es una versión modificada de la ballesta clásica, que te permite utilizar " +"piedras como proyectiles en lugar de los tradicionales pernos. " +"Originalmente, fue pensada para cazar animales pequeños." #: lang/json/gun_from_json.py msgid "pistol crossbow" @@ -124577,6 +124834,8 @@ msgid "" "A slow-loading hand weapon that launches bolts. Bolts fired from this " "weapon have a good chance of remaining intact for re-use." msgstr "" +"Es un arma de mano de recarga lenta que lanza pernos. Los pernos disparados " +"con esta arma tienen una gran probabilidad de no romperse." #: lang/json/gun_from_json.py msgid "composite crossbow" @@ -124645,8 +124904,8 @@ msgstr "" #: lang/json/gun_from_json.py msgid "PPA-5" msgid_plural "PPA-5s" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "PPA-5" +msgstr[1] "PPA-5" #: lang/json/gun_from_json.py msgid "" @@ -124656,6 +124915,12 @@ msgid "" " It was designed to take down heavy vehicles, and was expected to fully " "enter US Army service not long before the Cataclysm." msgstr "" +"Llamado así por sus siglas en inglés de Acelerador Portátil de Plasma Modelo" +" Cinco, desarrollado por Lockheed Martin Corporation. Este dispositivo " +"utiliza bancos de capacitores avanzados par crear un anillo de plasma de " +"hidrógeno supercalentado y acelerarlo a increíble velocidad. Fue diseñado " +"para destruir vehículos grandes y se esperaba que entrara en servicio en el " +"Ejército de Estados Unidos poco antes del Cataclismo." #: lang/json/gun_from_json.py msgid "Boeing XM-P plasma rifle" @@ -124752,14 +125017,16 @@ msgstr "" msgctxt "weapon" msgid "sling" msgid_plural "slings" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "honda" +msgstr[1] "hondas" #: lang/json/gun_from_json.py msgid "" "A leather sling, can launch rocks much further and faster than throwing them" " by hand." msgstr "" +"Es una honda de cuero. Puede lanzar piedras mucho más lejos y rápido que si " +"lo hicieras con la mano." #: lang/json/gun_from_json.py msgctxt "gun_type_type" @@ -124777,18 +125044,22 @@ msgid "" "A forked piece of wood with an elastic band stretched between two of its " "tips. Can launch tiny pebbles and similar things at high speeds." msgstr "" +"Es un pedazo de madera bifurcado con una banda elástica estirada entre las " +"dos puntas. Puede lanzar piedritas y cosas similares a gran velocidad." #: lang/json/gun_from_json.py msgid "staff sling" msgid_plural "staff slings" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "honda con vara" +msgstr[1] "hondas con vara" #: lang/json/gun_from_json.py msgid "" "This staff can launch rocks with a whiping motion that sends them flying " "much further and faster than throwing them." msgstr "" +"Esta vara puede lanzar piedras con un movimiento de látigo que las arroja " +"volando mucho más lejos y rápido que si lo hicieras con la mano." #: lang/json/gun_from_json.py msgid "brace slingshot" @@ -124801,6 +125072,8 @@ msgid "" "A modern slingshot with a wrist brace, allowing it to fire tiny objects " "slightly more forcefully than a simple wooden slingshot." msgstr "" +"Es una gomera moderna con apoyo para la muñeca, que permite disparar " +"pequeños objetos un poco más ferozmente que con una gomera simple de madera." #: lang/json/gun_from_json.py msgid "pneumatic speargun" @@ -124929,20 +125202,22 @@ msgstr "" #: lang/json/gun_from_json.py msgid "vibrating bioblaster" msgid_plural "vibrating bioblasters" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "bioblaster vibrante" +msgstr[1] "bioblasteres vibrantes" #: lang/json/gun_from_json.py msgid "" "You ripped this from a mi-go abomination. You think you should wear gloves " "to reload it. " msgstr "" +"Le sacaste esto a una abominación mi-go. Te parece que deberías usar guantes" +" para recargarlo." #: lang/json/gun_from_json.py msgid "hard-light longbow" msgid_plural "hard-light longbows" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "arco largo de luz dura" +msgstr[1] "arcos largos de luz dura" #: lang/json/gun_from_json.py msgid "" @@ -124953,18 +125228,28 @@ msgid "" "not as powerful as conventional ballistic weapons. It runs off of UPS " "power, and rather efficiently so." msgstr "" +"Es un arco largo de metal blanco, elegante y sin cuerda, construido con " +"materiales ultralivianos de la era atómica. En lugar de usar munición, cada " +"disparo usa proyectores de alta tecnología para crear una cuerda y una " +"flecha de luz dura en el lugar, lo que otorga excelente precisión y " +"penetración de armadura. Igual, al ser un arco, no es tan potente como armas" +" de fuego convencionales. Funciona con un UPS y de manera bastante " +"eficiente." #: lang/json/gun_from_json.py msgid "deployed grenade launcher" msgid_plural "deployed grenade launchers" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "lanzagranadas desplegado" +msgstr[1] "lanzagranadas desplegados" #: lang/json/gun_from_json.py msgid "" "A grenade launcher mounted onto your right-hand wrist. Less powerful than a" " proper grenade launcher, but infinitely more portable, and quick to reload." msgstr "" +"Es un lanzagranadas montado en tu muñeca derecha. Menos potente que un " +"lanzagranadas convencional pero definitivamente más portátil y rápido de " +"recargar." #: lang/json/gun_from_json.py msgid "shoddy laser rifle" @@ -124983,8 +125268,8 @@ msgstr "" #: lang/json/gun_from_json.py msgid "eidolon derringer" msgid_plural "eidolon derringers" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "derringer eidolon" +msgstr[1] "derringeres eidolon" #: lang/json/gun_from_json.py msgid "" @@ -124992,12 +125277,15 @@ msgid "" "operations and close range assassination. It will fire up to four 5x50mm " "penetrators simultaneously, to devastating effect." msgstr "" +"Es un derringer miniatura diseñado como arma de apoyo para operaciones " +"encubiertas y asesinatos a corta distancia. Puede disparar hasta cuatro " +"penetradores 5x50mm de manera simultánea, causando un efecto devastador." #: lang/json/gun_from_json.py msgid "wrist-stunner" msgid_plural "wrist-stunners" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "aturdidor de muñeca" +msgstr[1] "aturdidores de muñeca" #: lang/json/gun_from_json.py msgid "" @@ -125005,12 +125293,15 @@ msgid "" "laser stun gun embed on its wrist is still functional, and can be used when " "connected to an UPS." msgstr "" +"Es una mano robótica cortada de un Centinela Wraitheon. La potente arma de " +"aturdimiento de electroláser metida en su muñeca todavía funciona, y puede " +"ser usada si está conectada a un UPS." #: lang/json/gun_from_json.py msgid "wrist-trilaser" msgid_plural "wrist-trilasers" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "triláser de muñeca" +msgstr[1] "triláseres de muñeca" #: lang/json/gun_from_json.py msgid "" @@ -125018,26 +125309,31 @@ msgid "" "the wraitheon drone it originally belonged too. Can still be fired when " "connected to an UPS." msgstr "" +"Es una potente arma láser de tres cañones, todavía montada en la mano " +"robótica de un dron wraitheon al que pertenecía originalmente. Todavía puede" +" ser disparada si se la conecta a un UPS." #: lang/json/gun_from_json.py msgid "trilaser" -msgstr "" +msgstr "triláser" #: lang/json/gun_from_json.py msgid "bionic skullgun" msgid_plural "bionic skullguns" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "arma craneal biónica" +msgstr[1] "armas craneales biónicas" #: lang/json/gun_from_json.py msgid "Bionic one-shot subdermal .40 pistol integrated with your head." msgstr "" +"Es una pistola biónica .40 de un disparo solo integrada en tu cabeza, bajo " +"la piel." #: lang/json/gun_from_json.py msgid "CRIT .5 LP" msgid_plural "CRIT .5 LPs" -msgstr[0] "" -msgstr[1] "" +msgstr[0] ".5 LP CRIT" +msgstr[1] ".5 LP CRIT" #: lang/json/gun_from_json.py msgid "" @@ -125046,12 +125342,17 @@ msgid "" "compensates for the lack of raw power and yet the gun manages to be " "relatively easy to aim and lightweight due to the superalloy construction." msgstr "" +"Es un arma de apoyo experimental de baja potencia, bajo desarrollo de " +"C.R.I.T R&D. La .5 LP es una pistola láser relativamente débil pero precisa." +" El diseño con dos cañones compensa la falta de poder y de todas maneras el " +"arma logra ser fácil de apuntar y liviana debido a su construcción de " +"superaleación." #: lang/json/gun_from_json.py msgid "CRIT Chain Laser" msgid_plural "CRIT Chain Lasers" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "Láser Encadenado CRIT" +msgstr[1] "Láser Encadenado CRIT" #: lang/json/gun_from_json.py msgid "" @@ -125060,36 +125361,45 @@ msgid "" "drill, this gun is a relatively light weapon for the amount of UPS it eats " "and destruction it can cause." msgstr "" +"Es la comprobada favorita de las forjas infernales de R&D. Basada en un " +"video de investigación sobre tres .5 LP atados juntos en un taladro de mano," +" esta arma es relativamente liviana para la cantidad de UPS que consume y la" +" destrucción que puede causar." #: lang/json/gun_from_json.py msgid "CRIT Laser Carbine" msgid_plural "CRIT Laser Carbines" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "Carabina Láser CRIT" +msgstr[1] "Carabinas Láser CRIT" #: lang/json/gun_from_json.py msgid "" "A short-barrel lightweight laser gun developed by C.R.I.T R&D. Mainly " "developed to test out a new breakthrough in laser weapons." msgstr "" +"Es un arma láser liviana de cañón corto desarrollada por C.R.I.T. R&D. " +"Principalmente desarrollada para probar un nuevo descubrimiento en armas " +"láser." #: lang/json/gun_from_json.py msgid "CRIT Energy Rifle" msgid_plural "CRIT Energy Rifles" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "Rifle de Energía CRIT" +msgstr[1] "Rifles de Energía CRIT" #: lang/json/gun_from_json.py msgid "" "A heavy energy gun developed by C.R.I.T R&D. Mainly developed to test out a" " new breakthrough in hybrid weaponry." msgstr "" +"Es un arma grande de energía desarrollada por C.R.I.T. R&D. Principalmente " +"desarrollada para probar un nuevo descubrimiento en armas híbridas." #: lang/json/gun_from_json.py msgid "CRIT CQB Standard Issue" msgid_plural "CRIT CQB Standard Issues" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "reglamentario CRIT de Pelea en Espacios Cerrados" +msgstr[1] "reglamentarios CRIT de Pelea en Espacios Cerrados" #: lang/json/gun_from_json.py msgid "" @@ -125099,26 +125409,34 @@ msgid "" "user's force and the rugged construction with tonfa-like grip can handle " "bashing in enemy heads." msgstr "" +"Es un simple arma combinada. Es una carabina semiautomática militar con la " +"versatilidad a media distancia del 9mm y la potencia de la escopeta 12 " +"gauge. Para completar más el aspecto de pelea en espacio cerrado, la culata " +"está construida para amplificar la fuerza del usuario y la construcción " +"robusta con una empuñadura similar a un tonfa puede romper la cabeza de un " +"enemigo." #: lang/json/gun_from_json.py msgid "CRIT Fire Glove" msgid_plural "CRIT Fire Gloves" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "Guante de Fuego CRIT" +msgstr[1] "Guantes de Fuego CRIT" #: lang/json/gun_from_json.py msgid "Experimental CQB weapon system under development in C.R.I.T R&D." msgstr "" +"Es un sistema experimantal de pelea en espacios cerrados, bajo desarrollo de" +" C.R.I.T R&D." #: lang/json/gun_from_json.py msgid "blast" -msgstr "" +msgstr "explosión" #: lang/json/gun_from_json.py msgid "pellet gun" msgid_plural "pellet guns" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "arma de aire comprimido" +msgstr[1] "armas de aire comprimido" #: lang/json/gun_from_json.py msgid "" @@ -125128,24 +125446,31 @@ msgid "" "short, but the break action charging system requires some arm strength to " "load a pellet." msgstr "" +"Es un arma de aire sorprendentemente potente que puede usarse para cazar " +"animales pequeños. Los pequeños perdigones de plomo o aleación que puede " +"usar brindan una potencia decente en cada tiro. Es bastante precisa y puede " +"hacer tanto daño como un .22 corto, pero el sistema de carga de cañón " +"basculante requiere un poco de fuerza de brazos para cargar los perdigones." #: lang/json/gun_from_json.py msgid "Plasma Cutter" msgid_plural "Plasma Cutters" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "Cortador de Plasma" +msgstr[1] "Cortadores de Plasma" #: lang/json/gun_from_json.py msgid "" "Experimental cutting tool under development in C.R.I.T R&D. It fires an " "extremely hot wave of plasma that slices into materials." msgstr "" +"Es una herramienta experimental de corte desarrollada por C.R.I.T. R&D. " +"Dispara una onda extremadamente caliente de plasma que corta los materiales." #: lang/json/gun_from_json.py msgid "Rivet Driver" msgid_plural "Rivet Drivers" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "remachadora eléctrica" +msgstr[1] "remachadoras eléctricas" #: lang/json/gun_from_json.py msgid "" @@ -125154,24 +125479,31 @@ msgid "" "firing it out, upon reaching a target, the fragile stake explodes into " "shards inside the target." msgstr "" +"Es una herramienta experimental de doble propósito bajo desarrollo de " +"C.R.I.T R&D. Usa un clavo normal y lo alarga en una fracción de segundo " +"antes de dispararlo, y al alcanzar el objetivo, el frágil proyectil explota " +"en esquirlas dentro." #: lang/json/gun_from_json.py msgid "Line Gun" msgid_plural "Line Guns" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "Cañón Líneal" +msgstr[1] "Cañones Líneales" #: lang/json/gun_from_json.py msgid "" "Experimental high power cutting tool under development in C.R.I.T R&D. It " "fires plasma in a wide line for slicing into dense materials." msgstr "" +"Es una herramienta experimental de corte de alta potencia desarrollada por " +"C.R.I.T. R&D. Dispara plasma en una línea ancha para cortar los materiales " +"densos." #: lang/json/gun_from_json.py msgid "Pulse Rifle" msgid_plural "Pulse Rifles" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "Rifle de Pulso" +msgstr[1] "Rifles de Pulso" #: lang/json/gun_from_json.py msgid "" @@ -125179,22 +125511,27 @@ msgid "" "Great for enclosed spaces and mobs of enemies. Shoots alloy rounds which " "instantly mushroom out upon impact." msgstr "" +"Es un rifle subsónico experimental de tres cañones bajo desarrollo de " +"C.R.I.T R&D. Muy bueno para espacios cerrados y contra grupos de enemigos. " +"Dispara balas de aleación que se abren al impactar." #: lang/json/gun_from_json.py msgid "Ripper" msgid_plural "Rippers" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "Destripador" +msgstr[1] "Destripadores" #: lang/json/gun_from_json.py msgid "" "Experimental EM saw under development in C.R.I.T R&D. Great for distance " "cutting of material." msgstr "" +"Es una herramienta experimental electromagnética desarrollada por C.R.I.T. " +"R&D. Es muy buena para cortar materiales a distancia." #: lang/json/gun_from_json.py msgid "em field saw" -msgstr "" +msgstr "sierra de campo electromagnético" #: lang/json/gun_from_json.py msgid "" @@ -125211,8 +125548,8 @@ msgstr "" #: lang/json/gun_from_json.py msgid "Yeet Cannon" msgid_plural "Yeet Cannons" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "Cañón Sacudidor" +msgstr[1] "Cañones Sacudidores" #: lang/json/gun_from_json.py msgid "" @@ -125220,6 +125557,8 @@ msgid "" "the zombies in your path, all the hulks, the spiders, and those damned mole " "rats." msgstr "" +"Podés sacudir todas las balas con esta joyita. Sacudiles a todos los zombis " +"que se te crucen, los gigantones, las arañas y todas esas ratas de mierda." #: lang/json/gun_from_json.py msgid "fake flamesword" @@ -125281,12 +125620,15 @@ msgid "" "much punch as the best of 'em and rewards the skilled shooter with easily-" "crafted ammunition." msgstr "" +"Esta antigua arma de fuego carece del ritmo de disparo de las armas modernas" +" pero causa tanto daño como la mejor, y premia al tirador habilidoso con una" +" munición fácil de fabricar." #: lang/json/gun_from_json.py msgid "pipe rifle" msgid_plural "pipe rifles" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "rifle de caño" +msgstr[1] "rifles de caño" #: lang/json/gun_from_json.py msgid "" @@ -125295,12 +125637,16 @@ msgid "" "There's no extractor, so it might be slow to reload, and its construction " "makes for poor reliability and longevity." msgstr "" +"Es un arma larga rudimentaria con calibre de munición estándar de rifle, " +"reforzada cerca de la recámara. Contiene una sola bala y tiene un mecanismo " +"rudimentario de disparo. No tiene extractor, así que es un poco lenta para " +"recargar, y su construcción la hace ser poco fiable y poco duradera." #: lang/json/gun_from_json.py msgid "survivor carbine" msgid_plural "survivor carbines" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "carabina de supervivencia" +msgstr[1] "carabinas de supervivencia" #: lang/json/gun_from_json.py msgid "" @@ -125310,12 +125656,17 @@ msgid "" " ideal durability and reliability, but this should still be a serviceable " "weapon, provided you can stay accurate with it." msgstr "" +"Es una carabina de construcción rudimentaria con calibre para munición " +"estándar de rifle, alimentada con cargadores de rifles de servicio. Posee un" +" sistema rudimentario de acción de palanca. Las grandes presiones " +"involucradas y la construcción cuestionable la hacen poco fiable y poco " +"duradera, pero se puede usar, si sos capaz de ser preciso con ella." #: lang/json/gun_from_json.py msgid "antique pistol" msgid_plural "antique pistols" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "pistola antigua" +msgstr[1] "pistolas antiguas" #: lang/json/gun_from_json.py msgid "" @@ -125324,12 +125675,17 @@ msgid "" "and is theoretically sustainable. Range and accuracy are hampered by lack " "of rifling, but this old design is still plenty lethal." msgstr "" +"Esta anticuada pistola de un disparo solo que completa muy bien el uniforme " +"de pirata. Aunque es lenta para cargar, no necesita vainas de latón para " +"poder disparar, y es, teóricamente, ecológica. La distancia y la precisión " +"se ven disminuidas por la ausencia de estriado, pero este viejo diseño igual" +" es bastante letal." #: lang/json/gun_from_json.py msgid "antique revolver" msgid_plural "antique revolvers" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "revólver antiguo" +msgstr[1] "revólveres antiguos" #: lang/json/gun_from_json.py msgid "" @@ -125338,12 +125694,16 @@ msgid "" "fairly lengthy process. Despite its age, this type of weapon would perform" " adequately against most two-legged threats." msgstr "" +"Es un viejo revólver diseñado durante el periodo de la emigración al oeste. " +"Deben cargarse cartuchos de papel de pólvora por cada disparo, lo que " +"resulta un proceso largo. A pesar de su edad, este tipo de armas se " +"desempeña adecuadamente contra la mayoría de amenazas de dos patas." #: lang/json/gun_from_json.py msgid "antique musket" msgid_plural "antique muskets" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "mosquete antiguo" +msgstr[1] "mosquetes antiguos" #: lang/json/gun_from_json.py msgid "" @@ -125353,12 +125713,18 @@ msgid "" "theoretically sustainable to fire. Range and accuracy are hampered by lack " "of rifling, but this time-tested design is plenty lethal." msgstr "" +"Con un diseño antiguo, esta arma larga de ánima lisa se vería más acorde en " +"un campo de batalla de antes de 1850 que en tus manos durante el Cataclismo." +" Es lenta para cargar pero no necesita vainas de latón para poder disparar, " +"y es, teóricamente, ecológica de usar. La distancia y la precisión se ven " +"disminuidas por la ausencia de estriado, pero este diseño que superó el paso" +" del tiempo es bastante letal." #: lang/json/gun_from_json.py msgid "grenade launcher" msgid_plural "grenade launchers" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "lanzagranadas" +msgstr[1] "lanzagranadas" #: lang/json/gun_from_json.py msgid "" @@ -125368,12 +125734,17 @@ msgid "" "personnel. Still deadly against hard or soft targets, depending on what " "cartridges are available." msgstr "" +"Es un viejo lanzagranadas de un disparo, que es parecida a una escopeta " +"recortada. Aunque ya ha sido reemplazado por los lanzadores de bajo el " +"cañón, los modelos dedicados como este han sido usados por las fuerzas de la" +" ley y el personal antidisturbios. Sigue siendo letal contra objetivos " +"suaves o duros, dependiendo del cartucho que esté usando." #: lang/json/gun_from_json.py msgid "automatic grenade launcher" msgid_plural "automatic grenade launchers" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "lanzagranadas automático" +msgstr[1] "lanzagranadas automáticos" #: lang/json/gun_from_json.py msgid "" @@ -125384,12 +125755,19 @@ msgid "" " isn't enough to solve your problems, surely a dozen more are. This must be" " mounted on a frame to be fired, and reloading is a bit slow." msgstr "" +"Este lanzagranadas grande e incómodo parece el hijo de una ametralladora y " +"un mortero. Su calibre es enorme y su acción es simplemente masiva. Un " +"enorme cinturón con cartuchos de granada las cargan en la bandeja, lo que " +"permite lanzar varias granadas en sucesión rápida. Si una granada disparada " +"no es suficiente para resolver tu problema, seguro que una docena lo será. " +"Este lanzagranadas tiene que estar montado en una estructura para ser usado," +" y es lento para recargar." #: lang/json/gun_from_json.py msgid "grenade pistol" msgid_plural "grenade pistols" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "pistola de granadas" +msgstr[1] "pistolas de granadas" #: lang/json/gun_from_json.py msgid "" @@ -125401,12 +125779,19 @@ msgid "" "devastating against hard or soft targets. This could be attached to a " "suitable rifle, if so desired." msgstr "" +"Es una pistola regordeta de un disparo solo con un cañón de calibre grande, " +"adecuada para lanzar granadas y bengalas. Es un arma más conveniente para " +"transportar un lanzagranadas que los que van montados en rifles, común entre" +" las fuerzas especiales. En años más recientes, se vendieron algunas " +"versiones para lanzar bengalas. Con el cartucho adecuado, sería devastadora " +"contra objetivos duros o suaves. Puede ser unido a un rifle compatible, si " +"se quiere." #: lang/json/gun_from_json.py msgid "revolver grenade launcher" msgid_plural "revolver grenade launchers" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "revólver lanzagranadas" +msgstr[1] "revólveres lanzagranadas" #: lang/json/gun_from_json.py msgid "" @@ -125416,12 +125801,18 @@ msgid "" "must be re-wound. Needless to say, six well placed shots is an incredible " "amount of firepower, depending on the cartridges loaded." msgstr "" +"Se parece a un revólver grande de un dibujito animado, este lanzador es " +"capaz de disparar seis granadas en una sucesión rápida. Su enorme cilindro " +"está envuelto por un contractor giratorio, que acelera el disparo pero hace " +"más lenta la recarga, ya que tiene que ser enrollado nuevamente. No hay " +"necesidad de decir pero seis disparos es una increíble cantidad de poder de " +"fuego, dependiendo de los cartuchos usados." #: lang/json/gun_from_json.py msgid "cowboy carbine" msgid_plural "cowboy carbines" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "carabina de cowboy" +msgstr[1] "carabinas de cowboy" #: lang/json/gun_from_json.py msgid "" @@ -125431,12 +125822,17 @@ msgid "" "the number of calibers carried and squeeze more power from their pistol " "ammunition." msgstr "" +"Desde los primeros días de la emigración al oeste, cuando la munición era " +"escasa, los tiradores buscaron compartir munición entre sus armas de apoyo y" +" armas largas. Esta carabina de acción de palanca le permite al tirador " +"reducir el número de calibres llevados y exprimir más potencia de la " +"munición de pistola." #: lang/json/gun_from_json.py msgid "machine pistol" msgid_plural "machine pistols" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "pistola ametralladora" +msgstr[1] "pistolas ametralladora" #: lang/json/gun_from_json.py msgid "" @@ -125445,12 +125841,17 @@ msgid "" " Machine pistols mostly see use by vehicle crewmen or bodygaurds of VIPs. " "Due to its preposterous rate of fire it is difficult to control." msgstr "" +"Esta pistola es una ametralladora chiquita que podés meter en una funda y " +"con la que podés vaciarle el cargador a un velocidad feroz contra enemigos " +"cercanos. Las pistolas ametralladora son usadas mayormente por hombres en " +"vehículos o guardaespaldas. Su absurda velocidad de disparo la hace difícil " +"de controlar." #: lang/json/gun_from_json.py msgid "defensive pistol" msgid_plural "defensive pistols" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "pistola defensiva" +msgstr[1] "pistolas defensivas" #: lang/json/gun_from_json.py msgid "" @@ -125459,12 +125860,16 @@ msgid "" "capable of meeting FBI penetration minimums, the lack of a shoulder stock " "limits its utility." msgstr "" +"Es una pistola moderna ideal para servicio policial, militar o defensa " +"personal, con un cargador de caja extraíble y una acción fiable. A pesar de " +"que su calibre es capaz de cumplir con los requisitos de penetración del " +"FBI, la ausencia de culata de apoyo limita su utilidad." #: lang/json/gun_from_json.py msgid "survivalist carbine" msgid_plural "survivalist carbines" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "carabina de supervivencia" +msgstr[1] "carabinas de supervivencia" #: lang/json/gun_from_json.py msgid "" @@ -125474,12 +125879,17 @@ msgid "" "with duty pistols, allowing one to transition to a more stable weapon " "without carrying extra ammo or magazines." msgstr "" +"Estas pequeñas carabinas comparten la munición y cargadores de las pistolas " +"comunes, lo que la hace más controlable que un rifle normal, y también " +"disminuye el costo de la munición. Debido a su compatibilidad, son buena " +"compañía de pistolas de servicio, permitiendo la transición a un arma más " +"estable sin tener que llevar otros cargadores o munición." #: lang/json/gun_from_json.py msgid "police revolver" msgid_plural "police revolvers" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "revólver de policía" +msgstr[1] "revólveres de policía" #: lang/json/gun_from_json.py msgid "" @@ -125490,6 +125900,12 @@ msgid "" "make for a serviceable sidearm, and there are no magazines for you to lose " "or damage." msgstr "" +"Los revólveres como este, con calibre defensivo, fueron los favoritos de los" +" departamentos de policía por casi un siglo, hasta el tiroteo de Miami en " +"1986. Después de eso, los revólveres de recarga y disparo lento fueron " +"considerados un problema. Igual, la precisión de este modelo y el retroceso " +"moderado la vuelven utilizable y no hay cargadores que puedas perder o " +"romper." #: lang/json/gun_from_json.py msgid "submachine gun" @@ -125505,12 +125921,17 @@ msgid "" "controllable in automatic fire. It feeds from detachable box magazines, " "which are easy to unload into close range targets." msgstr "" +"Esta arma larga compacta con calibre de munición de pistola es perfecta para" +" atacar trincheras, hombres en vehículos, equipos SWAT y fuerzas especiales." +" Aunque no es tan precisa como un rifle apropiado, especialmente a grandes " +"distancias, es muy controlable en disparo automático. Utiliza cargadores de " +"caja extraíbles, lo que la hace fácil de usar con objetivos cercanos." #: lang/json/gun_from_json.py msgid "survivor subgun" msgid_plural "survivor subguns" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "fusil de supervivencia" +msgstr[1] "fusiles de supervivencia" #: lang/json/gun_from_json.py msgid "" @@ -125521,12 +125942,18 @@ msgid "" "their nations well enough, so this should be good for zombies... right? " "Accepts standard pistol ammunition." msgstr "" +"Es un subfusil automático fabricado rudimentariamente, que acepta cargadores" +" estándar de pistola y de subfusil. El cerrojo grande lo hace difícil de " +"apuntar y su construcción cuestionable le da una pobre fiabilidad y " +"longevidad. Algunos diseños similares de desesperación se usaron bastante " +"durante la Segunda Guerra Mundial, así que debería servir contra zombis... " +"¿no?" #: lang/json/gun_from_json.py msgid "hand cannon" msgid_plural "hand cannons" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "cañón de mano" +msgstr[1] "cañones de mano" #: lang/json/gun_from_json.py msgid "" @@ -125536,12 +125963,17 @@ msgid "" "deficiencies. Though tradtionally such magnums are revolvers, this one is a" " magazine fed semi-automatic." msgstr "" +"Esta pistola grande es casi tan pesada como una carabina chica, y casi con " +"la misma potencia. Con el potente calibre magnum, es adecuada para cazar " +"animales de tamaño medio, humanos o para compensar las propias deficiencias." +" Aunque tradicionalmente esos magnum eran revólveres, esta pistola utiliza " +"cargadores y es semiautomática." #: lang/json/gun_from_json.py msgid "magnum levergun" msgid_plural "magnum leverguns" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "arma magnum de palanca" +msgstr[1] "armas magnum de palanca" #: lang/json/gun_from_json.py msgid "" @@ -125551,12 +125983,17 @@ msgid "" "number of calibers carried, and allow you to squeeze more power from " "ammuntion." msgstr "" +"Es una versión moderna del clásico de acción de palanca, este rifle más " +"grande puede usar la poderosa munición magnum de pistola y también munición " +"de pistolas más débiles. Llevar esto con una pistola magnum te permite " +"reducir el número de calibres necesarios y permite también sacarle más " +"potencia a la munición." #: lang/json/gun_from_json.py msgid "handmade magnum carbine" msgid_plural "handmade magnum carbines" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "carabina magnum casera" +msgstr[1] "carabinas magnum caseras" #: lang/json/gun_from_json.py msgid "" @@ -125565,12 +126002,17 @@ msgid "" "with a rudimentary lever action system. Its powerful cartridge and relative" " precision should serve well against zombies and medium game." msgstr "" +"Es una carabina de construcción rudimentaria con calibre para munición de " +"pistolas magnum y pistolas estándar. Utiliza cargadores comerciales de " +"pistola magnum, y tiene un sistema rudimentario de acción de palanca. Su " +"poderoso cartucho y relativa precisión deberían funcionar bien contra zombis" +" y animales medianos." #: lang/json/gun_from_json.py msgid "pipe magnum" msgid_plural "pipe magnums" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "magnum de caño" +msgstr[1] "magnums de caño" #: lang/json/gun_from_json.py msgid "" @@ -125579,12 +126021,16 @@ msgid "" "assembly to fire it. There's no extractor, so it might be slow to reload, " "and its construction makes for poor reliability and longevity." msgstr "" +"Es un arma hecha con un caño robusto, reforzada en la recámara. Contiene una" +" sola bala de pistola magnum o estándar y tiene un mecanismo rudimentario de" +" disparo. No tiene extractor, así que es un poco lenta para recargar, y su " +"construcción la hace ser poco fiable y poco duradera." #: lang/json/gun_from_json.py msgid "hunting magnum" msgid_plural "hunting magnums" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "magnum de caza" +msgstr[1] "magnums de caza" #: lang/json/gun_from_json.py msgid "" @@ -125593,12 +126039,17 @@ msgid "" "revolvers' cylinders can thus chamber both magnum and standard pistol " "ammunition. You could take medium to large game with this hefty piece." msgstr "" +"Las primeras armas de cazadores ayudaron a desarrollar la munición de magnum" +" de este revólver desde los calibres estándar, que necesitaban revólveres " +"más grandes para usar con seguridad. Los cilindros de estos revólveres " +"pueden usar munición tanto de pistolas magnum como estándar. Esta cosa " +"pesada se puede usar contra animales grandes y medianos." #: lang/json/gun_from_json.py msgid "plinker carbine" msgid_plural "plinker carbines" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "carabina de práctica" +msgstr[1] "carabinas de práctica" #: lang/json/gun_from_json.py msgid "" @@ -125607,12 +126058,16 @@ msgid "" "of holding an impressive amount of its small cartridges. You could take " "small game with this, but anything bigger might not even notice." msgstr "" +"Con casi nada de retroceso y munición económica, rifles como este son " +"populares como armas para principiantes. Tiene un cargador integrado capaz " +"de contener una cantidad impresionante de pequeños cartuchos. Se pueden " +"cazar animales pequeños con esto pero algo más grande ni se mosqueará." #: lang/json/gun_from_json.py msgid "target pistol" msgid_plural "target pistols" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "pistola de práctica" +msgstr[1] "pistolas de práctica" #: lang/json/gun_from_json.py msgid "" @@ -125621,12 +126076,16 @@ msgid "" "unsuited for taking on anything but small game, as it is meant to poke holes" " in paper. Accepts box magazines." msgstr "" +"Esta pistola mediana dispara munición de práctica barata y es " +"excepcionalmente popular en prácticas de tiro. Esta pistola no es adecuada " +"para algo más que animales pequeños, y está pensada para hacer agujeros en " +"el papel. Puede usar cargadores de caja." #: lang/json/gun_from_json.py msgid "zip gun" msgid_plural "zip guns" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "arma casera" +msgstr[1] "armas caseras" #: lang/json/gun_from_json.py msgid "" @@ -125636,6 +126095,11 @@ msgid "" "might be slow to reload, and its construction leaves its longevity in " "question." msgstr "" +"Es un arma de fuego rudimentaria de un disparo solo, hecha con componentes " +"hogareños, y con calibre para munición pequeña de práctica. Las armas como " +"esta generalmente aparecen en manos de criminales e insurgentes. No tiene " +"extractor así que es medio lenta para recargar, y su construcción pone en " +"duda su longevidad." #: lang/json/gun_from_json.py msgid "assault rifle" @@ -125651,6 +126115,11 @@ msgid "" "large creatures and light vehicles, this should take care of most of your " "problems out to several hundred meters." msgstr "" +"Un rifle como este, producto de décadas de mejoras, son cómodos, fiables y " +"adaptables. Es un 'rifle de asalto' capaz de brindar disparo semiautomático " +"preciso y ráfagas de disparo automático. Excepto criaturas grandes y " +"vehículos ligeros, esto podría encargarse de la mayoría de tus problemas " +"hasta unos cien metros de distancia." #: lang/json/gun_from_json.py msgid "light machine gun" @@ -125667,12 +126136,18 @@ msgid "" "does allow for a considerable amount of energy to be sent down range. Slow " "to reload." msgstr "" +"Esta ametralladora ligera es un implemento formidable para fuego de " +"supresión, una parte importante de la táctica de los escuadrones. Su " +"alimentación por cinto permite cargar cientos de balas y sus componentes " +"pesados pueden soportar grandes ráfagas de disparos. Aunque no sea tan " +"precisa como un rifle de servicio, una ametralladora ligera permite enviar " +"una cantidad considerable de energía. Es lenta de recargar." #: lang/json/gun_from_json.py msgid "sniper rifle" msgid_plural "sniper rifles" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "rifle francotirador" +msgstr[1] "rifles francotirador" #: lang/json/gun_from_json.py msgid "" @@ -125681,12 +126156,17 @@ msgid "" "interfaces for optics and supports. With care and practice, all should be " "quite capable of eliminating bipedal threats from very safe ranges. " msgstr "" +"Los rifles francotirador satisfacen la necesidad de disparos precisos a los " +"militares, policías y civiles. Los ejemplares modernos tienen cargadores " +"extraíbles y varias monturas para ópticas y soportes. Con cuidado y " +"práctica, cualquiera puede ser capaz de eliminar amenazas bípedas desde " +"distancias muy seguras." #: lang/json/gun_from_json.py msgid "sporter carbine" msgid_plural "sporter carbines" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "carabina deportiva" +msgstr[1] "carabinas deportivas" #: lang/json/gun_from_json.py msgid "" @@ -125697,12 +126177,18 @@ msgid "" "rifles are just as adequate for taking on anything smaller than large game, " "however." msgstr "" +"Aunque a veces se la etiqueta incorrectamente como rifle de asalto, esta " +"común carabina barata que usa cargadores no es capaz de tener disparo " +"automático. Es efectiva casi de la misma manera que un rifle de servicio, la" +" variedad más amplia de componentes y diferentes niveles de mantenimiento la" +" hacen menos fiables que las versiones militares. Estos rifles son adecuados" +" para animales pequeños y medianos." #: lang/json/gun_from_json.py msgid "anti-materiel rifle" msgid_plural "anti-materiel rifles" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "rifle antimaterial" +msgstr[1] "rifles antimaterial" #: lang/json/gun_from_json.py msgid "" @@ -125711,12 +126197,16 @@ msgid "" "material weapon is best suited for blinding tanks, shooting at aircraft, or " "destroying explosives. It feeds from comically oversized magazines." msgstr "" +"Este rifle pesado, grande, intimidatorio y excesivo dispara grandes " +"proyectiles con precisión relativa. Aunque parece un rifle francotirador, " +"esta arma antimaterial es mejor para atacar tanques, aviones y para destruir" +" explosivos. Utiliza cargadores de un tamaño casi humorísticamente gigante.." #: lang/json/gun_from_json.py msgid "elephant rifle" msgid_plural "elephant rifles" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "rifle de caza mayor" +msgstr[1] "rifles de caza mayor" #: lang/json/gun_from_json.py msgid "" @@ -125726,6 +126216,11 @@ msgid "" "recoil is monstruous. You could probably kill anything with this, " "especially if you were to fire both barrels at once." msgstr "" +"Con grabados elegantes, acero azul brillante y madera veteada, este rifle " +"doble de acción de quiebre es casi demasiado linda para usar. Tu hombro te " +"va a pedir que no lo hagas. La recámara es casi tan ancha como para dos " +"dedos y el retroceso es monstruoso. Probablemente puedas matar cualquier " +"cosa con esto, especialmente si podés disparar los dos cañones a la vez." #: lang/json/gun_from_json.py msgid "heavy machine gun" @@ -125741,12 +126236,18 @@ msgid "" "drones are just as susceptible, as are any other 'smaller' threats. Slow to" " reload, incredibly loud, and must be mounted to be fired." msgstr "" +"Esta ametralladora grande y desgarbada, alimentada con cintas, dispara " +"proyectiles enormes y está pensada originalmente como un cambio en las armas" +" antivehiculares. Aunque ya no es adecuada contra tanques o aviones " +"modernos, los vehículos más ligeros o los drones pueden ser susceptibles, " +"como cualquier otra amenaza 'pequeña'. Lenta para recargar, increíblemente " +"ruidosa y debe estar montada para poder disparar." #: lang/json/gun_from_json.py msgid "disposable rocket launcher" msgid_plural "disposable rocket launchers" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "lanzacohetes descartable" +msgstr[1] "lanzacohetes descartables" #: lang/json/gun_from_json.py msgid "" @@ -125756,12 +126257,18 @@ msgid "" " portability and lack of dead weight once expended. Has a backblast, so " "make sure nothing you mind destroying is behind you." msgstr "" +"Construido con fibra de vidrio con miras rudimentarias de plástico y cañón " +"de titanio, esto es un lanzador de un solo misil. Aunque no es tan efectivo " +"como otras armas antiarmaduras, el valor verdadero de este lanzacohetes " +"descartable es su transportabilidad y ausencia de peso muerto una vez " +"utilizado. Tiene una descarga posterior así que asegurate de que no haya " +"nada importante atrás tuyo." #: lang/json/gun_from_json.py msgid "recoilless rocket launcher" msgid_plural "recoilless rocket launchers" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "lanzacohetes sin retroceso" +msgstr[1] "lanzacohetes sin retroceso" #: lang/json/gun_from_json.py msgid "" @@ -125774,12 +126281,20 @@ msgid "" "tremendous backblast, so make sure nothing you mind destroying is behind " "you." msgstr "" +"Este gigantesco tubo pesado tiene un mira óptica compleja y puede lanzar una" +" gran variedad de cargas explosivas, incluyendo misiles comunes o guiados " +"por láser. Dependiendo de la ojiva cargada, esta arma es efectiva contra " +"tanques antiguos, la mayoría de los vehículos, bunkers y personal. Está " +"pensado para ser usada por dos hombres así que la recarga es un poco lenta. " +"Su diseño sin retroceso permite un rango y daño superiores, pero tiene una " +"tremenda descarga trasera, así que asegurate que no haya nada importante " +"atrás tuyo." #: lang/json/gun_from_json.py msgid "double-barrel shotgun" msgid_plural "double-barrel shotguns" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "escopeta de doble cañón" +msgstr[1] "escopetas de doble cañón" #: lang/json/gun_from_json.py msgid "" @@ -125788,6 +126303,10 @@ msgid "" " Shotshells come in many varieties, and shotguns are suitable for anything " "from some large game to small birds." msgstr "" +"Es un antigua escopeta de doble cañón con un gatillo para cada uno. Los " +"dispares seguidos son increíblemente rápidos pero la recarga es lenta. Los " +"cartuchos vienen en una gran variedad y las escopetas son adecuadas para " +"cualquier cosa desde animales grandes hasta pájaros pequeños." #: lang/json/gun_from_json.py msgid "" @@ -125796,12 +126315,17 @@ msgid "" "there are no extractors or ejectors. Shotshells come in many varieties, and" " shotguns are suitable for anything from some large game to small birds." msgstr "" +"Esta escopeta rudimentaria de doble cañón con un gatillo para cada uno. Los " +"dispares seguidos son increíblemente rápidos pero la recarga es lenta ya que" +" no tienen extractores o eyectores. Los cartuchos vienen en una gran " +"variedad y las escopetas son adecuadas para cualquier cosa desde animales " +"grandes hasta pájaros pequeños." #: lang/json/gun_from_json.py msgid "sporting shotgun" msgid_plural "sporting shotguns" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "escopeta deportiva" +msgstr[1] "escopetas deportivas" #: lang/json/gun_from_json.py msgid "" @@ -125811,12 +126335,17 @@ msgid "" "varieties, and shotguns are suitable for anything from some large game to " "small birds." msgstr "" +"Con un diseño simple y popular, las escopetas deportivas son usadas por las " +"fuerzas de la ley, civiles y ocasionalmente militares. Su cargador interno " +"es pequeño comparado con los estándares modernos, y la velocidad de recarga " +"es baja. Los cartuchos vienen en una gran variedad y las escopetas son " +"adecuadas para cualquier cosa desde animales grandes hasta pájaros pequeños." #: lang/json/gun_from_json.py msgid "tactical shotgun" msgid_plural "tactical shotguns" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "escopeta táctica" +msgstr[1] "escopetas tácticas" #: lang/json/gun_from_json.py msgid "" @@ -125827,12 +126356,18 @@ msgid "" "known for being somewhat finicky, these can be tuned to run with some " "reliability." msgstr "" +"Es una escopeta alimentada por un cargador extraíble, más que nada orientada" +" hacia civiles entusiastas de las armas. Con rieles y una apariencia negra " +"amenazante, esta clase de escopeta no parece ser deportiva. Los cargadores " +"reducen los tiempos de recarga que generalmente poseen las escopetas. Aunque" +" es conocida por ser un poco quisquillosa, puede llegar a funcionar con " +"bastante fiabilidad." #: lang/json/gun_from_json.py msgid "slam-fire shotgun" msgid_plural "slam-fire shotguns" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "escopeta slamfire" +msgstr[1] "escopetas slamfire" #: lang/json/gun_from_json.py msgid "Ichaival" @@ -125846,24 +126381,28 @@ msgid "" " string. It has gold and silver ornaments on it, as well as an ornate " "Raven." msgstr "" +"Ichaival, el arco de Odín. Se dice que dispara 10 flechas por cada vez que " +"se usa. Tiene adornos de oro y plata, y también un Cuervo." #: lang/json/gun_from_json.py msgid "Druid composite bow" msgid_plural "Druid composite bows" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "arco compuesto de Druida" +msgstr[1] "arcos compuestos de Druida" #: lang/json/gun_from_json.py msgid "" "A bow made of multiple materials to maximize energy efficiency. There are " "two Druid runes embedded at the tips." msgstr "" +"Es un arco hecho con varios materiales para maximizar la eficiencia de la " +"energía. Tiene dos runas Druidas incrustadas en las puntas." #: lang/json/gun_from_json.py msgid "M47A1 Techno-Medusa" msgid_plural "M47A1 Techno-Medusae" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "M47A1 Techno-Medusa" +msgstr[1] "M47A1 Techno-Medusae" #: lang/json/gun_from_json.py msgid "" @@ -125872,12 +126411,16 @@ msgid "" "reliability with smaller cartridges, it is not as accurate as dedicated " "caliber revolvers due to freebore." msgstr "" +"Es una actualización de la M47 Medusa de Phillips & Rodgers, mejorada " +"mágicamente. Es un revólver multicalibre ideal para supervivientes. Aunque " +"la Tecnomancia mejora la fiabilidad con cartuchos más chicos, no es tan " +"precisa como revólveres de calibre dedicado debido a su freebore." #: lang/json/gun_from_json.py msgid "gunblade" msgid_plural "gunblades" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "espadarma" +msgstr[1] "espadarmas" #: lang/json/gun_from_json.py msgid "" @@ -125885,34 +126428,43 @@ msgid "" "barrel pump shotgun attached to the blade's spine for finishing blows or a " "first strike." msgstr "" +"Con una construcción similar a la falcata, esta cuchilla inclinada hacia " +"adelante tiene una escopeta de cañón corto unida a la cuchilla para dar el " +"primer golpe o el golpe final." #: lang/json/gun_from_json.py msgid "shotcestus" msgid_plural "shotcesti" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "shotcestus" +msgstr[1] "shotcesti" #: lang/json/gun_from_json.py msgid "" "A sawn-off double-barrel shotgun mounted on a metal cestus. The lack of a " "stock to absorb recoil means some strength is required to fire." msgstr "" +"Es una escopeta doble cañón recortada, montada sobre un cestus metálico. La " +"falta de culata para absorber el retroceso hace que se necesite un poco de " +"fuerza para poder usarla." #: lang/json/gun_from_json.py msgid "Woodbow" msgid_plural "Woodbows" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "Maderarco" +msgstr[1] "Maderarcos" #: lang/json/gun_from_json.py msgid "" "A magically conjured ornate recurve bow of solid flexible wood. A matching " "conjured wooden arrow appears when you draw the string back for firing." msgstr "" +"Es un arco recurvo de madera flexible, adornado y conjurado mágicamente. La " +"correspondiente flecha de madera conjurada aparecerá cuando tires de la " +"cuerda para usar el arco." #: lang/json/gun_from_json.py msgid "Fake gun that fires barbed javelins." -msgstr "" +msgstr "Es una pistola falsa que dispara jabalinas con púas." #: lang/json/gunmod_from_json.py msgid "pipe combination gun shotgun" @@ -125925,8 +126477,8 @@ msgid "" "The integrated underbarrel shotgun of a pipe combination gun which holds two" " shots. It's irremovable." msgstr "" -"Es una escopeta integrada bajocañón de un arma de caño combinada, que tiene " -"dos disparos. No se puede quitar." +"Es el bajocañón integrado de escopeta de un arma de caño combinada, que " +"contiene dos disparos. No se puede quitar." #: lang/json/gunmod_from_json.py msgid "underbarrel" @@ -126169,8 +126721,8 @@ msgstr "" #: lang/json/gunmod_from_json.py msgid "Leadworks magazine adapter" msgid_plural "Leadworks magazine adapters" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "adaptador de cargador Leadworks" +msgstr[1] "adaptadores de cargador Leadworks" #: lang/json/gunmod_from_json.py msgid "" @@ -126239,7 +126791,7 @@ msgid "" "A small extension above the grip which an arrow rests upon while being " "aimed. Improves accuracy with no drawbacks." msgstr "" -"Es una pequeña extensión sobre el mango que permite apoyar la flecha " +"Es una pequeña extensión sobre la empuñadura que permite apoyar la flecha " "mientras se está apuntando. Mejora la precisión y no tiene desventajas." #: lang/json/gunmod_from_json.py @@ -126262,8 +126814,9 @@ msgid "" "A counterweight placed forward of the bow's grip allows for greater " "accuracy. Aside from increased weight and size, there are no drawbacks." msgstr "" -"Es un contrapeso puesto delante del mango del arco, que permite mejorar la " -"precisión. La única desventaja que tiene es el incremento del peso." +"Es un contrapeso puesto delante de la empuñadura del arco, que permite " +"mejorar la precisión. La única desventaja que tiene es el incremento del " +"peso." #: lang/json/gunmod_from_json.py msgid "stabilizer" @@ -126309,8 +126862,8 @@ msgstr "amortiguación" #: lang/json/gunmod_from_json.py msgid "belt clip" msgid_plural "belt clips" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "clip de cinturón" +msgstr[1] "clips de cinturón" #: lang/json/gunmod_from_json.py msgid "" @@ -126320,36 +126873,41 @@ msgid "" " to carry with the chamber empty or select a firearm with a very heavy " "trigger pull." msgstr "" +"Es un clip para cinturón que se pone en la empuñadura o al costado de una " +"pistola para facilitar la 'portación a la mexicana', que es llevar encima el" +" arma sin funda. No ofrece ninguna protección para el gatillo así que se " +"recomienda a los usuarios llevarla con la recámara vacía o llevar un arma " +"con un gatillo difícil de apretar." #: lang/json/gunmod_from_json.py msgctxt "gun_type_type" msgid "rugerlcp" -msgstr "" +msgstr "rugerlcp" #: lang/json/gunmod_from_json.py msgctxt "gun_type_type" msgid "kp32" -msgstr "" +msgstr "kp32" #: lang/json/gunmod_from_json.py msgctxt "gun_type_type" msgid "kp3at" -msgstr "" +msgstr "kp3at" #: lang/json/gunmod_from_json.py msgctxt "gun_type_type" msgid "kpf9" -msgstr "" +msgstr "kpf9" #: lang/json/gunmod_from_json.py msgctxt "gun_type_type" msgid "cop_38" -msgstr "" +msgstr "cop_38" #: lang/json/gunmod_from_json.py msgctxt "gun_type_type" msgid "moss_brownie" -msgstr "" +msgstr "moss_brownie" #: lang/json/gunmod_from_json.py msgid "shortened barrel" @@ -126378,18 +126936,22 @@ msgid "" "Testmod for UPS drain on mods, this should never spawn, if you see this, " "it's a bug. 50x more UPS drain." msgstr "" +"Mod de prueba para el uso de UPS en modificaciones, esto no debería aparecer" +" nunca, si lo ves, es un bug. Multiplica por 50 el uso de UPS." #: lang/json/gunmod_from_json.py msgid "Power shot" msgid_plural "Power shots" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "Tiro poderoso" +msgstr[1] "Tiros poderosos" #: lang/json/gunmod_from_json.py msgid "" "This is a pseudo item -- the builtin part of a fusion blaster for the " "maximum power firing mode." msgstr "" +"Es un pseudo objeto -- es la parte de la pistola de fusión para el modo de " +"máxima potencia." #: lang/json/gunmod_from_json.py msgid "brass catcher" @@ -126408,12 +126970,12 @@ msgstr "" #: lang/json/gunmod_from_json.py msgid ".300 AAC Blackout AR-15 conversion kit" msgid_plural ".300 AAC Blackout AR-15 conversion kits" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "kit de conversión .300 AAC Blackout AR-15" +msgstr[1] "kits de conversión .300 AAC Blackout AR-15" #: lang/json/gunmod_from_json.py msgid "A complete AR-15 upper assembly with a .300 AAC Blackout barrel." -msgstr "" +msgstr "Es un montaje superior AR-15 con cañón .300 AAC Blackout." #: lang/json/gunmod_from_json.py msgctxt "gun_type_type" @@ -126423,22 +126985,22 @@ msgstr "ar15" #: lang/json/gunmod_from_json.py msgctxt "gun_type_type" msgid "m4a1" -msgstr "" +msgstr "m4a1" #: lang/json/gunmod_from_json.py msgctxt "gun_type_type" msgid "m16a4" -msgstr "" +msgstr "m16a4" #: lang/json/gunmod_from_json.py msgctxt "gun_type_type" msgid "h&k416a5" -msgstr "" +msgstr "h&k416a5" #: lang/json/gunmod_from_json.py msgctxt "gun_type_type" msgid "m27iar" -msgstr "" +msgstr "m27iar" #: lang/json/gunmod_from_json.py msgid "lightweight replacement furniture" @@ -126451,19 +127013,19 @@ msgid "" "A set of lightweight composite grips and furniture that reduces a firearm's " "weight, and as a consequence, its handling and melee damage." msgstr "" -"Es un equipo de mangos compuestos y accesorios que reducen el peso de un " -"arma de fuego, y como consecuencia reduce también su manejo y daño para el " -"cuerpo a cuerpo." +"Es un equipo de empuñaduras compuestas y accesorios que reducen el peso de " +"un arma de fuego, y como consecuencia reduce también su manejo y daño para " +"el cuerpo a cuerpo." #: lang/json/gunmod_from_json.py msgid "grip" -msgstr "mango" +msgstr "empuñadura" #: lang/json/gunmod_from_json.py msgid "ergonomic grip" msgid_plural "ergonomic grips" -msgstr[0] "mango ergonómico" -msgstr[1] "mangos ergonómicos" +msgstr[0] "empuñadura ergonómica" +msgstr[1] "empuñaduras ergonómicas" #: lang/json/gunmod_from_json.py msgid "" @@ -126475,13 +127037,15 @@ msgstr "" #: lang/json/gunmod_from_json.py msgid "breacher grip" msgid_plural "breacher grips" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "empuñadura breacher" +msgstr[1] "empuñaduras breacher" #: lang/json/gunmod_from_json.py msgid "" "A very uncomfortable straight grip. Clearly not intended for regular use." msgstr "" +"Es una empuñadura recta muy incómoda. Claramente, no está hecha para un uso " +"regular." #: lang/json/gunmod_from_json.py msgid "beam scatterer" @@ -126506,14 +127070,17 @@ msgstr "lente" #: lang/json/gunmod_from_json.py msgid "focusing lens" msgid_plural "focusing lenses" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "lente de enfoque" +msgstr[1] "lentes de enfoque" #: lang/json/gunmod_from_json.py msgid "" "A set of optics to concentrate the laser beam on a smaller focus point. " "This increases range and damage output, but complicates targeting." msgstr "" +"Es un conjunto de ópticas para concentrar un rayo láser a un punto de " +"enfoque más pequeño. Esto incrementa la distancia y el daño, pero dificulta " +"la precisión." #: lang/json/gunmod_from_json.py msgid "electrolaser conversion" @@ -126534,14 +127101,16 @@ msgstr "" #: lang/json/gunmod_from_json.py msgid "effective emitter" msgid_plural "effective emitters" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "emisor efectivo" +msgstr[1] "emisores efectivos" #: lang/json/gunmod_from_json.py msgid "" "A set of electronics to optimize emitter workcycle and increase overall " "energy efficiency. Decreases power consumption." msgstr "" +"Es un grupo de electrónicos que mejoran el trabajo del emisor e incrementan " +"la eficiencia general de la energía. Disminuye el consumo de energía." #: lang/json/gunmod_from_json.py msgid "emitter" @@ -126558,6 +127127,8 @@ msgid "" "A capacitor with a higher energy density increases range and damage; at the " "cost of a markedly increased power consumption." msgstr "" +"Es un capacitor con energía de alta densidad que incrementa el alcance y el " +"daño, pero consume bastante más energía." #: lang/json/gunmod_from_json.py msgid "speedloader chute" @@ -126575,7 +127146,7 @@ msgstr "" #: lang/json/gunmod_from_json.py msgid "loading port" -msgstr "" +msgstr "puerto de carga" #: lang/json/gunmod_from_json.py msgid "match trigger" @@ -126613,17 +127184,17 @@ msgstr "" #: lang/json/gunmod_from_json.py msgctxt "gun_type_type" msgid "ar15_retool_300blk" -msgstr "" +msgstr "ar15_retool_300blk" #: lang/json/gunmod_from_json.py msgctxt "gun_type_type" msgid "ar_pistol" -msgstr "" +msgstr "ar_pistol" #: lang/json/gunmod_from_json.py msgctxt "gun_type_type" msgid "oa93" -msgstr "" +msgstr "oa93" #: lang/json/gunmod_from_json.py msgid "lightning link" @@ -126663,8 +127234,8 @@ msgstr "" #: lang/json/gunmod_from_json.py msgid "replaceable furniture kit" msgid_plural "replaceable furniture kits" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "kit de accesorios reemplazables" +msgstr[1] "kits de accesorios reemplazables" #: lang/json/gunmod_from_json.py msgid "" @@ -126673,16 +127244,21 @@ msgid "" "furniture so that it could be easily changed if needed. This allows " "installing any kind of more advanced grips or other furniture." msgstr "" +"Es un kit que consiste en varias partes de acero y plástico que cuando se " +"instala, modificará permanentemente y reemplazará parcialmente algunos " +"accesorios del arma, para que puedan ser cambiados si es necesario. Esto " +"permite instalar cualquier clase de empuñaduras más avanzadas u otros " +"accesorios." #: lang/json/gunmod_from_json.py msgid "grip mount" -msgstr "" +msgstr "montura de empuñadura" #: lang/json/gunmod_from_json.py msgid "side mount" msgid_plural "side mounts" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "montura lateral" +msgstr[1] "monturas laterales" #: lang/json/gunmod_from_json.py msgid "" @@ -126690,16 +127266,19 @@ msgid "" "permanently installed onto almost any weapon, along with some fasteners. " "Ideal for bringing out your inner tacticool on older guns." msgstr "" +"Es una montura plástica para poner un accesorio de riel, diseñada para ser " +"instalada permanentemente en casi cualquier arma, y tiene unos ganchos. " +"Ideal para hacerte el tacticool con tus armas viejas." #: lang/json/gunmod_from_json.py msgid "rail mount" -msgstr "" +msgstr "montura de riel" #: lang/json/gunmod_from_json.py msgid "sights mount" msgid_plural "sights mounts" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "montura de mira" +msgstr[1] "monturas de mira" #: lang/json/gunmod_from_json.py msgid "" @@ -126708,12 +127287,15 @@ msgid "" "with some fasteners. Ideal for bringing out your inner tacticool on older " "guns." msgstr "" +"Es una montura plástica para poner una mira, diseñada para ser instalada " +"permanentemente en casi cualquier arma excepto lanzadores y pistolas, y " +"tiene unos ganchos. Ideal para hacerte el tacticool con tus armas viejas." #: lang/json/gunmod_from_json.py msgid "launcher sights mount" msgid_plural "launcher sights mounts" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "montura de mira de lanzador" +msgstr[1] "monturas de mira de lanzador" #: lang/json/gunmod_from_json.py msgid "" @@ -126721,12 +127303,15 @@ msgid "" "installed onto almost any launcher, along with some fasteners. Ideal for " "bringing out your inner tacticool on rocket launchers." msgstr "" +"Es una montura plástica para poner una mira, diseñada para ser instalada " +"permanentemente en casi cualquier lanzador, y tiene unos ganchos. Ideal para" +" hacerte el tacticool con tus lanzamisiles." #: lang/json/gunmod_from_json.py msgid "pistol sights mount" msgid_plural "pistol sights mounts" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "montura de mira para pistola" +msgstr[1] "monturas de mira para pistola" #: lang/json/gunmod_from_json.py msgid "" @@ -126734,12 +127319,15 @@ msgid "" "installed onto almost any pistol, along with some fasteners. Ideal for " "bringing out your inner tacticool on pocket pistols." msgstr "" +"Es una montura plástica para poner una mira, diseñada para ser instalada " +"permanentemente en casi cualquier pistola, y tiene unos ganchos. Ideal para " +"hacerte el tacticool con tus pistolas." #: lang/json/gunmod_from_json.py msgid "replaceable stock kit" msgid_plural "replaceable stock kits" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "kit de culata reemplazable" +msgstr[1] "kits de culata reemplazable" #: lang/json/gunmod_from_json.py msgid "" @@ -126749,16 +127337,22 @@ msgid "" "simply attach a stock mount if gun had no stock to start with. This allows " "easy installation of any kind of more advanced stocks." msgstr "" +"Es un kit que consiste en varias partes de acero que cuando se instalan, " +"quitaran la culata fija original del arma, instalará una nueva montura de " +"culata, modificará la culata original para que sea compatible y volver a " +"ponerla en el arma, o solamente unirá la montura de culata si el arma no " +"tenía una originalmente. Este permite una fácil instalación de cualquier " +"culata más avanzada." #: lang/json/gunmod_from_json.py msgid "stock mount" -msgstr "" +msgstr "montura de culata" #: lang/json/gunmod_from_json.py msgid "bottom mount" msgid_plural "bottom mounts" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "montura de abajo" +msgstr[1] "monturas de abajo" #: lang/json/gunmod_from_json.py msgid "" @@ -126766,10 +127360,13 @@ msgid "" "be permanently installed onto almost any weapon, along with some fasteners." " Ideal for bringing out your inner tacticool on older guns." msgstr "" +"Es una montura plástica para poner un accesorio bajo el cañón, diseñada para" +" ser instalada permanentemente en casi cualquier arma, y tiene unos ganchos." +" Ideal para hacerte el tacticool con tus armas viejas." #: lang/json/gunmod_from_json.py msgid "underbarrel mount" -msgstr "" +msgstr "montura de bajocañón" #: lang/json/gunmod_from_json.py msgid "ported barrel" @@ -126808,8 +127405,8 @@ msgstr "" #: lang/json/gunmod_from_json.py msgid "modified muzzle brake" msgid_plural "modified muzzle brakes" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "freno modificado de boca" +msgstr[1] "frenos modificados de boca" #: lang/json/gunmod_from_json.py msgid "" @@ -126818,6 +127415,11 @@ msgid "" " This one was modified and customized to mount on pretty much any firearm " "other than launchers, if you so want." msgstr "" +"El freno de boca redirige los gases de la combustión de la pólvora para " +"compensar el levantamiento de la bocacha. Disminuye el retroceso pero " +"aumenta el tamaño y el ruido, y reduce un poco la precisión. Este fue " +"modificado y personalizado para poner en casi cualquier arma de fuego " +"excepto lanzadores." #: lang/json/gunmod_from_json.py msgid "homemade suppressor" @@ -126836,12 +127438,19 @@ msgid "" "when attached, will interfere with your ability to aim down the base sights " "of the gun." msgstr "" +"Es un silenciador casero hecho con un caño y trapos. Mientras los trapos " +"aguanten, reducirá el ruido y el destello de la bocacha generados al " +"disparar. Las armas de fuego son extremadamente ruidosas y puede dañar tu " +"audición si no usás protección; un silenciador reduce el ruido a niveles " +"seguros, y también reduce un poco el retroceso y la velocidad de bocacha. " +"Este silenciador es grande y, cuando se lo pone, interfiere tu habilidad " +"para apuntar." #: lang/json/gunmod_from_json.py msgid "'solvent trap' suppressor" msgid_plural "'solvent trap' suppressors" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "silenciador 'trampa de solvente'" +msgstr[1] "silenciadores 'trampa de solvente'" #: lang/json/gunmod_from_json.py msgid "" @@ -126853,12 +127462,19 @@ msgid "" " attached, will interfere with your ability to aim down the base sights of " "the gun." msgstr "" +"Es un filtro de automotores adaptado rudimentariamente para coincidir con el" +" cañón del arma, armando un silenciador ilegal y no registrado. Qué bueno " +"que no parece haber ningún agente de la ATF que te pueda arrestar. Aunque es" +" similar en diseño a un silenciador real, no fue diseñado para grandes " +"presiones y eventualmente perderá efectividad. El filtro instalado es grande" +" y cuando está puesto, interferirá tu habilidad de apuntar con las miras del" +" arma." #: lang/json/gunmod_from_json.py msgid "soda bottle silencer" msgid_plural "soda bottle silencers" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "silenciador de botella" +msgstr[1] "silenciadores de botella" #: lang/json/gunmod_from_json.py msgid "" @@ -126870,6 +127486,13 @@ msgid "" "suppressor is large and, when attached, will interfere with your ability to " "aim down the base sights of the gun." msgstr "" +"Es un 'silenciador' hecho con una botella de gaseosa de 2 litros, algunas " +"botellas más chicas que son como bafles, y un buen pedazo de cinta adhesiva." +" Los disparos son estrepitosamente fuertes y pueden dañar tu audición si no " +"tenés protección y podés estar seguro de que esto no te va a proteger las " +"orejas. Lo que puede hacer es permitirte disparar un tiro o dos con un poco " +"menos de ruido. Este simple silenciador es grande y, cuando está puesto, " +"interferirá en tu habilidad para apuntar con las miras del arma." #: lang/json/gunmod_from_json.py msgid "RK6S34 suppressor" @@ -126898,6 +127521,8 @@ msgid "" "A large suppressor designed to work with shotguns. It's a lot more complex " "than a suppressor for a pistol or rifle." msgstr "" +"Es un silenciador grande diseñado para funcionar en escopetas. Es mucho más " +"complejo que un silenciador para pistola o para rifle." #: lang/json/gunmod_from_json.py msgid "suppressor" @@ -126920,8 +127545,8 @@ msgstr "" #: lang/json/gunmod_from_json.py msgid "MP5SD integral suppressor" msgid_plural "MP5SD integral suppressors" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "silenciador integrado MP5SD" +msgstr[1] "silenciadores integrados MP5SD" #: lang/json/gunmod_from_json.py msgid "" @@ -126930,6 +127555,10 @@ msgid "" "most ammo subsonic at the cost of terminal performance. It is not as " "effective as modern designs." msgstr "" +"Es un silenciador integrado para la H&K MP5SD. Su recámara de gran expansión" +" desacelera la munición a través de varios puertos en el cañón, volviendo " +"subsónica a la mayoría de la munición a cambio de un poco de desempeño " +"terminal. No es tan efectivo como los diseños modernos." #: lang/json/gunmod_from_json.py msgid "compact suppressor" @@ -126948,8 +127577,8 @@ msgstr "" #: lang/json/gunmod_from_json.py msgid "rail-mounted crossbow" msgid_plural "rail-mounted crossbows" -msgstr[0] "ballesta montable de riel" -msgstr[1] "ballestas montables de riel" +msgstr[0] "ballesta montable en riel" +msgstr[1] "ballestas montables en riel" #: lang/json/gunmod_from_json.py msgid "" @@ -126966,8 +127595,8 @@ msgstr "riel" #: lang/json/gunmod_from_json.py msgid "modified rail-mounted crossbow" msgid_plural "modified rail-mounted crossbows" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "ballesta modificada montable en riel" +msgstr[1] "ballestas modificadas montables en riel" #: lang/json/gunmod_from_json.py msgid "" @@ -126976,6 +127605,10 @@ msgid "" " and customized to mount on pretty much any weapon other than pistols and " "SMGs, if you so want." msgstr "" +"Es un kit para poner un par de brazos de ballesta y riel de tiro en el cañón" +" de un arma larga. Permite disparar pernos de ballestas. Esta ha sido " +"modificada y personalizada para poder ponerse en casi cualquier arma si " +"querés, excepto pistolas y subfusiles." #: lang/json/gunmod_from_json.py msgid "offset iron sights" @@ -126995,8 +127628,8 @@ msgstr "" #: lang/json/gunmod_from_json.py msgid "modified offset iron sights" msgid_plural "modified offset iron sights" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "mirilla modificada metálica de compensación" +msgstr[1] "mirillas modificadas metálicas de compensación" #: lang/json/gunmod_from_json.py msgid "" @@ -127004,6 +127637,10 @@ msgid "" "other modification prevents use of the primary sights. This one was " "modified and customized to mount on pretty much any weapon, if you so want." msgstr "" +"Es un equipo alternativo de mirillas metálicas montadas a 45º para usar " +"cuando una mira o alguna otra modificación impide el uso de las mirillas " +"primarias. Esta ha sido modificada y personalizada para poner en casi " +"cualquier arma." #: lang/json/gunmod_from_json.py msgid "offset sight rail" @@ -127020,14 +127657,16 @@ msgstr "" #: lang/json/gunmod_from_json.py msgid "modified offset sight rail" msgid_plural "modified offset sight rails" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "mirilla metálica de riel de compensación" +msgstr[1] "mirillas metálicas de riel de compensación" #: lang/json/gunmod_from_json.py msgid "" "An additional rail set at 45° for attaching a secondary optic. This one was" " modified and customized to mount on pretty much any weapon, if you so want." msgstr "" +"Es un grupo de rieles adicionales en 45° para poner una óptica secundaria. " +"Esta ha sido modificada y personalizada para poner en casi cualquier arma." #: lang/json/gunmod_from_json.py msgid "rail laser sight" @@ -127063,8 +127702,8 @@ msgstr "" #: lang/json/gunmod_from_json.py msgid "modified gyroscopic stabilizer" msgid_plural "modified gyroscopic stabilizers" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "estabilizador giroscópico modificado" +msgstr[1] "estabilizadores giroscópicos modificados" #: lang/json/gunmod_from_json.py msgid "" @@ -127073,6 +127712,10 @@ msgid "" "one was modified and customized to mount on pretty much any weapon other " "than pistols, if you so want." msgstr "" +"Es una unidad avanzada que se ata con una correa al costado del arma de " +"fuego y reduce la vibración, lo que reduce mucho el retroceso e incrementa " +"levemente la precisión. Este fue modificado y personalizado para poner en " +"casi cualquier arma excepto pistolas." #: lang/json/gunmod_from_json.py msgid "five pin bow sight" @@ -127187,12 +127830,15 @@ msgid "" "A 3-18x44 rifle scope. It is adjustable for windage and elevation in 1/10th" " mrad increments and is remarkably small and light for its magnification." msgstr "" +"Es una mira de rifle 3-18x44. Se puede ajustar por desviación y elevación en" +" incrementos de 1/10mo mrad y es notablemente pequeña y liviana por su " +"aumento." #: lang/json/gunmod_from_json.py msgid "modified rifle scope" msgid_plural "modified rifle scopes" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "mira modificada para rifle" +msgstr[1] "miras modificadas para rifle" #: lang/json/gunmod_from_json.py msgid "" @@ -127201,6 +127847,10 @@ msgid "" "This one was modified and customized to mount on pretty much any weapon " "other than pistols and SMGs, if you so want." msgstr "" +"Es una mira de rifle 3-18x44. Se puede ajustar por desviación y elevación en" +" incrementos de 1/10mo mrad y es notablemente pequeña y liviana por su " +"aumento. Esta fue modificada y personalizada para poner en casi cualquier " +"arma excepto pistolas y subfusiles." #: lang/json/gunmod_from_json.py msgid "ACOG scope" @@ -127213,12 +127863,14 @@ msgid "" "A 4x32 TA01 Advanced Combat Optical Gunsight with a tritium illuminated " "crosshair." msgstr "" +"Es una Mira Óptica Avanzada de Combate 4x32 TA01 con puntos de mira de " +"tritio iluminados." #: lang/json/gunmod_from_json.py msgid "modified ACOG scope" msgid_plural "modified ACOG scopes" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "mira modificada ACOG" +msgstr[1] "miras modificadas ACOG" #: lang/json/gunmod_from_json.py msgid "" @@ -127226,6 +127878,9 @@ msgid "" "crosshair. This one was modified and customized to mount on pretty much any" " weapon other than pistols, if you so want." msgstr "" +"Es una Mira Óptica Avanzada de Combate 4x32 TA01 con puntos de mira de " +"tritio iluminados. Esta fue modificada y personalizada para poner en casi " +"cualquier arma excepto pistolas." #: lang/json/gunmod_from_json.py msgid "RS1219 scope" @@ -127255,6 +127910,9 @@ msgid "" "crosshairs. Not as good as the ones made before the Cataclysm. Increases " "weight but improves accuracy." msgstr "" +"Es una simple mira telescópica hecha a mano, es como un telescopio con " +"puntos de mira. No tan buena como las fabricadas antes del Cataclismo. " +"Incrementa el peso pero mejora la precisión." #: lang/json/gunmod_from_json.py msgid "telescopic pistol sight" @@ -127322,8 +127980,8 @@ msgstr "" #: lang/json/gunmod_from_json.py msgid "folding wire stock" msgid_plural "folding wire stocks" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "culata de alambre plegable" +msgstr[1] "culatas de alambre plegables" #: lang/json/gunmod_from_json.py msgid "" @@ -127331,6 +127989,9 @@ msgid "" "before use. It's somewhat wobbly but is better than nothing at all. " "Increases the time needed to wield the weapon." msgstr "" +"Es una culata de alambre plegable que puede ocupar muy poco espacio pero " +"necesita desplegarse antes de usar. Es medio endeble pero es mejor que nada." +" Incrementa el tiempo necesario para empuñar el arma." #: lang/json/gunmod_from_json.py msgid "pistol stock" @@ -127341,6 +128002,8 @@ msgstr[1] "culatas para pistola" #: lang/json/gunmod_from_json.py msgid "An add-on stock for handguns considerably improving control of recoil." msgstr "" +"Es una culata para pistolas que mejora considerablemente el control del " +"retroceso." #: lang/json/gunmod_from_json.py msgid "recoil stock" @@ -127390,8 +128053,8 @@ msgstr "" #: lang/json/gunmod_from_json.py msgid "modified bipod" msgid_plural "modified bipods" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "bípode modificado" +msgstr[1] "bípodes modificados" #: lang/json/gunmod_from_json.py msgid "" @@ -127401,6 +128064,11 @@ msgid "" "was modified and customized to mount on pretty much any weapon, if you so " "want." msgstr "" +"Los bípodes son usados comúnmente en los rifles y ametralladoras, para " +"proveer de un apoyo adelante y reducir el movimiento. Aunque mejoran mucho " +"el manejo del retroceso, solo pueden usarse en ciertas superficies y son " +"lentos para armar. Este ha sido modificado y personalizado pra poner en casi" +" cualquier arma." #: lang/json/gunmod_from_json.py msgid "combination gun shotgun" @@ -127413,14 +128081,14 @@ msgid "" "The integrated underbarrel shotgun of a combination gun which holds two " "shots. It's irremovable." msgstr "" -"Es una escopeta integrada bajocañón de un arma combinada, que tiene dos " -"disparos. No se puede quitar." +"Es el bajocañón integrado de escopeta de un arma combinada, que contiene dos" +" disparos. No se puede quitar." #: lang/json/gunmod_from_json.py msgid "factory handguard" msgid_plural "factory handguards" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "guardamano de fábrica" +msgstr[1] "guardamanos de fábrica" #: lang/json/gunmod_from_json.py msgid "" @@ -127428,32 +128096,35 @@ msgid "" " as efficient as a proper forward grip or bipod at controlling recoil, but " "it's better than nothing." msgstr "" +"Es una empuñadura extraíble que se vuelve estándar en armas sin riel. No es " +"tan eficiente como una empuñadura delantera apropiada o un bípode para " +"controlar el retroceso, pero es mejor que nada." #: lang/json/gunmod_from_json.py msgctxt "gun_type_type" msgid "fs2000" -msgstr "" +msgstr "fs2000" #: lang/json/gunmod_from_json.py msgid "forward grip" msgid_plural "forward grips" -msgstr[0] "mango vertical" -msgstr[1] "mangos verticales" +msgstr[0] "empuñadura delantera" +msgstr[1] "empuñaduras delanteras" #: lang/json/gunmod_from_json.py msgid "" "A grip placed forward on the barrel allows for greater control. Not as " "effective as a bipod but usable under all conditions." msgstr "" -"Es un mango que se pone en el cañón del arma, lo que permite tener mayor " -"control. No es tan efectivo como un bípode pero se puede usar bajo cualquier" -" condición." +"Es una empuñadura que se pone en el cañón del arma, lo que permite tener " +"mayor control. No es tan efectiva como un bípode pero se puede usar bajo " +"cualquier condición." #: lang/json/gunmod_from_json.py msgid "modified forward grip" msgid_plural "modified forward grips" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "empuñadura delantera modificada" +msgstr[1] "empuñaduras delanteras modificadas" #: lang/json/gunmod_from_json.py msgid "" @@ -127461,6 +128132,10 @@ msgid "" "effective as a bipod but usable under all conditions. This one was modified" " and customized to mount on pretty much any weapon, if you so want." msgstr "" +"Es una empuñadura que se pone en el cañón del arma, lo que permite tener " +"mayor control. No es tan efectivo como un bípode pero se puede usar bajo " +"cualquier condición. Esta fue modificada y personalizada para poner en casi " +"cualquier arma." #: lang/json/gunmod_from_json.py msgid "integrated bayonet" @@ -127487,44 +128162,50 @@ msgid "" "The integrated second shotgun magazine of the Kel-Tec KSG which holds 7 " "shots. It's irremovable." msgstr "" -"Es el cargador de escopeta secundario integrado del Kel-Tec KSG, que " -"contiene 7 disparos. No se puede quitar." +"Es un segundo cargador de escopeta integrado de Kel-Tec KSG, que contiene 7 " +"disparos. No se puede quitar." #: lang/json/gunmod_from_json.py msgid "KSG-25 second magazine" msgid_plural "KSG-25 second magazines" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "cargador secundario KSG-25" +msgstr[1] "cargadores secundarios KSG-25" #: lang/json/gunmod_from_json.py msgid "" "The integrated second shotgun magazine of the Kel-Tec KSG-25 which holds 12 " "shots. It's irremovable." msgstr "" +"Es un segundo cargador de escopeta integrado de Kel-Tec KSG-25, que contiene" +" 12 disparos. No se puede quitar." #: lang/json/gunmod_from_json.py msgid "TS12 second magazine" msgid_plural "TS12 second magazines" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "cargador secundario TS12" +msgstr[1] "cargadores secundarios TS12" #: lang/json/gunmod_from_json.py msgid "" "The integrated second shotgun magazine of the Tavor TS12 which holds 5 " "shots. It's irremovable." msgstr "" +"Es un segundo cargador de escopeta integrado de Tavor TS12, que contiene 5 " +"disparos. No se puede quitar." #: lang/json/gunmod_from_json.py msgid "TS12 third magazine" msgid_plural "TS12 third magazines" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "cargador terciario TS12" +msgstr[1] "cargadores terciarios TS12" #: lang/json/gunmod_from_json.py msgid "" "The integrated third shotgun magazine of the Tavor TS12 which holds 5 shots." " It's irremovable." msgstr "" +"Es un tercer cargador de escopeta integrado de Tavor TS12, que contiene 5 " +"disparos. No se puede quitar." #: lang/json/gunmod_from_json.py msgid "underbarrel laser sight" @@ -127553,6 +128234,9 @@ msgid "" "The Lemat revolver is unique in that its cylinder axis pin is also a fully " "functional 20ga percussion-primed smoothbore barrel. It's irremovable." msgstr "" +"El revólver Lemat es único en que su eje cilíndrico es también un cañón " +"completamente funcional de ánima lisa, activado por percusión, calibre 20ga." +" No se puede quitar." #: lang/json/gunmod_from_json.py msgid "M203" @@ -127573,8 +128257,8 @@ msgstr "" #: lang/json/gunmod_from_json.py msgid "modified M203" msgid_plural "modified M203s" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "M203 modificado" +msgstr[1] "M203 modificados" #: lang/json/gunmod_from_json.py msgid "" @@ -127583,6 +128267,10 @@ msgid "" "and fired. This one was modified and customized to mount on pretty much any" " weapon other than pistols and pump-action guns, if you so want." msgstr "" +"El lanzagranadas M203 fue diseñado originalmente para usar variantes del " +"M16, pero hoy en día puede ser adjuntado a cualquier rifle. Permite cargar y" +" disparar una granada 40mm. Este fue modificado y personalizado para poner " +"en casi cualquier arma excepto pistolas y armas de acción de bombeo." #: lang/json/gunmod_from_json.py msgid "M320 GLM" @@ -127597,12 +128285,15 @@ msgid "" " be either attached to a rifle or combined with a buttstock for standalone " "use." msgstr "" +"El Módulo M320 Lanzagranadas ofrece la funcionalidad de lanzadores más " +"grandes en un tamaño pequeño, a cambio de una menor precisión. Puede ser " +"adjuntado a un rifle o combinado con una culata para ser usado solo." #: lang/json/gunmod_from_json.py msgid "modified M320 GLM" msgid_plural "modified M320 GLMs" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "M320 GLM modificado" +msgstr[1] "M320 GLM modificados" #: lang/json/gunmod_from_json.py msgid "" @@ -127612,18 +128303,25 @@ msgid "" "use. This one was modified and customized to mount on pretty much any " "weapon other than pistols and pump-action guns, if you so want." msgstr "" +"El Módulo M320 Lanzagranadas ofrece la funcionalidad de lanzadores más " +"grandes en un tamaño pequeño, a cambio de una menor precisión. Puede ser " +"adjuntado a un rifle o combinado con una culata para ser usado solo. Este " +"fue modificado y personalizado para poner en casi cualquier arma excepto " +"pistolas y armas de acción de bombeo." #: lang/json/gunmod_from_json.py msgid "M6 Survival Gun shotgun" msgid_plural "M6 Survival Gun shotguns" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "cañón de supervivencia M6" +msgstr[1] "cañones de supervivencia M6" #: lang/json/gunmod_from_json.py msgid "" "The integrated 12 gauge shotgun barrel of the Chiappa M6 Survival Gun. It's" " irremovable." msgstr "" +"Es el cañón integrado calibre 12 gauge de la escopeta Chiappa M6 de " +"supervivencia. No se puede quitar." #: lang/json/gunmod_from_json.py msgid "masterkey shotgun" @@ -127644,8 +128342,8 @@ msgstr "" #: lang/json/gunmod_from_json.py msgid "modified masterkey shotgun" msgid_plural "modified masterkey shotguns" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "escopeta masterkey modificada" +msgstr[1] "escopetas masterkey modificadas" #: lang/json/gunmod_from_json.py msgid "" @@ -127654,6 +128352,10 @@ msgid "" "fired. This one was modified and customized to mount on pretty much any " "weapon other than pistols and pump-action guns, if you so want." msgstr "" +"Es una escopeta minimalista de acción de bombeo, que puede ser puesta bajo " +"el cañón de varios rifles. Permite cargar y disparar un total de cuatro " +"cartuchos de escopeta. Este fue modificado y personalizado para poner en " +"casi cualquier arma excepto pistolas y armas de acción de bombeo." #: lang/json/gunmod_from_json.py msgid "40mm pipe launcher" @@ -127666,6 +128368,8 @@ msgid "" "This is a home built launcher tube that can be attached to almost any weapon" " except handguns. It allows a single 40mm grenade to be loaded and fired." msgstr "" +"Es un tubo lanzador casero que puede ser adjuntado a casi cualquier arma, " +"excepto pistolas. Permite cargar y disparar una granada 40mm." #: lang/json/gunmod_from_json.py msgid "pistol bayonet" @@ -127701,8 +128405,8 @@ msgstr "" #: lang/json/gunmod_from_json.py msgid "modified RM121 aux shotgun" msgid_plural "modified RM121 aux shotguns" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "escopeta auxiliar modificada RM121" +msgstr[1] "escopetas auxiliares modificadas RM121" #: lang/json/gunmod_from_json.py msgid "" @@ -127711,6 +128415,10 @@ msgid "" "Accepts RMSA10 box magazines. This one was modified and customized to mount" " on pretty much any weapon other than pistols, if you so want." msgstr "" +"El sistema auxiliar Rivtech RM121 es una escopeta semiautomática sin vaina, " +"que puede ser montada debajo del cañón de varios rifles. Acepta los " +"cargadores de caja RMSA10. Este fue modificado y personalizado para poner en" +" casi cualquier arma excepto pistolas." #: lang/json/gunmod_from_json.py msgid "underslung shotgun" @@ -127729,8 +128437,8 @@ msgstr "" #: lang/json/gunmod_from_json.py msgid "modified underslung shotgun" msgid_plural "modified underslung shotguns" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "escopeta colgante modificada" +msgstr[1] "escopetas colgantes modificadas" #: lang/json/gunmod_from_json.py msgid "" @@ -127739,6 +128447,10 @@ msgid "" " was modified and customized to mount on pretty much any weapon other than " "pistols, if you so want." msgstr "" +"Es una escopeta corta con dos cañones que puede ser puesta bajo el cañón de " +"varios rifles. Permite cargar y disparar un total de dos cartuchos de " +"escopeta. Este fue modificado y personalizado para poner en casi cualquier " +"arma excepto pistolas." #: lang/json/gunmod_from_json.py msgid "spare magazine" @@ -127814,32 +128526,36 @@ msgstr "" #: lang/json/gunmod_from_json.py msgid "Underslung flare launcher" msgid_plural "Underslung flare launchers" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "lanzabengalas colgante" +msgstr[1] "lanzabengalas colgantes" #: lang/json/gunmod_from_json.py msgid "" "A small barrel which launches signal flares. However, due to its awkward " "position, it has lower accuracy compared to an actual flaregun." msgstr "" +"Es un pequeño cañón que lanza bengalas. Sin embargo, debido a su incómoda " +"posición, tiene poca precisión comparada con una pistola de bengalas normal." #: lang/json/gunmod_from_json.py msgid "CQB SI shotgun" msgid_plural "CQB SI shotguns" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "escopeta CQB SI" +msgstr[1] "escopetas CQB SI" #: lang/json/gunmod_from_json.py msgid "" "The integrated underbarrel shotgun of this gun which holds a single shot. " "It's irremovable." msgstr "" +"Es el bajocañón integrado de escopeta que contiene un disparo. No se puede " +"quitar." #: lang/json/gunmod_from_json.py msgid "butt hook stock" msgid_plural "butt hook stocks" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "culata gancho" +msgstr[1] "culatas gancho" #: lang/json/gunmod_from_json.py msgid "" @@ -127847,12 +128563,15 @@ msgid "" "and the pivoting hook which latches onto your forearm allows for greater " "stability. " msgstr "" +"Es una culata militar que se pliega para reducir el tamaño del arma. El peso" +" y el gancho giratorio que se engancha en tu antebrazo permiten una gran " +"estabilidad." #: lang/json/gunmod_from_json.py msgid "diffracting lens" msgid_plural "diffracting lenses" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "lente difractario" +msgstr[1] "lentes difractarios" #: lang/json/gunmod_from_json.py msgid "" @@ -127860,36 +128579,43 @@ msgid "" "beam into several lower powered beams. This slightly increases point-blank " "damage and makes it difficult to not hit, but reduces range" msgstr "" +"Es un conjunto de ópticas hechas para ponerse en armas láser, que difractan " +"el rayo láser en varios rayos de bajo poder. Esto incrementa levemente el " +"daño a quemarropa, pero reduce su alcance." #: lang/json/gunmod_from_json.py msgid "tactical flashlight" msgid_plural "tactical flashlights" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "linterna táctica" +msgstr[1] "linternas tácticas" #: lang/json/gunmod_from_json.py msgid "" "A compact flashlight which is mounted to the side of your weapon, not " "powerful, but good enough for tight hallways." msgstr "" +"Es una linterna compacta que se pone en el costado del arma, no es muy " +"potente pero es suficiente para espacios pequeños." #: lang/json/gunmod_from_json.py msgid "tactical flashlight (on)" msgid_plural "tactical flashlights (on)" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "linterna táctica (enc.)" +msgstr[1] "linternas tácticas (enc.)" #: lang/json/gunmod_from_json.py msgid "" "A compact flashlight which is attached to the side of your weapon, not " "powerful, but good enough for tight hallways." msgstr "" +"Es una linterna compacta puesta en el costado del arma, no es muy potente " +"pero es suficiente para espacios pequeños." #: lang/json/gunmod_from_json.py msgid "underbarrel launcher" msgid_plural "underbarrel launchers" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "lanzador bajocañón" +msgstr[1] "lanzadores bajocañón" #: lang/json/gunmod_from_json.py msgid "" @@ -127901,12 +128627,19 @@ msgid "" "targets. The launcher can be detached and its sights reattached to the " "could be disassembled and attached to a suitable rifle, if so desired." msgstr "" +"Es un lanzador regordete de un disparo con un cañón de calibre grande, " +"adecuado para granadas y bengalas. Este lanzador puede ser unido a un rifle " +"u otra arma adecuada, e incluye su propio grupo de miras. En años más " +"recientes, se vendieron algunas versiones para lanzar bengalas. Con el " +"cartucho adecuado, sería devastadora contra objetivos duros o suaves. Este " +"lanzador puede ser quitado y sus miras vueltas a poner para poder desarmar y" +" poner en un rifle adecuado." #: lang/json/gunmod_from_json.py msgid "underbarrel launcher (modified)" msgid_plural "underbarrel launcher (modified)" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "lanzador bajocañón (modificado)" +msgstr[1] "lanzadores bajocañón (modificados)" #: lang/json/gunmod_from_json.py msgid "" @@ -127917,36 +128650,45 @@ msgid "" "would be devastating against hard or soft targets. A crude homemade bracket " "has been attached, allowing a wider variety of hosts." msgstr "" +"Es un lanzador regordete de un disparo adecuado para granadas y bengalas. " +"Este lanzador puede ser unido a un rifle u otra arma adecuada, e incluye su " +"propio grupo de miras. En años más recientes, se vendieron algunas versiones" +" para lanzar bengalas. Con el cartucho adecuado, sería devastadora contra " +"objetivos duros o suaves. Tiene puesto un soporte rudimentario que permite " +"una variedad más amplia de receptores." #: lang/json/gunmod_from_json.py msgid "underbarrel shotgun" msgid_plural "underbarrel shotguns" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "escopeta bajocañón" +msgstr[1] "escopetas bajocañón" #: lang/json/gunmod_from_json.py msgid "modified underbarrel shotgun" msgid_plural "modified underbarrel shotguns" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "escopeta bajocañón modificada" +msgstr[1] "escopetas bajocañón modificadas" #: lang/json/gunmod_from_json.py msgid "'Silent Winds' suppressor" msgid_plural "'Silent Winds' suppressors" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "silenciador 'Vientos Silenciosos'" +msgstr[1] "silenciadores 'Vientos Silenciosos'" #: lang/json/gunmod_from_json.py msgid "" "Rather than using purely wipe media, an additional localized silence spell " "in this tube quiets gunshots going through to hearing-safe levels." msgstr "" +"Más que usar trapos como material, un hechizo de silencio ha sido localizado" +" en este tubo para silenciar los disparos a niveles seguros para la " +"audición." #: lang/json/gunmod_from_json.py msgid "mana laser sight (rail)" msgid_plural "mana laser sights (rail)" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "mirilla láser de maná (riel)" +msgstr[1] "mirillas láser de maná (riel)" #: lang/json/gunmod_from_json.py msgid "" @@ -127955,12 +128697,16 @@ msgid "" "acquisition. Aside from increased weight, there are no drawbacks. You can " "rotate the attachment rail to fit under the barrel." msgstr "" +"Es una pequeña mira láser que usa luz a través de un cristal de maná, que se" +" monta sobre el riel de accesorios del arma de fuego para mejorar la " +"facilidad y velocidad para apuntar. La única desventaja que tiene es el " +"incremento del peso. Podés rotar el riel para ponerla bajo el cañón." #: lang/json/gunmod_from_json.py msgid "mana laser sight (underbarrel)" msgid_plural "mana laser sights (underbarrel)" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "mirilla láser de maná (bajocañón)" +msgstr[1] "mirillas láser de maná (bajocañón)" #: lang/json/gunmod_from_json.py msgid "" @@ -127969,18 +128715,25 @@ msgid "" "acquisition. Aside from increased weight, there are no drawbacks. You can " "rotate the attachment rail to fit on the rail." msgstr "" +"Es una pequeña mira láser que usa luz a través de un cristal de maná, que se" +" monta sobre el riel de accesorios del arma de fuego para mejorar la " +"facilidad y velocidad para apuntar. La única desventaja que tiene es el " +"incremento del peso. Podés rotar el riel para ponerla sobre el riel." #: lang/json/gunmod_from_json.py msgid "mana dot sight" msgid_plural "mana dot sights" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "mirilla de punto de maná" +msgstr[1] "mirillas de punto de maná" #: lang/json/gunmod_from_json.py msgid "" "Adds a blue dot optic made from crystallized mana to the top of your gun, " "replacing the iron sights. Increases accuracy and weight." msgstr "" +"Agrega un punto azul óptico hecho de maná cristalizado a la parte superior " +"del arma, reemplazando la mirilla metálica del arma. Incrementa la precisión" +" y el peso." #: lang/json/harvest_from_json.py msgid "You gut and fillet the fish" @@ -128016,15 +128769,17 @@ msgstr "" #: lang/json/harvest_from_json.py msgid "" -msgstr "" +msgstr "" #: lang/json/harvest_from_json.py msgid "You laboriously dissect the colossal insect. " msgstr "" +"Con mucho trabajo lográs diseccionar el gigantesco insecto. " +"" #: lang/json/harvest_from_json.py msgid "" -msgstr "" +msgstr "" #: lang/json/harvest_from_json.py msgid "" @@ -128033,6 +128788,10 @@ msgid "" " tissue sacs that appear to be floating, which doesn't fit with what you " "know about real bees." msgstr "" +"Lo que parece ser pelo de insecto en la quitina de esta criatura se parece " +"más a pequeñas plumas cuando las cortás. Adentro hay un grupo de sacos " +"similares a burbujas que parecen estar flotando, lo que no se ajusta a tu " +"conocimiento sobre abejas reales." #: lang/json/harvest_from_json.py msgid "" @@ -128041,6 +128800,10 @@ msgid "" "tissue sacs that appear to be floating, which doesn't fit with what you know" " about real wasps." msgstr "" +"Hay una membrana similar a la piel, un poco peluda, cubierta de vasos " +"sanguíneos debajo de la quitina de esta criatura. Adentro hay un grupo de " +"sacos similares a burbujas que parecen estar flotando, lo que no se ajusta a" +" tu conocimiento sobre avispas reales." #: lang/json/harvest_from_json.py msgid "You laboriously hack and dig through the remains of the fungal mass." @@ -131141,11 +131904,13 @@ msgid "" "This gear requires careful balance to use. Being hit while wearing it could " "make you fall down." msgstr "" +"Este equipo necesita un equilibrio cuidadoso para ser usado. Ser golpeado " +"mientras se utiliza te podría hacer caer." #. ~ Please leave anything in unchanged. #: lang/json/json_flag_from_json.py msgid "This item can be used to communicate with radio waves." -msgstr "" +msgstr "Este objeto puede ser usado para comunicarse mediante ondas de radio." #. ~ Please leave anything in unchanged. #: lang/json/json_flag_from_json.py @@ -131153,11 +131918,14 @@ msgid "" "This item can be used to pick locks with zero " "effort." msgstr "" +"Este objeto puede ser usado para abrir cerraduras sin " +"ningún esfuerzo." #. ~ Please leave anything in unchanged. #: lang/json/json_flag_from_json.py msgid "This clothing is designed to keep you dry in the rain." msgstr "" +"Esta ropa está diseñada para mantenerte seco bajo la lluvia." #. ~ Please leave anything in unchanged. #: lang/json/json_flag_from_json.py @@ -131165,6 +131933,8 @@ msgid "" "It seems partially intangible, and can occupy the same space as" " other things when worn." msgstr "" +"Parece parcialmente intangible, y puede ocupar el mismo espacio" +" que otras cosas cuando se lleva puesto." #. ~ Please leave anything in unchanged. #: lang/json/json_flag_from_json.py @@ -131249,7 +132019,7 @@ msgstr "" #. ~ Please leave anything in unchanged. #: lang/json/json_flag_from_json.py msgid "This item can be worn simultaneously with power armor." -msgstr "" +msgstr "Este objeto puede ser usado simultáneamente con la armadura de poder." #. ~ Please leave anything in unchanged. #: lang/json/json_flag_from_json.py @@ -131285,6 +132055,8 @@ msgid "" "This gear has certain parts padded with steel to increase " "protection with moderate increase to encumbrance." msgstr "" +"Este equipo tiene algunas partes acolchadas con acero para incrementar" +" la protección pero con un leve aumento de incomodidad." #. ~ Please leave anything in unchanged. #: lang/json/json_flag_from_json.py @@ -131312,12 +132084,12 @@ msgstr "Este objeto tiene una receta de nanofabricador." #. ~ Please leave anything in unchanged. #: lang/json/json_flag_from_json.py msgid "This bionic is faulty." -msgstr "" +msgstr "Este biónico está defectuoso." #. ~ Please leave anything in unchanged. #: lang/json/json_flag_from_json.py msgid "This bionic provides power." -msgstr "" +msgstr "Este biónico provee energía." #. ~ Please leave anything in unchanged. #: lang/json/json_flag_from_json.py @@ -131330,6 +132102,8 @@ msgid "" "This food is unappetizing in a way that can't be covered up " "by most cooking." msgstr "" +"Esta comida es poco apetecible de tal manera que su sabor no" +" puede ser cubierto por otra comida." #. ~ Please leave anything in unchanged. #: lang/json/json_flag_from_json.py @@ -131337,6 +132111,8 @@ msgid "" "This food is raw, and will be more nutritious if " "cooked." msgstr "" +"Esta comida está cruda, y será más nutritiva si se" +" la cocina." #. ~ Please leave anything in unchanged. #: lang/json/json_flag_from_json.py @@ -131394,6 +132170,8 @@ msgid "" "You can 'e'xamine the tile to access the controls, or use the vehicle " "control key (default '^')." msgstr "" +"Podés 'e'xaminar el espacio para acceder a los controles, o usar la tecla " +"para controlar vehículos (por defecto '^')." #. ~ Please leave anything in unchanged. #: lang/json/json_flag_from_json.py @@ -131401,6 +132179,9 @@ msgid "" "With a seat or saddle, you drive from here. You can 'e'xamine the tile to " "access the controls, or use the vehicle control key (default '^')." msgstr "" +"Manejás desde acá con un asiento o una montura. Podés 'e'xaminar el espacio " +"para acceder a los controles o usar la tecla para controlar vehículos (por " +"defecto '^')." #. ~ Please leave anything in unchanged. #: lang/json/json_flag_from_json.py @@ -131545,12 +132326,12 @@ msgstr "" #. ~ Please leave anything in unchanged. #: lang/json/json_flag_from_json.py msgid "This has a flat surface. It would be a nice place to eat." -msgstr "" +msgstr "Esto tiene una superficie plana. Sería un buen lugar para comer." #. ~ Please leave anything in unchanged. #: lang/json/json_flag_from_json.py msgid "You can craft here." -msgstr "" +msgstr "Podés fabricar acá." #: lang/json/keybinding_from_json.py src/input.cpp src/messages.cpp msgid "Scroll up" @@ -131638,15 +132419,15 @@ msgstr "Marcar objeto seleccionado" #: lang/json/keybinding_from_json.py msgid "Enable auto note for map extra" -msgstr "" +msgstr "Activar noto automática para mapa" #: lang/json/keybinding_from_json.py msgid "Toggle auto notes option" -msgstr "" +msgstr "Cambiar opción de auto notas" #: lang/json/keybinding_from_json.py msgid "Disable auto note for map extra" -msgstr "" +msgstr "Desactivar noto automática para mapa" #: lang/json/keybinding_from_json.py msgid "Add rule" @@ -131706,11 +132487,11 @@ msgstr "Elegir armadura para mover" #: lang/json/keybinding_from_json.py msgid "Select panel for moving" -msgstr "" +msgstr "Elegir panel para mover" #: lang/json/keybinding_from_json.py msgid "Toggle panel off" -msgstr "" +msgstr "Desactivar panel" #: lang/json/keybinding_from_json.py msgid "Change side armor is worn on" @@ -131846,11 +132627,11 @@ msgstr "Borrar nota" #: lang/json/keybinding_from_json.py msgid "Edit Note" -msgstr "" +msgstr "Editar Nota" #: lang/json/keybinding_from_json.py msgid "Mark as Dangerous" -msgstr "" +msgstr "Marcar como Peligroso" #: lang/json/keybinding_from_json.py msgid "Create/Edit Note" @@ -131870,11 +132651,11 @@ msgstr "Activar/Desactivar Transparencias" #: lang/json/keybinding_from_json.py msgid "Toggle Land Use Codes" -msgstr "" +msgstr "Cambiar Códigos de Uso en Tierra" #: lang/json/keybinding_from_json.py msgid "Toggle Map Notes" -msgstr "" +msgstr "Cambiar Notas de Mapa" #: lang/json/keybinding_from_json.py msgid "Toggle City Labels" @@ -131918,7 +132699,7 @@ msgstr "Salir" #: lang/json/keybinding_from_json.py msgid "Choose destination" -msgstr "" +msgstr "Elegir destino" #: lang/json/keybinding_from_json.py msgid "Fire Weapon" @@ -131962,7 +132743,7 @@ msgstr "Cambiar Modo de Disparo" #: lang/json/keybinding_from_json.py msgid "Toggle turret lines" -msgstr "" +msgstr "Cambiar líneas de torreta" #: lang/json/keybinding_from_json.py msgid "Toggle Snap to Target" @@ -131970,7 +132751,7 @@ msgstr "Activar/Desactivar Saltar a objetivo" #: lang/json/keybinding_from_json.py msgid "Toggle moving view / cursor" -msgstr "" +msgstr "Cambiar cursor / vista de movimiento" #: lang/json/keybinding_from_json.py msgid "Select" @@ -132038,7 +132819,7 @@ msgstr "Recrear personaje aleatorio y escenario" #: lang/json/keybinding_from_json.py msgid "Pick random character description" -msgstr "" +msgstr "Seleccionar descripción aleatoria de personaje" #: lang/json/keybinding_from_json.py msgid "Choose character start location" @@ -132154,7 +132935,7 @@ msgstr "Cambiar tamaño" #: lang/json/keybinding_from_json.py msgid "To start" -msgstr "" +msgstr "Para arrancar" #: lang/json/keybinding_from_json.py msgid "Swap origin and target" @@ -132178,11 +132959,11 @@ msgstr "Modificar campos" #: lang/json/keybinding_from_json.py msgid "Edit terrain" -msgstr "" +msgstr "Modificar terreno" #: lang/json/keybinding_from_json.py msgid "Edit furniture" -msgstr "" +msgstr "Modificar mueble" #: lang/json/keybinding_from_json.py msgid "Edit overmap / mapgen" @@ -132222,7 +133003,7 @@ msgstr "Activar/Desactivar Modo de selección de categoría" #: lang/json/keybinding_from_json.py msgid "Toggle inventory view to show item categories" -msgstr "" +msgstr "Cambiar viste de inventario para mostrar categorías" #: lang/json/keybinding_from_json.py msgid "Set item filter" @@ -132230,7 +133011,7 @@ msgstr "Establecer filtro de objetos" #: lang/json/keybinding_from_json.py msgid "Toggle item as favorite" -msgstr "" +msgstr "Hacer o sacar objeto como favorito" #: lang/json/keybinding_from_json.py msgid "Toggle activate/examine" @@ -132238,11 +133019,11 @@ msgstr "Activar/Desactivar Examinar" #: lang/json/keybinding_from_json.py msgid "Toggle safe fuel mod" -msgstr "" +msgstr "Cambiar mod de combustible seguro" #: lang/json/keybinding_from_json.py msgid "Toggle auto start mod" -msgstr "" +msgstr "Cambiar mod de auto arrancar" #: lang/json/keybinding_from_json.py src/martialarts.cpp msgid "Pause" @@ -132294,11 +133075,11 @@ msgstr "Inventario avanzado" #: lang/json/keybinding_from_json.py msgid "Pick up Nearby Item(s)" -msgstr "" +msgstr "Agarrar objeto(s) cercanos" #: lang/json/keybinding_from_json.py msgid "Pickup Item(s) at Player Feet" -msgstr "" +msgstr "Agarrar objeto(s) bajo los pies" #: lang/json/keybinding_from_json.py msgid "Grab something nearby" @@ -132326,7 +133107,7 @@ msgstr "Mirar alrededor" #: lang/json/keybinding_from_json.py msgid "Toggle thief mode" -msgstr "" +msgstr "Cambiar modo ladrón" #: lang/json/keybinding_from_json.py msgid "Peek Around Corners" @@ -132370,7 +133151,7 @@ msgstr "Sacarse objeto puesto" #: lang/json/keybinding_from_json.py msgid "Consume Item Menu" -msgstr "" +msgstr "Menú de Consumir Objeto" #: lang/json/keybinding_from_json.py msgid "Wield" @@ -132382,7 +133163,7 @@ msgstr "Elegir Estilo de Artes Marciales" #: lang/json/keybinding_from_json.py msgid "Reload Weapons" -msgstr "" +msgstr "Recargar Armas" #: lang/json/keybinding_from_json.py msgid "Reload Wielded Item" @@ -132458,7 +133239,7 @@ msgstr "Manejar vehículo" #: lang/json/keybinding_from_json.py msgid "Toggle Auto Travel Mode" -msgstr "" +msgstr "Cambiar Modo de Auto Viaje" #: lang/json/keybinding_from_json.py msgid "Toggle Safe Mode" @@ -132490,7 +133271,7 @@ msgstr "Ver Mapa" #: lang/json/keybinding_from_json.py msgid "Look at the sky" -msgstr "" +msgstr "Mirar el cielo" #: lang/json/keybinding_from_json.py msgid "View Factions" @@ -132498,7 +133279,7 @@ msgstr "Ver Bandos" #: lang/json/keybinding_from_json.py msgid "View Achievements, Scores, and Kills" -msgstr "" +msgstr "Ver Logros, Puntos y Muertes" #: lang/json/keybinding_from_json.py msgid "View Morale" @@ -132534,23 +133315,23 @@ msgstr "Ver Mapa de Olor" #: lang/json/keybinding_from_json.py msgid "View Scent Type" -msgstr "" +msgstr "Ver Tipo de Olor" #: lang/json/keybinding_from_json.py msgid "View Temperature Map" -msgstr "" +msgstr "Ver Mapa de Temperatura" #: lang/json/keybinding_from_json.py msgid "View Visibility Map" -msgstr "" +msgstr "Ver Mapa de Visibilidad" #: lang/json/keybinding_from_json.py msgid "View Lighting Map" -msgstr "" +msgstr "Ver Mapa de Luz" #: lang/json/keybinding_from_json.py msgid "View Radiation Map" -msgstr "" +msgstr "Ver Mapa de Radiació" #: lang/json/keybinding_from_json.py msgid "Switch Sidebar Style" @@ -132562,15 +133343,15 @@ msgstr "Act./Desact. Pantalla completa" #: lang/json/keybinding_from_json.py msgid "Toggle Minimap" -msgstr "" +msgstr "Mostrar/Ocultar Minimapa" #: lang/json/keybinding_from_json.py msgid "Toggle Panel Admin" -msgstr "" +msgstr "Mostrar/Ocultar Panel de Administrador" #: lang/json/keybinding_from_json.py msgid "Reload Item" -msgstr "" +msgstr "Recargar objeto" #: lang/json/keybinding_from_json.py msgid "Reload Tileset" @@ -132594,7 +133375,7 @@ msgstr "Activar/Desactivar auto recolectar" #: lang/json/keybinding_from_json.py msgid "Toggle Auto Pickup" -msgstr "" +msgstr "Cambiar Auto Agarrar" #: lang/json/keybinding_from_json.py msgid "Action Menu" @@ -132674,27 +133455,27 @@ msgstr "Mods Activos de Mundo" #: lang/json/keybinding_from_json.py msgid "Cycle move mode (run/walk/crouch)" -msgstr "" +msgstr "Cambiar modo de movimiento (correr/caminar/agacharse)" #: lang/json/keybinding_from_json.py msgid "Reset Movement to Walk" -msgstr "" +msgstr "Volver a Caminar" #: lang/json/keybinding_from_json.py msgid "Toggle Run" -msgstr "" +msgstr "Empezar a Correr" #: lang/json/keybinding_from_json.py msgid "Toggle Crouch" -msgstr "" +msgstr "Empezar a Agacharse" #: lang/json/keybinding_from_json.py msgid "Movement Mode Menu" -msgstr "" +msgstr "Menú de Modo de Movimiento" #: lang/json/keybinding_from_json.py msgid "Spellcasting" -msgstr "" +msgstr "Hechizos" #: lang/json/keybinding_from_json.py src/game_inventory.cpp msgid "Compare" @@ -132769,7 +133550,7 @@ msgstr "Agregar atajo de teclado global" #: lang/json/keybinding_from_json.py msgid "Execute action keybinding" -msgstr "" +msgstr "Tecla de ejecutar acción" #: lang/json/keybinding_from_json.py msgid "Add zone" @@ -132845,7 +133626,7 @@ msgstr "Mover todos los objetos" #: lang/json/keybinding_from_json.py msgid "Mark/unmark non-favorite items in multidrop menu" -msgstr "" +msgstr "Marcar/Desmarcar no favoritos en menú de soltar varios objetos" #: lang/json/keybinding_from_json.py msgid "Select items @ North-West" @@ -132936,7 +133717,7 @@ msgstr "No" #: lang/json/keybinding_from_json.py msgid "Quit" -msgstr "" +msgstr "Salir" #: lang/json/keybinding_from_json.py msgid "Ignore further distractions and finish" @@ -132956,19 +133737,19 @@ msgstr "Cancelar" #: lang/json/keybinding_from_json.py msgid "Describe creature" -msgstr "" +msgstr "Describir criatura" #: lang/json/keybinding_from_json.py msgid "Describe furniture" -msgstr "" +msgstr "Describir mueble" #: lang/json/keybinding_from_json.py msgid "Describe terrain" -msgstr "" +msgstr "Describir terreno" #: lang/json/keybinding_from_json.py msgid "Switch lists" -msgstr "" +msgstr "Cambiar listas" #: lang/json/keybinding_from_json.py src/action.cpp msgid "Back" @@ -132976,19 +133757,19 @@ msgstr "Volver" #: lang/json/keybinding_from_json.py msgid "More" -msgstr "" +msgstr "Más" #: lang/json/keybinding_from_json.py msgid "Examine item" -msgstr "" +msgstr "Examinar objeto" #: lang/json/keybinding_from_json.py msgid "Cancel trading" -msgstr "" +msgstr "Cancelar negociación" #: lang/json/keybinding_from_json.py src/player_display.cpp msgid "Change profession name" -msgstr "" +msgstr "Cambiar nombre de profesión" #: lang/json/keybinding_from_json.py src/vehicle_use.cpp src/vehicle_use.cpp msgid "Control multiple electronics" @@ -133024,7 +133805,7 @@ msgstr "Luces atómicas" #: lang/json/keybinding_from_json.py src/vehicle_use.cpp msgid "Control autopilot" -msgstr "" +msgstr "Controlar piloto automático" #: lang/json/keybinding_from_json.py msgid "Toggle camera system" @@ -133060,11 +133841,11 @@ msgstr "Activar/Desactivar freezer" #: lang/json/keybinding_from_json.py msgid "Toggle space heater" -msgstr "" +msgstr "Prender/Apagar caloventor" #: lang/json/keybinding_from_json.py msgid "Toggle cooler" -msgstr "" +msgstr "Prender/Apagar refrigerador" #: lang/json/keybinding_from_json.py msgid "Toggle headlights" @@ -133145,88 +133926,88 @@ msgstr "Nada" #. ~ Description for {'str': 'Nothing'} #: lang/json/map_extra_from_json.py msgid "Nothing of interest is here." -msgstr "" +msgstr "Acá no hay nada de interés." #: lang/json/map_extra_from_json.py msgid "Crater" -msgstr "" +msgstr "Cráter" #. ~ Description for {'str': 'Crater'} #: lang/json/map_extra_from_json.py msgid "There is a crater here." -msgstr "" +msgstr "Acá hay un cráter." #: lang/json/map_extra_from_json.py msgid "College Kids" -msgstr "" +msgstr "Estudiantes" #. ~ Description for {'str': 'College Kids'} #: lang/json/map_extra_from_json.py msgid "Several corpses of college kids are here." -msgstr "" +msgstr "Acá hay varios cadáveres de niños estudiantes." #: lang/json/map_extra_from_json.py msgid "Drug Deal" -msgstr "" +msgstr "Transacción de Droga" #. ~ Description for {'str': 'Drug Deal'} #: lang/json/map_extra_from_json.py msgid "Several corpses of drug dealers are here." -msgstr "" +msgstr "Acá hay varios cadáveres de narcotraficantes." #: lang/json/map_extra_from_json.py msgid "Roadworks" -msgstr "" +msgstr "Obras viales" #. ~ Description for {'str': 'Roadworks'} #: lang/json/map_extra_from_json.py msgid "Roadworks are here." -msgstr "" +msgstr "Acá hay unas obras sobre la calle." #: lang/json/map_extra_from_json.py msgid "Road Mayhem" -msgstr "" +msgstr "Caos en la Calle" #. ~ Description for {'str': 'Road Mayhem'} #: lang/json/map_extra_from_json.py msgid "Road mayhem is here." -msgstr "" +msgstr "Acá hay algún quilombo en la calle." #: lang/json/map_extra_from_json.py msgid "Roadblock (Military)" -msgstr "" +msgstr "Barricada (Militar)" #. ~ Description for {'str': 'Roadblock (Military)'} #: lang/json/map_extra_from_json.py msgid "This road is blocked by military." -msgstr "" +msgstr "Acá la calle está bloqueada por los militares." #: lang/json/map_extra_from_json.py msgid "Roadblock (Bandits)" -msgstr "" +msgstr "Barricada (Bandidos)" #. ~ Description for {'str': 'Roadblock (Bandits)'} #: lang/json/map_extra_from_json.py msgid "This road is blocked by bandits." -msgstr "" +msgstr "Acá la calle está bloqueada por los bandidos." #: lang/json/map_extra_from_json.py msgid "Minefield" -msgstr "" +msgstr "Campo Minado" #. ~ Description for {'str': 'Minefield'} #: lang/json/map_extra_from_json.py msgid "Mines are scattered here." -msgstr "" +msgstr "Acá hay minas plantadas." #: lang/json/map_extra_from_json.py msgid "Supply Drop" -msgstr "" +msgstr "Suministros" #. ~ Description for {'str': 'Supply Drop'} #: lang/json/map_extra_from_json.py msgid "Several supply crates were dropped here." -msgstr "" +msgstr "Hay varias cajas con suministros que cayeron acá." #: lang/json/map_extra_from_json.py msgctxt "Map Extra" @@ -133236,7 +134017,7 @@ msgstr "Militar" #. ~ Description for {'str': 'Military', 'ctxt': 'Map Extra'} #: lang/json/map_extra_from_json.py msgid "Several corpses of soldiers are here." -msgstr "" +msgstr "Acá hay varios cadáveres de soldados." #: lang/json/map_extra_from_json.py msgid "Helicopter Crash" @@ -133245,52 +134026,52 @@ msgstr "Helicóptero Estrellado" #. ~ Description for {'str': 'Helicopter Crash'} #: lang/json/map_extra_from_json.py msgid "Helicopter crashed here." -msgstr "" +msgstr "Acá se cayó un helicóptero." #: lang/json/map_extra_from_json.py msgid "Scientists" -msgstr "" +msgstr "Científicos" #. ~ Description for {'str': 'Scientists'} #: lang/json/map_extra_from_json.py msgid "Several corpses of scientists are here." -msgstr "" +msgstr "Acá hay varios cadáveres de científicos." #: lang/json/map_extra_from_json.py msgid "Portal" -msgstr "" +msgstr "Portal" #. ~ Description for {'str': 'Portal'} #: lang/json/map_extra_from_json.py msgid "Portal is here." -msgstr "" +msgstr "Acá hay un portal." #: lang/json/map_extra_from_json.py msgid "Portal In" -msgstr "" +msgstr "Portal de Entrada" #. ~ Description for {'str': 'Portal In'} #: lang/json/map_extra_from_json.py msgid "Another portal is here." -msgstr "" +msgstr "Acá hay otro portal." #: lang/json/map_extra_from_json.py msgid "Spider Nest" -msgstr "" +msgstr "Nido de Arañas" #. ~ Description for {'str': 'Spider Nest'} #: lang/json/map_extra_from_json.py msgid "Spider nest is here." -msgstr "" +msgstr "Acá hay un nido de arañas." #: lang/json/map_extra_from_json.py msgid "Wasp Nest" -msgstr "" +msgstr "Nido de Avispas" #. ~ Description for {'str': 'Wasp Nest'} #: lang/json/map_extra_from_json.py msgid "Wasp nest is here." -msgstr "" +msgstr "Acá hay un nido de avispas." #: lang/json/map_extra_from_json.py src/gamemode_defense.cpp msgid "Spiders" @@ -133300,179 +134081,182 @@ msgstr "Arañas" #: lang/json/map_extra_from_json.py msgid "This area is covered with webs. Probably spiders are nearby" msgstr "" +"Este área está cubierta de telarañas. Probablemente haya arañas cerca." #. ~ Description for {'str': 'Shia LaBeouf'} #: lang/json/map_extra_from_json.py msgid "Cannibal is nearby." -msgstr "" +msgstr "Hay un caníbal cerca." #: lang/json/map_extra_from_json.py msgid "Jabberwock" -msgstr "" +msgstr "Jabberwock" #. ~ Description for {'str': 'Jabberwock'} #: lang/json/map_extra_from_json.py msgid "Jabberwock is nearby." -msgstr "" +msgstr "Hay un jabberwock cerca." #: lang/json/map_extra_from_json.py msgid "Grove" -msgstr "" +msgstr "Arboleda" #. ~ Description for {'str': 'Grove'} #: lang/json/map_extra_from_json.py msgid "This area is covered with a single type of trees." -msgstr "" +msgstr "Esta área está cubierta con un solo tipo de árboles." #: lang/json/map_extra_from_json.py msgid "Shrubberry" -msgstr "" +msgstr "Arbustos" #. ~ Description for {'str': 'Shrubberry'} #: lang/json/map_extra_from_json.py msgid "This area is covered with a single type of shrubs." -msgstr "" +msgstr "Esta área está cubierta con un solo tipo de arbusto." #: lang/json/map_extra_from_json.py msgid "Clearcut" -msgstr "" +msgstr "Claro" #. ~ Description for {'str': 'Clearcut'} #: lang/json/map_extra_from_json.py msgid "Most trees in this area were uniformly cut down." -msgstr "" +msgstr "En esta área la mayoría de los árboles fueron talados." #: lang/json/map_extra_from_json.py msgid "Pond" -msgstr "" +msgstr "Charco" #. ~ Description for {'str': 'Pond'} #: lang/json/map_extra_from_json.py msgid "Small pond is here." -msgstr "" +msgstr "Acá hay un pequeño charco." #: lang/json/map_extra_from_json.py msgid "Stand of trees" -msgstr "" +msgstr "Línea de árboles" #. ~ Description for {'str': 'Stand of trees'} #: lang/json/map_extra_from_json.py msgid "A copse of trees." -msgstr "" +msgstr "Es un bosquecillo de árboles." #: lang/json/map_extra_from_json.py msgid "Tall grass" -msgstr "" +msgstr "Pasto alto" #. ~ Description for {'str': 'Tall grass'} #: lang/json/map_extra_from_json.py msgid "A meadow of tall grass." -msgstr "" +msgstr "Es un campo con pasto alto." #: lang/json/map_extra_from_json.py msgid "Derelict shed" -msgstr "" +msgstr "Caseta abandonada" #. ~ Description for {'str': 'Derelict shed'} #: lang/json/map_extra_from_json.py msgid "A collapsed shed." -msgstr "" +msgstr "Es una caseta medio derrumbada." #: lang/json/map_extra_from_json.py msgid "Clay Deposit" -msgstr "" +msgstr "Depósito de Arcilla" #. ~ Description for {'str': 'Clay Deposit'} #: lang/json/map_extra_from_json.py msgid "Small clay deposit is here." -msgstr "" +msgstr "Acá hay un pequeño depósito de arcilla." #: lang/json/map_extra_from_json.py msgid "Dead Vegetation" -msgstr "" +msgstr "Vegetación Muerta" #. ~ Description for {'str': 'Dead Vegetation'} #. ~ Description for {'str': 'Dead Vegetation (Point)'} #: lang/json/map_extra_from_json.py msgid "Dead vegetation is here." -msgstr "" +msgstr "Acá hay vegetación muerta." #: lang/json/map_extra_from_json.py msgid "Dead Vegetation (Point)" -msgstr "" +msgstr "Vegetación Muerta (Punto)" #: lang/json/map_extra_from_json.py msgid "Burned Ground" -msgstr "" +msgstr "Suelo Quemado" #. ~ Description for {'str': 'Burned Ground'} #. ~ Description for {'str': 'Burned Ground (Point)'} #: lang/json/map_extra_from_json.py msgid "Burned ground is here." -msgstr "" +msgstr "Acá está el suelo quemado." #: lang/json/map_extra_from_json.py msgid "Burned Ground (Point)" -msgstr "" +msgstr "Suelo Quemado (Punto)" #: lang/json/map_extra_from_json.py msgid "Marloss Pilgrimage" -msgstr "" +msgstr "Peregrinación Marloss" #. ~ Description for {'str': 'Marloss Pilgrimage'} #: lang/json/map_extra_from_json.py msgid "Marloss Pilgrimage is here." -msgstr "" +msgstr "Acá hay una peregrinación marloss." #: lang/json/map_extra_from_json.py msgid "Casings" -msgstr "" +msgstr "Vainas" #. ~ Description for {'str': 'Casings'} #: lang/json/map_extra_from_json.py msgid "Several spent casings are here." -msgstr "" +msgstr "Acá hay varias vainas servidas." #: lang/json/map_extra_from_json.py msgid "Looters" -msgstr "" +msgstr "Saqueadores" #. ~ Description for {'str': 'Looters'} #: lang/json/map_extra_from_json.py msgid "Some looters gathering everything not nailed down." -msgstr "" +msgstr "Hay unos saqueadores que se están llevando todo lo que pueden." #: lang/json/map_extra_from_json.py msgid "Corpses" -msgstr "" +msgstr "Cadáveres" #. ~ Description for {'str': 'Corpses'} #: lang/json/map_extra_from_json.py msgid "Some unfortunates from the billions lost in the Cataclysm." msgstr "" +"Algunos de los desafortunados entre los miles de millones perdidos en el " +"Cataclismo." #. ~ Description for {'str': 'Wasp Nest'} #: lang/json/map_extra_from_json.py msgid "A wasp nest." -msgstr "" +msgstr "Es un nido de avispas." #: lang/json/map_extra_from_json.py msgid "Dermatik Nest" -msgstr "" +msgstr "Nido de Dermatik" #. ~ Description for {'str': 'Dermatik Nest'} #: lang/json/map_extra_from_json.py msgid "A dermatik nest." -msgstr "" +msgstr "Es un nido de dermatiks." #: lang/json/map_extra_from_json.py lang/json/vehicle_from_json.py msgid "Prison Bus" -msgstr "" +msgstr "Colectivo de Cárcel" #. ~ Description for {'str': 'Prison Bus'} #: lang/json/map_extra_from_json.py msgid "A prison bus." -msgstr "" +msgstr "Es un colectivo para trasladar presos." #: lang/json/map_extra_from_json.py msgid "Mass Grave" @@ -133481,34 +134265,34 @@ msgstr "Fosa Común" #. ~ Description for {'str': 'Mass Grave'} #: lang/json/map_extra_from_json.py msgid "A mass grave." -msgstr "" +msgstr "Es una fosa común." #: lang/json/map_extra_from_json.py msgid "Grave" -msgstr "" +msgstr "Tumba" #. ~ Description for {'str': 'Grave'} #: lang/json/map_extra_from_json.py msgid "A grave." -msgstr "" +msgstr "Es una tumba." #: lang/json/map_extra_from_json.py msgid "Zombie Trap" -msgstr "" +msgstr "Trampa Zombi" #. ~ Description for {'str': 'Zombie Trap'} #: lang/json/map_extra_from_json.py msgid "Zombie trap." -msgstr "" +msgstr "Es una trampa para zombi." #: lang/json/map_extra_from_json.py msgid "Reed" -msgstr "" +msgstr "Junco" #. ~ Description for {'str': 'Reed'} #: lang/json/map_extra_from_json.py msgid "Water vegetation." -msgstr "" +msgstr "Es vegetación en el agua." #. ~ Computer name #: lang/json/mapgen_from_json.py @@ -133530,6 +134314,8 @@ msgstr "Banco Computarizado Consolidado de Hacienda de Alta Seguridad" msgid "" "ERROR! Access denied! Unauthorized access will be met with lethal force!" msgstr "" +"¡ERROR! ¡Acceso denegado! ¡El acceso no autorizado será castigado con fuerza" +" letal!" #. ~ Sign #: lang/json/mapgen_from_json.py @@ -133634,7 +134420,7 @@ msgstr " Zona de Basura" #. ~ Sign #: lang/json/mapgen_from_json.py msgid "St. John Dairy. 555-0199 Daily Farm Tours" -msgstr "" +msgstr "Granja San Juan. 555-0199 Visitas a la Granja" #. ~ Sign #: lang/json/mapgen_from_json.py @@ -133905,7 +134691,7 @@ msgstr "Aviso de Seguridad [1057]" #. ~ Computer option #: lang/json/mapgen_from_json.py msgid "Security Reminder [1058]" -msgstr "" +msgstr "Aviso de Seguridad [1058]" #. ~ Computer option #: lang/json/mapgen_from_json.py @@ -133950,87 +134736,87 @@ msgstr "Analizar sangre" #. ~ Computer name #: lang/json/mapgen_from_json.py msgid "Irradiation Facility Operation Console" -msgstr "" +msgstr "Consola de Operación de Irradiación" #. ~ Computer option #: lang/json/mapgen_from_json.py msgid "Uplink to mainframe servers" -msgstr "" +msgstr "Enlace a servidores centrales" #. ~ Computer option #: lang/json/mapgen_from_json.py msgid "Cycle conveyor belt" -msgstr "" +msgstr "Cambiar cinta transportadora" #. ~ Computer option #: lang/json/mapgen_from_json.py msgid "Toggle safety shutters" -msgstr "" +msgstr "Cambiar obturadores de seguridad" #. ~ Computer option #: lang/json/mapgen_from_json.py msgid "Probe radiation levels" -msgstr "" +msgstr "Sondear niveles de radiación" #. ~ Computer option #: lang/json/mapgen_from_json.py msgid "Commence irradiation sequence" -msgstr "" +msgstr "Iniciar secuencia de irradiación" #. ~ Computer option #: lang/json/mapgen_from_json.py msgid "[Maintenance] Extract radiation core" -msgstr "" +msgstr "[Mantenimiento] Extraer núcleo de radiación" #. ~ Computer name #: lang/json/mapgen_from_json.py msgid "Hazardous Materials Containment" -msgstr "" +msgstr "Contención de Materiales Peligrosos" #. ~ Computer option #: lang/json/mapgen_from_json.py msgid "Access containment zone" -msgstr "" +msgstr "Acceder zona de contención" #. ~ Computer option #: lang/json/mapgen_from_json.py msgid "Geiger counter readout" -msgstr "" +msgstr "Lectura de contador geiger" #. ~ Computer name #: lang/json/mapgen_from_json.py msgid "Security Access Terminal" -msgstr "" +msgstr "Terminal de Acceso de Seguridad" #. ~ Computer option #: lang/json/mapgen_from_json.py msgid "Open doors" -msgstr "" +msgstr "Abrir puertas" #. ~ Computer option #: lang/json/mapgen_from_json.py msgid "Access security locker" -msgstr "" +msgstr "Acceder casillero de seguridad" #. ~ Computer name #: lang/json/mapgen_from_json.py msgid "Power Management" -msgstr "" +msgstr "Administración de Energía" #. ~ Computer option #: lang/json/mapgen_from_json.py msgid "External power management" -msgstr "" +msgstr "Administración de energía externa" #. ~ Computer option #: lang/json/mapgen_from_json.py msgid "Backup power management" -msgstr "" +msgstr "Administración de energía de reserva" #. ~ Computer option #: lang/json/mapgen_from_json.py msgid "Run emergency auto-diagnostic" -msgstr "" +msgstr "Iniciar autodiagnóstico de emergencia" #. ~ Sign #: lang/json/mapgen_from_json.py @@ -134199,7 +134985,7 @@ msgstr "funciona." #. ~ Sign #: lang/json/mapgen_from_json.py msgid "Restricted area! Violators will be shot!" -msgstr "" +msgstr "¡Área restringida! ¡Se disparará contra los infractores!" #. ~ Computer name #: lang/json/mapgen_from_json.py @@ -134223,7 +135009,7 @@ msgstr "" #. ~ Sign #: lang/json/mapgen_from_json.py msgid "Travelier MOTEL" -msgstr "" +msgstr "HOTEL Viajero" #. ~ Computer name #: lang/json/mapgen_from_json.py @@ -134343,17 +135129,17 @@ msgstr "¡NO tirarse de cabeza!" #. ~ Computer name #: lang/json/mapgen_from_json.py msgid "Armory Access" -msgstr "" +msgstr "Acceso a Armería" #. ~ Computer option #: lang/json/mapgen_from_json.py msgid "Open Armory Door" -msgstr "" +msgstr "Abrir Puerta de Armería" #. ~ Sign #: lang/json/mapgen_from_json.py msgid "Private property. No trespassing!" -msgstr "" +msgstr "Propiedad privada. ¡No pasar!" #. ~ Sign #: lang/json/mapgen_from_json.py @@ -134449,7 +135235,7 @@ msgstr "el nombre ya no está pero quedó el slogan: 'Te vamos a reparar todo'" #. ~ Computer name #: lang/json/mapgen_from_json.py msgid "EnviroCom OS v2.03 - Basement Access" -msgstr "" +msgstr "EnviroCom OS v2.03 - Acceso Sótano" #. ~ Computer option #: lang/json/mapgen_from_json.py @@ -134459,12 +135245,12 @@ msgstr "Abrir escaleras" #. ~ Sign #: lang/json/mapgen_from_json.py msgid "Authorized personnel only" -msgstr "" +msgstr "Solo para personal autorizado" #. ~ Sign #: lang/json/mapgen_from_json.py msgid " sewage treatment plant" -msgstr "" +msgstr " planta de tratamiento de aguas cloacales" #. ~ Computer name #: lang/json/mapgen_from_json.py @@ -134561,7 +135347,7 @@ msgstr "Instalar Modificación de Repetidor" #. ~ Computer option #: lang/json/mapgen_from_json.py msgid "Browse Audio Archive" -msgstr "" +msgstr "Revisar Archivo de Audio" #. ~ Sign #: lang/json/mapgen_from_json.py @@ -134576,7 +135362,7 @@ msgstr "Casa de Té La Hoja Verde" #. ~ Sign #: lang/json/mapgen_from_json.py msgid "RESTRICTED AREA! AUTHORIZED PERSONNEL ONLY" -msgstr "" +msgstr "¡ÁREA RESTRINGIDA! SOLO PERSONAL AUTORIZADO" #. ~ Sign #: lang/json/mapgen_from_json.py @@ -134701,7 +135487,7 @@ msgstr "DESTRABAR ALMACENAMIENTO" #. ~ Computer name #: lang/json/mapgen_from_json.py msgid "Prototype DARPA-713 AUTHORIZED PILOTS ONLY" -msgstr "" +msgstr "Prototype DARPA-713 SOLO PILOTOS AUTORIZADOS" #. ~ Computer name #: lang/json/mapgen_from_json.py @@ -134746,12 +135532,12 @@ msgstr "Ver Rutas de Subte" #. ~ Computer name #: lang/json/mapgen_from_json.py msgid "Security Terminal" -msgstr "" +msgstr "Terminal de Seguridad" #. ~ Computer option #: lang/json/mapgen_from_json.py msgid "UNLOCK SECURITY DOORS" -msgstr "" +msgstr "ABRIR PUERTAS DE SEGURIDAD" #. ~ Sign #: lang/json/mapgen_from_json.py @@ -134762,6 +135548,11 @@ msgid "" "\n" "Regulation EX-127 applies." msgstr "" +"Peligro:\n" +"Sala dimensionalmente inestable.\n" +"No pasar la línea amarilla.\n" +"\n" +"Se aplica la regulación EX-127." #. ~ Sign #: lang/json/mapgen_from_json.py @@ -134772,16 +135563,21 @@ msgid "" "\n" "Ration VC+983 appliance." msgstr "" +"Pegrilo;\n" +"Sala dimen inesta sional.\n" +"Pasar la amínea linilla.\n" +"\n" +"Aplique VC+983 ración." #. ~ Computer name #: lang/json/mapgen_from_json.py msgid "Surgery room computer" -msgstr "" +msgstr "Computadora sala de cirugía" #. ~ Computer option #: lang/json/mapgen_from_json.py msgid "Open Storage Chambers" -msgstr "" +msgstr "Abrir Cámaras de Almacenamiento" #. ~ Computer option #: lang/json/mapgen_from_json.py src/mapgen.cpp src/mapgen.cpp @@ -134791,32 +135587,32 @@ msgstr "Manifiesto" #. ~ Computer name #: lang/json/mapgen_from_json.py msgid "Medical Storage Access" -msgstr "" +msgstr "Acceso Almacenamiento Médico" #. ~ Computer option #: lang/json/mapgen_from_json.py msgid "Open Storage Door" -msgstr "" +msgstr "Abrir Puerta de Almacenamiento" #. ~ Sign #: lang/json/mapgen_from_json.py msgid "DANGER MINEFIELD" -msgstr "" +msgstr "PELIGRO CAMPO MINADO" #. ~ Computer name #: lang/json/mapgen_from_json.py msgid "Vehicle Testing Track" -msgstr "" +msgstr "Pista de Prueba de Vehículo" #. ~ Computer name #: lang/json/mapgen_from_json.py msgid "Weapons Testing Range" -msgstr "" +msgstr "Prueba de Rango de Armas" #. ~ Computer name #: lang/json/mapgen_from_json.py msgid "Armory Entrance" -msgstr "" +msgstr "Entrada Armería" #. ~ Sign #: lang/json/mapgen_from_json.py @@ -134824,11 +135620,13 @@ msgid "" "Whately Family Mortuary Services. Serving New England for three hundred " "years.'" msgstr "" +"Servicios Funerarios Familia Whately. Sirviendo a New England por " +"trescientos años." #. ~ Computer name #: lang/json/mapgen_from_json.py msgid "DinoLab Operating Theater Controls" -msgstr "" +msgstr "Controles De Operación DinoLab al Teatro" #: lang/json/martial_art_from_json.py msgid "No style" @@ -134842,13 +135640,13 @@ msgstr "No es un arte marcial, es el viejo y conocido pegar piñas y patadas." #. ~ initiate message for martial art '{'str': 'No style'}' #: lang/json/martial_art_from_json.py msgid "You decide to not use any martial arts." -msgstr "" +msgstr "Decidís no usar ninguna arte marcial." #. ~ initiate message for martial art '{'str': 'No style'}' #: lang/json/martial_art_from_json.py #, python-format msgid "%s enters a generic combat stance." -msgstr "" +msgstr "%s adopta una postura genérica de combate." #: lang/json/martial_art_from_json.py msgid "Force unarmed" @@ -134866,13 +135664,13 @@ msgstr "" #. ~ initiate message for martial art '{'str': 'Force unarmed'}' #: lang/json/martial_art_from_json.py msgid "You force yourself to fight unarmed." -msgstr "" +msgstr "Te forzás a pelear desarmado." #. ~ initiate message for martial art '{'str': 'Force unarmed'}' #: lang/json/martial_art_from_json.py #, python-format msgid "%s decides to fight unarmed." -msgstr "" +msgstr "%s decide pelera desarmado." #: lang/json/martial_art_from_json.py msgid "Aikido" @@ -134885,21 +135683,24 @@ msgid "" "injury to the attacker. It uses defensive throws and disarms but lacks " "offensive techniques." msgstr "" +"El aikido es un arte marcial japonés focalizado en la defensa propia y en " +"minimizar el daño hacia el atacante. Utiliza desarmes y tiradas defensivas, " +"pero no tiene técnicas ofensivas." #. ~ initiate message for martial art '{'str': 'Aikido'}' #: lang/json/martial_art_from_json.py msgid "You enter the hamni stance." -msgstr "" +msgstr "Adoptás la postura hamni." #. ~ initiate message for martial art '{'str': 'Aikido'}' #: lang/json/martial_art_from_json.py #, python-format msgid "%s changes into a relaxed combat posture." -msgstr "" +msgstr "%s cambia y adopta una postura relajada de combate." #: lang/json/martial_art_from_json.py msgid "Aikido Stance" -msgstr "" +msgstr "Postura de Aikido" #. ~ Description of buff 'Aikido Stance' for martial art '{'str': 'Aikido'}' #: lang/json/martial_art_from_json.py @@ -134909,10 +135710,13 @@ msgid "" "\n" "Blocked damage reduced by 100% of Dexterity." msgstr "" +"Al dejar de lado la ofensiva en favor de la defensa propia, te hacés mejor protegiendo.\n" +"\n" +"El daño bloqueado se reduce un 100% de la Destreza." #: lang/json/martial_art_from_json.py msgid "Intermediate Aikido" -msgstr "" +msgstr "Aikido Intermedio" #. ~ Description of buff 'Intermediate Aikido' for martial art '{'str': #. 'Aikido'}' @@ -134924,10 +135728,14 @@ msgid "" "Blocked Damage reduced by 100% of Dexterity.\n" "+1 Block attempts, +1 Dodge attempts." msgstr "" +"Un practicante intermedio de aikido puede protegerse contra varios oponentes a la vez.\n" +"\n" +"El daño bloqueado se reduce un 100% de la Destreza.\n" +"+1 intentos de Bloqueo, +1 intentos de Esquivar." #: lang/json/martial_art_from_json.py msgid "Advanced Aikido" -msgstr "" +msgstr "Aikido Avanzado" #. ~ Description of buff 'Advanced Aikido' for martial art '{'str': 'Aikido'}' #: lang/json/martial_art_from_json.py @@ -134936,6 +135744,9 @@ msgid "" "\n" "+1 Block attempts, +1 Dodge attempts." msgstr "" +"Un practicante avanzado de aikido puede protegerse contra varios oponentes a la vez, más que lo normal.\n" +"\n" +"+1 intentos de Bloqueo, +1 intentos de Esquivar." #: lang/json/martial_art_from_json.py msgid "Boxing" @@ -134948,21 +135759,24 @@ msgid "" "of the Victorian era. Strength reduces blocked damage and moving increase " "dodge skill." msgstr "" +"El deporte del verdadero caballero. El boxeo moderno ha evolucionado desde " +"los combates profesionales de la era victoriana. La fuerza reduce el daño " +"bloqueado y moverse incrementa la habilidad para esquivar." #. ~ initiate message for martial art '{'str': 'Boxing'}' #: lang/json/martial_art_from_json.py msgid "You lower your chin and raise your fists to eye level." -msgstr "" +msgstr "Bajás tu mentón y levantás tus puños al nivel de los ojos." #. ~ initiate message for martial art '{'str': 'Boxing'}' #: lang/json/martial_art_from_json.py #, python-format msgid "%s prepares to fight with raised fists." -msgstr "" +msgstr "%s se prepara para pelear levantando los puños." #: lang/json/martial_art_from_json.py msgid "Boxing Stance" -msgstr "" +msgstr "Postura de Boxeo" #. ~ Description of buff 'Boxing Stance' for martial art '{'str': 'Boxing'}' #: lang/json/martial_art_from_json.py @@ -134972,6 +135786,9 @@ msgid "" "\n" "+2 Bash damage, Blocked damge reduced by 50% of Strength." msgstr "" +"Una postura sólida te permite bloquear más daño que lo normal y meter mejores piñas.\n" +"\n" +"+2 al daño Golpeante, Daño bloqueado disminuido en 50% de la Fuerza." #: lang/json/martial_art_from_json.py msgid "Footwork" @@ -134985,6 +135802,10 @@ msgid "" "+1.0 Dodge skill.\n" "Lasts for 1 turns. Stacks 2 times." msgstr "" +"Te hacés más difícil de recibir golpes al mecerte y zigzaguear mientras te movés.\n" +"\n" +"+1.0 a Esquivar.\n" +"Dura 1 turno. Se acumula hasta 2 veces." #: lang/json/martial_art_from_json.py msgid "Counter Chance" @@ -134999,6 +135820,10 @@ msgid "" "+25% Bash damage.\n" "Lasts for 1 turn." msgstr "" +"Ya tuviste tu oportunidad. ¡Ahora golpeá!\n" +"\n" +"+25% en daño Golpeante.\n" +"Dura 1 turno." #: lang/json/martial_art_from_json.py msgid "Brawling" @@ -135011,21 +135836,24 @@ msgid "" "unarmed or with any weapon to a certain extent. It's not stylish or " "sporting, but it gets the job done." msgstr "" +"Estás acostumbrado a la pelea mano contra pata. Sabés cómo pelear desarmado " +"o con cualquier arma hasta cierto punto. No tiene estilo ni es deportivo, " +"pero funciona bien." #. ~ initiate message for martial art '{'str': 'Brawling'}' #: lang/json/martial_art_from_json.py msgid "You grit your teeth and prepare for a good fight." -msgstr "" +msgstr "Apretás los dientes y te preparás para una buena pelea." #. ~ initiate message for martial art '{'str': 'Brawling'}' #: lang/json/martial_art_from_json.py #, python-format msgid "%s gets ready to brawl." -msgstr "" +msgstr "%s se prepara para las piñas." #: lang/json/martial_art_from_json.py msgid "Enhanced Blocking" -msgstr "" +msgstr "Bloqueo Mejorado" #. ~ Description of buff 'Enhanced Blocking' for martial art '{'str': #. 'Brawling'}' @@ -135035,6 +135863,9 @@ msgid "" "\n" "+1 Block attempts." msgstr "" +"La experiencia en el combate te llevó a ser capaz de bloquear varios ataques a la vez.\n" +"\n" +"+1 intentos de Bloqueo." #: lang/json/martial_art_from_json.py msgid "Capoeira" @@ -135047,21 +135878,25 @@ msgid "" "on fluid movement and sweeping kicks. Moving briefly enables stronger " "techniques. Missing an attack grants bonus damage for a short time." msgstr "" +"Es un estilo similar a la danza que tiene sus raíces en la esclavitud en " +"Brasil. La capoeira está focalizada en la fluidez de los movimientos y las " +"patadas barridas. Moverte habilita brevemente las técnicas más potentes. Al " +"errar un ataque tenés un bonus al daño por un breve tiempo." #. ~ initiate message for martial art '{'str': 'Capoeira'}' #: lang/json/martial_art_from_json.py msgid "You begin performing the ginga." -msgstr "" +msgstr "Empezás a interpretar la ginga." #. ~ initiate message for martial art '{'str': 'Capoeira'}' #: lang/json/martial_art_from_json.py #, python-format msgid "%s begins to rhythmically rock back and forth." -msgstr "" +msgstr "%s comienza a moverse hacia adelante y atrás de manera rítmica." #: lang/json/martial_art_from_json.py msgid "Capoeira Stance" -msgstr "" +msgstr "Postura de Capoeira" #. ~ Description of buff 'Capoeira Stance' for martial art '{'str': #. 'Capoeira'}' @@ -135071,6 +135906,9 @@ msgid "" "\n" "+1.0 Dodge skill, +1 Dodge attempts." msgstr "" +"No dejás de moverte nunca mientras hacés la ginga. Esto te hace muy movedizo mientras peleás.\n" +"\n" +"+1.0 a Esquivar, +1 intentos de Esquivar." #: lang/json/martial_art_from_json.py msgid "Capoeira Momentum" @@ -135086,6 +135924,11 @@ msgid "" "Enables \"Spin Kick\" and \"Sweep Kick\" techniques.\n" "Lasts 3 turns." msgstr "" +"Podés sentir el ritmo mientras te movés. No solo sos más difícil de golpear, ¡también tus patadas son más copadas!\n" +"\n" +"+1.0 a Esquivar.\n" +"Habilita las técnicas \"Patada Giratoria\" y \"Patada Barredora\".\n" +"Dura 3 turnos." #: lang/json/martial_art_from_json.py msgid "Capoeira Tempo" @@ -135101,6 +135944,10 @@ msgid "" "+15% Bash damage.\n" "Lasts 2 turns. Stacks 3 times." msgstr "" +"No le erraste, es solamente parte de la danza y ¡la mejor parte está por empezar!\n" +"\n" +"+15% al daño Golpeante.\n" +"Dura 2 turnos. Se acumula hasta 3 veces." #: lang/json/martial_art_from_json.py msgid "Crane Kung Fu" @@ -135113,17 +135960,21 @@ msgid "" "techniques and jumping dodges. Dexterity determines your damage, rather " "than Strength; you also receive a dodge bonus move or dodge an attack." msgstr "" +"Uno de los cinco estilos animales Shaolin. La Grulla utiliza técnicas " +"intrincadas de mano y saltos para esquivar. La destreza determina el daño " +"que causás, más que la fuerza. También tenés un bonus a un movimiento para " +"esquivar o a esquivar un ataque." #. ~ initiate message for martial art '{'str': 'Crane Kung Fu'}' #: lang/json/martial_art_from_json.py msgid "You raise your leg slightly and balance like a crane." -msgstr "" +msgstr "Levantás tu pierna levemente y te balanceas como una grulla." #. ~ initiate message for martial art '{'str': 'Crane Kung Fu'}' #: lang/json/martial_art_from_json.py #, python-format msgid "%s assumes a crane-like stance." -msgstr "" +msgstr "%s adopta la postura de la grulla." #: lang/json/martial_art_from_json.py msgid "Crane's Precision" @@ -135139,6 +135990,10 @@ msgid "" "\n" "Bash damage increased by 75% of Dexterity but decreased by 75% of Strength." msgstr "" +"Tus ataques golpean las debilidades de tus enemigos con velocidad y precisión en lugar de fuerza bruta.\n" +"La Destreza incrementa el daño de cuerpo a cuerpo en lugar de la Fuerza.\n" +"\n" +"El daño Golpeante se incrementa en 75% de la Destreza pero disminuy en un 75% de la Fuerza." #: lang/json/martial_art_from_json.py msgid "Crane's Flight" @@ -135153,10 +136008,14 @@ msgid "" "+1.0 Dodge skill.\n" "Lasts 2 turns." msgstr "" +"Igualito a un pájaro, te elevás en el aire para esquivar el peligro.\n" +"\n" +"+1.0 a Esquivar.\n" +"Dura 2 turnos." #: lang/json/martial_art_from_json.py msgid "Crane's Grace" -msgstr "" +msgstr "Gracia de Grulla" #. ~ Description of buff 'Crane's Grace' for martial art '{'str': 'Crane Kung #. Fu'}' @@ -135167,6 +136026,10 @@ msgid "" "+1 Dodge attempts, +1.0 Dodge skill.\n" "Lasts 2 turns." msgstr "" +"Similar a una grulla, te volvés rápido para esquivar el peligro.\n" +"\n" +"+1 intentos de Esquivar, +1.0 a Esquivar.\n" +"Dura 2 turnos." #: lang/json/martial_art_from_json.py msgid "Dragon Kung Fu" @@ -135180,17 +136043,21 @@ msgid "" "Your attacks lead to counterattacks which disable your opponents and set " "them up for a powerful finishing move." msgstr "" +"Uno de los cinco estilos animales Shaolin. El Dragón utiliza movimientos " +"fluidos y golpes fuertes. La Inteligencia mejora tu precisión en lugar de la" +" Destreza. Tus ataques son para contratacar lo que desarma a tus oponentes y" +" los prepara para un potente movimiento final." #. ~ initiate message for martial art '{'str': 'Dragon Kung Fu'}' #: lang/json/martial_art_from_json.py msgid "You relax and patiently await conflict like the great dragon." -msgstr "" +msgstr "Te relajás y esperás pacientemente el conflicto como un gran dragón." #. ~ initiate message for martial art '{'str': 'Dragon Kung Fu'}' #: lang/json/martial_art_from_json.py #, python-format msgid "%s assumes a dragon-like stance." -msgstr "" +msgstr "%s adopta la postura del dragón." #: lang/json/martial_art_from_json.py msgid "Dragon's Flight" @@ -135206,10 +136073,15 @@ msgid "" "Enables \"Dragon Vortex Block\" and \"Dragon Wing Dodge\"\n" "Lasts 1 turn." msgstr "" +"La vida y el combate son un círculo. El ataque lleva a un contraataque y a un ataque otra vez. Buscá completar el ciclo.\n" +"\n" +"+1 a Precisión, +2 daño Golpeante.\n" +"Habilita \"Bloqueo del Dragón\" y \"Ala Esquivadora de Dragón\"\n" +"Dura 1 turno." #: lang/json/martial_art_from_json.py msgid "Dragon's Knowledge" -msgstr "" +msgstr "Conocimiento de Dragón" #. ~ Description of buff 'Dragon's Knowledge' for martial art '{'str': 'Dragon #. Kung Fu'}' @@ -135221,6 +136093,10 @@ msgid "" "\n" "Accuracy increased by 25% of Intelligence but decreased by 25% of Dexterity." msgstr "" +"Planeás tu ataque con mucha anticipación confiando en tu intuición en lugar de tu velocidad para atacar.\n" +"La Inteligencia incrementa la Precisión en lugar de la Destreza.\n" +"\n" +"La Precisión se incrementa en un 25% de la Inteligencia pero disminuye en un 25% de la Destreza." #: lang/json/martial_art_from_json.py msgid "Eskrima" @@ -135240,17 +136116,17 @@ msgstr "" #. ~ initiate message for martial art '{'str': 'Eskrima'}' #: lang/json/martial_art_from_json.py msgid "You enter an open guard stance and prepare to strike." -msgstr "" +msgstr "Entrás en una postura de guardia abierta y te preparás para golpear." #. ~ initiate message for martial art '{'str': 'Eskrima'}' #: lang/json/martial_art_from_json.py #, python-format msgid "%s enters an open stance." -msgstr "" +msgstr "%s entra en una postura abierta." #: lang/json/martial_art_from_json.py msgid "Eskrima Stance" -msgstr "" +msgstr "Postura de Eskrima" #. ~ Description of buff 'Eskrima Stance' for martial art '{'str': 'Eskrima'}' #: lang/json/martial_art_from_json.py @@ -135259,6 +136135,9 @@ msgid "" "\n" "+2 Accuracy." msgstr "" +"Tenés habilidad para sacar lo mejor de cada arma. El término 'arma' puede ser muy subjetivo.\n" +"\n" +"+2 a Precisión." #: lang/json/martial_art_from_json.py msgid "Eskrima Combination" @@ -135275,6 +136154,11 @@ msgid "" "Enables \"Combination Strike\" technique.\n" "Lasts 3 turns. Stacks 3 times." msgstr "" +"Podés continuar un golpe crítico con un ataque más potente si la oportunidad se presenta.\n" +"\n" +"+15% bonus a todo el daño.\n" +"Habilita la técnica \"Golpe Combinado\".\n" +"Dura 3 turnos. Se acumula hasta 3 veces." #: lang/json/martial_art_from_json.py msgid "Fencing" @@ -135288,21 +136172,25 @@ msgid "" "Skilled fencers can take advantage of blocks and feints to deliver accurate " "strikes." msgstr "" +"El noble arte de la esgrima se aprende con espadas flexibles de competición," +" pero las técnicas provienen de (y se aplican a) ejemplos más funcionales. " +"Los esgrimistas habilidosos pueden sacar ventaja de los bloqueos y fintas " +"para ejecutar golpes precisos." #. ~ initiate message for martial art '{'str': 'Fencing'}' #: lang/json/martial_art_from_json.py msgid "You move into the en-garde stance." -msgstr "" +msgstr "Te movés para ponerte en-garde." #. ~ initiate message for martial art '{'str': 'Fencing'}' #: lang/json/martial_art_from_json.py #, python-format msgid "%s moves into a fencing stance." -msgstr "" +msgstr "%s se mueve para adoptar la postura de esgrima." #: lang/json/martial_art_from_json.py msgid "Fencing Stance" -msgstr "" +msgstr "Postura de Esgrima" #. ~ Description of buff 'Fencing Stance' for martial art '{'str': 'Fencing'}' #: lang/json/martial_art_from_json.py @@ -135312,6 +136200,9 @@ msgid "" "\n" "Blocked damage reduced by 50% of Dexterity." msgstr "" +"Tu postura de costado minimiza las oportunidades de ser dañado en el combate.\n" +"\n" +"El daño bloqueado se reduce un 50% de la Destreza." #: lang/json/martial_art_from_json.py lang/json/technique_from_json.py msgid "Parry" @@ -135325,10 +136216,14 @@ msgid "" "+1 Accuracy.\n" "Lasts 1 turn." msgstr "" +"Tu próximo golpe encontrará la marca mucho más fácil desde el golpe que paraste.\n" +"\n" +"+1 a Precisión.\n" +"Dura 1 turno." #: lang/json/martial_art_from_json.py msgid "Remise" -msgstr "" +msgstr "Remise" #. ~ Description of buff 'Remise' for martial art '{'str': 'Fencing'}' #: lang/json/martial_art_from_json.py @@ -135339,6 +136234,11 @@ msgid "" "Enables \"Compound Attack\" technique.\n" "Lasts 1 turn." msgstr "" +"¡Tu finta es la trampa perfecta para el ataque devastador que le sigue!\n" +"\n" +"+1 a Precisión.\n" +"Habilita la técnica \"Ataque Compuesto\".\n" +"Dura 1 turno." #. ~ Description for martial art '{'str': 'Fior Di Battaglia'}' #: lang/json/martial_art_from_json.py @@ -135347,21 +136247,24 @@ msgid "" "\"Flower of Battle\" places great focus on countering one's opponent and " "knocking them down before landing a killing blow" msgstr "" +"Son técnicas marciales de la Europa medieval para lucha con armas de asta. " +"La \"Flor de la Batalla\" pone mucha atención en contrarrestar al oponente y" +" derribarlo antes de dar el golpe final." #. ~ initiate message for martial art '{'str': 'Fior Di Battaglia'}' #: lang/json/martial_art_from_json.py msgid "You hold your weapon in a firm grip, ready to block any attack." -msgstr "" +msgstr "Agarrás tu arma firmemente, preparado para bloquear cualquier ataque." #. ~ initiate message for martial art '{'str': 'Fior Di Battaglia'}' #: lang/json/martial_art_from_json.py #, python-format msgid "%s grips their weapon tightly." -msgstr "" +msgstr "%s agarra su arma firmemente." #: lang/json/martial_art_from_json.py msgid "Stand Your Ground" -msgstr "" +msgstr "Mantenerse Firme" #. ~ Description of buff 'Stand Your Ground' for martial art '{'str': 'Fior Di #. Battaglia'}' @@ -135372,10 +136275,13 @@ msgid "" "\n" "+2 Block attempts, -1.0 Dodge skill, blocked damage reduced by 50% of Strength." msgstr "" +"Sos recio y no te rendirás ante ninguna amenaza.\n" +"\n" +"+2 intentos de Bloqueo, -1.0 a Esquivar, daño bloqueado reducido en 50% de la Fuerza." #: lang/json/martial_art_from_json.py msgid "Tactical Retreat" -msgstr "" +msgstr "Retirada Táctica" #. ~ Description of buff 'Tactical Retreat' for martial art '{'str': 'Fior Di #. Battaglia'}' @@ -135387,10 +136293,14 @@ msgid "" "-2 Block attempts, +1.0 Dodge skill, blocked damaged increased by 50% of Strength.\n" "Lasts 1 turn." msgstr "" +"¡Te movés y anulás los efectos de Mantenerse Firme!\n" +"\n" +"-2 intentos de Bloqueo, +1.0 a Esquivar, daño bloqueado reducido en 50% de la Fuerza.\n" +"Dura 1 turno." #: lang/json/martial_art_from_json.py msgid "Defense Break" -msgstr "" +msgstr "Romper Defensa" #. ~ Description of buff 'Defense Break' for martial art '{'str': 'Fior Di #. Battaglia'}' @@ -135401,10 +136311,14 @@ msgid "" "+1 Accuracy.\n" "Lasts 1 turn. Stacks 3 times." msgstr "" +"Cada bloqueo exitoso revela un abertura en la guardia de tu oponente.\n" +"\n" +"+1 a Precisión.\n" +"Dura 1 turno. Se acumula hasta 3 veces." #: lang/json/martial_art_from_json.py msgid "Tactical Feinting" -msgstr "" +msgstr "Finta Táctica" #. ~ Description of buff 'Tactical Feinting' for martial art '{'str': 'Fior Di #. Battaglia'}' @@ -135415,6 +136329,10 @@ msgid "" "Enables \"Hook and Drag\" technique.\n" "Lasts 1 turn." msgstr "" +"¡Se tragan tu finta!\n" +"\n" +"Habilita la técnica \"Enganchar y Arrastrar\".\n" +"Dura 1 turno." #: lang/json/martial_art_from_json.py msgid "Judo" @@ -135427,21 +136345,25 @@ msgid "" "offensive. You are resistant to most effects that can knock you down and " "can counter grab and takedown attacks with strong judo throw." msgstr "" +"El judo es un arte marcial que se focaliza en agarradas y tiradas, tanto " +"defensivas como ofensivas. Sos resistente a la mayoría de los efectos que " +"pueden derribarte y podés contrarrestar un agarre y desarmar ataques con una" +" tirada de judo." #. ~ initiate message for martial art '{'str': 'Judo'}' #: lang/json/martial_art_from_json.py msgid "You prepare yourself for a grapple." -msgstr "" +msgstr "Te preparás para un agarre." #. ~ initiate message for martial art '{'str': 'Judo'}' #: lang/json/martial_art_from_json.py #, python-format msgid "%s prepares for a grapple." -msgstr "" +msgstr "%s se prepara para un agarre." #: lang/json/martial_art_from_json.py msgid "Judo Stance" -msgstr "" +msgstr "Postura de Judo" #. ~ Description of buff 'Judo Stance' for martial art '{'str': 'Judo'}' #: lang/json/martial_art_from_json.py @@ -135449,6 +136371,8 @@ msgid "" "Your knowledge of grappling allows you to recover from knock down effects instantly.\n" "In addition, you can counter grabs and takedown attacks with a judo throw." msgstr "" +"Tu conocimiento de los agarres te permite recuperarte de una caída instantáneamente.\n" +"Además, podés contrarrestar agarres y desarmar ataques con una tirada de judo." #: lang/json/martial_art_from_json.py msgid "Karate" @@ -135469,17 +136393,17 @@ msgstr "" #. ~ initiate message for martial art '{'str': 'Karate'}' #: lang/json/martial_art_from_json.py msgid "You adopt a classic karate stance." -msgstr "" +msgstr "Adoptás la clásica postura de karate." #. ~ initiate message for martial art '{'str': 'Karate'}' #: lang/json/martial_art_from_json.py #, python-format msgid "%s adopts a classic karate stance." -msgstr "" +msgstr "%s adopta la clásica postura de karate." #: lang/json/martial_art_from_json.py msgid "Karate Strike" -msgstr "" +msgstr "Golpe de Karate" #. ~ Description of buff 'Karate Strike' for martial art '{'str': 'Karate'}' #: lang/json/martial_art_from_json.py @@ -135490,10 +136414,14 @@ msgid "" "+2 Block attempts, +1 Dodges attempts, blocked damge reduced by 50% of Strength.\n" "Lasts 2 turns." msgstr "" +"Acertar un golpe te permite posicionarte perfectamente para una defensa máxima contra múltiples oponentes.\n" +"\n" +"+2 intentos de Bloqueo, +1 intentos de Esquivar, daño bloqueado reducido en 50% de la Fuerza.\n" +"Dura 2 turnos." #: lang/json/martial_art_from_json.py msgid "Karate Stance" -msgstr "" +msgstr "Postura de Karate" #. ~ Description of buff 'Karate Stance' for martial art '{'str': 'Karate'}' #: lang/json/martial_art_from_json.py @@ -135502,6 +136430,9 @@ msgid "" "\n" "+2 Accuracy." msgstr "" +"Tu seria postura te permite golpear con mayor precisión.\n" +"\n" +"+2 a Precisión." #: lang/json/martial_art_from_json.py msgid "Krav Maga" @@ -135522,17 +136453,17 @@ msgstr "" #. ~ initiate message for martial art '{'str': 'Krav Maga'}' #: lang/json/martial_art_from_json.py msgid "You assume a practical combat stance." -msgstr "" +msgstr "Asumís una postura práctica para el combate." #. ~ initiate message for martial art '{'str': 'Krav Maga'}' #: lang/json/martial_art_from_json.py #, python-format msgid "%s assumes a practical combat stance." -msgstr "" +msgstr "%s asume una postura práctica para el combate." #: lang/json/martial_art_from_json.py msgid "Krav Maga Stance" -msgstr "" +msgstr "Postura de Krav Maga" #. ~ Description of buff 'Krav Maga Stance' for martial art '{'str': 'Krav #. Maga'}' @@ -135542,6 +136473,9 @@ msgid "" "\n" "+1 Accuracy, +1 Block attempts." msgstr "" +"Tu entrenamiento hace más fácil acertar golpes y pelear contra múltiples oponentes.\n" +"\n" +"+1 a Precisión, +1 intentos de Bloqueo." #: lang/json/martial_art_from_json.py msgid "Leopard Kung Fu" @@ -135555,21 +136489,25 @@ msgid "" "than Strength. Moving increases dodge skill and accuracy further; attacking" " after moving increases damage." msgstr "" +"Uno de los cinco estilos animales Shaolin. El Leopardo se focaliza en golpes" +" rápidos, estratégicamente planeados. La Destreza determina tu daño, en " +"lugar de la Fuerza. Al moverte incrementás más tu habilidad para esquivar y " +"tu precisión; atacar después de moverte incrementa el daño." #. ~ initiate message for martial art '{'str': 'Leopard Kung Fu'}' #: lang/json/martial_art_from_json.py msgid "You prepare to pounce like a leopard." -msgstr "" +msgstr "Te preparás para saltar como un leopardo." #. ~ initiate message for martial art '{'str': 'Leopard Kung Fu'}' #: lang/json/martial_art_from_json.py #, python-format msgid "%s assumes a leopard-like stance." -msgstr "" +msgstr "%s adopta una postura de leopardo." #: lang/json/martial_art_from_json.py msgid "Leopard's Strategy" -msgstr "" +msgstr "Estrategia del Leopardo" #. ~ Description of buff 'Leopard's Strategy' for martial art '{'str': #. 'Leopard Kung Fu'}' @@ -135581,10 +136519,14 @@ msgid "" "\n" "Bash damage increased by 75% of Dexterity but decreased by 75% of Strength." msgstr "" +"Peleás abrumando a tus oponentes con golpes rápidos que son mucho más difíciles de defender.\n" +"La Destreza incrementa el daño de cuerpo a cuerpo en lugar de la Fuerza.\n" +"\n" +"El daño Golpeante se incrementa en 75% de la Destreza pero disminuy en un 75% de la Fuerza." #: lang/json/martial_art_from_json.py msgid "Leopard's Agility" -msgstr "" +msgstr "Agilidad de Leopardo" #. ~ Description of buff 'Leopard's Agility' for martial art '{'str': 'Leopard #. Kung Fu'}' @@ -135594,6 +136536,9 @@ msgid "" "\n" "+1.0 Dodge skill." msgstr "" +"Igual que un gato, sos rápido, ágil y difícil de agarrar.\n" +"\n" +"+1.0 a Esquivar." #: lang/json/martial_art_from_json.py msgid "Leopard's Stalk" @@ -135609,10 +136554,15 @@ msgid "" "Enables \"Leopard's Pounce\" buff.\n" "Lasts 1 turn." msgstr "" +"Acechás orgullosamente por las sombras y te preparas para saltar con una furia imparable.\n" +"\n" +"+2 a Precisión.\n" +"Habilita la técnica \"Salto del Leopardo\".\n" +"Dura 1 turno." #: lang/json/martial_art_from_json.py msgid "Leopard's Pounce" -msgstr "" +msgstr "Salto del Leopardo" #. ~ Description of buff 'Leopard's Pounce' for martial art '{'str': 'Leopard #. Kung Fu'}' @@ -135624,6 +136574,10 @@ msgid "" "+25% bonus to all damage.\n" "Lasts 1 turn." msgstr "" +"Estás preparado. ¡Atacá y reclamá tu presa!\n" +"\n" +"+25% bonus a todo el daño.\n" +"Dura 1 turno." #: lang/json/martial_art_from_json.py msgid "Medieval Swordsmanship" @@ -135638,6 +136592,11 @@ msgid "" "treatise compares the Italian and German traditions of medieval combat with " "detailed step-by-step pictures." msgstr "" +"El arte de la espada larga y de la espada y escudo, previo al posterior " +"desarrollo de la esgrima. Diseñado para el combate tanto con armadura como " +"sin ella, incluye el forcejeo y también las técnicas defensivas y ofensivas " +"con la espada. Este tratado comprara las tradiciones italiana y alemana del " +"combate medieval con dibujos detallados." #. ~ initiate message for martial art '{'str': 'Medieval Swordsmanship'}' #: lang/json/martial_art_from_json.py @@ -137042,7 +138001,7 @@ msgstr "" #. ~ initiate message for martial art 'C.R.I.T Enforcement' #: lang/json/martial_art_from_json.py msgid "You ready yourself to stand your ground." -msgstr "" +msgstr "Te preparás para mantenerte firme." #. ~ initiate message for martial art 'C.R.I.T Enforcement' #: lang/json/martial_art_from_json.py @@ -149038,7 +149997,7 @@ msgstr "Miembros Elásticos" #. ~ Description for {'str': 'Stretchy Limbs'} #: lang/json/mutation_from_json.py msgid "Your limbs seem to have a little more 'give' to them. +1 Dexterity." -msgstr "Tus miembros parecen tener un poco más de elasticidad. Destreza +1." +msgstr "Tus miembros parecen tener un poco más de elasticidad. +1 a Destreza." #: lang/json/mutation_from_json.py msgid "Rubbery Limbs" @@ -205624,31 +206583,31 @@ msgstr "" #: lang/json/technique_from_json.py msgid "Sweep Kick" -msgstr "" +msgstr "Patada Barredora" #: lang/json/technique_from_json.py #, python-format msgid "You sweep kick %s" -msgstr "" +msgstr "Le tirás una patada barredora al %s" #: lang/json/technique_from_json.py #, python-format msgid " sweep kicks %s" -msgstr "" +msgstr " le tira una patada barredora al %s" #: lang/json/technique_from_json.py msgid "Spin Kick" -msgstr "" +msgstr "Patada Giratoria" #: lang/json/technique_from_json.py #, python-format msgid "You spin kick %s" -msgstr "" +msgstr "Le tirás una patada giratoria al %s" #: lang/json/technique_from_json.py #, python-format msgid " spin kicks %s" -msgstr "" +msgstr " le tira una patada giratoria al %s" #: lang/json/technique_from_json.py msgid "Crane Wing" @@ -205708,21 +206667,21 @@ msgstr "" #: lang/json/technique_from_json.py msgid "Dragon Claw" -msgstr "" +msgstr "Garra de Dragón" #: lang/json/technique_from_json.py #, python-format msgid "You lash out at %s with a Dragon Claw" -msgstr "" +msgstr "Atacás al %s con la Garra de Dragón" #: lang/json/technique_from_json.py #, python-format msgid " lashes out at %s with a Dragon Claw" -msgstr "" +msgstr " ataca al %s con la Garra de Dragón" #: lang/json/technique_from_json.py msgid "Dragon Vortex Block" -msgstr "" +msgstr "Bloqueo del Dragón" #: lang/json/technique_from_json.py #, python-format @@ -205736,7 +206695,7 @@ msgstr "" #: lang/json/technique_from_json.py msgid "Dragon Wing Dodge" -msgstr "" +msgstr "Ala Esquivadora de Dragón" #: lang/json/technique_from_json.py #, python-format @@ -205750,17 +206709,17 @@ msgstr "" #: lang/json/technique_from_json.py msgid "Dragon Tail" -msgstr "" +msgstr "Cola de Dragón" #: lang/json/technique_from_json.py #, python-format msgid "You sweep %s with a quick Dragon Tail" -msgstr "" +msgstr "Barrés al %s con una rápida Cola de Dragón" #: lang/json/technique_from_json.py #, python-format msgid " sweeps %s with a quick Dragon Tail sweep" -msgstr "" +msgstr " barre al %s con una rápida barrida de la Cola de Dragón" #: lang/json/technique_from_json.py msgid "Dragon Strike" @@ -205769,12 +206728,12 @@ msgstr "Golpe de Dragón" #: lang/json/technique_from_json.py #, python-format msgid "You descend upon %s with a powerful Dragon Strike" -msgstr "" +msgstr "Descendés sobre el %s con un potente Golpe de Dragón" #: lang/json/technique_from_json.py #, python-format msgid " descends upon %s with a powerful Dragon Strike" -msgstr "" +msgstr " desciende sobre el %s con un potente Golpe de Dragón" #: lang/json/technique_from_json.py msgid "Round Strike" @@ -205820,7 +206779,7 @@ msgstr " le da un golpe seco al %s" #: lang/json/technique_from_json.py msgid "Combination Strike" -msgstr "" +msgstr "Golpe Combinado" #: lang/json/technique_from_json.py #, python-format @@ -205886,17 +206845,18 @@ msgstr " le da un golpe de esgrima al %s" #: lang/json/technique_from_json.py msgid "Compound Attack" -msgstr "" +msgstr "Ataque Compuesto" #: lang/json/technique_from_json.py #, python-format msgid "Your feint leads to a compound attack against %s" -msgstr "" +msgstr "Tu finta te lleva a hacer un ataque compuesto contra el %s" #: lang/json/technique_from_json.py #, python-format msgid "'s feint leads to a compound attack against %s" msgstr "" +"La finta de lo lleva a hacer un ataque compuesto contra el %s" #: lang/json/technique_from_json.py msgid "Fencing Riposte" @@ -205966,7 +206926,7 @@ msgstr "" #: lang/json/technique_from_json.py msgid "Hook and Drag" -msgstr "" +msgstr "Enganchar y Arrastrar" #: lang/json/technique_from_json.py #, python-format @@ -206704,12 +207664,12 @@ msgstr "" #: lang/json/technique_from_json.py #, python-format msgid "You crouch low and sweep-kick %s" -msgstr "" +msgstr "Te agachás y le tirás una patada barredora al %s" #: lang/json/technique_from_json.py #, python-format msgid " crouches low and sweep-kicks %s" -msgstr "" +msgstr " se agacha y le tira una patada barredora al %s" #: lang/json/technique_from_json.py #, python-format @@ -216956,6 +217916,15 @@ msgid "" "extending the time until the food spoils." msgstr "" +#. ~ Description for {'str': 'wooden wheel mount'} +#: lang/json/vehicle_part_from_json.py +msgid "A piece of wood with holes suitable for a bike or motorbike wheel." +msgstr "" + +#: lang/json/vehicle_part_from_json.py +msgid "wooden wheel mount (steerable)" +msgstr "" + #: lang/json/vehicle_part_from_json.py msgid "light wheel mount (steerable)" msgstr "" @@ -226912,7 +227881,7 @@ msgstr "¡Golpe en la cabeza!" #: src/creature.cpp src/iuse.cpp src/melee.cpp msgid "Critical!" -msgstr "Critico!" +msgstr "¡Crítico!" #: src/creature.cpp msgid "Good hit!" @@ -248490,13 +249459,13 @@ msgstr "%s pero no causás daño." #: src/melee.cpp #, c-format msgid "%s. Critical!" -msgstr "%s. ¡Critico!" +msgstr "%s. ¡Crítico!" #. ~ someone hits something for %d damage (critical) #: src/melee.cpp #, c-format msgid "%s for %d damage. Critical!" -msgstr "" +msgstr "%s por %d de daño. ¡Crítico!" #. ~ NPC hits something #: src/melee.cpp diff --git a/lang/po/es_ES.po b/lang/po/es_ES.po index 48399e4e65948..3b1825809680a 100644 --- a/lang/po/es_ES.po +++ b/lang/po/es_ES.po @@ -16,7 +16,7 @@ msgid "" msgstr "" "Project-Id-Version: cataclysm-dda 0.E\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-06-23 10:06+0800\n" +"POT-Creation-Date: 2020-06-26 12:50+0800\n" "PO-Revision-Date: 2018-04-26 14:47+0000\n" "Last-Translator: Miguel de Dios Matias , 2020\n" "Language-Team: Spanish (Spain) (https://www.transifex.com/cataclysm-dda-translators/teams/2217/es_ES/)\n" @@ -51742,6 +51742,34 @@ msgid "" "Felt patches, bundled tightly together for storage. Disassemble to unpack." msgstr "" +#: lang/json/GENERIC_from_json.py +msgid "bundle of planks" +msgid_plural "bundles of planks" +msgstr[0] "" +msgstr[1] "" + +#. ~ Description for {'str': 'bundle of planks', 'str_pl': 'bundles of +#. planks'} +#: lang/json/GENERIC_from_json.py +msgid "" +"Ten construction planks securely lashed together with a rope. Disassemble " +"to unpack." +msgstr "" + +#: lang/json/GENERIC_from_json.py +msgid "bundle of stout branches" +msgid_plural "bundles of stout branches" +msgstr[0] "" +msgstr[1] "" + +#. ~ Description for {'str': 'bundle of stout branches', 'str_pl': 'bundles of +#. stout branches'} +#: lang/json/GENERIC_from_json.py +msgid "" +"Ten stout branches securely lashed together with a rope. Disassemble to " +"untie them." +msgstr "" + #: lang/json/GENERIC_from_json.py msgid "t-substrate sample" msgid_plural "t-substrate samples" @@ -63669,6 +63697,17 @@ msgstr "" "Una canillas de metal que puede ser puesta en un tanque de agua para " "facilitar el uso." +#: lang/json/GENERIC_from_json.py lang/json/vehicle_part_from_json.py +msgid "wooden wheel mount" +msgid_plural "wooden wheel mounts" +msgstr[0] "" +msgstr[1] "" + +#. ~ Description for {'str': 'wooden wheel mount'} +#: lang/json/GENERIC_from_json.py +msgid "A piece of wood with holes suitable for a bike wheel." +msgstr "" + #: lang/json/GENERIC_from_json.py lang/json/vehicle_part_from_json.py msgid "light wheel mount" msgid_plural "light wheel mounts" @@ -111313,8 +111352,8 @@ msgid "" msgstr "" #: lang/json/gun_from_json.py -msgid "MAS 223" -msgid_plural "MAS 223" +msgid "MAS .223" +msgid_plural "MAS .223" msgstr[0] "" msgstr[1] "" @@ -205133,6 +205172,15 @@ msgid "" "extending the time until the food spoils." msgstr "" +#. ~ Description for {'str': 'wooden wheel mount'} +#: lang/json/vehicle_part_from_json.py +msgid "A piece of wood with holes suitable for a bike or motorbike wheel." +msgstr "" + +#: lang/json/vehicle_part_from_json.py +msgid "wooden wheel mount (steerable)" +msgstr "" + #: lang/json/vehicle_part_from_json.py msgid "light wheel mount (steerable)" msgstr "" diff --git a/lang/po/ja.po b/lang/po/ja.po index 4d504309dc8a6..d9b0193f2c5e9 100644 --- a/lang/po/ja.po +++ b/lang/po/ja.po @@ -32,7 +32,7 @@ msgid "" msgstr "" "Project-Id-Version: cataclysm-dda 0.E\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-06-23 10:06+0800\n" +"POT-Creation-Date: 2020-06-26 12:50+0800\n" "PO-Revision-Date: 2018-04-26 14:47+0000\n" "Last-Translator: Pigmentblue15, 2020\n" "Language-Team: Japanese (https://www.transifex.com/cataclysm-dda-translators/teams/2217/ja/)\n" @@ -21586,14 +21586,14 @@ msgstr "背骨と脳幹の間に移植された爆弾です。タイマーが付 #: lang/json/BIONIC_ITEM_from_json.py lang/json/bionic_from_json.py msgid "Skullgun CBM" msgid_plural "Skullgun CBMs" -msgstr[0] "" +msgstr[0] "CBM: スカルガン" #. ~ Description for {'str': 'Skullgun CBM'} #: lang/json/BIONIC_ITEM_from_json.py lang/json/bionic_from_json.py msgid "" "Concealed in your head is a single shot .40 pistol. Activate the bionic to " "fire and reload the skullgun." -msgstr "" +msgstr "頭部に.40口径の単発銃を埋め込みます。起動することで射撃したり装填したりできます。" #: lang/json/BIONIC_ITEM_from_json.py msgid "Precision Solderers CBM" @@ -45546,6 +45546,32 @@ msgid "" "Felt patches, bundled tightly together for storage. Disassemble to unpack." msgstr "保管のためにしっかりと束ねたフェルトの生地です。分解して使いましょう。" +#: lang/json/GENERIC_from_json.py +msgid "bundle of planks" +msgid_plural "bundles of planks" +msgstr[0] "素材束(木材)" + +#. ~ Description for {'str': 'bundle of planks', 'str_pl': 'bundles of +#. planks'} +#: lang/json/GENERIC_from_json.py +msgid "" +"Ten construction planks securely lashed together with a rope. Disassemble " +"to unpack." +msgstr "10枚の建設用木材をロープで固定したものです。分解して使いましょう。" + +#: lang/json/GENERIC_from_json.py +msgid "bundle of stout branches" +msgid_plural "bundles of stout branches" +msgstr[0] "素材束(頑丈な短枝)" + +#. ~ Description for {'str': 'bundle of stout branches', 'str_pl': 'bundles of +#. stout branches'} +#: lang/json/GENERIC_from_json.py +msgid "" +"Ten stout branches securely lashed together with a rope. Disassemble to " +"untie them." +msgstr "10本の頑丈な短枝をロープで固定したものです。分解して使いましょう。" + #: lang/json/GENERIC_from_json.py msgid "t-substrate sample" msgid_plural "t-substrate samples" @@ -56113,6 +56139,16 @@ msgstr[0] "蛇口" msgid "A metal faucet that can be attached to a water tank for easy access." msgstr "金属製の蛇口です。給水タンクに取り付けると内部の液体を簡単に流せます。" +#: lang/json/GENERIC_from_json.py lang/json/vehicle_part_from_json.py +msgid "wooden wheel mount" +msgid_plural "wooden wheel mounts" +msgstr[0] "木製ホイールハブ" + +#. ~ Description for {'str': 'wooden wheel mount'} +#: lang/json/GENERIC_from_json.py +msgid "A piece of wood with holes suitable for a bike wheel." +msgstr "自転車のホイールを取り付ける穴が付いた木製部品です。" + #: lang/json/GENERIC_from_json.py lang/json/vehicle_part_from_json.py msgid "light wheel mount" msgid_plural "light wheel mounts" @@ -59254,7 +59290,7 @@ msgstr "分類: 木材" #. ~ Description for Loot: Wood #: lang/json/LOOT_ZONE_from_json.py msgid "Destination for firewood and items that can be used as such." -msgstr "焚き木として利用できるアイテムを置く区域です。" +msgstr "焚物として利用できるアイテムを置く区域です。" #: lang/json/LOOT_ZONE_from_json.py msgid "Loot: Custom" @@ -71582,19 +71618,19 @@ msgstr "頭蓋爆弾発動に使われる疑似呪文です。致命的なダメ #: lang/json/SPELL_from_json.py msgid "Skullgun Snapback" -msgstr "" +msgstr "スカルガン反動(CBM)" #. ~ Description for Skullgun Snapback #: lang/json/SPELL_from_json.py msgid "" "This fake spell occurs on skullgun activation. May be fatal if done in " "critical condition." -msgstr "" +msgstr "スカルガン発動に使われる疑似呪文です。体力によっては致命的なダメージを与える可能性があります。" #. ~ Message for SPELL 'Skullgun Snapback' #: lang/json/SPELL_from_json.py msgid "Your head snaps back from the force of the shot." -msgstr "" +msgstr "発射の反動で首が後ろに折れ曲がりました。" #: lang/json/SPELL_from_json.py msgid "psi stun" @@ -75038,7 +75074,7 @@ msgstr "雷の神トールが身に着けていた魔法の帯、またはその #: lang/json/TOOL_ARMOR_from_json.py msgid "lesser dimensional toolbelt" msgid_plural "lesser dimensional toolbelts" -msgstr[0] "" +msgstr[0] "小型次元ツールベルト" #. ~ Description for {'str': 'lesser dimensional toolbelt'} #: lang/json/TOOL_ARMOR_from_json.py @@ -75046,12 +75082,12 @@ msgid "" "A sturdy workman's belt that fits around your waist, covered in easy to " "access pouches. Like all dimensional spaces, they hold more than they " "should with a fair weight reduction." -msgstr "" +msgstr "収納しやすいポーチがたくさん付いた、腰にフィットする丈夫な作業用ベルトです。他の次元アイテムと同様に、収納した物の重量が軽減されます。" #: lang/json/TOOL_ARMOR_from_json.py msgid "greater dimensional toolbelt" msgid_plural "greater dimensional toolbelts" -msgstr[0] "" +msgstr[0] "大型次元ツールベルト" #. ~ Description for {'str': 'greater dimensional toolbelt'} #: lang/json/TOOL_ARMOR_from_json.py @@ -75060,6 +75096,7 @@ msgid "" "access pouches. Like all dimensional spaces, they hold far more than they " "should with a substantial weight reduction." msgstr "" +"収納しやすいポーチがたくさん付いた、腰にフィットする大型の作業用ベルトです。他の次元アイテムと同様に、収納した物の重量が大幅に軽減されます。" #: lang/json/TOOL_ARMOR_from_json.py msgid "Belt of Weaponry" @@ -90497,7 +90534,7 @@ msgstr "貯蔵穴の設置には深い穴が必要です。" #: lang/json/construction_from_json.py msgid "Mark firewood source" -msgstr "焚木置き場の印を付ける" +msgstr "焚物置き場の印を付ける" #: lang/json/construction_from_json.py msgid "" @@ -100107,9 +100144,9 @@ msgid "" msgstr "古風なデザインのAR-15を小型化した、自衛用に市販されている銃身約19cmの銃です。" #: lang/json/gun_from_json.py -msgid "MAS 223" -msgid_plural "MAS 223" -msgstr[0] "ライフル(.223口径/MAS 223)" +msgid "MAS .223" +msgid_plural "MAS .223" +msgstr[0] "ライフル(.223口径/MAS .223)" #: lang/json/gun_from_json.py msgid "" @@ -103640,11 +103677,11 @@ msgstr "三連レーザー" #: lang/json/gun_from_json.py msgid "bionic skullgun" msgid_plural "bionic skullguns" -msgstr[0] "" +msgstr[0] "ショットガン(.40口径/スカルガン)" #: lang/json/gun_from_json.py msgid "Bionic one-shot subdermal .40 pistol integrated with your head." -msgstr "" +msgstr "頭部と一体化した、.40口径単発銃です。" #: lang/json/gun_from_json.py msgid "CRIT .5 LP" @@ -108699,7 +108736,7 @@ msgstr "このアイテムは火を起こせます。" #. ~ Please leave anything in unchanged. #: lang/json/json_flag_from_json.py msgid "This item can serve as a firewood." -msgstr "このアイテムは焚き木として利用できます。" +msgstr "このアイテムは焚物として利用できます。" #. ~ Please leave anything in unchanged. #: lang/json/json_flag_from_json.py @@ -115247,6 +115284,10 @@ msgid "" "+1 Accuracy.\n" "Lasts 1 turn. Stacks 2 times" msgstr "" +"鍛錬と不屈の精神によって、集中力を高めることで物理的な脅威を克服します。\n" +"\n" +"命中+1\n" +"1ターン継続、最大蓄積数2" #: lang/json/martial_art_from_json.py msgid "Hylian Swordsmanship" @@ -115361,7 +115402,7 @@ msgstr "" #: lang/json/martial_art_from_json.py msgid "Charge Up" -msgstr "" +msgstr "突進" #. ~ Description of buff 'Charge Up' for martial art '{'str': 'Hylian #. Swordsmanship'}' @@ -115373,6 +115414,10 @@ msgid "" "+20% damage, enables \"Spin Attack\" technique.\n" "Lasts 1 turn." msgstr "" +"回転しながら強烈な斬撃を放つために、ある程度勢いをつけます。\n" +"\n" +"全与ダメージ+20%、「回転斬り」有効化\n" +"1ターン継続" #: lang/json/martial_art_from_json.py msgid "Iron Heart" @@ -115487,7 +115532,7 @@ msgstr "%sはバトルを挑む準備を整えました。" #: lang/json/martial_art_from_json.py msgid "Stamina" -msgstr "" +msgstr "持久力" #. ~ Description of buff 'Stamina' for martial art '{'str': 'Pokken'}' #: lang/json/martial_art_from_json.py @@ -115498,10 +115543,14 @@ msgid "" "Gain bash, cut, stab armor equal to 50% of Strength.\n" "Lasts 3 turns." msgstr "" +"攻撃を受けると防御力が上昇します。\n" +"\n" +"筋力x50%の打撃/斬撃/刺突耐性上昇\n" +"3ターン継続" #: lang/json/martial_art_from_json.py msgid "Sniper" -msgstr "" +msgstr "スナイパー" #. ~ Description of buff 'Sniper' for martial art '{'str': 'Pokken'}' #: lang/json/martial_art_from_json.py @@ -115512,10 +115561,14 @@ msgid "" "+50% damage.\n" "Lasts 1 turn." msgstr "" +"会心攻撃が命中すると攻撃力が上昇します。\n" +"\n" +"与ダメージ+50%\n" +"1ターン継続" #: lang/json/martial_art_from_json.py msgid "Moxie" -msgstr "" +msgstr "自信過剰" #. ~ Description of buff 'Moxie' for martial art '{'str': 'Pokken'}' #: lang/json/martial_art_from_json.py @@ -115526,6 +115579,10 @@ msgid "" "+50% damage.\n" "Lasts 3 turns." msgstr "" +"敵を倒すと攻撃力が上昇します。\n" +"\n" +"与ダメージ+50%\n" +"3ターン継続" #: lang/json/martial_art_from_json.py msgid "Setting Sun" @@ -115554,7 +115611,7 @@ msgstr "%sは重心をずらし、今までと異なる構えをとりました #: lang/json/martial_art_from_json.py msgid "Baffling Defense" -msgstr "" +msgstr "奇妙な構え" #. ~ Description of buff 'Baffling Defense' for martial art '{'str': 'Setting #. Sun'}' @@ -115566,10 +115623,14 @@ msgid "" "Dodging Skill increased by 20% of Intelligence, enables \"Mighty Throw\" and \"Ballista Throw\" techniques.\n" "Lasts 1 turn." msgstr "" +"わざと不用意に動いて適当に構え、敵を混乱させてその隙に投げ技を仕掛けます。\n" +"\n" +"知性x20%の回避スキル上昇、「全力投げ」「巨砲投げ」有効化\n" +"1ターン継続" #: lang/json/martial_art_from_json.py msgid "Feigned Opening" -msgstr "" +msgstr "偽装降伏" #. ~ Description of buff 'Feigned Opening' for martial art '{'str': 'Setting #. Sun'}' @@ -115580,6 +115641,10 @@ msgid "" "+20 Speed.\n" "Lasts 1 turn." msgstr "" +"意図的に防御態勢を解くことで相手を油断させて、不意打ちを狙います。\n" +"\n" +"速度+20\n" +"1ターン継続" #: lang/json/martial_art_from_json.py msgid "Shii-Cho" @@ -115671,7 +115736,7 @@ msgstr "" #: lang/json/martial_art_from_json.py msgid "Stone Dragon" -msgstr "" +msgstr "岩竜" #. ~ Description for martial art '{'str': 'Stone Dragon'}' #: lang/json/martial_art_from_json.py @@ -115681,21 +115746,22 @@ msgid "" "single, focused blow. Stone Dragon's defensive abilities focus on tapping " "into the enduring power of stone to turn aside attacks." msgstr "" +"強さ、筋力、持久力に重きを置く戦術です。この教えを学べば、武術に長けた者なら一撃で鋼鉄を砕くことすら可能になります。防御面では石がもっている恒久性に着目し、攻撃を逸らすことに焦点を当てています。" #. ~ initiate message for martial art '{'str': 'Stone Dragon'}' #: lang/json/martial_art_from_json.py msgid "You dig your heels into the ground and steady yourself." -msgstr "" +msgstr "踵で地面をしっかりと踏みしめ、体勢を安定させました。" #. ~ initiate message for martial art '{'str': 'Stone Dragon'}' #: lang/json/martial_art_from_json.py #, python-format msgid "%s digs their heels into the ground." -msgstr "" +msgstr "%sは踵で地面をしっかりと踏みしめました。" #: lang/json/martial_art_from_json.py msgid "Stone Bones" -msgstr "" +msgstr "岩の骨" #. ~ Description of buff 'Stone Bones' for martial art '{'str': 'Stone #. Dragon'}' @@ -115706,10 +115772,14 @@ msgid "" "+1 bash, cut, and stab armor.\n" "Lasts 1 turn. Stacks 5 times." msgstr "" +"精神を集中して防御力を高め、武器が敵に当たったときの衝撃を利用してカウンターに備えました。\n" +"\n" +"打撃/斬撃/刺突耐性+1\n" +"1ターン継続、最大蓄積数5" #: lang/json/martial_art_from_json.py msgid "Stonefoot Stance" -msgstr "" +msgstr "岩足の構え" #. ~ Description of buff 'Stonefoot Stance' for martial art '{'str': 'Stone #. Dragon'}' @@ -115720,10 +115790,13 @@ msgid "" "\n" "+10% damage, +2 bash, cut, and stab armor." msgstr "" +"しゃがみこんで地面を足で踏みしめ、力を溜める構えです。ただし、動き過ぎると体勢が崩れてしまいます。\n" +"\n" +"与ダメージ+10%、打撃/斬撃/刺突耐性+2" #: lang/json/martial_art_from_json.py msgid "Cracked Stone" -msgstr "" +msgstr "ヒビ割れた岩" #. ~ Description of buff 'Cracked Stone' for martial art '{'str': 'Stone #. Dragon'}' @@ -115734,10 +115807,14 @@ msgid "" "Enables \"Shattered Stone\" buff.\n" "Lasts 1 turn." msgstr "" +"動き過ぎると岩足の構えの効果が薄れてしまいます。姿勢を崩さないように、じっとしていましょう。\n" +"\n" +"「崩れた岩」有効化\n" +"1ターン継続" #: lang/json/martial_art_from_json.py msgid "Stattered Stone" -msgstr "" +msgstr "崩れた岩" #. ~ Description of buff 'Stattered Stone' for martial art '{'str': 'Stone #. Dragon'}' @@ -115749,10 +115826,14 @@ msgid "" "-10% damage, -2 bash, cut, and stab armor.\n" "Lasts 4 turn." msgstr "" +"適切な体勢を維持できていません。構え直すためにしばらくその場でじっとしている必要があります。\n" +"\n" +"与ダメージ-10%、打撃/斬撃/刺突耐性-2\n" +"4ターン継続" #: lang/json/martial_art_from_json.py msgid "Iron Bones" -msgstr "" +msgstr "鉄の骨" #. ~ Description of buff 'Iron Bones' for martial art '{'str': 'Stone #. Dragon'}' @@ -115763,10 +115844,14 @@ msgid "" "+5 bash, cut, and stab armor.\n" "Lasts 1 turn." msgstr "" +"攻撃が命中すると瞑想状態になり、ダメージをほとんど受けなくなります。\n" +"\n" +"打撃/斬撃/刺突耐性+5\n" +"1ターン継続" #: lang/json/martial_art_from_json.py msgid "Tiger Claw" -msgstr "" +msgstr "虎の爪" #. ~ Description for martial art '{'str': 'Tiger Claw'}' #: lang/json/martial_art_from_json.py @@ -115776,21 +115861,22 @@ msgid "" "with a furry similar to that of a barbarian, and rely on overwhelming, " "vicious assaults to defeat their enemies." msgstr "" +"虎の爪の規律は、その者の心の奥底に潜む猛烈な怒りを受け入れることを目指しています。実戦では虎の爪の戦士は野生動物のように唸り、蛮族のような毛皮をまとって戦い、敵を倒すために圧倒的かつ悪質な攻撃を繰り出します。" #. ~ initiate message for martial art '{'str': 'Tiger Claw'}' #: lang/json/martial_art_from_json.py msgid "You emit a low growl as you prepare for battle." -msgstr "" +msgstr "低い唸り声を発し、戦いに備えました。" #. ~ initiate message for martial art '{'str': 'Tiger Claw'}' #: lang/json/martial_art_from_json.py #, python-format msgid "%s hunkers down like a wild animal." -msgstr "" +msgstr "%sは野生動物のように屈みこみました。" #: lang/json/martial_art_from_json.py msgid "Improved Critical" -msgstr "" +msgstr "強化会心攻撃" #. ~ Description of buff 'Improved Critical' for martial art '{'str': 'Tiger #. Claw'}' @@ -115801,10 +115887,13 @@ msgid "" "\n" "+5% critical hit chance." msgstr "" +"どんな時も全力で攻撃します。死にたくないのなら、この衝動を押し込めてはいけません。\n" +"\n" +"会心発生+5%" #: lang/json/martial_art_from_json.py msgid "Pounching Charge" -msgstr "" +msgstr "先制パンチ" #. ~ Description of buff 'Pounching Charge' for martial art '{'str': 'Tiger #. Claw'}' @@ -115816,10 +115905,14 @@ msgid "" "+2 Accuracy, +10% damage.\n" "Lasts 1 turn." msgstr "" +"野獣のような雄たけびをあげて戦いに身を投じます。鋭い先制攻撃を繰り出します。\n" +"\n" +"命中+2、与ダメージ+10%\n" +"1ターン継続" #: lang/json/martial_art_from_json.py msgid "Cornered Predator" -msgstr "" +msgstr "手負いの獣" #. ~ Description of buff 'Cornered Predator' for martial art '{'str': 'Tiger #. Claw'}' @@ -115831,10 +115924,14 @@ msgid "" "-20% move cost.\n" "Lasts 1 turn." msgstr "" +"追いつめられた動物は恐ろしく危険です。今の状況はまさにそれです。\n" +"\n" +"行動コスト-20%\n" +"1ターン継続" #: lang/json/martial_art_from_json.py msgid "Blood In The Water" -msgstr "" +msgstr "血潮の香り" #. ~ Description of buff 'Blood In The Water' for martial art '{'str': 'Tiger #. Claw'}' @@ -115846,10 +115943,14 @@ msgid "" "+1 Accuracy, +15% damage.\n" "Lasts 1 turn. Stacks 2 times." msgstr "" +"血の香りを嗅いで精神が駆り立てられました。もっと欲しい。今すぐ欲しい!\n" +"\n" +"命中+1、与ダメージ+15%\n" +"1ターン継続、最大蓄積数2" #: lang/json/martial_art_from_json.py msgid "Prey on the Weak" -msgstr "" +msgstr "弱肉強食" #. ~ Description of buff 'Prey on the Weak' for martial art '{'str': 'Tiger #. Claw'}' @@ -115860,6 +115961,10 @@ msgid "" "+30 Speed.\n" "Lasts 2 turns. Stacks 2 times" msgstr "" +"草食動物の群れの中に放たれた猛獣のように、弱い相手を次々に切り裂きます。\n" +"\n" +"速度+30\n" +"2ターン継続、最大蓄積数2" #: lang/json/material_from_json.py src/bionics.cpp msgid "Alcohol" @@ -116345,7 +116450,7 @@ msgstr "乳化ハイドロゲル" #: lang/json/material_from_json.py msgid "pulped" -msgstr "" +msgstr "ドロドロの" #: lang/json/material_from_json.py msgid "Arcane Skin" @@ -118365,7 +118470,7 @@ msgstr "物資を探すのを手伝ってくれると助かるんだけど。" msgid "" "We could use some 3 liter jars to preserve our produce. Can you bring me 10" " large three liter jars? I'll give you some preserves in exchange." -msgstr "" +msgstr "ガラス瓶を使って農作物を保存したいんだ。3Lのガラス瓶を10個持ってきてもらえないか?報酬としてジャムをあげよう。" #: lang/json/mission_def_from_json.py msgid "Thank you. It's important to preserve foods while we can." @@ -120151,14 +120256,14 @@ msgstr "何もないじゃないか。自分で金を探すことにするよ。 #: lang/json/mission_def_from_json.py msgid "Active Noise Control" -msgstr "" +msgstr "ノイズを消し去る" #. ~ Description for mission 'Active Noise Control' #: lang/json/mission_def_from_json.py msgid "" "Investigate Hub 01's radio tower, discover the source of the interference, " "and fix the problem." -msgstr "" +msgstr "ハブ01の電波塔を調査して電波への干渉源を見つけ出し、問題を解決しましょう。" #: lang/json/mission_def_from_json.py msgid "" @@ -120166,69 +120271,70 @@ msgid "" "stopped working recently. If you are willing to be my back up while I check" " it out, I'll owe you a favor." msgstr "" +"先日、近くにあるタワーに無線送信機を設置したんだが、ついさっき機能しなくなってしまったんだ。原因を調査する手伝いをしてくれたら、非常にありがたいな。" #: lang/json/mission_def_from_json.py msgid "Alright, lets be off. You don't mind taking point, right?" -msgstr "" +msgstr "よし、さっそく現場へ向かおう。もちろん、来てくれるよな?" #: lang/json/mission_def_from_json.py msgid "Well thanks for offering, I guess." -msgstr "" +msgstr "そうか、話を聞いてくれただけでも嬉しいよ。" #: lang/json/mission_def_from_json.py msgid "" "I'm sure we'll figure it out once there. In any case, make sure to shoot " "first." -msgstr "" +msgstr "一度現場へ行ってみないと原因は分からないだろうな。いずれにせよ、発砲準備はしておこう。" #: lang/json/mission_def_from_json.py msgid "You think we killed the culprit?" -msgstr "" +msgstr "原因を取り除けたか?" #: lang/json/mission_def_from_json.py msgid "Sure seems like it. Lets go back to the hub." -msgstr "" +msgstr "確かにそうみたいだ。ハブに戻ろう。" #: lang/json/mission_def_from_json.py msgid "Sure, thanks for nothing." -msgstr "" +msgstr "ああ、そりゃどうも。" #: lang/json/mission_def_from_json.py msgid "Return to Hub 01" -msgstr "" +msgstr "ハブ01へ戻る" #. ~ Description for mission 'Return to Hub 01' #: lang/json/mission_def_from_json.py msgid "Return to Hub 01." -msgstr "" +msgstr "ハブ01へ戻りましょう。" #: lang/json/mission_def_from_json.py msgid "Lets go back to the Hub" -msgstr "" +msgstr "ハブへ戻ろう。" #: lang/json/mission_def_from_json.py msgid "Well…" -msgstr "" +msgstr "ええと..." #: lang/json/mission_def_from_json.py msgid "You keep a map around don't you? I do, and you probably should too." -msgstr "" +msgstr "マップがあるだろ?私も持ってるが、自分で見た方が早い。" #: lang/json/mission_def_from_json.py msgid "We there yet?" -msgstr "" +msgstr "まだか?" #: lang/json/mission_def_from_json.py msgid "Thanks for having my back. As I said, I owe you one." -msgstr "" +msgstr "手伝ってくれてありがとう。一つ借りができたな。" #: lang/json/mission_def_from_json.py msgid "Are you lost or something?" -msgstr "" +msgstr "道に迷いでもしたのか?" #: lang/json/mission_def_from_json.py msgid "Can't believe we got lost…" -msgstr "" +msgstr "まさか迷子になるなんて..." #: lang/json/mission_def_from_json.py msgid "Make 2 Stills" @@ -123902,7 +124008,7 @@ msgstr "頭部から発光器官が生えています。意識的にはコント #: lang/json/mutation_from_json.py msgid "Reflex Photophore (on)" -msgstr "" +msgstr "反射性発光器官(オン)" #: lang/json/mutation_from_json.py msgid "Weak Photophore" @@ -123918,12 +124024,12 @@ msgstr "頭部から発光器官が伸び、弱々しい光を放っています #: lang/json/mutation_from_json.py msgid "Weak Photophore (on)" -msgstr "" +msgstr "弱い発光器官(オン)" #. ~ Description for {'str': 'Weak Photophore (on)'} #: lang/json/mutation_from_json.py msgid "Your photophore is glowing softly." -msgstr "" +msgstr "発光器官がほのかに光っています。" #: lang/json/mutation_from_json.py msgid "Photophore" @@ -123932,16 +124038,16 @@ msgstr "発光器官" #. ~ Description for {'str': 'Photophore'} #: lang/json/mutation_from_json.py msgid "You can make your photophore glow brightly." -msgstr "" +msgstr "発光器官が眩い光を放っています。" #: lang/json/mutation_from_json.py msgid "Photophore (on)" -msgstr "" +msgstr "発光器官(オン)" #. ~ Description for {'str': 'Photophore (on)'} #: lang/json/mutation_from_json.py msgid "You photophore is glowing brightly." -msgstr "" +msgstr "発光器官が明るく光っています。" #: lang/json/mutation_from_json.py msgid "Normal Human" @@ -124060,7 +124166,7 @@ msgid "" "Your wounds heal themselves quicker than usual. You heal 50% faster whilst " "asleep and 20% faster whilst awake. Your broken limbs also heal twice as " "fast." -msgstr "" +msgstr "傷が普通より早く治ります。睡眠時は通常より50%、起きている間は通常より20%早くHPが回復します。骨折した手足も2倍の速さで治ります。" #: lang/json/mutation_from_json.py msgid "Light Eater" @@ -124543,7 +124649,7 @@ msgstr "武術鍛錬" msgid "" "You have received some martial arts training at a local dojo. You start " "with your choice of Karate, Judo, Aikido, Tai Chi, Taekwondo, or Pankration." -msgstr "道場で格闘術の教えを受けてきました。空手、柔道、合気道、太極拳、テコンドー、パンクラチオンの内1つを修得した状態で始まります。" +msgstr "道場で格闘術の教えを受けてきました。空手、柔道、合気道、太極拳、テコンドー、パンクラチオンのいずれかを選択して開始します。" #: lang/json/mutation_from_json.py msgid "Self-Defense Classes" @@ -124555,7 +124661,7 @@ msgid "" "You have taken some self-defense classes at a nearby gym. You start with " "your choice of Capoeira, Krav Maga, Muay Thai, Ninjutsu, Wing Chun, or Zui " "Quan." -msgstr "近所のジムで護身術を習っていました。カポエイラ、クラヴマガ、ムエタイ、忍術、詠春拳、酔拳のうち1つを修得しています。" +msgstr "近所のジムで護身術を習っていました。カポエイラ、クラヴマガ、ムエタイ、忍術、詠春拳、酔拳のいずれかを選択して開始します。" #: lang/json/mutation_from_json.py msgid "Shaolin Adept" @@ -124579,7 +124685,7 @@ msgid "" "Eskrima, Fencing, Fior Di Battaglia, Medieval Swordsmanship, Niten Ichi-Ryu," " Pentjak Silat, or Sōjutsu." msgstr "" -"武器を使った戦闘術を鍛錬してきました。開始時からエスクリマ、フェンシング、西洋長柄武器術、中世剣術、二天一流、プンチャック・シラット、槍術のうち1つを習得しています。" +"武器を使った戦闘術を鍛錬してきました。開始時からエスクリマ、フェンシング、西洋長柄武器術、中世剣術、二天一流、プンチャック・シラット、槍術のいずれかを選択して開始します。" #: lang/json/mutation_from_json.py msgid "Competitive Fencer" @@ -124698,7 +124804,7 @@ msgstr "低速治癒" msgid "" "Your wounds heal a little slower than most. Your HP whilst asleep as well " "as your broken limbs heal at 75% the regular rate." -msgstr "" +msgstr "傷の治りが普通より遅くなります。睡眠中のHP回復速度はもちろん、骨折した手足の回復速度も通常の75%です。" #: lang/json/mutation_from_json.py msgid "Poor Healer" @@ -124710,7 +124816,7 @@ msgstr "治癒力不足" msgid "" "Your health recovery is severely impaired. Your HP whilst asleep as well as" " your broken limbs heal at 33% the regular rate." -msgstr "" +msgstr "回復力が著しく損なわれています。睡眠中のHP回復速度はもちろん、骨折した手足の回復速度も通常の33%です。" #: lang/json/mutation_from_json.py msgid "Imperceptive Healer" @@ -124722,7 +124828,7 @@ msgstr "治癒力欠乏" msgid "" "Wounds are incredibly dangerous to you, as they barely heal at all. Your HP" " whilst asleep as well as your broken limbs heal at 10% the regular rate." -msgstr "" +msgstr "回復力は無いに等しく、死の危険性が付きまといます。睡眠中のHP回復速度はもちろん、骨折した手足の回復速度も通常の10%です。" #: lang/json/mutation_from_json.py msgid "Far-Sighted" @@ -125442,6 +125548,7 @@ msgid "" "Your wounds heal very quickly. You heal 50% faster whilst asleep and 66% " "faster whilst awake. Your broken limbs also heal 4 times faster than usual." msgstr "" +"傷が普通より非常に早く治ります。睡眠時は通常より50%、起きている間は通常より60%早くHPが回復します。骨折した手足も4倍の速さで治ります。" #: lang/json/mutation_from_json.py msgid "Regeneration" @@ -125455,6 +125562,7 @@ msgid "" " whilst asleep and 200% faster whilst awake. Your broken limbs also heal 16" " times faster than usual." msgstr "" +"傷が信じられないほど早く治ります。睡眠時は通常より150%、起きている間は通常より200%早くHPが回復します。骨折した手足も16倍の速さで治ります。" #: lang/json/mutation_from_json.py msgid "Reptilian Healing" @@ -125465,7 +125573,7 @@ msgstr "爬虫類の治癒力" msgid "" "Your broken limbs mend themselves without significant difficulty. You do " "not require splints and broken limbs heal 20 times faster than usual." -msgstr "" +msgstr "手足が骨折しても自力で治るため、大した問題になりません。骨折した部位に添え木を着用する必要がなく、通常の20倍の速度で回復します。" #: lang/json/mutation_from_json.py msgid "Very Little Sleep" @@ -129631,7 +129739,7 @@ msgstr "高反射神経" #: lang/json/mutation_from_json.py msgid "" "You have fast reflexes, allowing you to dodge attacks and grabs more easily." -msgstr "" +msgstr "反射神経が高く、攻撃や拘束をより簡単に回避できます。" #: lang/json/mutation_from_json.py msgid "Survivor Story" @@ -130897,7 +131005,7 @@ msgid "" "You are a martial adept and learned one of the martial disciplines of the " "Sublime Way. You start with your choice of Desert Wind, Diamond Mind, Iron " "Heart, Setting Sun, Stone Dragon, or Tiger Claw." -msgstr "" +msgstr "「崇高な道」が使う武術を学んだ武術の達人です。砂漠の旋風、金剛石の心、鋼の心、落陽、岩竜、虎の爪のいずれかを選択して開始します。" #: lang/json/mutation_from_json.py msgid "Magus" @@ -135612,7 +135720,7 @@ msgstr "" #: lang/json/professions_from_json.py msgctxt "profession_male" msgid "Professional Cyclist" -msgstr "プロの自転車乗り" +msgstr "プロのロードレーサー" #. ~ Profession (male Professional Cyclist) description #: lang/json/professions_from_json.py @@ -135628,7 +135736,7 @@ msgstr "" #: lang/json/professions_from_json.py msgctxt "profession_female" msgid "Professional Cyclist" -msgstr "プロの自転車乗り" +msgstr "プロのロードレーサー" #. ~ Profession (female Professional Cyclist) description #: lang/json/professions_from_json.py @@ -135986,7 +136094,7 @@ msgstr "" #: lang/json/professions_from_json.py msgctxt "profession_male" msgid "Chain Smoker" -msgstr "ヘビースモーカー" +msgstr "チェーンスモーカー" #. ~ Profession (male Chain Smoker) description #: lang/json/professions_from_json.py @@ -136001,7 +136109,7 @@ msgstr "" #: lang/json/professions_from_json.py msgctxt "profession_female" msgid "Chain Smoker" -msgstr "ヘビースモーカー" +msgstr "チェーンスモーカー" #. ~ Profession (female Chain Smoker) description #: lang/json/professions_from_json.py @@ -136016,7 +136124,7 @@ msgstr "" #: lang/json/professions_from_json.py msgctxt "profession_male" msgid "Crackhead" -msgstr "高純度コカイン常用者" +msgstr "高純度コカイン乱用者" #. ~ Profession (male Crackhead) description #: lang/json/professions_from_json.py @@ -136031,7 +136139,7 @@ msgstr "" #: lang/json/professions_from_json.py msgctxt "profession_female" msgid "Crackhead" -msgstr "高純度コカイン常用者" +msgstr "高純度コカイン乱用者" #. ~ Profession (female Crackhead) description #: lang/json/professions_from_json.py @@ -136078,7 +136186,7 @@ msgstr "" #: lang/json/professions_from_json.py msgctxt "profession_male" msgid "Tweaker" -msgstr "薬物中毒者" +msgstr "アンフェタミン乱用者" #. ~ Profession (male Tweaker) description #: lang/json/professions_from_json.py @@ -136093,7 +136201,7 @@ msgstr "" #: lang/json/professions_from_json.py msgctxt "profession_female" msgid "Tweaker" -msgstr "薬物中毒者" +msgstr "アンフェタミン乱用者" #. ~ Profession (female Tweaker) description #: lang/json/professions_from_json.py @@ -136108,7 +136216,7 @@ msgstr "" #: lang/json/professions_from_json.py msgctxt "profession_male" msgid "Pillhead" -msgstr "アヘン中毒者" +msgstr "アヘン乱用者" #. ~ Profession (male Pillhead) description #: lang/json/professions_from_json.py @@ -136123,7 +136231,7 @@ msgstr "" #: lang/json/professions_from_json.py msgctxt "profession_female" msgid "Pillhead" -msgstr "アヘン中毒者" +msgstr "アヘン乱用者" #. ~ Profession (female Pillhead) description #: lang/json/professions_from_json.py @@ -137698,7 +137806,7 @@ msgstr "大切な日に大変動が起き、結婚式の服装のまま一目散 #: lang/json/professions_from_json.py msgctxt "profession_male" msgid "Punk Rock Dude" -msgstr "パンクロックの伊達男" +msgstr "パンクロッカー" #. ~ Profession (Punk Rock Dude) description #: lang/json/professions_from_json.py @@ -137711,7 +137819,7 @@ msgstr "黙示録の世界を歌う邪悪な歌に命が吹き込まれました #: lang/json/professions_from_json.py msgctxt "profession_female" msgid "Punk Rock Girl" -msgstr "パンクロックの少女" +msgstr "パンクロッカー" #. ~ Profession (Punk Rock Girl) description #: lang/json/professions_from_json.py @@ -139802,7 +139910,7 @@ msgstr "" #: lang/json/professions_from_json.py msgctxt "profession_male" msgid "EMT" -msgstr "" +msgstr "救急隊員" #. ~ Profession (male EMT) description #: lang/json/professions_from_json.py @@ -139812,11 +139920,12 @@ msgid "" "Now all you have is your trusty ambulance, ready to transport patients " "through the apocalypse." msgstr "" +"同僚からの応援要請に対応しようとしていましたが、結局合流はできませんでした。信頼できる救急車に乗っており、世界が崩壊する状況でも患者を輸送する準備はできています。" #: lang/json/professions_from_json.py msgctxt "profession_female" msgid "EMT" -msgstr "" +msgstr "救急隊員" #. ~ Profession (female EMT) description #: lang/json/professions_from_json.py @@ -139826,11 +139935,12 @@ msgid "" "Now all you have is your trusty ambulance, ready to transport patients " "through the apocalypse." msgstr "" +"同僚からの応援要請に対応しようとしていましたが、結局合流はできませんでした。信頼できる救急車に乗っており、世界が崩壊する状況でも患者を輸送する準備はできています。" #: lang/json/professions_from_json.py msgctxt "profession_male" msgid "Paramedic" -msgstr "" +msgstr "救急救命士" #. ~ Profession (male Paramedic) description #: lang/json/professions_from_json.py @@ -139839,12 +139949,12 @@ msgid "" "You were separated from your partner while out on a call. You managed to " "hang onto some medical supplies, but it's looking like the only life that " "needs saving now is yours." -msgstr "" +msgstr "同僚に応援を要請しましたが、結局合流はできませんでした。医薬品はなんとか持ち出せましたが、今はまず自分の命を救う必要がありそうです。" #: lang/json/professions_from_json.py msgctxt "profession_female" msgid "Paramedic" -msgstr "" +msgstr "救急救命士" #. ~ Profession (female Paramedic) description #: lang/json/professions_from_json.py @@ -139853,12 +139963,12 @@ msgid "" "You were separated from your partner while out on a call. You managed to " "hang onto some medical supplies, but it's looking like the only life that " "needs saving now is yours." -msgstr "" +msgstr "同僚に応援を要請しましたが、結局合流はできませんでした。医薬品はなんとか持ち出せましたが、今はまず自分の命を救う必要がありそうです。" #: lang/json/professions_from_json.py msgctxt "profession_male" msgid "Combat Medic" -msgstr "" +msgstr "衛生兵" #. ~ Profession (male Combat Medic) description #: lang/json/professions_from_json.py @@ -139867,12 +139977,12 @@ msgid "" "You were on the front-lines when everything happened, patching up the " "wounded and providing support. But they wouldn't stop coming. Now you're " "on your own." -msgstr "" +msgstr "負傷者に包帯を巻き、添え木を当てていたまさにその時、大変動が訪れました。負傷者は増え続け、やがて誰もいなくなりました。" #: lang/json/professions_from_json.py msgctxt "profession_female" msgid "Combat Medic" -msgstr "" +msgstr "衛生兵" #. ~ Profession (female Combat Medic) description #: lang/json/professions_from_json.py @@ -139881,12 +139991,12 @@ msgid "" "You were on the front-lines when everything happened, patching up the " "wounded and providing support. But they wouldn't stop coming. Now you're " "on your own." -msgstr "" +msgstr "負傷者に包帯を巻き、添え木を当てていたまさにその時、大変動が訪れました。負傷者は増え続け、やがて誰もいなくなりました。" #: lang/json/professions_from_json.py msgctxt "profession_male" msgid "Heroin Addict" -msgstr "" +msgstr "ヘロイン乱用者" #. ~ Profession (male Heroin Addict) description #: lang/json/professions_from_json.py @@ -139895,12 +140005,12 @@ msgid "" "The last thing you remember was meeting God behind the local Foodplace. " "Then people started eating each other. This doesn't feel like a fever " "dream." -msgstr "" +msgstr "地元のフードプレイスの裏手で神に出会ったのが、最後の記憶です。そして人々は互いを喰い始めました。これは熱狂がもたらす幻覚なのでしょうか。" #: lang/json/professions_from_json.py msgctxt "profession_female" msgid "Heroin Addict" -msgstr "" +msgstr "ヘロイン乱用者" #. ~ Profession (female Heroin Addict) description #: lang/json/professions_from_json.py @@ -139909,7 +140019,7 @@ msgid "" "The last thing you remember was meeting God behind the local Foodplace. " "Then people started eating each other. This doesn't feel like a fever " "dream." -msgstr "" +msgstr "地元のフードプレイスの裏手で神に出会ったのが、最後の記憶です。そして人々は互いを喰い始めました。これは熱狂がもたらす幻覚なのでしょうか。" #: lang/json/professions_from_json.py msgctxt "profession_male" @@ -140093,6 +140203,7 @@ msgid "" "you in the middle of your 16 hour extended shift, and you barely managed to " "escape the building, tools in hand." msgstr "" +"CBMの製造に関係する警備の厳重な施設で、面白味のない技師の仕事をしていました。残業含め16時間の業務をこなしている途中で最後の避難命令が発出され、工具を掴んでなんとか屋外へ脱出しました。" #: lang/json/professions_from_json.py msgctxt "profession_female" @@ -140108,6 +140219,7 @@ msgid "" "you in the middle of your 16 hour extended shift, and you barely managed to " "escape the building, tools in hand." msgstr "" +"CBMの製造に関係する警備の厳重な施設で、面白味のない技師の仕事をしていました。残業含め16時間の業務をこなしている途中で最後の避難命令が発出され、工具を掴んでなんとか屋外へ脱出しました。" #: lang/json/professions_from_json.py msgctxt "profession_male" @@ -141412,7 +141524,7 @@ msgstr "テストでズルをするために使っていた魔法は、大変動 #: lang/json/professions_from_json.py msgctxt "profession_male" msgid "Introspectionist" -msgstr "" +msgstr "瞑想者" #. ~ Profession (male Introspectionist) description #: lang/json/professions_from_json.py @@ -141422,11 +141534,12 @@ msgid "" "improving yourself. It was you and your best friend, but now the apocalypse" " won't leave you alone. " msgstr "" +"自分を磨くことに集中したかったため、社会からはなれたところにいました。孤独こそが唯一の友人だったのに、この状況では嫌でも天変地異に好かれてしまいます。" #: lang/json/professions_from_json.py msgctxt "profession_female" msgid "Introspectionist" -msgstr "" +msgstr "瞑想者" #. ~ Profession (female Introspectionist) description #: lang/json/professions_from_json.py @@ -141436,11 +141549,12 @@ msgid "" "improving yourself. It was you and your best friend, but now the apocalypse" " won't leave you alone. " msgstr "" +"自分を磨くことに集中したかったため、社会からはなれたところにいました。孤独こそが唯一の友人だったのに、この状況では嫌でも天変地異に好かれてしまいます。" #: lang/json/professions_from_json.py msgctxt "profession_male" msgid "Vengeful Preacher" -msgstr "" +msgstr "不良牧師" #. ~ Profession (male Vengeful Preacher) description #: lang/json/professions_from_json.py @@ -141450,11 +141564,12 @@ msgid "" "yourself to death ever since. God is punishing everyone with this " "apocalypse, you are going to show them your brand of mercy." msgstr "" +"配偶者が病気で死んだとき、信仰を失いました。それ以来、いつも飲んだくれています。神はこの黙示録の日にあらゆる人を罰しています。わずかに残った慈悲を人々に見せるべきでしょうか。" #: lang/json/professions_from_json.py msgctxt "profession_female" msgid "Vengeful Preacher" -msgstr "" +msgstr "不良牧師" #. ~ Profession (female Vengeful Preacher) description #: lang/json/professions_from_json.py @@ -141464,11 +141579,12 @@ msgid "" "yourself to death ever since. God is punishing everyone with this " "apocalypse, you are going to show them your brand of mercy." msgstr "" +"配偶者が病気で死んだとき、信仰を失いました。それ以来、いつも飲んだくれています。神はこの黙示録の日にあらゆる人を罰しています。わずかに残った慈悲を人々に見せるべきでしょうか。" #: lang/json/professions_from_json.py msgctxt "profession_male" msgid "Techno-Prepper" -msgstr "" +msgstr "テクノプレッパー" #. ~ Profession (male Techno-Prepper) description #: lang/json/professions_from_json.py @@ -141476,12 +141592,12 @@ msgctxt "prof_desc_male" msgid "" "You've long suspected the world might suddenly turn over. With your " "training, spells, and revolver, your chances are far better than most." -msgstr "" +msgstr "世界がいつか突然崩壊するだろうと信じていました。訓練と呪文とリボルバーがあれば、生き延びるチャンスは大いに高まります。" #: lang/json/professions_from_json.py msgctxt "profession_female" msgid "Techno-Prepper" -msgstr "" +msgstr "テクノプレッパー" #. ~ Profession (female Techno-Prepper) description #: lang/json/professions_from_json.py @@ -141489,12 +141605,12 @@ msgctxt "prof_desc_female" msgid "" "You've long suspected the world might suddenly turn over. With your " "training, spells, and revolver, your chances are far better than most." -msgstr "" +msgstr "世界がいつか突然崩壊するだろうと信じていました。訓練と呪文とリボルバーがあれば、生き延びるチャンスは大いに高まります。" #: lang/json/professions_from_json.py msgctxt "profession_male" msgid "Bionic Pseudovamp" -msgstr "" +msgstr "疑似バンパイア" #. ~ Profession (male Bionic Pseudovamp) description #: lang/json/professions_from_json.py @@ -141504,11 +141620,12 @@ msgid "" "augment yourself with spells and bionics into a denizen of the night. Your " "neglect to your health in your pursuit has left you pale and unlively." msgstr "" +"小さい頃からホラーノベルを溺愛しており、資産を使って呪文や生体部品で夜の住人を増やしていました。趣味に没頭するあまり健康意識が低くなり、青白く不健康な身体になってしまいました。" #: lang/json/professions_from_json.py msgctxt "profession_female" msgid "Bionic Pseudovamp" -msgstr "" +msgstr "疑似バンパイア" #. ~ Profession (female Bionic Pseudovamp) description #: lang/json/professions_from_json.py @@ -141518,11 +141635,12 @@ msgid "" "augment yourself with spells and bionics into a denizen of the night. Your " "neglect to your health in your pursuit has left you pale and unlively." msgstr "" +"小さい頃からホラーノベルを溺愛しており、資産を使って呪文や生体部品で夜の住人を増やしていました。趣味に没頭するあまり健康意識が低くなり、青白く不健康な身体になってしまいました。" #: lang/json/professions_from_json.py msgctxt "profession_male" msgid "Academy Wizard" -msgstr "" +msgstr "魔法学校の生徒" #. ~ Profession (male Academy Wizard) description #: lang/json/professions_from_json.py @@ -141532,11 +141650,12 @@ msgid "" "and a handful of useful spells. With the teachers converted into the undead " "and classes cancelled, the final lesson has begun." msgstr "" +"魔法学校に入学して1年が経ち、忍耐力、知恵、そしていくつかの便利な魔法を学びました。教師がゾンビになったので、もう授業はありません。最後のレッスンの始まりです。" #: lang/json/professions_from_json.py msgctxt "profession_female" msgid "Academy Wizard" -msgstr "" +msgstr "魔法学校の生徒" #. ~ Profession (female Academy Wizard) description #: lang/json/professions_from_json.py @@ -141546,11 +141665,12 @@ msgid "" "and a handful of useful spells. With the teachers converted into the undead " "and classes cancelled, the final lesson has begun." msgstr "" +"魔法学校に入学して1年が経ち、忍耐力、知恵、そしていくつかの便利な魔法を学びました。教師がゾンビになったので、もう授業はありません。最後のレッスンの始まりです。" #: lang/json/professions_from_json.py msgctxt "profession_male" msgid "Corrosive Rocker" -msgstr "" +msgstr "過激なロッカー" #. ~ Profession (male Corrosive Rocker) description #: lang/json/professions_from_json.py @@ -141559,11 +141679,12 @@ msgid "" "Your metal career soared to new heights when you swapped special effects for" " acrid gore and spiked whips. It seems the Cataclysm is now your final tour." msgstr "" +"特殊効果を活用して強烈で刺激的なステージを作りあげ、所属していたメタルバンドは大躍進を遂げました。大変動の世界を舞台にしたファイナルツアーの幕開けです。" #: lang/json/professions_from_json.py msgctxt "profession_female" msgid "Corrosive Rocker" -msgstr "" +msgstr "過激なロッカー" #. ~ Profession (female Corrosive Rocker) description #: lang/json/professions_from_json.py @@ -141572,11 +141693,12 @@ msgid "" "Your metal career soared to new heights when you swapped special effects for" " acrid gore and spiked whips. It seems the Cataclysm is now your final tour." msgstr "" +"特殊効果を活用して強烈で刺激的なステージを作りあげ、所属していたメタルバンドは大躍進を遂げました。大変動の世界を舞台にしたファイナルツアーの幕開けです。" #: lang/json/professions_from_json.py msgctxt "profession_male" msgid "Shock Officer" -msgstr "" +msgstr "魔法警官" #. ~ Profession (male Shock Officer) description #: lang/json/professions_from_json.py @@ -141586,11 +141708,12 @@ msgid "" "decrease suspect casualties and equipment costs by substituting tasers and " "bullets for less-lethal Stormshaping." msgstr "" +"実験的な法執行プログラムに登録されました。容疑者の死傷を減らしコストも削減できることから、テーザー銃と銃弾の代わりにストームシェイプの魔法を使っています。" #: lang/json/professions_from_json.py msgctxt "profession_female" msgid "Shock Officer" -msgstr "" +msgstr "魔法警官" #. ~ Profession (female Shock Officer) description #: lang/json/professions_from_json.py @@ -141600,11 +141723,12 @@ msgid "" "decrease suspect casualties and equipment costs by substituting tasers and " "bullets for less-lethal Stormshaping." msgstr "" +"実験的な法執行プログラムに登録されました。容疑者の死傷を減らしコストも削減できることから、テーザー銃と銃弾の代わりにストームシェイプの魔法を使っています。" #: lang/json/professions_from_json.py msgctxt "profession_male" msgid "Earthquake Brawler" -msgstr "" +msgstr "魔法格闘家" #. ~ Profession (male Earthquake Brawler) description #: lang/json/professions_from_json.py @@ -141614,11 +141738,12 @@ msgid "" "unstoppable punching machine. Now that all your opponents are undead, " "there's no need to hold back." msgstr "" +"地下で開かれているマジックボクシング大会で、無敵の削岩機として名を馳せていました。対戦相手は皆ゾンビになったので、もう手加減する必要はありません。" #: lang/json/professions_from_json.py msgctxt "profession_female" msgid "Earthquake Brawler" -msgstr "" +msgstr "魔法格闘家" #. ~ Profession (female Earthquake Brawler) description #: lang/json/professions_from_json.py @@ -141628,6 +141753,7 @@ msgid "" "unstoppable punching machine. Now that all your opponents are undead, " "there's no need to hold back." msgstr "" +"地下で開かれているマジックボクシング大会で、無敵の削岩機として名を馳せていました。対戦相手は皆ゾンビになったので、もう手加減する必要はありません。" #: lang/json/professions_from_json.py msgctxt "profession_male" @@ -143303,7 +143429,7 @@ msgstr "金属加工炉" #: lang/json/recipe_from_json.py msgid "Let's build an anvil and crucible to increase our crafting options." -msgstr "" +msgstr "金床とるつぼを置いて、作れるものを増やしましょう。" #: lang/json/recipe_from_json.py msgid "add an anvil and crucible" @@ -163182,7 +163308,7 @@ msgstr "沼地にもゾンビがいるらしいな。ゾンビが恐竜を噛ん msgid "" "I know there aren't alligators in the sewer, but I heard there was some kind" " of big lizard down there. Probably not a good idea to check." -msgstr "" +msgstr "下水道にワニはいないだろうが、大きなトカゲはいるそうだ。覗いてみようなんて思わない方がいい。" #: lang/json/snippet_from_json.py msgid "" @@ -163190,12 +163316,13 @@ msgid "" "something nice and gave them a pet you could ride them like a pony. Or " "maybe they'd eat you instead." msgstr "" +"大型恐竜の中には、結構聞き分けの良さそうな奴もいる。何か餌を食べさせてペットにすれば、ポニーみたいに騎乗できるんじゃないか?失敗すればこっちが食べられるだろうけど。" #: lang/json/snippet_from_json.py msgid "" "One time I found a strange egg out in the woods. It was probably a " "dinosaur, but I cooked it and it was pretty good!" -msgstr "" +msgstr "森の中で、奇妙な卵を見つけたことがある。恐竜の卵だったのかもしれないけど、調理したら結構おいしかったよ。" #: lang/json/snippet_from_json.py msgid "" @@ -163212,6 +163339,7 @@ msgid "" "were already prepared and are proceeding ahead of schedule. The hosts are " "most receptive to improvement." msgstr "" +"訪問者たちの研究は着実に進んでいる。大型レーザー作戦は十分な資金を得られなかったが、次善策の改造兵士については既に準備が整っており、予定よりも前倒しで進んでいる。改造希望者は多数用意できる。" #: lang/json/snippet_from_json.py msgid "" @@ -166267,7 +166395,7 @@ msgstr "郊外" #: lang/json/start_location_from_json.py msgid "Desert Island" -msgstr "" +msgstr "無人島" #: lang/json/start_location_from_json.py msgid "Experiment Cell" @@ -179251,11 +179379,11 @@ msgstr "分かった。" #: lang/json/talk_topic_from_json.py msgid "Better keep our eyes on the road." -msgstr "" +msgstr "道中は注意しておいた方がいい。" #: lang/json/talk_topic_from_json.py msgid "Better be careful around here." -msgstr "" +msgstr "この辺は気を付けて行動した方がいい。" #: lang/json/talk_topic_from_json.py msgid "Yes?" @@ -179279,7 +179407,7 @@ msgstr "会えて嬉しいよ。" #: lang/json/talk_topic_from_json.py msgid "About those jobs…" -msgstr "" +msgstr "それらの仕事について..." #: lang/json/talk_topic_from_json.py msgid "Good to see you around." @@ -179291,7 +179419,7 @@ msgstr "何か手伝ってほしいことはある?" #: lang/json/talk_topic_from_json.py msgid "Lets set a combat strategy" -msgstr "" +msgstr "戦闘時の約束事を決めよう。" #: lang/json/talk_topic_from_json.py msgid "" @@ -179346,7 +179474,7 @@ msgstr "どうかした?" #: lang/json/talk_topic_from_json.py msgid "Want help with something?" -msgstr "" +msgstr "何か手伝おうか?" #: lang/json/talk_topic_from_json.py msgid "What do you know about our employers?" @@ -182338,7 +182466,7 @@ msgstr "は%sに勢いよく切り込みました。" #: lang/json/technique_from_json.py msgid "Mordhau" -msgstr "" +msgstr "モルダウ" #: lang/json/technique_from_json.py #, python-format @@ -183590,17 +183718,17 @@ msgstr "大いなる天眼の一撃" #: lang/json/technique_from_json.py msgid "Spin Attack" -msgstr "スピンアタック" +msgstr "回転斬り" #: lang/json/technique_from_json.py #, python-format msgid "You unleash a spin attack against %s and those nearby" -msgstr "スピンアタックを放ち、周囲を巻き込みながら%sを攻撃しました。" +msgstr "回転斬りを放ち、周囲を巻き込みながら%sを攻撃しました。" #: lang/json/technique_from_json.py #, python-format msgid " unleashes a spin attack against %s and those nearby" -msgstr "はスピンアタックを放ち、周囲を巻き込みながら%sを攻撃しました。" +msgstr "は回転斬りを放ち、周囲を巻き込みながら%sを攻撃しました。" #: lang/json/technique_from_json.py msgid "Disarming Strike" @@ -183898,59 +184026,59 @@ msgstr "は周囲を巻き込みながら素早く%sを薙ぎ払いま #: lang/json/technique_from_json.py msgid "Mountain Hammer" -msgstr "" +msgstr "巨大鎚" #: lang/json/technique_from_json.py #, python-format msgid "You crush %s with the weight of your Mountain Hammer" -msgstr "" +msgstr "%sを押し潰しました。" #: lang/json/technique_from_json.py #, python-format msgid " crushes %s with the weight of their Mountain Hammer" -msgstr "" +msgstr "は%sを押し潰しました。" #: lang/json/technique_from_json.py msgid "Irrestistible Mountain Strike" -msgstr "" +msgstr "大嶽殺" #: lang/json/technique_from_json.py #, python-format msgid "You smash down on %s with a Mountain Strike" -msgstr "" +msgstr "%sを叩きのめし、押し潰しました。" #: lang/json/technique_from_json.py #, python-format msgid " smashes down on %s with a Mountain Strike" -msgstr "" +msgstr "は%sを叩きのめし、押し潰しました。" #: lang/json/technique_from_json.py msgid "Colossus Strike" -msgstr "" +msgstr "巨大な一撃" #: lang/json/technique_from_json.py #, python-format msgid "You completely shatter %s with a Colossus Strike" -msgstr "" +msgstr "%sを完璧に轢き潰す攻撃を繰り出しました。" #: lang/json/technique_from_json.py #, python-format msgid " completely shatters %s with a Colossus Strike" -msgstr "" +msgstr "は%sを完璧に轢き潰す攻撃を繰り出しました。" #: lang/json/technique_from_json.py msgid "Wolverine Stance" -msgstr "" +msgstr "イタチの構え" #: lang/json/technique_from_json.py #, python-format msgid "The %s tries to grab you, but you thrash your way to freedom!" -msgstr "" +msgstr "%sが掴みかかろうとしましたが、転げ回って自由になりました!" #: lang/json/technique_from_json.py #, python-format msgid "The %s tries to grab , but they thrash their way to freedom!" -msgstr "" +msgstr "は%sに掴まれそうになりましたが、転げ回って自由になりました!" #: lang/json/ter_furn_transform_messages_from_json.py msgid "The earth here does not listen to your command to move." @@ -186724,12 +186852,12 @@ msgstr "危ない!給油ポンプは壊れており、価値ある液体を汲 #. ~ Description for fuel tank #: lang/json/terrain_from_json.py msgid "A tank filled with diesel." -msgstr "" +msgstr "灯油を満載したタンクです。" #. ~ Description for broken fuel tank #: lang/json/terrain_from_json.py msgid "A broken tank which was filled with diesel." -msgstr "" +msgstr "灯油を満載した壊れたタンクです。" #: lang/json/terrain_from_json.py msgid "diesel pump" @@ -189673,7 +189801,7 @@ msgstr "持ち上げ(セルフ)" #: lang/json/tool_quality_from_json.py msgid "siphoning" -msgstr "" +msgstr "抜取" #: lang/json/tool_quality_from_json.py msgid "chiseling" @@ -189882,7 +190010,7 @@ msgstr "縦樋漏斗" #: lang/json/trap_from_json.py msgid "firewood source" -msgstr "焚木置き場" +msgstr "焚物置き場" #: lang/json/trap_from_json.py msgid "practice target" @@ -191237,7 +191365,7 @@ msgstr "車載/M249" #: lang/json/vehicle_part_from_json.py msgid "mounted M249S" -msgstr "" +msgstr "車載/M249S" #: lang/json/vehicle_part_from_json.py msgid "mounted gatling shotgun" @@ -191265,7 +191393,7 @@ msgstr "車載/M60" #: lang/json/vehicle_part_from_json.py msgid "mounted M60 Semi Auto" -msgstr "" +msgstr "車載/M60セミオート" #: lang/json/vehicle_part_from_json.py msgid "mounted Mark 19 grenade launcher" @@ -191642,7 +191770,7 @@ msgstr "ボートが浸水するのを防ぐ木製の板です。" #. ~ Description for {'str': 'raft boat hull'} #: lang/json/vehicle_part_from_json.py msgid "Logs tied together that will keep your boat out of the water." -msgstr "" +msgstr "丸太同士が水に浮くように繋がっています。" #. ~ Description for {'str': 'plastic boat hull'} #: lang/json/vehicle_part_from_json.py @@ -192087,7 +192215,7 @@ msgstr "非常に重い牽引ケーブルです。ケーブルのもう一方端 #: lang/json/vehicle_part_from_json.py msgid "flimsy wooden seat" -msgstr "" +msgstr "簡易座席(木)" #. ~ Description for {'str': 'flimsy wooden seat'} #. ~ Description for {'str': 'wooden seat'} @@ -192307,6 +192435,15 @@ msgid "" "extending the time until the food spoils." msgstr "容量60Lの冷蔵タンクです。電源を入れると中に入れた液体が冷え、日持ちするようになります。" +#. ~ Description for {'str': 'wooden wheel mount'} +#: lang/json/vehicle_part_from_json.py +msgid "A piece of wood with holes suitable for a bike or motorbike wheel." +msgstr "自転車やバイクのホイールを取り付ける穴が付いた木製部品です。" + +#: lang/json/vehicle_part_from_json.py +msgid "wooden wheel mount (steerable)" +msgstr "木製ホイールハブ (転舵輪)" + #: lang/json/vehicle_part_from_json.py msgid "light wheel mount (steerable)" msgstr "小型ホイールハブ (転舵輪)" @@ -192956,7 +193093,7 @@ msgstr "%sに達成" #: src/achievement.cpp #, c-format msgid "Failed %s" -msgstr "" +msgstr "%sに失敗しました" #: src/achievement.cpp msgid "" @@ -193183,7 +193320,7 @@ msgstr "それはピッキングできません。" #: src/activity_actor.cpp msgid "" "You feel you should've fallen asleep by now, but somehow you're still awake." -msgstr "" +msgstr "さっき眠りに落ちたはずなのに、何故かまだ起きています。" #: src/activity_actor.cpp msgid "You toss and turn…" @@ -193191,7 +193328,7 @@ msgstr "寝返りを打ちました..." #: src/activity_actor.cpp msgid "You try to sleep, but can't." -msgstr "" +msgstr "眠ろうとしましたが眠れません。" #: src/activity_actor.cpp msgid "You have trouble sleeping, keep trying?" @@ -198007,7 +198144,7 @@ msgstr "命中" #: src/bonuses.cpp msgid "Critical Hit Chance" -msgstr "" +msgstr "会心発生率" #: src/bonuses.cpp src/martialarts.cpp msgid "Dodge" @@ -198902,7 +199039,7 @@ msgstr "潤喉 *" #: src/character.cpp msgid "Satisfied" -msgstr "" +msgstr "満足" #: src/character.cpp msgid "Hungry" @@ -198926,7 +199063,7 @@ msgstr "空腹 ***" #: src/character.cpp msgid "ERROR!" -msgstr "" +msgstr "エラー!" #: src/character.cpp src/npctalk.cpp msgid "Exhausted" @@ -199865,7 +200002,7 @@ msgstr "友好NPCはこの区域内から発生する正体不明の音のみを #: src/clzones.cpp msgid "Source: Firewood" -msgstr "供給: 焚き木" +msgstr "供給: 焚物" #: src/clzones.cpp msgid "" @@ -199873,7 +200010,7 @@ msgid "" " automatically refuel fires. This will be done to maintain light during " "long-running tasks such as crafting, reading or waiting." msgstr "" -"印を付けたタイルに置かれている焚き木など可燃物は、火の中へ自動的に補給されます。製作や読書などの長時間の作業時には、火を絶やさないよう自動補給機能が働きますが、火の傍でただ立っているだけの時などは、自動補給は行われません。" +"印を付けたタイルに置かれている木材などの可燃物は、火の中へ自動的に補給されます。製作や読書などの長時間の作業時には、火を絶やさないよう自動補給機能が働きますが、火の傍でただ立っているだけの時などは、自動補給は行われません。" #: src/clzones.cpp msgid "Construction: Blueprint" @@ -204346,12 +204483,12 @@ msgstr "伝説的" #: src/faction.cpp msgctxt "Faction respect" msgid "Unchallenged" -msgstr "無名" +msgstr "傑出" #: src/faction.cpp msgctxt "Faction respect" msgid "Mighty" -msgstr "強大" +msgstr "高名" #: src/faction.cpp msgctxt "Faction respect" @@ -204361,12 +204498,12 @@ msgstr "有名" #: src/faction.cpp msgctxt "Faction respect" msgid "Well-Known" -msgstr "有名" +msgstr "名うて" #: src/faction.cpp msgctxt "Faction respect" msgid "Spoken Of" -msgstr "語り草" +msgstr "風の噂" #: src/faction.cpp msgctxt "Faction respect" @@ -204381,22 +204518,22 @@ msgstr "虫けら" #: src/faction.cpp msgctxt "Faction respect" msgid "Despicable" -msgstr "卑賤" +msgstr "軽蔑" #: src/faction.cpp msgctxt "Faction respect" msgid "Parasite" -msgstr "寄生虫" +msgstr "厄介者" #: src/faction.cpp msgctxt "Faction respect" msgid "Leech" -msgstr "腰巾着" +msgstr "取り巻き" #: src/faction.cpp msgctxt "Faction respect" msgid "Laughingstock" -msgstr "笑い者" +msgstr "軽視" #: src/faction.cpp msgctxt "Faction respect" @@ -204406,7 +204543,7 @@ msgstr "中立的" #: src/faction.cpp msgctxt "Faction wealth" msgid "Filthy rich" -msgstr "守銭奴" +msgstr "大富豪" #: src/faction.cpp msgctxt "Faction wealth" @@ -204421,7 +204558,7 @@ msgstr "潤沢" #: src/faction.cpp msgctxt "Faction wealth" msgid "Well-Off" -msgstr "余裕がある" +msgstr "余裕" #: src/faction.cpp msgctxt "Faction wealth" @@ -204431,32 +204568,32 @@ msgstr "快適" #: src/faction.cpp msgctxt "Faction wealth" msgid "Wanting" -msgstr "物足りない" +msgstr "不足ぎみ" #: src/faction.cpp msgctxt "Faction wealth" msgid "Failing" -msgstr "破綻傾向" +msgstr "貧乏" #: src/faction.cpp msgctxt "Faction wealth" msgid "Impoverished" -msgstr "不毛" +msgstr "困窮" #: src/faction.cpp msgctxt "Faction wealth" msgid "Destitute" -msgstr "貧困" +msgstr "絶望的" #: src/faction.cpp msgctxt "Faction food" msgid "Overflowing" -msgstr "有り余る" +msgstr "豊穣" #: src/faction.cpp msgctxt "Faction food" msgid "Well-Stocked" -msgstr "余裕がある" +msgstr "万全" #: src/faction.cpp msgctxt "Faction food" @@ -204506,17 +204643,17 @@ msgstr "訓練不足" #: src/faction.cpp msgctxt "Faction combat lvl" msgid "Crippled" -msgstr "烏合の衆" +msgstr "未熟" #: src/faction.cpp msgctxt "Faction combat lvl" msgid "Feeble" -msgstr "脆弱" +msgstr "ド素人" #: src/faction.cpp msgctxt "Faction combat lvl" msgid "Worthless" -msgstr "一銭の価値もない" +msgstr "無価値" #: src/faction.cpp msgid "Press enter to rename this camp" @@ -206548,7 +206685,7 @@ msgstr "実績「%s」を取得しました。" #: src/game.cpp #, c-format msgid "You lost the conduct \"%s\"." -msgstr "" +msgstr "「%s」の制約を破りました。" #: src/game.cpp src/options.cpp #, c-format @@ -206741,7 +206878,7 @@ msgstr "開ける" #: src/game.cpp msgctxt "action" msgid "pocket autopickup settings" -msgstr "" +msgstr "ポケット自動拾得設定" #: src/game.cpp msgctxt "action" @@ -207220,46 +207357,46 @@ msgstr "火力が強すぎて、どれほど長く燃え続けるか予測でき #: src/game.cpp msgid "It's going to go out soon without extra fuel." -msgstr "焚き木を追加しなければすぐに消えてしまいそうです。" +msgstr "焚物を追加しなければすぐに消えてしまいそうです。" #: src/game.cpp #, c-format msgid "" "Without extra fuel it might burn yet for maybe %s, but might also go out " "sooner." -msgstr "%sは燃え続けそうですが、焚き木を追加しなければすぐに消えてしまいます。" +msgstr "%sは燃え続けそうですが、焚物を追加しなければすぐに消えてしまいます。" #: src/game.cpp #, c-format msgid "" "Without extra fuel it might burn yet for between %s to %s, but might also go" " out sooner." -msgstr "%sから%sは燃え続けそうですが、焚き木を追加しなければすぐに消えてしまいます。" +msgstr "%sから%sは燃え続けそうですが、焚物を追加しなければすぐに消えてしまいます。" #: src/game.cpp msgid "" "It's quite decent and looks like it'll burn for a bit without extra fuel." -msgstr "十分な火力があり、焚き木を追加しなくても少しは燃え続けそうです。" +msgstr "十分な火力があり、焚物を追加しなくても少しは燃え続けそうです。" #: src/game.cpp msgid "It looks solid, and will burn for a few hours without extra fuel." -msgstr "十分な火力があり、焚き木を追加しなくても数時間は燃え続けそうです。" +msgstr "十分な火力があり、焚物を追加しなくても数時間は燃え続けそうです。" #: src/game.cpp msgid "" "It's very well supplied and even without extra fuel might burn for at least " "a part of a day." -msgstr "焚き木が十分にくべられており、そのままでも1日は燃え続けそうです。" +msgstr "焚物が十分にくべられており、そのままでも1日は燃え続けそうです。" #: src/game.cpp #, c-format msgid "Without extra fuel it will burn for about %s." -msgstr "焚き木を追加しなくても%sは燃え続けそうです。" +msgstr "焚物を追加しなくても%sは燃え続けそうです。" #: src/game.cpp #, c-format msgid "Without extra fuel it will burn for between %s to %s." -msgstr "焚き木を追加しなくても%sから%sは燃え続けそうです。" +msgstr "焚物を追加しなくても%sから%sは燃え続けそうです。" #: src/game.cpp msgid "You cannot interact with a vehicle while mounted." @@ -207375,16 +207512,16 @@ msgstr "視界外" #: src/game.cpp #, c-format msgid "Cover: %d%%" -msgstr "" +msgstr "遮蔽率: %d%%" #: src/game.cpp msgid "Impassable" -msgstr "" +msgstr "通行不可" #: src/game.cpp #, c-format msgid "Move cost: %d" -msgstr "" +msgstr "移動コスト: %d" #: src/game.cpp #, c-format @@ -207412,7 +207549,7 @@ msgstr "未完成のタスク: %s(%d%%)" #: src/game.cpp msgid "Vehicle: " -msgstr "" +msgstr "車両: " #: src/game.cpp msgid "You cannot see what is inside of it." @@ -208271,7 +208408,7 @@ msgstr "%sが邪魔です!" #: src/game.cpp #, c-format msgid "%s Attempt to push past? You may have to fight your way back up." -msgstr "" +msgstr "%s押し退けますか?戦闘が発生する可能性があります。" #: src/game.cpp msgid "" @@ -208980,7 +209117,7 @@ msgstr "コスト" #: src/game_inventory.cpp msgid "WIELD COST" -msgstr "" +msgstr "装備コスト" #: src/game_inventory.cpp msgid "Wield item" @@ -209769,7 +209906,7 @@ msgstr "この車両は飛び辛そうです。" #: src/handle_action.cpp msgid "This vehicle cannot be flown without z levels." -msgstr "" +msgstr "この車両はZ軸拡張がないと飛行できません。" #: src/handle_action.cpp msgid "You steer the vehicle into a descent." @@ -211841,17 +211978,17 @@ msgstr "文盲なので、画面に表示された文章を読めません。" #: src/iexamine.cpp #, c-format msgid "Failure! No %s pumps found!" -msgstr "" +msgstr "失敗しました!%sポンプが見当たりません!" #: src/iexamine.cpp #, c-format msgid "Failure! No %s tank found!" -msgstr "" +msgstr "失敗しました!%sタンクが見当たりません!" #: src/iexamine.cpp #, c-format msgid "This station is out of %s. We apologize for the inconvenience." -msgstr "" +msgstr "当ガソリンスタンドは%sの在庫が不足しています。ご不便をお掛けして申し訳ございません。" #: src/iexamine.cpp msgid "Welcome to AutoGas!" @@ -211864,7 +212001,7 @@ msgstr "御用は何ですか?" #: src/iexamine.cpp #, c-format msgid "Buy %s." -msgstr "" +msgstr "%sの購入" #: src/iexamine.cpp msgid "Refund cash." @@ -211873,12 +212010,12 @@ msgstr "現金の払い戻し" #: src/iexamine.cpp #, c-format msgid "Current %s pump: " -msgstr "" +msgstr "現在の%sポンプ: " #: src/iexamine.cpp #, c-format msgid "Choose a %s pump." -msgstr "" +msgstr "%sポンプの変更" #: src/iexamine.cpp msgid "Your discount: " @@ -211887,7 +212024,7 @@ msgstr "割引: " #: src/iexamine.cpp #, c-format msgid "Your price per %s unit: " -msgstr "" +msgstr "%s価格(単位): " #: src/iexamine.cpp msgid "Hack console." @@ -211896,7 +212033,7 @@ msgstr "コンソールをハッキングしました。" #: src/iexamine.cpp #, c-format msgid "Please choose %s pump:" -msgstr "" +msgstr "%sポンプの選択: " #: src/iexamine.cpp msgid "Pump " @@ -211909,7 +212046,7 @@ msgstr "お金が足りません、キャッシュカードにお金を補充し #: src/iexamine.cpp #, c-format msgid "How many liters of %s to buy? Max: %d L. (0 to cancel)" -msgstr "" +msgstr "%sを何リットル購入しますか?最大: %dL(0で取消) " #: src/iexamine.cpp msgid "Glug Glug Glug" @@ -212029,7 +212166,7 @@ msgstr "患者は死亡しています。続行するには死体を除去して msgid "" "ERROR Bionic Level Assessment: FULL CYBORG. Autodoc Mk. XI can't operate. " "Please move patient to appropriate facility. Exiting." -msgstr "" +msgstr "生体レベル評価エラー: 完全機械化済。オートドクMk.XIは未対応です。患者を適切な装置に移してください。操作を終了します。" #: src/iexamine.cpp msgid "Autodoc Mk. XI. Status: Online. Please choose operation." @@ -212113,7 +212250,7 @@ msgstr "骨折箇所を固定する" #: src/iexamine.cpp msgid "Treat wounds" -msgstr "" +msgstr "傷を治療する" #: src/iexamine.cpp msgid "You don't have any bionics installed." @@ -212143,11 +212280,11 @@ msgstr "%1$sに固定が必要な四肢はありません。" #: src/iexamine.cpp msgid "You don't have any wounds that need treatment." -msgstr "" +msgstr "治療な必要な傷はありません。" #: src/iexamine.cpp msgid " doesn't have any wounds that need treatment." -msgstr "" +msgstr "に治療が必要な傷はありません。" #: src/iexamine.cpp msgid "" @@ -212155,6 +212292,7 @@ msgid "" "detected you've already taken antibiotics, it decided not to apply another " "dose right now." msgstr "" +"オートドクは身体が細菌に感染していることを検出しました。しかし、既に抗生物質を服用していることも検出したため、今すぐ再治療する必要はないと判断しました。" #: src/iexamine.cpp msgid "" @@ -212162,18 +212300,19 @@ msgid "" "also detected you've already taken antibiotics, it decided not to apply " "another dose right now." msgstr "" +"オートドクはの身体が細菌に感染していることを検出しました。しかし、既に抗生物質を服用していることも検出したため、今すぐ再治療する必要はないと判断しました。" #: src/iexamine.cpp msgid "" "The autodoc detected a bacterial infection in your body and injected " "antibiotics to treat it." -msgstr "" +msgstr "オートドクは身体が細菌に感染していることを検出し、抗生物質を注射しました。" #: src/iexamine.cpp msgid "" "The autodoc detected a bacterial infection in 's body and injected " "antibiotics to treat it." -msgstr "" +msgstr "オートドクはの身体が細菌に感染していることを検出し、抗生物質を注射しました。" #: src/iexamine.cpp src/iuse.cpp msgid "The muscle spasms start to go away." @@ -212188,28 +212327,28 @@ msgstr "発作に効く薬ではないようです。" msgid "" "The autodoc detected a bleeding on your %s and applied a hemostatic drug to " "stop it." -msgstr "" +msgstr "オートドクは%sからの出血を検出し、止血剤を塗布して治療しました。" #: src/iexamine.cpp #, c-format msgid "" "The autodoc detected a bleeding on 's %s and applied a hemostatic " "drug to stop it." -msgstr "" +msgstr "オートドクはの%sからの出血を検出し、止血剤を塗布して治療しました。" #: src/iexamine.cpp #, c-format msgid "" "The autodoc detected an open wound on your %s and applied a disinfectant to " "clean it." -msgstr "" +msgstr "オートドクは%sに傷口を検出し、消毒薬を塗布して治療しました。" #: src/iexamine.cpp #, c-format msgid "" "The autodoc detected an open wound on 's %s and applied a " "disinfectant to clean it." -msgstr "" +msgstr "オートドクはの%sに傷口を検出し、消毒薬を塗布して治療しました。" #: src/iexamine.cpp #, c-format @@ -213507,7 +213646,7 @@ msgstr "分量: " #: src/item.cpp msgid "Consume time: " -msgstr "" +msgstr "消費所要時間:" #: src/item.cpp msgid "* Consuming this item is addicting." @@ -214321,31 +214460,31 @@ msgstr "* この道具はCBM電力で作動します。" #: src/item.cpp msgid "It's new, and ready to burn." -msgstr "" +msgstr "まだ燃えていません。すぐに火がつきます。" #: src/item.cpp msgid "Almost new, with much material to burn." -msgstr "" +msgstr "ほとんど燃えていません。燃える素材は十分残っています。" #: src/item.cpp msgid "More than a quarter has burned away." -msgstr "" +msgstr "4分の1以上が燃え尽きました。" #: src/item.cpp msgid "More than half has burned away." -msgstr "" +msgstr "半分以上が燃え尽きました。" #: src/item.cpp msgid "Less than a quarter left to burn." -msgstr "" +msgstr "燃える素材はもう4分の1も残っていません。" #: src/item.cpp msgid "Almost completely burned out." -msgstr "" +msgstr "ほぼ完全に燃え尽きました。" #: src/item.cpp msgid "Fuel: " -msgstr "" +msgstr "焚物: " #: src/item.cpp #, c-format @@ -214375,6 +214514,8 @@ msgstr "* このアイテムは修復不可能です。" #, c-format msgid "Disassembly takes %1$s and might yield: %2$s." msgstr "" +"分解所要時間: %1$s\n" +"分解結果: %2$s" #. ~ 1 is approx. time, 2 is a list of items and tools with qualities, 3 is a #. list of items. @@ -214386,6 +214527,9 @@ msgid "" "Disassembly takes %1$s, requires %2$s and might " "yield: %3$s." msgstr "" +"分解所要時間: %1$s\n" +"必要: %2$s\n" +"分解結果:%3$s" #: src/item.cpp #, c-format @@ -215226,52 +215370,52 @@ msgstr "どの動作を実行しますか?" #: src/item_contents.cpp #, c-format msgid "Press a key to add to %s" -msgstr "" +msgstr "%s追加モード" #: src/item_contents.cpp msgid "blacklist" -msgstr "" +msgstr "ブラックリスト" #: src/item_contents.cpp msgid "whitelist" -msgstr "" +msgstr "ホワイトリスト" #: src/item_contents.cpp msgid " priority, " -msgstr "" +msgstr "優先度、" #: src/item_contents.cpp msgid " item, " -msgstr "" +msgstr "アイテム、" #: src/item_contents.cpp msgid " category, " -msgstr "" +msgstr "分類、" #: src/item_contents.cpp msgid " whitelist, " -msgstr "" +msgstr "ホワイトリスト、" #: src/item_contents.cpp msgid " blacklist" -msgstr "" +msgstr "ブラックリスト" #: src/item_contents.cpp #, c-format msgid "Enter Priority (current priority %d)" -msgstr "" +msgstr "優先度を入力(現在: %d)" #: src/item_contents.cpp msgid "item id" -msgstr "" +msgstr "アイテムID" #: src/item_contents.cpp msgid "item category" -msgstr "" +msgstr "アイテム分類" #: src/item_contents.cpp msgid "Select an item from nearby" -msgstr "" +msgstr "付近のアイテムを選択" #: src/item_contents.cpp msgid "is not a container" @@ -215283,20 +215427,20 @@ msgstr "には剛性がない" #: src/item_contents.cpp msgid "Total capacity:" -msgstr "" +msgstr "総容量:" #: src/item_contents.cpp src/item_pocket.cpp msgid " Weight: " -msgstr "" +msgstr "重量: " #: src/item_contents.cpp #, c-format msgid "%d Pockets with capacity:" -msgstr "" +msgstr "%d個のポケット:" #: src/item_contents.cpp msgid "Pocket with capacity:" -msgstr "" +msgstr "ポケット:" #: src/item_factory.cpp msgid "" @@ -215342,22 +215486,22 @@ msgstr "ポケット%d:" #: src/item_pocket.cpp msgid "Maximum item length: " -msgstr "" +msgstr "最大長: " #: src/item_pocket.cpp #, c-format msgid "Minimum item volume: %s" -msgstr "" +msgstr "最小容量: %s" #: src/item_pocket.cpp #, c-format msgid "Maximum item volume: %s" -msgstr "" +msgstr "最大容量: %s" #: src/item_pocket.cpp #, c-format msgid "Base moves to remove item: %d" -msgstr "" +msgstr "取出基本コスト: %d" #: src/item_pocket.cpp msgid "This pocket is rigid." @@ -215396,7 +215540,7 @@ msgstr "収納されたアイテムは元の重量の%.0f%% msgid "" "This pocket expands at %.0f%% of the rate of volume of " "items inside." -msgstr "" +msgstr "このポケットは体積が内部アイテムの%.0f%%分増加します。" #: src/item_pocket.cpp #, c-format @@ -215430,7 +215574,7 @@ msgstr "MOD以外は収納できない" #: src/item_pocket.cpp msgid "holster does not accept this item type" -msgstr "" +msgstr "ホルスターはこのアイテムを収納できません" #: src/item_pocket.cpp msgid "holster already contains an item" @@ -215502,31 +215646,31 @@ msgstr "容積が足りない" #: src/item_pocket.cpp msgid "Priority:" -msgstr "" +msgstr "優先度:" #: src/item_pocket.cpp #, c-format msgid "Item Whitelist: %s" -msgstr "" +msgstr "アイテムホワイトリスト: %s" #: src/item_pocket.cpp msgid "(empty)" -msgstr "" +msgstr "(空)" #: src/item_pocket.cpp #, c-format msgid "Item Blacklist: %s" -msgstr "" +msgstr "アイテムブラックリスト: %s" #: src/item_pocket.cpp #, c-format msgid "Category Whitelist: %s" -msgstr "" +msgstr "分類ホワイトリスト: %s" #: src/item_pocket.cpp #, c-format msgid "Category Blacklist: %s" -msgstr "" +msgstr "分類ブラックリスト: %s" #: src/itype.h msgid "click." @@ -219250,12 +219394,12 @@ msgstr[0] "%1$dの%2$sを%3$sに装填しました。" #: src/iuse_actor.cpp #, c-format msgid "You deploy the %s wrong. It is hostile!" -msgstr "" +msgstr "設定を間違えたようです。%sは敵対化しました!" #: src/iuse_actor.cpp #, c-format msgid "You deploy the %s." -msgstr "" +msgstr "%sを設置しました。" #: src/iuse_actor.cpp msgid "Place npc where?" @@ -219644,7 +219788,7 @@ msgstr "呪文: " #: src/iuse_actor.cpp msgid "You can't read." -msgstr "" +msgstr "読めません。" #: src/iuse_actor.cpp #, c-format @@ -221121,6 +221265,7 @@ msgid "" " they are kitten or not. The game ends when robot finds kitten. " "Alternatively, you may end the game by hitting %s." msgstr "" +"ゲームの目的は子猫を見つけることです。しかし様々な子猫以外の存在がこのゲームを複雑にしています。ロボットはそれが子猫であるか否か、触れなければ判別できません。ロボットが子猫を見つけたらゲームクリアです。途中で終了する場合は%sを押してください。" #: src/iuse_software_kitten.cpp msgid "Press any key to start." @@ -221129,12 +221274,12 @@ msgstr "始めるには何かキーを押して下さい。" #: src/iuse_software_kitten.cpp #, c-format msgid "robotfindskitten v22July2008 - press %s to quit." -msgstr "" +msgstr "robotfindskitten v22July2008 - %s で終了" #: src/iuse_software_kitten.cpp #, c-format msgid "Invalid command: Use direction keys or press %s to quit." -msgstr "" +msgstr "無効なコマンド:方向キーを使うか、%sを押して終了して下さい。" #: src/iuse_software_kitten.cpp msgid "You found kitten! Way to go, robot!" @@ -226630,7 +226775,7 @@ msgstr "見られている気がします。嫌な気分です。" #: src/monattack.cpp msgid "Your sight darkens as the visions overtake you!" -msgstr "" +msgstr "目の前が真っ暗になり、何も見えません!" #: src/monattack.cpp #, c-format @@ -228264,11 +228409,11 @@ msgstr "瀕死" #: src/monster.cpp msgid "Can see to your current location" -msgstr "" +msgstr "現在地から視認できます。" #: src/monster.cpp msgid "Can't see to your current location" -msgstr "" +msgstr "現在地から視認できません。" #: src/monster.cpp #, c-format @@ -229622,7 +229767,7 @@ msgstr "開始/手足の負傷" #: src/newcharacter.cpp msgid "Fungal infected player" -msgstr "" +msgstr "真菌感染" #: src/newcharacter.cpp msgid "No starting NPC" @@ -229924,11 +230069,11 @@ msgstr "%1$sが何か言っていますが聴こえません!" #: src/npc.cpp msgid "Aware of your presence" -msgstr "" +msgstr "あなたを知覚しています。" #: src/npc.cpp msgid "Unaware of you" -msgstr "" +msgstr "あなたを知覚していません。" #: src/npc.cpp msgid "Completely untrusting" @@ -231433,11 +231578,11 @@ msgstr "隣接破壊" #: src/options.cpp msgid "Pulp Adjacent Zombie Only" -msgstr "" +msgstr "隣接ゾンビのみ死体破壊" #: src/options.cpp msgid "Pulp Zombies Only" -msgstr "" +msgstr "ゾンビのみ死体破壊" #: src/options.cpp msgid "Auto mining" @@ -232437,13 +232582,13 @@ msgstr "使用したいタイルセットを選択します。" #: src/options.cpp msgid "Memory map overlay preset" -msgstr "" +msgstr "記憶マップ/描画色" #: src/options.cpp msgid "" "Specified the overlay in which the memory map is drawn. Requires restart. " "For custom overlay define gamma and RGB values for dark and light colors." -msgstr "" +msgstr "記憶マップの描画色を指定します。反映には再起動が必要です。カスタムに設定すると、暗色と明色のガンマ値とRGB値を指定できます。" #: src/options.cpp msgid "Darkened" @@ -232455,67 +232600,67 @@ msgstr "セピア" #: src/options.cpp msgid "Sepia Dark" -msgstr "" +msgstr "ダークセピア" #: src/options.cpp msgid "Blue Dark" -msgstr "" +msgstr "ダークブルー" #: src/options.cpp msgid "Custom dark color RGB overlay - RED" -msgstr "" +msgstr "暗色RGB値 - レッド" #: src/options.cpp msgid "Specify RGB value for color RED for dark color overlay." -msgstr "" +msgstr "暗色のレッドの値を指定します。" #: src/options.cpp msgid "Custom dark color RGB overlay - GREEN" -msgstr "" +msgstr "暗色RGB値 - グリーン" #: src/options.cpp msgid "Specify RGB value for color GREEN for dark color overlay." -msgstr "" +msgstr "暗色のグリーンの値を指定します。" #: src/options.cpp msgid "Custom dark color RGB overlay - BLUE" -msgstr "" +msgstr "暗色RGB値 - ブルー" #: src/options.cpp msgid "Specify RGB value for color BLUE for dark color overlay." -msgstr "" +msgstr "暗色のブルーの値を指定します。" #: src/options.cpp msgid "Custom bright color RGB overlay - RED" -msgstr "" +msgstr "明色RGB値 - レッド" #: src/options.cpp msgid "Specify RGB value for color RED for bright color overlay." -msgstr "" +msgstr "明色のレッドの値を指定します。" #: src/options.cpp msgid "Custom bright color RGB overlay - GREEN" -msgstr "" +msgstr "明色RGB値 - グリーン" #: src/options.cpp msgid "Specify RGB value for color GREEN for bright color overlay." -msgstr "" +msgstr "明色のグリーンの値を指定します。" #: src/options.cpp msgid "Custom bright color RGB overlay - BLUE" -msgstr "" +msgstr "明色RGB値 - ブルー" #: src/options.cpp msgid "Specify RGB value for color BLUE for bright color overlay." -msgstr "" +msgstr "明色のブルーの値を指定します。" #: src/options.cpp msgid "Custom gamma for overlay" -msgstr "" +msgstr "ガンマ値" #: src/options.cpp msgid "Specify gamma value for overlay." -msgstr "" +msgstr "描画色のガンマ値を指定します。" #: src/options.cpp msgid "Pixel minimap" @@ -234994,7 +235139,7 @@ msgstr "%sに修理すべき故障は見当たりません。" #: src/player.cpp msgid "It is damaged, but cannot be repaired." -msgstr "" +msgstr "損傷していますが修復が可能です。" #: src/player.cpp #, c-format @@ -235292,7 +235437,7 @@ msgstr "この作業は%sスキルをレベル%d以上に上昇させるには #: src/player.cpp msgid " (empty)" -msgstr "" +msgstr "(空)" #: src/player.cpp msgid "Wield what?" @@ -235357,7 +235502,7 @@ msgstr "水泳コスト: %+d\n" #: src/player_display.cpp #, c-format msgid "Movement point cost: %+d\n" -msgstr "" +msgstr "移動コスト: %+d\n" #: src/player_display.cpp #, c-format @@ -235748,7 +235893,7 @@ msgstr "前の分類に切替" #: src/player_display.cpp msgid "Toggle skill training / Upgrade stat" -msgstr "" +msgstr "切替/スキル鍛錬・能力値上昇" #: src/player_hardcoded_effects.cpp msgid "You feel nauseous." @@ -236822,7 +236967,7 @@ msgstr[0] "%1$d個の%2$sレベル%3$d以上の工具" #, c-format msgid "%1$d tool with %2$s of %3$d or more" msgid_plural "%1$d tools with %2$s of %3$d or more" -msgstr[0] "" +msgstr[0] "%1$d個の%2$s性能%3$d以上の道具" #. ~ %1$s: tool name, %2$d: charge requirement #: src/requirements.cpp @@ -237051,15 +237196,15 @@ msgstr "限定" #: src/scores_ui.cpp msgid "achievements" -msgstr "" +msgstr "実績" #: src/scores_ui.cpp msgid "conducts" -msgstr "" +msgstr "制約" #: src/scores_ui.cpp msgid "Conducts" -msgstr "" +msgstr "制約" #: src/scores_ui.cpp #, c-format @@ -237068,18 +237213,20 @@ msgid "" "the debug menu to work around a game bug, then you can re-enable %s via the " "debug menu (\"Enable achievements\" under the \"Game\" submenu)." msgstr "" +"%sは恐らくデバッグメニュー使用が原因で無効化されています。デバッグメニューをゲームのバグ回避のみに使用した場合は、%sをデバッグメニューから再度有効化できます(サブメニュー[ゲーム]内の[有効化" +" - 実績]を選択)。" #: src/scores_ui.cpp #, c-format msgid "This game has no valid %s.\n" -msgstr "" +msgstr "有効な%sがありません。\n" #: src/scores_ui.cpp #, c-format msgid "" "Note that only %s that existed when you started this game and still exist " "now will appear here." -msgstr "" +msgstr "ゲーム開始時に存在し現在も存在する%sのみが表示されます。" #: src/scores_ui.cpp msgid "This game has no valid scores.\n" @@ -237099,7 +237246,7 @@ msgstr "実績" #: src/scores_ui.cpp msgid "CONDUCTS" -msgstr "" +msgstr "制約" #: src/scores_ui.cpp msgid "SCORES" @@ -238439,7 +238586,7 @@ msgstr "運転中は部品を取り付けられません。" msgid "" "Installing this part will mean that this vehicle is no longer flightworthy." " Continue?" -msgstr "" +msgstr "この部品を取り付けると、車両が飛行できなくなります。続けますか?" #: src/veh_interact.cpp msgid "Installing this part will make the vehicle unfoldable. Continue?" @@ -238477,11 +238624,11 @@ msgstr "この車両は修復できません\n" msgid "" "Repairing this part will mean that this vehicle is no longer flightworthy. " "Continue?" -msgstr "" +msgstr "この部品を修復すると、車両が飛行できなくなります。続けますか?" #: src/veh_interact.cpp msgid "You chose not to install this part to keep the vehicle flyable.\n" -msgstr "" +msgstr "部品の取り付けを中止しました。車両は飛行可能です。\n" #: src/veh_interact.cpp msgid "Your morale is too low to mend…" @@ -238639,7 +238786,7 @@ msgstr "運転中には何も取り外さない方が良いでしょう。" msgid "" "Removing this part will mean that this vehicle is no longer flightworthy. " "Continue?" -msgstr "" +msgstr "この部品を取り外すと、車両が飛行できなくなります。続けますか?" #: src/veh_interact.cpp msgid "The vehicle has no liquid fuel left to siphon." @@ -240872,7 +241019,7 @@ msgstr "--利用可能なMODはありません--" #: src/worldfactory.cpp msgid "--NO RESULTS FOUND--" -msgstr "" +msgstr "--結果が見つかりません--" #: src/worldfactory.cpp msgid "Saved list of active mods as default" diff --git a/lang/po/pl.po b/lang/po/pl.po index 35e0b6c2bdefd..f463cf2720e64 100644 --- a/lang/po/pl.po +++ b/lang/po/pl.po @@ -17,7 +17,7 @@ msgid "" msgstr "" "Project-Id-Version: cataclysm-dda 0.E\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-06-23 10:06+0800\n" +"POT-Creation-Date: 2020-06-26 12:50+0800\n" "PO-Revision-Date: 2018-04-26 14:47+0000\n" "Last-Translator: Aleksander Sienkiewicz , 2020\n" "Language-Team: Polish (https://www.transifex.com/cataclysm-dda-translators/teams/2217/pl/)\n" @@ -59132,6 +59132,38 @@ msgstr "" "Filcowe łaty, ciasno zwinięte dla łatwiejszego przechowywania. Rozłóż by " "rozpakować." +#: lang/json/GENERIC_from_json.py +msgid "bundle of planks" +msgid_plural "bundles of planks" +msgstr[0] "" +msgstr[1] "" +msgstr[2] "" +msgstr[3] "" + +#. ~ Description for {'str': 'bundle of planks', 'str_pl': 'bundles of +#. planks'} +#: lang/json/GENERIC_from_json.py +msgid "" +"Ten construction planks securely lashed together with a rope. Disassemble " +"to unpack." +msgstr "" + +#: lang/json/GENERIC_from_json.py +msgid "bundle of stout branches" +msgid_plural "bundles of stout branches" +msgstr[0] "" +msgstr[1] "" +msgstr[2] "" +msgstr[3] "" + +#. ~ Description for {'str': 'bundle of stout branches', 'str_pl': 'bundles of +#. stout branches'} +#: lang/json/GENERIC_from_json.py +msgid "" +"Ten stout branches securely lashed together with a rope. Disassemble to " +"untie them." +msgstr "" + #: lang/json/GENERIC_from_json.py msgid "t-substrate sample" msgid_plural "t-substrate samples" @@ -72880,6 +72912,19 @@ msgstr "" "Metalowy kran który może być przyłączony do zbiornika z wodą dla łatwego " "dostępu." +#: lang/json/GENERIC_from_json.py lang/json/vehicle_part_from_json.py +msgid "wooden wheel mount" +msgid_plural "wooden wheel mounts" +msgstr[0] "" +msgstr[1] "" +msgstr[2] "" +msgstr[3] "" + +#. ~ Description for {'str': 'wooden wheel mount'} +#: lang/json/GENERIC_from_json.py +msgid "A piece of wood with holes suitable for a bike wheel." +msgstr "" + #: lang/json/GENERIC_from_json.py lang/json/vehicle_part_from_json.py msgid "light wheel mount" msgid_plural "light wheel mounts" @@ -124911,8 +124956,8 @@ msgid "" msgstr "" #: lang/json/gun_from_json.py -msgid "MAS 223" -msgid_plural "MAS 223" +msgid "MAS .223" +msgid_plural "MAS .223" msgstr[0] "" msgstr[1] "" msgstr[2] "" @@ -221245,6 +221290,15 @@ msgid "" "extending the time until the food spoils." msgstr "" +#. ~ Description for {'str': 'wooden wheel mount'} +#: lang/json/vehicle_part_from_json.py +msgid "A piece of wood with holes suitable for a bike or motorbike wheel." +msgstr "" + +#: lang/json/vehicle_part_from_json.py +msgid "wooden wheel mount (steerable)" +msgstr "" + #: lang/json/vehicle_part_from_json.py msgid "light wheel mount (steerable)" msgstr "" diff --git a/lang/po/ru.po b/lang/po/ru.po index 7d1eec0f70907..b89e813a54032 100644 --- a/lang/po/ru.po +++ b/lang/po/ru.po @@ -70,7 +70,7 @@ msgid "" msgstr "" "Project-Id-Version: cataclysm-dda 0.E\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-06-23 10:06+0800\n" +"POT-Creation-Date: 2020-06-26 12:50+0800\n" "PO-Revision-Date: 2018-04-26 14:47+0000\n" "Last-Translator: CountAlex, 2020\n" "Language-Team: Russian (https://www.transifex.com/cataclysm-dda-translators/teams/2217/ru/)\n" @@ -62428,6 +62428,39 @@ msgstr "" "Войлочные лоскуты, крепко связанные для удобства хранения. Разберите для " "распаковки." +#: lang/json/GENERIC_from_json.py +msgid "bundle of planks" +msgid_plural "bundles of planks" +msgstr[0] "связка досок" +msgstr[1] "связки досок" +msgstr[2] "связок досок" +msgstr[3] "связки досок" + +#. ~ Description for {'str': 'bundle of planks', 'str_pl': 'bundles of +#. planks'} +#: lang/json/GENERIC_from_json.py +msgid "" +"Ten construction planks securely lashed together with a rope. Disassemble " +"to unpack." +msgstr "" +"Десяток строительных досок, связанных верёвкой. Разберите для распаковки." + +#: lang/json/GENERIC_from_json.py +msgid "bundle of stout branches" +msgid_plural "bundles of stout branches" +msgstr[0] "связка прочных веток" +msgstr[1] "связки прочных веток" +msgstr[2] "связок прочных веток" +msgstr[3] "связки прочных веток" + +#. ~ Description for {'str': 'bundle of stout branches', 'str_pl': 'bundles of +#. stout branches'} +#: lang/json/GENERIC_from_json.py +msgid "" +"Ten stout branches securely lashed together with a rope. Disassemble to " +"untie them." +msgstr "Десяток прочных веток, связанных верёвкой. Разберите для распаковки." + #: lang/json/GENERIC_from_json.py msgid "t-substrate sample" msgid_plural "t-substrate samples" @@ -76694,6 +76727,19 @@ msgstr "" "Металлический водопроводный кран, который можно присоединить к баку с водой " "для её использования." +#: lang/json/GENERIC_from_json.py lang/json/vehicle_part_from_json.py +msgid "wooden wheel mount" +msgid_plural "wooden wheel mounts" +msgstr[0] "деревянное крепление колеса" +msgstr[1] "деревянных крепления колеса" +msgstr[2] "деревянных креплений колеса" +msgstr[3] "деревянные крепления колеса" + +#. ~ Description for {'str': 'wooden wheel mount'} +#: lang/json/GENERIC_from_json.py +msgid "A piece of wood with holes suitable for a bike wheel." +msgstr "Кусок дерева с отверстиями для велосипедного колеса." + #: lang/json/GENERIC_from_json.py lang/json/vehicle_part_from_json.py msgid "light wheel mount" msgid_plural "light wheel mounts" @@ -132585,12 +132631,12 @@ msgstr "" "рекламируемая как оружие самозащиты." #: lang/json/gun_from_json.py -msgid "MAS 223" -msgid_plural "MAS 223" -msgstr[0] "MAS 223" -msgstr[1] "MAS 223" -msgstr[2] "MAS 223" -msgstr[3] "MAS 223" +msgid "MAS .223" +msgid_plural "MAS .223" +msgstr[0] "MAS .223" +msgstr[1] "MAS .223" +msgstr[2] "MAS .223" +msgstr[3] "MAS .223" #: lang/json/gun_from_json.py msgid "" @@ -171504,11 +171550,11 @@ msgstr "гараж у заправки" #: lang/json/overmap_terrain_from_json.py msgid "dispensary" -msgstr "гомеопатическая аптека" +msgstr "пункт продажи каннабиоидов" #: lang/json/overmap_terrain_from_json.py msgid "dispensary roof" -msgstr "крыша аптеки" +msgstr "крыша пункта продажи каннабиоидов" #: lang/json/overmap_terrain_from_json.py msgid "small office" @@ -239178,6 +239224,16 @@ msgstr "" "Охлаждаемый бак на 60 литров. Когда включен, жидкость внутри охлаждается, " "что не дает ей быстро испортиться." +#. ~ Description for {'str': 'wooden wheel mount'} +#: lang/json/vehicle_part_from_json.py +msgid "A piece of wood with holes suitable for a bike or motorbike wheel." +msgstr "" +"Кусок дерева с отверстиями для велосипедного или мотоциклетного колеса." + +#: lang/json/vehicle_part_from_json.py +msgid "wooden wheel mount (steerable)" +msgstr "деревянное крепление колеса (управляемое)" + #: lang/json/vehicle_part_from_json.py msgid "light wheel mount (steerable)" msgstr "легкое крепление колеса (управляемое)" diff --git a/lang/po/zh_CN.po b/lang/po/zh_CN.po index f2b29d3f77a8c..d049a11a75c85 100644 --- a/lang/po/zh_CN.po +++ b/lang/po/zh_CN.po @@ -39,6 +39,7 @@ # 保周 郭 , 2020 # none none <514065589@qq.com>, 2020 # ehnuhc , 2020 +# Brett Dong , 2020 # 等离子 坦克 , 2020 # 曾泰瑋 , 2020 # Mein Führer <851000914@qq.com>, 2020 @@ -47,21 +48,20 @@ # VoidForge , 2020 # GeekDuanLian , 2020 # Silencess , 2020 -# L rient <1972308206@qq.com>, 2020 # fei li , 2020 +# L rient <1972308206@qq.com>, 2020 # 何方神圣 何 <1366003560@qq.com>, 2020 -# cainiao , 2020 # Amans Tofu , 2020 # Aloxaf , 2020 -# Brett Dong , 2020 +# cainiao , 2020 # msgid "" msgstr "" "Project-Id-Version: cataclysm-dda 0.E\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-06-23 10:06+0800\n" +"POT-Creation-Date: 2020-06-26 12:50+0800\n" "PO-Revision-Date: 2018-04-26 14:47+0000\n" -"Last-Translator: Brett Dong , 2020\n" +"Last-Translator: cainiao , 2020\n" "Language-Team: Chinese (China) (https://www.transifex.com/cataclysm-dda-translators/teams/2217/zh_CN/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -20039,13 +20039,13 @@ msgstr "一个覆盖全身的肉眼无法看见的环境防护光环。" #: lang/json/ARMOR_from_json.py msgid "aura of repelling arc" msgid_plural "aura of repelling arcs" -msgstr[0] "电弧击退光环" +msgstr[0] "电弧光环" #. ~ Description for aura of repelling arc #: lang/json/ARMOR_from_json.py msgid "" "An invisible aura that strikes melee attackers with arcs of electricity." -msgstr "" +msgstr "一个肉眼无法看见的能向近战攻击你的敌人放出电弧的光环。" #: lang/json/BATTERY_from_json.py msgid "test battery" @@ -21690,14 +21690,14 @@ msgstr "一个安装在脊柱和脑干交汇处的炸弹。它在安装时就被 #: lang/json/BIONIC_ITEM_from_json.py lang/json/bionic_from_json.py msgid "Skullgun CBM" msgid_plural "Skullgun CBMs" -msgstr[0] "" +msgstr[0] "头骨枪CBM" #. ~ Description for {'str': 'Skullgun CBM'} #: lang/json/BIONIC_ITEM_from_json.py lang/json/bionic_from_json.py msgid "" "Concealed in your head is a single shot .40 pistol. Activate the bionic to " "fire and reload the skullgun." -msgstr "" +msgstr "你的头骨中隐藏着一只 .40 口径单发手枪。激活这个生化插件以发射并装填该手枪。" #: lang/json/BIONIC_ITEM_from_json.py msgid "Precision Solderers CBM" @@ -26306,7 +26306,7 @@ msgstr "一些爱开玩笑的人轻轻地烧焦了这边平装本反乌托邦小 msgid "" "\"It was a pleasure to burn. It was a special pleasure to see things eaten," " to see things blackened and changed.\"" -msgstr "这本书封面写着:“燃烧是一种乐趣。看到吃的东西,看到变黑变黑的东西,我感到特别高兴。”" +msgstr "这本书封面写着:“燃烧是一种乐趣。看到火焰吞噬它们,看到变黑变黑的东西,我感到特别高兴。”" #: lang/json/BOOK_from_json.py msgid "" @@ -29642,7 +29642,7 @@ msgstr "你可以引导魔力打开近距离内任何锁着的门。" #: lang/json/BOOK_from_json.py msgid "Scroll of Repelling Arc" msgid_plural "Scrolls of Repelling Arc" -msgstr[0] "" +msgstr[0] "电弧光环卷轴" #. ~ Description for {'str': 'Scroll of Repelling Arc', 'str_pl': 'Scrolls of #. Repelling Arc'} @@ -29650,7 +29650,7 @@ msgstr[0] "" msgid "" "You manifest an aura of crackling electricity around you to strike attackers" " with baleful lightning." -msgstr "" +msgstr "你在你周围召唤出一道噼啪作响的电弧光环,能放出可怕的闪电电击近战攻击你的敌人。" #: lang/json/BOOK_from_json.py msgid "A Technomancer's Guide to Debugging C:DDA" @@ -33238,7 +33238,7 @@ msgstr[0] "红茶" msgid "" "The beverage of gentlemen everywhere, made from applying hot water to " "oxidized leaves of the tea plant /Camellia sinensis/." -msgstr "一份供全球绅士享用的饮品,通过将滚烫的开水倒入被称作\"山茶科山茶属\"的茶树的脱氧叶子中而成。" +msgstr "一份供全球绅士享用的饮品,通过将滚烫的开水倒入被称作\"山茶科山茶属\"的茶树的发酵过的叶子中而成。" #: lang/json/COMESTIBLE_from_json.py msgid "bark tea" @@ -33293,7 +33293,7 @@ msgid "" "Made from applying hot water to leaves of the tea plant /Camellia sinensis/." " Green tea has a lighter, fresher taste than black and is traditionally " "preferred in Asian cultures." -msgstr "用热水浸泡茶树的叶子制成。绿茶比红茶口味清淡,是传统亚洲文化中的首选。" +msgstr "一道通过将滚烫的开水倒入被称作\"山茶科山茶属\"的茶树的叶子制成的饮品。绿茶比红茶口味清淡,是传统亚洲文化中的首选。" #: lang/json/COMESTIBLE_from_json.py msgid "fruit tea" @@ -45638,6 +45638,32 @@ msgid "" "Felt patches, bundled tightly together for storage. Disassemble to unpack." msgstr "一捆被紧紧地捆在一起以存放的毛毡片,可拆解。" +#: lang/json/GENERIC_from_json.py +msgid "bundle of planks" +msgid_plural "bundles of planks" +msgstr[0] "木板捆" + +#. ~ Description for {'str': 'bundle of planks', 'str_pl': 'bundles of +#. planks'} +#: lang/json/GENERIC_from_json.py +msgid "" +"Ten construction planks securely lashed together with a rope. Disassemble " +"to unpack." +msgstr "十片被绳子紧紧地捆在一起以存放的木板,可拆解。" + +#: lang/json/GENERIC_from_json.py +msgid "bundle of stout branches" +msgid_plural "bundles of stout branches" +msgstr[0] "粗树枝捆" + +#. ~ Description for {'str': 'bundle of stout branches', 'str_pl': 'bundles of +#. stout branches'} +#: lang/json/GENERIC_from_json.py +msgid "" +"Ten stout branches securely lashed together with a rope. Disassemble to " +"untie them." +msgstr "十根被绳子紧紧地捆在一起以存放的粗树枝,可拆解。" + #: lang/json/GENERIC_from_json.py msgid "t-substrate sample" msgid_plural "t-substrate samples" @@ -53458,7 +53484,7 @@ msgstr "这是一种大型的柔性塑料薄膜,这种塑料可能用于商业 #: lang/json/GENERIC_from_json.py msgid "rigid plastic sheet" msgid_plural "rigid plastic sheets" -msgstr[0] "" +msgstr[0] "硬塑料板" #. ~ Description for rigid plastic sheet #: lang/json/GENERIC_from_json.py @@ -53551,12 +53577,12 @@ msgid "" "An enormous beam of solid wood, very heavy and hard to lug around, but also " "very sturdy for construction. You could saw or chop it into smaller pieces," " like planks or panels." -msgstr "巨大的实木梁,很重,很难被拖动,但对于建筑来说也很坚固。你可以把它锯成小块,比如木板或中木板。" +msgstr "巨大的实木梁,很重,很难被拖动,但对于建筑来说也很坚固。你可以把它锯成小块,比如木板或木墙板。" #: lang/json/GENERIC_from_json.py msgid "wooden panel" msgid_plural "wooden panels" -msgstr[0] "中木板" +msgstr[0] "木墙板" #. ~ Description for {'str': 'wooden panel'} #: lang/json/GENERIC_from_json.py @@ -54748,14 +54774,14 @@ msgstr "一个让船只保持浮力的木板。将其安装在载具上直到它 #: lang/json/GENERIC_from_json.py lang/json/vehicle_part_from_json.py msgid "raft boat hull" msgid_plural "raft boat hulls" -msgstr[0] "" +msgstr[0] "木筏船壳" #. ~ Description for {'str': 'raft boat hull'} #: lang/json/GENERIC_from_json.py msgid "" "Logs tied together to make a vehicle float. Add boat hulls to a vehicle " "until it floats. Then attach oars or a motor to get the boat to move." -msgstr "" +msgstr "一根让船只保持浮力的原木。将其安装在载具上直到它浮在水上。继续添加桨或者引擎来移动船只。" #: lang/json/GENERIC_from_json.py lang/json/vehicle_part_from_json.py msgid "plastic boat hull" @@ -56143,6 +56169,16 @@ msgstr[0] "水龙头" msgid "A metal faucet that can be attached to a water tank for easy access." msgstr "一个金属的水龙头,可以连接到水箱上,方便随时取水。" +#: lang/json/GENERIC_from_json.py lang/json/vehicle_part_from_json.py +msgid "wooden wheel mount" +msgid_plural "wooden wheel mounts" +msgstr[0] "木制轮架" + +#. ~ Description for {'str': 'wooden wheel mount'} +#: lang/json/GENERIC_from_json.py +msgid "A piece of wood with holes suitable for a bike wheel." +msgstr "一种带有孔的木片,适合自行车车轮使用。" + #: lang/json/GENERIC_from_json.py lang/json/vehicle_part_from_json.py msgid "light wheel mount" msgid_plural "light wheel mounts" @@ -56725,13 +56761,13 @@ msgstr "它已经给出了答案,而你还没有问任何问题。" #: lang/json/GENERIC_from_json.py msgid "woven metamaterial sheet" msgid_plural "woven metamaterial sheets" -msgstr[0] "" +msgstr[0] "超级材料织物" #. ~ Description for {'str': 'woven metamaterial sheet'} #: lang/json/GENERIC_from_json.py msgid "" "An intricately spun and carefully engineered sheet of iridescent fibers." -msgstr "" +msgstr "一片经过精心设计用十分复杂工艺制成的彩虹色纤维织物。" #: lang/json/GENERIC_from_json.py msgid "nanowire battery" @@ -56820,19 +56856,19 @@ msgstr[0] "超导电磁铁" #. ~ Description for {'str': 'super conductive electromagnet'} #: lang/json/GENERIC_from_json.py msgid "A powerful electromagnet made from a room temperature superconductor." -msgstr "" +msgstr "一个由室温超导体制成的强力电磁铁。" #: lang/json/GENERIC_from_json.py msgid "ferrofluid dynamo" msgid_plural "ferrofluid dynamos" -msgstr[0] "" +msgstr[0] "铁磁流体发电机" #. ~ Description for {'str': 'ferrofluid dynamo'} #: lang/json/GENERIC_from_json.py msgid "" "Black metallic fluid, harmonically flowing from one fractal shape to the " "next." -msgstr "" +msgstr "黑色金属流体,和谐地流动变形成一个又一个分形形状。" #: lang/json/GENERIC_from_json.py msgid "composite alloy" @@ -57472,22 +57508,22 @@ msgstr "这个装置包含了绝地武士光剑战斗的第一式:锡秋剑法 #: lang/json/GENERIC_from_json.py msgid "Shards of Granite" msgid_plural "Shards of Granite" -msgstr[0] "" +msgstr[0] "宁为玉碎" #. ~ Description for {'str_sp': 'Shards of Granite'} #: lang/json/GENERIC_from_json.py msgid "This book contains the teaching of the Stone Dragon discipline." -msgstr "" +msgstr "这本书包含了石龙流派相关的武术教学。" #: lang/json/GENERIC_from_json.py msgid "Reaping Talons" msgid_plural "Reaping Talons" -msgstr[0] "" +msgstr[0] "收割之爪" #. ~ Description for {'str_sp': 'Reaping Talons'} #: lang/json/GENERIC_from_json.py msgid "This book contains the teaching of the Tiger Claw discipline." -msgstr "" +msgstr "这本书包含了虎爪流派相关的武术教学。" #: lang/json/GENERIC_from_json.py msgid "stone shell" @@ -57511,7 +57547,7 @@ msgstr[0] "发光粉尘" msgid "" "The powdered remains of a will-o-wisps's physical form. It seems to still " "possess an otherworldly glow." -msgstr "" +msgstr "一缕鬼火遗留下来的物理形态粉末,它似乎还在散发着神奇的光芒。" #: lang/json/GENERIC_from_json.py msgid "magical reading light" @@ -57626,7 +57662,7 @@ msgstr[0] "魔力之尘" #: lang/json/GENERIC_from_json.py msgid "" "Crystalized mana in powdered form. It faintly pulses with arcane energy." -msgstr "" +msgstr "一撮粉末状的魔力结晶。它所含的奥术能量不断脉动着发出微光。" #: lang/json/GENERIC_from_json.py msgid "black dragon scale" @@ -62009,7 +62045,7 @@ msgid "" "A total conversion that shifts the Cataclysm towards an alien occupation " "survival scenario. THIS MOD WILL BREAK INTENDED FUNCTIONALITY OF OTHER " "MODS! USE OTHER MODS AT YOUR OWN RISK." -msgstr "" +msgstr "将大灾变转变为类似XCOM2风格的外星人入侵的全面改动类模组。不兼容其他模组,使用风险自负!" #: lang/json/MOD_INFO_from_json.py msgid "DinoMod" @@ -64014,7 +64050,7 @@ msgstr[0] "人类" #. ~ Description for {'str': 'human'} #: lang/json/MONSTER_from_json.py msgid "Place holder for human corpses. If you see this, it's a bug." -msgstr "" +msgstr "该物品用于人类尸体的占位符,如果你看到这个说明游戏出Bug了。" #: lang/json/MONSTER_from_json.py msgid "bat" @@ -65971,14 +66007,14 @@ msgstr "水蛭花茎秆射出了一道电弧!" #: lang/json/MONSTER_from_json.py msgid "signal tree" msgid_plural "signal trees" -msgstr[0] "" +msgstr[0] "信号树" #. ~ Description for signal tree #: lang/json/MONSTER_from_json.py msgid "" "A trunk reaches tall into the sky, its topmost branches glowing with yellow " "light. A low drum periodically shakes its vicinities." -msgstr "" +msgstr "这棵树的树干高高耸立入云,其最顶端的树枝发出黄光。它周期性地发出一阵阵低沉的鼓声震撼着四周。" #: lang/json/MONSTER_from_json.py msgid "leech pod cluster" @@ -66547,7 +66583,7 @@ msgstr "" #: lang/json/MONSTER_from_json.py msgid "spearcat hunter" msgid_plural "spearcat hunters" -msgstr[0] "" +msgstr[0] "矛猫猎手" #. ~ Description for {'str': 'spearcat hunter'} #: lang/json/MONSTER_from_json.py @@ -66555,12 +66591,12 @@ msgid "" "This cougar's eyes ooze with dark, oily fluid, and its fur is torn, " "revealing deep festering wounds. Its claws and teeth are unnaturally long " "and sharpened into dangerous looking spikes" -msgstr "" +msgstr "这只美洲狮的眼睛渗出深色油状液体,它的皮毛被撕裂,露出了深深的溃烂的伤口。它的爪子和牙齿不自然地生长,并磨尖成危险的尖刺。" #: lang/json/MONSTER_from_json.py msgid "skeletal zombear" msgid_plural "skeletal zombears" -msgstr[0] "" +msgstr[0] "骸骨丧尸熊" #. ~ Description for {'str': 'skeletal zombear'} #: lang/json/MONSTER_from_json.py @@ -66570,11 +66606,12 @@ msgid "" " seep from its joints as it shambles aimlessly, with sickening crackling " "sounds filling the air around it." msgstr "" +"一层巨大的过度生长而成的骨化组织已经形成了致密有机的骨制盔甲并取代了这只丧尸熊表面原来早已腐烂的皮肤。大块的黑色粘液从其关节不断渗出,它毫无目的蹒跚而行,全身的关节随着移动不断发出令人作呕的咔嗒声。" #: lang/json/MONSTER_from_json.py msgid "shadowcat" msgid_plural "shadowcats" -msgstr[0] "" +msgstr[0] "幽灵猫" #. ~ Description for {'str': 'shadowcat'} #: lang/json/MONSTER_from_json.py @@ -66582,12 +66619,12 @@ msgid "" "An uncanny shadow envelops this creature, as if light itself were too " "repulsed to touch it. All you can make out is the outline of a large, " "shambling cat." -msgstr "" +msgstr "这只丧尸怪物被一层不可思议的阴影所笼罩,看上去似如光线也拒绝去接触它一般。你唯一能够分辨出来的是一个步履蹒跚的大型猫科动物轮廓。" #: lang/json/MONSTER_from_json.py msgid "acidic zombear" msgid_plural "acidic zombears" -msgstr[0] "" +msgstr[0] "酸液丧尸熊" #. ~ Description for {'str': 'acidic zombear'} #: lang/json/MONSTER_from_json.py @@ -66595,12 +66632,12 @@ msgid "" "A sickly-looking dead dead black bear with patchy fur. Its skin looks " "especially thin, with a sticky, yellow fluid flowing through the clearly " "visible veins." -msgstr "" +msgstr "一头看上去很恶心的毛皮斑驳的丧尸熊。它的皮肤看上去非常薄,你都能看到它皮肤下的血管中流动着粘粘的黄色液体。" #: lang/json/MONSTER_from_json.py msgid "antlered hammer" msgid_plural "antlered hammers" -msgstr[0] "" +msgstr[0] "鹿角锤头兽" #. ~ Description for {'str': 'antlered hammer'} #: lang/json/MONSTER_from_json.py @@ -66608,12 +66645,12 @@ msgid "" "This once great moose's eyes ooze with dark, oily fluid, and its flesh is " "torn and scarred. Its entire body bulges with distended muscles and " "swollen, festering wounds." -msgstr "" +msgstr "这只曾经体型巨大的驼鹿的眼睛渗出深色油状液体,被撕烈的血肉瘢痕累累。它全身上下布满了肿胀的肌肉和溃烂的伤口。" #: lang/json/MONSTER_from_json.py msgid "thorny bear shambler" msgid_plural "thorny bear shamblers" -msgstr[0] "" +msgstr[0] "荆棘蹒跚兽" #. ~ Description for {'str': 'thorny bear shambler'} #: lang/json/MONSTER_from_json.py @@ -66623,6 +66660,7 @@ msgid "" "Long interlocking thorns wrap the antlers, dripping with a mysterious " "silvery liquid." msgstr "" +"曾经是一只体型巨大的驼鹿,现在被长而密的毛发所覆盖,上面缠绕着荆棘藤蔓,这些藤蔓缠绕在一起包裹着它的尸体。长长的刺缠绕在鹿角上,有种神秘的银色液体从尖端滴下来。" #: lang/json/MONSTER_from_json.py msgid "jawed terror" @@ -69375,7 +69413,7 @@ msgstr[0] "紫绿色恐龙幼仔" #: lang/json/MONSTER_from_json.py msgid "fungal Spinosaurus zombie" msgid_plural "fungal Spinosaurus zombies" -msgstr[0] "" +msgstr[0] "真菌丧尸棘龙" #. ~ Description for {'str': 'fungal Spinosaurus zombie'} #: lang/json/MONSTER_from_json.py @@ -69384,11 +69422,12 @@ msgid "" "back, fungal tendrils now sprout from its mouth, eyes, and other orifices, " "holding together an enormous shambling mass of mold-covered flesh." msgstr "" +"曾经是一只巨大的长着鳄鱼头和背帆的食肉恐龙,但现在真菌从它的口、眼、耳和其他孔洞处喷薄而出并互相连接起来,被霉菌覆盖的巨大躯体还在蹒跚而行。" #: lang/json/MONSTER_from_json.py msgid "fungal Z-Rex" msgid_plural "fungal Z-Rexes" -msgstr[0] "" +msgstr[0] "真菌丧尸霸王龙" #. ~ Description for {'str': 'fungal Z-Rex', 'str_pl': 'fungal Z-Rexes'} #: lang/json/MONSTER_from_json.py @@ -69396,12 +69435,12 @@ msgid "" "Once monarch of the dinosaurs, fungal tendrils now sprout from its mouth, " "eyes, and other orifices, holding together an enormous shambling mass of " "mold-covered flesh." -msgstr "" +msgstr "曾经是一只食肉恐龙中的霸王,但现在真菌从它的口、眼、耳和其他孔洞处喷薄而出并互相连接起来,被霉菌覆盖的巨大躯体还在蹒跚而行。" #: lang/json/MONSTER_from_json.py msgid "fungal Deinonychus zombie" msgid_plural "fungal Deinonychus zombies" -msgstr[0] "" +msgstr[0] "真菌丧尸恐爪龙" #. ~ Description for {'str': 'fungal Deinonychus zombie'} #: lang/json/MONSTER_from_json.py @@ -69409,24 +69448,24 @@ msgid "" "Once a medium-sized feathered carnivorous dinosaur, fungal tendrils now " "sprout from its mouth, eyes, and other orifices, holding together an " "enormous shambling mass of mold-covered flesh." -msgstr "" +msgstr "曾经是一只长着羽毛的中型食肉恐龙,但现在真菌从它的口、眼、耳和其他孔洞处喷薄而出并互相连接起来,被霉菌覆盖的巨大躯体还在蹒跚而行。" #: lang/json/MONSTER_from_json.py msgid "Gallimimus zombie" msgid_plural "Gallimimus zombie" -msgstr[0] "" +msgstr[0] "丧尸似鸡龙" #. ~ Description for {'str_sp': 'Gallimimus zombie'} #: lang/json/MONSTER_from_json.py msgid "" "The shuffling corpse of a medium-sized bipedal dinosaur covered with " "tattered feathers and black putrid liquid." -msgstr "" +msgstr "一具体表覆盖着破碎羽毛和黑色腐臭液体的蹒跚而行的中型两足恐龙尸体。" #: lang/json/MONSTER_from_json.py msgid "Pachy zombie" msgid_plural "Pachy zombie" -msgstr[0] "" +msgstr[0] "丧尸厚头龙" #. ~ Description for {'str_sp': 'Pachy zombie'} #: lang/json/MONSTER_from_json.py @@ -69434,12 +69473,12 @@ msgid "" "The shuffling corpse of a medium-sized bipedal dinosaur covered with " "tattered feathers and black putrid liquid. It looks like a reptilian " "ostrich with a round hard-looking domed head." -msgstr "" +msgstr "一具体表覆盖着破碎羽毛和黑色腐臭液体的蹒跚而行的中型两足恐龙尸体。它看起来像一只长着坚硬圆头的蜥蜴鸵鸟。" #: lang/json/MONSTER_from_json.py msgid "Campto zombie" msgid_plural "Campto zombie" -msgstr[0] "" +msgstr[0] "丧尸弯龙" #. ~ Description for {'str_sp': 'Campto zombie'} #: lang/json/MONSTER_from_json.py @@ -69447,7 +69486,7 @@ msgid "" "The shuffling corpse of a large feathered bipedal dinosaur with strong legs," " broad shoulders and a pointed beak. Its tattered feathers are stained with" " black, sticky liquid." -msgstr "" +msgstr "一具体表覆盖着破碎羽毛和黑色腐臭液体的蹒跚而行的大型两足恐龙尸体。它的两条腿很强壮,肩膀宽阔,长着尖喙。" #: lang/json/MONSTER_from_json.py msgid "Spinosaurus zombie" @@ -69464,7 +69503,7 @@ msgstr "一具巨大的腐烂恐龙尸体,有着凶猛的像鳄鱼一样的头 #: lang/json/MONSTER_from_json.py msgid "Spinosaurus shady zombie" msgid_plural "Spinosaurus shady zombies" -msgstr[0] "" +msgstr[0] "暗影丧尸棘龙" #. ~ Description for {'str': 'Spinosaurus shady zombie'} #: lang/json/MONSTER_from_json.py @@ -69473,6 +69512,7 @@ msgid "" " huge bipedal dinosaur with a tattered sail. The head is long and narrow " "with a V-shaped snout." msgstr "" +"这只丧尸恐龙被一层不可思议的阴影所笼罩,看上去似如光线也拒绝去接触它一般。你唯一能够分辨出来的是一个步履蹒跚的长着破碎背帆的巨型恐龙轮廓。头部又长又窄,有一个V形的鼻子。" #: lang/json/MONSTER_from_json.py msgid "Z-Rex" @@ -69487,7 +69527,7 @@ msgstr "一大坨破烂发臭的血肉,托起了它巨大的牙齿。" #: lang/json/MONSTER_from_json.py msgid "Shady Z-Rex" msgid_plural "Shady Z-Rexs" -msgstr[0] "" +msgstr[0] "暗影丧尸霸王龙" #. ~ Description for {'str': 'Shady Z-Rex'} #: lang/json/MONSTER_from_json.py @@ -69496,6 +69536,7 @@ msgid "" " huge bipedal dinosaur with feathery edges. The head looks big, lots of big" " teeth would fit in it." msgstr "" +"这只丧尸恐龙被一层不可思议的阴影所笼罩,看上去似如光线也拒绝去接触它一般。你唯一能够分辨出来的是一个步履蹒跚的模糊的巨型恐龙轮廓。头看起来很大,能装下很多大牙齿。" #: lang/json/MONSTER_from_json.py msgid "S-Rex" @@ -69512,43 +69553,43 @@ msgstr "巨大而密集的骨柱托举起巨大的尖牙,牙尖不断滴下黑 #: lang/json/MONSTER_from_json.py msgid "Albertosaurus zombie" msgid_plural "Albertosaurus zombie" -msgstr[0] "" +msgstr[0] "丧尸阿尔伯塔龙" #. ~ Description for {'str_sp': 'Albertosaurus zombie'} #: lang/json/MONSTER_from_json.py msgid "" "Massive jaws drooling black liquid lifted over grasping claws by a huge " "shuffling dinosaur corpse." -msgstr "" +msgstr "一具巨大的恐龙尸体撑起了巨大的大嘴和一对贪婪的利爪,嘴中不断滴落下黑色的液体。" #: lang/json/MONSTER_from_json.py msgid "Triceraterror" msgid_plural "Triceraterror" -msgstr[0] "" +msgstr[0] "丧尸三角龙" #. ~ Description for {'str_sp': 'Triceraterror'} #: lang/json/MONSTER_from_json.py msgid "" "A massive shambling rhino-like dinosaur corpse with a bony crest from which " "three wicked looking horns emerge. Its black eyes ooze like tears." -msgstr "" +msgstr "一具庞大的、犀牛般的蹒跚而行的恐龙尸体。头上有三个恐怖大角和一个骨质头冠。眼中流出的黑色液体如同眼泪一般滴落下来。" #: lang/json/MONSTER_from_json.py msgid "Stegosaurus zombie" msgid_plural "Stegosaurus zombie" -msgstr[0] "" +msgstr[0] "丧尸剑龙" #. ~ Description for {'str_sp': 'Stegosaurus zombie'} #: lang/json/MONSTER_from_json.py msgid "" "A large shambling quadruped dinosaur corpse dragging with the weight of the " "plates on its back, waving a much livelier looking spiked tail." -msgstr "" +msgstr "一具巨大的蹒跚而行的四足恐龙尸体,移动缓慢,背上有骨板,尾巴上有骨钉。" #: lang/json/MONSTER_from_json.py msgid "Ankylosaurus zombie" msgid_plural "Ankylosaurus zombie" -msgstr[0] "" +msgstr[0] "丧尸甲龙" #. ~ Description for {'str_sp': 'Ankylosaurus zombie'} #: lang/json/MONSTER_from_json.py @@ -69557,11 +69598,12 @@ msgid "" "armored plates and black, glistening eyes. Its tail ends in a massive " "spiked club of bone." msgstr "" +"这具巨大的恐龙尸体看上去像是一种巨大的史前犰狳,它的尾端有一个巨大的骨质狼牙棒。它体表的装甲骨板有些已经剥落了,黑色空洞的眼中有什么正在闪闪发光。" #: lang/json/MONSTER_from_json.py msgid "Apatosaurus zombie" msgid_plural "Apatosaurus zombie" -msgstr[0] "" +msgstr[0] "丧尸迷惑龙" #. ~ Description for {'str_sp': 'Apatosaurus zombie'} #: lang/json/MONSTER_from_json.py @@ -69569,12 +69611,12 @@ msgid "" "Massive, long-necked, four-legged dinosaur corpse with a long, whip-like " "tail. The head is upright and the neck looks like it would still make a " "good strong club." -msgstr "" +msgstr "一具巨大的蹒跚而行的长颈四足恐龙尸体,尾巴非常长,像鞭子一样。它的头能够直立起来,脖子看起来像一根巨大而坚固的棍子。" #: lang/json/MONSTER_from_json.py msgid "Zombie dragon" msgid_plural "Zombie dragon" -msgstr[0] "" +msgstr[0] "丧尸角鼻龙" #. ~ Description for {'str_sp': 'Zombie dragon'} #: lang/json/MONSTER_from_json.py @@ -69582,19 +69624,19 @@ msgid "" "This zombie is enormous, scaly, studded with bony spikes, and it moves with " "horrible speed. Its colorful horns are worn and wet with filth and its " "bright scales and bone spikes have taken a beating." -msgstr "" +msgstr "一具巨大的、鳞片上布满了骨刺的恐龙尸体,它以可怕的速度移动。它五颜六色的角已经磨损,沾满了污秽,它明亮的鳞片和骨刺看上去也受过重创。" #: lang/json/MONSTER_from_json.py msgid "Allosaurus zombie" msgid_plural "Allosaurus zombie" -msgstr[0] "" +msgstr[0] "丧尸异特龙" #. ~ Description for {'str_sp': 'Allosaurus zombie'} #: lang/json/MONSTER_from_json.py msgid "" "The shambling corpse of a large predatory bipedal dinosaur, with tiger-like " "stripes on its broad, scaled back." -msgstr "" +msgstr "一具巨大的蹒跚而行的掠夺性双足恐龙尸体,宽阔的脊背上有着和老虎相似的条纹。" #: lang/json/MONSTER_from_json.py msgid "Deinonychus zombie" @@ -69612,7 +69654,7 @@ msgstr "一具体表覆盖着破碎羽毛和黑色腐臭液体的蹒跚而行的 #: lang/json/MONSTER_from_json.py msgid "Deinonychus shady zombie" msgid_plural "Deinonychus shady zombies" -msgstr[0] "" +msgstr[0] "暗影丧尸恐爪龙" #. ~ Description for {'str': 'Deinonychus shady zombie'} #: lang/json/MONSTER_from_json.py @@ -69621,47 +69663,48 @@ msgid "" " medium-sized bipedal dinosaur with feathery edges. Both feet brandish a " "large sickle-like claw." msgstr "" +"这只丧尸恐龙被一层不可思议的阴影所笼罩,看上去似如光线也拒绝去接触它一般。你唯一能够分辨出来的是一个步履蹒跚的模糊的中型恐龙轮廓。双脚前端巨大的镰刀状利爪正不断挥舞着。" #: lang/json/MONSTER_from_json.py msgid "Utahraptor zombie" msgid_plural "Utahraptor zombies" -msgstr[0] "" +msgstr[0] "丧尸犹他盗龙" #. ~ Description for {'str': 'Utahraptor zombie'} #: lang/json/MONSTER_from_json.py msgid "" "The swaying, hopping corpse of a large bipedal dinosaur with feathered arms," " a long tail, and long sharp scythe-like claws." -msgstr "" +msgstr "一具巨大的蹒跚而行的两足恐龙尸体。有着被破碎羽毛覆盖的胳膊、一个长尾巴和长长的镰刀状的爪子。" #: lang/json/MONSTER_from_json.py msgid "Parasaurolophus zombie" msgid_plural "Parasaurolophus zombie" -msgstr[0] "" +msgstr[0] "丧尸副栉龙" #. ~ Description for {'str_sp': 'Parasaurolophus zombie'} #: lang/json/MONSTER_from_json.py msgid "" "A huge mottled dinosaur with a blunt head crest, dead and walking, eyes " "vacant and swollen." -msgstr "" +msgstr "一具巨大的、身体上有各色斑块的蹒跚而行的恐龙尸体,长着钝的头脊。它正四处游荡,黑漆漆的眼中空洞而肿胀。" #: lang/json/MONSTER_from_json.py msgid "Dimorphodon zombie" msgid_plural "Dimorphodon zombie" -msgstr[0] "" +msgstr[0] "丧尸双型齿翼龙" #. ~ Description for {'str_sp': 'Dimorphodon zombie'} #: lang/json/MONSTER_from_json.py msgid "" "The raggedly flying corpse of a feathered reptile over three feet long, " "with short wings and a big colorful beak." -msgstr "" +msgstr "一具长着破碎羽毛的蹒跚而行的爬行动物尸体,有三英尺长,长着较短的翅膀和颜色鲜艳的大嘴。" #: lang/json/MONSTER_from_json.py msgid "Dilophosaurus zombie" msgid_plural "Dilophosaurus zombie" -msgstr[0] "" +msgstr[0] "丧尸双脊龙" #. ~ Description for {'str_sp': 'Dilophosaurus zombie'} #: lang/json/MONSTER_from_json.py @@ -69669,7 +69712,7 @@ msgid "" "The shuffling corpse of a medium dinosaur with sharp teeth and two prominent" " bony crests on its head with ragged strips of ripped flesh hanging down " "like a frill." -msgstr "" +msgstr "一具蹒跚而行的中型恐龙尸体,牙齿很锋利,头部有两个突出的骨冠,上面撕裂的血肉像褶边一样垂落下来。" #: lang/json/MONSTER_from_json.py msgid "improvised SMG turret" @@ -71478,19 +71521,19 @@ msgstr "这个虚拟法术用于引爆颅脑内的炸弹。极度致命。" #: lang/json/SPELL_from_json.py msgid "Skullgun Snapback" -msgstr "" +msgstr "头骨枪反震" #. ~ Description for Skullgun Snapback #: lang/json/SPELL_from_json.py msgid "" "This fake spell occurs on skullgun activation. May be fatal if done in " "critical condition." -msgstr "" +msgstr "这个虚拟法术用于模拟头骨枪的反震。在重伤时可能会致命。" #. ~ Message for SPELL 'Skullgun Snapback' #: lang/json/SPELL_from_json.py msgid "Your head snaps back from the force of the shot." -msgstr "" +msgstr "你的头被射击的威力反震了!" #: lang/json/SPELL_from_json.py msgid "psi stun" @@ -72461,28 +72504,28 @@ msgstr "雾墙术" #: lang/json/SPELL_from_json.py msgid "Repelling Arc Aura" -msgstr "" +msgstr "电弧光环" #. ~ Description for Repelling Arc Aura #: lang/json/SPELL_from_json.py msgid "This is a sub-spell of the Repelling Arc spell." -msgstr "" +msgstr "这只是电弧光环附属的法术。" #. ~ description for the sound of spell 'Repelling Arc Aura' #: lang/json/SPELL_from_json.py msgid "arcing electricity!" -msgstr "" +msgstr "放电声!" #: lang/json/SPELL_from_json.py msgid "Repelling Arc" -msgstr "" +msgstr "电弧光环" #. ~ Description for Repelling Arc #: lang/json/SPELL_from_json.py msgid "" "Manifests an aura of crackling electricity around you to strike attackers " "with baleful lightning." -msgstr "" +msgstr "你在你周围召唤出一道噼啪作响的电弧光环,能放出可怕的闪电电击近战攻击你的敌人。" #: lang/json/SPELL_from_json.py msgid "Bless" @@ -74915,7 +74958,7 @@ msgstr "传说中雷神索尔的神奇腰带,至少看起来是这样的。它 #: lang/json/TOOL_ARMOR_from_json.py msgid "lesser dimensional toolbelt" msgid_plural "lesser dimensional toolbelts" -msgstr[0] "" +msgstr[0] "次级次元工具腰带" #. ~ Description for {'str': 'lesser dimensional toolbelt'} #: lang/json/TOOL_ARMOR_from_json.py @@ -74923,12 +74966,12 @@ msgid "" "A sturdy workman's belt that fits around your waist, covered in easy to " "access pouches. Like all dimensional spaces, they hold more than they " "should with a fair weight reduction." -msgstr "" +msgstr "一条结实合身的工具腰带,上面套着许多方便取用的袋子。像所有的次元袋一样,放入的物品重量会减轻。" #: lang/json/TOOL_ARMOR_from_json.py msgid "greater dimensional toolbelt" msgid_plural "greater dimensional toolbelts" -msgstr[0] "" +msgstr[0] "高级次元工具腰带" #. ~ Description for {'str': 'greater dimensional toolbelt'} #: lang/json/TOOL_ARMOR_from_json.py @@ -74936,7 +74979,7 @@ msgid "" "A heavy duty workman's belt that fits around your waist, covered in easy to " "access pouches. Like all dimensional spaces, they hold far more than they " "should with a substantial weight reduction." -msgstr "" +msgstr "一条特别结实合身的工具腰带,上面套着许多方便取用的袋子。像所有的次元袋一样,放入的物品重量会大大减轻。" #: lang/json/TOOL_ARMOR_from_json.py msgid "Belt of Weaponry" @@ -80893,24 +80936,24 @@ msgstr "扁平的石头绑在了木棍上,就当做铁锹用吧,虽然显然 #: lang/json/TOOL_from_json.py msgid "metal rake" msgid_plural "metal rakes" -msgstr[0] "" +msgstr[0] "金属耙子" #. ~ Description for {'str': 'metal rake'} #: lang/json/TOOL_from_json.py msgid "A sturdy metal rake, a must-have during autumn." -msgstr "" +msgstr "一把结实的金属耙子,秋季收获必备农具。" #: lang/json/TOOL_from_json.py msgid "plastic rake" msgid_plural "plastic rakes" -msgstr[0] "" +msgstr[0] "塑料耙子" #. ~ Description for {'str': 'plastic rake'} #: lang/json/TOOL_from_json.py msgid "" "A cheap plastic rake. Will break quite fast if used for anything other than" " raking leaves." -msgstr "" +msgstr "一把廉价的塑料耙子。如果把它用于除了耙树叶以外的任何用途,都会很快损坏它。" #: lang/json/TOOL_from_json.py msgid "scythe" @@ -80938,22 +80981,22 @@ msgstr "一个挖坑工具,你可以用它在你周围挖坑。" #: lang/json/TOOL_from_json.py msgid "snow shovel" msgid_plural "snow shovels" -msgstr[0] "" +msgstr[0] "雪铲" #. ~ Description for {'str': 'snow shovel'} #: lang/json/TOOL_from_json.py msgid "This is a sturdy tool used for shoving snow." -msgstr "" +msgstr "一把用来铲雪的结实铲子。" #: lang/json/TOOL_from_json.py msgid "plastic snow shovel" msgid_plural "plastic snow shovels" -msgstr[0] "" +msgstr[0] "塑料雪铲" #. ~ Description for {'str': 'plastic snow shovel'} #: lang/json/TOOL_from_json.py msgid "A cheap plastic shovel used for shoving snow." -msgstr "" +msgstr "一把用来铲雪的廉价塑料铲子。" #: lang/json/TOOL_from_json.py msgid "sickle" @@ -81666,7 +81709,7 @@ msgstr "一种便携式烧炭金工锻造工具。搭配合适当的工具,你 #: lang/json/TOOL_from_json.py msgid "Rock Forge" msgid_plural "Rock Forges" -msgstr[0] "" +msgstr[0] "石头熔炉" #: lang/json/TOOL_from_json.py msgid "metalworking chisel" @@ -81797,13 +81840,13 @@ msgstr "长颈的铝制火钳。是烹饪或金属加工制造的常用工具。 #: lang/json/TOOL_from_json.py msgid "sandpaper" msgid_plural "sheets of sandpaper" -msgstr[0] "" +msgstr[0] "砂纸" #. ~ Description for {'str': 'sandpaper', 'str_pl': 'sheets of sandpaper'} #: lang/json/TOOL_from_json.py msgid "" "A sheet of rough paper. It is commonly used in metalworking and carpentry." -msgstr "" +msgstr "一张粗糙的纸。常用于金属加工和木工。" #: lang/json/TOOL_from_json.py msgid "compressed air horn" @@ -81957,14 +82000,14 @@ msgstr "一床皮毛制成的铺盖,可以卷起来随身携带。垫在地上 #: lang/json/TOOL_from_json.py msgid "garden hose" msgid_plural "garden hoses" -msgstr[0] "" +msgstr[0] "花园软管" #. ~ Description for {'str': 'garden hose'} #: lang/json/TOOL_from_json.py msgid "" "This is a flexible garden hose. If you cut it in smaller pieces, it could " "be used for crafting, or siphoning fuel from a vehicle." -msgstr "" +msgstr "一根柔软的花园用橡胶软管。可以用来做东西,也可以用来从载具中抽油。" #: lang/json/TOOL_from_json.py msgid "grip hook" @@ -84246,7 +84289,7 @@ msgstr "这是一个可移动的砖窑,设计用来烧砖的,但也可以烧 #: lang/json/TOOL_from_json.py msgid "Kiln" msgid_plural "Kilns" -msgstr[0] "" +msgstr[0] "窑炉" #: lang/json/TOOL_from_json.py msgid "paint chipper" @@ -85224,7 +85267,7 @@ msgstr "" #: lang/json/TOOL_from_json.py msgid "complete bionic toolkit" msgid_plural "complete bionic toolkits" -msgstr[0] "" +msgstr[0] "生化插件完全工具箱" #. ~ Description for {'str': 'complete bionic toolkit'} #: lang/json/TOOL_from_json.py @@ -85233,7 +85276,7 @@ msgid "" "designed to disassemble and test the quality of industrially produced " "bionics. A highly skilled and patient engineer could use them to manually " "assemble new cybernetics." -msgstr "" +msgstr "一整套非常小的自动化工具和加密所用的数字密钥,通常用于在工厂中拆解和测试生化插件。一个经验丰富且耐心的工程师可以用它手工组装新的生化插件。" #: lang/json/TOOL_from_json.py msgid "energy saber" @@ -89623,7 +89666,7 @@ msgstr "移除窗户上的胶带" #: lang/json/construction_from_json.py msgid "Board Up Window" -msgstr "用木板封死窗户" +msgstr "用木板封窗" #: lang/json/construction_from_json.py msgid "Reinforce Boarded Window" @@ -99778,9 +99821,9 @@ msgid "" msgstr "一把采用经典 AR-15 步枪设计,结构紧凑,使用7.5英寸长枪管的手枪,商业宣传上主要将它当作家庭防御武器销售。" #: lang/json/gun_from_json.py -msgid "MAS 223" -msgid_plural "MAS 223" -msgstr[0] "MAS 223 步枪" +msgid "MAS .223" +msgid_plural "MAS .223" +msgstr[0] "MAS .223 步枪" #: lang/json/gun_from_json.py msgid "" @@ -102683,13 +102726,13 @@ msgstr "" #: lang/json/gun_from_json.py msgid "makeshift shotgun" msgid_plural "makeshift shotguns" -msgstr[0] "" +msgstr[0] "自制霰弹枪" #: lang/json/gun_from_json.py msgid "" "A crude shotgun, composed of two thick steel pipes, an end cap and a nail. " "The lack of sights make this weapon only useful at point-blank range." -msgstr "" +msgstr "一把自制的简易霰弹枪,由两根厚钢管、一个后盖和一根钉子组成。由于缺乏瞄具,这把武器只能在近距离使用。" #: lang/json/gun_from_json.py msgid "flaregun" @@ -103326,11 +103369,11 @@ msgstr "三管激光" #: lang/json/gun_from_json.py msgid "bionic skullgun" msgid_plural "bionic skullguns" -msgstr[0] "" +msgstr[0] "生化头骨枪" #: lang/json/gun_from_json.py msgid "Bionic one-shot subdermal .40 pistol integrated with your head." -msgstr "" +msgstr "通过生化插件植入你头骨的单发伸缩式手枪。" #: lang/json/gun_from_json.py msgid "CRIT .5 LP" @@ -104112,7 +104155,7 @@ msgstr "" #: lang/json/gun_from_json.py msgid "slam-fire shotgun" msgid_plural "slam-fire shotguns" -msgstr[0] "" +msgstr[0] "猛击击发式霰弹枪" #: lang/json/gun_from_json.py msgid "Ichaival" @@ -110783,12 +110826,12 @@ msgstr "这里有个吸引丧尸的陷阱。" #: lang/json/map_extra_from_json.py msgid "Reed" -msgstr "" +msgstr "芦苇" #. ~ Description for {'str': 'Reed'} #: lang/json/map_extra_from_json.py msgid "Water vegetation." -msgstr "" +msgstr "一种水生植物。" #. ~ Computer name #: lang/json/mapgen_from_json.py @@ -112278,7 +112321,7 @@ msgid "" msgstr "" "你通过反复移动来变得更加难以击中。\n" "\n" -"闪避技能+1。\n" +"闪避技能 +1。\n" "持续 1 回合,可叠加 2 次。" #: lang/json/martial_art_from_json.py @@ -112376,7 +112419,7 @@ msgid "" msgstr "" "你的持续不停的舞步使你在战斗时异常灵巧。\n" "\n" -"闪避技能+1,闪避次数+1。" +"闪避技能 +1,闪避次数 +1。" #: lang/json/martial_art_from_json.py msgid "Capoeira Momentum" @@ -112394,7 +112437,7 @@ msgid "" msgstr "" "你能感受到你移动的节奏。你不仅更加难以击中,你的脚法也变得更加炫酷!\n" "\n" -"闪避技能+1。\n" +"闪避技能 +1。\n" "解锁“回旋踢”和“扫堂腿”。\n" "持续 3 回合。" @@ -112412,6 +112455,10 @@ msgid "" "+15% Bash damage.\n" "Lasts 2 turns. Stacks 3 times." msgstr "" +"你从不击空,这只是舞蹈的一部分,而最棒的部分即将开始!\n" +"\n" +"+15% 钝击伤害。\n" +"持续 2 回合。可叠加 3 次。" #: lang/json/martial_art_from_json.py msgid "Crane Kung Fu" @@ -112607,6 +112654,11 @@ msgid "" "Enables \"Combination Strike\" technique.\n" "Lasts 3 turns. Stacks 3 times." msgstr "" +"机会出现之时,你能在暴击之后连上一招更强的攻击。\n" +"\n" +"+15% 所有伤害。\n" +"解锁“组合连击”招式。\n" +"持续 3 回合。可叠加 3 次。" #: lang/json/martial_art_from_json.py msgid "Fencing" @@ -112661,10 +112713,14 @@ msgid "" "+1 Accuracy.\n" "Lasts 1 turn." msgstr "" +"由于你的招架,你的下一次攻击更容易命中敌人。\n" +"\n" +"命中 +1。\n" +"持续 1 回合。" #: lang/json/martial_art_from_json.py msgid "Remise" -msgstr "" +msgstr "二次进攻" #. ~ Description of buff 'Remise' for martial art '{'str': 'Fencing'}' #: lang/json/martial_art_from_json.py @@ -112675,6 +112731,11 @@ msgid "" "Enables \"Compound Attack\" technique.\n" "Lasts 1 turn." msgstr "" +"你的佯攻是下一次致命一击的完美准备!\n" +"\n" +"命中 +1。\n" +"解锁“复杂进攻”招式。\n" +"持续 1 回合。" #. ~ Description for martial art '{'str': 'Fior Di Battaglia'}' #: lang/json/martial_art_from_json.py @@ -112733,7 +112794,7 @@ msgstr "" #: lang/json/martial_art_from_json.py msgid "Defense Break" -msgstr "" +msgstr "防御反击" #. ~ Description of buff 'Defense Break' for martial art '{'str': 'Fior Di #. Battaglia'}' @@ -112744,10 +112805,14 @@ msgid "" "+1 Accuracy.\n" "Lasts 1 turn. Stacks 3 times." msgstr "" +"每一个成功的阻挡都会暴露出你对手防守的漏洞。\n" +"\n" +"命中 +1。\n" +"持续 1 回合。可叠加 3 次。" #: lang/json/martial_art_from_json.py msgid "Tactical Feinting" -msgstr "" +msgstr "战术佯攻" #. ~ Description of buff 'Tactical Feinting' for martial art '{'str': 'Fior Di #. Battaglia'}' @@ -112758,6 +112823,10 @@ msgid "" "Enables \"Hook and Drag\" technique.\n" "Lasts 1 turn." msgstr "" +"他们被你的佯攻给骗了!\n" +"\n" +"解锁“勾拽”招式。\n" +"持续 1 回合。" #: lang/json/martial_art_from_json.py msgid "Judo" @@ -112950,7 +113019,7 @@ msgid "" msgstr "" "你就像猫一样动作敏捷,难以被抓住。\n" "\n" -"闪避技能 +1.0。" +"闪避技能 +1。" #: lang/json/martial_art_from_json.py msgid "Leopard's Stalk" @@ -113036,7 +113105,7 @@ msgstr "" #: lang/json/martial_art_from_json.py msgid "Deflection" -msgstr "" +msgstr "偏斜格挡" #. ~ Description of buff 'Deflection' for martial art '{'str': 'Medieval #. Swordsmanship'}' @@ -113046,10 +113115,14 @@ msgid "" "Enables \"Sweeping Strike\" and \"Deathblow\" techniques.\n" "Lasts 1 turn." msgstr "" +"你偏斜格挡了敌人的攻击,现在可以反击敌人了!\n" +"\n" +"解锁“横扫攻击”和“雷霆一击”招式。\n" +"持续 1 回合。" #: lang/json/martial_art_from_json.py msgid "Manslayer" -msgstr "" +msgstr "杀心四起" #. ~ Description of buff 'Manslayer' for martial art '{'str': 'Medieval #. Swordsmanship'}' @@ -113059,6 +113132,10 @@ msgid "" "Enables \"Vicious Strike\" techniques.\n" "Lasts 1 turn." msgstr "" +"你的强力一击给了你结束这场战斗的机会!\n" +"\n" +"解锁“要害攻击”招式。\n" +"持续 1 回合。" #: lang/json/martial_art_from_json.py msgid "Muay Thai" @@ -113116,6 +113193,10 @@ msgid "" "+Bash damage increased by 25% of Strength, blocked damage decreased by 50% of Strength.\n" "Lasts 5 turns." msgstr "" +"受到攻击并不会减慢你的速度。你能比你的对手撑得更久,从而赢得这场比赛。\n" +"\n" +"钝击伤害按 力量 的 25% 增加,格挡后伤害按 力量 的 50% 降低。\n" +"持续 5 回合。" #: lang/json/martial_art_from_json.py msgid "Ninjutsu" @@ -113190,12 +113271,12 @@ msgid "" msgstr "" "忍者训练注重敏捷和灵活。\n" "\n" -"+1 闪避技能,命中率按敏捷的 20% 提升。\n" +"闪避技能 +1,命中按敏捷的 20% 提升。\n" "持续 1 回合。" #: lang/json/martial_art_from_json.py msgid "Loss of Surprise" -msgstr "" +msgstr "失去奇袭" #. ~ Description of buff 'Loss of Surprise' for martial art '{'str': #. 'Ninjutsu'}' @@ -113207,10 +113288,14 @@ msgid "" "-50% all damage.\n" "Last 3 turns." msgstr "" +"你的意图已经被人发现了!你需要过一阵子才能再次偷袭。\n" +"\n" +"-50% 所有伤害。\n" +"持续 3 回合。" #: lang/json/martial_art_from_json.py msgid "Escape Plan" -msgstr "" +msgstr "逃脱计划" #. ~ Description of buff 'Escape Plan' for martial art '{'str': 'Ninjutsu'}' #: lang/json/martial_art_from_json.py @@ -113220,6 +113305,10 @@ msgid "" "+2 Dodge attempts, +10 movement speed.\n" "Last 3 turns." msgstr "" +"你的目标已经死了。是时候逃离并计划下一次攻击了。\n" +"\n" +"闪避次数 +2,移动速度 +10。\n" +"持续 3 回合。" #: lang/json/martial_art_from_json.py msgid "Niten Ichi-Ryu" @@ -113285,9 +113374,9 @@ msgid "" msgstr "" "暗如黑夜\n" "魑魅魍魉,四面袭来\n" -"快跑!\n" +"速速逃离!\n" "\n" -"-5.0 闪避技能。\n" +"闪避技能 -5。\n" "持续 1 回合。" #: lang/json/martial_art_from_json.py @@ -113305,7 +113394,7 @@ msgid "" "Enables \"In-One Timing\".\n" "Lasts 1 turn." msgstr "" -"幸有此光\n" +"幸有此光,\n" "于此暗夜\n" "有如月洒清辉\n" "\n" @@ -113314,7 +113403,7 @@ msgstr "" #: lang/json/martial_art_from_json.py msgid "Falling Leaf" -msgstr "" +msgstr "落叶" #. ~ Description of buff 'Falling Leaf' for martial art '{'str': 'Niten Ichi- #. Ryu'}' @@ -113327,10 +113416,16 @@ msgid "" "-1.0 Dodge skill, -1 bash damage, -1 cut damage.\n" "Lasts 1 turn. Stacks 5 times." msgstr "" +"利刃已出鞘,\n" +"然则万物随时间消逝。\n" +"唯忍得以精进此术。\n" +"\n" +"闪避技能 -1,钝击伤害 -1,斩击伤害 -1。\n" +"持续 1 回合。可堆叠 5 次。" #: lang/json/martial_art_from_json.py msgid "Stillness" -msgstr "" +msgstr "宁静" #. ~ Description of buff 'Stillness' for martial art '{'str': 'Niten Ichi- #. Ryu'}' @@ -113344,6 +113439,12 @@ msgid "" "+2 Accuracy, Dodge skill increased by 50% of Perception.\n" "Lasts 2 turns." msgstr "" +"风暴之眼,\n" +"安宁须臾,\n" +"旋即消失无踪。\n" +"\n" +"命中 +2,闪避技能按 感知 的 50% 增加。\n" +"持续2回合。" #: lang/json/martial_art_from_json.py msgid "Pankration" @@ -113518,7 +113619,7 @@ msgstr "" #: lang/json/martial_art_from_json.py msgid "Snake's Coil" -msgstr "" +msgstr "蛇之盘绕" #. ~ Description of buff 'Snake's Coil' for martial art '{'str': 'Snake Kung #. Fu'}' @@ -113530,6 +113631,10 @@ msgid "" "+1 Accuracy, gain armor penetration equal to 50% of Perceptions.\n" "Lasts 1 turn. Stacks 3 times." msgstr "" +"每条蛇都会等待完美一击的时刻的到来。当你的对手分心时,毫无怜悯地攻击他们的弱点!\n" +"\n" +"命中 +1,护甲穿透按 感知的 50% 增加。\n" +"持续 1 回合。可叠加 3 次。" #: lang/json/martial_art_from_json.py msgid "Sōjutsu" @@ -113710,7 +113815,7 @@ msgstr "" #: lang/json/martial_art_from_json.py msgid "Cross Hands" -msgstr "" +msgstr "十字手" #. ~ Description of buff 'Cross Hands' for martial art '{'str': 'Tai Chi'}' #: lang/json/martial_art_from_json.py @@ -113722,6 +113827,10 @@ msgid "" "Enables \"Palm Strike\" and \"Double Palm Strike\" techniques.\n" "Lasts 3 turns." msgstr "" +"通过花点时间为自己下一步招式做准备,你可以充分利用全身来攻击和防守。\n" +"\n" +"闪避技能 +1,格挡后伤害按感知的50%减少。\n" +"解锁“拗步掌”和“双按掌”招式。持续 3 回合。" #: lang/json/martial_art_from_json.py msgid "Tiger Kung Fu" @@ -113786,7 +113895,7 @@ msgstr "" #: lang/json/martial_art_from_json.py msgid "Tiger Rampage" -msgstr "" +msgstr "虎之怒" #. ~ Description of buff 'Tiger Rampage' for martial art '{'str': 'Tiger Kung #. Fu'}' @@ -113798,6 +113907,10 @@ msgid "" "Gain Armor Penetration equal to 50% of Strength.\n" "Lasts 1 turns. Stacks 2 times." msgstr "" +"你的对手输了就是你赢了。你的下一击能够击破对手的防御。\n" +"\n" +"护甲穿透按力量的50%增加。\n" +"持续 1 回合。可叠加 2 次。" #: lang/json/martial_art_from_json.py msgid "Wing Chun" @@ -113858,11 +113971,11 @@ msgid "" msgstr "" "你对平衡和技巧有更深的理解。这让你能更好地躲开你的对手的攻击。\n" "\n" -"闪避技能按感知的 15% 增加。格挡伤害按感知的 50% 减少。" +"闪避技能按感知的 15% 增加。格挡后伤害按感知的 50% 减少。" #: lang/json/martial_art_from_json.py msgid "Biu Ji" -msgstr "" +msgstr "咏春标指" #. ~ Description of buff 'Biu Ji' for martial art '{'str': 'Wing Chun'}' #: lang/json/martial_art_from_json.py @@ -113873,6 +113986,10 @@ msgid "" "Accuracy increased by 20% of Perception, Enables \"Straight Punch (Knockback)\" and \"L-Hook (Knockback)\" techniques.\n" "Lasts 2 turns." msgstr "" +"通过完美地应用插指的招式,你可以打击对手的弱点,迫使他们后退,然后跟进一击!\n" +"\n" +"命中按感知的20%增加,解锁“直拳(击退)”和“左勾拳(击退)”招式。\n" +"持续2回合。" #: lang/json/martial_art_from_json.py msgid "Zui Quan" @@ -114005,18 +114122,18 @@ msgstr "+力量 钝击防护,+敏捷 酸液防护,+智力 电击防护,+ #: lang/json/martial_art_from_json.py msgid "Getting Angry" -msgstr "" +msgstr "我怒了" #. ~ Description of buff 'Getting Angry' for martial art '{'str': 'Debug #. Mastery'}' #: lang/json/martial_art_from_json.py msgid "" "When I get my hands on you… +2 bash damage for 2 turns. Stacks 5 times." -msgstr "" +msgstr "别让我抓住你…… +2 钝击伤害。持续 2 回合。可叠加 5 次。" #: lang/json/martial_art_from_json.py msgid "Lightning Strike" -msgstr "" +msgstr "闪电攻击" #. ~ Description of buff 'Lightning Strike' for martial art '{'str': 'Debug #. Mastery'}' @@ -114024,12 +114141,12 @@ msgstr "" msgid "" "Lightning strikes twice. +Perception electric damage for 3 turns. Stacks 2" " times." -msgstr "" +msgstr "闪电能击中两次。+感知 电击伤害。持续 3 回合。可叠加 2 次。" #. ~ Description of buff 'On Fire' for martial art '{'str': 'Debug Mastery'}' #: lang/json/martial_art_from_json.py msgid "YOU ARE ON FIRE! +5 fire damage for 5 turns." -msgstr "" +msgstr "你着火了!+5 火焰伤害。持续 5 回合。" #: lang/json/martial_art_from_json.py msgid "Bionic Combatives" @@ -114078,7 +114195,7 @@ msgstr "" #: lang/json/martial_art_from_json.py msgid "Optimization" -msgstr "" +msgstr "优化" #. ~ Description of buff 'Optimization' for martial art '{'str': 'Bionic #. Combatives'}' @@ -114091,6 +114208,12 @@ msgid "" "+1 Accuracy, +2 all damage.\n" "Lasts 3 turns. Stacks 3 times." msgstr "" +">10 锁定目标\n" +">20 击杀目标\n" +">30 GOTO 10\n" +"\n" +"命中 +1,所有伤害 +2。\n" +"持续 3 回合。可叠加 3 次。" #: lang/json/martial_art_from_json.py msgid "Centipede Kung Fu" @@ -114135,7 +114258,7 @@ msgstr "" #: lang/json/martial_art_from_json.py msgid "Centipede's Venom" -msgstr "" +msgstr "蜈蚣毒" #. ~ Description of buff 'Centipede's Venom' for martial art '{'str': #. 'Centipede Kung Fu'}' @@ -114146,6 +114269,10 @@ msgid "" "+2 bashing damage.\n" "Lasts 2 turns." msgstr "" +"你的毒液是你的对手永远无法忘却的长久苦痛。\n" +"\n" +"+2 钝击伤害。\n" +"持续 2 回合。" #: lang/json/martial_art_from_json.py msgid "Lizard Kung Fu" @@ -114205,8 +114332,8 @@ msgid "" msgstr "" "通过简单地攀爬、跳跃或推开附近的墙壁,你可以避开对手最严重的攻击。\n" "\n" -"紧邻墙壁时 +3.0 闪避技能。\n" -"紧邻墙壁时解锁\"壁虎之尾\"及\"壁虎墙反\"招式。" +"紧邻墙壁时闪避技能 +3。\n" +"紧邻墙壁时解锁“壁虎扫尾”及“壁虎墙反”。" #: lang/json/martial_art_from_json.py msgid "Lizard's Leap" @@ -114284,13 +114411,13 @@ msgid "" msgstr "" "向前冲,抓住你的猎物!\n" "\n" -"所有伤害 +10%。\n" +"+10% 伤害。\n" "解锁“巨螯击”。\n" "可叠加 2 次。持续 2 回合。" #: lang/json/martial_art_from_json.py msgid "Scorpion's Intimidation" -msgstr "" +msgstr "蝎之威慑" #. ~ Description of buff 'Scorpion's Intimidation' for martial art '{'str': #. 'Scorpion Kung Fu'}' @@ -114301,6 +114428,10 @@ msgid "" "+1 Dodge attempts.\n" "Lasts 1 turn." msgstr "" +"没有什么比一只被激怒的蝎子更可怕的了。你的攻击可以威吓你的敌人。\n" +"\n" +"闪避次数 +1。\n" +"持续 1 回合。" #: lang/json/martial_art_from_json.py msgid "Toad Kung Fu" @@ -114363,7 +114494,7 @@ msgstr "" #: lang/json/martial_art_from_json.py msgid "Toad's Meditation" -msgstr "" +msgstr "蛤蟆冥想功" #. ~ Description of buff 'Toad's Meditation' for martial art '{'str': 'Toad #. Kung Fu'}' @@ -114374,10 +114505,14 @@ msgid "" "+3 bash, cut, and stab armor.\n" "Lasts 2 turns." msgstr "" +"通过集中注意力,你可以增强你的铁布衫的力量。\n" +"\n" +"+3 钝击/斩击/刺击防护。\n" +"持续 2 回合。" #: lang/json/martial_art_from_json.py msgid "Toad's Venom" -msgstr "" +msgstr "蛤蟆毒" #. ~ Description of buff 'Toad's Venom' for martial art '{'str': 'Toad Kung #. Fu'}' @@ -114388,6 +114523,10 @@ msgid "" "+2 bash damage.\n" "Lasts 5 turns." msgstr "" +"你的毒液只不过是你的铁布衫威力的又一课。\n" +"\n" +"+2 钝击伤害。\n" +"持续 5 回合。" #: lang/json/martial_art_from_json.py msgid "Viper Kung Fu" @@ -114444,7 +114583,7 @@ msgid "" msgstr "" "你的闪避能力使你的对手很容易受到痛苦的伤害。\n" "\n" -" 解锁\"毒蛇疯咬\"。\n" +"解锁“毒蛇疯咬”。\n" "持续 1 回合。" #: lang/json/martial_art_from_json.py @@ -114760,7 +114899,7 @@ msgstr "" #: lang/json/martial_art_from_json.py msgid "Quicksilver Motion" -msgstr "" +msgstr "神行" #. ~ Description of buff 'Quicksilver Motion' for martial art '{'str': #. 'Diamond Mind'}' @@ -114771,10 +114910,14 @@ msgid "" "+50 Speed.\n" "Lasts 1 turn." msgstr "" +"在眨眼间,你已经移动。你的速度、反应、以及自信让你可以快速而勇敢的移动,让对手猝不及防。\n" +"\n" +"+50 速度。\n" +"持续 1 回合。" #: lang/json/martial_art_from_json.py msgid "Mind over Body" -msgstr "" +msgstr "精神御体" #. ~ Description of buff 'Mind over Body' for martial art '{'str': 'Diamond #. Mind'}' @@ -114785,6 +114928,10 @@ msgid "" "+1 Accuracy.\n" "Lasts 1 turn. Stacks 2 times" msgstr "" +"你的训练和精神力让你可以用专注力克服肉体的威胁。\n" +"\n" +"命中 +1。\n" +"持续 1 回合。可叠加 2 次。" #: lang/json/martial_art_from_json.py msgid "Hylian Swordsmanship" @@ -114898,7 +115045,7 @@ msgstr "" #: lang/json/martial_art_from_json.py msgid "Charge Up" -msgstr "" +msgstr "蓄力" #. ~ Description of buff 'Charge Up' for martial art '{'str': 'Hylian #. Swordsmanship'}' @@ -114910,6 +115057,10 @@ msgid "" "+20% damage, enables \"Spin Attack\" technique.\n" "Lasts 1 turn." msgstr "" +"花点时间准备,你可以释放出一记威力强大的回旋斩!\n" +"\n" +"+20% 伤害,解锁“回旋斩”招式。\n" +"持续 1 回合。" #: lang/json/martial_art_from_json.py msgid "Iron Heart" @@ -115024,7 +115175,7 @@ msgstr "%s 准备好和人战斗了。" #: lang/json/martial_art_from_json.py msgid "Stamina" -msgstr "" +msgstr "持久力" #. ~ Description of buff 'Stamina' for martial art '{'str': 'Pokken'}' #: lang/json/martial_art_from_json.py @@ -115035,10 +115186,14 @@ msgid "" "Gain bash, cut, stab armor equal to 50% of Strength.\n" "Lasts 3 turns." msgstr "" +"受到攻击时,你的防御会提高。\n" +"\n" +"钝击/斩击/刺击防护按力量的50%提升。\n" +"持续3回合。" #: lang/json/martial_art_from_json.py msgid "Sniper" -msgstr "" +msgstr "狙击手" #. ~ Description of buff 'Sniper' for martial art '{'str': 'Pokken'}' #: lang/json/martial_art_from_json.py @@ -115049,10 +115204,14 @@ msgid "" "+50% damage.\n" "Lasts 1 turn." msgstr "" +"击中要害时,威力会变得更强。\n" +"\n" +"+50% 伤害。\n" +"持续 1 回合。" #: lang/json/martial_art_from_json.py msgid "Moxie" -msgstr "" +msgstr "自信过度" #. ~ Description of buff 'Moxie' for martial art '{'str': 'Pokken'}' #: lang/json/martial_art_from_json.py @@ -115063,6 +115222,10 @@ msgid "" "+50% damage.\n" "Lasts 3 turns." msgstr "" +"如果打倒对手,就会充满自信,攻击会提高。\n" +"\n" +"+50% 伤害。\n" +"持续 3 回合。" #: lang/json/martial_art_from_json.py msgid "Setting Sun" @@ -115075,7 +115238,8 @@ msgid "" "strength against them. With a quick shift in stance and carefully aimed " "attack, a Setting Sun warrior sends a charging enemy tumbling in a new " "direction." -msgstr "暮日流派教导其修习者利用敌人的力量对抗敌人。随着姿态的快速转变和精心瞄准的攻击,暮日战士能让向自己冲锋的敌人失去重心翻滚向另一个方向。" +msgstr "" +"暮日派教导门徒借力打力。随着姿态的快速转变和精心瞄准的攻击,依靠着四两拨千斤的巧劲,暮日派的武道家可以让向自己冲锋的敌人失去重心翻滚向另一个方向。" #. ~ initiate message for martial art '{'str': 'Setting Sun'}' #: lang/json/martial_art_from_json.py @@ -115090,7 +115254,7 @@ msgstr "%s 调整重心,摆出了一个新姿势。" #: lang/json/martial_art_from_json.py msgid "Baffling Defense" -msgstr "" +msgstr "千幻防御" #. ~ Description of buff 'Baffling Defense' for martial art '{'str': 'Setting #. Sun'}' @@ -115102,10 +115266,14 @@ msgid "" "Dodging Skill increased by 20% of Intelligence, enables \"Mighty Throw\" and \"Ballista Throw\" techniques.\n" "Lasts 1 turn." msgstr "" +"你单脚半蹲,手放在顶门。你的对手犹豫着,没法确定该如何攻击这种诡异的架势。\n" +"\n" +"闪避技能按智力的20%提升,解锁“暮日投”和“穿云投”。\n" +"持续 1 回合。" #: lang/json/martial_art_from_json.py msgid "Feigned Opening" -msgstr "" +msgstr "伪装破绽" #. ~ Description of buff 'Feigned Opening' for martial art '{'str': 'Setting #. Sun'}' @@ -115116,6 +115284,10 @@ msgid "" "+20 Speed.\n" "Lasts 1 turn." msgstr "" +"你向你的目标露出好像是致命的破绽,但却轻易地避开,并在同时让对手为轻率的进击付出代价。\n" +"\n" +"+20 速度。\n" +"持续 1 回合。" #: lang/json/martial_art_from_json.py msgid "Shii-Cho" @@ -115207,7 +115379,7 @@ msgstr "" #: lang/json/martial_art_from_json.py msgid "Stone Dragon" -msgstr "" +msgstr "石龙" #. ~ Description for martial art '{'str': 'Stone Dragon'}' #: lang/json/martial_art_from_json.py @@ -115216,22 +115388,22 @@ msgid "" "Its teachings grant a martial adept the ability to splinter steel with a " "single, focused blow. Stone Dragon's defensive abilities focus on tapping " "into the enduring power of stone to turn aside attacks." -msgstr "" +msgstr "石龙流专注于力量,威力以及强壮。它传授了武技学徒在一击间破金裂石的威力。石龙的防御则注重于将对手的攻击的破坏力转嫁到大地上。" #. ~ initiate message for martial art '{'str': 'Stone Dragon'}' #: lang/json/martial_art_from_json.py msgid "You dig your heels into the ground and steady yourself." -msgstr "" +msgstr "你将脚后跟扎入土中,稳住自己。" #. ~ initiate message for martial art '{'str': 'Stone Dragon'}' #: lang/json/martial_art_from_json.py #, python-format msgid "%s digs their heels into the ground." -msgstr "" +msgstr "%s 将脚后跟扎入土中。" #: lang/json/martial_art_from_json.py msgid "Stone Bones" -msgstr "" +msgstr "石铸身躯" #. ~ Description of buff 'Stone Bones' for martial art '{'str': 'Stone #. Dragon'}' @@ -115242,10 +115414,14 @@ msgid "" "+1 bash, cut, and stab armor.\n" "Lasts 1 turn. Stacks 5 times." msgstr "" +"你聚集你的武器打中对手时产生的能量来强化自己的防御,这使你在面对接下来的反击时立于不败之地。\n" +"\n" +"+1 钝击/斩击/刺击防护。\n" +"持续 1 回合。可叠加 5 次。" #: lang/json/martial_art_from_json.py msgid "Stonefoot Stance" -msgstr "" +msgstr "坚如磐石" #. ~ Description of buff 'Stonefoot Stance' for martial art '{'str': 'Stone #. Dragon'}' @@ -115256,10 +115432,13 @@ msgid "" "\n" "+10% damage, +2 bash, cut, and stab armor." msgstr "" +"你将重心沉向下盘,将大地之力转化为自身的抗力。然而,移动太多会破坏你的姿势。\n" +"\n" +"+10% 伤害,+2 钝击/斩击/刺击防护。" #: lang/json/martial_art_from_json.py msgid "Cracked Stone" -msgstr "" +msgstr "裂石" #. ~ Description of buff 'Cracked Stone' for martial art '{'str': 'Stone #. Dragon'}' @@ -115270,10 +115449,14 @@ msgid "" "Enables \"Shattered Stone\" buff.\n" "Lasts 1 turn." msgstr "" +"移动太多会破坏坚如磐石的效果。站稳了别动,不要破坏了你的姿势!\n" +"\n" +"解锁“碎石”。\n" +"持续 1 回合。" #: lang/json/martial_art_from_json.py msgid "Stattered Stone" -msgstr "" +msgstr "碎石" #. ~ Description of buff 'Stattered Stone' for martial art '{'str': 'Stone #. Dragon'}' @@ -115285,10 +115468,14 @@ msgid "" "-10% damage, -2 bash, cut, and stab armor.\n" "Lasts 4 turn." msgstr "" +"你完全无法保持坚如磐石的姿势,必须停止移动一段时间以重新获得增益。\n" +"\n" +"-10% 伤害,-2 钝击/斩击/刺击防护。\n" +"持续 4 回合。" #: lang/json/martial_art_from_json.py msgid "Iron Bones" -msgstr "" +msgstr "铁布衫" #. ~ Description of buff 'Iron Bones' for martial art '{'str': 'Stone #. Dragon'}' @@ -115299,10 +115486,14 @@ msgid "" "+5 bash, cut, and stab armor.\n" "Lasts 1 turn." msgstr "" +"每当你成功攻击敌人,你就能进入冥想状态,使你近乎无懈可击。\n" +"\n" +"+5 钝击/斩击/刺击防护。\n" +"持续 1 回合。" #: lang/json/martial_art_from_json.py msgid "Tiger Claw" -msgstr "" +msgstr "虎爪" #. ~ Description for martial art '{'str': 'Tiger Claw'}' #: lang/json/martial_art_from_json.py @@ -115312,21 +115503,22 @@ msgid "" "with a furry similar to that of a barbarian, and rely on overwhelming, " "vicious assaults to defeat their enemies." msgstr "" +"虎爪流派信奉潜伏在其门徒的内心之中的狂暴怒火。在战斗中,这些战士像野兽一样咆哮,用类似于野蛮人的怒火攻击,依靠势不可挡、凶残的攻击来击败他们的敌人。" #. ~ initiate message for martial art '{'str': 'Tiger Claw'}' #: lang/json/martial_art_from_json.py msgid "You emit a low growl as you prepare for battle." -msgstr "" +msgstr "你发出低沉的咆哮,准备好作战。" #. ~ initiate message for martial art '{'str': 'Tiger Claw'}' #: lang/json/martial_art_from_json.py #, python-format msgid "%s hunkers down like a wild animal." -msgstr "" +msgstr "%s 像一只野兽一样蹲下。" #: lang/json/martial_art_from_json.py msgid "Improved Critical" -msgstr "" +msgstr "精通重击" #. ~ Description of buff 'Improved Critical' for martial art '{'str': 'Tiger #. Claw'}' @@ -115337,10 +115529,13 @@ msgid "" "\n" "+5% critical hit chance." msgstr "" +"总是全力打击。除非你想死,否则永远不要限制自己。\n" +"\n" +"+5% 暴击几率。" #: lang/json/martial_art_from_json.py msgid "Pounching Charge" -msgstr "" +msgstr "猛扑冲锋" #. ~ Description of buff 'Pounching Charge' for martial art '{'str': 'Tiger #. Claw'}' @@ -115352,10 +115547,14 @@ msgid "" "+2 Accuracy, +10% damage.\n" "Lasts 1 turn." msgstr "" +"伴随着野兽般的咆哮,你自己投身进入了这场打斗中。先发制人,一击致命。\n" +"\n" +"命中 +2,+10% 伤害。\n" +"持续 1 回合。" #: lang/json/martial_art_from_json.py msgid "Cornered Predator" -msgstr "" +msgstr "落难猎手" #. ~ Description of buff 'Cornered Predator' for martial art '{'str': 'Tiger #. Claw'}' @@ -115367,10 +115566,14 @@ msgid "" "-20% move cost.\n" "Lasts 1 turn." msgstr "" +"一只走投无路的捕食者是可怕而危险的生物。你也一样。\n" +"\n" +" -20% 移动耗时。\n" +"持续 1 回合。" #: lang/json/martial_art_from_json.py msgid "Blood In The Water" -msgstr "" +msgstr "嗜血" #. ~ Description of buff 'Blood In The Water' for martial art '{'str': 'Tiger #. Claw'}' @@ -115382,10 +115585,14 @@ msgid "" "+1 Accuracy, +15% damage.\n" "Lasts 1 turn. Stacks 2 times." msgstr "" +"血腥的气味驱使着你狂怒地攻击。你想要更多。现在!\n" +"\n" +" 命中 +1,+15% 伤害。\n" +"持续 1 回合。可叠加 2 次。" #: lang/json/martial_art_from_json.py msgid "Prey on the Weak" -msgstr "" +msgstr "弱肉强食" #. ~ Description of buff 'Prey on the Weak' for martial art '{'str': 'Tiger #. Claw'}' @@ -115396,6 +115603,10 @@ msgid "" "+30 Speed.\n" "Lasts 2 turns. Stacks 2 times" msgstr "" +"你像扫过羊群的狼一样收割弱者的生命。\n" +"\n" +"+30 速度。\n" +"持续 2 回合。可叠加 2 次。" #: lang/json/material_from_json.py src/bionics.cpp msgid "Alcohol" @@ -115881,7 +116092,7 @@ msgstr "乳化水凝胶" #: lang/json/material_from_json.py msgid "pulped" -msgstr "" +msgstr "碎浆的" #: lang/json/material_from_json.py msgid "Arcane Skin" @@ -117883,7 +118094,7 @@ msgstr "黛西" #: lang/json/mission_def_from_json.py msgid "Find 10 3L jars" -msgstr "收集 20 个3L玻璃罐" +msgstr "收集 10 个3L玻璃罐" #: lang/json/mission_def_from_json.py msgid "I could use some help scavenging." @@ -117893,7 +118104,7 @@ msgstr "我需要有人帮我找点东西。" msgid "" "We could use some 3 liter jars to preserve our produce. Can you bring me 10" " large three liter jars? I'll give you some preserves in exchange." -msgstr "" +msgstr "我们需要些 3L 玻璃罐来腌制我们的作物。你能帮我找 10 个吗?作为谢礼我会给你一些腌菜。" #: lang/json/mission_def_from_json.py msgid "Thank you. It's important to preserve foods while we can." @@ -119669,84 +119880,84 @@ msgstr "好吧,我只能自己去找金子了,算我自讨没趣。" #: lang/json/mission_def_from_json.py msgid "Active Noise Control" -msgstr "" +msgstr "激活降噪程序" #. ~ Description for mission 'Active Noise Control' #: lang/json/mission_def_from_json.py msgid "" "Investigate Hub 01's radio tower, discover the source of the interference, " "and fix the problem." -msgstr "" +msgstr "调查Hub 01附近的无线电塔,寻找干扰源,并解决问题。" #: lang/json/mission_def_from_json.py msgid "" "A few days ago, I installed a radio transmitter in the nearby tower, but it " "stopped working recently. If you are willing to be my back up while I check" " it out, I'll owe you a favor." -msgstr "" +msgstr "几天前,我在附近无线电塔里安装了一台无线电发射机,但它最近停止工作了。如果你愿意在我检查时帮我一把,我会欠你一个大人情的。" #: lang/json/mission_def_from_json.py msgid "Alright, lets be off. You don't mind taking point, right?" -msgstr "" +msgstr "好吧,我们走吧。你不介意我去侦察一下,对吧?" #: lang/json/mission_def_from_json.py msgid "Well thanks for offering, I guess." -msgstr "" +msgstr "谢谢你的提议,我想还是算了吧。" #: lang/json/mission_def_from_json.py msgid "" "I'm sure we'll figure it out once there. In any case, make sure to shoot " "first." -msgstr "" +msgstr "我相信我们一到那里就会想出办法的。无论如何,一定要先开枪。" #: lang/json/mission_def_from_json.py msgid "You think we killed the culprit?" -msgstr "" +msgstr "你觉得我们解决问题了吗?" #: lang/json/mission_def_from_json.py msgid "Sure seems like it. Lets go back to the hub." -msgstr "" +msgstr "看起来问题解决了。我们快点回HUB吧。" #: lang/json/mission_def_from_json.py msgid "Sure, thanks for nothing." -msgstr "" +msgstr "好吧,算我自讨没趣。" #: lang/json/mission_def_from_json.py msgid "Return to Hub 01" -msgstr "" +msgstr "返回 HUB 01" #. ~ Description for mission 'Return to Hub 01' #: lang/json/mission_def_from_json.py msgid "Return to Hub 01." -msgstr "" +msgstr "返回 HUB 01。" #: lang/json/mission_def_from_json.py msgid "Lets go back to the Hub" -msgstr "" +msgstr "我们快点回HUB吧。" #: lang/json/mission_def_from_json.py msgid "Well…" -msgstr "" +msgstr "呃……" #: lang/json/mission_def_from_json.py msgid "You keep a map around don't you? I do, and you probably should too." -msgstr "" +msgstr "你随身不带地图的吗?我可会带,你或许也应该这么做。" #: lang/json/mission_def_from_json.py msgid "We there yet?" -msgstr "" +msgstr "我们到了吗?" #: lang/json/mission_def_from_json.py msgid "Thanks for having my back. As I said, I owe you one." -msgstr "" +msgstr "谢谢你帮我。就像我之前说的,我欠你一个人情。" #: lang/json/mission_def_from_json.py msgid "Are you lost or something?" -msgstr "" +msgstr "你迷路了还是怎么了?" #: lang/json/mission_def_from_json.py msgid "Can't believe we got lost…" -msgstr "" +msgstr "不敢相信我们迷路了……" #: lang/json/mission_def_from_json.py msgid "Make 2 Stills" @@ -123418,7 +123629,7 @@ msgstr "你的头上长出了一个发光器。你不能自主控制它,它可 #: lang/json/mutation_from_json.py msgid "Reflex Photophore (on)" -msgstr "" +msgstr "反射式发光器(开)" #: lang/json/mutation_from_json.py msgid "Weak Photophore" @@ -123434,12 +123645,12 @@ msgstr "从你的额头上长出一个发光器官,你可以使它发出微光 #: lang/json/mutation_from_json.py msgid "Weak Photophore (on)" -msgstr "" +msgstr "弱发光器(开)" #. ~ Description for {'str': 'Weak Photophore (on)'} #: lang/json/mutation_from_json.py msgid "Your photophore is glowing softly." -msgstr "" +msgstr "你额头上的发光器官正在发出柔弱的微光。" #: lang/json/mutation_from_json.py msgid "Photophore" @@ -123448,16 +123659,16 @@ msgstr "发光器" #. ~ Description for {'str': 'Photophore'} #: lang/json/mutation_from_json.py msgid "You can make your photophore glow brightly." -msgstr "" +msgstr "你额头上的发光器官现在能发出强光。" #: lang/json/mutation_from_json.py msgid "Photophore (on)" -msgstr "" +msgstr "发光器(开)" #. ~ Description for {'str': 'Photophore (on)'} #: lang/json/mutation_from_json.py msgid "You photophore is glowing brightly." -msgstr "" +msgstr "你额头上的发光器官正在发出强光。" #: lang/json/mutation_from_json.py msgid "Normal Human" @@ -123575,7 +123786,7 @@ msgid "" "Your wounds heal themselves quicker than usual. You heal 50% faster whilst " "asleep and 20% faster whilst awake. Your broken limbs also heal twice as " "fast." -msgstr "" +msgstr "你的伤口比平时愈合得更快。伤口在睡眠时恢复速度提升50%,在清醒时提升20%。你折断的四肢愈合速度也是正常人的两倍。" #: lang/json/mutation_from_json.py msgid "Light Eater" @@ -124206,7 +124417,7 @@ msgstr "慢速自愈" msgid "" "Your wounds heal a little slower than most. Your HP whilst asleep as well " "as your broken limbs heal at 75% the regular rate." -msgstr "" +msgstr "你的伤口愈合得比大多数人都慢。伤口在睡眠时恢复速度以及折断的四肢的愈合速度是正常人的75%。" #: lang/json/mutation_from_json.py msgid "Poor Healer" @@ -124218,7 +124429,7 @@ msgstr "自愈弱化" msgid "" "Your health recovery is severely impaired. Your HP whilst asleep as well as" " your broken limbs heal at 33% the regular rate." -msgstr "" +msgstr "你的机体自愈能力严重受损。伤口在睡眠时恢复速度以及折断的四肢的愈合速度是正常人的33%。" #: lang/json/mutation_from_json.py msgid "Imperceptive Healer" @@ -124230,7 +124441,7 @@ msgstr "自愈无效" msgid "" "Wounds are incredibly dangerous to you, as they barely heal at all. Your HP" " whilst asleep as well as your broken limbs heal at 10% the regular rate." -msgstr "" +msgstr "伤口对你来说是非常危险的,因为它们几乎无法愈合。伤口在睡眠时恢复速度以及折断的四肢的愈合速度是正常人的10%。" #: lang/json/mutation_from_json.py msgid "Far-Sighted" @@ -124946,7 +125157,7 @@ msgstr "高速自愈" msgid "" "Your wounds heal very quickly. You heal 50% faster whilst asleep and 66% " "faster whilst awake. Your broken limbs also heal 4 times faster than usual." -msgstr "" +msgstr "你的伤口很快就愈合了。伤口在睡眠时恢复速度提升50%,在清醒时提升66%。你折断的四肢愈合速度也是正常人的4倍。" #: lang/json/mutation_from_json.py msgid "Regeneration" @@ -124959,7 +125170,7 @@ msgid "" "Your flesh regenerates from wounds incredibly quickly. You heal 150% faster" " whilst asleep and 200% faster whilst awake. Your broken limbs also heal 16" " times faster than usual." -msgstr "" +msgstr "你的肉体受伤后愈合得非常快。伤口在睡眠时恢复速度提升150%,在清醒时提升200%。你折断的四肢愈合速度也是正常人的16倍。" #: lang/json/mutation_from_json.py msgid "Reptilian Healing" @@ -124970,7 +125181,7 @@ msgstr "蜥蜴再生" msgid "" "Your broken limbs mend themselves without significant difficulty. You do " "not require splints and broken limbs heal 20 times faster than usual." -msgstr "" +msgstr "你断掉的四肢可以轻易的自愈。你折断的四肢不需要夹板就能愈合,而且愈合速度是正常人的20倍。" #: lang/json/mutation_from_json.py msgid "Very Little Sleep" @@ -129072,7 +129283,7 @@ msgstr "快速反应" #: lang/json/mutation_from_json.py msgid "" "You have fast reflexes, allowing you to dodge attacks and grabs more easily." -msgstr "" +msgstr "你反应很快,让你更容易闪避敌人的攻击和擒抱。" #: lang/json/mutation_from_json.py msgid "Survivor Story" @@ -130332,7 +130543,7 @@ msgid "" "You are a martial adept and learned one of the martial disciplines of the " "Sublime Way. You start with your choice of Desert Wind, Diamond Mind, Iron " "Heart, Setting Sun, Stone Dragon, or Tiger Claw." -msgstr "" +msgstr "你是一名武道高手,学会了至高之道的武术流派中的一种。你能够从漠风,钢魂,铁心,暮日,石龙,及虎爪流派中选择一种开始游戏。" #: lang/json/mutation_from_json.py msgid "Magus" @@ -139089,7 +139300,7 @@ msgstr "你受够了看到东西从天上掉下来,把士兵和幸存者从一 #: lang/json/professions_from_json.py msgctxt "profession_male" msgid "EMT" -msgstr "" +msgstr "急救员" #. ~ Profession (male EMT) description #: lang/json/professions_from_json.py @@ -139098,12 +139309,12 @@ msgid "" "You were responding to a call with your partner before you got separated. " "Now all you have is your trusty ambulance, ready to transport patients " "through the apocalypse." -msgstr "" +msgstr "你和你的搭档在出急救现场时被分开了。现在你只有你那辆可靠的救护车陪着你,准备好把病人运过大灾变。" #: lang/json/professions_from_json.py msgctxt "profession_female" msgid "EMT" -msgstr "" +msgstr "急救员" #. ~ Profession (female EMT) description #: lang/json/professions_from_json.py @@ -139112,12 +139323,12 @@ msgid "" "You were responding to a call with your partner before you got separated. " "Now all you have is your trusty ambulance, ready to transport patients " "through the apocalypse." -msgstr "" +msgstr "你和你的搭档在出急救现场时被分开了。现在你只有你那辆可靠的救护车陪着你,准备好把病人运过大灾变。" #: lang/json/professions_from_json.py msgctxt "profession_male" msgid "Paramedic" -msgstr "" +msgstr "急救护士" #. ~ Profession (male Paramedic) description #: lang/json/professions_from_json.py @@ -139126,12 +139337,12 @@ msgid "" "You were separated from your partner while out on a call. You managed to " "hang onto some medical supplies, but it's looking like the only life that " "needs saving now is yours." -msgstr "" +msgstr "你和你的搭档在出急救现场时被分开了。你设法保存了一些医疗用品,但看起来现在唯一需要拯救的是你的生命。" #: lang/json/professions_from_json.py msgctxt "profession_female" msgid "Paramedic" -msgstr "" +msgstr "急救护士" #. ~ Profession (female Paramedic) description #: lang/json/professions_from_json.py @@ -139140,12 +139351,12 @@ msgid "" "You were separated from your partner while out on a call. You managed to " "hang onto some medical supplies, but it's looking like the only life that " "needs saving now is yours." -msgstr "" +msgstr "你和你的搭档在出急救现场时被分开了。你设法保存了一些医疗用品,但看起来现在唯一需要拯救的是你的生命。" #: lang/json/professions_from_json.py msgctxt "profession_male" msgid "Combat Medic" -msgstr "" +msgstr "战地医生" #. ~ Profession (male Combat Medic) description #: lang/json/professions_from_json.py @@ -139154,12 +139365,12 @@ msgid "" "You were on the front-lines when everything happened, patching up the " "wounded and providing support. But they wouldn't stop coming. Now you're " "on your own." -msgstr "" +msgstr "在这一切发生时你正好在前线,为受伤的人修补伤口并提供支援。但它们源源不断向你涌来。现在你只能靠自己了。" #: lang/json/professions_from_json.py msgctxt "profession_female" msgid "Combat Medic" -msgstr "" +msgstr "战地医生" #. ~ Profession (female Combat Medic) description #: lang/json/professions_from_json.py @@ -139168,12 +139379,12 @@ msgid "" "You were on the front-lines when everything happened, patching up the " "wounded and providing support. But they wouldn't stop coming. Now you're " "on your own." -msgstr "" +msgstr "在这一切发生时你正好在前线,为受伤的人修补伤口并提供支援。但它们源源不断向你涌来。现在你只能靠自己了。" #: lang/json/professions_from_json.py msgctxt "profession_male" msgid "Heroin Addict" -msgstr "" +msgstr "白粉仔" #. ~ Profession (male Heroin Addict) description #: lang/json/professions_from_json.py @@ -139182,12 +139393,12 @@ msgid "" "The last thing you remember was meeting God behind the local Foodplace. " "Then people started eating each other. This doesn't feel like a fever " "dream." -msgstr "" +msgstr "你所记得的最后一件事是在当地美食广场后面和上帝谈心。然后人们开始互相吃。这感觉不像是一场狂热之梦。" #: lang/json/professions_from_json.py msgctxt "profession_female" msgid "Heroin Addict" -msgstr "" +msgstr "白粉妹" #. ~ Profession (female Heroin Addict) description #: lang/json/professions_from_json.py @@ -139196,7 +139407,7 @@ msgid "" "The last thing you remember was meeting God behind the local Foodplace. " "Then people started eating each other. This doesn't feel like a fever " "dream." -msgstr "" +msgstr "你所记得的最后一件事是在当地美食广场后面和上帝谈心。然后人们开始互相吃。这感觉不像是一场狂热之梦。" #: lang/json/professions_from_json.py msgctxt "profession_male" @@ -139380,6 +139591,7 @@ msgid "" "you in the middle of your 16 hour extended shift, and you barely managed to " "escape the building, tools in hand." msgstr "" +"你是一名在一间全无菌高安防级别的设施中制造生化插件的技术工程师。你在长达16个小时的轮班之中收到了那条最后的疏散令,而你靠手里的工具勉强逃出了那栋大楼。" #: lang/json/professions_from_json.py msgctxt "profession_female" @@ -139395,6 +139607,7 @@ msgid "" "you in the middle of your 16 hour extended shift, and you barely managed to " "escape the building, tools in hand." msgstr "" +"你是一名在一间全无菌高安防级别的设施中制造生化插件的技术工程师。你在长达16个小时的轮班之中收到了那条最后的疏散令,而你靠手里的工具勉强逃出了那栋大楼。" #: lang/json/professions_from_json.py msgctxt "profession_male" @@ -140691,7 +140904,7 @@ msgstr "随着大灾变的到来,你可能得想办法把在考试时学会的 #: lang/json/professions_from_json.py msgctxt "profession_male" msgid "Introspectionist" -msgstr "" +msgstr "内省主义者" #. ~ Profession (male Introspectionist) description #: lang/json/professions_from_json.py @@ -140700,12 +140913,12 @@ msgid "" "You segregated yourself from society because you wanted to concentrate on " "improving yourself. It was you and your best friend, but now the apocalypse" " won't leave you alone. " -msgstr "" +msgstr "你把自己与社会隔离开来,因为你想集中精力提升自我。这本是你自己最好的朋友,但现在大灾变可不会让你一个人呆着。" #: lang/json/professions_from_json.py msgctxt "profession_female" msgid "Introspectionist" -msgstr "" +msgstr "内省主义者" #. ~ Profession (female Introspectionist) description #: lang/json/professions_from_json.py @@ -140714,12 +140927,12 @@ msgid "" "You segregated yourself from society because you wanted to concentrate on " "improving yourself. It was you and your best friend, but now the apocalypse" " won't leave you alone. " -msgstr "" +msgstr "你把自己与社会隔离开来,因为你想集中精力提升自我。这本是你自己最好的朋友,但现在大灾变可不会让你一个人呆着。" #: lang/json/professions_from_json.py msgctxt "profession_male" msgid "Vengeful Preacher" -msgstr "" +msgstr "复仇传教士" #. ~ Profession (male Vengeful Preacher) description #: lang/json/professions_from_json.py @@ -140728,12 +140941,12 @@ msgid "" "You lost your faith when your spouse died of illness. You've been drinking " "yourself to death ever since. God is punishing everyone with this " "apocalypse, you are going to show them your brand of mercy." -msgstr "" +msgstr "当你的配偶因病去世时,你失去了你的信仰。从那以后你就一直喝得烂醉如泥。上帝用大灾变惩罚每一个人,而你要向它们展示你仁慈的烙印。" #: lang/json/professions_from_json.py msgctxt "profession_female" msgid "Vengeful Preacher" -msgstr "" +msgstr "复仇传教士" #. ~ Profession (female Vengeful Preacher) description #: lang/json/professions_from_json.py @@ -140742,12 +140955,12 @@ msgid "" "You lost your faith when your spouse died of illness. You've been drinking " "yourself to death ever since. God is punishing everyone with this " "apocalypse, you are going to show them your brand of mercy." -msgstr "" +msgstr "当你的配偶因病去世时,你失去了你的信仰。从那以后你就一直喝得烂醉如泥。上帝用大灾变惩罚每一个人,而你要向它们展示你仁慈的烙印。" #: lang/json/professions_from_json.py msgctxt "profession_male" msgid "Techno-Prepper" -msgstr "" +msgstr "科技法师信徒" #. ~ Profession (male Techno-Prepper) description #: lang/json/professions_from_json.py @@ -140755,12 +140968,12 @@ msgctxt "prof_desc_male" msgid "" "You've long suspected the world might suddenly turn over. With your " "training, spells, and revolver, your chances are far better than most." -msgstr "" +msgstr "你早就怀疑世界会突然终结。有了你的训练、法术和左轮手枪,你活下去的几率比大多数人都要高得多。" #: lang/json/professions_from_json.py msgctxt "profession_female" msgid "Techno-Prepper" -msgstr "" +msgstr "科技法师信徒" #. ~ Profession (female Techno-Prepper) description #: lang/json/professions_from_json.py @@ -140768,12 +140981,12 @@ msgctxt "prof_desc_female" msgid "" "You've long suspected the world might suddenly turn over. With your " "training, spells, and revolver, your chances are far better than most." -msgstr "" +msgstr "你早就怀疑世界会突然终结。有了你的训练、法术和左轮手枪,你活下去的几率比大多数人都要高得多。" #: lang/json/professions_from_json.py msgctxt "profession_male" msgid "Bionic Pseudovamp" -msgstr "" +msgstr "生化伪吸血鬼" #. ~ Profession (male Bionic Pseudovamp) description #: lang/json/professions_from_json.py @@ -140783,11 +140996,12 @@ msgid "" "augment yourself with spells and bionics into a denizen of the night. Your " "neglect to your health in your pursuit has left you pale and unlively." msgstr "" +"你一直对恐怖小说着迷,并利用你手中的财富买来各种法术和生化插件强化自己,使你成为一个暗夜的居民。你在追求抛瓦的过程中忽视了自己的健康,结果使你面色苍白,毫无生气。" #: lang/json/professions_from_json.py msgctxt "profession_female" msgid "Bionic Pseudovamp" -msgstr "" +msgstr "生化伪吸血鬼" #. ~ Profession (female Bionic Pseudovamp) description #: lang/json/professions_from_json.py @@ -140797,11 +141011,12 @@ msgid "" "augment yourself with spells and bionics into a denizen of the night. Your " "neglect to your health in your pursuit has left you pale and unlively." msgstr "" +"你一直对恐怖小说着迷,并利用你手中的财富买来各种法术和生化插件强化自己,使你成为一个暗夜的居民。你在追求抛瓦的过程中忽视了自己的健康,结果使你面色苍白,毫无生气。" #: lang/json/professions_from_json.py msgctxt "profession_male" msgid "Academy Wizard" -msgstr "" +msgstr "魔法师研究生" #. ~ Profession (male Academy Wizard) description #: lang/json/professions_from_json.py @@ -140810,12 +141025,12 @@ msgid "" "A year of enrollment in a wizard's academy has taught you patience, wisdom, " "and a handful of useful spells. With the teachers converted into the undead " "and classes cancelled, the final lesson has begun." -msgstr "" +msgstr "在魔法学院的一年里,你学会了耐心、智慧和一些有用的法术。随着教师转变成不死人,课程渐渐取消,最后一课已经开始。" #: lang/json/professions_from_json.py msgctxt "profession_female" msgid "Academy Wizard" -msgstr "" +msgstr "魔法师研究生" #. ~ Profession (female Academy Wizard) description #: lang/json/professions_from_json.py @@ -140824,12 +141039,12 @@ msgid "" "A year of enrollment in a wizard's academy has taught you patience, wisdom, " "and a handful of useful spells. With the teachers converted into the undead " "and classes cancelled, the final lesson has begun." -msgstr "" +msgstr "在魔法学院的一年里,你学会了耐心、智慧和一些有用的法术。随着教师转变成不死人,课程渐渐取消,最后一课已经开始。" #: lang/json/professions_from_json.py msgctxt "profession_male" msgid "Corrosive Rocker" -msgstr "" +msgstr "腐蚀摇滚乐手" #. ~ Profession (male Corrosive Rocker) description #: lang/json/professions_from_json.py @@ -140837,12 +141052,12 @@ msgctxt "prof_desc_male" msgid "" "Your metal career soared to new heights when you swapped special effects for" " acrid gore and spiked whips. It seems the Cataclysm is now your final tour." -msgstr "" +msgstr "当你把特效换成辛辣刺鼻的血肉和带刺的长鞭时,你的金属摇滚职业生涯飙升到了一个新的高度。看来大灾变现在是你的最后一次巡演了。" #: lang/json/professions_from_json.py msgctxt "profession_female" msgid "Corrosive Rocker" -msgstr "" +msgstr "腐蚀摇滚乐手" #. ~ Profession (female Corrosive Rocker) description #: lang/json/professions_from_json.py @@ -140850,12 +141065,12 @@ msgctxt "prof_desc_female" msgid "" "Your metal career soared to new heights when you swapped special effects for" " acrid gore and spiked whips. It seems the Cataclysm is now your final tour." -msgstr "" +msgstr "当你把特效换成辛辣刺鼻的血肉和带刺的长鞭时,你的金属摇滚职业生涯飙升到了一个新的高度。看来大灾变现在是你的最后一次巡演了。" #: lang/json/professions_from_json.py msgctxt "profession_male" msgid "Shock Officer" -msgstr "" +msgstr "电击警官" #. ~ Profession (male Shock Officer) description #: lang/json/professions_from_json.py @@ -140864,12 +141079,12 @@ msgid "" "You were enrolled in a experimental law enforcement program designed to " "decrease suspect casualties and equipment costs by substituting tasers and " "bullets for less-lethal Stormshaping." -msgstr "" +msgstr "你是一项实验性执法部门计划的参与者,目的是通过将电击枪和子弹替换为低致命性的风暴塑造者法术来减少嫌疑犯的伤亡以及设备采购成本。" #: lang/json/professions_from_json.py msgctxt "profession_female" msgid "Shock Officer" -msgstr "" +msgstr "电击警官" #. ~ Profession (female Shock Officer) description #: lang/json/professions_from_json.py @@ -140878,12 +141093,12 @@ msgid "" "You were enrolled in a experimental law enforcement program designed to " "decrease suspect casualties and equipment costs by substituting tasers and " "bullets for less-lethal Stormshaping." -msgstr "" +msgstr "你是一项实验性执法部门计划的参与者,目的是通过将电击枪和子弹替换为低致命性的风暴塑造者法术来减少嫌疑犯的伤亡以及设备采购成本。" #: lang/json/professions_from_json.py msgctxt "profession_male" msgid "Earthquake Brawler" -msgstr "" +msgstr "塑地拳手" #. ~ Profession (male Earthquake Brawler) description #: lang/json/professions_from_json.py @@ -140892,12 +141107,12 @@ msgid "" "You've made a name for yourself in underground magic boxing rings as an " "unstoppable punching machine. Now that all your opponents are undead, " "there's no need to hold back." -msgstr "" +msgstr "你在地下魔法拳击圈中声名显赫,被称为一台挡不住的打桩机。既然你所有的对手都变成不死人了,那也没必要留手了。" #: lang/json/professions_from_json.py msgctxt "profession_female" msgid "Earthquake Brawler" -msgstr "" +msgstr "塑地拳手" #. ~ Profession (female Earthquake Brawler) description #: lang/json/professions_from_json.py @@ -140906,7 +141121,7 @@ msgid "" "You've made a name for yourself in underground magic boxing rings as an " "unstoppable punching machine. Now that all your opponents are undead, " "there's no need to hold back." -msgstr "" +msgstr "你在地下魔法拳击圈中声名显赫,被称为一台挡不住的打桩机。既然你所有的对手都变成不死人了,那也没必要留手了。" #: lang/json/professions_from_json.py msgctxt "profession_male" @@ -142575,7 +142790,7 @@ msgstr "建造金属锻造工坊" #: lang/json/recipe_from_json.py msgid "Let's build an anvil and crucible to increase our crafting options." -msgstr "" +msgstr "让我们建造一个铁砧和熔炉来增加我们的制造物品选项。" #: lang/json/recipe_from_json.py msgid "add an anvil and crucible" @@ -165320,7 +165535,7 @@ msgstr "无人区" #: lang/json/start_location_from_json.py msgid "Desert Island" -msgstr "" +msgstr "沙漠岛" #: lang/json/start_location_from_json.py msgid "Experiment Cell" @@ -178295,11 +178510,11 @@ msgstr "知道了。" #: lang/json/talk_topic_from_json.py msgid "Better keep our eyes on the road." -msgstr "" +msgstr "我们最好盯着路。" #: lang/json/talk_topic_from_json.py msgid "Better be careful around here." -msgstr "" +msgstr "这里最好小心点。" #: lang/json/talk_topic_from_json.py msgid "Yes?" @@ -178323,7 +178538,7 @@ msgstr "很高兴见到你。" #: lang/json/talk_topic_from_json.py msgid "About those jobs…" -msgstr "" +msgstr "关于那些任务……" #: lang/json/talk_topic_from_json.py msgid "Good to see you around." @@ -178335,7 +178550,7 @@ msgstr "还有什么需要吗?" #: lang/json/talk_topic_from_json.py msgid "Lets set a combat strategy" -msgstr "" +msgstr "让我们制定一个作战策略" #: lang/json/talk_topic_from_json.py msgid "" @@ -178390,7 +178605,7 @@ msgstr "你在想什么呢?" #: lang/json/talk_topic_from_json.py msgid "Want help with something?" -msgstr "" +msgstr "想要帮忙吗?" #: lang/json/talk_topic_from_json.py msgid "What do you know about our employers?" @@ -179923,7 +180138,7 @@ msgstr " 并指如矛,猛击 %s" #: lang/json/technique_from_json.py msgid "Lizard Tail" -msgstr "壁虎之尾" +msgstr "壁虎扫尾" #: lang/json/technique_from_json.py #, python-format @@ -182933,59 +183148,59 @@ msgstr "迅速地扫击了%s和附近的敌人" #: lang/json/technique_from_json.py msgid "Mountain Hammer" -msgstr "" +msgstr "巨山破" #: lang/json/technique_from_json.py #, python-format msgid "You crush %s with the weight of your Mountain Hammer" -msgstr "" +msgstr "你用一记巨山破砸向 %s" #: lang/json/technique_from_json.py #, python-format msgid " crushes %s with the weight of their Mountain Hammer" -msgstr "" +msgstr " 用一记巨山破砸向 %s" #: lang/json/technique_from_json.py msgid "Irrestistible Mountain Strike" -msgstr "" +msgstr "地动山摇" #: lang/json/technique_from_json.py #, python-format msgid "You smash down on %s with a Mountain Strike" -msgstr "" +msgstr "你用一记地动山摇将 %s 砸翻" #: lang/json/technique_from_json.py #, python-format msgid " smashes down on %s with a Mountain Strike" -msgstr "" +msgstr " 用一记地动山摇将 %s 砸翻" #: lang/json/technique_from_json.py msgid "Colossus Strike" -msgstr "" +msgstr "巨人冲击" #: lang/json/technique_from_json.py #, python-format msgid "You completely shatter %s with a Colossus Strike" -msgstr "" +msgstr "你用一记巨人冲击击碎了 %s" #: lang/json/technique_from_json.py #, python-format msgid " completely shatters %s with a Colossus Strike" -msgstr "" +msgstr " 用一记巨人冲击击碎了 %s" #: lang/json/technique_from_json.py msgid "Wolverine Stance" -msgstr "" +msgstr "狼獾势" #: lang/json/technique_from_json.py #, python-format msgid "The %s tries to grab you, but you thrash your way to freedom!" -msgstr "" +msgstr "%s 试图抓住你,但你奋力挣脱了。" #: lang/json/technique_from_json.py #, python-format msgid "The %s tries to grab , but they thrash their way to freedom!" -msgstr "" +msgstr "%s 试着抓住 ,但被奋力挣脱了!" #: lang/json/ter_furn_transform_messages_from_json.py msgid "The earth here does not listen to your command to move." @@ -186167,7 +186382,7 @@ msgstr "一台用来给奶牛挤奶的机械。" #: lang/json/terrain_from_json.py msgid "bulk tank" -msgstr "储奶罐" +msgstr "大型储罐" #. ~ Description for bulk tank #: lang/json/terrain_from_json.py @@ -190610,7 +190825,7 @@ msgstr "一块木板,可以把水挡在船外。" #. ~ Description for {'str': 'raft boat hull'} #: lang/json/vehicle_part_from_json.py msgid "Logs tied together that will keep your boat out of the water." -msgstr "" +msgstr "绑在一起的原木,可以把水挡在船外。" #. ~ Description for {'str': 'plastic boat hull'} #: lang/json/vehicle_part_from_json.py @@ -191051,7 +191266,7 @@ msgstr "一种重型牵引索。当一头连接至另一辆车后,可以拉动 #: lang/json/vehicle_part_from_json.py msgid "flimsy wooden seat" -msgstr "" +msgstr "薄木椅" #. ~ Description for {'str': 'flimsy wooden seat'} #. ~ Description for {'str': 'wooden seat'} @@ -191266,6 +191481,15 @@ msgid "" "extending the time until the food spoils." msgstr "一个60升容量的冷藏罐。开启时,冷却里面的液体,延缓腐烂。" +#. ~ Description for {'str': 'wooden wheel mount'} +#: lang/json/vehicle_part_from_json.py +msgid "A piece of wood with holes suitable for a bike or motorbike wheel." +msgstr "一种带有孔的木片,适合自行车车轮使用。" + +#: lang/json/vehicle_part_from_json.py +msgid "wooden wheel mount (steerable)" +msgstr "木制轮架(可转向)" + #: lang/json/vehicle_part_from_json.py msgid "light wheel mount (steerable)" msgstr "轻型轮架(可转向)" @@ -196951,7 +197175,7 @@ msgstr "命中" #: src/bonuses.cpp msgid "Critical Hit Chance" -msgstr "" +msgstr "暴击几率" #: src/bonuses.cpp src/martialarts.cpp msgid "Dodge" @@ -197878,7 +198102,7 @@ msgstr "精疲力尽" #: src/character.cpp msgid "Dead Tired" -msgstr "精疲力尽" +msgstr "精疲力竭" #: src/character.cpp msgid "Pain " @@ -203311,7 +203535,7 @@ msgstr "害虫" #: src/faction.cpp msgctxt "Faction respect" msgid "Despicable" -msgstr "卑劣" +msgstr "卑劣不堪" #: src/faction.cpp msgctxt "Faction respect" @@ -203321,7 +203545,7 @@ msgstr "寄生虫" #: src/faction.cpp msgctxt "Faction respect" msgid "Leech" -msgstr "水蛭" +msgstr "吸血鬼" #: src/faction.cpp msgctxt "Faction respect" @@ -203356,7 +203580,7 @@ msgstr "丰衣足食" #: src/faction.cpp msgctxt "Faction wealth" msgid "Comfortable" -msgstr "舒适" +msgstr "小康之家" #: src/faction.cpp msgctxt "Faction wealth" @@ -203406,22 +203630,22 @@ msgstr "饥肠辘辘" #: src/faction.cpp msgctxt "Faction combat lvl" msgid "Legendary" -msgstr "传奇" +msgstr "举世无双" #: src/faction.cpp msgctxt "Faction combat lvl" msgid "Expert" -msgstr "所向无敌" +msgstr "所向披靡" #: src/faction.cpp msgctxt "Faction combat lvl" msgid "Veteran" -msgstr "兵强将勇" +msgstr "精兵强将" #: src/faction.cpp msgctxt "Faction combat lvl" msgid "Skilled" -msgstr "兵强马壮" +msgstr "人强马壮" #: src/faction.cpp msgctxt "Faction combat lvl" @@ -205679,7 +205903,7 @@ msgstr "开启" #: src/game.cpp msgctxt "action" msgid "pocket autopickup settings" -msgstr "" +msgstr "口袋自动拾取设置" #: src/game.cpp msgctxt "action" @@ -207208,7 +207432,7 @@ msgstr "%s挡住了你!" #: src/game.cpp #, c-format msgid "%s Attempt to push past? You may have to fight your way back up." -msgstr "" +msgstr "%s 真的要挤过去吗?你可能要杀出一条血路才能回来。" #: src/game.cpp msgid "" @@ -211046,7 +211270,7 @@ msgstr "断肢夹板" #: src/iexamine.cpp msgid "Treat wounds" -msgstr "" +msgstr "治疗伤势" #: src/iexamine.cpp msgid "You don't have any bionics installed." @@ -211076,37 +211300,37 @@ msgstr " 的四肢完好,不需要夹板。" #: src/iexamine.cpp msgid "You don't have any wounds that need treatment." -msgstr "" +msgstr "你没有任何需要治疗的伤口。" #: src/iexamine.cpp msgid " doesn't have any wounds that need treatment." -msgstr "" +msgstr " 没有任何需要治疗的伤口。" #: src/iexamine.cpp msgid "" "The autodoc detected a bacterial infection in your body, but as it also " "detected you've already taken antibiotics, it decided not to apply another " "dose right now." -msgstr "" +msgstr "全自动医疗仪检测到了你体内的细菌感染,但是由于它也检测到你已经服用了抗生素,所以它决定现在不再使用第二份抗生素。" #: src/iexamine.cpp msgid "" "The autodoc detected a bacterial infection in 's body, but as it " "also detected you've already taken antibiotics, it decided not to apply " "another dose right now." -msgstr "" +msgstr "全自动医疗仪检测到了 体内的细菌感染,但是由于它也检测到已经服用了抗生素,所以它决定现在不再使用第二份抗生素。" #: src/iexamine.cpp msgid "" "The autodoc detected a bacterial infection in your body and injected " "antibiotics to treat it." -msgstr "" +msgstr "全自动医疗仪检测到了你体内的细菌感染,并注射抗生素来治疗它。" #: src/iexamine.cpp msgid "" "The autodoc detected a bacterial infection in 's body and injected " "antibiotics to treat it." -msgstr "" +msgstr "全自动医疗仪检测到了 体内的细菌感染,并注射抗生素来治疗它。" #: src/iexamine.cpp src/iuse.cpp msgid "The muscle spasms start to go away." @@ -211121,28 +211345,28 @@ msgstr "药物无法缓解当前的痉挛症状。" msgid "" "The autodoc detected a bleeding on your %s and applied a hemostatic drug to " "stop it." -msgstr "" +msgstr "全自动医疗仪检测到了你的 %s 出血,并注射止血药物来阻止流血。" #: src/iexamine.cpp #, c-format msgid "" "The autodoc detected a bleeding on 's %s and applied a hemostatic " "drug to stop it." -msgstr "" +msgstr "全自动医疗仪检测到了 的 %s 出血,并注射止血药物来阻止流血。" #: src/iexamine.cpp #, c-format msgid "" "The autodoc detected an open wound on your %s and applied a disinfectant to " "clean it." -msgstr "" +msgstr "全自动医疗仪检测到了你的 %s 的开放性伤口,并使用消毒剂来清洗它。" #: src/iexamine.cpp #, c-format msgid "" "The autodoc detected an open wound on 's %s and applied a " "disinfectant to clean it." -msgstr "" +msgstr "全自动医疗仪检测到了 的 %s 的开放性伤口,并使用消毒剂来清洗它。" #: src/iexamine.cpp #, c-format @@ -213253,31 +213477,31 @@ msgstr "* 这件工具使用 生化能量。" #: src/item.cpp msgid "It's new, and ready to burn." -msgstr "" +msgstr "它是全新的,可以拿来烧了。" #: src/item.cpp msgid "Almost new, with much material to burn." -msgstr "" +msgstr "它几乎是全新的,还有很多材料可以烧。" #: src/item.cpp msgid "More than a quarter has burned away." -msgstr "" +msgstr "它已经烧掉了约四分之一。" #: src/item.cpp msgid "More than half has burned away." -msgstr "" +msgstr "它已经烧掉了约一半。" #: src/item.cpp msgid "Less than a quarter left to burn." -msgstr "" +msgstr "它已经烧掉了约四分之三。" #: src/item.cpp msgid "Almost completely burned out." -msgstr "" +msgstr "它几乎快烧光了。" #: src/item.cpp msgid "Fuel: " -msgstr "" +msgstr "燃料:" #: src/item.cpp #, c-format @@ -213306,7 +213530,7 @@ msgstr "* 这件物品 无法修理。" #: src/item.cpp #, c-format msgid "Disassembly takes %1$s and might yield: %2$s." -msgstr "" +msgstr "拆解 需要 %1$s 可获得:%2$s。" #. ~ 1 is approx. time, 2 is a list of items and tools with qualities, 3 is a #. list of items. @@ -213317,7 +213541,7 @@ msgstr "" msgid "" "Disassembly takes %1$s, requires %2$s and might " "yield: %3$s." -msgstr "" +msgstr "拆解 需要 %1$s 及 %2$s 可获得:%3$s" #: src/item.cpp #, c-format @@ -214156,52 +214380,52 @@ msgstr "执行哪个行动?" #: src/item_contents.cpp #, c-format msgid "Press a key to add to %s" -msgstr "" +msgstr "按键添加至%s" #: src/item_contents.cpp msgid "blacklist" -msgstr "" +msgstr "黑名单" #: src/item_contents.cpp msgid "whitelist" -msgstr "" +msgstr "白名单" #: src/item_contents.cpp msgid " priority, " -msgstr "" +msgstr "优先级," #: src/item_contents.cpp msgid " item, " -msgstr "" +msgstr "物品," #: src/item_contents.cpp msgid " category, " -msgstr "" +msgstr "类别," #: src/item_contents.cpp msgid " whitelist, " -msgstr "" +msgstr "白名单," #: src/item_contents.cpp msgid " blacklist" -msgstr "" +msgstr "黑名单" #: src/item_contents.cpp #, c-format msgid "Enter Priority (current priority %d)" -msgstr "" +msgstr "输入优先级(当前优先级 %d)" #: src/item_contents.cpp msgid "item id" -msgstr "" +msgstr "物品ID" #: src/item_contents.cpp msgid "item category" -msgstr "" +msgstr "物品类别" #: src/item_contents.cpp msgid "Select an item from nearby" -msgstr "" +msgstr "选择附近的物品" #: src/item_contents.cpp msgid "is not a container" @@ -214432,31 +214656,31 @@ msgstr "可用容量不足" #: src/item_pocket.cpp msgid "Priority:" -msgstr "" +msgstr "优先级:" #: src/item_pocket.cpp #, c-format msgid "Item Whitelist: %s" -msgstr "" +msgstr "物品白名单:%s" #: src/item_pocket.cpp msgid "(empty)" -msgstr "" +msgstr "(空)" #: src/item_pocket.cpp #, c-format msgid "Item Blacklist: %s" -msgstr "" +msgstr "物品黑名单:%s" #: src/item_pocket.cpp #, c-format msgid "Category Whitelist: %s" -msgstr "" +msgstr "类别白名单:%s" #: src/item_pocket.cpp #, c-format msgid "Category Blacklist: %s" -msgstr "" +msgstr "类别黑名单:%s" #: src/itype.h msgid "click." @@ -230355,11 +230579,11 @@ msgstr "捣碎附近" #: src/options.cpp msgid "Pulp Adjacent Zombie Only" -msgstr "" +msgstr "捣碎附近丧尸" #: src/options.cpp msgid "Pulp Zombies Only" -msgstr "" +msgstr "捣碎丧尸" #: src/options.cpp msgid "Auto mining" @@ -235702,7 +235926,7 @@ msgstr[0] "%1$d 个 %2$s 功能至少 %3$d 级的工具。" #, c-format msgid "%1$d tool with %2$s of %3$d or more" msgid_plural "%1$d tools with %2$s of %3$d or more" -msgstr[0] "" +msgstr[0] "%1$d 个 %2$s 功能至少 %3$d 级 的工具" #. ~ %1$s: tool name, %2$d: charge requirement #: src/requirements.cpp @@ -239750,7 +239974,7 @@ msgstr "--无有效模组--" #: src/worldfactory.cpp msgid "--NO RESULTS FOUND--" -msgstr "" +msgstr "--未找到结果--" #: src/worldfactory.cpp msgid "Saved list of active mods as default" diff --git a/lang/po/zh_TW.po b/lang/po/zh_TW.po index 1f7ce57770883..46e27a6cc215d 100644 --- a/lang/po/zh_TW.po +++ b/lang/po/zh_TW.po @@ -29,7 +29,7 @@ msgid "" msgstr "" "Project-Id-Version: cataclysm-dda 0.E\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-06-23 10:06+0800\n" +"POT-Creation-Date: 2020-06-26 12:50+0800\n" "PO-Revision-Date: 2018-04-26 14:47+0000\n" "Last-Translator: Yangerine Yuan , 2020\n" "Language-Team: Chinese (Taiwan) (https://www.transifex.com/cataclysm-dda-translators/teams/2217/zh_TW/)\n" @@ -45048,6 +45048,32 @@ msgid "" "Felt patches, bundled tightly together for storage. Disassemble to unpack." msgstr "將大量的毛氈補丁緊緊地捆成一束, 以便於存放。可拆解回原來的大量毛氈補丁。" +#: lang/json/GENERIC_from_json.py +msgid "bundle of planks" +msgid_plural "bundles of planks" +msgstr[0] "" + +#. ~ Description for {'str': 'bundle of planks', 'str_pl': 'bundles of +#. planks'} +#: lang/json/GENERIC_from_json.py +msgid "" +"Ten construction planks securely lashed together with a rope. Disassemble " +"to unpack." +msgstr "" + +#: lang/json/GENERIC_from_json.py +msgid "bundle of stout branches" +msgid_plural "bundles of stout branches" +msgstr[0] "" + +#. ~ Description for {'str': 'bundle of stout branches', 'str_pl': 'bundles of +#. stout branches'} +#: lang/json/GENERIC_from_json.py +msgid "" +"Ten stout branches securely lashed together with a rope. Disassemble to " +"untie them." +msgstr "" + #: lang/json/GENERIC_from_json.py msgid "t-substrate sample" msgid_plural "t-substrate samples" @@ -55476,6 +55502,16 @@ msgstr[0] "水龍頭" msgid "A metal faucet that can be attached to a water tank for easy access." msgstr "一個金屬的水龍頭, 可以連接到水箱上以便取水。" +#: lang/json/GENERIC_from_json.py lang/json/vehicle_part_from_json.py +msgid "wooden wheel mount" +msgid_plural "wooden wheel mounts" +msgstr[0] "" + +#. ~ Description for {'str': 'wooden wheel mount'} +#: lang/json/GENERIC_from_json.py +msgid "A piece of wood with holes suitable for a bike wheel." +msgstr "" + #: lang/json/GENERIC_from_json.py lang/json/vehicle_part_from_json.py msgid "light wheel mount" msgid_plural "light wheel mounts" @@ -98794,8 +98830,8 @@ msgid "" msgstr "" #: lang/json/gun_from_json.py -msgid "MAS 223" -msgid_plural "MAS 223" +msgid "MAS .223" +msgid_plural "MAS .223" msgstr[0] "" #: lang/json/gun_from_json.py @@ -105362,7 +105398,7 @@ msgid "" "In addition to the primary crafting skills, other skills may be necessary to" " create certain items. Traps, Marksmanship, and First Aid are all required " "for certain items." -msgstr "" +msgstr "除了主要的製作相關技能, 其他技能在製作特定物品時也可能用到。陷阱技能、射擊技能、以及急救技能, 都在製作特定物品時會用到。" #: lang/json/help_from_json.py msgid "" @@ -107418,7 +107454,7 @@ msgstr "" msgid "" "If your vehicle consists of a single tile, this wheel is enough to allow it " "to move." -msgstr "" +msgstr "如果你的載具只有一格,這個輪子就足夠讓它移動了。" #. ~ Please leave anything in unchanged. #: lang/json/json_flag_from_json.py @@ -116974,7 +117010,7 @@ msgstr "" msgid "" "Lots of people used to have first aid kits in their bathrooms. I'm sure " "they can't all have been looted." -msgstr "" +msgstr "很多人會習慣在他們的浴室裡放急救箱。我確定它們不會全被拿走的。" #: lang/json/mission_def_from_json.py msgid "" @@ -118653,6 +118689,8 @@ msgid "" "prevent me from ever seeing it. I could use your help getting a few bottles" " of aspirin to start with." msgstr "" +"我現在手上只有一些繃帶和少量的急救箱… 換句話說, 我無法處理最嚴重的醫療緊急情況。我以為拾荒者會優先向我提供醫療物資, " +"但我想這方面的黑市賣家早已把物資買光。我想你首先幫我搜集幾瓶阿斯匹靈。" #: lang/json/mission_def_from_json.py msgid "Aspirin is pretty common in homes and convenience stores." @@ -181131,7 +181169,7 @@ msgstr "路面" msgid "" "A segment of asphalt, slowly degrading from cracks, frost heaves and lack of" " maintenance." -msgstr "" +msgstr "一段瀝青,正在緩慢的碎裂中,凍結的冰塊破壞了結構並且缺乏了維護。" #: lang/json/terrain_from_json.py msgid "yellow pavement" @@ -188226,6 +188264,15 @@ msgid "" "extending the time until the food spoils." msgstr "" +#. ~ Description for {'str': 'wooden wheel mount'} +#: lang/json/vehicle_part_from_json.py +msgid "A piece of wood with holes suitable for a bike or motorbike wheel." +msgstr "" + +#: lang/json/vehicle_part_from_json.py +msgid "wooden wheel mount (steerable)" +msgstr "" + #: lang/json/vehicle_part_from_json.py msgid "light wheel mount (steerable)" msgstr "輕型輪轂(轉向輪)" From 3234649478f9252beff6551d0cf731fafd028951 Mon Sep 17 00:00:00 2001 From: Kevin Granade Date: Fri, 3 Jul 2020 00:41:41 -0700 Subject: [PATCH 065/420] Global reference migrations part 14 (#41787) --- src/bionics.cpp | 2 +- src/magic_teleporter_list.cpp | 13 +- src/magic_teleporter_list.h | 4 +- src/mapgen.cpp | 2 +- src/npc.cpp | 6 +- src/npc.h | 10 +- src/npcmove.cpp | 371 +++++++++++++++++++--------------- src/overmap_ui.cpp | 72 +++---- 8 files changed, 261 insertions(+), 219 deletions(-) diff --git a/src/bionics.cpp b/src/bionics.cpp index 1f8bdd8a35aea..495641fffce94 100644 --- a/src/bionics.cpp +++ b/src/bionics.cpp @@ -506,7 +506,7 @@ void npc::check_or_use_weapon_cbm( const bionic_id &cbm_id ) if( is_armed() ) { stow_item( weapon ); } - if( g->u.sees( pos() ) ) { + if( get_player_character().sees( pos() ) ) { add_msg( m_info, _( "%s activates their %s." ), disp_name(), bio.info().name ); } diff --git a/src/magic_teleporter_list.cpp b/src/magic_teleporter_list.cpp index c50bb36e846a2..3de08f1fa71db 100644 --- a/src/magic_teleporter_list.cpp +++ b/src/magic_teleporter_list.cpp @@ -9,6 +9,7 @@ #include "bodypart.h" #include "calendar.h" #include "catacharset.h" +#include "character.h" #include "color.h" #include "coordinate_conversions.h" #include "enums.h" @@ -70,7 +71,7 @@ static cata::optional find_valid_teleporters_omt( const tripoint &omt_ return cata::nullopt; } -bool teleporter_list::place_avatar_overmap( avatar &you, const tripoint &omt_pt ) const +bool teleporter_list::place_avatar_overmap( Character &you, const tripoint &omt_pt ) const { tinymap omt_dest( 2, true ); tripoint sm_dest = omt_to_sm_copy( omt_pt ); @@ -101,9 +102,9 @@ void teleporter_list::translocate( const std::set &targets ) bool valid_targets = false; for( const tripoint &pt : targets ) { - avatar *you = g->critter_at( pt ); + Character *you = g->critter_at( pt ); - if( you ) { + if( you && you->is_avatar() ) { valid_targets = true; if( !place_avatar_overmap( *you, *omt_dest ) ) { add_msg( _( "Failed to teleport. Teleporter obstructed or destroyed." ) ); @@ -169,11 +170,13 @@ class teleporter_callback : public uilist_callback mvwputch( menu->window, point( start_x, i ), c_magenta, LINE_XOXO ); } if( entnum >= 0 && static_cast( entnum ) < index_pairs.size() ) { - overmap_ui::draw_overmap_chunk( menu->window, g->u, index_pairs[entnum], point( start_x + 1, 1 ), + avatar &player_character = get_avatar(); + overmap_ui::draw_overmap_chunk( menu->window, player_character, index_pairs[entnum], + point( start_x + 1, 1 ), 29, 21 ); mvwprintz( menu->window, point( start_x + 2, 1 ), c_white, string_format( _( "Distance: %d (%d, %d)" ), - rl_dist( ms_to_omt_copy( g->m.getabs( g->u.pos() ) ), index_pairs[entnum] ), + rl_dist( ms_to_omt_copy( get_map().getabs( player_character.pos() ) ), index_pairs[entnum] ), index_pairs[entnum].x, index_pairs[entnum].y ) ); } wnoutrefresh( menu->window ); diff --git a/src/magic_teleporter_list.h b/src/magic_teleporter_list.h index 4e925c0d37477..f06d7c8a7bd2c 100644 --- a/src/magic_teleporter_list.h +++ b/src/magic_teleporter_list.h @@ -9,7 +9,7 @@ #include "optional.h" #include "point.h" -class avatar; +class Character; class JsonIn; class JsonOut; @@ -23,7 +23,7 @@ class teleporter_list cata::optional choose_teleport_location(); // returns true if a teleport is successful // does not do any loading or unloading - bool place_avatar_overmap( avatar &you, const tripoint &omt_pt ) const; + bool place_avatar_overmap( Character &you, const tripoint &omt_pt ) const; public: bool knows_translocator( const tripoint &omt_pos ) const; // adds teleporter to known_teleporters and does any other activation necessary diff --git a/src/mapgen.cpp b/src/mapgen.cpp index 234b022c7ddf5..07ab7f9be94c5 100644 --- a/src/mapgen.cpp +++ b/src/mapgen.cpp @@ -6047,7 +6047,7 @@ void map::rotate( int turns, const bool setpos_safe ) // to be between 0-11,0-11 and teleports NPCs when used inside of update_mapgen // calls const tripoint new_global_sq = sq - local_sq + new_pos; - np.setpos( g->m.getlocal( new_global_sq ) ); + np.setpos( get_map().getlocal( new_global_sq ) ); } else { // OK, this is ugly: we remove the NPC from the whole map // Then we place it back from scratch diff --git a/src/npc.cpp b/src/npc.cpp index 573f8291453f6..87c7b04e432f7 100644 --- a/src/npc.cpp +++ b/src/npc.cpp @@ -1951,7 +1951,7 @@ bool npc::has_faction_relationship( const player &p, const npc_factions::relatio return my_fac->has_relationship( p_fac->id, flag ); } -bool npc::is_ally( const player &p ) const +bool npc::is_ally( const Character &p ) const { if( p.getID() == getID() ) { return true; @@ -1991,7 +1991,7 @@ bool npc::is_player_ally() const return is_ally( g->u ); } -bool npc::is_friendly( const player &p ) const +bool npc::is_friendly( const Character &p ) const { return is_ally( p ) || ( p.is_player() && ( is_walking_with() || is_player_ally() ) ); } @@ -2011,7 +2011,7 @@ bool npc::is_walking_with() const return attitude == NPCATT_FOLLOW || attitude == NPCATT_LEAD || attitude == NPCATT_WAIT; } -bool npc::is_obeying( const player &p ) const +bool npc::is_obeying( const Character &p ) const { return ( p.is_player() && is_walking_with() && is_player_ally() ) || ( is_ally( p ) && is_stationary( true ) ); diff --git a/src/npc.h b/src/npc.h index 599c54544de2f..831897df1d608 100644 --- a/src/npc.h +++ b/src/npc.h @@ -887,18 +887,18 @@ class npc : public player bool is_enemy() const; // Traveling w/ player (whether as a friend or a slave) bool is_following() const; - bool is_obeying( const player &p ) const; + bool is_obeying( const Character &p ) const; bool is_hallucination() const override; // true if the NPC isn't actually real // Ally of or traveling with p - bool is_friendly( const player &p ) const; + bool is_friendly( const Character &p ) const; // Leading the player bool is_leader() const; // Leading, following, or waiting for the player bool is_walking_with() const; // In the same faction - bool is_ally( const player &p ) const; + bool is_ally( const Character &p ) const; // Is an ally of the player bool is_player_ally() const; // Isn't moving @@ -1185,8 +1185,8 @@ class npc : public player void heal_player( player &patient ); void heal_self(); void pretend_heal( player &patient, item used ); // healing action of hallucinations - void mug_player( player &mark ); - void look_for_player( const player &sought ); + void mug_player( Character &mark ); + void look_for_player( const Character &sought ); // Do we have an idea of where u are? bool saw_player_recently() const; /** Returns true if food was consumed, false otherwise. */ diff --git a/src/npcmove.cpp b/src/npcmove.cpp index 8c5aaa03396f6..0e3df1a116950 100644 --- a/src/npcmove.cpp +++ b/src/npcmove.cpp @@ -13,7 +13,6 @@ #include "active_item_cache.h" #include "activity_handlers.h" -#include "avatar.h" #include "basecamp.h" #include "bionics.h" #include "bodypart.h" @@ -216,7 +215,7 @@ static bool clear_shot_reach( const tripoint &from, const tripoint &to, bool che Creature *inter = g->critter_at( p ); if( check_ally && inter != nullptr ) { return false; - } else if( g->m.impassable( p ) ) { + } else if( get_map().impassable( p ) ) { return false; } } @@ -226,6 +225,7 @@ static bool clear_shot_reach( const tripoint &from, const tripoint &to, bool che tripoint npc::good_escape_direction( bool include_pos ) { + map &here = get_map(); if( path.empty() ) { zone_type_id retreat_zone = zone_type_id( "NPC_RETREAT" ); const tripoint &abs_pos = global_square_location(); @@ -233,7 +233,7 @@ tripoint npc::good_escape_direction( bool include_pos ) cata::optional retreat_target = mgr.get_nearest( retreat_zone, abs_pos, 60, fac_id ); if( retreat_target && *retreat_target != abs_pos ) { - update_path( g->m.getlocal( *retreat_target ) ); + update_path( here.getlocal( *retreat_target ) ); if( !path.empty() ) { return path[0]; } @@ -247,7 +247,7 @@ tripoint npc::good_escape_direction( bool include_pos ) return MAX_FLOAT; } float rating = threat_val; - for( const auto &e : g->m.field_at( pt ) ) { + for( const auto &e : here.field_at( pt ) ) { if( is_dangerous_field( e.second ) ) { // TODO: Rate fire higher than smoke rating += e.second.get_field_intensity(); @@ -277,12 +277,13 @@ tripoint npc::good_escape_direction( bool include_pos ) bool npc::sees_dangerous_field( const tripoint &p ) const { - return is_dangerous_fields( g->m.field_at( p ) ); + return is_dangerous_fields( get_map().field_at( p ) ); } bool npc::could_move_onto( const tripoint &p ) const { - if( !g->m.passable( p ) ) { + map &here = get_map(); + if( !here.passable( p ) ) { return false; } @@ -290,8 +291,8 @@ bool npc::could_move_onto( const tripoint &p ) const return true; } - const auto fields_here = g->m.field_at( pos() ); - for( const auto &e : g->m.field_at( p ) ) { + const auto fields_here = here.field_at( pos() ); + for( const auto &e : here.field_at( p ) ) { if( !is_dangerous_field( e.second ) ) { continue; } @@ -309,7 +310,7 @@ std::vector npc::find_dangerous_explosives() const { std::vector result; - const auto active_items = g->m.get_active_items_in_radius( pos(), MAX_VIEW_DISTANCE, + const auto active_items = get_map().get_active_items_in_radius( pos(), MAX_VIEW_DISTANCE, special_item_type::explosive ); for( const auto &elem : active_items ) { @@ -371,8 +372,9 @@ void npc::assess_danger() invalidate_range_cache(); max_range = *confident_range_cache; } - const auto ok_by_rules = [max_range, def_radius, this]( const Creature & c, int dist, - int scaled_dist ) { + Character &player_character = get_player_character(); + const auto ok_by_rules = [max_range, def_radius, this, &player_character]( const Creature & c, + int dist, int scaled_dist ) { // If we're forbidden to attack, no need to check engagement rules if( rules.has_flag( ally_rule::forbid_engage ) ) { return false; @@ -383,7 +385,7 @@ void npc::assess_danger() case combat_engagement::CLOSE: // Either close to player or close enough that we can reach it and close to us return ( dist <= max_range && scaled_dist <= def_radius * 0.5 ) || - too_close( c.pos(), g->u.pos(), def_radius ); + too_close( c.pos(), player_character.pos(), def_radius ); case combat_engagement::WEAK: return c.get_hp() <= average_damage_dealt(); case combat_engagement::HIT: @@ -403,13 +405,14 @@ void npc::assess_danger() for( direction threat_dir : npc_threat_dir ) { cur_threat_map[ threat_dir ] = 0.25f * ai_cache.threat_map[ threat_dir ]; } + map &here = get_map(); // first, check if we're about to be consumed by fire // TODO: Use the field cache - for( const tripoint &pt : g->m.points_in_radius( pos(), 6 ) ) { - if( pt == pos() || g->m.has_flag( TFLAG_FIRE_CONTAINER, pt ) ) { + for( const tripoint &pt : here.points_in_radius( pos(), 6 ) ) { + if( pt == pos() || here.has_flag( TFLAG_FIRE_CONTAINER, pt ) ) { continue; } - if( g->m.get_field( pt, fd_fire ) != nullptr ) { + if( here.get_field( pt, fd_fire ) != nullptr ) { int dist = rl_dist( pos(), pt ); cur_threat_map[direction_from( pos(), pt )] += 2.0f * ( NPC_DANGER_MAX - dist ); if( dist < 3 && !has_effect( effect_npc_fire_bad ) ) { @@ -435,11 +438,11 @@ void npc::assess_danger() hostile_guys.emplace_back( g->shared_from( guy ) ); } } - if( sees( g->u.pos() ) ) { + if( sees( player_character.pos() ) ) { if( is_enemy() ) { - hostile_guys.emplace_back( g->shared_from( g->u ) ); - } else if( is_friendly( g->u ) ) { - ai_cache.friends.emplace_back( g->shared_from( g->u ) ); + hostile_guys.emplace_back( g->shared_from( player_character ) ); + } else if( is_friendly( player_character ) ) { + ai_cache.friends.emplace_back( g->shared_from( player_character ) ); } } @@ -511,7 +514,7 @@ void npc::assess_danger() ai_cache.danger_assessment = assessment; return; } - const auto handle_hostile = [&]( const player & foe, float foe_threat, + const auto handle_hostile = [&]( const Character & foe, float foe_threat, const std::string & bogey, const std::string & warning ) { int dist = rl_dist( pos(), foe.pos() ); if( foe_threat > ( 8.0f + personality.bravery + rng( 0, 5 ) ) ) { @@ -564,16 +567,16 @@ void npc::assess_danger() assessment = std::max( min_danger, assessment - guy_threat * 0.5f ); } - if( sees( g->u.pos() ) ) { + if( sees( player_character.pos() ) ) { // Mod for the player // cap player difficulty at 150 - float player_diff = evaluate_enemy( g->u ); + float player_diff = evaluate_enemy( player_character ); if( is_enemy() ) { - assessment += handle_hostile( g->u, player_diff, "maniac", "kill_player" ); - } else if( is_friendly( g->u ) ) { + assessment += handle_hostile( player_character, player_diff, "maniac", "kill_player" ); + } else if( is_friendly( player_character ) ) { float min_danger = assessment >= NPC_DANGER_VERY_LOW ? NPC_DANGER_VERY_LOW : -10.0f; assessment = std::max( min_danger, assessment - player_diff * 0.5f ); - ai_cache.friends.emplace_back( g->shared_from( g->u ) ); + ai_cache.friends.emplace_back( g->shared_from( player_character ) ); } } assessment *= 0.1f; @@ -626,11 +629,12 @@ float npc::character_danger( const Character &uc ) const void npc::regen_ai_cache() { + map &here = get_map(); auto i = std::begin( ai_cache.sound_alerts ); while( i != std::end( ai_cache.sound_alerts ) ) { - if( sees( g->m.getlocal( i->abs_pos ) ) ) { + if( sees( here.getlocal( i->abs_pos ) ) ) { // if they were responding to a call for guards because of thievery - npc *const sound_source = g->critter_at( g->m.getlocal( i->abs_pos ) ); + npc *const sound_source = g->critter_at( here.getlocal( i->abs_pos ) ); if( sound_source ) { if( my_fac == sound_source->my_fac && sound_source->known_stolen_item ) { sound_source->known_stolen_item = nullptr; @@ -663,6 +667,7 @@ void npc::regen_ai_cache() } // Non-allied NPCs with a completed mission should move to the player if( !is_player_ally() && !is_stationary( true ) ) { + Character &player_character = get_player_character(); for( auto &miss : chatbin.missions_assigned ) { if( miss->is_complete( getID() ) ) { // unless the player found an item and already told the NPC he wanted to keep it @@ -672,8 +677,8 @@ void npc::regen_ai_cache() has_effect( effect_npc_player_still_looking ) ) { continue; } - if( global_omt_location() != g->u.global_omt_location() ) { - goal = g->u.global_omt_location(); + if( global_omt_location() != player_character.global_omt_location() ) { + goal = player_character.global_omt_location(); } set_attitude( NPCATT_TALK ); break; @@ -709,8 +714,9 @@ void npc::move() name, target_name, ai_cache.danger, weapon.is_gun() ? confident_shoot_range( weapon, recoil_total() ) : weapon.reach_range( *this ) ); + Character &player_character = get_player_character(); //faction opinion determines if it should consider you hostile - if( !is_enemy() && guaranteed_hostile() && sees( g->u ) ) { + if( !is_enemy() && guaranteed_hostile() && sees( player_character ) ) { if( is_player_ally() ) { mutiny(); } @@ -747,12 +753,13 @@ void npc::move() if( is_enemy() && vehicle_danger( avoidance_vehicles_radius ) > 0 ) { // TODO: Think about how this actually needs to work, for now assume flee from player - ai_cache.target = g->shared_from( g->u ); + ai_cache.target = g->shared_from( player_character ); } + map &here = get_map(); if( !ai_cache.dangerous_explosives.empty() ) { action = npc_escape_explosion; - } else if( target == &g->u && attitude == NPCATT_FLEE_TEMP ) { + } else if( target == &player_character && attitude == NPCATT_FLEE_TEMP ) { action = method_of_fleeing(); } else if( has_effect( effect_npc_run_away ) ) { action = method_of_fleeing(); @@ -765,7 +772,7 @@ void npc::move() } else if( !ai_cache.sound_alerts.empty() && !is_walking_with() ) { tripoint cur_s_abs_pos = ai_cache.s_abs_pos; if( !ai_cache.guard_pos ) { - ai_cache.guard_pos = g->m.getabs( pos() ); + ai_cache.guard_pos = here.getabs( pos() ); } if( ai_cache.sound_alerts.size() > 1 ) { std::sort( ai_cache.sound_alerts.begin(), ai_cache.sound_alerts.end(), @@ -810,7 +817,7 @@ void npc::move() } if( action == npc_undecided && is_walking_with() && rules.has_flag( ally_rule::follow_close ) && - rl_dist( pos(), g->u.pos() ) > follow_distance() ) { + rl_dist( pos(), player_character.pos() ) > follow_distance() ) { action = npc_follow_player; } @@ -829,7 +836,7 @@ void npc::move() if( !activity_route.empty() && !has_destination_activity() ) { tripoint final_destination; if( destination_point ) { - final_destination = g->m.getlocal( *destination_point ); + final_destination = here.getlocal( *destination_point ); } else { final_destination = activity_route.back(); } @@ -850,7 +857,7 @@ void npc::move() // an interrupted activity can cause this situation. stops allied NPCs zooming off like random NPCs if( attitude == NPCATT_ACTIVITY && !activity ) { revert_after_activity(); - if( is_ally( g->u ) ) { + if( is_ally( player_character ) ) { attitude = NPCATT_FOLLOW; mission = NPC_MISSION_NULL; } @@ -882,7 +889,7 @@ void npc::move() } // check if in vehicle before rushing off to fetch things - if( is_walking_with() && g->u.in_vehicle ) { + if( is_walking_with() && player_character.in_vehicle ) { action = npc_follow_embarked; } else if( fetching_item ) { // Set to true if find_item() found something @@ -911,7 +918,8 @@ void npc::move() ( ( action == npc_follow_embarked && in_vehicle ) || ( action == npc_follow_player && - ( rl_dist( pos(), g->u.pos() ) <= follow_distance() || posz() != g->u.posz() ) ) + ( rl_dist( pos(), player_character.pos() ) <= follow_distance() || + posz() != player_character.posz() ) ) ) ) { action = method_of_attack(); } @@ -935,6 +943,8 @@ void npc::execute_action( npc_action action ) name, target, npc_action_name(action)); */ + Character &player_character = get_player_character(); + map &here = get_map(); switch( action ) { case npc_pause: move_pause(); @@ -949,7 +959,7 @@ void npc::execute_action( npc_action action ) case npc_investigate_sound: { tripoint cur_pos = pos(); - update_path( g->m.getlocal( ai_cache.s_abs_pos ) ); + update_path( here.getlocal( ai_cache.s_abs_pos ) ); move_to_next(); if( pos() == cur_pos ) { ai_cache.stuck += 1; @@ -958,7 +968,7 @@ void npc::execute_action( npc_action action ) break; case npc_return_to_guard_pos: { - const tripoint local_guard_pos = g->m.getlocal( *ai_cache.guard_pos ); + const tripoint local_guard_pos = here.getlocal( *ai_cache.guard_pos ); update_path( local_guard_pos ); if( pos() == local_guard_pos || path.empty() ) { move_pause(); @@ -997,7 +1007,7 @@ void npc::execute_action( npc_action action ) if( !has_effect( effect_lying_down ) ) { activate_bionic_by_id( bio_soporific ); add_effect( effect_lying_down, 30_minutes, num_bp, false, 1 ); - if( g->u.sees( *this ) && !g->u.in_sleep_state() ) { + if( player_character.sees( *this ) && !player_character.in_sleep_state() ) { add_msg( _( "%s lies down to sleep." ), name ); } } @@ -1055,7 +1065,7 @@ void npc::execute_action( npc_action action ) melee_attack( *cur, true ); } } else { - look_for_player( g->u ); + look_for_player( player_character ); } break; @@ -1087,7 +1097,7 @@ void npc::execute_action( npc_action action ) update_path( *last_player_seen_pos ); move_to_next(); } else { - look_for_player( g->u ); + look_for_player( player_character ); } break; @@ -1107,9 +1117,9 @@ void npc::execute_action( npc_action action ) break; } case npc_follow_player: - update_path( g->u.pos() ); + update_path( player_character.pos() ); if( static_cast( path.size() ) <= follow_distance() && - g->u.posz() == posz() ) { // We're close enough to u. + player_character.posz() == posz() ) { // We're close enough to u. move_pause(); } else if( !path.empty() ) { move_to_next(); @@ -1121,7 +1131,7 @@ void npc::execute_action( npc_action action ) break; case npc_follow_embarked: { - const optional_vpart_position vp = g->m.veh_at( g->u.pos() ); + const optional_vpart_position vp = here.veh_at( player_character.pos() ); if( !vp ) { debugmsg( "Following an embarked player with no vehicle at their location?" ); @@ -1134,7 +1144,7 @@ void npc::execute_action( npc_action action ) // Try to find the last destination // This is mount point, not actual position point last_dest( INT_MIN, INT_MIN ); - if( !path.empty() && veh_pointer_or_null( g->m.veh_at( path[path.size() - 1] ) ) == veh ) { + if( !path.empty() && veh_pointer_or_null( here.veh_at( path[path.size() - 1] ) ) == veh ) { last_dest = vp->mount(); } @@ -1231,7 +1241,7 @@ void npc::execute_action( npc_action action ) break; case npc_mug_player: - mug_player( g->u ); + mug_player( player_character ); break; case npc_goto_destination: @@ -1759,6 +1769,7 @@ healing_options npc::patient_assessment( const Character &c ) npc_action npc::address_needs( float danger ) { + Character &player_character = get_player_character(); // rng because NPCs are not meant to be hypervigilant hawks that notice everything // and swing into action with alarming alacrity. // no sometimes they are just looking the other way, sometimes they hestitate. @@ -1777,11 +1788,11 @@ npc_action npc::address_needs( float danger ) } if( get_skill_level( skill_firstaid ) > 0 ) { if( is_player_ally() ) { - healing_options try_to_fix_other = patient_assessment( g->u ); + healing_options try_to_fix_other = patient_assessment( player_character ); if( try_to_fix_other.any_true() ) { ai_cache.can_heal = has_healing_options( try_to_fix_other ); if( ai_cache.can_heal.any_true() ) { - ai_cache.ally = g->shared_from( g->u ); + ai_cache.ally = g->shared_from( player_character ); return npc_heal_player; } } @@ -1833,8 +1844,8 @@ npc_action npc::address_needs( float danger ) } } //Does the hallucination needs to disappear ? - if( is_hallucination() && g->u.sees( *this ) ) { - if( !g->u.has_effect( effect_hallu ) ) { + if( is_hallucination() && player_character.sees( *this ) ) { + if( !player_character.has_effect( effect_hallu ) ) { die( nullptr ); } } @@ -1872,7 +1883,7 @@ npc_action npc::address_needs( float danger ) if( danger <= 0.01 ) { if( get_fatigue() >= fatigue_levels::TIRED ) { return true; - } else if( is_walking_with() && g->u.in_sleep_state() && + } else if( is_walking_with() && player_character.in_sleep_state() && get_fatigue() > ( fatigue_levels::TIRED / 2 ) ) { return true; } @@ -1889,7 +1900,7 @@ npc_action npc::address_needs( float danger ) if( rules.has_flag( ally_rule::allow_sleep ) || get_fatigue() > fatigue_levels::MASSIVE_FATIGUE ) { return npc_sleep; - } else if( g->u.in_sleep_state() ) { + } else if( player_character.in_sleep_state() ) { // TODO: "Guard me while I sleep" command return npc_sleep; } @@ -1902,12 +1913,13 @@ npc_action npc::address_needs( float danger ) npc_action npc::address_player() { - if( ( attitude == NPCATT_TALK || attitude == NPCATT_RECOVER_GOODS ) && sees( g->u ) ) { - if( g->u.in_sleep_state() ) { + Character &player_character = get_player_character(); + if( ( attitude == NPCATT_TALK || attitude == NPCATT_RECOVER_GOODS ) && sees( player_character ) ) { + if( player_character.in_sleep_state() ) { // Leave sleeping characters alone. return npc_undecided; } - if( rl_dist( pos(), g->u.pos() ) <= 6 ) { + if( rl_dist( pos(), player_character.pos() ) <= 6 ) { return npc_talk_to_player; // Close enough to talk to you } else { if( one_in( 10 ) ) { @@ -1917,7 +1929,7 @@ npc_action npc::address_player() } } - if( attitude == NPCATT_MUG && sees( g->u ) ) { + if( attitude == NPCATT_MUG && sees( player_character ) ) { if( one_in( 3 ) ) { say( _( "Don't move a muscle…" ) ); } @@ -1939,7 +1951,7 @@ npc_action npc::address_player() } if( attitude == NPCATT_LEAD ) { - if( rl_dist( pos(), g->u.pos() ) >= 12 || !sees( g->u ) ) { + if( rl_dist( pos(), player_character.pos() ) >= 12 || !sees( player_character ) ) { int intense = get_effect_int( effect_catch_up ); if( intense < 10 ) { say( "" ); @@ -2152,7 +2164,8 @@ bool npc::update_path( const tripoint &p, const bool no_bashing, bool force ) } } - auto new_path = g->m.route( pos(), p, get_pathfinding_settings( no_bashing ), get_path_avoid() ); + auto new_path = get_map().route( pos(), p, get_pathfinding_settings( no_bashing ), + get_path_avoid() ); if( new_path.empty() ) { if( !ai_cache.sound_alerts.empty() ) { ai_cache.sound_alerts.erase( ai_cache.sound_alerts.begin() ); @@ -2177,27 +2190,29 @@ bool npc::update_path( const tripoint &p, const bool no_bashing, bool force ) bool npc::can_open_door( const tripoint &p, const bool inside ) const { - return !rules.has_flag( ally_rule::avoid_doors ) && g->m.open_door( p, inside, true ); + return !rules.has_flag( ally_rule::avoid_doors ) && get_map().open_door( p, inside, true ); } bool npc::can_move_to( const tripoint &p, bool no_bashing ) const { + map &here = get_map(); // Allow moving into any bashable spots, but penalize them during pathing // Doors are not passable for hallucinations - return( rl_dist( pos(), p ) <= 1 && g->m.has_floor( p ) && !g->is_dangerous_tile( p ) && - ( g->m.passable( p ) || ( can_open_door( p, !g->m.is_outside( pos() ) ) && !is_hallucination() ) || - ( !no_bashing && g->m.bash_rating( smash_ability(), p ) > 0 ) ) + return( rl_dist( pos(), p ) <= 1 && here.has_floor( p ) && !g->is_dangerous_tile( p ) && + ( here.passable( p ) || ( can_open_door( p, !here.is_outside( pos() ) ) && !is_hallucination() ) || + ( !no_bashing && here.bash_rating( smash_ability(), p ) > 0 ) ) ); } void npc::move_to( const tripoint &pt, bool no_bashing, std::set *nomove ) { tripoint p = pt; + map &here = get_map(); if( sees_dangerous_field( p ) || ( nomove != nullptr && nomove->find( p ) != nomove->end() ) ) { // Move to a neighbor field instead, if possible. // Maybe this code already exists somewhere? - auto other_points = g->m.get_dir_circle( pos(), p ); + auto other_points = here.get_dir_circle( pos(), p ); for( const tripoint &ot : other_points ) { if( could_move_onto( ot ) && ( nomove == nullptr || nomove->find( ot ) == nomove->end() ) ) { @@ -2256,7 +2271,7 @@ void npc::move_to( const tripoint &pt, bool no_bashing, std::set *nomo return; } - if( critter == &g->u ) { + if( critter->is_avatar() ) { say( "" ); } @@ -2284,7 +2299,7 @@ void npc::move_to( const tripoint &pt, bool no_bashing, std::set *nomo if( !activity_route.empty() && !np->has_destination_activity() ) { tripoint final_destination; if( destination_point ) { - final_destination = g->m.getlocal( *destination_point ); + final_destination = here.getlocal( *destination_point ); } else { final_destination = activity_route.back(); } @@ -2301,8 +2316,8 @@ void npc::move_to( const tripoint &pt, bool no_bashing, std::set *nomo // Boarding moving vehicles is fine, unboarding isn't bool moved = false; - if( const optional_vpart_position vp = g->m.veh_at( pos() ) ) { - const optional_vpart_position ovp = g->m.veh_at( p ); + if( const optional_vpart_position vp = here.veh_at( pos() ) ) { + const optional_vpart_position ovp = here.veh_at( p ); if( vp->vehicle().is_moving() && ( veh_pointer_or_null( ovp ) != veh_pointer_or_null( vp ) || !ovp.part_with_feature( VPFLAG_BOARDABLE, true ) ) ) { @@ -2321,10 +2336,10 @@ void npc::move_to( const tripoint &pt, bool no_bashing, std::set *nomo } moves -= 100; moved = true; - } else if( g->m.passable( p ) && !g->m.has_flag( "DOOR", p ) ) { + } else if( here.passable( p ) && !here.has_flag( "DOOR", p ) ) { bool diag = trigdist && posx() != p.x && posy() != p.y; if( is_mounted() ) { - const double base_moves = run_cost( g->m.combined_movecost( pos(), p ), + const double base_moves = run_cost( here.combined_movecost( pos(), p ), diag ) * 100.0 / mounted_creature->get_speed(); const double encumb_moves = get_weight() / 4800.0_gram; moves -= static_cast( std::ceil( base_moves + encumb_moves ) ); @@ -2332,33 +2347,33 @@ void npc::move_to( const tripoint &pt, bool no_bashing, std::set *nomo mounted_creature->use_mech_power( -1 ); } } else { - moves -= run_cost( g->m.combined_movecost( pos(), p ), diag ); + moves -= run_cost( here.combined_movecost( pos(), p ), diag ); } moved = true; - } else if( g->m.open_door( p, !g->m.is_outside( pos() ), true ) ) { + } else if( here.open_door( p, !here.is_outside( pos() ), true ) ) { if( !is_hallucination() ) { // hallucinations don't open doors - g->m.open_door( p, !g->m.is_outside( pos() ) ); + here.open_door( p, !here.is_outside( pos() ) ); moves -= 100; } else { // hallucinations teleport through doors moves -= 100; moved = true; } - } else if( get_dex() > 1 && g->m.has_flag_ter_or_furn( "CLIMBABLE", p ) ) { + } else if( get_dex() > 1 && here.has_flag_ter_or_furn( "CLIMBABLE", p ) ) { ///\EFFECT_DEX_NPC increases chance to climb CLIMBABLE furniture or terrain int climb = get_dex(); if( one_in( climb ) ) { add_msg_if_npc( m_neutral, _( "%1$s tries to climb the %2$s but slips." ), name, - g->m.tername( p ) ); + here.tername( p ) ); moves -= 400; } else { - add_msg_if_npc( m_neutral, _( "%1$s climbs over the %2$s." ), name, g->m.tername( p ) ); + add_msg_if_npc( m_neutral, _( "%1$s climbs over the %2$s." ), name, here.tername( p ) ); moves -= ( 500 - ( rng( 0, climb ) * 20 ) ); moved = true; } - } else if( !no_bashing && smash_ability() > 0 && g->m.is_bashable( p ) && - g->m.bash_rating( smash_ability(), p ) > 0 ) { + } else if( !no_bashing && smash_ability() > 0 && here.is_bashable( p ) && + here.bash_rating( smash_ability(), p ) > 0 ) { moves -= !is_armed() ? 80 : weapon.attack_time() * 0.8; - g->m.bash( p, smash_ability() ); + here.bash( p, smash_ability() ); } else { if( attitude == NPCATT_MUG || attitude == NPCATT_KILL || @@ -2382,37 +2397,37 @@ void npc::move_to( const tripoint &pt, bool no_bashing, std::set *nomo mounted_creature->setpos( pos() ); mounted_creature->facing = facing; mounted_creature->process_triggers(); - g->m.creature_in_field( *mounted_creature ); - g->m.creature_on_trap( *mounted_creature ); + here.creature_in_field( *mounted_creature ); + here.creature_on_trap( *mounted_creature ); } } - if( g->m.has_flag( "UNSTABLE", pos() ) ) { + if( here.has_flag( "UNSTABLE", pos() ) ) { add_effect( effect_bouldering, 1_turns, num_bp, true ); } else if( has_effect( effect_bouldering ) ) { remove_effect( effect_bouldering ); } - if( g->m.has_flag_ter_or_furn( TFLAG_NO_SIGHT, pos() ) ) { + if( here.has_flag_ter_or_furn( TFLAG_NO_SIGHT, pos() ) ) { add_effect( effect_no_sight, 1_turns, num_bp, true ); } else if( has_effect( effect_no_sight ) ) { remove_effect( effect_no_sight ); } if( in_vehicle ) { - g->m.unboard_vehicle( old_pos ); + here.unboard_vehicle( old_pos ); } // Close doors behind self (if you can) if( ( rules.has_flag( ally_rule::close_doors ) && is_player_ally() ) && !is_hallucination() ) { - doors::close_door( g->m, *this, old_pos ); + doors::close_door( here, *this, old_pos ); } - if( g->m.veh_at( p ).part_with_feature( VPFLAG_BOARDABLE, true ) ) { - g->m.board_vehicle( p, this ); + if( here.veh_at( p ).part_with_feature( VPFLAG_BOARDABLE, true ) ) { + here.board_vehicle( p, this ); } - g->m.creature_on_trap( *this ); - g->m.creature_in_field( *this ); + here.creature_on_trap( *this ); + here.creature_in_field( *this ); } } @@ -2495,7 +2510,8 @@ void npc::move_away_from( const tripoint &pt, bool no_bash_atk, std::setm.points_in_radius( pos(), 1 ) ) { + map &here = get_map(); + for( const tripoint &p : here.points_in_radius( pos(), 1 ) ) { if( nomove != nullptr && nomove->find( p ) != nomove->end() ) { continue; } @@ -2504,11 +2520,11 @@ void npc::move_away_from( const tripoint &pt, bool no_bash_atk, std::setu.pos() ) { + if( p == get_player_character().pos() ) { continue; } - const int cost = g->m.combined_movecost( pos(), p ); + const int cost = here.combined_movecost( pos(), p ); if( cost <= 0 ) { continue; } @@ -2548,17 +2564,18 @@ bool npc::find_job_to_perform() void npc::worker_downtime() { + map &here = get_map(); // are we already in a chair - if( g->m.has_flag_furn( "CAN_SIT", pos() ) ) { + if( here.has_flag_furn( "CAN_SIT", pos() ) ) { // just chill here move_pause(); return; } // already know of a chair, go there if( chair_pos != no_goal_point ) { - if( g->m.has_flag_furn( "CAN_SIT", g->m.getlocal( chair_pos ) ) ) { - update_path( g->m.getlocal( chair_pos ) ); - if( pos() == g->m.getlocal( chair_pos ) || path.empty() ) { + if( here.has_flag_furn( "CAN_SIT", here.getlocal( chair_pos ) ) ) { + update_path( here.getlocal( chair_pos ) ); + if( pos() == here.getlocal( chair_pos ) || path.empty() ) { move_pause(); path.clear(); } else { @@ -2572,11 +2589,11 @@ void npc::worker_downtime() } else { // find a chair if( !is_mounted() ) { - for( const tripoint &elem : g->m.points_in_radius( pos(), 30 ) ) { - if( g->m.has_flag_furn( "CAN_SIT", elem ) && !g->critter_at( elem ) && could_move_onto( elem ) && - g->m.point_within_camp( g->m.getabs( elem ) ) ) { + for( const tripoint &elem : here.points_in_radius( pos(), 30 ) ) { + if( here.has_flag_furn( "CAN_SIT", elem ) && !g->critter_at( elem ) && could_move_onto( elem ) && + here.point_within_camp( here.getabs( elem ) ) ) { // this one will do - chair_pos = g->m.getabs( elem ); + chair_pos = here.getabs( elem ); return; } } @@ -2585,8 +2602,8 @@ void npc::worker_downtime() // we got here if there are no chairs available. // wander back to near the bulletin board of the camp. if( wander_pos != no_goal_point ) { - update_path( g->m.getlocal( wander_pos ) ); - if( pos() == g->m.getlocal( wander_pos ) || path.empty() ) { + update_path( here.getlocal( wander_pos ) ); + if( pos() == here.getlocal( wander_pos ) || path.empty() ) { move_pause(); path.clear(); if( one_in( 30 ) ) { @@ -2606,16 +2623,16 @@ void npc::worker_downtime() } basecamp *temp_camp = *bcp; std::vector pts; - for( const tripoint &elem : g->m.points_in_radius( g->m.getlocal( temp_camp->get_bb_pos() ), + for( const tripoint &elem : here.points_in_radius( here.getlocal( temp_camp->get_bb_pos() ), 10 ) ) { - if( g->critter_at( elem ) || !could_move_onto( elem ) || g->m.has_flag( TFLAG_DEEP_WATER, elem ) || - !g->m.has_floor( elem ) || g->is_dangerous_tile( elem ) ) { + if( g->critter_at( elem ) || !could_move_onto( elem ) || here.has_flag( TFLAG_DEEP_WATER, elem ) || + !here.has_floor( elem ) || g->is_dangerous_tile( elem ) ) { continue; } pts.push_back( elem ); } if( !pts.empty() ) { - wander_pos = g->m.getabs( random_entry( pts ) ); + wander_pos = here.getabs( random_entry( pts ) ); return; } } @@ -2653,7 +2670,8 @@ void npc::move_pause() static cata::optional nearest_passable( const tripoint &p, const tripoint &closest_to ) { - if( g->m.passable( p ) ) { + map &here = get_map(); + if( here.passable( p ) ) { return p; } @@ -2664,8 +2682,8 @@ static cata::optional nearest_passable( const tripoint &p, const tripo const tripoint & r ) { return rl_dist( closest_to, l ) < rl_dist( closest_to, r ); } ); - auto iter = std::find_if( candidates.begin(), candidates.end(), []( const tripoint & pt ) { - return g->m.passable( pt ); + auto iter = std::find_if( candidates.begin(), candidates.end(), [&here]( const tripoint & pt ) { + return here.passable( pt ); } ); if( iter != candidates.end() ) { return *iter; @@ -2694,9 +2712,10 @@ void npc::move_away_from( const std::vector &spheres, bool no_bashing ) std::vector escape_points; + map &here = get_map(); std::copy_if( range.begin(), range.end(), std::back_inserter( escape_points ), - [&]( const tripoint & elem ) { - return g->m.passable( elem ); + [&here]( const tripoint & elem ) { + return here.passable( elem ); } ); cata::sort_by_rating( escape_points.begin(), escape_points.end(), [&]( const tripoint & elem ) { @@ -2706,7 +2725,7 @@ void npc::move_away_from( const std::vector &spheres, bool no_bashing ) } ); const int distance = rl_dist( pos(), elem ); - const int move_cost = g->m.move_cost( elem ); + const int move_cost = here.move_cost( elem ); return std::make_tuple( danger, distance, move_cost ); } ); @@ -2728,9 +2747,10 @@ void npc::move_away_from( const std::vector &spheres, bool no_bashing ) void npc::see_item_say_smth( const itype_id &object, const std::string &smth ) { + map &here = get_map(); for( const tripoint &p : closest_tripoints_first( pos(), 6 ) ) { - if( g->m.sees_some_items( p, *this ) && sees( p ) ) { - for( const item &it : g->m.i_at( p ) ) { + if( here.sees_some_items( p, *this ) && sees( p ) ) { + for( const item &it : here.i_at( p ) ) { if( one_in( 100 ) && ( it.typeId() == object ) ) { say( smth ); } @@ -2788,8 +2808,10 @@ void npc::find_item() npc *npc_to_add = npc_to_get.get(); followers.push_back( npc_to_add ); } + Character &player_character = get_player_character(); for( auto &elem : followers ) { - if( !it.is_owned_by( *this, true ) && ( g->u.sees( this->pos() ) || g->u.sees( wanted_item_pos ) || + if( !it.is_owned_by( *this, true ) && ( player_character.sees( this->pos() ) || + player_character.sees( wanted_item_pos ) || elem->sees( this->pos() ) || elem->sees( wanted_item_pos ) ) ) { return; } @@ -2810,16 +2832,17 @@ void npc::find_item() } }; + map &here = get_map(); // Harvest item doesn't exist, so we'll be checking by its name std::string wanted_name; const auto consider_terrain = - [ this, whitelisting, volume_allowed, &wanted, &wanted_name ]( const tripoint & p ) { + [ this, whitelisting, volume_allowed, &wanted, &wanted_name, &here ]( const tripoint & p ) { // We only want to pick plants when there are no items to pick if( !whitelisting || wanted != nullptr || !wanted_name.empty() || volume_allowed < 250_ml ) { return; } - const auto harvest = g->m.get_harvest_names( p ); + const auto &harvest = here.get_harvest_names( p ); for( const auto &entry : harvest ) { if( item_name_whitelisted( entry ) ) { wanted_name = entry; @@ -2839,9 +2862,9 @@ void npc::find_item() const tripoint abs_p = global_square_location() - pos() + p; const int prev_num_items = ai_cache.searched_tiles.get( abs_p, -1 ); // Prefetch the number of items present so we can bail out if we already checked here. - const map_stack m_stack = g->m.i_at( p ); + const map_stack m_stack = here.i_at( p ); int num_items = m_stack.size(); - const optional_vpart_position vp = g->m.veh_at( p ); + const optional_vpart_position vp = here.veh_at( p ); if( vp ) { const cata::optional cargo = vp.part_with_feature( VPFLAG_CARGO, true ); if( cargo ) { @@ -2858,7 +2881,7 @@ void npc::find_item() } }; bool can_see = false; - if( g->m.sees_some_items( p, *this ) && sees( p ) ) { + if( here.sees_some_items( p, *this ) && sees( p ) ) { can_see = true; for( const item &it : m_stack ) { consider_item( it, p ); @@ -2935,12 +2958,13 @@ void npc::pick_up_item() return; } - const cata::optional vp = g->m.veh_at( wanted_item_pos ).part_with_feature( + map &here = get_map(); + const cata::optional vp = here.veh_at( wanted_item_pos ).part_with_feature( VPFLAG_CARGO, false ); const bool has_cargo = vp && !vp->has_feature( "LOCKED" ); - if( ( !g->m.has_items( wanted_item_pos ) && !has_cargo && - !g->m.is_harvestable( wanted_item_pos ) && sees( wanted_item_pos ) ) || + if( ( !here.has_items( wanted_item_pos ) && !has_cargo && + !here.is_harvestable( wanted_item_pos ) && sees( wanted_item_pos ) ) || ( is_player_ally() && g->check_zone( zone_type_id( "NO_NPC_PICKUP" ), wanted_item_pos ) ) ) { // Items we wanted no longer exist and we can see it // Or player who is leading us doesn't want us to pick it up @@ -2980,16 +3004,17 @@ void npc::pick_up_item() if( picked_up.empty() ) { // Last chance: plant harvest - if( g->m.is_harvestable( wanted_item_pos ) ) { - g->m.examine( *this, wanted_item_pos ); + if( here.is_harvestable( wanted_item_pos ) ) { + here.examine( *this, wanted_item_pos ); // Note: we didn't actually pick up anything, just spawned items // but we want the item picker to find new items fetching_item = false; return; } } + Character &player_character = get_player_character(); // Describe the pickup to the player - bool u_see = g->u.sees( *this ) || g->u.sees( wanted_item_pos ); + bool u_see = player_character.sees( *this ) || player_character.sees( wanted_item_pos ); if( u_see ) { if( picked_up.size() == 1 ) { add_msg( _( "%1$s picks up a %2$s." ), name, picked_up.front().tname() ); @@ -3066,7 +3091,7 @@ std::list npc_pickup_from_stack( npc &who, T &items ) std::list npc::pick_up_item_map( const tripoint &where ) { - map_stack stack = g->m.i_at( where ); + map_stack stack = get_map().i_at( where ); return npc_pickup_from_stack( *this, stack ); } @@ -3135,6 +3160,7 @@ void npc::drop_items( const units::mass &drop_weight, const units::volume &drop_ } } + map &here = get_map(); std::string item_name; // For description below int num_items_dropped = 0; // For description below // Now, drop items, starting from the top of each list @@ -3180,11 +3206,11 @@ void npc::drop_items( const units::mass &drop_weight, const units::volume &drop_ item_name += _( " and " ) + dropped.tname(); } if( !is_hallucination() ) { // hallucinations can't drop real items - g->m.add_item_or_charges( pos(), dropped ); + here.add_item_or_charges( pos(), dropped ); } } // Finally, describe the action if u can see it - if( g->u.sees( *this ) ) { + if( get_player_character().sees( *this ) ) { if( num_items_dropped >= 3 ) { add_msg( ngettext( "%s drops %d item.", "%s drops %d items.", num_items_dropped ), name, num_items_dropped ); @@ -3197,20 +3223,23 @@ void npc::drop_items( const units::mass &drop_weight, const units::volume &drop_ bool npc::find_corpse_to_pulp() { - if( ( is_player_ally() && ( !rules.has_flag( ally_rule::allow_pulp ) || g->u.in_vehicle ) ) || + Character &player_character = get_player_character(); + if( ( is_player_ally() && ( !rules.has_flag( ally_rule::allow_pulp ) || + player_character.in_vehicle ) ) || is_hallucination() ) { return false; } + map &here = get_map(); // Pathing with overdraw can get expensive, limit it int path_counter = 4; - const auto check_tile = [this, &path_counter]( const tripoint & p ) -> const item * { - if( !g->m.sees_some_items( p, *this ) || !sees( p ) ) + const auto check_tile = [this, &path_counter, &here]( const tripoint & p ) -> const item * { + if( !here.sees_some_items( p, *this ) || !sees( p ) ) { return nullptr; } - const map_stack items = g->m.i_at( p ); + const map_stack items = here.i_at( p ); const item *found = nullptr; for( const item &it : items ) { @@ -3250,8 +3279,8 @@ bool npc::find_corpse_to_pulp() if( corpse == nullptr ) { // If we're following the player, don't wander off to pulp corpses - const tripoint &around = is_walking_with() ? g->u.pos() : pos(); - for( const item_location &location : g->m.get_active_items_in_radius( around, range, + const tripoint &around = is_walking_with() ? player_character.pos() : pos(); + for( const item_location &location : here.get_active_items_in_radius( around, range, special_item_type::corpse ) ) { corpse = check_tile( location.position() ); @@ -3285,7 +3314,7 @@ bool npc::do_pulp() // TODO: Don't recreate the activity every time int old_moves = moves; assign_activity( ACT_PULP, calendar::INDEFINITELY_LONG, 0 ); - activity.placement = g->m.getabs( *pulp_location ); + activity.placement = get_map().getabs( *pulp_location ); activity.do_turn( *this ); return moves != old_moves; } @@ -3428,7 +3457,7 @@ bool npc::scan_new_items() static void npc_throw( npc &np, item &it, int index, const tripoint &pos ) { - if( g->u.sees( np ) ) { + if( get_player_character().sees( np ) ) { add_msg( _( "%1$s throws a %2$s." ), np.name, it.tname() ); } @@ -3543,9 +3572,10 @@ bool npc::alt_attack() return true; } + map &here = get_map(); // We need to throw this live (grenade, etc) NOW! Pick another target? for( int dist = 2; dist <= conf; dist++ ) { - for( const tripoint &pt : g->m.points_in_radius( pos(), dist ) ) { + for( const tripoint &pt : here.points_in_radius( pos(), dist ) ) { const monster *const target_ptr = g->critter_at( pt ); int newdist = rl_dist( pos(), pt ); // TODO: Change "newdist >= 2" to "newdist >= safe_distance(used)" @@ -3569,7 +3599,7 @@ bool npc::alt_attack() */ int best_dist = 0; for( int dist = 2; dist <= conf; dist++ ) { - for( const tripoint &pt : g->m.points_in_radius( pos(), dist ) ) { + for( const tripoint &pt : here.points_in_radius( pos(), dist ) ) { int new_dist = rl_dist( pos(), pt ); if( new_dist > best_dist && wont_hit_friend( pt, *used, true ) ) { best_dist = new_dist; @@ -3611,8 +3641,9 @@ void npc::heal_player( player &patient ) return; } + Character &player_character = get_player_character(); // Close enough to heal! - bool u_see = g->u.sees( *this ) || g->u.sees( patient ); + bool u_see = player_character.sees( *this ) || player_character.sees( patient ); if( u_see ) { add_msg( _( "%1$s heals %2$s." ), disp_name(), patient.disp_name() ); } @@ -3633,7 +3664,7 @@ void npc::heal_player( player &patient ) void npc:: pretend_heal( player &patient, item used ) { - if( g->u.sees( *this ) ) { + if( get_player_character().sees( *this ) ) { add_msg( _( "%1$s heals %2$s." ), disp_name(), patient.disp_name() ); // you can tell that it's not real by looking at your HP though } @@ -3667,7 +3698,7 @@ void npc::heal_self() return; } - if( g->u.sees( *this ) ) { + if( get_player_character().sees( *this ) ) { add_msg( _( "%s applies a %s" ), disp_name(), used.tname() ); } warn_about( "heal_self", 1_turns ); @@ -3687,7 +3718,7 @@ void npc::use_painkiller() debugmsg( "NPC tried to use painkillers, but has none!" ); move_pause(); } else { - if( g->u.sees( *this ) ) { + if( get_player_character().sees( *this ) ) { add_msg( _( "%1$s takes some %2$s." ), disp_name(), it->tname() ); } item_location loc = item_location( *this, it ); @@ -3789,8 +3820,9 @@ bool npc::consume_food_from_camp() if( !is_player_ally() ) { return false; } + Character &player_character = get_player_character(); cata::optional potential_bc; - for( const tripoint &camp_pos : g->u.camps ) { + for( const tripoint &camp_pos : player_character.camps ) { if( rl_dist( camp_pos, global_omt_location() ) < 3 ) { potential_bc = overmap_buffer.find_camp( camp_pos.xy() ); if( potential_bc ) { @@ -3807,7 +3839,7 @@ bool npc::consume_food_from_camp() set_thirst( 0 ); return true; } - faction *yours = g->u.get_faction(); + faction *yours = player_character.get_faction(); int camp_kcals = std::min( std::max( 0, 19 * get_healthy_kcal() / 20 - get_stored_kcal() - stomach.get_calories() ), yours->food_supply ); if( camp_kcals > 0 ) { @@ -3862,7 +3894,7 @@ bool npc::consume_food() return consumed; } -void npc::mug_player( player &mark ) +void npc::mug_player( Character &mark ) { if( mark.is_armed() ) { make_angry(); @@ -3874,7 +3906,8 @@ void npc::mug_player( player &mark ) return; } - const bool u_see = g->u.sees( *this ) || g->u.sees( mark ); + Character &player_character = get_player_character(); + const bool u_see = player_character.sees( *this ) || player_character.sees( mark ); if( mark.cash > 0 ) { if( !is_hallucination() ) { // hallucinations can't take items cash += mark.cash; @@ -3940,7 +3973,7 @@ void npc::mug_player( player &mark ) } } -void npc::look_for_player( const player &sought ) +void npc::look_for_player( const Character &sought ) { complain_about( "look_for_player", 5_minutes, "", false ); update_path( sought.pos() ); @@ -3983,7 +4016,8 @@ void npc::look_for_player( const player &sought ) bool npc::saw_player_recently() const { - return last_player_seen_pos && g->m.inbounds( *last_player_seen_pos ) && last_seen_player_turn > 0; + return last_player_seen_pos && get_map().inbounds( *last_player_seen_pos ) && + last_seen_player_turn > 0; } bool npc::has_omt_destination() const @@ -3996,13 +4030,15 @@ void npc::reach_omt_destination() if( !omt_path.empty() ) { omt_path.clear(); } + map &here = get_map(); if( is_travelling() ) { - guard_pos = g->m.getabs( pos() ); + guard_pos = here.getabs( pos() ); goal = no_goal_point; if( is_player_ally() ) { + Character &player_character = get_player_character(); talk_function::assign_guard( *this ); - if( rl_dist( g->u.pos(), pos() ) > SEEX * 2 || !g->u.sees( pos() ) ) { - if( g->u.has_item_with_flag( "TWO_WAY_RADIO", true ) && + if( rl_dist( player_character.pos(), pos() ) > SEEX * 2 || !player_character.sees( pos() ) ) { + if( player_character.has_item_with_flag( "TWO_WAY_RADIO", true ) && has_item_with_flag( "TWO_WAY_RADIO", true ) ) { add_msg( m_info, _( "From your two-way radio you hear %s reporting in, " "'I've arrived, boss!'" ), disp_name() ); @@ -4029,7 +4065,7 @@ void npc::reach_omt_destination() } // If we are guarding, remember our position in case we get forcibly moved goal = global_omt_location(); - if( guard_pos == g->m.getabs( pos() ) ) { + if( guard_pos == here.getabs( pos() ) ) { // This is the specific point return; } @@ -4038,10 +4074,10 @@ void npc::reach_omt_destination() // No point recalculating the path to get home move_to_next(); } else if( guard_pos != no_goal_point ) { - update_path( g->m.getlocal( guard_pos ) ); + update_path( here.getlocal( guard_pos ) ); move_to_next(); } else { - guard_pos = g->m.getabs( pos() ); + guard_pos = here.getabs( pos() ); } } @@ -4065,7 +4101,7 @@ void npc::set_omt_destination() // all of the following luxuries are at ground level. // so please wallow in hunger & fear if below ground. - if( posz() != 0 && !g->m.has_zlevels() ) { + if( posz() != 0 && !get_map().has_zlevels() ) { goal = no_goal_point; return; } @@ -4135,8 +4171,9 @@ void npc::set_omt_destination() void npc::go_to_omt_destination() { + map &here = get_map(); if( ai_cache.guard_pos ) { - if( g->m.getabs( pos() ) == *ai_cache.guard_pos ) { + if( here.getabs( pos() ) == *ai_cache.guard_pos ) { path.clear(); ai_cache.guard_pos = cata::nullopt; move_pause(); @@ -4178,18 +4215,18 @@ void npc::go_to_omt_destination() return; } } - tripoint sm_tri = g->m.getlocal( sm_to_ms_copy( omt_to_sm_copy( omt_path.back() ) ) ); + tripoint sm_tri = here.getlocal( sm_to_ms_copy( omt_to_sm_copy( omt_path.back() ) ) ); tripoint centre_sub = sm_tri + point( SEEX, SEEY ); - if( !g->m.passable( centre_sub ) ) { - auto candidates = g->m.points_in_radius( centre_sub, 2 ); + if( !here.passable( centre_sub ) ) { + auto candidates = here.points_in_radius( centre_sub, 2 ); for( const auto &elem : candidates ) { - if( g->m.passable( elem ) ) { + if( here.passable( elem ) ) { centre_sub = elem; break; } } } - path = g->m.route( pos(), centre_sub, get_pathfinding_settings(), get_path_avoid() ); + path = here.route( pos(), centre_sub, get_pathfinding_settings(), get_path_avoid() ); add_msg( m_debug, "%s going (%d,%d,%d)->(%d,%d,%d)", name, omt_pos.x, omt_pos.y, omt_pos.z, goal.x, goal.y, goal.z ); @@ -4203,7 +4240,7 @@ void npc::go_to_omt_destination() void npc::guard_current_pos() { goal = global_omt_location(); - guard_pos = g->m.getabs( pos() ); + guard_pos = get_map().getabs( pos() ); } std::string npc_action_name( npc_action action ) @@ -4402,7 +4439,7 @@ bool npc::complain_about( const std::string &issue, const time_duration &dur, // Don't wake player up with non-serious complaints // Stop complaining while asleep const bool do_complain = force || ( rules.has_flag( ally_rule::allow_complain ) && - !g->u.in_sleep_state() && !in_sleep_state() ); + !get_player_character().in_sleep_state() && !in_sleep_state() ); if( complain_since( issue, dur ) && do_complain ) { say( speech, priority ); @@ -4422,7 +4459,7 @@ bool npc::complain() static const std::string hunger_string = "hunger"; static const std::string thirst_string = "thirst"; - if( !is_player_ally() || !g->u.sees( *this ) ) { + if( !is_player_ally() || !get_player_character().sees( *this ) ) { return false; } @@ -4524,7 +4561,7 @@ void npc::do_reload( const item &it ) moves -= reload_time; recoil = MAX_RECOIL; - if( g->u.sees( *this ) ) { + if( get_player_character().sees( *this ) ) { add_msg( _( "%1$s reloads their %2$s." ), name, it.tname() ); sfx::play_variant_sound( "reload", it.typeId().str(), sfx::get_heard_volume( pos() ), sfx::get_heard_angle( pos() ) ); diff --git a/src/overmap_ui.cpp b/src/overmap_ui.cpp index 8468e86d9a50b..044db22d4c803 100644 --- a/src/overmap_ui.cpp +++ b/src/overmap_ui.cpp @@ -129,7 +129,7 @@ static std::tuple get_note_display_info( const std::stri static std::array, npm_width *npm_height> get_overmap_neighbors( const tripoint ¤t ) { - const bool has_debug_vision = g->u.has_trait( trait_DEBUG_NIGHTVISION ); + const bool has_debug_vision = get_player_character().has_trait( trait_DEBUG_NIGHTVISION ); std::array, npm_width *npm_height> map_around; int index = 0; @@ -446,7 +446,7 @@ static point draw_notes( const tripoint &origin ) const std::string note_symbol = std::string( 1, std::get<0>( om_symbol ) ); const std::string note_text = note.substr( std::get<2>( om_symbol ), std::string::npos ); point p_omt( p ); - const point p_player = g->u.global_omt_location().xy(); + const point p_player = get_player_character().global_omt_location().xy(); const int distance_player = rl_dist( p_player, p_omt ); const point sm_pos = omt_to_sm_copy( p_omt ); const point p_om = omt_to_om_remain( p_omt ); @@ -486,17 +486,18 @@ void draw( const catacurses::window &w, const catacurses::window &wbar, const tr const int om_half_height = om_map_height / 2; const bool viewing_weather = ( ( data.debug_weather || data.visible_weather ) && center.z == 10 ); + avatar &player_character = get_avatar(); // Target of current mission - const tripoint target = g->u.get_active_mission_target(); + const tripoint target = player_character.get_active_mission_target(); const bool has_target = target != overmap::invalid_tripoint; // seen status & terrain of center position bool csee = false; oter_id ccur_ter = oter_str_id::NULL_ID(); // Debug vision allows seeing everything - const bool has_debug_vision = g->u.has_trait( trait_DEBUG_NIGHTVISION ); + const bool has_debug_vision = player_character.has_trait( trait_DEBUG_NIGHTVISION ); // sight_points is hoisted for speed reasons. const int sight_points = !has_debug_vision ? - g->u.overmap_sight_range( g->light_level( g->u.posz() ) ) : + player_character.overmap_sight_range( g->light_level( player_character.posz() ) ) : 100; // Whether showing hordes is currently enabled const bool showhordes = uistate.overmap_show_hordes; @@ -634,8 +635,8 @@ void draw( const catacurses::window &w, const catacurses::window &wbar, const tr } } } - for( auto &elem : g->u.omt_path ) { - tripoint tri_to_add = tripoint( elem.xy(), g->u.posz() ); + for( auto &elem : player_character.omt_path ) { + tripoint tri_to_add = tripoint( elem.xy(), player_character.posz() ); player_path_route.push_back( tri_to_add ); } for( const auto &np : followers ) { @@ -672,8 +673,8 @@ void draw( const catacurses::window &w, const catacurses::window &wbar, const tr } // Check if location is within player line-of-sight - const bool los = see && g->u.overmap_los( omp, sight_points ); - const bool los_sky = g->u.overmap_los( omp, sight_points * 2 ); + const bool los = see && player_character.overmap_los( omp, sight_points ); + const bool los_sky = player_character.overmap_los( omp, sight_points * 2 ); int mycount = std::count( path_route.begin(), path_route.end(), omp ); bool player_path_count = false; std::vector::iterator it; @@ -683,7 +684,7 @@ void draw( const catacurses::window &w, const catacurses::window &wbar, const tr } if( blink && omp == orig ) { // Display player pos, should always be visible - ter_color = g->u.symbol_color(); + ter_color = player_character.symbol_color(); ter_sym = "@"; } else if( viewing_weather && ( data.debug_weather || los_sky ) ) { const weather_type type = get_weather_at_point( omp ); @@ -968,7 +969,7 @@ void draw( const catacurses::window &w, const catacurses::window &wbar, const tr } } else if( viewing_weather ) { const bool weather_is_visible = ( data.debug_weather || - g->u.overmap_los( center, sight_points * 2 ) ); + player_character.overmap_los( center, sight_points * 2 ) ); if( weather_is_visible ) { weather_datum weather = weather_data( get_weather_at_point( center ) ); // NOLINTNEXTLINE(cata-use-named-point-constants) @@ -1006,7 +1007,7 @@ void draw( const catacurses::window &w, const catacurses::window &wbar, const tr } //Show mission targets on this location - for( auto &mission : g->u.get_active_missions() ) { + for( auto &mission : player_character.get_active_missions() ) { if( mission->get_target() == center ) { mvwprintz( wbar, point( 1, ++lines ), c_white, mission->name() ); } @@ -1539,9 +1540,10 @@ static tripoint display( const tripoint &orig, const draw_data_t &data = draw_da path_type ptype; ptype.only_known_by_player = true; ptype.avoid_danger = true; - bool in_vehicle = g->u.in_vehicle && g->u.controlling_vehicle; + avatar &player_character = get_avatar(); + bool in_vehicle = player_character.in_vehicle && player_character.controlling_vehicle; map &here = get_map(); - const optional_vpart_position vp = here.veh_at( g->u.pos() ); + const optional_vpart_position vp = here.veh_at( player_character.pos() ); if( vp && in_vehicle ) { vehicle &veh = vp->vehicle(); if( veh.can_float() && veh.is_watercraft() && veh.is_in_water() ) { @@ -1554,37 +1556,37 @@ static tripoint display( const tripoint &orig, const draw_data_t &data = draw_da } else { const oter_id oter = overmap_buffer.ter( curs ); // going to or coming from a water tile - if( is_river_or_lake( oter ) || here.has_flag( "SWIMMABLE", g->u.pos() ) ) { + if( is_river_or_lake( oter ) || here.has_flag( "SWIMMABLE", player_character.pos() ) ) { ptype.amphibious = true; } } - const tripoint player_omt_pos = g->u.global_omt_location(); - if( !g->u.omt_path.empty() && g->u.omt_path.front() == curs ) { + const tripoint player_omt_pos = player_character.global_omt_location(); + if( !player_character.omt_path.empty() && player_character.omt_path.front() == curs ) { std::string confirm_msg; - if( g->u.weight_carried() > g->u.weight_capacity() ) { + if( player_character.weight_carried() > player_character.weight_capacity() ) { confirm_msg = _( "You are overburdened, are you sure you want to travel (it may be painful)?" ); } else { confirm_msg = _( "Travel to this point?" ); } if( query_yn( confirm_msg ) ) { // renew the path incase of a leftover dangling path point - g->u.omt_path = overmap_buffer.get_npc_path( player_omt_pos, curs, ptype ); - if( g->u.in_vehicle && g->u.controlling_vehicle ) { - vehicle *player_veh = veh_pointer_or_null( here.veh_at( g->u.pos() ) ); - player_veh->omt_path = g->u.omt_path; + player_character.omt_path = overmap_buffer.get_npc_path( player_omt_pos, curs, ptype ); + if( player_character.in_vehicle && player_character.controlling_vehicle ) { + vehicle *player_veh = veh_pointer_or_null( here.veh_at( player_character.pos() ) ); + player_veh->omt_path = player_character.omt_path; player_veh->is_autodriving = true; - g->u.assign_activity( ACT_AUTODRIVE ); + player_character.assign_activity( ACT_AUTODRIVE ); } else { - g->u.reset_move_mode(); - g->u.assign_activity( ACT_TRAVELLING ); + player_character.reset_move_mode(); + player_character.assign_activity( ACT_TRAVELLING ); } action = "QUIT"; } } if( curs == player_omt_pos ) { - g->u.omt_path.clear(); + player_character.omt_path.clear(); } else { - g->u.omt_path = overmap_buffer.get_npc_path( player_omt_pos, curs, ptype ); + player_character.omt_path = overmap_buffer.get_npc_path( player_omt_pos, curs, ptype ); } } else if( action == "TOGGLE_BLINKING" ) { uistate.overmap_blinking = !uistate.overmap_blinking; @@ -1643,21 +1645,21 @@ static tripoint display( const tripoint &orig, const draw_data_t &data = draw_da void ui::omap::display() { - overmap_ui::display( g->u.global_omt_location(), overmap_ui::draw_data_t() ); + overmap_ui::display( get_player_character().global_omt_location(), overmap_ui::draw_data_t() ); } void ui::omap::display_hordes() { overmap_ui::draw_data_t data; data.debug_mongroup = true; - overmap_ui::display( g->u.global_omt_location(), data ); + overmap_ui::display( get_player_character().global_omt_location(), data ); } void ui::omap::display_weather() { overmap_ui::draw_data_t data; data.debug_weather = true; - tripoint pos = g->u.global_omt_location(); + tripoint pos = get_player_character().global_omt_location(); pos.z = 10; overmap_ui::display( pos, data ); } @@ -1666,7 +1668,7 @@ void ui::omap::display_visible_weather() { overmap_ui::draw_data_t data; data.visible_weather = true; - tripoint pos = g->u.global_omt_location(); + tripoint pos = get_player_character().global_omt_location(); pos.z = 10; overmap_ui::display( pos, data ); } @@ -1675,14 +1677,14 @@ void ui::omap::display_scents() { overmap_ui::draw_data_t data; data.debug_scent = true; - overmap_ui::display( g->u.global_omt_location(), data ); + overmap_ui::display( get_player_character().global_omt_location(), data ); } void ui::omap::display_editor() { overmap_ui::draw_data_t data; data.debug_editor = true; - overmap_ui::display( g->u.global_omt_location(), data ); + overmap_ui::display( get_player_character().global_omt_location(), data ); } void ui::omap::display_zones( const tripoint ¢er, const tripoint &select, const int iZoneIndex ) @@ -1695,7 +1697,7 @@ void ui::omap::display_zones( const tripoint ¢er, const tripoint &select, co tripoint ui::omap::choose_point() { - return overmap_ui::display( g->u.global_omt_location() ); + return overmap_ui::display( get_player_character().global_omt_location() ); } tripoint ui::omap::choose_point( const tripoint &origin ) @@ -1705,7 +1707,7 @@ tripoint ui::omap::choose_point( const tripoint &origin ) tripoint ui::omap::choose_point( int z ) { - tripoint loc = g->u.global_omt_location(); + tripoint loc = get_player_character().global_omt_location(); loc.z = z; return overmap_ui::display( loc ); } From e27503a7db737cb50149427a7a9ca88168cfe46d Mon Sep 17 00:00:00 2001 From: John Bytheway Date: Fri, 3 Jul 2020 08:37:42 -0400 Subject: [PATCH 066/420] Fix comment --- src/item.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/item.h b/src/item.h index 50aa19fb5867f..02073cd0dd66a 100644 --- a/src/item.h +++ b/src/item.h @@ -738,7 +738,7 @@ class item : public visitable return contents.has_pocket_type( item_pocket::pocket_type::CONTAINER ); } /** - * Puts the given item into this one, no checks are performed. + * Puts the given item into this one. */ ret_val put_in( const item &payload, item_pocket::pocket_type pk_type ); From 0f4db78a988ca11fca33cfcaef25cde4ffea5dbe Mon Sep 17 00:00:00 2001 From: John Bytheway Date: Fri, 3 Jul 2020 08:38:09 -0400 Subject: [PATCH 067/420] Use ret_val::make_failure's built-in formatting No need to call string_format here. --- src/item_contents.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/item_contents.cpp b/src/item_contents.cpp index 4f52782bdc3e3..635fe585c1fe4 100644 --- a/src/item_contents.cpp +++ b/src/item_contents.cpp @@ -351,10 +351,10 @@ struct item_contents::item_contents_helper { std::unique( failure_messages.begin(), failure_messages.end() ), failure_messages.end() ); return ret_val::make_failure( - null_pocket, string_format( - ngettext( "pocket unacceptable because %s", "pockets unacceptable because %s", - num_pockets_of_type ), - enumerate_as_string( failure_messages, enumeration_conjunction::or_ ) ) ); + null_pocket, + ngettext( "pocket unacceptable because %s", "pockets unacceptable because %s", + num_pockets_of_type ), + enumerate_as_string( failure_messages, enumeration_conjunction::or_ ) ); } }; From 16fb74669fb642f89ca4fcd096c6e1d4b0d45575 Mon Sep 17 00:00:00 2001 From: ZhilkinSerg Date: Fri, 3 Jul 2020 10:34:11 +0300 Subject: [PATCH 068/420] Fix fire field processing --- src/map.h | 4 ++-- src/map_field.cpp | 7 +++---- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/src/map.h b/src/map.h index e1d0a1ca0dcba..d3d52ac6a5a19 100644 --- a/src/map.h +++ b/src/map.h @@ -1167,8 +1167,8 @@ class map // See fields.cpp bool process_fields(); bool process_fields_in_submap( submap *current_submap, const tripoint &submap_pos ); - bool process_fire_field_in_submap( submap *current_submap, field_entry &cur, const tripoint &p, - bool &dirty_transparency_cache ); + bool process_fire_field_in_submap( maptile &map_tile, const tripoint &p, + field_entry &cur, bool &dirty_transparency_cache ); /** * Apply field effects to the creature when it's on a square with fields. */ diff --git a/src/map_field.cpp b/src/map_field.cpp index 79e3c017fbc2d..a5e8bb76bd856 100644 --- a/src/map_field.cpp +++ b/src/map_field.cpp @@ -500,7 +500,7 @@ bool map::process_fields_in_submap( submap *const current_submap, sblk.apply_slime( p, cur.get_field_intensity() * curtype.obj().apply_slime_factor ); } if( curtype == fd_fire ) { - if( process_fire_field_in_submap( current_submap, cur, p, dirty_transparency_cache ) ) { + if( process_fire_field_in_submap( map_tile, p, cur, dirty_transparency_cache ) ) { break; } } @@ -872,12 +872,11 @@ bool map::process_fields_in_submap( submap *const current_submap, return dirty_transparency_cache; } -bool map::process_fire_field_in_submap( submap *current_submap, field_entry &cur, - const tripoint &p, bool &dirty_transparency_cache ) +bool map::process_fire_field_in_submap( maptile &map_tile, const tripoint &p, + field_entry &cur, bool &dirty_transparency_cache ) { bool breaks_loop = false; map &here = get_map(); - maptile map_tile( current_submap, point_zero ); field_entry *tmpfld = nullptr; cur.set_field_age( std::max( -24_hours, cur.get_field_age() ) ); // Entire objects for ter/frn for flags From d1d778246a39545a01e792712195515ba789ef60 Mon Sep 17 00:00:00 2001 From: Termineitor244 Date: Fri, 3 Jul 2020 13:08:40 -0500 Subject: [PATCH 069/420] Firefigther belt can now store firefigther tools Just expanded a little the maximum weight and length of its pocket so the halligan bar and the fire axe can be stored in the belt. --- data/json/items/armor/belts.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/data/json/items/armor/belts.json b/data/json/items/armor/belts.json index 8213a4ff36063..8c644d0ad7604 100644 --- a/data/json/items/armor/belts.json +++ b/data/json/items/armor/belts.json @@ -19,8 +19,8 @@ "pocket_data": [ { "max_contains_volume": "2 L", - "max_contains_weight": "4 kg", - "max_item_length": "70 cm", + "max_contains_weight": "6 kg", + "max_item_length": "90 cm", "moves": 50, "flag_restriction": [ "BELT_CLIP" ], "holster": true From 7d8a16ac6f1ebed6ae6b4bcd0a0190f6c5f41509 Mon Sep 17 00:00:00 2001 From: Sergey Alirzaev Date: Fri, 3 Jul 2020 21:12:21 +0300 Subject: [PATCH 070/420] Extend nested items deselection on container drop to `,` as discussed in https://github.com/CleverRaven/Cataclysm-DDA/pull/41514 fixes https://github.com/CleverRaven/Cataclysm-DDA/issues/41724 --- src/inventory_ui.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/inventory_ui.cpp b/src/inventory_ui.cpp index 1ea1e2590f965..027bca08d9be8 100644 --- a/src/inventory_ui.cpp +++ b/src/inventory_ui.cpp @@ -2434,6 +2434,7 @@ drop_locations inventory_drop_selector::execute() const auto selected( get_active_column().get_entries( filter_to_nonfavorite_and_nonworn ) ); process_selected( count, selected ); + deselect_contained_items(); } else if( input.action == "RIGHT" ) { const auto selected( get_active_column().get_all_selected() ); From 42586da12686055bd120cfbb9f3eaa47b76de7df Mon Sep 17 00:00:00 2001 From: "U-N\\N" Date: Fri, 3 Jul 2020 11:54:40 +0200 Subject: [PATCH 071/420] use base_volume for washing --- src/iuse.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/iuse.cpp b/src/iuse.cpp index e35c769cedf11..ae046084fac2a 100644 --- a/src/iuse.cpp +++ b/src/iuse.cpp @@ -9813,7 +9813,7 @@ int iuse::wash_items( player *p, bool soft_items, bool hard_items ) ) { units::volume total_volume = 0_ml; for( const auto &p : locs ) { - total_volume += ( *p.first )->volume() * p.second / + total_volume += ( *p.first )->base_volume() * p.second / ( ( *p.first )->count_by_charges() ? ( *p.first )->charges : 1 ); } washing_requirements required = washing_requirements_for_volume( total_volume ); @@ -9851,7 +9851,7 @@ int iuse::wash_items( player *p, bool soft_items, bool hard_items ) return 0; } item &i = *pair.first; - total_volume += i.volume() * pair.second / ( i.count_by_charges() ? i.charges : 1 ); + total_volume += i.base_volume() * pair.second / ( i.count_by_charges() ? i.charges : 1 ); } washing_requirements required = washing_requirements_for_volume( total_volume ); From 15a374811f7ea863df15b80181220bd28211ca2a Mon Sep 17 00:00:00 2001 From: ZhilkinSerg Date: Fri, 3 Jul 2020 17:35:54 +0300 Subject: [PATCH 072/420] Move controlling_vehicle to Character class --- src/character.h | 2 ++ src/player.h | 2 -- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/character.h b/src/character.h index e6177d039a240..b8a17d3bd0e18 100644 --- a/src/character.h +++ b/src/character.h @@ -346,6 +346,8 @@ class Character : public Creature, public visitable bool in_species( const species_id &spec ) const override; // Turned to false for simulating NPCs on distant missions so they don't drop all their gear in sight bool death_drops; + // Is currently in control of a vehicle + bool controlling_vehicle = false; enum class comfort_level : int { impossible = -999, diff --git a/src/player.h b/src/player.h index 8816647beb6bf..322d0b825f63e 100644 --- a/src/player.h +++ b/src/player.h @@ -674,8 +674,6 @@ class player : public Character // ---------------VALUES----------------- tripoint view_offset; - // Is currently in control of a vehicle - bool controlling_vehicle; // Relative direction of a grab, add to posx, posy to get the coordinates of the grabbed thing. tripoint grab_point; int volume; From 3f3bf722536f1f2875060c9876a4c205a3e41acd Mon Sep 17 00:00:00 2001 From: Kevin Granade Date: Sat, 4 Jul 2020 02:49:44 +0000 Subject: [PATCH 073/420] Global reference migration part 16 --- src/game.cpp | 2 +- src/game.h | 2 +- src/trapfunc.cpp | 212 ++++++++++++++++++++++++++--------------------- 3 files changed, 118 insertions(+), 98 deletions(-) diff --git a/src/game.cpp b/src/game.cpp index 13fa89d556037..985f77511c887 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -11117,7 +11117,7 @@ void game::vertical_notes( int z_before, int z_after ) } } -point game::update_map( player &p ) +point game::update_map( Character &p ) { point p2( p.posx(), p.posy() ); return update_map( p2.x, p2.y ); diff --git a/src/game.h b/src/game.h index d3b60f04c8daa..8c0d0fd6d1319 100644 --- a/src/game.h +++ b/src/game.h @@ -554,7 +554,7 @@ class game Creature *is_hostile_very_close(); // Handles shifting coordinates transparently when moving between submaps. // Helper to make calling with a player pointer less verbose. - point update_map( player &p ); + point update_map( Character &p ); point update_map( int &x, int &y ); void update_overmap_seen(); // Update which overmap tiles we can see diff --git a/src/trapfunc.cpp b/src/trapfunc.cpp index 96b57b3b0af00..7e8312606b774 100644 --- a/src/trapfunc.cpp +++ b/src/trapfunc.cpp @@ -67,7 +67,7 @@ static const mtype_id mon_shadow_snake( "mon_shadow_snake" ); static float pit_effectiveness( const tripoint &p ) { units::volume corpse_volume = 0_ml; - for( item &pit_content : g->m.i_at( p ) ) { + for( item &pit_content : get_map().i_at( p ) ) { if( pit_content.is_corpse() ) { corpse_volume += pit_content.volume(); } @@ -98,7 +98,7 @@ bool trapfunc::bubble( const tripoint &p, Creature *c, item * ) } } sounds::sound( p, 18, sounds::sound_t::alarm, _( "Pop!" ), false, "trap", "bubble_wrap" ); - g->m.remove_trap( p ); + get_map().remove_trap( p ); return true; } @@ -124,7 +124,7 @@ bool trapfunc::glass( const tripoint &p, Creature *c, item * ) } } sounds::sound( p, 10, sounds::sound_t::combat, _( "glass cracking!" ), false, "trap", "glass" ); - g->m.remove_trap( p ); + get_map().remove_trap( p ); return true; } @@ -147,7 +147,8 @@ bool trapfunc::beartrap( const tripoint &p, Creature *c, item * ) return false; } sounds::sound( p, 8, sounds::sound_t::combat, _( "SNAP!" ), false, "trap", "bear_trap" ); - g->m.remove_trap( p ); + map &here = get_map(); + here.remove_trap( p ); if( c != nullptr ) { // What got hit? const bodypart_id hit = one_in( 2 ) ? bodypart_id( "leg_l" ) : bodypart_id( "leg_r" ); @@ -176,7 +177,7 @@ bool trapfunc::beartrap( const tripoint &p, Creature *c, item * ) } c->check_dead_state(); } else { - g->m.spawn_item( p, "beartrap" ); + here.spawn_item( p, "beartrap" ); } return true; } @@ -197,7 +198,7 @@ bool trapfunc::board( const tripoint &, Creature *c, item * ) if( z != nullptr ) { if( z->has_effect( effect_ridden ) ) { add_msg( m_warning, _( "Your %s stepped on a spiked board!" ), c->get_name() ); - g->u.moves -= 80; + get_player_character().moves -= 80; } else { z->moves -= 80; } @@ -232,7 +233,7 @@ bool trapfunc::caltrops( const tripoint &, Creature *c, item * ) if( z != nullptr ) { if( z->has_effect( effect_ridden ) ) { add_msg( m_warning, _( "Your %s steps on a sharp metal caltrop!" ), c->get_name() ); - g->u.moves -= 80; + get_player_character().moves -= 80; } else { z->moves -= 80; } @@ -267,12 +268,12 @@ bool trapfunc::caltrops_glass( const tripoint &p, Creature *c, item * ) c->deal_damage( nullptr, bodypart_id( "foot_r" ), damage_instance( DT_CUT, rng( 9, 30 ) ) ); } c->check_dead_state(); - if( g->u.sees( p ) ) { + if( get_player_character().sees( p ) ) { add_msg( _( "The shards shatter!" ) ); sounds::sound( p, 8, sounds::sound_t::combat, _( "glass cracking!" ), false, "trap", "glass_caltrops" ); } - g->m.remove_trap( p ); + get_map().remove_trap( p ); return true; } @@ -289,21 +290,24 @@ bool trapfunc::tripwire( const tripoint &p, Creature *c, item * ) _( " trips over a tripwire!" ) ); monster *z = dynamic_cast( c ); player *n = dynamic_cast( c ); + + Character &player_character = get_player_character(); + map &here = get_map(); if( z != nullptr ) { if( z->has_effect( effect_ridden ) ) { add_msg( m_bad, _( "Your %s trips over a tripwire!" ), z->get_name() ); std::vector valid; - for( const tripoint &jk : g->m.points_in_radius( p, 1 ) ) { + for( const tripoint &jk : here.points_in_radius( p, 1 ) ) { if( g->is_empty( jk ) ) { valid.push_back( jk ); } } if( !valid.empty() ) { - g->u.setpos( random_entry( valid ) ); - z->setpos( g->u.pos() ); + player_character.setpos( random_entry( valid ) ); + z->setpos( player_character.pos() ); } - g->u.moves -= 150; - g->update_map( g->u ); + player_character.moves -= 150; + g->update_map( player_character ); } else { z->stumble(); } @@ -312,7 +316,7 @@ bool trapfunc::tripwire( const tripoint &p, Creature *c, item * ) } } else if( n != nullptr ) { std::vector valid; - for( const tripoint &jk : g->m.points_in_radius( p, 1 ) ) { + for( const tripoint &jk : here.points_in_radius( p, 1 ) ) { if( g->is_empty( jk ) ) { valid.push_back( jk ); } @@ -321,8 +325,8 @@ bool trapfunc::tripwire( const tripoint &p, Creature *c, item * ) n->setpos( random_entry( valid ) ); } n->moves -= 150; - if( c == &g->u ) { - g->update_map( g->u ); + if( c->is_avatar() ) { + g->update_map( player_character ); } if( !n->is_mounted() ) { ///\EFFECT_DEX decreases chance of taking damage from a tripwire trap @@ -387,7 +391,7 @@ bool trapfunc::crossbow( const tripoint &p, Creature *c, item * ) _( " dodges the shot!" ) ); } } else if( z != nullptr ) { - bool seen = g->u.sees( *z ); + bool seen = get_player_character().sees( *z ); int chance = 0; // adapted from shotgun code - chance of getting hit depends on size switch( z->type->size ) { @@ -419,19 +423,21 @@ bool trapfunc::crossbow( const tripoint &p, Creature *c, item * ) } c->check_dead_state(); } - g->m.remove_trap( p ); - g->m.spawn_item( p, "crossbow" ); - g->m.spawn_item( p, "string_36" ); + map &here = get_map(); + here.remove_trap( p ); + here.spawn_item( p, "crossbow" ); + here.spawn_item( p, "string_36" ); if( add_bolt ) { - g->m.spawn_item( p, "bolt_steel", 1, 1 ); + here.spawn_item( p, "bolt_steel", 1, 1 ); } return true; } bool trapfunc::shotgun( const tripoint &p, Creature *c, item * ) { + map &here = get_map(); sounds::sound( p, 60, sounds::sound_t::combat, _( "Kerblam!" ), false, "fire_gun", - g->m.tr_at( p ).loadid == tr_shotgun_1 ? "shotgun_s" : "shotgun_d" ); + here.tr_at( p ).loadid == tr_shotgun_1 ? "shotgun_s" : "shotgun_d" ); int shots = 1; if( c != nullptr ) { if( c->has_effect( effect_ridden ) ) { @@ -444,7 +450,7 @@ bool trapfunc::shotgun( const tripoint &p, Creature *c, item * ) if( n != nullptr ) { ///\EFFECT_STR_MAX increases chance of two shots from shotgun trap shots = ( one_in( 8 ) || one_in( 20 - n->str_max ) ? 2 : 1 ); - if( g->m.tr_at( p ).loadid != tr_shotgun_2 ) { + if( here.tr_at( p ).loadid != tr_shotgun_2 ) { shots = 1; } ///\EFFECT_DODGE reduces chance of being hit by shotgun trap @@ -486,7 +492,7 @@ bool trapfunc::shotgun( const tripoint &p, Creature *c, item * ) _( " dodges the shot!" ) ); } } else if( z != nullptr ) { - bool seen = g->u.sees( *z ); + bool seen = get_player_character().sees( *z ); int chance = 0; switch( z->type->size ) { case creature_size::tiny: @@ -506,7 +512,7 @@ bool trapfunc::shotgun( const tripoint &p, Creature *c, item * ) break; } shots = ( one_in( 8 ) || one_in( chance ) ? 2 : 1 ); - if( g->m.tr_at( p ).loadid != tr_shotgun_2 ) { + if( here.tr_at( p ).loadid != tr_shotgun_2 ) { shots = 1; } if( seen ) { @@ -518,9 +524,9 @@ bool trapfunc::shotgun( const tripoint &p, Creature *c, item * ) c->check_dead_state(); } - g->m.spawn_item( p, g->m.tr_at( p ).loadid == tr_shotgun_1 ? "shotgun_s" : "shotgun_d" ); - g->m.spawn_item( p, "string_36" ); - g->m.remove_trap( p ); + here.spawn_item( p, here.tr_at( p ).loadid == tr_shotgun_1 ? "shotgun_s" : "shotgun_d" ); + here.spawn_item( p, "string_36" ); + here.remove_trap( p ); return true; } @@ -545,7 +551,7 @@ bool trapfunc::blade( const tripoint &, Creature *c, item * ) bool trapfunc::snare_light( const tripoint &p, Creature *c, item * ) { sounds::sound( p, 2, sounds::sound_t::combat, _( "Snap!" ), false, "trap", "snare" ); - g->m.remove_trap( p ); + get_map().remove_trap( p ); if( c == nullptr ) { return false; } @@ -571,7 +577,7 @@ bool trapfunc::snare_light( const tripoint &p, Creature *c, item * ) bool trapfunc::snare_heavy( const tripoint &p, Creature *c, item * ) { sounds::sound( p, 4, sounds::sound_t::combat, _( "Snap!" ), false, "trap", "snare" ); - g->m.remove_trap( p ); + get_map().remove_trap( p ); if( c == nullptr ) { return false; } @@ -622,7 +628,7 @@ bool trapfunc::landmine( const tripoint &p, Creature *c, item * ) _( " triggers a land mine!" ) ); } explosion_handler::explosion( p, 18, 0.5, false, 8 ); - g->m.remove_trap( p ); + get_map().remove_trap( p ); return true; } @@ -633,7 +639,7 @@ bool trapfunc::boobytrap( const tripoint &p, Creature *c, item * ) _( " triggers a booby trap!" ) ); } explosion_handler::explosion( p, 18, 0.6, false, 12 ); - g->m.remove_trap( p ); + get_map().remove_trap( p ); return true; } @@ -644,10 +650,10 @@ bool trapfunc::telepad( const tripoint &p, Creature *c, item * ) if( c == nullptr ) { return false; } - if( c == &g->u ) { + if( c->is_avatar() ) { c->add_msg_if_player( m_warning, _( "The air shimmers around you…" ) ); } else { - if( g->u.sees( p ) ) { + if( get_player_character().sees( p ) ) { add_msg( _( "The air shimmers around %s…" ), c->disp_name() ); } } @@ -657,7 +663,7 @@ bool trapfunc::telepad( const tripoint &p, Creature *c, item * ) bool trapfunc::goo( const tripoint &p, Creature *c, item * ) { - g->m.remove_trap( p ); + get_map().remove_trap( p ); if( c == nullptr ) { return false; } @@ -677,7 +683,7 @@ bool trapfunc::goo( const tripoint &p, Creature *c, item * ) return true; } else if( z != nullptr ) { if( z->has_effect( effect_ridden ) ) { - g->u.forced_dismount(); + get_player_character().forced_dismount(); } //All monsters except for blobs get a speed decrease if( z->type->id != mon_blob ) { @@ -703,6 +709,7 @@ bool trapfunc::dissector( const tripoint &p, Creature *c, item * ) return false; } monster *z = dynamic_cast( c ); + bool player_sees = get_player_character().sees( p ); if( z != nullptr ) { if( z->type->in_species( species_ROBOT ) ) { //The monster is a robot. So the dissector should not try to dissect the monsters flesh. @@ -726,7 +733,7 @@ bool trapfunc::dissector( const tripoint &p, Creature *c, item * ) ch->deal_damage( nullptr, bodypart_id( "leg_r" ), damage_instance( DT_CUT, 12 ) ); ch->deal_damage( nullptr, bodypart_id( "foot_l" ), damage_instance( DT_CUT, 10 ) ); ch->deal_damage( nullptr, bodypart_id( "foot_r" ), damage_instance( DT_CUT, 10 ) ); - if( g->u.sees( p ) ) { + if( player_sees ) { ch->add_msg_player_or_npc( m_bad, _( "Electrical beams emit from the floor and slice your flesh!" ), _( "Electrical beams emit from the floor and slice s flesh!" ) ); } @@ -736,7 +743,7 @@ bool trapfunc::dissector( const tripoint &p, Creature *c, item * ) //~ the sound of a dissector dissecting sounds::sound( p, 10, sounds::sound_t::combat, _( "BRZZZAP!" ), false, "trap", "dissector" ); - if( g->u.sees( p ) ) { + if( player_sees ) { add_msg( m_bad, _( "Electrical beams emit from the floor and slice the %s!" ), c->get_name() ); } c->deal_damage( nullptr, bodypart_id( "head" ), damage_instance( DT_CUT, 15 ) ); @@ -792,7 +799,7 @@ bool trapfunc::pit( const tripoint &p, Creature *c, item * ) } else if( z != nullptr ) { if( z->has_effect( effect_ridden ) ) { add_msg( m_bad, _( "Your %s falls into a pit!" ), z->get_name() ); - g->u.forced_dismount(); + get_player_character().forced_dismount(); } z->deal_damage( nullptr, bodypart_id( "leg_l" ), damage_instance( DT_BASH, eff * rng( 10, 20 ) ) ); z->deal_damage( nullptr, bodypart_id( "leg_r" ), damage_instance( DT_BASH, eff * rng( 10, 20 ) ) ); @@ -815,6 +822,7 @@ bool trapfunc::pit_spikes( const tripoint &p, Creature *c, item * ) c->add_effect( effect_in_pit, 1_turns, num_bp, true ); monster *z = dynamic_cast( c ); player *n = dynamic_cast( c ); + Character &player_character = get_player_character(); if( n != nullptr ) { int dodge = n->get_dodge(); int damage = pit_effectiveness( p ) * rng( 20, 50 ); @@ -864,20 +872,21 @@ bool trapfunc::pit_spikes( const tripoint &p, Creature *c, item * ) } else if( z != nullptr ) { if( z->has_effect( effect_ridden ) ) { add_msg( m_bad, _( "Your %s falls into a pit!" ), z->get_name() ); - g->u.forced_dismount(); + player_character.forced_dismount(); } z->deal_damage( nullptr, bodypart_id( "torso" ), damage_instance( DT_CUT, rng( 20, 50 ) ) ); } c->check_dead_state(); if( one_in( 4 ) ) { - if( g->u.sees( p ) ) { + if( player_character.sees( p ) ) { add_msg( _( "The spears break!" ) ); } - g->m.ter_set( p, t_pit ); + map &here = get_map(); + here.ter_set( p, t_pit ); // 4 spears to a pit for( int i = 0; i < 4; i++ ) { if( one_in( 3 ) ) { - g->m.spawn_item( p, "pointy_stick" ); + here.spawn_item( p, "pointy_stick" ); } } } @@ -898,6 +907,7 @@ bool trapfunc::pit_glass( const tripoint &p, Creature *c, item * ) c->add_effect( effect_in_pit, 1_turns, num_bp, true ); monster *z = dynamic_cast( c ); player *n = dynamic_cast( c ); + Character &player_character = get_player_character(); if( n != nullptr ) { int dodge = n->get_dodge(); int damage = pit_effectiveness( p ) * rng( 15, 35 ); @@ -951,20 +961,21 @@ bool trapfunc::pit_glass( const tripoint &p, Creature *c, item * ) } else if( z != nullptr ) { if( z->has_effect( effect_ridden ) ) { add_msg( m_bad, _( "Your %s falls into a pit!" ), z->get_name() ); - g->u.forced_dismount(); + player_character.forced_dismount(); } z->deal_damage( nullptr, bodypart_id( "torso" ), damage_instance( DT_CUT, rng( 20, 50 ) ) ); } c->check_dead_state(); if( one_in( 5 ) ) { - if( g->u.sees( p ) ) { + if( player_character.sees( p ) ) { add_msg( _( "The shards shatter!" ) ); } - g->m.ter_set( p, t_pit ); + map &here = get_map(); + here.ter_set( p, t_pit ); // 20 shards in a pit. for( int i = 0; i < 20; i++ ) { if( one_in( 3 ) ) { - g->m.spawn_item( p, "glass_shard" ); + here.spawn_item( p, "glass_shard" ); } } } @@ -977,7 +988,7 @@ bool trapfunc::lava( const tripoint &p, Creature *c, item * ) return false; } c->add_msg_player_or_npc( m_bad, _( "The %s burns you horribly!" ), _( "The %s burns !" ), - g->m.tername( p ) ); + get_map().tername( p ) ); monster *z = dynamic_cast( c ); player *n = dynamic_cast( c ); if( n != nullptr ) { @@ -1041,29 +1052,30 @@ static bool sinkhole_safety_roll( player *p, const itype_id &itemname, const int ///\EFFECT_THROW increases chance to attach grapnel, bullwhip, or rope when falling into a sinkhole const int throwing_skill_level = p->get_skill_level( skill_throw ); const int roll = rng( throwing_skill_level, throwing_skill_level + p->str_cur + p->dex_cur ); + map &here = get_map(); if( roll < diff ) { p->add_msg_if_player( m_bad, _( "You fail to attach it…" ) ); p->use_amount( itemname, 1 ); - g->m.spawn_item( random_neighbor( p->pos() ), itemname ); + here.spawn_item( random_neighbor( p->pos() ), itemname ); return false; } std::vector safe; - for( const tripoint &tmp : g->m.points_in_radius( p->pos(), 1 ) ) { - if( g->m.passable( tmp ) && g->m.tr_at( tmp ).loadid != tr_pit ) { + for( const tripoint &tmp : here.points_in_radius( p->pos(), 1 ) ) { + if( here.passable( tmp ) && here.tr_at( tmp ).loadid != tr_pit ) { safe.push_back( tmp ); } } if( safe.empty() ) { p->add_msg_if_player( m_bad, _( "There's nowhere to pull yourself to, and you sink!" ) ); p->use_amount( itemname, 1 ); - g->m.spawn_item( random_neighbor( p->pos() ), itemname ); + here.spawn_item( random_neighbor( p->pos() ), itemname ); return false; } else { p->add_msg_player_or_npc( m_good, _( "You pull yourself to safety!" ), _( " steps on a sinkhole, but manages to pull themselves to safety." ) ); p->setpos( random_entry( safe ) ); - if( p == &g->u ) { + if( p->is_avatar() ) { g->update_map( *p ); } @@ -1079,10 +1091,11 @@ bool trapfunc::sinkhole( const tripoint &p, Creature *c, item *i ) } monster *z = dynamic_cast( c ); player *pl = dynamic_cast( c ); + map &here = get_map(); if( z != nullptr ) { if( z->has_effect( effect_ridden ) ) { add_msg( m_bad, _( "Your %s falls into a sinkhole!" ), z->get_name() ); - g->u.forced_dismount(); + get_player_character().forced_dismount(); } } else if( pl != nullptr ) { bool success = false; @@ -1100,8 +1113,8 @@ bool trapfunc::sinkhole( const tripoint &p, Creature *c, item *i ) pl->add_msg_player_or_npc( m_warning, _( "The sinkhole collapses!" ), _( "A sinkhole under collapses!" ) ); if( success ) { - g->m.remove_trap( p ); - g->m.ter_set( p, t_pit ); + here.remove_trap( p ); + here.ter_set( p, t_pit ); return true; } pl->add_msg_player_or_npc( m_bad, _( "You fall into the sinkhole!" ), @@ -1109,8 +1122,8 @@ bool trapfunc::sinkhole( const tripoint &p, Creature *c, item *i ) } else { return false; } - g->m.remove_trap( p ); - g->m.ter_set( p, t_pit ); + here.remove_trap( p ); + here.ter_set( p, t_pit ); c->moves -= 100; pit( p, c, i ); return true; @@ -1125,18 +1138,19 @@ bool trapfunc::ledge( const tripoint &p, Creature *c, item * ) if( m != nullptr && m->flies() ) { return false; } - if( !g->m.has_zlevels() ) { - if( c == &g->u ) { + map &here = get_map(); + if( !here.has_zlevels() ) { + if( c->is_avatar() ) { add_msg( m_warning, _( "You fall down a level!" ) ); g->vertical_move( -1, true ); - if( g->u.has_trait( trait_WINGS_BIRD ) || ( one_in( 2 ) && - g->u.has_trait( trait_WINGS_BUTTERFLY ) ) ) { + if( c->has_trait( trait_WINGS_BIRD ) || ( one_in( 2 ) && + c->has_trait( trait_WINGS_BUTTERFLY ) ) ) { add_msg( _( "You flap your wings and flutter down gracefully." ) ); - } else if( g->u.has_active_bionic( bio_shock_absorber ) ) { + } else if( c->as_character()->has_active_bionic( bio_shock_absorber ) ) { add_msg( m_info, _( "You hit the ground hard, but your shock absorbers handle the impact admirably!" ) ); } else { - g->u.impact( 20, p ); + c->as_avatar()->impact( 20, p ); } } else { c->add_msg_if_npc( _( " falls down a level!" ) ); @@ -1156,7 +1170,7 @@ bool trapfunc::ledge( const tripoint &p, Creature *c, item * ) tripoint where = p; tripoint below = where; below.z--; - while( g->m.valid_move( where, below, false, true ) ) { + while( here.valid_move( where, below, false, true ) ) { where.z--; if( g->critter_at( where ) != nullptr ) { where.z++; @@ -1175,7 +1189,7 @@ bool trapfunc::ledge( const tripoint &p, Creature *c, item * ) } std::vector valid; - for( const tripoint &pt : g->m.points_in_radius( below, 1 ) ) { + for( const tripoint &pt : here.points_in_radius( below, 1 ) ) { if( g->is_empty( pt ) ) { valid.push_back( pt ); } @@ -1225,15 +1239,16 @@ bool trapfunc::ledge( const tripoint &p, Creature *c, item * ) bool trapfunc::temple_flood( const tripoint &p, Creature *c, item * ) { // Monsters and npcs are completely ignored here, should they? - if( c == &g->u ) { + if( c->is_avatar() ) { add_msg( m_warning, _( "You step on a loose tile, and water starts to flood the room!" ) ); tripoint tmp = p; int &i = tmp.x; int &j = tmp.y; + map &here = get_map(); for( i = 0; i < MAPSIZE_X; i++ ) { for( j = 0; j < MAPSIZE_Y; j++ ) { - if( g->m.tr_at( tmp ).loadid == tr_temple_flood ) { - g->m.remove_trap( tmp ); + if( here.tr_at( tmp ).loadid == tr_temple_flood ) { + here.remove_trap( tmp ); } } } @@ -1246,31 +1261,32 @@ bool trapfunc::temple_flood( const tripoint &p, Creature *c, item * ) bool trapfunc::temple_toggle( const tripoint &p, Creature *c, item * ) { // Monsters and npcs are completely ignored here, should they? - if( c == &g->u ) { + if( c->is_avatar() ) { add_msg( _( "You hear the grinding of shifting rock." ) ); - const ter_id type = g->m.ter( p ); + map &here = get_map(); + const ter_id type = here.ter( p ); tripoint tmp = p; int &i = tmp.x; int &j = tmp.y; for( i = 0; i < MAPSIZE_X; i++ ) { for( j = 0; j < MAPSIZE_Y; j++ ) { if( type == t_floor_red ) { - if( g->m.ter( tmp ) == t_rock_green ) { - g->m.ter_set( tmp, t_floor_green ); - } else if( g->m.ter( tmp ) == t_floor_green ) { - g->m.ter_set( tmp, t_rock_green ); + if( here.ter( tmp ) == t_rock_green ) { + here.ter_set( tmp, t_floor_green ); + } else if( here.ter( tmp ) == t_floor_green ) { + here.ter_set( tmp, t_rock_green ); } } else if( type == t_floor_green ) { - if( g->m.ter( tmp ) == t_rock_blue ) { - g->m.ter_set( tmp, t_floor_blue ); - } else if( g->m.ter( tmp ) == t_floor_blue ) { - g->m.ter_set( tmp, t_rock_blue ); + if( here.ter( tmp ) == t_rock_blue ) { + here.ter_set( tmp, t_floor_blue ); + } else if( here.ter( tmp ) == t_floor_blue ) { + here.ter_set( tmp, t_rock_blue ); } } else if( type == t_floor_blue ) { - if( g->m.ter( tmp ) == t_rock_red ) { - g->m.ter_set( tmp, t_floor_red ); - } else if( g->m.ter( tmp ) == t_floor_red ) { - g->m.ter_set( tmp, t_rock_red ); + if( here.ter( tmp ) == t_rock_red ) { + here.ter_set( tmp, t_floor_red ); + } else if( here.ter( tmp ) == t_floor_red ) { + here.ter_set( tmp, t_rock_red ); } } } @@ -1295,7 +1311,7 @@ bool trapfunc::glow( const tripoint &p, Creature *c, item * ) if( z->has_effect( effect_ridden ) ) { if( one_in( 3 ) ) { add_msg( m_bad, _( "You're bathed in radiation!" ) ); - g->u.irradiate( rng( 10, 30 ) ); + get_player_character().irradiate( rng( 10, 30 ) ); } else if( one_in( 4 ) ) { add_msg( m_bad, _( "A blinding flash strikes you!" ) ); explosion_handler::flashbang( p ); @@ -1342,9 +1358,10 @@ bool trapfunc::hum( const tripoint &p, Creature *, item * ) bool trapfunc::shadow( const tripoint &p, Creature *c, item * ) { - if( c != &g->u ) { + if( !c->is_avatar() ) { return false; } + map &here = get_map(); // Monsters and npcs are completely ignored here, should they? for( int tries = 0; tries < 10; tries++ ) { tripoint monp = p; @@ -1355,13 +1372,13 @@ bool trapfunc::shadow( const tripoint &p, Creature *c, item * ) monp.x = p.x + ( one_in( 2 ) ? -5 : +5 ); monp.y = p.y + rng( -5, +5 ); } - if( !g->m.sees( monp, p, 10 ) ) { + if( !here.sees( monp, p, 10 ) ) { continue; } if( monster *const spawned = g->place_critter_at( mon_shadow, monp ) ) { spawned->add_msg_if_npc( m_warning, _( "A shadow forms nearby." ) ); spawned->reset_special_rng( "DISAPPEAR" ); - g->m.remove_trap( p ); + here.remove_trap( p ); break; } } @@ -1373,15 +1390,16 @@ bool trapfunc::map_regen( const tripoint &p, Creature *c, item * ) if( c ) { player *n = dynamic_cast( c ); if( n ) { + map &here = get_map(); n->add_msg_if_player( m_warning, _( "Your surroundings shift!" ) ); const tripoint &omt_pos = n->global_omt_location(); - const std::string ®en_mapgen = g->m.tr_at( p ).map_regen_target(); - g->m.remove_trap( p ); + const std::string ®en_mapgen = here.tr_at( p ).map_regen_target(); + here.remove_trap( p ); if( !run_mapgen_update_func( regen_mapgen, omt_pos, nullptr, false ) ) { popup( _( "Failed to generate the new map" ) ); return false; } - g->m.set_transparency_cache_dirty( p.z ); + here.set_transparency_cache_dirty( p.z ); return true; } } @@ -1410,11 +1428,12 @@ bool trapfunc::cast_spell( const tripoint &p, Creature *critter, item * ) if( critter == nullptr ) { return false; } - const spell trap_spell = g->m.tr_at( p ).spell_data.get_spell( 0 ); + map &here = get_map(); + const spell trap_spell = here.tr_at( p ).spell_data.get_spell( 0 ); npc dummy; trap_spell.cast_all_effects( dummy, critter->pos() ); trap_spell.make_sound( p, 20 ); - g->m.remove_trap( p ); + here.remove_trap( p ); return true; } @@ -1422,8 +1441,9 @@ bool trapfunc::snake( const tripoint &p, Creature *, item * ) { //~ the sound a snake makes sounds::sound( p, 10, sounds::sound_t::movement, _( "ssssssss" ), false, "misc", "snake_hiss" ); + map &here = get_map(); if( one_in( 6 ) ) { - g->m.remove_trap( p ); + here.remove_trap( p ); } if( one_in( 3 ) ) { for( int tries = 0; tries < 10; tries++ ) { @@ -1435,13 +1455,13 @@ bool trapfunc::snake( const tripoint &p, Creature *, item * ) monp.x = p.x + ( one_in( 2 ) ? -5 : +5 ); monp.y = p.y + rng( -5, +5 ); } - if( !g->m.sees( monp, p, 10 ) ) { + if( !here.sees( monp, p, 10 ) ) { continue; } if( monster *const spawned = g->place_critter_at( mon_shadow_snake, monp ) ) { spawned->add_msg_if_npc( m_warning, _( "A shadowy snake forms nearby." ) ); spawned->reset_special_rng( "DISAPPEAR" ); - g->m.remove_trap( p ); + here.remove_trap( p ); break; } } From 10b21e615dccb76b782b503074de68fbca73732a Mon Sep 17 00:00:00 2001 From: Kevin Granade Date: Sat, 4 Jul 2020 02:55:20 +0000 Subject: [PATCH 074/420] Global reference migration part seventeen --- src/vehicle.cpp | 331 +++++++++++++++++++++++++++--------------------- src/vehicle.h | 4 +- 2 files changed, 190 insertions(+), 145 deletions(-) diff --git a/src/vehicle.cpp b/src/vehicle.cpp index 3b9f20be1bb85..044ab18efea90 100644 --- a/src/vehicle.cpp +++ b/src/vehicle.cpp @@ -128,37 +128,39 @@ class DefaultRemovePartHandler : public RemovePartHandler ~DefaultRemovePartHandler() override = default; void unboard( const tripoint &loc ) override { - g->m.unboard_vehicle( loc ); + get_map().unboard_vehicle( loc ); } void add_item_or_charges( const tripoint &loc, item it, bool /*permit_oob*/ ) override { - g->m.add_item_or_charges( loc, std::move( it ) ); + get_map().add_item_or_charges( loc, std::move( it ) ); } void set_transparency_cache_dirty( const int z ) override { - g->m.set_transparency_cache_dirty( z ); + get_map().set_transparency_cache_dirty( z ); } void removed( vehicle &veh, const int part ) override { + avatar &player_character = get_avatar(); // If the player is currently working on the removed part, stop them as it's futile now. - const player_activity &act = g->u.activity; + const player_activity &act = player_character.activity; + map &here = get_map(); if( act.id() == ACT_VEHICLE && act.moves_left > 0 && act.values.size() > 6 ) { - if( veh_pointer_or_null( g->m.veh_at( tripoint( act.values[0], act.values[1], - g->u.posz() ) ) ) == &veh ) { + if( veh_pointer_or_null( here.veh_at( tripoint( act.values[0], act.values[1], + player_character.posz() ) ) ) == &veh ) { if( act.values[6] >= part ) { - g->u.cancel_activity(); + player_character.cancel_activity(); add_msg( m_info, _( "The vehicle part you were working on has gone!" ) ); } } } // TODO: maybe do this for all the nearby NPCs as well? - if( g->u.get_grab_type() == object_type::VEHICLE && - g->u.grab_point == veh.global_part_pos3( part ) ) { + if( player_character.get_grab_type() == object_type::VEHICLE && + player_character.grab_point == veh.global_part_pos3( part ) ) { if( veh.parts_at_relative( veh.part( part ).mount, false ).empty() ) { add_msg( m_info, _( "The vehicle part you were holding has been destroyed!" ) ); - g->u.grab( object_type::NONE ); + player_character.grab( object_type::NONE ); } } - g->m.dirty_vehicle_list.insert( &veh ); + here.dirty_vehicle_list.insert( &veh ); } void spawn_animal_from_part( item &base, const tripoint &loc ) override { base.release_monster( loc, 1 ); @@ -258,7 +260,7 @@ vehicle::vehicle() : vehicle( vproto_id() ) vehicle::~vehicle() = default; -bool vehicle::player_in_control( const player &p ) const +bool vehicle::player_in_control( const Character &p ) const { // Debug switch to prevent vehicles from skidding // without having to place the player in them. @@ -266,7 +268,7 @@ bool vehicle::player_in_control( const player &p ) const return true; } - const optional_vpart_position vp = g->m.veh_at( p.pos() ); + const optional_vpart_position vp = get_map().veh_at( p.pos() ); if( vp && &vp->vehicle() == this && ( ( part_with_feature( vp->part_index(), "CONTROL_ANIMAL", true ) >= 0 && has_engine_type( fuel_type_animal, false ) && has_harnessed_animal() ) || @@ -278,7 +280,7 @@ bool vehicle::player_in_control( const player &p ) const return remote_controlled( p ); } -bool vehicle::remote_controlled( const player &p ) const +bool vehicle::remote_controlled( const Character &p ) const { vehicle *veh = g->remoteveh(); if( veh != this ) { @@ -702,13 +704,14 @@ void vehicle::autopilot_patrol() * in a criss-cross fashion. * in an auto-tractor, this would eventually cover the entire rectangle. */ + map &here = get_map(); // if we are close to a waypoint, then return to come back to this function next turn. if( autodrive_local_target != tripoint_zero ) { - if( rl_dist( g->m.getabs( global_pos3() ), autodrive_local_target ) <= 3 ) { + if( rl_dist( here.getabs( global_pos3() ), autodrive_local_target ) <= 3 ) { autodrive_local_target = tripoint_zero; return; } - if( !g->m.inbounds( g->m.getlocal( autodrive_local_target ) ) ) { + if( !here.inbounds( here.getlocal( autodrive_local_target ) ) ) { autodrive_local_target = tripoint_zero; is_patrolling = false; return; @@ -718,7 +721,7 @@ void vehicle::autopilot_patrol() } zone_manager &mgr = zone_manager::get_manager(); const auto &zone_src_set = mgr.get_near( zone_type_id( "VEHICLE_PATROL" ), - g->m.getabs( global_pos3() ), 60 ); + here.getabs( global_pos3() ), 60 ); if( zone_src_set.empty() ) { is_patrolling = false; return; @@ -748,8 +751,8 @@ void vehicle::autopilot_patrol() point_along, min.z ); tripoint chosen_tri = min_tri; - if( rl_dist( max_tri, g->m.getabs( global_pos3() ) ) >= rl_dist( min_tri, - g->m.getabs( global_pos3() ) ) ) { + if( rl_dist( max_tri, here.getabs( global_pos3() ) ) >= rl_dist( min_tri, + here.getabs( global_pos3() ) ) ) { chosen_tri = max_tri; } autodrive_local_target = chosen_tri; @@ -768,10 +771,11 @@ std::set vehicle::immediate_path( int rotate ) adjusted_angle = ( ( adjusted_angle + 15 / 2 ) / 15 ) * 15; tileray collision_vector; collision_vector.init( adjusted_angle ); + map &here = get_map(); point top_left_actual = global_pos3().xy() + coord_translate( front_left ); point top_right_actual = global_pos3().xy() + coord_translate( front_right ); - std::vector front_row = line_to( g->m.getabs( top_left_actual ), - g->m.getabs( top_right_actual ) ); + std::vector front_row = line_to( here.getabs( top_left_actual ), + here.getabs( top_right_actual ) ); for( const point &elem : front_row ) { for( int i = 0; i < distance_to_check; ++i ) { collision_vector.advance( i ); @@ -828,30 +832,32 @@ void vehicle::stop_autodriving() void vehicle::drive_to_local_target( const tripoint &target, bool follow_protocol ) { - if( follow_protocol && g->u.in_vehicle ) { + Character &player_character = get_player_character(); + if( follow_protocol && player_character.in_vehicle ) { stop_autodriving(); return; } refresh(); - tripoint vehpos = g->m.getabs( global_pos3() ); + map &here = get_map(); + tripoint vehpos = here.getabs( global_pos3() ); double angle = get_angle_from_targ( target ); // now we got the angle to the target, we can work out when we are heading towards disaster. // Check the tileray in the direction we need to head towards. std::set points_to_check = immediate_path( angle ); bool stop = false; for( const point &pt_elem : points_to_check ) { - point elem = g->m.getlocal( pt_elem ); + point elem = here.getlocal( pt_elem ); if( stop ) { break; } - const optional_vpart_position ovp = g->m.veh_at( tripoint( elem, sm_pos.z ) ); - if( g->m.impassable_ter_furn( tripoint( elem, sm_pos.z ) ) || ( ovp && + const optional_vpart_position ovp = here.veh_at( tripoint( elem, sm_pos.z ) ); + if( here.impassable_ter_furn( tripoint( elem, sm_pos.z ) ) || ( ovp && &ovp->vehicle() != this ) ) { stop = true; break; } - if( elem == g->u.pos().xy() ) { - if( follow_protocol || g->u.in_vehicle ) { + if( elem == player_character.pos().xy() ) { + if( follow_protocol || player_character.in_vehicle ) { continue; } else { stop = true; @@ -899,14 +905,15 @@ void vehicle::drive_to_local_target( const tripoint &target, bool follow_protoco // we really want to avoid running the player over. // If its a helicopter, we dont need to worry about airborne obstacles so much // And fuel efficiency is terrible at low speeds. - const int safe_player_follow_speed = 400 * g->u.current_movement_mode()->move_speed_mult(); + const int safe_player_follow_speed = 400 * + player_character.current_movement_mode()->move_speed_mult(); if( follow_protocol ) { if( ( ( turn_x > 0 || turn_x < 0 ) && velocity > safe_player_follow_speed ) || - rl_dist( vehpos, g->m.getabs( g->u.pos() ) ) < 7 + ( ( mount_max.y * 3 ) + 4 ) ) { + rl_dist( vehpos, here.getabs( player_character.pos() ) ) < 7 + ( ( mount_max.y * 3 ) + 4 ) ) { accel_y = 1; } if( ( velocity < std::min( safe_velocity(), safe_player_follow_speed ) && turn_x == 0 && - rl_dist( vehpos, g->m.getabs( g->u.pos() ) ) > 8 + ( ( mount_max.y * 3 ) + 4 ) ) || + rl_dist( vehpos, here.getabs( player_character.pos() ) ) > 8 + ( ( mount_max.y * 3 ) + 4 ) ) || velocity < 100 ) { accel_y = -1; } @@ -928,7 +935,7 @@ void vehicle::drive_to_local_target( const tripoint &target, bool follow_protoco double vehicle::get_angle_from_targ( const tripoint &targ ) { - tripoint vehpos = g->m.getabs( global_pos3() ); + tripoint vehpos = get_map().getabs( global_pos3() ); rl_vec2d facevec = face_vec(); point rel_pos_target = targ.xy() - vehpos.xy(); rl_vec2d targetvec = rl_vec2d( rel_pos_target.x, rel_pos_target.y ); @@ -945,12 +952,14 @@ void vehicle::do_autodrive() if( omt_path.empty() ) { stop_autodriving(); } + Character &player_character = get_player_character(); + map &here = get_map(); tripoint vehpos = global_pos3(); - tripoint veh_omt_pos = ms_to_omt_copy( g->m.getabs( vehpos ) ); + tripoint veh_omt_pos = ms_to_omt_copy( here.getabs( vehpos ) ); // we're at or close to the waypoint, pop it out and look for the next one. - if( ( is_autodriving && !g->u.omt_path.empty() && !omt_path.empty() ) && + if( ( is_autodriving && !player_character.omt_path.empty() && !omt_path.empty() ) && veh_omt_pos == omt_path.back() ) { - g->u.omt_path.pop_back(); + player_character.omt_path.pop_back(); omt_path.pop_back(); } if( omt_path.empty() ) { @@ -983,8 +992,8 @@ void vehicle::do_autodrive() tripoint global_a = tripoint( veh_omt_pos.x * ( 2 * SEEX ), veh_omt_pos.y * ( 2 * SEEY ), veh_omt_pos.z ); tripoint autodrive_temp_target = ( global_a + tripoint( side, - sm_pos.z ) - g->m.getabs( vehpos ) ) + vehpos; - autodrive_local_target = g->m.getabs( autodrive_temp_target ); + sm_pos.z ) - here.getabs( vehpos ) ) + vehpos; + autodrive_local_target = here.getabs( autodrive_temp_target ); drive_to_local_target( autodrive_local_target, false ); } @@ -1058,7 +1067,7 @@ void vehicle::smash( map &m, float hp_percent_loss_min, float hp_percent_loss_ma // This is a heuristic: we just assume the default handler is good enough when called // on the main game map. And assume that we run from some mapgen code if called on // another instance. - if( g && &g->m == &m ) { + if( g && &get_map() == &m ) { handler_ptr = std::make_unique(); } else { handler_ptr = std::make_unique( m ); @@ -1226,12 +1235,13 @@ int vehicle::part_vpower_w( const int index, const bool at_full_hp ) const } } ///\EFFECT_STR increases power produced for MUSCLE_* vehicles - pwr += ( g->u.str_cur - 8 ) * part_info( index ).engine_muscle_power_factor(); + pwr += ( get_player_character().str_cur - 8 ) * part_info( index ).engine_muscle_power_factor(); /// wind-powered vehicles have differing power depending on wind direction if( vp.info().fuel_type == fuel_type_wind ) { - int windpower = g->weather.windspeed; + weather_manager &weather = get_weather(); + int windpower = weather.windspeed; rl_vec2d windvec; - double raddir = ( ( g->weather.winddirection + 180 ) % 360 ) * ( M_PI / 180 ); + double raddir = ( ( weather.winddirection + 180 ) % 360 ) * ( M_PI / 180 ); windvec = windvec.normalized(); windvec.y = -std::cos( raddir ); windvec.x = std::sin( raddir ); @@ -1681,6 +1691,7 @@ int vehicle::install_part( const point &dp, const vehicle_part &new_part ) bool vehicle::try_to_rack_nearby_vehicle( const std::vector> &list_of_racks ) { + map &here = get_map(); for( const auto &this_bike_rack : list_of_racks ) { std::vector carry_vehs; carry_vehs.assign( 4, nullptr ); @@ -1693,7 +1704,7 @@ bool vehicle::try_to_rack_nearby_vehicle( const std::vector> &l int i = 0; for( const point &offset : four_cardinal_directions ) { tripoint search_pos( rack_pos + offset ); - test_veh = veh_pointer_or_null( g->m.veh_at( search_pos ) ); + test_veh = veh_pointer_or_null( here.veh_at( search_pos ) ); if( test_veh == nullptr || test_veh == this ) { continue; } else if( test_veh != carry_vehs[ i ] ) { @@ -1850,11 +1861,12 @@ bool vehicle::merge_rackable_vehicle( vehicle *carry_veh, const std::vector // update when we next interact with them zones_dirty = true; + map &here = get_map(); //~ %1$s is the vehicle being loaded onto the bicycle rack add_msg( _( "You load the %1$s on the rack" ), carry_veh->name ); - g->m.destroy_vehicle( carry_veh ); - g->m.dirty_vehicle_list.insert( this ); - g->m.set_transparency_cache_dirty( sm_pos.z ); + here.destroy_vehicle( carry_veh ); + here.dirty_vehicle_list.insert( this ); + here.set_transparency_cache_dirty( sm_pos.z ); refresh(); } else { //~ %1$s is the vehicle being loaded onto the bicycle rack @@ -2094,7 +2106,8 @@ bool vehicle::remove_carried_vehicle( const std::vector &carried_parts ) new_dir = 180; } } - vehicle *new_vehicle = g->m.add_vehicle( vproto_id( "none" ), new_pos3, new_dir ); + map &here = get_map(); + vehicle *new_vehicle = here.add_vehicle( vproto_id( "none" ), new_pos3, new_dir ); if( new_vehicle == nullptr ) { add_msg( m_debug, "Unable to unload bike rack, host face %d, new_dir %d!", face.dir(), new_dir ); return false; @@ -2154,7 +2167,7 @@ bool vehicle::remove_carried_vehicle( const std::vector &carried_parts ) new_vehicle->toggle_tracking(); //turn on tracking for our newly created vehicle new_vehicle->remove_tracked_flag(); //remove our tracking flags now that the vehicle isn't carried } - g->m.dirty_vehicle_list.insert( this ); + here.dirty_vehicle_list.insert( this ); part_removal_cleanup(); } else { //~ %s is the vehicle being loaded onto the bicycle rack @@ -2275,6 +2288,7 @@ bool vehicle::split_vehicles( const std::vector> &new_vehs, { bool did_split = false; size_t i = 0; + map &here = get_map(); for( i = 0; i < new_vehs.size(); i ++ ) { std::vector split_parts = new_vehs[ i ]; if( split_parts.empty() ) { @@ -2307,7 +2321,7 @@ bool vehicle::split_vehicles( const std::vector> &new_vehs, } new_v_pos3 = global_part_pos3( parts[ split_part0 ] ); mnt_offset = parts[ split_part0 ].mount; - new_vehicle = g->m.add_vehicle( vproto_id( "none" ), new_v_pos3, face.dir() ); + new_vehicle = here.add_vehicle( vproto_id( "none" ), new_v_pos3, face.dir() ); if( new_vehicle == nullptr ) { // the split part was out of the map bounds. continue; @@ -2401,8 +2415,8 @@ bool vehicle::split_vehicles( const std::vector> &new_vehs, // time we interact with them new_vehicle->zones_dirty = true; - g->m.dirty_vehicle_list.insert( new_vehicle ); - g->m.set_transparency_cache_dirty( sm_pos.z ); + here.dirty_vehicle_list.insert( new_vehicle ); + here.set_transparency_cache_dirty( sm_pos.z ); if( !new_labels.empty() ) { new_vehicle->labels = new_labels; } @@ -2993,13 +3007,14 @@ int vehicle::part_displayed_at( const point &dp ) const return -1; } - bool in_vehicle = g->u.in_vehicle; + Character &player_character = get_player_character(); + bool in_vehicle = player_character.in_vehicle; if( in_vehicle ) { // They're in a vehicle, but are they in /this/ vehicle? std::vector psg_parts = boarded_parts(); in_vehicle = false; for( auto &psg_part : psg_parts ) { - if( get_passenger( psg_part ) == &g->u ) { + if( get_passenger( psg_part ) == &player_character ) { in_vehicle = true; break; } @@ -3245,18 +3260,20 @@ int vehicle::fuel_left( const itype_id &ftype, bool recurse ) const //muscle engines have infinite fuel if( ftype == fuel_type_muscle ) { + Character &player_character = get_player_character(); // TODO: Allow NPCs to power those - const optional_vpart_position vp = g->m.veh_at( g->u.pos() ); - bool player_controlling = player_in_control( g->u ); + const optional_vpart_position vp = get_map().veh_at( player_character.pos() ); + bool player_controlling = player_in_control( player_character ); //if the engine in the player tile is a muscle engine, and player is controlling vehicle if( vp && &vp->vehicle() == this && player_controlling ) { const int p = avail_part_with_feature( vp->part_index(), VPFLAG_ENGINE, true ); if( p >= 0 && is_part_on( p ) && part_info( p ).fuel_type == fuel_type_muscle ) { //Broken limbs prevent muscle engines from working - if( ( part_info( p ).has_flag( "MUSCLE_LEGS" ) && ( g->u.get_working_leg_count() >= 2 ) ) || + if( ( part_info( p ).has_flag( "MUSCLE_LEGS" ) && + ( player_character.get_working_leg_count() >= 2 ) ) || ( part_info( p ).has_flag( "MUSCLE_ARMS" ) && - ( g->u.get_working_arm_count() >= 2 ) ) ) { + ( player_character.get_working_arm_count() >= 2 ) ) ) { fl += 10; } } @@ -3453,10 +3470,11 @@ bool vehicle::can_use_rails() const if( !can_use ) { return false; } + map &here = get_map(); bool is_wheel_on_rail = false; for( int part_index : rail_wheelcache ) { // at least one wheel should be on track - if( g->m.has_flag_ter_or_furn( TFLAG_RAIL, global_part_pos3( part_index ) ) ) { + if( here.has_flag_ter_or_furn( TFLAG_RAIL, global_part_pos3( part_index ) ) ) { is_wheel_on_rail = true; break; } @@ -3696,14 +3714,16 @@ int vehicle::safe_velocity( const bool fueled ) const bool vehicle::do_environmental_effects() { bool needed = false; + map &here = get_map(); // check for smoking parts for( const vpart_reference &vp : get_all_parts() ) { /* Only lower blood level if: * - The part is outside. * - The weather is any effect that would cause the player to be wet. */ - if( vp.part().blood > 0 && g->m.is_outside( vp.pos() ) ) { + if( vp.part().blood > 0 && here.is_outside( vp.pos() ) ) { needed = true; - if( g->weather.weather >= WEATHER_LIGHT_DRIZZLE && g->weather.weather <= WEATHER_ACID_RAIN ) { + weather_manager &weather = get_weather(); + if( weather.weather >= WEATHER_LIGHT_DRIZZLE && weather.weather <= WEATHER_ACID_RAIN ) { vp.part().blood--; } } @@ -3718,7 +3738,7 @@ void vehicle::spew_field( double joules, int part, field_type_id type, int inten } intensity = std::max( joules / 10000, static_cast( intensity ) ); const tripoint dest = exhaust_dest( part ); - g->m.mod_field_intensity( dest, type, intensity ); + get_map().mod_field_intensity( dest, type, intensity ); } /** @@ -4096,7 +4116,7 @@ bool vehicle::has_sufficient_rotorlift() const bool vehicle::is_rotorcraft() const { return ( has_part( "ROTOR" ) || has_part( "ROTOR_SIMPLE" ) ) && has_sufficient_rotorlift() && - player_in_control( g->u ); + player_in_control( get_player_character() ); } bool vehicle::is_flyable() const @@ -4420,7 +4440,7 @@ float vehicle::steering_effectiveness() const float vehicle::handling_difficulty() const { const float steer = std::max( 0.0f, steering_effectiveness() ); - const float ktraction = k_traction( g->m.vehicle_wheel_traction( *this ) ); + const float ktraction = k_traction( get_map().vehicle_wheel_traction( *this ) ); const float aligned = std::max( 0.0f, 1.0f - ( face_vec() - dir_vec() ).magnitude() ); // TestVehicle: perfect steering, moving on road at 100 mph (25 tiles per turn) = 0.0 @@ -4502,7 +4522,9 @@ void vehicle::consume_fuel( int load, const int t_seconds, bool skip_electric ) fuel_remainder[ ft ] = -amnt_precise_j; } } - if( load > 0 && fuel_left( fuel_type_muscle ) > 0 && g->u.has_effect( effect_winded ) ) { + Character &player_character = get_player_character(); + if( load > 0 && fuel_left( fuel_type_muscle ) > 0 && + player_character.has_effect( effect_winded ) ) { cruise_velocity = 0; if( velocity == 0 ) { stop(); @@ -4510,7 +4532,7 @@ void vehicle::consume_fuel( int load, const int t_seconds, bool skip_electric ) } // we want this to update the activity level whenever the engine is running if( load > 0 && fuel_left( fuel_type_muscle ) > 0 ) { - g->u.increase_activity_level( ACTIVE_EXERCISE ); + player_character.increase_activity_level( ACTIVE_EXERCISE ); //do this as a function of current load // But only if the player is actually there! int eff_load = load / 10; @@ -4520,28 +4542,30 @@ void vehicle::consume_fuel( int load, const int t_seconds, bool skip_electric ) base_burn = std::max( eff_load / 3, base_burn ); //charge bionics when using muscle engine const item muscle( "muscle" ); - for( const bionic_id &bid : g->u.get_bionic_fueled_with( muscle ) ) { - if( g->u.has_active_bionic( bid ) ) { // active power gen + for( const bionic_id &bid : player_character.get_bionic_fueled_with( muscle ) ) { + if( player_character.has_active_bionic( bid ) ) { // active power gen // more pedaling = more power - g->u.mod_power_level( units::from_kilojoule( muscle.fuel_energy() ) * bid->fuel_efficiency * - ( load / 1000 ) ); + player_character.mod_power_level( units::from_kilojoule( muscle.fuel_energy() ) * + bid->fuel_efficiency * + ( load / 1000 ) ); mod += eff_load / 5; } else { // passive power gen - g->u.mod_power_level( units::from_kilojoule( muscle.fuel_energy() ) * bid->passive_fuel_efficiency * - ( load / 1000 ) ); + player_character.mod_power_level( units::from_kilojoule( muscle.fuel_energy() ) * + bid->passive_fuel_efficiency * + ( load / 1000 ) ); mod += eff_load / 10; } } // decreased stamina burn scalable with load - if( g->u.has_active_bionic( bio_jointservo ) ) { - g->u.mod_power_level( units::from_kilojoule( -std::max( eff_load / 20, 1 ) ) ); + if( player_character.has_active_bionic( bio_jointservo ) ) { + player_character.mod_power_level( units::from_kilojoule( -std::max( eff_load / 20, 1 ) ) ); mod -= std::max( eff_load / 5, 5 ); } if( one_in( 1000 / load ) && one_in( 10 ) ) { - g->u.mod_thirst( 1 ); - g->u.mod_fatigue( 1 ); + player_character.mod_thirst( 1 ); + player_character.mod_fatigue( 1 ); } - g->u.mod_stamina( -( base_burn + mod ) ); + player_character.mod_stamina( -( base_burn + mod ) ); add_msg( m_debug, "Load: %d", load ); add_msg( m_debug, "Mod: %d", mod ); add_msg( m_debug, "Burn: %d", -( base_burn + mod ) ); @@ -4603,12 +4627,13 @@ int vehicle::total_engine_epower_w() const int vehicle::total_solar_epower_w() const { int epower_w = 0; + map &here = get_map(); for( int part : solar_panels ) { if( parts[ part ].is_unavailable() ) { continue; } - if( !is_sm_tile_outside( g->m.getabs( global_part_pos3( part ) ) ) ) { + if( !is_sm_tile_outside( here.getabs( global_part_pos3( part ) ) ) ) { continue; } @@ -4624,21 +4649,23 @@ int vehicle::total_solar_epower_w() const int vehicle::total_wind_epower_w() const { - const oter_id &cur_om_ter = overmap_buffer.ter( ms_to_omt_copy( g->m.getabs( global_pos3() ) ) ); - const w_point weatherPoint = *g->weather.weather_precise; + map &here = get_map(); + const oter_id &cur_om_ter = overmap_buffer.ter( ms_to_omt_copy( here.getabs( global_pos3() ) ) ); + weather_manager &weather = get_weather(); + const w_point weatherPoint = *weather.weather_precise; int epower_w = 0; for( int part : wind_turbines ) { if( parts[ part ].is_unavailable() ) { continue; } - if( !is_sm_tile_outside( g->m.getabs( global_part_pos3( part ) ) ) ) { + if( !is_sm_tile_outside( here.getabs( global_part_pos3( part ) ) ) ) { continue; } - double windpower = get_local_windpower( g->weather.windspeed, cur_om_ter, global_part_pos3( part ), - g->weather.winddirection, false ); - if( windpower <= ( g->weather.windspeed / 10.0 ) ) { + double windpower = get_local_windpower( weather.windspeed, cur_om_ter, global_part_pos3( part ), + weather.winddirection, false ); + if( windpower <= ( weather.windspeed / 10.0 ) ) { continue; } epower_w += part_epower_w( part ) * windpower; @@ -4649,12 +4676,13 @@ int vehicle::total_wind_epower_w() const int vehicle::total_water_wheel_epower_w() const { int epower_w = 0; + map &here = get_map(); for( int part : water_wheels ) { if( parts[ part ].is_unavailable() ) { continue; } - if( !is_sm_tile_over_water( g->m.getabs( global_part_pos3( part ) ) ) ) { + if( !is_sm_tile_over_water( here.getabs( global_part_pos3( part ) ) ) ) { continue; } @@ -4714,6 +4742,7 @@ void vehicle::power_parts() int delta_energy_bat = power_to_energy_bat( epower, 1_turns ); int storage_deficit_bat = std::max( 0, fuel_capacity( fuel_type_battery ) - fuel_left( fuel_type_battery ) - delta_energy_bat ); + Character &player_character = get_player_character(); // Reactors trigger only on demand. If we'd otherwise run out of power, see // if we can spin up the reactors. if( !reactors.empty() && storage_deficit_bat > 0 ) { @@ -4758,7 +4787,7 @@ void vehicle::power_parts() for( auto &elem : reactors ) { parts[ elem ].enabled = false; } - if( player_in_control( g->u ) || g->u.sees( global_pos3() ) ) { + if( player_in_control( player_character ) || player_character.sees( global_pos3() ) ) { add_msg( _( "The %s's reactor dies!" ), name ); } } @@ -4792,13 +4821,13 @@ void vehicle::power_parts() is_alarm_on = false; camera_on = false; - if( player_in_control( g->u ) || g->u.sees( global_pos3() ) ) { + if( player_in_control( player_character ) || player_character.sees( global_pos3() ) ) { add_msg( _( "The %s's battery dies!" ), name ); } if( engine_epower < 0 ) { // Not enough epower to run gas engine ignition system engine_on = false; - if( player_in_control( g->u ) || g->u.sees( global_pos3() ) ) { + if( player_in_control( player_character ) || player_character.sees( global_pos3() ) ) { add_msg( _( "The %s's engine dies!" ), name ); } } @@ -4807,9 +4836,10 @@ void vehicle::power_parts() vehicle *vehicle::find_vehicle( const tripoint &where ) { + map &here = get_map(); // Is it in the reality bubble? - tripoint veh_local = g->m.getlocal( where ); - if( const optional_vpart_position vp = g->m.veh_at( veh_local ) ) { + tripoint veh_local = here.getlocal( where ); + if( const optional_vpart_position vp = here.veh_at( veh_local ) ) { return &vp->vehicle(); } @@ -4863,7 +4893,7 @@ int vehicle::traverse_vehicle_graph( Vehicle *start_veh, int amount, Func action visited_vehs.insert( current_veh ); connected_vehs.pop(); - g->u.add_msg_if_player( m_debug, "Traversing graph with %d power", amount ); + add_msg( m_debug, "Traversing graph with %d power", amount ); for( auto &p : current_veh->loose_parts ) { if( !current_veh->part_info( p ).has_flag( "POWER_TRANSFER" ) ) { @@ -4884,12 +4914,11 @@ int vehicle::traverse_vehicle_graph( Vehicle *start_veh, int amount, Func action connected_vehs.push( std::make_pair( target_veh, target_loss ) ); float loss_amount = ( static_cast( amount ) * static_cast( target_loss ) ) / 100; - g->u.add_msg_if_player( m_debug, "Visiting remote %p with %d power (loss %f, which is %d percent)", - static_cast( target_veh ), amount, loss_amount, target_loss ); + add_msg( m_debug, "Visiting remote %p with %d power (loss %f, which is %d percent)", + static_cast( target_veh ), amount, loss_amount, target_loss ); amount = action( target_veh, amount, static_cast( loss_amount ) ); - g->u.add_msg_if_player( m_debug, "After remote %p, %d power", static_cast( target_veh ), - amount ); + add_msg( m_debug, "After remote %p, %d power", static_cast( target_veh ), amount ); if( amount < 1 ) { break; // No more charge to donate away. @@ -4929,7 +4958,7 @@ int vehicle::charge_battery( int amount, bool include_other_vehicles ) } auto charge_visitor = []( vehicle * veh, int amount, int lost ) { - g->u.add_msg_if_player( m_debug, "CH: %d", amount - lost ); + add_msg( m_debug, "CH: %d", amount - lost ); return veh->charge_battery( amount - lost, false ); }; @@ -4966,7 +4995,7 @@ int vehicle::discharge_battery( int amount, bool recurse ) } auto discharge_visitor = []( vehicle * veh, int amount, int lost ) { - g->u.add_msg_if_player( m_debug, "CH: %d", amount + lost ); + add_msg( m_debug, "CH: %d", amount + lost ); return veh->discharge_battery( amount + lost, false ); }; if( amount > 0 && recurse ) { // need more power! @@ -4994,6 +5023,7 @@ void vehicle::do_engine_damage( size_t e, int strain ) void vehicle::idle( bool on_map ) { power_parts(); + Character &player_character = get_player_character(); if( engine_on && total_power_w() > 0 ) { int idle_rate = alternator_load; if( idle_rate < 10 ) { @@ -5012,7 +5042,7 @@ void vehicle::idle( bool on_map ) noise_and_smoke( idle_rate, 1_turns ); } } else { - if( engine_on && g->u.sees( global_pos3() ) && + if( engine_on && player_character.sees( global_pos3() ) && ( has_engine_type_not( fuel_type_muscle, true ) && has_engine_type_not( fuel_type_animal, true ) && has_engine_type_not( fuel_type_wind, true ) && has_engine_type_not( fuel_type_mana, true ) ) ) { add_msg( _( "The %s's engine dies!" ), name ); @@ -5020,9 +5050,9 @@ void vehicle::idle( bool on_map ) engine_on = false; } - if( !warm_enough_to_plant( g->u.pos() ) ) { + if( !warm_enough_to_plant( player_character.pos() ) ) { for( const vpart_reference &vp : get_enabled_parts( "PLANTER" ) ) { - if( g->u.sees( global_pos3() ) ) { + if( player_character.sees( global_pos3() ) ) { add_msg( _( "The %s's planter turns off due to low temperature." ), name ); } vp.part().enabled = false; @@ -5072,6 +5102,7 @@ void vehicle::on_move() void vehicle::slow_leak() { + map &here = get_map(); // for each badly damaged tanks (lower than 50% health), leak a small amount for( auto &p : parts ) { auto health = p.health_percent(); @@ -5087,12 +5118,12 @@ void vehicle::slow_leak() // damaged batteries self-discharge without leaking, plutonium leaks slurry if( fuel != fuel_type_battery && fuel != fuel_type_plutonium_cell ) { item leak( fuel, calendar::turn, qty ); - g->m.add_item_or_charges( dest, leak ); + here.add_item_or_charges( dest, leak ); p.ammo_consume( qty, global_part_pos3( p ) ); } else if( fuel == fuel_type_plutonium_cell ) { if( p.ammo_remaining() >= PLUTONIUM_CHARGES / 10 ) { item leak( "plut_slurry_dense", calendar::turn, qty ); - g->m.add_item_or_charges( dest, leak ); + here.add_item_or_charges( dest, leak ); p.ammo_consume( qty * PLUTONIUM_CHARGES / 10, global_part_pos3( p ) ); } else { p.ammo_consume( p.ammo_remaining(), global_part_pos3( p ) ); @@ -5321,7 +5352,7 @@ void vehicle::gain_moves() { fuel_used_last_turn.clear(); check_falling_or_floating(); - const bool pl_control = player_in_control( g->u ); + const bool pl_control = player_in_control( get_player_character() ); if( is_moving() || is_falling ) { if( !loose_parts.empty() ) { shed_loose_parts(); @@ -5375,9 +5406,10 @@ void vehicle::gain_moves() void vehicle::dump_items_from_part( const size_t index ) { + map &here = get_map(); vehicle_part &vp = parts[ index ]; for( item &e : vp.items ) { - g->m.add_item_or_charges( global_part_pos3( vp ), e ); + here.add_item_or_charges( global_part_pos3( vp ), e ); } vp.items.clear(); } @@ -5392,10 +5424,10 @@ bool vehicle::decrement_summon_timer() const size_t p = vp.part_index(); dump_items_from_part( p ); } - if( g->u.sees( global_pos3() ) ) { + if( get_player_character().sees( global_pos3() ) ) { add_msg( m_info, _( "Your %s winks out of existence." ), name ); } - g->m.destroy_vehicle( this ); + get_map().destroy_vehicle( this ); return true; } else { *summon_time_limit -= 1_turns; @@ -5752,13 +5784,14 @@ void vehicle::do_towing_move() invalidate_towing(); return; } - const tripoint tower_tow_point = g->m.getabs( global_part_pos3( tow_index ) ); - const tripoint towed_tow_point = g->m.getabs( towed_veh->global_part_pos3( other_tow_index ) ); + map &here = get_map(); + const tripoint tower_tow_point = here.getabs( global_part_pos3( tow_index ) ); + const tripoint towed_tow_point = here.getabs( towed_veh->global_part_pos3( other_tow_index ) ); // same as above, but where the pulling vehicle is pulling from double towing_veh_angle = towed_veh->get_angle_from_targ( tower_tow_point ); const bool reverse = towed_veh->tow_data.tow_direction == TOW_BACK; int accel_y = 0; - tripoint vehpos = g->m.getabs( towed_veh->global_pos3() ); + tripoint vehpos = here.getabs( towed_veh->global_pos3() ); int turn_x = get_turn_from_angle( towing_veh_angle, vehpos, tower_tow_point, reverse ); if( rl_dist( towed_tow_point, tower_tow_point ) < 6 ) { accel_y = reverse ? -1 : 1; @@ -5781,21 +5814,21 @@ void vehicle::do_towing_move() towed_veh->autodrive( point( turn_x, accel_y ) ); } else { towed_veh->skidding = true; - std::vector lineto = line_to( g->m.getlocal( towed_tow_point ), - g->m.getlocal( tower_tow_point ) ); + std::vector lineto = line_to( here.getlocal( towed_tow_point ), + here.getlocal( tower_tow_point ) ); tripoint nearby_destination; if( lineto.size() >= 2 ) { nearby_destination = lineto[1]; } else { nearby_destination = tower_tow_point; } - const int destination_delta_x = g->m.getlocal( tower_tow_point ).x - nearby_destination.x; - const int destination_delta_y = g->m.getlocal( tower_tow_point ).y - nearby_destination.y; + const int destination_delta_x = here.getlocal( tower_tow_point ).x - nearby_destination.x; + const int destination_delta_y = here.getlocal( tower_tow_point ).y - nearby_destination.y; const int destination_delta_z = towed_veh->global_pos3().z; const tripoint move_destination( clamp( destination_delta_x, -1, 1 ), clamp( destination_delta_y, -1, 1 ), clamp( destination_delta_z, -1, 1 ) ); - g->m.move_vehicle( *towed_veh, move_destination, towed_veh->face ); + here.move_vehicle( *towed_veh, move_destination, towed_veh->face ); towed_veh->move = tileray( point( destination_delta_x, destination_delta_y ) ); } @@ -5803,8 +5836,9 @@ void vehicle::do_towing_move() bool vehicle::is_external_part( const tripoint &part_pt ) const { - for( const tripoint &elem : g->m.points_in_radius( part_pt, 1 ) ) { - const optional_vpart_position vp = g->m.veh_at( elem ); + map &here = get_map(); + for( const tripoint &elem : here.points_in_radius( part_pt, 1 ) ) { + const optional_vpart_position vp = here.veh_at( elem ); if( !vp ) { return true; } @@ -5924,6 +5958,7 @@ void vehicle::invalidate_towing( bool first_vehicle ) if( other_veh && first_vehicle ) { other_veh->invalidate_towing(); } + map &here = get_map(); for( const vpart_reference &vp : get_all_parts() ) { const size_t p = vp.part_index(); if( vp.part().removed ) { @@ -5934,7 +5969,7 @@ void vehicle::invalidate_towing( bool first_vehicle ) if( first_vehicle ) { vehicle_part *part = &parts[part_with_feature( p, "TOW_CABLE", true )]; item drop = part->properties_to_item(); - g->m.add_item_or_charges( global_part_pos3( *part ), drop ); + here.add_item_or_charges( global_part_pos3( *part ), drop ); } remove_part( part_with_feature( p, "TOW_CABLE", true ) ); break; @@ -5955,7 +5990,8 @@ bool vehicle::tow_cable_too_far() const debugmsg( "towing data exists but no towing part" ); return false; } - tripoint towing_point = g->m.getabs( global_part_pos3( index ) ); + map &here = get_map(); + tripoint towing_point = here.getabs( global_part_pos3( index ) ); if( !tow_data.get_towed_by()->tow_data.get_towed() ) { debugmsg( "vehicle %s has data for a towing vehicle, but that towing vehicle does not have %s listed as towed", disp_name(), disp_name() ); @@ -5966,7 +6002,7 @@ bool vehicle::tow_cable_too_far() const debugmsg( "towing data exists but no towing part" ); return false; } - tripoint towed_point = g->m.getabs( tow_data.get_towed_by()->global_part_pos3( other_index ) ); + tripoint towed_point = here.getabs( tow_data.get_towed_by()->global_part_pos3( other_index ) ); if( towing_point == tripoint_zero || towed_point == tripoint_zero ) { debugmsg( "towing data exists but no towing part" ); return false; @@ -5986,7 +6022,8 @@ bool vehicle::no_towing_slack() const debugmsg( "towing data exists but no towing part" ); return false; } - tripoint towing_point = g->m.getabs( global_part_pos3( index ) ); + map &here = get_map(); + tripoint towing_point = here.getabs( global_part_pos3( index ) ); if( !tow_data.get_towed()->tow_data.get_towed_by() ) { debugmsg( "vehicle %s has data for a towed vehicle, but that towed vehicle does not have %s listed as tower", disp_name(), disp_name() ); @@ -5997,7 +6034,7 @@ bool vehicle::no_towing_slack() const debugmsg( "towing data exists but no towing part" ); return false; } - tripoint towed_point = g->m.getabs( tow_data.get_towed()->global_part_pos3( other_index ) ); + tripoint towed_point = here.getabs( tow_data.get_towed()->global_part_pos3( other_index ) ); if( towing_point == tripoint_zero || towed_point == tripoint_zero ) { debugmsg( "towing data exists but no towing part" ); return false; @@ -6012,7 +6049,7 @@ void vehicle::remove_remote_part( int part_num ) // If the target vehicle is still there, ask it to remove its part if( veh != nullptr ) { - const tripoint local_abs = g->m.getabs( global_part_pos3( part_num ) ); + const tripoint local_abs = get_map().getabs( global_part_pos3( part_num ) ); for( size_t j = 0; j < veh->loose_parts.size(); j++ ) { int remote_partnum = veh->loose_parts[j]; @@ -6028,6 +6065,7 @@ void vehicle::remove_remote_part( int part_num ) void vehicle::shed_loose_parts() { + map &here = get_map(); // remove_part rebuilds the loose_parts vector, when all of those parts have been removed, // it will stay empty. while( !loose_parts.empty() ) { @@ -6047,7 +6085,7 @@ void vehicle::shed_loose_parts() auto part = &parts[elem]; if( !magic ) { item drop = part->properties_to_item(); - g->m.add_item_or_charges( global_part_pos3( *part ), drop ); + here.add_item_or_charges( global_part_pos3( *part ), drop ); } remove_part( elem ); @@ -6126,9 +6164,10 @@ bool vpart_position::is_inside() const void vehicle::unboard_all() { + map &here = get_map(); std::vector bp = boarded_parts(); for( auto &i : bp ) { - g->m.unboard_vehicle( global_part_pos3( i ) ); + here.unboard_vehicle( global_part_pos3( i ) ); } } @@ -6261,7 +6300,7 @@ void vehicle::shift_parts( const point &delta ) pivot_anchor[0] -= delta; refresh(); //Need to also update the map after this - g->m.reset_vehicle_cache( sm_pos.z ); + get_map().reset_vehicle_cache( sm_pos.z ); } /** @@ -6305,20 +6344,22 @@ int vehicle::break_off( int p, int dmg ) if( rng( 0, part_info( p ).durability / 10 ) >= dmg ) { return dmg; } + map &here = get_map(); const tripoint pos = global_part_pos3( p ); const auto scatter_parts = [&]( const vehicle_part & pt ) { for( const item &piece : pt.pieces_for_broken_part() ) { // inside the loop, so each piece goes to a different place // TODO: this may spawn items behind a wall - const tripoint where = random_entry( g->m.points_in_radius( pos, SCATTER_DISTANCE ) ); + const tripoint where = random_entry( here.points_in_radius( pos, SCATTER_DISTANCE ) ); // TODO: balance audit, ensure that less pieces are generated than one would need // to build the component (smash a vehicle box that took 10 lumps of steel, // find 12 steel lumps scattered after atom-smashing it with a tree trunk) if( !magic ) { - g->m.add_item_or_charges( where, piece ); + here.add_item_or_charges( where, piece ); } } }; + Character &player_character = get_player_character(); if( part_info( p ).location == part_location_structure ) { // For structural parts, remove other parts first std::vector parts_in_square = parts_at_relative( parts[p].mount, true ); @@ -6330,26 +6371,26 @@ int vehicle::break_off( int p, int dmg ) if( parts[ parts_in_square[ index ] ].is_broken() ) { // Tearing off a broken part - break it up - if( g->u.sees( pos ) ) { + if( player_character.sees( pos ) ) { add_msg( m_bad, _( "The %s's %s breaks into pieces!" ), name, parts[ parts_in_square[ index ] ].name() ); } scatter_parts( parts[parts_in_square[index]] ); } else { // Intact (but possibly damaged) part - remove it in one piece - if( g->u.sees( pos ) ) { + if( player_character.sees( pos ) ) { add_msg( m_bad, _( "The %1$s's %2$s is torn off!" ), name, parts[ parts_in_square[ index ] ].name() ); } if( !magic ) { item part_as_item = parts[parts_in_square[index]].properties_to_item(); - g->m.add_item_or_charges( pos, part_as_item ); + here.add_item_or_charges( pos, part_as_item ); } } remove_part( parts_in_square[index] ); } // After clearing the frame, remove it. - if( g->u.sees( pos ) ) { + if( player_character.sees( pos ) ) { add_msg( m_bad, _( "The %1$s's %2$s is destroyed!" ), name, parts[ p ].name() ); } scatter_parts( parts[p] ); @@ -6357,7 +6398,7 @@ int vehicle::break_off( int p, int dmg ) find_and_split_vehicles( p ); } else { //Just break it off - if( g->u.sees( pos ) ) { + if( player_character.sees( pos ) ) { add_msg( m_bad, _( "The %1$s's %2$s is destroyed!" ), name, parts[ p ].name() ); } @@ -6406,7 +6447,8 @@ int vehicle::damage_direct( int p, int dmg, damage_type type ) if( is_autodriving ) { stop_autodriving(); } - g->m.set_memory_seen_cache_dirty( global_part_pos3( p ) ); + map &here = get_map(); + here.set_memory_seen_cache_dirty( global_part_pos3( p ) ); if( parts[p].is_broken() ) { return break_off( p, dmg ); } @@ -6435,7 +6477,7 @@ int vehicle::damage_direct( int p, int dmg, damage_type type ) leak_fuel( parts [ p ] ); for( const auto &e : parts[p].items ) { - g->m.add_item_or_charges( global_part_pos3( p ), e ); + here.add_item_or_charges( global_part_pos3( p ), e ); } parts[p].items.clear(); @@ -6449,7 +6491,7 @@ int vehicle::damage_direct( int p, int dmg, damage_type type ) if( parts[p].is_fuel_store() ) { explode_fuel( p, type ); } else if( parts[ p ].is_broken() && part_flag( p, "UNMOUNT_ON_DAMAGE" ) ) { - g->m.spawn_item( global_part_pos3( p ), part_info( p ).item, 1, 0, calendar::turn ); + here.spawn_item( global_part_pos3( p ), part_info( p ).item, 1, 0, calendar::turn ); monster *mon = get_pet( p ); if( mon != nullptr && mon->has_effect( effect_harnessed ) ) { mon->remove_effect( effect_harnessed ); @@ -6471,10 +6513,11 @@ void vehicle::leak_fuel( vehicle_part &pt ) return; } + map &here = get_map(); // leak in random directions but prefer closest tiles and avoid walls or other obstacles std::vector tiles = closest_tripoints_first( global_part_pos3( pt ), 1 ); - tiles.erase( std::remove_if( tiles.begin(), tiles.end(), []( const tripoint & e ) { - return !g->m.passable( e ); + tiles.erase( std::remove_if( tiles.begin(), tiles.end(), [&here]( const tripoint & e ) { + return !here.passable( e ); } ), tiles.end() ); // leak up to 1/3 of remaining fuel per iteration and continue until the part is empty @@ -6483,7 +6526,7 @@ void vehicle::leak_fuel( vehicle_part &pt ) int qty = pt.ammo_consume( rng( 0, std::max( pt.ammo_remaining() / 3, 1 ) ), global_part_pos3( pt ) ); if( qty > 0 ) { - g->m.add_item_or_charges( random_entry( tiles ), item( fuel, calendar::turn, qty ) ); + here.add_item_or_charges( random_entry( tiles ), item( fuel, calendar::turn, qty ) ); } } @@ -6627,6 +6670,7 @@ void vehicle::update_time( const time_point &update_to ) int exhaust_part; std::tie( exhaust_part, muffle ) = get_exhaust_part(); + map &here = get_map(); // Parts emitting fields for( int idx : emitters ) { const vehicle_part &pt = parts[idx]; @@ -6634,13 +6678,13 @@ void vehicle::update_time( const time_point &update_to ) continue; } for( const emit_id &e : pt.info().emissions ) { - g->m.emit_field( global_part_pos3( pt ), e ); + here.emit_field( global_part_pos3( pt ), e ); } for( const emit_id &e : pt.info().exhaust ) { if( exhaust_part == -1 ) { - g->m.emit_field( global_part_pos3( pt ), e ); + here.emit_field( global_part_pos3( pt ), e ); } else { - g->m.emit_field( exhaust_dest( exhaust_part ), e ); + here.emit_field( exhaust_dest( exhaust_part ), e ); } } discharge_battery( pt.info().epower ); @@ -6670,7 +6714,7 @@ void vehicle::update_time( const time_point &update_to ) return; } // Get one weather data set per vehicle, they don't differ much across vehicle area - auto accum_weather = sum_conditions( update_from, update_to, g->m.getabs( global_pos3() ) ); + auto accum_weather = sum_conditions( update_from, update_to, here.getabs( global_pos3() ) ); // make some reference objects to use to check for reload const item water( "water" ); const item water_clean( "water_clean" ); @@ -6679,7 +6723,7 @@ void vehicle::update_time( const time_point &update_to ) const auto &pt = parts[idx]; // we need an unbroken funnel mounted on the exterior of the vehicle - if( pt.is_unavailable() || !is_sm_tile_outside( g->m.getabs( global_part_pos3( pt ) ) ) ) { + if( pt.is_unavailable() || !is_sm_tile_outside( here.getabs( global_part_pos3( pt ) ) ) ) { continue; } @@ -6717,7 +6761,7 @@ void vehicle::update_time( const time_point &update_to ) continue; } - if( !is_sm_tile_outside( g->m.getabs( global_part_pos3( part ) ) ) ) { + if( !is_sm_tile_outside( here.getabs( global_part_pos3( part ) ) ) ) { continue; } @@ -6951,6 +6995,7 @@ std::set vehicle::advance_precalc_mounts( const point &new_pos, const tripo bool vehicle::refresh_zones() { if( zones_dirty ) { + map &here = get_map(); decltype( loot_zones ) new_zones; for( auto const &z : loot_zones ) { zone_data zone = z.second; @@ -6966,7 +7011,7 @@ bool vehicle::refresh_zones() continue; } tripoint zone_pos = global_part_pos3( part_idx ); - zone_pos = g->m.getabs( zone_pos ); + zone_pos = here.getabs( zone_pos ); //Set the position of the zone to that part zone.set_position( std::pair( zone_pos, zone_pos ), false ); new_zones.emplace( z.first, zone ); diff --git a/src/vehicle.h b/src/vehicle.h index 8a1edc1872625..31f7be9d68725 100644 --- a/src/vehicle.h +++ b/src/vehicle.h @@ -758,9 +758,9 @@ class vehicle bool mod_hp( vehicle_part &pt, int qty, damage_type dt = DT_NULL ); // check if given player controls this vehicle - bool player_in_control( const player &p ) const; + bool player_in_control( const Character &p ) const; // check if player controls this vehicle remotely - bool remote_controlled( const player &p ) const; + bool remote_controlled( const Character &p ) const; // init parts state for randomly generated vehicle void init_state( int init_veh_fuel, int init_veh_status ); From d26d54bfdd1b634687c6a5a8c9a0103040dd7c30 Mon Sep 17 00:00:00 2001 From: John Bytheway Date: Sat, 4 Jul 2020 04:55:33 +0100 Subject: [PATCH 075/420] Add translator glossary, explain Exodii (#41808) --- doc/TRANSLATING.md | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/doc/TRANSLATING.md b/doc/TRANSLATING.md index 85af77c583d2b..5803eaa09144a 100644 --- a/doc/TRANSLATING.md +++ b/doc/TRANSLATING.md @@ -2,6 +2,7 @@ * [Translators](#translators) * [Getting Started](#getting-Started) + * [Glossary](#glossary) * [Grammatical gender](#grammatical-gender) * [Tips](#tips) * [Developers](#developers) @@ -87,6 +88,22 @@ Click on the "Save" button when you are satisfied with your translation. See [Transifex's documentation][3] for more information. +### Glossary + +This glossary is intended to help explain some CDDA-specific terms and their +etymology in order to help translations. + +* **Exodii**: The Exodii are a bunch of humans from another dimension. When + the Blob invaded their world, they managed to acquire enough technology to + open portals of their own, and now they portal to worlds that have been + attacked by the Blob and try to rescue survivors. Exodii is a horrible + mangling of "Exodus" - the word literally means leaving or going out and has + connotations of forced emigration and refugees. The Exodii are the people of + an Exodus. While Exodus is a Latin word and "ii" to indicate a plural is a + Latin thing, but this isn't actually the [correct Latin + plural](https://www.latin-is-simple.com/en/vocabulary/noun/9294/) for this + word. + ### Grammatical gender For NPC dialogue (and potentially other strings) some languages may wish to From 4e51fcca245ac09f57397efc93f5f733475330e6 Mon Sep 17 00:00:00 2001 From: anothersimulacrum Date: Fri, 3 Jul 2020 23:02:12 -0700 Subject: [PATCH 076/420] Privatize recipe::time (#41818) Accessing the time as a raw integer could potentially lead to mistakes where what units it's in (e.g. seconds, moves, etc) was not understood. Also, more importantly, I'm working on other changes which this facilitates. --- src/crafting.cpp | 10 +++++----- src/game.cpp | 6 +++--- src/game_inventory.cpp | 2 +- src/item.cpp | 2 +- src/iuse.cpp | 4 ++-- src/recipe.cpp | 10 ++++++++++ src/recipe.h | 6 +++++- 7 files changed, 27 insertions(+), 13 deletions(-) diff --git a/src/crafting.cpp b/src/crafting.cpp index a47f4c21cd3b4..492ce12dbc41e 100644 --- a/src/crafting.cpp +++ b/src/crafting.cpp @@ -1130,7 +1130,7 @@ void player::complete_craft( item &craft, const tripoint &loc ) std::max( get_skill_level( making.skill_used ), 1 ) * std::max( get_int(), 1 ); const double time_to_learn = 1000 * 8 * std::pow( difficulty, 4 ) / learning_speed; - if( x_in_y( making.time, time_to_learn ) ) { + if( x_in_y( making.time_to_craft_moves(), time_to_learn ) ) { learn_recipe( &making ); add_msg( m_good, _( "You memorized the recipe for %s!" ), making.result_name() ); @@ -2055,12 +2055,12 @@ bool player::disassemble( item_location target, bool interactive ) if( activity.id() != ACT_DISASSEMBLE ) { if( num_dis != 0 ) { - assign_activity( ACT_DISASSEMBLE, r.time * num_dis ); + assign_activity( ACT_DISASSEMBLE, r.time_to_craft_moves() * num_dis ); } else { - assign_activity( ACT_DISASSEMBLE, r.time ); + assign_activity( ACT_DISASSEMBLE, r.time_to_craft_moves() ); } } else if( activity.moves_left <= 0 ) { - activity.moves_left = r.time; + activity.moves_left = r.time_to_craft_moves(); } // index is used as a bool that indicates if we want recursive uncraft. @@ -2144,7 +2144,7 @@ void player::complete_disassemble() return; } - activity.moves_left = next_recipe.time; + activity.moves_left = next_recipe.time_to_craft_moves(); } void player::complete_disassemble( item_location &target, const recipe &dis ) diff --git a/src/game.cpp b/src/game.cpp index 13fa89d556037..ea3d769fbd8f6 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -8378,8 +8378,8 @@ static void add_disassemblables( uilist &menu, const auto &msg = string_format( pgettext( "butchery menu", "%s (%d)" ), it.tname(), stack.second ); menu.addentry_col( menu_index++, true, hotkey, msg, - to_string_clipped( time_duration::from_turns( recipe_dictionary::get_uncraft( - it.typeId() ).time / 100 ) ) ); + to_string_clipped( recipe_dictionary::get_uncraft( + it.typeId() ).time_to_craft() ) ); hotkey = -1; } } @@ -8681,7 +8681,7 @@ void game::butcher() int time_to_disassemble = 0; int time_to_disassemble_all = 0; for( const auto &stack : disassembly_stacks ) { - const int time = recipe_dictionary::get_uncraft( stack.first->typeId() ).time; + const int time = recipe_dictionary::get_uncraft( stack.first->typeId() ).time_to_craft_moves(); time_to_disassemble += time; time_to_disassemble_all += time * stack.second; } diff --git a/src/game_inventory.cpp b/src/game_inventory.cpp index b0ace9e77847a..8808d49cddc2f 100644 --- a/src/game_inventory.cpp +++ b/src/game_inventory.cpp @@ -468,7 +468,7 @@ class disassemble_inventory_preset : public pickup_inventory_preset }, _( "YIELD" ) ); append_cell( [ this ]( const item_location & loc ) { - return to_string_clipped( time_duration::from_turns( get_recipe( loc ).time / 100 ) ); + return to_string_clipped( get_recipe( loc ).time_to_craft() ); }, _( "TIME" ) ); } diff --git a/src/item.cpp b/src/item.cpp index 188a27fb3d39b..a1b73f91bc8a2 100644 --- a/src/item.cpp +++ b/src/item.cpp @@ -3139,7 +3139,7 @@ void item::disassembly_info( std::vector &info, const iteminfo_query * const recipe &dis = recipe_dictionary::get_uncraft( typeId() ); const requirement_data &req = dis.disassembly_requirements(); if( !req.is_empty() ) { - const std::string approx_time = to_string_approx( time_duration::from_turns( dis.time / 100 ) ); + const std::string approx_time = to_string_approx( dis.time_to_craft() ); const requirement_data::alter_item_comp_vector &comps_list = req.get_components(); const std::string comps_str = enumerate_as_string( comps_list.begin(), comps_list.end(), diff --git a/src/iuse.cpp b/src/iuse.cpp index ae046084fac2a..2dd9d35b4e1f1 100644 --- a/src/iuse.cpp +++ b/src/iuse.cpp @@ -8933,9 +8933,9 @@ int iuse::multicooker( player *p, item *it, bool t, const tripoint &pos ) const recipe *meal = dishes[choice]; int mealtime; if( it->get_var( "MULTI_COOK_UPGRADE" ) == "UPGRADE" ) { - mealtime = meal->time; + mealtime = meal->time_to_craft_moves(); } else { - mealtime = meal->time * 2; + mealtime = meal->time_to_craft_moves() * 2; } const int all_charges = charges_to_start + mealtime / ( it->type->tool->power_draw / 10000 ); diff --git a/src/recipe.cpp b/src/recipe.cpp index 14a33433bcbff..d8de962a5b798 100644 --- a/src/recipe.cpp +++ b/src/recipe.cpp @@ -40,6 +40,16 @@ time_duration recipe::batch_duration( int batch, float multiplier, size_t assist return time_duration::from_turns( batch_time( batch, multiplier, assistants ) / 100 ); } +time_duration recipe::time_to_craft() const +{ + return time_duration::from_seconds( time_to_craft_moves() / 100 ); +} + +int recipe::time_to_craft_moves() const +{ + return time; +} + int recipe::batch_time( int batch, float multiplier, size_t assistants ) const { // 1.0f is full speed diff --git a/src/recipe.h b/src/recipe.h index 65e81e55cf408..6c546eff9d371 100644 --- a/src/recipe.h +++ b/src/recipe.h @@ -38,6 +38,8 @@ class recipe private: itype_id result_ = itype_id::NULL_ID(); + int time = 0; // in movement points (100 per turn) + public: recipe(); @@ -56,7 +58,6 @@ class recipe translation description; - int time = 0; // in movement points (100 per turn) int difficulty = 0; /** Fetch combined requirement data (inline and via "using" syntax). @@ -146,6 +147,9 @@ class recipe time_duration batch_duration( int batch = 1, float multiplier = 1.0, size_t assistants = 0 ) const; + time_duration time_to_craft() const; + int time_to_craft_moves() const; + bool has_flag( const std::string &flag_name ) const; bool is_reversible() const { From 32bda5329c378c569d39254c70c2e57f3898bf3c Mon Sep 17 00:00:00 2001 From: John Bytheway Date: Fri, 3 Jul 2020 21:09:07 -0400 Subject: [PATCH 077/420] Enable modernize-use-transparent-functors Enable this clang-tidy check. --- .clang-tidy | 1 - src/shadowcasting.h | 2 +- tests/colony_test.cpp | 2 +- tests/flat_set_test.cpp | 2 +- tests/list_test.cpp | 2 +- 5 files changed, 4 insertions(+), 5 deletions(-) diff --git a/.clang-tidy b/.clang-tidy index 77f4f5274f2a0..ed5a2a124635b 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -35,7 +35,6 @@ readability-*,\ -modernize-return-braced-init-list,\ -modernize-use-default-member-init,\ -modernize-use-emplace,\ --modernize-use-transparent-functors,\ -performance-for-range-copy,\ -performance-inefficient-vector-operation,\ -performance-noexcept-move-constructor,\ diff --git a/src/shadowcasting.h b/src/shadowcasting.h index 2ab0fa3311935..a3be0fd69f467 100644 --- a/src/shadowcasting.h +++ b/src/shadowcasting.h @@ -50,7 +50,7 @@ struct four_quadrants { friend four_quadrants operator*( const four_quadrants &l, const four_quadrants &r ) { four_quadrants result; std::transform( l.values.begin(), l.values.end(), r.values.begin(), - result.values.begin(), std::multiplies() ); + result.values.begin(), std::multiplies<>() ); return result; } diff --git a/tests/colony_test.cpp b/tests/colony_test.cpp index e982134564347..4ac23050709d9 100644 --- a/tests/colony_test.cpp +++ b/tests/colony_test.cpp @@ -772,7 +772,7 @@ TEST_CASE( "colony sort", "[colony]" ) // Less-than sort test CHECK( sorted ); - test_colony.sort( std::greater() ); + test_colony.sort( std::greater<>() ); prev = 65536; diff --git a/tests/flat_set_test.cpp b/tests/flat_set_test.cpp index f5262888daaeb..b2aea60f0ec5f 100644 --- a/tests/flat_set_test.cpp +++ b/tests/flat_set_test.cpp @@ -74,7 +74,7 @@ TEST_CASE( "flat_set_ranged_operations", "[flat_set]" ) TEST_CASE( "reversed_flat_set_insertion", "[flat_set]" ) { - cata::flat_set> s; + cata::flat_set> s; s.insert( 2 ); s.insert( 1 ); s.insert( 4 ); diff --git a/tests/list_test.cpp b/tests/list_test.cpp index e6f28f4e5683e..1aa6122d580e3 100644 --- a/tests/list_test.cpp +++ b/tests/list_test.cpp @@ -583,7 +583,7 @@ TEST_CASE( "list sort and reverse", "[list]" ) } SECTION( "greater than (predicate)" ) { - test_list.sort( std::greater() ); + test_list.sort( std::greater<>() ); bool passed = true; int previous = 65535; From 1aef7826ff1bd830cb0cb3152a7bddc9b0a798a8 Mon Sep 17 00:00:00 2001 From: Menschheit Date: Sat, 4 Jul 2020 09:58:58 +0200 Subject: [PATCH 078/420] Fix crash by using vehicle welding rig (#41798) --- src/activity_handlers.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/activity_handlers.cpp b/src/activity_handlers.cpp index 09886602274db..18faacbc89100 100644 --- a/src/activity_handlers.cpp +++ b/src/activity_handlers.cpp @@ -2546,7 +2546,10 @@ struct weldrig_hack { item &get_item() { if( veh != nullptr && part >= 0 ) { - pseudo.charges = veh->drain( itype_battery, 1000 - pseudo.charges ); + item pseudo_magazine( pseudo.magazine_default() ); + pseudo.put_in( pseudo_magazine, item_pocket::pocket_type::MAGAZINE_WELL ); + pseudo.ammo_set( itype_battery, veh->drain( itype_battery, + pseudo.ammo_capacity( ammotype( "battery" ) ) ) ); return pseudo; } @@ -2560,8 +2563,7 @@ struct weldrig_hack { return; } - veh->charge_battery( pseudo.charges ); - pseudo.charges = 0; + veh->charge_battery( pseudo.ammo_remaining() ); } ~weldrig_hack() { @@ -2670,7 +2672,6 @@ void activity_handlers::repair_item_finish( player_activity *act, player *p ) } const item &fix = *act->targets[1]; - if( repeat == repeat_type::INIT ) { const int level = p->get_skill_level( actor->used_skill ); repair_item_actor::repair_type action_type = actor->default_action( fix, level ); @@ -2726,7 +2727,6 @@ void activity_handlers::repair_item_finish( player_activity *act, player *p ) } } while( repeat == repeat_type::INIT ); } - // Otherwise keep retrying act->moves_left = actor->move_cost; } From 1337a50986315f4b1eaf90826ec37ab2043cd734 Mon Sep 17 00:00:00 2001 From: Stephen Pittman Date: Sat, 4 Jul 2020 17:59:43 +1000 Subject: [PATCH 079/420] Fix check for vehicle in active range (#41800) --- src/map.cpp | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/src/map.cpp b/src/map.cpp index 17a4cca14ff34..a7bd4f69cd501 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -1,6 +1,7 @@ #include "map.h" #include +#include #include #include #include @@ -450,17 +451,17 @@ bool map::vehproceed( VehicleList &vehicle_list ) int maxz = zlevels ? OVERMAP_HEIGHT : abs_sub.z; for( int zlev = minz; zlev <= maxz; ++zlev ) { level_cache &cache = get_cache( zlev ); - bool still_active = false; - if( cache.veh_in_active_range ) { - for( const bool &has_vehicle : cache.veh_exists_at ) { - if( has_vehicle ) { - still_active = true; - break; - } - } - } - cache.veh_in_active_range = still_active; + + // Check if any vehicles exist in the active range for this z-level + cache.veh_in_active_range = cache.veh_in_active_range && + std::any_of( std::begin( cache.veh_exists_at ), + std::end( cache.veh_exists_at ), []( const auto & row ) { + return std::any_of( std::begin( row ), std::end( row ), []( bool veh_exists ) { + return veh_exists; + } ); + } ); } + return true; } From 6e848fe6faad76aa8ae7e2ba67fd02ea3cb50dca Mon Sep 17 00:00:00 2001 From: LyleSY Date: Sat, 4 Jul 2020 04:00:27 -0400 Subject: [PATCH 080/420] [DinoMod] All of the dino zombie brutes. Lots of skeletons too. (#41759) --- data/mods/DinoMod/monstergroups/dinosaur.json | 94 ++- data/mods/DinoMod/monsters/fungus.json | 6 +- data/mods/DinoMod/monsters/zed-dinosaur.json | 138 +--- .../DinoMod/monsters/zinosaur_upgrade.json | 599 ++++++++++++++++++ 4 files changed, 715 insertions(+), 122 deletions(-) create mode 100644 data/mods/DinoMod/monsters/zinosaur_upgrade.json diff --git a/data/mods/DinoMod/monstergroups/dinosaur.json b/data/mods/DinoMod/monstergroups/dinosaur.json index 63c0568c8586f..d1543cede0246 100644 --- a/data/mods/DinoMod/monstergroups/dinosaur.json +++ b/data/mods/DinoMod/monstergroups/dinosaur.json @@ -85,19 +85,87 @@ "name": "GROUP_DINOSAUR_ZOMBIE", "default": "mon_null", "monsters": [ - { "monster": "mon_zeinonychus", "freq": 3, "cost_multiplier": 30, "starts": 72, "pack_size": [ 2, 3 ] }, + { "monster": "mon_zallimimus", "freq": 50, "cost_multiplier": 10, "starts": 72, "pack_size": [ 4, 8 ] }, + { "monster": "mon_zachycephalosaurus", "freq": 25, "cost_multiplier": 10, "starts": 72, "pack_size": [ 1, 2 ] }, + { "monster": "mon_zamptosaurus", "freq": 100, "cost_multiplier": 10, "starts": 72, "pack_size": [ 1, 2 ] }, + { "monster": "mon_zpinosaurus", "freq": 1, "cost_multiplier": 90, "starts": 72 }, { "monster": "mon_zyrannosaurus", "freq": 1, "cost_multiplier": 80, "starts": 72 }, - { "monster": "mon_zpinosaurus", "freq": 1, "cost_multiplier": 90, "starts": 72 } + { "monster": "mon_zalbertosaurus", "freq": 4, "cost_multiplier": 70, "starts": 72 }, + { "monster": "mon_zriceratops", "freq": 3, "cost_multiplier": 60, "starts": 72, "pack_size": [ 1, 2 ] }, + { "monster": "mon_ztegosaurus", "freq": 5, "cost_multiplier": 40, "starts": 72, "pack_size": [ 2, 4 ] }, + { "monster": "mon_zankylosaurus", "freq": 5, "cost_multiplier": 40, "starts": 72 }, + { "monster": "mon_zapatosaurus", "freq": 5, "cost_multiplier": 50, "starts": 72, "pack_size": [ 1, 2 ] }, + { "monster": "mon_zeratosaurus", "freq": 1, "cost_multiplier": 60, "starts": 72 }, + { "monster": "mon_zallosaurus", "freq": 8, "cost_multiplier": 60, "starts": 72 }, + { "monster": "mon_zeinonychus", "freq": 3, "cost_multiplier": 30, "starts": 72, "pack_size": [ 2, 3 ] }, + { "monster": "mon_zutahraptor", "freq": 5, "cost_multiplier": 60, "starts": 72 }, + { "monster": "mon_zarasaurolophus", "freq": 3, "cost_multiplier": 20, "starts": 72, "pack_size": [ 2, 4 ] }, + { "monster": "mon_zimorphodon", "freq": 50, "cost_multiplier": 10, "starts": 72, "pack_size": [ 2, 4 ] }, + { "monster": "mon_zilophosaurus", "freq": 1, "cost_multiplier": 20, "starts": 72, "pack_size": [ 1, 2 ] } + ] + }, + { + "type": "monstergroup", + "name": "GROUP_zpinosaurus_UPGRADE", + "default": "mon_zpinosaurus_brute", + "monsters": [ + { "monster": "mon_zpinosaurus_shady", "freq": 15, "cost_multiplier": 5 }, + { "monster": "mon_zpinosaurus_brute", "freq": 23, "cost_multiplier": 5 } ] }, { "type": "monstergroup", "name": "GROUP_zyrannosaurus_UPGRADE", "default": "mon_syrannosaurus", - "//": "No dogs, humans, bionics, or profession-types", "monsters": [ { "monster": "mon_syrannosaurus", "freq": 45, "cost_multiplier": 5 }, - { "monster": "mon_zyrannosaurus_shady", "freq": 15, "cost_multiplier": 5 } + { "monster": "mon_zyrannosaurus_shady", "freq": 15, "cost_multiplier": 5 }, + { "monster": "mon_zyrannosaurus_brute", "freq": 23, "cost_multiplier": 5 } + ] + }, + { + "type": "monstergroup", + "name": "GROUP_zalbertosaurus_UPGRADE", + "default": "mon_salbertosaurus", + "monsters": [ + { "monster": "mon_salbertosaurus", "freq": 45, "cost_multiplier": 5 }, + { "monster": "mon_zalbertosaurus_brute", "freq": 23, "cost_multiplier": 5 } + ] + }, + { + "type": "monstergroup", + "name": "GROUP_zeratosaurus_UPGRADE", + "default": "mon_seratosaurus", + "monsters": [ + { "monster": "mon_seratosaurus", "freq": 45, "cost_multiplier": 5 }, + { "monster": "mon_zeratosaurus_brute", "freq": 23, "cost_multiplier": 5 } + ] + }, + { + "type": "monstergroup", + "name": "GROUP_zallosaurus_UPGRADE", + "default": "mon_sallosaurus", + "monsters": [ + { "monster": "mon_sallosaurus", "freq": 45, "cost_multiplier": 5 }, + { "monster": "mon_zallosaurus_brute", "freq": 23, "cost_multiplier": 5 } + ] + }, + { + "type": "monstergroup", + "name": "GROUP_zeinonychus_UPGRADE", + "default": "mon_zeinonychus_brute", + "monsters": [ + { "monster": "mon_zeinonychus_shady", "freq": 15, "cost_multiplier": 5 }, + { "monster": "mon_zeinonychus_brute", "freq": 23, "cost_multiplier": 5 } + ] + }, + { + "type": "monstergroup", + "name": "GROUP_zutahraptor_UPGRADE", + "default": "mon_sutahraptor", + "monsters": [ + { "monster": "mon_sutahraptor", "freq": 45, "cost_multiplier": 5 }, + { "monster": "mon_zutahraptor_brute", "freq": 23, "cost_multiplier": 5 } ] }, { @@ -105,9 +173,23 @@ "name": "GROUP_ZOMBIE", "default": "mon_zombie", "monsters": [ - { "monster": "mon_zeinonychus", "freq": 3, "cost_multiplier": 30, "starts": 72, "pack_size": [ 2, 3 ] }, + { "monster": "mon_zallimimus", "freq": 5, "cost_multiplier": 10, "starts": 72, "pack_size": [ 4, 8 ] }, + { "monster": "mon_zachycephalosaurus", "freq": 3, "cost_multiplier": 10, "starts": 72, "pack_size": [ 1, 2 ] }, + { "monster": "mon_zamptosaurus", "freq": 10, "cost_multiplier": 10, "starts": 72, "pack_size": [ 1, 2 ] }, { "monster": "mon_zpinosaurus", "freq": 1, "cost_multiplier": 90, "starts": 72 }, - { "monster": "mon_zyrannosaurus", "freq": 1, "cost_multiplier": 80, "starts": 72 } + { "monster": "mon_zyrannosaurus", "freq": 1, "cost_multiplier": 80, "starts": 72 }, + { "monster": "mon_zalbertosaurus", "freq": 4, "cost_multiplier": 70, "starts": 72 }, + { "monster": "mon_zriceratops", "freq": 3, "cost_multiplier": 60, "starts": 72, "pack_size": [ 1, 2 ] }, + { "monster": "mon_ztegosaurus", "freq": 5, "cost_multiplier": 40, "starts": 72, "pack_size": [ 2, 4 ] }, + { "monster": "mon_zankylosaurus", "freq": 5, "cost_multiplier": 40, "starts": 72 }, + { "monster": "mon_zapatosaurus", "freq": 5, "cost_multiplier": 50, "starts": 72, "pack_size": [ 1, 2 ] }, + { "monster": "mon_zeratosaurus", "freq": 1, "cost_multiplier": 60, "starts": 72 }, + { "monster": "mon_zallosaurus", "freq": 8, "cost_multiplier": 60, "starts": 72 }, + { "monster": "mon_zeinonychus", "freq": 3, "cost_multiplier": 30, "starts": 72, "pack_size": [ 2, 3 ] }, + { "monster": "mon_zutahraptor", "freq": 5, "cost_multiplier": 60, "starts": 72 }, + { "monster": "mon_zarasaurolophus", "freq": 3, "cost_multiplier": 20, "starts": 72, "pack_size": [ 2, 4 ] }, + { "monster": "mon_zimorphodon", "freq": 5, "cost_multiplier": 10, "starts": 72, "pack_size": [ 2, 4 ] }, + { "monster": "mon_zilophosaurus", "freq": 1, "cost_multiplier": 20, "starts": 72, "pack_size": [ 1, 2 ] } ] } ] diff --git a/data/mods/DinoMod/monsters/fungus.json b/data/mods/DinoMod/monsters/fungus.json index 551957b6e7f0c..122d5d67bff80 100644 --- a/data/mods/DinoMod/monsters/fungus.json +++ b/data/mods/DinoMod/monsters/fungus.json @@ -18,7 +18,7 @@ "vision_day": 5, "vision_night": 5, "special_attacks": [ [ "FUNGUS", 200 ], { "type": "bite", "cooldown": 5 } ], - "upgrades": { "into": "mon_zpinosaurus_fungus" }, + "upgrades": { }, "flags": [ "SEES", "SMELLS", "POISON", "STUMBLES", "BASHES", "DESTROYS", "NO_BREATHE", "FILTHY", "WARM", "SWIMS" ] }, { @@ -40,7 +40,7 @@ "vision_day": 5, "vision_night": 5, "special_attacks": [ [ "FUNGUS", 200 ], { "type": "bite", "cooldown": 5 } ], - "upgrades": { "half_life": 1400000, "into_group": "GROUP_zyrannosaurus_UPGRADE" }, + "upgrades": { }, "flags": [ "SEES", "SMELLS", "POISON", "STUMBLES", "BASHES", "DESTROYS", "NO_BREATHE", "FILTHY", "WARM" ] }, { @@ -62,7 +62,7 @@ "vision_day": 5, "vision_night": 5, "special_attacks": [ [ "FUNGUS", 200 ], { "type": "bite", "cooldown": 5 } ], - "upgrades": { "into": "mon_zeinonychus_fungus" }, + "upgrades": { }, "flags": [ "SEES", "SMELLS", "KEENNOSE", "POISON", "STUMBLES", "NO_BREATHE", "FILTHY", "WARM" ] } ] diff --git a/data/mods/DinoMod/monsters/zed-dinosaur.json b/data/mods/DinoMod/monsters/zed-dinosaur.json index 6789cf645a720..08b5a8dbc88f9 100644 --- a/data/mods/DinoMod/monsters/zed-dinosaur.json +++ b/data/mods/DinoMod/monsters/zed-dinosaur.json @@ -26,6 +26,7 @@ "death_function": [ "NORMAL" ], "special_attacks": [ [ "scratch", 10 ], { "type": "bite", "cooldown": 5 } ], "description": "The shuffling corpse of a medium-sized bipedal dinosaur covered with tattered feathers and black putrid liquid.", + "upgrades": { "half_life": 14, "into": "mon_zallimimus_brute" }, "flags": [ "SEES", "SMELLS", "HEARS", "POISON", "STUMBLES", "NO_BREATHE", "REVIVES", "FILTHY", "WARM" ], "vision_night": 3, "harvest": "zed_dino_feather_leather", @@ -57,6 +58,7 @@ "death_function": [ "NORMAL" ], "special_attacks": [ [ "scratch", 10 ], { "type": "bite", "cooldown": 5 } ], "description": "The shuffling corpse of a medium-sized bipedal dinosaur covered with tattered feathers and black putrid liquid. It looks like a reptilian ostrich with a round hard-looking domed head.", + "upgrades": { "half_life": 14, "into": "mon_zachycephalosaurus_brute" }, "flags": [ "SEES", "SMELLS", "HEARS", "POISON", "STUMBLES", "NO_BREATHE", "REVIVES", "FILTHY", "WARM" ], "vision_night": 3, "harvest": "zed_dino_feather_leather", @@ -89,6 +91,7 @@ "death_function": [ "NORMAL" ], "special_attacks": [ [ "scratch", 10 ], { "type": "bite", "cooldown": 5 } ], "description": "The shuffling corpse of a large feathered bipedal dinosaur with strong legs, broad shoulders and a pointed beak. Its tattered feathers are stained with black, sticky liquid.", + "upgrades": { "half_life": 14, "into": "mon_zamptosaurus_brute" }, "flags": [ "SEES", "SMELLS", "HEARS", "POISON", "STUMBLES", "NO_BREATHE", "REVIVES", "FILTHY", "WARM", "BASHES" ], "vision_night": 3, "harvest": "zed_dino_feather_leather", @@ -120,7 +123,7 @@ "death_function": [ "NORMAL" ], "description": "Enormous putrid dinosaur corpse with a ferocious crocodile-like head, oozing black eyes, and a tattered sail on its back.", "special_attacks": [ [ "scratch", 10 ], { "type": "bite", "cooldown": 5 } ], - "upgrades": { "half_life": 14, "into": "mon_zpinosaurus_shady" }, + "upgrades": { "half_life": 14, "into_group": "GROUP_zpinosaurus_UPGRADE" }, "flags": [ "SEES", "SMELLS", @@ -140,33 +143,6 @@ "harvest": "zed_dino_feather_leather", "categories": [ "DINOSAUR", "CLASSIC" ] }, - { - "type": "MONSTER", - "id": "mon_zpinosaurus_shady", - "name": { "str": "Spinosaurus shady zombie" }, - "copy-from": "mon_zpinosaurus", - "color": "light_gray", - "description": "An uncanny shadow envelops this dinosaur. You can make out the outline of a huge bipedal dinosaur with a tattered sail. The head is long and narrow with a V-shaped snout.", - "flags": [ - "SEES", - "SMELLS", - "HEARS", - "POISON", - "STUMBLES", - "BASHES", - "DESTROYS", - "BLEED", - "NO_BREATHE", - "REVIVES", - "FILTHY", - "WARM", - "SWIMS", - "NIGHT_INVISIBILITY" - ], - "vision_day": 3, - "vision_night": 30, - "categories": [ "DINOSAUR" ] - }, { "type": "MONSTER", "id": "mon_zyrannosaurus", @@ -212,56 +188,6 @@ "harvest": "zed_dino_feather_leather", "categories": [ "DINOSAUR", "CLASSIC" ] }, - { - "type": "MONSTER", - "id": "mon_zyrannosaurus_shady", - "name": { "str": "Shady Z-Rex" }, - "copy-from": "mon_zyrannosaurus", - "color": "light_gray", - "description": "An uncanny shadow envelops this dinosaur. You can make out the outline of a huge bipedal dinosaur with feathery edges. The head looks big, lots of big teeth would fit in it.", - "flags": [ - "SEES", - "SMELLS", - "HEARS", - "POISON", - "STUMBLES", - "BASHES", - "DESTROYS", - "BLEED", - "NO_BREATHE", - "REVIVES", - "FILTHY", - "WARM", - "NIGHT_INVISIBILITY" - ], - "vision_day": 3, - "vision_night": 30, - "categories": [ "DINOSAUR" ] - }, - { - "type": "MONSTER", - "id": "mon_syrannosaurus", - "name": { "str": "S-Rex", "str_pl": "S-Rexes" }, - "copy-from": "mon_zyrannosaurus", - "color": "white", - "material": [ "bone" ], - "speed": 35, - "melee_skill": 13, - "melee_dice": 4, - "melee_dice_sides": 12, - "melee_cut": 20, - "armor_bash": 0, - "armor_cut": 30, - "armor_bullet": 30, - "armor_stab": 30, - "armor_acid": 3, - "vision_day": 30, - "hp": 200, - "description": "Monstrous columns of dense bone lifting enormous sharp pointed teeth dripping with black goo.", - "flags": [ "SEES", "HEARS", "POISON", "HARDTOSHOOT", "BLEED", "NO_BREATHE", "REVIVES", "FILTHY" ], - "harvest": "mr_bones", - "categories": [ "DINOSAUR" ] - }, { "type": "MONSTER", "id": "mon_zalbertosaurus", @@ -275,7 +201,8 @@ "armor_bullet": 3, "hp": 330, "special_attacks": [ { "type": "bite", "cooldown": 5 }, [ "GRAB", 7 ], [ "scratch", 20 ], [ "LUNGE", 5 ] ], - "description": "Massive jaws drooling black liquid lifted over grasping claws by a huge shuffling dinosaur corpse." + "description": "Massive jaws drooling black liquid lifted over grasping claws by a huge shuffling dinosaur corpse.", + "upgrades": { "half_life": 14, "into_group": "GROUP_zalbertosaurus_UPGRADE" } }, { "type": "MONSTER", @@ -295,7 +222,8 @@ "armor_cut": 5, "armor_bullet": 3, "hp": 250, - "description": "A massive shambling rhino-like dinosaur corpse with a bony crest from which three wicked looking horns emerge. Its black eyes ooze like tears." + "description": "A massive shambling rhino-like dinosaur corpse with a bony crest from which three wicked looking horns emerge. Its black eyes ooze like tears.", + "upgrades": { "half_life": 14, "into": "mon_zriceratops_brute" } }, { "type": "MONSTER", @@ -315,7 +243,8 @@ "armor_cut": 4, "armor_bullet": 2, "hp": 250, - "description": "A large shambling quadruped dinosaur corpse dragging with the weight of the plates on its back, waving a much livelier looking spiked tail." + "description": "A large shambling quadruped dinosaur corpse dragging with the weight of the plates on its back, waving a much livelier looking spiked tail.", + "upgrades": { "half_life": 14, "into": "mon_ztegosaurus_brute" } }, { "type": "MONSTER", @@ -335,7 +264,8 @@ "armor_cut": 7, "armor_bullet": 4, "hp": 200, - "description": "The shuffling corpse of what looks like a giant armadillo with peeling armored plates and black, glistening eyes. Its tail ends in a massive spiked club of bone." + "description": "The shuffling corpse of what looks like a giant armadillo with peeling armored plates and black, glistening eyes. Its tail ends in a massive spiked club of bone.", + "upgrades": { "half_life": 14, "into": "mon_zankylosaurus_brute" } }, { "type": "MONSTER", @@ -358,7 +288,8 @@ { "id": "slam", "cooldown": 10, "damage_max_instance": [ { "damage_type": "bash", "amount": 12 } ] }, [ "SMASH", 30 ] ], - "description": "Massive, long-necked, four-legged dinosaur corpse with a long, whip-like tail. The head is upright and the neck looks like it would still make a good strong club." + "description": "Massive, long-necked, four-legged dinosaur corpse with a long, whip-like tail. The head is upright and the neck looks like it would still make a good strong club.", + "upgrades": { "half_life": 14, "into": "mon_zapatosaurus_brute" } }, { "type": "MONSTER", @@ -378,6 +309,7 @@ "armor_bullet": 2, "hp": 115, "description": "This zombie is enormous, scaly, studded with bony spikes, and it moves with horrible speed. Its colorful horns are worn and wet with filth and its bright scales and bone spikes have taken a beating.", + "upgrades": { "half_life": 14, "into_group": "GROUP_zeratosaurus_UPGRADE" }, "flags": [ "SEES", "SMELLS", @@ -411,6 +343,7 @@ "armor_bullet": 2, "hp": 200, "description": "The shambling corpse of a large predatory bipedal dinosaur, with tiger-like stripes on its broad, scaled back.", + "upgrades": { "half_life": 14, "into_group": "GROUP_zallosaurus_UPGRADE" }, "flags": [ "SEES", "SMELLS", "HEARS", "POISON", "STUMBLES", "BASHES", "BLEED", "NO_BREATHE", "REVIVES", "FILTHY", "WARM" ] }, { @@ -444,38 +377,13 @@ { "type": "bite", "cooldown": 5 } ], "description": "The shuffling corpse of a medium-sized bipedal dinosaur covered with tattered feathers and black putrid liquid. Both feet brandish a large sickle-like claw.", - "upgrades": { "half_life": 14, "into": "mon_zeinonychus_shady" }, + "upgrades": { "half_life": 14, "into_group": "GROUP_zeinonychus_UPGRADE" }, "flags": [ "SEES", "SMELLS", "HEARS", "KEENNOSE", "BLEED", "POISON", "STUMBLES", "NO_BREATHE", "REVIVES", "FILTHY", "WARM" ], "vision_day": 30, "vision_night": 10, "harvest": "zed_dino_feather_leather", "categories": [ "DINOSAUR", "CLASSIC" ] }, - { - "type": "MONSTER", - "id": "mon_zeinonychus_shady", - "name": { "str": "Deinonychus shady zombie" }, - "copy-from": "mon_zeinonychus", - "color": "light_gray", - "description": "An uncanny shadow envelops this dinosaur. You can make out the outline of a medium-sized bipedal dinosaur with feathery edges. Both feet brandish a large sickle-like claw.", - "flags": [ - "SEES", - "SMELLS", - "HEARS", - "KEENNOSE", - "BLEED", - "POISON", - "STUMBLES", - "NO_BREATHE", - "REVIVES", - "FILTHY", - "WARM", - "NIGHT_INVISIBILITY" - ], - "vision_day": 3, - "vision_night": 40, - "categories": [ "DINOSAUR" ] - }, { "type": "MONSTER", "id": "mon_zutahraptor", @@ -493,7 +401,8 @@ "armor_cut": 4, "armor_bullet": 2, "hp": 170, - "description": "The swaying, hopping corpse of a large bipedal dinosaur with feathered arms, a long tail, and long sharp scythe-like claws." + "description": "The swaying, hopping corpse of a large bipedal dinosaur with feathered arms, a long tail, and long sharp scythe-like claws.", + "upgrades": { "half_life": 14, "into_group": "GROUP_zutahraptor_UPGRADE" } }, { "type": "MONSTER", @@ -513,7 +422,8 @@ "armor_cut": 7, "armor_bullet": 4, "hp": 500, - "description": "A huge mottled dinosaur with a blunt head crest, dead and walking, eyes vacant and swollen." + "description": "A huge mottled dinosaur with a blunt head crest, dead and walking, eyes vacant and swollen.", + "upgrades": { "half_life": 14, "into": "mon_zarasaurolophus_brute" } }, { "type": "MONSTER", @@ -533,7 +443,8 @@ "armor_cut": 4, "armor_bullet": 1, "hp": 50, - "description": "The raggedly flying corpse of a feathered reptile over three feet long, with short wings and a big colorful beak." + "description": "The raggedly flying corpse of a feathered reptile over three feet long, with short wings and a big colorful beak.", + "upgrades": { "half_life": 14, "into": "mon_zimorphodon_brute" } }, { "type": "MONSTER", @@ -552,6 +463,7 @@ "armor_cut": 4, "armor_bullet": 2, "hp": 200, - "description": "The shuffling corpse of a medium dinosaur with sharp teeth and two prominent bony crests on its head with ragged strips of ripped flesh hanging down like a frill." + "description": "The shuffling corpse of a medium dinosaur with sharp teeth and two prominent bony crests on its head with ragged strips of ripped flesh hanging down like a frill.", + "upgrades": { "half_life": 14, "into": "mon_zilophosaurus_brute" } } ] diff --git a/data/mods/DinoMod/monsters/zinosaur_upgrade.json b/data/mods/DinoMod/monsters/zinosaur_upgrade.json new file mode 100644 index 0000000000000..7a276665d403d --- /dev/null +++ b/data/mods/DinoMod/monsters/zinosaur_upgrade.json @@ -0,0 +1,599 @@ +[ + { + "type": "MONSTER", + "id": "mon_zallimimus_brute", + "name": { "str": "Gruesome Gallimimus" }, + "copy-from": "mon_zallimimus", + "description": "The shuffling corpse of a medium-sized bipedal dinosaur covered with tattered feathers and black putrid liquid. Its entire body bulges with distended muscles and swollen, festering wounds.", + "diff": 2, + "color": "red", + "proportional": { "hp": 1.5, "speed": 1.5 }, + "relative": { + "melee_dice": 1, + "melee_dice_sides": 5, + "melee_cut": 2, + "armor_bash": 4, + "armor_cut": 6, + "armor_bullet": 5, + "vision_night": 1 + }, + "upgrades": { }, + "special_attacks": [ [ "SMASH", 30 ] ], + "extend": { "flags": [ "GROUP_BASH", "PUSH_MON", "PUSH_VEH" ] }, + "categories": [ "DINOSAUR" ] + }, + { + "type": "MONSTER", + "id": "mon_zachycephalosaurus_brute", + "name": { "str": "Skull Breaker" }, + "copy-from": "mon_zachycephalosaurus", + "description": "The shuffling corpse of a medium-sized bipedal dinosaur covered with tattered feathers and black putrid liquid. Its round, hard-looking domed head sits on a body bulging with distended muscles and swollen, festering wounds.", + "diff": 2, + "color": "red", + "proportional": { "hp": 1.5, "speed": 1.5 }, + "relative": { + "melee_dice": 1, + "melee_dice_sides": 5, + "melee_cut": 2, + "armor_bash": 4, + "armor_cut": 6, + "armor_bullet": 5, + "vision_night": 1 + }, + "upgrades": { }, + "special_attacks": [ [ "SMASH", 30 ] ], + "extend": { "flags": [ "GROUP_BASH", "PUSH_MON", "PUSH_VEH" ] }, + "categories": [ "DINOSAUR" ] + }, + { + "type": "MONSTER", + "id": "mon_zamptosaurus_brute", + "name": { "str": "Crusher Camp" }, + "copy-from": "mon_zamptosaurus", + "description": "The shuffling corpse of a large feathered bipedal dinosaur with grossly bulging legs, massive hulking shoulders and a vicious pointed beak. Its tattered feathers are stained with black, sticky liquid.", + "diff": 2, + "color": "red", + "proportional": { "hp": 1.5, "speed": 1.5 }, + "relative": { + "melee_dice": 1, + "melee_dice_sides": 5, + "melee_cut": 2, + "armor_bash": 4, + "armor_cut": 6, + "armor_bullet": 5, + "vision_night": 1 + }, + "upgrades": { }, + "special_attacks": [ [ "SMASH", 30 ] ], + "extend": { "flags": [ "GROUP_BASH", "PUSH_MON", "PUSH_VEH" ] }, + "categories": [ "DINOSAUR" ] + }, + { + "type": "MONSTER", + "id": "mon_zpinosaurus_brute", + "name": { "str": "Spino Sledge" }, + "copy-from": "mon_zpinosaurus", + "description": "Enormous putrid dinosaur corpse with a ferocious crocodile-like head, oozing black eyes, and a tattered sail on its back. Its body is even bigger than normal, bulging with distended muscles and swollen, festering wounds.", + "diff": 2, + "color": "red", + "proportional": { "hp": 1.5, "speed": 1.5 }, + "relative": { + "melee_dice": 1, + "melee_dice_sides": 5, + "melee_cut": 2, + "armor_bash": 4, + "armor_cut": 6, + "armor_bullet": 5, + "vision_night": 1 + }, + "upgrades": { }, + "special_attacks": [ [ "SMASH", 30 ] ], + "extend": { "flags": [ "GROUP_BASH", "PUSH_MON", "PUSH_VEH" ] }, + "categories": [ "DINOSAUR" ] + }, + { + "type": "MONSTER", + "id": "mon_zyrannosaurus_brute", + "name": { "str": "Rage Rex" }, + "copy-from": "mon_zyrannosaurus", + "description": "Massive piles of ragged, stinking flesh lifting enormous teeth. Its entire body bulges with distended muscles and swollen, festering wounds.", + "diff": 2, + "color": "red", + "proportional": { "hp": 1.5, "speed": 1.5 }, + "relative": { + "melee_dice": 1, + "melee_dice_sides": 5, + "melee_cut": 2, + "armor_bash": 4, + "armor_cut": 6, + "armor_bullet": 5, + "vision_night": 1 + }, + "upgrades": { }, + "special_attacks": [ [ "SMASH", 30 ] ], + "extend": { "flags": [ "GROUP_BASH", "PUSH_MON", "PUSH_VEH" ] }, + "categories": [ "DINOSAUR" ] + }, + { + "type": "MONSTER", + "id": "mon_zalbertosaurus_brute", + "name": { "str": "Alberta Anvil" }, + "copy-from": "mon_zalbertosaurus", + "description": "Massive jaws and grabbing claws lifting by a body bulging with distended muscles and swollen, festering wounds.", + "diff": 2, + "color": "red", + "proportional": { "hp": 1.5, "speed": 1.5 }, + "relative": { + "melee_dice": 1, + "melee_dice_sides": 5, + "melee_cut": 2, + "armor_bash": 4, + "armor_cut": 6, + "armor_bullet": 5, + "vision_night": 1 + }, + "upgrades": { }, + "special_attacks": [ [ "SMASH", 30 ], [ "GRAB", 7 ] ], + "extend": { "flags": [ "GROUP_BASH", "PUSH_MON", "PUSH_VEH" ] }, + "categories": [ "DINOSAUR" ] + }, + { + "type": "MONSTER", + "id": "mon_zriceratops_brute", + "name": { "str": "Triceratruck" }, + "copy-from": "mon_zriceratops", + "description": "A massive shambling rhino-like dinosaur corpse with a bony crest from which three wicked looking horns emerge. Its black eyes ooze like tears. Its entire body bulges with distended muscles and swollen, festering wounds.", + "diff": 2, + "color": "red", + "proportional": { "hp": 1.5, "speed": 1.5 }, + "relative": { + "melee_dice": 1, + "melee_dice_sides": 5, + "melee_cut": 2, + "armor_bash": 4, + "armor_cut": 6, + "armor_bullet": 5, + "vision_night": 1 + }, + "upgrades": { }, + "special_attacks": [ [ "SMASH", 30 ] ], + "extend": { "flags": [ "GROUP_BASH", "PUSH_MON", "PUSH_VEH" ] }, + "categories": [ "DINOSAUR" ] + }, + { + "type": "MONSTER", + "id": "mon_ztegosaurus_brute", + "name": { "str": "Stegosaurus Sledge" }, + "copy-from": "mon_ztegosaurus", + "description": "A large shambling quadruped dinosaur corpse with plates on its back, waving a spiked tail. Its entire body bulges with distended muscles and swollen, festering wounds.", + "diff": 2, + "color": "red", + "proportional": { "hp": 1.5, "speed": 1.5 }, + "relative": { + "melee_dice": 1, + "melee_dice_sides": 5, + "melee_cut": 2, + "armor_bash": 4, + "armor_cut": 6, + "armor_bullet": 5, + "vision_night": 1 + }, + "upgrades": { }, + "special_attacks": [ [ "SMASH", 30 ] ], + "extend": { "flags": [ "GROUP_BASH", "PUSH_MON", "PUSH_VEH" ] }, + "categories": [ "DINOSAUR" ] + }, + { + "type": "MONSTER", + "id": "mon_zankylosaurus_brute", + "name": { "str": "Dino Tank" }, + "copy-from": "mon_zankylosaurus", + "description": "Heavily armored zombie dinosaur. Its entire body bulges with distended muscles and swollen, festering wounds.", + "diff": 2, + "color": "red", + "proportional": { "hp": 1.5, "speed": 1.5 }, + "relative": { + "melee_dice": 1, + "melee_dice_sides": 5, + "melee_cut": 2, + "armor_bash": 4, + "armor_cut": 6, + "armor_bullet": 5, + "vision_night": 1 + }, + "upgrades": { }, + "special_attacks": [ [ "SMASH", 30 ] ], + "extend": { "flags": [ "GROUP_BASH", "PUSH_MON", "PUSH_VEH" ] }, + "categories": [ "DINOSAUR" ] + }, + { + "type": "MONSTER", + "id": "mon_zapatosaurus_brute", + "name": { "str": "Zombie Dreadnought" }, + "copy-from": "mon_zapatosaurus", + "description": "Massive, long-necked, four-legged dinosaur corpse with a long, whip-like tail. Its entire body bulges with distended muscles and swollen, festering wounds.", + "diff": 2, + "color": "red", + "proportional": { "hp": 1.5, "speed": 1.5 }, + "relative": { + "melee_dice": 1, + "melee_dice_sides": 5, + "melee_cut": 2, + "armor_bash": 4, + "armor_cut": 6, + "armor_bullet": 5, + "vision_night": 1 + }, + "upgrades": { }, + "special_attacks": [ [ "SMASH", 30 ] ], + "extend": { "flags": [ "GROUP_BASH", "PUSH_MON", "PUSH_VEH" ] }, + "categories": [ "DINOSAUR" ] + }, + { + "type": "MONSTER", + "id": "mon_zeratosaurus_brute", + "name": { "str": "Draco Titan" }, + "copy-from": "mon_zeratosaurus", + "description": "This zombie is enormous, scaly, studded with bony spikes, and it moves with horrible speed. Its colorful horns and bone spikes sit on a body bulging with distended muscles and swollen, festering wounds.", + "diff": 2, + "color": "red", + "proportional": { "hp": 1.5, "speed": 1.5 }, + "relative": { + "melee_dice": 1, + "melee_dice_sides": 5, + "melee_cut": 2, + "armor_bash": 4, + "armor_cut": 6, + "armor_bullet": 5, + "vision_night": 1 + }, + "upgrades": { }, + "special_attacks": [ [ "SMASH", 30 ] ], + "extend": { "flags": [ "GROUP_BASH", "PUSH_MON", "PUSH_VEH" ] }, + "categories": [ "DINOSAUR" ] + }, + { + "type": "MONSTER", + "id": "mon_zallosaurus_brute", + "name": { "str": "Allosaurus Avalanche" }, + "copy-from": "mon_zallosaurus", + "description": "The shambling corpse of a large predatory bipedal dinosaur. Its entire body bulges with distended muscles and swollen, festering wounds.", + "diff": 2, + "color": "red", + "proportional": { "hp": 1.5, "speed": 1.5 }, + "relative": { + "melee_dice": 1, + "melee_dice_sides": 5, + "melee_cut": 2, + "armor_bash": 4, + "armor_cut": 6, + "armor_bullet": 5, + "vision_night": 1 + }, + "upgrades": { }, + "special_attacks": [ [ "SMASH", 30 ] ], + "extend": { "flags": [ "GROUP_BASH", "PUSH_MON", "PUSH_VEH" ] }, + "categories": [ "DINOSAUR" ] + }, + { + "type": "MONSTER", + "id": "mon_zeinonychus_brute", + "name": { "str": "Deino Destroyer" }, + "copy-from": "mon_zeinonychus", + "description": "The shuffling corpse of a medium-sized bipedal dinosaur covered with tattered feathers and black putrid liquid. Both feet brandish a large sickle-like claw. Its entire body bulges with distended muscles and swollen, festering wounds.", + "diff": 2, + "color": "red", + "proportional": { "hp": 1.5, "speed": 1.5 }, + "relative": { + "melee_dice": 1, + "melee_dice_sides": 5, + "melee_cut": 2, + "armor_bash": 4, + "armor_cut": 6, + "armor_bullet": 5, + "vision_night": 1 + }, + "upgrades": { }, + "special_attacks": [ [ "SMASH", 30 ] ], + "extend": { "flags": [ "GROUP_BASH", "PUSH_MON", "PUSH_VEH" ] }, + "categories": [ "DINOSAUR" ] + }, + { + "type": "MONSTER", + "id": "mon_zutahraptor_brute", + "name": { "str": "Utah Hoodoo" }, + "copy-from": "mon_zutahraptor", + "description": "The swaying, hopping corpse of a large bipedal dinosaur with feathered arms, a long tail, and long sharp scythe-like claws. Its entire body bulges with distended muscles and swollen, festering wounds.", + "diff": 2, + "color": "red", + "proportional": { "hp": 1.5, "speed": 1.5 }, + "relative": { + "melee_dice": 1, + "melee_dice_sides": 5, + "melee_cut": 2, + "armor_bash": 4, + "armor_cut": 6, + "armor_bullet": 5, + "vision_night": 1 + }, + "upgrades": { }, + "special_attacks": [ [ "SMASH", 30 ] ], + "extend": { "flags": [ "GROUP_BASH", "PUSH_MON", "PUSH_VEH" ] }, + "categories": [ "DINOSAUR" ] + }, + { + "type": "MONSTER", + "id": "mon_zarasaurolophus_brute", + "name": { "str": "Parasaur Punch" }, + "copy-from": "mon_zarasaurolophus", + "description": "A huge mottled dinosaur with a blunt head crest, dead and walking, eyes vacant and swollen. Its entire body bulges with distended muscles and swollen, festering wounds.", + "diff": 2, + "color": "red", + "proportional": { "hp": 1.5, "speed": 1.5 }, + "relative": { + "melee_dice": 1, + "melee_dice_sides": 5, + "melee_cut": 2, + "armor_bash": 4, + "armor_cut": 6, + "armor_bullet": 5, + "vision_night": 1 + }, + "upgrades": { }, + "special_attacks": [ [ "SMASH", 30 ] ], + "extend": { "flags": [ "GROUP_BASH", "PUSH_MON", "PUSH_VEH" ] }, + "categories": [ "DINOSAUR" ] + }, + { + "type": "MONSTER", + "id": "mon_zimorphodon_brute", + "name": { "str": "Winged Horror" }, + "copy-from": "mon_zimorphodon", + "description": "The flying corpse of a feathered reptile over three feet long, with short wings and a big colorful beak. Its entire body bulges with distended muscles and swollen, festering wounds.", + "diff": 2, + "color": "red", + "proportional": { "hp": 1.5, "speed": 1.5 }, + "relative": { + "melee_dice": 1, + "melee_dice_sides": 5, + "melee_cut": 2, + "armor_bash": 4, + "armor_cut": 6, + "armor_bullet": 5, + "vision_night": 1 + }, + "upgrades": { }, + "special_attacks": [ [ "SMASH", 30 ] ], + "extend": { "flags": [ "GROUP_BASH", "PUSH_MON", "PUSH_VEH" ] }, + "categories": [ "DINOSAUR" ] + }, + { + "type": "MONSTER", + "id": "mon_zilophosaurus_brute", + "name": { "str": "Crested Crusher" }, + "copy-from": "mon_zilophosaurus", + "description": "The shuffling corpse of a medium dinosaur with sharp teeth and two prominent bony crests on its head with ragged strips of ripped flesh hanging down like a frill. Its entire body bulges with distended muscles and swollen, festering wounds.", + "diff": 2, + "color": "red", + "proportional": { "hp": 1.5, "speed": 1.5 }, + "relative": { + "melee_dice": 1, + "melee_dice_sides": 5, + "melee_cut": 2, + "armor_bash": 4, + "armor_cut": 6, + "armor_bullet": 5, + "vision_night": 1 + }, + "upgrades": { }, + "special_attacks": [ [ "SMASH", 30 ] ], + "extend": { "flags": [ "GROUP_BASH", "PUSH_MON", "PUSH_VEH" ] }, + "categories": [ "DINOSAUR" ] + }, + { + "type": "MONSTER", + "id": "mon_zpinosaurus_shady", + "name": { "str": "Spinosaurus shady zombie" }, + "copy-from": "mon_zpinosaurus", + "color": "light_gray", + "description": "An uncanny shadow envelops this dinosaur. You can make out the outline of a huge bipedal dinosaur with a tattered sail. The head is long and narrow with a V-shaped snout.", + "upgrades": { }, + "flags": [ + "SEES", + "SMELLS", + "HEARS", + "POISON", + "STUMBLES", + "BASHES", + "DESTROYS", + "BLEED", + "NO_BREATHE", + "REVIVES", + "FILTHY", + "WARM", + "SWIMS", + "NIGHT_INVISIBILITY" + ], + "vision_day": 3, + "vision_night": 30, + "categories": [ "DINOSAUR" ] + }, + { + "type": "MONSTER", + "id": "mon_zyrannosaurus_shady", + "name": { "str": "Shady Z-Rex" }, + "copy-from": "mon_zyrannosaurus", + "color": "light_gray", + "description": "An uncanny shadow envelops this dinosaur. You can make out the outline of a huge bipedal dinosaur with feathery edges. The head looks big, lots of big teeth would fit in it.", + "upgrades": { }, + "flags": [ + "SEES", + "SMELLS", + "HEARS", + "POISON", + "STUMBLES", + "BASHES", + "DESTROYS", + "BLEED", + "NO_BREATHE", + "REVIVES", + "FILTHY", + "WARM", + "NIGHT_INVISIBILITY" + ], + "vision_day": 3, + "vision_night": 30, + "categories": [ "DINOSAUR" ] + }, + { + "type": "MONSTER", + "id": "mon_zeinonychus_shady", + "name": { "str": "Deinonychus shady zombie" }, + "copy-from": "mon_zeinonychus", + "color": "light_gray", + "description": "An uncanny shadow envelops this dinosaur. You can make out the outline of a medium-sized bipedal dinosaur with feathery edges. Both feet brandish a large sickle-like claw.", + "upgrades": { }, + "flags": [ + "SEES", + "SMELLS", + "HEARS", + "KEENNOSE", + "BLEED", + "POISON", + "STUMBLES", + "NO_BREATHE", + "REVIVES", + "FILTHY", + "WARM", + "NIGHT_INVISIBILITY" + ], + "vision_day": 3, + "vision_night": 40, + "categories": [ "DINOSAUR" ] + }, + { + "type": "MONSTER", + "id": "mon_syrannosaurus", + "name": { "str": "S-Rex", "str_pl": "S-Rexes" }, + "copy-from": "mon_zyrannosaurus", + "color": "white", + "material": [ "bone" ], + "armor_bash": 0, + "description": "Monstrous columns of dense bone lifting enormous sharp pointed teeth dripping with black goo.", + "proportional": { "hp": 0.4, "speed": 0.875 }, + "relative": { + "melee_dice": 9, + "melee_dice_sides": 5, + "melee_cut": 7, + "armor_cut": 22, + "armor_bullet": 30, + "armor_stab": 30, + "armor_acid": 3, + "vision_day": -20 + }, + "upgrades": { }, + "extend": { "flags": [ "HARDTOSHOOT" ] }, + "harvest": "mr_bones", + "categories": [ "DINOSAUR" ] + }, + { + "type": "MONSTER", + "id": "mon_salbertosaurus", + "name": { "str": "Skeletal Albertosaurus" }, + "copy-from": "mon_zalbertosaurus", + "color": "white", + "material": [ "bone" ], + "armor_bash": 0, + "description": "Monstrous columns of dense bone lifting sharp pointed teeth dripping with black goo. Skeletal claws reach ahead.", + "proportional": { "hp": 0.4, "speed": 0.875 }, + "relative": { + "melee_dice": 9, + "melee_dice_sides": 5, + "melee_cut": 7, + "armor_cut": 22, + "armor_bullet": 30, + "armor_stab": 30, + "armor_acid": 3, + "vision_day": -20 + }, + "upgrades": { }, + "extend": { "flags": [ "HARDTOSHOOT" ] }, + "harvest": "mr_bones", + "categories": [ "DINOSAUR" ] + }, + { + "type": "MONSTER", + "id": "mon_seratosaurus", + "name": { "str": "Bone Dragon" }, + "copy-from": "mon_zeratosaurus", + "color": "white", + "material": [ "bone" ], + "armor_bash": 0, + "description": "Monstrous columns of dense bone lifting sharp pointed teeth dripping with black goo. Spikes and colorful horns jut out to complete the effect.", + "proportional": { "hp": 0.4, "speed": 0.875 }, + "relative": { + "melee_dice": 9, + "melee_dice_sides": 5, + "melee_cut": 7, + "armor_cut": 22, + "armor_bullet": 30, + "armor_stab": 30, + "armor_acid": 3, + "vision_day": -20 + }, + "upgrades": { }, + "extend": { "flags": [ "HARDTOSHOOT" ] }, + "harvest": "mr_bones", + "categories": [ "DINOSAUR" ] + }, + { + "type": "MONSTER", + "id": "mon_sallosaurus", + "name": { "str": "Skeletal allosaurus" }, + "copy-from": "mon_zallosaurus", + "color": "white", + "material": [ "bone" ], + "armor_bash": 0, + "description": "Monstrous columns of dense bone lifting sharp pointed teeth dripping with black goo.", + "proportional": { "hp": 0.4, "speed": 0.875 }, + "relative": { + "melee_dice": 9, + "melee_dice_sides": 5, + "melee_cut": 7, + "armor_cut": 22, + "armor_bullet": 30, + "armor_stab": 30, + "armor_acid": 3, + "vision_day": -20 + }, + "upgrades": { }, + "extend": { "flags": [ "HARDTOSHOOT" ] }, + "harvest": "mr_bones", + "categories": [ "DINOSAUR" ] + }, + { + "type": "MONSTER", + "id": "mon_sutahraptor", + "name": { "str": "Utah Bones" }, + "copy-from": "mon_zutahraptor", + "color": "white", + "material": [ "bone" ], + "armor_bash": 0, + "description": "Monstrous columns of dense bone lifting sharp pointed teeth dripping with black goo. There is a long tail and long sharp scythe-like claws", + "proportional": { "hp": 0.4, "speed": 0.875 }, + "relative": { + "melee_dice": 9, + "melee_dice_sides": 5, + "melee_cut": 7, + "armor_cut": 22, + "armor_bullet": 30, + "armor_stab": 30, + "armor_acid": 3, + "vision_day": -20 + }, + "upgrades": { }, + "extend": { "flags": [ "HARDTOSHOOT" ] }, + "harvest": "mr_bones", + "categories": [ "DINOSAUR" ] + } +] From c6c87ec0ab2d572f006553654f558eca5fd993be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=A4r=20Karlsson?= Date: Sat, 4 Jul 2020 10:00:59 +0200 Subject: [PATCH 081/420] Audit longest_side, volume, and weight for 308.json (#41722) --- data/json/items/gun/308.json | 42 ++++++++++++++++++++++++------------ 1 file changed, 28 insertions(+), 14 deletions(-) diff --git a/data/json/items/gun/308.json b/data/json/items/gun/308.json index 6f04272046c33..24e1732bbfe02 100644 --- a/data/json/items/gun/308.json +++ b/data/json/items/gun/308.json @@ -7,7 +7,8 @@ "name": { "str_sp": "FN FAL" }, "description": "Originally designed during the Cold War, the FN FAL is probably the most successful battle rifle ever designed. Even though often labeled as obsolete, its high rate of fire and powerful ammunition make it perfectly capable of holding its ground against modern competitors.", "weight": "4250 g", - "volume": "2 L", + "volume": "4511 ml", + "longest_side": "1090 mm", "price": 350000, "price_postapoc": 5500, "to_hit": -1, @@ -40,7 +41,8 @@ "name": { "str_sp": "H&K G3" }, "description": "An early battle rifle developed after the end of WWII. The G3 is designed to unload large amounts of deadly ammunition, but it is less suitable over long ranges.", "weight": "4380 g", - "volume": "2 L", + "volume": "3724 ml", + "longest_side": "1025 mm", "price": 205000, "price_postapoc": 6000, "to_hit": -1, @@ -72,7 +74,8 @@ "name": { "str": "M134D-H Minigun" }, "description": "The M134D-H Minigun is a (relatively) lightweight heavy rotary machine gun. Its six barrels are rotated by an electric motor, powered by UPS or vehicle. If you could find enough ammo for it, it would become a devastating weapon. It must be mounted on a vehicle before use.", "weight": "19770 g", - "volume": "6 L", + "volume": "8300 ml", + "longest_side": "800 mm", "price": 5500000, "price_postapoc": 10000, "to_hit": -4, @@ -109,6 +112,8 @@ "name": { "str_sp": "M14 EBR-RI" }, "description": "A highly modified version of the M14 rifle designed to cover both CQB and designated marksman roles. A very powerful and versatile rifle, if somewhat heavy.", "weight": "5070 g", + "volume": "3672 ml", + "longest_side": "889 mm", "price": 195000, "price_postapoc": 6500, "material": [ "steel", "plastic" ], @@ -136,7 +141,8 @@ "name": { "str_sp": "M1A" }, "description": "The child of the M1 Garand World War 2 rifle, the M1A is a semi-automatic variant of the M14, favored for its accuracy and modular use.", "weight": "4230 g", - "volume": "2250 ml", + "volume": "3970 ml", + "longest_side": "1126 mm", "price": 130000, "price_postapoc": 3500, "to_hit": -1, @@ -181,8 +187,9 @@ "type": "GUN", "name": { "str": "M240" }, "description": "The M240 is a medium machine gun used by the US military, replacing the older M60. Quite inaccurate and difficult to control, the M240 is designed to fire many rounds very quickly.", - "weight": "12400 g", - "volume": "3 L", + "weight": "12500 g", + "volume": "11780 ml", + "longest_side": "1260 mm", "price": 1000000, "price_postapoc": 7500, "to_hit": -1, @@ -228,7 +235,8 @@ "name": { "str": "M60" }, "description": "The M60 is a general-purpose machine gun developed to replace the .30-caliber M1918 and M1919. Heavy and difficult to handle fired from the shoulder, as most people aren't action-movie heroes.", "weight": "10500 g", - "volume": "3 L", + "volume": "10900 ml", + "longest_side": "1105 mm", "price": 1000000, "price_postapoc": 7500, "to_hit": -1, @@ -282,8 +290,9 @@ "type": "GUN", "name": { "str_sp": "Savage 111F" }, "description": "A very accurate rifle chambered for the powerful .308 round. Its very low ammo capacity is offset by its accuracy and near-complete lack of recoil.", - "weight": "2993 g", - "volume": "3 L", + "weight": "3630 g", + "volume": "2875 ml", + "longest_side": "1041 mm", "price": 53000, "price_postapoc": 4250, "to_hit": -1, @@ -307,7 +316,9 @@ "type": "GUN", "name": { "str_sp": "FN SCAR-H" }, "description": "A highly accurate and modular battle rifle specially designed for the United States Special Operations Command. The 'H' in its name stands for heavy, as it uses the powerful .308 round.", - "weight": "3640 g", + "weight": "3630 g", + "volume": "5427 ml", + "longest_side": "906 mm", "ammo": [ "308" ], "ranged_damage": { "damage_type": "bullet", "amount": -3 }, "min_cycle_recoil": 2700, @@ -370,7 +381,8 @@ "name": { "str": "HK417 A2" }, "description": "A German battle rifle with a 13\" barrel and telescopic stock. It is a gas operated, rotating bolt rifle with a short-stroke piston design similar to that of the G36.", "weight": "4220 g", - "volume": "1750 ml", + "volume": "4000 ml", + "longest_side": "914 mm", "price": 320000, "price_postapoc": 6000, "to_hit": -1, @@ -402,8 +414,9 @@ "type": "GUN", "name": { "str": "M110A1" }, "description": "A derivative of H&K's G28 with an aluminum upper receiver to meet US Army weight requirements. It is a gas operated, rotating bolt rifle accurate to 1.5 MOA with standard ammunition.", - "weight": "4330 g", - "volume": "2 L", + "weight": "3800 g", + "volume": "4000 ml", + "longest_side": "1028 mm", "price": 320000, "price_postapoc": 6000, "to_hit": -1, @@ -435,7 +448,8 @@ "name": { "str": "AR-10" }, "description": "Somewhat similar to the later AR-15, the AR-10 is a gas operated, rotating bolt rifle chambered for 7.62x51mm rounds.", "weight": "3290 g", - "volume": "2 L", + "volume": "4250 ml", + "longest_side": "1050 mm", "price": 120000, "price_postapoc": 5500, "to_hit": -1, From 07e2689f0185e37f2bca2fb256660753431339a7 Mon Sep 17 00:00:00 2001 From: John Candlebury Date: Sat, 4 Jul 2020 02:07:41 -0600 Subject: [PATCH 082/420] Fleshier collapsed tower basement (#41767) --- .../furniture_and_terrain/terrain-flesh.json | 41 +++++++++ data/json/mapgen/collapsed_tower.json | 46 ++++++---- .../json/mapgen_palettes/collapsed_tower.json | 2 +- data/json/monsters/zed_fusion.json | 86 +++++++++++++++++++ 4 files changed, 158 insertions(+), 17 deletions(-) create mode 100644 data/json/furniture_and_terrain/terrain-flesh.json diff --git a/data/json/furniture_and_terrain/terrain-flesh.json b/data/json/furniture_and_terrain/terrain-flesh.json new file mode 100644 index 0000000000000..61dda64871e97 --- /dev/null +++ b/data/json/furniture_and_terrain/terrain-flesh.json @@ -0,0 +1,41 @@ +[ + { + "type": "terrain", + "id": "t_thconc_floor_flesh", + "name": "overgrown floor", + "description": "A bare concrete floor, almost completely covered by twitching filaments of grey flesh.", + "symbol": ".", + "color": "red_yellow", + "move_cost": 5, + "roof": "t_flat_roof", + "flags": [ "TRANSPARENT", "SUPPORTS_ROOF", "COLLAPSES", "INDOORS", "FLAT" ], + "bash": { + "sound": "SMASH!", + "ter_set": "t_concrete", + "str_min": 10, + "str_max": 20, + "str_min_supported": 10, + "items": [ { "item": "meat_tainted", "count": [ 5, 10 ] } ] + } + }, + { + "type": "terrain", + "id": "t_concrete_wall_flesh", + "name": "overgrown wall", + "description": "A concrete wall overgrown by a grotesque grid of veins and knotted flesh.", + "symbol": "LINE_OXOX", + "color": "magenta_yellow", + "move_cost": 0, + "coverage": 100, + "roof": "t_flat_roof", + "flags": [ "NOITEM", "SUPPORTS_ROOF", "WALL", "NO_SCENT", "AUTO_WALL_SYMBOL", "MINEABLE", "BLOCK_WIND" ], + "bash": { + "str_min": 10, + "str_max": 20, + "sound": "crash!", + "sound_fail": "whump!", + "ter_set": "t_strconc_wall", + "items": [ { "item": "meat_tainted", "count": [ 5, 10 ] } ] + } + } +] diff --git a/data/json/mapgen/collapsed_tower.json b/data/json/mapgen/collapsed_tower.json index 2ed4e08a0e50d..c492f621a83c9 100644 --- a/data/json/mapgen/collapsed_tower.json +++ b/data/json/mapgen/collapsed_tower.json @@ -2,11 +2,23 @@ { "name": "GROUP_COLLAPSED_TOWER", "type": "monstergroup", - "default": "mon_null", + "default": "mon_zombie", "monsters": [ - { "monster": "mon_zombie_gasbag_immobile", "freq": 150, "cost_multiplier": 1 }, - { "monster": "mon_zombie", "freq": 200, "cost_multiplier": 7, "pack_size": [ 3, 5 ] }, - { "monster": "mon_zombie_gasbag_crawler", "freq": 150, "cost_multiplier": 7, "pack_size": [ 1, 2 ] }, + { "monster": "mon_zombie_gasbag_immobile", "freq": 150, "cost_multiplier": 1, "pack_size": [ 1, 4 ] }, + { "monster": "mon_zombie_gasbag_crawler", "freq": 150, "cost_multiplier": 3, "pack_size": [ 1, 2 ] }, + { "monster": "mon_zombie_scissorlimbs", "freq": 50, "cost_multiplier": 9, "pack_size": [ 1, 2 ] }, + { "monster": "mon_zombie_gasbag_impaler", "freq": 50, "cost_multiplier": 6, "pack_size": [ 1, 4 ] } + ] + }, + { + "name": "GROUP_COLLAPSED_TOWER_BASEMENT", + "type": "monstergroup", + "default": "mon_zombie_gasbag_immobile", + "monsters": [ + { "monster": "mon_zombie_hanging_innards", "freq": 50, "cost_multiplier": 1 }, + { "monster": "mon_zombie_giant_heart", "freq": 50, "cost_multiplier": 1 }, + { "monster": "mon_zombie_gasbag_crawler", "freq": 150, "cost_multiplier": 3, "pack_size": [ 1, 2 ] }, + { "monster": "mon_zombie_scissorlimbs", "freq": 50, "cost_multiplier": 9, "pack_size": [ 1, 2 ] }, { "monster": "mon_zombie_gasbag_impaler", "freq": 50, "cost_multiplier": 6, "pack_size": [ 1, 4 ] } ] }, @@ -46,9 +58,9 @@ ".||||||||#|l l| ||66 | | |-c----c-| | z~~ ###### zTz |s", "....s.. |--- ---|#|||66 ||#| #|#||| zz~~~ zzz 1s", "....|.. |### #||| B B | |l ll ll ll|l| ~~~ 1s", - "....s..||-- --||||| | |l ll ll l|####rHr z~~ >????> zzz 1s", + "....s..||-- --||||| | |l ll ll l|####rHr z~~ c????c zzz 1s", "....s..||B666B 46B || B w |## | #|?rrr ##~~ rrrrrr s", - ". |||||| 666 6B ||||| w #|###llll l#|||r###zT~~ Hr>>rH |", + ". |||||| 666 6B ||||| w #|###llll l#|||r###zT~~ HrccrH |", ".. ### B B 666|| | ##|||||--| ##|?r|||||#~~~ ||||s", ".. |||# B B B B||| | |##| ###| ## - |||||###### ## |s", ".... |# B B 666 || | w C || ~ ||||||666666 ||| |s", @@ -65,9 +77,9 @@ "..|||z|C | C| B B |||||||||||| |CCC||```3 >|| ||CCC CCC|s", "....s CB |||| CCC CC||#D | C||||| |#```3 |#||||C C B|s", ".... CC 4 CCC||B 4##||##3 ##|||#|#```||||||#3 ||#|####### 1s", - ".... |||--22-||||||||||| ## ||||||||||||||| ||| ||||||||||||||s", - "....szzzzz|||zzzzzzz|||##| z## |||z||z|###zzzz1111 ||##zzzzzzzzzzs", - "...##.......||......||.... ##...........|||.##......|||||.............", + ".... |||--22-||||||||||| ## |||||||||||||||||||||||||||||||||||s", + "....szzzzz|||zzzzzzz|||##| z## |||z||z|###||zz1111|||##zzzzzzzzzzs", + "...##.......||......||.... ##...........||||||......|||||.............", ".#..sssssssssssssssssssssss 3sssssssssssssssssssssssss||sssssssssssssss" ], "set": [ @@ -166,6 +178,8 @@ "palettes": [ "collapsed_tower" ], "furniture": { "R": "f_rack" }, "terrain": { + "|": "t_concrete_wall_flesh", + " ": "t_thconc_floor_flesh", "C": "t_machinery_electronic", "x": "t_machinery_heavy", "?": "t_nanofab", @@ -183,14 +197,14 @@ { "item": "cleaning", "x": [ 15, 15 ], "y": [ 21, 22 ], "chance": 60 } ], "place_monster": [ { "monster": "mon_talon_m202a1", "x": [ 12, 13 ], "y": [ 20, 23 ] } ], - "monster": { " ": { "monster": "mon_zombie_living_wall", "chance": 20 } }, + "monster": { " ": { "monster": "mon_zombie_living_wall", "chance": 10 } }, "place_monsters": [ - { "monster": "GROUP_COLLAPSED_TOWER", "x": [ 0, 23 ], "y": [ 0, 15 ], "density": 2.5 }, - { "monster": "GROUP_COLLAPSED_TOWER", "x": [ 24, 47 ], "y": [ 0, 23 ], "density": 2.5 }, - { "monster": "GROUP_COLLAPSED_TOWER", "x": [ 48, 71 ], "y": [ 0, 23 ], "density": 2.5 }, - { "monster": "GROUP_COLLAPSED_TOWER", "x": [ 0, 23 ], "y": [ 37, 47 ], "density": 2.5 }, - { "monster": "GROUP_COLLAPSED_TOWER", "x": [ 24, 47 ], "y": [ 24, 47 ], "density": 2.5 }, - { "monster": "GROUP_COLLAPSED_TOWER", "x": [ 48, 71 ], "y": [ 24, 47 ], "density": 2.5 } + { "monster": "GROUP_COLLAPSED_TOWER_BASEMENT", "x": [ 0, 23 ], "y": [ 0, 15 ], "density": 2.5 }, + { "monster": "GROUP_COLLAPSED_TOWER_BASEMENT", "x": [ 24, 47 ], "y": [ 0, 23 ], "density": 2.5 }, + { "monster": "GROUP_COLLAPSED_TOWER_BASEMENT", "x": [ 48, 71 ], "y": [ 0, 23 ], "density": 2.5 }, + { "monster": "GROUP_COLLAPSED_TOWER_BASEMENT", "x": [ 0, 23 ], "y": [ 37, 47 ], "density": 2.5 }, + { "monster": "GROUP_COLLAPSED_TOWER_BASEMENT", "x": [ 24, 47 ], "y": [ 24, 47 ], "density": 2.5 }, + { "monster": "GROUP_COLLAPSED_TOWER_BASEMENT", "x": [ 48, 71 ], "y": [ 24, 47 ], "density": 2.5 } ] } } diff --git a/data/json/mapgen_palettes/collapsed_tower.json b/data/json/mapgen_palettes/collapsed_tower.json index 00c7b558f2c69..a31f5b11e171a 100644 --- a/data/json/mapgen_palettes/collapsed_tower.json +++ b/data/json/mapgen_palettes/collapsed_tower.json @@ -39,7 +39,7 @@ ":": "f_dresser", ";": "f_toilet", "=": "f_fireplace", - ">": "f_counter", + "c": "f_counter", "?": "f_sofa", "@": "f_bed", "A": "f_sink", diff --git a/data/json/monsters/zed_fusion.json b/data/json/monsters/zed_fusion.json index 57bcf155d19ae..3017c635850da 100644 --- a/data/json/monsters/zed_fusion.json +++ b/data/json/monsters/zed_fusion.json @@ -211,6 +211,92 @@ "death_function": [ "GAS" ], "flags": [ "SEES", "HEARS", "SMELLS", "WARM", "POISON", "CLIMBS", "NO_BREATHE", "CLIMBS", "HARDTOSHOOT" ] }, + { + "id": "mon_zombie_scissorlimbs", + "type": "MONSTER", + "name": { "str": "scissorlimbs" }, + "description": " A nightmarish spider of gore stands tall among the ruins, and keeps silent watch of the blighted landscape. Its spindly limbs of bone slip between the rubble with otherworldly speed.", + "default_faction": "zombie", + "bodytype": "spider", + "species": [ "ZOMBIE", "HUMAN" ], + "diff": 5, + "volume": "122500 ml", + "weight": "81500 g", + "hp": 10, + "speed": 250, + "material": [ "flesh" ], + "symbol": "M", + "color": "red_yellow", + "aggression": 100, + "morale": 100, + "melee_skill": 5, + "melee_dice": 5, + "melee_dice_sides": 2, + "melee_cut": 12, + "armor_bash": 2, + "armor_cut": 10, + "armor_bullet": 10, + "vision_day": 1, + "special_attacks": [ { "type": "leap", "cooldown": 5, "max_range": 3, "allow_no_target": true }, [ "scratch", 5 ] ], + "death_function": [ "GAS" ], + "flags": [ "SEES", "HEARS", "GOODHEARING", "WARM", "POISON", "CLIMBS", "NO_BREATHE", "CLIMBS", "HARDTOSHOOT" ] + }, + { + "id": "mon_zombie_hanging_innards", + "type": "MONSTER", + "name": { "str": "hanging innards" }, + "description": "Great snakes of flesh hang from the ceiling above, madly thrashing and reaching about.", + "default_faction": "zombie", + "bodytype": "spider", + "species": [ "ZOMBIE", "HUMAN" ], + "diff": 5, + "volume": "122500 ml", + "weight": "81500 g", + "hp": 200, + "speed": 110, + "material": [ "flesh" ], + "symbol": "S", + "color": "magenta_red", + "aggression": 100, + "morale": 100, + "melee_skill": 5, + "melee_dice": 5, + "melee_dice_sides": 2, + "armor_bash": 6, + "armor_cut": 10, + "armor_bullet": 6, + "vision_day": 2, + "vision_night": 2, + "harvest": "exempt", + "special_attacks": [ [ "RANGED_PULL", 20 ], [ "SMASH", 20 ] ], + "death_function": [ "GAS" ], + "flags": [ "SEES", "HEARS", "SMELLS", "WARM", "POISON", "CLIMBS", "NO_BREATHE", "IMMOBILE" ] + }, + { + "id": "mon_zombie_giant_heart", + "type": "MONSTER", + "name": { "str": "spasming lump" }, + "description": "A great pile of merged bodies and mutated flesh. It spasms in an arrhythmic and desperate manner.", + "default_faction": "zombie", + "volume": "875000 ml", + "weight": "200 kg", + "species": [ "ZOMBIE", "HUMAN" ], + "diff": 1, + "hp": 3, + "speed": 100, + "material": [ "flesh" ], + "symbol": "O", + "color": "red_yellow", + "morale": 10, + "vision_day": 60, + "vision_night": 60, + "armor_bash": 50, + "armor_cut": 25, + "armor_bullet": 20, + "harvest": "exempt", + "death_function": [ "NORMAL" ], + "flags": [ "SEES", "HEARS", "SMELLS", "IMMOBILE", "WARM", "POISON", "IMMOBILE", "NO_BREATHE", "FILTHY" ] + }, { "id": "mon_zombie_living_wall", "type": "MONSTER", From 69cf4495c25f0ed3dc618e725f56f96a4354c33e Mon Sep 17 00:00:00 2001 From: TobalJackson Date: Sat, 4 Jul 2020 03:17:40 -0500 Subject: [PATCH 083/420] added safe_fuel_threshold option for bionic control (#41374) --- src/bionics.cpp | 76 ++++++++++++++++++++++++++++++++++++++++++---- src/bionics.h | 4 +++ src/bionics_ui.cpp | 5 ++- 3 files changed, 78 insertions(+), 7 deletions(-) diff --git a/src/bionics.cpp b/src/bionics.cpp index 495641fffce94..ce109c669b82a 100644 --- a/src/bionics.cpp +++ b/src/bionics.cpp @@ -1274,8 +1274,11 @@ bool Character::burn_fuel( int b, bool start ) } if( !bio.has_flag( flag_SAFE_FUEL_OFF ) && - get_power_level() + units::from_kilojoule( fuel_energy ) * effective_efficiency - > get_max_power_level() ) { + ( ( get_power_level() + units::from_kilojoule( fuel_energy ) * effective_efficiency > + get_max_power_level() ) || + ( ( ( get_power_level() + units::from_kilojoule( fuel_energy ) * effective_efficiency ) > + ( get_max_power_level() * bio.get_safe_fuel_thresh() ) ) + ) ) ) { if( is_metabolism_powered ) { add_msg_player_or_npc( m_info, _( "Your %s turns off to not waste calories." ), _( "'s %s turns off to not waste calories." ), @@ -2836,8 +2839,42 @@ void bionic::toggle_safe_fuel_mod() } if( !has_flag( flag_SAFE_FUEL_OFF ) ) { set_flag( flag_SAFE_FUEL_OFF ); + set_safe_fuel_thresh( 2.0 ); } else { - remove_flag( flag_SAFE_FUEL_OFF ); + uilist tmenu; + tmenu.text = _( "Chose Safe Fuel Level Threshold" ); + tmenu.addentry( 1, true, 'o', _( "Full Power" ) ); + if( get_auto_start_thresh() < 0.80 ) { + tmenu.addentry( 2, true, 't', _( "Above 80 %%" ) ); + } + if( get_auto_start_thresh() < 0.55 ) { + tmenu.addentry( 3, true, 'f', _( "Above 55 %%" ) ); + } + if( get_auto_start_thresh() < 0.30 ) { + tmenu.addentry( 4, true, 's', _( "Above 30 %%" ) ); + } + tmenu.query(); + + switch( tmenu.ret ) { + case 1: + remove_flag( flag_SAFE_FUEL_OFF ); + set_safe_fuel_thresh( 1.0 ); + break; + case 2: + remove_flag( flag_SAFE_FUEL_OFF ); + set_safe_fuel_thresh( 0.80 ); + break; + case 3: + remove_flag( flag_SAFE_FUEL_OFF ); + set_safe_fuel_thresh( 0.55 ); + break; + case 4: + remove_flag( flag_SAFE_FUEL_OFF ); + set_safe_fuel_thresh( 0.30 ); + break; + default: + break; + } } } @@ -2850,9 +2887,15 @@ void bionic::toggle_auto_start_mod() uilist tmenu; tmenu.text = _( "Chose Start Power Level Threshold" ); tmenu.addentry( 1, true, 'o', _( "No Power Left" ) ); - tmenu.addentry( 2, true, 't', _( "Below 25 %%" ) ); - tmenu.addentry( 3, true, 'f', _( "Below 50 %%" ) ); - tmenu.addentry( 4, true, 's', _( "Below 75 %%" ) ); + if( get_safe_fuel_thresh() > 0.25 ) { + tmenu.addentry( 2, true, 't', _( "Below 25 %%" ) ); + } + if( get_safe_fuel_thresh() > 0.50 ) { + tmenu.addentry( 3, true, 'f', _( "Below 50 %%" ) ); + } + if( get_safe_fuel_thresh() > 0.75 ) { + tmenu.addentry( 4, true, 's', _( "Below 75 %%" ) ); + } tmenu.query(); switch( tmenu.ret ) { @@ -2891,6 +2934,21 @@ bool bionic::is_auto_start_on() const return get_auto_start_thresh() > -1.0; } +float bionic::get_safe_fuel_thresh() const +{ + return safe_fuel_threshold; +} + +bool bionic::is_safe_fuel_on() const +{ + return get_safe_fuel_thresh() < 2.0; +} + +void bionic::set_safe_fuel_thresh( float val ) +{ + safe_fuel_threshold = val; +} + void bionic::serialize( JsonOut &json ) const { json.start_object(); @@ -2907,6 +2965,9 @@ void bionic::serialize( JsonOut &json ) const if( is_auto_start_on() ) { json.member( "auto_start_threshold", auto_start_threshold ); } + if( is_safe_fuel_on() ) { + json.member( "safe_fuel_threshold", safe_fuel_threshold ); + } json.end_object(); } @@ -2930,6 +2991,9 @@ void bionic::deserialize( JsonIn &jsin ) if( jo.has_float( "auto_start_threshold" ) ) { auto_start_threshold = jo.get_float( "auto_start_threshold" ); } + if( jo.has_float( "safe_fuel_threshold" ) ) { + safe_fuel_threshold = jo.get_float( "safe_fuel_threshold" ); + } if( jo.has_array( "bionic_tags" ) ) { for( const std::string line : jo.get_array( "bionic_tags" ) ) { bionic_tags.insert( line ); diff --git a/src/bionics.h b/src/bionics.h index cbbc564606469..b8dd19c7d3a80 100644 --- a/src/bionics.h +++ b/src/bionics.h @@ -181,6 +181,9 @@ struct bionic { float get_auto_start_thresh() const; bool is_auto_start_on() const; + void set_safe_fuel_thresh( float val ); + float get_safe_fuel_thresh() const; + bool is_safe_fuel_on() const; bool activate_spell( Character &caster ); void serialize( JsonOut &json ) const; @@ -189,6 +192,7 @@ struct bionic { // generic bionic specific flags cata::flat_set bionic_tags; float auto_start_threshold = -1.0; + float safe_fuel_threshold = -1.0; }; // A simpler wrapper to allow forward declarations of it. std::vector can not diff --git a/src/bionics_ui.cpp b/src/bionics_ui.cpp index 25f2f09cb55ec..302cd658ca74c 100644 --- a/src/bionics_ui.cpp +++ b/src/bionics_ui.cpp @@ -194,7 +194,10 @@ static std::string build_bionic_poweronly_string( const bionic &bio ) } if( !bio.has_flag( flag_SAFE_FUEL_OFF ) && ( !bio.info().fuel_opts.empty() || bio.info().is_remote_fueled ) ) { - properties.push_back( _( "(fuel saving ON)" ) ); + //properties.push_back( _( "(fuel saving ON)" ) ); + const std::string label = string_format( _( "(fuel saving ON > %d %%)" ), + static_cast( bio.get_safe_fuel_thresh() * 100 ) ); + properties.push_back( label ); } if( bio.is_auto_start_on() && ( !bio.info().fuel_opts.empty() || bio.info().is_remote_fueled ) ) { const std::string label = string_format( _( "(auto start < %d %%)" ), From b8e1130f0f7f5890b3db39bd11c1da1d8fa7bd70 Mon Sep 17 00:00:00 2001 From: LucasHoage Date: Sat, 4 Jul 2020 06:32:11 -0400 Subject: [PATCH 084/420] Changed rubber hose stats based on discord feedback --- data/json/items/tool/workshop.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/data/json/items/tool/workshop.json b/data/json/items/tool/workshop.json index ed256a47020e6..28322a5f4e7b1 100644 --- a/data/json/items/tool/workshop.json +++ b/data/json/items/tool/workshop.json @@ -339,8 +339,8 @@ "volume": "500 ml", "price": 200, "price_postapoc": 100, - "to_hit": 3, - "bashing": 4, + "to_hit": -1, + "bashing": 2, "material": [ "rubber" ], "symbol": ",", "color": "green", From 3f9bdf480eb221f62e21be53cdc4de6241a07060 Mon Sep 17 00:00:00 2001 From: BevapDin Date: Fri, 26 Jun 2020 19:33:55 +0200 Subject: [PATCH 085/420] Move check for trap::is_always_invisible into trap::can_see `can_see` is a more commonly used function and it is easy to forget checking for `is_always_invisible`. --- src/player.cpp | 2 +- src/trap.cpp | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/player.cpp b/src/player.cpp index 643ff5d2c31a3..c99d48e6265d2 100644 --- a/src/player.cpp +++ b/src/player.cpp @@ -863,7 +863,7 @@ void player::search_surroundings() if( !sees( tp ) ) { continue; } - if( tr.is_always_invisible() || tr.can_see( tp, *this ) ) { + if( tr.can_see( tp, *this ) ) { // Already seen, or can never be seen continue; } diff --git a/src/trap.cpp b/src/trap.cpp index 3fe2a01f2a700..9a2ab37b14427 100644 --- a/src/trap.cpp +++ b/src/trap.cpp @@ -226,6 +226,9 @@ bool trap::can_see( const tripoint &pos, const Character &p ) const // There is no trap at all, so logically one can not see it. return false; } + if( is_always_invisible() ) { + return false; + } return visibility < 0 || p.knows_trap( pos ); } From b02fecf284b5ad97473c81340269ce45ad51380a Mon Sep 17 00:00:00 2001 From: BevapDin Date: Fri, 26 Jun 2020 20:30:31 +0200 Subject: [PATCH 086/420] Check for trap visiblity via trap::can_see Instead of using the visibility directly. Note that `can_see` handles the `is_null` case as well. it also checks for the trap being a known trap. --- src/iuse.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/iuse.cpp b/src/iuse.cpp index 2dd9d35b4e1f1..8a0ada47c01ce 100644 --- a/src/iuse.cpp +++ b/src/iuse.cpp @@ -7003,7 +7003,7 @@ static std::string colorized_trap_name_at( const tripoint &point ) { const trap &trap = get_map().tr_at( point ); std::string name; - if( !trap.is_null() && trap.get_visibility() <= 1 ) { + if( trap.can_see( point, g->u ) ) { name = colorize( trap.name(), trap.color ) + _( " on " ); } return name; From 5fa65495252b57638eb0906ab8b071fd7e12d81f Mon Sep 17 00:00:00 2001 From: BevapDin Date: Fri, 26 Jun 2020 19:39:07 +0200 Subject: [PATCH 087/420] Refactor condition checking to be simpler as as list of excluding conditions --- src/character.cpp | 32 +++++++++++++++++++------------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/src/character.cpp b/src/character.cpp index 603c2e4a5bd36..ccbe5bc1d1c9f 100644 --- a/src/character.cpp +++ b/src/character.cpp @@ -8158,22 +8158,28 @@ tripoint Character::adjacent_tile() const // Don't consider player position continue; } + if( g->critter_at( p ) != nullptr ) { + continue; + } + if( here.impassable( p ) ) { + continue; + } const trap &curtrap = here.tr_at( p ); - if( g->critter_at( p ) == nullptr && here.passable( p ) && - ( curtrap.is_null() || curtrap.is_benign() ) ) { - // Only consider tile if unoccupied, passable and has no traps - dangerous_fields = 0; - auto &tmpfld = here.field_at( p ); - for( auto &fld : tmpfld ) { - const field_entry &cur = fld.second; - if( cur.is_dangerous() ) { - dangerous_fields++; - } + if( !curtrap.is_null() && !curtrap.is_benign() ) { + continue; + } + // Only consider tile if unoccupied, passable and has no traps + dangerous_fields = 0; + auto &tmpfld = here.field_at( p ); + for( auto &fld : tmpfld ) { + const field_entry &cur = fld.second; + if( cur.is_dangerous() ) { + dangerous_fields++; } + } - if( dangerous_fields == 0 ) { - ret.push_back( p ); - } + if( dangerous_fields == 0 ) { + ret.push_back( p ); } } From f8bc908235ae6765895e81b6626d582e17fa2f24 Mon Sep 17 00:00:00 2001 From: BevapDin Date: Fri, 26 Jun 2020 20:01:35 +0200 Subject: [PATCH 088/420] Move check for trap being detectable via ground sonar into a function. This can later be changed to some member. --- src/player.cpp | 4 +--- src/trap.cpp | 6 ++++++ src/trap.h | 4 ++++ 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/src/player.cpp b/src/player.cpp index c99d48e6265d2..08c0a12d83236 100644 --- a/src/player.cpp +++ b/src/player.cpp @@ -852,9 +852,7 @@ void player::search_surroundings() if( tr.is_null() || tp == pos() ) { continue; } - if( has_active_bionic( bio_ground_sonar ) && !knows_trap( tp ) && - ( tr.loadid == tr_beartrap_buried || - tr.loadid == tr_landmine_buried || tr.loadid == tr_sinkhole ) ) { + if( has_active_bionic( bio_ground_sonar ) && !knows_trap( tp ) && tr.detected_by_ground_sonar() ) { const std::string direction = direction_name( direction_from( pos(), tp ) ); add_msg_if_player( m_warning, _( "Your ground sonar detected a %1$s to the %2$s!" ), tr.name(), direction ); diff --git a/src/trap.cpp b/src/trap.cpp index 9a2ab37b14427..15c7c2f703018 100644 --- a/src/trap.cpp +++ b/src/trap.cpp @@ -190,6 +190,12 @@ void trap::reset() trap_factory.reset(); } +bool trap::detected_by_ground_sonar() const +{ + // @TODO make this a property + return loadid == tr_beartrap_buried || loadid == tr_landmine_buried || loadid == tr_sinkhole; +} + bool trap::detect_trap( const tripoint &pos, const Character &p ) const { // Some decisions are based around: diff --git a/src/trap.h b/src/trap.h index 3ee31bfc1318a..27348ceb5b4fe 100644 --- a/src/trap.h +++ b/src/trap.h @@ -159,6 +159,10 @@ struct trap { bool is_benign() const { return benign; } + /** + * Whether this kind of trap will be detected by ground sonar (e.g. via the bionic). + */ + bool detected_by_ground_sonar() const; /** Player has not yet seen the trap and returns the variable chance, at this moment, of whether the trap is seen or not. */ bool detect_trap( const tripoint &pos, const Character &p ) const; From 6ef0412191550e413a0851e84ed5764726aff6af Mon Sep 17 00:00:00 2001 From: BevapDin Date: Fri, 26 Jun 2020 20:04:24 +0200 Subject: [PATCH 089/420] Move creating describe string for traps for editmap into a member of trap This allows to make some of the properties it shows private. --- src/editmap.cpp | 4 +--- src/trap.cpp | 6 ++++++ src/trap.h | 2 ++ 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/editmap.cpp b/src/editmap.cpp index 1612f7a3fdf34..5072372999a28 100644 --- a/src/editmap.cpp +++ b/src/editmap.cpp @@ -959,9 +959,7 @@ std::string describe( const furn_t &type ) template<> std::string describe( const trap &type ) { - return string_format( _( "Visible: %d\nAvoidance: %d\nDifficulty: %d\nBenign: %s" ), - type.get_visibility(), type.get_avoidance(), type.get_difficulty(), - type.is_benign() ? _( "Yes" ) : _( "No" ) ); + return type.debug_describe(); } template diff --git a/src/trap.cpp b/src/trap.cpp index 15c7c2f703018..a05ca328b0bdb 100644 --- a/src/trap.cpp +++ b/src/trap.cpp @@ -393,3 +393,9 @@ void trap::finalize() tr_snake = trapfind( "tr_snake" ); tr_glass_pit = trapfind( "tr_glass_pit" ); } + +std::string trap::debug_describe() const +{ + return string_format( _( "Visible: %d\nAvoidance: %d\nDifficulty: %d\nBenign: %s" ), visibility, + avoidance, difficulty, is_benign() ? _( "Yes" ) : _( "No" ) ); +} diff --git a/src/trap.h b/src/trap.h index 27348ceb5b4fe..74bc768b51cc7 100644 --- a/src/trap.h +++ b/src/trap.h @@ -206,6 +206,8 @@ struct trap { */ void load( const JsonObject &jo, const std::string &src ); + std::string debug_describe() const; + /*@{*/ /** * @name Funnels From 290e658f3c08e6081deb877eacbed5c35dcf4af1 Mon Sep 17 00:00:00 2001 From: BevapDin Date: Fri, 26 Jun 2020 20:07:14 +0200 Subject: [PATCH 090/420] Move check for trap being trivial to spot into a function of trap. --- src/player.cpp | 2 +- src/trap.cpp | 6 ++++++ src/trap.h | 3 +++ 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/player.cpp b/src/player.cpp index 08c0a12d83236..1a721d393cdaf 100644 --- a/src/player.cpp +++ b/src/player.cpp @@ -867,7 +867,7 @@ void player::search_surroundings() } // Chance to detect traps we haven't yet seen. if( tr.detect_trap( tp, *this ) ) { - if( tr.get_visibility() > 0 ) { + if( !tr.is_trivial_to_spot() ) { // Only bug player about traps that aren't trivial to spot. const std::string direction = direction_name( direction_from( pos(), tp ) ); diff --git a/src/trap.cpp b/src/trap.cpp index a05ca328b0bdb..5f1bef9b1198a 100644 --- a/src/trap.cpp +++ b/src/trap.cpp @@ -190,6 +190,12 @@ void trap::reset() trap_factory.reset(); } +bool trap::is_trivial_to_spot() const +{ + // @TODO technically the trap may not be detected even with visibility == 0, see trap::detect_trap + return visibility <= 0 && !is_always_invisible(); +} + bool trap::detected_by_ground_sonar() const { // @TODO make this a property diff --git a/src/trap.h b/src/trap.h index 74bc768b51cc7..a74b92f97a967 100644 --- a/src/trap.h +++ b/src/trap.h @@ -159,6 +159,9 @@ struct trap { bool is_benign() const { return benign; } + + bool is_trivial_to_spot() const; + /** * Whether this kind of trap will be detected by ground sonar (e.g. via the bionic). */ From d97de8c8077170173c54157cbf4e34e6f3015dcd Mon Sep 17 00:00:00 2001 From: BevapDin Date: Fri, 26 Jun 2020 20:10:52 +0200 Subject: [PATCH 091/420] Remove unused function trap::get_visibility Move the comments to the member that this function used to expose. --- src/trap.h | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/trap.h b/src/trap.h index a74b92f97a967..63f7c9b9f4d4d 100644 --- a/src/trap.h +++ b/src/trap.h @@ -92,7 +92,13 @@ struct trap { int sym = 0; nc_color color; private: - // 1 to ??, affects detection + /** + * How easy it is to spot the trap. Smaller values means it's easier to spot. + * 1 to ??, affects detection + */ + // @TODO it can be negative (?) + // @TODO Add checks for it having proper values + // @TODO check usage in combination with is_always_invisible int visibility = 1; // 0 to ??, affects avoidance int avoidance = 0; @@ -127,12 +133,6 @@ struct trap { bool is_always_invisible() const { return always_invisible; } - /** - * How easy it is to spot the trap. Smaller values means it's easier to spot. - */ - int get_visibility() const { - return visibility; - } std::string map_regen_target() const; From 273eee88c958adfe21948c0679571f498295775a Mon Sep 17 00:00:00 2001 From: BevapDin Date: Fri, 26 Jun 2020 20:20:22 +0200 Subject: [PATCH 092/420] Move check for handling traps while being mounted into iexamine::trap This is, so the visibility can be considered. Examining an *invisible* trap while being mounted would reveal the existance of the trap as the message "You cannot do that while mounted." would appear. --- src/game.cpp | 5 ++--- src/iexamine.cpp | 4 ++++ 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/game.cpp b/src/game.cpp index 6ba24b1f0e511..bb113357ac0cd 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -5908,10 +5908,9 @@ void game::examine( const tripoint &examp ) none = false; } - if( !m.tr_at( examp ).is_null() && !u.is_mounted() ) { + // iexamine::trap will handle the invisible traps. + if( !m.tr_at( examp ).is_null() ) { iexamine::trap( u, examp ); - } else if( !m.tr_at( examp ).is_null() && u.is_mounted() ) { - add_msg( m_warning, _( "You cannot do that while mounted." ) ); } // In case of teleport trap or somesuch diff --git a/src/iexamine.cpp b/src/iexamine.cpp index a79e5526dff67..7098a9f8765c4 100644 --- a/src/iexamine.cpp +++ b/src/iexamine.cpp @@ -3698,6 +3698,10 @@ void iexamine::trap( player &p, const tripoint &examp ) } const int possible = tr.get_difficulty(); bool seen = tr.can_see( examp, p ); + if( seen && g->u.is_mounted() ) { + add_msg( m_warning, _( "You cannot do that while mounted." ) ); + return; + } if( tr.loadid == tr_unfinished_construction || here.partial_con_at( examp ) ) { partial_con *pc = here.partial_con_at( examp ); if( pc ) { From 7b0bce9c1fd8834e6496d10138ea604b1e99e5fa Mon Sep 17 00:00:00 2001 From: BevapDin Date: Fri, 26 Jun 2020 20:28:31 +0200 Subject: [PATCH 093/420] Move iexamine::trap into trap. Being in `iexamine` allowed to be attached to *any* terrain / furniture, which made no sense (as the terrain / furniture may not even have a trap). This change also allows the function to use internal properties of the trap. --- doc/JSON_FLAGS.md | 1 - src/game.cpp | 6 ++---- src/iexamine.cpp | 21 +++++++++------------ src/iexamine.h | 1 - src/trap.h | 9 +++++++++ 5 files changed, 20 insertions(+), 18 deletions(-) diff --git a/doc/JSON_FLAGS.md b/doc/JSON_FLAGS.md index 9de5fccbca008..0b62e0d0339fe 100644 --- a/doc/JSON_FLAGS.md +++ b/doc/JSON_FLAGS.md @@ -631,7 +631,6 @@ List of known flags, used in both `terrain.json` and `furniture.json`. - ```shrub_wildveggies``` Pick a wild veggies shrub. - ```slot_machine``` Gamble. - ```toilet``` Either drink or get water out of the toilet. -- ```trap``` Interact with a trap. - ```water_source``` Drink or get water from a water source. ### Fungal Conversions Only diff --git a/src/game.cpp b/src/game.cpp index bb113357ac0cd..1fc718747647b 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -5908,10 +5908,8 @@ void game::examine( const tripoint &examp ) none = false; } - // iexamine::trap will handle the invisible traps. - if( !m.tr_at( examp ).is_null() ) { - iexamine::trap( u, examp ); - } + // trap::iexamine will handle the invisible traps. + m.tr_at( examp ).examine( u, examp ); // In case of teleport trap or somesuch if( player_pos != u.pos() ) { diff --git a/src/iexamine.cpp b/src/iexamine.cpp index 7098a9f8765c4..89ce25e1e9d1e 100644 --- a/src/iexamine.cpp +++ b/src/iexamine.cpp @@ -3689,20 +3689,19 @@ void iexamine::recycle_compactor( player &, const tripoint &examp ) } } -void iexamine::trap( player &p, const tripoint &examp ) +void trap::examine( player &p, const tripoint &examp ) const { map &here = get_map(); - const auto &tr = here.tr_at( examp ); - if( !p.is_player() || tr.is_null() ) { + if( !p.is_player() || is_null() ) { return; } - const int possible = tr.get_difficulty(); - bool seen = tr.can_see( examp, p ); + const int possible = get_difficulty(); + bool seen = can_see( examp, p ); if( seen && g->u.is_mounted() ) { add_msg( m_warning, _( "You cannot do that while mounted." ) ); return; } - if( tr.loadid == tr_unfinished_construction || here.partial_con_at( examp ) ) { + if( loadid == tr_unfinished_construction || here.partial_con_at( examp ) ) { partial_con *pc = here.partial_con_at( examp ); if( pc ) { if( g->u.fine_detail_vision_mod() > 4 && !g->u.has_trait( trait_DEBUG_HS ) ) { @@ -3732,17 +3731,16 @@ void iexamine::trap( player &p, const tripoint &examp ) } } if( seen && possible >= 99 ) { - add_msg( m_info, _( "That %s looks too dangerous to mess with. Best leave it alone." ), - tr.name() ); + add_msg( m_info, _( "That %s looks too dangerous to mess with. Best leave it alone." ), name() ); return; } // Some traps are not actual traps. Those should get a different query. if( seen && possible == 0 && - tr.get_avoidance() == 0 ) { // Separated so saying no doesn't trigger the other query. - if( query_yn( _( "There is a %s there. Take down?" ), tr.name() ) ) { + get_avoidance() == 0 ) { // Separated so saying no doesn't trigger the other query. + if( query_yn( _( "There is a %s there. Take down?" ), name() ) ) { here.disarm_trap( examp ); } - } else if( seen && query_yn( _( "There is a %s there. Disarm?" ), tr.name() ) ) { + } else if( seen && query_yn( _( "There is a %s there. Disarm?" ), name() ) ) { here.disarm_trap( examp ); } } @@ -6028,7 +6026,6 @@ iexamine_function iexamine_function_from_string( const std::string &function_nam { "tree_maple_tapped", &iexamine::tree_maple_tapped }, { "shrub_wildveggies", &iexamine::shrub_wildveggies }, { "recycle_compactor", &iexamine::recycle_compactor }, - { "trap", &iexamine::trap }, { "water_source", &iexamine::water_source }, { "clean_water_source", &iexamine::clean_water_source }, { "reload_furniture", &iexamine::reload_furniture }, diff --git a/src/iexamine.h b/src/iexamine.h index abb9665b081a7..2c59f1743d7f0 100644 --- a/src/iexamine.h +++ b/src/iexamine.h @@ -82,7 +82,6 @@ void shrub_marloss( player &p, const tripoint &examp ); void tree_marloss( player &p, const tripoint &examp ); void shrub_wildveggies( player &p, const tripoint &examp ); void recycle_compactor( player &p, const tripoint &examp ); -void trap( player &p, const tripoint &examp ); void water_source( player &p, const tripoint &examp ); void clean_water_source( player &, const tripoint &examp ); void kiln_empty( player &p, const tripoint &examp ); diff --git a/src/trap.h b/src/trap.h index 63f7c9b9f4d4d..f969c7e706b9b 100644 --- a/src/trap.h +++ b/src/trap.h @@ -134,6 +134,15 @@ struct trap { return always_invisible; } + /** + * Called when the player examines a tile. This is supposed to handled + * all kind of interaction of the player with the trap, including removal. + * It also handles visibility of the trap, and it does nothing when + * called on the null trap. + */ + // Implemented for historical reasons in iexamine.cpp + void examine( player &p, const tripoint &examp ) const; + std::string map_regen_target() const; /** From 614dc4e0110ff3bcb0886bc8b1b1a55a36f01c4f Mon Sep 17 00:00:00 2001 From: BevapDin Date: Fri, 26 Jun 2020 20:37:31 +0200 Subject: [PATCH 094/420] Add a wrapper for trap::can_see in class map This makes it easier to check for visible maps without actually getting the trap from the map first. --- src/action.cpp | 3 +-- src/map.cpp | 5 +++++ src/map.h | 2 ++ 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/action.cpp b/src/action.cpp index 55089b097c265..6c90378d8d543 100644 --- a/src/action.cpp +++ b/src/action.cpp @@ -658,8 +658,7 @@ bool can_examine_at( const tripoint &p ) return true; } - const trap &tr = here.tr_at( p ); - return tr.can_see( p, g->u ); + return here.can_see_trap_at( p, g->u ); } static bool can_pickup_at( const tripoint &p ) diff --git a/src/map.cpp b/src/map.cpp index a7bd4f69cd501..98583404a4ce9 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -5061,6 +5061,11 @@ std::list > map::get_rc_items( const tripoint &p ) return rc_pairs; } +bool map::can_see_trap_at( const tripoint &p, const Character &c ) const +{ + return tr_at( p ).can_see( p, c ); +} + const trap &map::tr_at( const tripoint &p ) const { if( !inbounds( p ) ) { diff --git a/src/map.h b/src/map.h index d3d52ac6a5a19..d54221b832fd4 100644 --- a/src/map.h +++ b/src/map.h @@ -1156,6 +1156,8 @@ class map void trap_set( const tripoint &p, const trap_id &type ); const trap &tr_at( const tripoint &p ) const; + /// See @ref trap::can_see, which is called for the trap here. + bool can_see_trap_at( const tripoint &p, const Character &c ) const; void disarm_trap( const tripoint &p ); void remove_trap( const tripoint &p ); From 199c08fe9a5b797b31ef3a883a6f39a4d37f34f2 Mon Sep 17 00:00:00 2001 From: BevapDin Date: Fri, 26 Jun 2020 20:45:43 +0200 Subject: [PATCH 095/420] Avoid repeated calls to trap::get_difficulty Just use the "cache" of that value stored as local variable. --- src/map.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/map.cpp b/src/map.cpp index 98583404a4ce9..c0560f33cf1a7 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -5157,7 +5157,7 @@ void map::disarm_trap( const tripoint &p ) int roll = rng( tSkillLevel, 4 * tSkillLevel ); // Some traps are not actual traps. Skip the rolls, different message and give the option to grab it right away. - if( tr.get_avoidance() == 0 && tr.get_difficulty() == 0 ) { + if( tr.get_avoidance() == 0 && diff == 0 ) { add_msg( _( "The %s is taken down." ), tr.name() ); tr.on_disarmed( *this, p ); return; @@ -5173,7 +5173,7 @@ void map::disarm_trap( const tripoint &p ) } if( roll >= diff ) { add_msg( _( "You disarm the trap!" ) ); - const int morale_buff = tr.get_avoidance() * 0.4 + tr.get_difficulty() + rng( 0, 4 ); + const int morale_buff = tr.get_avoidance() * 0.4 + diff + rng( 0, 4 ); g->u.rem_morale( MORALE_FAILURE ); g->u.add_morale( MORALE_ACCOMPLISHMENT, morale_buff, 40 ); tr.on_disarmed( *this, p ); From 92e6f053aaf5d88597a6bd4b99c9909618a702d2 Mon Sep 17 00:00:00 2001 From: BevapDin Date: Fri, 26 Jun 2020 20:48:57 +0200 Subject: [PATCH 096/420] Add function to easy trap take down. Instead of doing the same check at different places. --- src/iexamine.cpp | 3 +-- src/map.cpp | 2 +- src/trap.cpp | 5 +++++ src/trap.h | 5 +++++ 4 files changed, 12 insertions(+), 3 deletions(-) diff --git a/src/iexamine.cpp b/src/iexamine.cpp index 89ce25e1e9d1e..726e366991d79 100644 --- a/src/iexamine.cpp +++ b/src/iexamine.cpp @@ -3735,8 +3735,7 @@ void trap::examine( player &p, const tripoint &examp ) const return; } // Some traps are not actual traps. Those should get a different query. - if( seen && possible == 0 && - get_avoidance() == 0 ) { // Separated so saying no doesn't trigger the other query. + if( seen && easy_take_down() ) { // Separated so saying no doesn't trigger the other query. if( query_yn( _( "There is a %s there. Take down?" ), name() ) ) { here.disarm_trap( examp ); } diff --git a/src/map.cpp b/src/map.cpp index c0560f33cf1a7..c19e494c556ed 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -5157,7 +5157,7 @@ void map::disarm_trap( const tripoint &p ) int roll = rng( tSkillLevel, 4 * tSkillLevel ); // Some traps are not actual traps. Skip the rolls, different message and give the option to grab it right away. - if( tr.get_avoidance() == 0 && diff == 0 ) { + if( tr.easy_take_down() ) { add_msg( _( "The %s is taken down." ), tr.name() ); tr.on_disarmed( *this, p ); return; diff --git a/src/trap.cpp b/src/trap.cpp index 5f1bef9b1198a..6ea16c0ad0435 100644 --- a/src/trap.cpp +++ b/src/trap.cpp @@ -343,6 +343,11 @@ void trap::check_consistency() } } +bool trap::easy_take_down() const +{ + return avoidance == 0 && difficulty == 0; +} + void trap::finalize() { for( const trap &t_const : trap_factory.get_all() ) { diff --git a/src/trap.h b/src/trap.h index f969c7e706b9b..5c13b16f42717 100644 --- a/src/trap.h +++ b/src/trap.h @@ -168,6 +168,11 @@ struct trap { bool is_benign() const { return benign; } + /** + * @return True for traps that can simply be taken down without any skill check or similar. + * This usually applies to traps like funnels, rollmat. + */ + bool easy_take_down() const; bool is_trivial_to_spot() const; From 10d137aa74ea8f6a97dc83d1d568ee7e1bf5b055 Mon Sep 17 00:00:00 2001 From: BevapDin Date: Fri, 26 Jun 2020 20:55:22 +0200 Subject: [PATCH 097/420] Add function to check for traps that can not be disarmed. --- src/iexamine.cpp | 5 ++--- src/trap.cpp | 5 +++++ src/trap.h | 6 ++++++ 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/src/iexamine.cpp b/src/iexamine.cpp index 726e366991d79..aaaf20ac72468 100644 --- a/src/iexamine.cpp +++ b/src/iexamine.cpp @@ -3695,8 +3695,7 @@ void trap::examine( player &p, const tripoint &examp ) const if( !p.is_player() || is_null() ) { return; } - const int possible = get_difficulty(); - bool seen = can_see( examp, p ); + const bool seen = can_see( examp, p ); if( seen && g->u.is_mounted() ) { add_msg( m_warning, _( "You cannot do that while mounted." ) ); return; @@ -3730,7 +3729,7 @@ void trap::examine( player &p, const tripoint &examp ) const return; } } - if( seen && possible >= 99 ) { + if( seen && can_not_be_disarmed() ) { add_msg( m_info, _( "That %s looks too dangerous to mess with. Best leave it alone." ), name() ); return; } diff --git a/src/trap.cpp b/src/trap.cpp index 6ea16c0ad0435..f6d3773f1f098 100644 --- a/src/trap.cpp +++ b/src/trap.cpp @@ -348,6 +348,11 @@ bool trap::easy_take_down() const return avoidance == 0 && difficulty == 0; } +bool trap::can_not_be_disarmed() const +{ + return difficulty >= 99; +} + void trap::finalize() { for( const trap &t_const : trap_factory.get_all() ) { diff --git a/src/trap.h b/src/trap.h index 5c13b16f42717..909734cedc525 100644 --- a/src/trap.h +++ b/src/trap.h @@ -176,6 +176,12 @@ struct trap { bool is_trivial_to_spot() const; + /** + * Some traps are part of the terrain (e.g. pits) and can therefor not be disarmed + * via the usual mechanics. They can be "disarmed" by changing the terrain they are part of. + */ + bool can_not_be_disarmed() const; + /** * Whether this kind of trap will be detected by ground sonar (e.g. via the bionic). */ From 3f253f049801a5fb9e1c8102ee2cefdb5e7fd0a8 Mon Sep 17 00:00:00 2001 From: BevapDin Date: Fri, 26 Jun 2020 20:59:44 +0200 Subject: [PATCH 098/420] Slight restructuring of trap::examine to make the following simpler: Separates the if blocks and returns from within them. This style is already used in the function above this part. --- src/iexamine.cpp | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/iexamine.cpp b/src/iexamine.cpp index aaaf20ac72468..81d5de71745bd 100644 --- a/src/iexamine.cpp +++ b/src/iexamine.cpp @@ -3733,13 +3733,19 @@ void trap::examine( player &p, const tripoint &examp ) const add_msg( m_info, _( "That %s looks too dangerous to mess with. Best leave it alone." ), name() ); return; } + // Some traps are not actual traps. Those should get a different query. if( seen && easy_take_down() ) { // Separated so saying no doesn't trigger the other query. - if( query_yn( _( "There is a %s there. Take down?" ), name() ) ) { - here.disarm_trap( examp ); + if( !query_yn( _( "There is a %s there. Take down?" ), name() ) ) { + return; } - } else if( seen && query_yn( _( "There is a %s there. Disarm?" ), name() ) ) { here.disarm_trap( examp ); + return; + } + + if( seen && query_yn( _( "There is a %s there. Disarm?" ), name() ) ) { + here.disarm_trap( examp ); + return; } } From c85db26ed7cabc4c9a8a9d54474fd34216e2286e Mon Sep 17 00:00:00 2001 From: BevapDin Date: Fri, 26 Jun 2020 21:01:33 +0200 Subject: [PATCH 099/420] Remove redundant seen check in trap::examine Just check the visibility once and bail out quickly. Note that `trap::can_see` handles the `is_null` case. --- src/iexamine.cpp | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/src/iexamine.cpp b/src/iexamine.cpp index 81d5de71745bd..9c0782b19c47a 100644 --- a/src/iexamine.cpp +++ b/src/iexamine.cpp @@ -3692,11 +3692,16 @@ void iexamine::recycle_compactor( player &, const tripoint &examp ) void trap::examine( player &p, const tripoint &examp ) const { map &here = get_map(); - if( !p.is_player() || is_null() ) { + if( !p.is_player() ) { return; } - const bool seen = can_see( examp, p ); - if( seen && g->u.is_mounted() ) { + + // If the player can't see the trap, they can't interact with it. + if( !can_see( examp, p ) ) { + return; + } + + if( g->u.is_mounted() ) { add_msg( m_warning, _( "You cannot do that while mounted." ) ); return; } @@ -3729,13 +3734,13 @@ void trap::examine( player &p, const tripoint &examp ) const return; } } - if( seen && can_not_be_disarmed() ) { + if( can_not_be_disarmed() ) { add_msg( m_info, _( "That %s looks too dangerous to mess with. Best leave it alone." ), name() ); return; } // Some traps are not actual traps. Those should get a different query. - if( seen && easy_take_down() ) { // Separated so saying no doesn't trigger the other query. + if( easy_take_down() ) { // Separated so saying no doesn't trigger the other query. if( !query_yn( _( "There is a %s there. Take down?" ), name() ) ) { return; } @@ -3743,7 +3748,7 @@ void trap::examine( player &p, const tripoint &examp ) const return; } - if( seen && query_yn( _( "There is a %s there. Disarm?" ), name() ) ) { + if( query_yn( _( "There is a %s there. Disarm?" ), name() ) ) { here.disarm_trap( examp ); return; } From 9f7a24c96de953d3c332e4488a257f7a09641031 Mon Sep 17 00:00:00 2001 From: BevapDin Date: Fri, 26 Jun 2020 21:02:59 +0200 Subject: [PATCH 100/420] Move handling of special easy take down code from map::disarm_trap into caller. --- src/iexamine.cpp | 5 +++-- src/map.cpp | 3 --- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/iexamine.cpp b/src/iexamine.cpp index 9c0782b19c47a..8a5422cabd903 100644 --- a/src/iexamine.cpp +++ b/src/iexamine.cpp @@ -3739,12 +3739,13 @@ void trap::examine( player &p, const tripoint &examp ) const return; } - // Some traps are not actual traps. Those should get a different query. + // Some traps are not actual traps. Those should get a different query, no skill checks, and the option to grab it right away. if( easy_take_down() ) { // Separated so saying no doesn't trigger the other query. if( !query_yn( _( "There is a %s there. Take down?" ), name() ) ) { return; } - here.disarm_trap( examp ); + add_msg( _( "The %s is taken down." ), name() ); + on_disarmed( here, examp ); return; } diff --git a/src/map.cpp b/src/map.cpp index c19e494c556ed..cb792ebf77118 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -5156,10 +5156,7 @@ void map::disarm_trap( const tripoint &p ) const int diff = tr.get_difficulty(); int roll = rng( tSkillLevel, 4 * tSkillLevel ); - // Some traps are not actual traps. Skip the rolls, different message and give the option to grab it right away. if( tr.easy_take_down() ) { - add_msg( _( "The %s is taken down." ), tr.name() ); - tr.on_disarmed( *this, p ); return; } From 65a48e6860a4bec21f71d7e8c0353cf622e634e2 Mon Sep 17 00:00:00 2001 From: BevapDin Date: Fri, 26 Jun 2020 21:05:21 +0200 Subject: [PATCH 101/420] Don't call map::disarm_trap when all it does it to remove the trap. Unfinshed construction is an easy to take down trap, so `map::disarm_trap` will essentially only remove the trap from the map. We can do this directly. --- src/iexamine.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/iexamine.cpp b/src/iexamine.cpp index 8a5422cabd903..572d8c14dc6a6 100644 --- a/src/iexamine.cpp +++ b/src/iexamine.cpp @@ -3716,7 +3716,7 @@ void trap::examine( player &p, const tripoint &examp ) const if( !query_yn( _( "Unfinished task: %s, %d%% complete here, continue construction?" ), built.description, pc->counter / 100000 ) ) { if( query_yn( _( "Cancel construction?" ) ) ) { - here.disarm_trap( examp ); + tr.on_disarmed( here, examp ); for( const item &it : pc->components ) { here.add_item_or_charges( g->u.pos(), it ); } From 7ee58540aaccad6dfae4011260180cc98e0d494d Mon Sep 17 00:00:00 2001 From: BevapDin Date: Fri, 26 Jun 2020 21:09:50 +0200 Subject: [PATCH 102/420] Move content of map::disarm_trap into trap::examine There it is part of the trap class and --- src/iexamine.cpp | 43 +++++++++++++++++++++++++++++++++++- src/map.cpp | 57 ------------------------------------------------ src/map.h | 1 - 3 files changed, 42 insertions(+), 59 deletions(-) diff --git a/src/iexamine.cpp b/src/iexamine.cpp index 572d8c14dc6a6..7eaf8e5213380 100644 --- a/src/iexamine.cpp +++ b/src/iexamine.cpp @@ -63,6 +63,7 @@ #include "messages.h" #include "mission_companion.h" #include "monster.h" +#include "morale_types.h" #include "mtype.h" #include "npc.h" #include "options.h" @@ -157,6 +158,7 @@ static const skill_id skill_cooking( "cooking" ); static const skill_id skill_fabrication( "fabrication" ); static const skill_id skill_mechanics( "mechanics" ); static const skill_id skill_survival( "survival" ); +static const skill_id skill_traps( "traps" ); static const trait_id trait_AMORPHOUS( "AMORPHOUS" ); static const trait_id trait_ARACHNID_ARMS_OK( "ARACHNID_ARMS_OK" ); @@ -3750,7 +3752,46 @@ void trap::examine( player &p, const tripoint &examp ) const } if( query_yn( _( "There is a %s there. Disarm?" ), name() ) ) { - here.disarm_trap( examp ); + const int tSkillLevel = g->u.get_skill_level( skill_traps ); + int roll = rng( tSkillLevel, 4 * tSkillLevel ); + + ///\EFFECT_PER increases chance of disarming trap + + ///\EFFECT_DEX increases chance of disarming trap + + ///\EFFECT_TRAPS increases chance of disarming trap + while( ( rng( 5, 20 ) < g->u.per_cur || rng( 1, 20 ) < g->u.dex_cur ) && roll < 50 ) { + roll++; + } + if( roll >= difficulty ) { + add_msg( _( "You disarm the trap!" ) ); + const int morale_buff = avoidance * 0.4 + difficulty + rng( 0, 4 ); + g->u.rem_morale( MORALE_FAILURE ); + g->u.add_morale( MORALE_ACCOMPLISHMENT, morale_buff, 40 ); + on_disarmed( here, examp ); + if( difficulty > 1.25 * tSkillLevel ) { // failure might have set off trap + g->u.practice( skill_traps, 1.5 * ( difficulty - tSkillLevel ) ); + } + } else if( roll >= difficulty * .8 ) { + add_msg( _( "You fail to disarm the trap." ) ); + const int morale_debuff = -rng( 6, 18 ); + g->u.rem_morale( MORALE_ACCOMPLISHMENT ); + g->u.add_morale( MORALE_FAILURE, morale_debuff, -40 ); + if( difficulty > 1.25 * tSkillLevel ) { + g->u.practice( skill_traps, 1.5 * ( difficulty - tSkillLevel ) ); + } + } else { + add_msg( m_bad, _( "You fail to disarm the trap, and you set it off!" ) ); + const int morale_debuff = -rng( 12, 24 ); + g->u.rem_morale( MORALE_ACCOMPLISHMENT ); + g->u.add_morale( MORALE_FAILURE, morale_debuff, -40 ); + trigger( examp, &g->u ); + if( difficulty - roll <= 6 ) { + // Give xp for failing, but not if we failed terribly (in which + // case the trap may not be disarmable). + g->u.practice( skill_traps, 2 * difficulty ); + } + } return; } } diff --git a/src/map.cpp b/src/map.cpp index cb792ebf77118..43d85c4169723 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -114,8 +114,6 @@ static const itype_id itype_welder( "welder" ); static const mtype_id mon_zombie( "mon_zombie" ); -static const skill_id skill_traps( "traps" ); - static const efftype_id effect_boomered( "boomered" ); static const efftype_id effect_crushed( "crushed" ); @@ -5144,61 +5142,6 @@ void map::trap_set( const tripoint &p, const trap_id &type ) } } -void map::disarm_trap( const tripoint &p ) -{ - const trap &tr = tr_at( p ); - if( tr.is_null() ) { - debugmsg( "Tried to disarm a trap where there was none (%d %d %d)", p.x, p.y, p.z ); - return; - } - - const int tSkillLevel = g->u.get_skill_level( skill_traps ); - const int diff = tr.get_difficulty(); - int roll = rng( tSkillLevel, 4 * tSkillLevel ); - - if( tr.easy_take_down() ) { - return; - } - - ///\EFFECT_PER increases chance of disarming trap - - ///\EFFECT_DEX increases chance of disarming trap - - ///\EFFECT_TRAPS increases chance of disarming trap - while( ( rng( 5, 20 ) < g->u.per_cur || rng( 1, 20 ) < g->u.dex_cur ) && roll < 50 ) { - roll++; - } - if( roll >= diff ) { - add_msg( _( "You disarm the trap!" ) ); - const int morale_buff = tr.get_avoidance() * 0.4 + diff + rng( 0, 4 ); - g->u.rem_morale( MORALE_FAILURE ); - g->u.add_morale( MORALE_ACCOMPLISHMENT, morale_buff, 40 ); - tr.on_disarmed( *this, p ); - if( diff > 1.25 * tSkillLevel ) { // failure might have set off trap - g->u.practice( skill_traps, 1.5 * ( diff - tSkillLevel ) ); - } - } else if( roll >= diff * .8 ) { - add_msg( _( "You fail to disarm the trap." ) ); - const int morale_debuff = -rng( 6, 18 ); - g->u.rem_morale( MORALE_ACCOMPLISHMENT ); - g->u.add_morale( MORALE_FAILURE, morale_debuff, -40 ); - if( diff > 1.25 * tSkillLevel ) { - g->u.practice( skill_traps, 1.5 * ( diff - tSkillLevel ) ); - } - } else { - add_msg( m_bad, _( "You fail to disarm the trap, and you set it off!" ) ); - const int morale_debuff = -rng( 12, 24 ); - g->u.rem_morale( MORALE_ACCOMPLISHMENT ); - g->u.add_morale( MORALE_FAILURE, morale_debuff, -40 ); - tr.trigger( p, &g->u ); - if( diff - roll <= 6 ) { - // Give xp for failing, but not if we failed terribly (in which - // case the trap may not be disarmable). - g->u.practice( skill_traps, 2 * diff ); - } - } -} - void map::remove_trap( const tripoint &p ) { if( !inbounds( p ) ) { diff --git a/src/map.h b/src/map.h index d54221b832fd4..d1dd37d5656f9 100644 --- a/src/map.h +++ b/src/map.h @@ -1159,7 +1159,6 @@ class map /// See @ref trap::can_see, which is called for the trap here. bool can_see_trap_at( const tripoint &p, const Character &c ) const; - void disarm_trap( const tripoint &p ); void remove_trap( const tripoint &p ); const std::vector &get_furn_field_locations() const; const std::vector &trap_locations( const trap_id &type ) const; From 3d99f035fb2f9fdb28d2cb588462fbacc0c3a032 Mon Sep 17 00:00:00 2001 From: BevapDin Date: Fri, 26 Jun 2020 21:11:41 +0200 Subject: [PATCH 103/420] Remove unused function trap::get_difficulty --- src/trap.h | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/src/trap.h b/src/trap.h index 909734cedc525..bc828328dc70a 100644 --- a/src/trap.h +++ b/src/trap.h @@ -102,7 +102,11 @@ struct trap { int visibility = 1; // 0 to ??, affects avoidance int avoidance = 0; - // 0 to ??, difficulty of assembly & disassembly + /* + * This is used when disarming the trap. A value of 0 means disarming will always work + * (e.g. for funnels), a values of 99 means it can not be disarmed at all. Smaller values + * makes it easier to disarm the trap. + */ int difficulty = 0; // 0 to ??, trap radius int trap_radius = 0; @@ -153,14 +157,6 @@ struct trap { int get_avoidance() const { return avoidance; } - /** - * This is used when disarming the trap. A value of 0 means disarming will always work - * (e.g. for funnels), a values of 99 means it can not be disarmed at all. Smaller values - * makes it easier to disarm the trap. - */ - int get_difficulty() const { - return difficulty; - } /** * If true, this is not really a trap and there won't be any safety queries before stepping * onto it (e.g. for funnels). From 6e7c8fbf26e0da095ae3b4025ebd29c2146147ef Mon Sep 17 00:00:00 2001 From: BevapDin Date: Fri, 26 Jun 2020 21:33:09 +0200 Subject: [PATCH 104/420] Make trap::trigger simpler to call. Adds two overloads, one for triggering via item, one for triggering via creature. --- src/ballistics.cpp | 2 +- src/iexamine.cpp | 2 +- src/iuse_actor.cpp | 2 +- src/map.cpp | 2 +- src/trap.cpp | 13 +++++++++++++ src/trap.h | 19 ++++++++++++++++++- 6 files changed, 35 insertions(+), 5 deletions(-) diff --git a/src/ballistics.cpp b/src/ballistics.cpp index 5a8e7793a6216..c33b5437524b4 100644 --- a/src/ballistics.cpp +++ b/src/ballistics.cpp @@ -138,7 +138,7 @@ static void drop_or_embed_projectile( const dealt_projectile_attack &attack ) } const trap &tr = here.tr_at( pt ); if( tr.triggered_by_item( dropped_item ) ) { - tr.trigger( pt, nullptr, &dropped_item ); + tr.trigger( pt, dropped_item ); } } } diff --git a/src/iexamine.cpp b/src/iexamine.cpp index 7eaf8e5213380..cd3c2cd682b36 100644 --- a/src/iexamine.cpp +++ b/src/iexamine.cpp @@ -3785,7 +3785,7 @@ void trap::examine( player &p, const tripoint &examp ) const const int morale_debuff = -rng( 12, 24 ); g->u.rem_morale( MORALE_ACCOMPLISHMENT ); g->u.add_morale( MORALE_FAILURE, morale_debuff, -40 ); - trigger( examp, &g->u ); + trigger( examp, g->u ); if( difficulty - roll <= 6 ) { // Give xp for failing, but not if we failed terribly (in which // case the trap may not be disarmable). diff --git a/src/iuse_actor.cpp b/src/iuse_actor.cpp index 5b8dbc93da0b5..38883c5d7a1a8 100644 --- a/src/iuse_actor.cpp +++ b/src/iuse_actor.cpp @@ -3584,7 +3584,7 @@ bool place_trap_actor::is_allowed( player &p, const tripoint &pos, const std::st name ); } else { p.add_msg_if_player( m_bad, _( "You trigger a %s!" ), existing_trap.name() ); - existing_trap.trigger( pos, &p ); + existing_trap.trigger( pos, p ); } return false; } diff --git a/src/map.cpp b/src/map.cpp index 43d85c4169723..03227e371f557 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -8016,7 +8016,7 @@ void map::creature_on_trap( Creature &c, const bool may_avoid ) if( may_avoid && c.avoid_trap( c.pos(), tr ) ) { return; } - tr.trigger( c.pos(), &c ); + tr.trigger( c.pos(), c ); } template diff --git a/src/trap.cpp b/src/trap.cpp index f6d3773f1f098..c5dcdd145605d 100644 --- a/src/trap.cpp +++ b/src/trap.cpp @@ -244,8 +244,21 @@ bool trap::can_see( const tripoint &pos, const Character &p ) const return visibility < 0 || p.knows_trap( pos ); } +void trap::trigger( const tripoint &pos, Creature &creature ) const +{ + return trigger( pos, &creature, nullptr ); +} + +void trap::trigger( const tripoint &pos, item &item ) const +{ + return trigger( pos, nullptr, &item ); +} + void trap::trigger( const tripoint &pos, Creature *creature, item *item ) const { + if( is_null() ) { + return; + } const bool is_real_creature = creature != nullptr && !creature->is_hallucination(); if( is_real_creature || item != nullptr ) { bool triggered = act( pos, creature, item ); diff --git a/src/trap.h b/src/trap.h index bc828328dc70a..1095f9ec3f3e2 100644 --- a/src/trap.h +++ b/src/trap.h @@ -190,6 +190,7 @@ struct trap { * the trap) or by the visibility of the trap (the trap is not hidden at all)? */ bool can_see( const tripoint &pos, const Character &p ) const; + private: /** * Trigger trap effects. * @param creature The creature that triggered the trap, it does not necessarily have to @@ -199,11 +200,27 @@ struct trap { * @param pos The location of the trap in the main map. * @param item The item that triggered the trap */ - void trigger( const tripoint &pos, Creature *creature = nullptr, item *item = nullptr ) const; + // Don't call from outside this class. Add a wrapper like the ones below instead. + void trigger( const tripoint &pos, Creature *creature, item *item ) const; + public: + /*@{*/ + /** + * This applies the effects of the trap to the world and + * possibly to the triggering object (creature, item). + * + * The function assumes the + * caller has already checked whether the trap should be activated + * (e.g. the creature has had a chance to avoid the trap, but it failed). + */ + void trigger( const tripoint &pos, Creature &creature ) const; + void trigger( const tripoint &pos, item &item ) const; + /*@}*/ + /** * If the given item is throw onto the trap, does it trigger the trap? */ bool triggered_by_item( const item &itm ) const; + /** * Called when a trap at the given point in the map has been disarmed. * It should spawn trap items (if any) and remove the trap from the map via From d65f1381d4e3a9e8b3094082d6638d03373fd9c6 Mon Sep 17 00:00:00 2001 From: BevapDin Date: Fri, 26 Jun 2020 21:39:27 +0200 Subject: [PATCH 105/420] Check for existence of traps via trap::can_see. This checks not only the existence, but also whether the character can actually see it. Non-visible traps are considered non-existence the character will continue with their action as if there is no trap at all instead of having magical knowledge to better not interact with that seemingly harmless tile. --- src/character.cpp | 4 +++- src/character.h | 2 ++ src/construction.cpp | 2 ++ src/game.cpp | 6 ++++++ src/iexamine.cpp | 6 ++++-- src/iuse.cpp | 7 +++++-- src/vehicle_move.cpp | 2 +- 7 files changed, 23 insertions(+), 6 deletions(-) diff --git a/src/character.cpp b/src/character.cpp index ccbe5bc1d1c9f..a3e7d504fbf35 100644 --- a/src/character.cpp +++ b/src/character.cpp @@ -8165,7 +8165,9 @@ tripoint Character::adjacent_tile() const continue; } const trap &curtrap = here.tr_at( p ); - if( !curtrap.is_null() && !curtrap.is_benign() ) { + // If we don't known a trap here, the spot "appears" to be good, so consider it. + // Same if we know a benign trap (as it's not dangerous). + if( curtrap.can_see( p, *this ) && !curtrap.is_benign() ) { continue; } // Only consider tile if unoccupied, passable and has no traps diff --git a/src/character.h b/src/character.h index b8a17d3bd0e18..567628b3963fd 100644 --- a/src/character.h +++ b/src/character.h @@ -2206,6 +2206,8 @@ class Character : public Creature, public visitable float hearing_ability() const; using trap_map = std::map; + // Use @ref trap::can_see to check whether a character knows about a + // specific trap - it will consider visibile and known traps. bool knows_trap( const tripoint &pos ) const; void add_known_trap( const tripoint &pos, const trap &t ); /** Define color for displaying the body temperature */ diff --git a/src/construction.cpp b/src/construction.cpp index 1b5765c4367e2..9144e8cfba314 100644 --- a/src/construction.cpp +++ b/src/construction.cpp @@ -1059,6 +1059,8 @@ void complete_construction( player *p ) bool construct::check_empty( const tripoint &p ) { map &here = get_map(); + // @TODO should check for *visible* traps only. But calling code must + // first know how to handle constructing on top of an invisible trap! return ( here.has_flag( flag_FLAT, p ) && !here.has_furn( p ) && g->is_empty( p ) && here.tr_at( p ).is_null() && here.i_at( p ).empty() && !here.veh_at( p ) ); diff --git a/src/game.cpp b/src/game.cpp index 1fc718747647b..fe0e99b397c6d 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -10066,6 +10066,10 @@ bool game::grabbed_furn_move( const tripoint &dp ) !m.veh_at( fdest ) && ( !has_floor || m.tr_at( fdest ).is_null() ) ); + // @TODO: it should be possible to move over invisible traps. This should probably + // trigger the trap. + // The current check (no move if trap) allows a player to detect invisible traps by + // attempting to move stuff onto it. const furn_t furntype = m.furn( fpos ).obj(); const int src_items = m.i_at( fpos ).size(); @@ -10799,6 +10803,8 @@ void game::vertical_move( int movez, bool force, bool peeking ) for( const auto &np : npcs_to_bring ) { const auto found = std::find_if( candidates.begin(), candidates.end(), [this, np]( const tripoint & c ) { + // @TODO NPC should appear on top of invisible traps (and trigger them), + // instead of magically choosing tiles without dangerous traps. return !np->is_dangerous_fields( m.field_at( c ) ) && m.tr_at( c ).is_benign(); } ); if( found != candidates.end() ) { diff --git a/src/iexamine.cpp b/src/iexamine.cpp index cd3c2cd682b36..b8c1b175b2f7e 100644 --- a/src/iexamine.cpp +++ b/src/iexamine.cpp @@ -1076,11 +1076,12 @@ void iexamine::rubble( player &p, const tripoint &examp ) return; } map &here = get_map(); - if( ( here.veh_at( examp ) || !here.tr_at( examp ).is_null() || + if( ( here.veh_at( examp ) || here.can_see_trap_at( examp, p ) || g->critter_at( examp ) != nullptr ) && !query_yn( _( "Clear up that %s?" ), here.furnname( examp ) ) ) { return; } + // @TODO add check for triggering the trap while digging there. p.assign_activity( ACT_CLEAR_RUBBLE, moves, -1, 0 ); p.activity.placement = examp; } @@ -3564,13 +3565,14 @@ void iexamine::shrub_wildveggies( player &p, const tripoint &examp ) // Ask if there's something possibly more interesting than this shrub here if( ( !here.i_at( examp ).empty() || here.veh_at( examp ) || - !here.tr_at( examp ).is_null() || + here.can_see_trap_at( examp, p ) || g->critter_at( examp ) != nullptr ) && !query_yn( _( "Forage through %s?" ), here.tername( examp ) ) ) { none( p, examp ); return; } + // @TODO maybe add check for triggering a trap here. add_msg( _( "You forage through the %s." ), here.tername( examp ) ); ///\EFFECT_SURVIVAL speeds up foraging int move_cost = 100000 / ( 2 * p.get_skill_level( skill_survival ) + 5 ); diff --git a/src/iuse.cpp b/src/iuse.cpp index 8a0ada47c01ce..1a512b627827d 100644 --- a/src/iuse.cpp +++ b/src/iuse.cpp @@ -2815,9 +2815,10 @@ int iuse::dig( player *p, item *it, bool t, const tripoint & ) map &here = get_map(); const bool can_dig_here = here.has_flag( "DIGGABLE", dig_point ) && !here.has_furn( dig_point ) && - here.tr_at( dig_point ).is_null() && + !here.can_see_trap_at( dig_point, *p ) && ( here.ter( dig_point ) == t_grave_new || here.i_at( dig_point ).empty() ) && !here.veh_at( dig_point ); + // @TODO Trigger trap when attempting to dig on it. if( !can_dig_here ) { p->add_msg_if_player( @@ -2917,9 +2918,11 @@ int iuse::dig_channel( player *p, item *it, bool t, const tripoint & ) map &here = get_map(); const bool can_dig_here = here.has_flag( flag_DIGGABLE, dig_point ) && !here.has_furn( dig_point ) && - here.tr_at( dig_point ).is_null() && here.i_at( dig_point ).empty() && !here.veh_at( dig_point ) && + !here.can_see_trap_at( dig_point, *p ) && here.i_at( dig_point ).empty() && + !here.veh_at( dig_point ) && ( here.has_flag( flag_CURRENT, north ) || here.has_flag( flag_CURRENT, south ) || here.has_flag( flag_CURRENT, east ) || here.has_flag( flag_CURRENT, west ) ); + // @TODO Trigger trap when attempting to dig on it. if( !can_dig_here ) { p->add_msg_if_player( diff --git a/src/vehicle_move.cpp b/src/vehicle_move.cpp index 78bccb6028481..9dd8aa4a2d395 100644 --- a/src/vehicle_move.cpp +++ b/src/vehicle_move.cpp @@ -884,7 +884,7 @@ void vehicle::handle_trap( const tripoint &p, int part ) } const bool seen = g->u.sees( p ); - const bool known = g->u.knows_trap( p ); + const bool known = tr.can_see( p, g->u ); if( seen ) { if( known ) { //~ %1$s: name of the vehicle; %2$s: name of the related vehicle part; %3$s: trap name From d24e2c7ea9e9618856fe3a8210c6d56ac7197e9a Mon Sep 17 00:00:00 2001 From: BevapDin Date: Sat, 27 Jun 2020 13:04:23 +0200 Subject: [PATCH 106/420] Remove redundant variable. Checking via `trap::is_null` does the very same. --- src/vehicle_move.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/vehicle_move.cpp b/src/vehicle_move.cpp index 9dd8aa4a2d395..fcd96c7ac1058 100644 --- a/src/vehicle_move.cpp +++ b/src/vehicle_move.cpp @@ -871,9 +871,8 @@ void vehicle::handle_trap( const tripoint &p, int part ) return; } const trap &tr = g->m.tr_at( p ); - const trap_id t = tr.loadid; - if( t == tr_null ) { + if( tr.is_null() ) { // If the trap doesn't exist, we can't interact with it, so just return return; } From 3ca3d45661b26c0a81d125a54332f401455c44c0 Mon Sep 17 00:00:00 2001 From: BevapDin Date: Sat, 27 Jun 2020 13:28:05 +0200 Subject: [PATCH 107/420] Check via `trap::is_null` instead of accessing trap::loadid directly. --- src/activity_item_handling.cpp | 2 +- src/construction.cpp | 2 +- src/editmap.cpp | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/activity_item_handling.cpp b/src/activity_item_handling.cpp index e43ba65f52b61..185b78ee9ab7e 100644 --- a/src/activity_item_handling.cpp +++ b/src/activity_item_handling.cpp @@ -1727,7 +1727,7 @@ static bool construction_activity( player &p, const zone_data * /*zone*/, const pc.id = built_chosen.id; map &here = get_map(); // Set the trap that has the examine function - if( here.tr_at( src_loc ).loadid == tr_null ) { + if( here.tr_at( src_loc ).is_null() ) { here.trap_set( src_loc, tr_unfinished_construction ); } // Use up the components diff --git a/src/construction.cpp b/src/construction.cpp index 9144e8cfba314..5076223474410 100644 --- a/src/construction.cpp +++ b/src/construction.cpp @@ -941,7 +941,7 @@ void place_construction( const std::string &desc ) // Special handling for constructions that take place on existing traps. // Basically just don't add the unfinished construction trap. // TODO: handle this cleaner, instead of adding a special case to pit iexamine. - if( here.tr_at( pnt ).loadid == tr_null ) { + if( here.tr_at( pnt ).is_null() ) { here.trap_set( pnt, tr_unfinished_construction ); } // Use up the components diff --git a/src/editmap.cpp b/src/editmap.cpp index 5072372999a28..be926111ea49a 100644 --- a/src/editmap.cpp +++ b/src/editmap.cpp @@ -777,7 +777,7 @@ void editmap::update_view_with_help( const std::string &txt, const std::string & } const trap &cur_trap = here.tr_at( target ); - if( cur_trap.loadid != tr_null ) { + if( !cur_trap.is_null() ) { mvwprintz( w_info, point( 1, off ), cur_trap.color, _( "trap: %s (%d)" ), cur_trap.name(), cur_trap.loadid.to_i() ); off++; // 11 From 29480349ead09053f5920674787436f86531281f Mon Sep 17 00:00:00 2001 From: BevapDin Date: Sat, 27 Jun 2020 13:32:04 +0200 Subject: [PATCH 108/420] Avoid repeated lookup of trap object from id `map::tr_at` already gives us the trap object. We can use it directly instead of getting it back from the id via `obj()`. --- src/cata_tiles.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/cata_tiles.cpp b/src/cata_tiles.cpp index bde784aee3783..e7461cf12d62c 100644 --- a/src/cata_tiles.cpp +++ b/src/cata_tiles.cpp @@ -2433,8 +2433,8 @@ bool cata_tiles::draw_trap( const tripoint &p, const lit_level ll, int &height_3 map &here = get_map(); // first memorize the actual trap - const trap_id &tr = here.tr_at( p ).loadid; - if( tr && !invisible[0] && tr.obj().can_see( p, g->u ) ) { + const trap &tr = here.tr_at( p ); + if( !tr.is_null() && !invisible[0] && tr.can_see( p, g->u ) ) { const int neighborhood[4] = { static_cast( here.tr_at( p + point_south ).loadid ), static_cast( here.tr_at( p + point_east ).loadid ), @@ -2443,8 +2443,8 @@ bool cata_tiles::draw_trap( const tripoint &p, const lit_level ll, int &height_3 }; int subtile = 0; int rotation = 0; - get_tile_values( tr.to_i(), neighborhood, subtile, rotation ); - const std::string trname = tr.id().str(); + get_tile_values( tr.loadid.to_i(), neighborhood, subtile, rotation ); + const std::string trname = tr.loadid.id().str(); if( here.check_seen_cache( p ) ) { g->u.memorize_tile( here.getabs( p ), trname, subtile, rotation ); } @@ -2454,9 +2454,9 @@ bool cata_tiles::draw_trap( const tripoint &p, const lit_level ll, int &height_3 nv_goggles_activated, height_3d ); } } - if( overridden || ( !invisible[0] && neighborhood_overridden && tr.obj().can_see( p, g->u ) ) ) { + if( overridden || ( !invisible[0] && neighborhood_overridden && tr.can_see( p, g->u ) ) ) { // and then draw the override trap - const trap_id &tr2 = overridden ? override->second : tr; + const trap_id &tr2 = overridden ? override->second : tr.loadid; if( tr2 ) { // both the current and neighboring overrides may change the appearance // of the tile, so always re-calculate it. From 29d627eb2058ff68c11b7af6cdeb2a4d110090b7 Mon Sep 17 00:00:00 2001 From: BevapDin Date: Sat, 27 Jun 2020 14:00:08 +0200 Subject: [PATCH 109/420] Use string id instead of loadid. --- src/map_extras.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/map_extras.cpp b/src/map_extras.cpp index 185a6a6b75d03..e89a4044c7735 100644 --- a/src/map_extras.cpp +++ b/src/map_extras.cpp @@ -3029,7 +3029,7 @@ static bool mx_city_trap( map &/*m*/, const tripoint &abs_sub ) for( const tripoint &p : points_in_radius( trap_center, 1 ) ) { compmap.trap_set( p, tr_blade ); } - compmap.trap_set( trap_center, tr_engine ); + compmap.trap_set( trap_center, trap_str_id( "tr_engine" ) ); //... and a loudspeaker to attract zombies compmap.add_spawn( mon_turret_speaker, 1, trap_center ); } From 188f9080511b4a1964004f06f16d7130ea18332f Mon Sep 17 00:00:00 2001 From: BevapDin Date: Sat, 27 Jun 2020 13:58:48 +0200 Subject: [PATCH 110/420] Remove trap_id definitions of ids that are not used as such in the code. --- src/trap.cpp | 42 +----------------------------------------- src/trap.h | 20 -------------------- 2 files changed, 1 insertion(+), 61 deletions(-) diff --git a/src/trap.cpp b/src/trap.cpp index c5dcdd145605d..97ac346124b18 100644 --- a/src/trap.cpp +++ b/src/trap.cpp @@ -302,26 +302,9 @@ void trap::on_disarmed( map &m, const tripoint &p ) const // convenient int-lookup names for hard-coded functions trap_id tr_null, -tr_bubblewrap, -tr_glass, -tr_cot, -tr_funnel, -tr_metal_funnel, -tr_makeshift_funnel, -tr_leather_funnel, -tr_rollmat, -tr_fur_rollmat, -tr_beartrap, tr_beartrap_buried, -tr_nailboard, -tr_caltrops, -tr_caltrops_glass, -tr_tripwire, -tr_crossbow, tr_shotgun_2, -tr_shotgun_2_1, tr_shotgun_1, -tr_engine, tr_blade, tr_landmine, tr_landmine_buried, @@ -330,19 +313,16 @@ tr_goo, tr_dissector, tr_sinkhole, tr_pit, -tr_spike_pit, tr_lava, tr_portal, tr_ledge, -tr_boobytrap, tr_temple_flood, tr_temple_toggle, tr_glow, tr_hum, tr_shadow, tr_drain, -tr_snake, -tr_glass_pit; +tr_snake; void trap::check_consistency() { @@ -380,26 +360,9 @@ void trap::finalize() return trap_str_id( id ).id(); }; tr_null = trap_str_id::NULL_ID().id(); - tr_bubblewrap = trapfind( "tr_bubblewrap" ); - tr_glass = trapfind( "tr_glass" ); - tr_cot = trapfind( "tr_cot" ); - tr_funnel = trapfind( "tr_funnel" ); - tr_metal_funnel = trapfind( "tr_metal_funnel" ); - tr_makeshift_funnel = trapfind( "tr_makeshift_funnel" ); - tr_leather_funnel = trapfind( "tr_leather_funnel" ); - tr_rollmat = trapfind( "tr_rollmat" ); - tr_fur_rollmat = trapfind( "tr_fur_rollmat" ); - tr_beartrap = trapfind( "tr_beartrap" ); tr_beartrap_buried = trapfind( "tr_beartrap_buried" ); - tr_nailboard = trapfind( "tr_nailboard" ); - tr_caltrops = trapfind( "tr_caltrops" ); - tr_caltrops_glass = trapfind( "tr_caltrops_glass" ); - tr_tripwire = trapfind( "tr_tripwire" ); - tr_crossbow = trapfind( "tr_crossbow" ); tr_shotgun_2 = trapfind( "tr_shotgun_2" ); - tr_shotgun_2_1 = trapfind( "tr_shotgun_2_1" ); tr_shotgun_1 = trapfind( "tr_shotgun_1" ); - tr_engine = trapfind( "tr_engine" ); tr_blade = trapfind( "tr_blade" ); tr_landmine = trapfind( "tr_landmine" ); tr_landmine_buried = trapfind( "tr_landmine_buried" ); @@ -408,11 +371,9 @@ void trap::finalize() tr_dissector = trapfind( "tr_dissector" ); tr_sinkhole = trapfind( "tr_sinkhole" ); tr_pit = trapfind( "tr_pit" ); - tr_spike_pit = trapfind( "tr_spike_pit" ); tr_lava = trapfind( "tr_lava" ); tr_portal = trapfind( "tr_portal" ); tr_ledge = trapfind( "tr_ledge" ); - tr_boobytrap = trapfind( "tr_boobytrap" ); tr_temple_flood = trapfind( "tr_temple_flood" ); tr_temple_toggle = trapfind( "tr_temple_toggle" ); tr_glow = trapfind( "tr_glow" ); @@ -420,7 +381,6 @@ void trap::finalize() tr_shadow = trapfind( "tr_shadow" ); tr_drain = trapfind( "tr_drain" ); tr_snake = trapfind( "tr_snake" ); - tr_glass_pit = trapfind( "tr_glass_pit" ); } std::string trap::debug_describe() const diff --git a/src/trap.h b/src/trap.h index 1095f9ec3f3e2..36faa525b818f 100644 --- a/src/trap.h +++ b/src/trap.h @@ -296,26 +296,9 @@ const trap_function &trap_function_from_string( const std::string &function_name extern trap_id tr_null, -tr_bubblewrap, -tr_glass, -tr_cot, -tr_funnel, -tr_metal_funnel, -tr_makeshift_funnel, -tr_leather_funnel, -tr_rollmat, -tr_fur_rollmat, -tr_beartrap, tr_beartrap_buried, -tr_nailboard, -tr_caltrops, -tr_caltrops_glass, -tr_tripwire, -tr_crossbow, tr_shotgun_2, -tr_shotgun_2_1, tr_shotgun_1, -tr_engine, tr_blade, tr_landmine, tr_landmine_buried, @@ -324,12 +307,9 @@ tr_goo, tr_dissector, tr_sinkhole, tr_pit, -tr_spike_pit, -tr_glass_pit, tr_lava, tr_portal, tr_ledge, -tr_boobytrap, tr_temple_flood, tr_temple_toggle, tr_glow, From b02d0b84dce6405b994e6ce4f388ca995043e9cd Mon Sep 17 00:00:00 2001 From: BevapDin Date: Sat, 27 Jun 2020 14:13:47 +0200 Subject: [PATCH 111/420] Make checking for a specific type of trap simpler: Allows comparing a trap object with an id. --- src/construction.cpp | 4 ++-- src/game.cpp | 16 +++++++--------- src/iexamine.cpp | 2 +- src/map.cpp | 2 +- src/trap.h | 7 +++++++ src/trapfunc.cpp | 12 ++++++------ 6 files changed, 24 insertions(+), 19 deletions(-) diff --git a/src/construction.cpp b/src/construction.cpp index 5076223474410..4f435bbc91e6c 100644 --- a/src/construction.cpp +++ b/src/construction.cpp @@ -969,7 +969,7 @@ void complete_construction( player *p ) partial_con *pc = here.partial_con_at( terp ); if( !pc ) { debugmsg( "No partial construction found at activity placement in complete_construction()" ); - if( here.tr_at( terp ).loadid == tr_unfinished_construction ) { + if( here.tr_at( terp ) == tr_unfinished_construction ) { here.remove_trap( terp ); } if( p->is_npc() ) { @@ -1003,7 +1003,7 @@ void complete_construction( player *p ) award_xp( *elem ); } } - if( here.tr_at( terp ).loadid == tr_unfinished_construction ) { + if( here.tr_at( terp ) == tr_unfinished_construction ) { here.remove_trap( terp ); } here.partial_con_remove( terp ); diff --git a/src/game.cpp b/src/game.cpp index fe0e99b397c6d..6786519fac0ab 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -1810,7 +1810,7 @@ int get_heat_radiation( const tripoint &location, bool direct ) int ffire = maptile_field_intensity( mt, fd_fire ); if( ffire > 0 ) { heat_intensity = ffire; - } else if( here.tr_at( dest ).loadid == tr_lava ) { + } else if( here.tr_at( dest ) == tr_lava ) { heat_intensity = 3; } if( heat_intensity == 0 ) { @@ -1843,8 +1843,7 @@ int get_convection_temperature( const tripoint &location ) int temp_mod = 0; map &here = get_map(); // Directly on lava tiles - int lava_mod = here.tr_at( location ).loadid == tr_lava ? - fd_fire.obj().get_convection_temperature_mod() : 0; + int lava_mod = here.tr_at( location ) == tr_lava ? fd_fire->get_convection_temperature_mod() : 0; // Modifier from fields for( auto fd : here.field_at( location ) ) { // Nullify lava modifier when there is open fire @@ -6292,7 +6291,7 @@ void game::print_trap_info( const tripoint &lp, const catacurses::window &w_look if( tr.can_see( lp, u ) ) { partial_con *pc = m.partial_con_at( lp ); std::string tr_name; - if( pc && tr.loadid == tr_unfinished_construction ) { + if( pc && tr == tr_unfinished_construction ) { const construction &built = pc->id.obj(); tr_name = string_format( _( "Unfinished task: %s, %d%% complete" ), built.description, pc->counter / 100000 ); @@ -9216,8 +9215,7 @@ bool game::prompt_dangerous_tile( const tripoint &dest_loc ) const !query_yn( _( "Really step into %s?" ), enumerate_as_string( harmful_stuff ) ) ) { return false; } - if( !harmful_stuff.empty() && u.is_mounted() && - m.tr_at( dest_loc ).loadid == tr_ledge ) { + if( !harmful_stuff.empty() && u.is_mounted() && m.tr_at( dest_loc ) == tr_ledge ) { add_msg( m_warning, _( "Your %s refuses to move over that ledge!" ), u.mounted_creature->get_name() ); return false; @@ -9242,7 +9240,7 @@ std::vector game::get_dangerous_tile( const tripoint &dest_loc ) co true ) ); // HACK: Hack for now, later ledge should stop being a trap // Note: in non-z-level mode, ledges obey different rules and so should be handled as regular traps - if( tr.loadid == tr_ledge && m.has_zlevels() ) { + if( tr == tr_ledge && m.has_zlevels() ) { if( !boardable ) { harmful_stuff.emplace_back( tr.name() ); } @@ -10392,7 +10390,7 @@ void game::fling_creature( Creature *c, const int &dir, float flvel, bool contro // Fall down to the ground - always on the last reached tile if( !m.has_flag( "SWIMMABLE", c->pos() ) ) { - const trap_id trap_under_creature = m.tr_at( c->pos() ).loadid; + const trap &trap_under_creature = m.tr_at( c->pos() ); // Didn't smash into a wall or a floor so only take the fall damage if( thru && trap_under_creature == tr_ledge ) { m.creature_on_trap( *c, false ); @@ -11829,7 +11827,7 @@ void game::process_artifact( item &it, player &p ) case ARTC_PORTAL: for( const tripoint &dest : m.points_in_radius( p.pos(), 1 ) ) { m.remove_field( dest, fd_fatigue ); - if( m.tr_at( dest ).loadid == tr_portal ) { + if( m.tr_at( dest ) == tr_portal ) { add_msg( m_good, _( "The portal collapses!" ) ); m.remove_trap( dest ); it.charges++; diff --git a/src/iexamine.cpp b/src/iexamine.cpp index b8c1b175b2f7e..098fc99871bb8 100644 --- a/src/iexamine.cpp +++ b/src/iexamine.cpp @@ -3709,7 +3709,7 @@ void trap::examine( player &p, const tripoint &examp ) const add_msg( m_warning, _( "You cannot do that while mounted." ) ); return; } - if( loadid == tr_unfinished_construction || here.partial_con_at( examp ) ) { + if( *this == tr_unfinished_construction || here.partial_con_at( examp ) ) { partial_con *pc = here.partial_con_at( examp ); if( pc ) { if( g->u.fine_detail_vision_mod() > 4 && !g->u.has_trait( trait_DEBUG_HS ) ) { diff --git a/src/map.cpp b/src/map.cpp index 03227e371f557..cac0a3810542a 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -1818,7 +1818,7 @@ bool map::valid_move( const tripoint &from, const tripoint &to, // actually make a valid ledge drop location with zlevels on, this forces // at least one zlevel drop and if down_ter is impassible it's probably // inside a wall, we could workaround that further but it's unnecessary. - const bool up_is_ledge = tr_at( up_p ).loadid == tr_ledge; + const bool up_is_ledge = tr_at( up_p ) == tr_ledge; if( up_ter.movecost == 0 ) { // Unpassable tile diff --git a/src/trap.h b/src/trap.h index 36faa525b818f..68a5d375e71d9 100644 --- a/src/trap.h +++ b/src/trap.h @@ -138,6 +138,13 @@ struct trap { return always_invisible; } + bool operator==( const trap_id &id ) const { + return loadid == id; + } + bool operator!=( const trap_id &id ) const { + return loadid != id; + } + /** * Called when the player examines a tile. This is supposed to handled * all kind of interaction of the player with the trap, including removal. diff --git a/src/trapfunc.cpp b/src/trapfunc.cpp index 7e8312606b774..27f0acac82f16 100644 --- a/src/trapfunc.cpp +++ b/src/trapfunc.cpp @@ -437,7 +437,7 @@ bool trapfunc::shotgun( const tripoint &p, Creature *c, item * ) { map &here = get_map(); sounds::sound( p, 60, sounds::sound_t::combat, _( "Kerblam!" ), false, "fire_gun", - here.tr_at( p ).loadid == tr_shotgun_1 ? "shotgun_s" : "shotgun_d" ); + here.tr_at( p ) == tr_shotgun_1 ? "shotgun_s" : "shotgun_d" ); int shots = 1; if( c != nullptr ) { if( c->has_effect( effect_ridden ) ) { @@ -450,7 +450,7 @@ bool trapfunc::shotgun( const tripoint &p, Creature *c, item * ) if( n != nullptr ) { ///\EFFECT_STR_MAX increases chance of two shots from shotgun trap shots = ( one_in( 8 ) || one_in( 20 - n->str_max ) ? 2 : 1 ); - if( here.tr_at( p ).loadid != tr_shotgun_2 ) { + if( here.tr_at( p ) != tr_shotgun_2 ) { shots = 1; } ///\EFFECT_DODGE reduces chance of being hit by shotgun trap @@ -512,7 +512,7 @@ bool trapfunc::shotgun( const tripoint &p, Creature *c, item * ) break; } shots = ( one_in( 8 ) || one_in( chance ) ? 2 : 1 ); - if( here.tr_at( p ).loadid != tr_shotgun_2 ) { + if( here.tr_at( p ) != tr_shotgun_2 ) { shots = 1; } if( seen ) { @@ -524,7 +524,7 @@ bool trapfunc::shotgun( const tripoint &p, Creature *c, item * ) c->check_dead_state(); } - here.spawn_item( p, here.tr_at( p ).loadid == tr_shotgun_1 ? "shotgun_s" : "shotgun_d" ); + here.spawn_item( p, here.tr_at( p ) == tr_shotgun_1 ? "shotgun_s" : "shotgun_d" ); here.spawn_item( p, "string_36" ); here.remove_trap( p ); return true; @@ -1062,7 +1062,7 @@ static bool sinkhole_safety_roll( player *p, const itype_id &itemname, const int std::vector safe; for( const tripoint &tmp : here.points_in_radius( p->pos(), 1 ) ) { - if( here.passable( tmp ) && here.tr_at( tmp ).loadid != tr_pit ) { + if( here.passable( tmp ) && here.tr_at( tmp ) != tr_pit ) { safe.push_back( tmp ); } } @@ -1247,7 +1247,7 @@ bool trapfunc::temple_flood( const tripoint &p, Creature *c, item * ) map &here = get_map(); for( i = 0; i < MAPSIZE_X; i++ ) { for( j = 0; j < MAPSIZE_Y; j++ ) { - if( here.tr_at( tmp ).loadid == tr_temple_flood ) { + if( here.tr_at( tmp ) == tr_temple_flood ) { here.remove_trap( tmp ); } } From 15c39c9425f961176c5671fcf2fb5ef5101b727f Mon Sep 17 00:00:00 2001 From: BevapDin Date: Sat, 27 Jun 2020 14:35:48 +0200 Subject: [PATCH 112/420] Trigger traps doing various activities (e.g. digging) Note: triggering is done *after* the activity finishes. If the trap causes damage, the game would show a query to stop the current activity (which has in fact already been finished). --- src/activity_actor.cpp | 4 ++++ src/activity_handlers.cpp | 7 ++++++- src/iexamine.cpp | 2 -- src/iuse.cpp | 2 -- src/map.cpp | 21 ++++++++++++++++----- src/map.h | 8 ++++++++ 6 files changed, 34 insertions(+), 10 deletions(-) diff --git a/src/activity_actor.cpp b/src/activity_actor.cpp index b48bfff63e87e..77ec49578932a 100644 --- a/src/activity_actor.cpp +++ b/src/activity_actor.cpp @@ -405,6 +405,8 @@ void dig_activity_actor::finish( player_activity &act, Character &who ) } act.set_to_null(); + + here.maybe_trigger_trap( location, who, true ); } void dig_activity_actor::serialize( JsonOut &jsout ) const @@ -471,6 +473,8 @@ void dig_channel_activity_actor::finish( player_activity &act, Character &who ) here.ter( location ).obj().name() ); act.set_to_null(); + + here.maybe_trigger_trap( location, who, true ); } void dig_channel_activity_actor::serialize( JsonOut &jsout ) const diff --git a/src/activity_handlers.cpp b/src/activity_handlers.cpp index 18faacbc89100..3cf7f80c77d86 100644 --- a/src/activity_handlers.cpp +++ b/src/activity_handlers.cpp @@ -1771,7 +1771,8 @@ void activity_handlers::forage_finish( player_activity *act, player *p ) debugmsg( "Invalid season" ); } - here.ter_set( here.getlocal( act->placement ), next_ter ); + const tripoint bush_pos = here.getlocal( act->placement ); + here.ter_set( bush_pos, next_ter ); // Survival gives a bigger boost, and Perception is leveled a bit. // Both survival and perception affect time to forage @@ -1809,6 +1810,8 @@ void activity_handlers::forage_finish( player_activity *act, player *p ) iexamine::practice_survival_while_foraging( p ); act->set_to_null(); + + here.maybe_trigger_trap( bush_pos, *p, true ); } void activity_handlers::generic_game_do_turn( player_activity * /*act*/, player *p ) @@ -2900,6 +2903,8 @@ void activity_handlers::clear_rubble_finish( player_activity *act, player *p ) here.furn_set( pos, f_null ); act->set_to_null(); + + here.maybe_trigger_trap( pos, *p, true ); } void activity_handlers::meditate_finish( player_activity *act, player *p ) diff --git a/src/iexamine.cpp b/src/iexamine.cpp index 098fc99871bb8..98c56b8231ccb 100644 --- a/src/iexamine.cpp +++ b/src/iexamine.cpp @@ -1081,7 +1081,6 @@ void iexamine::rubble( player &p, const tripoint &examp ) !query_yn( _( "Clear up that %s?" ), here.furnname( examp ) ) ) { return; } - // @TODO add check for triggering the trap while digging there. p.assign_activity( ACT_CLEAR_RUBBLE, moves, -1, 0 ); p.activity.placement = examp; } @@ -3572,7 +3571,6 @@ void iexamine::shrub_wildveggies( player &p, const tripoint &examp ) return; } - // @TODO maybe add check for triggering a trap here. add_msg( _( "You forage through the %s." ), here.tername( examp ) ); ///\EFFECT_SURVIVAL speeds up foraging int move_cost = 100000 / ( 2 * p.get_skill_level( skill_survival ) + 5 ); diff --git a/src/iuse.cpp b/src/iuse.cpp index 1a512b627827d..d52824d7370be 100644 --- a/src/iuse.cpp +++ b/src/iuse.cpp @@ -2818,7 +2818,6 @@ int iuse::dig( player *p, item *it, bool t, const tripoint & ) !here.can_see_trap_at( dig_point, *p ) && ( here.ter( dig_point ) == t_grave_new || here.i_at( dig_point ).empty() ) && !here.veh_at( dig_point ); - // @TODO Trigger trap when attempting to dig on it. if( !can_dig_here ) { p->add_msg_if_player( @@ -2922,7 +2921,6 @@ int iuse::dig_channel( player *p, item *it, bool t, const tripoint & ) !here.veh_at( dig_point ) && ( here.has_flag( flag_CURRENT, north ) || here.has_flag( flag_CURRENT, south ) || here.has_flag( flag_CURRENT, east ) || here.has_flag( flag_CURRENT, west ) ); - // @TODO Trigger trap when attempting to dig on it. if( !can_dig_here ) { p->add_msg_if_player( diff --git a/src/map.cpp b/src/map.cpp index cac0a3810542a..89e39b50003e0 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -8003,19 +8003,30 @@ field &map::get_field( const tripoint &p ) void map::creature_on_trap( Creature &c, const bool may_avoid ) { - const auto &tr = tr_at( c.pos() ); - if( tr.is_null() ) { - return; - } // boarded in a vehicle means the player is above the trap, like a flying monster and can // never trigger the trap. const player *const p = dynamic_cast( &c ); if( p != nullptr && p->in_vehicle ) { return; } - if( may_avoid && c.avoid_trap( c.pos(), tr ) ) { + maybe_trigger_trap( c.pos(), c, may_avoid ); +} + +void map::maybe_trigger_trap( const tripoint &pos, Creature &c, const bool may_avoid ) +{ + const auto &tr = tr_at( pos ); + if( tr.is_null() ) { + return; + } + + if( may_avoid && c.avoid_trap( pos, tr ) ) { return; } + + if( !tr.is_always_invisible() ) { + c.add_msg_player_or_npc( m_bad, _( "You trigger a %s!" ), _( " triggers a %s!" ), + tr.name() ); + } tr.trigger( c.pos(), c ); } diff --git a/src/map.h b/src/map.h index d1dd37d5656f9..e6841f0b8d67c 100644 --- a/src/map.h +++ b/src/map.h @@ -1163,6 +1163,14 @@ class map const std::vector &get_furn_field_locations() const; const std::vector &trap_locations( const trap_id &type ) const; + /** + * Handles activating a trap. It includes checks for avoiding the trap + * (which also makes it visible). + * This functions assumes the character is either on top of the trap, + * or adjacent to it. + */ + void maybe_trigger_trap( const tripoint &pos, Creature &c, bool may_avoid ); + // Spawns byproducts from items destroyed in fire. void create_burnproducts( const tripoint &p, const item &fuel, const units::mass &burned_mass ); // See fields.cpp From d0ab1b547eba9e21fe49c0318ac12bcb03b562ef Mon Sep 17 00:00:00 2001 From: BevapDin Date: Sat, 27 Jun 2020 14:49:07 +0200 Subject: [PATCH 113/420] Simplify some code: the check for it being a specific trap type ("tr_unfinished_construction") is unnecessary. The check for the result of `partial_con_at` controls the block. If that function yields null, it is completely skipped. If it's not null, the check for trap id is ignored. --- src/iexamine.cpp | 41 +++++++++++++++++------------------------ 1 file changed, 17 insertions(+), 24 deletions(-) diff --git a/src/iexamine.cpp b/src/iexamine.cpp index 98c56b8231ccb..885513a4220fd 100644 --- a/src/iexamine.cpp +++ b/src/iexamine.cpp @@ -3707,34 +3707,27 @@ void trap::examine( player &p, const tripoint &examp ) const add_msg( m_warning, _( "You cannot do that while mounted." ) ); return; } - if( *this == tr_unfinished_construction || here.partial_con_at( examp ) ) { - partial_con *pc = here.partial_con_at( examp ); - if( pc ) { - if( g->u.fine_detail_vision_mod() > 4 && !g->u.has_trait( trait_DEBUG_HS ) ) { - add_msg( m_info, _( "It is too dark to construct right now." ) ); - return; - } - const construction &built = pc->id.obj(); - if( !query_yn( _( "Unfinished task: %s, %d%% complete here, continue construction?" ), - built.description, pc->counter / 100000 ) ) { - if( query_yn( _( "Cancel construction?" ) ) ) { - tr.on_disarmed( here, examp ); - for( const item &it : pc->components ) { - here.add_item_or_charges( g->u.pos(), it ); - } - here.partial_con_remove( examp ); - return; - } else { - return; + + if( partial_con *const pc = here.partial_con_at( examp ) ) { + if( g->u.fine_detail_vision_mod() > 4 && !g->u.has_trait( trait_DEBUG_HS ) ) { + add_msg( m_info, _( "It is too dark to construct right now." ) ); + return; + } + const construction &built = pc->id.obj(); + if( !query_yn( _( "Unfinished task: %s, %d%% complete here, continue construction?" ), + built.description, pc->counter / 100000 ) ) { + if( query_yn( _( "Cancel construction?" ) ) ) { + on_disarmed( here, examp ); + for( const item &it : pc->components ) { + here.add_item_or_charges( g->u.pos(), it ); } - } else { - g->u.assign_activity( ACT_BUILD ); - g->u.activity.placement = here.getabs( examp ); - return; + here.partial_con_remove( examp ); } } else { - return; + g->u.assign_activity( ACT_BUILD ); + g->u.activity.placement = here.getabs( examp ); } + return; } if( can_not_be_disarmed() ) { add_msg( m_info, _( "That %s looks too dangerous to mess with. Best leave it alone." ), name() ); From ffda46bca8a1906c0e9c3707f299aed32d658c41 Mon Sep 17 00:00:00 2001 From: BevapDin Date: Sat, 27 Jun 2020 14:53:56 +0200 Subject: [PATCH 114/420] Remove useless parameter. The function uses `g->u` all the time anyway. --- src/game.cpp | 2 +- src/iexamine.cpp | 7 ++----- src/trap.h | 2 +- 3 files changed, 4 insertions(+), 7 deletions(-) diff --git a/src/game.cpp b/src/game.cpp index 6786519fac0ab..a8a80673a0a6c 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -5908,7 +5908,7 @@ void game::examine( const tripoint &examp ) } // trap::iexamine will handle the invisible traps. - m.tr_at( examp ).examine( u, examp ); + m.tr_at( examp ).examine( examp ); // In case of teleport trap or somesuch if( player_pos != u.pos() ) { diff --git a/src/iexamine.cpp b/src/iexamine.cpp index 885513a4220fd..402f18c380744 100644 --- a/src/iexamine.cpp +++ b/src/iexamine.cpp @@ -3691,15 +3691,12 @@ void iexamine::recycle_compactor( player &, const tripoint &examp ) } } -void trap::examine( player &p, const tripoint &examp ) const +void trap::examine( const tripoint &examp ) const { map &here = get_map(); - if( !p.is_player() ) { - return; - } // If the player can't see the trap, they can't interact with it. - if( !can_see( examp, p ) ) { + if( !can_see( examp, g->u ) ) { return; } diff --git a/src/trap.h b/src/trap.h index 68a5d375e71d9..150a788dfbc4e 100644 --- a/src/trap.h +++ b/src/trap.h @@ -152,7 +152,7 @@ struct trap { * called on the null trap. */ // Implemented for historical reasons in iexamine.cpp - void examine( player &p, const tripoint &examp ) const; + void examine( const tripoint &examp ) const; std::string map_regen_target() const; From 177a3680f4842f2affb9221e2bb63be049aadd7f Mon Sep 17 00:00:00 2001 From: BevapDin Date: Sat, 27 Jun 2020 15:18:43 +0200 Subject: [PATCH 115/420] Make traps visible when they are successfully avoided. --- src/map.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/map.cpp b/src/map.cpp index 89e39b50003e0..dcb1cdf11b9eb 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -8020,6 +8020,11 @@ void map::maybe_trigger_trap( const tripoint &pos, Creature &c, const bool may_a } if( may_avoid && c.avoid_trap( pos, tr ) ) { + player *const pl = c.as_player(); + if( !tr.is_always_invisible() && pl && !pl->knows_trap( pos ) ) { + pl->add_msg_if_player( _( "You've spotted a %1$ss!" ), tr.name() ); + pl->add_known_trap( pos, tr ); + } return; } From b3b2c0599da1d1fd02f565ee49f223ddd5c553cb Mon Sep 17 00:00:00 2001 From: BevapDin Date: Fri, 3 Jul 2020 18:18:59 +0200 Subject: [PATCH 116/420] Add some traps documentation. --- src/trap.h | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/trap.h b/src/trap.h index 150a788dfbc4e..5a5b199de84fe 100644 --- a/src/trap.h +++ b/src/trap.h @@ -83,6 +83,26 @@ struct vehicle_handle_trap_data { using trap_function = std::function; +/** + * Some traps aren't actually traps in the usual sense of the word. We use traps to implement + * funnels (the main map keeps a list of traps and we iterate over that list during rain + * in order to fill the containers with water). + * Use @ref is_benign to check for that kind of "non-dangerous" traps. Traps that are not benign + * are considered dangerous. + * + * Some traps are always invisible. They are never revealed as such to the player. + * They can still be triggered. Use @ref is_always_invisible to check for that. + * + * Traps names can be empty. This usually applies to always invisible traps. + * + * Some traps are always revealed to the player (e.g. funnels). Other traps can be spotted when + * the player is close (also needs perception stat). + * + * Use @ref map::can_see_trap_at or @ref trap::can_see to check whether a creature knows about a + * given trap. Monsters / NPCs should base their behavior on that information and not on a simple + * check for any trap being there (e.g. don't use `map::tr_at(...).is_null()` - that would reveal + * the existence of the trap). + */ struct trap { trap_str_id id; trap_id loadid; From 8aaf68f79737c2629364c1165eba6d760b65e815 Mon Sep 17 00:00:00 2001 From: Lucas Hoage <67556457+LucasHoage@users.noreply.github.com> Date: Sat, 4 Jul 2020 11:29:04 -0400 Subject: [PATCH 117/420] Fixed missing fliers by adding new content (#41832) --- data/json/snippets/fliers.json | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/data/json/snippets/fliers.json b/data/json/snippets/fliers.json index 54ca6acac2266..15f7dd85e55f2 100644 --- a/data/json/snippets/fliers.json +++ b/data/json/snippets/fliers.json @@ -2,7 +2,6 @@ { "type": "snippet", "category": "flier", - "//": "Missing flier_ 27, 29, 30", "text": [ { "id": "flier_1", @@ -108,10 +107,22 @@ "id": "flier_26", "text": "This is an advertisement for Rivtech brand ammunition. It shows a picture of an armored steel plate with a gaping hole blasted through the middle. Sitting beside the plate is a block of brightly colored caseless ammunition. The caption reads: \"Rivtech 8x40mm caseless. Nothing else comes close.\"" }, + { + "id": "flier_27", + "text": "This is an advertisement for SUDS Laundromat. It shows words surrounded by bubbles that appear to be floating upward. It reads: \"Tergitol Tuesdays! 50% off on all washers and driers!\"" + }, { "id": "flier_28", "text": "This is a propaganda poster showing the Northrop Dispatch's military variant. It depicts the iconic dark green, arachnoid dispatch, standing before a fence and facing away from the camera as blurring machines rush forward from its back towards black silhouettes menacing on the horizon. It reads: \"WE ARE HERE TO PROTECT YOU.\"" }, + { + "id": "flier_29", + "text": "This is an advertisement for Iron Gym. It shows pictures of people performing various exercises such as running, yoga and weight lifting. It reads: \"I lift things up and put them down!\"" + }, + { + "id": "flier_30", + "text": "This is an advertisement for Space Time Inc. It has pictures of astronauts floating around a spaceship with the Moon in the background. It reads: \"Own your own piece of the Moon! For only $29.99 a month, you can have prime real estate amongst the stars!\"" + }, { "id": "flier_31", "text": "This is a public notice from the Centers for Disease Control. Its message, repeated in several languages, reads: \"BOIL WATER ADVISORY. An unidentified agent has contaminated local groundwater. It is highly infectious and can cause erratic and violent behavior. Boil all water, and isolate any loved ones showing concerning symptoms. Visit www.cdc.gov/cdda-advisory for more information.\"" From 33596291f07cfa8d89e8d1a7153aad75566c8418 Mon Sep 17 00:00:00 2001 From: Eric Pierce Date: Sat, 4 Jul 2020 17:48:34 -0600 Subject: [PATCH 118/420] Add cat and kitten varieties to crazy cat persons This commit changes the Crazy Cat Dude/Lady's 30 pet shorthair cats into a mix of the various varieties, including a few kittens. --- data/json/professions.json | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/data/json/professions.json b/data/json/professions.json index b895a804c7a01..355c55f90a1de 100644 --- a/data/json/professions.json +++ b/data/json/professions.json @@ -1162,7 +1162,22 @@ "description": "Everyone is dead? Oh well, it doesn't matter; it's not like you got along with people much anyway. Your beloved cats are all the friends you need!", "points": 5, "skills": [ { "level": 1, "name": "survival" }, { "level": 1, "name": "cooking" }, { "level": 1, "name": "tailor" } ], - "pets": [ { "name": "mon_cat", "amount": 30 } ], + "pets": [ + { "name": "mon_cat", "amount": 5 }, + { "name": "mon_cat_tabby", "amount": 4 }, + { "name": "mon_cat_longhair", "amount": 3 }, + { "name": "mon_cat_persian", "amount": 3 }, + { "name": "mon_cat_siamese", "amount": 3 }, + { "name": "mon_cat_calico", "amount": 2 }, + { "name": "mon_cat_maine_coon", "amount": 2 }, + { "name": "mon_cat_devon_rex", "amount": 2 }, + { "name": "mon_cat_bengal", "amount": 1 }, + { "name": "mon_cat_sphynx", "amount": 1 }, + { "name": "mon_cat_chonker_kitten", "amount": 1 }, + { "name": "mon_cat_longhair_kitten", "amount": 1 }, + { "name": "mon_cat_calico_kitten", "amount": 1 }, + { "name": "mon_cat_tabby_kitten", "amount": 1 } + ], "items": { "both": { "items": [ "jeans", "longshirt", "socks", "coat_winter", "boots_winter", "knit_scarf", "pockknife", "water_clean", "wristwatch" ], From 30c28690ec3f2a0699ddfc7f3d236d6079bdb637 Mon Sep 17 00:00:00 2001 From: LucasHoage Date: Sun, 5 Jul 2020 00:32:17 -0400 Subject: [PATCH 119/420] Fixed blood draw kits. --- data/json/items/comestibles/carnivore.json | 2 +- data/json/items/tool/med.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/data/json/items/comestibles/carnivore.json b/data/json/items/comestibles/carnivore.json index 7373949410ee3..6ae7a556bfbe9 100644 --- a/data/json/items/comestibles/carnivore.json +++ b/data/json/items/comestibles/carnivore.json @@ -570,7 +570,7 @@ "type": "COMESTIBLE", "id": "blood", "name": { "str_sp": "blood" }, - "weight": "265 g", + "weight": "262 g", "color": "red", "container": "bag_iv", "comestible_type": "DRINK", diff --git a/data/json/items/tool/med.json b/data/json/items/tool/med.json index e0415752eb74d..cc02fa2cd5f25 100644 --- a/data/json/items/tool/med.json +++ b/data/json/items/tool/med.json @@ -208,7 +208,7 @@ "watertight": true, "rigid": true, "max_contains_volume": "250 ml", - "max_contains_weight": "50 g" + "max_contains_weight": "262 g" } ] }, From ec87c83e2869cb134ca0655d3f042fea85a190eb Mon Sep 17 00:00:00 2001 From: I-am-Erk <45136638+I-am-Erk@users.noreply.github.com> Date: Sat, 4 Jul 2020 23:47:27 -0700 Subject: [PATCH 120/420] Ultica Update: 4 July 2020 (#41838) --- gfx/UltimateCataclysmDemo/giant.png | Bin 96100 -> 96468 bytes gfx/UltimateCataclysmDemo/large.png | Bin 102803 -> 116107 bytes gfx/UltimateCataclysmDemo/normal.png | Bin 762605 -> 809288 bytes gfx/UltimateCataclysmDemo/tall.png | Bin 202973 -> 238820 bytes gfx/UltimateCataclysmDemo/tile_config.json | 2 +- gfx/UltimateCataclysmDemo/tileset.txt | 2 +- 6 files changed, 2 insertions(+), 2 deletions(-) diff --git a/gfx/UltimateCataclysmDemo/giant.png b/gfx/UltimateCataclysmDemo/giant.png index 45f379069c22490eed954956c31fa7128f03e8e3..2c8dc0a9b2a3a80e8a939cf055f655a5f993341e 100644 GIT binary patch delta 62056 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 diff --git a/gfx/UltimateCataclysmDemo/large.png b/gfx/UltimateCataclysmDemo/large.png index f1f3c6402fb198e2f8f7085bd998027373fa1398..ee9ab1ca7fff2f232ae0ce460bb8c99de2799199 100644 GIT binary patch delta 106812 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

?xo#kXm0gJ6-N(e*K!N#l2ZH1EceYk@jq|$ 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%0D5*>;fbDfkca`DYRnWd8gE6*AP6 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 NbJFQV!?BBT{{$O|Bwzpl delta 93635 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