diff --git a/.appveyor.yml b/.appveyor.yml index 2b72b508a288f..ee4818c7917e0 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -1,5 +1,5 @@ version: '{branch}.{build}' -image: Previous Visual Studio 2019 +image: Visual Studio 2019 configuration: Release platform: x64 shallow_clone: true diff --git a/.clang-tidy b/.clang-tidy index 77f4f5274f2a0..6bebf2ef578c9 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -35,8 +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,\ -performance-implicit-conversion-in-loop,\ diff --git a/.gitignore b/.gitignore index 5776d2ca2ed77..e5a0613ca1770 100644 --- a/.gitignore +++ b/.gitignore @@ -69,6 +69,9 @@ Cataclysm.exe.lastcodeanalysissucceeded #Visual Studio 2017 /msvc-full-features/PredictedInputCache_Debug_x64.dat +#vcpkg +/msvc-full-features/vcpkg_installed + # PVS Studio /msvc-full-features/*PVS-Studio* diff --git a/build-scripts/build.sh b/build-scripts/build.sh index c7cdd1a135996..eebe7f135dedc 100755 --- a/build-scripts/build.sh +++ b/build-scripts/build.sh @@ -124,7 +124,6 @@ then else remaining_cpp_files="$all_cpp_files" fi - set -x function analyze_files_in_random_order { @@ -142,6 +141,7 @@ then echo "Analyzing remaining files" analyze_files_in_random_order "$remaining_cpp_files" + set -x else # Regular build make -j$num_jobs diff --git a/build-scripts/clang-tidy-wrapper.sh b/build-scripts/clang-tidy-wrapper.sh index b1ea138356676..4ff613644422e 100755 --- a/build-scripts/clang-tidy-wrapper.sh +++ b/build-scripts/clang-tidy-wrapper.sh @@ -5,7 +5,6 @@ set -o pipefail plugin=build/tools/clang-tidy-plugin/libCataAnalyzerPlugin.so -set -x if [ -f "$plugin" ] then LD_PRELOAD=$plugin "$CATA_CLANG_TIDY" "$@" diff --git a/data/json/ascii_arts.json b/data/json/ascii_arts.json index 67d8c18292ba8..942de32c24045 100644 --- a/data/json/ascii_arts.json +++ b/data/json/ascii_arts.json @@ -219,5 +219,502 @@ " ║ HEAVY ║", " ╚═══════════════╝" ] + }, + { + "type": "ascii_art", + "id": "mp3", + "picture": [ + "", + "", + "            /\\         /\\", + "           /  \\       /  \\", + "          /    \\     /    \\", + "         /      \\   /      \\", + "         ║       \\ /       ║", + "      ___║        |        ║___", + "     |   /        |        \\   |", + "     |__/         |         \\__|", + "                  |", + "                  |", + "                  |", + "     ╔═══════════════════════════╗", + "     ║  ╔═════════════════════╗  ║", + "     ║  ║                     ║  ║", + "     ║  ║                     ║  ║", + "     ║  ║                     ║  ║", + "     ║  ║                     ║  ║", + "     ║  ║                     ║  ║", + "     ║  ║                     ║  ║", + "     ║  ║                     ║  ║", + "     ║  ║                     ║  ║", + "     ║  ╚═════════════════════╝  ║", + "     ║                           ║", + "     ║         .........         ║", + "     ║      ...         ...      ║", + "     ║     .     MENU      .     ║", + "     ║    .      .....      .    ║", + "     ║   .      .     .      .   ║", + "     ║   .  ⏮  .   ⏯   .  ⏭  .   ║", + "     ║   .      .     .      .   ║", + "     ║    .      .....      .    ║", + "     ║     .               .     ║", + "     ║      ...   VOL   ...      ║", + "     ║         .........         ║", + "     ║                           ║", + "     ╚═╦═════════════════════════╝", + "       ║          |", + "       \\          |", + "        \\        /", + "         \\      /", + "          \\    /", + "           \\  /", + "            \\/" + ] + }, + { + "type": "ascii_art", + "id": "lajatang", + "picture": [ + "", + "  .                  .", + " ..                  ..", + " ..                  ..", + " ...                ...", + " ....              ....", + "  ....            ....", + "    .....      .....", + "      ............", + "        ........", + "          ||", + "          ||", + "          ||", + "          ||", + "          ||", + "           ||", + "          ||", + "          ||", + "           ||", + "          ||", + "          ||", + "          ||", + "          ||", + "          ||", + "          ||", + "          ||", + "          ||", + "          ||", + "          ||", + "          ||", + "           ||", + "           ||", + "           ||", + "           ||", + "           ||", + "          ||", + "          ||", + "       ........", + "     ............", + "    .....      .....", + "  ....            ....", + " ....              ....", + " ...                ...", + " ..                  ..", + " ..                  ..", + "  .                  ." + ] + }, + { + "type": "ascii_art", + "id": "wristwatch", + "picture": [ + "", + "   _____", + "       /     \\", + "        │  o  │", + "        │     │", + "        │  o  │", + "        │     │", + "        │  o  │", + "        │     │", + "        │  o  │", + "        │     │", + "        │  o  │", + "        │     │", + "        │     │", + "        │     │", + "        │     │", + "        │     │", + "       .........", + "    ...   12    ...", + "   .   11  │   1   .", + "  . 10     │      2 .", + " .         │         .", + " . 9       └───     3.", + " .                   .", + "  . 8             4 .", + "   .   7       5   .", + "    ...    6    ...", + "       .........", + "        │     │", + "        │     │", + "        │     │", + "        │     │", + "        │     │", + "        │     │", + "        │     │", + "        │     │", + "        │     │", + "        │     │", + "        │     │", + "        │     │", + "        │     │", + "        └─────┘", + "        ║  ║  ║", + "        ╚══╩══╝" + ] + }, + { + "type": "ascii_art", + "id": "mobile_memory_card", + "picture": [ + "", + "", + " ______________", + "   /  ╔╗╔╗╔╗╔╗╔╗╔╗|", + "  /╔╗ ╚╝╚╝╚╝╚╝╚╝╚╝|", + " / ╚╝            |", + " |                |", + " |                |", + " |                |", + " |                |", + " |                |", + " | MADE IN CHINA  |", + " |________________|" + ] + }, + { + "type": "ascii_art", + "id": "halberd", + "picture": [ + "", + "        .", + "        .", + "        .", + "        .", + "       ...", + "       ...", + "       ...", + "       ...      ...", + "       ...     .......", + "       ...    ..........", + "      .....  ............", + "     .....................", + "    .......................", + "   ........................", + "  .........................", + " ...    ..  ..............", + " .      ..   ............", + "        ..    ..........", + "        ..     .......", + "        ..      ...", + "        ..", + "        ..", + "        ..", + "        ..", + "        ..", + "        ..", + "        ..", + "        ..", + "        ..", + "        ..", + "        ..", + "        ..", + "        ..", + "        ..", + "        ..", + "        ..", + "        ..", + "        ..", + "        ..", + "        ..", + "        ..", + "        ..", + "        ..", + "        ..", + "        ..", + "        .." + ] + }, + { + "type": "ascii_art", + "id": "1st_aid", + "picture": [ + "", + "", + "            ╔═══════════╗", + "   ════════════════════════════════╗", + "  /         ║           ║          / ║", + " ╔═════════╩═══════════╩═════════╗  ║", + "  ║                               ║  ║", + "  ║             ╔═══╗             ║  ║", + "  ║             ║   ║             ║  ║", + " ║         ╔═══╝   ╚═══╗         ║  ║", + " ║         ║           ║         ║  ║", + "  ║         ╚═══╗   ╔═══╝         ║  ║", + " ║             ║   ║             ║  ║", + " ║             ╚═══╝             ║  ║", + "  ║                               ║ /", + "  ╚═══════════════════════════════╝" + ] + }, + { + "type": "ascii_art", + "id": "thermometer", + "picture": [ + "", + "", + "         :::::::::", + "      :::         :::", + "    ::      ┌─┐      ::", + "   :120_____│ │------50:", + "  :     ____│ │----     :", + " :      ____│ │----      :", + " :100 ______│ │------ 40 :", + " :      ____│ │----      :", + " :      ____│ │----      :", + " : 80 ______│ │------ 30 :", + " :      ____│ │----      :", + " :      ----│ │----      :", + " :      ----││------ 20 :", + " : 60 ------││----      :", + " :      ----││----      :", + " :      ----││------ 10 :", + " :      ----││----      :", + " : 40 ------││----      :", + " :      ____││______ 0  :", + " :      ____││____      :", + " : 20 ______││____      :", + " :      ____││____      :", + " :      ____││------ -10:", + " :      ____││----      :", + " :  0 ------││----      :", + " :          ││------ -20:", + " :          ││          :", + " :          ││          :", + " :  °F      ││      °C  :", + " :          ││          :", + " :          ││          :", + " :::::::::::::::::::::::::" + ] + }, + { + "type": "ascii_art", + "id": "umbrella", + "picture": [ + "", + "", + "                :::::::::::", + "           :::::###########:::::", + "         ::####:###########:####::", + "       ::#####:#############:#####::", + "      :######:###############:######:", + "     :#######:###############:#######:", + "    :#######:#################:#######:", + "   :##:::::::#####:::::::#####:::::::##:", + "  :#::       ::#::  I|I  ::#::       ::#:", + "  :::         :::   I|I   :::         :::", + "                    I|I", + "                    I|I", + "                    I|I", + "                    I|I", + "                    I|I", + "                    I|I", + "                    I|I", + "                    I|I", + "                    I|I", + "                    I|I", + "                    I|I", + "                    I|I", + "                    I|I", + "                    I|I", + "                    I|I", + "                J   JJJ", + "                JJJJJJJ", + "                 JJJJJ" + ] + }, + { + "type": "ascii_art", + "id": "badge_deputy", + "picture": [ + "", + "                   ⚪", + "                  / \\", + "                 /   \\", + "                /     \\", + "      ⚪ _______/       \\_______ ⚪", + "       \\                        /", + "        \\          /", + "         \\    ════════════    /", + "          \\     DEPUTY     /", + "         /    SHERIFF     \\", + "        /     ════════════    \\", + "       /            \\", + "      /_______           _______\\", + "     ⚪         \\       /         ⚪", + "                \\     /", + "                 \\   /", + "                  \\ /", + "                   ⚪" + ] + }, + { + "type": "ascii_art", + "id": "box_cigarette", + "picture": [ + "", + "                     ╔══╗", + "                ╔══╗   ", + "┌───────────────    ───────────────┐", + "│          ╔══╗                     │", + "│                   ╔══╗          │", + "│     ╔══╗                      │", + "│                             │", + "│                    ╔══╗     │", + "│                           │", + "├─┐                       ┌─┤", + "│ └┐                     ┌┘ │", + "│                         │", + "│                         │", + "│ └──╨──╨─╨──╨─╨──╨─╨──╨─╨──╨─╨──╨──┘ │", + "│                                       │", + "│ _________ .__ │", + "│ \\_ ___ \\|__| ____ ______ │", + "│ / \\ \\/| |/ ___\\/ ___/ │", + "│ \\ \\___| / /_/ >___ \\ │", + "│ \\______ /__\\___ /____ > │", + "│ \\/ /_____/ \\/ │", + "│ ________ __ │", + "│ \\______ \\ _____ _______| | __ │", + "│ | | \\\\__ \\\\_ __ \\ |/ / │", + "│ | ` \\/ __ \\| | \\/ < │", + "│ /_______ (____ /__| |__|_ \\ │", + "│ \\/ \\/ \\/ │", + "│ .____ │", + "│ | | __ __ ____ ____ _____ │", + "│ | | | | \\/ \\ / ___\\/ ___/│", + "│ | |___| | / | \\/ /_/ >___ \\ │", + "│ |_______ \\____/|___| /\\___ /____ >│", + "│ \\/ \\//_____/ \\/ │", + "│ _____ .__ ._ │", + "│ / _ \\ | |__ ____ _____ __| |│", + "│ / /_\\ \\| | \\_/ __ \\\\__ \\ / __ |│", + "│/ | \\ Y \\ ___/ / __ \\_/ /_/ |│", + "│\\____|__ /___| /\\___ >____ /\\____ |│", + "│ \\/ \\/ \\/ \\/ \\/│", + "│                                       │", + "└───────────────────────────────────────┘" + ] + }, + { + "type": "ascii_art", + "id": "textbook_chemistry", + "picture": [ + "", + "   /════════════════════════════════════╗", + "  /───────────────────────────────────┐│", + " /───────────────────────────────────┐││", + "╔═══════════════════════════════════╗│││", + "   Textbook by K.M. Pozdro     │││", + "     ╔═╗╦ ╦╔═╗╔╦╗╦╔═╗╔╦╗╦═╗╦ ╦     │││", + "     ║ ╠═╣║╣ ║║║║╚═╗ ║ ╠╦╝╚╦╝     │││", + "     ╚═╝╩ ╩╚═╝╩ ╩╩╚═╝ ╩ ╩╚═ ╩      │││", + "                        _____     │││", + "                             \\\\   │││", + "                              \\\\  │││", + "                //            /   │││", + "               //            /    │││", + " \\             \\             \\\\   │││", + "  \\             \\_____        \\\\  │││", + "  /       ///   /     \\       /   │││", + " /       ///   /       \\     /    │││", + " \\       /     \\      //     \\    │││", + "  \\_____/       \\____//       \\   │││", + "  /     \\       /             /   │││", + " /       \\_____/        _____/    │││", + " \\       //    \\             \\    │││", + "  \\_____//      \\_____        \\   │││", + "        \\       /    \\\\       //  │││", + "         \\_____/      \\\\     //   │││", + " \\       /     \\       /     \\    │││", + "  \\_____/       \\_____/       \\   │││", + "  /     \\       /             /   │││", + " /       \\_____/        _____/    │││", + "               \\       /          │││", + "                \\_____/           │││", + "                                 ││/", + "                                 │/", + "╚═══════════════════════════════════╝" + ] + }, + { + "type": "ascii_art", + "id": "electrohack", + "picture": [ + "", + "                     ┌────┐", + "                     │□  □│", + "                         ", + "      ╔════════════════════════╗", + "      001100010011001100110011", + "      001101110100100000110100", + "      011110000011000001110010", + "       ╚════════════════════════╝", + "        ╚╝╚╝╚╝               ", + "                             ", + "                             ╚══──", + "                            ╚════──", + "                           ╚══════──" + ] + }, + { + "type": "ascii_art", + "id": "manual_first_aid", + "picture": [ + "   /════════════════════════════════════╗", + "  /───────────────────────────────────┐│║", + " /───────────────────────────────────┐││║", + "╔═══════════════════════════════════╗│││║", + "║                                   │││║", + "║                                   │││║", + "╔╦╗╦ ╦╔═╗ ╔╗ ╦╔═╗ ╔╗ ╔═╗╔═╗╦╔═  │││║", + "║ ╠═╣║╣ ╠╩╗║║ ╦ ╠╩╗║ ║║ ║╠╩╗  │││║", + "╩ ╩ ╩╚═╝ ╚═╝╩╚═╝ ╚═╝╚═╝╚═╝╩ ╩ │││║", + "║  ╔═╗╔═╗ ╔═╗╦╦═╗╔═╗╔╦╗ ╔═╗╦╔╦╗   │││║", + "║  ║ ║╠╣ ╠╣ ║╠╦╝╚═╗ ║ ╠═╣║ ║║   │││║", + "║  ╚═╝╚ ╚ ╩╩╚═╚═╝ ╩ ╩ ╩╩═╩╝   │││║", + "║                                   │││║", + "║              │││║", + "║              │││║", + "║              │││║", + "║              ╔═════╗          │││║", + "║              ║     ║          │││║", + "║              ║     ║          │││║", + "║              ║     ║          │││║", + "║       ╔══════╝     ╚══════╗   │││║", + "║       ║                   ║   │││║", + "║       ║                   ║   │││║", + "║       ╚══════╗     ╔══════╝   │││║", + "║              ║     ║          │││║", + "║              ║     ║              │││║", + "║              ║     ║              │││║", + "║              ╚═════╝              │││║", + "║                                   │││║", + "║                                   │││║", + "║             By C. Red             │││║", + "║                                   ││/", + "║                                   │/", + "╚═══════════════════════════════════╝" + ] } ] diff --git a/data/json/body_parts.json b/data/json/body_parts.json index 21a845e65be0c..c55f0943ac93b 100644 --- a/data/json/body_parts.json +++ b/data/json/body_parts.json @@ -109,6 +109,7 @@ "hot_morale_mod": 0.5, "cold_morale_mod": 0.5, "squeamish_penalty": 5, + "is_limb": true, "base_hp": 60, "bionic_slots": 20 }, @@ -133,6 +134,7 @@ "hot_morale_mod": 0.5, "cold_morale_mod": 0.5, "squeamish_penalty": 5, + "is_limb": true, "base_hp": 60, "bionic_slots": 20 }, @@ -206,6 +208,7 @@ "hot_morale_mod": 0.5, "cold_morale_mod": 0.5, "squeamish_penalty": 5, + "is_limb": true, "base_hp": 60, "bionic_slots": 30 }, @@ -231,6 +234,7 @@ "hot_morale_mod": 0.5, "cold_morale_mod": 0.5, "squeamish_penalty": 5, + "is_limb": true, "base_hp": 60, "bionic_slots": 30 }, 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/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-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/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/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/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/ammo/10mm.json b/data/json/items/ammo/10mm.json index 0ed5e796e9cfe..4aea51b6f297b 100644 --- a/data/json/items/ammo/10mm.json +++ b/data/json/items/ammo/10mm.json @@ -5,7 +5,7 @@ "name": { "str": "10mm Auto FMJ" }, "description": "A jacketed 10mm Auto round. The 10mm Auto cartridge is a rather powerful handgun round and the progenitor to the more popular .40 S&W.", "weight": "9 g", - "volume": "250 ml", + "volume": "117 ml", "price": 400, "price_postapoc": 800, "flags": [ "IRREPLACEABLE_CONSUMABLE" ], diff --git a/data/json/items/ammo/20x66mm.json b/data/json/items/ammo/20x66mm.json index 3618f33b38f80..c46761a6132d1 100644 --- a/data/json/items/ammo/20x66mm.json +++ b/data/json/items/ammo/20x66mm.json @@ -117,7 +117,7 @@ "//": "2.5x the Generic Rate of $1/shot", "description": "20x66mm caseless shotgun rounds, buckshot type. Proprietary ammunition for Rivtech shotguns. Being caseless rounds, these cannot be disassembled or reloaded.", "weight": "56 g", - "volume": "250 ml", + "volume": "415 ml", "price": 1500, "price_postapoc": 4000, "flags": [ "IRREPLACEABLE_CONSUMABLE" ], diff --git a/data/json/items/ammo/22.json b/data/json/items/ammo/22.json index fcbcda4300296..b0eb8be83634c 100644 --- a/data/json/items/ammo/22.json +++ b/data/json/items/ammo/22.json @@ -28,7 +28,7 @@ "name": { "str": ".22 LR" }, "description": ".22 Long Rifle ammunition with 40gr unjacketed bullets. The .22LR round is extremely weak with very low stopping power, short range, and negligible recoil. It is most useful for rifle training, and hunting small animals.", "weight": "3 g", - "volume": "250 ml", + "volume": "65 ml", "price": 150, "price_postapoc": 800, "flags": [ "IRREPLACEABLE_CONSUMABLE" ], diff --git a/data/json/items/ammo/223.json b/data/json/items/ammo/223.json index 0920cde8c0d42..34f66b290c308 100644 --- a/data/json/items/ammo/223.json +++ b/data/json/items/ammo/223.json @@ -5,7 +5,7 @@ "name": { "str": ".223 Remington" }, "description": ".223 Remington ammunition with 36gr JHP bullets. The .223 round has been very popular with civilian shooters for almost a century, finding use in a wide variety of weapons. It generates lower pressure than 5.56 NATO leading to slightly decreased accuracy and recoil.", "weight": "12 g", - "volume": "250 ml", + "volume": "194 ml", "price": 280, "price_postapoc": 900, "flags": [ "IRREPLACEABLE_CONSUMABLE" ], diff --git a/data/json/items/ammo/270win.json b/data/json/items/ammo/270win.json index d9812da22a13a..c433098d960f5 100644 --- a/data/json/items/ammo/270win.json +++ b/data/json/items/ammo/270win.json @@ -5,7 +5,7 @@ "name": { "str": ".270 Winchester JSP" }, "description": ".270 Winchester ammunition with 130gr soft point bullets. The .270 round was not initially successful, but over a few decades it became one of the most popular rifle cartridges for hunting and silhouette shooting. It is a powerful round capable of taking down large targets with ease.", "weight": "20 g", - "volume": "250 ml", + "volume": "189 ml", "price": 170, "price_postapoc": 600, "flags": [ "IRREPLACEABLE_CONSUMABLE" ], diff --git a/data/json/items/ammo/300.json b/data/json/items/ammo/300.json index d0a05c6f90e9f..0e5455cbffbc7 100644 --- a/data/json/items/ammo/300.json +++ b/data/json/items/ammo/300.json @@ -5,7 +5,7 @@ "name": { "str": ".300 Winchester Magnum" }, "description": ".300 Winchester Magnum rounds with 220gr JHP bullets. The 300WM round is an extremely powerful and accurate rifle round introduced in 1963. It has proven popular with hunters and snipers, although it is not as common as .308 or .30-06.", "weight": "28 g", - "volume": "250 ml", + "volume": "169 ml", "price": 220, "price_postapoc": 400, "flags": [ "IRREPLACEABLE_CONSUMABLE" ], diff --git a/data/json/items/ammo/3006.json b/data/json/items/ammo/3006.json index d4fd0091d45a9..eda24b798537f 100644 --- a/data/json/items/ammo/3006.json +++ b/data/json/items/ammo/3006.json @@ -5,7 +5,7 @@ "name": { "str": ".30-06 Springfield" }, "description": ".30-06 Springfield rounds with 165gr soft point bullets. The .30-06 cartridge has excellent accuracy, range, and stopping power making it popular with hunters and snipers for well over a century.", "weight": "20 g", - "volume": "250 ml", + "volume": "192 ml", "price": 160, "price_postapoc": 800, "flags": [ "IRREPLACEABLE_CONSUMABLE" ], diff --git a/data/json/items/ammo/300blk.json b/data/json/items/ammo/300blk.json index 1d1f2407bdbb1..171c1d3d5095b 100644 --- a/data/json/items/ammo/300blk.json +++ b/data/json/items/ammo/300blk.json @@ -5,7 +5,7 @@ "name": { "str": ".300 AAC Blackout" }, "description": "A .300 AAC Blackout round with a 125gr open tip match bullet. 300 BLK is an intermediate cartridge necked up from 5.56x45mm, designed to achieve similar ballistics to 7.62x39mm. It is compatible with standard AR-15 lower receivers and will feed from STANAG magazines.", "weight": "18 g", - "volume": "250 ml", + "volume": "194 ml", "price": 290, "price_postapoc": 1500, "flags": [ "IRREPLACEABLE_CONSUMABLE" ], diff --git a/data/json/items/ammo/308.json b/data/json/items/ammo/308.json index 3691c8269a384..0924126081723 100644 --- a/data/json/items/ammo/308.json +++ b/data/json/items/ammo/308.json @@ -5,7 +5,7 @@ "name": { "str": ".308 Winchester" }, "description": ".308 Winchester ammunition with 168gr hollow point bullets. The .308 round is one of the most popular hunting cartridges in the world due to its accuracy and power.", "weight": "18 g", - "volume": "250 ml", + "volume": "158 ml", "price": 180, "price_postapoc": 800, "flags": [ "IRREPLACEABLE_CONSUMABLE" ], diff --git a/data/json/items/ammo/32.json b/data/json/items/ammo/32.json index df6067a0cadbe..0d153ce5795ba 100644 --- a/data/json/items/ammo/32.json +++ b/data/json/items/ammo/32.json @@ -5,7 +5,7 @@ "name": { "str": ".32 ACP" }, "description": "The .32 ACP was a popular handgun cartridge in the 20th century. Not so powerful as the .38 or the 9x19mm though.", "weight": "5 g", - "volume": "250 ml", + "volume": "116 ml", "price": 160, "price_postapoc": 1600, "flags": [ "IRREPLACEABLE_CONSUMABLE" ], diff --git a/data/json/items/ammo/357mag.json b/data/json/items/ammo/357mag.json index 74bfec49915a6..74218a588890e 100644 --- a/data/json/items/ammo/357mag.json +++ b/data/json/items/ammo/357mag.json @@ -5,7 +5,7 @@ "name": { "str": ".357 magnum FMJ" }, "description": "Jacketed .357 magnum ammunition. The .357 magnum round is derived from the earlier .38 special, with a marginally longer case and generating greater pressure.", "weight": "8 g", - "volume": "250 ml", + "volume": "145 ml", "price": 140, "price_postapoc": 1000, "flags": [ "IRREPLACEABLE_CONSUMABLE" ], diff --git a/data/json/items/ammo/357sig.json b/data/json/items/ammo/357sig.json index 095acfc0f7d9d..e1b15fc0fc059 100644 --- a/data/json/items/ammo/357sig.json +++ b/data/json/items/ammo/357sig.json @@ -5,7 +5,7 @@ "name": { "str": ".357 SIG FMJ" }, "description": "Jacketed .357 SIG ammunition. The .357 SIG round is a high velocity pistol cartridge, giving it a flatter trajectory than many handgun rounds.", "weight": "8 g", - "volume": "250 ml", + "volume": "132 ml", "price": 370, "price_postapoc": 1000, "flags": [ "IRREPLACEABLE_CONSUMABLE" ], diff --git a/data/json/items/ammo/36paper.json b/data/json/items/ammo/36paper.json index 407aa0d1e7fc3..d94448307ca25 100644 --- a/data/json/items/ammo/36paper.json +++ b/data/json/items/ammo/36paper.json @@ -5,7 +5,7 @@ "name": { "str": ".36 paper cartridge" }, "description": "A paper cartridge containing a premeasured amount of black powder and a .36 projectile. Used by the Colt M1861 Navy.", "weight": "6 g", - "volume": "250 ml", + "volume": "11 ml", "price": 400, "price_postapoc": 1000, "material": [ "paper", "powder", "lead" ], diff --git a/data/json/items/ammo/38.json b/data/json/items/ammo/38.json index 336f65bbafd6b..44adc85c0a9f1 100644 --- a/data/json/items/ammo/38.json +++ b/data/json/items/ammo/38.json @@ -13,7 +13,7 @@ "name": { "str": ".38 Special" }, "description": ".38 Special ammunition with 130gr FMJ bullets. The .38 Special round was extremely common among US police forces during the 20th century.", "weight": "8 g", - "volume": "250 ml", + "volume": "113 ml", "price": 210, "price_postapoc": 800, "flags": [ "IRREPLACEABLE_CONSUMABLE" ], diff --git a/data/json/items/ammo/380.json b/data/json/items/ammo/380.json index d7956141ecba3..333d1eb1db35b 100644 --- a/data/json/items/ammo/380.json +++ b/data/json/items/ammo/380.json @@ -5,7 +5,7 @@ "name": { "str": ".380 ACP FMJ" }, "description": ".380 ACP ammunition with a brass jacketed 95gr bullet. Popular in pocket pistols for over a century, it is often considered the weakest caliber to consider carrying. One should be careful not to chamber it in 9x18mm Makarov or 9x19mm firearms.", "weight": "6 g", - "volume": "250 ml", + "volume": "89 ml", "price": 140, "price_postapoc": 1000, "flags": [ "IRREPLACEABLE_CONSUMABLE" ], diff --git a/data/json/items/ammo/38super.json b/data/json/items/ammo/38super.json index f11b34e732653..8ac784d86148c 100644 --- a/data/json/items/ammo/38super.json +++ b/data/json/items/ammo/38super.json @@ -5,7 +5,7 @@ "name": { "str": ".38 Super FMJ" }, "description": ".38 Super ammunition with 147gr FMJ bullets. The .38 Super round was developed from .38 ACP in the 1920s, designed to penetrate the body armor of the era.", "weight": "8 g", - "volume": "250 ml", + "volume": "97 ml", "price": 210, "price_postapoc": 1200, "flags": [ "IRREPLACEABLE_CONSUMABLE" ], diff --git a/data/json/items/ammo/40.json b/data/json/items/ammo/40.json index 8b8f0fcc914d1..540c66ecbbd15 100644 --- a/data/json/items/ammo/40.json +++ b/data/json/items/ammo/40.json @@ -13,7 +13,7 @@ "name": { "str": ".40 S&W JHP" }, "description": ".40 S&W ammunition with 135gr JHP bullets. The .40 S&W round is a descended from the 10mm Auto cartridge and maintains most of its predecessor's strengths while reducing recoil.", "weight": "9 g", - "volume": "250 ml", + "volume": "106 ml", "price": 220, "price_postapoc": 800, "flags": [ "IRREPLACEABLE_CONSUMABLE" ], diff --git a/data/json/items/ammo/410shot.json b/data/json/items/ammo/410shot.json index 757b9b403a3ef..f671fa68ef1bf 100644 --- a/data/json/items/ammo/410shot.json +++ b/data/json/items/ammo/410shot.json @@ -5,7 +5,7 @@ "name": { "str": ".410 000 shot" }, "description": "A .410 shell with 5 000 pellets. Good for a hunting or combat load.", "weight": "16 g", - "volume": "250 ml", + "volume": "170 ml", "price": 175, "price_postapoc": 800, "flags": [ "IRREPLACEABLE_CONSUMABLE" ], diff --git a/data/json/items/ammo/44.json b/data/json/items/ammo/44.json index 5fb3c5572891c..12a6dbdf19d47 100644 --- a/data/json/items/ammo/44.json +++ b/data/json/items/ammo/44.json @@ -13,7 +13,7 @@ "name": { "str": ".44 Magnum" }, "description": ".44 magnum ammunition with 240gr JHP bullets. The .44 magnum round is one of the most powerful handgun cartridges available. It has excellent stopping power but suffers from extremely high recoil for a handgun round.", "weight": "13 g", - "volume": "250 ml", + "volume": "87 ml", "price": 175, "price_postapoc": 600, "flags": [ "IRREPLACEABLE_CONSUMABLE" ], diff --git a/data/json/items/ammo/44paper.json b/data/json/items/ammo/44paper.json index 277fbb1fde8a7..4e8aac11438e2 100644 --- a/data/json/items/ammo/44paper.json +++ b/data/json/items/ammo/44paper.json @@ -5,7 +5,7 @@ "name": { "str": ".44 paper cartridge" }, "description": "A paper cartridge containing a premeasured amount of black powder and a .44 projectile. Used by the Colt M1860 Army.", "weight": "7 g", - "volume": "250 ml", + "volume": "20 ml", "price": 350, "price_postapoc": 1000, "material": [ "paper", "powder", "lead" ], diff --git a/data/json/items/ammo/45.json b/data/json/items/ammo/45.json index ad35f3146cb7b..efbc4137e9372 100644 --- a/data/json/items/ammo/45.json +++ b/data/json/items/ammo/45.json @@ -13,7 +13,7 @@ "name": { "str": ".45 ACP JHP" }, "description": ".45 ACP ammunition with 185gr JHP bullets. The .45 ACP round was developed to replace the .38 Long Colt cartridge in the early 20th century. It has good stopping power but above average recoil.", "weight": "10 g", - "volume": "250 ml", + "volume": "112 ml", "price": 180, "price_postapoc": 600, "flags": [ "IRREPLACEABLE_CONSUMABLE" ], diff --git a/data/json/items/ammo/454.json b/data/json/items/ammo/454.json index d46502dbd2c42..00fb52e2a7be5 100644 --- a/data/json/items/ammo/454.json +++ b/data/json/items/ammo/454.json @@ -5,7 +5,7 @@ "name": { "str": ".454 Casull" }, "description": ".454 Casull ammunition with 300gr jacketed soft point bullets. The .454 Casull round is derived from .45 Long Colt with a stronger, lengthened case. It is an exceptionally powerful cartridge with higher stopping power than many rifle rounds, although it suffers from extreme recoil making it unsuitable for most purposes.", "weight": "16 g", - "volume": "250 ml", + "volume": "79 ml", "price": 500, "price_postapoc": 800, "flags": [ "IRREPLACEABLE_CONSUMABLE" ], diff --git a/data/json/items/ammo/4570.json b/data/json/items/ammo/4570.json index 8ecb9882c0c68..5ebae6226c854 100644 --- a/data/json/items/ammo/4570.json +++ b/data/json/items/ammo/4570.json @@ -5,7 +5,7 @@ "name": { "str": ".45-70 SP" }, "description": ".45-70 Government ammunition loaded with a 305 grain soft point round. One of the oldest cartridges still in use, it is still a favorite for large game hunting at short ranges.", "weight": "35 g", - "volume": "250 ml", + "volume": "167 ml", "price": 125, "price_postapoc": 800, "flags": [ "IRREPLACEABLE_CONSUMABLE" ], diff --git a/data/json/items/ammo/45colt.json b/data/json/items/ammo/45colt.json index 2932999efae2a..bbdc072ca8cec 100644 --- a/data/json/items/ammo/45colt.json +++ b/data/json/items/ammo/45colt.json @@ -5,7 +5,7 @@ "name": { "str": ".45 Colt JHP" }, "description": ".45 Colt ammunition with 250gr jacketed hollow point bullets. Originally designed for the Colt Single Action Army, and still used for modern reproduction revolvers. Originally a black powder cartridge, modern loads can make this round competitive in the new era.", "weight": "12 g", - "volume": "250 ml", + "volume": "190 ml", "price": 200, "price_postapoc": 800, "flags": [ "IRREPLACEABLE_CONSUMABLE" ], diff --git a/data/json/items/ammo/46.json b/data/json/items/ammo/46.json index 37650e0ab4db3..b6e46642af770 100644 --- a/data/json/items/ammo/46.json +++ b/data/json/items/ammo/46.json @@ -5,7 +5,7 @@ "name": { "str": "4.6x30mm" }, "description": "4.6x30mm ammunition with 31gr copper plated steel bullets. The 4.6x30mm round was developed by H&K to compete with FN Herstal's 5.7x28mm cartridge. It has low recoil and excellent armor penetration.", "weight": "5 g", - "volume": "250 ml", + "volume": "156 ml", "price": 300, "price_postapoc": 1600, "flags": [ "IRREPLACEABLE_CONSUMABLE" ], diff --git a/data/json/items/ammo/50.json b/data/json/items/ammo/50.json index 0a0eff6245bd5..619f68301ff7a 100644 --- a/data/json/items/ammo/50.json +++ b/data/json/items/ammo/50.json @@ -15,7 +15,7 @@ "name": { "str": ".50 BMG M33 Ball" }, "description": ".50 BMG ammunition with mild steel cored 661gr FMJ bullets. The .50 BMG is a very powerful rifle round designed for anti-aircraft use, later adapted to anti-vehicular and anti-personnel roles. Its stupendous energy and armor piercing capabilities make it one of the most deadly rounds available, offset only by its drastic recoil and noise.", "weight": "114 g", - "volume": "250 ml", + "volume": "451 ml", "price": 2200, "price_postapoc": 2500, "flags": [ "IRREPLACEABLE_CONSUMABLE" ], diff --git a/data/json/items/ammo/500.json b/data/json/items/ammo/500.json index 1f115ef4770c6..2deef17dabe57 100644 --- a/data/json/items/ammo/500.json +++ b/data/json/items/ammo/500.json @@ -5,7 +5,7 @@ "name": { "str": ".500 S&W Magnum" }, "description": ".500 S&W Magnum ammunition with 500gr bullets. The .500 S&W Magnum round is a colossally powerful handgun cartridge capable of killing almost any target with one hit. It has extremely high damage and recoil to match.", "weight": "15 g", - "volume": "250 ml", + "volume": "82 ml", "price": 200, "price_postapoc": 1000, "flags": [ "IRREPLACEABLE_CONSUMABLE" ], diff --git a/data/json/items/ammo/545x39.json b/data/json/items/ammo/545x39.json index 67b9e40515881..abcbb7940770d 100644 --- a/data/json/items/ammo/545x39.json +++ b/data/json/items/ammo/545x39.json @@ -6,7 +6,7 @@ "//": "Mainlined from Icecoon's Weapons Pack.", "description": "5.45x39mm 7N10 ammunition with 56gr FMJ bullets. The 5.45x39mm round was introduced along with the AK-74 in 1974. It has superior wounding potential to the older 7.62x39mm round and quickly replaced it in Soviet military use.", "weight": "10 g", - "volume": "250 ml", + "volume": "134 ml", "price": 100, "price_postapoc": 900, "flags": [ "IRREPLACEABLE_CONSUMABLE" ], diff --git a/data/json/items/ammo/57.json b/data/json/items/ammo/57.json index 8213a2379c543..a6aac6c2c8490 100644 --- a/data/json/items/ammo/57.json +++ b/data/json/items/ammo/57.json @@ -5,7 +5,7 @@ "name": { "str": "5.7x28mm SS190" }, "description": "5.7x28mm ammunition with 31gr AP FMJ bullets. The 5.7x28mm cartridge was designed by FN Herstal to replace the 9x19mm round in NATO use. Although the project to replace 9x19mm Parabellum was effectively canceled the 5.7x28mm round has seen action in many conflicts and has proven to be reliable. It has very low recoil and its armor penetration is a defining feature.", "weight": "6 g", - "volume": "250 ml", + "volume": "121 ml", "price": 350, "price_postapoc": 1600, "flags": [ "IRREPLACEABLE_CONSUMABLE" ], diff --git a/data/json/items/ammo/5x50.json b/data/json/items/ammo/5x50.json index c1a285cb912a6..82811ce0a5228 100644 --- a/data/json/items/ammo/5x50.json +++ b/data/json/items/ammo/5x50.json @@ -5,7 +5,7 @@ "name": { "str": "RA110 5x50mm flechette" }, "description": "Designed to defeat modern body armor, the Rivtech 5x50mm flechette round features a biodegradable sabot and a single, fin-stabilized penetrator.", "weight": "8 g", - "volume": "250 ml", + "volume": "144 ml", "price": 1125, "material": [ "plastic", "powder", "steel" ], "symbol": "=", diff --git a/data/json/items/ammo/700nx.json b/data/json/items/ammo/700nx.json index 7eb5b7e653e6d..5655276b2a155 100644 --- a/data/json/items/ammo/700nx.json +++ b/data/json/items/ammo/700nx.json @@ -5,7 +5,7 @@ "name": { "str": ".700 NX" }, "description": "The .700 Nitro Express is a very powerful rifle round designed for long-range use. Its stupendous accuracy and armor piercing capabilities make it one of the most deadly rounds available, offset only by its drastic recoil and noise.", "weight": "80 g", - "volume": "250 ml", + "volume": "165 ml", "price": 5700, "price_postapoc": 2000, "flags": [ "IRREPLACEABLE_CONSUMABLE" ], diff --git a/data/json/items/ammo/762.json b/data/json/items/ammo/762.json index 7f770d227c134..8b974c3a8fee0 100644 --- a/data/json/items/ammo/762.json +++ b/data/json/items/ammo/762.json @@ -5,7 +5,7 @@ "name": { "str": "7.62x39mm 57-N-231" }, "description": "7.62x39mm 57-N-231 ammunition with 121.9gr steel core FMJ bullets. Developed in WW2 by the Soviet Union the 7.62x39mm round rapidly became extremely popular all over the world. The bullet has poor wounding potential due to its stability, only beginning to yaw after 26cm.", "weight": "16 g", - "volume": "250 ml", + "volume": "113 ml", "price": 120, "price_postapoc": 900, "flags": [ "IRREPLACEABLE_CONSUMABLE" ], diff --git a/data/json/items/ammo/762R.json b/data/json/items/ammo/762R.json index 805a79dc09d58..dc61c187481c0 100644 --- a/data/json/items/ammo/762R.json +++ b/data/json/items/ammo/762R.json @@ -5,7 +5,7 @@ "name": { "str": "7.62x54mmR" }, "description": "7.62x54mmR ammunition with 150gr FMJ bullets. The 7.62x54mmR round is one of the oldest still in common use, primarily due to the popularity of the Mosin-Nagant rifle. Although it has not been used by militaries for several decades it remains popular with civilians. It is a powerful cartridge capable of killing most targets with one shot.", "weight": "18 g", - "volume": "250 ml", + "volume": "185 ml", "price": 200, "price_postapoc": 600, "flags": [ "IRREPLACEABLE_CONSUMABLE" ], diff --git a/data/json/items/ammo/762x25.json b/data/json/items/ammo/762x25.json index b383f45be5e81..b710af263f010 100644 --- a/data/json/items/ammo/762x25.json +++ b/data/json/items/ammo/762x25.json @@ -5,7 +5,7 @@ "name": { "str": "7.62x25mm JHP" }, "description": "A commercial version of the 7.62x25mm cartridge created for the armed forces of Soviet Russia. It was derived from the 7.63x25mm cartridge used by the C96 pistol.", "weight": "10 g", - "volume": "250 ml", + "volume": "107 ml", "price": 100, "price_postapoc": 800, "flags": [ "IRREPLACEABLE_CONSUMABLE" ], diff --git a/data/json/items/ammo/8x40mm.json b/data/json/items/ammo/8x40mm.json index 25d9b16a37b46..ccc64636b94b3 100644 --- a/data/json/items/ammo/8x40mm.json +++ b/data/json/items/ammo/8x40mm.json @@ -16,7 +16,7 @@ "//": "Cased ammo tends to be roughly $1/shot, more or less. Rivtech ammo, being New and Proprietary and Expensive, $2-2.50 or so.", "description": "8x40mm caseless rounds. Proprietary ammunition for Rivtech firearms. Being caseless rounds, these cannot be disassembled or reloaded.", "weight": "12 g", - "volume": "250 ml", + "volume": "230 ml", "price": 225, "price_postapoc": 8000, "flags": [ "IRREPLACEABLE_CONSUMABLE" ], diff --git a/data/json/items/ammo/9mm.json b/data/json/items/ammo/9mm.json index f429d6ac5a2e5..a3d86fd9a0bd7 100644 --- a/data/json/items/ammo/9mm.json +++ b/data/json/items/ammo/9mm.json @@ -5,7 +5,7 @@ "name": { "str": "9x19mm JHP" }, "description": "9x19mm ammunition with a 116gr jacketed hollow point bullet. JHP rounds have inferior penetration to FMJ rounds but their expansion slightly increases stopping power against unarmored targets and reduces overpenetration.", "weight": "7 g", - "volume": "250 ml", + "volume": "115 ml", "price": 150, "price_postapoc": 1000, "flags": [ "IRREPLACEABLE_CONSUMABLE" ], diff --git a/data/json/items/ammo/9x18.json b/data/json/items/ammo/9x18.json index 92485acef56ba..345dd5966bc7e 100644 --- a/data/json/items/ammo/9x18.json +++ b/data/json/items/ammo/9x18.json @@ -5,7 +5,7 @@ "name": { "str": "9x18mm 57-N-181S" }, "description": "9x18mm Makarov ammunition with 93gr steel core FMJ bullets. The 9x18mm round was very common in the Eastern Bloc during the 20th century and remained in Russian military service into the 21st century.", "weight": "8 g", - "volume": "250 ml", + "volume": "97 ml", "price": 100, "price_postapoc": 1000, "flags": [ "IRREPLACEABLE_CONSUMABLE" ], diff --git a/data/json/items/ammo/flintlock.json b/data/json/items/ammo/flintlock.json index f7fc54e73daac..11e39af83f301 100644 --- a/data/json/items/ammo/flintlock.json +++ b/data/json/items/ammo/flintlock.json @@ -5,7 +5,7 @@ "name": { "str": "paper cartridge" }, "description": "A paper cartridge containing black powder and a metallic projectile. Historically used to reload muzzleloaders in a more reasonable time.", "weight": "40 g", - "volume": "250 ml", + "volume": "10 ml", "price": 1100, "price_postapoc": 1200, "material": [ "lead", "powder" ], @@ -27,7 +27,7 @@ "name": { "str": "paper shot cartridge" }, "description": "A paper cartridge containing black powder and metallic shot. Historically used to reload muzzleloaders in a more reasonable time.", "weight": "40 g", - "volume": "250 ml", + "volume": "10 ml", "price": 1100, "price_postapoc": 1200, "material": [ "lead", "powder" ], diff --git a/data/json/items/ammo/shot.json b/data/json/items/ammo/shot.json index 49b5723185fec..88544919ee865 100644 --- a/data/json/items/ammo/shot.json +++ b/data/json/items/ammo/shot.json @@ -94,7 +94,7 @@ "name": { "str": "00 shot" }, "description": "A shell filled with metal pellets. Extremely damaging, plus the spread makes it very accurate at short range. Favored by SWAT forces.", "weight": "32 g", - "volume": "250 ml", + "volume": "439 ml", "price": 500, "price_postapoc": 800, "flags": [ "IRREPLACEABLE_CONSUMABLE" ], diff --git a/data/json/items/ammo/signal_flare.json b/data/json/items/ammo/signal_flare.json index df4e447f95f66..c346a97e0199b 100644 --- a/data/json/items/ammo/signal_flare.json +++ b/data/json/items/ammo/signal_flare.json @@ -5,7 +5,7 @@ "name": { "str": "signal flare" }, "description": "A heavy plastic cartridge made for use in flare guns. It appears to be a modified 12 gauge shotgun shell.", "weight": "32 g", - "volume": "250 ml", + "volume": "377 ml", "price": 500, "price_postapoc": 500, "material": [ "plastic", "powder" ], 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 diff --git a/data/json/items/armor/jewelry.json b/data/json/items/armor/jewelry.json index 6982965d88a4b..44428ce2fa0b6 100644 --- a/data/json/items/armor/jewelry.json +++ b/data/json/items/armor/jewelry.json @@ -1293,6 +1293,7 @@ "price_postapoc": 100, "to_hit": -1, "material": [ "plastic", "steel" ], + "ascii_picture": "wristwatch", "symbol": "[", "color": "dark_gray", "covers": [ "HAND_EITHER" ], @@ -1343,7 +1344,8 @@ "copy-from": "badge_abstract", "type": "ARMOR", "name": { "str": "deputy badge" }, - "description": "A tarnished silver star gives an air of authority to the wearer." + "description": "A tarnished silver star gives an air of authority to the wearer.", + "ascii_picture": "badge_deputy" }, { "id": "badge_detective", diff --git a/data/json/items/armor/storage.json b/data/json/items/armor/storage.json index 836b8a59c04ad..d7964bdbb4315 100644 --- a/data/json/items/armor/storage.json +++ b/data/json/items/armor/storage.json @@ -1310,6 +1310,7 @@ "moves": 300 } ], + "warmth": 10, "material_thickness": 2, "flags": [ "WATER_FRIENDLY", "STURDY", "BELTED", "OVERSIZE" ] }, @@ -1340,6 +1341,7 @@ "moves": 300 } ], + "warmth": 6, "material_thickness": 2, "flags": [ "WATER_FRIENDLY", "STURDY", "BELTED" ] }, @@ -1370,6 +1372,7 @@ "moves": 300 } ], + "warmth": 8, "material_thickness": 2, "flags": [ "WATER_FRIENDLY", "STURDY", "BELTED", "OVERSIZE" ] }, @@ -1400,6 +1403,7 @@ "moves": 250 } ], + "warmth": 2, "material_thickness": 2, "flags": [ "WATER_FRIENDLY", "STURDY", "BELTED" ] }, diff --git a/data/json/items/book/chemistry.json b/data/json/items/book/chemistry.json index fa29d746e9ecb..a60e354da1a3d 100644 --- a/data/json/items/book/chemistry.json +++ b/data/json/items/book/chemistry.json @@ -259,6 +259,7 @@ "price_postapoc": 2000, "bashing": 5, "material": [ "paper" ], + "ascii_picture": "textbook_chemistry", "symbol": "?", "color": "blue", "skill": "chemistry", diff --git a/data/json/items/book/firstaid.json b/data/json/items/book/firstaid.json index b2920e02f7acc..8409c02d90902 100644 --- a/data/json/items/book/firstaid.json +++ b/data/json/items/book/firstaid.json @@ -47,6 +47,7 @@ "price": 2500, "price_postapoc": 1250, "material": [ "paper" ], + "ascii_picture": "manual_first_aid", "symbol": "?", "color": "green", "skill": "firstaid", 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" ], diff --git a/data/json/items/comestibles/bread.json b/data/json/items/comestibles/bread.json index 42f7197fd7c5d..5a4096e0fa027 100644 --- a/data/json/items/comestibles/bread.json +++ b/data/json/items/comestibles/bread.json @@ -37,10 +37,10 @@ "color": "white", "use_action": { "target": "sourdough_starter", - "msg": "After feeding it and caring for it for weeks, your sourdough starter is finally ready for the big leagues.", + "msg": "After feeding it and caring for it for a week, your sourdough starter is finally ready for the big leagues.", "moves": 50, "type": "delayed_transform", - "transform_age": 200000, + "transform_age": 518400, "not_ready_msg": "You've been caring for your starter for a while, but it's going to need longer before you can do anything tasty with it." } }, @@ -49,7 +49,7 @@ "type": "GENERIC", "category": "food", "name": { "str": "freshly fed sourdough starter" }, - "description": "This jar contains a floury paste with sourdough starter mixed in. It needs a few hours to recover its strength before it can be used again.", + "description": "This jar contains a floury paste with sourdough starter mixed in. It needs a day to recover its strength before it can be used again.", "weight": "52 g", "volume": "250 ml", "price": 10, @@ -64,7 +64,7 @@ "msg": "The starter is now stinky and bubbly, and looks ready for cooking.", "moves": 50, "type": "delayed_transform", - "transform_age": 180000, + "transform_age": 86400, "not_ready_msg": "The starter isn't quite ready to go." } }, 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/comestibles/drink.json b/data/json/items/comestibles/drink.json index 39ca8c3237680..85e121bfc4f10 100644 --- a/data/json/items/comestibles/drink.json +++ b/data/json/items/comestibles/drink.json @@ -98,6 +98,7 @@ "stim": 1, "quench": 34, "healthy": 1, + "calories": 9, "description": "A healthy beverage made from bee balm steeped in boiling water. Can be used to reduce negative effects of common cold or flu.", "price": 100, "price_postapoc": 25, @@ -146,7 +147,7 @@ "material": [ "milk", "water" ], "volume": "250 ml", "phase": "liquid", - "flags": [ "EATEN_HOT", "NUTRIENT_OVERRIDE" ], + "flags": [ "EATEN_HOT" ], "fun": 14 }, { @@ -159,6 +160,7 @@ "spoils_in": "10 days", "quench": 34, "healthy": 1, + "calories": 0, "description": "A healthy beverage made from chamomile flowers steeped in boiling water. Can be used to treat insomnia.", "price": 100, "price_postapoc": 25, @@ -384,14 +386,14 @@ "symbol": "~", "quench": 48, "healthy": 1, - "calories": 9, + "calories": 26, "description": "A healthy beverage made from dandelion roots steeped in boiling water.", "price": 50, "price_postapoc": 25, "volume": "250 ml", "material": [ "water" ], "phase": "liquid", - "flags": [ "EATEN_HOT", "NUTRIENT_OVERRIDE" ], + "flags": [ "EATEN_HOT" ], "fun": 2 }, { @@ -404,14 +406,14 @@ "symbol": "~", "quench": 48, "healthy": 2, - "calories": 12, + "calories": 77, "description": "A healthy beverage made from dandelion and burdock roots steeped in boiling water. The additional ingredient makes it slightly healthier and fun.", "price": 50, "price_postapoc": 25, "volume": "250 ml", "material": [ "water" ], "phase": "liquid", - "flags": [ "EATEN_HOT", "NUTRIENT_OVERRIDE" ], + "flags": [ "EATEN_HOT" ], "fun": 3 }, { @@ -499,9 +501,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", @@ -630,6 +631,7 @@ "spoils_in": "10 days", "quench": 34, "healthy": 1, + "calories": 0, "description": "A healthy beverage made from lotus flowers steeped in boiling water.", "price": 100, "price_postapoc": 25, @@ -911,6 +913,7 @@ "spoils_in": "10 days", "quench": 34, "healthy": 1, + "calories": 0, "description": "A healthy beverage made from spurge flowers steeped in boiling water. Can be used to prevent asthma attacks.", "price": 100, "price_postapoc": 25, @@ -975,7 +978,7 @@ "volume": "250 ml", "material": [ "water" ], "phase": "liquid", - "flags": [ "EATEN_HOT", "NUTRIENT_OVERRIDE" ], + "flags": [ "EATEN_HOT" ], "fun": 6 }, { @@ -995,7 +998,7 @@ "volume": "500 ml", "material": [ "water" ], "phase": "liquid", - "flags": [ "EATEN_HOT", "NUTRIENT_OVERRIDE" ], + "flags": [ "EATEN_HOT" ], "fun": -5 }, { @@ -1059,7 +1062,7 @@ "name": "fruit tea", "copy-from": "tea", "color": "red", - "calories": 6, + "calories": 0, "fatigue_mod": 0, "stim": 0, "description": "A tasty beverage made with herbs and dried fruit from plants other than the tea plant. While colloquially called 'tea', technically it's an infusion.", diff --git a/data/json/items/comestibles/med.json b/data/json/items/comestibles/med.json index 36ccd8e75e7c6..d6ae3e01d9289 100644 --- a/data/json/items/comestibles/med.json +++ b/data/json/items/comestibles/med.json @@ -10,6 +10,7 @@ "price": 3000, "price_postapoc": 3000, "material": [ "plastic" ], + "ascii_picture": "1st_aid", "symbol": "!", "color": "red", "use_action": [ "DISASSEMBLE" ], @@ -173,7 +174,7 @@ "copy-from": "bandages", "description": "Simple cloth bandages. Better than nothing.", "price_postapoc": 100, - "flags": [ "NO_INGEST" ], + "flags": [ "NO_INGEST", "BLIND_EASY" ], "use_action": { "type": "heal", "bandages_power": 2, "bleed": 0.9, "move_cost": 300 } }, { diff --git a/data/json/items/comestibles/veggy_dishes.json b/data/json/items/comestibles/veggy_dishes.json index a21d87f3eaecb..8badc03dbe885 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": 600, "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 ] ] @@ -716,7 +719,7 @@ "symbol": "%", "quench": -1, "healthy": 1, - "calories": 200, + "calories": 267, "description": "A serving of sticky vinegared rice commonly used in sushi.", "price": 100, "price_postapoc": 400, diff --git a/data/json/items/containers.json b/data/json/items/containers.json index f088a75b17cfc..38fbabee95068 100644 --- a/data/json/items/containers.json +++ b/data/json/items/containers.json @@ -194,8 +194,9 @@ "name": { "str": "body bag" }, "looks_like": "bag_canvas", "description": "A large, human size, rectangular bag made of strong plastic, with a zipper in the middle. Used to hold a dead body.", - "weight": "500 g", + "weight": "1500 g", "volume": "1 L", + "longest_side": "40 cm", "price": 0, "price_postapoc": 10, "to_hit": -5, @@ -207,6 +208,7 @@ "watertight": true, "max_contains_volume": "100 L", "max_contains_weight": "100 kg", + "max_item_length": "200 cm", "moves": 400 } ], @@ -437,6 +439,7 @@ "price": 0, "price_postapoc": 0, "material": [ "paper" ], + "ascii_picture": "box_cigarette", "symbol": ")", "color": "white", "pocket_data": [ { "pocket_type": "CONTAINER", "rigid": true, "max_contains_volume": "250 ml", "max_contains_weight": "3 kg" } ] diff --git a/data/json/items/generic.json b/data/json/items/generic.json index 56c232bcc8776..1b662c9e1207c 100644 --- a/data/json/items/generic.json +++ b/data/json/items/generic.json @@ -1768,6 +1768,7 @@ "price": 1000, "price_postapoc": 10, "material": [ "plastic" ], + "ascii_picture": "mobile_memory_card", "flags": [ "MC_MOBILE", "MC_RANDOM_STUFF", "MC_MAY_BE_ENCRYPTED", "MC_TURN_USED" ], "weight": "5 g", "volume": "2 ml" @@ -1782,6 +1783,7 @@ "price": 500, "price_postapoc": 10, "material": [ "plastic" ], + "ascii_picture": "mobile_memory_card", "flags": [ "MC_MOBILE", "MC_USED" ], "weight": "5 g", "volume": "1 ml" @@ -3023,6 +3025,15 @@ "to_hit": -3, "flags": [ "TRADER_AVOID", "NO_REPAIR" ] }, + { + "type": "GENERIC", + "id": "broken_robofac_laserturret_mk1", + "symbol": ",", + "color": "green", + "name": { "str": "broken laser turret" }, + "weight": "40 kg", + "copy-from": "broken_turret" + }, { "type": "GENERIC", "id": "fire_brick", diff --git a/data/json/items/generic/toys_and_sports.json b/data/json/items/generic/toys_and_sports.json index fc737f5880585..d39878db21e59 100644 --- a/data/json/items/generic/toys_and_sports.json +++ b/data/json/items/generic/toys_and_sports.json @@ -141,9 +141,10 @@ "price": 3000, "price_postapoc": 10, "material": [ "ceramic" ], - "weight": "2000 g", - "volume": "2500 ml", - "bashing": 16, + "weight": "7257 g", + "volume": "5277 ml", + "longest_side": "216 mm", + "bashing": 18, "to_hit": -2 }, { diff --git a/data/json/items/gun/223.json b/data/json/items/gun/223.json index 26a10ce9d8ce7..86afc015cf1fd 100644 --- a/data/json/items/gun/223.json +++ b/data/json/items/gun/223.json @@ -109,11 +109,26 @@ "symbol": "(", "color": "dark_gray", "ammo": [ "223" ], + "skill": "pistol", "range": -6, "ranged_damage": { "damage_type": "bullet", "amount": -9 }, "dispersion": 380, "durability": 6, "min_cycle_recoil": 1350, + "valid_mod_locations": [ + [ "accessories", 4 ], + [ "barrel", 1 ], + [ "bore", 1 ], + [ "brass catcher", 1 ], + [ "grip", 1 ], + [ "mechanism", 4 ], + [ "magazine", 1 ], + [ "muzzle", 1 ], + [ "rail mount", 2 ], + [ "sights", 1 ], + [ "stock mount", 1 ], + [ "underbarrel", 1 ] + ], "pocket_data": [ { "pocket_type": "MAGAZINE_WELL", @@ -507,6 +522,7 @@ "description": "An AR-15 derivative pistol manufactured by Olympic Arms in the nineties. The main difference compared to the AR-15 is that the recoil spring has been moved to the top of the gun, circumventing the necessity for a solid buttstock.", "weight": "2023 g", "volume": "2135 ml", + "longest_side": "456 mm", "price": 125000, "price_postapoc": 5500, "to_hit": -2, @@ -515,11 +531,26 @@ "symbol": "(", "color": "dark_gray", "ammo": [ "223" ], + "skill": "pistol", "range": -6, "ranged_damage": { "damage_type": "bullet", "amount": -11 }, "dispersion": 380, "durability": 6, "min_cycle_recoil": 1350, + "valid_mod_locations": [ + [ "accessories", 4 ], + [ "barrel", 1 ], + [ "bore", 1 ], + [ "brass catcher", 1 ], + [ "grip", 1 ], + [ "mechanism", 4 ], + [ "magazine", 1 ], + [ "muzzle", 1 ], + [ "rail mount", 2 ], + [ "sights", 1 ], + [ "stock mount", 1 ], + [ "underbarrel mount", 1 ] + ], "pocket_data": [ { "pocket_type": "MAGAZINE_WELL", 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, diff --git a/data/json/items/gun/410shot.json b/data/json/items/gun/410shot.json index ef2cbf0cc4e24..3a4777afe7c3e 100644 --- a/data/json/items/gun/410shot.json +++ b/data/json/items/gun/410shot.json @@ -41,6 +41,7 @@ "longest_side": "1152 mm", "price_postapoc": 2000, "blackpowder_tolerance": 80, - "ammo": [ "410shot" ] + "ammo": [ "410shot" ], + "pocket_data": [ { "pocket_type": "MAGAZINE", "rigid": true, "ammo_restriction": { "410shot": 1 } } ] } ] diff --git a/data/json/items/gun/monster_gun.json b/data/json/items/gun/monster_gun.json index 8e0b7e9200b59..4bfc73c060e37 100644 --- a/data/json/items/gun/monster_gun.json +++ b/data/json/items/gun/monster_gun.json @@ -61,5 +61,34 @@ "range": 12, "dispersion": 150, "durability": 8 + }, + { + "id": "feral_human_thrown_rock", + "type": "GUN", + "symbol": "%", + "color": "red", + "name": { "str": "hurled rubble" }, + "description": "Stone at the ready to crush that which isn't part of the blob.", + "material": [ "stone" ], + "flags": [ + "PRIMITIVE_RANGED_WEAPON", + "NEVER_JAMS", + "NONCONDUCTIVE", + "NO_REPAIR", + "WATERPROOF_GUN", + "NO_SALVAGE", + "NO_UNLOAD", + "NO_AMMO" + ], + "skill": "throw", + "ranged_damage": { "damage_type": "bash", "amount": 1 }, + "weight": "540 g", + "volume": "750ml", + "bashing": 2, + "to_hit": 1, + "loudness": 2, + "range": 6, + "dispersion": 150, + "durability": 8 } ] diff --git a/data/json/items/gun/shot.json b/data/json/items/gun/shot.json index bb29392ff70a8..bca608fc6bb05 100644 --- a/data/json/items/gun/shot.json +++ b/data/json/items/gun/shot.json @@ -7,7 +7,8 @@ "name": { "str": "12 gauge pistol" }, "description": "A single shot pistol that loads 12 gauge shotgun shells, handcrafted from scrap.", "weight": "828 g", - "volume": "500 ml", + "volume": "800 ml", + "longest_side": "35 cm", "price": 100000, "price_postapoc": 500, "to_hit": -2, @@ -43,6 +44,7 @@ "description": "An electrically driven six barrel gatling shotgun, fed from handmade cloth belts. Even properly mounted, this seems like an unwieldy beast, and the six separate barrels make for difficult zeroing. The externally driven action means this is much less likely to jam.", "weight": "4980 g", "volume": "4500 ml", + "longest_side": "110 cm", "price": 180000, "price_postapoc": 1750, "to_hit": -2, @@ -71,9 +73,10 @@ "looks_like": "remington_870", "type": "GUN", "name": { "str": "handmade lever shotgun" }, - "description": "A short homemade lever-action shotgun with a small internal tube magazine. While still a primitive pipe and 2x4 design, it is a formiddable shotgun in it's own right with room for improvement.", + "description": "A short homemade lever-action shotgun with a small internal tube magazine. While still a primitive pipe and 2x4 design, it is a formiddable shotgun in its own right with room for improvement.", "weight": "2311 g", "volume": "2 L", + "longest_side": "85 cm", "price": 10000, "price_postapoc": 2250, "to_hit": -1, @@ -107,8 +110,9 @@ "type": "GUN", "name": { "str": "Browning Auto 5" }, "description": "This humble looking shotgun was the earliest successful semi-automatic shotgun, and the second most successful in U.S. history, with over 2.7 million made between 1902 and 1998. The recoil tuning mechanism under the handguard and long dwell time of the action make for pleasant shooting.", - "weight": "4100 g", + "weight": "3090 g", "volume": "3525 ml", + "longest_side": "1263 mm", "looks_like": "remington_870", "price": 80000, "price_postapoc": 2000, @@ -131,6 +135,7 @@ "description": "A bullpup pump-action shotgun, the Kel-Tec KSG uses a pair of magazine tubes to increase its capacity. Each tube has to be loaded separately, but this offers the option of loading different ammunition for different situations.", "weight": "1550 g", "volume": "3371 ml", + "longest_side": "672 mm", "price": 99000, "price_postapoc": 2250, "to_hit": -1, @@ -166,6 +171,7 @@ "description": "A bullpup pump-action shotgun, the Kel-Tec KSG-25 uses a pair of magazine tubes to increase its capacity. Each tube has to be loaded separately, but this offers the option of loading different ammunition for different situations. The big brother of the KSG, it has a longer barrel and longer magazine tubes.", "weight": "2100 g", "volume": "4495 ml", + "longest_side": "971 mm", "price": 140000, "to_hit": -1, "bashing": 9, @@ -198,8 +204,9 @@ "type": "GUN", "name": { "str": "M1014 shotgun" }, "description": "Benelli's first gas-operated shotgun, featuring dual pistons for enhanced reliability with various loads and a collapsible buttstock that reduces length by almost 8 inches. Adopted in 1999 as the M1014 Joint Service Combat Shotgun, the Benelli M4 is one of the finest combat shotguns available.", - "weight": "3550 g", - "volume": "2500 ml", + "weight": "3629 g", + "volume": "6302 ml", + "longest_side": "1021 mm", "price": 169900, "price_postapoc": 3500, "to_hit": -1, @@ -230,8 +237,9 @@ "type": "GUN", "name": { "str_sp": "Mossberg 500 Field" }, "description": "The Mossberg 500 is a popular series of pump-action shotguns, often acquired 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.", - "weight": "3180 g", + "weight": "3402 g", "volume": "2450 ml", + "longest_side": "1166 mm", "price": 53800, "price_postapoc": 2250, "to_hit": -1, @@ -252,6 +260,7 @@ "description": "The Mossberg 500 is a popular series of pump-action shotguns, often acquired for military use. It is noted for its high durability and low recoil. This one is fitted with an 18.5 inch barrel.", "weight": "3062 g", "volume": "2386 ml", + "longest_side": "1016 mm", "looks_like": "mossberg_500", "price": 53800, "price_postapoc": 2250, @@ -268,6 +277,7 @@ "description": "The Mossberg 590A1 is a military and police oriented version of the Mossberg 500. It features a heavier barrel, a bayonet lug, and a different magazine tube for easier cleaning and maintenance.", "weight": "3289 g", "volume": "2548 ml", + "longest_side": "1043 mm", "looks_like": "mossberg_500", "default_mods": [ "sights_mount", "underbarrel_mount" ], "price": 70000, @@ -283,6 +293,7 @@ "description": "This semi-automatic offering from Mossberg features a recoil reducing gas system, rifle style sights and a factory-installed picatinny rail. Affordable pricing and decent ergonomics make this a popular entry-level 3-gun shotgun.", "weight": "3402 g", "volume": "2623 ml", + "longest_side": "994 mm", "looks_like": "m1014", "price": 60000, "price_postapoc": 2250, @@ -342,6 +353,7 @@ "description": "A home-made shotgun. It is simply a pipe attached to a stock, with a hammer to strike the single round it holds.", "weight": "2267 g", "volume": "2250 ml", + "longest_side": "95 cm", "price": 20000, "price_postapoc": 500, "to_hit": -1, @@ -376,6 +388,7 @@ "description": "With over 10 million made, the Remington 870 is one of the most popular shotguns on the market, and finds use with hunters and law enforcement agencies alike thanks to its high accuracy and muzzle velocity. This one is a 28 inch barreled model for hunting fowl and game.", "weight": "3400 g", "volume": "2487 ml", + "longest_side": "1234 mm", "price": 58700, "price_postapoc": 2500, "to_hit": -1, @@ -396,6 +409,7 @@ "description": "This Remington 870 Modular Combat System shotgun is currently setup for breaching operations, with a 10 inch barrel and no stock. It is small enough to carry as a secondary weapon, specifically to pop open any pesky doors you might come across. The grip's design makes for controllable yet unpleasant recoil, and the barrel lacks any sights.", "weight": "2812 g", "volume": "1284 ml", + "longest_side": "53 cm", "looks_like": "remington_870", "ranged_damage": { "damage_type": "bullet", "amount": -5 }, "skill": "pistol", @@ -431,6 +445,7 @@ "description": "With over 10 million made, the Remington 870 is one of the most popular shotguns on the market, and finds use with hunters and law enforcement agencies alike thanks to its high accuracy and muzzle velocity. This one is an 18.5 inch barreled defensive model.", "weight": "3402 g", "volume": "2365 ml", + "longest_side": "983 mm", "looks_like": "remington_870", "ranged_damage": { "damage_type": "bullet", "amount": 1 }, "dispersion": 345, @@ -447,6 +462,7 @@ "description": "This semi-automatic shotgun features a self compensating gas system that will feed a wide array of shells while also reducing recoil. Introduced in 1963, it is favored by law enforcement, hunters and competition shooters, and has been the best-selling autoloading shotgun in U.S. history. This is the nickel finished, teflon coated competition model, with a full length magazine tube and 30 inch barrel.", "weight": "3742 g", "volume": "2626 ml", + "longest_side": "1288 mm", "price_postapoc": 3250, "looks_like": "remington_870", "price": 130000, @@ -473,7 +489,7 @@ [ "underbarrel mount", 1 ] ], "extend": { "flags": [ "RELOAD_ONE" ] }, - "pocket_data": [ { "pocket_type": "MAGAZINE", "rigid": true, "ammo_restriction": { "shot": 10 } } ] + "pocket_data": [ { "pocket_type": "MAGAZINE", "rigid": true, "ammo_restriction": { "shot": 5 } } ] }, { "id": "revolver_shotgun", @@ -484,6 +500,7 @@ "description": "A shotgun modified to use a revolver cylinder mechanism, it can hold 6 cartridges.", "weight": "5443 g", "volume": "3 L", + "longest_side": "60 cm", "price": 75000, "price_postapoc": 1250, "to_hit": -1, @@ -514,8 +531,9 @@ "type": "GUN", "name": { "str": "Saiga-12" }, "description": "The Saiga-12 is a semi-automatic shotgun designed on the same Kalashnikov pattern as the AK47 rifle. It reloads with a magazine, rather than one shell at a time like most shotguns. It is one of the last designs of Mikhail Kalashnikov, and was popular in open division shotgun competitions prior to its ban from import via executive order.", - "weight": "3600 g", - "volume": "2750 ml", + "weight": "3225 g", + "volume": "3064 ml", + "longest_side": "1068 mm", "price": 189000, "price_postapoc": 4500, "to_hit": -1, @@ -583,6 +601,7 @@ "description": "An old shotgun, possibly antique. It is little more than a barrel, a wood stock, and a hammer to strike the cartridge. Its simple design keeps it both light and accurate.", "weight": "2494 g", "volume": "2044 ml", + "longest_side": "95 cm", "price": 10000, "price_postapoc": 1500, "to_hit": -1, @@ -614,8 +633,9 @@ "type": "GUN", "name": { "str": "Cobray Streetsweeper" }, "description": "Less shotgun and more comically oversized revolver, the Cobray Streetsweeper sold poorly before it was deemed a destructive device. The cylinder is driven by a clockspring, cannot be indexed by hand, and must be ejected with 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.", - "weight": "419 g", + "weight": "4200 g", "volume": "3193 ml", + "longest_side": "592 mm", "looks_like": "revolver_shotgun", "price": 120000, "price_postapoc": 3500, @@ -654,6 +674,7 @@ "description": "Mean and intimidating looking, the SPAS 12 has the dubious honor of being declared a destructive device and being banned from import by name, adding to its already considerable appeal. It is a combination pump-action and semi-automatic firearm, with an arm stabilizing hook for one handed shooting.", "weight": "4400 g", "volume": "2768 ml", + "longest_side": "835 mm", "looks_like": "m1014", "price": 180000, "price_postapoc": 4750, @@ -687,8 +708,9 @@ "type": "GUN", "name": { "str": "Tavor TS12" }, "description": "This is a triple tube magazine fed, gas-operated bullpup shotgun of Israeli Weapon Industries make. It is capable of loading 15 shells, all in a relatively small package. Like many other modern IWI designs, this looks more like a sci-fi prop gun than a firearm. An integral top rail is provided for mounting sights.", - "weight": "4200 g", + "weight": "3429 g", "volume": "4040 ml", + "longest_side": "725 mm", "looks_like": "ksg", "price": 180000, "price_postapoc": 5500, @@ -724,6 +746,7 @@ "description": "The USAS 12 looks like a boxy upsized caricature of the M16A2. Like its Auto Assault-12 predecessor, it is a select-fire shotgun fed from large box or drum magazines. The in-line recoil system and sheer weight make for pleasant shooting.", "weight": "5450 g", "volume": "4695 ml", + "longest_side": "962 mm", "looks_like": "saiga_12", "price": 300000, "price_postapoc": 5000, @@ -764,6 +787,7 @@ "description": "One of the first commercially successful repeating shotguns, the Winchester 1887 was specifically made lever-action at Winchester's request. Though later overshadowed in success by pump designs, the 1887 remains popular today. This one has a very short barrel, no stock, and would pair nicely with a motorcycle jacket and a Harley Davidson.", "weight": "2994 g", "volume": "1127 ml", + "longest_side": "708 mm", "looks_like": "browning_blr", "price": 100800, "price_postapoc": 2250, @@ -799,6 +823,7 @@ "description": "The Winchester 1897 was one of the first commercially successful pump action shotguns. In its 'trench' configuraton it has become a heavily romanticized American icon of World War 1. With its barrel shroud, bayonet lug and 17 inch bayonet, this shotgun is undeniably fearsome in appearance. There aren't any more trenches to clear, so the next zombie infested town will have to suffice.", "weight": "3629 g", "volume": "2564 ml", + "longest_side": "989 mm", "looks_like": "remington_870_express", "price": 850000, "price_postapoc": 2500, @@ -827,13 +852,14 @@ "pocket_data": [ { "pocket_type": "MAGAZINE", "rigid": true, "ammo_restriction": { "shot": 6 } } ] }, { - "id": "four_winds_shotgun", + "id": "slamfire_shotgun", "copy-from": "shotgun_base", "type": "GUN", - "name": { "str": "four winds shotgun" }, + "name": { "str": "slam-fire pipe 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", + "longest_side": "65 cm", "looks_like": "pipe", "price": 8500, "price_postapoc": 1000, @@ -848,5 +874,19 @@ "valid_mod_locations": [ [ "sling", 1 ], [ "sights mount", 1 ], [ "underbarrel mount", 1 ] ], "flags": [ "RELOAD_EJECT" ], "pocket_data": [ { "pocket_type": "MAGAZINE", "rigid": true, "ammo_restriction": { "shot": 1 } } ] + }, + { + "id": "slamfire_shotgun_d", + "copy-from": "slamfire_shotgun", + "type": "GUN", + "name": { "str": "double slam-fire pipe shotgun" }, + "description": "A crude shotgun, composed of four thick steel pipes, two end caps and two nails. The lack of sights make this weapon only useful at point-blank range.", + "weight": "7260 g", + "volume": "4530 ml", + "clip_size": 2, + "reload": 900, + "modes": [ [ "DEFAULT", "double", 2 ] ], + "//": "no provision for separate triggers", + "pocket_data": [ { "pocket_type": "MAGAZINE", "rigid": true, "ammo_restriction": { "shot": 2 } } ] } ] diff --git a/data/json/items/melee/spears_and_polearms.json b/data/json/items/melee/spears_and_polearms.json index ba1c803d2a06c..578423d3415b9 100644 --- a/data/json/items/melee/spears_and_polearms.json +++ b/data/json/items/melee/spears_and_polearms.json @@ -287,6 +287,7 @@ "description": "This is a versatile polearm with an axe blade, a spike, and other fun things attached to a long sturdy stick.", "price": 50000, "material": [ "wood", "steel" ], + "ascii_picture": "halberd", "flags": [ "DURABLE_MELEE", "REACH_ATTACK", "NONCONDUCTIVE", "POLEARM", "SHEATH_SPEAR", "ALWAYS_TWOHAND" ], "techniques": [ "WBLOCK_1", "WIDE", "SWEEP" ], "weight": "3175 g", @@ -306,6 +307,7 @@ "description": "This is a dull, cheaply made replica of a polearm with an axe blade, a spike, and other fun things attached to a thick pole.", "price": 5000, "material": [ "wood", "aluminum" ], + "ascii_picture": "halberd", "flags": [ "REACH_ATTACK", "NONCONDUCTIVE", "POLEARM", "SHEATH_SPEAR", "ALWAYS_TWOHAND", "FRAGILE_MELEE" ], "techniques": [ "WBLOCK_1", "SWEEP" ], "weight": "1644 g", diff --git a/data/json/items/melee/swords_and_blades.json b/data/json/items/melee/swords_and_blades.json index b522cba17549d..ddfeaab699e21 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 ] ] }, @@ -1713,6 +1713,7 @@ "price": 12000, "price_postapoc": 1250, "material": [ "wood", "iron" ], + "ascii_picture": "lajatang", "techniques": [ "WBLOCK_1", "SPIN" ], "flags": [ "DURABLE_MELEE", "NONCONDUCTIVE", "ALWAYS_TWOHAND" ], "weight": "2500 g", diff --git a/data/json/items/migration.json b/data/json/items/migration.json index a8c09f9122da4..697712907fc35 100644 --- a/data/json/items/migration.json +++ b/data/json/items/migration.json @@ -1235,8 +1235,8 @@ "replace": "carton_sealed" }, { - "id": "slam_shotgun", + "id": "four_winds_shotgun", "type": "MIGRATION", - "replace": "four_winds_shotgun" + "replace": "slamfire_shotgun" } ] diff --git a/data/json/items/tool/electronics.json b/data/json/items/tool/electronics.json index e687cdd3b0fde..7873180518ae3 100644 --- a/data/json/items/tool/electronics.json +++ b/data/json/items/tool/electronics.json @@ -223,6 +223,7 @@ "to_hit": 1, "bashing": 5, "material": [ "plastic", "aluminum" ], + "ascii_picture": "electrohack", "symbol": ",", "color": "green", "ammo": [ "battery" ], @@ -385,6 +386,7 @@ "price": 3000, "price_postapoc": 100, "material": [ "aluminum", "plastic" ], + "ascii_picture": "mp3", "symbol": ";", "color": "dark_gray", "ammo": [ "battery" ], diff --git a/data/json/items/tool/explosives.json b/data/json/items/tool/explosives.json index d2db890e53673..40fb855d9c7b6 100644 --- a/data/json/items/tool/explosives.json +++ b/data/json/items/tool/explosives.json @@ -673,7 +673,7 @@ "type": "TOOL", "category": "weapons", "name": { "str": "EMP grenade" }, - "description": "This is a grenade that generates an electromagnetic pulse with a low-inductance capacitor bank discharged into a single-loop antenna. Use this item to pull the pin and light the fuse, turning it into an active EMP grenade. You will then have three turns before it detonates, creating an EMP field that damages robots and drains bionic energy.", + "description": "This is a grenade that generates an electromagnetic pulse with a low-inductance capacitor bank discharged into a single-loop antenna. It also produces a mild electric shock cloud. Use this item to pull the pin and light the fuse, turning it into an active EMP grenade. You will then have three turns before it detonates, creating an EMP field that damages robots and drains bionic energy.", "weight": "400 g", "volume": "250 ml", "price": 6000, @@ -700,7 +700,7 @@ "type": "TOOL", "category": "weapons", "name": { "str": "active EMP grenade" }, - "description": "This EMP grenade is active, and will shortly detonate, creating a large EMP field that damages robots and drains bionic energy. You may not want to be holding it much longer.", + "description": "This EMP grenade is active, and will shortly detonate, creating a large EMP field that damages robots and drains bionic energy as well as a mild electric shock cloud. You may not want to be holding it much longer.", "weight": "400 g", "volume": "250 ml", "price": 0, @@ -715,8 +715,10 @@ "turns_per_charge": 1, "use_action": { "type": "explosion", - "draw_explosion_radius": 4, - "draw_explosion_color": "light_blue", + "fields_type": "fd_electricity", + "fields_radius": 2, + "fields_min_intensity": 3, + "fields_max_intensity": 3, "emp_blast_radius": 4, "sound_volume": 0, "sound_msg": "Tick.", @@ -1104,7 +1106,7 @@ "max_charges": 1, "turns_per_charge": 1, "use_action": [ "MOLOTOV_LIT" ], - "flags": [ "LIGHT_15", "TRADER_AVOID", "NPC_THROW_NOW", "NO_REPAIR", "WATER_EXTINGUISH", "ACT_ON_RANGED_HIT" ] + "flags": [ "LIGHT_15", "TRADER_AVOID", "NPC_THROW_NOW", "NO_REPAIR", "WATER_EXTINGUISH" ] }, { "id": "bootleg_pipebomb", diff --git a/data/json/items/tool/fire.json b/data/json/items/tool/fire.json index 142937578c0f3..2456464fbbc1d 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", "rigid": true, "ammo_restriction": { "butane": 100 }, "watertight": true } ], "use_action": { "type": "firestarter", "moves": 50 }, "flags": [ "FIRESTARTER", "NO_UNLOAD" ] }, @@ -139,7 +139,7 @@ "initial_charges": 20, "max_charges": 20, "charges_per_use": 1, - "pocket_data": [ { "pocket_type": "MAGAZINE", "holster": true, "ammo_restriction": { "match": 20 } } ], + "pocket_data": [ { "pocket_type": "MAGAZINE", "rigid": true, "holster": true, "ammo_restriction": { "match": 20 } } ], "use_action": { "type": "firestarter", "moves": 40, "moves_slow": 1000 }, "flags": [ "FIRESTARTER", "NO_RELOAD", "NO_UNLOAD" ] }, 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/data/json/items/tool/lighting.json b/data/json/items/tool/lighting.json index 3af4420f42209..0f3643b35f684 100644 --- a/data/json/items/tool/lighting.json +++ b/data/json/items/tool/lighting.json @@ -199,8 +199,8 @@ "material": [ "plastic", "aluminum" ], "symbol": ";", "color": "blue", - "weight": "400 g", - "volume": "500 ml", + "weight": "200 g", + "volume": "200 ml", "price": 500, "price_postapoc": 100, "charges_per_use": 1, diff --git a/data/json/items/tool/med.json b/data/json/items/tool/med.json index e0415752eb74d..fdde5da93823b 100644 --- a/data/json/items/tool/med.json +++ b/data/json/items/tool/med.json @@ -158,6 +158,7 @@ "price_postapoc": 10, "to_hit": -3, "material": [ "plastic" ], + "ascii_picture": "thermometer", "symbol": ";", "color": "red", "use_action": [ "WEATHER_TOOL" ], @@ -208,7 +209,7 @@ "watertight": true, "rigid": true, "max_contains_volume": "250 ml", - "max_contains_weight": "50 g" + "max_contains_weight": "262 g" } ] }, diff --git a/data/json/items/tool/misc.json b/data/json/items/tool/misc.json index dc4ce3b901890..ad67371b24191 100644 --- a/data/json/items/tool/misc.json +++ b/data/json/items/tool/misc.json @@ -808,6 +808,7 @@ "bashing": 6, "cutting": 4, "material": [ "plastic", "aluminum" ], + "ascii_picture": "umbrella", "symbol": "/", "color": "magenta", "techniques": [ "WBLOCK_1" ], diff --git a/data/json/items/tool/toileteries.json b/data/json/items/tool/toileteries.json index d6a91037f1cf2..590453603fa38 100644 --- a/data/json/items/tool/toileteries.json +++ b/data/json/items/tool/toileteries.json @@ -182,8 +182,9 @@ "type": "TOOL", "name": { "str": "washboard" }, "description": "This is a wooden washboard. You can use it to wash filthy clothing if it's supplied with cleansing agent.", - "weight": "90 g", - "volume": "250 ml", + "weight": "786 g", + "volume": "1966 ml", + "longest_side": "40 cm", "price": 1000, "price_postapoc": 50, "to_hit": -1, @@ -198,8 +199,9 @@ "type": "TOOL", "name": { "str": "washing kit" }, "description": "A combination kit of a washboard and a scrubbing tool. Everything you need to clean items after the apocalypse.", - "weight": "100 g", - "volume": "275 ml", + "weight": "866 g", + "volume": "2246 ml", + "longest_side": "40 cm", "price": 0, "price_postapoc": 50, "material": [ "wood", "plastic" ], diff --git a/data/json/items/tool/traps.json b/data/json/items/tool/traps.json index a26f41f8db1d1..e44491720afa0 100644 --- a/data/json/items/tool/traps.json +++ b/data/json/items/tool/traps.json @@ -210,16 +210,16 @@ "id": "shotgun_trap", "type": "TOOL", "name": { "str": "shotgun trap" }, - "description": "This is a simple tripwire is attached to the trigger of a loaded double-barreled shotgun. When pulled, the shotgun fires. Two shells are loaded; the first time the trigger is pulled, one or both shells may be discharged.", + "description": "This is a simple tripwire is attached to the trigger of a loaded double slamfire shotgun. When pulled, the shotgun fires. Two shells are loaded; the first time the trigger is pulled, one or both shells may be discharged.", "weight": "2922 g", "volume": "1750 ml", "price": 25000, "price_postapoc": 1000, "to_hit": -2, "bashing": 12, - "material": [ "steel", "wood" ], + "material": [ "steel" ], "symbol": ";", - "color": "brown", + "color": "dark_gray", "use_action": { "type": "place_trap", "trap": "tr_shotgun_2", 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", diff --git a/data/json/items/tool_armor.json b/data/json/items/tool_armor.json index a0a718705bb39..4b6dbe1849f63 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" ] } @@ -2871,7 +2872,7 @@ "id": "solarpack", "type": "TOOL_ARMOR", "name": { "str": "solar backpack (folded)", "str_pl": "solar backpacks (folded)" }, - "description": "Personal portable charging system consisting of an array of solar panels neatly folded in a form of a large backpack. It can be worn as one, and has an integrated cable to plug it into a cable charger system.", + "description": "Personal portable charging system consisting of an array of solar panels neatly folded in a form of a large backpack. It can be worn as one, and has an integrated cable to plug it into a cable charger system CBM.", "weight": "7500 g", "volume": "5 L", "price": 500000, @@ -2893,7 +2894,7 @@ "type": "TOOL_ARMOR", "repairs_like": "solarpack", "name": { "str": "solar backpack (unfolded)", "str_pl": "solar backpacks (unfolded)" }, - "description": "Unfolded array of portable solar panels ready to push some power into an active cable charger system.", + "description": "Unfolded array of portable solar panels ready to push some power into an active cable charger system CBM.", "weight": "7500 g", "volume": "15 L", "price": 500000, 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/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/robofachq_static.json b/data/json/mapgen/robofachq_static.json index 662312a1303ad..9a80c7b2bdad6 100644 --- a/data/json/mapgen/robofachq_static.json +++ b/data/json/mapgen/robofachq_static.json @@ -121,13 +121,13 @@ "fill_ter": "t_thconc_floor", "rows": [ "############################################| |############################################", - "########|||||||||||||||||||||||||#|||||||||||||22|||############################################", + "########|||||||||||||||||||||||||#|||||||||||||55|||############################################", "########|k ht|A h ^|#|lll|lllll| YY |############################################", "########|i k k htth ht| Cd6ddC |#|bYb|bYbYb|T YY T|########||||||||############################", "########|k o k htth ht| CCCCCC |#|b b|b b b|| YY ||######||=A6666A=||##########################", "########| W ^|^ h ^|#|l l|l l l| YY |######|,=Y Y=,|##########################", "########|FFFF| htth ?||||[[||||||l l|l l l|T YY T|######|,= htth =,|##########################", - "########|||||| htth ?||^^ ^^|| |||22||||||||||,= htth =,|##########################", + "########|||||| htth ?||^^ ^^|| |||55||||||||||,= htth =,|##########################", "########|ffff| 2 YY 2Y bbb bbbb| |L | i|,= htth =,|##########################", "########| |^??t t??^| ,, ||||||||||||| |LY2 Y;|,= htth =,|##########################", "###||||||hd ||||||||||| ,, 2 r r r| d( Y b|||||2||,= htth =,|##########################", @@ -211,7 +211,7 @@ { "item": "lab_bookshelves", "chance": 55, "repeat": [ 1, 3 ] } ] }, - "monster": { "T": { "monster": "mon_turret_rifle" } }, + "monster": { "T": { "monster": "mon_robofac_laserturret_mk1" } }, "npcs": { "G": { "class": "hub_security" }, "Q": { "class": "hub_security_head" } } } }, @@ -450,19 +450,19 @@ "##############################################||5555||tt tt|####################################", "#####################################||||||||||^ ^| h h|####################################", "#####################################|rrrrrrrr|^ ^| |####################################", - "#####################################|r r|| ||||2|||####################################", + "#####################################|r r||T T||||2|||####################################", "#####################################|r YY 2 (t r|####################################", "#####################################|||||||||| YY (thY r|####################################", - "#####################################|bbbbbbbb| YY (t Y r|####################################", + "#####################################|bbbbbbbb| YY (tGY r|####################################", "#####################################|Y Y2 ( r|####################################", "#####################################| llllll ||2222||||2|||####################################", - "#####################################|2||||||2|^ Y^|####################################", + "#####################################|2||||||2|^ G Y^|####################################", "#####################################| ~| |~ |^ Y^|####################################", "#####################################| i| |i |^ Y^|####################################", "#####################################||||||||||| ||||||||####################################", "#####################################|lll 2 ^|##########################################", "#####################################|^ ( YY ^|##########################################", - "#####################################| d ( YY ^|##########################################", + "#####################################| dG ( YY ^|##########################################", "#####################################|^hdh ( ^|##########################################", "#####################################|||||||| ||||##########################################" ], @@ -472,7 +472,9 @@ "l": [ { "item": "NC_ROBOFAC_FIELD_RESEARCHER_worn", "chance": 50 }, { "item": "NC_ROBOFAC_SCIENTIST_worn", "chance": 50 } ], "i": { "item": "cleaning", "chance": 50 }, "B": [ { "item": "textbooks", "chance": 50 }, { "item": "manuals", "chance": 50 } ] - } + }, + "monster": { "T": { "monster": "mon_robofac_laserturret_mk1" } }, + "npcs": { "G": { "class": "hub_security" } } } }, { 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/monster_factions.json b/data/json/monster_factions.json index 510b9501d9a28..5614331a49660 100644 --- a/data/json/monster_factions.json +++ b/data/json/monster_factions.json @@ -54,6 +54,12 @@ "name": "jabberwock", "by_mood": [ "jabberwock" ] }, + { + "type": "MONSTER_FACTION", + "name": "robofac", + "neutral": [ "cop_bot", "defense_bot", "utility_bot", "animal" ], + "hate": [ "zombie", "fungus", "cult", "triffid", "nether" ] + }, { "type": "MONSTER_FACTION", "name": "bot", diff --git a/data/json/monstergroups/wilderness.json b/data/json/monstergroups/wilderness.json index 11f4dcfd889a1..ae61db8f209a6 100644 --- a/data/json/monstergroups/wilderness.json +++ b/data/json/monstergroups/wilderness.json @@ -725,6 +725,14 @@ { "monster": "mon_zolf", "freq": 2, "cost_multiplier": 2, "starts": 168, "pack_size": [ 1, 4 ] }, { "monster": "mon_zolf", "freq": 2, "cost_multiplier": 2, "starts": 672, "pack_size": [ 1, 4 ] }, { "monster": "mon_zolf", "freq": 2, "cost_multiplier": 2, "starts": 2160, "pack_size": [ 1, 4 ] }, + { + "monster": "mon_zombie_horse", + "freq": 2, + "cost_multiplier": 15, + "ends": 2000, + "conditions": [ "DAWN", "SPRING", "SUMMER", "AUTUMN" ], + "pack_size": [ 1, 4 ] + }, { "monster": "mon_groundhog", "freq": 30, diff --git a/data/json/monstergroups/zanimal_upgrades.json b/data/json/monstergroups/zanimal_upgrades.json index 8ed879c184ece..63728e8f84c79 100644 --- a/data/json/monstergroups/zanimal_upgrades.json +++ b/data/json/monstergroups/zanimal_upgrades.json @@ -1,4 +1,24 @@ [ + { + "type": "monstergroup", + "name": "GROUP_ZOMBIE_DOG_UPGRADE", + "default": "mon_dog_skeleton", + "//": "No bionics or fungal", + "monsters": [ + { "monster": "mon_dog_skeleton", "freq": 45, "cost_multiplier": 5 }, + { "monster": "mon_dog_zombie_brute", "freq": 45, "cost_multiplier": 2 } + ] + }, + { + "type": "monstergroup", + "name": "GROUP_ZOLF_UPGRADE", + "default": "mon_wolf_skeleton", + "//": "No bionics or fungal", + "monsters": [ + { "monster": "mon_wolf_skeleton", "freq": 45, "cost_multiplier": 5 }, + { "monster": "mon_dog_zombie_brute", "freq": 45, "cost_multiplier": 2 } + ] + }, { "type": "monstergroup", "name": "GROUP_ZOMBEAR_UPGRADE", diff --git a/data/json/monstergroups/zombies.json b/data/json/monstergroups/zombies.json index 251c75fcd26c7..a4b77da6f149e 100644 --- a/data/json/monstergroups/zombies.json +++ b/data/json/monstergroups/zombies.json @@ -10,6 +10,9 @@ { "monster": "mon_zombie_fat", "freq": 50, "cost_multiplier": 1, "pack_size": [ 2, 3 ] }, { "monster": "mon_zombie_rot", "freq": 50, "cost_multiplier": 1, "pack_size": [ 2, 3 ] }, { "monster": "mon_zombie_runner", "freq": 20, "cost_multiplier": 2, "pack_size": [ 1, 2 ] }, + { "monster": "mon_feral_human_pipe", "freq": 40, "cost_multiplier": 1, "pack_size": [ 2, 3 ] }, + { "monster": "mon_feral_human_crowbar", "freq": 40, "cost_multiplier": 1, "pack_size": [ 2, 3 ] }, + { "monster": "mon_feral_human_axe", "freq": 20, "cost_multiplier": 2, "pack_size": [ 1, 2 ] }, { "monster": "mon_zombie_crawler", "freq": 25, "cost_multiplier": 1 }, { "monster": "mon_zombie_brainless", "freq": 25, "cost_multiplier": 1 }, { "monster": "mon_zombie_dog", "freq": 25, "cost_multiplier": 1, "pack_size": [ 2, 3 ] } @@ -58,6 +61,9 @@ { "monster": "mon_zombie_fat", "freq": 50, "cost_multiplier": 1, "pack_size": [ 2, 3 ] }, { "monster": "mon_zombie_rot", "freq": 50, "cost_multiplier": 1, "pack_size": [ 2, 3 ] }, { "monster": "mon_zombie_runner", "freq": 20, "cost_multiplier": 2, "pack_size": [ 1, 2 ] }, + { "monster": "mon_feral_human_pipe", "freq": 20, "cost_multiplier": 1, "pack_size": [ 2, 3 ] }, + { "monster": "mon_feral_human_crowbar", "freq": 20, "cost_multiplier": 1, "pack_size": [ 2, 3 ] }, + { "monster": "mon_feral_human_axe", "freq": 10, "cost_multiplier": 2, "pack_size": [ 1, 2 ] }, { "monster": "mon_zombie_crawler", "freq": 25, "cost_multiplier": 1 }, { "monster": "mon_zombie_brainless", "freq": 25, "cost_multiplier": 1 } ] @@ -105,6 +111,9 @@ { "monster": "mon_beekeeper", "freq": 1, "cost_multiplier": 5 }, { "monster": "mon_zombie_technician", "freq": 1, "cost_multiplier": 12 }, { "monster": "mon_zombie_runner", "freq": 20, "cost_multiplier": 5, "pack_size": [ 1, 4 ] }, + { "monster": "mon_feral_human_pipe", "freq": 4, "cost_multiplier": 1, "pack_size": [ 2, 3 ] }, + { "monster": "mon_feral_human_crowbar", "freq": 4, "cost_multiplier": 1, "pack_size": [ 2, 3 ] }, + { "monster": "mon_feral_human_axe", "freq": 2, "cost_multiplier": 2, "pack_size": [ 1, 2 ] }, { "monster": "mon_zombie_brainless", "freq": 65, "cost_multiplier": 1 } ] }, @@ -119,6 +128,9 @@ { "monster": "mon_zombie_rot", "freq": 60, "cost_multiplier": 0 }, { "monster": "mon_zombie_dog", "freq": 50, "cost_multiplier": 0 }, { "monster": "mon_zombie_crawler", "freq": 30, "cost_multiplier": 0 }, + { "monster": "mon_feral_human_pipe", "freq": 40, "cost_multiplier": 0 }, + { "monster": "mon_feral_human_crowbar", "freq": 40, "cost_multiplier": 0 }, + { "monster": "mon_feral_human_axe", "freq": 20, "cost_multiplier": 0 }, { "monster": "mon_zombie_brainless", "freq": 30, "cost_multiplier": 0 } ] }, @@ -475,6 +487,9 @@ { "monster": "mon_zombie", "freq": 1, "cost_multiplier": 7, "pack_size": [ 5, 10 ] }, { "monster": "mon_zombie", "freq": 1, "cost_multiplier": 13, "pack_size": [ 15, 20 ] }, { "monster": "mon_zombie", "freq": 1, "cost_multiplier": 20, "pack_size": [ 25, 30 ] }, + { "monster": "mon_feral_human_pipe", "freq": 4, "cost_multiplier": 1, "pack_size": [ 5, 9 ] }, + { "monster": "mon_feral_human_crowbar", "freq": 4, "cost_multiplier": 1, "pack_size": [ 4, 7 ] }, + { "monster": "mon_feral_human_axe", "freq": 2, "cost_multiplier": 2, "pack_size": [ 2, 4 ] }, { "monster": "mon_zombie", "freq": 75, "cost_multiplier": 2 }, { "monster": "mon_zombie_fat", "freq": 75, "cost_multiplier": 2 }, { "monster": "mon_zombie_fat", "freq": 3, "cost_multiplier": 7, "pack_size": [ 3, 5 ] }, @@ -561,6 +576,16 @@ { "monster": "mon_zombie", "freq": 40, "cost_multiplier": 1 } ] }, + { + "name": "FERAL_HUMANS", + "type": "monstergroup", + "default": "mon_feral_human_pipe", + "monsters": [ + { "monster": "mon_feral_human_pipe", "freq": 100, "cost_multiplier": 1, "pack_size": [ 3, 8 ] }, + { "monster": "mon_feral_human_crowbar", "freq": 40, "cost_multiplier": 1, "pack_size": [ 2, 6 ] }, + { "monster": "mon_feral_human_axe", "freq": 20, "cost_multiplier": 2, "pack_size": [ 1, 4 ] } + ] + }, { "type": "monstergroup", "name": "DUMP_ZOMBIES", diff --git a/data/json/monsters/feral_humans.json b/data/json/monsters/feral_humans.json new file mode 100644 index 0000000000000..a1a5f552ba4fe --- /dev/null +++ b/data/json/monsters/feral_humans.json @@ -0,0 +1,76 @@ +[ + { + "id": "mon_feral_human_pipe", + "type": "MONSTER", + "name": { "str": "feral human" }, + "description": "Pupils dilated and what remains to be seen of the iris and sclera are bloodshot. It still breathes but the zombies treat it like one of them.", + "default_faction": "mon_chud", + "looks_like": "chud", + "bodytype": "human", + "species": [ "HUMAN" ], + "volume": "62500 ml", + "weight": "81500 g", + "hp": 80, + "speed": 100, + "material": [ "flesh" ], + "symbol": "@", + "color": "magenta", + "aggression": 30, + "morale": 100, + "melee_skill": 2, + "melee_dice": 1, + "melee_dice_sides": 3, + "melee_cut": 0, + "dodge": 1, + "harvest": "human", + "vision_day": 30, + "vision_night": 3, + "starting_ammo": { "rock": 6 }, + "special_attacks": [ + { + "type": "gun", + "cooldown": 5, + "move_cost": 150, + "gun_type": "feral_human_thrown_rock", + "fake_skills": [ [ "gun", 2 ], [ "throw", 2 ] ], + "fake_dex": 6, + "fake_per": 6, + "require_targeting_player": false, + "ranges": [ [ 2, 5, "DEFAULT" ] ], + "description": "They throw a loose brick at you!" + } + ], + "death_drops": "default_zombie_clothes", + "death_function": [ "NORMAL" ], + "anger_triggers": [ "FRIEND_DIED", "FRIEND_ATTACKED", "HURT" ], + "flags": [ "SEES", "HEARS", "SMELLS", "WARM", "BASHES", "GROUP_BASH", "BLEED", "HUMAN", "CAN_OPEN_DOORS", "PATH_AVOID_DANGER_2" ] + }, + { + "id": "mon_feral_human_crowbar", + "type": "MONSTER", + "copy-from": "mon_feral_human_pipe", + "melee_dice": 2, + "melee_dice_sides": 6, + "special_attacks": [ + { + "type": "gun", + "cooldown": 5, + "move_cost": 150, + "gun_type": "feral_human_thrown_rock", + "fake_skills": [ [ "gun", 2 ], [ "thrown", 2 ] ], + "fake_dex": 6, + "fake_per": 6, + "require_targeting_player": false, + "ranges": [ [ 2, 5, "DEFAULT" ] ], + "description": "They throw a loose brick at you!" + } + ] + }, + { + "id": "mon_feral_human_axe", + "type": "MONSTER", + "copy-from": "mon_feral_human_pipe", + "melee_dice": 3, + "melee_dice_sides": 8 + } +] diff --git a/data/json/monsters/mammal.json b/data/json/monsters/mammal.json index 520375c618e54..6a97daeaa102c 100644 --- a/data/json/monsters/mammal.json +++ b/data/json/monsters/mammal.json @@ -1724,6 +1724,7 @@ "harvest": "mammal_large_leather", "biosignature": { "biosig_item": "feces_manure", "biosig_timer": 2 }, "special_attacks": [ [ "EAT_CROP", 60 ] ], + "zombify_into": "mon_zombie_horse", "flags": [ "SEES", "HEARS", diff --git a/data/json/monsters/robofac_robots.json b/data/json/monsters/robofac_robots.json new file mode 100644 index 0000000000000..b369d63535236 --- /dev/null +++ b/data/json/monsters/robofac_robots.json @@ -0,0 +1,36 @@ +[ + { + "id": "mon_robofac_laserturret_mk1", + "type": "MONSTER", + "name": { "str": "prototype laser turret" }, + "description": "This appears to be a very experimental automated tower. Plating-less and seemingly half-built, it's little more than an oversized laser emitter and a camera, both welded to a swiveling platform.", + "default_faction": "robofac", + "species": [ "ROBOT" ], + "volume": "30000 ml", + "weight": "40750 g", + "hp": 30, + "speed": 100, + "material": [ "steel" ], + "symbol": "2", + "color": "white", + "aggression": 100, + "morale": 100, + "armor_bash": 2, + "armor_cut": 5, + "armor_bullet": 2, + "vision_day": 50, + "special_attacks": [ + { + "type": "gun", + "cooldown": 1, + "gun_type": "v29", + "fake_skills": [ [ "gun", 4 ], [ "pistol", 4 ] ], + "ranges": [ [ 0, 30, "DEFAULT" ] ] + } + ], + "special_when_hit": [ "RETURN_FIRE", 100 ], + "death_drops": { }, + "death_function": [ "BROKEN" ], + "flags": [ "SEES", "NOHEAD", "ELECTRONIC", "COLDPROOF", "IMMOBILE", "NO_BREATHE" ] + } +] diff --git a/data/json/monsters/zanimal_upgrade.json b/data/json/monsters/zanimal_upgrade.json index 2ae29edc219cd..4a1af0eb7d9ae 100644 --- a/data/json/monsters/zanimal_upgrade.json +++ b/data/json/monsters/zanimal_upgrade.json @@ -1,4 +1,132 @@ [ + { + "id": "mon_dog_skeleton", + "type": "MONSTER", + "name": { "str": "skeletal dog" }, + "description": "This once-canine has shed all of its skin, revealing a carapace of fused bones and ribs. Devoid entirely of flesh, this walking suit of bone seems to be controlled by a net of veins and sinews which pulse with glistening black goo.", + "default_faction": "zombie", + "bodytype": "dog", + "species": [ "ZOMBIE" ], + "volume": "30000 ml", + "weight": "40750 g", + "hp": 12, + "speed": 100, + "material": [ "bone" ], + "symbol": "d", + "color": "white", + "aggression": 100, + "morale": 100, + "melee_skill": 4, + "melee_dice": 1, + "melee_dice_sides": 9, + "melee_cut": 6, + "dodge": 3, + "armor_cut": 15, + "armor_bullet": 12, + "armor_stab": 30, + "armor_acid": 3, + "vision_night": 3, + "harvest": "mr_bones", + "special_attacks": [ { "type": "bite", "cooldown": 5 } ], + "upgrades": { "half_life": 15, "into": "mon_dog_skeleton_brute" }, + "death_function": [ "NORMAL" ], + "flags": [ "SEES", "HEARS", "NO_BREATHE", "HARDTOSHOOT", "REVIVES", "POISON", "FILTHY" ] + }, + { + "id": "mon_dog_zombie_brute", + "type": "MONSTER", + "name": { "str": "barghest" }, + "copy-from": "mon_dog_zombie_rot", + "description": "Huge swollen zombie dog, smeared black with slime. Its teeth are longer and its broad back is rippling with muscles and oozing 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": { "half_life": 21, "into": "mon_dog_zombie_hulk" }, + "special_attacks": [ [ "SMASH", 30 ], { "type": "bite", "cooldown": 2, "accuracy": 4, "no_infection_chance": 12 } ], + "extend": { "flags": [ "GROUP_BASH", "PUSH_VEH" ] } + }, + { + "id": "mon_dog_zombie_hulk", + "type": "MONSTER", + "name": { "str": "hulking horror" }, + "copy-from": "mon_dog_zombie_brute", + "description": "A four-legged canine body now grotesquely swollen, with arms as wide as a trash can and massive exposed teeth.", + "diff": 5, + "volume": "440 L", + "weight": "100 kg", + "color": "white_magenta", + "proportional": { "hp": 4, "speed": 1.25 }, + "relative": { "melee_skill": 1, "melee_dice": 1, "melee_cut": -2 }, + "armor_bash": 8, + "armor_cut": 12, + "armor_bullet": 10, + "upgrades": { }, + "special_attacks": [ [ "SMASH", 20 ], { "type": "bite", "cooldown": 2, "accuracy": 4, "no_infection_chance": 12 } ], + "extend": { "flags": [ "DESTROYS" ] } + }, + { + "id": "mon_dog_skeleton_brute", + "type": "MONSTER", + "copy-from": "mon_dog_skeleton", + "name": { "str": "boneplate wolf" }, + "description": "This is a four legged creature covered in fused bony plates, shaped somewhat like a dog or wolf. Joints and cracks around its body ooze with black goo.", + "volume": "46 L", + "weight": "60 kg", + "hp": 70, + "relative": { "melee_dice": -1, "melee_dice_sides": 2, "melee_cut": 6 }, + "armor_cut": 30, + "armor_bullet": 24, + "armor_bash": 12, + "armor_acid": 1, + "special_attacks": [ + [ "SMASH", 45 ], + { "id": "scratch", "damage_max_instance": [ { "damage_type": "cut", "amount": 15, "armor_multiplier": 0.6 } ] } + ], + "upgrades": { "half_life": 12, "into": "mon_skeleton_hulk" } + }, + { + "id": "mon_wolf_skeleton", + "type": "MONSTER", + "name": { "str": "skeletal wolf" }, + "description": "This once-canine has shed all of its skin, revealing a carapace of fused bones and ribs. Devoid entirely of flesh, this walking suit of bone seems to be controlled by a net of veins and sinews which pulse with glistening black goo.", + "looks_like": "mon_dog_skeleton", + "default_faction": "zombie", + "bodytype": "dog", + "species": [ "ZOMBIE" ], + "volume": "30000 ml", + "weight": "40750 g", + "hp": 18, + "speed": 100, + "material": [ "bone" ], + "symbol": "d", + "color": "white", + "aggression": 100, + "morale": 100, + "melee_skill": 4, + "melee_dice": 3, + "melee_dice_sides": 7, + "melee_cut": 8, + "dodge": 4, + "armor_cut": 15, + "armor_bullet": 12, + "armor_stab": 30, + "armor_acid": 3, + "vision_night": 3, + "harvest": "mr_bones", + "special_attacks": [ { "type": "bite", "cooldown": 5 } ], + "upgrades": { "half_life": 15, "into": "mon_dog_skeleton_brute" }, + "death_function": [ "NORMAL" ], + "flags": [ "SEES", "HEARS", "NO_BREATHE", "HARDTOSHOOT", "REVIVES", "POISON", "FILTHY" ] + }, { "id": "mon_zougar_hunter", "type": "MONSTER", diff --git a/data/json/monsters/zed-animal.json b/data/json/monsters/zed-animal.json index 5a26d8b249761..f17bab2b4ccc2 100644 --- a/data/json/monsters/zed-animal.json +++ b/data/json/monsters/zed-animal.json @@ -57,41 +57,9 @@ "harvest": "zombie", "special_attacks": [ { "type": "bite", "cooldown": 5 } ], "death_function": [ "NORMAL" ], - "upgrades": { "half_life": 28, "into": "mon_dog_skeleton" }, + "upgrades": { "half_life": 28, "into_group": "GROUP_ZOMBIE_DOG_UPGRADE" }, "flags": [ "SEES", "HEARS", "SMELLS", "STUMBLES", "WARM", "BASHES", "POISON", "NO_BREATHE", "REVIVES", "FILTHY" ] }, - { - "id": "mon_dog_skeleton", - "type": "MONSTER", - "name": { "str": "skeletal dog" }, - "description": "This once-canine has shed all of its skin, revealing a carapace of fused bones and ribs. Devoid entirely of flesh, this walking suit of bone seems to be controlled by a net of veins and sinews which pulse with glistening black goo.", - "default_faction": "zombie", - "bodytype": "dog", - "species": [ "ZOMBIE" ], - "volume": "30000 ml", - "weight": "40750 g", - "hp": 12, - "speed": 100, - "material": [ "bone" ], - "symbol": "d", - "color": "white", - "aggression": 100, - "morale": 100, - "melee_skill": 4, - "melee_dice": 1, - "melee_dice_sides": 9, - "melee_cut": 6, - "dodge": 3, - "armor_cut": 15, - "armor_bullet": 12, - "armor_stab": 30, - "armor_acid": 3, - "vision_night": 3, - "harvest": "mr_bones", - "special_attacks": [ { "type": "bite", "cooldown": 5 } ], - "death_function": [ "NORMAL" ], - "flags": [ "SEES", "HEARS", "NO_BREATHE", "HARDTOSHOOT", "REVIVES", "POISON", "FILTHY" ] - }, { "id": "mon_dog_zombie_cop", "type": "MONSTER", @@ -124,6 +92,7 @@ "special_attacks": [ { "type": "bite", "cooldown": 5 } ], "death_drops": { "subtype": "collection", "groups": [ [ "dog_cop", 40 ] ], "//": "40% chance of an item from group dog_cop" }, "death_function": [ "NORMAL" ], + "upgrades": { "half_life": 28, "into_group": "GROUP_ZOMBIE_DOG_UPGRADE" }, "flags": [ "SEES", "HEARS", "SMELLS", "STUMBLES", "WARM", "BASHES", "POISON", "NO_BREATHE", "REVIVES", "PUSH_MON", "FILTHY" ] }, { @@ -154,6 +123,7 @@ "harvest": "zombie", "special_attacks": [ { "type": "bite", "cooldown": 2, "accuracy": 4, "no_infection_chance": 12 } ], "death_function": [ "NORMAL" ], + "upgrades": { "half_life": 28, "into": "mon_dog_zombie_brute" }, "flags": [ "SEES", "HEARS", "SMELLS", "STUMBLES", "WARM", "BASHES", "POISON", "NO_BREATHE", "REVIVES", "PUSH_MON", "FILTHY" ] }, { @@ -185,6 +155,7 @@ "anger_triggers": [ "PLAYER_CLOSE", "HURT" ], "fear_triggers": [ "FIRE" ], "death_function": [ "NORMAL" ], + "upgrades": { "half_life": 28, "into_group": "GROUP_ZOLF_UPGRADE" }, "flags": [ "SEES", "HEARS", "SMELLS", "STUMBLES", "WARM", "KEENNOSE", "BASHES", "POISON", "NO_BREATHE", "REVIVES", "FILTHY" ] }, { @@ -380,6 +351,37 @@ "FILTHY" ] }, + { + "id": "mon_zombie_horse", + "type": "MONSTER", + "name": { "str": "zombie horse" }, + "description": "From the looks of this zombie horse's ghastly features, with its protruding ribs, whitish skull, and empty eyes, the exposed part of the internal organs shows a seemingly lifeless color, and the black body fluid drips slowly. The new strength makes the horse no longer dependent on its muscles, but it can still pursue the enemy quickly.", + "default_faction": "zombie", + "bodytype": "horse", + "categories": [ "WILDLIFE" ], + "species": [ "ZOMBIE" ], + "volume": "85200 ml", + "weight": "125 kg", + "hp": 70, + "speed": 215, + "material": [ "flesh" ], + "symbol": "V", + "color": "black", + "aggression": 100, + "morale": 100, + "melee_skill": 4, + "melee_dice": 3, + "melee_dice_sides": 6, + "melee_cut": 6, + "armor_bash": 5, + "armor_cut": 1, + "vision_day": 30, + "vision_night": 3, + "harvest": "zombie_leather", + "anger_triggers": [ "PLAYER_CLOSE", "PLAYER_WEAK" ], + "death_function": [ "NORMAL" ], + "flags": [ "SEES", "HEARS", "SMELLS", "BASHES", "NO_BREATHE", "REVIVES", "BONES", "FAT" ] + }, { "id": "mon_ziger", "type": "MONSTER", 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", diff --git a/data/json/monsters/zed_misc.json b/data/json/monsters/zed_misc.json index 1b2d09f287f2b..daad66ace5c9a 100644 --- a/data/json/monsters/zed_misc.json +++ b/data/json/monsters/zed_misc.json @@ -478,7 +478,7 @@ { "id": "mon_zombie_hunter", "type": "MONSTER", - "name": { "str": "feral hunter" }, + "name": { "str": "zombie hunter" }, "description": "This once-human body is barely recognizable, scrambling about on all fours, its nails and teeth both sharpened into dangerous looking spikes.", "default_faction": "zombie", "bodytype": "human", @@ -719,7 +719,7 @@ { "id": "mon_zombie_runner", "type": "MONSTER", - "name": { "str": "feral runner" }, + "name": { "str": "zombie runner" }, "description": "This recently-risen body moves quickly, darting its head back and forth and gnawing at its hands.", "default_faction": "zombie", "bodytype": "human", @@ -758,7 +758,7 @@ { "id": "mon_zombie_predator", "type": "MONSTER", - "name": { "str": "feral predator" }, + "name": { "str": "zombie predator" }, "description": "With its joints in odd places and angles, this humanoid creature prowls across the landscape with surprising speed. Its teeth and arms are sharpened into fine points, and black ooze seeps out from cuts between its muscles.", "default_faction": "zombie", "bodytype": "human", diff --git a/data/json/mutations/mutations.json b/data/json/mutations/mutations.json index ba20dc4af3b40..f98735d420e09 100644 --- a/data/json/mutations/mutations.json +++ b/data/json/mutations/mutations.json @@ -701,7 +701,8 @@ "type": "mutation", "id": "CANNIBAL", "name": { "str": "Cannibal" }, - "points": 1, + "points": 3, + "//": "Feral humans vastly increase the cannibal meat supply, therefore this trait needs to increase in cost.", "description": "For your whole life you've been forbidden from indulging in your peculiar tastes. Now the world's ended, and you'll be damned if anyone is going to tell you that you can't eat people.", "starting_trait": true, "valid": false, diff --git a/data/json/npcs/TALK_TEST.json b/data/json/npcs/TALK_TEST.json index 93cdab41ac3b3..6c23dd217571c 100644 --- a/data/json/npcs/TALK_TEST.json +++ b/data/json/npcs/TALK_TEST.json @@ -605,6 +605,31 @@ } ] }, + { + "type": "talk_topic", + "id": "TALK_TEST_VARS_TIME", + "dynamic_line": "This is a test conversation that shouldn't appear in the game.", + "responses": [ + { "text": "This is a basic test response.", "topic": "TALK_DONE" }, + { + "text": "This is a u_add_var time test response.", + "topic": "TALK_DONE", + "effect": { "u_add_var": "test", "type": "test", "context": "var_time_test", "time": true }, + "condition": { "not": { "u_has_var": "test", "type": "test", "context": "var_time_test", "time": true } } + }, + { + "text": "This is a npc_add_var time test response.", + "topic": "TALK_DONE", + "effect": { "npc_add_var": "test", "type": "test", "context": "var_time_test", "time": true }, + "condition": { "not": { "u_has_var": "test", "type": "test", "context": "var_time_test", "time": true } } + }, + { + "text": "This is a u_compare_var time test response for > 3_days.", + "topic": "TALK_DONE", + "condition": { "u_compare_time_since_var": "test", "type": "test", "context": "var_time_test", "op": ">", "time": "3 days" } + } + ] + }, { "type": "talk_topic", "id": "TALK_TEST_ADJUST_VARS", diff --git a/data/json/npcs/factions.json b/data/json/npcs/factions.json index ab58aa84435c4..a02e7fa68c8f8 100644 --- a/data/json/npcs/factions.json +++ b/data/json/npcs/factions.json @@ -54,6 +54,7 @@ "type": "faction", "id": "robofac", "name": "Hub 01", + "mon_faction": "robofac", "likes_u": -200, "respects_u": 0, "known_by_u": false, diff --git a/data/json/obsolete_terrains.json b/data/json/obsolete_terrains.json index 27f16350b7ec6..e87ea95d62cce 100644 --- a/data/json/obsolete_terrains.json +++ b/data/json/obsolete_terrains.json @@ -2,315 +2,23 @@ { "type": "obsolete_terrain", "terrains": [ - "apartments_con_tower_1", - "apartments_con_tower_1_entrance", - "apartments_mod_tower_1", - "apartments_mod_tower_1_entrance", - "bridge_ew", - "bridge_ns", - "public_works", - "public_works_entrance", - "hdwr_large_entrance", - "hdwr_large_SW", - "hdwr_large_NW", - "hdwr_large_NE", - "hdwr_large_backroom", - "hdwr_large_loadingbay", - "cemetery_4square_00", - "cemetery_4square_10", - "cemetery_4square_01", - "cemetery_4square_11", - "loffice_tower_1", - "loffice_tower_2", - "loffice_tower_3", - "loffice_tower_4", - "loffice_tower_5", - "loffice_tower_6", - "loffice_tower_7", - "loffice_tower_8", - "loffice_tower_9", - "loffice_tower_10", - "loffice_tower_11", - "loffice_tower_12", - "loffice_tower_13", - "loffice_tower_14", - "loffice_tower_15", - "loffice_tower_16", - "school_1", - "school_2", - "school_3", - "school_4", - "school_5", - "school_6", - "school_7", - "school_8", - "school_9", - "prison_1", - "prison_2", - "prison_3", - "prison_4", - "prison_5", - "prison_6", - "prison_7", - "prison_8", - "prison_9", - "prison_b_entrance", - "prison_b", - "hospital_entrance", - "hospital", - "cathedral_1_entrance", - "cathedral_1", - "cathedral_b_entrance", - "cathedral_b", - "hotel_tower_1_1", - "hotel_tower_1_2", - "hotel_tower_1_3", - "hotel_tower_1_4", - "hotel_tower_1_5", - "hotel_tower_1_6", - "hotel_tower_1_7", - "hotel_tower_1_8", - "hotel_tower_1_9", - "hotel_tower_b_1", - "hotel_tower_b_2", - "hotel_tower_b_3", - "bunker", - "farm", - "farm_field", - "subway_station", - "mansion", - "mansion_entrance", - "ranch_camp_1", - "ranch_camp_2", - "ranch_camp_3", - "ranch_camp_4", - "ranch_camp_5", - "ranch_camp_6", - "ranch_camp_7", - "ranch_camp_8", - "ranch_camp_9", - "ranch_camp_10", - "ranch_camp_11", - "ranch_camp_12", - "ranch_camp_13", - "ranch_camp_14", - "ranch_camp_15", - "ranch_camp_16", - "ranch_camp_17", - "ranch_camp_18", - "ranch_camp_19", - "ranch_camp_20", - "ranch_camp_21", - "ranch_camp_22", - "ranch_camp_23", - "ranch_camp_24", - "ranch_camp_25", - "ranch_camp_26", - "ranch_camp_27", - "ranch_camp_28", - "ranch_camp_29", - "ranch_camp_30", - "ranch_camp_31", - "ranch_camp_32", - "ranch_camp_33", - "ranch_camp_34", - "ranch_camp_35", - "ranch_camp_36", - "ranch_camp_37", - "ranch_camp_38", - "ranch_camp_39", - "ranch_camp_40", - "ranch_camp_41", - "ranch_camp_42", - "ranch_camp_43", - "ranch_camp_44", - "ranch_camp_45", - "ranch_camp_46", - "ranch_camp_47", - "ranch_camp_48", - "ranch_camp_49", - "ranch_camp_50", - "ranch_camp_51", - "ranch_camp_52", - "ranch_camp_53", - "ranch_camp_54", - "ranch_camp_55", - "ranch_camp_56", - "ranch_camp_57", - "ranch_camp_58", - "ranch_camp_59", - "ranch_camp_60", - "ranch_camp_61", - "ranch_camp_62", - "ranch_camp_63", - "ranch_camp_64", - "ranch_camp_65", - "ranch_camp_66", - "ranch_camp_67", - "ranch_camp_68", - "ranch_camp_69", - "ranch_camp_70", - "ranch_camp_71", - "ranch_camp_72", - "ranch_camp_73", - "ranch_camp_74", - "ranch_camp_75", - "ranch_camp_76", - "ranch_camp_77", - "ranch_camp_78", - "ranch_camp_79", - "ranch_camp_80", - "ranch_camp_81", - "ranch_camp_57_roof", - "ranch_camp_65_roof", - "ranch_camp_66_roof", - "ranch_camp_67_roof", - "ranch_camp_68_roof", - "ranch_camp_74_roof", - "ranch_camp_75_roof", - "ranch_camp_57_silo", - "ranch_camp_57_silocap", - "bandit_cabin", - "bandit_camp_1", - "bandit_camp_2", - "bandit_camp_3", - "bandit_camp_4", - "bandit_garage_1", - "bandit_garage_2", - "cabin", - "cabin_strange", - "cabin_strange_b", - "campsite", - "campsite_a", - "campsite_cabin_incomplete", - "campsite_field_biker", - "campsite_field_biker_destroyed", - "haz_sar", - "haz_sar_entrance", - "haz_sar_b1", - "haz_sar_entrance_b1", - "hunter_shack", - "basement_bionic", - "lmoe", - "lmoe_roof", - "lmoe_under", - "lmoe_under_empty", - "magic_basement", - "outpost", - "park", - "pond_field", - "pond_forest", - "pond_swamp", - "pool", - "pwr_large_entrance", - "pwr_large_2", - "pwr_large_3", - "pwr_large_4", - "pwr_sub_s", - "radio_tower", - "robofachq_ai_a0", - "robofachq_ai_a1", - "robofachq_ai_a2", - "robofachq_ai_a3", - "robofachq_ai_b0", - "robofachq_ai_b1", - "robofachq_ai_b2", - "robofachq_ai_b3", - "robofachq_aiutl_a0", - "robofachq_aiutl_a1", - "robofachq_aiutl_a2", - "robofachq_aiutl_a3", - "robofachq_aiutl_b0", - "robofachq_aiutl_b1", - "robofachq_aiutl_b2", - "robofachq_aiutl_b3", - "robofachq_hab_a0", - "robofachq_hab_a1", - "robofachq_hab_a2", - "robofachq_hab_a3", - "robofachq_hab_b0", - "robofachq_hab_b1", - "robofachq_hab_b2", - "robofachq_hab_b3", - "robofachq_roof_a0", - "robofachq_roof_a1", - "robofachq_roof_a2", - "robofachq_roof_a3", - "robofachq_surface_a0", - "robofachq_surface_a1", - "robofachq_surface_a2", - "robofachq_surface_a3", - "robofachq_surface_b0", - "robofachq_surface_b1", - "robofachq_surface_b2", - "robofachq_surface_b3", - "robofachq_surface_parking", - "robofachq_surface_entrance", - "robofachq_surface_car_entrance", - "sai", - "shelter", - "shelter_1", - "shelter_2", - "shelter_roof", - "shelter_roof_1", - "shelter_roof_2", - "shelter_under", - "shipwreck_river_1", - "shipwreck_river_2", - "shipwreck_river_3", - "shipwreck_river_4", - "toxic_dump", - "orchard_tree_apple", - "orchard_stall", - "orchard_processing", - "sewage_treatment", - "sewage_treatment_hub", - "sewage_treatment_under", - "dairy_farm_NW", - "dairy_farm_NE", - "dairy_farm_SW", - "dairy_farm_SE", - "megastore", - "megastore_entrance", - "haz_sar_entrance", - "haz_sar", - "haz_sar_entrance_b1", - "haz_sar_b1", - "haz_sar_entrance_north", - "haz_sar_north", - "haz_sar_entrance_b1_north", - "haz_sar_b1_north", - "haz_sar_entrance_east", - "haz_sar_east", - "haz_sar_entrance_b1_east", - "haz_sar_b1_east", - "haz_sar_entrance_south", - "haz_sar_south", - "haz_sar_entrance_b1_south", - "haz_sar_b1_south", - "haz_sar_entrance_west", - "haz_sar_west", - "haz_sar_entrance_b1_west", - "haz_sar_b1_west", - "house_base", - "house_base_north", - "house_base_south", - "house_base_east", - "house_base_west", "mass_grave_north", "mass_grave_south", "mass_grave_east", "mass_grave_west", - "house", - "house_north", - "house_south", - "house_east", - "house_west", - "rural_house", - "rural_house_north", - "rural_house_south", - "rural_house_east", - "rural_house_west" + "bridge_north", + "bridge_east", + "bridge_south", + "bridge_west", + "fema", + "fema_entrance", + "fema_1_3", + "fema_2_1", + "fema_2_2", + "fema_2_3", + "fema_3_1", + "fema_3_2", + "fema_3_3" ] } ] 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/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" ], diff --git a/data/json/recipes/armor/head.json b/data/json/recipes/armor/head.json index 200771e9c45fb..a8d29aa576dd5 100644 --- a/data/json/recipes/armor/head.json +++ b/data/json/recipes/armor/head.json @@ -1017,12 +1017,13 @@ "category": "CC_ARMOR", "subcategory": "CSC_ARMOR_HEAD", "skill_used": "fabrication", - "time": "6 m", + "time": "5 m", "autolearn": true, + "reversible": true, "components": [ [ [ "hat_hard", 1 ], [ "helmet_bike", 1 ] ], [ [ "flashlight", 1 ] ], - [ [ "cordage", 1, "LIST" ], [ "duct_tape", 10 ], [ "medical_tape", 20 ] ] + [ [ "rag", 2 ], [ "leather", 2 ], [ "cordage", 1, "LIST" ], [ "duct_tape", 10 ], [ "medical_tape", 20 ] ] ] }, { diff --git a/data/json/recipes/food/offal_dishes.json b/data/json/recipes/food/offal_dishes.json index 8915083ef24af..18bb8c63a3a78 100644 --- a/data/json/recipes/food/offal_dishes.json +++ b/data/json/recipes/food/offal_dishes.json @@ -385,7 +385,12 @@ "book_learn": [ [ "offalcooking", 3 ] ], "qualities": [ { "id": "COOK", "level": 2 }, { "id": "CUT_FINE", "level": 1 } ], "tools": [ [ [ "funnel", -1 ] ] ], - "components": [ [ [ "stomach", 2 ], [ "stomach_large", 1 ] ], [ [ "salt", 20 ] ], [ [ "water", 2 ] ], [ [ "bag_plastic", 1 ] ] ] + "components": [ + [ [ "stomach", 2 ], [ "stomach_large", 1 ] ], + [ [ "salt", 20 ] ], + [ [ "water", 2 ], [ "water_clean", 2 ] ], + [ [ "bag_plastic", 1 ] ] + ] }, { "type": "recipe", 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 ] ] ] + } +] diff --git a/data/json/recipes/recipe_deconstruction.json b/data/json/recipes/recipe_deconstruction.json index f783464cbbb6d..b4a17128f5bec 100644 --- a/data/json/recipes/recipe_deconstruction.json +++ b/data/json/recipes/recipe_deconstruction.json @@ -622,6 +622,26 @@ [ [ "steel_chunk", 12 ] ] ] }, + { + "result": "broken_robofac_laserturret_mk1", + "type": "uncraft", + "skill_used": "electronics", + "difficulty": 6, + "time": "3 h", + "using": [ [ "soldering_standard", 10 ] ], + "qualities": [ { "id": "SCREW", "level": 1 } ], + "components": [ + [ [ "ai_module", 1 ] ], + [ [ "gun_module", 1 ] ], + [ [ "targeting_module", 1 ] ], + [ [ "identification_module", 1 ] ], + [ [ "sensor_module", 1 ] ], + [ [ "medium_storage_battery", 1 ] ], + [ [ "power_supply", 1 ] ], + [ [ "robot_controls", 1 ] ], + [ [ "turret_chassis", 1 ] ] + ] + }, { "result": "broken_eyebot", "type": "uncraft", @@ -3749,6 +3769,26 @@ "qualities": [ { "id": "CUT", "level": 1 } ], "components": [ [ [ "rag", 25 ] ], [ [ "element", 5 ] ], [ [ "cable", 5 ] ] ] }, + { + "result": "wearable_light", + "type": "uncraft", + "skill_used": "fabrication", + "time": "30 s", + "qualities": [ { "id": "CUT", "level": 1 } ], + "components": [ [ [ "flashlight", 1 ] ], [ [ "rag", 2 ] ] ] + }, + { + "result": "miner_hat", + "type": "uncraft", + "skill_used": "fabrication", + "time": "30 s", + "qualities": [ { "id": "CUT", "level": 1 } ], + "components": [ + [ [ "hat_hard", 1 ], [ "helmet_bike", 1 ] ], + [ [ "flashlight", 1 ] ], + [ [ "rag", 2 ], [ "leather", 2 ], [ "cordage", 1, "LIST" ], [ "duct_tape", 10 ], [ "medical_tape", 20 ] ] + ] + }, { "result": "adobe_pallet_done", "type": "uncraft", diff --git a/data/json/recipes/recipe_food.json b/data/json/recipes/recipe_food.json index d565e5812bad4..b98a1213150b7 100644 --- a/data/json/recipes/recipe_food.json +++ b/data/json/recipes/recipe_food.json @@ -6292,7 +6292,7 @@ "//": "Making the very first starter is a lot more finnicky than splitting it once it's mature.", "autolearn": true, "qualities": [ { "id": "CONTAIN", "level": 1 } ], - "components": [ [ [ "water", 1 ], [ "water_clean", 1 ] ], [ [ "jar_glass", 1 ] ], [ [ "flour", 4 ] ] ] + "components": [ [ [ "water_clean", 1 ] ], [ [ "jar_glass", 1 ] ], [ [ "flour", 4 ] ] ] }, { "type": "recipe", @@ -6305,7 +6305,7 @@ "result_mult": 2, "autolearn": true, "qualities": [ { "id": "CONTAIN", "level": 1 } ], - "components": [ [ [ "water", 1 ], [ "water_clean", 1 ] ], [ [ "jar_glass", 1 ] ], [ [ "flour", 4 ] ], [ [ "sourdough_starter", 1 ] ] ] + "components": [ [ [ "water_clean", 1 ] ], [ [ "jar_glass", 1 ] ], [ [ "flour", 4 ] ], [ [ "sourdough_starter", 1 ] ] ] }, { "type": "recipe", @@ -6320,7 +6320,13 @@ "autolearn": true, "qualities": [ { "id": "COOK", "level": 3 } ], "tools": [ [ [ "surface_heat", 8, "LIST" ] ] ], - "components": [ [ [ "flour", 10 ] ], [ [ "sourdough_starter", 1 ] ], [ [ "water", 3 ], [ "water_clean", 3 ] ] ] + "//": "The single water_clean here is for feeding the new starter.", + "components": [ + [ [ "flour", 10 ] ], + [ [ "sourdough_starter", 1 ] ], + [ [ "water", 2 ], [ "water_clean", 2 ] ], + [ [ "water_clean", 1 ] ] + ] }, { "type": "recipe", diff --git a/data/json/recipes/recipe_others.json b/data/json/recipes/recipe_others.json index 4e39d6a1186a0..d8b49e9b4e897 100644 --- a/data/json/recipes/recipe_others.json +++ b/data/json/recipes/recipe_others.json @@ -452,7 +452,7 @@ "subcategory": "CSC_OTHER_TOOLS", "skill_used": "fabrication", "difficulty": 1, - "time": "20 m", + "time": "5 m", "reversible": true, "autolearn": true, "qualities": [ { "id": "CUT", "level": 1 } ], @@ -491,6 +491,7 @@ "difficulty": 2, "time": "25 m", "autolearn": true, + "reversible": true, "using": [ [ "soldering_standard", 10 ] ], "qualities": [ { "id": "CUT", "level": 1 }, { "id": "SCREW", "level": 1 } ], "components": [ @@ -1582,7 +1583,7 @@ "decomp_learn": 2, "autolearn": true, "using": [ [ "cordage", 1 ] ], - "components": [ [ [ "shotgun_d", 1 ] ], [ [ "shot_00", 2 ], [ "shot_slug", 2 ], [ "shot_flechette", 2 ] ] ] + "components": [ [ [ "slamfire_shotgun_d", 1 ] ], [ [ "shot_00", 2 ], [ "shot_slug", 2 ], [ "shot_flechette", 2 ] ] ] }, { "type": "recipe", diff --git a/data/json/recipes/weapon/ranged.json b/data/json/recipes/weapon/ranged.json index 1941edb1980c9..ac8a885227af8 100644 --- a/data/json/recipes/weapon/ranged.json +++ b/data/json/recipes/weapon/ranged.json @@ -260,7 +260,7 @@ }, { "type": "recipe", - "result": "four_winds_shotgun", + "result": "slamfire_shotgun", "category": "CC_WEAPON", "subcategory": "CSC_WEAPON_RANGED", "skill_used": "mechanics", @@ -274,7 +274,7 @@ { "id": "GLARE", "level": 2 }, { "id": "HAMMER", "level": 2 }, { "id": "FILE", "level": 1 }, - { "id": "WELD", "level": 2 } + { "id": "WELD", "level": 1 } ], "tools": [ [ @@ -304,7 +304,7 @@ }, { "type": "recipe", - "result": "four_winds_shotgun", + "result": "slamfire_shotgun", "id_suffix": "without_welding", "category": "CC_WEAPON", "subcategory": "CSC_WEAPON_RANGED", @@ -344,5 +344,150 @@ ] ], "components": [ [ [ "pipe", 2 ] ], [ [ "nail", 1 ] ], [ [ "pipe_fittings", 1 ] ] ] + }, + { + "type": "recipe", + "result": "slamfire_shotgun_d", + "id_suffix": "with_tape", + "category": "CC_WEAPON", + "subcategory": "CSC_WEAPON_RANGED", + "skill_used": "mechanics", + "skills_required": [ [ "gun", 1 ] ], + "time": "1 h", + "autolearn": true, + "components": [ [ [ "four_winds_shotgun", 2 ] ], [ [ "duct_tape", 100 ] ] ] + }, + { + "type": "recipe", + "result": "slamfire_shotgun_d", + "category": "CC_WEAPON", + "subcategory": "CSC_WEAPON_RANGED", + "skill_used": "mechanics", + "skills_required": [ [ "gun", 1 ] ], + "difficulty": 2, + "time": "6 h", + "autolearn": true, + "book_learn": [ [ "manual_shotgun", 1 ] ], + "qualities": [ + { "id": "SAW_M", "level": 1 }, + { "id": "GLARE", "level": 2 }, + { "id": "HAMMER", "level": 2 }, + { "id": "FILE", "level": 1 }, + { "id": "WELD", "level": 1 } + ], + "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", 4 ] ], [ [ "nail", 2 ] ], [ [ "scrap", 6 ] ] ] + }, + { + "type": "recipe", + "result": "slamfire_shotgun_d", + "id_suffix": "welded_together", + "category": "CC_WEAPON", + "subcategory": "CSC_WEAPON_RANGED", + "skill_used": "mechanics", + "skills_required": [ [ "gun", 1 ] ], + "difficulty": 2, + "time": "6 h", + "autolearn": true, + "book_learn": [ [ "manual_shotgun", 1 ] ], + "qualities": [ + { "id": "SAW_M", "level": 1 }, + { "id": "GLARE", "level": 2 }, + { "id": "HAMMER", "level": 2 }, + { "id": "FILE", "level": 1 }, + { "id": "WELD", "level": 1 } + ], + "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": [ [ [ "four_winds_shotgun", 2 ] ], [ [ "scrap", 1 ] ] ] + }, + { + "type": "recipe", + "result": "slamfire_shotgun_d", + "id_suffix": "bolted_and_welded", + "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 }, + { "id": "WELD", "level": 1 } + ], + "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", 4 ] ], [ [ "nail", 2 ] ], [ [ "pipe_fittings", 2 ] ], [ [ "scrap", 3 ] ] ] } ] diff --git a/data/json/regional_map_settings.json b/data/json/regional_map_settings.json index 4d45cca7a5304..3b0ec60531d91 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": { @@ -918,7 +918,22 @@ "base_acid": 0.0, "base_wind": 3.4, "base_wind_distrib_peaks": 80, - "base_wind_season_variation": 50 + "base_wind_season_variation": 50, + "weather_types": [ + "clear", + "sunny", + "cloudy", + "light_drizzle", + "drizzle", + "rain", + "thunder", + "lightning", + "acid_drizzle", + "acid_rain", + "flurries", + "snowing", + "snowstorm" + ] }, "overmap_feature_flag_settings": { "clear_blacklist": false, "blacklist": [ ], "clear_whitelist": false, "whitelist": [ ] } } diff --git a/data/json/snippets/fliers.json b/data/json/snippets/fliers.json index 54ca6acac2266..f4cd2afd3ed6a 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.\"" diff --git a/data/json/test_regions.json b/data/json/test_regions.json index d46d4f3782a68..095d3ed601b9f 100644 --- a/data/json/test_regions.json +++ b/data/json/test_regions.json @@ -127,7 +127,27 @@ "s_lot": 6 } }, - "weather": { "base_temperature": 8.0, "base_humidity": 40.0, "base_pressure": 1015.0, "base_acid": 0.0 }, + "weather": { + "base_temperature": 8.0, + "base_humidity": 40.0, + "base_pressure": 1015.0, + "base_acid": 0.0, + "weather_types": [ + "clear", + "sunny", + "cloudy", + "light_drizzle", + "drizzle", + "rain", + "thunder", + "lightning", + "acid_drizzle", + "acid_rain", + "flurries", + "snowing", + "snowstorm" + ] + }, "overmap_feature_flag_settings": { "clear_blacklist": false, "blacklist": [ ], "clear_whitelist": false, "whitelist": [ ] } }, { diff --git a/data/json/traps.json b/data/json/traps.json index 3e3d2d7d46e11..7a547c334bcee 100644 --- a/data/json/traps.json +++ b/data/json/traps.json @@ -265,7 +265,7 @@ "avoidance": 5, "difficulty": 6, "action": "shotgun", - "drops": [ "string_36", "shotgun_d", { "item": "shot_00", "quantity": 2, "charges": 1 } ], + "drops": [ "string_36", "slamfire_shotgun_d", { "item": "shot_00", "quantity": 2, "charges": 1 } ], "vehicle_data": { "chance": 70, "damage": 300, @@ -287,7 +287,7 @@ "avoidance": 5, "difficulty": 6, "action": "shotgun", - "drops": [ "string_36", "shotgun_d", { "item": "shot_00", "quantity": 1, "charges": 1 } ], + "drops": [ "string_36", "slamfire_shotgun_d", { "item": "shot_00", "quantity": 1, "charges": 1 } ], "vehicle_data": { "chance": 70, "damage": 300, @@ -296,7 +296,7 @@ "sound_type": "fire_gun", "sound_variant": "shotgun_d", "remove_trap": true, - "spawn_items": [ "shotgun_d", "string_6" ] + "spawn_items": [ "slamfire_shotgun_d", "string_6" ] } }, { @@ -310,7 +310,7 @@ "avoidance": 5, "difficulty": 6, "action": "shotgun", - "drops": [ "string_36", "shotgun_s", { "item": "shot_00", "quantity": 1, "charges": 1 } ], + "drops": [ "string_36", "slamfire_shotgun", { "item": "shot_00", "quantity": 1, "charges": 1 } ], "vehicle_data": { "chance": 70, "damage": 300, @@ -319,7 +319,7 @@ "sound_type": "fire_gun", "sound_variant": "shotgun_s", "remove_trap": true, - "spawn_items": [ "shotgun_s", "string_6" ] + "spawn_items": [ "four_winds_shotgun", "string_6" ] } }, { diff --git a/data/json/vehicle_groups.json b/data/json/vehicle_groups.json index 2d85139ae9b96..ba344589253dc 100644 --- a/data/json/vehicle_groups.json +++ b/data/json/vehicle_groups.json @@ -13,6 +13,7 @@ [ "suv_electric", 200 ], [ "suv_electric", 120 ], [ "car_mini", 400 ], + [ "car_hybrid", 500 ], [ "beetle", 500 ], [ "bicycle", 800 ], [ "bicycle_dirt", 200 ], @@ -97,7 +98,8 @@ [ "cube_van", 500 ], [ "cube_van_cheap", 500 ], [ "hippie_van", 500 ], - [ "4x4_car", 500 ] + [ "4x4_car", 500 ], + [ "car_hybrid", 500 ] ] }, { @@ -113,6 +115,7 @@ [ "car_anmlcmpt", 250 ], [ "car_hatch", 1000 ], [ "electric_car", 500 ], + [ "car_hybrid", 500 ], [ "car_sports", 1000 ], [ "car_sports_electric", 300 ], [ "pickup", 600 ], @@ -159,6 +162,7 @@ [ "car_hatch", 500 ], [ "car_mini", 250 ], [ "electric_car", 500 ], + [ "car_hybrid", 400 ], [ "beetle", 300 ], [ "car_sports", 100 ], [ "car_sports_electric", 50 ], @@ -225,6 +229,7 @@ [ "car_anmlcmpt", 100 ], [ "car_hatch", 500 ], [ "electric_car", 750 ], + [ "car_hybrid", 500 ], [ "hippie_van", 750 ], [ "golf_cart", 300 ], [ "scooter", 300 ], @@ -327,6 +332,7 @@ [ "car_hatch", 750 ], [ "car_chassis", 2000 ], [ "electric_car", 400 ], + [ "car_hybrid", 400 ], [ "car_sports", 400 ], [ "car_sports_electric", 100 ], [ "car_racing_electric", 5 ], @@ -350,6 +356,7 @@ [ "car_sports", 200 ], [ "suv", 400 ], [ "car_mini", 250 ], + [ "car_hybrid", 200 ], [ "rv", 250 ], [ "meth_lab", 50 ], [ "beetle", 400 ], @@ -369,6 +376,7 @@ [ "car_hatch", 475 ], [ "car_anmlcmpt", 150 ], [ "electric_car", 100 ], + [ "car_hybrid", 100 ], [ "suv", 800 ], [ "suv_electric", 100 ], [ "suv_electric_rack", 100 ], @@ -392,6 +400,7 @@ [ "suv", 400 ], [ "rv", 200 ], [ "car_sports", 300 ], + [ "car_hybrid", 200 ], [ "cube_van_cheap", 200 ], [ "pickup", 500 ], [ "hippie_van", 300 ] @@ -409,6 +418,7 @@ [ "car_hatch", 500 ], [ "car_anmlcmpt", 150 ], [ "electric_car", 750 ], + [ "car_hybrid", 500 ], [ "hippie_van", 750 ], [ "motorcycle", 500 ], [ "superbike", 50 ], @@ -570,6 +580,7 @@ [ "car", 2000 ], [ "car_hatch", 1000 ], [ "electric_car", 500 ], + [ "car_hybrid", 500 ], [ "suv", 800 ], [ "suv_electric", 200 ], [ "car_mini", 400 ], @@ -600,6 +611,7 @@ [ "4x4_car", 200 ], [ "car_sports_electric", 100 ], [ "car_mini", 375 ], + [ "car_hybrid", 200 ], [ "beetle", 750 ], [ "car", 1500 ], [ "car_hatch", 750 ], @@ -630,6 +642,7 @@ [ "car", 1000 ], [ "car_hatch", 500 ], [ "electric_car", 500 ], + [ "car_hybrid", 400 ], [ "suv", 800 ], [ "suv_electric", 200 ], [ "car_mini", 400 ], @@ -666,6 +679,7 @@ [ "car", 2000 ], [ "car_hatch", 1000 ], [ "electric_car", 500 ], + [ "car_hybrid", 400 ], [ "suv", 800 ], [ "suv_electric", 200 ], [ "car_mini", 400 ], @@ -692,6 +706,7 @@ [ "car", 2000 ], [ "car_hatch", 1000 ], [ "electric_car", 500 ], + [ "car_hybrid", 400 ], [ "car_mini", 250 ], [ "suv", 800 ], [ "suv_electric", 200 ], @@ -711,6 +726,7 @@ "vehicles": [ [ "car", 500 ], [ "electric_car", 100 ], + [ "car_hybrid", 100 ], [ "car_sports", 300 ], [ "car_sports_electric", 300 ], [ "suv", 500 ], diff --git a/data/json/vehicleparts/vehicle_parts.json b/data/json/vehicleparts/vehicle_parts.json index ad3972da7ed0f..78eda76c26d9c 100644 --- a/data/json/vehicleparts/vehicle_parts.json +++ b/data/json/vehicleparts/vehicle_parts.json @@ -1872,6 +1872,35 @@ "flags": [ "ON_CONTROLS", "SECURITY", "FOLDABLE", "ENABLED_DRAINS_EPOWER" ], "breaks_into": "ig_vp_device" }, + { + "type": "vehicle_part", + "id": "smart_engine_controller", + "name": { "str": "smart engine controller" }, + "symbol": "'", + "color": "light_gray", + "broken_symbol": "'", + "broken_color": "red", + "damage_modifier": 10, + "durability": 35, + "description": "Electronic module that automatically switches combustion and electric engines on and off minimizing fuel consumption and optimizing power output and battery charge rate. Must be turned on to work. Simplified ruleset: 1. When throttling, maximize acceleration. 2. Keep battery at 90%. 3. Minimize fuel consumption.", + "epower": -25, + "folded_volume": 1, + "item": "processor", + "requirements": { + "install": { + "skills": [ [ "mechanics", 4 ], [ "electronics", 4 ] ], + "time": "5 m", + "qualities": [ { "id": "SCREW_FINE", "level": 1 } ] + }, + "removal": { + "skills": [ [ "mechanics", 4 ], [ "electronics", 4 ] ], + "time": "5 m", + "qualities": [ { "id": "SCREW_FINE", "level": 1 } ] + } + }, + "flags": [ "ON_CONTROLS", "FOLDABLE", "ENABLED_DRAINS_EPOWER", "SMART_ENGINE_CONTROLLER" ], + "breaks_into": "ig_vp_device" + }, { "type": "vehicle_part", "id": "seatbelt_heavyduty", diff --git a/data/json/vehicleparts/vp_flags.json b/data/json/vehicleparts/vp_flags.json index 50299b7a1a5d6..34b5a82490319 100644 --- a/data/json/vehicleparts/vp_flags.json +++ b/data/json/vehicleparts/vp_flags.json @@ -54,6 +54,12 @@ "context": [ "vehicle_part" ], "info": "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 '^')." }, + { + "id": "SMART_ENGINE_CONTROLLER", + "type": "json_flag", + "context": [ "vehicle_part" ], + "info": "When active, turns engines on and off automatically." + }, { "id": "COVERED", "type": "json_flag", diff --git a/data/json/vehicles/cars.json b/data/json/vehicles/cars.json index b7965cc0d8f3f..e0467dd473ae0 100644 --- a/data/json/vehicles/cars.json +++ b/data/json/vehicles/cars.json @@ -467,6 +467,62 @@ { "x": -3, "y": 2, "parts": [ "wheel_mount_medium", "wheel" ] } ] }, + { + "id": "car_hybrid", + "type": "vehicle", + "name": "Hybrid Car", + "blueprint": [ + [ "o-++-o" ], + [ "+=##'|" ], + [ "+=##'|" ], + [ "o-++-o" ] + ], + "parts": [ + { "x": 0, "y": 0, "part": "frame_vertical_2" }, + { "x": 0, "y": 0, "parts": [ "reclining_seat", "seatbelt" ] }, + { + "x": 0, + "y": 0, + "parts": [ "controls", "dashboard", "vehicle_clock", "smart_engine_controller", "stereo", "horn_car", "vehicle_alarm" ] + }, + { "x": 0, "y": 0, "part": "roof" }, + { "x": 0, "y": 1, "parts": [ "frame_vertical_2", "reclining_seat", "seatbelt", "roof" ] }, + { "x": 0, "y": -1, "parts": [ "frame_vertical", "door" ] }, + { "x": 0, "y": 2, "parts": [ "frame_vertical", "door" ] }, + { "x": -1, "y": 0, "parts": [ "frame_vertical_2", "seat", "seatbelt", "roof" ] }, + { "x": -1, "y": 1, "parts": [ "frame_vertical_2", "seat", "seatbelt", "roof" ] }, + { "x": -1, "y": -1, "parts": [ "frame_vertical", "door" ] }, + { "x": -1, "y": 2, "parts": [ "frame_vertical", "door" ] }, + { "x": 1, "y": 0, "parts": [ "frame_horizontal", "windshield" ] }, + { "x": 1, "y": 1, "parts": [ "frame_horizontal", "windshield" ] }, + { "x": 1, "y": -1, "parts": [ "frame_vertical", "windshield" ] }, + { "x": 1, "y": 2, "parts": [ "frame_vertical", "windshield" ] }, + { "x": 2, "y": 0, "parts": [ "frame_horizontal", "halfboard_horizontal" ] }, + { "x": 2, "y": 0, "parts": [ "engine_inline4", "alternator_truck" ] }, + { "x": 2, "y": 1, "parts": [ "frame_horizontal", "engine_electric", "halfboard_horizontal" ] }, + { "x": 2, "y": -1, "parts": [ "frame_nw", "halfboard_nw", "headlight" ] }, + { "x": 2, "y": -1, "parts": [ "wheel_mount_medium_steerable", "wheel" ] }, + { "x": 2, "y": 2, "parts": [ "frame_ne", "halfboard_ne", "headlight" ] }, + { "x": 2, "y": 2, "parts": [ "wheel_mount_medium_steerable", "wheel" ] }, + { "x": -2, "y": 0, "parts": [ "frame_vertical", "medium_storage_battery", "trunk", "muffler", "roof" ] }, + { "x": -2, "y": 1, "parts": [ "frame_vertical", "medium_storage_battery", "trunk", "roof" ] }, + { "x": -2, "y": -1, "parts": [ "frame_vertical", "halfboard_vertical", "tank" ] }, + { "x": -2, "y": 2, "parts": [ "frame_vertical", "halfboard_vertical" ] }, + { "x": -3, "y": -1, "parts": [ "frame_horizontal", "halfboard_sw", "wheel_mount_medium", "wheel" ] }, + { "x": -3, "y": 0, "parts": [ "frame_horizontal", "door_trunk" ] }, + { "x": -3, "y": 1, "parts": [ "frame_horizontal", "door_trunk" ] }, + { "x": -3, "y": 2, "parts": [ "frame_horizontal", "halfboard_se", "wheel_mount_medium", "wheel" ] } + ], + "items": [ + { "x": 0, "y": 0, "chance": 14, "item_groups": [ "car_misc" ] }, + { "x": 0, "y": 0, "chance": 5, "item_groups": [ "snacks" ] }, + { "x": 0, "y": 1, "chance": 8, "item_groups": [ "car_misc" ] }, + { "x": 0, "y": 1, "chance": 2, "item_groups": [ "fast_food" ] }, + { "x": -2, "y": 0, "chance": 11, "item_groups": [ "car_kit" ] }, + { "x": -2, "y": 1, "chance": 15, "item_groups": [ "car_kit" ] }, + { "x": -2, "y": 1, "chance": 15, "items": [ "jack_small", "wheel" ] } + ] + }, { "id": "car_mini", "type": "vehicle", diff --git a/data/json/weather_type.json b/data/json/weather_type.json new file mode 100644 index 0000000000000..468798782d984 --- /dev/null +++ b/data/json/weather_type.json @@ -0,0 +1,293 @@ +[ + { + "id": "null", + "type": "weather_type", + "name": "NULL Weather - BUG", + "color": "c_magenta", + "map_color": "c_magenta_red", + "glyph": "0", + "ranged_penalty": 0, + "sight_penalty": 0.0, + "light_modifier": 0, + "sound_attn": 0, + "dangerous": false, + "precip": "none", + "rains": false, + "acidic": false, + "sun_intensity": "normal" + }, + { + "id": "clear", + "type": "weather_type", + "name": "Clear", + "color": "c_cyan", + "map_color": "c_yellow_white", + "glyph": " ", + "ranged_penalty": 0, + "sight_penalty": 1.0, + "light_modifier": 0, + "sound_attn": 0, + "dangerous": false, + "precip": "none", + "rains": false, + "acidic": false, + "sun_intensity": "normal" + }, + { + "id": "sunny", + "type": "weather_type", + "name": "Sunny", + "color": "c_light_cyan", + "map_color": "c_yellow_white", + "glyph": "*", + "ranged_penalty": 0, + "sight_penalty": 1.0, + "light_modifier": 2, + "sound_attn": 0, + "dangerous": false, + "precip": "none", + "rains": false, + "acidic": false, + "sun_intensity": "high", + "requirements": { "pressure_min": 1020, "humidity_max": 70, "time": "day" } + }, + { + "id": "cloudy", + "type": "weather_type", + "name": "Cloudy", + "color": "c_light_gray", + "map_color": "c_dark_gray_white", + "glyph": "~", + "ranged_penalty": 0, + "sight_penalty": 1.0, + "light_modifier": -20, + "sound_attn": 0, + "dangerous": false, + "precip": "none", + "rains": false, + "acidic": false, + "sun_intensity": "light", + "requirements": { "pressure_max": 1010, "humidity_min": 40 } + }, + { + "id": "light_drizzle", + "type": "weather_type", + "name": "Light Drizzle", + "color": "c_light_blue", + "map_color": "h_light_blue", + "glyph": ".", + "ranged_penalty": 0, + "sight_penalty": 1.01, + "light_modifier": -10, + "sound_attn": 0, + "dangerous": false, + "precip": "very_light", + "rains": true, + "acidic": false, + "tiles_animation": "weather_rain_drop", + "weather_animation": { "factor": 0.01, "color": "c_light_blue", "glyph": "," }, + "sound_category": "drizzle", + "sun_intensity": "light", + "requirements": { "pressure_max": 1003, "humidity_min": 96, "humidity_and_pressure": false, "required_weathers": [ "cloudy" ] } + }, + { + "id": "drizzle", + "type": "weather_type", + "name": "Drizzle", + "color": "c_light_blue", + "map_color": "h_light_blue", + "glyph": ".", + "ranged_penalty": 1, + "sight_penalty": 1.03, + "light_modifier": -20, + "sound_attn": 1, + "dangerous": false, + "precip": "light", + "rains": true, + "acidic": false, + "tiles_animation": "weather_rain_drop", + "weather_animation": { "factor": 0.01, "color": "c_light_blue", "glyph": "." }, + "sound_category": "drizzle", + "sun_intensity": "light", + "requirements": { "pressure_max": 1000, "humidity_min": 97, "humidity_and_pressure": false, "required_weathers": [ "light_drizzle" ] } + }, + { + "id": "rain", + "type": "weather_type", + "name": "Rain", + "color": "c_blue", + "map_color": "h_blue", + "glyph": "o", + "ranged_penalty": 3, + "sight_penalty": 1.1, + "light_modifier": -30, + "sound_attn": 4, + "dangerous": false, + "precip": "heavy", + "rains": true, + "acidic": false, + "tiles_animation": "weather_rain_drop", + "weather_animation": { "factor": 0.02, "color": "c_light_blue", "glyph": "," }, + "sound_category": "rainy", + "sun_intensity": "light", + "requirements": { + "pressure_max": 993, + "humidity_min": 98, + "humidity_and_pressure": false, + "required_weathers": [ "light_drizzle", "drizzle" ] + } + }, + { + "id": "thunder", + "type": "weather_type", + "name": "Thunder Storm", + "color": "c_dark_gray", + "map_color": "i_blue", + "glyph": "%", + "ranged_penalty": 4, + "sight_penalty": 1.2, + "light_modifier": -40, + "sound_attn": 8, + "dangerous": false, + "precip": "heavy", + "rains": true, + "acidic": false, + "effects": [ { "name": "thunder", "intensity": 50 } ], + "tiles_animation": "weather_rain_drop", + "weather_animation": { "factor": 0.02, "color": "c_light_blue", "glyph": "." }, + "sound_category": "thunder", + "sun_intensity": "none", + "requirements": { "pressure_max": 996, "required_weathers": [ "rain" ] } + }, + { + "id": "lightning", + "type": "weather_type", + "name": "Lightning Storm", + "color": "c_yellow", + "map_color": "h_yellow", + "glyph": "%", + "ranged_penalty": 4, + "sight_penalty": 1.25, + "light_modifier": -45, + "sound_attn": 8, + "dangerous": false, + "precip": "heavy", + "rains": true, + "acidic": false, + "effects": [ { "name": "thunder", "intensity": 50 }, { "name": "lightning", "intensity": 600 } ], + "tiles_animation": "weather_rain_drop", + "weather_animation": { "factor": 0.04, "color": "c_light_blue", "glyph": "," }, + "sound_category": "thunder", + "sun_intensity": "none", + "requirements": { "pressure_max": 990, "required_weathers": [ "thunder" ] } + }, + { + "id": "acid_drizzle", + "type": "weather_type", + "name": "Acidic Drizzle", + "color": "c_light_green", + "map_color": "c_yellow_green", + "glyph": ".", + "ranged_penalty": 2, + "sight_penalty": 1.03, + "light_modifier": -20, + "sound_attn": 1, + "dangerous": true, + "precip": "light", + "rains": true, + "acidic": true, + "effects": [ { "name": "light_acid", "intensity": 180 } ], + "tiles_animation": "weather_acid_drop", + "weather_animation": { "factor": 0.01, "color": "c_light_green", "glyph": "." }, + "sound_category": "drizzle", + "sun_intensity": "normal", + "requirements": { "acidic": true, "required_weathers": [ "drizzle" ] } + }, + { + "id": "acid_rain", + "type": "weather_type", + "name": "Acid Rain", + "color": "c_green", + "map_color": "c_yellow_green", + "glyph": "o", + "ranged_penalty": 4, + "sight_penalty": 1.1, + "light_modifier": -30, + "sound_attn": 4, + "dangerous": true, + "precip": "heavy", + "rains": true, + "acidic": true, + "effects": [ { "name": "acid", "intensity": 2 } ], + "tiles_animation": "weather_acid_drop", + "weather_animation": { "factor": 0.02, "color": "c_light_green", "glyph": "," }, + "sound_category": "rainy", + "sun_intensity": "none", + "requirements": { "acidic": true, "required_weathers": [ "rain" ] } + }, + { + "id": "flurries", + "type": "weather_type", + "name": "Flurries", + "color": "c_white", + "map_color": "c_dark_gray_cyan", + "glyph": ".", + "ranged_penalty": 2, + "sight_penalty": 1.12, + "light_modifier": -15, + "sound_attn": 2, + "dangerous": false, + "precip": "light", + "rains": false, + "acidic": false, + "tiles_animation": "weather_snowflake", + "weather_animation": { "factor": 0.01, "color": "c_white", "glyph": "." }, + "sound_category": "flurries", + "sun_intensity": "light", + "requirements": { "temperature_max": 33, "required_weathers": [ "drizzle" ] } + }, + { + "id": "snowing", + "type": "weather_type", + "name": "Snowing", + "color": "c_white", + "map_color": "c_dark_gray_cyan", + "glyph": "*", + "ranged_penalty": 4, + "sight_penalty": 1.3, + "light_modifier": -20, + "sound_attn": 4, + "dangerous": false, + "precip": "heavy", + "rains": false, + "acidic": false, + "effects": [ { "name": "wet", "intensity": 10 } ], + "tiles_animation": "weather_snowflake", + "weather_animation": { "factor": 0.02, "color": "c_white", "glyph": "," }, + "sound_category": "snow", + "sun_intensity": "light", + "requirements": { "temperature_max": 33, "required_weathers": [ "rain", "thunder", "lightning" ] } + }, + { + "id": "snowstorm", + "type": "weather_type", + "name": "Snowstorm", + "color": "c_white", + "map_color": "c_white_cyan", + "glyph": "%", + "ranged_penalty": 6, + "sight_penalty": 1.2, + "light_modifier": -30, + "sound_attn": 6, + "dangerous": false, + "precip": "heavy", + "rains": false, + "acidic": false, + "effects": [ { "name": "wet", "intensity": 40 } ], + "tiles_animation": "weather_snowflake", + "weather_animation": { "factor": 0.04, "color": "c_white", "glyph": "*" }, + "sound_category": "snowstorm", + "sun_intensity": "none", + "requirements": { "temperature_max": 33, "windpower_min": 15, "required_weathers": [ "thunder", "lightning" ] } + } +] 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/BlazeIndustries/items/vehicle/blaze_gun.json b/data/mods/BlazeIndustries/items/vehicle/blaze_gun.json new file mode 100644 index 0000000000000..a533cf00369ec --- /dev/null +++ b/data/mods/BlazeIndustries/items/vehicle/blaze_gun.json @@ -0,0 +1,85 @@ +[ + { + "id": "blaze_marlin_9a", + "looks_like": "ar15", + "type": "GUN", + "reload_noise_volume": 10, + "name": { "str_sp": "modified Marlin 39A" }, + "description": "A Marlin 39A, modified for use in a vehicle turret.", + "weight": "2948 g", + "volume": "2500 ml", + "price": 23000, + "price_postapoc": 2000, + "to_hit": -1, + "bashing": 12, + "material": [ "iron", "wood" ], + "symbol": "(", + "color": "brown", + "ammo": [ "22" ], + "skill": "rifle", + "ranged_damage": { "damage_type": "bullet", "amount": 5 }, + "dispersion": 90, + "durability": 8, + "blackpowder_tolerance": 56, + "clip_size": 19, + "valid_mod_locations": [ + [ "accessories", 4 ], + [ "barrel", 1 ], + [ "bore", 1 ], + [ "brass catcher", 1 ], + [ "grip", 1 ], + [ "mechanism", 4 ], + [ "muzzle", 1 ], + [ "rail", 1 ], + [ "sights", 1 ], + [ "sling", 1 ], + [ "stock", 1 ], + [ "underbarrel", 1 ] + ], + "faults": [ "fault_gun_blackpowder", "fault_gun_dirt" ], + "flags": [ "RELOAD_ONE" ], + "pocket_data": [ { "pocket_type": "MAGAZINE", "ammo_restriction": { "22": 19 } } ] + }, + { + "id": "blaze_sks", + "looks_like": "ar15", + "type": "GUN", + "reload_noise_volume": 10, + "name": { "str": "modified SKS" }, + "description": "An SKS, modified to be suitable for use in a vehicle turret.", + "weight": "3850 g", + "volume": "2838 ml", + "longest_side": "103 cm", + "price": 38000, + "price_postapoc": 3500, + "to_hit": -1, + "bashing": 12, + "material": [ "steel", "wood" ], + "symbol": "(", + "color": "brown", + "ammo": [ "762" ], + "skill": "rifle", + "ranged_damage": { "damage_type": "bullet", "amount": 1 }, + "dispersion": 90, + "durability": 8, + "clip_size": 10, + "barrel_length": "500 ml", + "built_in_mods": [ "inter_bayonet" ], + "valid_mod_locations": [ + [ "accessories", 4 ], + [ "barrel", 1 ], + [ "bore", 1 ], + [ "brass catcher", 1 ], + [ "grip", 1 ], + [ "mechanism", 4 ], + [ "muzzle", 1 ], + [ "rail", 1 ], + [ "sights", 1 ], + [ "sling", 1 ], + [ "stock", 1 ], + [ "underbarrel", 1 ] + ], + "flags": [ "RELOAD_ONE", "NEVER_JAMS" ], + "pocket_data": [ { "pocket_type": "MAGAZINE", "ammo_restriction": { "762": 10 } } ] + } +] diff --git a/data/mods/BlazeIndustries/recipes/blaze_gun_recipes.json b/data/mods/BlazeIndustries/recipes/blaze_gun_recipes.json new file mode 100644 index 0000000000000..9e9ca36c76dcd --- /dev/null +++ b/data/mods/BlazeIndustries/recipes/blaze_gun_recipes.json @@ -0,0 +1,26 @@ +[ + { + "result": "blaze_sks", + "type": "recipe", + "category": "CC_OTHER", + "subcategory": "CSC_OTHER_MATERIALS", + "skill_used": "mechanics", + "difficulty": 2, + "time": "20 m", + "autolearn": true, + "qualities": [ { "id": "HAMMER", "level": 2 }, { "id": "SAW_M", "level": 1 } ], + "components": [ [ [ "sks", 1 ] ] ] + }, + { + "result": "blaze_marlin_9a", + "type": "recipe", + "category": "CC_OTHER", + "subcategory": "CSC_OTHER_MATERIALS", + "skill_used": "mechanics", + "difficulty": 2, + "time": "20 m", + "autolearn": true, + "qualities": [ { "id": "HAMMER", "level": 2 }, { "id": "SAW_M", "level": 1 } ], + "components": [ [ [ "marlin_9a", 1 ] ] ] + } +] diff --git a/data/mods/BlazeIndustries/vehicleparts/blaze_turrets_vanilla.json b/data/mods/BlazeIndustries/vehicleparts/blaze_turrets_vanilla.json index 7a80bf31e28e4..cb7e6e71e2feb 100644 --- a/data/mods/BlazeIndustries/vehicleparts/blaze_turrets_vanilla.json +++ b/data/mods/BlazeIndustries/vehicleparts/blaze_turrets_vanilla.json @@ -366,8 +366,8 @@ "copy-from": "turret", "type": "vehicle_part", "name": { "str": "mounted Marlin 39A" }, - "item": "marlin_9a", - "breaks_into": [ { "item": "marlin_9a", "prob": 50 } ], + "item": "blaze_marlin_9a", + "breaks_into": [ { "item": "blaze_marlin_9a", "prob": 50 } ], "requirements": { "install": { "skills": [ [ "mechanics", 3 ] ] }, "removal": { "skills": [ [ "mechanics", 1 ] ] } } }, { @@ -614,8 +614,8 @@ "copy-from": "turret", "type": "vehicle_part", "name": { "str": "mounted SKS" }, - "item": "sks", - "breaks_into": [ { "item": "sks", "prob": 50 } ], + "item": "blaze_sks", + "breaks_into": [ { "item": "blaze_sks", "prob": 50 } ], "requirements": { "install": { "skills": [ [ "mechanics", 3 ] ] }, "removal": { "skills": [ [ "mechanics", 1 ] ] } } }, { 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/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" ] + } +] diff --git a/data/mods/Generic_Guns/firearms/gg_firearms_migration.json b/data/mods/Generic_Guns/firearms/gg_firearms_migration.json index 09a76cd919cd4..398eaa75f7b0e 100644 --- a/data/mods/Generic_Guns/firearms/gg_firearms_migration.json +++ b/data/mods/Generic_Guns/firearms/gg_firearms_migration.json @@ -308,8 +308,13 @@ "replace": "shot_pump" }, { - "id": "slam_shotgun", + "id": "slamfire_shotgun", "type": "MIGRATION", - "replace": "four_winds_shotgun" + "replace": "slamfire_shotgun" + }, + { + "id": "slamfire_shotgun_d", + "type": "MIGRATION", + "replace": "slamfire_shotgun_d" } ] 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/Generic_Guns/firearms/shot.json b/data/mods/Generic_Guns/firearms/shot.json index 4986437868f5d..d566b22c79a9a 100644 --- a/data/mods/Generic_Guns/firearms/shot.json +++ b/data/mods/Generic_Guns/firearms/shot.json @@ -50,25 +50,19 @@ "ammo": [ "ammo_shot" ] }, { - "id": "four_winds_shotgun", - "copy-from": "four_winds_shotgun", + "id": "slamfire_shotgun", + "copy-from": "slamfire_shotgun", "type": "GUN", - "name": { "str": "four winds shotgun" }, + "name": { "str": "slam-fire pipe 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", - "looks_like": "pipe", - "price": 8500, - "price_postapoc": 1000, - "bashing": 8, - "material": [ "steel" ], - "ranged_damage": { "damage_type": "bullet", "amount": -6 }, - "dispersion": 960, - "durability": 4, - "clip_size": 1, - "modes": [ [ "DEFAULT", "single", 1 ] ], - "valid_mod_locations": [ [ "sling", 1 ], [ "sights mount", 1 ], [ "underbarrel mount", 1 ] ], - "flags": [ "RELOAD_EJECT" ], - "pocket_data": [ { "pocket_type": "MAGAZINE", "ammo_restriction": { "shot": 1 } } ] + "ammo": [ "ammo_shot" ] + }, + { + "id": "slamfire_shotgun_d", + "copy-from": "slamfire_shotgun_d", + "type": "GUN", + "name": { "str": "double slam-fire pipe 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.", + "ammo": [ "ammo_shot" ] } ] diff --git a/data/mods/Generic_Guns/recipes/recipes_firearms_single.json b/data/mods/Generic_Guns/recipes/recipes_firearms_single.json index d3c1c77753998..b2914a75b4556 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": "four_winds_shotgun", + "result": "slamfire_shotgun", "category": "CC_WEAPON", "subcategory": "CSC_WEAPON_RANGED", "skill_used": "mechanics", @@ -139,7 +139,7 @@ { "id": "GLARE", "level": 2 }, { "id": "HAMMER", "level": 2 }, { "id": "FILE", "level": 1 }, - { "id": "WELD", "level": 2 } + { "id": "WELD", "level": 1 } ], "tools": [ [ @@ -167,7 +167,7 @@ }, { "type": "recipe", - "result": "four_winds_shotgun", + "result": "slamfire_shotgun", "id_suffix": "without_welding", "category": "CC_WEAPON", "subcategory": "CSC_WEAPON_RANGED", @@ -205,5 +205,117 @@ ] ], "components": [ [ [ "pipe", 2 ] ], [ [ "nail", 1 ] ], [ [ "pipe_fittings", 1 ] ] ] + }, + { + "type": "recipe", + "result": "slamfire_shotgun_d", + "category": "CC_WEAPON", + "subcategory": "CSC_WEAPON_RANGED", + "skill_used": "mechanics", + "skills_required": [ [ "gun", 1 ] ], + "difficulty": 2, + "time": "2 h", + "autolearn": true, + "book_learn": [ [ "manual_shotgun", 1 ] ], + "qualities": [ + { "id": "SAW_M", "level": 1 }, + { "id": "GLARE", "level": 2 }, + { "id": "HAMMER", "level": 2 }, + { "id": "FILE", "level": 1 }, + { "id": "WELD", "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", 4 ] ], [ [ "nail", 2 ] ], [ [ "scrap", 5 ] ] ] + }, + { + "type": "recipe", + "result": "slamfire_shotgun_d", + "id_suffix": "welded_and_bolted", + "category": "CC_WEAPON", + "subcategory": "CSC_WEAPON_RANGED", + "skill_used": "mechanics", + "skills_required": [ [ "gun", 1 ] ], + "difficulty": 2, + "time": "2 h", + "autolearn": true, + "book_learn": [ [ "manual_shotgun", 1 ] ], + "qualities": [ + { "id": "SAW_M", "level": 1 }, + { "id": "GLARE", "level": 2 }, + { "id": "HAMMER", "level": 2 }, + { "id": "FILE", "level": 1 }, + { "id": "WRENCH", "level": 1 }, + { "id": "WELD", "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", 4 ] ], [ [ "nail", 2 ] ], [ [ "pipe_fittings", 2 ] ], [ [ "scrap", 2 ] ] ] + }, + { + "type": "recipe", + "result": "slamfire_shotgun_d", + "id_suffix": "welded_together", + "category": "CC_WEAPON", + "subcategory": "CSC_WEAPON_RANGED", + "skill_used": "mechanics", + "skills_required": [ [ "gun", 1 ] ], + "difficulty": 1, + "time": "2 h", + "autolearn": true, + "qualities": [ { "id": "WELD", "level": 1 } ], + "components": [ [ [ "slamfire_shotgun", 2 ] ], [ [ "scrap", 2 ] ] ] + }, + { + "type": "recipe", + "result": "slamfire_shotgun_d", + "id_suffix": "taped_together", + "category": "CC_WEAPON", + "subcategory": "CSC_WEAPON_RANGED", + "skill_used": "mechanics", + "skills_required": [ [ "gun", 1 ] ], + "time": "1 h", + "autolearn": true, + "components": [ [ [ "slamfire_shotgun", 2 ] ], [ [ "duct_tape", 100 ] ] ] } ] 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" ], diff --git a/data/mods/Magiclysm/professions.json b/data/mods/Magiclysm/professions.json index 0f460566c65d1..cd898e0b6809a 100644 --- a/data/mods/Magiclysm/professions.json +++ b/data/mods/Magiclysm/professions.json @@ -271,6 +271,7 @@ "boots", "wristwatch", "holy_symbol", + "mbag", "priest_beginner", "whiskey", "whiskey", 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/data/mods/desert_region/desert_regional_map_settings.json b/data/mods/desert_region/desert_regional_map_settings.json index d292d51678de5..d23a5d52b67a9 100644 --- a/data/mods/desert_region/desert_regional_map_settings.json +++ b/data/mods/desert_region/desert_regional_map_settings.json @@ -709,7 +709,22 @@ "spring_humidity_manual_mod": 10, "summer_humidity_manual_mod": 0, "autumn_humidity_manual_mod": 10, - "winter_humidity_manual_mod": 15 + "winter_humidity_manual_mod": 15, + "weather_types": [ + "clear", + "sunny", + "cloudy", + "light_drizzle", + "drizzle", + "rain", + "thunder", + "lightning", + "acid_drizzle", + "acid_rain", + "flurries", + "snowing", + "snowstorm" + ] }, "overmap_feature_flag_settings": { "clear_blacklist": false, "blacklist": [ ], "clear_whitelist": false, "whitelist": [ ] } } diff --git a/data/mods/rural_biome/rural_regional_map_settings.json b/data/mods/rural_biome/rural_regional_map_settings.json index c254c1cc0fbc3..32519ed5125c8 100644 --- a/data/mods/rural_biome/rural_regional_map_settings.json +++ b/data/mods/rural_biome/rural_regional_map_settings.json @@ -608,7 +608,22 @@ "base_acid": 0.0, "base_wind": 3.4, "base_wind_distrib_peaks": 80, - "base_wind_season_variation": 50 + "base_wind_season_variation": 50, + "weather_types": [ + "clear", + "sunny", + "cloudy", + "light_drizzle", + "drizzle", + "rain", + "thunder", + "lightning", + "acid_drizzle", + "acid_rain", + "flurries", + "snowing", + "snowstorm" + ] }, "overmap_feature_flag_settings": { "clear_blacklist": false, diff --git a/data/raw/keybindings/vehicle.json b/data/raw/keybindings/vehicle.json index f3cc5141bceb2..433cefb64acbc 100644 --- a/data/raw/keybindings/vehicle.json +++ b/data/raw/keybindings/vehicle.json @@ -223,6 +223,13 @@ "name": "Toggle tracking", "bindings": [ { "input_method": "keyboard", "key": "K" } ] }, + { + "id": "TOGGLE_SMART_ENGINE_CONTROLLER", + "type": "keybinding", + "category": "VEHICLE", + "name": "Toggle smart engine controller", + "bindings": [ { "input_method": "keyboard", "key": "Y" } ] + }, { "id": "TURRET_FIRE_MODE", "type": "keybinding", diff --git a/doc/COMPILING/COMPILING-VS-VCPKG.md b/doc/COMPILING/COMPILING-VS-VCPKG.md index 376386d76bd08..7a52ec15f119b 100644 --- a/doc/COMPILING/COMPILING-VS-VCPKG.md +++ b/doc/COMPILING/COMPILING-VS-VCPKG.md @@ -29,7 +29,7 @@ cd vcpkg .\vcpkg integrate install ``` -4. Install (or upgrade) neccessary packages with following command line: +4. (Optionally) Install (or upgrade) necessary packages with following command line: #### install 64 bit dependencies: @@ -61,7 +61,7 @@ git clone https://github.com/CleverRaven/Cataclysm-DDA.git cd Cataclysm-DDA ``` -2. Open the provided solution (`msvc-full-features\Cataclysm-vcpkg-static.sln`) in `Visual Studio`, select configuration (`Release` or `Debug`) and platform (`x64` or `x86`) and build it. +2. Open the provided solution (`msvc-full-features\Cataclysm-vcpkg-static.sln`) in `Visual Studio`, select configuration (`Release` or `Debug`) and platform (`x64` or `x86`) and build it. All necessary dependencies will be built and cached for future use by vcpkg automatically. **Note**: This will compile release version with Sound, Tiles and Localization support (language files won't be automatically compiled). 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/doc/JSON_INFO.md b/doc/JSON_INFO.md index 138b3ed9092ba..31f94c7a94d38 100644 --- a/doc/JSON_INFO.md +++ b/doc/JSON_INFO.md @@ -442,6 +442,7 @@ This section describes each json file and their contents. Each json has their ow | squeamish_penalty | (_optional_) Mood effect of wearing filthy clothing on this part. (default: `0`) | stat_hp_mods | (_optional_) Values modifiying hp_max of this part following this formula: `hp_max += int_mod*int_max + dex_mod*dex_max + str_mod*str_max + per_mod*per_max + health_mod*get_healthy()` with X_max being the unmodifed value of the X stat and get_healthy() being the hidden health stat of the character. | bionic_slots | (_optional_) How many bionic slots does this part have. +| is_limb | (_optional_) Is this bodypart a limb. (default: `false`) ```C++ { diff --git a/doc/NPCs.md b/doc/NPCs.md index 6248dce2a2aaa..b7c40527e44cc 100644 --- a/doc/NPCs.md +++ b/doc/NPCs.md @@ -426,7 +426,7 @@ Effect | Description `u_add_trait: trait_string`
`npc_add_trait: trait_string` | Your character or the NPC will gain the trait. `u_lose_effect: effect_string`
`npc_lose_effect: effect_string` | Your character or the NPC will lose the effect if they have it. `u_lose_trait: trait_string`
`npc_lose_trait: trait_string` | Your character or the NPC will lose the trait. -`u_add_var, npc_add_var`: `var_name, type: type_str`, `context: context_str`, `value: value_str` | Your character or the NPC will store `value_str` as a variable that can be later retrieved by `u_has_var` or `npc_has_var`. `npc_add_var` can be used to store arbitrary local variables, and `u_add_var` can be used to store arbitrary "global" variables, and should be used in preference to setting effects. +`u_add_var, npc_add_var`: `var_name, type: type_str`, `context: context_str`, either `value: value_str` or `time: true` | Your character or the NPC will store `value_str` as a variable that can be later retrieved by `u_has_var` or `npc_has_var`. `npc_add_var` can be used to store arbitrary local variables, and `u_add_var` can be used to store arbitrary "global" variables, and should be used in preference to setting effects. If `time` is used instead of `value_str`, then the current turn of the game is stored. `u_lose_var`, `npc_lose_var`: `var_name`, `type: type_str`, `context: context_str` | Your character or the NPC will clear any stored variable that has the same `var_name`, `type_str`, and `context_str`. `u_adjust_var, npc_adjust_var`: `var_name, type: type_str`, `context: context_str`, `adjustment: adjustment_num` | Your character or the NPC will adjust the stored variable by `adjustment_num`. `barber_hair` | Opens a menu allowing the player to choose a new hair style. @@ -554,6 +554,7 @@ Condition | Type | Description `"u_has_any_trait"`
`"npc_has_any_trait"` | array | `true` if the player character or NPC has any trait or mutation in the array. Used to check multiple specific traits. `"u_has_var"`, `"npc_has_var"` | string | `"type": type_str`, `"context": context_str`, and `"value": value_str` are required fields in the same dictionary as `"u_has_var"` or `"npc_has_var"`.
`true` is the player character or NPC has a variable set by `"u_add_var"` or `"npc_add_var"` with the string, `type_str`, `context_str`, and `value_str`. `"u_compare_var"`, `"npc_compare_var"` | dictionary | `"type": type_str`, `"context": context_str`, `"op": op_str`, `"value": value_num` are required fields, referencing a var as in `"u_add_var"` or `"npc_add_var"`.
`true` if the player character or NPC has a stored variable that is true for the provided operator `op_str` (one of `==`, `!=`, `<`, `>`, `<=`, `>=`) and value. +`"u_compare_time_since_var"`, `"npc_compare_time_since_var_"` | dictionary | `"type": type_str`, `"context": context_str`, `"op": op_str`, `"time": time_string` are required fields, referencing a var as in `"u_add_var"` or `"npc_add_var"`.
`true` if the player character or NPC has a stored variable and the current turn and that value (converted to a time point) plus the time_string is true for the provided operator `op_str` (one of `==`, `!=`, `<`, `>`, `<=`, `>=`). *example*: `{ "u_compare_time_since_var": "test", "type": "test", "context": "var_time_test", "op": ">", "time": "3 days" }` returns true if the player character has a "test", "test", "var_time_test" variable and the current turn is greater than that value plus 3 days' worth of turns. `"u_has_strength"`
`"npc_has_strength"` | int | `true` if the player character's or NPC's strength is at least the value of `u_has_strength` or `npc_has_strength`. `"u_has_dexterity"`
`"npc_has_dexterity"` | int | `true` if the player character's or NPC's dexterity is at least the value of `u_has_dexterity` or `npc_has_dexterity`. `"u_has_intelligence"`
`"npc_has_intelligence"` | int | `true` if the player character's or NPC's intelligence is at least the value of `u_has_intelligence` or `npc_has_intelligence`. diff --git a/doc/REGION_SETTINGS.md b/doc/REGION_SETTINGS.md index 4781f29931c00..584f7bb7c913c 100644 --- a/doc/REGION_SETTINGS.md +++ b/doc/REGION_SETTINGS.md @@ -494,6 +494,7 @@ The **weather** section defines the base weather attributes used for the region. | `base_wind` | Base wind for the region in mph units. Roughly the yearly average. | | `base_wind_distrib_peaks` | How high the wind peaks can go. Higher values produce windier days. | | `base_wind_season_variation` | How the wind varies with season. Lower values produce more variation | +| `weather_types` | Ids of the weather types allowed in this region. When choosing weather they will be iterated over in the order they are listed and the last valid entry will be the weather. | ### Example @@ -507,7 +508,23 @@ The **weather** section defines the base weather attributes used for the region. "base_wind": 5.7, "base_wind_distrib_peaks": 30, "base_wind_season_variation": 64, - "base_acid": 0.0 + "base_acid": 0.0, + "weather_types": [ + "clear", + "sunny", + "cloudy", + "light_drizzle", + "drizzle", + "rain", + "thunder", + "lightning", + "acid_drizzle", + "acid_rain", + "flurries", + "snowing", + "snowstorm" + ] + }, } } ``` 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 diff --git a/doc/WEATHER_TYPE.md b/doc/WEATHER_TYPE.md new file mode 100644 index 0000000000000..1cb25b36fad84 --- /dev/null +++ b/doc/WEATHER_TYPE.md @@ -0,0 +1,77 @@ +## Weather_type + +Each weather type is a type of weather that occurs, its effects and what causes it. The only required entries are null and clear. + + +##Fields + +| Identifier | Description | +| ------------------------------ | --------------------------------------------------------------------- | +| `name` | UI name of weather type. | +| `color` | UI color of weather type. | +| `map_color` | Map color of weather type. | +| `glyph` | Map glyph of weather type. | +| `ranged_penalty` | Penalty to ranged attacks. | +| `sight_penalty` | Penalty to per-square visibility, applied in transparency map. | +| `light_modifier` | modification to ambient light. | +| `sound_attn` | Sound attenuation of a given weather type. | +| `dangerous` | If true, our activity gets interrupted. | +| `precip` | Amount of associated precipitation. Valid values are: none, very_light, light and heavy | +| `rains` | Whether said precipitation falls as rain. | +| `acidic` | Whether said precipitation is acidic. | +| `tiles_animation` | Optional, name of the tiles animation to use | +| `sound_category` | Optional, if playing sound effects what to use. Valid values are: silent, drizzle, rainy, thunder, flurries, + snowstorm and snow. | +| `sun_intensity` | Strength of the sun. Valid values are: none, light, normal, and high | +| `weather_animation` | Optional, Information controlling weather animations. Members: factor, color and glyph | +| `effects` | String, int pair array for the effects the weather has. At present wet, thunder, lightning, light_acid, and acid are supported. | + `wet` | wets player by int amount + `thunder` | thunder sound with chance 1 in int + `lightening` | 1 in int chance of sound plus message and possible super charging electric fields + `light_acid` | causes pain unless waterproof + `acid_rain` | causes more pain unless waterproof + +| `requirements` | Optional, is what determines what weather it is. All members are optional. + When checking what weather it is it loops through the entries in order and uses the last one to succeed. | + + `pressure_min` + `pressure_max` + `humidity_min` + `humidity_max` + `temperature_min` + `temperature_max` + `windpower_min` + `windpower_max` + | These are all minimum and maximum values for which the weather will occur. I.e it will only rain if its humid enough | + + `humidity_and_pressure` | if there are pressure and humidity requirements are they both required or just one | + `acidic` | does this require acidic precipitation | + `time` | Valid values are: day, night, and both. | + `required_weathers` | a string array of possible weathers it is at this point in the loop. i.e. rain can only happen if the conditions for clouds light drizzle or drizzle are present | + +### Example + +```json +{ + "id": "lightning", + "type": "weather_type", + "name": "Lightning Storm", + "color": "c_yellow", + "map_color": "h_yellow", + "glyph": "%", + "ranged_penalty": 4, + "sight_penalty": 1.25, + "light_modifier": -45, + "sound_attn": 8, + "dangerous": false, + "precip": "heavy", + "rains": true, + "acidic": false, + "effects": [ { "name": "thunder", "intensity": 50 }, { "name": "lightning", "intensity": 600 } ], + "tiles_animation": "weather_rain_drop", + "weather_animation": { "factor": 0.04, "color": "c_light_blue", "glyph": "," }, + "sound_category": "thunder", + "sun_intensity": "none", + "requirements": { "pressure_max": 990, "required_weathers": [ "thunder" ] } +} +``` diff --git a/gfx/UltimateCataclysmDemo/giant.png b/gfx/UltimateCataclysmDemo/giant.png index 45f379069c224..2c8dc0a9b2a3a 100644 Binary files a/gfx/UltimateCataclysmDemo/giant.png and b/gfx/UltimateCataclysmDemo/giant.png differ diff --git a/gfx/UltimateCataclysmDemo/large.png b/gfx/UltimateCataclysmDemo/large.png index f1f3c6402fb19..ee9ab1ca7fff2 100644 Binary files a/gfx/UltimateCataclysmDemo/large.png and b/gfx/UltimateCataclysmDemo/large.png differ diff --git a/gfx/UltimateCataclysmDemo/normal.png b/gfx/UltimateCataclysmDemo/normal.png index 28cf1b0d0ec06..cbc7a756646a4 100644 Binary files a/gfx/UltimateCataclysmDemo/normal.png and b/gfx/UltimateCataclysmDemo/normal.png differ diff --git a/gfx/UltimateCataclysmDemo/tall.png b/gfx/UltimateCataclysmDemo/tall.png index 6414703079b69..3aa0cecd810b7 100644 Binary files a/gfx/UltimateCataclysmDemo/tall.png and b/gfx/UltimateCataclysmDemo/tall.png differ diff --git a/gfx/UltimateCataclysmDemo/tile_config.json b/gfx/UltimateCataclysmDemo/tile_config.json index ffe0c7da8b823..08a580d2482dc 100644 --- a/gfx/UltimateCataclysmDemo/tile_config.json +++ b/gfx/UltimateCataclysmDemo/tile_config.json @@ -1 +1 @@ -{"tile_info": [{"width": 32, "height": 32}], "tiles-new": [{"file": "normal.png", "tiles": [{"id": "f_indoor_plant", "fg": 3}, {"id": "f_indoor_plant_y", "fg": 1}, {"id": ["f_indoor_plant_y_season_autumn", "f_indoor_plant_y_season_winter"], "fg": 2}, {"id": "f_recycle_bin", "fg": 4}, {"id": "f_armchair", "fg": 5}, {"id": "f_wreckage", "fg": 6}, {"id": "f_gunsafe_ml", "fg": 8}, {"id": "f_gunsafe_mj", "fg": 7}, {"id": "f_gun_safe_el", "fg": 9}, {"id": "f_mutpoppy", "fg": 10}, {"id": "f_planter_mature", "multitile": true, "fg": 13, "additional_tiles": [{"id": "center", "fg": 14}, {"id": "corner", "fg": [23, 26, 22, 16]}, {"id": "t_connection", "fg": [19, 18, 25, 15]}, {"id": "edge", "fg": [20, 21]}, {"id": "end_piece", "fg": [17, 12, 11, 24]}, {"id": "unconnected", "fg": 13}]}, {"id": "f_boulder_small", "fg": 27}, {"id": "f_dandelion", "fg": 28}, {"id": "f_planter", "multitile": true, "fg": 37, "additional_tiles": [{"id": "center", "fg": 32}, {"id": "corner", "fg": [42, 36, 39, 35]}, {"id": "t_connection", "fg": [29, 40, 44, 43]}, {"id": "edge", "fg": [41, 38]}, {"id": "end_piece", "fg": [33, 34, 30, 31]}, {"id": "unconnected", "fg": 37}]}, {"id": "f_bathtub", "multitile": true, "fg": 54, "additional_tiles": [{"id": "center", "fg": 55}, {"id": "corner", "fg": [53, 50, 48, 56]}, {"id": "t_connection", "fg": [60, 52, 51, 47]}, {"id": "edge", "fg": [59, 57]}, {"id": "end_piece", "fg": [45, 49, 46, 58]}, {"id": "unconnected", "fg": 54}]}, {"id": "f_sofa", "multitile": true, "fg": 65, "additional_tiles": [{"id": "center", "fg": 66}, {"id": "corner", "fg": [74, 69, 76, 62]}, {"id": "t_connection", "fg": [70, 68, 61, 63]}, {"id": "edge", "fg": [75, 73]}, {"id": "end_piece", "fg": [64, 67, 72, 71]}, {"id": "unconnected", "fg": 65}]}, {"id": "f_alien_anemone", "fg": 78}, {"id": "f_alien_table", "fg": 77}, {"id": "f_filing_cabinet", "fg": 79}, {"id": "f_toilet", "fg": 80}, {"id": "f_ash", "fg": 81}, {"id": "f_desk", "multitile": true, "fg": 82, "additional_tiles": [{"id": "center", "fg": 91}, {"id": "corner", "fg": [87, 84, 92, 86]}, {"id": "t_connection", "fg": [90, 89, 85, 95]}, {"id": "edge", "fg": [96, 83]}, {"id": "end_piece", "fg": [97, 94, 93, 88]}, {"id": "unconnected", "fg": 82}]}, {"id": "f_trashcan", "fg": 98}, {"id": "f_entertainment_center", "fg": 99}, {"id": "f_grave_stone", "fg": [{"weight": 1, "sprite": 100}, {"weight": 1, "sprite": 101}]}, {"id": "f_rubble", "fg": 102}, {"id": "f_rubble_rock", "fg": 103}, {"id": "f_sign", "fg": 104}, {"id": "f_flower_spurge", "fg": 105}, {"id": "f_planter_harvest", "multitile": true, "fg": 117, "additional_tiles": [{"id": "center", "fg": 110}, {"id": "corner", "fg": [115, 120, 111, 118]}, {"id": "t_connection", "fg": [112, 113, 121, 107]}, {"id": "edge", "fg": [106, 108]}, {"id": "end_piece", "fg": [116, 114, 109, 119]}, {"id": "unconnected", "fg": 117}]}, {"id": "f_cardboard_box", "fg": 122}, {"id": "f_datura", "fg": 123}, {"id": "f_planter_seedling", "multitile": true, "fg": 131, "additional_tiles": [{"id": "center", "fg": 132}, {"id": "corner", "fg": [134, 125, 136, 128]}, {"id": "t_connection", "fg": [124, 133, 130, 126]}, {"id": "edge", "fg": [139, 137]}, {"id": "end_piece", "fg": [127, 135, 129, 138]}, {"id": "unconnected", "fg": 131}]}, {"id": "f_table", "multitile": true, "fg": 140, "additional_tiles": [{"id": "center", "fg": 154}, {"id": "corner", "fg": [148, 151, 142, 152]}, {"id": "t_connection", "fg": [141, 147, 144, 153]}, {"id": "edge", "fg": [143, 150]}, {"id": "end_piece", "fg": [146, 149, 155, 145]}, {"id": "unconnected", "fg": 140}]}, {"id": "f_boulder_large", "fg": 156}, {"id": "f_flower_tulip", "fg": [{"weight": 1, "sprite": 158}, {"weight": 2, "sprite": 157}]}, {"id": "f_cupboard", "multitile": true, "fg": 174, "additional_tiles": [{"id": "center", "fg": 173}, {"id": "corner", "fg": [167, 164, 166, 160]}, {"id": "t_connection", "fg": [163, 172, 168, 169]}, {"id": "edge", "fg": [171, 162]}, {"id": "end_piece", "fg": [159, 161, 165, 170]}, {"id": "unconnected", "fg": 174}]}, {"id": "f_bluebell", "fg": [{"weight": 1, "sprite": 175}, {"weight": 2, "sprite": 176}]}, {"id": "f_bed", "multitile": true, "fg": 182, "additional_tiles": [{"id": "center", "fg": 178}, {"id": "corner", "fg": [188, 187, 181, 186]}, {"id": "t_connection", "fg": [190, 185, 191, 189]}, {"id": "edge", "fg": [192, 180]}, {"id": "end_piece", "fg": [177, 183, 179, 184]}, {"id": "unconnected", "fg": 182}]}, {"id": "f_firering", "fg": 193}, {"id": "f_bench", "multitile": true, "fg": 207, "additional_tiles": [{"id": "center", "fg": 200}, {"id": "corner", "fg": [203, 205, 197, 196]}, {"id": "t_connection", "fg": [194, 201, 204, 206]}, {"id": "edge", "fg": [198, 202]}, {"id": "end_piece", "fg": [208, 199, 209, 195]}, {"id": "unconnected", "fg": 207}]}, {"id": "f_mailbox", "fg": 210}, {"id": "f_grave_stone_old", "fg": [{"weight": 1, "sprite": 212}, {"weight": 1, "sprite": 211}]}, {"id": "f_rack_wood", "fg": 213}, {"id": "f_boulder_medium", "fg": 214}, {"id": "f_chamomile", "fg": 215}, {"id": "f_hay", "fg": 216}, {"id": "f_counter", "multitile": true, "fg": 223, "additional_tiles": [{"id": "center", "fg": 219}, {"id": "corner", "fg": [228, 220, 226, 224]}, {"id": "t_connection", "fg": [225, 218, 232, 227]}, {"id": "edge", "fg": [222, 221]}, {"id": "end_piece", "fg": [229, 231, 230, 217]}, {"id": "unconnected", "fg": 223}]}, {"id": "f_air_conditioner", "fg": 233}, {"id": "f_chair", "fg": 234}, {"id": "f_stool", "fg": 235}, {"id": "f_dahlia", "fg": [{"weight": 1, "sprite": 236}, {"weight": 2, "sprite": 237}]}, {"id": ["f_displaycase"], "fg": 238}, {"id": "f_rack", "fg": 239}, {"id": "vp_door_trunk", "fg": 240, "rotates": true, "multitile": true, "additional_tiles": [{"id": "open", "fg": 241}]}, {"id": ["vp_headlight", "vp_headlight_reinforced"], "fg": 242}, {"id": "vp_door", "fg": 243, "rotates": true, "multitile": true, "additional_tiles": [{"id": "open", "fg": 244}]}, {"id": "vp_halfboard_ne", "fg": 248, "rotates": true}, {"id": "vp_halfboard_sw", "fg": 250, "rotates": true}, {"id": "vp_halfboard_se", "fg": 245, "rotates": true}, {"id": "vp_halfboard_cover", "fg": 247, "rotates": true}, {"id": "vp_halfboard_vertical_2", "fg": 252, "rotates": true}, {"id": "vp_halfboard_horizontal_2", "fg": 249, "rotates": true}, {"id": "vp_halfboard_vertical", "fg": 246, "rotates": true}, {"id": "vp_halfboard_nw", "fg": 253, "rotates": true}, {"id": "vp_halfboard_horizontal", "fg": 251, "rotates": true}, {"id": "vp_windshield", "fg": 257, "rotates": true, "multitile": true, "additional_tiles": [{"id": "center", "fg": [254, 258, 255, 256]}, {"id": "edge", "fg": 257}, {"id": "unconnected", "fg": 257}, {"id": "end_piece", "fg": [254, 258, 255, 256]}, {"id": "t_connection", "fg": [254, 258, 255, 256]}, {"id": "corner", "fg": [254, 258, 255, 256]}]}, {"id": "powder_candy", "fg": 259}, {"id": "joint", "fg": 261}, {"id": "joint_lit", "fg": 262}, {"id": "joint_roach", "fg": 260}, {"id": "bag_canvas", "fg": 263}, {"id": "wood_panel", "fg": 264}, {"id": "jar_glass", "fg": 265}, {"id": "shot_hull", "fg": 266}, {"id": "wrench", "fg": 267}, {"id": "needle_bone", "fg": 270}, {"id": "needle_curved", "fg": 268}, {"id": "needle_wood", "fg": 269}, {"id": "pinecone", "fg": 271}, {"id": "ash", "fg": 272}, {"id": "lighter", "fg": [{"weight": 1, "sprite": 273}, {"weight": 1, "sprite": 275}, {"weight": 1, "sprite": 274}]}, {"id": "chips", "fg": 276}, {"id": "rifle_flintlock", "fg": 277}, {"id": "balclava", "fg": 301}, {"id": "beret", "fg": 290}, {"id": "boots", "fg": 317}, {"id": "boxer_briefs", "fg": 296}, {"id": "boxer_shorts", "fg": 282}, {"id": "boy_shorts", "fg": 287}, {"id": "bra", "fg": 309}, {"id": "briefs", "fg": 293}, {"id": "corset", "fg": 306}, {"id": "cowboy_hat", "fg": 318}, {"id": "dress_shoes", "fg": 286}, {"id": "hat_ball", "fg": 300}, {"id": "hat_cotton", "fg": 299}, {"id": "hat_fur", "fg": 303}, {"id": "hat_knit", "fg": 281}, {"id": "hat_noise_cancelling", "fg": 326}, {"id": "helmet_army", "fg": 284}, {"id": "helmet_barbute", "fg": 315}, {"id": "helmet_chitin", "fg": 278}, {"id": "helmet_kabuto", "fg": 314}, {"id": "hoodie", "fg": 313}, {"id": "jeans", "fg": 308}, {"id": "longshirt", "fg": 295}, {"id": "maid_dress", "fg": 280}, {"id": "maid_hat", "fg": 294}, {"id": "mask_dust", "fg": 305}, {"id": "panties", "fg": 291}, {"id": "pants", "fg": 288}, {"id": "pants_cargo", "fg": 302}, {"id": "polo_shirt", "fg": 310}, {"id": "ragpouch", "fg": 324}, {"id": "sneakers", "fg": 283}, {"id": "socks", "fg": 298}, {"id": "stockings", "fg": 289}, {"id": "sweater", "fg": 311}, {"id": "sweatshirt", "fg": 285}, {"id": "tank_top", "fg": 325}, {"id": "tshirt", "fg": 307}, {"id": "turban", "fg": 319}, {"id": "undershirt", "fg": 322}, {"id": "coat_lab", "fg": 321}, {"id": "coat_rain", "fg": 292}, {"id": "sports_bra", "fg": 297}, {"id": "skirt", "fg": 279}, {"id": "jacket_light", "fg": 312}, {"id": "jacket_army", "fg": 323}, {"id": "hat_hard", "fg": 320}, {"id": "striped_pants", "fg": 304}, {"id": "striped_shirt", "fg": 316}, {"id": "", "fg": []}, {"id": "bat_metal", "fg": 327}, {"id": "hacksaw", "fg": 328}, {"id": "crucible_clay", "fg": 329}, {"id": "many_years_old_newspaper", "fg": 334}, {"id": "months_old_newspaper", "fg": 330}, {"id": "newest_newspaper", "fg": 333}, {"id": "one_year_old_newspaper", "fg": 332}, {"id": "weeks_old_newspaper", "fg": 331}, {"id": "pointy_stick", "fg": 343}, {"id": "spear_wood", "fg": 342}, {"id": "spear_spike", "fg": 337}, {"id": "spear_knife", "fg": 340}, {"id": "spear_knife_superior", "fg": 341}, {"id": "spear_pipe", "fg": 335}, {"id": "spear_rebar", "fg": 339}, {"id": "spear_steel", "fg": 336}, {"id": "spear_copper", "fg": 338}, {"id": "hickory_root", "fg": 344}, {"id": "flashlight", "fg": 345}, {"id": "heavy_flashlight", "fg": 346}, {"id": "can_drink", "fg": 347}, {"id": "50_casing", "fg": 348}, {"id": "apple", "fg": 350}, {"id": "banana", "fg": 351}, {"id": "broccoli", "fg": 359}, {"id": "corn", "fg": 362}, {"id": "cucumber", "fg": 355}, {"id": "egg_bird", "fg": 361}, {"id": "grapes", "fg": 349}, {"id": "lemon", "fg": 352}, {"id": "onion", "fg": 360}, {"id": "orange", "fg": 354}, {"id": "pear", "fg": 356}, {"id": "potato", "fg": 353}, {"id": "pumpkin", "fg": 358}, {"id": "tomato", "fg": 357}, {"id": "sewing_kit", "fg": 363}, {"id": "jug_plastic", "fg": 364}, {"id": "pot", "fg": 365}, {"id": "backpack", "fg": 366}, {"id": "cattlefodder", "fg": 367}, {"id": "cup_plastic", "fg": 368}, {"id": "knife_meat_cleaver", "fg": 369}, {"id": "fire_ax", "fg": 370}, {"id": "ax", "fg": 372}, {"id": "hatchet", "fg": 371}, {"id": ["glock_17", "glock_19", "glock_18c", "glock_22", "glock_31"], "fg": 373}, {"id": "straw_pile", "fg": 374}, {"id": "thread", "fg": 375}, {"id": "wheat", "fg": 376}, {"id": "pan", "fg": 377}, {"id": "bottle_glass", "fg": 378}, {"id": "nail", "fg": 379}, {"id": "juniper", "fg": 380}, {"id": "pool_ball", "fg": 381}, {"id": "crowbar", "fg": 382}, {"id": "rag", "fg": 383}, {"id": "id_industrial", "fg": 384}, {"id": "corpse_generic_human", "fg": 385}, {"id": "chainsaw_off", "fg": 386}, {"id": "teapot", "fg": 387}, {"id": "mp5mag", "fg": 388}, {"id": "meat", "fg": 389}, {"id": "cig_butt", "fg": 390}, {"id": "usb_drive", "fg": 391}, {"id": "1st_aid", "fg": 392}, {"id": "welder_crude", "fg": 393}, {"id": "nailbat", "fg": 394}, {"id": "remington_870", "fg": 395}, {"id": "sheet_metal_small", "fg": 396}, {"id": "screwdriver", "fg": 397}, {"id": "primitive_hammer", "fg": 398}, {"id": "steel_lump", "fg": 399}, {"id": "string_6", "fg": 400}, {"id": "saw", "fg": 401}, {"id": "aspirin", "fg": 417}, {"id": "bandages", "fg": 403}, {"id": "syringe", "fg": 409}, {"id": "antibiotics", "fg": 416}, {"id": "weak_antibiotic", "fg": 402}, {"id": "strong_antibiotic", "fg": 410}, {"id": "vitamins", "fg": 407}, {"id": "gummy_vitamins", "fg": 408}, {"id": "calcium_tablet", "fg": 413}, {"id": "oxycodone", "fg": 411}, {"id": "tramadol", "fg": 412}, {"id": "codeine", "fg": 406}, {"id": "prussian_blue", "fg": 405}, {"id": "iodine", "fg": 414}, {"id": "antiparasitic", "fg": 415}, {"id": "antifungal", "fg": 404}, {"id": "can_drink_unsealed", "fg": 418}, {"id": "9mm_casing", "fg": 419}, {"id": "wrapper", "fg": 420}, {"id": "rebar", "fg": 421}, {"id": "glass_shard", "fg": 422}, {"id": "pot_copper", "fg": 423}, {"id": "40mm_casing", "fg": 424}, {"id": "nailboard", "fg": 425}, {"id": "scissors", "fg": 426}, {"id": "ak74", "fg": 427}, {"id": "coffeemaker", "fg": 428}, {"id": "blanket", "fg": 430}, {"id": "down_blanket", "fg": 429}, {"id": "longbow", "fg": 431}, {"id": "pneumatic_shotgun", "fg": 432}, {"id": "thermos", "fg": 433}, {"id": "box_cigarette", "fg": 434}, {"id": "m79", "fg": 435}, {"id": "stick", "fg": 436}, {"id": "steel_chunk", "fg": 437}, {"id": "television", "fg": 438}, {"id": "shovel", "fg": 439}, {"id": "id_science", "fg": 440}, {"id": "cable", "fg": 441}, {"id": "pipe", "fg": 442}, {"id": "bottle_plastic", "fg": 443}, {"id": "fp_loyalty_card", "fg": 444}, {"id": "id_military", "fg": 445}, {"id": "water", "fg": 447}, {"id": "water_clean", "fg": 448}, {"id": "blood", "fg": 446}, {"id": "filter_air", "fg": 449}, {"id": "welder", "fg": 450}, {"id": "broom", "fg": 451}, {"id": "rolling_pin", "fg": 452}, {"id": "makeshift_crowbar", "fg": 453}, {"id": "bat", "fg": 454}, {"id": "wood_sheet", "fg": 455}, {"id": "jar_3l_glass_sealed", "fg": 456}, {"id": "plastic_sheet", "fg": 457}, {"id": "box_large", "fg": 458}, {"id": "knife_vegetable_cleaver", "fg": 459}, {"id": "arming_sword", "fg": 460}, {"id": "crossbow", "fg": 461}, {"id": "pot_canning", "fg": 462}, {"id": "sheet_metal", "fg": 463}, {"id": "brick", "fg": 464}, {"id": "wire", "fg": 465}, {"id": "2x4", "fg": 466}, {"id": ["seed_hops", "seed_blackberries", "seed_blueberries", "coffee_pod", "seed_wheat", "roasted_coffee_bean", "seed_chamomile", "coffee_bean", "seed_celery", "fried_seeds", "seed_buckwheat", "seed_oats", "seed_mushroom_morel", "datura_seed", "seed_mushroom", "seed_rhubarb", "seed_wild_herbs", "seed_raw_dandelion", "seed_veggy_wild", "seed_chili_pepper", "seed_dogbane", "seed_mugwort", "seed_bee_balm", "seed_flower", "seed_sunflower", "seed_thyme", "seed_canola", "seed_pumpkin", "seed_beans", "seed_lentils", "soybean_seed", "marloss_seed", "fungal_seeds", "seed_weed", "seed_potato_raw", "seed_cucumber", "seed_corn", "seed_carrot", "garlic_clove", "seed_cactus", "seed_wildcarrot", "seed_cattail", "seed_chicory", "seed_salsify_raw", "seed_dahlia", "seed_garlic", "seed_broccoli", "seed_onion", "seed_zucchini", "seed_cotton_boll", "seed_tomato", "seed_rose", "seed_lettuce", "seed_cabbage", "seed_sugar_beet", "seed_tobacco", "seed_barley", "seed_grapes", "seed_strawberries", "seed_raspberries", "seed_cranberries", "seed_huckleberries", "seed_mulberries", "seed_elderberries"], "fg": 467}, {"id": "acorns", "fg": 468}, {"id": "slingshot", "fg": 469}, {"id": "bottle_plastic_small", "fg": 470}, {"id": "sharp_rock", "fg": 471}, {"id": "bowl_plastic", "fg": 472}, {"id": "contacts", "fg": 473}, {"id": "matches", "fg": 474}, {"id": "box_small", "fg": 475}, {"id": "plastic_shopping_bag", "fg": 476}, {"id": "pillow", "fg": 478}, {"id": "down_pillow", "fg": 477}, {"id": "mop", "fg": 479}, {"id": "board_trap", "fg": 480}, {"id": "can_food", "fg": 481}, {"id": "scrap", "fg": 482}, {"id": "string_36", "fg": 483}, {"id": "file", "fg": 484}, {"id": "box_medium", "fg": 485}, {"id": "paperback_novel", "fg": 507}, {"id": "novel_adventure", "fg": 507}, {"id": "novel_buddy", "fg": 500}, {"id": "novel_coa", "fg": 489}, {"id": "novel_coa2", "fg": 490}, {"id": "novel_crime", "fg": 493}, {"id": "novel_crime2", "fg": 501}, {"id": "novel_drama", "fg": 497}, {"id": "novel_erotic", "fg": 505}, {"id": "novel_experimental", "fg": 496}, {"id": "novel_fantasy", "fg": 502}, {"id": "novel_horror", "fg": 503}, {"id": "novel_mystery", "fg": 506}, {"id": "novel_pulp", "fg": 494}, {"id": "novel_road", "fg": 491}, {"id": "novel_romance", "fg": 488}, {"id": "novel_samurai", "fg": 486}, {"id": "novel_satire", "fg": 498}, {"id": "novel_scifi", "fg": 510}, {"id": "novel_sports", "fg": 509}, {"id": "novel_spy", "fg": 511}, {"id": "novel_swash", "fg": 499}, {"id": "novel_thriller", "fg": 504}, {"id": "novel_tragedy", "fg": 495}, {"id": "novel_war", "fg": 508}, {"id": "novel_war2", "fg": 492}, {"id": "novel_western", "fg": 487}, {"id": "marble", "fg": 512}, {"id": "withered", "fg": 513}, {"id": "bolt_cf", "fg": 514}, {"id": "bolt_explosive", "fg": 522}, {"id": "bolt_metal", "fg": 517}, {"id": ["bolt_steel", "bolt_steel_bodkin", "bolt_steel_target"], "fg": 521}, {"id": ["bolt_wood", "bolt_crude", "bolt_simple_wood", "bolt_simple_small_game", "bolt_makeshift", "bolt_wood_bodkin", "bolt_wood_small_game"], "fg": 531}, {"id": "arrow_cf", "fg": 520}, {"id": "arrow_exploding", "fg": 515}, {"id": "arrow_field_point_fletched", "fg": 524}, {"id": "arrow_fire_hardened_fletched", "fg": 530}, {"id": "arrow_flammable", "fg": 528}, {"id": "arrow_flamming", "fg": 519}, {"id": "arrow_heavy_fire_hardened_fletched", "fg": 529}, {"id": "arrow_metal", "fg": 523}, {"id": "arrow_metal_sharpened_fletched", "fg": 516}, {"id": "arrow_plastic", "fg": 525}, {"id": "arrow_small_game_fletched", "fg": 526}, {"id": "arrow_wood", "fg": 527}, {"id": "arrow_wood_heavy", "fg": 518}, {"id": "rock", "fg": 532}, {"id": "knife_butcher", "fg": 533}, {"id": "stick_long", "fg": 534}, {"id": "log", "fg": 535}, {"id": "jar_glass_sealed", "fg": 536}, {"id": "jar_3l_glass", "fg": 537}, {"id": "knife_butter", "fg": 538}, {"id": "223_casing", "fg": 539}, {"id": "bwirebat", "fg": 540}, {"id": "bag_plastic", "fg": 541}, {"id": ["rifle_9mm", "rifle_3006", "rifle_45", "rifle_22", "rifle_40", "rifle_44", "rifle_38", "rifle_223"], "fg": 542}, {"id": "pipe_shotgun", "fg": 543}, {"id": "spoon", "fg": 544}, {"id": "hammer", "fg": 545}, {"id": "hickory_nut", "fg": 546}, {"id": "glass_plate", "fg": 547}, {"id": "cash_card", "fg": 548}, {"id": "fork", "fg": 549}, {"id": "splinter", "fg": 550}, {"id": "ar15", "fg": 551}, {"id": "pine_bough", "fg": 552}, {"id": "bag_zipper", "fg": 553}, {"id": "tailors_kit", "fg": 554}, {"id": "sponge", "fg": 555}, {"id": "t_strconc_floor", "multitile": true, "fg": 571, "additional_tiles": [{"id": "center", "fg": 568}, {"id": "corner", "fg": [556, 563, 565, 561]}, {"id": "t_connection", "fg": [557, 562, 566, 559]}, {"id": "edge", "fg": [567, 569]}, {"id": "end_piece", "fg": [570, 558, 564, 560]}, {"id": "unconnected", "fg": 571}]}, {"id": "t_tatami", "fg": [{"weight": 100, "sprite": 573}, {"weight": 100, "sprite": 572}]}, {"id": "t_floor_waxed_y", "multitile": true, "fg": 579, "additional_tiles": [{"id": "center", "fg": 585}, {"id": "corner", "fg": [582, 574, 578, 588]}, {"id": "t_connection", "fg": [580, 576, 587, 575]}, {"id": "edge", "fg": [583, 586]}, {"id": "end_piece", "fg": [584, 577, 581, 589]}, {"id": "unconnected", "fg": 579}]}, {"id": "t_pit_shallow", "fg": 590, "bg": [{"weight": 100, "sprite": 1233}, {"weight": 100, "sprite": 1235}]}, {"id": "t_pit_shallow_season_summer", "fg": 590, "bg": [{"weight": 100, "sprite": 1240}, {"weight": 100, "sprite": 1229}]}, {"id": "t_pit_shallow_season_autumn", "fg": 590, "bg": [{"weight": 100, "sprite": 1234}, {"weight": 100, "sprite": 1228}]}, {"id": "t_rock", "fg": 591}, {"id": "t_railroad_rubble", "fg": 592}, {"id": "t_gravel", "fg": 592}, {"id": "t_railroad_rubble_season_winter", "fg": 643}, {"id": "t_sand", "fg": 593}, {"id": "t_sand_season_winter", "fg": 643}, {"id": "t_floor_resin", "multitile": true, "fg": 596, "additional_tiles": [{"id": "center", "fg": 595}, {"id": "corner", "fg": [601, 608, 602, 598]}, {"id": "t_connection", "fg": [600, 594, 604, 603]}, {"id": "edge", "fg": [609, 597]}, {"id": "end_piece", "fg": [606, 607, 605, 599]}, {"id": "unconnected", "fg": 596}]}, {"id": "t_metal_floor", "multitile": true, "fg": 617, "additional_tiles": [{"id": "center", "fg": 611}, {"id": "corner", "fg": [619, 615, 613, 614]}, {"id": "t_connection", "fg": [625, 620, 622, 624]}, {"id": "edge", "fg": [623, 621]}, {"id": "end_piece", "fg": [610, 616, 612, 618]}, {"id": "unconnected", "fg": 617}]}, {"id": ["t_door_metal_locked", "t_door_metal_pickable"], "fg": 626}, {"id": "t_wall_b", "multitile": true, "fg": 630, "additional_tiles": [{"id": "center", "fg": 628}, {"id": "corner", "fg": [641, 634, 640, 632]}, {"id": "t_connection", "fg": [638, 633, 642, 636]}, {"id": "edge", "fg": [627, 629]}, {"id": "end_piece", "fg": [635, 631, 639, 637]}, {"id": "unconnected", "fg": 630}]}, {"id": "t_floor", "multitile": true, "fg": 655, "additional_tiles": [{"id": "center", "fg": 652}, {"id": "corner", "fg": [657, 654, 650, 658]}, {"id": "t_connection", "fg": [656, 645, 651, 659]}, {"id": "edge", "fg": [647, 648]}, {"id": "end_piece", "fg": [653, 649, 660, 646]}, {"id": "unconnected", "fg": 655}]}, {"id": "t_brick_wall", "multitile": true, "fg": 674, "additional_tiles": [{"id": "center", "fg": 676}, {"id": "corner", "fg": [666, 668, 667, 672]}, {"id": "t_connection", "fg": [662, 670, 673, 675]}, {"id": "edge", "fg": [669, 661]}, {"id": "end_piece", "fg": [664, 665, 671, 663]}, {"id": "unconnected", "fg": 674}]}, {"id": "t_wall_wood", "multitile": true, "fg": 684, "additional_tiles": [{"id": "center", "fg": 680}, {"id": "corner", "fg": [677, 686, 690, 688]}, {"id": "t_connection", "fg": [685, 692, 679, 682]}, {"id": "edge", "fg": [678, 689]}, {"id": "end_piece", "fg": [691, 681, 687, 683]}, {"id": "unconnected", "fg": 684}]}, {"id": ["t_linoleum_white", "t_linoleum_white_no_roof"], "multitile": true, "fg": 696, "additional_tiles": [{"id": "center", "fg": 708}, {"id": "corner", "fg": [703, 702, 693, 707]}, {"id": "t_connection", "fg": [695, 705, 701, 704]}, {"id": "edge", "fg": [706, 700]}, {"id": "end_piece", "fg": [694, 697, 699, 698]}, {"id": "unconnected", "fg": 696}]}, {"id": "t_fungus", "multitile": true, "fg": 721, "bg": 1233, "additional_tiles": [{"id": "center", "bg": 1233, "fg": 716}, {"id": "corner", "bg": 1233, "fg": [718, 722, 710, 711]}, {"id": "t_connection", "bg": 1233, "fg": [723, 709, 715, 717]}, {"id": "edge", "bg": 1233, "fg": [719, 713]}, {"id": "end_piece", "bg": 1233, "fg": [720, 712, 724, 714]}, {"bg": 1233, "id": "unconnected", "fg": 721}]}, {"id": "t_fungus_season_summer", "multitile": true, "fg": 721, "bg": 1240, "additional_tiles": [{"id": "center", "bg": 1240, "fg": 716}, {"id": "corner", "bg": 1240, "fg": [718, 722, 710, 711]}, {"id": "t_connection", "bg": 1240, "fg": [723, 709, 715, 717]}, {"id": "edge", "bg": 1240, "fg": [719, 713]}, {"id": "end_piece", "bg": 1240, "fg": [720, 712, 724, 714]}, {"bg": 1240, "id": "unconnected", "fg": 721}]}, {"id": "t_fungus_season_autumn", "multitile": true, "fg": 721, "bg": 1234, "additional_tiles": [{"id": "center", "bg": 1234, "fg": 716}, {"id": "corner", "bg": 1234, "fg": [718, 722, 710, 711]}, {"id": "t_connection", "bg": 1234, "fg": [723, 709, 715, 717]}, {"id": "edge", "bg": 1234, "fg": [719, 713]}, {"id": "end_piece", "bg": 1234, "fg": [720, 712, 724, 714]}, {"bg": 1234, "id": "unconnected", "fg": 721}]}, {"id": "t_fungus_season_winter", "multitile": true, "fg": 721, "bg": 643, "additional_tiles": [{"id": "center", "bg": 643, "fg": 716}, {"id": "corner", "bg": 643, "fg": [718, 722, 710, 711]}, {"id": "t_connection", "bg": 643, "fg": [723, 709, 715, 717]}, {"id": "edge", "bg": 643, "fg": [719, 713]}, {"id": "end_piece", "bg": 643, "fg": [720, 712, 724, 714]}, {"bg": 643, "id": "unconnected", "fg": 721}]}, {"id": "t_carpet_red", "multitile": true, "fg": 735, "additional_tiles": [{"id": "center", "fg": 732}, {"id": "corner", "fg": [734, 730, 733, 736]}, {"id": "t_connection", "fg": [727, 731, 726, 737]}, {"id": "edge", "fg": [725, 740]}, {"id": "end_piece", "fg": [738, 739, 729, 728]}, {"id": "unconnected", "fg": 735}]}, {"id": ["t_water_moving_sh", "t_swater_moving_sh"], "multitile": true, "fg": 743, "bg": 1233, "additional_tiles": [{"id": "center", "bg": 1233, "fg": 742}, {"id": "corner", "bg": 1233, "fg": [754, 741, 745, 747]}, {"id": "t_connection", "bg": 1233, "fg": [756, 748, 746, 755]}, {"id": "edge", "bg": 1233, "fg": [751, 752]}, {"id": "end_piece", "bg": 1233, "fg": [753, 744, 749, 750]}, {"bg": 1233, "id": "unconnected", "fg": 743}]}, {"id": ["t_water_moving_sh_season_summer", "t_swater_moving_sh_season_summer"], "multitile": true, "fg": 743, "bg": 1240, "additional_tiles": [{"id": "center", "bg": 1240, "fg": 742}, {"id": "corner", "bg": 1240, "fg": [754, 741, 745, 747]}, {"id": "t_connection", "bg": 1240, "fg": [756, 748, 746, 755]}, {"id": "edge", "bg": 1240, "fg": [751, 752]}, {"id": "end_piece", "bg": 1240, "fg": [753, 744, 749, 750]}, {"bg": 1240, "id": "unconnected", "fg": 743}]}, {"id": ["t_water_moving_sh_season_autumn", "t_swater_moving_sh_season_autumn"], "multitile": true, "fg": 743, "bg": 1234, "additional_tiles": [{"id": "center", "bg": 1234, "fg": 742}, {"id": "corner", "bg": 1234, "fg": [754, 741, 745, 747]}, {"id": "t_connection", "bg": 1234, "fg": [756, 748, 746, 755]}, {"id": "edge", "bg": 1234, "fg": [751, 752]}, {"id": "end_piece", "bg": 1234, "fg": [753, 744, 749, 750]}, {"bg": 1234, "id": "unconnected", "fg": 743}]}, {"id": ["t_water_moving_sh_season_winter", "t_swater_moving_sh_season_winter"], "multitile": true, "fg": 743, "bg": 643, "additional_tiles": [{"id": "center", "bg": 643, "fg": 742}, {"id": "corner", "bg": 643, "fg": [754, 741, 745, 747]}, {"id": "t_connection", "bg": 643, "fg": [756, 748, 746, 755]}, {"id": "edge", "bg": 643, "fg": [751, 752]}, {"id": "end_piece", "bg": 643, "fg": [753, 744, 749, 750]}, {"bg": 643, "id": "unconnected", "fg": 743}]}, {"id": ["t_window", "t_window_alarm"], "fg": 758}, {"id": "t_window_empty", "fg": 762}, {"id": "t_window_domestic", "fg": 761}, {"id": "t_window_open", "fg": 760}, {"id": "t_window_no_curtains", "fg": 759}, {"id": "t_window_no_curtains_open", "fg": 757}, {"id": "t_fence_wire", "multitile": true, "fg": 771, "additional_tiles": [{"id": "center", "fg": 768}, {"id": "corner", "fg": [772, 769, 775, 777]}, {"id": "t_connection", "fg": [774, 767, 770, 763]}, {"id": "edge", "fg": [776, 773]}, {"id": "end_piece", "fg": [765, 766, 764, 778]}, {"id": "unconnected", "fg": 771}]}, {"id": "t_fence_barbed_season_spring", "multitile": true, "fg": 784, "bg": 1233, "additional_tiles": [{"id": "center", "bg": 1233, "fg": 781}, {"id": "corner", "bg": 1233, "fg": [780, 793, 791, 794]}, {"id": "t_connection", "bg": 1233, "fg": [786, 782, 788, 789]}, {"id": "edge", "bg": 1233, "fg": [792, 787]}, {"id": "end_piece", "bg": 1233, "fg": [783, 779, 785, 790]}, {"bg": 1233, "id": "unconnected", "fg": 784}]}, {"id": "t_fence_barbed_season_summer", "multitile": true, "fg": 784, "bg": 1240, "additional_tiles": [{"id": "center", "bg": 1240, "fg": 781}, {"id": "corner", "bg": 1240, "fg": [780, 793, 791, 794]}, {"id": "t_connection", "bg": 1240, "fg": [786, 782, 788, 789]}, {"id": "edge", "bg": 1240, "fg": [792, 787]}, {"id": "end_piece", "bg": 1240, "fg": [783, 779, 785, 790]}, {"bg": 1240, "id": "unconnected", "fg": 784}]}, {"id": "t_fence_barbed_season_autumn", "multitile": true, "fg": 784, "bg": 1234, "additional_tiles": [{"id": "center", "bg": 1234, "fg": 781}, {"id": "corner", "bg": 1234, "fg": [780, 793, 791, 794]}, {"id": "t_connection", "bg": 1234, "fg": [786, 782, 788, 789]}, {"id": "edge", "bg": 1234, "fg": [792, 787]}, {"id": "end_piece", "bg": 1234, "fg": [783, 779, 785, 790]}, {"bg": 1234, "id": "unconnected", "fg": 784}]}, {"id": "t_fence_barbed_season_winter", "multitile": true, "fg": 784, "bg": 643, "additional_tiles": [{"id": "center", "bg": 643, "fg": 781}, {"id": "corner", "bg": 643, "fg": [780, 793, 791, 794]}, {"id": "t_connection", "bg": 643, "fg": [786, 782, 788, 789]}, {"id": "edge", "bg": 643, "fg": [792, 787]}, {"id": "end_piece", "bg": 643, "fg": [783, 779, 785, 790]}, {"bg": 643, "id": "unconnected", "fg": 784}]}, {"id": "t_floor_waxed", "multitile": true, "fg": 800, "additional_tiles": [{"id": "center", "fg": 809}, {"id": "corner", "fg": [802, 797, 798, 803]}, {"id": "t_connection", "fg": [807, 796, 806, 799]}, {"id": "edge", "fg": [805, 795]}, {"id": "end_piece", "fg": [808, 804, 801, 810]}, {"id": "unconnected", "fg": 800}]}, {"id": "t_curtains", "fg": 811}, {"id": "t_door_glass_c", "fg": 812}, {"id": ["t_wall_glass", "t_wall_glass_alarm"], "multitile": true, "fg": 814, "additional_tiles": [{"id": "center", "fg": 821}, {"id": "corner", "fg": [828, 825, 816, 831]}, {"id": "t_connection", "fg": [829, 819, 818, 824]}, {"id": "edge", "fg": [813, 817]}, {"id": "end_piece", "fg": [815, 827, 832, 820]}, {"id": "unconnected", "fg": 814}]}, {"id": ["t_sidewalk", "t_sidewalk_bg_dp"], "multitile": true, "fg": 846, "additional_tiles": [{"id": "center", "fg": 833}, {"id": "corner", "fg": [834, 848, 835, 839]}, {"id": "t_connection", "fg": [837, 836, 845, 843]}, {"id": "edge", "fg": [842, 847]}, {"id": "end_piece", "fg": [841, 838, 840, 844]}, {"id": "unconnected", "fg": 846}]}, {"id": "t_sidewalk_season_winter", "fg": 643}, {"id": "t_carpet_purple", "multitile": true, "fg": 855, "additional_tiles": [{"id": "center", "fg": 853}, {"id": "corner", "fg": [858, 860, 849, 852]}, {"id": "t_connection", "fg": [862, 864, 856, 854]}, {"id": "edge", "fg": [861, 863]}, {"id": "end_piece", "fg": [857, 859, 851, 850]}, {"id": "unconnected", "fg": 855}]}, {"id": ["t_shrub_raspberry"], "fg": 865, "bg": 1233}, {"id": ["t_shrub_raspberry_harvested"], "fg": 868, "bg": 1240}, {"id": "t_shrub_raspberry_season_summer", "fg": 867, "bg": 1240}, {"id": "t_shrub_raspberry_season_autumn", "fg": 865, "bg": 1234}, {"id": "t_shrub_raspberry_season_winter", "fg": 866, "bg": 644}, {"id": "t_woodchips", "fg": 869}, {"id": "t_woodchips_season_winter", "fg": 643}, {"id": "t_fence_post_season_spring", "fg": 880, "bg": 1233}, {"id": "t_fence_post_season_summer", "fg": 880, "bg": 1240}, {"id": "t_fence_post_season_autumn", "fg": 880, "bg": 1234}, {"id": "t_fence_post_season_winter", "fg": 880, "bg": 643}, {"id": "t_fence_season_spring", "multitile": true, "fg": 880, "bg": 1233, "additional_tiles": [{"id": "center", "bg": 1233, "fg": 879}, {"id": "corner", "bg": 1233, "fg": [873, 871, 874, 881]}, {"id": "t_connection", "bg": 1233, "fg": [883, 872, 884, 885]}, {"id": "edge", "bg": 1233, "fg": [882, 886]}, {"id": "end_piece", "bg": 1233, "fg": [877, 876, 887, 875]}, {"bg": 1233, "id": "unconnected", "fg": 880}]}, {"id": "t_fence_season_summer", "multitile": true, "fg": 880, "bg": 1240, "additional_tiles": [{"id": "center", "bg": 1240, "fg": 879}, {"id": "corner", "bg": 1240, "fg": [873, 871, 874, 881]}, {"id": "t_connection", "bg": 1240, "fg": [883, 872, 884, 885]}, {"id": "edge", "bg": 1240, "fg": [882, 886]}, {"id": "end_piece", "bg": 1240, "fg": [877, 876, 887, 875]}, {"bg": 1240, "id": "unconnected", "fg": 880}]}, {"id": "t_fence_season_autumn", "multitile": true, "fg": 880, "bg": 1234, "additional_tiles": [{"id": "center", "bg": 1234, "fg": 879}, {"id": "corner", "bg": 1234, "fg": [873, 871, 874, 881]}, {"id": "t_connection", "bg": 1234, "fg": [883, 872, 884, 885]}, {"id": "edge", "bg": 1234, "fg": [882, 886]}, {"id": "end_piece", "bg": 1234, "fg": [877, 876, 887, 875]}, {"bg": 1234, "id": "unconnected", "fg": 880}]}, {"id": "t_fence_season_winter", "multitile": true, "fg": 880, "bg": 643, "additional_tiles": [{"id": "center", "bg": 643, "fg": 879}, {"id": "corner", "bg": 643, "fg": [873, 871, 874, 881]}, {"id": "t_connection", "bg": 643, "fg": [883, 872, 884, 885]}, {"id": "edge", "bg": 643, "fg": [882, 886]}, {"id": "end_piece", "bg": 643, "fg": [877, 876, 887, 875]}, {"bg": 643, "id": "unconnected", "fg": 880}]}, {"id": "t_fencegate_c_season_spring", "fg": 870, "bg": 1233}, {"id": "t_fencegate_c_season_summer", "fg": 870, "bg": 1240}, {"id": "t_fencegate_c_season_autumn", "fg": 870, "bg": 1234}, {"id": "t_fencegate_c_season_winter", "fg": 870, "bg": 643}, {"id": "t_fencegate_o_season_spring", "fg": 878, "bg": 1233}, {"id": "t_fencegate_o_season_summer", "fg": 878, "bg": 1240}, {"id": "t_fencegate_o_season_autumn", "fg": 878, "bg": 1234}, {"id": "t_fencegate_o_season_winter", "fg": 878, "bg": 643}, {"id": "t_wall_y", "multitile": true, "fg": 902, "additional_tiles": [{"id": "center", "fg": 903}, {"id": "corner", "fg": [896, 900, 895, 889]}, {"id": "t_connection", "fg": [893, 898, 892, 897]}, {"id": "edge", "fg": [894, 899]}, {"id": "end_piece", "fg": [890, 891, 901, 888]}, {"id": "unconnected", "fg": 902}]}, {"id": "t_rdoor_c", "fg": 905}, {"id": "t_rdoor_o", "fg": 906}, {"id": "t_rdoor_b", "fg": 904}, {"id": ["t_gates_mech_control"], "fg": 909, "bg": 939}, {"id": ["t_gates_mech_control_lab"], "fg": 911, "bg": 939}, {"id": ["t_gates_control_concrete"], "fg": 908, "bg": 982}, {"id": ["t_gates_control_concrete_lab"], "fg": 911, "bg": 2374}, {"id": ["t_gates_control_brick"], "fg": 908, "bg": 674}, {"id": ["t_gates_control_brick_lab"], "fg": 911, "bg": 674}, {"id": ["t_gates_control_metal"], "fg": 908, "bg": 2382}, {"id": ["t_gates_control_metal_lab"], "fg": 911, "bg": 2382}, {"id": ["t_elevator_control"], "fg": 907, "bg": 939}, {"id": ["t_elevator_control_off"], "fg": 910, "bg": 939}, {"id": ["t_stump"], "fg": 912, "bg": 1233}, {"id": ["t_stump_season_summer"], "fg": 912, "bg": 1240}, {"id": ["t_stump_season_autumn"], "fg": 912, "bg": 1234}, {"id": "t_stump_season_winter", "fg": 912, "bg": 644}, {"id": ["t_water_moving_dp", "t_swater_moving_dp"], "multitile": true, "fg": 916, "bg": 1233, "additional_tiles": [{"id": "center", "bg": 1233, "fg": 915}, {"id": "corner", "bg": 1233, "fg": [921, 918, 917, 922]}, {"id": "t_connection", "bg": 1233, "fg": [923, 928, 925, 927]}, {"id": "edge", "bg": 1233, "fg": [920, 919]}, {"id": "end_piece", "bg": 1233, "fg": [924, 913, 914, 926]}, {"bg": 1233, "id": "unconnected", "fg": 916}]}, {"id": ["t_water_moving_dp_season_summer", "t_swater_moving_dp_season_summer"], "multitile": true, "fg": 916, "bg": 1240, "additional_tiles": [{"id": "center", "bg": 1240, "fg": 915}, {"id": "corner", "bg": 1240, "fg": [921, 918, 917, 922]}, {"id": "t_connection", "bg": 1240, "fg": [923, 928, 925, 927]}, {"id": "edge", "bg": 1240, "fg": [920, 919]}, {"id": "end_piece", "bg": 1240, "fg": [924, 913, 914, 926]}, {"bg": 1240, "id": "unconnected", "fg": 916}]}, {"id": ["t_water_moving_dp_season_autumn", "t_swater_moving_dp_season_autumn"], "multitile": true, "fg": 916, "bg": 1234, "additional_tiles": [{"id": "center", "bg": 1234, "fg": 915}, {"id": "corner", "bg": 1234, "fg": [921, 918, 917, 922]}, {"id": "t_connection", "bg": 1234, "fg": [923, 928, 925, 927]}, {"id": "edge", "bg": 1234, "fg": [920, 919]}, {"id": "end_piece", "bg": 1234, "fg": [924, 913, 914, 926]}, {"bg": 1234, "id": "unconnected", "fg": 916}]}, {"id": ["t_water_moving_dp_season_winter", "t_swater_moving_dp_season_winter"], "multitile": true, "fg": 916, "bg": 643, "additional_tiles": [{"id": "center", "bg": 643, "fg": 915}, {"id": "corner", "bg": 643, "fg": [921, 918, 917, 922]}, {"id": "t_connection", "bg": 643, "fg": [923, 928, 925, 927]}, {"id": "edge", "bg": 643, "fg": [920, 919]}, {"id": "end_piece", "bg": 643, "fg": [924, 913, 914, 926]}, {"bg": 643, "id": "unconnected", "fg": 916}]}, {"id": "t_thconc_floor", "multitile": true, "fg": 938, "additional_tiles": [{"id": "center", "fg": 939}, {"id": "corner", "fg": [935, 933, 943, 934]}, {"id": "t_connection", "fg": [940, 936, 929, 937]}, {"id": "edge", "fg": [942, 944]}, {"id": "end_piece", "fg": [932, 931, 941, 930]}, {"id": "unconnected", "fg": 938}]}, {"id": "t_carpet_green", "multitile": true, "fg": 955, "additional_tiles": [{"id": "center", "fg": 950}, {"id": "corner", "fg": [946, 956, 954, 952]}, {"id": "t_connection", "fg": [948, 949, 947, 951]}, {"id": "edge", "fg": [960, 953]}, {"id": "end_piece", "fg": [958, 957, 959, 945]}, {"id": "unconnected", "fg": 955}]}, {"id": "t_door_lab_c", "fg": 962}, {"id": "t_door_lab_o", "fg": 961}, {"id": "t_dirtfloor", "multitile": true, "fg": 977, "additional_tiles": [{"id": "center", "fg": 971}, {"id": "corner", "fg": [972, 976, 969, 975]}, {"id": "t_connection", "fg": [974, 966, 978, 965]}, {"id": "edge", "fg": [968, 973]}, {"id": "end_piece", "fg": [970, 967, 963, 964]}, {"id": "unconnected", "fg": 977}]}, {"id": "t_concrete_wall", "multitile": true, "fg": 982, "additional_tiles": [{"id": "center", "fg": 986}, {"id": "corner", "fg": [994, 985, 993, 984]}, {"id": "t_connection", "fg": [989, 979, 987, 983]}, {"id": "edge", "fg": [992, 981]}, {"id": "end_piece", "fg": [991, 988, 990, 980]}, {"id": "unconnected", "fg": 982}]}, {"id": "t_wall_w", "multitile": true, "fg": 996, "additional_tiles": [{"id": "center", "fg": 1006}, {"id": "corner", "fg": [1010, 1002, 1005, 998]}, {"id": "t_connection", "fg": [997, 1009, 999, 995]}, {"id": "edge", "fg": [1000, 1007]}, {"id": "end_piece", "fg": [1008, 1004, 1001, 1003]}, {"id": "unconnected", "fg": 996}]}, {"id": "t_rock_wall", "multitile": true, "fg": 1011, "additional_tiles": [{"id": "center", "fg": 1020}, {"id": "corner", "fg": [1015, 1023, 1012, 1013]}, {"id": "t_connection", "fg": [1016, 1025, 1026, 1022]}, {"id": "edge", "fg": [1014, 1021]}, {"id": "end_piece", "fg": [1017, 1018, 1019, 1024]}, {"id": "unconnected", "fg": 1011}]}, {"id": "t_rock_floor", "multitile": true, "fg": 1034, "additional_tiles": [{"id": "center", "fg": 1038}, {"id": "corner", "fg": [1036, 1042, 1041, 1040]}, {"id": "t_connection", "fg": [1027, 1032, 1035, 1030]}, {"id": "edge", "fg": [1029, 1028]}, {"id": "end_piece", "fg": [1039, 1037, 1033, 1031]}, {"id": "unconnected", "fg": 1034}]}, {"id": "t_grass_tall", "multitile": true, "fg": 1054, "bg": 1233, "additional_tiles": [{"id": "center", "bg": 1233, "fg": 1052}, {"id": "corner", "bg": 1233, "fg": [1046, 1050, 1056, 1049]}, {"id": "t_connection", "bg": 1233, "fg": [1048, 1044, 1053, 1057]}, {"id": "edge", "bg": 1233, "fg": [1047, 1045]}, {"id": "end_piece", "bg": 1233, "fg": [1051, 1043, 1055, 1058]}, {"bg": 1233, "id": "unconnected", "fg": 1054}]}, {"id": "t_grass_tall_season_summer", "multitile": true, "fg": 1062, "bg": 1240, "additional_tiles": [{"id": "center", "bg": 1240, "fg": 1069}, {"id": "corner", "bg": 1240, "fg": [1067, 1071, 1074, 1059]}, {"id": "t_connection", "bg": 1240, "fg": [1066, 1073, 1065, 1070]}, {"id": "edge", "bg": 1240, "fg": [1060, 1072]}, {"id": "end_piece", "bg": 1240, "fg": [1061, 1063, 1064, 1068]}, {"bg": 1240, "id": "unconnected", "fg": 1062}]}, {"id": "t_grass_tall_season_autumn", "multitile": true, "fg": 1088, "bg": 1234, "additional_tiles": [{"id": "center", "bg": 1234, "fg": 1082}, {"id": "corner", "bg": 1234, "fg": [1075, 1090, 1078, 1080]}, {"id": "t_connection", "bg": 1234, "fg": [1086, 1081, 1083, 1085]}, {"id": "edge", "bg": 1234, "fg": [1076, 1077]}, {"id": "end_piece", "bg": 1234, "fg": [1089, 1087, 1084, 1079]}, {"bg": 1234, "id": "unconnected", "fg": 1088}]}, {"id": "t_grass_tall_season_winter", "fg": 644}, {"id": "f_grass_tall", "multitile": true, "fg": 1054, "additional_tiles": [{"id": "center", "fg": 1052}, {"id": "corner", "fg": [1046, 1050, 1056, 1049]}, {"id": "t_connection", "fg": [1048, 1044, 1053, 1057]}, {"id": "edge", "fg": [1047, 1045]}, {"id": "end_piece", "fg": [1051, 1043, 1055, 1058]}, {"id": "unconnected", "fg": 1054}]}, {"id": "f_grass_tall_summer", "multitile": true, "fg": 1062, "additional_tiles": [{"id": "center", "fg": 1069}, {"id": "corner", "fg": [1067, 1071, 1074, 1059]}, {"id": "t_connection", "fg": [1066, 1073, 1065, 1070]}, {"id": "edge", "fg": [1060, 1072]}, {"id": "end_piece", "fg": [1061, 1063, 1064, 1068]}, {"id": "unconnected", "fg": 1062}]}, {"id": "f_grass_tall_autumn", "multitile": true, "fg": 1088, "additional_tiles": [{"id": "center", "fg": 1082}, {"id": "corner", "fg": [1075, 1090, 1078, 1080]}, {"id": "t_connection", "fg": [1086, 1081, 1083, 1085]}, {"id": "edge", "fg": [1076, 1077]}, {"id": "end_piece", "fg": [1089, 1087, 1084, 1079]}, {"id": "unconnected", "fg": 1088}]}, {"id": "t_grass_long", "multitile": true, "fg": 1096, "bg": 1233, "additional_tiles": [{"id": "center", "bg": 1233, "fg": 1102}, {"id": "corner", "bg": 1233, "fg": [1097, 1100, 1091, 1095]}, {"id": "t_connection", "bg": 1233, "fg": [1098, 1103, 1092, 1106]}, {"id": "edge", "bg": 1233, "fg": [1104, 1094]}, {"id": "end_piece", "bg": 1233, "fg": [1099, 1093, 1105, 1101]}, {"bg": 1233, "id": "unconnected", "fg": 1096}]}, {"id": "t_grass_long_season_autumn", "multitile": true, "fg": 1122, "bg": 1234, "additional_tiles": [{"id": "center", "bg": 1234, "fg": 1115}, {"id": "corner", "bg": 1234, "fg": [1114, 1121, 1120, 1117]}, {"id": "t_connection", "bg": 1234, "fg": [1108, 1107, 1112, 1119]}, {"id": "edge", "bg": 1234, "fg": [1110, 1113]}, {"id": "end_piece", "bg": 1234, "fg": [1109, 1116, 1111, 1118]}, {"bg": 1234, "id": "unconnected", "fg": 1122}]}, {"id": "t_grass_long_season_summer", "multitile": true, "fg": 1135, "bg": 1240, "additional_tiles": [{"id": "center", "bg": 1240, "fg": 1132}, {"id": "corner", "bg": 1240, "fg": [1136, 1129, 1125, 1130]}, {"id": "t_connection", "bg": 1240, "fg": [1128, 1133, 1134, 1127]}, {"id": "edge", "bg": 1240, "fg": [1138, 1126]}, {"id": "end_piece", "bg": 1240, "fg": [1124, 1131, 1137, 1123]}, {"bg": 1240, "id": "unconnected", "fg": 1135}]}, {"id": "t_grass_long_season_winter", "fg": 644}, {"id": "f_grass_long", "multitile": true, "fg": 1096, "additional_tiles": [{"id": "center", "fg": 1102}, {"id": "corner", "fg": [1097, 1100, 1091, 1095]}, {"id": "t_connection", "fg": [1098, 1103, 1092, 1106]}, {"id": "edge", "fg": [1104, 1094]}, {"id": "end_piece", "fg": [1099, 1093, 1105, 1101]}, {"id": "unconnected", "fg": 1096}]}, {"id": ["t_shrub_lilac"], "fg": 1142, "bg": 1233}, {"id": ["t_shrub_lilac_harvested"], "fg": 1139, "bg": 1233}, {"id": "t_shrub_lilac_season_summer", "fg": 1141, "bg": 1240}, {"id": "t_shrub_lilac_season_autumn", "fg": 1139, "bg": 1234}, {"id": "t_shrub_lilac_season_winter", "fg": 1140, "bg": 644}, {"id": "t_elevator", "fg": [{"weight": 100, "sprite": 1143}, {"weight": 100, "sprite": 1144}]}, {"id": "t_moss", "fg": 1145}, {"id": "t_moss_season_winter", "fg": 644}, {"id": "t_scrap_floor", "multitile": true, "fg": 1155, "additional_tiles": [{"id": "center", "fg": 1152}, {"id": "corner", "fg": [1150, 1157, 1146, 1161]}, {"id": "t_connection", "fg": [1149, 1160, 1148, 1151]}, {"id": "edge", "fg": [1159, 1153]}, {"id": "end_piece", "fg": [1156, 1147, 1158, 1154]}, {"id": "unconnected", "fg": 1155}]}, {"id": "t_grass_dead", "multitile": true, "fg": 1176, "bg": 1233, "additional_tiles": [{"id": "center", "bg": 1233, "fg": 1172}, {"id": "corner", "bg": 1233, "fg": [1177, 1168, 1169, 1164]}, {"id": "t_connection", "bg": 1233, "fg": [1171, 1166, 1174, 1165]}, {"id": "edge", "bg": 1233, "fg": [1163, 1170]}, {"id": "end_piece", "bg": 1233, "fg": [1175, 1162, 1167, 1173]}, {"bg": 1233, "id": "unconnected", "fg": 1176}]}, {"id": "t_grass_dead_season_summer", "multitile": true, "fg": 1176, "bg": 1240, "additional_tiles": [{"id": "center", "bg": 1240, "fg": 1172}, {"id": "corner", "bg": 1240, "fg": [1177, 1168, 1169, 1164]}, {"id": "t_connection", "bg": 1240, "fg": [1171, 1166, 1174, 1165]}, {"id": "edge", "bg": 1240, "fg": [1163, 1170]}, {"id": "end_piece", "bg": 1240, "fg": [1175, 1162, 1167, 1173]}, {"bg": 1240, "id": "unconnected", "fg": 1176}]}, {"id": "t_grass_dead_season_autumn", "multitile": true, "fg": 1176, "bg": 1234, "additional_tiles": [{"id": "center", "bg": 1234, "fg": 1172}, {"id": "corner", "bg": 1234, "fg": [1177, 1168, 1169, 1164]}, {"id": "t_connection", "bg": 1234, "fg": [1171, 1166, 1174, 1165]}, {"id": "edge", "bg": 1234, "fg": [1163, 1170]}, {"id": "end_piece", "bg": 1234, "fg": [1175, 1162, 1167, 1173]}, {"bg": 1234, "id": "unconnected", "fg": 1176}]}, {"id": "t_grass_dead_season_winter", "fg": 643}, {"id": ["t_water_sh", "t_swater_sh"], "multitile": true, "fg": 1190, "bg": 1233, "additional_tiles": [{"id": "center", "bg": 1233, "fg": [{"weight": 1, "sprite": 1186}, {"weight": 1, "sprite": 1193}, {"weight": 1, "sprite": 1188}]}, {"id": "corner", "bg": 1233, "fg": [1181, 1184, 1195, 1192]}, {"id": "t_connection", "bg": 1233, "fg": [1182, 1187, 1191, 1194]}, {"id": "edge", "bg": 1233, "fg": [1180, 1183]}, {"id": "end_piece", "bg": 1233, "fg": [1179, 1185, 1178, 1189]}, {"bg": 1233, "id": "unconnected", "fg": 1190}]}, {"id": ["t_water_sh_season_summer", "t_swater_sh_season_summer"], "multitile": true, "fg": 1190, "bg": 1240, "additional_tiles": [{"id": "center", "bg": 1240, "fg": [{"weight": 1, "sprite": 1186}, {"weight": 1, "sprite": 1193}, {"weight": 1, "sprite": 1188}]}, {"id": "corner", "bg": 1240, "fg": [1181, 1184, 1195, 1192]}, {"id": "t_connection", "bg": 1240, "fg": [1182, 1187, 1191, 1194]}, {"id": "edge", "bg": 1240, "fg": [1180, 1183]}, {"id": "end_piece", "bg": 1240, "fg": [1179, 1185, 1178, 1189]}, {"bg": 1240, "id": "unconnected", "fg": 1190}]}, {"id": ["t_water_sh_season_autumn", "t_swater_sh_season_autumn"], "multitile": true, "fg": 1190, "bg": 1234, "additional_tiles": [{"id": "center", "bg": 1234, "fg": [{"weight": 1, "sprite": 1186}, {"weight": 1, "sprite": 1193}, {"weight": 1, "sprite": 1188}]}, {"id": "corner", "bg": 1234, "fg": [1181, 1184, 1195, 1192]}, {"id": "t_connection", "bg": 1234, "fg": [1182, 1187, 1191, 1194]}, {"id": "edge", "bg": 1234, "fg": [1180, 1183]}, {"id": "end_piece", "bg": 1234, "fg": [1179, 1185, 1178, 1189]}, {"bg": 1234, "id": "unconnected", "fg": 1190}]}, {"id": ["t_water_sh_season_winter", "t_swater_sh_season_winter"], "multitile": true, "fg": 1190, "bg": 643, "additional_tiles": [{"id": "center", "bg": 643, "fg": [{"weight": 1, "sprite": 1186}, {"weight": 1, "sprite": 1193}, {"weight": 1, "sprite": 1188}]}, {"id": "corner", "bg": 643, "fg": [1181, 1184, 1195, 1192]}, {"id": "t_connection", "bg": 643, "fg": [1182, 1187, 1191, 1194]}, {"id": "edge", "bg": 643, "fg": [1180, 1183]}, {"id": "end_piece", "bg": 643, "fg": [1179, 1185, 1178, 1189]}, {"bg": 643, "id": "unconnected", "fg": 1190}]}, {"id": "t_reinforced_glass", "multitile": true, "fg": 1207, "additional_tiles": [{"id": "center", "fg": 1198}, {"id": "corner", "fg": [1211, 1206, 1201, 1205]}, {"id": "t_connection", "fg": [1208, 1209, 1200, 1204]}, {"id": "edge", "fg": [1197, 1210]}, {"id": "end_piece", "fg": [1203, 1202, 1196, 1199]}, {"id": "unconnected", "fg": 1207}]}, {"id": "t_carpet_yellow", "multitile": true, "fg": 1213, "additional_tiles": [{"id": "center", "fg": 1226}, {"id": "corner", "fg": [1212, 1221, 1224, 1217]}, {"id": "t_connection", "fg": [1227, 1215, 1214, 1220]}, {"id": "edge", "fg": [1219, 1225]}, {"id": "end_piece", "fg": [1223, 1222, 1218, 1216]}, {"id": "unconnected", "fg": 1213}]}, {"id": "t_dirt", "fg": [{"weight": 100, "sprite": 1233}, {"weight": 70, "sprite": 1235}, {"weight": 30, "sprite": 1239}, {"weight": 30, "sprite": 1230}, {"weight": 10, "sprite": 1237}, {"weight": 20, "sprite": 1231}, {"weight": 20, "sprite": 1232}, {"weight": 10, "sprite": 1236}, {"weight": 10, "sprite": 1238}], "bg": [{"weight": 100, "sprite": 1233}, {"weight": 200, "sprite": 1235}]}, {"id": "t_dirt_season_summer", "fg": [{"weight": 100, "sprite": 1240}, {"weight": 100, "sprite": 1229}, {"weight": 30, "sprite": 1239}, {"weight": 30, "sprite": 1230}, {"weight": 10, "sprite": 1237}, {"weight": 12, "sprite": 1231}, {"weight": 12, "sprite": 1232}, {"weight": 7, "sprite": 1236}, {"weight": 6, "sprite": 1238}], "bg": [{"weight": 100, "sprite": 1240}, {"weight": 100, "sprite": 1229}]}, {"id": "t_dirt_season_autumn", "fg": [{"weight": 100, "sprite": 1234}, {"weight": 100, "sprite": 1228}, {"weight": 30, "sprite": 1239}, {"weight": 30, "sprite": 1230}, {"weight": 10, "sprite": 1237}, {"weight": 6, "sprite": 1231}, {"weight": 6, "sprite": 1232}, {"weight": 6, "sprite": 1236}, {"weight": 4, "sprite": 1238}], "bg": [{"weight": 100, "sprite": 1234}, {"weight": 100, "sprite": 1228}]}, {"id": "t_dirt_season_winter", "fg": 643}, {"id": "t_door_metal_c_peep", "fg": 1241}, {"id": "t_shrub", "fg": 1242, "bg": 1233}, {"id": "t_shrub_season_summer", "fg": 1242, "bg": 1240}, {"id": "t_shrub_season_autumn", "fg": 1242, "bg": 1234}, {"id": "t_shrub_season_winter", "fg": 1243, "bg": 644}, {"id": "t_pit", "fg": 1244, "bg": [{"weight": 100, "sprite": 1233}, {"weight": 100, "sprite": 1235}]}, {"id": "t_pit_season_summer", "fg": 1244, "bg": [{"weight": 100, "sprite": 1240}, {"weight": 100, "sprite": 1229}]}, {"id": "t_pit_season_autumn", "fg": 1244, "bg": [{"weight": 100, "sprite": 1234}, {"weight": 100, "sprite": 1228}]}, {"id": "t_door_c", "fg": 1245}, {"id": "t_door_locked", "fg": 1247}, {"id": "t_door_o", "fg": 1246}, {"id": "t_door_b", "fg": 1248}, {"id": "t_scrap_wall", "multitile": true, "fg": 1263, "additional_tiles": [{"id": "center", "fg": 1260}, {"id": "corner", "fg": [1250, 1264, 1262, 1265]}, {"id": "t_connection", "fg": [1255, 1258, 1254, 1252]}, {"id": "edge", "fg": [1253, 1257]}, {"id": "end_piece", "fg": [1259, 1251, 1249, 1256]}, {"id": "unconnected", "fg": 1263}]}, {"id": "t_clay", "multitile": true, "fg": 1270, "bg": 1233, "additional_tiles": [{"id": "center", "bg": 1233, "fg": 1277}, {"id": "corner", "bg": 1233, "fg": [1272, 1267, 1269, 1278]}, {"id": "t_connection", "bg": 1233, "fg": [1280, 1279, 1271, 1275]}, {"id": "edge", "bg": 1233, "fg": [1266, 1268]}, {"id": "end_piece", "bg": 1233, "fg": [1276, 1281, 1274, 1273]}, {"bg": 1233, "id": "unconnected", "fg": 1270}]}, {"id": "t_clay_season_summer", "multitile": true, "fg": 1270, "bg": 1240, "additional_tiles": [{"id": "center", "bg": 1240, "fg": 1277}, {"id": "corner", "bg": 1240, "fg": [1272, 1267, 1269, 1278]}, {"id": "t_connection", "bg": 1240, "fg": [1280, 1279, 1271, 1275]}, {"id": "edge", "bg": 1240, "fg": [1266, 1268]}, {"id": "end_piece", "bg": 1240, "fg": [1276, 1281, 1274, 1273]}, {"bg": 1240, "id": "unconnected", "fg": 1270}]}, {"id": "t_clay_season_autumn", "multitile": true, "fg": 1270, "bg": 1234, "additional_tiles": [{"id": "center", "bg": 1234, "fg": 1277}, {"id": "corner", "bg": 1234, "fg": [1272, 1267, 1269, 1278]}, {"id": "t_connection", "bg": 1234, "fg": [1280, 1279, 1271, 1275]}, {"id": "edge", "bg": 1234, "fg": [1266, 1268]}, {"id": "end_piece", "bg": 1234, "fg": [1276, 1281, 1274, 1273]}, {"bg": 1234, "id": "unconnected", "fg": 1270}]}, {"id": "t_clay_season_winter", "fg": 643}, {"id": ["t_shrub_hydrangea"], "fg": 1284, "bg": 1233}, {"id": ["t_shrub_hydrangea_harvested"], "fg": 1285, "bg": 1233}, {"id": "t_shrub_hydrangea_season_summer", "fg": 1282, "bg": 1240}, {"id": "t_shrub_hydrangea_season_autumn", "fg": 1284, "bg": 1234}, {"id": "t_shrub_hydrangea_season_winter", "fg": 1283, "bg": 644}, {"id": "t_bars", "fg": 1286}, {"id": ["t_shrub_grape"], "fg": 1290, "bg": 1233}, {"id": ["t_shrub_grape_harvested"], "fg": 1287, "bg": 1240}, {"id": "t_shrub_grape_season_summer", "fg": 1289, "bg": 1240}, {"id": "t_shrub_grape_season_autumn", "fg": 1290, "bg": 1234}, {"id": "t_shrub_grape_season_winter", "fg": 1288, "bg": 644}, {"id": "t_door_glass_o", "fg": 1291}, {"id": ["t_shrub_rose"], "fg": 1294, "bg": 1233}, {"id": "t_shrub_rose_season_summer", "fg": 1294, "bg": 1240}, {"id": "t_shrub_rose_harvested", "fg": 1294, "bg": 1234}, {"id": "t_shrub_rose_season_autumn", "fg": 1292, "bg": 1234}, {"id": "t_shrub_rose_season_winter", "fg": 1293, "bg": 644}, {"id": "t_wall", "multitile": true, "fg": 1307, "additional_tiles": [{"id": "center", "fg": 1302}, {"id": "corner", "fg": [1308, 1300, 1297, 1306]}, {"id": "t_connection", "fg": [1304, 1299, 1310, 1295]}, {"id": "edge", "fg": [1296, 1298]}, {"id": "end_piece", "fg": [1303, 1309, 1305, 1301]}, {"id": "unconnected", "fg": 1307}]}, {"id": "t_wall_p", "multitile": true, "fg": 1320, "additional_tiles": [{"id": "center", "fg": 1325}, {"id": "corner", "fg": [1313, 1316, 1321, 1318]}, {"id": "t_connection", "fg": [1323, 1317, 1319, 1311]}, {"id": "edge", "fg": [1324, 1312]}, {"id": "end_piece", "fg": [1322, 1314, 1326, 1315]}, {"id": "unconnected", "fg": 1320}]}, {"id": "t_wall_log", "multitile": true, "fg": 1334, "bg": 1233, "additional_tiles": [{"id": "center", "fg": 1338}, {"id": "corner", "fg": [1339, 1328, 1342, 1336]}, {"id": "t_connection", "fg": [1333, 1330, 1341, 1332]}, {"id": "edge", "bg": 1233, "fg": [1329, 1331]}, {"id": "end_piece", "bg": 1233, "fg": [1340, 1335, 1337, 1327]}, {"bg": 1233, "id": "unconnected", "fg": 1334}]}, {"id": "t_wall_g", "multitile": true, "fg": 1348, "additional_tiles": [{"id": "center", "fg": 1352}, {"id": "corner", "fg": [1358, 1355, 1349, 1357]}, {"id": "t_connection", "fg": [1353, 1344, 1354, 1350]}, {"id": "edge", "fg": [1345, 1346]}, {"id": "end_piece", "fg": [1356, 1351, 1347, 1343]}, {"id": "unconnected", "fg": 1348}]}, {"id": "t_door_boarded", "fg": 1359}, {"id": "t_door_boarded_damaged", "fg": 1360}, {"id": ["t_water_pool", "t_water_pool_shallow"], "multitile": true, "fg": 1368, "additional_tiles": [{"id": "center", "fg": [{"weight": 1, "sprite": 1186}, {"weight": 1, "sprite": 1193}, {"weight": 1, "sprite": 1188}]}, {"id": "corner", "fg": [1363, 1372, 1371, 1365]}, {"id": "t_connection", "fg": [1367, 1364, 1369, 1373]}, {"id": "edge", "fg": [1370, 1362]}, {"id": "end_piece", "fg": [1374, 1375, 1361, 1366]}, {"id": "unconnected", "fg": 1368}]}, {"id": "t_pavement", "fg": 1376}, {"id": "t_pavement_season_winter", "fg": 643}, {"id": ["t_water_dp", "t_swater_dp"], "multitile": true, "fg": 1384, "bg": 1233, "additional_tiles": [{"id": "center", "bg": 1233, "fg": [{"weight": 1, "sprite": 1386}, {"weight": 1, "sprite": 1387}, {"weight": 1, "sprite": 1393}]}, {"id": "corner", "bg": 1233, "fg": [1392, 1377, 1379, 1388]}, {"id": "t_connection", "bg": 1233, "fg": [1381, 1389, 1391, 1380]}, {"id": "edge", "bg": 1233, "fg": [1383, 1394]}, {"id": "end_piece", "bg": 1233, "fg": [1390, 1385, 1382, 1378]}, {"bg": 1233, "id": "unconnected", "fg": 1384}]}, {"id": ["t_water_dp_season_summer", "t_swater_dp_season_summer"], "multitile": true, "fg": 1384, "bg": 1240, "additional_tiles": [{"id": "center", "bg": 1240, "fg": [{"weight": 1, "sprite": 1386}, {"weight": 1, "sprite": 1387}, {"weight": 1, "sprite": 1393}]}, {"id": "corner", "bg": 1240, "fg": [1392, 1377, 1379, 1388]}, {"id": "t_connection", "bg": 1240, "fg": [1381, 1389, 1391, 1380]}, {"id": "edge", "bg": 1240, "fg": [1383, 1394]}, {"id": "end_piece", "bg": 1240, "fg": [1390, 1385, 1382, 1378]}, {"bg": 1240, "id": "unconnected", "fg": 1384}]}, {"id": ["t_water_dp_season_autumn", "t_swater_dp_season_autumn"], "multitile": true, "fg": 1384, "bg": 1234, "additional_tiles": [{"id": "center", "bg": 1234, "fg": [{"weight": 1, "sprite": 1386}, {"weight": 1, "sprite": 1387}, {"weight": 1, "sprite": 1393}]}, {"id": "corner", "bg": 1234, "fg": [1392, 1377, 1379, 1388]}, {"id": "t_connection", "bg": 1234, "fg": [1381, 1389, 1391, 1380]}, {"id": "edge", "bg": 1234, "fg": [1383, 1394]}, {"id": "end_piece", "bg": 1234, "fg": [1390, 1385, 1382, 1378]}, {"bg": 1234, "id": "unconnected", "fg": 1384}]}, {"id": ["t_water_dp_season_winter", "t_swater_dp_season_winter"], "multitile": true, "fg": 1384, "bg": 643, "additional_tiles": [{"id": "center", "bg": 643, "fg": [{"weight": 1, "sprite": 1386}, {"weight": 1, "sprite": 1387}, {"weight": 1, "sprite": 1393}]}, {"id": "corner", "bg": 643, "fg": [1392, 1377, 1379, 1388]}, {"id": "t_connection", "bg": 643, "fg": [1381, 1389, 1391, 1380]}, {"id": "edge", "bg": 643, "fg": [1383, 1394]}, {"id": "end_piece", "bg": 643, "fg": [1390, 1385, 1382, 1378]}, {"bg": 643, "id": "unconnected", "fg": 1384}]}, {"id": ["t_linoleum_gray", "t_linoluem_gray_no_roof"], "multitile": true, "fg": 1398, "additional_tiles": [{"id": "center", "fg": 1397}, {"id": "corner", "fg": [1406, 1399, 1405, 1401]}, {"id": "t_connection", "fg": [1402, 1396, 1395, 1408]}, {"id": "edge", "fg": [1400, 1407]}, {"id": "end_piece", "fg": [1404, 1409, 1410, 1403]}, {"id": "unconnected", "fg": 1398}]}, {"id": "t_grass", "multitile": true, "fg": 1417, "bg": 1233, "additional_tiles": [{"id": "center", "bg": 1233, "fg": [{"weight": 1, "sprite": 1418}, {"weight": 1, "sprite": 1421}, {"weight": 1, "sprite": 1433}, {"weight": 1, "sprite": 1432}, {"weight": 1, "sprite": 1413}, {"weight": 1, "sprite": 1424}, {"weight": 1, "sprite": 1415}, {"weight": 1, "sprite": 1434}, {"weight": 1, "sprite": 1428}]}, {"id": "corner", "bg": 1233, "fg": [1427, 1420, 1425, 1416]}, {"id": "t_connection", "bg": 1233, "fg": [1412, 1430, 1411, 1431]}, {"id": "edge", "bg": 1233, "fg": [1426, 1414]}, {"id": "end_piece", "bg": 1233, "fg": [1423, 1429, 1419, 1422]}, {"bg": 1233, "id": "unconnected", "fg": 1417}]}, {"id": "t_grass_season_summer", "multitile": true, "fg": 1417, "bg": 1240, "additional_tiles": [{"id": "center", "bg": 1240, "fg": 1460}, {"id": "corner", "bg": 1240, "fg": [1465, 1452, 1454, 1453]}, {"id": "t_connection", "bg": 1240, "fg": [1455, 1457, 1464, 1463]}, {"id": "edge", "bg": 1240, "fg": [1459, 1458]}, {"id": "end_piece", "bg": 1240, "fg": [1451, 1462, 1461, 1456]}, {"bg": 1240, "id": "unconnected", "fg": 1466}]}, {"id": "t_grass_season_autumn", "multitile": true, "fg": 1417, "bg": 1234, "additional_tiles": [{"id": "center", "bg": 1234, "fg": 1444}, {"id": "corner", "bg": 1234, "fg": [1449, 1435, 1450, 1446]}, {"id": "t_connection", "bg": 1234, "fg": [1447, 1438, 1445, 1443]}, {"id": "edge", "bg": 1234, "fg": [1440, 1439]}, {"id": "end_piece", "bg": 1234, "fg": [1442, 1441, 1437, 1448]}, {"bg": 1234, "id": "unconnected", "fg": 1436}]}, {"id": "t_grass_season_winter", "fg": 643}, {"id": "t_stairs_down", "fg": 1467}, {"id": "t_wood_stairs_down", "fg": 1468}, {"id": "t_floor_wax", "multitile": true, "fg": 1478, "additional_tiles": [{"id": "center", "fg": 1481}, {"id": "corner", "fg": [1483, 1470, 1477, 1474]}, {"id": "t_connection", "fg": [1479, 1484, 1471, 1472]}, {"id": "edge", "fg": [1475, 1480]}, {"id": "end_piece", "fg": [1469, 1482, 1473, 1476]}, {"id": "unconnected", "fg": 1478}]}, {"id": "t_wall_r", "multitile": true, "fg": 1485, "additional_tiles": [{"id": "center", "fg": 1492}, {"id": "corner", "fg": [1494, 1493, 1498, 1499]}, {"id": "t_connection", "fg": [1488, 1490, 1491, 1495]}, {"id": "edge", "fg": [1500, 1497]}, {"id": "end_piece", "fg": [1489, 1496, 1487, 1486]}, {"id": "unconnected", "fg": 1485}]}, {"id": "t_concrete", "multitile": true, "fg": 1508, "additional_tiles": [{"id": "center", "fg": 1502}, {"id": "corner", "fg": [1513, 1510, 1501, 1511]}, {"id": "t_connection", "fg": [1509, 1503, 1515, 1505]}, {"id": "edge", "fg": [1507, 1512]}, {"id": "end_piece", "fg": [1506, 1504, 1516, 1514]}, {"id": "unconnected", "fg": 1508}]}, {"id": "t_concrete_season_winter", "fg": 643}, {"id": "t_wall_resin", "multitile": true, "fg": 1525, "additional_tiles": [{"id": "center", "fg": 1532}, {"id": "corner", "fg": [1520, 1531, 1522, 1528]}, {"id": "t_connection", "fg": [1517, 1523, 1519, 1521]}, {"id": "edge", "fg": [1534, 1533]}, {"id": "end_piece", "fg": [1530, 1524, 1526, 1527]}, {"id": "unconnected", "fg": 1525}]}, {"id": "t_resin_hole_c", "fg": 1529}, {"id": "t_resin_hole_o", "fg": 1518}, {"id": "t_ladder_down", "fg": 1535}, {"id": "tr_nailboard", "fg": 1536, "bg": 480}, {"id": "animation_hit", "fg": 1537}, {"id": ["overlay_effects_bleed"], "fg": 1543}, {"id": ["overlay_effects_deaf"], "fg": 1538}, {"id": ["overlay_effects_downed"], "fg": 1545}, {"id": ["overlay_effects_grabbed"], "fg": 1544}, {"id": ["overlay_effects_winded"], "fg": 1542}, {"id": ["overlay_effects_hot"], "fg": 1539}, {"id": ["overlay_effects_cold"], "fg": 1548}, {"id": ["overlay_male_crouch", "overlay_female_crouch"], "fg": 1541}, {"id": ["overlay_male_run", "overlay_female_run"], "fg": 1546}, {"id": ["overlay_hostile_sees_player"], "fg": 1547}, {"id": ["zombie_revival_indicator"], "fg": 1540}, {"id": "cursor", "fg": 1549}, {"id": "highlight", "fg": 1552}, {"id": "highlight_item", "fg": 1553}, {"id": "line_target", "fg": 1550}, {"id": "line_trail", "fg": 1551}, {"id": "animation_line", "fg": 1554}, {"id": "mon_pig", "fg": [{"weight": 8, "sprite": 1557}, {"weight": 3, "sprite": 1556}, {"weight": 1, "sprite": 1555}], "bg": 1581}, {"id": "mon_piglet", "fg": 1558, "bg": 1582}, {"id": "mon_deer_mouse", "fg": 1559, "bg": 1582}, {"id": "mon_locust_nymph", "fg": 1560, "bg": 1582}, {"id": "mon_skittering_plague", "fg": 1561, "bg": 1581}, {"id": "mon_black_rat", "fg": 1562, "bg": 1582}, {"id": "mon_zolf", "fg": 1563, "bg": 1581}, {"id": "mon_dog", "fg": 1564, "bg": 1581}, {"id": "mon_dog_skeleton", "fg": 1568, "bg": 1581}, {"id": "mon_zombie_dog", "fg": [{"weight": 1, "sprite": 1569}, {"weight": 1, "sprite": 1570}], "bg": 1581}, {"id": "mon_dog_beagle", "fg": 1567, "bg": 1582}, {"id": "mon_dog_gshepherd", "fg": 1566, "bg": 1581}, {"id": "mon_dog_boxer", "fg": 1565, "bg": 1581}, {"id": "mon_dog_dachshund", "fg": 1571, "bg": 1581}, {"id": "mon_blob", "fg": 1572, "bg": 1581}, {"id": "mon_squirrel_red", "fg": 1573, "bg": 1582}, {"id": "mon_plague_vector", "fg": 1574, "bg": 1581}, {"id": "mon_blob_small", "fg": [{"weight": 1, "sprite": 1575}, {"weight": 1, "sprite": 1576}], "bg": 1582}, {"id": "mon_dragonfly", "fg": 1577, "bg": 1582}, {"id": "mon_zombie_child", "fg": [{"weight": 1, "sprite": 1578}, {"weight": 1, "sprite": 1579}], "bg": 1582}, {"id": "mon_plague_nymph", "fg": 1580, "bg": 1582}, {"id": "mon_squirrel", "fg": 1583, "bg": 1582}, {"id": "mon_giant_cockroach", "fg": 1584, "bg": 1581}, {"id": "mon_zombie_pig", "fg": 1585, "bg": 1581}, {"id": "mon_rabbit", "fg": 1586, "bg": 1582}, {"id": "mon_zombeaver", "fg": 1587, "bg": 1582}, {"id": "mon_ant", "fg": 1588, "bg": 1581}, {"id": "mon_ant_acid", "fg": 1593, "bg": 1581}, {"id": "corpse_mon_ant", "fg": 1595}, {"id": "corpse_mon_ant_acid", "fg": 1590}, {"id": "mon_ant_larva", "fg": 1589, "bg": 1582}, {"id": "mon_ant_acid_larva", "fg": 1591, "bg": 1582}, {"id": "corpse_mon_ant_larva", "fg": 1592}, {"id": "corpse_mon_ant_acid_larva", "fg": 1594}, {"id": "mon_centipede_giant", "fg": 1596, "bg": 1581}, {"id": "mon_cat", "fg": [{"weight": 1, "sprite": 1597}, {"weight": 1, "sprite": 1598}], "bg": 1582}, {"id": "mon_beaver", "fg": 1599, "bg": 1582}, {"id": "mon_wolf", "fg": 1600, "bg": 1581}, {"id": "mon_otter", "fg": 1601, "bg": 1582}, {"id": "mon_giant_cockroach_nymph", "fg": 1602, "bg": 1582}, {"id": "mon_duck", "fg": 1603, "bg": 1582}, {"id": "mon_mosquito", "fg": 1604, "bg": 1582}, {"id": "mon_locust", "fg": 1605, "bg": 1581}, {"id": "mon_pregnant_giant_cockroach", "fg": 1606, "bg": 1581}, {"id": "mon_dermatik_larva", "fg": 1607, "bg": 1582}, {"id": "mon_bear_cub", "fg": 1608, "bg": 1582}, {"id": "mon_wasp_small", "fg": 1609, "bg": 1582}, {"id": "fd_fire", "fg": 1610}], "//": "range 1 to 1616"}, {"file": "tall.png", "tiles": [{"id": "f_bookcase", "fg": 1617}, {"id": "f_arcade_machine", "fg": 1618}, {"id": "f_floor_lamp", "fg": 1619}, {"id": "f_dryer", "fg": 1620}, {"id": "f_dresser", "fg": 1621}, {"id": "f_crate_c", "fg": 1623}, {"id": "f_crate_o", "fg": 1622}, {"id": "f_alien_tendril", "fg": 1626}, {"id": "f_alien_zapper", "fg": 1627}, {"id": ["f_alien_pod", "f_alien_pod_organ"], "fg": 1624}, {"id": "f_alien_pod_resin", "fg": 1625}, {"id": "f_glass_cabinet", "fg": 1628}, {"id": "f_shower", "fg": 1629}, {"id": "f_standing_tank", "fg": 1630}, {"id": "f_bigmirror", "fg": 1631}, {"id": "f_glass_fridge", "fg": 1632}, {"id": "f_cattails_season_spring", "fg": 1636, "rotates": false}, {"id": "f_cattails_season_summer", "fg": 1634, "rotates": false}, {"id": "f_cattails_season_autumn", "fg": 1635, "rotates": false}, {"id": "f_cattails_season_winter", "fg": 1633, "rotates": false}, {"id": "f_workbench", "fg": 1637}, {"id": "f_dumpster", "multitile": true, "fg": 1640, "additional_tiles": [{"id": "edge", "fg": [1641, 1644]}, {"id": "end_piece", "fg": [1638, 1642, 1643, 1639]}, {"id": "unconnected", "fg": 1640}]}, {"id": "f_washer", "fg": 1645}, {"id": "f_fireplace", "fg": 1646}, {"id": "f_home_furnace", "fg": 1647}, {"id": "f_locker", "fg": 1648}, {"id": "f_statue", "fg": 1649}, {"id": "f_woodstove", "fg": 1650}, {"id": "f_brazier", "fg": 1651}, {"id": "f_birdbath", "fg": 1652}, {"id": "f_rack_coat", "fg": 1653}, {"id": "f_water_heater", "fg": 1654}, {"id": "f_oven", "fg": 1655}, {"id": "player_male", "fg": 1789}, {"id": "npc_male", "fg": 1789}, {"id": "npc_female", "fg": 1790}, {"id": "player_female", "fg": 1790}, {"id": "t_gutter_downspout", "fg": 1656}, {"id": "t_utility_light", "fg": 1657}, {"id": "t_little_column", "fg": 1658, "bg": 1664}, {"id": "t_machinery_heavy", "fg": 1659, "bg": 1664}, {"id": "t_atm", "fg": 1660, "bg": 1664}, {"id": "t_column", "fg": 1661, "bg": 1664}, {"id": "t_machinery_electronic", "fg": 1668, "bg": 1664}, {"id": "t_machinery_old", "fg": 1669, "bg": 1667}, {"id": "t_stairs_up", "fg": 1670}, {"id": "t_wood_stairs_up", "fg": 1671}, {"id": "t_ladder_up", "fg": 1672}, {"id": "overlay_female_mutation_SCALES", "fg": 1673}, {"id": "overlay_male_mutation_SCALES", "fg": 1674}, {"id": "overlay_female_mutation_TAIL_THICK", "fg": 1675}, {"id": "overlay_male_mutation_TAIL_THICK", "fg": 1676}, {"id": "overlay_female_mutation_TALONS", "fg": 1678}, {"id": "overlay_male_mutation_TALONS", "fg": 1677}, {"id": "overlay_female_mutation_BEAK_PECK", "fg": 1679}, {"id": "overlay_male_mutation_BEAK_PECK", "fg": 1680}, {"id": "overlay_female_mutation_BIRD_EYE", "fg": 1681}, {"id": "overlay_male_mutation_BIRD_EYE", "fg": 1682}, {"id": "overlay_female_mutation_LIZ_EYE", "fg": 1683}, {"id": "overlay_male_mutation_LIZ_EYE", "fg": 1684}, {"id": "overlay_female_mutation_MUZZLE_LONG", "fg": 1686}, {"id": "overlay_male_mutation_MUZZLE_LONG", "fg": 1685}, {"id": "overlay_female_mutation_BEAK_HUM", "fg": 1687}, {"id": "overlay_male_mutation_BEAK_HUM", "fg": 1688}, {"id": "overlay_female_mutation_PLANTSKIN", "fg": 1689}, {"id": "overlay_male_mutation_PLANTSKIN", "fg": 1690}, {"id": "overlay_female_mutation_FANGS", "fg": 1692}, {"id": "overlay_male_mutation_FANGS", "fg": 1691}, {"id": "overlay_female_mutation_CHITIN", "fg": 1693}, {"id": "overlay_male_mutation_CHITIN", "fg": 1694}, {"id": "overlay_female_mutation_ARACHNID_ARMS", "fg": 1695}, {"id": "overlay_male_mutation_ARACHNID_ARMS", "fg": 1696}, {"id": "overlay_female_mutation_FEATHERS", "fg": 1697}, {"id": "overlay_male_mutation_FEATHERS", "fg": 1698}, {"id": "overlay_female_mutation_VINES1", "fg": 1699}, {"id": "overlay_male_mutation_VINES1", "fg": 1700}, {"id": "overlay_female_mutation_WINGS_BIRD", "fg": 1702}, {"id": "overlay_male_mutation_WINGS_BIRD", "fg": 1701}, {"id": "overlay_female_mutation_BEAK", "fg": 1704}, {"id": "overlay_male_mutation_BEAK", "fg": 1703}, {"id": "overlay_female_mutation_CHITIN2", "fg": 1705}, {"id": "overlay_male_mutation_CHITIN2", "fg": 1706}, {"id": "overlay_female_mutation_LEAVES", "fg": 1707}, {"id": "overlay_male_mutation_LEAVES", "fg": 1708}, {"id": "overlay_male_mutation_hair_brown_medium", "fg": 1709}, {"id": "overlay_female_mutation_hair_brown_medium", "fg": 1710}, {"id": "overlay_male_mutation_hair_brown_fro", "fg": 1711}, {"id": "overlay_female_mutation_hair_brown_fro", "fg": 1712}, {"id": "overlay_male_mutation_hair_red_mohawk", "fg": 1713}, {"id": "overlay_female_mutation_hair_red_mohawk", "fg": 1714}, {"id": "overlay_male_mutation_hair_brown_mohawk", "fg": 1716}, {"id": "overlay_female_mutation_hair_brown_mohawk", "fg": 1715}, {"id": "overlay_male_mutation_hair_brown_crewcut", "fg": 1718}, {"id": "overlay_female_mutation_hair_brown_crewcut", "fg": 1717}, {"id": "overlay_male_mutation_hair_black_crewcut", "fg": 1720}, {"id": "overlay_female_mutation_hair_black_crewcut", "fg": 1719}, {"id": "overlay_male_mutation_hair_gray_medium", "fg": 1722}, {"id": "overlay_female_mutation_hair_gray_medium", "fg": 1721}, {"id": "overlay_male_mutation_hair_white_crewcut", "fg": 1723}, {"id": "overlay_female_mutation_hair_white_crewcut", "fg": 1724}, {"id": "overlay_male_mutation_hair_red_medium", "fg": 1726}, {"id": "overlay_female_mutation_hair_red_medium", "fg": 1725}, {"id": "overlay_male_mutation_hair_blond_short", "fg": 1728}, {"id": "overlay_female_mutation_hair_blond_short", "fg": 1727}, {"id": "overlay_male_mutation_hair_white_mohawk", "fg": 1730}, {"id": "overlay_female_mutation_hair_white_mohawk", "fg": 1729}, {"id": "overlay_male_mutation_hair_red_crewcut", "fg": 1732}, {"id": "overlay_female_mutation_hair_red_crewcut", "fg": 1731}, {"id": "overlay_male_mutation_hair_red_fro", "fg": 1734}, {"id": "overlay_female_mutation_hair_red_fro", "fg": 1733}, {"id": "overlay_male_mutation_hair_white_medium", "fg": 1735}, {"id": "overlay_female_mutation_hair_white_medium", "fg": 1736}, {"id": "overlay_male_mutation_hair_black_medium", "fg": 1737}, {"id": "overlay_female_mutation_hair_black_medium", "fg": 1738}, {"id": "overlay_male_mutation_hair_blond_medium", "fg": 1740}, {"id": "overlay_female_mutation_hair_blond_medium", "fg": 1739}, {"id": "overlay_male_mutation_hair_black_fro", "fg": 1742}, {"id": "overlay_female_mutation_hair_black_fro", "fg": 1741}, {"id": "overlay_male_mutation_hair_black_mohawk", "fg": 1743}, {"id": "overlay_female_mutation_hair_black_mohawk", "fg": 1744}, {"id": "overlay_male_mutation_hair_gray_mohawk", "fg": 1745}, {"id": "overlay_female_mutation_hair_gray_mohawk", "fg": 1746}, {"id": "overlay_male_mutation_hair_brown_long", "fg": 1748}, {"id": "overlay_female_mutation_hair_brown_long", "fg": 1747}, {"id": "overlay_male_mutation_hair_gray_short", "fg": 1750}, {"id": "overlay_female_mutation_hair_gray_short", "fg": 1749}, {"id": "overlay_male_mutation_hair_white_short", "fg": 1751}, {"id": "overlay_female_mutation_hair_white_short", "fg": 1752}, {"id": "overlay_male_mutation_hair_white_long", "fg": 1753}, {"id": "overlay_female_mutation_hair_white_long", "fg": 1754}, {"id": "overlay_male_mutation_hair_gray_long", "fg": 1756}, {"id": "overlay_female_mutation_hair_gray_long", "fg": 1755}, {"id": "overlay_male_mutation_hair_brown_short", "fg": 1757}, {"id": "overlay_female_mutation_hair_brown_short", "fg": 1758}, {"id": "overlay_male_mutation_hair_gray_crewcut", "fg": 1760}, {"id": "overlay_female_mutation_hair_gray_crewcut", "fg": 1759}, {"id": "overlay_male_mutation_hair_blond_crewcut", "fg": 1762}, {"id": "overlay_female_mutation_hair_blond_crewcut", "fg": 1761}, {"id": "overlay_male_mutation_hair_black_short", "fg": 1763}, {"id": "overlay_female_mutation_hair_black_short", "fg": 1764}, {"id": "overlay_male_mutation_hair_blond_mohawk", "fg": 1766}, {"id": "overlay_female_mutation_hair_blond_mohawk", "fg": 1765}, {"id": "overlay_male_mutation_hair_black_long", "fg": 1768}, {"id": "overlay_female_mutation_hair_black_long", "fg": 1767}, {"id": "overlay_male_mutation_hair_blond_fro", "fg": 1769}, {"id": "overlay_female_mutation_hair_blond_fro", "fg": 1770}, {"id": "overlay_male_mutation_hair_blond_long", "fg": 1772}, {"id": "overlay_female_mutation_hair_blond_long", "fg": 1771}, {"id": "overlay_male_mutation_hair_gray_fro", "fg": 1774}, {"id": "overlay_female_mutation_hair_gray_fro", "fg": 1773}, {"id": "overlay_male_mutation_hair_red_short", "fg": 1776}, {"id": "overlay_female_mutation_hair_red_short", "fg": 1775}, {"id": "overlay_male_mutation_hair_red_long", "fg": 1778}, {"id": "overlay_female_mutation_hair_red_long", "fg": 1777}, {"id": "overlay_male_mutation_hair_white_fro", "fg": 1779}, {"id": "overlay_female_mutation_hair_white_fro", "fg": 1780}, {"id": "overlay_female_mutation_SKIN_LIGHT", "fg": 1782, "bg": 2179}, {"id": "overlay_male_mutation_SKIN_LIGHT", "fg": 1781, "bg": 2179}, {"id": "overlay_female_mutation_SKIN_DARK", "fg": 1784, "bg": 2179}, {"id": "overlay_male_mutation_SKIN_DARK", "fg": 1783, "bg": 2179}, {"id": "overlay_female_mutation_SKIN_TAN", "fg": 1786, "bg": 2179}, {"id": "overlay_male_mutation_SKIN_TAN", "fg": 1785, "bg": 2179}, {"id": "overlay_male_mutation_SKIN_PINK", "fg": 1788, "bg": 2179}, {"id": "overlay_female_mutation_SKIN_PINK", "fg": 1787, "bg": 2179}, {"id": "overlay_male_mutation_SKIN_MEDIUM", "fg": 1789, "bg": 2179}, {"id": "overlay_female_mutation_SKIN_MEDIUM", "fg": 1790, "bg": 2179}, {"id": "overlay_wielded_bag_canvas", "fg": 1791}, {"id": "overlay_female_wielded_wood_panel", "fg": 1793}, {"id": "overlay_male_wielded_wood_panel", "fg": 1792}, {"id": "overlay_wielded_jar_glass", "fg": 1794}, {"id": "overlay_female_wielded_rifle_flintlock", "fg": 1796}, {"id": "overlay_male_wielded_rifle_flintlock", "fg": 1795}, {"id": "overlay_female_wielded_bat_metal", "fg": 1798}, {"id": "overlay_male_wielded_bat_metal", "fg": 1797}, {"id": "overlay_male_wielded_pointy_stick", "fg": 1799}, {"id": "overlay_female_wielded_pointy_stick", "fg": 1804}, {"id": "overlay_male_wielded_spear_wood", "fg": 1814}, {"id": "overlay_female_wielded_spear_wood", "fg": 1813}, {"id": "overlay_male_wielded_spear_spike", "fg": 1808}, {"id": "overlay_female_wielded_spear_spike", "fg": 1815}, {"id": "overlay_male_wielded_spear_knife", "fg": 1811}, {"id": "overlay_female_wielded_spear_knife", "fg": 1812}, {"id": "overlay_male_wielded_spear_knife_superior", "fg": 1801}, {"id": "overlay_female_wielded_spear_knife_superior", "fg": 1816}, {"id": "overlay_male_wielded_spear_rebar", "fg": 1806}, {"id": "overlay_female_wielded_spear_rebar", "fg": 1810}, {"id": "overlay_male_wielded_spear_pipe", "fg": 1807}, {"id": "overlay_female_wielded_spear_pipe", "fg": 1802}, {"id": "overlay_male_wielded_spear_steel", "fg": 1803}, {"id": "overlay_female_wielded_spear_steel", "fg": 1800}, {"id": "overlay_male_wielded_spear_copper", "fg": 1805}, {"id": "overlay_female_wielded_spear_copper", "fg": 1809}, {"id": "overlay_wielded_flashlight", "fg": 1818}, {"id": "overlay_wielded_heavy_flashlight", "fg": 1817}, {"id": "overlay_wielded_jug_plastic", "fg": 1819}, {"id": "overlay_female_wielded_pot", "fg": 1821}, {"id": "overlay_male_wielded_pot", "fg": 1820}, {"id": "overlay_wielded_fire_ax", "fg": 1822}, {"id": "overlay_wielded_ax", "fg": 1823}, {"id": "overlay_wielded_hatchet", "fg": 1824}, {"id": "overlay_wielded_bottle_glass", "fg": 1825}, {"id": "overlay_female_wielded_rag", "fg": 1826}, {"id": "overlay_male_wielded_rag", "fg": 1827}, {"id": "overlay_female_wielded_corpse_generic_human", "fg": 1828}, {"id": "overlay_male_wielded_corpse_generic_human", "fg": 1829}, {"id": "overlay_male_wielded_chainsaw_off", "fg": 1830}, {"id": "overlay_female_wielded_chainsaw_off", "fg": 1831}, {"id": "overlay_female_wielded_teapot", "fg": 1833}, {"id": "overlay_male_wielded_teapot", "fg": 1832}, {"id": "overlay_male_wielded_1st_aid", "fg": 1834}, {"id": "overlay_female_wielded_1st_aid", "fg": 1835}, {"id": "overlay_female_wielded_nailbat", "fg": 1836}, {"id": "overlay_male_wielded_nailbat", "fg": 1837}, {"id": "overlay_male_wielded_primitive_hammer", "fg": 1838}, {"id": "overlay_female_wielded_primitive_hammer", "fg": 1839}, {"id": "overlay_wielded_steel_lump", "fg": 1840}, {"id": "overlay_wielded_antibiotics", "fg": 1841}, {"id": "overlay_wielded_weak_antibiotic", "fg": 1853}, {"id": "overlay_wielded_strong_antibiotic", "fg": 1848}, {"id": "overlay_wielded_calcium_tablet", "fg": 1843}, {"id": "overlay_wielded_vitamins", "fg": 1851}, {"id": "overlay_wielded_gummy_vitamins", "fg": 1842}, {"id": "overlay_wielded_antifungal", "fg": 1849}, {"id": "overlay_wielded_antiparasitic", "fg": 1846}, {"id": "overlay_wielded_iodine", "fg": 1852}, {"id": "overlay_wielded_prussian_blue", "fg": 1850}, {"id": "overlay_wielded_tramadol", "fg": 1845}, {"id": "overlay_wielded_codeine", "fg": 1844}, {"id": "overlay_wielded_oxycodone", "fg": 1847}, {"id": "overlay_wielded_rebar", "fg": 1854}, {"id": "overlay_wielded_steel_glass_shard", "fg": 1855}, {"id": "overlay_male_wielded_pot_copper", "fg": 1857}, {"id": "overlay_female_wielded_pot_copper", "fg": 1856}, {"id": "overlay_female_wielded_nailboard", "fg": 1858}, {"id": "overlay_male_wielded_nailboard", "fg": 1859}, {"id": "overlay_wielded_scissors", "fg": 1860}, {"id": "overlay_female_wielded_ak74", "fg": 1862}, {"id": "overlay_male_wielded_ak74", "fg": 1861}, {"id": "overlay_wielded_coffeemaker", "fg": 1863}, {"id": "overlay_male_wielded_thermos", "fg": 1865}, {"id": "overlay_female_wielded_thermos", "fg": 1864}, {"id": "overlay_female_wielded_stick", "fg": 1866}, {"id": "overlay_male_wielded_stick", "fg": 1867}, {"id": "overlay_wielded_steel_chunk", "fg": 1868}, {"id": "overlay_wielded_pipe", "fg": 1869}, {"id": "overlay_wielded_bottle_plastic", "fg": 1870}, {"id": "overlay_wielded_broom", "fg": 1871}, {"id": "overlay_wielded_sw_619", "fg": 1872}, {"id": "overlay_wielded_rolling_pin", "fg": 1873}, {"id": "overlay_wielded_makeshift_crowbar", "fg": 1874}, {"id": "overlay_female_wielded_bat", "fg": 1876}, {"id": "overlay_male_wielded_bat", "fg": 1875}, {"id": "overlay_male_wielded_wood_sheet", "fg": 1877}, {"id": "overlay_female_wielded_wood_sheet", "fg": 1878}, {"id": "overlay_wielded_jar_3l_glass_sealed", "fg": 1879}, {"id": "overlay_wielded_box_large", "fg": 1880}, {"id": "overlay_wielded_crowbar", "fg": 1884}, {"id": "overlay_male_wielded_screwdriver", "fg": 1885}, {"id": "overlay_female_wielded_screwdriver", "fg": 1881}, {"id": "overlay_wielded_shovel", "fg": 1886}, {"id": "overlay_wielded_wrench", "fg": 1882}, {"id": "overlay_wielded_hammer", "fg": 1883}, {"id": "overlay_wielded_arming_sword", "fg": 1887}, {"id": "overlay_wielded_brick", "fg": 1888}, {"id": "overlay_male_wielded_2x4", "fg": 1889}, {"id": "overlay_female_wielded_2x4", "fg": 1890}, {"id": "overlay_wielded_katana", "fg": 1891}, {"id": "overlay_wielded_bottle_plastic_small", "fg": 1892}, {"id": "overlay_wielded_sharp_rock", "fg": 1893}, {"id": ["overlay_wielded_glock_17", "overlay_wielded_glock_19", "overlay_wielded_glock_18c", "overlay_wielded_glock_22", "overlay_wielded_glock_31"], "fg": 1894}, {"id": "overlay_wielded_box_small", "fg": 1895}, {"id": "overlay_male_wielded_pillow", "fg": 1897}, {"id": "overlay_female_wielded_pillow", "fg": 1898}, {"id": "overlay_male_wielded_down_pillow", "fg": 1899}, {"id": "overlay_female_wielded_down_pillow", "fg": 1896}, {"id": "overlay_wielded_mop", "fg": 1900}, {"id": ["overlay_wielded_corpse_mon_ant", "overlay_wielded_corpse_mon_ant_soldier", "overlay_wielded_corpse_mon_ant_queen"], "fg": 1902}, {"id": ["overlay_wielded_corpse_mon_ant_acid", "overlay_wielded_corpse_mon_ant_acid_soldier", "overlay_wielded_corpse_mon_ant_acid_queen"], "fg": 1904}, {"id": ["overlay_male_wielded_corpse_mon_zombie", "overlay_male_wielded_corpse_mon_zombie_grappler", "overlay_male_wielded_corpse_mon_zombie_biter", "overlay_male_wielded_corpse_mon_zombie_tough", "overlay_male_wielded_corpse_mon_zombie_hunter", "overlay_male_wielded_corpse_mon_zombie_brute", "overlay_male_wielded_corpse_mon_zombie_predator", "overlay_male_wielded_corpse_mon_zombie_necro", "overlay_male_wielded_corpse_mon_zombie_thorny", "overlay_male_wielded_corpse_mon_zombie_smoker", "overlay_male_wielded_corpse_mon_zombie_shady", "overlay_male_wielded_corpse_mon_zombie_master", "overlay_male_wielded_corpse_mon_zombie_acidic", "overlay_male_wielded_corpse_mon_zombie_fat", "overlay_male_wielded_corpse_mon_zombie_corrosive", "overlay_male_wielded_corpse_mon_zombie_screecher", "overlay_male_wielded_corpse_mon_zombie_scientist", "overlay_male_wielded_corpse_mon_zombie_runner", "overlay_male_wielded_corpse_mon_zombie_spitter", "overlay_male_wielded_corpse_mon_zombie_labsecurity", "overlay_male_wielded_corpse_mon_zombie_child", "overlay_male_wielded_corpse_mon_zombie_hulk", "overlay_male_wielded_corpse_mon_zombie_brute_grappler", "overlay_male_wielded_corpse_mon_zombie_brute_ninja", "overlay_male_wielded_corpse_mon_zombie_kevlar_2", "overlay_male_wielded_corpse_mon_zombie_kevlar_1", "overlay_male_wielded_corpse_mon_zombie_hazmat", "overlay_male_wielded_corpse_mon_zombie_electric", "overlay_male_wielded_corpse_mon_zombie_technician", "overlay_male_wielded_corpse_mon_zombie_fungus", "overlay_male_wielded_corpse_mon_zombie_cop", "overlay_male_wielded_corpse_mon_zombie_child_fungus", "overlay_male_wielded_corpse_mon_zombie_swimmer", "overlay_male_wielded_corpse_mon_zombie_mancroc", "overlay_male_wielded_corpse_mon_zombie_soldier", "overlay_male_wielded_corpse_mon_zombie_skull", "overlay_male_wielded_corpse_mon_zombie_brainless", "overlay_male_wielded_corpse_mon_zombie_survivor", "overlay_male_wielded_corpse_mon_zombie_brute_shocker", "overlay_male_wielded_corpse_mon_zombie_soldier_acid_1", "overlay_male_wielded_corpse_mon_zombie_soldier_blackops_2", "overlay_male_wielded_corpse_mon_zombie_soldier_blackops_1", "overlay_male_wielded_corpse_mon_zombie_soldier_acid_2", "overlay_male_wielded_corpse_mon_zombie_shriekling", "overlay_male_wielded_corpse_mon_zombie_ears", "overlay_male_wielded_corpse_mon_zombie_nullfield", "overlay_male_wielded_corpse_mon_zombie_waif", "overlay_male_wielded_corpse_mon_zombie_sproglodyte", "overlay_male_wielded_corpse_mon_zombie_creepy", "overlay_male_wielded_corpse_mon_zombie_anklebiter", "overlay_male_wielded_corpse_mon_zombie_bio_op", "overlay_male_wielded_corpse_mon_zombie_armored", "overlay_male_wielded_corpse_mon_zombie_prisoner", "overlay_male_wielded_corpse_mon_zombie_military_pilot"], "fg": 1901}, {"id": ["overlay_female_wielded_corpse_mon_zombie", "overlay_female_wielded_corpse_mon_zombie_grappler", "overlay_female_wielded_corpse_mon_zombie_biter", "overlay_female_wielded_corpse_mon_zombie_tough", "overlay_female_wielded_corpse_mon_zombie_hunter", "overlay_female_wielded_corpse_mon_zombie_brute", "overlay_female_wielded_corpse_mon_zombie_predator", "overlay_female_wielded_corpse_mon_zombie_necro", "overlay_female_wielded_corpse_mon_zombie_thorny", "overlay_female_wielded_corpse_mon_zombie_smoker", "overlay_female_wielded_corpse_mon_zombie_shady", "overlay_female_wielded_corpse_mon_zombie_master", "overlay_female_wielded_corpse_mon_zombie_acidic", "overlay_female_wielded_corpse_mon_zombie_fat", "overlay_female_wielded_corpse_mon_zombie_corrosive", "overlay_female_wielded_corpse_mon_zombie_screecher", "overlay_female_wielded_corpse_mon_zombie_scientist", "overlay_female_wielded_corpse_mon_zombie_runner", "overlay_female_wielded_corpse_mon_zombie_spitter", "overlay_female_wielded_corpse_mon_zombie_labsecurity", "overlay_female_wielded_corpse_mon_zombie_child", "overlay_female_wielded_corpse_mon_zombie_hulk", "overlay_female_wielded_corpse_mon_zombie_brute_grappler", "overlay_female_wielded_corpse_mon_zombie_brute_ninja", "overlay_female_wielded_corpse_mon_zombie_kevlar_2", "overlay_female_wielded_corpse_mon_zombie_kevlar_1", "overlay_female_wielded_corpse_mon_zombie_hazmat", "overlay_female_wielded_corpse_mon_zombie_electric", "overlay_female_wielded_corpse_mon_zombie_technician", "overlay_female_wielded_corpse_mon_zombie_fungus", "overlay_female_wielded_corpse_mon_zombie_cop", "overlay_female_wielded_corpse_mon_zombie_child_fungus", "overlay_female_wielded_corpse_mon_zombie_swimmer", "overlay_female_wielded_corpse_mon_zombie_mancroc", "overlay_female_wielded_corpse_mon_zombie_soldier", "overlay_female_wielded_corpse_mon_zombie_skull", "overlay_female_wielded_corpse_mon_zombie_brainless", "overlay_female_wielded_corpse_mon_zombie_survivor", "overlay_female_wielded_corpse_mon_zombie_brute_shocker", "overlay_female_wielded_corpse_mon_zombie_soldier_acid_1", "overlay_female_wielded_corpse_mon_zombie_soldier_blackops_2", "overlay_female_wielded_corpse_mon_zombie_soldier_blackops_1", "overlay_female_wielded_corpse_mon_zombie_soldier_acid_2", "overlay_female_wielded_corpse_mon_zombie_shriekling", "overlay_female_wielded_corpse_mon_zombie_ears", "overlay_female_wielded_corpse_mon_zombie_nullfield", "overlay_female_wielded_corpse_mon_zombie_waif", "overlay_female_wielded_corpse_mon_zombie_sproglodyte", "overlay_female_wielded_corpse_mon_zombie_creepy", "overlay_female_wielded_corpse_mon_zombie_anklebiter", "overlay_female_wielded_corpse_mon_zombie_bio_op", "overlay_female_wielded_corpse_mon_zombie_armored", "overlay_female_wielded_corpse_mon_zombie_prisoner", "overlay_female_wielded_corpse_mon_zombie_military_pilot"], "fg": 1903}, {"id": "overlay_wielded_box_medium", "fg": 1905}, {"id": "overlay_wielded_rock", "fg": 1906}, {"id": "overlay_female_wielded_stick_long", "fg": 1907}, {"id": "overlay_male_wielded_stick_long", "fg": 1908}, {"id": "overlay_wielded_log", "fg": 1909}, {"id": "overlay_female_wielded_television", "fg": 1911}, {"id": "overlay_male_wielded_television", "fg": 1910}, {"id": "overlay_wielded_jar_glass_sealed", "fg": 1912}, {"id": "overlay_wielded_jar_3l_glass", "fg": 1913}, {"id": "overlay_female_wielded_bwirebat", "fg": 1914}, {"id": "overlay_male_wielded_bwirebat", "fg": 1915}, {"id": "overlay_wielded_bag_plastic", "fg": 1916}, {"id": "overlay_female_wielded_splinter", "fg": 1918}, {"id": "overlay_male_wielded_splinter", "fg": 1917}, {"id": "overlay_wielded_ar15", "fg": 1919}, {"id": "overlay_female_worn_pants_army", "fg": 1921}, {"id": "overlay_male_worn_pants_army", "fg": 1920}, {"id": "overlay_female_worn_hat_noise_cancelling", "fg": 1923}, {"id": "overlay_male_worn_hat_noise_cancelling", "fg": 1922}, {"id": "overlay_female_worn_striped_pants", "fg": 1924}, {"id": "overlay_male_worn_striped_pants", "fg": 1925}, {"id": "overlay_female_worn_motorbike_pants", "fg": 1927}, {"id": "overlay_male_worn_motorbike_pants", "fg": 1926}, {"id": "overlay_female_worn_beekeeping_hood", "fg": 1928}, {"id": "overlay_male_worn_beekeeping_hood", "fg": 1929}, {"id": "overlay_male_worn_ski_jacket", "fg": 1931}, {"id": "overlay_female_worn_ski_jacket", "fg": 1930}, {"id": "overlay_female_worn_boots_bunker", "fg": 1932}, {"id": "overlay_male_worn_boots_bunker", "fg": 1933}, {"id": "overlay_male_worn_leg_warmers", "fg": 1935}, {"id": "overlay_female_worn_leg_warmers", "fg": 1934}, {"id": "overlay_worn_dress_shoes", "fg": 1936}, {"id": "overlay_male_worn_glasses_bifocal", "fg": 1938}, {"id": "overlay_female_worn_glasses_bifocal", "fg": 1937}, {"id": "overlay_female_worn_glasses_safety", "fg": 1940}, {"id": "overlay_male_worn_glasses_safety", "fg": 1939}, {"id": "overlay_female_worn_ragpouch", "fg": 1941}, {"id": "overlay_male_worn_ragpouch", "fg": 1942}, {"id": "overlay_female_worn_welding_mask_crude_raised", "fg": 1943}, {"id": "overlay_male_worn_welding_mask_crude_raised", "fg": 1944}, {"id": "overlay_male_worn_gloves_leather", "fg": 1946}, {"id": "overlay_female_worn_gloves_leather", "fg": 1945}, {"id": "overlay_female_worn_helmet_army", "fg": 1947}, {"id": "overlay_male_worn_helmet_army", "fg": 1948}, {"id": "overlay_female_worn_pants_faux_fur", "fg": 1949}, {"id": "overlay_male_worn_pants_faux_fur", "fg": 1950}, {"id": "overlay_male_worn_long_glove_white", "fg": 1952}, {"id": "overlay_female_worn_long_glove_white", "fg": 1951}, {"id": "overlay_female_worn_jacket_army", "fg": 1953}, {"id": "overlay_male_worn_jacket_army", "fg": 1954}, {"id": "overlay_female_worn_pants_cargo", "fg": 1955}, {"id": "overlay_male_worn_pants_cargo", "fg": 1956}, {"id": "overlay_female_worn_gloves_work", "fg": 1957}, {"id": "overlay_male_worn_gloves_work", "fg": 1958}, {"id": "overlay_female_worn_geta", "fg": 1959}, {"id": "overlay_male_worn_geta", "fg": 1960}, {"id": "overlay_male_worn_hat_fur", "fg": 1961}, {"id": "overlay_female_worn_hat_fur", "fg": 1962}, {"id": "overlay_female_worn_gloves_wool", "fg": 1964}, {"id": "overlay_male_worn_gloves_wool", "fg": 1963}, {"id": "overlay_male_worn_arm_warmers", "fg": 1965}, {"id": "overlay_female_worn_arm_warmers", "fg": 1966}, {"id": "overlay_female_worn_beret", "fg": 1968}, {"id": "overlay_male_worn_beret", "fg": 1967}, {"id": "overlay_female_worn_boxer_briefs", "fg": 1969}, {"id": "overlay_male_worn_boxer_briefs", "fg": 1970}, {"id": "overlay_male_worn_touring_suit", "fg": 1972}, {"id": "overlay_female_worn_touring_suit", "fg": 1971}, {"id": "overlay_female_worn_pants_checkered", "fg": 1974}, {"id": "overlay_male_worn_pants_checkered", "fg": 1973}, {"id": "overlay_male_worn_turban", "fg": 1975}, {"id": "overlay_female_worn_turban", "fg": 1976}, {"id": "overlay_female_worn_pants_ski", "fg": 1978}, {"id": "overlay_male_worn_pants_ski", "fg": 1977}, {"id": "overlay_female_worn_glasses_bal", "fg": 1979}, {"id": "overlay_male_worn_glasses_bal", "fg": 1980}, {"id": "overlay_male_worn_boots", "fg": 1981}, {"id": "overlay_female_worn_boots", "fg": 1982}, {"id": "overlay_female_worn_corset", "fg": 1984}, {"id": "overlay_male_worn_corset", "fg": 1983}, {"id": "overlay_female_worn_welding_mask", "fg": 1985}, {"id": "overlay_male_worn_welding_mask", "fg": 1986}, {"id": "overlay_female_worn_jacket_flannel", "fg": 1987}, {"id": "overlay_male_worn_jacket_flannel", "fg": 1988}, {"id": "overlay_male_worn_shorts", "fg": 1990}, {"id": "overlay_female_worn_shorts", "fg": 1989}, {"id": "overlay_female_worn_dinosuit", "fg": 1992}, {"id": "overlay_male_worn_dinosuit", "fg": 1991}, {"id": "overlay_male_worn_undershirt", "fg": 1993}, {"id": "overlay_female_worn_boots_combat", "fg": 1994}, {"id": "overlay_male_worn_boots_combat", "fg": 1995}, {"id": "overlay_female_worn_stockings", "fg": 1996}, {"id": "overlay_male_worn_stockings", "fg": 1997}, {"id": "overlay_male_worn_skirt_leather", "fg": 1999}, {"id": "overlay_female_worn_skirt_leather", "fg": 1998}, {"id": "overlay_female_worn_boots_rubber", "fg": 2001}, {"id": "overlay_male_worn_boots_rubber", "fg": 2000}, {"id": "overlay_female_worn_dress_shirt", "fg": 2002}, {"id": "overlay_male_worn_dress_shirt", "fg": 2003}, {"id": "overlay_female_worn_gloves_golf", "fg": 2005}, {"id": "overlay_male_worn_gloves_golf", "fg": 2004}, {"id": "overlay_female_worn_army_top", "fg": 2006}, {"id": "overlay_male_worn_army_top", "fg": 2007}, {"id": "overlay_female_worn_boxer_shorts", "fg": 2009}, {"id": "overlay_male_worn_boxer_shorts", "fg": 2008}, {"id": "overlay_female_worn_helmet_kabuto", "fg": 2010}, {"id": "overlay_male_worn_helmet_kabuto", "fg": 2011}, {"id": "overlay_male_worn_shoes_bowling", "fg": 2012}, {"id": "overlay_female_worn_shoes_bowling", "fg": 2013}, {"id": "overlay_female_worn_gloves_medical", "fg": 2014}, {"id": "overlay_male_worn_gloves_medical", "fg": 2015}, {"id": "overlay_worn_jacket_jean", "fg": 2016}, {"id": "overlay_female_worn_boots_fur", "fg": 2017}, {"id": "overlay_male_worn_boots_fur", "fg": 2018}, {"id": "overlay_male_worn_blanket", "fg": 2021}, {"id": "overlay_female_worn_blanket", "fg": 2020}, {"id": "overlay_male_worn_down_blanket", "fg": 2019}, {"id": "overlay_female_worn_down_blanket", "fg": 2022}, {"id": "overlay_male_worn_cargo_pants", "fg": 2023}, {"id": "overlay_female_worn_panties", "fg": 2024}, {"id": "overlay_male_worn_panties", "fg": 2025}, {"id": "overlay_female_worn_hat_hard", "fg": 2026}, {"id": "overlay_male_worn_hat_hard", "fg": 2027}, {"id": "overlay_male_worn_glasses_eye", "fg": 2029}, {"id": "overlay_female_worn_glasses_eye", "fg": 2028}, {"id": "overlay_female_worn_helmet_chitin", "fg": 2031}, {"id": "overlay_male_worn_helmet_chitin", "fg": 2030}, {"id": "overlay_female_worn_boy_shorts", "fg": 2032}, {"id": "overlay_male_worn_boy_shorts", "fg": 2033}, {"id": "overlay_male_worn_sweatshirt", "fg": 2034}, {"id": "overlay_female_worn_sweatshirt", "fg": 2035}, {"id": "overlay_male_worn_rucksack", "fg": 2037}, {"id": "overlay_female_worn_rucksack", "fg": 2036}, {"id": "overlay_female_worn_sunglasses", "fg": 2039}, {"id": "overlay_male_worn_sunglasses", "fg": 2038}, {"id": "overlay_female_worn_mask_dust", "fg": 2040}, {"id": "overlay_male_worn_mask_dust", "fg": 2041}, {"id": "overlay_male_worn_helmet_motor", "fg": 2043}, {"id": "overlay_female_worn_helmet_motor", "fg": 2042}, {"id": "overlay_female_worn_sweater", "fg": 2044}, {"id": "overlay_male_worn_sweater", "fg": 2045}, {"id": "overlay_female_worn_jacket_light", "fg": 2046}, {"id": "overlay_male_worn_jacket_light", "fg": 2047}, {"id": "overlay_male_worn_skirt", "fg": 2049}, {"id": "overlay_female_worn_skirt", "fg": 2048}, {"id": "overlay_female_worn_striped_shirt", "fg": 2051}, {"id": "overlay_male_worn_striped_shirt", "fg": 2050}, {"id": "overlay_female_worn_hat_ball", "fg": 2053}, {"id": "overlay_male_worn_hat_ball", "fg": 2052}, {"id": "overlay_male_worn_bra", "fg": 2055}, {"id": "overlay_female_worn_bra", "fg": 2054}, {"id": "overlay_male_worn_glove_jackson", "fg": 2057}, {"id": "overlay_female_worn_glove_jackson", "fg": 2056}, {"id": "overlay_male_worn_boots_western", "fg": 2058}, {"id": "overlay_female_worn_boots_western", "fg": 2059}, {"id": "overlay_female_worn_hazmat_suit", "fg": 2060}, {"id": "overlay_male_worn_hazmat_suit", "fg": 2061}, {"id": "overlay_female_worn_scarf", "fg": 2063}, {"id": "overlay_male_worn_scarf", "fg": 2062}, {"id": "overlay_male_worn_kevlar", "fg": 2065}, {"id": "overlay_female_worn_kevlar", "fg": 2064}, {"id": "overlay_female_worn_backpack_leather", "fg": 2066}, {"id": "overlay_male_worn_backpack_leather", "fg": 2067}, {"id": "overlay_female_worn_armor_samurai", "fg": 2069}, {"id": "overlay_male_worn_armor_samurai", "fg": 2068}, {"id": "overlay_female_worn_jumpsuit", "fg": 2071}, {"id": "overlay_male_worn_jumpsuit", "fg": 2070}, {"id": "overlay_female_worn_jeans", "fg": 2072}, {"id": "overlay_male_worn_jeans", "fg": 2073}, {"id": "overlay_female_worn_boots_winter", "fg": 2075}, {"id": "overlay_male_worn_boots_winter", "fg": 2074}, {"id": "overlay_female_worn_shorts_denim", "fg": 2076}, {"id": "overlay_male_worn_shorts_denim", "fg": 2077}, {"id": "overlay_female_worn_gloves_rubber", "fg": 2079}, {"id": "overlay_male_worn_gloves_rubber", "fg": 2078}, {"id": "overlay_male_worn_hat_cotton", "fg": 2080}, {"id": "overlay_female_worn_hat_cotton", "fg": 2081}, {"id": "overlay_male_worn_robofac_jumpsuit", "fg": 2083}, {"id": "overlay_female_worn_robofac_jumpsuit", "fg": 2082}, {"id": "overlay_female_worn_gloves_liner", "fg": 2084}, {"id": "overlay_male_worn_gloves_liner", "fg": 2085}, {"id": "overlay_female_worn_shorts_cargo", "fg": 2087}, {"id": "overlay_male_worn_shorts_cargo", "fg": 2086}, {"id": "overlay_female_worn_coat_lab", "fg": 2089}, {"id": "overlay_male_worn_coat_lab", "fg": 2088}, {"id": "overlay_male_worn_tank_top", "fg": 2091}, {"id": "overlay_female_worn_tank_top", "fg": 2090}, {"id": "overlay_male_worn_socks", "fg": 2093}, {"id": "overlay_female_worn_socks", "fg": 2092}, {"id": "overlay_female_worn_sports_bra", "fg": 2094}, {"id": "overlay_male_worn_sports_bra", "fg": 2095}, {"id": "overlay_male_worn_bunker_coat", "fg": 2097}, {"id": "overlay_female_worn_bunker_coat", "fg": 2096}, {"id": "overlay_female_worn_gloves_winter", "fg": 2098}, {"id": "overlay_male_worn_gloves_winter", "fg": 2099}, {"id": "overlay_female_worn_mittens", "fg": 2101}, {"id": "overlay_male_worn_mittens", "fg": 2100}, {"id": "overlay_female_worn_glasses_reading", "fg": 2103}, {"id": "overlay_male_worn_glasses_reading", "fg": 2102}, {"id": "overlay_female_worn_longshirt", "fg": 2105}, {"id": "overlay_male_worn_longshirt", "fg": 2104}, {"id": "overlay_female_worn_gloves_cut_resistant", "fg": 2106}, {"id": "overlay_male_worn_gloves_cut_resistant", "fg": 2107}, {"id": "overlay_female_worn_pants", "fg": 2109}, {"id": "overlay_male_worn_pants", "fg": 2108}, {"id": "overlay_female_worn_maid_hat", "fg": 2112}, {"id": "overlay_female_worn_maid_dress", "fg": 2111}, {"id": "overlay_male_worn_maid_hat", "fg": 2110}, {"id": "overlay_male_worn_maid_dress", "fg": 2113}, {"id": "overlay_male_worn_bunker_pants", "fg": 2114}, {"id": "overlay_female_worn_bunker_pants", "fg": 2115}, {"id": "overlay_female_worn_helmet_bike", "fg": 2116}, {"id": "overlay_male_worn_helmet_bike", "fg": 2117}, {"id": "overlay_female_worn_pants_leather", "fg": 2118}, {"id": "overlay_male_worn_pants_leather", "fg": 2119}, {"id": "overlay_male_worn_boots_steel", "fg": 2121}, {"id": "overlay_female_worn_boots_steel", "fg": 2120}, {"id": "overlay_female_worn_survivor_suit", "fg": 2123}, {"id": "overlay_male_worn_survivor_suit", "fg": 2122}, {"id": "overlay_female_worn_pants_fur", "fg": 2124}, {"id": "overlay_male_worn_pants_fur", "fg": 2125}, {"id": "overlay_female_worn_gloves_fingerless", "fg": 2127}, {"id": "overlay_male_worn_gloves_fingerless", "fg": 2126}, {"id": "overlay_female_worn_union_suit", "fg": 2128}, {"id": "overlay_male_worn_union_suit", "fg": 2129}, {"id": "overlay_female_worn_sneakers", "fg": 2130}, {"id": "overlay_male_worn_sneakers", "fg": 2131}, {"id": "overlay_male_worn_flip_flops", "fg": 2133}, {"id": "overlay_female_worn_flip_flops", "fg": 2132}, {"id": "overlay_male_worn_tshirt", "fg": 2135}, {"id": "overlay_female_worn_tshirt", "fg": 2134}, {"id": "overlay_female_worn_boots_hiking", "fg": 2136}, {"id": "overlay_male_worn_boots_hiking", "fg": 2137}, {"id": "overlay_female_worn_gloves_fur", "fg": 2139}, {"id": "overlay_male_worn_gloves_fur", "fg": 2138}, {"id": "overlay_male_worn_firehelmet", "fg": 2141}, {"id": "overlay_female_worn_firehelmet", "fg": 2140}, {"id": "overlay_female_worn_welding_mask_raised", "fg": 2143}, {"id": "overlay_male_worn_welding_mask_raised", "fg": 2142}, {"id": "overlay_female_worn_bandana", "fg": 2144}, {"id": "overlay_male_worn_bandana", "fg": 2145}, {"id": "overlay_male_worn_hat_knit", "fg": 2147}, {"id": "overlay_female_worn_hat_knit", "fg": 2146}, {"id": "overlay_female_worn_hoodie", "fg": 2149}, {"id": "overlay_male_worn_hoodie", "fg": 2148}, {"id": "overlay_male_worn_coat_winter", "fg": 2150}, {"id": "overlay_female_worn_coat_winter", "fg": 2151}, {"id": "overlay_female_worn_duster", "fg": 2152}, {"id": "overlay_male_worn_duster", "fg": 2153}, {"id": "overlay_female_worn_glasses_monocle", "fg": 2154}, {"id": "overlay_male_worn_glasses_monocle", "fg": 2155}, {"id": "overlay_male_worn_welding_mask_crude", "fg": 2157}, {"id": "overlay_female_worn_welding_mask_crude", "fg": 2156}, {"id": "overlay_male_worn_balclava", "fg": 2159}, {"id": "overlay_female_worn_balclava", "fg": 2158}, {"id": "overlay_female_worn_motorbike_boots", "fg": 2161}, {"id": "overlay_male_worn_motorbike_boots", "fg": 2160}, {"id": "overlay_female_worn_helmet_barbute", "fg": 2162}, {"id": "overlay_male_worn_helmet_barbute", "fg": 2163}, {"id": "overlay_female_worn_gloves_tactical", "fg": 2165}, {"id": "overlay_male_worn_gloves_tactical", "fg": 2164}, {"id": "overlay_female_worn_jeans_red", "fg": 2167}, {"id": "overlay_male_worn_jeans_red", "fg": 2166}, {"id": "overlay_female_worn_cowboy_hat", "fg": 2168}, {"id": "overlay_male_worn_cowboy_hat", "fg": 2169}, {"id": "overlay_male_worn_sundress", "fg": 2171}, {"id": "overlay_female_worn_sundress", "fg": 2170}, {"id": "overlay_female_worn_coat_rain", "fg": 2172}, {"id": "overlay_male_worn_coat_rain", "fg": 2173}, {"id": "overlay_male_worn_gloves_light", "fg": 2175}, {"id": "overlay_female_worn_gloves_light", "fg": 2174}, {"id": "mon_skeleton", "fg": 2176, "bg": 2179}, {"id": "mon_dermatik", "fg": 2177, "bg": 2179}, {"id": "mon_zombie_brainless", "fg": 2178, "bg": 2179}, {"id": "mon_zombie_soldier", "fg": 2180, "bg": 2179}, {"id": "mon_fungaloid", "fg": 2181, "bg": 2179}, {"id": "mon_zougar", "fg": 2182, "bg": 2179}, {"id": "mon_bee", "fg": 2183, "bg": 2179}, {"id": "mon_zombie_swimmer", "fg": 2184, "bg": 2179}, {"id": "mon_mosquito_giant", "fg": 2185, "bg": 2179}, {"id": "mon_cougar", "fg": 2186, "bg": 2179}, {"id": "mon_dragonfly_giant", "fg": 2187, "bg": 2179}, {"id": "mon_fly", "fg": 2188, "bg": 2179}], "//": "range 1617 to 2192", "sprite_width": 32, "sprite_height": 64, "sprite_offset_x": 0, "sprite_offset_y": -32}, {"file": "large.png", "tiles": [{"id": "f_rotary_clothesline", "fg": 2193}, {"id": "f_fridge", "fg": 2194}, {"id": "t_tree_young", "fg": [{"weight": 100, "sprite": 2204}, {"weight": 100, "sprite": 2202}], "bg": 2195}, {"id": "t_tree_young_season_summer", "fg": [{"weight": 100, "sprite": 2200}, {"weight": 100, "sprite": 2201}], "bg": 2198, "rotates": false}, {"id": "t_tree_young_season_autumn", "fg": [{"weight": 100, "sprite": 2206}, {"weight": 100, "sprite": 2205}], "bg": 2197, "rotates": false}, {"id": "t_tree_young_season_winter", "fg": [{"weight": 100, "sprite": 2203}, {"weight": 100, "sprite": 2199}], "bg": 2196, "rotates": false}, {"id": "mon_zoose", "fg": 2207, "bg": 2233}, {"id": "mon_zombie_acidic", "fg": 2210, "bg": 2231}, {"id": "mon_zombie_corrosive", "fg": 2208, "bg": 2231}, {"id": "mon_zombie_spitter", "fg": 2211, "bg": 2231}, {"id": "corpse_mon_zombie_spitter", "fg": 2209}, {"id": "mon_moose", "fg": [{"weight": 2, "sprite": 2213}, {"weight": 2, "sprite": 2212}], "bg": 2233}, {"id": "mon_zombear", "fg": 2214, "bg": 2232}, {"id": "mon_zombie_grappler", "fg": [{"weight": 1, "sprite": 2216}, {"weight": 1, "sprite": 2215}], "bg": 2231}, {"id": "mon_boomer", "fg": 2217, "bg": 2231}, {"id": "mon_zombie", "fg": [{"weight": 100, "sprite": 2218}, {"weight": 150, "sprite": 2221}, {"weight": 100, "sprite": 2223}, {"weight": 100, "sprite": 2219}, {"weight": 150, "sprite": 2220}], "bg": 2231}, {"id": ["corpse_mon_zombie", "corpse_mon_zombie_grappler", "corpse_mon_zombie_biter", "corpse_mon_zombie_hunter", "corpse_mon_zombie_brute", "corpse_mon_zombie_predator", "corpse_mon_zombie_necro", "corpse_mon_zombie_thorny", "corpse_mon_zombie_smoker", "corpse_mon_zombie_shady", "corpse_mon_zombie_master", "corpse_mon_zombie_acidic", "corpse_mon_zombie_fat", "corpse_mon_zombie_corrosive", "corpse_mon_zombie_screecher", "corpse_mon_zombie_scientist", "corpse_mon_zombie_runner", "corpse_mon_zombie_labsecurity", "corpse_mon_zombie_child", "corpse_mon_zombie_hulk", "corpse_mon_zombie_brute_grappler", "corpse_mon_zombie_brute_ninja", "corpse_mon_zombie_kevlar_2", "corpse_mon_zombie_kevlar_1", "corpse_mon_zombie_hazmat", "corpse_mon_zombie_electric", "corpse_mon_zombie_technician", "corpse_mon_zombie_fungus", "corpse_mon_zombie_cop", "corpse_mon_zombie_child_fungus", "corpse_mon_zombie_swimmer", "corpse_mon_zombie_mancroc", "corpse_mon_zombie_soldier", "corpse_mon_zombie_skull", "corpse_mon_zombie_brainless", "corpse_mon_zombie_survivor", "corpse_mon_zombie_brute_shocker", "corpse_mon_zombie_soldier_acid_1", "corpse_mon_zombie_soldier_blackops_2", "corpse_mon_zombie_soldier_blackops_1", "corpse_mon_zombie_soldier_acid_2", "corpse_mon_zombie_shriekling", "corpse_mon_zombie_ears", "corpse_mon_zombie_nullfield", "corpse_mon_zombie_waif", "corpse_mon_zombie_sproglodyte", "corpse_mon_zombie_creepy", "corpse_mon_zombie_anklebiter", "corpse_mon_zombie_bio_op", "corpse_mon_zombie_armored", "corpse_mon_zombie_prisoner", "corpse_mon_zombie_military_pilot"], "fg": 2222}, {"id": "mon_zombie_smoker", "fg": 2224, "bg": 2231}, {"id": "mon_zombie_necro", "fg": 2225, "bg": 2231}, {"id": "mon_zombie_biter", "fg": 2226, "bg": 2232}, {"id": "mon_zombie_hunter", "fg": 2228, "bg": 2232}, {"id": "mon_zombie_cop", "fg": 2230, "bg": 2231}, {"id": "mon_zombie_labsecurity", "fg": 2229, "bg": 2231}, {"id": "mon_zombie_rot", "fg": 2234, "bg": 2231}, {"id": "mon_zombie_gasbag", "fg": [{"weight": 1, "sprite": 2236}, {"weight": 1, "sprite": 2235}], "bg": 2231}, {"id": "mon_blob_large", "fg": 2237, "bg": 2233}, {"id": "mon_ant_soldier", "fg": 2242, "bg": 2233}, {"id": "mon_ant_acid_soldier", "fg": 2245, "bg": 2233}, {"id": "corpse_mon_ant_soldier", "fg": 2243}, {"id": "corpse_mon_ant_acid_soldier", "fg": 2241}, {"id": "mon_ant_queen", "fg": 2239, "bg": 2233}, {"id": "mon_ant_acid_queen", "fg": 2238, "bg": 2233}, {"id": "corpse_mon_ant_queen", "fg": 2240}, {"id": "corpse_mon_ant_acid_queen", "fg": 2244}, {"id": "mon_zombie_shady", "fg": 2246, "bg": 2231}, {"id": "mon_zombie_crawler", "fg": 2247, "bg": 2231}, {"id": "mon_zombie_fat", "fg": [{"weight": 1, "sprite": 2248}, {"weight": 1, "sprite": 2249}], "bg": 2231}, {"id": "mon_zombie_tough", "fg": 2250, "bg": 2231}, {"id": "corpse_mon_zombie_tough", "fg": 2251}, {"id": "mon_zombie_electric", "fg": [{"weight": 1, "sprite": 2254}, {"weight": 1, "sprite": 2252}], "bg": 2231}, {"id": "mon_zombie_nullfield", "fg": [{"weight": 1, "sprite": 2253}, {"weight": 1, "sprite": 2255}], "bg": 2231}, {"id": "mon_zombie_scientist", "fg": [{"weight": 1, "sprite": 2257}, {"weight": 1, "sprite": 2256}], "bg": 2231}, {"id": "mon_wasp", "fg": 2258, "bg": 2232}, {"id": "mon_zombie_runner", "fg": 2259, "bg": 2231}, {"id": "mon_mi_go", "fg": 2260, "bg": 2232}, {"id": "mon_zombie_brute", "fg": [{"weight": 100, "sprite": 2261}, {"weight": 100, "sprite": 2262}, {"weight": 50, "sprite": 2264}, {"weight": 50, "sprite": 2263}], "bg": 2232}, {"id": "mon_bear", "fg": 2265, "bg": 2232}], "//": "range 2193 to 2272", "sprite_width": 64, "sprite_height": 64, "sprite_offset_x": -16, "sprite_offset_y": -32}, {"file": "huge.png", "tiles": [{"id": "mon_zombie_hulk", "fg": 2273}], "//": "range 2273 to 2288", "sprite_width": 64, "sprite_height": 96, "sprite_offset_x": -16, "sprite_offset_y": -64}, {"file": "giant.png", "tiles": [{"id": "t_tree", "fg": [{"weight": 100, "sprite": 2297}, {"weight": 100, "sprite": 2296}, {"weight": 100, "sprite": 2298}, {"weight": 100, "sprite": 2292}], "bg": 2302}, {"id": "t_tree_season_summer", "fg": [{"weight": 100, "sprite": 2290}, {"weight": 100, "sprite": 2295}, {"weight": 100, "sprite": 2291}, {"weight": 100, "sprite": 2289}], "bg": 2303}, {"id": "t_tree_season_autumn", "fg": [{"weight": 100, "sprite": 2300}, {"weight": 100, "sprite": 2294}, {"weight": 100, "sprite": 2293}, {"weight": 100, "sprite": 2299}], "bg": 2301}, {"id": "t_tree_season_winter", "fg": [{"weight": 100, "sprite": 2306}, {"weight": 100, "sprite": 2307}, {"weight": 100, "sprite": 2305}, {"weight": 100, "sprite": 2308}], "bg": 2304}, {"id": "t_tree_dead", "fg": [{"weight": 100, "sprite": 2306}, {"weight": 100, "sprite": 2307}, {"weight": 100, "sprite": 2305}, {"weight": 100, "sprite": 2308}, {"weight": 100, "sprite": 2611}], "bg": 2302}, {"id": "t_tree_dead_season_summer", "fg": [{"weight": 100, "sprite": 2306}, {"weight": 100, "sprite": 2307}, {"weight": 100, "sprite": 2305}, {"weight": 100, "sprite": 2308}, {"weight": 100, "sprite": 2611}], "bg": 2303}, {"id": "t_tree_dead_season_autumn", "fg": [{"weight": 100, "sprite": 2306}, {"weight": 100, "sprite": 2307}, {"weight": 100, "sprite": 2305}, {"weight": 100, "sprite": 2308}, {"weight": 100, "sprite": 2611}], "bg": 2301}, {"id": "t_tree_dead_season_winter", "fg": [{"weight": 100, "sprite": 2306}, {"weight": 100, "sprite": 2307}, {"weight": 100, "sprite": 2305}, {"weight": 100, "sprite": 2308}, {"weight": 100, "sprite": 2611}], "bg": 2304}, {"id": "t_tree_birch", "fg": [{"weight": 1, "sprite": 2313}, {"weight": 1, "sprite": 2310}], "bg": 2302}, {"id": "t_tree_birch_season_summer", "fg": [{"weight": 1, "sprite": 2313}, {"weight": 1, "sprite": 2310}], "bg": 2303}, {"id": "t_tree_birch_season_winter", "fg": [{"weight": 1, "sprite": 2314}, {"weight": 1, "sprite": 2311}], "bg": 2304}, {"id": "t_tree_birch_season_autumn", "fg": [{"weight": 1, "sprite": 2312}, {"weight": 1, "sprite": 2309}], "bg": 2301}], "//": "range 2289 to 2320", "sprite_width": 96, "sprite_height": 96, "sprite_offset_x": -32, "sprite_offset_y": -64}, {"file": "incomplete.png", "tiles": [{"id": "f_sink", "multitile": true, "fg": 2331, "additional_tiles": [{"id": "center", "fg": 2336}, {"id": "corner", "fg": [2322, 2334, 2326, 2321]}, {"id": "t_connection", "fg": [2328, 2335, 2330, 2325]}, {"id": "edge", "fg": [2329, 2327]}, {"id": "end_piece", "fg": [2333, 2324, 2323, 2332]}, {"id": "unconnected", "fg": 2331}]}, {"id": "toolbox", "fg": 2337}, {"id": "acoustic_guitar", "fg": 2338}, {"id": "rope_6", "fg": 2339}, {"id": "colt_army", "fg": 2340}, {"id": "ref_lighter", "fg": 2341}, {"id": "soap", "fg": 2342}, {"id": "waffleiron", "fg": 2343}, {"id": "bubblewrap", "fg": 2344}, {"id": "soldering_iron", "fg": 2345}, {"id": "stepladder", "fg": 2346}, {"id": "mag_pistol", "fg": 2347}, {"id": "brazier", "fg": 2348}, {"id": "pliers", "fg": 2349}, {"id": "spray_can", "fg": 2350}, {"id": "condom", "fg": 2351}, {"id": "colt_navy", "fg": 2352}, {"id": "hotplate", "fg": 2353}, {"id": "charcoal", "fg": 2354}, {"id": "stanag30", "fg": 2355}, {"id": "keg", "fg": 2356}, {"id": "pitchfork", "fg": 2357}, {"id": "knife_steak", "fg": 2358}, {"id": "wood_beam", "fg": 2359}, {"id": "smart_phone", "fg": 2360}, {"id": "mosin91_30", "fg": 2361}, {"id": "extinguisher", "fg": 2362}, {"id": "hk_mp5", "fg": 2363}, {"id": "sw_610", "fg": 2364}, {"id": "colt_lightning", "fg": 2365}, {"id": "mag_smg", "fg": 2366}, {"id": "duct_tape", "fg": 2367}, {"id": "t_chainfence", "multitile": true, "rotates": false, "fg": 2368, "bg": 1376, "additional_tiles": [{"id": "edge", "bg": 1376, "fg": [2371, 2368]}, {"id": "end_piece", "bg": 1376, "fg": [2371, 2368, 2371, 2368]}, {"bg": 1376, "id": "unconnected", "fg": 2368}]}, {"id": "t_chainfence_season_winter", "multitile": true, "rotates": false, "fg": 2368, "bg": 643, "additional_tiles": [{"id": "edge", "bg": 643, "fg": [2371, 2368]}, {"id": "end_piece", "bg": 643, "fg": [2371, 2368, 2371, 2368]}, {"bg": 643, "id": "unconnected", "fg": 2368}]}, {"id": "t_chaingate_c", "fg": 2372, "bg": 1376}, {"id": "t_chaingate_l", "fg": 2373, "bg": 1376}, {"id": "t_chaingate_o", "fg": 2369, "bg": 1376}, {"id": "t_chainfence_posts", "fg": 2370, "bg": 1376}, {"id": "t_chaingate_c_season_winter", "fg": 2372, "bg": 643}, {"id": "t_chaingate_l_season_winter", "fg": 2373, "bg": 643}, {"id": "t_chaingate_o_season_winter", "fg": 2369, "bg": 643}, {"id": "t_chainfence_posts_season_winter", "fg": 2370, "bg": 643}, {"id": "t_strconc_wall", "fg": 2374}, {"id": "t_splitrail_fence_season_spring", "fg": 2375, "bg": 1233}, {"id": "t_splitrail_fence_season_summer", "fg": 2375, "bg": 1240}, {"id": "t_splitrail_fence_season_autumn", "fg": 2375, "bg": 1234}, {"id": "t_splitrail_fence_season_winter", "fg": 2375, "bg": 643}, {"id": "t_railing", "fg": 2376, "bg": 1233}, {"id": "t_console", "fg": 2377}, {"id": "t_underbrush", "fg": [{"weight": 100, "sprite": 2380}, {"weight": 100, "sprite": 2379}], "bg": 1233}, {"id": "t_underbrush_harvested", "fg": 2378, "bg": 1233}, {"id": "t_underbrush_season_summer", "fg": [{"weight": 100, "sprite": 2380}, {"weight": 100, "sprite": 2379}], "bg": 1240}, {"id": "t_underbrush_harvested_season_summer", "fg": 2378, "bg": 1240}, {"id": "t_underbrush_season_autumn", "fg": [{"weight": 100, "sprite": 2380}, {"weight": 100, "sprite": 2379}], "bg": 1234}, {"id": "t_underbrush_harvested_season_winter", "fg": 2378, "bg": 644}, {"id": "t_underbrush_season_winter", "fg": [{"weight": 100, "sprite": 2380}, {"weight": 100, "sprite": 2379}], "bg": 644}, {"id": ["t_window_boarded", "t_window_boarded_noglass"], "fg": 2381, "bg": 758}, {"id": "t_wall_metal", "fg": 2382}, {"id": ["t_window_reinforced", "t_window_reinforced_noglass"], "fg": 2383, "bg": 758}, {"id": "t_door_metal_o", "fg": 2384}, {"id": "t_window_frame", "fg": 2385}, {"id": "t_pavement_y", "fg": 2386}, {"id": "t_pavement_y_season_winter", "fg": 643}, {"id": "t_dirtmound", "fg": 2387}, {"id": "t_reinforced_glass_shutter", "fg": 2389}, {"id": "t_reinforced_glass_shutter_open", "fg": 2388}, {"id": "t_trunk", "multitile": true, "fg": [2390, 2391], "bg": [{"weight": 100, "sprite": 1233}, {"weight": 100, "sprite": 1235}]}, {"id": "t_trunk_season_summer", "multitile": true, "fg": [2390, 2391], "bg": [{"weight": 100, "sprite": 1240}, {"weight": 100, "sprite": 1229}]}, {"id": "t_trunk_season_autumn", "multitile": true, "fg": [2390, 2391], "bg": [{"weight": 100, "sprite": 1234}, {"weight": 100, "sprite": 1228}]}, {"id": "t_trunk_season_winter", "multitile": true, "fg": [2390, 2391], "bg": 643}, {"id": "t_console_broken", "fg": 2392}, {"id": ["t_junk_palisade", "t_junk_wall"], "fg": [{"weight": 100, "sprite": 2396}, {"weight": 100, "sprite": 2395}, {"weight": 100, "sprite": 2393}, {"weight": 100, "sprite": 2394}]}, {"id": "t_door_metal_c", "fg": 2397}, {"id": "fd_smoke", "fg": 2400}, {"id": "fd_fungal_haze", "fg": 2399}, {"id": "fd_nuke_gas", "fg": 2398}, {"id": "fd_acid", "fg": 2401}, {"id": "fd_blood", "fg": 2403}, {"id": ["fd_blood_insect", "fd_blood_invertebrate"], "fg": 2402}, {"id": "fd_electricity", "fg": [{"weight": 100, "sprite": 2405}, {"weight": 100, "sprite": 2404}]}, {"id": "fd_web", "fg": [{"weight": 100, "sprite": 2407}, {"weight": 100, "sprite": 2406}, {"weight": 25, "sprite": 2408}, {"weight": 25, "sprite": 2409}]}], "//": "range 2321 to 2416"}, {"file": "fillerhoder.png", "tiles": [{"id": "vp_aisle_horizontal", "fg": 2429}, {"id": "vp_aisle_vertical", "fg": 2426}, {"id": "vp_bed", "fg": 2425}, {"id": "vp_folding_seat", "fg": 2455}, {"id": "vp_frame_cover", "fg": 2503}, {"id": "vp_frame_cross", "fg": 2502}, {"id": "vp_frame_handle", "fg": 2583}, {"id": "vp_frame_horizontal", "fg": 2520}, {"id": "vp_frame_horizontal_2", "fg": 2524}, {"id": "vp_frame_ne", "fg": 2481}, {"id": "vp_frame_nw", "fg": 2568}, {"id": "vp_frame_se", "fg": 2478}, {"id": "vp_frame_sw", "fg": 2566}, {"id": "vp_frame_vertical", "fg": 2488}, {"id": "vp_frame_vertical_2", "fg": 2551}, {"id": "vp_frame_wood_cover", "fg": 2588}, {"id": "vp_frame_wood_cross", "fg": 2533}, {"id": "vp_frame_wood_handle", "fg": 2583}, {"id": "vp_frame_wood_horizontal", "fg": 2452}, {"id": "vp_frame_wood_horizontal_2", "fg": 2428}, {"id": "vp_frame_wood_ne", "fg": 2457}, {"id": "vp_frame_wood_nw", "fg": 2511}, {"id": "vp_frame_wood_se", "fg": 2436}, {"id": "vp_frame_wood_sw", "fg": 2464}, {"id": "vp_frame_wood_vertical", "fg": 2439}, {"id": "vp_frame_wood_vertical_2", "fg": 2565}, {"id": "vp_hdboard_horizontal", "fg": 2577}, {"id": "vp_hdboard_ne", "fg": 2419}, {"id": "vp_hdboard_nw", "fg": 2499}, {"id": "vp_hdboard_se", "fg": 2442}, {"id": "vp_hdboard_sw", "fg": 2458}, {"id": "vp_hdboard_vertical", "fg": 2546}, {"id": "vp_hdframe_cover", "fg": 2572}, {"id": "vp_hdframe_cross", "fg": 2532}, {"id": "vp_hdframe_horizontal", "fg": 2527}, {"id": "vp_hdframe_horizontal_2", "fg": 2475}, {"id": "vp_hdframe_ne", "fg": 2578}, {"id": "vp_hdframe_nw", "fg": 2530}, {"id": "vp_hdframe_se", "fg": 2545}, {"id": "vp_hdframe_sw", "fg": 2484}, {"id": "vp_hdframe_vertical", "fg": 2491}, {"id": "vp_hdframe_vertical_2", "fg": 2592}, {"id": "vp_reclining_seat", "fg": 2455}, {"id": "vp_saddle", "fg": 2476}, {"id": "vp_seat", "fg": 2455}, {"id": "vp_washing_machine", "fg": 2493}, {"id": "vp_woodboard_horizontal", "fg": 2548}, {"id": "vp_woodboard_ne", "fg": 2556}, {"id": "vp_woodboard_nw", "fg": 2453}, {"id": "vp_woodboard_se", "fg": 2507}, {"id": "vp_woodboard_sw", "fg": 2571}, {"id": "vp_woodboard_vertical", "fg": 2514}, {"id": "vp_woodhalfboard_horizontal", "fg": 2548}, {"id": "vp_woodhalfboard_horizontal_2", "fg": 2548}, {"id": "vp_woodhalfboard_ne", "fg": 2556}, {"id": "vp_woodhalfboard_nw", "fg": 2453}, {"id": "vp_woodhalfboard_se", "fg": 2507}, {"id": "vp_woodhalfboard_sw", "fg": 2571}, {"id": "vp_woodhalfboard_vertical", "fg": 2514}, {"id": "vp_woodhalfboard_vertical_2", "fg": 2514}, {"id": "vp_basketlg", "fg": 2462}, {"id": "vp_basketsm", "fg": 2462}, {"id": "vp_battery_motorbike", "fg": 2590}, {"id": "vp_blade_horizontal", "fg": 2589}, {"id": "vp_blade_vertical", "fg": 2433}, {"id": "vp_box", "fg": 2531}, {"id": "vp_cargo_space", "fg": 2486, "bg": 2565}, {"id": "vp_chemlab", "fg": 2431}, {"id": "vp_controls", "fg": 2505}, {"id": "vp_craft_rig", "fg": 2431}, {"id": "vp_door_internal", "fg": 2471}, {"id": "vp_door_opaque", "fg": 2501}, {"id": "vp_door_shutter", "fg": 2473}, {"id": "vp_door_sliding", "fg": 2518}, {"id": "vp_engine_1cyl", "fg": 2563}, {"id": "vp_engine_electric", "fg": 2468}, {"id": "vp_engine_electric_large", "fg": 2541}, {"id": "vp_engine_inline4", "fg": 2535}, {"id": "vp_engine_plasma", "fg": 2482}, {"id": "vp_engine_v12", "fg": 2550, "bg": 2553}, {"id": "vp_engine_v6", "fg": 2550}, {"id": "vp_engine_v8", "fg": 2553}, {"id": "vp_engine_vtwin", "fg": 2423}, {"id": "vp_external_gas_tank", "fg": 2420}, {"id": "vp_flamethrower", "fg": 2539}, {"id": "vp_floodlight", "fg": 2418, "bg": 2480}, {"id": "vp_foot_pedals", "fg": 2504}, {"id": "vp_fusion_gun", "fg": 2490}, {"id": "vp_gas_tank", "fg": 2420}, {"id": "vp_gas_tank_small", "fg": 2420}, {"id": "vp_hatch", "fg": 2557}, {"id": "vp_hatch_opaque", "fg": 2585}, {"id": "vp_hddoor", "fg": 2497}, {"id": "vp_hddoor_internal", "fg": 2519}, {"id": "vp_hddoor_opaque", "fg": 2465}, {"id": "vp_hddoor_shutter", "fg": 2448}, {"id": "vp_hddoor_sliding", "fg": 2521}, {"id": "vp_hddoor_trunk", "fg": 2500}, {"id": "vp_hdhatch", "fg": 2515}, {"id": "vp_hdhatch_opaque", "fg": 2479}, {"id": "vp_hdroof", "fg": 2559}, {"id": "vp_hydrogen_tank", "fg": 2522}, {"id": "vp_kitchen_unit", "fg": 2431}, {"id": "vp_laser_gun", "fg": 2490}, {"id": "vp_light_blue", "fg": 2510, "bg": 2573}, {"id": "vp_light_red", "fg": 2510, "bg": 2472}, {"id": "vp_lit_aisle_horizontal", "fg": 2429}, {"id": "vp_lit_aisle_vertical", "fg": 2426}, {"id": "vp_m249", "fg": 2569}, {"id": "vp_minifridge", "fg": 2529}, {"id": "vp_minireactor", "fg": 2454}, {"id": "vp_mounted_browning", "fg": 2569}, {"id": "vp_mounted_mk19", "fg": 2569}, {"id": "vp_muffler", "fg": 2554}, {"id": ["vp_omnicam", "vp_omnomnicam"], "fg": 2526, "bg": 2559}, {"id": "vp_plasma_gun", "fg": 2508}, {"id": "vp_plating_hard", "fg": 2512}, {"id": "vp_plating_military", "fg": 2430}, {"id": "vp_plating_spiked", "fg": 2547}, {"id": "vp_plating_steel", "fg": 2430}, {"id": "vp_plating_superalloy", "fg": 2460}, {"id": "vp_plating_wood", "fg": 2489}, {"id": "vp_recharge_station", "fg": 2590, "bg": 2531}, {"id": "vp_reinforced_solar_panel", "fg": 2564, "bg": 2441}, {"id": "vp_reinforced_solar_panel_v2", "fg": 2564, "bg": 2441}, {"id": "vp_reinforced_windshield", "fg": 2564, "bg": 2509}, {"id": "vp_roof", "fg": 2480}, {"id": "vp_roof_cloth", "fg": 2528}, {"id": "vp_seatbelt", "fg": 2424}, {"id": "vp_seatbelt_heavyduty", "fg": 2424}, {"id": "vp_small_storage_battery", "fg": 2590}, {"id": "vp_solar_panel", "fg": 2441}, {"id": "vp_solar_panel_v2", "fg": 2441}, {"id": "vp_solar_panel_v3", "fg": 2441}, {"id": "vp_spike", "fg": 2456}, {"id": "vp_storage_battery", "fg": 2590}, {"id": "vp_storage_car", "fg": 2590}, {"id": "vp_storage_truck", "fg": 2590}, {"id": "vp_trunk", "fg": 2531}, {"id": "vp_trunk_floor", "fg": 2434}, {"id": "vp_turret_mount", "fg": 2506, "bg": 2559}, {"id": "vp_v_curtain", "fg": 2438}, {"id": "vp_veh_forge", "fg": 2450}, {"id": "vp_veh_table", "fg": 2586}, {"id": "vp_water_tank", "fg": 2470}, {"id": "vp_welding_rig", "fg": 2574}, {"id": "vp_wheel", "fg": 2591}, {"id": "vp_wheel_armor", "fg": 2591}, {"id": "vp_wheel_armor_steerable", "fg": 2591}, {"id": "vp_wheel_bicycle", "fg": 2562}, {"id": "vp_wheel_bicycle_steerable", "fg": 2562}, {"id": "vp_wheel_caster", "fg": 2467}, {"id": "vp_wheel_motorbike", "fg": 2576}, {"id": "vp_wheel_motorbike_steerable", "fg": 2576}, {"id": "vp_wheel_small", "fg": 2421}, {"id": "vp_wheel_small_steerable", "fg": 2421}, {"id": "vp_wheel_steerable", "fg": 2591}, {"id": "vp_wheel_unicycle", "fg": 2562}, {"id": "vp_wheel_wheelchair", "fg": 2562}, {"id": "vp_wheel_wide", "fg": 2451}, {"id": "vp_wheel_wide_steerable", "fg": 2451}, {"id": "alloy_plate", "fg": 2460, "bg": 2498}, {"id": "foot_crank", "fg": 2504, "bg": 2498}, {"id": "frame", "fg": 2533, "bg": 2498}, {"id": "glass_sheet", "fg": 2509, "bg": 2498}, {"id": "hard_plate", "fg": 2512, "bg": 2498}, {"id": "kitchen_unit", "fg": 2431, "bg": 2498}, {"id": "motor", "fg": 2468, "bg": 2498}, {"id": "motor_large", "fg": 2541, "bg": 2498}, {"id": "muffler", "fg": 2554, "bg": 2498}, {"id": "plasma_engine", "fg": 2482, "bg": 2498}, {"id": "saddle", "fg": 2476, "bg": 2498}, {"id": "seat", "fg": 2455, "bg": 2498}, {"id": "solar_panel", "fg": 2441, "bg": 2498}, {"id": "spiked_plate", "fg": 2547, "bg": 2498}, {"id": "steel_plate", "fg": 2430, "bg": 2498}, {"id": "storage_battery", "fg": 2590, "bg": 2498}, {"id": "vehicle_controls", "fg": 2505, "bg": 2498}, {"id": "weldrig", "fg": 2574, "bg": 2498}, {"id": "forge", "fg": 2450, "bg": 2498}, {"id": "1cyl_combustion", "fg": 2563, "bg": 2498}, {"id": "i4_combustion", "fg": 2535, "bg": 2498}, {"id": "v2_combustion", "fg": 2423, "bg": 2498}, {"id": "v6_combustion", "fg": 2550, "bg": 2498}, {"id": "v8_combustion", "fg": 2553, "bg": 2498}, {"id": "vp_board_horizontal", "fg": 2492}, {"id": "vp_board_ne", "fg": 2461}, {"id": "vp_board_nw", "fg": 2579}, {"id": "vp_board_se", "fg": 2440}, {"id": "vp_board_sw", "fg": 2561}, {"id": "vp_board_vertical", "fg": 2549}, {"id": "vp_hdhalfboard_horizontal", "fg": 2417}, {"id": "vp_hdhalfboard_horizontal_2", "fg": 2449}, {"id": "vp_hdhalfboard_ne", "fg": 2587}, {"id": "vp_hdhalfboard_nw", "fg": 2570}, {"id": "vp_hdhalfboard_se", "fg": 2495}, {"id": "vp_hdhalfboard_sw", "fg": 2517}, {"id": "vp_hdhalfboard_vertical", "fg": 2543}, {"id": "vp_hdhalfboard_vertical_2", "fg": 2559}, {"id": "vp_hdstowboard_horizontal", "fg": 2463}, {"id": "vp_hdstowboard_ne", "fg": 2525}, {"id": "vp_hdstowboard_nw", "fg": 2537}, {"id": "vp_hdstowboard_se", "fg": 2427}, {"id": "vp_hdstowboard_sw", "fg": 2575}, {"id": "vp_hdstowboard_vertical", "fg": 2446}, {"id": "vp_stowboard_horizontal", "fg": 2582}, {"id": "vp_stowboard_ne", "fg": 2444}, {"id": "vp_stowboard_nw", "fg": 2513}, {"id": "vp_stowboard_se", "fg": 2560}, {"id": "vp_stowboard_sw", "fg": 2432}, {"id": "vp_stowboard_vertical", "fg": 2477}, {"id": "vp_wing_mirror", "fg": 2445}, {"id": "[vp_xlframe_cover],[vp_fxlframe_cover]", "fg": 2485}, {"id": "[vp_xlframe_cross],[vp_fxlframe_cross]", "fg": 2483}, {"id": "[vp_xlframe_horizontal_2],[vp_fxlframe_horizontal_2]", "fg": 2558}, {"id": "[vp_xlframe_horizontal],[vp_fxlframe_horizontal]", "fg": 2536}, {"id": "[vp_xlframe_ne],[vp_fxlframe_ne]", "fg": 2459}, {"id": "[vp_xlframe_nw],[vp_fxlframe_nw]", "fg": 2447}, {"id": "[vp_xlframe_se],[vp_fxlframe_se]", "fg": 2523}, {"id": "[vp_xlframe_sw],[vp_fxlframe_sw]", "fg": 2580}, {"id": "[vp_xlframe_vertical_2],[vp_fxlframe_vertical_2]", "fg": 2466}, {"id": "[vp_xlframe_vertical],[vp_fxlframe_vertical]", "fg": 2555}, {"id": "[vp_xlhalfboard_horizontal_2],[vp_fxlhalfboard_horizontal_2]", "fg": 2494}, {"id": "[vp_xlhalfboard_horizontal],[vp_fxlhalfboard_horizontal]", "fg": 2567}, {"id": "[vp_xlhalfboard_ne],[vp_fxlhalfboard_ne]", "fg": 2540}, {"id": "[vp_xlhalfboard_nw],[vp_fxlhalfboard_nw]", "fg": 2435}, {"id": "[vp_xlhalfboard_se],[vp_fxlhalfboard_se]", "fg": 2437}, {"id": "[vp_xlhalfboard_sw],[vp_fxlhalfboard_sw", "fg": 2474}, {"id": "[vp_xlhalfboard_vertical_2],[vp_fxlhalfboard_vertical_2]", "fg": 2487}, {"id": "[vp_xlhalfboard_vertical],[vp_fxlhalfboard_vertical]", "fg": 2443}, {"id": "wheel", "fg": 2591, "bg": 2498}, {"id": "wheel_bicycle", "fg": 2562, "bg": 2498}, {"id": "wheel_motorbike", "fg": 2576, "bg": 2498}, {"id": "wheel_small", "fg": 2421, "bg": 2498}, {"id": "wheel_wide", "fg": 2451, "bg": 2498}], "//": "range 2417 to 2608"}, {"file": "opengameartgiant.png", "tiles": [{"id": "t_tree_chestnut", "fg": 2610, "bg": 2302}, {"id": "t_tree_chestnut_season_summer", "fg": 2611, "bg": 2303}, {"id": "t_tree_chestnut_season_autumn", "fg": 2611, "bg": 2301}, {"id": "t_tree_chestnut_season_winter", "fg": 2611, "bg": 2304}, {"id": "t_tree_pine", "fg": 2614, "bg": 2302}, {"id": "t_tree_pine_season_summer", "fg": 2614, "bg": 2303}, {"id": "t_tree_pine_season_autumn", "fg": 2614, "bg": 2301}, {"id": "t_tree_pine_season_winter", "fg": 2614, "bg": 2304}, {"id": "t_tree_deadpine", "fg": 2612, "bg": 2302}, {"id": "t_tree_deadpine_season_summer", "fg": 2612, "bg": 2303}, {"id": "t_tree_deadpine_season_autumn", "fg": 2612, "bg": 2301}, {"id": "t_tree_deadpine_season_winter", "fg": 2612, "bg": 2304}, {"id": "t_tree_hickory", "fg": 2609, "bg": 2302}, {"id": "t_tree_hickory_season_summer", "fg": 2609, "bg": 2303}, {"id": "t_tree_hickory_season_autumn", "fg": 2609, "bg": 2301}, {"id": "t_tree_hickory_season_winter", "fg": 2613, "bg": 2304}, {"id": "t_tree_hickory_dead", "fg": 2613, "bg": 2302}, {"id": "t_tree_hickory_dead_season_summer", "fg": 2613, "bg": 2303}, {"id": "t_tree_hickory_dead_season_autumn", "fg": 2613, "bg": 2301}, {"id": "t_tree_hickory_dead_season_winter", "fg": 2613, "bg": 2304}], "//": "range 2609 to 2624", "sprite_width": 96, "sprite_height": 96, "sprite_offset_x": -32, "sprite_offset_y": -64}, {"file": "fallback.png", "tiles": [], "ascii": [{"offset": 0, "bold": false, "color": "BLACK"}, {"offset": 256, "bold": true, "color": "WHITE"}, {"offset": 512, "bold": false, "color": "WHITE"}, {"offset": 768, "bold": true, "color": "BLACK"}, {"offset": 1024, "bold": false, "color": "RED"}, {"offset": 1280, "bold": false, "color": "GREEN"}, {"offset": 1536, "bold": false, "color": "BLUE"}, {"offset": 1792, "bold": false, "color": "CYAN"}, {"offset": 2048, "bold": false, "color": "MAGENTA"}, {"offset": 2304, "bold": false, "color": "YELLOW"}, {"offset": 2560, "bold": true, "color": "RED"}, {"offset": 2816, "bold": true, "color": "GREEN"}, {"offset": 3072, "bold": true, "color": "BLUE"}, {"offset": 3328, "bold": true, "color": "CYAN"}, {"offset": 3584, "bold": true, "color": "MAGENTA"}, {"offset": 3840, "bold": true, "color": "YELLOW"}]}]} \ No newline at end of file +{"tile_info": [{"width": 32, "height": 32}], "tiles-new": [{"file": "normal.png", "tiles": [{"id": "f_indoor_plant", "fg": 3}, {"id": "f_indoor_plant_y", "fg": 1}, {"id": ["f_indoor_plant_y_season_autumn", "f_indoor_plant_y_season_winter"], "fg": 2}, {"id": "f_recycle_bin", "fg": 4}, {"id": "f_armchair", "fg": 5}, {"id": "f_wreckage", "fg": 6}, {"id": "f_gunsafe_ml", "fg": 8}, {"id": "f_gunsafe_mj", "fg": 7}, {"id": "f_gun_safe_el", "fg": 9}, {"id": "f_mutpoppy", "fg": 10}, {"id": "f_planter_mature", "multitile": true, "fg": 13, "additional_tiles": [{"id": "center", "fg": 14}, {"id": "corner", "fg": [23, 26, 22, 16]}, {"id": "t_connection", "fg": [19, 18, 25, 15]}, {"id": "edge", "fg": [20, 21]}, {"id": "end_piece", "fg": [17, 12, 11, 24]}, {"id": "unconnected", "fg": 13}]}, {"id": "f_boulder_small", "fg": 27}, {"id": "f_dandelion", "fg": 28}, {"id": "f_planter", "multitile": true, "fg": 37, "additional_tiles": [{"id": "center", "fg": 32}, {"id": "corner", "fg": [42, 36, 39, 35]}, {"id": "t_connection", "fg": [29, 40, 44, 43]}, {"id": "edge", "fg": [41, 38]}, {"id": "end_piece", "fg": [33, 34, 30, 31]}, {"id": "unconnected", "fg": 37}]}, {"id": "f_bathtub", "multitile": true, "fg": 54, "additional_tiles": [{"id": "center", "fg": 55}, {"id": "corner", "fg": [53, 50, 48, 56]}, {"id": "t_connection", "fg": [60, 52, 51, 47]}, {"id": "edge", "fg": [59, 57]}, {"id": "end_piece", "fg": [45, 49, 46, 58]}, {"id": "unconnected", "fg": 54}]}, {"id": "f_sofa", "multitile": true, "fg": 65, "additional_tiles": [{"id": "center", "fg": 66}, {"id": "corner", "fg": [74, 69, 76, 62]}, {"id": "t_connection", "fg": [70, 68, 61, 63]}, {"id": "edge", "fg": [75, 73]}, {"id": "end_piece", "fg": [64, 67, 72, 71]}, {"id": "unconnected", "fg": 65}]}, {"id": "f_alien_anemone", "fg": 78}, {"id": "f_alien_table", "fg": 77}, {"id": "f_filing_cabinet", "fg": 79}, {"id": "f_toilet", "fg": 80}, {"id": "f_ash", "fg": 81}, {"id": "f_desk", "multitile": true, "fg": 82, "additional_tiles": [{"id": "center", "fg": 91}, {"id": "corner", "fg": [87, 84, 92, 86]}, {"id": "t_connection", "fg": [90, 89, 85, 95]}, {"id": "edge", "fg": [96, 83]}, {"id": "end_piece", "fg": [97, 94, 93, 88]}, {"id": "unconnected", "fg": 82}]}, {"id": "f_trashcan", "fg": 98}, {"id": "f_entertainment_center", "fg": 99}, {"id": "f_grave_stone", "fg": [{"weight": 1, "sprite": 100}, {"weight": 1, "sprite": 101}]}, {"id": "f_rubble", "fg": 102}, {"id": "f_rubble_rock", "fg": 103}, {"id": "f_sign", "fg": 104}, {"id": "f_flower_spurge", "fg": 105}, {"id": "f_planter_harvest", "multitile": true, "fg": 117, "additional_tiles": [{"id": "center", "fg": 110}, {"id": "corner", "fg": [115, 120, 111, 118]}, {"id": "t_connection", "fg": [112, 113, 121, 107]}, {"id": "edge", "fg": [106, 108]}, {"id": "end_piece", "fg": [116, 114, 109, 119]}, {"id": "unconnected", "fg": 117}]}, {"id": "f_cardboard_box", "fg": 122}, {"id": "f_datura", "fg": 123}, {"id": "f_planter_seedling", "multitile": true, "fg": 131, "additional_tiles": [{"id": "center", "fg": 132}, {"id": "corner", "fg": [134, 125, 136, 128]}, {"id": "t_connection", "fg": [124, 133, 130, 126]}, {"id": "edge", "fg": [139, 137]}, {"id": "end_piece", "fg": [127, 135, 129, 138]}, {"id": "unconnected", "fg": 131}]}, {"id": "f_table", "multitile": true, "fg": 140, "additional_tiles": [{"id": "center", "fg": 154}, {"id": "corner", "fg": [148, 151, 142, 152]}, {"id": "t_connection", "fg": [141, 147, 144, 153]}, {"id": "edge", "fg": [143, 150]}, {"id": "end_piece", "fg": [146, 149, 155, 145]}, {"id": "unconnected", "fg": 140}]}, {"id": "f_boulder_large", "fg": 156}, {"id": "f_flower_tulip", "fg": [{"weight": 1, "sprite": 158}, {"weight": 2, "sprite": 157}]}, {"id": "f_cupboard", "multitile": true, "fg": 174, "additional_tiles": [{"id": "center", "fg": 173}, {"id": "corner", "fg": [167, 164, 166, 160]}, {"id": "t_connection", "fg": [163, 172, 168, 169]}, {"id": "edge", "fg": [171, 162]}, {"id": "end_piece", "fg": [159, 161, 165, 170]}, {"id": "unconnected", "fg": 174}]}, {"id": "f_metal_crate_r", "fg": 175}, {"id": "f_metal_crate_c", "fg": 177}, {"id": "f_metal_crate_o", "fg": 176}, {"id": "f_bluebell", "fg": [{"weight": 1, "sprite": 178}, {"weight": 2, "sprite": 179}]}, {"id": "f_bed", "multitile": true, "fg": 185, "additional_tiles": [{"id": "center", "fg": 181}, {"id": "corner", "fg": [191, 190, 184, 189]}, {"id": "t_connection", "fg": [193, 188, 194, 192]}, {"id": "edge", "fg": [195, 183]}, {"id": "end_piece", "fg": [180, 186, 182, 187]}, {"id": "unconnected", "fg": 185}]}, {"id": "f_firering", "fg": 196}, {"id": "f_bench", "multitile": true, "fg": 210, "additional_tiles": [{"id": "center", "fg": 203}, {"id": "corner", "fg": [206, 208, 200, 199]}, {"id": "t_connection", "fg": [197, 204, 207, 209]}, {"id": "edge", "fg": [201, 205]}, {"id": "end_piece", "fg": [211, 202, 212, 198]}, {"id": "unconnected", "fg": 210}]}, {"id": "f_mailbox", "fg": 213}, {"id": "f_grave_stone_old", "fg": [{"weight": 1, "sprite": 215}, {"weight": 1, "sprite": 214}]}, {"id": "f_rack_wood", "fg": 216}, {"id": "f_boulder_medium", "fg": 217}, {"id": "f_chamomile", "fg": 218}, {"id": "f_hay", "fg": 219}, {"id": "f_counter", "multitile": true, "fg": 226, "additional_tiles": [{"id": "center", "fg": 222}, {"id": "corner", "fg": [231, 223, 229, 227]}, {"id": "t_connection", "fg": [228, 221, 235, 230]}, {"id": "edge", "fg": [225, 224]}, {"id": "end_piece", "fg": [232, 234, 233, 220]}, {"id": "unconnected", "fg": 226}]}, {"id": "f_air_conditioner", "fg": 236}, {"id": "f_chair", "fg": 237}, {"id": "f_stool", "fg": 238}, {"id": "f_dahlia", "fg": [{"weight": 1, "sprite": 239}, {"weight": 2, "sprite": 240}]}, {"id": ["f_displaycase"], "fg": 241}, {"id": "f_rack", "fg": 242}, {"id": "vp_door_trunk", "fg": 243, "rotates": true, "multitile": true, "additional_tiles": [{"id": "open", "fg": 244}]}, {"id": ["vp_headlight", "vp_headlight_reinforced"], "fg": 245}, {"id": "vp_door", "fg": 246, "rotates": true, "multitile": true, "additional_tiles": [{"id": "open", "fg": 247}]}, {"id": "vp_halfboard_ne", "fg": 251, "rotates": true}, {"id": "vp_halfboard_nw", "fg": 256, "rotates": true}, {"id": "vp_halfboard_se", "fg": 248, "rotates": true}, {"id": "vp_halfboard_sw", "fg": 253, "rotates": true}, {"id": "vp_halfboard_vertical", "fg": 249, "rotates": true}, {"id": "vp_halfboard_horizontal_2", "fg": 252, "rotates": true}, {"id": "vp_halfboard_horizontal", "fg": 254, "rotates": true}, {"id": "vp_halfboard_vertical_2", "fg": 255, "rotates": true}, {"id": "vp_halfboard_cover", "fg": 250, "rotates": true}, {"id": "vp_windshield", "fg": 260, "rotates": true, "multitile": true, "additional_tiles": [{"id": "center", "fg": [257, 261, 258, 259]}, {"id": "edge", "fg": 260}, {"id": "unconnected", "fg": 260}, {"id": "end_piece", "fg": [257, 261, 258, 259]}, {"id": "t_connection", "fg": [257, 261, 258, 259]}, {"id": "corner", "fg": [257, 261, 258, 259]}]}, {"id": ["vp_folding_seat", "vp_reclining_seat", "vp_seat"], "fg": 263}, {"id": "vp_saddle", "fg": 262}, {"id": "powder_candy", "fg": 264}, {"id": "joint", "fg": 266}, {"id": "joint_lit", "fg": 267}, {"id": "joint_roach", "fg": 265}, {"id": "bag_canvas", "fg": 268}, {"id": "wood_panel", "fg": 269}, {"id": "jar_glass", "fg": 270}, {"id": "shot_hull", "fg": 271}, {"id": "wrench", "fg": 272}, {"id": "needle_bone", "fg": 275}, {"id": "needle_curved", "fg": 273}, {"id": "needle_wood", "fg": 274}, {"id": "pinecone", "fg": 276}, {"id": "ash", "fg": 277}, {"id": "lighter", "fg": [{"weight": 1, "sprite": 278}, {"weight": 1, "sprite": 280}, {"weight": 1, "sprite": 279}]}, {"id": "chips", "fg": 281}, {"id": "rifle_flintlock", "fg": 282}, {"id": "balclava", "fg": 306}, {"id": "beret", "fg": 295}, {"id": "boots", "fg": 322}, {"id": "boxer_briefs", "fg": 301}, {"id": "boxer_shorts", "fg": 287}, {"id": "boy_shorts", "fg": 292}, {"id": "bra", "fg": 314}, {"id": "briefs", "fg": 298}, {"id": "corset", "fg": 311}, {"id": "cowboy_hat", "fg": 323}, {"id": "dress_shoes", "fg": 291}, {"id": "hat_ball", "fg": 305}, {"id": "hat_cotton", "fg": 304}, {"id": "hat_fur", "fg": 308}, {"id": "hat_knit", "fg": 286}, {"id": "hat_noise_cancelling", "fg": 331}, {"id": "helmet_army", "fg": 289}, {"id": "helmet_barbute", "fg": 320}, {"id": "helmet_chitin", "fg": 283}, {"id": "helmet_kabuto", "fg": 319}, {"id": "hoodie", "fg": 318}, {"id": "jeans", "fg": 313}, {"id": "longshirt", "fg": 300}, {"id": "maid_dress", "fg": 285}, {"id": "maid_hat", "fg": 299}, {"id": "mask_dust", "fg": 310}, {"id": "panties", "fg": 296}, {"id": "pants", "fg": 293}, {"id": "pants_cargo", "fg": 307}, {"id": "polo_shirt", "fg": 315}, {"id": "ragpouch", "fg": 329}, {"id": "sneakers", "fg": 288}, {"id": "socks", "fg": 303}, {"id": "stockings", "fg": 294}, {"id": "sweater", "fg": 316}, {"id": "sweatshirt", "fg": 290}, {"id": "tank_top", "fg": 330}, {"id": "tshirt", "fg": 312}, {"id": "turban", "fg": 324}, {"id": "undershirt", "fg": 327}, {"id": "coat_lab", "fg": 326}, {"id": "coat_rain", "fg": 297}, {"id": "sports_bra", "fg": 302}, {"id": "skirt", "fg": 284}, {"id": "jacket_light", "fg": 317}, {"id": "jacket_army", "fg": 328}, {"id": "hat_hard", "fg": 325}, {"id": "striped_pants", "fg": 309}, {"id": "striped_shirt", "fg": 321}, {"id": "", "fg": []}, {"id": "bat_metal", "fg": 332}, {"id": "hacksaw", "fg": 333}, {"id": "crucible_clay", "fg": 334}, {"id": "many_years_old_newspaper", "fg": 339}, {"id": "months_old_newspaper", "fg": 335}, {"id": "newest_newspaper", "fg": 338}, {"id": "one_year_old_newspaper", "fg": 337}, {"id": "weeks_old_newspaper", "fg": 336}, {"id": "pointy_stick", "fg": 348}, {"id": "spear_wood", "fg": 347}, {"id": "spear_spike", "fg": 342}, {"id": "spear_knife", "fg": 345}, {"id": "spear_knife_superior", "fg": 346}, {"id": "spear_pipe", "fg": 340}, {"id": "spear_rebar", "fg": 344}, {"id": "spear_steel", "fg": 341}, {"id": "spear_copper", "fg": 343}, {"id": "hickory_root", "fg": 349}, {"id": "flashlight", "fg": 350}, {"id": "heavy_flashlight", "fg": 351}, {"id": "can_drink", "fg": 352}, {"id": "50_casing", "fg": 353}, {"id": "apple", "fg": 355}, {"id": "banana", "fg": 356}, {"id": "broccoli", "fg": 364}, {"id": "corn", "fg": 367}, {"id": "cucumber", "fg": 360}, {"id": "egg_bird", "fg": 366}, {"id": "grapes", "fg": 354}, {"id": "lemon", "fg": 357}, {"id": "onion", "fg": 365}, {"id": "orange", "fg": 359}, {"id": "pear", "fg": 361}, {"id": "potato", "fg": 358}, {"id": "pumpkin", "fg": 363}, {"id": "tomato", "fg": 362}, {"id": "sewing_kit", "fg": 368}, {"id": "jug_plastic", "fg": 369}, {"id": "pot", "fg": 370}, {"id": "backpack", "fg": 371}, {"id": "cattlefodder", "fg": 372}, {"id": "cup_plastic", "fg": 373}, {"id": "knife_meat_cleaver", "fg": 374}, {"id": "fire_ax", "fg": 375}, {"id": "ax", "fg": 377}, {"id": "hatchet", "fg": 376}, {"id": ["glock_17", "glock_19", "glock_18c", "glock_22", "glock_31"], "fg": 378}, {"id": "straw_pile", "fg": 379}, {"id": "thread", "fg": 380}, {"id": "wheat", "fg": 381}, {"id": "pan", "fg": 382}, {"id": "bottle_glass", "fg": 383}, {"id": "nail", "fg": 384}, {"id": "juniper", "fg": 385}, {"id": "pool_ball", "fg": 386}, {"id": "crowbar", "fg": 387}, {"id": "rag", "fg": 388}, {"id": "id_industrial", "fg": 389}, {"id": "corpse_generic_human", "fg": 390}, {"id": "chainsaw_off", "fg": 391}, {"id": "teapot", "fg": 392}, {"id": "mp5mag", "fg": 393}, {"id": "meat", "fg": 394}, {"id": "cig_butt", "fg": 395}, {"id": "usb_drive", "fg": 396}, {"id": "1st_aid", "fg": 397}, {"id": "welder_crude", "fg": 398}, {"id": "nailbat", "fg": 399}, {"id": "remington_870", "fg": 400}, {"id": "sheet_metal_small", "fg": 401}, {"id": "screwdriver", "fg": 402}, {"id": "primitive_hammer", "fg": 403}, {"id": "steel_lump", "fg": 404}, {"id": "string_6", "fg": 405}, {"id": "saw", "fg": 406}, {"id": "aspirin", "fg": 422}, {"id": "bandages", "fg": 408}, {"id": "syringe", "fg": 414}, {"id": "antibiotics", "fg": 421}, {"id": "weak_antibiotic", "fg": 407}, {"id": "strong_antibiotic", "fg": 415}, {"id": "vitamins", "fg": 412}, {"id": "gummy_vitamins", "fg": 413}, {"id": "calcium_tablet", "fg": 418}, {"id": "oxycodone", "fg": 416}, {"id": "tramadol", "fg": 417}, {"id": "codeine", "fg": 411}, {"id": "prussian_blue", "fg": 410}, {"id": "iodine", "fg": 419}, {"id": "antiparasitic", "fg": 420}, {"id": "antifungal", "fg": 409}, {"id": "can_drink_unsealed", "fg": 423}, {"id": "9mm_casing", "fg": 424}, {"id": "wrapper", "fg": 425}, {"id": "rebar", "fg": 426}, {"id": "glass_shard", "fg": 427}, {"id": "pot_copper", "fg": 428}, {"id": "40mm_casing", "fg": 429}, {"id": "nailboard", "fg": 430}, {"id": "scissors", "fg": 431}, {"id": "ak74", "fg": 432}, {"id": "coffeemaker", "fg": 433}, {"id": "blanket", "fg": 435}, {"id": "down_blanket", "fg": 434}, {"id": "longbow", "fg": 436}, {"id": "pneumatic_shotgun", "fg": 437}, {"id": "thermos", "fg": 438}, {"id": "box_cigarette", "fg": 439}, {"id": "m79", "fg": 440}, {"id": "stick", "fg": 441}, {"id": "steel_chunk", "fg": 442}, {"id": "television", "fg": 443}, {"id": "shovel", "fg": 444}, {"id": "id_science", "fg": 445}, {"id": "cable", "fg": 446}, {"id": "pipe", "fg": 447}, {"id": "bottle_plastic", "fg": 448}, {"id": "fp_loyalty_card", "fg": 449}, {"id": "id_military", "fg": 450}, {"id": "water", "fg": 452}, {"id": "water_clean", "fg": 453}, {"id": "blood", "fg": 451}, {"id": "filter_air", "fg": 454}, {"id": "welder", "fg": 455}, {"id": "broom", "fg": 456}, {"id": "rolling_pin", "fg": 457}, {"id": "makeshift_crowbar", "fg": 458}, {"id": "bat", "fg": 459}, {"id": "wood_sheet", "fg": 460}, {"id": "jar_3l_glass_sealed", "fg": 461}, {"id": "plastic_sheet", "fg": 462}, {"id": "box_large", "fg": 463}, {"id": "knife_vegetable_cleaver", "fg": 464}, {"id": "arming_sword", "fg": 465}, {"id": "crossbow", "fg": 466}, {"id": "pot_canning", "fg": 467}, {"id": "sheet_metal", "fg": 468}, {"id": "brick", "fg": 469}, {"id": "wire", "fg": 470}, {"id": "2x4", "fg": 471}, {"id": ["seed_hops", "seed_blackberries", "seed_blueberries", "coffee_pod", "seed_wheat", "roasted_coffee_bean", "seed_chamomile", "coffee_bean", "seed_celery", "fried_seeds", "seed_buckwheat", "seed_oats", "seed_mushroom_morel", "datura_seed", "seed_mushroom", "seed_rhubarb", "seed_wild_herbs", "seed_raw_dandelion", "seed_veggy_wild", "seed_chili_pepper", "seed_dogbane", "seed_mugwort", "seed_bee_balm", "seed_flower", "seed_sunflower", "seed_thyme", "seed_canola", "seed_pumpkin", "seed_beans", "seed_lentils", "soybean_seed", "marloss_seed", "fungal_seeds", "seed_weed", "seed_potato_raw", "seed_cucumber", "seed_corn", "seed_carrot", "garlic_clove", "seed_cactus", "seed_wildcarrot", "seed_cattail", "seed_chicory", "seed_salsify_raw", "seed_dahlia", "seed_garlic", "seed_broccoli", "seed_onion", "seed_zucchini", "seed_cotton_boll", "seed_tomato", "seed_rose", "seed_lettuce", "seed_cabbage", "seed_sugar_beet", "seed_tobacco", "seed_barley", "seed_grapes", "seed_strawberries", "seed_raspberries", "seed_cranberries", "seed_huckleberries", "seed_mulberries", "seed_elderberries"], "fg": 472}, {"id": "acorns", "fg": 473}, {"id": "slingshot", "fg": 474}, {"id": "bottle_plastic_small", "fg": 475}, {"id": "sharp_rock", "fg": 476}, {"id": "bowl_plastic", "fg": 477}, {"id": "contacts", "fg": 478}, {"id": "matches", "fg": 479}, {"id": "box_small", "fg": 480}, {"id": "plastic_shopping_bag", "fg": 481}, {"id": "pillow", "fg": 483}, {"id": "down_pillow", "fg": 482}, {"id": "mop", "fg": 484}, {"id": "board_trap", "fg": 485}, {"id": "can_food", "fg": 486}, {"id": "scrap", "fg": 487}, {"id": "string_36", "fg": 488}, {"id": "file", "fg": 489}, {"id": "box_medium", "fg": 490}, {"id": "paperback_novel", "fg": 512}, {"id": "novel_adventure", "fg": 512}, {"id": "novel_buddy", "fg": 505}, {"id": "novel_coa", "fg": 494}, {"id": "novel_coa2", "fg": 495}, {"id": "novel_crime", "fg": 498}, {"id": "novel_crime2", "fg": 506}, {"id": "novel_drama", "fg": 502}, {"id": "novel_erotic", "fg": 510}, {"id": "novel_experimental", "fg": 501}, {"id": "novel_fantasy", "fg": 507}, {"id": "novel_horror", "fg": 508}, {"id": "novel_mystery", "fg": 511}, {"id": "novel_pulp", "fg": 499}, {"id": "novel_road", "fg": 496}, {"id": "novel_romance", "fg": 493}, {"id": "novel_samurai", "fg": 491}, {"id": "novel_satire", "fg": 503}, {"id": "novel_scifi", "fg": 515}, {"id": "novel_sports", "fg": 514}, {"id": "novel_spy", "fg": 516}, {"id": "novel_swash", "fg": 504}, {"id": "novel_thriller", "fg": 509}, {"id": "novel_tragedy", "fg": 500}, {"id": "novel_war", "fg": 513}, {"id": "novel_war2", "fg": 497}, {"id": "novel_western", "fg": 492}, {"id": "marble", "fg": 517}, {"id": "withered", "fg": 518}, {"id": "bolt_cf", "fg": 519}, {"id": "bolt_explosive", "fg": 527}, {"id": "bolt_metal", "fg": 522}, {"id": ["bolt_steel", "bolt_steel_bodkin", "bolt_steel_target"], "fg": 526}, {"id": ["bolt_wood", "bolt_crude", "bolt_simple_wood", "bolt_simple_small_game", "bolt_makeshift", "bolt_wood_bodkin", "bolt_wood_small_game"], "fg": 536}, {"id": "arrow_cf", "fg": 525}, {"id": "arrow_exploding", "fg": 520}, {"id": "arrow_field_point_fletched", "fg": 529}, {"id": "arrow_fire_hardened_fletched", "fg": 535}, {"id": "arrow_flammable", "fg": 533}, {"id": "arrow_flamming", "fg": 524}, {"id": "arrow_heavy_fire_hardened_fletched", "fg": 534}, {"id": "arrow_metal", "fg": 528}, {"id": "arrow_metal_sharpened_fletched", "fg": 521}, {"id": "arrow_plastic", "fg": 530}, {"id": "arrow_small_game_fletched", "fg": 531}, {"id": "arrow_wood", "fg": 532}, {"id": "arrow_wood_heavy", "fg": 523}, {"id": "rock", "fg": 537}, {"id": "knife_butcher", "fg": 538}, {"id": "stick_long", "fg": 539}, {"id": "log", "fg": 540}, {"id": "jar_glass_sealed", "fg": 541}, {"id": "jar_3l_glass", "fg": 542}, {"id": "knife_butter", "fg": 543}, {"id": "223_casing", "fg": 544}, {"id": "bwirebat", "fg": 545}, {"id": "bag_plastic", "fg": 546}, {"id": ["rifle_9mm", "rifle_3006", "rifle_45", "rifle_22", "rifle_40", "rifle_44", "rifle_38", "rifle_223"], "fg": 547}, {"id": "pipe_shotgun", "fg": 548}, {"id": "spoon", "fg": 549}, {"id": "hammer", "fg": 550}, {"id": "hickory_nut", "fg": 551}, {"id": "glass_plate", "fg": 552}, {"id": "cash_card", "fg": 553}, {"id": "fork", "fg": 554}, {"id": "splinter", "fg": 555}, {"id": "ar15", "fg": 556}, {"id": "pine_bough", "fg": 557}, {"id": "bag_zipper", "fg": 558}, {"id": "tailors_kit", "fg": 559}, {"id": "sponge", "fg": 560}, {"id": "t_strconc_floor", "multitile": true, "fg": 576, "additional_tiles": [{"id": "center", "fg": 573}, {"id": "corner", "fg": [561, 568, 570, 566]}, {"id": "t_connection", "fg": [562, 567, 571, 564]}, {"id": "edge", "fg": [572, 574]}, {"id": "end_piece", "fg": [575, 563, 569, 565]}, {"id": "unconnected", "fg": 576}]}, {"id": "t_tatami", "fg": [{"weight": 100, "sprite": 578}, {"weight": 100, "sprite": 577}]}, {"id": "t_floor_waxed_y", "multitile": true, "fg": 584, "additional_tiles": [{"id": "center", "fg": 590}, {"id": "corner", "fg": [587, 579, 583, 593]}, {"id": "t_connection", "fg": [585, 581, 592, 580]}, {"id": "edge", "fg": [588, 591]}, {"id": "end_piece", "fg": [589, 582, 586, 594]}, {"id": "unconnected", "fg": 584}]}, {"id": "t_pit_shallow", "fg": 595, "bg": [{"weight": 100, "sprite": 1312}, {"weight": 100, "sprite": 1315}]}, {"id": "t_pit_shallow_season_summer", "fg": 595, "bg": [{"weight": 100, "sprite": 1326}, {"weight": 100, "sprite": 1306}]}, {"id": "t_pit_shallow_season_autumn", "fg": 595, "bg": [{"weight": 100, "sprite": 1313}, {"weight": 100, "sprite": 1305}]}, {"id": "t_rock", "fg": 596}, {"id": "t_railroad_rubble", "fg": 597}, {"id": "t_gravel", "fg": 597}, {"id": "t_railroad_rubble_season_winter", "fg": 682}, {"id": "t_sand", "fg": 598}, {"id": "t_floor_resin", "multitile": true, "fg": 601, "additional_tiles": [{"id": "center", "fg": 600}, {"id": "corner", "fg": [606, 613, 607, 603]}, {"id": "t_connection", "fg": [605, 599, 609, 608]}, {"id": "edge", "fg": [614, 602]}, {"id": "end_piece", "fg": [611, 612, 610, 604]}, {"id": "unconnected", "fg": 601}]}, {"id": "t_metal_floor", "multitile": true, "fg": 622, "additional_tiles": [{"id": "center", "fg": 616}, {"id": "corner", "fg": [624, 620, 618, 619]}, {"id": "t_connection", "fg": [630, 625, 627, 629]}, {"id": "edge", "fg": [628, 626]}, {"id": "end_piece", "fg": [615, 621, 617, 623]}, {"id": "unconnected", "fg": 622}]}, {"id": ["t_door_metal_locked", "t_door_metal_pickable"], "fg": 631}, {"id": "t_wall_b", "multitile": true, "fg": 635, "additional_tiles": [{"id": "center", "fg": 633}, {"id": "corner", "fg": [646, 639, 645, 637]}, {"id": "t_connection", "fg": [643, 638, 647, 641]}, {"id": "edge", "fg": [632, 634]}, {"id": "end_piece", "fg": [640, 636, 644, 642]}, {"id": "unconnected", "fg": 635}]}, {"id": "t_floor", "multitile": true, "fg": 695, "additional_tiles": [{"id": "center", "fg": 692}, {"id": "corner", "fg": [697, 694, 690, 698]}, {"id": "t_connection", "fg": [696, 685, 691, 699]}, {"id": "edge", "fg": [687, 688]}, {"id": "end_piece", "fg": [693, 689, 700, 686]}, {"id": "unconnected", "fg": 695}]}, {"id": "t_brick_wall", "multitile": true, "fg": 714, "additional_tiles": [{"id": "center", "fg": 716}, {"id": "corner", "fg": [706, 708, 707, 712]}, {"id": "t_connection", "fg": [702, 710, 713, 715]}, {"id": "edge", "fg": [709, 701]}, {"id": "end_piece", "fg": [704, 705, 711, 703]}, {"id": "unconnected", "fg": 714}]}, {"id": "t_wall_wood", "multitile": true, "fg": 724, "additional_tiles": [{"id": "center", "fg": 720}, {"id": "corner", "fg": [717, 726, 730, 728]}, {"id": "t_connection", "fg": [725, 732, 719, 722]}, {"id": "edge", "fg": [718, 729]}, {"id": "end_piece", "fg": [731, 721, 727, 723]}, {"id": "unconnected", "fg": 724}]}, {"id": ["t_linoleum_white", "t_linoleum_white_no_roof"], "multitile": true, "fg": 736, "additional_tiles": [{"id": "center", "fg": 748}, {"id": "corner", "fg": [743, 742, 733, 747]}, {"id": "t_connection", "fg": [735, 745, 741, 744]}, {"id": "edge", "fg": [746, 740]}, {"id": "end_piece", "fg": [734, 737, 739, 738]}, {"id": "unconnected", "fg": 736}]}, {"id": "t_fungus", "multitile": true, "fg": 761, "bg": 1312, "additional_tiles": [{"id": "center", "bg": 1312, "fg": 756}, {"id": "corner", "bg": 1312, "fg": [758, 762, 750, 751]}, {"id": "t_connection", "bg": 1312, "fg": [763, 749, 755, 757]}, {"id": "edge", "bg": 1312, "fg": [759, 753]}, {"id": "end_piece", "bg": 1312, "fg": [760, 752, 764, 754]}, {"bg": 1312, "id": "unconnected", "fg": 761}]}, {"id": "t_fungus_season_summer", "multitile": true, "fg": 761, "bg": 1326, "additional_tiles": [{"id": "center", "bg": 1326, "fg": 756}, {"id": "corner", "bg": 1326, "fg": [758, 762, 750, 751]}, {"id": "t_connection", "bg": 1326, "fg": [763, 749, 755, 757]}, {"id": "edge", "bg": 1326, "fg": [759, 753]}, {"id": "end_piece", "bg": 1326, "fg": [760, 752, 764, 754]}, {"bg": 1326, "id": "unconnected", "fg": 761}]}, {"id": "t_fungus_season_autumn", "multitile": true, "fg": 761, "bg": 1313, "additional_tiles": [{"id": "center", "bg": 1313, "fg": 756}, {"id": "corner", "bg": 1313, "fg": [758, 762, 750, 751]}, {"id": "t_connection", "bg": 1313, "fg": [763, 749, 755, 757]}, {"id": "edge", "bg": 1313, "fg": [759, 753]}, {"id": "end_piece", "bg": 1313, "fg": [760, 752, 764, 754]}, {"bg": 1313, "id": "unconnected", "fg": 761}]}, {"id": "t_fungus_season_winter", "multitile": true, "fg": 761, "bg": 682, "additional_tiles": [{"id": "center", "bg": 682, "fg": 756}, {"id": "corner", "bg": 682, "fg": [758, 762, 750, 751]}, {"id": "t_connection", "bg": 682, "fg": [763, 749, 755, 757]}, {"id": "edge", "bg": 682, "fg": [759, 753]}, {"id": "end_piece", "bg": 682, "fg": [760, 752, 764, 754]}, {"bg": 682, "id": "unconnected", "fg": 761}]}, {"id": "t_carpet_red", "multitile": true, "fg": 775, "additional_tiles": [{"id": "center", "fg": 772}, {"id": "corner", "fg": [774, 770, 773, 776]}, {"id": "t_connection", "fg": [767, 771, 766, 777]}, {"id": "edge", "fg": [765, 780]}, {"id": "end_piece", "fg": [778, 779, 769, 768]}, {"id": "unconnected", "fg": 775}]}, {"id": ["t_water_moving_sh", "t_swater_moving_sh"], "multitile": true, "fg": 785, "bg": 1312, "additional_tiles": [{"id": "center", "bg": 1312, "fg": [{"weight": 1, "sprite": 784}, {"weight": 1, "sprite": 789}, {"weight": 1, "sprite": 783}, {"weight": 1, "sprite": 781}]}, {"id": "corner", "bg": 1312, "fg": [797, 782, 787, 790]}, {"id": "t_connection", "bg": 1312, "fg": [799, 791, 788, 798]}, {"id": "edge", "bg": 1312, "fg": [794, 795]}, {"id": "end_piece", "bg": 1312, "fg": [796, 786, 792, 793]}, {"bg": 1312, "id": "unconnected", "fg": 785}]}, {"id": ["t_water_moving_sh_season_summer", "t_swater_moving_sh_season_summer"], "multitile": true, "fg": 785, "bg": 1326, "additional_tiles": [{"id": "center", "bg": 1326, "fg": [{"weight": 1, "sprite": 784}, {"weight": 1, "sprite": 789}, {"weight": 1, "sprite": 783}, {"weight": 1, "sprite": 781}]}, {"id": "corner", "bg": 1326, "fg": [797, 782, 787, 790]}, {"id": "t_connection", "bg": 1326, "fg": [799, 791, 788, 798]}, {"id": "edge", "bg": 1326, "fg": [794, 795]}, {"id": "end_piece", "bg": 1326, "fg": [796, 786, 792, 793]}, {"bg": 1326, "id": "unconnected", "fg": 785}]}, {"id": ["t_water_moving_sh_season_autumn", "t_swater_moving_sh_season_autumn"], "multitile": true, "fg": 785, "bg": 1313, "additional_tiles": [{"id": "center", "bg": 1313, "fg": [{"weight": 1, "sprite": 784}, {"weight": 1, "sprite": 789}, {"weight": 1, "sprite": 783}, {"weight": 1, "sprite": 781}]}, {"id": "corner", "bg": 1313, "fg": [797, 782, 787, 790]}, {"id": "t_connection", "bg": 1313, "fg": [799, 791, 788, 798]}, {"id": "edge", "bg": 1313, "fg": [794, 795]}, {"id": "end_piece", "bg": 1313, "fg": [796, 786, 792, 793]}, {"bg": 1313, "id": "unconnected", "fg": 785}]}, {"id": ["t_water_moving_sh_season_winter", "t_swater_moving_sh_season_winter"], "multitile": true, "fg": 785, "bg": 1321, "additional_tiles": [{"id": "center", "bg": 1321, "fg": [{"weight": 1, "sprite": 784}, {"weight": 1, "sprite": 789}, {"weight": 1, "sprite": 783}, {"weight": 1, "sprite": 781}]}, {"id": "corner", "bg": 1321, "fg": [797, 782, 787, 790]}, {"id": "t_connection", "bg": 1321, "fg": [799, 791, 788, 798]}, {"id": "edge", "bg": 1321, "fg": [794, 795]}, {"id": "end_piece", "bg": 1321, "fg": [796, 786, 792, 793]}, {"bg": 1321, "id": "unconnected", "fg": 785}]}, {"id": ["t_window", "t_window_alarm"], "fg": 801}, {"id": "t_window_empty", "fg": 805}, {"id": "t_window_domestic", "fg": 804}, {"id": "t_window_open", "fg": 803}, {"id": "t_window_no_curtains", "fg": 802}, {"id": "t_window_no_curtains_open", "fg": 800}, {"id": "t_fence_wire", "multitile": true, "fg": 814, "additional_tiles": [{"id": "center", "fg": 811}, {"id": "corner", "fg": [815, 812, 818, 820]}, {"id": "t_connection", "fg": [817, 810, 813, 806]}, {"id": "edge", "fg": [819, 816]}, {"id": "end_piece", "fg": [808, 809, 807, 821]}, {"id": "unconnected", "fg": 814}]}, {"id": "t_fence_barbed_season_spring", "multitile": true, "fg": 827, "bg": 1312, "additional_tiles": [{"id": "center", "bg": 1312, "fg": 824}, {"id": "corner", "bg": 1312, "fg": [823, 836, 834, 837]}, {"id": "t_connection", "bg": 1312, "fg": [829, 825, 831, 832]}, {"id": "edge", "bg": 1312, "fg": [835, 830]}, {"id": "end_piece", "bg": 1312, "fg": [826, 822, 828, 833]}, {"bg": 1312, "id": "unconnected", "fg": 827}]}, {"id": "t_fence_barbed_season_summer", "multitile": true, "fg": 827, "bg": 1326, "additional_tiles": [{"id": "center", "bg": 1326, "fg": 824}, {"id": "corner", "bg": 1326, "fg": [823, 836, 834, 837]}, {"id": "t_connection", "bg": 1326, "fg": [829, 825, 831, 832]}, {"id": "edge", "bg": 1326, "fg": [835, 830]}, {"id": "end_piece", "bg": 1326, "fg": [826, 822, 828, 833]}, {"bg": 1326, "id": "unconnected", "fg": 827}]}, {"id": "t_fence_barbed_season_autumn", "multitile": true, "fg": 827, "bg": 1313, "additional_tiles": [{"id": "center", "bg": 1313, "fg": 824}, {"id": "corner", "bg": 1313, "fg": [823, 836, 834, 837]}, {"id": "t_connection", "bg": 1313, "fg": [829, 825, 831, 832]}, {"id": "edge", "bg": 1313, "fg": [835, 830]}, {"id": "end_piece", "bg": 1313, "fg": [826, 822, 828, 833]}, {"bg": 1313, "id": "unconnected", "fg": 827}]}, {"id": "t_fence_barbed_season_winter", "multitile": true, "fg": 827, "bg": 682, "additional_tiles": [{"id": "center", "bg": 682, "fg": 824}, {"id": "corner", "bg": 682, "fg": [823, 836, 834, 837]}, {"id": "t_connection", "bg": 682, "fg": [829, 825, 831, 832]}, {"id": "edge", "bg": 682, "fg": [835, 830]}, {"id": "end_piece", "bg": 682, "fg": [826, 822, 828, 833]}, {"bg": 682, "id": "unconnected", "fg": 827}]}, {"id": "t_floor_waxed", "multitile": true, "fg": 843, "additional_tiles": [{"id": "center", "fg": 852}, {"id": "corner", "fg": [845, 840, 841, 846]}, {"id": "t_connection", "fg": [850, 839, 849, 842]}, {"id": "edge", "fg": [848, 838]}, {"id": "end_piece", "fg": [851, 847, 844, 853]}, {"id": "unconnected", "fg": 843}]}, {"id": "t_curtains", "fg": 854}, {"id": "t_door_glass_c", "fg": 855}, {"id": ["t_wall_glass", "t_wall_glass_alarm"], "multitile": true, "fg": 857, "additional_tiles": [{"id": "center", "fg": 864}, {"id": "corner", "fg": [871, 868, 859, 874]}, {"id": "t_connection", "fg": [872, 862, 861, 867]}, {"id": "edge", "fg": [856, 860]}, {"id": "end_piece", "fg": [858, 870, 875, 863]}, {"id": "unconnected", "fg": 857}]}, {"id": ["t_sidewalk", "t_sidewalk_bg_dp"], "multitile": true, "fg": 889, "additional_tiles": [{"id": "center", "fg": 876}, {"id": "corner", "fg": [877, 891, 878, 882]}, {"id": "t_connection", "fg": [880, 879, 888, 886]}, {"id": "edge", "fg": [885, 890]}, {"id": "end_piece", "fg": [884, 881, 883, 887]}, {"id": "unconnected", "fg": 889}]}, {"id": "t_sidewalk_season_winter", "fg": 682}, {"id": "t_carpet_purple", "multitile": true, "fg": 898, "additional_tiles": [{"id": "center", "fg": 896}, {"id": "corner", "fg": [901, 903, 892, 895]}, {"id": "t_connection", "fg": [905, 907, 899, 897]}, {"id": "edge", "fg": [904, 906]}, {"id": "end_piece", "fg": [900, 902, 894, 893]}, {"id": "unconnected", "fg": 898}]}, {"id": ["t_shrub_raspberry"], "fg": 908, "bg": 1312}, {"id": ["t_shrub_raspberry_harvested"], "fg": 911, "bg": 1326}, {"id": "t_shrub_raspberry_season_summer", "fg": 910, "bg": 1326}, {"id": "t_shrub_raspberry_season_autumn", "fg": 908, "bg": 1313}, {"id": "t_shrub_raspberry_season_winter", "fg": 909, "bg": 649}, {"id": "t_woodchips", "fg": 912}, {"id": "t_woodchips_season_winter", "fg": 682}, {"id": "t_fence_post_season_spring", "fg": 923, "bg": 1312}, {"id": "t_fence_post_season_summer", "fg": 923, "bg": 1326}, {"id": "t_fence_post_season_autumn", "fg": 923, "bg": 1313}, {"id": "t_fence_post_season_winter", "fg": 923, "bg": 682}, {"id": "t_fence_season_spring", "multitile": true, "fg": 923, "bg": 1312, "additional_tiles": [{"id": "center", "bg": 1312, "fg": 922}, {"id": "corner", "bg": 1312, "fg": [916, 914, 917, 924]}, {"id": "t_connection", "bg": 1312, "fg": [926, 915, 927, 928]}, {"id": "edge", "bg": 1312, "fg": [925, 929]}, {"id": "end_piece", "bg": 1312, "fg": [920, 919, 930, 918]}, {"bg": 1312, "id": "unconnected", "fg": 923}]}, {"id": "t_fence_season_summer", "multitile": true, "fg": 923, "bg": 1326, "additional_tiles": [{"id": "center", "bg": 1326, "fg": 922}, {"id": "corner", "bg": 1326, "fg": [916, 914, 917, 924]}, {"id": "t_connection", "bg": 1326, "fg": [926, 915, 927, 928]}, {"id": "edge", "bg": 1326, "fg": [925, 929]}, {"id": "end_piece", "bg": 1326, "fg": [920, 919, 930, 918]}, {"bg": 1326, "id": "unconnected", "fg": 923}]}, {"id": "t_fence_season_autumn", "multitile": true, "fg": 923, "bg": 1313, "additional_tiles": [{"id": "center", "bg": 1313, "fg": 922}, {"id": "corner", "bg": 1313, "fg": [916, 914, 917, 924]}, {"id": "t_connection", "bg": 1313, "fg": [926, 915, 927, 928]}, {"id": "edge", "bg": 1313, "fg": [925, 929]}, {"id": "end_piece", "bg": 1313, "fg": [920, 919, 930, 918]}, {"bg": 1313, "id": "unconnected", "fg": 923}]}, {"id": "t_fence_season_winter", "multitile": true, "fg": 923, "bg": 682, "additional_tiles": [{"id": "center", "bg": 682, "fg": 922}, {"id": "corner", "bg": 682, "fg": [916, 914, 917, 924]}, {"id": "t_connection", "bg": 682, "fg": [926, 915, 927, 928]}, {"id": "edge", "bg": 682, "fg": [925, 929]}, {"id": "end_piece", "bg": 682, "fg": [920, 919, 930, 918]}, {"bg": 682, "id": "unconnected", "fg": 923}]}, {"id": "t_fencegate_c_season_spring", "fg": 913, "bg": 1312}, {"id": "t_fencegate_c_season_summer", "fg": 913, "bg": 1326}, {"id": "t_fencegate_c_season_autumn", "fg": 913, "bg": 1313}, {"id": "t_fencegate_c_season_winter", "fg": 913, "bg": 682}, {"id": "t_fencegate_o_season_spring", "fg": 921, "bg": 1312}, {"id": "t_fencegate_o_season_summer", "fg": 921, "bg": 1326}, {"id": "t_fencegate_o_season_autumn", "fg": 921, "bg": 1313}, {"id": "t_fencegate_o_season_winter", "fg": 921, "bg": 682}, {"id": "t_wall_y", "multitile": true, "fg": 945, "additional_tiles": [{"id": "center", "fg": 946}, {"id": "corner", "fg": [939, 943, 938, 932]}, {"id": "t_connection", "fg": [936, 941, 935, 940]}, {"id": "edge", "fg": [937, 942]}, {"id": "end_piece", "fg": [933, 934, 944, 931]}, {"id": "unconnected", "fg": 945}]}, {"id": "t_rdoor_c", "fg": 948}, {"id": "t_rdoor_o", "fg": 949}, {"id": "t_rdoor_b", "fg": 947}, {"id": ["t_gates_mech_control"], "fg": 952, "bg": 983}, {"id": ["t_gates_mech_control_lab"], "fg": 954, "bg": 983}, {"id": ["t_gates_control_concrete"], "fg": 951, "bg": 1026}, {"id": ["t_gates_control_concrete_lab"], "fg": 954, "bg": 2534}, {"id": ["t_gates_control_brick"], "fg": 951, "bg": 714}, {"id": ["t_gates_control_brick_lab"], "fg": 954, "bg": 714}, {"id": ["t_gates_control_metal"], "fg": 951, "bg": 2542}, {"id": ["t_gates_control_metal_lab"], "fg": 954, "bg": 2542}, {"id": ["t_elevator_control"], "fg": 950, "bg": 983}, {"id": ["t_elevator_control_off"], "fg": 953, "bg": 983}, {"id": ["t_stump"], "fg": 955, "bg": 1312}, {"id": ["t_stump_season_summer"], "fg": 955, "bg": 1326}, {"id": ["t_stump_season_autumn"], "fg": 955, "bg": 1313}, {"id": "t_stump_season_winter", "fg": 955, "bg": 649}, {"id": ["t_water_moving_dp", "t_swater_moving_dp"], "multitile": true, "fg": 959, "bg": 1312, "additional_tiles": [{"id": "center", "bg": 1312, "fg": [{"weight": 1, "sprite": 958}, {"weight": 1, "sprite": 964}]}, {"id": "corner", "bg": 1312, "fg": [965, 961, 960, 966]}, {"id": "t_connection", "bg": 1312, "fg": [967, 972, 969, 971]}, {"id": "edge", "bg": 1312, "fg": [963, 962]}, {"id": "end_piece", "bg": 1312, "fg": [968, 956, 957, 970]}, {"bg": 1312, "id": "unconnected", "fg": 959}]}, {"id": ["t_water_moving_dp_season_summer", "t_swater_moving_dp_season_summer"], "multitile": true, "fg": 959, "bg": 1326, "additional_tiles": [{"id": "center", "bg": 1326, "fg": [{"weight": 1, "sprite": 958}, {"weight": 1, "sprite": 964}]}, {"id": "corner", "bg": 1326, "fg": [965, 961, 960, 966]}, {"id": "t_connection", "bg": 1326, "fg": [967, 972, 969, 971]}, {"id": "edge", "bg": 1326, "fg": [963, 962]}, {"id": "end_piece", "bg": 1326, "fg": [968, 956, 957, 970]}, {"bg": 1326, "id": "unconnected", "fg": 959}]}, {"id": ["t_water_moving_dp_season_autumn", "t_swater_moving_dp_season_autumn"], "multitile": true, "fg": 959, "bg": 1313, "additional_tiles": [{"id": "center", "bg": 1313, "fg": [{"weight": 1, "sprite": 958}, {"weight": 1, "sprite": 964}]}, {"id": "corner", "bg": 1313, "fg": [965, 961, 960, 966]}, {"id": "t_connection", "bg": 1313, "fg": [967, 972, 969, 971]}, {"id": "edge", "bg": 1313, "fg": [963, 962]}, {"id": "end_piece", "bg": 1313, "fg": [968, 956, 957, 970]}, {"bg": 1313, "id": "unconnected", "fg": 959}]}, {"id": ["t_water_moving_dp_season_winter", "t_swater_moving_dp_season_winter"], "multitile": true, "fg": 959, "bg": 1321, "additional_tiles": [{"id": "center", "bg": 1321, "fg": [{"weight": 1, "sprite": 958}, {"weight": 1, "sprite": 964}]}, {"id": "corner", "bg": 1321, "fg": [965, 961, 960, 966]}, {"id": "t_connection", "bg": 1321, "fg": [967, 972, 969, 971]}, {"id": "edge", "bg": 1321, "fg": [963, 962]}, {"id": "end_piece", "bg": 1321, "fg": [968, 956, 957, 970]}, {"bg": 1321, "id": "unconnected", "fg": 959}]}, {"id": "t_thconc_floor", "multitile": true, "fg": 982, "additional_tiles": [{"id": "center", "fg": 983}, {"id": "corner", "fg": [979, 977, 987, 978]}, {"id": "t_connection", "fg": [984, 980, 973, 981]}, {"id": "edge", "fg": [986, 988]}, {"id": "end_piece", "fg": [976, 975, 985, 974]}, {"id": "unconnected", "fg": 982}]}, {"id": "t_carpet_green", "multitile": true, "fg": 999, "additional_tiles": [{"id": "center", "fg": 994}, {"id": "corner", "fg": [990, 1000, 998, 996]}, {"id": "t_connection", "fg": [992, 993, 991, 995]}, {"id": "edge", "fg": [1004, 997]}, {"id": "end_piece", "fg": [1002, 1001, 1003, 989]}, {"id": "unconnected", "fg": 999}]}, {"id": "t_door_lab_c", "fg": 1006}, {"id": "t_door_lab_o", "fg": 1005}, {"id": "t_dirtfloor", "multitile": true, "fg": 1021, "additional_tiles": [{"id": "center", "fg": 1015}, {"id": "corner", "fg": [1016, 1020, 1013, 1019]}, {"id": "t_connection", "fg": [1018, 1010, 1022, 1009]}, {"id": "edge", "fg": [1012, 1017]}, {"id": "end_piece", "fg": [1014, 1011, 1007, 1008]}, {"id": "unconnected", "fg": 1021}]}, {"id": "t_concrete_wall", "multitile": true, "fg": 1026, "additional_tiles": [{"id": "center", "fg": 1030}, {"id": "corner", "fg": [1038, 1029, 1037, 1028]}, {"id": "t_connection", "fg": [1033, 1023, 1031, 1027]}, {"id": "edge", "fg": [1036, 1025]}, {"id": "end_piece", "fg": [1035, 1032, 1034, 1024]}, {"id": "unconnected", "fg": 1026}]}, {"id": "t_wall_w", "multitile": true, "fg": 1040, "additional_tiles": [{"id": "center", "fg": 1050}, {"id": "corner", "fg": [1054, 1046, 1049, 1042]}, {"id": "t_connection", "fg": [1041, 1053, 1043, 1039]}, {"id": "edge", "fg": [1044, 1051]}, {"id": "end_piece", "fg": [1052, 1048, 1045, 1047]}, {"id": "unconnected", "fg": 1040}]}, {"id": "t_rock_wall", "multitile": true, "fg": 1055, "additional_tiles": [{"id": "center", "fg": 1064}, {"id": "corner", "fg": [1059, 1067, 1056, 1057]}, {"id": "t_connection", "fg": [1060, 1069, 1070, 1066]}, {"id": "edge", "fg": [1058, 1065]}, {"id": "end_piece", "fg": [1061, 1062, 1063, 1068]}, {"id": "unconnected", "fg": 1055}]}, {"id": "t_rock_floor", "multitile": true, "fg": 1078, "additional_tiles": [{"id": "center", "fg": 1082}, {"id": "corner", "fg": [1080, 1086, 1085, 1084]}, {"id": "t_connection", "fg": [1071, 1076, 1079, 1074]}, {"id": "edge", "fg": [1073, 1072]}, {"id": "end_piece", "fg": [1083, 1081, 1077, 1075]}, {"id": "unconnected", "fg": 1078}]}, {"id": "t_grass_tall", "multitile": true, "fg": 1098, "bg": 1312, "additional_tiles": [{"id": "center", "bg": 1312, "fg": 1096}, {"id": "corner", "bg": 1312, "fg": [1090, 1094, 1100, 1093]}, {"id": "t_connection", "bg": 1312, "fg": [1092, 1088, 1097, 1101]}, {"id": "edge", "bg": 1312, "fg": [1091, 1089]}, {"id": "end_piece", "bg": 1312, "fg": [1095, 1087, 1099, 1102]}, {"bg": 1312, "id": "unconnected", "fg": 1098}]}, {"id": "t_grass_tall_season_summer", "multitile": true, "fg": 1106, "bg": 1326, "additional_tiles": [{"id": "center", "bg": 1326, "fg": 1113}, {"id": "corner", "bg": 1326, "fg": [1111, 1115, 1118, 1103]}, {"id": "t_connection", "bg": 1326, "fg": [1110, 1117, 1109, 1114]}, {"id": "edge", "bg": 1326, "fg": [1104, 1116]}, {"id": "end_piece", "bg": 1326, "fg": [1105, 1107, 1108, 1112]}, {"bg": 1326, "id": "unconnected", "fg": 1106}]}, {"id": "t_grass_tall_season_autumn", "multitile": true, "fg": 1132, "bg": 1313, "additional_tiles": [{"id": "center", "bg": 1313, "fg": 1126}, {"id": "corner", "bg": 1313, "fg": [1119, 1134, 1122, 1124]}, {"id": "t_connection", "bg": 1313, "fg": [1130, 1125, 1127, 1129]}, {"id": "edge", "bg": 1313, "fg": [1120, 1121]}, {"id": "end_piece", "bg": 1313, "fg": [1133, 1131, 1128, 1123]}, {"bg": 1313, "id": "unconnected", "fg": 1132}]}, {"id": "t_grass_tall_season_winter", "fg": 649}, {"id": "f_grass_tall", "multitile": true, "fg": 1098, "additional_tiles": [{"id": "center", "fg": 1096}, {"id": "corner", "fg": [1090, 1094, 1100, 1093]}, {"id": "t_connection", "fg": [1092, 1088, 1097, 1101]}, {"id": "edge", "fg": [1091, 1089]}, {"id": "end_piece", "fg": [1095, 1087, 1099, 1102]}, {"id": "unconnected", "fg": 1098}]}, {"id": "f_grass_tall_summer", "multitile": true, "fg": 1106, "additional_tiles": [{"id": "center", "fg": 1113}, {"id": "corner", "fg": [1111, 1115, 1118, 1103]}, {"id": "t_connection", "fg": [1110, 1117, 1109, 1114]}, {"id": "edge", "fg": [1104, 1116]}, {"id": "end_piece", "fg": [1105, 1107, 1108, 1112]}, {"id": "unconnected", "fg": 1106}]}, {"id": "f_grass_tall_autumn", "multitile": true, "fg": 1132, "additional_tiles": [{"id": "center", "fg": 1126}, {"id": "corner", "fg": [1119, 1134, 1122, 1124]}, {"id": "t_connection", "fg": [1130, 1125, 1127, 1129]}, {"id": "edge", "fg": [1120, 1121]}, {"id": "end_piece", "fg": [1133, 1131, 1128, 1123]}, {"id": "unconnected", "fg": 1132}]}, {"id": "t_grass_long", "multitile": true, "fg": 1140, "bg": 1312, "additional_tiles": [{"id": "center", "bg": 1312, "fg": 1146}, {"id": "corner", "bg": 1312, "fg": [1141, 1144, 1135, 1139]}, {"id": "t_connection", "bg": 1312, "fg": [1142, 1147, 1136, 1150]}, {"id": "edge", "bg": 1312, "fg": [1148, 1138]}, {"id": "end_piece", "bg": 1312, "fg": [1143, 1137, 1149, 1145]}, {"bg": 1312, "id": "unconnected", "fg": 1140}]}, {"id": "t_grass_long_season_autumn", "multitile": true, "fg": 1166, "bg": 1313, "additional_tiles": [{"id": "center", "bg": 1313, "fg": 1159}, {"id": "corner", "bg": 1313, "fg": [1158, 1165, 1164, 1161]}, {"id": "t_connection", "bg": 1313, "fg": [1152, 1151, 1156, 1163]}, {"id": "edge", "bg": 1313, "fg": [1154, 1157]}, {"id": "end_piece", "bg": 1313, "fg": [1153, 1160, 1155, 1162]}, {"bg": 1313, "id": "unconnected", "fg": 1166}]}, {"id": "t_grass_long_season_summer", "multitile": true, "fg": 1179, "bg": 1326, "additional_tiles": [{"id": "center", "bg": 1326, "fg": 1176}, {"id": "corner", "bg": 1326, "fg": [1180, 1173, 1169, 1174]}, {"id": "t_connection", "bg": 1326, "fg": [1172, 1177, 1178, 1171]}, {"id": "edge", "bg": 1326, "fg": [1182, 1170]}, {"id": "end_piece", "bg": 1326, "fg": [1168, 1175, 1181, 1167]}, {"bg": 1326, "id": "unconnected", "fg": 1179}]}, {"id": "t_grass_long_season_winter", "fg": 649}, {"id": "f_grass_long", "multitile": true, "fg": 1140, "additional_tiles": [{"id": "center", "fg": 1146}, {"id": "corner", "fg": [1141, 1144, 1135, 1139]}, {"id": "t_connection", "fg": [1142, 1147, 1136, 1150]}, {"id": "edge", "fg": [1148, 1138]}, {"id": "end_piece", "fg": [1143, 1137, 1149, 1145]}, {"id": "unconnected", "fg": 1140}]}, {"id": "f_grass_long_season_autumn", "multitile": true, "fg": 1166, "additional_tiles": [{"id": "center", "fg": 1159}, {"id": "corner", "fg": [1158, 1165, 1164, 1161]}, {"id": "t_connection", "fg": [1152, 1151, 1156, 1163]}, {"id": "edge", "fg": [1154, 1157]}, {"id": "end_piece", "fg": [1153, 1160, 1155, 1162]}, {"id": "unconnected", "fg": 1166}]}, {"id": "f_grass_long_season_summer", "multitile": true, "fg": 1179, "additional_tiles": [{"id": "center", "fg": 1176}, {"id": "corner", "fg": [1180, 1173, 1169, 1174]}, {"id": "t_connection", "fg": [1172, 1177, 1178, 1171]}, {"id": "edge", "fg": [1182, 1170]}, {"id": "end_piece", "fg": [1168, 1175, 1181, 1167]}, {"id": "unconnected", "fg": 1179}]}, {"id": ["t_shrub_lilac"], "fg": 1186, "bg": 1312}, {"id": ["t_shrub_lilac_harvested"], "fg": 1183, "bg": 1312}, {"id": "t_shrub_lilac_season_summer", "fg": 1185, "bg": 1326}, {"id": "t_shrub_lilac_season_autumn", "fg": 1183, "bg": 1313}, {"id": "t_shrub_lilac_season_winter", "fg": 1184, "bg": 649}, {"id": "t_elevator", "fg": [{"weight": 100, "sprite": 1187}, {"weight": 100, "sprite": 1188}]}, {"id": "t_moss", "fg": 1189}, {"id": "t_moss_season_winter", "fg": 649}, {"id": "t_scrap_floor", "multitile": true, "fg": 1199, "additional_tiles": [{"id": "center", "fg": 1196}, {"id": "corner", "fg": [1194, 1201, 1190, 1205]}, {"id": "t_connection", "fg": [1193, 1204, 1192, 1195]}, {"id": "edge", "fg": [1203, 1197]}, {"id": "end_piece", "fg": [1200, 1191, 1202, 1198]}, {"id": "unconnected", "fg": 1199}]}, {"id": "t_grass_dead", "multitile": true, "fg": 1220, "bg": 1312, "additional_tiles": [{"id": "center", "bg": 1312, "fg": 1216}, {"id": "corner", "bg": 1312, "fg": [1221, 1212, 1213, 1208]}, {"id": "t_connection", "bg": 1312, "fg": [1215, 1210, 1218, 1209]}, {"id": "edge", "bg": 1312, "fg": [1207, 1214]}, {"id": "end_piece", "bg": 1312, "fg": [1219, 1206, 1211, 1217]}, {"bg": 1312, "id": "unconnected", "fg": 1220}]}, {"id": "t_grass_dead_season_summer", "multitile": true, "fg": 1251, "bg": 1326, "additional_tiles": [{"id": "center", "bg": 1326, "fg": 1250}, {"id": "corner", "bg": 1326, "fg": [1238, 1239, 1243, 1241]}, {"id": "t_connection", "bg": 1326, "fg": [1249, 1253, 1247, 1252]}, {"id": "edge", "bg": 1326, "fg": [1242, 1248]}, {"id": "end_piece", "bg": 1326, "fg": [1245, 1246, 1244, 1240]}, {"bg": 1326, "id": "unconnected", "fg": 1251}]}, {"id": "t_grass_dead_season_autumn", "multitile": true, "fg": 1223, "bg": 1313, "additional_tiles": [{"id": "center", "bg": 1313, "fg": 1237}, {"id": "corner", "bg": 1313, "fg": [1227, 1228, 1229, 1235]}, {"id": "t_connection", "bg": 1313, "fg": [1225, 1230, 1231, 1232]}, {"id": "edge", "bg": 1313, "fg": [1222, 1234]}, {"id": "end_piece", "bg": 1313, "fg": [1236, 1226, 1224, 1233]}, {"bg": 1313, "id": "unconnected", "fg": 1223}]}, {"id": "t_grass_dead_season_winter", "multitile": true, "fg": 675, "bg": 1321, "additional_tiles": [{"id": "center", "bg": 1321, "fg": [{"weight": 1, "sprite": 682}, {"weight": 1, "sprite": 674}, {"weight": 1, "sprite": 673}, {"weight": 1, "sprite": 671}]}, {"id": "corner", "bg": 1321, "fg": [678, 677, 684, 669]}, {"id": "t_connection", "bg": 1321, "fg": [668, 680, 681, 679]}, {"id": "edge", "bg": 1321, "fg": [683, 676]}, {"id": "end_piece", "bg": 1321, "fg": [667, 672, 670, 666]}, {"bg": 1321, "id": "unconnected", "fg": 675}]}, {"id": ["t_water_sh", "t_swater_sh"], "multitile": true, "fg": 1267, "bg": 1312, "additional_tiles": [{"id": "center", "bg": 1312, "fg": [{"weight": 1, "sprite": 1263}, {"weight": 1, "sprite": 1270}, {"weight": 1, "sprite": 1265}, {"weight": 1, "sprite": 1257}]}, {"id": "corner", "bg": 1312, "fg": [1258, 1261, 1272, 1269]}, {"id": "t_connection", "bg": 1312, "fg": [1259, 1264, 1268, 1271]}, {"id": "edge", "bg": 1312, "fg": [1256, 1260]}, {"id": "end_piece", "bg": 1312, "fg": [1255, 1262, 1254, 1266]}, {"bg": 1312, "id": "unconnected", "fg": 1267}]}, {"id": ["t_water_sh_season_summer", "t_swater_sh_season_summer"], "multitile": true, "fg": 1267, "bg": 1326, "additional_tiles": [{"id": "center", "bg": 1326, "fg": [{"weight": 1, "sprite": 1263}, {"weight": 1, "sprite": 1270}, {"weight": 1, "sprite": 1265}]}, {"id": "corner", "bg": 1326, "fg": [1258, 1261, 1272, 1269]}, {"id": "t_connection", "bg": 1326, "fg": [1259, 1264, 1268, 1271]}, {"id": "edge", "bg": 1326, "fg": [1256, 1260]}, {"id": "end_piece", "bg": 1326, "fg": [1255, 1262, 1254, 1266]}, {"bg": 1326, "id": "unconnected", "fg": 1267}]}, {"id": ["t_water_sh_season_autumn", "t_swater_sh_season_autumn"], "multitile": true, "fg": 1267, "bg": 1313, "additional_tiles": [{"id": "center", "bg": 1313, "fg": [{"weight": 1, "sprite": 1263}, {"weight": 1, "sprite": 1270}, {"weight": 1, "sprite": 1265}]}, {"id": "corner", "bg": 1313, "fg": [1258, 1261, 1272, 1269]}, {"id": "t_connection", "bg": 1313, "fg": [1259, 1264, 1268, 1271]}, {"id": "edge", "bg": 1313, "fg": [1256, 1260]}, {"id": "end_piece", "bg": 1313, "fg": [1255, 1262, 1254, 1266]}, {"bg": 1313, "id": "unconnected", "fg": 1267}]}, {"id": ["t_water_sh_season_winter", "t_swater_sh_season_winter"], "multitile": true, "fg": 1267, "bg": 682, "additional_tiles": [{"id": "center", "bg": 682, "fg": [{"weight": 1, "sprite": 1263}, {"weight": 1, "sprite": 1270}, {"weight": 1, "sprite": 1265}]}, {"id": "corner", "bg": 682, "fg": [1258, 1261, 1272, 1269]}, {"id": "t_connection", "bg": 682, "fg": [1259, 1264, 1268, 1271]}, {"id": "edge", "bg": 682, "fg": [1256, 1260]}, {"id": "end_piece", "bg": 682, "fg": [1255, 1262, 1254, 1266]}, {"bg": 682, "id": "unconnected", "fg": 1267}]}, {"id": "t_reinforced_glass", "multitile": true, "fg": 1284, "additional_tiles": [{"id": "center", "fg": 1275}, {"id": "corner", "fg": [1288, 1283, 1278, 1282]}, {"id": "t_connection", "fg": [1285, 1286, 1277, 1281]}, {"id": "edge", "fg": [1274, 1287]}, {"id": "end_piece", "fg": [1280, 1279, 1273, 1276]}, {"id": "unconnected", "fg": 1284}]}, {"id": "t_carpet_yellow", "multitile": true, "fg": 1290, "additional_tiles": [{"id": "center", "fg": 1303}, {"id": "corner", "fg": [1289, 1298, 1301, 1294]}, {"id": "t_connection", "fg": [1304, 1292, 1291, 1297]}, {"id": "edge", "fg": [1296, 1302]}, {"id": "end_piece", "fg": [1300, 1299, 1295, 1293]}, {"id": "unconnected", "fg": 1290}]}, {"id": "t_dirt", "bg": [{"weight": 100, "sprite": 1312}, {"weight": 100, "sprite": 1315}, {"weight": 100, "sprite": 1309}, {"weight": 100, "sprite": 1318}]}, {"id": "t_dirt_season_autumn", "bg": [{"weight": 100, "sprite": 1313}, {"weight": 100, "sprite": 1305}, {"weight": 100, "sprite": 1319}, {"weight": 100, "sprite": 1308}]}, {"id": "t_dirt_season_summer", "bg": [{"weight": 100, "sprite": 1326}, {"weight": 100, "sprite": 1306}, {"weight": 100, "sprite": 1314}, {"weight": 100, "sprite": 1327}]}, {"id": "t_dirt_season_winter", "bg": [{"weight": 100, "sprite": 1321}, {"weight": 100, "sprite": 1317}, {"weight": 100, "sprite": 1316}, {"weight": 100, "sprite": 1324}]}, {"id": "t_door_metal_c_peep", "fg": 1328}, {"id": "t_shrub", "fg": 1329, "bg": 1312}, {"id": "t_shrub_season_summer", "fg": 1329, "bg": 1326}, {"id": "t_shrub_season_autumn", "fg": 1329, "bg": 1313}, {"id": "t_shrub_season_winter", "fg": 1330, "bg": 649}, {"id": "t_pit", "fg": 1331, "bg": [{"weight": 100, "sprite": 1312}, {"weight": 100, "sprite": 1315}]}, {"id": "t_pit_season_summer", "fg": 1331, "bg": [{"weight": 100, "sprite": 1326}, {"weight": 100, "sprite": 1306}]}, {"id": "t_pit_season_autumn", "fg": 1331, "bg": [{"weight": 100, "sprite": 1313}, {"weight": 100, "sprite": 1305}]}, {"id": "t_door_c", "fg": 1332}, {"id": "t_door_locked", "fg": 1334}, {"id": "t_door_o", "fg": 1333}, {"id": "t_door_b", "fg": 1335}, {"id": "t_scrap_wall", "multitile": true, "fg": 1350, "additional_tiles": [{"id": "center", "fg": 1347}, {"id": "corner", "fg": [1337, 1351, 1349, 1352]}, {"id": "t_connection", "fg": [1342, 1345, 1341, 1339]}, {"id": "edge", "fg": [1340, 1344]}, {"id": "end_piece", "fg": [1346, 1338, 1336, 1343]}, {"id": "unconnected", "fg": 1350}]}, {"id": "t_clay", "multitile": true, "fg": 1357, "bg": 1312, "additional_tiles": [{"id": "center", "bg": 1312, "fg": 1364}, {"id": "corner", "bg": 1312, "fg": [1359, 1354, 1356, 1365]}, {"id": "t_connection", "bg": 1312, "fg": [1367, 1366, 1358, 1362]}, {"id": "edge", "bg": 1312, "fg": [1353, 1355]}, {"id": "end_piece", "bg": 1312, "fg": [1363, 1368, 1361, 1360]}, {"bg": 1312, "id": "unconnected", "fg": 1357}]}, {"id": "t_clay_season_summer", "multitile": true, "fg": 1357, "bg": 1326, "additional_tiles": [{"id": "center", "bg": 1326, "fg": 1364}, {"id": "corner", "bg": 1326, "fg": [1359, 1354, 1356, 1365]}, {"id": "t_connection", "bg": 1326, "fg": [1367, 1366, 1358, 1362]}, {"id": "edge", "bg": 1326, "fg": [1353, 1355]}, {"id": "end_piece", "bg": 1326, "fg": [1363, 1368, 1361, 1360]}, {"bg": 1326, "id": "unconnected", "fg": 1357}]}, {"id": "t_clay_season_autumn", "multitile": true, "fg": 1357, "bg": 1313, "additional_tiles": [{"id": "center", "bg": 1313, "fg": 1364}, {"id": "corner", "bg": 1313, "fg": [1359, 1354, 1356, 1365]}, {"id": "t_connection", "bg": 1313, "fg": [1367, 1366, 1358, 1362]}, {"id": "edge", "bg": 1313, "fg": [1353, 1355]}, {"id": "end_piece", "bg": 1313, "fg": [1363, 1368, 1361, 1360]}, {"bg": 1313, "id": "unconnected", "fg": 1357}]}, {"id": "t_clay_season_winter", "multitile": true, "fg": 1357, "bg": 1321, "additional_tiles": [{"id": "center", "bg": 1321, "fg": 1364}, {"id": "corner", "bg": 1321, "fg": [1359, 1354, 1356, 1365]}, {"id": "t_connection", "bg": 1321, "fg": [1367, 1366, 1358, 1362]}, {"id": "edge", "bg": 1321, "fg": [1353, 1355]}, {"id": "end_piece", "bg": 1321, "fg": [1363, 1368, 1361, 1360]}, {"bg": 1321, "id": "unconnected", "fg": 1357}]}, {"id": ["t_shrub_hydrangea"], "fg": 1371, "bg": 1312}, {"id": ["t_shrub_hydrangea_harvested"], "fg": 1372, "bg": 1312}, {"id": "t_shrub_hydrangea_season_summer", "fg": 1369, "bg": 1326}, {"id": "t_shrub_hydrangea_season_autumn", "fg": 1371, "bg": 1313}, {"id": "t_shrub_hydrangea_season_winter", "fg": 1370, "bg": 649}, {"id": "t_bars", "fg": 1373}, {"id": ["t_shrub_grape"], "fg": 1377, "bg": 1312}, {"id": ["t_shrub_grape_harvested"], "fg": 1374, "bg": 1326}, {"id": "t_shrub_grape_season_summer", "fg": 1376, "bg": 1326}, {"id": "t_shrub_grape_season_autumn", "fg": 1377, "bg": 1313}, {"id": "t_shrub_grape_season_winter", "fg": 1375, "bg": 649}, {"id": "t_door_glass_o", "fg": 1378}, {"id": ["t_shrub_rose"], "fg": 1381, "bg": 1312}, {"id": "t_shrub_rose_season_summer", "fg": 1381, "bg": 1326}, {"id": "t_shrub_rose_harvested", "fg": 1381, "bg": 1313}, {"id": "t_shrub_rose_season_autumn", "fg": 1379, "bg": 1313}, {"id": "t_shrub_rose_season_winter", "fg": 1380, "bg": 649}, {"id": "t_wall", "multitile": true, "fg": 1394, "additional_tiles": [{"id": "center", "fg": 1389}, {"id": "corner", "fg": [1395, 1387, 1384, 1393]}, {"id": "t_connection", "fg": [1391, 1386, 1397, 1382]}, {"id": "edge", "fg": [1383, 1385]}, {"id": "end_piece", "fg": [1390, 1396, 1392, 1388]}, {"id": "unconnected", "fg": 1394}]}, {"id": "t_wall_p", "multitile": true, "fg": 1407, "additional_tiles": [{"id": "center", "fg": 1412}, {"id": "corner", "fg": [1400, 1403, 1408, 1405]}, {"id": "t_connection", "fg": [1410, 1404, 1406, 1398]}, {"id": "edge", "fg": [1411, 1399]}, {"id": "end_piece", "fg": [1409, 1401, 1413, 1402]}, {"id": "unconnected", "fg": 1407}]}, {"id": "t_wall_log", "multitile": true, "fg": 1421, "bg": 1312, "additional_tiles": [{"id": "center", "fg": 1425}, {"id": "corner", "fg": [1426, 1415, 1429, 1423]}, {"id": "t_connection", "fg": [1420, 1417, 1428, 1419]}, {"id": "edge", "bg": 1312, "fg": [1416, 1418]}, {"id": "end_piece", "bg": 1312, "fg": [1427, 1422, 1424, 1414]}, {"bg": 1312, "id": "unconnected", "fg": 1421}]}, {"id": "t_wall_g", "multitile": true, "fg": 1435, "additional_tiles": [{"id": "center", "fg": 1439}, {"id": "corner", "fg": [1445, 1442, 1436, 1444]}, {"id": "t_connection", "fg": [1440, 1431, 1441, 1437]}, {"id": "edge", "fg": [1432, 1433]}, {"id": "end_piece", "fg": [1443, 1438, 1434, 1430]}, {"id": "unconnected", "fg": 1435}]}, {"id": "t_door_boarded", "fg": 1446}, {"id": "t_door_boarded_damaged", "fg": 1447}, {"id": ["t_water_pool", "t_water_pool_shallow"], "multitile": true, "fg": 1455, "additional_tiles": [{"id": "center", "fg": [{"weight": 1, "sprite": 1263}, {"weight": 1, "sprite": 1270}, {"weight": 1, "sprite": 1265}]}, {"id": "corner", "fg": [1450, 1459, 1458, 1452]}, {"id": "t_connection", "fg": [1454, 1451, 1456, 1460]}, {"id": "edge", "fg": [1457, 1449]}, {"id": "end_piece", "fg": [1461, 1462, 1448, 1453]}, {"id": "unconnected", "fg": 1455}]}, {"id": "t_pavement", "fg": 1463}, {"id": "t_pavement_season_winter", "fg": 682}, {"id": ["t_water_dp", "t_swater_dp"], "multitile": true, "fg": 1471, "bg": 1312, "additional_tiles": [{"id": "center", "bg": 1312, "fg": [{"weight": 1, "sprite": 1473}, {"weight": 1, "sprite": 1474}, {"weight": 1, "sprite": 1481}, {"weight": 1, "sprite": 1479}]}, {"id": "corner", "bg": 1312, "fg": [1480, 1464, 1466, 1475]}, {"id": "t_connection", "bg": 1312, "fg": [1468, 1476, 1478, 1467]}, {"id": "edge", "bg": 1312, "fg": [1470, 1482]}, {"id": "end_piece", "bg": 1312, "fg": [1477, 1472, 1469, 1465]}, {"bg": 1312, "id": "unconnected", "fg": 1471}]}, {"id": ["t_water_dp_season_summer", "t_swater_dp_season_summer"], "multitile": true, "fg": 1471, "bg": 1326, "additional_tiles": [{"id": "center", "bg": 1326, "fg": [{"weight": 1, "sprite": 1473}, {"weight": 1, "sprite": 1474}, {"weight": 1, "sprite": 1481}]}, {"id": "corner", "bg": 1326, "fg": [1480, 1464, 1466, 1475]}, {"id": "t_connection", "bg": 1326, "fg": [1468, 1476, 1478, 1467]}, {"id": "edge", "bg": 1326, "fg": [1470, 1482]}, {"id": "end_piece", "bg": 1326, "fg": [1477, 1472, 1469, 1465]}, {"bg": 1326, "id": "unconnected", "fg": 1471}]}, {"id": ["t_water_dp_season_autumn", "t_swater_dp_season_autumn"], "multitile": true, "fg": 1471, "bg": 1313, "additional_tiles": [{"id": "center", "bg": 1313, "fg": [{"weight": 1, "sprite": 1473}, {"weight": 1, "sprite": 1474}, {"weight": 1, "sprite": 1481}]}, {"id": "corner", "bg": 1313, "fg": [1480, 1464, 1466, 1475]}, {"id": "t_connection", "bg": 1313, "fg": [1468, 1476, 1478, 1467]}, {"id": "edge", "bg": 1313, "fg": [1470, 1482]}, {"id": "end_piece", "bg": 1313, "fg": [1477, 1472, 1469, 1465]}, {"bg": 1313, "id": "unconnected", "fg": 1471}]}, {"id": ["t_water_dp_season_winter", "t_swater_dp_season_winter"], "multitile": true, "fg": 1471, "bg": 682, "additional_tiles": [{"id": "center", "bg": 682, "fg": [{"weight": 1, "sprite": 1473}, {"weight": 1, "sprite": 1474}, {"weight": 1, "sprite": 1481}]}, {"id": "corner", "bg": 682, "fg": [1480, 1464, 1466, 1475]}, {"id": "t_connection", "bg": 682, "fg": [1468, 1476, 1478, 1467]}, {"id": "edge", "bg": 682, "fg": [1470, 1482]}, {"id": "end_piece", "bg": 682, "fg": [1477, 1472, 1469, 1465]}, {"bg": 682, "id": "unconnected", "fg": 1471}]}, {"id": ["t_linoleum_gray", "t_linoluem_gray_no_roof"], "multitile": true, "fg": 1486, "additional_tiles": [{"id": "center", "fg": 1485}, {"id": "corner", "fg": [1494, 1487, 1493, 1489]}, {"id": "t_connection", "fg": [1490, 1484, 1483, 1496]}, {"id": "edge", "fg": [1488, 1495]}, {"id": "end_piece", "fg": [1492, 1497, 1498, 1491]}, {"id": "unconnected", "fg": 1486}]}, {"id": "t_grass", "multitile": true, "fg": 1504, "bg": 1312, "additional_tiles": [{"id": "center", "bg": 1312, "fg": [{"weight": 1, "sprite": 1505}, {"weight": 1, "sprite": 1511}, {"weight": 1, "sprite": 1508}, {"weight": 1, "sprite": 1499}]}, {"id": "corner", "bg": 1312, "fg": [1514, 1507, 1512, 1503]}, {"id": "t_connection", "bg": 1312, "fg": [1501, 1516, 1500, 1517]}, {"id": "edge", "bg": 1312, "fg": [1513, 1502]}, {"id": "end_piece", "bg": 1312, "fg": [1510, 1515, 1506, 1509]}, {"bg": 1312, "id": "unconnected", "fg": 1504}]}, {"id": "t_grass_season_summer", "multitile": true, "fg": 1555, "bg": 1326, "additional_tiles": [{"id": "center", "bg": 1326, "fg": [{"weight": 1, "sprite": 1547}, {"weight": 1, "sprite": 1546}, {"weight": 1, "sprite": 1552}, {"weight": 1, "sprite": 1548}]}, {"id": "corner", "bg": 1326, "fg": [1554, 1538, 1540, 1539]}, {"id": "t_connection", "bg": 1326, "fg": [1541, 1543, 1553, 1551]}, {"id": "edge", "bg": 1326, "fg": [1545, 1544]}, {"id": "end_piece", "bg": 1326, "fg": [1537, 1550, 1549, 1542]}, {"bg": 1326, "id": "unconnected", "fg": 1555}]}, {"id": "t_grass_season_autumn", "multitile": true, "fg": 1519, "bg": 1313, "additional_tiles": [{"id": "center", "bg": 1313, "fg": [{"weight": 1, "sprite": 1528}, {"weight": 1, "sprite": 1532}, {"weight": 1, "sprite": 1527}, {"weight": 1, "sprite": 1535}]}, {"id": "corner", "bg": 1313, "fg": [1534, 1518, 1536, 1530]}, {"id": "t_connection", "bg": 1313, "fg": [1531, 1521, 1529, 1526]}, {"id": "edge", "bg": 1313, "fg": [1523, 1522]}, {"id": "end_piece", "bg": 1313, "fg": [1525, 1524, 1520, 1533]}, {"bg": 1313, "id": "unconnected", "fg": 1519}]}, {"id": "t_grass_season_winter", "multitile": true, "fg": 675, "bg": 1321, "additional_tiles": [{"id": "center", "bg": 1321, "fg": [{"weight": 1, "sprite": 682}, {"weight": 1, "sprite": 674}, {"weight": 1, "sprite": 673}, {"weight": 1, "sprite": 671}]}, {"id": "corner", "bg": 1321, "fg": [678, 677, 684, 669]}, {"id": "t_connection", "bg": 1321, "fg": [668, 680, 681, 679]}, {"id": "edge", "bg": 1321, "fg": [683, 676]}, {"id": "end_piece", "bg": 1321, "fg": [667, 672, 670, 666]}, {"bg": 1321, "id": "unconnected", "fg": 675}]}, {"id": "t_stairs_down", "fg": 1556}, {"id": "t_wood_stairs_down", "fg": 1557}, {"id": "t_floor_wax", "multitile": true, "fg": 1567, "additional_tiles": [{"id": "center", "fg": 1570}, {"id": "corner", "fg": [1572, 1559, 1566, 1563]}, {"id": "t_connection", "fg": [1568, 1573, 1560, 1561]}, {"id": "edge", "fg": [1564, 1569]}, {"id": "end_piece", "fg": [1558, 1571, 1562, 1565]}, {"id": "unconnected", "fg": 1567}]}, {"id": "t_wall_r", "multitile": true, "fg": 1574, "additional_tiles": [{"id": "center", "fg": 1581}, {"id": "corner", "fg": [1583, 1582, 1587, 1588]}, {"id": "t_connection", "fg": [1577, 1579, 1580, 1584]}, {"id": "edge", "fg": [1589, 1586]}, {"id": "end_piece", "fg": [1578, 1585, 1576, 1575]}, {"id": "unconnected", "fg": 1574}]}, {"id": "t_concrete", "multitile": true, "fg": 1597, "additional_tiles": [{"id": "center", "fg": 1591}, {"id": "corner", "fg": [1602, 1599, 1590, 1600]}, {"id": "t_connection", "fg": [1598, 1592, 1604, 1594]}, {"id": "edge", "fg": [1596, 1601]}, {"id": "end_piece", "fg": [1595, 1593, 1605, 1603]}, {"id": "unconnected", "fg": 1597}]}, {"id": "t_concrete_season_winter", "fg": 682}, {"id": "t_wall_resin", "multitile": true, "fg": 1614, "additional_tiles": [{"id": "center", "fg": 1621}, {"id": "corner", "fg": [1609, 1620, 1611, 1617]}, {"id": "t_connection", "fg": [1606, 1612, 1608, 1610]}, {"id": "edge", "fg": [1623, 1622]}, {"id": "end_piece", "fg": [1619, 1613, 1615, 1616]}, {"id": "unconnected", "fg": 1614}]}, {"id": "t_resin_hole_c", "fg": 1618}, {"id": "t_resin_hole_o", "fg": 1607}, {"id": "t_ladder_down", "fg": 1624}, {"id": "tr_nailboard", "fg": 1625, "bg": 485}, {"id": "animation_hit", "fg": 1626}, {"id": ["overlay_effects_bleed"], "fg": 1632}, {"id": ["overlay_effects_deaf"], "fg": 1627}, {"id": ["overlay_effects_downed"], "fg": 1634}, {"id": ["overlay_effects_grabbed"], "fg": 1633}, {"id": ["overlay_effects_winded"], "fg": 1631}, {"id": ["overlay_effects_hot"], "fg": 1628}, {"id": ["overlay_effects_cold"], "fg": 1637}, {"id": ["overlay_male_crouch", "overlay_female_crouch"], "fg": 1630}, {"id": ["overlay_male_run", "overlay_female_run"], "fg": 1635}, {"id": ["overlay_hostile_sees_player"], "fg": 1636}, {"id": ["zombie_revival_indicator"], "fg": 1629}, {"id": "cursor", "fg": 1638}, {"id": "highlight", "fg": 1641}, {"id": "highlight_item", "fg": 1642}, {"id": "line_target", "fg": 1639}, {"id": "line_trail", "fg": 1640}, {"id": "animation_line", "fg": 1643}, {"id": "mon_pig", "fg": [{"weight": 8, "sprite": 1646}, {"weight": 3, "sprite": 1645}, {"weight": 1, "sprite": 1644}], "bg": 1670}, {"id": "mon_piglet", "fg": 1647, "bg": 1671}, {"id": "mon_deer_mouse", "fg": 1648, "bg": 1671}, {"id": "mon_locust_nymph", "fg": 1649, "bg": 1671}, {"id": "mon_skittering_plague", "fg": 1650, "bg": 1670}, {"id": "mon_black_rat", "fg": 1651, "bg": 1671}, {"id": "mon_zolf", "fg": 1652, "bg": 1670}, {"id": "mon_dog", "fg": 1653, "bg": 1670}, {"id": "mon_dog_skeleton", "fg": 1657, "bg": 1670}, {"id": "mon_zombie_dog", "fg": [{"weight": 1, "sprite": 1658}, {"weight": 1, "sprite": 1659}], "bg": 1670}, {"id": "mon_dog_beagle", "fg": 1656, "bg": 1671}, {"id": "mon_dog_gshepherd", "fg": 1655, "bg": 1670}, {"id": "mon_dog_boxer", "fg": 1654, "bg": 1670}, {"id": "mon_dog_dachshund", "fg": 1660, "bg": 1670}, {"id": "mon_blob", "fg": 1661, "bg": 1670}, {"id": "mon_squirrel_red", "fg": 1662, "bg": 1671}, {"id": "mon_plague_vector", "fg": 1663, "bg": 1670}, {"id": "mon_blob_small", "fg": [{"weight": 1, "sprite": 1664}, {"weight": 1, "sprite": 1665}], "bg": 1671}, {"id": "mon_dragonfly", "fg": 1666, "bg": 1671}, {"id": "mon_zombie_child", "fg": [{"weight": 1, "sprite": 1667}, {"weight": 1, "sprite": 1668}], "bg": 1671}, {"id": "mon_plague_nymph", "fg": 1669, "bg": 1671}, {"id": "mon_squirrel", "fg": 1672, "bg": 1671}, {"id": "mon_giant_cockroach", "fg": 1673, "bg": 1670}, {"id": "mon_zombie_pig", "fg": 1674, "bg": 1670}, {"id": "mon_rabbit", "fg": 1675, "bg": 1671}, {"id": "mon_zombeaver", "fg": 1676, "bg": 1671}, {"id": "mon_ant", "fg": 1677, "bg": 1670}, {"id": "mon_ant_acid", "fg": 1682, "bg": 1670}, {"id": "corpse_mon_ant", "fg": 1684}, {"id": "corpse_mon_ant_acid", "fg": 1679}, {"id": "mon_ant_larva", "fg": 1678, "bg": 1671}, {"id": "mon_ant_acid_larva", "fg": 1680, "bg": 1671}, {"id": "corpse_mon_ant_larva", "fg": 1681}, {"id": "corpse_mon_ant_acid_larva", "fg": 1683}, {"id": "mon_centipede_giant", "fg": 1685, "bg": 1670}, {"id": "mon_cat", "fg": [{"weight": 1, "sprite": 1686}, {"weight": 1, "sprite": 1687}], "bg": 1671}, {"id": "mon_beaver", "fg": 1688, "bg": 1671}, {"id": "mon_wolf", "fg": 1689, "bg": 1670}, {"id": "mon_otter", "fg": 1690, "bg": 1671}, {"id": "mon_giant_cockroach_nymph", "fg": 1691, "bg": 1671}, {"id": "mon_duck", "fg": 1692, "bg": 1671}, {"id": "mon_mosquito", "fg": 1693, "bg": 1671}, {"id": "mon_locust", "fg": 1694, "bg": 1670}, {"id": "mon_pregnant_giant_cockroach", "fg": 1695, "bg": 1670}, {"id": "mon_dermatik_larva", "fg": 1696, "bg": 1671}, {"id": "mon_bear_cub", "fg": 1697, "bg": 1671}, {"id": "mon_wasp_small", "fg": 1698, "bg": 1671}, {"id": "fd_fire", "fg": 1699}], "//": "range 1 to 1712"}, {"file": "tall.png", "tiles": [{"id": "f_scrap_antenna", "animated": true, "fg": [{"weight": 15, "sprite": 1718}, {"weight": 15, "sprite": 1719}, {"weight": 15, "sprite": 1714}, {"weight": 15, "sprite": 1713}, {"weight": 15, "sprite": 1716}, {"weight": 15, "sprite": 1715}], "rotates": false}, {"id": "f_bookcase", "fg": 1720}, {"id": "f_arcade_machine", "fg": 1721}, {"id": "f_floor_lamp", "fg": 1722}, {"id": "f_dryer", "fg": 1723}, {"id": "f_dresser", "fg": 1724}, {"id": "f_crate_c", "fg": 1726}, {"id": "f_crate_o", "fg": 1725}, {"id": "f_alien_tendril", "fg": 1729}, {"id": "f_alien_zapper", "fg": 1730}, {"id": ["f_alien_pod", "f_alien_pod_organ"], "fg": 1727}, {"id": "f_alien_pod_resin", "fg": 1728}, {"id": "f_glass_cabinet", "fg": 1731}, {"id": "f_machinery_old", "fg": 1736}, {"id": "f_machinery_electronic", "fg": 1733}, {"id": "f_machinery_heavy", "fg": 1732}, {"id": "f_machinery_light", "fg": [{"weight": 100, "sprite": 1734}, {"weight": 100, "sprite": 1735}]}, {"id": "f_shower", "fg": 1737}, {"id": "f_console", "multitile": true, "fg": 1767, "additional_tiles": [{"id": "center", "fg": 1747}, {"id": "corner", "fg": [1763, 1764, 1756, 1741]}, {"id": "t_connection", "fg": [1743, 1761, 1766, 1765]}, {"id": "edge", "fg": [1762, 1744]}, {"id": "end_piece", "fg": [1760, 1752, 1738, 1749]}, {"id": "unconnected", "fg": 1767}]}, {"id": "f_console_broken", "multitile": true, "fg": 1758, "additional_tiles": [{"id": "center", "fg": 1754}, {"id": "corner", "fg": [1748, 1759, 1745, 1757]}, {"id": "t_connection", "fg": [1755, 1753, 1768, 1769]}, {"id": "edge", "fg": [1750, 1742]}, {"id": "end_piece", "fg": [1739, 1751, 1746, 1740]}, {"id": "unconnected", "fg": 1758}]}, {"id": "f_standing_tank", "fg": 1770}, {"id": "f_bigmirror", "fg": 1771}, {"id": "f_glass_fridge", "fg": 1772}, {"id": "f_cattails_season_spring", "fg": 1776, "rotates": false}, {"id": "f_cattails_season_summer", "fg": 1774, "rotates": false}, {"id": "f_cattails_season_autumn", "fg": 1775, "rotates": false}, {"id": "f_cattails_season_winter", "fg": 1773, "rotates": false}, {"id": "f_workbench", "fg": 1777}, {"id": "f_dumpster", "multitile": true, "fg": 1780, "additional_tiles": [{"id": "edge", "fg": [1781, 1784]}, {"id": "end_piece", "fg": [1778, 1782, 1783, 1779]}, {"id": "unconnected", "fg": 1780}]}, {"id": "f_washer", "fg": 1785}, {"id": "f_fireplace", "fg": 1786}, {"id": "f_home_furnace", "fg": 1787}, {"id": "f_locker", "fg": 1788}, {"id": "f_statue", "fg": 1789}, {"id": "f_woodstove", "fg": 1790}, {"id": "f_brazier", "fg": 1791}, {"id": "f_birdbath", "fg": 1792}, {"id": "f_rack_coat", "fg": 1793}, {"id": "f_water_heater", "fg": 1794}, {"id": "f_oven", "fg": 1795}, {"id": "player_male", "fg": 1930}, {"id": "npc_male", "fg": 1930}, {"id": "npc_female", "fg": 1931}, {"id": "player_female", "fg": 1931}, {"id": "t_gutter_downspout", "fg": 1797}, {"id": "t_utility_light", "fg": 1798}, {"id": "t_little_column", "fg": 1799, "bg": 1805}, {"id": "t_machinery_heavy", "fg": 1800, "bg": 1805}, {"id": "t_atm", "fg": 1801, "bg": 1805}, {"id": "t_column", "fg": 1802, "bg": 1805}, {"id": "t_machinery_electronic", "fg": 1809, "bg": 1805}, {"id": "t_machinery_old", "fg": 1810, "bg": 1808}, {"id": "t_stairs_up", "fg": 1811}, {"id": "t_wood_stairs_up", "fg": 1812}, {"id": "t_ladder_up", "fg": 1813}, {"id": "overlay_female_mutation_SCALES", "fg": 1814}, {"id": "overlay_male_mutation_SCALES", "fg": 1815}, {"id": "overlay_female_mutation_TAIL_THICK", "fg": 1816}, {"id": "overlay_male_mutation_TAIL_THICK", "fg": 1817}, {"id": "overlay_female_mutation_TALONS", "fg": 1819}, {"id": "overlay_male_mutation_TALONS", "fg": 1818}, {"id": "overlay_female_mutation_BEAK_PECK", "fg": 1820}, {"id": "overlay_male_mutation_BEAK_PECK", "fg": 1821}, {"id": "overlay_female_mutation_BIRD_EYE", "fg": 1822}, {"id": "overlay_male_mutation_BIRD_EYE", "fg": 1823}, {"id": "overlay_female_mutation_LIZ_EYE", "fg": 1824}, {"id": "overlay_male_mutation_LIZ_EYE", "fg": 1825}, {"id": "overlay_female_mutation_MUZZLE_LONG", "fg": 1827}, {"id": "overlay_male_mutation_MUZZLE_LONG", "fg": 1826}, {"id": "overlay_female_mutation_BEAK_HUM", "fg": 1828}, {"id": "overlay_male_mutation_BEAK_HUM", "fg": 1829}, {"id": "overlay_female_mutation_PLANTSKIN", "fg": 1830}, {"id": "overlay_male_mutation_PLANTSKIN", "fg": 1831}, {"id": "overlay_female_mutation_FANGS", "fg": 1833}, {"id": "overlay_male_mutation_FANGS", "fg": 1832}, {"id": "overlay_female_mutation_CHITIN", "fg": 1834}, {"id": "overlay_male_mutation_CHITIN", "fg": 1835}, {"id": "overlay_female_mutation_ARACHNID_ARMS", "fg": 1836}, {"id": "overlay_male_mutation_ARACHNID_ARMS", "fg": 1837}, {"id": "overlay_female_mutation_FEATHERS", "fg": 1838}, {"id": "overlay_male_mutation_FEATHERS", "fg": 1839}, {"id": "overlay_female_mutation_VINES1", "fg": 1840}, {"id": "overlay_male_mutation_VINES1", "fg": 1841}, {"id": "overlay_female_mutation_WINGS_BIRD", "fg": 1843}, {"id": "overlay_male_mutation_WINGS_BIRD", "fg": 1842}, {"id": "overlay_female_mutation_BEAK", "fg": 1845}, {"id": "overlay_male_mutation_BEAK", "fg": 1844}, {"id": "overlay_female_mutation_CHITIN2", "fg": 1846}, {"id": "overlay_male_mutation_CHITIN2", "fg": 1847}, {"id": "overlay_female_mutation_LEAVES", "fg": 1848}, {"id": "overlay_male_mutation_LEAVES", "fg": 1849}, {"id": "overlay_male_mutation_hair_brown_medium", "fg": 1850}, {"id": "overlay_female_mutation_hair_brown_medium", "fg": 1851}, {"id": "overlay_male_mutation_hair_brown_fro", "fg": 1852}, {"id": "overlay_female_mutation_hair_brown_fro", "fg": 1853}, {"id": "overlay_male_mutation_hair_red_mohawk", "fg": 1854}, {"id": "overlay_female_mutation_hair_red_mohawk", "fg": 1855}, {"id": "overlay_male_mutation_hair_brown_mohawk", "fg": 1857}, {"id": "overlay_female_mutation_hair_brown_mohawk", "fg": 1856}, {"id": "overlay_male_mutation_hair_brown_crewcut", "fg": 1859}, {"id": "overlay_female_mutation_hair_brown_crewcut", "fg": 1858}, {"id": "overlay_male_mutation_hair_black_crewcut", "fg": 1861}, {"id": "overlay_female_mutation_hair_black_crewcut", "fg": 1860}, {"id": "overlay_male_mutation_hair_gray_medium", "fg": 1863}, {"id": "overlay_female_mutation_hair_gray_medium", "fg": 1862}, {"id": "overlay_male_mutation_hair_white_crewcut", "fg": 1864}, {"id": "overlay_female_mutation_hair_white_crewcut", "fg": 1865}, {"id": "overlay_male_mutation_hair_red_medium", "fg": 1867}, {"id": "overlay_female_mutation_hair_red_medium", "fg": 1866}, {"id": "overlay_male_mutation_hair_blond_short", "fg": 1869}, {"id": "overlay_female_mutation_hair_blond_short", "fg": 1868}, {"id": "overlay_male_mutation_hair_white_mohawk", "fg": 1871}, {"id": "overlay_female_mutation_hair_white_mohawk", "fg": 1870}, {"id": "overlay_male_mutation_hair_red_crewcut", "fg": 1873}, {"id": "overlay_female_mutation_hair_red_crewcut", "fg": 1872}, {"id": "overlay_male_mutation_hair_red_fro", "fg": 1875}, {"id": "overlay_female_mutation_hair_red_fro", "fg": 1874}, {"id": "overlay_male_mutation_hair_white_medium", "fg": 1876}, {"id": "overlay_female_mutation_hair_white_medium", "fg": 1877}, {"id": "overlay_male_mutation_hair_black_medium", "fg": 1878}, {"id": "overlay_female_mutation_hair_black_medium", "fg": 1879}, {"id": "overlay_male_mutation_hair_blond_medium", "fg": 1881}, {"id": "overlay_female_mutation_hair_blond_medium", "fg": 1880}, {"id": "overlay_male_mutation_hair_black_fro", "fg": 1883}, {"id": "overlay_female_mutation_hair_black_fro", "fg": 1882}, {"id": "overlay_male_mutation_hair_black_mohawk", "fg": 1884}, {"id": "overlay_female_mutation_hair_black_mohawk", "fg": 1885}, {"id": "overlay_male_mutation_hair_gray_mohawk", "fg": 1886}, {"id": "overlay_female_mutation_hair_gray_mohawk", "fg": 1887}, {"id": "overlay_male_mutation_hair_brown_long", "fg": 1889}, {"id": "overlay_female_mutation_hair_brown_long", "fg": 1888}, {"id": "overlay_male_mutation_hair_gray_short", "fg": 1891}, {"id": "overlay_female_mutation_hair_gray_short", "fg": 1890}, {"id": "overlay_male_mutation_hair_white_short", "fg": 1892}, {"id": "overlay_female_mutation_hair_white_short", "fg": 1893}, {"id": "overlay_male_mutation_hair_white_long", "fg": 1894}, {"id": "overlay_female_mutation_hair_white_long", "fg": 1895}, {"id": "overlay_male_mutation_hair_gray_long", "fg": 1897}, {"id": "overlay_female_mutation_hair_gray_long", "fg": 1896}, {"id": "overlay_male_mutation_hair_brown_short", "fg": 1898}, {"id": "overlay_female_mutation_hair_brown_short", "fg": 1899}, {"id": "overlay_male_mutation_hair_gray_crewcut", "fg": 1901}, {"id": "overlay_female_mutation_hair_gray_crewcut", "fg": 1900}, {"id": "overlay_male_mutation_hair_blond_crewcut", "fg": 1903}, {"id": "overlay_female_mutation_hair_blond_crewcut", "fg": 1902}, {"id": "overlay_male_mutation_hair_black_short", "fg": 1904}, {"id": "overlay_female_mutation_hair_black_short", "fg": 1905}, {"id": "overlay_male_mutation_hair_blond_mohawk", "fg": 1907}, {"id": "overlay_female_mutation_hair_blond_mohawk", "fg": 1906}, {"id": "overlay_male_mutation_hair_black_long", "fg": 1909}, {"id": "overlay_female_mutation_hair_black_long", "fg": 1908}, {"id": "overlay_male_mutation_hair_blond_fro", "fg": 1910}, {"id": "overlay_female_mutation_hair_blond_fro", "fg": 1911}, {"id": "overlay_male_mutation_hair_blond_long", "fg": 1913}, {"id": "overlay_female_mutation_hair_blond_long", "fg": 1912}, {"id": "overlay_male_mutation_hair_gray_fro", "fg": 1915}, {"id": "overlay_female_mutation_hair_gray_fro", "fg": 1914}, {"id": "overlay_male_mutation_hair_red_short", "fg": 1917}, {"id": "overlay_female_mutation_hair_red_short", "fg": 1916}, {"id": "overlay_male_mutation_hair_red_long", "fg": 1919}, {"id": "overlay_female_mutation_hair_red_long", "fg": 1918}, {"id": "overlay_male_mutation_hair_white_fro", "fg": 1920}, {"id": "overlay_female_mutation_hair_white_fro", "fg": 1921}, {"id": "overlay_female_mutation_SKIN_LIGHT", "fg": 1923, "bg": 1796}, {"id": "overlay_male_mutation_SKIN_LIGHT", "fg": 1922, "bg": 1796}, {"id": "overlay_female_mutation_SKIN_DARK", "fg": 1925, "bg": 1796}, {"id": "overlay_male_mutation_SKIN_DARK", "fg": 1924, "bg": 1796}, {"id": "overlay_female_mutation_SKIN_TAN", "fg": 1927, "bg": 1796}, {"id": "overlay_male_mutation_SKIN_TAN", "fg": 1926, "bg": 1796}, {"id": "overlay_male_mutation_SKIN_PINK", "fg": 1929, "bg": 1796}, {"id": "overlay_female_mutation_SKIN_PINK", "fg": 1928, "bg": 1796}, {"id": "overlay_male_mutation_SKIN_MEDIUM", "fg": 1930, "bg": 1796}, {"id": "overlay_female_mutation_SKIN_MEDIUM", "fg": 1931, "bg": 1796}, {"id": "overlay_wielded_bag_canvas", "fg": 1932}, {"id": "overlay_female_wielded_wood_panel", "fg": 1934}, {"id": "overlay_male_wielded_wood_panel", "fg": 1933}, {"id": "overlay_wielded_jar_glass", "fg": 1935}, {"id": "overlay_female_wielded_rifle_flintlock", "fg": 1937}, {"id": "overlay_male_wielded_rifle_flintlock", "fg": 1936}, {"id": "overlay_female_wielded_bat_metal", "fg": 1939}, {"id": "overlay_male_wielded_bat_metal", "fg": 1938}, {"id": "overlay_male_wielded_pointy_stick", "fg": 1940}, {"id": "overlay_female_wielded_pointy_stick", "fg": 1945}, {"id": "overlay_male_wielded_spear_wood", "fg": 1955}, {"id": "overlay_female_wielded_spear_wood", "fg": 1954}, {"id": "overlay_male_wielded_spear_spike", "fg": 1949}, {"id": "overlay_female_wielded_spear_spike", "fg": 1956}, {"id": "overlay_male_wielded_spear_knife", "fg": 1952}, {"id": "overlay_female_wielded_spear_knife", "fg": 1953}, {"id": "overlay_male_wielded_spear_knife_superior", "fg": 1942}, {"id": "overlay_female_wielded_spear_knife_superior", "fg": 1957}, {"id": "overlay_male_wielded_spear_rebar", "fg": 1947}, {"id": "overlay_female_wielded_spear_rebar", "fg": 1951}, {"id": "overlay_male_wielded_spear_pipe", "fg": 1948}, {"id": "overlay_female_wielded_spear_pipe", "fg": 1943}, {"id": "overlay_male_wielded_spear_steel", "fg": 1944}, {"id": "overlay_female_wielded_spear_steel", "fg": 1941}, {"id": "overlay_male_wielded_spear_copper", "fg": 1946}, {"id": "overlay_female_wielded_spear_copper", "fg": 1950}, {"id": "overlay_wielded_flashlight", "fg": 1959}, {"id": "overlay_wielded_heavy_flashlight", "fg": 1958}, {"id": "overlay_wielded_jug_plastic", "fg": 1960}, {"id": "overlay_female_wielded_pot", "fg": 1962}, {"id": "overlay_male_wielded_pot", "fg": 1961}, {"id": "overlay_wielded_fire_ax", "fg": 1963}, {"id": "overlay_wielded_ax", "fg": 1964}, {"id": "overlay_wielded_hatchet", "fg": 1965}, {"id": "overlay_wielded_bottle_glass", "fg": 1966}, {"id": "overlay_female_wielded_rag", "fg": 1967}, {"id": "overlay_male_wielded_rag", "fg": 1968}, {"id": "overlay_female_wielded_corpse_generic_human", "fg": 1969}, {"id": "overlay_male_wielded_corpse_generic_human", "fg": 1970}, {"id": "overlay_male_wielded_chainsaw_off", "fg": 1971}, {"id": "overlay_female_wielded_chainsaw_off", "fg": 1972}, {"id": "overlay_female_wielded_teapot", "fg": 1974}, {"id": "overlay_male_wielded_teapot", "fg": 1973}, {"id": "overlay_male_wielded_1st_aid", "fg": 1975}, {"id": "overlay_female_wielded_1st_aid", "fg": 1976}, {"id": "overlay_female_wielded_nailbat", "fg": 1977}, {"id": "overlay_male_wielded_nailbat", "fg": 1978}, {"id": "overlay_male_wielded_primitive_hammer", "fg": 1979}, {"id": "overlay_female_wielded_primitive_hammer", "fg": 1980}, {"id": "overlay_wielded_steel_lump", "fg": 1981}, {"id": "overlay_wielded_antibiotics", "fg": 1982}, {"id": "overlay_wielded_weak_antibiotic", "fg": 1994}, {"id": "overlay_wielded_strong_antibiotic", "fg": 1989}, {"id": "overlay_wielded_calcium_tablet", "fg": 1984}, {"id": "overlay_wielded_vitamins", "fg": 1992}, {"id": "overlay_wielded_gummy_vitamins", "fg": 1983}, {"id": "overlay_wielded_antifungal", "fg": 1990}, {"id": "overlay_wielded_antiparasitic", "fg": 1987}, {"id": "overlay_wielded_iodine", "fg": 1993}, {"id": "overlay_wielded_prussian_blue", "fg": 1991}, {"id": "overlay_wielded_tramadol", "fg": 1986}, {"id": "overlay_wielded_codeine", "fg": 1985}, {"id": "overlay_wielded_oxycodone", "fg": 1988}, {"id": "overlay_wielded_rebar", "fg": 1995}, {"id": "overlay_wielded_steel_glass_shard", "fg": 1996}, {"id": "overlay_male_wielded_pot_copper", "fg": 1998}, {"id": "overlay_female_wielded_pot_copper", "fg": 1997}, {"id": "overlay_female_wielded_nailboard", "fg": 1999}, {"id": "overlay_male_wielded_nailboard", "fg": 2000}, {"id": "overlay_wielded_scissors", "fg": 2001}, {"id": "overlay_female_wielded_ak74", "fg": 2003}, {"id": "overlay_male_wielded_ak74", "fg": 2002}, {"id": "overlay_wielded_coffeemaker", "fg": 2004}, {"id": "overlay_male_wielded_thermos", "fg": 2006}, {"id": "overlay_female_wielded_thermos", "fg": 2005}, {"id": "overlay_female_wielded_stick", "fg": 2007}, {"id": "overlay_male_wielded_stick", "fg": 2008}, {"id": "overlay_wielded_steel_chunk", "fg": 2009}, {"id": "overlay_wielded_pipe", "fg": 2010}, {"id": "overlay_wielded_bottle_plastic", "fg": 2011}, {"id": "overlay_wielded_broom", "fg": 2012}, {"id": "overlay_wielded_sw_619", "fg": 2013}, {"id": "overlay_wielded_rolling_pin", "fg": 2014}, {"id": "overlay_wielded_makeshift_crowbar", "fg": 2015}, {"id": "overlay_female_wielded_bat", "fg": 2017}, {"id": "overlay_male_wielded_bat", "fg": 2016}, {"id": "overlay_male_wielded_wood_sheet", "fg": 2018}, {"id": "overlay_female_wielded_wood_sheet", "fg": 2019}, {"id": "overlay_wielded_jar_3l_glass_sealed", "fg": 2020}, {"id": "overlay_wielded_box_large", "fg": 2021}, {"id": "overlay_wielded_crowbar", "fg": 2025}, {"id": "overlay_male_wielded_screwdriver", "fg": 2026}, {"id": "overlay_female_wielded_screwdriver", "fg": 2022}, {"id": "overlay_wielded_shovel", "fg": 2027}, {"id": "overlay_wielded_wrench", "fg": 2023}, {"id": "overlay_wielded_hammer", "fg": 2024}, {"id": "overlay_wielded_arming_sword", "fg": 2028}, {"id": "overlay_wielded_brick", "fg": 2029}, {"id": "overlay_male_wielded_2x4", "fg": 2030}, {"id": "overlay_female_wielded_2x4", "fg": 2031}, {"id": "overlay_wielded_katana", "fg": 2032}, {"id": "overlay_wielded_bottle_plastic_small", "fg": 2033}, {"id": "overlay_wielded_sharp_rock", "fg": 2034}, {"id": ["overlay_wielded_glock_17", "overlay_wielded_glock_19", "overlay_wielded_glock_18c", "overlay_wielded_glock_22", "overlay_wielded_glock_31"], "fg": 2035}, {"id": "overlay_wielded_box_small", "fg": 2036}, {"id": "overlay_male_wielded_pillow", "fg": 2038}, {"id": "overlay_female_wielded_pillow", "fg": 2039}, {"id": "overlay_male_wielded_down_pillow", "fg": 2040}, {"id": "overlay_female_wielded_down_pillow", "fg": 2037}, {"id": "overlay_wielded_mop", "fg": 2041}, {"id": ["overlay_wielded_corpse_mon_ant", "overlay_wielded_corpse_mon_ant_soldier", "overlay_wielded_corpse_mon_ant_queen"], "fg": 2043}, {"id": ["overlay_wielded_corpse_mon_ant_acid", "overlay_wielded_corpse_mon_ant_acid_soldier", "overlay_wielded_corpse_mon_ant_acid_queen"], "fg": 2045}, {"id": ["overlay_male_wielded_corpse_mon_zombie", "overlay_male_wielded_corpse_mon_zombie_grappler", "overlay_male_wielded_corpse_mon_zombie_biter", "overlay_male_wielded_corpse_mon_zombie_tough", "overlay_male_wielded_corpse_mon_zombie_hunter", "overlay_male_wielded_corpse_mon_zombie_brute", "overlay_male_wielded_corpse_mon_zombie_predator", "overlay_male_wielded_corpse_mon_zombie_necro", "overlay_male_wielded_corpse_mon_zombie_thorny", "overlay_male_wielded_corpse_mon_zombie_smoker", "overlay_male_wielded_corpse_mon_zombie_shady", "overlay_male_wielded_corpse_mon_zombie_master", "overlay_male_wielded_corpse_mon_zombie_acidic", "overlay_male_wielded_corpse_mon_zombie_fat", "overlay_male_wielded_corpse_mon_zombie_corrosive", "overlay_male_wielded_corpse_mon_zombie_screecher", "overlay_male_wielded_corpse_mon_zombie_scientist", "overlay_male_wielded_corpse_mon_zombie_runner", "overlay_male_wielded_corpse_mon_zombie_spitter", "overlay_male_wielded_corpse_mon_zombie_labsecurity", "overlay_male_wielded_corpse_mon_zombie_child", "overlay_male_wielded_corpse_mon_zombie_hulk", "overlay_male_wielded_corpse_mon_zombie_brute_grappler", "overlay_male_wielded_corpse_mon_zombie_brute_ninja", "overlay_male_wielded_corpse_mon_zombie_kevlar_2", "overlay_male_wielded_corpse_mon_zombie_kevlar_1", "overlay_male_wielded_corpse_mon_zombie_hazmat", "overlay_male_wielded_corpse_mon_zombie_electric", "overlay_male_wielded_corpse_mon_zombie_technician", "overlay_male_wielded_corpse_mon_zombie_fungus", "overlay_male_wielded_corpse_mon_zombie_cop", "overlay_male_wielded_corpse_mon_zombie_child_fungus", "overlay_male_wielded_corpse_mon_zombie_swimmer", "overlay_male_wielded_corpse_mon_zombie_mancroc", "overlay_male_wielded_corpse_mon_zombie_soldier", "overlay_male_wielded_corpse_mon_zombie_skull", "overlay_male_wielded_corpse_mon_zombie_brainless", "overlay_male_wielded_corpse_mon_zombie_survivor", "overlay_male_wielded_corpse_mon_zombie_brute_shocker", "overlay_male_wielded_corpse_mon_zombie_soldier_acid_1", "overlay_male_wielded_corpse_mon_zombie_soldier_blackops_2", "overlay_male_wielded_corpse_mon_zombie_soldier_blackops_1", "overlay_male_wielded_corpse_mon_zombie_soldier_acid_2", "overlay_male_wielded_corpse_mon_zombie_shriekling", "overlay_male_wielded_corpse_mon_zombie_ears", "overlay_male_wielded_corpse_mon_zombie_nullfield", "overlay_male_wielded_corpse_mon_zombie_waif", "overlay_male_wielded_corpse_mon_zombie_sproglodyte", "overlay_male_wielded_corpse_mon_zombie_creepy", "overlay_male_wielded_corpse_mon_zombie_anklebiter", "overlay_male_wielded_corpse_mon_zombie_bio_op", "overlay_male_wielded_corpse_mon_zombie_armored", "overlay_male_wielded_corpse_mon_zombie_prisoner", "overlay_male_wielded_corpse_mon_zombie_military_pilot"], "fg": 2042}, {"id": ["overlay_female_wielded_corpse_mon_zombie", "overlay_female_wielded_corpse_mon_zombie_grappler", "overlay_female_wielded_corpse_mon_zombie_biter", "overlay_female_wielded_corpse_mon_zombie_tough", "overlay_female_wielded_corpse_mon_zombie_hunter", "overlay_female_wielded_corpse_mon_zombie_brute", "overlay_female_wielded_corpse_mon_zombie_predator", "overlay_female_wielded_corpse_mon_zombie_necro", "overlay_female_wielded_corpse_mon_zombie_thorny", "overlay_female_wielded_corpse_mon_zombie_smoker", "overlay_female_wielded_corpse_mon_zombie_shady", "overlay_female_wielded_corpse_mon_zombie_master", "overlay_female_wielded_corpse_mon_zombie_acidic", "overlay_female_wielded_corpse_mon_zombie_fat", "overlay_female_wielded_corpse_mon_zombie_corrosive", "overlay_female_wielded_corpse_mon_zombie_screecher", "overlay_female_wielded_corpse_mon_zombie_scientist", "overlay_female_wielded_corpse_mon_zombie_runner", "overlay_female_wielded_corpse_mon_zombie_spitter", "overlay_female_wielded_corpse_mon_zombie_labsecurity", "overlay_female_wielded_corpse_mon_zombie_child", "overlay_female_wielded_corpse_mon_zombie_hulk", "overlay_female_wielded_corpse_mon_zombie_brute_grappler", "overlay_female_wielded_corpse_mon_zombie_brute_ninja", "overlay_female_wielded_corpse_mon_zombie_kevlar_2", "overlay_female_wielded_corpse_mon_zombie_kevlar_1", "overlay_female_wielded_corpse_mon_zombie_hazmat", "overlay_female_wielded_corpse_mon_zombie_electric", "overlay_female_wielded_corpse_mon_zombie_technician", "overlay_female_wielded_corpse_mon_zombie_fungus", "overlay_female_wielded_corpse_mon_zombie_cop", "overlay_female_wielded_corpse_mon_zombie_child_fungus", "overlay_female_wielded_corpse_mon_zombie_swimmer", "overlay_female_wielded_corpse_mon_zombie_mancroc", "overlay_female_wielded_corpse_mon_zombie_soldier", "overlay_female_wielded_corpse_mon_zombie_skull", "overlay_female_wielded_corpse_mon_zombie_brainless", "overlay_female_wielded_corpse_mon_zombie_survivor", "overlay_female_wielded_corpse_mon_zombie_brute_shocker", "overlay_female_wielded_corpse_mon_zombie_soldier_acid_1", "overlay_female_wielded_corpse_mon_zombie_soldier_blackops_2", "overlay_female_wielded_corpse_mon_zombie_soldier_blackops_1", "overlay_female_wielded_corpse_mon_zombie_soldier_acid_2", "overlay_female_wielded_corpse_mon_zombie_shriekling", "overlay_female_wielded_corpse_mon_zombie_ears", "overlay_female_wielded_corpse_mon_zombie_nullfield", "overlay_female_wielded_corpse_mon_zombie_waif", "overlay_female_wielded_corpse_mon_zombie_sproglodyte", "overlay_female_wielded_corpse_mon_zombie_creepy", "overlay_female_wielded_corpse_mon_zombie_anklebiter", "overlay_female_wielded_corpse_mon_zombie_bio_op", "overlay_female_wielded_corpse_mon_zombie_armored", "overlay_female_wielded_corpse_mon_zombie_prisoner", "overlay_female_wielded_corpse_mon_zombie_military_pilot"], "fg": 2044}, {"id": "overlay_wielded_box_medium", "fg": 2046}, {"id": "overlay_wielded_rock", "fg": 2047}, {"id": "overlay_female_wielded_stick_long", "fg": 2048}, {"id": "overlay_male_wielded_stick_long", "fg": 2049}, {"id": "overlay_wielded_log", "fg": 2050}, {"id": "overlay_female_wielded_television", "fg": 2052}, {"id": "overlay_male_wielded_television", "fg": 2051}, {"id": "overlay_wielded_jar_glass_sealed", "fg": 2053}, {"id": "overlay_wielded_jar_3l_glass", "fg": 2054}, {"id": "overlay_female_wielded_bwirebat", "fg": 2055}, {"id": "overlay_male_wielded_bwirebat", "fg": 2056}, {"id": "overlay_wielded_bag_plastic", "fg": 2057}, {"id": "overlay_female_wielded_splinter", "fg": 2059}, {"id": "overlay_male_wielded_splinter", "fg": 2058}, {"id": "overlay_wielded_ar15", "fg": 2060}, {"id": "overlay_female_worn_pants_army", "fg": 2062}, {"id": "overlay_male_worn_pants_army", "fg": 2061}, {"id": "overlay_female_worn_hat_noise_cancelling", "fg": 2064}, {"id": "overlay_male_worn_hat_noise_cancelling", "fg": 2063}, {"id": "overlay_female_worn_striped_pants", "fg": 2065}, {"id": "overlay_male_worn_striped_pants", "fg": 2066}, {"id": "overlay_female_worn_motorbike_pants", "fg": 2068}, {"id": "overlay_male_worn_motorbike_pants", "fg": 2067}, {"id": "overlay_female_worn_beekeeping_hood", "fg": 2069}, {"id": "overlay_male_worn_beekeeping_hood", "fg": 2070}, {"id": "overlay_male_worn_ski_jacket", "fg": 2072}, {"id": "overlay_female_worn_ski_jacket", "fg": 2071}, {"id": "overlay_female_worn_boots_bunker", "fg": 2073}, {"id": "overlay_male_worn_boots_bunker", "fg": 2074}, {"id": "overlay_male_worn_leg_warmers", "fg": 2076}, {"id": "overlay_female_worn_leg_warmers", "fg": 2075}, {"id": "overlay_worn_dress_shoes", "fg": 2077}, {"id": "overlay_male_worn_glasses_bifocal", "fg": 2079}, {"id": "overlay_female_worn_glasses_bifocal", "fg": 2078}, {"id": "overlay_female_worn_glasses_safety", "fg": 2081}, {"id": "overlay_male_worn_glasses_safety", "fg": 2080}, {"id": "overlay_female_worn_ragpouch", "fg": 2082}, {"id": "overlay_male_worn_ragpouch", "fg": 2083}, {"id": "overlay_female_worn_welding_mask_crude_raised", "fg": 2084}, {"id": "overlay_male_worn_welding_mask_crude_raised", "fg": 2085}, {"id": "overlay_male_worn_gloves_leather", "fg": 2087}, {"id": "overlay_female_worn_gloves_leather", "fg": 2086}, {"id": "overlay_female_worn_helmet_army", "fg": 2088}, {"id": "overlay_male_worn_helmet_army", "fg": 2089}, {"id": "overlay_female_worn_pants_faux_fur", "fg": 2090}, {"id": "overlay_male_worn_pants_faux_fur", "fg": 2091}, {"id": "overlay_male_worn_long_glove_white", "fg": 2093}, {"id": "overlay_female_worn_long_glove_white", "fg": 2092}, {"id": "overlay_female_worn_jacket_army", "fg": 2094}, {"id": "overlay_male_worn_jacket_army", "fg": 2095}, {"id": "overlay_female_worn_pants_cargo", "fg": 2096}, {"id": "overlay_male_worn_pants_cargo", "fg": 2097}, {"id": "overlay_female_worn_gloves_work", "fg": 2098}, {"id": "overlay_male_worn_gloves_work", "fg": 2099}, {"id": "overlay_female_worn_geta", "fg": 2100}, {"id": "overlay_male_worn_geta", "fg": 2101}, {"id": "overlay_male_worn_hat_fur", "fg": 2102}, {"id": "overlay_female_worn_hat_fur", "fg": 2103}, {"id": "overlay_female_worn_gloves_wool", "fg": 2105}, {"id": "overlay_male_worn_gloves_wool", "fg": 2104}, {"id": "overlay_male_worn_arm_warmers", "fg": 2106}, {"id": "overlay_female_worn_arm_warmers", "fg": 2107}, {"id": "overlay_female_worn_beret", "fg": 2109}, {"id": "overlay_male_worn_beret", "fg": 2108}, {"id": "overlay_female_worn_boxer_briefs", "fg": 2110}, {"id": "overlay_male_worn_boxer_briefs", "fg": 2111}, {"id": "overlay_male_worn_touring_suit", "fg": 2113}, {"id": "overlay_female_worn_touring_suit", "fg": 2112}, {"id": "overlay_female_worn_pants_checkered", "fg": 2115}, {"id": "overlay_male_worn_pants_checkered", "fg": 2114}, {"id": "overlay_male_worn_turban", "fg": 2116}, {"id": "overlay_female_worn_turban", "fg": 2117}, {"id": "overlay_female_worn_pants_ski", "fg": 2119}, {"id": "overlay_male_worn_pants_ski", "fg": 2118}, {"id": "overlay_female_worn_glasses_bal", "fg": 2120}, {"id": "overlay_male_worn_glasses_bal", "fg": 2121}, {"id": "overlay_male_worn_boots", "fg": 2122}, {"id": "overlay_female_worn_boots", "fg": 2123}, {"id": "overlay_female_worn_corset", "fg": 2125}, {"id": "overlay_male_worn_corset", "fg": 2124}, {"id": "overlay_female_worn_welding_mask", "fg": 2126}, {"id": "overlay_male_worn_welding_mask", "fg": 2127}, {"id": "overlay_female_worn_jacket_flannel", "fg": 2128}, {"id": "overlay_male_worn_jacket_flannel", "fg": 2129}, {"id": "overlay_male_worn_shorts", "fg": 2131}, {"id": "overlay_female_worn_shorts", "fg": 2130}, {"id": "overlay_female_worn_dinosuit", "fg": 2133}, {"id": "overlay_male_worn_dinosuit", "fg": 2132}, {"id": "overlay_male_worn_undershirt", "fg": 2134}, {"id": "overlay_female_worn_boots_combat", "fg": 2135}, {"id": "overlay_male_worn_boots_combat", "fg": 2136}, {"id": "overlay_female_worn_stockings", "fg": 2137}, {"id": "overlay_male_worn_stockings", "fg": 2138}, {"id": "overlay_male_worn_skirt_leather", "fg": 2140}, {"id": "overlay_female_worn_skirt_leather", "fg": 2139}, {"id": "overlay_female_worn_boots_rubber", "fg": 2142}, {"id": "overlay_male_worn_boots_rubber", "fg": 2141}, {"id": "overlay_female_worn_dress_shirt", "fg": 2143}, {"id": "overlay_male_worn_dress_shirt", "fg": 2144}, {"id": "overlay_female_worn_gloves_golf", "fg": 2146}, {"id": "overlay_male_worn_gloves_golf", "fg": 2145}, {"id": "overlay_female_worn_army_top", "fg": 2147}, {"id": "overlay_male_worn_army_top", "fg": 2148}, {"id": "overlay_female_worn_boxer_shorts", "fg": 2150}, {"id": "overlay_male_worn_boxer_shorts", "fg": 2149}, {"id": "overlay_female_worn_helmet_kabuto", "fg": 2151}, {"id": "overlay_male_worn_helmet_kabuto", "fg": 2152}, {"id": "overlay_male_worn_shoes_bowling", "fg": 2153}, {"id": "overlay_female_worn_shoes_bowling", "fg": 2154}, {"id": "overlay_female_worn_gloves_medical", "fg": 2155}, {"id": "overlay_male_worn_gloves_medical", "fg": 2156}, {"id": "overlay_worn_jacket_jean", "fg": 2157}, {"id": "overlay_female_worn_boots_fur", "fg": 2158}, {"id": "overlay_male_worn_boots_fur", "fg": 2159}, {"id": "overlay_male_worn_blanket", "fg": 2162}, {"id": "overlay_female_worn_blanket", "fg": 2161}, {"id": "overlay_male_worn_down_blanket", "fg": 2160}, {"id": "overlay_female_worn_down_blanket", "fg": 2163}, {"id": "overlay_male_worn_cargo_pants", "fg": 2164}, {"id": "overlay_female_worn_panties", "fg": 2165}, {"id": "overlay_male_worn_panties", "fg": 2166}, {"id": "overlay_female_worn_hat_hard", "fg": 2167}, {"id": "overlay_male_worn_hat_hard", "fg": 2168}, {"id": "overlay_male_worn_glasses_eye", "fg": 2170}, {"id": "overlay_female_worn_glasses_eye", "fg": 2169}, {"id": "overlay_female_worn_helmet_chitin", "fg": 2172}, {"id": "overlay_male_worn_helmet_chitin", "fg": 2171}, {"id": "overlay_female_worn_boy_shorts", "fg": 2173}, {"id": "overlay_male_worn_boy_shorts", "fg": 2174}, {"id": "overlay_male_worn_sweatshirt", "fg": 2175}, {"id": "overlay_female_worn_sweatshirt", "fg": 2176}, {"id": "overlay_male_worn_rucksack", "fg": 2178}, {"id": "overlay_female_worn_rucksack", "fg": 2177}, {"id": "overlay_female_worn_sunglasses", "fg": 2180}, {"id": "overlay_male_worn_sunglasses", "fg": 2179}, {"id": "overlay_female_worn_mask_dust", "fg": 2181}, {"id": "overlay_male_worn_mask_dust", "fg": 2182}, {"id": "overlay_male_worn_helmet_motor", "fg": 2184}, {"id": "overlay_female_worn_helmet_motor", "fg": 2183}, {"id": "overlay_female_worn_sweater", "fg": 2185}, {"id": "overlay_male_worn_sweater", "fg": 2186}, {"id": "overlay_female_worn_jacket_light", "fg": 2187}, {"id": "overlay_male_worn_jacket_light", "fg": 2188}, {"id": "overlay_male_worn_skirt", "fg": 2190}, {"id": "overlay_female_worn_skirt", "fg": 2189}, {"id": "overlay_female_worn_striped_shirt", "fg": 2192}, {"id": "overlay_male_worn_striped_shirt", "fg": 2191}, {"id": "overlay_female_worn_hat_ball", "fg": 2194}, {"id": "overlay_male_worn_hat_ball", "fg": 2193}, {"id": "overlay_male_worn_bra", "fg": 2196}, {"id": "overlay_female_worn_bra", "fg": 2195}, {"id": "overlay_male_worn_glove_jackson", "fg": 2198}, {"id": "overlay_female_worn_glove_jackson", "fg": 2197}, {"id": "overlay_male_worn_boots_western", "fg": 2199}, {"id": "overlay_female_worn_boots_western", "fg": 2200}, {"id": "overlay_female_worn_hazmat_suit", "fg": 2201}, {"id": "overlay_male_worn_hazmat_suit", "fg": 2202}, {"id": "overlay_female_worn_scarf", "fg": 2204}, {"id": "overlay_male_worn_scarf", "fg": 2203}, {"id": "overlay_male_worn_kevlar", "fg": 2206}, {"id": "overlay_female_worn_kevlar", "fg": 2205}, {"id": "overlay_female_worn_backpack_leather", "fg": 2207}, {"id": "overlay_male_worn_backpack_leather", "fg": 2208}, {"id": "overlay_female_worn_armor_samurai", "fg": 2210}, {"id": "overlay_male_worn_armor_samurai", "fg": 2209}, {"id": "overlay_female_worn_jumpsuit", "fg": 2212}, {"id": "overlay_male_worn_jumpsuit", "fg": 2211}, {"id": "overlay_female_worn_jeans", "fg": 2213}, {"id": "overlay_male_worn_jeans", "fg": 2214}, {"id": "overlay_female_worn_boots_winter", "fg": 2216}, {"id": "overlay_male_worn_boots_winter", "fg": 2215}, {"id": "overlay_female_worn_shorts_denim", "fg": 2217}, {"id": "overlay_male_worn_shorts_denim", "fg": 2218}, {"id": "overlay_female_worn_gloves_rubber", "fg": 2220}, {"id": "overlay_male_worn_gloves_rubber", "fg": 2219}, {"id": "overlay_male_worn_hat_cotton", "fg": 2221}, {"id": "overlay_female_worn_hat_cotton", "fg": 2222}, {"id": "overlay_male_worn_robofac_jumpsuit", "fg": 2224}, {"id": "overlay_female_worn_robofac_jumpsuit", "fg": 2223}, {"id": "overlay_female_worn_gloves_liner", "fg": 2225}, {"id": "overlay_male_worn_gloves_liner", "fg": 2226}, {"id": "overlay_female_worn_shorts_cargo", "fg": 2228}, {"id": "overlay_male_worn_shorts_cargo", "fg": 2227}, {"id": "overlay_female_worn_coat_lab", "fg": 2230}, {"id": "overlay_male_worn_coat_lab", "fg": 2229}, {"id": "overlay_male_worn_tank_top", "fg": 2232}, {"id": "overlay_female_worn_tank_top", "fg": 2231}, {"id": "overlay_male_worn_socks", "fg": 2234}, {"id": "overlay_female_worn_socks", "fg": 2233}, {"id": "overlay_female_worn_sports_bra", "fg": 2235}, {"id": "overlay_male_worn_sports_bra", "fg": 2236}, {"id": "overlay_male_worn_bunker_coat", "fg": 2238}, {"id": "overlay_female_worn_bunker_coat", "fg": 2237}, {"id": "overlay_female_worn_gloves_winter", "fg": 2239}, {"id": "overlay_male_worn_gloves_winter", "fg": 2240}, {"id": "overlay_female_worn_mittens", "fg": 2242}, {"id": "overlay_male_worn_mittens", "fg": 2241}, {"id": "overlay_female_worn_glasses_reading", "fg": 2244}, {"id": "overlay_male_worn_glasses_reading", "fg": 2243}, {"id": "overlay_female_worn_longshirt", "fg": 2246}, {"id": "overlay_male_worn_longshirt", "fg": 2245}, {"id": "overlay_female_worn_gloves_cut_resistant", "fg": 2247}, {"id": "overlay_male_worn_gloves_cut_resistant", "fg": 2248}, {"id": "overlay_female_worn_pants", "fg": 2250}, {"id": "overlay_male_worn_pants", "fg": 2249}, {"id": "overlay_female_worn_maid_hat", "fg": 2253}, {"id": "overlay_female_worn_maid_dress", "fg": 2252}, {"id": "overlay_male_worn_maid_hat", "fg": 2251}, {"id": "overlay_male_worn_maid_dress", "fg": 2254}, {"id": "overlay_male_worn_bunker_pants", "fg": 2255}, {"id": "overlay_female_worn_bunker_pants", "fg": 2256}, {"id": "overlay_female_worn_helmet_bike", "fg": 2257}, {"id": "overlay_male_worn_helmet_bike", "fg": 2258}, {"id": "overlay_female_worn_pants_leather", "fg": 2259}, {"id": "overlay_male_worn_pants_leather", "fg": 2260}, {"id": "overlay_male_worn_boots_steel", "fg": 2262}, {"id": "overlay_female_worn_boots_steel", "fg": 2261}, {"id": "overlay_female_worn_survivor_suit", "fg": 2264}, {"id": "overlay_male_worn_survivor_suit", "fg": 2263}, {"id": "overlay_female_worn_pants_fur", "fg": 2265}, {"id": "overlay_male_worn_pants_fur", "fg": 2266}, {"id": "overlay_female_worn_gloves_fingerless", "fg": 2268}, {"id": "overlay_male_worn_gloves_fingerless", "fg": 2267}, {"id": "overlay_female_worn_union_suit", "fg": 2269}, {"id": "overlay_male_worn_union_suit", "fg": 2270}, {"id": "overlay_female_worn_sneakers", "fg": 2271}, {"id": "overlay_male_worn_sneakers", "fg": 2272}, {"id": "overlay_male_worn_flip_flops", "fg": 2274}, {"id": "overlay_female_worn_flip_flops", "fg": 2273}, {"id": "overlay_male_worn_tshirt", "fg": 2276}, {"id": "overlay_female_worn_tshirt", "fg": 2275}, {"id": "overlay_female_worn_boots_hiking", "fg": 2277}, {"id": "overlay_male_worn_boots_hiking", "fg": 2278}, {"id": "overlay_female_worn_gloves_fur", "fg": 2280}, {"id": "overlay_male_worn_gloves_fur", "fg": 2279}, {"id": "overlay_male_worn_firehelmet", "fg": 2282}, {"id": "overlay_female_worn_firehelmet", "fg": 2281}, {"id": "overlay_female_worn_welding_mask_raised", "fg": 2284}, {"id": "overlay_male_worn_welding_mask_raised", "fg": 2283}, {"id": "overlay_female_worn_bandana", "fg": 2285}, {"id": "overlay_male_worn_bandana", "fg": 2286}, {"id": "overlay_male_worn_hat_knit", "fg": 2288}, {"id": "overlay_female_worn_hat_knit", "fg": 2287}, {"id": "overlay_female_worn_hoodie", "fg": 2290}, {"id": "overlay_male_worn_hoodie", "fg": 2289}, {"id": "overlay_male_worn_coat_winter", "fg": 2291}, {"id": "overlay_female_worn_coat_winter", "fg": 2292}, {"id": "overlay_female_worn_duster", "fg": 2293}, {"id": "overlay_male_worn_duster", "fg": 2294}, {"id": "overlay_female_worn_glasses_monocle", "fg": 2295}, {"id": "overlay_male_worn_glasses_monocle", "fg": 2296}, {"id": "overlay_male_worn_welding_mask_crude", "fg": 2298}, {"id": "overlay_female_worn_welding_mask_crude", "fg": 2297}, {"id": "overlay_male_worn_balclava", "fg": 2300}, {"id": "overlay_female_worn_balclava", "fg": 2299}, {"id": "overlay_female_worn_motorbike_boots", "fg": 2302}, {"id": "overlay_male_worn_motorbike_boots", "fg": 2301}, {"id": "overlay_female_worn_helmet_barbute", "fg": 2303}, {"id": "overlay_male_worn_helmet_barbute", "fg": 2304}, {"id": "overlay_female_worn_gloves_tactical", "fg": 2306}, {"id": "overlay_male_worn_gloves_tactical", "fg": 2305}, {"id": "overlay_female_worn_jeans_red", "fg": 2308}, {"id": "overlay_male_worn_jeans_red", "fg": 2307}, {"id": "overlay_female_worn_cowboy_hat", "fg": 2309}, {"id": "overlay_male_worn_cowboy_hat", "fg": 2310}, {"id": "overlay_male_worn_sundress", "fg": 2312}, {"id": "overlay_female_worn_sundress", "fg": 2311}, {"id": "overlay_female_worn_coat_rain", "fg": 2313}, {"id": "overlay_male_worn_coat_rain", "fg": 2314}, {"id": "overlay_male_worn_gloves_light", "fg": 2316}, {"id": "overlay_female_worn_gloves_light", "fg": 2315}, {"id": "mon_skeleton", "fg": 2317, "bg": 1796}, {"id": "mon_dermatik", "fg": 2318, "bg": 1796}, {"id": "mon_exodii_worker", "fg": [{"weight": 15, "sprite": 2320}, {"weight": 15, "sprite": 2321}, {"weight": 10, "sprite": 2322}, {"weight": 10, "sprite": 2319}], "bg": 1796}, {"id": "mon_zombie_brainless", "fg": 2323, "bg": 1796}, {"id": "mon_zombie_soldier", "fg": 2324, "bg": 1796}, {"id": "mon_fungaloid", "fg": 2325, "bg": 1796}, {"id": "mon_zougar", "fg": 2326, "bg": 1796}, {"id": "mon_bee", "fg": 2327, "bg": 1796}, {"id": "mon_zombie_swimmer", "fg": 2328, "bg": 1796}, {"id": "mon_mosquito_giant", "fg": 2329, "bg": 1796}, {"id": "mon_cougar", "fg": 2330, "bg": 1796}, {"id": "mon_dragonfly_giant", "fg": 2331, "bg": 1796}, {"id": "mon_fly", "fg": 2332, "bg": 1796}], "//": "range 1713 to 2336", "sprite_width": 32, "sprite_height": 64, "sprite_offset_x": 0, "sprite_offset_y": -32}, {"file": "large.png", "tiles": [{"id": "f_rotary_clothesline", "fg": 2337}, {"id": "f_fridge", "fg": 2338}, {"id": "t_tree_young", "fg": [{"weight": 100, "sprite": 2351}, {"weight": 100, "sprite": 2349}], "bg": 2342}, {"id": "t_tree_young_season_summer", "fg": [{"weight": 100, "sprite": 2347}, {"weight": 100, "sprite": 2348}], "bg": 2345, "rotates": false}, {"id": "t_tree_young_season_autumn", "fg": [{"weight": 100, "sprite": 2353}, {"weight": 100, "sprite": 2352}], "bg": 2344, "rotates": false}, {"id": "t_tree_young_season_winter", "fg": [{"weight": 100, "sprite": 2350}, {"weight": 100, "sprite": 2346}], "bg": 2343, "rotates": false}, {"id": "mon_zoose", "fg": 2354, "bg": 2341}, {"id": "mon_zombie_acidic", "fg": 2357, "bg": 2339}, {"id": "mon_zombie_corrosive", "fg": 2355, "bg": 2339}, {"id": "mon_zombie_spitter", "fg": 2358, "bg": 2339}, {"id": "corpse_mon_zombie_spitter", "fg": 2356}, {"id": "mon_moose", "fg": [{"weight": 2, "sprite": 2360}, {"weight": 2, "sprite": 2359}], "bg": 2341}, {"id": "mon_zombear", "fg": 2361, "bg": 2340}, {"id": "mon_zombie_grappler", "fg": [{"weight": 1, "sprite": 2363}, {"weight": 1, "sprite": 2362}], "bg": 2339}, {"id": "mon_boomer", "fg": 2364, "bg": 2339}, {"id": "mon_zombie", "fg": [{"weight": 100, "sprite": 2365}, {"weight": 150, "sprite": 2368}, {"weight": 100, "sprite": 2370}, {"weight": 100, "sprite": 2366}, {"weight": 150, "sprite": 2367}], "bg": 2339}, {"id": ["corpse_mon_zombie", "corpse_mon_zombie_grappler", "corpse_mon_zombie_biter", "corpse_mon_zombie_hunter", "corpse_mon_zombie_brute", "corpse_mon_zombie_predator", "corpse_mon_zombie_necro", "corpse_mon_zombie_thorny", "corpse_mon_zombie_smoker", "corpse_mon_zombie_shady", "corpse_mon_zombie_master", "corpse_mon_zombie_acidic", "corpse_mon_zombie_fat", "corpse_mon_zombie_corrosive", "corpse_mon_zombie_screecher", "corpse_mon_zombie_scientist", "corpse_mon_zombie_runner", "corpse_mon_zombie_labsecurity", "corpse_mon_zombie_child", "corpse_mon_zombie_hulk", "corpse_mon_zombie_brute_grappler", "corpse_mon_zombie_brute_ninja", "corpse_mon_zombie_kevlar_2", "corpse_mon_zombie_kevlar_1", "corpse_mon_zombie_hazmat", "corpse_mon_zombie_electric", "corpse_mon_zombie_technician", "corpse_mon_zombie_fungus", "corpse_mon_zombie_cop", "corpse_mon_zombie_child_fungus", "corpse_mon_zombie_swimmer", "corpse_mon_zombie_mancroc", "corpse_mon_zombie_soldier", "corpse_mon_zombie_skull", "corpse_mon_zombie_brainless", "corpse_mon_zombie_survivor", "corpse_mon_zombie_brute_shocker", "corpse_mon_zombie_soldier_acid_1", "corpse_mon_zombie_soldier_blackops_2", "corpse_mon_zombie_soldier_blackops_1", "corpse_mon_zombie_soldier_acid_2", "corpse_mon_zombie_shriekling", "corpse_mon_zombie_ears", "corpse_mon_zombie_nullfield", "corpse_mon_zombie_waif", "corpse_mon_zombie_sproglodyte", "corpse_mon_zombie_creepy", "corpse_mon_zombie_anklebiter", "corpse_mon_zombie_bio_op", "corpse_mon_zombie_armored", "corpse_mon_zombie_prisoner", "corpse_mon_zombie_military_pilot"], "fg": 2369}, {"id": "mon_zombie_smoker", "fg": 2371, "bg": 2339}, {"id": "mon_zombie_necro", "fg": 2372, "bg": 2339}, {"id": "mon_zombie_biter", "fg": 2373, "bg": 2340}, {"id": "mon_exodii_quad", "fg": [{"weight": 15, "sprite": 2380}, {"weight": 10, "sprite": 2377}, {"weight": 15, "sprite": 2376}, {"weight": 15, "sprite": 2379}], "bg": 2341}, {"id": "mon_exodii_turret", "fg": [{"weight": 15, "sprite": 2378}, {"weight": 5, "sprite": 2375}, {"weight": 15, "sprite": 2374}, {"weight": 15, "sprite": 2381}], "bg": 2340}, {"id": "mon_zombie_hunter", "fg": 2382, "bg": 2340}, {"id": "mon_zombie_cop", "fg": 2384, "bg": 2339}, {"id": "mon_zombie_labsecurity", "fg": 2383, "bg": 2339}, {"id": "mon_zombie_rot", "fg": 2385, "bg": 2339}, {"id": "mon_zombie_gasbag", "fg": [{"weight": 1, "sprite": 2387}, {"weight": 1, "sprite": 2386}], "bg": 2339}, {"id": "mon_blob_large", "fg": 2388, "bg": 2341}, {"id": "mon_ant_soldier", "fg": 2393, "bg": 2341}, {"id": "mon_ant_acid_soldier", "fg": 2396, "bg": 2341}, {"id": "corpse_mon_ant_soldier", "fg": 2394}, {"id": "corpse_mon_ant_acid_soldier", "fg": 2392}, {"id": "mon_ant_queen", "fg": 2390, "bg": 2341}, {"id": "mon_ant_acid_queen", "fg": 2389, "bg": 2341}, {"id": "corpse_mon_ant_queen", "fg": 2391}, {"id": "corpse_mon_ant_acid_queen", "fg": 2395}, {"id": "mon_zombie_shady", "fg": 2397, "bg": 2339}, {"id": "mon_zombie_crawler", "fg": 2398, "bg": 2339}, {"id": "mon_zombie_fat", "fg": [{"weight": 1, "sprite": 2399}, {"weight": 1, "sprite": 2400}], "bg": 2339}, {"id": "mon_zombie_tough", "fg": 2401, "bg": 2339}, {"id": "corpse_mon_zombie_tough", "fg": 2402}, {"id": "mon_zombie_electric", "fg": [{"weight": 1, "sprite": 2405}, {"weight": 1, "sprite": 2403}], "bg": 2339}, {"id": "mon_zombie_nullfield", "fg": [{"weight": 1, "sprite": 2404}, {"weight": 1, "sprite": 2406}], "bg": 2339}, {"id": "mon_zombie_scientist", "fg": [{"weight": 1, "sprite": 2408}, {"weight": 1, "sprite": 2407}], "bg": 2339}, {"id": "mon_wasp", "fg": 2409, "bg": 2340}, {"id": "mon_zombie_runner", "fg": 2410, "bg": 2339}, {"id": "mon_mi_go", "fg": 2411, "bg": 2340}, {"id": "mon_zombie_brute", "fg": [{"weight": 100, "sprite": 2412}, {"weight": 100, "sprite": 2413}, {"weight": 50, "sprite": 2415}, {"weight": 50, "sprite": 2414}], "bg": 2340}, {"id": "mon_bear", "fg": 2416, "bg": 2340}], "//": "range 2337 to 2432", "sprite_width": 64, "sprite_height": 64, "sprite_offset_x": -16, "sprite_offset_y": -32}, {"file": "huge.png", "tiles": [{"id": "mon_zombie_hulk", "fg": 2433}], "//": "range 2433 to 2448", "sprite_width": 64, "sprite_height": 96, "sprite_offset_x": -16, "sprite_offset_y": -64}, {"file": "giant.png", "tiles": [{"id": "t_tree", "fg": [{"weight": 100, "sprite": 2457}, {"weight": 100, "sprite": 2456}, {"weight": 100, "sprite": 2458}, {"weight": 100, "sprite": 2452}], "bg": 2462}, {"id": "t_tree_season_summer", "fg": [{"weight": 100, "sprite": 2450}, {"weight": 100, "sprite": 2455}, {"weight": 100, "sprite": 2451}, {"weight": 100, "sprite": 2449}], "bg": 2463}, {"id": "t_tree_season_autumn", "fg": [{"weight": 100, "sprite": 2460}, {"weight": 100, "sprite": 2454}, {"weight": 100, "sprite": 2453}, {"weight": 100, "sprite": 2459}], "bg": 2461}, {"id": "t_tree_season_winter", "fg": [{"weight": 100, "sprite": 2466}, {"weight": 100, "sprite": 2467}, {"weight": 100, "sprite": 2465}, {"weight": 100, "sprite": 2468}], "bg": 2464}, {"id": "t_tree_dead", "fg": [{"weight": 100, "sprite": 2466}, {"weight": 100, "sprite": 2467}, {"weight": 100, "sprite": 2465}, {"weight": 100, "sprite": 2468}, {"weight": 100, "sprite": 2771}], "bg": 2462}, {"id": "t_tree_dead_season_summer", "fg": [{"weight": 100, "sprite": 2466}, {"weight": 100, "sprite": 2467}, {"weight": 100, "sprite": 2465}, {"weight": 100, "sprite": 2468}, {"weight": 100, "sprite": 2771}], "bg": 2463}, {"id": "t_tree_dead_season_autumn", "fg": [{"weight": 100, "sprite": 2466}, {"weight": 100, "sprite": 2467}, {"weight": 100, "sprite": 2465}, {"weight": 100, "sprite": 2468}, {"weight": 100, "sprite": 2771}], "bg": 2461}, {"id": "t_tree_dead_season_winter", "fg": [{"weight": 100, "sprite": 2466}, {"weight": 100, "sprite": 2467}, {"weight": 100, "sprite": 2465}, {"weight": 100, "sprite": 2468}, {"weight": 100, "sprite": 2771}], "bg": 2464}, {"id": "t_tree_birch", "fg": [{"weight": 1, "sprite": 2473}, {"weight": 1, "sprite": 2470}], "bg": 2462}, {"id": "t_tree_birch_season_summer", "fg": [{"weight": 1, "sprite": 2473}, {"weight": 1, "sprite": 2470}], "bg": 2463}, {"id": "t_tree_birch_season_winter", "fg": [{"weight": 1, "sprite": 2474}, {"weight": 1, "sprite": 2471}], "bg": 2464}, {"id": "t_tree_birch_season_autumn", "fg": [{"weight": 1, "sprite": 2472}, {"weight": 1, "sprite": 2469}], "bg": 2461}], "//": "range 2449 to 2480", "sprite_width": 96, "sprite_height": 96, "sprite_offset_x": -32, "sprite_offset_y": -64}, {"file": "incomplete.png", "tiles": [{"id": "f_sink", "multitile": true, "fg": 2491, "additional_tiles": [{"id": "center", "fg": 2496}, {"id": "corner", "fg": [2482, 2494, 2486, 2481]}, {"id": "t_connection", "fg": [2488, 2495, 2490, 2485]}, {"id": "edge", "fg": [2489, 2487]}, {"id": "end_piece", "fg": [2493, 2484, 2483, 2492]}, {"id": "unconnected", "fg": 2491}]}, {"id": "toolbox", "fg": 2497}, {"id": "acoustic_guitar", "fg": 2498}, {"id": "rope_6", "fg": 2499}, {"id": "colt_army", "fg": 2500}, {"id": "ref_lighter", "fg": 2501}, {"id": "soap", "fg": 2502}, {"id": "waffleiron", "fg": 2503}, {"id": "bubblewrap", "fg": 2504}, {"id": "soldering_iron", "fg": 2505}, {"id": "stepladder", "fg": 2506}, {"id": "mag_pistol", "fg": 2507}, {"id": "brazier", "fg": 2508}, {"id": "pliers", "fg": 2509}, {"id": "spray_can", "fg": 2510}, {"id": "condom", "fg": 2511}, {"id": "colt_navy", "fg": 2512}, {"id": "hotplate", "fg": 2513}, {"id": "charcoal", "fg": 2514}, {"id": "stanag30", "fg": 2515}, {"id": "keg", "fg": 2516}, {"id": "pitchfork", "fg": 2517}, {"id": "knife_steak", "fg": 2518}, {"id": "wood_beam", "fg": 2519}, {"id": "smart_phone", "fg": 2520}, {"id": "mosin91_30", "fg": 2521}, {"id": "extinguisher", "fg": 2522}, {"id": "hk_mp5", "fg": 2523}, {"id": "sw_610", "fg": 2524}, {"id": "colt_lightning", "fg": 2525}, {"id": "mag_smg", "fg": 2526}, {"id": "duct_tape", "fg": 2527}, {"id": "t_chainfence", "multitile": true, "rotates": false, "fg": 2528, "bg": 1463, "additional_tiles": [{"id": "edge", "bg": 1463, "fg": [2531, 2528]}, {"id": "end_piece", "bg": 1463, "fg": [2531, 2528, 2531, 2528]}, {"bg": 1463, "id": "unconnected", "fg": 2528}]}, {"id": "t_chainfence_season_winter", "multitile": true, "rotates": false, "fg": 2528, "bg": 682, "additional_tiles": [{"id": "edge", "bg": 682, "fg": [2531, 2528]}, {"id": "end_piece", "bg": 682, "fg": [2531, 2528, 2531, 2528]}, {"bg": 682, "id": "unconnected", "fg": 2528}]}, {"id": "t_chaingate_c", "fg": 2532, "bg": 1463}, {"id": "t_chaingate_l", "fg": 2533, "bg": 1463}, {"id": "t_chaingate_o", "fg": 2529, "bg": 1463}, {"id": "t_chainfence_posts", "fg": 2530, "bg": 1463}, {"id": "t_chaingate_c_season_winter", "fg": 2532, "bg": 682}, {"id": "t_chaingate_l_season_winter", "fg": 2533, "bg": 682}, {"id": "t_chaingate_o_season_winter", "fg": 2529, "bg": 682}, {"id": "t_chainfence_posts_season_winter", "fg": 2530, "bg": 682}, {"id": "t_strconc_wall", "fg": 2534}, {"id": "t_splitrail_fence_season_spring", "fg": 2535, "bg": 1312}, {"id": "t_splitrail_fence_season_summer", "fg": 2535, "bg": 1326}, {"id": "t_splitrail_fence_season_autumn", "fg": 2535, "bg": 1313}, {"id": "t_splitrail_fence_season_winter", "fg": 2535, "bg": 682}, {"id": "t_railing", "fg": 2536, "bg": 1312}, {"id": "t_console", "fg": 2537}, {"id": "t_underbrush", "fg": [{"weight": 100, "sprite": 2540}, {"weight": 100, "sprite": 2539}], "bg": 1312}, {"id": "t_underbrush_harvested", "fg": 2538, "bg": 1312}, {"id": "t_underbrush_season_summer", "fg": [{"weight": 100, "sprite": 2540}, {"weight": 100, "sprite": 2539}], "bg": 1326}, {"id": "t_underbrush_harvested_season_summer", "fg": 2538, "bg": 1326}, {"id": "t_underbrush_season_autumn", "fg": [{"weight": 100, "sprite": 2540}, {"weight": 100, "sprite": 2539}], "bg": 1313}, {"id": "t_underbrush_harvested_season_winter", "fg": 2538, "bg": 649}, {"id": "t_underbrush_season_winter", "fg": [{"weight": 100, "sprite": 2540}, {"weight": 100, "sprite": 2539}], "bg": 649}, {"id": ["t_window_boarded", "t_window_boarded_noglass"], "fg": 2541, "bg": 801}, {"id": "t_wall_metal", "fg": 2542}, {"id": ["t_window_reinforced", "t_window_reinforced_noglass"], "fg": 2543, "bg": 801}, {"id": "t_door_metal_o", "fg": 2544}, {"id": "t_window_frame", "fg": 2545}, {"id": "t_pavement_y", "fg": 2546}, {"id": "t_pavement_y_season_winter", "fg": 682}, {"id": "t_dirtmound", "fg": 2547}, {"id": "t_reinforced_glass_shutter", "fg": 2549}, {"id": "t_reinforced_glass_shutter_open", "fg": 2548}, {"id": "t_trunk", "multitile": true, "fg": [2550, 2551], "bg": [{"weight": 100, "sprite": 1312}, {"weight": 100, "sprite": 1315}]}, {"id": "t_trunk_season_summer", "multitile": true, "fg": [2550, 2551], "bg": [{"weight": 100, "sprite": 1326}, {"weight": 100, "sprite": 1306}]}, {"id": "t_trunk_season_autumn", "multitile": true, "fg": [2550, 2551], "bg": [{"weight": 100, "sprite": 1313}, {"weight": 100, "sprite": 1305}]}, {"id": "t_trunk_season_winter", "multitile": true, "fg": [2550, 2551], "bg": 682}, {"id": "t_console_broken", "fg": 2552}, {"id": ["t_junk_palisade", "t_junk_wall"], "fg": [{"weight": 100, "sprite": 2556}, {"weight": 100, "sprite": 2555}, {"weight": 100, "sprite": 2553}, {"weight": 100, "sprite": 2554}]}, {"id": "t_door_metal_c", "fg": 2557}, {"id": "fd_smoke", "fg": 2560}, {"id": "fd_fungal_haze", "fg": 2559}, {"id": "fd_nuke_gas", "fg": 2558}, {"id": "fd_acid", "fg": 2561}, {"id": "fd_blood", "fg": 2563}, {"id": ["fd_blood_insect", "fd_blood_invertebrate"], "fg": 2562}, {"id": "fd_electricity", "fg": [{"weight": 100, "sprite": 2565}, {"weight": 100, "sprite": 2564}]}, {"id": "fd_web", "fg": [{"weight": 100, "sprite": 2567}, {"weight": 100, "sprite": 2566}, {"weight": 25, "sprite": 2568}, {"weight": 25, "sprite": 2569}]}], "//": "range 2481 to 2576"}, {"file": "fillerhoder.png", "tiles": [{"id": "vp_aisle_horizontal", "fg": 2589}, {"id": "vp_aisle_vertical", "fg": 2586}, {"id": "vp_bed", "fg": 2585}, {"id": "vp_frame_cover", "fg": 2663}, {"id": "vp_frame_cross", "fg": 2662}, {"id": "vp_frame_handle", "fg": 2743}, {"id": "vp_frame_horizontal", "fg": 2680}, {"id": "vp_frame_horizontal_2", "fg": 2684}, {"id": "vp_frame_ne", "fg": 2641}, {"id": "vp_frame_nw", "fg": 2728}, {"id": "vp_frame_se", "fg": 2638}, {"id": "vp_frame_sw", "fg": 2726}, {"id": "vp_frame_vertical", "fg": 2648}, {"id": "vp_frame_vertical_2", "fg": 2711}, {"id": "vp_frame_wood_cover", "fg": 2748}, {"id": "vp_frame_wood_cross", "fg": 2693}, {"id": "vp_frame_wood_handle", "fg": 2743}, {"id": "vp_frame_wood_horizontal", "fg": 2612}, {"id": "vp_frame_wood_horizontal_2", "fg": 2588}, {"id": "vp_frame_wood_ne", "fg": 2617}, {"id": "vp_frame_wood_nw", "fg": 2671}, {"id": "vp_frame_wood_se", "fg": 2596}, {"id": "vp_frame_wood_sw", "fg": 2624}, {"id": "vp_frame_wood_vertical", "fg": 2599}, {"id": "vp_frame_wood_vertical_2", "fg": 2725}, {"id": "vp_hdboard_horizontal", "fg": 2737}, {"id": "vp_hdboard_ne", "fg": 2579}, {"id": "vp_hdboard_nw", "fg": 2659}, {"id": "vp_hdboard_se", "fg": 2602}, {"id": "vp_hdboard_sw", "fg": 2618}, {"id": "vp_hdboard_vertical", "fg": 2706}, {"id": "vp_hdframe_cover", "fg": 2732}, {"id": "vp_hdframe_cross", "fg": 2692}, {"id": "vp_hdframe_horizontal", "fg": 2687}, {"id": "vp_hdframe_horizontal_2", "fg": 2635}, {"id": "vp_hdframe_ne", "fg": 2738}, {"id": "vp_hdframe_nw", "fg": 2690}, {"id": "vp_hdframe_se", "fg": 2705}, {"id": "vp_hdframe_sw", "fg": 2644}, {"id": "vp_hdframe_vertical", "fg": 2651}, {"id": "vp_hdframe_vertical_2", "fg": 2752}, {"id": "vp_washing_machine", "fg": 2653}, {"id": "vp_woodboard_horizontal", "fg": 2708}, {"id": "vp_woodboard_ne", "fg": 2716}, {"id": "vp_woodboard_nw", "fg": 2613}, {"id": "vp_woodboard_se", "fg": 2667}, {"id": "vp_woodboard_sw", "fg": 2731}, {"id": "vp_woodboard_vertical", "fg": 2674}, {"id": "vp_woodhalfboard_horizontal", "fg": 2708}, {"id": "vp_woodhalfboard_horizontal_2", "fg": 2708}, {"id": "vp_woodhalfboard_ne", "fg": 2716}, {"id": "vp_woodhalfboard_nw", "fg": 2613}, {"id": "vp_woodhalfboard_se", "fg": 2667}, {"id": "vp_woodhalfboard_sw", "fg": 2731}, {"id": "vp_woodhalfboard_vertical", "fg": 2674}, {"id": "vp_woodhalfboard_vertical_2", "fg": 2674}, {"id": "vp_basketlg", "fg": 2622}, {"id": "vp_basketsm", "fg": 2622}, {"id": "vp_battery_motorbike", "fg": 2750}, {"id": "vp_blade_horizontal", "fg": 2749}, {"id": "vp_blade_vertical", "fg": 2593}, {"id": "vp_box", "fg": 2691}, {"id": "vp_cargo_space", "fg": 2646, "bg": 2725}, {"id": "vp_chemlab", "fg": 2591}, {"id": "vp_controls", "fg": 2665}, {"id": "vp_craft_rig", "fg": 2591}, {"id": "vp_door_internal", "fg": 2631}, {"id": "vp_door_opaque", "fg": 2661}, {"id": "vp_door_shutter", "fg": 2633}, {"id": "vp_door_sliding", "fg": 2678}, {"id": "vp_engine_1cyl", "fg": 2723}, {"id": "vp_engine_electric", "fg": 2628}, {"id": "vp_engine_electric_large", "fg": 2701}, {"id": "vp_engine_inline4", "fg": 2695}, {"id": "vp_engine_plasma", "fg": 2642}, {"id": "vp_engine_v12", "fg": 2710, "bg": 2713}, {"id": "vp_engine_v6", "fg": 2710}, {"id": "vp_engine_v8", "fg": 2713}, {"id": "vp_engine_vtwin", "fg": 2583}, {"id": "vp_external_gas_tank", "fg": 2580}, {"id": "vp_flamethrower", "fg": 2699}, {"id": "vp_floodlight", "fg": 2578, "bg": 2640}, {"id": "vp_foot_pedals", "fg": 2664}, {"id": "vp_fusion_gun", "fg": 2650}, {"id": "vp_gas_tank", "fg": 2580}, {"id": "vp_gas_tank_small", "fg": 2580}, {"id": "vp_hatch", "fg": 2717}, {"id": "vp_hatch_opaque", "fg": 2745}, {"id": "vp_hddoor", "fg": 2657}, {"id": "vp_hddoor_internal", "fg": 2679}, {"id": "vp_hddoor_opaque", "fg": 2625}, {"id": "vp_hddoor_shutter", "fg": 2608}, {"id": "vp_hddoor_sliding", "fg": 2681}, {"id": "vp_hddoor_trunk", "fg": 2660}, {"id": "vp_hdhatch", "fg": 2675}, {"id": "vp_hdhatch_opaque", "fg": 2639}, {"id": "vp_hdroof", "fg": 2719}, {"id": "vp_hydrogen_tank", "fg": 2682}, {"id": "vp_kitchen_unit", "fg": 2591}, {"id": "vp_laser_gun", "fg": 2650}, {"id": "vp_light_blue", "fg": 2670, "bg": 2733}, {"id": "vp_light_red", "fg": 2670, "bg": 2632}, {"id": "vp_lit_aisle_horizontal", "fg": 2589}, {"id": "vp_lit_aisle_vertical", "fg": 2586}, {"id": "vp_m249", "fg": 2729}, {"id": "vp_minifridge", "fg": 2689}, {"id": "vp_minireactor", "fg": 2614}, {"id": "vp_mounted_browning", "fg": 2729}, {"id": "vp_mounted_mk19", "fg": 2729}, {"id": "vp_muffler", "fg": 2714}, {"id": ["vp_omnicam", "vp_omnomnicam"], "fg": 2686, "bg": 2719}, {"id": "vp_plasma_gun", "fg": 2668}, {"id": "vp_plating_hard", "fg": 2672}, {"id": "vp_plating_military", "fg": 2590}, {"id": "vp_plating_spiked", "fg": 2707}, {"id": "vp_plating_steel", "fg": 2590}, {"id": "vp_plating_superalloy", "fg": 2620}, {"id": "vp_plating_wood", "fg": 2649}, {"id": "vp_recharge_station", "fg": 2750, "bg": 2691}, {"id": "vp_reinforced_solar_panel", "fg": 2724, "bg": 2601}, {"id": "vp_reinforced_solar_panel_v2", "fg": 2724, "bg": 2601}, {"id": "vp_reinforced_windshield", "fg": 2724, "bg": 2669}, {"id": "vp_roof", "fg": 2640}, {"id": "vp_roof_cloth", "fg": 2688}, {"id": "vp_seatbelt", "fg": 2584}, {"id": "vp_seatbelt_heavyduty", "fg": 2584}, {"id": "vp_small_storage_battery", "fg": 2750}, {"id": "vp_solar_panel", "fg": 2601}, {"id": "vp_solar_panel_v2", "fg": 2601}, {"id": "vp_solar_panel_v3", "fg": 2601}, {"id": "vp_spike", "fg": 2616}, {"id": "vp_storage_battery", "fg": 2750}, {"id": "vp_storage_car", "fg": 2750}, {"id": "vp_storage_truck", "fg": 2750}, {"id": "vp_trunk", "fg": 2691}, {"id": "vp_trunk_floor", "fg": 2594}, {"id": "vp_turret_mount", "fg": 2666, "bg": 2719}, {"id": "vp_v_curtain", "fg": 2598}, {"id": "vp_veh_forge", "fg": 2610}, {"id": "vp_veh_table", "fg": 2746}, {"id": "vp_water_tank", "fg": 2630}, {"id": "vp_welding_rig", "fg": 2734}, {"id": "vp_wheel", "fg": 2751}, {"id": "vp_wheel_armor", "fg": 2751}, {"id": "vp_wheel_armor_steerable", "fg": 2751}, {"id": "vp_wheel_bicycle", "fg": 2722}, {"id": "vp_wheel_bicycle_steerable", "fg": 2722}, {"id": "vp_wheel_caster", "fg": 2627}, {"id": "vp_wheel_motorbike", "fg": 2736}, {"id": "vp_wheel_motorbike_steerable", "fg": 2736}, {"id": "vp_wheel_small", "fg": 2581}, {"id": "vp_wheel_small_steerable", "fg": 2581}, {"id": "vp_wheel_steerable", "fg": 2751}, {"id": "vp_wheel_unicycle", "fg": 2722}, {"id": "vp_wheel_wheelchair", "fg": 2722}, {"id": "vp_wheel_wide", "fg": 2611}, {"id": "vp_wheel_wide_steerable", "fg": 2611}, {"id": "alloy_plate", "fg": 2620, "bg": 2658}, {"id": "foot_crank", "fg": 2664, "bg": 2658}, {"id": "frame", "fg": 2693, "bg": 2658}, {"id": "glass_sheet", "fg": 2669, "bg": 2658}, {"id": "hard_plate", "fg": 2672, "bg": 2658}, {"id": "kitchen_unit", "fg": 2591, "bg": 2658}, {"id": "motor", "fg": 2628, "bg": 2658}, {"id": "motor_large", "fg": 2701, "bg": 2658}, {"id": "muffler", "fg": 2714, "bg": 2658}, {"id": "plasma_engine", "fg": 2642, "bg": 2658}, {"id": "saddle", "fg": 2636, "bg": 2658}, {"id": "seat", "fg": 2615, "bg": 2658}, {"id": "solar_panel", "fg": 2601, "bg": 2658}, {"id": "spiked_plate", "fg": 2707, "bg": 2658}, {"id": "steel_plate", "fg": 2590, "bg": 2658}, {"id": "storage_battery", "fg": 2750, "bg": 2658}, {"id": "vehicle_controls", "fg": 2665, "bg": 2658}, {"id": "weldrig", "fg": 2734, "bg": 2658}, {"id": "forge", "fg": 2610, "bg": 2658}, {"id": "1cyl_combustion", "fg": 2723, "bg": 2658}, {"id": "i4_combustion", "fg": 2695, "bg": 2658}, {"id": "v2_combustion", "fg": 2583, "bg": 2658}, {"id": "v6_combustion", "fg": 2710, "bg": 2658}, {"id": "v8_combustion", "fg": 2713, "bg": 2658}, {"id": "vp_board_horizontal", "fg": 2652}, {"id": "vp_board_ne", "fg": 2621}, {"id": "vp_board_nw", "fg": 2739}, {"id": "vp_board_se", "fg": 2600}, {"id": "vp_board_sw", "fg": 2721}, {"id": "vp_board_vertical", "fg": 2709}, {"id": "vp_hdhalfboard_horizontal", "fg": 2577}, {"id": "vp_hdhalfboard_horizontal_2", "fg": 2609}, {"id": "vp_hdhalfboard_ne", "fg": 2747}, {"id": "vp_hdhalfboard_nw", "fg": 2730}, {"id": "vp_hdhalfboard_se", "fg": 2655}, {"id": "vp_hdhalfboard_sw", "fg": 2677}, {"id": "vp_hdhalfboard_vertical", "fg": 2703}, {"id": "vp_hdhalfboard_vertical_2", "fg": 2719}, {"id": "vp_hdstowboard_horizontal", "fg": 2623}, {"id": "vp_hdstowboard_ne", "fg": 2685}, {"id": "vp_hdstowboard_nw", "fg": 2697}, {"id": "vp_hdstowboard_se", "fg": 2587}, {"id": "vp_hdstowboard_sw", "fg": 2735}, {"id": "vp_hdstowboard_vertical", "fg": 2606}, {"id": "vp_stowboard_horizontal", "fg": 2742}, {"id": "vp_stowboard_ne", "fg": 2604}, {"id": "vp_stowboard_nw", "fg": 2673}, {"id": "vp_stowboard_se", "fg": 2720}, {"id": "vp_stowboard_sw", "fg": 2592}, {"id": "vp_stowboard_vertical", "fg": 2637}, {"id": "vp_wing_mirror", "fg": 2605}, {"id": "[vp_xlframe_cover],[vp_fxlframe_cover]", "fg": 2645}, {"id": "[vp_xlframe_cross],[vp_fxlframe_cross]", "fg": 2643}, {"id": "[vp_xlframe_horizontal_2],[vp_fxlframe_horizontal_2]", "fg": 2718}, {"id": "[vp_xlframe_horizontal],[vp_fxlframe_horizontal]", "fg": 2696}, {"id": "[vp_xlframe_ne],[vp_fxlframe_ne]", "fg": 2619}, {"id": "[vp_xlframe_nw],[vp_fxlframe_nw]", "fg": 2607}, {"id": "[vp_xlframe_se],[vp_fxlframe_se]", "fg": 2683}, {"id": "[vp_xlframe_sw],[vp_fxlframe_sw]", "fg": 2740}, {"id": "[vp_xlframe_vertical_2],[vp_fxlframe_vertical_2]", "fg": 2626}, {"id": "[vp_xlframe_vertical],[vp_fxlframe_vertical]", "fg": 2715}, {"id": "[vp_xlhalfboard_horizontal_2],[vp_fxlhalfboard_horizontal_2]", "fg": 2654}, {"id": "[vp_xlhalfboard_horizontal],[vp_fxlhalfboard_horizontal]", "fg": 2727}, {"id": "[vp_xlhalfboard_ne],[vp_fxlhalfboard_ne]", "fg": 2700}, {"id": "[vp_xlhalfboard_nw],[vp_fxlhalfboard_nw]", "fg": 2595}, {"id": "[vp_xlhalfboard_se],[vp_fxlhalfboard_se]", "fg": 2597}, {"id": "[vp_xlhalfboard_sw],[vp_fxlhalfboard_sw", "fg": 2634}, {"id": "[vp_xlhalfboard_vertical_2],[vp_fxlhalfboard_vertical_2]", "fg": 2647}, {"id": "[vp_xlhalfboard_vertical],[vp_fxlhalfboard_vertical]", "fg": 2603}, {"id": "wheel", "fg": 2751, "bg": 2658}, {"id": "wheel_bicycle", "fg": 2722, "bg": 2658}, {"id": "wheel_motorbike", "fg": 2736, "bg": 2658}, {"id": "wheel_small", "fg": 2581, "bg": 2658}, {"id": "wheel_wide", "fg": 2611, "bg": 2658}], "//": "range 2577 to 2768"}, {"file": "opengameartgiant.png", "tiles": [{"id": "t_tree_chestnut", "fg": 2770, "bg": 2462}, {"id": "t_tree_chestnut_season_summer", "fg": 2771, "bg": 2463}, {"id": "t_tree_chestnut_season_autumn", "fg": 2771, "bg": 2461}, {"id": "t_tree_chestnut_season_winter", "fg": 2771, "bg": 2464}, {"id": "t_tree_pine", "fg": 2774, "bg": 2462}, {"id": "t_tree_pine_season_summer", "fg": 2774, "bg": 2463}, {"id": "t_tree_pine_season_autumn", "fg": 2774, "bg": 2461}, {"id": "t_tree_pine_season_winter", "fg": 2774, "bg": 2464}, {"id": "t_tree_deadpine", "fg": 2772, "bg": 2462}, {"id": "t_tree_deadpine_season_summer", "fg": 2772, "bg": 2463}, {"id": "t_tree_deadpine_season_autumn", "fg": 2772, "bg": 2461}, {"id": "t_tree_deadpine_season_winter", "fg": 2772, "bg": 2464}, {"id": "t_tree_hickory", "fg": 2769, "bg": 2462}, {"id": "t_tree_hickory_season_summer", "fg": 2769, "bg": 2463}, {"id": "t_tree_hickory_season_autumn", "fg": 2769, "bg": 2461}, {"id": "t_tree_hickory_season_winter", "fg": 2773, "bg": 2464}, {"id": "t_tree_hickory_dead", "fg": 2773, "bg": 2462}, {"id": "t_tree_hickory_dead_season_summer", "fg": 2773, "bg": 2463}, {"id": "t_tree_hickory_dead_season_autumn", "fg": 2773, "bg": 2461}, {"id": "t_tree_hickory_dead_season_winter", "fg": 2773, "bg": 2464}], "//": "range 2769 to 2784", "sprite_width": 96, "sprite_height": 96, "sprite_offset_x": -32, "sprite_offset_y": -64}, {"file": "fallback.png", "tiles": [], "ascii": [{"offset": 0, "bold": false, "color": "BLACK"}, {"offset": 256, "bold": true, "color": "WHITE"}, {"offset": 512, "bold": false, "color": "WHITE"}, {"offset": 768, "bold": true, "color": "BLACK"}, {"offset": 1024, "bold": false, "color": "RED"}, {"offset": 1280, "bold": false, "color": "GREEN"}, {"offset": 1536, "bold": false, "color": "BLUE"}, {"offset": 1792, "bold": false, "color": "CYAN"}, {"offset": 2048, "bold": false, "color": "MAGENTA"}, {"offset": 2304, "bold": false, "color": "YELLOW"}, {"offset": 2560, "bold": true, "color": "RED"}, {"offset": 2816, "bold": true, "color": "GREEN"}, {"offset": 3072, "bold": true, "color": "BLUE"}, {"offset": 3328, "bold": true, "color": "CYAN"}, {"offset": 3584, "bold": true, "color": "MAGENTA"}, {"offset": 3840, "bold": true, "color": "YELLOW"}]}]} diff --git a/gfx/UltimateCataclysmDemo/tileset.txt b/gfx/UltimateCataclysmDemo/tileset.txt index 623cb620032d1..5058fd31a7450 100644 --- a/gfx/UltimateCataclysmDemo/tileset.txt +++ b/gfx/UltimateCataclysmDemo/tileset.txt @@ -1,4 +1,4 @@ -#Ultimate Cataclysm Demo Update 17 June 2020 +#Ultimate Cataclysm Demo Update 4 July 2020 NAME: UltimateCataclysmDemo VIEW: Ultica (Beta) JSON: tile_config.json diff --git a/lang/extract_json_strings.py b/lang/extract_json_strings.py index b1313bd274a1d..9e9c10d9dc2a3 100755 --- a/lang/extract_json_strings.py +++ b/lang/extract_json_strings.py @@ -167,7 +167,8 @@ def warning_supressed(filename): "vehicle_part", "vitamin", "WHEEL", - "help" + "help", + "weather_type" } # for these objects a plural form is needed 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 "輕型輪轂(轉向輪)" diff --git a/msvc-full-features/Cataclysm-lib-vcpkg-static.vcxproj b/msvc-full-features/Cataclysm-lib-vcpkg-static.vcxproj index 66cf377f1163a..578271efa0d3e 100644 --- a/msvc-full-features/Cataclysm-lib-vcpkg-static.vcxproj +++ b/msvc-full-features/Cataclysm-lib-vcpkg-static.vcxproj @@ -1,137 +1,145 @@ - - - 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 + + + true + true + true + true + true + x86-windows + x64-windows + $(Configuration) - - StaticLibrary - v142 - MultiByte + StaticLibrary + v142 + MultiByte - true + true - false - false + false + false + + + + + + + + + + $(ProjectName)-$(Configuration)-$(Platform) + .lib + $(SolutionDir)..\ + $(SolutionDir)$(ProjectName)\$(Configuration)\$(Platform)\ + + + true + + + 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 - - - - - + + + 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 e1bfccb6bdd0c..6591853dcfe85 100644 --- a/msvc-full-features/Cataclysm-test-vcpkg-static.vcxproj +++ b/msvc-full-features/Cataclysm-test-vcpkg-static.vcxproj @@ -27,6 +27,16 @@ x86-windows-static x64-windows-static + + true + true + true + true + true + x86-windows + x64-windows + $(Configuration) + Application @@ -138,4 +148,4 @@ - \ No newline at end of file + diff --git a/msvc-full-features/Cataclysm-vcpkg-static.sln b/msvc-full-features/Cataclysm-vcpkg-static.sln index ce23375f05220..38c183d6ab5f7 100644 --- a/msvc-full-features/Cataclysm-vcpkg-static.sln +++ b/msvc-full-features/Cataclysm-vcpkg-static.sln @@ -7,6 +7,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution ProjectSection(SolutionItems) = preProject ..\.editorconfig = ..\.editorconfig AStyleExtension-Cataclysm-DDA.cfg = AStyleExtension-Cataclysm-DDA.cfg + vcpkg.json = vcpkg.json EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Cataclysm-vcpkg-static", "Cataclysm-vcpkg-static.vcxproj", "{19F0BE17-3DAF-40E8-A9D2-904A56382E54}" diff --git a/msvc-full-features/Cataclysm-vcpkg-static.vcxproj b/msvc-full-features/Cataclysm-vcpkg-static.vcxproj index b8021c0e3d405..422b2df1fcbcb 100644 --- a/msvc-full-features/Cataclysm-vcpkg-static.vcxproj +++ b/msvc-full-features/Cataclysm-vcpkg-static.vcxproj @@ -27,6 +27,16 @@ x86-windows-static x64-windows-static + + true + true + true + true + true + x86-windows + x64-windows + $(Configuration) + Application @@ -132,4 +142,4 @@ - \ No newline at end of file + diff --git a/msvc-full-features/JsonFormatter.vcxproj b/msvc-full-features/JsonFormatter.vcxproj index bf31ec6133545..7ffd9f160301e 100644 --- a/msvc-full-features/JsonFormatter.vcxproj +++ b/msvc-full-features/JsonFormatter.vcxproj @@ -27,6 +27,16 @@ x86-windows-static x64-windows-static + + true + true + true + true + true + x86-windows + x64-windows + $(Configuration) + Application diff --git a/msvc-full-features/vcpkg.json b/msvc-full-features/vcpkg.json new file mode 100644 index 0000000000000..4d441c9ec9c6f --- /dev/null +++ b/msvc-full-features/vcpkg.json @@ -0,0 +1,14 @@ +{ + "name": "cdda-vcpkg-dependencies", + "version-string": "0.E", + "dependencies": [ + "sdl2", + "sdl2-image", + { + "name": "sdl2-mixer", + "features": [ "dynamic-load", "libflac", "mpg123", "libmodplug", "libvorbis" ] + }, + "sdl2-ttf", + "gettext" + ] +} 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/activity_actor.cpp b/src/activity_actor.cpp index a005dfa2e9c85..77ec49578932a 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 ) ); @@ -402,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 @@ -460,7 +465,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 ) ); @@ -468,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 @@ -1129,8 +1136,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 +1147,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 +1171,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 +1323,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/activity_handlers.cpp b/src/activity_handlers.cpp index 09886602274db..e78782f4a275c 100644 --- a/src/activity_handlers.cpp +++ b/src/activity_handlers.cpp @@ -1718,7 +1718,7 @@ void activity_handlers::firstaid_finish( player_activity *act, player *p ) // TODO: Store the patient somehow, retrieve here player &patient = *p; - const hp_part healed = static_cast( act->values[0] ); + const bodypart_id healed = bodypart_id( act->str_values[0] ); const int charges_consumed = actor->finish_using( *p, patient, *used_tool, healed ); p->consume_charges( it, charges_consumed ); @@ -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 ) @@ -2284,7 +2287,7 @@ void activity_handlers::vibe_do_turn( player_activity *act, player *p ) //Deduct 1 battery charge for every minute in use, or vibrator is much less effective item &vibrator_item = *act->targets.front(); - if( p->encumb( bp_mouth ) >= 30 ) { + if( p->encumb( bodypart_id( "mouth" ) ) >= 30 ) { act->moves_left = 0; add_msg( m_bad, _( "You have trouble breathing, and stop." ) ); } @@ -2546,7 +2549,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 +2566,7 @@ struct weldrig_hack { return; } - veh->charge_battery( pseudo.charges ); - pseudo.charges = 0; + veh->charge_battery( pseudo.ammo_remaining() ); } ~weldrig_hack() { @@ -2670,7 +2675,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 +2730,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; } @@ -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/activity_item_handling.cpp b/src/activity_item_handling.cpp index e43ba65f52b61..0fbd1e36f82d9 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 @@ -3014,7 +3014,7 @@ int get_auto_consume_moves( player &p, const bool food ) void try_fuel_fire( player_activity &act, player &p, const bool starting_fire ) { const tripoint pos = p.pos(); - std::vector adjacent = closest_tripoints_first( pos, PICKUP_RANGE ); + std::vector adjacent = closest_points_first( pos, PICKUP_RANGE ); adjacent.erase( adjacent.begin() ); cata::optional best_fire = starting_fire ? act.placement : find_best_fire( adjacent, diff --git a/src/advanced_inv.cpp b/src/advanced_inv.cpp index 602d5e17e5951..1d3f1bbef3c05 100644 --- a/src/advanced_inv.cpp +++ b/src/advanced_inv.cpp @@ -195,6 +195,11 @@ void advanced_inventory::init() src = ( save_state->active_left ) ? left : right; dest = ( save_state->active_left ) ? right : left; + + //sanity check, badly initialized values may cause problem in move_all_items( see assert() ) + if( panes[src].get_area() == AIM_ALL && panes[dest].get_area() == AIM_ALL ) { + panes[dest].set_area( AIM_INVENTORY ); + } } void advanced_inventory::print_items( const advanced_inventory_pane &pane, bool active ) @@ -839,24 +844,21 @@ bool advanced_inventory::move_all_items( bool nested_call ) drop_locations dropped_favorite; if( spane.get_area() == AIM_INVENTORY ) { - for( size_t index = 0; index < g->u.inv.size(); ++index ) { - const std::list &stack = g->u.inv.const_stack( index ); - const item &it = stack.front(); - item_location indexed_item( g->u, const_cast( &it ) ); - - if( !spane.is_filtered( it ) ) { - int count; - if( it.count_by_charges() ) { - count = it.charges; - } else { - count = stack.size(); + //add all solid top level items + for( item &cloth : g->u.worn ) { + for( item *it : cloth.contents.all_items_top() ) { + if( !it->made_of_from_type( phase_id::SOLID ) ) { + continue; } - if( it.is_favorite ) { - dropped_favorite.emplace_back( indexed_item, count ); - } else { - dropped.emplace_back( indexed_item, count ); + if( !spane.is_filtered( *it ) ) { + if( it->is_favorite ) { + dropped_favorite.emplace_back( item_location( item_location( g->u, &cloth ), it ), it->count() ); + } else { + dropped.emplace_back( item_location( item_location( g->u, &cloth ), it ), it->count() ); + } } } + } } else if( spane.get_area() == AIM_WORN ) { // do this in reverse, to account for vector item removal messing with future indices diff --git a/src/advanced_inv_listitem.cpp b/src/advanced_inv_listitem.cpp index 77ebbd5139a94..b1b52c32286d3 100644 --- a/src/advanced_inv_listitem.cpp +++ b/src/advanced_inv_listitem.cpp @@ -16,7 +16,7 @@ advanced_inv_listitem::advanced_inv_listitem( item *an_item, int index, int coun , stacks( count ) , volume( an_item->volume() * stacks ) , weight( an_item->weight() * stacks ) - , cat( &an_item->get_category() ) + , cat( &an_item->get_category_of_contents() ) , from_vehicle( from_vehicle ) { items.push_back( an_item ); @@ -35,7 +35,7 @@ advanced_inv_listitem::advanced_inv_listitem( const std::vector &list, i stacks( list.size() ), volume( list.front()->volume() * stacks ), weight( list.front()->weight() * stacks ), - cat( &list.front()->get_category() ), + cat( &list.front()->get_category_of_contents() ), from_vehicle( from_vehicle ) { assert( stacks >= 1 ); diff --git a/src/animation.cpp b/src/animation.cpp index 9a43828fb16e5..42d3bb5bfeceb 100644 --- a/src/animation.cpp +++ b/src/animation.cpp @@ -84,7 +84,7 @@ class bullet_animation : public basic_animation bool is_point_visible( const tripoint &p, int margin = 0 ) { - return g->is_in_viewport( p, margin ) && g->u.sees( p ); + return g->is_in_viewport( p, margin ) && get_player_character().sees( p ); } bool is_radius_visible( const tripoint ¢er, int radius ) @@ -303,20 +303,21 @@ void explosion_handler::draw_custom_explosion( const tripoint &, // Start by getting rid of everything except current z-level std::map neighbors; + avatar &player_character = get_avatar(); #if defined(TILES) if( !use_tiles ) { for( const auto &pr : all_area ) { - const tripoint relative_point = relative_view_pos( g->u, pr.first ); + const tripoint relative_point = relative_view_pos( player_character, pr.first ); if( relative_point.z == 0 ) { neighbors[pr.first] = explosion_tile{ N_NO_NEIGHBORS, pr.second }; } } } else { // In tiles mode, the coordinates have to be absolute - const tripoint view_center = relative_view_pos( g->u, g->u.pos() ); + const tripoint view_center = relative_view_pos( player_character, player_character.pos() ); for( const auto &pr : all_area ) { // Relative point is only used for z level check - const tripoint relative_point = relative_view_pos( g->u, pr.first ); + const tripoint relative_point = relative_view_pos( player_character, pr.first ); if( relative_point.z == view_center.z ) { neighbors[pr.first] = explosion_tile{ N_NO_NEIGHBORS, pr.second }; } @@ -324,7 +325,7 @@ void explosion_handler::draw_custom_explosion( const tripoint &, } #else for( const auto &pr : all_area ) { - const tripoint relative_point = relative_view_pos( g->u, pr.first ); + const tripoint relative_point = relative_view_pos( player_character, pr.first ); if( relative_point.z == 0 ) { neighbors[pr.first] = explosion_tile{ N_NO_NEIGHBORS, pr.second }; } @@ -439,7 +440,8 @@ void draw_bullet_curses( map &m, const tripoint &t, const char bullet, const tri return; } - const tripoint vp = g->u.pos() + g->u.view_offset; + avatar &player_character = get_avatar(); + const tripoint vp = player_character.pos() + player_character.view_offset; if( vp.z != t.z ) { return; @@ -447,7 +449,7 @@ void draw_bullet_curses( map &m, const tripoint &t, const char bullet, const tri shared_ptr_fast bullet_cb = make_shared_fast( [&]() { if( p != nullptr && p->z == vp.z ) { - m.drawsq( g->w_terrain, g->u, *p, false, true, vp ); + m.drawsq( g->w_terrain, player_character, *p, false, true, vp ); } mvwputch( g->w_terrain, t.xy() - vp.xy() + point( POSX, POSY ), c_red, bullet ); } ); @@ -628,7 +630,7 @@ void draw_line_curses( game &g, const tripoint ¢er, const std::vector &points ) { + map &here = get_map(); for( const tripoint &p : points ) { - g.m.drawsq( g.w_terrain, g.u, p, true, true ); + here.drawsq( g.w_terrain, g.u, p, true, true ); } const tripoint p = points.empty() ? tripoint {POSX, POSY, 0} : @@ -733,36 +736,7 @@ void game::draw_weather( const weather_printable &w ) return; } - static const std::string weather_acid_drop {"weather_acid_drop"}; - static const std::string weather_rain_drop {"weather_rain_drop"}; - static const std::string weather_snowflake {"weather_snowflake"}; - - std::string weather_name; - switch( w.wtype ) { - // Acid weathers; uses acid droplet tile, fallthrough intended - case WEATHER_ACID_DRIZZLE: - case WEATHER_ACID_RAIN: - weather_name = weather_acid_drop; - break; - // Normal rainy weathers; uses normal raindrop tile, fallthrough intended - case WEATHER_LIGHT_DRIZZLE: - case WEATHER_DRIZZLE: - case WEATHER_RAINY: - case WEATHER_THUNDER: - case WEATHER_LIGHTNING: - weather_name = weather_rain_drop; - break; - // Snowy weathers; uses snowflake tile, fallthrough intended - case WEATHER_FLURRIES: - case WEATHER_SNOW: - case WEATHER_SNOWSTORM: - weather_name = weather_snowflake; - break; - default: - break; - } - - tilecontext->init_draw_weather( w, std::move( weather_name ) ); + tilecontext->init_draw_weather( w, w.wtype->tiles_animation ); } #else void game::draw_weather( const weather_printable &w ) diff --git a/src/armor_layers.cpp b/src/armor_layers.cpp index f3fc57eab24a2..8f8d539ce76b2 100644 --- a/src/armor_layers.cpp +++ b/src/armor_layers.cpp @@ -742,7 +742,7 @@ void player::sort_armor() } worn.splice( to, worn, tmp_worn[selected] ); selected = leftListIndex; - reset_encumbrance(); + calc_encumbrance(); } }; @@ -817,7 +817,7 @@ void player::sort_armor() } ); std::copy( worn_copy.begin(), worn_copy.end(), worn.begin() ); - reset_encumbrance(); + calc_encumbrance(); } else if( action == "EQUIP_ARMOR" ) { // filter inventory for all items that are armor/clothing item_location loc = game_menus::inv::wear( *this ); diff --git a/src/auto_pickup.cpp b/src/auto_pickup.cpp index ca2777e160844..7d8a4a917653e 100644 --- a/src/auto_pickup.cpp +++ b/src/auto_pickup.cpp @@ -6,8 +6,8 @@ #include #include -#include "avatar.h" #include "cata_utility.h" +#include "character.h" #include "color.h" #include "cursesdef.h" #include "debug.h" @@ -77,6 +77,7 @@ void user_interface::show() int iLine = 0; int iColumn = 1; int iStartPos = 0; + Character &player_character = get_player_character(); ui.on_redraw( [&]( const ui_adaptor & ) { // Redraw the border @@ -99,7 +100,7 @@ void user_interface::show() tmpx += shortcut_print( w_header, point( tmpx, 0 ), c_white, c_light_green, _( "ove" ) ) + 2; tmpx += shortcut_print( w_header, point( tmpx, 0 ), c_white, c_light_green, _( "nable" ) ) + 2; tmpx += shortcut_print( w_header, point( tmpx, 0 ), c_white, c_light_green, _( "isable" ) ) + 2; - if( !g->u.name.empty() ) { + if( !player_character.name.empty() ) { shortcut_print( w_header, point( tmpx, 0 ), c_white, c_light_green, _( "est" ) ); } tmpx = 0; @@ -363,7 +364,7 @@ void user_interface::show() iLine--; iColumn = 1; } - } else if( action == "TEST_RULE" && currentPageNonEmpty && !g->u.name.empty() ) { + } else if( action == "TEST_RULE" && currentPageNonEmpty && !player_character.name.empty() ) { cur_rules[iLine].test_pattern(); } else if( action == "SWITCH_AUTO_PICKUP_OPTION" ) { // TODO: Now that NPCs use this function, it could be used for them too @@ -389,9 +390,10 @@ void player_settings::show() { user_interface ui; + Character &player_character = get_player_character(); ui.title = _( " AUTO PICKUP MANAGER " ); ui.tabs.emplace_back( _( "[]" ), global_rules ); - if( !g->u.name.empty() ) { + if( !player_character.name.empty() ) { ui.tabs.emplace_back( _( "[]" ), character_rules ); } ui.is_autopickup = true; @@ -403,7 +405,7 @@ void player_settings::show() } save_global(); - if( !g->u.name.empty() ) { + if( !player_character.name.empty() ) { save_character(); } invalidate(); diff --git a/src/avatar.cpp b/src/avatar.cpp index 22ebcce09c737..311a47517c1a7 100644 --- a/src/avatar.cpp +++ b/src/avatar.cpp @@ -1270,7 +1270,8 @@ void avatar::reset_stats() // Dodge-related effects mod_dodge_bonus( mabuff_dodge_bonus() - - ( encumb( bp_leg_l ) + encumb( bp_leg_r ) ) / 20.0f - encumb( bp_torso ) / 10.0f ); + ( encumb( bodypart_id( "leg_l" ) ) + encumb( bodypart_id( "leg_r" ) ) ) / 20.0f - encumb( + bodypart_id( "torso" ) ) / 10.0f ); // Whiskers don't work so well if they're covered if( has_trait( trait_WHISKERS ) && !wearing_something_on( bodypart_id( "mouth" ) ) ) { mod_dodge_bonus( 1 ); diff --git a/src/avatar_action.cpp b/src/avatar_action.cpp index bd71cca6d6d68..e6d0224ae55ea 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; @@ -377,7 +380,7 @@ bool avatar_action::move( avatar &you, map &m, const tripoint &d ) add_msg( m_info, _( "%s to dive underwater." ), press_x( ACTION_MOVE_DOWN ) ); } - avatar_action::swim( get_map(), g->u, dest_loc ); + avatar_action::swim( get_map(), get_avatar(), dest_loc ); } g->on_move_effects(); @@ -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 ) ) { @@ -689,7 +692,7 @@ static bool gunmode_checks_weapon( avatar &you, const map &m, std::vectoru.mounted_creature.get(); + monster *mons = get_player_character().mounted_creature.get(); if( !mons->type->mech_weapon.is_empty() ) { is_mech_weapon = true; } @@ -960,7 +963,7 @@ void avatar_action::plthrow( avatar &you, item_location loc, return; } if( you.is_mounted() ) { - monster *mons = g->u.mounted_creature.get(); + monster *mons = get_player_character().mounted_creature.get(); if( mons->has_flag( MF_RIDEABLE_MECH ) ) { if( !mons->check_mech_powered() ) { add_msg( m_bad, _( "Your %s refuses to move as its batteries have been drained." ), diff --git a/src/ballistics.cpp b/src/ballistics.cpp index 5a8e7793a6216..f03aa4e8fc7f1 100644 --- a/src/ballistics.cpp +++ b/src/ballistics.cpp @@ -8,8 +8,8 @@ #include #include -#include "avatar.h" #include "calendar.h" +#include "character.h" #include "creature.h" #include "damage.h" #include "debug.h" @@ -49,10 +49,11 @@ static void drop_or_embed_projectile( const dealt_projectile_attack &attack ) } const tripoint &pt = attack.end_point; + Character &player_character = get_player_character(); if( effects.count( "SHATTER_SELF" ) ) { // Drop the contents, not the thrown item - if( g->u.sees( pt ) ) { + if( player_character.sees( pt ) ) { add_msg( _( "The %s shatters!" ), drop_item.tname() ); } @@ -70,7 +71,7 @@ static void drop_or_embed_projectile( const dealt_projectile_attack &attack ) if( effects.count( "BURST" ) ) { // Drop the contents, not the thrown item - if( g->u.sees( pt ) ) { + if( player_character.sees( pt ) ) { add_msg( _( "The %s bursts!" ), drop_item.tname() ); } @@ -107,7 +108,7 @@ static void drop_or_embed_projectile( const dealt_projectile_attack &attack ) if( embed ) { mon->add_item( dropped_item ); - if( g->u.sees( *mon ) ) { + if( player_character.sees( *mon ) ) { add_msg( _( "The %1$s embeds in %2$s!" ), dropped_item.tname(), mon->disp_name() ); } } else { @@ -138,7 +139,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/basecamp.cpp b/src/basecamp.cpp index 02031287c8751..e60190acf44c2 100644 --- a/src/basecamp.cpp +++ b/src/basecamp.cpp @@ -711,8 +711,10 @@ basecamp_action_components::basecamp_action_components( bool basecamp_action_components::choose_components() { const auto filter = is_crafting_component; + avatar &player_character = get_avatar(); const requirement_data *req = - making_.deduped_requirements().select_alternative( g->u, base_._inv, filter, batch_size_ ); + making_.deduped_requirements().select_alternative( player_character, base_._inv, filter, + batch_size_ ); if( !req ) { return false; } @@ -722,8 +724,8 @@ bool basecamp_action_components::choose_components() } for( const auto &it : req->get_components() ) { comp_selection is = - g->u.select_item_component( it, batch_size_, base_._inv, true, filter, - !base_.by_radio ); + player_character.select_item_component( it, batch_size_, base_._inv, true, filter, + !base_.by_radio ); if( is.use_from == usage_from::cancel ) { return false; } @@ -732,8 +734,8 @@ bool basecamp_action_components::choose_components() // this may consume pseudo-resources from fake items for( const auto &it : req->get_tools() ) { comp_selection ts = - g->u.select_tool_component( it, batch_size_, base_._inv, DEFAULT_HOTKEYS, true, - !base_.by_radio ); + player_character.select_tool_component( it, batch_size_, base_._inv, DEFAULT_HOTKEYS, true, + !base_.by_radio ); if( ts.use_from == usage_from::cancel ) { return false; } @@ -751,13 +753,15 @@ void basecamp_action_components::consume_components() target_map = map_.get(); } const tripoint &origin = target_map->getlocal( base_.get_dumping_spot() ); + avatar &player_character = get_avatar(); for( const comp_selection &sel : item_selections_ ) { - g->u.consume_items( *target_map, sel, batch_size_, is_crafting_component, origin, - basecamp::inv_range ); + player_character.consume_items( *target_map, sel, batch_size_, is_crafting_component, origin, + basecamp::inv_range ); } // this may consume pseudo-resources from fake items for( const comp_selection &sel : tool_selections_ ) { - g->u.consume_tools( *target_map, sel, batch_size_, origin, basecamp::inv_range, &base_ ); + player_character.consume_tools( *target_map, sel, batch_size_, origin, basecamp::inv_range, + &base_ ); } // go back and consume the actual resources for( basecamp_resource &bcp_r : base_.resources ) { diff --git a/src/behavior.cpp b/src/behavior.cpp index 92e18fcf4c7a8..ef301ed09572b 100644 --- a/src/behavior.cpp +++ b/src/behavior.cpp @@ -36,7 +36,7 @@ behavior_return node_t::tick( const oracle_t *subject ) const { if( children.empty() ) { status_t result = status_t::running; - for( std::pair< predicate_type, std::string > predicate_pair : conditions ) { + for( const std::pair< predicate_type, std::string > &predicate_pair : conditions ) { result = predicate_pair.first( subject, predicate_pair.second ); if( result != status_t::running ) { break; @@ -46,7 +46,7 @@ behavior_return node_t::tick( const oracle_t *subject ) const } else { assert( strategy != nullptr ); status_t result = status_t::running; - for( std::pair< predicate_type, std::string > predicate_pair : conditions ) { + for( const std::pair< predicate_type, std::string > &predicate_pair : conditions ) { result = predicate_pair.first( subject, predicate_pair.second ); if( result != status_t::running ) { break; diff --git a/src/bionics.cpp b/src/bionics.cpp index 1f8bdd8a35aea..fc868f2695968 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 ); } @@ -644,7 +644,7 @@ bool Character::activate_bionic( int b, bool eff_only, bool *close_bionics_ui ) } else if( bio.id == bio_evap ) { add_msg_activate(); const w_point weatherPoint = *g->weather.weather_precise; - int humidity = get_local_humidity( weatherPoint.humidity, g->weather.weather, + int humidity = get_local_humidity( weatherPoint.humidity, get_weather().weather_id, g->is_sheltered( g->u.pos() ) ); // thirst units = 5 mL int water_available = std::lround( humidity * 3.0 / 100.0 ); @@ -1011,7 +1011,7 @@ bool Character::activate_bionic( int b, bool eff_only, bool *close_bionics_ui ) const w_point weatherPoint = *g->weather.weather_precise; add_msg_if_player( m_info, _( "Relative Humidity: %s." ), print_humidity( - get_local_humidity( weatherPoint.humidity, g->weather.weather, + get_local_humidity( weatherPoint.humidity, get_weather().weather_id, g->is_sheltered( g->u.pos() ) ) ) ); add_msg_if_player( m_info, _( "Pressure: %s." ), print_pressure( static_cast( weatherPoint.pressure ) ) ); @@ -1117,7 +1117,7 @@ bool Character::activate_bionic( int b, bool eff_only, bool *close_bionics_ui ) } // Recalculate stats (strength, mods from pain etc.) that could have been affected - reset_encumbrance(); + calc_encumbrance(); reset(); // Also reset crafting inventory cache if this bionic spawned a fake item @@ -1195,7 +1195,7 @@ bool Character::deactivate_bionic( int b, bool eff_only ) } // Recalculate stats (strength, mods from pain etc.) that could have been affected - reset_encumbrance(); + calc_encumbrance(); reset(); if( !bio.id->enchantments.empty() ) { recalculate_enchantment_cache(); @@ -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." ), @@ -1303,7 +1306,7 @@ bool Character::burn_fuel( int b, bool start ) mod_power_level( power_gain ); } else if( is_perpetual_fuel ) { if( fuel == fuel_type_sun_light && g->is_in_sunlight( pos() ) ) { - const weather_type &wtype = current_weather( pos() ); + const weather_type_id &wtype = current_weather( pos() ); const float tick_sunlight = incident_sunlight( wtype, calendar::turn ); const double intensity = tick_sunlight / default_daylight_level(); mod_power_level( units::from_kilojoule( fuel_energy ) * intensity * effective_efficiency ); @@ -1734,8 +1737,8 @@ void Character::process_bionic( int b ) // Aero-Evaporator provides water at 60 watts with 2 L / kWh efficiency // which is 10 mL per 5 minutes. Humidity can modify the amount gained. if( calendar::once_every( 5_minutes ) ) { - const w_point weatherPoint = *g->weather.weather_precise; - int humidity = get_local_humidity( weatherPoint.humidity, g->weather.weather, + const w_point weatherPoint = *get_weather().weather_precise; + int humidity = get_local_humidity( weatherPoint.humidity, get_weather().weather_id, g->is_sheltered( g->u.pos() ) ); // in thirst units = 5 mL water int water_available = std::lround( humidity * 3.0 / 100.0 ); @@ -2701,7 +2704,7 @@ void Character::add_bionic( const bionic_id &b ) } } - reset_encumbrance(); + calc_encumbrance(); recalc_sight_limits(); if( !b->enchantments.empty() ) { recalculate_enchantment_cache(); @@ -2738,7 +2741,7 @@ void Character::remove_bionic( const bionic_id &b ) } *my_bionics = new_my_bionics; - reset_encumbrance(); + calc_encumbrance(); recalc_sight_limits(); if( !b->enchantments.empty() ) { recalculate_enchantment_cache(); @@ -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 %%)" ), diff --git a/src/bodypart.cpp b/src/bodypart.cpp index cda9a32201a7b..15f4f7c20e49c 100644 --- a/src/bodypart.cpp +++ b/src/bodypart.cpp @@ -52,25 +52,6 @@ std::string enum_to_string( side data ) abort(); } -template<> -std::string enum_to_string( hp_part data ) -{ - switch( data ) { - // *INDENT-OFF* - case hp_part::hp_head: return "head"; - case hp_part::hp_torso: return "torso"; - case hp_part::hp_arm_l: return "arm_l"; - case hp_part::hp_arm_r: return "arm_r"; - case hp_part::hp_leg_l: return "leg_l"; - case hp_part::hp_leg_r: return "leg_r"; - // *INDENT-ON* - case hp_part::num_hp_parts: - break; - } - debugmsg( "Invalid hp_part" ); - abort(); -} - } // namespace io namespace @@ -220,6 +201,8 @@ void body_part_type::load( const JsonObject &jo, const std::string & ) mandatory( jo, was_loaded, "base_hp", base_hp ); optional( jo, was_loaded, "stat_hp_mods", hp_mods ); + optional( jo, was_loaded, "is_limb", is_limb, false ); + mandatory( jo, was_loaded, "legacy_id", legacy_id ); token = legacy_id_to_enum( legacy_id ); @@ -425,6 +408,11 @@ int bodypart::get_damage_disinfected() const return damage_disinfected; } +encumbrance_data bodypart::get_encumbrance_data() const +{ + return encumb_data; +} + void bodypart::set_hp_cur( int set ) { hp_cur = set; @@ -450,6 +438,11 @@ void bodypart::set_damage_disinfected( int set ) damage_disinfected = set; } +void bodypart::set_encumbrance_data( encumbrance_data set ) +{ + encumb_data = set; +} + void bodypart::mod_hp_cur( int mod ) { hp_cur += mod; diff --git a/src/bodypart.h b/src/bodypart.h index 78c4eb7ec32ac..14e8a5bd61f65 100644 --- a/src/bodypart.h +++ b/src/bodypart.h @@ -8,6 +8,7 @@ #include #include +#include "enums.h" #include "flat_set.h" #include "int_id.h" #include "string_id.h" @@ -129,6 +130,8 @@ struct body_part_type { int base_hp = 60; stat_hp_mods hp_mods; + bool is_limb = false; + void load( const JsonObject &jo, const std::string &src ); void finalize(); void check() const; @@ -149,6 +152,46 @@ struct body_part_type { int bionic_slots_ = 0; }; +struct layer_details { + + std::vector pieces; + int max = 0; + int total = 0; + + void reset(); + int layer( int encumbrance ); + + bool operator ==( const layer_details &rhs ) const { + return max == rhs.max && + total == rhs.total && + pieces == rhs.pieces; + } +}; + +struct encumbrance_data { + int encumbrance = 0; + int armor_encumbrance = 0; + int layer_penalty = 0; + + std::array( layer_level::NUM_LAYER_LEVELS )> + layer_penalty_details; + + void layer( const layer_level level, const int encumbrance ) { + layer_penalty += layer_penalty_details[static_cast( level )].layer( encumbrance ); + } + + void reset() { + *this = encumbrance_data(); + } + + bool operator ==( const encumbrance_data &rhs ) const { + return encumbrance == rhs.encumbrance && + armor_encumbrance == rhs.armor_encumbrance && + layer_penalty == rhs.layer_penalty && + layer_penalty_details == rhs.layer_penalty_details; + } +}; + class bodypart { private: @@ -161,6 +204,8 @@ class bodypart int damage_bandaged = 0; int damage_disinfected = 0; + encumbrance_data encumb_data; + public: bodypart(): id( bodypart_str_id( "num_bp" ) ), hp_cur( 0 ), hp_max( 0 ) {} bodypart( bodypart_str_id id ): id( id ), hp_cur( id->base_hp ), hp_max( id->base_hp ) {} @@ -176,12 +221,16 @@ class bodypart int get_damage_bandaged() const; int get_damage_disinfected() const; + encumbrance_data get_encumbrance_data() const; + void set_hp_cur( int set ); void set_hp_max( int set ); void set_healed_total( int set ); void set_damage_bandaged( int set ); void set_damage_disinfected( int set ); + void set_encumbrance_data( encumbrance_data set ); + void mod_hp_cur( int mod ); void mod_hp_max( int mod ); void mod_healed_total( int mod ); diff --git a/src/cata_tiles.cpp b/src/cata_tiles.cpp index d058232b725ff..e7461cf12d62c 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; } @@ -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. @@ -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/cata_variant.cpp b/src/cata_variant.cpp index d8725ffffe294..d715ff572ce6c 100644 --- a/src/cata_variant.cpp +++ b/src/cata_variant.cpp @@ -18,7 +18,6 @@ std::string enum_to_string( cata_variant_type type ) case cata_variant_type::chrono_seconds: return "chrono_seconds"; case cata_variant_type::debug_menu_index: return "debug_menu_index"; case cata_variant_type::efftype_id: return "efftype_id"; - case cata_variant_type::hp_part: return "hp_part"; case cata_variant_type::int_: return "int"; case cata_variant_type::itype_id: return "itype_id"; case cata_variant_type::matype_id: return "matype_id"; diff --git a/src/cata_variant.h b/src/cata_variant.h index d3b020f05bcc3..9fbbd39cee4bc 100644 --- a/src/cata_variant.h +++ b/src/cata_variant.h @@ -24,7 +24,6 @@ template struct enum_traits; enum body_part : int; enum class mutagen_technique : int; -enum hp_part : int; namespace debug_menu { @@ -45,7 +44,6 @@ enum class cata_variant_type : int { chrono_seconds, debug_menu_index, efftype_id, - hp_part, int_, itype_id, matype_id, @@ -167,7 +165,7 @@ struct convert_enum { }; // These are the specializations of convert for each value type. -static_assert( static_cast( cata_variant_type::num_types ) == 30, +static_assert( static_cast( cata_variant_type::num_types ) == 29, "This assert is a reminder to add conversion support for any new types to the " "below specializations" ); @@ -230,9 +228,6 @@ struct convert : convert_string_id struct convert : convert_string_id {}; -template<> -struct convert : convert_enum {}; - template<> struct convert { using type = int; diff --git a/src/character.cpp b/src/character.cpp index a46065613732e..d3e38beb6af19 100644 --- a/src/character.cpp +++ b/src/character.cpp @@ -591,7 +591,7 @@ int Character::effective_dispersion( int dispersion ) const /** @EFFECT_PER penalizes sight dispersion when low. */ dispersion += ranged_per_mod(); - dispersion += encumb( bp_eyes ) / 2; + dispersion += encumb( bodypart_id( "eyes" ) ) / 2; return std::max( dispersion, 0 ); } @@ -660,7 +660,7 @@ double Character::aim_speed_dex_modifier() const double Character::aim_speed_encumbrance_modifier() const { - return ( encumb( bp_hand_l ) + encumb( bp_hand_r ) ) / 10.0; + return ( encumb( bodypart_id( "hand_l" ) ) + encumb( bodypart_id( "hand_r" ) ) ) / 10.0; } double Character::aim_cap_from_volume( const item &gun ) const @@ -942,9 +942,10 @@ int Character::swim_speed() const ret -= hand_bonus_mult * ( 60 + str_cur * 5 ); } /** @EFFECT_SWIMMING increases swim speed */ - ret += ( 50 - get_skill_level( skill_swimming ) * 2 ) * ( ( encumb( bp_leg_l ) + encumb( - bp_leg_r ) ) / 10 ); - ret += ( 80 - get_skill_level( skill_swimming ) * 3 ) * ( encumb( bp_torso ) / 10 ); + ret += ( 50 - get_skill_level( skill_swimming ) * 2 ) * ( ( encumb( bodypart_id( "leg_l" ) ) + + encumb( + bodypart_id( "leg_r" ) ) ) / 10 ); + ret += ( 80 - get_skill_level( skill_swimming ) * 3 ) * ( encumb( bodypart_id( "torso" ) ) / 10 ); if( get_skill_level( skill_swimming ) < 10 ) { for( auto &i : worn ) { ret += i.volume() / 125_ml * ( 10 - get_skill_level( skill_swimming ) ); @@ -1876,7 +1877,7 @@ float Character::get_vision_threshold( float light_level ) const LIGHT_AMBIENT_MINIMAL ) / ( LIGHT_AMBIENT_LIT - LIGHT_AMBIENT_MINIMAL ) ); - float range = get_per() / 3.0f - encumb( bp_eyes ) / 10.0f; + float range = get_per() / 3.0f - encumb( bodypart_id( "eyes" ) ) / 10.0f; if( vision_mode_cache[NV_GOGGLES] || vision_mode_cache[NIGHTVISION_3] || vision_mode_cache[FULL_ELFA_VISION] || vision_mode_cache[CEPH_VISION] ) { range += 10; @@ -1914,7 +1915,7 @@ void Character::check_item_encumbrance_flag() } if( update_required ) { - reset_encumbrance(); + calc_encumbrance(); } } @@ -2203,7 +2204,8 @@ int Character::get_standard_stamina_cost( item *thrown_item ) //If the item is thrown, override with the thrown item instead. const int weight_cost = ( thrown_item == nullptr ) ? this->weapon.weight() / ( 16_gram ) : thrown_item->weight() / ( 16_gram ); - const int encumbrance_cost = this->encumb( bp_arm_l ) + this->encumb( bp_arm_r ); + const int encumbrance_cost = this->encumb( bodypart_id( "arm_l" ) ) + this->encumb( + bodypart_id( "arm_r" ) ); return ( weight_cost + encumbrance_cost + 50 ) * -1; } @@ -2236,7 +2238,7 @@ cata::optional::iterator> Character::wear_item( const item &to_w moves -= item_wear_cost( to_wear ); for( const bodypart_id &bp : get_all_body_parts() ) { - if( to_wear.covers( bp ) && encumb( bp->token ) >= 40 ) { + if( to_wear.covers( bp ) && encumb( bp ) >= 40 ) { add_msg_if_player( m_warning, bp == bodypart_id( "eyes" ) ? _( "Your %s are very encumbered! %s" ) : _( "Your %s is very encumbered! %s" ), @@ -2265,7 +2267,7 @@ cata::optional::iterator> Character::wear_item( const item &to_w inv.update_cache_with_item( *new_item_it ); recalc_sight_limits(); - reset_encumbrance(); + calc_encumbrance(); return new_item_it; } @@ -3613,25 +3615,22 @@ bool Character::has_nv() return nv; } -void Character::reset_encumbrance() +void Character::calc_encumbrance() { - encumbrance_cache_dirty = true; + calc_encumbrance( item() ); } -std::array Character::calc_encumbrance() const -{ - return calc_encumbrance( item() ); -} - -std::array Character::calc_encumbrance( const item &new_item ) const +void Character::calc_encumbrance( const item &new_item ) { - std::array enc; - + std::map enc; item_encumb( enc, new_item ); mut_cbm_encumb( enc ); - return enc; + for( const std::pair &elem : enc ) { + set_part_encumbrance_data( elem.first, elem.second ); + } + } units::mass Character::get_weight() const @@ -3650,29 +3649,6 @@ units::mass Character::get_weight() const return ret; } -std::array Character::get_encumbrance() const -{ - if( encumbrance_cache_dirty ) { - encumbrance_cache = calc_encumbrance(); - encumbrance_cache_dirty = false; - } - return encumbrance_cache; -} - -std::array Character::get_encumbrance( const item &new_item ) const -{ - return calc_encumbrance( new_item ); -} - -int Character::extraEncumbrance( const layer_level level, const int bp ) const -{ - if( encumbrance_cache_dirty ) { - encumbrance_cache = calc_encumbrance(); - encumbrance_cache_dirty = false; - } - return encumbrance_cache[bp].layer_penalty_details[static_cast( level )].total; -} - bool Character::change_side( item &it, bool interactive ) { if( !it.swap_side() ) { @@ -3692,7 +3668,7 @@ bool Character::change_side( item &it, bool interactive ) } mod_moves( -250 ); - reset_encumbrance(); + calc_encumbrance(); return true; } @@ -3711,12 +3687,10 @@ bool Character::change_side( item_location &loc, bool interactive ) return change_side( *loc, interactive ); } -static void layer_item( std::array &vals, - const item &it, - std::array &highest_layer_so_far, - bool power_armor, const Character &c ) +static void layer_item( std::map &vals, const item &it, + std::map &highest_layer_so_far, bool power_armor, const Character &c ) { - const auto item_layer = it.get_layer(); + const layer_level item_layer = it.get_layer(); int encumber_val = it.get_encumber( c ); // For the purposes of layering penalty, set a min of 2 and a max of 10 per item. int layering_encumbrance = std::min( 10, std::max( 2, encumber_val ) ); @@ -3739,17 +3713,16 @@ static void layer_item( std::array &vals, if( !covered_parts.test( bp.id() ) ) { continue; } - highest_layer_so_far[bp->token] = - std::max( highest_layer_so_far[bp->token], item_layer ); + highest_layer_so_far[bp] = std::max( highest_layer_so_far[bp], item_layer ); // Apply layering penalty to this layer, as well as any layer worn // within it that would normally be worn outside of it. for( layer_level penalty_layer = item_layer; - penalty_layer <= highest_layer_so_far[bp->token]; ++penalty_layer ) { - vals[bp->token].layer( penalty_layer, layering_encumbrance ); + penalty_layer <= highest_layer_so_far[bp]; ++penalty_layer ) { + vals[bp].layer( penalty_layer, layering_encumbrance ); } - vals[bp->token].armor_encumbrance += armorenc; + vals[bp].armor_encumbrance += armorenc; } } @@ -3934,12 +3907,12 @@ std::list::iterator Character::position_to_wear_new_item( const item &new_ * This is currently handled by each of these articles of clothing * being on a different layer and/or body part, therefore accumulating no encumbrance. */ -void Character::item_encumb( std::array &vals, +void Character::item_encumb( std::map &vals, const item &new_item ) const { // reset all layer data - vals = std::array(); + vals = std::map(); // Figure out where new_item would be worn std::list::const_iterator new_item_position = worn.end(); @@ -3952,8 +3925,7 @@ void Character::item_encumb( std::array &vals, // Track highest layer observed so far so we can penalize out-of-order // items - std::array highest_layer_so_far; - std::fill( highest_layer_so_far.begin(), highest_layer_so_far.end(), layer_level::PERSONAL ); + std::map highest_layer_so_far; const bool power_armored = is_wearing_active_power_armor(); for( auto w_it = worn.begin(); w_it != worn.end(); ++w_it ) { @@ -3968,7 +3940,7 @@ void Character::item_encumb( std::array &vals, } // make sure values are sane - for( const body_part bp : all_body_parts ) { + for( const bodypart_id &bp : get_all_body_parts() ) { encumbrance_data &elem = vals[bp]; elem.armor_encumbrance = std::max( 0, elem.armor_encumbrance ); @@ -3978,44 +3950,40 @@ void Character::item_encumb( std::array &vals, } } -int Character::encumb( body_part bp ) const +int Character::encumb( const bodypart_id &bp ) const { - if( encumbrance_cache_dirty ) { - encumbrance_cache = calc_encumbrance(); - encumbrance_cache_dirty = false; - } - return encumbrance_cache[bp].encumbrance; + return get_part_encumbrance_data( bp ).encumbrance; } -static void apply_mut_encumbrance( std::array &vals, +static void apply_mut_encumbrance( std::map &vals, const trait_id &mut, const body_part_set &oversize ) { for( const std::pair &enc : mut->encumbrance_always ) { - vals[enc.first->token].encumbrance += enc.second; + vals[enc.first.id()].encumbrance += enc.second; } for( const std::pair &enc : mut->encumbrance_covered ) { if( !oversize.test( enc.first ) ) { - vals[enc.first->token].encumbrance += enc.second; + vals[enc.first.id()].encumbrance += enc.second; } } } -void Character::mut_cbm_encumb( std::array &vals ) const +void Character::mut_cbm_encumb( std::map &vals ) const { for( const bionic_id &bid : get_bionics() ) { for( const std::pair &element : bid->encumbrance ) { - vals[element.first->token].encumbrance += element.second; + vals[element.first.id()].encumbrance += element.second; } } if( has_active_bionic( bio_shock_absorber ) ) { - for( auto &val : vals ) { - val.encumbrance += 3; // Slight encumbrance to all parts except eyes + for( std::pair &val : vals ) { + val.second.encumbrance += 3; // Slight encumbrance to all parts except eyes } - vals[bp_eyes].encumbrance -= 3; + vals[bodypart_id( "eyes" )].encumbrance -= 3; } // Lower penalty for bps covered only by XL armor @@ -5446,9 +5414,11 @@ void Character::update_bodytemp() -1.5f * get_fatigue() ) ); // Sunlight - const int sunlight_warmth = g->is_in_sunlight( pos() ) ? ( g->weather.weather == WEATHER_SUNNY ? - 1000 : - 500 ) : 0; + const int sunlight_warmth = g->is_in_sunlight( pos() ) ? + ( get_weather().weather_id->sun_intensity == + sun_intensity_type::high ? + 1000 : + 500 ) : 0; const int best_fire = get_heat_radiation( pos(), true ); const int lying_warmth = use_floor_warmth ? floor_warmth( pos() ) : 0; @@ -5489,8 +5459,7 @@ void Character::update_bodytemp() bp ) / 100.0 ) ); // Calculate windchill int windchill = get_local_windchill( player_local_temp, - get_local_humidity( weather.humidity, g->weather.weather, - sheltered ), + get_local_humidity( weather.humidity, get_weather().weather_id, sheltered ), bp_windpower ); // If you're standing in water, air temperature is replaced by water temperature. No wind. // Convert to 0.01C @@ -6067,10 +6036,10 @@ float Character::get_hit_base() const return get_dex() / 4.0f; } -hp_part Character::body_window( const std::string &menu_header, - bool show_all, bool precise, - int normal_bonus, int head_bonus, int torso_bonus, - float bleed, float bite, float infect, float bandage_power, float disinfectant_power ) const +bodypart_id Character::body_window( const std::string &menu_header, + bool show_all, bool precise, + int normal_bonus, int head_bonus, int torso_bonus, + float bleed, float bite, float infect, float bandage_power, float disinfectant_power ) const { /* This struct establishes some kind of connection between the hp_part (which can be healed and * have HP) and the body_part. Note that there are more body_parts than hp_parts. For example: @@ -6078,24 +6047,23 @@ hp_part Character::body_window( const std::string &menu_header, struct healable_bp { mutable bool allowed; bodypart_id bp; - hp_part hp; std::string name; // Translated name as it appears in the menu. int bonus; }; /* The array of the menu entries show to the player. The entries are displayed in this order, * it may be changed here. */ - std::array parts = { { - { false, bodypart_id( "head" ), hp_head, _( "Head" ), head_bonus }, - { false, bodypart_id( "torso" ), hp_torso, _( "Torso" ), torso_bonus }, - { false, bodypart_id( "arm_l" ), hp_arm_l, _( "Left Arm" ), normal_bonus }, - { false, bodypart_id( "arm_r" ), hp_arm_r, _( "Right Arm" ), normal_bonus }, - { false, bodypart_id( "leg_l" ), hp_leg_l, _( "Left Leg" ), normal_bonus }, - { false, bodypart_id( "leg_r" ), hp_leg_r, _( "Right Leg" ), normal_bonus }, + std::array parts = { { + { false, bodypart_id( "head" ), _( "Head" ), head_bonus }, + { false, bodypart_id( "torso" ), _( "Torso" ), torso_bonus }, + { false, bodypart_id( "arm_l" ), _( "Left Arm" ), normal_bonus }, + { false, bodypart_id( "arm_r" ), _( "Right Arm" ), normal_bonus }, + { false, bodypart_id( "leg_l" ), _( "Left Leg" ), normal_bonus }, + { false, bodypart_id( "leg_r" ), _( "Right Leg" ), normal_bonus }, } }; int max_bp_name_len = 0; - for( const auto &e : parts ) { + for( const healable_bp &e : parts ) { max_bp_name_len = std::max( max_bp_name_len, utf8_width( e.name ) ); } @@ -6107,7 +6075,7 @@ hp_part Character::body_window( const std::string &menu_header, bool is_valid_choice = false; for( size_t i = 0; i < parts.size(); i++ ) { - const auto &e = parts[i]; + const healable_bp &e = parts[i]; const bodypart_id &bp = e.bp; const body_part bp_token = bp->token; const int maximal_hp = get_part_hp_max( bp ); @@ -6280,9 +6248,9 @@ hp_part Character::body_window( const std::string &menu_header, bmenu.query(); if( bmenu.ret >= 0 && static_cast( bmenu.ret ) < parts.size() && parts[bmenu.ret].allowed ) { - return parts[bmenu.ret].hp; + return parts[bmenu.ret].bp; } else { - return num_hp_parts; + return bodypart_id( "num_bp" ); } } @@ -6868,52 +6836,6 @@ float Character::rest_quality() const return has_effect( effect_sleep ) ? 1.0f : 0.0f; } -hp_part Character::bp_to_hp( const body_part bp ) -{ - switch( bp ) { - case bp_head: - case bp_eyes: - case bp_mouth: - return hp_head; - case bp_torso: - return hp_torso; - case bp_arm_l: - case bp_hand_l: - return hp_arm_l; - case bp_arm_r: - case bp_hand_r: - return hp_arm_r; - case bp_leg_l: - case bp_foot_l: - return hp_leg_l; - case bp_leg_r: - case bp_foot_r: - return hp_leg_r; - default: - return num_hp_parts; - } -} - -body_part Character::hp_to_bp( const hp_part hpart ) -{ - switch( hpart ) { - case hp_head: - return bp_head; - case hp_torso: - return bp_torso; - case hp_arm_l: - return bp_arm_l; - case hp_arm_r: - return bp_arm_r; - case hp_leg_l: - return bp_leg_l; - case hp_leg_r: - return bp_leg_r; - default: - return num_bp; - } -} - std::string Character::extended_description() const { std::string ss; @@ -7624,7 +7546,7 @@ void Character::update_stamina( int turns ) mutation_value( stamina_regen_modifier ) + ( mutation_value( "max_stamina_modifier" ) - 1.0f ) ); // But mouth encumbrance interferes, even with mutated stamina. stamina_recovery += stamina_multiplier * std::max( 1.0f, - base_regen_rate - ( encumb( bp_mouth ) / 5.0f ) ); + base_regen_rate - ( encumb( bodypart_id( "mouth" ) ) / 5.0f ) ); // TODO: recovering stamina causes hunger/thirst/fatigue. // TODO: Tiredness slowing recovery @@ -7698,7 +7620,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" ) { @@ -7900,9 +7821,9 @@ int Character::item_handling_cost( const item &it, bool penalties, int base_cost // For single handed items use the least encumbered hand if( it.is_two_handed( *this ) ) { - mv += encumb( bp_hand_l ) + encumb( bp_hand_r ); + mv += encumb( bodypart_id( "hand_l" ) ) + encumb( bodypart_id( "hand_r" ) ); } else { - mv += std::min( encumb( bp_hand_l ), encumb( bp_hand_r ) ); + mv += std::min( encumb( bodypart_id( "hand_l" ) ), encumb( bodypart_id( "hand_r" ) ) ); } return std::max( mv, 0 ); @@ -8029,7 +7950,7 @@ int Character::get_shout_volume() const // Balanced around whisper for wearing bondage mask // and noise ~= 10 (door smashing) for wearing dust mask for character with strength = 8 /** @EFFECT_STR increases shouting volume */ - const int penalty = encumb( bp_mouth ) * 3 / 2; + const int penalty = encumb( bodypart_id( "mouth" ) ) * 3 / 2; int noise = base + str_cur * shout_multiplier - penalty; // Minimum noise volume possible after all reductions. @@ -8097,7 +8018,7 @@ void Character::shout( std::string msg, bool order ) } } - const int penalty = encumb( bp_mouth ) * 3 / 2; + const int penalty = encumb( bodypart_id( "mouth" ) ) * 3 / 2; // TODO: indistinct noise descriptions should be handled in the sounds code if( noise <= minimum_noise ) { add_msg_if_player( m_warning, @@ -8159,22 +8080,30 @@ 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 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 + 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 ); } } @@ -9708,12 +9637,12 @@ int Character::bonus_item_warmth( const bodypart_id &bp ) const } // If the player's head is not encumbered, check if hood can be put up - if( bp == bodypart_id( "head" ) && encumb( bp_head ) < 10 ) { + if( bp == bodypart_id( "head" ) && encumb( bodypart_id( "head" ) ) < 10 ) { ret += bestwarmth( worn, "HOOD" ); } // If the player's mouth is not encumbered, check if collar can be put up - if( bp == bodypart_id( "mouth" ) && encumb( bp_mouth ) < 10 ) { + if( bp == bodypart_id( "mouth" ) && encumb( bodypart_id( "mouth" ) ) < 10 ) { ret += bestwarmth( worn, "COLLAR" ); } @@ -10447,8 +10376,8 @@ int Character::run_cost( int base_cost, bool diag ) const } movecost += - ( ( encumb( bp_foot_l ) + encumb( bp_foot_r ) ) * 2.5 + - ( encumb( bp_leg_l ) + encumb( bp_leg_r ) ) * 1.5 ) / 10; + ( ( encumb( bodypart_id( "foot_l" ) ) + encumb( bodypart_id( "foot_r" ) ) ) * 2.5 + + ( encumb( bodypart_id( "leg_l" ) ) + encumb( bodypart_id( "leg_r" ) ) ) * 1.5 ) / 10; // ROOTS3 does slow you down as your roots are probing around for nutrients, // whether you want them to or not. ROOTS1 is just too squiggly without shoes @@ -10651,7 +10580,7 @@ bool Character::can_hear( const tripoint &source, const int volume ) const } const int dist = rl_dist( source, pos() ); const float volume_multiplier = hearing_ability(); - return ( volume - weather::sound_attn( g->weather.weather ) ) * volume_multiplier >= dist; + return ( volume - get_weather().weather_id->sound_attn ) * volume_multiplier >= dist; } float Character::hearing_ability() const @@ -10782,6 +10711,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..cd1b86e95010e 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; @@ -226,46 +227,6 @@ enum class rechargeable_cbm : int { other }; -struct layer_details { - - std::vector pieces; - int max = 0; - int total = 0; - - void reset(); - int layer( int encumbrance ); - - bool operator ==( const layer_details &rhs ) const { - return max == rhs.max && - total == rhs.total && - pieces == rhs.pieces; - } -}; - -struct encumbrance_data { - int encumbrance = 0; - int armor_encumbrance = 0; - int layer_penalty = 0; - - std::array( layer_level::NUM_LAYER_LEVELS )> - layer_penalty_details; - - void layer( const layer_level level, const int encumbrance ) { - layer_penalty += layer_penalty_details[static_cast( level )].layer( encumbrance ); - } - - void reset() { - *this = encumbrance_data(); - } - - bool operator ==( const encumbrance_data &rhs ) const { - return encumbrance == rhs.encumbrance && - armor_encumbrance == rhs.armor_encumbrance && - layer_penalty == rhs.layer_penalty && - layer_penalty_details == rhs.layer_penalty_details; - } -}; - struct aim_type { std::string name; std::string action; @@ -345,6 +306,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, @@ -583,19 +546,11 @@ class Character : public Creature, public visitable /** Handles stat and bonus reset. */ void reset() override; - /** Recalculates encumbrance cache. */ - void reset_encumbrance(); /** Returns ENC provided by armor, etc. */ - int encumb( body_part bp ) const; + int encumb( const bodypart_id &bp ) const; /** Returns body weight plus weight of inventory and worn/wielded items */ units::mass get_weight() const override; - /** Get encumbrance for all body parts. */ - std::array get_encumbrance() const; - /** Get encumbrance for all body parts as if `new_item` was also worn. */ - std::array get_encumbrance( const item &new_item ) const; - /** Get encumbrance penalty per layer & body part */ - int extraEncumbrance( layer_level level, int bp ) const; /** Returns true if the character is wearing power armor */ bool is_wearing_power_armor( bool *hasHelmet = nullptr ) const; @@ -850,11 +805,6 @@ class Character : public Creature, public visitable void activate_mutation( const trait_id &mutation ); void deactivate_mutation( const trait_id &mut ); - /** Converts a body_part to an hp_part */ - static hp_part bp_to_hp( body_part bp ); - /** Converts an hp_part to a body_part */ - static body_part hp_to_bp( hp_part hpart ); - bool can_mount( const monster &critter ) const; void mount_creature( monster &z ); bool is_mounted() const; @@ -884,7 +834,7 @@ class Character : public Creature, public visitable void on_hurt( Creature *source, bool disturb = true ); /** Heals a body_part for dam */ void heal_bp( bodypart_id bp, int dam ) override; - /** Heals an hp_part for dam */ + /** Heals an part for dam */ void heal( const bodypart_id &healed, int dam ); /** Heals all body parts for dam */ void healall( int dam ); @@ -907,10 +857,10 @@ class Character : public Creature, public visitable * bandage_power - quality of bandage * disinfectant_power - quality of disinfectant */ - hp_part body_window( const std::string &menu_header, - bool show_all, bool precise, - int normal_bonus, int head_bonus, int torso_bonus, - float bleed, float bite, float infect, float bandage_power, float disinfectant_power ) const; + bodypart_id body_window( const std::string &menu_header, + bool show_all, bool precise, + int normal_bonus, int head_bonus, int torso_bonus, + float bleed, float bite, float infect, float bandage_power, float disinfectant_power ) const; // Returns color which this limb would have in healing menus nc_color limb_color( const bodypart_id &bp, bool bleed, bool bite, bool infect ) const; @@ -978,13 +928,8 @@ class Character : public Creature, public visitable /** Applies stat mods to character. */ void apply_mods( const trait_id &mut, bool add_remove ); - /** Recalculate encumbrance for all body parts. */ - std::array calc_encumbrance() const; - /** Recalculate encumbrance for all body parts as if `new_item` was also worn. */ - std::array calc_encumbrance( const item &new_item ) const; - /** Applies encumbrance from mutations and bionics only */ - void mut_cbm_encumb( std::array &vals ) const; + void mut_cbm_encumb( std::map &vals ) const; /** Return the position in the worn list where new_item would be * put by default */ @@ -993,12 +938,16 @@ class Character : public Creature, public visitable /** Applies encumbrance from items only * If new_item is not null, then calculate under the asumption that it * is added to existing work items. */ - void item_encumb( std::array &vals, - const item &new_item ) const; + void item_encumb( std::map &vals, const item &new_item ) const; std::array, num_bp> mut_drench; public: + /** Recalculate encumbrance for all body parts. */ + void calc_encumbrance(); + /** Recalculate encumbrance for all body parts as if `new_item` was also worn. */ + void calc_encumbrance( const item &new_item ); + // recalculates enchantment cache by iterating through all held, worn, and wielded items void recalculate_enchantment_cache(); // gets add and mult value from enchantment cache @@ -2203,6 +2152,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 */ @@ -2213,6 +2164,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; @@ -2282,8 +2234,6 @@ class Character : public Creature, public visitable trap_map known_traps; mutable std::map cached_info; - mutable std::array encumbrance_cache; - mutable bool encumbrance_cache_dirty = true; bool bio_soporific_powered_at_last_sleep_check = false; /** last time we checked for sleep */ time_point last_sleep_check = calendar::turn_zero; diff --git a/src/clzones.cpp b/src/clzones.cpp index 90f102b9b9758..6cfccadba53d7 100644 --- a/src/clzones.cpp +++ b/src/clzones.cpp @@ -248,15 +248,15 @@ loot_options::query_loot_result loot_options::query_loot() plot_options::query_seed_result plot_options::query_seed() { - player &p = g->u; + Character &player_character = get_player_character(); - std::vector seed_inv = p.items_with( []( const item & itm ) { + std::vector seed_inv = player_character.items_with( []( const item & itm ) { return itm.is_seed(); } ); auto &mgr = zone_manager::get_manager(); map &here = get_map(); const std::unordered_set &zone_src_set = mgr.get_near( zone_type_id( "LOOT_SEEDS" ), - here.getabs( p.pos() ), 60 ); + here.getabs( player_character.pos() ), 60 ); for( const tripoint &elem : zone_src_set ) { tripoint elem_loc = here.getlocal( elem ); for( item &it : here.i_at( elem_loc ) ) { @@ -805,7 +805,7 @@ cata::optional zone_manager::get_nearest( const zone_type_id &type, co zone_type_id zone_manager::get_near_zone_type_for_item( const item &it, const tripoint &where, int range ) const { - const item_category &cat = it.get_category(); + const item_category &cat = it.get_category_of_contents(); if( has_near( zone_type_id( "LOOT_CUSTOM" ), where, range ) ) { if( !get_near( zone_type_id( "LOOT_CUSTOM" ), where, range, &it ).empty() ) { diff --git a/src/condition.cpp b/src/condition.cpp index 030844864de4b..29f2756bf4eb1 100644 --- a/src/condition.cpp +++ b/src/condition.cpp @@ -50,7 +50,7 @@ static const efftype_id effect_currently_busy( "currently_busy" ); std::string get_talk_varname( const JsonObject &jo, const std::string &member, bool check_value ) { if( !jo.has_string( "type" ) || !jo.has_string( "context" ) || - ( check_value && !jo.has_string( "value" ) ) ) { + ( check_value && !( jo.has_string( "value" ) || jo.has_member( "time" ) ) ) ) { jo.throw_error( "invalid " + member + " condition in " + jo.str() ); } const std::string &var_basename = jo.get_string( member ); @@ -176,7 +176,7 @@ void conditional_t::set_u_has_mission( const JsonObject &jo ) { const std::string &mission = jo.get_string( "u_has_mission" ); condition = [mission]( const T & ) { - for( auto miss_it : g->u.get_active_missions() ) { + for( auto miss_it : get_avatar().get_active_missions() ) { if( miss_it->mission_id() == mission_type_id( mission ) ) { return true; } @@ -405,12 +405,16 @@ template void conditional_t::set_has_var( const JsonObject &jo, const std::string &member, bool is_npc ) { const std::string var_name = get_talk_varname( jo, member, false ); - const std::string &value = jo.get_string( "value" ); - condition = [var_name, value, is_npc]( const T & d ) { + const std::string &value = jo.has_member( "value" ) ? jo.get_string( "value" ) : std::string(); + const bool time_check = jo.has_member( "time" ) && jo.get_bool( "time" ); + condition = [var_name, value, time_check, is_npc]( const T & d ) { player *actor = d.alpha; if( is_npc ) { actor = dynamic_cast( d.beta ); } + if( time_check ) { + return !actor->get_value( var_name ).empty(); + } return actor->get_value( var_name ) == value; }; } @@ -457,6 +461,53 @@ void conditional_t::set_compare_var( const JsonObject &jo, const std::string }; } +template +void conditional_t::set_compare_time_since_var( const JsonObject &jo, const std::string &member, + bool is_npc ) +{ + const std::string var_name = get_talk_varname( jo, member, false ); + const std::string &op = jo.get_string( "op" ); + const int value = to_turns( read_from_json_string( *jo.get_raw( "time" ), + time_duration::units ) ); + condition = [var_name, op, value, is_npc]( const T & d ) { + player *actor = d.alpha; + if( is_npc ) { + actor = dynamic_cast( d.beta ); + } + + int stored_value = 0; + const std::string &var = actor->get_value( var_name ); + if( var.empty() ) { + return false; + } else { + stored_value = std::stoi( var ); + } + stored_value += value; + int now = to_turn( calendar::turn ); + + if( op == "==" ) { + return stored_value == now; + + } else if( op == "!=" ) { + return stored_value != now; + + } else if( op == "<=" ) { + return now <= stored_value; + + } else if( op == ">=" ) { + return now >= stored_value; + + } else if( op == "<" ) { + return now < stored_value; + + } else if( op == ">" ) { + return now > stored_value; + } + + return false; + }; +} + template void conditional_t::set_npc_role_nearby( const JsonObject &jo ) { @@ -715,7 +766,7 @@ template void conditional_t::set_npc_friend() { condition = []( const T & d ) { - return d.beta->is_friendly( g->u ); + return d.beta->is_friendly( get_player_character() ); }; } @@ -828,7 +879,7 @@ template void conditional_t::set_u_has_camp() { condition = []( const T & ) { - return !g->u.camps.empty(); + return !get_player_character().camps.empty(); }; } @@ -1041,6 +1092,10 @@ conditional_t::conditional_t( const JsonObject &jo ) set_compare_var( jo, "u_compare_var" ); } else if( jo.has_string( "npc_compare_var" ) ) { set_compare_var( jo, "npc_compare_var", is_npc ); + } else if( jo.has_string( "u_compare_time_since_var" ) ) { + set_compare_time_since_var( jo, "u_compare_time_since_var" ); + } else if( jo.has_string( "npc_compare_time_since_var" ) ) { + set_compare_time_since_var( jo, "npc_compare_time_since_var", is_npc ); } else if( jo.has_string( "npc_role_nearby" ) ) { set_npc_role_nearby( jo ); } else if( jo.has_int( "npc_allies" ) ) { diff --git a/src/condition.h b/src/condition.h index baa2b77058eef..49d53def09611 100644 --- a/src/condition.h +++ b/src/condition.h @@ -40,7 +40,8 @@ const std::unordered_set complex_conds = { { "npc_aim_rule", "npc_engagement_rule", "npc_rule", "npc_override", "npc_cbm_reserve_rule", "npc_cbm_recharge_rule", "days_since_cataclysm", "is_season", "mission_goal", "u_has_var", "npc_has_var", - "u_has_skill", "npc_has_skill", "u_know_recipe", "u_compare_var", "npc_compare_var" + "u_has_skill", "npc_has_skill", "u_know_recipe", "u_compare_var", "npc_compare_var", + "u_compare_time_since_var", "npc_compare_time_since_var" } }; } // namespace dialogue_data @@ -75,6 +76,8 @@ struct conditional_t { void set_has_trait_flag( const JsonObject &jo, const std::string &member, bool is_npc = false ); void set_has_var( const JsonObject &jo, const std::string &member, bool is_npc = false ); void set_compare_var( const JsonObject &jo, const std::string &member, bool is_npc = false ); + void set_compare_time_since_var( const JsonObject &jo, const std::string &member, + bool is_npc = false ); void set_has_activity( bool is_npc = false ); void set_is_riding( bool is_npc = false ); void set_npc_has_class( const JsonObject &jo ); diff --git a/src/construction.cpp b/src/construction.cpp index 6c2cfe62de99c..4f435bbc91e6c 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 & ); @@ -937,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 @@ -965,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() ) { @@ -999,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 ); @@ -1055,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 ) ); @@ -1118,6 +1124,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 +1440,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 +1555,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 +1573,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; diff --git a/src/consumption.cpp b/src/consumption.cpp index 2e313ed0d7406..d136456dc24c9 100644 --- a/src/consumption.cpp +++ b/src/consumption.cpp @@ -1436,7 +1436,7 @@ bool Character::feed_furnace_with( item &it ) return false; } if( it.is_favorite && - !g->u.query_yn( _( "Are you sure you want to eat your favorited %s?" ), it.tname() ) ) { + !get_avatar().query_yn( _( "Are you sure you want to eat your favorited %s?" ), it.tname() ) ) { return false; } diff --git a/src/coordinates.h b/src/coordinates.h index 3aaff85dc9e5d..ccb1cdfc6c7c1 100644 --- a/src/coordinates.h +++ b/src/coordinates.h @@ -8,6 +8,397 @@ #include "enums.h" #include "game_constants.h" #include "point.h" +#include "debug.h" + +namespace coords +{ + +enum class scale { + map_square, + submap, + overmap_terrain, + segment, + overmap, + vehicle +}; + +constexpr scale ms = scale::map_square; +constexpr scale sm = scale::submap; +constexpr scale omt = scale::overmap_terrain; +constexpr scale seg = scale::segment; +constexpr scale om = scale::overmap; + +constexpr int map_squares_per( scale s ) +{ + static_assert( SEEX == SEEY, "we assume submaps are square" ); + static_assert( OMAPX == OMAPY, "we assume overmaps are square" ); + + switch( s ) { + case scale::map_square: + return 1; + case scale::submap: + return SEEX; + case scale::overmap_terrain: + return SEEX * 2; + case scale::segment: + return SEG_SIZE * map_squares_per( scale::overmap_terrain ); + case scale::overmap: + return OMAPX * map_squares_per( scale::overmap_terrain ); + default: + constexpr_fatal( 0, "Requested scale of %d", s ); + } +} + +enum class origin { + relative, // this is a special origin that can be added to any other + abs, // the global absolute origin for the entire game + submap, // from corner of submap + overmap_terrain, // from corner of overmap_terrain + overmap, // from corner of overmap +}; + +constexpr origin origin_from_scale( scale s ) +{ + switch( s ) { + case scale::submap: + return origin::submap; + case scale::overmap_terrain: + return origin::overmap_terrain; + case scale::overmap: + return origin::overmap; + default: + constexpr_fatal( origin::abs, "Requested origin for scale %d", s ); + } +} + +// A generic coordinate-type-safe point. +// Point should be the underlying representation type (either point or +// tripoint). +// Scale and Origin define the coordinate system for the point. +template +class coord_point +{ + public: + static constexpr int dimension = Point::dimension; + + constexpr coord_point() = default; + explicit constexpr coord_point( const Point &p ) : + raw_( p ) + {} + template + constexpr coord_point( T x, T y ) : raw_( x, y ) {} + template + constexpr coord_point( T x, T y, T z ) : raw_( x, y, z ) {} + template + constexpr coord_point( const coord_point &xy, T z ) : + raw_( xy.raw(), z ) + {} + + constexpr Point &raw() { + return raw_; + } + constexpr const Point &raw() const { + return raw_; + } + + constexpr auto &x() { + return raw_.x; + } + constexpr auto x() const { + return raw_.x; + } + constexpr auto &y() { + return raw_.y; + } + constexpr auto y() const { + return raw_.y; + } + constexpr auto xy() const { + return coord_point( raw_.xy() ); + } + constexpr auto &z() { + return raw_.z; + } + constexpr auto z() const { + return raw_.z; + } + + std::string to_string() const { + return raw_.to_string(); + } + + void serialize( JsonOut &jsout ) const { + raw().serialize( jsout ); + } + void deserialize( JsonIn &jsin ) { + raw().deserialize( jsin ); + } + + coord_point &operator+=( const coord_point &r ) { + raw_ += r.raw(); + return *this; + } + + coord_point &operator-=( const coord_point &r ) { + raw_ -= r.raw(); + return *this; + } + + coord_point &operator+=( const point &r ) { + raw_ += r; + return *this; + } + + coord_point &operator-=( const point &r ) { + raw_ -= r; + return *this; + } + + coord_point &operator+=( const tripoint &r ) { + raw_ += r; + return *this; + } + + coord_point &operator-=( const tripoint &r ) { + raw_ -= r; + return *this; + } + + friend inline coord_point operator+( const coord_point &l, const point &r ) { + return coord_point( l.raw() + r ); + } + + friend inline coord_point operator+( const coord_point &l, const tripoint &r ) { + return coord_point( l.raw() + r ); + } + + friend inline coord_point operator-( const coord_point &l, const point &r ) { + return coord_point( l.raw() - r ); + } + + friend inline coord_point operator-( const coord_point &l, const tripoint &r ) { + return coord_point( l.raw() - r ); + } + private: + Point raw_; +}; + +template +constexpr inline bool operator==( const coord_point &l, + const coord_point &r ) +{ + return l.raw() == r.raw(); +} + +template +constexpr inline bool operator!=( const coord_point &l, + const coord_point &r ) +{ + return l.raw() != r.raw(); +} + +template +constexpr inline bool operator<( const coord_point &l, + const coord_point &r ) +{ + return l.raw() < r.raw(); +} + +template +constexpr inline auto operator+( + const coord_point &l, + const coord_point &r ) +{ + using PointResult = decltype( PointL() + PointR() ); + return coord_point( l.raw() + r.raw() ); +} + +template < typename PointL, typename PointR, origin OriginR, scale Scale, + // enable_if to prevent ambiguity with above when both args are + // relative + typename = std::enable_if_t < OriginR != origin::relative >> +constexpr inline auto operator+( + const coord_point &l, + const coord_point &r ) +{ + using PointResult = decltype( PointL() + PointR() ); + return coord_point( l.raw() + r.raw() ); +} + +template +constexpr inline auto operator-( + const coord_point &l, + const coord_point &r ) +{ + using PointResult = decltype( PointL() + PointR() ); + return coord_point( l.raw() - r.raw() ); +} + +template < typename PointL, typename PointR, origin Origin, scale Scale, + // enable_if to prevent ambiguity with above when both args are + // relative + typename = std::enable_if_t < Origin != origin::relative >> +constexpr inline auto operator-( + const coord_point &l, + const coord_point &r ) +{ + using PointResult = decltype( PointL() + PointR() ); + return coord_point( l.raw() - r.raw() ); +} + +// Only relative points can be multiplied by a constant +template +constexpr inline coord_point operator*( + int l, const coord_point &r ) +{ + return coord_point( l * r.raw() ); +} + +template +constexpr inline coord_point operator*( + const coord_point &r, int l ) +{ + return coord_point( r.raw() * l ); +} + +template +inline std::ostream &operator<<( std::ostream &os, const coord_point &p ) +{ + return os << p.raw(); +} + +template +struct project_to_impl; + +template +struct project_to_impl { + template + coord_point operator()( + const coord_point &src ) { + return coord_point( multiply_xy( src.raw(), ScaleUp ) ); + } +}; + +template +struct project_to_impl<0, ScaleDown, ResultScale> { + template + coord_point operator()( + const coord_point &src ) { + return coord_point( + divide_xy_round_to_minus_infinity( src.raw(), ScaleDown ) ); + } +}; + +template +inline coord_point project_to( + const coord_point &src ) +{ + constexpr int scale_down = map_squares_per( ResultScale ) / map_squares_per( SourceScale ); + constexpr int scale_up = map_squares_per( SourceScale ) / map_squares_per( ResultScale ); + return project_to_impl()( src ); +} + +template +struct quotient_remainder { + constexpr static origin RemainderOrigin = origin_from_scale( CoarseScale ); + using quotient_type = coord_point; + quotient_type quotient; + using remainder_type = coord_point; + remainder_type remainder; + + // For assigning to std::tie( q, r ); + operator std::tuple() { + return std::tie( quotient, remainder ); + } +}; + +template +inline quotient_remainder project_remain( + const coord_point &src ) +{ + constexpr int ScaleDown = map_squares_per( ResultScale ) / map_squares_per( SourceScale ); + static_assert( ScaleDown > 0, "You can only project to coarser coordinate systems" ); + constexpr static origin RemainderOrigin = origin_from_scale( ResultScale ); + coord_point quotient( + divide_xy_round_to_minus_infinity( src.raw(), ScaleDown ) ); + coord_point remainder( + src.raw() - quotient.raw() * ScaleDown ); + + return { quotient, remainder }; +} + +} // namespace coords + +namespace std +{ + +template +struct hash> { + std::size_t operator()( const coords::coord_point &p ) const { + const hash h{}; + return h( p.raw() ); + } +}; + +} // namespace std + +/** Typedefs for point types with coordinate mnemonics. + * + * Each name is of the form (tri)point__ where tells you the + * context in which the point has meaning, and tells you what one unit + * of the point means. + * + * For example: + * point_omt_ms is the position of a map square within an overmap terrain. + * tripoint_rel_sm is a relative tripoint submap offset. + */ +/*@{*/ +using point_rel_ms = coords::coord_point; +using point_abs_ms = coords::coord_point; +using point_sm_ms = coords::coord_point; +using point_omt_ms = coords::coord_point; +using point_abs_sm = coords::coord_point; +using point_omt_sm = coords::coord_point; +using point_om_sm = coords::coord_point; +using point_abs_omt = coords::coord_point; +using point_om_omt = coords::coord_point; +using point_abs_seg = coords::coord_point; +using point_abs_om = coords::coord_point; + +using tripoint_rel_ms = coords::coord_point; +using tripoint_abs_ms = coords::coord_point; +using tripoint_sm_ms = coords::coord_point; +using tripoint_omt_ms = coords::coord_point; +using tripoint_abs_sm = coords::coord_point; +using tripoint_abs_omt = coords::coord_point; +using tripoint_abs_seg = coords::coord_point; +using tripoint_abs_om = coords::coord_point; +/*@}*/ + +using coords::project_to; +using coords::project_remain; + +template +std::vector> + closest_points_first( const coords::coord_point &loc, + int min_dist, int max_dist ) +{ + std::vector raw_result = closest_points_first( loc.raw(), min_dist, max_dist ); + std::vector> result; + result.reserve( raw_result.size() ); + std::transform( raw_result.begin(), raw_result.end(), std::back_inserter( result ), + []( const Point & p ) { + return coords::coord_point( p ); + } ); + return result; +} +template +std::vector> + closest_points_first( const coords::coord_point &loc, + int max_dist ) +{ + return closest_points_first( loc, 0, max_dist ); +} /* find appropriate subdivided coordinates for absolute tile coordinate. * This is less obvious than one might think, for negative coordinates, so this diff --git a/src/crafting.cpp b/src/crafting.cpp index a47f4c21cd3b4..1166876872c14 100644 --- a/src/crafting.cpp +++ b/src/crafting.cpp @@ -472,7 +472,7 @@ std::vector player::get_eligible_containers_for_crafting() const map &here = get_map(); // get all potential containers within PICKUP_RANGE tiles including vehicles - for( const tripoint &loc : closest_tripoints_first( pos(), PICKUP_RANGE ) ) { + for( const tripoint &loc : closest_points_first( pos(), PICKUP_RANGE ) ) { // can not reach this -> can not access its contents if( pos() != loc && !here.clear_path( pos(), loc, PICKUP_RANGE, 1, 100 ) ) { continue; @@ -663,7 +663,7 @@ static item *set_item_inventory( player &p, item &newit ) static item_location set_item_map( const tripoint &loc, item &newit ) { // Includes loc - for( const tripoint &tile : closest_tripoints_first( loc, 2 ) ) { + for( const tripoint &tile : closest_points_first( loc, 2 ) ) { // Pass false to disallow overflow, null_item_reference indicates failure. item *it_on_map = &get_map().add_item_or_charges( tile, newit, false ); if( it_on_map != &null_item_reference() ) { @@ -733,7 +733,7 @@ void player::start_craft( craft_command &command, const tripoint &loc ) // In case we were wearing something just consumed if( !craft.components.empty() ) { - reset_encumbrance(); + calc_encumbrance(); } item_location craft_in_world; @@ -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() ); @@ -1993,8 +1993,9 @@ bool player::disassemble( item_location target, bool interactive ) return false; } + avatar &player_character = get_avatar(); const auto &r = recipe_dictionary::get_uncraft( obj.typeId() ); - if( !obj.is_owned_by( g->u, true ) ) { + if( !obj.is_owned_by( player_character, true ) ) { if( !query_yn( _( "Disassembling the %s may anger the people who own it, continue?" ), obj.tname() ) ) { return false; @@ -2002,15 +2003,15 @@ bool player::disassemble( item_location target, bool interactive ) if( obj.get_owner() ) { std::vector witnesses; for( npc &elem : g->all_npcs() ) { - if( rl_dist( elem.pos(), g->u.pos() ) < MAX_VIEW_DISTANCE && elem.get_faction() && - obj.is_owned_by( elem ) && elem.sees( g->u.pos() ) ) { + if( rl_dist( elem.pos(), player_character.pos() ) < MAX_VIEW_DISTANCE && elem.get_faction() && + obj.is_owned_by( elem ) && elem.sees( player_character.pos() ) ) { elem.say( "", 7 ); npc *npc_to_add = &elem; witnesses.push_back( npc_to_add ); } } if( !witnesses.empty() ) { - if( g->u.add_faction_warning( obj.get_owner() ) ) { + if( player_character.add_faction_warning( obj.get_owner() ) ) { for( npc *elem : witnesses ) { elem->make_angry(); } @@ -2055,12 +2056,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 +2145,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 ) @@ -2276,7 +2277,7 @@ void player::complete_disassemble( item_location &target, const recipe &dis ) item act_item = newit; if( act_item.has_temperature() ) { - act_item.set_item_temperature( temp_to_kelvin( g->weather.get_temperature( loc ) ) ); + act_item.set_item_temperature( temp_to_kelvin( get_weather().get_temperature( loc ) ) ); } // Refitted clothing disassembles into refitted components (when applicable) diff --git a/src/creature.cpp b/src/creature.cpp index dea53afb5fd64..08a92575852b2 100644 --- a/src/creature.cpp +++ b/src/creature.cpp @@ -1529,6 +1529,11 @@ int Creature::get_part_damage_bandaged( const bodypart_id &id ) const return get_part( id ).get_damage_bandaged(); } +encumbrance_data Creature::get_part_encumbrance_data( const bodypart_id &id ) const +{ + return get_part( id ).get_encumbrance_data(); +} + void Creature::set_part_hp_cur( const bodypart_id &id, int set ) { get_part( id )->set_hp_cur( set ); @@ -1554,6 +1559,11 @@ void Creature::set_part_damage_bandaged( const bodypart_id &id, int set ) get_part( id )->set_damage_bandaged( set ); } +void Creature::set_part_encumbrance_data( const bodypart_id &id, encumbrance_data set ) +{ + get_part( id )->set_encumbrance_data( set ); +} + void Creature::mod_part_hp_cur( const bodypart_id &id, int mod ) { get_part( id )->mod_hp_cur( mod ); diff --git a/src/creature.h b/src/creature.h index 0941fceb4e6c0..cd622e94ea74e 100644 --- a/src/creature.h +++ b/src/creature.h @@ -43,7 +43,6 @@ struct point; enum damage_type : int; enum m_flag : int; -enum hp_part : int; struct damage_instance; struct damage_unit; struct dealt_damage_instance; @@ -621,11 +620,16 @@ class Creature int get_part_damage_disinfected( const bodypart_id &id ) const; int get_part_damage_bandaged( const bodypart_id &id ) const; + encumbrance_data get_part_encumbrance_data( 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 set_part_encumbrance_data( const bodypart_id &id, encumbrance_data 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 ); diff --git a/src/debug.h b/src/debug.h index 4f8ab2ffc385a..f6d4834ed7199 100644 --- a/src/debug.h +++ b/src/debug.h @@ -80,6 +80,21 @@ inline void realDebugmsg( const char *const filename, const char *const line, std::forward( args )... ) ); } +// A fatal error for use in constexpr functions +// This exists for compatibility reasons. On gcc 5.3 we need a +// different implementation that is messier. +// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=67371 +// Pass a placeholder return value to be used on gcc 5.3 (it won't +// actually be returned, it's just needed for the type), and then +// args as if to debugmsg for the remaining args. +#if defined(__GNUC__) && __GNUC__ < 6 +#define constexpr_fatal(ret, ...) \ + do { return false ? ( ret ) : ( abort(), ( ret ) ); } while(false) +#else +#define constexpr_fatal(ret, ...) \ + do { debugmsg(__VA_ARGS__); abort(); } while(false) +#endif + /** * Used to generate game report information. */ diff --git a/src/debug_menu.cpp b/src/debug_menu.cpp index 48e78acf531f8..8c02e61cc325b 100644 --- a/src/debug_menu.cpp +++ b/src/debug_menu.cpp @@ -89,6 +89,7 @@ #include "ui.h" #include "ui_manager.h" #include "units.h" +#include "vehicle.h" #include "veh_type.h" #include "vitamin.h" #include "vpart_position.h" @@ -102,8 +103,6 @@ static const mtype_id mon_generator( "mon_generator" ); static const trait_id trait_ASTHMA( "ASTHMA" ); -class vehicle; - extern std::map> > nested_mapgen; @@ -177,6 +176,7 @@ std::string enum_to_string( debug_menu::debug_menu case debug_menu::debug_menu_index::LEVEL_SPELLS: return "LEVEL_SPELLS"; case debug_menu::debug_menu_index::TEST_MAP_EXTRA_DISTRIBUTION: return "TEST_MAP_EXTRA_DISTRIBUTION"; case debug_menu::debug_menu_index::NESTED_MAPGEN: return "NESTED_MAPGEN"; + case debug_menu::debug_menu_index::VEHICLE_BATTERY_CHARGE: return "VEHICLE_BATTERY_CHARGE"; // *INDENT-ON* case debug_menu::debug_menu_index::last: break; @@ -275,6 +275,15 @@ static int game_uilist() return uilist( _( "Game…" ), uilist_initializer ); } +static int vehicle_uilist() +{ + std::vector uilist_initializer = { + { uilist_entry( debug_menu_index::VEHICLE_BATTERY_CHARGE, true, 'b', _( "Change [b]attery charge" ) ) }, + }; + + return uilist( _( "Vehicle…" ), uilist_initializer ); +} + static int teleport_uilist() { const std::vector uilist_initializer = { @@ -336,6 +345,7 @@ static cata::optional debug_menu_uilist( bool display_all_entr { uilist_entry( 6, true, 'g', _( "Game…" ) ) }, { uilist_entry( 2, true, 's', _( "Spawning…" ) ) }, { uilist_entry( 3, true, 'p', _( "Player…" ) ) }, + { uilist_entry( 7, true, 'v', _( "Vehicle…" ) ) }, { uilist_entry( 4, true, 't', _( "Teleport…" ) ) }, { uilist_entry( 5, true, 'm', _( "Map…" ) ) }, }; @@ -375,6 +385,9 @@ static cata::optional debug_menu_uilist( bool display_all_entr case 6: action = game_uilist(); break; + case 7: + action = vehicle_uilist(); + break; default: return cata::nullopt; @@ -1267,15 +1280,13 @@ void debug() popup_top( "Monster flag usage statistics were dumped to debug.log and cleared." ); std::string s = _( "Location %d:%d in %d:%d, %s\n" ); - s += _( "Current turn: %d.\n%s\n" ); + s += _( "Current turn: %d.\n" ); s += ngettext( "%d creature exists.\n", "%d creatures exist.\n", g->num_creatures() ); popup_top( s.c_str(), u.posx(), g->u.posy(), g->get_levx(), g->get_levy(), overmap_buffer.ter( g->u.global_omt_location() )->get_name(), to_turns( calendar::turn - calendar::turn_zero ), - get_option( "RANDOM_NPC" ) ? _( "NPCs are going to spawn." ) : - _( "NPCs are NOT going to spawn." ), g->num_creatures() ); for( const npc &guy : g->all_npcs() ) { tripoint t = guy.global_sm_location(); @@ -1341,11 +1352,20 @@ void debug() if( veh_menu.ret >= 0 && veh_menu.ret < static_cast( veh_strings.size() ) ) { // Didn't cancel const vproto_id &selected_opt = veh_strings[veh_menu.ret].second; - // TODO: Allow picking this when add_vehicle has 3d argument tripoint dest = u.pos(); - vehicle *veh = here.add_vehicle( selected_opt, dest, -90, 100, 0 ); - if( veh != nullptr ) { - here.board_vehicle( dest, &u ); + uilist veh_cond_menu; + veh_cond_menu.text = _( "Vehicle condition" ); + veh_cond_menu.addentry( 0, true, MENU_AUTOASSIGN, _( "Light damage" ) ); + veh_cond_menu.addentry( 1, true, MENU_AUTOASSIGN, _( "Undamaged" ) ); + veh_cond_menu.addentry( 2, true, MENU_AUTOASSIGN, _( "Disabled (tires or engine)" ) ); + veh_cond_menu.query(); + + if( veh_cond_menu.ret >= 0 && veh_cond_menu.ret < 3 ) { + // TODO: Allow picking this when add_vehicle has 3d argument + vehicle *veh = here.add_vehicle( selected_opt, dest, -90, 100, veh_cond_menu.ret - 1 ); + if( veh != nullptr ) { + here.board_vehicle( dest, &u ); + } } } } @@ -1403,15 +1423,16 @@ void debug() weather_menu.text = _( "Select new weather pattern:" ); weather_menu.addentry( 0, true, MENU_AUTOASSIGN, g->weather.weather_override == WEATHER_NULL ? _( "Keep normal weather patterns" ) : _( "Disable weather forcing" ) ); - for( int weather_id = 1; weather_id < NUM_WEATHER_TYPES; weather_id++ ) { - weather_menu.addentry( weather_id, true, MENU_AUTOASSIGN, - weather::name( static_cast( weather_id ) ) ); + for( size_t i = 0; i < weather_types::get_all().size(); i++ ) { + weather_menu.addentry( i, true, MENU_AUTOASSIGN, + weather_types::get_all()[i].name ); } weather_menu.query(); - if( weather_menu.ret >= 0 && weather_menu.ret < NUM_WEATHER_TYPES ) { - weather_type selected_weather = static_cast( weather_menu.ret ); + if( weather_menu.ret >= 0 && + static_cast( weather_menu.ret ) < weather_types::get_all().size() ) { + const weather_type_id selected_weather = weather_types::get_all()[weather_menu.ret].id; g->weather.weather_override = selected_weather; g->weather.set_nextweather( calendar::turn ); } @@ -1873,6 +1894,32 @@ void debug() case debug_menu_index::TEST_MAP_EXTRA_DISTRIBUTION: MapExtras::debug_spawn_test(); break; + + case debug_menu_index::VEHICLE_BATTERY_CHARGE: { + + optional_vpart_position v_part_pos = here.veh_at( u.pos() ); + if( !v_part_pos ) { + add_msg( m_bad, _( "There's no vehicle there." ) ); + break; + } + + int amount = 0; + string_input_popup popup; + popup + .title( _( "By how much? (in kJ, negative to discharge)" ) ) + .width( 30 ) + .edit( amount ); + if( !popup.canceled() ) { + vehicle &veh = v_part_pos->vehicle(); + if( amount >= 0 ) { + veh.charge_battery( amount, false ); + } else { + veh.discharge_battery( -amount, false ); + } + } + break; + } + case debug_menu_index::last: return; } diff --git a/src/debug_menu.h b/src/debug_menu.h index e5451397611e2..1fe4a7f709c03 100644 --- a/src/debug_menu.h +++ b/src/debug_menu.h @@ -76,6 +76,7 @@ enum class debug_menu_index : int { LEVEL_SPELLS, TEST_MAP_EXTRA_DISTRIBUTION, NESTED_MAPGEN, + VEHICLE_BATTERY_CHARGE, last }; diff --git a/src/dump.cpp b/src/dump.cpp index 703bb07d5a825..5c81b7a69344c 100644 --- a/src/dump.cpp +++ b/src/dump.cpp @@ -8,8 +8,8 @@ #include #include -#include "avatar.h" #include "bodypart.h" +#include "character.h" #include "compatibility.h" // needed for the workaround for the std::to_string bug in some compilers #include "damage.h" #include "flat_set.h" @@ -109,7 +109,7 @@ bool game::dump_stats( const std::string &what, dump_mode mode, auto dump = [&rows]( const item & obj ) { std::vector r; r.push_back( obj.tname( 1, false ) ); - r.push_back( to_string( obj.get_encumber( g->u ) ) ); + r.push_back( to_string( obj.get_encumber( get_player_character() ) ) ); r.push_back( to_string( obj.get_warmth() ) ); r.push_back( to_string( to_gram( obj.weight() ) ) ); r.push_back( to_string( obj.get_coverage() ) ); @@ -161,7 +161,7 @@ bool game::dump_stats( const std::string &what, dump_mode mode, for( const itype *e : item_controller->all() ) { item food( e, calendar::turn, item::solitary_tag {} ); - if( food.is_food() && g->u.can_eat( food ).success() ) { + if( food.is_food() && get_player_character().can_eat( food ).success() ) { dump( food ); } } diff --git a/src/editmap.cpp b/src/editmap.cpp index 1612f7a3fdf34..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 @@ -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/game.cpp b/src/game.cpp index 2581eb5a28f50..ba2cd7c5c33aa 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -249,6 +249,8 @@ static const trait_id trait_PARKOUR( "PARKOUR" ); static const trait_id trait_VINES2( "VINES2" ); static const trait_id trait_VINES3( "VINES3" ); static const trait_id trait_THICKSKIN( "THICKSKIN" ); +static const trait_id trait_NPC_STATIC_NPC( "NPC_STATIC_NPC" ); +static const trait_id trait_NPC_STARTING_NPC( "NPC_STARTING_NPC" ); static const trap_str_id tr_unfinished_construction( "tr_unfinished_construction" ); @@ -587,7 +589,7 @@ void game::setup() calendar::set_eternal_season( ::get_option( "ETERNAL_SEASON" ) ); calendar::set_season_length( ::get_option( "SEASON_LENGTH" ) ); - weather.weather = WEATHER_CLEAR; // Start with some nice weather... + weather.weather_id = WEATHER_CLEAR; // Weather shift in 30 weather.nextweather = calendar::start_of_cataclysm + time_duration::from_hours( get_option( "INITIAL_TIME" ) ) + 30_minutes; @@ -704,10 +706,8 @@ bool game::start_game() get_auto_notes_settings().clear(); get_auto_notes_settings().default_initialize(); - //Put some NPCs in there! - if( get_option( "STARTING_NPC" ) == "always" || - ( get_option( "STARTING_NPC" ) == "scenario" && - !g->scen->has_flag( "LONE_START" ) ) ) { + // spawn the starting NPC, assuming it's not disallowed by the scenario + if( !g->scen->has_flag( "LONE_START" ) ) { create_starting_npcs(); } //Load NPCs. Set nearby npcs to active. @@ -946,11 +946,6 @@ const kill_tracker &game::get_kill_tracker() const void game::create_starting_npcs() { - if( !get_option( "STATIC_NPC" ) || - get_option( "STARTING_NPC" ) == "never" ) { - return; //Do not generate a starting npc. - } - //We don't want more than one starting npc per starting location const int radius = 1; if( !overmap_buffer.get_npcs_near_player( radius ).empty() ) { @@ -1573,8 +1568,7 @@ bool game::do_turn() } if( get_levz() >= 0 && !u.is_underwater() ) { - do_rain( weather.weather ); - weather::effect( weather.weather )(); + handle_weather_effects( weather.weather_id ); } const bool player_is_sleeping = u.has_effect( effect_sleep ); @@ -1810,7 +1804,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 +1837,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 @@ -3327,7 +3320,8 @@ void game::disp_NPCs() { const tripoint ppos = u.global_omt_location(); const tripoint &lpos = u.pos(); - std::vector> npcs = overmap_buffer.get_npcs_near_player( 100 ); + const int scan_range = 120; + std::vector> npcs = overmap_buffer.get_npcs_near_player( scan_range ); std::sort( npcs.begin(), npcs.end(), npc_dist_to_player() ); catacurses::window w; @@ -3347,13 +3341,22 @@ void game::disp_NPCs() mvwprintz( w, point( 0, 1 ), c_white, _( "Your local position: %d, %d, %d" ), lpos.x, lpos.y, lpos.z ); size_t i; + int static_npc_count = 0; + for( i = 0; i < npcs.size(); i++ ) { + if( + npcs[i]->has_trait( trait_NPC_STARTING_NPC ) || npcs[i]->has_trait( trait_NPC_STATIC_NPC ) ) { + static_npc_count++; + } + } + mvwprintz( w, point( 0, 2 ), c_white, _( "Total NPCs within %d OMTs: %d. %d are static NPCs." ), + scan_range, npcs.size(), static_npc_count ); for( i = 0; i < 20 && i < npcs.size(); i++ ) { const tripoint apos = npcs[i]->global_omt_location(); - mvwprintz( w, point( 0, i + 3 ), c_white, "%s: %d, %d, %d", npcs[i]->name, + mvwprintz( w, point( 0, i + 4 ), c_white, "%s: %d, %d, %d", npcs[i]->name, apos.x, apos.y, apos.z ); } for( const monster &m : all_monsters() ) { - mvwprintz( w, point( 0, i + 3 ), c_white, "%s: %d, %d, %d", m.name(), + mvwprintz( w, point( 0, i + 4 ), c_white, "%s: %d, %d, %d", m.name(), m.posx(), m.posy(), m.posz() ); ++i; } @@ -3982,7 +3985,7 @@ float game::natural_light_level( const int zlev ) const ret = default_daylight_level(); } - ret += weather::light_modifier( weather.weather ); + ret += get_weather().weather_id->light_modifier; // Artifact light level changes here. Even though some of these only have an effect // aboveground it is cheaper performance wise to simply iterate through the entire @@ -5239,8 +5242,8 @@ bool game::is_empty( const tripoint &p ) bool game::is_in_sunlight( const tripoint &p ) { - return ( m.is_outside( p ) && light_level( p.z ) >= 40 && - ( weather.weather == WEATHER_CLEAR || weather.weather == WEATHER_SUNNY ) ); + return ( m.is_outside( p ) && light_level( p.z ) >= 40 && !is_night( calendar::turn ) && + get_weather().weather_id->sun_intensity >= sun_intensity_type::normal ); } bool game::is_sheltered( const tripoint &p ) @@ -5908,11 +5911,8 @@ void game::examine( const tripoint &examp ) none = false; } - if( !m.tr_at( examp ).is_null() && !u.is_mounted() ) { - 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." ) ); - } + // trap::iexamine will handle the invisible traps. + m.tr_at( examp ).examine( examp ); // In case of teleport trap or somesuch if( player_pos != u.pos() ) { @@ -6295,7 +6295,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 ); @@ -7225,7 +7225,7 @@ std::vector game::find_nearby_items( int iRadius ) return ret; } - for( auto &points_p_it : closest_tripoints_first( u.pos(), iRadius ) ) { + for( auto &points_p_it : closest_points_first( u.pos(), iRadius ) ) { if( points_p_it.y >= u.posy() - iRadius && points_p_it.y <= u.posy() + iRadius && u.sees( points_p_it ) && m.sees_some_items( points_p_it, u ) ) { @@ -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; } @@ -9219,8 +9219,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; @@ -9245,7 +9244,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() ); } @@ -9277,7 +9276,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 +9419,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 +9968,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; } @@ -10068,6 +10068,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(); @@ -10390,7 +10394,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 ); @@ -10792,7 +10796,7 @@ void game::vertical_move( int movez, bool force, bool peeking ) } if( !npcs_to_bring.empty() ) { // Would look nicer randomly scrambled - std::vector candidates = closest_tripoints_first( u.pos(), 1 ); + std::vector candidates = closest_points_first( u.pos(), 1 ); candidates.erase( std::remove_if( candidates.begin(), candidates.end(), [this]( const tripoint & c ) { return !is_empty( c ); @@ -10801,6 +10805,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() ) { @@ -11116,7 +11122,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 ); @@ -11519,20 +11525,31 @@ void game::perhaps_add_random_npc() return; } // Create a new NPC? - // Only allow NPCs on 0 z-level, otherwise they can bug out due to lack of spots - if( !get_option( "RANDOM_NPC" ) || ( !m.has_zlevels() && get_levz() != 0 ) ) { + + double spawn_time = get_option( "NPC_SPAWNTIME" ); + if( spawn_time == 0.0 ) { return; } - float density = get_option( "NPC_DENSITY" ); - static constexpr int density_search_radius = 60; - const float npc_num = overmap_buffer.get_npcs_near_player( density_search_radius ).size(); - if( npc_num > 0.0 ) { + // spawn algorithm is a chance per hour, but the config is specified in average days + // actual chance per hour is (100 / 24 ) / days + static constexpr double days_to_rate_factor = 100.0 / 24; + double spawn_rate = days_to_rate_factor / spawn_time; + static constexpr int radius_spawn_range = 90; + std::vector> npcs = overmap_buffer.get_npcs_near_player( radius_spawn_range ); + size_t npc_num = npcs.size(); + for( auto &npc : npcs ) { + if( npc->has_trait( trait_NPC_STATIC_NPC ) || npc->has_trait( trait_NPC_STARTING_NPC ) ) { + npc_num--; + } + } + + if( npc_num > 0 ) { // 100%, 80%, 64%, 52%, 41%, 33%... - density *= std::pow( 0.8f, npc_num ); + spawn_rate *= std::pow( 0.8f, npc_num ); } - if( !x_in_y( density, 100 ) ) { + if( !x_in_y( spawn_rate, 100 ) ) { return; } bool spawn_allowed = false; @@ -11542,7 +11559,6 @@ void game::perhaps_add_random_npc() if( counter >= 10 ) { return; } - static constexpr int radius_spawn_range = 120; const tripoint u_omt = u.global_omt_location(); spawn_point = u_omt + point( rng( -radius_spawn_range, radius_spawn_range ), rng( -radius_spawn_range, radius_spawn_range ) ); @@ -11564,6 +11580,7 @@ void game::perhaps_add_random_npc() faction_id( "no_faction" ) ); tmp->set_fac( new_solo_fac ? new_solo_fac->id : faction_id( "no_faction" ) ); // adds the npc to the correct overmap. + // Only spawn random NPCs on z-level 0 tripoint submap_spawn = omt_to_sm_copy( spawn_point ); tmp->spawn_at_sm( tripoint( submap_spawn.xy(), 0 ) ); overmap_buffer.insert_npc( tmp ); @@ -11825,7 +11842,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/game.h b/src/game.h index 509b4218ef7dc..496e88482a535 100644 --- a/src/game.h +++ b/src/game.h @@ -88,7 +88,6 @@ enum safe_mode_type { }; enum body_part : int; -enum weather_type : int; enum action_id : int; struct special_game; @@ -149,6 +148,10 @@ class game friend class editmap; friend class advanced_inventory; friend class main_menu; + friend map &get_map(); + friend Character &get_player_character(); + friend avatar &get_avatar(); + friend weather_manager &get_weather(); public: game(); ~game(); @@ -554,7 +557,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 @@ -741,9 +744,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 @@ -964,9 +967,8 @@ class game pimpl memorial_logger_ptr; pimpl spell_events_ptr; - public: - /** Make map a reference here, to avoid map.h in game.h */ map &m; + public: avatar &u; scent_map &scent; timed_event_manager &timed_events; 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/gates.cpp b/src/gates.cpp index 71a0e178799a9..091f654fbf926 100644 --- a/src/gates.cpp +++ b/src/gates.cpp @@ -218,7 +218,7 @@ void gates::open_gate( const tripoint &pos ) } } - if( g->u.sees( pos ) ) { + if( get_player_character().sees( pos ) ) { if( open ) { add_msg( gate.open_message ); } else if( close ) { @@ -277,7 +277,7 @@ void doors::close_door( map &m, Character &who, const tripoint &closep ) const int inside_closable = veh->next_part_to_close( vpart ); const int openable = veh->next_part_to_open( vpart ); if( closable >= 0 ) { - if( !veh->handle_potential_theft( dynamic_cast( g->u ) ) ) { + if( !veh->handle_potential_theft( get_avatar() ) ) { return; } veh->close( closable ); diff --git a/src/grab.cpp b/src/grab.cpp index ffa5a942f3d76..a4526ad0c4564 100644 --- a/src/grab.cpp +++ b/src/grab.cpp @@ -27,7 +27,7 @@ bool game::grabbed_veh_move( const tripoint &dp ) } vehicle *grabbed_vehicle = &grabbed_vehicle_vp->vehicle(); if( !grabbed_vehicle || - !grabbed_vehicle->handle_potential_theft( dynamic_cast( g->u ) ) ) { + !grabbed_vehicle->handle_potential_theft( get_avatar() ) ) { return false; } const int grabbed_part = grabbed_vehicle_vp->part_index(); @@ -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/handle_action.cpp b/src/handle_action.cpp index c0a516b4db933..46be8803301dc 100644 --- a/src/handle_action.cpp +++ b/src/handle_action.cpp @@ -221,7 +221,7 @@ input_context game::get_player_input( std::string &action ) } //x% of the Viewport, only shown on visible areas - const auto weather_info = get_weather_animation( weather.weather ); + const auto weather_info = weather.weather_id->weather_animation; point offset( u.view_offset.xy() + point( -getmaxx( w_terrain ) / 2 + u.posx(), -getmaxy( w_terrain ) / 2 + u.posy() ) ); @@ -243,7 +243,7 @@ input_context game::get_player_input( std::string &action ) weather_printable wPrint; wPrint.colGlyph = weather_info.color; wPrint.cGlyph = weather_info.glyph; - wPrint.wtype = weather.weather; + wPrint.wtype = weather.weather_id; wPrint.vdrops.clear(); ctxt.set_timeout( 125 ); diff --git a/src/iexamine.cpp b/src/iexamine.cpp index a79e5526dff67..667b4ff8d0a16 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" ); @@ -897,7 +899,7 @@ void iexamine::elevator( player &p, const tripoint &examp ) } else if( here.ter( critter.pos() ) == ter_id( "t_elevator" ) ) { tripoint critter_omt = ms_to_omt_copy( here.getabs( critter.pos() ) ); if( critter_omt == new_floor_omt ) { - for( const tripoint &candidate : closest_tripoints_first( critter.pos(), 10 ) ) { + for( const tripoint &candidate : closest_points_first( critter.pos(), 10 ) ) { if( here.ter( candidate ) != ter_id( "t_elevator" ) && here.passable( candidate ) && !g->critter_at( candidate ) ) { @@ -920,7 +922,7 @@ void iexamine::elevator( player &p, const tripoint &examp ) tripoint critter_omt = ms_to_omt_copy( here.getabs( critter.pos() ) ); if( critter_omt == original_floor_omt ) { - for( const tripoint &candidate : closest_tripoints_first( p.pos(), 10 ) ) { + for( const tripoint &candidate : closest_points_first( p.pos(), 10 ) ) { if( here.ter( candidate ) == ter_id( "t_elevator" ) && candidate != p.pos() && !g->critter_at( candidate ) ) { @@ -1074,7 +1076,7 @@ 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; @@ -1148,9 +1150,11 @@ void iexamine::bars( player &p, const tripoint &examp ) return; } map &here = get_map(); - if( ( ( p.encumb( bp_torso ) ) >= 10 ) && ( ( p.encumb( bp_head ) ) >= 10 ) && - ( p.encumb( bp_foot_l ) >= 10 || - p.encumb( bp_foot_r ) >= 10 ) ) { // Most likely places for rigid gear that would catch on the bars. + if( ( ( p.encumb( bodypart_id( "torso" ) ) ) >= 10 ) && + ( ( p.encumb( bodypart_id( "head" ) ) ) >= 10 ) && + ( p.encumb( bodypart_id( "foot_l" ) ) >= 10 || + p.encumb( bodypart_id( "foot_r" ) ) >= + 10 ) ) { // Most likely places for rigid gear that would catch on the bars. add_msg( m_info, _( "Your amorphous body could slip though the %s, but your cumbersome gear can't." ), here.tername( examp ) ); @@ -1998,7 +2002,7 @@ void iexamine::egg_sack_generic( player &p, const tripoint &examp, here.furn_set( examp, f_egg_sacke ); int monster_count = 0; if( one_in( 2 ) ) { - for( const tripoint &nearby_pos : closest_tripoints_first( examp, 1 ) ) { + for( const tripoint &nearby_pos : closest_points_first( examp, 1 ) ) { if( !one_in( 3 ) ) { continue; } else if( g->place_critter_at( montype, nearby_pos ) ) { @@ -3562,7 +3566,7 @@ 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 ); @@ -3689,57 +3693,98 @@ void iexamine::recycle_compactor( player &, const tripoint &examp ) } } -void iexamine::trap( player &p, const tripoint &examp ) +void trap::examine( const tripoint &examp ) const { map &here = get_map(); - const auto &tr = here.tr_at( examp ); - if( !p.is_player() || tr.is_null() ) { + + // If the player can't see the trap, they can't interact with it. + if( !can_see( examp, g->u ) ) { return; } - const int possible = tr.get_difficulty(); - bool seen = tr.can_see( examp, p ); - if( tr.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 ) ) { - 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?" ) ) ) { - here.disarm_trap( 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( g->u.is_mounted() ) { + add_msg( m_warning, _( "You cannot do that while mounted." ) ); + 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() ); + return; } - if( seen && possible >= 99 ) { - add_msg( m_info, _( "That %s looks too dangerous to mess with. Best leave it alone." ), - tr.name() ); + + // 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; + } + add_msg( _( "The %s is taken down." ), name() ); + on_disarmed( here, examp ); 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() ) ) { - here.disarm_trap( examp ); + + if( query_yn( _( "There is a %s there. Disarm?" ), name() ) ) { + 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 ); + } } - } else if( seen && query_yn( _( "There is a %s there. Disarm?" ), tr.name() ) ) { - here.disarm_trap( examp ); + return; } } @@ -6024,7 +6069,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/init.cpp b/src/init.cpp index 20132236b1e48..f85e164a00414 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -90,6 +90,8 @@ #include "veh_type.h" #include "vehicle_group.h" #include "vitamin.h" +#include "weather.h" +#include "weather_type.h" #include "worldfactory.h" DynamicDataLoader::DynamicDataLoader() @@ -202,6 +204,7 @@ void DynamicDataLoader::initialize() add( "json_flag", &json_flag::load ); add( "fault", &fault::load_fault ); add( "field_type", &field_types::load ); + add( "weather_type", &weather_types::load ); add( "ammo_effect", &ammo_effects::load ); add( "emit", &emit::load_emit ); add( "activity_type", &activity_type::load ); @@ -553,6 +556,7 @@ void DynamicDataLoader::unload_data() vehicle_prototype::reset(); vitamin::reset(); vpart_info::reset(); + weather_types::reset(); } void DynamicDataLoader::finalize_loaded_data() @@ -571,6 +575,7 @@ void DynamicDataLoader::finalize_loaded_data( loading_ui &ui ) using named_entry = std::pair>; const std::vector entries = {{ { _( "Body parts" ), &body_part_type::finalize_all }, + { _( "Weather types" ), &weather_types::finalize_all }, { _( "Field types" ), &field_types::finalize_all }, { _( "Ammo effects" ), &ammo_effects::finalize_all }, { _( "Emissions" ), &emit::finalize }, @@ -653,6 +658,7 @@ void DynamicDataLoader::check_consistency( loading_ui &ui ) } }, { _( "Vitamins" ), &vitamin::check_consistency }, + { _( "Weather types" ), &weather_types::check_consistency }, { _( "Field types" ), &field_types::check_consistency }, { _( "Ammo effects" ), &ammo_effects::check_consistency }, { _( "Emissions" ), &emit::check_consistency }, 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/inventory_ui.cpp b/src/inventory_ui.cpp index 1ea1e2590f965..ab54eccdd4600 100644 --- a/src/inventory_ui.cpp +++ b/src/inventory_ui.cpp @@ -1373,7 +1373,7 @@ 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 ) ) { + for( const tripoint &pos : closest_points_first( u.pos(), radius ) ) { // can not reach this -> can not access its contents if( u.pos() != pos && !here.clear_path( u.pos(), pos, rl_dist( u.pos(), pos ), 1, 100 ) ) { continue; @@ -2384,7 +2384,7 @@ void inventory_drop_selector::deselect_contained_items() if( !selected->is_item() ) { continue; } - for( item_location selected_loc : selected->locations ) { + for( const item_location &selected_loc : selected->locations ) { if( selected_loc == loc_contained ) { set_chosen_count( *selected, 0 ); } @@ -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() ); diff --git a/src/item.cpp b/src/item.cpp index 68330de06a237..b00e986a431aa 100644 --- a/src/item.cpp +++ b/src/item.cpp @@ -102,6 +102,7 @@ static const ammotype ammo_plutonium( "plutonium" ); static const item_category_id item_category_drugs( "drugs" ); static const item_category_id item_category_food( "food" ); static const item_category_id item_category_maps( "maps" ); +static const item_category_id item_category_container( "container" ); static const efftype_id effect_cig( "cig" ); static const efftype_id effect_shakes( "shakes" ); @@ -562,7 +563,7 @@ item &item::activate() bool item::activate_thrown( const tripoint &pos ) { - return type->invoke( g->u, *this, pos ); + return type->invoke( get_avatar(), *this, pos ); } units::energy item::set_energy( const units::energy &qty ) @@ -592,6 +593,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 +630,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; } @@ -1026,10 +1029,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 ) @@ -1165,9 +1169,10 @@ static std::string get_freshness_description( const item &food_item ) time_left = shelf_life; } + Character &player_character = get_player_character(); if( food_item.is_fresh() ) { // Fresh food is assumed to be obviously so regardless of skill. - if( g->u.can_estimate_rot() ) { + if( player_character.can_estimate_rot() ) { return string_format( _( "* This food looks as fresh as it can be. " "It still has %s until it spoils." ), to_string_approx( time_left ) ); @@ -1176,7 +1181,7 @@ static std::string get_freshness_description( const item &food_item ) } } else if( food_item.is_going_bad() ) { // Old food likewise is assumed to be fairly obvious. - if( g->u.can_estimate_rot() ) { + if( player_character.can_estimate_rot() ) { return string_format( _( "* This food looks old. " "It's just %s from becoming inedible." ), to_string_approx( time_left ) ); @@ -1186,7 +1191,7 @@ static std::string get_freshness_description( const item &food_item ) } } - if( !g->u.can_estimate_rot() ) { + if( !player_character.can_estimate_rot() ) { // Unskilled characters only get a hint that more information exists... return _( "* This food looks fine. If you were more skilled in " "cooking or survival, you might be able to make a better estimation." ); @@ -1371,7 +1376,7 @@ double item::effective_dps( const player &guy, monster &mon ) const { const float mon_dodge = mon.get_dodge(); float base_hit = guy.get_dex() / 4.0f + guy.get_hit_weapon( *this ); - base_hit *= std::max( 0.25f, 1.0f - guy.encumb( bp_torso ) / 100.0f ); + base_hit *= std::max( 0.25f, 1.0f - guy.encumb( bodypart_id( "torso" ) ) / 100.0f ); float mon_defense = mon_dodge + mon.size_melee_penalty() / 5.0; constexpr double hit_trials = 10000.0; const int rng_mean = std::max( std::min( static_cast( base_hit - mon_defense ), 20 ), @@ -1487,7 +1492,7 @@ std::map item::dps( const bool for_display, const bool for_ std::map item::dps( const bool for_display, const bool for_calc ) const { - return dps( for_display, for_calc, g->u ); + return dps( for_display, for_calc, get_avatar() ); } double item::average_dps( const player &guy ) const @@ -1677,9 +1682,10 @@ void item::med_info( const item *med_item, std::vector &info, const it info.push_back( iteminfo( "MED", _( "Quench: " ), med_com->quench ) ); } + Character &player_character = get_player_character(); if( med_item->get_comestible_fun() != 0 && parts->test( iteminfo_parts::MED_JOY ) ) { info.push_back( iteminfo( "MED", _( "Enjoyability: " ), - g->u.fun_for( *med_item ).first ) ); + player_character.fun_for( *med_item ).first ) ); } if( med_com->stim != 0 && parts->test( iteminfo_parts::MED_STIMULATION ) ) { @@ -1695,7 +1701,7 @@ void item::med_info( const item *med_item, std::vector &info, const it if( parts->test( iteminfo_parts::MED_CONSUME_TIME ) ) { info.push_back( iteminfo( "MED", _( "Consume time: " ), - to_string( g->u.get_consume_time( *med_item ) ) ) ); + to_string( player_character.get_consume_time( *med_item ) ) ) ); } if( med_com->addict && parts->test( iteminfo_parts::DESCRIPTION_MED_ADDICTING ) ) { @@ -1709,12 +1715,13 @@ void item::food_info( const item *food_item, std::vector &info, nutrients min_nutr; nutrients max_nutr; + Character &player_character = get_player_character(); std::string recipe_exemplar = get_var( "recipe_exemplar", "" ); if( recipe_exemplar.empty() ) { - min_nutr = max_nutr = g->u.compute_effective_nutrients( *food_item ); + min_nutr = max_nutr = player_character.compute_effective_nutrients( *food_item ); } else { std::tie( min_nutr, max_nutr ) = - g->u.compute_nutrient_range( *food_item, recipe_id( recipe_exemplar ) ); + player_character.compute_nutrient_range( *food_item, recipe_id( recipe_exemplar ) ); } bool show_nutr = parts->test( iteminfo_parts::FOOD_NUTRITION ) || @@ -1745,7 +1752,7 @@ void item::food_info( const item *food_item, std::vector &info, } } - const std::pair fun_for_food_item = g->u.fun_for( *food_item ); + const std::pair fun_for_food_item = player_character.fun_for( *food_item ); if( fun_for_food_item.first != 0 && parts->test( iteminfo_parts::FOOD_JOY ) ) { info.push_back( iteminfo( "FOOD", _( "Enjoyability: " ), fun_for_food_item.first ) ); } @@ -1755,24 +1762,24 @@ void item::food_info( const item *food_item, std::vector &info, std::abs( static_cast( food_item->charges ) * batch ) ) ); } if( food_item->corpse != nullptr && parts->test( iteminfo_parts::FOOD_SMELL ) && - ( debug || ( g != nullptr && ( g->u.has_trait( trait_CARNIVORE ) || - g->u.has_artifact_with( AEP_SUPER_CLAIRVOYANCE ) ) ) ) ) { + ( debug || ( g != nullptr && ( player_character.has_trait( trait_CARNIVORE ) || + player_character.has_artifact_with( AEP_SUPER_CLAIRVOYANCE ) ) ) ) ) { info.push_back( iteminfo( "FOOD", _( "Smells like: " ) + food_item->corpse->nname() ) ); } if( parts->test( iteminfo_parts::FOOD_CONSUME_TIME ) ) { info.push_back( iteminfo( "FOOD", _( "Consume time: " ), - to_string( g->u.get_consume_time( *food_item ) ) ) ); + to_string( player_character.get_consume_time( *food_item ) ) ) ); } auto format_vitamin = [&]( const std::pair &v, bool display_vitamins ) { const bool is_vitamin = v.first->type() == vitamin_type::VITAMIN; // only display vitamins that we actually require - if( g->u.vitamin_rate( v.first ) == 0_turns || v.second == 0 || + if( player_character.vitamin_rate( v.first ) == 0_turns || v.second == 0 || display_vitamins != is_vitamin || v.first->has_flag( flag_NO_DISPLAY ) ) { return std::string(); } - const double multiplier = g->u.vitamin_rate( v.first ) / 1_days * 100; + const double multiplier = player_character.vitamin_rate( v.first ) / 1_days * 100; const int min_value = min_nutr.get_vitamin( v.first ); const int max_value = v.second; const int min_rda = std::lround( min_value * multiplier ); @@ -1806,14 +1813,14 @@ void item::food_info( const item *food_item, std::vector &info, insert_separation_line( info ); if( parts->test( iteminfo_parts::FOOD_ALLERGEN ) - && g->u.allergy_type( *food_item ) != morale_type( "morale_null" ) ) { + && player_character.allergy_type( *food_item ) != morale_type( "morale_null" ) ) { info.emplace_back( "DESCRIPTION", _( "* This food will cause an allergic reaction." ) ); } if( food_item->has_flag( flag_CANNIBALISM ) && parts->test( iteminfo_parts::FOOD_CANNIBALISM ) ) { - if( !g->u.has_trait_flag( trait_flag_CANNIBAL ) ) { + if( !player_character.has_trait_flag( trait_flag_CANNIBAL ) ) { info.emplace_back( "DESCRIPTION", _( "* This food contains human flesh." ) ); } else { @@ -1828,7 +1835,8 @@ void item::food_info( const item *food_item, std::vector &info, } ///\EFFECT_SURVIVAL >=3 allows detection of poisonous food - if( food_item->has_flag( flag_HIDDEN_POISON ) && g->u.get_skill_level( skill_survival ) >= 3 && + if( food_item->has_flag( flag_HIDDEN_POISON ) && + player_character.get_skill_level( skill_survival ) >= 3 && parts->test( iteminfo_parts::FOOD_POISON ) ) { info.emplace_back( "DESCRIPTION", _( "* On closer inspection, this appears to be " @@ -1836,7 +1844,8 @@ void item::food_info( const item *food_item, std::vector &info, } ///\EFFECT_SURVIVAL >=5 allows detection of hallucinogenic food - if( food_item->has_flag( flag_HIDDEN_HALLU ) && g->u.get_skill_level( skill_survival ) >= 5 && + if( food_item->has_flag( flag_HIDDEN_HALLU ) && + player_character.get_skill_level( skill_survival ) >= 5 && parts->test( iteminfo_parts::FOOD_HALLUCINOGENIC ) ) { info.emplace_back( "DESCRIPTION", _( "* On closer inspection, this appears to be " @@ -1865,18 +1874,19 @@ void item::food_info( const item *food_item, std::vector &info, _( "* It was frozen once and after thawing became mushy and " "tasteless. It will rot quickly if thawed again." ) ); } - if( food_item->has_flag( flag_NO_PARASITES ) && g->u.get_skill_level( skill_cooking ) >= 3 ) { + if( food_item->has_flag( flag_NO_PARASITES ) && + player_character.get_skill_level( skill_cooking ) >= 3 ) { info.emplace_back( "DESCRIPTION", _( "* It seems that deep freezing killed all " "parasites." ) ); } if( food_item->rotten() ) { - if( g->u.has_bionic( bio_digestion ) ) { + if( player_character.has_bionic( bio_digestion ) ) { info.push_back( iteminfo( "DESCRIPTION", _( "This food has started to rot, " "but your bionic digestion can tolerate " "it." ) ) ); - } else if( g->u.has_trait( trait_SAPROVORE ) ) { + } else if( player_character.has_trait( trait_SAPROVORE ) ) { info.push_back( iteminfo( "DESCRIPTION", _( "This food has started to rot, " "but you can tolerate it." ) ) ); @@ -2081,7 +2091,8 @@ void item::gun_info( const item *mod, std::vector &info, const iteminf } } - int max_gun_range = loaded_mod->gun_range( &g->u ); + avatar &player_character = get_avatar(); + int max_gun_range = loaded_mod->gun_range( &player_character ); if( max_gun_range > 0 && parts->test( iteminfo_parts::GUN_MAX_RANGE ) ) { info.emplace_back( "GUN", _( "Maximum range: " ), "", iteminfo::no_flags, max_gun_range ); @@ -2133,7 +2144,7 @@ void item::gun_info( const item *mod, std::vector &info, const iteminf // if effective sight dispersion differs from actual sight dispersion display both int act_disp = mod->sight_dispersion(); - int eff_disp = g->u.effective_dispersion( act_disp ); + int eff_disp = player_character.effective_dispersion( act_disp ); int adj_disp = eff_disp - act_disp; if( parts->test( iteminfo_parts::GUN_DISPERSION_SIGHT ) ) { @@ -2153,16 +2164,16 @@ void item::gun_info( const item *mod, std::vector &info, const iteminf bool bipod = mod->has_flag( flag_BIPOD ); - if( loaded_mod->gun_recoil( g->u ) ) { + if( loaded_mod->gun_recoil( player_character ) ) { if( parts->test( iteminfo_parts::GUN_RECOIL ) ) { info.emplace_back( "GUN", _( "Effective recoil: " ), "", iteminfo::no_newline | iteminfo::lower_is_better, - loaded_mod->gun_recoil( g->u ) ); + loaded_mod->gun_recoil( player_character ) ); } if( bipod && parts->test( iteminfo_parts::GUN_RECOIL_BIPOD ) ) { info.emplace_back( "GUN", "bipod_recoil", _( " (with bipod )" ), iteminfo::lower_is_better | iteminfo::no_name, - loaded_mod->gun_recoil( g->u, true ) ); + loaded_mod->gun_recoil( player_character, true ) ); } } info.back().bNewLine = true; @@ -2229,8 +2240,8 @@ void item::gun_info( const item *mod, std::vector &info, const iteminf if( parts->test( iteminfo_parts::GUN_AIMING_STATS ) ) { insert_separation_line( info ); info.emplace_back( "GUN", _( "Base aim speed: " ), "", iteminfo::no_flags, - g->u.aim_per_move( *mod, MAX_RECOIL ) ); - for( const aim_type &type : g->u.get_aim_types( *mod ) ) { + player_character.aim_per_move( *mod, MAX_RECOIL ) ); + for( const aim_type &type : player_character.get_aim_types( *mod ) ) { // Nameless aim levels don't get an entry. if( type.name.empty() ) { continue; @@ -2239,11 +2250,11 @@ void item::gun_info( const item *mod, std::vector &info, const iteminf // distinct tag per aim type. const std::string tag = "GUN_" + type.name; info.emplace_back( tag, string_format( "%s", type.name ) ); - int max_dispersion = g->u.get_weapon_dispersion( *loaded_mod ).max(); + int max_dispersion = player_character.get_weapon_dispersion( *loaded_mod ).max(); int range = range_with_even_chance_of_good_hit( max_dispersion + type.threshold ); info.emplace_back( tag, _( "Even chance of good hit at range: " ), _( "" ), iteminfo::no_flags, range ); - int aim_mv = g->u.gun_engagement_moves( *mod, type.threshold ); + int aim_mv = player_character.gun_engagement_moves( *mod, type.threshold ); info.emplace_back( tag, _( "Time to reach aim level: " ), _( " moves" ), iteminfo::lower_is_better, aim_mv ); } @@ -2494,8 +2505,9 @@ void item::armor_info( std::vector &info, const iteminfo_query *parts, return; } - int encumbrance = get_encumber( g->u ); - const sizing sizing_level = get_sizing( g->u, encumbrance != 0 ); + Character &player_character = get_player_character(); + int encumbrance = get_encumber( player_character ); + const sizing sizing_level = get_sizing( player_character, encumbrance != 0 ); const std::string space = " "; body_part_set covered_parts = get_covered_body_parts(); bool covers_anything = covered_parts.any(); @@ -2621,7 +2633,7 @@ void item::armor_info( std::vector &info, const iteminfo_query *parts, if( const islot_armor *t = find_armor_data() ) { if( t->max_encumber != t->encumber ) { const int encumbrance_when_full = - get_encumber( g->u, encumber_flags::assume_full ); + get_encumber( player_character, encumber_flags::assume_full ); info.push_back( iteminfo( "ARMOR", space + _( "Encumbrance when full: " ), "", iteminfo::no_newline | iteminfo::lower_is_better, encumbrance_when_full ) ); @@ -2678,8 +2690,9 @@ void item::armor_fit_info( std::vector &info, const iteminfo_query *pa return; } - int encumbrance = get_encumber( g->u ); - const sizing sizing_level = get_sizing( g->u, encumbrance != 0 ); + Character &player_character = get_player_character(); + int encumbrance = get_encumber( player_character ); + const sizing sizing_level = get_sizing( player_character, encumbrance != 0 ); if( has_flag( flag_HELMET_COMPAT ) && parts->test( iteminfo_parts::DESCRIPTION_FLAGS_HELMETCOMPAT ) ) { @@ -2843,11 +2856,12 @@ void item::book_info( std::vector &info, const iteminfo_query *parts, if( !book.skill && !type->can_use( "MA_MANUAL" ) && parts->test( iteminfo_parts::BOOK_SUMMARY ) ) { info.push_back( iteminfo( "BOOK", _( "Just for fun." ) ) ); } + avatar &player_character = get_avatar(); if( type->can_use( "MA_MANUAL" ) && parts->test( iteminfo_parts::BOOK_SUMMARY ) ) { info.push_back( iteminfo( "BOOK", _( "Some sort of martial arts training " "manual." ) ) ); - if( g->u.has_identified( typeId() ) ) { + if( player_character.has_identified( typeId() ) ) { const matype_id style_to_learn = martial_art_learned_from( *type ); info.push_back( iteminfo( "BOOK", string_format( _( "You can learn %s style " @@ -2866,9 +2880,9 @@ void item::book_info( std::vector &info, const iteminfo_query *parts, info.push_back( iteminfo( "BOOK", _( "It can be understood by " "beginners." ) ) ); } - if( g->u.has_identified( typeId() ) ) { + if( player_character.has_identified( typeId() ) ) { if( book.skill ) { - const SkillLevel &skill = g->u.get_skill_level_object( book.skill ); + const SkillLevel &skill = player_character.get_skill_level_object( book.skill ); if( skill.can_train() && parts->test( iteminfo_parts::BOOK_SKILLRANGE_MAX ) ) { const std::string skill_name = book.skill->name(); std::string fmt = string_format( _( "Can bring your %s skill to " @@ -2893,11 +2907,11 @@ void item::book_info( std::vector &info, const iteminfo_query *parts, _( "Requires intelligence of to easily " "read." ), iteminfo::lower_is_better, book.intel ) ); } - if( g->u.book_fun_for( *this, g->u ) != 0 && + if( player_character.book_fun_for( *this, player_character ) != 0 && parts->test( iteminfo_parts::BOOK_MORALECHANGE ) ) { info.push_back( iteminfo( "BOOK", "", _( "Reading this book affects your morale by " ), - iteminfo::show_plus, g->u.book_fun_for( *this, g->u ) ) ); + iteminfo::show_plus, player_character.book_fun_for( *this, player_character ) ) ); } if( parts->test( iteminfo_parts::BOOK_TIMEPERCHAPTER ) ) { std::string fmt = ngettext( @@ -2917,7 +2931,7 @@ void item::book_info( std::vector &info, const iteminfo_query *parts, } if( book.chapters > 0 && parts->test( iteminfo_parts::BOOK_NUMUNREADCHAPTERS ) ) { - const int unread = get_remaining_chapters( g->u ); + const int unread = get_remaining_chapters( player_character ); std::string fmt = ngettext( "This book has unread chapter.", "This book has unread chapters.", unread ); @@ -2927,8 +2941,9 @@ void item::book_info( std::vector &info, const iteminfo_query *parts, if( parts->test( iteminfo_parts::BOOK_INCLUDED_RECIPES ) ) { std::vector recipe_list; for( const islot_book::recipe_with_description_t &elem : book.recipes ) { - const bool knows_it = g->u.knows_recipe( elem.recipe ); - const bool can_learn = g->u.get_skill_level( elem.recipe->skill_used ) >= elem.skill_level; + const bool knows_it = player_character.knows_recipe( elem.recipe ); + const bool can_learn = player_character.get_skill_level( elem.recipe->skill_used ) >= + elem.skill_level; // If the player knows it, they recognize it even if it's not clearly stated. if( elem.is_hidden() && !knows_it ) { continue; @@ -3136,7 +3151,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(), @@ -3419,9 +3434,11 @@ void item::combat_info( std::vector &info, const iteminfo_query *parts } } + Character &player_character = get_player_character(); // display which martial arts styles character can use with this weapon if( parts->test( iteminfo_parts::DESCRIPTION_APPLICABLEMARTIALARTS ) ) { - const std::string valid_styles = g->u.martial_arts_data.enumerate_known_styles( typeId() ); + const std::string valid_styles = player_character.martial_arts_data.enumerate_known_styles( + typeId() ); if( !valid_styles.empty() ) { insert_separation_line( info ); info.push_back( iteminfo( "DESCRIPTION", @@ -3445,13 +3462,13 @@ void item::combat_info( std::vector &info, const iteminfo_query *parts } ///\EFFECT_MELEE >2 allows seeing melee damage stats on weapons - if( ( g->u.get_skill_level( skill_melee ) > 2 && + if( ( player_character.get_skill_level( skill_melee ) > 2 && ( dmg_bash || dmg_cut || dmg_stab || type->m_to_hit > 0 ) ) || debug_mode ) { damage_instance non_crit; - g->u.roll_all_damage( false, non_crit, true, *this ); + player_character.roll_all_damage( false, non_crit, true, *this ); damage_instance crit; - g->u.roll_all_damage( true, crit, true, *this ); - int attack_cost = g->u.attack_speed( *this ); + player_character.roll_all_damage( true, crit, true, *this ); + int attack_cost = player_character.attack_speed( *this ); insert_separation_line( info ); if( parts->test( iteminfo_parts::DESCRIPTION_MELEEDMG ) ) { info.push_back( iteminfo( "DESCRIPTION", _( "Average melee damage:" ) ) ); @@ -3460,9 +3477,9 @@ void item::combat_info( std::vector &info, const iteminfo_query *parts if( parts->test( iteminfo_parts::DESCRIPTION_MELEEDMG_CRIT ) ) { info.push_back( iteminfo( "DESCRIPTION", string_format( _( "Critical hit chance %d%% - %d%%" ), - static_cast( g->u.crit_chance( 0, 100, *this ) * + static_cast( player_character.crit_chance( 0, 100, *this ) * 100 ), - static_cast( g->u.crit_chance( 100, 0, *this ) * + static_cast( player_character.crit_chance( 100, 0, *this ) * 100 ) ) ) ); } // Bash damage @@ -3586,8 +3603,9 @@ void item::final_info( std::vector &info, const iteminfo_query *parts, } } + avatar &player_character = get_avatar(); if( parts->test( iteminfo_parts::DESCRIPTION_ALLERGEN ) ) { - if( is_armor() && g->u.has_trait( trait_WOOLALLERGY ) && + if( is_armor() && player_character.has_trait( trait_WOOLALLERGY ) && ( made_of( material_id( "wool" ) ) || item_tags.count( "wooled" ) ) ) { info.push_back( iteminfo( "DESCRIPTION", _( "* This clothing will give you an allergic " @@ -3794,8 +3812,9 @@ void item::final_info( std::vector &info, const iteminfo_query *parts, if( parts->test( iteminfo_parts::DESCRIPTION_APPLICABLE_RECIPES ) ) { // with the inventory display allowing you to select items, showing the things you could make with contained items could be confusing. itype_id tid = typeId(); - const inventory &crafting_inv = g->u.crafting_inventory(); - const recipe_subset available_recipe_subset = g->u.get_available_recipes( crafting_inv ); + const inventory &crafting_inv = player_character.crafting_inventory(); + const recipe_subset available_recipe_subset = player_character.get_available_recipes( + crafting_inv ); const std::set &item_recipes = available_recipe_subset.of_component( tid ); if( item_recipes.empty() ) { @@ -3970,7 +3989,7 @@ const std::string &item::symbol() const nc_color item::color_in_inventory() const { // TODO: make a const reference - avatar &u = g->u; + avatar &player_character = get_avatar(); // Only item not otherwise colored gets colored as favorite nc_color ret = is_favorite ? c_white : c_light_gray; @@ -3980,10 +3999,12 @@ nc_color item::color_in_inventory() const static_cast( iuse->get_actor_ptr() ); for( const std::string &spell_id_str : actor_ptr->spells ) { const spell_id sp_id( spell_id_str ); - if( u.magic.knows_spell( sp_id ) && !u.magic.get_spell( sp_id ).is_max_level() ) { + if( player_character.magic.knows_spell( sp_id ) && + !player_character.magic.get_spell( sp_id ).is_max_level() ) { ret = c_yellow; } - if( !u.magic.knows_spell( sp_id ) && u.magic.can_learn_spell( u, sp_id ) ) { + if( !player_character.magic.knows_spell( sp_id ) && + player_character.magic.can_learn_spell( player_character, sp_id ) ) { return c_light_blue; } } @@ -3991,14 +4012,14 @@ nc_color item::color_in_inventory() const ret = c_cyan; } else if( has_flag( flag_LITCIG ) ) { ret = c_red; - } else if( is_armor() && u.has_trait( trait_WOOLALLERGY ) && + } else if( is_armor() && player_character.has_trait( trait_WOOLALLERGY ) && ( made_of( material_id( "wool" ) ) || item_tags.count( "wooled" ) ) ) { ret = c_red; } else if( is_filthy() || item_tags.count( "DIRTY" ) ) { ret = c_brown; } else if( is_bionic() ) { - if( !u.has_bionic( type->bionic->id ) ) { - ret = u.bionic_installation_issues( type->bionic->id ).empty() ? c_green : c_red; + if( !player_character.has_bionic( type->bionic->id ) ) { + ret = player_character.bionic_installation_issues( type->bionic->id ).empty() ? c_green : c_red; } else if( !has_flag( flag_NO_STERILE ) ) { ret = c_dark_gray; } @@ -4014,7 +4035,7 @@ nc_color item::color_in_inventory() const // Give color priority to allergy (allergy > inedible by freeze or other conditions) // TODO: refactor u.will_eat to let this section handle coloring priority without duplicating code. - if( u.allergy_type( *food ) != morale_type( "morale_null" ) ) { + if( player_character.allergy_type( *food ) != morale_type( "morale_null" ) ) { return c_red; } @@ -4024,7 +4045,7 @@ nc_color item::color_in_inventory() const // Red: morale penalty // Yellow: will rot soon // Cyan: will rot eventually - const ret_val rating = u.will_eat( *food ); + const ret_val rating = player_character.will_eat( *food ); // TODO: More colors switch( rating.value() ) { case EDIBLE: @@ -4056,8 +4077,9 @@ nc_color item::color_in_inventory() const // Gun with integrated mag counts as both for( const ammotype &at : ammo_types() ) { // get_ammo finds uncontained ammo, find_ammo finds ammo in magazines - bool has_ammo = !u.get_ammo( at ).empty() || !u.find_ammo( *this, false, -1 ).empty(); - bool has_mag = magazine_integral() || !u.find_ammo( *this, true, -1 ).empty(); + bool has_ammo = !player_character.get_ammo( at ).empty() || + !player_character.find_ammo( *this, false, -1 ).empty(); + bool has_mag = magazine_integral() || !player_character.find_ammo( *this, true, -1 ).empty(); if( has_ammo && has_mag ) { ret = c_green; break; @@ -4070,10 +4092,10 @@ nc_color item::color_in_inventory() const // Likewise, ammo is green if you have guns that use it // ltred if you have the gun but no mags // Gun with integrated mag counts as both - bool has_gun = u.has_item_with( [this]( const item & i ) { + bool has_gun = player_character.has_item_with( [this]( const item & i ) { return i.is_gun() && i.ammo_types().count( ammo_type() ); } ); - bool has_mag = u.has_item_with( [this]( const item & i ) { + bool has_mag = player_character.has_item_with( [this]( const item & i ) { return ( i.is_gun() && i.magazine_integral() && i.ammo_types().count( ammo_type() ) ) || ( i.is_magazine() && i.ammo_types().count( ammo_type() ) ); } ); @@ -4085,31 +4107,31 @@ nc_color item::color_in_inventory() const } else if( is_magazine() ) { // Magazines are green if you have guns and ammo for them // ltred if you have one but not the other - bool has_gun = u.has_item_with( [this]( const item & it ) { + bool has_gun = player_character.has_item_with( [this]( const item & it ) { return it.is_gun() && it.magazine_compatible().count( typeId() ) > 0; } ); - bool has_ammo = !u.find_ammo( *this, false, -1 ).empty(); + bool has_ammo = !player_character.find_ammo( *this, false, -1 ).empty(); if( has_gun && has_ammo ) { ret = c_green; } else if( has_gun || has_ammo ) { ret = c_light_red; } } else if( is_book() ) { - if( u.has_identified( typeId() ) ) { + if( player_character.has_identified( typeId() ) ) { const islot_book &tmp = *type->book; if( tmp.skill && // Book can improve skill: blue - u.get_skill_level_object( tmp.skill ).can_train() && - u.get_skill_level( tmp.skill ) >= tmp.req && - u.get_skill_level( tmp.skill ) < tmp.level ) { + player_character.get_skill_level_object( tmp.skill ).can_train() && + player_character.get_skill_level( tmp.skill ) >= tmp.req && + player_character.get_skill_level( tmp.skill ) < tmp.level ) { ret = c_light_blue; } else if( type->can_use( "MA_MANUAL" ) && - !u.martial_arts_data.has_martialart( martial_art_learned_from( *type ) ) ) { + !player_character.martial_arts_data.has_martialart( martial_art_learned_from( *type ) ) ) { ret = c_light_blue; } else if( tmp.skill && // Book can't improve skill right now, but maybe later: pink - u.get_skill_level_object( tmp.skill ).can_train() && - u.get_skill_level( tmp.skill ) < tmp.level ) { + player_character.get_skill_level_object( tmp.skill ).can_train() && + player_character.get_skill_level( tmp.skill ) < tmp.level ) { ret = c_pink; - } else if( !u.studied_all_recipes( + } else if( !player_character.studied_all_recipes( *type ) ) { // Book can't improve skill anymore, but has more recipes: yellow ret = c_yellow; } @@ -4136,15 +4158,13 @@ void item::on_wear( Character &p ) int lhs = 0; int rhs = 0; set_side( side::LEFT ); - const auto left_enc = p.get_encumbrance( *this ); - for( const body_part bp : all_body_parts ) { - lhs += left_enc[bp].encumbrance; + for( const bodypart_id &bp : p.get_all_body_parts() ) { + lhs += p.get_part_encumbrance_data( bp ).encumbrance; } set_side( side::RIGHT ); - const auto right_enc = p.get_encumbrance( *this ); - for( const body_part bp : all_body_parts ) { - rhs += right_enc[bp].encumbrance; + for( const bodypart_id &bp : p.get_all_body_parts() ) { + rhs += p.get_part_encumbrance_data( bp ).encumbrance; } set_side( lhs <= rhs ? side::LEFT : side::RIGHT ); @@ -4152,11 +4172,11 @@ void item::on_wear( Character &p ) } // TODO: artifacts currently only work with the player character - if( &p == &g->u && type->artifact ) { + if( p.is_avatar() && type->artifact ) { g->add_artifact_messages( type->artifact->effects_worn ); } // if game is loaded - don't want ownership assigned during char creation - if( g->u.getID().is_valid() ) { + if( get_player_character().getID().is_valid() ) { handle_pickup_ownership( p ); } p.on_item_wear( *this ); @@ -4174,7 +4194,7 @@ void item::on_takeoff( Character &p ) void item::on_wield( player &p, int mv ) { // TODO: artifacts currently only work with the player character - if( &p == &g->u && type->artifact ) { + if( p.is_avatar() && type->artifact ) { g->add_artifact_messages( type->artifact->effects_wielded ); } @@ -4219,7 +4239,7 @@ void item::on_wield( player &p, int mv ) msg = _( "You wield your %s." ); } // if game is loaded - don't want ownership assigned during char creation - if( g->u.getID().is_valid() ) { + if( get_player_character().getID().is_valid() ) { handle_pickup_ownership( p ); } p.add_msg_if_player( m_neutral, msg, tname() ); @@ -4237,15 +4257,16 @@ void item::handle_pickup_ownership( Character &c ) if( is_owned_by( c ) ) { return; } + Character &player_character = get_player_character(); // Add ownership to item if unowned if( owner.is_null() ) { set_owner( c ); } else { - if( !is_owned_by( c ) && &c == &g->u ) { + if( !is_owned_by( c ) && c.is_avatar() ) { std::vector witnesses; for( npc &elem : g->all_npcs() ) { - if( rl_dist( elem.pos(), g->u.pos() ) < MAX_VIEW_DISTANCE && elem.get_faction() && - is_owned_by( elem ) && elem.sees( g->u.pos() ) ) { + if( rl_dist( elem.pos(), player_character.pos() ) < MAX_VIEW_DISTANCE && + elem.get_faction() && is_owned_by( elem ) && elem.sees( player_character.pos() ) ) { elem.say( "", 7 ); npc *npc_to_add = &elem; witnesses.push_back( npc_to_add ); @@ -4277,11 +4298,11 @@ void item::on_pickup( Character &p ) return; } // TODO: artifacts currently only work with the player character - if( &p == &g->u && type->artifact ) { + if( p.is_avatar() && type->artifact ) { g->add_artifact_messages( type->artifact->effects_carried ); } // if game is loaded - don't want ownership assigned during char creation - if( g->u.getID().is_valid() ) { + if( get_player_character().getID().is_valid() ) { handle_pickup_ownership( p ); } contents.on_pickup( p ); @@ -4422,11 +4443,13 @@ std::string item::tname( unsigned int quantity, bool with_prefix, unsigned int t maintext = label( quantity ); } + Character &player_character = get_player_character(); std::string tagtext; if( is_food() ) { - if( has_flag( flag_HIDDEN_POISON ) && g->u.get_skill_level( skill_survival ) >= 3 ) { + if( has_flag( flag_HIDDEN_POISON ) && player_character.get_skill_level( skill_survival ) >= 3 ) { tagtext += _( " (poisonous)" ); - } else if( has_flag( flag_HIDDEN_HALLU ) && g->u.get_skill_level( skill_survival ) >= 5 ) { + } else if( has_flag( flag_HIDDEN_HALLU ) && + player_character.get_skill_level( skill_survival ) >= 5 ) { tagtext += _( " (hallucinogenic)" ); } } @@ -4459,7 +4482,7 @@ std::string item::tname( unsigned int quantity, bool with_prefix, unsigned int t } } - const sizing sizing_level = get_sizing( g->u, get_encumber( g->u ) != 0 ); + const sizing sizing_level = get_sizing( player_character, get_encumber( player_character ) != 0 ); if( sizing_level == sizing::human_sized_small_char ) { tagtext += _( " (too big)" ); @@ -4510,7 +4533,7 @@ std::string item::tname( unsigned int quantity, bool with_prefix, unsigned int t if( has_flag( flag_WET ) ) { tagtext += _( " (wet)" ); } - if( already_used_by_player( g->u ) ) { + if( already_used_by_player( player_character ) ) { tagtext += _( " (used)" ); } if( active && ( has_flag( flag_WATER_EXTINGUISH ) || has_flag( flag_LITCIG ) ) ) { @@ -4590,13 +4613,14 @@ std::string item::display_name( unsigned int quantity ) const sidetxt = string_format( " (%s)", _( "right" ) ); break; } + avatar &player_character = get_avatar(); int amount = 0; int max_amount = 0; bool show_amt = false; // We should handle infinite charges properly in all cases. if( is_book() && get_chapters() > 0 ) { // a book which has remaining unread chapters - amount = get_remaining_chapters( g->u ); + amount = get_remaining_chapters( player_character ); } else if( magazine_current() ) { show_amt = true; const item *mag = magazine_current(); @@ -4675,7 +4699,7 @@ std::string item::display_name( unsigned int quantity ) const // HACK: This is a hack to prevent possible crashing when displaying maps as items during character creation if( is_map() && calendar::turn != calendar::turn_zero ) { const city *c = overmap_buffer.closest_city( omt_to_sm_copy( get_var( "reveal_map_center_omt", - g->u.global_omt_location() ) ) ).city; + player_character.global_omt_location() ) ) ).city; if( c != nullptr ) { name = string_format( "%s %s", c->name, name ); } @@ -5662,7 +5686,7 @@ bool item::ready_to_revive( const tripoint &pos ) const if( !can_revive() ) { return false; } - if( g->m.veh_at( pos ) ) { + if( get_map().veh_at( pos ) ) { return false; } if( !calendar::once_every( 1_seconds ) ) { @@ -5678,7 +5702,7 @@ bool item::ready_to_revive( const tripoint &pos ) const // If we're a special revival zombie, wait to get up until the player is nearby. const bool isReviveSpecial = has_flag( flag_REVIVE_SPECIAL ); if( isReviveSpecial ) { - const int distance = rl_dist( pos, g->u.pos() ); + const int distance = rl_dist( pos, get_player_character().pos() ); if( distance > 3 ) { return false; } @@ -7091,7 +7115,7 @@ int item::ammo_remaining() const !contents.has_pocket_type( item_pocket::pocket_type::MAGAZINE ) ) { // includes auxiliary gunmods if( has_flag( flag_USES_BIONIC_POWER ) ) { - int power = units::to_kilojoule( g->u.get_power_level() ); + int power = units::to_kilojoule( get_player_character().get_power_level() ); return power; } return charges; @@ -7192,9 +7216,10 @@ int item::ammo_consume( int qty, const tripoint &pos ) if( !contents.has_pocket_type( item_pocket::pocket_type::MAGAZINE ) || ( is_tool() && type->tool->ammo_id.empty() ) ) { qty = std::min( qty, charges ); + Character &player_character = get_player_character(); if( has_flag( flag_USES_BIONIC_POWER ) ) { - charges = units::to_kilojoule( g->u.get_power_level() ); - g->u.mod_power_level( units::from_kilojoule( -qty ) ); + charges = units::to_kilojoule( player_character.get_power_level() ); + player_character.mod_power_level( units::from_kilojoule( -qty ) ); } charges -= qty; if( charges == 0 ) { @@ -8418,6 +8443,15 @@ const item_category &item::get_category() const return type->category_force.is_valid() ? type->category_force.obj() : null_category; } +const item_category &item::get_category_of_contents() const +{ + if( type->category_force == item_category_container && contents.num_item_stacks() == 1 ) { + return contents.only_item().get_category(); + } else { + return this->get_category(); + } +} + iteminfo::iteminfo( const std::string &Type, const std::string &Name, const std::string &Fmt, flags Flags, double Value ) { @@ -8554,7 +8588,7 @@ bool item_compare_by_charges( const item &left, const item &right ) } static const std::string USED_BY_IDS( "USED_BY_IDS" ); -bool item::already_used_by_player( const player &p ) const +bool item::already_used_by_player( const Character &p ) const { const auto it = item_vars.find( USED_BY_IDS ); if( it == item_vars.end() ) { @@ -8731,7 +8765,7 @@ bool item::process_temperature_rot( float insulation, const tripoint &pos, const weather_generator &wgen = g->weather.get_cur_weather_gen(); const unsigned int seed = g->get_seed(); - int local_mod = g->new_game ? 0 : g->m.get_temperature( pos ); + int local_mod = g->new_game ? 0 : get_map().get_temperature( pos ); int enviroment_mod; // Toilets and vending machines will try to get the heat radiation and convection during mapgen and segfault. @@ -9049,7 +9083,7 @@ void item::process_artifact( player *carrier, const tripoint & /*pos*/ ) // don't consider npcs. Also they are not processed when laying on the ground. // TODO: change game::process_artifact to work with npcs, // TODO: consider moving game::process_artifact here. - if( carrier == &g->u ) { + if( carrier && carrier->is_avatar() ) { g->process_artifact( *this, *carrier ); } } @@ -9096,7 +9130,7 @@ bool item::process_corpse( player *carrier, const tripoint &pos ) } if( rng( 0, volume() / units::legacy_volume_factor ) > burnt && g->revive_corpse( pos, *this ) ) { if( carrier == nullptr ) { - if( g->u.sees( pos ) ) { + if( get_player_character().sees( pos ) ) { if( corpse->in_species( species_ROBOT ) ) { add_msg( m_warning, _( "A nearby robot has repaired itself and stands up!" ) ); } else { @@ -9121,13 +9155,15 @@ bool item::process_corpse( player *carrier, const tripoint &pos ) bool item::process_fake_mill( player * /*carrier*/, const tripoint &pos ) { - if( g->m.furn( pos ) != furn_str_id( "f_wind_mill_active" ) && - g->m.furn( pos ) != furn_str_id( "f_water_mill_active" ) ) { + map &here = get_map(); + if( here.furn( pos ) != furn_str_id( "f_wind_mill_active" ) && + here.furn( pos ) != furn_str_id( "f_water_mill_active" ) ) { item_counter = 0; return true; //destroy fake mill } if( age() >= 6_hours || item_counter == 0 ) { - iexamine::mill_finalize( g->u, pos, birthday() ); //activate effects when timers goes to zero + iexamine::mill_finalize( get_avatar(), pos, + birthday() ); //activate effects when timers goes to zero return true; //destroy fake mill item } @@ -9136,8 +9172,9 @@ bool item::process_fake_mill( player * /*carrier*/, const tripoint &pos ) bool item::process_fake_smoke( player * /*carrier*/, const tripoint &pos ) { - if( g->m.furn( pos ) != furn_str_id( "f_smoking_rack_active" ) && - g->m.furn( pos ) != furn_str_id( "f_metal_smoking_rack_active" ) ) { + map &here = get_map(); + if( here.furn( pos ) != furn_str_id( "f_smoking_rack_active" ) && + here.furn( pos ) != furn_str_id( "f_metal_smoking_rack_active" ) ) { item_counter = 0; return true; //destroy fake smoke } @@ -9160,6 +9197,7 @@ bool item::process_litcig( player *carrier, const tripoint &pos ) if( !active ) { return false; } + map &here = get_map(); // if carried by someone: if( carrier != nullptr ) { time_duration duration = 15_seconds; @@ -9180,24 +9218,24 @@ bool item::process_litcig( player *carrier, const tripoint &pos ) ( carrier->has_trait( trait_JITTERY ) && one_in( 200 ) ) ) { carrier->add_msg_if_player( m_bad, _( "Your shaking hand causes you to drop your %s." ), tname() ); - g->m.add_item_or_charges( pos + point( rng( -1, 1 ), rng( -1, 1 ) ), *this ); + here.add_item_or_charges( pos + point( rng( -1, 1 ), rng( -1, 1 ) ), *this ); return true; // removes the item that has just been added to the map } if( carrier->has_effect( effect_sleep ) ) { carrier->add_msg_if_player( m_bad, _( "You fall asleep and drop your %s." ), tname() ); - g->m.add_item_or_charges( pos + point( rng( -1, 1 ), rng( -1, 1 ) ), *this ); + here.add_item_or_charges( pos + point( rng( -1, 1 ), rng( -1, 1 ) ), *this ); return true; // removes the item that has just been added to the map } } else { // If not carried by someone, but laying on the ground: if( item_counter % 5 == 0 ) { // lit cigarette can start fires - if( g->m.flammable_items_at( pos ) || - g->m.has_flag( flag_FLAMMABLE, pos ) || - g->m.has_flag( flag_FLAMMABLE_ASH, pos ) ) { - g->m.add_field( pos, fd_fire, 1 ); + if( here.flammable_items_at( pos ) || + here.has_flag( flag_FLAMMABLE, pos ) || + here.has_flag( flag_FLAMMABLE_ASH, pos ) ) { + here.add_field( pos, fd_fire, 1 ); } } } @@ -9215,7 +9253,7 @@ bool item::process_litcig( player *carrier, const tripoint &pos ) convert( itype_joint_roach ); if( carrier != nullptr ) { carrier->add_effect( effect_weed_high, 1_minutes ); // one last puff - g->m.add_field( pos + point( rng( -1, 1 ), rng( -1, 1 ) ), fd_weedsmoke, 2 ); + here.add_field( pos + point( rng( -1, 1 ), rng( -1, 1 ) ), fd_weedsmoke, 2 ); weed_msg( *carrier ); } } @@ -9233,33 +9271,27 @@ bool item::process_extinguish( player *carrier, const tripoint &pos ) bool submerged = false; bool precipitation = false; bool windtoostrong = false; - w_point weatherPoint = *g->weather.weather_precise; - int windpower = g->weather.windspeed; - switch( g->weather.weather ) { - case WEATHER_LIGHT_DRIZZLE: + w_point weatherPoint = *get_weather().weather_precise; + int windpower = get_weather().windspeed; + switch( get_weather().weather_id->precip ) { + case precip_class::very_light: precipitation = one_in( 100 ); break; - case WEATHER_DRIZZLE: - case WEATHER_FLURRIES: + case precip_class::light: precipitation = one_in( 50 ); break; - case WEATHER_RAINY: - case WEATHER_SNOW: - precipitation = one_in( 25 ); - break; - case WEATHER_THUNDER: - case WEATHER_LIGHTNING: - case WEATHER_SNOWSTORM: + case precip_class::heavy: precipitation = one_in( 10 ); break; default: break; } - if( in_inv && g->m.has_flag( flag_DEEP_WATER, pos ) ) { + map &here = get_map(); + if( in_inv && here.has_flag( flag_DEEP_WATER, pos ) ) { extinguish = true; submerged = true; } - if( ( !in_inv && g->m.has_flag( flag_LIQUID, pos ) ) || + if( ( !in_inv && here.has_flag( flag_LIQUID, pos ) ) || ( precipitation && !g->is_sheltered( pos ) ) ) { extinguish = true; } @@ -9297,7 +9329,7 @@ bool item::process_extinguish( player *carrier, const tripoint &pos ) if( type->tool->revert_to ) { convert( *type->tool->revert_to ); } else { - type->invoke( carrier != nullptr ? *carrier : g->u, *this, pos, "transform" ); + type->invoke( carrier != nullptr ? *carrier : get_avatar(), *this, pos, "transform" ); } } @@ -9312,7 +9344,8 @@ cata::optional item::get_cable_target( Character *p, const tripoint &p if( state != "pay_out_cable" && state != "cable_charger_link" ) { return cata::nullopt; } - const optional_vpart_position vp_pos = g->m.veh_at( pos ); + map &here = get_map(); + const optional_vpart_position vp_pos = here.veh_at( pos ); if( vp_pos ) { const cata::optional seat = vp_pos.part_with_feature( "BOARDABLE", true ); if( seat && p == seat->vehicle().get_passenger( seat->part_index() ) ) { @@ -9323,7 +9356,7 @@ cata::optional item::get_cable_target( Character *p, const tripoint &p tripoint source2( get_var( "source_x", 0 ), get_var( "source_y", 0 ), get_var( "source_z", 0 ) ); tripoint source( source2 ); - return g->m.getlocal( source ); + return here.getlocal( source ); } bool item::process_cable( player *carrier, const tripoint &pos ) @@ -9360,7 +9393,8 @@ bool item::process_cable( player *carrier, const tripoint &pos ) return false; } - if( !g->m.veh_at( *source ) || ( source->z != g->get_levz() && !g->m.has_zlevels() ) ) { + map &here = get_map(); + if( !here.veh_at( *source ) || ( source->z != g->get_levz() && !here.has_zlevels() ) ) { if( carrier->has_item( *this ) ) { carrier->add_msg_if_player( m_bad, _( "You notice the cable has come loose!" ) ); } @@ -9452,6 +9486,7 @@ bool item::process_tool( player *carrier, const tripoint &pos ) } } + avatar &player_character = get_avatar(); // if insufficient available charges shutdown the tool if( energy > 0 ) { if( carrier && has_flag( flag_USE_UPS ) ) { @@ -9460,7 +9495,7 @@ bool item::process_tool( player *carrier, const tripoint &pos ) // invoking the object can convert the item to another type const bool had_revert_to = type->tool->revert_to.has_value(); - type->invoke( carrier != nullptr ? *carrier : g->u, *this, pos ); + type->invoke( carrier != nullptr ? *carrier : player_character, *this, pos ); if( had_revert_to ) { deactivate( carrier ); return false; @@ -9469,7 +9504,7 @@ bool item::process_tool( player *carrier, const tripoint &pos ) } } - type->tick( carrier != nullptr ? *carrier : g->u, *this, pos ); + type->tick( carrier != nullptr ? *carrier : player_character, *this, pos ); return false; } @@ -9534,14 +9569,15 @@ bool item::process_internal( player *carrier, const tripoint &pos, } if( item_counter == 0 && type->countdown_action ) { - type->countdown_action.call( carrier ? *carrier : g->u, *this, false, pos ); + type->countdown_action.call( carrier ? *carrier : get_avatar(), *this, false, pos ); if( type->countdown_destroy ) { return true; } } + map &here = get_map(); for( const emit_id &e : type->emits ) { - g->m.emit_field( pos, e ); + here.emit_field( pos, e ); } if( has_flag( flag_FAKE_SMOKE ) && process_fake_smoke( carrier, pos ) ) { @@ -9579,7 +9615,7 @@ bool item::process_internal( player *carrier, const tripoint &pos, if( has_temperature() && process_temperature_rot( insulation, pos, carrier, flag, spoil_modifier ) ) { if( is_comestible() ) { - g->m.rotten_item_spawn( *this, pos ); + here.rotten_item_spawn( *this, pos ); } return true; } @@ -9857,7 +9893,7 @@ bool item::is_filthy() const bool item::on_drop( const tripoint &pos ) { - return on_drop( pos, g->m ); + return on_drop( pos, get_map() ); } bool item::on_drop( const tripoint &pos, map &m ) @@ -9869,8 +9905,9 @@ bool item::on_drop( const tripoint &pos, map &m ) item_tags.insert( "DIRTY" ); } - g->u.flag_encumbrance(); - return type->drop_action && type->drop_action.call( g->u, *this, false, pos ); + avatar &player_character = get_avatar(); + player_character.flag_encumbrance(); + return type->drop_action && type->drop_action.call( player_character, *this, false, pos ); } time_duration item::age() const @@ -10035,7 +10072,7 @@ units::volume item::check_for_free_space( const item *it ) const if( containedPockets.success() ) { volume += check_for_free_space( container ); - for( auto pocket : containedPockets.value() ) { + for( const auto &pocket : containedPockets.value() ) { if( pocket.rigid() ) { volume += pocket.remaining_volume(); } diff --git a/src/item.h b/src/item.h index 4d25475c675c9..04cfa9799e707 100644 --- a/src/item.h +++ b/src/item.h @@ -463,6 +463,9 @@ class item : public visitable // Returns the category of this item. const item_category &get_category() const; + // Returns the category of item inside this item. I.e. "can of meat" would be food, instead of container. + // If there are multiple items/stacks or none then it defaults to category of this item. + const item_category &get_category_of_contents() const; class reload_option { @@ -738,9 +741,9 @@ 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. */ - 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, @@ -1094,7 +1097,7 @@ class item : public visitable * Check whether the item has been marked (by calling mark_as_used_by_player) * as used by this specific player. */ - bool already_used_by_player( const player &p ) const; + bool already_used_by_player( const Character &p ) const; /** * Marks the item as being used by this specific player, it remains unmarked * for other players. The player is identified by its id. diff --git a/src/item_contents.cpp b/src/item_contents.cpp index 37bf67cabea6b..635fe585c1fe4 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, + 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 @@ -375,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 ); 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 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_location.cpp b/src/item_location.cpp index 51a614767f147..0474e4034b4ff 100644 --- a/src/item_location.cpp +++ b/src/item_location.cpp @@ -3,7 +3,6 @@ #include #include -#include "avatar.h" #include "character.h" #include "character_id.h" #include "color.h" @@ -19,7 +18,6 @@ #include "map.h" #include "map_selector.h" #include "optional.h" -#include "player.h" #include "point.h" #include "safe_reference.h" #include "translations.h" @@ -217,7 +215,7 @@ class item_location::impl::item_on_map : public item_location::impl obj = *target(); } - int mv = dynamic_cast( &ch )->item_handling_cost( obj, true, MAP_HANDLING_PENALTY ); + int mv = ch.item_handling_cost( obj, true, MAP_HANDLING_PENALTY ); mv += 100 * rl_dist( ch.pos(), cur ); // TODO: handle unpacking costs @@ -444,8 +442,7 @@ class item_location::impl::item_on_vehicle : public item_location::impl obj = *target(); } - int mv = dynamic_cast( &ch )->item_handling_cost( obj, true, - VEHICLE_HANDLING_PENALTY ); + int mv = ch.item_handling_cost( obj, true, VEHICLE_HANDLING_PENALTY ); mv += 100 * rl_dist( ch.pos(), cur.veh.global_part_pos3( cur.part ) ); // TODO: handle unpacking costs @@ -658,7 +655,7 @@ void item_location::deserialize( JsonIn &js ) } else { // This is for migrating saves before npc item locations were supported and all // character item locations were assumed to be on g->u - who_id = g->u.getID(); + who_id = get_player_character().getID(); } ptr.reset( new impl::item_on_person( who_id, idx ) ); 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(); } diff --git a/src/iuse.cpp b/src/iuse.cpp index b02f7f613781d..d6fdb8bd607fd 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,17 +705,18 @@ 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 ) && + 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() ); @@ -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 ); - for( const tripoint &dest : closest_tripoints_first( p.pos(), 4 ) ) { - if( g->m.impassable( dest ) ) { + map &here = get_map(); + fungal_effects fe( *g, here ); + for( const tripoint &dest : closest_points_first( p.pos(), 4 ) ) { + 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 ) ) { @@ -2023,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 ) ); @@ -2039,11 +2048,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(); @@ -2066,7 +2075,7 @@ int iuse::rm13armor_off( player *p, item *it, bool, const tripoint & ) p->add_msg_if_player( _( "Electro-reactive armor system: ONLINE." ) ); p->add_msg_if_player( _( "All systems nominal." ) ); it->convert( itype_id( oname ) ).active = true; - p->reset_encumbrance(); + p->calc_encumbrance(); return it->type->charges_to_use(); } } @@ -2086,7 +2095,7 @@ int iuse::rm13armor_on( player *p, item *it, bool t, const tripoint & ) p->add_msg_if_player( _( "Shutting down." ) ); p->add_msg_if_player( _( "Your RM13 combat armor turns off." ) ); it->convert( itype_id( oname ) ).active = false; - p->reset_encumbrance(); + p->calc_encumbrance(); } return it->type->charges_to_use(); } @@ -2401,11 +2410,13 @@ int iuse::hammer( player *p, item *it, bool, const tripoint & ) t_rdoor_boarded_damaged }; - const std::function f = [&allowed_ter_id]( const tripoint & pnt ) { - if( pnt == g->u.pos() ) { + map &here = get_map(); + const std::function f = + [&allowed_ter_id, &here, p]( const tripoint & pnt ) { + if( pnt == p->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 +2428,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 +2476,14 @@ int iuse::crowbar( player *p, item *it, bool, const tripoint &pos ) f_coffin_c }; - const std::function f = [&allowed_ter_id, - &allowed_furn_id]( const tripoint & pnt ) { - if( pnt == g->u.pos() ) { + map &here = get_map(); + 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 = 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 +2496,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 +2530,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 +2592,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 +2625,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 +2658,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 +2788,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 +2812,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.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 ); 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 +2837,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( @@ -2842,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; @@ -2900,11 +2914,13 @@ 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.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 ) ); if( !can_dig_here ) { p->add_msg_if_player( @@ -2912,8 +2928,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 +2946,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 +2984,13 @@ int iuse::fill_pit( player *p, item *it, bool t, const tripoint & ) t_dirtmound }; - const std::function f = [&allowed_ter_id]( const tripoint & pnt ) { - if( pnt == g->u.pos() ) { + map &here = get_map(); + const std::function f = + [&allowed_ter_id, &here, p]( const tripoint & pnt ) { + if( pnt == p->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 +3000,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." ) ); @@ -3003,8 +3021,8 @@ 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 int helpersize = g->u.get_num_crafting_helpers( 3 ); + 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 ) { add_msg( m_info, _( "%s helps with this task…" ), np->name ); @@ -3030,7 +3048,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( @@ -3045,12 +3063,12 @@ 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; } - 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 +3084,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 +3115,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 +3356,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 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 ) { add_msg( m_info, _( "%s helps with this task…" ), np->name ); @@ -3361,9 +3381,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 +3425,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 +3453,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 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 ) { add_msg( m_info, _( "%s helps with this task…" ), np->name ); @@ -3459,9 +3480,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 +3510,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(); } @@ -3567,7 +3590,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; @@ -3582,7 +3605,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,17 +3643,19 @@ 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; } + 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() ); } @@ -3639,7 +3664,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 ) ) { @@ -3653,13 +3678,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 ) ) { + if( player_character.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 +3706,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!" ), @@ -3696,12 +3722,13 @@ 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!" ), 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 +3740,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( @@ -3728,21 +3755,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 ); } } } @@ -3753,7 +3781,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( @@ -3768,20 +3796,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 ) ); } } } @@ -3792,7 +3820,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 ); @@ -3800,9 +3828,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; @@ -3810,9 +3838,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 +3868,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 +3890,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 +3935,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 +4068,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; @@ -4050,7 +4081,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 ) { @@ -4072,7 +4103,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(); } @@ -4266,7 +4297,8 @@ static std::string get_music_description() return _( "a sweet guitar solo!" ); } -void iuse::play_music( player &p, const tripoint &source, const int volume, const int max_morale ) +void iuse::play_music( Character &p, const tripoint &source, const int volume, + const int max_morale ) { // TODO: what about other "player", e.g. when a NPC is listening or when the PC is listening, // the other characters around should be able to profit as well. @@ -4442,7 +4474,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 ) { @@ -4725,7 +4757,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() ); @@ -4744,9 +4776,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 +4803,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 +4859,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 +4902,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 +4914,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 +4952,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 +4966,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 ) { - if( pnt == g->u.pos() ) { + map &here = get_map(); + const std::function f = [&here, p]( const tripoint & pnt ) { + if( pnt == p->pos() ) { return false; } - return g->m.has_flag( "TREE", pnt ); + return here.has_flag( "TREE", pnt ); }; const cata::optional pnt_ = choose_adjacent_highlight( @@ -4954,13 +4989,13 @@ 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; } 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 +5014,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; }; @@ -4997,13 +5033,13 @@ 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; } 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 +5081,14 @@ int iuse::oxytorch( player *p, item *it, bool, const tripoint & ) f_rack }; - const std::function f = [&allowed_ter_id, - &allowed_furn_id]( const tripoint & pnt ) { - if( pnt == g->u.pos() ) { + map &here = get_map(); + 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 = 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 +5101,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 +5172,14 @@ int iuse::hacksaw( player *p, item *it, bool t, const tripoint & ) const std::set allowed_furn_id { f_rack }; - const std::function f = [&allowed_ter_id, - &allowed_furn_id]( const tripoint & pnt ) { - if( pnt == g->u.pos() ) { + map &here = get_map(); + 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 = 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 +5192,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 +5204,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 +5234,13 @@ 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 ) { - if( pnt == g->u.pos() ) { + map &here = get_map(); + const std::function f = + [&allowed_ter_id, &here, p]( const tripoint & pnt ) { + if( pnt == p->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 +5251,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 +5264,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 +5299,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 +5310,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 +5354,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 +5389,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 +5410,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,9 +5455,9 @@ 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 ) && - ( blood || g->u.sees( tmp ) ) ) { + for( const tripoint &tmp : here.points_in_radius( p->pos(), 4 ) ) { + if( !one_in( 4 ) && here.add_field( tmp, fd_blood, 3 ) && + ( blood || get_player_character().sees( tmp ) ) ) { blood = true; } } @@ -5428,14 +5470,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 +5486,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 +5502,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 +5510,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 +5571,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; @@ -5555,10 +5597,10 @@ int iuse::artifact( player *p, item *it, bool, const tripoint & ) case AEA_FIRESTORM: { p->add_msg_if_player( m_bad, _( "Fire rains down around you!" ) ); - std::vector ps = closest_tripoints_first( p->pos(), 3 ); + std::vector ps = closest_points_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 +5659,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 +5714,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 +5740,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 +5796,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?" ) ) ) { @@ -5803,7 +5846,7 @@ int iuse::towel( player *p, item *it, bool t, const tripoint & ) return towel_common( p, it, t ); } -int iuse::towel_common( player *p, item *it, bool t ) +int iuse::towel_common( Character *p, item *it, bool t ) { if( t ) { // Continuous usage, do nothing as not initiated by the player, this is for @@ -5881,21 +5924,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 +5948,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() ) { @@ -6088,7 +6132,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; @@ -6958,9 +7003,9 @@ 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 ) { + if( trap.can_see( point, get_player_character() ) ) { name = colorize( trap.name(), trap.color ) + _( " on " ); } return name; @@ -6969,7 +7014,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 +7045,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 +7059,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 +7095,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 +7238,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 +7254,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 +7267,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 +7391,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 +7410,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++; } @@ -7378,7 +7427,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() + "\""; } @@ -7447,15 +7496,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 +7521,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 +7596,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 ) { @@ -7576,9 +7625,8 @@ static extended_photo_def photo_def_for_camera_point( const tripoint &aim_point, } else { photo_text += _( "It is day. " ); } - - const weather_datum w_data = weather_data( g->weather.weather ); - photo_text += string_format( _( "The weather is %s." ), colorize( w_data.name, w_data.color ) ); + photo_text += string_format( _( "The weather is %s." ), colorize( get_weather().weather_id->name, + get_weather().weather_id->color ) ); } for( const auto &figure : description_figures_appearance ) { @@ -7752,6 +7800,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 +7810,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 +7825,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 +8063,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 +8283,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 +8342,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 +8350,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 +8477,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 && @@ -8498,7 +8549,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 ); @@ -8511,7 +8563,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 { @@ -8532,8 +8584,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(); } @@ -8575,7 +8627,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 +8680,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; @@ -8853,7 +8905,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 @@ -8862,7 +8914,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( @@ -8882,9 +8934,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 ); @@ -8934,7 +8986,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." ), @@ -9010,6 +9062,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 +9070,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 +9087,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 +9137,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 +9209,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 +9257,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 +9368,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 +9390,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 +9398,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 +9407,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 ) ) { @@ -9399,7 +9453,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…" ), @@ -9418,13 +9472,13 @@ int iuse::weather_tool( player *p, item *it, bool, const tripoint & ) if( it->typeId() == itype_hygrometer ) { 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() ) ) ) ); + print_humidity( get_local_humidity( weatherPoint.humidity, get_weather().weather_id, + 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() ) ) ) ); + print_humidity( get_local_humidity( weatherPoint.humidity, get_weather().weather_id, + g->is_sheltered( p->pos() ) ) ) ); } } if( it->has_flag( "BAROMETER" ) ) { @@ -9440,7 +9494,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 +9692,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 +9707,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; } @@ -9759,7 +9814,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 ); @@ -9797,7 +9852,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 ); @@ -9813,8 +9868,8 @@ 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 int helpersize = g->u.get_num_crafting_helpers( 3 ); + 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 ) { add_msg( m_info, _( "%s helps with this task…" ), np->name ); @@ -9847,18 +9902,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; diff --git a/src/iuse.h b/src/iuse.h index 6de160f2a237e..5e076cc5acac7 100644 --- a/src/iuse.h +++ b/src/iuse.h @@ -229,8 +229,8 @@ int artifact( player *, item *, bool, const tripoint & ); // Helper functions for other iuse functions void cut_log_into_planks( player & ); -void play_music( player &p, const tripoint &source, int volume, int max_morale ); -int towel_common( player *, item *, bool ); +void play_music( Character &p, const tripoint &source, int volume, int max_morale ); +int towel_common( Character *, item *, bool ); // Helper for validating a potential taget of robot control bool robotcontrol_can_target( player *, const monster & ); diff --git a/src/iuse_actor.cpp b/src/iuse_actor.cpp index 5b8dbc93da0b5..1493dc96a074f 100644 --- a/src/iuse_actor.cpp +++ b/src/iuse_actor.cpp @@ -273,7 +273,7 @@ int iuse_transform::use( player &p, item &it, bool t, const tripoint &pos ) cons it.put_in( *obj, item_pocket::pocket_type::CONTAINER ); } if( p.is_worn( *obj ) ) { - p.reset_encumbrance(); + p.calc_encumbrance(); p.update_bodytemp(); p.on_worn_item_transform( obj_copy, *obj ); } @@ -370,6 +370,7 @@ int unpack_actor::use( player &p, item &it, bool, const tripoint & ) const p.add_msg_if_player( _( "You unpack the %s." ), it.tname() ); + map &here = get_map(); for( item &content : items ) { if( content.is_armor() ) { if( items_fit ) { @@ -389,7 +390,7 @@ int unpack_actor::use( player &p, item &it, bool, const tripoint & ) const content.set_flag( "FILTHY" ); } - g->m.add_item_or_charges( p.pos(), content ); + here.add_item_or_charges( p.pos(), content ); } p.i_rem( &it ); @@ -475,13 +476,14 @@ std::unique_ptr explosion_iuse::clone() const // They must also be passable. static std::vector points_for_gas_cloud( const tripoint ¢er, int radius ) { + map &here = get_map(); std::vector result; - for( const auto &p : closest_tripoints_first( center, radius ) ) { - if( g->m.impassable( p ) ) { + for( const auto &p : closest_points_first( center, radius ) ) { + if( here.impassable( p ) ) { continue; } if( p != center ) { - if( !g->m.clear_path( center, p, radius, 1, 100 ) ) { + if( !here.clear_path( center, p, radius, 1, 100 ) ) { // Can not splatter gas from center to that point, something is in the way continue; } @@ -551,20 +553,21 @@ int explosion_iuse::use( player &p, item &it, bool t, const tripoint &pos ) cons if( do_flashbang ) { explosion_handler::flashbang( pos, flashbang_player_immune ); } + map &here = get_map(); if( fields_radius >= 0 && fields_type.id() ) { std::vector gas_sources = points_for_gas_cloud( pos, fields_radius ); for( auto &gas_source : gas_sources ) { const int field_intensity = rng( fields_min_intensity, fields_max_intensity ); - g->m.add_field( gas_source, fields_type, field_intensity, 1_turns ); + here.add_field( gas_source, fields_type, field_intensity, 1_turns ); } } if( scrambler_blast_radius >= 0 ) { - for( const tripoint &dest : g->m.points_in_radius( pos, scrambler_blast_radius ) ) { + for( const tripoint &dest : here.points_in_radius( pos, scrambler_blast_radius ) ) { explosion_handler::scrambler_blast( dest ); } } if( emp_blast_radius >= 0 ) { - for( const tripoint &dest : g->m.points_in_radius( pos, emp_blast_radius ) ) { + for( const tripoint &dest : here.points_in_radius( pos, emp_blast_radius ) ) { explosion_handler::emp_blast( dest ); } } @@ -619,7 +622,7 @@ int unfold_vehicle_iuse::use( player &p, item &it, bool, const tripoint & ) cons } } - vehicle *veh = g->m.add_vehicle( vehicle_id, p.pos(), 0, 0, 0, false ); + vehicle *veh = get_map().add_vehicle( vehicle_id, 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; @@ -721,7 +724,7 @@ void consume_drug_iuse::info( const item &, std::vector &dump ) const { const std::string vits = enumerate_as_string( vitamins.begin(), vitamins.end(), []( const decltype( vitamins )::value_type & v ) { - const time_duration rate = g->u.vitamin_rate( v.first ); + const time_duration rate = get_player_character().vitamin_rate( v.first ); if( rate <= 0_turns ) { return std::string(); } @@ -786,10 +789,11 @@ int consume_drug_iuse::use( player &p, item &it, bool, const tripoint & ) const for( const auto &stat_adjustment : stat_adjustments ) { p.mod_stat( stat_adjustment.first, stat_adjustment.second ); } + map &here = get_map(); for( const auto &field : fields_produced ) { const field_type_id fid = field_type_id( field.first ); 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()}, fid, field.second ); } @@ -964,11 +968,12 @@ void place_npc_iuse::load( const JsonObject &obj ) int place_npc_iuse::use( player &p, item &, bool, const tripoint & ) const { + map &here = get_map(); cata::optional target_pos; if( place_randomly ) { const tripoint_range target_range = points_in_radius( p.pos(), 1 ); - target_pos = random_point( target_range, []( const tripoint & t ) { - return !g->m.passable( t ); + target_pos = random_point( target_range, [&here]( const tripoint & t ) { + return !here.passable( t ); } ); } else { const std::string query = _( "Place npc where?" ); @@ -977,12 +982,12 @@ int place_npc_iuse::use( player &p, item &, bool, const tripoint & ) const if( !target_pos ) { return 0; } - if( !g->m.passable( target_pos.value() ) ) { + if( !here.passable( target_pos.value() ) ) { p.add_msg_if_player( m_info, _( "There is no square to spawn npc in!" ) ); return 0; } - g->m.place_npc( target_pos.value().xy(), npc_class_id ); + here.place_npc( target_pos.value().xy(), npc_class_id ); p.mod_moves( -moves ); p.add_msg_if_player( m_info, "%s", _( summon_msg ) ); return 1; @@ -1024,7 +1029,7 @@ int ups_based_armor_actor::use( player &p, item &it, bool t, const tripoint & ) return 0; } it.active = !it.active; - p.reset_encumbrance(); + p.calc_encumbrance(); if( it.active ) { if( activate_msg.empty() ) { p.add_msg_if_player( m_info, _( "You activate your %s." ), it.tname() ); @@ -1115,7 +1120,8 @@ int deploy_furn_actor::use( player &p, item &it, bool, const tripoint &pos ) con return 0; } - optional_vpart_position veh_there = g->m.veh_at( pnt ); + map &here = get_map(); + optional_vpart_position veh_there = here.veh_at( pnt ); if( veh_there.has_value() ) { // TODO: check for protrusion+short furniture, wheels+tiny furniture, NOCOLLIDE flag, etc. // and/or integrate furniture deployment with construction (which already seems to perform these checks sometimes?) @@ -1125,17 +1131,17 @@ int deploy_furn_actor::use( player &p, item &it, bool, const tripoint &pos ) con } // For example: dirt = 2, long grass = 3 - if( g->m.move_cost( pnt ) != 2 && g->m.move_cost( pnt ) != 3 ) { + if( here.move_cost( pnt ) != 2 && here.move_cost( pnt ) != 3 ) { p.add_msg_if_player( m_info, _( "You can't deploy a %s there." ), it.tname() ); return 0; } - if( g->m.has_furn( pnt ) ) { + if( here.has_furn( pnt ) ) { p.add_msg_if_player( m_info, _( "There is already furniture at that location." ) ); return 0; } - g->m.furn_set( pnt, furn_type ); + here.furn_set( pnt, furn_type ); p.mod_moves( to_turns( 2_seconds ) ); return 1; } @@ -1230,14 +1236,15 @@ bool firestarter_actor::prep_firestarter_use( const player &p, tripoint &pos ) p.add_msg_if_player( _( "But you're already smokin' hot." ) ); return false; } - if( g->m.get_field( pos, fd_fire ) ) { + map &here = get_map(); + if( here.get_field( pos, fd_fire ) ) { // check if there's already a fire p.add_msg_if_player( m_info, _( "There is already a fire." ) ); return false; } // Check for a brazier. bool has_unactivated_brazier = false; - for( const item &i : g->m.i_at( pos ) ) { + for( const item &i : here.i_at( pos ) ) { if( i.typeId() == itype_brazier ) { has_unactivated_brazier = true; } @@ -1249,7 +1256,7 @@ bool firestarter_actor::prep_firestarter_use( const player &p, tripoint &pos ) void firestarter_actor::resolve_firestarter_use( player &p, const tripoint &pos ) { - if( g->m.add_field( pos, fd_fire, 1, 10_minutes ) ) { + if( get_map().add_field( pos, fd_fire, 1, 10_minutes ) ) { if( !p.has_trait( trait_PYROMANIA ) ) { p.add_msg_if_player( _( "You successfully light a fire." ) ); } else { @@ -1290,8 +1297,8 @@ float firestarter_actor::light_mod( const tripoint &pos ) const } const float light_level = g->natural_light_level( pos.z ); - if( ( g->weather.weather == WEATHER_CLEAR || g->weather.weather == WEATHER_SUNNY ) && - light_level >= 60.0f && !g->m.has_flag( TFLAG_INDOORS, pos ) ) { + if( get_weather().weather_id->sun_intensity >= sun_intensity_type::normal && + light_level >= 60.0f && !get_map().has_flag( TFLAG_INDOORS, pos ) ) { return std::pow( light_level / 80.0f, 8 ); } @@ -1300,11 +1307,12 @@ float firestarter_actor::light_mod( const tripoint &pos ) const int firestarter_actor::moves_cost_by_fuel( const tripoint &pos ) const { - if( g->m.flammable_items_at( pos, 100 ) ) { + map &here = get_map(); + if( here.flammable_items_at( pos, 100 ) ) { return moves_cost_fast; } - if( g->m.flammable_items_at( pos, 10 ) ) { + if( here.flammable_items_at( pos, 10 ) ) { return ( moves_cost_slow + moves_cost_fast ) / 2; } @@ -1337,7 +1345,7 @@ int firestarter_actor::use( player &p, item &it, bool t, const tripoint &spos ) _( "If the current weather holds, it will take around %d minutes to light a fire." ) : _( "At your skill level, it will take around %d minutes to light a fire." ), moves / to_moves( 1_minutes ) ); - } else if( moves < to_moves( 2_turns ) && g->m.is_flammable( pos ) ) { + } else if( moves < to_moves( 2_turns ) && get_map().is_flammable( pos ) ) { // If less than 2 turns, don't start a long action resolve_firestarter_use( p, pos ); p.mod_moves( -moves ); @@ -1541,8 +1549,9 @@ int salvage_actor::cut_up( player &p, item &it, item_location &cut ) const // Original item has been consumed. cut.remove_item(); // Force an encumbrance update in case they were wearing that item. - p.reset_encumbrance(); + p.calc_encumbrance(); + map &here = get_map(); for( const auto &salvaged : materials_salvaged ) { itype_id mat_name = salvaged.first; int amount = salvaged.second; @@ -1561,7 +1570,7 @@ int salvage_actor::cut_up( player &p, item &it, item_location &cut ) const p.i_add_or_drop( result, amount ); } else { for( int i = 0; i < amount; i++ ) { - g->m.add_item_or_charges( pos, result ); + here.add_item_or_charges( pos, result ); } } } else { @@ -1701,7 +1710,7 @@ int inscribe_actor::use( player &p, item &it, bool t, const tripoint & ) const dest_.value() ); } - item_location loc = game_menus::inv::titled_menu( g->u, _( "Inscribe which item?" ) ); + item_location loc = game_menus::inv::titled_menu( get_avatar(), _( "Inscribe which item?" ) ); if( !loc ) { p.add_msg_if_player( m_info, _( "Never mind." ) ); return 0; @@ -1747,8 +1756,8 @@ bool cauterize_actor::cauterize_effect( player &p, item &it, bool force ) { // TODO: Make this less hacky static const heal_actor dummy = prepare_dummy(); - hp_part hpart = dummy.use_healing_item( p, p, it, force ); - if( hpart != num_hp_parts ) { + bodypart_id hpart = dummy.use_healing_item( p, p, it, force ); + if( hpart != bodypart_id( "num_bp" ) ) { p.add_msg_if_player( m_neutral, _( "You cauterize yourself." ) ); if( !( p.has_trait( trait_NOPAIN ) ) ) { p.mod_pain( 15 ); @@ -1756,9 +1765,9 @@ bool cauterize_actor::cauterize_effect( player &p, item &it, bool force ) } else { p.add_msg_if_player( m_neutral, _( "It itches a little." ) ); } - const body_part bp = player::hp_to_bp( hpart ); - if( p.has_effect( effect_bite, bp ) ) { - p.add_effect( effect_bite, 260_minutes, bp, true ); + + if( p.has_effect( effect_bite, hpart->token ) ) { + p.add_effect( effect_bite, 260_minutes, hpart->token, true ); } p.moves = 0; @@ -2524,7 +2533,7 @@ static item_location get_item_location( player &p, item &it, const tripoint &pos } // Item in a vehicle - if( const optional_vpart_position &vp = g->m.veh_at( pos ) ) { + if( const optional_vpart_position &vp = get_map().veh_at( pos ) ) { vehicle_cursor vc( vp->vehicle(), vp->part_index() ); bool found_in_vehicle = false; vc.visit_items( [&]( const item * e ) { @@ -2844,12 +2853,13 @@ repair_item_actor::repair_type repair_item_actor::default_action( const item &fi return RT_REFIT; } - const bool smol = g->u.has_trait( trait_SMALL2 ) || - g->u.has_trait( trait_SMALL_OK ); + Character &player_character = get_player_character(); + const bool smol = player_character.has_trait( trait_SMALL2 ) || + player_character.has_trait( trait_SMALL_OK ); const bool is_undersized = fix.has_flag( flag_UNDERSIZE ); const bool is_oversized = fix.has_flag( flag_OVERSIZE ); - const bool resizing_matters = fix.get_encumber( g->u ) != 0; + const bool resizing_matters = fix.get_encumber( player_character ) != 0; const bool too_big_while_smol = smol && !is_undersized && !is_oversized; if( too_big_while_smol && can_be_refitted && resizing_matters ) { @@ -3124,8 +3134,8 @@ int heal_actor::use( player &p, item &it, bool, const tripoint &pos ) const } player &patient = get_patient( p, pos ); - const hp_part hpp = use_healing_item( p, patient, it, false ); - if( hpp == num_hp_parts ) { + const bodypart_str_id hpp = use_healing_item( p, patient, it, false ).id(); + if( hpp == bodypart_str_id( "num_bp" ) ) { return 0; } @@ -3142,7 +3152,7 @@ int heal_actor::use( player &p, item &it, bool, const tripoint &pos ) const /** @EFFECT_FIRSTAID speeds up firstaid activity */ p.assign_activity( ACT_FIRSTAID, cost, 0, 0, it.tname() ); p.activity.targets.push_back( item_location( p, &it ) ); - p.activity.values.push_back( hpp ); + p.activity.str_values.push_back( hpp.c_str() ); p.moves = 0; return 0; } @@ -3157,14 +3167,14 @@ std::unique_ptr heal_actor::clone() const return std::make_unique( *this ); } -int heal_actor::get_heal_value( const player &healer, hp_part healed ) const +int heal_actor::get_heal_value( const Character &healer, bodypart_id healed ) const { int heal_base; float bonus_mult; - if( healed == hp_head ) { + if( healed == bodypart_id( "head" ) ) { heal_base = head_power; bonus_mult = head_scaling; - } else if( healed == hp_torso ) { + } else if( healed == bodypart_id( "torso" ) ) { heal_base = torso_power; bonus_mult = torso_scaling; } else { @@ -3180,7 +3190,7 @@ int heal_actor::get_heal_value( const player &healer, hp_part healed ) const return heal_base; } -int heal_actor::get_bandaged_level( const player &healer ) const +int heal_actor::get_bandaged_level( const Character &healer ) const { if( bandages_power > 0 ) { /** @EFFECT_FIRSTAID increases healing item effects */ @@ -3190,7 +3200,7 @@ int heal_actor::get_bandaged_level( const player &healer ) const return bandages_power; } -int heal_actor::get_disinfected_level( const player &healer ) const +int heal_actor::get_disinfected_level( const Character &healer ) const { if( disinfectant_power > 0 ) { /** @EFFECT_FIRSTAID increases healing item effects */ @@ -3200,24 +3210,21 @@ int heal_actor::get_disinfected_level( const player &healer ) const return disinfectant_power; } -int heal_actor::finish_using( player &healer, player &patient, item &it, hp_part healed ) const +int heal_actor::finish_using( player &healer, player &patient, item &it, bodypart_id healed ) const { float practice_amount = limb_power * 3.0f; const int dam = get_heal_value( healer, healed ); - - const bodypart_id bp = convert_bp( Character::hp_to_bp( healed ) ).id(); - const int cur_hp = patient.get_part_hp_cur( bp ); + const int cur_hp = patient.get_part_hp_cur( healed ); if( ( cur_hp >= 1 ) && ( dam > 0 ) ) { // Prevent first-aid from mending limbs - patient.heal( bp, dam ); + patient.heal( healed, dam ); } else if( ( cur_hp >= 1 ) && ( dam < 0 ) ) { - patient.apply_damage( nullptr, bp, -dam ); //hurt takes + damage + patient.apply_damage( nullptr, healed, -dam ); //hurt takes + damage } - const body_part bp_healed = player::hp_to_bp( healed ); - + Character &player_character = get_player_character(); const bool u_see = healer.is_player() || patient.is_player() || - g->u.sees( healer ) || g->u.sees( patient ); + player_character.sees( healer ) || player_character.sees( patient ); const bool player_healing_player = healer.is_player() && patient.is_player(); // Need a helper here - messages are from healer's point of view // but it would be cool if NPCs could use this function too @@ -3234,9 +3241,9 @@ int heal_actor::finish_using( player &healer, player &patient, item &it, hp_part } }; - if( patient.has_effect( effect_bleed, bp_healed ) ) { + if( patient.has_effect( effect_bleed, healed->token ) ) { if( x_in_y( bleed, 1.0f ) ) { - patient.remove_effect( effect_bleed, bp_healed ); + patient.remove_effect( effect_bleed, healed->token ); heal_msg( m_good, _( "You stop the bleeding." ), _( "The bleeding is stopped." ) ); } else { heal_msg( m_warning, _( "You fail to stop the bleeding." ), _( "The wound still bleeds." ) ); @@ -3244,9 +3251,9 @@ int heal_actor::finish_using( player &healer, player &patient, item &it, hp_part practice_amount += bleed * 3.0f; } - if( patient.has_effect( effect_bite, bp_healed ) ) { + if( patient.has_effect( effect_bite, healed->token ) ) { if( x_in_y( bite, 1.0f ) ) { - patient.remove_effect( effect_bite, bp_healed ); + patient.remove_effect( effect_bite, healed->token ); heal_msg( m_good, _( "You clean the wound." ), _( "The wound is cleaned." ) ); } else { heal_msg( m_warning, _( "Your wound still aches." ), _( "The wound still looks bad." ) ); @@ -3254,10 +3261,10 @@ int heal_actor::finish_using( player &healer, player &patient, item &it, hp_part practice_amount += bite * 3.0f; } - if( patient.has_effect( effect_infected, bp_healed ) ) { + if( patient.has_effect( effect_infected, healed->token ) ) { if( x_in_y( infect, 1.0f ) ) { - const time_duration infected_dur = patient.get_effect_dur( effect_infected, bp_healed ); - patient.remove_effect( effect_infected, bp_healed ); + const time_duration infected_dur = patient.get_effect_dur( effect_infected, healed->token ); + patient.remove_effect( effect_infected, healed->token ); patient.add_effect( effect_recover, infected_dur ); heal_msg( m_good, _( "You disinfect the wound." ), _( "The wound is disinfected." ) ); } else { @@ -3296,20 +3303,20 @@ int heal_actor::finish_using( player &healer, player &patient, item &it, hp_part // apply healing over time effects if( bandages_power > 0 ) { int bandages_intensity = get_bandaged_level( healer ); - patient.add_effect( effect_bandaged, 1_turns, bp_healed ); - effect &e = patient.get_effect( effect_bandaged, bp_healed ); + patient.add_effect( effect_bandaged, 1_turns, healed->token ); + effect &e = patient.get_effect( effect_bandaged, healed->token ); e.set_duration( e.get_int_dur_factor() * bandages_intensity ); - patient.set_part_damage_bandaged( bp, - patient.get_part_hp_max( bp ) - patient.get_part_hp_cur( bp ) ); + patient.set_part_damage_bandaged( healed, + patient.get_part_hp_max( healed ) - patient.get_part_hp_cur( healed ) ); practice_amount += 2 * bandages_intensity; } if( disinfectant_power > 0 ) { int disinfectant_intensity = get_disinfected_level( healer ); - patient.add_effect( effect_disinfected, 1_turns, bp_healed ); - effect &e = patient.get_effect( effect_disinfected, bp_healed ); + patient.add_effect( effect_disinfected, 1_turns, healed->token ); + effect &e = patient.get_effect( effect_disinfected, healed->token ); e.set_duration( e.get_int_dur_factor() * disinfectant_intensity ); - patient.set_part_damage_disinfected( bp, - patient.get_part_hp_max( bp ) - patient.get_part_hp_cur( bp ) ); + patient.set_part_damage_disinfected( healed, + patient.get_part_hp_max( healed ) - patient.get_part_hp_cur( healed ) ); practice_amount += 2 * disinfectant_intensity; } practice_amount = std::max( 9.0f, practice_amount ); @@ -3318,7 +3325,7 @@ int heal_actor::finish_using( player &healer, player &patient, item &it, hp_part return it.type->charges_to_use(); } -static hp_part pick_part_to_heal( +static bodypart_id pick_part_to_heal( const player &healer, const player &patient, const std::string &menu_header, int limb_power, int head_bonus, int torso_bonus, @@ -3335,24 +3342,23 @@ static hp_part pick_part_to_heal( /** @EFFECT_FIRSTAID increases precision when using first aid on someone else */ ( healer.get_skill_level( skill_firstaid ) * 4 + healer.per_cur >= 20 ); while( true ) { - hp_part healed_part = patient.body_window( menu_header, force, precise, - limb_power, head_bonus, torso_bonus, - bleed_chance, bite_chance, infect_chance, bandage_power, disinfectant_power ); - if( healed_part == num_hp_parts ) { - return num_hp_parts; + bodypart_id healed_part = patient.body_window( menu_header, force, precise, + limb_power, head_bonus, torso_bonus, + bleed_chance, bite_chance, infect_chance, bandage_power, disinfectant_power ); + if( healed_part == bodypart_id( "num_bp" ) ) { + return bodypart_id( "num_bp" ); } - const bodypart_id &bp = convert_bp( player::hp_to_bp( healed_part ) ).id(); - if( ( infect && patient.has_effect( effect_infected, bp->token ) ) || - ( bite && patient.has_effect( effect_bite, bp->token ) ) || - ( bleed && patient.has_effect( effect_bleed, bp->token ) ) ) { + if( ( infect && patient.has_effect( effect_infected, healed_part->token ) ) || + ( bite && patient.has_effect( effect_bite, healed_part->token ) ) || + ( bleed && patient.has_effect( effect_bleed, healed_part->token ) ) ) { return healed_part; } - if( patient.is_limb_broken( bp ) ) { - if( healed_part == hp_arm_l || healed_part == hp_arm_r ) { + if( patient.is_limb_broken( healed_part ) ) { + if( healed_part == bodypart_id( "arm_l" ) || healed_part == bodypart_id( "arm_r" ) ) { add_msg( m_info, _( "That arm is broken. It needs surgical attention or a splint." ) ); - } else if( healed_part == hp_leg_l || healed_part == hp_leg_r ) { + } else if( healed_part == bodypart_id( "leg_l" ) || healed_part == bodypart_id( "leg_r" ) ) { add_msg( m_info, _( "That leg is broken. It needs surgical attention or a splint." ) ); } else { add_msg( m_info, "That body part is bugged. It needs developer's attention." ); @@ -3361,24 +3367,25 @@ static hp_part pick_part_to_heal( continue; } - if( force || patient.get_part_hp_cur( bp ) < patient.get_part_hp_max( bp ) ) { + if( force || patient.get_part_hp_cur( healed_part ) < patient.get_part_hp_max( healed_part ) ) { return healed_part; } } } -hp_part heal_actor::use_healing_item( player &healer, player &patient, item &it, bool force ) const +bodypart_id heal_actor::use_healing_item( player &healer, player &patient, item &it, + bool force ) const { bodypart_id healed = bodypart_id( "num_bp" ); - const int head_bonus = get_heal_value( healer, hp_head ); - const int limb_power = get_heal_value( healer, hp_arm_l ); - const int torso_bonus = get_heal_value( healer, hp_torso ); + const int head_bonus = get_heal_value( healer, bodypart_id( "head" ) ); + const int limb_power = get_heal_value( healer, bodypart_id( "arm_l" ) ); + const int torso_bonus = get_heal_value( healer, bodypart_id( "torso" ) ); if( !patient.can_use_heal_item( it ) ) { patient.add_msg_player_or_npc( m_bad, _( "Your biology is not compatible with that item." ), _( "'s biology is not compatible with that item." ) ); - return num_hp_parts; // canceled + return bodypart_id( "num_bp" ); // canceled } if( healer.is_npc() ) { @@ -3404,24 +3411,20 @@ hp_part heal_actor::use_healing_item( player &healer, player &patient, item &it, // Player healing self - let player select if( healer.activity.id() != ACT_FIRSTAID ) { const std::string menu_header = _( "Select a body part for: " ) + it.tname(); - healed = convert_bp( Character::hp_to_bp( pick_part_to_heal( healer, patient, menu_header, - limb_power, head_bonus, torso_bonus, - bleed, bite, infect, force, - get_bandaged_level( healer ), - get_disinfected_level( healer ) ) ) ).id(); + healed = pick_part_to_heal( healer, patient, menu_header, limb_power, head_bonus, torso_bonus, + bleed, bite, infect, force, get_bandaged_level( healer ), get_disinfected_level( healer ) ); if( healed == bodypart_id( "num_bp" ) ) { add_msg( m_info, _( "Never mind." ) ); - return num_hp_parts; // canceled + return bodypart_id( "num_bp" ); // canceled } } // Brick healing if using a first aid kit for the first time. if( long_action && healer.activity.id() != ACT_FIRSTAID ) { // Cancel and wait for activity completion. - return Character::bp_to_hp( healed->token ); + return healed; } else if( healer.activity.id() == ACT_FIRSTAID ) { // Completed activity, extract body part from it. - healed = convert_bp( Character::hp_to_bp( static_cast - ( healer.activity.values[0] ) ) ).id(); + healed = bodypart_id( healer.activity.str_values[0] ); } } else { // Player healing NPC @@ -3430,18 +3433,15 @@ hp_part heal_actor::use_healing_item( player &healer, player &patient, item &it, //~ %1$s: patient name, %2$s: healing item name "Select a body part of %1$s for %2$s:" ), patient.disp_name(), it.tname() ); - healed = convert_bp( Character::hp_to_bp( pick_part_to_heal( healer, patient, menu_header, - limb_power, head_bonus, torso_bonus, - bleed, bite, infect, force, - get_bandaged_level( healer ), - get_disinfected_level( healer ) ) ) ).id(); + healed = pick_part_to_heal( healer, patient, menu_header, limb_power, head_bonus, torso_bonus, + bleed, bite, infect, force, get_bandaged_level( healer ), get_disinfected_level( healer ) ); } if( healed != bodypart_id( "num_bp" ) ) { - finish_using( healer, patient, it, Character::bp_to_hp( healed->token ) ); + finish_using( healer, patient, it, healed ); } - return Character::bp_to_hp( healed->token ); + return healed; } void heal_actor::info( const item &, std::vector &dump ) const @@ -3451,6 +3451,7 @@ void heal_actor::info( const item &, std::vector &dump ) const dump.emplace_back( "HEAL", _( "Healing effects " ) ); } + Character &player_character = get_player_character(); if( head_power > 0 || torso_power > 0 || limb_power > 0 ) { dump.emplace_back( "HEAL", _( "Base healing: " ) ); dump.emplace_back( "HEAL_BASE", _( "Head: " ), "", iteminfo::no_newline, head_power ); @@ -3459,10 +3460,11 @@ void heal_actor::info( const item &, std::vector &dump ) const if( g != nullptr ) { dump.emplace_back( "HEAL", _( "Actual healing: " ) ); dump.emplace_back( "HEAL_ACT", _( "Head: " ), "", iteminfo::no_newline, - get_heal_value( g->u, hp_head ) ); + get_heal_value( player_character, bodypart_id( "head" ) ) ); dump.emplace_back( "HEAL_ACT", _( " Torso: " ), "", iteminfo::no_newline, - get_heal_value( g->u, hp_torso ) ); - dump.emplace_back( "HEAL_ACT", _( " Limbs: " ), get_heal_value( g->u, hp_arm_l ) ); + get_heal_value( player_character, bodypart_id( "torso" ) ) ); + dump.emplace_back( "HEAL_ACT", _( " Limbs: " ), get_heal_value( player_character, + bodypart_id( "arm_l" ) ) ); } } @@ -3471,7 +3473,7 @@ void heal_actor::info( const item &, std::vector &dump ) const texitify_base_healing_power( static_cast( bandages_power ) ) ); if( g != nullptr ) { dump.emplace_back( "HEAL", _( "Actual bandaging quality: " ), - texitify_healing_power( get_bandaged_level( g->u ) ) ); + texitify_healing_power( get_bandaged_level( player_character ) ) ); } } @@ -3480,7 +3482,7 @@ void heal_actor::info( const item &, std::vector &dump ) const texitify_base_healing_power( static_cast( disinfectant_power ) ) ); if( g != nullptr ) { dump.emplace_back( "HEAL", _( "Actual disinfecting quality: " ), - texitify_healing_power( get_disinfected_level( g->u ) ) ); + texitify_healing_power( get_disinfected_level( player_character ) ) ); } } @@ -3539,15 +3541,17 @@ std::unique_ptr place_trap_actor::clone() const static bool is_solid_neighbor( const tripoint &pos, const point &offset ) { + map &here = get_map(); const tripoint a = pos + tripoint( offset, 0 ); const tripoint b = pos - tripoint( offset, 0 ); - return g->m.move_cost( a ) != 2 && g->m.move_cost( b ) != 2; + return here.move_cost( a ) != 2 && here.move_cost( b ) != 2; } static bool has_neighbor( const tripoint &pos, const ter_id &terrain_id ) { - for( const tripoint &t : g->m.points_in_radius( pos, 1, 0 ) ) { - if( g->m.ter( t ) == terrain_id ) { + map &here = get_map(); + for( const tripoint &t : here.points_in_radius( pos, 1, 0 ) ) { + if( here.ter( t ) == terrain_id ) { return true; } } @@ -3561,7 +3565,8 @@ bool place_trap_actor::is_allowed( player &p, const tripoint &pos, const std::st name ); return false; } - if( g->m.move_cost( pos ) != 2 ) { + map &here = get_map(); + if( here.move_cost( pos ) != 2 ) { p.add_msg_if_player( m_info, _( "You can't place a %s there." ), name ); return false; } @@ -3577,14 +3582,14 @@ bool place_trap_actor::is_allowed( player &p, const tripoint &pos, const std::st needs_neighbor_terrain.obj().name() ); return false; } - const trap &existing_trap = g->m.tr_at( pos ); + const trap &existing_trap = here.tr_at( pos ); if( !existing_trap.is_null() ) { if( existing_trap.can_see( pos, p ) ) { p.add_msg_if_player( m_info, _( "You can't place a %s there. It contains a trap already." ), 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; } @@ -3593,8 +3598,9 @@ bool place_trap_actor::is_allowed( player &p, const tripoint &pos, const std::st static void place_and_add_as_known( player &p, const tripoint &pos, const trap_str_id &id ) { - g->m.trap_set( pos, id ); - const trap &tr = g->m.tr_at( pos ); + map &here = get_map(); + here.trap_set( pos, id ); + const trap &tr = here.tr_at( pos ); if( !tr.can_see( pos, p ) ) { p.add_known_trap( pos, tr ); } @@ -3622,13 +3628,14 @@ int place_trap_actor::use( player &p, item &it, bool, const tripoint & ) const return 0; } + map &here = get_map(); int distance_to_trap_center = unburied_data.trap.obj().get_trap_radius() + outer_layer_trap.obj().get_trap_radius() + 1; if( unburied_data.trap.obj().get_trap_radius() > 0 ) { // Math correction for multi-tile traps pos.x = ( pos.x - p.posx() ) * distance_to_trap_center + p.posx(); pos.y = ( pos.y - p.posy() ) * distance_to_trap_center + p.posy(); - for( const tripoint &t : g->m.points_in_radius( pos, outer_layer_trap.obj().get_trap_radius(), + for( const tripoint &t : here.points_in_radius( pos, outer_layer_trap.obj().get_trap_radius(), 0 ) ) { if( !is_allowed( p, t, it.tname() ) ) { p.add_msg_if_player( m_info, @@ -3640,7 +3647,7 @@ int place_trap_actor::use( player &p, item &it, bool, const tripoint & ) const } const bool has_shovel = p.has_quality( quality_id( "DIG" ), 3 ); - const bool is_diggable = g->m.has_flag( "DIGGABLE", pos ); + const bool is_diggable = here.has_flag( "DIGGABLE", pos ); bool bury = false; if( could_bury && has_shovel && is_diggable ) { bury = query_yn( _( bury_question ) ); @@ -3652,7 +3659,7 @@ int place_trap_actor::use( player &p, item &it, bool, const tripoint & ) const p.mod_moves( -data.moves ); place_and_add_as_known( p, pos, data.trap ); - for( const tripoint &t : g->m.points_in_radius( pos, data.trap.obj().get_trap_radius(), 0 ) ) { + for( const tripoint &t : here.points_in_radius( pos, data.trap.obj().get_trap_radius(), 0 ) ) { if( t != pos ) { place_and_add_as_known( p, t, outer_layer_trap ); } @@ -3668,9 +3675,10 @@ void emit_actor::load( const JsonObject &obj ) int emit_actor::use( player &, item &it, bool, const tripoint &pos ) const { + map &here = get_map(); const float scaling = scale_qty ? it.charges : 1; for( const auto &e : emits ) { - g->m.emit_field( pos, e, scaling ); + here.emit_field( pos, e, scaling ); } return 1; @@ -3759,8 +3767,10 @@ std::unique_ptr saw_barrel_actor::clone() const int install_bionic_actor::use( player &p, item &it, bool, const tripoint & ) const { if( p.can_install_bionics( *it.type, p, false ) ) { - p.consume_installation_requirment( it.type->bionic->id ); - p.consume_anesth_requirment( *it.type, p ); + if( !p.has_trait( trait_DEBUG_BIONICS ) ) { + p.consume_installation_requirment( it.type->bionic->id ); + p.consume_anesth_requirment( *it.type, p ); + } return p.install_bionics( *it.type, p, false ) ? it.type->charges_to_use() : 0; } else { return 0; @@ -4044,13 +4054,14 @@ int deploy_tent_actor::use( player &p, item &it, bool, const tripoint & ) const } const tripoint direction = *dir; + map &here = get_map(); // We place the center of the structure (radius + 1) // spaces away from the player. // First check there's enough room. const tripoint center = p.pos() + tripoint( ( radius + 1 ) * direction.x, ( radius + 1 ) * direction.y, 0 ); - for( const tripoint &dest : g->m.points_in_radius( center, radius ) ) { - if( const auto vp = g->m.veh_at( dest ) ) { + for( const tripoint &dest : here.points_in_radius( center, radius ) ) { + if( const auto vp = here.veh_at( dest ) ) { add_msg( m_info, _( "The %s is in the way." ), vp->vehicle().name ); return 0; } @@ -4058,28 +4069,28 @@ int deploy_tent_actor::use( player &p, item &it, bool, const tripoint & ) const add_msg( m_info, _( "The %s is in the way." ), c->disp_name() ); return 0; } - if( g->m.impassable( dest ) || !g->m.has_flag( "FLAT", dest ) ) { + if( here.impassable( dest ) || !here.has_flag( "FLAT", dest ) ) { add_msg( m_info, _( "The %s in that direction isn't suitable for placing the %s." ), - g->m.name( dest ), it.tname() ); + here.name( dest ), it.tname() ); return 0; } - if( g->m.has_furn( dest ) ) { - add_msg( m_info, _( "There is already furniture (%s) there." ), g->m.furnname( dest ) ); + if( here.has_furn( dest ) ) { + add_msg( m_info, _( "There is already furniture (%s) there." ), here.furnname( dest ) ); return 0; } } // Make a square of floor surrounded by wall. - for( const tripoint &dest : g->m.points_in_radius( center, radius ) ) { - g->m.furn_set( dest, wall ); + for( const tripoint &dest : here.points_in_radius( center, radius ) ) { + here.furn_set( dest, wall ); } - for( const tripoint &dest : g->m.points_in_radius( center, radius - 1 ) ) { - g->m.furn_set( dest, floor ); + for( const tripoint &dest : here.points_in_radius( center, radius - 1 ) ) { + here.furn_set( dest, floor ); } // Place the center floor and the door. if( floor_center ) { - g->m.furn_set( center, *floor_center ); + here.furn_set( center, *floor_center ); } - g->m.furn_set( p.pos() + direction, door_closed ); + here.furn_set( p.pos() + direction, door_closed ); add_msg( m_info, _( "You set up the %s on the ground." ), it.tname() ); add_msg( m_info, _( "Examine the center square to pack it up again." ) ); return 1; @@ -4087,8 +4098,9 @@ int deploy_tent_actor::use( player &p, item &it, bool, const tripoint & ) const bool deploy_tent_actor::check_intact( const tripoint ¢er ) const { - for( const tripoint &dest : g->m.points_in_radius( center, radius ) ) { - const furn_id fid = g->m.furn( dest ); + map &here = get_map(); + for( const tripoint &dest : here.points_in_radius( center, radius ) ) { + const furn_id fid = here.furn( dest ); if( dest == center && floor_center ) { if( fid != *floor_center ) { return false; diff --git a/src/iuse_actor.h b/src/iuse_actor.h index e2a14bfc7b43e..70d7108ac7347 100644 --- a/src/iuse_actor.h +++ b/src/iuse_actor.h @@ -30,7 +30,6 @@ class player; struct iteminfo; struct tripoint; -enum hp_part : int; enum body_part : int; class JsonObject; class item_location; @@ -924,15 +923,15 @@ class heal_actor : public iuse_actor std::set used_up_item_flags; /** How much hp would `healer` heal using this actor on `healed` body part. */ - int get_heal_value( const player &healer, hp_part healed ) const; + int get_heal_value( const Character &healer, bodypart_id healed ) const; /** How many intensity levels will be applied using this actor by `healer`. */ - int get_bandaged_level( const player &healer ) const; + int get_bandaged_level( const Character &healer ) const; /** How many intensity levels will be applied using this actor by `healer`. */ - int get_disinfected_level( const player &healer ) const; + int get_disinfected_level( const Character &healer ) const; /** Does the actual healing. Used by both long and short actions. Returns charges used. */ - int finish_using( player &healer, player &patient, item &it, hp_part healed ) const; + int finish_using( player &healer, player &patient, item &it, bodypart_id healed ) const; - hp_part use_healing_item( player &healer, player &patient, item &it, bool force ) const; + bodypart_id use_healing_item( player &healer, player &patient, item &it, bool force ) const; heal_actor( const std::string &type = "heal" ) : iuse_actor( type ) {} diff --git a/src/lightmap.cpp b/src/lightmap.cpp index 32f81d7d94a8e..29096b3252caa 100644 --- a/src/lightmap.cpp +++ b/src/lightmap.cpp @@ -90,7 +90,7 @@ bool map::build_transparency_cache( const int zlev ) &transparency_cache[0][0], MAPSIZE_X * MAPSIZE_Y, static_cast( LIGHT_TRANSPARENCY_OPEN_AIR ) ); - const float sight_penalty = weather::sight_penalty( g->weather.weather ); + const float sight_penalty = get_weather().weather_id->sight_penalty; // Traverse the submaps in order for( int smx = 0; smx < my_MAPSIZE; ++smx ) { @@ -245,7 +245,7 @@ void map::build_sunlight_cache( int zlev ) const auto &prev_transparency_cache = prev_map_cache.transparency_cache; const auto &prev_floor_cache = prev_map_cache.floor_cache; const auto &outside_cache = map_cache.outside_cache; - const float sight_penalty = weather::sight_penalty( g->weather.weather ); + const float sight_penalty = get_weather().weather_id->sight_penalty; for( int x = 0, prev_x = offset.x; x < MAPSIZE_X; x++, prev_x++ ) { bool x_inbounds = prev_x >= 0 && prev_x < MAPSIZE_X; for( int y = 0, prev_y = offset.y; y < MAPSIZE_Y; y++, prev_y++ ) { diff --git a/src/magic.cpp b/src/magic.cpp index c87849df39e93..016e4e64984e9 100644 --- a/src/magic.cpp +++ b/src/magic.cpp @@ -675,7 +675,8 @@ int spell::energy_cost( const Character &guy ) const } if( !has_flag( spell_flag::NO_HANDS ) ) { // the first 10 points of combined encumbrance is ignored, but quickly adds up - const int hands_encumb = std::max( 0, guy.encumb( bp_hand_l ) + guy.encumb( bp_hand_r ) - 10 ); + const int hands_encumb = std::max( 0, + guy.encumb( bodypart_id( "hand_l" ) ) + guy.encumb( bodypart_id( "hand_r" ) ) - 10 ); switch( type->energy_source ) { default: cost += 10 * hands_encumb; @@ -731,7 +732,7 @@ int spell::get_difficulty() const return type->difficulty; } -int spell::casting_time( const Character &guy ) const +int spell::casting_time( const Character &guy, bool ignore_encumb ) const { // casting time in moves int casting_time = 0; @@ -746,15 +747,19 @@ int spell::casting_time( const Character &guy ) const } else { casting_time = type->base_casting_time; } - if( !has_flag( spell_flag::NO_LEGS ) ) { - // the first 20 points of encumbrance combined is ignored - const int legs_encumb = std::max( 0, guy.encumb( bp_leg_l ) + guy.encumb( bp_leg_r ) - 20 ); - casting_time += legs_encumb * 3; - } - if( has_flag( spell_flag::SOMATIC ) ) { - // the first 20 points of encumbrance combined is ignored - const int arms_encumb = std::max( 0, guy.encumb( bp_arm_l ) + guy.encumb( bp_arm_r ) - 20 ); - casting_time += arms_encumb * 2; + if( !ignore_encumb ) { + if( !has_flag( spell_flag::NO_LEGS ) ) { + // the first 20 points of encumbrance combined is ignored + const int legs_encumb = std::max( 0, + guy.encumb( bodypart_id( "leg_l" ) ) + guy.encumb( bodypart_id( "leg_r" ) ) - 20 ); + casting_time += legs_encumb * 3; + } + if( has_flag( spell_flag::SOMATIC ) ) { + // the first 20 points of encumbrance combined is ignored + const int arms_encumb = std::max( 0, + guy.encumb( bodypart_id( "arm_l" ) ) + guy.encumb( bodypart_id( "arm_r" ) ) - 20 ); + casting_time += arms_encumb * 2; + } } return casting_time; } @@ -793,13 +798,14 @@ float spell::spell_fail( const Character &guy ) const float fail_chance = std::pow( ( effective_skill - 30.0f ) / 30.0f, 2 ); if( has_flag( spell_flag::SOMATIC ) && !guy.has_trait_flag( "SUBTLE_SPELL" ) ) { // the first 20 points of encumbrance combined is ignored - const int arms_encumb = std::max( 0, guy.encumb( bp_arm_l ) + guy.encumb( bp_arm_r ) - 20 ); + const int arms_encumb = std::max( 0, + guy.encumb( bodypart_id( "arm_l" ) ) + guy.encumb( bodypart_id( "arm_r" ) ) - 20 ); // each encumbrance point beyond the "gray" color counts as half an additional fail % fail_chance += arms_encumb / 200.0f; } if( has_flag( spell_flag::VERBAL ) && !guy.has_trait_flag( "SILENT_SPELL" ) ) { // a little bit of mouth encumbrance is allowed, but not much - const int mouth_encumb = std::max( 0, guy.encumb( bp_mouth ) - 5 ); + const int mouth_encumb = std::max( 0, guy.encumb( bodypart_id( "mouth" ) ) - 5 ); fail_chance += mouth_encumb / 100.0f; } // concentration spells work better than you'd expect with a higher focus pool @@ -877,7 +883,7 @@ std::string spell::energy_cost_string( const Character &guy ) const return colorize( to_string( energy_cost( guy ) ), c_light_blue ); } if( energy_source() == magic_energy_type::hp ) { - auto pair = get_hp_bar( energy_cost( guy ), guy.get_hp_max() / num_hp_parts ); + auto pair = get_hp_bar( energy_cost( guy ), guy.get_hp_max() / 6 ); return colorize( pair.first, pair.second ); } if( energy_source() == magic_energy_type::stamina ) { @@ -1296,7 +1302,7 @@ void known_magic::serialize( JsonOut &json ) const json.member( "spellbook" ); json.start_array(); - for( auto pair : spellbook ) { + for( const auto &pair : spellbook ) { json.start_object(); json.member( "id", pair.second.id() ); json.member( "xp", pair.second.xp() ); @@ -1605,11 +1611,13 @@ static bool casting_time_encumbered( const spell &sp, const Character &guy ) int encumb = 0; if( !sp.has_flag( spell_flag::NO_LEGS ) ) { // the first 20 points of encumbrance combined is ignored - encumb += std::max( 0, guy.encumb( bp_leg_l ) + guy.encumb( bp_leg_r ) - 20 ); + encumb += std::max( 0, guy.encumb( bodypart_id( "leg_l" ) ) + guy.encumb( + bodypart_id( "leg_r" ) ) - 20 ); } if( sp.has_flag( spell_flag::SOMATIC ) ) { // the first 20 points of encumbrance combined is ignored - encumb += std::max( 0, guy.encumb( bp_arm_l ) + guy.encumb( bp_arm_r ) - 20 ); + encumb += std::max( 0, guy.encumb( bodypart_id( "arm_l" ) ) + guy.encumb( + bodypart_id( "arm_r" ) ) - 20 ); } return encumb > 0; } @@ -1617,7 +1625,9 @@ static bool casting_time_encumbered( const spell &sp, const Character &guy ) static bool energy_cost_encumbered( const spell &sp, const Character &guy ) { if( !sp.has_flag( spell_flag::NO_HANDS ) ) { - return std::max( 0, guy.encumb( bp_hand_l ) + guy.encumb( bp_hand_r ) - 10 ) > 0; + return std::max( 0, guy.encumb( bodypart_id( "hand_l" ) ) + guy.encumb( + bodypart_id( "hand_r" ) ) - 10 ) > + 0; } return false; } diff --git a/src/magic.h b/src/magic.h index a46ad99c80cce..cded00ea86f74 100644 --- a/src/magic.h +++ b/src/magic.h @@ -358,7 +358,7 @@ class spell float spell_fail( const Character &guy ) const; std::string colorized_fail_percent( const Character &guy ) const; // how long does it take to cast the spell - int casting_time( const Character &guy ) const; + int casting_time( const Character &guy, bool ignore_encumb = false ) const; // can the Character cast this spell? bool can_cast( const Character &guy ) const; diff --git a/src/magic_spell_effect.cpp b/src/magic_spell_effect.cpp index f3aed9077037f..6116154f6ff24 100644 --- a/src/magic_spell_effect.cpp +++ b/src/magic_spell_effect.cpp @@ -128,7 +128,7 @@ static void swap_pos( Creature &caster, const tripoint &target ) critter->setpos( caster.pos() ); caster.setpos( target ); //update map in case a monster swapped positions with the player - g->update_map( g->u ); + g->update_map( get_player_character() ); } void spell_effect::pain_split( const spell &sp, Creature &caster, const tripoint & ) @@ -162,9 +162,10 @@ static bool in_spell_aoe( const tripoint &start, const tripoint &end, const int if( ignore_walls ) { return true; } + map &here = get_map(); const std::vector trajectory = line_to( start, end ); for( const tripoint &pt : trajectory ) { - if( g->m.impassable( pt ) ) { + if( here.impassable( pt ) ) { return false; } } @@ -176,7 +177,7 @@ std::set spell_effect::spell_effect_blast( const spell &, const tripoi { std::set targets; // TODO: Make this breadth-first - for( const tripoint &potential_target : g->m.points_in_radius( target, aoe_radius ) ) { + for( const tripoint &potential_target : get_map().points_in_radius( target, aoe_radius ) ) { if( in_spell_aoe( target, potential_target, aoe_radius, ignore_walls ) ) { targets.emplace( potential_target ); } @@ -201,7 +202,7 @@ std::set spell_effect::spell_effect_cone( const spell &sp, const tripo for( const tripoint &ep : end_points ) { std::vector trajectory = line_to( source, ep ); for( const tripoint &tp : trajectory ) { - if( ignore_walls || g->m.passable( tp ) ) { + if( ignore_walls || get_map().passable( tp ) ) { targets.emplace( tp ); } else { break; @@ -219,7 +220,7 @@ static bool test_always_true( const tripoint & ) } static bool test_passable( const tripoint &p ) { - return g->m.passable( p ); + return get_map().passable( p ); } std::set spell_effect::spell_effect_line( const spell &, const tripoint &source, @@ -389,7 +390,7 @@ static std::set spell_effect_area( const spell &sp, const tripoint &ta explosion_colors[pt] = sp.damage_type_color(); } - explosion_handler::draw_custom_explosion( g->u.pos(), explosion_colors ); + explosion_handler::draw_custom_explosion( get_player_character().pos(), explosion_colors ); return targets; } @@ -419,14 +420,15 @@ static void add_effect_to_target( const tripoint &target, const spell &sp ) static void damage_targets( const spell &sp, Creature &caster, const std::set &targets ) { + map &here = get_map(); for( const tripoint &target : targets ) { if( !sp.is_valid_target( caster, target ) ) { continue; } sp.make_sound( target ); sp.create_field( target ); - if( sp.has_flag( spell_flag::IGNITE_FLAMMABLE ) && g->m.is_flammable( target ) ) { - g->m.add_field( target, fd_fire, 1, 10_minutes ); + if( sp.has_flag( spell_flag::IGNITE_FLAMMABLE ) && here.is_flammable( target ) ) { + here.add_field( target, fd_fire, 1, 10_minutes ); } Creature *const cr = g->critter_at( target ); if( !cr ) { @@ -462,7 +464,7 @@ void spell_effect::projectile_attack( const spell &sp, Creature &caster, { std::vector trajectory = line_to( caster.pos(), target ); for( std::vector::iterator iter = trajectory.begin(); iter != trajectory.end(); iter++ ) { - if( g->m.impassable( *iter ) ) { + if( get_map().impassable( *iter ) ) { if( iter != trajectory.begin() ) { target_attack( sp, caster, *( iter - 1 ) ); } else { @@ -522,7 +524,7 @@ static void magical_polymorph( monster &victim, Creature &caster, const spell &s return; } - if( g->u.sees( victim ) ) { + if( get_player_character().sees( victim ) ) { add_msg( _( "The %s transforms into a %s." ), victim.type->nname(), new_id->nname() ); } @@ -605,6 +607,7 @@ int area_expander::run( const tripoint ¢er ) // Number of nodes expanded. int expanded = 0; + map &here = get_map(); while( !frontier.empty() ) { int best_index = frontier.top(); frontier.pop(); @@ -613,7 +616,7 @@ int area_expander::run( const tripoint ¢er ) for( size_t i = 0; i < 8; i++ ) { tripoint pt = best.position + point( x_offset[ i ], y_offset[ i ] ); - if( g->m.impassable( pt ) ) { + if( here.impassable( pt ) ) { continue; } @@ -672,7 +675,7 @@ static void spell_move( const spell &sp, const Creature &caster, if( can_target_creature ) { if( Creature *victim = g->critter_at( from ) ) { - Creature::Attitude cr_att = victim->attitude_to( g->u ); + Creature::Attitude cr_att = victim->attitude_to( get_player_character() ); bool valid = cr_att != Creature::Attitude::FRIENDLY && sp.is_valid_effect_target( spell_target::hostile ); valid |= cr_att == Creature::Attitude::FRIENDLY && sp.is_valid_effect_target( spell_target::ally ); @@ -683,10 +686,11 @@ static void spell_move( const spell &sp, const Creature &caster, } } + map &here = get_map(); // Moving items if( sp.is_valid_effect_target( spell_target::item ) ) { - map_stack src_items = g->m.i_at( from ); - map_stack dst_items = g->m.i_at( to ); + map_stack src_items = here.i_at( from ); + map_stack dst_items = here.i_at( to ); for( const item &item : src_items ) { dst_items.insert( item ); } @@ -694,15 +698,15 @@ static void spell_move( const spell &sp, const Creature &caster, } // Helper function to move particular field type if corresponding target flag is enabled. - auto move_field = [&sp, from, to]( spell_target target, field_type_id fid ) { + auto move_field = [&sp, from, to, &here]( spell_target target, field_type_id fid ) { if( !sp.is_valid_effect_target( target ) ) { return; } - auto &src_field = g->m.field_at( from ); + auto &src_field = here.field_at( from ); if( field_entry *entry = src_field.find_field( fid ) ) { int intensity = entry->get_field_intensity(); - g->m.remove_field( from, fid ); - g->m.set_field_intensity( to, fid, intensity ); + here.remove_field( from, fid ); + here.set_field_intensity( to, fid, intensity ); } }; // Moving fields. @@ -760,17 +764,18 @@ void spell_effect::spawn_ethereal_item( const spell &sp, Creature &caster, const if( sp.has_flag( spell_flag::WITH_CONTAINER ) ) { granted = granted.in_its_container(); } - if( g->u.can_wear( granted ).success() ) { + avatar &player_character = get_avatar(); + if( player_character.can_wear( granted ).success() ) { granted.set_flag( "FIT" ); - g->u.wear_item( granted, false ); - } else if( !g->u.is_armed() && g->u.wield( granted, 0 ) ) { + player_character.wear_item( granted, false ); + } else if( !player_character.is_armed() && player_character.wield( granted, 0 ) ) { // nothing to do } else { - g->u.i_add( granted ); + player_character.i_add( granted ); } if( !granted.count_by_charges() ) { for( int i = 1; i < sp.damage(); i++ ) { - g->u.i_add( granted ); + player_character.i_add( granted ); } } sp.make_sound( caster.pos() ); @@ -908,11 +913,12 @@ void spell_effect::spawn_summoned_monster( const spell &sp, Creature &caster, void spell_effect::spawn_summoned_vehicle( const spell &sp, Creature &caster, const tripoint &target ) { - if( g->m.veh_at( target ) ) { + ::map &here = get_map(); + if( here.veh_at( target ) ) { caster.add_msg_if_player( m_bad, _( "There is already a vehicle there." ) ); return; } - if( vehicle *veh = g->m.add_vehicle( sp.summon_vehicle_id(), target, -90, 100, 0 ) ) { + if( vehicle *veh = here.add_vehicle( sp.summon_vehicle_id(), target, -90, 100, 0 ) ) { veh->magic = true; if( !sp.has_flag( spell_flag::PERMANENT ) ) { veh->summon_time_limit = sp.duration_turns(); @@ -1084,12 +1090,13 @@ void spell_effect::mutate( const spell &sp, Creature &caster, const tripoint &ta void spell_effect::bash( const spell &sp, Creature &caster, const tripoint &target ) { + ::map &here = get_map(); const std::set area = spell_effect_blast( sp, caster.pos(), target, sp.aoe(), false ); for( const tripoint &potential_target : area ) { if( !sp.is_valid_target( caster, potential_target ) ) { continue; } // the bash already makes noise, so no need for spell::make_sound() - g->m.bash( potential_target, sp.damage(), sp.has_flag( spell_flag::SILENT ) ); + here.bash( potential_target, sp.damage(), sp.has_flag( spell_flag::SILENT ) ); } } 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/map.cpp b/src/map.cpp index 1ece0df9221c3..2dabc1d774a47 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -1,6 +1,7 @@ #include "map.h" #include +#include #include #include #include @@ -113,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" ); @@ -243,53 +242,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 +286,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 +299,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 +332,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 +443,23 @@ 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 ); + + // 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; } @@ -629,9 +635,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 ) { @@ -956,7 +964,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. @@ -1007,12 +1015,12 @@ void map::board_vehicle( const tripoint &pos, Character *p ) p->setpos( pos ); p->in_vehicle = true; - if( p == &g->u ) { + if( p->is_avatar() ) { g->update_map( g->u ); } } -void map::unboard_vehicle( const vpart_reference &vp, player *passenger, bool dead_passenger ) +void map::unboard_vehicle( const vpart_reference &vp, Character *passenger, bool dead_passenger ) { // Mark the part as un-occupied regardless of whether there's a live passenger here. vp.part().remove_flag( vehicle_part::passenger_flag ); @@ -1050,12 +1058,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 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( 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 + ( 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", @@ -1067,6 +1085,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 +1111,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=(" @@ -1118,6 +1137,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 ) { @@ -1134,52 +1157,69 @@ 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; + 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 + next_pos ) ) { + 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 + next_pos + tripoint( 0, 0, psg_offset_z ) ); // someone is in the way so try again if( g->critter_at( psgp ) ) { complete = false; continue; } - if( psg == &g->u ) { + if( psg->is_avatar() ) { // 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, adjust_pos, parts_to_move ); 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(); @@ -1190,15 +1230,24 @@ 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; - on_vehicle_moved( veh.sm_pos.z ); + for( int vsmz : smzs ) { + on_vehicle_moved( vsmz ); + } 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 @@ -1715,7 +1764,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 +1777,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 +1786,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() ); @@ -1769,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 @@ -1787,19 +1836,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 +1905,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; } @@ -4167,7 +4218,7 @@ item &map::add_item_or_charges( const tripoint &pos, item obj, bool overflow ) } else if( overflow ) { // ...otherwise try to overflow to adjacent tiles (if permitted) const int max_dist = 2; - std::vector tiles = closest_tripoints_first( pos, max_dist ); + std::vector tiles = closest_points_first( pos, max_dist ); tiles.erase( tiles.begin() ); // we already tried this position const int max_path_length = 4 * max_dist; const pathfinding_settings setting( 0, max_dist, max_path_length, 0, false, true, false, false, @@ -4731,7 +4782,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 ); @@ -5007,6 +5059,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 ) ) { @@ -5085,64 +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 ); - - // 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 ) { - add_msg( _( "The %s is taken down." ), tr.name() ); - tr.on_disarmed( *this, p ); - 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 + tr.get_difficulty() + 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 ) ) { @@ -5154,7 +5153,7 @@ void map::remove_trap( const tripoint &p ) trap_id tid = current_submap->get_trap( l ); if( tid != tr_null ) { - if( g != nullptr && this == &g->m ) { + if( g != nullptr && this == &get_map() ) { g->u.add_known_trap( p, tr_null.obj() ); } @@ -5222,7 +5221,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 ); @@ -5308,7 +5308,7 @@ bool map::add_field( const tripoint &p, const field_type_id &type, int intensity } } - if( g != nullptr && this == &g->m && p == g->u.pos() ) { + if( g != nullptr && this == &get_map() && p == g->u.pos() ) { creature_in_field( g->u ); //Hit the player with the field if it spawned on top of them. } @@ -5372,7 +5372,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; @@ -5389,7 +5390,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; @@ -5500,7 +5502,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: @@ -5568,7 +5571,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 +5581,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 +5616,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 +5624,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 +5641,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 +5664,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++; @@ -5673,7 +5678,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 { @@ -5690,9 +5696,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 +5708,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 +5905,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, @@ -5991,7 +6000,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 ) ) { @@ -6332,10 +6342,10 @@ std::vector map::get_dir_circle( const tripoint &f, const tripoint &t // The line below can be crazy expensive - we only take the FIRST point of it const std::vector line = line_to( f, t, 0, 0 ); - const std::vector spiral = closest_tripoints_first( f, 1 ); + const std::vector spiral = closest_points_first( f, 1 ); const std::vector pos_index {1, 2, 4, 6, 8, 7, 5, 3}; - // All possible constellations (closest_tripoints_first goes clockwise) + // All possible constellations (closest_points_first goes clockwise) // 753 531 312 124 246 468 687 875 // 8 1 7 2 5 4 3 6 1 8 2 7 4 5 6 3 // 642 864 786 578 357 135 213 421 @@ -6488,6 +6498,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 ); @@ -7024,7 +7036,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; @@ -7323,7 +7336,7 @@ void map::spawn_monsters_submap( const tripoint &gp, bool ignore_sight ) const auto valid_location = [&]( const tripoint & p ) { // Checking for creatures via g is only meaningful if this is the main game map. // If it's some local map instance, the coordinates will most likely not even match. - return ( !g || &g->m != this || !g->critter_at( p ) ) && tmp.can_move_to( p ); + return ( !g || &get_map() != this || !g->critter_at( p ) ) && tmp.can_move_to( p ); }; const auto place_it = [&]( const tripoint & p ) { @@ -7700,40 +7713,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 ); } } } @@ -7914,7 +7931,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(); @@ -7985,20 +8003,36 @@ 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; } - tr.trigger( c.pos(), &c ); + + 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; + } + + 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 ); } template @@ -8120,7 +8154,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 ) ); @@ -8145,7 +8180,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 ); } @@ -8190,7 +8226,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 ) { @@ -8353,7 +8390,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 ccb53bfa0fd6f..7dfcbec413d87 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 ); @@ -576,13 +577,18 @@ class map 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, + void unboard_vehicle( const vpart_reference &, Character *passenger, bool dead_passenger = false ); // Remove passenger from vehicle at p. 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 ); @@ -1150,17 +1156,28 @@ 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 ); 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 bool process_fields(); bool process_fields_in_submap( submap *current_submap, const tripoint &submap_pos ); + 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. */ @@ -1343,8 +1360,7 @@ class map void place_toilet( const point &p, int charges = 6 * 4 ); void place_vending( const point &p, const std::string &type, bool reinforced = false ); // places an NPC, if static NPCs are enabled or if force is true - character_id place_npc( const point &p, const string_id &type, - bool force = false ); + character_id place_npc( const point &p, const string_id &type ); void apply_faction_ownership( const point &p1, const point &p2, const faction_id &id ); void add_spawn( const mtype_id &type, int count, const tripoint &p, bool friendly = false, int faction_id = -1, int mission_id = -1, @@ -1648,7 +1664,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, @@ -1808,7 +1824,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/map_extras.cpp b/src/map_extras.cpp index 55ffc8b999d9c..e89a4044c7735 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 ); @@ -3019,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 ); } diff --git a/src/map_field.cpp b/src/map_field.cpp index 293f011238b20..a5e8bb76bd856 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(), @@ -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( map_tile, p, cur, dirty_transparency_cache ) ) { + break; } } @@ -1349,6 +872,497 @@ bool map::process_fields_in_submap( submap *const current_submap, return 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(); + 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? /* @@ -1387,16 +1401,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 ); diff --git a/src/map_selector.cpp b/src/map_selector.cpp index 8c14a7c3a9c7f..601c53201ebe0 100644 --- a/src/map_selector.cpp +++ b/src/map_selector.cpp @@ -14,7 +14,7 @@ map_selector::map_selector( const tripoint &pos, int radius, bool accessible ) { - for( const tripoint &e : closest_tripoints_first( pos, radius ) ) { + for( const tripoint &e : closest_points_first( pos, radius ) ) { if( !accessible || get_map().clear_path( pos, e, radius, 1, 100 ) ) { data.emplace_back( e ); } diff --git a/src/mapdata.cpp b/src/mapdata.cpp index 7fedd71ff51c4..ed44da5fdb1c8 100644 --- a/src/mapdata.cpp +++ b/src/mapdata.cpp @@ -175,8 +175,11 @@ 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 { "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..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, @@ -202,6 +204,7 @@ enum ter_bitflags : int { TFLAG_RAIL, TFLAG_THIN_OBSTACLE, TFLAG_SMALL_PASSAGE, + TFLAG_Z_TRANSPARENT, NUM_TERFLAGS }; diff --git a/src/mapgen.cpp b/src/mapgen.cpp index 234b022c7ddf5..f000603ab0d97 100644 --- a/src/mapgen.cpp +++ b/src/mapgen.cpp @@ -5673,11 +5673,8 @@ void map::place_vending( const point &p, const std::string &type, bool reinforce } } -character_id map::place_npc( const point &p, const string_id &type, bool force ) +character_id map::place_npc( const point &p, const string_id &type ) { - if( !force && !get_option( "STATIC_NPC" ) ) { - return character_id(); //Do not generate an npc. - } shared_ptr_fast temp = make_shared_fast(); temp->normalize(); temp->load_npc_template( type ); @@ -6047,7 +6044,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/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/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" ); diff --git a/src/mattack_actors.cpp b/src/mattack_actors.cpp index c9c677ae2b864..bb6cf66b59c6c 100644 --- a/src/mattack_actors.cpp +++ b/src/mattack_actors.cpp @@ -167,7 +167,7 @@ void mon_spellcasting_actor::load_internal( const JsonObject &obj, const std::st spell_data = intermediate.get_spell(); spell_data.set_message( monster_message ); avatar fake_player; - move_cost = spell_data.casting_time( fake_player ); + move_cost = spell_data.casting_time( fake_player, true ); } bool mon_spellcasting_actor::call( monster &mon ) const @@ -287,7 +287,7 @@ bool melee_actor::call( monster &z ) const int hitspread = target->deal_melee_attack( &z, dice( acc, 10 ) ); if( hitspread < 0 ) { - auto msg_type = target == &g->u ? m_warning : m_info; + auto msg_type = target->is_avatar() ? m_warning : m_info; sfx::play_variant_sound( "mon_bite", "bite_miss", sfx::get_heard_volume( z.pos() ), sfx::get_heard_angle( z.pos() ) ); target->add_msg_player_or_npc( msg_type, miss_msg_u, miss_msg_npc, z.name() ); diff --git a/src/melee.cpp b/src/melee.cpp index 63ac92469fb80..321a5f0590e89 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 { @@ -316,7 +316,7 @@ float Character::hit_roll() const hit *= 0.75f; } - hit *= std::max( 0.25f, 1.0f - encumb( bp_torso ) / 100.0f ); + hit *= std::max( 0.25f, 1.0f - encumb( bodypart_id( "torso" ) ) / 100.0f ); return melee::melee_hit_range( hit ); } @@ -339,7 +339,7 @@ std::string Character::get_miss_reason() // in one turn add_miss_reason( _( "Your torso encumbrance throws you off-balance." ), - roll_remainder( encumb( bp_torso ) / 10.0 ) ); + roll_remainder( encumb( bodypart_id( "torso" ) ) / 10.0 ) ); const int farsightedness = 2 * ( has_trait( trait_HYPEROPIC ) && !worn_with_flag( "FIX_FARSIGHT" ) && !has_effect( effect_contacts ) ); @@ -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 { @@ -2256,8 +2260,8 @@ int Character::attack_speed( const item &weap ) const const int skill_cost = static_cast( ( base_move_cost * ( 15 - melee_skill ) / 15 ) ); /** @EFFECT_DEX increases attack speed */ const int dexbonus = dex_cur / 2; - const int encumbrance_penalty = encumb( bp_torso ) + - ( encumb( bp_hand_l ) + encumb( bp_hand_r ) ) / 2; + const int encumbrance_penalty = encumb( bodypart_id( "torso" ) ) + + ( encumb( bodypart_id( "hand_l" ) ) + encumb( bodypart_id( "hand_r" ) ) ) / 2; const int ma_move_cost = mabuff_attack_cost_penalty(); const float stamina_ratio = static_cast( get_stamina() ) / static_cast ( get_stamina_max() ); @@ -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/mission_end.cpp b/src/mission_end.cpp index 86641904bae5e..e0956e85a5efd 100644 --- a/src/mission_end.cpp +++ b/src/mission_end.cpp @@ -2,7 +2,7 @@ #include -#include "avatar.h" +#include "character.h" #include "debug.h" #include "game.h" #include "messages.h" @@ -26,6 +26,6 @@ void mission_end::deposit_box( mission *miss ) } else if( one_in( 3 ) ) { itemName = "m4a1"; } - g->u.i_add( item( itemName, 0 ) ); + get_player_character().i_add( item( itemName, 0 ) ); add_msg( m_good, _( "%s gave you an item from the deposit box." ), p->name ); } diff --git a/src/monattack.cpp b/src/monattack.cpp index 648b4cdaa2790..c767a6ddada5c 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 && 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() ); } @@ -2333,7 +2371,7 @@ bool mattack::formblob( monster *z ) } bool didit = false; - std::vector pts = closest_tripoints_first( z->pos(), 1 ); + std::vector pts = closest_points_first( z->pos(), 1 ); // Don't check own tile pts.erase( pts.begin() ); for( const tripoint &dest : pts ) { @@ -2417,9 +2455,9 @@ 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 ); + std::vector nearby_points = closest_points_first( z->pos(), 3 ); for( monster &candidate : g->all_monsters() ) { if( candidate.type->in_species( species_BLOB ) && candidate.type->id != mon_blob_brain ) { // Just give the allies consistent assignments. @@ -2452,7 +2490,7 @@ bool mattack::jackson( monster *z ) { // Jackson draws nearby zombies into the dance. std::list allies; - std::vector nearby_points = closest_tripoints_first( z->pos(), 3 ); + std::vector nearby_points = closest_points_first( z->pos(), 3 ); for( monster &candidate : g->all_monsters() ) { if( candidate.type->in_species( species_ZOMBIE ) && candidate.type->id != mon_zombie_jackson ) { // Just give the allies consistent assignments. @@ -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 && 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 && 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 && 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 && 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 && 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,9 +3996,8 @@ bool mattack::multi_robot( monster *z ) mode = 1; } else if( dist <= 30 ) { mode = 2; - } else if( ( target == &g->u && g->u.in_vehicle ) || - z->friendly != 0 || - cap > 4 ) { + } else if( ( target && 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. // Or if you've hacked it so the turret's on your side. ;-) if( dist < 50 ) { @@ -3985,8 +4040,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 +4064,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 +4109,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 +4145,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 +4180,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 +4195,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 +4213,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 +4250,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 +4290,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 +4332,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 +4356,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 +4394,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 +4433,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 +4466,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 +4502,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 +4539,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 +4607,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 +4647,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 +4772,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 +4805,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 && 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 +4911,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 +4949,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 +4976,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 +4996,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 +5008,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 +5033,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 +5065,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 +5093,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 +5157,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 +5246,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 +5340,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 +5421,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 +5466,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 +5615,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 +5670,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 +5721,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 +5754,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 +5785,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 +5813,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 +5838,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 +5851,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 +5868,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 ); diff --git a/src/mondeath.cpp b/src/mondeath.cpp index fb77eaee2811d..07a0230c32137 100644 --- a/src/mondeath.cpp +++ b/src/mondeath.cpp @@ -12,9 +12,9 @@ #include #include -#include "avatar.h" #include "bodypart.h" #include "calendar.h" +#include "character.h" #include "colony.h" #include "creature.h" #include "enums.h" @@ -38,7 +38,6 @@ #include "monster.h" #include "morale_types.h" #include "mtype.h" -#include "player.h" #include "pldata.h" #include "point.h" #include "rng.h" @@ -94,7 +93,7 @@ void mdeath::normal( monster &z ) sfx::play_variant_sound( "mon_death", "zombie_death", sfx::get_heard_volume( z.pos() ) ); } - if( g->u.sees( z ) ) { + if( get_player_character().sees( z ) ) { //Currently it is possible to get multiple messages that a monster died. add_msg( m_good, _( "The %s dies!" ), z.name() ); } @@ -130,6 +129,7 @@ static void scatter_chunks( const itype_id &chunk_name, int chunk_amt, monster & pile_size = std::min( chunk_amt, pile_size ); distance = std::abs( distance ); const item chunk( chunk_name, calendar::turn, pile_size ); + map &here = get_map(); for( int i = 0; i < chunk_amt; i += pile_size ) { bool drop_chunks = true; tripoint tarp( z.pos() + point( rng( -distance, distance ), rng( -distance, distance ) ) ); @@ -138,13 +138,13 @@ static void scatter_chunks( const itype_id &chunk_name, int chunk_amt, monster & for( size_t j = 0; j < traj.size(); j++ ) { tarp = traj[j]; if( one_in( 2 ) && z.bloodType().id() ) { - g->m.add_splatter( z.bloodType(), tarp ); + here.add_splatter( z.bloodType(), tarp ); } else { - g->m.add_splatter( z.gibType(), tarp, rng( 1, j + 1 ) ); + here.add_splatter( z.gibType(), tarp, rng( 1, j + 1 ) ); } - if( g->m.impassable( tarp ) ) { - g->m.bash( tarp, distance ); - if( g->m.impassable( tarp ) ) { + if( here.impassable( tarp ) ) { + here.bash( tarp, distance ); + if( here.impassable( tarp ) ) { // Target is obstacle, not destroyed by bashing, // stop trajectory in front of it, if this is the first // point (e.g. wall adjacent to monster), don't drop anything on it @@ -158,7 +158,7 @@ static void scatter_chunks( const itype_id &chunk_name, int chunk_amt, monster & } } if( drop_chunks ) { - g->m.add_item_or_charges( tarp, chunk ); + here.add_item_or_charges( tarp, chunk ); } } } @@ -181,8 +181,9 @@ void mdeath::splatter( monster &z ) const field_type_id type_blood = z.bloodType(); const field_type_id type_gib = z.gibType(); + map &here = get_map(); if( gibbable ) { - const auto area = g->m.points_in_radius( z.pos(), 1 ); + const auto area = here.points_in_radius( z.pos(), 1 ); int number_of_gibs = std::min( std::floor( corpse_damage ) - 1, 1 + max_hp / 5.0f ); if( pulverized && z.type->size >= creature_size::medium ) { @@ -191,8 +192,8 @@ void mdeath::splatter( monster &z ) } for( int i = 0; i < number_of_gibs; ++i ) { - g->m.add_splatter( type_gib, random_entry( area ), rng( 1, i + 1 ) ); - g->m.add_splatter( type_blood, random_entry( area ) ); + here.add_splatter( type_gib, random_entry( area ), rng( 1, i + 1 ) ); + here.add_splatter( type_blood, random_entry( area ) ); } } // 1% of the weight of the monster is the base, with overflow damage as a multiplier @@ -231,13 +232,13 @@ void mdeath::splatter( monster &z ) if( z.has_effect( effect_no_ammo ) ) { corpse.set_var( "no_ammo", "no_ammo" ); } - g->m.add_item_or_charges( z.pos(), corpse ); + here.add_item_or_charges( z.pos(), corpse ); } } void mdeath::acid( monster &z ) { - if( g->u.sees( z ) ) { + if( get_player_character().sees( z ) ) { if( z.type->dies.size() == 1 ) { //If this death function is the only function. The corpse gets dissolved. add_msg( m_mixed, _( "The %s's body dissolves into acid." ), z.name() ); @@ -245,35 +246,38 @@ void mdeath::acid( monster &z ) add_msg( m_warning, _( "The %s's body leaks acid." ), z.name() ); } } - g->m.add_field( z.pos(), fd_acid, 3 ); + get_map().add_field( z.pos(), fd_acid, 3 ); } void mdeath::boomer( monster &z ) { + map &here = get_map(); std::string explode = string_format( _( "a %s explode!" ), z.name() ); sounds::sound( z.pos(), 24, sounds::sound_t::combat, explode, false, "explosion", "small" ); - for( const tripoint &dest : g->m.points_in_radius( z.pos(), 1 ) ) { // *NOPAD* - g->m.bash( dest, 10 ); + for( const tripoint &dest : here.points_in_radius( z.pos(), 1 ) ) { // *NOPAD* + here.bash( dest, 10 ); if( monster *const target = g->critter_at( dest ) ) { target->stumble(); target->moves -= 250; } } - if( rl_dist( z.pos(), g->u.pos() ) == 1 ) { - g->u.add_env_effect( effect_boomered, bp_eyes, 2, 24_turns ); + Character &player_character = get_player_character(); + if( rl_dist( z.pos(), player_character.pos() ) == 1 ) { + player_character.add_env_effect( effect_boomered, bp_eyes, 2, 24_turns ); } - g->m.propagate_field( z.pos(), fd_bile, 15, 1 ); + here.propagate_field( z.pos(), fd_bile, 15, 1 ); } void mdeath::boomer_glow( monster &z ) { std::string explode = string_format( _( "a %s explode!" ), z.name() ); sounds::sound( z.pos(), 24, sounds::sound_t::combat, explode, false, "explosion", "small" ); + map &here = get_map(); - for( const tripoint &dest : g->m.points_in_radius( z.pos(), 1 ) ) { // *NOPAD* - g->m.bash( dest, 10 ); + for( const tripoint &dest : here.points_in_radius( z.pos(), 1 ) ) { // *NOPAD* + here.bash( dest, 10 ); if( monster *const target = g->critter_at( dest ) ) { target->stumble(); target->moves -= 250; @@ -290,7 +294,7 @@ void mdeath::boomer_glow( monster &z ) } } - g->m.propagate_field( z.pos(), fd_bile, 30, 2 ); + here.propagate_field( z.pos(), fd_bile, 30, 2 ); } void mdeath::kill_vines( monster &z ) @@ -320,8 +324,9 @@ void mdeath::kill_vines( monster &z ) void mdeath::vine_cut( monster &z ) { + map &here = get_map(); std::vector vines; - for( const tripoint &tmp : g->m.points_in_radius( z.pos(), 1 ) ) { + for( const tripoint &tmp : here.points_in_radius( z.pos(), 1 ) ) { if( tmp == z.pos() ) { continue; // Skip ourselves } @@ -334,7 +339,7 @@ void mdeath::vine_cut( monster &z ) for( auto &vine : vines ) { bool found_neighbor = false; - for( const tripoint &dest : g->m.points_in_radius( vine->pos(), 1 ) ) { + for( const tripoint &dest : here.points_in_radius( vine->pos(), 1 ) ) { if( dest != z.pos() ) { // Not the dying vine if( monster *const v = g->critter_at( dest ) ) { @@ -353,7 +358,7 @@ void mdeath::vine_cut( monster &z ) void mdeath::triffid_heart( monster &z ) { - if( g->u.sees( z ) ) { + if( get_player_character().sees( z ) ) { add_msg( m_warning, _( "The surrounding roots begin to crack and crumble." ) ); } g->timed_events.add( timed_event_type::ROOTS_DIE, calendar::turn + 10_minutes ); @@ -364,9 +369,10 @@ void mdeath::fungus( monster &z ) //~ the sound of a fungus dying sounds::sound( z.pos(), 10, sounds::sound_t::combat, _( "Pouf!" ), false, "misc", "puff" ); - fungal_effects fe( *g, g->m ); - for( const tripoint &sporep : g->m.points_in_radius( z.pos(), 1 ) ) { // *NOPAD* - if( g->m.impassable( sporep ) ) { + map &here = get_map(); + fungal_effects fe( *g, here ); + for( const tripoint &sporep : here.points_in_radius( z.pos(), 1 ) ) { // *NOPAD* + if( here.impassable( sporep ) ) { continue; } // z is dead, don't credit it with the kill @@ -377,14 +383,14 @@ void mdeath::fungus( monster &z ) void mdeath::disintegrate( monster &z ) { - if( g->u.sees( z ) ) { + if( get_player_character().sees( z ) ) { add_msg( m_good, _( "The %s disintegrates!" ), z.name() ); } } void mdeath::worm( monster &z ) { - if( g->u.sees( z ) ) { + if( get_player_character().sees( z ) ) { if( z.type->dies.size() == 1 ) { add_msg( m_good, _( "The %s splits in two!" ), z.name() ); } else { @@ -400,7 +406,7 @@ void mdeath::worm( monster &z ) void mdeath::disappear( monster &z ) { - if( g->u.sees( z ) ) { + if( get_player_character().sees( z ) ) { add_msg( m_good, _( "The %s disappears." ), z.name() ); } } @@ -419,11 +425,12 @@ void mdeath::guilt( monster &z ) guilt_tresholds[50] = _( "You regret killing %s." ); guilt_tresholds[25] = _( "You feel remorse for killing %s." ); - if( g->u.has_trait( trait_PSYCHOPATH ) || g->u.has_trait_flag( "PRED3" ) || - g->u.has_trait_flag( "PRED4" ) || g->u.has_trait( trait_KILLER ) ) { + Character &player_character = get_player_character(); + if( player_character.has_trait( trait_PSYCHOPATH ) || player_character.has_trait_flag( "PRED3" ) || + player_character.has_trait_flag( "PRED4" ) || player_character.has_trait( trait_KILLER ) ) { return; } - if( rl_dist( z.pos(), g->u.pos() ) > MAX_GUILT_DISTANCE ) { + if( rl_dist( z.pos(), player_character.pos() ) > MAX_GUILT_DISTANCE ) { // Too far away, we can deal with it. return; } @@ -439,7 +446,8 @@ void mdeath::guilt( monster &z ) "about their deaths anymore." ), z.name( maxKills ) ); } return; - } else if( ( g->u.has_trait_flag( "PRED1" ) ) || ( g->u.has_trait_flag( "PRED2" ) ) ) { + } else if( ( player_character.has_trait_flag( "PRED1" ) ) || + ( player_character.has_trait_flag( "PRED2" ) ) ) { msg = ( _( "Culling the weak is distasteful, but necessary." ) ); msgtype = m_neutral; } else { @@ -459,30 +467,31 @@ void mdeath::guilt( monster &z ) time_duration decayDelay = 3_minutes * ( 1.0 - ( static_cast( kill_count ) / maxKills ) ); if( z.type->in_species( species_ZOMBIE ) ) { moraleMalus /= 10; - if( g->u.has_trait( trait_PACIFIST ) ) { + if( player_character.has_trait( trait_PACIFIST ) ) { moraleMalus *= 5; - } else if( g->u.has_trait_flag( "PRED1" ) ) { + } else if( player_character.has_trait_flag( "PRED1" ) ) { moraleMalus /= 4; - } else if( g->u.has_trait_flag( "PRED2" ) ) { + } else if( player_character.has_trait_flag( "PRED2" ) ) { moraleMalus /= 5; } } - g->u.add_morale( MORALE_KILLED_MONSTER, moraleMalus, maxMalus, duration, decayDelay ); + player_character.add_morale( MORALE_KILLED_MONSTER, moraleMalus, maxMalus, duration, decayDelay ); } void mdeath::blobsplit( monster &z ) { int speed = z.get_speed() - rng( 30, 50 ); - g->m.spawn_item( z.pos(), "slime_scrap", 1, 0, calendar::turn ); + get_map().spawn_item( z.pos(), "slime_scrap", 1, 0, calendar::turn ); + Character &player_character = get_player_character(); if( z.get_speed() <= 0 ) { - if( g->u.sees( z ) ) { + if( player_character.sees( z ) ) { // TODO: Add vermin-tagged tiny versions of the splattered blob :) add_msg( m_good, _( "The %s splatters apart." ), z.name() ); } return; } - if( g->u.sees( z ) ) { + if( player_character.sees( z ) ) { if( z.type->dies.size() == 1 ) { add_msg( m_good, _( "The %s splits in two!" ), z.name() ); } else { @@ -512,20 +521,22 @@ void mdeath::brainblob( monster &z ) void mdeath::jackson( monster &z ) { + bool music_stopped = false; for( monster &critter : g->all_monsters() ) { if( critter.type->id == mon_zombie_dancer ) { critter.poly( mon_zombie_hulk ); critter.remove_effect( effect_controlled ); } - if( g->u.sees( z ) ) { - add_msg( m_warning, _( "The music stops!" ) ); - } + music_stopped = true; + } + if( music_stopped && get_player_character().sees( z ) ) { + add_msg( m_warning, _( "The music stops!" ) ); } } void mdeath::melt( monster &z ) { - if( g->u.sees( z ) ) { + if( get_player_character().sees( z ) ) { add_msg( m_good, _( "The %s melts away." ), z.name() ); } } @@ -543,12 +554,13 @@ void mdeath::amigara( monster &z ) } // We were the last! - if( g->u.has_effect( effect_amigara ) ) { - g->u.remove_effect( effect_amigara ); + Character &player_character = get_player_character(); + if( player_character.has_effect( effect_amigara ) ) { + player_character.remove_effect( effect_amigara ); add_msg( _( "Your obsession with the fault fades away…" ) ); } - g->m.spawn_artifact( z.pos() ); + get_map().spawn_artifact( z.pos() ); } void mdeath::thing( monster &z ) @@ -581,7 +593,8 @@ void mdeath::explode( monster &z ) void mdeath::focused_beam( monster &z ) { - map_stack items = g->m.i_at( z.pos() ); + map &here = get_map(); + map_stack items = here.i_at( z.pos() ); for( map_stack::iterator it = items.begin(); it != items.end(); ) { if( it->typeId() == itype_processor ) { it = items.erase( it ); @@ -592,7 +605,7 @@ void mdeath::focused_beam( monster &z ) if( !z.inv.empty() ) { - if( g->u.sees( z ) ) { + if( get_player_character().sees( z ) ) { add_msg( m_warning, _( "As the final light is destroyed, it erupts in a blinding flare!" ) ); } @@ -604,10 +617,10 @@ void mdeath::focused_beam( monster &z ) std::vector traj = line_to( z.pos(), p, 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, 2 ); + here.add_field( elem, fd_dazzling, 2 ); } } @@ -634,7 +647,8 @@ void mdeath::broken( monster &z ) const float corpse_damage = 2.5 * overflow_damage / max_hp; broken_mon.set_damage( static_cast( std::floor( corpse_damage * itype::damage_scale ) ) ); - g->m.add_item_or_charges( z.pos(), broken_mon ); + map &here = get_map(); + here.add_item_or_charges( z.pos(), broken_mon ); if( z.type->has_flag( MF_DROPS_AMMO ) ) { for( const std::pair &ammo_entry : z.ammo ) { @@ -661,14 +675,14 @@ void mdeath::broken( monster &z ) mags.insert( mags.end(), mag ); ammo_count -= mag.type->magazine->capacity; } - g->m.spawn_items( z.pos(), mags ); + here.spawn_items( z.pos(), mags ); spawned = true; break; } } } if( !spawned ) { - g->m.spawn_item( z.pos(), ammo_entry.first, ammo_entry.second, 1, + here.spawn_item( z.pos(), ammo_entry.first, ammo_entry.second, 1, calendar::turn ); } } @@ -676,17 +690,19 @@ void mdeath::broken( monster &z ) } // TODO: make mdeath::splatter work for robots - if( ( broken_mon.damage() >= broken_mon.max_damage() ) && g->u.sees( z.pos() ) ) { + Character &player_character = get_player_character(); + if( ( broken_mon.damage() >= broken_mon.max_damage() ) && player_character.sees( z.pos() ) ) { add_msg( m_good, _( "The %s is destroyed!" ), z.name() ); - } else if( g->u.sees( z.pos() ) ) { + } else if( player_character.sees( z.pos() ) ) { add_msg( m_good, _( "The %s collapses!" ), z.name() ); } } void mdeath::ratking( monster &z ) { - g->u.remove_effect( effect_rat ); - if( g->u.sees( z ) ) { + Character &player_character = get_player_character(); + player_character.remove_effect( effect_rat ); + if( player_character.sees( z ) ) { add_msg( m_warning, _( "Rats suddenly swarm into view." ) ); } @@ -697,8 +713,9 @@ void mdeath::ratking( monster &z ) void mdeath::darkman( monster &z ) { - g->u.remove_effect( effect_darkness ); - if( g->u.sees( z ) ) { + Character &player_character = get_player_character(); + player_character.remove_effect( effect_darkness ); + if( player_character.sees( z ) ) { add_msg( m_good, _( "The %s melts away." ), z.name() ); } } @@ -707,21 +724,22 @@ void mdeath::gas( monster &z ) { std::string explode = string_format( _( "a %s explode!" ), z.name() ); sounds::sound( z.pos(), 24, sounds::sound_t::combat, explode, false, "explosion", "small" ); - g->m.emit_field( z.pos(), emit_id( "emit_toxic_blast" ) ); + get_map().emit_field( z.pos(), emit_id( "emit_toxic_blast" ) ); } void mdeath::smokeburst( monster &z ) { std::string explode = string_format( _( "a %s explode!" ), z.name() ); sounds::sound( z.pos(), 24, sounds::sound_t::combat, explode, false, "explosion", "small" ); - g->m.emit_field( z.pos(), emit_id( "emit_smoke_blast" ) ); + get_map().emit_field( z.pos(), emit_id( "emit_smoke_blast" ) ); } void mdeath::fungalburst( monster &z ) { + map &here = get_map(); // If the fungus died from anti-fungal poison, don't pouf - if( g->m.get_field_intensity( z.pos(), fd_fungicidal_gas ) ) { - if( g->u.sees( z ) ) { + if( here.get_field_intensity( z.pos(), fd_fungicidal_gas ) ) { + if( get_player_character().sees( z ) ) { add_msg( m_good, _( "The %s inflates and melts away." ), z.name() ); } return; @@ -729,12 +747,12 @@ void mdeath::fungalburst( monster &z ) std::string explode = string_format( _( "a %s explodes!" ), z.name() ); sounds::sound( z.pos(), 24, sounds::sound_t::combat, explode, false, "explosion", "small" ); - g->m.emit_field( z.pos(), emit_id( "emit_fungal_blast" ) ); + here.emit_field( z.pos(), emit_id( "emit_fungal_blast" ) ); } void mdeath::jabberwock( monster &z ) { - player *ch = dynamic_cast( z.get_killer() ); + Character *ch = dynamic_cast( z.get_killer() ); bool vorpal = ch && ch->is_player() && ch->weapon.has_flag( "DIAMOND" ) && @@ -756,7 +774,7 @@ void mdeath::jabberwock( monster &z ) void mdeath::gameover( monster &z ) { add_msg( m_bad, _( "The %s was destroyed! GAME OVER!" ), z.name() ); - g->u.set_part_hp_cur( bodypart_id( "torso" ), 0 ); + get_player_character().set_part_hp_cur( bodypart_id( "torso" ), 0 ); } void mdeath::kill_breathers( monster &/*z*/ ) @@ -771,7 +789,7 @@ void mdeath::kill_breathers( monster &/*z*/ ) void mdeath::broken_ammo( monster &z ) { - if( g->u.sees( z.pos() ) ) { + if( get_player_character().sees( z.pos() ) ) { //~ %s is the possessive form of the monster's name add_msg( m_info, _( "The %s's interior compartment sizzles with destructive energy." ), z.name() ); @@ -791,15 +809,16 @@ void make_mon_corpse( monster &z, int damageLvl ) if( z.has_effect( effect_no_ammo ) ) { corpse.set_var( "no_ammo", "no_ammo" ); } - g->m.add_item_or_charges( z.pos(), corpse ); + get_map().add_item_or_charges( z.pos(), corpse ); } void mdeath::preg_roach( monster &z ) { + Character &player_character = get_player_character(); int num_roach = rng( 1, 3 ); while( num_roach > 0 && g->place_critter_around( mon_giant_cockroach_nymph, z.pos(), 1 ) ) { num_roach--; - if( g->u.sees( z ) ) { + if( player_character.sees( z ) ) { add_msg( m_warning, _( "A cockroach nymph crawls out of the pregnant giant cockroach corpse." ) ); } } @@ -808,7 +827,7 @@ void mdeath::preg_roach( monster &z ) void mdeath::fireball( monster &z ) { if( one_in( 10 ) ) { - g->m.propagate_field( z.pos(), fd_fire, 15, 3 ); + get_map().propagate_field( z.pos(), fd_fire, 15, 3 ); std::string explode = string_format( _( "an explosion of tank of the %s's flamethrower!" ), z.name() ); sounds::sound( z.pos(), 24, sounds::sound_t::combat, explode, false, "explosion", "default" ); @@ -820,8 +839,9 @@ void mdeath::fireball( monster &z ) void mdeath::conflagration( monster &z ) { - for( const auto &dest : g->m.points_in_radius( z.pos(), 1 ) ) { - g->m.propagate_field( dest, fd_fire, 18, 3 ); + map &here = get_map(); + for( const auto &dest : here.points_in_radius( z.pos(), 1 ) ) { + here.propagate_field( dest, fd_fire, 18, 3 ); } const std::string explode = string_format( _( "a %s explode!" ), z.name() ); sounds::sound( z.pos(), 24, sounds::sound_t::combat, explode, false, "explosion", "small" ); @@ -830,20 +850,21 @@ void mdeath::conflagration( monster &z ) void mdeath::necro_boomer( monster &z ) { + map &here = get_map(); std::string explode = string_format( _( "a %s explodes!" ), z.name() ); sounds::sound( z.pos(), 24, sounds::sound_t::combat, explode, false, "explosion", "small" ); - for( const tripoint &aoe : g->m.points_in_radius( z.pos(), 10 ) ) { - for( item &corpse : g->m.i_at( aoe ) ) { + for( const tripoint &aoe : here.points_in_radius( z.pos(), 10 ) ) { + for( item &corpse : here.i_at( aoe ) ) { if( !corpse.is_corpse() ) { continue; } if( g->revive_corpse( aoe, corpse ) ) { - g->m.i_rem( aoe, &corpse ); + here.i_rem( aoe, &corpse ); break; } } } - for( const tripoint &aoe : g->m.points_in_radius( z.pos(), 10 ) ) { + for( const tripoint &aoe : here.points_in_radius( z.pos(), 10 ) ) { monster *mon = g->critter_at( aoe ); if( mon != nullptr && one_in( 10 ) ) { mon->allow_upgrade(); diff --git a/src/mondefense.cpp b/src/mondefense.cpp index cdaaafa3d690f..e8797d9f9a5b0 100644 --- a/src/mondefense.cpp +++ b/src/mondefense.cpp @@ -126,7 +126,7 @@ void mdefense::acidsplash( monster &m, Creature *const source, } // Don't splatter directly on the `m`, that doesn't work well - std::vector pts = closest_tripoints_first( source->pos(), 1 ); + std::vector pts = closest_points_first( source->pos(), 1 ); pts.erase( std::remove( pts.begin(), pts.end(), m.pos() ), pts.end() ); projectile prj; diff --git a/src/monexamine.cpp b/src/monexamine.cpp index 9fd185b08dc67..76c8dd6741ac7 100644 --- a/src/monexamine.cpp +++ b/src/monexamine.cpp @@ -70,6 +70,7 @@ bool monexamine::pet_menu( monster &z ) attach_bag, remove_bag, drop_all, + push_monster, give_items, mon_armor_add, mon_harness_remove, @@ -98,6 +99,7 @@ bool monexamine::pet_menu( monster &z ) amenu.text = string_format( _( "What to do with your %s?" ), pet_name ); amenu.addentry( swap_pos, true, 's', _( "Swap positions" ) ); + amenu.addentry( push_monster, true, 'p', _( "Push %s" ), pet_name ); amenu.addentry( rename, true, 'e', _( "Rename" ) ); Character &player_character = get_player_character(); if( z.has_effect( effect_has_bag ) ) { @@ -221,6 +223,9 @@ bool monexamine::pet_menu( monster &z ) case swap_pos: swap( z ); break; + case push_monster: + push( z ); + break; case rename: rename_pet( z ); break; diff --git a/src/monmove.cpp b/src/monmove.cpp index 3bf63ea95b2ef..c7b5f7c7cffcd 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,17 @@ 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_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; } } 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 +325,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 +355,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 +503,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 +526,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 +563,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 +598,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 +634,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 +645,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 +661,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 +679,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 +722,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 +758,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 +775,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 +807,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 +839,7 @@ void monster::move() } } - if( !g->m.has_zlevels() ) { + if( !here.has_zlevels() ) { // Otherwise weird things happen destination.z = posz(); } @@ -865,19 +872,28 @@ 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 = g->m.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 = get_map().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, 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( !can_climb() || !g->m.has_floor_or_support( candidate ) ) { + 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; } @@ -890,7 +906,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 +947,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 +979,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 +995,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 +1042,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 +1073,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 +1122,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 +1145,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 +1157,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 +1186,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 +1206,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 +1262,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 +1326,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 +1342,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 +1415,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 +1461,9 @@ bool monster::attack_at( const tripoint &p ) static tripoint find_closest_stair( const tripoint &near_this, const ter_bitflags stair_type ) { - for( const tripoint &candidate : closest_tripoints_first( near_this, 10 ) ) { - if( g->m.has_flag( stair_type, candidate ) ) { + map &here = get_map(); + for( const tripoint &candidate : closest_points_first( near_this, 10 ) ) { + if( here.has_flag( stair_type, candidate ) ) { return candidate; } } @@ -1457,38 +1480,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 +1535,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 +1545,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 +1580,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 +1631,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 +1659,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 +1670,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 +1712,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 +1733,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 +1794,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 +1821,25 @@ 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( here.has_flag( TFLAG_RAMP_DOWN, dest ) ) { + valid_stumbles.push_back( tripoint( dest.xy(), dest.z - 1 ) ); + } else if( here.has_flag( TFLAG_RAMP_UP, dest ) ) { + valid_stumbles.push_back( tripoint( dest.xy(), dest.z + 1 ) ); + } else { + 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 +1850,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 +1871,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 +1917,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 +1942,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 +1955,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 +1979,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 +1989,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 +2006,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 +2052,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 +2070,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..0e2ea45eaf6b2 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,19 @@ monster_attitude monster::attitude( const Character *u ) const if( has_effect( effect_docile ) ) { return MATT_FPASSIVE; } - if( u == &g->u ) { - 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 != nullptr ) { + 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 ) ) { + return MATT_FRIEND; + } + if( u->is_hallucination() ) { + return MATT_IGNORE; + } + } } } if( effect_cache[FLEEING] ) { @@ -1170,8 +1174,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 +1364,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 +1375,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 +1415,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 +1427,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 +1656,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 +1677,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 +1685,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 +1725,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 +1737,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 +1750,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 +1995,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 +2075,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 +2087,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 +2114,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 +2128,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 +2142,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 +2236,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 +2261,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 +2269,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 +2312,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 +2380,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 +2457,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 +2473,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 +2484,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 +2504,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 +2600,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 +2667,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 +2675,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 +2690,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 +2829,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 +2932,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/mutation.cpp b/src/mutation.cpp index d2b134efc4cb2..0f5080298b279 100644 --- a/src/mutation.cpp +++ b/src/mutation.cpp @@ -151,7 +151,7 @@ void Character::set_mutation( const trait_id &trait ) cached_mutations.push_back( &trait.obj() ); mutation_effect( trait ); recalc_sight_limits(); - reset_encumbrance(); + calc_encumbrance(); } void Character::unset_mutation( const trait_id &trait_ ) @@ -170,7 +170,7 @@ void Character::unset_mutation( const trait_id &trait_ ) my_mutations.erase( iter ); mutation_loss_effect( trait ); recalc_sight_limits(); - reset_encumbrance(); + calc_encumbrance(); } void Character::switch_mutations( const trait_id &switched, const trait_id &target, diff --git a/src/newcharacter.cpp b/src/newcharacter.cpp index 1ad16e7d335c2..99d00441be599 100644 --- a/src/newcharacter.cpp +++ b/src/newcharacter.cpp @@ -2187,8 +2187,7 @@ tab_direction set_scenario( avatar &u, points_left &points, wprintz( w_flags, c_light_gray, _( "Fungal infected player" ) ); wprintz( w_flags, c_light_gray, ( "\n" ) ); } - if( get_option( "STARTING_NPC" ) == "scenario" && - sorted_scens[cur_id]->has_flag( "LONE_START" ) ) { + if( sorted_scens[cur_id]->has_flag( "LONE_START" ) ) { wprintz( w_flags, c_light_gray, _( "No starting NPC" ) ); wprintz( w_flags, c_light_gray, ( "\n" ) ); } @@ -2915,8 +2914,8 @@ void Character::add_traits() void Character::add_traits( points_left &points ) { - // TODO: get rid of using g->u here, use `this` instead - for( const trait_id &tr : g->u.prof->get_locked_traits() ) { + // TODO: get rid of using get_avatar() here, use `this` instead + for( const trait_id &tr : get_avatar().prof->get_locked_traits() ) { if( !has_trait( tr ) ) { toggle_trait( tr ); } else { diff --git a/src/npc.cpp b/src/npc.cpp index 573f8291453f6..a0d2519d9e4db 100644 --- a/src/npc.cpp +++ b/src/npc.cpp @@ -752,7 +752,7 @@ void npc::place_on_map() return; } - for( const tripoint &p : closest_tripoints_first( pos(), SEEX + 1 ) ) { + for( const tripoint &p : closest_points_first( pos(), SEEX + 1 ) ) { if( g->is_empty( p ) ) { setpos( p ); return; @@ -909,7 +909,8 @@ void npc::finish_read( item &book ) const skill_id &skill = reading->skill; // NPCs don't need to identify the book or learn recipes yet. // NPCs don't read to other NPCs yet. - const bool display_messages = my_fac->id == faction_id( "your_followers" ) && g->u.sees( pos() ); + const bool display_messages = my_fac->id == faction_id( "your_followers" ) && + get_player_character().sees( pos() ); bool continuous = false; //whether to continue reading or not if( book_fun_for( book, *this ) != 0 ) { @@ -1028,7 +1029,7 @@ void npc::do_npc_read() } item &chosen = *loc.obtain( *ch ); if( can_read( chosen, fail_reasons ) ) { - if( g->u.sees( pos() ) ) { + if( get_player_character().sees( pos() ) ) { add_msg( m_info, _( "%s starts reading." ), disp_name() ); } start_read( chosen, pl ); @@ -1106,9 +1107,10 @@ bool npc::wear_if_wanted( const item &it, std::string &reason ) void npc::stow_item( item &it ) { + bool avatar_sees = get_player_character().sees( pos() ); if( wear_item( weapon, false ) ) { // Wearing the item was successful, remove weapon and post message. - if( g->u.sees( pos() ) ) { + if( avatar_sees ) { add_msg_if_npc( m_info, _( " wears the %s." ), weapon.tname() ); } remove_weapon(); @@ -1119,7 +1121,7 @@ void npc::stow_item( item &it ) } for( auto &e : worn ) { if( e.can_holster( it ) ) { - if( g->u.sees( pos() ) ) { + if( avatar_sees ) { //~ %1$s: weapon name, %2$s: holster name add_msg_if_npc( m_info, _( " puts away the %1$s in the %2$s." ), weapon.tname(), e.tname() ); @@ -1130,16 +1132,16 @@ void npc::stow_item( item &it ) } } if( volume_carried() + weapon.volume() <= volume_capacity() ) { - if( g->u.sees( pos() ) ) { + if( avatar_sees ) { add_msg_if_npc( m_info, _( " puts away the %s." ), weapon.tname() ); } i_add( remove_weapon() ); moves -= 15; } else { // No room for weapon, so we drop it - if( g->u.sees( pos() ) ) { + if( avatar_sees ) { add_msg_if_npc( m_info, _( " drops the %s." ), weapon.tname() ); } - g->m.add_item_or_charges( pos(), remove_weapon() ); + get_map().add_item_or_charges( pos(), remove_weapon() ); } } @@ -1183,7 +1185,7 @@ bool npc::wield( item &it ) g->events().send( getID(), weapon.typeId() ); - if( g->u.sees( pos() ) ) { + if( get_player_character().sees( pos() ) ) { add_msg_if_npc( m_info, _( " wields a %s." ), weapon.tname() ); } invalidate_range_cache(); @@ -1337,7 +1339,7 @@ void npc::mutiny() if( !my_fac || !is_player_ally() ) { return; } - const bool seen = g->u.sees( pos() ); + const bool seen = get_player_character().sees( pos() ); if( seen ) { add_msg( m_bad, _( "%s is tired of your incompetent leadership and abuse!" ), disp_name() ); } @@ -1368,7 +1370,7 @@ float npc::vehicle_danger( int radius ) const { const tripoint from( posx() - radius, posy() - radius, posz() ); const tripoint to( posx() + radius, posy() + radius, posz() ); - VehicleList vehicles = g->m.get_vehicles( from, to ); + VehicleList vehicles = get_map().get_vehicles( from, to ); int danger = 0; @@ -1539,13 +1541,14 @@ void npc::decide_needs() void npc::say( const std::string &line, const sounds::sound_t spriority ) const { std::string formatted_line = line; - parse_tags( formatted_line, g->u, *this ); + Character &player_character = get_player_character(); + parse_tags( formatted_line, player_character, *this ); if( has_trait( trait_MUTE ) ) { return; } std::string sound = string_format( _( "%1$s saying \"%2$s\"" ), name, formatted_line ); - if( g->u.sees( *this ) && g->u.is_deaf() ) { + if( player_character.sees( *this ) && player_character.is_deaf() ) { add_msg( m_warning, _( "%1$s says something but you can't hear it!" ), name ); } // Hallucinations don't make noise when they speak @@ -1951,7 +1954,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; @@ -1975,7 +1978,8 @@ bool npc::is_ally( const player &p ) const return true; } if( faction_api_version < 2 ) { - if( is_ally( g->u ) && guy.is_ally( g->u ) ) { + Character &player_character = get_player_character(); + if( is_ally( player_character ) && guy.is_ally( player_character ) ) { return true; } else if( get_attitude_group( get_attitude() ) == guy.get_attitude_group( guy.get_attitude() ) ) { @@ -1988,10 +1992,10 @@ bool npc::is_ally( const player &p ) const bool npc::is_player_ally() const { - return is_ally( g->u ); + return is_ally( get_player_character() ); } -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 +2015,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 ) ); @@ -2087,14 +2091,15 @@ Creature::Attitude npc::attitude_to( const Creature &other ) const } } + Character &player_character = get_player_character(); if( is_player_ally() ) { // Friendly NPCs share player's alliances - return g->u.attitude_to( other ); + return player_character.attitude_to( other ); } if( other.is_npc() ) { // Hostile NPCs are also hostile towards player's allies - if( is_enemy() && other.attitude_to( g->u ) == Attitude::FRIENDLY ) { + if( is_enemy() && other.attitude_to( player_character ) == Attitude::FRIENDLY ) { return Attitude::HOSTILE; } @@ -2132,7 +2137,7 @@ void npc::npc_dismount() return; } cata::optional pnt; - for( const auto &elem : g->m.points_in_radius( pos(), 1 ) ) { + for( const auto &elem : get_map().points_in_radius( pos(), 1 ) ) { if( g->is_empty( elem ) ) { pnt = elem; break; @@ -2199,11 +2204,13 @@ bool npc::is_active() const int npc::follow_distance() const { + Character &player_character = get_player_character(); + map &here = get_map(); // HACK: If the player is standing on stairs, follow closely // This makes the stair hack less painful to use if( is_walking_with() && - ( g->m.has_flag( TFLAG_GOES_DOWN, g->u.pos() ) || - g->m.has_flag( TFLAG_GOES_UP, g->u.pos() ) ) ) { + ( here.has_flag( TFLAG_GOES_DOWN, player_character.pos() ) || + here.has_flag( TFLAG_GOES_UP, player_character.pos() ) ) ) { return 1; } // Uses ally_rule follow_distance_2 to determine if should follow by 2 or 4 tiles @@ -2211,7 +2218,7 @@ int npc::follow_distance() const return 2; } // If NPC doesn't see player, change follow distance to 2 - if( !sees( g->u ) ) { + if( !sees( player_character ) ) { return 2; } return 4; @@ -2253,15 +2260,16 @@ int npc::print_info( const catacurses::window &w, int line, int vLines, int colu trim_and_print( w, point( column + bar.first.length() + 1, line ), iWidth, basic_symbol_color(), name ); + Character &player_character = get_player_character(); // Hostility indicator in the second line. - Attitude att = attitude_to( g->u ); + Attitude att = attitude_to( player_character ); const std::pair res = Creature::get_attitude_ui_data( att ); mvwprintz( w, point( column, ++line ), res.second, res.first.translated() ); // Awareness indicator on the third line. - std::string senses_str = sees( g->u ) ? _( "Aware of your presence" ) : + std::string senses_str = sees( player_character ) ? _( "Aware of your presence" ) : _( "Unaware of you" ); - mvwprintz( w, point( column, ++line ), sees( g->u ) ? c_yellow : c_green, senses_str ); + mvwprintz( w, point( column, ++line ), sees( player_character ) ? c_yellow : c_green, senses_str ); // Print what item the NPC is holding if any on the fourth line. if( is_armed() ) { @@ -2288,8 +2296,8 @@ int npc::print_info( const catacurses::window &w, int line, int vLines, int colu // 3 perception and 3 distance would see all mutations - cap 0 // 3 perception and 15 distance - cap 5, some mutations visible // 3 perception and 20 distance would be barely able to discern huge antlers on a person - cap 10 - const int per = g->u.get_per(); - const int dist = rl_dist( g->u.pos(), pos() ); + const int per = player_character.get_per(); + const int dist = rl_dist( player_character.pos(), pos() ); int visibility_cap; if( per <= 1 ) { visibility_cap = INT_MAX; @@ -2474,7 +2482,7 @@ void npc::die( Creature *nkiller ) // Need to unboard from vehicle before dying, otherwise // the vehicle code cannot find us if( in_vehicle ) { - g->m.unboard_vehicle( pos(), true ); + get_map().unboard_vehicle( pos(), true ); } if( is_mounted() ) { monster *critter = mounted_creature.get(); @@ -2497,14 +2505,15 @@ void npc::die( Creature *nkiller ) dead = true; Character::die( nkiller ); + Character &player_character = get_player_character(); if( is_hallucination() ) { - if( g->u.sees( *this ) ) { + if( player_character.sees( *this ) ) { add_msg( _( "%s disappears." ), name.c_str() ); } return; } - if( g->u.sees( *this ) ) { + if( player_character.sees( *this ) ) { add_msg( _( "%s dies!" ), name ); } @@ -2512,15 +2521,15 @@ void npc::die( Creature *nkiller ) g->events().send( ch->getID(), getID(), get_name() ); } - if( killer == &g->u && ( !guaranteed_hostile() || hit_by_player ) ) { - bool cannibal = g->u.has_trait( trait_CANNIBAL ); - bool psycho = g->u.has_trait( trait_PSYCHOPATH ); - if( g->u.has_trait( trait_SAPIOVORE ) || psycho ) { + if( killer == &player_character && ( !guaranteed_hostile() || hit_by_player ) ) { + bool cannibal = player_character.has_trait( trait_CANNIBAL ); + bool psycho = player_character.has_trait( trait_PSYCHOPATH ); + if( player_character.has_trait( trait_SAPIOVORE ) || psycho ) { // No morale effect } else if( cannibal ) { - g->u.add_morale( MORALE_KILLED_INNOCENT, -5, 0, 2_days, 3_hours ); + player_character.add_morale( MORALE_KILLED_INNOCENT, -5, 0, 2_days, 3_hours ); } else { - g->u.add_morale( MORALE_KILLED_INNOCENT, -100, 0, 2_days, 3_hours ); + player_character.add_morale( MORALE_KILLED_INNOCENT, -100, 0, 2_days, 3_hours ); } } @@ -2623,7 +2632,7 @@ void npc::add_msg_if_npc( const std::string &msg ) const void npc::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 ) ); } } @@ -2637,7 +2646,7 @@ void npc::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 ) ); } } @@ -2722,14 +2731,15 @@ void npc::on_load() // Not necessarily true, but it's not a bad idea to set this has_new_items = true; + map &here = get_map(); // for spawned npcs - 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.veh_at( pos() ).part_with_feature( VPFLAG_BOARDABLE, true ) && !in_vehicle ) { - g->m.board_vehicle( pos(), this ); + if( here.veh_at( pos() ).part_with_feature( VPFLAG_BOARDABLE, true ) && !in_vehicle ) { + here.board_vehicle( pos(), this ); } if( has_effect( effect_riding ) && !mounted_creature ) { if( const monster *const mon = g->critter_at( pos() ) ) { @@ -2801,7 +2811,7 @@ bool npc::dispose_item( item_location &&obj, const std::string & ) if( opts.empty() ) { // Drop it - g->m.add_item_or_charges( pos(), *obj ); + get_map().add_item_or_charges( pos(), *obj ); obj.remove_item(); return true; } @@ -2902,7 +2912,7 @@ bool npc::will_accept_from_player( const item &it ) const return false; } - if( is_minion() || g->u.has_trait( trait_DEBUG_MIND_CONTROL ) || + if( is_minion() || get_player_character().has_trait( trait_DEBUG_MIND_CONTROL ) || it.has_flag( flag_NPC_SAFE ) ) { return true; } @@ -2949,16 +2959,17 @@ std::set npc::get_path_avoid() const // TODO: Cache this somewhere ret.insert( critter.pos() ); } + map &here = get_map(); if( rules.has_flag( ally_rule::avoid_doors ) ) { - for( const tripoint &p : g->m.points_in_radius( pos(), 30 ) ) { - if( g->m.open_door( p, true, true ) ) { + for( const tripoint &p : here.points_in_radius( pos(), 30 ) ) { + if( here.open_door( p, true, true ) ) { ret.insert( p ); } } } if( rules.has_flag( ally_rule::hold_the_line ) ) { - for( const tripoint &p : g->m.points_in_radius( g->u.pos(), 1 ) ) { - if( g->m.close_door( p, true, true ) || g->m.move_cost( p ) > 2 ) { + for( const tripoint &p : here.points_in_radius( get_player_character().pos(), 1 ) ) { + if( here.close_door( p, true, true ) || here.move_cost( p ) > 2 ) { ret.insert( p ); } } @@ -3173,7 +3184,7 @@ void npc::set_attitude( npc_attitude new_attitude ) name, npc_attitude_id( attitude ), npc_attitude_id( new_attitude ) ); attitude_group new_group = get_attitude_group( new_attitude ); attitude_group old_group = get_attitude_group( attitude ); - if( new_group != old_group && !is_fake() && g->u.sees( *this ) ) { + if( new_group != old_group && !is_fake() && get_player_character().sees( *this ) ) { switch( new_group ) { case attitude_group::hostile: add_msg_if_npc( m_bad, _( " gets angry!" ) ); diff --git a/src/npc.h b/src/npc.h index 48136e010532d..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. */ @@ -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; diff --git a/src/npcmove.cpp b/src/npcmove.cpp index 8c5aaa03396f6..3e20cdbbfa31a 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" @@ -198,8 +197,6 @@ void print_action( const char *prepend, npc_action action ); bool compare_sound_alert( const dangerous_sound &sound_a, const dangerous_sound &sound_b ); -hp_part most_damaged_hp_part( const Character &c ); - bool compare_sound_alert( const dangerous_sound &sound_a, const dangerous_sound &sound_b ) { if( sound_a.type != sound_b.type ) { @@ -216,7 +213,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 +223,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 +231,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 +245,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 +275,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 +289,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 +308,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 +370,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 +383,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 +403,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 +436,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 +512,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 +565,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 +627,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 +665,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 +675,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 +712,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 +751,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 +770,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 +815,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 +834,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 +855,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 +887,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 +916,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 +941,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 +957,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 +966,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(); @@ -975,7 +983,7 @@ void npc::execute_action( npc_action action ) // Find a nice spot to sleep int best_sleepy = sleep_spot( pos() ); tripoint best_spot = pos(); - for( const tripoint &p : closest_tripoints_first( pos(), 6 ) ) { + for( const tripoint &p : closest_points_first( pos(), 6 ) ) { if( !could_move_onto( p ) || !g->is_empty( p ) ) { continue; } @@ -997,7 +1005,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 +1063,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 +1095,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 +1115,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 +1129,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 +1142,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 +1239,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 +1767,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 +1786,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 +1842,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 +1881,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 +1898,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 +1911,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 +1927,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 +1949,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 +2162,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 +2188,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 +2269,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 +2297,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 +2314,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 +2334,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 +2345,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 +2395,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 ); } } @@ -2452,7 +2465,7 @@ void npc::avoid_friendly_fire() center.y = std::round( center.y / friend_count ); center.z = std::round( center.z / friend_count ); - std::vector candidates = closest_tripoints_first( pos(), 1 ); + std::vector candidates = closest_points_first( pos(), 1 ); candidates.erase( candidates.begin() ); std::sort( candidates.begin(), candidates.end(), [&tar, ¢er]( const tripoint & l, const tripoint & r ) { @@ -2495,7 +2508,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 +2518,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 +2562,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 +2587,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 +2600,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 +2621,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,19 +2668,20 @@ 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; } // We need to path to adjacent tile, not the exact one // Let's pick the closest one to us that is passable - std::vector candidates = closest_tripoints_first( p, 1 ); + std::vector candidates = closest_points_first( p, 1 ); std::sort( candidates.begin(), candidates.end(), [ closest_to ]( const tripoint & l, 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 +2710,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 +2723,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 +2745,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 ) { - 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 ) ) { + map &here = get_map(); + for( const tripoint &p : closest_points_first( pos(), 6 ) ) { + 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 +2806,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 +2830,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; @@ -2829,7 +2850,7 @@ void npc::find_item() } }; - for( const tripoint &p : closest_tripoints_first( pos(), range ) ) { + for( const tripoint &p : closest_points_first( pos(), range ) ) { // TODO: Make this sight check not overdraw nearby tiles // TODO: Optimize that zone check if( is_player_ally() && g->check_zone( zone_type_no_npc_pickup, p ) ) { @@ -2839,9 +2860,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 +2879,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 +2956,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 +3002,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 +3089,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 +3158,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 +3204,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 +3221,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 +3277,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 +3312,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 +3455,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 +3570,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 +3597,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 +3639,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 +3662,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 +3696,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 +3716,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 +3818,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 +3837,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 +3892,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 +3904,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 +3971,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 +4014,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 +4028,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 +4063,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 +4072,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 +4099,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 +4169,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 +4213,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 +4238,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 +4437,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 +4457,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 +4559,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/npctalk.cpp b/src/npctalk.cpp index ec0a479a66436..53c2d0c457752 100644 --- a/src/npctalk.cpp +++ b/src/npctalk.cpp @@ -1995,13 +1995,18 @@ void talk_effect_fun_t::set_remove_trait( const JsonObject &jo, const std::strin void talk_effect_fun_t::set_add_var( const JsonObject &jo, const std::string &member, bool is_npc ) { const std::string var_name = get_talk_varname( jo, member ); - const std::string &value = jo.get_string( "value" ); - function = [is_npc, var_name, value]( const dialogue & d ) { + const bool time_check = jo.has_member( "time" ) && jo.get_bool( "time" ); + const std::string &value = time_check ? "" : jo.get_string( "value" ); + function = [is_npc, var_name, value, time_check]( const dialogue & d ) { player *actor = d.alpha; if( is_npc ) { actor = dynamic_cast( d.beta ); } - actor->set_value( var_name, value ); + if( time_check ) { + actor->set_value( var_name, string_format( "%d", to_turn( calendar::turn ) ) ); + } else { + actor->set_value( var_name, value ); + } }; } diff --git a/src/options.cpp b/src/options.cpp index 096a75d14e8bb..6419e39c0453a 100644 --- a/src/options.cpp +++ b/src/options.cpp @@ -2042,9 +2042,9 @@ void options_manager::add_options_world_default() 0.01, 10.0, 1.0, 0.01 ); - add( "NPC_DENSITY", "world_default", translate_marker( "NPC spawn rate scaling factor" ), - translate_marker( "A scaling factor that determines density of dynamic NPC spawns." ), - 0.0, 100.0, 0.1, 0.01 + add( "NPC_SPAWNTIME", "world_default", translate_marker( "Random NPC spawn time" ), + translate_marker( "Baseline average number of days between random NPC spawns. Average duration goes up with the number of NPCs already spawned. Set to 0 days to disable random NPCs." ), + 0.0, 100.0, 4.0, 0.01 ); add( "MONSTER_UPGRADE_FACTOR", "world_default", @@ -2118,26 +2118,6 @@ void options_manager::add_options_world_default() add_empty_line(); - add( "STATIC_NPC", "world_default", translate_marker( "Static NPCs" ), - translate_marker( "If true, static NPCs will spawn at pre-defined locations. Requires world reset." ), - true - ); - - add( "STARTING_NPC", "world_default", translate_marker( "Starting NPCs spawn" ), - translate_marker( "Determines whether starting NPCs should spawn, and if they do, how exactly." ), - { { "never", translate_marker( "Never" ) }, { "always", translate_marker( "Always" ) }, { "scenario", translate_marker( "Scenario-based" ) } }, - "scenario" - ); - - get_option( "STARTING_NPC" ).setPrerequisite( "STATIC_NPC" ); - - add( "RANDOM_NPC", "world_default", translate_marker( "Random NPCs" ), - translate_marker( "If true, the game will randomly spawn NPCs during gameplay." ), - false - ); - - add_empty_line(); - add( "RAD_MUTATION", "world_default", translate_marker( "Mutations by radiation" ), translate_marker( "If true, radiation causes the player to mutate." ), true diff --git a/src/overmap.cpp b/src/overmap.cpp index d64ef6cddd869..7a40629e1c466 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,77 @@ 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 ); + } + } + } + } + + generate_bridgeheads( bridge_points ); + + return requires_over; +} + +void overmap::generate_bridgeheads( const std::vector &bridge_points ) +{ + 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 ) ); + } +} + std::vector overmap::find_terrain( const std::string &term, int zlevel ) { std::vector found; @@ -2244,7 +2323,7 @@ void overmap::place_forest_trailheads() const auto trailhead_close_to_road = [&]( const tripoint & trailhead ) { bool close = false; - for( const tripoint &nearby_point : closest_tripoints_first( + for( const tripoint &nearby_point : closest_points_first( trailhead, settings.forest_trail.trailhead_road_distance ) ) { diff --git a/src/overmap.h b/src/overmap.h index c77e5e716d540..48a97240263a4 100644 --- a/src/overmap.h +++ b/src/overmap.h @@ -387,6 +387,9 @@ class overmap const overmap *south, const overmap *west, overmap_special_batch &enabled_specials ); bool generate_sub( int z ); + bool generate_over( int z ); + // Check and put bridgeheads + void generate_bridgeheads( const std::vector &bridge_points ); const city &get_nearest_city( const tripoint &p ) const; diff --git a/src/overmap_ui.cpp b/src/overmap_ui.cpp index 8468e86d9a50b..40bcaba432dae 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; @@ -197,10 +197,10 @@ static void update_note_preview( const std::string ¬e, wnoutrefresh( *w_preview_map ); } -static weather_type get_weather_at_point( const tripoint &pos ) +static weather_type_id get_weather_at_point( const tripoint &pos ) { // Weather calculation is a bit expensive, so it's cached here. - static std::map weather_cache; + static std::map weather_cache; static time_point last_weather_display = calendar::before_time_starts; if( last_weather_display != calendar::turn ) { last_weather_display = calendar::turn; @@ -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,12 +684,12 @@ 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 ); - ter_color = weather::map_color( type ); - ter_sym = weather::glyph( type ); + const weather_type_id type = get_weather_at_point( omp ); + ter_color = type->map_color; + ter_sym = type->glyph; } else if( data.debug_scent && get_scent_glyph( omp, ter_color, ter_sym ) ) { // get_scent_glyph has changed ter_color and ter_sym if omp has a scent } else if( blink && has_target && omp.xy() == target.xy() ) { @@ -968,11 +969,11 @@ 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) - mvwprintz( wbar, point( 1, 1 ), weather.color, weather.name ); + mvwprintz( wbar, point( 1, 1 ), get_weather_at_point( center )->color, + get_weather_at_point( center )->name ); } else { // NOLINTNEXTLINE(cata-use-named-point-constants) mvwprintz( wbar, point( 1, 1 ), c_dark_gray, _( "# Unexplored" ) ); @@ -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 ); } diff --git a/src/overmapbuffer.cpp b/src/overmapbuffer.cpp index d48aa6cfb6225..6e9aef594e765 100644 --- a/src/overmapbuffer.cpp +++ b/src/overmapbuffer.cpp @@ -994,7 +994,7 @@ std::vector overmapbuffer::find_all( const tripoint &origin, const int min_dist = params.min_distance; const int max_dist = params.search_range ? params.search_range : OMAPX; - for( const tripoint &loc : closest_tripoints_first( origin, min_dist, max_dist ) ) { + for( const tripoint &loc : closest_points_first( origin, min_dist, max_dist ) ) { if( is_findable_location( loc, params ) ) { result.push_back( loc ); } diff --git a/src/panels.cpp b/src/panels.cpp index 2764f9cd11e73..532566d129a75 100644 --- a/src/panels.cpp +++ b/src/panels.cpp @@ -863,7 +863,7 @@ static int get_int_digits( const int &digits ) // panels code // =============================== -static void draw_limb_health( avatar &u, const catacurses::window &w, int limb_index ) +static void draw_limb_health( avatar &u, const catacurses::window &w, bodypart_id bp ) { const bool no_feeling = u.has_trait( trait_NOPAIN ); const bool is_self_aware = u.has_trait( trait_SELFAWARE ) && !no_feeling; @@ -873,8 +873,8 @@ static void draw_limb_health( avatar &u, const catacurses::window &w, int limb_i wprintz( w, color, sym ); } }; - const bodypart_id bp = convert_bp( avatar::hp_to_bp( static_cast( limb_index ) ) ).id(); - if( u.is_limb_broken( bp ) && ( limb_index >= hp_arm_l && limb_index <= hp_leg_r ) ) { + + if( u.is_limb_broken( bp ) && bp->is_limb ) { //Limb is broken std::string limb = "~~%~~"; nc_color color = c_light_red; @@ -924,27 +924,24 @@ static void draw_limb_health( avatar &u, const catacurses::window &w, int limb_i static void draw_limb2( avatar &u, const catacurses::window &w ) { - static std::array part = { { - bodypart_id( "head" ), bodypart_id( "torso" ), bodypart_id( "arm_l" ), bodypart_id( "arm_r" ), bodypart_id( "leg_l" ), bodypart_id( "leg_r" ) - } - }; - werase( w ); - // print limb health - for( int i = 0; i < num_hp_parts; i++ ) { - const std::string str = body_part_hp_bar_ui_text( part[i] ); + // print bodypart health + int i = 0; + for( const bodypart_id &bp : u.get_all_body_parts( true ) ) { + const std::string str = body_part_hp_bar_ui_text( bp ); if( i % 2 == 0 ) { wmove( w, point( 0, i / 2 ) ); } else { wmove( w, point( 11, i / 2 ) ); } - wprintz( w, u.limb_color( part[i], true, true, true ), str ); + wprintz( w, u.limb_color( bp, true, true, true ), str ); if( i % 2 == 0 ) { wmove( w, point( 5, i / 2 ) ); } else { wmove( w, point( 16, i / 2 ) ); } - draw_limb_health( u, w, i ); + draw_limb_health( u, w, bp ); + i++; } // print mood @@ -1125,7 +1122,8 @@ static void draw_limb_narrow( avatar &u, const catacurses::window &w ) { werase( w ); int ny2 = 0; - for( int i = 0; i < num_hp_parts; i++ ) { + int i = 0; + for( const bodypart_id &bp : u.get_all_body_parts( true ) ) { int ny; int nx; if( i < 3 ) { @@ -1136,16 +1134,14 @@ static void draw_limb_narrow( avatar &u, const catacurses::window &w ) nx = 26; } wmove( w, point( nx, ny ) ); - draw_limb_health( u, w, i ); + draw_limb_health( u, w, bp ); + i++; } // display limbs status - static std::array part = { { - bodypart_id( "head" ), bodypart_id( "torso" ), bodypart_id( "arm_l" ), bodypart_id( "arm_r" ), bodypart_id( "leg_l" ), bodypart_id( "leg_r" ) - } - }; ny2 = 0; - for( size_t i = 0; i < part.size(); i++ ) { + i = 0; + for( const bodypart_id &bp : u.get_all_body_parts( true ) ) { int ny; int nx; if( i < 3 ) { @@ -1156,34 +1152,29 @@ static void draw_limb_narrow( avatar &u, const catacurses::window &w ) nx = 19; } - std::string str = body_part_hp_bar_ui_text( part[i] ); + std::string str = body_part_hp_bar_ui_text( bp ); wmove( w, point( nx, ny ) ); str = left_justify( str, 5 ); - wprintz( w, u.limb_color( part[i], true, true, true ), str + ":" ); + wprintz( w, u.limb_color( bp, true, true, true ), str + ":" ); + i++; } wnoutrefresh( w ); } static void draw_limb_wide( avatar &u, const catacurses::window &w ) { - const std::vector> parts = { - {bodypart_id( "arm_l" ), 2}, - {bodypart_id( "head" ), 0}, - {bodypart_id( "arm_r" ), 3}, - {bodypart_id( "leg_l" ), 4}, - {bodypart_id( "torso" ), 1}, - {bodypart_id( "leg_r" ), 5} - }; werase( w ); - for( int i = 0; i < num_hp_parts; i++ ) { + int i = 0; + for( const bodypart_id &bp : u.get_all_body_parts( true ) ) { int offset = i * 15; int ny = offset / 45; int nx = offset % 45; std::string str = string_format( " %s: ", - left_justify( body_part_hp_bar_ui_text( parts[i].first ), 5 ) ); - nc_color part_color = u.limb_color( parts[i].first, true, true, true ); + left_justify( body_part_hp_bar_ui_text( bp ), 5 ) ); + nc_color part_color = u.limb_color( bp, true, true, true ); print_colored_text( w, point( nx, ny ), part_color, c_white, str ); - draw_limb_health( u, w, parts[i].second ); + draw_limb_health( u, w, bp ); + i++; } wnoutrefresh( w ); } @@ -1335,8 +1326,7 @@ static void draw_loc_labels( const avatar &u, const catacurses::window &w, bool } else { // NOLINTNEXTLINE(cata-use-named-point-constants) mvwprintz( w, point( 1, 1 ), c_light_gray, _( "Sky :" ) ); - const weather_datum wdata = weather_data( get_weather().weather ); - wprintz( w, wdata.color, " %s", wdata.name ); + wprintz( w, get_weather().weather_id->color, " %s", get_weather().weather_id->name ); } // display lighting const std::pair ll = get_light_level( @@ -1502,8 +1492,7 @@ static void draw_env_compact( avatar &u, const catacurses::window &w ) if( g->get_levz() < 0 ) { mvwprintz( w, point( 8, 3 ), c_light_gray, _( "Underground" ) ); } else { - const weather_datum wdata = weather_data( g->weather.weather ); - mvwprintz( w, point( 8, 3 ), wdata.color, wdata.name ); + mvwprintz( w, point( 8, 3 ), get_weather().weather_id->color, get_weather().weather_id->name ); } // display lighting const std::pair ll = get_light_level( @@ -1550,11 +1539,6 @@ static void draw_wind_padding( avatar &u, const catacurses::window &w ) static void draw_health_classic( avatar &u, const catacurses::window &w ) { - static std::array part = { { - bodypart_id( "head" ), bodypart_id( "torso" ), bodypart_id( "arm_l" ), bodypart_id( "arm_r" ), bodypart_id( "leg_l" ), bodypart_id( "leg_r" ) - } - }; - vehicle *veh = g->remoteveh(); if( veh == nullptr && u.in_vehicle ) { veh = veh_pointer_or_null( get_map().veh_at( u.pos() ) ); @@ -1566,12 +1550,14 @@ static void draw_health_classic( avatar &u, const catacurses::window &w ) draw_rectangle( w, c_light_gray, point_zero, point( 6, 6 ) ); // print limb health - for( int i = 0; i < num_hp_parts; i++ ) { - const std::string str = body_part_hp_bar_ui_text( part[i] ); + int i = 0; + for( const bodypart_id &bp : u.get_all_body_parts( true ) ) { + const std::string str = body_part_hp_bar_ui_text( bp ); wmove( w, point( 8, i ) ); - wprintz( w, u.limb_color( part[i], true, true, true ), str ); + wprintz( w, u.limb_color( bp, true, true, true ), str ); wmove( w, point( 14, i ) ); - draw_limb_health( u, w, i ); + draw_limb_health( u, w, bp ); + i++; } // needs @@ -1849,9 +1835,8 @@ static void draw_weather_classic( avatar &, const catacurses::window &w ) if( g->get_levz() < 0 ) { mvwprintz( w, point_zero, c_light_gray, _( "Underground" ) ); } else { - const weather_datum wdata = weather_data( get_weather().weather ); mvwprintz( w, point_zero, c_light_gray, _( "Weather :" ) ); - mvwprintz( w, point( 10, 0 ), wdata.color, wdata.name ); + mvwprintz( w, point( 10, 0 ), get_weather().weather_id->color, get_weather().weather_id->name ); } mvwprintz( w, point( 31, 0 ), c_light_gray, _( "Moon :" ) ); nc_color clr = c_white; 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/pickup.cpp b/src/pickup.cpp index 417b46006f33f..6285b167ae9df 100644 --- a/src/pickup.cpp +++ b/src/pickup.cpp @@ -231,9 +231,6 @@ bool pick_one_up( item_location &loc, int quantity, bool &got_water, bool &offer item &it = *newloc.get_item(); //new item (copy) item newit = it; - item leftovers = newit; - - const auto wield_check = u.can_wield( newit ); if( !newit.is_owned_by( u, true ) ) { // Has the player given input on if stealing is ok? @@ -253,12 +250,9 @@ bool pick_one_up( item_location &loc, int quantity, bool &got_water, bool &offer // Handle charges, quantity == 0 means move all if( quantity != 0 && newit.count_by_charges() ) { - leftovers.charges = newit.charges - quantity; - if( leftovers.charges > 0 ) { + if( newit.charges > quantity ) { newit.charges = quantity; } - } else { - leftovers.charges = 0; } bool did_prompt = false; @@ -309,7 +303,8 @@ bool pick_one_up( item_location &loc, int quantity, bool &got_water, bool &offer case WEAR: picked_up = !!u.wear_item( newit ); break; - case WIELD: + case WIELD: { + const auto wield_check = u.can_wield( it ); if( wield_check.success() ) { //using original item, possibly modifying it picked_up = u.wield( it ); @@ -326,6 +321,7 @@ bool pick_one_up( item_location &loc, int quantity, bool &got_water, bool &offer add_msg( m_neutral, wield_check.c_str() ); } break; + } case SPILL: if( newit.is_container_empty() ) { debugmsg( "Tried to spill contents from an empty container" ); @@ -356,10 +352,12 @@ bool pick_one_up( item_location &loc, int quantity, bool &got_water, bool &offer } if( picked_up ) { - // If we picked up a whole stack, remove the original item - // Otherwise, replace the item with the leftovers - if( leftovers.charges > 0 ) { - *loc.get_item() = std::move( leftovers ); + item &orig_it = *loc.get_item(); + // Subtract moved charges instead of assigning leftover charges, + // since the total charges of the original item may have changed + // due to merging. + if( orig_it.charges > newit.charges ) { + orig_it.charges -= newit.charges; } else { loc.remove_item(); } diff --git a/src/player.cpp b/src/player.cpp index 643ff5d2c31a3..e39168c1ca269 100644 --- a/src/player.cpp +++ b/src/player.cpp @@ -278,7 +278,7 @@ player::player() } recalc_sight_limits(); - reset_encumbrance(); + calc_encumbrance(); } player::~player() = default; @@ -558,7 +558,7 @@ double player::recoil_vehicle() const // TODO: vary penalty dependent upon vehicle part on which player is boarded if( in_vehicle ) { - if( const optional_vpart_position vp = g->m.veh_at( pos() ) ) { + if( const optional_vpart_position vp = get_map().veh_at( pos() ) ) { return static_cast( std::abs( vp->vehicle().velocity ) ) * 3 / 100; } } @@ -751,6 +751,7 @@ void player::pause() moves = 0; recoil = MAX_RECOIL; + map &here = get_map(); // Train swimming if underwater if( !in_vehicle ) { if( underwater ) { @@ -761,7 +762,7 @@ void player::pause() bodypart_str_id( "foot_l" ), bodypart_str_id( "foot_r" ), bodypart_str_id( "hand_l" ), bodypart_str_id( "hand_r" ) } }, true ); - } else if( g->m.has_flag( TFLAG_DEEP_WATER, pos() ) ) { + } else if( here.has_flag( TFLAG_DEEP_WATER, pos() ) ) { practice( skill_swimming, 1 ); // Same as above, except no head/eyes/mouth drench( 100, { { @@ -770,7 +771,7 @@ void player::pause() bodypart_str_id( "hand_r" ) } }, true ); - } else if( g->m.has_flag( "SWIMMABLE", pos() ) ) { + } else if( here.has_flag( "SWIMMABLE", pos() ) ) { drench( 40, { { bodypart_str_id( "foot_l" ), bodypart_str_id( "foot_r" ), bodypart_str_id( "leg_l" ), bodypart_str_id( "leg_r" ) } }, false ); } @@ -796,7 +797,7 @@ void player::pause() } // Don't drop on the ground when the ground is on fire - if( total_left > 1_minutes && !is_dangerous_fields( g->m.field_at( pos() ) ) ) { + if( total_left > 1_minutes && !is_dangerous_fields( here.field_at( pos() ) ) ) { add_effect( effect_downed, 2_turns, num_bp, false, 0, true ); add_msg_player_or_npc( m_warning, _( "You roll on the ground, trying to smother the fire!" ), @@ -818,7 +819,7 @@ void player::pause() } if( in_vehicle && one_in( 8 ) ) { - VehicleList vehs = g->m.get_vehicles(); + VehicleList vehs = here.get_vehicles(); vehicle *veh = nullptr; for( auto &v : vehs ) { veh = v.v; @@ -844,17 +845,16 @@ void player::search_surroundings() if( controlling_vehicle ) { return; } + map &here = get_map(); // Search for traps in a larger area than before because this is the only // way we can "find" traps that aren't marked as visible. // Detection formula takes care of likelihood of seeing within this range. - for( const tripoint &tp : g->m.points_in_radius( pos(), 5 ) ) { - const trap &tr = g->m.tr_at( tp ); + for( const tripoint &tp : here.points_in_radius( pos(), 5 ) ) { + const trap &tr = here.tr_at( tp ); 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 ); @@ -863,13 +863,13 @@ 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; } // 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 ) ); @@ -929,7 +929,7 @@ void player::on_hit( Creature *source, bodypart_id bp_hit, return; } - bool u_see = g->u.sees( *this ); + bool u_see = get_player_character().sees( *this ); if( has_active_bionic( bionic_id( "bio_ods" ) ) && get_power_level() > 5_kJ ) { if( is_player() ) { add_msg( m_good, _( "Your offensive defense system shocks %s in mid-attack!" ), @@ -1119,8 +1119,9 @@ float player::fall_damage_mod() const /** @EFFECT_DODGE decreases damage from falling */ float dex_dodge = dex_cur / 2.0 + get_skill_level( skill_dodge ); // Penalize for wearing heavy stuff - const float average_leg_encumb = ( encumb( bp_leg_l ) + encumb( bp_leg_r ) ) / 2.0; - dex_dodge -= ( average_leg_encumb + encumb( bp_torso ) ) / 10; + const float average_leg_encumb = ( encumb( bodypart_id( "leg_l" ) ) + encumb( + bodypart_id( "leg_r" ) ) ) / 2.0; + dex_dodge -= ( average_leg_encumb + encumb( bodypart_id( "torso" ) ) ) / 10; // But prevent it from increasing damage dex_dodge = std::max( 0.0f, dex_dodge ); // 100% damage at 0, 75% at 10, 50% at 20 and so on @@ -1160,6 +1161,7 @@ int player::impact( const int force, const tripoint &p ) const bool slam = p != pos(); std::string target_name = "a swarm of bugs"; Creature *critter = g->critter_at( p ); + map &here = get_map(); if( critter != this && critter != nullptr ) { target_name = critter->disp_name(); // Slamming into creatures and NPCs @@ -1168,7 +1170,7 @@ int player::impact( const int force, const tripoint &p ) // TODO: Modify based on something? mod = 1.0f; effective_force = force; - } else if( const optional_vpart_position vp = g->m.veh_at( p ) ) { + } else if( const optional_vpart_position vp = here.veh_at( p ) ) { // Slamming into vehicles // TODO: Integrate it with vehicle collision function somehow target_name = vp->vehicle().disp_name(); @@ -1186,20 +1188,20 @@ int player::impact( const int force, const tripoint &p ) } } else { // Slamming into terrain/furniture - target_name = g->m.disp_name( p ); - int hard_ground = g->m.has_flag( TFLAG_DIGGABLE, p ) ? 0 : 3; + target_name = here.disp_name( p ); + int hard_ground = here.has_flag( TFLAG_DIGGABLE, p ) ? 0 : 3; armor_eff = 0.25f; // Not much // Get cut by stuff // This isn't impalement on metal wreckage, more like flying through a closed window - cut = g->m.has_flag( TFLAG_SHARP, p ) ? 5 : 0; + cut = here.has_flag( TFLAG_SHARP, p ) ? 5 : 0; effective_force = force + hard_ground; mod = slam ? 1.0f : fall_damage_mod(); - if( g->m.has_furn( p ) ) { + if( here.has_furn( p ) ) { // TODO: Make furniture matter - } else if( g->m.has_flag( TFLAG_SWIMMABLE, p ) ) { + } else if( here.has_flag( TFLAG_SWIMMABLE, p ) ) { const int swim_skill = get_skill_level( skill_swimming ); effective_force /= 4.0f + 0.1f * swim_skill; - if( g->m.has_flag( TFLAG_DEEP_WATER, p ) ) { + if( here.has_flag( TFLAG_DEEP_WATER, p ) ) { effective_force /= 1.5f; mod /= 1.0f + ( 0.1f * swim_skill ); } @@ -1314,20 +1316,21 @@ void player::knock_back_to( const tripoint &to ) return; } + map &here = get_map(); // If we're still in the function at this point, we're actually moving a tile! - if( g->m.has_flag( "LIQUID", to ) && g->m.has_flag( TFLAG_DEEP_WATER, to ) ) { + if( here.has_flag( "LIQUID", to ) && here.has_flag( TFLAG_DEEP_WATER, to ) ) { if( !is_npc() ) { - avatar_action::swim( g->m, g->u, to ); + avatar_action::swim( here, get_avatar(), to ); } // TODO: NPCs can't swim! - } else if( g->m.impassable( to ) ) { // Wait, it's a wall + } else if( here.impassable( to ) ) { // Wait, it's a wall // It's some kind of wall. // TODO: who knocked us back? Maybe that creature should be the source of the damage? apply_damage( nullptr, bodypart_id( "torso" ), 3 ); add_effect( effect_stunned, 2_turns ); add_msg_player_or_npc( _( "You bounce off a %s!" ), _( " bounces off a %s!" ), - g->m.obstacle_name( to ) ); + here.obstacle_name( to ) ); } else { // It's no wall setpos( to ); @@ -1831,7 +1834,7 @@ void player::process_items() w.encumbrance_update_ = false; } if( update_required ) { - reset_encumbrance(); + calc_encumbrance(); } if( has_active_bionic( bionic_id( "bio_ups" ) ) ) { ch_UPS += units::to_kilojoule( get_power_level() ); @@ -2036,13 +2039,14 @@ item::reload_option player::select_ammo( const item &base, std::transform( opts.begin(), opts.end(), std::back_inserter( where ), [this]( const item::reload_option & e ) { bool is_ammo_container = e.ammo->is_ammo_container(); + Character &player_character = get_player_character(); if( is_ammo_container || e.ammo->is_container() ) { if( is_ammo_container && is_worn( *e.ammo ) ) { return e.ammo->type_name(); } - return string_format( _( "%s, %s" ), e.ammo->type_name(), e.ammo.describe( &g->u ) ); + return string_format( _( "%s, %s" ), e.ammo->type_name(), e.ammo.describe( &player_character ) ); } - return e.ammo.describe( &g->u ); + return e.ammo.describe( &player_character ); } ); // Pads elements to match longest member and return length @@ -2790,7 +2794,7 @@ bool player::takeoff( item &it, std::list *res ) mod_moves( -250 ); recalc_sight_limits(); - reset_encumbrance(); + calc_encumbrance(); return true; } @@ -3429,10 +3433,11 @@ recipe_subset player::get_available_recipes( const inventory &crafting_inv, void player::try_to_sleep( const time_duration &dur ) { - const optional_vpart_position vp = g->m.veh_at( pos() ); - const trap &trap_at_pos = g->m.tr_at( pos() ); - const ter_id ter_at_pos = g->m.ter( pos() ); - const furn_id furn_at_pos = g->m.furn( pos() ); + map &here = get_map(); + const optional_vpart_position vp = here.veh_at( pos() ); + const trap &trap_at_pos = here.tr_at( pos() ); + const ter_id ter_at_pos = here.ter( pos() ); + const furn_id furn_at_pos = here.furn( pos() ); bool plantsleep = false; bool fungaloid_cosplay = false; bool websleep = false; @@ -3457,7 +3462,7 @@ void player::try_to_sleep( const time_duration &dur ) } } else if( has_trait( trait_M_SKIN3 ) ) { fungaloid_cosplay = true; - if( g->m.has_flag_ter_or_furn( "FUNGUS", pos() ) ) { + if( here.has_flag_ter_or_furn( "FUNGUS", pos() ) ) { add_msg_if_player( m_good, _( "Our fibers meld with the ground beneath us. The gills on our neck begin to seed the air with spores as our awareness fades." ) ); } @@ -3471,7 +3476,7 @@ void player::try_to_sleep( const time_duration &dur ) webforce = true; } if( websleep || webforce ) { - int web = g->m.get_field_intensity( pos(), fd_web ); + int web = here.get_field_intensity( pos(), fd_web ); if( !webforce ) { // At this point, it's kinda weird, but surprisingly comfy... if( web >= 3 ) { @@ -3481,7 +3486,7 @@ void player::try_to_sleep( const time_duration &dur ) } else if( web > 0 ) { add_msg_if_player( m_info, _( "You try to sleep, but the webs get in the way. You brush them aside." ) ); - g->m.remove_field( pos(), fd_web ); + here.remove_field( pos(), fd_web ); } } else { // Here, you're just not comfortable outside a nice thick web. @@ -3504,7 +3509,7 @@ void player::try_to_sleep( const time_duration &dur ) add_msg_if_player( m_good, _( "You lay beneath the waves' embrace, gazing up through the water's surface…" ) ); watersleep = true; - } else if( g->m.has_flag_ter( "SWIMMABLE", pos() ) ) { + } else if( here.has_flag_ter( "SWIMMABLE", pos() ) ) { add_msg_if_player( m_good, _( "You settle into the water and begin to drowse…" ) ); watersleep = true; } @@ -3580,7 +3585,7 @@ int player::sleep_spot( const tripoint &p ) const // Mousefolk can sleep just about anywhere. sleepy += 40; } - if( watersleep && g->m.has_flag_ter( "SWIMMABLE", pos() ) ) { + if( watersleep && get_map().has_flag_ter( "SWIMMABLE", pos() ) ) { sleepy += 10; //comfy water! } @@ -3661,7 +3666,7 @@ float player::fine_detail_vision_mod( const tripoint &p ) const // Same calculation as above, but with a result 3 lower. float ambient_light = std::max( 1.0f, - LIGHT_AMBIENT_LIT - g->m.ambient_light_at( p == tripoint_zero ? pos() : p ) + 1.0f ); + LIGHT_AMBIENT_LIT - get_map().ambient_light_at( p == tripoint_zero ? pos() : p ) + 1.0f ); return std::min( own_light, ambient_light ); } @@ -3934,7 +3939,7 @@ void player::store( item &container, item &put, bool penalties, int base_cost, { moves -= item_store_cost( put, container, penalties, base_cost ); container.put_in( i_rem( &put ), pk_type ); - reset_encumbrance(); + calc_encumbrance(); } nc_color encumb_color( int level ) @@ -3961,8 +3966,8 @@ float player::get_melee() const bool player::uncanny_dodge() { - bool is_u = this == &g->u; - bool seen = g->u.sees( *this ); + bool is_u = is_avatar();; + bool seen = get_player_character().sees( *this ); if( this->get_power_level() < 74_kJ || !this->has_active_bionic( bio_uncanny_dodge ) ) { return false; } @@ -3989,11 +3994,12 @@ bool player::uncanny_dodge() int player::climbing_cost( const tripoint &from, const tripoint &to ) const { - if( !g->m.valid_move( from, to, false, true ) ) { + map &here = get_map(); + if( !here.valid_move( from, to, false, true ) ) { return 0; } - const int diff = g->m.climb_difficulty( from ); + const int diff = here.climb_difficulty( from ); if( diff > 5 ) { return 0; @@ -4020,7 +4026,7 @@ void player::environmental_revert_effect() set_rad( 0 ); recalc_sight_limits(); - reset_encumbrance(); + calc_encumbrance(); } //message related stuff 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; diff --git a/src/player_display.cpp b/src/player_display.cpp index e3557d751c564..2007f81aca275 100644 --- a/src/player_display.cpp +++ b/src/player_display.cpp @@ -48,35 +48,37 @@ static int temperature_print_rescaling( int temp ) return ( temp / 100.0 ) * 2 - 100; } -static body_part other_part( body_part bp ) +static bodypart_id other_part( const bodypart_id &bp ) { - return static_cast( bp_aiOther[bp] ); + return bp->opposite_part; } -static bool should_combine_bps( const player &p, body_part l, body_part r, +static bool should_combine_bps( const player &p, const bodypart_id &l, const bodypart_id &r, const item *selected_clothing ) { - const auto enc_data = p.get_encumbrance(); + const encumbrance_data enc_l = p.get_part_encumbrance_data( l ); + const encumbrance_data enc_r = p.get_part_encumbrance_data( r ); + return l != r && // are different parts l == other_part( r ) && r == other_part( l ) && // are complementary parts // same encumberance & temperature - enc_data[l] == enc_data[r] && - temperature_print_rescaling( p.temp_conv[l] ) == temperature_print_rescaling( p.temp_conv[r] ) && + enc_l == enc_r && + temperature_print_rescaling( p.temp_conv[l->token] ) == temperature_print_rescaling( + p.temp_conv[r->token] ) && // selected_clothing covers both or neither parts ( !selected_clothing || - ( selected_clothing->covers( convert_bp( l ).id() ) == selected_clothing->covers( convert_bp( - r ).id() ) ) ); + ( selected_clothing->covers( l ) == selected_clothing->covers( r ) ) ); } -static std::vector> list_and_combine_bps( const player &p, +static std::vector> list_and_combine_bps( const player &p, const item *selected_clothing ) { // bool represents whether the part has been combined with its other half - std::vector> bps; - for( auto bp : all_body_parts ) { + std::vector> bps; + for( const bodypart_id &bp : p.get_all_body_parts() ) { // assuming that a body part has at most one other half if( other_part( other_part( bp ) ) != bp ) { - debugmsg( "Bodypart %d has more than one other half!", bp ); + debugmsg( "Bodypart %d has more than one other half!", bp.id().c_str() ); } if( should_combine_bps( p, bp, other_part( bp ), selected_clothing ) ) { if( bp < other_part( bp ) ) { @@ -94,7 +96,7 @@ void player::print_encumbrance( const catacurses::window &win, const int line, const item *const selected_clothing ) const { // bool represents whether the part has been combined with its other half - const std::vector> bps = list_and_combine_bps( *this, + const std::vector> bps = list_and_combine_bps( *this, selected_clothing ); // width/height excluding title & scrollbar @@ -109,7 +111,6 @@ void player::print_encumbrance( const catacurses::window &win, const int line, *** for displaying triple digit encumbrance, due to new encumbrance system. *** *** If the player wants to see the total without having to do them maths, the *** *** armor layers ui shows everything they want :-) -Davek ***/ - const auto enc_data = get_encumbrance(); for( int i = 0; i < height; ++i ) { int thisline = firstline + i; if( thisline < 0 ) { @@ -118,12 +119,11 @@ void player::print_encumbrance( const catacurses::window &win, const int line, if( static_cast( thisline ) >= bps.size() ) { break; } - const body_part bp = bps[thisline].first; + const bodypart_id bp = bps[thisline].first; const bool combine = bps[thisline].second; - const encumbrance_data &e = enc_data[bp]; - const bool highlighted = selected_clothing ? selected_clothing->covers( convert_bp( - bp ).id() ) : false; - std::string out = body_part_name_as_heading( convert_bp( bp ).id(), combine ? 2 : 1 ); + const encumbrance_data &e = get_part_encumbrance_data( bp ); + const bool highlighted = selected_clothing ? selected_clothing->covers( bp ) : false; + std::string out = body_part_name_as_heading( bp, combine ? 2 : 1 ); if( utf8_width( out ) > 7 ) { out = utf8_truncate( out, 7 ); } @@ -142,8 +142,8 @@ void player::print_encumbrance( const catacurses::window &win, const int line, // take into account the new encumbrance system for layers mvwprintz( win, point( 12, 1 + i ), encumb_color( e.encumbrance ), "%-3d", e.layer_penalty ); // print warmth, tethered to right hand side of the window - mvwprintz( win, point( width - 6, 1 + i ), bodytemp_color( bp ), "(% 3d)", - temperature_print_rescaling( temp_conv[bp] ) ); + mvwprintz( win, point( width - 6, 1 + i ), bodytemp_color( bp->token ), "(% 3d)", + temperature_print_rescaling( temp_conv[bp->token] ) ); } if( draw_scrollbar ) { @@ -196,21 +196,22 @@ static std::string dodge_skill_text( double mod ) return string_format( _( "Dodge skill: %+.1f\n" ), mod ); } -static int get_encumbrance( const player &p, body_part bp, bool combine ) +static int get_encumbrance( const player &p, const bodypart_id &bp, bool combine ) { // Body parts that can't combine with anything shouldn't print double values on combine // This shouldn't happen, but handle this, just in case - const bool combines_with_other = static_cast( bp_aiOther[bp] ) != bp; + const bool combines_with_other = bp->opposite_part != bp.id(); return p.encumb( bp ) * ( ( combine && combines_with_other ) ? 2 : 1 ); } -static std::string get_encumbrance_description( const player &p, body_part bp, bool combine ) +static std::string get_encumbrance_description( const player &p, const bodypart_id bp, + bool combine ) { std::string s; const int eff_encumbrance = get_encumbrance( p, bp, combine ); - switch( bp ) { + switch( bp->token ) { case bp_torso: { const int melee_roll_pen = std::max( -eff_encumbrance, -80 ); s += string_format( _( "Melee attack rolls: %+d%%\n" ), melee_roll_pen ); @@ -478,10 +479,10 @@ static void draw_encumbrance_tab( const catacurses::window &w_encumb, static void draw_encumbrance_info( const catacurses::window &w_info, const player &you, const unsigned int line ) { - const std::vector> bps = list_and_combine_bps( you, nullptr ); + const std::vector> bps = list_and_combine_bps( you, nullptr ); werase( w_info ); - body_part bp = num_bp; + bodypart_id bp; bool combined_here = false; if( line < bps.size() ) { bp = bps[line].first; @@ -985,7 +986,7 @@ static bool handle_player_display_action( player &you, unsigned int &line, line_end = 8; break; case player_display_tab::encumbrance: { - const std::vector> bps = list_and_combine_bps( you, nullptr ); + const std::vector> bps = list_and_combine_bps( you, nullptr ); line_end = bps.size(); break; } @@ -1137,10 +1138,8 @@ void player::disp_info() effect_name_and_text.push_back( { starvation_name, starvation_text } ); } - if( ( has_trait( trait_id( "TROGLO" ) ) && g->is_in_sunlight( pos() ) && - g->weather.weather == WEATHER_SUNNY ) || - ( has_trait( trait_id( "TROGLO2" ) ) && g->is_in_sunlight( pos() ) && - g->weather.weather != WEATHER_SUNNY ) ) { + if( has_trait( trait_id( "TROGLO" ) ) && g->is_in_sunlight( pos() ) && + get_weather().weather_id->sun_intensity >= sun_intensity_type::high ) { effect_name_and_text.push_back( { _( "In Sunlight" ), _( "The sunlight irritates you.\n" "Strength - 1; Dexterity - 1; Intelligence - 1; Perception - 1" ) diff --git a/src/player_hardcoded_effects.cpp b/src/player_hardcoded_effects.cpp index f3bb49fccfbb7..f7ca3cfcf9358 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(); @@ -1068,18 +1071,13 @@ void player::hardcoded_effects( effect &it ) } // TODO: Move this to update_needs when NPCs can mutate - bool compatible_weather_types = g->weather.weather == WEATHER_CLEAR || - g->weather.weather == WEATHER_SUNNY - || g->weather.weather == WEATHER_DRIZZLE || g->weather.weather == WEATHER_RAINY || - g->weather.weather == WEATHER_FLURRIES - || g->weather.weather == WEATHER_CLOUDY || g->weather.weather == WEATHER_SNOW; - 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 ) { + if( g->natural_light_level( posz() ) >= 12 && + get_weather().weather_id->sun_intensity >= sun_intensity_type::light ) { if( get_hunger() >= -30 ) { mod_hunger( -5 ); // photosynthesis warrants absorbing kcal directly @@ -1092,7 +1090,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 +1148,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 +1157,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 +1220,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 +1293,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/pldata.h b/src/pldata.h index e6012f5b35fcc..ffd2ec0294ca2 100644 --- a/src/pldata.h +++ b/src/pldata.h @@ -41,21 +41,6 @@ struct enum_traits { static constexpr add_type last = add_type::NUM_ADD_TYPES; }; -enum hp_part : int { - hp_head = 0, - hp_torso, - hp_arm_l, - hp_arm_r, - hp_leg_l, - hp_leg_r, - num_hp_parts -}; - -template<> -struct enum_traits { - static constexpr hp_part last = num_hp_parts; -}; - class addiction { public: diff --git a/src/point.cpp b/src/point.cpp index 56600f031a731..e2f9d33c5cb73 100644 --- a/src/point.cpp +++ b/src/point.cpp @@ -80,12 +80,12 @@ point clamp( const point &p, const inclusive_rectangle &r ) return point( clamp( p.x, r.p_min.x, r.p_max.x ), clamp( p.y, r.p_min.y, r.p_max.y ) ); } -std::vector closest_tripoints_first( const tripoint ¢er, int max_dist ) +std::vector closest_points_first( const tripoint ¢er, int max_dist ) { - return closest_tripoints_first( center, 0, max_dist ); + return closest_points_first( center, 0, max_dist ); } -std::vector closest_tripoints_first( const tripoint ¢er, int min_dist, int max_dist ) +std::vector closest_points_first( const tripoint ¢er, int min_dist, int max_dist ) { const std::vector points = closest_points_first( center.xy(), min_dist, max_dist ); diff --git a/src/point.h b/src/point.h index 4649d6b1fdfee..32248ad071ed1 100644 --- a/src/point.h +++ b/src/point.h @@ -33,6 +33,8 @@ class JsonOut; // NOLINTNEXTLINE(cata-xy) struct point { + static constexpr int dimension = 2; + int x = 0; int y = 0; constexpr point() = default; @@ -104,6 +106,9 @@ struct point { std::string to_string() const; + void serialize( JsonOut &jsout ) const; + void deserialize( JsonIn &jsin ); + friend inline constexpr bool operator<( const point &a, const point &b ) { return a.x < b.x || ( a.x == b.x && a.y < b.y ); } @@ -120,11 +125,29 @@ struct point { #endif }; -void serialize( const point &p, JsonOut &jsout ); -void deserialize( point &p, JsonIn &jsin ); +inline int divide_round_to_minus_infinity( int n, int d ) +{ + if( n >= 0 ) { + return n / d; + } + return ( n - d + 1 ) / d; +} + +inline point multiply_xy( const point &p, int f ) +{ + return point( p.x * f, p.y * f ); +} + +inline point divide_xy_round_to_minus_infinity( const point &p, int d ) +{ + return point( divide_round_to_minus_infinity( p.x, d ), + divide_round_to_minus_infinity( p.y, d ) ); +} // NOLINTNEXTLINE(cata-xy) struct tripoint { + static constexpr int dimension = 3; + int x = 0; int y = 0; int z = 0; @@ -228,6 +251,18 @@ struct tripoint { } }; +inline tripoint multiply_xy( const tripoint &p, int f ) +{ + return tripoint( p.x * f, p.y * f, p.z ); +} + +inline tripoint divide_xy_round_to_minus_infinity( const tripoint &p, int d ) +{ + return tripoint( divide_round_to_minus_infinity( p.x, d ), + divide_round_to_minus_infinity( p.y, d ), + p.z ); +} + struct rectangle { point p_min; point p_max; @@ -337,8 +372,8 @@ struct sphere { * Following functions return points in a spiral pattern starting at center_x/center_y until it hits the radius. Clockwise fashion. * Credit to Tom J Nowell; http://stackoverflow.com/a/1555236/1269969 */ -std::vector closest_tripoints_first( const tripoint ¢er, int max_dist ); -std::vector closest_tripoints_first( const tripoint ¢er, int min_dist, int max_dist ); +std::vector closest_points_first( const tripoint ¢er, int max_dist ); +std::vector closest_points_first( const tripoint ¢er, int min_dist, int max_dist ); std::vector closest_points_first( const point ¢er, int max_dist ); std::vector closest_points_first( const point ¢er, int min_dist, int max_dist ); diff --git a/src/ranged.cpp b/src/ranged.cpp index 88cd6da579413..9f28a8e586fbe 100644 --- a/src/ranged.cpp +++ b/src/ranged.cpp @@ -680,7 +680,8 @@ bool player::handle_gun_damage( item &it ) void npc::pretend_fire( npc *source, int shots, item &gun ) { int curshot = 0; - if( g->u.sees( *source ) && one_in( 50 ) ) { + Character &player_character = get_player_character(); + if( player_character.sees( *source ) && one_in( 50 ) ) { add_msg( m_info, _( "%s shoots something." ), source->disp_name() ); } while( curshot != shots ) { @@ -692,7 +693,7 @@ void npc::pretend_fire( npc *source, int shots, item &gun ) item *weapon = &gun; const auto data = weapon->gun_noise( shots > 1 ); - if( g->u.sees( *source ) ) { + if( player_character.sees( *source ) ) { add_msg( m_warning, _( "You hear %s." ), data.sound ); } curshot++; @@ -730,10 +731,11 @@ int player::fire_gun( const tripoint &target, int shots, item &gun ) debugmsg( "Attempted to fire zero or negative shots using %s", gun.tname() ); } + map &here = get_map(); // usage of any attached bipod is dependent upon terrain - bool bipod = g->m.has_flag_ter_or_furn( "MOUNTABLE", pos() ); + bool bipod = here.has_flag_ter_or_furn( "MOUNTABLE", pos() ); if( !bipod ) { - if( const optional_vpart_position vp = g->m.veh_at( pos() ) ) { + if( const optional_vpart_position vp = here.veh_at( pos() ) ) { bipod = vp->vehicle().has_part( pos(), "MOUNTABLE" ); } } @@ -765,7 +767,7 @@ int player::fire_gun( const tripoint &target, int shots, item &gun ) dispersion.add_range( recoil_total() ); // If this is a vehicle mounted turret, which vehicle is it mounted on? - const vehicle *in_veh = has_effect( effect_on_roof ) ? veh_pointer_or_null( g->m.veh_at( + const vehicle *in_veh = has_effect( effect_on_roof ) ? veh_pointer_or_null( here.veh_at( pos() ) ) : nullptr; auto shot = projectile_attack( make_gun_projectile( gun ), pos(), aim, dispersion, this, in_veh ); @@ -861,8 +863,8 @@ int throw_cost( const player &c, const item &to_throw ) const int skill_cost = static_cast( ( base_move_cost * ( 20 - throw_skill ) / 20 ) ); ///\EFFECT_DEX increases throwing speed const int dexbonus = c.get_dex(); - const int encumbrance_penalty = c.encumb( bp_torso ) + - ( c.encumb( bp_hand_l ) + c.encumb( bp_hand_r ) ) / 2; + const int encumbrance_penalty = c.encumb( bodypart_id( "torso" ) ) + + ( c.encumb( bodypart_id( "hand_l" ) ) + c.encumb( bodypart_id( "hand_r" ) ) ) / 2; const float stamina_ratio = static_cast( c.get_stamina() ) / c.get_stamina_max(); const float stamina_penalty = 1.0 + std::max( ( 0.25f - stamina_ratio ) * 4.0f, 0.0f ); @@ -883,7 +885,8 @@ int Character::throw_dispersion_per_dodge( bool add_encumbrance ) const // +100 at 8, +80 at 12, +66.6 at 16, +57 at 20, +50 at 24 // Each 10 encumbrance on either hand is like -1 dex (can bring penalty to +400 per dodge) // Maybe TODO: Only use one hand - const int encumbrance = add_encumbrance ? encumb( bp_hand_l ) + encumb( bp_hand_r ) : 0; + const int encumbrance = add_encumbrance ? encumb( bodypart_id( "hand_l" ) ) + encumb( + bodypart_id( "hand_r" ) ) : 0; ///\EFFECT_DEX increases throwing accuracy against targets with good dodge stat float effective_dex = 2 + get_dex() / 4.0f - ( encumbrance ) / 40.0f; return static_cast( 100.0f / std::max( 1.0f, effective_dex ) ); @@ -925,7 +928,7 @@ int Character::throwing_dispersion( const item &to_throw, Creature *critter, } // 1 perception per 1 eye encumbrance ///\EFFECT_PER decreases throwing accuracy penalty from eye encumbrance - dispersion += std::max( 0, ( encumb( bp_eyes ) - get_per() ) * 10 ); + dispersion += std::max( 0, ( encumb( bodypart_id( "eyes" ) ) - get_per() ) * 10 ); // If throwing blind, we're assuming they mechanically can't achieve the // accuracy of a normal throw. @@ -1537,16 +1540,17 @@ int time_to_attack( const Character &p, const itype &firing ) static void cycle_action( item &weap, const tripoint &pos ) { + map &here = get_map(); // eject casings and linkages in random direction avoiding walls using player position as fallback - std::vector tiles = closest_tripoints_first( pos, 1 ); + std::vector tiles = closest_points_first( pos, 1 ); tiles.erase( tiles.begin() ); tiles.erase( std::remove_if( tiles.begin(), tiles.end(), [&]( const tripoint & e ) { - return !g->m.passable( e ); + return !here.passable( e ); } ), tiles.end() ); tripoint eject = tiles.empty() ? pos : random_entry( tiles ); // for turrets try and drop casings or linkages directly to any CARGO part on the same tile - const optional_vpart_position vp = g->m.veh_at( pos ); + const optional_vpart_position vp = here.veh_at( pos ); std::vector cargo; if( vp && weap.has_flag( "VEHICLE" ) ) { cargo = vp->vehicle().get_parts_at( pos, "CARGO", part_status_flag::any ); @@ -1558,7 +1562,7 @@ static void cycle_action( item &weap, const tripoint &pos ) weap.put_in( item( casing ).set_flag( "CASING" ), item_pocket::pocket_type::CONTAINER ); } else { if( cargo.empty() ) { - g->m.add_item_or_charges( eject, item( casing ) ); + here.add_item_or_charges( eject, item( casing ) ); } else { vp->vehicle().add_item( *cargo.front(), item( casing ) ); } @@ -1576,7 +1580,7 @@ static void cycle_action( item &weap, const tripoint &pos ) linkage.set_flag( "CASING" ); weap.put_in( linkage, item_pocket::pocket_type::CONTAINER ); } else if( cargo.empty() ) { - g->m.add_item_or_charges( eject, linkage ); + here.add_item_or_charges( eject, linkage ); } else { vp->vehicle().add_item( *cargo.front(), linkage ); } @@ -1670,7 +1674,7 @@ item::sound_data item::gun_noise( const bool burst ) const static bool is_driving( const player &p ) { - const optional_vpart_position vp = g->m.veh_at( p.pos() ); + const optional_vpart_position vp = get_map().veh_at( p.pos() ); return vp && vp->vehicle().is_moving() && vp->vehicle().player_in_control( p ); } @@ -1703,7 +1707,8 @@ dispersion_sources player::get_weapon_dispersion( const item &obj ) const dispersion_sources dispersion( weapon_dispersion ); dispersion.add_range( ranged_dex_mod() ); - dispersion.add_range( ( encumb( bp_arm_l ) + encumb( bp_arm_r ) ) / 5.0 ); + dispersion.add_range( ( encumb( bodypart_id( "arm_l" ) ) + encumb( bodypart_id( "arm_r" ) ) ) / + 5.0 ); if( is_driving( *this ) ) { // get volume of gun (or for auxiliary gunmods the parent gun) @@ -1794,7 +1799,8 @@ double player::gun_value( const item &weap, int ammo ) const int move_cost = time_to_attack( *this, *weap.type ); if( gun.clip != 0 && gun.clip < 10 ) { // TODO: RELOAD_ONE should get a penalty here - int reload_cost = gun.reload_time + encumb( bp_hand_l ) + encumb( bp_hand_r ); + int reload_cost = gun.reload_time + encumb( bodypart_id( "hand_l" ) ) + encumb( + bodypart_id( "hand_r" ) ); reload_cost /= gun.clip; move_cost += reload_cost; } @@ -1876,8 +1882,9 @@ target_handler::trajectory target_ui::run() sight_dispersion = you->effective_dispersion( relevant->sight_dispersion() ); } + map &here = get_map(); // Load settings - allow_zlevel_shift = g->m.has_zlevels() && get_option( "FOV_3D" ); + allow_zlevel_shift = here.has_zlevels() && get_option( "FOV_3D" ); snap_to_target = get_option( "SNAP_TO_TARGET" ); if( mode == TargetMode::Turrets ) { // Due to how cluttered the display would become, disable it by default @@ -1885,10 +1892,11 @@ target_handler::trajectory target_ui::run() draw_turret_lines = vturrets->size() == 1; } - on_out_of_scope cleanup( []() { - g->m.invalidate_map_cache( g->u.pos().z + g->u.view_offset.z ); + avatar &player_character = get_avatar(); + on_out_of_scope cleanup( [&here, &player_character]() { + here.invalidate_map_cache( player_character.pos().z + player_character.view_offset.z ); } ); - restore_on_out_of_scope view_offset_prev( g->u.view_offset ); + restore_on_out_of_scope view_offset_prev( player_character.view_offset ); shared_ptr_fast target_ui_cb = make_shared_fast( [&]() { @@ -2228,13 +2236,14 @@ bool target_ui::set_cursor_pos( const tripoint &new_pos ) // Make sure new position is valid or find a closest valid position std::vector new_traj; tripoint valid_pos = new_pos; + map &here = get_map(); if( new_pos != src ) { // On Z axis, make sure we do not exceed map boundaries valid_pos.z = clamp( valid_pos.z, -OVERMAP_DEPTH, OVERMAP_HEIGHT ); // Or current view range valid_pos.z = clamp( valid_pos.z - src.z, -fov_3d_z_range, fov_3d_z_range ) + src.z; - new_traj = g->m.find_clear_path( src, valid_pos ); + new_traj = here.find_clear_path( src, valid_pos ); if( range == 1 ) { // We should always be able to hit adjacent squares if( square_dist( src, valid_pos ) > 1 ) { @@ -2280,7 +2289,7 @@ bool target_ui::set_cursor_pos( const tripoint &new_pos ) traj = new_traj; } else { dst = valid_pos; - traj = g->m.find_clear_path( src, dst ); + traj = here.find_clear_path( src, dst ); } if( snap_to_target ) { @@ -2363,13 +2372,14 @@ void target_ui::update_target_list() bool target_ui::choose_initial_target( bool reentered, tripoint &new_dst ) { + map &here = get_map(); // Determine if we had a target and it is still visible if( !you->last_target.expired() ) { Creature *cr = you->last_target.lock().get(); if( pl_can_target( cr ) ) { // There it is! new_dst = cr->pos(); - you->last_target_pos = g->m.getabs( new_dst ); + you->last_target_pos = here.getabs( new_dst ); return true; } } @@ -2378,7 +2388,7 @@ bool target_ui::choose_initial_target( bool reentered, tripoint &new_dst ) // and still can aim at that tile. cata::optional local_last_tgt_pos = cata::nullopt; if( you->last_target_pos ) { - tripoint local = g->m.getlocal( *you->last_target_pos ); + tripoint local = here.getlocal( *you->last_target_pos ); if( dist_fn( local ) > range ) { // No luck you->last_target_pos = cata::nullopt; @@ -2400,10 +2410,10 @@ bool target_ui::choose_initial_target( bool reentered, tripoint &new_dst ) // Try to find at least something if( targets.empty() ) { // The closest practice target - const std::vector nearby = closest_tripoints_first( src, range ); + const std::vector nearby = closest_points_first( src, range ); const auto target_spot = std::find_if( nearby.begin(), nearby.end(), - [this]( const tripoint & pt ) { - return g->m.tr_at( pt ).id == tr_practice_target && this->you->sees( pt ); + [this, &here]( const tripoint & pt ) { + return here.tr_at( pt ).id == tr_practice_target && this->you->sees( pt ); } ); if( target_spot != nearby.end() ) { @@ -2455,7 +2465,7 @@ bool target_ui::pl_can_target( const Creature *cr ) void target_ui::set_last_target() { - you->last_target_pos = g->m.getabs( dst ); + you->last_target_pos = get_map().getabs( dst ); if( dst_critter ) { you->last_target = g->shared_from( *dst_critter ); } else { @@ -2520,7 +2530,7 @@ void target_ui::set_view_offset( const tripoint &new_offset ) if( changed_z ) { // We need to do a bunch of cache updates since we're // looking at a different z-level. - g->m.invalidate_map_cache( new_.z ); + get_map().invalidate_map_cache( new_.z ); } } @@ -2550,7 +2560,7 @@ void target_ui::recalc_aim_turning_penalty() if( lt_ptr ) { curr_recoil_pos = lt_ptr->pos(); } else if( you->last_target_pos ) { - curr_recoil_pos = g->m.getlocal( *you->last_target_pos ); + curr_recoil_pos = get_map().getlocal( *you->last_target_pos ); } else { curr_recoil_pos = src; } @@ -2731,7 +2741,7 @@ void target_ui::draw_terrain_overlay() g->draw_highlight( tile ); } else { #endif - g->m.drawsq( g->w_terrain, *you, tile, true, true, center ); + get_map().drawsq( g->w_terrain, *you, tile, true, true, center ); #ifdef TILES } #endif 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 { diff --git a/src/requirements.cpp b/src/requirements.cpp index 1f4328bd7e44e..6f0c4775c48fc 100644 --- a/src/requirements.cpp +++ b/src/requirements.cpp @@ -12,8 +12,8 @@ #include #include -#include "avatar.h" #include "cata_utility.h" +#include "character.h" #include "color.h" #include "debug.h" #include "game.h" @@ -643,7 +643,7 @@ std::vector requirement_data::get_folded_tools_list( int width, nc_ bool requirement_data::can_make_with_inventory( const inventory &crafting_inv, const std::function &filter, int batch, craft_flags flags ) const { - if( g->u.has_trait( trait_DEBUG_HS ) ) { + if( get_player_character().has_trait( trait_DEBUG_HS ) ) { return true; } @@ -705,7 +705,7 @@ bool quality_requirement::has( const inventory &crafting_inv, const std::function &, int, craft_flags, const std::function & ) const { - if( g->u.has_trait( trait_DEBUG_HS ) ) { + if( get_player_character().has_trait( trait_DEBUG_HS ) ) { return true; } return crafting_inv.has_quality( type, level, count ); @@ -724,7 +724,7 @@ bool tool_comp::has( const inventory &crafting_inv, const std::function &filter, int batch, craft_flags flags, const std::function &visitor ) const { - if( g->u.has_trait( trait_DEBUG_HS ) ) { + if( get_player_character().has_trait( trait_DEBUG_HS ) ) { return true; } if( !by_charges() ) { @@ -756,7 +756,7 @@ bool item_comp::has( const inventory &crafting_inv, const std::function &filter, int batch, craft_flags, const std::function & ) const { - if( g->u.has_trait( trait_DEBUG_HS ) ) { + if( get_player_character().has_trait( trait_DEBUG_HS ) ) { return true; } const int cnt = std::abs( count ) * batch; diff --git a/src/safemode_ui.cpp b/src/safemode_ui.cpp index fa3367e55eb09..bc2f4f7285b8e 100644 --- a/src/safemode_ui.cpp +++ b/src/safemode_ui.cpp @@ -8,8 +8,8 @@ #include #include -#include "avatar.h" #include "cata_utility.h" +#include "character.h" #include "color.h" #include "compatibility.h" #include "cursesdef.h" @@ -121,6 +121,7 @@ void safemode::show( const std::string &custom_name_in, bool is_safemode_in ) ctxt.register_action( "SWAP_RULE_GLOBAL_CHAR" ); } + Character &player_character = get_player_character(); ui.on_redraw( [&]( const ui_adaptor & ) { draw_border( w_border, BORDER_COLOR, custom_name_in ); @@ -201,7 +202,7 @@ void safemode::show( const std::string &custom_name_in, bool is_safemode_in ) auto ¤t_tab = ( tab == GLOBAL_TAB ) ? global_rules : character_rules; - if( tab == CHARACTER_TAB && g->u.name.empty() ) { + if( tab == CHARACTER_TAB && player_character.name.empty() ) { character_rules.clear(); mvwprintz( w, point( 15, 8 ), c_white, _( "Please load a character first to use this page!" ) ); } else if( empty() ) { @@ -269,7 +270,7 @@ void safemode::show( const std::string &custom_name_in, bool is_safemode_in ) } } else if( action == "QUIT" ) { break; - } else if( tab == CHARACTER_TAB && g->u.name.empty() ) { + } else if( tab == CHARACTER_TAB && player_character.name.empty() ) { //Only allow loaded games to use the char sheet } else if( action == "DOWN" ) { line++; @@ -308,7 +309,7 @@ void safemode::show( const std::string &custom_name_in, bool is_safemode_in ) current_tab.push_back( current_tab[line] ); line = current_tab.size() - 1; } else if( action == "SWAP_RULE_GLOBAL_CHAR" && !current_tab.empty() ) { - if( ( tab == GLOBAL_TAB && !g->u.name.empty() ) || tab == CHARACTER_TAB ) { + if( ( tab == GLOBAL_TAB && !player_character.name.empty() ) || tab == CHARACTER_TAB ) { changes_made = true; //copy over auto &temp_rules_from = ( tab == GLOBAL_TAB ) ? global_rules : character_rules; @@ -467,7 +468,7 @@ void safemode::show( const std::string &custom_name_in, bool is_safemode_in ) if( query_yn( _( "Save changes?" ) ) ) { if( is_safemode_in ) { save_global(); - if( !g->u.name.empty() ) { + if( !player_character.name.empty() ) { save_character(); } } else { @@ -489,7 +490,8 @@ void safemode::test_pattern( const int tab_in, const int row_in ) return; } - if( g->u.name.empty() ) { + Character &player_character = get_player_character(); + if( player_character.name.empty() ) { popup( _( "No monsters loaded. Please start a game first." ) ); return; } diff --git a/src/savegame.cpp b/src/savegame.cpp index 28ff9e50bb914..ea6cd55abf40a 100644 --- a/src/savegame.cpp +++ b/src/savegame.cpp @@ -336,6 +336,7 @@ bool overmap::obsolete_terrain( const std::string &ter ) */ void overmap::convert_terrain( const std::unordered_map &needs_conversion ) { + std::vector bridge_points; for( const auto &convert : needs_conversion ) { const tripoint pos = convert.first; const std::string old = convert.second; @@ -350,14 +351,17 @@ void overmap::convert_terrain( const std::unordered_map & std::vector nearby; std::vector> convert_unrelated_adjacent_tiles; - if( old == "rural_house" || old == "rural_house_north" ) { - ter_set( pos, oter_id( "rural_house1_north" ) ); - } else if( old == "rural_house_south" ) { - ter_set( pos, oter_id( "rural_house1_south" ) ); - } else if( old == "rural_house_east" ) { - ter_set( pos, oter_id( "rural_house1_east" ) ); - } else if( old == "rural_house_west" ) { - ter_set( pos, oter_id( "rural_house1_west" ) ); + if( old == "fema" || old == "fema_entrance" || old == "fema_1_3" || + old == "fema_2_1" || old == "fema_2_2" || old == "fema_2_3" || + old == "fema_3_1" || old == "fema_3_2" || old == "fema_3_3" ) { + ter_set( pos, oter_id( old + "_north" ) ); + } else if( old.compare( 0, 6, "bridge" ) == 0 ) { + ter_set( pos, oter_id( old ) ); + const oter_id oter_ground = ter( tripoint( pos.xy(), 0 ) ); + if( is_ot_match( "bridge", oter_ground, ot_match_type::type ) ) { + ter_set( pos + tripoint_above, oter_id( "bridge_road" + oter_get_rotation_string( oter_ground ) ) ); + bridge_points.emplace_back( pos.xy() ); + } } else if( old.compare( 0, 10, "mass_grave" ) == 0 ) { ter_set( pos, oter_id( "field" ) ); } @@ -376,6 +380,8 @@ void overmap::convert_terrain( const std::unordered_map & ter_set( pos + conv.first, oter_id( conv.second ) ); } } + + generate_bridgeheads( bridge_points ); } void overmap::load_monster_groups( JsonIn &jsin ) diff --git a/src/savegame_json.cpp b/src/savegame_json.cpp index a7204704b4bf7..9a0cf5dce9d1e 100644 --- a/src/savegame_json.cpp +++ b/src/savegame_json.cpp @@ -727,7 +727,7 @@ void Character::load( const JsonObject &data ) on_stat_change( "pkill", pkill ); on_stat_change( "perceived_pain", get_perceived_pain() ); recalc_sight_limits(); - reset_encumbrance(); + calc_encumbrance(); assign( data, "power_level", power_level, false, 0_kJ ); assign( data, "max_power_level", max_power_level, false, 0_kJ ); @@ -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 ); @@ -2633,20 +2644,20 @@ void vehicle_part::serialize( JsonOut &json ) const /* * label */ -static void deserialize( label &val, JsonIn &jsin ) +void label::deserialize( JsonIn &jsin ) { JsonObject data = jsin.get_object(); - data.read( "x", val.x ); - data.read( "y", val.y ); - data.read( "text", val.text ); + data.read( "x", x ); + data.read( "y", y ); + data.read( "text", text ); } -static void serialize( const label &val, JsonOut &json ) +void label::serialize( JsonOut &json ) const { json.start_object(); - json.member( "x", val.x ); - json.member( "y", val.y ); - json.member( "text", val.text ); + json.member( "x", x ); + json.member( "y", y ); + json.member( "text", text ); json.end_object(); } @@ -3315,19 +3326,19 @@ void map_memory::load( const JsonObject &jsin ) } } -void deserialize( point &p, JsonIn &jsin ) +void point::deserialize( JsonIn &jsin ) { jsin.start_array(); - p.x = jsin.get_int(); - p.y = jsin.get_int(); + x = jsin.get_int(); + y = jsin.get_int(); jsin.end_array(); } -void serialize( const point &p, JsonOut &jsout ) +void point::serialize( JsonOut &jsout ) const { jsout.start_array(); - jsout.write( p.x ); - jsout.write( p.y ); + jsout.write( x ); + jsout.write( y ); jsout.end_array(); } diff --git a/src/scent_map.cpp b/src/scent_map.cpp index a29c807c3a438..905b685bea3c0 100644 --- a/src/scent_map.cpp +++ b/src/scent_map.cpp @@ -134,7 +134,7 @@ bool scent_map::inbounds( const tripoint &p ) const const int levz = gm.get_levz(); const bool scent_map_z_level_inbounds = ( p.z == levz ) || ( std::abs( p.z - levz ) == SCENT_MAP_Z_REACH && - gm.m.valid_move( p, tripoint( p.xy(), levz ), false, true ) ); + get_map().valid_move( p, tripoint( p.xy(), levz ), false, true ) ); if( !scent_map_z_level_inbounds ) { return false; } 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/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/src/sounds.cpp b/src/sounds.cpp index dc9f0d3e62129..c12da23fb3722 100644 --- a/src/sounds.cpp +++ b/src/sounds.cpp @@ -61,7 +61,7 @@ # define dbg(x) DebugLog((x),D_SDL) << __FILE__ << ":" << __LINE__ << ": " #endif -weather_type previous_weather; +weather_type_id previous_weather; int prev_hostiles = 0; int previous_speed = 0; int previous_gear = 0; @@ -278,7 +278,7 @@ static int get_signal_for_hordes( const centroid ¢r ) { //Volume in tiles. Signal for hordes in submaps //modify vol using weather vol.Weather can reduce monster hearing - const int vol = centr.volume - weather::sound_attn( get_weather().weather ); + const int vol = centr.volume - get_weather().weather_id->sound_attn; const int min_vol_cap = 60; //Hordes can't hear volume lower than this const int underground_div = 2; //Coefficient for volume reduction underground const int hordes_sig_div = SEEX; //Divider coefficient for hordes @@ -302,7 +302,7 @@ static int get_signal_for_hordes( const centroid ¢r ) void sounds::process_sounds() { std::vector sound_clusters = cluster_sounds( recent_sounds ); - const int weather_vol = weather::sound_attn( get_weather().weather ); + const int weather_vol = get_weather().weather_id->sound_attn; for( const auto &this_centroid : sound_clusters ) { // Since monsters don't go deaf ATM we can just use the weather modified volume // If they later get physical effects from loud noises we'll have to change this @@ -384,7 +384,7 @@ void sounds::process_sound_markers( player *p ) { bool is_deaf = p->is_deaf(); const float volume_multiplier = p->hearing_ability(); - const int weather_vol = weather::sound_attn( get_weather().weather ); + const int weather_vol = get_weather().weather_id->sound_attn; for( const auto &sound_event_pair : sounds_since_last_turn ) { const tripoint &pos = sound_event_pair.first; const sound_event &sound = sound_event_pair.second; @@ -868,7 +868,7 @@ void sfx::do_ambient() const int heard_volume = get_heard_volume( player_character.pos() ); const bool is_underground = player_character.pos().z < 0; const bool is_sheltered = g->is_sheltered( player_character.pos() ); - const bool weather_changed = get_weather().weather != previous_weather; + const bool weather_changed = get_weather().weather_id != previous_weather; // Step in at night time / we are not indoors if( is_night( calendar::turn ) && !is_sheltered && !is_channel_playing( channel::nighttime_outdoors_env ) && !is_deaf ) { @@ -900,15 +900,15 @@ void sfx::do_ambient() play_ambient_variant_sound( "environment", "indoors", heard_volume, channel::indoors_env, 1000 ); } - weather_type current_weather = get_weather().weather; // We are indoors and it is also raining - if( current_weather >= WEATHER_DRIZZLE && current_weather <= WEATHER_ACID_RAIN && - !is_underground - && is_sheltered && !is_channel_playing( channel::indoors_rain_env ) ) { + if( get_weather().weather_id->rains && + get_weather().weather_id->precip != precip_class::very_light && + !is_underground && is_sheltered && !is_channel_playing( channel::indoors_rain_env ) ) { play_ambient_variant_sound( "environment", "indoors_rain", heard_volume, channel::indoors_rain_env, 1000 ); } - if( ( !is_sheltered && current_weather != WEATHER_CLEAR && !is_deaf && + if( ( !is_sheltered && + get_weather().weather_id->sound_category != weather_sound_category::silent && !is_deaf && !is_channel_playing( channel::outdoors_snow_env ) && !is_channel_playing( channel::outdoors_flurry_env ) && !is_channel_playing( channel::outdoors_thunderstorm_env ) && @@ -919,51 +919,45 @@ void sfx::do_ambient() weather_changed && !is_deaf ) ) { fade_audio_group( group::weather, 1000 ); // We are outside and there is precipitation - switch( current_weather ) { - case WEATHER_ACID_DRIZZLE: - case WEATHER_DRIZZLE: - case WEATHER_LIGHT_DRIZZLE: + switch( get_weather().weather_id->sound_category ) { + case weather_sound_category::drizzle: play_ambient_variant_sound( "environment", "WEATHER_DRIZZLE", heard_volume, channel::outdoors_drizzle_env, 1000 ); break; - case WEATHER_RAINY: + case weather_sound_category::rainy: play_ambient_variant_sound( "environment", "WEATHER_RAINY", heard_volume, channel::outdoors_rain_env, 1000 ); break; - case WEATHER_ACID_RAIN: - case WEATHER_THUNDER: - case WEATHER_LIGHTNING: + case weather_sound_category::thunder: play_ambient_variant_sound( "environment", "WEATHER_THUNDER", heard_volume, channel::outdoors_thunderstorm_env, 1000 ); break; - case WEATHER_FLURRIES: + case weather_sound_category::flurries: play_ambient_variant_sound( "environment", "WEATHER_FLURRIES", heard_volume, channel::outdoors_flurry_env, 1000 ); break; - case WEATHER_CLEAR: - case WEATHER_SUNNY: - case WEATHER_CLOUDY: - case WEATHER_SNOWSTORM: + case weather_sound_category::snowstorm: play_ambient_variant_sound( "environment", "WEATHER_SNOWSTORM", heard_volume, channel::outdoor_blizzard, 1000 ); break; - case WEATHER_SNOW: + case weather_sound_category::snow: play_ambient_variant_sound( "environment", "WEATHER_SNOW", heard_volume, channel::outdoors_snow_env, 1000 ); break; - case WEATHER_NULL: - case NUM_WEATHER_TYPES: - // nothing here, those are pseudo-types, they should not be active at all. + case weather_sound_category::silent: + break; + case weather_sound_category::last: + debugmsg( "Invalid weather sound category." ); break; } } // Keep track of weather to compare for next iteration - previous_weather = current_weather; + previous_weather = get_weather().weather_id; } // firing is the item that is fired. It may be the wielded gun, but it can also be an attached @@ -1168,7 +1162,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 +1508,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/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/suffer.cpp b/src/suffer.cpp index 481490761e772..7eac1bdf5c87d 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,10 +719,10 @@ 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 ) ) { - 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() ); + if( leafy && get_map().is_outside( pos() ) && ( g->light_level( pos().z ) >= 40 ) ) { + const float weather_factor = ( get_weather().weather_id->sun_intensity >= + sun_intensity_type::normal ) ? 1.0 : 0.5; + const int player_local_temp = get_weather().get_temperature( pos() ); int flux = ( player_local_temp - 65 ) / 2; if( !has_hat ) { sunlight_nutrition += ( 100 + flux ) * weather_factor; @@ -764,7 +765,7 @@ void Character::suffer_in_sunlight() } if( ( has_trait( trait_TROGLO ) || has_trait( trait_TROGLO2 ) ) && - g->weather.weather == WEATHER_SUNNY ) { + get_weather().weather_id->sun_intensity >= sun_intensity_type::high ) { mod_str_bonus( -1 ); mod_dex_bonus( -1 ); add_miss_reason( _( "The sunlight distracts you." ), 1 ); @@ -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; @@ -917,8 +919,8 @@ void Character::suffer_from_other_mutations() } if( has_trait( trait_SORES ) ) { - for( const body_part bp : all_body_parts ) { - if( bp == bp_head ) { + for( const bodypart_id bp : get_all_body_parts() ) { + if( bp == bodypart_id( "head" ) ) { continue; } int sores_pain = 5 + 0.4 * std::abs( encumb( bp ) ); @@ -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." ) ); @@ -1259,9 +1262,9 @@ void Character::suffer_from_artifacts() } if( has_artifact_with( AEP_BAD_WEATHER ) && calendar::once_every( 1_minutes ) && - g->weather.weather != WEATHER_SNOWSTORM ) { - g->weather.weather_override = WEATHER_SNOWSTORM; - g->weather.set_nextweather( calendar::turn ); + get_weather().weather_id->precip < precip_class::heavy ) { + get_weather().weather_override = get_bad_weather(); + get_weather().set_nextweather( calendar::turn ); } if( has_artifact_with( AEP_MUTAGENIC ) && one_turn_in( 48_hours ) ) { diff --git a/src/teleport.cpp b/src/teleport.cpp index 732bf41e5b8d8..c420880121dd6 100644 --- a/src/teleport.cpp +++ b/src/teleport.cpp @@ -3,9 +3,9 @@ #include #include -#include "avatar.h" #include "bodypart.h" #include "calendar.h" +#include "character.h" #include "creature.h" #include "debug.h" #include "enums.h" @@ -14,7 +14,6 @@ #include "game.h" #include "map.h" #include "messages.h" -#include "player.h" #include "point.h" #include "rng.h" #include "translations.h" @@ -30,8 +29,8 @@ bool teleport::teleport( Creature &critter, int min_distance, int max_distance, debugmsg( "ERROR: Function teleport::teleport called with invalid arguments." ); return false; } - player *const p = critter.as_player(); - const bool c_is_u = p != nullptr && p == &g->u; + Character *const p = critter.as_character(); + const bool c_is_u = p != nullptr && p->is_avatar(); int tries = 0; tripoint origin = critter.pos(); tripoint new_pos = origin; @@ -67,7 +66,7 @@ bool teleport::teleport( Creature &critter, int min_distance, int max_distance, } //handles telefragging other creatures if( Creature *const poor_soul = g->critter_at( new_pos ) ) { - player *const poor_player = dynamic_cast( poor_soul ); + Character *const poor_player = dynamic_cast( poor_soul ); if( safe ) { if( c_is_u ) { add_msg( m_bad, _( "You cannot teleport safely." ) ); @@ -78,7 +77,7 @@ bool teleport::teleport( Creature &critter, int min_distance, int max_distance, poor_player->add_msg_if_player( m_warning, _( "You feel disjointed." ) ); return false; } else { - const bool poor_soul_is_u = ( poor_soul == &g->u ); + const bool poor_soul_is_u = poor_soul->is_avatar(); if( poor_soul_is_u ) { add_msg( m_bad, _( "…" ) ); add_msg( m_bad, _( "You explode into thousands of fragments." ) ); @@ -90,7 +89,7 @@ bool teleport::teleport( Creature &critter, int min_distance, int max_distance, poor_soul->disp_name() ); g->events().send( p->getID(), poor_soul->get_name() ); } else { - if( g->u.sees( *poor_soul ) ) { + if( get_player_character().sees( *poor_soul ) ) { add_msg( m_good, _( "%1$s teleports into %2$s, killing them!" ), critter.disp_name(), poor_soul->disp_name() ); } diff --git a/src/timed_event.cpp b/src/timed_event.cpp index 4d0ca8439bff2..1fd1e629d68e8 100644 --- a/src/timed_event.cpp +++ b/src/timed_event.cpp @@ -49,20 +49,22 @@ timed_event::timed_event( timed_event_type e_t, const time_point &w, int f_id, t void timed_event::actualize() { + avatar &player_character = get_avatar(); + map &here = get_map(); switch( type ) { case timed_event_type::HELP: debugmsg( "Currently disabled while NPC and monster factions are being rewritten." ); break; case timed_event_type::ROBOT_ATTACK: { - const auto u_pos = g->u.global_sm_location(); + const auto u_pos = player_character.global_sm_location(); if( rl_dist( u_pos, map_point ) <= 4 ) { const mtype_id &robot_type = one_in( 2 ) ? mon_copbot : mon_riotbot; - g->events().send( g->u.getID() ); + g->events().send( player_character.getID() ); point rob( u_pos.x > map_point.x ? 0 - SEEX * 2 : SEEX * 4, u_pos.y > map_point.y ? 0 - SEEY * 2 : SEEY * 4 ); - g->place_critter_at( robot_type, tripoint( rob, g->u.posz() ) ); + g->place_critter_at( robot_type, tripoint( rob, player_character.posz() ) ); } } break; @@ -76,17 +78,18 @@ void timed_event::actualize() pgettext( "memorial_female", "Drew the attention of more dark wyrms!" ) ); int num_wyrms = rng( 1, 4 ); for( int i = 0; i < num_wyrms; i++ ) { - if( monster *const mon = g->place_critter_around( mon_dark_wyrm, g->u.pos(), 2 ) ) { - g->m.ter_set( mon->pos(), t_rock_floor ); + if( monster *const mon = g->place_critter_around( mon_dark_wyrm, player_character.pos(), 2 ) ) { + here.ter_set( mon->pos(), t_rock_floor ); } } // You could drop the flag, you know. - if( g->u.has_amount( itype_petrified_eye, 1 ) ) { - sounds::sound( g->u.pos(), 60, sounds::sound_t::alert, _( "a tortured scream!" ), false, "shout", + if( player_character.has_amount( itype_petrified_eye, 1 ) ) { + sounds::sound( player_character.pos(), 60, sounds::sound_t::alert, _( "a tortured scream!" ), false, + "shout", "scream_tortured" ); - if( !g->u.is_deaf() ) { + if( !player_character.is_deaf() ) { add_msg( _( "The eye you're carrying lets out a tortured scream!" ) ); - g->u.add_morale( MORALE_SCREAM, -15, 0, 30_minutes, 30_seconds ); + player_character.add_morale( MORALE_SCREAM, -15, 0, 30_minutes, 30_seconds ); } } // They just keep coming! @@ -102,20 +105,20 @@ void timed_event::actualize() int num_horrors = rng( 3, 5 ); cata::optional fault_point; bool horizontal = false; - for( const tripoint &p : g->m.points_on_zlevel() ) { - if( g->m.ter( p ) == t_fault ) { + for( const tripoint &p : here.points_on_zlevel() ) { + if( here.ter( p ) == t_fault ) { fault_point = p; - horizontal = g->m.ter( p + tripoint_east ) == t_fault || g->m.ter( p + tripoint_west ) == t_fault; + horizontal = here.ter( p + tripoint_east ) == t_fault || here.ter( p + tripoint_west ) == t_fault; break; } } for( int i = 0; fault_point && i < num_horrors; i++ ) { for( int tries = 0; tries < 10; ++tries ) { - tripoint monp = g->u.pos(); + tripoint monp = player_character.pos(); if( horizontal ) { monp.x = rng( fault_point->x, fault_point->x + 2 * SEEX - 8 ); for( int n = -1; n <= 1; n++ ) { - if( g->m.ter( point( monp.x, fault_point->y + n ) ) == t_rock_floor ) { + if( here.ter( point( monp.x, fault_point->y + n ) ) == t_rock_floor ) { monp.y = fault_point->y + n; } } @@ -123,7 +126,7 @@ void timed_event::actualize() // Vertical fault monp.y = rng( fault_point->y, fault_point->y + 2 * SEEY - 8 ); for( int n = -1; n <= 1; n++ ) { - if( g->m.ter( point( fault_point->x + n, monp.y ) ) == t_rock_floor ) { + if( here.ter( point( fault_point->x + n, monp.y ) ) == t_rock_floor ) { monp.x = fault_point->x + n; } } @@ -138,9 +141,9 @@ void timed_event::actualize() case timed_event_type::ROOTS_DIE: g->events().send(); - for( const tripoint &p : g->m.points_on_zlevel() ) { - if( g->m.ter( p ) == t_root_wall && one_in( 3 ) ) { - g->m.ter_set( p, t_underbrush ); + for( const tripoint &p : here.points_on_zlevel() ) { + if( here.ter( p ) == t_root_wall && one_in( 3 ) ) { + here.ter_set( p, t_underbrush ); } } break; @@ -148,10 +151,10 @@ void timed_event::actualize() case timed_event_type::TEMPLE_OPEN: { g->events().send(); bool saw_grate = false; - for( const tripoint &p : g->m.points_on_zlevel() ) { - if( g->m.ter( p ) == t_grate ) { - g->m.ter_set( p, t_stairs_down ); - if( !saw_grate && g->u.sees( p ) ) { + for( const tripoint &p : here.points_on_zlevel() ) { + if( here.ter( p ) == t_grate ) { + here.ter_set( p, t_stairs_down ); + if( !saw_grate && player_character.sees( p ) ) { saw_grate = true; } } @@ -166,14 +169,14 @@ void timed_event::actualize() bool flooded = false; ter_id flood_buf[MAPSIZE_X][MAPSIZE_Y]; - for( const tripoint &p : g->m.points_on_zlevel() ) { - flood_buf[p.x][p.y] = g->m.ter( p ); + for( const tripoint &p : here.points_on_zlevel() ) { + flood_buf[p.x][p.y] = here.ter( p ); } - for( const tripoint &p : g->m.points_on_zlevel() ) { - if( g->m.ter( p ) == t_water_sh ) { + for( const tripoint &p : here.points_on_zlevel() ) { + if( here.ter( p ) == t_water_sh ) { bool deepen = false; for( const tripoint &w : points_in_radius( p, 1 ) ) { - if( g->m.ter( w ) == t_water_dp ) { + if( here.ter( w ) == t_water_dp ) { deepen = true; break; } @@ -182,10 +185,10 @@ void timed_event::actualize() flood_buf[p.x][p.y] = t_water_dp; flooded = true; } - } else if( g->m.ter( p ) == t_rock_floor ) { + } else if( here.ter( p ) == t_rock_floor ) { bool flood = false; for( const tripoint &w : points_in_radius( p, 1 ) ) { - if( g->m.ter( w ) == t_water_dp || g->m.ter( w ) == t_water_sh ) { + if( here.ter( w ) == t_water_dp || here.ter( w ) == t_water_sh ) { flood = true; break; } @@ -201,8 +204,9 @@ void timed_event::actualize() return; } // Check if we should print a message - if( flood_buf[g->u.posx()][g->u.posy()] != g->m.ter( g->u.pos() ) ) { - if( flood_buf[g->u.posx()][g->u.posy()] == t_water_sh ) { + if( flood_buf[player_character.posx()][player_character.posy()] != here.ter( + player_character.pos() ) ) { + if( flood_buf[player_character.posx()][player_character.posy()] == t_water_sh ) { add_msg( m_warning, _( "Water quickly floods up to your knees." ) ); g->memorial().add( pgettext( "memorial_male", "Water level reached knees." ), @@ -213,12 +217,12 @@ void timed_event::actualize() g->memorial().add( pgettext( "memorial_male", "Water level reached the ceiling." ), pgettext( "memorial_female", "Water level reached the ceiling." ) ); - avatar_action::swim( g->m, g->u, g->u.pos() ); + avatar_action::swim( here, player_character, player_character.pos() ); } } - // flood_buf is filled with correct tiles; now copy them back to g->m - for( const tripoint &p : g->m.points_on_zlevel() ) { - g->m.ter_set( p, flood_buf[p.x][p.y] ); + // flood_buf is filled with correct tiles; now copy them back to here + for( const tripoint &p : here.points_on_zlevel() ) { + here.ter_set( p, flood_buf[p.x][p.y] ); } g->timed_events.add( timed_event_type::TEMPLE_FLOOD, calendar::turn + rng( 2_turns, 3_turns ) ); @@ -231,7 +235,7 @@ void timed_event::actualize() } }; const mtype_id &montype = random_entry( temple_monsters ); - g->place_critter_around( montype, g->u.pos(), 2 ); + g->place_critter_around( montype, player_character.pos(), 2 ); } break; @@ -243,17 +247,19 @@ void timed_event::actualize() void timed_event::per_turn() { + Character &player_character = get_player_character(); + map &here = get_map(); switch( type ) { case timed_event_type::WANTED: { // About once every 5 minutes. Suppress in classic zombie mode. if( g->get_levz() >= 0 && one_in( 50 ) && !get_option( "DISABLE_ROBOT_RESPONSE" ) ) { - point place = g->m.random_outdoor_tile(); + point place = here.random_outdoor_tile(); if( place.x == -1 && place.y == -1 ) { // We're safely indoors! return; } - g->place_critter_at( mon_eyebot, tripoint( place, g->u.posz() ) ); - if( g->u.sees( tripoint( place, g->u.posz() ) ) ) { + g->place_critter_at( mon_eyebot, tripoint( place, player_character.posz() ) ); + if( player_character.sees( tripoint( place, player_character.posz() ) ) ) { add_msg( m_warning, _( "An eyebot swoops down nearby!" ) ); } // One eyebot per trigger is enough, really @@ -267,7 +273,7 @@ void timed_event::per_turn() when -= 1_turns; return; } - if( calendar::once_every( 3_turns ) && !g->u.is_deaf() ) { + if( calendar::once_every( 3_turns ) && !player_character.is_deaf() ) { add_msg( m_warning, _( "You hear screeches from the rock above and around you!" ) ); } break; @@ -302,7 +308,7 @@ void timed_event_manager::process() void timed_event_manager::add( const timed_event_type type, const time_point &when, const int faction_id ) { - add( type, when, faction_id, g->u.global_sm_location() ); + add( type, when, faction_id, get_player_character().global_sm_location() ); } void timed_event_manager::add( const timed_event_type type, const time_point &when, diff --git a/src/trap.cpp b/src/trap.cpp index 3fe2a01f2a700..4a58c4a9c0046 100644 --- a/src/trap.cpp +++ b/src/trap.cpp @@ -190,6 +190,18 @@ 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 + 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: @@ -200,7 +212,7 @@ bool trap::detect_trap( const tripoint &pos, const Character &p ) const // noticing a buried landmine if standing right next to it. // Effective Perception... ///\EFFECT_PER increases chance of detecting a trap - return p.per_cur - p.encumb( bp_eyes ) / 10 + + return p.per_cur - p.encumb( bodypart_id( "eyes" ) ) / 10 + // ...small bonus from stimulants... ( p.get_stim() > 10 ? rng( 1, 2 ) : 0 ) + // ...bonus from trap skill... @@ -226,11 +238,27 @@ 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 ); } +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 ); @@ -274,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, @@ -302,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() { @@ -328,6 +336,16 @@ void trap::check_consistency() } } +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() ) { @@ -342,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" ); @@ -370,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" ); @@ -382,5 +381,10 @@ 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 +{ + 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 3ee31bfc1318a..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; @@ -92,11 +112,21 @@ 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; - // 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; @@ -127,12 +157,22 @@ struct trap { bool is_always_invisible() const { return always_invisible; } + + bool operator==( const trap_id &id ) const { + return loadid == id; + } + bool operator!=( const trap_id &id ) const { + return loadid != id; + } + /** - * How easy it is to spot the trap. Smaller values means it's easier to spot. + * 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. */ - int get_visibility() const { - return visibility; - } + // Implemented for historical reasons in iexamine.cpp + void examine( const tripoint &examp ) const; std::string map_regen_target() const; @@ -144,14 +184,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). @@ -159,6 +191,24 @@ 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; + + /** + * 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). + */ + 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; @@ -167,6 +217,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 @@ -176,11 +227,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 @@ -202,6 +269,8 @@ struct trap { */ void load( const JsonObject &jo, const std::string &src ); + std::string debug_describe() const; + /*@{*/ /** * @name Funnels @@ -254,26 +323,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, @@ -282,12 +334,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, diff --git a/src/trapfunc.cpp b/src/trapfunc.cpp index 96b57b3b0af00..fb1144d3d8bf5 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 ) == 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 ) != 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 ) != 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 ) == 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 ) { @@ -1032,39 +1043,40 @@ static tripoint random_neighbor( tripoint center ) return center; } -static bool sinkhole_safety_roll( player *p, const itype_id &itemname, const int diff ) +static bool sinkhole_safety_roll( player &p, const itype_id &itemname, const int diff ) { ///\EFFECT_STR increases chance to attach grapnel, bullwhip, or rope when falling into a sinkhole ///\EFFECT_DEX increases chance to attach grapnel, bullwhip, or rope when falling into a sinkhole ///\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 ); + 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 ); + p.add_msg_if_player( m_bad, _( "You fail to attach it…" ) ); + p.use_amount( itemname, 1 ); + 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 ) != 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 ); + p.add_msg_if_player( m_bad, _( "There's nowhere to pull yourself to, and you sink!" ) ); + p.use_amount( itemname, 1 ); + 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 ) { - g->update_map( *p ); + 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.is_avatar() ) { + g->update_map( p ); } return true; @@ -1079,29 +1091,30 @@ 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; if( query_for_item( pl, itype_grapnel, _( "You step into a sinkhole! Throw your grappling hook out to try to catch something?" ) ) ) { - success = sinkhole_safety_roll( pl, itype_grapnel, 6 ); + success = sinkhole_safety_roll( *pl, itype_grapnel, 6 ); } else if( query_for_item( pl, itype_bullwhip, _( "You step into a sinkhole! Throw your whip out to try and snag something?" ) ) ) { - success = sinkhole_safety_roll( pl, itype_bullwhip, 8 ); + success = sinkhole_safety_roll( *pl, itype_bullwhip, 8 ); } else if( query_for_item( pl, itype_rope_30, _( "You step into a sinkhole! Throw your rope out to try to catch something?" ) ) ) { - success = sinkhole_safety_roll( pl, itype_rope_30, 12 ); + success = sinkhole_safety_roll( *pl, itype_rope_30, 12 ); } 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 ); } @@ -1224,16 +1238,20 @@ bool trapfunc::ledge( const tripoint &p, Creature *c, item * ) bool trapfunc::temple_flood( const tripoint &p, Creature *c, item * ) { + if( c == nullptr ) { + return false; + } // 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 ) == tr_temple_flood ) { + here.remove_trap( tmp ); } } } @@ -1245,32 +1263,36 @@ bool trapfunc::temple_flood( const tripoint &p, Creature *c, item * ) bool trapfunc::temple_toggle( const tripoint &p, Creature *c, item * ) { + if( c == nullptr ) { + return false; + } // 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 +1317,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 +1364,10 @@ bool trapfunc::hum( const tripoint &p, Creature *, item * ) bool trapfunc::shadow( const tripoint &p, Creature *c, item * ) { - if( c != &g->u ) { + if( c == nullptr || !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 +1378,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 +1396,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 +1434,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 +1447,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 +1461,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; } } diff --git a/src/turret.cpp b/src/turret.cpp index dfe6451c58f28..77c1760dc98a5 100644 --- a/src/turret.cpp +++ b/src/turret.cpp @@ -396,8 +396,10 @@ bool vehicle::turrets_aim( std::vector &turrets ) t->reset_target( global_part_pos3( *t ) ); } + avatar &player_character = get_avatar(); // Get target - target_handler::trajectory trajectory = target_handler::mode_turrets( g->u, *this, turrets ); + target_handler::trajectory trajectory = target_handler::mode_turrets( player_character, *this, + turrets ); bool got_target = !trajectory.empty(); if( got_target ) { @@ -410,7 +412,8 @@ bool vehicle::turrets_aim( std::vector &turrets ) } ///\EFFECT_INT speeds up aiming of vehicle turrets - g->u.moves = std::min( 0, g->u.moves - 100 + ( 5 * g->u.int_cur ) ); + player_character.moves = std::min( 0, + player_character.moves - 100 + ( 5 * player_character.int_cur ) ); } return got_target; } @@ -567,7 +570,8 @@ int vehicle::automatic_fire_turret( vehicle_part &pt ) area += area == 1 ? 1 : 2; } - const bool u_see = g->u.sees( pos ); + Character &player_character = get_player_character(); + const bool u_see = player_character.sees( pos ); // The current target of the turret. auto &target = pt.target; if( target.first == target.second ) { @@ -616,7 +620,7 @@ int vehicle::automatic_fire_turret( vehicle_part &pt ) shots = gun.fire( cpu, targ ); - if( shots && u_see && !g->u.sees( targ ) ) { + if( shots && u_see && !player_character.sees( targ ) ) { add_msg( _( "The %1$s fires its %2$s!" ), name, pt.name() ); } diff --git a/src/type_id.h b/src/type_id.h index 16243d48fd54a..7c579370e5c2f 100644 --- a/src/type_id.h +++ b/src/type_id.h @@ -179,6 +179,9 @@ using vpart_id = string_id; struct vehicle_prototype; using vproto_id = string_id; +struct weather_type; +using weather_type_id = string_id; + class zone_type; using zone_type_id = string_id; diff --git a/src/ui.cpp b/src/ui.cpp index 41eed88461d57..0f344af8d2e90 100644 --- a/src/ui.cpp +++ b/src/ui.cpp @@ -917,10 +917,11 @@ struct pointmenu_cb::impl_t { pointmenu_cb::impl_t::impl_t( const std::vector &pts ) : points( pts ) { last = INT_MIN; - last_view = g->u.view_offset; - terrain_draw_cb = make_shared_fast( [this]() { + avatar &player_character = get_avatar(); + last_view = player_character.view_offset; + terrain_draw_cb = make_shared_fast( [this, &player_character]() { if( last >= 0 && static_cast( last ) < points.size() ) { - g->draw_trail_to_square( g->u.view_offset, true ); + g->draw_trail_to_square( player_character.view_offset, true ); } } ); g->add_draw_callback( terrain_draw_cb ); @@ -928,7 +929,7 @@ pointmenu_cb::impl_t::impl_t( const std::vector &pts ) : points( pts ) pointmenu_cb::impl_t::~impl_t() { - g->u.view_offset = last_view; + get_avatar().view_offset = last_view; } void pointmenu_cb::impl_t::select( uilist *const menu ) @@ -937,13 +938,14 @@ void pointmenu_cb::impl_t::select( uilist *const menu ) return; } last = menu->selected; + avatar &player_character = get_avatar(); if( menu->selected < 0 || menu->selected >= static_cast( points.size() ) ) { - g->u.view_offset = tripoint_zero; + player_character.view_offset = tripoint_zero; } else { const tripoint ¢er = points[menu->selected]; - g->u.view_offset = center - g->u.pos(); + player_character.view_offset = center - player_character.pos(); // TODO: Remove this line when it's safe - g->u.view_offset.z = 0; + player_character.view_offset.z = 0; } g->invalidate_main_ui_adaptor(); } diff --git a/src/veh_interact.cpp b/src/veh_interact.cpp index 051bb37277794..9980fc2b07f0c 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.add_vehicle_to_cache( veh ); break; } @@ -3115,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 ); } @@ -3181,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(); - g->m.destroy_vehicle( veh ); + // 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(); - g->m.update_vehicle_cache( veh, veh->sm_pos.z ); + if( veh->parts_at_relative( mount, true ).empty() ) { + get_map().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() ) { @@ -3193,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 f4954245dc703..b129659941fc0 100644 --- a/src/vehicle.cpp +++ b/src/vehicle.cpp @@ -78,6 +78,7 @@ static const itype_id fuel_type_battery( "battery" ); static const itype_id fuel_type_muscle( "muscle" ); static const itype_id fuel_type_plutonium_cell( "plut_cell" ); static const itype_id fuel_type_wind( "wind" ); +static const itype_id fuel_type_mana( "mana" ); static const fault_id fault_engine_belt_drive( "fault_engine_belt_drive" ); static const fault_id fault_engine_filter_air( "fault_engine_filter_air" ); @@ -98,12 +99,11 @@ static const itype_id itype_water_clean( "water_clean" ); static const itype_id itype_water_purifier( "water_purifier" ); static const std::string flag_PERPETUAL( "PERPETUAL" ); +static const std::string flag_E_COMBUSTION( "E_COMBUSTION" ); static bool is_sm_tile_outside( const tripoint &real_global_pos ); static bool is_sm_tile_over_water( const tripoint &real_global_pos ); -static const itype_id fuel_type_mana( "mana" ); - // 1 kJ per battery charge const int bat_energy_j = 1000; @@ -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,19 +268,20 @@ 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 && + p.controlling_vehicle && ( ( part_with_feature( vp->part_index(), "CONTROL_ANIMAL", true ) >= 0 && has_engine_type( fuel_type_animal, false ) && has_harnessed_animal() ) || - ( part_with_feature( vp->part_index(), VPFLAG_CONTROLS, false ) >= 0 ) ) && - p.controlling_vehicle ) { + ( part_with_feature( vp->part_index(), VPFLAG_CONTROLS, false ) >= 0 ) ) + ) { return true; } 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 +705,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 +722,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 +752,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 +772,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 +833,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 +906,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 +936,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 +953,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 +993,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 ); } @@ -1023,11 +1033,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.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 ) * part.info().durability ), DT_BASH ) ) { + hp_percent_loss_max * dist ) * + part.info().durability ), DT_BASH ) ) { part.ammo_unset(); } } @@ -1056,7 +1068,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 ); @@ -1137,6 +1149,11 @@ bool vehicle::is_engine_type( const int e, const itype_id &ft ) const parts[engines[e]].ammo_current() == ft; } +bool vehicle::is_combustion_engine_type( const int e ) const +{ + return parts[engines[e]].info().has_flag( flag_E_COMBUSTION ); +} + bool vehicle::is_perpetual_type( const int e ) const { const itype_id &ft = part_info( engines[e] ).fuel_type; @@ -1163,8 +1180,9 @@ bool vehicle::is_alternator_on( const int a ) const return std::any_of( engines.begin(), engines.end(), [this, &alt]( int idx ) { auto &eng = parts [ idx ]; //fuel_left checks that the engine can produce power to be absorbed - return eng.is_available() && eng.enabled && fuel_left( eng.fuel_current() ) && - eng.mount == alt.mount && !eng.faults().count( fault_engine_belt_drive ); + return eng.mount == alt.mount && eng.is_available() && eng.enabled && + fuel_left( eng.fuel_current() ) && + !eng.faults().count( fault_engine_belt_drive ); } ); } @@ -1224,12 +1242,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 ); @@ -1679,6 +1698,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 ); @@ -1691,7 +1711,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 ] ) { @@ -1848,11 +1868,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 @@ -1990,12 +2011,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 { @@ -2006,10 +2030,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(); @@ -2089,7 +2113,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; @@ -2149,7 +2174,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 @@ -2270,6 +2295,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() ) { @@ -2302,7 +2328,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; @@ -2396,8 +2422,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; } @@ -2610,7 +2636,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 +2652,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 +2672,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 +2962,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() ); } } @@ -2988,13 +3014,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; } @@ -3030,12 +3057,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 +3070,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 +3080,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 +3092,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 +3103,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; @@ -3152,11 +3179,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; + const point old_msp = get_map().getabs( global_pos3().xy() ); + sm_pos = p; if( !tracking_on ) { return; } @@ -3211,22 +3237,25 @@ 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 { - int fl = std::accumulate( parts.begin(), parts.end(), 0, [&ftype]( const int &lhs, - const vehicle_part & rhs ) { - // don't count frozen liquid - if( rhs.is_tank() && !rhs.base.contents.empty() && - rhs.base.contents.legacy_front().made_of( phase_id::SOLID ) ) { - return lhs; + int fl = 0; + + for( const int i : fuel_containers ) { + const vehicle_part &part = parts[i]; + if( part.ammo_current() != ftype || + // don't count frozen liquid + ( !part.base.contents.empty() && part.is_tank() && + part.base.contents.legacy_front().made_of( phase_id::SOLID ) ) ) { + continue; } - return lhs + ( rhs.ammo_current() == ftype ? rhs.ammo_remaining() : 0 ); - } ); + fl += part.ammo_remaining(); + } if( recurse && ftype == fuel_type_battery ) { auto fuel_counting_visitor = [&]( vehicle const * veh, int amount, int ) { @@ -3241,18 +3270,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; } } @@ -3449,10 +3480,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; } @@ -3692,14 +3724,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 ) { + if( get_weather().weather_id->rains && + get_weather().weather_id->precip != precip_class::very_light ) { vp.part().blood--; } } @@ -3714,7 +3748,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 ); } /** @@ -4092,7 +4126,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 @@ -4416,7 +4450,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 @@ -4428,6 +4462,32 @@ float vehicle::handling_difficulty() const return velocity * diff_mod / vehicles::vmiph_per_tile; } + +int vehicle::engine_fuel_usage( int e ) const +{ + if( !is_engine_on( e ) ) { + return 0; + } + + static const itype_id null_fuel_type( "null" ); + const itype_id &cur_fuel = parts[engines[e]].fuel_current(); + if( cur_fuel == null_fuel_type ) { + return 0; + } + + if( is_perpetual_type( e ) ) { + return 0; + } + const auto &info = part_info( engines[ e ] ); + + int usage = info.energy_consumption; + if( parts[ engines[ e ] ].faults().count( fault_engine_filter_air ) ) { + usage *= 2; + } + + return usage; +} + std::map vehicle::fuel_usage() const { std::map ret; @@ -4439,7 +4499,6 @@ std::map vehicle::fuel_usage() const } const size_t e = engines[ i ]; - const auto &info = part_info( e ); static const itype_id null_fuel_type( "null" ); const itype_id &cur_fuel = parts[ e ].fuel_current(); if( cur_fuel == null_fuel_type ) { @@ -4447,12 +4506,7 @@ std::map vehicle::fuel_usage() const } if( !is_perpetual_type( i ) ) { - int usage = info.energy_consumption; - if( parts[ e ].faults().count( fault_engine_filter_air ) ) { - usage *= 2; - } - - ret[ cur_fuel ] += usage; + ret[cur_fuel] += engine_fuel_usage( i ); } } @@ -4498,7 +4552,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(); @@ -4506,7 +4562,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; @@ -4516,28 +4572,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 ) ); @@ -4564,6 +4622,43 @@ int vehicle::total_accessory_epower_w() const return epower; } +std::pair vehicle::battery_power_level() const +{ + int total_epower_capacity = 0; + int remaining_epower = 0; + + for( const int bi : batteries ) { + const vehicle_part &b = parts[bi]; + if( b.is_available() ) { + remaining_epower += b.ammo_remaining(); + total_epower_capacity += b.ammo_capacity( ammotype( "battery" ) ); + } + } + + return std::make_pair( remaining_epower, total_epower_capacity ); +} + +bool vehicle::start_engine( int e, bool turn_on ) +{ + if( parts[engines[e]].enabled == turn_on ) { + return false; + } + bool res = false; + if( turn_on ) { + toggle_specific_engine( e, true ); + // prevent starting of the faulty engines + if( ! start_engine( e ) ) { + toggle_specific_engine( e, false ); + } else { + res = true; + } + } else { + toggle_specific_engine( e, false ); + res = true; + } + return res; +} + int vehicle::total_alternator_epower_w() const { int epower = 0; @@ -4599,12 +4694,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; } @@ -4612,7 +4708,7 @@ int vehicle::total_solar_epower_w() const } // Weather doesn't change much across the area of the vehicle, so just // sample it once. - weather_type wtype = current_weather( global_pos3() ); + weather_type_id wtype = current_weather( global_pos3() ); const float tick_sunlight = incident_sunlight( wtype, calendar::turn ); double intensity = tick_sunlight / default_daylight_level(); return epower_w * intensity; @@ -4620,21 +4716,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; @@ -4645,12 +4743,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; } @@ -4708,8 +4807,10 @@ void vehicle::power_parts() int epower = engine_epower + total_accessory_epower_w() + total_alternator_epower_w(); 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 ); + int battery_left, battery_capacity; + std::tie( battery_left, battery_capacity ) = battery_power_level(); + int storage_deficit_bat = std::max( 0, battery_capacity - battery_left - 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 ) { @@ -4754,7 +4855,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 ); } } @@ -4788,13 +4889,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 ); } } @@ -4803,9 +4904,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(); } @@ -4859,7 +4961,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" ) ) { @@ -4880,12 +4982,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. @@ -4925,7 +5026,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 ); }; @@ -4962,7 +5063,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! @@ -4989,7 +5090,10 @@ void vehicle::do_engine_damage( size_t e, int strain ) void vehicle::idle( bool on_map ) { + avg_velocity = ( velocity + avg_velocity ) / 2; + power_parts(); + Character &player_character = get_player_character(); if( engine_on && total_power_w() > 0 ) { int idle_rate = alternator_load; if( idle_rate < 10 ) { @@ -5008,7 +5112,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 ); @@ -5016,15 +5120,17 @@ 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; } } + smart_controller_handle_turn(); + if( !on_map ) { return; } else { @@ -5068,6 +5174,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(); @@ -5083,12 +5190,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 ) ); @@ -5317,7 +5424,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(); @@ -5371,9 +5478,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(); } @@ -5388,10 +5496,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; @@ -5453,6 +5561,9 @@ void vehicle::refresh() steering.clear(); speciality.clear(); floating.clear(); + batteries.clear(); + fuel_containers.clear(); + alternator_load = 0; extra_drag = 0; all_wheels_on_one_axis = true; @@ -5476,6 +5587,9 @@ void vehicle::refresh() int railwheel_xmax = INT_MIN; int railwheel_ymax = INT_MIN; + has_enabled_smart_controller = false; + smart_controller_state = cata::nullopt; + bool refresh_done = false; // Main loop over all vehicle parts. @@ -5522,6 +5636,12 @@ void vehicle::refresh() if( vpi.has_flag( VPFLAG_ROTOR ) || vpi.has_flag( VPFLAG_ROTOR_SIMPLE ) ) { rotors.push_back( p ); } + if( vp.part().is_battery() ) { + batteries.push_back( p ); + } + if( vp.part().is_fuel_store( false ) ) { + fuel_containers.push_back( p ); + } if( vpi.has_flag( "WIND_TURBINE" ) ) { wind_turbines.push_back( p ); } @@ -5543,6 +5663,9 @@ void vehicle::refresh() if( vpi.has_flag( VPFLAG_WHEEL ) ) { wheelcache.push_back( p ); } + if( vpi.has_flag( "SMART_ENGINE_CONTROLLER" ) && vp.part().enabled ) { + has_enabled_smart_controller = true; + } if( vpi.has_flag( VPFLAG_WHEEL ) && vpi.has_flag( VPFLAG_RAIL ) ) { rail_wheelcache.push_back( p ); if( first_wheel_y_mount == INT_MAX ) { @@ -5748,13 +5871,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; @@ -5777,21 +5901,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 ) ); } @@ -5799,8 +5923,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; } @@ -5920,6 +6045,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 ) { @@ -5930,7 +6056,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; @@ -5951,7 +6077,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() ); @@ -5962,7 +6089,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; @@ -5982,7 +6109,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() ); @@ -5993,7 +6121,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; @@ -6008,7 +6136,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]; @@ -6024,6 +6152,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() ) { @@ -6043,7 +6172,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 ); @@ -6122,9 +6251,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 ) ); } } @@ -6257,7 +6387,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 ); } /** @@ -6301,20 +6431,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 ); @@ -6326,26 +6458,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] ); @@ -6353,7 +6485,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() ); } @@ -6402,7 +6534,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 ); } @@ -6431,7 +6564,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(); @@ -6445,7 +6578,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 ); @@ -6467,10 +6600,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 ); + std::vector tiles = closest_points_first( global_part_pos3( pt ), 1 ); + 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 @@ -6479,7 +6613,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 ) ); } } @@ -6623,6 +6757,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]; @@ -6630,13 +6765,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 ); @@ -6666,7 +6801,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" ); @@ -6675,7 +6810,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; } @@ -6713,7 +6848,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; } @@ -6830,7 +6965,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; } @@ -6880,23 +7015,74 @@ 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, const bool adjust_pos, + std::set parts_to_move ) +{ + 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. + // 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] ); + // 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] ) ) { + 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 ); + } + if( adjust_pos ) { + if( parts_to_move.empty() ) { + pivot_anchor[0] = pivot_anchor[1]; + pivot_rotation[0] = pivot_rotation[1]; + } + pos = new_pos; } - 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() { if( zones_dirty ) { + map &here = get_map(); decltype( loot_zones ) new_zones; for( auto const &z : loot_zones ) { zone_data zone = z.second; @@ -6912,7 +7098,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 52f4dbdd9a512..eeb2a82f6aad0 100644 --- a/src/vehicle.h +++ b/src/vehicle.h @@ -101,6 +101,16 @@ enum veh_coll_type : int { num_veh_coll_types }; +struct smart_controller_cache { + time_point created = calendar::turn; + time_point gas_engine_last_turned_on = calendar::start_of_cataclysm; + bool gas_engine_shutdown_forbidden; + int velocity; + int battery_percent; + int battery_net_charge_rate; + float load; +}; + struct veh_collision { //int veh? int part = 0; @@ -365,7 +375,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; @@ -571,6 +581,9 @@ struct label : public point { label( const point &p, std::string text ) : point( p ), text( std::move( text ) ) {} std::string text; + + void deserialize( JsonIn &jsin ); + void serialize( JsonOut &json ) const; }; class RemovePartHandler; @@ -758,9 +771,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 ); @@ -1042,10 +1055,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; @@ -1146,6 +1159,8 @@ class vehicle */ std::map fuel_usage() const; + // current fuel usage for specific engine + int engine_fuel_usage( int e ) const; /** * Get all vehicle lights (excluding any that are destroyed) * @param active if true return only lights which are enabled @@ -1175,6 +1190,9 @@ class vehicle // taken from batteries. void power_parts(); + // Current and total battery power level as a pair + std::pair battery_power_level() const; + /** * Try to charge our (and, optionally, connected vehicles') batteries by the given amount. * @return amount of charge left over. @@ -1387,6 +1405,14 @@ class vehicle // @param z = z thrust for helicopters etc void thrust( int thd, int z = 0 ); + /** + * if smart controller is enabled, turns on and off engines depending on load and battery level + * @param thrusting must be true when called from vehicle::thrust and vehicle is thrusting + * @param k_traction_cache cached value of vehicle::k_traction, if empty, will be computed + */ + void smart_controller_handle_turn( bool thrusting = false, + cata::optional k_traction_cache = cata::nullopt ); + //deceleration due to ground friction and air resistance int slowdown( int velocity ) const; @@ -1635,10 +1661,16 @@ class vehicle bool is_part_on( int p ) const; //returns whether the engine uses specified fuel type bool is_engine_type( int e, const itype_id &ft ) const; + //returns whether the engine uses one of specific "combustion" fuel types (gas, diesel and diesel substitutes) + bool is_combustion_engine_type( int e ) const; //returns whether the alternator is operational bool is_alternator_on( int a ) const; - //mark engine as on or off + //turn engine as on or off (note: doesn't perform checks if engine can start) void toggle_specific_engine( int e, bool on ); + // try to turn engine on or off + // (tries to start it and toggles it on if successful, shutdown is always a success) + // returns true if engine status was changed + bool start_engine( int e, bool turn_on ); void toggle_specific_part( int p, bool on ); //true if an engine exists with specified type //If enabled true, this engine must be enabled to return true @@ -1692,7 +1724,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 +1776,11 @@ 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, + 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 @@ -1764,6 +1800,8 @@ class vehicle // List of parts that will not be on a vehicle very often, or which only one will be present std::vector speciality; std::vector floating; // List of parts that provide buoyancy to boats + std::vector batteries; // List of batteries + std::vector fuel_containers; // List parts with non-null ammo_type // config values std::string name; // vehicle name @@ -1786,6 +1824,9 @@ class vehicle bool magic = false; // when does the magic vehicle disappear? cata::optional summon_time_limit = cata::nullopt; + // cached values of the factors that determined last chosen engine state + cata::optional smart_controller_state = cata::nullopt; + bool has_enabled_smart_controller = false; private: mutable units::mass mass_cache; @@ -1834,6 +1875,12 @@ class vehicle point pos; // vehicle current velocity, mph * 100 int velocity = 0; + /** + * vehicle continuous moving average velocity + * see https://en.wikipedia.org/wiki/Moving_average#Exponential_moving_average + * alpha is 0.5: avg_velocity = avg_velocity + 0.5(velocity - avg_velocity) = 0.5 avg_velocity + 0.5 velocity + */ + int avg_velocity = 0; // velocity vehicle's cruise control trying to achieve int cruise_velocity = 0; // Only used for collisions, vehicle falls instantly 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 ) { diff --git a/src/vehicle_move.cpp b/src/vehicle_move.cpp index 282963a234ddd..f1056feca21f4 100644 --- a/src/vehicle_move.cpp +++ b/src/vehicle_move.cpp @@ -35,6 +35,7 @@ #include "enums.h" #include "int_id.h" #include "monster.h" +#include "vpart_range.h" #define dbg(x) DebugLog((x),D_MAP) << __FILE__ << ":" << __LINE__ << ": " @@ -116,6 +117,263 @@ int vehicle::slowdown( int at_velocity ) const return std::max( 1, slowdown ); } +void vehicle:: smart_controller_handle_turn( bool thrusting, + cata::optional k_traction_cache ) +{ + + if( !engine_on || !has_enabled_smart_controller ) { + smart_controller_state = cata::nullopt; + return; + } + + if( smart_controller_state && smart_controller_state->created == calendar::turn ) { + return; + } + + // controlled engines + // note: contains indices of of elements in `engines` array, not the part ids + std::vector c_engines; + for( int i = 0; i < static_cast( engines.size() ); ++i ) { + if( ( is_engine_type( i, fuel_type_battery ) || is_combustion_engine_type( i ) ) && + ( ( parts[ engines[ i ] ].is_available() && engine_fuel_left( i ) > 0 ) || + is_part_on( engines[ i ] ) ) ) { + c_engines.push_back( i ); + } + } + + bool rotorcraft = is_flying && is_rotorcraft(); + + if( rotorcraft || c_engines.size() <= 1 || c_engines.size() > 5 ) { // bail and shut down + for( const vpart_reference &vp : get_avail_parts( "SMART_ENGINE_CONTROLLER" ) ) { + vp.part().enabled = false; + } + + if( player_in_control( g->u ) ) { + if( rotorcraft ) { + add_msg( _( "Smart controller does not support flying vehicles." ) ); + } else if( c_engines.size() <= 1 ) { + add_msg( _( "Smart controller detects only a single controllable engine." ) ); + add_msg( _( "Smart controller is designed to control more than one engine." ) ); + } else { + add_msg( _( "Smart controller does not support more than five engines." ) ); + } + add_msg( m_bad, _( "Smart controller is shutting down." ) ); + } + has_enabled_smart_controller = false; + smart_controller_state = cata::nullopt; + return; + } + + int cur_battery_level, max_battery_level; + std::tie( cur_battery_level, max_battery_level ) = battery_power_level(); + int battery_level_percent = max_battery_level == 0 ? 0 : cur_battery_level * 100 / + max_battery_level; + + // when battery > 90%, discharge is allowed + // otherwise trying to charge battery to 90% within 30 minutes + bool discharge_forbidden_soft = battery_level_percent <= 90; + bool discharge_forbidden_hard = battery_level_percent <= 25; + int target_charging_rate = ( max_battery_level == 0 || !discharge_forbidden_soft ) ? 0 : + ( 9 * max_battery_level - 10 * cur_battery_level ) / ( 6 * 3 ); + // ( max_battery_level * 0.9 - cur_battery_level ) * (1000 / (60 * 30)) // originally + // ^ 90% bat to W ^ ^ 30 minutes + + int accel_demand = cruise_on + ? // using avg_velocity reduces unnecessary oscillations when traction is low + std::max( std::abs( cruise_velocity - velocity ), std::abs( cruise_velocity - avg_velocity ) ) : + ( thrusting ? 1000 : 0 ); + if( velocity != 0 && accel_demand == 0 ) { + accel_demand = 1; // to prevent zero fuel usage + } + + int velocity_demand = std::max( std::abs( this->velocity ), std::abs( cruise_velocity ) ); + + // for stationary vehicles all velocity and acceleration calculations are skipped + bool is_stationary = avg_velocity == 0 && velocity_demand == 0 && accel_demand == 0; + + bool gas_engine_shutdown_forbidden = smart_controller_state && + ( calendar::turn - smart_controller_state->gas_engine_last_turned_on ) < + 15_seconds; + + + smart_controller_cache cur_state; + + float traction = is_stationary ? 1.0f : + ( k_traction_cache ? *k_traction_cache : k_traction( get_map().vehicle_wheel_traction( *this ) ) ); + + int prev_mask = 0; + // opt_ prefix denotes values for currently found "optimal" engine configuration + int opt_net_echarge_rate = net_battery_charge_rate_w(); + // total engine fuel energy usage (J) + int opt_fuel_usage = 0; + + int opt_accel = is_stationary ? 1 : current_acceleration() * traction; + int opt_safe_vel = is_stationary ? 1 : safe_ground_velocity( true ); + float cur_load_approx = static_cast( std::min( accel_demand, + opt_accel ) ) / std::max( opt_accel, 1 ); + float cur_load_alternator = std::min( 0.01f, static_cast( alternator_load ) / 1000 ); + + for( size_t i = 0; i < c_engines.size(); ++i ) { + if( is_engine_on( c_engines[i] ) ) { + prev_mask |= 1 << i; + bool is_electric = is_engine_type( c_engines[i], fuel_type_battery ); + int fu = engine_fuel_usage( c_engines[i] ) * ( cur_load_approx + ( is_electric ? 0 : + cur_load_alternator ) ); + opt_fuel_usage += fu; + if( is_electric ) { + opt_net_echarge_rate -= fu; + } + } + } + cur_state.created = calendar::turn; + cur_state.battery_percent = battery_level_percent; + cur_state.battery_net_charge_rate = opt_net_echarge_rate; + cur_state.velocity = avg_velocity; + cur_state.load = cur_load_approx + cur_load_alternator; + if( smart_controller_state ) { + cur_state.gas_engine_last_turned_on = smart_controller_state->gas_engine_last_turned_on; + } + cur_state.gas_engine_shutdown_forbidden = gas_engine_shutdown_forbidden; + + int opt_mask = prev_mask; // save current engine state, because it will be temporarily modified + + // if vehicle state has not change, skip actual optimization + if( smart_controller_state && + std::abs( smart_controller_state->velocity - cur_state.velocity ) < 100 && + std::abs( smart_controller_state->battery_percent - cur_state.battery_percent ) <= 2 && + std::abs( smart_controller_state->load - cur_state.load ) < 0.1 && // load diff < 10% + smart_controller_state->battery_net_charge_rate == cur_state.battery_net_charge_rate && + // reevaluate cache if when cache was created, gas engine shutdown was forbidden, but now it's not + !( smart_controller_state->gas_engine_shutdown_forbidden && !gas_engine_shutdown_forbidden ) + ) { + smart_controller_state->created = calendar::turn; + return; + } + + // trying all combinations of engine state (max 31 iterations for 5 engines) + for( int mask = 1; mask < static_cast( 1 << c_engines.size() ); ++mask ) { + if( mask == prev_mask ) { + continue; + } + + bool gas_engine_to_shut_down = false; + for( size_t i = 0; i < c_engines.size(); ++i ) { + bool old_state = ( prev_mask & ( 1 << i ) ) != 0; + bool new_state = ( mask & ( 1 << i ) ) != 0; + // switching enabled flag temporarily to perform calculations below + toggle_specific_engine( c_engines[i], new_state ); + + if( old_state && !new_state && !is_engine_type( c_engines[i], fuel_type_battery ) ) { + gas_engine_to_shut_down = true; + } + } + + if( gas_engine_to_shut_down && gas_engine_shutdown_forbidden ) { + continue; // skip checking this state + } + + int safe_vel = is_stationary ? 1 : safe_ground_velocity( true ); + int accel = is_stationary ? 1 : current_acceleration() * traction; + int fuel_usage = 0; + int net_echarge_rate = net_battery_charge_rate_w(); + float load_approx = static_cast( std::min( accel_demand, accel ) ) / std::max( accel, 1 ); + update_alternator_load(); + float load_approx_alternator = std::min( 0.01f, static_cast( alternator_load ) / 1000 ); + + for( int e : c_engines ) { + bool is_electric = is_engine_type( e, fuel_type_battery ); + int fu = engine_fuel_usage( e ) * ( load_approx + ( is_electric ? 0 : load_approx_alternator ) ); + fuel_usage += fu; + if( is_electric ) { + net_echarge_rate -= fu; + } + } + + if( std::forward_as_tuple( + !discharge_forbidden_hard || ( net_echarge_rate > 0 ), + accel >= accel_demand, + opt_accel < accel_demand ? accel : 0, // opt_accel usage here is intentional + safe_vel >= velocity_demand, + opt_safe_vel < velocity_demand ? -safe_vel : 0, //opt_safe_vel usage here is intentional + !discharge_forbidden_soft || ( net_echarge_rate > target_charging_rate ), + -fuel_usage, + net_echarge_rate + ) >= std::forward_as_tuple( + !discharge_forbidden_hard || ( opt_net_echarge_rate > 0 ), + opt_accel >= accel_demand, + opt_accel < accel_demand ? opt_accel : 0, + opt_safe_vel >= velocity_demand, + opt_safe_vel < velocity_demand ? -opt_safe_vel : 0, + !discharge_forbidden_soft || ( opt_net_echarge_rate > target_charging_rate ), + -opt_fuel_usage, + opt_net_echarge_rate + ) ) { + opt_mask = mask; + opt_fuel_usage = fuel_usage; + opt_net_echarge_rate = net_echarge_rate; + opt_accel = accel; + opt_safe_vel = safe_vel; + + cur_state.battery_net_charge_rate = net_echarge_rate; + cur_state.load = load_approx + load_approx_alternator; + // other `cur_state` fields do not change for different engine state combinations + } + } + + for( size_t i = 0; i < c_engines.size(); ++i ) { // return to prev state + toggle_specific_engine( c_engines[i], static_cast( prev_mask & ( 1 << i ) ) ); + } + + if( opt_mask != prev_mask ) { // we found new configuration + bool failed_to_start = false; + bool turned_on_gas_engine = false; + for( size_t i = 0; i < c_engines.size(); ++i ) { + // ..0.. < ..1.. was off, new state on + if( ( prev_mask & ( 1 << i ) ) < ( opt_mask & ( 1 << i ) ) ) { + if( !start_engine( c_engines[i], true ) ) { + failed_to_start = true; + } + turned_on_gas_engine |= !is_engine_type( c_engines[i], fuel_type_battery ); + } + } + if( failed_to_start ) { + this->smart_controller_state = cata::nullopt; + + for( size_t i = 0; i < c_engines.size(); ++i ) { // return to prev state + toggle_specific_engine( c_engines[i], static_cast( prev_mask & ( 1 << i ) ) ); + } + for( const vpart_reference &vp : get_avail_parts( "SMART_ENGINE_CONTROLLER" ) ) { + vp.part().enabled = false; + } + if( player_in_control( g->u ) ) { + add_msg( m_bad, _( "Smart controller failed to start an engine." ) ); + add_msg( m_bad, _( "Smart controller is shutting down." ) ); + } + has_enabled_smart_controller = false; + + } else { //successfully changed engines state + for( size_t i = 0; i < c_engines.size(); ++i ) { + // was on, needs to be off + if( ( prev_mask & ( 1 << i ) ) > ( opt_mask & ( 1 << i ) ) ) { + start_engine( c_engines[i], false ); + } + } + if( turned_on_gas_engine ) { + cur_state.gas_engine_last_turned_on = calendar::turn; + } + smart_controller_state = cur_state; + + if( player_in_control( g->u ) ) { + add_msg( m_debug, _( "Smart controller optimizes engine state." ) ); + } + } + } else { + // as the optimization was performed (even without state change), cache needs to be updated as well + smart_controller_state = cur_state; + } + update_alternator_load(); +} + void vehicle::thrust( int thd, int z ) { //if vehicle is stopped, set target direction to forward. @@ -124,7 +382,7 @@ void vehicle::thrust( int thd, int z ) turn_dir = face.dir(); stop(); } - bool pl_ctrl = player_in_control( g->u ); + bool pl_ctrl = player_in_control( get_player_character() ); // No need to change velocity if there are no wheels if( ( in_water && can_float() ) || ( is_rotorcraft() && ( z != 0 || is_flying ) ) ) { @@ -150,7 +408,12 @@ void vehicle::thrust( int thd, int z ) } // TODO: Pass this as an argument to avoid recalculating - float traction = k_traction( g->m.vehicle_wheel_traction( *this ) ); + float traction = k_traction( get_map().vehicle_wheel_traction( *this ) ); + + if( thrusting ) { + smart_controller_handle_turn( true, traction ); + } + int accel = current_acceleration() * traction; if( accel < 200 && velocity > 0 && is_towing() ) { if( pl_ctrl ) { @@ -358,8 +621,9 @@ void vehicle::stop( bool update_cache ) if( !update_cache ) { return; } + map &here = get_map(); for( const tripoint &p : get_points() ) { - g->m.set_memory_seen_cache_dirty( p ); + here.set_memory_seen_cache_dirty( p ); } } @@ -407,6 +671,7 @@ bool vehicle::collision( std::vector &colls, int lowest_velocity = coll_velocity; const int sign_before = sgn( velocity_before ); bool empty = true; + map &here = get_map(); for( int p = 0; static_cast( p ) < parts.size(); p++ ) { const vpart_info &info = part_info( p ); if( ( info.location != part_location_structure && info.rotor_diameter() == 0 ) || @@ -420,7 +685,7 @@ bool vehicle::collision( std::vector &colls, veh_collision coll = part_collision( p, dsp, just_detect, bash_floor ); if( coll.type == veh_coll_nothing && info.rotor_diameter() > 0 ) { size_t radius = static_cast( std::round( info.rotor_diameter() / 2.0f ) ); - for( const tripoint &rotor_point : g->m.points_in_radius( dsp, radius ) ) { + for( const tripoint &rotor_point : here.points_in_radius( dsp, radius ) ) { veh_collision rotor_coll = part_collision( p, rotor_point, just_detect, false ); if( rotor_coll.type != veh_coll_nothing ) { coll = rotor_coll; @@ -485,9 +750,10 @@ static void terrain_collision_data( const tripoint &p, bool bash_floor, float &mass, float &density, float &elastic ) { elastic = 0.30; + map &here = get_map(); // Just a rough rescale for now to obtain approximately equal numbers - const int bash_min = g->m.bash_resistance( p, bash_floor ); - const int bash_max = g->m.bash_strength( p, bash_floor ); + const int bash_min = here.bash_resistance( p, bash_floor ); + const int bash_max = here.bash_strength( p, bash_floor ); mass = ( bash_min + bash_max ) / 2.0; density = bash_min; } @@ -498,11 +764,12 @@ veh_collision vehicle::part_collision( int part, const tripoint &p, // Vertical collisions need to be handled differently // All collisions have to be either fully vertical or fully horizontal for now const bool vert_coll = bash_floor || p.z != sm_pos.z; - const bool pl_ctrl = player_in_control( g->u ); + Character &player_character = get_player_character(); + const bool pl_ctrl = player_in_control( player_character ); Creature *critter = g->critter_at( p, true ); player *ph = dynamic_cast( critter ); - Creature *driver = pl_ctrl ? &g->u : nullptr; + Creature *driver = pl_ctrl ? &player_character : nullptr; // If in a vehicle assume it's this one if( ph != nullptr && ph->in_vehicle ) { @@ -510,7 +777,8 @@ veh_collision vehicle::part_collision( int part, const tripoint &p, ph = nullptr; } - const optional_vpart_position ovp = g->m.veh_at( p ); + map &here = get_map(); + const optional_vpart_position ovp = here.veh_at( p ); // Disable vehicle/critter collisions when bashing floor // TODO: More elegant code const bool is_veh_collision = !bash_floor && ovp && &ovp->vehicle() != this; @@ -547,7 +815,7 @@ veh_collision vehicle::part_collision( int part, const tripoint &p, return ret; } // we just ran into a fish, so move it out of the way - if( g->m.has_flag( "SWIMMABLE", critter->pos() ) ) { + if( here.has_flag( "SWIMMABLE", critter->pos() ) ) { tripoint end_pos = critter->pos(); tripoint start_pos; const int angle = move.dir() + 45 * ( parts[part].mount.x > pivot_point().x ? -1 : 1 ); @@ -589,31 +857,31 @@ veh_collision vehicle::part_collision( int part, const tripoint &p, part_dens = 15; mass2 = units::to_kilogram( critter->get_weight() ); ret.target_name = critter->disp_name(); - } else if( ( bash_floor && g->m.is_bashable_ter_furn( p, true ) ) || - ( g->m.is_bashable_ter_furn( p, false ) && g->m.move_cost_ter_furn( p ) != 2 && + } else if( ( bash_floor && here.is_bashable_ter_furn( p, true ) ) || + ( here.is_bashable_ter_furn( p, false ) && here.move_cost_ter_furn( p ) != 2 && // Don't collide with tiny things, like flowers, unless we have a wheel in our space. ( part_with_feature( ret.part, VPFLAG_WHEEL, true ) >= 0 || - !g->m.has_flag_ter_or_furn( "TINY", p ) ) && + !here.has_flag_ter_or_furn( "TINY", p ) ) && // Protrusions don't collide with short terrain. // Tiny also doesn't, but it's already excluded unless there's a wheel present. !( part_with_feature( ret.part, "PROTRUSION", true ) >= 0 && - g->m.has_flag_ter_or_furn( "SHORT", p ) ) && + here.has_flag_ter_or_furn( "SHORT", p ) ) && // These are bashable, but don't interact with vehicles. - !g->m.has_flag_ter_or_furn( "NOCOLLIDE", p ) && + !here.has_flag_ter_or_furn( "NOCOLLIDE", p ) && // Do not collide with track tiles if we can use rails - !( g->m.has_flag_ter_or_furn( TFLAG_RAIL, p ) && this->can_use_rails() ) ) ) { + !( here.has_flag_ter_or_furn( TFLAG_RAIL, p ) && this->can_use_rails() ) ) ) { // Movecost 2 indicates flat terrain like a floor, no collision there. ret.type = veh_coll_bashable; terrain_collision_data( p, bash_floor, mass2, part_dens, e ); - ret.target_name = g->m.disp_name( p ); - } else if( g->m.impassable_ter_furn( p ) || - ( bash_floor && !g->m.has_flag( TFLAG_NO_FLOOR, p ) ) ) { + ret.target_name = here.disp_name( p ); + } else if( here.impassable_ter_furn( p ) || + ( bash_floor && !here.has_flag( TFLAG_NO_FLOOR, p ) ) ) { // not destructible ret.type = veh_coll_other; mass2 = 1000; e = 0.10; part_dens = 80; - ret.target_name = g->m.disp_name( p ); + ret.target_name = here.disp_name( p ); } if( ret.type == veh_coll_nothing || just_detect ) { @@ -713,23 +981,23 @@ veh_collision vehicle::part_collision( int part, const tripoint &p, // Something bashable -- use map::bash to determine outcome // NOTE: Floor bashing disabled for balance reasons // Floor values are still used to set damage dealt to vehicle - smashed = g->m.is_bashable_ter_furn( p, false ) && - g->m.bash_resistance( p, bash_floor ) <= obj_dmg && - g->m.bash( p, obj_dmg, false, false, false, this ).success; + smashed = here.is_bashable_ter_furn( p, false ) && + here.bash_resistance( p, bash_floor ) <= obj_dmg && + here.bash( p, obj_dmg, false, false, false, this ).success; if( smashed ) { - if( g->m.is_bashable_ter_furn( p, bash_floor ) ) { + if( here.is_bashable_ter_furn( p, bash_floor ) ) { // There's new terrain there to smash smashed = false; terrain_collision_data( p, bash_floor, mass2, part_dens, e ); - ret.target_name = g->m.disp_name( p ); - } else if( g->m.impassable_ter_furn( p ) ) { + ret.target_name = here.disp_name( p ); + } else if( here.impassable_ter_furn( p ) ) { // There's new terrain there, but we can't smash it! smashed = false; ret.type = veh_coll_other; mass2 = 1000; e = 0.10; part_dens = 80; - ret.target_name = g->m.disp_name( p ); + ret.target_name = here.disp_name( p ); } } } else if( ret.type == veh_coll_body ) { @@ -870,10 +1138,10 @@ void vehicle::handle_trap( const tripoint &p, int part ) if( pwh < 0 ) { return; } - const trap &tr = g->m.tr_at( p ); - const trap_id t = tr.loadid; + map &here = get_map(); + const trap &tr = here.tr_at( p ); - if( t == tr_null ) { + if( tr.is_null() ) { // If the trap doesn't exist, we can't interact with it, so just return return; } @@ -883,8 +1151,9 @@ void vehicle::handle_trap( const tripoint &p, int part ) return; } - const bool seen = g->u.sees( p ); - const bool known = g->u.knows_trap( p ); + Character &player_character = get_player_character(); + const bool seen = player_character.sees( p ); + const bool known = tr.can_see( p, player_character ); if( seen ) { if( known ) { //~ %1$s: name of the vehicle; %2$s: name of the related vehicle part; %3$s: trap name @@ -907,28 +1176,28 @@ void vehicle::handle_trap( const tripoint &p, int part ) } bool still_has_trap = true; if( veh_data.remove_trap || veh_data.do_explosion ) { - g->m.remove_trap( p ); + here.remove_trap( p ); still_has_trap = false; } for( const auto &it : veh_data.spawn_items ) { int cnt = roll_remainder( it.second ); if( cnt > 0 ) { - g->m.spawn_item( p, it.first, cnt ); + here.spawn_item( p, it.first, cnt ); } } if( veh_data.set_trap ) { - g->m.trap_set( p, veh_data.set_trap.id() ); + here.trap_set( p, veh_data.set_trap.id() ); still_has_trap = true; } if( still_has_trap ) { - const trap &tr = g->m.tr_at( p ); + const trap &tr = here.tr_at( p ); if( seen || known ) { // known status has been reset by map::trap_set() - g->u.add_known_trap( p, tr ); + player_character.add_known_trap( p, tr ); } if( seen && !known ) { // hard to miss! - const std::string direction = direction_name( direction_from( g->u.pos(), p ) ); + const std::string direction = direction_name( direction_from( player_character.pos(), p ) ); add_msg( _( "You've spotted a %1$s to the %2$s!" ), tr.name(), direction ); } } @@ -1005,7 +1274,7 @@ bool vehicle::check_is_heli_landed() { // @TODO - when there are chasms that extend below z-level 0 - perhaps the heli // will be able to descend into them but for now, assume z-level-0 == the ground. - if( global_pos3().z == 0 || !g->m.has_flag_ter_or_furn( TFLAG_NO_FLOOR, global_pos3() ) ) { + if( global_pos3().z == 0 || !get_map().has_flag_ter_or_furn( TFLAG_NO_FLOOR, global_pos3() ) ) { is_flying = false; return true; } @@ -1020,20 +1289,21 @@ bool vehicle::check_heli_descend( player &p ) } int count = 0; int air_count = 0; + map &here = get_map(); for( const tripoint &pt : get_points( true ) ) { tripoint below( pt.xy(), pt.z - 1 ); - if( g->m.has_zlevels() && ( pt.z < -OVERMAP_DEPTH || - !g->m.has_flag_ter_or_furn( TFLAG_NO_FLOOR, pt ) ) ) { + if( here.has_zlevels() && ( pt.z < -OVERMAP_DEPTH || + !here.has_flag_ter_or_furn( TFLAG_NO_FLOOR, pt ) ) ) { p.add_msg_if_player( _( "You are already landed!" ) ); return false; } - const optional_vpart_position ovp = g->m.veh_at( below ); - if( g->m.impassable_ter_furn( below ) || ovp || g->critter_at( below ) ) { + const optional_vpart_position ovp = here.veh_at( below ); + if( here.impassable_ter_furn( below ) || ovp || g->critter_at( below ) ) { p.add_msg_if_player( m_bad, _( "It would be unsafe to try and land when there are obstacles below you." ) ); return false; } - if( g->m.has_flag_ter_or_furn( TFLAG_NO_FLOOR, below ) ) { + if( here.has_flag_ter_or_furn( TFLAG_NO_FLOOR, below ) ) { air_count++; } count++; @@ -1056,10 +1326,11 @@ bool vehicle::check_heli_ascend( player &p ) p.add_msg_if_player( m_bad, _( "It would be unsafe to try and take off while you are moving." ) ); return false; } + map &here = get_map(); for( const tripoint &pt : get_points( true ) ) { tripoint above( pt.xy(), pt.z + 1 ); - const optional_vpart_position ovp = g->m.veh_at( above ); - if( g->m.has_flag_ter_or_furn( TFLAG_INDOORS, pt ) || g->m.impassable_ter_furn( above ) || ovp || + const optional_vpart_position ovp = here.veh_at( above ); + if( here.has_flag_ter_or_furn( TFLAG_INDOORS, pt ) || here.impassable_ter_furn( above ) || ovp || g->critter_at( above ) ) { p.add_msg_if_player( m_bad, _( "It would be unsafe to try and ascend when there are obstacles above you." ) ); @@ -1071,9 +1342,9 @@ bool vehicle::check_heli_ascend( player &p ) void vehicle::pldrive( const point &p, int z ) { - player &u = g->u; + player &player_character = get_avatar(); if( z != 0 && is_rotorcraft() ) { - u.moves = std::min( u.moves, 0 ); + player_character.moves = std::min( player_character.moves, 0 ); thrust( 0, z ); } int turn_delta = 15 * p.x; @@ -1097,15 +1368,15 @@ void vehicle::pldrive( const point &p, int z ) // If you've got more moves than speed, it's most likely time stop // Let's get rid of that - u.moves = std::min( u.moves, u.get_speed() ); + player_character.moves = std::min( player_character.moves, player_character.get_speed() ); ///\EFFECT_DEX reduces chance of losing control of vehicle when turning ///\EFFECT_PER reduces chance of losing control of vehicle when turning ///\EFFECT_DRIVING reduces chance of losing control of vehicle when turning - float skill = std::min( 10.0f, - u.get_skill_level( skill_driving ) + ( u.get_dex() + u.get_per() ) / 10.0f ); + float skill = std::min( 10.0f, player_character.get_skill_level( skill_driving ) + + ( player_character.get_dex() + player_character.get_per() ) / 10.0f ); float penalty = rng_float( 0.0f, handling_diff ) - skill; int cost; if( penalty > 0.0f ) { @@ -1113,7 +1384,7 @@ void vehicle::pldrive( const point &p, int z ) cost = 100 * ( 1.0f + penalty / 2.5f ); } else { // At 10 skill, with a perfect vehicle, we could turn up to 3 times per turn - cost = std::max( u.get_speed(), 100 ) * ( 1.0f - ( -penalty / 10.0f ) * 2 / 3 ); + cost = std::max( player_character.get_speed(), 100 ) * ( 1.0f - ( -penalty / 10.0f ) * 2 / 3 ); } if( penalty > skill || cost > 400 ) { @@ -1121,12 +1392,12 @@ void vehicle::pldrive( const point &p, int z ) // Anything from a wasted attempt to 2 turns in the intended direction turn_delta *= rng( 0, 2 ); // Also wastes next turn - cost = std::max( cost, u.moves + 100 ); + cost = std::max( cost, player_character.moves + 100 ); } else if( one_in( 10 ) ) { // Don't warn all the time or it gets spammy - if( cost >= u.get_speed() * 2 ) { + if( cost >= player_character.get_speed() * 2 ) { add_msg( m_warning, _( "It takes you a very long time to steer that vehicle!" ) ); - } else if( cost >= u.get_speed() * 1.5f ) { + } else if( cost >= player_character.get_speed() * 1.5f ) { add_msg( m_warning, _( "It takes you a long time to steer that vehicle!" ) ); } } @@ -1134,7 +1405,7 @@ void vehicle::pldrive( const point &p, int z ) turn( turn_delta ); // At most 3 turns per turn, because otherwise it looks really weird and jumpy - u.moves -= std::max( cost, u.get_speed() / 3 + 1 ); + player_character.moves -= std::max( cost, player_character.get_speed() / 3 + 1 ); } if( p.y != 0 ) { @@ -1143,7 +1414,7 @@ void vehicle::pldrive( const point &p, int z ) cruise_thrust( -p.y * thr_amount ); } else { thrust( -p.y ); - u.moves = std::min( u.moves, 0 ); + player_character.moves = std::min( player_character.moves, 0 ); } } @@ -1153,9 +1424,10 @@ void vehicle::pldrive( const point &p, int z ) ///\EFFECT_DEX increases chance of regaining control of a vehicle ///\EFFECT_DRIVING increases chance of regaining control of a vehicle - if( handling_diff * rng( 1, 10 ) < u.dex_cur + u.get_skill_level( skill_driving ) * 2 ) { + if( handling_diff * rng( 1, 10 ) < + player_character.dex_cur + player_character.get_skill_level( skill_driving ) * 2 ) { add_msg( _( "You regain control of the %s." ), name ); - u.practice( skill_driving, velocity / 5 ); + player_character.practice( skill_driving, velocity / 5 ); velocity = static_cast( forward_velocity() ); skidding = false; move.init( turn_dir ); @@ -1274,10 +1546,11 @@ void vehicle::precalculate_vehicle_turning( int new_turn_dir, bool check_rail_di */ turning_wheels_that_are_one_axis = 0; + map &here = get_map(); 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 ); @@ -1294,7 +1567,7 @@ void vehicle::precalculate_vehicle_turning( int new_turn_dir, bool check_rail_di // advance precalculated wheel position 1 time in direction of moving wheel_tripoint += dp; - if( !g->m.has_flag_ter_or_furn( ter_flag_to_check, wheel_tripoint ) ) { + if( !here.has_flag_ter_or_furn( ter_flag_to_check, wheel_tripoint ) ) { // this tile is not allowed, disallow turn rails_ahead = false; break; @@ -1302,7 +1575,7 @@ void vehicle::precalculate_vehicle_turning( int new_turn_dir, bool check_rail_di // special case for rails if( check_rail_direction ) { - ter_id terrain_at_wheel = g->m.ter( wheel_tripoint ); + ter_id terrain_at_wheel = here.ter( wheel_tripoint ); // check is it correct tile to turn into if( !is_diagonal_movement && ( terrain_at_wheel == t_railroad_track_d || terrain_at_wheel == t_railroad_track_d1 || @@ -1425,7 +1698,8 @@ bool vehicle::is_wheel_state_correct_to_turn_on_rails( int wheels_on_rail, int w vehicle *vehicle::act_on_map() { const tripoint pt = global_pos3(); - if( !g->m.inbounds( pt ) ) { + map &here = get_map(); + if( !here.inbounds( pt ) ) { dbg( D_INFO ) << "stopping out-of-map vehicle. (x,y,z)=(" << pt.x << "," << pt.y << "," << pt.z << ")"; stop( false ); @@ -1436,7 +1710,8 @@ vehicle *vehicle::act_on_map() if( decrement_summon_timer() ) { return nullptr; } - const bool pl_ctrl = player_in_control( g->u ); + Character &player_character = get_player_character(); + const bool pl_ctrl = player_in_control( player_character ); // TODO: Remove this hack, have vehicle sink a z-level if( is_floating && !can_float() ) { add_msg( m_bad, _( "Your %s sank." ), name ); @@ -1447,9 +1722,9 @@ vehicle *vehicle::act_on_map() g->setremoteveh( nullptr ); } - g->m.on_vehicle_moved( sm_pos.z ); + here.on_vehicle_moved( sm_pos.z ); // Destroy vehicle (sank to nowhere) - g->m.destroy_vehicle( this ); + here.destroy_vehicle( this ); return nullptr; } @@ -1483,7 +1758,7 @@ vehicle *vehicle::act_on_map() return this; } - const float wheel_traction_area = g->m.vehicle_wheel_traction( *this ); + const float wheel_traction_area = here.vehicle_wheel_traction( *this ); const float traction = k_traction( wheel_traction_area ); if( traction < 0.001f ) { of_turn = 0; @@ -1592,7 +1867,55 @@ vehicle *vehicle::act_on_map() } } - return g->m.move_vehicle( *this, dp, mdir ); + return here.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() @@ -1609,23 +1932,25 @@ void vehicle::check_falling_or_floating() return; } - is_falling = g->m.has_zlevels(); + map &here = get_map(); + is_falling = here.has_zlevels(); if( is_flying && is_rotorcraft() ) { is_falling = false; } 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 &= here.has_flag_ter_or_furn( TFLAG_NO_FLOOR, p ) && + ( p.z > -OVERMAP_DEPTH ) && !here.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; + deep_water_tiles += here.has_flag( TFLAG_DEEP_WATER, p ) ? 1 : 0; + water_tiles += here.has_flag( TFLAG_SWIMMABLE, p ) ? 1 : 0; } // floating if 2/3rds of the vehicle is in deep water is_floating = 3 * deep_water_tiles >= 2 * pts.size(); @@ -1779,7 +2104,7 @@ int map::shake_vehicle( vehicle &veh, const int velocity_before, const int direc _( " is hurled from the %s's seat by " "the power of the impact!" ), veh.name ); unboard_vehicle( part_pos ); - } else if( g->u.sees( part_pos ) ) { + } else if( get_player_character().sees( part_pos ) ) { add_msg( m_bad, _( "The %s is hurled from %s's by the power of the impact!" ), pet->disp_name(), veh.name ); } diff --git a/src/vehicle_part.cpp b/src/vehicle_part.cpp index 3cf10e0e41350..ba14b1db9f965 100644 --- a/src/vehicle_part.cpp +++ b/src/vehicle_part.cpp @@ -6,7 +6,7 @@ #include #include -#include "avatar.h" +#include "character.h" #include "color.h" #include "debug.h" #include "enums.h" @@ -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(); } @@ -580,7 +579,7 @@ bool vehicle::can_enable( const vehicle_part &pt, bool alert ) const return false; } - if( pt.info().has_flag( "PLANTER" ) && !warm_enough_to_plant( g->u.pos() ) ) { + if( pt.info().has_flag( "PLANTER" ) && !warm_enough_to_plant( get_player_character().pos() ) ) { if( alert ) { add_msg( m_bad, _( "It is too cold to plant anything now." ) ); } diff --git a/src/vehicle_selector.cpp b/src/vehicle_selector.cpp index a1dcc3b14701c..4c6ff142c67e6 100644 --- a/src/vehicle_selector.cpp +++ b/src/vehicle_selector.cpp @@ -12,7 +12,7 @@ vehicle_selector::vehicle_selector( const tripoint &pos, int radius, bool access bool visibility_only ) { map &here = get_map(); - for( const tripoint &e : closest_tripoints_first( pos, radius ) ) { + for( const tripoint &e : closest_points_first( pos, radius ) ) { if( !accessible || ( visibility_only ? here.sees( pos, e, radius ) : here.clear_path( pos, e, radius, 1, 100 ) ) ) { if( const optional_vpart_position vp = here.veh_at( e ) ) { @@ -26,7 +26,7 @@ vehicle_selector::vehicle_selector( const tripoint &pos, int radius, bool access const vehicle &ignore ) { map &here = get_map(); - for( const tripoint &e : closest_tripoints_first( pos, radius ) ) { + for( const tripoint &e : closest_points_first( pos, radius ) ) { if( !accessible || here.clear_path( pos, e, radius, 1, 100 ) ) { if( const optional_vpart_position vp = here.veh_at( e ) ) { if( &vp->vehicle() != &ignore ) { diff --git a/src/vehicle_use.cpp b/src/vehicle_use.cpp index 01ac30d7147c0..f4ac178a89eda 100644 --- a/src/vehicle_use.cpp +++ b/src/vehicle_use.cpp @@ -284,6 +284,8 @@ void vehicle::set_electronics_menu_options( std::vector &options, keybind( "TOGGLE_SCOOP" ), "SCOOP" ); add_toggle( pgettext( "electronics menu option", "water purifier" ), keybind( "TOGGLE_WATER_PURIFIER" ), "WATER_PURIFIER" ); + add_toggle( pgettext( "electronics menu option", "smart controller" ), + keybind( "TOGGLE_SMART_ENGINE_CONTROLLER" ), "SMART_ENGINE_CONTROLLER" ); if( has_part( "DOOR_MOTOR" ) ) { options.emplace_back( _( "Toggle doors" ), keybind( "TOGGLE_DOORS" ) ); @@ -400,7 +402,7 @@ void vehicle::control_engines() if( engines_were_on && !engine_on ) { add_msg( _( "You turn off the %s's engines to change their configurations." ), name ); - } else if( !g->u.controlling_vehicle ) { + } else if( !get_player_character().controlling_vehicle ) { add_msg( _( "You change the %s's engine configuration." ), name ); return; } @@ -437,15 +439,16 @@ bool vehicle::interact_vehicle_locked() return true; } + Character &player_character = get_player_character(); add_msg( _( "You don't find any keys in the %s." ), name ); - const inventory &inv = g->u.crafting_inventory(); + const inventory &inv = player_character.crafting_inventory(); if( inv.has_quality( quality_id( "SCREW" ) ) ) { if( query_yn( _( "You don't find any keys in the %s. Attempt to hotwire vehicle?" ), name ) ) { ///\EFFECT_MECHANICS speeds up vehicle hotwiring - int skill = g->u.get_skill_level( skill_mechanics ); + int skill = player_character.get_skill_level( skill_mechanics ); const int moves = to_moves( 6000_seconds / ( ( skill > 0 ) ? skill : 1 ) ); - tripoint target = g->m.getabs( global_pos3() ) + coord_translate( parts[0].mount ); - g->u.assign_activity( hotwire_car_activity_actor( moves, target ) ); + tripoint target = get_map().getabs( global_pos3() ) + coord_translate( parts[0].mount ); + player_character.assign_activity( hotwire_car_activity_actor( moves, target ) ); } else if( has_security_working() && query_yn( _( "Trigger the %s's Alarm?" ), name ) ) { is_alarm_on = true; } else { @@ -471,10 +474,11 @@ void vehicle::smash_security_system() break; } } + Character &player_character = get_player_character(); //controls and security must both be valid if( c >= 0 && s >= 0 ) { ///\EFFECT_MECHANICS reduces chance of damaging controls when smashing security system - int skill = g->u.get_skill_level( skill_mechanics ); + int skill = player_character.get_skill_level( skill_mechanics ); int percent_controls = 70 / ( 1 + skill ); int percent_alarm = ( skill + 3 ) * 10; int rand = rng( 1, 100 ); @@ -483,7 +487,7 @@ void vehicle::smash_security_system() damage_direct( c, part_info( c ).durability / 4 ); if( parts[ c ].removed || parts[ c ].is_broken() ) { - g->u.controlling_vehicle = false; + player_character.controlling_vehicle = false; is_alarm_on = false; add_msg( _( "You destroy the controls…" ) ); } else { @@ -511,7 +515,7 @@ std::string vehicle::tracking_toggle_string() void vehicle::autopilot_patrol_check() { zone_manager &mgr = zone_manager::get_manager(); - if( mgr.has_near( zone_type_id( "VEHICLE_PATROL" ), g->m.getabs( global_pos3() ), 60 ) ) { + if( mgr.has_near( zone_type_id( "VEHICLE_PATROL" ), get_map().getabs( global_pos3() ), 60 ) ) { enable_patrol(); } else { g->zones_manager(); @@ -581,11 +585,12 @@ void vehicle::use_controls( const tripoint &pos ) bool remote = g->remoteveh() == this; bool has_electronic_controls = false; + avatar &player_character = get_avatar(); if( remote ) { options.emplace_back( _( "Stop controlling" ), keybind( "RELEASE_CONTROLS" ) ); actions.push_back( [&] { - g->u.controlling_vehicle = false; + player_character.controlling_vehicle = false; g->setremoteveh( nullptr ); add_msg( _( "You stop controlling the vehicle." ) ); refresh(); @@ -593,11 +598,11 @@ void vehicle::use_controls( const tripoint &pos ) has_electronic_controls = has_part( "CTRL_ELECTRONIC" ) || has_part( "REMOTE_CONTROLS" ); - } else if( veh_pointer_or_null( g->m.veh_at( pos ) ) == this ) { - if( g->u.controlling_vehicle ) { + } else if( veh_pointer_or_null( get_map().veh_at( pos ) ) == this ) { + if( player_character.controlling_vehicle ) { options.emplace_back( _( "Let go of controls" ), keybind( "RELEASE_CONTROLS" ) ); actions.push_back( [&] { - g->u.controlling_vehicle = false; + player_character.controlling_vehicle = false; add_msg( _( "You let go of the controls." ) ); refresh(); } ); @@ -617,7 +622,7 @@ void vehicle::use_controls( const tripoint &pos ) } if( has_part( "ENGINE" ) ) { - if( g->u.controlling_vehicle || ( remote && engine_on ) ) { + if( player_character.controlling_vehicle || ( remote && engine_on ) ) { options.emplace_back( _( "Stop driving" ), keybind( "TOGGLE_ENGINE" ) ); actions.push_back( [&] { if( engine_on && has_engine_type_not( fuel_type_muscle, true ) ) @@ -653,7 +658,7 @@ void vehicle::use_controls( const tripoint &pos ) } vehicle_noise = 0; engine_on = false; - g->u.controlling_vehicle = false; + player_character.controlling_vehicle = false; g->setremoteveh( nullptr ); sfx::do_vehicle_engine_sfx(); refresh(); @@ -759,7 +764,7 @@ void vehicle::use_controls( const tripoint &pos ) if( menu.ret >= 0 ) { // allow player to turn off engine without triggering another warning if( menu.ret != 0 && menu.ret != 1 && menu.ret != 2 && menu.ret != 3 ) { - if( !handle_potential_theft( dynamic_cast( g->u ) ) ) { + if( !handle_potential_theft( player_character ) ) { return; } } @@ -778,7 +783,8 @@ bool vehicle::fold_up() return false; } - if( g->u.controlling_vehicle ) { + avatar &player_character = get_avatar(); + if( player_character.controlling_vehicle ) { add_msg( m_warning, _( "As the pitiless metal bars close on your nether regions, you reconsider trying to fold the %s while riding it." ), name ); @@ -792,8 +798,8 @@ bool vehicle::fold_up() add_msg( _( "You painstakingly pack the %s into a portable configuration." ), name ); - if( g->u.get_grab_type() != object_type::NONE ) { - g->u.grab( object_type::NONE ); + if( player_character.get_grab_type() != object_type::NONE ) { + player_character.grab( object_type::NONE ); add_msg( _( "You let go of %s as you fold it." ), name ); } @@ -808,11 +814,12 @@ bool vehicle::fold_up() // create a folding [non]bicycle item item bicycle( can_be_folded ? "generic_folded_vehicle" : "folding_bicycle", calendar::turn ); + map &here = get_map(); // Drop stuff in containers on ground for( const vpart_reference &vp : get_any_parts( "CARGO" ) ) { const size_t p = vp.part_index(); for( auto &elem : get_items( p ) ) { - g->m.add_item_or_charges( g->u.pos(), elem ); + here.add_item_or_charges( player_character.pos(), elem ); } while( !get_items( p ).empty() ) { get_items( p ).erase( get_items( p ).begin() ); @@ -844,12 +851,12 @@ bool vehicle::fold_up() bicycle.set_var( "description", string_format( _( "A folded %s." ), name ) ); } - g->m.add_item_or_charges( global_part_pos3( 0 ), bicycle ); - g->m.destroy_vehicle( this ); + here.add_item_or_charges( global_part_pos3( 0 ), bicycle ); + here.destroy_vehicle( this ); // TODO: take longer to fold bigger vehicles // TODO: make this interruptible - g->u.moves -= 500; + player_character.moves -= 500; return true; } @@ -859,7 +866,7 @@ double vehicle::engine_cold_factor( const int e ) const return 0.0; } - int eff_temp = g->weather.get_temperature( g->u.pos() ); + int eff_temp = g->weather.get_temperature( get_player_character().pos() ); if( !parts[ engines[ e ] ].faults().count( fault_engine_glow_plug ) ) { eff_temp = std::min( eff_temp, 20 ); } @@ -906,13 +913,14 @@ bool vehicle::start_engine( const int e ) out_of_fuel = true; } + Character &player_character = get_player_character(); if( out_of_fuel ) { if( einfo.fuel_type == fuel_type_muscle ) { // Muscle engines cannot start with broken limbs - if( einfo.has_flag( "MUSCLE_ARMS" ) && ( g->u.get_working_arm_count() < 2 ) ) { + if( einfo.has_flag( "MUSCLE_ARMS" ) && ( player_character.get_working_arm_count() < 2 ) ) { add_msg( _( "You cannot use %s with a broken arm." ), eng.name() ); return false; - } else if( einfo.has_flag( "MUSCLE_LEGS" ) && ( g->u.get_working_leg_count() < 2 ) ) { + } else if( einfo.has_flag( "MUSCLE_LEGS" ) && ( player_character.get_working_leg_count() < 2 ) ) { add_msg( _( "You cannot use %s with a broken leg." ), eng.name() ); return false; } @@ -1062,14 +1070,15 @@ void vehicle::start_engines( const bool take_control, const bool autodrive ) return; } - if( take_control && !g->u.controlling_vehicle ) { - g->u.controlling_vehicle = true; + Character &player_character = get_player_character(); + if( take_control && !player_character.controlling_vehicle ) { + player_character.controlling_vehicle = true; add_msg( _( "You take control of the %s." ), name ); } if( !autodrive ) { - g->u.assign_activity( ACT_START_ENGINES, start_time ); - g->u.activity.placement = starting_engine_position - g->u.pos(); - g->u.activity.values.push_back( take_control ); + player_character.assign_activity( ACT_START_ENGINES, start_time ); + player_character.activity.placement = starting_engine_position - player_character.pos(); + player_character.activity.values.push_back( take_control ); } } @@ -1122,9 +1131,8 @@ void vehicle::honk_horn() void vehicle::reload_seeds( const tripoint &pos ) { - player &p = g->u; - - std::vector seed_inv = p.items_with( []( const item & itm ) { + Character &player_character = get_player_character(); + std::vector seed_inv = player_character.items_with( []( const item & itm ) { return itm.is_seed(); } ); @@ -1149,13 +1157,13 @@ void vehicle::reload_seeds( const tripoint &pos ) itype_id seed_id = std::get<0>( seed_entries[seed_index] ); std::list used_seed; if( item::count_by_charges( seed_id ) ) { - used_seed = p.use_charges( seed_id, actual_amount ); + used_seed = player_character.use_charges( seed_id, actual_amount ); } else { - used_seed = p.use_amount( seed_id, actual_amount ); + used_seed = player_character.use_amount( seed_id, actual_amount ); } used_seed.front().set_age( 0_turns ); //place seeds into the planter - put_into_vehicle_or_drop( p, item_drop_reason::deliberate, used_seed, pos ); + put_into_vehicle_or_drop( player_character, item_drop_reason::deliberate, used_seed, pos ); } } } @@ -1182,8 +1190,9 @@ void vehicle::beeper_sound() void vehicle::play_music() { + Character &player_character = get_player_character(); for( const vpart_reference &vp : get_enabled_parts( "STEREO" ) ) { - iuse::play_music( g->u, vp.pos(), 15, 30 ); + iuse::play_music( player_character, vp.pos(), 15, 30 ); } } @@ -1204,29 +1213,30 @@ void vehicle::crash_terrain_around() if( total_power_w() <= 0 ) { return; } + map &here = get_map(); for( const vpart_reference &vp : get_enabled_parts( "CRASH_TERRAIN_AROUND" ) ) { tripoint crush_target( 0, 0, -OVERMAP_LAYERS ); const tripoint start_pos = vp.pos(); const transform_terrain_data &ttd = vp.info().transform_terrain; for( size_t i = 0; i < eight_horizontal_neighbors.size() && - !g->m.inbounds_z( crush_target.z ); i++ ) { + !here.inbounds_z( crush_target.z ); i++ ) { tripoint cur_pos = start_pos + eight_horizontal_neighbors.at( i ); bool busy_pos = false; for( const vpart_reference &vp_tmp : get_all_parts() ) { busy_pos |= vp_tmp.pos() == cur_pos; } for( const std::string &flag : ttd.pre_flags ) { - if( g->m.has_flag( flag, cur_pos ) && !busy_pos ) { + if( here.has_flag( flag, cur_pos ) && !busy_pos ) { crush_target = cur_pos; break; } } } //target chosen - if( g->m.inbounds_z( crush_target.z ) ) { + if( here.inbounds_z( crush_target.z ) ) { velocity = 0; cruise_velocity = 0; - g->m.destroy( crush_target ); + here.destroy( crush_target ); sounds::sound( crush_target, 500, sounds::sound_t::combat, _( "Clanggggg!" ), false, "smash_success", "hit_vehicle" ); } @@ -1235,12 +1245,13 @@ void vehicle::crash_terrain_around() void vehicle::transform_terrain() { + map &here = get_map(); for( const vpart_reference &vp : get_enabled_parts( "TRANSFORM_TERRAIN" ) ) { const tripoint start_pos = vp.pos(); const transform_terrain_data &ttd = vp.info().transform_terrain; bool prereq_fulfilled = false; for( const std::string &flag : ttd.pre_flags ) { - if( g->m.has_flag( flag, start_pos ) ) { + if( here.has_flag( flag, start_pos ) ) { prereq_fulfilled = true; break; } @@ -1248,15 +1259,15 @@ void vehicle::transform_terrain() if( prereq_fulfilled ) { const ter_id new_ter = ter_id( ttd.post_terrain ); if( new_ter != t_null ) { - g->m.ter_set( start_pos, new_ter ); + here.ter_set( start_pos, new_ter ); } const furn_id new_furn = furn_id( ttd.post_furniture ); if( new_furn != f_null ) { - g->m.furn_set( start_pos, new_furn ); + here.furn_set( start_pos, new_furn ); } const field_type_id new_field = field_type_id( ttd.post_field ); if( new_field.id() ) { - g->m.add_field( start_pos, new_field, ttd.post_field_intensity, ttd.post_field_age ); + here.add_field( start_pos, new_field, ttd.post_field_intensity, ttd.post_field_age ); } } else { const int speed = std::abs( velocity ); @@ -1270,17 +1281,18 @@ void vehicle::transform_terrain() void vehicle::operate_reaper() { + map &here = get_map(); for( const vpart_reference &vp : get_enabled_parts( "REAPER" ) ) { const size_t reaper_id = vp.part_index(); const tripoint reaper_pos = vp.pos(); const int plant_produced = rng( 1, vp.info().bonus ); const int seed_produced = rng( 1, 3 ); const units::volume max_pickup_volume = vp.info().size / 20; - if( g->m.furn( reaper_pos ) != f_plant_harvest ) { + if( here.furn( reaper_pos ) != f_plant_harvest ) { continue; } // Can't use item_stack::only_item() since there might be fertilizer - map_stack items = g->m.i_at( reaper_pos ); + map_stack items = here.i_at( reaper_pos ); map_stack::iterator seed = std::find_if( items.begin(), items.end(), []( const item & it ) { return it.is_seed(); } ); @@ -1289,13 +1301,13 @@ void vehicle::operate_reaper() // Otherworldly plants, the earth-made reaper can not handle those. continue; } - g->m.furn_set( reaper_pos, f_null ); + here.furn_set( reaper_pos, f_null ); // Secure the seed type before i_clear destroys the item. const itype &seed_type = *seed->type; - g->m.i_clear( reaper_pos ); + here.i_clear( reaper_pos ); for( auto &i : iexamine::get_harvest_items( seed_type, plant_produced, seed_produced, false ) ) { - g->m.add_item_or_charges( reaper_pos, i ); + here.add_item_or_charges( reaper_pos, i ); } sounds::sound( reaper_pos, rng( 10, 25 ), sounds::sound_t::combat, _( "Swish" ), false, "vehicle", "reaper" ); @@ -1314,6 +1326,7 @@ void vehicle::operate_reaper() void vehicle::operate_planter() { + map &here = get_map(); for( const vpart_reference &vp : get_enabled_parts( "PLANTER" ) ) { const size_t planter_id = vp.part_index(); const tripoint loc = vp.pos(); @@ -1321,12 +1334,12 @@ void vehicle::operate_planter() for( auto i = v.begin(); i != v.end(); i++ ) { if( i->is_seed() ) { // If it is an "advanced model" then it will avoid damaging itself or becoming damaged. It's a real feature. - if( g->m.ter( loc ) != t_dirtmound && vp.has_feature( "ADVANCED_PLANTER" ) ) { + if( here.ter( loc ) != t_dirtmound && vp.has_feature( "ADVANCED_PLANTER" ) ) { //then don't put the item there. break; - } else if( g->m.ter( loc ) == t_dirtmound ) { - g->m.set( loc, t_dirt, f_plant_seed ); - } else if( !g->m.has_flag( "PLOWABLE", loc ) ) { + } else if( here.ter( loc ) == t_dirtmound ) { + here.set( loc, t_dirt, f_plant_seed ); + } else if( !here.has_flag( "PLOWABLE", loc ) ) { //If it isn't plowable terrain, then it will most likely be damaged. damage( planter_id, rng( 1, 10 ), DT_BASH, false ); sounds::sound( loc, rng( 10, 20 ), sounds::sound_t::combat, _( "Clink" ), false, "smash_success", @@ -1334,13 +1347,13 @@ void vehicle::operate_planter() } if( !i->count_by_charges() || i->charges == 1 ) { i->set_age( 0_turns ); - g->m.add_item( loc, *i ); + here.add_item( loc, *i ); v.erase( i ); } else { item tmp = *i; tmp.charges = 1; tmp.set_age( 0_turns ); - g->m.add_item( loc, tmp ); + here.add_item( loc, tmp ); i->charges--; } break; @@ -1351,6 +1364,7 @@ void vehicle::operate_planter() void vehicle::operate_scoop() { + map &here = get_map(); for( const vpart_reference &vp : get_enabled_parts( "SCOOP" ) ) { const size_t scoop = vp.part_index(); const int chance_to_damage_item = 9; @@ -1363,17 +1377,17 @@ void vehicle::operate_scoop() random_entry_ref( sound_msgs ), false, "vehicle", "scoop" ); std::vector parts_points; for( const tripoint ¤t : - g->m.points_in_radius( global_part_pos3( scoop ), 1 ) ) { + here.points_in_radius( global_part_pos3( scoop ), 1 ) ) { parts_points.push_back( current ); } for( const tripoint &position : parts_points ) { - g->m.mop_spills( position ); - if( !g->m.has_items( position ) ) { + here.mop_spills( position ); + if( !here.has_items( position ) ) { continue; } item *that_item_there = nullptr; - map_stack items = g->m.i_at( position ); - if( g->m.has_flag( "SEALED", position ) ) { + map_stack items = here.i_at( position ); + if( here.has_flag( "SEALED", position ) ) { // Ignore it. Street sweepers are not known for their ability to harvest crops. continue; } @@ -1396,7 +1410,7 @@ void vehicle::operate_scoop() } //This attempts to add the item to the scoop inventory and if successful, removes it from the map. if( add_item( scoop, *that_item_there ) ) { - g->m.i_rem( position, that_item_there ); + here.i_rem( position, that_item_there ); } else { break; } @@ -1485,8 +1499,9 @@ void vehicle::open_or_close( const int part_index, const bool opening ) //find_lines_of_parts() doesn't return the part_index we passed, so we set it on it's own parts[part_index].open = opening; insides_dirty = true; - g->m.set_transparency_cache_dirty( sm_pos.z ); - const int dist = rl_dist( g->u.pos(), mount_to_tripoint( parts[part_index].mount ) ); + get_map().set_transparency_cache_dirty( sm_pos.z ); + const int dist = rl_dist( get_player_character().pos(), + mount_to_tripoint( parts[part_index].mount ) ); if( dist < 20 ) { sfx::play_variant_sound( opening ? "vehicle_open" : "vehicle_close", parts[ part_index ].info().get_id().str(), 100 - dist * 3 ); @@ -1553,8 +1568,9 @@ void vehicle::use_autoclave( int p ) void vehicle::use_washing_machine( int p ) { + avatar &player_character = get_avatar(); // Get all the items that can be used as detergent - const inventory &inv = g->u.crafting_inventory(); + const inventory &inv = player_character.crafting_inventory(); std::vector detergents = inv.items_with( [inv]( const item & it ) { return it.has_flag( "DETERGENT" ) && inv.has_charges( it.typeId(), 5 ); } ); @@ -1629,7 +1645,7 @@ void vehicle::use_washing_machine( int p ) std::vector detergent; detergent.push_back( item_comp( det_types[chosen_detergent], 5 ) ); - g->u.consume_items( detergent, 1, is_crafting_component ); + player_character.consume_items( detergent, 1, is_crafting_component ); add_msg( m_good, _( "You pour some detergent into the washing machine, close its lid, and turn it on. The washing machine is being filled with water from vehicle tanks." ) ); @@ -1638,7 +1654,8 @@ void vehicle::use_washing_machine( int p ) void vehicle::use_dishwasher( int p ) { - bool detergent_is_enough = g->u.crafting_inventory().has_charges( itype_detergent, 5 ); + avatar &player_character = get_avatar(); + bool detergent_is_enough = player_character.crafting_inventory().has_charges( itype_detergent, 5 ); auto items = get_items( p ); static const std::string filthy( "FILTHY" ); bool filthy_items = std::all_of( items.begin(), items.end(), []( const item & i ) { @@ -1686,7 +1703,7 @@ void vehicle::use_dishwasher( int p ) std::vector detergent; detergent.push_back( item_comp( itype_detergent, 5 ) ); - g->u.consume_items( detergent, 1, is_crafting_component ); + player_character.consume_items( detergent, 1, is_crafting_component ); add_msg( m_good, _( "You pour some detergent into the dishwasher, close its lid, and turn it on. The dishwasher is being filled with water from vehicle tanks." ) ); @@ -1699,7 +1716,7 @@ void vehicle::use_monster_capture( int part, const tripoint &pos ) return; } item base = item( parts[part].get_base() ); - base.type->invoke( g->u, base, pos ); + base.type->invoke( get_avatar(), base, pos ); parts[part].set_base( base ); if( base.has_var( "contained_name" ) ) { parts[part].set_flag( vehicle_part::animal_flag ); @@ -1762,7 +1779,7 @@ void vehicle::use_harness( int part, const tripoint &pos ) add_msg( m_info, _( "You untie your %s." ), m.get_name() ); m.remove_effect( effect_tied ); if( m.tied_item ) { - g->u.i_add( *m.tied_item ); + get_player_character().i_add( *m.tied_item ); m.tied_item.reset(); } } @@ -1860,7 +1877,7 @@ void vehicle::use_bike_rack( int part ) success = try_to_rack_nearby_vehicle( racks_parts ); } if( success ) { - g->m.invalidate_map_cache( g->get_levz() ); + get_map().invalidate_map_cache( g->get_levz() ); } } @@ -1869,8 +1886,10 @@ void vehicle::interact_with( const tripoint &pos, int interact_part ) { std::vector menu_items; std::vector options_message; - const bool has_items_on_ground = g->m.sees_some_items( pos, g->u ); - const bool items_are_sealed = g->m.has_flag( "SEALED", pos ); + map &here = get_map(); + avatar &player_character = get_avatar(); + const bool has_items_on_ground = here.sees_some_items( pos, player_character ); + const bool items_are_sealed = here.has_flag( "SEALED", pos ); auto turret = turret_query( pos ); @@ -2009,7 +2028,7 @@ void vehicle::interact_with( const tripoint &pos, int interact_part ) choice = selectmenu.ret; } if( choice != EXAMINE && choice != TRACK && choice != GET_ITEMS_ON_GROUND ) { - if( !handle_potential_theft( dynamic_cast( g->u ) ) ) { + if( !handle_potential_theft( dynamic_cast( player_character ) ) ) { return; } } @@ -2018,10 +2037,18 @@ void vehicle::interact_with( const tripoint &pos, int interact_part ) 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 ); - g->u.invoke_item( &pseudo ); + player_character.invoke_item( &pseudo ); charge_battery( pseudo.ammo_remaining() ); return true; }; @@ -2049,7 +2076,7 @@ void vehicle::interact_with( const tripoint &pos, int interact_part ) return; } case USE_TOWEL: { - iuse::towel_common( &g->u, nullptr, false ); + iuse::towel_common( &player_character, nullptr, false ); return; } case USE_AUTOCLAVE: { @@ -2065,13 +2092,13 @@ void vehicle::interact_with( const tripoint &pos, int interact_part ) return; } case FILL_CONTAINER: { - g->u.siphon( *this, itype_water_clean ); + player_character.siphon( *this, itype_water_clean ); return; } case DRINK: { item water( "water_clean", 0 ); - if( g->u.can_consume( water ) ) { - g->u.assign_activity( player_activity( consume_activity_actor( water ) ) ); + if( player_character.can_consume( water ) ) { + player_character.assign_activity( player_activity( consume_activity_actor( water ) ) ); drain( itype_water_clean, 1 ); } return; @@ -2079,7 +2106,7 @@ void vehicle::interact_with( const tripoint &pos, int interact_part ) case USE_WELDER: { if( veh_tool( itype_welder ) ) { // HACK: Evil hack incoming - auto &act = g->u.activity; + auto &act = player_character.activity; if( act.id() == ACT_REPAIR_ITEM ) { // Magic: first tell activity the item doesn't really exist act.index = INT_MIN; @@ -2125,11 +2152,11 @@ void vehicle::interact_with( const tripoint &pos, int interact_part ) return; } case RELOAD_TURRET: { - item::reload_option opt = g->u.select_ammo( *turret.base(), true ); + item::reload_option opt = player_character.select_ammo( *turret.base(), true ); if( opt ) { - g->u.assign_activity( ACT_RELOAD, opt.moves(), opt.qty() ); - g->u.activity.targets.emplace_back( turret.base() ); - g->u.activity.targets.push_back( std::move( opt.ammo ) ); + player_character.assign_activity( ACT_RELOAD, opt.moves(), opt.qty() ); + player_character.activity.targets.emplace_back( turret.base() ); + player_character.activity.targets.push_back( std::move( opt.ammo ) ); } return; } @@ -2170,7 +2197,7 @@ void vehicle::interact_with( const tripoint &pos, int interact_part ) return; } case WORKBENCH: { - iexamine::workbench_internal( g->u, pos, vpart_reference( *this, workbench_part ) ); + iexamine::workbench_internal( player_character, pos, vpart_reference( *this, workbench_part ) ); return; } } diff --git a/src/weather.cpp b/src/weather.cpp index 882859210654e..a1cc7bba60db6 100644 --- a/src/weather.cpp +++ b/src/weather.cpp @@ -7,6 +7,7 @@ #include #include +#include "assign.h" #include "avatar.h" #include "bodypart.h" #include "calendar.h" @@ -63,18 +64,29 @@ weather_manager &get_weather() static bool is_player_outside() { - return get_map().is_outside( point( g->u.posx(), g->u.posy() ) ) && g->get_levz() >= 0; + return get_map().is_outside( point( get_player_character().posx(), + get_player_character().posy() ) ) && g->get_levz() >= 0; } -static constexpr int THUNDER_CHANCE = 50; -static constexpr int LIGHTNING_CHANCE = 600; +weather_type_id get_bad_weather() +{ + weather_type_id bad_weather = WEATHER_NULL; + const weather_generator &weather_gen = get_weather().get_cur_weather_gen(); + for( const std::string &weather_type : weather_gen.weather_types ) { + weather_type_id current_conditions = weather_type_id( weather_type ); + if( current_conditions->precip == precip_class::heavy ) { + bad_weather = current_conditions; + } + } + return bad_weather; +} /** * Glare. * Causes glare effect to player's eyes if they are not wearing applicable eye protection. * @param intensity Level of sun brighthess */ -void weather_effect::glare( sun_intensity intensity ) +void glare( weather_type_id w ) { //General prepequisites for glare if( !is_player_outside() || !g->is_in_sunlight( g->u.pos() ) || g->u.in_sleep_state() || @@ -91,7 +103,7 @@ void weather_effect::glare( sun_intensity intensity ) //Winter snow glare: for both clear & sunny weather effect = &effect_snow_glare; dur = g->u.has_effect( *effect ) ? 1_turns : 2_turns; - } else if( intensity == sun_intensity::high ) { + } else if( w->sun_intensity == sun_intensity_type::high ) { //Sun glare: only for bright sunny weather effect = &effect_glare; dur = g->u.has_effect( *effect ) ? 1_turns : 2_turns; @@ -108,42 +120,43 @@ void weather_effect::glare( sun_intensity intensity ) ////// food vs weather -int incident_sunlight( weather_type wtype, const time_point &t ) +int incident_sunlight( weather_type_id wtype, const time_point &t ) { - return std::max( 0.0f, sunlight( t, false ) + weather::light_modifier( wtype ) ); + return std::max( 0.0f, sunlight( t, false ) + wtype->light_modifier ); } -inline void proc_weather_sum( const weather_type wtype, weather_sum &data, +inline void proc_weather_sum( const weather_type_id wtype, weather_sum &data, const time_point &t, const time_duration &tick_size ) { - switch( wtype ) { - case WEATHER_LIGHT_DRIZZLE: - data.rain_amount += 1 * to_turns( tick_size ); - break; - case WEATHER_DRIZZLE: - data.rain_amount += 4 * to_turns( tick_size ); - break; - case WEATHER_RAINY: - case WEATHER_THUNDER: - case WEATHER_LIGHTNING: - data.rain_amount += 8 * to_turns( tick_size ); - break; - case WEATHER_ACID_DRIZZLE: - data.acid_amount += 4 * to_turns( tick_size ); - break; - case WEATHER_ACID_RAIN: - data.acid_amount += 8 * to_turns( tick_size ); - break; - default: - break; + int amount = 0; + if( wtype->rains ) { + switch( wtype->precip ) { + case precip_class::very_light: + amount = 1 * to_turns( tick_size ); + break; + case precip_class::light: + amount = 4 * to_turns( tick_size ); + break; + case precip_class::heavy: + amount = 8 * to_turns( tick_size ); + break; + default: + break; + } + } + if( wtype->acidic ) { + data.acid_amount += amount; + } else { + data.rain_amount += amount; } + // TODO: Change this sunlight "sampling" here into a proper interpolation const float tick_sunlight = incident_sunlight( wtype, t ); data.sunlight += tick_sunlight * to_turns( tick_size ); } -weather_type current_weather( const tripoint &location, const time_point &t ) +weather_type_id current_weather( const tripoint &location, const time_point &t ) { const auto wgen = g->weather.get_cur_weather_gen(); if( g->weather.weather_override != WEATHER_NULL ) { @@ -169,7 +182,7 @@ weather_sum sum_conditions( const time_point &start, const time_point &end, tick_size = 1_minutes; } - weather_type wtype = current_weather( location, t ); + weather_type_id wtype = current_weather( location, t ); proc_weather_sum( wtype, data, t, tick_size ); data.wind_amount += get_local_windpower( g->weather.windspeed, overmap_buffer.ter( ms_to_omt_copy( location ) ), @@ -382,7 +395,7 @@ static void fill_water_collectors( int mmPerHour, bool acid ) * @see map::decay_fields_and_scent * @see player::drench */ -static void wet_player( int amount ) +void weather_effect::wet_player( int amount ) { if( !is_player_outside() || g->u.has_trait( trait_FEATHERS ) || @@ -412,66 +425,13 @@ static void wet_player( int amount ) g->u.drench( amount, drenched_parts, false ); } -double precip_mm_per_hour( precip_class const p ) -// Precipitation rate expressed as the rainfall equivalent if all -// the precipitation were rain (rather than snow). -{ - return - p == precip_class::VERY_LIGHT ? 0.5 : - p == precip_class::LIGHT ? 1.5 : - p == precip_class::HEAVY ? 3 : - 0; -} - -void do_rain( weather_type const w ) -{ - if( !weather::rains( w ) || weather::precip( w ) == precip_class::NONE ) { - return; - } - fill_water_collectors( precip_mm_per_hour( weather::precip( w ) ), weather::acidic( w ) ); - int wetness = 0; - time_duration decay_time = 60_turns; - if( weather::precip( w ) == precip_class::VERY_LIGHT ) { - wetness = 5; - decay_time = 5_turns; - } else if( weather::precip( w ) == precip_class::LIGHT ) { - wetness = 30; - decay_time = 15_turns; - } else if( weather::precip( w ) == precip_class::HEAVY ) { - decay_time = 45_turns; - wetness = 60; - } - get_map().decay_fields_and_scent( decay_time ); - wet_player( wetness ); -} - -void weather_effect::none() -{ - glare( sun_intensity::normal ); -} -void weather_effect::flurry() {} - -void weather_effect::sunny() -{ - glare( sun_intensity::high ); -} - -void weather_effect::snow() -{ - wet_player( 10 ); -} - -void weather_effect::snowstorm() -{ - wet_player( 40 ); -} /** * Thunder. * Flavor messages. Very wet. */ -void weather_effect::thunder() +void weather_effect::thunder( int intensity ) { - if( !g->u.has_effect( effect_sleep ) && !g->u.is_deaf() && one_in( THUNDER_CHANCE ) ) { + if( !g->u.has_effect( effect_sleep ) && !g->u.is_deaf() && one_in( intensity ) ) { if( g->get_levz() >= 0 ) { add_msg( _( "You hear a distant rumble of thunder." ) ); sfx::play_variant_sound( "environment", "thunder_far", 80, rng( 0, 359 ) ); @@ -492,10 +452,9 @@ void weather_effect::thunder() * only manifest properly near the player due to the "reality bubble", this was causing undesired metagame tactics * such as players leaving their shelter for a more "expendable" area during lightning storms. */ -void weather_effect::lightning() +void weather_effect::lightning( int intensity ) { - thunder(); - if( one_in( LIGHTNING_CHANCE ) ) { + if( one_in( intensity ) ) { if( g->get_levz() >= 0 ) { add_msg( _( "A flash of lightning illuminates your surroundings!" ) ); sfx::play_variant_sound( "environment", "thunder_near", 100, rng( 0, 359 ) ); @@ -505,14 +464,13 @@ void weather_effect::lightning() g->weather.lightning_active = false; } } - /** * Acid drizzle. * Causes minor pain only. */ -void weather_effect::light_acid() +void weather_effect::light_acid( int intensity ) { - if( calendar::once_every( 1_minutes ) && is_player_outside() ) { + if( calendar::once_every( time_duration::from_seconds( intensity ) ) && is_player_outside() ) { if( g->u.weapon.has_flag( "RAIN_PROTECT" ) && !one_in( 3 ) ) { add_msg( _( "Your %s protects you from the acidic drizzle." ), g->u.weapon.tname() ); } else { @@ -537,9 +495,9 @@ void weather_effect::light_acid() * Acid rain. * Causes major pain. Damages non acid-proof mobs. Very wet (acid). */ -void weather_effect::acid() +void weather_effect::acid( int intensity ) { - if( calendar::once_every( 2_turns ) && is_player_outside() ) { + if( calendar::once_every( time_duration::from_seconds( intensity ) ) && is_player_outside() ) { if( g->u.weapon.has_flag( "RAIN_PROTECT" ) && one_in( 4 ) ) { add_msg( _( "Your umbrella protects you from the acid rain." ) ); } else { @@ -560,6 +518,45 @@ void weather_effect::acid() } } +double precip_mm_per_hour( precip_class const p ) +// Precipitation rate expressed as the rainfall equivalent if all +// the precipitation were rain (rather than snow). +{ + return + p == precip_class::very_light ? 0.5 : + p == precip_class::light ? 1.5 : + p == precip_class::heavy ? 3 : + 0; +} + +void handle_weather_effects( weather_type_id const w ) +{ + if( w->rains && w->precip != precip_class::none ) { + fill_water_collectors( precip_mm_per_hour( w->precip ), + w->acidic ); + int wetness = 0; + time_duration decay_time = 60_turns; + if( w->precip == precip_class::very_light ) { + wetness = 5; + decay_time = 5_turns; + } else if( w->precip == precip_class::light ) { + wetness = 30; + decay_time = 15_turns; + } else if( w->precip == precip_class::heavy ) { + decay_time = 45_turns; + wetness = 60; + } + get_map().decay_fields_and_scent( decay_time ); + weather_effect::wet_player( wetness ); + } + glare( w ); + std::vector> weather_effects = w->effects; + + for( const std::pair &effect : weather_effects ) { + effect.first( effect.second ); + } +} + static std::string to_string( const weekdays &d ) { static const std::array weekday_names = {{ @@ -623,7 +620,7 @@ std::string weather_forecast( const point &abs_sm_pos ) _( "The current time is %1$s Eastern Standard Time. At %2$s in %3$s, it was %4$s. The temperature was %5$s. " ), to_string_time_of_day( calendar::turn ), print_time_just_hour( calendar::turn ), city_name, - weather::name( g->weather.weather ), print_temperature( g->weather.temperature ) + get_weather().weather_id->name, print_temperature( get_weather().temperature ) ); //weather_report += ", the dewpoint ???, and the relative humidity ???. "; @@ -647,7 +644,7 @@ std::string weather_forecast( const point &abs_sm_pos ) const time_point last_hour = calendar::turn - ( calendar::turn - calendar::turn_zero ) % 1_hours; for( int d = 0; d < 6; d++ ) { - weather_type forecast = WEATHER_NULL; + weather_type_id forecast = WEATHER_NULL; const auto wgen = g->weather.get_cur_weather_gen(); for( time_point i = last_hour + d * 12_hours; i < last_hour + ( d + 1 ) * 12_hours; i += 1_hours ) { w_point w = wgen.get_weather( abs_ms_pos, i, g->get_seed() ); @@ -672,7 +669,7 @@ std::string weather_forecast( const point &abs_sm_pos ) } weather_report += string_format( _( "%s… %s. Highs of %s. Lows of %s. " ), - day, weather::name( forecast ), + day, forecast->name, print_temperature( high ), print_temperature( low ) ); } @@ -863,14 +860,14 @@ std::string get_wind_arrow( int dirangle ) return wind_arrow; } -int get_local_humidity( double humidity, weather_type weather, bool sheltered ) +int get_local_humidity( double humidity, weather_type_id weather, bool sheltered ) { int tmphumidity = humidity; if( sheltered ) { // Norm for a house? tmphumidity = humidity * ( 100 - humidity ) / 100 + humidity; - } else if( weather == WEATHER_RAINY || weather == WEATHER_DRIZZLE || weather == WEATHER_THUNDER || - weather == WEATHER_LIGHTNING ) { + } else if( weather->rains && + weather->precip >= precip_class::light ) { tmphumidity = 100; } @@ -978,7 +975,7 @@ weather_manager::weather_manager() weather_override = WEATHER_NULL; nextweather = calendar::before_time_starts; temperature = 0; - weather = WEATHER_CLEAR; + weather_id = WEATHER_CLEAR; } const weather_generator &weather_manager::get_cur_weather_gen() const @@ -993,16 +990,13 @@ void weather_manager::update_weather() w_point &w = *weather_precise; winddirection = wind_direction_override ? *wind_direction_override : w.winddirection; windspeed = windspeed_override ? *windspeed_override : w.windpower; - if( weather == WEATHER_NULL || calendar::turn >= nextweather ) { + if( weather_id == WEATHER_NULL || calendar::turn >= nextweather ) { const weather_generator &weather_gen = get_cur_weather_gen(); w = weather_gen.get_weather( g->u.global_square_location(), calendar::turn, g->get_seed() ); - weather_type old_weather = weather; - weather = weather_override == WEATHER_NULL ? - weather_gen.get_weather_conditions( w ) - : weather_override; - if( weather == WEATHER_SUNNY && is_night( calendar::turn ) ) { - weather = WEATHER_CLEAR; - } + weather_type_id old_weather = weather_id; + weather_id = weather_override == WEATHER_NULL ? + weather_gen.get_weather_conditions( w ) + : weather_override; if( !g->u.has_artifact_with( AEP_BAD_WEATHER ) ) { weather_override = WEATHER_NULL; } @@ -1012,23 +1006,21 @@ 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 && here.is_outside( g->u.pos() ) + if( weather_id != old_weather && weather_id->dangerous && + g->get_levz() >= 0 && get_map().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 ) ); + string_format( _( "The weather changed to %s!" ), weather_id->name ) ); } - if( weather != old_weather && g->u.has_activity( ACT_WAIT_WEATHER ) ) { + if( weather_id != old_weather && g->u.has_activity( ACT_WAIT_WEATHER ) ) { g->u.assign_activity( ACT_WAIT_WEATHER, 0, 0 ); } - if( wdata.sight_penalty != - weather::sight_penalty( old_weather ) ) { + if( weather_id->sight_penalty != + old_weather->sight_penalty ) { for( int i = -OVERMAP_DEPTH; i <= OVERMAP_HEIGHT; i++ ) { - here.set_transparency_cache_dirty( i ); + get_map().set_transparency_cache_dirty( i ); } } } diff --git a/src/weather.h b/src/weather.h index b365cd4273328..903feabbbe66b 100644 --- a/src/weather.h +++ b/src/weather.h @@ -44,50 +44,8 @@ class item; struct trap; struct rl_vec2d; -/** - * Weather type enum. - */ -enum weather_type : int { - WEATHER_NULL, //!< For data and stuff - WEATHER_CLEAR, //!< No effects - WEATHER_SUNNY, //!< Glare if no eye protection - WEATHER_CLOUDY, //!< No effects - WEATHER_LIGHT_DRIZZLE,//!< very Light rain - WEATHER_DRIZZLE, //!< Light rain - WEATHER_RAINY, //!< Lots of rain, sight penalties - WEATHER_THUNDER, //!< Warns of lightning to come - WEATHER_LIGHTNING, //!< Rare lightning strikes! - WEATHER_ACID_DRIZZLE, //!< No real effects; warning of acid rain - WEATHER_ACID_RAIN, //!< Minor acid damage - WEATHER_FLURRIES, //!< Light snow - WEATHER_SNOW, //!< snow glare effects - WEATHER_SNOWSTORM, //!< sight penalties - NUM_WEATHER_TYPES //!< Sentinel value -}; - -enum class precip_class : int { - NONE, - VERY_LIGHT, - LIGHT, - HEAVY -}; - double precip_mm_per_hour( precip_class p ); -void do_rain( weather_type w ); - -/** - * Weather animation class. - */ -struct weather_animation_t { - float factor; - nc_color color; - char glyph; -}; - -/** - * Weather animation settings for the given type. - */ -weather_animation_t get_weather_animation( weather_type type ); +void handle_weather_effects( weather_type_id w ); /** * Weather drawing tracking. @@ -96,7 +54,7 @@ weather_animation_t get_weather_animation( weather_type type ); */ struct weather_printable { //!< Weather type in use. - weather_type wtype; + weather_type_id wtype; //!< Coordinates targeted for droplets. std::vector > vdrops; //!< Color to draw glyph this animation frame. @@ -111,43 +69,12 @@ struct weather_printable { */ namespace weather_effect { - -enum class sun_intensity : int { - normal = 1, - high -}; - -//!< Fallback weather. -void none(); -void glare( sun_intensity ); -void thunder(); -void lightning(); -void light_acid(); -void acid(); -//!< Currently flurries have no additional effects. -void flurry(); -void snow(); -void sunny(); -void snowstorm(); -} //namespace weather_effect - -using weather_effect_fn = void ( * )(); - -struct weather_datum { - std::string name; //!< UI name of weather type. - nc_color color; //!< UI color of weather type. - nc_color map_color; //!< Map color of weather type. - char glyph; //!< Map glyph of weather type. - int ranged_penalty; //!< Penalty to ranged attacks. - float sight_penalty; //!< Penalty to per-square visibility, applied in transparency map. - int light_modifier; //!< Modification to ambient light. - int sound_attn; //!< Sound attenuation of a given weather type. - bool dangerous; //!< If true, our activity gets interrupted. - precip_class precip; //!< Amount of associated precipitation. - bool rains; //!< Whether said precipitation falls as rain. - bool acidic; //!< Whether said precipitation is acidic. - weather_effect_fn effect; //!< Function pointer for weather effects. -}; +void thunder( int intensity ); +void lightning( int intensity ); +void light_acid( int intensity ); +void acid( int intensity ); +void wet_player( int amount ); +} // namespace weather_effect struct weather_sum { int rain_amount = 0; @@ -156,24 +83,7 @@ struct weather_sum { int wind_amount = 0; }; -weather_datum weather_data( weather_type type ); -namespace weather -{ -std::string name( weather_type type ); -nc_color color( weather_type type ); -nc_color map_color( weather_type type ); -char glyph( weather_type type ); -int ranged_penalty( weather_type type ); -float sight_penalty( weather_type type ); -int light_modifier( weather_type type ); -int sound_attn( weather_type type ); -bool dangerous( weather_type type ); -precip_class precip( weather_type type ); -bool rains( weather_type type ); -bool acidic( weather_type type ); -weather_effect_fn effect( weather_type type ); -} // namespace weather - +weather_type_id get_bad_weather(); std::string get_shortdirstring( int angle ); std::string get_dirstring( int angle ); @@ -193,7 +103,7 @@ std::string print_pressure( double pressure, int decimals = 0 ); // Return windchill offset in degrees F, starting from given temperature, humidity and wind int get_local_windchill( double temperature_f, double humidity, double wind_mph ); -int get_local_humidity( double humidity, weather_type weather, bool sheltered = false ); +int get_local_humidity( double humidity, weather_type_id weather, bool sheltered = false ); double get_local_windpower( double windpower, const oter_id &omter, const tripoint &location, const int &winddirection, bool sheltered = false ); @@ -231,14 +141,15 @@ bool warm_enough_to_plant( const tripoint &pos ); bool is_wind_blocker( const tripoint &location ); -weather_type current_weather( const tripoint &location, - const time_point &t = calendar::turn ); +weather_type_id current_weather( const tripoint &location, + const time_point &t = calendar::turn ); +void glare( weather_type_id w ); /** * Amount of sunlight incident at the ground, taking weather and time of day * into account. */ -int incident_sunlight( weather_type wtype, +int incident_sunlight( weather_type_id wtype, const time_point &t = calendar::turn ); class weather_manager @@ -252,14 +163,14 @@ class weather_manager int temperature = 0; bool lightning_active = false; // Weather pattern - weather_type weather = weather_type::WEATHER_NULL; + weather_type_id weather_id = WEATHER_NULL; int winddirection = 0; int windspeed = 0; // Cached weather data pimpl weather_precise; cata::optional wind_direction_override; cata::optional windspeed_override; - weather_type weather_override; + weather_type_id weather_override; // not only sets nextweather, but updates weather as well void set_nextweather( time_point t ); // The time at which weather will shift next. diff --git a/src/weather_data.cpp b/src/weather_data.cpp deleted file mode 100644 index 04591a419d955..0000000000000 --- a/src/weather_data.cpp +++ /dev/null @@ -1,189 +0,0 @@ -#include "weather.h" // IWYU pragma: associated - -#include -#include -#include -#include - -#include "color.h" -#include "translations.h" - -/** - * @ingroup Weather - * @{ - */ - -weather_animation_t get_weather_animation( weather_type const type ) -{ - static const std::map map { - {WEATHER_ACID_DRIZZLE, weather_animation_t {0.01f, c_light_green, '.'}}, - {WEATHER_ACID_RAIN, weather_animation_t {0.02f, c_light_green, ','}}, - {WEATHER_LIGHT_DRIZZLE, weather_animation_t{0.01f, c_light_blue, ','}}, - {WEATHER_DRIZZLE, weather_animation_t {0.01f, c_light_blue, '.'}}, - {WEATHER_RAINY, weather_animation_t {0.02f, c_light_blue, ','}}, - {WEATHER_THUNDER, weather_animation_t {0.02f, c_light_blue, '.'}}, - {WEATHER_LIGHTNING, weather_animation_t {0.04f, c_light_blue, ','}}, - {WEATHER_FLURRIES, weather_animation_t {0.01f, c_white, '.'}}, - {WEATHER_SNOW, weather_animation_t {0.02f, c_white, ','}}, - {WEATHER_SNOWSTORM, weather_animation_t {0.04f, c_white, '*'}} - }; - - const auto it = map.find( type ); - if( it != std::end( map ) ) { - return it->second; - } - - return {0.0f, c_white, '?'}; -} -struct weather_result { - weather_datum datum; - bool is_valid; -}; -static weather_result weather_data_internal( weather_type const type ) -{ - /** - * Weather types data definition. - * Name, color in UI, color and glyph on map, ranged penalty, sight penalty, - * light modifier, sound attenuation, warn player? - * Note light modifier assumes baseline of default_daylight_level() at 60 - */ - // TODO: but it actually isn't 60, it's 100. Fix this comment or fix the value - static const std::array data {{ - weather_datum { - "NULL Weather - BUG (weather_data.cpp:weather_data)", c_magenta, c_magenta_red, - '0', 0, 0.0f, 0, 0, false, - precip_class::NONE, false, false, &weather_effect::none - }, - weather_datum { - translate_marker( "Clear" ), c_cyan, c_yellow_white, ' ', 0, 1.0f, 0, 0, false, - precip_class::NONE, false, false, &weather_effect::none - }, - weather_datum { - translate_marker( "Sunny" ), c_light_cyan, c_yellow_white, '*', 0, 1.0f, 2, 0, false, - precip_class::NONE, false, false, &weather_effect::sunny - }, - weather_datum { - translate_marker( "Cloudy" ), c_light_gray, c_dark_gray_white, '~', 0, 1.0f, -20, 0, false, - precip_class::NONE, false, false, &weather_effect::none - }, - weather_datum { - translate_marker( "Light Drizzle" ), c_light_blue, h_light_blue, '.', 0, 1.01f, -10, 0, false, - precip_class::VERY_LIGHT, true, false, &weather_effect::none - }, - weather_datum { - translate_marker( "Drizzle" ), c_light_blue, h_light_blue, '.', 1, 1.03f, -20, 1, false, - precip_class::LIGHT, true, false, &weather_effect::none - }, - weather_datum { - translate_marker( "Rain" ), c_blue, h_blue, 'o', 3, 1.1f, -30, 4, false, - precip_class::HEAVY, true, false, &weather_effect::none - }, - weather_datum { - translate_marker( "Thunder Storm" ), c_dark_gray, i_blue, '%', 4, 1.2f, -40, 8, false, - precip_class::HEAVY, true, false, &weather_effect::thunder - }, - weather_datum { - translate_marker( "Lightning Storm" ), c_yellow, h_yellow, '%', 4, 1.25f, -45, 8, false, - precip_class::HEAVY, true, false, &weather_effect::lightning - }, - weather_datum { - translate_marker( "Acidic Drizzle" ), c_light_green, c_yellow_green, '.', 2, 1.03f, -20, 1, true, - precip_class::LIGHT, true, true, &weather_effect::light_acid - }, - weather_datum { - translate_marker( "Acid Rain" ), c_green, c_yellow_green, 'o', 4, 1.1f, -30, 4, true, - precip_class::HEAVY, true, true, &weather_effect::acid - }, - weather_datum { - translate_marker( "Flurries" ), c_white, c_dark_gray_cyan, '.', 2, 1.12f, -15, 2, false, - precip_class::LIGHT, false, false, &weather_effect::flurry - }, - weather_datum { - translate_marker( "Snowing" ), c_white, c_dark_gray_cyan, '*', 4, 1.13f, -20, 4, false, - precip_class::HEAVY, false, false, &weather_effect::snow - }, - weather_datum { - translate_marker( "Snowstorm" ), c_white, c_white_cyan, '%', 6, 1.2f, -30, 6, false, - precip_class::HEAVY, false, false, &weather_effect::snowstorm - } - }}; - - const size_t i = static_cast( type ); - if( i < NUM_WEATHER_TYPES ) { - return { data[i], i > 0 }; - } - - return { data[0], false }; -} - -static weather_datum weather_data_interal_localized( weather_type const type ) -{ - weather_result res = weather_data_internal( type ); - if( res.is_valid ) { - res.datum.name = _( res.datum.name ); - } - return res.datum; -} - -weather_datum weather_data( weather_type const type ) -{ - return weather_data_interal_localized( type ); -} - -namespace weather -{ -std::string name( weather_type const type ) -{ - return weather_data_interal_localized( type ).name; -} -nc_color color( weather_type const type ) -{ - return weather_data_internal( type ).datum.color; -} -nc_color map_color( weather_type const type ) -{ - return weather_data_internal( type ).datum.map_color; -} -char glyph( weather_type const type ) -{ - return weather_data_internal( type ).datum.glyph; -} -int ranged_penalty( weather_type const type ) -{ - return weather_data_internal( type ).datum.ranged_penalty; -} -float sight_penalty( weather_type const type ) -{ - return weather_data_internal( type ).datum.sight_penalty; -} -int light_modifier( weather_type const type ) -{ - return weather_data_internal( type ).datum.light_modifier; -} -int sound_attn( weather_type const type ) -{ - return weather_data_internal( type ).datum.sound_attn; -} -bool dangerous( weather_type const type ) -{ - return weather_data_internal( type ).datum.dangerous; -} -precip_class precip( weather_type const type ) -{ - return weather_data_internal( type ).datum.precip; -} -bool rains( weather_type const type ) -{ - return weather_data_internal( type ).datum.rains; -} -bool acidic( weather_type const type ) -{ - return weather_data_internal( type ).datum.acidic; -} -weather_effect_fn effect( weather_type const type ) -{ - return weather_data_internal( type ).datum.effect; -} -} // namespace weather - -///@} diff --git a/src/weather_gen.cpp b/src/weather_gen.cpp index 2dec277b23ea1..b715a1300627c 100644 --- a/src/weather_gen.cpp +++ b/src/weather_gen.cpp @@ -86,7 +86,7 @@ static double weather_temperature_from_common_data( const weather_generator &wg, const int seasonal_temp_mod[4] = { wg.spring_temp_manual_mod, wg.summer_temp_manual_mod, wg.autumn_temp_manual_mod, wg.winter_temp_manual_mod }; const double baseline( wg.base_temperature + - seasonal_temp_mod[ season ] + + seasonal_temp_mod[season] + dayv * daily_magnitude_K + seasonality * seasonality_magnitude_K ); @@ -146,7 +146,7 @@ w_point weather_generator::get_weather( const tripoint &location, const time_poi 10 * ( -seasonality + 2 ); // Wind power - W = std::max( 0, static_cast( base_wind * rng( 1, 2 ) / std::pow( ( P + W ) / 1014.78, rng( 9, + W = std::max( 0, static_cast( base_wind * rng( 1, 2 ) / std::pow( ( P + W ) / 1014.78, rng( 9, base_wind_distrib_peaks ) ) + -cyf / base_wind_season_variation * rng( 1, 2 ) ) ); // Initial static variable @@ -165,65 +165,60 @@ w_point weather_generator::get_weather( const tripoint &location, const time_poi // Acid rains const double acid_content = base_acid * A; bool acid = acid_content >= 1.0; - return w_point {T, H, P, W, wind_desc, current_winddir, acid}; + return w_point{ T, H, P, W, wind_desc, current_winddir, acid }; } -weather_type weather_generator::get_weather_conditions( const tripoint &location, +weather_type_id weather_generator::get_weather_conditions( const tripoint &location, const time_point &t, unsigned seed ) const { w_point w( get_weather( location, t, seed ) ); - weather_type wt = get_weather_conditions( w ); - // Make sure we don't say it's sunny at night! =P - if( wt == WEATHER_SUNNY && is_night( t ) ) { - return WEATHER_CLEAR; - } + weather_type_id wt = get_weather_conditions( w ); return wt; } -weather_type weather_generator::get_weather_conditions( const w_point &w ) const +weather_type_id weather_generator::get_weather_conditions( const w_point &w ) const { - weather_type r( WEATHER_CLEAR ); - if( w.pressure > 1020 && w.humidity < 70 ) { - r = WEATHER_SUNNY; - } - if( w.pressure < 1010 && w.humidity > 40 ) { - r = WEATHER_CLOUDY; - } - if( r == WEATHER_CLOUDY && ( w.humidity > 96 || w.pressure < 1003 ) ) { - r = WEATHER_LIGHT_DRIZZLE; - } - if( r >= WEATHER_LIGHT_DRIZZLE && ( w.humidity > 97 || w.pressure < 1000 ) ) { - r = WEATHER_DRIZZLE; - } - if( r >= WEATHER_CLOUDY && ( w.humidity > 98 || w.pressure < 993 ) ) { - r = WEATHER_RAINY; - } - if( r == WEATHER_RAINY && w.pressure < 996 ) { - r = WEATHER_THUNDER; - } - if( r == WEATHER_THUNDER && w.pressure < 990 ) { - r = WEATHER_LIGHTNING; - } + weather_type_id current_conditions = WEATHER_CLEAR; + for( const std::string &weather_type : weather_types ) { + weather_type_id type = weather_type_id( weather_type ); + + const weather_requirements &requires = type->requirements; + bool test_pressure = + requires.pressure_max > w.pressure && + requires.pressure_min < w.pressure; + bool test_humidity = + requires.humidity_max > w.humidity && + requires.humidity_min < w.humidity; + if( ( requires.humidity_and_pressure && !( test_pressure && test_humidity ) ) || + ( !requires.humidity_and_pressure && !( test_pressure || test_humidity ) ) ) { + continue; + } + bool test_temperature = + requires.temperature_max > w.temperature && + requires.temperature_min < w.temperature; + bool test_windspeed = + requires.windpower_max > w.windpower && + requires.windpower_min < w.windpower; + bool test_acidic = !requires.acidic || w.acidic; + if( !( test_temperature && test_windspeed && test_acidic ) ) { + continue; + } - if( w.temperature <= 32 ) { - if( r == WEATHER_DRIZZLE ) { - r = WEATHER_FLURRIES; - } else if( r > WEATHER_DRIZZLE ) { - if( r >= WEATHER_THUNDER && w.windpower > 15 ) { - r = WEATHER_SNOWSTORM; - } else { - r = WEATHER_SNOW; + if( !requires.required_weathers.empty() ) { + if( std::find( requires.required_weathers.begin(), requires.required_weathers.end(), + current_conditions ) == requires.required_weathers.end() ) { + continue; } } - } - if( r == WEATHER_DRIZZLE && w.acidic ) { - r = WEATHER_ACID_DRIZZLE; - } - if( r > WEATHER_DRIZZLE && w.acidic ) { - r = WEATHER_ACID_RAIN; + if( !( requires.time == weather_time_requirement_type::both || + ( requires.time == weather_time_requirement_type::day && is_day( calendar::turn ) ) || + ( requires.time == weather_time_requirement_type::night && !is_day( calendar::turn ) ) ) ) { + continue; + } + current_conditions = type; } - return r; + return current_conditions; } int weather_generator::get_wind_direction( const season_type season ) const @@ -298,8 +293,7 @@ void weather_generator::test_weather( unsigned seed = 1000 ) const const time_point end = begin + 2 * calendar::year_length(); for( time_point i = begin; i < end; i += 20_minutes ) { w_point w = get_weather( tripoint_zero, to_turn( i ), seed ); - weather_type c = get_weather_conditions( w ); - weather_datum wd = weather_data( c ); + weather_type_id conditions = get_weather_conditions( w ); int year = to_turns( i - calendar::turn_zero ) / to_turns ( calendar::year_length() ) + 1; @@ -312,9 +306,11 @@ void weather_generator::test_weather( unsigned seed = 1000 ) const day = day_of_season( i ); } testfile << "|;" << year << ";" << season_of_year( i ) << ";" << day << ";" << hour << ";" << minute - << ";" << w.temperature << ";" << w.humidity << ";" << w.pressure << ";" << wd.name << ";" << + << ";" << w.temperature << ";" << w.humidity << ";" << w.pressure << ";" << conditions->name << ";" + << w.windpower << ";" << w.winddirection << std::endl; } + }, "weather test file" ); } @@ -336,5 +332,9 @@ weather_generator weather_generator::load( const JsonObject &jo ) ret.summer_humidity_manual_mod = jo.get_int( "summer_humidity_manual_mod", 0 ); ret.autumn_humidity_manual_mod = jo.get_int( "autumn_humidity_manual_mod", 0 ); ret.winter_humidity_manual_mod = jo.get_int( "winter_humidity_manual_mod", 0 ); + ret.weather_types = jo.get_string_array( "weather_types" ); + if( ret.weather_types.size() < 2 ) { + jo.throw_error( "Need at least 2 weather types per region for null and default." ); + } return ret; } diff --git a/src/weather_gen.h b/src/weather_gen.h index 9b269ec82cced..92f6b4c031711 100644 --- a/src/weather_gen.h +++ b/src/weather_gen.h @@ -3,14 +3,15 @@ #define CATA_SRC_WEATHER_GEN_H #include +#include #include "calendar.h" +#include "color.h" +#include "weather_type.h" struct tripoint; class JsonObject; -enum weather_type : int; - struct w_point { double temperature = 0; double humidity = 0; @@ -46,7 +47,7 @@ class weather_generator //How much the wind folows seasonal variation ( lower means more change ) int base_wind_season_variation = 0; static int current_winddir; - + std::vector weather_types; weather_generator(); /** @@ -55,8 +56,9 @@ class weather_generator * relative position (relative to the map you called getabs on). */ w_point get_weather( const tripoint &, const time_point &, unsigned ) const; - weather_type get_weather_conditions( const tripoint &, const time_point &, unsigned seed ) const; - weather_type get_weather_conditions( const w_point & ) const; + weather_type_id get_weather_conditions( const tripoint &, const time_point &, + unsigned seed ) const; + weather_type_id get_weather_conditions( const w_point & ) const; int get_wind_direction( season_type ) const; int convert_winddir( int ) const; int get_water_temperature() const; diff --git a/src/weather_type.cpp b/src/weather_type.cpp new file mode 100644 index 0000000000000..f87bf7f8e2638 --- /dev/null +++ b/src/weather_type.cpp @@ -0,0 +1,247 @@ +#include "weather_type.h" +#include "weather.h" +#include "game_constants.h" + +namespace +{ +generic_factory weather_type_factory( "weather_type" ); +} // namespace + + +namespace io +{ +template<> +std::string enum_to_string( precip_class data ) +{ + switch( data ) { + case precip_class::none: + return "none"; + case precip_class::very_light: + return "very_light"; + case precip_class::light: + return "light"; + case precip_class::heavy: + return "heavy"; + case precip_class::last: + break; + } + debugmsg( "Invalid precip_class" ); + abort(); +} + +template<> +std::string enum_to_string( sun_intensity_type data ) +{ + switch( data ) { + case sun_intensity_type::none: + return "none"; + case sun_intensity_type::light: + return "light"; + case sun_intensity_type::normal: + return "normal"; + case sun_intensity_type::high: + return "high"; + case sun_intensity_type::last: + break; + } + debugmsg( "Invalid sun_intensity_type" ); + abort(); +} + +template<> +std::string enum_to_string( weather_time_requirement_type data ) +{ + switch( data ) { + case weather_time_requirement_type::day: + return "day"; + case weather_time_requirement_type::night: + return "night"; + case weather_time_requirement_type::both: + return "both"; + case weather_time_requirement_type::last: + break; + } + debugmsg( "Invalid time_requirement_type" ); + abort(); +} + +template<> +std::string enum_to_string( weather_sound_category data ) +{ + switch( data ) { + case weather_sound_category::drizzle: + return "drizzle"; + case weather_sound_category::flurries: + return "flurries"; + case weather_sound_category::rainy: + return "rainy"; + case weather_sound_category::snow: + return "snow"; + case weather_sound_category::snowstorm: + return "snowstorm"; + case weather_sound_category::thunder: + return "thunder"; + case weather_sound_category::silent: + return "silent"; + case weather_sound_category::last: + break; + } + debugmsg( "Invalid time_requirement_type" ); + abort(); +} + +} // namespace io + +template<> +const weather_type &weather_type_id::obj() const +{ + return weather_type_factory.obj( *this ); +} + +/** @relates string_id */ +template<> +bool string_id::is_valid() const +{ + return weather_type_factory.is_valid( *this ); +} + +void weather_type::finalize() +{ + +} + +void weather_type::check() const +{ + for( const weather_type_id &required : requirements.required_weathers ) { + if( !required.is_valid() ) { + debugmsg( "Required weather type %s does not exist.", required.c_str() ); + abort(); + } + } +} + +void weather_type::load( const JsonObject &jo, const std::string & ) +{ + mandatory( jo, was_loaded, "name", name ); + mandatory( jo, was_loaded, "id", id ); + + assign( jo, "color", color ); + assign( jo, "map_color", map_color ); + + std::string glyph; + mandatory( jo, was_loaded, "glyph", glyph ); + if( glyph.size() != 1 ) { + jo.throw_error( "glyph must be only one character" ); + } else { + glyph = glyph[0]; + } + mandatory( jo, was_loaded, "ranged_penalty", ranged_penalty ); + mandatory( jo, was_loaded, "sight_penalty", sight_penalty ); + mandatory( jo, was_loaded, "light_modifier", light_modifier ); + + mandatory( jo, was_loaded, "sound_attn", sound_attn ); + mandatory( jo, was_loaded, "dangerous", dangerous ); + mandatory( jo, was_loaded, "precip", precip ); + mandatory( jo, was_loaded, "rains", rains ); + optional( jo, was_loaded, "acidic", acidic, false ); + optional( jo, was_loaded, "tiles_animation", tiles_animation, "" ); + optional( jo, was_loaded, "sound_category", sound_category, weather_sound_category::silent ); + mandatory( jo, was_loaded, "sun_intensity", sun_intensity ); + + for( const JsonObject weather_effect : jo.get_array( "effects" ) ) { + + std::pair pair = std::make_pair( weather_effect.get_string( "name" ), + weather_effect.get_int( "intensity" ) ); + + static const std::map all_weather_effects = { + { "wet", &weather_effect::wet_player }, + { "thunder", &weather_effect::thunder }, + { "lightning", &weather_effect::lightning }, + { "light_acid", &weather_effect::light_acid }, + { "acid", &weather_effect::acid } + }; + const auto iter = all_weather_effects.find( pair.first ); + if( iter == all_weather_effects.end() ) { + weather_effect.throw_error( "Invalid weather effect", "name" ); + } + effects.emplace_back( iter->second, pair.second ); + } + weather_animation = { 0.0f, c_white, '?' }; + if( jo.has_member( "weather_animation" ) ) { + JsonObject weather_animation_jo = jo.get_object( "weather_animation" ); + weather_animation_t animation; + mandatory( weather_animation_jo, was_loaded, "factor", animation.factor ); + if( !assign( weather_animation_jo, "color", animation.color ) ) { + weather_animation_jo.throw_error( "missing mandatory member \"color\"" ); + } + mandatory( weather_animation_jo, was_loaded, "glyph", glyph ); + if( glyph.size() != 1 ) { + weather_animation_jo.throw_error( "glyph must be only one character" ); + } else { + animation.glyph = glyph[0]; + } + weather_animation = animation; + } + + requirements = {}; + if( jo.has_member( "requirements" ) ) { + JsonObject weather_requires = jo.get_object( "requirements" ); + weather_requirements new_requires; + + optional( weather_requires, was_loaded, "pressure_min", new_requires.pressure_min, INT_MIN ); + optional( weather_requires, was_loaded, "pressure_max", new_requires.pressure_max, INT_MAX ); + optional( weather_requires, was_loaded, "humidity_min", new_requires.humidity_min, INT_MIN ); + optional( weather_requires, was_loaded, "humidity_max", new_requires.humidity_max, INT_MAX ); + optional( weather_requires, was_loaded, "temperature_min", new_requires.temperature_min, INT_MIN ); + optional( weather_requires, was_loaded, "temperature_max", new_requires.temperature_max, INT_MAX ); + optional( weather_requires, was_loaded, "windpower_min", new_requires.windpower_min, INT_MIN ); + optional( weather_requires, was_loaded, "windpower_max", new_requires.windpower_max, INT_MAX ); + optional( weather_requires, was_loaded, "humidity_and_pressure", new_requires.humidity_and_pressure, + true ); + optional( weather_requires, was_loaded, "acidic", new_requires.acidic, false ); + optional( weather_requires, was_loaded, "time", new_requires.time, + weather_time_requirement_type::both ); + for( const std::string &required_weather : + weather_requires.get_string_array( "required_weathers" ) ) { + new_requires.required_weathers.push_back( weather_type_id( required_weather ) ); + } + requirements = new_requires; + } +} + +void weather_types::reset() +{ + weather_type_factory.reset(); +} + +void weather_types::finalize_all() +{ + weather_type_factory.finalize(); + for( const weather_type &wt : weather_type_factory.get_all() ) { + const_cast( wt ).finalize(); + } +} + +const std::vector &weather_types::get_all() +{ + return weather_type_factory.get_all(); +} + +void weather_types::check_consistency() +{ + if( !WEATHER_CLEAR.is_valid() ) { + debugmsg( "Weather type clear is required." ); + abort(); + } + if( !WEATHER_NULL.is_valid() ) { + debugmsg( "Weather type null is required." ); + abort(); + } + weather_type_factory.check(); +} + +void weather_types::load( const JsonObject &jo, const std::string &src ) +{ + weather_type_factory.load( jo, src ); +} + diff --git a/src/weather_type.h b/src/weather_type.h new file mode 100644 index 0000000000000..084a0110648e2 --- /dev/null +++ b/src/weather_type.h @@ -0,0 +1,134 @@ +#pragma once +#ifndef CATA_SRC_WEATHER_TYPE_H +#define CATA_SRC_WEATHER_TYPE_H + +#include + +#include "generic_factory.h" +#include "translations.h" +#include "type_id.h" + +using weather_effect_fn = void ( * )( int intensity ); + +const weather_type_id WEATHER_NULL( "null" ); +const weather_type_id WEATHER_CLEAR( "clear" ); + +enum class precip_class : int { + none, + very_light, + light, + heavy, + last +}; +template<> +struct enum_traits { + static constexpr precip_class last = precip_class::last; +}; + +enum class sun_intensity_type : int { + none, + light, + normal, + high, + last +}; +template<> +struct enum_traits { + static constexpr sun_intensity_type last = sun_intensity_type::last; +}; + +enum class weather_time_requirement_type : int { + day, + night, + both, + last +}; +template<> +struct enum_traits { + static constexpr weather_time_requirement_type last = weather_time_requirement_type::last; +}; + + +enum weather_sound_category : int { + silent, + drizzle, + rainy, + thunder, + flurries, + snowstorm, + snow, + last +}; + +template<> +struct enum_traits { + static constexpr weather_sound_category last = weather_sound_category::last; +}; + +/** + * Weather animation class. + */ +struct weather_animation_t { + float factor; + nc_color color; + char glyph; +}; + +struct weather_requirements { + int windpower_min = INT_MIN; + int windpower_max = INT_MAX; + int temperature_min = INT_MIN; + int temperature_max = INT_MAX; + int pressure_min = INT_MIN; + int pressure_max = INT_MAX; + int humidity_min = INT_MIN; + int humidity_max = INT_MAX; + bool humidity_and_pressure = true; + bool acidic = false; + weather_time_requirement_type time; + std::vector required_weathers; +}; + +struct weather_type { + public: + friend class generic_factory; + bool was_loaded = false; + weather_type_id id; + std::string name; //!< UI name of weather type. + nc_color color; //!< UI color of weather type. + nc_color map_color; //!< Map color of weather type. + char glyph; //!< Map glyph of weather type. + int ranged_penalty; //!< Penalty to ranged attacks. + float sight_penalty; //!< Penalty to per-square visibility, applied in transparency map. + int light_modifier; //!< Modification to ambient light. + int sound_attn; //!< Sound attenuation of a given weather type. + bool dangerous; //!< If true, our activity gets interrupted. + precip_class precip; //!< Amount of associated precipitation. + bool rains; //!< Whether said precipitation falls as rain. + bool acidic; //!< Whether said precipitation is acidic. + std::vector < std::pair < weather_effect_fn, int >> effects; //!< vector for weather effects. + std::string tiles_animation; //!< string for tiles animation + weather_animation_t weather_animation; //!< Information for weather animations + weather_sound_category sound_category; //!< if playing sound effects what to use + sun_intensity_type sun_intensity; //!< strength of the sun + weather_requirements requirements; //!< when this weather should happen + + void load( const JsonObject &jo, const std::string &src ); + void finalize(); + void check() const; + weather_type() = default; +}; +namespace weather_types +{ +/** Get all currently loaded weather types */ +const std::vector &get_all(); +/** Finalize all loaded weather types */ +void finalize_all(); +/** Clear all loaded weather types (invalidating any pointers) */ +void reset(); +/** Load weather type from JSON definition */ +void load( const JsonObject &jo, const std::string &src ); +/** Checks all loaded from JSON are valid */ +void check_consistency(); +} // namespace weather_types +#endif // CATA_SRC_WEATHER_TYPE_H diff --git a/src/wish.cpp b/src/wish.cpp index 1c8691355e6ad..28284f0a2b7d3 100644 --- a/src/wish.cpp +++ b/src/wish.cpp @@ -409,7 +409,7 @@ void debug_menu::wishmonster( const cata::optional &p ) const mtype_id &mon_type = mtypes[ wmenu.ret ]->id; if( cata::optional spawn = p ? p : g->look_around() ) { int num_spawned = 0; - for( const tripoint &destination : closest_tripoints_first( *spawn, cb.group ) ) { + for( const tripoint &destination : closest_points_first( *spawn, cb.group ) ) { monster *const mon = g->place_critter_at( mon_type, destination ); if( !mon ) { continue; @@ -594,16 +594,24 @@ void debug_menu::wishitem( player *p, const tripoint &pos ) if( granted.count_by_charges() ) { if( amount > 0 ) { granted.charges = amount; - p->i_add( granted ); + if( p->can_stash( granted ) ) { + p->i_add( granted ); + } else { + get_map().add_item_or_charges( p->pos(), granted ); + } } } else { for( int i = 0; i < amount; i++ ) { - p->i_add( granted ); + if( p->can_stash( granted ) ) { + p->i_add( granted ); + } else { + get_map().add_item_or_charges( p->pos(), granted ); + } } } 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/cata_generators.cpp b/tests/cata_generators.cpp new file mode 100644 index 0000000000000..ede6a7838c2b6 --- /dev/null +++ b/tests/cata_generators.cpp @@ -0,0 +1,67 @@ +#include "cata_generators.h" + +#include "point.h" +#include "rng.h" + +class RandomPointGenerator final : + public Catch::Generators::IGenerator +{ + public: + RandomPointGenerator( int low, int high ) : + engine( rng_get_engine() ), + dist( low, high ) { + this->next(); + } + + const point &get() const override { + return current_point; + } + + bool next() override { + current_point = point( dist( engine ), dist( engine ) ); + return true; + } + protected: + cata_default_random_engine &engine; + std::uniform_int_distribution<> dist; + point current_point; +}; + +class RandomTripointGenerator final : + public Catch::Generators::IGenerator +{ + public: + RandomTripointGenerator( int low, int high, int zlow, int zhigh ) : + engine( rng_get_engine() ), + xy_dist( low, high ), + z_dist( zlow, zhigh ) { + this->next(); + } + + const tripoint &get() const override { + return current_point; + } + + bool next() override { + current_point = tripoint( xy_dist( engine ), xy_dist( engine ), z_dist( engine ) ); + return true; + } + protected: + cata_default_random_engine &engine; + std::uniform_int_distribution<> xy_dist; + std::uniform_int_distribution<> z_dist; + tripoint current_point; +}; + +Catch::Generators::GeneratorWrapper random_points( int low, int high ) +{ + return Catch::Generators::GeneratorWrapper( + std::make_unique( low, high ) ); +} + +Catch::Generators::GeneratorWrapper random_tripoints( + int low, int high, int zlow, int zhigh ) +{ + return Catch::Generators::GeneratorWrapper( + std::make_unique( low, high, zlow, zhigh ) ); +} diff --git a/tests/cata_generators.h b/tests/cata_generators.h new file mode 100644 index 0000000000000..85dbc1c6a0e9f --- /dev/null +++ b/tests/cata_generators.h @@ -0,0 +1,18 @@ +#pragma once +#ifndef CATA_TESTS_CATA_GENERATORS_H +#define CATA_TESTS_CATA_GENERATORS_H + +// Some Catch2 Generators for generating our data types + +#include "catch/catch.hpp" +#include "game_constants.h" + +struct point; +struct tripoint; + +Catch::Generators::GeneratorWrapper random_points( int low = -1000, int high = 1000 ); + +Catch::Generators::GeneratorWrapper random_tripoints( + int low = -1000, int high = 1000, int zlow = -OVERMAP_DEPTH, int zhigh = OVERMAP_HEIGHT ); + +#endif // CATA_TESTS_CATA_GENERATORS_H diff --git a/tests/char_biometrics_test.cpp b/tests/char_biometrics_test.cpp index e233bbc4c6425..b2a376704b75a 100644 --- a/tests/char_biometrics_test.cpp +++ b/tests/char_biometrics_test.cpp @@ -496,6 +496,7 @@ TEST_CASE( "activity level reset, increase and decrease", "[biometrics][activity TEST_CASE( "mutations may affect character metabolic rate", "[biometrics][metabolism]" ) { avatar dummy; + dummy.set_body(); // Metabolic base rate uses PLAYER_HUNGER_RATE from game_balance.json, described as "base hunger // rate per 5 minutes". With no metabolism-affecting mutations, metabolism should be this value. @@ -532,6 +533,7 @@ TEST_CASE( "mutations may affect character metabolic rate", "[biometrics][metabo TEST_CASE( "basal metabolic rate with various size and metabolism", "[biometrics][bmr]" ) { avatar dummy; + dummy.set_body(); // Basal metabolic rate depends on size (height), bodyweight (BMI), and activity level // scaled by metabolic base rate. Assume default metabolic rate. diff --git a/tests/char_edible_rating_test.cpp b/tests/char_edible_rating_test.cpp index 241b349c0e2cc..d5eb07d8a013d 100644 --- a/tests/char_edible_rating_test.cpp +++ b/tests/char_edible_rating_test.cpp @@ -71,6 +71,7 @@ TEST_CASE( "cannot eat dirty food", "[can_eat][edible_rating][dirty]" ) TEST_CASE( "who can eat while underwater", "[can_eat][edible_rating][underwater]" ) { avatar dummy; + dummy.set_body(); item sushi( "sushi_fishroll" ); item water( "water_clean" ); @@ -194,7 +195,7 @@ TEST_CASE( "when frozen food can be eaten", "[can_eat][edible_rating][frozen]" ) TEST_CASE( "who can eat inedible animal food", "[can_eat][edible_rating][inedible][animal]" ) { avatar dummy; - + dummy.set_body(); // Note: There are similar conditions for INEDIBLE food with FELINE or LUPINE flags, but // "birdfood" and "cattlefodder" are the only INEDIBLE items that exist in the game. @@ -246,6 +247,7 @@ TEST_CASE( "who can eat inedible animal food", "[can_eat][edible_rating][inedibl TEST_CASE( "what herbivores can eat", "[can_eat][edible_rating][herbivore]" ) { avatar dummy; + dummy.set_body(); GIVEN( "character is an herbivore" ) { dummy.toggle_trait( trait_id( "HERBIVORE" ) ); @@ -272,6 +274,7 @@ TEST_CASE( "what herbivores can eat", "[can_eat][edible_rating][herbivore]" ) TEST_CASE( "what carnivores can eat", "[can_eat][edible_rating][carnivore]" ) { avatar dummy; + dummy.set_body(); GIVEN( "character is a carnivore" ) { dummy.toggle_trait( trait_id( "CARNIVORE" ) ); @@ -320,6 +323,7 @@ TEST_CASE( "what carnivores can eat", "[can_eat][edible_rating][carnivore]" ) TEST_CASE( "what you can eat with a mycus dependency", "[can_eat][edible_rating][mycus]" ) { avatar dummy; + dummy.set_body(); GIVEN( "character is mycus-dependent" ) { dummy.toggle_trait( trait_id( "M_DEPENDENT" ) ); @@ -344,6 +348,7 @@ TEST_CASE( "what you can eat with a mycus dependency", "[can_eat][edible_rating] TEST_CASE( "what you can drink with a proboscis", "[can_eat][edible_rating][proboscis]" ) { avatar dummy; + dummy.set_body(); GIVEN( "character has a proboscis" ) { dummy.toggle_trait( trait_id( "PROBOSCIS" ) ); @@ -413,6 +418,7 @@ TEST_CASE( "can eat with nausea", "[will_eat][edible_rating][nausea]" ) TEST_CASE( "can eat with allergies", "[will_eat][edible_rating][allergy]" ) { avatar dummy; + dummy.set_body(); item fruit( "apple" ); REQUIRE( fruit.has_flag( "ALLERGEN_FRUIT" ) ); @@ -430,6 +436,7 @@ TEST_CASE( "can eat with allergies", "[will_eat][edible_rating][allergy]" ) TEST_CASE( "who will eat rotten food", "[will_eat][edible_rating][rotten]" ) { avatar dummy; + dummy.set_body(); GIVEN( "food just barely rotten" ) { item toastem_rotten = item( "toastem" ); @@ -480,6 +487,7 @@ TEST_CASE( "who will eat rotten food", "[will_eat][edible_rating][rotten]" ) TEST_CASE( "who will eat cooked human flesh", "[will_eat][edible_rating][cannibal]" ) { avatar dummy; + dummy.set_body(); GIVEN( "some cooked human flesh" ) { item flesh( "human_cooked" ); diff --git a/tests/char_stamina_test.cpp b/tests/char_stamina_test.cpp index 031d154c1629d..1e3c588b2d647 100644 --- a/tests/char_stamina_test.cpp +++ b/tests/char_stamina_test.cpp @@ -428,6 +428,7 @@ TEST_CASE( "stamina regen with mouth encumbrance", "[stamina][update][regen][enc player &dummy = g->u; clear_character( dummy ); catch_breath( dummy ); + dummy.set_body(); int turn_moves = to_moves( 1_turns ); @@ -436,7 +437,7 @@ TEST_CASE( "stamina regen with mouth encumbrance", "[stamina][update][regen][enc GIVEN( "character has mouth encumbrance" ) { dummy.wear_item( item( "scarf_fur" ) ); - REQUIRE( dummy.encumb( bp_mouth ) == 10 ); + REQUIRE( dummy.encumb( bodypart_id( "mouth" ) ) == 10 ); THEN( "stamina regen is reduced" ) { CHECK( actual_regen_rate( dummy, turn_moves ) == ( normal_regen_rate - 2 ) * turn_moves ); @@ -444,7 +445,7 @@ TEST_CASE( "stamina regen with mouth encumbrance", "[stamina][update][regen][enc WHEN( "they have even more mouth encumbrance" ) { // Layering two scarves triples the encumbrance dummy.wear_item( item( "scarf_fur" ) ); - REQUIRE( dummy.encumb( bp_mouth ) == 30 ); + REQUIRE( dummy.encumb( bodypart_id( "mouth" ) ) == 30 ); THEN( "stamina regen is reduced further" ) { CHECK( actual_regen_rate( dummy, turn_moves ) == ( normal_regen_rate - 6 ) * turn_moves ); 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/coordinate_test.cpp b/tests/coordinate_test.cpp new file mode 100644 index 0000000000000..a8798b41095d8 --- /dev/null +++ b/tests/coordinate_test.cpp @@ -0,0 +1,317 @@ +#include "catch/catch.hpp" + +#include "coordinates.h" +#include "coordinate_conversions.h" +#include "cata_generators.h" +#include "stringmaker.h" + +constexpr int num_trials = 5; + +static_assert( point::dimension == 2, "" ); +static_assert( tripoint::dimension == 3, "" ); +static_assert( point_abs_omt::dimension == 2, "" ); +static_assert( tripoint_abs_omt::dimension == 3, "" ); + +TEST_CASE( "coordinate_strings", "[coords]" ) +{ + CHECK( point_abs_omt( point( 3, 4 ) ).to_string() == "(3,4)" ); + + SECTION( "coord_point_matches_point" ) { + point p = GENERATE( take( num_trials, random_points() ) ); + point_abs_ms cp( p ); + CHECK( p.to_string() == cp.to_string() ); + } +} + +TEST_CASE( "coordinate_operations", "[coords]" ) +{ + SECTION( "construct_from_raw_point" ) { + point p = GENERATE( take( num_trials, random_points() ) ); + point_abs_ms cp( p ); + CHECK( cp.x() == p.x ); + CHECK( cp.y() == p.y ); + } + + SECTION( "construct_from_raw_tripoint" ) { + tripoint p = GENERATE( take( num_trials, random_tripoints() ) ); + tripoint_abs_ms cp( p ); + CHECK( cp.x() == p.x ); + CHECK( cp.y() == p.y ); + CHECK( cp.z() == p.z ); + } + + SECTION( "construct_from_values" ) { + tripoint p = GENERATE( take( num_trials, random_tripoints() ) ); + { + //NOLINTNEXTLINE(cata-use-point-apis) + point_abs_ms cp( p.x, p.y ); + CHECK( cp.x() == p.x ); + CHECK( cp.y() == p.y ); + } + { + //NOLINTNEXTLINE(cata-use-point-apis) + tripoint_abs_ms cp( p.x, p.y, p.z ); + CHECK( cp.x() == p.x ); + CHECK( cp.y() == p.y ); + CHECK( cp.z() == p.z ); + } + } + + SECTION( "addition" ) { + tripoint t0 = GENERATE( take( num_trials, random_tripoints() ) ); + point p0 = t0.xy(); + point p1 = GENERATE( take( num_trials, random_points() ) ); + CAPTURE( p0, p1 ); + tripoint_abs_ms abst0( t0 ); + point_abs_ms abs0( p0 ); + point_rel_ms rel0( p0 ); + point_rel_ms rel1( p1 ); + SECTION( "rel + rel -> rel" ) { + point_rel_ms sum = rel0 + rel1; + CHECK( sum.raw() == p0 + p1 ); + } + SECTION( "abs + rel -> abs" ) { + point_abs_ms sum = abs0 + rel1; + CHECK( sum.raw() == p0 + p1 ); + tripoint_abs_ms sum_t = abst0 + rel1; + CHECK( sum_t.raw() == t0 + p1 ); + } + SECTION( "abs + raw -> abs" ) { + point_abs_ms sum = abs0 + p1; + CHECK( sum.raw() == p0 + p1 ); + } + SECTION( "rel + abs -> abs" ) { + point_abs_ms sum = rel1 + abs0; + CHECK( sum.raw() == p0 + p1 ); + } + SECTION( "rel += rel" ) { + rel0 += rel1; + CHECK( rel0.raw() == p0 + p1 ); + } + SECTION( "abs += rel" ) { + abs0 += rel1; + CHECK( abs0.raw() == p0 + p1 ); + } + SECTION( "abs += raw" ) { + abs0 += p1; + CHECK( abs0.raw() == p0 + p1 ); + } + } + + SECTION( "subtraction" ) { + tripoint t0 = GENERATE( take( num_trials, random_tripoints() ) ); + point p0 = t0.xy(); + point p1 = GENERATE( take( num_trials, random_points() ) ); + CAPTURE( p0, p1 ); + tripoint_abs_ms abst0( t0 ); + point_abs_ms abs0( p0 ); + point_abs_ms abs1( p1 ); + point_rel_ms rel0( p0 ); + point_rel_ms rel1( p1 ); + SECTION( "rel - rel -> rel" ) { + point_rel_ms diff = rel0 - rel1; + CHECK( diff.raw() == p0 - p1 ); + } + SECTION( "abs - rel -> abs" ) { + point_abs_ms diff = abs0 - rel1; + CHECK( diff.raw() == p0 - p1 ); + } + SECTION( "abs - raw -> abs" ) { + point_abs_ms diff = abs0 - p1; + CHECK( diff.raw() == p0 - p1 ); + } + SECTION( "abs - abs -> rel" ) { + point_rel_ms diff0 = abs0 - abs1; + CHECK( diff0.raw() == p0 - p1 ); + tripoint_rel_ms diff1 = abst0 - abs1; + CHECK( diff1.raw() == t0 - p1 ); + } + SECTION( "rel -= rel" ) { + rel0 -= rel1; + CHECK( rel0.raw() == p0 - p1 ); + } + SECTION( "abs -= rel" ) { + abs0 -= rel1; + CHECK( abs0.raw() == p0 - p1 ); + } + SECTION( "abs -= raw" ) { + abs0 -= p1; + CHECK( abs0.raw() == p0 - p1 ); + } + } +} + +TEST_CASE( "coordinate_comparison", "[coords]" ) +{ + SECTION( "compare_points" ) { + point p0 = GENERATE( take( num_trials, random_points() ) ); + point p1 = GENERATE( take( num_trials, random_points() ) ); + CAPTURE( p0, p1 ); + point_rel_ms cp0( p0 ); + point_rel_ms cp1( p1 ); + CAPTURE( cp0, cp1 ); + + CHECK( ( p0 < p1 ) == ( cp0 < cp1 ) ); + CHECK( ( p0 == p1 ) == ( cp0 == cp1 ) ); + CHECK( cp0 == cp0 ); + CHECK( !( cp0 != cp0 ) ); + } + + SECTION( "compare_tripoints" ) { + tripoint p0 = GENERATE( take( num_trials, random_tripoints() ) ); + tripoint p1 = GENERATE( take( num_trials, random_tripoints() ) ); + CAPTURE( p0, p1 ); + tripoint_rel_ms cp0( p0 ); + tripoint_rel_ms cp1( p1 ); + CAPTURE( cp0, cp1 ); + + CHECK( ( p0 < p1 ) == ( cp0 < cp1 ) ); + CHECK( ( p0 == p1 ) == ( cp0 == cp1 ) ); + CHECK( cp0 == cp0 ); + CHECK( !( cp0 != cp0 ) ); + } +} + +TEST_CASE( "coordinate_hash", "[coords]" ) +{ + SECTION( "point_hash" ) { + point p = GENERATE( take( num_trials, random_points() ) ); + point_abs_ms cp( p ); + CHECK( std::hash()( cp ) == std::hash()( p ) ); + } + + SECTION( "tripoint_hash" ) { + tripoint p = GENERATE( take( num_trials, random_tripoints() ) ); + tripoint_abs_ms cp( p ); + CHECK( std::hash()( cp ) == std::hash()( p ) ); + } +} + +TEST_CASE( "coordinate_conversion_consistency", "[coords]" ) +{ + // Verifies that the new coord_point-based conversions yield the same + // results as the legacy conversion functions. + SECTION( "omt_to_om_point" ) { + point p = GENERATE( take( num_trials, random_points() ) ); + CAPTURE( p ); + point_abs_om new_conversion = project_to( point_abs_omt( p ) ); + point old_conversion = omt_to_om_copy( p ); + CHECK( old_conversion == new_conversion.raw() ); + } + + SECTION( "omt_to_om_tripoint" ) { + tripoint p = GENERATE( take( num_trials, random_tripoints() ) ); + CAPTURE( p ); + tripoint_abs_om new_conversion = project_to( tripoint_abs_omt( p ) ); + tripoint old_conversion = omt_to_om_copy( p ); + CHECK( old_conversion == new_conversion.raw() ); + } + + SECTION( "omt_to_om_remain_point" ) { + point p = GENERATE( take( num_trials, random_points() ) ); + CAPTURE( p ); + point_abs_om new_conversion; + point_om_omt remainder; + std::tie( new_conversion, remainder ) = project_remain( point_abs_omt( p ) ); + point old_conversion = omt_to_om_remain( p ); + CHECK( old_conversion == new_conversion.raw() ); + CHECK( p == remainder.raw() ); + } + + SECTION( "sm_to_omt_point" ) { + point p = GENERATE( take( num_trials, random_points() ) ); + CAPTURE( p ); + point_abs_omt new_conversion = project_to( point_abs_sm( p ) ); + point old_conversion = sm_to_omt_copy( p ); + CHECK( old_conversion == new_conversion.raw() ); + } + + SECTION( "sm_to_omt_remain_point" ) { + point p = GENERATE( take( num_trials, random_points() ) ); + CAPTURE( p ); + point_abs_omt new_conversion; + point_omt_sm remainder; + std::tie( new_conversion, remainder ) = project_remain( point_abs_sm( p ) ); + point old_conversion = sm_to_omt_remain( p ); + CHECK( old_conversion == new_conversion.raw() ); + CHECK( p == remainder.raw() ); + } + + SECTION( "sm_to_om_point" ) { + point p = GENERATE( take( num_trials, random_points() ) ); + CAPTURE( p ); + point_abs_om new_conversion = project_to( point_abs_sm( p ) ); + point old_conversion = sm_to_om_copy( p ); + CHECK( old_conversion == new_conversion.raw() ); + } + + SECTION( "sm_to_om_remain_point" ) { + point p = GENERATE( take( num_trials, random_points() ) ); + CAPTURE( p ); + point_abs_om new_conversion; + point_om_sm remainder; + std::tie( new_conversion, remainder ) = project_remain( point_abs_sm( p ) ); + point old_conversion = sm_to_om_remain( p ); + CHECK( old_conversion == new_conversion.raw() ); + CHECK( p == remainder.raw() ); + } + + SECTION( "omt_to_sm_point" ) { + point p = GENERATE( take( num_trials, random_points() ) ); + CAPTURE( p ); + point_abs_sm new_conversion = project_to( point_abs_omt( p ) ); + point old_conversion = omt_to_sm_copy( p ); + CHECK( old_conversion == new_conversion.raw() ); + } + + SECTION( "om_to_sm_point" ) { + point p = GENERATE( take( num_trials, random_points() ) ); + CAPTURE( p ); + point_abs_sm new_conversion = project_to( point_abs_om( p ) ); + point old_conversion = om_to_sm_copy( p ); + CHECK( old_conversion == new_conversion.raw() ); + } + + SECTION( "ms_to_sm_point" ) { + point p = GENERATE( take( num_trials, random_points() ) ); + CAPTURE( p ); + point_abs_sm new_conversion = project_to( point_abs_ms( p ) ); + point old_conversion = ms_to_sm_copy( p ); + CHECK( old_conversion == new_conversion.raw() ); + } + + SECTION( "sm_to_ms_point" ) { + point p = GENERATE( take( num_trials, random_points() ) ); + CAPTURE( p ); + point_abs_ms new_conversion = project_to( point_abs_sm( p ) ); + point old_conversion = sm_to_ms_copy( p ); + CHECK( old_conversion == new_conversion.raw() ); + } + + SECTION( "ms_to_omt_point" ) { + point p = GENERATE( take( num_trials, random_points() ) ); + CAPTURE( p ); + point_abs_omt new_conversion = project_to( point_abs_ms( p ) ); + point old_conversion = ms_to_omt_copy( p ); + CHECK( old_conversion == new_conversion.raw() ); + } + + SECTION( "ms_to_omt_remain_point" ) { + point p = GENERATE( take( num_trials, random_points() ) ); + CAPTURE( p ); + point_abs_omt new_conversion; + point_omt_ms remainder; + std::tie( new_conversion, remainder ) = project_remain( point_abs_ms( p ) ); + point old_conversion = ms_to_omt_remain( p ); + CHECK( old_conversion == new_conversion.raw() ); + CHECK( p == remainder.raw() ); + } + + SECTION( "omt_to_seg_tripoint" ) { + tripoint p = GENERATE( take( num_trials, random_tripoints() ) ); + CAPTURE( p ); + tripoint_abs_seg new_conversion = project_to( tripoint_abs_omt( p ) ); + tripoint old_conversion = omt_to_seg_copy( p ); + CHECK( old_conversion == new_conversion.raw() ); + } +} diff --git a/tests/creature_effect_test.cpp b/tests/creature_effect_test.cpp index 714f405f7efb4..ad82e8e5e0d0f 100644 --- a/tests/creature_effect_test.cpp +++ b/tests/creature_effect_test.cpp @@ -417,6 +417,7 @@ TEST_CASE( "monster is_immune_effect", "[creature][monster][effect][immune]" ) TEST_CASE( "character is_immune_effect", "[creature][character][effect][immune]" ) { avatar dummy; + dummy.set_body(); dummy.clear_mutations(); // TODO: Character may be immune to: diff --git a/tests/effective_dps_test.cpp b/tests/effective_dps_test.cpp index 00761c3e18c1f..daca6debc51fc 100644 --- a/tests/effective_dps_test.cpp +++ b/tests/effective_dps_test.cpp @@ -4,7 +4,6 @@ #include "avatar.h" #include "calendar.h" #include "creature.h" -#include "game.h" #include "item.h" #include "melee.h" #include "player.h" @@ -96,7 +95,7 @@ static void check_accuracy_dps( avatar &attacker, monster &defender, item &wpn1, } TEST_CASE( "effective damage per second", "[effective][dps]" ) { - avatar &dummy = g->u; + avatar &dummy = get_avatar(); clear_character( dummy ); item clumsy_sword( "test_clumsy_sword" ); @@ -159,7 +158,7 @@ TEST_CASE( "effective damage per second", "[effective][dps]" ) TEST_CASE( "effective vs actual damage per second", "[actual][dps][!mayfail]" ) { - avatar &dummy = g->u; + avatar &dummy = get_avatar(); clear_character( dummy ); monster soldier( mtype_id( "mon_zombie_soldier" ) ); @@ -191,7 +190,7 @@ TEST_CASE( "effective vs actual damage per second", "[actual][dps][!mayfail]" ) TEST_CASE( "accuracy increases success", "[accuracy][dps]" ) { - avatar &dummy = g->u; + avatar &dummy = get_avatar(); clear_character( dummy ); monster soldier( mtype_id( "mon_zombie_soldier" ) ); @@ -250,8 +249,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; } @@ -263,21 +262,21 @@ static void calc_expected_dps( avatar &test_guy, const std::string &weapon_id, d */ TEST_CASE( "expected weapon dps", "[expected][dps]" ) { - avatar &test_guy = g->u; + avatar &test_guy = get_avatar(); make_experienced_tester( test_guy ); 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 +296,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 +308,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 +324,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 +338,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 +382,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 +400,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 +427,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 +451,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 +476,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 ); diff --git a/tests/encumbrance_test.cpp b/tests/encumbrance_test.cpp index d548f51e987c1..4d84777ad1664 100644 --- a/tests/encumbrance_test.cpp +++ b/tests/encumbrance_test.cpp @@ -32,8 +32,8 @@ static void test_encumbrance_on( for( const item &i : clothing ) { p.worn.push_back( i ); } - p.reset_encumbrance(); - encumbrance_data enc = p.get_encumbrance()[ get_body_part_token( body_part ) ]; + p.calc_encumbrance(); + encumbrance_data enc = p.get_part_encumbrance_data( bodypart_id( body_part ) ); CHECK( enc.encumbrance == expected_encumbrance ); } @@ -87,47 +87,47 @@ static constexpr int jacket_jean_e = 9; TEST_CASE( "regular_clothing_encumbrance", "[encumbrance]" ) { - test_encumbrance( { "postman_shirt" }, "TORSO", postman_shirt_e ); - test_encumbrance( { "longshirt" }, "TORSO", longshirt_e ); - test_encumbrance( { "jacket_jean" }, "TORSO", jacket_jean_e ); + test_encumbrance( { "postman_shirt" }, "torso", postman_shirt_e ); + test_encumbrance( { "longshirt" }, "torso", longshirt_e ); + test_encumbrance( { "jacket_jean" }, "torso", jacket_jean_e ); } TEST_CASE( "separate_layer_encumbrance", "[encumbrance]" ) { - test_encumbrance( { "longshirt", "jacket_jean" }, "TORSO", longshirt_e + jacket_jean_e ); + test_encumbrance( { "longshirt", "jacket_jean" }, "torso", longshirt_e + jacket_jean_e ); } TEST_CASE( "out_of_order_encumbrance", "[encumbrance]" ) { - test_encumbrance( { "jacket_jean", "longshirt" }, "TORSO", longshirt_e * 2 + jacket_jean_e ); + test_encumbrance( { "jacket_jean", "longshirt" }, "torso", longshirt_e * 2 + jacket_jean_e ); } TEST_CASE( "same_layer_encumbrance", "[encumbrance]" ) { // When stacking within a layer, encumbrance for additional items is // counted twice - test_encumbrance( { "longshirt", "longshirt" }, "TORSO", longshirt_e * 2 + longshirt_e ); + test_encumbrance( { "longshirt", "longshirt" }, "torso", longshirt_e * 2 + longshirt_e ); // ... with a minimum of 2 - test_encumbrance( { "postman_shirt", "postman_shirt" }, "TORSO", postman_shirt_e * 2 + 2 ); + test_encumbrance( { "postman_shirt", "postman_shirt" }, "torso", postman_shirt_e * 2 + 2 ); // ... and a maximum of 10 - test_encumbrance( { "jacket_jean", "jacket_jean" }, "TORSO", jacket_jean_e * 2 + 10 ); + test_encumbrance( { "jacket_jean", "jacket_jean" }, "torso", jacket_jean_e * 2 + 10 ); } TEST_CASE( "tiny_clothing", "[encumbrance]" ) { item i( "longshirt" ); i.set_flag( "UNDERSIZE" ); - test_encumbrance_items( { i }, "TORSO", longshirt_e * 3 ); + test_encumbrance_items( { i }, "torso", longshirt_e * 3 ); } TEST_CASE( "tiny_character", "[encumbrance]" ) { item i( "longshirt" ); SECTION( "regular shirt" ) { - test_encumbrance_items( { i }, "TORSO", longshirt_e * 2, add_trait( "SMALL2" ) ); + test_encumbrance_items( { i }, "torso", longshirt_e * 2, add_trait( "SMALL2" ) ); } SECTION( "undersize shrt" ) { i.set_flag( "UNDERSIZE" ); - test_encumbrance_items( { i }, "TORSO", longshirt_e, add_trait( "SMALL2" ) ); + test_encumbrance_items( { i }, "torso", longshirt_e, add_trait( "SMALL2" ) ); } } diff --git a/tests/explosion_balance_test.cpp b/tests/explosion_balance_test.cpp index 12aade2b89328..c1869945fdccc 100644 --- a/tests/explosion_balance_test.cpp +++ b/tests/explosion_balance_test.cpp @@ -48,7 +48,7 @@ static void check_lethality( const std::string &explosive_id, const int range, f // Spawn some monsters in a circle. tripoint origin( 30, 30, 0 ); int num_subjects_this_time = 0; - for( const tripoint &monster_position : closest_tripoints_first( origin, range ) ) { + for( const tripoint &monster_position : closest_points_first( origin, range ) ) { if( rl_dist( monster_position, origin ) != range ) { continue; } 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/food_fun_for_test.cpp b/tests/food_fun_for_test.cpp index 6bcd7ac95679a..e862d037325b1 100644 --- a/tests/food_fun_for_test.cpp +++ b/tests/food_fun_for_test.cpp @@ -74,6 +74,7 @@ TEST_CASE( "fun for food eaten while sick", "[fun_for][food][sick]" ) TEST_CASE( "fun for rotten food", "[fun_for][food][rotten]" ) { avatar dummy; + dummy.set_body(); std::pair actual_fun; GIVEN( "some rotten food" ) { @@ -240,6 +241,7 @@ TEST_CASE( "fun for melted food", "[fun_for][food][melted]" ) TEST_CASE( "fun for cat food", "[fun_for][food][cat][feline]" ) { avatar dummy; + dummy.set_body(); std::pair actual_fun; GIVEN( "cat food" ) { @@ -270,6 +272,7 @@ TEST_CASE( "fun for cat food", "[fun_for][food][cat][feline]" ) TEST_CASE( "fun for dog food", "[fun_for][food][dog][lupine]" ) { avatar dummy; + dummy.set_body(); std::pair actual_fun; GIVEN( "dog food" ) { @@ -301,6 +304,7 @@ TEST_CASE( "fun for dog food", "[fun_for][food][dog][lupine]" ) TEST_CASE( "fun for gourmand", "[fun_for][food][gourmand]" ) { avatar dummy; + dummy.set_body(); std::pair actual_fun; GIVEN( "food that tastes good" ) { @@ -404,6 +408,7 @@ TEST_CASE( "fun for food eaten too often", "[fun_for][food][monotony]" ) TEST_CASE( "fun for bionic bio taste blocker", "[fun_for][food][bionic]" ) { avatar dummy; + dummy.set_body(); std::pair actual_fun; GIVEN( "food that tastes bad" ) { diff --git a/tests/ground_destroy_test.cpp b/tests/ground_destroy_test.cpp index 2a76bd17fb684..48ecba6ca4af9 100644 --- a/tests/ground_destroy_test.cpp +++ b/tests/ground_destroy_test.cpp @@ -4,7 +4,6 @@ #include "avatar.h" #include "catch/catch.hpp" -#include "game.h" #include "int_id.h" #include "item.h" #include "itype.h" @@ -25,12 +24,13 @@ TEST_CASE( "pavement_destroy", "[.]" ) REQUIRE( flat_roof_id != t_null ); clear_map_and_put_player_underground(); + map &here = get_map(); // Populate the map with pavement. - g->m.ter_set( tripoint_zero, ter_id( "t_pavement" ) ); + here.ter_set( tripoint_zero, ter_id( "t_pavement" ) ); // Destroy it - g->m.destroy( tripoint_zero, true ); - ter_id after_destroy = g->m.ter( tripoint_zero ); + here.destroy( tripoint_zero, true ); + ter_id after_destroy = here.ter( tripoint_zero ); if( after_destroy == flat_roof_id ) { FAIL( flat_roof_id.obj().name() << " found after destroying pavement" ); } else { @@ -50,12 +50,13 @@ TEST_CASE( "explosion_on_ground", "[.]" ) std::vector test_terrain_id = { ter_id( "t_dirt" ), ter_id( "t_grass" ) }; + map &here = get_map(); int idx = 0; const int area_dim = 16; // Populate map with various test terrain. for( int x = 0; x < area_dim; x++ ) { for( int y = 0; y < area_dim; y++ ) { - g->m.ter_set( tripoint( x, y, 0 ), test_terrain_id[idx] ); + here.ter_set( tripoint( x, y, 0 ), test_terrain_id[idx] ); idx = ( idx + 1 ) % test_terrain_id.size(); } } @@ -66,13 +67,13 @@ TEST_CASE( "explosion_on_ground", "[.]" ) const tripoint area_center( area_dim / 2, area_dim / 2, 0 ); item rdx_keg( rdx_keg_typeid ); rdx_keg.charges = 0; - rdx_keg.type->invoke( g->u, rdx_keg, area_center ); + rdx_keg.type->invoke( get_avatar(), rdx_keg, area_center ); // Check area to see if any t_flat_roof is present. for( int x = 0; x < area_dim; x++ ) { for( int y = 0; y < area_dim; y++ ) { tripoint pt( x, y, 0 ); - ter_id t_id = g->m.ter( pt ); + ter_id t_id = here.ter( pt ); if( t_id == flat_roof_id ) { FAIL( "After explosion, " << t_id.obj().name() << " found at " << x << "," << y ); } @@ -97,11 +98,12 @@ TEST_CASE( "explosion_on_floor_with_rock_floor_basement", "[.]" ) clear_map_and_put_player_underground(); + map &here = get_map(); const int area_dim = 24; for( int x = 0; x < area_dim; x++ ) { for( int y = 0; y < area_dim; y++ ) { - g->m.ter_set( tripoint( x, y, 0 ), floor_id ); - g->m.ter_set( tripoint( x, y, -1 ), rock_floor_id ); + here.ter_set( tripoint( x, y, 0 ), floor_id ); + here.ter_set( tripoint( x, y, -1 ), rock_floor_id ); } } // Detonate an RDX keg item in the middle of the populated map space @@ -111,14 +113,14 @@ TEST_CASE( "explosion_on_floor_with_rock_floor_basement", "[.]" ) const tripoint area_center( area_dim / 2, area_dim / 2, 0 ); item rdx_keg( rdx_keg_typeid ); rdx_keg.charges = 0; - rdx_keg.type->invoke( g->u, rdx_keg, area_center ); + rdx_keg.type->invoke( get_avatar(), rdx_keg, area_center ); // Check z0 for open air bool found_open_air = false; for( int x = 0; x < area_dim; x++ ) { for( int y = 0; y < area_dim; y++ ) { tripoint pt( x, y, 0 ); - ter_id t_id = g->m.ter( pt ); + ter_id t_id = here.ter( pt ); INFO( "t " << t_id.obj().name() << " at " << x << "," << y ); if( t_id == open_air_id ) { found_open_air = true; @@ -153,10 +155,11 @@ TEST_CASE( "collapse_checks", "[.]" ) clear_map_and_put_player_underground(); + map &here = get_map(); // build a structure const tripoint &midair = tripoint( tripoint_zero.xy(), tripoint_zero.z + 1 ); - for( const tripoint &pt : g->m.points_in_radius( midair, wall_size, 1 ) ) { - g->m.ter_set( pt, floor_id ); + for( const tripoint &pt : here.points_in_radius( midair, wall_size, 1 ) ) { + here.ter_set( pt, floor_id ); } std::set corners; for( int delta_z = 0; delta_z < 3; delta_z++ ) { @@ -164,31 +167,31 @@ TEST_CASE( "collapse_checks", "[.]" ) for( int delta_y = 0; delta_y <= 1; delta_y++ ) { const tripoint pt( delta_x * wall_size, delta_y * wall_size, delta_z ); corners.insert( pt ); - g->m.ter_set( pt, wall_id ); + here.ter_set( pt, wall_id ); } } } // make sure it's a valid structure - for( const tripoint &pt : g->m.points_in_radius( midair, wall_size, 1 ) ) { + for( const tripoint &pt : here.points_in_radius( midair, wall_size, 1 ) ) { if( corners.find( pt ) != corners.end() ) { - REQUIRE( g->m.ter( pt ) == wall_id ); + REQUIRE( here.ter( pt ) == wall_id ); } else { - REQUIRE( g->m.ter( pt ) == floor_id ); + REQUIRE( here.ter( pt ) == floor_id ); } } // destroy the floor on the first floor; floor above should not collapse - for( const tripoint &pt : g->m.points_in_radius( tripoint_zero, wall_size ) ) { + for( const tripoint &pt : here.points_in_radius( tripoint_zero, wall_size ) ) { if( corners.find( pt ) == corners.end() ) { - g->m.destroy( pt, true ); + here.destroy( pt, true ); } } - for( const tripoint &pt : g->m.points_in_radius( midair, wall_size ) ) { + for( const tripoint &pt : here.points_in_radius( midair, wall_size ) ) { if( corners.find( pt ) != corners.end() ) { - CHECK( g->m.ter( pt ) == wall_id ); + CHECK( here.ter( pt ) == wall_id ); } else { - CHECK( g->m.ter( pt ) == floor_id ); + CHECK( here.ter( pt ) == floor_id ); } } @@ -196,17 +199,17 @@ TEST_CASE( "collapse_checks", "[.]" ) for( int delta_x = 0; delta_x <= 1; delta_x++ ) { for( int delta_y = 0; delta_y <= 1; delta_y++ ) { const tripoint pt( delta_x * wall_size, delta_y * wall_size, 0 ); - g->m.destroy( pt, true ); + here.destroy( pt, true ); } } int open_air_count = 0; int tile_count = 0; int no_wall_count = 0; - for( const tripoint &pt : g->m.points_in_radius( midair, wall_size, 1 ) ) { + for( const tripoint &pt : here.points_in_radius( midair, wall_size, 1 ) ) { if( pt.z == 0 ) { continue; } - const ter_id t_id = g->m.ter( pt ); + const ter_id t_id = here.ter( pt ); tile_count += 1; if( t_id == t_open_air ) { open_air_count += 1; 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 ); 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" ); } diff --git a/tests/iuse_actor_test.cpp b/tests/iuse_actor_test.cpp index 91cadb681680c..ca9c7e54ee01f 100644 --- a/tests/iuse_actor_test.cpp +++ b/tests/iuse_actor_test.cpp @@ -16,15 +16,14 @@ static player &get_sanitized_player( ) { - player &dummy = g->u; - + avatar &player_character = get_avatar(); // Remove first worn item until there are none left. std::list temp; - while( dummy.takeoff( dummy.i_at( -2 ), &temp ) ) {} - dummy.inv.clear(); - dummy.remove_weapon(); + while( player_character.takeoff( player_character.i_at( -2 ), &temp ) ) {} + player_character.inv.clear(); + player_character.remove_weapon(); - return dummy; + return player_character; } static monster *find_adjacent_monster( const tripoint &pos ) @@ -45,23 +44,23 @@ static monster *find_adjacent_monster( const tripoint &pos ) TEST_CASE( "manhack", "[iuse_actor][manhack]" ) { - player &dummy = get_sanitized_player(); + player &player_character = get_sanitized_player(); g->clear_zombies(); - item &test_item = dummy.i_add( item( "bot_manhack", 0, item::default_charges_tag{} ) ); + item &test_item = player_character.i_add( item( "bot_manhack", 0, item::default_charges_tag{} ) ); - REQUIRE( dummy.has_item( test_item ) ); + REQUIRE( player_character.has_item( test_item ) ); - monster *new_manhack = find_adjacent_monster( dummy.pos() ); + monster *new_manhack = find_adjacent_monster( player_character.pos() ); REQUIRE( new_manhack == nullptr ); - dummy.invoke_item( &test_item ); + player_character.invoke_item( &test_item ); - REQUIRE( !dummy.has_item_with( []( const item & it ) { + REQUIRE( !player_character.has_item_with( []( const item & it ) { return it.typeId() == itype_id( "bot_manhack" ); } ) ); - new_manhack = find_adjacent_monster( dummy.pos() ); + new_manhack = find_adjacent_monster( player_character.pos() ); REQUIRE( new_manhack != nullptr ); REQUIRE( new_manhack->type->id == mtype_id( "mon_manhack" ) ); g->clear_zombies(); 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; diff --git a/tests/magic_spell_test.cpp b/tests/magic_spell_test.cpp index b7af678a8a473..a00c622408c52 100644 --- a/tests/magic_spell_test.cpp +++ b/tests/magic_spell_test.cpp @@ -465,7 +465,7 @@ TEST_CASE( "spell effect - target_attack", "[magic][spell][effect][target_attack int after_hp = 0; // Avatar/spellcaster - avatar &dummy = g->u; + avatar &dummy = get_avatar(); clear_character( dummy ); dummy.setpos( dummy_loc ); REQUIRE( dummy.pos() == dummy_loc ); @@ -514,7 +514,7 @@ TEST_CASE( "spell effect - summon", "[magic][spell][effect][summon]" ) const tripoint dummy_loc = { 60, 60, 0 }; const tripoint mummy_loc = { 61, 60, 0 }; - avatar &dummy = g->u; + avatar &dummy = get_avatar(); clear_character( dummy ); dummy.setpos( dummy_loc ); REQUIRE( dummy.pos() == dummy_loc ); @@ -553,7 +553,7 @@ TEST_CASE( "spell effect - recover_energy", "[magic][spell][effect][recover_ener // For that, "target_attack" with a negative damage is used. // Yer a wizard, ya dummy - player &dummy = g->u; + avatar &dummy = get_avatar(); clear_character( dummy ); clear_map(); 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 ); 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 ); } diff --git a/tests/map_test.cpp b/tests/map_test.cpp index cb3a919d8975e..6bf8ca312b7e9 100644 --- a/tests/map_test.cpp +++ b/tests/map_test.cpp @@ -14,18 +14,19 @@ TEST_CASE( "destroy_grabbed_furniture" ) { clear_map(); + avatar &player_character = get_avatar(); GIVEN( "Furniture grabbed by the player" ) { const tripoint test_origin( 60, 60, 0 ); map &here = get_map(); - g->u.setpos( test_origin ); + player_character.setpos( test_origin ); const tripoint grab_point = test_origin + tripoint_east; here.furn_set( grab_point, furn_id( "f_chair" ) ); - g->u.grab( object_type::FURNITURE, grab_point ); + player_character.grab( object_type::FURNITURE, grab_point ); WHEN( "The furniture grabbed by the player is destroyed" ) { here.destroy( grab_point ); THEN( "The player's grab is released" ) { - CHECK( g->u.get_grab_type() == object_type::NONE ); - CHECK( g->u.grab_point == tripoint_zero ); + CHECK( player_character.get_grab_type() == object_type::NONE ); + CHECK( player_character.grab_point == tripoint_zero ); } } } diff --git a/tests/melee_dodge_hit_test.cpp b/tests/melee_dodge_hit_test.cpp index 0146ca8a643aa..4f16a8ae3ad60 100644 --- a/tests/melee_dodge_hit_test.cpp +++ b/tests/melee_dodge_hit_test.cpp @@ -68,7 +68,7 @@ TEST_CASE( "Character::get_hit_base", "[character][melee][hit][dex]" ) { clear_map(); - avatar &dummy = g->u; + avatar &dummy = get_avatar(); clear_character( dummy ); SECTION( "character get_hit_base increases by 1/4 for each point of DEX" ) { @@ -96,7 +96,7 @@ TEST_CASE( "Character::get_dodge_base", "[character][melee][dodge][dex][skill]" { clear_map(); - avatar &dummy = g->u; + avatar &dummy = get_avatar(); clear_character( dummy ); // Character::get_dodge_base is simply DEXTERITY / 2 + DODGE_SKILL @@ -188,7 +188,7 @@ TEST_CASE( "player::get_dodge", "[player][melee][dodge]" ) { clear_map(); - avatar &dummy = g->u; + avatar &dummy = get_avatar(); clear_character( dummy ); const float base_dodge = dummy.get_dodge_base(); @@ -222,7 +222,7 @@ TEST_CASE( "player::get_dodge with effects", "[player][melee][dodge][effect]" ) { clear_map(); - avatar &dummy = g->u; + avatar &dummy = get_avatar(); clear_character( dummy ); // Compare all effects against base dodge ability @@ -281,7 +281,7 @@ TEST_CASE( "player::get_dodge while grabbed", "[player][melee][dodge][grab]" ) { clear_map(); - avatar &dummy = g->u; + avatar &dummy = get_avatar(); clear_character( dummy ); // Base dodge rate when not grabbed @@ -354,7 +354,7 @@ TEST_CASE( "player::get_dodge while grabbed", "[player][melee][dodge][grab]" ) TEST_CASE( "player::get_dodge stamina effects", "[player][melee][dodge][stamina]" ) { - avatar &dummy = g->u; + avatar &dummy = get_avatar(); clear_character( dummy ); SECTION( "8/8/8/8, no skills, unencumbered" ) { diff --git a/tests/memorial_test.cpp b/tests/memorial_test.cpp index 105451d3eb82d..0d5bd60ac3c3d 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" ); @@ -88,6 +89,10 @@ TEST_CASE( "memorials" ) check_memorial( m, b, "Became wanted by the police!", ch ); + // To insure we don't trigger losing the Structural Integrity conduct during the test, + // Break the subject's leg first. + b.send( ch, bp_leg_l ); + check_memorial( m, b, "Broke her right arm.", ch, bp_arm_r ); @@ -193,8 +198,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/modify_morale_test.cpp b/tests/modify_morale_test.cpp index b05bf6b887fdc..ccf8c29a35b2f 100644 --- a/tests/modify_morale_test.cpp +++ b/tests/modify_morale_test.cpp @@ -80,6 +80,7 @@ TEST_CASE( "dining with table and chair", "[food][modify_morale][table][chair]" clear_map(); map &here = get_map(); avatar dummy; + dummy.set_body(); const tripoint avatar_pos( 60, 60, 0 ); dummy.setpos( avatar_pos ); dummy.worn.push_back( item( "backpack" ) ); @@ -269,6 +270,7 @@ TEST_CASE( "drugs", "[food][modify_morale][drug]" ) TEST_CASE( "cannibalism", "[food][modify_morale][cannibal]" ) { avatar dummy; + dummy.set_body(); dummy.worn.push_back( item( "backpack" ) ); item &human = dummy.i_add( item( "bone_human" ) ); @@ -344,6 +346,7 @@ TEST_CASE( "cannibalism", "[food][modify_morale][cannibal]" ) TEST_CASE( "sweet junk food", "[food][modify_morale][junk][sweet]" ) { avatar dummy; + dummy.set_body(); dummy.worn.push_back( item( "backpack" ) ); GIVEN( "some sweet junk food" ) { @@ -397,6 +400,7 @@ TEST_CASE( "sweet junk food", "[food][modify_morale][junk][sweet]" ) TEST_CASE( "junk food that is not ingested", "[modify_morale][junk][no_ingest]" ) { avatar dummy; + dummy.set_body(); dummy.worn.push_back( item( "backpack" ) ); item &caff_gum = dummy.i_add( item( "caff_gum" ) ); @@ -462,6 +466,7 @@ TEST_CASE( "junk food that is not ingested", "[modify_morale][junk][no_ingest]" TEST_CASE( "food allergies and intolerances", "[food][modify_morale][allergy]" ) { avatar dummy; + dummy.set_body(); dummy.worn.push_back( item( "backpack" ) ); int penalty = -75; @@ -548,6 +553,7 @@ TEST_CASE( "food allergies and intolerances", "[food][modify_morale][allergy]" ) TEST_CASE( "saprophage character", "[food][modify_morale][saprophage]" ) { avatar dummy; + dummy.set_body(); dummy.worn.push_back( item( "backpack" ) ); GIVEN( "character is a saprophage, preferring rotted food" ) { @@ -584,6 +590,7 @@ TEST_CASE( "saprophage character", "[food][modify_morale][saprophage]" ) TEST_CASE( "ursine honey", "[food][modify_morale][ursine][honey]" ) { avatar dummy; + dummy.set_body(); dummy.worn.push_back( item( "backpack" ) ); item &honeycomb = dummy.i_add( item( "honeycomb" ) ); diff --git a/tests/mutation_test.cpp b/tests/mutation_test.cpp index 654dae132bfd1..5cd4cec7efebe 100644 --- a/tests/mutation_test.cpp +++ b/tests/mutation_test.cpp @@ -18,6 +18,7 @@ std::string get_mutations_as_string( const player &p ); static void give_all_mutations( player &p, const mutation_category_trait &category, const bool include_postthresh ) { + p.set_body(); const std::vector category_mutations = mutations_category[category.id]; // Add the threshold mutation first diff --git a/tests/new_character_test.cpp b/tests/new_character_test.cpp index 9184eb3a9afe3..9223612a2d63a 100644 --- a/tests/new_character_test.cpp +++ b/tests/new_character_test.cpp @@ -147,7 +147,7 @@ TEST_CASE( "starting_items", "[slow]" ) g->u.worn.clear(); g->u.remove_weapon(); g->u.inv.clear(); - g->u.reset_encumbrance(); + g->u.calc_encumbrance(); g->u.male = i == 0; g->u.add_profession_items(); diff --git a/tests/npc_talk_test.cpp b/tests/npc_talk_test.cpp index e1b5a1b5c9ddf..2d519730557b2 100644 --- a/tests/npc_talk_test.cpp +++ b/tests/npc_talk_test.cpp @@ -42,7 +42,7 @@ static const trait_id trait_PROF_SWAT( "PROF_SWAT" ); static npc &create_test_talker() { const string_id test_talker( "test_talker" ); - const character_id model_id = get_map().place_npc( point( 25, 25 ), test_talker, true ); + const character_id model_id = get_map().place_npc( point( 25, 25 ), test_talker ); g->load_npcs(); npc *model_npc = g->find_npc( model_id ); @@ -856,6 +856,31 @@ TEST_CASE( "npc_talk_adjust_vars", "[npc_talk]" ) CHECK( d.responses[10].text == "This is a npc_compare_var test response for < 0." ); } +TEST_CASE( "npc_talk_vars_time", "[npc_talk]" ) +{ + dialogue d; + prep_test( d ); + + time_point start_turn = calendar::turn; + calendar::turn = calendar::turn + time_duration( 1_hours ); + d.add_topic( "TALK_TEST_VARS_TIME" ); + gen_response_lines( d, 3 ); + CHECK( d.responses[0].text == "This is a basic test response." ); + CHECK( d.responses[1].text == "This is a u_add_var time test response." ); + CHECK( d.responses[2].text == "This is a npc_add_var time test response." ); + talk_effect_t &effects = d.responses[1].success; + effects.apply( d ); + gen_response_lines( d, 1 ); + CHECK( d.responses[0].text == "This is a basic test response." ); + time_point then = calendar::turn; + calendar::turn = calendar::turn + time_duration( 4_days ); + REQUIRE( then < calendar::turn ); + gen_response_lines( d, 2 ); + CHECK( d.responses[0].text == "This is a basic test response." ); + CHECK( d.responses[1].text == "This is a u_compare_var time test response for > 3_days." ); + calendar::turn = start_turn; +} + TEST_CASE( "npc_talk_bionics", "[npc_talk]" ) { dialogue d; diff --git a/tests/npc_test.cpp b/tests/npc_test.cpp index 8f2253af2dfb2..900b448326870 100644 --- a/tests/npc_test.cpp +++ b/tests/npc_test.cpp @@ -5,7 +5,6 @@ #include #include -#include "avatar.h" #include "calendar.h" #include "catch/catch.hpp" #include "common_types.h" @@ -313,40 +312,42 @@ TEST_CASE( "npc-movement" ) clear_map(); + Character &player_character = get_player_character(); + map &here = get_map(); for( int y = 0; y < height; ++y ) { for( int x = 0; x < width; ++x ) { const char type = setup[y][x]; - const tripoint p = g->u.pos() + point( x, y ); + const tripoint p = player_character.pos() + point( x, y ); // create walls if( type == '#' ) { - g->m.ter_set( p, t_reinforced_glass ); + here.ter_set( p, t_reinforced_glass ); } else { - g->m.ter_set( p, t_floor ); + here.ter_set( p, t_floor ); } // spawn acid // a copy is needed because we will remove elements from it - const field fs = g->m.field_at( p ); + const field fs = here.field_at( p ); for( const auto &f : fs ) { - g->m.remove_field( p, f.first ); + here.remove_field( p, f.first ); } if( type == 'A' || type == 'R' || type == 'W' || type == 'M' || type == 'B' || type == 'C' ) { - g->m.add_field( p, fd_acid, 3 ); + here.add_field( p, fd_acid, 3 ); } // spawn rubbles if( type == 'R' ) { - g->m.furn_set( p, f_rubble ); + here.furn_set( p, f_rubble ); } else { - g->m.furn_set( p, f_null ); + here.furn_set( p, f_null ); } // create vehicles if( type == 'V' || type == 'W' || type == 'M' ) { - vehicle *veh = g->m.add_vehicle( vproto_id( "none" ), p, 270, 0, 0 ); + vehicle *veh = here.add_vehicle( vproto_id( "none" ), p, 270, 0, 0 ); REQUIRE( veh != nullptr ); veh->install_part( point_zero, vpart_frame_vertical ); veh->install_part( point_zero, vpart_seat ); - g->m.add_vehicle_to_cache( veh ); + here.add_vehicle_to_cache( veh ); } // spawn npcs if( type == 'A' || type == 'R' || type == 'W' || type == 'M' @@ -374,28 +375,28 @@ TEST_CASE( "npc-movement" ) for( int y = 0; y < height; ++y ) { for( int x = 0; x < width; ++x ) { const char type = setup[y][x]; - const tripoint p = g->u.pos() + point( x, y ); + const tripoint p = player_character.pos() + point( x, y ); if( type == '#' ) { - REQUIRE( !g->m.passable( p ) ); + REQUIRE( !here.passable( p ) ); } else { - REQUIRE( g->m.passable( p ) ); + REQUIRE( here.passable( p ) ); } if( type == 'R' ) { - REQUIRE( g->m.has_flag( "UNSTABLE", p ) ); + REQUIRE( here.has_flag( "UNSTABLE", p ) ); } else { - REQUIRE( !g->m.has_flag( "UNSTABLE", p ) ); + REQUIRE( !here.has_flag( "UNSTABLE", p ) ); } if( type == 'V' || type == 'W' || type == 'M' ) { - REQUIRE( g->m.veh_at( p ).part_with_feature( VPFLAG_BOARDABLE, true ).has_value() ); + REQUIRE( here.veh_at( p ).part_with_feature( VPFLAG_BOARDABLE, true ).has_value() ); } else { - REQUIRE( !g->m.veh_at( p ).part_with_feature( VPFLAG_BOARDABLE, true ).has_value() ); + REQUIRE( !here.veh_at( p ).part_with_feature( VPFLAG_BOARDABLE, true ).has_value() ); } npc *guy = g->critter_at( p ); if( type == 'A' || type == 'R' || type == 'W' || type == 'M' || type == 'B' || type == 'C' ) { REQUIRE( guy != nullptr ); - REQUIRE( guy->is_dangerous_fields( g->m.field_at( p ) ) ); + REQUIRE( guy->is_dangerous_fields( here.field_at( p ) ) ); } else { REQUIRE( guy == nullptr ); } @@ -403,16 +404,16 @@ TEST_CASE( "npc-movement" ) } SECTION( "NPCs escape dangerous terrain by pushing other NPCs" ) { - check_npc_movement( g->u.pos() ); + check_npc_movement( player_character.pos() ); } SECTION( "Player in vehicle & NPCs escaping dangerous terrain" ) { - const tripoint origin = g->u.pos(); + const tripoint origin = player_character.pos(); for( int y = 0; y < height; ++y ) { for( int x = 0; x < width; ++x ) { if( setup[y][x] == 'V' ) { - g->place_player( g->u.pos() + point( x, y ) ); + g->place_player( player_character.pos() + point( x, y ) ); break; } } @@ -434,8 +435,9 @@ TEST_CASE( "npc_can_target_player" ) clear_npcs(); clear_creatures(); - npc &hostile = spawn_npc( g->u.pos().xy() + point_south, "thug" ); - REQUIRE( rl_dist( g->u.pos(), hostile.pos() ) <= 1 ); + Character &player_character = get_player_character(); + npc &hostile = spawn_npc( player_character.pos().xy() + point_south, "thug" ); + REQUIRE( rl_dist( player_character.pos(), hostile.pos() ) <= 1 ); hostile.set_attitude( NPCATT_KILL ); hostile.name = "Enemy NPC"; @@ -443,5 +445,5 @@ TEST_CASE( "npc_can_target_player" ) hostile.regen_ai_cache(); REQUIRE( hostile.current_target() != nullptr ); - CHECK( hostile.current_target() == static_cast( &g->u ) ); + CHECK( hostile.current_target() == static_cast( &player_character ) ); } diff --git a/tests/player_helpers.cpp b/tests/player_helpers.cpp index df42e38bedb94..7619904eb3585 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 ); } ); @@ -55,7 +55,7 @@ void clear_character( player &dummy, bool debug_storage ) // delete all worn items. dummy.worn.clear(); - dummy.reset_encumbrance(); + dummy.calc_encumbrance(); dummy.inv.clear(); dummy.remove_weapon(); dummy.clear_mutations(); @@ -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 ); g->load_npcs(); npc *guy = g->find_npc( model_id ); diff --git a/tests/point_test.cpp b/tests/point_test.cpp index 596d9c6d103bd..55fd1c820808d 100644 --- a/tests/point_test.cpp +++ b/tests/point_test.cpp @@ -2,6 +2,7 @@ #include #include "catch/catch.hpp" +#include "coordinates.h" #include "point.h" TEST_CASE( "rectangle_containment", "[point]" ) @@ -65,27 +66,27 @@ TEST_CASE( "tripoint_xy", "[point]" ) CHECK( p.xy() == point( 1, 2 ) ); } -TEST_CASE( "closest_tripoints_first", "[point]" ) +TEST_CASE( "closest_points_first_tripoint", "[point]" ) { const tripoint center = { 1, -1, 2 }; GIVEN( "min_dist > max_dist" ) { - const std::vector result = closest_tripoints_first( center, 1, 0 ); + const std::vector result = closest_points_first( center, 1, 0 ); CHECK( result.empty() ); } GIVEN( "min_dist = max_dist = 0" ) { - const std::vector result = closest_tripoints_first( center, 0, 0 ); + const std::vector result = closest_points_first( center, 0, 0 ); - CHECK( result.size() == 1 ); + REQUIRE( result.size() == 1 ); CHECK( result[0] == tripoint{ 1, -1, 2 } ); } GIVEN( "min_dist = 0, max_dist = 1" ) { - const std::vector result = closest_tripoints_first( center, 0, 1 ); + const std::vector result = closest_points_first( center, 0, 1 ); - CHECK( result.size() == 9 ); + REQUIRE( result.size() == 9 ); CHECK( result[0] == tripoint{ 1, -1, 2 } ); CHECK( result[1] == tripoint{ 2, -1, 2 } ); CHECK( result[2] == tripoint{ 2, 0, 2 } ); @@ -98,9 +99,9 @@ TEST_CASE( "closest_tripoints_first", "[point]" ) } GIVEN( "min_dist = 2, max_dist = 2" ) { - const std::vector result = closest_tripoints_first( center, 2, 2 ); + const std::vector result = closest_points_first( center, 2, 2 ); - CHECK( result.size() == 16 ); + REQUIRE( result.size() == 16 ); CHECK( result[0] == tripoint{ 3, -2, 2 } ); CHECK( result[1] == tripoint{ 3, -1, 2 } ); @@ -120,3 +121,36 @@ TEST_CASE( "closest_tripoints_first", "[point]" ) CHECK( result[15] == tripoint{ 3, -3, 2 } ); } } + +TEST_CASE( "closest_points_first_point_abs_omt", "[point]" ) +{ + const point_abs_omt center( point( 1, 3 ) ); + + GIVEN( "min_dist > max_dist" ) { + const std::vector result = closest_points_first( center, 1, 0 ); + + CHECK( result.empty() ); + } + + GIVEN( "min_dist = max_dist = 0" ) { + const std::vector result = closest_points_first( center, 0, 0 ); + + REQUIRE( result.size() == 1 ); + CHECK( result[0].raw() == point{ 1, 3 } ); + } + + GIVEN( "min_dist = 0, max_dist = 1" ) { + const std::vector result = closest_points_first( center, 0, 1 ); + + REQUIRE( result.size() == 9 ); + CHECK( result[0].raw() == point{ 1, 3 } ); + CHECK( result[1].raw() == point{ 2, 3 } ); + CHECK( result[2].raw() == point{ 2, 4 } ); + CHECK( result[3].raw() == point{ 1, 4 } ); + CHECK( result[4].raw() == point{ 0, 4 } ); + CHECK( result[5].raw() == point{ 0, 3 } ); + CHECK( result[6].raw() == point{ 0, 2 } ); + CHECK( result[7].raw() == point{ 1, 2 } ); + CHECK( result[8].raw() == point{ 2, 2 } ); + } +} diff --git a/tests/ranged_balance_test.cpp b/tests/ranged_balance_test.cpp index fae734b679735..7c3e00c9dd189 100644 --- a/tests/ranged_balance_test.cpp +++ b/tests/ranged_balance_test.cpp @@ -258,7 +258,7 @@ static void assert_encumbrance( npc &shooter, int encumbrance ) { for( const bodypart_id &bp : shooter.get_all_body_parts() ) { INFO( "Body Part: " << body_part_name( bp ) ); - REQUIRE( shooter.encumb( bp->token ) == encumbrance ); + REQUIRE( shooter.encumb( bp ) == encumbrance ); } } @@ -268,6 +268,7 @@ TEST_CASE( "unskilled_shooter_accuracy", "[ranged] [balance] [slow]" ) { clear_map(); standard_npc shooter( "Shooter", shooter_pos, {}, 0, 8, 8, 8, 7 ); + shooter.set_body(); shooter.worn.push_back( item( "backpack" ) ); equip_shooter( shooter, { "bastsandals", "armguard_chitin", "armor_chitin", "beekeeping_gloves", "fencing_mask" } ); assert_encumbrance( shooter, 10 ); @@ -308,6 +309,7 @@ TEST_CASE( "competent_shooter_accuracy", "[ranged] [balance]" ) { clear_map(); standard_npc shooter( "Shooter", shooter_pos, {}, 5, 10, 10, 10, 10 ); + shooter.set_body(); equip_shooter( shooter, { "cloak_wool", "footrags_wool", "gloves_wraps_fur", "glasses_safety", "balclava" } ); assert_encumbrance( shooter, 5 ); @@ -347,6 +349,7 @@ TEST_CASE( "expert_shooter_accuracy", "[ranged] [balance]" ) { clear_map(); standard_npc shooter( "Shooter", shooter_pos, {}, 10, 20, 20, 20, 20 ); + shooter.set_body(); equip_shooter( shooter, { } ); assert_encumbrance( shooter, 0 ); diff --git a/tests/reading_test.cpp b/tests/reading_test.cpp index e13bb96ad78f3..a1ba79073aef1 100644 --- a/tests/reading_test.cpp +++ b/tests/reading_test.cpp @@ -48,6 +48,7 @@ TEST_CASE( "identifying unread books", "[reading][book][identify]" ) TEST_CASE( "reading a book for fun", "[reading][book][fun]" ) { avatar dummy; + dummy.set_body(); dummy.worn.push_back( item( "backpack" ) ); GIVEN( "a fun book" ) { @@ -250,6 +251,7 @@ TEST_CASE( "estimated reading time for a book", "[reading][book][time]" ) TEST_CASE( "reasons for not being able to read", "[reading][reasons]" ) { avatar dummy; + dummy.set_body(); dummy.worn.push_back( item( "backpack" ) ); std::vector reasons; std::vector expect_reasons; diff --git a/tests/reloading_test.cpp b/tests/reloading_test.cpp index 2121acd958e12..deab69b37e401 100644 --- a/tests/reloading_test.cpp +++ b/tests/reloading_test.cpp @@ -20,7 +20,7 @@ TEST_CASE( "reload_gun_with_integral_magazine", "[reload],[gun]" ) { - player &dummy = g->u; + player &dummy = get_avatar(); clear_avatar(); // Make sure the player doesn't drop anything :P @@ -41,7 +41,7 @@ TEST_CASE( "reload_gun_with_integral_magazine", "[reload],[gun]" ) TEST_CASE( "reload_gun_with_integral_magazine_using_speedloader", "[reload],[gun]" ) { - player &dummy = g->u; + player &dummy = get_avatar(); clear_avatar(); // Make sure the player doesn't drop anything :P @@ -74,7 +74,7 @@ TEST_CASE( "reload_gun_with_integral_magazine_using_speedloader", "[reload],[gun TEST_CASE( "reload_gun_with_swappable_magazine", "[reload],[gun]" ) { - player &dummy = g->u; + player &dummy = get_avatar(); clear_avatar(); // Make sure the player doesn't drop anything :P @@ -150,7 +150,7 @@ static void reload_a_revolver( player &dummy, item &gun, item &ammo ) TEST_CASE( "automatic_reloading_action", "[reload],[gun]" ) { - player &dummy = g->u; + player &dummy = get_avatar(); clear_avatar(); // Make sure the player doesn't drop anything :P diff --git a/tests/stomach_contents_test.cpp b/tests/stomach_contents_test.cpp index 6f5a81094a27c..05f0da0ced676 100644 --- a/tests/stomach_contents_test.cpp +++ b/tests/stomach_contents_test.cpp @@ -4,7 +4,6 @@ #include "avatar.h" #include "catch/catch.hpp" #include "calendar.h" -#include "game.h" #include "player.h" #include "player_helpers.h" #include "item.h" @@ -15,13 +14,13 @@ static void reset_time() { calendar::turn = calendar::start_of_cataclysm; - player &p = g->u; - p.set_stored_kcal( p.get_healthy_kcal() ); - p.set_hunger( 0 ); + Character &player_character = get_player_character(); + player_character.set_stored_kcal( player_character.get_healthy_kcal() ); + player_character.set_hunger( 0 ); clear_avatar(); } -static void pass_time( player &p, time_duration amt ) +static void pass_time( Character &p, time_duration amt ) { for( auto turns = 1_turns; turns < amt; turns += 1_turns ) { calendar::turn += 1_turns; @@ -29,13 +28,13 @@ static void pass_time( player &p, time_duration amt ) } } -static void clear_stomach( player &p ) +static void clear_stomach( Character &p ) { p.stomach.empty(); p.guts.empty(); } -static void set_all_vitamins( int target, player &p ) +static void set_all_vitamins( int target, Character &p ) { p.vitamin_set( vitamin_id( "vitA" ), target ); p.vitamin_set( vitamin_id( "vitB" ), target ); @@ -46,7 +45,7 @@ static void set_all_vitamins( int target, player &p ) // time (in minutes) it takes for the player to feel hungry // passes time on the calendar -static time_duration time_until_hungry( player &p ) +static time_duration time_until_hungry( Character &p ) { unsigned int thirty_minutes = 0; do { @@ -58,7 +57,7 @@ static time_duration time_until_hungry( player &p ) return thirty_minutes * 30_minutes; } -static void print_stomach_contents( player &p, const bool print ) +static void print_stomach_contents( Character &p, const bool print ) { if( !print ) { return; @@ -87,7 +86,7 @@ static void eat_all_nutrients( player &p ) // player does not thirst or tire or require vitamins TEST_CASE( "starve_test", "[starve][slow]" ) { - player &dummy = g->u; + Character &dummy = get_player_character(); reset_time(); clear_stomach( dummy ); @@ -125,7 +124,7 @@ TEST_CASE( "starve_test", "[starve][slow]" ) // player does not thirst or tire or require vitamins TEST_CASE( "starve_test_hunger3", "[starve][slow]" ) { - player &dummy = g->u; + Character &dummy = get_player_character(); reset_time(); clear_stomach( dummy ); while( !( dummy.has_trait( trait_id( "HUNGER3" ) ) ) ) { @@ -164,7 +163,7 @@ TEST_CASE( "all_nutrition_starve_test", "[starve][slow]" ) { // change this bool when editing the test const bool print_tests = false; - player &dummy = g->u; + avatar &dummy = get_avatar(); reset_time(); clear_stomach( dummy ); eat_all_nutrients( dummy ); @@ -205,7 +204,7 @@ TEST_CASE( "tape_worm_halves_nutrients" ) { const efftype_id effect_tapeworm( "tapeworm" ); const bool print_tests = false; - player &dummy = g->u; + avatar &dummy = get_avatar(); reset_time(); clear_stomach( dummy ); eat_all_nutrients( dummy ); @@ -225,7 +224,7 @@ TEST_CASE( "hunger" ) { // change this bool when editing the test const bool print_tests = false; - player &dummy = g->u; + avatar &dummy = get_avatar(); reset_time(); clear_stomach( dummy ); dummy.initialize_stomach_contents(); @@ -268,8 +267,8 @@ TEST_CASE( "hunger" ) if( print_tests ) { printf( "%d minutes til hunger sets in\n", hunger_time ); } - CHECK( hunger_time <= 375 ); - CHECK( hunger_time >= 345 ); + CHECK( hunger_time <= 285 ); + CHECK( hunger_time >= 255 ); if( print_tests ) { print_stomach_contents( dummy, print_tests ); printf( "eat 16 veggy\n" ); diff --git a/tests/string_formatter_test.cpp b/tests/string_formatter_test.cpp index 001a83c06e95d..3ddf328dc156e 100644 --- a/tests/string_formatter_test.cpp +++ b/tests/string_formatter_test.cpp @@ -100,7 +100,10 @@ void mingw_test( const char *const old_pattern, const char *const new_pattern, c CHECK( original_result == new_result ); } -TEST_CASE( "string_formatter" ) +// Marking mayfail due to failure in Appveyor. Looks like a bug in the Visual +// Studio runtime libraries. Once that failure stops showing up on Appveyor, +// this can cease to be marked thus. +TEST_CASE( "string_formatter", "[!mayfail]" ) { test_typed_printf( "%hhi", "%i" ); test_typed_printf( "%hhu", "%u" ); @@ -144,19 +147,8 @@ TEST_CASE( "string_formatter" ) #pragma GCC diagnostic pop test_new_old_pattern( "%6$-*5$.*4$f%3$s%2$s%1$s", "%6$-*5$.*4$f", "", "", "", 7, 4, 100.44 ); } - CHECK_THROWS( test_for_error( "%6$-*5$.*4$f", 1, 2, 3 ) ); - CHECK_THROWS( test_for_error( "%6$-*5$.*4$f", 1, 2, 3, 4 ) ); - CHECK_THROWS( test_for_error( "%6$-*5$.*4$f", 1, 2, 3, 4, 5 ) ); - - // invalid format specifier - CHECK_THROWS( test_for_error( "%k" ) ); - // can't print a void pointer - CHECK_THROWS( test_for_error( "%s", static_cast( nullptr ) ) ); - CHECK_THROWS( test_for_error( "%d", static_cast( nullptr ) ) ); - CHECK_THROWS( test_for_error( "%d", "some string" ) ); test_for_expected( "", "", "whatever", 5, 0.4 ); - CHECK_THROWS( test_for_error( "%d %d %d %d %d", 1, 2, 3, 4 ) ); test_for_expected( "1 2 3 4 5", "%d %d %d %d %d", 1, 2, 3, 4, 5 ); // test automatic type conversion @@ -570,3 +562,19 @@ TEST_CASE( "string_formatter" ) importet_test( 414, "1234ABCD ", "% -+0*.*X", 20, 5, 305441741 ); importet_test( 415, "00EDCB5433 ", "% -+0*.*X", 20, 10, 3989525555U ); } + +TEST_CASE( "string_formatter_errors" ) +{ + CHECK_THROWS( test_for_error( "%6$-*5$.*4$f", 1, 2, 3 ) ); + CHECK_THROWS( test_for_error( "%6$-*5$.*4$f", 1, 2, 3, 4 ) ); + CHECK_THROWS( test_for_error( "%6$-*5$.*4$f", 1, 2, 3, 4, 5 ) ); + + // invalid format specifier + CHECK_THROWS( test_for_error( "%k" ) ); + // can't print a void pointer + CHECK_THROWS( test_for_error( "%s", static_cast( nullptr ) ) ); + CHECK_THROWS( test_for_error( "%d", static_cast( nullptr ) ) ); + CHECK_THROWS( test_for_error( "%d", "some string" ) ); + + CHECK_THROWS( test_for_error( "%d %d %d %d %d", 1, 2, 3, 4 ) ); +} diff --git a/tests/stringmaker.h b/tests/stringmaker.h index f65d7fc3c2450..bf8cf7521a5dd 100644 --- a/tests/stringmaker.h +++ b/tests/stringmaker.h @@ -26,6 +26,13 @@ struct StringMaker { } }; +template<> +struct StringMaker { + static std::string convert( const point &p ) { + return string_format( "point( %d, %d )", p.x, p.y ); + } +}; + template<> struct StringMaker { static std::string convert( const rectangle &r ) { diff --git a/tests/test_main.cpp b/tests/test_main.cpp index 316df98c4e7e7..758a99316668b 100644 --- a/tests/test_main.cpp +++ b/tests/test_main.cpp @@ -141,17 +141,17 @@ static void init_global_game_state( const std::vector &mods, g->load_core_data( ui ); g->load_world_modfiles( ui ); - g->u = avatar(); - g->u.create( character_type::NOW ); + get_avatar() = avatar(); + get_avatar().create( character_type::NOW ); - g->m = map(); + get_map() = map(); overmap_special_batch empty_specials( point_zero ); overmap_buffer.create_custom_overmap( point_zero, empty_specials ); - g->m.load( tripoint( g->get_levx(), g->get_levy(), g->get_levz() ), false ); + get_map().load( tripoint( g->get_levx(), g->get_levy(), g->get_levz() ), false ); - g->weather.update_weather(); + get_weather().update_weather(); } // Checks if any of the flags are in container, removes them all diff --git a/tests/throwing_test.cpp b/tests/throwing_test.cpp index 15eca333b61e8..dc0e71d15bcb6 100644 --- a/tests/throwing_test.cpp +++ b/tests/throwing_test.cpp @@ -55,7 +55,7 @@ static std::ostream &operator<<( std::ostream &stream, const throw_test_pstats & static const skill_id skill_throw = skill_id( "throw" ); -static void reset_player( player &p, const throw_test_pstats &pstats, const tripoint &pos ) +static void reset_player( Character &p, const throw_test_pstats &pstats, const tripoint &pos ) { p.reset(); p.set_stamina( p.get_stamina_max() ); @@ -171,7 +171,7 @@ constexpr throw_test_pstats hi_skill_athlete_stats = { MAX_SKILL, 12, 12, 12 }; TEST_CASE( "basic_throwing_sanity_tests", "[throwing],[balance]" ) { - player &p = g->u; + avatar &p = get_avatar(); clear_map(); SECTION( "test_player_vs_zombie_rock_basestats" ) { @@ -216,7 +216,7 @@ TEST_CASE( "basic_throwing_sanity_tests", "[throwing],[balance]" ) TEST_CASE( "throwing_skill_impact_test", "[throwing],[balance]" ) { - player &p = g->u; + avatar &p = get_avatar(); clear_map(); // we already cover low stats in the sanity tests and we only cover a few @@ -304,7 +304,7 @@ static void test_player_kills_monster( TEST_CASE( "player_kills_zombie_before_reach", "[throwing],[balance][scenario]" ) { - player &p = g->u; + avatar &p = get_avatar(); clear_map(); SECTION( "test_player_kills_zombie_with_rock_basestats" ) { @@ -315,7 +315,7 @@ TEST_CASE( "player_kills_zombie_before_reach", "[throwing],[balance][scenario]" int throw_cost( const player &c, const item &to_throw ); TEST_CASE( "time_to_throw_independent_of_number_of_projectiles", "[throwing],[balance]" ) { - player &p = g->u; + player &p = get_avatar(); clear_avatar(); item thrown( "throwing_stick", calendar::turn, 10 ); diff --git a/tests/vehicle_efficiency_test.cpp b/tests/vehicle_efficiency_test.cpp index 87ceb1d9257bd..f3256deb70031 100644 --- a/tests/vehicle_efficiency_test.cpp +++ b/tests/vehicle_efficiency_test.cpp @@ -10,10 +10,10 @@ #include #include -#include "avatar.h" #include "bodypart.h" #include "calendar.h" #include "catch/catch.hpp" +#include "character.h" #include "enums.h" #include "game.h" #include "item.h" @@ -42,11 +42,12 @@ static void clear_game( const ter_id &terrain ) clear_npcs(); clear_vehicles(); + Character &player_character = get_player_character(); // Move player somewhere safe - REQUIRE_FALSE( g->u.in_vehicle ); - g->u.setpos( tripoint_zero ); + REQUIRE_FALSE( player_character.in_vehicle ); + player_character.setpos( tripoint_zero ); // Blind the player to avoid needless drawing-related overhead - g->u.add_effect( effect_blind, 1_turns, num_bp, true ); + player_character.add_effect( effect_blind, 1_turns, num_bp, true ); build_test_map( terrain ); } @@ -431,7 +432,7 @@ TEST_CASE( "vehicle_efficiency", "[vehicle] [engine]" ) test_vehicle( "truck_swat", 5959334, 483800, 322700, 29610, 7604 ); test_vehicle( "tractor_plow", 723658, 482400, 482400, 113900, 113900 ); test_vehicle( "apc", 5801619, 1069000, 922400, 130800, 85590 ); - test_vehicle( "humvee", 5503245, 574300, 325900, 25620, 9171 ); + test_vehicle( "humvee", 5503345, 574300, 325900, 25620, 9171 ); test_vehicle( "road_roller", 8829220, 357200, 380200, 22760, 6925 ); test_vehicle( "golf_cart", 444630, 52460, 105500, 27250, 14200 ); } 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 ); } diff --git a/tests/vehicle_power_test.cpp b/tests/vehicle_power_test.cpp index 6d016d910b3bc..f069d3ba1a01f 100644 --- a/tests/vehicle_power_test.cpp +++ b/tests/vehicle_power_test.cpp @@ -72,7 +72,7 @@ TEST_CASE( "vehicle power with reactor and solar panels", "[vehicle][power]" ) calendar::turn = calendar::turn_zero + calendar::season_length() + 1_days; const time_point start_time = sunrise( calendar::turn ) + 3_hours; veh_ptr->update_time( start_time ); - get_weather().weather_override = WEATHER_SUNNY; + get_weather().weather_override = weather_type_id( "sunny" ); AND_GIVEN( "the battery has no charge" ) { veh_ptr->discharge_battery( veh_ptr->fuel_left( fuel_type_battery ) ); diff --git a/tests/vehicle_ramp_test.cpp b/tests/vehicle_ramp_test.cpp new file mode 100644 index 0000000000000..7c44124012c66 --- /dev/null +++ b/tests/vehicle_ramp_test.cpp @@ -0,0 +1,328 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "avatar.h" +#include "catch/catch.hpp" +#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.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" + +static const efftype_id effect_blind( "blind" ); + +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; + clear_creatures(); + clear_npcs(); + clear_vehicles(); + + Character &player_character = get_player_character(); + // Move player somewhere safe + REQUIRE_FALSE( player_character.in_vehicle ); + player_character.setpos( tripoint_zero ); + // Blind the player to avoid needless drawing-related overhead + //player_character.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" ) ); + } + } + } + here.invalidate_map_cache( 0 ); + here.build_map_cache( 0, true ); +} + +// Algorithm goes as follows: +// 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, + 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; + } + + vehicle &veh = *veh_ptr; + veh.check_falling_or_floating(); + + REQUIRE( !veh.is_in_water() ); + + veh.tags.insert( "IN_CONTROL_OVERRIDE" ); + veh.engine_on = true; + Character &player_character = get_player_character(); + player_character.setpos( map_starting_point ); + + REQUIRE( player_character.pos() == map_starting_point ); + if( player_character.pos() != map_starting_point ) { + return; + } + get_map().board_vehicle( map_starting_point, &player_character ); + REQUIRE( player_character.pos() == map_starting_point ); + if( player_character.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( player_character.pos() == ppos ); + } + } + vpts = veh.get_points(); + cycles++; + } + const cata::optional vp = here.veh_at( player_character.pos() ).part_with_feature( + VPFLAG_BOARDABLE, true ); + REQUIRE( vp ); + if( vp ) { + const int z_change = map_starting_point.z - player_character.pos().z; + here.unboard_vehicle( *vp, &player_character, false ); + here.ter_set( map_starting_point, ter_id( "t_pavement" ) ); + player_character.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 ); + } +} + +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 ); + } +} diff --git a/tests/vehicle_split_test.cpp b/tests/vehicle_split_test.cpp index 3a615b43603c2..de96ec8412166 100644 --- a/tests/vehicle_split_test.cpp +++ b/tests/vehicle_split_test.cpp @@ -2,9 +2,8 @@ #include #include -#include "avatar.h" #include "catch/catch.hpp" -#include "game.h" +#include "character.h" #include "map.h" #include "vehicle.h" #include "type_id.h" @@ -12,26 +11,28 @@ TEST_CASE( "vehicle_split_section" ) { + map &here = get_map(); + Character &player_character = get_player_character(); for( int dir = 0; dir < 360; dir += 15 ) { - CHECK( !g->u.in_vehicle ); + CHECK( !player_character.in_vehicle ); const tripoint test_origin( 15, 15, 0 ); - g->u.setpos( test_origin ); + player_character.setpos( test_origin ); tripoint vehicle_origin = tripoint( 10, 10, 0 ); - VehicleList vehs = g->m.get_vehicles(); + VehicleList vehs = here.get_vehicles(); vehicle *veh_ptr; for( auto &vehs_v : vehs ) { veh_ptr = vehs_v.v; - g->m.destroy_vehicle( veh_ptr ); + here.destroy_vehicle( veh_ptr ); } - REQUIRE( g->m.get_vehicles().empty() ); - veh_ptr = g->m.add_vehicle( vproto_id( "cross_split_test" ), vehicle_origin, dir, 0, 0 ); + REQUIRE( here.get_vehicles().empty() ); + veh_ptr = here.add_vehicle( vproto_id( "cross_split_test" ), vehicle_origin, dir, 0, 0 ); REQUIRE( veh_ptr != nullptr ); std::set original_points = veh_ptr->get_points( true ); - g->m.destroy( vehicle_origin ); + here.destroy( vehicle_origin ); veh_ptr->part_removal_cleanup(); REQUIRE( veh_ptr->get_parts_at( vehicle_origin, "", part_status_flag::available ).empty() ); - vehs = g->m.get_vehicles(); + vehs = here.get_vehicles(); // destroying the center frame results in 4 new vehicles CHECK( vehs.size() == 4 ); if( vehs.size() == 4 ) { @@ -59,19 +60,19 @@ TEST_CASE( "vehicle_split_section" ) } } } - g->m.destroy_vehicle( vehs[ 3 ].v ); - g->m.destroy_vehicle( vehs[ 2 ].v ); - g->m.destroy_vehicle( vehs[ 1 ].v ); - g->m.destroy_vehicle( vehs[ 0 ].v ); + here.destroy_vehicle( vehs[ 3 ].v ); + here.destroy_vehicle( vehs[ 2 ].v ); + here.destroy_vehicle( vehs[ 1 ].v ); + here.destroy_vehicle( vehs[ 0 ].v ); } - REQUIRE( g->m.get_vehicles().empty() ); + REQUIRE( here.get_vehicles().empty() ); vehicle_origin = tripoint( 20, 20, 0 ); - veh_ptr = g->m.add_vehicle( vproto_id( "circle_split_test" ), vehicle_origin, dir, 0, 0 ); + veh_ptr = here.add_vehicle( vproto_id( "circle_split_test" ), vehicle_origin, dir, 0, 0 ); REQUIRE( veh_ptr != nullptr ); - g->m.destroy( vehicle_origin ); + here.destroy( vehicle_origin ); veh_ptr->part_removal_cleanup(); REQUIRE( veh_ptr->get_parts_at( vehicle_origin, "", part_status_flag::available ).empty() ); - vehs = g->m.get_vehicles(); + vehs = here.get_vehicles(); CHECK( vehs.size() == 1 ); if( vehs.size() == 1 ) { CHECK( vehs[ 0 ].v->part_count() == 38 ); diff --git a/tests/vision_test.cpp b/tests/vision_test.cpp index a9d218973675b..e711727bd13b3 100644 --- a/tests/vision_test.cpp +++ b/tests/vision_test.cpp @@ -8,7 +8,6 @@ #include #include -#include "avatar.h" #include "calendar.h" #include "catch/catch.hpp" #include "character.h" @@ -56,23 +55,24 @@ static void full_map_test( const std::vector &setup, const efftype_id effect_narcosis( "narcosis" ); const ter_id t_flat_roof( "t_flat_roof" ); + Character &player_character = get_player_character(); g->place_player( tripoint( 60, 60, 0 ) ); - g->u.worn.clear(); // Remove any light-emitting clothing - g->u.clear_effects(); + player_character.worn.clear(); // Remove any light-emitting clothing + player_character.clear_effects(); clear_map(); g->reset_light_level(); if( !!( flags & vision_test_flags::crouching ) ) { - g->u.set_movement_mode( move_mode_crouch ); + player_character.set_movement_mode( move_mode_crouch ); } else { - g->u.set_movement_mode( move_mode_walk ); + player_character.set_movement_mode( move_mode_walk ); } - REQUIRE( !g->u.is_blind() ); - REQUIRE( !g->u.in_sleep_state() ); - REQUIRE( !g->u.has_effect( effect_narcosis ) ); + REQUIRE( !player_character.is_blind() ); + REQUIRE( !player_character.in_sleep_state() ); + REQUIRE( !player_character.has_effect( effect_narcosis ) ); - g->u.recalc_sight_limits(); + player_character.recalc_sight_limits(); calendar::turn = time; @@ -96,13 +96,13 @@ static void full_map_test( const std::vector &setup, case 'V': case 'U': case 'u': - origin = g->u.pos() - point( x, y ); + origin = player_character.pos() - point( x, y ); if( setup[y][x] == 'V' ) { item headlamp( "wearable_light_on" ); item battery( "light_battery_cell" ); battery.ammo_set( battery.ammo_default(), -1 ); headlamp.put_in( battery, item_pocket::pocket_type::MAGAZINE_WELL ); - g->u.worn.push_back( headlamp ); + player_character.worn.push_back( headlamp ); } break; } @@ -112,7 +112,7 @@ static void full_map_test( const std::vector &setup, { // Sanity check on player placement REQUIRE( origin.z < OVERMAP_HEIGHT ); - tripoint player_offset = g->u.pos() - origin; + tripoint player_offset = player_character.pos() - origin; REQUIRE( player_offset.y >= 0 ); REQUIRE( player_offset.y < height ); REQUIRE( player_offset.x >= 0 ); @@ -121,6 +121,7 @@ static void full_map_test( const std::vector &setup, REQUIRE( ( player_char == 'U' || player_char == 'u' || player_char == 'V' ) ); } + map &here = get_map(); for( int y = 0; y < height; ++y ) { for( int x = 0; x < width; ++x ) { const tripoint p = origin + point( x, y ); @@ -129,21 +130,21 @@ static void full_map_test( const std::vector &setup, case ' ': break; case 'L': - g->m.ter_set( p, t_utility_light ); - g->m.ter_set( above, t_flat_roof ); + here.ter_set( p, t_utility_light ); + here.ter_set( above, t_flat_roof ); break; case '#': - g->m.ter_set( p, t_brick_wall ); - g->m.ter_set( above, t_flat_roof ); + here.ter_set( p, t_brick_wall ); + here.ter_set( above, t_flat_roof ); break; case '=': - g->m.ter_set( p, t_window_frame ); - g->m.ter_set( above, t_flat_roof ); + here.ter_set( p, t_window_frame ); + here.ter_set( above, t_flat_roof ); break; case '-': case 'u': - g->m.ter_set( p, t_floor ); - g->m.ter_set( above, t_flat_roof ); + here.ter_set( p, t_floor ); + here.ter_set( above, t_flat_roof ); break; case 'U': case 'V': @@ -159,17 +160,17 @@ static void full_map_test( const std::vector &setup, // player's vision_threshold is based on the previous lighting level (so // they might, for example, have poor nightvision due to having just been // in daylight) - g->m.update_visibility_cache( origin.z ); - g->m.invalidate_map_cache( origin.z ); - g->m.build_map_cache( origin.z ); - g->m.update_visibility_cache( origin.z ); - g->m.invalidate_map_cache( origin.z ); - g->m.build_map_cache( origin.z ); - - const level_cache &cache = g->m.access_cache( origin.z ); - const level_cache &above_cache = g->m.access_cache( origin.z + 1 ); + here.update_visibility_cache( origin.z ); + here.invalidate_map_cache( origin.z ); + here.build_map_cache( origin.z ); + here.update_visibility_cache( origin.z ); + here.invalidate_map_cache( origin.z ); + here.build_map_cache( origin.z ); + + const level_cache &cache = here.access_cache( origin.z ); + const level_cache &above_cache = here.access_cache( origin.z + 1 ); const visibility_variables &vvcache = - g->m.get_visibility_variables_cache(); + here.get_visibility_variables_cache(); std::ostringstream fields; std::ostringstream transparency; @@ -186,7 +187,7 @@ static void full_map_test( const std::vector &setup, for( int x = 0; x < width; ++x ) { const tripoint p = origin + point( x, y ); const map::apparent_light_info al = map::apparent_light_helper( cache, p ); - for( auto &pr : g->m.field_at( p ) ) { + for( auto &pr : here.field_at( p ) ) { fields << pr.second.name() << ','; } fields << ' '; @@ -208,10 +209,10 @@ static void full_map_test( const std::vector &setup, floor_above << '\n'; } - INFO( "zlevels: " << g->m.has_zlevels() ); + INFO( "zlevels: " << here.has_zlevels() ); INFO( "origin: " << origin ); - INFO( "player: " << g->u.pos() ); - INFO( "unimpaired_range: " << g->u.unimpaired_range() ); + INFO( "player: " << player_character.pos() ); + INFO( "unimpaired_range: " << player_character.unimpaired_range() ); INFO( "vision_threshold: " << vvcache.vision_threshold ); INFO( "fields:\n" << fields.str() ); INFO( "transparency:\n" << transparency.str() ); @@ -228,7 +229,7 @@ static void full_map_test( const std::vector &setup, for( int y = 0; y < height; ++y ) { for( int x = 0; x < width; ++x ) { const tripoint p = origin + point( x, y ); - const lit_level level = g->m.apparent_light_at( p, vvcache ); + const lit_level level = here.apparent_light_at( p, vvcache ); const char exp_char = expected_results[y][x]; if( exp_char < '0' || exp_char > '9' ) { FAIL( "unexpected result char '" << diff --git a/tests/visitable_remove_test.cpp b/tests/visitable_remove_test.cpp index fc6cd722d4894..3dbd44a810414 100644 --- a/tests/visitable_remove_test.cpp +++ b/tests/visitable_remove_test.cpp @@ -54,7 +54,7 @@ TEST_CASE( "visitable_remove", "[visitable]" ) // check if all tiles within radius are loaded within current submap and passable const auto suitable = [&here]( const tripoint & pos, const int radius ) { - std::vector tiles = closest_tripoints_first( pos, radius ); + std::vector tiles = closest_points_first( pos, radius ); return std::all_of( tiles.begin(), tiles.end(), [&here]( const tripoint & e ) { if( !here.inbounds( e ) ) { return false; @@ -72,7 +72,7 @@ TEST_CASE( "visitable_remove", "[visitable]" ) // move player randomly until we find a suitable position while( !suitable( p.pos(), 1 ) ) { CHECK( !p.in_vehicle ); - p.setpos( random_entry( closest_tripoints_first( p.pos(), 1 ) ) ); + p.setpos( random_entry( closest_points_first( p.pos(), 1 ) ) ); } item temp_liquid( liquid_id ); @@ -295,7 +295,7 @@ TEST_CASE( "visitable_remove", "[visitable]" ) } GIVEN( "A player surrounded by several bottles of water" ) { - std::vector tiles = closest_tripoints_first( p.pos(), 1 ); + std::vector tiles = closest_points_first( p.pos(), 1 ); tiles.erase( tiles.begin() ); // player tile int our = 0; // bottles placed on player tile @@ -412,7 +412,7 @@ TEST_CASE( "visitable_remove", "[visitable]" ) } GIVEN( "An adjacent vehicle contains several bottles of water" ) { - std::vector tiles = closest_tripoints_first( p.pos(), 1 ); + std::vector tiles = closest_points_first( p.pos(), 1 ); tiles.erase( tiles.begin() ); // player tile tripoint veh = random_entry( tiles ); REQUIRE( here.add_vehicle( vproto_id( "shopping_cart" ), veh, 0, 0, 0 ) ); diff --git a/tests/weather_test.cpp b/tests/weather_test.cpp index 29b95858e1232..1e2befc78f779 100644 --- a/tests/weather_test.cpp +++ b/tests/weather_test.cpp @@ -70,7 +70,7 @@ TEST_CASE( "weather realism" ) int hour = to_hours( time_past_new_year( i ) ); hourly_precip[hour] += precip_mm_per_hour( - weather::precip( wgen.get_weather_conditions( w ) ) ) + wgen.get_weather_conditions( w )->precip ) / 60; } diff --git a/tests/wield_times_test.cpp b/tests/wield_times_test.cpp index 0d6c790a49cad..9cd37fb745a8c 100644 --- a/tests/wield_times_test.cpp +++ b/tests/wield_times_test.cpp @@ -59,6 +59,7 @@ TEST_CASE( "Wield time test", "[wield]" ) item knife( "knife_hunting" ); avatar guy; + guy.set_body(); guy.worn.push_back( backpack ); item_location backpack_loc( guy, &guy.worn.back() ); backpack_loc->put_in( plastic_bag, item_pocket::pocket_type::CONTAINER );